From 62cea59ef86dcb5b1da17df3906b4086b96ccc2b Mon Sep 17 00:00:00 2001 From: owain Date: Tue, 23 Jun 2026 12:08:33 +0100 Subject: [PATCH] feat: taller route tiles (h-46), labels overlay above route line for more place names, renamed auto routes become custom (rename clears auto_detected + startup backfill) --- backend/app/api/routes.py | 3 +++ backend/app/main.py | 14 ++++++++++++++ frontend/src/components/ui/RouteTileMap.jsx | 14 +++++++++++--- frontend/src/pages/RoutesPage.jsx | 2 +- 4 files changed, 29 insertions(+), 4 deletions(-) diff --git a/backend/app/api/routes.py b/backend/app/api/routes.py index 7d9650d..aeed46a 100644 --- a/backend/app/api/routes.py +++ b/backend/app/api/routes.py @@ -179,6 +179,9 @@ async def update_route( raise HTTPException(status_code=404, detail="Route not found") if body.name is not None and body.name.strip(): route.name = body.name.strip() + # A user-given name makes this a custom route (moves it out of the + # auto-detected group on the routes page). + route.auto_detected = False if body.sport_type is not None: route.sport_type = body.sport_type await db.commit() diff --git a/backend/app/main.py b/backend/app/main.py index fac75fb..d9c1ce0 100644 --- a/backend/app/main.py +++ b/backend/app/main.py @@ -189,6 +189,20 @@ async def init_db(): except Exception as e: print(f"avg_speed_ms fix skipped: {e}") + # An auto-detected route the user has renamed should count as a custom route. + # Auto names are generated as " route
"; any auto_detected + # route whose name no longer matches that pattern has been renamed → mark it + # custom so it groups with user-named routes. + try: + async with engine.begin() as conn: + await conn.execute(text( + "UPDATE named_routes SET auto_detected = false " + "WHERE auto_detected = true " + "AND name !~ '^.+ route [0-9]{1,2} [A-Za-z]{3} [0-9]{4}$'" + )) + except Exception as e: + print(f"route auto_detected backfill skipped: {e}") + # Seed admin user (only if password is configured) if not settings.admin_password: print("ADMIN_PASSWORD not set - skipping admin user seed") diff --git a/frontend/src/components/ui/RouteTileMap.jsx b/frontend/src/components/ui/RouteTileMap.jsx index 417d687..6dd8fea 100644 --- a/frontend/src/components/ui/RouteTileMap.jsx +++ b/frontend/src/components/ui/RouteTileMap.jsx @@ -2,8 +2,11 @@ import { useEffect, useRef } from 'react' import L from 'leaflet' import { sportColor } from '../../utils/format' -// Voyager raster tiles — same street style used on the activity map. -const TILE_URL = 'https://{s}.basemaps.cartocdn.com/rastertiles/voyager/{z}/{x}/{y}{r}.png' +// Voyager raster tiles — same street style used on the activity map. We split +// the basemap (no labels) from a labels-only overlay so place names can be drawn +// *above* the route line instead of being hidden underneath it. +const BASE_URL = 'https://{s}.basemaps.cartocdn.com/rastertiles/voyager_nolabels/{z}/{x}/{y}{r}.png' +const LABELS_URL = 'https://{s}.basemaps.cartocdn.com/rastertiles/voyager_only_labels/{z}/{x}/{y}{r}.png' function decodePolyline(encoded) { if (!encoded) return [] @@ -38,7 +41,12 @@ export default function RouteTileMap({ polyline, sportType, className = '' }) { zoomSnap: 0, }) mapRef.current = map - L.tileLayer(TILE_URL, { maxZoom: 19 }).addTo(map) + L.tileLayer(BASE_URL, { maxZoom: 19 }).addTo(map) + // A dedicated high-z pane keeps the labels overlay on top of the route line. + map.createPane('labels') + map.getPane('labels').style.zIndex = 650 + map.getPane('labels').style.pointerEvents = 'none' + L.tileLayer(LABELS_URL, { maxZoom: 19, pane: 'labels' }).addTo(map) return () => { map.remove(); mapRef.current = null } }, []) diff --git a/frontend/src/pages/RoutesPage.jsx b/frontend/src/pages/RoutesPage.jsx index dfc5917..2456f58 100644 --- a/frontend/src/pages/RoutesPage.jsx +++ b/frontend/src/pages/RoutesPage.jsx @@ -268,7 +268,7 @@ export default function RoutesPage() { isSelected ? '' : 'bg-gray-900 border-gray-800 hover:border-gray-600' }`}> + className="w-full h-[11.5rem] rounded-lg overflow-hidden" />

{route.name}

{formatDistance(route.distance_m, unit)}