leaflet-anvil 0.2.2 → 0.3.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.js CHANGED
@@ -39,7 +39,7 @@ __export(index_exports, {
39
39
  anvilControl: () => anvilControl
40
40
  });
41
41
  module.exports = __toCommonJS(index_exports);
42
- var L22 = __toESM(require("leaflet"));
42
+ var L23 = __toESM(require("leaflet"));
43
43
 
44
44
  // src/types.ts
45
45
  var AnvilMode = /* @__PURE__ */ ((AnvilMode2) => {
@@ -59,6 +59,7 @@ var AnvilMode = /* @__PURE__ */ ((AnvilMode2) => {
59
59
  AnvilMode2["Union"] = "union";
60
60
  AnvilMode2["Subtract"] = "subtract";
61
61
  AnvilMode2["Edit"] = "edit";
62
+ AnvilMode2["Topology"] = "topology";
62
63
  AnvilMode2["Delete"] = "delete";
63
64
  AnvilMode2["Off"] = "off";
64
65
  return AnvilMode2;
@@ -159,30 +160,35 @@ function getSnapLatLng(map, latlng, store, options, additionalPoints = [], skipL
159
160
  const verifiedAdditionalPoints = Array.isArray(additionalPoints) ? additionalPoints : [];
160
161
  const snapDistance = options.snapDistance || 10;
161
162
  const basePoint = map.latLngToContainerPoint(latlng);
163
+ const minPoint = basePoint.subtract(L.point(snapDistance, snapDistance));
164
+ const maxPoint = basePoint.add(L.point(snapDistance, snapDistance));
165
+ const searchBounds = L.latLngBounds(
166
+ map.containerPointToLatLng(minPoint),
167
+ map.containerPointToLatLng(maxPoint)
168
+ );
162
169
  let closestPoint = null;
163
170
  let minDistance = snapDistance;
164
171
  const skipLayers = Array.isArray(skipLayer) ? skipLayer : skipLayer ? [skipLayer] : [];
165
- const bounds = map.getBounds().pad(0.1);
166
172
  store.getGroup().eachLayer((layer) => {
167
173
  if (skipLayers.includes(layer)) return;
168
- if (layer.getBounds && !bounds.intersects(layer.getBounds())) return;
174
+ if (layer.getBounds && !searchBounds.intersects(layer.getBounds())) return;
169
175
  const pointsToCheck = [];
170
176
  if (layer instanceof L.Marker || layer instanceof L.CircleMarker || layer instanceof L.Circle) {
171
177
  pointsToCheck.push(layer.getLatLng());
172
178
  } else if (layer instanceof L.Polyline) {
173
179
  const latlngs = layer.getLatLngs();
174
- const flatten5 = (arr) => {
180
+ const flatten4 = (arr) => {
175
181
  let result = [];
176
182
  arr.forEach((item) => {
177
183
  if (Array.isArray(item)) {
178
- result = result.concat(flatten5(item));
184
+ result = result.concat(flatten4(item));
179
185
  } else if (item instanceof L.LatLng) {
180
186
  result.push(item);
181
187
  }
182
188
  });
183
189
  return result;
184
190
  };
185
- pointsToCheck.push(...flatten5(latlngs));
191
+ pointsToCheck.push(...flatten4(latlngs));
186
192
  }
187
193
  pointsToCheck.forEach((p) => {
188
194
  const containerPoint = map.latLngToContainerPoint(p);
@@ -235,30 +241,6 @@ function causesSelfIntersection(map, points, nextPoint, isClosing = false) {
235
241
  }
236
242
  return false;
237
243
  }
238
- function isSelfIntersecting(map, latlngs, isPolygon) {
239
- const flatten5 = (arr) => {
240
- if (!Array.isArray(arr)) return [];
241
- if (arr[0] instanceof L2.LatLng) return arr;
242
- return arr.reduce((acc, val) => acc.concat(flatten5(val)), []);
243
- };
244
- const points = flatten5(latlngs);
245
- if (points.length < 4) return false;
246
- const projectedPoints = points.map((p) => map.latLngToLayerPoint(p));
247
- const len = projectedPoints.length;
248
- for (let i = 0; i < len; i++) {
249
- const a = projectedPoints[i];
250
- const b = projectedPoints[(i + 1) % len];
251
- if (!isPolygon && i === len - 1) break;
252
- for (let j = i + 2; j < len; j++) {
253
- if (isPolygon && (j + 1) % len === i) continue;
254
- if (!isPolygon && j === len - 1) break;
255
- const c = projectedPoints[j];
256
- const d = projectedPoints[(j + 1) % len];
257
- if (segmentsIntersect(a, b, c, d)) return true;
258
- }
259
- }
260
- return false;
261
- }
262
244
 
263
245
  // src/modes/draw-polygon-mode.ts
264
246
  var DrawPolygonMode = class {
@@ -339,27 +321,27 @@ var DrawPolygonMode = class {
339
321
  ).addTo(this.map);
340
322
  }
341
323
  if (this.points.length === 1 && this.markers.length === 0) {
342
- const marker3 = L3.circleMarker(
324
+ const marker2 = L3.circleMarker(
343
325
  this.points[0],
344
326
  getModeHandleOptions(this.options, "polygon" /* Polygon */, {
345
327
  radius: 5,
346
328
  color: "#3388ff"
347
329
  })
348
330
  ).addTo(this.map);
349
- marker3.on("click", (e) => {
331
+ marker2.on("click", (e) => {
350
332
  L3.DomEvent.stopPropagation(e);
351
333
  this.finish();
352
334
  });
353
- this.markers.push(marker3);
335
+ this.markers.push(marker2);
354
336
  }
355
337
  }
356
338
  finish() {
357
339
  if (this.points.length < 3) return;
358
- const polygon7 = L3.polygon(
340
+ const polygon8 = L3.polygon(
359
341
  this.points,
360
342
  getModePathOptions(this.options, "polygon" /* Polygon */)
361
343
  ).addTo(this.map);
362
- this.map.fire(ANVIL_EVENTS.CREATED, { layer: polygon7 });
344
+ this.map.fire(ANVIL_EVENTS.CREATED, { layer: polygon8 });
363
345
  this.reset();
364
346
  }
365
347
  reset() {
@@ -483,8 +465,8 @@ var DrawMarkerMode = class {
483
465
  }
484
466
  onClick(e) {
485
467
  const latlng = this.store ? getSnapLatLng(this.map, e.latlng, this.store, this.options) : e.latlng;
486
- const marker3 = L5.marker(latlng, getModeVertexOptions(this.options, "marker" /* Marker */)).addTo(this.map);
487
- this.map.fire(ANVIL_EVENTS.CREATED, { layer: marker3 });
468
+ const marker2 = L5.marker(latlng, getModeVertexOptions(this.options, "marker" /* Marker */)).addTo(this.map);
469
+ this.map.fire(ANVIL_EVENTS.CREATED, { layer: marker2 });
488
470
  }
489
471
  };
490
472
 
@@ -598,7 +580,7 @@ var DrawSquareMode = class {
598
580
  if (!this.startLatLng) return;
599
581
  const currentLatLng = this.store ? getSnapLatLng(this.map, e.latlng, this.store, this.options) : e.latlng;
600
582
  const squareBounds = this.getSquareBounds(this.startLatLng, currentLatLng);
601
- const polygon7 = L7.polygon(
583
+ const polygon8 = L7.polygon(
602
584
  [
603
585
  squareBounds.getNorthWest(),
604
586
  squareBounds.getNorthEast(),
@@ -607,7 +589,7 @@ var DrawSquareMode = class {
607
589
  ],
608
590
  getModePathOptions(this.options, "square" /* Square */)
609
591
  ).addTo(this.map);
610
- this.map.fire(ANVIL_EVENTS.CREATED, { layer: polygon7 });
592
+ this.map.fire(ANVIL_EVENTS.CREATED, { layer: polygon8 });
611
593
  this.reset();
612
594
  }
613
595
  getSquareBounds(start, end) {
@@ -677,11 +659,11 @@ var DrawTriangleMode = class {
677
659
  if (!this.startLatLng) return;
678
660
  const currentLatLng = this.store ? getSnapLatLng(this.map, e.latlng, this.store, this.options) : e.latlng;
679
661
  const trianglePoints = this.getTrianglePoints(this.startLatLng, currentLatLng);
680
- const polygon7 = L8.polygon(
662
+ const polygon8 = L8.polygon(
681
663
  trianglePoints,
682
664
  getModePathOptions(this.options, "triangle" /* Triangle */)
683
665
  ).addTo(this.map);
684
- this.map.fire(ANVIL_EVENTS.CREATED, { layer: polygon7 });
666
+ this.map.fire(ANVIL_EVENTS.CREATED, { layer: polygon8 });
685
667
  this.reset();
686
668
  }
687
669
  getTrianglePoints(start, end) {
@@ -930,18 +912,18 @@ var CutMode = class {
930
912
  ).addTo(this.map);
931
913
  }
932
914
  if (this.points.length === 1 && this.markers.length === 0) {
933
- const marker3 = L11.circleMarker(
915
+ const marker2 = L11.circleMarker(
934
916
  this.points[0],
935
917
  getModeHandleOptions(this.options, "cut" /* Cut */, {
936
918
  radius: 6,
937
919
  color: "#ff0000"
938
920
  })
939
921
  ).addTo(this.map);
940
- marker3.on("click", (e) => {
922
+ marker2.on("click", (e) => {
941
923
  L11.DomEvent.stopPropagation(e);
942
924
  if (this.points.length >= 3) this.finish();
943
925
  });
944
- this.markers.push(marker3);
926
+ this.markers.push(marker2);
945
927
  }
946
928
  }
947
929
  finish() {
@@ -960,12 +942,12 @@ var CutMode = class {
960
942
  this.resetDrawing();
961
943
  return;
962
944
  }
963
- intersectedLayers.forEach((polygon7) => {
964
- const polyGeo = polygon7.toGeoJSON();
945
+ intersectedLayers.forEach((polygon8) => {
946
+ const polyGeo = polygon8.toGeoJSON();
965
947
  const result = turf2.difference(turf2.featureCollection([polyGeo, holeGeo]));
966
948
  if (result) {
967
- this.map.fire(ANVIL_EVENTS.DELETED, { layer: polygon7 });
968
- this.map.removeLayer(polygon7);
949
+ this.map.fire(ANVIL_EVENTS.DELETED, { layer: polygon8 });
950
+ this.map.removeLayer(polygon8);
969
951
  const flattened = turf2.flatten(result);
970
952
  flattened.features.forEach((f) => {
971
953
  const l = L11.geoJSON(f, {
@@ -990,8 +972,260 @@ var CutMode = class {
990
972
  };
991
973
 
992
974
  // src/modes/split-mode.ts
975
+ var L13 = __toESM(require("leaflet"));
976
+ var turf4 = __toESM(require("@turf/turf"));
977
+
978
+ // src/utils/polygon-topology.ts
993
979
  var L12 = __toESM(require("leaflet"));
994
980
  var turf3 = __toESM(require("@turf/turf"));
981
+ function createCoverageSnapshot(layers) {
982
+ const features = layers.map((layer) => sanitizePolygonFeature(layer.toGeoJSON())).filter((feature) => Boolean(feature));
983
+ if (features.length === 0) return null;
984
+ const target = unionPolygonFeatures(features);
985
+ if (!target) return null;
986
+ return { target };
987
+ }
988
+ function hasSharedBoundaryCoverage(layers) {
989
+ const features = layers.map((layer) => sanitizePolygonFeature(layer.toGeoJSON())).filter((feature) => Boolean(feature)).map((feature) => ({
990
+ feature,
991
+ boundary: turf3.polygonToLine(feature)
992
+ }));
993
+ for (let i = 0; i < features.length; i++) {
994
+ for (let j = i + 1; j < features.length; j++) {
995
+ if (getBoundaryContactLength(features[i].boundary, features[j].boundary) > 0) {
996
+ return true;
997
+ }
998
+ try {
999
+ const overlap = turf3.intersect(
1000
+ turf3.featureCollection([features[i].feature, features[j].feature])
1001
+ );
1002
+ if (overlap && turf3.area(overlap) > 0) {
1003
+ return true;
1004
+ }
1005
+ } catch {
1006
+ }
1007
+ }
1008
+ }
1009
+ return false;
1010
+ }
1011
+ function repairPolygonCoverage(layers, snapshot) {
1012
+ const target = sanitizePolygonFeature(snapshot.target);
1013
+ if (!target) return [];
1014
+ const current = layers.map((layer) => {
1015
+ const feature = sanitizePolygonFeature(layer.toGeoJSON());
1016
+ if (!feature) return null;
1017
+ return {
1018
+ layer,
1019
+ feature,
1020
+ boundary: turf3.polygonToLine(feature)
1021
+ };
1022
+ }).filter((entry) => Boolean(entry));
1023
+ if (current.length < 2) return [];
1024
+ const currentUnion = unionPolygonFeatures(current.map(({ feature }) => feature));
1025
+ if (!currentUnion) return [];
1026
+ let missingCoverage = null;
1027
+ try {
1028
+ missingCoverage = turf3.difference(
1029
+ turf3.featureCollection([target, currentUnion])
1030
+ );
1031
+ } catch {
1032
+ return [];
1033
+ }
1034
+ if (!missingCoverage) return [];
1035
+ const gaps = [];
1036
+ turf3.flattenEach(missingCoverage, (feature) => {
1037
+ const sanitized = sanitizePolygonFeature(feature);
1038
+ if (sanitized && sanitized.geometry.type === "Polygon") {
1039
+ gaps.push(sanitized);
1040
+ }
1041
+ });
1042
+ if (gaps.length === 0) return [];
1043
+ const assignments = new Map(current.map(({ layer }) => [layer, []]));
1044
+ gaps.forEach((gap) => {
1045
+ const owner = pickOwnerForCell(gap, current);
1046
+ if (!owner) return;
1047
+ assignments.get(owner.layer)?.push(gap);
1048
+ });
1049
+ const repaired = [];
1050
+ assignments.forEach((cells, layer) => {
1051
+ if (cells.length === 0) return;
1052
+ const existing = current.find((entry) => entry.layer === layer)?.feature;
1053
+ if (!existing) return;
1054
+ const merged = unionPolygonFeatures([existing, ...cells]);
1055
+ if (!merged) return;
1056
+ layer.setLatLngs(toLeafletLatLngs(merged.geometry));
1057
+ layer.redraw();
1058
+ repaired.push(layer);
1059
+ });
1060
+ return repaired;
1061
+ }
1062
+ function collectTouchingPolygons(group, seedLayers) {
1063
+ const seeds = seedLayers.map((layer) => ({
1064
+ layer,
1065
+ feature: sanitizePolygonFeature(layer.toGeoJSON())
1066
+ })).filter((entry) => Boolean(entry.feature));
1067
+ const related = new Set(seedLayers);
1068
+ group.eachLayer((layer) => {
1069
+ if (!(layer instanceof L12.Polygon) || related.has(layer)) return;
1070
+ const feature = sanitizePolygonFeature(layer.toGeoJSON());
1071
+ if (!feature) return;
1072
+ const touchesSeed = seeds.some((seed) => {
1073
+ try {
1074
+ return turf3.booleanIntersects(seed.feature, feature);
1075
+ } catch {
1076
+ return false;
1077
+ }
1078
+ });
1079
+ if (touchesSeed) related.add(layer);
1080
+ });
1081
+ return Array.from(related);
1082
+ }
1083
+ function pickOwnerForCell(cell, current) {
1084
+ const point5 = turf3.pointOnFeature(cell);
1085
+ const containing = current.filter(({ feature }) => {
1086
+ try {
1087
+ return turf3.booleanPointInPolygon(point5, feature) || turf3.booleanWithin(point5, feature);
1088
+ } catch {
1089
+ return false;
1090
+ }
1091
+ });
1092
+ if (containing.length === 1) return containing[0];
1093
+ if (containing.length > 1) return containing[0];
1094
+ const cellBoundary = turf3.polygonToLine(cell);
1095
+ let best = null;
1096
+ current.forEach((entry) => {
1097
+ const score = getBoundaryContactLength(cellBoundary, entry.boundary);
1098
+ if (!best || score > best.score) {
1099
+ best = { entry, score };
1100
+ }
1101
+ });
1102
+ if (best && best.score > 0) return best.entry;
1103
+ let nearest = null;
1104
+ current.forEach((entry) => {
1105
+ try {
1106
+ const distance = turf3.pointToLineDistance(point5, entry.boundary);
1107
+ if (!nearest || distance < nearest.distance) {
1108
+ nearest = { entry, distance };
1109
+ }
1110
+ } catch {
1111
+ }
1112
+ });
1113
+ return nearest?.entry ?? null;
1114
+ }
1115
+ function getPolygonOverlapArea(first, second) {
1116
+ try {
1117
+ const firstBbox = turf3.bbox(first);
1118
+ const secondBbox = turf3.bbox(second);
1119
+ if (firstBbox[2] < secondBbox[0] || secondBbox[2] < firstBbox[0] || firstBbox[3] < secondBbox[1] || secondBbox[3] < firstBbox[1]) {
1120
+ return 0;
1121
+ }
1122
+ const intersection = turf3.intersect(
1123
+ turf3.featureCollection([first, second])
1124
+ );
1125
+ return intersection ? turf3.area(intersection) : 0;
1126
+ } catch {
1127
+ return 0;
1128
+ }
1129
+ }
1130
+ function getBoundaryContactLength(cellBoundary, boundary) {
1131
+ try {
1132
+ const overlaps = turf3.lineOverlap(cellBoundary, boundary, { tolerance: 1e-12 });
1133
+ return overlaps.features.reduce((sum, feature) => sum + turf3.length(feature), 0);
1134
+ } catch {
1135
+ return 0;
1136
+ }
1137
+ }
1138
+ function unionPolygonFeatures(features) {
1139
+ if (features.length === 0) return null;
1140
+ let merged = sanitizePolygonFeature(features[0]);
1141
+ if (!merged) return null;
1142
+ for (const feature of features.slice(1)) {
1143
+ const next = sanitizePolygonFeature(feature);
1144
+ if (!next) continue;
1145
+ try {
1146
+ const unioned = turf3.union(turf3.featureCollection([merged, next]));
1147
+ if (!unioned) continue;
1148
+ const sanitizedUnion = sanitizePolygonFeature(unioned);
1149
+ if (sanitizedUnion) merged = sanitizedUnion;
1150
+ } catch {
1151
+ return null;
1152
+ }
1153
+ }
1154
+ return merged;
1155
+ }
1156
+ function sanitizePolygonFeature(feature) {
1157
+ let cleaned;
1158
+ try {
1159
+ cleaned = turf3.cleanCoords(feature, { mutate: false });
1160
+ } catch {
1161
+ cleaned = {
1162
+ ...feature,
1163
+ geometry: clonePolygonGeometry(feature.geometry)
1164
+ };
1165
+ }
1166
+ if (cleaned.geometry.type === "Polygon") {
1167
+ const rings = cleaned.geometry.coordinates.map((ring) => normalizeRing(ring)).filter((ring) => isValidRing(ring));
1168
+ if (rings.length === 0) return null;
1169
+ return turf3.polygon(rings, cleaned.properties || {}, {
1170
+ bbox: cleaned.bbox,
1171
+ id: cleaned.id
1172
+ });
1173
+ }
1174
+ const polygons = cleaned.geometry.coordinates.map((polygon8) => polygon8.map((ring) => normalizeRing(ring)).filter((ring) => isValidRing(ring))).filter((polygon8) => polygon8.length > 0);
1175
+ if (polygons.length === 0) return null;
1176
+ return turf3.multiPolygon(polygons, cleaned.properties || {}, {
1177
+ bbox: cleaned.bbox,
1178
+ id: cleaned.id
1179
+ });
1180
+ }
1181
+ function clonePolygonGeometry(geometry) {
1182
+ if (geometry.type === "Polygon") {
1183
+ return {
1184
+ type: "Polygon",
1185
+ coordinates: geometry.coordinates.map((ring) => ring.map((coord) => [...coord]))
1186
+ };
1187
+ }
1188
+ return {
1189
+ type: "MultiPolygon",
1190
+ coordinates: geometry.coordinates.map(
1191
+ (polygon8) => polygon8.map((ring) => ring.map((coord) => [...coord]))
1192
+ )
1193
+ };
1194
+ }
1195
+ function normalizeRing(ring) {
1196
+ const deduped = removeSequentialDuplicateCoords(ring);
1197
+ if (deduped.length === 0) return deduped;
1198
+ const first = deduped[0];
1199
+ const last = deduped[deduped.length - 1];
1200
+ if (coordKey(first) !== coordKey(last)) {
1201
+ deduped.push([...first]);
1202
+ }
1203
+ return deduped;
1204
+ }
1205
+ function removeSequentialDuplicateCoords(coords) {
1206
+ const result = [];
1207
+ coords.forEach((coord) => {
1208
+ if (result.length === 0 || coordKey(result[result.length - 1]) !== coordKey(coord)) {
1209
+ result.push([...coord]);
1210
+ }
1211
+ });
1212
+ return result;
1213
+ }
1214
+ function isValidRing(ring) {
1215
+ if (ring.length < 4) return false;
1216
+ if (coordKey(ring[0]) !== coordKey(ring[ring.length - 1])) return false;
1217
+ const uniqueCoords = new Set(ring.slice(0, -1).map((coord) => coordKey(coord)));
1218
+ return uniqueCoords.size >= 3;
1219
+ }
1220
+ function coordKey(coord) {
1221
+ return `${coord[0].toFixed(12)},${coord[1].toFixed(12)}`;
1222
+ }
1223
+ function toLeafletLatLngs(geometry) {
1224
+ const levelsDeep = geometry.type === "Polygon" ? 1 : 2;
1225
+ return L12.GeoJSON.coordsToLatLngs(geometry.coordinates, levelsDeep);
1226
+ }
1227
+
1228
+ // src/modes/split-mode.ts
995
1229
  var SplitMode = class {
996
1230
  constructor(map, store, options = {}) {
997
1231
  this.map = map;
@@ -1005,13 +1239,13 @@ var SplitMode = class {
1005
1239
  enable() {
1006
1240
  this.map.on("click", this.onMapClick, this);
1007
1241
  this.map.on("mousemove", this.onMouseMove, this);
1008
- L12.DomEvent.on(window, "keydown", this.onKeyDown, this);
1242
+ L13.DomEvent.on(window, "keydown", this.onKeyDown, this);
1009
1243
  this.map.getContainer().style.cursor = "crosshair";
1010
1244
  }
1011
1245
  disable() {
1012
1246
  this.map.off("click", this.onMapClick, this);
1013
1247
  this.map.off("mousemove", this.onMouseMove, this);
1014
- L12.DomEvent.off(window, "keydown", this.onKeyDown, this);
1248
+ L13.DomEvent.off(window, "keydown", this.onKeyDown, this);
1015
1249
  this.map.getContainer().style.cursor = "";
1016
1250
  this.resetDrawing();
1017
1251
  }
@@ -1034,7 +1268,7 @@ var SplitMode = class {
1034
1268
  const snapLatLng = getSnapLatLng(this.map, e.latlng, this.store, this.options, this.points);
1035
1269
  const lastPoint = this.points[this.points.length - 1];
1036
1270
  if (!this.ghostLine) {
1037
- this.ghostLine = L12.polyline(
1271
+ this.ghostLine = L13.polyline(
1038
1272
  [lastPoint, snapLatLng],
1039
1273
  getModeGhostPathOptions(this.options, "split" /* Split */, {
1040
1274
  dashArray: "5, 5",
@@ -1055,7 +1289,7 @@ var SplitMode = class {
1055
1289
  if (this.polyline) {
1056
1290
  this.polyline.setLatLngs(this.points);
1057
1291
  } else {
1058
- this.polyline = L12.polyline(
1292
+ this.polyline = L13.polyline(
1059
1293
  this.points,
1060
1294
  getModePathOptions(this.options, "split" /* Split */, {
1061
1295
  color: "#ff3300",
@@ -1065,18 +1299,18 @@ var SplitMode = class {
1065
1299
  }
1066
1300
  const lastPoint = this.points[this.points.length - 1];
1067
1301
  if (this.markers.length === 0) {
1068
- const marker3 = L12.circleMarker(
1302
+ const marker2 = L13.circleMarker(
1069
1303
  lastPoint,
1070
1304
  getModeHandleOptions(this.options, "split" /* Split */, {
1071
1305
  radius: 5,
1072
1306
  color: "#ff3300"
1073
1307
  })
1074
1308
  ).addTo(this.map);
1075
- marker3.on("click", (e) => {
1076
- L12.DomEvent.stopPropagation(e);
1309
+ marker2.on("click", (e) => {
1310
+ L13.DomEvent.stopPropagation(e);
1077
1311
  this.finish();
1078
1312
  });
1079
- this.markers.push(marker3);
1313
+ this.markers.push(marker2);
1080
1314
  } else {
1081
1315
  this.markers[0].setLatLng(lastPoint);
1082
1316
  }
@@ -1086,13 +1320,12 @@ var SplitMode = class {
1086
1320
  this.resetDrawing();
1087
1321
  return;
1088
1322
  }
1089
- const lineGeo = L12.polyline(this.points).toGeoJSON();
1090
- const coords = turf3.getCoords(lineGeo);
1323
+ const lineGeo = L13.polyline(this.points).toGeoJSON();
1091
1324
  const layersToSplit = [];
1092
1325
  this.store.getGroup().eachLayer((layer) => {
1093
- if (layer instanceof L12.Polygon) {
1326
+ if (layer instanceof L13.Polygon) {
1094
1327
  const polyGeo = layer.toGeoJSON();
1095
- const intersections = turf3.lineIntersect(lineGeo, polyGeo);
1328
+ const intersections = turf4.lineIntersect(lineGeo, polyGeo);
1096
1329
  if (intersections.features.length >= 2) {
1097
1330
  layersToSplit.push(layer);
1098
1331
  }
@@ -1102,62 +1335,321 @@ var SplitMode = class {
1102
1335
  this.resetDrawing();
1103
1336
  return;
1104
1337
  }
1105
- const p1 = coords[0];
1106
- const p2 = coords[coords.length - 1];
1107
- const dx = p2[0] - p1[0];
1108
- const dy = p2[1] - p1[1];
1109
- const factor = 1e3;
1110
- const start = [p1[0] - dx * factor, p1[1] - dy * factor];
1111
- const end = [p2[0] + dx * factor, p2[1] + dy * factor];
1112
- const angle = Math.atan2(dy, dx);
1113
- const perp = angle + Math.PI / 2;
1114
- const width = 1e3;
1115
- const blade = turf3.polygon([[
1116
- start,
1117
- end,
1118
- [end[0] + Math.cos(perp) * width, end[1] + Math.sin(perp) * width],
1119
- [start[0] + Math.cos(perp) * width, start[1] + Math.sin(perp) * width],
1120
- start
1121
- ]]);
1122
- layersToSplit.forEach((polygon7) => {
1123
- const polyGeo = polygon7.toGeoJSON();
1124
- if (this.options.magnetic) {
1125
- const intersections = turf3.lineIntersect(lineGeo, polyGeo);
1126
- this.insertVerticesIntoNeighbors(intersections, polygon7);
1127
- }
1128
- const part1 = turf3.difference(turf3.featureCollection([polyGeo, blade]));
1129
- const part2 = turf3.intersect(turf3.featureCollection([polyGeo, blade]));
1130
- if (part1 && part2) {
1131
- const processResult = (result) => {
1132
- const flattened = turf3.flatten(result);
1133
- flattened.features.forEach((f) => {
1134
- const l = L12.geoJSON(f, {
1135
- style: getModePathOptions(this.options, "split" /* Split */)
1136
- }).getLayers()[0];
1137
- l.addTo(this.map);
1138
- this.map.fire(ANVIL_EVENTS.CREATED, { layer: l });
1139
- });
1140
- };
1141
- this.map.fire(ANVIL_EVENTS.DELETED, { layer: polygon7 });
1142
- this.map.removeLayer(polygon7);
1143
- processResult(part1);
1144
- processResult(part2);
1338
+ const repairCandidates = collectTouchingPolygons(this.store.getGroup(), layersToSplit);
1339
+ const coverageSnapshot = createCoverageSnapshot(repairCandidates);
1340
+ const deletedPolygons = /* @__PURE__ */ new Set();
1341
+ const createdPolygons = [];
1342
+ layersToSplit.forEach((polygon8) => {
1343
+ const polyGeo = polygon8.toGeoJSON();
1344
+ const sourceStyle = { ...polygon8.options };
1345
+ const intersections = turf4.lineIntersect(lineGeo, polyGeo);
1346
+ this.insertVerticesIntoNeighbors(intersections, polygon8);
1347
+ const parts = this.normalizeSplitParts(
1348
+ polyGeo,
1349
+ this.splitPolygonAlongLine(polyGeo, lineGeo)
1350
+ );
1351
+ if (parts.length >= 2) {
1352
+ deletedPolygons.add(polygon8);
1353
+ this.map.fire(ANVIL_EVENTS.DELETED, { layer: polygon8 });
1354
+ this.map.removeLayer(polygon8);
1355
+ parts.forEach((feature) => {
1356
+ const l = L13.geoJSON(feature, {
1357
+ style: sourceStyle
1358
+ }).getLayers()[0];
1359
+ l.addTo(this.map);
1360
+ createdPolygons.push(l);
1361
+ this.map.fire(ANVIL_EVENTS.CREATED, { layer: l });
1362
+ });
1145
1363
  }
1146
1364
  });
1365
+ if (coverageSnapshot) {
1366
+ const currentRepairLayers = [
1367
+ ...repairCandidates.filter((layer) => !deletedPolygons.has(layer) && this.map.hasLayer(layer)),
1368
+ ...createdPolygons
1369
+ ].filter((layer, index, arr) => arr.indexOf(layer) === index);
1370
+ repairPolygonCoverage(currentRepairLayers, coverageSnapshot).forEach((layer) => this.map.fire(ANVIL_EVENTS.EDITED, { layer }));
1371
+ }
1147
1372
  this.resetDrawing();
1148
1373
  }
1374
+ splitPolygonAlongLine(polygon8, line) {
1375
+ const sanitizedPolygon = this.sanitizePolygonFeature(polygon8);
1376
+ const sanitizedLine = this.sanitizeLineStringFeature(line);
1377
+ if (!sanitizedPolygon || !sanitizedLine) return [];
1378
+ const boundaryLines = this.getBoundaryLines(sanitizedPolygon);
1379
+ if (boundaryLines.length === 0) return [];
1380
+ const intersectionCoords = this.getIntersectionCoords(sanitizedLine, boundaryLines);
1381
+ if (intersectionCoords.length < 2) return [];
1382
+ const splitter = turf4.multiPoint(intersectionCoords);
1383
+ const splitBoundary = [];
1384
+ boundaryLines.forEach((boundaryLine) => {
1385
+ turf4.lineSplit(boundaryLine, splitter).features.forEach((segment) => {
1386
+ const sanitizedSegment = this.sanitizeLineStringFeature(segment);
1387
+ if (sanitizedSegment) splitBoundary.push(sanitizedSegment);
1388
+ });
1389
+ });
1390
+ const splitLine = turf4.lineSplit(sanitizedLine, splitter);
1391
+ const innerSegments = splitLine.features.map((segment) => this.sanitizeLineStringFeature(segment)).filter((segment) => Boolean(segment)).filter((segment) => this.isSegmentInsidePolygon(segment, sanitizedPolygon));
1392
+ if (innerSegments.length === 0) return [];
1393
+ try {
1394
+ const polygonized = turf4.polygonize(
1395
+ turf4.featureCollection([
1396
+ ...splitBoundary,
1397
+ ...innerSegments
1398
+ ])
1399
+ );
1400
+ return polygonized.features.map((feature) => this.sanitizePolygonFeature(feature)).filter((feature) => Boolean(feature) && feature.geometry.type === "Polygon").filter((feature) => this.isPolygonInsideOriginal(feature, sanitizedPolygon));
1401
+ } catch {
1402
+ return [];
1403
+ }
1404
+ }
1405
+ normalizeSplitParts(target, parts) {
1406
+ const sanitizedTarget = this.sanitizePolygonFeature(target);
1407
+ if (!sanitizedTarget) return [];
1408
+ const rawParts = parts.map((part) => this.sanitizePolygonFeature(part)).filter((part) => Boolean(part) && part.geometry.type === "Polygon");
1409
+ if (rawParts.length < 2) return rawParts;
1410
+ const assigned = rawParts.map(() => []);
1411
+ let remainder = sanitizedTarget;
1412
+ rawParts.forEach((part, index) => {
1413
+ if (!remainder) return;
1414
+ const clipped = this.intersectPolygonFeatures(part, remainder);
1415
+ assigned[index].push(...clipped);
1416
+ remainder = this.subtractPolygonFeature(remainder, part);
1417
+ });
1418
+ if (remainder) {
1419
+ this.flattenPolygonFeatures(remainder).forEach((gap) => {
1420
+ const ownerIndex = this.pickBestPartIndex(gap, rawParts);
1421
+ if (ownerIndex >= 0) {
1422
+ assigned[ownerIndex].push(gap);
1423
+ }
1424
+ });
1425
+ }
1426
+ return assigned.flatMap((cells) => {
1427
+ if (cells.length === 0) return [];
1428
+ const merged = this.unionPolygonFeatures(cells);
1429
+ if (!merged) return [];
1430
+ return this.flattenPolygonFeatures(merged);
1431
+ });
1432
+ }
1433
+ isSegmentInsidePolygon(segment, polygon8) {
1434
+ const coords = segment.geometry.coordinates;
1435
+ if (coords.length < 2) return false;
1436
+ const midpoint2 = turf4.midpoint(
1437
+ turf4.point(coords[0]),
1438
+ turf4.point(coords[coords.length - 1])
1439
+ );
1440
+ return turf4.booleanWithin(midpoint2, polygon8) || turf4.booleanPointInPolygon(midpoint2, polygon8);
1441
+ }
1442
+ isPolygonInsideOriginal(candidate, original) {
1443
+ const point5 = turf4.pointOnFeature(candidate);
1444
+ return turf4.booleanWithin(point5, original) || turf4.booleanPointInPolygon(point5, original);
1445
+ }
1446
+ intersectPolygonFeatures(first, second) {
1447
+ try {
1448
+ const intersection = turf4.intersect(
1449
+ turf4.featureCollection([first, second])
1450
+ );
1451
+ if (!intersection) return [];
1452
+ return this.flattenPolygonFeatures(intersection);
1453
+ } catch {
1454
+ return [];
1455
+ }
1456
+ }
1457
+ subtractPolygonFeature(base, subtractor) {
1458
+ try {
1459
+ const diff = turf4.difference(
1460
+ turf4.featureCollection([base, subtractor])
1461
+ );
1462
+ if (!diff) return null;
1463
+ return this.sanitizePolygonFeature(diff);
1464
+ } catch {
1465
+ return base;
1466
+ }
1467
+ }
1468
+ unionPolygonFeatures(features) {
1469
+ if (features.length === 0) return null;
1470
+ let merged = this.sanitizePolygonFeature(features[0]);
1471
+ if (!merged) return null;
1472
+ for (const feature of features.slice(1)) {
1473
+ const next = this.sanitizePolygonFeature(feature);
1474
+ if (!next) continue;
1475
+ try {
1476
+ const unioned = turf4.union(turf4.featureCollection([merged, next]));
1477
+ if (!unioned) continue;
1478
+ const sanitizedUnion = this.sanitizePolygonFeature(unioned);
1479
+ if (sanitizedUnion) merged = sanitizedUnion;
1480
+ } catch {
1481
+ return null;
1482
+ }
1483
+ }
1484
+ return merged;
1485
+ }
1486
+ flattenPolygonFeatures(feature) {
1487
+ const polygons = [];
1488
+ turf4.flattenEach(feature, (flattened) => {
1489
+ const sanitized = this.sanitizePolygonFeature(flattened);
1490
+ if (sanitized && sanitized.geometry.type === "Polygon") {
1491
+ polygons.push(sanitized);
1492
+ }
1493
+ });
1494
+ return polygons;
1495
+ }
1496
+ pickBestPartIndex(cell, parts) {
1497
+ const cellBoundary = turf4.polygonToLine(cell);
1498
+ let bestIndex = -1;
1499
+ let bestScore = -1;
1500
+ parts.forEach((part, index) => {
1501
+ const boundary = turf4.polygonToLine(part);
1502
+ const score = this.getBoundaryContactLength(cellBoundary, boundary);
1503
+ if (score > bestScore) {
1504
+ bestScore = score;
1505
+ bestIndex = index;
1506
+ }
1507
+ });
1508
+ if (bestScore > 0) return bestIndex;
1509
+ const point5 = turf4.pointOnFeature(cell);
1510
+ let nearestIndex = -1;
1511
+ let nearestDistance = Infinity;
1512
+ parts.forEach((part, index) => {
1513
+ try {
1514
+ const boundary = turf4.polygonToLine(part);
1515
+ const distance = turf4.pointToLineDistance(point5, boundary);
1516
+ if (distance < nearestDistance) {
1517
+ nearestDistance = distance;
1518
+ nearestIndex = index;
1519
+ }
1520
+ } catch {
1521
+ }
1522
+ });
1523
+ return nearestIndex;
1524
+ }
1525
+ getBoundaryContactLength(first, second) {
1526
+ try {
1527
+ const overlaps = turf4.lineOverlap(first, second, { tolerance: 1e-12 });
1528
+ return overlaps.features.reduce((sum, feature) => sum + turf4.length(feature), 0);
1529
+ } catch {
1530
+ return 0;
1531
+ }
1532
+ }
1533
+ getBoundaryLines(polygon8) {
1534
+ const boundary = turf4.polygonToLine(polygon8);
1535
+ const lines = [];
1536
+ turf4.flattenEach(boundary, (feature) => {
1537
+ const sanitized = this.sanitizeLineStringFeature(feature);
1538
+ if (sanitized) lines.push(sanitized);
1539
+ });
1540
+ return lines;
1541
+ }
1542
+ getIntersectionCoords(line, boundaryLines) {
1543
+ const intersections = /* @__PURE__ */ new Map();
1544
+ boundaryLines.forEach((boundaryLine) => {
1545
+ turf4.lineIntersect(line, boundaryLine).features.forEach((feature) => {
1546
+ const coord = turf4.getCoord(feature);
1547
+ intersections.set(this.coordKey(coord), coord);
1548
+ });
1549
+ });
1550
+ return Array.from(intersections.values());
1551
+ }
1552
+ sanitizeLineStringFeature(feature) {
1553
+ let cleaned;
1554
+ try {
1555
+ cleaned = turf4.cleanCoords(feature, { mutate: false });
1556
+ } catch {
1557
+ cleaned = {
1558
+ ...feature,
1559
+ geometry: {
1560
+ ...feature.geometry,
1561
+ coordinates: feature.geometry.coordinates.map((coord) => [...coord])
1562
+ }
1563
+ };
1564
+ }
1565
+ if (cleaned.geometry.type !== "LineString") return null;
1566
+ const coordinates = this.removeSequentialDuplicateCoords(cleaned.geometry.coordinates);
1567
+ const uniqueCoords = new Set(coordinates.map((coord) => this.coordKey(coord)));
1568
+ if (coordinates.length < 2 || uniqueCoords.size < 2) return null;
1569
+ return turf4.lineString(coordinates, cleaned.properties || {}, {
1570
+ bbox: cleaned.bbox,
1571
+ id: cleaned.id
1572
+ });
1573
+ }
1574
+ sanitizePolygonFeature(feature) {
1575
+ let cleaned;
1576
+ try {
1577
+ cleaned = turf4.cleanCoords(feature, { mutate: false });
1578
+ } catch {
1579
+ cleaned = {
1580
+ ...feature,
1581
+ geometry: this.clonePolygonGeometry(feature.geometry)
1582
+ };
1583
+ }
1584
+ if (cleaned.geometry.type === "Polygon") {
1585
+ const rings = cleaned.geometry.coordinates.map((ring) => this.normalizeRing(ring)).filter((ring) => this.isValidRing(ring));
1586
+ if (rings.length === 0) return null;
1587
+ return turf4.polygon(rings, cleaned.properties || {}, {
1588
+ bbox: cleaned.bbox,
1589
+ id: cleaned.id
1590
+ });
1591
+ }
1592
+ const polygons = cleaned.geometry.coordinates.map((polygon8) => polygon8.map((ring) => this.normalizeRing(ring)).filter((ring) => this.isValidRing(ring))).filter((polygon8) => polygon8.length > 0);
1593
+ if (polygons.length === 0) return null;
1594
+ return turf4.multiPolygon(polygons, cleaned.properties || {}, {
1595
+ bbox: cleaned.bbox,
1596
+ id: cleaned.id
1597
+ });
1598
+ }
1599
+ normalizeRing(ring) {
1600
+ const deduped = this.removeSequentialDuplicateCoords(ring);
1601
+ if (deduped.length === 0) return deduped;
1602
+ const first = deduped[0];
1603
+ const last = deduped[deduped.length - 1];
1604
+ if (this.coordKey(first) !== this.coordKey(last)) {
1605
+ deduped.push([...first]);
1606
+ }
1607
+ return deduped;
1608
+ }
1609
+ removeSequentialDuplicateCoords(coords) {
1610
+ const result = [];
1611
+ coords.forEach((coord) => {
1612
+ if (result.length === 0 || this.coordKey(result[result.length - 1]) !== this.coordKey(coord)) {
1613
+ result.push([...coord]);
1614
+ }
1615
+ });
1616
+ return result;
1617
+ }
1618
+ isValidRing(ring) {
1619
+ if (ring.length < 4) return false;
1620
+ if (this.coordKey(ring[0]) !== this.coordKey(ring[ring.length - 1])) return false;
1621
+ const uniqueCoords = new Set(ring.slice(0, -1).map((coord) => this.coordKey(coord)));
1622
+ return uniqueCoords.size >= 3;
1623
+ }
1624
+ coordKey(coord) {
1625
+ return `${coord[0].toFixed(12)},${coord[1].toFixed(12)}`;
1626
+ }
1627
+ clonePolygonGeometry(geometry) {
1628
+ if (geometry.type === "Polygon") {
1629
+ return {
1630
+ type: "Polygon",
1631
+ coordinates: geometry.coordinates.map((ring) => ring.map((coord) => [...coord]))
1632
+ };
1633
+ }
1634
+ return {
1635
+ type: "MultiPolygon",
1636
+ coordinates: geometry.coordinates.map(
1637
+ (polygon8) => polygon8.map((ring) => ring.map((coord) => [...coord]))
1638
+ )
1639
+ };
1640
+ }
1149
1641
  insertVerticesIntoNeighbors(intersections, activePolygon) {
1150
- const intersectionCoords = intersections.features.map((f) => turf3.getCoord(f));
1642
+ const intersectionCoords = intersections.features.map((f) => turf4.getCoord(f));
1151
1643
  const layers = this.store.getGroup().getLayers();
1152
1644
  layers.forEach((layer) => {
1153
- if (!(layer instanceof L12.Polyline) || layer === activePolygon) return;
1645
+ if (!(layer instanceof L13.Polyline) || layer === activePolygon) return;
1154
1646
  const latlngs = layer.getLatLngs();
1155
1647
  let changed = false;
1156
1648
  const processArr = (arr) => {
1157
1649
  if (arr.length === 0) return;
1158
- if (arr[0] instanceof L12.LatLng || typeof arr[0].lat === "number") {
1650
+ if (arr[0] instanceof L13.LatLng || typeof arr[0].lat === "number") {
1159
1651
  const ring = arr;
1160
- const isPolygon = layer instanceof L12.Polygon;
1652
+ const isPolygon = layer instanceof L13.Polygon;
1161
1653
  const ringLen = ring.length;
1162
1654
  for (let i = ringLen - 1; i >= 0; i--) {
1163
1655
  if (i === ringLen - 1 && !isPolygon) continue;
@@ -1166,9 +1658,9 @@ var SplitMode = class {
1166
1658
  const a = this.map.latLngToContainerPoint(p1);
1167
1659
  const b = this.map.latLngToContainerPoint(p2);
1168
1660
  intersectionCoords.forEach((coord) => {
1169
- const intersectLL = L12.latLng(coord[1], coord[0]);
1661
+ const intersectLL = L13.latLng(coord[1], coord[0]);
1170
1662
  const p = this.map.latLngToContainerPoint(intersectLL);
1171
- const closest = L12.LineUtil.closestPointOnSegment(p, a, b);
1663
+ const closest = L13.LineUtil.closestPointOnSegment(p, a, b);
1172
1664
  if (p.distanceTo(closest) < 1) {
1173
1665
  const alreadyExists = ring.some(
1174
1666
  (ll) => this.map.latLngToContainerPoint(ll).distanceTo(p) < 1
@@ -1204,7 +1696,7 @@ var SplitMode = class {
1204
1696
  };
1205
1697
 
1206
1698
  // src/modes/drag-mode.ts
1207
- var L13 = __toESM(require("leaflet"));
1699
+ var L14 = __toESM(require("leaflet"));
1208
1700
  var DragMode = class {
1209
1701
  constructor(map, store, options = {}) {
1210
1702
  this.map = map;
@@ -1235,31 +1727,31 @@ var DragMode = class {
1235
1727
  this.stopDragging();
1236
1728
  }
1237
1729
  addLayerListener(layer) {
1238
- if (layer instanceof L13.Path || layer instanceof L13.Marker) {
1730
+ if (layer instanceof L14.Path || layer instanceof L14.Marker) {
1239
1731
  layer.on("mousedown", this.onMouseDown, this);
1240
1732
  layer.getElement()?.style.setProperty("cursor", "move");
1241
1733
  }
1242
1734
  }
1243
1735
  removeLayerListener(layer) {
1244
- if (layer instanceof L13.Path || layer instanceof L13.Marker) {
1736
+ if (layer instanceof L14.Path || layer instanceof L14.Marker) {
1245
1737
  layer.off("mousedown", this.onMouseDown, this);
1246
1738
  layer.getElement()?.style.setProperty("cursor", "");
1247
1739
  }
1248
1740
  }
1249
1741
  onMouseDown(e) {
1250
- L13.DomEvent.stopPropagation(e);
1742
+ L14.DomEvent.stopPropagation(e);
1251
1743
  const layer = e.target;
1252
1744
  if (!this.store.hasLayer(layer)) {
1253
1745
  return;
1254
1746
  }
1255
1747
  this.draggingLayer = layer;
1256
1748
  this.startLatLng = e.latlng;
1257
- if (this.draggingLayer instanceof L13.Marker) {
1749
+ if (this.draggingLayer instanceof L14.Marker) {
1258
1750
  this.initialLatLngs = this.draggingLayer.getLatLng();
1259
- } else if (this.draggingLayer instanceof L13.Path) {
1260
- if (this.draggingLayer instanceof L13.Circle) {
1751
+ } else if (this.draggingLayer instanceof L14.Path) {
1752
+ if (this.draggingLayer instanceof L14.Circle) {
1261
1753
  this.initialLatLngs = this.draggingLayer.getLatLng();
1262
- } else if (this.draggingLayer instanceof L13.Polyline) {
1754
+ } else if (this.draggingLayer instanceof L14.Polyline) {
1263
1755
  this.initialLatLngs = JSON.parse(JSON.stringify(this.draggingLayer.getLatLngs()));
1264
1756
  }
1265
1757
  this.originalPathStyle = { ...this.draggingLayer.options };
@@ -1286,10 +1778,10 @@ var DragMode = class {
1286
1778
  );
1287
1779
  const deltaLat = currentLatLng.lat - this.startLatLng.lat;
1288
1780
  const deltaLng = currentLatLng.lng - this.startLatLng.lng;
1289
- if (this.draggingLayer instanceof L13.Marker || this.draggingLayer instanceof L13.Circle) {
1781
+ if (this.draggingLayer instanceof L14.Marker || this.draggingLayer instanceof L14.Circle) {
1290
1782
  const start = this.initialLatLngs;
1291
1783
  this.draggingLayer.setLatLng([start.lat + deltaLat, start.lng + deltaLng]);
1292
- } else if (this.draggingLayer instanceof L13.Polyline) {
1784
+ } else if (this.draggingLayer instanceof L14.Polyline) {
1293
1785
  const newLatLngs = this.moveLatLngs(this.initialLatLngs, deltaLat, deltaLng);
1294
1786
  this.draggingLayer.setLatLngs(newLatLngs);
1295
1787
  }
@@ -1298,11 +1790,11 @@ var DragMode = class {
1298
1790
  if (Array.isArray(latlngs)) {
1299
1791
  return latlngs.map((item) => this.moveLatLngs(item, deltaLat, deltaLng));
1300
1792
  }
1301
- return L13.latLng(latlngs.lat + deltaLat, latlngs.lng + deltaLng);
1793
+ return L14.latLng(latlngs.lat + deltaLat, latlngs.lng + deltaLng);
1302
1794
  }
1303
1795
  onMouseUp() {
1304
1796
  if (this.draggingLayer) {
1305
- if (this.draggingLayer instanceof L13.Path) {
1797
+ if (this.draggingLayer instanceof L14.Path) {
1306
1798
  this.draggingLayer.setStyle(this.originalPathStyle || {});
1307
1799
  }
1308
1800
  this.map.fire(ANVIL_EVENTS.EDITED, { layer: this.draggingLayer });
@@ -1313,7 +1805,7 @@ var DragMode = class {
1313
1805
  this.map.off("mousemove", this.onMouseMove, this);
1314
1806
  this.map.off("mouseup", this.onMouseUp, this);
1315
1807
  this.map.dragging.enable();
1316
- if (this.draggingLayer instanceof L13.Path && this.originalPathStyle) {
1808
+ if (this.draggingLayer instanceof L14.Path && this.originalPathStyle) {
1317
1809
  this.draggingLayer.setStyle(this.originalPathStyle);
1318
1810
  }
1319
1811
  this.draggingLayer = null;
@@ -1324,7 +1816,7 @@ var DragMode = class {
1324
1816
  };
1325
1817
 
1326
1818
  // src/modes/scale-mode.ts
1327
- var L14 = __toESM(require("leaflet"));
1819
+ var L15 = __toESM(require("leaflet"));
1328
1820
  var ScaleMode = class {
1329
1821
  constructor(map, store, options = {}) {
1330
1822
  this.map = map;
@@ -1339,7 +1831,7 @@ var ScaleMode = class {
1339
1831
  }
1340
1832
  enable() {
1341
1833
  this.store.getGroup().eachLayer((layer) => {
1342
- if (layer instanceof L14.Path) {
1834
+ if (layer instanceof L15.Path) {
1343
1835
  layer.on("mousedown", this.onMouseDown, this);
1344
1836
  layer.getElement()?.style.setProperty("cursor", "nwse-resize");
1345
1837
  }
@@ -1347,7 +1839,7 @@ var ScaleMode = class {
1347
1839
  }
1348
1840
  disable() {
1349
1841
  this.store.getGroup().eachLayer((layer) => {
1350
- if (layer instanceof L14.Path) {
1842
+ if (layer instanceof L15.Path) {
1351
1843
  layer.off("mousedown", this.onMouseDown, this);
1352
1844
  layer.getElement()?.style.setProperty("cursor", "");
1353
1845
  }
@@ -1355,17 +1847,17 @@ var ScaleMode = class {
1355
1847
  this.stopScaling();
1356
1848
  }
1357
1849
  onMouseDown(e) {
1358
- L14.DomEvent.stopPropagation(e);
1850
+ L15.DomEvent.stopPropagation(e);
1359
1851
  const layer = e.target;
1360
1852
  if (!this.store.hasLayer(layer)) {
1361
1853
  return;
1362
1854
  }
1363
1855
  this.selectedLayer = layer;
1364
- if (this.selectedLayer instanceof L14.Circle) {
1856
+ if (this.selectedLayer instanceof L15.Circle) {
1365
1857
  this.centerLatLng = this.selectedLayer.getLatLng();
1366
1858
  this.initialRadius = this.selectedLayer.getRadius();
1367
1859
  }
1368
- if (this.selectedLayer instanceof L14.Path) {
1860
+ if (this.selectedLayer instanceof L15.Path) {
1369
1861
  this.originalPathStyle = { ...this.selectedLayer.options };
1370
1862
  this.selectedLayer.setStyle(
1371
1863
  getModeSelectionPathOptions(this.options, "scale" /* Scale */, this.originalPathStyle, {
@@ -1373,7 +1865,7 @@ var ScaleMode = class {
1373
1865
  color: "#ffcc00"
1374
1866
  })
1375
1867
  );
1376
- if (!(this.selectedLayer instanceof L14.Circle)) {
1868
+ if (!(this.selectedLayer instanceof L15.Circle)) {
1377
1869
  const bounds = this.selectedLayer.getBounds();
1378
1870
  this.centerLatLng = bounds.getCenter();
1379
1871
  this.initialLatLngs = JSON.parse(JSON.stringify(this.selectedLayer.getLatLngs()));
@@ -1390,9 +1882,9 @@ var ScaleMode = class {
1390
1882
  if (!this.selectedLayer || !this.centerLatLng || this.initialDistance === 0) return;
1391
1883
  const currentDistance = this.map.distance(this.centerLatLng, e.latlng);
1392
1884
  const ratio = currentDistance / this.initialDistance;
1393
- if (this.selectedLayer instanceof L14.Circle) {
1885
+ if (this.selectedLayer instanceof L15.Circle) {
1394
1886
  this.selectedLayer.setRadius(this.initialRadius * ratio);
1395
- } else if (this.selectedLayer instanceof L14.Path) {
1887
+ } else if (this.selectedLayer instanceof L15.Path) {
1396
1888
  const newLatLngs = this.scaleLatLngs(this.initialLatLngs, this.centerLatLng, ratio);
1397
1889
  this.selectedLayer.setLatLngs(newLatLngs);
1398
1890
  }
@@ -1403,11 +1895,11 @@ var ScaleMode = class {
1403
1895
  }
1404
1896
  const lat = center.lat + (latlngs.lat - center.lat) * ratio;
1405
1897
  const lng = center.lng + (latlngs.lng - center.lng) * ratio;
1406
- return L14.latLng(lat, lng);
1898
+ return L15.latLng(lat, lng);
1407
1899
  }
1408
1900
  onMouseUp() {
1409
1901
  if (this.selectedLayer) {
1410
- if (this.selectedLayer instanceof L14.Path) {
1902
+ if (this.selectedLayer instanceof L15.Path) {
1411
1903
  this.selectedLayer.setStyle(this.originalPathStyle || {});
1412
1904
  }
1413
1905
  this.map.fire(ANVIL_EVENTS.EDITED, { layer: this.selectedLayer });
@@ -1418,7 +1910,7 @@ var ScaleMode = class {
1418
1910
  this.map.off("mousemove", this.onMouseMove, this);
1419
1911
  this.map.off("mouseup", this.onMouseUp, this);
1420
1912
  this.map.dragging.enable();
1421
- if (this.selectedLayer instanceof L14.Path && this.originalPathStyle) {
1913
+ if (this.selectedLayer instanceof L15.Path && this.originalPathStyle) {
1422
1914
  this.selectedLayer.setStyle(this.originalPathStyle);
1423
1915
  }
1424
1916
  this.selectedLayer = null;
@@ -1429,7 +1921,7 @@ var ScaleMode = class {
1429
1921
  };
1430
1922
 
1431
1923
  // src/modes/rotate-mode.ts
1432
- var L15 = __toESM(require("leaflet"));
1924
+ var L16 = __toESM(require("leaflet"));
1433
1925
  var RotateMode = class {
1434
1926
  constructor(map, store, options = {}) {
1435
1927
  this.map = map;
@@ -1443,7 +1935,7 @@ var RotateMode = class {
1443
1935
  }
1444
1936
  enable() {
1445
1937
  this.store.getGroup().eachLayer((layer) => {
1446
- if (layer instanceof L15.Path) {
1938
+ if (layer instanceof L16.Path) {
1447
1939
  layer.on("mousedown", this.onMouseDown, this);
1448
1940
  layer.getElement()?.style.setProperty("cursor", "crosshair");
1449
1941
  }
@@ -1451,7 +1943,7 @@ var RotateMode = class {
1451
1943
  }
1452
1944
  disable() {
1453
1945
  this.store.getGroup().eachLayer((layer) => {
1454
- if (layer instanceof L15.Path) {
1946
+ if (layer instanceof L16.Path) {
1455
1947
  layer.off("mousedown", this.onMouseDown, this);
1456
1948
  layer.getElement()?.style.setProperty("cursor", "");
1457
1949
  }
@@ -1459,9 +1951,9 @@ var RotateMode = class {
1459
1951
  this.stopRotating();
1460
1952
  }
1461
1953
  onMouseDown(e) {
1462
- L15.DomEvent.stopPropagation(e);
1954
+ L16.DomEvent.stopPropagation(e);
1463
1955
  const layer = e.target;
1464
- if (!this.store.hasLayer(layer) || !(layer instanceof L15.Path) || layer instanceof L15.Circle) {
1956
+ if (!this.store.hasLayer(layer) || !(layer instanceof L16.Path) || layer instanceof L16.Circle) {
1465
1957
  return;
1466
1958
  }
1467
1959
  const pathLayer = layer;
@@ -1470,9 +1962,9 @@ var RotateMode = class {
1470
1962
  this.centerLatLng = bounds.getCenter();
1471
1963
  this.initialLatLngs = JSON.parse(JSON.stringify(pathLayer.getLatLngs()));
1472
1964
  this.originalPathStyle = { ...pathLayer.options };
1473
- const point2 = this.map.latLngToContainerPoint(e.latlng);
1965
+ const point5 = this.map.latLngToContainerPoint(e.latlng);
1474
1966
  const centerPoint = this.map.latLngToContainerPoint(this.centerLatLng);
1475
- this.startAngle = Math.atan2(point2.y - centerPoint.y, point2.x - centerPoint.x);
1967
+ this.startAngle = Math.atan2(point5.y - centerPoint.y, point5.x - centerPoint.x);
1476
1968
  pathLayer.setStyle(
1477
1969
  getModeSelectionPathOptions(this.options, "rotate" /* Rotate */, this.originalPathStyle, {
1478
1970
  weight: 4,
@@ -1485,9 +1977,9 @@ var RotateMode = class {
1485
1977
  }
1486
1978
  onMouseMove(e) {
1487
1979
  if (!this.selectedLayer || !this.centerLatLng) return;
1488
- const point2 = this.map.latLngToContainerPoint(e.latlng);
1980
+ const point5 = this.map.latLngToContainerPoint(e.latlng);
1489
1981
  const centerPoint = this.map.latLngToContainerPoint(this.centerLatLng);
1490
- const currentAngle = Math.atan2(point2.y - centerPoint.y, point2.x - centerPoint.x);
1982
+ const currentAngle = Math.atan2(point5.y - centerPoint.y, point5.x - centerPoint.x);
1491
1983
  const rotationAngle = currentAngle - this.startAngle;
1492
1984
  const newLatLngs = this.rotateLatLngs(this.initialLatLngs, this.centerLatLng, rotationAngle);
1493
1985
  this.selectedLayer.setLatLngs(newLatLngs);
@@ -1496,12 +1988,12 @@ var RotateMode = class {
1496
1988
  if (Array.isArray(latlngs)) {
1497
1989
  return latlngs.map((item) => this.rotateLatLngs(item, center, angle));
1498
1990
  }
1499
- const point2 = this.map.latLngToContainerPoint(latlngs);
1991
+ const point5 = this.map.latLngToContainerPoint(latlngs);
1500
1992
  const centerPoint = this.map.latLngToContainerPoint(center);
1501
1993
  const cos = Math.cos(angle);
1502
1994
  const sin = Math.sin(angle);
1503
- const dx = point2.x - centerPoint.x;
1504
- const dy = point2.y - centerPoint.y;
1995
+ const dx = point5.x - centerPoint.x;
1996
+ const dy = point5.y - centerPoint.y;
1505
1997
  const nx = dx * cos - dy * sin + centerPoint.x;
1506
1998
  const ny = dx * sin + dy * cos + centerPoint.y;
1507
1999
  return this.map.containerPointToLatLng([nx, ny]);
@@ -1528,8 +2020,8 @@ var RotateMode = class {
1528
2020
  };
1529
2021
 
1530
2022
  // src/modes/union-mode.ts
1531
- var L16 = __toESM(require("leaflet"));
1532
- var turf4 = __toESM(require("@turf/turf"));
2023
+ var L17 = __toESM(require("leaflet"));
2024
+ var turf5 = __toESM(require("@turf/turf"));
1533
2025
  var UnionMode = class {
1534
2026
  constructor(map, store, options = {}) {
1535
2027
  this.map = map;
@@ -1540,7 +2032,7 @@ var UnionMode = class {
1540
2032
  }
1541
2033
  enable() {
1542
2034
  this.store.getGroup().eachLayer((layer) => {
1543
- if (layer instanceof L16.Polygon) {
2035
+ if (layer instanceof L17.Polygon) {
1544
2036
  layer.on("click", this.onLayerClick, this);
1545
2037
  layer.getElement()?.style.setProperty("cursor", "pointer");
1546
2038
  }
@@ -1548,7 +2040,7 @@ var UnionMode = class {
1548
2040
  }
1549
2041
  disable() {
1550
2042
  this.store.getGroup().eachLayer((layer) => {
1551
- if (layer instanceof L16.Polygon) {
2043
+ if (layer instanceof L17.Polygon) {
1552
2044
  layer.off("click", this.onLayerClick, this);
1553
2045
  layer.getElement()?.style.setProperty("cursor", "");
1554
2046
  }
@@ -1556,7 +2048,7 @@ var UnionMode = class {
1556
2048
  this.reset();
1557
2049
  }
1558
2050
  onLayerClick(e) {
1559
- L16.DomEvent.stopPropagation(e);
2051
+ L17.DomEvent.stopPropagation(e);
1560
2052
  const layer = e.target;
1561
2053
  if (!this.firstLayer) {
1562
2054
  this.firstLayer = layer;
@@ -1575,7 +2067,7 @@ var UnionMode = class {
1575
2067
  }
1576
2068
  const g1 = this.firstLayer.toGeoJSON();
1577
2069
  const g2 = layer.toGeoJSON();
1578
- const united = turf4.union(turf4.featureCollection([g1, g2]));
2070
+ const united = turf5.union(turf5.featureCollection([g1, g2]));
1579
2071
  if (!united) {
1580
2072
  console.error("Union failed - results null");
1581
2073
  this.reset();
@@ -1585,9 +2077,9 @@ var UnionMode = class {
1585
2077
  this.map.fire(ANVIL_EVENTS.DELETED, { layer });
1586
2078
  this.map.removeLayer(this.firstLayer);
1587
2079
  this.map.removeLayer(layer);
1588
- const flattened = turf4.flatten(united);
2080
+ const flattened = turf5.flatten(united);
1589
2081
  flattened.features.forEach((f) => {
1590
- const newLayerGroup = L16.geoJSON(f, {
2082
+ const newLayerGroup = L17.geoJSON(f, {
1591
2083
  style: getModePathOptions(this.options, "union" /* Union */)
1592
2084
  });
1593
2085
  const l = newLayerGroup.getLayers()[0];
@@ -1607,8 +2099,8 @@ var UnionMode = class {
1607
2099
  };
1608
2100
 
1609
2101
  // src/modes/subtract-mode.ts
1610
- var L17 = __toESM(require("leaflet"));
1611
- var turf5 = __toESM(require("@turf/turf"));
2102
+ var L18 = __toESM(require("leaflet"));
2103
+ var turf6 = __toESM(require("@turf/turf"));
1612
2104
  var SubtractMode = class {
1613
2105
  constructor(map, store, options = {}) {
1614
2106
  this.map = map;
@@ -1619,7 +2111,7 @@ var SubtractMode = class {
1619
2111
  }
1620
2112
  enable() {
1621
2113
  this.store.getGroup().eachLayer((layer) => {
1622
- if (layer instanceof L17.Polygon) {
2114
+ if (layer instanceof L18.Polygon) {
1623
2115
  layer.on("click", this.onLayerClick, this);
1624
2116
  layer.getElement()?.style.setProperty("cursor", "pointer");
1625
2117
  }
@@ -1627,7 +2119,7 @@ var SubtractMode = class {
1627
2119
  }
1628
2120
  disable() {
1629
2121
  this.store.getGroup().eachLayer((layer) => {
1630
- if (layer instanceof L17.Polygon) {
2122
+ if (layer instanceof L18.Polygon) {
1631
2123
  layer.off("click", this.onLayerClick, this);
1632
2124
  layer.getElement()?.style.setProperty("cursor", "");
1633
2125
  }
@@ -1635,7 +2127,7 @@ var SubtractMode = class {
1635
2127
  this.reset();
1636
2128
  }
1637
2129
  onLayerClick(e) {
1638
- L17.DomEvent.stopPropagation(e);
2130
+ L18.DomEvent.stopPropagation(e);
1639
2131
  const layer = e.target;
1640
2132
  if (!this.baseLayer) {
1641
2133
  this.baseLayer = layer;
@@ -1654,15 +2146,15 @@ var SubtractMode = class {
1654
2146
  }
1655
2147
  const g1 = this.baseLayer.toGeoJSON();
1656
2148
  const g2 = layer.toGeoJSON();
1657
- const diff = turf5.difference(turf5.featureCollection([g1, g2]));
2149
+ const diff = turf6.difference(turf6.featureCollection([g1, g2]));
1658
2150
  this.map.fire(ANVIL_EVENTS.DELETED, { layer: this.baseLayer });
1659
2151
  this.map.fire(ANVIL_EVENTS.DELETED, { layer });
1660
2152
  this.map.removeLayer(this.baseLayer);
1661
2153
  this.map.removeLayer(layer);
1662
2154
  if (diff) {
1663
- const flattened = turf5.flatten(diff);
2155
+ const flattened = turf6.flatten(diff);
1664
2156
  flattened.features.forEach((f) => {
1665
- const newLayerGroup = L17.geoJSON(f, {
2157
+ const newLayerGroup = L18.geoJSON(f, {
1666
2158
  style: getModePathOptions(this.options, "subtract" /* Subtract */)
1667
2159
  });
1668
2160
  const l = newLayerGroup.getLayers()[0];
@@ -1683,9 +2175,9 @@ var SubtractMode = class {
1683
2175
  };
1684
2176
 
1685
2177
  // src/modes/edit-mode.ts
1686
- var L18 = __toESM(require("leaflet"));
1687
- var EditMode = class {
1688
- constructor(map, store, options = {}) {
2178
+ var L19 = __toESM(require("leaflet"));
2179
+ var _EditMode = class _EditMode {
2180
+ constructor(map, store, options = {}, config = {}) {
1689
2181
  this.map = map;
1690
2182
  this.store = store;
1691
2183
  this.options = options;
@@ -1693,38 +2185,90 @@ var EditMode = class {
1693
2185
  __publicField(this, "markers", []);
1694
2186
  __publicField(this, "ghostMarker", null);
1695
2187
  __publicField(this, "segments", []);
1696
- __publicField(this, "_isDragging", false);
2188
+ __publicField(this, "isDragging", false);
2189
+ __publicField(this, "dragState", null);
2190
+ __publicField(this, "suppressNextClick", false);
1697
2191
  __publicField(this, "originalLayerStyles", /* @__PURE__ */ new Map());
2192
+ __publicField(this, "handleRenderer");
2193
+ __publicField(this, "handlePaneName", "anvil-edit-handles");
2194
+ __publicField(this, "config");
2195
+ __publicField(this, "coverageSnapshot", null);
2196
+ __publicField(this, "dragRollbackState", null);
2197
+ this.config = {
2198
+ modeId: "edit" /* Edit */,
2199
+ autoSelectAll: false,
2200
+ allowLayerSelection: true,
2201
+ clearSelectionOnMapClick: true,
2202
+ defaultSelectionPathOptions: {
2203
+ color: "#ff00ff",
2204
+ weight: 4
2205
+ },
2206
+ defaultHandleOptions: {
2207
+ radius: 6,
2208
+ color: "#ff00ff",
2209
+ fillColor: "#fff",
2210
+ fillOpacity: 1,
2211
+ weight: 2
2212
+ },
2213
+ defaultGhostHandleOptions: {
2214
+ radius: 5,
2215
+ color: "#fff",
2216
+ fillColor: "#ff00ff",
2217
+ fillOpacity: 0.85,
2218
+ weight: 2
2219
+ },
2220
+ ...config
2221
+ };
1698
2222
  }
1699
2223
  enable() {
2224
+ this.ensureHandlePane();
2225
+ this.handleRenderer = this.createHandleRenderer();
1700
2226
  this.store.getGroup().eachLayer((layer) => {
1701
- if (layer instanceof L18.Path || layer instanceof L18.Marker) {
1702
- layer.on("click", this.onLayerClick, this);
1703
- layer.on("mousemove", this.onMouseMove, this);
1704
- layer.getElement()?.style.setProperty("cursor", "pointer");
1705
- }
2227
+ this.bindLayerEvents(layer);
1706
2228
  });
1707
2229
  this.map.on("click", this.onMapClick, this);
1708
- this.map.on("mousemove", this.onMouseMove, this);
2230
+ this.map.on("mousedown", this.onMapMouseDown, this);
2231
+ this.map.on("mousemove", this.onMapMouseMove, this);
2232
+ this.map.on("mouseup", this.onMapMouseUp, this);
2233
+ this.map.on("contextmenu", this.onMapContextMenu, this);
2234
+ this.map.on(ANVIL_EVENTS.CREATED, this.onLayerCreated, this);
2235
+ this.map.on(ANVIL_EVENTS.DELETED, this.onLayerDeleted, this);
2236
+ L19.DomEvent.on(window, "mousemove", this.onWindowMouseMove, this);
2237
+ L19.DomEvent.on(window, "mouseup", this.onWindowMouseUp, this);
2238
+ if (this.config.autoSelectAll) {
2239
+ this.syncAutoSelection();
2240
+ }
1709
2241
  }
1710
2242
  disable() {
2243
+ this.finishDrag(false);
1711
2244
  this.store.getGroup().eachLayer((layer) => {
1712
- if (layer instanceof L18.Path || layer instanceof L18.Marker) {
1713
- layer.off("click", this.onLayerClick, this);
1714
- layer.off("mousemove", this.onMouseMove, this);
1715
- layer.getElement()?.style.setProperty("cursor", "");
1716
- }
2245
+ this.unbindLayerEvents(layer);
1717
2246
  });
1718
2247
  this.map.off("click", this.onMapClick, this);
1719
- this.map.off("mousemove", this.onMouseMove, this);
2248
+ this.map.off("mousedown", this.onMapMouseDown, this);
2249
+ this.map.off("mousemove", this.onMapMouseMove, this);
2250
+ this.map.off("mouseup", this.onMapMouseUp, this);
2251
+ this.map.off("contextmenu", this.onMapContextMenu, this);
2252
+ this.map.off(ANVIL_EVENTS.CREATED, this.onLayerCreated, this);
2253
+ this.map.off(ANVIL_EVENTS.DELETED, this.onLayerDeleted, this);
2254
+ L19.DomEvent.off(window, "mousemove", this.onWindowMouseMove, this);
2255
+ L19.DomEvent.off(window, "mouseup", this.onWindowMouseUp, this);
1720
2256
  this.restoreAllPathStyles();
1721
2257
  this.clearMarkers();
1722
2258
  this.activeLayers.clear();
1723
2259
  }
1724
2260
  onLayerClick(e) {
1725
- L18.DomEvent.stopPropagation(e);
2261
+ if (this.suppressNextClick) {
2262
+ this.stopLeafletEvent(e);
2263
+ return;
2264
+ }
2265
+ this.stopLeafletEvent(e);
2266
+ this.blurActiveElement();
2267
+ if (!this.config.allowLayerSelection) {
2268
+ return;
2269
+ }
1726
2270
  const layer = e.target;
1727
- const isMultiSelect = e.originalEvent.shiftKey || this.options.magnetic;
2271
+ const isMultiSelect = e.originalEvent.shiftKey;
1728
2272
  if (!isMultiSelect) {
1729
2273
  if (this.activeLayers.has(layer) && this.activeLayers.size === 1) return;
1730
2274
  this.restoreAllPathStyles();
@@ -1734,131 +2278,317 @@ var EditMode = class {
1734
2278
  } else {
1735
2279
  if (this.activeLayers.has(layer)) {
1736
2280
  this.activeLayers.delete(layer);
1737
- if (layer instanceof L18.Path) this.restorePathStyle(layer);
2281
+ if (layer instanceof L19.Path) this.restorePathStyle(layer);
1738
2282
  } else {
1739
2283
  this.activeLayers.add(layer);
1740
2284
  }
1741
2285
  this.clearMarkers();
1742
2286
  }
1743
- this.activeLayers.forEach((l) => {
1744
- if (l instanceof L18.Path) this.applySelectionStyle(l);
2287
+ this.activeLayers.forEach((activeLayer) => {
2288
+ if (activeLayer instanceof L19.Path) this.applySelectionStyle(activeLayer);
1745
2289
  });
1746
2290
  this.createMarkers();
1747
2291
  }
1748
2292
  onMapClick() {
2293
+ if (this.suppressNextClick) {
2294
+ this.suppressNextClick = false;
2295
+ return;
2296
+ }
2297
+ if (this.isDragging) return;
2298
+ if (!this.config.clearSelectionOnMapClick) return;
1749
2299
  this.restoreAllPathStyles();
1750
2300
  this.clearMarkers();
1751
2301
  this.activeLayers.clear();
1752
2302
  }
2303
+ onLayerCreated(e) {
2304
+ this.bindLayerEvents(e.layer);
2305
+ if (this.config.autoSelectAll) this.syncAutoSelection();
2306
+ }
2307
+ onLayerDeleted(e) {
2308
+ this.unbindLayerEvents(e.layer);
2309
+ this.activeLayers.delete(e.layer);
2310
+ if (e.layer instanceof L19.Path) this.originalLayerStyles.delete(e.layer);
2311
+ if (this.config.autoSelectAll) this.syncAutoSelection();
2312
+ }
2313
+ onMapMouseDown(e) {
2314
+ if (this.activeLayers.size === 0 || this.dragState) return;
2315
+ const hit = this.hitTestHandle(e.latlng);
2316
+ if (!hit) return;
2317
+ this.stopLeafletEvent(e);
2318
+ this.blurActiveElement();
2319
+ this.suppressNextClick = true;
2320
+ if (hit.kind === "ghost") {
2321
+ this.startGhostDrag(hit.segment, hit.marker);
2322
+ return;
2323
+ }
2324
+ if (hit.kind === "circle") {
2325
+ this.startCircleDrag(hit.circle, hit.marker);
2326
+ return;
2327
+ }
2328
+ this.startVertexDrag(hit.group, hit.marker);
2329
+ }
2330
+ onMapMouseMove(e) {
2331
+ if (this.dragState) {
2332
+ this.updateDrag(e.latlng);
2333
+ return;
2334
+ }
2335
+ if (this.activeLayers.size === 0 || this.segments.length === 0) {
2336
+ this.removeGhost();
2337
+ return;
2338
+ }
2339
+ const mousePoint = this.map.latLngToContainerPoint(e.latlng);
2340
+ const isOverVertex = this.markers.some((marker2) => {
2341
+ const markerPoint = this.map.latLngToContainerPoint(marker2.getLatLng());
2342
+ return markerPoint.distanceTo(mousePoint) < 15;
2343
+ });
2344
+ if (isOverVertex) {
2345
+ this.removeGhost();
2346
+ return;
2347
+ }
2348
+ let closestSegment = null;
2349
+ let minDistance = 24;
2350
+ let bestLatLng = null;
2351
+ this.segments.forEach((segment) => {
2352
+ const a = this.map.latLngToContainerPoint(segment.p1);
2353
+ const b = this.map.latLngToContainerPoint(segment.p2);
2354
+ const projection = L19.LineUtil.closestPointOnSegment(mousePoint, a, b);
2355
+ const distance = mousePoint.distanceTo(projection);
2356
+ if (distance < minDistance) {
2357
+ minDistance = distance;
2358
+ closestSegment = segment;
2359
+ bestLatLng = this.map.containerPointToLatLng(projection);
2360
+ }
2361
+ });
2362
+ if (closestSegment && bestLatLng) {
2363
+ this.showGhost(bestLatLng, closestSegment);
2364
+ } else {
2365
+ this.removeGhost();
2366
+ }
2367
+ }
2368
+ onMapMouseUp() {
2369
+ this.finishDrag();
2370
+ }
2371
+ onMapContextMenu(e) {
2372
+ const hit = this.hitTestHandle(e.latlng);
2373
+ if (!hit || hit.kind === "ghost") return;
2374
+ this.stopLeafletEvent(e);
2375
+ this.blurActiveElement();
2376
+ this.suppressNextClick = true;
2377
+ if (hit.kind === "circle") {
2378
+ this.activeLayers.delete(hit.circle);
2379
+ this.originalLayerStyles.delete(hit.circle);
2380
+ this.map.removeLayer(hit.circle);
2381
+ this.map.fire(ANVIL_EVENTS.DELETED, { layer: hit.circle });
2382
+ this.refreshMarkers();
2383
+ return;
2384
+ }
2385
+ this.deleteVertex(hit.group);
2386
+ }
2387
+ onWindowMouseMove(e) {
2388
+ if (!this.dragState) return;
2389
+ const rect = this.map.getContainer().getBoundingClientRect();
2390
+ const point5 = L19.point(e.clientX - rect.left, e.clientY - rect.top);
2391
+ this.updateDrag(this.map.containerPointToLatLng(point5));
2392
+ }
2393
+ onWindowMouseUp() {
2394
+ this.finishDrag();
2395
+ }
1753
2396
  createMarkers() {
1754
2397
  this.clearMarkers();
1755
2398
  if (this.activeLayers.size === 0) return;
1756
2399
  const vertexMap = /* @__PURE__ */ new Map();
1757
- this.segments = [];
1758
2400
  const segmentMap = /* @__PURE__ */ new Map();
1759
- const getPosKey = (ll) => `${ll.lat.toFixed(6)},${ll.lng.toFixed(6)}`;
2401
+ this.segments = [];
2402
+ const getPosKey = (latlng) => `${latlng.lat.toFixed(6)},${latlng.lng.toFixed(6)}`;
1760
2403
  this.activeLayers.forEach((layer) => {
1761
- if (layer instanceof L18.Marker) {
1762
- this.handleMarkerEdit(layer);
1763
- } else if (layer instanceof L18.Circle) {
1764
- this.handleCircleEdit(layer);
1765
- } else if (layer instanceof L18.Polyline) {
1766
- const latlngs = layer.getLatLngs();
1767
- const traverse = (arr, currentPath) => {
1768
- if (!arr) return;
1769
- if (Array.isArray(arr) && arr.length > 0 && (arr[0] instanceof L18.LatLng || typeof arr[0].lat === "number")) {
1770
- const isPolygon = layer instanceof L18.Polygon;
1771
- let ringLen = arr.length;
1772
- if (isPolygon && ringLen > 1) {
1773
- if (getPosKey(arr[0]) === getPosKey(arr[ringLen - 1])) {
1774
- ringLen--;
1775
- }
2404
+ if (layer instanceof L19.Marker) {
2405
+ this.enableMarkerEditing(layer);
2406
+ return;
2407
+ }
2408
+ if (layer instanceof L19.Circle) {
2409
+ this.createCircleHandle(layer);
2410
+ return;
2411
+ }
2412
+ if (!(layer instanceof L19.Polyline)) return;
2413
+ const traverse = (arr, currentPath) => {
2414
+ if (!arr) return;
2415
+ if (Array.isArray(arr) && arr.length > 0 && (arr[0] instanceof L19.LatLng || typeof arr[0].lat === "number")) {
2416
+ const isPolygon = layer instanceof L19.Polygon;
2417
+ let ringLen = arr.length;
2418
+ if (isPolygon && ringLen > 1 && getPosKey(arr[0]) === getPosKey(arr[ringLen - 1])) {
2419
+ ringLen--;
2420
+ }
2421
+ arr.forEach((latlng, index) => {
2422
+ if (index >= ringLen) return;
2423
+ const key = getPosKey(latlng);
2424
+ if (!vertexMap.has(key)) {
2425
+ vertexMap.set(key, { latlng, refs: [], marker: null });
1776
2426
  }
1777
- arr.forEach((ll, i) => {
1778
- if (i >= ringLen) return;
1779
- const key = getPosKey(ll);
1780
- if (!vertexMap.has(key)) vertexMap.set(key, { latlng: ll, refs: [], marker: null });
1781
- vertexMap.get(key).refs.push({ layer, path: [...currentPath, i] });
1782
- if (i < ringLen - 1 || isPolygon) {
1783
- const nextIndex = (i + 1) % ringLen;
1784
- const nextLL = arr[nextIndex];
1785
- if (getPosKey(ll) === getPosKey(nextLL)) return;
1786
- const k1 = getPosKey(ll);
1787
- const k2 = getPosKey(nextLL);
1788
- const midKey = [k1, k2].sort().join("|");
1789
- if (!segmentMap.has(midKey)) {
1790
- segmentMap.set(midKey, { p1: ll, p2: nextLL, refs: [] });
1791
- }
1792
- segmentMap.get(midKey).refs.push({ layer, path: [...currentPath, i] });
2427
+ vertexMap.get(key).refs.push({ layer, path: [...currentPath, index] });
2428
+ if (index < ringLen - 1 || isPolygon) {
2429
+ const nextIndex = (index + 1) % ringLen;
2430
+ const nextLatLng = arr[nextIndex];
2431
+ if (getPosKey(latlng) === getPosKey(nextLatLng)) return;
2432
+ const segmentKey = [getPosKey(latlng), getPosKey(nextLatLng)].sort().join("|");
2433
+ if (!segmentMap.has(segmentKey)) {
2434
+ segmentMap.set(segmentKey, { p1: latlng, p2: nextLatLng, refs: [] });
1793
2435
  }
1794
- });
1795
- } else if (Array.isArray(arr)) {
1796
- arr.forEach((item, i) => traverse(item, [...currentPath, i]));
1797
- }
1798
- };
1799
- traverse(latlngs, []);
1800
- }
2436
+ segmentMap.get(segmentKey).refs.push({ layer, path: [...currentPath, index] });
2437
+ }
2438
+ });
2439
+ return;
2440
+ }
2441
+ if (Array.isArray(arr)) {
2442
+ arr.forEach((item, index) => traverse(item, [...currentPath, index]));
2443
+ }
2444
+ };
2445
+ traverse(layer.getLatLngs(), []);
1801
2446
  });
1802
2447
  this.segments = Array.from(segmentMap.values());
1803
2448
  vertexMap.forEach((group) => {
1804
- const marker3 = this.createEditMarker(group.latlng);
1805
- group.marker = marker3;
1806
- this.markers.push(marker3);
1807
- marker3.on("dragstart", () => {
1808
- this._isDragging = true;
1809
- });
1810
- marker3.on("drag", (e) => {
1811
- const mouseEvent = e;
1812
- const skipLayersArray = Array.from(this.activeLayers);
1813
- const additionalPoints = [
1814
- ...this.markers.filter((m) => m !== marker3).map((m) => m.getLatLng()),
1815
- ...Array.from(this.activeLayers).filter((l) => l instanceof L18.Marker).map((l) => l.getLatLng())
1816
- ];
1817
- const snapped = getSnapLatLng(this.map, mouseEvent.latlng, this.store, this.options, additionalPoints, skipLayersArray);
1818
- if (this.options.preventSelfIntersection) {
1819
- let wouldIntersect = false;
1820
- group.refs.forEach((ref) => {
1821
- const fullStructure = ref.layer.getLatLngs();
1822
- let target = fullStructure;
1823
- for (let i = 0; i < ref.path.length - 1; i++) {
1824
- target = target[ref.path[i]];
1825
- }
1826
- const oldPos = target[ref.path[ref.path.length - 1]];
1827
- target[ref.path[ref.path.length - 1]] = snapped;
1828
- if (isSelfIntersecting(this.map, fullStructure, ref.layer instanceof L18.Polygon)) {
1829
- wouldIntersect = true;
1830
- }
1831
- target[ref.path[ref.path.length - 1]] = oldPos;
1832
- });
1833
- if (wouldIntersect) return;
2449
+ const marker2 = this.createHandle(group.latlng);
2450
+ group.marker = marker2;
2451
+ this.setHandleMeta(marker2, { kind: "vertex", group });
2452
+ this.markers.push(marker2);
2453
+ });
2454
+ }
2455
+ startVertexDrag(group, marker2) {
2456
+ this.captureCoverageSnapshotIfNeeded();
2457
+ this.captureDragRollbackState(group.refs.map((ref) => ref.layer));
2458
+ this.removeGhost();
2459
+ this.dragState = { kind: "vertex", group, marker: marker2 };
2460
+ this.isDragging = true;
2461
+ this.map.dragging?.disable();
2462
+ }
2463
+ startGhostDrag(segment, marker2) {
2464
+ this.captureCoverageSnapshotIfNeeded();
2465
+ this.captureDragRollbackState(segment.refs.map((ref) => ref.layer));
2466
+ const startLatLng = marker2.getLatLng();
2467
+ segment.refs.forEach((ref) => {
2468
+ const fullStructure = ref.layer.getLatLngs();
2469
+ let target = fullStructure;
2470
+ for (let i = 0; i < ref.path.length - 1; i++) {
2471
+ target = target[ref.path[i]];
2472
+ }
2473
+ target.splice(ref.path[ref.path.length - 1] + 1, 0, startLatLng);
2474
+ ref.layer.setLatLngs(fullStructure);
2475
+ });
2476
+ this.dragState = { kind: "ghost", segment, marker: marker2 };
2477
+ this.isDragging = true;
2478
+ this.map.dragging?.disable();
2479
+ }
2480
+ startCircleDrag(circle2, marker2) {
2481
+ this.coverageSnapshot = null;
2482
+ this.captureDragRollbackState([circle2]);
2483
+ this.removeGhost();
2484
+ this.dragState = { kind: "circle", circle: circle2, marker: marker2 };
2485
+ this.isDragging = true;
2486
+ this.map.dragging?.disable();
2487
+ }
2488
+ updateDrag(latlng) {
2489
+ if (!this.dragState) return;
2490
+ if (this.dragState.kind === "vertex") {
2491
+ this.updateVertexDrag(this.dragState.group, this.dragState.marker, latlng);
2492
+ return;
2493
+ }
2494
+ if (this.dragState.kind === "ghost") {
2495
+ this.updateGhostDrag(this.dragState.segment, this.dragState.marker, latlng);
2496
+ return;
2497
+ }
2498
+ this.updateCircleDrag(this.dragState.circle, this.dragState.marker, latlng);
2499
+ }
2500
+ updateVertexDrag(group, marker2, latlng) {
2501
+ const skipLayersArray = Array.from(this.activeLayers);
2502
+ const snapped = getSnapLatLng(this.map, latlng, this.store, this.options, [], skipLayersArray);
2503
+ if (this.options.preventSelfIntersection) {
2504
+ let wouldIntersect = false;
2505
+ group.refs.forEach((ref) => {
2506
+ if (this.wouldVertexMoveSelfIntersect(ref, snapped)) {
2507
+ wouldIntersect = true;
1834
2508
  }
1835
- marker3.setLatLng(snapped);
1836
- group.latlng = snapped;
1837
- group.refs.forEach((ref) => {
1838
- const fullStructure = ref.layer.getLatLngs();
1839
- let target = fullStructure;
1840
- for (let i = 0; i < ref.path.length - 1; i++) {
1841
- target = target[ref.path[i]];
1842
- }
1843
- target[ref.path[ref.path.length - 1]] = snapped;
1844
- ref.layer.setLatLngs(fullStructure);
1845
- ref.layer.redraw();
1846
- });
1847
2509
  });
1848
- marker3.on("dragend", () => {
1849
- this._isDragging = false;
1850
- this.activeLayers.forEach((l) => this.map.fire(ANVIL_EVENTS.EDITED, { layer: l }));
1851
- this.refreshMarkers();
1852
- });
1853
- marker3.on("contextmenu", (e) => {
1854
- const mouseEvent = e;
1855
- L18.DomEvent.stopPropagation(mouseEvent);
1856
- this.deleteVertex(group);
2510
+ if (wouldIntersect) return;
2511
+ }
2512
+ marker2.setLatLng(snapped);
2513
+ group.latlng = snapped;
2514
+ group.refs.forEach((ref) => {
2515
+ const fullStructure = ref.layer.getLatLngs();
2516
+ let target = fullStructure;
2517
+ for (let i = 0; i < ref.path.length - 1; i++) {
2518
+ target = target[ref.path[i]];
2519
+ }
2520
+ target[ref.path[ref.path.length - 1]] = snapped;
2521
+ ref.layer.setLatLngs(fullStructure);
2522
+ ref.layer.redraw();
2523
+ });
2524
+ }
2525
+ updateGhostDrag(segment, marker2, latlng) {
2526
+ const skipLayersArray = Array.from(this.activeLayers);
2527
+ const snapped = getSnapLatLng(this.map, latlng, this.store, this.options, [], skipLayersArray);
2528
+ if (this.options.preventSelfIntersection) {
2529
+ let wouldIntersect = false;
2530
+ segment.refs.forEach((ref) => {
2531
+ if (this.wouldInsertedVertexSelfIntersect(ref, snapped)) {
2532
+ wouldIntersect = true;
2533
+ }
1857
2534
  });
2535
+ if (wouldIntersect) return;
2536
+ }
2537
+ marker2.setLatLng(snapped);
2538
+ segment.refs.forEach((ref) => {
2539
+ const fullStructure = ref.layer.getLatLngs();
2540
+ let target = fullStructure;
2541
+ for (let i = 0; i < ref.path.length - 1; i++) {
2542
+ target = target[ref.path[i]];
2543
+ }
2544
+ target[ref.path[ref.path.length - 1] + 1] = snapped;
2545
+ ref.layer.setLatLngs(fullStructure);
2546
+ ref.layer.redraw();
1858
2547
  });
1859
2548
  }
2549
+ updateCircleDrag(circle2, marker2, latlng) {
2550
+ const additionalPoints = this.markers.filter((candidate) => candidate !== marker2).map((candidate) => candidate.getLatLng());
2551
+ const snapped = getSnapLatLng(this.map, latlng, this.store, this.options, additionalPoints, circle2);
2552
+ marker2.setLatLng(snapped);
2553
+ circle2.setLatLng(snapped);
2554
+ }
2555
+ finishDrag(emitEdited = true) {
2556
+ if (!this.dragState) return;
2557
+ const editedLayers = /* @__PURE__ */ new Set();
2558
+ if (this.dragState.kind === "vertex") {
2559
+ this.dragState.group.refs.forEach((ref) => editedLayers.add(ref.layer));
2560
+ } else if (this.dragState.kind === "ghost") {
2561
+ this.dragState.segment.refs.forEach((ref) => editedLayers.add(ref.layer));
2562
+ } else {
2563
+ editedLayers.add(this.dragState.circle);
2564
+ }
2565
+ this.dragState = null;
2566
+ this.isDragging = false;
2567
+ this.suppressNextClick = true;
2568
+ if (this.map.dragging && !this.map.dragging.enabled()) {
2569
+ this.map.dragging.enable();
2570
+ }
2571
+ if (this.options.preventSelfIntersection && this.wouldCurrentDragCreateOverlap()) {
2572
+ this.restoreDragRollbackState();
2573
+ this.coverageSnapshot = null;
2574
+ this.refreshMarkers();
2575
+ return;
2576
+ }
2577
+ this.repairCoverageIfNeeded().forEach((layer) => editedLayers.add(layer));
2578
+ this.dragRollbackState = null;
2579
+ if (emitEdited) {
2580
+ editedLayers.forEach((layer) => this.map.fire(ANVIL_EVENTS.EDITED, { layer }));
2581
+ }
2582
+ this.refreshMarkers();
2583
+ }
1860
2584
  deleteVertex(group) {
2585
+ this.captureCoverageSnapshotIfNeeded();
2586
+ if (this.options.preventSelfIntersection) {
2587
+ const wouldIntersect = group.refs.some((ref) => this.wouldDeletedVertexSelfIntersect(ref));
2588
+ if (wouldIntersect) return;
2589
+ }
1861
2590
  const layersToDelete = /* @__PURE__ */ new Set();
2591
+ const editedLayers = /* @__PURE__ */ new Set();
1862
2592
  group.refs.forEach((ref) => {
1863
2593
  const fullStructure = ref.layer.getLatLngs();
1864
2594
  let target = fullStructure;
@@ -1866,230 +2596,166 @@ var EditMode = class {
1866
2596
  target = target[ref.path[i]];
1867
2597
  }
1868
2598
  const index = ref.path[ref.path.length - 1];
1869
- const isPolygon = ref.layer instanceof L18.Polygon;
1870
- const minVertices = isPolygon ? 3 : 2;
2599
+ const minVertices = ref.layer instanceof L19.Polygon ? 3 : 2;
1871
2600
  if (target.length > minVertices) {
1872
2601
  target.splice(index, 1);
1873
2602
  ref.layer.setLatLngs(fullStructure);
1874
2603
  ref.layer.redraw();
1875
- this.map.fire(ANVIL_EVENTS.EDITED, { layer: ref.layer });
2604
+ editedLayers.add(ref.layer);
1876
2605
  } else {
1877
2606
  layersToDelete.add(ref.layer);
1878
2607
  }
1879
2608
  });
1880
2609
  layersToDelete.forEach((layer) => {
1881
2610
  this.activeLayers.delete(layer);
1882
- if (layer instanceof L18.Path) {
1883
- this.originalLayerStyles.delete(layer);
1884
- }
2611
+ if (layer instanceof L19.Path) this.originalLayerStyles.delete(layer);
1885
2612
  this.map.removeLayer(layer);
1886
2613
  this.map.fire(ANVIL_EVENTS.DELETED, { layer });
1887
2614
  });
1888
- this.refreshMarkers();
1889
- }
1890
- onMouseMove(e) {
1891
- const mouseEvent = e;
1892
- if (this.activeLayers.size === 0 || this.segments.length === 0 || this._isDragging) {
1893
- if (!this._isDragging) this.removeGhost();
1894
- return;
1895
- }
1896
- const mousePoint = this.map.latLngToContainerPoint(mouseEvent.latlng);
1897
- const isOverVertex = this.markers.some((m) => {
1898
- const p = this.map.latLngToContainerPoint(m.getLatLng());
1899
- return p.distanceTo(mousePoint) < 15;
1900
- });
1901
- if (isOverVertex) {
1902
- this.removeGhost();
1903
- return;
1904
- }
1905
- let closestSeg = null;
1906
- let minDistance = 24;
1907
- let bestLatLng = null;
1908
- this.segments.forEach((seg) => {
1909
- const A = this.map.latLngToContainerPoint(seg.p1);
1910
- const B = this.map.latLngToContainerPoint(seg.p2);
1911
- const proj = L18.LineUtil.closestPointOnSegment(mousePoint, A, B);
1912
- const dist = mousePoint.distanceTo(proj);
1913
- if (dist < minDistance) {
1914
- minDistance = dist;
1915
- closestSeg = seg;
1916
- bestLatLng = this.map.containerPointToLatLng(proj);
1917
- }
1918
- });
1919
- if (closestSeg && bestLatLng) {
1920
- this.showGhost(bestLatLng, closestSeg);
2615
+ if (layersToDelete.size === 0) {
2616
+ this.repairCoverageIfNeeded().forEach((layer) => editedLayers.add(layer));
1921
2617
  } else {
1922
- this.removeGhost();
2618
+ this.coverageSnapshot = null;
1923
2619
  }
1924
- }
1925
- createEditMarker(latlng) {
1926
- const visuals = this.getEditHandleVisuals();
1927
- return L18.marker(latlng, {
1928
- draggable: true,
1929
- zIndexOffset: 2e3,
1930
- icon: L18.divIcon({
1931
- className: "anvil-edit-marker",
1932
- html: this.createHandleHtml(visuals.size, visuals.fillColor, visuals.borderColor, visuals.borderWidth),
1933
- iconSize: [visuals.size, visuals.size],
1934
- iconAnchor: [visuals.size / 2, visuals.size / 2]
1935
- })
1936
- }).addTo(this.map);
2620
+ editedLayers.forEach((layer) => this.map.fire(ANVIL_EVENTS.EDITED, { layer }));
2621
+ this.refreshMarkers();
1937
2622
  }
1938
2623
  showGhost(latlng, segment) {
1939
2624
  if (this.ghostMarker) {
1940
- if (!this._isDragging) {
1941
- this.ghostMarker.setLatLng(latlng);
1942
- this.ghostMarker._activeSeg = segment;
1943
- }
2625
+ this.ghostMarker.setLatLng(latlng);
2626
+ this.ghostMarker._activeSeg = segment;
1944
2627
  return;
1945
2628
  }
1946
- const visuals = this.getGhostHandleVisuals();
1947
- this.ghostMarker = L18.marker(latlng, {
1948
- draggable: true,
1949
- opacity: 0.7,
1950
- zIndexOffset: 3e3,
1951
- icon: L18.divIcon({
1952
- className: "anvil-ghost-marker",
1953
- html: this.createHandleHtml(visuals.size, visuals.fillColor, visuals.borderColor, visuals.borderWidth),
1954
- iconSize: [visuals.size, visuals.size],
1955
- iconAnchor: [visuals.size / 2, visuals.size / 2]
1956
- })
1957
- }).addTo(this.map);
1958
- this.ghostMarker.on("dragstart", () => {
1959
- this._isDragging = true;
1960
- const activeSeg = this.ghostMarker._activeSeg || segment;
1961
- const startLL = this.ghostMarker.getLatLng();
1962
- activeSeg.refs.forEach((ref) => {
1963
- const fullStructure = ref.layer.getLatLngs();
1964
- let target = fullStructure;
1965
- for (let i = 0; i < ref.path.length - 1; i++) {
1966
- target = target[ref.path[i]];
1967
- }
1968
- target.splice(ref.path[ref.path.length - 1] + 1, 0, startLL);
1969
- ref.layer.setLatLngs(fullStructure);
1970
- });
1971
- });
1972
- this.ghostMarker.on("drag", (e) => {
1973
- const mouseEvent = e;
1974
- const skipLayersArray = Array.from(this.activeLayers);
1975
- const additionalPoints = [
1976
- ...this.markers.map((m) => m.getLatLng()),
1977
- ...Array.from(this.activeLayers).filter((l) => l instanceof L18.Marker).map((l) => l.getLatLng())
1978
- ];
1979
- const snapped = getSnapLatLng(this.map, mouseEvent.latlng, this.store, this.options, additionalPoints, skipLayersArray);
1980
- if (this.options.preventSelfIntersection) {
1981
- let wouldIntersect = false;
1982
- const activeSeg2 = this.ghostMarker._activeSeg || segment;
1983
- activeSeg2.refs.forEach((ref) => {
1984
- const fullStructure = ref.layer.getLatLngs();
1985
- let target = fullStructure;
1986
- for (let i = 0; i < ref.path.length - 1; i++) {
1987
- target = target[ref.path[i]];
1988
- }
1989
- const oldPos = target[ref.path[ref.path.length - 1] + 1];
1990
- target[ref.path[ref.path.length - 1] + 1] = snapped;
1991
- if (isSelfIntersecting(this.map, fullStructure, ref.layer instanceof L18.Polygon)) {
1992
- wouldIntersect = true;
1993
- }
1994
- target[ref.path[ref.path.length - 1] + 1] = oldPos;
1995
- });
1996
- if (wouldIntersect) return;
1997
- }
1998
- this.ghostMarker.setLatLng(snapped);
1999
- const activeSeg = this.ghostMarker._activeSeg || segment;
2000
- activeSeg.refs.forEach((ref) => {
2001
- const fullStructure = ref.layer.getLatLngs();
2002
- let target = fullStructure;
2003
- for (let i = 0; i < ref.path.length - 1; i++) {
2004
- target = target[ref.path[i]];
2005
- }
2006
- target[ref.path[ref.path.length - 1] + 1] = snapped;
2007
- ref.layer.setLatLngs(fullStructure);
2008
- ref.layer.redraw();
2009
- });
2010
- });
2011
- this.ghostMarker.on("dragend", () => {
2012
- this._isDragging = false;
2013
- this.activeLayers.forEach((l) => this.map.fire(ANVIL_EVENTS.EDITED, { layer: l }));
2014
- this.removeGhost();
2015
- this.refreshMarkers();
2016
- });
2629
+ const marker2 = this.createGhostHandle(latlng);
2630
+ marker2._activeSeg = segment;
2631
+ this.ghostMarker = marker2;
2017
2632
  }
2018
2633
  removeGhost() {
2019
- if (this.ghostMarker && !this._isDragging) {
2020
- this.map.removeLayer(this.ghostMarker);
2021
- this.ghostMarker = null;
2022
- }
2023
- }
2024
- handleMarkerEdit(marker3) {
2025
- marker3.dragging?.enable();
2026
- marker3.on("drag", (e) => {
2027
- const mouseEvent = e;
2028
- const additionalPoints = this.markers.map((m) => m.getLatLng());
2029
- const snapped = getSnapLatLng(this.map, mouseEvent.latlng, this.store, this.options, additionalPoints, marker3);
2030
- marker3.setLatLng(snapped);
2634
+ if (!this.ghostMarker || this.isDragging) return;
2635
+ this.ghostMarker.off();
2636
+ this.map.removeLayer(this.ghostMarker);
2637
+ this.ghostMarker = null;
2638
+ }
2639
+ enableMarkerEditing(marker2) {
2640
+ marker2.dragging?.enable();
2641
+ marker2.off("drag");
2642
+ marker2.off("dragend");
2643
+ marker2.off("contextmenu");
2644
+ marker2.on("drag", (e) => {
2645
+ const additionalPoints = this.markers.map((candidate) => candidate.getLatLng());
2646
+ const snapped = getSnapLatLng(this.map, e.latlng, this.store, this.options, additionalPoints, marker2);
2647
+ marker2.setLatLng(snapped);
2031
2648
  });
2032
- marker3.on("dragend", () => {
2033
- this.map.fire(ANVIL_EVENTS.EDITED, { layer: marker3 });
2649
+ marker2.on("dragend", () => {
2650
+ this.map.fire(ANVIL_EVENTS.EDITED, { layer: marker2 });
2034
2651
  });
2035
- marker3.on("contextmenu", (e) => {
2036
- const mouseEvent = e;
2037
- L18.DomEvent.stopPropagation(mouseEvent);
2038
- this.activeLayers.delete(marker3);
2039
- this.map.removeLayer(marker3);
2040
- this.map.fire(ANVIL_EVENTS.DELETED, { layer: marker3 });
2652
+ marker2.on("contextmenu", (e) => {
2653
+ this.stopLeafletEvent(e);
2654
+ this.activeLayers.delete(marker2);
2655
+ this.map.removeLayer(marker2);
2656
+ this.map.fire(ANVIL_EVENTS.DELETED, { layer: marker2 });
2041
2657
  this.refreshMarkers();
2042
2658
  });
2043
2659
  }
2044
- handleCircleEdit(circle2) {
2045
- const marker3 = this.createEditMarker(circle2.getLatLng());
2046
- marker3.on("drag", (e) => {
2047
- const mouseEvent = e;
2048
- const additionalPoints = this.markers.filter((m) => m !== marker3).map((m) => m.getLatLng());
2049
- const snapped = getSnapLatLng(this.map, mouseEvent.latlng, this.store, this.options, additionalPoints, circle2);
2050
- marker3.setLatLng(snapped);
2051
- circle2.setLatLng(snapped);
2052
- });
2053
- marker3.on("dragend", () => {
2054
- this.map.fire(ANVIL_EVENTS.EDITED, { layer: circle2 });
2055
- });
2056
- marker3.on("contextmenu", (e) => {
2057
- const mouseEvent = e;
2058
- L18.DomEvent.stopPropagation(mouseEvent);
2059
- this.activeLayers.delete(circle2);
2060
- this.originalLayerStyles.delete(circle2);
2061
- this.map.removeLayer(circle2);
2062
- this.map.fire(ANVIL_EVENTS.DELETED, { layer: circle2 });
2063
- this.refreshMarkers();
2064
- });
2065
- this.markers.push(marker3);
2660
+ createCircleHandle(circle2) {
2661
+ const marker2 = this.createHandle(circle2.getLatLng());
2662
+ this.setHandleMeta(marker2, { kind: "circle", circle: circle2 });
2663
+ this.markers.push(marker2);
2066
2664
  }
2067
2665
  clearMarkers() {
2068
2666
  this.activeLayers.forEach((layer) => {
2069
- if (layer instanceof L18.Marker) {
2667
+ if (layer instanceof L19.Marker) {
2070
2668
  layer.dragging?.disable();
2071
2669
  layer.off("drag");
2072
2670
  layer.off("dragend");
2073
2671
  layer.off("contextmenu");
2074
2672
  }
2075
2673
  });
2076
- this.markers.forEach((m) => this.map.removeLayer(m));
2674
+ this.markers.forEach((marker2) => {
2675
+ this.map.removeLayer(marker2);
2676
+ });
2077
2677
  this.markers = [];
2078
- this.removeGhost();
2678
+ this.segments = [];
2679
+ this.dragState = null;
2680
+ this.isDragging = false;
2681
+ this.coverageSnapshot = null;
2682
+ this.dragRollbackState = null;
2683
+ if (this.ghostMarker) {
2684
+ this.map.removeLayer(this.ghostMarker);
2685
+ this.ghostMarker = null;
2686
+ }
2687
+ if (this.map.dragging && !this.map.dragging.enabled()) {
2688
+ this.map.dragging.enable();
2689
+ }
2079
2690
  }
2080
2691
  refreshMarkers() {
2692
+ if (this.config.autoSelectAll) {
2693
+ this.syncAutoSelection();
2694
+ return;
2695
+ }
2081
2696
  this.clearMarkers();
2082
2697
  this.createMarkers();
2083
2698
  }
2699
+ bindLayerEvents(layer) {
2700
+ if (!this.isEditableLayer(layer)) return;
2701
+ layer.on("click", this.onLayerClick, this);
2702
+ layer.on("mousemove", this.onMapMouseMove, this);
2703
+ layer.getElement()?.style.setProperty("cursor", "pointer");
2704
+ }
2705
+ unbindLayerEvents(layer) {
2706
+ if (!this.isEditableLayer(layer)) return;
2707
+ layer.off("click", this.onLayerClick, this);
2708
+ layer.off("mousemove", this.onMapMouseMove, this);
2709
+ layer.getElement()?.style.setProperty("cursor", "");
2710
+ }
2711
+ isEditableLayer(layer) {
2712
+ return layer instanceof L19.Path || layer instanceof L19.Marker;
2713
+ }
2714
+ getActivePolygonLayers() {
2715
+ return Array.from(this.activeLayers).filter((layer) => layer instanceof L19.Polygon);
2716
+ }
2717
+ shouldMaintainCoverage() {
2718
+ const polygons = this.getActivePolygonLayers();
2719
+ return polygons.length > 1 && hasSharedBoundaryCoverage(polygons);
2720
+ }
2721
+ captureCoverageSnapshotIfNeeded() {
2722
+ if (!this.shouldMaintainCoverage()) {
2723
+ this.coverageSnapshot = null;
2724
+ return;
2725
+ }
2726
+ this.coverageSnapshot = createCoverageSnapshot(this.getActivePolygonLayers());
2727
+ }
2728
+ repairCoverageIfNeeded() {
2729
+ if (!this.coverageSnapshot) return [];
2730
+ const polygons = this.getActivePolygonLayers();
2731
+ const repaired = polygons.length > 1 ? repairPolygonCoverage(polygons, this.coverageSnapshot) : [];
2732
+ this.coverageSnapshot = null;
2733
+ return repaired;
2734
+ }
2735
+ syncAutoSelection() {
2736
+ this.restoreAllPathStyles();
2737
+ this.clearMarkers();
2738
+ this.activeLayers.clear();
2739
+ this.store.getGroup().eachLayer((layer) => {
2740
+ if (!this.isEditableLayer(layer)) return;
2741
+ this.activeLayers.add(layer);
2742
+ });
2743
+ this.activeLayers.forEach((layer) => {
2744
+ if (layer instanceof L19.Path) this.applySelectionStyle(layer);
2745
+ });
2746
+ this.createMarkers();
2747
+ }
2084
2748
  applySelectionStyle(layer) {
2085
2749
  if (!this.originalLayerStyles.has(layer)) {
2086
2750
  this.originalLayerStyles.set(layer, { ...layer.options });
2087
2751
  }
2088
2752
  layer.setStyle(
2089
- getModeSelectionPathOptions(this.options, "edit" /* Edit */, this.originalLayerStyles.get(layer) || {}, {
2090
- color: "#ff00ff",
2091
- weight: 4
2092
- })
2753
+ getModeSelectionPathOptions(
2754
+ this.options,
2755
+ this.config.modeId,
2756
+ this.originalLayerStyles.get(layer) || {},
2757
+ this.config.defaultSelectionPathOptions
2758
+ )
2093
2759
  );
2094
2760
  }
2095
2761
  restorePathStyle(layer) {
@@ -2104,45 +2770,319 @@ var EditMode = class {
2104
2770
  });
2105
2771
  this.originalLayerStyles.clear();
2106
2772
  }
2107
- getEditHandleVisuals() {
2108
- const selection = getModeSelectionPathOptions(this.options, "edit" /* Edit */, {}, { color: "#ff00ff" });
2109
- const handle = getModeHandleOptions(this.options, "edit" /* Edit */, {
2110
- radius: 6,
2111
- color: selection.color || "#ff00ff",
2112
- fillColor: "#fff",
2113
- fillOpacity: 1,
2114
- weight: 2
2115
- });
2773
+ createHandle(latlng) {
2774
+ const marker2 = L19.circleMarker(latlng, this.getEditHandleOptions()).addTo(this.map);
2775
+ marker2.bringToFront();
2776
+ return marker2;
2777
+ }
2778
+ createGhostHandle(latlng) {
2779
+ const marker2 = L19.circleMarker(latlng, this.getGhostHandleOptions()).addTo(this.map);
2780
+ marker2.bringToFront();
2781
+ return marker2;
2782
+ }
2783
+ getEditHandleOptions() {
2784
+ const handle = getModeHandleOptions(this.options, this.config.modeId, this.config.defaultHandleOptions);
2116
2785
  return {
2117
- size: (handle.radius || 6) * 2,
2118
- fillColor: handle.fillColor || "#fff",
2119
- borderColor: handle.color || (selection.color || "#ff00ff"),
2120
- borderWidth: handle.weight || 2
2786
+ ...handle,
2787
+ pane: this.handlePaneName,
2788
+ renderer: this.handleRenderer,
2789
+ interactive: false,
2790
+ bubblingMouseEvents: false
2121
2791
  };
2122
2792
  }
2123
- getGhostHandleVisuals() {
2124
- const selection = getModeSelectionPathOptions(this.options, "edit" /* Edit */, {}, { color: "#ff00ff" });
2125
- const handle = getModeHandleOptions(this.options, "edit" /* Edit */, {
2126
- radius: 5,
2127
- color: "#fff",
2128
- fillColor: selection.color || "#ff00ff",
2129
- fillOpacity: 1,
2130
- weight: 2
2131
- });
2793
+ getGhostHandleOptions() {
2794
+ const handle = getModeHandleOptions(this.options, this.config.modeId, this.config.defaultGhostHandleOptions);
2132
2795
  return {
2133
- size: (handle.radius || 5) * 2,
2134
- fillColor: handle.fillColor || (selection.color || "#ff00ff"),
2135
- borderColor: handle.color || "#fff",
2136
- borderWidth: handle.weight || 2
2796
+ ...handle,
2797
+ pane: this.handlePaneName,
2798
+ renderer: this.handleRenderer,
2799
+ interactive: false,
2800
+ bubblingMouseEvents: false
2137
2801
  };
2138
2802
  }
2139
- createHandleHtml(size, fillColor, borderColor, borderWidth) {
2140
- return `<div style="width: ${size}px; height: ${size}px; background: ${fillColor}; border: ${borderWidth}px solid ${borderColor}; border-radius: 50%; box-sizing: border-box; box-shadow: 0 0 4px rgba(0,0,0,0.3);"></div>`;
2803
+ wouldVertexMoveSelfIntersect(ref, latlng) {
2804
+ const ring = this.getEditableRing(ref);
2805
+ if (!ring) return false;
2806
+ const { points, index, isPolygon } = ring;
2807
+ const updatedPoints = points.slice();
2808
+ updatedPoints[index] = latlng;
2809
+ const changedSegments = [
2810
+ index - 1,
2811
+ index
2812
+ ];
2813
+ if (isPolygon) {
2814
+ if (changedSegments[0] < 0) changedSegments[0] = points.length - 1;
2815
+ if (changedSegments[1] >= points.length) changedSegments[1] = 0;
2816
+ }
2817
+ return this.changedSegmentsIntersect(updatedPoints, changedSegments, isPolygon);
2818
+ }
2819
+ wouldInsertedVertexSelfIntersect(ref, latlng) {
2820
+ const ring = this.getEditableRing(ref);
2821
+ if (!ring) return false;
2822
+ const { points, index, isPolygon } = ring;
2823
+ const insertAt = index + 1;
2824
+ const updatedPoints = [
2825
+ ...points.slice(0, insertAt),
2826
+ latlng,
2827
+ ...points.slice(insertAt)
2828
+ ];
2829
+ return this.changedSegmentsIntersect(updatedPoints, [index, index + 1], isPolygon);
2830
+ }
2831
+ wouldDeletedVertexSelfIntersect(ref) {
2832
+ const ring = this.getEditableRing(ref);
2833
+ if (!ring) return false;
2834
+ const { points, index, isPolygon } = ring;
2835
+ const minVertices = isPolygon ? 3 : 2;
2836
+ if (points.length <= minVertices) return false;
2837
+ if (!isPolygon && (index === 0 || index === points.length - 1)) return false;
2838
+ const updatedPoints = points.slice();
2839
+ updatedPoints.splice(index, 1);
2840
+ if (updatedPoints.length < minVertices) return false;
2841
+ const changedSegmentIndex = isPolygon ? (index - 1 + updatedPoints.length) % updatedPoints.length : index - 1;
2842
+ return this.changedSegmentsIntersect(updatedPoints, [changedSegmentIndex], isPolygon);
2843
+ }
2844
+ getEditableRing(ref) {
2845
+ const fullStructure = ref.layer.getLatLngs();
2846
+ let target = fullStructure;
2847
+ for (let i = 0; i < ref.path.length - 1; i++) {
2848
+ target = target[ref.path[i]];
2849
+ }
2850
+ if (!Array.isArray(target)) return null;
2851
+ const isPolygon = ref.layer instanceof L19.Polygon;
2852
+ const ring = target;
2853
+ let points = ring.slice();
2854
+ if (isPolygon && points.length > 1 && this.sameLatLng(points[0], points[points.length - 1])) {
2855
+ points = points.slice(0, -1);
2856
+ }
2857
+ return {
2858
+ points,
2859
+ index: ref.path[ref.path.length - 1],
2860
+ isPolygon
2861
+ };
2862
+ }
2863
+ captureDragRollbackState(layers) {
2864
+ const rollback = /* @__PURE__ */ new Map();
2865
+ layers.forEach((layer) => {
2866
+ if (rollback.has(layer)) return;
2867
+ if (layer instanceof L19.Polyline) {
2868
+ const state = {
2869
+ latlngs: this.cloneLatLngStructure(layer.getLatLngs())
2870
+ };
2871
+ if (layer instanceof L19.Polygon) {
2872
+ const feature = sanitizePolygonFeature(
2873
+ layer.toGeoJSON()
2874
+ );
2875
+ if (feature) state.feature = feature;
2876
+ }
2877
+ rollback.set(layer, state);
2878
+ return;
2879
+ }
2880
+ if (layer instanceof L19.Circle || layer instanceof L19.Marker) {
2881
+ rollback.set(layer, { latlng: L19.latLng(layer.getLatLng().lat, layer.getLatLng().lng) });
2882
+ }
2883
+ });
2884
+ this.dragRollbackState = rollback;
2885
+ }
2886
+ restoreDragRollbackState() {
2887
+ if (!this.dragRollbackState) return;
2888
+ this.dragRollbackState.forEach((state, layer) => {
2889
+ if (layer instanceof L19.Polyline && state.latlngs) {
2890
+ layer.setLatLngs(this.cloneLatLngStructure(state.latlngs));
2891
+ layer.redraw();
2892
+ return;
2893
+ }
2894
+ if ((layer instanceof L19.Circle || layer instanceof L19.Marker) && state.latlng) {
2895
+ layer.setLatLng(L19.latLng(state.latlng.lat, state.latlng.lng));
2896
+ }
2897
+ });
2898
+ this.dragRollbackState = null;
2899
+ }
2900
+ wouldCurrentDragCreateOverlap() {
2901
+ if (!this.dragRollbackState) return false;
2902
+ const changedPolygonLayers = Array.from(this.dragRollbackState.entries()).filter((entry) => entry[0] instanceof L19.Polygon && Boolean(entry[1].feature));
2903
+ if (changedPolygonLayers.length === 0) return false;
2904
+ const allPolygons = this.store.getLayers().filter((layer) => layer instanceof L19.Polygon);
2905
+ const currentFeatures = /* @__PURE__ */ new Map();
2906
+ allPolygons.forEach((layer) => {
2907
+ const feature = sanitizePolygonFeature(
2908
+ layer.toGeoJSON()
2909
+ );
2910
+ if (feature) currentFeatures.set(layer, feature);
2911
+ });
2912
+ const changedLayerSet = new Set(changedPolygonLayers.map(([layer]) => layer));
2913
+ for (const [changedLayer, snapshot] of changedPolygonLayers) {
2914
+ const beforeChanged = snapshot.feature;
2915
+ const afterChanged = currentFeatures.get(changedLayer);
2916
+ if (!beforeChanged || !afterChanged) continue;
2917
+ for (const otherLayer of allPolygons) {
2918
+ if (otherLayer === changedLayer) continue;
2919
+ if (changedLayerSet.has(otherLayer) && !this.shouldCheckChangedPair(changedLayer, otherLayer)) continue;
2920
+ const beforeOther = this.dragRollbackState.get(otherLayer)?.feature ?? currentFeatures.get(otherLayer);
2921
+ const afterOther = currentFeatures.get(otherLayer);
2922
+ if (!beforeOther || !afterOther) continue;
2923
+ const beforeOverlap = getPolygonOverlapArea(beforeChanged, beforeOther);
2924
+ const afterOverlap = getPolygonOverlapArea(afterChanged, afterOther);
2925
+ if (afterOverlap > beforeOverlap + _EditMode.OVERLAP_AREA_TOLERANCE) {
2926
+ return true;
2927
+ }
2928
+ }
2929
+ }
2930
+ return false;
2931
+ }
2932
+ shouldCheckChangedPair(first, second) {
2933
+ const layers = Array.from(this.dragRollbackState?.keys() ?? []).filter(
2934
+ (layer) => layer instanceof L19.Polygon
2935
+ );
2936
+ return layers.indexOf(first) < layers.indexOf(second);
2937
+ }
2938
+ cloneLatLngStructure(value) {
2939
+ if (!Array.isArray(value)) return value;
2940
+ return value.map((item) => {
2941
+ if (Array.isArray(item)) {
2942
+ return this.cloneLatLngStructure(item);
2943
+ }
2944
+ if (item instanceof L19.LatLng || typeof item?.lat === "number") {
2945
+ return L19.latLng(item.lat, item.lng);
2946
+ }
2947
+ return item;
2948
+ });
2949
+ }
2950
+ changedSegmentsIntersect(points, changedSegments, isPolygon) {
2951
+ if (points.length < (isPolygon ? 3 : 2)) return false;
2952
+ const segmentCount = isPolygon ? points.length : points.length - 1;
2953
+ if (segmentCount < 2) return false;
2954
+ const projected = points.map((point5) => this.map.latLngToLayerPoint(point5));
2955
+ for (const changedIndex of changedSegments) {
2956
+ if (changedIndex < 0 || changedIndex >= segmentCount) continue;
2957
+ const aIndex = changedIndex;
2958
+ const bIndex = this.segmentEndIndex(changedIndex, points.length, isPolygon);
2959
+ for (let otherIndex = 0; otherIndex < segmentCount; otherIndex++) {
2960
+ if (otherIndex === changedIndex) continue;
2961
+ const cIndex = otherIndex;
2962
+ const dIndex = this.segmentEndIndex(otherIndex, points.length, isPolygon);
2963
+ if (this.segmentsShareEndpoint(aIndex, bIndex, cIndex, dIndex)) continue;
2964
+ if (segmentsIntersect(
2965
+ projected[aIndex],
2966
+ projected[bIndex],
2967
+ projected[cIndex],
2968
+ projected[dIndex]
2969
+ )) {
2970
+ return true;
2971
+ }
2972
+ }
2973
+ }
2974
+ return false;
2975
+ }
2976
+ segmentEndIndex(segmentIndex, pointCount, isPolygon) {
2977
+ return isPolygon ? (segmentIndex + 1) % pointCount : segmentIndex + 1;
2978
+ }
2979
+ segmentsShareEndpoint(aStart, aEnd, bStart, bEnd) {
2980
+ return aStart === bStart || aStart === bEnd || aEnd === bStart || aEnd === bEnd;
2981
+ }
2982
+ sameLatLng(a, b) {
2983
+ return a.lat === b.lat && a.lng === b.lng;
2984
+ }
2985
+ setHandleMeta(marker2, meta) {
2986
+ marker2._anvilHandleMeta = meta;
2987
+ }
2988
+ hitTestHandle(latlng) {
2989
+ const mousePoint = this.map.latLngToContainerPoint(latlng);
2990
+ let bestMatch = null;
2991
+ let bestDistance = 12;
2992
+ const considerMarker = (marker2, meta) => {
2993
+ const point5 = this.map.latLngToContainerPoint(marker2.getLatLng());
2994
+ const distance = point5.distanceTo(mousePoint);
2995
+ if (distance >= bestDistance) return;
2996
+ bestDistance = distance;
2997
+ if (meta.kind === "ghost") {
2998
+ bestMatch = { kind: "ghost", segment: meta.segment, marker: marker2 };
2999
+ return;
3000
+ }
3001
+ if (meta.kind === "circle") {
3002
+ bestMatch = { kind: "circle", circle: meta.circle, marker: marker2 };
3003
+ return;
3004
+ }
3005
+ bestMatch = { kind: "vertex", group: meta.group, marker: marker2 };
3006
+ };
3007
+ this.markers.forEach((marker2) => {
3008
+ const meta = marker2._anvilHandleMeta;
3009
+ if (meta) considerMarker(marker2, meta);
3010
+ });
3011
+ if (this.ghostMarker) {
3012
+ const segment = this.ghostMarker._activeSeg;
3013
+ if (segment) considerMarker(this.ghostMarker, { kind: "ghost", segment });
3014
+ }
3015
+ return bestMatch;
3016
+ }
3017
+ createHandleRenderer() {
3018
+ if (typeof document === "undefined") return void 0;
3019
+ if (typeof navigator !== "undefined" && /jsdom|node\.js/i.test(navigator.userAgent)) return void 0;
3020
+ try {
3021
+ const canvas2 = document.createElement("canvas");
3022
+ if (typeof canvas2.getContext === "function" && canvas2.getContext("2d")) {
3023
+ return L19.canvas({ padding: 0.5, pane: this.handlePaneName });
3024
+ }
3025
+ } catch {
3026
+ return void 0;
3027
+ }
3028
+ return L19.svg({ padding: 0.5, pane: this.handlePaneName });
3029
+ }
3030
+ ensureHandlePane() {
3031
+ const existingPane = this.map.getPane(this.handlePaneName);
3032
+ const pane = existingPane || this.map.createPane(this.handlePaneName);
3033
+ pane.style.zIndex = "650";
3034
+ pane.style.pointerEvents = "none";
3035
+ }
3036
+ stopLeafletEvent(e) {
3037
+ L19.DomEvent.stopPropagation(e);
3038
+ const originalEvent = e?.originalEvent;
3039
+ if (originalEvent) {
3040
+ L19.DomEvent.preventDefault(originalEvent);
3041
+ L19.DomEvent.stopPropagation(originalEvent);
3042
+ }
3043
+ }
3044
+ blurActiveElement() {
3045
+ if (typeof document === "undefined") return;
3046
+ const activeElement = document.activeElement;
3047
+ activeElement?.blur?.();
3048
+ }
3049
+ };
3050
+ __publicField(_EditMode, "OVERLAP_AREA_TOLERANCE", 1e-3);
3051
+ var EditMode = _EditMode;
3052
+
3053
+ // src/modes/topology-mode.ts
3054
+ var TopologyMode = class extends EditMode {
3055
+ constructor(map, store, options = {}) {
3056
+ super(map, store, options, {
3057
+ modeId: "topology" /* Topology */,
3058
+ autoSelectAll: true,
3059
+ allowLayerSelection: false,
3060
+ clearSelectionOnMapClick: false,
3061
+ defaultSelectionPathOptions: {
3062
+ color: "#0891b2",
3063
+ weight: 3,
3064
+ opacity: 0.95
3065
+ },
3066
+ defaultHandleOptions: {
3067
+ radius: 5,
3068
+ color: "#0891b2",
3069
+ fillColor: "#ecfeff",
3070
+ fillOpacity: 1,
3071
+ weight: 2
3072
+ },
3073
+ defaultGhostHandleOptions: {
3074
+ radius: 4,
3075
+ color: "#cffafe",
3076
+ fillColor: "#0891b2",
3077
+ fillOpacity: 0.9,
3078
+ weight: 2
3079
+ }
3080
+ });
2141
3081
  }
2142
3082
  };
2143
3083
 
2144
3084
  // src/modes/delete-mode.ts
2145
- var L19 = __toESM(require("leaflet"));
3085
+ var L20 = __toESM(require("leaflet"));
2146
3086
  var DeleteMode = class {
2147
3087
  constructor(map, store) {
2148
3088
  this.map = map;
@@ -2168,18 +3108,18 @@ var DeleteMode = class {
2168
3108
  }
2169
3109
  addLayerListener(layer) {
2170
3110
  layer.on("click", this.onClick, this);
2171
- if (layer instanceof L19.Path || layer instanceof L19.Marker) {
3111
+ if (layer instanceof L20.Path || layer instanceof L20.Marker) {
2172
3112
  layer.getElement()?.style.setProperty("cursor", "pointer");
2173
3113
  }
2174
3114
  }
2175
3115
  removeLayerListener(layer) {
2176
3116
  layer.off("click", this.onClick, this);
2177
- if (layer instanceof L19.Path || layer instanceof L19.Marker) {
3117
+ if (layer instanceof L20.Path || layer instanceof L20.Marker) {
2178
3118
  layer.getElement()?.style.setProperty("cursor", "");
2179
3119
  }
2180
3120
  }
2181
3121
  onClick(e) {
2182
- L19.DomEvent.stopPropagation(e);
3122
+ L20.DomEvent.stopPropagation(e);
2183
3123
  const layer = e.target;
2184
3124
  this.map.removeLayer(layer);
2185
3125
  this.map.fire(ANVIL_EVENTS.DELETED, { layer });
@@ -2187,11 +3127,11 @@ var DeleteMode = class {
2187
3127
  };
2188
3128
 
2189
3129
  // src/layers/layer-store.ts
2190
- var L20 = __toESM(require("leaflet"));
3130
+ var L21 = __toESM(require("leaflet"));
2191
3131
  var LayerStore = class {
2192
3132
  constructor(map, layerGroup) {
2193
3133
  __publicField(this, "group");
2194
- this.group = layerGroup || L20.featureGroup();
3134
+ this.group = layerGroup || L21.featureGroup();
2195
3135
  this.group.addTo(map);
2196
3136
  }
2197
3137
  addLayer(layer) {
@@ -2212,29 +3152,31 @@ var LayerStore = class {
2212
3152
  };
2213
3153
 
2214
3154
  // src/controls/anvil-control.ts
2215
- var L21 = __toESM(require("leaflet"));
3155
+ var L22 = __toESM(require("leaflet"));
2216
3156
  var import_lucide = require("lucide");
2217
3157
  var ANVIL_TOOLBAR_STYLE_ID = "anvil-toolbar-styles";
2218
3158
  var MODE_CONFIGS = [
2219
- { id: "marker" /* Marker */, title: "Marker", icon: import_lucide.MapPin },
2220
- { id: "polyline" /* Polyline */, title: "Line", icon: import_lucide.Waypoints },
2221
- { id: "polygon" /* Polygon */, title: "Polygon", icon: import_lucide.Pentagon },
2222
- { id: "rectangle" /* Rectangle */, title: "Rectangle", icon: import_lucide.RectangleHorizontal },
2223
- { id: "square" /* Square */, title: "Square", icon: import_lucide.Square },
2224
- { id: "triangle" /* Triangle */, title: "Triangle", icon: import_lucide.Triangle },
2225
- { id: "circle" /* Circle */, title: "Circle", icon: import_lucide.Circle },
2226
- { id: "freehand" /* Freehand */, title: "Freehand", icon: import_lucide.Hand },
2227
- { id: "cut" /* Cut */, title: "Cut", icon: import_lucide.Scissors },
2228
- { id: "split" /* Split */, title: "Split", icon: import_lucide.Split },
2229
- { id: "union" /* Union */, title: "Union", icon: import_lucide.SquaresUnite },
2230
- { id: "subtract" /* Subtract */, title: "Subtract", icon: import_lucide.SquaresSubtract },
2231
- { id: "drag" /* Drag */, title: "Drag", icon: import_lucide.Move },
2232
- { id: "scale" /* Scale */, title: "Scale", icon: import_lucide.Scaling },
2233
- { id: "rotate" /* Rotate */, title: "Rotate", icon: import_lucide.RotateCw },
2234
- { id: "edit" /* Edit */, title: "Edit", icon: import_lucide.SquarePen },
2235
- { id: "delete" /* Delete */, title: "Delete", icon: import_lucide.Trash2 },
2236
- { id: "off" /* Off */, title: "Turn Off", icon: import_lucide.Power }
3159
+ { id: "marker" /* Marker */, defaultTooltip: "Marker", icon: import_lucide.MapPin },
3160
+ { id: "polyline" /* Polyline */, defaultTooltip: "Line", icon: import_lucide.Waypoints },
3161
+ { id: "polygon" /* Polygon */, defaultTooltip: "Polygon", icon: import_lucide.Pentagon },
3162
+ { id: "rectangle" /* Rectangle */, defaultTooltip: "Rectangle", icon: import_lucide.RectangleHorizontal },
3163
+ { id: "square" /* Square */, defaultTooltip: "Square", icon: import_lucide.Square },
3164
+ { id: "triangle" /* Triangle */, defaultTooltip: "Triangle", icon: import_lucide.Triangle },
3165
+ { id: "circle" /* Circle */, defaultTooltip: "Circle", icon: import_lucide.Circle },
3166
+ { id: "freehand" /* Freehand */, defaultTooltip: "Freehand", icon: import_lucide.Hand },
3167
+ { id: "cut" /* Cut */, defaultTooltip: "Cut", icon: import_lucide.Scissors },
3168
+ { id: "split" /* Split */, defaultTooltip: "Split", icon: import_lucide.Split },
3169
+ { id: "union" /* Union */, defaultTooltip: "Union", icon: import_lucide.SquaresUnite },
3170
+ { id: "subtract" /* Subtract */, defaultTooltip: "Subtract", icon: import_lucide.SquaresSubtract },
3171
+ { id: "drag" /* Drag */, defaultTooltip: "Drag", icon: import_lucide.Move },
3172
+ { id: "scale" /* Scale */, defaultTooltip: "Scale", icon: import_lucide.Scaling },
3173
+ { id: "rotate" /* Rotate */, defaultTooltip: "Rotate", icon: import_lucide.RotateCw },
3174
+ { id: "edit" /* Edit */, defaultTooltip: "Edit", icon: import_lucide.SquarePen },
3175
+ { id: "topology" /* Topology */, defaultTooltip: "Topology", icon: import_lucide.Network },
3176
+ { id: "delete" /* Delete */, defaultTooltip: "Delete", icon: import_lucide.Trash2 },
3177
+ { id: "off" /* Off */, defaultTooltip: "Turn Off", icon: import_lucide.Power }
2237
3178
  ];
3179
+ var MODE_CONFIGS_BY_ID = new Map(MODE_CONFIGS.map((config) => [config.id, config]));
2238
3180
  function ensureToolbarStyles() {
2239
3181
  if (typeof document === "undefined" || document.getElementById(ANVIL_TOOLBAR_STYLE_ID)) return;
2240
3182
  const style = document.createElement("style");
@@ -2304,9 +3246,15 @@ function buildIcon(iconNode) {
2304
3246
  focusable: "false"
2305
3247
  });
2306
3248
  }
2307
- var AnvilControl = class extends L21.Control {
3249
+ function resolveModeTooltip(config, modeTooltips) {
3250
+ if (typeof modeTooltips === "function") {
3251
+ return modeTooltips(config.id, config.defaultTooltip) ?? config.defaultTooltip;
3252
+ }
3253
+ return modeTooltips?.[config.id] ?? config.defaultTooltip;
3254
+ }
3255
+ var AnvilControl = class extends L22.Control {
2308
3256
  constructor(anvil, options) {
2309
- super(L21.Util.extend({ position: "topleft" }, options));
3257
+ super(L22.Util.extend({ position: "topleft" }, options));
2310
3258
  __publicField(this, "_btns", {});
2311
3259
  __publicField(this, "_anvil");
2312
3260
  __publicField(this, "_options");
@@ -2315,19 +3263,24 @@ var AnvilControl = class extends L21.Control {
2315
3263
  }
2316
3264
  onAdd(map) {
2317
3265
  ensureToolbarStyles();
2318
- const container = L21.DomUtil.create("div", "anvil-toolbar-container");
3266
+ const container = L22.DomUtil.create("div", "anvil-toolbar-container");
2319
3267
  const modesInput = this._options.modes || MODE_CONFIGS.filter(({ id }) => id !== "off" /* Off */).map(({ id }) => id);
2320
3268
  const blocks = Array.isArray(modesInput[0]) ? modesInput : [modesInput];
2321
3269
  const createButton = (group, config) => {
2322
- const btn = L21.DomUtil.create("a", "anvil-control-btn", group);
3270
+ const btn = L22.DomUtil.create("a", "anvil-control-btn", group);
2323
3271
  btn.href = "#";
2324
- btn.title = config.title;
2325
3272
  btn.setAttribute("role", "button");
2326
- btn.setAttribute("aria-label", config.title);
2327
3273
  btn.appendChild(buildIcon(config.icon));
2328
- L21.DomEvent.disableClickPropagation(btn);
2329
- L21.DomEvent.on(btn, "click", (e) => {
2330
- L21.DomEvent.preventDefault(e);
3274
+ const syncTooltip = () => {
3275
+ const tooltip = resolveModeTooltip(config, this._options.modeTooltips);
3276
+ btn.title = tooltip;
3277
+ btn.setAttribute("aria-label", tooltip);
3278
+ };
3279
+ syncTooltip();
3280
+ L22.DomEvent.disableClickPropagation(btn);
3281
+ L22.DomEvent.on(btn, "mouseover focus", syncTooltip);
3282
+ L22.DomEvent.on(btn, "click", (e) => {
3283
+ L22.DomEvent.preventDefault(e);
2331
3284
  if (config.id === "off" /* Off */) {
2332
3285
  this._anvil.disable();
2333
3286
  } else {
@@ -2337,14 +3290,14 @@ var AnvilControl = class extends L21.Control {
2337
3290
  this._btns[config.id] = btn;
2338
3291
  };
2339
3292
  blocks.forEach((block, index) => {
2340
- const group = L21.DomUtil.create("div", "leaflet-bar anvil-toolbar-group", container);
3293
+ const group = L22.DomUtil.create("div", "leaflet-bar anvil-toolbar-group", container);
2341
3294
  block.forEach((modeId) => {
2342
- const config = MODE_CONFIGS.find(({ id }) => id === modeId);
3295
+ const config = MODE_CONFIGS_BY_ID.get(modeId);
2343
3296
  if (!config) return;
2344
3297
  createButton(group, config);
2345
3298
  });
2346
3299
  if (index === blocks.length - 1 && !this._btns["off" /* Off */]) {
2347
- createButton(group, MODE_CONFIGS.find(({ id }) => id === "off" /* Off */));
3300
+ createButton(group, MODE_CONFIGS_BY_ID.get("off" /* Off */));
2348
3301
  }
2349
3302
  });
2350
3303
  const updateFn = (mode) => {
@@ -2363,6 +3316,21 @@ function anvilControl(anvil, options) {
2363
3316
  }
2364
3317
 
2365
3318
  // src/anvil.ts
3319
+ var ANVIL_INTERACTION_STYLE_ID = "anvil-interaction-styles";
3320
+ function ensureInteractionStyles() {
3321
+ if (typeof document === "undefined" || document.getElementById(ANVIL_INTERACTION_STYLE_ID)) return;
3322
+ const style = document.createElement("style");
3323
+ style.id = ANVIL_INTERACTION_STYLE_ID;
3324
+ style.textContent = `
3325
+ .leaflet-container .leaflet-pane .leaflet-interactive:focus,
3326
+ .leaflet-container .leaflet-pane .leaflet-interactive:focus-visible,
3327
+ .leaflet-container .leaflet-pane svg path:focus,
3328
+ .leaflet-container .leaflet-pane svg path:focus-visible {
3329
+ outline: none !important;
3330
+ }
3331
+ `;
3332
+ document.head.appendChild(style);
3333
+ }
2366
3334
  var Anvil = class {
2367
3335
  constructor(map, options) {
2368
3336
  this.map = map;
@@ -2370,6 +3338,7 @@ var Anvil = class {
2370
3338
  __publicField(this, "store");
2371
3339
  __publicField(this, "options");
2372
3340
  __publicField(this, "control");
3341
+ ensureInteractionStyles();
2373
3342
  this.options = {
2374
3343
  snapping: false,
2375
3344
  snapDistance: 10,
@@ -2429,6 +3398,9 @@ var Anvil = class {
2429
3398
  if (flattenedModes.includes("edit" /* Edit */)) {
2430
3399
  this.modeManager.addMode("edit" /* Edit */, new EditMode(this.map, this.store, this.options));
2431
3400
  }
3401
+ if (flattenedModes.includes("topology" /* Topology */)) {
3402
+ this.modeManager.addMode("topology" /* Topology */, new TopologyMode(this.map, this.store, this.options));
3403
+ }
2432
3404
  if (flattenedModes.includes("delete" /* Delete */)) {
2433
3405
  this.modeManager.addMode("delete" /* Delete */, new DeleteMode(map, this.store));
2434
3406
  }
@@ -2440,7 +3412,8 @@ var Anvil = class {
2440
3412
  });
2441
3413
  this.control = anvilControl(this, {
2442
3414
  position: this.options.controlPosition,
2443
- modes: modesFromOptions
3415
+ modes: modesFromOptions,
3416
+ modeTooltips: this.options.modeTooltips
2444
3417
  });
2445
3418
  this.control?.addTo(this.map);
2446
3419
  }
@@ -2456,7 +3429,7 @@ var Anvil = class {
2456
3429
  };
2457
3430
 
2458
3431
  // src/index.ts
2459
- var leaflet = L22;
3432
+ var leaflet = L23;
2460
3433
  leaflet.Anvil = Anvil;
2461
3434
  leaflet.anvil = (map, options) => new Anvil(map, options);
2462
3435
  if (leaflet.Control) {