Add Netzverknüpfungspunkt (POI) layer and fix double zoom issue
Deploy TrassenPlaner / deploy (push) Waiting to run Details

This commit is contained in:
Johannes Baumeister 2026-04-20 12:11:25 +02:00
parent d226fe5bdd
commit 6cd1ec4145
2 changed files with 92 additions and 6 deletions

View File

@ -652,10 +652,14 @@
<label style="display: flex; align-items: center; gap: 6px; cursor: pointer;"><input type="checkbox" id="toggle-wea" checked onchange="toggleLayer('wea', this.checked)"> WEA-Standorte</label> <label style="display: flex; align-items: center; gap: 6px; cursor: pointer;"><input type="checkbox" id="toggle-wea" checked onchange="toggleLayer('wea', this.checked)"> WEA-Standorte</label>
<span id="status-wea" style="color: var(--danger); font-weight: 600;">Fehlt</span> <span id="status-wea" style="color: var(--danger); font-weight: 600;">Fehlt</span>
</div> </div>
<div style="display: flex; justify-content: space-between; align-items: center;"> <div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 4px;">
<label style="display: flex; align-items: center; gap: 6px; cursor: pointer;"><input type="checkbox" id="toggle-infrastructure" checked onchange="toggleLayer('infrastructure', this.checked)"> Infrastruktur (UW)</label> <label style="display: flex; align-items: center; gap: 6px; cursor: pointer;"><input type="checkbox" id="toggle-infrastructure" checked onchange="toggleLayer('infrastructure', this.checked)"> Infrastruktur (UW)</label>
<span id="status-infrastructure" style="color: var(--danger); font-weight: 600;">Fehlt</span> <span id="status-infrastructure" style="color: var(--danger); font-weight: 600;">Fehlt</span>
</div> </div>
<div style="display: flex; justify-content: space-between; align-items: center;">
<label style="display: flex; align-items: center; gap: 6px; cursor: pointer;"><input type="checkbox" id="toggle-poi" checked onchange="toggleLayer('poi', this.checked)"> Netzverknüpfungspunkt</label>
<span id="status-poi" style="color: var(--danger); font-weight: 600;">Fehlt</span>
</div>
</div> </div>
<hr> <hr>
<button class="btn" id="btn-sync-folder"> <button class="btn" id="btn-sync-folder">
@ -770,6 +774,7 @@
owners: [], owners: [],
usage: [], usage: [],
wea: [], wea: [],
poi: [],
highlightedOwners: {}, highlightedOwners: {},
variants: [ variants: [
{ id: 1, name: "Variante A", color: "#cca300", routes: [], active: true, visible: true, stats: { total: 0, drilling: 0, open: 0, muffen: 0 } }, { id: 1, name: "Variante A", color: "#cca300", routes: [], active: true, visible: true, stats: { total: 0, drilling: 0, open: 0, muffen: 0 } },
@ -787,7 +792,8 @@
ownerColor: false, ownerColor: false,
usage: false, usage: false,
wea: true, wea: true,
infrastructure: true infrastructure: true,
poi: true
}, },
directoryHandle: null, directoryHandle: null,
measurementLines: [], measurementLines: [],
@ -1314,6 +1320,28 @@
} }
}); });
} }
}).addTo(map),
poi: L.geoJSON(null, {
pointToLayer: (feature, latlng) => {
return L.marker(latlng, {
icon: L.divIcon({
className: 'nvp-icon',
html: '<i data-lucide="zap"></i>',
iconSize: [24, 24],
iconAnchor: [12, 12],
popupAnchor: [0, -12]
})
});
},
onEachFeature: (f, layer) => {
layer.bindPopup(`<b>Netzverknüpfungspunkt</b>`);
layer.on('add', function () {
const el = layer.getElement();
if (el && window.lucide) {
lucide.createIcons({ root: el });
}
});
}
}).addTo(map) }).addTo(map)
}; };
@ -1480,8 +1508,24 @@
if (layerKey === 'usage') updateUsageLayer(); if (layerKey === 'usage') updateUsageLayer();
if (layerKey === 'wea') updateWEALayer(); if (layerKey === 'wea') updateWEALayer();
if (layerKey === 'infrastructure') updateInfrastructureLayer(); if (layerKey === 'infrastructure') updateInfrastructureLayer();
if (layerKey === 'poi') updatePOILayer();
}; };
function updatePOILayer() {
if (!state.poi || !state.poi.features) return;
const statusEl = document.getElementById('status-poi');
if (statusEl) {
statusEl.innerText = `${state.poi.features.length} NVP`;
statusEl.style.color = 'var(--success)';
}
layers.poi.clearLayers();
if (state.visibleLayers.poi) {
layers.poi.addData(state.poi);
}
}
function updateWEALayer() { function updateWEALayer() {
if (!state.wea || !state.wea.features) return; if (!state.wea || !state.wea.features) return;
@ -1495,10 +1539,6 @@
if (state.visibleLayers.wea) { if (state.visibleLayers.wea) {
layers.wea.addData(state.wea); layers.wea.addData(state.wea);
} }
if (Object.keys(layers.wea._layers).length > 0 && state.visibleLayers.wea) {
map.fitBounds(layers.wea.getBounds(), { padding: [50, 50] });
}
} }
function updateInfrastructureLayer() { function updateInfrastructureLayer() {
@ -2918,6 +2958,20 @@
} }
} catch (e) { console.error("[V3-Sync] Usage Error:", e); } } catch (e) { console.error("[V3-Sync] Usage Error:", e); }
// 4. POI (Netzverknüpfungspunkt)
try {
const res = await fetch(`${API_BASE}/poi`);
if (res.ok) {
let gj = sanitize(await res.json());
if (gj.features.length > 0) {
const c = gj.features[0].geometry.coordinates;
if (Math.abs(c[0]) > 1000) gj = transformGeoJSON(gj, "EPSG:25832", "EPSG:4326");
}
state.poi = gj;
updatePOILayer();
}
} catch (e) { console.error("[V3-Sync] POI Error:", e); }
// 4. WEA Standorte // 4. WEA Standorte
try { try {
const res = await fetch(`${API_BASE}/wea`); const res = await fetch(`${API_BASE}/wea`);

View File

@ -149,6 +149,38 @@ app.get('/api/wea', async (req, res) => {
} }
}); });
// 2.6 Get POI Data (Netzverknüpfungspunkt)
app.get('/api/poi', async (req, res) => {
const client = await pool.connect();
try {
await setSchema(client);
const query = `
SELECT
id,
ST_AsGeoJSON(ST_Transform(geom, 4326)) as geometry
FROM netzverknüpfungspunkt
`;
const result = await client.query(query);
const geojson = {
type: "FeatureCollection",
features: result.rows.map(row => {
const { geometry, ...properties } = row;
return {
type: "Feature",
geometry: JSON.parse(geometry),
properties: properties
};
})
};
res.json(geojson);
} catch (err) {
console.error("POI Error:", err);
res.json({ type: "FeatureCollection", features: [] });
} finally {
client.release();
}
});
// 2.7 Get Infrastructure Data // 2.7 Get Infrastructure Data
app.get('/api/infrastructure', async (req, res) => { app.get('/api/infrastructure', async (req, res) => {
const client = await pool.connect(); const client = await pool.connect();