Centrer la carte sur le GPS du téléphone (HTTPS terminé par le proxy)
Le bouton ⌖ recentre la carte sur la position GPS du téléphone. L'API Geolocation exige un contexte sécurisé : la webapp sert désormais du HTTP et le TLS est porté par le reverse proxy (Traefik, labels dans l'override local) ; le webapp est transparent au proxy (URLs relatives, X-Forwarded-For pour la restriction LIDAR_REGEN_CIDR). Ajoute le modèle d'override docker-compose.webapp.override.yml.example, un rebuild d'index à la demande et met à jour doc/déploiement ; les tuiles sont servies à la demande.
This commit is contained in:
@ -1332,9 +1332,11 @@ _HTML_TEMPLATE = """<!DOCTYPE html>
|
||||
<button id="zoomIn" title="Zoomer">+</button>
|
||||
<button id="zoomOut" title="Dézoomer">−</button>
|
||||
<button id="zoomReset" title="Recadrer sur l'ensemble des tuiles">⤢</button>
|
||||
<button id="btnGps" title="Centrer la carte sur la position GPS du téléphone">⌖</button>
|
||||
</nav>
|
||||
|
||||
<div id="zoomIndicator" class="chip">Zoom</div>
|
||||
<div id="gpsToast" class="chip" hidden></div>
|
||||
<div id="hint" class="chip">
|
||||
Molette : zoom · Clic-glisser : déplacer<br>
|
||||
Clic sur tuile : infos de génération
|
||||
@ -1618,6 +1620,7 @@ body.zooming .ltile { will-change: transform; }
|
||||
#mapTools { top: 12px; right: 12px; display: flex; gap: 6px; padding: 6px; }
|
||||
#mapTools button { width: 32px; height: 32px; padding: 0; font-size: 16px; }
|
||||
#btnMenu { display: none; }
|
||||
#btnGps.busy { color: var(--accent); border-color: var(--accent); }
|
||||
|
||||
/* --- Puces d'état (bas d'écran) --- */
|
||||
.chip {
|
||||
@ -1627,6 +1630,10 @@ body.zooming .ltile { will-change: transform; }
|
||||
font-size: 11px; color: var(--muted);
|
||||
}
|
||||
#zoomIndicator { bottom: 12px; left: var(--left-ui); font-variant-numeric: tabular-nums; }
|
||||
#gpsToast {
|
||||
top: 54px; left: 50%; transform: translateX(-50%);
|
||||
max-width: 80vw; text-align: center; line-height: 1.5;
|
||||
}
|
||||
#hint { bottom: 12px; right: 12px; text-align: right; line-height: 1.5; }
|
||||
|
||||
/* --- Onglet Génération --- */
|
||||
@ -2351,6 +2358,53 @@ document.getElementById('zoomReset').addEventListener('click', () => {
|
||||
history.replaceState(null, '', location.pathname + location.search);
|
||||
if (gridBounds) map.fitBounds(gridBounds, { padding: [24, 24] });
|
||||
});
|
||||
|
||||
// ============================================================
|
||||
// Position GPS du téléphone : centrer la carte dessus
|
||||
// ============================================================
|
||||
const gpsBtn = document.getElementById('btnGps');
|
||||
const gpsToast = document.getElementById('gpsToast');
|
||||
let gpsToastTimer = null;
|
||||
let gpsMarker = null;
|
||||
function gpsMsg(text, ms) {
|
||||
gpsToast.textContent = text;
|
||||
gpsToast.hidden = false;
|
||||
clearTimeout(gpsToastTimer);
|
||||
gpsToastTimer = setTimeout(() => { gpsToast.hidden = true; }, ms || 4000);
|
||||
}
|
||||
gpsBtn.addEventListener('click', () => {
|
||||
if (!('geolocation' in navigator)) {
|
||||
gpsMsg('Géolocalisation non prise en charge par ce navigateur ' +
|
||||
'(connexion sécurisée HTTPS requise).', 6000);
|
||||
return;
|
||||
}
|
||||
gpsBtn.classList.add('busy');
|
||||
try {
|
||||
navigator.geolocation.getCurrentPosition(pos => {
|
||||
gpsBtn.classList.remove('busy');
|
||||
const ll = [pos.coords.latitude, pos.coords.longitude];
|
||||
map.setView(ll, Math.max(map.getZoom(), 16));
|
||||
if (gpsMarker) gpsMarker.remove();
|
||||
gpsMarker = L.circleMarker(ll, {
|
||||
radius: 5, color: '#39d98a', fillColor: '#39d98a', fillOpacity: 0.9
|
||||
}).addTo(map);
|
||||
if (pos.coords.accuracy) {
|
||||
gpsMarker.bindTooltip('Précision GPS ~' + Math.round(pos.coords.accuracy) + ' m');
|
||||
}
|
||||
gpsMsg('Carte centrée sur votre position GPS.');
|
||||
}, err => {
|
||||
gpsBtn.classList.remove('busy');
|
||||
let why = 'Aucune position reçue';
|
||||
if (err && err.code === 1) why = 'Autorisation de localisation refusée';
|
||||
else if (err && err.code === 2) why = 'Position momentanément indisponible';
|
||||
else if (err && err.code === 3) why = 'Délai de localisation dépassé';
|
||||
gpsMsg(why + '.', 6000);
|
||||
}, { enableHighAccuracy: true, timeout: 15000, maximumAge: 60000 });
|
||||
} catch (e) {
|
||||
gpsBtn.classList.remove('busy');
|
||||
gpsMsg('La géolocalisation exige une connexion sécurisée (HTTPS).', 6000);
|
||||
}
|
||||
});
|
||||
// will-change pendant l'animation de zoom uniquement : le scaling passe sur
|
||||
// le GPU, et les couches sont libérées juste après (mémoire vidéo).
|
||||
let zoomGpuTimer = null;
|
||||
|
||||
Reference in New Issue
Block a user