feat(history): show reverse-geocoded location during trip scrubbing
- Location name (road + city) appears below time/speed in the timeline panel - Debounced 600ms so dragging the scrubber doesn't spam Nominatim - Deduped by ~3 decimal lat/lon so stationary replay doesn't re-fetch - Cleared when a new trip is loaded Co-authored-by: Copilot <[email protected]>
This commit is contained in:
@@ -22,6 +22,34 @@
|
|||||||
let replayTimer = null;
|
let replayTimer = null;
|
||||||
let replayController = null;
|
let replayController = null;
|
||||||
const recenterBtn = document.getElementById("btn-recenter-history");
|
const recenterBtn = document.getElementById("btn-recenter-history");
|
||||||
|
const replayLocation = document.getElementById("trip-replay-location");
|
||||||
|
|
||||||
|
let _geoTimer = null;
|
||||||
|
let _lastGeoKey = null;
|
||||||
|
|
||||||
|
function geocodePoint(lat, lon) {
|
||||||
|
const key = `${lat.toFixed(3)},${lon.toFixed(3)}`;
|
||||||
|
if (key === _lastGeoKey) return;
|
||||||
|
_lastGeoKey = key;
|
||||||
|
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 road = a.road || a.hamlet || a.village || a.suburb || '';
|
||||||
|
const city = a.city || a.town || a.village || a.municipality || '';
|
||||||
|
replayLocation.textContent = [road, city].filter(Boolean).join(', ')
|
||||||
|
|| data.display_name?.split(',').slice(0, 2).join(',') || '';
|
||||||
|
})
|
||||||
|
.catch(() => {});
|
||||||
|
}
|
||||||
|
|
||||||
|
function scheduleGeocode(lat, lon) {
|
||||||
|
clearTimeout(_geoTimer);
|
||||||
|
_geoTimer = setTimeout(() => geocodePoint(lat, lon), 600);
|
||||||
|
}
|
||||||
|
|
||||||
function formatDuration(seconds) {
|
function formatDuration(seconds) {
|
||||||
const hours = Math.floor(seconds / 3600);
|
const hours = Math.floor(seconds / 3600);
|
||||||
@@ -69,6 +97,7 @@
|
|||||||
replayTime.textContent = formatDate(point.datetime);
|
replayTime.textContent = formatDate(point.datetime);
|
||||||
const source = point.speed_source === "obd" ? "OBD wheel" : "GPS";
|
const source = point.speed_source === "obd" ? "OBD wheel" : "GPS";
|
||||||
replaySpeed.textContent = `${source} speed: ${formatSpeed(point.speed)}`;
|
replaySpeed.textContent = `${source} speed: ${formatSpeed(point.speed)}`;
|
||||||
|
scheduleGeocode(point.latitude, point.longitude);
|
||||||
}
|
}
|
||||||
|
|
||||||
function initializeTimeline(points) {
|
function initializeTimeline(points) {
|
||||||
@@ -77,6 +106,8 @@
|
|||||||
scrubber.max = Math.max(0, points.length - 1);
|
scrubber.max = Math.max(0, points.length - 1);
|
||||||
scrubber.value = 0;
|
scrubber.value = 0;
|
||||||
timeline.hidden = points.length === 0;
|
timeline.hidden = points.length === 0;
|
||||||
|
replayLocation.textContent = '';
|
||||||
|
_lastGeoKey = null;
|
||||||
if (vehicleMarker) {
|
if (vehicleMarker) {
|
||||||
vehicleMarker.remove();
|
vehicleMarker.remove();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -67,6 +67,7 @@
|
|||||||
<strong id="trip-replay-time"></strong>
|
<strong id="trip-replay-time"></strong>
|
||||||
<span id="trip-replay-speed" class="text-muted"></span>
|
<span id="trip-replay-speed" class="text-muted"></span>
|
||||||
</div>
|
</div>
|
||||||
|
<div id="trip-replay-location" class="text-muted mt-1" style="font-size:0.75rem;min-height:1rem"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user