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/README.md +42 -7
- package/dist/index.d.mts +6 -2
- package/dist/index.d.ts +6 -2
- package/dist/index.js +1488 -515
- package/dist/index.mjs +1489 -515
- package/package.json +11 -4
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
|
|
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 && !
|
|
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
|
|
180
|
+
const flatten4 = (arr) => {
|
|
175
181
|
let result = [];
|
|
176
182
|
arr.forEach((item) => {
|
|
177
183
|
if (Array.isArray(item)) {
|
|
178
|
-
result = result.concat(
|
|
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(...
|
|
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
|
|
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
|
-
|
|
331
|
+
marker2.on("click", (e) => {
|
|
350
332
|
L3.DomEvent.stopPropagation(e);
|
|
351
333
|
this.finish();
|
|
352
334
|
});
|
|
353
|
-
this.markers.push(
|
|
335
|
+
this.markers.push(marker2);
|
|
354
336
|
}
|
|
355
337
|
}
|
|
356
338
|
finish() {
|
|
357
339
|
if (this.points.length < 3) return;
|
|
358
|
-
const
|
|
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:
|
|
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
|
|
487
|
-
this.map.fire(ANVIL_EVENTS.CREATED, { layer:
|
|
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
|
|
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:
|
|
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
|
|
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:
|
|
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
|
|
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
|
-
|
|
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(
|
|
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((
|
|
964
|
-
const polyGeo =
|
|
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:
|
|
968
|
-
this.map.removeLayer(
|
|
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
|
-
|
|
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
|
-
|
|
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 =
|
|
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 =
|
|
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
|
|
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
|
-
|
|
1076
|
-
|
|
1309
|
+
marker2.on("click", (e) => {
|
|
1310
|
+
L13.DomEvent.stopPropagation(e);
|
|
1077
1311
|
this.finish();
|
|
1078
1312
|
});
|
|
1079
|
-
this.markers.push(
|
|
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 =
|
|
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
|
|
1326
|
+
if (layer instanceof L13.Polygon) {
|
|
1094
1327
|
const polyGeo = layer.toGeoJSON();
|
|
1095
|
-
const intersections =
|
|
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
|
|
1106
|
-
const
|
|
1107
|
-
const
|
|
1108
|
-
const
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
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) =>
|
|
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
|
|
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
|
|
1650
|
+
if (arr[0] instanceof L13.LatLng || typeof arr[0].lat === "number") {
|
|
1159
1651
|
const ring = arr;
|
|
1160
|
-
const isPolygon = layer instanceof
|
|
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 =
|
|
1661
|
+
const intersectLL = L13.latLng(coord[1], coord[0]);
|
|
1170
1662
|
const p = this.map.latLngToContainerPoint(intersectLL);
|
|
1171
|
-
const closest =
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
|
1749
|
+
if (this.draggingLayer instanceof L14.Marker) {
|
|
1258
1750
|
this.initialLatLngs = this.draggingLayer.getLatLng();
|
|
1259
|
-
} else if (this.draggingLayer instanceof
|
|
1260
|
-
if (this.draggingLayer instanceof
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
1885
|
+
if (this.selectedLayer instanceof L15.Circle) {
|
|
1394
1886
|
this.selectedLayer.setRadius(this.initialRadius * ratio);
|
|
1395
|
-
} else if (this.selectedLayer instanceof
|
|
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
|
|
1898
|
+
return L15.latLng(lat, lng);
|
|
1407
1899
|
}
|
|
1408
1900
|
onMouseUp() {
|
|
1409
1901
|
if (this.selectedLayer) {
|
|
1410
|
-
if (this.selectedLayer instanceof
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
1954
|
+
L16.DomEvent.stopPropagation(e);
|
|
1463
1955
|
const layer = e.target;
|
|
1464
|
-
if (!this.store.hasLayer(layer) || !(layer instanceof
|
|
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
|
|
1965
|
+
const point5 = this.map.latLngToContainerPoint(e.latlng);
|
|
1474
1966
|
const centerPoint = this.map.latLngToContainerPoint(this.centerLatLng);
|
|
1475
|
-
this.startAngle = Math.atan2(
|
|
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
|
|
1980
|
+
const point5 = this.map.latLngToContainerPoint(e.latlng);
|
|
1489
1981
|
const centerPoint = this.map.latLngToContainerPoint(this.centerLatLng);
|
|
1490
|
-
const currentAngle = Math.atan2(
|
|
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
|
|
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 =
|
|
1504
|
-
const dy =
|
|
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
|
|
1532
|
-
var
|
|
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
|
|
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
|
|
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
|
-
|
|
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 =
|
|
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 =
|
|
2080
|
+
const flattened = turf5.flatten(united);
|
|
1589
2081
|
flattened.features.forEach((f) => {
|
|
1590
|
-
const newLayerGroup =
|
|
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
|
|
1611
|
-
var
|
|
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
|
|
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
|
|
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
|
-
|
|
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 =
|
|
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 =
|
|
2155
|
+
const flattened = turf6.flatten(diff);
|
|
1664
2156
|
flattened.features.forEach((f) => {
|
|
1665
|
-
const newLayerGroup =
|
|
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
|
|
1687
|
-
var
|
|
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, "
|
|
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
|
-
|
|
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("
|
|
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
|
-
|
|
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("
|
|
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
|
-
|
|
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
|
|
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
|
|
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((
|
|
1744
|
-
if (
|
|
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
|
-
|
|
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
|
|
1762
|
-
this.
|
|
1763
|
-
|
|
1764
|
-
|
|
1765
|
-
|
|
1766
|
-
|
|
1767
|
-
|
|
1768
|
-
|
|
1769
|
-
|
|
1770
|
-
|
|
1771
|
-
|
|
1772
|
-
|
|
1773
|
-
|
|
1774
|
-
|
|
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
|
-
|
|
1778
|
-
|
|
1779
|
-
const
|
|
1780
|
-
|
|
1781
|
-
|
|
1782
|
-
|
|
1783
|
-
|
|
1784
|
-
|
|
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
|
-
|
|
1796
|
-
|
|
1797
|
-
|
|
1798
|
-
}
|
|
1799
|
-
|
|
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
|
|
1805
|
-
group.marker =
|
|
1806
|
-
this.
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
|
|
1810
|
-
|
|
1811
|
-
|
|
1812
|
-
|
|
1813
|
-
|
|
1814
|
-
|
|
1815
|
-
|
|
1816
|
-
|
|
1817
|
-
|
|
1818
|
-
|
|
1819
|
-
|
|
1820
|
-
|
|
1821
|
-
|
|
1822
|
-
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
|
|
1829
|
-
|
|
1830
|
-
|
|
1831
|
-
|
|
1832
|
-
|
|
1833
|
-
|
|
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
|
-
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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.
|
|
2618
|
+
this.coverageSnapshot = null;
|
|
1923
2619
|
}
|
|
1924
|
-
|
|
1925
|
-
|
|
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
|
-
|
|
1941
|
-
|
|
1942
|
-
this.ghostMarker._activeSeg = segment;
|
|
1943
|
-
}
|
|
2625
|
+
this.ghostMarker.setLatLng(latlng);
|
|
2626
|
+
this.ghostMarker._activeSeg = segment;
|
|
1944
2627
|
return;
|
|
1945
2628
|
}
|
|
1946
|
-
const
|
|
1947
|
-
|
|
1948
|
-
|
|
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
|
|
2020
|
-
|
|
2021
|
-
|
|
2022
|
-
|
|
2023
|
-
}
|
|
2024
|
-
|
|
2025
|
-
|
|
2026
|
-
|
|
2027
|
-
|
|
2028
|
-
|
|
2029
|
-
|
|
2030
|
-
|
|
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
|
-
|
|
2033
|
-
this.map.fire(ANVIL_EVENTS.EDITED, { layer:
|
|
2649
|
+
marker2.on("dragend", () => {
|
|
2650
|
+
this.map.fire(ANVIL_EVENTS.EDITED, { layer: marker2 });
|
|
2034
2651
|
});
|
|
2035
|
-
|
|
2036
|
-
|
|
2037
|
-
|
|
2038
|
-
this.
|
|
2039
|
-
this.map.
|
|
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
|
-
|
|
2045
|
-
const
|
|
2046
|
-
|
|
2047
|
-
|
|
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
|
|
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((
|
|
2674
|
+
this.markers.forEach((marker2) => {
|
|
2675
|
+
this.map.removeLayer(marker2);
|
|
2676
|
+
});
|
|
2077
2677
|
this.markers = [];
|
|
2078
|
-
this.
|
|
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(
|
|
2090
|
-
|
|
2091
|
-
|
|
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
|
-
|
|
2108
|
-
const
|
|
2109
|
-
|
|
2110
|
-
|
|
2111
|
-
|
|
2112
|
-
|
|
2113
|
-
|
|
2114
|
-
|
|
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
|
-
|
|
2118
|
-
|
|
2119
|
-
|
|
2120
|
-
|
|
2786
|
+
...handle,
|
|
2787
|
+
pane: this.handlePaneName,
|
|
2788
|
+
renderer: this.handleRenderer,
|
|
2789
|
+
interactive: false,
|
|
2790
|
+
bubblingMouseEvents: false
|
|
2121
2791
|
};
|
|
2122
2792
|
}
|
|
2123
|
-
|
|
2124
|
-
const
|
|
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
|
-
|
|
2134
|
-
|
|
2135
|
-
|
|
2136
|
-
|
|
2796
|
+
...handle,
|
|
2797
|
+
pane: this.handlePaneName,
|
|
2798
|
+
renderer: this.handleRenderer,
|
|
2799
|
+
interactive: false,
|
|
2800
|
+
bubblingMouseEvents: false
|
|
2137
2801
|
};
|
|
2138
2802
|
}
|
|
2139
|
-
|
|
2140
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
|
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 ||
|
|
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
|
|
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 */,
|
|
2220
|
-
{ id: "polyline" /* Polyline */,
|
|
2221
|
-
{ id: "polygon" /* Polygon */,
|
|
2222
|
-
{ id: "rectangle" /* Rectangle */,
|
|
2223
|
-
{ id: "square" /* Square */,
|
|
2224
|
-
{ id: "triangle" /* Triangle */,
|
|
2225
|
-
{ id: "circle" /* Circle */,
|
|
2226
|
-
{ id: "freehand" /* Freehand */,
|
|
2227
|
-
{ id: "cut" /* Cut */,
|
|
2228
|
-
{ id: "split" /* Split */,
|
|
2229
|
-
{ id: "union" /* Union */,
|
|
2230
|
-
{ id: "subtract" /* Subtract */,
|
|
2231
|
-
{ id: "drag" /* Drag */,
|
|
2232
|
-
{ id: "scale" /* Scale */,
|
|
2233
|
-
{ id: "rotate" /* Rotate */,
|
|
2234
|
-
{ id: "edit" /* Edit */,
|
|
2235
|
-
{ id: "
|
|
2236
|
-
{ id: "
|
|
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
|
-
|
|
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(
|
|
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 =
|
|
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 =
|
|
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
|
-
|
|
2329
|
-
|
|
2330
|
-
|
|
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 =
|
|
3293
|
+
const group = L22.DomUtil.create("div", "leaflet-bar anvil-toolbar-group", container);
|
|
2341
3294
|
block.forEach((modeId) => {
|
|
2342
|
-
const config =
|
|
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,
|
|
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 =
|
|
3432
|
+
var leaflet = L23;
|
|
2460
3433
|
leaflet.Anvil = Anvil;
|
|
2461
3434
|
leaflet.anvil = (map, options) => new Anvil(map, options);
|
|
2462
3435
|
if (leaflet.Control) {
|