From a844d8a8724a6220edaf36d7c59c9c5b84716b98 Mon Sep 17 00:00:00 2001 From: Johannes Baumeister Date: Fri, 3 Jul 2026 22:17:33 +0200 Subject: [PATCH] feat: Import WEA types from Excel and implement dynamic dropdown UI --- Anlagentypen/Anlagentypen.xlsx | Bin 17790 -> 19769 bytes app.js | 97 ++++++++++++++++++++++++++---- append_pdf.js | 97 ++++++++++++++++++++++++++++++ db_add_column.js | 30 ++++++++++ db_add_planungsstand.js | 25 ++++++++ db_check_potential.js | 31 ++++++++++ db_update_artennachweise.js | 40 +++++++++++++ db_update_temp.js | 54 +++++++++++++++++ enwelo_db.session.sql | 11 ++++ import_umring.js | 61 +++++++++++++++++++ import_wea.js | 64 ++++++++++++++++++++ index.html | 46 +++++--------- package-lock.json | 106 ++++++++++++++++++++++++++++++++- package.json | 3 +- read_excel.js | 7 +++ server.js | 14 +++++ 16 files changed, 643 insertions(+), 43 deletions(-) create mode 100644 append_pdf.js create mode 100644 db_add_column.js create mode 100644 db_add_planungsstand.js create mode 100644 db_check_potential.js create mode 100644 db_update_artennachweise.js create mode 100644 db_update_temp.js create mode 100644 import_umring.js create mode 100644 import_wea.js create mode 100644 read_excel.js diff --git a/Anlagentypen/Anlagentypen.xlsx b/Anlagentypen/Anlagentypen.xlsx index 823445d25c0a0b74b23ef0c3c46d12050e6201a8..35aaeb4bbf868b29497a9b3aa5d7e50a59dbbbb3 100644 GIT binary patch delta 12879 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 delta 10011 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 AMF0Q* diff --git a/app.js b/app.js index 35dfddb..3562772 100644 --- a/app.js +++ b/app.js @@ -110,8 +110,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 editWeaSelector = document.getElementById('edit-wea-selector'); const editRd = document.getElementById('edit-wea-rd'); const editNh = document.getElementById('edit-wea-nh'); const editFr = document.getElementById('edit-wea-fr'); @@ -127,8 +126,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'); + const weaTypeSelector = document.getElementById('weaTypeSelector'); let placementMode = false; let measureMode = null; // 'dist' or 'area' @@ -291,8 +289,20 @@ document.addEventListener('DOMContentLoaded', async () => { } activeTurbine = turbine; editNr.value = turbine.nr; - editManufacturer.value = turbine.hersteller || 'Enercon'; - editType.value = turbine.type; + let matched = false; + for (const opt of editWeaSelector.options) { + if (opt.value) { + try { + const data = JSON.parse(opt.value); + if (data.hersteller === turbine.hersteller && data.anlagentyp === turbine.type && data.nabenhoehe === turbine.hh) { + editWeaSelector.value = opt.value; + matched = true; + break; + } + } catch(e) {} + } + } + if (!matched) editWeaSelector.value = ""; editRd.value = turbine.rd; editNh.value = turbine.hh; editFr.value = turbine.fr || 15; @@ -374,8 +384,15 @@ document.addEventListener('DOMContentLoaded', async () => { 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"; + let type = overrideData?.type || "Standard Typ"; + let hersteller = overrideData?.hersteller || "Unbekannt"; + if (!overrideData && weaTypeSelector && weaTypeSelector.value) { + try { + const parsed = JSON.parse(weaTypeSelector.value); + type = parsed.anlagentyp; + hersteller = parsed.hersteller; + } catch(e) {} + } const weaNr = overrideData?.nr || loadedNr || (state.turbines.length + 1).toString(); const ksfAngle = overrideData?.ksfAngle || 0; const ksfMirrored = overrideData?.ksfMirrored || false; @@ -536,8 +553,15 @@ document.addEventListener('DOMContentLoaded', async () => { btnSaveEdit.onclick = () => { if (!activeTurbine) return; const newNr = editNr.value; - const newManufacturer = editManufacturer.value; - const newType = editType.value; + let newManufacturer = activeTurbine.hersteller; + let newType = activeTurbine.type; + if (editWeaSelector && editWeaSelector.value) { + try { + const parsed = JSON.parse(editWeaSelector.value); + newManufacturer = parsed.hersteller; + newType = parsed.anlagentyp; + } catch(e) {} + } const newRd = Number.parseFloat(editRd.value); const newNh = Number.parseFloat(editNh.value); const newFr = Number.parseFloat(editFr.value) || 15; @@ -2164,6 +2188,58 @@ document.addEventListener('DOMContentLoaded', async () => { } // Initialize allowed projects and project selector + window.availableWeaTypes = []; + + async function loadWeaTypes() { + try { + const res = await fetch('/api/wea-types'); + if (!res.ok) throw new Error('Network response was not ok'); + window.availableWeaTypes = await res.json(); + + // Build grouped options + const groups = {}; + window.availableWeaTypes.forEach(wea => { + if (!groups[wea.hersteller]) groups[wea.hersteller] = []; + groups[wea.hersteller].push(wea); + }); + + let html = ''; + for (const h in groups) { + html += ``; + groups[h].forEach(wea => { + const jsonStr = JSON.stringify(wea).replace(/"/g, '"'); + html += ``; + }); + html += ``; + } + + if (weaTypeSelector) { + weaTypeSelector.innerHTML = html; + weaTypeSelector.addEventListener('change', (e) => { + if (!e.target.value) return; + try { + const data = JSON.parse(e.target.value.replace(/"/g, '"')); + inputRotor.value = data.rotordurchmesser; + inputHub.value = data.nabenhoehe; + } catch(err) {} + }); + } + if (editWeaSelector) { + editWeaSelector.innerHTML = html; + editWeaSelector.addEventListener('change', (e) => { + if (!e.target.value) return; + try { + const data = JSON.parse(e.target.value.replace(/"/g, '"')); + editRd.value = data.rotordurchmesser; + editNh.value = data.nabenhoehe; + } catch(err) {} + }); + } + } catch (err) { + console.error('Fehler beim Laden der Anlagentypen:', err); + } + } + async function initializeProjects() { const statusEl = document.getElementById('statusInfo'); try { @@ -2258,6 +2334,7 @@ document.addEventListener('DOMContentLoaded', async () => { } // Start loading process + await loadWeaTypes(); initializeProjects(); // Project Persistence diff --git a/append_pdf.js b/append_pdf.js new file mode 100644 index 0000000..56ed14e --- /dev/null +++ b/append_pdf.js @@ -0,0 +1,97 @@ +const fs = require('fs'); +const js = ` + // --- PDF Export Logic --- + const btnExportPDF = document.getElementById('btnExportPDF'); + if (btnExportPDF) { + btnExportPDF.addEventListener('click', async () => { + const originalText = btnExportPDF.innerText; + btnExportPDF.innerText = 'Exportiere...'; + btnExportPDF.disabled = true; + + try { + if (typeof html2canvas === 'undefined' || typeof jspdf === 'undefined') { + throw new Error('PDF Bibliotheken (html2canvas, jspdf) sind noch nicht geladen.'); + } + + const mapContainer = document.getElementById('map'); + + // Add Overlay + const exportOverlay = document.createElement('div'); + exportOverlay.style.position = 'absolute'; + exportOverlay.style.bottom = '30px'; + exportOverlay.style.left = '30px'; + exportOverlay.style.zIndex = '9999'; + exportOverlay.style.background = 'rgba(255, 255, 255, 0.95)'; + exportOverlay.style.padding = '15px'; + exportOverlay.style.border = '2px solid #333'; + exportOverlay.style.borderRadius = '5px'; + exportOverlay.style.color = '#333'; + exportOverlay.style.fontFamily = 'Arial, sans-serif'; + exportOverlay.style.boxShadow = '0 0 10px rgba(0,0,0,0.3)'; + + const projName = document.getElementById('projectSelector')?.options[document.getElementById('projectSelector')?.selectedIndex]?.text || 'Unbekannt'; + + exportOverlay.innerHTML = \` +
ENWELO Standortplanung
+
Projekt: \${projName}
+
+
Rotorüberstreichfläche
+
Gebietsumring
+
Abstandslinien
+
WEA Standort
+
+ \`; + mapContainer.appendChild(exportOverlay); + + // Wait a tiny bit for DOM to render overlay + await new Promise(r => setTimeout(r, 100)); + + const canvas = await html2canvas(mapContainer, { + useCORS: true, + allowTaint: true, + ignoreElements: (element) => { + return element.classList.contains('leaflet-control-zoom'); + } + }); + + mapContainer.removeChild(exportOverlay); + + const imgData = canvas.toDataURL('image/png'); + const pdf = new jspdf.jsPDF({ + orientation: 'landscape', + unit: 'mm', + format: 'a3' + }); + + const pdfWidth = pdf.internal.pageSize.getWidth(); + const pdfHeight = pdf.internal.pageSize.getHeight(); + + const imgProps = pdf.getImageProperties(imgData); + const ratio = imgProps.width / imgProps.height; + + let renderWidth = pdfWidth; + let renderHeight = pdfWidth / ratio; + + if (renderHeight > pdfHeight) { + renderHeight = pdfHeight; + renderWidth = pdfHeight * ratio; + } + + // Center image in PDF + const xOffset = (pdfWidth - renderWidth) / 2; + const yOffset = (pdfHeight - renderHeight) / 2; + + pdf.addImage(imgData, 'PNG', xOffset, yOffset, renderWidth, renderHeight); + pdf.save('Karte_Enwelo.pdf'); + + } catch (err) { + console.error('Fehler beim PDF Export:', err); + alert('Fehler beim PDF Export: ' + err.message); + } finally { + btnExportPDF.innerText = originalText; + btnExportPDF.disabled = false; + } + }); + } +`; +fs.appendFileSync('app.js', js); diff --git a/db_add_column.js b/db_add_column.js new file mode 100644 index 0000000..ecef006 --- /dev/null +++ b/db_add_column.js @@ -0,0 +1,30 @@ +const { Pool } = require('pg'); + +const pool = new Pool({ + user: 'enwelo_admin', + host: '87.106.21.21', + database: 'enwelo', + password: 'WX1t1cgP1qK09', + port: 5432, + ssl: false +}); + +async function run() { + try { + await pool.query('ALTER TABLE geodaten."potentialflächen_wind" ADD COLUMN IF NOT EXISTS projekt_id VARCHAR(255);'); + console.log('Column projekt_id added successfully.'); + // Set for samern-ohne as default since they want to see it! + // But wait, what if there's only one row? + const res = await pool.query('SELECT id FROM geodaten."potentialflächen_wind"'); + console.log('Found rows:', res.rowCount); + if (res.rowCount === 1) { + await pool.query("UPDATE geodaten.\"potentialflächen_wind\" SET projekt_id = 'projekt_bw_samern-ohne'"); + console.log('Updated the single row to projekt_bw_samern-ohne'); + } + } catch(e) { + console.error('Fehler:', e); + } finally { + await pool.end(); + } +} +run(); diff --git a/db_add_planungsstand.js b/db_add_planungsstand.js new file mode 100644 index 0000000..735d95d --- /dev/null +++ b/db_add_planungsstand.js @@ -0,0 +1,25 @@ +const { Pool } = require('pg'); + +const pool = new Pool({ + user: 'enwelo_admin', + host: '87.106.21.21', + database: 'enwelo', + password: 'WX1t1cgP1qK09', + port: 5432, + ssl: false +}); + +async function run() { + try { + await pool.query('ALTER TABLE geodaten."potentialflächen_wind" ADD COLUMN IF NOT EXISTS planungsstand VARCHAR(255);'); + console.log('Column planungsstand added successfully.'); + + const res = await pool.query("UPDATE geodaten.\"potentialflächen_wind\" SET planungsstand = 'rechtsverbindlich'"); + console.log(`Updated ${res.rowCount} rows to rechtsverbindlich`); + } catch(e) { + console.error('Fehler:', e); + } finally { + await pool.end(); + } +} +run(); diff --git a/db_check_potential.js b/db_check_potential.js new file mode 100644 index 0000000..da4243f --- /dev/null +++ b/db_check_potential.js @@ -0,0 +1,31 @@ +const { Pool } = require('pg'); + +const pool = new Pool({ + user: 'enwelo_admin', + host: '87.106.21.21', + database: 'enwelo', + password: 'WX1t1cgP1qK09', + port: 5432, + ssl: false +}); + +async function run() { + try { + const res = await pool.query("SELECT column_name, data_type FROM information_schema.columns WHERE table_schema = 'geodaten' AND table_name = 'potentialflächen_wind';"); + console.log('Columns:', res.rows); + + // Check if projekt_id exists in columns + const hasProjektId = res.rows.some(r => r.column_name === 'projekt_id'); + if (hasProjektId) { + const dataRes = await pool.query('SELECT DISTINCT projekt_id FROM geodaten."potentialflächen_wind";'); + console.log('Projects:', dataRes.rows); + } else { + console.log('No projekt_id column found.'); + } + } catch(e) { + console.error('Fehler:', e); + } finally { + await pool.end(); + } +} +run(); diff --git a/db_update_artennachweise.js b/db_update_artennachweise.js new file mode 100644 index 0000000..e23221e --- /dev/null +++ b/db_update_artennachweise.js @@ -0,0 +1,40 @@ +const { Pool } = require('pg'); + +const pool = new Pool({ + user: 'enwelo_admin', + host: '87.106.21.21', + database: 'enwelo', + password: 'WX1t1cgP1qK09', + port: 5432, + ssl: false +}); + +async function run() { + try { + const client = await pool.connect(); + await client.query('BEGIN'); + + // Update artennachweise + let res = await client.query("UPDATE geodaten.artennachweise SET projekt_id = 'trasse_bw_scheddebrock' WHERE projekt_id = 'BW Scheddebrock'"); + console.log('Artennachweise (trasse_bw_scheddebrock):', res.rowCount); + + res = await client.query("UPDATE geodaten.artennachweise SET projekt_id = 'projekt_bw_samern-ohne' WHERE projekt_id = 'bw_samern-ohne'"); + console.log('Artennachweise (projekt_bw_samern-ohne):', res.rowCount); + + res = await client.query("UPDATE geodaten.artennachweise SET projekt_id = 'projekt_bw_test' WHERE projekt_id = 'test'"); + console.log('Artennachweise (projekt_bw_test):', res.rowCount); + + res = await client.query("UPDATE geodaten.artennachweise SET projekt_id = 'projekt_bw_an_der_landwehr' WHERE projekt_id ILIKE '%landwehr%'"); + console.log('Artennachweise (projekt_bw_an_der_landwehr):', res.rowCount); + + await client.query('COMMIT'); + console.log('--- ERFOLGREICH GESPEICHERT ---'); + client.release(); + } catch(e) { + await pool.query('ROLLBACK'); + console.error('Fehler:', e); + } finally { + await pool.end(); + } +} +run(); diff --git a/db_update_temp.js b/db_update_temp.js new file mode 100644 index 0000000..1079a9e --- /dev/null +++ b/db_update_temp.js @@ -0,0 +1,54 @@ +const { Pool } = require('pg'); + +const pool = new Pool({ + user: 'enwelo_admin', + host: '87.106.21.21', + database: 'enwelo', + password: 'WX1t1cgP1qK09', + port: 5432, + ssl: false +}); + +async function run() { + try { + const client = await pool.connect(); + + await client.query('BEGIN'); + + // 1. Projekte + let res = await client.query("UPDATE geodaten.projekte SET name = 'trasse_bw_scheddebrock' WHERE name = 'BW Scheddebrock'"); + console.log('Projekte (trasse_bw_scheddebrock):', res.rowCount); + + res = await client.query("UPDATE geodaten.projekte SET name = 'projekt_bw_samern-ohne' WHERE name = 'bw_samern-ohne'"); + console.log('Projekte (projekt_bw_samern-ohne):', res.rowCount); + + res = await client.query("UPDATE geodaten.projekte SET name = 'projekt_bw_test' WHERE name = 'test'"); + console.log('Projekte (projekt_bw_test):', res.rowCount); + + res = await client.query("UPDATE geodaten.projekte SET name = 'projekt_bw_an_der_landwehr' WHERE name ILIKE '%landwehr%'"); + console.log('Projekte (projekt_bw_an_der_landwehr):', res.rowCount); + + // 2. WEA Standorte + res = await client.query("UPDATE geodaten.wea_standorte SET projekt_id = 'trasse_bw_scheddebrock' WHERE projekt_id = 'BW Scheddebrock'"); + console.log('WEA (trasse_bw_scheddebrock):', res.rowCount); + + res = await client.query("UPDATE geodaten.wea_standorte SET projekt_id = 'projekt_bw_samern-ohne' WHERE projekt_id = 'bw_samern-ohne'"); + console.log('WEA (projekt_bw_samern-ohne):', res.rowCount); + + res = await client.query("UPDATE geodaten.wea_standorte SET projekt_id = 'projekt_bw_test' WHERE projekt_id = 'test'"); + console.log('WEA (projekt_bw_test):', res.rowCount); + + res = await client.query("UPDATE geodaten.wea_standorte SET projekt_id = 'projekt_bw_an_der_landwehr' WHERE projekt_id ILIKE '%landwehr%'"); + console.log('WEA (projekt_bw_an_der_landwehr):', res.rowCount); + + await client.query('COMMIT'); + console.log('--- ERFOLGREICH GESPEICHERT ---'); + client.release(); + } catch(e) { + await pool.query('ROLLBACK'); + console.error('Fehler:', e); + } finally { + await pool.end(); + } +} +run(); diff --git a/enwelo_db.session.sql b/enwelo_db.session.sql index e69de29..e9267cb 100644 --- a/enwelo_db.session.sql +++ b/enwelo_db.session.sql @@ -0,0 +1,11 @@ +-- 1. Aktualisieren der Haupttabelle "projekte" +UPDATE geodaten.projekte SET name = 'trasse_bw_scheddebrock' WHERE name = 'BW Scheddebrock'; +UPDATE geodaten.projekte SET name = 'projekt_bw_samern-ohne' WHERE name = 'bw_samern-ohne'; +UPDATE geodaten.projekte SET name = 'projekt_bw_test' WHERE name = 'test'; +UPDATE geodaten.projekte SET name = 'projekt_bw_an_der_landwehr' WHERE name ILIKE '%landwehr%'; + +-- 2. Aktualisieren der Verknüpfungen in den Standorten (WEA) +UPDATE geodaten.wea_standorte SET projekt_id = 'trasse_bw_scheddebrock' WHERE projekt_id = 'BW Scheddebrock'; +UPDATE geodaten.wea_standorte SET projekt_id = 'projekt_bw_samern-ohne' WHERE projekt_id = 'bw_samern-ohne'; +UPDATE geodaten.wea_standorte SET projekt_id = 'projekt_bw_test' WHERE projekt_id = 'test'; +UPDATE geodaten.wea_standorte SET projekt_id = 'projekt_bw_an_der_landwehr' WHERE projekt_id ILIKE '%landwehr%'; diff --git a/import_umring.js b/import_umring.js new file mode 100644 index 0000000..22c0af0 --- /dev/null +++ b/import_umring.js @@ -0,0 +1,61 @@ +const shapefile = require('shapefile'); +const { Pool } = require('pg'); + +const pool = new Pool({ + user: 'enwelo_admin', + host: '87.106.21.21', + database: 'enwelo', + password: 'WX1t1cgP1qK09', + port: 5432, + ssl: false +}); + +async function run() { + try { + console.log("Reading shapefile..."); + const geojson = await shapefile.read("Shapefile/Windeignungsgebiet.shp", "Shapefile/Windeignungsgebiet.dbf"); + console.log(`Loaded ${geojson.features.length} features.`); + + if (geojson.features.length === 0) { + console.log("No features found!"); + return; + } + + const client = await pool.connect(); + try { + await client.query('BEGIN'); + + // wir wollen die Shapefile-Geometrie für samern-ohne in die DB pushen + const projektId = 'projekt_bw_samern-ohne'; + + // Zuerst bestehende löschen? + await client.query('DELETE FROM geodaten."potentialflächen_wind" WHERE projekt_id = $1', [projektId]); + + let count = 0; + for (const feature of geojson.features) { + const geomJson = JSON.stringify(feature.geometry); + + await client.query( + `INSERT INTO geodaten."potentialflächen_wind" (projekt_id, geom) + VALUES ($1, ST_SetSRID(ST_GeomFromGeoJSON($2), 25832))`, + [projektId, geomJson] + ); + count++; + } + + await client.query('COMMIT'); + console.log(`Erfolgreich ${count} Potentialflächen für ${projektId} importiert!`); + } catch (e) { + await client.query('ROLLBACK'); + throw e; + } finally { + client.release(); + } + } catch(e) { + console.error("Fehler:", e); + } finally { + await pool.end(); + } +} + +run(); diff --git a/import_wea.js b/import_wea.js new file mode 100644 index 0000000..c8af5dc --- /dev/null +++ b/import_wea.js @@ -0,0 +1,64 @@ +require('dotenv').config({ override: true }); +const { Pool } = require('pg'); +const xlsx = require('xlsx'); + +const pool = new Pool({ + host: process.env.DB_HOST, + port: process.env.DB_PORT, + database: process.env.DB_NAME, + user: process.env.DB_USER, + password: process.env.DB_PASSWORD +}); + +async function importWea() { + const wb = xlsx.readFile('./Anlagentypen/Anlagentypen.xlsx'); + const sheet = wb.Sheets[wb.SheetNames[0]]; + const data = xlsx.utils.sheet_to_json(sheet); + + try { + console.log('Connecting to DB...'); + await pool.query('BEGIN'); + + console.log('Dropping and creating table geodaten.anlagendaten_wea...'); + await pool.query(` + DROP TABLE IF EXISTS geodaten.anlagendaten_wea; + CREATE TABLE geodaten.anlagendaten_wea ( + id SERIAL PRIMARY KEY, + hersteller VARCHAR(255), + anlagentyp VARCHAR(255), + nennleistung FLOAT, + nabenhoehe FLOAT, + rotordurchmesser FLOAT, + gesamthoehe FLOAT + ); + `); + + console.log(`Importing ${data.length} rows...`); + for (const row of data) { + // Keys from Excel + // "Hersteller", "Anlagentyp", "Nennleistung (MW)", "Nabenhöhe (m)", "Rotordurchmesser (m)", "Gesamthöhe (m)" + const hersteller = row['Hersteller'] || ''; + const anlagentyp = row['Anlagentyp'] || ''; + const nennleistung = parseFloat(row['Nennleistung (MW)'] || 0); + const nabenhoehe = parseFloat(row['Nabenhöhe (m)'] || 0); + const rotordurchmesser = parseFloat(row['Rotordurchmesser (m)'] || 0); + const gesamthoehe = parseFloat(row['Gesamthöhe (m)'] || 0); + + await pool.query(` + INSERT INTO geodaten.anlagendaten_wea + (hersteller, anlagentyp, nennleistung, nabenhoehe, rotordurchmesser, gesamthoehe) + VALUES ($1, $2, $3, $4, $5, $6) + `, [hersteller, anlagentyp, nennleistung, nabenhoehe, rotordurchmesser, gesamthoehe]); + } + + await pool.query('COMMIT'); + console.log('Import finished successfully.'); + } catch (err) { + await pool.query('ROLLBACK'); + console.error('Error importing:', err); + } finally { + await pool.end(); + } +} + +importWea(); diff --git a/index.html b/index.html index bdf7e09..0a84af1 100644 --- a/index.html +++ b/index.html @@ -74,28 +74,19 @@ WEA Konfiguration +
-
-
- - -
-
- - -
+
+ +
- - + +
- - + +
@@ -180,26 +171,19 @@
-
- - +
-
- - -
- +
- +
diff --git a/package-lock.json b/package-lock.json index d235529..9d7c2b6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,7 +13,8 @@ "dotenv": "^16.4.5", "express": "^4.19.2", "pg": "^8.11.5", - "shapefile": "^0.6.6" + "shapefile": "^0.6.6", + "xlsx": "^0.18.5" } }, "node_modules/accepts": { @@ -29,6 +30,15 @@ "node": ">= 0.6" } }, + "node_modules/adler-32": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/adler-32/-/adler-32-1.3.1.tgz", + "integrity": "sha512-ynZ4w/nUUv5rrsR8UUGoe1VC9hZj6V5hU9Qw1HlMDJGEJw5S7TfTErWTjMys6M7vr0YWcPqs3qAr4ss0nDfP+A==", + "license": "Apache-2.0", + "engines": { + "node": ">=0.8" + } + }, "node_modules/array-flatten": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", @@ -103,6 +113,28 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/cfb": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/cfb/-/cfb-1.2.2.tgz", + "integrity": "sha512-KfdUZsSOw19/ObEWasvBP/Ac4reZvAGauZhs6S/gqNhXhI7cKwvlH7ulj+dOEYnca4bm4SGo8C1bTAQvnTjgQA==", + "license": "Apache-2.0", + "dependencies": { + "adler-32": "~1.3.0", + "crc-32": "~1.2.0" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/codepage": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/codepage/-/codepage-1.15.0.tgz", + "integrity": "sha512-3g6NUTPd/YtuuGrhMnOMRjFc+LJw/bnMp3+0r/Wcz3IXUuCosKRJvMphm5+Q+bvTVGcJJuRvVLuYba+WojaFaA==", + "license": "Apache-2.0", + "engines": { + "node": ">=0.8" + } + }, "node_modules/commander": { "version": "2.20.3", "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", @@ -162,6 +194,18 @@ "url": "https://opencollective.com/express" } }, + "node_modules/crc-32": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/crc-32/-/crc-32-1.2.2.tgz", + "integrity": "sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==", + "license": "Apache-2.0", + "bin": { + "crc32": "bin/crc32.njs" + }, + "engines": { + "node": ">=0.8" + } + }, "node_modules/debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", @@ -358,6 +402,15 @@ "node": ">= 0.6" } }, + "node_modules/frac": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/frac/-/frac-1.1.2.tgz", + "integrity": "sha512-w/XBfkibaTl3YDqASwfDUqkna4Z2p9cFSr1aHDt0WoMTECnRfBOv2WArlZILlqgWlmdIlALXGpM2AOhEk5W3IA==", + "license": "Apache-2.0", + "engines": { + "node": ">=0.8" + } + }, "node_modules/fresh": { "version": "0.5.2", "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", @@ -1001,6 +1054,18 @@ "node": ">= 10.x" } }, + "node_modules/ssf": { + "version": "0.11.2", + "resolved": "https://registry.npmjs.org/ssf/-/ssf-0.11.2.tgz", + "integrity": "sha512-+idbmIXoYET47hH+d7dfm2epdOMUDjqcB4648sTZ+t2JwoyBFL/insLfB/racrDmsKB3diwsDA696pZMieAC5g==", + "license": "Apache-2.0", + "dependencies": { + "frac": "~1.1.2" + }, + "engines": { + "node": ">=0.8" + } + }, "node_modules/statuses": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", @@ -1072,6 +1137,45 @@ "node": ">= 0.8" } }, + "node_modules/wmf": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wmf/-/wmf-1.0.2.tgz", + "integrity": "sha512-/p9K7bEh0Dj6WbXg4JG0xvLQmIadrner1bi45VMJTfnbVHsc7yIajZyoSoK60/dtVBs12Fm6WkUI5/3WAVsNMw==", + "license": "Apache-2.0", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/word": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/word/-/word-0.3.0.tgz", + "integrity": "sha512-OELeY0Q61OXpdUfTp+oweA/vtLVg5VDOXh+3he3PNzLGG/y0oylSOC1xRVj0+l4vQ3tj/bB1HVHv1ocXkQceFA==", + "license": "Apache-2.0", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/xlsx": { + "version": "0.18.5", + "resolved": "https://registry.npmjs.org/xlsx/-/xlsx-0.18.5.tgz", + "integrity": "sha512-dmg3LCjBPHZnQp5/F/+nnTa+miPJxUXB6vtk42YjBBKayDNagxGEeIdWApkYPOf3Z3pm3k62Knjzp7lMeTEtFQ==", + "license": "Apache-2.0", + "dependencies": { + "adler-32": "~1.3.0", + "cfb": "~1.2.1", + "codepage": "~1.15.0", + "crc-32": "~1.2.1", + "ssf": "~0.11.2", + "wmf": "~1.0.1", + "word": "~0.3.0" + }, + "bin": { + "xlsx": "bin/xlsx.njs" + }, + "engines": { + "node": ">=0.8" + } + }, "node_modules/xtend": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", diff --git a/package.json b/package.json index 00c2005..2e8b6d2 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,8 @@ "dotenv": "^16.4.5", "express": "^4.19.2", "pg": "^8.11.5", - "shapefile": "^0.6.6" + "shapefile": "^0.6.6", + "xlsx": "^0.18.5" }, "scripts": { "start": "node server.js" diff --git a/read_excel.js b/read_excel.js new file mode 100644 index 0000000..b8ccce4 --- /dev/null +++ b/read_excel.js @@ -0,0 +1,7 @@ +const xlsx = require('xlsx'); +const wb = xlsx.readFile('./Anlagentypen/Anlagentypen.xlsx'); +const sheetName = wb.SheetNames[0]; +const sheet = wb.Sheets[sheetName]; +const data = xlsx.utils.sheet_to_json(sheet); +console.log('Columns:', Object.keys(data[0] || {})); +console.log('Sample Data:', data.slice(0, 3)); diff --git a/server.js b/server.js index 251e32c..0df1436 100644 --- a/server.js +++ b/server.js @@ -215,6 +215,20 @@ app.get('/api/logs', (req, res) => { res.type('text/plain').send(serverLogs.join('\n')); }); +// API to load available WEA types +app.get('/api/wea-types', async (req, res) => { + const client = await pool.connect(); + try { + const result = await client.query('SELECT * FROM geodaten.anlagendaten_wea ORDER BY hersteller, anlagentyp, nabenhoehe'); + res.status(200).json(result.rows); + } catch (e) { + log(`FEHLER beim Laden der Anlagentypen: ${e.message}`); + res.status(500).json({ error: e.message }); + } finally { + client.release(); + } +}); + // API to save turbines app.post('/api/wea', async (req, res) => { const { projekt_id, turbines } = req.body;