// Onglet « Carte et corrections » : déplacer ou placer les photos et vidéos, // masquer des zones de trace. Chaque correction est enregistrée aussitôt dans // voyage.toml (ou site.toml pour une zone privée) ; les pages du site la // prennent en compte à la génération suivante. (function () { "use strict"; const racine = document.getElementById("editeur"); if (!racine || !window.L) return; const voyage = racine.dataset.voyage; const base = document.body.dataset.base || ""; // « /admin » derrière le serveur web const $ = id => document.getElementById(id); let carte = null, donnees = null, coucheTraces, coucheMedias, coucheZones; let brouillon = null, modeZone = false, aPlacer = null, cadree = false; // --- Échanges avec le serveur ------------------------------------------- function message(html) { const m = $("messages"); m.innerHTML = html; clearTimeout(window._rtgMessage); window._rtgMessage = setTimeout(() => { m.innerHTML = ""; }, 6000); } function lireReponse(r) { const redirection = r.headers.get("HX-Redirect"); if (redirection) { location.href = redirection; throw new Error("session"); } if (!(r.headers.get("Content-Type") || "").includes("json")) { return r.text().then(t => { message(t); throw new Error("refus"); }); } return r.json(); } function poster(url, params) { return fetch(base + url, { method: "POST", headers: { "HX-Request": "true", "Content-Type": "application/x-www-form-urlencoded" }, body: new URLSearchParams(Object.assign({ v: voyage }, params)) }).then(lireReponse); } function etat(texte) { $("ed-etat").textContent = texte; } function charger() { return fetch(base + "/voyage/carte.json?v=" + encodeURIComponent(voyage), { headers: { "HX-Request": "true" } }) .then(lireReponse) .then(d => { donnees = d; remplirJours(); dessinerTout(); }); } // --- Carte ---------------------------------------------------------------- function initialiser() { $("ed-carte").innerHTML = ""; carte = L.map("ed-carte"); const plan = L.tileLayer("https://tile.openstreetmap.org/{z}/{x}/{y}.png", { maxZoom: 19, attribution: "© OpenStreetMap" }).addTo(carte); const satellite = L.tileLayer( "https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}", { maxZoom: 19, attribution: "Esri World Imagery" }); L.control.layers({ "Plan": plan, "Satellite": satellite }).addTo(carte); carte.setView([46.5, 2.5], 5); coucheTraces = L.featureGroup().addTo(carte); coucheZones = L.featureGroup().addTo(carte); coucheMedias = L.featureGroup().addTo(carte); carte.on("click", clicCarte); $("ed-jour").addEventListener("change", () => { cadree = false; dessinerTout(); }); $("ed-photos").addEventListener("change", dessinerMedias); $("ed-videos").addEventListener("change", dessinerMedias); $("ed-nouvelle-zone").addEventListener("click", commencerZone); $("ed-zone-annuler").addEventListener("click", finirZone); $("ed-zone-ok").addEventListener("click", enregistrerZone); $("ed-rayon").addEventListener("input", () => { $("ed-rayon-texte").textContent = $("ed-rayon").value + " m"; if (brouillon) brouillon.setRadius(+$("ed-rayon").value); }); document.addEventListener("keydown", e => { if (e.key === "Escape") { aPlacer = null; finirZone(); mode(""); } }); document.body.addEventListener("tache-finie", () => charger()); charger(); } function remplirJours() { const choix = $("ed-jour"), actuel = choix.value; choix.length = 1; donnees.jours.forEach(j => choix.add(new Option(j.nom, j.jour))); choix.value = actuel; } function jourChoisi() { return $("ed-jour").value; } function dessinerTout() { dessinerTraces(); dessinerZones(); dessinerMedias(); if (!cadree) { const b = coucheTraces.getLayers().length ? coucheTraces.getBounds() : (coucheMedias.getLayers().length ? coucheMedias.getBounds() : null); if (b && b.isValid()) carte.fitBounds(b, { padding: [30, 30] }); cadree = true; } } function dessinerTraces() { coucheTraces.clearLayers(); const jour = jourChoisi(); donnees.couches.forEach(c => { if (jour && c.jour !== jour) return; c.segments.forEach(s => L.polyline(s, { color: c.couleur, weight: 4, opacity: 0.85, interactive: false }) .addTo(coucheTraces)); }); } function icone(m) { return L.divIcon({ className: "", iconSize: [16, 16], iconAnchor: [8, 8], html: '' }); } function popup(m) { const d = document.createElement("div"); d.className = "ed-popup"; if (m.vignette) { const img = document.createElement("img"); img.src = m.vignette; img.alt = ""; img.loading = "lazy"; d.append(img); } const t = document.createElement("div"); t.textContent = m.fichier + " · " + m.jour + " " + m.heure; d.append(t); if (m.deplace) { const b = document.createElement("button"); b.className = "petit"; b.textContent = "Revenir à la position d'origine"; b.addEventListener("click", () => { carte.closePopup(); enregistrerPosition(m.fichier, "", ""); }); d.append(b); } return d; } function dessinerMedias() { coucheMedias.clearLayers(); const jour = jourChoisi(), photos = $("ed-photos").checked, videos = $("ed-videos").checked; const sans = []; donnees.medias.forEach(m => { if (jour && m.jour !== jour) return; if ((m.type === "photo" && !photos) || (m.type === "video" && !videos)) return; if (m.lat === null) { sans.push(m); return; } L.marker([m.lat, m.lon], { icon: icone(m), draggable: true, title: m.fichier }) .bindPopup(() => popup(m), { minWidth: 200 }) .on("dragend", e => { const p = e.target.getLatLng(); enregistrerPosition(m.fichier, p.lat.toFixed(6), p.lng.toFixed(6)); }) .addTo(coucheMedias); }); const liste = $("ed-sans"); liste.innerHTML = ""; $("ed-nb-sans").textContent = sans.length; sans.forEach(m => { const li = document.createElement("li"); const a = document.createElement("a"); a.href = "#carte"; a.textContent = (m.type === "photo" ? "📷 " : "🎬 ") + m.fichier; a.title = m.jour + " " + m.heure; a.addEventListener("click", e => { e.preventDefault(); finirZone(); aPlacer = m.fichier; mode("Cliquez sur la carte pour placer " + m.fichier + " (Échap pour annuler)"); }); const quand = document.createElement("span"); quand.className = "discret"; quand.textContent = " " + m.jour + " " + m.heure; li.append(a, quand); liste.append(li); }); if (!sans.length) liste.innerHTML = '
  • Aucun
  • '; } function enregistrerPosition(fichier, lat, lon) { poster("/voyage/position", { fichier: fichier, lat: lat, lon: lon }).then(r => { if (r.media) { const i = donnees.medias.findIndex(m => m.fichier === fichier); if (i >= 0) donnees.medias[i] = r.media; } dessinerMedias(); etat("✓ " + fichier + (lat ? " placé" : " remis à sa position d'origine") + ". Régénérez le voyage pour mettre le site à jour."); }).catch(() => dessinerMedias()); } // --- Zones masquées ------------------------------------------------------- function dessinerZones() { coucheZones.clearLayers(); const liste = $("ed-zones"); liste.innerHTML = ""; const ajouter = (z, i, portee) => { const site = portee === "site"; const cercle = L.circle([z[0], z[1]], { radius: z[2], color: site ? "#7c3aed" : "#dc2626", weight: 2, fillOpacity: 0.15, dashArray: site ? "6 6" : null }).addTo(coucheZones); const li = document.createElement("li"); const a = document.createElement("a"); a.href = "#carte"; a.textContent = (site ? "Zone privée" : "Zone") + " · " + z[2] + " m"; a.addEventListener("click", e => { e.preventDefault(); carte.fitBounds(cercle.getBounds(), { maxZoom: 16 }); }); const b = document.createElement("button"); b.className = "petit"; b.textContent = "Supprimer"; b.addEventListener("click", () => { poster("/voyage/masque", { action: "supprimer", portee: portee, index: i }).then(r => { donnees.masques = r.masques; donnees.zones = r.zones; dessinerZones(); etat("✓ Zone supprimée. Régénérez " + (site ? "les voyages" : "le voyage") + " pour mettre le site à jour."); }); }); li.append(a, " ", b); if (site) li.title = "Retirée de tous les voyages (site.toml)"; liste.append(li); }; donnees.masques.forEach((z, i) => ajouter(z, i, "voyage")); donnees.zones.forEach((z, i) => ajouter(z, i, "site")); } function commencerZone() { aPlacer = null; modeZone = true; mode("Cliquez au centre de la zone à masquer ; un nouveau clic la déplace."); } function finirZone() { modeZone = false; if (brouillon) { brouillon.remove(); brouillon = null; } $("ed-brouillon").hidden = true; if (!aPlacer) mode(""); } function enregistrerZone() { if (!brouillon) return; const c = brouillon.getLatLng(), site = $("ed-portee-site").checked; poster("/voyage/masque", { action: "ajouter", portee: site ? "site" : "voyage", lat: c.lat.toFixed(6), lon: c.lng.toFixed(6), rayon: $("ed-rayon").value }).then(r => { donnees.masques = r.masques; donnees.zones = r.zones; finirZone(); dessinerZones(); etat("✓ Zone enregistrée. Régénérez " + (site ? "les voyages" : "le voyage") + " pour mettre le site à jour."); }); } function clicCarte(e) { if (aPlacer) { const nom = aPlacer; aPlacer = null; mode(""); enregistrerPosition(nom, e.latlng.lat.toFixed(6), e.latlng.lng.toFixed(6)); } else if (modeZone) { if (!brouillon) { brouillon = L.circle(e.latlng, { radius: +$("ed-rayon").value, color: "#dc2626", weight: 2, dashArray: "4 4", fillOpacity: 0.2 }).addTo(carte); } else { brouillon.setLatLng(e.latlng); } $("ed-brouillon").hidden = false; mode("Ajustez le rayon puis enregistrez ; un clic sur la carte déplace la zone."); } } function mode(texte) { const m = $("ed-mode"); m.textContent = texte; m.hidden = !texte; $("ed-carte").classList.toggle("en-mode", !!texte); } // L'onglet peut être caché au chargement : on prépare la carte à sa première ouverture. document.addEventListener("rtg:onglet", e => { if (e.detail !== "carte") return; if (!carte) initialiser(); else carte.invalidateSize(); }); if (location.hash === "#carte") setTimeout(() => { if (!carte) initialiser(); }, 0); })();