auto-sync: 2026-05-02 08:40:01

This commit is contained in:
Stream
2026-05-02 08:40:01 +03:00
parent e03d82a3f2
commit 11137f12b5
3 changed files with 129 additions and 146 deletions

View File

@@ -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:

View File

@@ -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>

View File

@@ -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
}
}