Skip to content

Commit 319b315

Browse files
authored
Merge pull request #190 from gergernaut/feat/lagekarte-circle-center-radius-186
feat(lagekarte): Kreis-Mittelpunkt + Radius editierbar (#186)
2 parents eb7c9b1 + 52ec563 commit 319b315

1 file changed

Lines changed: 65 additions & 22 deletions

File tree

‎packages/web/src/lagekarte/Lagekarte.tsx‎

Lines changed: 65 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -253,29 +253,53 @@ function createLayer(
253253
});
254254
}
255255

256+
// Zusatz-Layer (Mittelpunkt-Punkt, Beschriftung) — dem Kreis/der Fläche
257+
// beigelegt, ohne sie selbst zu mutieren (Invariante #1: reiner Render-Pfad).
258+
const extras: L.Layer[] = [];
259+
260+
// Mittelpunkt bei Kreisen (#186): kleiner, gleichfarbiger, nicht-interaktiver
261+
// Punkt am Zentrum — macht das exakte Zentrum sichtbar (radius in Pixeln).
262+
if (feature.shape === "circle" && feature.geometry[0]) {
263+
extras.push(
264+
L.circleMarker(feature.geometry[0], {
265+
radius: 3,
266+
color: feature.color,
267+
fillColor: feature.color,
268+
fillOpacity: 1,
269+
weight: 1,
270+
interactive: false,
271+
}),
272+
);
273+
}
274+
256275
const label = feature.label?.trim();
257-
if (!labelsVisible || !label) return shape;
258-
259-
const labelElement = document.createElement("div");
260-
labelElement.className = "lagekarte-area-label__text";
261-
labelElement.textContent = label;
262-
labelElement.style.backgroundColor = `color-mix(in srgb, ${feature.color} 14%, transparent)`;
263-
labelElement.style.color = `color-mix(in srgb, ${feature.color} 72%, black)`;
264-
labelElement.style.borderColor = feature.color;
265-
const center =
266-
feature.shape === "circle" ? feature.geometry[0] : shape.getBounds().getCenter();
267-
if (!center) return shape;
268-
const labelMarker = L.marker(center, {
269-
icon: L.divIcon({
270-
className: "lagekarte-area-label",
271-
html: labelElement,
272-
iconAnchor: [0, 0],
273-
iconSize: [0, 0],
274-
}),
275-
interactive: false,
276-
keyboard: false,
277-
});
278-
return L.featureGroup([shape, labelMarker]);
276+
if (labelsVisible && label) {
277+
const labelElement = document.createElement("div");
278+
labelElement.className = "lagekarte-area-label__text";
279+
labelElement.textContent = label;
280+
labelElement.style.backgroundColor = `color-mix(in srgb, ${feature.color} 14%, transparent)`;
281+
labelElement.style.color = `color-mix(in srgb, ${feature.color} 72%, black)`;
282+
labelElement.style.borderColor = feature.color;
283+
const center =
284+
feature.shape === "circle" ? feature.geometry[0] : shape.getBounds().getCenter();
285+
if (center) {
286+
extras.push(
287+
L.marker(center, {
288+
icon: L.divIcon({
289+
className: "lagekarte-area-label",
290+
html: labelElement,
291+
iconAnchor: [0, 0],
292+
iconSize: [0, 0],
293+
}),
294+
interactive: false,
295+
keyboard: false,
296+
}),
297+
);
298+
}
299+
}
300+
301+
if (extras.length === 0) return shape;
302+
return L.featureGroup([shape, ...extras]);
279303
}
280304

281305
const tip = buildTooltip(feature);
@@ -332,6 +356,8 @@ export function Lagekarte({
332356
const [areaColor, setAreaColor] = useState("#d5372b");
333357
const [areaOpacity, setAreaOpacity] = useState(0.3);
334358
const [areaDash, setAreaDash] = useState("");
359+
// Radius des ausgewählten Kreises in Metern, im Editor bearbeitbar (#186).
360+
const [areaRadius, setAreaRadius] = useState(0);
335361
const [label, setLabel] = useState("");
336362
const [description, setDescription] = useState("");
337363
// Ausrichtung des ausgewählten Symbols (0–359°, 0 = Norden). Pro Symbol, nicht global
@@ -568,6 +594,7 @@ export function Lagekarte({
568594
setAreaColor(selectedFeature.color);
569595
setAreaOpacity(selectedFeature.opacity);
570596
setAreaDash(selectedFeature.dashArray ?? "");
597+
setAreaRadius(Math.round(selectedFeature.radiusM ?? 0));
571598
} else {
572599
setRotation(selectedFeature.rotation ?? 0);
573600
}
@@ -673,6 +700,9 @@ export function Lagekarte({
673700
color: areaColor,
674701
opacity: areaOpacity,
675702
dashArray: areaDash || "",
703+
// Radius nur bei Kreisen mitschreiben (#186); >0 erzwingen, damit ein
704+
// versehentlich geleertes Feld den Kreis nicht kollabieren lässt.
705+
...(current.shape === "circle" ? { radiusM: Math.max(1, areaRadius) } : {}),
676706
label,
677707
description,
678708
updatedAt,
@@ -1753,6 +1783,19 @@ export function Lagekarte({
17531783
<button type="button" title="Gestrichelt" aria-pressed={areaDash === "5,5"} onClick={() => setAreaDash("5,5")}>╌</button>
17541784
<button type="button" title="Gepunktet" aria-pressed={areaDash === "2,4"} onClick={() => setAreaDash("2,4")}>┄</button>
17551785
</fieldset>
1786+
{selectedFeature.shape === "circle" && (
1787+
<label className="lagekarte-field">
1788+
<span>Radius {formatDistance(Math.max(1, areaRadius))}</span>
1789+
<input
1790+
type="number"
1791+
min="1"
1792+
step="10"
1793+
value={areaRadius}
1794+
aria-label="Radius in Metern"
1795+
onChange={(event) => setAreaRadius(Math.max(0, Number(event.target.value)))}
1796+
/>
1797+
</label>
1798+
)}
17561799
</>
17571800
)}
17581801
{selectedFeature.kind === "symbol" && (

0 commit comments

Comments
 (0)