]*>(.*?)<\/gml:posList>/gs;
let match;
while ((match = regex.exec(text)) !== null) {
const coordsStr = match[1].trim().split(/\s+/);
const coords = [];
// EPSG:4326 in MapServer WFS 1.1.0 is generally Lat Lon
for (let i = 0; i < coordsStr.length; i += 2) {
const lat = parseFloat(coordsStr[i]);
const lon = parseFloat(coordsStr[i + 1]);
if (!isNaN(lat) && !isNaN(lon)) {
coords.push([lon, lat]); // Turf uses Lon, Lat
}
}
if (coords.length > 1) {
// Add a dummy properties object to store the type name later
features.push(turf.lineString(coords, { type: type }));
}
}
return features;
} catch (e) {
console.error("WFS GML Error:", e);
return [];
}
};
/**
* Perform a WMS GetFeatureInfo request to check for features at a point
*/
const queryWMS = async (proxy, layers, pos) => {
const utm = proj4(EPSG4326, EPSG25832, [pos.lng, pos.lat]);
const size = 5;
const bbox = `${utm[0] - size},${utm[1] - size},${utm[0] + size},${utm[1] + size}`;
const infoFormat = encodeURIComponent('application/geo+json');
const url = `/api/wms/${proxy}?SERVICE=WMS&VERSION=1.1.1&REQUEST=GetFeatureInfo&LAYERS=${layers}&QUERY_LAYERS=${layers}&BBOX=${bbox}&FEATURE_COUNT=1&HEIGHT=100&WIDTH=100&X=50&Y=50&SRS=EPSG:25832&INFO_FORMAT=${infoFormat}&STYLES=`;
try {
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), 10000); // 10s timeout for WMS
const r = await fetch(url, { signal: controller.signal });
if (!r.ok) {
clearTimeout(timeoutId);
throw new Error("WMS Status " + r.status);
}
const text = await r.text();
clearTimeout(timeoutId);
// Handle HTML/XML responses (often returned when no features found or format not supported)
if (text.trim().startsWith(' elements that contain actual alphanumeric data.
const hasDataCells = /]*>.*?\w+.*?<\/td>/i.test(text);
const isNoResult = lowerText.includes('keine information') || lowerText.includes('no features found') || lowerText.includes('empty') || lowerText.includes('keine objekte');
if (hasDataCells && !isNoResult) {
return [{ properties: { info: "Feature found" } }];
}
return [];
}
try {
const d = JSON.parse(text);
return d.features || d.results || [];
} catch (jsonErr) {
// If JSON fails, it might be plain text
const lowerText = text.toLowerCase();
const isNoResult = lowerText.includes('no results') || lowerText.includes('keine objekte') || lowerText.includes('no features found') || text.trim() === '';
if (!isNoResult && text.trim().length > 0) {
return [{ properties: { info: text.trim() } }];
}
return [];
}
} catch (e) {
console.error("WMS Error:", e);
return [];
}
};
/**
* Initialize a mini map in the report area
*/
const initReportMiniMap = (pos) => {
const mapDom = document.getElementById('report-mini-map');
if (!mapDom) return;
// Short timeout to ensure DOM is rendered
setTimeout(() => {
const miniMap = L.map('report-mini-map', {
center: [pos.lat, pos.lng],
zoom: 16, // Weiter rausgezoomt
zoomControl: false,
attributionControl: false,
dragging: false,
scrollWheelZoom: false,
doubleClickZoom: false,
boxZoom: false,
touchZoom: false,
keyboard: false
});
// DOP Layer
L.tileLayer.wms('/api/wms/dop', {
layers: 'nw_dop_rgb',
format: 'image/png',
transparent: false,
version: '1.3.0'
}).addTo(miniMap);
// ALKIS Layer (Parcels)
L.tileLayer.wms('/api/wms/alkis', {
layers: 'adv_alkis_flurstuecke',
format: 'image/png',
transparent: true,
styles: 'Gelb',
version: '1.3.0'
}).addTo(miniMap);
// Standort als einfacher Punkt (CircleMarker)
L.circleMarker([pos.lat, pos.lng], {
radius: 7,
fillColor: "#ff4444",
color: "#fff",
weight: 2,
opacity: 1,
fillOpacity: 0.9
}).addTo(miniMap);
}, 200);
};
document.getElementById('check-site-btn').onclick = async () => {
if (!userMarker) return;
const pos = userMarker.getLatLng();
const resultsArea = document.getElementById('results-area');
const today = new Date().toLocaleDateString('de-DE');
// Header & Loading
resultsArea.innerHTML = `
Bitte haben Sie einen Moment Geduld...
Abfrage der Geodaten läuft...
Analyse läuft...
`;
resultsArea.classList.add('results-visible');
// UTM / Coords Helper
const utm = proj4(EPSG4326, EPSG25832, [pos.lng, pos.lat]);
// Category Data Fetching
const fetchParcel = async () => {
try {
// Primär: Neuer lokaler ALKIS-Handler (GML-Parsing im Backend)
const r = await fetch(`/api/local/alkis-info?lat=${pos.lat}&lng=${pos.lng}`);
if (r.ok) {
const data = await r.json();
if (data.found) {
const p = data.properties;
return `
Prüfdatum: ${today}
Koordinaten (WGS84): ${pos.lat.toFixed(6)}, ${pos.lng.toFixed(6)}
Koordinaten (UTM32): ${Math.round(utm[0])}, ${Math.round(utm[1])}
Kreis / Gemeinde: ${p.kreis || '-'} / ${p.gemeinde || '-'}
Gemarkung: ${p.gemarkung || '-'}
Flur / Flurstück: ${p.flur || '-'} / ${p.flstnrzae || '-'}${p.flstnrnen ? '/' + p.flstnrnen : ''}
Fläche (amtl.): ${p.flaeche ? Math.round(parseFloat(p.flaeche)).toLocaleString('de-DE') + ' m²' : '-'}
Lage: ${p.lagebeztxt || '-'}
`;
}
}
// Sekundär: Fallback auf ATKIS-DLM (Topographie) falls ALKIS fehlschlägt
const atkis = await queryWFS('atkis', 'adv:AX_Gemarkung', pos, 0.0001);
if (atkis && atkis.length > 0) {
const p = atkis[0].properties;
return `
Prüfdatum: ${today}
WGS84: ${pos.lat.toFixed(6)}, ${pos.lng.toFixed(6)}
Gemeinde: ${p.gemeindename || '-'}
Gemarkung: ${p.gemarkungsname || '-'}
Nur ATKIS-Topographiedaten verfügbar
`;
}
} catch (e) {
console.error("Parcel Fetch Error:", e);
}
return `
Prüfdatum: ${today}
WGS84: ${pos.lat.toFixed(6)}, ${pos.lng.toFixed(6)}
Amtliche Flurstücksdaten (ALKIS) zur Zeit nicht abrufbar
`;
};
// Parallel processing for basic checks
const [parcelHtml, planRes, potRes] = await Promise.all([
fetchParcel(),
fetch(`/api/local/check-planning?lat=${pos.lat}&lng=${pos.lng}`).then(r => r.json()),
fetch(`/api/local/check-potential?lat=${pos.lat}&lng=${pos.lng}`).then(r => r.json())
]);
const dist = userMarker.minDist || 2500;
let distStatus = "In der Regel ✔";
let distColor = "text-emerald-500";
if (dist < 400) {
distStatus = "Keine moderne Windenergieanlage möglich.";
distColor = "text-red-500 font-bold";
} else if (dist < 500) {
distStatus = "Abstand macht moderne Anlagen in der Regel nicht möglich. Eine detaillierte Prüfung ist notwendig.";
distColor = "text-red-500 font-bold";
} else if (dist <= 1000) {
distStatus = "HINWEIS: Wohnbebauung im Nahbereich (< 1000m)";
distColor = "text-red-500 font-bold";
} else {
distStatus = "In der Regel ✔ (> 1000m)";
distColor = "text-emerald-500";
}
let reportHtml = `
Analyse-Ergebnis
Wichtiger Hinweis:
Dies ist eine automatisierte Vorab-Prüfung. Die Ergebnisse können Fehler enthalten und ersetzen keine professionelle Flächenprüfung.
Empfehlung: Unabhängig vom Ergebnis empfehlen wir Rücksprache mit einem Mitarbeiter der ENWELO zu halten:
`;
// --- NEW: Combined Infrastructure Query to avoid 429 Too Many Requests ---
// Optimized Radiuses and timeout to avoid 504 Gateway Timeout
// Radiuses reduced significantly!
const combinedInfraQuery = `[out:json][timeout:15];(
way["power"~"line|cable"]["voltage"~"110000|220000|380000"](around:2500, ${pos.lat}, ${pos.lng});
node["aeroway"~"aerodrome|navaid"](around:15000, ${pos.lat}, ${pos.lng});
way["aeroway"="aerodrome"](around:15000, ${pos.lat}, ${pos.lng});
node["power"~"substation|transformer"](around:5000, ${pos.lat}, ${pos.lng});
way["power"~"substation|transformer"](around:5000, ${pos.lat}, ${pos.lng});
);out body geom;`;
console.log("[InfraCheck] Fetching combined infrastructure data...");
document.getElementById('loader-status').innerText = "Abfrage Infrastruktur (OpenStreetMap)...";
const infraData = await queryOverpass(combinedInfraQuery);
const infraElements = infraData?.elements || [];
document.getElementById('loader-status').innerText = "Abfrage Straßennetz (WFS NRW)...";
// WFS Road fetching (parallel)
// About 0.015 degrees is roughly 1-1.5km
const [bab, bstr, lstr, kstr] = await Promise.all([
queryWFSGML('nrw_roads', 'ms:Bundesautobahnen', pos, 0.015),
queryWFSGML('nrw_roads', 'ms:Bundesstrassen', pos, 0.015),
queryWFSGML('nrw_roads', 'ms:Landesstrassen', pos, 0.015),
queryWFSGML('nrw_roads', 'ms:Kreisstrassen', pos, 0.015),
]);
const wfsRoads = [...bab, ...bstr, ...lstr, ...kstr];
document.getElementById('loader-status').innerText = "Erstelle den Bericht...";
for (const cat of CATEGORIES) {
reportHtml += `
${cat.name}`;
if (cat.isSpecial === "usage") {
let usageStatus = "Keine Daten";
let usageColor = "text-slate-400";
let nutzart = "Unbekannt";
let bez = "Unbekannt";
try {
const r = await fetch(`/api/local/alkis-usage?lat=${pos.lat}&lng=${pos.lng}`);
if (r.ok) {
const data = await r.json();
if (data.found) {
nutzart = data.nutzart || "Unbekannt";
bez = data.bez || "Unbekannt";
const bezLower = bez.toLowerCase();
const nutzartLower = nutzart.toLowerCase();
// Heuristik für geeignete Flächen
const isAcker = bezLower.includes("acker") || nutzartLower.includes("acker");
const isGruenland = bezLower.includes("grünland") || bezLower.includes("wiese") || bezLower.includes("weide") || nutzartLower.includes("grünland");
const isLandwirtschaft = bezLower.includes("landwirtschaft") || nutzartLower.includes("landwirtschaft");
if (isAcker || isGruenland || isLandwirtschaft) {
usageStatus = "✔ (" + (bez !== "Unbekannt" ? bez : nutzart) + ")";
usageColor = "text-emerald-500";
} else {
usageStatus = "Prüfung notwendig";
usageColor = "text-amber-500 font-bold";
}
}
}
} catch (e) {
console.error("ALKIS usage error:", e);
}
reportHtml += `Status: ${usageStatus} `;
reportHtml += `Nutzart: ${nutzart} `;
reportHtml += `Bezeichnung: ${bez} `;
} else if (cat.isSpecial === "parcel") {
reportHtml += parcelHtml;
} else if (cat.isSpecial === "residential") {
reportHtml += `
Abstand (Ist): ${Math.round(dist)} Meter
Max. Anlagenhöhe (H/2): ${Math.floor(dist / 2)} Meter
Status Wohnbebauung:
${distStatus}
`;
} else if (cat.isSpecial === "planning") {
// Wind turbines for Repowering Check
let repoweringInfo = `Repowering-Potenzial: Keine Bestandsanlage < 600 Meter `;
let repoweringPossible = false;
try {
const turbinesRes = await fetch(`/api/local/wind-turbines?lat=${pos.lat}&lng=${pos.lng}`).then(r => r.json());
if (turbinesRes && turbinesRes.features && turbinesRes.features.length > 0) {
const ptNode = turf.point([pos.lng, pos.lat]);
const candidates = turbinesRes.features.filter(f => {
const d = turf.distance(ptNode, f, { units: 'meters' });
// Repowering: 15 Jahre alt bis 2031
const ibYear = parseInt(f.properties.ibjahr || 0);
const isOldEnough = (ibYear > 0 && (ibYear + 15 <= 2031));
return d <= 600 && isOldEnough;
});
if (candidates.length > 0) {
repoweringPossible = true;
candidates.sort((a, b) => turf.distance(ptNode, a) - turf.distance(ptNode, b));
const best = candidates[0].properties;
const dist = turf.distance(ptNode, candidates[0], { units: 'meters' });
repoweringInfo = `Repowering-Potenzial: Möglich (${candidates.length} Anlagen)
Nächs. Bestandsanlage: ${dist.toFixed(0)} Meter (Inbetriebnahme: ${best.ibjahr}) `;
}
}
} catch (e) { console.error("Repowering check fail", e); }
let municipalityNote = "";
if (!planRes.found && !repoweringPossible) {
municipalityNote = `Wichtiger Hinweis Planung: Positivplanung durch die Gemeinde notwendig `;
}
reportHtml += `
Regionalplan: ${planRes.found ? 'Passend' : 'Prüfung notwendig: Kein Windenergiebereich'}
${repoweringInfo}
${municipalityNote}
`;
} else if (cat.isSpecial === "infrastructure") {
// Use WFS pre-fetched data for roads
let nearestRoad = null;
let minDistRoad = 999999;
const point = turf.point([pos.lng, pos.lat]);
wfsRoads.forEach(lineFeature => {
const d = turf.pointToLineDistance(point, lineFeature, { units: 'meters' });
if (d < minDistRoad) {
minDistRoad = d;
nearestRoad = lineFeature;
}
});
let roadDisplay = "Keine klassifizierte Straße im Nahbereich";
if (nearestRoad) {
const typeMap = {
'ms:Bundesautobahnen': 'Autobahn',
'ms:Bundesstrassen': 'Bundesstraße',
'ms:Landesstrassen': 'Landesstraße',
'ms:Kreisstrassen': 'Kreisstraße'
};
const typeStr = typeMap[nearestRoad.properties.type] || "Straße";
const roadInfo = `${typeStr} in ${minDistRoad.toFixed(1)} Meter`;
if (minDistRoad <= 200) {
roadDisplay = `Prüfung notwendig: ${roadInfo}`;
} else {
roadDisplay = `✔ (${roadInfo})`;
}
} else {
roadDisplay = `✔ (> 1000 Meter)`;
}
reportHtml += `Straßennetz: ${roadDisplay} `;
// Power Line Distance (>= 110kV) - using pre-fetched data
const powerElements = infraElements.filter(el => (el.tags?.power === 'line' || el.tags?.power === 'cable') && el.tags?.voltage);
let nearestLine = null;
let minDistPower = 999999;
powerElements.forEach(el => {
if (el.geometry) {
const line = turf.lineString(el.geometry.map(g => [g.lon, g.lat]));
const d = turf.pointToLineDistance(point, line, { units: 'meters' });
if (d < minDistPower) {
minDistPower = d;
nearestLine = el;
}
}
});
let powerStatus = "✔ (> 2 Kilometer)";
let powerColor = "text-emerald-500";
if (nearestLine) {
powerStatus = `Abstand: ${minDistPower.toFixed(0)} Meter`;
powerColor = minDistPower < 100 ? "text-amber-500" : "text-emerald-500";
}
reportHtml += `Freileitungen (>= 110kV): ${powerStatus} `;
// Existing Wind Turbines (Infrastruktur Check - 600 Meter)
let turbineStatus = "✔ (> 600 Meter)";
let turbineColor = "text-emerald-500";
let closestInfo = "";
try {
const r = await fetch(`/api/local/wind-turbines?lat=${pos.lat}&lng=${pos.lng}`);
if (!r.ok) {
const errText = await r.text();
console.error("[WindCheck] API Error:", r.status, errText);
turbineStatus = `Fehler (${r.status})`;
} else {
const data = await r.json();
console.log(`[WindCheck] API returned ${data.features?.length || 0} WGS84 turbines for ${pos.lat},${pos.lng}`);
if (data && data.features && data.features.length > 0) {
const ptNode = turf.point([pos.lng, pos.lat]);
let minDist = 999999;
let nearest = null;
data.features.forEach(f => {
const d = turf.distance(ptNode, f, { units: 'meters' });
if (d < minDist) {
minDist = d;
nearest = f;
}
});
console.log(`[WindCheck] Nearest turbine at ${minDist.toFixed(1)}m`);
if (minDist <= 600) {
const jahr = nearest.properties.ibjahr || "Unbekannt";
turbineStatus = `Prüfung notwendig: Anlage in ${minDist.toFixed(0)} Meter`;
turbineColor = "text-amber-500";
closestInfo = `Inbetriebnahme: ${jahr} `;
} else if (minDist < 2000) {
turbineStatus = `✔ (Nächste in ${minDist.toFixed(0)} Meter)`;
}
}
}
} catch (e) {
console.error("Turbine check fail", e);
turbineStatus = "Check-Fehler: " + e.message;
}
reportHtml += `Windenergieanlagen (600 Meter): ${turbineStatus} `;
if (closestInfo) reportHtml += closestInfo;
} else if (cat.isSpecial === "aviation") {
let radarFound = false;
MILITARY_RADARS.forEach(r => {
const d = turf.distance(turf.point([pos.lng, pos.lat]), turf.point([r.lng, r.lat]), { units: 'kilometers' });
if (d < 15) {
reportHtml += `Militärradar ${r.name}: Prüfung notwendig (${d.toFixed(1)} Kilometer) `;
radarFound = true;
}
});
const aviationElements = infraElements.filter(el => el.tags?.aeroway);
let nearestVor = null;
let minDistVor = 15;
let nearestAir = null;
let minDistAir = 25;
const point = turf.point([pos.lng, pos.lat]);
aviationElements.forEach(el => {
let elLon, elLat;
if (el.type === 'node') { elLon = el.lon; elLat = el.lat; }
else if (el.center) { elLon = el.center.lon; elLat = el.center.lat; }
else if (el.geometry && el.geometry.length > 0) { elLon = el.geometry[0].lon; elLat = el.geometry[0].lat; }
if (elLon !== undefined && elLat !== undefined) {
const elPt = turf.point([elLon, elLat]);
const d = turf.distance(point, elPt, { units: 'kilometers' });
if (el.tags.aeroway === 'navaid' || el.tags.navaid) {
if (d < minDistVor) {
minDistVor = d;
nearestVor = el;
}
} else if (el.tags.aeroway === 'aerodrome' || el.tags.aeroway === 'airport') {
if (d < minDistAir) {
minDistAir = d;
nearestAir = el;
}
}
}
});
const vorStatus = nearestVor ? `Prüfung notwendig: ${nearestVor.tags.name || 'VOR'} in ${minDistVor.toFixed(1)} Kilometer` : `✔ (> 15 Kilometer)`;
const airStatus = nearestAir ? `Prüfung notwendig: ${nearestAir.tags.name || 'Flugplatz'} in ${minDistAir.toFixed(1)} Kilometer` : `✔ (> 25 Kilometer)`;
reportHtml += `Drehfunkfeuer (VOR): ${vorStatus} `;
reportHtml += `Flugplatz-Nahbereich: ${airStatus} `;
} else if (cat.isSpecial === "grid_connection") {
const gridElements = infraElements.filter(el => el.tags?.power === 'substation' || el.tags?.power === 'transformer');
let nearestSub = null;
let minDistGrid = 15; // km
const point = turf.point([pos.lng, pos.lat]);
gridElements.forEach(el => {
let elLon, elLat;
if (el.type === 'node') { elLon = el.lon; elLat = el.lat; }
else if (el.center) { elLon = el.center.lon; elLat = el.center.lat; }
else if (el.geometry && el.geometry.length > 0) { elLon = el.geometry[0].lon; elLat = el.geometry[0].lat; }
if (elLon !== undefined && elLat !== undefined) {
const elPt = turf.point([elLon, elLat]);
const d = turf.distance(point, elPt, { units: 'kilometers' });
if (d < minDistGrid) {
minDistGrid = d;
nearestSub = el;
}
}
});
let gridStatus = "Kein Anschluss (> 15 Kilometer)";
let gridColor = "text-amber-500";
if (nearestSub) {
const type = nearestSub.tags.power === 'substation' ? 'Umspannwerk' : 'Transformator';
const name = nearestSub.tags.name ? ` (${nearestSub.tags.name})` : "";
gridStatus = `${type}${name} in ${minDistGrid.toFixed(1)} Kilometer`;
gridColor = "text-emerald-500";
}
reportHtml += `Netzanschluss (15 Kilometer): ${gridStatus} `;
reportHtml += `
Hinweis Netzanschluss: Die Daten der Netzanschlüsse sind nicht vollständig. Eine verbindliche Netzanschlussanfrage ist zwingend erforderlich.
`;
} else if (cat.name.includes("Naturschutz")) {
// Gezielte Abfrage für alle relevanten Schutzgebiete
const nsg = await queryWMS('linfos', 'Naturschutzgebiete', pos);
const ffh = await queryWMS('linfos', 'FFH-Gebiete', pos);
const vsg = await queryWMS('linfos', 'Vogelschutzgebiete', pos);
const biotop = await queryWMS('linfos', 'geschuetzteBiotope', pos);
const lsg = await queryWMS('linfos', 'Landschaftsschutzgebiet', pos);
const gsn_res = await queryWMS('linfos', 'GSN', pos); // Gebiete für den Schutz der Natur (queryfähig)
const nature_found = nsg.length > 0 || ffh.length > 0 || vsg.length > 0 || biotop.length > 0 || lsg.length > 0 || gsn_res.length > 0;
reportHtml += `Naturschutzgebiet: ${nsg.length > 0 ? 'Tabu / Standort im Naturschutzgebiet' : '✔'} `;
reportHtml += `FFH-Gebiet: ${ffh.length > 0 ? 'Tabu / Standort im FFH-Gebiet' : '✔'} `;
reportHtml += `Vogelschutzgebiet: ${vsg.length > 0 ? 'Tabu / Standort im Vogelschutzgebiet' : '✔'} `;
reportHtml += `Gesetzl. gesch. Biotope: ${biotop.length > 0 ? 'Prüfung notwendig / Standort im Biotop' : '✔'} `;
reportHtml += `Landschaftsschutzgebiet: ${lsg.length > 0 ? 'Prüfung notwendig / Standort im Landschaftsschutzgebiet' : '✔'} `;
reportHtml += `Bereich zum Schutz der Natur: ${gsn_res.length > 0 ? 'Prüfung notwendig' : '✔'} `;
// Fallback für weitere Layer aus der Konfiguration
if (cat.items) {
const checked = ['Naturschutzgebiete', 'FFH-Gebiete', 'Vogelschutzgebiete', 'geschuetzteBiotope', 'Landschaftsschutzgebiet', 'GSN', 'BSN'];
for (const item of cat.items.filter(i => !checked.includes(i.layers))) {
const r = await queryWMS('linfos', item.layers, pos);
reportHtml += `${item.name}: ${r.length > 0 ? 'Prüfung notwendig' : '✔'} `;
}
}
reportHtml += `
Artenschutz: Diese automatisierte Abfrage ersetzt keine detaillierte Artenschutzrechtliche Prüfung (ffh-VP / ASP).
`;
} else if (cat.name.includes("Wasserschutz")) {
// Flood zones & Water protection
const uesg = await queryWMS('uesg', '6', pos); // Festgesetzte ÜSG (Layer 6)
const zones = await queryWMS('wsg', '4', pos); // Trinkwasser festgesetzt (Layer 4)
let wsgLabel = "✔";
if (zones.length > 0) {
const p = zones[0].properties || {};
const zone = p.schutzzone || p.zone || p.ZONENNAME || p.ZONE || p.name || "";
wsgLabel = `Prüfung notwendig: ${zone}`.trim();
if (wsgLabel === "Prüfung notwendig:") wsgLabel = "Prüfung notwendig / Standort im Wasserschutzgebiet";
}
reportHtml += `Wasserschutzgebiet: ${wsgLabel} `;
reportHtml += `Überschwemmungsgebiet: ${uesg.length > 0 ? 'Prüfung notwendig' : '✔'} `;
if (cat.items) {
// Check if any other items in this category are NOT covered by the above
for (const item of cat.items.filter(i => !i.layers.includes('wsg_zone1') && !i.name.includes('Überschwemmungsgebiete'))) {
const r = await queryWMS(item.url.split('/').pop(), item.layers, pos);
reportHtml += `${item.name}: ${r.length > 0 ? 'Prüfung notwendig' : '✔'} `;
}
}
} else {
const catResults = await Promise.all(cat.items.map(async item => {
const proxy = item.url.split('/').pop();
const r = await queryWFS(proxy, item.layers, pos, 0.001);
return `${item.name}: ${r.length > 0 ? 'Konflikt' : '✔'} `;
}));
reportHtml += catResults.join('');
}
reportHtml += ``;
}
// --- NEUE KATEGORIE: LANUV POTENZIAL (Ganz am Ende) ---
reportHtml += `
Informations-Zusatz: LANUV-Potenzial
Hintergrund: Potentialstudie des Landesamtes (LANUV). Diese Daten geben einen Hinweis über die generelle Eignung einer Fläche.
LANUV-Potenzial: ${potRes.found ? 'JA (Fläche vorhanden) ✔' : 'NEIN (Keine Fläche)'}
${potRes.found && potRes.properties ? `Details: ${JSON.stringify(potRes.properties)} ` : ''}
`;
reportHtml += `
* Alle Angaben ohne Gewähr. Erstellt am ${new Date().toLocaleString('de-DE')}.
`;
resultsArea.innerHTML = reportHtml;
// Initialize mini map after content is injected
initReportMiniMap(pos);
};
// --- Backend Health Check ---
async function checkBackendStatus() {
const statusEl = document.getElementById('backend-status');
const statusText = statusEl?.querySelector('.status-text');
if (!statusEl) return;
try {
const res = await fetch('/api/local/buildings?bbox=0,0,0,0'); // Simple ping
if (res.ok) {
statusEl.classList.remove('offline');
statusEl.classList.add('online');
if (statusText) statusText.textContent = "Geodaten-Server online";
} else {
throw new Error();
}
} catch (e) {
statusEl.classList.remove('online');
statusEl.classList.add('offline');
if (statusText) statusText.textContent = "Geodaten-Server offline";
}
}
// Initial check and regular interval
checkBackendStatus();
setInterval(checkBackendStatus, 10000);
// --- Impressum Modal Logic ---
const impressumModal = document.getElementById('impressum-modal');
const openImpressumBtn = document.getElementById('open-impressum');
const closeImpressumBtn = document.querySelector('.close-modal-btn');
if (openImpressumBtn && impressumModal) {
openImpressumBtn.addEventListener('click', () => {
impressumModal.classList.remove('hidden');
});
}
if (closeImpressumBtn && impressumModal) {
closeImpressumBtn.addEventListener('click', () => {
impressumModal.classList.add('hidden');
});
// Close on click outside
impressumModal.addEventListener('click', (e) => {
if (e.target === impressumModal) {
impressumModal.classList.add('hidden');
}
});
}
|