From f1145a4dbb4b163dc5114c637f9e18def010cd12 Mon Sep 17 00:00:00 2001 From: Mathieu Moran Date: Mon, 10 Aug 2026 02:43:23 -0400 Subject: [PATCH] feat(map): reverse geocode location + recenter button on dashboard and history - Dashboard: recenter button in GPS card header (hidden until first fix) - Dashboard: estimated location name under lat/lon via Nominatim reverse geocode (throttled 30s) - History: recenter button fits map back to trip bounds - Both buttons use crosshairs icon, hidden until data is available Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- static/js/history.js | 9 +++++++++ static/js/yaris.js | 33 +++++++++++++++++++++++++++++++++ templates/history.html | 7 ++++++- templates/index.html | 24 +++++++++++++++--------- 4 files changed, 63 insertions(+), 10 deletions(-) diff --git a/static/js/history.js b/static/js/history.js index ac1eea0..0810776 100644 --- a/static/js/history.js +++ b/static/js/history.js @@ -21,6 +21,7 @@ let replayPoints = []; let replayTimer = null; let replayController = null; + const recenterBtn = document.getElementById("btn-recenter-history"); function formatDuration(seconds) { const hours = Math.floor(seconds / 3600); @@ -132,6 +133,14 @@ } else if (coordinates.length === 1) { map.setView(coordinates[0], 15); } + recenterBtn.hidden = false; + recenterBtn.onclick = function () { + if (coordinates.length > 1) { + map.fitBounds(routeLayer.getBounds(), { padding: [24, 24] }); + } else if (coordinates.length === 1) { + map.setView(coordinates[0], 15); + } + }; summary.textContent = `${formatDistance(result.trip.distance_km)} · ` + `${formatDuration(result.trip.duration_seconds)} · ` + `avg ${formatSpeed(result.trip.average_speed_kmh)} · ` diff --git a/static/js/yaris.js b/static/js/yaris.js index c6b867f..7fa77c8 100644 --- a/static/js/yaris.js +++ b/static/js/yaris.js @@ -185,6 +185,31 @@ socket.on('obd_error', function (data) { }); let mapCentered = false; +let _lastLat = null, _lastLon = null; +let _geocodeTimer = null; + +const _recenterBtn = document.getElementById('btn-recenter'); +_recenterBtn.addEventListener('click', function () { + if (_lastLat != null && _lastLon != null) { + map.setView([_lastLat, _lastLon], 15); + } +}); + +function reverseGeocode(lat, lon) { + fetch(`https://nominatim.openstreetmap.org/reverse?format=jsonv2&lat=${lat}&lon=${lon}`, { + headers: { 'Accept-Language': 'en' } + }) + .then(r => r.ok ? r.json() : null) + .then(data => { + if (!data) return; + const a = data.address || {}; + const name = a.road || a.hamlet || a.village || a.suburb || ''; + const city = a.city || a.town || a.village || a.municipality || ''; + const label = [name, city].filter(Boolean).join(', '); + document.getElementById('gps-location-name').textContent = label || data.display_name?.split(',').slice(0,2).join(',') || ''; + }) + .catch(() => {}); +} socket.on('gps_location', function(data) { console.log("location received") @@ -204,6 +229,14 @@ socket.on('gps_location', function(data) { map.setView([lat, lon], 15); mapCentered = true; } + _lastLat = lat; + _lastLon = lon; + _recenterBtn.hidden = false; + // Throttle geocode to at most once every 30s + if (!_geocodeTimer) { + reverseGeocode(lat, lon); + _geocodeTimer = setTimeout(() => { _geocodeTimer = null; }, 30000); + } } }); diff --git a/templates/history.html b/templates/history.html index 2909a63..41436cf 100644 --- a/templates/history.html +++ b/templates/history.html @@ -46,7 +46,12 @@
Route replay
- Select a trip +
+ + Select a trip +
diff --git a/templates/index.html b/templates/index.html index 6ab91b4..1d7c3c5 100644 --- a/templates/index.html +++ b/templates/index.html @@ -95,21 +95,27 @@
-
+
Live GPS
+
-