Remplacer la fiche de dalle flottante par une bulle et une fiche détaillée dans le panneau
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@ -467,6 +467,17 @@ function fmtDate(v, withTime) {
|
||||
return d.toLocaleString('fr-FR', opt);
|
||||
}
|
||||
|
||||
function fmtDensity(q) {
|
||||
if (!q || q.ground_density == null) return 'non renseignée';
|
||||
return q.ground_density.toFixed(q.ground_density < 10 ? 1 : 0).replace('.', ',') + ' pts sol/m²';
|
||||
}
|
||||
|
||||
function fmtAcq(q) {
|
||||
if (!q || !q.acq_start) return 'non renseignée';
|
||||
return q.acq_end && q.acq_end.slice(0, 10) !== q.acq_start.slice(0, 10)
|
||||
? fmtDate(q.acq_start) + ' → ' + fmtDate(q.acq_end) : fmtDate(q.acq_start);
|
||||
}
|
||||
|
||||
function kv(rows) {
|
||||
return '<dl class="info-kv">' + rows.filter(r => r[1] != null && r[1] !== '')
|
||||
.map(r => '<dt>' + r[0] + '</dt><dd>' + r[1] + '</dd>').join('') + '</dl>';
|
||||
@ -523,7 +534,7 @@ function calageSection(c) {
|
||||
|
||||
function renderTileCard(data, ign) {
|
||||
const regenBtn = (GEN.available && GEN.allowed)
|
||||
? '<div class="card-foot"><button id="tileRegen" class="btn" title="Ajouter cette dalle à la sélection de génération (téléchargement IGN + traitement)">↻ ' + (data.found ? 'Régénérer' : 'Générer') + ' cette dalle</button></div>'
|
||||
? '<div class="row"><button id="tileRegen" class="btn" title="Ajouter cette dalle à la sélection de génération (téléchargement IGN + traitement)">↻ ' + (data.found ? 'Régénérer' : 'Générer') + ' cette dalle</button></div>'
|
||||
: '';
|
||||
el('tileTitle').textContent = 'Dalle ' + data.col + ' · ' + data.row;
|
||||
const bb = data.l93_bbox || [];
|
||||
@ -551,7 +562,6 @@ function renderTileCard(data, ign) {
|
||||
el('tileBody').innerHTML = html + regenBtn;
|
||||
const btn = el('tileRegen');
|
||||
if (btn) btn.addEventListener('click', genAddCellFromQuery);
|
||||
el('tilecard').hidden = false;
|
||||
}
|
||||
|
||||
function showTileInfo(data, latlng) {
|
||||
@ -559,10 +569,8 @@ function showTileInfo(data, latlng) {
|
||||
if (!data) {
|
||||
el('tileTitle').textContent = 'Dalle';
|
||||
el('tileBody').innerHTML = '<div class="meta">Serveur injoignable : réessayez dans un instant.</div>';
|
||||
el('tilecard').hidden = false;
|
||||
return;
|
||||
}
|
||||
selectTileOutline(data);
|
||||
renderTileCard(data, null);
|
||||
// Fiche IGN à part : la sélection s'affiche sans attendre le catalogue.
|
||||
const key = data.col + '_' + data.row;
|
||||
@ -573,6 +581,54 @@ function showTileInfo(data, latlng) {
|
||||
.then(ign => { if (showTileInfo.current === key) renderTileCard(data, ign); });
|
||||
}
|
||||
|
||||
let tilePopup = null;
|
||||
let tileReturnTab = null; // onglet à rouvrir après « ← Retour »
|
||||
|
||||
function showTilePopup(data, latlng) {
|
||||
lastTileQuery = latlng || lastTileQuery;
|
||||
if (tilePopup) tilePopup.remove();
|
||||
const html = !data
|
||||
? '<div class="tile-pop">Serveur injoignable : réessayez dans un instant.</div>'
|
||||
: '<div class="tile-pop"><b>Dalle ' + data.col + ' · ' + data.row + '</b><br>'
|
||||
+ (data.found ? '' : 'Pas encore de rendu<br>')
|
||||
+ 'Acquisition : ' + fmtAcq(data.quality) + '<br>'
|
||||
+ 'Densité : ' + fmtDensity(data.quality)
|
||||
+ '<div class="row"><button class="btn" id="tilePopDetails">Détails</button></div></div>';
|
||||
tilePopup = L.popup({ maxWidth: 260, autoPanPaddingTopLeft: [isPhone() ? 12 : el('panel').getBoundingClientRect().right + 12, 12] })
|
||||
.setLatLng(latlng).setContent(html).openOn(map);
|
||||
if (!data) return;
|
||||
selectTileOutline(data);
|
||||
showTilePopup.data = data;
|
||||
const btn = el('tilePopDetails');
|
||||
if (btn) btn.addEventListener('click', openTileDetails);
|
||||
tilePopup.on('remove', () => { if (el('tileView').hidden && tileSel) { map.removeLayer(tileSel); tileSel = null; } });
|
||||
}
|
||||
|
||||
function openTileDetails() {
|
||||
const data = showTilePopup.data;
|
||||
if (!data) return;
|
||||
if (tilePopup) { tilePopup.off('remove'); tilePopup.remove(); tilePopup = null; }
|
||||
tileReturnTab = PANEL.tab;
|
||||
for (const t of ['view', 'print', 'gen', 'share']) el('tab-' + t).hidden = true;
|
||||
el('tileView').hidden = false;
|
||||
if (isPhone() && PANEL.sheet === 'closed') setSheet('half');
|
||||
if (!isPhone() && PANEL.collapsed) setCollapsed(false);
|
||||
showTileInfo(data, lastTileQuery);
|
||||
}
|
||||
|
||||
// Ferme la bulle, sinon la fiche ; renvoie true si quelque chose a été fermé.
|
||||
function closeTileViews() {
|
||||
if (tilePopup) { tilePopup.remove(); tilePopup = null; return true; }
|
||||
if (!el('tileView').hidden) {
|
||||
el('tileView').hidden = true;
|
||||
if (tileSel) { map.removeLayer(tileSel); tileSel = null; }
|
||||
openTab(tileReturnTab || 'view');
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
el('tileBack').addEventListener('click', closeTileViews);
|
||||
|
||||
// --- rose des vents du relief orienté --------------------------------------
|
||||
// Mêmes couleurs que visualizations._relief_lut : teinte CIELAB = orientation
|
||||
// (aspect = orientation boussole + 90°, repère arctan2(dy, dx) du rendu),
|
||||
@ -761,8 +817,8 @@ function genAddCellFromQuery() {
|
||||
genApi('GET', 'api/cell?lat=' + lastTileQuery.lat + '&lng=' + lastTileQuery.lng)
|
||||
.then(d => {
|
||||
genAddCells([{ col: d.col, row: d.row, corners: d.corners }]);
|
||||
closeTileViews();
|
||||
genOpenCard('1 dalle sélectionnée — options puis « Générer ».');
|
||||
el('tilecard').hidden = true;
|
||||
})
|
||||
.catch(msg => toast('Échec : ' + msg.message, 6000));
|
||||
}
|
||||
@ -949,7 +1005,8 @@ el('genQueueClear').addEventListener('click', () =>
|
||||
// se déplace librement autour, on le fait glisser à la souris ou au doigt.
|
||||
// Géométrie exacte calculée par le serveur (projection Lambert 93).
|
||||
const PRINT_LS = 'lidar-print';
|
||||
const PRINT = { on: false, rect: null, handle: null, seq: 0, center: null, corners: null, drag: null };
|
||||
const PRINT = { on: false, rect: null, handle: null, seq: 0, center: null, corners: null, drag: null,
|
||||
dragging: false };
|
||||
map.createPane('printFrame').style.zIndex = '690';
|
||||
|
||||
function printSettings() {
|
||||
@ -1026,6 +1083,7 @@ function printRefreshFrame(fit) {
|
||||
// stables pendant le défilement automatique),
|
||||
// géométrie exacte redemandée au serveur au relâchement.
|
||||
function printDragStart() {
|
||||
PRINT.dragging = true;
|
||||
PRINT.drag = { start: map.latLngToLayerPoint(PRINT.handle.getLatLng()),
|
||||
corners: PRINT.corners.map(c => map.latLngToLayerPoint(c)) };
|
||||
}
|
||||
@ -1043,6 +1101,7 @@ function printDragEnd() {
|
||||
PRINT.corners = PRINT.rect.getLatLngs()[0];
|
||||
printSave();
|
||||
printRefreshFrame();
|
||||
setTimeout(() => { PRINT.dragging = false; }, 300);
|
||||
}
|
||||
|
||||
function printCenterHere() {
|
||||
@ -1140,7 +1199,9 @@ function panelRender() {
|
||||
b.hidden = !tabs.includes(b.dataset.tab);
|
||||
b.setAttribute('aria-selected', String(b.dataset.tab === PANEL.tab));
|
||||
});
|
||||
for (const t of ['view', 'print', 'gen', 'share']) el('tab-' + t).hidden = t !== PANEL.tab;
|
||||
if (el('tileView').hidden) {
|
||||
for (const t of ['view', 'print', 'gen', 'share']) el('tab-' + t).hidden = t !== PANEL.tab;
|
||||
}
|
||||
const panel = el('panel');
|
||||
// Repli en bande d'icônes : ordinateur seulement (le téléphone a son volet).
|
||||
const collapsed = PANEL.collapsed && !isPhone();
|
||||
@ -1157,6 +1218,7 @@ function panelRender() {
|
||||
|
||||
function openTab(tab) {
|
||||
if (!panelTabs().includes(tab)) return;
|
||||
el('tileView').hidden = true;
|
||||
const before = PANEL.sheet;
|
||||
// Onglet actif retouché sur téléphone : le volet change de hauteur.
|
||||
if (isPhone() && tab === PANEL.tab && PANEL.sheet !== 'closed') {
|
||||
@ -1216,9 +1278,6 @@ window.addEventListener('resize', panelRender); // hauteur du volet en vh (rot
|
||||
grip.addEventListener('pointercancel', end);
|
||||
})();
|
||||
|
||||
// Fiche de dalle fermée par Échap (provisoire : la Task 4 la remplace).
|
||||
function closeTileViews() { if (el('tilecard').hidden) return false; el('tilecard').hidden = true; return true; }
|
||||
|
||||
// --- démarrage ------------------------------------------------------------
|
||||
function fitData() {
|
||||
if (META.bounds) {
|
||||
@ -1258,7 +1317,7 @@ fetch('api/map/meta').then(r => r.json()).then(meta => {
|
||||
panelRender();
|
||||
if (show) { genUpdateStatus(d); genPollNow(); }
|
||||
}).catch(() => {});
|
||||
}).catch(() => toast('Serveur de tuiles injoignable.', 10000));
|
||||
}).catch(() => { toast('Serveur de tuiles injoignable.', 10000); panelRender(); });
|
||||
|
||||
let applyingHash = false;
|
||||
window.addEventListener('hashchange', () => {
|
||||
@ -1278,17 +1337,15 @@ window.addEventListener('hashchange', () => {
|
||||
|
||||
map.on('moveend zoomend', saveHashSoon);
|
||||
map.on('click', (e) => {
|
||||
if (genDrawing || Date.now() < genIgnoreClickUntil) return;
|
||||
// Pas de bulle pendant un tracé de zone ni juste après le relâchement de
|
||||
// la poignée du cadre d'export.
|
||||
if (genDrawing || Date.now() < genIgnoreClickUntil || PRINT.dragging) return;
|
||||
fetch('api/map/tile?lat=' + e.latlng.lat + '&lng=' + e.latlng.lng)
|
||||
.then(r => r.ok ? r.json() : null)
|
||||
.catch(() => null)
|
||||
.then(d => showTileInfo(d, e.latlng));
|
||||
.then(d => showTilePopup(d, e.latlng));
|
||||
});
|
||||
|
||||
el('tileClose').addEventListener('click', () => {
|
||||
el('tilecard').hidden = true;
|
||||
if (tileSel) { map.removeLayer(tileSel); tileSel = null; }
|
||||
});
|
||||
el('btnShare').addEventListener('click', () => {
|
||||
const url = location.origin + location.pathname + viewHash(true);
|
||||
history.replaceState(null, '', url);
|
||||
|
||||
Reference in New Issue
Block a user