++ Prima versione del designer piantine + Skill grafica
This commit is contained in:
143
resources/js/piantine.js
Normal file
143
resources/js/piantine.js
Normal file
@@ -0,0 +1,143 @@
|
||||
// Editor delle piantine (pagina /piantine, solo amministratore).
|
||||
//
|
||||
// Mappa Leaflet su tessere OpenStreetMap: mostra l'area del KML, i poligoni
|
||||
// dei territori già salvati e permette di disegnarne di nuovi con
|
||||
// leaflet-draw. Parla con il componente Livewire attraverso eventi:
|
||||
// JS → Livewire: 'poligono-disegnato', 'territorio-selezionato-mappa'
|
||||
// Livewire → JS: 'piantine-dati-aggiornati'
|
||||
|
||||
import L from 'leaflet';
|
||||
import 'leaflet/dist/leaflet.css';
|
||||
import 'leaflet-draw';
|
||||
import 'leaflet-draw/dist/leaflet.draw.css';
|
||||
|
||||
const STILE_AREA = { color: '#2563eb', weight: 2, dashArray: '8 6', fill: false, interactive: false };
|
||||
const STILE_SALVATO = { color: '#6b7280', weight: 2, fillColor: '#6b7280', fillOpacity: 0.12 };
|
||||
const STILE_SELEZIONATO = { color: '#4f46e5', weight: 3, fillColor: '#6366f1', fillOpacity: 0.2 };
|
||||
const STILE_IN_ATTESA = { color: '#d81b1b', weight: 3, fillColor: '#ef4444', fillOpacity: 0.15 };
|
||||
|
||||
function initPiantine() {
|
||||
const contenitore = document.getElementById('piantine-map');
|
||||
if (!contenitore || contenitore.dataset.inizializzato) {
|
||||
return;
|
||||
}
|
||||
contenitore.dataset.inizializzato = '1';
|
||||
|
||||
let dati = { area: null, poligoni: [], selezionato: null };
|
||||
try {
|
||||
dati = JSON.parse(document.getElementById('piantine-dati').textContent);
|
||||
} catch {
|
||||
// si parte comunque con la mappa vuota
|
||||
}
|
||||
|
||||
// Etichette in italiano per la barra di disegno.
|
||||
L.drawLocal.draw.toolbar.buttons.polygon = 'Disegna il poligono di un territorio';
|
||||
L.drawLocal.draw.toolbar.actions = { title: 'Annulla il disegno', text: 'Annulla' };
|
||||
L.drawLocal.draw.toolbar.finish = { title: 'Concludi il disegno', text: 'Concludi' };
|
||||
L.drawLocal.draw.toolbar.undo = { title: 'Rimuovi l\'ultimo punto', text: 'Ultimo punto' };
|
||||
L.drawLocal.draw.handlers.polygon.tooltip = {
|
||||
start: 'Clicca sulla mappa per iniziare il poligono.',
|
||||
cont: 'Continua a cliccare lungo il confine.',
|
||||
end: 'Clicca sul primo punto per chiudere il poligono.',
|
||||
};
|
||||
|
||||
const mappa = L.map(contenitore, { zoomControl: true });
|
||||
|
||||
L.tileLayer('https://tile.openstreetmap.org/{z}/{x}/{y}.png', {
|
||||
maxZoom: 19,
|
||||
attribution: '© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors',
|
||||
}).addTo(mappa);
|
||||
|
||||
let layerArea = null;
|
||||
const layerSalvati = L.featureGroup().addTo(mappa);
|
||||
let layerInAttesa = null;
|
||||
|
||||
mappa.addControl(new L.Control.Draw({
|
||||
draw: {
|
||||
polygon: { allowIntersection: false, showArea: false, shapeOptions: STILE_IN_ATTESA },
|
||||
polyline: false,
|
||||
rectangle: false,
|
||||
circle: false,
|
||||
circlemarker: false,
|
||||
marker: false,
|
||||
},
|
||||
edit: false,
|
||||
}));
|
||||
|
||||
function ridisegna(nuoviDati, adattaVista) {
|
||||
dati = nuoviDati;
|
||||
|
||||
if (layerArea) {
|
||||
mappa.removeLayer(layerArea);
|
||||
layerArea = null;
|
||||
}
|
||||
if (dati.area) {
|
||||
layerArea = L.geoJSON(dati.area, { style: STILE_AREA, interactive: false }).addTo(mappa);
|
||||
}
|
||||
|
||||
layerSalvati.clearLayers();
|
||||
for (const poligono of dati.poligoni) {
|
||||
const selezionato = dati.selezionato === poligono.territorioId;
|
||||
const layer = L.geoJSON(
|
||||
{ type: 'Feature', properties: {}, geometry: poligono.geometry },
|
||||
{ style: selezionato ? STILE_SELEZIONATO : STILE_SALVATO }
|
||||
);
|
||||
layer.bindTooltip(`Terr. ${poligono.numero}`, { sticky: true });
|
||||
layer.on('click', () => {
|
||||
window.Livewire.dispatch('territorio-selezionato-mappa', { territorioId: poligono.territorioId });
|
||||
});
|
||||
layerSalvati.addLayer(layer);
|
||||
}
|
||||
|
||||
if (!adattaVista) {
|
||||
return;
|
||||
}
|
||||
const riferimento = layerArea ?? (dati.poligoni.length ? layerSalvati : null);
|
||||
if (riferimento) {
|
||||
mappa.fitBounds(riferimento.getBounds().pad(0.08));
|
||||
} else {
|
||||
mappa.setView([41.9, 12.5], 6); // Italia: nessun dato ancora caricato
|
||||
}
|
||||
}
|
||||
|
||||
ridisegna(dati, true);
|
||||
|
||||
mappa.on(L.Draw.Event.CREATED, (evento) => {
|
||||
if (layerInAttesa) {
|
||||
mappa.removeLayer(layerInAttesa);
|
||||
}
|
||||
layerInAttesa = evento.layer;
|
||||
layerInAttesa.setStyle(STILE_IN_ATTESA);
|
||||
layerInAttesa.addTo(mappa);
|
||||
|
||||
window.Livewire.dispatch('poligono-disegnato', { geometry: evento.layer.toGeoJSON().geometry });
|
||||
});
|
||||
|
||||
let registrato = false;
|
||||
document.addEventListener('livewire:init', registraEventi);
|
||||
if (window.Livewire) {
|
||||
registraEventi();
|
||||
}
|
||||
|
||||
function registraEventi() {
|
||||
if (registrato) {
|
||||
return;
|
||||
}
|
||||
registrato = true;
|
||||
window.Livewire.on('piantine-dati-aggiornati', ({ dati: nuovi }) => {
|
||||
// Dopo un salvataggio il poligono provvisorio è diventato
|
||||
// definitivo (o è stato scartato): via il tracciato rosso.
|
||||
if (layerInAttesa) {
|
||||
mappa.removeLayer(layerInAttesa);
|
||||
layerInAttesa = null;
|
||||
}
|
||||
ridisegna(nuovi, false);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (document.readyState === 'loading') {
|
||||
document.addEventListener('DOMContentLoaded', initPiantine);
|
||||
} else {
|
||||
initPiantine();
|
||||
}
|
||||
Reference in New Issue
Block a user