From c8349a3171f1f65a818b199b1fb29d0b9d2ed94b Mon Sep 17 00:00:00 2001 From: kdeterme Date: Fri, 25 Sep 2026 14:23:51 +0200 Subject: [PATCH] feat: add continuous GPS tracking, pulsing location marker, and distance calculation to inspection maps --- loko/assets/static/assets/inspection_map.js | 74 +++++-- .../components/asset_fields/_naturerwiz.html | 181 ++++++++++++++++-- .../inspections/quick_inspection_map.html | 31 ++- 3 files changed, 255 insertions(+), 31 deletions(-) diff --git a/loko/assets/static/assets/inspection_map.js b/loko/assets/static/assets/inspection_map.js index e9dfb3a..870ce36 100644 --- a/loko/assets/static/assets/inspection_map.js +++ b/loko/assets/static/assets/inspection_map.js @@ -290,7 +290,7 @@ 'match', ['coalesce', ['get', 'structure_type'], ''], ['massif drainant', 'tranchee drainante', 'drain', 'collecteur en beton poreux', 'reseau egout separatif', 'oed'], '#8d6e63', ['noue', 'jardin de pluie', 'bande filtrante', 'evc'], '#009688', - ['filet eau', 'caniveau infiltrant', 'gargouille de trottoir', 'avaloir de rail', 'trop plein', 'ta'], '#0277bd', + ['filet eau', 'caniveau infiltrant', 'gargouille de trottoir', 'avaloir de rail', 'trop plein', 'ta'], '#455a64', '#0288d1' ], 'line-width': 2.5 @@ -343,11 +343,13 @@ ['==', ['coalesce', ['get', 'status'], ''], 'inactive'], '#343a40', '#198754' ], + ['match', ['coalesce', ['get', 'model_name'], ''], ['roaddrain', 'drain'], true, false], '#37474f', [ 'match', ['coalesce', ['get', 'structure_type'], ''], ['massif drainant', 'tranchee drainante', 'oed'], '#8d6e63', ['jardin de pluie', 'noue', 'evc'], '#009688', ['bassin orage', 'citerne', 'ta'], '#0277bd', + ['avaloir', 'avaloir de rail', 'bouche injection', 'filet eau', 'caniveau infiltrant', 'gargouille de trottoir', 'puits infiltration', 'drain', 'regard', 'vanne regulation', 'vanne isolation'], '#37474f', '#0288d1' ] ], @@ -731,51 +733,99 @@ }, 400); } + let gpsWatchId = null; + let isFirstGpsFix = true; + function setupGeolocation() { const gpsBtn = document.getElementById('btn-gps-locate'); if (gpsBtn) { - gpsBtn.addEventListener('click', locateUser); + gpsBtn.addEventListener('click', function() { + if (window.currentGpsPosition && map) { + map.flyTo({ + center: [window.currentGpsPosition.lng, window.currentGpsPosition.lat], + zoom: 18, + duration: 800 + }); + } else { + startGpsTracking(true); + } + }); } const hasPreselect = (window.PRESELECT_LON !== null && window.PRESELECT_LAT !== null && typeof window.PRESELECT_LON !== 'undefined' && typeof window.PRESELECT_LAT !== 'undefined'); - if (!hasPreselect) { - locateUser(); + startGpsTracking(!hasPreselect); + + window.addEventListener('beforeunload', stopGpsTracking); + } + + function stopGpsTracking() { + if (gpsWatchId !== null && navigator.geolocation) { + navigator.geolocation.clearWatch(gpsWatchId); + gpsWatchId = null; } } function locateUser() { + startGpsTracking(true); + } + + function startGpsTracking(shouldFlyTo = false) { if (!navigator.geolocation) return; const i18n = window.INSPECTION_MAP_I18N || {}; const statusText = document.getElementById('gps-status-text'); if (statusText) statusText.textContent = i18n.geolocation || (typeof gettext !== 'undefined' ? gettext('Géolocalisation...') : 'Géolocalisation...'); - navigator.geolocation.getCurrentPosition( + if (gpsWatchId !== null) { + navigator.geolocation.clearWatch(gpsWatchId); + gpsWatchId = null; + } + + gpsWatchId = navigator.geolocation.watchPosition( function(position) { const lng = position.coords.longitude; const lat = position.coords.latitude; + const accuracy = position.coords.accuracy; + + window.currentGpsPosition = { lng, lat, accuracy, timestamp: Date.now() }; + + try { + window.dispatchEvent(new CustomEvent('loko:gps-update', { + detail: window.currentGpsPosition + })); + } catch(e) {} if (typeof maplibregl !== 'undefined' && maplibregl.Marker && map) { if (userMarker) { userMarker.setLngLat([lng, lat]); } else { const el = document.createElement('div'); - el.className = 'user-gps-dot'; + el.className = 'user-gps-dot-container'; + el.innerHTML = '
'; - userMarker = new maplibregl.Marker({ element: el }) + userMarker = new maplibregl.Marker({ element: el, anchor: 'center' }) .setLngLat([lng, lat]) .addTo(map); } - map.flyTo({ center: [lng, lat], zoom: 17 }); + if (shouldFlyTo || isFirstGpsFix) { + map.flyTo({ center: [lng, lat], zoom: 18, duration: 1000 }); + isFirstGpsFix = false; + shouldFlyTo = false; + } + } + + if (statusText) { + statusText.textContent = i18n.positionUpdated || (typeof gettext !== 'undefined' ? gettext('Position actualisée') : 'Position actualisée'); } - if (statusText) statusText.textContent = i18n.positionUpdated || (typeof gettext !== 'undefined' ? gettext('Position actualisée') : 'Position actualisée'); }, function(err) { - console.warn('GPS error:', err.message); - if (statusText) statusText.textContent = i18n.gpsUnavailable || (typeof gettext !== 'undefined' ? gettext('GPS indisponible') : 'GPS indisponible'); + console.warn('GPS watchPosition error:', err.message); + if (statusText) { + statusText.textContent = i18n.gpsUnavailable || (typeof gettext !== 'undefined' ? gettext('GPS indisponible') : 'GPS indisponible'); + } }, - { enableHighAccuracy: true, timeout: 10000, maximumAge: 5000 } + { enableHighAccuracy: true, timeout: 10000, maximumAge: 2000 } ); } diff --git a/loko/inspections/templates/inspections/components/asset_fields/_naturerwiz.html b/loko/inspections/templates/inspections/components/asset_fields/_naturerwiz.html index 9dfe0df..738ad5e 100644 --- a/loko/inspections/templates/inspections/components/asset_fields/_naturerwiz.html +++ b/loko/inspections/templates/inspections/components/asset_fields/_naturerwiz.html @@ -95,8 +95,12 @@ -
+
+ +