From 11137f12b5ebd2eb83bb59ff8e3387751ee47935 Mon Sep 17 00:00:00 2001 From: Stream Date: Sat, 2 May 2026 08:40:01 +0300 Subject: [PATCH] auto-sync: 2026-05-02 08:40:01 --- tasks/enduro-trails/prototype/app.py | 87 ++++++++----- .../enduro-trails/prototype/static/index.html | 72 +++++------ .../enduro-trails/prototype/static/style.json | 116 +++++------------- 3 files changed, 129 insertions(+), 146 deletions(-) diff --git a/tasks/enduro-trails/prototype/app.py b/tasks/enduro-trails/prototype/app.py index 48f4e92..44e7918 100644 --- a/tasks/enduro-trails/prototype/app.py +++ b/tasks/enduro-trails/prototype/app.py @@ -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: diff --git a/tasks/enduro-trails/prototype/static/index.html b/tasks/enduro-trails/prototype/static/index.html index 2c1b646..0b1aabd 100644 --- a/tasks/enduro-trails/prototype/static/index.html +++ b/tasks/enduro-trails/prototype/static/index.html @@ -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; } @@ -282,7 +284,7 @@
Асфальт -
+
Вершина
diff --git a/tasks/enduro-trails/prototype/static/style.json b/tasks/enduro-trails/prototype/static/style.json index 3b81ea0..a83dd02 100644 --- a/tasks/enduro-trails/prototype/static/style.json +++ b/tasks/enduro-trails/prototype/static/style.json @@ -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 } }