UI: Add Edit Mode Toggle and Outline Layer
Deploy Bürgerwind / deploy (push) Successful in 17s Details

This commit is contained in:
Johannes Baumeister 2026-05-20 13:44:51 +02:00
parent be2831fcbd
commit 89dfcdff25
2 changed files with 152 additions and 15 deletions

139
app.js
View File

@ -11,7 +11,8 @@ document.addEventListener('DOMContentLoaded', async () => {
bakedData: {}, // Cache for standalone persistence bakedData: {}, // Cache for standalone persistence
ownerMapping: { firstName: 'vorname', lastName: 'nachname' }, // Default for ALKIS and modern Shapefiles ownerMapping: { firstName: 'vorname', lastName: 'nachname' }, // Default for ALKIS and modern Shapefiles
ownerStatuses: {}, // { "name vorname": { status: "...", notiz: "..." } } ownerStatuses: {}, // { "name vorname": { status: "...", notiz: "..." } }
showAuxiliary: true showAuxiliary: true,
isEditMode: false
}; };
const STATUS_MAP = { const STATUS_MAP = {
@ -383,7 +384,7 @@ document.addEventListener('DOMContentLoaded', async () => {
ksfAngle, ksfMirrored, ksfAngle, ksfMirrored,
totalHeight: geoms.totalHeight, totalHeight: geoms.totalHeight,
layers: { layers: {
marker: L.marker(latlng, { draggable: true, icon: turbineIcon }), marker: L.marker(latlng, { draggable: state.isEditMode, icon: turbineIcon }),
sweptArea: L.geoJSON(geoms.sweptArea, { style: { color: '#00c8ff', weight: 1, dashArray: '4, 4', fillOpacity: 0.1 } }), sweptArea: L.geoJSON(geoms.sweptArea, { style: { color: '#00c8ff', weight: 1, dashArray: '4, 4', fillOpacity: 0.1 } }),
techDist: L.geoJSON(geoms.techDist, { style: { color: '#ffcc00', weight: 2, dashArray: '5, 5', fillOpacity: 0 } }), techDist: L.geoJSON(geoms.techDist, { style: { color: '#ffcc00', weight: 2, dashArray: '5, 5', fillOpacity: 0 } }),
techDistSmall: L.geoJSON(geoms.techDistSmall, { style: { color: '#ffcc00', weight: 1.5, dashArray: '2, 4', fillOpacity: 0 } }), techDistSmall: L.geoJSON(geoms.techDistSmall, { style: { color: '#ffcc00', weight: 1.5, dashArray: '2, 4', fillOpacity: 0 } }),
@ -393,7 +394,7 @@ document.addEventListener('DOMContentLoaded', async () => {
blf: L.geoJSON(geoms.blf, { style: { color: '#9b59b6', weight: 1, dashArray: '3, 3', fillOpacity: 0.2 } }), blf: L.geoJSON(geoms.blf, { style: { color: '#9b59b6', weight: 1, dashArray: '3, 3', fillOpacity: 0.2 } }),
mf: L.geoJSON(geoms.mf, { style: { color: '#95a5a6', weight: 1, dashArray: '2, 2', fillOpacity: 0.15 } }), mf: L.geoJSON(geoms.mf, { style: { color: '#95a5a6', weight: 1, dashArray: '2, 2', fillOpacity: 0.15 } }),
rotationHandle: L.marker(latlng, { rotationHandle: L.marker(latlng, {
draggable: true, draggable: state.isEditMode,
icon: L.divIcon({ icon: L.divIcon({
className: 'rotation-handle', className: 'rotation-handle',
html: ` html: `
@ -437,7 +438,9 @@ document.addEventListener('DOMContentLoaded', async () => {
updateLabel(turbine, geoms); updateLabel(turbine, geoms);
// Click to Edit // Click to Edit
turbine.layers.marker.on('click', () => openEditPanel(turbine)); turbine.layers.marker.on('click', () => {
if (state.isEditMode) openEditPanel(turbine);
});
// Drag Update // Drag Update
turbine.layers.marker.on('drag', (e) => { turbine.layers.marker.on('drag', (e) => {
@ -633,6 +636,20 @@ document.addEventListener('DOMContentLoaded', async () => {
}; };
} }
const checkShowOwnersOutline = document.getElementById('checkShowOwnersOutline');
if (checkShowOwnersOutline) {
checkShowOwnersOutline.onchange = () => {
const outlineLayer = overlays["Flurstücke & Eigentümer (Umriss)"];
if (outlineLayer) {
if (checkShowOwnersOutline.checked) {
state.map.addLayer(outlineLayer);
} else {
state.map.removeLayer(outlineLayer);
}
}
};
}
// UTM Creation Logic // UTM Creation Logic
const btnCreateAtUTM = document.getElementById('btnCreateAtUTM'); const btnCreateAtUTM = document.getElementById('btnCreateAtUTM');
const inputUtmE = document.getElementById('utm-e'); const inputUtmE = document.getElementById('utm-e');
@ -1207,6 +1224,59 @@ document.addEventListener('DOMContentLoaded', async () => {
layerControl.addOverlay(layer, layerName); layerControl.addOverlay(layer, layerName);
layer.bringToFront(); // Ensure it's on top of local shapefiles layer.bringToFront(); // Ensure it's on top of local shapefiles
// ---- Erzeuge zusätzlich den Umriss-Layer ----
const outlineLayerName = "Flurstücke & Eigentümer (Umriss)";
const outlineLayer = L.geoJSON(geojson, {
style: () => ({ color: '#000', weight: 1.5, fillOpacity: 0, fillColor: 'transparent' }),
onEachFeature: (feature, outLayer) => {
if (feature.properties) {
const props = feature.properties;
const firstName = (props.vorname || props.VNA || '').trim();
const lastName = (props.nachname || props.GNA || '').trim();
let rawStatus = props.status || 'Kein Status';
let notiz = props.notiz || '';
const matched = getAlkisOwnerStatus(firstName, lastName, state);
if (matched) {
rawStatus = matched.status;
notiz = matched.notiz;
}
const status = (rawStatus && LEGACY_STATUS_MAP[rawStatus.toLowerCase()]) ? LEGACY_STATUS_MAP[rawStatus.toLowerCase()] : (rawStatus || 'Kein Status');
const popup = generateAlkisPopup(props, firstName, lastName, status, notiz, outlineLayerName);
outLayer.bindPopup(popup);
const flur = props.FLN || '-';
let fst = '-';
if (props.ZAE) {
fst = props.NEN ? `${props.ZAE}/${props.NEN}` : props.ZAE;
}
const labelContent = `
<div class="alkis-label-inner">
<span class="owner-name">${lastName}, ${firstName}</span><br>
<span class="parcel-info">Flur ${flur}, Flst. ${fst}</span>
</div>
`;
outLayer.bindTooltip(labelContent, {
permanent: true,
direction: 'center',
className: 'alkis-label',
offset: [0, 0]
});
}
}
});
overlays[outlineLayerName] = outlineLayer;
const checkShowOwnersOutline = document.getElementById('checkShowOwnersOutline');
if (checkShowOwnersOutline && checkShowOwnersOutline.checked) {
state.map.addLayer(outlineLayer);
}
layerControl.addOverlay(outlineLayer, outlineLayerName);
outlineLayer.bringToFront();
} }
// Manual Import & Bundling // Manual Import & Bundling
@ -1912,6 +1982,67 @@ document.addEventListener('DOMContentLoaded', async () => {
}; };
} }
// Initialize Edit Mode Toggle
const btnToggleEditMode = document.getElementById('btnToggleEditMode');
const editModeIcon = document.getElementById('editModeIcon');
const editModeText = document.getElementById('editModeText');
function updateEditModeUI() {
if (state.isEditMode) {
editModeIcon.innerText = '🔓';
editModeText.innerText = 'Bearbeitung aktiv';
btnToggleEditMode.classList.replace('btn-secondary', 'btn-primary');
btnPlaceTurbine.disabled = false;
btnPlaceTurbine.style.opacity = '1';
btnCreateAtUTM.disabled = false;
btnCreateAtUTM.style.opacity = '1';
if (btnManageOwners) {
btnManageOwners.disabled = false;
btnManageOwners.style.opacity = '1';
}
} else {
editModeIcon.innerText = '🔒';
editModeText.innerText = 'Bearbeitung gesperrt';
btnToggleEditMode.classList.replace('btn-primary', 'btn-secondary');
btnPlaceTurbine.disabled = true;
btnPlaceTurbine.style.opacity = '0.5';
btnCreateAtUTM.disabled = true;
btnCreateAtUTM.style.opacity = '0.5';
if (btnManageOwners) {
btnManageOwners.disabled = true;
btnManageOwners.style.opacity = '0.5';
}
closeEditPanel();
if (ownerModal) ownerModal.style.display = 'none';
if (placementMode) {
placementMode = false;
btnPlaceTurbine.classList.remove('active');
state.map.getContainer().style.cursor = '';
state.map.getContainer().classList.remove('placement-active');
}
}
state.turbines.forEach(t => {
if (state.isEditMode) {
t.layers.marker.dragging.enable();
t.layers.rotationHandle.dragging.enable();
} else {
t.layers.marker.dragging.disable();
t.layers.rotationHandle.dragging.disable();
}
});
}
if (btnToggleEditMode) {
btnToggleEditMode.addEventListener('click', () => {
state.isEditMode = !state.isEditMode;
updateEditModeUI();
});
// Set initial state (locked)
updateEditModeUI();
}
updateLegend(); updateLegend();
console.log("WindPlaner initialisiert."); console.log("WindPlaner initialisiert.");
document.getElementById('statusInfo').innerText = "System bereit. Karte geladen."; document.getElementById('statusInfo').innerText = "System bereit. Karte geladen.";

View File

@ -42,6 +42,12 @@
<label style="cursor: pointer; display: flex; align-items: center; gap: 8px; font-size: 0.8rem; color: var(--text-dim);"> <label style="cursor: pointer; display: flex; align-items: center; gap: 8px; font-size: 0.8rem; color: var(--text-dim);">
<input type="checkbox" id="checkShowOwners" checked> Eigentümerzustimmung <input type="checkbox" id="checkShowOwners" checked> Eigentümerzustimmung
</label> </label>
<label style="cursor: pointer; display: flex; align-items: center; gap: 8px; font-size: 0.8rem; color: var(--text-dim);">
<input type="checkbox" id="checkShowOwnersOutline"> Flurstücke (Umriss)
</label>
<button id="btnToggleEditMode" class="btn-secondary btn-mini" style="margin-top: 8px; display: flex; align-items: center; justify-content: center; gap: 5px; padding: 6px;">
<span id="editModeIcon">🔒</span> <span id="editModeText">Bearbeitung gesperrt</span>
</button>
</div> </div>
</div> </div>
@ -98,11 +104,11 @@
<h2 style="margin-bottom: 6px;">UTM-Position</h2> <h2 style="margin-bottom: 6px;">UTM-Position</h2>
<div style="display: flex; gap: 6px; margin-bottom: 6px;"> <div style="display: flex; gap: 6px; margin-bottom: 6px;">
<div style="flex: 1;"> <div style="flex: 1;">
<label class="label-small">Rechts (E)</label> <label for="utm-e" class="label-small">Rechts (E)</label>
<input type="number" id="utm-e" class="input-styled input-small" placeholder="32..." step="0.01"> <input type="number" id="utm-e" class="input-styled input-small" placeholder="32..." step="0.01">
</div> </div>
<div style="flex: 1;"> <div style="flex: 1;">
<label class="label-small">Hoch (N)</label> <label for="utm-n" class="label-small">Hoch (N)</label>
<input type="number" id="utm-n" class="input-styled input-small" placeholder="5..." step="0.01"> <input type="number" id="utm-n" class="input-styled input-small" placeholder="5..." step="0.01">
</div> </div>
</div> </div>
@ -145,11 +151,11 @@
</div> </div>
<div class="edit-rows-container"> <div class="edit-rows-container">
<div class="edit-row"> <div class="edit-row">
<label>Nr:</label> <label for="edit-wea-nr">Nr:</label>
<input type="text" id="edit-wea-nr" class="edit-input"> <input type="text" id="edit-wea-nr" class="edit-input">
</div> </div>
<div class="edit-row separator"> <div class="edit-row separator">
<label>Hersteller:</label> <label for="edit-wea-manufacturer">Hersteller:</label>
<select id="edit-wea-manufacturer" class="edit-input"> <select id="edit-wea-manufacturer" class="edit-input">
<option value="Enercon">Enercon</option> <option value="Enercon">Enercon</option>
<option value="Nordex">Nordex</option> <option value="Nordex">Nordex</option>
@ -158,23 +164,23 @@
</select> </select>
</div> </div>
<div class="edit-row separator"> <div class="edit-row separator">
<label>Typ:</label> <label for="edit-wea-type">Typ:</label>
<input type="text" id="edit-wea-type" class="edit-input"> <input type="text" id="edit-wea-type" class="edit-input">
</div> </div>
<div class="edit-row"> <div class="edit-row">
<label>RD (m):</label> <label for="edit-wea-rd">RD (m):</label>
<input type="number" id="edit-wea-rd" class="edit-input"> <input type="number" id="edit-wea-rd" class="edit-input">
</div> </div>
<div class="edit-row separator"> <div class="edit-row separator">
<label>NH (m):</label> <label for="edit-wea-nh">NH (m):</label>
<input type="number" id="edit-wea-nh" class="edit-input"> <input type="number" id="edit-wea-nh" class="edit-input">
</div> </div>
<div class="edit-row"> <div class="edit-row">
<label>Fund (m):</label> <label for="edit-wea-fr">Fund (m):</label>
<input type="number" id="edit-wea-fr" class="edit-input"> <input type="number" id="edit-wea-fr" class="edit-input">
</div> </div>
<div class="edit-row separator"> <div class="edit-row separator">
<label>Wink (°):</label> <label for="edit-wea-ksf-angle">Wink (°):</label>
<input type="number" id="edit-wea-ksf-angle" class="edit-input" value="0"> <input type="number" id="edit-wea-ksf-angle" class="edit-input" value="0">
</div> </div>
<div class="edit-row" style="margin-top: 5px;"> <div class="edit-row" style="margin-top: 5px;">
@ -215,11 +221,11 @@
<p>Bitte ordnen Sie die Spalten für Namen aus dem Layer "Eigentümer" zu:</p> <p>Bitte ordnen Sie die Spalten für Namen aus dem Layer "Eigentümer" zu:</p>
<div style="display: flex; gap: 15px; justify-content: center; margin-top: 15px;"> <div style="display: flex; gap: 15px; justify-content: center; margin-top: 15px;">
<div> <div>
<label style="display: block; font-size: 0.8rem; margin-bottom: 5px;">Vorname</label> <label for="selectFirstName" style="display: block; font-size: 0.8rem; margin-bottom: 5px;">Vorname</label>
<select id="selectFirstName" class="input-styled" style="width: 150px;"></select> <select id="selectFirstName" class="input-styled" style="width: 150px;"></select>
</div> </div>
<div> <div>
<label style="display: block; font-size: 0.8rem; margin-bottom: 5px;">Nachname</label> <label for="selectLastName" style="display: block; font-size: 0.8rem; margin-bottom: 5px;">Nachname</label>
<select id="selectLastName" class="input-styled" style="width: 150px;"></select> <select id="selectLastName" class="input-styled" style="width: 150px;"></select>
</div> </div>
</div> </div>