diff --git a/app.js b/app.js index 33cf97a..df0b62a 100644 --- a/app.js +++ b/app.js @@ -112,9 +112,9 @@ document.addEventListener('DOMContentLoaded', async () => { let measureMode = null; // 'dist' or 'area' let measurePoints = []; let measureLayer = null; - let mouseMarker = L.marker([0, 0], { + let mouseMarker = L.marker([0, 0], { icon: L.divIcon({ className: 'measure-mouse-marker', iconSize: [0, 0] }), - interactive: false + interactive: false }).addTo(state.map); let activeTurbine = null; @@ -125,9 +125,9 @@ document.addEventListener('DOMContentLoaded', async () => { // Calculation Functions function calculateGeometries(latlng, rotorDiameter, hubHeight, foundationRadius, ksfAngle = 0, ksfMirrored = false, hersteller = 'Enercon') { console.log(`Berechne Geometrien für ${hersteller}, RD=${rotorDiameter}, HH=${hubHeight}`); - const rd = parseFloat(rotorDiameter) || 160; - const hh = parseFloat(hubHeight) || 165; - const fr = parseFloat(foundationRadius) || 15; + const rd = Number.parseFloat(rotorDiameter) || 160; + const hh = Number.parseFloat(hubHeight) || 165; + const fr = Number.parseFloat(foundationRadius) || 15; const totalHeight = hh + (rd / 2); const point = turf.point([latlng.lng, latlng.lat]); const steps = 128; // Increased resolution for smoother circles/ellipses @@ -141,14 +141,14 @@ document.addEventListener('DOMContentLoaded', async () => { const sweptArea = turf.circle(point, (rd / 2) / 1000, { units: 'kilometers', steps: steps }); // 2. Technical Distance 1 (Ellipse 2.5 x 4.0 RD) - const techDist = turf.ellipse(point, (rd * 4.0) / 1000, (rd * 2.5) / 1000, { + const techDist = turf.ellipse(point, (rd * 4) / 1000, (rd * 2.5) / 1000, { units: 'kilometers', angle: 135, steps: steps }); // 2b. Technical Distance 2 (2.0 RD Circle) - const techDistSmall = turf.circle(point, (rd * 2.0) / 1000, { + const techDistSmall = turf.circle(point, (rd * 2) / 1000, { units: 'kilometers', steps: steps }); @@ -160,14 +160,12 @@ document.addEventListener('DOMContentLoaded', async () => { const foundation = turf.circle(point, (fr) / 1000, { units: 'kilometers', steps: steps }); // Helper for KSF Geometries - const isNordex = hersteller && hersteller.toLowerCase() === 'nordex'; - const spg = (ksfMirrored !== isNordex) ? -1 : 1; + const isNordex = hersteller?.toLowerCase() === 'nordex'; + const spg = (ksfMirrored === isNordex) ? 1 : -1; // Turf uses CCW for positive angles. Most tools use CW. // We'll use negative angle for CCW to achieve CW rotation if expected. - const angle = -ksfAngle; const transform = (relCoords) => { - const rad = (-ksfAngle * Math.PI) / 180; // Negative for CW rotation if turf math is used, or just math // Manual Cartesian Rotation (Clockwise) // x' = x * cos(a) + y * sin(a) // y' = -x * sin(a) + y * cos(a) @@ -193,23 +191,23 @@ document.addEventListener('DOMContentLoaded', async () => { let blfCoords, ksfCoords, mfParts; - if (hersteller && hersteller.toLowerCase() === 'nordex') { + if (hersteller?.toLowerCase() === 'nordex') { // Nordex Geometries (Based on new QGIS schema provided by user) // KSF (Red): Complex polygon ksfCoords = [ - [18, -44.72], [18, 14.91], [-6.31, 14.91], [-18.38, 0], + [18, -44.72], [18, 14.91], [-6.31, 14.91], [-18.38, 0], [-18.38, -33.19], [-7, -44.72], [18, -44.72] ]; - + // AMF (Green Area): 180m long starting at KSF edge, 15m wide const amf = [ [18, -44.72], [18, -224.72], [3, -224.72], [3, -44.72], [18, -44.72] ]; mfParts = [amf]; - + // BLF/Road (Blue Area): Complex unified shape blfCoords = [ - [18, -224.72], [18, 40], [49.5, 40], [49.5, -100], + [18, -224.72], [18, 40], [49.5, 40], [49.5, -100], [24, -100], [24, -224.72], [18, -224.72] ]; } else { @@ -229,7 +227,7 @@ document.addEventListener('DOMContentLoaded', async () => { try { const blf = transform(blfCoords); const ksf = transform(ksfCoords); - + // Create the feature collection of polygons const mf = { type: "FeatureCollection", @@ -243,22 +241,6 @@ document.addEventListener('DOMContentLoaded', async () => { } } - function updateLabel(turbine, geoms) { - const labelText = ` -
- WEA ${turbine.nr}
- E: ${geoms.utmCoords[0].toFixed(0)} | N: ${geoms.utmCoords[1].toFixed(0)}
- NH: ${turbine.hh}m | RD: ${turbine.rd}m
- GH: ${geoms.totalHeight.toFixed(1)}m -
- `; - turbine.layers.marker.bindTooltip(labelText, { - permanent: true, - direction: 'right', - offset: [20, -10], - className: 'wea-label' - }).openTooltip(); - } function updateEditPanelPosition() { if (!activeTurbine || editPanel.style.display === 'none') return; @@ -367,11 +349,11 @@ document.addEventListener('DOMContentLoaded', async () => { }); function createTurbine(latlng, loadedNr = null, overrideData = null) { - const rd = overrideData?.rd || parseFloat(inputRotor.value) || 160; - const hh = overrideData?.hh || parseFloat(inputHub.value) || 165; - const fr = overrideData?.fr || parseFloat(inputFoundation.value) || 15; - const type = overrideData?.type || inputType.value || "Standard Typ"; - const hersteller = overrideData?.hersteller || inputManufacturer.value || "Enercon"; + const rd = overrideData?.rd || Number.parseFloat(inputRotor?.value) || 160; + const hh = overrideData?.hh || Number.parseFloat(inputHub?.value) || 165; + const fr = overrideData?.fr || Number.parseFloat(inputFoundation?.value) || 15; + const type = overrideData?.type || inputType?.value || "Standard Typ"; + const hersteller = overrideData?.hersteller || inputManufacturer?.value || "Enercon"; const weaNr = overrideData?.nr || loadedNr || (state.turbines.length + 1).toString(); const ksfAngle = overrideData?.ksfAngle || 0; const ksfMirrored = overrideData?.ksfMirrored || false; @@ -430,7 +412,6 @@ document.addEventListener('DOMContentLoaded', async () => { // Function to position the rotation handle based on current center and angle const updateRotationHandlePos = (turbine) => { const angleRad = (turbine.ksfAngle * Math.PI) / 180; - const dist = (turbine.rd * 0.4) / 1000; // Place it within or near the rotor radius for easy access (scaled for km) // In UTM for better precision const utmCenter = proj4(wgs84, utm32, [turbine.latlng.lng, turbine.latlng.lat]); @@ -476,7 +457,7 @@ document.addEventListener('DOMContentLoaded', async () => { updateProximityLines(); updateLegend(); // Show symbols in legend triggerAutoSave(); - if (activeTurbine && activeTurbine.id === turbine.id) { + if (activeTurbine?.id === turbine.id) { // Keep fields updated editNr.value = turbine.nr; updateEditPanelPosition(); // Sync floating panel @@ -499,7 +480,7 @@ document.addEventListener('DOMContentLoaded', async () => { if (angle < 0) angle += 360; turbine.ksfAngle = angle; - if (activeTurbine && activeTurbine.id === turbine.id) { + if (activeTurbine?.id === turbine.id) { editKsfAngle.value = angle.toFixed(1); } @@ -509,7 +490,7 @@ document.addEventListener('DOMContentLoaded', async () => { turbine.layers.mf.clearLayers().addData(newGeoms.mf); updateRotationHandlePos(turbine); }); - + turbine.layers.rotationHandle.on('dragend', triggerAutoSave); state.turbines.push(turbine); @@ -526,13 +507,13 @@ document.addEventListener('DOMContentLoaded', async () => { const newNr = editNr.value; const newManufacturer = editManufacturer.value; const newType = editType.value; - const newRd = parseFloat(editRd.value); - const newNh = parseFloat(editNh.value); - const newFr = parseFloat(editFr.value) || 15; - const newAngle = parseFloat(editKsfAngle.value) || 0; + const newRd = Number.parseFloat(editRd.value); + const newNh = Number.parseFloat(editNh.value); + const newFr = Number.parseFloat(editFr.value) || 15; + const newAngle = Number.parseFloat(editKsfAngle.value) || 0; const newMirrored = editKsfMirrored.checked; - if (isNaN(newRd) || isNaN(newNh) || isNaN(newFr)) { + if (Number.isNaN(newRd) || Number.isNaN(newNh) || Number.isNaN(newFr)) { alert("Bitte RD, NH und Fundament korrekt angeben."); return; } @@ -658,10 +639,10 @@ document.addEventListener('DOMContentLoaded', async () => { const inputUtmN = document.getElementById('utm-n'); btnCreateAtUTM.addEventListener('click', () => { - const e = parseFloat(inputUtmE.value); - const n = parseFloat(inputUtmN.value); + const e = Number.parseFloat(inputUtmE.value); + const n = Number.parseFloat(inputUtmN.value); - if (isNaN(e) || isNaN(n)) { + if (Number.isNaN(e) || Number.isNaN(n)) { alert("Bitte geben Sie gültige UTM-Koordinaten (Rechtswert und Hochwert) ein."); return; } @@ -797,19 +778,7 @@ document.addEventListener('DOMContentLoaded', async () => { } }); - // Variant Switching Logic - variantTabs.forEach(tab => { - tab.addEventListener('click', () => { - if (state.activeVariant === tab.dataset.variant) return; - variantTabs.forEach(t => t.classList.remove('active')); - tab.classList.add('active'); - state.map.removeLayer(variantLayers[state.activeVariant]); - state.activeVariant = tab.dataset.variant; - variantLayers[state.activeVariant].addTo(state.map); - updateProximityLines(); - closeEditPanel(); - }); - }); + // Base Layers & Overlays const baseLayers = { @@ -901,49 +870,10 @@ document.addEventListener('DOMContentLoaded', async () => { const layerData = state.bakedData[layerName].data; currentVariantTurbines.forEach(t => { - const GH = Number(t.hh) + (Number(t.rd) / 2); - const tPoint = turf.point([t.latlng.lng, t.latlng.lat]); - - turf.featureEach(layerData, (feature) => { - let closestPoint; - try { - if (feature.geometry.type === 'Point') { - closestPoint = turf.point(feature.geometry.coordinates); - } else if (feature.geometry.type === 'LineString' || feature.geometry.type === 'MultiLineString') { - closestPoint = turf.nearestPointOnLine(feature, tPoint); - } else if (feature.geometry.type === 'Polygon' || feature.geometry.type === 'MultiPolygon') { - // Find closest point on polygon boundary - const boundary = turf.polygonToLine(feature); - closestPoint = turf.nearestPointOnLine(boundary, tPoint); - } else { - return; - } - - const distKm = turf.distance(tPoint, closestPoint, { units: 'kilometers' }); - const distM = distKm * 1000; - - if (distM < threshold) { - const ratio = (distM / GH).toFixed(1); - const targetLatLng = L.latLng(closestPoint.geometry.coordinates[1], closestPoint.geometry.coordinates[0]); - - const line = L.polyline([t.latlng, targetLatLng], { - color: color, weight: 2, dashArray: '5, 8', opacity: 0.8 - }).addTo(proximityLinesLayer); - - const mid = L.latLng((t.latlng.lat + targetLatLng.lat) / 2, (t.latlng.lng + targetLatLng.lng) / 2); - - const label = type === 'house' ? `${distM.toFixed(1)} m (${ratio} H)` : `${distM.toFixed(1)} m`; - - line.bindTooltip(label, { - permanent: true, - direction: 'center', - className: type === 'house' ? 'proximity-tooltip-red' : - type === 'overhead' ? 'proximity-tooltip-yellow' : - 'proximity-tooltip-violet' - }).openTooltip(mid); - } - } catch (err) { console.warn("Distance calc error", err); } - }); + const features = layerData.features || (layerData.type === 'Feature' ? [layerData] : []); + for (const feature of features) { + processFeatureProximity(feature, t, color, threshold, type, proximityLinesLayer); + } }); }); } @@ -959,7 +889,7 @@ document.addEventListener('DOMContentLoaded', async () => { async function loadLocalLayer(path, label, color) { try { const response = await fetch(path).catch(() => null); - if (!response || !response.ok) return; + if (!response?.ok) return; const data = await response.json(); const layer = L.geoJSON(data, { style: { color: color, weight: 1.5, fillOpacity: 0.2 }, @@ -978,33 +908,11 @@ document.addEventListener('DOMContentLoaded', async () => { if (label.toLowerCase().includes('eigentümer') || label.toLowerCase().includes('flurstücke')) { layer.bringToBack(); } - } catch (e) { } + } catch (e) { + console.error("Fehler beim Laden des lokalen Layers:", e); + } } - function getDynamicStyle(layerName) { - const name = layerName.toLowerCase(); - - if (name.includes('wohnbebauung')) { - return { color: '#ff4444', weight: 1.5, fillOpacity: 0.6, isResidential: true }; - } - if (name.includes('freileitung')) { - return { color: '#ccaa00', weight: 3, fillOpacity: 0 }; - } - if (name.includes('leitung')) { - return { color: '#9400d3', weight: 2.5, fillOpacity: 0 }; - } - if (name.includes('eigentümer')) { - return { color: '#000000', weight: 2, fillOpacity: 0, fillColor: 'transparent' }; - } - if (name.includes('windeignungsgebiet') || name.includes('konzentrationszone') || name.includes('plangebiet')) { - return { color: '#ff0000', weight: 3, fillOpacity: 0, fillColor: 'transparent' }; - } - if (name.includes('standort') || (name.includes('wea') && !name.includes('zone'))) { - return { isTurbine: true }; - } - - return null; // Return null if no smart match - } async function processShapefileBuffers(shpBuffer, dbfBuffer, layerName, manualStyle = null) { const statusEl = document.getElementById('statusInfo'); @@ -1035,14 +943,14 @@ document.addEventListener('DOMContentLoaded', async () => { // Automated Turbine Creation from Points if (style.isTurbine) { geojson.features.forEach(f => { - if (f.geometry && f.geometry.type === 'Point') { + if (f.geometry?.type === 'Point') { const latlng = L.latLng(f.geometry.coordinates[1], f.geometry.coordinates[0]); const props = f.properties || {}; createTurbine(latlng, null, { nr: props.WEA_Nr || props.Nr || props.ID, type: props.Typ || props.Type, - rd: parseFloat(props.RD || props.Rotor), - hh: parseFloat(props.NH || props.HubHeight || props.HH) + rd: Number.parseFloat(props.RD || props.Rotor), + hh: Number.parseFloat(props.NH || props.HubHeight || props.HH) }); } }); @@ -1052,39 +960,10 @@ document.addEventListener('DOMContentLoaded', async () => { const layer = L.geoJSON(geojson, { style: (feature) => { - let fillColor = style.fillColor || style.color; + const isOwner = layerName.toLowerCase().includes('eigentümer'); + const fillColor = getAlkisFillColor(feature, style, layerName, state); - // Auto-detect ALKIS columns if not set - if (layerName.toLowerCase().includes('eigentümer') && !state.ownerMapping) { - const sampleProps = feature.properties; - const hasGNA = 'GNA' in sampleProps || 'gna' in sampleProps; - const hasVNA = 'VNA' in sampleProps || 'vna' in sampleProps; - if (hasGNA && hasVNA) { - state.ownerMapping = { - firstName: 'VNA' in sampleProps ? 'VNA' : 'vna', - lastName: 'GNA' in sampleProps ? 'GNA' : 'gna' - }; - console.log("ALKIS-Spalten automatisch erkannt:", state.ownerMapping); - } - } - - // Owner-Status-Coloring - if (layerName.toLowerCase().includes('eigentümer') && state.ownerMapping) { - const props = feature.properties; - const getProp = (key) => props[key] || props[key.toLowerCase()] || props[key.toUpperCase()] || ''; - const firstName = getProp(state.ownerMapping.firstName); - const lastName = getProp(state.ownerMapping.lastName); - const ownerName = `${firstName} ${lastName}`.trim().toLowerCase(); - - const stored = state.ownerStatuses[ownerName]; - const status = (typeof stored === 'string' ? stored : (stored?.status || "")).toLowerCase(); - - if (status === 'gbr' || status === 'gesichert') fillColor = '#2ecc71'; - if (status === 'external' || status === 'fremdplanung') fillColor = '#e74c3c'; - if (status === 'declined' || status === 'ablehnend' || status === 'negative') fillColor = '#e74c3c'; - if (status === 'undecided' || status === 'unentschlossen') fillColor = '#95a5a6'; - if (status === 'positive' || status === 'positiv') fillColor = '#5efd9c'; - if (status === 'in verhandlung') fillColor = '#f1c40f'; + if (isOwner && state.ownerMapping) { return { color: '#000', weight: 1, fillOpacity: 0.7, fillColor: fillColor }; } @@ -1162,155 +1041,32 @@ document.addEventListener('DOMContentLoaded', async () => { await processShapefileBuffers(shpBuffer, dbfBuffer, layerDef.name, layerDef); } catch (e) { - if (window.location.protocol !== 'file:') console.error(`Fehler bei ${layerDef.name}:`, e); + if (globalThis.location?.protocol !== 'file:') console.error(`Fehler bei ${layerDef.name}:`, e); } } async function initDynamicLayers() { const statusEl = document.getElementById('statusInfo'); - const isLocalFile = window.location.protocol === 'file:'; + const isLocalFile = globalThis.location?.protocol === 'file:'; try { - // Priority 1: Use window.BAKED_DATA if available (Standalone Mode) - if (window.BAKED_DATA) { + // Priority 1: Use globalThis.BAKED_DATA if available (Standalone Mode) + if (globalThis.BAKED_DATA) { // Support both old format (direct layers) and new format (with mapping/statuses) - let bakedLayers = window.BAKED_DATA; - if (window.BAKED_DATA.layers) { - bakedLayers = window.BAKED_DATA.layers; - state.ownerMapping = window.BAKED_DATA.ownerMapping || null; - state.ownerStatuses = window.BAKED_DATA.ownerStatuses || {}; + let bakedLayers = globalThis.BAKED_DATA; + if (globalThis.BAKED_DATA.layers) { + bakedLayers = globalThis.BAKED_DATA.layers; + state.ownerMapping = globalThis.BAKED_DATA.ownerMapping || null; + state.ownerStatuses = globalThis.BAKED_DATA.ownerStatuses || {}; } - for (const name in bakedLayers) { - const entry = bakedLayers[name]; - const smartStyle = getDynamicStyle(name); - const style = smartStyle || entry.style || { color: '#00c8ff', weight: 1.5, fillOpacity: 0.2 }; - - // Automated Turbine Creation from BAKED_DATA - if (style.isTurbine) { - entry.data.features.forEach(f => { - if (f.geometry && f.geometry.type === 'Point') { - const latlng = L.latLng(f.geometry.coordinates[1], f.geometry.coordinates[0]); - const props = f.properties || {}; - createTurbine(latlng, null, { - nr: props.WEA_Nr || props.Nr || props.ID, - type: props.Typ || props.Type, - rd: parseFloat(props.RD || props.Rotor), - hh: parseFloat(props.NH || props.HubHeight || props.HH) - }); - } - }); - state.bakedData[name] = entry; - continue; - } - - const layer = L.geoJSON(entry.data, { - style: (feature) => { - let fillColor = style.fillColor || style.color; - - if (name.toLowerCase().includes('eigentümer') && state.ownerMapping) { - const props = feature.properties; - const firstName = props[state.ownerMapping.firstName] || ''; - const lastName = props[state.ownerMapping.lastName] || ''; - const ownerName = `${firstName} ${lastName}`.trim(); - const status = state.ownerStatuses[ownerName]; - - if (status === 'gbr') fillColor = '#2ecc71'; - if (status === 'external') fillColor = '#e74c3c'; - if (status === 'declined') fillColor = '#f1c40f'; - if (status === 'positive') fillColor = '#5efd9c'; - if (status === 'undecided') fillColor = '#95a5a6'; - - return { color: '#000', weight: 1, fillOpacity: 0.7, fillColor: fillColor }; - } - - return { - color: style.color, - weight: style.weight, - fillOpacity: style.fillOpacity, - fillColor: fillColor - }; - }, - pointToLayer: (feature, latlng) => { - if (style.isResidential) { - return L.marker(latlng, { - icon: L.divIcon({ - className: 'residential-icon', - html: '🏠', - iconSize: [16, 16], - iconAnchor: [8, 8] - }) - }); - } - return L.circleMarker(latlng, { - radius: 4, - fillColor: style.color, - color: "#fff", - weight: 1, - opacity: 1, - fillOpacity: 0.8 - }); - }, - onEachFeature: (feature, layer) => { - if (feature.properties) { - let popup = `${name}

`; - for (let key in feature.properties) { - const val = feature.properties[key]; - if (val !== null && val !== undefined) popup += `${key}: ${val}
`; - } - layer.bindPopup(popup); - } - } - }); - overlays[name] = layer; - state.map.addLayer(layer); - layerControl.addOverlay(layer, name); - state.bakedData[name] = entry; - - if (name.toLowerCase().includes('eigentümer') || name.toLowerCase().includes('flurstücke')) { - layer.bringToBack(); - } - - // Auto-Zoom to Planning Area (Standalone Mode) - if (name.toLowerCase().includes('windeignungsgebiet')) { - state.map.fitBounds(layer.getBounds()); - } - } - statusEl.innerText = "Stand-Alone Daten geladen."; - // NICHT return – wir laden den ALKIS-DB-Layer immer, - // damit Status-Farben aus der Datenbank angezeigt werden. - } - - // Priority 2: Use window.LAYER_CONFIG (Script-based config) - // Nur laden wenn BAKED_DATA nicht vorhanden war - let layers = !window.BAKED_DATA ? window.LAYER_CONFIG : null; - - // Fallback: Fetch layers.json (if server is running) - if (!layers) { - const resp = await fetch('config/layers.json').catch(() => null); - if (resp && resp.ok) layers = await resp.json(); - } - - if (!layers) { - if (isLocalFile && !window.BAKED_DATA) { - statusEl.innerHTML = `Manuelles Laden: Ziehen Sie Shapefiles auf die Karte.`; - } - // Nicht return – wir versuchen trotzdem den ALKIS-Layer zu laden + loadBakedLayers(bakedLayers, state, overlays, layerControl, statusEl); } + // Priority 2: Use globalThis.LAYER_CONFIG (Script-based config) or remote config + const layers = await resolveLayerConfig(isLocalFile, statusEl); if (layers) { - for (const l of layers) { - // Lokalen Eigentümer-Layer umbenennen, um Verwechslung mit DB zu vermeiden - if (l.name.toLowerCase() === 'eigentümer') { - l.name = 'Eigentümer (Lokal)'; - } - - if (l.file.toLowerCase().endsWith('.geojson')) { - await loadLocalLayer(`data/${l.file}`, l.name, l.color); - } else { - await loadShapefileLayer(l); - } - } + await loadConfigLayers(layers); } // ALKIS aus Datenbank IMMER laden @@ -1319,26 +1075,13 @@ document.addEventListener('DOMContentLoaded', async () => { console.error("Netzwerkfehler beim Laden des ALKIS-Layers:", err); return null; }); - - if (alkisResp && alkisResp.ok) { + + if (alkisResp?.ok) { const data = await alkisResp.json(); console.log(`ALKIS API: ${data.features ? data.features.length : 0} Features erhalten.`); await processALKISData(data, "Eigentümer (ALKIS DB)"); - // KRITISCH: Lokalen Eigentümer-Shapefile-Layer entfernen, da der DB-Layer - // die gleichen Geometrien hat + Status-Farben. Der lokale Layer hat - // fillOpacity: 0 und verdeckt sonst die Farben des DB-Layers. - const localOwnerKeys = Object.keys(overlays).filter(k => - k.toLowerCase().includes('eigentümer') && k !== 'Eigentümer (ALKIS DB)' - ); - localOwnerKeys.forEach(key => { - console.log(`Entferne lokalen Layer "${key}" (wird durch ALKIS DB ersetzt).`); - if (state.map.hasLayer(overlays[key])) { - state.map.removeLayer(overlays[key]); - } - layerControl.removeLayer(overlays[key]); - delete overlays[key]; - }); + cleanupLocalOwnerLayers(); } else { const errorText = alkisResp ? await alkisResp.text() : "Server nicht erreichbar"; console.warn("ALKIS-Layer konnte nicht geladen werden:", errorText); @@ -1351,37 +1094,52 @@ document.addEventListener('DOMContentLoaded', async () => { } } + async function loadConfigLayers(layers) { + for (const l of layers) { + if (l.name.toLowerCase() === 'eigentümer') { + l.name = 'Eigentümer (Lokal)'; + } + if (l.file.toLowerCase().endsWith('.geojson')) { + await loadLocalLayer(`data/${l.file}`, l.name, l.color); + } else { + await loadShapefileLayer(l); + } + } + } + + function cleanupLocalOwnerLayers() { + const localOwnerKeys = Object.keys(overlays).filter(k => + k.toLowerCase().includes('eigentümer') && k !== 'Eigentümer (ALKIS DB)' + ); + localOwnerKeys.forEach(key => { + console.log(`Entferne lokalen Layer "${key}" (wird durch ALKIS DB ersetzt).`); + if (state.map.hasLayer(overlays[key])) { + state.map.removeLayer(overlays[key]); + } + layerControl.removeLayer(overlays[key]); + delete overlays[key]; + }); + } + async function processALKISData(geojson, layerName) { console.log(`Verarbeite ALKIS-Daten für Layer: ${layerName}. Features: ${geojson.features ? geojson.features.length : 0}`); - + const layer = L.geoJSON(geojson, { style: (feature) => { - const props = feature.properties; + const props = feature.properties || {}; const firstName = (props.vorname || props.VNA || '').trim(); const lastName = (props.nachname || props.GNA || '').trim(); - - // Normalisierung des Namens für den Abgleich - const normalize = (s) => (s || '').toString().toLowerCase().replace(/[^a-z0-9]/g, '').trim(); - const ownerKey = normalize(firstName + lastName); - - // Suche in den geladenen Status-Einträgen + let rawStatus = ''; - let foundInState = false; - - // Wir suchen im state.ownerStatuses - for (let key in state.ownerStatuses) { - if (normalize(key) === ownerKey) { - rawStatus = state.ownerStatuses[key].status; - foundInState = true; - break; - } + const matched = getAlkisOwnerStatus(firstName, lastName, state); + if (matched) { + rawStatus = matched.status; } - if (!rawStatus) rawStatus = props.status || ''; - + // Translate legacy values safely const status = (rawStatus && LEGACY_STATUS_MAP[rawStatus.toLowerCase()]) ? LEGACY_STATUS_MAP[rawStatus.toLowerCase()] : (rawStatus || 'none'); - + let fillColor = 'transparent'; let opacity = 0.1; @@ -1402,37 +1160,27 @@ document.addEventListener('DOMContentLoaded', async () => { const props = feature.properties; const firstName = (props.vorname || props.VNA || '').trim(); const lastName = (props.nachname || props.GNA || '').trim(); - const normalize = (s) => (s || '').toString().toLowerCase().replace(/[^a-z0-9]/g, '').trim(); - const ownerKey = normalize(firstName + lastName); - + let rawStatus = props.status || 'Kein Status'; let notiz = props.notiz || ''; - for (let key in state.ownerStatuses) { - if (normalize(key) === ownerKey) { - rawStatus = state.ownerStatuses[key].status; - notiz = state.ownerStatuses[key].notiz; - break; - } + const matched = getAlkisOwnerStatus(firstName, lastName, state); + if (matched) { + rawStatus = matched.status; + notiz = matched.notiz; } const status = (rawStatus && LEGACY_STATUS_MAP[rawStatus.toLowerCase()]) ? LEGACY_STATUS_MAP[rawStatus.toLowerCase()] : (rawStatus || 'Kein Status'); - let popup = `${layerName}

`; - popup += `Eigentümer: ${firstName} ${lastName}
`; - popup += `Status: ${status}
`; - if (notiz) popup += `Notiz: ${notiz}
`; - popup += `
`; - for (let key in props) { - if (['VNA', 'GNA', 'vorname', 'nachname', 'status', 'notiz', 'id', 'FLN', 'ZAE', 'NEN', 'FSK'].includes(key)) continue; - const val = props[key]; - if (val !== null && val !== undefined) popup += `${key}: ${val}
`; - } + const popup = generateAlkisPopup(props, firstName, lastName, status, notiz, layerName); layer.bindPopup(popup); // NEU: Tooltip-Label für die Karte (wird per CSS gesteuert erst bei Zoom eingeblendet) const flur = props.FLN || '-'; - const fst = props.ZAE ? (props.NEN ? `${props.ZAE}/${props.NEN}` : props.ZAE) : '-'; + let fst = '-'; + if (props.ZAE) { + fst = props.NEN ? `${props.ZAE}/${props.NEN}` : props.ZAE; + } const labelContent = `
${lastName}, ${firstName}
@@ -1450,13 +1198,13 @@ document.addEventListener('DOMContentLoaded', async () => { }); overlays[layerName] = layer; - + // Only add to map if checkbox is checked const checkShowOwners = document.getElementById('checkShowOwners'); if (!checkShowOwners || checkShowOwners.checked) { state.map.addLayer(layer); } - + layerControl.addOverlay(layer, layerName); layer.bringToFront(); // Ensure it's on top of local shapefiles } @@ -1478,7 +1226,7 @@ document.addEventListener('DOMContentLoaded', async () => { ownerMapping: state.ownerMapping, ownerStatuses: state.ownerStatuses }; - const content = `window.BAKED_DATA = ${JSON.stringify(exportData)};`; + const content = `globalThis.BAKED_DATA = ${JSON.stringify(exportData)};`; const blob = new Blob([content], { type: 'application/javascript' }); const url = URL.createObjectURL(blob); const a = document.createElement('a'); @@ -1520,20 +1268,20 @@ document.addEventListener('DOMContentLoaded', async () => { } ownerModal.style.display = 'flex'; - + // Versuche Auto-Mapping falls noch nicht geschehen if (!state.ownerMapping) { const layer = overlays[ownerLayer]; const allKeys = new Set(); layer.eachLayer(l => { - if (l.feature && l.feature.properties) { + if (l.feature?.properties) { Object.keys(l.feature.properties).forEach(k => allKeys.add(k)); } }); const sortedKeys = Array.from(allKeys); const vnaMatch = sortedKeys.find(k => k.toUpperCase() === 'VNA' || k.toUpperCase() === 'VORNAME'); const gnaMatch = sortedKeys.find(k => k.toUpperCase() === 'GNA' || k.toUpperCase() === 'NBA' || k.toUpperCase() === 'NACHNAME' || k.toUpperCase() === 'NAME'); - + if (vnaMatch && gnaMatch) { state.ownerMapping = { firstName: vnaMatch, lastName: gnaMatch }; console.log("Auto-Mapping erfolgreich:", state.ownerMapping); @@ -1544,10 +1292,10 @@ document.addEventListener('DOMContentLoaded', async () => { } } - if (!state.ownerMapping) { - showMappingStage(overlays[ownerLayer]); - } else { + if (state.ownerMapping) { showOwnerListStage(overlays[ownerLayer]); + } else { + showMappingStage(overlays[ownerLayer]); } }; @@ -1560,7 +1308,7 @@ document.addEventListener('DOMContentLoaded', async () => { // Extract properties from all available features to ensure we get all keys const allKeys = new Set(); layer.eachLayer(l => { - if (l.feature && l.feature.properties) { + if (l.feature?.properties) { Object.keys(l.feature.properties).forEach(k => allKeys.add(k)); } }); @@ -1624,9 +1372,9 @@ document.addEventListener('DOMContentLoaded', async () => { const address = `${str} ${hsn}, ${plz} ${ort}`.trim().replace(/^,/, '').trim(); if (!owners[fullName]) { - owners[fullName] = { - count: 0, - first, + owners[fullName] = { + count: 0, + first, last, address: address || 'Keine Adresse' }; @@ -1646,9 +1394,9 @@ document.addEventListener('DOMContentLoaded', async () => { const stored = state.ownerStatuses[name.toLowerCase()] || { status: 'none' }; const currentStatus = typeof stored === 'object' ? (stored.status || 'none') : (stored || 'none'); - const matchesQuery = name.toLowerCase().includes(query) || (o.address && o.address.toLowerCase().includes(query)); + const matchesQuery = name.toLowerCase().includes(query) || o.address?.toLowerCase().includes(query); let matchesStatus = !statusFilter || currentStatus === statusFilter; - + if (statusFilter === 'HAS_STATUS') { matchesStatus = currentStatus !== 'none' && currentStatus !== ''; } @@ -1666,17 +1414,17 @@ document.addEventListener('DOMContentLoaded', async () => { function renderOwnerRows(owners) { ownerTableBody.innerHTML = ''; - Object.keys(owners).sort().forEach(name => { + Object.keys(owners).sort((a, b) => a.localeCompare(b)).forEach(name => { const data = owners[name]; const stored = state.ownerStatuses[name.toLowerCase()] || { status: 'none', notiz: '' }; const status = typeof stored === 'object' ? (stored.status || 'none') : (stored || 'none'); const notiz = typeof stored === 'object' ? (stored.notiz || '') : ''; - + const row = document.createElement('tr'); // If last name is missing but first name has content, show first name in bold const displayLast = data.last || data.first || 'Unbekannt'; const displayFirst = data.last ? data.first : ''; - + row.innerHTML = ` ${displayLast} ${displayFirst} @@ -1702,7 +1450,7 @@ document.addEventListener('DOMContentLoaded', async () => { const status = e.target.value; const notizInput = document.querySelector(`.notiz-input[data-owner="${name}"]`); const notiz = notizInput ? notizInput.value : ""; - + state.ownerStatuses[name.toLowerCase()] = { status, notiz }; // Sync with DB @@ -1723,16 +1471,16 @@ document.addEventListener('DOMContentLoaded', async () => { const notiz = e.target.value; const sel = document.querySelector(`.status-select[data-owner="${name}"]`); const status = sel ? sel.value : 'none'; - + state.ownerStatuses[name.toLowerCase()] = { status, notiz }; - + const data = owners[name]; if (data) { await secureOwner(data.first, data.last, e.target, status, notiz); } refreshOwnerLayerStyle(); }; - + input.onkeydown = (e) => { if (e.key === 'Enter') e.target.blur(); }; @@ -1742,7 +1490,7 @@ document.addEventListener('DOMContentLoaded', async () => { async function secureOwner(vorname, nachname, element, status = 'none', notiz = '') { const isSelect = element.tagName === 'SELECT'; const isInput = element.tagName === 'INPUT'; - + // Visual feedback if (isSelect || isInput) { element.style.borderColor = '#2ecc71'; @@ -1836,9 +1584,9 @@ document.addEventListener('DOMContentLoaded', async () => { const last = s.nachname || ''; const fullName = `${first} ${last}`.trim().toLowerCase(); if (fullName) { - state.ownerStatuses[fullName] = { - status: s.status, - notiz: s.notiz + state.ownerStatuses[fullName] = { + status: s.status, + notiz: s.notiz }; } }); @@ -1870,10 +1618,10 @@ document.addEventListener('DOMContentLoaded', async () => { initDynamicLayers().then(async () => { // Erst Status laden await loadOwnerStatusesFromDB(); - + // Dann WEAs laden await loadTurbinesFromDB(); - + // Automatisches Mapping für den Eigentümer-Layer prüfen const ownerLayerName = Object.keys(overlays).find(k => k.toLowerCase().includes('eigentümer')); if (ownerLayerName && overlays[ownerLayerName]) { @@ -1881,7 +1629,7 @@ document.addEventListener('DOMContentLoaded', async () => { const layers = layer.getLayers(); if (layers.length > 0) { const firstFeature = layers[0].feature; - if (firstFeature && firstFeature.properties) { + if (firstFeature?.properties) { const props = firstFeature.properties; const vna = Object.keys(props).find(k => k.toUpperCase() === 'VNA'); const gna = Object.keys(props).find(k => k.toUpperCase() === 'GNA' || k.toUpperCase() === 'NBA'); @@ -1939,7 +1687,7 @@ document.addEventListener('DOMContentLoaded', async () => { async function saveTurbinesToDB() { const statusEl = document.getElementById('statusInfo'); if (statusEl) statusEl.innerText = "Automatische Speicherung..."; - + const projekt_id = "BWSamern-Ohne"; const turbineData = state.turbines.map(t => ({ nr: t.nr, @@ -1987,7 +1735,7 @@ document.addEventListener('DOMContentLoaded', async () => { } const dbTurbines = await response.json(); console.log(`Datenbank-Response: ${dbTurbines.length} WEAs erhalten.`); - + if (dbTurbines.length > 0) { // Clear existing turbines safely state.turbines.forEach(t => { @@ -1995,7 +1743,9 @@ document.addEventListener('DOMContentLoaded', async () => { if (t.layers && t.variant && variantLayers[t.variant]) { Object.values(t.layers).forEach(l => variantLayers[t.variant].removeLayer(l)); } - } catch (e) {} + } catch (e) { + console.error("Error clearing existing layers:", e); + } }); state.turbines = []; @@ -2003,7 +1753,7 @@ document.addEventListener('DOMContentLoaded', async () => { try { const latlng = L.latLng(t.lat, t.lng); const variant = t.variant || 'A'; - + if (!variantLayers[variant]) { console.warn(`Ungültige Variante: ${variant}`); return; @@ -2013,9 +1763,9 @@ document.addEventListener('DOMContentLoaded', async () => { nr: t.nr, hersteller: t.hersteller, type: t.type, - rd: parseFloat(t.rd) || 160, - hh: parseFloat(t.hh) || 165, - ksfAngle: parseFloat(t.ksfangle ?? t.ksfAngle ?? 0), + rd: Number.parseFloat(t.rd) || 160, + hh: Number.parseFloat(t.hh) || 165, + ksfAngle: Number.parseFloat(t.ksfangle ?? t.ksfAngle ?? 0), variant: variant }); } catch (e) { @@ -2034,54 +1784,51 @@ document.addEventListener('DOMContentLoaded', async () => { btnLoad.addEventListener('click', () => projectInput.click()); - projectInput.addEventListener('change', (e) => { + projectInput.addEventListener('change', async (e) => { const file = e.target.files[0]; if (!file) return; - const reader = new FileReader(); - reader.onload = (event) => { - try { - const data = JSON.parse(event.target.result); + try { + const text = await file.text(); + const data = JSON.parse(text); - // Clear existing turbines - state.turbines.forEach(t => { - Object.values(t.layers).forEach(l => { - variantLayers[t.variant].removeLayer(l); - }); + // Clear existing turbines + state.turbines.forEach(t => { + Object.values(t.layers).forEach(l => { + variantLayers[t.variant].removeLayer(l); }); - state.turbines = []; + }); + state.turbines = []; - // Restore Owner Data - state.ownerMapping = data.ownerMapping || null; - state.ownerStatuses = data.ownerStatuses || {}; + // Restore Owner Data + state.ownerMapping = data.ownerMapping || null; + state.ownerStatuses = data.ownerStatuses || {}; - // Reconstruct from data - data.turbines.forEach(tData => { - // Set current UI state momentarily for creating turbine correctly - // (Easier than refactoring createTurbine now) - inputType.value = tData.type; - inputRotor.value = tData.rd; - inputHub.value = tData.hh; + // Reconstruct from data + data.turbines.forEach(tData => { + // Set current UI state momentarily for creating turbine correctly + // (Easier than refactoring createTurbine now) + if (inputType) inputType.value = tData.type; + if (inputRotor) inputRotor.value = tData.rd; + if (inputHub) inputHub.value = tData.hh; - const oldVariant = state.activeVariant; - state.activeVariant = tData.variant; + const oldVariant = state.activeVariant; + state.activeVariant = tData.variant; - createTurbine(tData.latlng, tData.nr); + createTurbine(tData.latlng, tData.nr); - state.activeVariant = oldVariant; - }); + state.activeVariant = oldVariant; + }); - updateProximityLines(); - updateLegend(); - triggerAutoSave(); + updateProximityLines(); + updateLegend(); + triggerAutoSave(); - document.getElementById('statusInfo').innerText = "Projekt geladen."; - } catch (err) { - console.error("Fehler beim Laden:", err); - alert("Ungültige Projektdatei."); - } - }; - reader.readAsText(file); + document.getElementById('statusInfo').innerText = "Projekt geladen."; + } catch (err) { + console.error("Fehler beim Laden:", err); + alert("Ungültige Projektdatei."); + } }); // Geolocation Logic @@ -2116,7 +1863,11 @@ document.addEventListener('DOMContentLoaded', async () => { const { latitude, longitude, accuracy } = position.coords; const latlng = L.latLng(latitude, longitude); - if (!userMarker) { + if (userMarker) { + userMarker.setLatLng(latlng); + userAccuracyCircle.setLatLng(latlng); + userAccuracyCircle.setRadius(accuracy); + } else { userMarker = L.marker(latlng, { icon: L.divIcon({ className: 'user-location-marker pulse', @@ -2124,7 +1875,7 @@ document.addEventListener('DOMContentLoaded', async () => { iconAnchor: [8, 8] }) }).addTo(state.map); - + userAccuracyCircle = L.circle(latlng, { radius: accuracy, className: 'user-location-accuracy' @@ -2132,10 +1883,6 @@ document.addEventListener('DOMContentLoaded', async () => { // Center on first found position state.map.setView(latlng, 16); - } else { - userMarker.setLatLng(latlng); - userAccuracyCircle.setLatLng(latlng); - userAccuracyCircle.setRadius(accuracy); } document.getElementById('statusInfo').innerText = `Standort aktualisiert (Genauigkeit: ${accuracy.toFixed(0)}m)`; @@ -2145,7 +1892,7 @@ document.addEventListener('DOMContentLoaded', async () => { if (err.code === 1) msg = "Standortzugriff verweigert. Bitte erlauben Sie den Zugriff in den Browser-Einstellungen."; else if (err.code === 2) msg = "Standort nicht verfügbar. Prüfen Sie, ob GPS/Ortungsdienste am Gerät aktiviert sind."; else if (err.code === 3) msg = "Zeitüberschreitung bei der Standorterkennung."; - + alert(msg); stopTracking(); }, { @@ -2169,3 +1916,295 @@ document.addEventListener('DOMContentLoaded', async () => { console.log("WindPlaner initialisiert."); document.getElementById('statusInfo').innerText = "System bereit. Karte geladen."; }); + +function updateLabel(turbine, geoms) { + const labelText = ` +
+ WEA ${turbine.nr}
+ E: ${geoms.utmCoords[0].toFixed(0)} | N: ${geoms.utmCoords[1].toFixed(0)}
+ NH: ${turbine.hh}m | RD: ${turbine.rd}m
+ GH: ${geoms.totalHeight.toFixed(1)}m +
+ `; + turbine.layers.marker.bindTooltip(labelText, { + permanent: true, + direction: 'right', + offset: [20, -10], + className: 'wea-label' + }).openTooltip(); +} + +function processFeatureProximity(feature, t, color, threshold, type, proximityLinesLayer) { + const GH = Number(t.hh) + (Number(t.rd) / 2); + const tPoint = turf.point([t.latlng.lng, t.latlng.lat]); + let closestPoint; + try { + if (feature.geometry.type === 'Point') { + closestPoint = turf.point(feature.geometry.coordinates); + } else if (feature.geometry.type === 'LineString' || feature.geometry.type === 'MultiLineString') { + closestPoint = turf.nearestPointOnLine(feature, tPoint); + } else if (feature.geometry.type === 'Polygon' || feature.geometry.type === 'MultiPolygon') { + // Find closest point on polygon boundary + const boundary = turf.polygonToLine(feature); + closestPoint = turf.nearestPointOnLine(boundary, tPoint); + } else { + return; + } + + const distKm = turf.distance(tPoint, closestPoint, { units: 'kilometers' }); + const distM = distKm * 1000; + + if (distM < threshold) { + const ratio = (distM / GH).toFixed(1); + const targetLatLng = L.latLng(closestPoint.geometry.coordinates[1], closestPoint.geometry.coordinates[0]); + + const line = L.polyline([t.latlng, targetLatLng], { + color: color, weight: 2, dashArray: '5, 8', opacity: 0.8 + }).addTo(proximityLinesLayer); + + const mid = L.latLng((t.latlng.lat + targetLatLng.lat) / 2, (t.latlng.lng + targetLatLng.lng) / 2); + + const label = type === 'house' ? `${distM.toFixed(1)} m (${ratio} H)` : `${distM.toFixed(1)} m`; + + let tooltipClass = 'proximity-tooltip-violet'; + if (type === 'house') { + tooltipClass = 'proximity-tooltip-red'; + } else if (type === 'overhead') { + tooltipClass = 'proximity-tooltip-yellow'; + } + + line.bindTooltip(label, { + permanent: true, + direction: 'center', + className: tooltipClass + }).openTooltip(mid); + } + } catch (err) { + console.warn("Distance calc error", err); + } +} + +function detectAlkisOwnerColumns(feature, state) { + const sampleProps = feature.properties; + if (!sampleProps) return; + const hasGNA = 'GNA' in sampleProps || 'gna' in sampleProps; + const hasVNA = 'VNA' in sampleProps || 'vna' in sampleProps; + if (hasGNA && hasVNA) { + state.ownerMapping = { + firstName: 'VNA' in sampleProps ? 'VNA' : 'vna', + lastName: 'GNA' in sampleProps ? 'GNA' : 'gna' + }; + console.log("ALKIS-Spalten automatisch erkannt:", state.ownerMapping); + } +} + +function resolveOwnerStatusColor(ownerName, state) { + const stored = state.ownerStatuses[ownerName]; + const status = (typeof stored === 'string' ? stored : (stored?.status || "")).toLowerCase(); + + const statusColors = { + gbr: '#2ecc71', + gesichert: '#2ecc71', + external: '#e74c3c', + fremdplanung: '#e74c3c', + declined: '#e74c3c', + ablehnend: '#ablehnend', + negative: '#e74c3c', + undecided: '#95a5a6', + unentschlossen: '#95a5a6', + positive: '#5efd9c', + positiv: '#5efd9c', + 'in verhandlung': '#f1c40f' + }; + return statusColors[status] || null; +} + +function getAlkisFillColor(feature, style, layerName, state) { + let fillColor = style.fillColor || style.color; + const nameLow = layerName.toLowerCase(); + + if (nameLow.includes('eigentümer')) { + if (!state.ownerMapping) { + detectAlkisOwnerColumns(feature, state); + } + + if (state.ownerMapping) { + const props = feature.properties || {}; + const getProp = (key) => props[key] || props[key.toLowerCase()] || props[key.toUpperCase()] || ''; + const firstName = getProp(state.ownerMapping.firstName); + const lastName = getProp(state.ownerMapping.lastName); + const ownerName = `${firstName} ${lastName}`.trim().toLowerCase(); + + const statusColor = resolveOwnerStatusColor(ownerName, state); + if (statusColor) { + fillColor = statusColor; + } + } + } + + return fillColor; +} + +function getAlkisOwnerStatus(firstName, lastName, state) { + const normalize = (s) => (s || '').toString().toLowerCase().replace(/[^a-z0-9]/g, '').trim(); + const ownerKey = normalize(firstName + lastName); + + for (let key in state.ownerStatuses) { + if (normalize(key) === ownerKey) { + return state.ownerStatuses[key]; + } + } + return null; +} + +function generateAlkisPopup(props, firstName, lastName, status, notiz, layerName) { + let popup = `${layerName}

`; + popup += `Eigentümer: ${firstName} ${lastName}
`; + popup += `Status: ${status}
`; + if (notiz) popup += `Notiz: ${notiz}
`; + popup += `
`; + for (let key in props) { + if (['VNA', 'GNA', 'vorname', 'nachname', 'status', 'notiz', 'id', 'FLN', 'ZAE', 'NEN', 'FSK'].includes(key)) continue; + const val = props[key]; + if (val !== null && val !== undefined) popup += `${key}: ${val}
`; + } + return popup; +} + +function loadBakedLayers(bakedLayers, state, overlays, layerControl, statusEl) { + for (const name in bakedLayers) { + const entry = bakedLayers[name]; + const smartStyle = getDynamicStyle(name); + const style = smartStyle || entry.style || { color: '#00c8ff', weight: 1.5, fillOpacity: 0.2 }; + + // Automated Turbine Creation from BAKED_DATA + if (style.isTurbine) { + entry.data.features.forEach(f => { + if (f.geometry?.type === 'Point') { + const latlng = L.latLng(f.geometry.coordinates[1], f.geometry.coordinates[0]); + const props = f.properties || {}; + createTurbine(latlng, null, { + nr: props.WEA_Nr || props.Nr || props.ID, + type: props.Typ || props.Type, + rd: Number.parseFloat(props.RD || props.Rotor), + hh: Number.parseFloat(props.NH || props.HubHeight || props.HH) + }); + } + }); + state.bakedData[name] = entry; + continue; + } + + const layer = L.geoJSON(entry.data, { + style: (feature) => { + let fillColor = style.fillColor || style.color; + + if (name.toLowerCase().includes('eigentümer') && state.ownerMapping) { + const props = feature.properties; + const firstName = props[state.ownerMapping.firstName] || ''; + const lastName = props[state.ownerMapping.lastName] || ''; + const ownerName = `${firstName} ${lastName}`.trim(); + const status = state.ownerStatuses[ownerName]; + + if (status === 'gbr') fillColor = '#2ecc71'; + if (status === 'external') fillColor = '#e74c3c'; + if (status === 'declined') fillColor = '#f1c40f'; + if (status === 'positive') fillColor = '#5efd9c'; + if (status === 'undecided') fillColor = '#95a5a6'; + + return { color: '#000', weight: 1, fillOpacity: 0.7, fillColor: fillColor }; + } + + return { + color: style.color, + weight: style.weight, + fillOpacity: style.fillOpacity, + fillColor: fillColor + }; + }, + pointToLayer: (feature, latlng) => { + if (style.isResidential) { + return L.marker(latlng, { + icon: L.divIcon({ + className: 'residential-icon', + html: '🏠', + iconSize: [16, 16], + iconAnchor: [8, 8] + }) + }); + } + return L.circleMarker(latlng, { + radius: 4, + fillColor: style.color, + color: "#fff", + weight: 1, + opacity: 1, + fillOpacity: 0.8 + }); + }, + onEachFeature: (feature, layer) => { + if (feature.properties) { + let popup = `${name}

`; + for (let key in feature.properties) { + const val = feature.properties[key]; + if (val !== null && val !== undefined) popup += `${key}: ${val}
`; + } + layer.bindPopup(popup); + } + } + }); + overlays[name] = layer; + state.map.addLayer(layer); + layerControl.addOverlay(layer, name); + state.bakedData[name] = entry; + + if (name.toLowerCase().includes('eigentümer') || name.toLowerCase().includes('flurstücke')) { + layer.bringToBack(); + } + + // Auto-Zoom to Planning Area (Standalone Mode) + if (name.toLowerCase().includes('windeignungsgebiet')) { + state.map.fitBounds(layer.getBounds()); + } + } + statusEl.innerText = "Stand-Alone Daten geladen."; +} + +function getDynamicStyle(layerName) { + const name = layerName.toLowerCase(); + + if (name.includes('wohnbebauung')) { + return { color: '#ff4444', weight: 1.5, fillOpacity: 0.6, isResidential: true }; + } + if (name.includes('freileitung')) { + return { color: '#ccaa00', weight: 3, fillOpacity: 0 }; + } + if (name.includes('leitung')) { + return { color: '#9400d3', weight: 2.5, fillOpacity: 0 }; + } + if (name.includes('eigentümer')) { + return { color: '#000000', weight: 2, fillOpacity: 0, fillColor: 'transparent' }; + } + if (name.includes('windeignungsgebiet') || name.includes('konzentrationszone') || name.includes('plangebiet')) { + return { color: '#ff0000', weight: 3, fillOpacity: 0, fillColor: 'transparent' }; + } + if (name.includes('standort') || (name.includes('wea') && !name.includes('zone'))) { + return { isTurbine: true }; + } + + return null; // Return null if no smart match +} + +async function resolveLayerConfig(isLocalFile, statusEl) { + let layers = globalThis.BAKED_DATA ? null : globalThis.LAYER_CONFIG; + + if (!layers) { + const resp = await fetch('config/layers.json').catch(() => null); + if (resp?.ok) layers = await resp.json(); + } + + if (!layers && isLocalFile && !globalThis.BAKED_DATA) { + statusEl.innerHTML = `Manuelles Laden: Ziehen Sie Shapefiles auf die Karte.`; + } + return layers; +} diff --git a/backend/search_wea_views.js b/backend/search_wea_views.js index 884e0ef..2d43640 100644 --- a/backend/search_wea_views.js +++ b/backend/search_wea_views.js @@ -1,12 +1,24 @@ +require('dotenv').config({ path: require('node:path').join(__dirname, '../.env') }); const { Client } = require('pg'); async function check() { + const host = process.env.DB_HOST; + const port = Number(process.env.DB_PORT || 5432); + const user = process.env.DB_USER; + const password = process.env.DB_PASSWORD; + const database = process.env.DB_NAME; + + if (!host || !password) { + console.error("Missing database environment configuration (DB_HOST, DB_PASSWORD). Please check your .env file."); + return; + } + const client = new Client({ - host: '87.106.21.21', - port: 5432, - user: 'enwelo_admin', - password: 'WX1t1cgP1qK09', - database: 'enwelo' + host, + port, + user, + password, + database }); try { diff --git a/config/layers.js b/config/layers.js index dcfbf3b..845bf06 100644 --- a/config/layers.js +++ b/config/layers.js @@ -1,10 +1,10 @@ -window.LAYER_CONFIG = [ +globalThis.LAYER_CONFIG = [ { "name": "Eigentümer", "file": "Eigentümer", "color": "#000000", "fillColor": "transparent", - "weight": 2.0, + "weight": 2, "fillOpacity": 0 }, { @@ -25,7 +25,7 @@ window.LAYER_CONFIG = [ "name": "Freileitungen", "file": "Freileitungen", "color": "#ccaa00", - "weight": 3.0, + "weight": 3, "fillOpacity": 0 }, /* { diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..3a926a3 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,1016 @@ +{ + "name": "bwsamern-ohne-planungstool", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "bwsamern-ohne-planungstool", + "version": "1.0.0", + "dependencies": { + "body-parser": "^1.20.2", + "cors": "^2.8.5", + "dotenv": "^16.4.5", + "express": "^4.19.2", + "pg": "^8.11.5" + } + }, + "node_modules/accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "license": "MIT", + "dependencies": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==", + "license": "MIT" + }, + "node_modules/body-parser": { + "version": "1.20.5", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.5.tgz", + "integrity": "sha512-3grm+/2tUOvu2cjJkvsIxrv/wVpfXQW4PsQHYm7yk4vfpu7Ekl6nEsYBoJUL6qDwZUx8wUhQ8tR2qz+ad9c9OA==", + "license": "MIT", + "dependencies": { + "bytes": "~3.1.2", + "content-type": "~1.0.5", + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "~1.2.0", + "http-errors": "~2.0.1", + "iconv-lite": "~0.4.24", + "on-finished": "~2.4.1", + "qs": "~6.15.1", + "raw-body": "~2.5.3", + "type-is": "~1.6.18", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/content-disposition": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", + "license": "MIT", + "dependencies": { + "safe-buffer": "5.2.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-signature": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.7.tgz", + "integrity": "sha512-NXdYc3dLr47pBkpUCHtKSwIOQXLVn8dZEuywboCOJY/osA0wFSLlSawr3KN8qXJEyX66FcONTH8EIlVuK0yyFA==", + "license": "MIT" + }, + "node_modules/cors": { + "version": "2.8.6", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.6.tgz", + "integrity": "sha512-tJtZBBHA6vjIAaF6EnIaq6laBBP9aq/Y3ouVJjEfoHbRBcHBAHYcMh/w8LDrk2PvIMMq8gmopa5D4V8RmbrxGw==", + "license": "MIT", + "dependencies": { + "object-assign": "^4", + "vary": "^1" + }, + "engines": { + "node": ">= 0.10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/destroy": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", + "license": "MIT", + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/dotenv": { + "version": "16.6.1", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.6.1.tgz", + "integrity": "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", + "license": "MIT" + }, + "node_modules/encodeurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", + "license": "MIT" + }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/express": { + "version": "4.22.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.22.2.tgz", + "integrity": "sha512-IuL+Elrou2ZvCFHs18/CIzy2Nzvo25nZ1/D2eIZlz7c+QUayAcYoiM2BthCjs+EBHVpjYjcuLDAiCWgeIX3X1Q==", + "license": "MIT", + "dependencies": { + "accepts": "~1.3.8", + "array-flatten": "1.1.1", + "body-parser": "~1.20.5", + "content-disposition": "~0.5.4", + "content-type": "~1.0.4", + "cookie": "~0.7.1", + "cookie-signature": "~1.0.6", + "debug": "2.6.9", + "depd": "2.0.0", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "~1.3.1", + "fresh": "~0.5.2", + "http-errors": "~2.0.0", + "merge-descriptors": "1.0.3", + "methods": "~1.1.2", + "on-finished": "~2.4.1", + "parseurl": "~1.3.3", + "path-to-regexp": "~0.1.12", + "proxy-addr": "~2.0.7", + "qs": "~6.15.1", + "range-parser": "~1.2.1", + "safe-buffer": "5.2.1", + "send": "~0.19.0", + "serve-static": "~1.16.2", + "setprototypeof": "1.2.0", + "statuses": "~2.0.1", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/finalhandler": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.2.tgz", + "integrity": "sha512-aA4RyPcd3badbdABGDuTXCMTtOneUCAYH/gxoYRTZlIJdF0YPWuGqiAsIrhNnnqdXGswYk6dGujem4w80UJFhg==", + "license": "MIT", + "dependencies": { + "debug": "2.6.9", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "on-finished": "~2.4.1", + "parseurl": "~1.3.3", + "statuses": "~2.0.2", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.3.tgz", + "integrity": "sha512-ej4AhfhfL2Q2zpMmLo7U1Uv9+PyhIZpgQLGT1F9miIGmiCJIoCgSmczFdrc97mWT4kVY72KA+WnnhJ5pghSvSg==", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/http-errors": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", + "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==", + "license": "MIT", + "dependencies": { + "depd": "~2.0.0", + "inherits": "~2.0.4", + "setprototypeof": "~1.2.0", + "statuses": "~2.0.2", + "toidentifier": "~1.0.1" + }, + "engines": { + "node": ">= 0.8" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "license": "ISC" + }, + "node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/merge-descriptors": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", + "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "license": "MIT", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT" + }, + "node_modules/negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-inspect": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "license": "MIT", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/path-to-regexp": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.13.tgz", + "integrity": "sha512-A/AGNMFN3c8bOlvV9RreMdrv7jsmF9XIfDeCd87+I8RNg6s78BhJxMu69NEMHBSJFxKidViTEdruRwEk/WIKqA==", + "license": "MIT" + }, + "node_modules/pg": { + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/pg/-/pg-8.20.0.tgz", + "integrity": "sha512-ldhMxz2r8fl/6QkXnBD3CR9/xg694oT6DZQ2s6c/RI28OjtSOpxnPrUCGOBJ46RCUxcWdx3p6kw/xnDHjKvaRA==", + "license": "MIT", + "peer": true, + "dependencies": { + "pg-connection-string": "^2.12.0", + "pg-pool": "^3.13.0", + "pg-protocol": "^1.13.0", + "pg-types": "2.2.0", + "pgpass": "1.0.5" + }, + "engines": { + "node": ">= 16.0.0" + }, + "optionalDependencies": { + "pg-cloudflare": "^1.3.0" + }, + "peerDependencies": { + "pg-native": ">=3.0.1" + }, + "peerDependenciesMeta": { + "pg-native": { + "optional": true + } + } + }, + "node_modules/pg-cloudflare": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/pg-cloudflare/-/pg-cloudflare-1.3.0.tgz", + "integrity": "sha512-6lswVVSztmHiRtD6I8hw4qP/nDm1EJbKMRhf3HCYaqud7frGysPv7FYJ5noZQdhQtN2xJnimfMtvQq21pdbzyQ==", + "license": "MIT", + "optional": true + }, + "node_modules/pg-connection-string": { + "version": "2.12.0", + "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.12.0.tgz", + "integrity": "sha512-U7qg+bpswf3Cs5xLzRqbXbQl85ng0mfSV/J0nnA31MCLgvEaAo7CIhmeyrmJpOr7o+zm0rXK+hNnT5l9RHkCkQ==", + "license": "MIT" + }, + "node_modules/pg-int8": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/pg-int8/-/pg-int8-1.0.1.tgz", + "integrity": "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==", + "license": "ISC", + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/pg-pool": { + "version": "3.13.0", + "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.13.0.tgz", + "integrity": "sha512-gB+R+Xud1gLFuRD/QgOIgGOBE2KCQPaPwkzBBGC9oG69pHTkhQeIuejVIk3/cnDyX39av2AxomQiyPT13WKHQA==", + "license": "MIT", + "peerDependencies": { + "pg": ">=8.0" + } + }, + "node_modules/pg-protocol": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.13.0.tgz", + "integrity": "sha512-zzdvXfS6v89r6v7OcFCHfHlyG/wvry1ALxZo4LqgUoy7W9xhBDMaqOuMiF3qEV45VqsN6rdlcehHrfDtlCPc8w==", + "license": "MIT" + }, + "node_modules/pg-types": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/pg-types/-/pg-types-2.2.0.tgz", + "integrity": "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==", + "license": "MIT", + "dependencies": { + "pg-int8": "1.0.1", + "postgres-array": "~2.0.0", + "postgres-bytea": "~1.0.0", + "postgres-date": "~1.0.4", + "postgres-interval": "^1.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/pgpass": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/pgpass/-/pgpass-1.0.5.tgz", + "integrity": "sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug==", + "license": "MIT", + "dependencies": { + "split2": "^4.1.0" + } + }, + "node_modules/postgres-array": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz", + "integrity": "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/postgres-bytea": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-1.0.1.tgz", + "integrity": "sha512-5+5HqXnsZPE65IJZSMkZtURARZelel2oXUEO8rH83VS/hxH5vv1uHquPg5wZs8yMAfdv971IU+kcPUczi7NVBQ==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postgres-date": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-1.0.7.tgz", + "integrity": "sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postgres-interval": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-1.2.0.tgz", + "integrity": "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==", + "license": "MIT", + "dependencies": { + "xtend": "^4.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "license": "MIT", + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/qs": { + "version": "6.15.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.15.2.tgz", + "integrity": "sha512-Rzq0KEyX/w/tEybncDgdkZrJgVUsUMk3xjh3t5bv3S1HTAtg+uOYt72+ZfwiQwKdysThkTBdL/rTi6HDmX9Ddw==", + "license": "BSD-3-Clause", + "dependencies": { + "side-channel": "^1.1.0" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "2.5.3", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.3.tgz", + "integrity": "sha512-s4VSOf6yN0rvbRZGxs8Om5CWj6seneMwK3oDb4lWDH0UPhWcxwOWw5+qk24bxq87szX1ydrwylIOp2uG1ojUpA==", + "license": "MIT", + "dependencies": { + "bytes": "~3.1.2", + "http-errors": "~2.0.1", + "iconv-lite": "~0.4.24", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "license": "MIT" + }, + "node_modules/send": { + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/send/-/send-0.19.2.tgz", + "integrity": "sha512-VMbMxbDeehAxpOtWJXlcUS5E8iXh6QmN+BkRX1GARS3wRaXEEgzCcB10gTQazO42tpNIya8xIyNx8fll1OFPrg==", + "license": "MIT", + "dependencies": { + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "~0.5.2", + "http-errors": "~2.0.1", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "~2.4.1", + "range-parser": "~1.2.1", + "statuses": "~2.0.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/send/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/serve-static": { + "version": "1.16.3", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.3.tgz", + "integrity": "sha512-x0RTqQel6g5SY7Lg6ZreMmsOzncHFU7nhnRWkKgWuMTu5NN0DR5oruckMqRvacAN9d5w6ARnRBXl9xhDCgfMeA==", + "license": "MIT", + "dependencies": { + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "~0.19.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "license": "ISC" + }, + "node_modules/side-channel": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3", + "side-channel-list": "^1.0.0", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.1.tgz", + "integrity": "sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/split2": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz", + "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==", + "license": "ISC", + "engines": { + "node": ">= 10.x" + } + }, + "node_modules/statuses": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", + "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "license": "MIT", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "license": "MIT", + "dependencies": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", + "license": "MIT", + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "license": "MIT", + "engines": { + "node": ">=0.4" + } + } + } +} diff --git a/server.js b/server.js index ee4eea4..8a1909b 100644 --- a/server.js +++ b/server.js @@ -1,14 +1,18 @@ -require('dotenv').config(); +require('dotenv').config({ override: true }); const express = require('express'); const { Pool } = require('pg'); const bodyParser = require('body-parser'); const cors = require('cors'); -const path = require('path'); +const path = require('node:path'); const app = express(); +app.disable('x-powered-by'); const port = process.env.PORT || 3000; -app.use(cors()); +app.use(cors({ + origin: ['http://localhost:3000', 'http://localhost:5173', 'http://127.0.0.1:3000', 'http://127.0.0.1:5173'], + credentials: true +})); app.use(bodyParser.json()); // Serve static files from the root directory @@ -133,7 +137,7 @@ async function resolveProjectId(client, input) { // API zur Flächensicherung von Eigentümern app.post('/api/sicherung', async (req, res) => { - const { nachname, vorname, projekt_id, status, notiz } = req.body; + const { nachname, vorname, status, notiz } = req.body; const targetStatus = status || 'Gesichert'; const schema = process.env.DB_SCHEMA || 'geodaten'; diff --git a/style.css b/style.css index 53016f6..ef63116 100644 --- a/style.css +++ b/style.css @@ -298,10 +298,7 @@ body { opacity: 0.8; } -.legend-item { - display: flex; - align-items: center; - gap: 10px; +#floatingLegend .legend-item { font-size: 0.8rem; margin-bottom: 6px; color: rgba(255, 255, 255, 0.9); @@ -704,7 +701,7 @@ body { .status-select { padding: 5px 8px; border-radius: 6px; - background: rgba(255, 255, 255, 0.05); + background: #0a2d2d; border: 1px solid var(--border-color); color: white; font-size: 0.8rem;