auto-sync: 2026-05-02 08:40:01
This commit is contained in:
@@ -385,38 +385,67 @@ async def get_tile(z: int, x: int, y: int):
|
||||
conn = get_db()
|
||||
cur = conn.cursor()
|
||||
|
||||
# Trails — простой bbox по координатам из WKB (без пространственного индекса)
|
||||
# Используем ST_Intersects если Spatialite доступен, иначе fallback
|
||||
try:
|
||||
cur.execute("""
|
||||
SELECT osm_id, highway_type, track_type, surface, name,
|
||||
length_m, mtb_scale, geom
|
||||
FROM trails
|
||||
WHERE ST_Intersects(geom, BuildMBR(?,?,?,?,4326))
|
||||
LIMIT 5000
|
||||
""", (q_west, q_south, q_east, q_north))
|
||||
except Exception:
|
||||
# Fallback без пространственного индекса
|
||||
cur.execute("""
|
||||
SELECT osm_id, highway_type, track_type, surface, name,
|
||||
length_m, mtb_scale, geom
|
||||
FROM trails
|
||||
LIMIT 5000
|
||||
""")
|
||||
# Читаем все trails и фильтруем по bbox через WKB парсинг в Python
|
||||
# (ST_Intersects не работает с raw WKB без правильной инициализации Spatialite)
|
||||
cur.execute("""
|
||||
SELECT osm_id, highway_type, track_type, surface, name,
|
||||
length_m, mtb_scale, geom
|
||||
FROM trails
|
||||
""")
|
||||
all_trails = cur.fetchall()
|
||||
|
||||
trails_rows = cur.fetchall()
|
||||
# Фильтруем по bbox через первую точку WKB
|
||||
trails_rows = []
|
||||
for row in all_trails:
|
||||
geom_blob = row["geom"]
|
||||
if not geom_blob:
|
||||
continue
|
||||
try:
|
||||
blob = bytes(geom_blob)
|
||||
endian = "<" if blob[0] == 1 else ">"
|
||||
gtype = struct.unpack_from(endian + "I", blob, 1)[0]
|
||||
offset = 5
|
||||
if gtype & 0x20000000:
|
||||
offset += 4 # skip SRID
|
||||
npts = struct.unpack_from(endian + "I", blob, offset)[0]
|
||||
offset += 4
|
||||
if npts < 2:
|
||||
continue
|
||||
# Проверяем первую и последнюю точку для bbox
|
||||
lon1, lat1 = struct.unpack_from(endian + "dd", blob, offset)
|
||||
lon2, lat2 = struct.unpack_from(endian + "dd", blob, offset + (npts - 1) * 16)
|
||||
min_lon = min(lon1, lon2)
|
||||
max_lon = max(lon1, lon2)
|
||||
min_lat = min(lat1, lat2)
|
||||
max_lat = max(lat1, lat2)
|
||||
if max_lon < q_west or min_lon > q_east or max_lat < q_south or min_lat > q_north:
|
||||
continue
|
||||
trails_rows.append(row)
|
||||
if len(trails_rows) >= 5000:
|
||||
break
|
||||
except Exception:
|
||||
continue
|
||||
|
||||
try:
|
||||
cur.execute("""
|
||||
SELECT osm_id, poi_type, name, geom
|
||||
FROM poi
|
||||
WHERE ST_Intersects(geom, BuildMBR(?,?,?,?,4326))
|
||||
LIMIT 1000
|
||||
""", (q_west, q_south, q_east, q_north))
|
||||
except Exception:
|
||||
cur.execute("SELECT osm_id, poi_type, name, geom FROM poi LIMIT 1000")
|
||||
cur.execute("SELECT osm_id, poi_type, name, geom FROM poi")
|
||||
all_poi = cur.fetchall()
|
||||
poi_rows = []
|
||||
for row in all_poi:
|
||||
geom_blob = row["geom"]
|
||||
if not geom_blob:
|
||||
continue
|
||||
try:
|
||||
blob = bytes(geom_blob)
|
||||
endian = "<" if blob[0] == 1 else ">"
|
||||
offset = 5
|
||||
gtype = struct.unpack_from(endian + "I", blob, 1)[0]
|
||||
if gtype & 0x20000000:
|
||||
offset += 4
|
||||
lon, lat = struct.unpack_from(endian + "dd", blob, offset)
|
||||
if q_west <= lon <= q_east and q_south <= lat <= q_north:
|
||||
poi_rows.append(row)
|
||||
except Exception:
|
||||
continue
|
||||
|
||||
poi_rows = cur.fetchall()
|
||||
conn.close()
|
||||
|
||||
except Exception as e:
|
||||
|
||||
@@ -13,8 +13,8 @@
|
||||
* { box-sizing: border-box; margin: 0; padding: 0; }
|
||||
|
||||
body {
|
||||
background: #1a1a2e;
|
||||
color: #e0e0e0;
|
||||
background: #f5f3ee;
|
||||
color: #333333;
|
||||
font-family: 'Segoe UI', system-ui, sans-serif;
|
||||
height: 100vh;
|
||||
display: flex;
|
||||
@@ -22,20 +22,21 @@
|
||||
}
|
||||
|
||||
#header {
|
||||
background: #16213e;
|
||||
border-bottom: 1px solid #0f3460;
|
||||
background: #ffffff;
|
||||
border-bottom: 1px solid #ddd;
|
||||
padding: 10px 16px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 16px;
|
||||
flex-shrink: 0;
|
||||
z-index: 10;
|
||||
box-shadow: 0 1px 4px rgba(0,0,0,0.1);
|
||||
}
|
||||
|
||||
#header h1 {
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
color: #FF8C00;
|
||||
color: #e07b00;
|
||||
letter-spacing: 0.5px;
|
||||
}
|
||||
|
||||
@@ -53,9 +54,9 @@
|
||||
}
|
||||
|
||||
.toggle-btn {
|
||||
background: #0f3460;
|
||||
border: 1px solid #1a5276;
|
||||
color: #e0e0e0;
|
||||
background: #f0f0f0;
|
||||
border: 1px solid #ccc;
|
||||
color: #444;
|
||||
padding: 5px 12px;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
@@ -66,8 +67,8 @@
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.toggle-btn:hover { background: #1a5276; }
|
||||
.toggle-btn.active { background: #FF8C00; border-color: #FF8C00; color: #1a1a2e; font-weight: 600; }
|
||||
.toggle-btn:hover { background: #e0e0e0; }
|
||||
.toggle-btn.active { background: #ff6600; border-color: #ff6600; color: #fff; font-weight: 600; }
|
||||
|
||||
.dot {
|
||||
width: 10px; height: 10px;
|
||||
@@ -87,13 +88,14 @@
|
||||
position: absolute;
|
||||
bottom: 30px;
|
||||
left: 12px;
|
||||
background: rgba(22, 33, 62, 0.92);
|
||||
border: 1px solid #0f3460;
|
||||
background: rgba(255,255,255,0.95);
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 6px;
|
||||
padding: 10px 14px;
|
||||
font-size: 12px;
|
||||
z-index: 5;
|
||||
min-width: 160px;
|
||||
box-shadow: 0 2px 8px rgba(0,0,0,0.12);
|
||||
}
|
||||
|
||||
#legend h3 {
|
||||
@@ -120,28 +122,25 @@
|
||||
.legend-dashed {
|
||||
width: 28px;
|
||||
height: 0;
|
||||
border-top: 2px dashed #FFD700;
|
||||
border-top: 2px dashed #cc9900;
|
||||
}
|
||||
|
||||
/* Popup */
|
||||
.maplibregl-popup-content {
|
||||
background: #16213e !important;
|
||||
color: #e0e0e0 !important;
|
||||
border: 1px solid #0f3460 !important;
|
||||
background: #ffffff !important;
|
||||
color: #333 !important;
|
||||
border: 1px solid #ddd !important;
|
||||
border-radius: 6px !important;
|
||||
padding: 12px 14px !important;
|
||||
font-size: 13px !important;
|
||||
min-width: 180px;
|
||||
}
|
||||
|
||||
.maplibregl-popup-tip {
|
||||
border-top-color: #16213e !important;
|
||||
box-shadow: 0 2px 8px rgba(0,0,0,0.15) !important;
|
||||
}
|
||||
|
||||
.popup-title {
|
||||
font-weight: 600;
|
||||
font-size: 14px;
|
||||
color: #FF8C00;
|
||||
color: #e07b00;
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
|
||||
@@ -153,20 +152,21 @@
|
||||
}
|
||||
|
||||
.popup-key { color: #888; }
|
||||
.popup-val { color: #e0e0e0; font-weight: 500; }
|
||||
.popup-val { color: #333; font-weight: 500; }
|
||||
|
||||
/* Stats bar */
|
||||
#stats {
|
||||
position: absolute;
|
||||
top: 10px;
|
||||
right: 12px;
|
||||
background: rgba(22, 33, 62, 0.85);
|
||||
border: 1px solid #0f3460;
|
||||
background: rgba(255,255,255,0.9);
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 4px;
|
||||
padding: 6px 10px;
|
||||
font-size: 11px;
|
||||
color: #888;
|
||||
color: #666;
|
||||
z-index: 5;
|
||||
box-shadow: 0 1px 4px rgba(0,0,0,0.1);
|
||||
}
|
||||
|
||||
/* Loading indicator */
|
||||
@@ -175,20 +175,21 @@
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
background: rgba(22, 33, 62, 0.95);
|
||||
border: 1px solid #FF8C00;
|
||||
background: rgba(255,255,255,0.97);
|
||||
border: 1px solid #ff6600;
|
||||
border-radius: 8px;
|
||||
padding: 20px 30px;
|
||||
text-align: center;
|
||||
z-index: 100;
|
||||
display: none;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
#loading.visible { display: block; }
|
||||
#loading .spinner {
|
||||
width: 32px; height: 32px;
|
||||
border: 3px solid #0f3460;
|
||||
border-top-color: #FF8C00;
|
||||
border: 3px solid #eee;
|
||||
border-top-color: #ff6600;
|
||||
border-radius: 50%;
|
||||
animation: spin 0.8s linear infinite;
|
||||
margin: 0 auto 10px;
|
||||
@@ -202,8 +203,8 @@
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
background: rgba(22, 33, 62, 0.95);
|
||||
border: 1px solid #FF8C00;
|
||||
background: rgba(255,255,255,0.97);
|
||||
border: 1px solid #ff6600;
|
||||
border-radius: 8px;
|
||||
padding: 24px 32px;
|
||||
text-align: center;
|
||||
@@ -213,13 +214,14 @@
|
||||
}
|
||||
|
||||
#no-data-warning.visible { display: block; }
|
||||
#no-data-warning h2 { color: #FF8C00; margin-bottom: 10px; }
|
||||
#no-data-warning p { color: #aaa; font-size: 13px; line-height: 1.5; }
|
||||
#no-data-warning h2 { color: #ff6600; margin-bottom: 10px; }
|
||||
#no-data-warning p { color: #666; font-size: 13px; line-height: 1.5; }
|
||||
#no-data-warning code {
|
||||
background: #0f3460;
|
||||
background: #f5f5f5;
|
||||
padding: 2px 6px;
|
||||
border-radius: 3px;
|
||||
font-family: monospace;
|
||||
color: #333;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
@@ -282,7 +284,7 @@
|
||||
<div class="legend-line" style="background:#555; height:1px"></div>
|
||||
<span>Асфальт</span>
|
||||
</div>
|
||||
<div style="margin-top:8px; border-top:1px solid #0f3460; padding-top:8px;">
|
||||
<div style="margin-top:8px; border-top:1px solid #ddd; padding-top:8px;">
|
||||
<div class="legend-item">
|
||||
<span class="dot" style="background:#ff4444"></span><span>Вершина</span>
|
||||
</div>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"version": 8,
|
||||
"name": "Enduro Trails Dark",
|
||||
"name": "Enduro Trails Light",
|
||||
"metadata": {},
|
||||
"center": [37.6, 55.75],
|
||||
"zoom": 7,
|
||||
@@ -15,9 +15,7 @@
|
||||
},
|
||||
"osm-raster": {
|
||||
"type": "raster",
|
||||
"tiles": [
|
||||
"https://tile.openstreetmap.org/{z}/{x}/{y}.png"
|
||||
],
|
||||
"tiles": ["https://tile.openstreetmap.org/{z}/{x}/{y}.png"],
|
||||
"tileSize": 256,
|
||||
"attribution": "© OpenStreetMap contributors"
|
||||
}
|
||||
@@ -27,19 +25,17 @@
|
||||
{
|
||||
"id": "background",
|
||||
"type": "background",
|
||||
"paint": {
|
||||
"background-color": "#1a1a2e"
|
||||
}
|
||||
"paint": { "background-color": "#f5f3ee" }
|
||||
},
|
||||
{
|
||||
"id": "osm-base",
|
||||
"type": "raster",
|
||||
"source": "osm-raster",
|
||||
"paint": {
|
||||
"raster-opacity": 0.25,
|
||||
"raster-saturation": -0.8,
|
||||
"raster-brightness-min": 0.0,
|
||||
"raster-brightness-max": 0.3
|
||||
"raster-opacity": 0.45,
|
||||
"raster-saturation": -0.6,
|
||||
"raster-brightness-min": 0.6,
|
||||
"raster-brightness-max": 1.0
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -47,92 +43,53 @@
|
||||
"type": "line",
|
||||
"source": "trails-tiles",
|
||||
"source-layer": "trails",
|
||||
"filter": [
|
||||
"in", "highway", "primary", "secondary", "tertiary", "residential", "cycleway"
|
||||
],
|
||||
"filter": ["in", "highway", "primary", "secondary", "tertiary", "residential", "cycleway"],
|
||||
"paint": {
|
||||
"line-color": "#555555",
|
||||
"line-color": "#aaaaaa",
|
||||
"line-width": 1,
|
||||
"line-opacity": 0.7
|
||||
"line-opacity": 0.6
|
||||
},
|
||||
"layout": {
|
||||
"line-cap": "round",
|
||||
"line-join": "round"
|
||||
}
|
||||
"layout": { "line-cap": "round", "line-join": "round" }
|
||||
},
|
||||
{
|
||||
"id": "trails-grade12",
|
||||
"type": "line",
|
||||
"source": "trails-tiles",
|
||||
"source-layer": "trails",
|
||||
"filter": [
|
||||
"all",
|
||||
["==", "highway", "track"],
|
||||
["in", "tracktype", "grade1", "grade2"]
|
||||
],
|
||||
"filter": ["all", ["==", "highway", "track"], ["in", "tracktype", "grade1", "grade2"]],
|
||||
"paint": {
|
||||
"line-color": "#FFA500",
|
||||
"line-width": [
|
||||
"interpolate", ["linear"], ["zoom"],
|
||||
8, 1.5,
|
||||
12, 2.5,
|
||||
16, 4
|
||||
],
|
||||
"line-color": "#e07b00",
|
||||
"line-width": ["interpolate", ["linear"], ["zoom"], 8, 1.5, 12, 2.5, 16, 4],
|
||||
"line-opacity": 0.9
|
||||
},
|
||||
"layout": {
|
||||
"line-cap": "round",
|
||||
"line-join": "round"
|
||||
}
|
||||
"layout": { "line-cap": "round", "line-join": "round" }
|
||||
},
|
||||
{
|
||||
"id": "trails-grade345",
|
||||
"type": "line",
|
||||
"source": "trails-tiles",
|
||||
"source-layer": "trails",
|
||||
"filter": [
|
||||
"all",
|
||||
["==", "highway", "track"],
|
||||
["!in", "tracktype", "grade1", "grade2"]
|
||||
],
|
||||
"filter": ["all", ["==", "highway", "track"], ["!in", "tracktype", "grade1", "grade2"]],
|
||||
"paint": {
|
||||
"line-color": "#FF8C00",
|
||||
"line-width": [
|
||||
"interpolate", ["linear"], ["zoom"],
|
||||
8, 2,
|
||||
12, 3.5,
|
||||
16, 5
|
||||
],
|
||||
"line-color": "#ff6600",
|
||||
"line-width": ["interpolate", ["linear"], ["zoom"], 8, 2, 12, 3.5, 16, 5],
|
||||
"line-opacity": 0.95
|
||||
},
|
||||
"layout": {
|
||||
"line-cap": "round",
|
||||
"line-join": "round"
|
||||
}
|
||||
"layout": { "line-cap": "round", "line-join": "round" }
|
||||
},
|
||||
{
|
||||
"id": "trails-path-bridleway",
|
||||
"type": "line",
|
||||
"source": "trails-tiles",
|
||||
"source-layer": "trails",
|
||||
"filter": [
|
||||
"in", "highway", "path", "bridleway", "footway"
|
||||
],
|
||||
"filter": ["in", "highway", "path", "bridleway", "footway"],
|
||||
"paint": {
|
||||
"line-color": "#FFD700",
|
||||
"line-width": [
|
||||
"interpolate", ["linear"], ["zoom"],
|
||||
8, 1,
|
||||
12, 1.5,
|
||||
16, 3
|
||||
],
|
||||
"line-color": "#cc9900",
|
||||
"line-width": ["interpolate", ["linear"], ["zoom"], 8, 1, 12, 1.5, 16, 3],
|
||||
"line-opacity": 0.85,
|
||||
"line-dasharray": [3, 2]
|
||||
},
|
||||
"layout": {
|
||||
"line-cap": "butt",
|
||||
"line-join": "round"
|
||||
}
|
||||
"layout": { "line-cap": "butt", "line-join": "round" }
|
||||
},
|
||||
{
|
||||
"id": "poi-circles",
|
||||
@@ -140,23 +97,18 @@
|
||||
"source": "trails-tiles",
|
||||
"source-layer": "poi",
|
||||
"paint": {
|
||||
"circle-radius": [
|
||||
"interpolate", ["linear"], ["zoom"],
|
||||
8, 3,
|
||||
12, 6,
|
||||
16, 10
|
||||
],
|
||||
"circle-radius": ["interpolate", ["linear"], ["zoom"], 8, 3, 12, 6, 16, 10],
|
||||
"circle-color": [
|
||||
"match", ["get", "poi_type"],
|
||||
"natural=peak", "#ff4444",
|
||||
"natural=water", "#4488ff",
|
||||
"tourism=viewpoint", "#44ff88",
|
||||
"historic=ruins", "#cc88ff",
|
||||
"natural=cave_entrance", "#ffaa00",
|
||||
"ford=yes", "#00ccff",
|
||||
"#ffffff"
|
||||
"natural=peak", "#e63946",
|
||||
"natural=water", "#1d7fc4",
|
||||
"tourism=viewpoint", "#2a9d2a",
|
||||
"historic=ruins", "#9b59b6",
|
||||
"natural=cave_entrance", "#e67e22",
|
||||
"ford=yes", "#0099cc",
|
||||
"#888888"
|
||||
],
|
||||
"circle-stroke-color": "#1a1a2e",
|
||||
"circle-stroke-color": "#ffffff",
|
||||
"circle-stroke-width": 1.5,
|
||||
"circle-opacity": 0.9
|
||||
}
|
||||
@@ -176,8 +128,8 @@
|
||||
"text-optional": true
|
||||
},
|
||||
"paint": {
|
||||
"text-color": "#ffffff",
|
||||
"text-halo-color": "#1a1a2e",
|
||||
"text-color": "#333333",
|
||||
"text-halo-color": "#ffffff",
|
||||
"text-halo-width": 1.5
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user