bwscheddebrock_trassenplaner/index_fixed.html

2769 lines
192 KiB
HTML
Raw Permalink Blame History

<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>TrassenPlaner Pro</title>
<!-- Libraries (CDN) -->
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css" />
<script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"></script>
<script src="https://unpkg.com/@turf/turf@6/turf.min.js"></script>
<script src="https://unpkg.com/shpjs@latest/dist/shp.js"></script>
<script src="https://unpkg.com/lucide@latest"></script>
<script src="https://unpkg.com/leaflet-editable@1.2.0/src/Leaflet.Editable.js"></script>
<script src="https://unpkg.com/jszip@3.10.1/dist/jszip.min.js"></script>
<script src="https://unpkg.com/proj4@2.9.0/dist/proj4.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jspdf/2.5.1/jspdf.umd.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/html2canvas/1.4.1/html2canvas.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jspdf-autotable/3.8.2/jspdf.plugin.autotable.min.js"></script>
<style>
:root {
--primary: #cca300;
/* Dark Yellow */
--primary-hover: #b38f00;
--bg-glass: rgba(255, 253, 235, 0.85);
/* Warm base */
--border-glass: rgba(204, 163, 0, 0.2);
--sidebar-width: 380px;
--panel-width: 320px;
--success: #299500;
/* CMYK 75 10 100 35 */
--danger: #ef4444;
--warning: #66E659;
/* CMYK 60 10 65 0 */
--info: #8CE6D9;
/* CMYK 45 10 15 0 */
--gray: #64748b;
--corporate-teal: #004b50;
/* CMYK 100 25 25 40 - Adjusted to Dark Blue */
--light-green: #CCFFCC;
/* CMYK 20 0 20 0 */
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Inter', sans-serif;
height: 100vh;
overflow: hidden;
display: flex;
background: #0f172a;
}
/* Layout Structure */
#sidebar {
width: var(--sidebar-width);
height: 100%;
background: var(--corporate-teal);
border-right: 1px solid rgba(255, 255, 255, 0.1);
display: flex;
flex-direction: column;
z-index: 1000;
transition: transform 0.3s ease;
color: white;
}
#map-container {
flex: 1;
position: relative;
}
#map {
width: 100%;
height: 100%;
}
#right-panel {
position: absolute;
top: 20px;
right: 20px;
width: var(--panel-width);
background: var(--bg-glass);
backdrop-filter: blur(16px);
border-radius: 16px;
border: 1px solid var(--border-glass);
box-shadow: 0 10px 25px rgba(0, 0, 0, 0.1);
z-index: 1000;
padding: 20px;
}
/* Sidebar CRM Elements */
.sidebar-header {
padding: 24px;
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
}
.sidebar-content {
flex: 1;
overflow-y: auto;
padding: 20px;
}
.search-box {
width: 100%;
padding: 12px 16px;
border-radius: 10px;
border: 1px solid rgba(255, 255, 255, 0.2);
background: rgba(0, 0, 0, 0.2);
color: white;
margin-bottom: 20px;
outline: none;
transition: all 0.2s;
}
.search-box::placeholder {
color: rgba(255, 255, 255, 0.5);
}
.search-box:focus {
box-shadow: 0 0 0 3px rgba(255, 255, 255, 0.1);
border-color: var(--info);
}
.status-select {
margin-top: 10px;
width: 100%;
padding: 8px;
border-radius: 8px;
font-size: 12px;
background: rgba(0, 0, 0, 0.2);
color: white;
border: 1px solid rgba(255, 255, 255, 0.1);
outline: none;
}
.owner-card {
background: white;
border-radius: 12px;
padding: 16px;
margin-bottom: 12px;
border: 1px solid rgba(204, 163, 0, 0.1);
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
cursor: pointer;
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.05);
color: #333;
/* Base text color for cards */
}
.owner-card .owner-name {
color: var(--corporate-teal);
font-weight: 700;
margin-bottom: 4px;
}
.owner-card:hover {
transform: translateY(-4px);
box-shadow: 0 12px 20px -8px rgba(204, 163, 0, 0.2);
border-color: var(--info);
}
/* Variant Stats */
.stat-card {
background: var(--bg-glass);
padding: 16px;
border-radius: 12px;
margin-top: 10px;
font-size: 14px;
border: 1px solid var(--border-glass);
}
.stat-row {
display: flex;
justify-content: space-between;
margin-bottom: 6px;
padding-bottom: 4px;
border-bottom: 1px solid rgba(0, 0, 0, 0.03);
}
.drilling-stat {
color: #000000;
font-weight: 700;
}
/* Actions */
.btn {
display: inline-flex;
align-items: center;
gap: 8px;
padding: 10px 16px;
border-radius: 8px;
font-weight: 600;
cursor: pointer;
border: none;
transition: all 0.2s;
font-size: 14px;
}
.btn-primary {
background: linear-gradient(135deg, var(--primary) 0%, var(--corporate-teal) 100%);
color: white;
border: none;
box-shadow: 0 4px 10px rgba(0, 115, 115, 0.3);
}
.btn-primary:hover {
transform: translateY(-1px);
box-shadow: 0 6px 14px rgba(0, 115, 115, 0.4);
filter: brightness(1.1);
}
.btn-outline {
background: white;
border: 1.5px solid var(--primary);
color: var(--primary);
}
.btn-outline:hover {
background: var(--light-green);
border-color: var(--success);
color: var(--success);
}
/* Options Menu Styles */
.options-menu {
position: absolute;
bottom: 75px;
left: 20px;
right: 20px;
background: #ffffff;
border-radius: 12px;
box-shadow: 0 8px 30px rgba(0, 0, 0, 0.4);
display: none;
flex-direction: column;
padding: 10px;
gap: 5px;
z-index: 1001;
border: 1px solid rgba(255, 255, 255, 0.2);
animation: slideUp 0.2s ease-out;
}
@keyframes slideUp {
from {
transform: translateY(10px);
opacity: 0;
}
to {
transform: translateY(0);
opacity: 1;
}
}
.options-menu.active {
display: flex;
}
.options-menu .btn {
justify-content: flex-start;
padding: 8px 12px;
font-size: 13px;
color: #333;
border: none;
background: transparent;
}
.options-menu .btn:hover {
background: rgba(0, 75, 80, 0.05);
color: var(--corporate-teal);
}
.options-menu hr {
border: none;
border-top: 1px solid rgba(0, 0, 0, 0.05);
margin: 5px 0;
}
/* Required Plots Panel */
#required-plots-container {
margin-top: 20px;
max-height: 300px;
overflow-y: auto;
border-top: 1px solid rgba(0, 0, 0, 0.05);
padding-top: 16px;
}
.plot-card {
background: white;
border-radius: 10px;
padding: 12px;
margin-bottom: 8px;
border: 1px solid rgba(204, 163, 0, 0.1);
font-size: 12px;
display: flex;
align-items: center;
gap: 12px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.03);
transition: all 0.2s;
}
.plot-card:hover {
border-color: var(--primary);
background: var(--light-green);
}
.status-dot {
width: 10px;
height: 10px;
border-radius: 50%;
flex-shrink: 0;
border: 1px solid rgba(0, 0, 0, 0.1);
}
.plot-info {
flex: 1;
}
.plot-owner {
font-weight: 600;
color: #334155;
margin-bottom: 2px;
}
.plot-details {
color: #64748b;
}
.btn-outline:hover {
background: #f8fafc;
border-color: var(--primary);
color: var(--primary);
}
/* Dropzone Overlay */
#dropzone {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(37, 99, 235, 0.1);
border: 4px dashed var(--primary);
z-index: 5000;
display: none;
justify-content: center;
align-items: center;
font-size: 24px;
color: var(--primary);
font-weight: 700;
pointer-events: none;
}
.status-badge {
width: 12px;
height: 12px;
border-radius: 50%;
display: inline-block;
margin-right: 8px;
}
/* Marker & Icon Custom Styles */
.anlage-icon {
background: white;
border: 2px solid var(--corporate-teal);
border-radius: 50%;
width: 32px !important;
height: 32px !important;
display: flex;
align-items: center;
justify-content: center;
box-shadow: 0 3px 10px rgba(0, 0, 0, 0.2);
color: var(--corporate-teal);
}
.anlage-icon svg {
width: 20px;
height: 20px;
}
.nvp-icon {
background: #000;
border: 2px solid white;
border-radius: 4px;
width: 24px;
height: 24px;
}
/* Editing Marker Styles */
.leaflet-vertex-icon {
background-color: #fff !important;
border: 2px solid var(--corporate-teal) !important;
border-radius: 50% !important;
width: 12px !important;
height: 12px !important;
margin-left: -6px !important;
margin-top: -6px !important;
box-shadow: 0 0 0 2px rgba(255, 255, 255, 0.8), 0 2px 8px rgba(0, 0, 0, 0.3) !important;
cursor: move !important;
}
.leaflet-middle-marker {
background-color: rgba(255, 255, 255, 0.9) !important;
border: 2px dashed var(--primary) !important;
border-radius: 50% !important;
width: 12px !important;
height: 12px !important;
margin-left: -6px !important;
margin-top: -6px !important;
opacity: 0.8 !important;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2) !important;
cursor: cell !important;
}
.segment-label {
display: none;
/* Hidden by default */
background: rgba(255, 255, 255, 0.9);
border: 1px solid #333;
border-radius: 4px;
padding: 1px 4px;
font-size: 11px;
font-weight: 700;
white-space: nowrap;
pointer-events: none;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.3);
align-items: center;
justify-content: center;
transform: translateY(-5px);
text-shadow: 0 0 2px white;
}
.map-zoom-mid .segment-label,
.map-zoom-mid .drilling-segment-label {
display: flex !important;
}
.drilling-segment-label {
display: none !important;
}
/* Tooltips for owners */
.owner-label {
display: none;
background: rgba(255, 255, 255, 0.85);
border: 1px solid rgba(0, 0, 0, 0.1);
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
font-size: 10px;
color: #333;
text-align: center;
line-height: 1.2;
padding: 2px 4px;
border-radius: 4px;
white-space: nowrap;
}
/* Only show tooltips when map container has map-zoom-high class */
.map-zoom-high .owner-label {
display: block;
}
/* Panel Collapse CSS */
#sidebar {
transition: margin-left 0.3s ease;
}
#sidebar.collapsed {
margin-left: calc(-1 * var(--sidebar-width));
}
#right-panel {
transition: transform 0.3s ease;
}
#right-panel.collapsed {
transform: translateX(calc(100% + 40px));
}
.panel-toggle-btn {
position: absolute;
background: white;
border: 1px solid #ccc;
box-shadow: 0 2px 5px rgba(0,0,0,0.2);
cursor: pointer;
z-index: 2000;
padding: 8px 4px;
display: flex;
align-items: center;
justify-content: center;
color: var(--corporate-teal);
transition: left 0.3s ease, right 0.3s ease;
}
.sidebar-collapse-btn {
left: var(--sidebar-width);
top: 50%;
transform: translateY(-50%);
border-radius: 0 8px 8px 0;
border-left: none;
}
#sidebar.collapsed ~ .sidebar-collapse-btn {
left: 0;
}
.right-panel-collapse-btn {
left: -20px;
top: 20px;
border-radius: 8px 0 0 8px;
border-right: none;
}
</style>
</head>
<body>
<div id="dropzone">Shapefiles hier ablegen...</div>
<!-- Note Modal -->
<div id="note-modal" style="display: none; position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); background: white; padding: 20px; border-radius: 12px; box-shadow: 0 10px 30px rgba(0,0,0,0.5); z-index: 9999; flex-direction: column; width: 320px;">
<div style="display: flex; justify-content: space-between; align-items: center; border-bottom: 1px solid #e2e8f0; padding-bottom: 10px; margin-bottom: 10px;">
<h4 style="margin: 0; color: var(--corporate-teal); display: flex; align-items: center; gap: 8px;"><i data-lucide="file-edit" style="width: 18px;"></i> Notiz bearbeiten</h4>
<button id="note-modal-close" style="background: none; border: none; cursor: pointer; color: #64748b; padding: 4px; border-radius: 4px;"><i data-lucide="x" style="width: 18px;"></i></button>
</div>
<textarea id="note-modal-text" placeholder="Notiz hier eingeben..." style="width: 100%; height: 120px; padding: 10px; border: 1px solid #cbd5e1; border-radius: 8px; resize: vertical; font-family: inherit; font-size: 13px;"></textarea>
<button id="note-modal-save" class="btn btn-primary" style="align-self: flex-end; margin-top: 15px;">Speichern</button>
</div>
<aside id="sidebar">
<div class="sidebar-header">
<img src="Logos/20201202-ENWELO-Logo-4c_ohne_Claim.png" alt="ENWELO"
style="width: 100%; max-width: 220px; margin-bottom: 20px;">
<div style="display: flex; flex-direction: column;">
<h1
style="font-size: 20px; display: flex; align-items: center; gap: 10px; color: white; margin: 0; font-weight: 800; letter-spacing: -0.5px;">
<i data-lucide="map-pin"></i> TrassenPlaner Basic
</h1>
<span
style="font-size: 11px; color: var(--info); opacity: 0.9; margin-left: 34px; margin-top: -2px; font-weight: 600;">v1.0.4</span>
</div>
</div>
<div class="sidebar-content">
<div id="owner-search-container" style="margin-top: 0px; margin-bottom: 15px;">
<input type="text" class="search-box" id="owner-search" placeholder="Eigent<6E>mer suchen..."
style="margin-bottom: 0px; background: rgba(255, 255, 255, 0.1); border-color: rgba(255, 255, 255, 0.2); color: white;">
</div>
<div id="owner-list">
<div style="text-align: center; color: #888; margin-top: 40px;">
<i data-lucide="upload-cloud" style="width: 48px; height: 48px; opacity: 0.5;"></i>
<p>Shapefiles laden...</p>
</div>
</div>
</div>
<div
style="padding: 20px; border-top: 1px solid rgba(255, 255, 255, 0.1); display: flex; flex-direction: column; gap: 10px; position: relative;">
<!-- New Options Menu -->
<div id="options-popup" class="options-menu">
<div id="layer-status" style="padding: 8px; font-size: 11px; margin-bottom: 5px; color: #333;">
<div style="font-weight: bold; margin-bottom: 8px; color: var(--corporate-teal);"><i data-lucide="layers" style="width: 14px; display: inline-block; vertical-align: middle;"></i> Kartenebenen</div>
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 4px;">
<label style="display: flex; align-items: center; gap: 6px; cursor: pointer;"><input type="checkbox" id="toggle-owners" checked onchange="toggleLayer('owners', this.checked)"> Flurst<73>ck-Grenzen</label>
<span id="status-owners" style="color: var(--danger); font-weight: 600;">Fehlt</span>
</div>
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 4px; padding-left: 18px;">
<label style="display: flex; align-items: center; gap: 6px; cursor: pointer;"><input type="checkbox" id="toggle-owner-status" checked onchange="toggleLayer('ownerStatus', this.checked)"> Farbe: Status</label>
</div>
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 8px; padding-left: 18px;">
<label style="display: flex; align-items: center; gap: 6px; cursor: pointer;"><input type="checkbox" id="toggle-owner-color" onchange="toggleLayer('ownerColor', this.checked)"> Farbe: Eigent<6E>mer</label>
</div>
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 4px;">
<label style="display: flex; align-items: center; gap: 6px; cursor: pointer;"><input type="checkbox" id="toggle-usage" checked onchange="toggleLayer('usage', this.checked)"> Nutzung-Daten</label>
<span id="status-usage" style="color: var(--danger); font-weight: 600;">Fehlt</span>
</div>
<div style="display: flex; justify-content: space-between; align-items: center;">
<label style="display: flex; align-items: center; gap: 6px; cursor: pointer;"><input type="checkbox" id="toggle-wea" checked onchange="toggleLayer('wea', this.checked)"> WEA-Standorte</label>
<span id="status-wea" style="color: var(--danger); font-weight: 600;">Fehlt</span>
</div>
</div>
<hr>
<button class="btn" id="btn-sync-folder">
<i data-lucide="link" style="width: 16px;"></i> Ordner synchronisieren
</button>
<hr>
<button class="btn" onclick="document.getElementById('file-input').click()">
<i data-lucide="file-plus" style="width: 16px;"></i> Dateien w<>hlen
</button>
<hr>
<button class="btn" id="btn-export">
<i data-lucide="download" style="width: 16px;"></i> GDB Export (.zip)
</button>
<button class="btn" id="btn-import">
<i data-lucide="upload" style="width: 16px;"></i> Projekt-Import (.json)
</button>
<hr>
<button class="btn" id="btn-pdf-export-alt">
<i data-lucide="file-text" style="width: 16px;"></i> Als PDF exportieren
</button>
</div>
<button class="btn btn-outline" id="btn-toggle-options"
style="width: 100%; border-color: rgba(255,255,255,0.3); color: white; background: rgba(255,255,255,0.05);">
<i data-lucide="settings"></i> Optionen
</button>
<input type="file" id="file-input" multiple style="display: none;">
</div>
</aside>
<button id="sidebar-toggle-btn" class="panel-toggle-btn sidebar-collapse-btn" onclick="document.getElementById('sidebar').classList.toggle('collapsed'); this.querySelector('i').setAttribute('data-lucide', document.getElementById('sidebar').classList.contains('collapsed') ? 'chevron-right' : 'chevron-left'); lucide.createIcons({root: this});">
<i data-lucide="chevron-left"></i>
</button>
<main id="map-container">
<div id="map"></div>
<div id="right-panel">
<button class="panel-toggle-btn right-panel-collapse-btn" onclick="document.getElementById('right-panel').classList.toggle('collapsed'); this.querySelector('i').setAttribute('data-lucide', document.getElementById('right-panel').classList.contains('collapsed') ? 'chevron-left' : 'chevron-right'); lucide.createIcons({root: this});">
<i data-lucide="chevron-right"></i>
</button>
<h3 style="font-size: 16px; margin-bottom: 16px; display: flex; align-items: center; gap: 8px;">
<i data-lucide="activity"></i> Varianten
</h3>
<div id="variant-controls">
<!-- Variants will be injected here -->
</div>
<div id="required-plots-container" style="margin-top: 20px;">
<!-- Required plots will be injected here -->
</div>
<div style="margin-top: 20px; display: grid; grid-template-columns: 1fr 1fr; gap: 10px;">
<button class="btn btn-primary" id="btn-draw" style="width: 100%;">
<i data-lucide="plus"></i> Zeichnen
</button>
<button class="btn btn-outline" id="btn-measure" style="width: 100%;">
<i data-lucide="ruler"></i> Messen
</button>
</div>
</div>
</main>
<script>
// --- Core Application State ---
try {
proj4.defs("EPSG:25832", "+proj=utm +zone=32 +ellps=GRS80 +units=m +no_defs");
} catch (e) {
console.error("Proj4 definition failed:", e);
}
const state = {
owners: [],
usage: [],
wea: [],
highlightedOwners: {},
variants: [
{ id: 1, name: "Variante A", color: "#cca300", routes: [], active: true, visible: true, stats: { total: 0, drilling: 0, open: 0, muffen: 0 } },
{ id: 2, name: "Variante B", color: "#cca300", routes: [], active: false, visible: true, stats: { total: 0, drilling: 0, open: 0, muffen: 0 } },
{ id: 3, name: "Variante C", color: "#cca300", routes: [], active: false, visible: true, stats: { total: 0, drilling: 0, open: 0, muffen: 0 } },
{ id: 4, name: "Variante D", color: "#cca300", routes: [], active: false, visible: true, stats: { total: 0, drilling: 0, open: 0, muffen: 0 } },
{ id: 5, name: "Variante E", color: "#cca300", routes: [], active: false, visible: true, stats: { total: 0, drilling: 0, open: 0, muffen: 0 } },
{ id: 6, name: "Variante F", color: "#cca300", routes: [], active: false, visible: true, stats: { total: 0, drilling: 0, open: 0, muffen: 0 } },
{ id: 7, name: "Variante G", color: "#cca300", routes: [], active: false, visible: true, stats: { total: 0, drilling: 0, open: 0, muffen: 0 } },
{ id: 8, name: "Variante H", color: "#cca300", routes: [], active: false, visible: true, stats: { total: 0, drilling: 0, open: 0, muffen: 0 } }
],
visibleLayers: {
owners: true,
ownerStatus: true,
ownerColor: false,
usage: true,
wea: true
},
directoryHandle: null,
measurementLines: [],
isDrawing: false,
isMeasuring: false
};
// --- Modal & Highlight Helpers ---
window.currentNoteOwnerKey = null;
window.openNoteModal = function(ownerKey, currentNote) {
window.currentNoteOwnerKey = ownerKey;
document.getElementById('note-modal-text').value = currentNote || '';
document.getElementById('note-modal').style.display = 'flex';
};
window.toggleHighlightOwner = function(ownerKey) {
if (!state.highlightedOwners) state.highlightedOwners = {};
state.highlightedOwners[ownerKey] = !state.highlightedOwners[ownerKey];
updateOwnerLayer();
renderOwnerList(document.getElementById('owner-search').value);
};
// --- Table Download Generator ---
window.downloadVariantTable = function(id) {
const v = state.variants.find(varnt => varnt.id === id);
if (!v || !v.routes || v.routes.length < 2 || !state.owners || !state.owners.features) {
alert("Nicht genug Daten vorhanden (Trasse / Flurst<73>cke fehlen).");
return;
}
const { jsPDF } = window.jspdf;
const doc = new jsPDF({ orientation: 'portrait', unit: 'mm', format: 'a4' });
const ENWELO_LOGO_BASE64 = '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';
if (ENWELO_LOGO_BASE64 && ENWELO_LOGO_BASE64 !== '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
') {
try {
// Constant width of 45mm, height proportionally adjusted for the enwelo logo (approx 1695x662)
doc.addImage("data:image/png;base64," + ENWELO_LOGO_BASE64, 'PNG', 14, 10, 45, 17.5);
} catch(e) { }
}
doc.setFont("helvetica", "bold");
doc.setFontSize(14);
doc.setTextColor(0, 75, 80);
doc.text(`Betroffene Flurst<73>cke - ${v.name}`, 14, 30);
doc.setFont("helvetica", "normal");
doc.setFontSize(9);
const footerText = "Dieses Dokument wurde vom Auftragnehmer erstellt und aus dem Planungstool automatisch generiert.";
const latLngs = getFlattenedCoords(v.routes);
const lineStr = turf.lineString(latLngs.map(ll => [ll.lng, ll.lat]));
const lineBbox = turf.bbox(lineStr);
const tableData = [];
state.owners.features.forEach(f => {
try {
const obsBbox = turf.bbox(f);
if (lineBbox[0] > obsBbox[2] || lineBbox[2] < obsBbox[0] ||
lineBbox[1] > obsBbox[3] || lineBbox[3] < obsBbox[1]) return;
if (turf.booleanIntersects(lineStr, f)) {
const p = f.properties;
let intersectLength = 0;
try {
const split = turf.lineSplit(lineStr, f);
if (split.features.length > 0) {
split.features.forEach(seg => {
const mid = turf.along(seg, turf.length(seg)/2, {units: 'kilometers'});
if (turf.booleanPointInPolygon(mid, f)) {
intersectLength += turf.length(seg, {units: 'meters'});
}
});
} else {
const mid = turf.along(lineStr, turf.length(lineStr)/2, {units: 'kilometers'});
if (turf.booleanPointInPolygon(mid, f)) {
intersectLength = turf.length(lineStr, {units: 'meters'});
}
}
} catch(e) { }
tableData.push([
`${p.Vorname || ''} ${p.Nachname || ''}`.trim() || 'Unbekannt',
p.status || 'Unbekannt',
p.Flurstueck || p.Flurst<EFBFBD>ck || p.flurstueck || p.FLST_NR || p.FLST || p.flst || p.NUMMER || p.FlSt || '-',
p.Flur || p.FLUR || p.flur || p.FL || p.fl || '-',
p.Gemarkung || p.GEMARKUNG || '-',
intersectLength > 0 ? (intersectLength.toFixed(1) + ' m') : 'Betroffen'
]);
}
} catch(e) {}
});
const uniqueMap = new Map();
tableData.forEach(row => {
const key = row[0]+row[2]+row[3]+row[4];
if (!uniqueMap.has(key)) uniqueMap.set(key, row);
});
const finalData = Array.from(uniqueMap.values());
doc.autoTable({
startY: 38,
head: [['Name', 'Sicherungsstand', 'Flurst<73>ck', 'Flur', 'Gemarkung', 'Trassenl<6E>nge (ca.)']],
body: finalData,
styles: { fontSize: 9 },
headStyles: { fillColor: [0, 75, 80] },
margin: { bottom: 20 },
didDrawPage: function(data) {
doc.setFontSize(8);
doc.setTextColor(150);
doc.text(footerText, 14, doc.internal.pageSize.height - 10);
}
});
doc.save(`Flurstuecke_${v.name.replace(/\s+/g, '_')}.pdf`);
};
window.addEventListener('DOMContentLoaded', () => {
const btnClose = document.getElementById('note-modal-close');
const btnSave = document.getElementById('note-modal-save');
if (btnClose) {
btnClose.addEventListener('click', () => {
document.getElementById('note-modal').style.display = 'none';
});
}
if (btnSave) {
btnSave.addEventListener('click', () => {
if (!window.currentNoteOwnerKey) return;
const newNote = document.getElementById('note-modal-text').value;
const parts = window.currentNoteOwnerKey.split('|');
const pNachname = parts[0];
const pVorname = parts[1];
const pStr = parts[2];
const pOrt = parts[3];
// Update all matching features in state
if (state.owners && state.owners.features) {
state.owners.features.forEach(f => {
const p = f.properties;
if (p.Nachname == pNachname && (p.Vorname || '') == (pVorname || '') &&
(p.Str_HNr || p.STR || '') == pStr && (p.Ort || p.ORT || '') == pOrt) {
p.notiz = newNote;
}
});
}
document.getElementById('note-modal').style.display = 'none';
renderOwnerList(document.getElementById('owner-search').value);
updateOwnerLayer();
const activeV = state.variants.find(v => v.active);
if (activeV) updateRequiredPlots(activeV);
if (state.directoryHandle) saveToFolder();
});
}
});
// --- Helpers ---
function getStatusColor(status) {
const s = (status || 'Unbekannt').trim();
const colorMap = {
'Zustimmung liegt vor': '#66E659', // Medium Green
'Vertraglich gesichert': '#299500', // Dark Green
'Ablehnung': '#ef4444',
'Unentschlossen': '#f59e0b',
'M<>gliche positive R<>ckmeldung': '#CCFFCC', // Light Green
'In Gesellschaft': '#8b5cf6',
'Unbekannt': '#94a3b8'
};
return colorMap[s] || '#94a3b8';
}
function getUsageColor(type) {
const t = (type || '').toLowerCase();
if (t.includes('geh<65>lz') || t.includes('wald')) return '#22c55e'; // Green
if (t.includes('gew<65>sser') || t.includes('wasser')) return '#3b82f6'; // Blue
if (t.includes('bahn') || t.includes('stra<72>e') || t.includes('baufl<66>che') || t.includes('weg') || t.includes('verkehr')) return '#ef4444'; // Red
return '#f59e0b'; // Default orange/warning
}
function usageStyle(f) {
const color = getUsageColor(f.properties.nutzart || f.properties.NUTZART || '');
return {
color: color,
weight: 2,
fillOpacity: 0.4,
fillColor: color
};
}
const ownerColors = new Map();
function getOwnerRandomColor(ownerKey) {
if (!ownerKey) return '#cbd5e1';
if (ownerColors.has(ownerKey)) return ownerColors.get(ownerKey);
// Generate a random but stable color (hsl)
let hash = 0;
for (let i = 0; i < ownerKey.length; i++) {
hash = ownerKey.charCodeAt(i) + ((hash << 5) - hash);
}
const h = Math.abs(hash % 360);
const s = 60 + Math.abs(hash % 30); // 60-90% saturation
const l = 40 + Math.abs(hash % 20); // 40-60% lightness
const color = `hsl(${h}, ${s}%, ${l}%)`;
ownerColors.set(ownerKey, color);
return color;
}
// --- Map Initialization ---
const map = L.map('map', {
zoomControl: false,
editable: true,
preferCanvas: true, // Crucial for html2canvas to capture vector layers correctly
maxZoom: 22
}).setView([52.2291, 7.5501], 14);
// Manage visibility of detailed owner labels based on zoom
function updateMapZoomClass() {
const zoom = map.getZoom();
const container = map.getContainer();
// Level 1: Cable Dimensions (Bema<6D>ung)
if (zoom >= 17) {
container.classList.add('map-zoom-mid');
} else {
container.classList.remove('map-zoom-mid');
}
// Level 2: Owner Labels (Flurst<73>cksbeschriftung)
if (zoom >= 17) {
container.classList.add('map-zoom-high');
} else {
container.classList.remove('map-zoom-high');
}
}
map.on('zoomend', updateMapZoomClass);
updateMapZoomClass(); // initialize
// Robust initialization for Leaflet.Editable
function ensureEditable() {
if (!map.editTools) {
if (window.L && L.Editable) {
map.editTools = new L.Editable(map, {
lineOptions: { color: '#cca300', weight: 6 },
vertexOptions: { color: '#cca300', radius: 5 },
middleMarkerOptions: { color: '#cca300', opacity: 0.5, radius: 3 },
drawingLineOptions: { color: '#cca300', weight: 6 },
drawingVertexOptions: { color: '#cca300', radius: 5 }
});
console.log("Leaflet.Editable manually initialized");
} else {
console.error("Leaflet.Editable library not found!");
}
}
// Create panes for routes to ensure proper layering
if (!map.getPane('trassenPane')) {
const trassenPane = map.createPane('trassenPane');
trassenPane.style.zIndex = 550; // Base route
}
if (!map.getPane('drillingPane')) {
const drillingPane = map.createPane('drillingPane');
drillingPane.style.zIndex = 560; // Drilling (over route)
}
if (!map.getPane('labelPane')) {
const labelPane = map.createPane('labelPane');
labelPane.style.zIndex = 570; // Labels and Markers (top)
}
}
ensureEditable();
const osmLayer = L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
attribution: '<27> OpenStreetMap',
maxZoom: 22,
maxNativeZoom: 19
});
const satelliteLayer = L.tileLayer('https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}', {
attribution: 'Tiles &copy; Esri &mdash; Source: Esri, i-cubed, USDA, USGS, AEX, GeoEye, Getmapping, Aerogrid, IGN, IGP, UPR-EGP, and the GIS User Community',
maxZoom: 22,
maxNativeZoom: 18
});
// Add default layer
satelliteLayer.addTo(map);
// Layer Control
const baseMaps = {
"Luftbild (Satellit)": satelliteLayer,
"OpenStreetMap": osmLayer
};
L.control.layers(baseMaps, null, { position: 'topleft' }).addTo(map);
L.control.zoom({ position: 'bottomright' }).addTo(map);
// Global editing events for real-time synchronization
map.on('editable:vertex:drag editable:drawing:move editable:vertex:dragend editable:vertex:deleted editable:vertex:new editable:drawing:end editable:editing editable:drag', (e) => {
try {
// Determine which layer is being edited (support for direct layer drag and vertex drag)
const layer = e.layer || (e.vertex ? (e.vertex.editor ? e.vertex.editor.feature : e.vertex.polyline) : null);
if (!layer || !layer.getLatLngs) return;
const variant = state.variants.find(v => routeLayers[v.id] === layer);
if (variant) {
const liveCoords = getFlattenedCoords(layer.getLatLngs());
const tempPt = (e.type === 'editable:drawing:move') ? e.latlng : null;
const isContinuous = ['editable:vertex:drag', 'editable:drawing:move', 'editable:drag', 'editable:editing'].includes(e.type);
// Always make sure variant.routes is up to date during dragging to allow calculateStats and labels to function properly
if (isContinuous) {
renderSegmentLabels(variant, liveCoords, tempPt);
} else {
variant.routes = liveCoords;
calculateStats(variant); // this internally calls renderSegmentLabels(variant) with updated routes
updateVariantStatsUI(variant);
renderVariants();
if (state.directoryHandle) saveToFolder();
}
}
} catch (err) {
console.error("Sync error:", err);
}
});
// --- Layer Groups ---
const layers = {
owners: L.geoJSON(null, {
style: (f) => {
const p = f.properties;
const mergedOwners = p._mergedOwners || [p];
const isHighlighted = mergedOwners.some(mp => {
const key = `${mp.Nachname}|${mp.Vorname}|${mp.Str_HNr || mp.STR}|${mp.Ort || mp.ORT}`;
return state.highlightedOwners && state.highlightedOwners[key];
});
if (isHighlighted) {
return { color: '#ffee00', weight: 6, opacity: 1, fillColor: '#ffee00', fillOpacity: 0.3 };
}
return {
color: '#000000',
weight: 2.5,
opacity: 1,
fillColor: '#ffffff',
fillOpacity: 0.01
};
},
onEachFeature: (f, layer) => {
const p = f.properties;
// Handle pre-merged multiple owners (from updateOwnerLayer)
const mergedOwners = p._mergedOwners || [p];
layer.bindPopup(() => {
return mergedOwners.map(mp => {
return `<b>${mp.Vorname || ''} ${mp.Nachname || ''}</b><br>${mp.Str_HNr || mp.STR || ''}<br>${mp.PLZ || ''} ${mp.Ort || mp.ORT || ''}<br>Status: <b>${mp.status || 'Unbekannt'}</b>`;
}).join('<hr style="margin: 8px 0; border: none; border-top: 1px solid #ccc;">');
});
// Add text showing who it belongs to
// Join multiple names with " und "
const names = mergedOwners.map(mp => `${mp.Vorname || ''} ${mp.Nachname || ''}`.trim()).filter(n => n);
// If more than two, maybe just show "Name u. a." or list them all. We will list up to 2, then "u. a.".
let nameDisplay = '';
if (names.length === 1) nameDisplay = names[0];
else if (names.length === 2) nameDisplay = names.join(' und ');
else if (names.length > 2) nameDisplay = names[0] + ' u. a.';
// Label collection
let labelParts = [];
if (nameDisplay) labelParts.push(`<b>${nameDisplay}</b>`);
// Abbreviated Flur and Flurstueck
const flur = p.Flur || p.FLUR || p.flur || p.FL || p.fl || '';
const flst = p.Flurstueck || p.Flurst<EFBFBD>ck || p.flurstueck || p.FLST_NR || p.FLST || p.flst || p.NUMMER || p.FlSt || '';
let flurFlstText = '';
if (flur) flurFlstText += `Fl. ${flur}`;
if (flst) {
if (flurFlstText) flurFlstText += `, FlSt ${flst}`;
else flurFlstText += `FlSt ${flst}`;
}
if (flurFlstText) labelParts.push(flurFlstText);
if (labelParts.length > 0) {
layer.bindTooltip(labelParts.join('<br>'), {
permanent: true,
direction: 'center',
className: 'owner-label',
interactive: false
});
}
}
}).addTo(map),
ownerColor: L.geoJSON(null, {
style: (f) => {
const properties = f.properties._mergedOwners && f.properties._mergedOwners.length > 0 ? f.properties._mergedOwners[0] : f.properties;
const name = (properties.Nachname || '').trim();
const street = (properties.Str_HNr || properties.STR || '').trim();
const key = `${name}-${street}`;
return {
stroke: false,
fillColor: getOwnerRandomColor(key),
fillOpacity: 0.7
};
},
interactive: false
}).addTo(map),
ownerStatus: L.geoJSON(null, {
style: (f) => {
// Extract status, assuming the first one holds the color or they are all same
// It uses _mergedOwners from preprocessing if available, else standard feature properties
const properties = f.properties._mergedOwners && f.properties._mergedOwners.length > 0 ? f.properties._mergedOwners[0] : f.properties;
const status = (properties.status || 'Unbekannt').trim();
const color = getStatusColor(status);
const isUnknown = status === 'Unbekannt';
return {
stroke: false,
fillColor: color,
fillOpacity: isUnknown ? 0.0 : 0.6
};
},
interactive: false // Don't steal popup clicks from the outline layer
}).addTo(map),
usage: L.geoJSON(null, {
style: usageStyle,
onEachFeature: (f, layer) => {
if (f.properties && (f.properties.nutzart || f.properties.NUTZART)) {
layer.bindPopup(`Nutzung: <b>${f.properties.nutzart || f.properties.NUTZART}</b>`);
}
}
}).addTo(map),
wea: L.geoJSON(null, {
pointToLayer: (feature, latlng) => {
return L.marker(latlng, {
icon: L.divIcon({
className: 'anlage-icon',
html: '<i data-lucide="fan"></i>',
iconSize: [32, 32],
iconAnchor: [16, 16],
popupAnchor: [0, -16]
})
});
},
onEachFeature: (f, layer) => {
const name = f.properties.Name || f.properties.NAME || f.properties.bezeichnun || 'WEA';
layer.bindPopup(`<b>${name}</b><br>Windenergieanlage`);
// Immediately initialize lucide icon for this marker
layer.on('add', function () {
const el = layer.getElement();
if (el && window.lucide) {
lucide.createIcons({ root: el });
}
});
}
}).addTo(map)
};
// --- File Selection Implementation ---
document.getElementById('file-input').addEventListener('change', async (e) => {
const files = Array.from(e.target.files);
await processFileList(files);
});
// --- Drag & Drop Implementation ---
const dropzone = document.getElementById('dropzone');
window.addEventListener('dragover', (e) => { e.preventDefault(); dropzone.style.display = 'flex'; });
window.addEventListener('dragleave', (e) => { if (e.relatedTarget === null) dropzone.style.display = 'none'; });
window.addEventListener('drop', async (e) => {
e.preventDefault();
dropzone.style.display = 'none';
const files = Array.from(e.dataTransfer.files);
await processFileList(files);
});
async function processFileList(files) {
console.log("Processing files:", files.map(f => f.name));
const fileGroups = {};
for (let file of files) {
const parts = file.name.split('.');
const ext = parts.pop().toLowerCase();
const base = parts.join('.').toLowerCase();
if (!fileGroups[base]) fileGroups[base] = {};
fileGroups[base][ext] = file;
}
for (let base in fileGroups) {
if (fileGroups[base].shp && fileGroups[base].dbf) {
await processShapefileGroup(base, fileGroups[base]);
}
}
}
async function processShapefileGroup(name, group) {
console.log("Loading Shapefile Group:", name);
// Handle both File objects (drag-drop) and ArrayBuffers (folder sync)
const shpBuffer = group.shp instanceof ArrayBuffer ? group.shp : await group.shp.arrayBuffer();
const dbfBuffer = group.dbf instanceof ArrayBuffer ? group.dbf : await group.dbf.arrayBuffer();
try {
let geojson = await shp.combine([
shp.parseShp(shpBuffer),
shp.parseDbf(dbfBuffer)
]);
// Check for UTM32 and transform if necessary
// Easting in Germany is usually > 200,000
const firstFeat = geojson.features[0];
if (firstFeat && firstFeat.geometry) {
let coord = firstFeat.geometry.coordinates;
while (Array.isArray(coord[0])) coord = coord[0];
const testVal = coord[0]; // Assuming x is first
if (testVal > 180 || testVal < -180) {
console.log("Large coordinates detected (" + testVal + "), transforming from UTM32 to WGS84...");
geojson = transformGeoJSON(geojson, "EPSG:25832", "EPSG:4326");
}
}
const lowerName = name.toLowerCase();
if (lowerName.includes('eigentuemer') || lowerName.includes('eigent<6E>mer')) {
state.owners = geojson;
state.owners.features.forEach((f, i) => f.id = f.id || i);
updateOwnerLayer();
renderOwnerList();
// Zoom to owner data if available
if (layers.owners.getBounds().isValid()) {
map.fitBounds(layers.owners.getBounds(), { padding: [20, 20] });
}
} else if (lowerName.includes('nutzung')) {
state.usage = geojson;
cachedObstacles = null; // Reset cache for drilling logic
updateUsageLayer();
} else if (lowerName.includes('wea')) {
state.wea = geojson;
updateWEALayer();
}
} catch (err) {
console.error("Fehler beim Kombinieren der Shapefile-Daten:", err);
}
}
function transformGeoJSON(geojson, from, to) {
const transformed = JSON.parse(JSON.stringify(geojson));
transformed.features.forEach(feature => {
if (!feature.geometry) return;
const transformPoint = (c) => proj4(from, to, c);
if (feature.geometry.type === 'Point') {
feature.geometry.coordinates = transformPoint(feature.geometry.coordinates);
} else if (feature.geometry.type === 'LineString' || feature.geometry.type === 'MultiPoint') {
feature.geometry.coordinates = feature.geometry.coordinates.map(transformPoint);
} else if (feature.geometry.type === 'Polygon' || feature.geometry.type === 'MultiLineString') {
feature.geometry.coordinates = feature.geometry.coordinates.map(ring => ring.map(transformPoint));
} else if (feature.geometry.type === 'MultiPolygon') {
feature.geometry.coordinates = feature.geometry.coordinates.map(poly => poly.map(ring => ring.map(transformPoint)));
}
});
return transformed;
}
function updateOwnerLayer() {
if (!state.owners || !state.owners.features) return;
const statusEl = document.getElementById('status-owners');
if (statusEl) {
statusEl.innerText = `${state.owners.features.length} Objekte`;
statusEl.style.color = 'var(--success)';
}
layers.owners.clearLayers();
if (layers.ownerStatus) layers.ownerStatus.clearLayers();
if (layers.ownerColor) layers.ownerColor.clearLayers();
const processedFeatures = [];
// Group features logic...
const groupedFeaturesMap = new Map();
state.owners.features.forEach(f => {
const p = f.properties;
const flur = p.Flur || p.FLUR || '';
const flst = p.Flurstueck || p.Flurst<EFBFBD>ck || p.flurstueck || p.FLST_NR || '';
const key = `${flur}-${flst}`;
if (flur && flst) {
if (groupedFeaturesMap.has(key)) {
groupedFeaturesMap.get(key).properties._mergedOwners.push(p);
} else {
const featureCopy = JSON.parse(JSON.stringify(f));
featureCopy.properties._mergedOwners = [p];
groupedFeaturesMap.set(key, featureCopy);
processedFeatures.push(featureCopy);
}
} else {
const featureCopy = JSON.parse(JSON.stringify(f));
featureCopy.properties._mergedOwners = [p];
processedFeatures.push(featureCopy);
}
});
if (state.visibleLayers.owners) {
layers.owners.addData({ type: "FeatureCollection", features: processedFeatures });
}
if (state.visibleLayers.ownerStatus && layers.ownerStatus) {
layers.ownerStatus.addData({ type: "FeatureCollection", features: processedFeatures });
}
if (state.visibleLayers.ownerColor && layers.ownerColor) {
layers.ownerColor.addData({ type: "FeatureCollection", features: processedFeatures });
}
}
window.toggleLayer = (layerKey, visible) => {
state.visibleLayers[layerKey] = visible;
if (layerKey === 'owners' || layerKey === 'ownerStatus' || layerKey === 'ownerColor') updateOwnerLayer();
if (layerKey === 'usage') updateUsageLayer();
if (layerKey === 'wea') updateWEALayer();
};
function updateWEALayer() {
if (!state.wea || !state.wea.features) return;
const statusEl = document.getElementById('status-wea');
if (statusEl) {
statusEl.innerText = `${state.wea.features.length} WEA`;
statusEl.style.color = 'var(--success)';
}
layers.wea.clearLayers();
if (state.visibleLayers.wea) {
layers.wea.addData(state.wea);
}
if (Object.keys(layers.wea._layers).length > 0 && state.visibleLayers.wea) {
map.fitBounds(layers.wea.getBounds(), { padding: [50, 50] });
}
}
function varStyle(name) {
const val = getComputedStyle(document.documentElement).getPropertyValue(name).trim();
return val || '#94a3b8';
}
function updateUsageLayer() {
if (!state.usage || !state.usage.features) return;
const statusEl = document.getElementById('status-usage');
// Filtering: Only show requested types
const requestedTypes = ['geh<65>lz', 'wald', 'bahnverkehr', 'stra<72>enverkehr', 'wohnbafl<66>che', 'weg', 'flie<69>gew<65>sser', 'stehendes gew<65>sser', 'gew<65>sser'];
const filteredFeatures = state.usage.features.filter(f => {
const type = (f.properties.nutzart || f.properties.NUTZART || '').toLowerCase();
return requestedTypes.some(rt => type.includes(rt));
});
if (statusEl) {
statusEl.innerText = `${filteredFeatures.length} Objekte`;
statusEl.style.color = 'var(--success)';
}
layers.usage.clearLayers();
if (state.visibleLayers.usage) {
layers.usage.addData({
type: "FeatureCollection",
features: filteredFeatures
});
}
if (layers.usage.getBounds().isValid() && state.visibleLayers.usage && filteredFeatures.length > 0) {
map.fitBounds(layers.usage.getBounds());
}
}
function zoomToPlot(props) {
if (!layers.owners) return;
const targetFlur = props.Flur || props.FLUR || props.flur || props.FL || props.fl || '';
const targetFlst = props.Flurstueck || props.Flurst<EFBFBD>ck || props.flurstueck || props.FLST_NR || props.FLST || props.flst || props.NUMMER || props.FlSt || '';
let found = false;
layers.owners.eachLayer(l => {
if (found) return;
const p = l.feature.properties;
const lFlur = p.Flur || p.FLUR || p.flur || p.FL || p.fl || '';
const lFlst = p.Flurstueck || p.Flurst<EFBFBD>ck || p.flurstueck || p.FLST_NR || p.FLST || p.flst || p.NUMMER || p.FlSt || '';
if (lFlur == targetFlur && lFlst == targetFlst) {
map.fitBounds(l.getBounds(), { padding: [100, 100], maxZoom: 18 });
l.openPopup();
found = true;
}
});
}
function renderOwnerList(filter = '') {
const list = document.getElementById('owner-list');
list.innerHTML = '';
// Group by unique owner to avoid duplicates
const uniqueOwners = new Map();
(state.owners.features || []).forEach(f => {
const p = f.properties;
const searchStr = `${p.Vorname || ''} ${p.Nachname || ''}`.toLowerCase();
if (searchStr.includes(filter.toLowerCase())) {
// Unique key: Name + Address
const key = `${p.Nachname}|${p.Vorname}|${p.Str_HNr || p.STR}|${p.Ort || p.ORT}`;
if (!uniqueOwners.has(key)) {
uniqueOwners.set(key, f);
}
}
});
uniqueOwners.forEach((f, key) => {
const currentNote = f.properties.notiz || '';
const isHighlighted = state.highlightedOwners && state.highlightedOwners[key];
const card = document.createElement('div');
card.className = 'owner-card';
if (isHighlighted) {
card.style.borderColor = '#ffee00';
card.style.borderWidth = '2px';
card.style.boxShadow = '0 0 10px rgba(255, 238, 0, 0.4)';
}
card.innerHTML = `
<div style="display: flex; justify-content: space-between; align-items: flex-start;">
<div>
<div class="owner-name">${f.properties.Vorname || ''} ${f.properties.Nachname || ''}</div>
<div style="font-size: 12px; color: #64748b;">${f.properties.Str_HNr || f.properties.STR || ''}, ${f.properties.PLZ || ''} ${f.properties.Ort || f.properties.ORT || ''}</div>
</div>
<div style="display: flex; gap: 4px;">
<button class="btn btn-icon ${isHighlighted ? 'active-highlight' : ''}" title="Fl<46>chen auf Karte markieren" onclick="event.stopPropagation(); toggleHighlightOwner('${key.replace(/'/g, "\\'")}')" style="padding: 4px; border: none; background: ${isHighlighted ? '#ffee00' : 'rgba(0,0,0,0.05)'}; border-radius: 6px; cursor: pointer;">
<i data-lucide="search" style="width: 16px; height: 16px; color: ${isHighlighted ? '#000' : '#333'};"></i>
</button>
<button class="btn btn-icon" title="Notiz bearbeiten" onclick="event.stopPropagation(); openNoteModal('${key.replace(/'/g, "\\'")}', \`${currentNote.replace(/`/g, '\\`')}\`)" style="padding: 4px; border: none; background: rgba(0,0,0,0.05); border-radius: 6px; cursor: pointer;">
<i data-lucide="file-edit" style="width: 16px; height: 16px; color: #333;"></i>
</button>
</div>
</div>
${currentNote ? `<div style="font-size: 11px; margin-top: 6px; padding: 6px; background: rgba(0,75,80,0.05); border-radius: 4px; border-left: 2px solid var(--corporate-teal); color: #333; font-style: italic;">${currentNote.replace(/\n/g, '<br>')}</div>` : ''}
<select class="status-select" data-id="${f.id}" style="margin-top: 8px;">
<option ${f.properties.status === 'Unbekannt' ? 'selected' : ''}>Unbekannt</option>
<option ${f.properties.status === 'In Gesellschaft' ? 'selected' : ''}>In Gesellschaft</option>
<option ${f.properties.status === 'Zustimmung liegt vor' ? 'selected' : ''}>Zustimmung liegt vor</option>
<option ${f.properties.status === 'Vertraglich gesichert' ? 'selected' : ''}>Vertraglich gesichert</option>
<option ${f.properties.status === 'Unentschlossen' ? 'selected' : ''}>Unentschlossen</option>
<option ${f.properties.status === 'M<>gliche positive R<>ckmeldung' ? 'selected' : ''}>M<>gliche positive R<>ckmeldung</option>
<option ${f.properties.status === 'Ablehnung' ? 'selected' : ''}>Ablehnung</option>
</select>
`;
card.addEventListener('click', (e) => {
if (e.target.classList.contains('status-select')) return;
zoomToPlot(f.properties);
});
list.appendChild(card);
});
lucide.createIcons({ root: list });
document.querySelectorAll('.status-select').forEach(sel => {
sel.addEventListener('change', (e) => {
const id = e.target.dataset.id;
const status = e.target.value;
const feature = state.owners.features.find(feat => feat.id == id);
if (feature) {
const name = feature.properties.Nachname;
const street = feature.properties.Str_HNr || feature.properties.STR;
state.owners.features.forEach(f => {
if (f.properties.Nachname === name && (f.properties.Str_HNr === street || f.properties.STR === street)) {
f.properties.status = status;
}
});
updateOwnerLayer();
}
});
});
}
document.getElementById('owner-search').addEventListener('input', (e) => {
renderOwnerList(e.target.value);
});
// --- Route Drawing Logic ---
const routeLayers = {};
const drillingLayers = {};
const labelLayers = {};
function updateRouteLayers() {
state.variants.forEach(v => {
// Ensure layer exists
if (!routeLayers[v.id]) {
routeLayers[v.id] = L.polyline(v.routes, {
color: '#cca300',
weight: 6,
editable: true,
pane: 'trassenPane',
// Explicitly set editable options on the layer to ensure uniformity
lineOptions: { color: '#cca300', weight: 6 },
vertexOptions: { color: '#cca300', radius: 5 },
middleMarkerOptions: { color: '#cca300', opacity: 0.5, radius: 3 }
});
drillingLayers[v.id] = L.featureGroup({ interactive: false, pane: 'drillingPane' });
labelLayers[v.id] = L.featureGroup({ interactive: false, pane: 'labelPane' });
if (v.visible) {
routeLayers[v.id].addTo(map);
drillingLayers[v.id].addTo(map);
labelLayers[v.id].addTo(map);
}
// Allow vertex insertion by clicking the polyline itself
routeLayers[v.id].on('click', handleVertexInsertion);
}
const layer = routeLayers[v.id];
layer.setStyle({ color: '#cca300' });
// Sync visibility
if (v.visible) {
if (!map.hasLayer(layer)) layer.addTo(map);
if (!map.hasLayer(drillingLayers[v.id])) drillingLayers[v.id].addTo(map);
if (!map.hasLayer(labelLayers[v.id])) labelLayers[v.id].addTo(map);
// Sync geometry if not actively being dragged/edited
if (!v.active) {
const current = JSON.stringify(layer.getLatLngs());
const target = JSON.stringify(v.routes);
if (current !== target) {
layer.setLatLngs(v.routes);
}
}
// Sync edit state
if (layer.enableEdit) {
if (v.active) {
layer.enableEdit();
layer.bringToFront();
drillingLayers[v.id].bringToFront();
labelLayers[v.id].bringToFront();
} else {
layer.disableEdit();
}
}
calculateStats(v);
} else {
if (map.hasLayer(layer)) map.removeLayer(layer);
if (map.hasLayer(drillingLayers[v.id])) map.removeLayer(drillingLayers[v.id]);
if (map.hasLayer(labelLayers[v.id])) map.removeLayer(labelLayers[v.id]);
}
});
}
document.getElementById('btn-draw').addEventListener('click', () => {
state.isDrawing = !state.isDrawing;
state.isMeasuring = false;
if (state.isDrawing) {
const activeV = state.variants.find(v => v.active);
if (activeV && routeLayers[activeV.id]) {
// Start/continue editing
if (!map.editTools) {
alert("Das Zeichen-Werkzeug konnte nicht geladen werden. Bitte pr<70>fen Sie Ihre Internetverbindung oder nutzen Sie einen anderen Browser (Chrome/Edge).");
state.isDrawing = false;
updateToolCursors();
return;
}
if (activeV.routes.length === 0) {
map.editTools.startPolyline();
} else if (routeLayers[activeV.id].editor) {
routeLayers[activeV.id].editor.continueForward();
} else {
routeLayers[activeV.id].enableEdit().continueForward();
}
}
} else {
if (map.editTools) map.editTools.stopDrawing();
}
updateToolCursors();
updateRouteLayers();
renderVariants();
});
function updateToolCursors() {
const container = map.getContainer();
if (state.isDrawing) container.style.cursor = 'crosshair';
else if (state.isMeasuring) container.style.cursor = 'help';
else container.style.cursor = '';
document.getElementById('btn-draw').className = state.isDrawing ? 'btn btn-primary' : 'btn btn-outline';
document.getElementById('btn-measure').className = state.isMeasuring ? 'btn btn-primary' : 'btn btn-outline';
}
map.on('click', (e) => {
if (state.isMeasuring) {
addMeasurementPoint(e.latlng);
}
});
// Capture changes (drawing, dragging, inserting, deleting)
map.on('editable:drawing:clicked editable:drawing:move editable:drawing:end editable:created editable:vertex:drag editable:vertex:dragend editable:vertex:deleted editable:vertex:inserted', (e) => {
const activeV = state.variants.find(v => v.active);
if (!activeV) return;
// Enforce color on everything newly created or edited
if (e.layer && e.layer.setStyle) {
e.layer.setStyle({ color: '#cca300', weight: 6 });
}
// If a new layer was created (via startPolyline), merge it into our existing layer
if (e.type === 'editable:created' && routeLayers[activeV.id] && e.layer !== routeLayers[activeV.id]) {
const newPoints = e.layer.getLatLngs();
routeLayers[activeV.id].setLatLngs(newPoints);
activeV.routes = newPoints;
map.removeLayer(e.layer); // We only want our primary layer
routeLayers[activeV.id].enableEdit();
} else if (routeLayers[activeV.id] && e.layer === routeLayers[activeV.id]) {
activeV.routes = e.layer.getLatLngs();
}
// Live updates for labels, drillings and stats
calculateStats(activeV);
updateVariantStatsUI(activeV);
if (e.type.includes('end') || e.type === 'editable:created') {
renderVariants();
if (state.directoryHandle) saveToFolder();
}
});
// Helper function to flatten coordinates
function getFlattenedCoords(rawRoutes) {
let latLngs = [];
const flattenLL = (raw) => {
if (!raw) return;
if (typeof raw === 'object' && raw.lat !== undefined && !isNaN(raw.lat)) {
latLngs.push(L.latLng(raw));
} else if (Array.isArray(raw) && raw.length === 2 && typeof raw[0] === 'number') {
latLngs.push(L.latLng(raw));
} else if (Array.isArray(raw)) {
raw.forEach(item => flattenLL(item));
}
};
flattenLL(rawRoutes);
return latLngs.map(ll => {
try { return L.latLng(ll); } catch (e) { return null; }
}).filter(ll => ll && !isNaN(ll.lat) && !isNaN(ll.lng));
}
function renderSegmentLabels(variant, coordsInput = null, tempPt = null) {
const vLabel = labelLayers[variant.id];
if (!vLabel) return;
// Use passed coordinates or fall back to state
let latLngs = coordsInput ? [...coordsInput] : getFlattenedCoords(variant.routes);
if (tempPt) latLngs.push(L.latLng(tempPt));
if (!variant.visible || latLngs.length < 2) {
vLabel.clearLayers();
return;
}
const currentMarkers = vLabel.getLayers();
let markerIdx = 0;
for (let i = 0; i < latLngs.length - 1; i++) {
const p1 = latLngs[i];
const p2 = latLngs[i + 1];
const dist = map.distance(p1, p2);
const mid = L.latLng((p1.lat + p2.lat) / 2, (p1.lng + p2.lng) / 2);
const labelText = `${dist.toFixed(0)}m`;
if (markerIdx < currentMarkers.length) {
const m = currentMarkers[markerIdx];
m.setLatLng(mid);
const el = m.getElement();
let contentSpan = null;
if (el) {
contentSpan = el.querySelector('.segment-label-content');
}
if (contentSpan) {
contentSpan.textContent = labelText;
} else {
// Backup if element not yet rendered or span missing
m.setIcon(L.divIcon({
className: 'segment-label',
html: `<span class="segment-label-content">${labelText}</span>`,
iconSize: [46, 20],
iconAnchor: [23, 10]
}));
}
} else {
L.marker(mid, {
interactive: false,
pane: 'labelPane', // Explicitly set pane
icon: L.divIcon({
className: 'segment-label',
html: `<span class="segment-label-content">${labelText}</span>`,
iconSize: [46, 20],
iconAnchor: [23, 10]
})
}).addTo(vLabel);
}
markerIdx++;
}
// Remove excess markers
while (markerIdx < currentMarkers.length) {
vLabel.removeLayer(currentMarkers[markerIdx++]);
}
}
// Cache for obstacles to avoid repeated filtering/lowercase during drag
let cachedObstacles = null;
function calculateStats(variant) {
if (!variant) return;
const vDrill = drillingLayers[variant.id];
if (vDrill) vDrill.clearLayers();
const latLngs = getFlattenedCoords(variant.routes);
if (latLngs.length < 2) {
variant.stats.total = 0;
variant.stats.drilling = 0;
variant.stats.muffen = 0;
return;
}
// Sync labels for final state
renderSegmentLabels(variant);
const line = turf.lineString(latLngs.map(ll => [ll.lng, ll.lat]));
variant.stats.total = turf.length(line, { units: 'meters' });
const startPoint = turf.point([latLngs[0].lng, latLngs[0].lat]);
// --- Bohrungs-Logik (Optimized Version) ---
const drillingRanges = [];
// Only re-filter if cache is empty or data changed
if (!cachedObstacles && state.usage.features) {
const keywords = ['bahn', 'gew<65>sser', 'wasser', 'stra<72>e', 'verkehr', 'geh<65>lz', 'baufl<66>che', 'wald', 'forst', 'hecke', 'weg', 'pfad', 'graben', 'bach', 'fluss'];
cachedObstacles = state.usage.features.filter(f => {
const type = (f.properties.nutzart || f.properties.NUTZART || f.properties.Nutzart || '').toLowerCase();
return keywords.some(k => type.includes(k));
});
}
const currentObstacles = cachedObstacles || [];
if (currentObstacles.length > 0) {
const lineBbox = turf.bbox(line);
currentObstacles.forEach(obs => {
try {
const obsBbox = turf.bbox(obs);
if (lineBbox[0] > obsBbox[2] || lineBbox[2] < obsBbox[0] ||
lineBbox[1] > obsBbox[3] || lineBbox[3] < obsBbox[1]) return;
if (turf.booleanIntersects(line, obs)) {
// Robust Intersection Strategy:
// 1. Find all intersection points
const intersect = turf.lineIntersect(line, obs);
let distances = [0, variant.stats.total];
intersect.features.forEach(f => {
const d = turf.length(turf.lineSlice(startPoint, f, line), { units: 'meters' });
distances.push(d);
});
// 2. Sort unique distances to create segments
distances = [...new Set(distances)].sort((a, b) => a - b);
// 3. Test the midpoint of each segment
for (let i = 0; i < distances.length - 1; i++) {
const dStart = distances[i];
const dEnd = distances[i + 1];
const midDist = (dStart + dEnd) / 2;
const midPt = turf.along(line, midDist / 1000, { units: 'kilometers' });
if (turf.booleanPointInPolygon(midPt, obs)) {
drillingRanges.push([
Math.max(0, dStart - 20),
Math.min(variant.stats.total, dEnd + 20)
]);
}
}
}
} catch (e) { /* ignore error */ }
});
}
// Merge
let mergedRanges = [];
if (drillingRanges.length > 0) {
drillingRanges.sort((a, b) => a[0] - b[0]);
let cur = drillingRanges[0];
for (let i = 1; i < drillingRanges.length; i++) {
if (drillingRanges[i][0] <= cur[1]) cur[1] = Math.max(cur[1], drillingRanges[i][1]);
else { mergedRanges.push(cur); cur = drillingRanges[i]; }
}
mergedRanges.push(cur);
}
variant.stats.drilling = mergedRanges.reduce((sum, r) => sum + (r[1] - r[0]), 0);
variant.stats.open = Math.max(0, variant.stats.total - variant.stats.drilling);
variant.stats.muffen = mergedRanges.length * 2;
variant.stats.hasTooLongDrilling = false;
// Reconstruct geometry
variant.drillingSegments = mergedRanges.map(range => {
try {
const lengthM = range[1] - range[0];
if (lengthM > 180) variant.stats.hasTooLongDrilling = true;
const s = turf.along(line, range[0] / 1000, { units: 'kilometers' });
const e = turf.along(line, range[1] / 1000, { units: 'kilometers' });
const sCoord = [s.geometry.coordinates[1], s.geometry.coordinates[0]];
const eCoord = [e.geometry.coordinates[1], e.geometry.coordinates[0]];
return {
path: [sCoord, eCoord],
muffen: [sCoord, eCoord],
length: lengthM
};
} catch (e) { return null; }
}).filter(s => s !== null);
if (variant.visible) {
// Drilling & Muffen
if (vDrill) {
variant.drillingSegments.forEach(seg => {
L.polyline(seg.path, {
color: '#000000',
weight: 8,
dashArray: '5, 10',
interactive: false,
pane: 'drillingPane' // Explicitly set pane
}).addTo(vDrill);
// Individual drilling label (number only)
const midLat = (seg.path[0][0] + seg.path[1][0]) / 2;
const midLng = (seg.path[0][1] + seg.path[1][1]) / 2;
L.marker([midLat, midLng], {
interactive: false,
pane: 'labelPane', // Explicitly set pane
icon: L.divIcon({
className: 'drilling-segment-label',
html: `<div style="background: #000000; color: white; padding: 1px 6px; border-radius: 4px; font-size: 10px; font-weight: bold; white-space: nowrap; box-shadow: 0 2px 4px rgba(0,0,0,0.2);">${seg.length.toFixed(0)}m</div>`,
iconSize: [50, 20],
iconAnchor: [25, 25]
})
}).addTo(vDrill);
// Add Muffen markers
seg.muffen.forEach(mpos => {
L.circleMarker(mpos, {
radius: 5,
color: '#000000',
fillColor: '#fff',
fillOpacity: 1,
weight: 2,
interactive: false
}).addTo(vDrill);
});
});
}
// ONLY update the sidebar UI/list if this is the active variant
if (variant.active) {
updateRequiredPlots(variant);
}
}
}
function updateRequiredPlots(variant) {
const container = document.getElementById('required-plots-container');
if (!container) return;
container.innerHTML = '<h4 style="font-size: 13px; margin-bottom: 10px; color: #64748b; display: flex; align-items: center; gap: 8px;"><i data-lucide="layers" style="width: 14px;"></i> Ben<65>tigte Flurst<73>cke</h4>';
if (!variant || !variant.routes || variant.routes.length < 2 || !state.owners.features || state.owners.features.length === 0) {
container.innerHTML += '<p style="font-size: 11px; color: #94a3b8; text-align: center; margin-top: 20px;">Keine Daten oder Trasse vorhanden</p>';
return;
}
try {
const latLngs = getFlattenedCoords(variant.routes);
if (latLngs.length < 2) return;
const line = turf.lineString(latLngs.map(ll => [ll.lng, ll.lat]));
const lineBbox = turf.bbox(line);
const intersectingPlots = state.owners.features.filter(f => {
try {
const obsBbox = turf.bbox(f);
if (lineBbox[0] > obsBbox[2] || lineBbox[2] < obsBbox[0] ||
lineBbox[1] > obsBbox[3] || lineBbox[3] < obsBbox[1]) return false;
return turf.booleanIntersects(line, f);
} catch (e) { return false; }
});
if (intersectingPlots.length === 0) {
container.innerHTML += '<p style="font-size: 11px; color: #94a3b8; text-align: center; margin-top: 20px;">Keine <20>berschneidungen gefunden</p>';
} else {
// Group by Flur/Flurst<73>ck to avoid duplicates if owners are split across features
const seen = new Set();
intersectingPlots.forEach(f => {
const p = f.properties;
const flur = p.Flur || p.FLUR || p.flur || p.FL || p.fl || '';
const flst = p.Flurstueck || p.Flurst<EFBFBD>ck || p.flurstueck || p.FLST_NR || p.FLST || p.flst || p.NUMMER || p.FlSt || '';
const gem = p.Gemarkung || p.GEMARKUNG || '';
const ownerKey = `${p.Vorname}-${p.Nachname}`;
const key = `${gem}-${flur}-${flst}-${ownerKey}`;
if (seen.has(key)) return;
seen.add(key);
const statusColor = getStatusColor(p.status || 'Unbekannt');
const name = `${p.Vorname || ''} ${p.Nachname || ''}`.trim() || 'Unbekannter Eigent<6E>mer';
const currentNote = p.notiz || '';
const noteOwnerKey = `${p.Nachname}|${p.Vorname}|${p.Str_HNr || p.STR}|${p.Ort || p.ORT}`;
const card = document.createElement('div');
card.className = 'plot-card';
card.style.cursor = 'pointer';
card.innerHTML = `
<div class="status-dot" style="background-color: ${statusColor};" title="${p.status || 'Unbekannt'}"></div>
<div class="plot-info">
<div style="display: flex; justify-content: space-between; align-items: flex-start;">
<div class="plot-owner">${name}</div>
<button class="btn btn-icon" title="Notiz bearbeiten" onclick="event.stopPropagation(); openNoteModal('${noteOwnerKey.replace(/'/g, "\\'")}', \`${currentNote.replace(/`/g, '\\`')}\`)" style="padding: 2px; border: none; background: rgba(0,0,0,0.05); border-radius: 4px; cursor: pointer;">
<i data-lucide="file-edit" style="width: 14px; height: 14px; color: #333;"></i>
</button>
</div>
<div class="plot-details">${gem ? gem + ' | ' : ''}Fl. ${flur}, FlSt ${flst}</div>
${currentNote ? `<div style="font-size: 10px; margin-top: 4px; color: #64748b; font-style: italic; background: rgba(0,0,0,0.02); padding: 4px; border-radius: 4px; border-left: 2px solid var(--corporate-teal);"><i data-lucide="info" style="width: 10px; height: 10px; display: inline-block; margin-right: 2px; vertical-align: middle;"></i>${currentNote}</div>` : ''}
</div>
`;
card.addEventListener('click', () => zoomToPlot(p));
container.appendChild(card);
});
}
} catch (err) {
console.error("Error updating required plots:", err);
}
lucide.createIcons({ root: container });
}
// --- Variant Management UI ---
function renderVariants() {
const container = document.getElementById('variant-controls');
if (!container) return;
// Critical Safeguard: Ensure variants is always an array
if (!Array.isArray(state.variants)) state.variants = [];
container.innerHTML = '';
if (state.variants.length > 0 && !state.variants.find(v => v.active)) {
state.variants[0].active = true;
}
// Render Tabs
const tabContainer = document.createElement('div');
tabContainer.style.display = 'flex';
tabContainer.style.gap = '6px';
tabContainer.style.marginBottom = '12px';
tabContainer.style.overflowX = 'auto';
tabContainer.style.paddingBottom = '4px';
state.variants.forEach(v => {
const btn = document.createElement('button');
const vName = v.name || "Variante";
btn.innerText = vName.replace('Variante ', ''); // Kurzer Name 'A', 'B' usw.
btn.title = v.name;
btn.style.padding = '6px 14px';
btn.style.border = 'none';
btn.style.borderRadius = '6px';
btn.style.cursor = 'pointer';
btn.style.fontWeight = 'bold';
btn.style.flexShrink = '0';
if (v.active) {
btn.style.background = 'var(--primary)';
btn.style.color = 'white';
btn.style.boxShadow = '0 2px 4px rgba(0,0,0,0.1)';
} else {
btn.style.background = '#e2e8f0';
btn.style.color = '#475569';
}
btn.onclick = () => setActiveVariant(v.id);
tabContainer.appendChild(btn);
});
container.appendChild(tabContainer);
// Render Active Variant Content
const v = state.variants.find(v => v.active);
if (v) {
const div = document.createElement('div');
div.id = `variant-card-${v.id}`;
div.style.padding = '12px';
div.style.background = 'white';
div.style.borderRadius = '10px';
div.style.border = '1px solid #e2e8f0';
div.innerHTML = `
<div style="display: flex; align-items: center; justify-content: space-between; margin-bottom: 12px; border-bottom: 1px solid #e2e8f0; padding-bottom: 8px;">
<span style="font-weight: bold; color: #cca300; font-size: 15px; cursor: pointer;" onclick="renameVariant(${v.id})" title="Klicken zum Umbenennen">
${v.name || 'Variante'}
</span>
<div style="display: flex; gap: 10px; align-items: center;">
<label style="display: flex; align-items: center; gap: 2px; font-size: 12px; cursor: pointer;" title="Auf Karte ein-/ausblenden">
<input type="checkbox" ${v.visible ? 'checked' : ''} onclick="toggleVariantVisibility(${v.id}, this.checked)">
<i data-lucide="eye" style="width: 14px;"></i>
</label>
<i data-lucide="table" class="action-icon" title="Tabelle herunterladen" style="cursor: pointer; width: 15px; color: var(--corporate-teal);" onclick="downloadVariantTable(${v.id})"></i>
<i data-lucide="copy" class="action-icon" title="Duplizieren" style="cursor: pointer; width: 15px; color: #64748b;" onclick="duplicateVariant(${v.id})"></i>
<i data-lucide="trash-2" class="action-icon" title="Inhalt l<>schen" style="cursor: pointer; width: 15px; color: var(--danger);" onclick="clearVariant(${v.id})"></i>
</div>
</div>
<div class="stat-card" style="padding: 0; margin: 0; background: transparent;">
<div class="stat-row"><span>Gesamtl<74>nge:</span> <span class="stat-total-val" style="font-weight: 600;">${(v.stats?.total || 0).toFixed(0)} m</span></div>
<div class="stat-row"><span>Offenbauweise:</span> <span class="stat-open-val" style="font-weight: 600;">${(v.stats?.open || 0).toFixed(0)} m</span></div>
<div class="stat-row drilling-stat"><span>Bohrungsanteil:</span> <span class="stat-drilling-val">${(v.stats?.drilling || 0).toFixed(0)} m</span></div>
<div class="stat-row muffen-stat" style="color: #8b5cf6; font-weight: 600;"><span>Anzahl Muffen:</span> <span class="stat-muffen-val">${v.stats?.muffen || 0}</span></div>
</div>
${v.stats?.hasTooLongDrilling ?
'<div style="background: rgba(239,68,68,0.1); border: 1px solid var(--danger); color: var(--danger); padding: 8px; border-radius: 8px; font-size: 11px; margin-top: 10px; display: flex; align-items: center; gap: 6px;">' +
'<i data-lucide="alert-triangle" style="width: 14px;"></i>' +
'<span>Achtung: Bohrung > 180m</span>' +
'</div>' : ''}
`;
container.appendChild(div);
}
lucide.createIcons();
}
function updateVariantStatsUI(variant) {
const card = document.getElementById(`variant-card-${variant.id}`);
if (card) {
const totalSpan = card.querySelector('.stat-total-val');
const openSpan = card.querySelector('.stat-open-val');
const drillingSpan = card.querySelector('.stat-drilling-val');
const muffenSpan = card.querySelector('.stat-muffen-val');
if (totalSpan) totalSpan.textContent = `${variant.stats.total.toFixed(0)} m`;
if (openSpan) openSpan.textContent = `${(variant.stats.open || 0).toFixed(0)} m`;
if (drillingSpan) drillingSpan.textContent = `${variant.stats.drilling.toFixed(0)} m`;
if (muffenSpan) muffenSpan.textContent = variant.stats.muffen || 0;
}
}
window.toggleVariantVisibility = (id, visible) => {
const v = state.variants.find(varnt => varnt.id === id);
if (v) {
v.visible = visible;
updateRouteLayers();
}
};
window.setActiveVariant = (id) => {
state.variants.forEach(v => v.active = (v.id === id));
renderVariants();
updateToolCursors();
updateRouteLayers();
const activeV = state.variants.find(v => v.id === id);
if (activeV) updateRequiredPlots(activeV);
};
window.renameVariant = (id) => {
const v = state.variants.find(v => v.id === id);
if (!v) return;
const newName = prompt("Neuer Name f<>r die Variante:", v.name);
if (newName && newName.trim()) {
v.name = newName.trim();
renderVariants();
if (state.directoryHandle) saveToFolder();
}
};
window.duplicateVariant = (id) => {
const original = state.variants.find(v => v.id === id);
if (!original) return;
const copy = JSON.parse(JSON.stringify(original));
copy.id = Date.now();
copy.name = (copy.name || "Variante") + " (Kopie)";
copy.active = true;
copy.visible = true;
state.variants.forEach(v => v.active = false);
state.variants.push(copy);
renderVariants();
updateRouteLayers();
const activeV = state.variants.find(v => v.active);
if (activeV) updateRequiredPlots(activeV);
if (state.directoryHandle) saveToFolder();
};
window.clearVariant = (id) => {
const v = state.variants.find(v => v.id === id);
if (!v) return;
if (!confirm(`M<EFBFBD>chten Sie alle gezeichneten Linien in '${v.name}' wirklich l<>schen?`)) return;
// Reset geometry and stats
v.routes = [];
v.stats = { total: 0, drilling: 0, open: 0, muffen: 0 };
if (routeLayers[id]) {
routeLayers[id].setLatLngs([]);
if (routeLayers[id].editor) routeLayers[id].editor.reset();
}
if (drillingLayers[id]) drillingLayers[id].clearLayers();
if (labelLayers[id]) labelLayers[id].clearLayers();
renderVariants();
updateRouteLayers();
if (v.active) updateRequiredPlots(v);
if (state.directoryHandle) saveToFolder();
};
// Note: deleteVariant is now effectively disabled to keep the slots fixed
window.deleteVariant = (id) => {
console.log("Delete disabled. Use clearVariant instead.");
window.clearVariant(id);
};
// Initial rendering deferred to end of script or window.load
function initApp() {
// Ensure we have at least our default variants if something went wrong
if (!state.variants || state.variants.length === 0) {
state.variants = [
{ id: 1, name: "Variante A", color: "#cca300", routes: [], active: true, visible: true, stats: { total: 0, drilling: 0, open: 0, muffen: 0 } },
{ id: 2, name: "Variante B", color: "#cca300", routes: [], active: false, visible: true, stats: { total: 0, drilling: 0, open: 0, muffen: 0 } },
{ id: 3, name: "Variante C", color: "#cca300", routes: [], active: false, visible: true, stats: { total: 0, drilling: 0, open: 0, muffen: 0 } },
{ id: 4, name: "Variante D", color: "#cca300", routes: [], active: false, visible: true, stats: { total: 0, drilling: 0, open: 0, muffen: 0 } },
{ id: 5, name: "Variante E", color: "#cca300", routes: [], active: false, visible: true, stats: { total: 0, drilling: 0, open: 0, muffen: 0 } },
{ id: 6, name: "Variante F", color: "#cca300", routes: [], active: false, visible: true, stats: { total: 0, drilling: 0, open: 0, muffen: 0 } },
{ id: 7, name: "Variante G", color: "#cca300", routes: [], active: false, visible: true, stats: { total: 0, drilling: 0, open: 0, muffen: 0 } },
{ id: 8, name: "Variante H", color: "#cca300", routes: [], active: false, visible: true, stats: { total: 0, drilling: 0, open: 0, muffen: 0 } }
];
}
// Force update colors to the new standard on startup
state.variants.forEach(v => v.color = '#cca300');
// Ensure at least one is active
if (!state.variants.find(v => v.active)) {
state.variants[0].active = true;
}
renderVariants();
updateRouteLayers();
updateOwnerLayer();
updateUsageLayer();
updateWEALayer();
const activeV = state.variants.find(v => v.active);
if (activeV) updateRequiredPlots(activeV);
}
// --- Ad-hoc Measurement Tool ---
let measureLayer = L.featureGroup().addTo(map);
let activeMeasure = null;
let tempLine = null;
document.getElementById('btn-measure').addEventListener('click', () => {
state.isMeasuring = !state.isMeasuring;
state.isDrawing = false;
updateToolCursors();
if (!state.isMeasuring && tempLine) {
map.removeLayer(tempLine);
tempLine = null;
}
});
function addMeasurementPoint(latlng) {
if (!activeMeasure) {
activeMeasure = L.polyline([latlng], { color: '#333', weight: 3 }).addTo(measureLayer);
tempLine = L.polyline([latlng, latlng], { color: '#999', weight: 2, dashArray: '5, 5' }).addTo(map);
map.on('mousemove', onMeasureMove);
} else {
activeMeasure.addLatLng(latlng);
tempLine.setLatLngs([latlng, latlng]);
}
}
function onMeasureMove(e) {
if (activeMeasure && tempLine) {
const pts = activeMeasure.getLatLngs();
const last = pts[pts.length - 1];
tempLine.setLatLngs([last, e.latlng]);
// Live distance tooltip
const dist = map.distance(last, e.latlng);
tempLine.bindTooltip(`${dist.toFixed(1)} m`, { sticky: true, offset: [15, 0] }).openTooltip();
}
}
map.on('contextmenu', () => {
if (state.isMeasuring && activeMeasure) {
const pts = activeMeasure.getLatLngs();
const totalDist = calculatePolylineDistance(pts);
// Add marker with distance and "X" to delete
const lastPt = pts[pts.length - 1];
const marker = L.marker(lastPt, {
icon: L.divIcon({
className: 'measure-label',
html: `<div style="background: white; padding: 2px 8px; border-radius: 10px; border: 1px solid #333; white-space: nowrap;">
${totalDist.toFixed(1)} m <span style="color:red; cursor:pointer;" onclick="clearMeasure(this)"><3E></span>
</div>`,
iconSize: [100, 20]
})
}).addTo(measureLayer);
// Link marker to polyline for deletion
marker.poly = activeMeasure;
activeMeasure = null;
if (tempLine) tempLine.remove();
map.off('mousemove', onMeasureMove);
}
});
function calculatePolylineDistance(pts) {
let d = 0;
for (let i = 0; i < pts.length - 1; i++) {
d += map.distance(pts[i], pts[i + 1]);
}
return d;
}
window.clearMeasure = (el) => {
measureLayer.clearLayers();
};
// Delete vertex on double-click
map.on('editable:vertex:dblclick', (e) => {
e.vertex.delete();
});
// Global Map Click for generous vertex insertion ("Click anywhere near the line")
const handleVertexInsertion = (e) => {
if (state.isMeasuring) return;
// Allow native drawing to process if we are currently drawing forward and click far away,
// but we still want to be able to insert points during drawing if we click an existing segment.
const variant = state.variants.find(v => v.active);
if (!variant) return;
const layer = routeLayers[variant.id];
if (!layer) return;
// If the variant is empty, we don't return here anymore,
// but we only attempt insertion if we have at least one segment (2 points).
const latlngs = getFlattenedCoords(layer.getLatLngs());
// If drawing and clicking far away, we let startPolyline/continueForward handle it
if (latlngs.length < 2) return;
if (!layer.editor) {
layer.enableEdit();
}
let minIdx = -1;
let minDist = Infinity;
const pt = map.latLngToLayerPoint(e.latlng);
for (let i = 0; i < latlngs.length - 1; i++) {
const p1 = map.latLngToLayerPoint(latlngs[i]);
const p2 = map.latLngToLayerPoint(latlngs[i + 1]);
const dist = L.LineUtil.pointToSegmentDistance(pt, p1, p2);
if (dist < minDist) {
minDist = dist;
minIdx = i;
}
}
// CRITICAL FIX: Check if we are clicking directly on or very near an existing vertex
// If we are closer than 10 pixels to any vertex, we assume the user wants
// to move the point, not insert a new one.
const isNearVertex = latlngs.some(ll => map.latLngToLayerPoint(ll).distanceTo(pt) < 12);
if (isNearVertex) return;
// High tolerance hit-testing: 35 pixels (approx. the width of a finger/mouse inaccuracy)
const tolerance = state.isDrawing ? 15 : 35;
if (minIdx !== -1 && minDist < tolerance) {
latlngs.splice(minIdx + 1, 0, e.latlng);
layer.setLatLngs(latlngs);
if (layer.editor && layer.editor.reset) {
layer.editor.reset();
}
// Force synchronization
variant.routes = getFlattenedCoords(layer.getLatLngs());
calculateStats(variant);
updateVariantStatsUI(variant);
renderVariants();
if (state.directoryHandle) saveToFolder();
}
};
map.on('click', handleVertexInsertion);
layers.owners.on('click', handleVertexInsertion);
layers.usage.on('click', handleVertexInsertion);
// --- Local Folder Persistence (File System Access API) ---
const DB_NAME = "TrassenPlanerSync";
const STORE_NAME = "directoryHandles";
async function openDB() {
return new Promise((resolve, reject) => {
const request = indexedDB.open(DB_NAME, 1);
request.onupgradeneeded = () => request.result.createObjectStore(STORE_NAME);
request.onsuccess = () => resolve(request.result);
request.onerror = () => reject(request.error);
});
}
async function saveHandle(handle) {
const db = await openDB();
const tx = db.transaction(STORE_NAME, "readwrite");
tx.objectStore(STORE_NAME).put(handle, "projectRoot");
return new Promise(r => tx.oncomplete = r);
}
async function getHandle() {
const db = await openDB();
return new Promise((resolve) => {
const request = db.transaction(STORE_NAME).objectStore(STORE_NAME).get("projectRoot");
request.onsuccess = () => resolve(request.result);
request.onerror = () => resolve(null);
});
}
document.getElementById('btn-sync-folder').addEventListener('click', async () => {
try {
if (!window.showDirectoryPicker) {
alert("Ihr Browser unterst<73>tzt keinen direkten Ordner-Zugriff. Bitte nutzen Sie Chrome oder Edge.");
return;
}
if (state.directoryHandle) {
// Check if already connected or just needs permission
const status = await state.directoryHandle.queryPermission({ mode: 'readwrite' });
if (status === 'granted') {
// Already works, so assume the user wants to SWITCH folders if they click again
if (!confirm("Ordner ist bereits synchronisiert. M<>chten Sie einen ANDEREN Ordner w<>hlen?")) {
return;
}
} else {
// Just needs permission restore
const newStatus = await state.directoryHandle.requestPermission({ mode: 'readwrite' });
if (newStatus === 'granted') {
updateSyncUI('connected');
await loadFromFolder();
return;
}
}
}
// New picker (either first time or switching)
state.directoryHandle = await window.showDirectoryPicker();
await saveHandle(state.directoryHandle);
updateSyncUI('connected');
await loadFromFolder();
} catch (err) {
console.log("Folder selection cancelled/failed", err);
}
});
function updateSyncUI(status) {
const btn = document.getElementById('btn-sync-folder');
if (status === 'connected') {
btn.innerHTML = '<i data-lucide="refresh-cw" style="width: 16px;"></i> Synchronisiert';
btn.style.color = 'var(--success)';
} else if (status === 'restorable') {
btn.innerHTML = '<i data-lucide="lock" style="width: 16px;"></i> Zugriff erlauben';
btn.style.color = 'var(--primary)';
} else {
btn.innerHTML = '<i data-lucide="link" style="width: 16px;"></i> Ordner synchronisieren';
btn.style.color = '';
}
lucide.createIcons({ root: btn });
}
let isLoading = false;
async function loadFromFolder() {
if (isLoading || !state.directoryHandle) return;
isLoading = true;
if (await state.directoryHandle.queryPermission({ mode: 'readwrite' }) !== 'granted') {
if (await state.directoryHandle.requestPermission({ mode: 'readwrite' }) !== 'granted') return;
}
try {
const geodatenHandle = await state.directoryHandle.getDirectoryHandle('Geodaten', { create: false }).catch(() => null);
if (!geodatenHandle) {
alert("Der Ordner 'Geodaten' wurde im gew<65>hlten Verzeichnis nicht gefunden. Bitte w<>hlen Sie den Hauptordner Ihres Projekts.");
return;
}
// 1. Project State
try {
const projectFile = await geodatenHandle.getFileHandle('project.json');
const file = await projectFile.getFile();
const imported = JSON.parse(await file.text());
if (imported.variants) {
// Better Merge: Start with current defaults (A-H) and overlay imported data
const merged = [...state.variants];
imported.variants.forEach(v => {
let idx = merged.findIndex(m => m.id === v.id || m.name === v.name);
if (idx !== -1) {
merged[idx] = { ...merged[idx], ...v };
} else {
merged.push(v);
}
});
state.variants = merged.sort((a, b) => {
const nameA = (a.name || "").toUpperCase();
const nameB = (b.name || "").toUpperCase();
if (nameA < nameB) return -1;
if (nameA > nameB) return 1;
return 0;
});
delete imported.variants;
}
Object.assign(state, imported);
// Force update colors to the new standard
if (state.variants) state.variants.forEach(v => v.color = '#cca300');
renderVariants(); updateOwnerLayer(); updateUsageLayer(); updateWEALayer(); updateRouteLayers(); renderOwnerList();
console.log("State loaded from project.json");
} catch (e) { }
// 2. Shapefiles & GeoJSONs
const fileGroups = {};
const geoJsons = {};
for await (const entry of geodatenHandle.values()) {
if (entry.kind !== 'file') continue;
const parts = entry.name.split('.');
const ext = parts.pop().toLowerCase();
const base = parts.join('.').toLowerCase();
if (ext === 'shp' || ext === 'dbf') {
if (!fileGroups[base]) fileGroups[base] = {};
fileGroups[base][ext] = await (await entry.getFile()).arrayBuffer();
} else if (ext === 'geojson') {
const content = JSON.parse(await (await entry.getFile()).text());
geoJsons[base] = content;
}
}
// Process Shapefiles first
for (let base in fileGroups) {
if (fileGroups[base].shp && fileGroups[base].dbf) {
await processShapefileGroup(base, fileGroups[base]);
}
}
// Fallback to GeoJSON if layers still empty
if (!state.owners?.features?.length && geoJsons['eigentuemer']) {
state.owners = geoJsons['eigentuemer'];
updateOwnerLayer(); renderOwnerList();
}
if (!state.usage?.features?.length && geoJsons['nutzung']) {
state.usage = geoJsons['nutzung'];
updateUsageLayer();
}
if (!state.wea?.features?.length && geoJsons['wea']) {
state.wea = geoJsons['wea'];
updateWEALayer();
}
} catch (err) { console.error("Auto-load failed:", err); }
finally { isLoading = false; }
}
let isSaving = false;
async function saveToFolder() {
if (isSaving || !state.directoryHandle) return;
isSaving = true;
try {
const geoHandle = await state.directoryHandle.getDirectoryHandle('Geodaten', { create: true });
const saveFile = async (name, data) => {
const handle = await geoHandle.getFileHandle(name, { create: true });
const writable = await handle.createWritable();
await writable.write(JSON.stringify(data, null, 2));
await writable.close();
};
// 1. Project State
const saveData = JSON.parse(JSON.stringify(state, (key, value) => {
if (key === 'directoryHandle' || key.startsWith('_')) return undefined;
return value;
}));
await saveFile('project.json', saveData);
// 2. Layer Data
if (state.owners?.features?.length > 0) await saveFile('eigentuemer.geojson', state.owners);
if (state.usage?.features?.length > 0) await saveFile('nutzung.geojson', state.usage);
if (state.wea?.features?.length > 0) await saveFile('wea.geojson', state.wea);
console.log("Deep-Save completed to Geodaten/");
} catch (err) {
console.error("Save failed:", err);
// Non-intrusive log instead of alert for background sync
} finally {
isSaving = false;
}
}
// --- PDF Export Implementation ---
async function runPdfExport(btnId) {
const btn = document.getElementById(btnId);
const originalText = btn.innerHTML;
btn.innerHTML = '<i class="spin" data-lucide="refresh-cw"></i> Exportiere...';
lucide.createIcons({ root: btn });
try {
const { jsPDF } = window.jspdf;
const doc = new jsPDF({
orientation: 'landscape',
unit: 'mm',
format: 'a4'
});
const pageWidth = doc.internal.pageSize.getWidth();
const pageHeight = doc.internal.pageSize.getHeight();
const margin = 10;
// 1. Capture Map
const mapContainer = document.getElementById('map');
await new Promise(r => setTimeout(r, 500));
const controls = document.querySelectorAll('.leaflet-control-container, .leaflet-control-zoom, .leaflet-control-layers');
controls.forEach(c => c.style.display = 'none');
const canvas = await html2canvas(mapContainer, {
useCORS: true,
scale: 2,
backgroundColor: '#ffffff',
ignoreElements: (el) => el.classList.contains('leaflet-control-container') || el.classList.contains('leaflet-control-zoom') || el.classList.contains('leaflet-control-layers')
});
controls.forEach(c => c.style.display = '');
const imgData = canvas.toDataURL('image/jpeg', 0.95);
const mapW = pageWidth - (margin * 2);
const mapH = pageHeight - (margin * 2) - 30;
doc.addImage(imgData, 'JPEG', margin, margin + 20, mapW, mapH);
// 2. Header
const logo = document.querySelector('.sidebar-header img');
if (logo && logo.complete && logo.naturalWidth > 0) {
try {
const logoCanvas = document.createElement('canvas');
logoCanvas.width = logo.naturalWidth;
logoCanvas.height = logo.naturalHeight;
const ctx = logoCanvas.getContext('2d');
ctx.drawImage(logo, 0, 0);
const logoData = logoCanvas.toDataURL('image/png');
const lWidth = 45;
const lHeight = lWidth * (logo.naturalHeight / logo.naturalWidth);
doc.addImage(logoData, 'PNG', margin, margin, lWidth, lHeight);
} catch (e) { }
}
doc.setFont("helvetica", "bold");
doc.setFontSize(18);
doc.setTextColor(0, 75, 80);
doc.text("Projekt: Trassenplanung BW Scheddebrock", margin + 50, margin + 10);
doc.setFontSize(10);
doc.setFont("helvetica", "normal");
doc.setTextColor(100, 100, 100);
doc.text(`Datum: ${new Date().toLocaleDateString('de-DE')} | Projektstand: v1.0.4`, margin + 50, margin + 16);
// 3. North Arrow
const nx = pageWidth - margin - 15;
const ny = margin + 12;
doc.setDrawColor(0, 0, 0);
doc.line(nx, ny, nx, ny + 12);
doc.line(nx, ny, nx - 4, ny + 4);
doc.line(nx, ny, nx + 4, ny + 4);
doc.text("N", nx - 1.5, ny - 3);
// 4. Legend
const legendX = margin + 5;
const legendY = pageHeight - margin - 35;
doc.setFillColor(255, 255, 255);
doc.rect(legendX, legendY, 65, 35, 'F');
doc.setDrawColor(200, 200, 200);
doc.rect(legendX, legendY, 65, 35, 'S');
doc.setFontSize(9);
doc.setFont("helvetica", "bold");
doc.setTextColor(0, 0, 0);
doc.text("Legende", legendX + 5, legendY + 7);
let ly = legendY + 13;
doc.setFont("helvetica", "normal");
doc.setFontSize(8);
doc.setFillColor(41, 149, 0);
doc.rect(legendX + 5, ly - 3, 4, 4, 'F');
doc.text("Vertraglich gesichert", legendX + 12, ly);
ly += 5;
doc.setFillColor(102, 230, 89);
doc.rect(legendX + 5, ly - 3, 4, 4, 'F');
doc.text("Zustimmung liegt vor", legendX + 12, ly);
ly += 5;
doc.setDrawColor(204, 163, 0);
doc.line(legendX + 5, ly - 1, legendX + 9, ly - 1);
doc.text("Geplante Trasse", legendX + 12, ly);
ly += 5;
doc.setDrawColor(0, 0, 0);
doc.setLineDash([1, 1], 0);
doc.line(legendX + 5, ly - 1, legendX + 9, ly - 1);
doc.setLineDash([], 0);
doc.text("HDD-Bohrung", legendX + 12, ly);
doc.save(`Trassenplan_Export_${new Date().toISOString().split('T')[0]}.pdf`);
} catch (err) {
console.error("PDF Export failed:", err);
alert("PDF Export fehlgeschlagen.");
} finally {
btn.innerHTML = originalText;
lucide.createIcons({ root: btn });
}
}
// Attach listeners to both possible PDF buttons (one in footer, one in options)
const pdfBtn = document.getElementById('btn-pdf-export');
if (pdfBtn) pdfBtn.addEventListener('click', () => runPdfExport('btn-pdf-export'));
const pdfBtnAlt = document.getElementById('btn-pdf-export-alt');
if (pdfBtnAlt) pdfBtnAlt.addEventListener('click', () => runPdfExport('btn-pdf-export-alt'));
document.getElementById('btn-export').addEventListener('click', async () => {
try {
if (state.directoryHandle) await saveToFolder();
const zip = new JSZip();
const saveData = JSON.parse(JSON.stringify(state, (key, value) => {
if (key === 'directoryHandle' || key.startsWith('_')) return undefined;
return value;
}));
zip.file("project.json", JSON.stringify(saveData));
zip.file("eigentuemer.geojson", JSON.stringify(state.owners));
zip.file("nutzung.geojson", JSON.stringify(state.usage));
zip.file("wea.geojson", JSON.stringify(state.wea));
state.variants.forEach(v => {
if (v.routes.length > 1) {
const line = turf.lineString(v.routes.map(ll => [ll.lng, ll.lat]));
zip.file(`trasse_${v.name.replace(/\s+/g, '_')}.geojson`, JSON.stringify(line));
}
});
const content = await zip.generateAsync({ type: "blob" });
const url = window.URL.createObjectURL(content);
const a = document.createElement("a");
a.href = url;
a.download = `TrassenPlaner_Export_${new Date().toISOString().split('T')[0]}.zip`;
document.body.appendChild(a);
a.click();
setTimeout(() => { window.URL.revokeObjectURL(url); a.remove(); }, 100);
} catch (err) {
console.error("Export failed:", err);
alert("Export fehlgeschlagen: " + err.message);
}
});
// Options Popup Logic
const toggleBtn = document.getElementById('btn-toggle-options');
const optionsPopup = document.getElementById('options-popup');
toggleBtn.addEventListener('click', (e) => {
e.stopPropagation();
optionsPopup.classList.toggle('active');
lucide.createIcons({ root: optionsPopup });
});
// Close when clicking outside
document.addEventListener('click', (e) => {
if (!optionsPopup.contains(e.target) && e.target !== toggleBtn) {
optionsPopup.classList.remove('active');
}
});
// Link the second PDF button to the original function
// (Handled above by runPdfExport function refactoring)
document.getElementById('btn-import').addEventListener('click', () => {
const input = document.createElement('input');
input.type = 'file'; input.accept = '.json';
input.onchange = e => {
const file = e.target.files[0];
const reader = new FileReader();
reader.onload = readerEvent => {
try {
const imported = JSON.parse(readerEvent.target.result);
if (imported.variants) {
const merged = [...state.variants];
imported.variants.forEach(v => {
let idx = merged.findIndex(m => m.id === v.id);
if (idx === -1 && v.name && v.name.startsWith("Variante ")) {
idx = merged.findIndex(m => m.name === v.name);
}
if (idx !== -1) {
merged[idx] = { ...merged[idx], ...v };
} else {
merged.push(v);
}
});
state.variants = merged.sort((a, b) => {
const nameA = (a.name || "").toUpperCase();
const nameB = (b.name || "").toUpperCase();
if (nameA < nameB) return -1;
if (nameA > nameB) return 1;
return 0;
});
delete imported.variants;
}
Object.assign(state, imported);
// Force update colors to the new standard
if (state.variants) state.variants.forEach(v => v.color = '#cca300');
updateOwnerLayer(); updateUsageLayer(); updateWEALayer(); updateRouteLayers();
renderOwnerList(); renderVariants();
} catch (err) {
console.error("Import error:", err);
alert("Import fehlgeschlagen: " + err.message);
}
}
reader.readAsText(file, 'UTF-8');
}
input.click();
});
window.addEventListener('load', async () => {
try {
initApp();
if (window.lucide) lucide.createIcons();
const handle = await getHandle();
if (handle) {
state.directoryHandle = handle;
// Check if we still have permission (usually "prompt" after reload)
const status = await handle.queryPermission({ mode: 'readwrite' });
if (status === 'granted') {
updateSyncUI('connected');
await loadFromFolder();
} else {
updateSyncUI('restorable');
console.log("Handle found, but needs permission restoration.");
}
}
} catch (err) {
console.error("Startup failed:", err);
}
});
window.addEventListener('keydown', (e) => {
if (e.key === 'Escape') {
state.isDrawing = false; state.isMeasuring = false;
if (activeMeasure) { activeMeasure.remove(); activeMeasure = null; }
if (tempLine) { tempLine.remove(); tempLine = null; }
updateToolCursors(); renderVariants();
}
});
</script>
</body>
</html>