From 19a4f0f16543aa51f4dc841e1e0ac85a32a9eec8 Mon Sep 17 00:00:00 2001 From: Johannes Baumeister Date: Fri, 8 May 2026 14:01:47 +0200 Subject: [PATCH] Feat: Add dropdown for manufacturer and implement Nordex KSF geometries --- app.js | 60 ++++++++++++++++++++++++++++++++++-------------------- index.html | 20 +++++++++++++++++- server.js | 9 ++++---- 3 files changed, 62 insertions(+), 27 deletions(-) diff --git a/app.js b/app.js index 7a361f0..4316948 100644 --- a/app.js +++ b/app.js @@ -79,6 +79,7 @@ document.addEventListener('DOMContentLoaded', async () => { const btnDeleteWEA = document.getElementById('btnDeleteWEA'); const editNr = document.getElementById('edit-wea-nr'); + const editManufacturer = document.getElementById('edit-wea-manufacturer'); const editType = document.getElementById('edit-wea-type'); const editRd = document.getElementById('edit-wea-rd'); const editNh = document.getElementById('edit-wea-nh'); @@ -95,6 +96,7 @@ document.addEventListener('DOMContentLoaded', async () => { const inputRotor = document.getElementById('rotorDiameter'); const inputHub = document.getElementById('hubHeight'); const inputFoundation = document.getElementById('foundationRadius'); + const inputManufacturer = document.getElementById('turbineManufacturer'); const inputType = document.getElementById('turbineType'); let placementMode = false; @@ -106,7 +108,7 @@ document.addEventListener('DOMContentLoaded', async () => { const wgs84 = "+proj=longlat +datum=WGS84 +no_defs"; // Calculation Functions - function calculateGeometries(latlng, rotorDiameter, hubHeight, foundationRadius, ksfAngle = 0, ksfMirrored = false) { + function calculateGeometries(latlng, rotorDiameter, hubHeight, foundationRadius, ksfAngle = 0, ksfMirrored = false, hersteller = 'Enercon') { // Ensure valid numbers for Turf const rd = parseFloat(rotorDiameter) || 160; const hh = parseFloat(hubHeight) || 165; @@ -173,24 +175,32 @@ document.addEventListener('DOMContentLoaded', async () => { return turf.polygon([finalCoords]); }; - // 4. Blattlagerfläche (BLF) - // Coords for "Nein" case (@spg=1): x is -41 - const blfCoords = [[-41, 9], [-61, 9], [-61, -81], [-41, -81], [-41, 9]]; + let blfCoords, ksfCoords, mfParts; + + if (hersteller === 'Nordex') { + // Nordex Geometries + blfCoords = [[65.15, 18.25], [155.15, 18.25], [155.15, 33.25], [65.15, 33.25], [65.15, 18.25]]; + ksfCoords = [[5.5, -18.25], [65.15, -18.25], [65.15, 18.25], [5.5, 18.25], [5.5, -18.25]]; + mfParts = [ + [[65.15, -7.5], [295.15, -7.5], [295.15, 7.5], [65.15, 7.5], [65.15, -7.5]], // AMF + [[5.5, 18.25], [31.5, 18.25], [31.5, 28.75], [5.5, 28.75], [5.5, 18.25]] // Naben-MF + ]; + } else { + // Enercon / Vestas / GE (Standard) + blfCoords = [[-41, 9], [-61, 9], [-61, -81], [-41, -81], [-41, 9]]; + ksfCoords = [[-8, 0], [-36, 0], [-36, -50], [-8, -50], [-8, 0]]; + mfParts = [ + [[-36, 0], [-36, 18], [-8, 18], [-8, 0], [-36, 0]], + [[12, -62], [-22, -62], [-22, -72], [12, -72], [12, -62]], + [[12, 0], [-8, 0], [-8, -50], [-36, -50], [-36, -72], [-22, -72], [-22, -62], [12, -62], [12, 0]], + [[-41, 18], [-47, 18], [-47, 9], [-41, 9], [-41, 18]], + [[-41, -81], [-47, -81], [-47, -96], [-41, -96], [-41, -81]], + [[-36, 18], [-41, 18], [-41, -72], [-36, -72], [-36, 18]] + ]; + } + const blf = transform(blfCoords); - - // 5. Kranstellfläche (KSF) - const ksfCoords = [[-8, 0], [-36, 0], [-36, -50], [-8, -50], [-8, 0]]; const ksf = transform(ksfCoords); - - // 6. Montagefläche (MF) - const mfParts = [ - [[-36, 0], [-36, 18], [-8, 18], [-8, 0], [-36, 0]], - [[12, -62], [-22, -62], [-22, -72], [12, -72], [12, -62]], - [[12, 0], [-8, 0], [-8, -50], [-36, -50], [-36, -72], [-22, -72], [-22, -62], [12, -62], [12, 0]], - [[-41, 18], [-47, 18], [-47, 9], [-41, 9], [-41, 18]], - [[-41, -81], [-47, -81], [-47, -96], [-41, -96], [-41, -81]], - [[-36, 18], [-41, 18], [-41, -72], [-36, -72], [-36, 18]] - ]; const mf = turf.featureCollection(mfParts.map(part => transform(part))); return { sweptArea, techDist, techDistSmall, loadRadius, foundation, blf, ksf, mf, totalHeight, utmCoords }; @@ -240,6 +250,7 @@ document.addEventListener('DOMContentLoaded', async () => { } activeTurbine = turbine; editNr.value = turbine.nr; + editManufacturer.value = turbine.hersteller || 'Enercon'; editType.value = turbine.type; editRd.value = turbine.rd; editNh.value = turbine.hh; @@ -323,11 +334,12 @@ document.addEventListener('DOMContentLoaded', async () => { 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 weaNr = overrideData?.nr || loadedNr || (state.turbines.length + 1).toString(); const ksfAngle = overrideData?.ksfAngle || 0; const ksfMirrored = overrideData?.ksfMirrored || false; - const geoms = calculateGeometries(latlng, rd, hh, fr, ksfAngle, ksfMirrored); + const geoms = calculateGeometries(latlng, rd, hh, fr, ksfAngle, ksfMirrored, hersteller); const turbineIcon = L.divIcon({ className: 'turbine-icon-container', @@ -348,7 +360,7 @@ document.addEventListener('DOMContentLoaded', async () => { id: `WEA_${Date.now()}`, nr: weaNr, variant: overrideData?.variant || state.activeVariant, - type, rd, hh, fr, latlng, + hersteller, type, rd, hh, fr, latlng, ksfAngle, ksfMirrored, totalHeight: geoms.totalHeight, layers: { @@ -413,7 +425,7 @@ document.addEventListener('DOMContentLoaded', async () => { turbine.layers.marker.on('drag', (e) => { const newPos = e.target.getLatLng(); turbine.latlng = newPos; - const newGeoms = calculateGeometries(newPos, turbine.rd, turbine.hh, turbine.fr, turbine.ksfAngle, turbine.ksfMirrored); + const newGeoms = calculateGeometries(newPos, turbine.rd, turbine.hh, turbine.fr, turbine.ksfAngle, turbine.ksfMirrored, turbine.hersteller); turbine.layers.sweptArea.clearLayers().addData(newGeoms.sweptArea); turbine.layers.techDist.clearLayers().addData(newGeoms.techDist); turbine.layers.techDistSmall.clearLayers().addData(newGeoms.techDistSmall); @@ -454,7 +466,7 @@ document.addEventListener('DOMContentLoaded', async () => { editKsfAngle.value = angle.toFixed(1); } - const newGeoms = calculateGeometries(turbine.latlng, turbine.rd, turbine.hh, turbine.fr, turbine.ksfAngle, turbine.ksfMirrored); + const newGeoms = calculateGeometries(turbine.latlng, turbine.rd, turbine.hh, turbine.fr, turbine.ksfAngle, turbine.ksfMirrored, turbine.hersteller); turbine.layers.ksf.clearLayers().addData(newGeoms.ksf); turbine.layers.blf.clearLayers().addData(newGeoms.blf); turbine.layers.mf.clearLayers().addData(newGeoms.mf); @@ -475,6 +487,7 @@ document.addEventListener('DOMContentLoaded', async () => { btnSaveEdit.onclick = () => { if (!activeTurbine) return; const newNr = editNr.value; + const newManufacturer = editManufacturer.value; const newType = editType.value; const newRd = parseFloat(editRd.value); const newNh = parseFloat(editNh.value); @@ -487,6 +500,7 @@ document.addEventListener('DOMContentLoaded', async () => { } activeTurbine.nr = newNr; + activeTurbine.hersteller = newManufacturer; activeTurbine.type = newType; activeTurbine.rd = newRd; activeTurbine.hh = newNh; @@ -494,7 +508,7 @@ document.addEventListener('DOMContentLoaded', async () => { activeTurbine.ksfAngle = newAngle; activeTurbine.ksfMirrored = newMirrored; - const geoms = calculateGeometries(activeTurbine.layers.marker.getLatLng(), newRd, newNh, newFr, newAngle, newMirrored); + const geoms = calculateGeometries(activeTurbine.layers.marker.getLatLng(), newRd, newNh, newFr, newAngle, newMirrored, newManufacturer); activeTurbine.totalHeight = geoms.totalHeight; activeTurbine.layers.sweptArea.clearLayers().addData(geoms.sweptArea); activeTurbine.layers.techDist.clearLayers().addData(geoms.techDist); @@ -1826,6 +1840,7 @@ document.addEventListener('DOMContentLoaded', async () => { const turbineData = state.turbines.map(t => ({ nr: t.nr, variant: t.variant, + hersteller: t.hersteller, type: t.type, rd: t.rd, hh: t.hh, @@ -1877,6 +1892,7 @@ document.addEventListener('DOMContentLoaded', async () => { const latlng = L.latLng(t.lat, t.lng); createTurbine(latlng, null, { nr: t.nr, + hersteller: t.hersteller, type: t.type, rd: t.rd, hh: t.hh, diff --git a/index.html b/index.html index e67fce0..f0aac6a 100644 --- a/index.html +++ b/index.html @@ -44,9 +44,18 @@

Anlagen-Konfiguration

+
+ + +
- +
@@ -151,6 +160,15 @@
+
+ + +
diff --git a/server.js b/server.js index c4b10ef..0dc26cb 100644 --- a/server.js +++ b/server.js @@ -75,9 +75,9 @@ app.post('/api/wea', async (req, res) => { log(`Füge WEA ein: Nr=${t.nr}, Typ=${t.type}, Pos=${t.latlng?.lat},${t.latlng?.lng}`); await client.query( `INSERT INTO ${schema}.wea_standorte - (projekt_id, wea_nummer, anlagentyp, nabenhoehe, rotordurchmesser, ksf_drehung, variante, geom) - VALUES ($1, $2, $3, $4, $5, $6, $7, ST_Transform(ST_SetSRID(ST_MakePoint($8, $9), 4326), 25832))`, - [targetProject, t.nr, t.type, t.hh, t.rd, t.ksfAngle, t.variant, t.latlng.lng, t.latlng.lat] + (projekt_id, wea_nummer, hersteller, anlagentyp, nabenhoehe, rotordurchmesser, ksf_drehung, variante, geom) + VALUES ($1, $2, $3, $4, $5, $6, $7, $8, ST_Transform(ST_SetSRID(ST_MakePoint($9, $10), 4326), 25832))`, + [targetProject, t.nr, t.hersteller, t.type, t.hh, t.rd, t.ksfAngle, t.variant, t.latlng.lng, t.latlng.lat] ); } } @@ -100,7 +100,7 @@ app.get('/api/wea/:projekt_id', async (req, res) => { log(`Lade WEAs für Projekt: ${projekt_id}`); try { const result = await pool.query( - `SELECT wea_nummer as nr, anlagentyp as type, nabenhoehe as hh, rotordurchmesser as rd, ksf_drehung as ksfAngle, variante as variant, + `SELECT wea_nummer as nr, hersteller as hersteller, anlagentyp as type, nabenhoehe as hh, rotordurchmesser as rd, ksf_drehung as ksfAngle, variante as variant, ST_X(ST_Transform(geom, 4326)) as lng, ST_Y(ST_Transform(geom, 4326)) as lat FROM geodaten.wea_standorte WHERE projekt_id = $1`, [projekt_id] @@ -226,6 +226,7 @@ app.get('/api/layers/alkis', async (req, res) => { 'PLZ', "PLZ", 'ORP', "ORP", 'STR', "STR", + 'HSN', "HSN", 'status', status, 'notiz', notiz )