leaflet-anvil 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs ADDED
@@ -0,0 +1,2054 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
3
+ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
4
+
5
+ // src/index.ts
6
+ import * as L22 from "leaflet";
7
+
8
+ // src/events.ts
9
+ var ANVIL_EVENTS = {
10
+ CREATED: "anvil:created",
11
+ EDITED: "anvil:edited",
12
+ DELETED: "anvil:deleted",
13
+ SPLIT: "anvil:split",
14
+ UNION: "anvil:union",
15
+ SUBTRACT: "anvil:subtract",
16
+ MODE_CHANGE: "anvil:modechange"
17
+ };
18
+
19
+ // src/modes/mode-manager.ts
20
+ var ModeManager = class {
21
+ constructor(map) {
22
+ this.map = map;
23
+ __publicField(this, "currentMode", null);
24
+ __publicField(this, "modes", /* @__PURE__ */ new Map());
25
+ }
26
+ addMode(name, mode) {
27
+ this.modes.set(name, mode);
28
+ }
29
+ enable(name) {
30
+ const mode = this.modes.get(name);
31
+ if (!mode) {
32
+ console.warn(`Anvil ModeManager: Mode "${name}" not found.`);
33
+ return;
34
+ }
35
+ if (this.currentMode === mode) return;
36
+ this.disable();
37
+ this.currentMode = mode;
38
+ this.currentMode.enable();
39
+ this.map.fire(ANVIL_EVENTS.MODE_CHANGE, { mode: name });
40
+ }
41
+ disable() {
42
+ if (this.currentMode) {
43
+ this.currentMode.disable();
44
+ this.currentMode = null;
45
+ this.map.fire(ANVIL_EVENTS.MODE_CHANGE, { mode: null });
46
+ }
47
+ }
48
+ };
49
+
50
+ // src/modes/draw-polygon-mode.ts
51
+ import * as L3 from "leaflet";
52
+
53
+ // src/utils/snapping.ts
54
+ import * as L from "leaflet";
55
+ function getSnapLatLng(map, latlng, store, options, additionalPoints = [], skipLayer) {
56
+ if (!options.snapping) return latlng;
57
+ const snapDistance = options.snapDistance || 10;
58
+ const basePoint = map.latLngToContainerPoint(latlng);
59
+ let closestPoint = null;
60
+ let minDistance = snapDistance;
61
+ const skipLayers = Array.isArray(skipLayer) ? skipLayer : skipLayer ? [skipLayer] : [];
62
+ const bounds = map.getBounds().pad(0.1);
63
+ store.getGroup().eachLayer((layer) => {
64
+ if (skipLayers.includes(layer)) return;
65
+ if (layer.getBounds && !bounds.intersects(layer.getBounds())) return;
66
+ const pointsToCheck = [];
67
+ if (layer instanceof L.Marker || layer instanceof L.CircleMarker || layer instanceof L.Circle) {
68
+ pointsToCheck.push(layer.getLatLng());
69
+ } else if (layer instanceof L.Polyline) {
70
+ const latlngs = layer.getLatLngs();
71
+ const flatten5 = (arr) => {
72
+ let result = [];
73
+ arr.forEach((item) => {
74
+ if (Array.isArray(item)) {
75
+ result = result.concat(flatten5(item));
76
+ } else if (item instanceof L.LatLng) {
77
+ result.push(item);
78
+ }
79
+ });
80
+ return result;
81
+ };
82
+ pointsToCheck.push(...flatten5(latlngs));
83
+ }
84
+ pointsToCheck.forEach((p) => {
85
+ const containerPoint = map.latLngToContainerPoint(p);
86
+ const dist = basePoint.distanceTo(containerPoint);
87
+ if (dist < minDistance) {
88
+ minDistance = dist;
89
+ closestPoint = p;
90
+ }
91
+ });
92
+ });
93
+ additionalPoints.forEach((p) => {
94
+ const containerPoint = map.latLngToContainerPoint(p);
95
+ const dist = basePoint.distanceTo(containerPoint);
96
+ if (dist < minDistance) {
97
+ minDistance = dist;
98
+ closestPoint = p;
99
+ }
100
+ });
101
+ return closestPoint || latlng;
102
+ }
103
+
104
+ // src/utils/geometry.ts
105
+ import * as L2 from "leaflet";
106
+ function segmentsIntersect(a, b, c, d) {
107
+ const crossProduct = (p1, p2, p3) => {
108
+ return (p2.y - p1.y) * (p3.x - p2.x) - (p2.x - p1.x) * (p3.y - p2.y);
109
+ };
110
+ const cp1 = crossProduct(a, b, c);
111
+ const cp2 = crossProduct(a, b, d);
112
+ const cp3 = crossProduct(c, d, a);
113
+ const cp4 = crossProduct(c, d, b);
114
+ if ((cp1 > 0 && cp2 < 0 || cp1 < 0 && cp2 > 0) && (cp3 > 0 && cp4 < 0 || cp3 < 0 && cp4 > 0)) {
115
+ return true;
116
+ }
117
+ return false;
118
+ }
119
+ function causesSelfIntersection(map, points, nextPoint, isClosing = false) {
120
+ if (points.length < 2) return false;
121
+ const p2 = map.latLngToLayerPoint(nextPoint);
122
+ const p1 = map.latLngToLayerPoint(points[points.length - 1]);
123
+ const limit = points.length - 2;
124
+ for (let i = 0; i < limit; i++) {
125
+ const a = map.latLngToLayerPoint(points[i]);
126
+ const b = map.latLngToLayerPoint(points[i + 1]);
127
+ if (segmentsIntersect(p1, p2, a, b)) {
128
+ return true;
129
+ }
130
+ }
131
+ if (isClosing) {
132
+ }
133
+ return false;
134
+ }
135
+ function isSelfIntersecting(map, latlngs, isPolygon) {
136
+ const flatten5 = (arr) => {
137
+ if (!Array.isArray(arr)) return [];
138
+ if (arr[0] instanceof L2.LatLng) return arr;
139
+ return arr.reduce((acc, val) => acc.concat(flatten5(val)), []);
140
+ };
141
+ const points = flatten5(latlngs);
142
+ if (points.length < 4) return false;
143
+ const projectedPoints = points.map((p) => map.latLngToLayerPoint(p));
144
+ const len = projectedPoints.length;
145
+ for (let i = 0; i < len; i++) {
146
+ const a = projectedPoints[i];
147
+ const b = projectedPoints[(i + 1) % len];
148
+ if (!isPolygon && i === len - 1) break;
149
+ for (let j = i + 2; j < len; j++) {
150
+ if (isPolygon && (j + 1) % len === i) continue;
151
+ if (!isPolygon && j === len - 1) break;
152
+ const c = projectedPoints[j];
153
+ const d = projectedPoints[(j + 1) % len];
154
+ if (segmentsIntersect(a, b, c, d)) return true;
155
+ }
156
+ }
157
+ return false;
158
+ }
159
+
160
+ // src/modes/draw-polygon-mode.ts
161
+ var DrawPolygonMode = class {
162
+ constructor(map, options = {}, store) {
163
+ this.map = map;
164
+ this.options = options;
165
+ this.store = store;
166
+ __publicField(this, "points", []);
167
+ __publicField(this, "markers", []);
168
+ __publicField(this, "polyline", null);
169
+ __publicField(this, "ghostLine", null);
170
+ }
171
+ enable() {
172
+ this.map.on("click", this.onClick, this);
173
+ this.map.on("mousemove", this.onMouseMove, this);
174
+ L3.DomEvent.on(window, "keydown", this.onKeyDown, this);
175
+ this.map.getContainer().style.cursor = "crosshair";
176
+ }
177
+ disable() {
178
+ this.map.off("click", this.onClick, this);
179
+ this.map.off("mousemove", this.onMouseMove, this);
180
+ L3.DomEvent.off(window, "keydown", this.onKeyDown, this);
181
+ this.map.getContainer().style.cursor = "";
182
+ this.reset();
183
+ }
184
+ onClick(e) {
185
+ const latlng = this.store ? getSnapLatLng(this.map, e.latlng, this.store, this.options, this.points) : e.latlng;
186
+ if (this.points.length > 0 && this.isFirstPoint(latlng)) {
187
+ if (this.options.preventSelfIntersection && causesSelfIntersection(this.map, this.points, this.points[0])) {
188
+ return;
189
+ }
190
+ this.finish();
191
+ return;
192
+ }
193
+ if (this.options.preventSelfIntersection && causesSelfIntersection(this.map, this.points, latlng)) {
194
+ return;
195
+ }
196
+ this.points.push(latlng);
197
+ this.updateDrawing();
198
+ }
199
+ isFirstPoint(latlng) {
200
+ if (this.points.length < 3) return false;
201
+ const firstPoint = this.points[0];
202
+ if (latlng.equals(firstPoint)) return true;
203
+ const containerPoint = this.map.latLngToContainerPoint(latlng);
204
+ const firstContainerPoint = this.map.latLngToContainerPoint(firstPoint);
205
+ return containerPoint.distanceTo(firstContainerPoint) < (this.options.snapDistance || 10);
206
+ }
207
+ onMouseMove(e) {
208
+ if (this.points.length === 0) return;
209
+ const snapLatLng = this.store ? getSnapLatLng(this.map, e.latlng, this.store, this.options, this.points) : e.latlng;
210
+ const lastPoint = this.points[this.points.length - 1];
211
+ if (!this.ghostLine) {
212
+ this.ghostLine = L3.polyline([lastPoint, snapLatLng], {
213
+ dashArray: "5, 5",
214
+ color: "#3388ff",
215
+ weight: 2
216
+ }).addTo(this.map);
217
+ } else {
218
+ this.ghostLine.setLatLngs([lastPoint, snapLatLng]);
219
+ }
220
+ }
221
+ onKeyDown(e) {
222
+ if (e.key === "Escape") {
223
+ this.reset();
224
+ }
225
+ }
226
+ updateDrawing() {
227
+ if (this.polyline) {
228
+ this.polyline.setLatLngs(this.points);
229
+ } else {
230
+ this.polyline = L3.polyline(this.points, { color: "#3388ff" }).addTo(this.map);
231
+ }
232
+ if (this.points.length === 1 && this.markers.length === 0) {
233
+ const marker4 = L3.circleMarker(this.points[0], {
234
+ radius: 5,
235
+ fillColor: "#fff",
236
+ fillOpacity: 1,
237
+ color: "#3388ff",
238
+ weight: 2
239
+ }).addTo(this.map);
240
+ marker4.on("click", (e) => {
241
+ L3.DomEvent.stopPropagation(e);
242
+ this.finish();
243
+ });
244
+ this.markers.push(marker4);
245
+ }
246
+ }
247
+ finish() {
248
+ if (this.points.length < 3) return;
249
+ const polygon7 = L3.polygon(this.points).addTo(this.map);
250
+ this.map.fire(ANVIL_EVENTS.CREATED, { layer: polygon7 });
251
+ this.reset();
252
+ }
253
+ reset() {
254
+ if (this.polyline) this.map.removeLayer(this.polyline);
255
+ if (this.ghostLine) this.map.removeLayer(this.ghostLine);
256
+ this.markers.forEach((m) => this.map.removeLayer(m));
257
+ this.points = [];
258
+ this.markers = [];
259
+ this.polyline = null;
260
+ this.ghostLine = null;
261
+ }
262
+ };
263
+
264
+ // src/modes/draw-polyline-mode.ts
265
+ import * as L4 from "leaflet";
266
+ var DrawPolylineMode = class {
267
+ constructor(map, options = {}, store) {
268
+ this.map = map;
269
+ this.options = options;
270
+ this.store = store;
271
+ __publicField(this, "points", []);
272
+ __publicField(this, "polyline", null);
273
+ __publicField(this, "ghostLine", null);
274
+ }
275
+ enable() {
276
+ this.map.on("click", this.onClick, this);
277
+ this.map.on("mousemove", this.onMouseMove, this);
278
+ this.map.on("dblclick", this.onDoubleClick, this);
279
+ L4.DomEvent.on(window, "keydown", this.onKeyDown, this);
280
+ this.map.doubleClickZoom.disable();
281
+ this.map.getContainer().style.cursor = "crosshair";
282
+ }
283
+ disable() {
284
+ this.map.off("click", this.onClick, this);
285
+ this.map.off("mousemove", this.onMouseMove, this);
286
+ this.map.off("dblclick", this.onDoubleClick, this);
287
+ L4.DomEvent.off(window, "keydown", this.onKeyDown, this);
288
+ this.map.doubleClickZoom.enable();
289
+ this.map.getContainer().style.cursor = "";
290
+ this.reset();
291
+ }
292
+ onClick(e) {
293
+ const latlng = this.store ? getSnapLatLng(this.map, e.latlng, this.store, this.options, this.points) : e.latlng;
294
+ if (this.options.preventSelfIntersection && causesSelfIntersection(this.map, this.points, latlng)) {
295
+ return;
296
+ }
297
+ this.points.push(latlng);
298
+ this.updateDrawing();
299
+ }
300
+ onMouseMove(e) {
301
+ if (this.points.length === 0) return;
302
+ const snapLatLng = this.store ? getSnapLatLng(this.map, e.latlng, this.store, this.options, this.points) : e.latlng;
303
+ const lastPoint = this.points[this.points.length - 1];
304
+ if (!this.ghostLine) {
305
+ this.ghostLine = L4.polyline([lastPoint, e.latlng], {
306
+ dashArray: "5, 5",
307
+ color: "#3388ff",
308
+ weight: 2
309
+ }).addTo(this.map);
310
+ } else {
311
+ this.ghostLine.setLatLngs([lastPoint, snapLatLng]);
312
+ }
313
+ }
314
+ onDoubleClick(e) {
315
+ L4.DomEvent.stopPropagation(e);
316
+ this.finish();
317
+ }
318
+ onKeyDown(e) {
319
+ if (e.key === "Escape") {
320
+ this.reset();
321
+ } else if (e.key === "Enter") {
322
+ this.finish();
323
+ }
324
+ }
325
+ updateDrawing() {
326
+ if (this.polyline) {
327
+ this.polyline.setLatLngs(this.points);
328
+ } else {
329
+ this.polyline = L4.polyline(this.points, { color: "#3388ff" }).addTo(this.map);
330
+ }
331
+ }
332
+ finish() {
333
+ if (this.points.length < 2) return;
334
+ const polyline6 = L4.polyline(this.points).addTo(this.map);
335
+ this.map.fire(ANVIL_EVENTS.CREATED, { layer: polyline6 });
336
+ this.reset();
337
+ }
338
+ reset() {
339
+ if (this.polyline) this.map.removeLayer(this.polyline);
340
+ if (this.ghostLine) this.map.removeLayer(this.ghostLine);
341
+ this.points = [];
342
+ this.polyline = null;
343
+ this.ghostLine = null;
344
+ }
345
+ };
346
+
347
+ // src/modes/draw-marker-mode.ts
348
+ import * as L5 from "leaflet";
349
+ var DrawMarkerMode = class {
350
+ constructor(map, options = {}, store) {
351
+ this.map = map;
352
+ this.options = options;
353
+ this.store = store;
354
+ }
355
+ enable() {
356
+ this.map.on("click", this.onClick, this);
357
+ this.map.getContainer().style.cursor = "crosshair";
358
+ }
359
+ disable() {
360
+ this.map.off("click", this.onClick, this);
361
+ this.map.getContainer().style.cursor = "";
362
+ }
363
+ onClick(e) {
364
+ const latlng = this.store ? getSnapLatLng(this.map, e.latlng, this.store, this.options) : e.latlng;
365
+ const marker4 = L5.marker(latlng).addTo(this.map);
366
+ this.map.fire(ANVIL_EVENTS.CREATED, { layer: marker4 });
367
+ }
368
+ };
369
+
370
+ // src/modes/draw-rectangle-mode.ts
371
+ import * as L6 from "leaflet";
372
+ var DrawRectangleMode = class {
373
+ constructor(map, options = {}, store) {
374
+ this.map = map;
375
+ this.options = options;
376
+ this.store = store;
377
+ __publicField(this, "rectangle", null);
378
+ __publicField(this, "startLatLng", null);
379
+ }
380
+ enable() {
381
+ this.map.on("mousedown", this.onMouseDown, this);
382
+ this.map.on("mousemove", this.onMouseMove, this);
383
+ this.map.on("mouseup", this.onMouseUp, this);
384
+ this.map.dragging.disable();
385
+ this.map.getContainer().style.cursor = "crosshair";
386
+ }
387
+ disable() {
388
+ this.map.off("mousedown", this.onMouseDown, this);
389
+ this.map.off("mousemove", this.onMouseMove, this);
390
+ this.map.off("mouseup", this.onMouseUp, this);
391
+ this.map.dragging.enable();
392
+ this.map.getContainer().style.cursor = "";
393
+ this.reset();
394
+ }
395
+ onMouseDown(e) {
396
+ this.startLatLng = this.store ? getSnapLatLng(this.map, e.latlng, this.store, this.options) : e.latlng;
397
+ }
398
+ onMouseMove(e) {
399
+ if (!this.startLatLng) return;
400
+ const currentLatLng = this.store ? getSnapLatLng(this.map, e.latlng, this.store, this.options) : e.latlng;
401
+ const bounds = L6.latLngBounds(this.startLatLng, currentLatLng);
402
+ if (!this.rectangle) {
403
+ this.rectangle = L6.rectangle(bounds, { color: "#3388ff", weight: 2 }).addTo(this.map);
404
+ } else {
405
+ this.rectangle.setBounds(bounds);
406
+ }
407
+ }
408
+ onMouseUp(e) {
409
+ if (!this.startLatLng) return;
410
+ const currentLatLng = this.store ? getSnapLatLng(this.map, e.latlng, this.store, this.options) : e.latlng;
411
+ const bounds = L6.latLngBounds(this.startLatLng, currentLatLng);
412
+ const rectangle3 = L6.rectangle(bounds).addTo(this.map);
413
+ this.map.fire(ANVIL_EVENTS.CREATED, { layer: rectangle3 });
414
+ this.reset();
415
+ }
416
+ reset() {
417
+ if (this.rectangle) this.map.removeLayer(this.rectangle);
418
+ this.rectangle = null;
419
+ this.startLatLng = null;
420
+ }
421
+ };
422
+
423
+ // src/modes/draw-square-mode.ts
424
+ import * as L7 from "leaflet";
425
+ var DrawSquareMode = class {
426
+ constructor(map, options = {}, store) {
427
+ this.map = map;
428
+ this.options = options;
429
+ this.store = store;
430
+ __publicField(this, "rectangle", null);
431
+ __publicField(this, "startLatLng", null);
432
+ }
433
+ enable() {
434
+ this.map.on("mousedown", this.onMouseDown, this);
435
+ this.map.on("mousemove", this.onMouseMove, this);
436
+ this.map.on("mouseup", this.onMouseUp, this);
437
+ this.map.dragging.disable();
438
+ this.map.getContainer().style.cursor = "crosshair";
439
+ }
440
+ disable() {
441
+ this.map.off("mousedown", this.onMouseDown, this);
442
+ this.map.off("mousemove", this.onMouseMove, this);
443
+ this.map.off("mouseup", this.onMouseUp, this);
444
+ this.map.dragging.enable();
445
+ this.map.getContainer().style.cursor = "";
446
+ this.reset();
447
+ }
448
+ onMouseDown(e) {
449
+ this.startLatLng = this.store ? getSnapLatLng(this.map, e.latlng, this.store, this.options) : e.latlng;
450
+ }
451
+ onMouseMove(e) {
452
+ if (!this.startLatLng) return;
453
+ const currentLatLng = this.store ? getSnapLatLng(this.map, e.latlng, this.store, this.options) : e.latlng;
454
+ const squareBounds = this.getSquareBounds(this.startLatLng, currentLatLng);
455
+ if (!this.rectangle) {
456
+ this.rectangle = L7.rectangle(squareBounds, { color: "#3388ff", weight: 2 }).addTo(this.map);
457
+ } else {
458
+ this.rectangle.setBounds(squareBounds);
459
+ }
460
+ }
461
+ onMouseUp(e) {
462
+ if (!this.startLatLng) return;
463
+ const currentLatLng = this.store ? getSnapLatLng(this.map, e.latlng, this.store, this.options) : e.latlng;
464
+ const squareBounds = this.getSquareBounds(this.startLatLng, currentLatLng);
465
+ const polygon7 = L7.polygon([
466
+ squareBounds.getNorthWest(),
467
+ squareBounds.getNorthEast(),
468
+ squareBounds.getSouthEast(),
469
+ squareBounds.getSouthWest()
470
+ ]).addTo(this.map);
471
+ this.map.fire(ANVIL_EVENTS.CREATED, { layer: polygon7 });
472
+ this.reset();
473
+ }
474
+ getSquareBounds(start, end) {
475
+ const startPoint = this.map.latLngToContainerPoint(start);
476
+ const endPoint = this.map.latLngToContainerPoint(end);
477
+ const dx = endPoint.x - startPoint.x;
478
+ const dy = endPoint.y - startPoint.y;
479
+ const side = Math.max(Math.abs(dx), Math.abs(dy));
480
+ const newEndPoint = L7.point(
481
+ startPoint.x + side * Math.sign(dx),
482
+ startPoint.y + side * Math.sign(dy)
483
+ );
484
+ return L7.latLngBounds(start, this.map.containerPointToLatLng(newEndPoint));
485
+ }
486
+ reset() {
487
+ if (this.rectangle) this.map.removeLayer(this.rectangle);
488
+ this.rectangle = null;
489
+ this.startLatLng = null;
490
+ }
491
+ };
492
+
493
+ // src/modes/draw-triangle-mode.ts
494
+ import * as L8 from "leaflet";
495
+ var DrawTriangleMode = class {
496
+ constructor(map, options = {}, store) {
497
+ this.map = map;
498
+ this.options = options;
499
+ this.store = store;
500
+ __publicField(this, "ghostTriangle", null);
501
+ __publicField(this, "startLatLng", null);
502
+ }
503
+ enable() {
504
+ this.map.on("mousedown", this.onMouseDown, this);
505
+ this.map.on("mousemove", this.onMouseMove, this);
506
+ this.map.on("mouseup", this.onMouseUp, this);
507
+ this.map.dragging.disable();
508
+ this.map.getContainer().style.cursor = "crosshair";
509
+ }
510
+ disable() {
511
+ this.map.off("mousedown", this.onMouseDown, this);
512
+ this.map.off("mousemove", this.onMouseMove, this);
513
+ this.map.off("mouseup", this.onMouseUp, this);
514
+ this.map.dragging.enable();
515
+ this.map.getContainer().style.cursor = "";
516
+ this.reset();
517
+ }
518
+ onMouseDown(e) {
519
+ this.startLatLng = this.store ? getSnapLatLng(this.map, e.latlng, this.store, this.options) : e.latlng;
520
+ }
521
+ onMouseMove(e) {
522
+ if (!this.startLatLng) return;
523
+ const currentLatLng = this.store ? getSnapLatLng(this.map, e.latlng, this.store, this.options) : e.latlng;
524
+ const trianglePoints = this.getTrianglePoints(this.startLatLng, currentLatLng);
525
+ if (!this.ghostTriangle) {
526
+ this.ghostTriangle = L8.polygon(trianglePoints, { color: "#3388ff", weight: 2 }).addTo(this.map);
527
+ } else {
528
+ this.ghostTriangle.setLatLngs(trianglePoints);
529
+ }
530
+ }
531
+ onMouseUp(e) {
532
+ if (!this.startLatLng) return;
533
+ const currentLatLng = this.store ? getSnapLatLng(this.map, e.latlng, this.store, this.options) : e.latlng;
534
+ const trianglePoints = this.getTrianglePoints(this.startLatLng, currentLatLng);
535
+ const polygon7 = L8.polygon(trianglePoints).addTo(this.map);
536
+ this.map.fire(ANVIL_EVENTS.CREATED, { layer: polygon7 });
537
+ this.reset();
538
+ }
539
+ getTrianglePoints(start, end) {
540
+ const midLng = (start.lng + end.lng) / 2;
541
+ const tip = L8.latLng(end.lat, midLng);
542
+ const baseLeft = L8.latLng(start.lat, start.lng);
543
+ const baseRight = L8.latLng(start.lat, end.lng);
544
+ return [
545
+ tip,
546
+ baseRight,
547
+ baseLeft
548
+ ];
549
+ }
550
+ reset() {
551
+ if (this.ghostTriangle) this.map.removeLayer(this.ghostTriangle);
552
+ this.ghostTriangle = null;
553
+ this.startLatLng = null;
554
+ }
555
+ };
556
+
557
+ // src/modes/draw-circle-mode.ts
558
+ import * as L9 from "leaflet";
559
+ var DrawCircleMode = class {
560
+ constructor(map, options = {}, store) {
561
+ this.map = map;
562
+ this.options = options;
563
+ this.store = store;
564
+ __publicField(this, "circle", null);
565
+ __publicField(this, "centerLatLng", null);
566
+ }
567
+ enable() {
568
+ this.map.on("mousedown", this.onMouseDown, this);
569
+ this.map.on("mousemove", this.onMouseMove, this);
570
+ this.map.on("mouseup", this.onMouseUp, this);
571
+ this.map.dragging.disable();
572
+ this.map.getContainer().style.cursor = "crosshair";
573
+ }
574
+ disable() {
575
+ this.map.off("mousedown", this.onMouseDown, this);
576
+ this.map.off("mousemove", this.onMouseMove, this);
577
+ this.map.off("mouseup", this.onMouseUp, this);
578
+ this.map.dragging.enable();
579
+ this.map.getContainer().style.cursor = "";
580
+ this.reset();
581
+ }
582
+ onMouseDown(e) {
583
+ this.centerLatLng = this.store ? getSnapLatLng(this.map, e.latlng, this.store, this.options) : e.latlng;
584
+ }
585
+ onMouseMove(e) {
586
+ if (!this.centerLatLng) return;
587
+ const currentLatLng = this.store ? getSnapLatLng(this.map, e.latlng, this.store, this.options) : e.latlng;
588
+ const radius = this.map.distance(this.centerLatLng, currentLatLng);
589
+ if (!this.circle) {
590
+ this.circle = L9.circle(this.centerLatLng, { radius, color: "#3388ff", weight: 2 }).addTo(this.map);
591
+ } else {
592
+ this.circle.setRadius(radius);
593
+ }
594
+ }
595
+ onMouseUp(e) {
596
+ if (!this.centerLatLng) return;
597
+ const currentLatLng = this.store ? getSnapLatLng(this.map, e.latlng, this.store, this.options) : e.latlng;
598
+ const radius = this.map.distance(this.centerLatLng, currentLatLng);
599
+ const circle2 = L9.circle(this.centerLatLng, { radius }).addTo(this.map);
600
+ this.map.fire(ANVIL_EVENTS.CREATED, { layer: circle2 });
601
+ this.reset();
602
+ }
603
+ reset() {
604
+ if (this.circle) this.map.removeLayer(this.circle);
605
+ this.circle = null;
606
+ this.centerLatLng = null;
607
+ }
608
+ };
609
+
610
+ // src/modes/freehand-mode.ts
611
+ import * as L10 from "leaflet";
612
+ import * as turf from "@turf/turf";
613
+ var FreehandMode = class {
614
+ constructor(map, _store, options = {}) {
615
+ this.map = map;
616
+ this.options = options;
617
+ __publicField(this, "points", []);
618
+ __publicField(this, "polyline", null);
619
+ __publicField(this, "isDrawing", false);
620
+ }
621
+ enable() {
622
+ this.map.dragging.disable();
623
+ this.map.on("mousedown", this.onMouseDown, this);
624
+ this.map.on("mousemove", this.onMouseMove, this);
625
+ this.map.on("mouseup", this.onMouseUp, this);
626
+ L10.DomEvent.on(window, "keydown", this.onKeyDown, this);
627
+ this.map.getContainer().style.cursor = "crosshair";
628
+ }
629
+ disable() {
630
+ this.map.dragging.enable();
631
+ this.map.off("mousedown", this.onMouseDown, this);
632
+ this.map.off("mousemove", this.onMouseMove, this);
633
+ this.map.off("mouseup", this.onMouseUp, this);
634
+ L10.DomEvent.off(window, "keydown", this.onKeyDown, this);
635
+ this.map.getContainer().style.cursor = "";
636
+ this.resetDrawing();
637
+ }
638
+ onMouseDown(e) {
639
+ this.isDrawing = true;
640
+ this.points = [e.latlng];
641
+ this.updateDrawing();
642
+ }
643
+ onMouseMove(e) {
644
+ if (!this.isDrawing) return;
645
+ const lastPoint = this.points[this.points.length - 1];
646
+ if (lastPoint.distanceTo(e.latlng) > 5) {
647
+ this.points.push(e.latlng);
648
+ this.updateDrawing();
649
+ }
650
+ }
651
+ onMouseUp() {
652
+ if (!this.isDrawing) return;
653
+ this.finish();
654
+ }
655
+ onKeyDown(e) {
656
+ if (e.key === "Escape") {
657
+ this.resetDrawing();
658
+ }
659
+ }
660
+ updateDrawing() {
661
+ if (this.polyline) {
662
+ this.polyline.setLatLngs(this.points);
663
+ } else {
664
+ this.polyline = L10.polyline(this.points, {
665
+ color: "#3388ff",
666
+ weight: 3,
667
+ opacity: 0.8,
668
+ dashArray: "5, 5"
669
+ }).addTo(this.map);
670
+ }
671
+ }
672
+ finish() {
673
+ if (this.points.length < 3) {
674
+ this.resetDrawing();
675
+ return;
676
+ }
677
+ const lineGeo = L10.polyline(this.points).toGeoJSON();
678
+ const tolerance = this.options.freehandTolerance || 1e-5;
679
+ const simplified = turf.simplify(lineGeo, { tolerance, highQuality: true });
680
+ const finalCoords = turf.getCoords(simplified);
681
+ if (finalCoords.length > 0 && (finalCoords[0][0] !== finalCoords[finalCoords.length - 1][0] || finalCoords[0][1] !== finalCoords[finalCoords.length - 1][1])) {
682
+ finalCoords.push(finalCoords[0]);
683
+ }
684
+ const finalPolygon = L10.polygon(finalCoords.map((c) => [c[1], c[0]])).addTo(this.map);
685
+ this.map.fire(ANVIL_EVENTS.CREATED, { layer: finalPolygon });
686
+ this.resetDrawing();
687
+ }
688
+ resetDrawing() {
689
+ if (this.polyline) this.map.removeLayer(this.polyline);
690
+ this.polyline = null;
691
+ this.points = [];
692
+ this.isDrawing = false;
693
+ }
694
+ };
695
+
696
+ // src/modes/cut-mode.ts
697
+ import * as L11 from "leaflet";
698
+ import * as turf2 from "@turf/turf";
699
+ var CutMode = class {
700
+ constructor(map, store, options = {}) {
701
+ this.map = map;
702
+ this.store = store;
703
+ this.options = options;
704
+ __publicField(this, "points", []);
705
+ __publicField(this, "markers", []);
706
+ __publicField(this, "polyline", null);
707
+ __publicField(this, "ghostLine", null);
708
+ }
709
+ enable() {
710
+ this.map.on("click", this.onMapClick, this);
711
+ this.map.on("mousemove", this.onMouseMove, this);
712
+ L11.DomEvent.on(window, "keydown", this.onKeyDown, this);
713
+ this.map.getContainer().style.cursor = "crosshair";
714
+ }
715
+ disable() {
716
+ this.map.off("click", this.onMapClick, this);
717
+ this.map.off("mousemove", this.onMouseMove, this);
718
+ L11.DomEvent.off(window, "keydown", this.onKeyDown, this);
719
+ this.resetDrawing();
720
+ this.map.getContainer().style.cursor = "";
721
+ }
722
+ onMapClick(e) {
723
+ const latlng = getSnapLatLng(this.map, e.latlng, this.store, this.options, this.points);
724
+ if (this.points.length > 0 && this.isFirstPoint(latlng)) {
725
+ this.finish();
726
+ return;
727
+ }
728
+ this.points.push(latlng);
729
+ this.updateDrawing();
730
+ }
731
+ isFirstPoint(latlng) {
732
+ if (this.points.length < 3) return false;
733
+ const firstPoint = this.points[0];
734
+ const containerPoint = this.map.latLngToContainerPoint(latlng);
735
+ const firstContainerPoint = this.map.latLngToContainerPoint(firstPoint);
736
+ return containerPoint.distanceTo(firstContainerPoint) < 15;
737
+ }
738
+ onMouseMove(e) {
739
+ if (this.points.length === 0) return;
740
+ const snapLatLng = getSnapLatLng(this.map, e.latlng, this.store, this.options, this.points);
741
+ const lastPoint = this.points[this.points.length - 1];
742
+ if (!this.ghostLine) {
743
+ this.ghostLine = L11.polyline([lastPoint, snapLatLng], {
744
+ dashArray: "5, 5",
745
+ color: "#ff0000",
746
+ weight: 2
747
+ }).addTo(this.map);
748
+ } else {
749
+ this.ghostLine.setLatLngs([lastPoint, snapLatLng]);
750
+ }
751
+ }
752
+ onKeyDown(e) {
753
+ if (e.key === "Escape") {
754
+ this.resetDrawing();
755
+ }
756
+ }
757
+ updateDrawing() {
758
+ if (this.polyline) {
759
+ this.polyline.setLatLngs(this.points);
760
+ } else {
761
+ this.polyline = L11.polyline(this.points, { color: "#ff0000" }).addTo(this.map);
762
+ }
763
+ if (this.points.length === 1 && this.markers.length === 0) {
764
+ const marker4 = L11.circleMarker(this.points[0], {
765
+ radius: 6,
766
+ fillColor: "#fff",
767
+ fillOpacity: 1,
768
+ color: "#ff0000",
769
+ weight: 2
770
+ }).addTo(this.map);
771
+ marker4.on("click", (e) => {
772
+ L11.DomEvent.stopPropagation(e);
773
+ if (this.points.length >= 3) this.finish();
774
+ });
775
+ this.markers.push(marker4);
776
+ }
777
+ }
778
+ finish() {
779
+ if (this.points.length < 3) return;
780
+ const holeGeo = L11.polygon(this.points).toGeoJSON();
781
+ const intersectedLayers = [];
782
+ this.store.getGroup().eachLayer((layer) => {
783
+ if (layer instanceof L11.Polygon) {
784
+ const polyGeo = layer.toGeoJSON();
785
+ if (turf2.booleanIntersects(polyGeo, holeGeo)) {
786
+ intersectedLayers.push(layer);
787
+ }
788
+ }
789
+ });
790
+ if (intersectedLayers.length === 0) {
791
+ this.resetDrawing();
792
+ return;
793
+ }
794
+ intersectedLayers.forEach((polygon7) => {
795
+ const polyGeo = polygon7.toGeoJSON();
796
+ const result = turf2.difference(turf2.featureCollection([polyGeo, holeGeo]));
797
+ if (result) {
798
+ this.map.fire(ANVIL_EVENTS.DELETED, { layer: polygon7 });
799
+ this.map.removeLayer(polygon7);
800
+ const flattened = turf2.flatten(result);
801
+ flattened.features.forEach((f) => {
802
+ const l = L11.geoJSON(f).getLayers()[0];
803
+ l.addTo(this.map);
804
+ this.map.fire(ANVIL_EVENTS.CREATED, { layer: l });
805
+ });
806
+ }
807
+ });
808
+ this.resetDrawing();
809
+ }
810
+ resetDrawing() {
811
+ if (this.polyline) this.map.removeLayer(this.polyline);
812
+ if (this.ghostLine) this.map.removeLayer(this.ghostLine);
813
+ this.markers.forEach((m) => this.map.removeLayer(m));
814
+ this.points = [];
815
+ this.markers = [];
816
+ this.polyline = null;
817
+ this.ghostLine = null;
818
+ }
819
+ };
820
+
821
+ // src/modes/split-mode.ts
822
+ import * as L12 from "leaflet";
823
+ import * as turf3 from "@turf/turf";
824
+ var SplitMode = class {
825
+ constructor(map, store, options = {}) {
826
+ this.map = map;
827
+ this.store = store;
828
+ this.options = options;
829
+ __publicField(this, "points", []);
830
+ __publicField(this, "markers", []);
831
+ __publicField(this, "polyline", null);
832
+ __publicField(this, "ghostLine", null);
833
+ }
834
+ enable() {
835
+ this.map.on("click", this.onMapClick, this);
836
+ this.map.on("mousemove", this.onMouseMove, this);
837
+ L12.DomEvent.on(window, "keydown", this.onKeyDown, this);
838
+ this.map.getContainer().style.cursor = "crosshair";
839
+ }
840
+ disable() {
841
+ this.map.off("click", this.onMapClick, this);
842
+ this.map.off("mousemove", this.onMouseMove, this);
843
+ L12.DomEvent.off(window, "keydown", this.onKeyDown, this);
844
+ this.map.getContainer().style.cursor = "";
845
+ this.resetDrawing();
846
+ }
847
+ onMapClick(e) {
848
+ const latlng = getSnapLatLng(this.map, e.latlng, this.store, this.options, this.points);
849
+ if (this.points.length > 0) {
850
+ const lastPoint = this.points[this.points.length - 1];
851
+ const p = this.map.latLngToContainerPoint(latlng);
852
+ const lp = this.map.latLngToContainerPoint(lastPoint);
853
+ if (p.distanceTo(lp) < 15 && this.points.length >= 2) {
854
+ this.finish();
855
+ return;
856
+ }
857
+ }
858
+ this.points.push(latlng);
859
+ this.updateDrawing();
860
+ }
861
+ onMouseMove(e) {
862
+ if (this.points.length === 0) return;
863
+ const snapLatLng = getSnapLatLng(this.map, e.latlng, this.store, this.options, this.points);
864
+ const lastPoint = this.points[this.points.length - 1];
865
+ if (!this.ghostLine) {
866
+ this.ghostLine = L12.polyline([lastPoint, snapLatLng], {
867
+ dashArray: "5, 5",
868
+ color: "#ff3300",
869
+ weight: 2
870
+ }).addTo(this.map);
871
+ } else {
872
+ this.ghostLine.setLatLngs([lastPoint, snapLatLng]);
873
+ }
874
+ }
875
+ onKeyDown(e) {
876
+ if (e.key === "Escape") {
877
+ this.resetDrawing();
878
+ }
879
+ }
880
+ updateDrawing() {
881
+ if (this.polyline) {
882
+ this.polyline.setLatLngs(this.points);
883
+ } else {
884
+ this.polyline = L12.polyline(this.points, { color: "#ff3300", weight: 3 }).addTo(this.map);
885
+ }
886
+ const lastPoint = this.points[this.points.length - 1];
887
+ if (this.markers.length === 0) {
888
+ const marker4 = L12.marker(lastPoint, {
889
+ icon: L12.divIcon({
890
+ className: "anvil-split-finish",
891
+ html: '<div style="width: 10px; height: 10px; background: #ff3300; border: 2px solid white; border-radius: 50%;"></div>',
892
+ iconSize: [10, 10],
893
+ iconAnchor: [5, 5]
894
+ })
895
+ }).addTo(this.map);
896
+ marker4.on("click", (e) => {
897
+ L12.DomEvent.stopPropagation(e);
898
+ this.finish();
899
+ });
900
+ this.markers.push(marker4);
901
+ } else {
902
+ this.markers[0].setLatLng(lastPoint);
903
+ }
904
+ }
905
+ finish() {
906
+ if (this.points.length < 2) {
907
+ this.resetDrawing();
908
+ return;
909
+ }
910
+ const lineGeo = L12.polyline(this.points).toGeoJSON();
911
+ const coords = turf3.getCoords(lineGeo);
912
+ const layersToSplit = [];
913
+ this.store.getGroup().eachLayer((layer) => {
914
+ if (layer instanceof L12.Polygon) {
915
+ const polyGeo = layer.toGeoJSON();
916
+ const intersections = turf3.lineIntersect(lineGeo, polyGeo);
917
+ if (intersections.features.length >= 2) {
918
+ layersToSplit.push(layer);
919
+ }
920
+ }
921
+ });
922
+ if (layersToSplit.length === 0) {
923
+ this.resetDrawing();
924
+ return;
925
+ }
926
+ const p1 = coords[0];
927
+ const p2 = coords[coords.length - 1];
928
+ const dx = p2[0] - p1[0];
929
+ const dy = p2[1] - p1[1];
930
+ const factor = 1e3;
931
+ const start = [p1[0] - dx * factor, p1[1] - dy * factor];
932
+ const end = [p2[0] + dx * factor, p2[1] + dy * factor];
933
+ const angle = Math.atan2(dy, dx);
934
+ const perp = angle + Math.PI / 2;
935
+ const width = 1e3;
936
+ const blade = turf3.polygon([[
937
+ start,
938
+ end,
939
+ [end[0] + Math.cos(perp) * width, end[1] + Math.sin(perp) * width],
940
+ [start[0] + Math.cos(perp) * width, start[1] + Math.sin(perp) * width],
941
+ start
942
+ ]]);
943
+ layersToSplit.forEach((polygon7) => {
944
+ const polyGeo = polygon7.toGeoJSON();
945
+ if (this.options.magnetic) {
946
+ const intersections = turf3.lineIntersect(lineGeo, polyGeo);
947
+ this.insertVerticesIntoNeighbors(intersections, polygon7);
948
+ }
949
+ const part1 = turf3.difference(turf3.featureCollection([polyGeo, blade]));
950
+ const part2 = turf3.intersect(turf3.featureCollection([polyGeo, blade]));
951
+ if (part1 && part2) {
952
+ const processResult = (result) => {
953
+ const flattened = turf3.flatten(result);
954
+ flattened.features.forEach((f) => {
955
+ const l = L12.geoJSON(f).getLayers()[0];
956
+ l.addTo(this.map);
957
+ this.map.fire(ANVIL_EVENTS.CREATED, { layer: l });
958
+ });
959
+ };
960
+ this.map.fire(ANVIL_EVENTS.DELETED, { layer: polygon7 });
961
+ this.map.removeLayer(polygon7);
962
+ processResult(part1);
963
+ processResult(part2);
964
+ }
965
+ });
966
+ this.resetDrawing();
967
+ }
968
+ insertVerticesIntoNeighbors(intersections, activePolygon) {
969
+ const intersectionCoords = intersections.features.map((f) => turf3.getCoord(f));
970
+ const layers = this.store.getGroup().getLayers();
971
+ layers.forEach((layer) => {
972
+ if (!(layer instanceof L12.Polyline) || layer === activePolygon) return;
973
+ const latlngs = layer.getLatLngs();
974
+ let changed = false;
975
+ const processArr = (arr) => {
976
+ if (arr.length === 0) return;
977
+ if (arr[0] instanceof L12.LatLng || typeof arr[0].lat === "number") {
978
+ const ring = arr;
979
+ const isPolygon = layer instanceof L12.Polygon;
980
+ const ringLen = ring.length;
981
+ for (let i = ringLen - 1; i >= 0; i--) {
982
+ if (i === ringLen - 1 && !isPolygon) continue;
983
+ const p1 = ring[i];
984
+ const p2 = ring[(i + 1) % ringLen];
985
+ const a = this.map.latLngToContainerPoint(p1);
986
+ const b = this.map.latLngToContainerPoint(p2);
987
+ intersectionCoords.forEach((coord) => {
988
+ const intersectLL = L12.latLng(coord[1], coord[0]);
989
+ const p = this.map.latLngToContainerPoint(intersectLL);
990
+ const closest = L12.LineUtil.closestPointOnSegment(p, a, b);
991
+ if (p.distanceTo(closest) < 1) {
992
+ const alreadyExists = ring.some(
993
+ (ll) => this.map.latLngToContainerPoint(ll).distanceTo(p) < 1
994
+ );
995
+ if (!alreadyExists) {
996
+ ring.splice(i + 1, 0, intersectLL);
997
+ changed = true;
998
+ }
999
+ }
1000
+ });
1001
+ }
1002
+ } else {
1003
+ arr.forEach((item) => processArr(item));
1004
+ }
1005
+ };
1006
+ processArr(latlngs);
1007
+ if (changed) {
1008
+ layer.setLatLngs(latlngs);
1009
+ if (layer.redraw) layer.redraw();
1010
+ this.map.fire(ANVIL_EVENTS.EDITED, { layer });
1011
+ }
1012
+ });
1013
+ }
1014
+ resetDrawing() {
1015
+ if (this.polyline) this.map.removeLayer(this.polyline);
1016
+ if (this.ghostLine) this.map.removeLayer(this.ghostLine);
1017
+ this.markers.forEach((m) => this.map.removeLayer(m));
1018
+ this.points = [];
1019
+ this.markers = [];
1020
+ this.polyline = null;
1021
+ this.ghostLine = null;
1022
+ }
1023
+ };
1024
+
1025
+ // src/modes/drag-mode.ts
1026
+ import * as L13 from "leaflet";
1027
+ var DragMode = class {
1028
+ constructor(map, store, options = {}) {
1029
+ this.map = map;
1030
+ this.store = store;
1031
+ this.options = options;
1032
+ __publicField(this, "draggingLayer", null);
1033
+ __publicField(this, "startLatLng", null);
1034
+ __publicField(this, "initialLatLngs", null);
1035
+ }
1036
+ enable() {
1037
+ this.store.getGroup().eachLayer((layer) => {
1038
+ if (layer instanceof L13.Path || layer instanceof L13.Marker) {
1039
+ layer.on("mousedown", this.onMouseDown, this);
1040
+ layer.getElement()?.style.setProperty("cursor", "move");
1041
+ }
1042
+ });
1043
+ }
1044
+ disable() {
1045
+ this.store.getGroup().eachLayer((layer) => {
1046
+ if (layer instanceof L13.Path || layer instanceof L13.Marker) {
1047
+ layer.off("mousedown", this.onMouseDown, this);
1048
+ layer.getElement()?.style.setProperty("cursor", "");
1049
+ }
1050
+ });
1051
+ this.stopDragging();
1052
+ }
1053
+ onMouseDown(e) {
1054
+ L13.DomEvent.stopPropagation(e);
1055
+ const layer = e.target;
1056
+ if (!this.store.hasLayer(layer)) {
1057
+ return;
1058
+ }
1059
+ this.draggingLayer = layer;
1060
+ this.startLatLng = e.latlng;
1061
+ if (this.draggingLayer instanceof L13.Marker) {
1062
+ this.initialLatLngs = this.draggingLayer.getLatLng();
1063
+ } else if (this.draggingLayer instanceof L13.Path) {
1064
+ if (this.draggingLayer instanceof L13.Circle) {
1065
+ this.initialLatLngs = this.draggingLayer.getLatLng();
1066
+ } else if (this.draggingLayer instanceof L13.Polyline) {
1067
+ this.initialLatLngs = JSON.parse(JSON.stringify(this.draggingLayer.getLatLngs()));
1068
+ }
1069
+ this.draggingLayer.setStyle({ weight: 4, color: "#ffcc00" });
1070
+ }
1071
+ this.map.on("mousemove", this.onMouseMove, this);
1072
+ this.map.on("mouseup", this.onMouseUp, this);
1073
+ this.map.dragging.disable();
1074
+ }
1075
+ onMouseMove(e) {
1076
+ if (!this.draggingLayer || !this.startLatLng) return;
1077
+ const currentLatLng = getSnapLatLng(
1078
+ this.map,
1079
+ e.latlng,
1080
+ this.store,
1081
+ this.options,
1082
+ [],
1083
+ this.draggingLayer
1084
+ );
1085
+ const deltaLat = currentLatLng.lat - this.startLatLng.lat;
1086
+ const deltaLng = currentLatLng.lng - this.startLatLng.lng;
1087
+ if (this.draggingLayer instanceof L13.Marker || this.draggingLayer instanceof L13.Circle) {
1088
+ const start = this.initialLatLngs;
1089
+ this.draggingLayer.setLatLng([start.lat + deltaLat, start.lng + deltaLng]);
1090
+ } else if (this.draggingLayer instanceof L13.Polyline) {
1091
+ const newLatLngs = this.moveLatLngs(this.initialLatLngs, deltaLat, deltaLng);
1092
+ this.draggingLayer.setLatLngs(newLatLngs);
1093
+ }
1094
+ }
1095
+ moveLatLngs(latlngs, deltaLat, deltaLng) {
1096
+ if (Array.isArray(latlngs)) {
1097
+ return latlngs.map((item) => this.moveLatLngs(item, deltaLat, deltaLng));
1098
+ }
1099
+ return L13.latLng(latlngs.lat + deltaLat, latlngs.lng + deltaLng);
1100
+ }
1101
+ onMouseUp() {
1102
+ if (this.draggingLayer) {
1103
+ if (this.draggingLayer instanceof L13.Path) {
1104
+ this.draggingLayer.setStyle({ weight: 3, color: "#3388ff" });
1105
+ }
1106
+ this.map.fire(ANVIL_EVENTS.EDITED, { layer: this.draggingLayer });
1107
+ }
1108
+ this.stopDragging();
1109
+ }
1110
+ stopDragging() {
1111
+ this.map.off("mousemove", this.onMouseMove, this);
1112
+ this.map.off("mouseup", this.onMouseUp, this);
1113
+ this.map.dragging.enable();
1114
+ this.draggingLayer = null;
1115
+ this.startLatLng = null;
1116
+ this.initialLatLngs = null;
1117
+ }
1118
+ };
1119
+
1120
+ // src/modes/scale-mode.ts
1121
+ import * as L14 from "leaflet";
1122
+ var ScaleMode = class {
1123
+ constructor(map, store, options) {
1124
+ this.map = map;
1125
+ this.store = store;
1126
+ this.options = options;
1127
+ __publicField(this, "selectedLayer", null);
1128
+ __publicField(this, "centerLatLng", null);
1129
+ __publicField(this, "initialLatLngs", null);
1130
+ __publicField(this, "initialRadius", 0);
1131
+ __publicField(this, "initialDistance", 0);
1132
+ }
1133
+ enable() {
1134
+ this.store.getGroup().eachLayer((layer) => {
1135
+ if (layer instanceof L14.Path) {
1136
+ layer.on("mousedown", this.onMouseDown, this);
1137
+ layer.getElement()?.style.setProperty("cursor", "nwse-resize");
1138
+ }
1139
+ });
1140
+ }
1141
+ disable() {
1142
+ this.store.getGroup().eachLayer((layer) => {
1143
+ if (layer instanceof L14.Path) {
1144
+ layer.off("mousedown", this.onMouseDown, this);
1145
+ layer.getElement()?.style.setProperty("cursor", "");
1146
+ }
1147
+ });
1148
+ this.stopScaling();
1149
+ }
1150
+ onMouseDown(e) {
1151
+ L14.DomEvent.stopPropagation(e);
1152
+ const layer = e.target;
1153
+ if (!this.store.hasLayer(layer)) {
1154
+ return;
1155
+ }
1156
+ this.selectedLayer = layer;
1157
+ if (this.selectedLayer instanceof L14.Circle) {
1158
+ this.centerLatLng = this.selectedLayer.getLatLng();
1159
+ this.initialRadius = this.selectedLayer.getRadius();
1160
+ } else if (this.selectedLayer instanceof L14.Path) {
1161
+ const bounds = this.selectedLayer.getBounds();
1162
+ this.centerLatLng = bounds.getCenter();
1163
+ this.initialLatLngs = JSON.parse(JSON.stringify(this.selectedLayer.getLatLngs()));
1164
+ this.selectedLayer.setStyle({ weight: 4, color: "#ffcc00" });
1165
+ }
1166
+ if (this.centerLatLng) {
1167
+ this.initialDistance = this.map.distance(this.centerLatLng, e.latlng);
1168
+ this.map.on("mousemove", this.onMouseMove, this);
1169
+ this.map.on("mouseup", this.onMouseUp, this);
1170
+ this.map.dragging.disable();
1171
+ }
1172
+ }
1173
+ onMouseMove(e) {
1174
+ if (!this.selectedLayer || !this.centerLatLng || this.initialDistance === 0) return;
1175
+ const currentDistance = this.map.distance(this.centerLatLng, e.latlng);
1176
+ const ratio = currentDistance / this.initialDistance;
1177
+ if (this.selectedLayer instanceof L14.Circle) {
1178
+ this.selectedLayer.setRadius(this.initialRadius * ratio);
1179
+ } else if (this.selectedLayer instanceof L14.Path) {
1180
+ const newLatLngs = this.scaleLatLngs(this.initialLatLngs, this.centerLatLng, ratio);
1181
+ this.selectedLayer.setLatLngs(newLatLngs);
1182
+ }
1183
+ }
1184
+ scaleLatLngs(latlngs, center, ratio) {
1185
+ if (Array.isArray(latlngs)) {
1186
+ return latlngs.map((item) => this.scaleLatLngs(item, center, ratio));
1187
+ }
1188
+ const lat = center.lat + (latlngs.lat - center.lat) * ratio;
1189
+ const lng = center.lng + (latlngs.lng - center.lng) * ratio;
1190
+ return L14.latLng(lat, lng);
1191
+ }
1192
+ onMouseUp() {
1193
+ if (this.selectedLayer) {
1194
+ if (this.selectedLayer instanceof L14.Path) {
1195
+ this.selectedLayer.setStyle({ weight: 3, color: "#3388ff" });
1196
+ }
1197
+ this.map.fire(ANVIL_EVENTS.EDITED, { layer: this.selectedLayer });
1198
+ }
1199
+ this.stopScaling();
1200
+ }
1201
+ stopScaling() {
1202
+ this.map.off("mousemove", this.onMouseMove, this);
1203
+ this.map.off("mouseup", this.onMouseUp, this);
1204
+ this.map.dragging.enable();
1205
+ this.selectedLayer = null;
1206
+ this.centerLatLng = null;
1207
+ this.initialLatLngs = null;
1208
+ }
1209
+ };
1210
+
1211
+ // src/modes/rotate-mode.ts
1212
+ import * as L15 from "leaflet";
1213
+ var RotateMode = class {
1214
+ constructor(map, store, options) {
1215
+ this.map = map;
1216
+ this.store = store;
1217
+ this.options = options;
1218
+ __publicField(this, "selectedLayer", null);
1219
+ __publicField(this, "centerLatLng", null);
1220
+ __publicField(this, "initialLatLngs", null);
1221
+ __publicField(this, "startAngle", 0);
1222
+ }
1223
+ enable() {
1224
+ this.store.getGroup().eachLayer((layer) => {
1225
+ if (layer instanceof L15.Path) {
1226
+ layer.on("mousedown", this.onMouseDown, this);
1227
+ layer.getElement()?.style.setProperty("cursor", "crosshair");
1228
+ }
1229
+ });
1230
+ }
1231
+ disable() {
1232
+ this.store.getGroup().eachLayer((layer) => {
1233
+ if (layer instanceof L15.Path) {
1234
+ layer.off("mousedown", this.onMouseDown, this);
1235
+ layer.getElement()?.style.setProperty("cursor", "");
1236
+ }
1237
+ });
1238
+ this.stopRotating();
1239
+ }
1240
+ onMouseDown(e) {
1241
+ L15.DomEvent.stopPropagation(e);
1242
+ const layer = e.target;
1243
+ if (!this.store.hasLayer(layer) || !(layer instanceof L15.Path) || layer instanceof L15.Circle) {
1244
+ return;
1245
+ }
1246
+ const pathLayer = layer;
1247
+ this.selectedLayer = pathLayer;
1248
+ const bounds = pathLayer.getBounds();
1249
+ this.centerLatLng = bounds.getCenter();
1250
+ this.initialLatLngs = JSON.parse(JSON.stringify(pathLayer.getLatLngs()));
1251
+ const point2 = this.map.latLngToContainerPoint(e.latlng);
1252
+ const centerPoint = this.map.latLngToContainerPoint(this.centerLatLng);
1253
+ this.startAngle = Math.atan2(point2.y - centerPoint.y, point2.x - centerPoint.x);
1254
+ pathLayer.setStyle({ weight: 4, color: "#ffcc00" });
1255
+ this.map.on("mousemove", this.onMouseMove, this);
1256
+ this.map.on("mouseup", this.onMouseUp, this);
1257
+ this.map.dragging.disable();
1258
+ }
1259
+ onMouseMove(e) {
1260
+ if (!this.selectedLayer || !this.centerLatLng) return;
1261
+ const point2 = this.map.latLngToContainerPoint(e.latlng);
1262
+ const centerPoint = this.map.latLngToContainerPoint(this.centerLatLng);
1263
+ const currentAngle = Math.atan2(point2.y - centerPoint.y, point2.x - centerPoint.x);
1264
+ const rotationAngle = currentAngle - this.startAngle;
1265
+ const newLatLngs = this.rotateLatLngs(this.initialLatLngs, this.centerLatLng, rotationAngle);
1266
+ this.selectedLayer.setLatLngs(newLatLngs);
1267
+ }
1268
+ rotateLatLngs(latlngs, center, angle) {
1269
+ if (Array.isArray(latlngs)) {
1270
+ return latlngs.map((item) => this.rotateLatLngs(item, center, angle));
1271
+ }
1272
+ const point2 = this.map.latLngToContainerPoint(latlngs);
1273
+ const centerPoint = this.map.latLngToContainerPoint(center);
1274
+ const cos = Math.cos(angle);
1275
+ const sin = Math.sin(angle);
1276
+ const dx = point2.x - centerPoint.x;
1277
+ const dy = point2.y - centerPoint.y;
1278
+ const nx = dx * cos - dy * sin + centerPoint.x;
1279
+ const ny = dx * sin + dy * cos + centerPoint.y;
1280
+ return this.map.containerPointToLatLng([nx, ny]);
1281
+ }
1282
+ onMouseUp() {
1283
+ if (this.selectedLayer) {
1284
+ this.selectedLayer.setStyle({ weight: 3, color: "#3388ff" });
1285
+ this.map.fire(ANVIL_EVENTS.EDITED, { layer: this.selectedLayer });
1286
+ }
1287
+ this.stopRotating();
1288
+ }
1289
+ stopRotating() {
1290
+ this.map.off("mousemove", this.onMouseMove, this);
1291
+ this.map.off("mouseup", this.onMouseUp, this);
1292
+ this.map.dragging.enable();
1293
+ this.selectedLayer = null;
1294
+ this.centerLatLng = null;
1295
+ this.initialLatLngs = null;
1296
+ }
1297
+ };
1298
+
1299
+ // src/modes/union-mode.ts
1300
+ import * as L16 from "leaflet";
1301
+ import * as turf4 from "@turf/turf";
1302
+ var UnionMode = class {
1303
+ constructor(map, store, options = {}) {
1304
+ this.map = map;
1305
+ this.store = store;
1306
+ this.options = options;
1307
+ __publicField(this, "firstLayer", null);
1308
+ }
1309
+ enable() {
1310
+ this.store.getGroup().eachLayer((layer) => {
1311
+ if (layer instanceof L16.Polygon) {
1312
+ layer.on("click", this.onLayerClick, this);
1313
+ layer.getElement()?.style.setProperty("cursor", "pointer");
1314
+ }
1315
+ });
1316
+ }
1317
+ disable() {
1318
+ this.store.getGroup().eachLayer((layer) => {
1319
+ if (layer instanceof L16.Polygon) {
1320
+ layer.off("click", this.onLayerClick, this);
1321
+ layer.getElement()?.style.setProperty("cursor", "");
1322
+ }
1323
+ });
1324
+ this.reset();
1325
+ }
1326
+ onLayerClick(e) {
1327
+ L16.DomEvent.stopPropagation(e);
1328
+ const layer = e.target;
1329
+ if (!this.firstLayer) {
1330
+ this.firstLayer = layer;
1331
+ this.firstLayer.setStyle({ color: "#ff00ff", weight: 4 });
1332
+ return;
1333
+ }
1334
+ if (this.firstLayer === layer) {
1335
+ this.reset();
1336
+ return;
1337
+ }
1338
+ const g1 = this.firstLayer.toGeoJSON();
1339
+ const g2 = layer.toGeoJSON();
1340
+ const united = turf4.union(turf4.featureCollection([g1, g2]));
1341
+ if (!united) {
1342
+ console.error("Union failed - results null");
1343
+ this.reset();
1344
+ return;
1345
+ }
1346
+ this.map.fire(ANVIL_EVENTS.DELETED, { layer: this.firstLayer });
1347
+ this.map.fire(ANVIL_EVENTS.DELETED, { layer });
1348
+ this.map.removeLayer(this.firstLayer);
1349
+ this.map.removeLayer(layer);
1350
+ const flattened = turf4.flatten(united);
1351
+ flattened.features.forEach((f) => {
1352
+ const newLayerGroup = L16.geoJSON(f, {
1353
+ style: this.options.pathOptions
1354
+ });
1355
+ const l = newLayerGroup.getLayers()[0];
1356
+ l.addTo(this.map);
1357
+ this.map.fire(ANVIL_EVENTS.CREATED, { layer: l });
1358
+ });
1359
+ this.firstLayer = null;
1360
+ }
1361
+ reset() {
1362
+ if (this.firstLayer) {
1363
+ this.firstLayer.setStyle({ color: "#3388ff", weight: 3, ...this.options.pathOptions });
1364
+ this.firstLayer = null;
1365
+ }
1366
+ }
1367
+ };
1368
+
1369
+ // src/modes/subtract-mode.ts
1370
+ import * as L17 from "leaflet";
1371
+ import * as turf5 from "@turf/turf";
1372
+ var SubtractMode = class {
1373
+ constructor(map, store, options = {}) {
1374
+ this.map = map;
1375
+ this.store = store;
1376
+ this.options = options;
1377
+ __publicField(this, "baseLayer", null);
1378
+ }
1379
+ enable() {
1380
+ this.store.getGroup().eachLayer((layer) => {
1381
+ if (layer instanceof L17.Polygon) {
1382
+ layer.on("click", this.onLayerClick, this);
1383
+ layer.getElement()?.style.setProperty("cursor", "pointer");
1384
+ }
1385
+ });
1386
+ }
1387
+ disable() {
1388
+ this.store.getGroup().eachLayer((layer) => {
1389
+ if (layer instanceof L17.Polygon) {
1390
+ layer.off("click", this.onLayerClick, this);
1391
+ layer.getElement()?.style.setProperty("cursor", "");
1392
+ }
1393
+ });
1394
+ this.reset();
1395
+ }
1396
+ onLayerClick(e) {
1397
+ L17.DomEvent.stopPropagation(e);
1398
+ const layer = e.target;
1399
+ if (!this.baseLayer) {
1400
+ this.baseLayer = layer;
1401
+ this.baseLayer.setStyle({ color: "#ff0000", weight: 4 });
1402
+ return;
1403
+ }
1404
+ if (this.baseLayer === layer) {
1405
+ this.reset();
1406
+ return;
1407
+ }
1408
+ const g1 = this.baseLayer.toGeoJSON();
1409
+ const g2 = layer.toGeoJSON();
1410
+ const diff = turf5.difference(turf5.featureCollection([g1, g2]));
1411
+ this.map.fire(ANVIL_EVENTS.DELETED, { layer: this.baseLayer });
1412
+ this.map.fire(ANVIL_EVENTS.DELETED, { layer });
1413
+ this.map.removeLayer(this.baseLayer);
1414
+ this.map.removeLayer(layer);
1415
+ if (diff) {
1416
+ const flattened = turf5.flatten(diff);
1417
+ flattened.features.forEach((f) => {
1418
+ const newLayerGroup = L17.geoJSON(f);
1419
+ const l = newLayerGroup.getLayers()[0];
1420
+ l.addTo(this.map);
1421
+ this.map.fire(ANVIL_EVENTS.CREATED, { layer: l });
1422
+ });
1423
+ }
1424
+ this.baseLayer = null;
1425
+ }
1426
+ reset() {
1427
+ if (this.baseLayer) {
1428
+ this.baseLayer.setStyle({ color: "#3388ff", weight: 3 });
1429
+ this.baseLayer = null;
1430
+ }
1431
+ }
1432
+ };
1433
+
1434
+ // src/modes/edit-mode.ts
1435
+ import * as L18 from "leaflet";
1436
+ var EditMode = class {
1437
+ constructor(map, store, options = {}) {
1438
+ this.map = map;
1439
+ this.store = store;
1440
+ this.options = options;
1441
+ __publicField(this, "activeLayers", /* @__PURE__ */ new Set());
1442
+ __publicField(this, "markers", []);
1443
+ __publicField(this, "ghostMarker", null);
1444
+ __publicField(this, "segments", []);
1445
+ __publicField(this, "_isDragging", false);
1446
+ }
1447
+ enable() {
1448
+ this.store.getGroup().eachLayer((layer) => {
1449
+ if (layer instanceof L18.Path || layer instanceof L18.Marker) {
1450
+ layer.on("click", this.onLayerClick, this);
1451
+ layer.on("mousemove", this.onMouseMove, this);
1452
+ layer.getElement()?.style.setProperty("cursor", "pointer");
1453
+ }
1454
+ });
1455
+ this.map.on("click", this.onMapClick, this);
1456
+ this.map.on("mousemove", this.onMouseMove, this);
1457
+ }
1458
+ disable() {
1459
+ this.store.getGroup().eachLayer((layer) => {
1460
+ if (layer instanceof L18.Path || layer instanceof L18.Marker) {
1461
+ layer.off("click", this.onLayerClick, this);
1462
+ layer.off("mousemove", this.onMouseMove, this);
1463
+ layer.getElement()?.style.setProperty("cursor", "");
1464
+ if (layer instanceof L18.Path) {
1465
+ layer.setStyle({ color: "#3388ff", weight: 3, fillOpacity: 0.2 });
1466
+ }
1467
+ }
1468
+ });
1469
+ this.map.off("click", this.onMapClick, this);
1470
+ this.map.off("mousemove", this.onMouseMove, this);
1471
+ this.clearMarkers();
1472
+ this.activeLayers.clear();
1473
+ }
1474
+ onLayerClick(e) {
1475
+ L18.DomEvent.stopPropagation(e);
1476
+ const layer = e.target;
1477
+ const isMultiSelect = e.originalEvent.shiftKey || this.options.magnetic;
1478
+ if (!isMultiSelect) {
1479
+ if (this.activeLayers.has(layer) && this.activeLayers.size === 1) return;
1480
+ this.activeLayers.forEach((l) => {
1481
+ if (l instanceof L18.Path) l.setStyle({ color: "#3388ff", weight: 3 });
1482
+ });
1483
+ this.clearMarkers();
1484
+ this.activeLayers.clear();
1485
+ this.activeLayers.add(layer);
1486
+ } else {
1487
+ if (this.activeLayers.has(layer)) {
1488
+ this.activeLayers.delete(layer);
1489
+ if (layer instanceof L18.Path) layer.setStyle({ color: "#3388ff", weight: 3 });
1490
+ } else {
1491
+ this.activeLayers.add(layer);
1492
+ }
1493
+ this.clearMarkers();
1494
+ }
1495
+ this.activeLayers.forEach((l) => {
1496
+ if (l instanceof L18.Path) l.setStyle({ color: "#ff00ff", weight: 4 });
1497
+ });
1498
+ this.createMarkers();
1499
+ }
1500
+ onMapClick() {
1501
+ this.clearMarkers();
1502
+ this.activeLayers.clear();
1503
+ }
1504
+ createMarkers() {
1505
+ this.clearMarkers();
1506
+ if (this.activeLayers.size === 0) return;
1507
+ const vertexMap = /* @__PURE__ */ new Map();
1508
+ this.segments = [];
1509
+ const segmentMap = /* @__PURE__ */ new Map();
1510
+ const getPosKey = (ll) => `${ll.lat.toFixed(6)},${ll.lng.toFixed(6)}`;
1511
+ this.activeLayers.forEach((layer) => {
1512
+ if (layer instanceof L18.Marker) {
1513
+ this.handleMarkerEdit(layer);
1514
+ } else if (layer instanceof L18.Circle) {
1515
+ this.handleCircleEdit(layer);
1516
+ } else if (layer instanceof L18.Polyline) {
1517
+ const latlngs = layer.getLatLngs();
1518
+ const traverse = (arr, currentPath) => {
1519
+ if (!arr) return;
1520
+ if (Array.isArray(arr) && arr.length > 0 && (arr[0] instanceof L18.LatLng || typeof arr[0].lat === "number")) {
1521
+ const isPolygon = layer instanceof L18.Polygon;
1522
+ let ringLen = arr.length;
1523
+ if (isPolygon && ringLen > 1) {
1524
+ if (getPosKey(arr[0]) === getPosKey(arr[ringLen - 1])) {
1525
+ ringLen--;
1526
+ }
1527
+ }
1528
+ arr.forEach((ll, i) => {
1529
+ if (i >= ringLen) return;
1530
+ const key = getPosKey(ll);
1531
+ if (!vertexMap.has(key)) vertexMap.set(key, { latlng: ll, refs: [], marker: null });
1532
+ vertexMap.get(key).refs.push({ layer, path: [...currentPath, i] });
1533
+ if (i < ringLen - 1 || isPolygon) {
1534
+ const nextIndex = (i + 1) % ringLen;
1535
+ const nextLL = arr[nextIndex];
1536
+ if (getPosKey(ll) === getPosKey(nextLL)) return;
1537
+ const k1 = getPosKey(ll);
1538
+ const k2 = getPosKey(nextLL);
1539
+ const midKey = [k1, k2].sort().join("|");
1540
+ if (!segmentMap.has(midKey)) {
1541
+ segmentMap.set(midKey, { p1: ll, p2: nextLL, refs: [] });
1542
+ }
1543
+ segmentMap.get(midKey).refs.push({ layer, path: [...currentPath, i] });
1544
+ }
1545
+ });
1546
+ } else if (Array.isArray(arr)) {
1547
+ arr.forEach((item, i) => traverse(item, [...currentPath, i]));
1548
+ }
1549
+ };
1550
+ traverse(latlngs, []);
1551
+ }
1552
+ });
1553
+ this.segments = Array.from(segmentMap.values());
1554
+ vertexMap.forEach((group) => {
1555
+ const marker4 = this.createEditMarker(group.latlng);
1556
+ group.marker = marker4;
1557
+ this.markers.push(marker4);
1558
+ marker4.on("dragstart", () => {
1559
+ this._isDragging = true;
1560
+ });
1561
+ marker4.on("drag", (e) => {
1562
+ const mouseEvent = e;
1563
+ const skipLayersArray = Array.from(this.activeLayers);
1564
+ const additionalPoints = [
1565
+ ...this.markers.filter((m) => m !== marker4).map((m) => m.getLatLng()),
1566
+ ...Array.from(this.activeLayers).filter((l) => l instanceof L18.Marker).map((l) => l.getLatLng())
1567
+ ];
1568
+ const snapped = getSnapLatLng(this.map, mouseEvent.latlng, this.store, this.options, additionalPoints, skipLayersArray);
1569
+ if (this.options.preventSelfIntersection) {
1570
+ let wouldIntersect = false;
1571
+ group.refs.forEach((ref) => {
1572
+ const fullStructure = ref.layer.getLatLngs();
1573
+ let target = fullStructure;
1574
+ for (let i = 0; i < ref.path.length - 1; i++) {
1575
+ target = target[ref.path[i]];
1576
+ }
1577
+ const oldPos = target[ref.path[ref.path.length - 1]];
1578
+ target[ref.path[ref.path.length - 1]] = snapped;
1579
+ if (isSelfIntersecting(this.map, fullStructure, ref.layer instanceof L18.Polygon)) {
1580
+ wouldIntersect = true;
1581
+ }
1582
+ target[ref.path[ref.path.length - 1]] = oldPos;
1583
+ });
1584
+ if (wouldIntersect) return;
1585
+ }
1586
+ marker4.setLatLng(snapped);
1587
+ group.latlng = snapped;
1588
+ group.refs.forEach((ref) => {
1589
+ const fullStructure = ref.layer.getLatLngs();
1590
+ let target = fullStructure;
1591
+ for (let i = 0; i < ref.path.length - 1; i++) {
1592
+ target = target[ref.path[i]];
1593
+ }
1594
+ target[ref.path[ref.path.length - 1]] = snapped;
1595
+ ref.layer.setLatLngs(fullStructure);
1596
+ ref.layer.redraw();
1597
+ });
1598
+ });
1599
+ marker4.on("dragend", () => {
1600
+ this._isDragging = false;
1601
+ this.activeLayers.forEach((l) => this.map.fire(ANVIL_EVENTS.EDITED, { layer: l }));
1602
+ this.refreshMarkers();
1603
+ });
1604
+ marker4.on("contextmenu", (e) => {
1605
+ const mouseEvent = e;
1606
+ L18.DomEvent.stopPropagation(mouseEvent);
1607
+ this.deleteVertex(group);
1608
+ });
1609
+ });
1610
+ }
1611
+ deleteVertex(group) {
1612
+ const layersToDelete = /* @__PURE__ */ new Set();
1613
+ group.refs.forEach((ref) => {
1614
+ const fullStructure = ref.layer.getLatLngs();
1615
+ let target = fullStructure;
1616
+ for (let i = 0; i < ref.path.length - 1; i++) {
1617
+ target = target[ref.path[i]];
1618
+ }
1619
+ const index = ref.path[ref.path.length - 1];
1620
+ const isPolygon = ref.layer instanceof L18.Polygon;
1621
+ const minVertices = isPolygon ? 3 : 2;
1622
+ if (target.length > minVertices) {
1623
+ target.splice(index, 1);
1624
+ ref.layer.setLatLngs(fullStructure);
1625
+ ref.layer.redraw();
1626
+ this.map.fire(ANVIL_EVENTS.EDITED, { layer: ref.layer });
1627
+ } else {
1628
+ layersToDelete.add(ref.layer);
1629
+ }
1630
+ });
1631
+ layersToDelete.forEach((layer) => {
1632
+ this.activeLayers.delete(layer);
1633
+ this.map.removeLayer(layer);
1634
+ this.map.fire(ANVIL_EVENTS.DELETED, { layer });
1635
+ });
1636
+ this.refreshMarkers();
1637
+ }
1638
+ onMouseMove(e) {
1639
+ const mouseEvent = e;
1640
+ if (this.activeLayers.size === 0 || this.segments.length === 0 || this._isDragging) {
1641
+ if (!this._isDragging) this.removeGhost();
1642
+ return;
1643
+ }
1644
+ const mousePoint = this.map.latLngToContainerPoint(mouseEvent.latlng);
1645
+ const isOverVertex = this.markers.some((m) => {
1646
+ const p = this.map.latLngToContainerPoint(m.getLatLng());
1647
+ return p.distanceTo(mousePoint) < 15;
1648
+ });
1649
+ if (isOverVertex) {
1650
+ this.removeGhost();
1651
+ return;
1652
+ }
1653
+ let closestSeg = null;
1654
+ let minDistance = 24;
1655
+ let bestLatLng = null;
1656
+ this.segments.forEach((seg) => {
1657
+ const A = this.map.latLngToContainerPoint(seg.p1);
1658
+ const B = this.map.latLngToContainerPoint(seg.p2);
1659
+ const proj = L18.LineUtil.closestPointOnSegment(mousePoint, A, B);
1660
+ const dist = mousePoint.distanceTo(proj);
1661
+ if (dist < minDistance) {
1662
+ minDistance = dist;
1663
+ closestSeg = seg;
1664
+ bestLatLng = this.map.containerPointToLatLng(proj);
1665
+ }
1666
+ });
1667
+ if (closestSeg && bestLatLng) {
1668
+ this.showGhost(bestLatLng, closestSeg);
1669
+ } else {
1670
+ this.removeGhost();
1671
+ }
1672
+ }
1673
+ createEditMarker(latlng) {
1674
+ return L18.marker(latlng, {
1675
+ draggable: true,
1676
+ zIndexOffset: 2e3,
1677
+ icon: L18.divIcon({
1678
+ className: "anvil-edit-marker",
1679
+ html: '<div style="width: 12px; height: 12px; background: #fff; border: 2px solid #ff00ff; border-radius: 50%; box-sizing: border-box; box-shadow: 0 0 4px rgba(0,0,0,0.3);"></div>',
1680
+ iconSize: [12, 12],
1681
+ iconAnchor: [6, 6]
1682
+ })
1683
+ }).addTo(this.map);
1684
+ }
1685
+ showGhost(latlng, segment) {
1686
+ if (this.ghostMarker) {
1687
+ if (!this._isDragging) {
1688
+ this.ghostMarker.setLatLng(latlng);
1689
+ this.ghostMarker._activeSeg = segment;
1690
+ }
1691
+ return;
1692
+ }
1693
+ this.ghostMarker = L18.marker(latlng, {
1694
+ draggable: true,
1695
+ opacity: 0.7,
1696
+ zIndexOffset: 3e3,
1697
+ icon: L18.divIcon({
1698
+ className: "anvil-ghost-marker",
1699
+ html: '<div style="width: 10px; height: 10px; background: #ff00ff; border: 2px solid #fff; border-radius: 50%; box-sizing: border-box; box-shadow: 0 0 4px rgba(0,0,0,0.3);"></div>',
1700
+ iconSize: [10, 10],
1701
+ iconAnchor: [5, 5]
1702
+ })
1703
+ }).addTo(this.map);
1704
+ this.ghostMarker.on("dragstart", () => {
1705
+ this._isDragging = true;
1706
+ const activeSeg = this.ghostMarker._activeSeg || segment;
1707
+ const startLL = this.ghostMarker.getLatLng();
1708
+ activeSeg.refs.forEach((ref) => {
1709
+ const fullStructure = ref.layer.getLatLngs();
1710
+ let target = fullStructure;
1711
+ for (let i = 0; i < ref.path.length - 1; i++) {
1712
+ target = target[ref.path[i]];
1713
+ }
1714
+ target.splice(ref.path[ref.path.length - 1] + 1, 0, startLL);
1715
+ ref.layer.setLatLngs(fullStructure);
1716
+ });
1717
+ });
1718
+ this.ghostMarker.on("drag", (e) => {
1719
+ const mouseEvent = e;
1720
+ const skipLayersArray = Array.from(this.activeLayers);
1721
+ const additionalPoints = [
1722
+ ...this.markers.map((m) => m.getLatLng()),
1723
+ ...Array.from(this.activeLayers).filter((l) => l instanceof L18.Marker).map((l) => l.getLatLng())
1724
+ ];
1725
+ const snapped = getSnapLatLng(this.map, mouseEvent.latlng, this.store, this.options, additionalPoints, skipLayersArray);
1726
+ if (this.options.preventSelfIntersection) {
1727
+ let wouldIntersect = false;
1728
+ const activeSeg2 = this.ghostMarker._activeSeg || segment;
1729
+ activeSeg2.refs.forEach((ref) => {
1730
+ const fullStructure = ref.layer.getLatLngs();
1731
+ let target = fullStructure;
1732
+ for (let i = 0; i < ref.path.length - 1; i++) {
1733
+ target = target[ref.path[i]];
1734
+ }
1735
+ const oldPos = target[ref.path[ref.path.length - 1] + 1];
1736
+ target[ref.path[ref.path.length - 1] + 1] = snapped;
1737
+ if (isSelfIntersecting(this.map, fullStructure, ref.layer instanceof L18.Polygon)) {
1738
+ wouldIntersect = true;
1739
+ }
1740
+ target[ref.path[ref.path.length - 1] + 1] = oldPos;
1741
+ });
1742
+ if (wouldIntersect) return;
1743
+ }
1744
+ this.ghostMarker.setLatLng(snapped);
1745
+ const activeSeg = this.ghostMarker._activeSeg || segment;
1746
+ activeSeg.refs.forEach((ref) => {
1747
+ const fullStructure = ref.layer.getLatLngs();
1748
+ let target = fullStructure;
1749
+ for (let i = 0; i < ref.path.length - 1; i++) {
1750
+ target = target[ref.path[i]];
1751
+ }
1752
+ target[ref.path[ref.path.length - 1] + 1] = snapped;
1753
+ ref.layer.setLatLngs(fullStructure);
1754
+ ref.layer.redraw();
1755
+ });
1756
+ });
1757
+ this.ghostMarker.on("dragend", () => {
1758
+ this._isDragging = false;
1759
+ this.activeLayers.forEach((l) => this.map.fire(ANVIL_EVENTS.EDITED, { layer: l }));
1760
+ this.removeGhost();
1761
+ this.refreshMarkers();
1762
+ });
1763
+ }
1764
+ removeGhost() {
1765
+ if (this.ghostMarker && !this._isDragging) {
1766
+ this.map.removeLayer(this.ghostMarker);
1767
+ this.ghostMarker = null;
1768
+ }
1769
+ }
1770
+ handleMarkerEdit(marker4) {
1771
+ marker4.dragging?.enable();
1772
+ marker4.on("drag", (e) => {
1773
+ const mouseEvent = e;
1774
+ const additionalPoints = this.markers.map((m) => m.getLatLng());
1775
+ const snapped = getSnapLatLng(this.map, mouseEvent.latlng, this.store, this.options, additionalPoints, marker4);
1776
+ marker4.setLatLng(snapped);
1777
+ });
1778
+ marker4.on("dragend", () => {
1779
+ this.map.fire(ANVIL_EVENTS.EDITED, { layer: marker4 });
1780
+ });
1781
+ marker4.on("contextmenu", (e) => {
1782
+ const mouseEvent = e;
1783
+ L18.DomEvent.stopPropagation(mouseEvent);
1784
+ this.activeLayers.delete(marker4);
1785
+ this.map.removeLayer(marker4);
1786
+ this.map.fire(ANVIL_EVENTS.DELETED, { layer: marker4 });
1787
+ this.refreshMarkers();
1788
+ });
1789
+ }
1790
+ handleCircleEdit(circle2) {
1791
+ const marker4 = this.createEditMarker(circle2.getLatLng());
1792
+ marker4.on("drag", (e) => {
1793
+ const mouseEvent = e;
1794
+ const additionalPoints = this.markers.filter((m) => m !== marker4).map((m) => m.getLatLng());
1795
+ const snapped = getSnapLatLng(this.map, mouseEvent.latlng, this.store, this.options, additionalPoints, circle2);
1796
+ marker4.setLatLng(snapped);
1797
+ circle2.setLatLng(snapped);
1798
+ });
1799
+ marker4.on("dragend", () => {
1800
+ this.map.fire(ANVIL_EVENTS.EDITED, { layer: circle2 });
1801
+ });
1802
+ marker4.on("contextmenu", (e) => {
1803
+ const mouseEvent = e;
1804
+ L18.DomEvent.stopPropagation(mouseEvent);
1805
+ this.activeLayers.delete(circle2);
1806
+ this.map.removeLayer(circle2);
1807
+ this.map.fire(ANVIL_EVENTS.DELETED, { layer: circle2 });
1808
+ this.refreshMarkers();
1809
+ });
1810
+ this.markers.push(marker4);
1811
+ }
1812
+ clearMarkers() {
1813
+ this.activeLayers.forEach((layer) => {
1814
+ if (layer instanceof L18.Marker) {
1815
+ layer.dragging?.disable();
1816
+ layer.off("drag");
1817
+ layer.off("dragend");
1818
+ layer.off("contextmenu");
1819
+ }
1820
+ });
1821
+ this.markers.forEach((m) => this.map.removeLayer(m));
1822
+ this.markers = [];
1823
+ this.removeGhost();
1824
+ }
1825
+ refreshMarkers() {
1826
+ this.clearMarkers();
1827
+ this.createMarkers();
1828
+ }
1829
+ };
1830
+
1831
+ // src/modes/delete-mode.ts
1832
+ import * as L19 from "leaflet";
1833
+ var DeleteMode = class {
1834
+ constructor(map, store) {
1835
+ this.map = map;
1836
+ this.store = store;
1837
+ }
1838
+ enable() {
1839
+ this.store.getGroup().eachLayer((layer) => {
1840
+ layer.on("click", this.onClick, this);
1841
+ if (layer instanceof L19.Path || layer instanceof L19.Marker) {
1842
+ layer.getElement()?.style.setProperty("cursor", "pointer");
1843
+ }
1844
+ });
1845
+ }
1846
+ disable() {
1847
+ this.store.getGroup().eachLayer((layer) => {
1848
+ layer.off("click", this.onClick, this);
1849
+ if (layer instanceof L19.Path || layer instanceof L19.Marker) {
1850
+ layer.getElement()?.style.setProperty("cursor", "");
1851
+ }
1852
+ });
1853
+ }
1854
+ onClick(e) {
1855
+ L19.DomEvent.stopPropagation(e);
1856
+ const layer = e.target;
1857
+ this.map.removeLayer(layer);
1858
+ this.map.fire(ANVIL_EVENTS.DELETED, { layer });
1859
+ }
1860
+ };
1861
+
1862
+ // src/layers/layer-store.ts
1863
+ import * as L20 from "leaflet";
1864
+ var LayerStore = class {
1865
+ constructor(map, layerGroup) {
1866
+ __publicField(this, "group");
1867
+ this.group = layerGroup || L20.featureGroup();
1868
+ this.group.addTo(map);
1869
+ }
1870
+ addLayer(layer) {
1871
+ this.group.addLayer(layer);
1872
+ }
1873
+ removeLayer(layer) {
1874
+ this.group.removeLayer(layer);
1875
+ }
1876
+ hasLayer(layer) {
1877
+ return this.group.hasLayer(layer);
1878
+ }
1879
+ getGroup() {
1880
+ return this.group;
1881
+ }
1882
+ };
1883
+
1884
+ // src/anvil.ts
1885
+ var Anvil = class {
1886
+ constructor(map, options) {
1887
+ this.map = map;
1888
+ __publicField(this, "modeManager");
1889
+ __publicField(this, "store");
1890
+ __publicField(this, "options");
1891
+ this.options = { snapping: false, snapDistance: 10, preventSelfIntersection: false, ...options };
1892
+ this.store = new LayerStore(map, this.options.layerGroup);
1893
+ this.modeManager = new ModeManager(map);
1894
+ this.modeManager.addMode("draw:polygon", new DrawPolygonMode(this.map, this.options, this.store));
1895
+ this.modeManager.addMode("draw:polyline", new DrawPolylineMode(this.map, this.options, this.store));
1896
+ this.modeManager.addMode("draw:marker", new DrawMarkerMode(this.map, this.options, this.store));
1897
+ this.modeManager.addMode("draw:rectangle", new DrawRectangleMode(this.map, this.options, this.store));
1898
+ this.modeManager.addMode("draw:square", new DrawSquareMode(this.map, this.options, this.store));
1899
+ this.modeManager.addMode("draw:triangle", new DrawTriangleMode(this.map, this.options, this.store));
1900
+ this.modeManager.addMode("draw:circle", new DrawCircleMode(this.map, this.options, this.store));
1901
+ this.modeManager.addMode("draw:freehand", new FreehandMode(this.map, this.store, this.options));
1902
+ this.modeManager.addMode("cut", new CutMode(map, this.store, this.options));
1903
+ this.modeManager.addMode("split", new SplitMode(map, this.store, this.options));
1904
+ this.modeManager.addMode("union", new UnionMode(map, this.store, this.options));
1905
+ this.modeManager.addMode("subtract", new SubtractMode(map, this.store, this.options));
1906
+ this.modeManager.addMode("drag", new DragMode(map, this.store, this.options));
1907
+ this.modeManager.addMode("scale", new ScaleMode(map, this.store, this.options));
1908
+ this.modeManager.addMode("rotate", new RotateMode(map, this.store, this.options));
1909
+ this.modeManager.addMode("edit", new EditMode(this.map, this.store, this.options));
1910
+ this.modeManager.addMode("delete", new DeleteMode(map, this.store));
1911
+ this.map.on(ANVIL_EVENTS.CREATED, (e) => {
1912
+ this.store.addLayer(e.layer);
1913
+ });
1914
+ this.map.on(ANVIL_EVENTS.DELETED, (e) => {
1915
+ this.store.removeLayer(e.layer);
1916
+ });
1917
+ }
1918
+ enable(mode) {
1919
+ this.modeManager.enable(mode);
1920
+ }
1921
+ disable() {
1922
+ this.modeManager.disable();
1923
+ }
1924
+ getLayerGroup() {
1925
+ return this.store.getGroup();
1926
+ }
1927
+ };
1928
+
1929
+ // src/controls/anvil-control.ts
1930
+ import * as L21 from "leaflet";
1931
+ var SVG_ATTRS = 'xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"';
1932
+ var ICONS = {
1933
+ // Marker: Map-Pin mit Punkt in der Mitte
1934
+ "draw:marker": `<svg ${SVG_ATTRS}><path d="M20 10c0 6-8 12-8 12s-8-6-8-12a8 8 0 0 1 16 0Z"/><circle cx="12" cy="10" r="3"/></svg>`,
1935
+ // Polyline: Kurvige Route mit Endpunkten
1936
+ "draw:polyline": `<svg ${SVG_ATTRS}><path d="M9 19h8.5a3.5 3.5 0 0 0 0-7h-11a3.5 3.5 0 0 1 0-7H15"/><circle cx="18" cy="5" r="3"/><circle cx="6" cy="19" r="3"/></svg>`,
1937
+ // Polygon: Klassische unregelmäßige 5-Eck-Form
1938
+ "draw:polygon": `<svg ${SVG_ATTRS}><path d="m12 2 10 7-3 12H5l-3-12Z"/></svg>`,
1939
+ // Rectangle: Horizontales Rechteck
1940
+ "draw:rectangle": `<svg ${SVG_ATTRS}><rect width="20" height="12" x="2" y="6" rx="2"/></svg>`,
1941
+ // Square: Quadratisches Rechteck (1:1)
1942
+ "draw:square": `<svg ${SVG_ATTRS}><rect width="18" height="18" x="3" y="3" rx="2"/></svg>`,
1943
+ // Triangle: Gleichschenkliges Dreieck
1944
+ "draw:triangle": `<svg ${SVG_ATTRS}><path d="m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3Z"/></svg>`,
1945
+ // Circle: Klassischer Kreis
1946
+ "draw:circle": `<svg ${SVG_ATTRS}><circle cx="12" cy="12" r="10"/></svg>`,
1947
+ // Freehand: Geschwungene Signatur-Linie
1948
+ "draw:freehand": `<svg ${SVG_ATTRS}><path d="m3 16 2 2 16-16"/><path d="M7 21h14"/><path d="M3 11c0 2 2 2 2 2z"/></svg>`,
1949
+ // Cut: Offene Schere
1950
+ "cut": `<svg ${SVG_ATTRS}><circle cx="6" cy="6" r="3"/><circle cx="6" cy="18" r="3"/><path d="M20 4 8.12 15.88"/><path d="M14.47 14.48 20 20"/><path d="M8.12 8.12 12 12"/></svg>`,
1951
+ // Split: Linie, die eine Form teilt
1952
+ "split": `<svg ${SVG_ATTRS}><path d="M3 12h18"/><path d="M8 3v18"/><path d="M16 3v18"/><rect width="18" height="18" x="3" y="3" rx="2" stroke-dasharray="4 4" opacity="0.5"/></svg>`,
1953
+ // Union: Zwei verschmolzene Rechtecke
1954
+ "union": `<svg ${SVG_ATTRS}><path d="M8 4H4v4"/><path d="M4 12v4a2 2 0 0 0 2 2h4"/><path d="M14 18h4v-4"/><path d="M20 10V6a2 2 0 0 0-2-2h-4"/><path d="M14 10h-4v4" stroke-dasharray="2 2"/></svg>`,
1955
+ // Subtract: Hauptform mit "ausgeschnittenem" Bereich (Minus-Metapher)
1956
+ "subtract": `<svg ${SVG_ATTRS}><path d="M4 4h16v16H4z"/><path d="M10 10h10v10H10z" stroke-dasharray="2 2" opacity="0.7"/><path d="M15 6h-6"/></svg>`,
1957
+ // Drag: Vier-Wege-Pfeil (Move-Metapher)
1958
+ "drag": `<svg ${SVG_ATTRS}><path d="m5 9-3 3 3 3"/><path d="m9 5 3-3 3 3"/><path d="m15 19 3 3 3-3"/><path d="m19 9 3 3-3 3"/><path d="M2 12h20"/><path d="M12 2v20"/></svg>`,
1959
+ // Scale: Diagonal-Pfeile (Maximize-Metapher)
1960
+ "scale": `<svg ${SVG_ATTRS}><path d="M15 3h6v6"/><path d="M9 21H3v-6"/><path d="M21 3 14 10"/><path d="M3 21l7-7"/></svg>`,
1961
+ // Rotate: Kreispfeil mit Ziel-Icon
1962
+ "rotate": `<svg ${SVG_ATTRS}><path d="M21 12a9 9 0 1 1-9-9c2.52 0 4.93 1 6.74 2.74L21 8"/><path d="M21 3v5h-5"/></svg>`,
1963
+ // Edit: Stift, der über einen Pfad zeichnet
1964
+ "edit": `<svg ${SVG_ATTRS}><path d="M12 20h9"/><path d="M16.5 3.5a2.12 2.12 0 0 1 3 3L7 19l-4 1 1-4Z"/></svg>`,
1965
+ // Delete: Mülleimer mit Deckel und Schlitzen
1966
+ "delete": `<svg ${SVG_ATTRS}><path d="M3 6h18"/><path d="M19 6v14c0 1-1 2-2 2H7c-1 0-2-1-2-2V6"/><path d="M8 6V4c0-1 1-2 2-2h4c1 0 2 1 2 2v2"/><line x1="10" x2="10" y1="11" y2="17"/><line x1="14" x2="14" y1="11" y2="17"/></svg>`,
1967
+ // Off: Durchgestrichener Kreis (Power-Off/Disable Metapher)
1968
+ "off": `<svg ${SVG_ATTRS}><circle cx="12" cy="12" r="10"/><path d="m15 9-6 6"/><path d="m9 9 6 6"/></svg>`
1969
+ };
1970
+ var AnvilControl = class extends L21.Control {
1971
+ constructor(anvil, options) {
1972
+ super(L21.Util.extend({ position: "topleft" }, options));
1973
+ __publicField(this, "_btns", {});
1974
+ __publicField(this, "_anvil");
1975
+ this._anvil = anvil;
1976
+ }
1977
+ onAdd(map) {
1978
+ console.log("AnvilControl: triggering onAdd");
1979
+ const container = L21.DomUtil.create("div", "leaflet-bar anvil-toolbar");
1980
+ container.style.backgroundColor = "white";
1981
+ container.style.display = "flex";
1982
+ container.style.flexDirection = "column";
1983
+ const modes = [
1984
+ { id: "draw:marker", title: "Marker" },
1985
+ { id: "draw:polyline", title: "Line" },
1986
+ { id: "draw:polygon", title: "Polygon" },
1987
+ { id: "draw:rectangle", title: "Rectangle" },
1988
+ { id: "draw:square", title: "Square" },
1989
+ { id: "draw:triangle", title: "Triangle" },
1990
+ { id: "draw:circle", title: "Circle" },
1991
+ { id: "draw:freehand", title: "Freehand" },
1992
+ { id: "cut", title: "Cut" },
1993
+ { id: "split", title: "Split" },
1994
+ { id: "union", title: "Union" },
1995
+ { id: "subtract", title: "Subtract" },
1996
+ { id: "drag", title: "Drag" },
1997
+ { id: "scale", title: "Scale" },
1998
+ { id: "rotate", title: "Rotate" },
1999
+ { id: "edit", title: "Edit" },
2000
+ { id: "delete", title: "Delete" },
2001
+ { id: "off", title: "Turn Off" }
2002
+ ];
2003
+ modes.forEach((mode) => {
2004
+ const btn = L21.DomUtil.create("a", "anvil-control-btn", container);
2005
+ btn.innerHTML = ICONS[mode.id] || mode.title;
2006
+ btn.href = "#";
2007
+ btn.title = mode.title;
2008
+ btn.style.display = "flex";
2009
+ btn.style.alignItems = "center";
2010
+ btn.style.justifyContent = "center";
2011
+ btn.style.width = "30px";
2012
+ btn.style.height = "30px";
2013
+ btn.style.color = "#333";
2014
+ btn.style.cursor = "pointer";
2015
+ L21.DomEvent.disableClickPropagation(btn);
2016
+ L21.DomEvent.on(btn, "click", (e) => {
2017
+ L21.DomEvent.preventDefault(e);
2018
+ if (mode.id === "off") {
2019
+ this._anvil.disable();
2020
+ } else {
2021
+ this._anvil.enable(mode.id);
2022
+ }
2023
+ });
2024
+ this._btns[mode.id] = btn;
2025
+ });
2026
+ const updateFn = (m) => {
2027
+ for (const id in this._btns) {
2028
+ const active = id === m || id === "off" && !m;
2029
+ this._btns[id].style.backgroundColor = active ? "#eee" : "#fff";
2030
+ }
2031
+ };
2032
+ updateFn(null);
2033
+ map.on(ANVIL_EVENTS.MODE_CHANGE, (e) => updateFn(e.mode), this);
2034
+ return container;
2035
+ }
2036
+ };
2037
+ function anvilControl(anvil, options) {
2038
+ return new AnvilControl(anvil, options);
2039
+ }
2040
+
2041
+ // src/index.ts
2042
+ var leaflet = L22;
2043
+ leaflet.Anvil = Anvil;
2044
+ leaflet.anvil = (map, options) => new Anvil(map, options);
2045
+ if (leaflet.Control) {
2046
+ leaflet.Control.Anvil = (anvil, options) => anvilControl(anvil, options);
2047
+ leaflet.control.anvil = (anvil, options) => anvilControl(anvil, options);
2048
+ }
2049
+ export {
2050
+ ANVIL_EVENTS,
2051
+ Anvil,
2052
+ AnvilControl,
2053
+ anvilControl
2054
+ };