From f95ee85aacfa2c6755cfea614dd35d1fff4cc223 Mon Sep 17 00:00:00 2001 From: Johannes Baumeister Date: Thu, 28 May 2026 16:59:07 +0200 Subject: [PATCH] feat: Artennachweis Layer integriert --- app.js | 43 ++++++++++++++++++++++++++++++++++++++++++- server.js | 27 +++++++++++++++++++++++++++ style.css | 19 +++++++++++++++++++ 3 files changed, 88 insertions(+), 1 deletion(-) diff --git a/app.js b/app.js index e0a7d14..13a52df 100644 --- a/app.js +++ b/app.js @@ -1112,7 +1112,48 @@ document.addEventListener('DOMContentLoaded', async () => { document.getElementById('statusInfo').innerHTML += ` | ALKIS-Layer Fehler`; } - statusEl.innerText = "Layer geladen (ALKIS DB integriert)."; + // Artennachweis laden + console.log("Lade Artennachweis-Layer aus Datenbank..."); + const artenResp = await fetch('/api/layers/artennachweis').catch(err => { + console.error("Netzwerkfehler beim Laden des Artennachweis-Layers:", err); + return null; + }); + + if (artenResp?.ok) { + const data = await artenResp.json(); + console.log(`Artennachweis API: ${data.features ? data.features.length : 0} Features erhalten.`); + if (data.features && data.features.length > 0) { + const artenLayer = L.geoJSON(data, { + pointToLayer: (feature, latlng) => { + return L.circleMarker(latlng, { + radius: 5, + fillColor: "#ff4444", + color: "#ffffff", + weight: 2, + opacity: 1, + fillOpacity: 0.8 + }); + }, + onEachFeature: (feature, layer) => { + if (feature.properties && feature.properties.art) { + layer.bindTooltip(feature.properties.art, { + permanent: true, + direction: 'top', + className: 'arten-label' + }); + layer.bindPopup(`Vogelart: ${feature.properties.art}`); + } + } + }); + overlays["Artennachweis"] = artenLayer; + state.map.addLayer(artenLayer); + layerControl.addOverlay(artenLayer, "Artennachweis"); + } + } else { + console.warn("Artennachweis-Layer konnte nicht geladen werden."); + } + + statusEl.innerText = "Layer geladen (ALKIS DB & Artennachweis integriert)."; } catch (e) { if (!isLocalFile) console.error("Layer-Init fehlgeschlagen:", e); } diff --git a/server.js b/server.js index 8a1909b..b256976 100644 --- a/server.js +++ b/server.js @@ -248,6 +248,33 @@ app.get('/api/layers/alkis', async (req, res) => { } }); +// NEU: API zum Laden des Artennachweis-Layers als GeoJSON +app.get('/api/layers/artennachweis', async (req, res) => { + log("Lade Artennachweis-Layer aus Datenbank..."); + try { + const result = await pool.query( + `SELECT jsonb_build_object( + 'type', 'FeatureCollection', + 'features', COALESCE(jsonb_agg(features.feature), '[]'::jsonb) + ) + FROM ( + SELECT jsonb_build_object( + 'type', 'Feature', + 'geometry', ST_AsGeoJSON(ST_Transform(geom, 4326))::jsonb, + 'properties', jsonb_build_object( + 'art', art + ) + ) AS feature + FROM geodaten.artennachweis + ) features` + ); + res.json(result.rows[0].jsonb_build_object); + } catch (err) { + log(`FEHLER beim Laden des Artennachweis-Layers: ${err.message}`); + res.status(500).json({ error: err.message }); + } +}); + // API für Projekt-Statistiken (Fortschrittsanzeige) app.get('/api/stats/:projekt_id', async (req, res) => { const { projekt_id } = req.params; diff --git a/style.css b/style.css index ef63116..339e9fc 100644 --- a/style.css +++ b/style.css @@ -857,4 +857,23 @@ body { transform: scale(0.95); box-shadow: 0 0 0 0 rgba(52, 152, 219, 0); } +} + +/* Artennachweis Labels */ +.arten-label { + background: rgba(255, 68, 68, 0.85) !important; + border: 1px solid rgba(255, 255, 255, 0.8) !important; + color: white !important; + font-weight: 700 !important; + font-size: 0.75rem !important; + border-radius: 4px !important; + padding: 2px 6px !important; + pointer-events: none !important; + box-shadow: 0 2px 4px rgba(0,0,0,0.4) !important; + text-shadow: 1px 1px 2px rgba(0,0,0,0.6); + white-space: nowrap; +} + +.arten-label::before { + border-top-color: rgba(255, 68, 68, 0.85) !important; } \ No newline at end of file