leaflet-polydraw
Version:
Advanced Leaflet plugin for freehand polygon drawing, with smart merging and powerful editing tools. Compatible with both Leaflet v1.x and v2.x.
24,207 lines • 866 kB
JavaScript
var __defProp = Object.defineProperty;
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
var _a, _b;
import * as L from "leaflet";
var LeafletVersion = /* @__PURE__ */ ((LeafletVersion2) => {
LeafletVersion2["V1"] = "1.x";
LeafletVersion2["V2"] = "2.x";
return LeafletVersion2;
})(LeafletVersion || {});
var DrawMode = /* @__PURE__ */ ((DrawMode2) => {
DrawMode2[DrawMode2["Off"] = 0] = "Off";
DrawMode2[DrawMode2["Add"] = 1] = "Add";
DrawMode2[DrawMode2["Edit"] = 2] = "Edit";
DrawMode2[DrawMode2["Subtract"] = 4] = "Subtract";
DrawMode2[DrawMode2["AppendMarker"] = 8] = "AppendMarker";
DrawMode2[DrawMode2["LoadPredefined"] = 16] = "LoadPredefined";
DrawMode2[DrawMode2["PointToPoint"] = 32] = "PointToPoint";
DrawMode2[DrawMode2["PointToPointSubtract"] = 64] = "PointToPointSubtract";
DrawMode2[DrawMode2["Clone"] = 128] = "Clone";
return DrawMode2;
})(DrawMode || {});
var MarkerPosition = /* @__PURE__ */ ((MarkerPosition2) => {
MarkerPosition2[MarkerPosition2["SouthWest"] = 0] = "SouthWest";
MarkerPosition2[MarkerPosition2["South"] = 1] = "South";
MarkerPosition2[MarkerPosition2["SouthEast"] = 2] = "SouthEast";
MarkerPosition2[MarkerPosition2["East"] = 3] = "East";
MarkerPosition2[MarkerPosition2["NorthEast"] = 4] = "NorthEast";
MarkerPosition2[MarkerPosition2["North"] = 5] = "North";
MarkerPosition2[MarkerPosition2["NorthWest"] = 6] = "NorthWest";
MarkerPosition2[MarkerPosition2["West"] = 7] = "West";
MarkerPosition2[MarkerPosition2["Hole"] = 8] = "Hole";
return MarkerPosition2;
})(MarkerPosition || {});
var DonutDirection = /* @__PURE__ */ ((DonutDirection2) => {
DonutDirection2["Both"] = "both";
DonutDirection2["Inward"] = "inward";
return DonutDirection2;
})(DonutDirection || {});
const defaultConfig = {
mergePolygons: true,
kinks: false,
tools: {
default: DrawMode.Off,
draw: true,
subtract: true,
p2p: true,
p2pSubtract: true,
clone: true,
erase: true,
eraseScope: "all"
},
modes: {
attachElbow: true,
dragElbow: true,
dragPolygons: true,
edgeDeletion: true
},
modifierSubtractMode: true,
interaction: {
drag: {
opacity: 0.7,
dragCursor: "move",
hoverCursor: "grab",
markerBehavior: "hide",
markerAnimationDuration: 200,
modifierSubtract: {
keys: {
windows: "ctrlKey",
mac: "metaKey",
linux: "ctrlKey"
},
hideMarkersOnDrag: true
}
},
edgeDeletion: {
keys: {
windows: "ctrlKey",
mac: "metaKey",
linux: "ctrlKey"
},
minVertices: 3
}
},
markers: {
deleteMarker: true,
infoMarker: true,
menuMarker: true,
coordsTitle: true,
zIndexOffset: 0,
markerIcon: {
styleClasses: ["polygon-marker"],
zIndexOffset: null
},
holeIcon: {
styleClasses: ["polygon-marker", "hole"],
zIndexOffset: null
},
markerInfoIcon: {
position: MarkerPosition.East,
showArea: true,
showPerimeter: true,
useMetrics: true,
usePerimeterMinValue: false,
areaLabel: "Area",
perimeterLabel: "Perimeter",
values: {
min: {
metric: "50",
imperial: "100"
},
unknown: {
metric: "-",
imperial: "-"
}
},
units: {
unknownUnit: "",
metric: {
onlyMetrics: true,
perimeter: {
m: "m",
km: "km"
},
area: {
m2: "m²",
km2: "km²",
daa: "daa",
ha: "ha"
}
},
imperial: {
perimeter: {
feet: "ft",
yards: "yd",
miles: "mi"
},
area: {
feet2: "ft²",
yards2: "yd²",
acres: "ac",
miles2: "mi²"
}
}
},
styleClasses: ["polygon-marker", "info"],
zIndexOffset: 1e4
},
markerMenuIcon: {
position: MarkerPosition.South,
styleClasses: ["polygon-marker", "menu"],
zIndexOffset: 1e4
},
markerDeleteIcon: {
position: MarkerPosition.North,
styleClasses: ["polygon-marker", "delete"],
zIndexOffset: 1e4
},
holeMarkers: {
menuMarker: false,
deleteMarker: true,
infoMarker: false
},
visualOptimization: {
toleranceMin: 5e-6,
toleranceMax: 5e-3,
curve: 1.35
}
},
styles: {
polyline: { weight: 2, opacity: 1, color: "#50622b" },
subtractLine: { weight: 2, opacity: 1, color: "#D9460F" },
polygon: {
weight: 2,
opacity: 1,
fillOpacity: 0.2,
smoothFactor: 0.3,
noClip: true,
color: "#50622b",
fillColor: "#b4cd8a"
},
hole: {
weight: 2,
opacity: 1,
fillOpacity: 0.5,
color: "#aa0000",
fillColor: "#ffcccc"
},
ui: {
controlButton: { backgroundColor: "#fff", color: "#000" },
controlButtonHover: { backgroundColor: "#f4f4f4" },
controlButtonActive: { backgroundColor: "rgb(128, 218, 255)", color: "#fff" },
indicatorActive: { backgroundColor: "#ffcc00" },
p2pMarker: { backgroundColor: "#fff", borderColor: "#50622b" },
p2pClosingMarker: { color: "#4CAF50" },
edgeHover: { color: "#7a9441" },
edgeDeletion: { color: "#D9460F" },
dragSubtract: { color: "#D9460F" }
}
},
polygonCreation: {
algorithm: "concaveman"
},
simplification: {
strategy: "simple",
simple: {
tolerance: 1e-4,
highQuality: false
},
dynamic: {
baseTolerance: 1e-4,
highQuality: false,
fractionGuard: 0.9,
multiplier: 2
}
},
polygonTools: {
simplify: {
enabled: true,
processHoles: true
},
doubleElbows: {
enabled: true,
processHoles: true
},
bbox: {
enabled: true,
processHoles: true,
addMidPointMarkers: true
},
bezier: {
enabled: true,
resolution: 1e4,
sharpness: 0.75,
resampleMultiplier: 10,
maxNodes: 1e3,
visualOptimizationLevel: 10,
ghostMarkers: false
},
scale: {
enabled: true
},
rotate: {
enabled: true
},
donut: {
enabled: true,
direction: DonutDirection.Both
},
visualOptimizationToggle: {
enabled: true
}
},
tooltips: {
enabled: true,
delayMs: 500,
direction: "left",
backgroundColor: "#645d5d",
color: "#ffffff"
},
history: {
capture: {
batch: true,
freehand: true,
pointToPoint: true,
addPredefinedPolygon: true,
eraseAll: true,
markerDrag: true,
polygonDrag: true,
polygonClone: true,
addVertex: true,
removeVertex: true,
removeHole: true,
modifierSubtract: true,
deletePolygon: true,
layerDelete: true,
layerReorder: true,
polygonActions: {
simplify: true,
doubleElbows: true,
bbox: true,
bezier: true,
scale: true,
rotate: true,
donut: true,
toggleOptimization: true,
polygonMenuAction: true
}
},
maxSize: 50
}
};
const EARTH_RADIUS = {
/**
* Mean radius in meters - good enough for this tool's precision requirements
* Used for distance calculations and area conversions
*/
MEAN_METERS: 6371e3,
/** Mean radius in kilometers */
MEAN_KILOMETERS: 6371,
/** Mean radius in miles */
MEAN_MILES: 3959
// WGS84 ellipsoid values (more precise but overkill for this application)
// WGS84_SEMI_MAJOR_AXIS: 6378137.0, // meters
// WGS84_SEMI_MINOR_AXIS: 6356752.314245, // meters
// WGS84_FLATTENING: 1 / 298.257223563,
};
const MATH = {
/** Degrees to radians conversion factor */
DEG_TO_RAD: Math.PI / 180,
/** Radians to degrees conversion factor */
RAD_TO_DEG: 180 / Math.PI,
/** Full circle in degrees */
FULL_CIRCLE_DEGREES: 360,
/** Full circle in radians */
FULL_CIRCLE_RADIANS: 2 * Math.PI
};
function point$1(coordinates, properties = {}) {
return {
type: "Feature",
geometry: {
type: "Point",
coordinates
},
properties
};
}
function lineString$1(coordinates, properties = {}) {
return {
type: "Feature",
geometry: {
type: "LineString",
coordinates
},
properties
};
}
function polygon$2(coordinates, properties = {}) {
return {
type: "Feature",
geometry: {
type: "Polygon",
coordinates
},
properties
};
}
function multiPolygon$2(coordinates, properties = {}) {
return {
type: "Feature",
geometry: {
type: "MultiPolygon",
coordinates
},
properties
};
}
function featureCollection$1(features) {
return {
type: "FeatureCollection",
features
};
}
function getCoords$1(feature2) {
if (feature2.geometry.type === "Polygon") {
return [feature2.geometry.coordinates];
} else if (feature2.geometry.type === "MultiPolygon") {
return feature2.geometry.coordinates;
}
throw new Error("Feature must be a Polygon or MultiPolygon");
}
function bbox$2(feature2) {
let minX = Infinity;
let minY = Infinity;
let maxX = -Infinity;
let maxY = -Infinity;
const processCoordinates = (coords) => {
if (Array.isArray(coords[0])) {
coords.forEach(
(item) => processCoordinates(item)
);
} else {
const [x, y] = coords;
minX = Math.min(minX, x);
minY = Math.min(minY, y);
maxX = Math.max(maxX, x);
maxY = Math.max(maxY, y);
}
};
const processGeometry = (geometry) => {
if (geometry.type === "GeometryCollection") {
const collection = geometry;
collection.geometries.forEach(processGeometry);
return;
}
processCoordinates(geometry.coordinates);
};
processGeometry(feature2.geometry);
return [minX, minY, maxX, maxY];
}
function bboxPolygon(bboxCoords) {
const [minX, minY, maxX, maxY] = bboxCoords;
return polygon$2([
[
[minX, minY],
[maxX, minY],
[maxX, maxY],
[minX, maxY],
[minX, minY]
]
]);
}
function distance$1(from, to, units = "kilometers") {
const fromCoords = Array.isArray(from) ? from : from.geometry.coordinates;
const toCoords = Array.isArray(to) ? to : to.geometry.coordinates;
const [lon1, lat1] = fromCoords;
const [lon2, lat2] = toCoords;
const R = units === "miles" ? EARTH_RADIUS.MEAN_MILES : units === "meters" ? EARTH_RADIUS.MEAN_METERS : EARTH_RADIUS.MEAN_KILOMETERS;
const dLat = toRadians(lat2 - lat1);
const dLon = toRadians(lon2 - lon1);
const a = Math.sin(dLat / 2) * Math.sin(dLat / 2) + Math.cos(toRadians(lat1)) * Math.cos(toRadians(lat2)) * Math.sin(dLon / 2) * Math.sin(dLon / 2);
const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
return R * c;
}
function midpoint(point1, point2) {
const coords1 = Array.isArray(point1) ? point1 : point1.geometry.coordinates;
const coords2 = Array.isArray(point2) ? point2 : point2.geometry.coordinates;
const [lon1, lat1] = coords1;
const [lon2, lat2] = coords2;
const midLon = (lon1 + lon2) / 2;
const midLat = (lat1 + lat2) / 2;
return point$1([midLon, midLat]);
}
function centroid$1(feature2) {
const coords = getCoords$1(feature2);
let totalX = 0;
let totalY = 0;
let totalPoints = 0;
for (const polygon2 of coords) {
for (const ring of polygon2) {
for (const coord of ring) {
totalX += coord[0];
totalY += coord[1];
totalPoints++;
}
}
}
return point$1([totalX / totalPoints, totalY / totalPoints]);
}
function toRadians(degrees) {
return degrees * MATH.DEG_TO_RAD;
}
var earthRadius = 63710088e-1;
var factors = {
centimeters: earthRadius * 100,
centimetres: earthRadius * 100,
degrees: 360 / (2 * Math.PI),
feet: earthRadius * 3.28084,
inches: earthRadius * 39.37,
kilometers: earthRadius / 1e3,
kilometres: earthRadius / 1e3,
meters: earthRadius,
metres: earthRadius,
miles: earthRadius / 1609.344,
millimeters: earthRadius * 1e3,
millimetres: earthRadius * 1e3,
nauticalmiles: earthRadius / 1852,
radians: 1,
yards: earthRadius * 1.0936
};
function feature$1(geom, properties, options = {}) {
const feat = { type: "Feature" };
if (options.id === 0 || options.id) {
feat.id = options.id;
}
if (options.bbox) {
feat.bbox = options.bbox;
}
feat.properties = properties || {};
feat.geometry = geom;
return feat;
}
function point(coordinates, properties, options = {}) {
if (!coordinates) {
throw new Error("coordinates is required");
}
if (!Array.isArray(coordinates)) {
throw new Error("coordinates must be an Array");
}
if (coordinates.length < 2) {
throw new Error("coordinates must be at least 2 numbers long");
}
if (!isNumber(coordinates[0]) || !isNumber(coordinates[1])) {
throw new Error("coordinates must contain numbers");
}
const geom = {
type: "Point",
coordinates
};
return feature$1(geom, properties, options);
}
function polygon$1(coordinates, properties, options = {}) {
for (const ring of coordinates) {
if (ring.length < 4) {
throw new Error(
"Each LinearRing of a Polygon must have 4 or more Positions."
);
}
if (ring[ring.length - 1].length !== ring[0].length) {
throw new Error("First and last Position are not equivalent.");
}
for (let j = 0; j < ring[ring.length - 1].length; j++) {
if (ring[ring.length - 1][j] !== ring[0][j]) {
throw new Error("First and last Position are not equivalent.");
}
}
}
const geom = {
type: "Polygon",
coordinates
};
return feature$1(geom, properties, options);
}
function lineString(coordinates, properties, options = {}) {
if (coordinates.length < 2) {
throw new Error("coordinates must be an array of two or more positions");
}
const geom = {
type: "LineString",
coordinates
};
return feature$1(geom, properties, options);
}
function featureCollection(features, options = {}) {
const fc = { type: "FeatureCollection" };
if (options.id) {
fc.id = options.id;
}
if (options.bbox) {
fc.bbox = options.bbox;
}
fc.features = features;
return fc;
}
function multiPolygon$1(coordinates, properties, options = {}) {
const geom = {
type: "MultiPolygon",
coordinates
};
return feature$1(geom, properties, options);
}
function radiansToLength(radians, units = "kilometers") {
const factor = factors[units];
if (!factor) {
throw new Error(units + " units is invalid");
}
return radians * factor;
}
function radiansToDegrees(radians) {
const normalisedRadians = radians % (2 * Math.PI);
return normalisedRadians * 180 / Math.PI;
}
function degreesToRadians(degrees) {
const normalisedDegrees = degrees % 360;
return normalisedDegrees * Math.PI / 180;
}
function isNumber(num) {
return !isNaN(num) && num !== null && !Array.isArray(num);
}
function isObject$1(input) {
return input !== null && typeof input === "object" && !Array.isArray(input);
}
function geomEach$9(geojson, callback) {
var i, j, g, geometry, stopG, geometryMaybeCollection, isGeometryCollection, featureProperties, featureBBox, featureId2, featureIndex = 0, isFeatureCollection = geojson.type === "FeatureCollection", isFeature = geojson.type === "Feature", stop = isFeatureCollection ? geojson.features.length : 1;
for (i = 0; i < stop; i++) {
geometryMaybeCollection = isFeatureCollection ? (
// @ts-expect-error: Known type conflict
geojson.features[i].geometry
) : isFeature ? (
// @ts-expect-error: Known type conflict
geojson.geometry
) : geojson;
featureProperties = isFeatureCollection ? (
// @ts-expect-error: Known type conflict
geojson.features[i].properties
) : isFeature ? (
// @ts-expect-error: Known type conflict
geojson.properties
) : {};
featureBBox = isFeatureCollection ? (
// @ts-expect-error: Known type conflict
geojson.features[i].bbox
) : isFeature ? (
// @ts-expect-error: Known type conflict
geojson.bbox
) : void 0;
featureId2 = isFeatureCollection ? (
// @ts-expect-error: Known type conflict
geojson.features[i].id
) : isFeature ? (
// @ts-expect-error: Known type conflict
geojson.id
) : void 0;
isGeometryCollection = geometryMaybeCollection ? geometryMaybeCollection.type === "GeometryCollection" : false;
stopG = isGeometryCollection ? geometryMaybeCollection.geometries.length : 1;
for (g = 0; g < stopG; g++) {
geometry = isGeometryCollection ? geometryMaybeCollection.geometries[g] : geometryMaybeCollection;
if (geometry === null) {
if (
// @ts-expect-error: Known type conflict
callback(
// @ts-expect-error: Known type conflict
null,
featureIndex,
featureProperties,
featureBBox,
featureId2
) === false
)
return false;
continue;
}
switch (geometry.type) {
case "Point":
case "LineString":
case "MultiPoint":
case "Polygon":
case "MultiLineString":
case "MultiPolygon": {
if (
// @ts-expect-error: Known type conflict
callback(
geometry,
featureIndex,
featureProperties,
featureBBox,
featureId2
) === false
)
return false;
break;
}
case "GeometryCollection": {
for (j = 0; j < geometry.geometries.length; j++) {
if (
// @ts-expect-error: Known type conflict
callback(
geometry.geometries[j],
featureIndex,
featureProperties,
featureBBox,
featureId2
) === false
)
return false;
}
break;
}
default:
throw new Error("Unknown Geometry Type");
}
}
featureIndex++;
}
}
function geomReduce(geojson, callback, initialValue) {
var previousValue = initialValue;
geomEach$9(
geojson,
function(currentGeometry, featureIndex, featureProperties, featureBBox, featureId2) {
if (featureIndex === 0 && initialValue === void 0)
previousValue = currentGeometry;
else
previousValue = callback(
// @ts-expect-error: Known type conflict
previousValue,
currentGeometry,
featureIndex,
featureProperties,
featureBBox,
featureId2
);
}
);
return previousValue;
}
function area(geojson) {
return geomReduce(
geojson,
(value, geom) => {
return value + calculateArea(geom);
},
0
);
}
function calculateArea(geom) {
let total = 0;
let i;
switch (geom.type) {
case "Polygon":
return polygonArea(geom.coordinates);
case "MultiPolygon":
for (i = 0; i < geom.coordinates.length; i++) {
total += polygonArea(geom.coordinates[i]);
}
return total;
case "Point":
case "MultiPoint":
case "LineString":
case "MultiLineString":
return 0;
}
return 0;
}
function polygonArea(coords) {
let total = 0;
if (coords && coords.length > 0) {
total += Math.abs(ringArea(coords[0]));
for (let i = 1; i < coords.length; i++) {
total -= Math.abs(ringArea(coords[i]));
}
}
return total;
}
var FACTOR = earthRadius * earthRadius / 2;
var PI_OVER_180 = Math.PI / 180;
function ringArea(coords) {
const coordsLength = coords.length - 1;
if (coordsLength <= 2) return 0;
let total = 0;
let i = 0;
while (i < coordsLength) {
const lower = coords[i];
const middle = coords[i + 1 === coordsLength ? 0 : i + 1];
const upper = coords[i + 2 >= coordsLength ? (i + 2) % coordsLength : i + 2];
const lowerX = lower[0] * PI_OVER_180;
const middleY = middle[1] * PI_OVER_180;
const upperX = upper[0] * PI_OVER_180;
total += (upperX - lowerX) * Math.sin(middleY);
i++;
}
return total * FACTOR;
}
var index_default$g = area;
function coordEach$6(geojson, callback, excludeWrapCoord) {
if (geojson === null) return;
var j, k, l, geometry, stopG, coords, geometryMaybeCollection, wrapShrink = 0, coordIndex = 0, isGeometryCollection, type = geojson.type, isFeatureCollection = type === "FeatureCollection", isFeature = type === "Feature", stop = isFeatureCollection ? geojson.features.length : 1;
for (var featureIndex = 0; featureIndex < stop; featureIndex++) {
geometryMaybeCollection = isFeatureCollection ? (
// @ts-expect-error: Known type conflict
geojson.features[featureIndex].geometry
) : isFeature ? (
// @ts-expect-error: Known type conflict
geojson.geometry
) : geojson;
isGeometryCollection = geometryMaybeCollection ? geometryMaybeCollection.type === "GeometryCollection" : false;
stopG = isGeometryCollection ? geometryMaybeCollection.geometries.length : 1;
for (var geomIndex = 0; geomIndex < stopG; geomIndex++) {
var multiFeatureIndex = 0;
var geometryIndex = 0;
geometry = isGeometryCollection ? geometryMaybeCollection.geometries[geomIndex] : geometryMaybeCollection;
if (geometry === null) continue;
coords = geometry.coordinates;
var geomType = geometry.type;
wrapShrink = 0;
switch (geomType) {
case null:
break;
case "Point":
if (
// @ts-expect-error: Known type conflict
callback(
coords,
coordIndex,
featureIndex,
multiFeatureIndex,
geometryIndex
) === false
)
return false;
coordIndex++;
multiFeatureIndex++;
break;
case "LineString":
case "MultiPoint":
for (j = 0; j < coords.length; j++) {
if (
// @ts-expect-error: Known type conflict
callback(
coords[j],
coordIndex,
featureIndex,
multiFeatureIndex,
geometryIndex
) === false
)
return false;
coordIndex++;
if (geomType === "MultiPoint") multiFeatureIndex++;
}
if (geomType === "LineString") multiFeatureIndex++;
break;
case "Polygon":
case "MultiLineString":
for (j = 0; j < coords.length; j++) {
for (k = 0; k < coords[j].length - wrapShrink; k++) {
if (
// @ts-expect-error: Known type conflict
callback(
coords[j][k],
coordIndex,
featureIndex,
multiFeatureIndex,
geometryIndex
) === false
)
return false;
coordIndex++;
}
if (geomType === "MultiLineString") multiFeatureIndex++;
if (geomType === "Polygon") geometryIndex++;
}
if (geomType === "Polygon") multiFeatureIndex++;
break;
case "MultiPolygon":
for (j = 0; j < coords.length; j++) {
geometryIndex = 0;
for (k = 0; k < coords[j].length; k++) {
for (l = 0; l < coords[j][k].length - wrapShrink; l++) {
if (
// @ts-expect-error: Known type conflict
callback(
coords[j][k][l],
coordIndex,
featureIndex,
multiFeatureIndex,
geometryIndex
) === false
)
return false;
coordIndex++;
}
geometryIndex++;
}
multiFeatureIndex++;
}
break;
case "GeometryCollection":
for (j = 0; j < geometry.geometries.length; j++)
if (
// @ts-expect-error: Known type conflict
coordEach$6(geometry.geometries[j], callback) === false
)
return false;
break;
default:
throw new Error("Unknown Geometry Type");
}
}
}
}
function getDefaultExportFromCjs(x) {
return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, "default") ? x["default"] : x;
}
function getAugmentedNamespace(n) {
if (Object.prototype.hasOwnProperty.call(n, "__esModule")) return n;
var f = n.default;
if (typeof f == "function") {
var a = function a2() {
if (this instanceof a2) {
return Reflect.construct(f, arguments, this.constructor);
}
return f.apply(this, arguments);
};
a.prototype = f.prototype;
} else a = {};
Object.defineProperty(a, "__esModule", { value: true });
Object.keys(n).forEach(function(k) {
var d = Object.getOwnPropertyDescriptor(n, k);
Object.defineProperty(a, k, d.get ? d : {
enumerable: true,
get: function() {
return n[k];
}
});
});
return a;
}
var concaveman$2 = { exports: {} };
var rbush_min$1 = { exports: {} };
var rbush_min = rbush_min$1.exports;
var hasRequiredRbush_min;
function requireRbush_min() {
if (hasRequiredRbush_min) return rbush_min$1.exports;
hasRequiredRbush_min = 1;
(function(module, exports) {
!(function(t, i) {
module.exports = i();
})(rbush_min, function() {
function t(t2, r2, e2, a2, h2) {
!(function t3(n2, r3, e3, a3, h3) {
for (; a3 > e3; ) {
if (a3 - e3 > 600) {
var o2 = a3 - e3 + 1, s2 = r3 - e3 + 1, l2 = Math.log(o2), f2 = 0.5 * Math.exp(2 * l2 / 3), u3 = 0.5 * Math.sqrt(l2 * f2 * (o2 - f2) / o2) * (s2 - o2 / 2 < 0 ? -1 : 1), m2 = Math.max(e3, Math.floor(r3 - s2 * f2 / o2 + u3)), c2 = Math.min(a3, Math.floor(r3 + (o2 - s2) * f2 / o2 + u3));
t3(n2, r3, m2, c2, h3);
}
var p2 = n2[r3], d2 = e3, x = a3;
for (i(n2, e3, r3), h3(n2[a3], p2) > 0 && i(n2, e3, a3); d2 < x; ) {
for (i(n2, d2, x), d2++, x--; h3(n2[d2], p2) < 0; ) d2++;
for (; h3(n2[x], p2) > 0; ) x--;
}
0 === h3(n2[e3], p2) ? i(n2, e3, x) : i(n2, ++x, a3), x <= r3 && (e3 = x + 1), r3 <= x && (a3 = x - 1);
}
})(t2, r2, e2 || 0, a2 || t2.length - 1, h2 || n);
}
function i(t2, i2, n2) {
var r2 = t2[i2];
t2[i2] = t2[n2], t2[n2] = r2;
}
function n(t2, i2) {
return t2 < i2 ? -1 : t2 > i2 ? 1 : 0;
}
var r = function(t2) {
void 0 === t2 && (t2 = 9), this._maxEntries = Math.max(4, t2), this._minEntries = Math.max(2, Math.ceil(0.4 * this._maxEntries)), this.clear();
};
function e(t2, i2, n2) {
if (!n2) return i2.indexOf(t2);
for (var r2 = 0; r2 < i2.length; r2++) if (n2(t2, i2[r2])) return r2;
return -1;
}
function a(t2, i2) {
h(t2, 0, t2.children.length, i2, t2);
}
function h(t2, i2, n2, r2, e2) {
e2 || (e2 = p(null)), e2.minX = 1 / 0, e2.minY = 1 / 0, e2.maxX = -1 / 0, e2.maxY = -1 / 0;
for (var a2 = i2; a2 < n2; a2++) {
var h2 = t2.children[a2];
o(e2, t2.leaf ? r2(h2) : h2);
}
return e2;
}
function o(t2, i2) {
return t2.minX = Math.min(t2.minX, i2.minX), t2.minY = Math.min(t2.minY, i2.minY), t2.maxX = Math.max(t2.maxX, i2.maxX), t2.maxY = Math.max(t2.maxY, i2.maxY), t2;
}
function s(t2, i2) {
return t2.minX - i2.minX;
}
function l(t2, i2) {
return t2.minY - i2.minY;
}
function f(t2) {
return (t2.maxX - t2.minX) * (t2.maxY - t2.minY);
}
function u2(t2) {
return t2.maxX - t2.minX + (t2.maxY - t2.minY);
}
function m(t2, i2) {
return t2.minX <= i2.minX && t2.minY <= i2.minY && i2.maxX <= t2.maxX && i2.maxY <= t2.maxY;
}
function c(t2, i2) {
return i2.minX <= t2.maxX && i2.minY <= t2.maxY && i2.maxX >= t2.minX && i2.maxY >= t2.minY;
}
function p(t2) {
return { children: t2, height: 1, leaf: true, minX: 1 / 0, minY: 1 / 0, maxX: -1 / 0, maxY: -1 / 0 };
}
function d(i2, n2, r2, e2, a2) {
for (var h2 = [n2, r2]; h2.length; ) if (!((r2 = h2.pop()) - (n2 = h2.pop()) <= e2)) {
var o2 = n2 + Math.ceil((r2 - n2) / e2 / 2) * e2;
t(i2, o2, n2, r2, a2), h2.push(n2, o2, o2, r2);
}
}
return r.prototype.all = function() {
return this._all(this.data, []);
}, r.prototype.search = function(t2) {
var i2 = this.data, n2 = [];
if (!c(t2, i2)) return n2;
for (var r2 = this.toBBox, e2 = []; i2; ) {
for (var a2 = 0; a2 < i2.children.length; a2++) {
var h2 = i2.children[a2], o2 = i2.leaf ? r2(h2) : h2;
c(t2, o2) && (i2.leaf ? n2.push(h2) : m(t2, o2) ? this._all(h2, n2) : e2.push(h2));
}
i2 = e2.pop();
}
return n2;
}, r.prototype.collides = function(t2) {
var i2 = this.data;
if (!c(t2, i2)) return false;
for (var n2 = []; i2; ) {
for (var r2 = 0; r2 < i2.children.length; r2++) {
var e2 = i2.children[r2], a2 = i2.leaf ? this.toBBox(e2) : e2;
if (c(t2, a2)) {
if (i2.leaf || m(t2, a2)) return true;
n2.push(e2);
}
}
i2 = n2.pop();
}
return false;
}, r.prototype.load = function(t2) {
if (!t2 || !t2.length) return this;
if (t2.length < this._minEntries) {
for (var i2 = 0; i2 < t2.length; i2++) this.insert(t2[i2]);
return this;
}
var n2 = this._build(t2.slice(), 0, t2.length - 1, 0);
if (this.data.children.length) if (this.data.height === n2.height) this._splitRoot(this.data, n2);
else {
if (this.data.height < n2.height) {
var r2 = this.data;
this.data = n2, n2 = r2;
}
this._insert(n2, this.data.height - n2.height - 1, true);
}
else this.data = n2;
return this;
}, r.prototype.insert = function(t2) {
return t2 && this._insert(t2, this.data.height - 1), this;
}, r.prototype.clear = function() {
return this.data = p([]), this;
}, r.prototype.remove = function(t2, i2) {
if (!t2) return this;
for (var n2, r2, a2, h2 = this.data, o2 = this.toBBox(t2), s2 = [], l2 = []; h2 || s2.length; ) {
if (h2 || (h2 = s2.pop(), r2 = s2[s2.length - 1], n2 = l2.pop(), a2 = true), h2.leaf) {
var f2 = e(t2, h2.children, i2);
if (-1 !== f2) return h2.children.splice(f2, 1), s2.push(h2), this._condense(s2), this;
}
a2 || h2.leaf || !m(h2, o2) ? r2 ? (n2++, h2 = r2.children[n2], a2 = false) : h2 = null : (s2.push(h2), l2.push(n2), n2 = 0, r2 = h2, h2 = h2.children[0]);
}
return this;
}, r.prototype.toBBox = function(t2) {
return t2;
}, r.prototype.compareMinX = function(t2, i2) {
return t2.minX - i2.minX;
}, r.prototype.compareMinY = function(t2, i2) {
return t2.minY - i2.minY;
}, r.prototype.toJSON = function() {
return this.data;
}, r.prototype.fromJSON = function(t2) {
return this.data = t2, this;
}, r.prototype._all = function(t2, i2) {
for (var n2 = []; t2; ) t2.leaf ? i2.push.apply(i2, t2.children) : n2.push.apply(n2, t2.children), t2 = n2.pop();
return i2;
}, r.prototype._build = function(t2, i2, n2, r2) {
var e2, h2 = n2 - i2 + 1, o2 = this._maxEntries;
if (h2 <= o2) return a(e2 = p(t2.slice(i2, n2 + 1)), this.toBBox), e2;
r2 || (r2 = Math.ceil(Math.log(h2) / Math.log(o2)), o2 = Math.ceil(h2 / Math.pow(o2, r2 - 1))), (e2 = p([])).leaf = false, e2.height = r2;
var s2 = Math.ceil(h2 / o2), l2 = s2 * Math.ceil(Math.sqrt(o2));
d(t2, i2, n2, l2, this.compareMinX);
for (var f2 = i2; f2 <= n2; f2 += l2) {
var u3 = Math.min(f2 + l2 - 1, n2);
d(t2, f2, u3, s2, this.compareMinY);
for (var m2 = f2; m2 <= u3; m2 += s2) {
var c2 = Math.min(m2 + s2 - 1, u3);
e2.children.push(this._build(t2, m2, c2, r2 - 1));
}
}
return a(e2, this.toBBox), e2;
}, r.prototype._chooseSubtree = function(t2, i2, n2, r2) {
for (; r2.push(i2), !i2.leaf && r2.length - 1 !== n2; ) {
for (var e2 = 1 / 0, a2 = 1 / 0, h2 = void 0, o2 = 0; o2 < i2.children.length; o2++) {
var s2 = i2.children[o2], l2 = f(s2), u3 = (m2 = t2, c2 = s2, (Math.max(c2.maxX, m2.maxX) - Math.min(c2.minX, m2.minX)) * (Math.max(c2.maxY, m2.maxY) - Math.min(c2.minY, m2.minY)) - l2);
u3 < a2 ? (a2 = u3, e2 = l2 < e2 ? l2 : e2, h2 = s2) : u3 === a2 && l2 < e2 && (e2 = l2, h2 = s2);
}
i2 = h2 || i2.children[0];
}
var m2, c2;
return i2;
}, r.prototype._insert = function(t2, i2, n2) {
var r2 = n2 ? t2 : this.toBBox(t2), e2 = [], a2 = this._chooseSubtree(r2, this.data, i2, e2);
for (a2.children.push(t2), o(a2, r2); i2 >= 0 && e2[i2].children.length > this._maxEntries; ) this._split(e2, i2), i2--;
this._adjustParentBBoxes(r2, e2, i2);
}, r.prototype._split = function(t2, i2) {
var n2 = t2[i2], r2 = n2.children.length, e2 = this._minEntries;
this._chooseSplitAxis(n2, e2, r2);
var h2 = this._chooseSplitIndex(n2, e2, r2), o2 = p(n2.children.splice(h2, n2.children.length - h2));
o2.height = n2.height, o2.leaf = n2.leaf, a(n2, this.toBBox), a(o2, this.toBBox), i2 ? t2[i2 - 1].children.push(o2) : this._splitRoot(n2, o2);
}, r.prototype._splitRoot = function(t2, i2) {
this.data = p([t2, i2]), this.data.height = t2.height + 1, this.data.leaf = false, a(this.data, this.toBBox);
}, r.prototype._chooseSplitIndex = function(t2, i2, n2) {
for (var r2, e2, a2, o2, s2, l2, u3, m2 = 1 / 0, c2 = 1 / 0, p2 = i2; p2 <= n2 - i2; p2++) {
var d2 = h(t2, 0, p2, this.toBBox), x = h(t2, p2, n2, this.toBBox), v = (e2 = d2, a2 = x, o2 = void 0, s2 = void 0, l2 = void 0, u3 = void 0, o2 = Math.max(e2.minX, a2.minX), s2 = Math.max(e2.minY, a2.minY), l2 = Math.min(e2.maxX, a2.maxX), u3 = Math.min(e2.maxY, a2.maxY), Math.max(0, l2 - o2) * Math.max(0, u3 - s2)), M = f(d2) + f(x);
v < m2 ? (m2 = v, r2 = p2, c2 = M < c2 ? M : c2) : v === m2 && M < c2 && (c2 = M, r2 = p2);
}
return r2 || n2 - i2;
}, r.prototype._chooseSplitAxis = function(t2, i2, n2) {
var r2 = t2.leaf ? this.compareMinX : s, e2 = t2.leaf ? this.compareMinY : l;
this._allDistMargin(t2, i2, n2, r2) < this._allDistMargin(t2, i2, n2, e2) && t2.children.sort(r2);
}, r.prototype._allDistMargin = function(t2, i2, n2, r2) {
t2.children.sort(r2);
for (var e2 = this.toBBox, a2 = h(t2, 0, i2, e2), s2 = h(t2, n2 - i2, n2, e2), l2 = u2(a2) + u2(s2), f2 = i2; f2 < n2 - i2; f2++) {
var m2 = t2.children[f2];
o(a2, t2.leaf ? e2(m2) : m2), l2 += u2(a2);
}
for (var c2 = n2 - i2 - 1; c2 >= i2; c2--) {
var p2 = t2.children[c2];
o(s2, t2.leaf ? e2(p2) : p2), l2 += u2(s2);
}
return l2;
}, r.prototype._adjustParentBBoxes = function(t2, i2, n2) {
for (var r2 = n2; r2 >= 0; r2--) o(i2[r2], t2);
}, r.prototype._condense = function(t2) {
for (var i2 = t2.length - 1, n2 = void 0; i2 >= 0; i2--) 0 === t2[i2].children.length ? i2 > 0 ? (n2 = t2[i2 - 1].children).splice(n2.indexOf(t2[i2]), 1) : this.clear() : a(t2[i2], this.toBBox);
}, r;
});
})(rbush_min$1);
return rbush_min$1.exports;
}
let TinyQueue$2 = class TinyQueue {
constructor(data = [], compare2 = defaultCompare$3) {
this.data = data;
this.length = this.data.length;
this.compare = compare2;
if (this.length > 0) {
for (let i = (this.length >> 1) - 1; i >= 0; i--) this._down(i);
}
}
push(item) {
this.data.push(item);
this.length++;
this._up(this.length - 1);
}
pop() {
if (this.length === 0) return void 0;
const top = this.data[0];
const bottom = this.data.pop();
this.length--;
if (this.length > 0) {
this.data[0] = bottom;
this._down(0);
}
return top;
}
peek() {
return this.data[0];
}
_up(pos) {
const { data, compare: compare2 } = this;
const item = data[pos];
while (pos > 0) {
const parent = pos - 1 >> 1;
const current = data[parent];
if (compare2(item, current) >= 0) break;
data[pos] = current;
pos = parent;
}
data[pos] = item;
}
_down(pos) {
const { data, compare: compare2 } = this;
const halfLength = this.length >> 1;
const item = data[pos];
while (pos < halfLength) {
let left = (pos << 1) + 1;
let best = data[left];
const right = left + 1;
if (right < this.length && compare2(data[right], best) < 0) {
left = right;
best = data[right];
}
if (compare2(best, item) >= 0) break;
data[pos] = best;
pos = left;
}
data[pos] = item;
}
};
function defaultCompare$3(a, b) {
return a < b ? -1 : a > b ? 1 : 0;
}
const tinyqueue = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
__proto__: null,
default: TinyQueue$2
}, Symbol.toStringTag, { value: "Module" }));
const require$$1 = /* @__PURE__ */ getAugmentedNamespace(tinyqueue);
var pointInPolygon$2 = { exports: {} };
var flat;
var hasRequiredFlat;
function requireFlat() {
if (hasRequiredFlat) return flat;
hasRequiredFlat = 1;
flat = function pointInPolygonFlat(point2, vs, start, end) {
var x = point2[0], y = point2[1];
var inside2 = false;
if (start === void 0) start = 0;
if (end === void 0) end = vs.length;
var len = (end - start) / 2;
for (var i = 0, j = len - 1; i < len; j = i++) {
var xi = vs[start + i * 2 + 0], yi = vs[start + i * 2 + 1];
var xj = vs[start + j * 2 + 0], yj = vs[start + j * 2 + 1];
var intersect2 = yi > y !== yj > y && x < (xj - xi) * (y - yi) / (yj - yi) + xi;
if (intersect2) inside2 = !inside2;
}
return inside2;
};
return flat;
}
var nested;
var hasRequiredNested;
function requireNested() {
if (hasRequiredNested) return nested;
hasRequiredNested = 1;
nested = function pointInPolygonNested(point2, vs, start, end) {
var x = point2[0], y = point2[1];
var inside2 = false;
if (start === void 0) start = 0;
if (end === void 0) end = vs.length;
var len = end - start;
for (var i = 0, j = len - 1; i < len; j = i++) {
var xi = vs[i + start][0], yi = vs[i + start][1];
var xj = vs[j + start][0], yj = vs[j + start][1];
var intersect2 = yi > y !== yj > y && x < (xj - xi) * (y - yi) / (yj - yi) + xi;
if (intersect2) inside2 = !inside2;
}
return inside2;
};
return nested;
}
var hasRequiredPointInPolygon;
function requirePointInPolygon() {
if (hasRequiredPointInPolygon) return pointInPolygon$2.exports;
hasRequiredPointInPolygon = 1;
var pointInPolygonFlat = requireFlat();
var pointInPolygonNested = requireNested();
pointInPolygon$2.exports = function pointInPolygon2(point2, vs, start, end) {
if (vs.length > 0 && Array.isArray(vs[0])) {
return pointInPolygonNested(point2, vs, start, end);
} else {
return pointInPolygonFlat(point2, vs, start, end);
}
};
pointInPolygon$2.exports.nested = pointInPolygonNested;
pointInPolygon$2.exports.flat = pointInPolygonFlat;
return pointInPolygon$2.exports;
}
var orient2d_min$1 = { exports: {} };
var orient2d_min = orient2d_min$1.exports;
var hasRequiredOrient2d_min;
function requireOrient2d_min() {
if (hasRequiredOrient2d_min) return orient2d_min$1.exports;
hasRequiredOrient2d_min = 1;
(function(module, exports) {
!(function(t, e) {
e(exports);
})(orient2d_min, function(t) {
const e = 134217729, n = 33306690738754706e-32;
function r(t2, e2, n2, r2, o2) {
let f2, i2, u3, c2, s2 = e2[0], a2 = r2[0], d2 = 0, l2 = 0;
a2 > s2 == a2 > -s2 ? (f2 = s2, s2 = e2[++d2]) : (f2 = a2, a2 = r2[++l2]);
let p = 0;
if (d2 < t2 && l2 < n2) for (a2 > s2 == a2 > -s2 ? (u3 = f2 - ((i2 = s2 + f2) - s2), s2 = e2[++d2]) : (u3 = f2 - ((i2 = a2 + f2) - a2), a2 = r2[++l2]), f2 = i2, 0 !== u3 && (o2[p++] = u3); d2 < t2 && l2 < n2; ) a2 > s2 == a2 > -s2 ? (u3 = f2 - ((i2 = f2 + s2) - (c2 = i2 - f2)) + (s2 - c2), s2 = e2[++d2]) : (u3 = f2 - ((i2 = f2 + a2) - (c2 = i2 - f2)) + (a2 - c2), a2 = r2[++l2]), f2 = i2, 0 !== u3 && (o2[p++] = u3);
for (; d2 < t2; ) u3 = f2 - ((i2 = f2 + s2) - (c2 = i2 - f2)) + (s2 - c2), s2 = e2[++d2], f2 = i2, 0 !== u3 && (o2[p++] = u3);
for (; l2 < n2; ) u3 = f2 - ((i2 = f2 + a2) - (c2 = i2 - f2)) + (a2 - c2), a2 = r2[++l2], f2 = i2, 0 !== u3 && (o2[p++] = u3);
return 0 === f2 && 0 !== p || (o2[p++] = f2), p;
}
function o(t2) {
return new Float64Array(t2);
}
const f = 33306690738754716e-32, i = 22204460492503146e-32, u2 = 11093356479670487e-47, c = o(4), s = o(8), a = o(12), d = o(16), l = o(4);
t.orient2d = function(t2, o2, p, b, y, h) {
const M = (o2 - h) * (p - y), x = (t2 - y) * (b - h), j = M - x;
if (0 === M || 0 === x || M > 0 != x > 0) return j;
const m = Math.abs(M + x);
return Math.abs(j) >= f * m ? j : -(function(t3, o3, f2, p2, b2, y2, h2) {
let M2, x2, j2, m2, _, v, w, A, F, O, P, g, k, q, z, B2, C, D2;
const E = t3 - b2, G = f2 - b2, H = o3 - y2, I = p2 - y2;
_ = (z = (A = E - (w = (v = e * E) - (v - E))) * (O = I - (F = (v = e * I) - (v - I))) - ((q = E * I) - w * F - A * F - w * O)) - (P = z - (C = (A = H - (w = (v = e * H) - (v - H))) * (O = G - (F = (v = e * G) - (v - G))) - ((B2 = H * G) - w * F - A * F - w * O))), c[0] = z - (P + _) + (_ - C), _ = (k = q - ((g = q + P) - (_ = g - q)) + (P - _)) - (P = k - B2), c[1] = k - (P + _) + (_ - B2), _ = (D2 = g + P) - g, c[2] = g - (D2 - _) + (P - _), c[3] = D2;
let J = (function(t4, e2) {
let n2 = e2[0];
for (let r2 = 1; r2 < t4; r2++) n2 += e2[r2];
return n2;
})(4, c), K = i * h2;
if (J >= K || -J >= K) return J;
if (M2 = t3 - (E + (_ = t3 - E)) + (_ - b2), j2 = f2 - (G + (_ = f2 - G)) + (_ - b2), x2 = o3 - (H + (_ = o3 - H)) + (_ - y2), m2 = p2 - (I + (_ = p2 - I)) + (_ - y2), 0 === M2 && 0 === x2 && 0 === j2 && 0 === m2) return J;
if (K = u2 * h2 + n * Math.abs(J), (J += E * m2 + I * M2 - (H * j2 + G * x2)) >= K || -J >= K) return J;
_ = (z = (A = M2 - (w = (v = e * M2) - (v - M2))) * (O = I - (F = (v = e * I) - (v - I))) - ((q = M2 * I) - w * F - A * F - w * O)) - (P = z - (C = (A = x2 - (w = (v = e * x2) - (v - x2))) * (O = G - (F = (v = e * G) - (v - G))) - ((B2 = x2 * G) - w * F - A * F - w * O))), l[0] = z - (P + _) + (_ - C), _ = (k = q - ((g = q + P) - (_ = g - q)) + (P - _)) - (P = k - B2), l[1] = k - (P + _) + (_ - B2), _ = (D2 = g + P) - g, l[2] = g - (D2 - _) + (P - _), l[3] = D2;
const L2 = r(4, c, 4, l, s);
_ = (z = (A = E - (w = (v = e * E) - (v - E))) * (O = m2 - (F = (v = e * m2) - (v - m2))) - ((q = E * m2) - w * F - A * F - w * O)) - (P = z - (C = (A = H - (w = (v = e * H) - (v - H))) * (O = j2 - (F = (v = e * j2) - (v - j2))) - ((B2 = H * j2) - w * F - A * F - w * O))), l[0] = z - (P + _) + (_ - C), _ = (k = q - ((g = q + P) - (_ = g - q)) + (P - _)) - (P = k - B2), l[1] = k - (P + _) + (_ - B2), _ = (D2 = g + P) - g, l[2] = g - (D2 - _) + (P - _), l[3] = D2;
const N = r(L2, s, 4, l, a);
_ = (z = (A = M2 - (w = (v = e * M2) - (v - M2))) * (O = m2 - (F = (v = e * m2) - (v - m2))) - ((q = M2 * m2) - w * F - A * F - w * O)) - (P = z - (C = (A = x2 - (w = (v = e * x2) - (v - x2))) * (O = j2 - (F = (v = e * j2) - (v - j2))) - ((B2 = x2 * j2) - w * F - A * F - w * O))), l[0] = z - (P + _) + (_ - C), _ = (k = q - ((g = q + P) - (_ = g - q)) + (P - _)) - (P = k - B2), l[1] = k - (P + _) + (_ - B2), _ = (D2 = g + P) - g, l[2] = g - (D2 - _) + (P - _), l[3] = D2;
const Q = r(N, a, 4, l, d);
return d[Q - 1];
})(t2, o2, p, b, y, h, m);
}, t.orient2dfast = function(t2, e2, n2, r2, o2, f2) {
return (e2 - f2) * (n2 - o2) - (t2 - o2) * (r2 - f2);
}, Object.defineProperty(t, "__esModule", { value: true });
});
})(orient2d_min$1, orient2d_min$1.exports);
return orient2d_min$1.exports;
}
var hasRequiredConcaveman;
function requireConcaveman() {
if (hasRequiredConcaveman) return concaveman$2.exports;
hasRequiredConcaveman = 1;
var RBush4 = requireRbush_min();
var Queue = require$$1;
var pointInPolygon2 = requirePointInPolygon();
var orient = requireOrient2d_min().orient2d;
if (Queue.default) {
Queue = Queue.default;
}
concaveman$2.exports = concaveman2;
concaveman$2.exports.default = concaveman2;
function concaveman2(points, concavity, lengthThreshold) {
concavity = Math.max(0, concavity === void 0 ? 2 : concavity);
lengthThreshold = lengthThreshold || 0;
var hull = fastConvexHull2(points);
var tree = new RBush4(16);
tree.toBBox = function(a2) {
return {
minX: a2[0],
minY: a2[1],
maxX: a2[0],
maxY: a2[1]
};
};
tree.compareMinX = function(a2, b2) {
return a2[0] - b2[0];
};
tree.compareMinY = function(a2, b2) {
return a2[1] - b2[1];
};
tree.load(points);
var queue = [];
for (var i = 0, last; i < hull.length; i++) {
var p = hull[i];
tree.remove(p);
last = insertNode2(p, last);
queue.push(last);
}
var segTree = new RBush4(16);
for (i = 0; i < queue.length; i++) segTree.insert(updateBBox2(queue[i]));
var sqConcavity = concavity * concavity;
var sqLenThreshold = lengthThreshold * lengthThreshold;
while (queue.length) {
var node = queue.shift();
var a = node.p;
var b = node.next.p;
var sqLen = getSqDist2(a, b);
if (sqLen < sqLenThreshold) continue;
var maxSqLen = sqLen / sqConcavity;
p = findCandidate2(tree, node.prev.p, a, b, node.next.next.p, maxSqLen, segTree);
if (p && Math.min(getSqDist2(p, a), getSqDist2(p, b)) <= maxSqLen) {
queue.push(node);
queue.push(insertNode2(p, node));
tree.remove(p);
segTree.remove(node);
segTree.insert(updateBBox2(node));
segTree.insert(updateBBox2(node.next));
}
}
node = last;
var concave = [];
do {
concave.push(node.p);
node = node.next;
} while (node !== last);
concave.push(node.p);
return concave;
}
function findCandidate2(tree, a, b, c, d, maxDist, segTree) {
var queue = new Queue([], compareDist2);
var node = tree.data;
while (node) {
for (var i = 0; i < node.children.length; i++) {
var child = node.children[i];
var dist = node.leaf ? sqSegDist2(child, b, c) : sqSegBoxDist2(b, c, child);
if (dist > maxDist) continue;
queue.push({
node: child,
dist
});
}
while (queue.length && !queue.peek().node.children) {
var item = queue.pop();
var p = item.node;
var d0 = sqSegDist2(p, a, b);
var d1 = sqSegDist2(p, c, d);
if (item.dist < d0 && item.dist < d1 && noIntersections2(b, p, segTree) && noIntersections2(c, p, segTree)) return p;
}
node = queue.pop();
if (node) node = node.node;
}
return null;
}
function compareDist2(a, b) {
return a.dist - b.dist;
}
function sqSegBoxDist2(a, b, bbox2) {
if (inside2(a, bbox2) || inside2(b, bbox2)) return 0;
var d1 = sqSegSegDist2(a[0], a[1], b[0], b[1], bbox2.minX, bbox2.minY, bbox2.maxX, bbox2.minY);
if (d1 === 0) return 0;
var d2 = sqSegSegDist2(a[0], a[1], b[0], b[1], bbox2.minX, bbox2.minY, bbox2.minX, bbox2.maxY);
if (d2 === 0) return 0;
var d3 = sqSegSegDist2(a[0], a[1], b[0], b[1], bbox2.maxX, bbox2.minY, bbox2.maxX, bbox2.maxY);
if (d3 === 0) return 0;
var d4 = sqSegSegDist2(a[0], a[1], b[0], b[1], bbox2.minX, bbox2.maxY, bbox2.maxX, bbox2.maxY);
if (d4 === 0) return 0;
return Math.min(d1, d2, d3, d4);
}
function inside2(a, bbox2) {
return a[0] >= bbox2.minX && a[0] <= bbox2.maxX && a[1] >= bbox2.minY && a[1] <= bbox2.maxY;
}
function noIntersections2(a, b, segTree) {
var minX = Math.min(a[0], b[0]);
var minY = Math.min(a[1], b[1]);
var maxX = Math.max(a[0], b[0]);
var maxY = Math.max(a[1], b[1]);
var edges = segTree.search({ minX, minY, maxX, maxY });
for (var i = 0; i < edges.length; i++) {
if (intersects2(edges[i].p, edges[i].next.p, a, b)) return false;
}
return true;
}
function cross2(p1, p2, p3) {
return orient(p1[0], p1[1], p2[0], p2[1], p3[0], p3[1]);
}
function intersects2(p1, q1, p2, q2) {
return p1 !== q2 && q1 !== p2 && cross2(p1, q1, p2) > 0 !== cross2(p1, q1, q2) > 0 && cross2(p2, q2, p1) > 0 !== cross2(p2, q2, q1) > 0;
}
function updateBBox2(node) {
var p1 = node.p;
var p2 = node.next.p;
node.minX = Math.min(p1[0], p2[0]);
node.minY = Math.min(p1[1], p2[1]);
node.maxX = Math.max(p1[0], p2[0]);
node.maxY = Math.max(p1[1], p2[1]);
return node;
}
function fastConvexHull2(points) {
var left = points[0];
var top = points[0];
var right = points[0];
var bottom = points[0];
for (var i = 0; i < points.length; i++) {
var p = points[i];
if (p[0] < left[0]) left = p;
if (p[0] > right[0]) right = p;
if (p[1] < top[1]) top = p;
if (p[1] > bottom[1]) bottom = p;
}
var cull = [left, top, right, bottom];
var filtered = cull.slice();
for (i = 0; i < points.length; i++) {
if (!pointInPolygon2(points[i], cull)) filtered.push(points[i]);
}
return convexHull2(filtered);
}
function insertNode2(p, prev) {
var node = {
p,
prev: null,
next: null,
minX: 0,
minY: 0,
maxX: 0,
maxY: 0
};
if (!prev) {
node.prev = node;
node.next = node;
} else {
node.next = prev.next;
node.prev = prev;
prev.next.prev = node;
prev.next = node;
}
return node;
}
function getSqDist2(p1, p2) {
var dx = p1[0] - p2[0], dy = p1[1] - p2[1];
return dx * dx + dy * dy;
}
function sqSegDist2(p, p1, p2) {
var x = p1[0], y = p1[1], dx = p2[0] - x, dy = p2[1] - y;
if (dx !== 0 || dy !== 0) {
var t = ((p[0] - x) * dx + (p[1] - y) * dy) / (dx * dx + dy * dy);
if (t > 1) {
x = p2[0];
y = p2[1];
} else if (t > 0) {
x += dx * t;
y += dy * t;
}
}
dx = p[0] - x;
dy = p[1] - y;
return dx * dx + dy * dy;
}
function sqSegSegDist2(x0, y0, x1, y1, x2, y2, x3, y3) {
var ux = x1 - x0;
var uy = y1 - y0;
var vx = x3 - x2;
var vy = y3 - y2;
var wx = x0 - x2;
var wy = y0 - y2;
var a = ux * ux + uy * uy;
var b = ux * vx + uy * vy;
var c = vx * vx + vy * vy;
var d = ux * wx + uy * wy;
var e = vx * wx + vy * wy;
var D2 = a * c - b * b;
var sc, sN, tc, tN;
var sD = D2;
var tD = D2;
if (D2 === 0) {
sN = 0;
sD = 1;
tN = e;
tD = c;
} else {
sN = b * e - c * d;
tN = a * e - b * d;
if (sN < 0) {
sN = 0;
tN = e;
tD = c;
} else if (sN > sD) {
sN = sD;
tN = e + b;
tD = c;
}
}
if (tN < 0) {
tN = 0;
if (-d < 0) sN = 0;
else if (-d > a) sN = sD;
else {
sN = -d;
sD = a;
}
} else if (tN > tD) {
tN = tD;
if (-d + b < 0) sN = 0;
else if (-d + b > a) sN = sD;
else {
sN = -d + b;
sD = a;
}
}
sc = sN === 0 ? 0 : sN / sD;
tc = tN === 0 ? 0 : tN / tD;
var cx = (1 - sc) * x0 + sc * x1;
var cy = (1 - sc) * y0 + sc * y1;
var cx2 = (1 - tc) * x2 + tc * x3;
var cy2 = (1 - tc) * y2 + tc * y3;
var dx = cx2 - cx;
var dy = cy2 - cy;
return dx * dx + dy * dy;
}
function compareByX2(a, b) {
return a[0] === b[0] ? a[1] - b[1] : a[0] - b[0];
}
function convexHull2(points) {
points.sort(compareByX2);
var lower = [];
for (var i = 0; i < points.length; i++) {
while (lower.length >= 2 && cross2(lower[lower.length - 2], lower[lower.length - 1], points[i]) <= 0) {
lower.pop();
}
lower.push(points[i]);
}
var upper = [];
for (var ii = points.length - 1; ii >= 0; ii--) {
while (upper.length >= 2 && cross2(upper[upper.length - 2], upper[upper.length - 1], points[ii]) <= 0) {
upper.pop();
}
upper.push(points[ii]);
}
upper.pop();
lower.pop();
return lower.concat(upper);
}
return concaveman$2.exports;
}
var concavemanExports = requireConcaveman();
const concaveman$1 = /* @__PURE__ */ getDefaultExportFromCjs(concavemanExports);
function convex(geojson, options = {}) {
options.concavity = options.concavity || Infinity;
const points = [];
coordEach$6(geojson, (coord) => {
points.push([coord[0], coord[1]]);
});
if (!points.length) {
return null;
}
const convexHull2 = concaveman$1(points, options.concavity);
if (convexHull2.length > 3) {
return polygon$1([convexHull2]);
}
return null;
}
var index_default$f = convex;
function coordEach$5(geojson, callback, excludeWrapCoord) {
if (geojson === null) return;
var j, k, l, geometry, stopG, coords, geometryMaybeCollection, wrapShrink = 0, coordIndex = 0, isGeometryCollection, type = geojson.type, isFeatureCollection = type === "FeatureCollection", isFeature = type === "Feature", stop = isFeatureCollection ? geojson.features.length : 1;
for (var featureIndex = 0; featureIndex < stop; featureIndex++) {
geometryMaybeCollection = isFeatureCollection ? (
// @ts-expect-error: Known type conflict
geojson.features[featureIndex].geometry
) : isFeature ? (
// @ts-expect-error: Known type conflict
geojson.geometry
) : geojson;
isGeometryCollection = geometryMaybeCollection ? geometryMaybeCollection.type === "GeometryCollection" : false;
stopG = isGeometryCollection ? geometryMaybeCollection.geometries.length : 1;
for (var geomIndex = 0; geomIndex < stopG; geomIndex++) {
var multiFeatureIndex = 0;
var geometryIndex = 0;
geometry = isGeometryCollection ? geometryMaybeCollection.geometries[geomIndex] : geometryMaybeCollection;
if (geometry === null) continue;
coords = geometry.coordinates;
var geomType = geometry.type;
wrapShrink = geomType === "Polygon" || geomType === "MultiPolygon" ? 1 : 0;
switch (geomType) {
case null:
break;
case "Point":
if (
// @ts-expect-error: Known type conflict
callback(
coords,
coordIndex,
featureIndex,
multiFeatureIndex,
geometryIndex
) === false
)
return false;
coordIndex++;
multiFeatureIndex++;
break;
case "LineString":
case "MultiPoint":
for (j = 0; j < coords.length; j++) {
if (
// @ts-expect-error: Known type conflict
callback(
coords[j],
coordIndex,
featureIndex,
multiFeatureIndex,
geometryIndex
) === false
)
return false;
coordIndex++;
if (geomType === "MultiPoint") multiFeatureIndex++;
}
if (geomType === "LineString") multiFeatureIndex++;
break;
case "Polygon":
case "MultiLineString":
for (j = 0; j < coords.length; j++) {
for (k = 0; k < coords[j].length - wrapShrink; k++) {
if (
// @ts-expect-error: Known type conflict
callback(
coords[j][k],
coordIndex,
featureIndex,
multiFeatureIndex,
geometryIndex
) === false
)
return false;
coordIndex++;
}
if (geomType === "MultiLineString") multiFeatureIndex++;
if (geomType === "Polygon") geometryIndex++;
}
if (geomType === "Polygon") multiFeatureIndex++;
break;
case "MultiPolygon":
for (j = 0; j < coords.length; j++) {
geometryIndex = 0;
for (k = 0; k < coords[j].length; k++) {
for (l = 0; l < coords[j][k].length - wrapShrink; l++) {
if (
// @ts-expect-error: Known type conflict
callback(
coords[j][k][l],
coordIndex,
featureIndex,
multiFeatureIndex,
geometryIndex
) === false
)
return false;
coordIndex++;
}
geometryIndex++;
}
multiFeatureIndex++;
}
break;
case "GeometryCollection":
for (j = 0; j < geometry.geometries.length; j++)
if (
// @ts-expect-error: Known type conflict
coordEach$5(geometry.geometries[j], callback) === false
)
return false;
break;
default:
throw new Error("Unknown Geometry Type");
}
}
}
}
function centroid(geojson, options = {}) {
let xSum = 0;
let ySum = 0;
let len = 0;
coordEach$5(
geojson,
function(coord) {
xSum += coord[0];
ySum += coord[1];
len++;
}
);
return point([xSum / len, ySum / len], options.properties);
}
function getCoord(coord) {
if (!coord) {
throw new Error("coord is required");
}
if (!Array.isArray(coord)) {
if (coord.type === "Feature" && coord.geometry !== null && coord.geometry.type === "Point") {
return [...coord.geometry.coordinates];
}
if (coord.type === "Point") {
return [...coord.coordinates];
}
}
if (Array.isArray(coord) && coord.length >= 2 && !Array.isArray(coord[0]) && !Array.isArray(coord[1])) {
return [...coord];
}
throw new Error("coord must be GeoJSON Point or an Array of numbers");
}
function getCoords(coords) {
if (Array.isArray(coords)) {
return coords;
}
if (coords.type === "Feature") {
if (coords.geometry !== null) {
return coords.geometry.coordinates;
}
} else {
if (coords.coordinates) {
return coords.coordinates;
}
}
throw new Error(
"coords must be GeoJSON Feature, Geometry Object or an Array"
);
}
function getGeom(geojson) {
if (geojson.type === "Feature") {
return geojson.geometry;
}
return geojson;
}
function getType(geojson, _name) {
if (geojson.type === "FeatureCollection") {
return "FeatureCollection";
}
if (geojson.type === "GeometryCollection") {
return "GeometryCollection";
}
if (geojson.type === "Feature" && geojson.geometry !== null) {
return geojson.geometry.type;
}
return geojson.type;
}
function coordEach$4(geojson, callback, excludeWrapCoord) {
if (geojson === null) return;
var j, k, l, geometry, stopG, coords, geometryMaybeCollection, wrapShrink = 0, coordIndex = 0, isGeometryCollection, type = geojson.type, isFeatureCollection = type === "FeatureCollection", isFeature = type === "Feature", stop = isFeatureCollection ? geojson.features.length : 1;
for (var featureIndex = 0; featureIndex < stop; featureIndex++) {
geometryMaybeCollection = isFeatureCollection ? (
// @ts-expect-error: Known type conflict
geojson.features[featureIndex].geometry
) : isFeature ? (
// @ts-expect-error: Known type conflict
geojson.geometry
) : geojson;
isGeometryCollection = geometryMaybeCollection ? geometryMaybeCollection.type === "GeometryCollection" : false;
stopG = isGeometryCollection ? geometryMaybeCollection.geometries.length : 1;
for (var geomIndex = 0; geomIndex < stopG; geomIndex++) {
var multiFeatureIndex = 0;
var geometryIndex = 0;
geometry = isGeometryCollection ? geometryMaybeCollection.geometries[geomIndex] : geometryMaybeCollection;
if (geometry === null) continue;
coords = geometry.coordinates;
var geomType = geometry.type;
wrapShrink = 0;
switch (geomType) {
case null:
break;
case "Point":
if (
// @ts-expect-error: Known type conflict
callback(
coords,
coordIndex,
featureIndex,
multiFeatureIndex,
geometryIndex
) === false
)
return false;
coordIndex++;
multiFeatureIndex++;
break;
case "LineString":
case "MultiPoint":
for (j = 0; j < coords.length; j++) {
if (
// @ts-expect-error: Known type conflict
callback(
coords[j],
coordIndex,
featureIndex,
multiFeatureIndex,
geometryIndex
) === false
)
return false;
coordIndex++;
if (geomType === "MultiPoint") multiFeatureIndex++;
}
if (geomType === "LineString") multiFeatureIndex++;
break;
case "Polygon":
case "MultiLineString":
for (j = 0; j < coords.length; j++) {
for (k = 0; k < coords[j].length - wrapShrink; k++) {
if (
// @ts-expect-error: Known type conflict
callback(
coords[j][k],
coordIndex,
featureIndex,
multiFeatureIndex,
geometryIndex
) === false
)
return false;
coordIndex++;
}
if (geomType === "MultiLineString") multiFeatureIndex++;
if (geomType === "Polygon") geometryIndex++;
}
if (geomType === "Polygon") multiFeatureIndex++;
break;
case "MultiPolygon":
for (j = 0; j < coords.length; j++) {
geometryIndex = 0;
for (k = 0; k < coords[j].length; k++) {
for (l = 0; l < coords[j][k].length - wrapShrink; l++) {
if (
// @ts-expect-error: Known type conflict
callback(
coords[j][k][l],
coordIndex,
featureIndex,
multiFeatureIndex,
geometryIndex
) === false
)
return false;
coordIndex++;
}
geometryIndex++;
}
multiFeatureIndex++;
}
break;
case "GeometryCollection":
for (j = 0; j < geometry.geometries.length; j++)
if (
// @ts-expect-error: Known type conflict
coordEach$4(geometry.geometries[j], callback) === false
)
return false;
break;
default:
throw new Error("Unknown Geometry Type");
}
}
}
}
function centerOfMass(geojson, options = {}) {
switch (getType(geojson)) {
case "Point":
return point(getCoord(geojson), options.properties);
case "Polygon":
var coords = [];
coordEach$4(geojson, function(coord) {
coords.push(coord);
});
var centre = centroid(geojson, { properties: options.properties });
var translation = centre.geometry.coordinates;
var sx = 0;
var sy = 0;
var sArea = 0;
var i, pi, pj, xi, xj, yi, yj, a;
var neutralizedPoints = coords.map(function(point2) {
return [point2[0] - translation[0], point2[1] - translation[1]];
});
for (i = 0; i < coords.length - 1; i++) {
pi = neutralizedPoints[i];
xi = pi[0];
yi = pi[1];
pj = neutralizedPoints[i + 1];
xj = pj[0];
yj = pj[1];
a = xi * yj - xj * yi;
sArea += a;
sx += (xi + xj) * a;
sy += (yi + yj) * a;
}
if (sArea === 0) {
return centre;
} else {
var area2 = sArea * 0.5;
var areaFactor = 1 / (6 * area2);
return point(
[translation[0] + areaFactor * sx, translation[1] + areaFactor * sy],
options.properties
);
}
default:
var hull = convex(geojson);
if (hull) return centerOfMass(hull, { properties: options.properties });
else return centroid(geojson, { properties: options.properties });
}
}
var index_default$e = centerOfMass;
let TinyQueue$1 = class TinyQueue2 {
constructor(data = [], compare2 = defaultCompare$2) {
this.data = data;
this.length = this.data.length;
this.compare = compare2;
if (this.length > 0) {
for (let i = (this.length >> 1) - 1; i >= 0; i--) this._down(i);
}
}
push(item) {
this.data.push(item);
this.length++;
this._up(this.length - 1);
}
pop() {
if (this.length === 0) return void 0;
const top = this.data[0];
const bottom = this.data.pop();
this.length--;
if (this.length > 0) {
this.data[0] = bottom;
this._down(0);
}
return top;
}
peek() {
return this.data[0];
}
_up(pos) {
const { data, compare: compare2 } = this;
const item = data[pos];
while (pos > 0) {
const parent = pos - 1 >> 1;
const current = data[parent];
if (compare2(item, current) >= 0) break;
data[pos] = current;
pos = parent;
}
data[pos] = item;
}
_down(pos) {
const { data, compare: compare2 } = this;
const halfLength = this.length >> 1;
const item = data[pos];
while (pos < halfLength) {
let left = (pos << 1) + 1;
let best = data[left];
const right = left + 1;
if (right < this.length && compare2(data[right], best) < 0) {
left = right;
best = data[right];
}
if (compare2(best, item) >= 0) break;
data[pos] = best;
pos = left;
}
data[pos] = item;
}
};
function defaultCompare$2(a, b) {
return a < b ? -1 : a > b ? 1 : 0;
}
function checkWhichEventIsLeft(e1, e2) {
if (e1.p.x > e2.p.x) return 1;
if (e1.p.x < e2.p.x) return -1;
if (e1.p.y !== e2.p.y) return e1.p.y > e2.p.y ? 1 : -1;
return 1;
}
function checkWhichSegmentHasRightEndpointFirst(seg1, seg2) {
if (seg1.rightSweepEvent.p.x > seg2.rightSweepEvent.p.x) return 1;
if (seg1.rightSweepEvent.p.x < seg2.rightSweepEvent.p.x) return -1;
if (seg1.rightSweepEvent.p.y !== seg2.rightSweepEvent.p.y) return seg1.rightSweepEvent.p.y < seg2.rightSweepEvent.p.y ? 1 : -1;
return 1;
}
let Event$1 = class Event2 {
constructor(p, featureId2, ringId2, eventId2) {
this.p = {
x: p[0],
y: p[1]
};
this.featureId = featureId2;
this.ringId = ringId2;
this.eventId = eventId2;
this.otherEvent = null;
this.isLeftEndpoint = null;
}
isSamePoint(eventToCheck) {
return this.p.x === eventToCheck.p.x && this.p.y === eventToCheck.p.y;
}
};
function fillEventQueue(geojson, eventQueue) {
if (geojson.type === "FeatureCollection") {
const features = geojson.features;
for (let i = 0; i < features.length; i++) {
processFeature(features[i], eventQueue);
}
} else {
processFeature(geojson, eventQueue);
}
}
let featureId = 0;
let ringId = 0;
let eventId = 0;
function processFeature(featureOrGeometry, eventQueue) {
const geom = featureOrGeometry.type === "Feature" ? featureOrGeometry.geometry : featureOrGeometry;
let coords = geom.coordinates;
if (geom.type === "Polygon" || geom.type === "MultiLineString") coords = [coords];
if (geom.type === "LineString") coords = [[coords]];
for (let i = 0; i < coords.length; i++) {
for (let ii = 0; ii < coords[i].length; ii++) {
let currentP = coords[i][ii][0];
let nextP = null;
ringId = ringId + 1;
for (let iii = 0; iii < coords[i][ii].length - 1; iii++) {
nextP = coords[i][ii][iii + 1];
const e1 = new Event$1(currentP, featureId, ringId, eventId);
const e2 = new Event$1(nextP, featureId, ringId, eventId + 1);
e1.otherEvent = e2;
e2.otherEvent = e1;
if (checkWhichEventIsLeft(e1, e2) > 0) {
e2.isLeftEndpoint = true;
e1.isLeftEndpoint = false;
} else {
e1.isLeftEndpoint = true;
e2.isLeftEndpoint = false;
}
eventQueue.push(e1);
eventQueue.push(e2);
currentP = nextP;
eventId = eventId + 1;
}
}
}
featureId = featureId + 1;
}
let Segment$1 = class Segment {
constructor(event) {
this.leftSweepEvent = event;
this.rightSweepEvent = event.otherEvent;
}
};
function testSegmentIntersect(seg1, seg2) {
if (seg1 === null || seg2 === null) return false;
if (seg1.leftSweepEvent.ringId === seg2.leftSweepEvent.ringId && (seg1.rightSweepEvent.isSamePoint(seg2.leftSweepEvent) || seg1.rightSweepEvent.isSamePoint(seg2.leftSweepEvent) || seg1.rightSweepEvent.isSamePoint(seg2.rightSweepEvent) || seg1.leftSweepEvent.isSamePoint(seg2.leftSweepEvent) || seg1.leftSweepEvent.isSamePoint(seg2.rightSweepEvent))) return false;
const x1 = seg1.leftSweepEvent.p.x;
const y1 = seg1.leftSweepEvent.p.y;
const x2 = seg1.rightSweepEvent.p.x;
const y2 = seg1.rightSweepEvent.p.y;
const x3 = seg2.leftSweepEvent.p.x;
const y3 = seg2.leftSweepEvent.p.y;
const x4 = seg2.rightSweepEvent.p.x;
const y4 = seg2.rightSweepEvent.p.y;
const denom = (y4 - y3) * (x2 - x1) - (x4 - x3) * (y2 - y1);
const numeA = (x4 - x3) * (y1 - y3) - (y4 - y3) * (x1 - x3);
const numeB = (x2 - x1) * (y1 - y3) - (y2 - y1) * (x1 - x3);
if (denom === 0) {
if (numeA === 0 && numeB === 0) return false;
return false;
}
const uA = numeA / denom;
const uB = numeB / denom;
if (uA >= 0 && uA <= 1 && uB >= 0 && uB <= 1) {
const x = x1 + uA * (x2 - x1);
const y = y1 + uA * (y2 - y1);
return [x, y];
}
return false;
}
function runCheck(eventQueue, ignoreSelfIntersections) {
ignoreSelfIntersections = ignoreSelfIntersections ? ignoreSelfIntersections : false;
const intersectionPoints = [];
const outQueue = new TinyQueue$1([], checkWhichSegmentHasRightEndpointFirst);
while (eventQueue.length) {
const event = eventQueue.pop();
if (event.isLeftEndpoint) {
const segment = new Segment$1(event);
for (let i = 0; i < outQueue.data.length; i++) {
const otherSeg = outQueue.data[i];
if (ignoreSelfIntersections) {
if (otherSeg.leftSweepEvent.featureId === event.featureId) continue;
}
const intersection3 = testSegmentIntersect(segment, otherSeg);
if (intersection3 !== false) intersectionPoints.push(intersection3);
}
outQueue.push(segment);
} else if (event.isLeftEndpoint === false) {
outQueue.pop();
}
}
return intersectionPoints;
}
function sweeplineIntersections$1(geojson, ignoreSelfIntersections) {
const eventQueue = new TinyQueue$1([], checkWhichEventIsLeft);
fillEventQueue(geojson, eventQueue);
return runCheck(eventQueue, ignoreSelfIntersections);
}
var sweeplineIntersections = sweeplineIntersections$1;
function lineIntersect(line1, line2, options = {}) {
const { removeDuplicates = true, ignoreSelfIntersections = true } = options;
let features = [];
if (line1.type === "FeatureCollection")
features = features.concat(line1.features);
else if (line1.type === "Feature") features.push(line1);
else if (line1.type === "LineString" || line1.type === "Polygon" || line1.type === "MultiLineString" || line1.type === "MultiPolygon") {
features.push(feature$1(line1));
}
if (line2.type === "FeatureCollection")
features = features.concat(line2.features);
else if (line2.type === "Feature") features.push(line2);
else if (line2.type === "LineString" || line2.type === "Polygon" || line2.type === "MultiLineString" || line2.type === "MultiPolygon") {
features.push(feature$1(line2));
}
const intersections = sweeplineIntersections(
featureCollection(features),
ignoreSelfIntersections
);
let results = [];
if (removeDuplicates) {
const unique = {};
intersections.forEach((intersection3) => {
const key = intersection3.join(",");
if (!unique[key]) {
unique[key] = true;
results.push(intersection3);
}
});
} else {
results = intersections;
}
return featureCollection(results.map((r) => point(r)));
}
var index_default$d = lineIntersect;
var isNumeric = /^-?(?:\d+(?:\.\d*)?|\.\d+)(?:e[+-]?\d+)?$/i, mathceil = Math.ceil, mathfloor = Math.floor, bignumberError = "[BigNumber Error] ", tooManyDigits = bignumberError + "Number primitive has more than 15 significant digits: ", BASE = 1e14, LOG_BASE = 14, MAX_SAFE_INTEGER = 9007199254740991, POWS_TEN = [1, 10, 100, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9, 1e10, 1e11, 1e12, 1e13], SQRT_BASE = 1e7, MAX = 1e9;
function clone$1(configObject) {
var div, convertBase, parseNumeric, P = BigNumber2.prototype = { constructor: BigNumber2, toString: null, valueOf: null }, ONE = new BigNumber2(1), DECIMAL_PLACES = 20, ROUNDING_MODE = 4, TO_EXP_NEG = -7, TO_EXP_POS = 21, MIN_EXP = -1e7, MAX_EXP = 1e7, CRYPTO = false, MODULO_MODE = 1, POW_PRECISION = 0, FORMAT = {
prefix: "",
groupSize: 3,
secondaryGroupSize: 0,
groupSeparator: ",",
decimalSeparator: ".",
fractionGroupSize: 0,
fractionGroupSeparator: " ",
// non-breaking space
suffix: ""
}, ALPHABET = "0123456789abcdefghijklmnopqrstuvwxyz", alphabetHasNormalDecimalDigits = true;
function BigNumber2(v, b) {
var alphabet, c, caseChanged, e, i, isNum, len, str, x = this;
if (!(x instanceof BigNumber2)) return new BigNumber2(v, b);
if (b == null) {
if (v && v._isBigNumber === true) {
x.s = v.s;
if (!v.c || v.e > MAX_EXP) {
x.c = x.e = null;
} else if (v.e < MIN_EXP) {
x.c = [x.e = 0];
} else {
x.e = v.e;
x.c = v.c.slice();
}
return;
}
if ((isNum = typeof v == "number") && v * 0 == 0) {
x.s = 1 / v < 0 ? (v = -v, -1) : 1;
if (v === ~~v) {
for (e = 0, i = v; i >= 10; i /= 10, e++) ;
if (e > MAX_EXP) {
x.c = x.e = null;
} else {
x.e = e;
x.c = [v];
}
return;
}
str = String(v);
} else {
if (!isNumeric.test(str = String(v))) return parseNumeric(x, str, isNum);
x.s = str.charCodeAt(0) == 45 ? (str = str.slice(1), -1) : 1;
}
if ((e = str.indexOf(".")) > -1) str = str.replace(".", "");
if ((i = str.search(/e/i)) > 0) {
if (e < 0) e = i;
e += +str.slice(i + 1);
str = str.substring(0, i);
} else if (e < 0) {
e = str.length;
}
} else {
intCheck(b, 2, ALPHABET.length, "Base");
if (b == 10 && alphabetHasNormalDecimalDigits) {
x = new BigNumber2(v);
return round(x, DECIMAL_PLACES + x.e + 1, ROUNDING_MODE);
}
str = String(v);
if (isNum = typeof v == "number") {
if (v * 0 != 0) return parseNumeric(x, str, isNum, b);
x.s = 1 / v < 0 ? (str = str.slice(1), -1) : 1;
if (BigNumber2.DEBUG && str.replace(/^0\.0*|\./, "").length > 15) {
throw Error(tooManyDigits + v);
}
} else {
x.s = str.charCodeAt(0) === 45 ? (str = str.slice(1), -1) : 1;
}
alphabet = ALPHABET.slice(0, b);
e = i = 0;
for (len = str.length; i < len; i++) {
if (alphabet.indexOf(c = str.charAt(i)) < 0) {
if (c == ".") {
if (i > e) {
e = len;
continue;
}
} else if (!caseChanged) {
if (str == str.toUpperCase() && (str = str.toLowerCase()) || str == str.toLowerCase() && (str = str.toUpperCase())) {
caseChanged = true;
i = -1;
e = 0;
continue;
}
}
return parseNumeric(x, String(v), isNum, b);
}
}
isNum = false;
str = convertBase(str, b, 10, x.s);
if ((e = str.indexOf(".")) > -1) str = str.replace(".", "");
else e = str.length;
}
for (i = 0; str.charCodeAt(i) === 48; i++) ;
for (len = str.length; str.charCodeAt(--len) === 48; ) ;
if (str = str.slice(i, ++len)) {
len -= i;
if (isNum && BigNumber2.DEBUG && len > 15 && (v > MAX_SAFE_INTEGER || v !== mathfloor(v))) {
throw Error(tooManyDigits + x.s * v);
}
if ((e = e - i - 1) > MAX_EXP) {
x.c = x.e = null;
} else if (e < MIN_EXP) {
x.c = [x.e = 0];
} else {
x.e = e;
x.c = [];
i = (e + 1) % LOG_BASE;
if (e < 0) i += LOG_BASE;
if (i < len) {
if (i) x.c.push(+str.slice(0, i));
for (len -= LOG_BASE; i < len; ) {
x.c.push(+str.slice(i, i += LOG_BASE));
}
i = LOG_BASE - (str = str.slice(i)).length;
} else {
i -= len;
}
for (; i--; str += "0") ;
x.c.push(+str);
}
} else {
x.c = [x.e = 0];
}
}
BigNumber2.clone = clone$1;
BigNumber2.ROUND_UP = 0;
BigNumber2.ROUND_DOWN = 1;
BigNumber2.ROUND_CEIL = 2;
BigNumber2.ROUND_FLOOR = 3;
BigNumber2.ROUND_HALF_UP = 4;
BigNumber2.ROUND_HALF_DOWN = 5;
BigNumber2.ROUND_HALF_EVEN = 6;
BigNumber2.ROUND_HALF_CEIL = 7;
BigNumber2.ROUND_HALF_FLOOR = 8;
BigNumber2.EUCLID = 9;
BigNumber2.config = BigNumber2.set = function(obj) {
var p, v;
if (obj != null) {
if (typeof obj == "object") {
if (obj.hasOwnProperty(p = "DECIMAL_PLACES")) {
v = obj[p];
intCheck(v, 0, MAX, p);
DECIMAL_PLACES = v;
}
if (obj.hasOwnProperty(p = "ROUNDING_MODE")) {
v = obj[p];
intCheck(v, 0, 8, p);
ROUNDING_MODE = v;
}
if (obj.hasOwnProperty(p = "EXPONENTIAL_AT")) {
v = obj[p];
if (v && v.pop) {
intCheck(v[0], -MAX, 0, p);
intCheck(v[1], 0, MAX, p);
TO_EXP_NEG = v[0];
TO_EXP_POS = v[1];
} else {
intCheck(v, -MAX, MAX, p);
TO_EXP_NEG = -(TO_EXP_POS = v < 0 ? -v : v);
}
}
if (obj.hasOwnProperty(p = "RANGE")) {
v = obj[p];
if (v && v.pop) {
intCheck(v[0], -MAX, -1, p);
intCheck(v[1], 1, MAX, p);
MIN_EXP = v[0];
MAX_EXP = v[1];
} else {
intCheck(v, -MAX, MAX, p);
if (v) {
MIN_EXP = -(MAX_EXP = v < 0 ? -v : v);
} else {
throw Error(bignumberError + p + " cannot be zero: " + v);
}
}
}
if (obj.hasOwnProperty(p = "CRYPTO")) {
v = obj[p];
if (v === !!v) {
if (v) {
if (typeof crypto != "undefined" && crypto && (crypto.getRandomValues || crypto.randomBytes)) {
CRYPTO = v;
} else {
CRYPTO = !v;
throw Error(bignumberError + "crypto unavailable");
}
} else {
CRYPTO = v;
}
} else {
throw Error(bignumberError + p + " not true or false: " + v);
}
}
if (obj.hasOwnProperty(p = "MODULO_MODE")) {
v = obj[p];
intCheck(v, 0, 9, p);
MODULO_MODE = v;
}
if (obj.hasOwnProperty(p = "POW_PRECISION")) {
v = obj[p];
intCheck(v, 0, MAX, p);
POW_PRECISION = v;
}
if (obj.hasOwnProperty(p = "FORMAT")) {
v = obj[p];
if (typeof v == "object") FORMAT = v;
else throw Error(bignumberError + p + " not an object: " + v);
}
if (obj.hasOwnProperty(p = "ALPHABET")) {
v = obj[p];
if (typeof v == "string" && !/^.?$|[+\-.\s]|(.).*\1/.test(v)) {
alphabetHasNormalDecimalDigits = v.slice(0, 10) == "0123456789";
ALPHABET = v;
} else {
throw Error(bignumberError + p + " invalid: " + v);
}
}
} else {
throw Error(bignumberError + "Object expected: " + obj);
}
}
return {
DECIMAL_PLACES,
ROUNDING_MODE,
EXPONENTIAL_AT: [TO_EXP_NEG, TO_EXP_POS],
RANGE: [MIN_EXP, MAX_EXP],
CRYPTO,
MODULO_MODE,
POW_PRECISION,
FORMAT,
ALPHABET
};
};
BigNumber2.isBigNumber = function(v) {
if (!v || v._isBigNumber !== true) return false;
if (!BigNumber2.DEBUG) return true;
var i, n, c = v.c, e = v.e, s = v.s;
out: if ({}.toString.call(c) == "[object Array]") {
if ((s === 1 || s === -1) && e >= -MAX && e <= MAX && e === mathfloor(e)) {
if (c[0] === 0) {
if (e === 0 && c.length === 1) return true;
break out;
}
i = (e + 1) % LOG_BASE;
if (i < 1) i += LOG_BASE;
if (String(c[0]).length == i) {
for (i = 0; i < c.length; i++) {
n = c[i];
if (n < 0 || n >= BASE || n !== mathfloor(n)) break out;
}
if (n !== 0) return true;
}
}
} else if (c === null && e === null && (s === null || s === 1 || s === -1)) {
return true;
}
throw Error(bignumberError + "Invalid BigNumber: " + v);
};
BigNumber2.maximum = BigNumber2.max = function() {
return maxOrMin(arguments, -1);
};
BigNumber2.minimum = BigNumber2.min = function() {
return maxOrMin(arguments, 1);
};
BigNumber2.random = (function() {
var pow2_53 = 9007199254740992;
var random53bitInt = Math.random() * pow2_53 & 2097151 ? function() {
return mathfloor(Math.random() * pow2_53);
} : function() {
return (Math.random() * 1073741824 | 0) * 8388608 + (Math.random() * 8388608 | 0);
};
return function(dp) {
var a, b, e, k, v, i = 0, c = [], rand = new BigNumber2(ONE);
if (dp == null) dp = DECIMAL_PLACES;
else intCheck(dp, 0, MAX);
k = mathceil(dp / LOG_BASE);
if (CRYPTO) {
if (crypto.getRandomValues) {
a = crypto.getRandomValues(new Uint32Array(k *= 2));
for (; i < k; ) {
v = a[i] * 131072 + (a[i + 1] >>> 11);
if (v >= 9e15) {
b = crypto.getRandomValues(new Uint32Array(2));
a[i] = b[0];
a[i + 1] = b[1];
} else {
c.push(v % 1e14);
i += 2;
}
}
i = k / 2;
} else if (crypto.randomBytes) {
a = crypto.randomBytes(k *= 7);
for (; i < k; ) {
v = (a[i] & 31) * 281474976710656 + a[i + 1] * 1099511627776 + a[i + 2] * 4294967296 + a[i + 3] * 16777216 + (a[i + 4] << 16) + (a[i + 5] << 8) + a[i + 6];
if (v >= 9e15) {
crypto.randomBytes(7).copy(a, i);
} else {
c.push(v % 1e14);
i += 7;
}
}
i = k / 7;
} else {
CRYPTO = false;
throw Error(bignumberError + "crypto unavailable");
}
}
if (!CRYPTO) {
for (; i < k; ) {
v = random53bitInt();
if (v < 9e15) c[i++] = v % 1e14;
}
}
k = c[--i];
dp %= LOG_BASE;
if (k && dp) {
v = POWS_TEN[LOG_BASE - dp];
c[i] = mathfloor(k / v) * v;
}
for (; c[i] === 0; c.pop(), i--) ;
if (i < 0) {
c = [e = 0];
} else {
for (e = -1; c[0] === 0; c.splice(0, 1), e -= LOG_BASE) ;
for (i = 1, v = c[0]; v >= 10; v /= 10, i++) ;
if (i < LOG_BASE) e -= LOG_BASE - i;
}
rand.e = e;
rand.c = c;
return rand;
};
})();
BigNumber2.sum = function() {
var i = 1, args = arguments, sum2 = new BigNumber2(args[0]);
for (; i < args.length; ) sum2 = sum2.plus(args[i++]);
return sum2;
};
convertBase = /* @__PURE__ */ (function() {
var decimal = "0123456789";
function toBaseOut(str, baseIn, baseOut, alphabet) {
var j, arr = [0], arrL, i = 0, len = str.length;
for (; i < len; ) {
for (arrL = arr.length; arrL--; arr[arrL] *= baseIn) ;
arr[0] += alphabet.indexOf(str.charAt(i++));
for (j = 0; j < arr.length; j++) {
if (arr[j] > baseOut - 1) {
if (arr[j + 1] == null) arr[j + 1] = 0;
arr[j + 1] += arr[j] / baseOut | 0;
arr[j] %= baseOut;
}
}
}
return arr.reverse();
}
return function(str, baseIn, baseOut, sign, callerIsToString) {
var alphabet, d, e, k, r, x, xc, y, i = str.indexOf("."), dp = DECIMAL_PLACES, rm = ROUNDING_MODE;
if (i >= 0) {
k = POW_PRECISION;
POW_PRECISION = 0;
str = str.replace(".", "");
y = new BigNumber2(baseIn);
x = y.pow(str.length - i);
POW_PRECISION = k;
y.c = toBaseOut(
toFixedPoint(coeffToString(x.c), x.e, "0"),
10,
baseOut,
decimal
);
y.e = y.c.length;
}
xc = toBaseOut(str, baseIn, baseOut, callerIsToString ? (alphabet = ALPHABET, decimal) : (alphabet = decimal, ALPHABET));
e = k = xc.length;
for (; xc[--k] == 0; xc.pop()) ;
if (!xc[0]) return alphabet.charAt(0);
if (i < 0) {
--e;
} else {
x.c = xc;
x.e = e;
x.s = sign;
x = div(x, y, dp, rm, baseOut);
xc = x.c;
r = x.r;
e = x.e;
}
d = e + dp + 1;
i = xc[d];
k = baseOut / 2;
r = r || d < 0 || xc[d + 1] != null;
r = rm < 4 ? (i != null || r) && (rm == 0 || rm == (x.s < 0 ? 3 : 2)) : i > k || i == k && (rm == 4 || r || rm == 6 && xc[d - 1] & 1 || rm == (x.s < 0 ? 8 : 7));
if (d < 1 || !xc[0]) {
str = r ? toFixedPoint(alphabet.charAt(1), -dp, alphabet.charAt(0)) : alphabet.charAt(0);
} else {
xc.length = d;
if (r) {
for (--baseOut; ++xc[--d] > baseOut; ) {
xc[d] = 0;
if (!d) {
++e;
xc = [1].concat(xc);
}
}
}
for (k = xc.length; !xc[--k]; ) ;
for (i = 0, str = ""; i <= k; str += alphabet.charAt(xc[i++])) ;
str = toFixedPoint(str, e, alphabet.charAt(0));
}
return str;
};
})();
div = /* @__PURE__ */ (function() {
function multiply(x, k, base) {
var m, temp, xlo, xhi, carry = 0, i = x.length, klo = k % SQRT_BASE, khi = k / SQRT_BASE | 0;
for (x = x.slice(); i--; ) {
xlo = x[i] % SQRT_BASE;
xhi = x[i] / SQRT_BASE | 0;
m = khi * xlo + xhi * klo;
temp = klo * xlo + m % SQRT_BASE * SQRT_BASE + carry;
carry = (temp / base | 0) + (m / SQRT_BASE | 0) + khi * xhi;
x[i] = temp % base;
}
if (carry) x = [carry].concat(x);
return x;
}
function compare2(a, b, aL, bL) {
var i, cmp;
if (aL != bL) {
cmp = aL > bL ? 1 : -1;
} else {
for (i = cmp = 0; i < aL; i++) {
if (a[i] != b[i]) {
cmp = a[i] > b[i] ? 1 : -1;
break;
}
}
}
return cmp;
}
function subtract(a, b, aL, base) {
var i = 0;
for (; aL--; ) {
a[aL] -= i;
i = a[aL] < b[aL] ? 1 : 0;
a[aL] = i * base + a[aL] - b[aL];
}
for (; !a[0] && a.length > 1; a.splice(0, 1)) ;
}
return function(x, y, dp, rm, base) {
var cmp, e, i, more, n, prod, prodL, q, qc, rem, remL, rem0, xi, xL, yc0, yL, yz, s = x.s == y.s ? 1 : -1, xc = x.c, yc = y.c;
if (!xc || !xc[0] || !yc || !yc[0]) {
return new BigNumber2(
// Return NaN if either NaN, or both Infinity or 0.
!x.s || !y.s || (xc ? yc && xc[0] == yc[0] : !yc) ? NaN : (
// Return ±0 if x is ±0 or y is ±Infinity, or return ±Infinity as y is ±0.
xc && xc[0] == 0 || !yc ? s * 0 : s / 0
)
);
}
q = new BigNumber2(s);
qc = q.c = [];
e = x.e - y.e;
s = dp + e + 1;
if (!base) {
base = BASE;
e = bitFloor(x.e / LOG_BASE) - bitFloor(y.e / LOG_BASE);
s = s / LOG_BASE | 0;
}
for (i = 0; yc[i] == (xc[i] || 0); i++) ;
if (yc[i] > (xc[i] || 0)) e--;
if (s < 0) {
qc.push(1);
more = true;
} else {
xL = xc.length;
yL = yc.length;
i = 0;
s += 2;
n = mathfloor(base / (yc[0] + 1));
if (n > 1) {
yc = multiply(yc, n, base);
xc = multiply(xc, n, base);
yL = yc.length;
xL = xc.length;
}
xi = yL;
rem = xc.slice(0, yL);
remL = rem.length;
for (; remL < yL; rem[remL++] = 0) ;
yz = yc.slice();
yz = [0].concat(yz);
yc0 = yc[0];
if (yc[1] >= base / 2) yc0++;
do {
n = 0;
cmp = compare2(yc, rem, yL, remL);
if (cmp < 0) {
rem0 = rem[0];
if (yL != remL) rem0 = rem0 * base + (rem[1] || 0);
n = mathfloor(rem0 / yc0);
if (n > 1) {
if (n >= base) n = base - 1;
prod = multiply(yc, n, base);
prodL = prod.length;
remL = rem.length;
while (compare2(prod, rem, prodL, remL) == 1) {
n--;
subtract(prod, yL < prodL ? yz : yc, prodL, base);
prodL = prod.length;
cmp = 1;
}
} else {
if (n == 0) {
cmp = n = 1;
}
prod = yc.slice();
prodL = prod.length;
}
if (prodL < remL) prod = [0].concat(prod);
subtract(rem, prod, remL, base);
remL = rem.length;
if (cmp == -1) {
while (compare2(yc, rem, yL, remL) < 1) {
n++;
subtract(rem, yL < remL ? yz : yc, remL, base);
remL = rem.length;
}
}
} else if (cmp === 0) {
n++;
rem = [0];
}
qc[i++] = n;
if (rem[0]) {
rem[remL++] = xc[xi] || 0;
} else {
rem = [xc[xi]];
remL = 1;
}
} while ((xi++ < xL || rem[0] != null) && s--);
more = rem[0] != null;
if (!qc[0]) qc.splice(0, 1);
}
if (base == BASE) {
for (i = 1, s = qc[0]; s >= 10; s /= 10, i++) ;
round(q, dp + (q.e = i + e * LOG_BASE - 1) + 1, rm, more);
} else {
q.e = e;
q.r = +more;
}
return q;
};
})();
function format(n, i, rm, id) {
var c0, e, ne, len, str;
if (rm == null) rm = ROUNDING_MODE;
else intCheck(rm, 0, 8);
if (!n.c) return n.toString();
c0 = n.c[0];
ne = n.e;
if (i == null) {
str = coeffToString(n.c);
str = id == 1 || id == 2 && (ne <= TO_EXP_NEG || ne >= TO_EXP_POS) ? toExponential(str, ne) : toFixedPoint(str, ne, "0");
} else {
n = round(new BigNumber2(n), i, rm);
e = n.e;
str = coeffToString(n.c);
len = str.length;
if (id == 1 || id == 2 && (i <= e || e <= TO_EXP_NEG)) {
for (; len < i; str += "0", len++) ;
str = toExponential(str, e);
} else {
i -= ne + (id === 2 && e > ne);
str = toFixedPoint(str, e, "0");
if (e + 1 > len) {
if (--i > 0) for (str += "."; i--; str += "0") ;
} else {
i += e - len;
if (i > 0) {
if (e + 1 == len) str += ".";
for (; i--; str += "0") ;
}
}
}
}
return n.s < 0 && c0 ? "-" + str : str;
}
function maxOrMin(args, n) {
var k, y, i = 1, x = new BigNumber2(args[0]);
for (; i < args.length; i++) {
y = new BigNumber2(args[i]);
if (!y.s || (k = compare(x, y)) === n || k === 0 && x.s === n) {
x = y;
}
}
return x;
}
function normalise(n, c, e) {
var i = 1, j = c.length;
for (; !c[--j]; c.pop()) ;
for (j = c[0]; j >= 10; j /= 10, i++) ;
if ((e = i + e * LOG_BASE - 1) > MAX_EXP) {
n.c = n.e = null;
} else if (e < MIN_EXP) {
n.c = [n.e = 0];
} else {
n.e = e;
n.c = c;
}
return n;
}
parseNumeric = /* @__PURE__ */ (function() {
var basePrefix = /^(-?)0([xbo])(?=\w[\w.]*$)/i, dotAfter = /^([^.]+)\.$/, dotBefore = /^\.([^.]+)$/, isInfinityOrNaN = /^-?(Infinity|NaN)$/, whitespaceOrPlus = /^\s*\+(?=[\w.])|^\s+|\s+$/g;
return function(x, str, isNum, b) {
var base, s = isNum ? str : str.replace(whitespaceOrPlus, "");
if (isInfinityOrNaN.test(s)) {
x.s = isNaN(s) ? null : s < 0 ? -1 : 1;
} else {
if (!isNum) {
s = s.replace(basePrefix, function(m, p1, p2) {
base = (p2 = p2.toLowerCase()) == "x" ? 16 : p2 == "b" ? 2 : 8;
return !b || b == base ? p1 : m;
});
if (b) {
base = b;
s = s.replace(dotAfter, "$1").replace(dotBefore, "0.$1");
}
if (str != s) return new BigNumber2(s, base);
}
if (BigNumber2.DEBUG) {
throw Error(bignumberError + "Not a" + (b ? " base " + b : "") + " number: " + str);
}
x.s = null;
}
x.c = x.e = null;
};
})();
function round(x, sd, rm, r) {
var d, i, j, k, n, ni, rd, xc = x.c, pows10 = POWS_TEN;
if (xc) {
out: {
for (d = 1, k = xc[0]; k >= 10; k /= 10, d++) ;
i = sd - d;
if (i < 0) {
i += LOG_BASE;
j = sd;
n = xc[ni = 0];
rd = mathfloor(n / pows10[d - j - 1] % 10);
} else {
ni = mathceil((i + 1) / LOG_BASE);
if (ni >= xc.length) {
if (r) {
for (; xc.length <= ni; xc.push(0)) ;
n = rd = 0;
d = 1;
i %= LOG_BASE;
j = i - LOG_BASE + 1;
} else {
break out;
}
} else {
n = k = xc[ni];
for (d = 1; k >= 10; k /= 10, d++) ;
i %= LOG_BASE;
j = i - LOG_BASE + d;
rd = j < 0 ? 0 : mathfloor(n / pows10[d - j - 1] % 10);
}
}
r = r || sd < 0 || // Are there any non-zero digits after the rounding digit?
// The expression n % pows10[d - j - 1] returns all digits of n to the right
// of the digit at j, e.g. if n is 908714 and j is 2, the expression gives 714.
xc[ni + 1] != null || (j < 0 ? n : n % pows10[d - j - 1]);
r = rm < 4 ? (rd || r) && (rm == 0 || rm == (x.s < 0 ? 3 : 2)) : rd > 5 || rd == 5 && (rm == 4 || r || rm == 6 && // Check whether the digit to the left of the rounding digit is odd.
(i > 0 ? j > 0 ? n / pows10[d - j] : 0 : xc[ni - 1]) % 10 & 1 || rm == (x.s < 0 ? 8 : 7));
if (sd < 1 || !xc[0]) {
xc.length = 0;
if (r) {
sd -= x.e + 1;
xc[0] = pows10[(LOG_BASE - sd % LOG_BASE) % LOG_BASE];
x.e = -sd || 0;
} else {
xc[0] = x.e = 0;
}
return x;
}
if (i == 0) {
xc.length = ni;
k = 1;
ni--;
} else {
xc.length = ni + 1;
k = pows10[LOG_BASE - i];
xc[ni] = j > 0 ? mathfloor(n / pows10[d - j] % pows10[j]) * k : 0;
}
if (r) {
for (; ; ) {
if (ni == 0) {
for (i = 1, j = xc[0]; j >= 10; j /= 10, i++) ;
j = xc[0] += k;
for (k = 1; j >= 10; j /= 10, k++) ;
if (i != k) {
x.e++;
if (xc[0] == BASE) xc[0] = 1;
}
break;
} else {
xc[ni] += k;
if (xc[ni] != BASE) break;
xc[ni--] = 0;
k = 1;
}
}
}
for (i = xc.length; xc[--i] === 0; xc.pop()) ;
}
if (x.e > MAX_EXP) {
x.c = x.e = null;
} else if (x.e < MIN_EXP) {
x.c = [x.e = 0];
}
}
return x;
}
function valueOf(n) {
var str, e = n.e;
if (e === null) return n.toString();
str = coeffToString(n.c);
str = e <= TO_EXP_NEG || e >= TO_EXP_POS ? toExponential(str, e) : toFixedPoint(str, e, "0");
return n.s < 0 ? "-" + str : str;
}
P.absoluteValue = P.abs = function() {
var x = new BigNumber2(this);
if (x.s < 0) x.s = 1;
return x;
};
P.comparedTo = function(y, b) {
return compare(this, new BigNumber2(y, b));
};
P.decimalPlaces = P.dp = function(dp, rm) {
var c, n, v, x = this;
if (dp != null) {
intCheck(dp, 0, MAX);
if (rm == null) rm = ROUNDING_MODE;
else intCheck(rm, 0, 8);
return round(new BigNumber2(x), dp + x.e + 1, rm);
}
if (!(c = x.c)) return null;
n = ((v = c.length - 1) - bitFloor(this.e / LOG_BASE)) * LOG_BASE;
if (v = c[v]) for (; v % 10 == 0; v /= 10, n--) ;
if (n < 0) n = 0;
return n;
};
P.dividedBy = P.div = function(y, b) {
return div(this, new BigNumber2(y, b), DECIMAL_PLACES, ROUNDING_MODE);
};
P.dividedToIntegerBy = P.idiv = function(y, b) {
return div(this, new BigNumber2(y, b), 0, 1);
};
P.exponentiatedBy = P.pow = function(n, m) {
var half, isModExp, i, k, more, nIsBig, nIsNeg, nIsOdd, y, x = this;
n = new BigNumber2(n);
if (n.c && !n.isInteger()) {
throw Error(bignumberError + "Exponent not an integer: " + valueOf(n));
}
if (m != null) m = new BigNumber2(m);
nIsBig = n.e > 14;
if (!x.c || !x.c[0] || x.c[0] == 1 && !x.e && x.c.length == 1 || !n.c || !n.c[0]) {
y = new BigNumber2(Math.pow(+valueOf(x), nIsBig ? n.s * (2 - isOdd(n)) : +valueOf(n)));
return m ? y.mod(m) : y;
}
nIsNeg = n.s < 0;
if (m) {
if (m.c ? !m.c[0] : !m.s) return new BigNumber2(NaN);
isModExp = !nIsNeg && x.isInteger() && m.isInteger();
if (isModExp) x = x.mod(m);
} else if (n.e > 9 && (x.e > 0 || x.e < -1 || (x.e == 0 ? x.c[0] > 1 || nIsBig && x.c[1] >= 24e7 : x.c[0] < 8e13 || nIsBig && x.c[0] <= 9999975e7))) {
k = x.s < 0 && isOdd(n) ? -0 : 0;
if (x.e > -1) k = 1 / k;
return new BigNumber2(nIsNeg ? 1 / k : k);
} else if (POW_PRECISION) {
k = mathceil(POW_PRECISION / LOG_BASE + 2);
}
if (nIsBig) {
half = new BigNumber2(0.5);
if (nIsNeg) n.s = 1;
nIsOdd = isOdd(n);
} else {
i = Math.abs(+valueOf(n));
nIsOdd = i % 2;
}
y = new BigNumber2(ONE);
for (; ; ) {
if (nIsOdd) {
y = y.times(x);
if (!y.c) break;
if (k) {
if (y.c.length > k) y.c.length = k;
} else if (isModExp) {
y = y.mod(m);
}
}
if (i) {
i = mathfloor(i / 2);
if (i === 0) break;
nIsOdd = i % 2;
} else {
n = n.times(half);
round(n, n.e + 1, 1);
if (n.e > 14) {
nIsOdd = isOdd(n);
} else {
i = +valueOf(n);
if (i === 0) break;
nIsOdd = i % 2;
}
}
x = x.times(x);
if (k) {
if (x.c && x.c.length > k) x.c.length = k;
} else if (isModExp) {
x = x.mod(m);
}
}
if (isModExp) return y;
if (nIsNeg) y = ONE.div(y);
return m ? y.mod(m) : k ? round(y, POW_PRECISION, ROUNDING_MODE, more) : y;
};
P.integerValue = function(rm) {
var n = new BigNumber2(this);
if (rm == null) rm = ROUNDING_MODE;
else intCheck(rm, 0, 8);
return round(n, n.e + 1, rm);
};
P.isEqualTo = P.eq = function(y, b) {
return compare(this, new BigNumber2(y, b)) === 0;
};
P.isFinite = function() {
return !!this.c;
};
P.isGreaterThan = P.gt = function(y, b) {
return compare(this, new BigNumber2(y, b)) > 0;
};
P.isGreaterThanOrEqualTo = P.gte = function(y, b) {
return (b = compare(this, new BigNumber2(y, b))) === 1 || b === 0;
};
P.isInteger = function() {
return !!this.c && bitFloor(this.e / LOG_BASE) > this.c.length - 2;
};
P.isLessThan = P.lt = function(y, b) {
return compare(this, new BigNumber2(y, b)) < 0;
};
P.isLessThanOrEqualTo = P.lte = function(y, b) {
return (b = compare(this, new BigNumber2(y, b))) === -1 || b === 0;
};
P.isNaN = function() {
return !this.s;
};
P.isNegative = function() {
return this.s < 0;
};
P.isPositive = function() {
return this.s > 0;
};
P.isZero = function() {
return !!this.c && this.c[0] == 0;
};
P.minus = function(y, b) {
var i, j, t, xLTy, x = this, a = x.s;
y = new BigNumber2(y, b);
b = y.s;
if (!a || !b) return new BigNumber2(NaN);
if (a != b) {
y.s = -b;
return x.plus(y);
}
var xe = x.e / LOG_BASE, ye = y.e / LOG_BASE, xc = x.c, yc = y.c;
if (!xe || !ye) {
if (!xc || !yc) return xc ? (y.s = -b, y) : new BigNumber2(yc ? x : NaN);
if (!xc[0] || !yc[0]) {
return yc[0] ? (y.s = -b, y) : new BigNumber2(xc[0] ? x : (
// IEEE 754 (2008) 6.3: n - n = -0 when rounding to -Infinity
ROUNDING_MODE == 3 ? -0 : 0
));
}
}
xe = bitFloor(xe);
ye = bitFloor(ye);
xc = xc.slice();
if (a = xe - ye) {
if (xLTy = a < 0) {
a = -a;
t = xc;
} else {
ye = xe;
t = yc;
}
t.reverse();
for (b = a; b--; t.push(0)) ;
t.reverse();
} else {
j = (xLTy = (a = xc.length) < (b = yc.length)) ? a : b;
for (a = b = 0; b < j; b++) {
if (xc[b] != yc[b]) {
xLTy = xc[b] < yc[b];
break;
}
}
}
if (xLTy) {
t = xc;
xc = yc;
yc = t;
y.s = -y.s;
}
b = (j = yc.length) - (i = xc.length);
if (b > 0) for (; b--; xc[i++] = 0) ;
b = BASE - 1;
for (; j > a; ) {
if (xc[--j] < yc[j]) {
for (i = j; i && !xc[--i]; xc[i] = b) ;
--xc[i];
xc[j] += BASE;
}
xc[j] -= yc[j];
}
for (; xc[0] == 0; xc.splice(0, 1), --ye) ;
if (!xc[0]) {
y.s = ROUNDING_MODE == 3 ? -1 : 1;
y.c = [y.e = 0];
return y;
}
return normalise(y, xc, ye);
};
P.modulo = P.mod = function(y, b) {
var q, s, x = this;
y = new BigNumber2(y, b);
if (!x.c || !y.s || y.c && !y.c[0]) {
return new BigNumber2(NaN);
} else if (!y.c || x.c && !x.c[0]) {
return new BigNumber2(x);
}
if (MODULO_MODE == 9) {
s = y.s;
y.s = 1;
q = div(x, y, 0, 3);
y.s = s;
q.s *= s;
} else {
q = div(x, y, 0, MODULO_MODE);
}
y = x.minus(q.times(y));
if (!y.c[0] && MODULO_MODE == 1) y.s = x.s;
return y;
};
P.multipliedBy = P.times = function(y, b) {
var c, e, i, j, k, m, xcL, xlo, xhi, ycL, ylo, yhi, zc, base, sqrtBase, x = this, xc = x.c, yc = (y = new BigNumber2(y, b)).c;
if (!xc || !yc || !xc[0] || !yc[0]) {
if (!x.s || !y.s || xc && !xc[0] && !yc || yc && !yc[0] && !xc) {
y.c = y.e = y.s = null;
} else {
y.s *= x.s;
if (!xc || !yc) {
y.c = y.e = null;
} else {
y.c = [0];
y.e = 0;
}
}
return y;
}
e = bitFloor(x.e / LOG_BASE) + bitFloor(y.e / LOG_BASE);
y.s *= x.s;
xcL = xc.length;
ycL = yc.length;
if (xcL < ycL) {
zc = xc;
xc = yc;
yc = zc;
i = xcL;
xcL = ycL;
ycL = i;
}
for (i = xcL + ycL, zc = []; i--; zc.push(0)) ;
base = BASE;
sqrtBase = SQRT_BASE;
for (i = ycL; --i >= 0; ) {
c = 0;
ylo = yc[i] % sqrtBase;
yhi = yc[i] / sqrtBase | 0;
for (k = xcL, j = i + k; j > i; ) {
xlo = xc[--k] % sqrtBase;
xhi = xc[k] / sqrtBase | 0;
m = yhi * xlo + xhi * ylo;
xlo = ylo * xlo + m % sqrtBase * sqrtBase + zc[j] + c;
c = (xlo / base | 0) + (m / sqrtBase | 0) + yhi * xhi;
zc[j--] = xlo % base;
}
zc[j] = c;
}
if (c) {
++e;
} else {
zc.splice(0, 1);
}
return normalise(y, zc, e);
};
P.negated = function() {
var x = new BigNumber2(this);
x.s = -x.s || null;
return x;
};
P.plus = function(y, b) {
var t, x = this, a = x.s;
y = new BigNumber2(y, b);
b = y.s;
if (!a || !b) return new BigNumber2(NaN);
if (a != b) {
y.s = -b;
return x.minus(y);
}
var xe = x.e / LOG_BASE, ye = y.e / LOG_BASE, xc = x.c, yc = y.c;
if (!xe || !ye) {
if (!xc || !yc) return new BigNumber2(a / 0);
if (!xc[0] || !yc[0]) return yc[0] ? y : new BigNumber2(xc[0] ? x : a * 0);
}
xe = bitFloor(xe);
ye = bitFloor(ye);
xc = xc.slice();
if (a = xe - ye) {
if (a > 0) {
ye = xe;
t = yc;
} else {
a = -a;
t = xc;
}
t.reverse();
for (; a--; t.push(0)) ;
t.reverse();
}
a = xc.length;
b = yc.length;
if (a - b < 0) {
t = yc;
yc = xc;
xc = t;
b = a;
}
for (a = 0; b; ) {
a = (xc[--b] = xc[b] + yc[b] + a) / BASE | 0;
xc[b] = BASE === xc[b] ? 0 : xc[b] % BASE;
}
if (a) {
xc = [a].concat(xc);
++ye;
}
return normalise(y, xc, ye);
};
P.precision = P.sd = function(sd, rm) {
var c, n, v, x = this;
if (sd != null && sd !== !!sd) {
intCheck(sd, 1, MAX);
if (rm == null) rm = ROUNDING_MODE;
else intCheck(rm, 0, 8);
return round(new BigNumber2(x), sd, rm);
}
if (!(c = x.c)) return null;
v = c.length - 1;
n = v * LOG_BASE + 1;
if (v = c[v]) {
for (; v % 10 == 0; v /= 10, n--) ;
for (v = c[0]; v >= 10; v /= 10, n++) ;
}
if (sd && x.e + 1 > n) n = x.e + 1;
return n;
};
P.shiftedBy = function(k) {
intCheck(k, -MAX_SAFE_INTEGER, MAX_SAFE_INTEGER);
return this.times("1e" + k);
};
P.squareRoot = P.sqrt = function() {
var m, n, r, rep, t, x = this, c = x.c, s = x.s, e = x.e, dp = DECIMAL_PLACES + 4, half = new BigNumber2("0.5");
if (s !== 1 || !c || !c[0]) {
return new BigNumber2(!s || s < 0 && (!c || c[0]) ? NaN : c ? x : 1 / 0);
}
s = Math.sqrt(+valueOf(x));
if (s == 0 || s == 1 / 0) {
n = coeffToString(c);
if ((n.length + e) % 2 == 0) n += "0";
s = Math.sqrt(+n);
e = bitFloor((e + 1) / 2) - (e < 0 || e % 2);
if (s == 1 / 0) {
n = "5e" + e;
} else {
n = s.toExponential();
n = n.slice(0, n.indexOf("e") + 1) + e;
}
r = new BigNumber2(n);
} else {
r = new BigNumber2(s + "");
}
if (r.c[0]) {
e = r.e;
s = e + dp;
if (s < 3) s = 0;
for (; ; ) {
t = r;
r = half.times(t.plus(div(x, t, dp, 1)));
if (coeffToString(t.c).slice(0, s) === (n = coeffToString(r.c)).slice(0, s)) {
if (r.e < e) --s;
n = n.slice(s - 3, s + 1);
if (n == "9999" || !rep && n == "4999") {
if (!rep) {
round(t, t.e + DECIMAL_PLACES + 2, 0);
if (t.times(t).eq(x)) {
r = t;
break;
}
}
dp += 4;
s += 4;
rep = 1;
} else {
if (!+n || !+n.slice(1) && n.charAt(0) == "5") {
round(r, r.e + DECIMAL_PLACES + 2, 1);
m = !r.times(r).eq(x);
}
break;
}
}
}
}
return round(r, r.e + DECIMAL_PLACES + 1, ROUNDING_MODE, m);
};
P.toExponential = function(dp, rm) {
if (dp != null) {
intCheck(dp, 0, MAX);
dp++;
}
return format(this, dp, rm, 1);
};
P.toFixed = function(dp, rm) {
if (dp != null) {
intCheck(dp, 0, MAX);
dp = dp + this.e + 1;
}
return format(this, dp, rm);
};
P.toFormat = function(dp, rm, format2) {
var str, x = this;
if (format2 == null) {
if (dp != null && rm && typeof rm == "object") {
format2 = rm;
rm = null;
} else if (dp && typeof dp == "object") {
format2 = dp;
dp = rm = null;
} else {
format2 = FORMAT;
}
} else if (typeof format2 != "object") {
throw Error(bignumberError + "Argument not an object: " + format2);
}
str = x.toFixed(dp, rm);
if (x.c) {
var i, arr = str.split("."), g1 = +format2.groupSize, g2 = +format2.secondaryGroupSize, groupSeparator = format2.groupSeparator || "", intPart = arr[0], fractionPart = arr[1], isNeg = x.s < 0, intDigits = isNeg ? intPart.slice(1) : intPart, len = intDigits.length;
if (g2) {
i = g1;
g1 = g2;
g2 = i;
len -= i;
}
if (g1 > 0 && len > 0) {
i = len % g1 || g1;
intPart = intDigits.substr(0, i);
for (; i < len; i += g1) intPart += groupSeparator + intDigits.substr(i, g1);
if (g2 > 0) intPart += groupSeparator + intDigits.slice(i);
if (isNeg) intPart = "-" + intPart;
}
str = fractionPart ? intPart + (format2.decimalSeparator || "") + ((g2 = +format2.fractionGroupSize) ? fractionPart.replace(
new RegExp("\\d{" + g2 + "}\\B", "g"),
"$&" + (format2.fractionGroupSeparator || "")
) : fractionPart) : intPart;
}
return (format2.prefix || "") + str + (format2.suffix || "");
};
P.toFraction = function(md) {
var d, d0, d1, d2, e, exp, n, n0, n1, q, r, s, x = this, xc = x.c;
if (md != null) {
n = new BigNumber2(md);
if (!n.isInteger() && (n.c || n.s !== 1) || n.lt(ONE)) {
throw Error(bignumberError + "Argument " + (n.isInteger() ? "out of range: " : "not an integer: ") + valueOf(n));
}
}
if (!xc) return new BigNumber2(x);
d = new BigNumber2(ONE);
n1 = d0 = new BigNumber2(ONE);
d1 = n0 = new BigNumber2(ONE);
s = coeffToString(xc);
e = d.e = s.length - x.e - 1;
d.c[0] = POWS_TEN[(exp = e % LOG_BASE) < 0 ? LOG_BASE + exp : exp];
md = !md || n.comparedTo(d) > 0 ? e > 0 ? d : n1 : n;
exp = MAX_EXP;
MAX_EXP = 1 / 0;
n = new BigNumber2(s);
n0.c[0] = 0;
for (; ; ) {
q = div(n, d, 0, 1);
d2 = d0.plus(q.times(d1));
if (d2.comparedTo(md) == 1) break;
d0 = d1;
d1 = d2;
n1 = n0.plus(q.times(d2 = n1));
n0 = d2;
d = n.minus(q.times(d2 = d));
n = d2;
}
d2 = div(md.minus(d0), d1, 0, 1);
n0 = n0.plus(d2.times(n1));
d0 = d0.plus(d2.times(d1));
n0.s = n1.s = x.s;
e = e * 2;
r = div(n1, d1, e, ROUNDING_MODE).minus(x).abs().comparedTo(
div(n0, d0, e, ROUNDING_MODE).minus(x).abs()
) < 1 ? [n1, d1] : [n0, d0];
MAX_EXP = exp;
return r;
};
P.toNumber = function() {
return +valueOf(this);
};
P.toPrecision = function(sd, rm) {
if (sd != null) intCheck(sd, 1, MAX);
return format(this, sd, rm, 2);
};
P.toString = function(b) {
var str, n = this, s = n.s, e = n.e;
if (e === null) {
if (s) {
str = "Infinity";
if (s < 0) str = "-" + str;
} else {
str = "NaN";
}
} else {
if (b == null) {
str = e <= TO_EXP_NEG || e >= TO_EXP_POS ? toExponential(coeffToString(n.c), e) : toFixedPoint(coeffToString(n.c), e, "0");
} else if (b === 10 && alphabetHasNormalDecimalDigits) {
n = round(new BigNumber2(n), DECIMAL_PLACES + e + 1, ROUNDING_MODE);
str = toFixedPoint(coeffToString(n.c), n.e, "0");
} else {
intCheck(b, 2, ALPHABET.length, "Base");
str = convertBase(toFixedPoint(coeffToString(n.c), e, "0"), 10, b, s, true);
}
if (s < 0 && n.c[0]) str = "-" + str;
}
return str;
};
P.valueOf = P.toJSON = function() {
return valueOf(this);
};
P._isBigNumber = true;
P[Symbol.toStringTag] = "BigNumber";
P[Symbol.for("nodejs.util.inspect.custom")] = P.valueOf;
if (configObject != null) BigNumber2.set(configObject);
return BigNumber2;
}
function bitFloor(n) {
var i = n | 0;
return n > 0 || n === i ? i : i - 1;
}
function coeffToString(a) {
var s, z, i = 1, j = a.length, r = a[0] + "";
for (; i < j; ) {
s = a[i++] + "";
z = LOG_BASE - s.length;
for (; z--; s = "0" + s) ;
r += s;
}
for (j = r.length; r.charCodeAt(--j) === 48; ) ;
return r.slice(0, j + 1 || 1);
}
function compare(x, y) {
var a, b, xc = x.c, yc = y.c, i = x.s, j = y.s, k = x.e, l = y.e;
if (!i || !j) return null;
a = xc && !xc[0];
b = yc && !yc[0];
if (a || b) return a ? b ? 0 : -j : i;
if (i != j) return i;
a = i < 0;
b = k == l;
if (!xc || !yc) return b ? 0 : !xc ^ a ? 1 : -1;
if (!b) return k > l ^ a ? 1 : -1;
j = (k = xc.length) < (l = yc.length) ? k : l;
for (i = 0; i < j; i++) if (xc[i] != yc[i]) return xc[i] > yc[i] ^ a ? 1 : -1;
return k == l ? 0 : k > l ^ a ? 1 : -1;
}
function intCheck(n, min, max, name) {
if (n < min || n > max || n !== mathfloor(n)) {
throw Error(bignumberError + (name || "Argument") + (typeof n == "number" ? n < min || n > max ? " out of range: " : " not an integer: " : " not a primitive number: ") + String(n));
}
}
function isOdd(n) {
var k = n.c.length - 1;
return bitFloor(n.e / LOG_BASE) == k && n.c[k] % 2 != 0;
}
function toExponential(str, e) {
return (str.length > 1 ? str.charAt(0) + "." + str.slice(1) : str) + (e < 0 ? "e" : "e+") + e;
}
function toFixedPoint(str, e, z) {
var len, zs;
if (e < 0) {
for (zs = z + "."; ++e; zs += z) ;
str = zs + str;
} else {
len = str.length;
if (++e > len) {
for (zs = z, e -= len; --e; zs += z) ;
str += zs;
} else if (e < len) {
str = str.slice(0, e) + "." + str.slice(e);
}
}
return str;
}
var BigNumber = clone$1();
var SplayTreeNode = class {
constructor(key) {
__publicField(this, "key");
__publicField(this, "left", null);
__publicField(this, "right", null);
this.key = key;
}
};
var SplayTreeSetNode = class extends SplayTreeNode {
constructor(key) {
super(key);
}
};
var SplayTree = class {
constructor() {
__publicField(this, "size", 0);
__publicField(this, "modificationCount", 0);
__publicField(this, "splayCount", 0);
}
splay(key) {
const root = this.root;
if (root == null) {
this.compare(key, key);
return -1;
}
let right = null;
let newTreeRight = null;
let left = null;
let newTreeLeft = null;
let current = root;
const compare2 = this.compare;
let comp;
while (true) {
comp = compare2(current.key, key);
if (comp > 0) {
let currentLeft = current.left;
if (currentLeft == null) break;
comp = compare2(currentLeft.key, key);
if (comp > 0) {
current.left = currentLeft.right;
currentLeft.right = current;
current = currentLeft;
currentLeft = current.left;
if (currentLeft == null) break;
}
if (right == null) {
newTreeRight = current;
} else {
right.left = current;
}
right = current;
current = currentLeft;
} else if (comp < 0) {
let currentRight = current.right;
if (currentRight == null) break;
comp = compare2(currentRight.key, key);
if (comp < 0) {
current.right = currentRight.left;
currentRight.left = current;
current = currentRight;
currentRight = current.right;
if (currentRight == null) break;
}
if (left == null) {
newTreeLeft = current;
} else {
left.right = current;
}
left = current;
current = currentRight;
} else {
break;
}
}
if (left != null) {
left.right = current.left;
current.left = newTreeLeft;
}
if (right != null) {
right.left = current.right;
current.right = newTreeRight;
}
if (this.root !== current) {
this.root = current;
this.splayCount++;
}
return comp;
}
splayMin(node) {
let current = node;
let nextLeft = current.left;
while (nextLeft != null) {
const left = nextLeft;
current.left = left.right;
left.right = current;
current = left;
nextLeft = current.left;
}
return current;
}
splayMax(node) {
let current = node;
let nextRight = current.right;
while (nextRight != null) {
const right = nextRight;
current.right = right.left;
right.left = current;
current = right;
nextRight = current.right;
}
return current;
}
_delete(key) {
if (this.root == null) return null;
const comp = this.splay(key);
if (comp != 0) return null;
let root = this.root;
const result = root;
const left = root.left;
this.size--;
if (left == null) {
this.root = root.right;
} else {
const right = root.right;
root = this.splayMax(left);
root.right = right;
this.root = root;
}
this.modificationCount++;
return result;
}
addNewRoot(node, comp) {
this.size++;
this.modificationCount++;
const root = this.root;
if (root == null) {
this.root = node;
return;
}
if (comp < 0) {
node.left = root;
node.right = root.right;
root.right = null;
} else {
node.right = root;
node.left = root.left;
root.left = null;
}
this.root = node;
}
_first() {
const root = this.root;
if (root == null) return null;
this.root = this.splayMin(root);
return this.root;
}
_last() {
const root = this.root;
if (root == null) return null;
this.root = this.splayMax(root);
return this.root;
}
clear() {
this.root = null;
this.size = 0;
this.modificationCount++;
}
has(key) {
return this.validKey(key) && this.splay(key) == 0;
}
defaultCompare() {
return (a, b) => a < b ? -1 : a > b ? 1 : 0;
}
wrap() {
return {
getRoot: () => {
return this.root;
},
setRoot: (root) => {
this.root = root;
},
getSize: () => {
return this.size;
},
getModificationCount: () => {
return this.modificationCount;
},
getSplayCount: () => {
return this.splayCount;
},
setSplayCount: (count) => {
this.splayCount = count;
},
splay: (key) => {
return this.splay(key);
},
has: (key) => {
return this.has(key);
}
};
}
};
var SplayTreeSet = class _SplayTreeSet extends SplayTree {
constructor(compare2, isValidKey) {
super();
__publicField(this, "root", null);
__publicField(this, "compare");
__publicField(this, "validKey");
__publicField(this, _a, "[object Set]");
this.compare = compare2 ?? this.defaultCompare();
this.validKey = isValidKey ?? ((v) => v != null && v != void 0);
}
delete(element) {
if (!this.validKey(element)) return false;
return this._delete(element) != null;
}
deleteAll(elements) {
for (const element of elements) {
this.delete(element);
}
}
forEach(f) {
const nodes = this[Symbol.iterator]();
let result;
while (result = nodes.next(), !result.done) {
f(result.value, result.value, this);
}
}
add(element) {
const compare2 = this.splay(element);
if (compare2 != 0) this.addNewRoot(new SplayTreeSetNode(element), compare2);
return this;
}
addAndReturn(element) {
const compare2 = this.splay(element);
if (compare2 != 0) this.addNewRoot(new SplayTreeSetNode(element), compare2);
return this.root.key;
}
addAll(elements) {
for (const element of elements) {
this.add(element);
}
}
isEmpty() {
return this.root == null;
}
isNotEmpty() {
return this.root != null;
}
single() {
if (this.size == 0) throw "Bad state: No element";
if (this.size > 1) throw "Bad state: Too many element";
return this.root.key;
}
first() {
if (this.size == 0) throw "Bad state: No element";
return this._first().key;
}
last() {
if (this.size == 0) throw "Bad state: No element";
return this._last().key;
}
lastBefore(element) {
if (element == null) throw "Invalid arguments(s)";
if (this.root == null) return null;
const comp = this.splay(element);
if (comp < 0) return this.root.key;
let node = this.root.left;
if (node == null) return null;
let nodeRight = node.right;
while (nodeRight != null) {
node = nodeRight;
nodeRight = node.right;
}
return node.key;
}
firstAfter(element) {
if (element == null) throw "Invalid arguments(s)";
if (this.root == null) return null;
const comp = this.splay(element);
if (comp > 0) return this.root.key;
let node = this.root.right;
if (node == null) return null;
let nodeLeft = node.left;
while (nodeLeft != null) {
node = nodeLeft;
nodeLeft = node.left;
}
return node.key;
}
retainAll(elements) {
const retainSet = new _SplayTreeSet(this.compare, this.validKey);
const modificationCount = this.modificationCount;
for (const object of elements) {
if (modificationCount != this.modificationCount) {
throw "Concurrent modification during iteration.";
}
if (this.validKey(object) && this.splay(object) == 0) {
retainSet.add(this.root.key);
}
}
if (retainSet.size != this.size) {
this.root = retainSet.root;
this.size = retainSet.size;
this.modificationCount++;
}
}
lookup(object) {
if (!this.validKey(object)) return null;
const comp = this.splay(object);
if (comp != 0) return null;
return this.root.key;
}
intersection(other) {
const result = new _SplayTreeSet(this.compare, this.validKey);
for (const element of this) {
if (other.has(element)) result.add(element);
}
return result;
}
difference(other) {
const result = new _SplayTreeSet(this.compare, this.validKey);
for (const element of this) {
if (!other.has(element)) result.add(element);
}
return result;
}
union(other) {
const u2 = this.clone();
u2.addAll(other);
return u2;
}
clone() {
const set2 = new _SplayTreeSet(this.compare, this.validKey);
set2.size = this.size;
set2.root = this.copyNode(this.root);
return set2;
}
copyNode(node) {
if (node == null) return null;
function copyChildren(node2, dest) {
let left;
let right;
do {
left = node2.left;
right = node2.right;
if (left != null) {
const newLeft = new SplayTreeSetNode(left.key);
dest.left = newLeft;
copyChildren(left, newLeft);
}
if (right != null) {
const newRight = new SplayTreeSetNode(right.key);
dest.right = newRight;
node2 = right;
dest = newRight;
}
} while (right != null);
}
const result = new SplayTreeSetNode(node.key);
copyChildren(node, result);
return result;
}
toSet() {
return this.clone();
}
entries() {
return new SplayTreeSetEntryIterableIterator(this.wrap());
}
keys() {
return this[Symbol.iterator]();
}
values() {
return this[Symbol.iterator]();
}
[(_b = Symbol.iterator, _a = Symbol.toStringTag, _b)]() {
return new SplayTreeKeyIterableIterator(this.wrap());
}
};
var SplayTreeIterableIterator = class {
constructor(tree) {
__publicField(this, "tree");
__publicField(this, "path", new Array());
__publicField(this, "modificationCount", null);
__publicField(this, "splayCount");
this.tree = tree;
this.splayCount = tree.getSplayCount();
}
[Symbol.iterator]() {
return this;
}
next() {
if (this.moveNext()) return { done: false, value: this.current() };
return { done: true, value: null };
}
current() {
if (!this.path.length) return null;
const node = this.path[this.path.length - 1];
return this.getValue(node);
}
rebuildPath(key) {
this.path.splice(0, this.path.length);
this.tree.splay(key);
this.path.push(this.tree.getRoot());
this.splayCount = this.tree.getSplayCount();
}
findLeftMostDescendent(node) {
while (node != null) {
this.path.push(node);
node = node.left;
}
}
moveNext() {
if (this.modificationCount != this.tree.getModificationCount()) {
if (this.modificationCount == null) {
this.modificationCount = this.tree.getModificationCount();
let node2 = this.tree.getRoot();
while (node2 != null) {
this.path.push(node2);
node2 = node2.left;
}
return this.path.length > 0;
}
throw "Concurrent modification during iteration.";
}
if (!this.path.length) return false;
if (this.splayCount != this.tree.getSplayCount()) {
this.rebuildPath(this.path[this.path.length - 1].key);
}
let node = this.path[this.path.length - 1];
let next = node.right;
if (next != null) {
while (next != null) {
this.path.push(next);
next = next.left;
}
return true;
}
this.path.pop();
while (this.path.length && this.path[this.path.length - 1].right === node) {
node = this.path.pop();
}
return this.path.length > 0;
}
};
var SplayTreeKeyIterableIterator = class extends SplayTreeIterableIterator {
getValue(node) {
return node.key;
}
};
var SplayTreeSetEntryIterableIterator = class extends SplayTreeIterableIterator {
getValue(node) {
return [node.key, node.key];
}
};
var constant_default = (x) => {
return () => {
return x;
};
};
var compare_default = (eps) => {
const almostEqual = eps ? (a, b) => b.minus(a).abs().isLessThanOrEqualTo(eps) : constant_default(false);
return (a, b) => {
if (almostEqual(a, b)) return 0;
return a.comparedTo(b);
};
};
function orient_default(eps) {
const almostCollinear = eps ? (area2, ax, ay, cx, cy) => area2.exponentiatedBy(2).isLessThanOrEqualTo(
cx.minus(ax).exponentiatedBy(2).plus(cy.minus(ay).exponentiatedBy(2)).times(eps)
) : constant_default(false);
return (a, b, c) => {
const ax = a.x, ay = a.y, cx = c.x, cy = c.y;
const area2 = ay.minus(cy).times(b.x.minus(cx)).minus(ax.minus(cx).times(b.y.minus(cy)));
if (almostCollinear(area2, ax, ay, cx, cy)) return 0;
return area2.comparedTo(0);
};
}
var identity_default = (x) => {
return x;
};
var snap_default = (eps) => {
if (eps) {
const xTree = new SplayTreeSet(compare_default(eps));
const yTree = new SplayTreeSet(compare_default(eps));
const snapCoord = (coord, tree) => {
return tree.addAndReturn(coord);
};
const snap = (v) => {
return {
x: snapCoord(v.x, xTree),
y: snapCoord(v.y, yTree)
};
};
snap({ x: new BigNumber(0), y: new BigNumber(0) });
return snap;
}
return identity_default;
};
var set = (eps) => {
return {
set: (eps2) => {
precision = set(eps2);
},
reset: () => set(eps),
compare: compare_default(eps),
snap: snap_default(eps),
orient: orient_default(eps)
};
};
var precision = set();
var isInBbox = (bbox2, point2) => {
return bbox2.ll.x.isLessThanOrEqualTo(point2.x) && point2.x.isLessThanOrEqualTo(bbox2.ur.x) && bbox2.ll.y.isLessThanOrEqualTo(point2.y) && point2.y.isLessThanOrEqualTo(bbox2.ur.y);
};
var getBboxOverlap = (b1, b2) => {
if (b2.ur.x.isLessThan(b1.ll.x) || b1.ur.x.isLessThan(b2.ll.x) || b2.ur.y.isLessThan(b1.ll.y) || b1.ur.y.isLessThan(b2.ll.y))
return null;
const lowerX = b1.ll.x.isLessThan(b2.ll.x) ? b2.ll.x : b1.ll.x;
const upperX = b1.ur.x.isLessThan(b2.ur.x) ? b1.ur.x : b2.ur.x;
const lowerY = b1.ll.y.isLessThan(b2.ll.y) ? b2.ll.y : b1.ll.y;
const upperY = b1.ur.y.isLessThan(b2.ur.y) ? b1.ur.y : b2.ur.y;
return { ll: { x: lowerX, y: lowerY }, ur: { x: upperX, y: upperY } };
};
var crossProduct = (a, b) => a.x.times(b.y).minus(a.y.times(b.x));
var dotProduct = (a, b) => a.x.times(b.x).plus(a.y.times(b.y));
var length$1 = (v) => dotProduct(v, v).sqrt();
var sineOfAngle = (pShared, pBase, pAngle) => {
const vBase = { x: pBase.x.minus(pShared.x), y: pBase.y.minus(pShared.y) };
const vAngle = { x: pAngle.x.minus(pShared.x), y: pAngle.y.minus(pShared.y) };
return crossProduct(vAngle, vBase).div(length$1(vAngle)).div(length$1(vBase));
};
var cosineOfAngle = (pShared, pBase, pAngle) => {
const vBase = { x: pBase.x.minus(pShared.x), y: pBase.y.minus(pShared.y) };
const vAngle = { x: pAngle.x.minus(pShared.x), y: pAngle.y.minus(pShared.y) };
return dotProduct(vAngle, vBase).div(length$1(vAngle)).div(length$1(vBase));
};
var horizontalIntersection = (pt, v, y) => {
if (v.y.isZero()) return null;
return { x: pt.x.plus(v.x.div(v.y).times(y.minus(pt.y))), y };
};
var verticalIntersection = (pt, v, x) => {
if (v.x.isZero()) return null;
return { x, y: pt.y.plus(v.y.div(v.x).times(x.minus(pt.x))) };
};
var intersection = (pt1, v1, pt2, v2) => {
if (v1.x.isZero()) return verticalIntersection(pt2, v2, pt1.x);
if (v2.x.isZero()) return verticalIntersection(pt1, v1, pt2.x);
if (v1.y.isZero()) return horizontalIntersection(pt2, v2, pt1.y);
if (v2.y.isZero()) return horizontalIntersection(pt1, v1, pt2.y);
const kross = crossProduct(v1, v2);
if (kross.isZero()) return null;
const ve = { x: pt2.x.minus(pt1.x), y: pt2.y.minus(pt1.y) };
const d1 = crossProduct(ve, v1).div(kross);
const d2 = crossProduct(ve, v2).div(kross);
const x1 = pt1.x.plus(d2.times(v1.x)), x2 = pt2.x.plus(d1.times(v2.x));
const y1 = pt1.y.plus(d2.times(v1.y)), y2 = pt2.y.plus(d1.times(v2.y));
const x = x1.plus(x2).div(2);
const y = y1.plus(y2).div(2);
return { x, y };
};
var SweepEvent = class _SweepEvent {
// Warning: 'point' input will be modified and re-used (for performance)
constructor(point2, isLeft) {
__publicField(this, "point");
__publicField(this, "isLeft");
__publicField(this, "segment");
__publicField(this, "otherSE");
__publicField(this, "consumedBy");
if (point2.events === void 0) point2.events = [this];
else point2.events.push(this);
this.point = point2;
this.isLeft = isLeft;
}
// for ordering sweep events in the sweep event queue
static compare(a, b) {
const ptCmp = _SweepEvent.comparePoints(a.point, b.point);
if (ptCmp !== 0) return ptCmp;
if (a.point !== b.point) a.link(b);
if (a.isLeft !== b.isLeft) return a.isLeft ? 1 : -1;
return Segment2.compare(a.segment, b.segment);
}
// for ordering points in sweep line order
static comparePoints(aPt, bPt) {
if (aPt.x.isLessThan(bPt.x)) return -1;
if (aPt.x.isGreaterThan(bPt.x)) return 1;
if (aPt.y.isLessThan(bPt.y)) return -1;
if (aPt.y.isGreaterThan(bPt.y)) return 1;
return 0;
}
link(other) {
if (other.point === this.point) {
throw new Error("Tried to link already linked events");
}
const otherEvents = other.point.events;
for (let i = 0, iMax = otherEvents.length; i < iMax; i++) {
const evt = otherEvents[i];
this.point.events.push(evt);
evt.point = this.point;
}
this.checkForConsuming();
}
/* Do a pass over our linked events and check to see if any pair
* of segments match, and should be consumed. */
checkForConsuming() {
const numEvents = this.point.events.length;
for (let i = 0; i < numEvents; i++) {
const evt1 = this.point.events[i];
if (evt1.segment.consumedBy !== void 0) continue;
for (let j = i + 1; j < numEvents; j++) {
const evt2 = this.point.events[j];
if (evt2.consumedBy !== void 0) continue;
if (evt1.otherSE.point.events !== evt2.otherSE.point.events) continue;
evt1.segment.consume(evt2.segment);
}
}
}
getAvailableLinkedEvents() {
const events = [];
for (let i = 0, iMax = this.point.events.length; i < iMax; i++) {
const evt = this.point.events[i];
if (evt !== this && !evt.segment.ringOut && evt.segment.isInResult()) {
events.push(evt);
}
}
return events;
}
/**
* Returns a comparator function for sorting linked events that will
* favor the event that will give us the smallest left-side angle.
* All ring construction starts as low as possible heading to the right,
* so by always turning left as sharp as possible we'll get polygons
* without uncessary loops & holes.
*
* The comparator function has a compute cache such that it avoids
* re-computing already-computed values.
*/
getLeftmostComparator(baseEvent) {
const cache = /* @__PURE__ */ new Map();
const fillCache = (linkedEvent) => {
const nextEvent = linkedEvent.otherSE;
cache.set(linkedEvent, {
sine: sineOfAngle(this.point, baseEvent.point, nextEvent.point),
cosine: cosineOfAngle(this.point, baseEvent.point, nextEvent.point)
});
};
return (a, b) => {
if (!cache.has(a)) fillCache(a);
if (!cache.has(b)) fillCache(b);
const { sine: asine, cosine: acosine } = cache.get(a);
const { sine: bsine, cosine: bcosine } = cache.get(b);
if (asine.isGreaterThanOrEqualTo(0) && bsine.isGreaterThanOrEqualTo(0)) {
if (acosine.isLessThan(bcosine)) return 1;
if (acosine.isGreaterThan(bcosine)) return -1;
return 0;
}
if (asine.isLessThan(0) && bsine.isLessThan(0)) {
if (acosine.isLessThan(bcosine)) return -1;
if (acosine.isGreaterThan(bcosine)) return 1;
return 0;
}
if (bsine.isLessThan(asine)) return -1;
if (bsine.isGreaterThan(asine)) return 1;
return 0;
};
}
};
var RingOut = class _RingOut {
constructor(events) {
__publicField(this, "events");
__publicField(this, "poly");
__publicField(this, "_isExteriorRing");
__publicField(this, "_enclosingRing");
this.events = events;
for (let i = 0, iMax = events.length; i < iMax; i++) {
events[i].segment.ringOut = this;
}
this.poly = null;
}
/* Given the segments from the sweep line pass, compute & return a series
* of closed rings from all the segments marked to be part of the result */
static factory(allSegments) {
const ringsOut = [];
for (let i = 0, iMax = allSegments.length; i < iMax; i++) {
const segment = allSegments[i];
if (!segment.isInResult() || segment.ringOut) continue;
let prevEvent = null;
let event = segment.leftSE;
let nextEvent = segment.rightSE;
const events = [event];
const startingPoint = event.point;
const intersectionLEs = [];
while (true) {
prevEvent = event;
event = nextEvent;
events.push(event);
if (event.point === startingPoint) break;
while (true) {
const availableLEs = event.getAvailableLinkedEvents();
if (availableLEs.length === 0) {
const firstPt = events[0].point;
const lastPt = events[events.length - 1].point;
throw new Error(
`Unable to complete output ring starting at [${firstPt.x}, ${firstPt.y}]. Last matching segment found ends at [${lastPt.x}, ${lastPt.y}].`
);
}
if (availableLEs.length === 1) {
nextEvent = availableLEs[0].otherSE;
break;
}
let indexLE = null;
for (let j = 0, jMax = intersectionLEs.length; j < jMax; j++) {
if (intersectionLEs[j].point === event.point) {
indexLE = j;
break;
}
}
if (indexLE !== null) {
const intersectionLE = intersectionLEs.splice(indexLE)[0];
const ringEvents = events.splice(intersectionLE.index);
ringEvents.unshift(ringEvents[0].otherSE);
ringsOut.push(new _RingOut(ringEvents.reverse()));
continue;
}
intersectionLEs.push({
index: events.length,
point: event.point
});
const comparator = event.getLeftmostComparator(prevEvent);
nextEvent = availableLEs.sort(comparator)[0].otherSE;
break;
}
}
ringsOut.push(new _RingOut(events));
}
return ringsOut;
}
getGeom() {
let prevPt = this.events[0].point;
const points = [prevPt];
for (let i = 1, iMax = this.events.length - 1; i < iMax; i++) {
const pt2 = this.events[i].point;
const nextPt2 = this.events[i + 1].point;
if (precision.orient(pt2, prevPt, nextPt2) === 0) continue;
points.push(pt2);
prevPt = pt2;
}
if (points.length === 1) return null;
const pt = points[0];
const nextPt = points[1];
if (precision.orient(pt, prevPt, nextPt) === 0) points.shift();
points.push(points[0]);
const step = this.isExteriorRing() ? 1 : -1;
const iStart = this.isExteriorRing() ? 0 : points.length - 1;
const iEnd = this.isExteriorRing() ? points.length : -1;
const orderedPoints = [];
for (let i = iStart; i != iEnd; i += step)
orderedPoints.push([points[i].x.toNumber(), points[i].y.toNumber()]);
return orderedPoints;
}
isExteriorRing() {
if (this._isExteriorRing === void 0) {
const enclosing = this.enclosingRing();
this._isExteriorRing = enclosing ? !enclosing.isExteriorRing() : true;
}
return this._isExteriorRing;
}
enclosingRing() {
if (this._enclosingRing === void 0) {
this._enclosingRing = this._calcEnclosingRing();
}
return this._enclosingRing;
}
/* Returns the ring that encloses this one, if any */
_calcEnclosingRing() {
var _a2, _b2;
let leftMostEvt = this.events[0];
for (let i = 1, iMax = this.events.length; i < iMax; i++) {
const evt = this.events[i];
if (SweepEvent.compare(leftMostEvt, evt) > 0) leftMostEvt = evt;
}
let prevSeg = leftMostEvt.segment.prevInResult();
let prevPrevSeg = prevSeg ? prevSeg.prevInResult() : null;
while (true) {
if (!prevSeg) return null;
if (!prevPrevSeg) return prevSeg.ringOut;
if (prevPrevSeg.ringOut !== prevSeg.ringOut) {
if (((_a2 = prevPrevSeg.ringOut) == null ? void 0 : _a2.enclosingRing()) !== prevSeg.ringOut) {
return prevSeg.ringOut;
} else return (_b2 = prevSeg.ringOut) == null ? void 0 : _b2.enclosingRing();
}
prevSeg = prevPrevSeg.prevInResult();
prevPrevSeg = prevSeg ? prevSeg.prevInResult() : null;
}
}
};
var PolyOut = class {
constructor(exteriorRing) {
__publicField(this, "exteriorRing");
__publicField(this, "interiorRings");
this.exteriorRing = exteriorRing;
exteriorRing.poly = this;
this.interiorRings = [];
}
addInterior(ring) {
this.interiorRings.push(ring);
ring.poly = this;
}
getGeom() {
const geom0 = this.exteriorRing.getGeom();
if (geom0 === null) return null;
const geom = [geom0];
for (let i = 0, iMax = this.interiorRings.length; i < iMax; i++) {
const ringGeom = this.interiorRings[i].getGeom();
if (ringGeom === null) continue;
geom.push(ringGeom);
}
return geom;
}
};
var MultiPolyOut = class {
constructor(rings) {
__publicField(this, "rings");
__publicField(this, "polys");
this.rings = rings;
this.polys = this._composePolys(rings);
}
getGeom() {
const geom = [];
for (let i = 0, iMax = this.polys.length; i < iMax; i++) {
const polyGeom = this.polys[i].getGeom();
if (polyGeom === null) continue;
geom.push(polyGeom);
}
return geom;
}
_composePolys(rings) {
var _a2;
const polys = [];
for (let i = 0, iMax = rings.length; i < iMax; i++) {
const ring = rings[i];
if (ring.poly) continue;
if (ring.isExteriorRing()) polys.push(new PolyOut(ring));
else {
const enclosingRing = ring.enclosingRing();
if (!(enclosingRing == null ? void 0 : enclosingRing.poly)) polys.push(new PolyOut(enclosingRing));
(_a2 = enclosingRing == null ? void 0 : enclosingRing.poly) == null ? void 0 : _a2.addInterior(ring);
}
}
return polys;
}
};
var SweepLine = class {
constructor(queue, comparator = Segment2.compare) {
__publicField(this, "queue");
__publicField(this, "tree");
__publicField(this, "segments");
this.queue = queue;
this.tree = new SplayTreeSet(comparator);
this.segments = [];
}
process(event) {
const segment = event.segment;
const newEvents = [];
if (event.consumedBy) {
if (event.isLeft) this.queue.delete(event.otherSE);
else this.tree.delete(segment);
return newEvents;
}
if (event.isLeft) this.tree.add(segment);
let prevSeg = segment;
let nextSeg = segment;
do {
prevSeg = this.tree.lastBefore(prevSeg);
} while (prevSeg != null && prevSeg.consumedBy != void 0);
do {
nextSeg = this.tree.firstAfter(nextSeg);
} while (nextSeg != null && nextSeg.consumedBy != void 0);
if (event.isLeft) {
let prevMySplitter = null;
if (prevSeg) {
const prevInter = prevSeg.getIntersection(segment);
if (prevInter !== null) {
if (!segment.isAnEndpoint(prevInter)) prevMySplitter = prevInter;
if (!prevSeg.isAnEndpoint(prevInter)) {
const newEventsFromSplit = this._splitSafely(prevSeg, prevInter);
for (let i = 0, iMax = newEventsFromSplit.length; i < iMax; i++) {
newEvents.push(newEventsFromSplit[i]);
}
}
}
}
let nextMySplitter = null;
if (nextSeg) {
const nextInter = nextSeg.getIntersection(segment);
if (nextInter !== null) {
if (!segment.isAnEndpoint(nextInter)) nextMySplitter = nextInter;
if (!nextSeg.isAnEndpoint(nextInter)) {
const newEventsFromSplit = this._splitSafely(nextSeg, nextInter);
for (let i = 0, iMax = newEventsFromSplit.length; i < iMax; i++) {
newEvents.push(newEventsFromSplit[i]);
}
}
}
}
if (prevMySplitter !== null || nextMySplitter !== null) {
let mySplitter = null;
if (prevMySplitter === null) mySplitter = nextMySplitter;
else if (nextMySplitter === null) mySplitter = prevMySplitter;
else {
const cmpSplitters = SweepEvent.comparePoints(
prevMySplitter,
nextMySplitter
);
mySplitter = cmpSplitters <= 0 ? prevMySplitter : nextMySplitter;
}
this.queue.delete(segment.rightSE);
newEvents.push(segment.rightSE);
const newEventsFromSplit = segment.split(mySplitter);
for (let i = 0, iMax = newEventsFromSplit.length; i < iMax; i++) {
newEvents.push(newEventsFromSplit[i]);
}
}
if (newEvents.length > 0) {
this.tree.delete(segment);
newEvents.push(event);
} else {
this.segments.push(segment);
segment.prev = prevSeg;
}
} else {
if (prevSeg && nextSeg) {
const inter = prevSeg.getIntersection(nextSeg);
if (inter !== null) {
if (!prevSeg.isAnEndpoint(inter)) {
const newEventsFromSplit = this._splitSafely(prevSeg, inter);
for (let i = 0, iMax = newEventsFromSplit.length; i < iMax; i++) {
newEvents.push(newEventsFromSplit[i]);
}
}
if (!nextSeg.isAnEndpoint(inter)) {
const newEventsFromSplit = this._splitSafely(nextSeg, inter);
for (let i = 0, iMax = newEventsFromSplit.length; i < iMax; i++) {
newEvents.push(newEventsFromSplit[i]);
}
}
}
}
this.tree.delete(segment);
}
return newEvents;
}
/* Safely split a segment that is currently in the datastructures
* IE - a segment other than the one that is currently being processed. */
_splitSafely(seg, pt) {
this.tree.delete(seg);
const rightSE = seg.rightSE;
this.queue.delete(rightSE);
const newEvents = seg.split(pt);
newEvents.push(rightSE);
if (seg.consumedBy === void 0) this.tree.add(seg);
return newEvents;
}
};
var Operation = class {
constructor() {
__publicField(this, "type");
__publicField(this, "numMultiPolys");
}
run(type, geom, moreGeoms) {
operation.type = type;
const multipolys = [new MultiPolyIn(geom, true)];
for (let i = 0, iMax = moreGeoms.length; i < iMax; i++) {
multipolys.push(new MultiPolyIn(moreGeoms[i], false));
}
operation.numMultiPolys = multipolys.length;
if (operation.type === "difference") {
const subject = multipolys[0];
let i = 1;
while (i < multipolys.length) {
if (getBboxOverlap(multipolys[i].bbox, subject.bbox) !== null) i++;
else multipolys.splice(i, 1);
}
}
if (operation.type === "intersection") {
for (let i = 0, iMax = multipolys.length; i < iMax; i++) {
const mpA = multipolys[i];
for (let j = i + 1, jMax = multipolys.length; j < jMax; j++) {
if (getBboxOverlap(mpA.bbox, multipolys[j].bbox) === null) return [];
}
}
}
const queue = new SplayTreeSet(SweepEvent.compare);
for (let i = 0, iMax = multipolys.length; i < iMax; i++) {
const sweepEvents = multipolys[i].getSweepEvents();
for (let j = 0, jMax = sweepEvents.length; j < jMax; j++) {
queue.add(sweepEvents[j]);
}
}
const sweepLine = new SweepLine(queue);
let evt = null;
if (queue.size != 0) {
evt = queue.first();
queue.delete(evt);
}
while (evt) {
const newEvents = sweepLine.process(evt);
for (let i = 0, iMax = newEvents.length; i < iMax; i++) {
const evt2 = newEvents[i];
if (evt2.consumedBy === void 0) queue.add(evt2);
}
if (queue.size != 0) {
evt = queue.first();
queue.delete(evt);
} else {
evt = null;
}
}
precision.reset();
const ringsOut = RingOut.factory(sweepLine.segments);
const result = new MultiPolyOut(ringsOut);
return result.getGeom();
}
};
var operation = new Operation();
var operation_default = operation;
var segmentId = 0;
var Segment2 = class _Segment {
/* Warning: a reference to ringWindings input will be stored,
* and possibly will be later modified */
constructor(leftSE, rightSE, rings, windings) {
__publicField(this, "id");
__publicField(this, "leftSE");
__publicField(this, "rightSE");
__publicField(this, "rings");
__publicField(this, "windings");
__publicField(this, "ringOut");
__publicField(this, "consumedBy");
__publicField(this, "prev");
__publicField(this, "_prevInResult");
__publicField(this, "_beforeState");
__publicField(this, "_afterState");
__publicField(this, "_isInResult");
this.id = ++segmentId;
this.leftSE = leftSE;
leftSE.segment = this;
leftSE.otherSE = rightSE;
this.rightSE = rightSE;
rightSE.segment = this;
rightSE.otherSE = leftSE;
this.rings = rings;
this.windings = windings;
}
/* This compare() function is for ordering segments in the sweep
* line tree, and does so according to the following criteria:
*
* Consider the vertical line that lies an infinestimal step to the
* right of the right-more of the two left endpoints of the input
* segments. Imagine slowly moving a point up from negative infinity
* in the increasing y direction. Which of the two segments will that
* point intersect first? That segment comes 'before' the other one.
*
* If neither segment would be intersected by such a line, (if one
* or more of the segments are vertical) then the line to be considered
* is directly on the right-more of the two left inputs.
*/
static compare(a, b) {
const alx = a.leftSE.point.x;
const blx = b.leftSE.point.x;
const arx = a.rightSE.point.x;
const brx = b.rightSE.point.x;
if (brx.isLessThan(alx)) return 1;
if (arx.isLessThan(blx)) return -1;
const aly = a.leftSE.point.y;
const bly = b.leftSE.point.y;
const ary = a.rightSE.point.y;
const bry = b.rightSE.point.y;
if (alx.isLessThan(blx)) {
if (bly.isLessThan(aly) && bly.isLessThan(ary)) return 1;
if (bly.isGreaterThan(aly) && bly.isGreaterThan(ary)) return -1;
const aCmpBLeft = a.comparePoint(b.leftSE.point);
if (aCmpBLeft < 0) return 1;
if (aCmpBLeft > 0) return -1;
const bCmpARight = b.comparePoint(a.rightSE.point);
if (bCmpARight !== 0) return bCmpARight;
return -1;
}
if (alx.isGreaterThan(blx)) {
if (aly.isLessThan(bly) && aly.isLessThan(bry)) return -1;
if (aly.isGreaterThan(bly) && aly.isGreaterThan(bry)) return 1;
const bCmpALeft = b.comparePoint(a.leftSE.point);
if (bCmpALeft !== 0) return bCmpALeft;
const aCmpBRight = a.comparePoint(b.rightSE.point);
if (aCmpBRight < 0) return 1;
if (aCmpBRight > 0) return -1;
return 1;
}
if (aly.isLessThan(bly)) return -1;
if (aly.isGreaterThan(bly)) return 1;
if (arx.isLessThan(brx)) {
const bCmpARight = b.comparePoint(a.rightSE.point);
if (bCmpARight !== 0) return bCmpARight;
}
if (arx.isGreaterThan(brx)) {
const aCmpBRight = a.comparePoint(b.rightSE.point);
if (aCmpBRight < 0) return 1;
if (aCmpBRight > 0) return -1;
}
if (!arx.eq(brx)) {
const ay = ary.minus(aly);
const ax = arx.minus(alx);
const by = bry.minus(bly);
const bx = brx.minus(blx);
if (ay.isGreaterThan(ax) && by.isLessThan(bx)) return 1;
if (ay.isLessThan(ax) && by.isGreaterThan(bx)) return -1;
}
if (arx.isGreaterThan(brx)) return 1;
if (arx.isLessThan(brx)) return -1;
if (ary.isLessThan(bry)) return -1;
if (ary.isGreaterThan(bry)) return 1;
if (a.id < b.id) return -1;
if (a.id > b.id) return 1;
return 0;
}
static fromRing(pt1, pt2, ring) {
let leftPt, rightPt, winding;
const cmpPts = SweepEvent.comparePoints(pt1, pt2);
if (cmpPts < 0) {
leftPt = pt1;
rightPt = pt2;
winding = 1;
} else if (cmpPts > 0) {
leftPt = pt2;
rightPt = pt1;
winding = -1;
} else
throw new Error(
`Tried to create degenerate segment at [${pt1.x}, ${pt1.y}]`
);
const leftSE = new SweepEvent(leftPt, true);
const rightSE = new SweepEvent(rightPt, false);
return new _Segment(leftSE, rightSE, [ring], [winding]);
}
/* When a segment is split, the rightSE is replaced with a new sweep event */
replaceRightSE(newRightSE) {
this.rightSE = newRightSE;
this.rightSE.segment = this;
this.rightSE.otherSE = this.leftSE;
this.leftSE.otherSE = this.rightSE;
}
bbox() {
const y1 = this.leftSE.point.y;
const y2 = this.rightSE.point.y;
return {
ll: { x: this.leftSE.point.x, y: y1.isLessThan(y2) ? y1 : y2 },
ur: { x: this.rightSE.point.x, y: y1.isGreaterThan(y2) ? y1 : y2 }
};
}
/* A vector from the left point to the right */
vector() {
return {
x: this.rightSE.point.x.minus(this.leftSE.point.x),
y: this.rightSE.point.y.minus(this.leftSE.point.y)
};
}
isAnEndpoint(pt) {
return pt.x.eq(this.leftSE.point.x) && pt.y.eq(this.leftSE.point.y) || pt.x.eq(this.rightSE.point.x) && pt.y.eq(this.rightSE.point.y);
}
/* Compare this segment with a point.
*
* A point P is considered to be colinear to a segment if there
* exists a distance D such that if we travel along the segment
* from one * endpoint towards the other a distance D, we find
* ourselves at point P.
*
* Return value indicates:
*
* 1: point lies above the segment (to the left of vertical)
* 0: point is colinear to segment
* -1: point lies below the segment (to the right of vertical)
*/
comparePoint(point2) {
return precision.orient(this.leftSE.point, point2, this.rightSE.point);
}
/**
* Given another segment, returns the first non-trivial intersection
* between the two segments (in terms of sweep line ordering), if it exists.
*
* A 'non-trivial' intersection is one that will cause one or both of the
* segments to be split(). As such, 'trivial' vs. 'non-trivial' intersection:
*
* * endpoint of segA with endpoint of segB --> trivial
* * endpoint of segA with point along segB --> non-trivial
* * endpoint of segB with point along segA --> non-trivial
* * point along segA with point along segB --> non-trivial
*
* If no non-trivial intersection exists, return null
* Else, return null.
*/
getIntersection(other) {
const tBbox = this.bbox();
const oBbox = other.bbox();
const bboxOverlap = getBboxOverlap(tBbox, oBbox);
if (bboxOverlap === null) return null;
const tlp = this.leftSE.point;
const trp = this.rightSE.point;
const olp = other.leftSE.point;
const orp = other.rightSE.point;
const touchesOtherLSE = isInBbox(tBbox, olp) && this.comparePoint(olp) === 0;
const touchesThisLSE = isInBbox(oBbox, tlp) && other.comparePoint(tlp) === 0;
const touchesOtherRSE = isInBbox(tBbox, orp) && this.comparePoint(orp) === 0;
const touchesThisRSE = isInBbox(oBbox, trp) && other.comparePoint(trp) === 0;
if (touchesThisLSE && touchesOtherLSE) {
if (touchesThisRSE && !touchesOtherRSE) return trp;
if (!touchesThisRSE && touchesOtherRSE) return orp;
return null;
}
if (touchesThisLSE) {
if (touchesOtherRSE) {
if (tlp.x.eq(orp.x) && tlp.y.eq(orp.y)) return null;
}
return tlp;
}
if (touchesOtherLSE) {
if (touchesThisRSE) {
if (trp.x.eq(olp.x) && trp.y.eq(olp.y)) return null;
}
return olp;
}
if (touchesThisRSE && touchesOtherRSE) return null;
if (touchesThisRSE) return trp;
if (touchesOtherRSE) return orp;
const pt = intersection(tlp, this.vector(), olp, other.vector());
if (pt === null) return null;
if (!isInBbox(bboxOverlap, pt)) return null;
return precision.snap(pt);
}
/**
* Split the given segment into multiple segments on the given points.
* * Each existing segment will retain its leftSE and a new rightSE will be
* generated for it.
* * A new segment will be generated which will adopt the original segment's
* rightSE, and a new leftSE will be generated for it.
* * If there are more than two points given to split on, new segments
* in the middle will be generated with new leftSE and rightSE's.
* * An array of the newly generated SweepEvents will be returned.
*
* Warning: input array of points is modified
*/
split(point2) {
const newEvents = [];
const alreadyLinked = point2.events !== void 0;
const newLeftSE = new SweepEvent(point2, true);
const newRightSE = new SweepEvent(point2, false);
const oldRightSE = this.rightSE;
this.replaceRightSE(newRightSE);
newEvents.push(newRightSE);
newEvents.push(newLeftSE);
const newSeg = new _Segment(
newLeftSE,
oldRightSE,
this.rings.slice(),
this.windings.slice()
);
if (SweepEvent.comparePoints(newSeg.leftSE.point, newSeg.rightSE.point) > 0) {
newSeg.swapEvents();
}
if (SweepEvent.comparePoints(this.leftSE.point, this.rightSE.point) > 0) {
this.swapEvents();
}
if (alreadyLinked) {
newLeftSE.checkForConsuming();
newRightSE.checkForConsuming();
}
return newEvents;
}
/* Swap which event is left and right */
swapEvents() {
const tmpEvt = this.rightSE;
this.rightSE = this.leftSE;
this.leftSE = tmpEvt;
this.leftSE.isLeft = true;
this.rightSE.isLeft = false;
for (let i = 0, iMax = this.windings.length; i < iMax; i++) {
this.windings[i] *= -1;
}
}
/* Consume another segment. We take their rings under our wing
* and mark them as consumed. Use for perfectly overlapping segments */
consume(other) {
let consumer = this;
let consumee = other;
while (consumer.consumedBy) consumer = consumer.consumedBy;
while (consumee.consumedBy) consumee = consumee.consumedBy;
const cmp = _Segment.compare(consumer, consumee);
if (cmp === 0) return;
if (cmp > 0) {
const tmp = consumer;
consumer = consumee;
consumee = tmp;
}
if (consumer.prev === consumee) {
const tmp = consumer;
consumer = consumee;
consumee = tmp;
}
for (let i = 0, iMax = consumee.rings.length; i < iMax; i++) {
const ring = consumee.rings[i];
const winding = consumee.windings[i];
const index = consumer.rings.indexOf(ring);
if (index === -1) {
consumer.rings.push(ring);
consumer.windings.push(winding);
} else consumer.windings[index] += winding;
}
consumee.rings = null;
consumee.windings = null;
consumee.consumedBy = consumer;
consumee.leftSE.consumedBy = consumer.leftSE;
consumee.rightSE.consumedBy = consumer.rightSE;
}
/* The first segment previous segment chain that is in the result */
prevInResult() {
if (this._prevInResult !== void 0) return this._prevInResult;
if (!this.prev) this._prevInResult = null;
else if (this.prev.isInResult()) this._prevInResult = this.prev;
else this._prevInResult = this.prev.prevInResult();
return this._prevInResult;
}
beforeState() {
if (this._beforeState !== void 0) return this._beforeState;
if (!this.prev)
this._beforeState = {
rings: [],
windings: [],
multiPolys: []
};
else {
const seg = this.prev.consumedBy || this.prev;
this._beforeState = seg.afterState();
}
return this._beforeState;
}
afterState() {
if (this._afterState !== void 0) return this._afterState;
const beforeState = this.beforeState();
this._afterState = {
rings: beforeState.rings.slice(0),
windings: beforeState.windings.slice(0),
multiPolys: []
};
const ringsAfter = this._afterState.rings;
const windingsAfter = this._afterState.windings;
const mpsAfter = this._afterState.multiPolys;
for (let i = 0, iMax = this.rings.length; i < iMax; i++) {
const ring = this.rings[i];
const winding = this.windings[i];
const index = ringsAfter.indexOf(ring);
if (index === -1) {
ringsAfter.push(ring);
windingsAfter.push(winding);
} else windingsAfter[index] += winding;
}
const polysAfter = [];
const polysExclude = [];
for (let i = 0, iMax = ringsAfter.length; i < iMax; i++) {
if (windingsAfter[i] === 0) continue;
const ring = ringsAfter[i];
const poly = ring.poly;
if (polysExclude.indexOf(poly) !== -1) continue;
if (ring.isExterior) polysAfter.push(poly);
else {
if (polysExclude.indexOf(poly) === -1) polysExclude.push(poly);
const index = polysAfter.indexOf(ring.poly);
if (index !== -1) polysAfter.splice(index, 1);
}
}
for (let i = 0, iMax = polysAfter.length; i < iMax; i++) {
const mp = polysAfter[i].multiPoly;
if (mpsAfter.indexOf(mp) === -1) mpsAfter.push(mp);
}
return this._afterState;
}
/* Is this segment part of the final result? */
isInResult() {
if (this.consumedBy) return false;
if (this._isInResult !== void 0) return this._isInResult;
const mpsBefore = this.beforeState().multiPolys;
const mpsAfter = this.afterState().multiPolys;
switch (operation_default.type) {
case "union": {
const noBefores = mpsBefore.length === 0;
const noAfters = mpsAfter.length === 0;
this._isInResult = noBefores !== noAfters;
break;
}
case "intersection": {
let least;
let most;
if (mpsBefore.length < mpsAfter.length) {
least = mpsBefore.length;
most = mpsAfter.length;
} else {
least = mpsAfter.length;
most = mpsBefore.length;
}
this._isInResult = most === operation_default.numMultiPolys && least < most;
break;
}
case "xor": {
const diff = Math.abs(mpsBefore.length - mpsAfter.length);
this._isInResult = diff % 2 === 1;
break;
}
case "difference": {
const isJustSubject = (mps) => mps.length === 1 && mps[0].isSubject;
this._isInResult = isJustSubject(mpsBefore) !== isJustSubject(mpsAfter);
break;
}
}
return this._isInResult;
}
};
var RingIn = class {
constructor(geomRing, poly, isExterior) {
__publicField(this, "poly");
__publicField(this, "isExterior");
__publicField(this, "segments");
__publicField(this, "bbox");
if (!Array.isArray(geomRing) || geomRing.length === 0) {
throw new Error("Input geometry is not a valid Polygon or MultiPolygon");
}
this.poly = poly;
this.isExterior = isExterior;
this.segments = [];
if (typeof geomRing[0][0] !== "number" || typeof geomRing[0][1] !== "number") {
throw new Error("Input geometry is not a valid Polygon or MultiPolygon");
}
const firstPoint = precision.snap({ x: new BigNumber(geomRing[0][0]), y: new BigNumber(geomRing[0][1]) });
this.bbox = {
ll: { x: firstPoint.x, y: firstPoint.y },
ur: { x: firstPoint.x, y: firstPoint.y }
};
let prevPoint = firstPoint;
for (let i = 1, iMax = geomRing.length; i < iMax; i++) {
if (typeof geomRing[i][0] !== "number" || typeof geomRing[i][1] !== "number") {
throw new Error("Input geometry is not a valid Polygon or MultiPolygon");
}
const point2 = precision.snap({ x: new BigNumber(geomRing[i][0]), y: new BigNumber(geomRing[i][1]) });
if (point2.x.eq(prevPoint.x) && point2.y.eq(prevPoint.y)) continue;
this.segments.push(Segment2.fromRing(prevPoint, point2, this));
if (point2.x.isLessThan(this.bbox.ll.x)) this.bbox.ll.x = point2.x;
if (point2.y.isLessThan(this.bbox.ll.y)) this.bbox.ll.y = point2.y;
if (point2.x.isGreaterThan(this.bbox.ur.x)) this.bbox.ur.x = point2.x;
if (point2.y.isGreaterThan(this.bbox.ur.y)) this.bbox.ur.y = point2.y;
prevPoint = point2;
}
if (!firstPoint.x.eq(prevPoint.x) || !firstPoint.y.eq(prevPoint.y)) {
this.segments.push(Segment2.fromRing(prevPoint, firstPoint, this));
}
}
getSweepEvents() {
const sweepEvents = [];
for (let i = 0, iMax = this.segments.length; i < iMax; i++) {
const segment = this.segments[i];
sweepEvents.push(segment.leftSE);
sweepEvents.push(segment.rightSE);
}
return sweepEvents;
}
};
var PolyIn = class {
constructor(geomPoly, multiPoly) {
__publicField(this, "multiPoly");
__publicField(this, "exteriorRing");
__publicField(this, "interiorRings");
__publicField(this, "bbox");
if (!Array.isArray(geomPoly)) {
throw new Error("Input geometry is not a valid Polygon or MultiPolygon");
}
this.exteriorRing = new RingIn(geomPoly[0], this, true);
this.bbox = {
ll: { x: this.exteriorRing.bbox.ll.x, y: this.exteriorRing.bbox.ll.y },
ur: { x: this.exteriorRing.bbox.ur.x, y: this.exteriorRing.bbox.ur.y }
};
this.interiorRings = [];
for (let i = 1, iMax = geomPoly.length; i < iMax; i++) {
const ring = new RingIn(geomPoly[i], this, false);
if (ring.bbox.ll.x.isLessThan(this.bbox.ll.x)) this.bbox.ll.x = ring.bbox.ll.x;
if (ring.bbox.ll.y.isLessThan(this.bbox.ll.y)) this.bbox.ll.y = ring.bbox.ll.y;
if (ring.bbox.ur.x.isGreaterThan(this.bbox.ur.x)) this.bbox.ur.x = ring.bbox.ur.x;
if (ring.bbox.ur.y.isGreaterThan(this.bbox.ur.y)) this.bbox.ur.y = ring.bbox.ur.y;
this.interiorRings.push(ring);
}
this.multiPoly = multiPoly;
}
getSweepEvents() {
const sweepEvents = this.exteriorRing.getSweepEvents();
for (let i = 0, iMax = this.interiorRings.length; i < iMax; i++) {
const ringSweepEvents = this.interiorRings[i].getSweepEvents();
for (let j = 0, jMax = ringSweepEvents.length; j < jMax; j++) {
sweepEvents.push(ringSweepEvents[j]);
}
}
return sweepEvents;
}
};
var MultiPolyIn = class {
constructor(geom, isSubject) {
__publicField(this, "isSubject");
__publicField(this, "polys");
__publicField(this, "bbox");
if (!Array.isArray(geom)) {
throw new Error("Input geometry is not a valid Polygon or MultiPolygon");
}
try {
if (typeof geom[0][0][0] === "number") geom = [geom];
} catch (ex) {
}
this.polys = [];
this.bbox = {
ll: { x: new BigNumber(Number.POSITIVE_INFINITY), y: new BigNumber(Number.POSITIVE_INFINITY) },
ur: { x: new BigNumber(Number.NEGATIVE_INFINITY), y: new BigNumber(Number.NEGATIVE_INFINITY) }
};
for (let i = 0, iMax = geom.length; i < iMax; i++) {
const poly = new PolyIn(geom[i], this);
if (poly.bbox.ll.x.isLessThan(this.bbox.ll.x)) this.bbox.ll.x = poly.bbox.ll.x;
if (poly.bbox.ll.y.isLessThan(this.bbox.ll.y)) this.bbox.ll.y = poly.bbox.ll.y;
if (poly.bbox.ur.x.isGreaterThan(this.bbox.ur.x)) this.bbox.ur.x = poly.bbox.ur.x;
if (poly.bbox.ur.y.isGreaterThan(this.bbox.ur.y)) this.bbox.ur.y = poly.bbox.ur.y;
this.polys.push(poly);
}
this.isSubject = isSubject;
}
getSweepEvents() {
const sweepEvents = [];
for (let i = 0, iMax = this.polys.length; i < iMax; i++) {
const polySweepEvents = this.polys[i].getSweepEvents();
for (let j = 0, jMax = polySweepEvents.length; j < jMax; j++) {
sweepEvents.push(polySweepEvents[j]);
}
}
return sweepEvents;
}
};
var union = (geom, ...moreGeoms) => operation_default.run("union", geom, moreGeoms);
var intersection2 = (geom, ...moreGeoms) => operation_default.run("intersection", geom, moreGeoms);
var difference = (geom, ...moreGeoms) => operation_default.run("difference", geom, moreGeoms);
precision.set;
function geomEach$8(geojson, callback) {
var i, j, g, geometry, stopG, geometryMaybeCollection, isGeometryCollection, featureProperties, featureBBox, featureId2, featureIndex = 0, isFeatureCollection = geojson.type === "FeatureCollection", isFeature = geojson.type === "Feature", stop = isFeatureCollection ? geojson.features.length : 1;
for (i = 0; i < stop; i++) {
geometryMaybeCollection = isFeatureCollection ? (
// @ts-expect-error: Known type conflict
geojson.features[i].geometry
) : isFeature ? (
// @ts-expect-error: Known type conflict
geojson.geometry
) : geojson;
featureProperties = isFeatureCollection ? (
// @ts-expect-error: Known type conflict
geojson.features[i].properties
) : isFeature ? (
// @ts-expect-error: Known type conflict
geojson.properties
) : {};
featureBBox = isFeatureCollection ? (
// @ts-expect-error: Known type conflict
geojson.features[i].bbox
) : isFeature ? (
// @ts-expect-error: Known type conflict
geojson.bbox
) : void 0;
featureId2 = isFeatureCollection ? (
// @ts-expect-error: Known type conflict
geojson.features[i].id
) : isFeature ? (
// @ts-expect-error: Known type conflict
geojson.id
) : void 0;
isGeometryCollection = geometryMaybeCollection ? geometryMaybeCollection.type === "GeometryCollection" : false;
stopG = isGeometryCollection ? geometryMaybeCollection.geometries.length : 1;
for (g = 0; g < stopG; g++) {
geometry = isGeometryCollection ? geometryMaybeCollection.geometries[g] : geometryMaybeCollection;
if (geometry === null) {
if (
// @ts-expect-error: Known type conflict
callback(
// @ts-expect-error: Known type conflict
null,
featureIndex,
featureProperties,
featureBBox,
featureId2
) === false
)
return false;
continue;
}
switch (geometry.type) {
case "Point":
case "LineString":
case "MultiPoint":
case "Polygon":
case "MultiLineString":
case "MultiPolygon": {
if (
// @ts-expect-error: Known type conflict
callback(
geometry,
featureIndex,
featureProperties,
featureBBox,
featureId2
) === false
)
return false;
break;
}
case "GeometryCollection": {
for (j = 0; j < geometry.geometries.length; j++) {
if (
// @ts-expect-error: Known type conflict
callback(
geometry.geometries[j],
featureIndex,
featureProperties,
featureBBox,
featureId2
) === false
)
return false;
}
break;
}
default:
throw new Error("Unknown Geometry Type");
}
}
featureIndex++;
}
}
function union2(features, options = {}) {
const geoms = [];
geomEach$8(features, (geom) => {
geoms.push(geom.coordinates);
});
if (geoms.length < 2) {
throw new Error("Must have at least 2 geometries");
}
const unioned = union(geoms[0], ...geoms.slice(1));
if (unioned.length === 0) return null;
if (unioned.length === 1) return polygon$1(unioned[0], options.properties);
else return multiPolygon$1(unioned, options.properties);
}
var index_default$c = union2;
function feature(geom, properties, options = {}) {
const feat = { type: "Feature" };
if (options.id === 0 || options.id) {
feat.id = options.id;
}
if (options.bbox) {
feat.bbox = options.bbox;
}
feat.properties = properties || {};
feat.geometry = geom;
return feat;
}
function polygon(coordinates, properties, options = {}) {
for (const ring of coordinates) {
if (ring.length < 4) {
throw new Error(
"Each LinearRing of a Polygon must have 4 or more Positions."
);
}
if (ring[ring.length - 1].length !== ring[0].length) {
throw new Error("First and last Position are not equivalent.");
}
for (let j = 0; j < ring[ring.length - 1].length; j++) {
if (ring[ring.length - 1][j] !== ring[0][j]) {
throw new Error("First and last Position are not equivalent.");
}
}
}
const geom = {
type: "Polygon",
coordinates
};
return feature(geom, properties, options);
}
function multiPolygon(coordinates, properties, options = {}) {
const geom = {
type: "MultiPolygon",
coordinates
};
return feature(geom, properties, options);
}
function featureEach$5(geojson, callback) {
if (geojson.type === "Feature") {
callback(geojson, 0);
} else if (geojson.type === "FeatureCollection") {
for (var i = 0; i < geojson.features.length; i++) {
if (callback(geojson.features[i], i) === false) break;
}
}
}
function geomEach$7(geojson, callback) {
var i, j, g, geometry, stopG, geometryMaybeCollection, isGeometryCollection, featureProperties, featureBBox, featureId2, featureIndex = 0, isFeatureCollection = geojson.type === "FeatureCollection", isFeature = geojson.type === "Feature", stop = isFeatureCollection ? geojson.features.length : 1;
for (i = 0; i < stop; i++) {
geometryMaybeCollection = isFeatureCollection ? (
// @ts-expect-error: Known type conflict
geojson.features[i].geometry
) : isFeature ? (
// @ts-expect-error: Known type conflict
geojson.geometry
) : geojson;
featureProperties = isFeatureCollection ? (
// @ts-expect-error: Known type conflict
geojson.features[i].properties
) : isFeature ? (
// @ts-expect-error: Known type conflict
geojson.properties
) : {};
featureBBox = isFeatureCollection ? (
// @ts-expect-error: Known type conflict
geojson.features[i].bbox
) : isFeature ? (
// @ts-expect-error: Known type conflict
geojson.bbox
) : void 0;
featureId2 = isFeatureCollection ? (
// @ts-expect-error: Known type conflict
geojson.features[i].id
) : isFeature ? (
// @ts-expect-error: Known type conflict
geojson.id
) : void 0;
isGeometryCollection = geometryMaybeCollection ? geometryMaybeCollection.type === "GeometryCollection" : false;
stopG = isGeometryCollection ? geometryMaybeCollection.geometries.length : 1;
for (g = 0; g < stopG; g++) {
geometry = isGeometryCollection ? geometryMaybeCollection.geometries[g] : geometryMaybeCollection;
if (geometry === null) {
if (
// @ts-expect-error: Known type conflict
callback(
// @ts-expect-error: Known type conflict
null,
featureIndex,
featureProperties,
featureBBox,
featureId2
) === false
)
return false;
continue;
}
switch (geometry.type) {
case "Point":
case "LineString":
case "MultiPoint":
case "Polygon":
case "MultiLineString":
case "MultiPolygon": {
if (
// @ts-expect-error: Known type conflict
callback(
geometry,
featureIndex,
featureProperties,
featureBBox,
featureId2
) === false
)
return false;
break;
}
case "GeometryCollection": {
for (j = 0; j < geometry.geometries.length; j++) {
if (
// @ts-expect-error: Known type conflict
callback(
geometry.geometries[j],
featureIndex,
featureProperties,
featureBBox,
featureId2
) === false
)
return false;
}
break;
}
default:
throw new Error("Unknown Geometry Type");
}
}
featureIndex++;
}
}
function intersect$1(features, options = {}) {
const geoms = [];
geomEach$7(features, (geom) => {
geoms.push(geom.coordinates);
});
if (geoms.length < 2) {
throw new Error("Must specify at least 2 geometries");
}
const intersection2$1 = intersection2(geoms[0], ...geoms.slice(1));
if (intersection2$1.length === 0) return null;
if (intersection2$1.length === 1)
return polygon(intersection2$1[0], options.properties);
return multiPolygon(intersection2$1, options.properties);
}
var index_default$b = intersect$1;
function geomEach$6(geojson, callback) {
var i, j, g, geometry, stopG, geometryMaybeCollection, isGeometryCollection, featureProperties, featureBBox, featureId2, featureIndex = 0, isFeatureCollection = geojson.type === "FeatureCollection", isFeature = geojson.type === "Feature", stop = isFeatureCollection ? geojson.features.length : 1;
for (i = 0; i < stop; i++) {
geometryMaybeCollection = isFeatureCollection ? (
// @ts-expect-error: Known type conflict
geojson.features[i].geometry
) : isFeature ? (
// @ts-expect-error: Known type conflict
geojson.geometry
) : geojson;
featureProperties = isFeatureCollection ? (
// @ts-expect-error: Known type conflict
geojson.features[i].properties
) : isFeature ? (
// @ts-expect-error: Known type conflict
geojson.properties
) : {};
featureBBox = isFeatureCollection ? (
// @ts-expect-error: Known type conflict
geojson.features[i].bbox
) : isFeature ? (
// @ts-expect-error: Known type conflict
geojson.bbox
) : void 0;
featureId2 = isFeatureCollection ? (
// @ts-expect-error: Known type conflict
geojson.features[i].id
) : isFeature ? (
// @ts-expect-error: Known type conflict
geojson.id
) : void 0;
isGeometryCollection = geometryMaybeCollection ? geometryMaybeCollection.type === "GeometryCollection" : false;
stopG = isGeometryCollection ? geometryMaybeCollection.geometries.length : 1;
for (g = 0; g < stopG; g++) {
geometry = isGeometryCollection ? geometryMaybeCollection.geometries[g] : geometryMaybeCollection;
if (geometry === null) {
if (
// @ts-expect-error: Known type conflict
callback(
// @ts-expect-error: Known type conflict
null,
featureIndex,
featureProperties,
featureBBox,
featureId2
) === false
)
return false;
continue;
}
switch (geometry.type) {
case "Point":
case "LineString":
case "MultiPoint":
case "Polygon":
case "MultiLineString":
case "MultiPolygon": {
if (
// @ts-expect-error: Known type conflict
callback(
geometry,
featureIndex,
featureProperties,
featureBBox,
featureId2
) === false
)
return false;
break;
}
case "GeometryCollection": {
for (j = 0; j < geometry.geometries.length; j++) {
if (
// @ts-expect-error: Known type conflict
callback(
geometry.geometries[j],
featureIndex,
featureProperties,
featureBBox,
featureId2
) === false
)
return false;
}
break;
}
default:
throw new Error("Unknown Geometry Type");
}
}
featureIndex++;
}
}
function difference2(features) {
const geoms = [];
geomEach$6(features, (geom) => {
geoms.push(geom.coordinates);
});
if (geoms.length < 2) {
throw new Error("Must have at least two features");
}
const properties = features.features[0].properties || {};
const differenced = difference(geoms[0], ...geoms.slice(1));
if (differenced.length === 0) return null;
if (differenced.length === 1) return polygon$1(differenced[0], properties);
return multiPolygon$1(differenced, properties);
}
var index_default$a = difference2;
function coordEach$3(geojson, callback, excludeWrapCoord) {
if (geojson === null) return;
var j, k, l, geometry, stopG, coords, geometryMaybeCollection, wrapShrink = 0, coordIndex = 0, isGeometryCollection, type = geojson.type, isFeatureCollection = type === "FeatureCollection", isFeature = type === "Feature", stop = isFeatureCollection ? geojson.features.length : 1;
for (var featureIndex = 0; featureIndex < stop; featureIndex++) {
geometryMaybeCollection = isFeatureCollection ? (
// @ts-expect-error: Known type conflict
geojson.features[featureIndex].geometry
) : isFeature ? (
// @ts-expect-error: Known type conflict
geojson.geometry
) : geojson;
isGeometryCollection = geometryMaybeCollection ? geometryMaybeCollection.type === "GeometryCollection" : false;
stopG = isGeometryCollection ? geometryMaybeCollection.geometries.length : 1;
for (var geomIndex = 0; geomIndex < stopG; geomIndex++) {
var multiFeatureIndex = 0;
var geometryIndex = 0;
geometry = isGeometryCollection ? geometryMaybeCollection.geometries[geomIndex] : geometryMaybeCollection;
if (geometry === null) continue;
coords = geometry.coordinates;
var geomType = geometry.type;
wrapShrink = 0;
switch (geomType) {
case null:
break;
case "Point":
if (
// @ts-expect-error: Known type conflict
callback(
coords,
coordIndex,
featureIndex,
multiFeatureIndex,
geometryIndex
) === false
)
return false;
coordIndex++;
multiFeatureIndex++;
break;
case "LineString":
case "MultiPoint":
for (j = 0; j < coords.length; j++) {
if (
// @ts-expect-error: Known type conflict
callback(
coords[j],
coordIndex,
featureIndex,
multiFeatureIndex,
geometryIndex
) === false
)
return false;
coordIndex++;
if (geomType === "MultiPoint") multiFeatureIndex++;
}
if (geomType === "LineString") multiFeatureIndex++;
break;
case "Polygon":
case "MultiLineString":
for (j = 0; j < coords.length; j++) {
for (k = 0; k < coords[j].length - wrapShrink; k++) {
if (
// @ts-expect-error: Known type conflict
callback(
coords[j][k],
coordIndex,
featureIndex,
multiFeatureIndex,
geometryIndex
) === false
)
return false;
coordIndex++;
}
if (geomType === "MultiLineString") multiFeatureIndex++;
if (geomType === "Polygon") geometryIndex++;
}
if (geomType === "Polygon") multiFeatureIndex++;
break;
case "MultiPolygon":
for (j = 0; j < coords.length; j++) {
geometryIndex = 0;
for (k = 0; k < coords[j].length; k++) {
for (l = 0; l < coords[j][k].length - wrapShrink; l++) {
if (
// @ts-expect-error: Known type conflict
callback(
coords[j][k][l],
coordIndex,
featureIndex,
multiFeatureIndex,
geometryIndex
) === false
)
return false;
coordIndex++;
}
geometryIndex++;
}
multiFeatureIndex++;
}
break;
case "GeometryCollection":
for (j = 0; j < geometry.geometries.length; j++)
if (
// @ts-expect-error: Known type conflict
coordEach$3(geometry.geometries[j], callback) === false
)
return false;
break;
default:
throw new Error("Unknown Geometry Type");
}
}
}
}
function featureEach$4(geojson, callback) {
if (geojson.type === "Feature") {
callback(geojson, 0);
} else if (geojson.type === "FeatureCollection") {
for (var i = 0; i < geojson.features.length; i++) {
if (callback(geojson.features[i], i) === false) break;
}
}
}
function explode$1(geojson) {
const points = [];
if (geojson.type === "FeatureCollection") {
featureEach$4(geojson, function(feature2) {
coordEach$3(feature2, function(coord) {
points.push(point(coord, feature2.properties));
});
});
} else if (geojson.type === "Feature") {
coordEach$3(geojson, function(coord) {
points.push(point(coord, geojson.properties));
});
} else {
coordEach$3(geojson, function(coord) {
points.push(point(coord));
});
}
return featureCollection(points);
}
var index_default$9 = explode$1;
function booleanPointOnLine(pt, line, options = {}) {
const ptCoords = getCoord(pt);
const lineCoords = getCoords(line);
for (let i = 0; i < lineCoords.length - 1; i++) {
let ignoreBoundary = false;
if (options.ignoreEndVertices) {
if (i === 0) {
ignoreBoundary = "start";
}
if (i === lineCoords.length - 2) {
ignoreBoundary = "end";
}
if (i === 0 && i + 1 === lineCoords.length - 1) {
ignoreBoundary = "both";
}
}
if (isPointOnLineSegment(
lineCoords[i],
lineCoords[i + 1],
ptCoords,
ignoreBoundary,
typeof options.epsilon === "undefined" ? null : options.epsilon
)) {
return true;
}
}
return false;
}
function isPointOnLineSegment(lineSegmentStart, lineSegmentEnd, pt, excludeBoundary, epsilon2) {
const x = pt[0];
const y = pt[1];
const x1 = lineSegmentStart[0];
const y1 = lineSegmentStart[1];
const x2 = lineSegmentEnd[0];
const y2 = lineSegmentEnd[1];
const dxc = pt[0] - x1;
const dyc = pt[1] - y1;
const dxl = x2 - x1;
const dyl = y2 - y1;
const cross2 = dxc * dyl - dyc * dxl;
if (epsilon2 !== null) {
if (Math.abs(cross2) > epsilon2) {
return false;
}
} else if (cross2 !== 0) {
return false;
}
if (Math.abs(dxl) === Math.abs(dyl) && Math.abs(dxl) === 0) {
if (excludeBoundary) {
return false;
}
if (pt[0] === lineSegmentStart[0] && pt[1] === lineSegmentStart[1]) {
return true;
} else {
return false;
}
}
if (!excludeBoundary) {
if (Math.abs(dxl) >= Math.abs(dyl)) {
return dxl > 0 ? x1 <= x && x <= x2 : x2 <= x && x <= x1;
}
return dyl > 0 ? y1 <= y && y <= y2 : y2 <= y && y <= y1;
} else if (excludeBoundary === "start") {
if (Math.abs(dxl) >= Math.abs(dyl)) {
return dxl > 0 ? x1 < x && x <= x2 : x2 <= x && x < x1;
}
return dyl > 0 ? y1 < y && y <= y2 : y2 <= y && y < y1;
} else if (excludeBoundary === "end") {
if (Math.abs(dxl) >= Math.abs(dyl)) {
return dxl > 0 ? x1 <= x && x < x2 : x2 < x && x <= x1;
}
return dyl > 0 ? y1 <= y && y < y2 : y2 < y && y <= y1;
} else if (excludeBoundary === "both") {
if (Math.abs(dxl) >= Math.abs(dyl)) {
return dxl > 0 ? x1 < x && x < x2 : x2 < x && x < x1;
}
return dyl > 0 ? y1 < y && y < y2 : y2 < y && y < y1;
}
return false;
}
function cleanCoords(geojson, options = {}) {
var mutate = typeof options === "object" ? options.mutate : options;
if (!geojson) throw new Error("geojson is required");
var type = getType(geojson);
var newCoords = [];
switch (type) {
case "LineString":
newCoords = cleanLine(geojson, type);
break;
case "MultiLineString":
case "Polygon":
getCoords(geojson).forEach(function(line) {
newCoords.push(cleanLine(line, type));
});
break;
case "MultiPolygon":
getCoords(geojson).forEach(function(polygons) {
var polyPoints = [];
polygons.forEach(function(ring) {
polyPoints.push(cleanLine(ring, type));
});
newCoords.push(polyPoints);
});
break;
case "Point":
return geojson;
case "MultiPoint":
var existing = {};
getCoords(geojson).forEach(function(coord) {
var key = coord.join("-");
if (!Object.prototype.hasOwnProperty.call(existing, key)) {
newCoords.push(coord);
existing[key] = true;
}
});
break;
default:
throw new Error(type + " geometry not supported");
}
if (geojson.coordinates) {
if (mutate === true) {
geojson.coordinates = newCoords;
return geojson;
}
return { type, coordinates: newCoords };
} else {
if (mutate === true) {
geojson.geometry.coordinates = newCoords;
return geojson;
}
return feature$1({ type, coordinates: newCoords }, geojson.properties, {
bbox: geojson.bbox,
id: geojson.id
});
}
}
function cleanLine(line, type) {
const points = getCoords(line);
if (points.length === 2 && !equals(points[0], points[1])) return points;
const newPoints = [];
let a = 0, b = 1, c = 2;
newPoints.push(points[a]);
while (c < points.length) {
if (booleanPointOnLine(points[b], lineString([points[a], points[c]]))) {
b = c;
} else {
newPoints.push(points[b]);
a = b;
b++;
c = b;
}
c++;
}
newPoints.push(points[b]);
if (type === "Polygon" || type === "MultiPolygon") {
if (booleanPointOnLine(
newPoints[0],
lineString([newPoints[1], newPoints[newPoints.length - 2]])
)) {
newPoints.shift();
newPoints.pop();
newPoints.push(newPoints[0]);
}
if (newPoints.length < 4) {
throw new Error("invalid polygon, fewer than 4 points");
}
if (!equals(newPoints[0], newPoints[newPoints.length - 1])) {
throw new Error("invalid polygon, first and last points not equal");
}
}
return newPoints;
}
function equals(pt1, pt2) {
return pt1[0] === pt2[0] && pt1[1] === pt2[1];
}
function clone(geojson) {
if (!geojson) {
throw new Error("geojson is required");
}
switch (geojson.type) {
case "Feature":
return cloneFeature(geojson);
case "FeatureCollection":
return cloneFeatureCollection(geojson);
case "Point":
case "LineString":
case "Polygon":
case "MultiPoint":
case "MultiLineString":
case "MultiPolygon":
case "GeometryCollection":
return cloneGeometry(geojson);
default:
throw new Error("unknown GeoJSON type");
}
}
function cloneFeature(geojson) {
const cloned = { type: "Feature" };
Object.keys(geojson).forEach((key) => {
switch (key) {
case "type":
case "properties":
case "geometry":
return;
default:
cloned[key] = geojson[key];
}
});
cloned.properties = cloneProperties(geojson.properties);
if (geojson.geometry == null) {
cloned.geometry = null;
} else {
cloned.geometry = cloneGeometry(geojson.geometry);
}
return cloned;
}
function cloneProperties(properties) {
const cloned = {};
if (!properties) {
return cloned;
}
Object.keys(properties).forEach((key) => {
const value = properties[key];
if (typeof value === "object") {
if (value === null) {
cloned[key] = null;
} else if (Array.isArray(value)) {
cloned[key] = value.map((item) => {
return item;
});
} else {
cloned[key] = cloneProperties(value);
}
} else {
cloned[key] = value;
}
});
return cloned;
}
function cloneFeatureCollection(geojson) {
const cloned = { type: "FeatureCollection" };
Object.keys(geojson).forEach((key) => {
switch (key) {
case "type":
case "features":
return;
default:
cloned[key] = geojson[key];
}
});
cloned.features = geojson.features.map((feature2) => {
return cloneFeature(feature2);
});
return cloned;
}
function cloneGeometry(geometry) {
const geom = { type: geometry.type };
if (geometry.bbox) {
geom.bbox = geometry.bbox;
}
if (geometry.type === "GeometryCollection") {
geom.geometries = geometry.geometries.map((g) => {
return cloneGeometry(g);
});
return geom;
}
geom.coordinates = deepSlice(geometry.coordinates);
return geom;
}
function deepSlice(coords) {
const cloned = coords;
if (typeof cloned[0] !== "object") {
return cloned.slice();
}
return cloned.map((coord) => {
return deepSlice(coord);
});
}
function geomEach$5(geojson, callback) {
var i, j, g, geometry, stopG, geometryMaybeCollection, isGeometryCollection, featureProperties, featureBBox, featureId2, featureIndex = 0, isFeatureCollection = geojson.type === "FeatureCollection", isFeature = geojson.type === "Feature", stop = isFeatureCollection ? geojson.features.length : 1;
for (i = 0; i < stop; i++) {
geometryMaybeCollection = isFeatureCollection ? (
// @ts-expect-error: Known type conflict
geojson.features[i].geometry
) : isFeature ? (
// @ts-expect-error: Known type conflict
geojson.geometry
) : geojson;
featureProperties = isFeatureCollection ? (
// @ts-expect-error: Known type conflict
geojson.features[i].properties
) : isFeature ? (
// @ts-expect-error: Known type conflict
geojson.properties
) : {};
featureBBox = isFeatureCollection ? (
// @ts-expect-error: Known type conflict
geojson.features[i].bbox
) : isFeature ? (
// @ts-expect-error: Known type conflict
geojson.bbox
) : void 0;
featureId2 = isFeatureCollection ? (
// @ts-expect-error: Known type conflict
geojson.features[i].id
) : isFeature ? (
// @ts-expect-error: Known type conflict
geojson.id
) : void 0;
isGeometryCollection = geometryMaybeCollection ? geometryMaybeCollection.type === "GeometryCollection" : false;
stopG = isGeometryCollection ? geometryMaybeCollection.geometries.length : 1;
for (g = 0; g < stopG; g++) {
geometry = isGeometryCollection ? geometryMaybeCollection.geometries[g] : geometryMaybeCollection;
if (geometry === null) {
if (
// @ts-expect-error: Known type conflict
callback(
// @ts-expect-error: Known type conflict
null,
featureIndex,
featureProperties,
featureBBox,
featureId2
) === false
)
return false;
continue;
}
switch (geometry.type) {
case "Point":
case "LineString":
case "MultiPoint":
case "Polygon":
case "MultiLineString":
case "MultiPolygon": {
if (
// @ts-expect-error: Known type conflict
callback(
geometry,
featureIndex,
featureProperties,
featureBBox,
featureId2
) === false
)
return false;
break;
}
case "GeometryCollection": {
for (j = 0; j < geometry.geometries.length; j++) {
if (
// @ts-expect-error: Known type conflict
callback(
geometry.geometries[j],
featureIndex,
featureProperties,
featureBBox,
featureId2
) === false
)
return false;
}
break;
}
default:
throw new Error("Unknown Geometry Type");
}
}
featureIndex++;
}
}
function getSqDist$1(p1, p2) {
var dx = p1[0] - p2[0], dy = p1[1] - p2[1];
return dx * dx + dy * dy;
}
function getSqSegDist(p, p1, p2) {
var x = p1[0], y = p1[1], dx = p2[0] - x, dy = p2[1] - y;
if (dx !== 0 || dy !== 0) {
var t = ((p[0] - x) * dx + (p[1] - y) * dy) / (dx * dx + dy * dy);
if (t > 1) {
x = p2[0];
y = p2[1];
} else if (t > 0) {
x += dx * t;
y += dy * t;
}
}
dx = p[0] - x;
dy = p[1] - y;
return dx * dx + dy * dy;
}
function simplifyRadialDist(points, sqTolerance) {
var prevPoint = points[0], newPoints = [prevPoint], point2;
for (var i = 1, len = points.length; i < len; i++) {
point2 = points[i];
if (getSqDist$1(point2, prevPoint) > sqTolerance) {
newPoints.push(point2);
prevPoint = point2;
}
}
if (prevPoint !== point2) newPoints.push(point2);
return newPoints;
}
function simplifyDPStep(points, first, last, sqTolerance, simplified) {
var maxSqDist = sqTolerance, index;
for (var i = first + 1; i < last; i++) {
var sqDist = getSqSegDist(points[i], points[first], points[last]);
if (sqDist > maxSqDist) {
index = i;
maxSqDist = sqDist;
}
}
if (maxSqDist > sqTolerance) {
if (index - first > 1)
simplifyDPStep(points, first, index, sqTolerance, simplified);
simplified.push(points[index]);
if (last - index > 1)
simplifyDPStep(points, index, last, sqTolerance, simplified);
}
}
function simplifyDouglasPeucker(points, sqTolerance) {
var last = points.length - 1;
var simplified = [points[0]];
simplifyDPStep(points, 0, last, sqTolerance, simplified);
simplified.push(points[last]);
return simplified;
}
function simplify(points, tolerance, highestQuality) {
if (points.length <= 2) return points;
var sqTolerance = tolerance !== void 0 ? tolerance * tolerance : 1;
points = highestQuality ? points : simplifyRadialDist(points, sqTolerance);
points = simplifyDouglasPeucker(points, sqTolerance);
return points;
}
function simplify2(geojson, options = {}) {
var _a2, _b2, _c;
options = options != null ? options : {};
if (!isObject$1(options)) throw new Error("options is invalid");
const tolerance = (_a2 = options.tolerance) != null ? _a2 : 1;
const highQuality = (_b2 = options.highQuality) != null ? _b2 : false;
const mutate = (_c = options.mutate) != null ? _c : false;
if (!geojson) throw new Error("geojson is required");
if (tolerance && tolerance < 0) throw new Error("invalid tolerance");
if (mutate !== true) geojson = clone(geojson);
geomEach$5(geojson, function(geom) {
simplifyGeom(geom, tolerance, highQuality);
});
return geojson;
}
function simplifyGeom(geometry, tolerance, highQuality) {
const type = geometry.type;
if (type === "Point" || type === "MultiPoint") return geometry;
cleanCoords(geometry, { mutate: true });
if (type !== "GeometryCollection") {
switch (type) {
case "LineString":
geometry.coordinates = simplify(
geometry.coordinates,
tolerance,
highQuality
);
break;
case "MultiLineString":
geometry.coordinates = geometry.coordinates.map(
(lines) => simplify(lines, tolerance, highQuality)
);
break;
case "Polygon":
geometry.coordinates = simplifyPolygon(
geometry.coordinates,
tolerance,
highQuality
);
break;
case "MultiPolygon":
geometry.coordinates = geometry.coordinates.map(
(rings) => simplifyPolygon(rings, tolerance, highQuality)
);
}
}
return geometry;
}
function simplifyPolygon(coordinates, tolerance, highQuality) {
return coordinates.map(function(ring) {
if (ring.length < 4) {
throw new Error("invalid polygon");
}
let ringTolerance = tolerance;
let simpleRing = simplify(ring, ringTolerance, highQuality);
while (!checkValidity(simpleRing) && ringTolerance >= Number.EPSILON) {
ringTolerance -= ringTolerance * 0.01;
simpleRing = simplify(ring, ringTolerance, highQuality);
}
if (!checkValidity(simpleRing)) {
return ring;
}
if (simpleRing[simpleRing.length - 1][0] !== simpleRing[0][0] || simpleRing[simpleRing.length - 1][1] !== simpleRing[0][1]) {
simpleRing.push(simpleRing[0]);
}
return simpleRing;
});
}
function checkValidity(ring) {
if (ring.length < 3) return false;
return !(ring.length === 3 && ring[2][0] === ring[0][0] && ring[2][1] === ring[0][1]);
}
var index_default$8 = simplify2;
function kinks(featureIn) {
let coordinates;
let feature2;
const results = {
type: "FeatureCollection",
features: []
};
if (featureIn.type === "Feature") {
feature2 = featureIn.geometry;
} else {
feature2 = featureIn;
}
if (feature2.type === "LineString") {
coordinates = [feature2.coordinates];
} else if (feature2.type === "MultiLineString") {
coordinates = feature2.coordinates;
} else if (feature2.type === "MultiPolygon") {
coordinates = [].concat(...feature2.coordinates);
} else if (feature2.type === "Polygon") {
coordinates = feature2.coordinates;
} else {
throw new Error(
"Input must be a LineString, MultiLineString, Polygon, or MultiPolygon Feature or Geometry"
);
}
coordinates.forEach((line1) => {
coordinates.forEach((line2) => {
for (let i = 0; i < line1.length - 1; i++) {
for (let k = i; k < line2.length - 1; k++) {
if (line1 === line2) {
if (Math.abs(i - k) === 1) {
continue;
}
if (
// segments are first and last segment of lineString
i === 0 && k === line1.length - 2 && // lineString is closed
line1[i][0] === line1[line1.length - 1][0] && line1[i][1] === line1[line1.length - 1][1]
) {
continue;
}
}
const intersection3 = lineIntersects(
line1[i][0],
line1[i][1],
line1[i + 1][0],
line1[i + 1][1],
line2[k][0],
line2[k][1],
line2[k + 1][0],
line2[k + 1][1]
);
if (intersection3) {
results.features.push(point([intersection3[0], intersection3[1]]));
}
}
}
});
});
return results;
}
function lineIntersects(line1StartX, line1StartY, line1EndX, line1EndY, line2StartX, line2StartY, line2EndX, line2EndY) {
let denominator;
let a;
let b;
let numerator1;
let numerator2;
const result = {
x: null,
y: null,
onLine1: false,
onLine2: false
};
denominator = (line2EndY - line2StartY) * (line1EndX - line1StartX) - (line2EndX - line2StartX) * (line1EndY - line1StartY);
if (denominator === 0) {
if (result.x !== null && result.y !== null) {
return result;
} else {
return false;
}
}
a = line1StartY - line2StartY;
b = line1StartX - line2StartX;
numerator1 = (line2EndX - line2StartX) * a - (line2EndY - line2StartY) * b;
numerator2 = (line1EndX - line1StartX) * a - (line1EndY - line1StartY) * b;
a = numerator1 / denominator;
b = numerator2 / denominator;
result.x = line1StartX + a * (line1EndX - line1StartX);
result.y = line1StartY + a * (line1EndY - line1StartY);
if (a >= 0 && a <= 1) {
result.onLine1 = true;
}
if (b >= 0 && b <= 1) {
result.onLine2 = true;
}
if (result.onLine1 && result.onLine2) {
return [result.x, result.y];
} else {
return false;
}
}
var index_default$7 = kinks;
function featureEach$3(geojson, callback) {
if (geojson.type === "Feature") {
callback(geojson, 0);
} else if (geojson.type === "FeatureCollection") {
for (var i = 0; i < geojson.features.length; i++) {
if (callback(geojson.features[i], i) === false) break;
}
}
}
function geomEach$4(geojson, callback) {
var i, j, g, geometry, stopG, geometryMaybeCollection, isGeometryCollection, featureProperties, featureBBox, featureId2, featureIndex = 0, isFeatureCollection = geojson.type === "FeatureCollection", isFeature = geojson.type === "Feature", stop = isFeatureCollection ? geojson.features.length : 1;
for (i = 0; i < stop; i++) {
geometryMaybeCollection = isFeatureCollection ? (
// @ts-expect-error: Known type conflict
geojson.features[i].geometry
) : isFeature ? (
// @ts-expect-error: Known type conflict
geojson.geometry
) : geojson;
featureProperties = isFeatureCollection ? (
// @ts-expect-error: Known type conflict
geojson.features[i].properties
) : isFeature ? (
// @ts-expect-error: Known type conflict
geojson.properties
) : {};
featureBBox = isFeatureCollection ? (
// @ts-expect-error: Known type conflict
geojson.features[i].bbox
) : isFeature ? (
// @ts-expect-error: Known type conflict
geojson.bbox
) : void 0;
featureId2 = isFeatureCollection ? (
// @ts-expect-error: Known type conflict
geojson.features[i].id
) : isFeature ? (
// @ts-expect-error: Known type conflict
geojson.id
) : void 0;
isGeometryCollection = geometryMaybeCollection ? geometryMaybeCollection.type === "GeometryCollection" : false;
stopG = isGeometryCollection ? geometryMaybeCollection.geometries.length : 1;
for (g = 0; g < stopG; g++) {
geometry = isGeometryCollection ? geometryMaybeCollection.geometries[g] : geometryMaybeCollection;
if (geometry === null) {
if (
// @ts-expect-error: Known type conflict
callback(
// @ts-expect-error: Known type conflict
null,
featureIndex,
featureProperties,
featureBBox,
featureId2
) === false
)
return false;
continue;
}
switch (geometry.type) {
case "Point":
case "LineString":
case "MultiPoint":
case "Polygon":
case "MultiLineString":
case "MultiPolygon": {
if (
// @ts-expect-error: Known type conflict
callback(
geometry,
featureIndex,
featureProperties,
featureBBox,
featureId2
) === false
)
return false;
break;
}
case "GeometryCollection": {
for (j = 0; j < geometry.geometries.length; j++) {
if (
// @ts-expect-error: Known type conflict
callback(
geometry.geometries[j],
featureIndex,
featureProperties,
featureBBox,
featureId2
) === false
)
return false;
}
break;
}
default:
throw new Error("Unknown Geometry Type");
}
}
featureIndex++;
}
}
function flattenEach$4(geojson, callback) {
geomEach$4(geojson, function(geometry, featureIndex, properties, bbox2, id) {
var type = geometry === null ? null : geometry.type;
switch (type) {
case null:
case "Point":
case "LineString":
case "Polygon":
if (
// @ts-expect-error: Known type conflict
callback(
feature$1(geometry, properties, { bbox: bbox2, id }),
featureIndex,
0
) === false
)
return false;
return;
}
var geomType;
switch (type) {
case "MultiPoint":
geomType = "Point";
break;
case "MultiLineString":
geomType = "LineString";
break;
case "MultiPolygon":
geomType = "Polygon";
break;
}
for (
var multiFeatureIndex = 0;
// @ts-expect-error: Known type conflict
multiFeatureIndex < geometry.coordinates.length;
multiFeatureIndex++
) {
var coordinate = geometry.coordinates[multiFeatureIndex];
var geom = {
type: geomType,
coordinates: coordinate
};
if (
// @ts-expect-error: Known type conflict
callback(feature$1(geom, properties), featureIndex, multiFeatureIndex) === false
)
return false;
}
});
}
function quickselect$1(arr, k, left, right, compare2) {
quickselectStep(arr, k, left || 0, right || arr.length - 1, compare2 || defaultCompare$1);
}
function quickselectStep(arr, k, left, right, compare2) {
while (right > left) {
if (right - left > 600) {
var n = right - left + 1;
var m = k - left + 1;
var z = Math.log(n);
var s = 0.5 * Math.exp(2 * z / 3);
var sd = 0.5 * Math.sqrt(z * s * (n - s) / n) * (m - n / 2 < 0 ? -1 : 1);
var newLeft = Math.max(left, Math.floor(k - m * s / n + sd));
var newRight = Math.min(right, Math.floor(k + (n - m) * s / n + sd));
quickselectStep(arr, k, newLeft, newRight, compare2);
}
var t = arr[k];
var i = left;
var j = right;
swap$1(arr, left, k);
if (compare2(arr[right], t) > 0) swap$1(arr, left, right);
while (i < j) {
swap$1(arr, i, j);
i++;
j--;
while (compare2(arr[i], t) < 0) i++;
while (compare2(arr[j], t) > 0) j--;
}
if (compare2(arr[left], t) === 0) swap$1(arr, left, j);
else {
j++;
swap$1(arr, j, right);
}
if (j <= k) left = j + 1;
if (k <= j) right = j - 1;
}
}
function swap$1(arr, i, j) {
var tmp = arr[i];
arr[i] = arr[j];
arr[j] = tmp;
}
function defaultCompare$1(a, b) {
return a < b ? -1 : a > b ? 1 : 0;
}
let RBush$2 = class RBush {
constructor(maxEntries = 9) {
this._maxEntries = Math.max(4, maxEntries);
this._minEntries = Math.max(2, Math.ceil(this._maxEntries * 0.4));
this.clear();
}
all() {
return this._all(this.data, []);
}
search(bbox2) {
let node = this.data;
const result = [];
if (!intersects$2(bbox2, node)) return result;
const toBBox2 = this.toBBox;
const nodesToSearch = [];
while (node) {
for (let i = 0; i < node.children.length; i++) {
const child = node.children[i];
const childBBox = node.leaf ? toBBox2(child) : child;
if (intersects$2(bbox2, childBBox)) {
if (node.leaf) result.push(child);
else if (contains$1(bbox2, childBBox)) this._all(child, result);
else nodesToSearch.push(child);
}
}
node = nodesToSearch.pop();
}
return result;
}
collides(bbox2) {
let node = this.data;
if (!intersects$2(bbox2, node)) return false;
const nodesToSearch = [];
while (node) {
for (let i = 0; i < node.children.length; i++) {
const child = node.children[i];
const childBBox = node.leaf ? this.toBBox(child) : child;
if (intersects$2(bbox2, childBBox)) {
if (node.leaf || contains$1(bbox2, childBBox)) return true;
nodesToSearch.push(child);
}
}
node = nodesToSearch.pop();
}
return false;
}
load(data) {
if (!(data && data.length)) return this;
if (data.length < this._minEntries) {
for (let i = 0; i < data.length; i++) {
this.insert(data[i]);
}
return this;
}
let node = this._build(data.slice(), 0, data.length - 1, 0);
if (!this.data.children.length) {
this.data = node;
} else if (this.data.height === node.height) {
this._splitRoot(this.data, node);
} else {
if (this.data.height < node.height) {
const tmpNode = this.data;
this.data = node;
node = tmpNode;
}
this._insert(node, this.data.height - node.height - 1, true);
}
return this;
}
insert(item) {
if (item) this._insert(item, this.data.height - 1);
return this;
}
clear() {
this.data = createNode$1([]);
return this;
}
remove(item, equalsFn) {
if (!item) return this;
let node = this.data;
const bbox2 = this.toBBox(item);
const path = [];
const indexes = [];
let i, parent, goingUp;
while (node || path.length) {
if (!node) {
node = path.pop();
parent = path[path.length - 1];
i = indexes.pop();
goingUp = true;
}
if (node.leaf) {
const index = findItem$1(item, node.children, equalsFn);
if (index !== -1) {
node.children.splice(index, 1);
path.push(node);
this._condense(path);
return this;
}
}
if (!goingUp && !node.leaf && contains$1(node, bbox2)) {
path.push(node);
indexes.push(i);
i = 0;
parent = node;
node = node.children[0];
} else if (parent) {
i++;
node = parent.children[i];
goingUp = false;
} else node = null;
}
return this;
}
toBBox(item) {
return item;
}
compareMinX(a, b) {
return a.minX - b.minX;
}
compareMinY(a, b) {
return a.minY - b.minY;
}
toJSON() {
return this.data;
}
fromJSON(data) {
this.data = data;
return this;
}
_all(node, result) {
const nodesToSearch = [];
while (node) {
if (node.leaf) result.push(...node.children);
else nodesToSearch.push(...node.children);
node = nodesToSearch.pop();
}
return result;
}
_build(items, left, right, height) {
const N = right - left + 1;
let M = this._maxEntries;
let node;
if (N <= M) {
node = createNode$1(items.slice(left, right + 1));
calcBBox$1(node, this.toBBox);
return node;
}
if (!height) {
height = Math.ceil(Math.log(N) / Math.log(M));
M = Math.ceil(N / Math.pow(M, height - 1));
}
node = createNode$1([]);
node.leaf = false;
node.height = height;
const N2 = Math.ceil(N / M);
const N1 = N2 * Math.ceil(Math.sqrt(M));
multiSelect$1(items, left, right, N1, this.compareMinX);
for (let i = left; i <= right; i += N1) {
const right2 = Math.min(i + N1 - 1, right);
multiSelect$1(items, i, right2, N2, this.compareMinY);
for (let j = i; j <= right2; j += N2) {
const right3 = Math.min(j + N2 - 1, right2);
node.children.push(this._build(items, j, right3, height - 1));
}
}
calcBBox$1(node, this.toBBox);
return node;
}
_chooseSubtree(bbox2, node, level, path) {
while (true) {
path.push(node);
if (node.leaf || path.length - 1 === level) break;
let minArea = Infinity;
let minEnlargement = Infinity;
let targetNode;
for (let i = 0; i < node.children.length; i++) {
const child = node.children[i];
const area2 = bboxArea$1(child);
const enlargement = enlargedArea$1(bbox2, child) - area2;
if (enlargement < minEnlargement) {
minEnlargement = enlargement;
minArea = area2 < minArea ? area2 : minArea;
targetNode = child;
} else if (enlargement === minEnlargement) {
if (area2 < minArea) {
minArea = area2;
targetNode = child;
}
}
}
node = targetNode || node.children[0];
}
return node;
}
_insert(item, level, isNode) {
const bbox2 = isNode ? item : this.toBBox(item);
const insertPath = [];
const node = this._chooseSubtree(bbox2, this.data, level, insertPath);
node.children.push(item);
extend$1(node, bbox2);
while (level >= 0) {
if (insertPath[level].children.length > this._maxEntries) {
this._split(insertPath, level);
level--;
} else break;
}
this._adjustParentBBoxes(bbox2, insertPath, level);
}
// split overflowed node into two
_split(insertPath, level) {
const node = insertPath[level];
const M = node.children.length;
const m = this._minEntries;
this._chooseSplitAxis(node, m, M);
const splitIndex = this._chooseSplitIndex(node, m, M);
const newNode = createNode$1(node.children.splice(splitIndex, node.children.length - splitIndex));
newNode.height = node.height;
newNode.leaf = node.leaf;
calcBBox$1(node, this.toBBox);
calcBBox$1(newNode, this.toBBox);
if (level) insertPath[level - 1].children.push(newNode);
else this._splitRoot(node, newNode);
}
_splitRoot(node, newNode) {
this.data = createNode$1([node, newNode]);
this.data.height = node.height + 1;
this.data.leaf = false;
calcBBox$1(this.data, this.toBBox);
}
_chooseSplitIndex(node, m, M) {
let index;
let minOverlap = Infinity;
let minArea = Infinity;
for (let i = m; i <= M - m; i++) {
const bbox1 = distBBox$1(node, 0, i, this.toBBox);
const bbox2 = distBBox$1(node, i, M, this.toBBox);
const overlap = intersectionArea$1(bbox1, bbox2);
const area2 = bboxArea$1(bbox1) + bboxArea$1(bbox2);
if (overlap < minOverlap) {
minOverlap = overlap;
index = i;
minArea = area2 < minArea ? area2 : minArea;
} else if (overlap === minOverlap) {
if (area2 < minArea) {
minArea = area2;
index = i;
}
}
}
return index || M - m;
}
// sorts node children by the best axis for split
_chooseSplitAxis(node, m, M) {
const compareMinX = node.leaf ? this.compareMinX : compareNodeMinX$1;
const compareMinY = node.leaf ? this.compareMinY : compareNodeMinY$1;
const xMargin = this._allDistMargin(node, m, M, compareMinX);
const yMargin = this._allDistMargin(node, m, M, compareMinY);
if (xMargin < yMargin) node.children.sort(compareMinX);
}
// total margin of all possible split distributions where each node is at least m full
_allDistMargin(node, m, M, compare2) {
node.children.sort(compare2);
const toBBox2 = this.toBBox;
const leftBBox = distBBox$1(node, 0, m, toBBox2);
const rightBBox = distBBox$1(node, M - m, M, toBBox2);
let margin = bboxMargin$1(leftBBox) + bboxMargin$1(rightBBox);
for (let i = m; i < M - m; i++) {
const child = node.children[i];
extend$1(leftBBox, node.leaf ? toBBox2(child) : child);
margin += bboxMargin$1(leftBBox);
}
for (let i = M - m - 1; i >= m; i--) {
const child = node.children[i];
extend$1(rightBBox, node.leaf ? toBBox2(child) : child);
margin += bboxMargin$1(rightBBox);
}
return margin;
}
_adjustParentBBoxes(bbox2, path, level) {
for (let i = level; i >= 0; i--) {
extend$1(path[i], bbox2);
}
}
_condense(path) {
for (let i = path.length - 1, siblings; i >= 0; i--) {
if (path[i].children.length === 0) {
if (i > 0) {
siblings = path[i - 1].children;
siblings.splice(siblings.indexOf(path[i]), 1);
} else this.clear();
} else calcBBox$1(path[i], this.toBBox);
}
}
};
function findItem$1(item, items, equalsFn) {
if (!equalsFn) return items.indexOf(item);
for (let i = 0; i < items.length; i++) {
if (equalsFn(item, items[i])) return i;
}
return -1;
}
function calcBBox$1(node, toBBox2) {
distBBox$1(node, 0, node.children.length, toBBox2, node);
}
function distBBox$1(node, k, p, toBBox2, destNode) {
if (!destNode) destNode = createNode$1(null);
destNode.minX = Infinity;
destNode.minY = Infinity;
destNode.maxX = -Infinity;
destNode.maxY = -Infinity;
for (let i = k; i < p; i++) {
const child = node.children[i];
extend$1(destNode, node.leaf ? toBBox2(child) : child);
}
return destNode;
}
function extend$1(a, b) {
a.minX = Math.min(a.minX, b.minX);
a.minY = Math.min(a.minY, b.minY);
a.maxX = Math.max(a.maxX, b.maxX);
a.maxY = Math.max(a.maxY, b.maxY);
return a;
}
function compareNodeMinX$1(a, b) {
return a.minX - b.minX;
}
function compareNodeMinY$1(a, b) {
return a.minY - b.minY;
}
function bboxArea$1(a) {
return (a.maxX - a.minX) * (a.maxY - a.minY);
}
function bboxMargin$1(a) {
return a.maxX - a.minX + (a.maxY - a.minY);
}
function enlargedArea$1(a, b) {
return (Math.max(b.maxX, a.maxX) - Math.min(b.minX, a.minX)) * (Math.max(b.maxY, a.maxY) - Math.min(b.minY, a.minY));
}
function intersectionArea$1(a, b) {
const minX = Math.max(a.minX, b.minX);
const minY = Math.max(a.minY, b.minY);
const maxX = Math.min(a.maxX, b.maxX);
const maxY = Math.min(a.maxY, b.maxY);
return Math.max(0, maxX - minX) * Math.max(0, maxY - minY);
}
function contains$1(a, b) {
return a.minX <= b.minX && a.minY <= b.minY && b.maxX <= a.maxX && b.maxY <= a.maxY;
}
function intersects$2(a, b) {
return b.minX <= a.maxX && b.minY <= a.maxY && b.maxX >= a.minX && b.maxY >= a.minY;
}
function createNode$1(children) {
return {
children,
height: 1,
leaf: true,
minX: Infinity,
minY: Infinity,
maxX: -Infinity,
maxY: -Infinity
};
}
function multiSelect$1(arr, left, right, n, compare2) {
const stack = [left, right];
while (stack.length) {
right = stack.pop();
left = stack.pop();
if (right - left <= n) continue;
const mid = left + Math.ceil((right - left) / n / 2) * n;
quickselect$1(arr, mid, left, right, compare2);
stack.push(left, mid, mid, right);
}
}
const epsilon = 11102230246251565e-32;
const splitter = 134217729;
const resulterrbound = (3 + 8 * epsilon) * epsilon;
function sum(elen, e, flen, f, h) {
let Q, Qnew, hh, bvirt;
let enow = e[0];
let fnow = f[0];
let eindex = 0;
let findex = 0;
if (fnow > enow === fnow > -enow) {
Q = enow;
enow = e[++eindex];
} else {
Q = fnow;
fnow = f[++findex];
}
let hindex = 0;
if (eindex < elen && findex < flen) {
if (fnow > enow === fnow > -enow) {
Qnew = enow + Q;
hh = Q - (Qnew - enow);
enow = e[++eindex];
} else {
Qnew = fnow + Q;
hh = Q - (Qnew - fnow);
fnow = f[++findex];
}
Q = Qnew;
if (hh !== 0) {
h[hindex++] = hh;
}
while (eindex < elen && findex < flen) {
if (fnow > enow === fnow > -enow) {
Qnew = Q + enow;
bvirt = Qnew - Q;
hh = Q - (Qnew - bvirt) + (enow - bvirt);
enow = e[++eindex];
} else {
Qnew = Q + fnow;
bvirt = Qnew - Q;
hh = Q - (Qnew - bvirt) + (fnow - bvirt);
fnow = f[++findex];
}
Q = Qnew;
if (hh !== 0) {
h[hindex++] = hh;
}
}
}
while (eindex < elen) {
Qnew = Q + enow;
bvirt = Qnew - Q;
hh = Q - (Qnew - bvirt) + (enow - bvirt);
enow = e[++eindex];
Q = Qnew;
if (hh !== 0) {
h[hindex++] = hh;
}
}
while (findex < flen) {
Qnew = Q + fnow;
bvirt = Qnew - Q;
hh = Q - (Qnew - bvirt) + (fnow - bvirt);
fnow = f[++findex];
Q = Qnew;
if (hh !== 0) {
h[hindex++] = hh;
}
}
if (Q !== 0 || hindex === 0) {
h[hindex++] = Q;
}
return hindex;
}
function estimate(elen, e) {
let Q = e[0];
for (let i = 1; i < elen; i++) Q += e[i];
return Q;
}
function vec(n) {
return new Float64Array(n);
}
const ccwerrboundA = (3 + 16 * epsilon) * epsilon;
const ccwerrboundB = (2 + 12 * epsilon) * epsilon;
const ccwerrboundC = (9 + 64 * epsilon) * epsilon * epsilon;
const B$1 = vec(4);
const C1 = vec(8);
const C2 = vec(12);
const D = vec(16);
const u = vec(4);
function orient2dadapt(ax, ay, bx, by, cx, cy, detsum) {
let acxtail, acytail, bcxtail, bcytail;
let bvirt, c, ahi, alo, bhi, blo, _i, _j, _0, s1, s0, t1, t0, u3;
const acx = ax - cx;
const bcx = bx - cx;
const acy = ay - cy;
const bcy = by - cy;
s1 = acx * bcy;
c = splitter * acx;
ahi = c - (c - acx);
alo = acx - ahi;
c = splitter * bcy;
bhi = c - (c - bcy);
blo = bcy - bhi;
s0 = alo * blo - (s1 - ahi * bhi - alo * bhi - ahi * blo);
t1 = acy * bcx;
c = splitter * acy;
ahi = c - (c - acy);
alo = acy - ahi;
c = splitter * bcx;
bhi = c - (c - bcx);
blo = bcx - bhi;
t0 = alo * blo - (t1 - ahi * bhi - alo * bhi - ahi * blo);
_i = s0 - t0;
bvirt = s0 - _i;
B$1[0] = s0 - (_i + bvirt) + (bvirt - t0);
_j = s1 + _i;
bvirt = _j - s1;
_0 = s1 - (_j - bvirt) + (_i - bvirt);
_i = _0 - t1;
bvirt = _0 - _i;
B$1[1] = _0 - (_i + bvirt) + (bvirt - t1);
u3 = _j + _i;
bvirt = u3 - _j;
B$1[2] = _j - (u3 - bvirt) + (_i - bvirt);
B$1[3] = u3;
let det = estimate(4, B$1);
let errbound = ccwerrboundB * detsum;
if (det >= errbound || -det >= errbound) {
return det;
}
bvirt = ax - acx;
acxtail = ax - (acx + bvirt) + (bvirt - cx);
bvirt = bx - bcx;
bcxtail = bx - (bcx + bvirt) + (bvirt - cx);
bvirt = ay - acy;
acytail = ay - (acy + bvirt) + (bvirt - cy);
bvirt = by - bcy;
bcytail = by - (bcy + bvirt) + (bvirt - cy);
if (acxtail === 0 && acytail === 0 && bcxtail === 0 && bcytail === 0) {
return det;
}
errbound = ccwerrboundC * detsum + resulterrbound * Math.abs(det);
det += acx * bcytail + bcy * acxtail - (acy * bcxtail + bcx * acytail);
if (det >= errbound || -det >= errbound) return det;
s1 = acxtail * bcy;
c = splitter * acxtail;
ahi = c - (c - acxtail);
alo = acxtail - ahi;
c = splitter * bcy;
bhi = c - (c - bcy);
blo = bcy - bhi;
s0 = alo * blo - (s1 - ahi * bhi - alo * bhi - ahi * blo);
t1 = acytail * bcx;
c = splitter * acytail;
ahi = c - (c - acytail);
alo = acytail - ahi;
c = splitter * bcx;
bhi = c - (c - bcx);
blo = bcx - bhi;
t0 = alo * blo - (t1 - ahi * bhi - alo * bhi - ahi * blo);
_i = s0 - t0;
bvirt = s0 - _i;
u[0] = s0 - (_i + bvirt) + (bvirt - t0);
_j = s1 + _i;
bvirt = _j - s1;
_0 = s1 - (_j - bvirt) + (_i - bvirt);
_i = _0 - t1;
bvirt = _0 - _i;
u[1] = _0 - (_i + bvirt) + (bvirt - t1);
u3 = _j + _i;
bvirt = u3 - _j;
u[2] = _j - (u3 - bvirt) + (_i - bvirt);
u[3] = u3;
const C1len = sum(4, B$1, 4, u, C1);
s1 = acx * bcytail;
c = splitter * acx;
ahi = c - (c - acx);
alo = acx - ahi;
c = splitter * bcytail;
bhi = c - (c - bcytail);
blo = bcytail - bhi;
s0 = alo * blo - (s1 - ahi * bhi - alo * bhi - ahi * blo);
t1 = acy * bcxtail;
c = splitter * acy;
ahi = c - (c - acy);
alo = acy - ahi;
c = splitter * bcxtail;
bhi = c - (c - bcxtail);
blo = bcxtail - bhi;
t0 = alo * blo - (t1 - ahi * bhi - alo * bhi - ahi * blo);
_i = s0 - t0;
bvirt = s0 - _i;
u[0] = s0 - (_i + bvirt) + (bvirt - t0);
_j = s1 + _i;
bvirt = _j - s1;
_0 = s1 - (_j - bvirt) + (_i - bvirt);
_i = _0 - t1;
bvirt = _0 - _i;
u[1] = _0 - (_i + bvirt) + (bvirt - t1);
u3 = _j + _i;
bvirt = u3 - _j;
u[2] = _j - (u3 - bvirt) + (_i - bvirt);
u[3] = u3;
const C2len = sum(C1len, C1, 4, u, C2);
s1 = acxtail * bcytail;
c = splitter * acxtail;
ahi = c - (c - acxtail);
alo = acxtail - ahi;
c = splitter * bcytail;
bhi = c - (c - bcytail);
blo = bcytail - bhi;
s0 = alo * blo - (s1 - ahi * bhi - alo * bhi - ahi * blo);
t1 = acytail * bcxtail;
c = splitter * acytail;
ahi = c - (c - acytail);
alo = acytail - ahi;
c = splitter * bcxtail;
bhi = c - (c - bcxtail);
blo = bcxtail - bhi;
t0 = alo * blo - (t1 - ahi * bhi - alo * bhi - ahi * blo);
_i = s0 - t0;
bvirt = s0 - _i;
u[0] = s0 - (_i + bvirt) + (bvirt - t0);
_j = s1 + _i;
bvirt = _j - s1;
_0 = s1 - (_j - bvirt) + (_i - bvirt);
_i = _0 - t1;
bvirt = _0 - _i;
u[1] = _0 - (_i + bvirt) + (bvirt - t1);
u3 = _j + _i;
bvirt = u3 - _j;
u[2] = _j - (u3 - bvirt) + (_i - bvirt);
u[3] = u3;
const Dlen = sum(C2len, C2, 4, u, D);
return D[Dlen - 1];
}
function orient2d(ax, ay, bx, by, cx, cy) {
const detleft = (ay - cy) * (bx - cx);
const detright = (ax - cx) * (by - cy);
const det = detleft - detright;
const detsum = Math.abs(detleft + detright);
if (Math.abs(det) >= ccwerrboundA * detsum) return det;
return -orient2dadapt(ax, ay, bx, by, cx, cy, detsum);
}
function pointInPolygon$1(p, polygon2) {
var i;
var ii;
var k = 0;
var f;
var u1;
var v1;
var u2;
var v2;
var currentP;
var nextP;
var x = p[0];
var y = p[1];
var numContours = polygon2.length;
for (i = 0; i < numContours; i++) {
ii = 0;
var contour = polygon2[i];
var contourLen = contour.length - 1;
currentP = contour[0];
if (currentP[0] !== contour[contourLen][0] && currentP[1] !== contour[contourLen][1]) {
throw new Error("First and last coordinates in a ring must be the same");
}
u1 = currentP[0] - x;
v1 = currentP[1] - y;
for (ii; ii < contourLen; ii++) {
nextP = contour[ii + 1];
u2 = nextP[0] - x;
v2 = nextP[1] - y;
if (v1 === 0 && v2 === 0) {
if (u2 <= 0 && u1 >= 0 || u1 <= 0 && u2 >= 0) {
return 0;
}
} else if (v2 >= 0 && v1 <= 0 || v2 <= 0 && v1 >= 0) {
f = orient2d(u1, u2, v1, v2, 0, 0);
if (f === 0) {
return 0;
}
if (f > 0 && v2 > 0 && v1 <= 0 || f < 0 && v2 <= 0 && v1 > 0) {
k++;
}
}
currentP = nextP;
v1 = v2;
u1 = u2;
}
}
if (k % 2 === 0) {
return false;
}
return true;
}
function booleanPointInPolygon(point2, polygon2, options = {}) {
if (!point2) {
throw new Error("point is required");
}
if (!polygon2) {
throw new Error("polygon is required");
}
const pt = getCoord(point2);
const geom = getGeom(polygon2);
const type = geom.type;
const bbox2 = polygon2.bbox;
let polys = geom.coordinates;
if (bbox2 && inBBox(pt, bbox2) === false) {
return false;
}
if (type === "Polygon") {
polys = [polys];
}
let result = false;
for (var i = 0; i < polys.length; ++i) {
const polyResult = pointInPolygon$1(pt, polys[i]);
if (polyResult === 0) return options.ignoreBoundary ? false : true;
else if (polyResult) result = true;
}
return result;
}
function inBBox(pt, bbox2) {
return bbox2[0] <= pt[0] && bbox2[1] <= pt[1] && bbox2[2] >= pt[0] && bbox2[3] >= pt[1];
}
var index_default$6 = booleanPointInPolygon;
function geojsonPolygonSelfIntersections(feature2, filterFn) {
if (feature2.geometry.type !== "Polygon")
throw new Error("The input feature must be a Polygon");
var coord = feature2.geometry.coordinates;
var output = [];
var seen = {};
var allEdgesAsRbushTreeItems = [];
for (var ring0 = 0; ring0 < coord.length; ring0++) {
for (var edge0 = 0; edge0 < coord[ring0].length - 1; edge0++) {
allEdgesAsRbushTreeItems.push(rbushTreeItem(ring0, edge0));
}
}
var tree = new RBush$2();
tree.load(allEdgesAsRbushTreeItems);
for (var ringA = 0; ringA < coord.length; ringA++) {
for (var edgeA = 0; edgeA < coord[ringA].length - 1; edgeA++) {
var bboxOverlaps = tree.search(rbushTreeItem(ringA, edgeA));
bboxOverlaps.forEach(function(bboxIsect) {
var ring1 = bboxIsect.ring;
var edge1 = bboxIsect.edge;
ifIsectAddToOutput(ringA, edgeA, ring1, edge1);
});
}
}
return output;
function ifIsectAddToOutput(ring02, edge02, ring1, edge1) {
var start0 = coord[ring02][edge02];
var end0 = coord[ring02][edge02 + 1];
var start1 = coord[ring1][edge1];
var end1 = coord[ring1][edge1 + 1];
var isect = intersect(start0, end0, start1, end1);
if (isect === null) return;
var frac0;
var frac1;
if (end0[0] !== start0[0]) {
frac0 = (isect[0] - start0[0]) / (end0[0] - start0[0]);
} else {
frac0 = (isect[1] - start0[1]) / (end0[1] - start0[1]);
}
if (end1[0] !== start1[0]) {
frac1 = (isect[0] - start1[0]) / (end1[0] - start1[0]);
} else {
frac1 = (isect[1] - start1[1]) / (end1[1] - start1[1]);
}
if (frac0 >= 1 || frac0 <= 0 || frac1 >= 1 || frac1 <= 0) return;
var key = isect;
var unique = !seen[key.toString()];
if (unique) {
seen[key.toString()] = true;
}
if (filterFn) {
output.push(
filterFn(
isect,
ring02,
edge02,
start0,
end0,
frac0,
ring1,
edge1,
start1,
end1,
frac1,
unique
)
);
}
}
function rbushTreeItem(ring, edge) {
var start = coord[ring][edge];
var end = coord[ring][edge + 1];
var minX;
var maxX;
var minY;
var maxY;
if (start[0] < end[0]) {
minX = start[0];
maxX = end[0];
} else {
minX = end[0];
maxX = start[0];
}
if (start[1] < end[1]) {
minY = start[1];
maxY = end[1];
} else {
minY = end[1];
maxY = start[1];
}
return {
minX,
minY,
maxX,
maxY,
ring,
edge
};
}
}
function intersect(start0, end0, start1, end1) {
if (equalArrays(start0, start1) || equalArrays(start0, end1) || equalArrays(end0, start1) || equalArrays(end1, start1))
return null;
var x0 = start0[0], y0 = start0[1], x1 = end0[0], y1 = end0[1], x2 = start1[0], y2 = start1[1], x3 = end1[0], y3 = end1[1];
var denom = (x0 - x1) * (y2 - y3) - (y0 - y1) * (x2 - x3);
if (denom === 0) return null;
var x4 = ((x0 * y1 - y0 * x1) * (x2 - x3) - (x0 - x1) * (x2 * y3 - y2 * x3)) / denom;
var y4 = ((x0 * y1 - y0 * x1) * (y2 - y3) - (y0 - y1) * (x2 * y3 - y2 * x3)) / denom;
return [x4, y4];
}
function equalArrays(array1, array2) {
if (!array1 || !array2) return false;
if (array1.length !== array2.length) return false;
for (var i = 0, l = array1.length; i < l; i++) {
if (array1[i] instanceof Array && array2[i] instanceof Array) {
if (!equalArrays(array1[i], array2[i])) return false;
} else if (array1[i] !== array2[i]) {
return false;
}
}
return true;
}
function simplepolygon(feature2) {
if (feature2.type != "Feature")
throw new Error("The input must a geojson object of type Feature");
if (feature2.geometry === void 0 || feature2.geometry == null)
throw new Error(
"The input must a geojson object with a non-empty geometry"
);
if (feature2.geometry.type != "Polygon")
throw new Error("The input must be a geojson Polygon");
var numRings = feature2.geometry.coordinates.length;
var vertices = [];
for (var i = 0; i < numRings; i++) {
var ring = feature2.geometry.coordinates[i];
if (!equalArrays2(ring[0], ring[ring.length - 1])) {
ring.push(ring[0]);
}
for (var j = 0; j < ring.length - 1; j++) {
vertices.push(ring[j]);
}
}
if (!isUnique(vertices))
throw new Error(
"The input polygon may not have duplicate vertices (except for the first and last vertex of each ring)"
);
var numvertices = vertices.length;
var selfIsectsData = geojsonPolygonSelfIntersections(
feature2,
function filterFn(isect, ring0, edge0, start0, end0, frac0, ring1, edge1, start1, end1, frac1, unique) {
return [
isect,
ring0,
edge0,
start0,
end0,
frac0,
ring1,
edge1,
start1,
end1,
frac1,
unique
];
}
);
var numSelfIsect = selfIsectsData.length;
if (numSelfIsect == 0) {
var outputFeatureArray = [];
for (var i = 0; i < numRings; i++) {
outputFeatureArray.push(
polygon$1([feature2.geometry.coordinates[i]], {
parent: -1,
winding: windingOfRing(feature2.geometry.coordinates[i])
})
);
}
let output2 = featureCollection(outputFeatureArray);
determineParents(output2);
setNetWinding(output2);
return output2;
}
var pseudoVtxListByRingAndEdge = [];
var isectList = [];
for (var i = 0; i < numRings; i++) {
pseudoVtxListByRingAndEdge.push([]);
for (var j = 0; j < feature2.geometry.coordinates[i].length - 1; j++) {
pseudoVtxListByRingAndEdge[i].push([
new PseudoVtx(
feature2.geometry.coordinates[i][modulo(j + 1, feature2.geometry.coordinates[i].length - 1)],
1,
[i, j],
[i, modulo(j + 1, feature2.geometry.coordinates[i].length - 1)],
void 0
)
]);
isectList.push(
new Isect(
feature2.geometry.coordinates[i][j],
[i, modulo(j - 1, feature2.geometry.coordinates[i].length - 1)],
[i, j],
void 0,
void 0,
false,
true
)
);
}
}
for (var i = 0; i < numSelfIsect; i++) {
pseudoVtxListByRingAndEdge[selfIsectsData[i][1]][selfIsectsData[i][2]].push(
new PseudoVtx(
selfIsectsData[i][0],
selfIsectsData[i][5],
[selfIsectsData[i][1], selfIsectsData[i][2]],
[selfIsectsData[i][6], selfIsectsData[i][7]],
void 0
)
);
if (selfIsectsData[i][11])
isectList.push(
new Isect(
selfIsectsData[i][0],
[selfIsectsData[i][1], selfIsectsData[i][2]],
[selfIsectsData[i][6], selfIsectsData[i][7]],
void 0,
void 0,
true,
true
)
);
}
var numIsect = isectList.length;
for (var i = 0; i < pseudoVtxListByRingAndEdge.length; i++) {
for (var j = 0; j < pseudoVtxListByRingAndEdge[i].length; j++) {
pseudoVtxListByRingAndEdge[i][j].sort(function(a, b) {
return a.param < b.param ? -1 : 1;
});
}
}
var allIsectsAsIsectRbushTreeItem = [];
for (var i = 0; i < numIsect; i++) {
allIsectsAsIsectRbushTreeItem.push({
minX: isectList[i].coord[0],
minY: isectList[i].coord[1],
maxX: isectList[i].coord[0],
maxY: isectList[i].coord[1],
index: i
});
}
var isectRbushTree = new RBush$2();
isectRbushTree.load(allIsectsAsIsectRbushTreeItem);
for (var i = 0; i < pseudoVtxListByRingAndEdge.length; i++) {
for (var j = 0; j < pseudoVtxListByRingAndEdge[i].length; j++) {
for (var k = 0; k < pseudoVtxListByRingAndEdge[i][j].length; k++) {
let coordToFind;
if (k == pseudoVtxListByRingAndEdge[i][j].length - 1) {
coordToFind = pseudoVtxListByRingAndEdge[i][modulo(j + 1, feature2.geometry.coordinates[i].length - 1)][0].coord;
} else {
coordToFind = pseudoVtxListByRingAndEdge[i][j][k + 1].coord;
}
var IsectRbushTreeItemFound = isectRbushTree.search({
minX: coordToFind[0],
minY: coordToFind[1],
maxX: coordToFind[0],
maxY: coordToFind[1]
})[0];
pseudoVtxListByRingAndEdge[i][j][k].nxtIsectAlongEdgeIn = IsectRbushTreeItemFound.index;
}
}
}
for (var i = 0; i < pseudoVtxListByRingAndEdge.length; i++) {
for (var j = 0; j < pseudoVtxListByRingAndEdge[i].length; j++) {
for (var k = 0; k < pseudoVtxListByRingAndEdge[i][j].length; k++) {
let coordToFind = pseudoVtxListByRingAndEdge[i][j][k].coord;
var IsectRbushTreeItemFound = isectRbushTree.search({
minX: coordToFind[0],
minY: coordToFind[1],
maxX: coordToFind[0],
maxY: coordToFind[1]
})[0];
var l = IsectRbushTreeItemFound.index;
if (l < numvertices) {
isectList[l].nxtIsectAlongRingAndEdge2 = pseudoVtxListByRingAndEdge[i][j][k].nxtIsectAlongEdgeIn;
} else {
if (equalArrays2(
isectList[l].ringAndEdge1,
pseudoVtxListByRingAndEdge[i][j][k].ringAndEdgeIn
)) {
isectList[l].nxtIsectAlongRingAndEdge1 = pseudoVtxListByRingAndEdge[i][j][k].nxtIsectAlongEdgeIn;
} else {
isectList[l].nxtIsectAlongRingAndEdge2 = pseudoVtxListByRingAndEdge[i][j][k].nxtIsectAlongEdgeIn;
}
}
}
}
}
var queue = [];
var i = 0;
for (var j = 0; j < numRings; j++) {
var leftIsect = i;
for (var k = 0; k < feature2.geometry.coordinates[j].length - 1; k++) {
if (isectList[i].coord[0] < isectList[leftIsect].coord[0]) {
leftIsect = i;
}
i++;
}
var isectAfterLeftIsect = isectList[leftIsect].nxtIsectAlongRingAndEdge2;
for (var k = 0; k < isectList.length; k++) {
if (isectList[k].nxtIsectAlongRingAndEdge1 == leftIsect || isectList[k].nxtIsectAlongRingAndEdge2 == leftIsect) {
var isectBeforeLeftIsect = k;
break;
}
}
var windingAtIsect = isConvex(
[
isectList[isectBeforeLeftIsect].coord,
isectList[leftIsect].coord,
isectList[isectAfterLeftIsect].coord
],
true
) ? 1 : -1;
queue.push({ isect: leftIsect, parent: -1, winding: windingAtIsect });
}
queue.sort(function(a, b) {
return isectList[a.isect].coord > isectList[b.isect].coord ? -1 : 1;
});
var outputFeatureArray = [];
while (queue.length > 0) {
var popped = queue.pop();
var startIsect = popped.isect;
var currentOutputRingParent = popped.parent;
var currentOutputRingWinding = popped.winding;
var currentOutputRing = outputFeatureArray.length;
var currentOutputRingCoords = [isectList[startIsect].coord];
var currentIsect = startIsect;
if (isectList[startIsect].ringAndEdge1Walkable) {
var walkingRingAndEdge = isectList[startIsect].ringAndEdge1;
var nxtIsect = isectList[startIsect].nxtIsectAlongRingAndEdge1;
} else {
var walkingRingAndEdge = isectList[startIsect].ringAndEdge2;
var nxtIsect = isectList[startIsect].nxtIsectAlongRingAndEdge2;
}
while (!equalArrays2(isectList[startIsect].coord, isectList[nxtIsect].coord)) {
currentOutputRingCoords.push(isectList[nxtIsect].coord);
var nxtIsectInQueue = void 0;
for (var i = 0; i < queue.length; i++) {
if (queue[i].isect == nxtIsect) {
nxtIsectInQueue = i;
break;
}
}
if (nxtIsectInQueue != void 0) {
queue.splice(nxtIsectInQueue, 1);
}
if (equalArrays2(walkingRingAndEdge, isectList[nxtIsect].ringAndEdge1)) {
walkingRingAndEdge = isectList[nxtIsect].ringAndEdge2;
isectList[nxtIsect].ringAndEdge2Walkable = false;
if (isectList[nxtIsect].ringAndEdge1Walkable) {
var pushing = {
isect: nxtIsect
};
if (isConvex(
[
isectList[currentIsect].coord,
isectList[nxtIsect].coord,
isectList[isectList[nxtIsect].nxtIsectAlongRingAndEdge2].coord
],
currentOutputRingWinding == 1
)) {
pushing.parent = currentOutputRingParent;
pushing.winding = -currentOutputRingWinding;
} else {
pushing.parent = currentOutputRing;
pushing.winding = currentOutputRingWinding;
}
queue.push(pushing);
}
currentIsect = nxtIsect;
nxtIsect = isectList[nxtIsect].nxtIsectAlongRingAndEdge2;
} else {
walkingRingAndEdge = isectList[nxtIsect].ringAndEdge1;
isectList[nxtIsect].ringAndEdge1Walkable = false;
if (isectList[nxtIsect].ringAndEdge2Walkable) {
var pushing = {
isect: nxtIsect
};
if (isConvex(
[
isectList[currentIsect].coord,
isectList[nxtIsect].coord,
isectList[isectList[nxtIsect].nxtIsectAlongRingAndEdge1].coord
],
currentOutputRingWinding == 1
)) {
pushing.parent = currentOutputRingParent;
pushing.winding = -currentOutputRingWinding;
} else {
pushing.parent = currentOutputRing;
pushing.winding = currentOutputRingWinding;
}
queue.push(pushing);
}
currentIsect = nxtIsect;
nxtIsect = isectList[nxtIsect].nxtIsectAlongRingAndEdge1;
}
}
currentOutputRingCoords.push(isectList[nxtIsect].coord);
outputFeatureArray.push(
polygon$1([currentOutputRingCoords], {
index: currentOutputRing,
parent: currentOutputRingParent,
winding: currentOutputRingWinding,
netWinding: void 0
})
);
}
let output = featureCollection(outputFeatureArray);
determineParents(output);
setNetWinding(output);
function determineParents(output2) {
var featuresWithoutParent = [];
for (var i2 = 0; i2 < output2.features.length; i2++) {
if (output2.features[i2].properties.parent == -1)
featuresWithoutParent.push(i2);
}
if (featuresWithoutParent.length > 1) {
for (var i2 = 0; i2 < featuresWithoutParent.length; i2++) {
var parent = -1;
var parentArea = Infinity;
for (var j2 = 0; j2 < output2.features.length; j2++) {
if (featuresWithoutParent[i2] == j2) continue;
if (booleanPointInPolygon(
output2.features[featuresWithoutParent[i2]].geometry.coordinates[0][0],
output2.features[j2],
{ ignoreBoundary: true }
)) {
if (area(output2.features[j2]) < parentArea) {
parent = j2;
}
}
}
output2.features[featuresWithoutParent[i2]].properties.parent = parent;
}
}
}
function setNetWinding(output2) {
for (var i2 = 0; i2 < output2.features.length; i2++) {
if (output2.features[i2].properties.parent == -1) {
var netWinding = output2.features[i2].properties.winding;
output2.features[i2].properties.netWinding = netWinding;
setNetWindingOfChildren(output2, i2, netWinding);
}
}
}
function setNetWindingOfChildren(output2, parent, ParentNetWinding) {
for (var i2 = 0; i2 < output2.features.length; i2++) {
if (output2.features[i2].properties.parent == parent) {
var netWinding = ParentNetWinding + output2.features[i2].properties.winding;
output2.features[i2].properties.netWinding = netWinding;
setNetWindingOfChildren(output2, i2, netWinding);
}
}
}
return output;
}
var PseudoVtx = class {
// The next intersection when following the incomming edge (so not when following ringAndEdgeOut!)
// Constructor for (ring- or intersection-) pseudo-vertices.
constructor(coord, param, ringAndEdgeIn, ringAndEdgeOut, nxtIsectAlongEdgeIn) {
this.coord = coord;
this.param = param;
this.ringAndEdgeIn = ringAndEdgeIn;
this.ringAndEdgeOut = ringAndEdgeOut;
this.nxtIsectAlongEdgeIn = nxtIsectAlongEdgeIn;
}
};
var Isect = class {
// Constructor for an intersection. There are two intersection-pseudo-vertices per self-intersection and one ring-pseudo-vertex per ring-vertex-intersection. Their labels 1 and 2 are not assigned a particular meaning but are permanent once given.
constructor(coord, ringAndEdge1, ringAndEdge2, nxtIsectAlongRingAndEdge1, nxtIsectAlongRingAndEdge2, ringAndEdge1Walkable, ringAndEdge2Walkable) {
this.coord = coord;
this.ringAndEdge1 = ringAndEdge1;
this.ringAndEdge2 = ringAndEdge2;
this.nxtIsectAlongRingAndEdge1 = nxtIsectAlongRingAndEdge1;
this.nxtIsectAlongRingAndEdge2 = nxtIsectAlongRingAndEdge2;
this.ringAndEdge1Walkable = ringAndEdge1Walkable;
this.ringAndEdge2Walkable = ringAndEdge2Walkable;
}
};
function isConvex(pts, righthanded) {
if (typeof righthanded === "undefined") righthanded = true;
if (pts.length != 3)
throw new Error("This function requires an array of three points [x,y]");
var d = (pts[1][0] - pts[0][0]) * (pts[2][1] - pts[0][1]) - (pts[1][1] - pts[0][1]) * (pts[2][0] - pts[0][0]);
return d >= 0 == righthanded;
}
function windingOfRing(ring) {
var leftVtx = 0;
for (var i = 0; i < ring.length - 1; i++) {
if (ring[i][0] < ring[leftVtx][0]) leftVtx = i;
}
if (isConvex(
[
ring[modulo(leftVtx - 1, ring.length - 1)],
ring[leftVtx],
ring[modulo(leftVtx + 1, ring.length - 1)]
],
true
)) {
var winding = 1;
} else {
var winding = -1;
}
return winding;
}
function equalArrays2(array1, array2) {
if (!array1 || !array2) return false;
if (array1.length != array2.length) return false;
for (var i = 0, l = array1.length; i < l; i++) {
if (array1[i] instanceof Array && array2[i] instanceof Array) {
if (!equalArrays2(array1[i], array2[i])) return false;
} else if (array1[i] != array2[i]) {
return false;
}
}
return true;
}
function modulo(n, m) {
return (n % m + m) % m;
}
function isUnique(array) {
var u2 = {};
var isUnique2 = 1;
for (var i = 0, l = array.length; i < l; ++i) {
if (Object.prototype.hasOwnProperty.call(u2, array[i].toString())) {
isUnique2 = 0;
break;
}
u2[array[i].toString()] = 1;
}
return isUnique2;
}
function unkinkPolygon(geojson) {
var features = [];
flattenEach$4(geojson, function(feature2) {
if (feature2.geometry.type !== "Polygon") return;
featureEach$3(simplepolygon(feature2), function(poly) {
features.push(polygon$1(poly.geometry.coordinates, feature2.properties));
});
});
return featureCollection(features);
}
var index_default$5 = unkinkPolygon;
function coordEach$2(geojson, callback, excludeWrapCoord) {
if (geojson === null) return;
var j, k, l, geometry, stopG, coords, geometryMaybeCollection, wrapShrink = 0, coordIndex = 0, isGeometryCollection, type = geojson.type, isFeatureCollection = type === "FeatureCollection", isFeature = type === "Feature", stop = isFeatureCollection ? geojson.features.length : 1;
for (var featureIndex = 0; featureIndex < stop; featureIndex++) {
geometryMaybeCollection = isFeatureCollection ? (
// @ts-expect-error: Known type conflict
geojson.features[featureIndex].geometry
) : isFeature ? (
// @ts-expect-error: Known type conflict
geojson.geometry
) : geojson;
isGeometryCollection = geometryMaybeCollection ? geometryMaybeCollection.type === "GeometryCollection" : false;
stopG = isGeometryCollection ? geometryMaybeCollection.geometries.length : 1;
for (var geomIndex = 0; geomIndex < stopG; geomIndex++) {
var multiFeatureIndex = 0;
var geometryIndex = 0;
geometry = isGeometryCollection ? geometryMaybeCollection.geometries[geomIndex] : geometryMaybeCollection;
if (geometry === null) continue;
coords = geometry.coordinates;
var geomType = geometry.type;
wrapShrink = 0;
switch (geomType) {
case null:
break;
case "Point":
if (
// @ts-expect-error: Known type conflict
callback(
coords,
coordIndex,
featureIndex,
multiFeatureIndex,
geometryIndex
) === false
)
return false;
coordIndex++;
multiFeatureIndex++;
break;
case "LineString":
case "MultiPoint":
for (j = 0; j < coords.length; j++) {
if (
// @ts-expect-error: Known type conflict
callback(
coords[j],
coordIndex,
featureIndex,
multiFeatureIndex,
geometryIndex
) === false
)
return false;
coordIndex++;
if (geomType === "MultiPoint") multiFeatureIndex++;
}
if (geomType === "LineString") multiFeatureIndex++;
break;
case "Polygon":
case "MultiLineString":
for (j = 0; j < coords.length; j++) {
for (k = 0; k < coords[j].length - wrapShrink; k++) {
if (
// @ts-expect-error: Known type conflict
callback(
coords[j][k],
coordIndex,
featureIndex,
multiFeatureIndex,
geometryIndex
) === false
)
return false;
coordIndex++;
}
if (geomType === "MultiLineString") multiFeatureIndex++;
if (geomType === "Polygon") geometryIndex++;
}
if (geomType === "Polygon") multiFeatureIndex++;
break;
case "MultiPolygon":
for (j = 0; j < coords.length; j++) {
geometryIndex = 0;
for (k = 0; k < coords[j].length; k++) {
for (l = 0; l < coords[j][k].length - wrapShrink; l++) {
if (
// @ts-expect-error: Known type conflict
callback(
coords[j][k][l],
coordIndex,
featureIndex,
multiFeatureIndex,
geometryIndex
) === false
)
return false;
coordIndex++;
}
geometryIndex++;
}
multiFeatureIndex++;
}
break;
case "GeometryCollection":
for (j = 0; j < geometry.geometries.length; j++)
if (
// @ts-expect-error: Known type conflict
coordEach$2(geometry.geometries[j], callback) === false
)
return false;
break;
default:
throw new Error("Unknown Geometry Type");
}
}
}
}
function bbox$1(geojson, options = {}) {
if (geojson.bbox != null && true !== options.recompute) {
return geojson.bbox;
}
const result = [Infinity, Infinity, -Infinity, -Infinity];
coordEach$2(geojson, (coord) => {
if (result[0] > coord[0]) {
result[0] = coord[0];
}
if (result[1] > coord[1]) {
result[1] = coord[1];
}
if (result[2] < coord[0]) {
result[2] = coord[0];
}
if (result[3] < coord[1]) {
result[3] = coord[1];
}
});
return result;
}
function featureEach$2(geojson, callback) {
if (geojson.type === "Feature") {
callback(geojson, 0);
} else if (geojson.type === "FeatureCollection") {
for (var i = 0; i < geojson.features.length; i++) {
if (callback(geojson.features[i], i) === false) break;
}
}
}
function toBBox(geojson) {
var bbox2;
if (geojson.bbox) bbox2 = geojson.bbox;
else if (Array.isArray(geojson) && geojson.length === 4) bbox2 = geojson;
else if (Array.isArray(geojson) && geojson.length === 6)
bbox2 = [geojson[0], geojson[1], geojson[3], geojson[4]];
else if (geojson.type === "Feature") bbox2 = bbox$1(geojson);
else if (geojson.type === "FeatureCollection") bbox2 = bbox$1(geojson);
else throw new Error("invalid geojson");
return {
minX: bbox2[0],
minY: bbox2[1],
maxX: bbox2[2],
maxY: bbox2[3]
};
}
var RBush$1 = class RBush2 {
constructor(maxEntries = 9) {
this.tree = new RBush$2(maxEntries);
this.tree.toBBox = toBBox;
}
/**
* [insert](https://github.com/mourner/rbush#data-format)
*
* @memberof rbush
* @param {Feature} feature insert single GeoJSON Feature
* @returns {RBush} GeoJSON RBush
* @example
* var poly = turf.polygon([[[-78, 41], [-67, 41], [-67, 48], [-78, 48], [-78, 41]]]);
* tree.insert(poly)
*/
insert(feature2) {
if (feature2.type !== "Feature") throw new Error("invalid feature");
feature2.bbox = feature2.bbox ? feature2.bbox : bbox$1(feature2);
this.tree.insert(feature2);
return this;
}
/**
* [load](https://github.com/mourner/rbush#bulk-inserting-data)
*
* @memberof rbush
* @param {FeatureCollection|Array<Feature>} features load entire GeoJSON FeatureCollection
* @returns {RBush} GeoJSON RBush
* @example
* var polys = turf.polygons([
* [[[-78, 41], [-67, 41], [-67, 48], [-78, 48], [-78, 41]]],
* [[[-93, 32], [-83, 32], [-83, 39], [-93, 39], [-93, 32]]]
* ]);
* tree.load(polys);
*/
load(features) {
var load = [];
if (Array.isArray(features)) {
features.forEach(function(feature2) {
if (feature2.type !== "Feature") throw new Error("invalid features");
feature2.bbox = feature2.bbox ? feature2.bbox : bbox$1(feature2);
load.push(feature2);
});
} else {
featureEach$2(features, function(feature2) {
if (feature2.type !== "Feature") throw new Error("invalid features");
feature2.bbox = feature2.bbox ? feature2.bbox : bbox$1(feature2);
load.push(feature2);
});
}
this.tree.load(load);
return this;
}
/**
* [remove](https://github.com/mourner/rbush#removing-data)
*
* @memberof rbush
* @param {Feature} feature remove single GeoJSON Feature
* @param {Function} equals Pass a custom equals function to compare by value for removal.
* @returns {RBush} GeoJSON RBush
* @example
* var poly = turf.polygon([[[-78, 41], [-67, 41], [-67, 48], [-78, 48], [-78, 41]]]);
*
* tree.remove(poly);
*/
remove(feature2, equals2) {
if (feature2.type !== "Feature") throw new Error("invalid feature");
feature2.bbox = feature2.bbox ? feature2.bbox : bbox$1(feature2);
this.tree.remove(feature2, equals2);
return this;
}
/**
* [clear](https://github.com/mourner/rbush#removing-data)
*
* @memberof rbush
* @returns {RBush} GeoJSON Rbush
* @example
* tree.clear()
*/
clear() {
this.tree.clear();
return this;
}
/**
* [search](https://github.com/mourner/rbush#search)
*
* @memberof rbush
* @param {BBox|FeatureCollection|Feature} geojson search with GeoJSON
* @returns {FeatureCollection} all features that intersects with the given GeoJSON.
* @example
* var poly = turf.polygon([[[-78, 41], [-67, 41], [-67, 48], [-78, 48], [-78, 41]]]);
*
* tree.search(poly);
*/
search(geojson) {
var features = this.tree.search(toBBox(geojson));
return featureCollection(features);
}
/**
* [collides](https://github.com/mourner/rbush#collisions)
*
* @memberof rbush
* @param {BBox|FeatureCollection|Feature} geojson collides with GeoJSON
* @returns {boolean} true if there are any items intersecting the given GeoJSON, otherwise false.
* @example
* var poly = turf.polygon([[[-78, 41], [-67, 41], [-67, 48], [-78, 48], [-78, 41]]]);
*
* tree.collides(poly);
*/
collides(geojson) {
return this.tree.collides(toBBox(geojson));
}
/**
* [all](https://github.com/mourner/rbush#search)
*
* @memberof rbush
* @returns {FeatureCollection} all the features in RBush
* @example
* tree.all()
*/
all() {
const features = this.tree.all();
return featureCollection(features);
}
/**
* [toJSON](https://github.com/mourner/rbush#export-and-import)
*
* @memberof rbush
* @returns {any} export data as JSON object
* @example
* var exported = tree.toJSON()
*/
toJSON() {
return this.tree.toJSON();
}
/**
* [fromJSON](https://github.com/mourner/rbush#export-and-import)
*
* @memberof rbush
* @param {any} json import previously exported data
* @returns {RBush} GeoJSON RBush
* @example
* var exported = {
* "children": [
* {
* "type": "Feature",
* "geometry": {
* "type": "Point",
* "coordinates": [110, 50]
* },
* "properties": {},
* "bbox": [110, 50, 110, 50]
* }
* ],
* "height": 1,
* "leaf": true,
* "minX": 110,
* "minY": 50,
* "maxX": 110,
* "maxY": 50
* }
* tree.fromJSON(exported)
*/
fromJSON(json) {
this.tree.fromJSON(json);
return this;
}
};
function geojsonRbush(maxEntries) {
return new RBush$1(maxEntries);
}
function coordEach$1(geojson, callback, excludeWrapCoord) {
if (geojson === null) return;
var j, k, l, geometry, stopG, coords, geometryMaybeCollection, wrapShrink = 0, coordIndex = 0, isGeometryCollection, type = geojson.type, isFeatureCollection = type === "FeatureCollection", isFeature = type === "Feature", stop = isFeatureCollection ? geojson.features.length : 1;
for (var featureIndex = 0; featureIndex < stop; featureIndex++) {
geometryMaybeCollection = isFeatureCollection ? (
// @ts-expect-error: Known type conflict
geojson.features[featureIndex].geometry
) : isFeature ? (
// @ts-expect-error: Known type conflict
geojson.geometry
) : geojson;
isGeometryCollection = geometryMaybeCollection ? geometryMaybeCollection.type === "GeometryCollection" : false;
stopG = isGeometryCollection ? geometryMaybeCollection.geometries.length : 1;
for (var geomIndex = 0; geomIndex < stopG; geomIndex++) {
var multiFeatureIndex = 0;
var geometryIndex = 0;
geometry = isGeometryCollection ? geometryMaybeCollection.geometries[geomIndex] : geometryMaybeCollection;
if (geometry === null) continue;
coords = geometry.coordinates;
var geomType = geometry.type;
wrapShrink = 0;
switch (geomType) {
case null:
break;
case "Point":
if (
// @ts-expect-error: Known type conflict
callback(
coords,
coordIndex,
featureIndex,
multiFeatureIndex,
geometryIndex
) === false
)
return false;
coordIndex++;
multiFeatureIndex++;
break;
case "LineString":
case "MultiPoint":
for (j = 0; j < coords.length; j++) {
if (
// @ts-expect-error: Known type conflict
callback(
coords[j],
coordIndex,
featureIndex,
multiFeatureIndex,
geometryIndex
) === false
)
return false;
coordIndex++;
if (geomType === "MultiPoint") multiFeatureIndex++;
}
if (geomType === "LineString") multiFeatureIndex++;
break;
case "Polygon":
case "MultiLineString":
for (j = 0; j < coords.length; j++) {
for (k = 0; k < coords[j].length - wrapShrink; k++) {
if (
// @ts-expect-error: Known type conflict
callback(
coords[j][k],
coordIndex,
featureIndex,
multiFeatureIndex,
geometryIndex
) === false
)
return false;
coordIndex++;
}
if (geomType === "MultiLineString") multiFeatureIndex++;
if (geomType === "Polygon") geometryIndex++;
}
if (geomType === "Polygon") multiFeatureIndex++;
break;
case "MultiPolygon":
for (j = 0; j < coords.length; j++) {
geometryIndex = 0;
for (k = 0; k < coords[j].length; k++) {
for (l = 0; l < coords[j][k].length - wrapShrink; l++) {
if (
// @ts-expect-error: Known type conflict
callback(
coords[j][k][l],
coordIndex,
featureIndex,
multiFeatureIndex,
geometryIndex
) === false
)
return false;
coordIndex++;
}
geometryIndex++;
}
multiFeatureIndex++;
}
break;
case "GeometryCollection":
for (j = 0; j < geometry.geometries.length; j++)
if (
// @ts-expect-error: Known type conflict
coordEach$1(geometry.geometries[j], callback) === false
)
return false;
break;
default:
throw new Error("Unknown Geometry Type");
}
}
}
}
function truncate(geojson, options) {
options = options != null ? options : {};
if (!isObject$1(options)) throw new Error("options is invalid");
var precision2 = options.precision;
var coordinates = options.coordinates;
var mutate = options.mutate;
precision2 = precision2 === void 0 || precision2 === null || isNaN(precision2) ? 6 : precision2;
coordinates = coordinates === void 0 || coordinates === null || isNaN(coordinates) ? 3 : coordinates;
if (!geojson) throw new Error("<geojson> is required");
if (typeof precision2 !== "number")
throw new Error("<precision> must be a number");
if (typeof coordinates !== "number")
throw new Error("<coordinates> must be a number");
if (mutate === false || mutate === void 0)
geojson = JSON.parse(JSON.stringify(geojson));
var factor = Math.pow(10, precision2);
coordEach$1(geojson, function(coords) {
truncateCoords(coords, factor, coordinates);
});
return geojson;
}
function truncateCoords(coords, factor, coordinates) {
if (coords.length > coordinates) coords.splice(coordinates, coords.length);
for (var i = 0; i < coords.length; i++) {
coords[i] = Math.round(coords[i] * factor) / factor;
}
return coords;
}
function geomEach$3(geojson, callback) {
var i, j, g, geometry, stopG, geometryMaybeCollection, isGeometryCollection, featureProperties, featureBBox, featureId2, featureIndex = 0, isFeatureCollection = geojson.type === "FeatureCollection", isFeature = geojson.type === "Feature", stop = isFeatureCollection ? geojson.features.length : 1;
for (i = 0; i < stop; i++) {
geometryMaybeCollection = isFeatureCollection ? (
// @ts-expect-error: Known type conflict
geojson.features[i].geometry
) : isFeature ? (
// @ts-expect-error: Known type conflict
geojson.geometry
) : geojson;
featureProperties = isFeatureCollection ? (
// @ts-expect-error: Known type conflict
geojson.features[i].properties
) : isFeature ? (
// @ts-expect-error: Known type conflict
geojson.properties
) : {};
featureBBox = isFeatureCollection ? (
// @ts-expect-error: Known type conflict
geojson.features[i].bbox
) : isFeature ? (
// @ts-expect-error: Known type conflict
geojson.bbox
) : void 0;
featureId2 = isFeatureCollection ? (
// @ts-expect-error: Known type conflict
geojson.features[i].id
) : isFeature ? (
// @ts-expect-error: Known type conflict
geojson.id
) : void 0;
isGeometryCollection = geometryMaybeCollection ? geometryMaybeCollection.type === "GeometryCollection" : false;
stopG = isGeometryCollection ? geometryMaybeCollection.geometries.length : 1;
for (g = 0; g < stopG; g++) {
geometry = isGeometryCollection ? geometryMaybeCollection.geometries[g] : geometryMaybeCollection;
if (geometry === null) {
if (
// @ts-expect-error: Known type conflict
callback(
// @ts-expect-error: Known type conflict
null,
featureIndex,
featureProperties,
featureBBox,
featureId2
) === false
)
return false;
continue;
}
switch (geometry.type) {
case "Point":
case "LineString":
case "MultiPoint":
case "Polygon":
case "MultiLineString":
case "MultiPolygon": {
if (
// @ts-expect-error: Known type conflict
callback(
geometry,
featureIndex,
featureProperties,
featureBBox,
featureId2
) === false
)
return false;
break;
}
case "GeometryCollection": {
for (j = 0; j < geometry.geometries.length; j++) {
if (
// @ts-expect-error: Known type conflict
callback(
geometry.geometries[j],
featureIndex,
featureProperties,
featureBBox,
featureId2
) === false
)
return false;
}
break;
}
default:
throw new Error("Unknown Geometry Type");
}
}
featureIndex++;
}
}
function flattenEach$3(geojson, callback) {
geomEach$3(geojson, function(geometry, featureIndex, properties, bbox2, id) {
var type = geometry === null ? null : geometry.type;
switch (type) {
case null:
case "Point":
case "LineString":
case "Polygon":
if (
// @ts-expect-error: Known type conflict
callback(
feature$1(geometry, properties, { bbox: bbox2, id }),
featureIndex,
0
) === false
)
return false;
return;
}
var geomType;
switch (type) {
case "MultiPoint":
geomType = "Point";
break;
case "MultiLineString":
geomType = "LineString";
break;
case "MultiPolygon":
geomType = "Polygon";
break;
}
for (
var multiFeatureIndex = 0;
// @ts-expect-error: Known type conflict
multiFeatureIndex < geometry.coordinates.length;
multiFeatureIndex++
) {
var coordinate = geometry.coordinates[multiFeatureIndex];
var geom = {
type: geomType,
coordinates: coordinate
};
if (
// @ts-expect-error: Known type conflict
callback(feature$1(geom, properties), featureIndex, multiFeatureIndex) === false
)
return false;
}
});
}
function lineSegment(geojson) {
if (!geojson) {
throw new Error("geojson is required");
}
const results = [];
flattenEach$3(geojson, (feature2) => {
lineSegmentFeature(feature2, results);
});
return featureCollection(results);
}
function lineSegmentFeature(geojson, results) {
let coords = [];
const geometry = geojson.geometry;
if (geometry !== null) {
switch (geometry.type) {
case "Polygon":
coords = getCoords(geometry);
break;
case "LineString":
coords = [getCoords(geometry)];
}
coords.forEach((coord) => {
const segments = createSegments(coord, geojson.properties);
segments.forEach((segment) => {
segment.id = results.length;
results.push(segment);
});
});
}
}
function createSegments(coords, properties) {
const segments = [];
coords.reduce((previousCoords, currentCoords) => {
const segment = lineString([previousCoords, currentCoords], properties);
segment.bbox = bbox(previousCoords, currentCoords);
segments.push(segment);
return currentCoords;
});
return segments;
}
function bbox(coords1, coords2) {
const x1 = coords1[0];
const y1 = coords1[1];
const x2 = coords2[0];
const y2 = coords2[1];
const west = x1 < x2 ? x1 : x2;
const south = y1 < y2 ? y1 : y2;
const east = x1 > x2 ? x1 : x2;
const north = y1 > y2 ? y1 : y2;
return [west, south, east, north];
}
function distance(from, to, options = {}) {
var coordinates1 = getCoord(from);
var coordinates2 = getCoord(to);
var dLat = degreesToRadians(coordinates2[1] - coordinates1[1]);
var dLon = degreesToRadians(coordinates2[0] - coordinates1[0]);
var lat1 = degreesToRadians(coordinates1[1]);
var lat2 = degreesToRadians(coordinates2[1]);
var a = Math.pow(Math.sin(dLat / 2), 2) + Math.pow(Math.sin(dLon / 2), 2) * Math.cos(lat1) * Math.cos(lat2);
return radiansToLength(
2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a)),
options.units
);
}
function geomEach$2(geojson, callback) {
var i, j, g, geometry, stopG, geometryMaybeCollection, isGeometryCollection, featureProperties, featureBBox, featureId2, featureIndex = 0, isFeatureCollection = geojson.type === "FeatureCollection", isFeature = geojson.type === "Feature", stop = isFeatureCollection ? geojson.features.length : 1;
for (i = 0; i < stop; i++) {
geometryMaybeCollection = isFeatureCollection ? (
// @ts-expect-error: Known type conflict
geojson.features[i].geometry
) : isFeature ? (
// @ts-expect-error: Known type conflict
geojson.geometry
) : geojson;
featureProperties = isFeatureCollection ? (
// @ts-expect-error: Known type conflict
geojson.features[i].properties
) : isFeature ? (
// @ts-expect-error: Known type conflict
geojson.properties
) : {};
featureBBox = isFeatureCollection ? (
// @ts-expect-error: Known type conflict
geojson.features[i].bbox
) : isFeature ? (
// @ts-expect-error: Known type conflict
geojson.bbox
) : void 0;
featureId2 = isFeatureCollection ? (
// @ts-expect-error: Known type conflict
geojson.features[i].id
) : isFeature ? (
// @ts-expect-error: Known type conflict
geojson.id
) : void 0;
isGeometryCollection = geometryMaybeCollection ? geometryMaybeCollection.type === "GeometryCollection" : false;
stopG = isGeometryCollection ? geometryMaybeCollection.geometries.length : 1;
for (g = 0; g < stopG; g++) {
geometry = isGeometryCollection ? geometryMaybeCollection.geometries[g] : geometryMaybeCollection;
if (geometry === null) {
if (
// @ts-expect-error: Known type conflict
callback(
// @ts-expect-error: Known type conflict
null,
featureIndex,
featureProperties,
featureBBox,
featureId2
) === false
)
return false;
continue;
}
switch (geometry.type) {
case "Point":
case "LineString":
case "MultiPoint":
case "Polygon":
case "MultiLineString":
case "MultiPolygon": {
if (
// @ts-expect-error: Known type conflict
callback(
geometry,
featureIndex,
featureProperties,
featureBBox,
featureId2
) === false
)
return false;
break;
}
case "GeometryCollection": {
for (j = 0; j < geometry.geometries.length; j++) {
if (
// @ts-expect-error: Known type conflict
callback(
geometry.geometries[j],
featureIndex,
featureProperties,
featureBBox,
featureId2
) === false
)
return false;
}
break;
}
default:
throw new Error("Unknown Geometry Type");
}
}
featureIndex++;
}
}
function flattenEach$2(geojson, callback) {
geomEach$2(geojson, function(geometry, featureIndex, properties, bbox2, id) {
var type = geometry === null ? null : geometry.type;
switch (type) {
case null:
case "Point":
case "LineString":
case "Polygon":
if (
// @ts-expect-error: Known type conflict
callback(
feature$1(geometry, properties, { bbox: bbox2, id }),
featureIndex,
0
) === false
)
return false;
return;
}
var geomType;
switch (type) {
case "MultiPoint":
geomType = "Point";
break;
case "MultiLineString":
geomType = "LineString";
break;
case "MultiPolygon":
geomType = "Polygon";
break;
}
for (
var multiFeatureIndex = 0;
// @ts-expect-error: Known type conflict
multiFeatureIndex < geometry.coordinates.length;
multiFeatureIndex++
) {
var coordinate = geometry.coordinates[multiFeatureIndex];
var geom = {
type: geomType,
coordinates: coordinate
};
if (
// @ts-expect-error: Known type conflict
callback(feature$1(geom, properties), featureIndex, multiFeatureIndex) === false
)
return false;
}
});
}
var __defProp$2 = Object.defineProperty;
var __defProps$1 = Object.defineProperties;
var __getOwnPropDescs$1 = Object.getOwnPropertyDescriptors;
var __getOwnPropSymbols$1 = Object.getOwnPropertySymbols;
var __hasOwnProp$1 = Object.prototype.hasOwnProperty;
var __propIsEnum$1 = Object.prototype.propertyIsEnumerable;
var __defNormalProp$1 = (obj, key, value) => key in obj ? __defProp$2(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
var __spreadValues$1 = (a, b) => {
for (var prop in b || (b = {}))
if (__hasOwnProp$1.call(b, prop))
__defNormalProp$1(a, prop, b[prop]);
if (__getOwnPropSymbols$1)
for (var prop of __getOwnPropSymbols$1(b)) {
if (__propIsEnum$1.call(b, prop))
__defNormalProp$1(a, prop, b[prop]);
}
return a;
};
var __spreadProps$1 = (a, b) => __defProps$1(a, __getOwnPropDescs$1(b));
function nearestPointOnLine(lines, inputPoint, options = {}) {
if (!lines || !inputPoint) {
throw new Error("lines and inputPoint are required arguments");
}
const inputPos = getCoord(inputPoint);
let closestPt = point([Infinity, Infinity], {
lineStringIndex: -1,
segmentIndex: -1,
totalDistance: -1,
lineDistance: -1,
segmentDistance: -1,
pointDistance: Infinity,
// deprecated properties START
multiFeatureIndex: -1,
index: -1,
location: -1,
dist: Infinity
// deprecated properties END
});
let totalDistance = 0;
let lineDistance = 0;
let currentLineStringIndex = -1;
flattenEach$2(
lines,
function(line, _featureIndex, lineStringIndex) {
if (currentLineStringIndex !== lineStringIndex) {
currentLineStringIndex = lineStringIndex;
lineDistance = 0;
}
const coords = getCoords(line);
for (let i = 0; i < coords.length - 1; i++) {
const start = point(coords[i]);
const startPos = getCoord(start);
const stop = point(coords[i + 1]);
const stopPos = getCoord(stop);
const segmentLength = distance(start, stop, options);
let intersectPos;
let wasEnd;
if (stopPos[0] === inputPos[0] && stopPos[1] === inputPos[1]) {
[intersectPos, wasEnd] = [stopPos, true];
} else if (startPos[0] === inputPos[0] && startPos[1] === inputPos[1]) {
[intersectPos, wasEnd] = [startPos, false];
} else {
[intersectPos, wasEnd] = nearestPointOnSegment(
startPos,
stopPos,
inputPos
);
}
const pointDistance = distance(inputPoint, intersectPos, options);
if (pointDistance < closestPt.properties.pointDistance) {
const segmentDistance = distance(start, intersectPos, options);
closestPt = point(intersectPos, {
lineStringIndex,
// Legacy behaviour where index progresses to next segment # if we
// went with the end point this iteration.
segmentIndex: wasEnd ? i + 1 : i,
totalDistance: totalDistance + segmentDistance,
lineDistance: lineDistance + segmentDistance,
segmentDistance,
pointDistance,
// deprecated properties START
multiFeatureIndex: -1,
index: -1,
location: -1,
dist: Infinity
// deprecated properties END
});
closestPt.properties = __spreadProps$1(__spreadValues$1({}, closestPt.properties), {
multiFeatureIndex: closestPt.properties.lineStringIndex,
index: closestPt.properties.segmentIndex,
location: closestPt.properties.totalDistance,
dist: closestPt.properties.pointDistance
// deprecated properties END
});
}
totalDistance += segmentLength;
lineDistance += segmentLength;
}
}
);
return closestPt;
}
function dot(v1, v2) {
const [v1x, v1y, v1z] = v1;
const [v2x, v2y, v2z] = v2;
return v1x * v2x + v1y * v2y + v1z * v2z;
}
function cross$1(v1, v2) {
const [v1x, v1y, v1z] = v1;
const [v2x, v2y, v2z] = v2;
return [v1y * v2z - v1z * v2y, v1z * v2x - v1x * v2z, v1x * v2y - v1y * v2x];
}
function magnitude(v) {
return Math.sqrt(Math.pow(v[0], 2) + Math.pow(v[1], 2) + Math.pow(v[2], 2));
}
function normalize(v) {
const mag = magnitude(v);
return [v[0] / mag, v[1] / mag, v[2] / mag];
}
function lngLatToVector(a) {
const lat = degreesToRadians(a[1]);
const lng = degreesToRadians(a[0]);
return [
Math.cos(lat) * Math.cos(lng),
Math.cos(lat) * Math.sin(lng),
Math.sin(lat)
];
}
function vectorToLngLat(v) {
const [x, y, z] = v;
const zClamp = Math.min(Math.max(z, -1), 1);
const lat = radiansToDegrees(Math.asin(zClamp));
const lng = radiansToDegrees(Math.atan2(y, x));
return [lng, lat];
}
function nearestPointOnSegment(posA, posB, posC) {
const A = lngLatToVector(posA);
const B2 = lngLatToVector(posB);
const C = lngLatToVector(posC);
const segmentAxis = cross$1(A, B2);
if (segmentAxis[0] === 0 && segmentAxis[1] === 0 && segmentAxis[2] === 0) {
if (dot(A, B2) > 0) {
return [[...posB], true];
} else {
return [[...posC], false];
}
}
const targetAxis = cross$1(segmentAxis, C);
if (targetAxis[0] === 0 && targetAxis[1] === 0 && targetAxis[2] === 0) {
return [[...posB], true];
}
const intersectionAxis = cross$1(targetAxis, segmentAxis);
const I1 = normalize(intersectionAxis);
const I2 = [-I1[0], -I1[1], -I1[2]];
const I = dot(C, I1) > dot(C, I2) ? I1 : I2;
const segmentAxisNorm = normalize(segmentAxis);
const cmpAI = dot(cross$1(A, I), segmentAxisNorm);
const cmpIB = dot(cross$1(I, B2), segmentAxisNorm);
if (cmpAI >= 0 && cmpIB >= 0) {
return [vectorToLngLat(I), false];
}
if (dot(A, C) > dot(B2, C)) {
return [[...posA], false];
} else {
return [[...posB], true];
}
}
function featureEach$1(geojson, callback) {
if (geojson.type === "Feature") {
callback(geojson, 0);
} else if (geojson.type === "FeatureCollection") {
for (var i = 0; i < geojson.features.length; i++) {
if (callback(geojson.features[i], i) === false) break;
}
}
}
function featureReduce(geojson, callback, initialValue) {
var previousValue = initialValue;
featureEach$1(geojson, function(currentFeature, featureIndex) {
if (featureIndex === 0 && initialValue === void 0)
previousValue = currentFeature;
else previousValue = callback(previousValue, currentFeature, featureIndex);
});
return previousValue;
}
function geomEach$1(geojson, callback) {
var i, j, g, geometry, stopG, geometryMaybeCollection, isGeometryCollection, featureProperties, featureBBox, featureId2, featureIndex = 0, isFeatureCollection = geojson.type === "FeatureCollection", isFeature = geojson.type === "Feature", stop = isFeatureCollection ? geojson.features.length : 1;
for (i = 0; i < stop; i++) {
geometryMaybeCollection = isFeatureCollection ? (
// @ts-expect-error: Known type conflict
geojson.features[i].geometry
) : isFeature ? (
// @ts-expect-error: Known type conflict
geojson.geometry
) : geojson;
featureProperties = isFeatureCollection ? (
// @ts-expect-error: Known type conflict
geojson.features[i].properties
) : isFeature ? (
// @ts-expect-error: Known type conflict
geojson.properties
) : {};
featureBBox = isFeatureCollection ? (
// @ts-expect-error: Known type conflict
geojson.features[i].bbox
) : isFeature ? (
// @ts-expect-error: Known type conflict
geojson.bbox
) : void 0;
featureId2 = isFeatureCollection ? (
// @ts-expect-error: Known type conflict
geojson.features[i].id
) : isFeature ? (
// @ts-expect-error: Known type conflict
geojson.id
) : void 0;
isGeometryCollection = geometryMaybeCollection ? geometryMaybeCollection.type === "GeometryCollection" : false;
stopG = isGeometryCollection ? geometryMaybeCollection.geometries.length : 1;
for (g = 0; g < stopG; g++) {
geometry = isGeometryCollection ? geometryMaybeCollection.geometries[g] : geometryMaybeCollection;
if (geometry === null) {
if (
// @ts-expect-error: Known type conflict
callback(
// @ts-expect-error: Known type conflict
null,
featureIndex,
featureProperties,
featureBBox,
featureId2
) === false
)
return false;
continue;
}
switch (geometry.type) {
case "Point":
case "LineString":
case "MultiPoint":
case "Polygon":
case "MultiLineString":
case "MultiPolygon": {
if (
// @ts-expect-error: Known type conflict
callback(
geometry,
featureIndex,
featureProperties,
featureBBox,
featureId2
) === false
)
return false;
break;
}
case "GeometryCollection": {
for (j = 0; j < geometry.geometries.length; j++) {
if (
// @ts-expect-error: Known type conflict
callback(
geometry.geometries[j],
featureIndex,
featureProperties,
featureBBox,
featureId2
) === false
)
return false;
}
break;
}
default:
throw new Error("Unknown Geometry Type");
}
}
featureIndex++;
}
}
function flattenEach$1(geojson, callback) {
geomEach$1(geojson, function(geometry, featureIndex, properties, bbox2, id) {
var type = geometry === null ? null : geometry.type;
switch (type) {
case null:
case "Point":
case "LineString":
case "Polygon":
if (
// @ts-expect-error: Known type conflict
callback(
feature$1(geometry, properties, { bbox: bbox2, id }),
featureIndex,
0
) === false
)
return false;
return;
}
var geomType;
switch (type) {
case "MultiPoint":
geomType = "Point";
break;
case "MultiLineString":
geomType = "LineString";
break;
case "MultiPolygon":
geomType = "Polygon";
break;
}
for (
var multiFeatureIndex = 0;
// @ts-expect-error: Known type conflict
multiFeatureIndex < geometry.coordinates.length;
multiFeatureIndex++
) {
var coordinate = geometry.coordinates[multiFeatureIndex];
var geom = {
type: geomType,
coordinates: coordinate
};
if (
// @ts-expect-error: Known type conflict
callback(feature$1(geom, properties), featureIndex, multiFeatureIndex) === false
)
return false;
}
});
}
function lineSplit(line, splitter2) {
if (!line) throw new Error("line is required");
if (!splitter2) throw new Error("splitter is required");
const lineType = getType(line);
const splitterType = getType(splitter2);
if (lineType !== "LineString") throw new Error("line must be LineString");
if (splitterType === "FeatureCollection")
throw new Error("splitter cannot be a FeatureCollection");
if (splitterType === "GeometryCollection")
throw new Error("splitter cannot be a GeometryCollection");
var truncatedSplitter = truncate(splitter2, { precision: 7 });
if (line.type !== "Feature") {
line = feature$1(line);
}
switch (splitterType) {
case "Point":
return splitLineWithPoint(
line,
truncatedSplitter
);
case "MultiPoint":
return splitLineWithPoints(
line,
truncatedSplitter
);
case "LineString":
case "MultiLineString":
case "Polygon":
case "MultiPolygon":
return splitLineWithPoints(
line,
lineIntersect(
line,
truncatedSplitter,
{
ignoreSelfIntersections: true
}
)
);
}
}
function splitLineWithPoints(line, splitter2) {
var results = [];
var tree = geojsonRbush();
flattenEach$1(
splitter2,
// this cast should be unnecessary (and is wrong, it could contain MultiPoints), but is a workaround for bad flattenEach typings
function(point2) {
results.forEach(function(feature2, index) {
feature2.id = index;
});
if (!results.length) {
results = splitLineWithPoint(line, point2).features;
tree.load(featureCollection(results));
} else {
var search = tree.search(point2);
if (search.features.length) {
var closestLine = findClosestFeature(point2, search);
results = results.filter(function(feature2) {
return feature2.id !== closestLine.id;
});
tree.remove(closestLine);
featureEach$1(splitLineWithPoint(closestLine, point2), function(line2) {
results.push(line2);
tree.insert(line2);
});
}
}
}
);
return featureCollection(results);
}
function splitLineWithPoint(line, splitter2) {
var results = [];
var startPoint = getCoords(line)[0];
var endPoint = getCoords(line)[line.geometry.coordinates.length - 1];
if (pointsEquals(startPoint, getCoord(splitter2)) || pointsEquals(endPoint, getCoord(splitter2)))
return featureCollection([line]);
var tree = geojsonRbush();
var segments = lineSegment(line);
tree.load(segments);
var search = tree.search(splitter2);
if (!search.features.length) return featureCollection([line]);
var closestSegment = findClosestFeature(splitter2, search);
var initialValue = [startPoint];
var lastCoords = featureReduce(
segments,
function(previous, current, index) {
var currentCoords = getCoords(current)[1];
var splitterCoords = getCoord(splitter2);
if (index === closestSegment.id) {
previous.push(splitterCoords);
results.push(lineString(previous));
if (pointsEquals(splitterCoords, currentCoords))
return [splitterCoords];
return [splitterCoords, currentCoords];
} else {
previous.push(currentCoords);
return previous;
}
},
initialValue
);
if (lastCoords.length > 1) {
results.push(lineString(lastCoords));
}
return featureCollection(results);
}
function findClosestFeature(point2, lines) {
if (!lines.features.length) throw new Error("lines must contain features");
if (lines.features.length === 1) return lines.features[0];
var closestFeature;
var closestDistance = Infinity;
featureEach$1(lines, function(segment) {
var pt = nearestPointOnLine(segment, point2);
var dist = pt.properties.dist;
if (dist < closestDistance) {
closestFeature = segment;
closestDistance = dist;
}
});
return closestFeature;
}
function pointsEquals(pt1, pt2) {
return pt1[0] === pt2[0] && pt1[1] === pt2[1];
}
function booleanWithin(feature1, feature2) {
var geom1 = getGeom(feature1);
var geom2 = getGeom(feature2);
var type1 = geom1.type;
var type2 = geom2.type;
switch (type1) {
case "Point":
switch (type2) {
case "MultiPoint":
return isPointInMultiPoint(geom1, geom2);
case "LineString":
return booleanPointOnLine(geom1, geom2, { ignoreEndVertices: true });
case "Polygon":
case "MultiPolygon":
return booleanPointInPolygon(geom1, geom2, { ignoreBoundary: true });
default:
throw new Error("feature2 " + type2 + " geometry not supported");
}
case "MultiPoint":
switch (type2) {
case "MultiPoint":
return isMultiPointInMultiPoint(geom1, geom2);
case "LineString":
return isMultiPointOnLine(geom1, geom2);
case "Polygon":
case "MultiPolygon":
return isMultiPointInPoly(geom1, geom2);
default:
throw new Error("feature2 " + type2 + " geometry not supported");
}
case "LineString":
switch (type2) {
case "LineString":
return isLineOnLine(geom1, geom2);
case "Polygon":
case "MultiPolygon":
return isLineInPoly(geom1, geom2);
default:
throw new Error("feature2 " + type2 + " geometry not supported");
}
case "Polygon":
switch (type2) {
case "Polygon":
case "MultiPolygon":
return isPolyInPoly(geom1, geom2);
default:
throw new Error("feature2 " + type2 + " geometry not supported");
}
default:
throw new Error("feature1 " + type1 + " geometry not supported");
}
}
function isPointInMultiPoint(point2, multiPoint) {
var i;
var output = false;
for (i = 0; i < multiPoint.coordinates.length; i++) {
if (compareCoords(multiPoint.coordinates[i], point2.coordinates)) {
output = true;
break;
}
}
return output;
}
function isMultiPointInMultiPoint(multiPoint1, multiPoint2) {
for (var i = 0; i < multiPoint1.coordinates.length; i++) {
var anyMatch = false;
for (var i2 = 0; i2 < multiPoint2.coordinates.length; i2++) {
if (compareCoords(multiPoint1.coordinates[i], multiPoint2.coordinates[i2])) {
anyMatch = true;
}
}
if (!anyMatch) {
return false;
}
}
return true;
}
function isMultiPointOnLine(multiPoint, lineString2) {
var foundInsidePoint = false;
for (var i = 0; i < multiPoint.coordinates.length; i++) {
if (!booleanPointOnLine(multiPoint.coordinates[i], lineString2)) {
return false;
}
if (!foundInsidePoint) {
foundInsidePoint = booleanPointOnLine(
multiPoint.coordinates[i],
lineString2,
{ ignoreEndVertices: true }
);
}
}
return foundInsidePoint;
}
function isMultiPointInPoly(multiPoint, polygon2) {
var output = true;
var isInside = false;
for (var i = 0; i < multiPoint.coordinates.length; i++) {
isInside = booleanPointInPolygon(multiPoint.coordinates[i], polygon2);
if (!isInside) {
output = false;
break;
}
{
isInside = booleanPointInPolygon(multiPoint.coordinates[i], polygon2, {
ignoreBoundary: true
});
}
}
return output && isInside;
}
function isLineOnLine(lineString1, lineString2) {
for (var i = 0; i < lineString1.coordinates.length; i++) {
if (!booleanPointOnLine(lineString1.coordinates[i], lineString2)) {
return false;
}
}
return true;
}
function splitLineIntoSegmentsOnPolygon(linestring, polygon2) {
const coords = linestring.coordinates;
const outputSegments = [];
for (let i = 0; i < coords.length - 1; i++) {
const seg = lineString([coords[i], coords[i + 1]]);
const split = lineSplit(seg, feature$1(polygon2));
if (split.features.length === 0) {
outputSegments.push(seg);
} else {
outputSegments.push(...split.features);
}
}
return featureCollection(outputSegments);
}
function isLineInPoly(linestring, polygon2) {
const polyBbox = bbox$1(polygon2);
const lineBbox = bbox$1(linestring);
if (!doBBoxOverlap(polyBbox, lineBbox)) {
return false;
}
for (const coord of linestring.coordinates) {
if (!booleanPointInPolygon(coord, polygon2)) {
return false;
}
}
let isContainedByPolygonBoundary = false;
const lineSegments = splitLineIntoSegmentsOnPolygon(linestring, polygon2);
for (const lineSegment2 of lineSegments.features) {
const midpoint2 = getMidpoint(
lineSegment2.geometry.coordinates[0],
lineSegment2.geometry.coordinates[1]
);
if (!booleanPointInPolygon(midpoint2, polygon2)) {
return false;
}
if (!isContainedByPolygonBoundary && booleanPointInPolygon(midpoint2, polygon2, { ignoreBoundary: true })) {
isContainedByPolygonBoundary = true;
}
}
return isContainedByPolygonBoundary;
}
function isPolyInPoly(geometry1, geometry2) {
var poly1Bbox = bbox$1(geometry1);
var poly2Bbox = bbox$1(geometry2);
if (!doBBoxOverlap(poly2Bbox, poly1Bbox)) {
return false;
}
for (var i = 0; i < geometry1.coordinates[0].length; i++) {
if (!booleanPointInPolygon(geometry1.coordinates[0][i], geometry2)) {
return false;
}
}
return true;
}
function doBBoxOverlap(bbox1, bbox2) {
if (bbox1[0] > bbox2[0]) return false;
if (bbox1[2] < bbox2[2]) return false;
if (bbox1[1] > bbox2[1]) return false;
if (bbox1[3] < bbox2[3]) return false;
return true;
}
function compareCoords(pair1, pair2) {
return pair1[0] === pair2[0] && pair1[1] === pair2[1];
}
function getMidpoint(pair1, pair2) {
return [(pair1[0] + pair2[0]) / 2, (pair1[1] + pair2[1]) / 2];
}
var index_default$4 = booleanWithin;
var __defProp$1 = Object.defineProperty;
var __name = (target, value) => __defProp$1(target, "name", { value, configurable: true });
var _GeojsonEquality = class _GeojsonEquality2 {
constructor(opts) {
this.direction = false;
this.compareProperties = true;
var _a2, _b2, _c;
this.precision = 10 ** -((_a2 = opts == null ? void 0 : opts.precision) != null ? _a2 : 17);
this.direction = (_b2 = opts == null ? void 0 : opts.direction) != null ? _b2 : false;
this.compareProperties = (_c = opts == null ? void 0 : opts.compareProperties) != null ? _c : true;
}
compare(g1, g2) {
if (g1.type !== g2.type) {
return false;
}
if (!sameLength(g1, g2)) {
return false;
}
switch (g1.type) {
case "Point":
return this.compareCoord(g1.coordinates, g2.coordinates);
case "LineString":
return this.compareLine(g1.coordinates, g2.coordinates);
case "Polygon":
return this.comparePolygon(g1, g2);
case "GeometryCollection":
return this.compareGeometryCollection(g1, g2);
case "Feature":
return this.compareFeature(g1, g2);
case "FeatureCollection":
return this.compareFeatureCollection(g1, g2);
default:
if (g1.type.startsWith("Multi")) {
const g1s = explode(g1);
const g2s = explode(
g2
);
return g1s.every(
(g1part) => g2s.some((g2part) => this.compare(g1part, g2part))
);
}
}
return false;
}
compareCoord(c1, c2) {
return c1.length === c2.length && c1.every((c, i) => Math.abs(c - c2[i]) < this.precision);
}
compareLine(path1, path2, ind = 0, isPoly = false) {
if (!sameLength(path1, path2)) {
return false;
}
const p1 = path1;
let p2 = path2;
if (isPoly && !this.compareCoord(p1[0], p2[0])) {
const startIndex = this.fixStartIndex(p2, p1);
if (!startIndex) {
return false;
} else {
p2 = startIndex;
}
}
const sameDirection = this.compareCoord(p1[ind], p2[ind]);
if (this.direction || sameDirection) {
return this.comparePath(p1, p2);
} else {
if (this.compareCoord(p1[ind], p2[p2.length - (1 + ind)])) {
return this.comparePath(p1.slice().reverse(), p2);
}
return false;
}
}
fixStartIndex(sourcePath, targetPath) {
let correctPath, ind = -1;
for (let i = 0; i < sourcePath.length; i++) {
if (this.compareCoord(sourcePath[i], targetPath[0])) {
ind = i;
break;
}
}
if (ind >= 0) {
correctPath = [].concat(
sourcePath.slice(ind, sourcePath.length),
sourcePath.slice(1, ind + 1)
);
}
return correctPath;
}
comparePath(p1, p2) {
return p1.every((c, i) => this.compareCoord(c, p2[i]));
}
comparePolygon(g1, g2) {
if (this.compareLine(g1.coordinates[0], g2.coordinates[0], 1, true)) {
const holes1 = g1.coordinates.slice(1, g1.coordinates.length);
const holes2 = g2.coordinates.slice(1, g2.coordinates.length);
return holes1.every(
(h1) => holes2.some((h2) => this.compareLine(h1, h2, 1, true))
);
}
return false;
}
compareGeometryCollection(g1, g2) {
return sameLength(g1.geometries, g2.geometries) && this.compareBBox(g1, g2) && g1.geometries.every((g, i) => this.compare(g, g2.geometries[i]));
}
compareFeature(g1, g2) {
return g1.id === g2.id && (this.compareProperties ? equal(g1.properties, g2.properties) : true) && this.compareBBox(g1, g2) && this.compare(g1.geometry, g2.geometry);
}
compareFeatureCollection(g1, g2) {
return sameLength(g1.features, g2.features) && this.compareBBox(g1, g2) && g1.features.every((f, i) => this.compare(f, g2.features[i]));
}
compareBBox(g1, g2) {
return Boolean(!g1.bbox && !g2.bbox) || (g1.bbox && g2.bbox ? this.compareCoord(g1.bbox, g2.bbox) : false);
}
};
__name(_GeojsonEquality, "GeojsonEquality");
var GeojsonEquality = _GeojsonEquality;
function sameLength(g1, g2) {
return g1.coordinates ? g1.coordinates.length === g2.coordinates.length : g1.length === g2.length;
}
__name(sameLength, "sameLength");
function explode(g) {
return g.coordinates.map((part) => ({
type: g.type.replace("Multi", ""),
coordinates: part
}));
}
__name(explode, "explode");
function geojsonEquality(g1, g2, opts) {
const eq = new GeojsonEquality(opts);
return eq.compare(g1, g2);
}
__name(geojsonEquality, "geojsonEquality");
function equal(object1, object2) {
if (object1 === null && object2 === null) {
return true;
}
if (object1 === null || object2 === null) {
return false;
}
const objKeys1 = Object.keys(object1);
const objKeys2 = Object.keys(object2);
if (objKeys1.length !== objKeys2.length) return false;
for (var key of objKeys1) {
const value1 = object1[key];
const value2 = object2[key];
const isObjects = isObject(value1) && isObject(value2);
if (isObjects && !equal(value1, value2) || !isObjects && value1 !== value2) {
return false;
}
}
return true;
}
__name(equal, "equal");
var isObject = /* @__PURE__ */ __name((object) => {
return object != null && typeof object === "object";
}, "isObject");
function booleanEqual(feature1, feature2, options = {}) {
let precision2 = options.precision;
precision2 = precision2 === void 0 || precision2 === null || isNaN(precision2) ? 6 : precision2;
if (typeof precision2 !== "number" || !(precision2 >= 0)) {
throw new Error("precision must be a positive number");
}
const type1 = getGeom(feature1).type;
const type2 = getGeom(feature2).type;
if (type1 !== type2) return false;
return geojsonEquality(cleanCoords(feature1), cleanCoords(feature2), {
precision: precision2
});
}
var index_default$3 = booleanEqual;
function featureEach(geojson, callback) {
if (geojson.type === "Feature") {
callback(geojson, 0);
} else if (geojson.type === "FeatureCollection") {
for (var i = 0; i < geojson.features.length; i++) {
if (callback(geojson.features[i], i) === false) break;
}
}
}
var __defProp2 = Object.defineProperty;
var __defProps = Object.defineProperties;
var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
var __getOwnPropSymbols = Object.getOwnPropertySymbols;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __propIsEnum = Object.prototype.propertyIsEnumerable;
var __defNormalProp2 = (obj, key, value) => key in obj ? __defProp2(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
var __spreadValues = (a, b) => {
for (var prop in b || (b = {}))
if (__hasOwnProp.call(b, prop))
__defNormalProp2(a, prop, b[prop]);
if (__getOwnPropSymbols)
for (var prop of __getOwnPropSymbols(b)) {
if (__propIsEnum.call(b, prop))
__defNormalProp2(a, prop, b[prop]);
}
return a;
};
var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
function nearestPoint(targetPoint, points, options = {}) {
if (!targetPoint) throw new Error("targetPoint is required");
if (!points) throw new Error("points is required");
let minDist = Infinity;
let bestFeatureIndex = 0;
featureEach(points, (pt, featureIndex) => {
const distanceToPoint = distance(targetPoint, pt, options);
if (distanceToPoint < minDist) {
bestFeatureIndex = featureIndex;
minDist = distanceToPoint;
}
});
const nearestPoint2 = clone(points.features[bestFeatureIndex]);
return __spreadProps(__spreadValues({}, nearestPoint2), {
properties: __spreadProps(__spreadValues({}, nearestPoint2.properties), {
featureIndex: bestFeatureIndex,
distanceToPoint: minDist
})
});
}
var index_default$2 = nearestPoint;
var Spline = class {
constructor(options) {
this.points = options.points || [];
this.duration = options.duration || 1e4;
this.sharpness = options.sharpness || 0.85;
this.centers = [];
this.controls = [];
this.stepLength = options.stepLength || 60;
this.length = this.points.length;
this.delay = 0;
for (let i = 0; i < this.length; i++) {
this.points[i].z = this.points[i].z || 0;
}
for (let i = 0; i < this.length - 1; i++) {
const p1 = this.points[i];
const p2 = this.points[i + 1];
this.centers.push({
x: (p1.x + p2.x) / 2,
y: (p1.y + p2.y) / 2,
z: (p1.z + p2.z) / 2
});
}
this.controls.push([this.points[0], this.points[0]]);
for (let i = 0; i < this.centers.length - 1; i++) {
const dx = this.points[i + 1].x - (this.centers[i].x + this.centers[i + 1].x) / 2;
const dy = this.points[i + 1].y - (this.centers[i].y + this.centers[i + 1].y) / 2;
const dz = this.points[i + 1].z - (this.centers[i].y + this.centers[i + 1].z) / 2;
this.controls.push([
{
x: (1 - this.sharpness) * this.points[i + 1].x + this.sharpness * (this.centers[i].x + dx),
y: (1 - this.sharpness) * this.points[i + 1].y + this.sharpness * (this.centers[i].y + dy),
z: (1 - this.sharpness) * this.points[i + 1].z + this.sharpness * (this.centers[i].z + dz)
},
{
x: (1 - this.sharpness) * this.points[i + 1].x + this.sharpness * (this.centers[i + 1].x + dx),
y: (1 - this.sharpness) * this.points[i + 1].y + this.sharpness * (this.centers[i + 1].y + dy),
z: (1 - this.sharpness) * this.points[i + 1].z + this.sharpness * (this.centers[i + 1].z + dz)
}
]);
}
this.controls.push([
this.points[this.length - 1],
this.points[this.length - 1]
]);
this.steps = this.cacheSteps(this.stepLength);
return this;
}
/**
* Caches an array of equidistant (more or less) points on the curve.
*/
cacheSteps(mindist) {
const steps = [];
let laststep = this.pos(0);
steps.push(0);
for (let t = 0; t < this.duration; t += 10) {
const step = this.pos(t);
const dist = Math.sqrt(
(step.x - laststep.x) * (step.x - laststep.x) + (step.y - laststep.y) * (step.y - laststep.y) + (step.z - laststep.z) * (step.z - laststep.z)
);
if (dist > mindist) {
steps.push(t);
laststep = step;
}
}
return steps;
}
/**
* returns angle and speed in the given point in the curve
*/
vector(t) {
const p1 = this.pos(t + 10);
const p2 = this.pos(t - 10);
return {
angle: 180 * Math.atan2(p1.y - p2.y, p1.x - p2.x) / 3.14,
speed: Math.sqrt(
(p2.x - p1.x) * (p2.x - p1.x) + (p2.y - p1.y) * (p2.y - p1.y) + (p2.z - p1.z) * (p2.z - p1.z)
)
};
}
/**
* Gets the position of the point, given time.
*
* WARNING: The speed is not constant. The time it takes between control points is constant.
*
* For constant speed, use Spline.steps[i];
*/
pos(time) {
let t = time - this.delay;
if (t < 0) {
t = 0;
}
if (t > this.duration) {
t = this.duration - 1;
}
const t2 = t / this.duration;
if (t2 >= 1) {
return this.points[this.length - 1];
}
const n = Math.floor((this.points.length - 1) * t2);
const t1 = (this.length - 1) * t2 - n;
return bezier(
t1,
this.points[n],
this.controls[n][1],
this.controls[n + 1][0],
this.points[n + 1]
);
}
};
function bezier(t, p1, c1, c2, p2) {
const b = B(t);
const pos = {
x: p2.x * b[0] + c2.x * b[1] + c1.x * b[2] + p1.x * b[3],
y: p2.y * b[0] + c2.y * b[1] + c1.y * b[2] + p1.y * b[3],
z: p2.z * b[0] + c2.z * b[1] + c1.z * b[2] + p1.z * b[3]
};
return pos;
}
function B(t) {
const t2 = t * t;
const t3 = t2 * t;
return [
t3,
3 * t2 * (1 - t),
3 * t * (1 - t) * (1 - t),
(1 - t) * (1 - t) * (1 - t)
];
}
function bezierSpline(line, options = {}) {
const resolution = options.resolution || 1e4;
const sharpness = options.sharpness || 0.85;
const coords = [];
const points = getGeom(line).coordinates.map((pt) => {
return { x: pt[0], y: pt[1] };
});
const spline = new Spline({
duration: resolution,
points,
sharpness
});
const pushCoord = (time) => {
var pos = spline.pos(time);
if (Math.floor(time / 100) % 2 === 0) {
coords.push([pos.x, pos.y]);
}
};
for (var i = 0; i < spline.duration; i += 10) {
pushCoord(i);
}
pushCoord(spline.duration);
return lineString(coords, options.properties);
}
var index_default$1 = bezierSpline;
function coordEach(geojson, callback, excludeWrapCoord) {
if (geojson === null) return;
var j, k, l, geometry, stopG, coords, geometryMaybeCollection, wrapShrink = 0, coordIndex = 0, isGeometryCollection, type = geojson.type, isFeatureCollection = type === "FeatureCollection", isFeature = type === "Feature", stop = isFeatureCollection ? geojson.features.length : 1;
for (var featureIndex = 0; featureIndex < stop; featureIndex++) {
geometryMaybeCollection = isFeatureCollection ? (
// @ts-expect-error: Known type conflict
geojson.features[featureIndex].geometry
) : isFeature ? (
// @ts-expect-error: Known type conflict
geojson.geometry
) : geojson;
isGeometryCollection = geometryMaybeCollection ? geometryMaybeCollection.type === "GeometryCollection" : false;
stopG = isGeometryCollection ? geometryMaybeCollection.geometries.length : 1;
for (var geomIndex = 0; geomIndex < stopG; geomIndex++) {
var multiFeatureIndex = 0;
var geometryIndex = 0;
geometry = isGeometryCollection ? geometryMaybeCollection.geometries[geomIndex] : geometryMaybeCollection;
if (geometry === null) continue;
coords = geometry.coordinates;
var geomType = geometry.type;
wrapShrink = 0;
switch (geomType) {
case null:
break;
case "Point":
if (
// @ts-expect-error: Known type conflict
callback(
coords,
coordIndex,
featureIndex,
multiFeatureIndex,
geometryIndex
) === false
)
return false;
coordIndex++;
multiFeatureIndex++;
break;
case "LineString":
case "MultiPoint":
for (j = 0; j < coords.length; j++) {
if (
// @ts-expect-error: Known type conflict
callback(
coords[j],
coordIndex,
featureIndex,
multiFeatureIndex,
geometryIndex
) === false
)
return false;
coordIndex++;
if (geomType === "MultiPoint") multiFeatureIndex++;
}
if (geomType === "LineString") multiFeatureIndex++;
break;
case "Polygon":
case "MultiLineString":
for (j = 0; j < coords.length; j++) {
for (k = 0; k < coords[j].length - wrapShrink; k++) {
if (
// @ts-expect-error: Known type conflict
callback(
coords[j][k],
coordIndex,
featureIndex,
multiFeatureIndex,
geometryIndex
) === false
)
return false;
coordIndex++;
}
if (geomType === "MultiLineString") multiFeatureIndex++;
if (geomType === "Polygon") geometryIndex++;
}
if (geomType === "Polygon") multiFeatureIndex++;
break;
case "MultiPolygon":
for (j = 0; j < coords.length; j++) {
geometryIndex = 0;
for (k = 0; k < coords[j].length; k++) {
for (l = 0; l < coords[j][k].length - wrapShrink; l++) {
if (
// @ts-expect-error: Known type conflict
callback(
coords[j][k][l],
coordIndex,
featureIndex,
multiFeatureIndex,
geometryIndex
) === false
)
return false;
coordIndex++;
}
geometryIndex++;
}
multiFeatureIndex++;
}
break;
case "GeometryCollection":
for (j = 0; j < geometry.geometries.length; j++)
if (
// @ts-expect-error: Known type conflict
coordEach(geometry.geometries[j], callback) === false
)
return false;
break;
default:
throw new Error("Unknown Geometry Type");
}
}
}
}
function geomEach(geojson, callback) {
var i, j, g, geometry, stopG, geometryMaybeCollection, isGeometryCollection, featureProperties, featureBBox, featureId2, featureIndex = 0, isFeatureCollection = geojson.type === "FeatureCollection", isFeature = geojson.type === "Feature", stop = isFeatureCollection ? geojson.features.length : 1;
for (i = 0; i < stop; i++) {
geometryMaybeCollection = isFeatureCollection ? (
// @ts-expect-error: Known type conflict
geojson.features[i].geometry
) : isFeature ? (
// @ts-expect-error: Known type conflict
geojson.geometry
) : geojson;
featureProperties = isFeatureCollection ? (
// @ts-expect-error: Known type conflict
geojson.features[i].properties
) : isFeature ? (
// @ts-expect-error: Known type conflict
geojson.properties
) : {};
featureBBox = isFeatureCollection ? (
// @ts-expect-error: Known type conflict
geojson.features[i].bbox
) : isFeature ? (
// @ts-expect-error: Known type conflict
geojson.bbox
) : void 0;
featureId2 = isFeatureCollection ? (
// @ts-expect-error: Known type conflict
geojson.features[i].id
) : isFeature ? (
// @ts-expect-error: Known type conflict
geojson.id
) : void 0;
isGeometryCollection = geometryMaybeCollection ? geometryMaybeCollection.type === "GeometryCollection" : false;
stopG = isGeometryCollection ? geometryMaybeCollection.geometries.length : 1;
for (g = 0; g < stopG; g++) {
geometry = isGeometryCollection ? geometryMaybeCollection.geometries[g] : geometryMaybeCollection;
if (geometry === null) {
if (
// @ts-expect-error: Known type conflict
callback(
// @ts-expect-error: Known type conflict
null,
featureIndex,
featureProperties,
featureBBox,
featureId2
) === false
)
return false;
continue;
}
switch (geometry.type) {
case "Point":
case "LineString":
case "MultiPoint":
case "Polygon":
case "MultiLineString":
case "MultiPolygon": {
if (
// @ts-expect-error: Known type conflict
callback(
geometry,
featureIndex,
featureProperties,
featureBBox,
featureId2
) === false
)
return false;
break;
}
case "GeometryCollection": {
for (j = 0; j < geometry.geometries.length; j++) {
if (
// @ts-expect-error: Known type conflict
callback(
geometry.geometries[j],
featureIndex,
featureProperties,
featureBBox,
featureId2
) === false
)
return false;
}
break;
}
default:
throw new Error("Unknown Geometry Type");
}
}
featureIndex++;
}
}
function flattenEach(geojson, callback) {
geomEach(geojson, function(geometry, featureIndex, properties, bbox2, id) {
var type = geometry === null ? null : geometry.type;
switch (type) {
case null:
case "Point":
case "LineString":
case "Polygon":
if (
// @ts-expect-error: Known type conflict
callback(
feature$1(geometry, properties, { bbox: bbox2, id }),
featureIndex,
0
) === false
)
return false;
return;
}
var geomType;
switch (type) {
case "MultiPoint":
geomType = "Point";
break;
case "MultiLineString":
geomType = "LineString";
break;
case "MultiPolygon":
geomType = "Polygon";
break;
}
for (
var multiFeatureIndex = 0;
// @ts-expect-error: Known type conflict
multiFeatureIndex < geometry.coordinates.length;
multiFeatureIndex++
) {
var coordinate = geometry.coordinates[multiFeatureIndex];
var geom = {
type: geomType,
coordinates: coordinate
};
if (
// @ts-expect-error: Known type conflict
callback(feature$1(geom, properties), featureIndex, multiFeatureIndex) === false
)
return false;
}
});
}
function segmentEach(geojson, callback) {
flattenEach(geojson, function(feature2, featureIndex, multiFeatureIndex) {
var segmentIndex = 0;
if (!feature2.geometry) return;
var type = feature2.geometry.type;
if (type === "Point" || type === "MultiPoint") return;
var previousCoords;
var previousFeatureIndex = 0;
var previousMultiIndex = 0;
var prevGeomIndex = 0;
if (
// @ts-expect-error: Known type conflict
coordEach(
feature2,
function(currentCoord, coordIndex, featureIndexCoord, multiPartIndexCoord, geometryIndex) {
if (
// @ts-expect-error: Known type conflict
previousCoords === void 0 || featureIndex > previousFeatureIndex || multiPartIndexCoord > previousMultiIndex || geometryIndex > prevGeomIndex
) {
previousCoords = currentCoord;
previousFeatureIndex = featureIndex;
previousMultiIndex = multiPartIndexCoord;
prevGeomIndex = geometryIndex;
segmentIndex = 0;
return;
}
var currentSegment = lineString(
// @ts-expect-error: Known type conflict
[previousCoords, currentCoord],
feature2.properties
);
if (
// @ts-expect-error: Known type conflict
callback(
// @ts-expect-error: Known type conflict
currentSegment,
featureIndex,
multiFeatureIndex,
geometryIndex,
segmentIndex
) === false
)
return false;
segmentIndex++;
previousCoords = currentCoord;
}
) === false
)
return false;
});
}
function segmentReduce(geojson, callback, initialValue) {
var previousValue = initialValue;
var started = false;
segmentEach(
geojson,
function(currentSegment, featureIndex, multiFeatureIndex, geometryIndex, segmentIndex) {
if (started === false && initialValue === void 0)
previousValue = currentSegment;
else
previousValue = callback(
previousValue,
// @ts-expect-error: Known type conflict
currentSegment,
featureIndex,
multiFeatureIndex,
geometryIndex,
segmentIndex
);
started = true;
}
);
return previousValue;
}
function length(geojson, options = {}) {
return segmentReduce(
geojson,
(previousValue, segment) => {
const coords = segment.geometry.coordinates;
return previousValue + distance(coords[0], coords[1], options);
},
0
);
}
var index_default = length;
function quickselect(arr, k, left = 0, right = arr.length - 1, compare2 = defaultCompare) {
while (right > left) {
if (right - left > 600) {
const n = right - left + 1;
const m = k - left + 1;
const z = Math.log(n);
const s = 0.5 * Math.exp(2 * z / 3);
const sd = 0.5 * Math.sqrt(z * s * (n - s) / n) * (m - n / 2 < 0 ? -1 : 1);
const newLeft = Math.max(left, Math.floor(k - m * s / n + sd));
const newRight = Math.min(right, Math.floor(k + (n - m) * s / n + sd));
quickselect(arr, k, newLeft, newRight, compare2);
}
const t = arr[k];
let i = left;
let j = right;
swap(arr, left, k);
if (compare2(arr[right], t) > 0) swap(arr, left, right);
while (i < j) {
swap(arr, i, j);
i++;
j--;
while (compare2(arr[i], t) < 0) i++;
while (compare2(arr[j], t) > 0) j--;
}
if (compare2(arr[left], t) === 0) swap(arr, left, j);
else {
j++;
swap(arr, j, right);
}
if (j <= k) left = j + 1;
if (k <= j) right = j - 1;
}
}
function swap(arr, i, j) {
const tmp = arr[i];
arr[i] = arr[j];
arr[j] = tmp;
}
function defaultCompare(a, b) {
return a < b ? -1 : a > b ? 1 : 0;
}
class RBush3 {
constructor(maxEntries = 9) {
this._maxEntries = Math.max(4, maxEntries);
this._minEntries = Math.max(2, Math.ceil(this._maxEntries * 0.4));
this.clear();
}
all() {
return this._all(this.data, []);
}
search(bbox2) {
let node = this.data;
const result = [];
if (!intersects$1(bbox2, node)) return result;
const toBBox2 = this.toBBox;
const nodesToSearch = [];
while (node) {
for (let i = 0; i < node.children.length; i++) {
const child = node.children[i];
const childBBox = node.leaf ? toBBox2(child) : child;
if (intersects$1(bbox2, childBBox)) {
if (node.leaf) result.push(child);
else if (contains(bbox2, childBBox)) this._all(child, result);
else nodesToSearch.push(child);
}
}
node = nodesToSearch.pop();
}
return result;
}
collides(bbox2) {
let node = this.data;
if (!intersects$1(bbox2, node)) return false;
const nodesToSearch = [];
while (node) {
for (let i = 0; i < node.children.length; i++) {
const child = node.children[i];
const childBBox = node.leaf ? this.toBBox(child) : child;
if (intersects$1(bbox2, childBBox)) {
if (node.leaf || contains(bbox2, childBBox)) return true;
nodesToSearch.push(child);
}
}
node = nodesToSearch.pop();
}
return false;
}
load(data) {
if (!(data && data.length)) return this;
if (data.length < this._minEntries) {
for (let i = 0; i < data.length; i++) {
this.insert(data[i]);
}
return this;
}
let node = this._build(data.slice(), 0, data.length - 1, 0);
if (!this.data.children.length) {
this.data = node;
} else if (this.data.height === node.height) {
this._splitRoot(this.data, node);
} else {
if (this.data.height < node.height) {
const tmpNode = this.data;
this.data = node;
node = tmpNode;
}
this._insert(node, this.data.height - node.height - 1, true);
}
return this;
}
insert(item) {
if (item) this._insert(item, this.data.height - 1);
return this;
}
clear() {
this.data = createNode([]);
return this;
}
remove(item, equalsFn) {
if (!item) return this;
let node = this.data;
const bbox2 = this.toBBox(item);
const path = [];
const indexes = [];
let i, parent, goingUp;
while (node || path.length) {
if (!node) {
node = path.pop();
parent = path[path.length - 1];
i = indexes.pop();
goingUp = true;
}
if (node.leaf) {
const index = findItem(item, node.children, equalsFn);
if (index !== -1) {
node.children.splice(index, 1);
path.push(node);
this._condense(path);
return this;
}
}
if (!goingUp && !node.leaf && contains(node, bbox2)) {
path.push(node);
indexes.push(i);
i = 0;
parent = node;
node = node.children[0];
} else if (parent) {
i++;
node = parent.children[i];
goingUp = false;
} else node = null;
}
return this;
}
toBBox(item) {
return item;
}
compareMinX(a, b) {
return a.minX - b.minX;
}
compareMinY(a, b) {
return a.minY - b.minY;
}
toJSON() {
return this.data;
}
fromJSON(data) {
this.data = data;
return this;
}
_all(node, result) {
const nodesToSearch = [];
while (node) {
if (node.leaf) result.push(...node.children);
else nodesToSearch.push(...node.children);
node = nodesToSearch.pop();
}
return result;
}
_build(items, left, right, height) {
const N = right - left + 1;
let M = this._maxEntries;
let node;
if (N <= M) {
node = createNode(items.slice(left, right + 1));
calcBBox(node, this.toBBox);
return node;
}
if (!height) {
height = Math.ceil(Math.log(N) / Math.log(M));
M = Math.ceil(N / Math.pow(M, height - 1));
}
node = createNode([]);
node.leaf = false;
node.height = height;
const N2 = Math.ceil(N / M);
const N1 = N2 * Math.ceil(Math.sqrt(M));
multiSelect(items, left, right, N1, this.compareMinX);
for (let i = left; i <= right; i += N1) {
const right2 = Math.min(i + N1 - 1, right);
multiSelect(items, i, right2, N2, this.compareMinY);
for (let j = i; j <= right2; j += N2) {
const right3 = Math.min(j + N2 - 1, right2);
node.children.push(this._build(items, j, right3, height - 1));
}
}
calcBBox(node, this.toBBox);
return node;
}
_chooseSubtree(bbox2, node, level, path) {
while (true) {
path.push(node);
if (node.leaf || path.length - 1 === level) break;
let minArea = Infinity;
let minEnlargement = Infinity;
let targetNode;
for (let i = 0; i < node.children.length; i++) {
const child = node.children[i];
const area2 = bboxArea(child);
const enlargement = enlargedArea(bbox2, child) - area2;
if (enlargement < minEnlargement) {
minEnlargement = enlargement;
minArea = area2 < minArea ? area2 : minArea;
targetNode = child;
} else if (enlargement === minEnlargement) {
if (area2 < minArea) {
minArea = area2;
targetNode = child;
}
}
}
node = targetNode || node.children[0];
}
return node;
}
_insert(item, level, isNode) {
const bbox2 = isNode ? item : this.toBBox(item);
const insertPath = [];
const node = this._chooseSubtree(bbox2, this.data, level, insertPath);
node.children.push(item);
extend(node, bbox2);
while (level >= 0) {
if (insertPath[level].children.length > this._maxEntries) {
this._split(insertPath, level);
level--;
} else break;
}
this._adjustParentBBoxes(bbox2, insertPath, level);
}
// split overflowed node into two
_split(insertPath, level) {
const node = insertPath[level];
const M = node.children.length;
const m = this._minEntries;
this._chooseSplitAxis(node, m, M);
const splitIndex = this._chooseSplitIndex(node, m, M);
const newNode = createNode(node.children.splice(splitIndex, node.children.length - splitIndex));
newNode.height = node.height;
newNode.leaf = node.leaf;
calcBBox(node, this.toBBox);
calcBBox(newNode, this.toBBox);
if (level) insertPath[level - 1].children.push(newNode);
else this._splitRoot(node, newNode);
}
_splitRoot(node, newNode) {
this.data = createNode([node, newNode]);
this.data.height = node.height + 1;
this.data.leaf = false;
calcBBox(this.data, this.toBBox);
}
_chooseSplitIndex(node, m, M) {
let index;
let minOverlap = Infinity;
let minArea = Infinity;
for (let i = m; i <= M - m; i++) {
const bbox1 = distBBox(node, 0, i, this.toBBox);
const bbox2 = distBBox(node, i, M, this.toBBox);
const overlap = intersectionArea(bbox1, bbox2);
const area2 = bboxArea(bbox1) + bboxArea(bbox2);
if (overlap < minOverlap) {
minOverlap = overlap;
index = i;
minArea = area2 < minArea ? area2 : minArea;
} else if (overlap === minOverlap) {
if (area2 < minArea) {
minArea = area2;
index = i;
}
}
}
return index || M - m;
}
// sorts node children by the best axis for split
_chooseSplitAxis(node, m, M) {
const compareMinX = node.leaf ? this.compareMinX : compareNodeMinX;
const compareMinY = node.leaf ? this.compareMinY : compareNodeMinY;
const xMargin = this._allDistMargin(node, m, M, compareMinX);
const yMargin = this._allDistMargin(node, m, M, compareMinY);
if (xMargin < yMargin) node.children.sort(compareMinX);
}
// total margin of all possible split distributions where each node is at least m full
_allDistMargin(node, m, M, compare2) {
node.children.sort(compare2);
const toBBox2 = this.toBBox;
const leftBBox = distBBox(node, 0, m, toBBox2);
const rightBBox = distBBox(node, M - m, M, toBBox2);
let margin = bboxMargin(leftBBox) + bboxMargin(rightBBox);
for (let i = m; i < M - m; i++) {
const child = node.children[i];
extend(leftBBox, node.leaf ? toBBox2(child) : child);
margin += bboxMargin(leftBBox);
}
for (let i = M - m - 1; i >= m; i--) {
const child = node.children[i];
extend(rightBBox, node.leaf ? toBBox2(child) : child);
margin += bboxMargin(rightBBox);
}
return margin;
}
_adjustParentBBoxes(bbox2, path, level) {
for (let i = level; i >= 0; i--) {
extend(path[i], bbox2);
}
}
_condense(path) {
for (let i = path.length - 1, siblings; i >= 0; i--) {
if (path[i].children.length === 0) {
if (i > 0) {
siblings = path[i - 1].children;
siblings.splice(siblings.indexOf(path[i]), 1);
} else this.clear();
} else calcBBox(path[i], this.toBBox);
}
}
}
function findItem(item, items, equalsFn) {
if (!equalsFn) return items.indexOf(item);
for (let i = 0; i < items.length; i++) {
if (equalsFn(item, items[i])) return i;
}
return -1;
}
function calcBBox(node, toBBox2) {
distBBox(node, 0, node.children.length, toBBox2, node);
}
function distBBox(node, k, p, toBBox2, destNode) {
if (!destNode) destNode = createNode(null);
destNode.minX = Infinity;
destNode.minY = Infinity;
destNode.maxX = -Infinity;
destNode.maxY = -Infinity;
for (let i = k; i < p; i++) {
const child = node.children[i];
extend(destNode, node.leaf ? toBBox2(child) : child);
}
return destNode;
}
function extend(a, b) {
a.minX = Math.min(a.minX, b.minX);
a.minY = Math.min(a.minY, b.minY);
a.maxX = Math.max(a.maxX, b.maxX);
a.maxY = Math.max(a.maxY, b.maxY);
return a;
}
function compareNodeMinX(a, b) {
return a.minX - b.minX;
}
function compareNodeMinY(a, b) {
return a.minY - b.minY;
}
function bboxArea(a) {
return (a.maxX - a.minX) * (a.maxY - a.minY);
}
function bboxMargin(a) {
return a.maxX - a.minX + (a.maxY - a.minY);
}
function enlargedArea(a, b) {
return (Math.max(b.maxX, a.maxX) - Math.min(b.minX, a.minX)) * (Math.max(b.maxY, a.maxY) - Math.min(b.minY, a.minY));
}
function intersectionArea(a, b) {
const minX = Math.max(a.minX, b.minX);
const minY = Math.max(a.minY, b.minY);
const maxX = Math.min(a.maxX, b.maxX);
const maxY = Math.min(a.maxY, b.maxY);
return Math.max(0, maxX - minX) * Math.max(0, maxY - minY);
}
function contains(a, b) {
return a.minX <= b.minX && a.minY <= b.minY && b.maxX <= a.maxX && b.maxY <= a.maxY;
}
function intersects$1(a, b) {
return b.minX <= a.maxX && b.minY <= a.maxY && b.maxX >= a.minX && b.maxY >= a.minY;
}
function createNode(children) {
return {
children,
height: 1,
leaf: true,
minX: Infinity,
minY: Infinity,
maxX: -Infinity,
maxY: -Infinity
};
}
function multiSelect(arr, left, right, n, compare2) {
const stack = [left, right];
while (stack.length) {
right = stack.pop();
left = stack.pop();
if (right - left <= n) continue;
const mid = left + Math.ceil((right - left) / n / 2) * n;
quickselect(arr, mid, left, right, compare2);
stack.push(left, mid, mid, right);
}
}
class TinyQueue3 {
constructor(data = [], compare2 = (a, b) => a < b ? -1 : a > b ? 1 : 0) {
this.data = data;
this.length = this.data.length;
this.compare = compare2;
if (this.length > 0) {
for (let i = (this.length >> 1) - 1; i >= 0; i--) this._down(i);
}
}
push(item) {
this.data.push(item);
this._up(this.length++);
}
pop() {
if (this.length === 0) return void 0;
const top = this.data[0];
const bottom = this.data.pop();
if (--this.length > 0) {
this.data[0] = bottom;
this._down(0);
}
return top;
}
peek() {
return this.data[0];
}
_up(pos) {
const { data, compare: compare2 } = this;
const item = data[pos];
while (pos > 0) {
const parent = pos - 1 >> 1;
const current = data[parent];
if (compare2(item, current) >= 0) break;
data[pos] = current;
pos = parent;
}
data[pos] = item;
}
_down(pos) {
const { data, compare: compare2 } = this;
const halfLength = this.length >> 1;
const item = data[pos];
while (pos < halfLength) {
let bestChild = (pos << 1) + 1;
const right = bestChild + 1;
if (right < this.length && compare2(data[right], data[bestChild]) < 0) {
bestChild = right;
}
if (compare2(data[bestChild], item) >= 0) break;
data[pos] = data[bestChild];
pos = bestChild;
}
data[pos] = item;
}
}
var pointInPolygonExports = requirePointInPolygon();
const pointInPolygon = /* @__PURE__ */ getDefaultExportFromCjs(pointInPolygonExports);
function concaveman(points, concavity, lengthThreshold) {
concavity = Math.max(0, concavity === void 0 ? 2 : concavity);
lengthThreshold = lengthThreshold || 0;
const hull = fastConvexHull(points);
const tree = new RBush3(16);
tree.toBBox = function(a) {
return {
minX: a[0],
minY: a[1],
maxX: a[0],
maxY: a[1]
};
};
tree.compareMinX = function(a, b) {
return a[0] - b[0];
};
tree.compareMinY = function(a, b) {
return a[1] - b[1];
};
tree.load(points);
const queue = [];
let last;
for (let i = 0; i < hull.length; i++) {
const p = hull[i];
tree.remove(p);
last = insertNode(p, last);
queue.push(last);
}
const segTree = new RBush3(16);
for (let i = 0; i < queue.length; i++) segTree.insert(updateBBox(queue[i]));
const sqConcavity = concavity * concavity;
const sqLenThreshold = lengthThreshold * lengthThreshold;
while (queue.length) {
const node2 = queue.shift();
const a = node2.p;
const b = node2.next.p;
const sqLen = getSqDist(a, b);
if (sqLen < sqLenThreshold) continue;
const maxSqLen = sqLen / sqConcavity;
const p = findCandidate(tree, node2.prev.p, a, b, node2.next.next.p, maxSqLen, segTree);
if (p && Math.min(getSqDist(p, a), getSqDist(p, b)) <= maxSqLen) {
queue.push(node2);
queue.push(insertNode(p, node2));
tree.remove(p);
segTree.remove(node2);
segTree.insert(updateBBox(node2));
segTree.insert(updateBBox(node2.next));
}
}
let node = last;
const concave = [];
do {
concave.push(node.p);
node = node.next;
} while (node !== last);
concave.push(node.p);
return concave;
}
function findCandidate(tree, a, b, c, d, maxDist, segTree) {
const queue = new TinyQueue3([], compareDist);
let node = tree.data;
while (node) {
for (let i = 0; i < node.children.length; i++) {
const child = node.children[i];
const dist = node.leaf ? sqSegDist(child, b, c) : sqSegBoxDist(b, c, child);
if (dist > maxDist) continue;
queue.push({
node: child,
dist
});
}
while (queue.length && !queue.peek().node.children) {
const item = queue.pop();
const p = item.node;
const d0 = sqSegDist(p, a, b);
const d1 = sqSegDist(p, c, d);
if (item.dist < d0 && item.dist < d1 && noIntersections(b, p, segTree) && noIntersections(c, p, segTree)) return p;
}
node = queue.pop();
if (node) node = node.node;
}
return null;
}
function compareDist(a, b) {
return a.dist - b.dist;
}
function sqSegBoxDist(a, b, bbox2) {
if (inside(a, bbox2) || inside(b, bbox2)) return 0;
const d1 = sqSegSegDist(a[0], a[1], b[0], b[1], bbox2.minX, bbox2.minY, bbox2.maxX, bbox2.minY);
if (d1 === 0) return 0;
const d2 = sqSegSegDist(a[0], a[1], b[0], b[1], bbox2.minX, bbox2.minY, bbox2.minX, bbox2.maxY);
if (d2 === 0) return 0;
const d3 = sqSegSegDist(a[0], a[1], b[0], b[1], bbox2.maxX, bbox2.minY, bbox2.maxX, bbox2.maxY);
if (d3 === 0) return 0;
const d4 = sqSegSegDist(a[0], a[1], b[0], b[1], bbox2.minX, bbox2.maxY, bbox2.maxX, bbox2.maxY);
if (d4 === 0) return 0;
return Math.min(d1, d2, d3, d4);
}
function inside(a, bbox2) {
return a[0] >= bbox2.minX && a[0] <= bbox2.maxX && a[1] >= bbox2.minY && a[1] <= bbox2.maxY;
}
function noIntersections(a, b, segTree) {
const minX = Math.min(a[0], b[0]);
const minY = Math.min(a[1], b[1]);
const maxX = Math.max(a[0], b[0]);
const maxY = Math.max(a[1], b[1]);
const edges = segTree.search({ minX, minY, maxX, maxY });
for (let i = 0; i < edges.length; i++) {
if (intersects(edges[i].p, edges[i].next.p, a, b)) return false;
}
return true;
}
function cross(p1, p2, p3) {
return orient2d(p1[0], p1[1], p2[0], p2[1], p3[0], p3[1]);
}
function intersects(p1, q1, p2, q2) {
return p1 !== q2 && q1 !== p2 && cross(p1, q1, p2) > 0 !== cross(p1, q1, q2) > 0 && cross(p2, q2, p1) > 0 !== cross(p2, q2, q1) > 0;
}
function updateBBox(node) {
const p1 = node.p;
const p2 = node.next.p;
node.minX = Math.min(p1[0], p2[0]);
node.minY = Math.min(p1[1], p2[1]);
node.maxX = Math.max(p1[0], p2[0]);
node.maxY = Math.max(p1[1], p2[1]);
return node;
}
function fastConvexHull(points) {
let left = points[0];
let top = points[0];
let right = points[0];
let bottom = points[0];
for (let i = 0; i < points.length; i++) {
const p = points[i];
if (p[0] < left[0]) left = p;
if (p[0] > right[0]) right = p;
if (p[1] < top[1]) top = p;
if (p[1] > bottom[1]) bottom = p;
}
const cull = [left, top, right, bottom];
const filtered = cull.slice();
for (let i = 0; i < points.length; i++) {
if (!pointInPolygon(points[i], cull)) filtered.push(points[i]);
}
return convexHull(filtered);
}
function insertNode(p, prev) {
const node = {
p,
prev: null,
next: null,
minX: 0,
minY: 0,
maxX: 0,
maxY: 0
};
if (!prev) {
node.prev = node;
node.next = node;
} else {
node.next = prev.next;
node.prev = prev;
prev.next.prev = node;
prev.next = node;
}
return node;
}
function getSqDist(p1, p2) {
const dx = p1[0] - p2[0], dy = p1[1] - p2[1];
return dx * dx + dy * dy;
}
function sqSegDist(p, p1, p2) {
let x = p1[0], y = p1[1], dx = p2[0] - x, dy = p2[1] - y;
if (dx !== 0 || dy !== 0) {
const t = ((p[0] - x) * dx + (p[1] - y) * dy) / (dx * dx + dy * dy);
if (t > 1) {
x = p2[0];
y = p2[1];
} else if (t > 0) {
x += dx * t;
y += dy * t;
}
}
dx = p[0] - x;
dy = p[1] - y;
return dx * dx + dy * dy;
}
function sqSegSegDist(x0, y0, x1, y1, x2, y2, x3, y3) {
const ux = x1 - x0;
const uy = y1 - y0;
const vx = x3 - x2;
const vy = y3 - y2;
const wx = x0 - x2;
const wy = y0 - y2;
const a = ux * ux + uy * uy;
const b = ux * vx + uy * vy;
const c = vx * vx + vy * vy;
const d = ux * wx + uy * wy;
const e = vx * wx + vy * wy;
const D2 = a * c - b * b;
let sN, tN;
let sD = D2;
let tD = D2;
if (D2 === 0) {
sN = 0;
sD = 1;
tN = e;
tD = c;
} else {
sN = b * e - c * d;
tN = a * e - b * d;
if (sN < 0) {
sN = 0;
tN = e;
tD = c;
} else if (sN > sD) {
sN = sD;
tN = e + b;
tD = c;
}
}
if (tN < 0) {
tN = 0;
if (-d < 0) sN = 0;
else if (-d > a) sN = sD;
else {
sN = -d;
sD = a;
}
} else if (tN > tD) {
tN = tD;
if (-d + b < 0) sN = 0;
else if (-d + b > a) sN = sD;
else {
sN = -d + b;
sD = a;
}
}
const sc = sN === 0 ? 0 : sN / sD;
const tc = tN === 0 ? 0 : tN / tD;
const cx = (1 - sc) * x0 + sc * x1;
const cy = (1 - sc) * y0 + sc * y1;
const cx2 = (1 - tc) * x2 + tc * x3;
const cy2 = (1 - tc) * y2 + tc * y3;
const dx = cx2 - cx;
const dy = cy2 - cy;
return dx * dx + dy * dy;
}
function compareByX(a, b) {
return a[0] === b[0] ? a[1] - b[1] : a[0] - b[0];
}
function convexHull(points) {
points.sort(compareByX);
const lower = [];
for (let i = 0; i < points.length; i++) {
while (lower.length >= 2 && cross(lower[lower.length - 2], lower[lower.length - 1], points[i]) <= 0) {
lower.pop();
}
lower.push(points[i]);
}
const upper = [];
for (let ii = points.length - 1; ii >= 0; ii--) {
while (upper.length >= 2 && cross(upper[upper.length - 2], upper[upper.length - 1], points[ii]) <= 0) {
upper.pop();
}
upper.push(points[ii]);
}
upper.pop();
lower.pop();
return lower.concat(upper);
}
const isTouchDevice = () => "ontouchstart" in window || navigator.maxTouchPoints > 0;
function isTestEnvironment() {
return typeof vitest !== "undefined";
}
class PolyDrawUtil {
/**
* Gets the bounds of the polygon with optional padding.
* @param polygon Array of LatLng points.
* @param padding Padding factor.
* @returns The LatLngBounds.
*/
static getBounds(polygon2, padding = 0) {
const tmpLatLng = [];
polygon2.forEach((ll) => {
if (isNaN(ll.lat) || isNaN(ll.lng)) ;
tmpLatLng.push(ll);
});
const polyLine = new L.Polyline(tmpLatLng);
const bounds = polyLine.getBounds();
if (padding !== 0) {
return bounds.pad(padding);
}
return bounds;
}
}
class Compass {
constructor(minLat = 0, minLng = 0, maxLat = 0, maxLng = 0) {
__publicField(this, "direction", {
East: new L.LatLng(0, 0),
North: new L.LatLng(0, 0),
NorthEast: new L.LatLng(0, 0),
NorthWest: new L.LatLng(0, 0),
South: new L.LatLng(0, 0),
SouthEast: new L.LatLng(0, 0),
SouthWest: new L.LatLng(0, 0),
West: new L.LatLng(0, 0)
});
this.direction.North = new L.LatLng(maxLat, (minLng + maxLng) / 2);
this.direction.NorthEast = new L.LatLng(maxLat, maxLng);
this.direction.East = new L.LatLng((minLat + maxLat) / 2, maxLng);
this.direction.SouthEast = new L.LatLng(minLat, maxLng);
this.direction.South = new L.LatLng(minLat, (minLng + maxLng) / 2);
this.direction.SouthWest = new L.LatLng(minLat, minLng);
this.direction.West = new L.LatLng((minLat + maxLat) / 2, minLng);
this.direction.NorthWest = new L.LatLng(maxLat, minLng);
}
getDirection(direction) {
switch (direction) {
case MarkerPosition.SouthWest:
return this.direction.SouthWest;
case MarkerPosition.West:
return this.direction.West;
case MarkerPosition.NorthWest:
return this.direction.NorthWest;
case MarkerPosition.North:
return this.direction.North;
case MarkerPosition.NorthEast:
return this.direction.NorthEast;
case MarkerPosition.East:
return this.direction.East;
case MarkerPosition.SouthEast:
return this.direction.SouthEast;
case MarkerPosition.South:
return this.direction.South;
default:
throw new Error();
}
}
getPositions(startPosition = MarkerPosition.SouthWest, clockwise = false, addClosingNode = true) {
const positions = [];
const posArray = this.getPositionAsArray(startPosition, clockwise);
posArray.forEach((v) => {
positions.push([v.lng, v.lat]);
});
if (addClosingNode) {
positions.push([posArray[0].lng, posArray[0].lat]);
}
return positions;
}
getPositionAsArray(startPosition = MarkerPosition.NorthEast, clockwise = false) {
const positions = [];
if (clockwise) {
positions.push(this.direction.SouthWest);
positions.push(this.direction.West);
positions.push(this.direction.NorthWest);
positions.push(this.direction.North);
positions.push(this.direction.NorthEast);
positions.push(this.direction.East);
positions.push(this.direction.SouthEast);
positions.push(this.direction.South);
} else {
positions.push(this.direction.SouthWest);
positions.push(this.direction.South);
positions.push(this.direction.SouthEast);
positions.push(this.direction.East);
positions.push(this.direction.NorthEast);
positions.push(this.direction.North);
positions.push(this.direction.NorthWest);
positions.push(this.direction.West);
}
if (startPosition !== MarkerPosition.SouthWest) {
const chunk = positions.splice(0, startPosition);
chunk.forEach((v, i) => {
positions.splice(startPosition + i, 0, v);
});
}
return positions;
}
}
class Perimeter {
constructor(length2, config) {
__publicField(this, "metricLength", "");
__publicField(this, "metricUnit", "");
__publicField(this, "imperialLength", "");
__publicField(this, "imperialUnit", "");
if (length2 !== null || length2 !== void 0) {
if (length2 === 0) {
if (config.markers.markerInfoIcon.usePerimeterMinValue) {
this.metricLength = config.markers.markerInfoIcon.values.min.metric;
this.metricUnit = config.markers.markerInfoIcon.units.metric.perimeter.m;
this.imperialLength = config.markers.markerInfoIcon.values.min.imperial;
this.imperialUnit = config.markers.markerInfoIcon.units.imperial.perimeter.feet;
} else {
this.metricLength = config.markers.markerInfoIcon.values.unknown.metric;
this.metricUnit = config.markers.markerInfoIcon.units.unknownUnit;
this.imperialLength = config.markers.markerInfoIcon.values.unknown.imperial;
this.imperialUnit = config.markers.markerInfoIcon.units.unknownUnit;
}
} else if (length2 < 100) {
this.metricLength = (Math.ceil(length2 / 10) * 10).toString();
this.metricUnit = config.markers.markerInfoIcon.units.metric.perimeter.m;
} else if (length2 < 500) {
this.metricLength = (Math.ceil(length2 / 50) * 50).toString();
this.metricUnit = config.markers.markerInfoIcon.units.metric.perimeter.m;
} else if (length2 < 1e3) {
this.metricLength = (Math.ceil(length2 / 100) * 100).toString();
this.metricUnit = config.markers.markerInfoIcon.units.metric.perimeter.m;
} else if (length2 < 1e4) {
this.metricLength = (Math.ceil(length2 / 100) * 100 / 1e3).toFixed(1);
this.metricUnit = config.markers.markerInfoIcon.units.metric.perimeter.km;
} else {
this.metricLength = (Math.ceil(length2 / 1e3) * 1e3 / 1e3).toString();
this.metricUnit = config.markers.markerInfoIcon.units.metric.perimeter.km;
}
const inch = length2 / 0.0254;
const feet = inch / 12;
const yards = feet / 3;
const miles = yards / 1760;
if (length2 < 1e3 / 2.54) {
this.imperialLength = (Math.ceil(feet / 10) * 10).toString();
this.imperialUnit = config.markers.markerInfoIcon.units.imperial.perimeter.feet;
} else if (length2 < 1e3 / 2.54 * 3) {
this.imperialLength = (Math.ceil(yards / 10) * 10).toString();
this.imperialUnit = config.markers.markerInfoIcon.units.imperial.perimeter.yards;
} else if (length2 < 1609) {
this.imperialLength = miles.toFixed(2);
this.imperialUnit = config.markers.markerInfoIcon.units.imperial.perimeter.miles;
} else if (length2 < 16093) {
this.imperialLength = miles.toFixed(1);
this.imperialUnit = config.markers.markerInfoIcon.units.imperial.perimeter.miles;
} else {
this.imperialLength = miles.toFixed(0);
this.imperialUnit = config.markers.markerInfoIcon.units.imperial.perimeter.miles;
}
}
}
}
class Area {
constructor(sqrMeterArea, config) {
__publicField(this, "metricArea", "");
__publicField(this, "metricUnit", "");
__publicField(this, "imperialArea", "");
__publicField(this, "imperialUnit", "");
const area2 = sqrMeterArea;
const onlyMetrics = config.markers.markerInfoIcon.units.metric.onlyMetrics;
if (area2 !== null || area2 !== void 0) {
if (area2 === 0) {
this.metricArea = "-";
this.metricUnit = config.markers.markerInfoIcon.units.unknownUnit;
this.imperialArea = "-";
this.imperialUnit = config.markers.markerInfoIcon.units.unknownUnit;
} else if (area2 < 1e4) {
this.metricArea = Math.round(area2).toString();
this.metricUnit = config.markers.markerInfoIcon.units.metric.area.m2;
} else if (area2 < 1e5) {
if (onlyMetrics) {
this.metricArea = (area2 / 1e6).toFixed(2);
this.metricUnit = config.markers.markerInfoIcon.units.metric.area.km2;
} else {
this.metricArea = (area2 / 1e3).toFixed(1);
this.metricUnit = config.markers.markerInfoIcon.units.metric.area.daa;
}
} else if (area2 < 1e7) {
if (onlyMetrics) {
this.metricArea = (area2 / 1e6).toFixed(2);
this.metricUnit = config.markers.markerInfoIcon.units.metric.area.km2;
} else {
this.metricArea = Math.round(area2 / 1e3).toString();
this.metricUnit = config.markers.markerInfoIcon.units.metric.area.daa;
}
} else if (area2 < 1e8) {
if (onlyMetrics) {
this.metricArea = (area2 / 1e6).toFixed(1);
this.metricUnit = config.markers.markerInfoIcon.units.metric.area.km2;
} else {
this.metricArea = Math.round(area2 / 1e4).toString();
this.metricUnit = config.markers.markerInfoIcon.units.metric.area.ha;
}
} else {
this.metricArea = Math.round(area2 / 1e6).toString();
this.metricUnit = config.markers.markerInfoIcon.units.metric.area.km2;
}
const inch2 = area2 * 1550;
const feet2 = inch2 * 69444e-7;
const yards2 = feet2 * 0.11111;
const acres = yards2 * 20661e-8;
const miles2 = yards2 * 32283e-11;
if (area2 < 92.9) {
this.imperialArea = Math.round(feet2).toString();
this.imperialUnit = config.markers.markerInfoIcon.units.imperial.area.feet2;
} else if (area2 < 836.14) {
this.imperialArea = yards2.toFixed(0);
this.imperialUnit = config.markers.markerInfoIcon.units.imperial.area.yards2;
} else if (area2 < 40469.6) {
this.imperialArea = acres.toFixed(2);
this.imperialUnit = config.markers.markerInfoIcon.units.imperial.area.acres;
} else if (area2 < 404696) {
this.imperialArea = acres.toFixed(1);
this.imperialUnit = config.markers.markerInfoIcon.units.imperial.area.acres;
} else if (area2 < 4046960) {
this.imperialArea = acres.toFixed(0);
this.imperialUnit = config.markers.markerInfoIcon.units.imperial.area.acres;
} else if (area2 < 25900404) {
this.imperialArea = miles2.toFixed(2);
this.imperialUnit = config.markers.markerInfoIcon.units.imperial.area.miles2;
} else if (area2 < 259004040) {
this.imperialArea = miles2.toFixed(1);
this.imperialUnit = config.markers.markerInfoIcon.units.imperial.area.miles2;
} else {
this.imperialArea = miles2.toFixed(0);
this.imperialUnit = config.markers.markerInfoIcon.units.imperial.area.miles2;
}
}
}
}
function deepMerge(target, ...sources) {
if (!sources.length) return target;
for (const source of sources) {
if (!source) continue;
for (const key in source) {
const sourceValue = source[key];
const targetValue = target[key];
if (sourceValue && typeof sourceValue === "object" && !Array.isArray(sourceValue)) {
const base = targetValue && typeof targetValue === "object" && !Array.isArray(targetValue) ? targetValue : {};
target[key] = deepMerge(base, sourceValue);
} else if (sourceValue !== void 0) {
target[key] = sourceValue;
}
}
}
return target;
}
class TurfHelper {
constructor(config) {
__publicField(this, "config", defaultConfig);
__publicField(this, "fallbackWarnings", /* @__PURE__ */ new Set());
this.config = deepMerge(structuredClone(defaultConfig), config);
}
warnFallbackOnce(key, message) {
if (isTestEnvironment()) return;
if (this.fallbackWarnings.has(key)) return;
this.fallbackWarnings.add(key);
console.warn(message);
}
/**
* Convert a degree-based tolerance into an approximate degree tolerance that is
* consistent in screen space by scaling with the local meters-per-degree.
* Input `tolerance` is treated as degrees; we scale it so that the resulting
* simplify tolerance corresponds to the same ground distance at the given point.
*/
toProjectedTolerance(tolerance, anchor) {
if (tolerance <= 0) return 0;
try {
const metersPerDegree = EARTH_RADIUS.MEAN_METERS * MATH.DEG_TO_RAD;
const targetMeters = tolerance * metersPerDegree;
const scale = Math.cos(anchor.lat * MATH.DEG_TO_RAD);
if (scale === 0) return tolerance;
return targetMeters / (metersPerDegree * scale);
} catch {
return tolerance;
}
}
/**
* Compute a diagonal (degrees) for a polygon/multipolygon bbox.
*/
getFeatureDiagonal(feature2) {
try {
const [minX, minY, maxX, maxY] = bbox$2(feature2);
return Math.hypot(maxX - minX, maxY - minY);
} catch {
return 0;
}
}
/**
* Compute a diagonal (degrees) for a ring of LatLngs.
*/
getRingDiagonal(ring) {
if (!ring || ring.length === 0) return 0;
let minLat = ring[0].lat;
let maxLat = ring[0].lat;
let minLng = ring[0].lng;
let maxLng = ring[0].lng;
for (const pt of ring) {
minLat = Math.min(minLat, pt.lat);
maxLat = Math.max(maxLat, pt.lat);
minLng = Math.min(minLng, pt.lng);
maxLng = Math.max(maxLng, pt.lng);
}
return Math.hypot(maxLng - minLng, maxLat - minLat);
}
/**
* Adapt a base tolerance to the shape size so small shapes don't oversimplify.
*/
getAdaptiveTolerance(baseTolerance, diagonal) {
if (baseTolerance <= 0) return 0;
if (diagonal <= 0) return baseTolerance;
const adaptive = diagonal * 0.02;
return Math.min(baseTolerance, adaptive);
}
union(poly1, poly2) {
try {
const fc = featureCollection$1([poly1, poly2]);
const u2 = index_default$c(fc);
return u2 ? this.getTurfPolygon(u2) : null;
} catch (error) {
if (!isTestEnvironment()) {
console.warn("Error in union:", error instanceof Error ? error.message : String(error));
}
return null;
}
}
/**
* Create polygon from drawing trace using configured algorithm.
*/
createPolygonFromTrace(feature2) {
var _a2;
const configuredAlgorithm = (_a2 = this.config.polygonCreation) == null ? void 0 : _a2.algorithm;
if (configuredAlgorithm !== void 0 && typeof configuredAlgorithm !== "string") {
this.warnFallbackOnce(
"polygonCreation.algorithm.type",
'[Leaflet.Polydraw] Invalid `polygonCreation.algorithm` type. Using "concaveman" as fallback.'
);
}
const algorithm = typeof configuredAlgorithm === "string" ? configuredAlgorithm : "concaveman";
switch (algorithm) {
case "concaveman":
return this.turfConcaveman(feature2);
case "convex":
return this.createConvexPolygon(feature2);
case "direct":
return this.createDirectPolygon(feature2);
case "buffer":
this.warnFallbackOnce(
"polygonCreation.algorithm.buffer",
'[Leaflet.Polydraw] `polygonCreation.algorithm: "buffer"` is deprecated. Using "concaveman" as fallback.'
);
return this.turfConcaveman(feature2);
default:
this.warnFallbackOnce(
`polygonCreation.algorithm.unknown:${algorithm}`,
`[Leaflet.Polydraw] Unknown polygon creation algorithm "${algorithm}". Using "concaveman" as fallback.`
);
return this.turfConcaveman(feature2);
}
}
/**
* Original concaveman implementation
*/
turfConcaveman(feature2) {
const points = index_default$9(feature2);
const coordinates = points.features.map((f) => f.geometry.coordinates);
return multiPolygon$2([[concaveman(coordinates)]]);
}
/**
* Create convex hull polygon (simplest, fewest edges)
*/
createConvexPolygon(feature2) {
const points = index_default$9(feature2);
const convexHull2 = index_default$f(points);
if (!convexHull2) {
this.warnFallbackOnce(
"polygonCreation.algorithm.convex.noHull",
'[Leaflet.Polydraw] Convex hull generation failed. Using "direct" polygon creation as fallback.'
);
return this.createDirectPolygon(feature2);
}
return this.getTurfPolygon(convexHull2);
}
/**
* Create polygon directly from line coordinates (moderate edge count)
*/
createDirectPolygon(feature2) {
let coordinates;
if (feature2.geometry.type === "LineString") {
coordinates = feature2.geometry.coordinates;
} else if (feature2.geometry.type === "Polygon") {
coordinates = feature2.geometry.coordinates[0];
} else {
const points = index_default$9(feature2);
coordinates = points.features.map((f) => f.geometry.coordinates);
}
if (coordinates.length > 0) {
const first = coordinates[0];
const last = coordinates[coordinates.length - 1];
if (first[0] !== last[0] || first[1] !== last[1]) {
coordinates.push([first[0], first[1]]);
}
}
if (coordinates.length < 4) {
if (!isTestEnvironment()) {
console.warn("Not enough points for direct polygon, falling back to concaveman");
}
return this.turfConcaveman(feature2);
}
return multiPolygon$2([[coordinates]]);
}
getSimplified(polygon2, dynamicTolerance = false) {
const simplification = this.resolveSimplificationConfig();
if (simplification.strategy === "none") {
return polygon2;
}
if (simplification.strategy === "simple") {
const adaptedTol = this.getAdaptiveTolerance(
simplification.simple.tolerance,
this.getFeatureDiagonal(polygon2)
);
return index_default$8(polygon2, {
tolerance: this.toProjectedTolerance(adaptedTol, this.getReferenceLatLng(polygon2)),
highQuality: simplification.simple.highQuality,
mutate: false
});
}
const numOfEdges = polygon2.geometry.coordinates[0][0].length;
const fractionGuard = simplification.dynamic.fractionGuard;
const multiplier = simplification.dynamic.multiplier;
const adaptedBaseTol = this.getAdaptiveTolerance(
simplification.dynamic.baseTolerance,
this.getFeatureDiagonal(polygon2)
);
const baseArgs = {
highQuality: simplification.dynamic.highQuality,
mutate: false
};
const runSimplify = (toleranceValue) => index_default$8(polygon2, {
...baseArgs,
tolerance: this.toProjectedTolerance(toleranceValue, this.getReferenceLatLng(polygon2))
});
if (!dynamicTolerance) {
return runSimplify(adaptedBaseTol);
}
let currentTolerance = adaptedBaseTol;
let simplified = runSimplify(currentTolerance);
while (simplified.geometry.coordinates[0][0].length > 4 && simplified.geometry.coordinates[0][0].length / (numOfEdges + 2) > fractionGuard) {
currentTolerance *= multiplier;
simplified = runSimplify(currentTolerance);
}
return simplified;
}
/**
* Simplify a LatLng ring using a specific tolerance.
* Returns the simplified vertices in LatLngLiteral order.
*/
simplifyLatLngRing(latlngs, tolerance, highQuality = true) {
var _a2, _b2;
if (!latlngs || latlngs.length === 0 || tolerance <= 0) {
return latlngs ? [...latlngs] : [];
}
const ring = [...latlngs];
const first = ring[0];
const last = ring[ring.length - 1];
if (!last || !first || last.lat !== first.lat || last.lng !== first.lng) {
ring.push({ lat: first.lat, lng: first.lng });
}
const coords = ring.map((point2) => [point2.lng, point2.lat]);
const adaptedTol = this.getAdaptiveTolerance(tolerance, this.getRingDiagonal(ring));
try {
const poly = polygon$2([coords]);
const simplified = index_default$8(poly, {
tolerance: this.toProjectedTolerance(adaptedTol, latlngs[0]),
highQuality,
mutate: false
});
const simplifiedCoords = (_b2 = (_a2 = simplified == null ? void 0 : simplified.geometry) == null ? void 0 : _a2.coordinates) == null ? void 0 : _b2[0];
if (!simplifiedCoords || simplifiedCoords.length === 0) {
return [...latlngs];
}
return simplifiedCoords.map((coord) => ({
lat: coord[1],
lng: coord[0]
}));
} catch (error) {
if (!isTestEnvironment()) {
console.warn(
"Error simplifying ring:",
error instanceof Error ? error.message : String(error)
);
}
return [...latlngs];
}
}
resolveSimplificationConfig() {
const raw = this.config.simplification;
const rawSimple = raw == null ? void 0 : raw.simple;
const rawDynamic = raw == null ? void 0 : raw.dynamic;
const rawStrategy = raw == null ? void 0 : raw.strategy;
const normalizeNonNegativeNumber = (value, fallback) => {
if (typeof value !== "number" || !Number.isFinite(value) || value < 0) {
if (value !== void 0) {
this.warnFallbackOnce(
`simplification.invalidNumber:${String(value)}`,
"[Leaflet.Polydraw] Invalid simplification numeric value. Using fallback default."
);
}
return fallback;
}
return value;
};
const normalizeBoolean = (value, fallback) => {
if (typeof value !== "boolean") {
if (value !== void 0) {
this.warnFallbackOnce(
`simplification.invalidBoolean:${String(value)}`,
"[Leaflet.Polydraw] Invalid simplification boolean value. Using fallback default."
);
}
return fallback;
}
return value;
};
const normalizeFraction = (value, fallback) => {
if (typeof value !== "number" || !Number.isFinite(value)) {
if (value !== void 0) {
this.warnFallbackOnce(
`simplification.invalidFraction:${String(value)}`,
"[Leaflet.Polydraw] Invalid `simplification.dynamic.fractionGuard`. Using fallback default."
);
}
return fallback;
}
if (value < 0 || value > 1) {
this.warnFallbackOnce(
`simplification.outOfRangeFraction:${value}`,
"[Leaflet.Polydraw] `simplification.dynamic.fractionGuard` is out of range [0, 1]. Clamping to nearest valid value."
);
}
return Math.max(0, Math.min(1, value));
};
const normalizeMultiplier = (value, fallback) => {
if (typeof value !== "number" || !Number.isFinite(value) || value <= 1) {
if (value !== void 0) {
this.warnFallbackOnce(
`simplification.invalidMultiplier:${String(value)}`,
"[Leaflet.Polydraw] Invalid `simplification.dynamic.multiplier` (must be > 1). Using fallback default."
);
}
return fallback;
}
return value;
};
let strategy = "simple";
if (rawStrategy === "simple" || rawStrategy === "dynamic" || rawStrategy === "none") {
strategy = rawStrategy;
} else if (rawStrategy !== void 0) {
this.warnFallbackOnce(
`simplification.invalidStrategy:${String(rawStrategy)}`,
'[Leaflet.Polydraw] Invalid `simplification.strategy`. Using "simple" as fallback.'
);
}
const simple = {
tolerance: normalizeNonNegativeNumber(rawSimple == null ? void 0 : rawSimple.tolerance, 1e-4),
highQuality: normalizeBoolean(rawSimple == null ? void 0 : rawSimple.highQuality, false)
};
const dynamic = {
baseTolerance: normalizeNonNegativeNumber(rawDynamic == null ? void 0 : rawDynamic.baseTolerance, 1e-4),
highQuality: normalizeBoolean(rawDynamic == null ? void 0 : rawDynamic.highQuality, false),
fractionGuard: normalizeFraction(rawDynamic == null ? void 0 : rawDynamic.fractionGuard, 0.9),
multiplier: normalizeMultiplier(rawDynamic == null ? void 0 : rawDynamic.multiplier, 2)
};
return {
strategy,
simple,
dynamic
};
}
getTurfPolygon(polygon2) {
let turfPolygon;
if (polygon2.geometry.type === "Polygon") {
turfPolygon = multiPolygon$2([polygon2.geometry.coordinates]);
} else {
turfPolygon = multiPolygon$2(polygon2.geometry.coordinates);
}
return turfPolygon;
}
getMultiPolygon(polygonArray) {
return multiPolygon$2(polygonArray);
}
getKinks(feature2) {
try {
if (!feature2 || !feature2.geometry || !feature2.geometry.coordinates) {
return [feature2];
}
const cleanedFeature = this.removeDuplicateVertices(feature2);
if (!cleanedFeature || !cleanedFeature.geometry || !cleanedFeature.geometry.coordinates) {
if (!isTestEnvironment()) {
console.warn("Feature became invalid after cleaning in getKinks");
}
return [feature2];
}
const hasHoles = this.polygonHasHoles(cleanedFeature);
if (hasHoles) {
return this.getKinksWithHolePreservation(cleanedFeature);
} else {
const unkink = index_default$5(cleanedFeature);
const coordinates = [];
featureEach$5(unkink, (current) => {
coordinates.push(current);
});
return coordinates;
}
} catch (error) {
if (!isTestEnvironment()) {
console.warn(
"Error processing kinks:",
error instanceof Error ? error.message : String(error)
);
}
return [feature2];
}
}
getCoords(feature2) {
return getCoords$1(feature2);
}
/**
* Get a representative lat/lng from a polygon/multipolygon to anchor tolerance scaling.
*/
getReferenceLatLng(feature2) {
var _a2, _b2;
try {
const coords = getCoords$1(feature2);
const first = (_b2 = (_a2 = coords == null ? void 0 : coords[0]) == null ? void 0 : _a2[0]) == null ? void 0 : _b2[0];
if (first && Array.isArray(first) && first.length >= 2) {
return { lat: first[1], lng: first[0] };
}
} catch {
}
return { lat: 0, lng: 0 };
}
hasKinks(feature2) {
const k = index_default$7(feature2);
return k.features.length > 0;
}
/**
* Get the convex hull of a polygon
*/
getConvexHull(polygon2) {
try {
const fc = featureCollection$1([polygon2]);
return index_default$f(fc);
} catch (error) {
if (!isTestEnvironment()) {
console.warn(
"Error in getConvexHull:",
error instanceof Error ? error.message : String(error)
);
}
return null;
}
}
/**
* Calculate midpoint between two LatLngLiteral points
*/
getMidpoint(point1, point2) {
const p1 = point$1([point1.lng, point1.lat]);
const p2 = point$1([point2.lng, point2.lat]);
const mp = midpoint(p1, p2);
return {
lat: mp.geometry.coordinates[1],
lng: mp.geometry.coordinates[0]
};
}
polygonIntersect(polygon2, latlngs) {
try {
if (!polygon2 || !polygon2.geometry || !polygon2.geometry.coordinates || !latlngs || !latlngs.geometry || !latlngs.geometry.coordinates) {
if (!isTestEnvironment()) {
console.warn("Invalid features passed to polygonIntersect");
}
return false;
}
const isMultiPolygonCoords = (coords) => {
var _a2, _b2;
return Array.isArray((_b2 = (_a2 = coords[0]) == null ? void 0 : _a2[0]) == null ? void 0 : _b2[0]);
};
const validateCoordinates = (coords) => {
if (!Array.isArray(coords)) return false;
const rings = isMultiPolygonCoords(coords) ? coords.flat() : coords;
for (const ring of rings) {
if (!Array.isArray(ring)) return false;
for (const coord of ring) {
if (!Array.isArray(coord) || coord.length < 2) return false;
if (coord.some((c) => c === null || c === void 0 || !isFinite(c))) {
return false;
}
}
}
return true;
};
if (!validateCoordinates(polygon2.geometry.coordinates) || !validateCoordinates(latlngs.geometry.coordinates)) {
return false;
}
try {
const fc = featureCollection$1([polygon2, latlngs]);
const intersection3 = index_default$b(fc);
if (intersection3 && intersection3.geometry && (intersection3.geometry.type === "Polygon" || intersection3.geometry.type === "MultiPolygon")) {
const polygonArea2 = index_default$g(intersection3);
if (polygonArea2 > 1e-6) {
return true;
}
}
} catch {
}
try {
const points1 = index_default$9(polygon2);
const points2 = index_default$9(latlngs);
for (const point2 of points2.features) {
if (index_default$6(point2, polygon2)) {
return true;
}
}
for (const point2 of points1.features) {
if (index_default$6(point2, latlngs)) {
return true;
}
}
} catch {
}
try {
const coords1 = getCoords$1(polygon2);
const coords2 = getCoords$1(latlngs);
for (const ring1 of coords1) {
const outerRing1 = ring1[0];
if (!outerRing1 || outerRing1.length < 2) continue;
for (let i = 0; i < outerRing1.length - 1; i++) {
const coord1 = outerRing1[i];
const coord2 = outerRing1[i + 1];
if (!coord1 || !coord2 || !Array.isArray(coord1) || !Array.isArray(coord2) || coord1.length < 2 || coord2.length < 2 || coord1.some((c) => c === null || c === void 0 || !isFinite(c)) || coord2.some((c) => c === null || c === void 0 || !isFinite(c))) {
continue;
}
const line1 = lineString$1([coord1, coord2]);
for (const ring2 of coords2) {
const outerRing2 = ring2[0];
if (!outerRing2 || outerRing2.length < 2) continue;
for (let j = 0; j < outerRing2.length - 1; j++) {
const coord3 = outerRing2[j];
const coord4 = outerRing2[j + 1];
if (!coord3 || !coord4 || !Array.isArray(coord3) || !Array.isArray(coord4) || coord3.length < 2 || coord4.length < 2 || coord3.some((c) => c === null || c === void 0 || !isFinite(c)) || coord4.some((c) => c === null || c === void 0 || !isFinite(c))) {
continue;
}
const line2 = lineString$1([coord3, coord4]);
try {
const intersection3 = index_default$d(line1, line2);
if (intersection3 && intersection3.features && intersection3.features.length > 0) {
return true;
}
} catch {
}
}
}
}
}
} catch {
}
try {
} catch (error) {
if (!isTestEnvironment()) {
console.warn(
"Error in bounding box check:",
error instanceof Error ? error.message : String(error)
);
}
}
return false;
} catch (error) {
if (!isTestEnvironment()) {
console.warn(
"Error in polygonIntersect:",
error instanceof Error ? error.message : String(error)
);
}
return false;
}
}
getIntersection(poly1, poly2) {
try {
const fc = featureCollection$1([poly1, poly2]);
const result = index_default$b(fc);
if (result && result.geometry && (result.geometry.type === "Polygon" || result.geometry.type === "MultiPolygon")) {
return result;
}
return null;
} catch (error) {
if (!isTestEnvironment()) {
console.warn(
"Error in getIntersection:",
error instanceof Error ? error.message : String(error)
);
}
return null;
}
}
getDistance(point1, point2) {
return distance$1(point1, point2);
}
isWithin(polygon1, polygon2) {
return index_default$4(polygon$2([polygon1]), polygon$2([polygon2]));
}
/**
* Check if one polygon is completely within another polygon
*/
isPolygonCompletelyWithin(innerPolygon, outerPolygon) {
try {
return index_default$4(innerPolygon, outerPolygon);
} catch {
const innerCoords = getCoords$1(innerPolygon);
const outerCoords = getCoords$1(outerPolygon);
for (const innerRing of innerCoords) {
for (const ring of innerRing) {
for (const coord of ring) {
const pt = point$1(coord);
let isInside = false;
for (const outerRing of outerCoords) {
for (const outerRingCoords of outerRing) {
const outerPoly = polygon$2([outerRingCoords]);
if (index_default$6(pt, outerPoly)) {
isInside = true;
break;
}
}
if (isInside) break;
}
if (!isInside) {
return false;
}
}
}
}
return true;
}
}
/**
* Normalize various point-like inputs into a GeoJSON Feature<Point>.
* Supports GeoJSON Feature<Point>, Turf Position [lng, lat], and Leaflet LatLngLiteral.
*/
toPointFeature(pt) {
const isPointFeature = (value) => {
var _a2;
if (!value || typeof value !== "object") return false;
const candidate = value;
return candidate.type === "Feature" && ((_a2 = candidate.geometry) == null ? void 0 : _a2.type) === "Point";
};
if (isPointFeature(pt)) {
return pt;
}
if (Array.isArray(pt) && pt.length >= 2 && typeof pt[0] === "number" && typeof pt[1] === "number") {
return point$1(pt);
}
const isLatLngLiteral = (value) => {
if (!value || typeof value !== "object") return false;
return typeof value.lat === "number" && typeof value.lng === "number";
};
if (isLatLngLiteral(pt)) {
const p = pt;
return point$1([p.lng, p.lat]);
}
const hasGeometryCoordinates = (value) => {
if (!value || typeof value !== "object") return false;
const maybeGeometry = value.geometry;
return !!maybeGeometry && Array.isArray(maybeGeometry.coordinates);
};
if (hasGeometryCoordinates(pt)) {
return point$1(pt.geometry.coordinates);
}
throw new Error("Unsupported point format provided to toPointFeature");
}
/**
* Check if a point lies within a polygon.
* Accepts Feature<Point>, Turf Position [lng, lat], or Leaflet LatLngLiteral.
* This normalization prevents noisy console warnings in tests.
*/
isPointInsidePolygon(pt, polygon2) {
try {
const pointFeature = this.toPointFeature(pt);
return index_default$6(pointFeature, polygon2);
} catch {
return false;
}
}
/**
* Checks if two polygons are equal.
* @param polygon1 First polygon.
* @param polygon2 Second polygon.
*/
equalPolygons(polygon1, polygon2) {
return index_default$3(polygon1, polygon2);
}
convertToBoundingBoxPolygon(polygon2) {
const bboxCoords = bbox$2(polygon2);
const bboxPoly = bboxPolygon(bboxCoords);
return bboxPoly;
}
polygonToMultiPolygon(poly) {
const multi = multiPolygon$2([poly.geometry.coordinates]);
return multi;
}
injectPointToPolygon(polygon2, point2, ringIndex) {
const newPoly = JSON.parse(JSON.stringify(polygon2));
let targetRing;
if (newPoly.geometry.type === "MultiPolygon") {
targetRing = newPoly.geometry.coordinates[0][ringIndex];
} else if (newPoly.geometry.type === "Polygon") {
targetRing = newPoly.geometry.coordinates[ringIndex];
} else {
return newPoly;
}
if (!targetRing) {
return newPoly;
}
let minDistance = Infinity;
let insertIndex = 0;
for (let i = 0; i < targetRing.length - 1; i++) {
const edgeStart = targetRing[i];
const edgeEnd = targetRing[i + 1];
const distance2 = this.distanceToLineSegment(point2, edgeStart, edgeEnd);
if (distance2 < minDistance) {
minDistance = distance2;
insertIndex = i + 1;
}
}
targetRing.splice(insertIndex, 0, point2);
return newPoly;
}
distanceToLineSegment(point2, lineStart, lineEnd) {
const A = point2[0] - lineStart[0];
const B2 = point2[1] - lineStart[1];
const C = lineEnd[0] - lineStart[0];
const D2 = lineEnd[1] - lineStart[1];
const dot2 = A * C + B2 * D2;
const lenSq = C * C + D2 * D2;
if (lenSq === 0) return Math.sqrt(A * A + B2 * B2);
const param = dot2 / lenSq;
let xx, yy;
if (param < 0) {
xx = lineStart[0];
yy = lineStart[1];
} else if (param > 1) {
xx = lineEnd[0];
yy = lineEnd[1];
} else {
xx = lineStart[0] + param * C;
yy = lineStart[1] + param * D2;
}
const dx = point2[0] - xx;
const dy = point2[1] - yy;
return Math.sqrt(dx * dx + dy * dy);
}
polygonDifference(polygon1, polygon2) {
try {
const fc = featureCollection$1([polygon1, polygon2]);
const diff = index_default$a(fc);
const result = diff ? this.getTurfPolygon(diff) : null;
return result;
} catch (error) {
if (!isTestEnvironment()) {
console.warn(
"Error in polygonDifference:",
error instanceof Error ? error.message : String(error)
);
}
return null;
}
}
getBoundingBoxCompassPosition(_polygon, _MarkerPosition, _useOffset, _offsetDirection) {
return null;
}
getNearestPointIndex(targetPoint, points) {
const nearest = index_default$2(targetPoint, points);
const props = nearest.properties;
return props.featureIndex ?? 0;
}
/**
* Convert LatLngLiteral object to js coordinate array format.
*
* This method serves as a semantic interface for coordinate conversion,
* ensuring consistent lng/lat order when interfacing with js functions.
* While simple, it provides a clear contract and future-proofing for any
* coordinate validation or transformation that might be needed later.
*
* @param point - LatLngLiteral object with lat/lng properties
* @returns js coordinate array in [lng, lat] format
*/
getCoord(point2) {
return [point2.lng, point2.lat];
}
/**
* Create a GeoJSON polygon feature from coordinates.
* This method provides access to the turf polygon helper function
* while keeping all turf imports centralized in this file.
*
* @param coordinates - Array of coordinate rings (outer ring + holes)
* @returns GeoJSON Feature<Polygon>
*/
createPolygon(coordinates) {
return polygon$2(coordinates);
}
getFeaturePointCollection(points) {
const pts = [];
points.forEach((v) => {
const p = point$1([v.lng, v.lat], {});
pts.push(p);
});
const fc = featureCollection$1(pts);
return fc;
}
getPolygonArea(poly) {
const polygonArea2 = index_default$g(poly);
return polygonArea2;
}
getPolygonPerimeter(poly) {
const polygonLength = index_default(poly, { units: "kilometers" });
return polygonLength;
}
getCenterOfMass(feature2) {
return index_default$e(feature2);
}
getDoubleElbowLatLngs(points) {
const doubleized = [];
const len = points.length;
const effectiveLen = points[0].lat === points[len - 1].lat && points[0].lng === points[len - 1].lng ? len - 1 : len;
for (let i = 0; i < effectiveLen; i++) {
const p1 = points[i];
const p2 = points[(i + 1) % effectiveLen];
doubleized.push(new L.LatLng(p1.lat, p1.lng));
const midPoint = midpoint(point$1([p1.lng, p1.lat]), point$1([p2.lng, p2.lat]));
doubleized.push(
new L.LatLng(midPoint.geometry.coordinates[1], midPoint.geometry.coordinates[0])
);
}
return doubleized;
}
getBezierMultiPolygon(polygonArray) {
const ensureClosedRing = (ring) => {
if (!ring || ring.length === 0) return ring;
const first = ring[0];
const last = ring[ring.length - 1];
if (first[0] === last[0] && first[1] === last[1]) {
return ring;
}
return [...ring, [first[0], first[1]]];
};
const resampleRing = (ring, targetCount) => {
const closed = ensureClosedRing(ring);
if (!closed || closed.length < 2) return closed;
const distance2 = (a, b) => {
const dx = a[0] - b[0];
const dy = a[1] - b[1];
return Math.sqrt(dx * dx + dy * dy);
};
let total = 0;
for (let i = 1; i < closed.length; i++) {
total += distance2(closed[i - 1], closed[i]);
}
if (!Number.isFinite(total) || total <= 0) {
return closed;
}
const step = total / Math.max(targetCount, 4);
const resampled = [closed[0]];
let carry = 0;
for (let i = 1; i < closed.length; i++) {
let prev = closed[i - 1];
const curr = closed[i];
let segLen = distance2(prev, curr);
while (carry + segLen >= step && segLen > 0) {
const t = (step - carry) / segLen;
const interp = [
prev[0] + (curr[0] - prev[0]) * t,
prev[1] + (curr[1] - prev[1]) * t
];
resampled.push(interp);
prev = interp;
segLen = distance2(prev, curr);
carry = 0;
}
carry += segLen;
}
return ensureClosedRing(resampled);
};
const dedupeRing = (ring) => {
if (!ring || ring.length === 0) return ring;
const tolerance = 1e-6;
const seen = /* @__PURE__ */ new Set();
const cleaned = [];
for (let i = 0; i < ring.length; i++) {
const pt = ring[i];
if (!Array.isArray(pt) || pt.length < 2) continue;
const key = `${Math.round(pt[0] / tolerance)}:${Math.round(pt[1] / tolerance)}`;
if (seen.has(key)) continue;
seen.add(key);
cleaned.push(pt);
}
return ensureClosedRing(cleaned);
};
const smoothedPolygons = polygonArray.map((poly) => {
return poly.map((ring) => {
const safeRing = ensureClosedRing(ring);
try {
const ls = lineString$1(safeRing);
const bez = index_default$1(ls, {
resolution: this.config.polygonTools.bezier.resolution,
sharpness: this.config.polygonTools.bezier.sharpness
});
const coords = bez.geometry.coordinates ?? safeRing;
const baseCount = Math.max(2, safeRing.length - 1);
const resampleMultiplier = Math.max(
1,
this.config.polygonTools.bezier.resampleMultiplier ?? 10
);
const maxNodes = Math.max(4, this.config.polygonTools.bezier.maxNodes ?? coords.length);
const targetCount = Math.max(4, Math.min(maxNodes, baseCount * resampleMultiplier));
const resampled = resampleRing(coords, targetCount);
const deduped = dedupeRing(resampled);
return deduped.length >= 4 ? deduped : safeRing;
} catch (error) {
if (!isTestEnvironment()) {
console.warn("Bezier smoothing failed for ring, falling back to original:", error);
}
return safeRing;
}
});
});
return this.getMultiPolygon(smoothedPolygons);
}
/**
* Check if a polygon has holes (more than one ring)
*/
polygonHasHoles(feature2) {
try {
if (feature2.geometry.type === "Polygon") {
return feature2.geometry.coordinates.length > 1;
} else if (feature2.geometry.type === "MultiPolygon") {
return feature2.geometry.coordinates.some((polygon2) => polygon2.length > 1);
}
return false;
} catch (error) {
if (!isTestEnvironment()) {
console.warn(
"Error checking for holes:",
error instanceof Error ? error.message : String(error)
);
}
return false;
}
}
/**
* Handle kinks in polygons with holes while preserving hole structure
*/
getKinksWithHolePreservation(feature2) {
try {
if (feature2.geometry.type === "Polygon") {
const coordinates = feature2.geometry.coordinates;
const outerRing = coordinates[0];
const holes = coordinates.slice(1);
const outerPolygon = {
type: "Feature",
geometry: {
type: "Polygon",
coordinates: [outerRing]
},
properties: feature2.properties || {}
};
const outerHasKinks = this.hasKinks(outerPolygon);
const outerRingIntersectsHoles = this.checkOuterRingHoleIntersection(outerRing, holes);
if (outerHasKinks || outerRingIntersectsHoles) {
if (outerHasKinks) {
} else {
}
const dragGoesCompletelyThroughHole = this.checkIfDragCompletelyThroughHole(
outerRing,
holes
);
if (dragGoesCompletelyThroughHole) {
return this.handleCompleteHoleTraversal(outerPolygon, holes);
}
const unkink = index_default$5(outerPolygon);
const splitPolygons = [];
featureEach$5(unkink, (splitPolygon) => {
const polygonWithBites = this.subtractIntersectingHoles(splitPolygon, holes);
splitPolygons.push(polygonWithBites);
});
return splitPolygons;
} else {
return [feature2];
}
} else if (feature2.geometry.type === "MultiPolygon") {
const allResults = [];
for (const polygonCoords of feature2.geometry.coordinates) {
const singlePolygon = {
type: "Feature",
geometry: {
type: "Polygon",
coordinates: polygonCoords
},
properties: feature2.properties || {}
};
const results = this.getKinksWithHolePreservation(singlePolygon);
allResults.push(...results);
}
return allResults;
}
return [feature2];
} catch (error) {
if (!isTestEnvironment()) {
console.warn(
"Error in getKinksWithHolePreservation:",
error instanceof Error ? error.message : String(error)
);
}
return [feature2];
}
}
/**
* Check if the drag line goes completely through a hole (like cutting cake)
*/
checkIfDragCompletelyThroughHole(outerRing, holes) {
try {
const duplicatePoints = this.findDuplicatePoints(outerRing);
if (duplicatePoints.length > 0) {
return true;
}
const selfIntersectionLine = this.findSelfIntersectionLine(outerRing);
if (selfIntersectionLine && holes.length > 0) {
for (const hole of holes) {
if (this.lineCompletelyTraversesHole(selfIntersectionLine, hole)) {
return true;
}
}
}
if (this.hasKinks({
type: "Feature",
geometry: { type: "Polygon", coordinates: [outerRing] },
properties: {}
})) {
for (const hole of holes) {
if (this.holeIsCutByKinks(outerRing, hole)) {
return true;
}
}
}
return false;
} catch (error) {
if (!isTestEnvironment()) {
console.warn(
"Error checking complete hole traversal:",
error instanceof Error ? error.message : String(error)
);
}
return false;
}
}
/**
* Find the line created by self-intersection in the outer ring
*/
findSelfIntersectionLine(outerRing) {
try {
const pointCounts = /* @__PURE__ */ new Map();
for (let i = 0; i < outerRing.length - 1; i++) {
const point2 = outerRing[i];
const key = `${point2[0]},${point2[1]}`;
if (!pointCounts.has(key)) {
pointCounts.set(key, { point: point2, indices: [i] });
} else {
pointCounts.get(key).indices.push(i);
}
}
for (const [, data] of pointCounts) {
if (data.indices.length >= 2) {
const startIndex = data.indices[0];
const endIndex = data.indices[1];
const line = [];
for (let i = startIndex; i <= endIndex; i++) {
line.push(outerRing[i]);
}
return line;
}
}
return null;
} catch (error) {
if (!isTestEnvironment()) {
console.warn(
"Error finding self-intersection line:",
error instanceof Error ? error.message : String(error)
);
}
return null;
}
}
/**
* Check if a line completely traverses a hole
*/
lineCompletelyTraversesHole(line, hole) {
try {
if (line.length < 2) return false;
const lineStart = line[0];
const lineEnd = line[line.length - 1];
const holePolygon = polygon$2([hole]);
const startInHole = index_default$6(point$1(lineStart), holePolygon);
const endInHole = index_default$6(point$1(lineEnd), holePolygon);
if (startInHole !== endInHole) {
return true;
}
return false;
} catch (error) {
if (!isTestEnvironment()) {
console.warn(
"Error checking line hole traversal:",
error instanceof Error ? error.message : String(error)
);
}
return false;
}
}
/**
* Check if a hole is cut by the kinks in the outer ring
*/
holeIsCutByKinks(outerRing, hole) {
try {
const outerPolygon = polygon$2([outerRing]);
const holePolygon = polygon$2([hole]);
const kinkFeatures = index_default$7(outerPolygon);
if (kinkFeatures.features.length === 0) return false;
for (const kink of kinkFeatures.features) {
const kinkPt = kink.geometry.coordinates;
try {
const d = distance$1(point$1(kinkPt), centroid$1(holePolygon));
if (d < 0.01) {
return true;
}
if (index_default$6(point$1(kinkPt), holePolygon)) {
return true;
}
} catch {
if (index_default$6(point$1(kinkPt), holePolygon)) {
return true;
}
}
}
return false;
} catch (error) {
if (!isTestEnvironment()) {
console.warn("Error checking hole cut by kinks:", error.message);
}
return false;
}
}
/**
* Find duplicate points in a ring (excluding first/last which should be the same)
*/
findDuplicatePoints(ring) {
const duplicates = [];
const seen = /* @__PURE__ */ new Set();
for (let i = 0; i < ring.length - 1; i++) {
const point2 = ring[i];
const key = `${point2[0]},${point2[1]}`;
if (seen.has(key)) {
duplicates.push(point2);
} else {
seen.add(key);
}
}
return duplicates;
}
/**
* Check if outer ring intersects with holes (fallback detection for hole traversal)
*/
checkOuterRingHoleIntersection(outerRing, holes) {
try {
if (holes.length === 0) {
return false;
}
const outerPolygon = polygon$2([outerRing]);
for (const hole of holes) {
const holePolygon = polygon$2([hole]);
const intersection3 = this.getIntersection(outerPolygon, holePolygon);
if (intersection3) {
return true;
}
for (let i = 0; i < outerRing.length - 1; i++) {
const pt = outerRing[i];
const pointInHole = index_default$6(point$1(pt), holePolygon);
if (pointInHole) {
return true;
}
}
}
return false;
} catch {
return false;
}
}
/**
* Handle complete hole traversal - create solid polygons (cake cutting)
*/
handleCompleteHoleTraversal(outerPolygon, _holes) {
try {
const solidPolygon = {
type: "Feature",
geometry: {
type: "Polygon",
coordinates: [outerPolygon.geometry.coordinates[0]]
// ONLY outer ring
},
properties: outerPolygon.properties || {}
};
const unkink = index_default$5(solidPolygon);
const resultPolygons = [];
featureEach$5(unkink, (splitPolygon) => {
splitPolygon._polydrawHoleTraversalOccurred = true;
resultPolygons.push(splitPolygon);
});
return resultPolygons;
} catch (error) {
if (!isTestEnvironment()) {
console.warn(
"Error handling complete hole traversal:",
error instanceof Error ? error.message : String(error)
);
}
try {
const fallbackPolygon = {
...outerPolygon,
geometry: {
...outerPolygon.geometry,
coordinates: [outerPolygon.geometry.coordinates[0]]
// Only outer ring
}
};
fallbackPolygon._polydrawHoleTraversalOccurred = true;
return [fallbackPolygon];
} catch {
return [outerPolygon];
}
}
}
/**
* Subtract intersecting holes from a polygon to create proper "bite" shapes
*/
subtractIntersectingHoles(basePolygon, holes) {
try {
let resultPolygon = basePolygon;
for (const hole of holes) {
const holePolygon = polygon$2([hole]);
const intersection3 = this.getIntersection(holePolygon, resultPolygon);
if (intersection3) {
const difference22 = this.polygonDifference(resultPolygon, holePolygon);
if (difference22) {
if (difference22.geometry.type === "Polygon") {
resultPolygon = difference22;
} else if (difference22.geometry.type === "MultiPolygon") {
const multiPoly = difference22;
let largestArea = 0;
let largestPolygon = null;
for (const coords of multiPoly.geometry.coordinates) {
const poly = {
type: "Feature",
geometry: { type: "Polygon", coordinates: coords },
properties: {}
};
const polygonArea2 = index_default$g(poly);
if (polygonArea2 > largestArea) {
largestArea = polygonArea2;
largestPolygon = poly;
}
}
if (largestPolygon) {
resultPolygon = largestPolygon;
}
}
} else {
}
} else {
}
}
return resultPolygon;
} catch (error) {
if (!isTestEnvironment()) {
console.warn(
"Error subtracting intersecting holes:",
error instanceof Error ? error.message : String(error)
);
}
return basePolygon;
}
}
/**
* Remove duplicate vertices from a polygon to prevent turf errors
*/
removeDuplicateVertices(feature2) {
if (!feature2 || !feature2.geometry || !feature2.geometry.coordinates) {
if (!isTestEnvironment()) {
console.warn("Invalid feature passed to removeDuplicateVertices");
}
return feature2;
}
const cleanCoordinates = (coords) => {
if (!coords || coords.length < 3) {
if (!isTestEnvironment()) {
console.warn("Invalid coordinates array - need at least 3 points for a polygon");
}
return coords || [];
}
const cleaned = [];
const tolerance = 1e-6;
const seen = /* @__PURE__ */ new Set();
for (let i = 0; i < coords.length; i++) {
const current = coords[i];
const next = coords[(i + 1) % coords.length];
if (!current || !Array.isArray(current) || current.length < 2 || !next || !Array.isArray(next) || next.length < 2) {
continue;
}
const latDiff = Math.abs(current[1] - next[1]);
const lngDiff = Math.abs(current[0] - next[0]);
const key = `${Math.round(current[0] / tolerance)}:${Math.round(current[1] / tolerance)}`;
const isDuplicate = seen.has(key);
if ((latDiff > tolerance || lngDiff > tolerance) && !isDuplicate) {
cleaned.push(current);
seen.add(key);
}
}
if (cleaned.length < 3) {
if (!isTestEnvironment()) {
console.warn("After cleaning, polygon has less than 3 points");
}
return coords;
}
if (cleaned.length > 0) {
const first = cleaned[0];
const last = cleaned[cleaned.length - 1];
const latDiff = Math.abs(first[1] - last[1]);
const lngDiff = Math.abs(first[0] - last[0]);
if (latDiff > tolerance || lngDiff > tolerance) {
cleaned.push([first[0], first[1]]);
}
}
return cleaned;
};
try {
if (feature2.geometry.type === "Polygon") {
const cleanedCoords = feature2.geometry.coordinates.map((ring) => cleanCoordinates(ring));
if (cleanedCoords.some((ring) => ring.length < 4)) {
if (!isTestEnvironment()) {
console.warn("Cleaned polygon has invalid ring with less than 4 coordinates");
}
return feature2;
}
return {
...feature2,
geometry: {
...feature2.geometry,
coordinates: cleanedCoords
}
};
} else if (feature2.geometry.type === "MultiPolygon") {
const cleanedCoords = feature2.geometry.coordinates.map(
(polygon2) => polygon2.map((ring) => cleanCoordinates(ring))
);
if (cleanedCoords.some((polygon2) => polygon2.some((ring) => ring.length < 4))) {
if (!isTestEnvironment()) {
console.warn("Cleaned multipolygon has invalid ring with less than 4 coordinates");
}
return feature2;
}
return {
...feature2,
geometry: {
...feature2.geometry,
coordinates: cleanedCoords
}
};
}
} catch (error) {
if (!isTestEnvironment()) {
console.warn(
"Error in removeDuplicateVertices:",
error instanceof Error ? error.message : String(error)
);
}
return feature2;
}
return feature2;
}
}
const getGlobalLeaflet = () => {
if (typeof globalThis === "undefined") return void 0;
return globalThis.L;
};
class LeafletVersionDetector {
/**
* Detects the Leaflet version being used
* @returns The detected Leaflet version
*/
static getVersion() {
if (this._detectedVersion) {
return this._detectedVersion;
}
const globalL = getGlobalLeaflet();
if (!globalL) {
return LeafletVersion.V2;
}
this._detectedVersion = this.detectVersion(globalL);
return this._detectedVersion;
}
/**
* Performs the actual version detection
* @returns The detected Leaflet version
*/
static detectVersion(globalL) {
const resolvedGlobal = globalL ?? getGlobalLeaflet();
if (!resolvedGlobal) {
return LeafletVersion.V2;
}
if (resolvedGlobal.version) {
if (resolvedGlobal.version.startsWith("2.")) {
return LeafletVersion.V2;
}
if (resolvedGlobal.version.startsWith("1.")) {
return LeafletVersion.V1;
}
}
if (typeof resolvedGlobal.marker !== "function") {
return LeafletVersion.V2;
}
if (typeof resolvedGlobal.marker === "function" && typeof resolvedGlobal.polyline === "function" && typeof resolvedGlobal.polygon === "function") {
return LeafletVersion.V1;
}
if (resolvedGlobal.Marker && typeof resolvedGlobal.Marker === "function" && resolvedGlobal.Polyline && typeof resolvedGlobal.Polyline === "function") {
return LeafletVersion.V2;
}
return LeafletVersion.V1;
}
/**
* Checks if we're running Leaflet v1.x
* @returns true if running v1.x
*/
static isV1() {
return this.getVersion() === LeafletVersion.V1;
}
/**
* Checks if we're running Leaflet v2.x
* @returns true if running v2.x
*/
static isV2() {
return this.getVersion() === LeafletVersion.V2;
}
/**
* Resets the cached version detection (useful for testing)
*/
static reset() {
this._detectedVersion = null;
}
/**
* Gets detailed version information for debugging
* @returns Object with version details
*/
static getVersionInfo() {
const version = this.getVersion();
const globalL = getGlobalLeaflet();
return {
version,
leafletVersion: globalL == null ? void 0 : globalL.version,
hasFactoryMethods: typeof (globalL == null ? void 0 : globalL.marker) === "function",
hasConstructors: !!(globalL == null ? void 0 : globalL.Marker) && typeof globalL.Marker === "function",
globalLAvailable: !!globalL
};
}
}
__publicField(LeafletVersionDetector, "_detectedVersion", null);
class LeafletAdapter {
constructor() {
/**
* DOM utility methods compatibility
*/
__publicField(this, "domUtil", {
create: (tagName, className, container) => {
if (typeof L !== "undefined" && L.DomUtil) {
return L.DomUtil.create(tagName, className, container);
} else {
const element = document.createElement(tagName);
if (className) {
element.className = className;
}
if (container) {
container.appendChild(element);
}
return element;
}
},
addClass: (el, name) => {
if (this.version === LeafletVersion.V1) {
L.DomUtil.addClass(el, name);
} else {
el.classList.add(name);
}
},
removeClass: (el, name) => {
if (this.version === LeafletVersion.V1) {
L.DomUtil.removeClass(el, name);
} else {
el.classList.remove(name);
}
},
hasClass: (el, name) => {
if (this.version === LeafletVersion.V1) {
return L.DomUtil.hasClass(el, name);
} else {
return el.classList.contains(name);
}
},
setPosition: (el, point2) => {
L.DomUtil.setPosition(el, point2);
},
getPosition: (el) => {
return L.DomUtil.getPosition(el);
}
});
/**
* DOM event methods compatibility
*/
__publicField(this, "domEvent", {
on: (obj, types, fn, context) => {
L.DomEvent.on(obj, types, fn, context);
return this;
},
off: (obj, types, fn, context) => {
L.DomEvent.off(obj, types, fn, context);
return this;
},
stopPropagation: (e) => {
L.DomEvent.stopPropagation(e);
return this;
},
preventDefault: (e) => {
L.DomEvent.preventDefault(e);
return this;
},
stop: (e) => {
L.DomEvent.stop(e);
return this;
},
disableClickPropagation: (el) => {
L.DomEvent.disableClickPropagation(el);
return this;
},
disableScrollPropagation: (el) => {
L.DomEvent.disableScrollPropagation(el);
return this;
},
getMousePosition: (e, container) => {
if (this.version === LeafletVersion.V1) {
return L.DomEvent.getMousePosition(e, container);
} else {
const domEvent = L.DomEvent;
return domEvent.getPointerPosition ? domEvent.getPointerPosition(e, container) : L.DomEvent.getMousePosition(e, container);
}
}
});
/**
* Utility methods compatibility
*/
__publicField(this, "util", {
extend: (dest, ...sources) => {
if (this.version === LeafletVersion.V1) {
return L.Util.extend(dest, ...sources);
} else {
return Object.assign(dest, ...sources);
}
},
bind: (fn, obj) => {
if (this.version === LeafletVersion.V1) {
return L.Util.bind(fn, obj);
} else {
return fn.bind(obj);
}
},
stamp: (obj) => {
return L.Util.stamp(obj);
},
throttle: (fn, time, context) => {
return L.Util.throttle(fn, time, context);
},
wrapNum: (x, range, includeMax) => {
return L.Util.wrapNum(x, range, includeMax);
},
falseFn: () => {
return L.Util.falseFn();
},
formatNum: (num, digits) => {
return L.Util.formatNum(num, digits);
},
trim: (str) => {
if (this.version === LeafletVersion.V1) {
return L.Util.trim(str);
} else {
return str.trim();
}
},
splitWords: (str) => {
return L.Util.splitWords(str);
},
setOptions: (obj, options) => {
return L.Util.setOptions(obj, options);
},
getParamString: (obj, existingUrl, uppercase) => {
if (this.version === LeafletVersion.V1) {
return L.Util.getParamString(obj, existingUrl, uppercase);
} else {
const params = new URLSearchParams();
for (const key in obj) {
if (Object.prototype.hasOwnProperty.call(obj, key)) {
const paramKey = uppercase ? key.toUpperCase() : key;
params.set(paramKey, String(obj[key]));
}
}
const paramString = params.toString();
if (existingUrl) {
const separator = existingUrl.indexOf("?") === -1 ? "?" : "&";
return existingUrl + separator + paramString;
}
return paramString ? "?" + paramString : "";
}
},
template: (str, data) => {
return L.Util.template(str, data);
},
isArray: (obj) => {
if (this.version === LeafletVersion.V1) {
return L.Util.isArray(obj);
} else {
return Array.isArray(obj);
}
},
indexOf: (array, item) => {
return L.Util.indexOf(array, item);
},
requestAnimFrame: (fn, context, immediate) => {
if (this.version === LeafletVersion.V1) {
return L.Util.requestAnimFrame(fn, context, immediate);
} else {
return requestAnimationFrame(context ? fn.bind(context) : fn);
}
},
cancelAnimFrame: (id) => {
if (this.version === LeafletVersion.V1) {
L.Util.cancelAnimFrame(id);
} else {
cancelAnimationFrame(id);
}
}
});
}
get version() {
return LeafletVersionDetector.getVersion();
}
/**
* Creates a tile layer compatible with both Leaflet versions
*/
createTileLayer(urlTemplate, options) {
return new L.TileLayer(urlTemplate, options);
}
/**
* Creates a map compatible with both Leaflet versions
*/
createMap(element, options) {
return new L.Map(element, options);
}
/**
* Creates a marker compatible with both Leaflet versions
*/
createMarker(latlng, options) {
return new L.Marker(latlng, options);
}
/**
* Creates a polyline compatible with both Leaflet versions
*/
createPolyline(latlngs, options) {
return new L.Polyline(latlngs, options);
}
/**
* Creates a polygon compatible with both Leaflet versions
*/
createPolygon(latlngs, options) {
return new L.Polygon(latlngs, options);
}
/**
* Creates a div icon compatible with both Leaflet versions
*/
createDivIcon(options) {
return new L.DivIcon(options);
}
/**
* Creates a LatLng object compatible with both Leaflet versions
*/
createLatLng(lat, lng, alt) {
return new L.LatLng(lat, lng, alt);
}
/**
* Creates a LatLngBounds object compatible with both Leaflet versions
*/
createLatLngBounds(corner1, corner2) {
if (corner1 && corner2) {
return new L.LatLngBounds(corner1, corner2);
}
if (corner1) {
return new L.LatLngBounds(corner1, corner1);
}
return new L.LatLngBounds([]);
}
/**
* Creates a Point object compatible with both Leaflet versions
*/
createPoint(x, y, round) {
return new L.Point(x, y, round);
}
/**
* Creates a popup compatible with both Leaflet versions
*/
createPopup(options, source) {
return new L.Popup(options, source);
}
/**
* Creates a feature group compatible with both Leaflet versions
*/
createFeatureGroup(layers) {
return new L.FeatureGroup(layers);
}
/**
* Creates a layer group compatible with both Leaflet versions
*/
createLayerGroup(layers) {
return new L.LayerGroup(layers);
}
/**
* Browser detection compatibility
*/
getBrowser() {
const self = this;
const browser = L.Browser;
return {
get ie() {
return self.version === LeafletVersion.V1 ? L.Browser.ie : false;
},
get ielt9() {
return self.version === LeafletVersion.V1 ? L.Browser.ielt9 : false;
},
get edge() {
return self.version === LeafletVersion.V1 ? L.Browser.edge : false;
},
get webkit() {
return self.version === LeafletVersion.V1 ? L.Browser.webkit : false;
},
get android() {
return self.version === LeafletVersion.V1 ? L.Browser.android : /Android/.test(navigator.userAgent);
},
get android23() {
return self.version === LeafletVersion.V1 ? L.Browser.android23 : false;
},
get androidStock() {
return self.version === LeafletVersion.V1 ? L.Browser.androidStock : false;
},
get opera() {
return self.version === LeafletVersion.V1 ? L.Browser.opera : false;
},
get chrome() {
return self.version === LeafletVersion.V1 ? L.Browser.chrome : /Chrome/.test(navigator.userAgent);
},
get gecko() {
return self.version === LeafletVersion.V1 ? L.Browser.gecko : false;
},
get safari() {
return self.version === LeafletVersion.V1 ? L.Browser.safari : /Safari/.test(navigator.userAgent) && !/Chrome/.test(navigator.userAgent);
},
get phantom() {
return self.version === LeafletVersion.V1 ? browser.phantom ?? false : false;
},
get opera12() {
return self.version === LeafletVersion.V1 ? L.Browser.opera12 : false;
},
get win() {
return self.version === LeafletVersion.V1 ? L.Browser.win : /Win/.test(navigator.platform);
},
get ie3d() {
return self.version === LeafletVersion.V1 ? L.Browser.ie3d : false;
},
get webkit3d() {
return self.version === LeafletVersion.V1 ? L.Browser.webkit3d : false;
},
get gecko3d() {
return self.version === LeafletVersion.V1 ? L.Browser.gecko3d : false;
},
get any3d() {
return self.version === LeafletVersion.V1 ? L.Browser.any3d : true;
},
get mobile() {
return L.Browser.mobile;
},
get mobileWebkit() {
return self.version === LeafletVersion.V1 ? L.Browser.mobileWebkit : false;
},
get mobileWebkit3d() {
return self.version === LeafletVersion.V1 ? L.Browser.mobileWebkit3d : false;
},
get msPointer() {
return self.version === LeafletVersion.V1 ? L.Browser.msPointer : false;
},
get pointer() {
return L.Browser.pointer || true;
},
get touch() {
return L.Browser.touch;
},
get touchNative() {
return self.version === LeafletVersion.V1 ? browser.touchNative ?? false : false;
},
get mobileOpera() {
return self.version === LeafletVersion.V1 ? L.Browser.mobileOpera : false;
},
get mobileGecko() {
return self.version === LeafletVersion.V1 ? L.Browser.mobileGecko : false;
},
get retina() {
return L.Browser.retina;
},
get passiveEvents() {
return self.version === LeafletVersion.V1 ? browser.passiveEvents ?? true : true;
},
get canvas() {
return self.version === LeafletVersion.V1 ? L.Browser.canvas : true;
},
get svg() {
return self.version === LeafletVersion.V1 ? L.Browser.svg : true;
},
get vml() {
return self.version === LeafletVersion.V1 ? L.Browser.vml : false;
},
get inlineSvg() {
return self.version === LeafletVersion.V1 ? browser.inlineSvg ?? true : true;
}
};
}
/**
* Gets the current Leaflet version being used
*/
getVersion() {
return this.version;
}
/**
* Checks if we're running Leaflet v1.x
*/
isV1() {
return this.version === LeafletVersion.V1;
}
/**
* Checks if we're running Leaflet v2.x
*/
isV2() {
return this.version === LeafletVersion.V2;
}
}
const leafletAdapter = new LeafletAdapter();
function applySvgIcon(button, svgMarkup) {
button.innerHTML = svgMarkup;
const svgElement = button.querySelector("svg");
if (!svgElement) return;
normalizeViewBox(svgElement);
inlineSvgStyles(svgElement);
svgElement.setAttribute("width", "24");
svgElement.setAttribute("height", "24");
svgElement.style.pointerEvents = "none";
svgElement.querySelectorAll("*").forEach((el) => {
el.style.pointerEvents = "none";
});
}
function normalizeViewBox(svgElement) {
if (svgElement.hasAttribute("viewBox")) return;
const widthAttr = svgElement.getAttribute("width");
const heightAttr = svgElement.getAttribute("height");
const width = widthAttr ? parseFloat(widthAttr) : NaN;
const height = heightAttr ? parseFloat(heightAttr) : NaN;
if (Number.isFinite(width) && Number.isFinite(height) && width > 0 && height > 0) {
svgElement.setAttribute("viewBox", `0 0 ${width} ${height}`);
}
}
function inlineSvgStyles(svgElement) {
const styleElements = Array.from(svgElement.querySelectorAll("style"));
if (styleElements.length === 0) return;
const inlinedClasses = /* @__PURE__ */ new Set();
styleElements.forEach((styleElement) => {
const cssText = styleElement.textContent;
if (!cssText) {
styleElement.remove();
return;
}
const ruleRegex = /([^{]+)\{([^}]*)\}/g;
let match;
while ((match = ruleRegex.exec(cssText)) !== null) {
const selectors = match[1].split(",").map((selector) => selector.trim()).filter(Boolean);
const declarations = match[2];
selectors.forEach((selector) => {
if (!selector.startsWith(".")) return;
const className = selector.slice(1);
inlinedClasses.add(className);
svgElement.querySelectorAll(`.${className}`).forEach((el) => {
declarations.split(";").forEach((declaration) => {
const [property, value] = declaration.split(":");
if (!property || !value) return;
el.style.setProperty(property.trim(), value.trim());
});
});
});
}
styleElement.remove();
});
if (inlinedClasses.size === 0) return;
svgElement.querySelectorAll("[class]").forEach((el) => {
const remaining = Array.from(el.classList).filter((cls) => !inlinedClasses.has(cls));
if (remaining.length > 0) {
el.setAttribute("class", remaining.join(" "));
} else {
el.removeAttribute("class");
}
});
}
const iconPolydraw2Svg = '<?xml version="1.0" encoding="UTF-8" standalone="no"?>\n<svg xmlns="http://www.w3.org/2000/svg" id="svg1" width="512" height="512" version="1.1">\n <path id="Shape" fill="#000" fill-rule="evenodd" stroke="none" d="M73.574 425.687c-1.14.188-4.099-.592-6.574-1.735-2.475-1.142-5.513-3.027-6.75-4.188C58 417.65 58 417.65 58 348.144c0-69.506 0-69.506 6.152-80.075a1599.57 1599.57 0 0 1 12.84-21.569c3.677-6.05 12.679-21.125 20.004-33.5 7.324-12.375 15.666-26.325 18.538-31 2.87-4.675 13.163-21.88 22.872-38.233a33686.921 33686.921 0 0 1 34.229-57.5c9.117-15.272 18.261-30.022 20.322-32.777 2.298-3.074 5.78-6.117 9.008-7.874 3.583-1.95 7.22-3.017 11.399-3.342 4.972-.388 7.445.018 13.04 2.137 3.799 1.439 10.999 4.919 16 7.733 5.003 2.814 13.371 7.613 18.596 10.663s13.775 8.134 19 11.297c5.225 3.163 12.061 7.246 15.192 9.073 3.13 1.828 9.402 5.521 13.936 8.208 4.535 2.686 10.397 6.897 13.027 9.357 3.265 3.054 5.457 6.261 6.912 10.115 1.672 4.43 2.036 7.161 1.694 12.704-.324 5.244-1.186 8.511-3.349 12.695-1.601 3.098-9.388 16.57-17.303 29.939-7.915 13.368-16.24 27.326-18.5 31.019a3087.099 3087.099 0 0 0-9.697 16c-3.073 5.107-11.2 18.736-18.061 30.286-6.86 11.55-16.45 27.75-21.312 36-4.86 8.25-11.179 18.825-14.04 23.5-2.862 4.675-8.308 13.83-12.101 20.343-3.794 6.513-10.508 17.762-14.92 24.997-7.721 12.664-8.263 13.29-14.5 16.77-3.563 1.987-15.357 8.416-26.21 14.286-10.851 5.87-28.851 15.66-40 21.756-11.147 6.096-25.86 14.032-32.694 17.638-6.834 3.605-13.359 6.709-14.5 6.897ZM116 376.754c2.096-.21 12.525-5.28 26-12.636 12.375-6.757 23.394-13.153 24.486-14.215 1.495-1.453 1.734-2.331.966-3.548-.56-.889-7.304-5.352-14.986-9.918-7.681-4.565-17.116-10.268-20.966-12.672-3.85-2.405-15.046-9.073-24.88-14.818-9.834-5.746-19.087-10.976-20.561-11.623-1.641-.72-3.24-.831-4.12-.287-.981.606-1.598 3.444-1.939 8.924-.275 4.42-.347 17.312-.16 28.65.341 20.616.341 20.616 3.5 23.908 1.738 1.81 9.01 6.733 16.16 10.94 10.754 6.327 13.605 7.587 16.5 7.295ZM184.98 329c.9 0 2.342-.563 3.204-1.25.86-.688 3.803-4.85 6.537-9.25 2.735-4.4 17.476-28.925 32.759-54.5 15.283-25.575 29.64-49.497 31.904-53.16 2.264-3.662 6.141-10.152 8.616-14.422 2.475-4.27 5.957-10.274 7.738-13.341 1.785-3.075 3.244-7.005 3.25-8.759.011-2.982-.694-3.614-11.238-10.077A2882.894 2882.894 0 0 0 249 152.847c-4.125-2.475-13.245-7.94-20.267-12.145a9841.507 9841.507 0 0 1-20.5-12.309 1566.199 1566.199 0 0 0-14.685-8.73c-3.823-2.238-8.13-4.242-9.57-4.453-1.998-.293-3.333.43-5.64 3.057-1.661 1.893-5.368 7.444-8.237 12.337a2040.256 2040.256 0 0 1-11.477 19.251 5775.594 5775.594 0 0 0-12.325 20.5A7048.542 7048.542 0 0 1 131.47 195c-4.823 7.975-10.898 18.1-13.501 22.5-2.604 4.4-8.45 14.075-12.991 21.5-4.542 7.425-10.268 16.988-12.725 21.252-3.26 5.657-4.304 8.405-3.861 10.168.454 1.81 4.239 4.57 15.1 11.01 7.973 4.726 20.686 12.257 28.252 16.735 7.566 4.477 17.582 10.485 22.257 13.351 4.675 2.866 13.19 7.973 18.921 11.348 5.732 3.375 11.158 6.136 12.059 6.136Zm100.681-194.022c2.318.016 3.852-.798 5.75-3.055 1.497-1.78 2.589-4.313 2.589-6.01 0-2.269-.848-3.508-3.75-5.478-2.063-1.4-6.53-4.29-9.927-6.421-3.398-2.132-9.923-6.015-14.5-8.63-4.578-2.614-10.798-6.408-13.823-8.43-3.025-2.022-7.75-4.873-10.5-6.334-2.75-1.462-8.15-4.689-12-7.171-5.023-3.24-7.838-4.434-9.968-4.232-2.1.2-3.65 1.298-5.297 3.75-1.437 2.14-2.215 4.588-2.032 6.391.254 2.498 1.604 3.72 9.297 8.412 4.95 3.02 15.525 9.427 23.5 14.238a3631.896 3631.896 0 0 1 26 15.848c7.807 4.82 12.515 7.107 14.661 7.122Z"/>\n <path id="path1" fill="#000" fill-rule="evenodd" stroke="none" d="M479.5 119.5c0 34 0 34-7 34.262-3.85.144-11.275.947-16.5 1.785s-12.533 2.42-16.24 3.514c-3.708 1.095-9.416 3.559-12.686 5.475-3.269 1.916-7.401 4.71-9.183 6.209-1.781 1.499-4.926 5.336-6.988 8.526-2.395 3.706-4.41 8.588-5.576 13.515-1.346 5.684-1.817 11.548-1.79 22.282.023 8.902.749 19.208 1.866 26.5 1.006 6.563 2.964 17.107 4.352 23.432 1.388 6.325 3.152 14.2 3.92 17.5.768 3.3 3.14 14.55 5.272 25s4.816 24.901 5.965 32.114c1.57 9.86 2.088 17.705 2.088 31.622 0 16.395-.284 19.635-2.485 28.386-1.367 5.433-4.299 13.56-6.515 18.061-2.832 5.75-6.265 10.559-11.545 16.172-5.308 5.642-10.516 9.846-17.735 14.316-5.62 3.481-13.145 7.611-16.72 9.179-3.575 1.567-10.739 4.4-15.92 6.293-5.181 1.894-13.506 4.308-18.5 5.365-4.994 1.056-13.58 2.637-19.08 3.513-6.633 1.057-18.754 1.716-36 1.958-14.3.201-30.5.01-36-.424s-15.625-1.537-22.5-2.45c-6.875-.912-16.575-2.328-21.557-3.146-4.98-.817-12.406-2.176-16.5-3.02-4.093-.843-9.693-2.157-12.443-2.92-2.75-.763-7.7-2.083-11-2.932-3.3-.85-10.5-2.648-16-3.996-5.5-1.348-15.85-4.3-23-6.56-7.15-2.261-14.912-4.805-17.25-5.653-2.904-1.055-4.25-2.127-4.25-3.386 0-1.228 3.252-3.97 9.75-8.22 5.363-3.507 13.202-8.277 17.421-10.6 7.671-4.225 7.671-4.225 18.5-1.287 5.956 1.616 19.154 4.998 29.329 7.515 10.175 2.517 23.45 5.465 29.5 6.55 6.05 1.087 15.05 2.427 20 2.98 4.95.552 14.85 1.53 22 2.175 8.107.73 21.657.955 36 .598 13.264-.33 27.021-1.234 32.5-2.135 5.225-.86 14-2.963 19.5-4.673s12.925-4.443 16.5-6.073a870.93 870.93 0 0 0 11.5-5.354c2.75-1.314 7.94-4.836 11.535-7.826 3.888-3.234 7.591-7.403 9.142-10.29 1.433-2.668 3.653-8.143 4.932-12.166 1.934-6.082 2.319-9.415 2.283-19.794-.024-7.149-.774-16.627-1.756-22.186-.943-5.339-2.407-13.531-3.253-18.206-.847-4.675-2.454-12.775-3.572-18-1.118-5.225-3.15-14-4.514-19.5-1.365-5.5-3.5-14.725-4.746-20.5-1.246-5.775-2.995-15-3.886-20.5-.892-5.5-2.081-14.08-2.643-19.068-.562-4.987-1.014-13.987-1.005-20 .01-6.012.652-14.799 1.427-19.525.775-4.726 2.12-11.026 2.988-14 .869-2.974 3.04-8.224 4.824-11.668 1.784-3.443 5.256-9.004 7.715-12.356 2.46-3.353 8.084-8.988 12.5-12.523 4.416-3.534 11.629-8.164 16.029-10.287 4.4-2.124 12.05-5.194 17-6.823 4.95-1.63 14.625-3.757 21.5-4.729s16.1-1.824 20.5-1.894l8-.127Z"/>\n <path id="path2" fill="#B6BCC4" fill-rule="evenodd" stroke="none" d="M116 375.743c-2.73.236-5.372-.8-12-4.708-4.675-2.756-11.133-6.704-14.35-8.773-3.218-2.07-6.48-4.977-7.25-6.462-1.094-2.111-1.4-8.348-1.4-28.56 0-21.125.267-26.213 1.461-27.799 1.43-1.9 1.574-1.879 6.75.986 2.909 1.61 8.214 4.584 11.789 6.61 3.575 2.026 9.1 5.209 12.28 7.073 3.179 1.865 10.253 6.09 15.722 9.39 5.468 3.3 13.836 8.245 18.596 10.989 4.76 2.744 10.736 6.348 13.279 8.01 2.568 1.678 4.753 3.927 4.917 5.062.246 1.716-3.42 4.101-23 14.96-14.766 8.189-24.575 13.03-26.794 13.222Z"/>\n <path id="path3" fill="#fff" fill-rule="evenodd" stroke="none" d="M185.256 329c-1.012 0-5.646-2.194-10.298-4.875-4.652-2.68-22.633-13.322-39.958-23.648-17.325-10.325-34.65-20.844-38.5-23.375-6.25-4.109-7.029-4.977-7.268-8.102-.202-2.632.816-5.36 4.106-11 2.407-4.125 21.576-36.075 42.6-71 21.022-34.925 39.47-65.075 40.994-67 2.195-2.772 3.589-3.562 6.699-3.797 3.477-.262 6.271 1.103 24.398 11.925 11.259 6.722 30.146 18.027 41.971 25.123 11.825 7.095 23.075 14.15 25 15.675 2.099 1.664 3.627 3.836 3.816 5.425.19 1.592-1.021 5.145-3.035 8.9-1.844 3.437-21.525 36.528-43.737 73.537-22.211 37.008-41.411 68.395-42.666 69.75-1.256 1.354-3.11 2.462-4.122 2.462Z"/>\n</svg>';
const iconP2PSubtractSvg = '<?xml version="1.0" encoding="UTF-8" standalone="no"?>\n<svg xmlns="http://www.w3.org/2000/svg" id="svg1" width="512" height="512" version="1.1">\n <path id="path2" fill="#000" fill-rule="evenodd" stroke="none" d="M193.812 478.594c-90.053.005-55.415-.21-62.995-.883-3.109-.277-27.11-21.644-46.82-41.478-19.71-19.834-38.405-39.3-41.545-43.255-3.14-3.957-6.62-10.716-7.734-15.02-1.731-6.696-1.64-9.153.633-17.007 2.63-9.085 3.646-10.163 97.355-103.187 67.82-67.324 96.742-95.006 101.9-97.53 4.693-2.298 10.024-3.498 15.308-3.447 5.932.057 10.337 1.334 16.417 4.757 5.373 3.027 23.906 20.087 52.456 48.29 24.281 23.987 46.739 47.413 49.906 52.058 4.533 6.648 5.893 10.36 6.393 17.444.536 7.598-.072 10.288-3.907 17.275-3.191 5.814-24.3 28.163-70.958 75.126-36.529 36.767-66.373 67.623-66.926 68.174-19.83 19.71-39.71 38.183-39.483 38.683zm84.581-137.331c-68.043-67.49-88.703-86.892-89.81-86.657-1.106.234-27.946 26.117-59.643 57.517-31.698 31.4-57.439 57.94-57.202 58.977.236 1.037 15.757 17.272 34.492 36.078 34.062 34.193 34.062 34.193 53.6 34.193 19.536 0 19.536 0 55.274-35.285 19.656-19.407 41.937-41.932 49.514-50.054zm60.94-60.024c-66.9-66.62-87.289-86.356-88.469-86.837-1.29-.527-8.787 5.684-18.813 15.585l-16.668 16.46 44.408 44.028c24.425 24.215 44.787 44.027 45.249 44.027.461 0 8.366-7.484 17.566-16.632z" style="stroke-width:1.5931"/>\n <path id="path3" d="m214.604 225.766 35.345-32.835 89.308 87.873-34.083 33.773z" style="fill:maroon;fill-opacity:1;stroke:#010101;stroke-width:7.48758"/>\n <path id="path7" d="m238.705 480.616 240.584-.033V45.207L32.68 44.87l.24 35.605 411.103-.882-.338 365.995-169.126-.577Z" style="fill:#000;fill-opacity:1;stroke:none;stroke-width:35;stroke-dasharray:none;stroke-opacity:1"/>\n <circle id="path8" cx="460.396" cy="461.547" r="39.134" style="fill:#000;fill-opacity:1;stroke:none;stroke-width:35;stroke-dasharray:none;stroke-opacity:1"/>\n <circle id="path8-8" cx="460.775" cy="65.834" r="39.134" style="fill:#000;fill-opacity:1;stroke:none;stroke-width:35;stroke-dasharray:none;stroke-opacity:1"/>\n <circle id="path8-8-0" cx="67.176" cy="62.815" r="39.134" style="fill:#000;fill-opacity:1;stroke:none;stroke-width:35;stroke-dasharray:none;stroke-opacity:1"/>\n</svg>';
const iconDrawSvg = '<?xml version="1.0" encoding="UTF-8" standalone="no"?>\n<svg xmlns="http://www.w3.org/2000/svg" id="svg1" width="512" height="512" version="1.1">\n <path id="Shape" fill="#000" fill-rule="evenodd" stroke="none" d="M73.574 425.687c-1.14.188-4.099-.592-6.574-1.735-2.475-1.142-5.513-3.027-6.75-4.188C58 417.65 58 417.65 58 348.144c0-69.506 0-69.506 6.152-80.075a1599.57 1599.57 0 0 1 12.84-21.569c3.677-6.05 12.679-21.125 20.004-33.5 7.324-12.375 15.666-26.325 18.538-31 2.87-4.675 13.163-21.88 22.872-38.233a33686.921 33686.921 0 0 1 34.229-57.5c9.117-15.272 18.261-30.022 20.322-32.777 2.298-3.074 5.78-6.117 9.008-7.874 3.583-1.95 7.22-3.017 11.399-3.342 4.972-.388 7.445.018 13.04 2.137 3.799 1.439 10.999 4.919 16 7.733 5.003 2.814 13.371 7.613 18.596 10.663s13.775 8.134 19 11.297c5.225 3.163 12.061 7.246 15.192 9.073 3.13 1.828 9.402 5.521 13.936 8.208 4.535 2.686 10.397 6.897 13.027 9.357 3.265 3.054 5.457 6.261 6.912 10.115 1.672 4.43 2.036 7.161 1.694 12.704-.324 5.244-1.186 8.511-3.349 12.695-1.601 3.098-9.388 16.57-17.303 29.939-7.915 13.368-16.24 27.326-18.5 31.019a3087.099 3087.099 0 0 0-9.697 16c-3.073 5.107-11.2 18.736-18.061 30.286-6.86 11.55-16.45 27.75-21.312 36-4.86 8.25-11.179 18.825-14.04 23.5-2.862 4.675-8.308 13.83-12.101 20.343-3.794 6.513-10.508 17.762-14.92 24.997-7.721 12.664-8.263 13.29-14.5 16.77-3.563 1.987-15.357 8.416-26.21 14.286-10.851 5.87-28.851 15.66-40 21.756-11.147 6.096-25.86 14.032-32.694 17.638-6.834 3.605-13.359 6.709-14.5 6.897ZM116 376.754c2.096-.21 12.525-5.28 26-12.636 12.375-6.757 23.394-13.153 24.486-14.215 1.495-1.453 1.734-2.331.966-3.548-.56-.889-7.304-5.352-14.986-9.918-7.681-4.565-17.116-10.268-20.966-12.672-3.85-2.405-15.046-9.073-24.88-14.818-9.834-5.746-19.087-10.976-20.561-11.623-1.641-.72-3.24-.831-4.12-.287-.981.606-1.598 3.444-1.939 8.924-.275 4.42-.347 17.312-.16 28.65.341 20.616.341 20.616 3.5 23.908 1.738 1.81 9.01 6.733 16.16 10.94 10.754 6.327 13.605 7.587 16.5 7.295ZM184.98 329c.9 0 2.342-.563 3.204-1.25.86-.688 3.803-4.85 6.537-9.25 2.735-4.4 17.476-28.925 32.759-54.5 15.283-25.575 29.64-49.497 31.904-53.16 2.264-3.662 6.141-10.152 8.616-14.422 2.475-4.27 5.957-10.274 7.738-13.341 1.785-3.075 3.244-7.005 3.25-8.759.011-2.982-.694-3.614-11.238-10.077A2882.894 2882.894 0 0 0 249 152.847c-4.125-2.475-13.245-7.94-20.267-12.145a9841.507 9841.507 0 0 1-20.5-12.309 1566.199 1566.199 0 0 0-14.685-8.73c-3.823-2.238-8.13-4.242-9.57-4.453-1.998-.293-3.333.43-5.64 3.057-1.661 1.893-5.368 7.444-8.237 12.337a2040.256 2040.256 0 0 1-11.477 19.251 5775.594 5775.594 0 0 0-12.325 20.5A7048.542 7048.542 0 0 1 131.47 195c-4.823 7.975-10.898 18.1-13.501 22.5-2.604 4.4-8.45 14.075-12.991 21.5-4.542 7.425-10.268 16.988-12.725 21.252-3.26 5.657-4.304 8.405-3.861 10.168.454 1.81 4.239 4.57 15.1 11.01 7.973 4.726 20.686 12.257 28.252 16.735 7.566 4.477 17.582 10.485 22.257 13.351 4.675 2.866 13.19 7.973 18.921 11.348 5.732 3.375 11.158 6.136 12.059 6.136Zm100.681-194.022c2.318.016 3.852-.798 5.75-3.055 1.497-1.78 2.589-4.313 2.589-6.01 0-2.269-.848-3.508-3.75-5.478-2.063-1.4-6.53-4.29-9.927-6.421-3.398-2.132-9.923-6.015-14.5-8.63-4.578-2.614-10.798-6.408-13.823-8.43-3.025-2.022-7.75-4.873-10.5-6.334-2.75-1.462-8.15-4.689-12-7.171-5.023-3.24-7.838-4.434-9.968-4.232-2.1.2-3.65 1.298-5.297 3.75-1.437 2.14-2.215 4.588-2.032 6.391.254 2.498 1.604 3.72 9.297 8.412 4.95 3.02 15.525 9.427 23.5 14.238a3631.896 3631.896 0 0 1 26 15.848c7.807 4.82 12.515 7.107 14.661 7.122Z" style="stroke:#000;stroke-opacity:1;stroke-width:20;stroke-dasharray:none"/>\n <path id="path1" fill="#000" fill-rule="evenodd" stroke="none" d="M479.5 119.5c0 34 0 34-7 34.262-3.85.144-11.275.947-16.5 1.785s-12.533 2.42-16.24 3.514c-3.708 1.095-9.416 3.559-12.686 5.475-3.269 1.916-7.401 4.71-9.183 6.209-1.781 1.499-4.926 5.336-6.988 8.526-2.395 3.706-4.41 8.588-5.576 13.515-1.346 5.684-1.817 11.548-1.79 22.282.023 8.902.749 19.208 1.866 26.5 1.006 6.563 2.964 17.107 4.352 23.432 1.388 6.325 3.152 14.2 3.92 17.5.768 3.3 3.14 14.55 5.272 25s4.816 24.901 5.965 32.114c1.57 9.86 2.088 17.705 2.088 31.622 0 16.395-.284 19.635-2.485 28.386-1.367 5.433-4.299 13.56-6.515 18.061-2.832 5.75-6.265 10.559-11.545 16.172-5.308 5.642-10.516 9.846-17.735 14.316-5.62 3.481-13.145 7.611-16.72 9.179-3.575 1.567-10.739 4.4-15.92 6.293-5.181 1.894-13.506 4.308-18.5 5.365-4.994 1.056-13.58 2.637-19.08 3.513-6.633 1.057-18.754 1.716-36 1.958-14.3.201-30.5.01-36-.424s-15.625-1.537-22.5-2.45c-6.875-.912-16.575-2.328-21.557-3.146-4.98-.817-12.406-2.176-16.5-3.02-4.093-.843-9.693-2.157-12.443-2.92-2.75-.763-7.7-2.083-11-2.932-3.3-.85-10.5-2.648-16-3.996-5.5-1.348-15.85-4.3-23-6.56-7.15-2.261-14.912-4.805-17.25-5.653-2.904-1.055-4.25-2.127-4.25-3.386 0-1.228 3.252-3.97 9.75-8.22 5.363-3.507 13.202-8.277 17.421-10.6 7.671-4.225 7.671-4.225 18.5-1.287 5.956 1.616 19.154 4.998 29.329 7.515 10.175 2.517 23.45 5.465 29.5 6.55 6.05 1.087 15.05 2.427 20 2.98 4.95.552 14.85 1.53 22 2.175 8.107.73 21.657.955 36 .598 13.264-.33 27.021-1.234 32.5-2.135 5.225-.86 14-2.963 19.5-4.673s12.925-4.443 16.5-6.073a870.93 870.93 0 0 0 11.5-5.354c2.75-1.314 7.94-4.836 11.535-7.826 3.888-3.234 7.591-7.403 9.142-10.29 1.433-2.668 3.653-8.143 4.932-12.166 1.934-6.082 2.319-9.415 2.283-19.794-.024-7.149-.774-16.627-1.756-22.186-.943-5.339-2.407-13.531-3.253-18.206-.847-4.675-2.454-12.775-3.572-18-1.118-5.225-3.15-14-4.514-19.5-1.365-5.5-3.5-14.725-4.746-20.5-1.246-5.775-2.995-15-3.886-20.5-.892-5.5-2.081-14.08-2.643-19.068-.562-4.987-1.014-13.987-1.005-20 .01-6.012.652-14.799 1.427-19.525.775-4.726 2.12-11.026 2.988-14 .869-2.974 3.04-8.224 4.824-11.668 1.784-3.443 5.256-9.004 7.715-12.356 2.46-3.353 8.084-8.988 12.5-12.523 4.416-3.534 11.629-8.164 16.029-10.287 4.4-2.124 12.05-5.194 17-6.823 4.95-1.63 14.625-3.757 21.5-4.729s16.1-1.824 20.5-1.894l8-.127Z"/>\n <path id="path2" fill="#B6BCC4" fill-rule="evenodd" stroke="none" d="M116 375.743c-2.73.236-5.372-.8-12-4.708-4.675-2.756-11.133-6.704-14.35-8.773-3.218-2.07-6.48-4.977-7.25-6.462-1.094-2.111-1.4-8.348-1.4-28.56 0-21.125.267-26.213 1.461-27.799 1.43-1.9 1.574-1.879 6.75.986 2.909 1.61 8.214 4.584 11.789 6.61 3.575 2.026 9.1 5.209 12.28 7.073 3.179 1.865 10.253 6.09 15.722 9.39 5.468 3.3 13.836 8.245 18.596 10.989 4.76 2.744 10.736 6.348 13.279 8.01 2.568 1.678 4.753 3.927 4.917 5.062.246 1.716-3.42 4.101-23 14.96-14.766 8.189-24.575 13.03-26.794 13.222Z"/>\n</svg>';
const iconSubtractSvg = '<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><g id="SVGRepo_bgCarrier" stroke-width="0"></g><g id="SVGRepo_tracerCarrier" stroke-linecap="round" stroke-linejoin="round"></g><g id="SVGRepo_iconCarrier"> <path fill-rule="evenodd" clip-rule="evenodd" d="M15.0722 3.9967L20.7508 9.83395L17.0544 13.5304L13.0758 17.5H21.0041V19H7.93503L4.00195 15.0669L15.0722 3.9967ZM10.952 17.5L15.4628 12.9994L11.8268 9.3634L6.12327 15.0669L8.55635 17.5H10.952Z" fill="#1F2328"></path> </g></svg>\n';
const iconCloneSvg = '<?xml version="1.0" encoding="UTF-8"?>\n<svg xmlns="http://www.w3.org/2000/svg" width="512" height="512" viewBox="0 0 512 512">\n <rect x="88" y="88" width="252" height="252" fill="none" stroke="#000" stroke-width="42"/>\n <rect x="172" y="172" width="252" height="252" fill="none" stroke="#000" stroke-width="42"/>\n</svg>';
const iconP2PDrawSvg = '<?xml version="1.0" encoding="UTF-8" standalone="no"?>\n<svg xmlns="http://www.w3.org/2000/svg" id="svg1" width="512" height="512" version="1.1">\n <path id="path7" d="m155.327 480.616 323.962-.033V45.207L32.68 44.87l.24 35.605 411.103-.882-.338 365.995-169.126-.577Z" style="fill:#000;fill-opacity:1;stroke:none;stroke-width:35;stroke-dasharray:none;stroke-opacity:1"/>\n <circle id="path8" cx="460.396" cy="461.547" r="39.134" style="fill:#000;fill-opacity:1;stroke:none;stroke-width:35;stroke-dasharray:none;stroke-opacity:1"/>\n <circle id="path8-8" cx="460.775" cy="65.834" r="39.134" style="fill:#000;fill-opacity:1;stroke:none;stroke-width:35;stroke-dasharray:none;stroke-opacity:1"/>\n <circle id="path8-8-0" cx="67.176" cy="62.815" r="39.134" style="fill:#000;fill-opacity:1;stroke:none;stroke-width:35;stroke-dasharray:none;stroke-opacity:1"/>\n <path id="path2" fill="#000" fill-rule="evenodd" stroke="none" d="M78.983 469.63c-2.155.594-4.661-.186-6.416-1.995-1.755-1.81-2.512-4.396-1.937-6.616.52-2.005 2.036-7.54 3.369-12.298 1.333-4.76 4.302-15.274 6.598-23.365 2.295-8.09 6.159-21.33 8.585-29.421a5326.7 5326.7 0 0 0 9.458-32.018c2.775-9.519 6.848-23.538 9.05-31.153a3424.585 3424.585 0 0 1 8.424-28.556c2.432-8.09 5.74-19.445 7.35-25.23 1.61-5.786 4.963-13.271 7.45-16.634 3.196-4.319 8.828-8.134 19.19-12.998 8.067-3.787 22.221-10.287 31.454-14.445 9.232-4.158 20.186-9.16 24.34-11.116a766.22 766.22 0 0 1 15.108-6.874c4.154-1.824 8.5-4.181 9.657-5.238 1.675-1.53 1.537-3.405-.681-9.226-2.27-5.96-2.436-8.498-.898-13.779 1.221-4.19 5.936-10.614 13.373-18.217 6.772-6.923 13.882-12.502 17.322-13.59 4.562-1.444 7.103-1.28 11.648.752 3.426 1.532 23.389 20.759 48.618 46.826 23.543 24.325 45.072 47.061 47.841 50.525 3.873 4.843 5.036 8.022 5.036 13.76 0 6.664-.988 8.548-9.232 17.594-5.078 5.572-11.81 12.267-14.959 14.876-4.16 3.448-7.614 4.745-12.635 4.745-3.801 0-8.653-1.259-10.783-2.797-2.18-1.574-4.506-2.141-5.322-1.298-.797.824-5.186 10.455-9.754 21.402a5146.966 5146.966 0 0 1-15.335 36.316c-3.866 9.027-10.026 22.886-13.689 30.798-4.617 9.975-8.217 15.361-11.743 17.572-2.796 1.753-10.879 5.156-17.961 7.562-7.083 2.406-20.431 6.749-29.664 9.65-9.232 2.902-25.661 8.033-36.509 11.403-10.847 3.37-31.242 9.76-45.323 14.201-14.08 4.44-32.777 10.286-41.547 12.99-8.771 2.704-17.539 5.355-19.483 5.892zm33.106-25.66c3.124-.525 14.558-3.756 25.407-7.18 10.85-3.424 33.511-10.53 50.36-15.791a27689.101 27689.101 0 0 1 50.776-15.803c14.114-4.37 21.467-7.596 24.568-10.778 2.433-2.497 8.003-13.108 12.378-23.578 4.374-10.471 10.397-24.88 13.384-32.018 2.987-7.14 8.327-19.6 11.866-27.691 3.54-8.091 6.71-17.047 7.046-19.903.555-4.717-2.161-8.042-29.692-36.345-16.667-17.133-31.78-31.396-33.584-31.694-1.804-.298-24.636 9.269-50.737 21.259-26.102 11.99-49.09 23.383-51.087 25.316-1.997 1.934-4.862 7.745-6.369 12.914a7969.157 7969.157 0 0 0-7.086 24.55 5161.998 5161.998 0 0 1-9.204 31.594c-2.67 9.043-9.076 30.85-14.234 48.46-5.158 17.609-10.028 34.72-10.822 38.026-.794 3.304-1.129 6.334-.743 6.731.385.397 3.176-1.24 6.202-3.637 3.026-2.397 17.965-17.532 33.199-33.633 15.233-16.1 31.64-33.573 36.46-38.83 8.522-9.292 8.717-9.73 7.063-15.905-1.163-4.344-1.143-8.487.066-13.114 1.094-4.191 4.275-9.1 8.364-12.909 3.648-3.398 9.265-6.662 12.561-7.3 4.51-.872 7.9-.158 13.92 2.932 4.845 2.486 9.223 6.36 11.188 9.9 1.776 3.2 3.3 9.202 3.385 13.339.09 4.285-1.167 10.058-2.921 13.416-1.693 3.242-6.11 7.651-9.814 9.798-5.669 3.285-8.24 3.73-16.226 2.804-9.49-1.099-9.49-1.099-49.778 40.478-22.158 22.867-40.577 42.47-40.932 43.56-.438 1.349 1.175 1.679 5.036 1.031zm90.33-98.225c3.355 0 6.06-1.502 8.524-4.733 2.602-3.409 3.338-6.138 2.634-9.765-.537-2.768-2.295-6.065-3.908-7.327-1.612-1.262-5.138-2.32-7.835-2.35-2.697-.03-6.285 1.234-7.974 2.81-1.808 1.687-3.07 5.242-3.07 8.653 0 3.481 1.339 7.169 3.358 9.25 1.846 1.904 5.568 3.462 8.271 3.462zm133.61-53.652c1.976 0 7.955-4.72 14.175-11.19 10.066-10.471 10.62-11.455 8.624-15.3-1.174-2.262-22.067-24.651-46.43-49.755-28.883-29.764-45.482-45.645-47.707-45.645-1.918 0-7.85 4.523-13.542 10.323-7.359 7.498-9.997 11.39-9.645 14.229.303 2.438 17.518 21.468 45.796 50.622 29.62 30.538 46.495 46.716 48.73 46.716z" style="stroke:#010101;stroke-width:17.0446;stroke-dasharray:none;stroke-opacity:1"/>\n <path id="path3" d="m243.547 189.376 16.458-15.442 96.745 96.717-16.459 16.256z" style="fill:green;fill-opacity:1;stroke:none;stroke-width:18.6862;stroke-dasharray:none;stroke-opacity:1"/>\n <circle id="path9" cx="201.178" cy="332.215" r="13.593" style="fill:#fff;fill-opacity:1;stroke:none;stroke-width:35;stroke-dasharray:none;stroke-opacity:1"/>\n</svg>';
const eraseSvg = '<?xml version="1.0" encoding="utf-8"?>\n<svg xmlns="http://www.w3.org/2000/svg" xml:space="preserve" id="Layer_1" x="0" y="0" style="enable-background:new 0 0 48 48" version="1.1" viewBox="0 0 48 48">\n <style>\n .st0{fill:#000}\n </style>\n <path d="M0 0h48v48H0z" style="fill:#333;fill-opacity:0"/>\n <path d="M26.6 10V7.8h-5.2V10h-8.8v2.8h22.8V10zM35.4 15.4H12.6v4.3h1.8V37c0 1.1.9 2 2 2h15.2c1.1 0 2-.9 2-2V19.7h1.8v-4.3zM19.7 34.2c0 .5-.4 1-1 1-.5 0-1-.4-1-1V22.6c0-.5.4-1 1-1 .5 0 1 .4 1 1v11.6zm5.6-.4c0 .7-.6 1.3-1.3 1.3-.7 0-1.3-.6-1.3-1.3V23c0-.7.6-1.3 1.3-1.3.7 0 1.3.6 1.3 1.3v10.8zm5 .4c0 .5-.4 1-1 1-.5 0-1-.4-1-1V22.6c0-.5.4-1 1-1 .5 0 1 .4 1 1v11.6z" class="st0"/>\n</svg>';
const iconCollapseSvg = '<?xml version="1.0" encoding="UTF-8" standalone="no"?>\n<svg xmlns="http://www.w3.org/2000/svg" width="512" height="512">\n <path d="m80.033 292.15 175.755-174.592 175.755 174.591" style="fill:none;stroke:#000;stroke-width:35;stroke-dasharray:none;stroke-opacity:1"/>\n <path d="M80.105 394.539 255.86 219.947 431.615 394.54" style="fill:none;stroke:#000;stroke-width:35;stroke-dasharray:none;stroke-opacity:1"/>\n</svg>';
const iconUndoSvg = '<?xml version="1.0" encoding="utf-8"?>\n<svg version="1.1" xmlns="http://www.w3.org/2000/svg" x="0px" y="0px" viewBox="0 0 48 48" xml:space="preserve">\n<style type="text/css">\n .st0{fill:none;stroke:#000000;stroke-width:3;stroke-linecap:round;stroke-linejoin:round;}\n</style>\n<g>\n <path class="st0" d="M14,18h10c6.6,0,12,5.4,12,12s-5.4,12-12,12s-12-5.4-12-12"/>\n <polyline class="st0" points="18,14 14,18 18,22"/>\n</g>\n</svg>\n';
const iconRedoSvg = '<?xml version="1.0" encoding="utf-8"?>\n<svg version="1.1" xmlns="http://www.w3.org/2000/svg" x="0px" y="0px" viewBox="0 0 48 48" xml:space="preserve">\n<style type="text/css">\n .st0{fill:none;stroke:#000000;stroke-width:3;stroke-linecap:round;stroke-linejoin:round;}\n</style>\n<g>\n <path class="st0" d="M34,18H24C17.4,18,12,23.4,12,30s5.4,12,12,12s12-5.4,12-12"/>\n <polyline class="st0" points="30,14 34,18 30,22"/>\n</g>\n</svg>\n';
const icons$1 = {
activate: iconPolydraw2Svg.trim(),
draw: iconDrawSvg.trim(),
subtract: iconSubtractSvg.trim(),
clone: iconCloneSvg.trim(),
p2p: iconP2PDrawSvg.trim(),
p2pSubtract: iconP2PSubtractSvg.trim(),
erase: eraseSvg.trim(),
collapse: iconCollapseSvg.trim(),
undo: iconUndoSvg.trim(),
redo: iconRedoSvg.trim()
};
const setButtonIcon = (button, svgMarkup) => {
applySvgIcon(button, svgMarkup);
};
const setButtonTooltip = (button, text) => {
button.setAttribute("data-tooltip", text);
button.setAttribute("aria-label", text);
};
function createButtons(container, subContainer, config, onActivateToggle, onDrawClick, onSubtractClick, onCloneClick, onEraseClick, onPointToPointClick, onPointToPointSubtractClick, onUndoClick, onRedoClick, eventManager, historyManager) {
const activate = leafletAdapter.domUtil.create(
"a",
"icon-activate",
container
);
activate.href = "#";
setButtonTooltip(activate, "Activate");
activate.setAttribute("data-polydraw", "activate-button");
setButtonIcon(activate, icons$1.activate);
activate.dataset.activeIcon = icons$1.activate;
activate.dataset.collapsedIcon = icons$1.collapse;
L.DomEvent.on(activate, "mousedown", L.DomEvent.stopPropagation);
L.DomEvent.on(activate, "touchstart", L.DomEvent.stopPropagation);
L.DomEvent.on(activate, "click", L.DomEvent.stop).on(activate, "click", onActivateToggle);
if (config.tools.draw) {
const draw = leafletAdapter.domUtil.create("a", "icon-draw", subContainer);
draw.href = "#";
setButtonTooltip(draw, "Draw");
draw.setAttribute("data-polydraw", "draw-button");
setButtonIcon(draw, icons$1.draw);
L.DomEvent.on(draw, "mousedown", L.DomEvent.stopPropagation);
L.DomEvent.on(draw, "touchstart", L.DomEvent.stopPropagation);
L.DomEvent.on(draw, "click", L.DomEvent.stopPropagation);
L.DomEvent.on(draw, "click", L.DomEvent.stop);
L.DomEvent.on(draw, "click", onDrawClick);
}
if (config.tools.subtract) {
const subtract = leafletAdapter.domUtil.create(
"a",
"icon-subtract",
subContainer
);
subtract.href = "#";
setButtonTooltip(subtract, "Subtract");
subtract.setAttribute("data-polydraw", "subtract-button");
setButtonIcon(subtract, icons$1.subtract);
L.DomEvent.on(subtract, "mousedown", L.DomEvent.stopPropagation);
L.DomEvent.on(subtract, "touchstart", L.DomEvent.stopPropagation);
L.DomEvent.on(subtract, "click", L.DomEvent.stopPropagation);
L.DomEvent.on(subtract, "click", L.DomEvent.stop);
L.DomEvent.on(subtract, "click", onSubtractClick);
}
if (config.tools.p2p) {
const p2p = leafletAdapter.domUtil.create("a", "icon-p2p", subContainer);
p2p.href = "#";
setButtonTooltip(p2p, "Point to Point");
p2p.setAttribute("data-polydraw", "p2p-button");
setButtonIcon(p2p, icons$1.p2p);
L.DomEvent.on(p2p, "mousedown", L.DomEvent.stopPropagation);
L.DomEvent.on(p2p, "touchstart", L.DomEvent.stopPropagation);
L.DomEvent.on(p2p, "click", L.DomEvent.stopPropagation);
L.DomEvent.on(p2p, "click", L.DomEvent.stop);
L.DomEvent.on(p2p, "click", onPointToPointClick);
}
if (config.tools.p2pSubtract) {
const p2pSubtract = leafletAdapter.domUtil.create(
"a",
"icon-p2p-subtract",
subContainer
);
p2pSubtract.href = "#";
setButtonTooltip(p2pSubtract, "Point to Point Subtract");
p2pSubtract.setAttribute("data-polydraw", "p2p-subtract-button");
setButtonIcon(p2pSubtract, icons$1.p2pSubtract);
L.DomEvent.on(p2pSubtract, "mousedown", L.DomEvent.stopPropagation);
L.DomEvent.on(p2pSubtract, "touchstart", L.DomEvent.stopPropagation);
L.DomEvent.on(p2pSubtract, "click", L.DomEvent.stopPropagation);
L.DomEvent.on(p2pSubtract, "click", L.DomEvent.stop);
L.DomEvent.on(p2pSubtract, "click", onPointToPointSubtractClick);
}
if (config.tools.clone) {
const clone2 = leafletAdapter.domUtil.create(
"a",
"icon-clone",
subContainer
);
clone2.href = "#";
setButtonTooltip(clone2, "Clone");
clone2.setAttribute("data-polydraw", "clone-button");
setButtonIcon(clone2, icons$1.clone);
L.DomEvent.on(clone2, "mousedown", L.DomEvent.stopPropagation);
L.DomEvent.on(clone2, "touchstart", L.DomEvent.stopPropagation);
L.DomEvent.on(clone2, "click", L.DomEvent.stopPropagation);
L.DomEvent.on(clone2, "click", L.DomEvent.stop);
L.DomEvent.on(clone2, "click", onCloneClick);
}
if (config.tools.erase) {
const erase = leafletAdapter.domUtil.create(
"a",
"icon-erase",
subContainer
);
erase.href = "#";
setButtonTooltip(erase, "Erase All");
erase.setAttribute("data-polydraw", "erase-button");
setButtonIcon(erase, icons$1.erase);
L.DomEvent.on(erase, "mousedown", L.DomEvent.stopPropagation);
L.DomEvent.on(erase, "touchstart", L.DomEvent.stopPropagation);
L.DomEvent.on(erase, "click", L.DomEvent.stopPropagation);
L.DomEvent.on(erase, "click", L.DomEvent.stop);
L.DomEvent.on(erase, "click", onEraseClick);
}
const undo = leafletAdapter.domUtil.create("a", "icon-undo", subContainer);
undo.href = "#";
setButtonTooltip(undo, "Undo (Ctrl+Z / Cmd+Z)");
setButtonIcon(undo, icons$1.undo);
L.DomEvent.on(undo, "mousedown", L.DomEvent.stopPropagation);
L.DomEvent.on(undo, "touchstart", L.DomEvent.stopPropagation);
L.DomEvent.on(undo, "click", L.DomEvent.stopPropagation);
L.DomEvent.on(undo, "click", L.DomEvent.stop);
L.DomEvent.on(undo, "click", onUndoClick);
const redo = leafletAdapter.domUtil.create("a", "icon-redo", subContainer);
redo.href = "#";
setButtonTooltip(redo, "Redo (Ctrl+Y / Cmd+Shift+Z)");
setButtonIcon(redo, icons$1.redo);
L.DomEvent.on(redo, "mousedown", L.DomEvent.stopPropagation);
L.DomEvent.on(redo, "touchstart", L.DomEvent.stopPropagation);
L.DomEvent.on(redo, "click", L.DomEvent.stopPropagation);
L.DomEvent.on(redo, "click", L.DomEvent.stop);
L.DomEvent.on(redo, "click", onRedoClick);
const updateHistoryButtonStates = (canUndo, canRedo) => {
if (canUndo) {
undo.style.opacity = "1";
undo.style.pointerEvents = "auto";
} else {
undo.style.opacity = "0.3";
undo.style.pointerEvents = "none";
}
if (canRedo) {
redo.style.opacity = "1";
redo.style.pointerEvents = "auto";
} else {
redo.style.opacity = "0.3";
redo.style.pointerEvents = "none";
}
};
eventManager.on("polydraw:history:changed", (data) => {
updateHistoryButtonStates(data.canUndo, data.canRedo);
});
eventManager.on("polydraw:history:undo", (data) => {
updateHistoryButtonStates(data.canUndo, data.canRedo);
});
eventManager.on("polydraw:history:redo", (data) => {
updateHistoryButtonStates(data.canUndo, data.canRedo);
});
updateHistoryButtonStates(historyManager.canUndo(), historyManager.canRedo());
}
class PolygonUtil {
/**
* Calculates the center of the polygon.
* @param polygon Array of LatLng points.
* @returns The center LatLng.
*/
static getCenter(polygon2) {
let x = 0;
let y = 0;
let z = 0;
polygon2.forEach((v) => {
let lat1 = v.lat;
let lon1 = v.lng;
lat1 = lat1 * MATH.DEG_TO_RAD;
lon1 = lon1 * MATH.DEG_TO_RAD;
x += Math.cos(lat1) * Math.cos(lon1);
y += Math.cos(lat1) * Math.sin(lon1);
z += Math.sin(lat1);
});
let lng = Math.atan2(y, x);
const hyp = Math.sqrt(x * x + y * y);
let lat = Math.atan2(z, hyp);
lat = lat * MATH.RAD_TO_DEG;
lng = lng * MATH.RAD_TO_DEG;
const center = { lat, lng };
return center;
}
/**
* Gets the southwest point of the polygon bounds.
* @param polygon Array of LatLng points.
* @returns The southwest LatLng.
*/
static getSouthWest(polygon2) {
const bounds = this.getBounds(polygon2);
return bounds.getSouthWest();
}
static getNorthEast(polygon2) {
const bounds = this.getBounds(polygon2);
return bounds.getNorthEast();
}
static getNorthWest(polygon2) {
const bounds = this.getBounds(polygon2);
return bounds.getNorthWest();
}
static getSouthEast(polygon2) {
const bounds = this.getBounds(polygon2);
return bounds.getSouthEast();
}
static getNorth(polygon2) {
const bounds = this.getBounds(polygon2);
return bounds.getNorth();
}
static getSouth(polygon2) {
const bounds = this.getBounds(polygon2);
return bounds.getSouth();
}
static getWest(polygon2) {
const bounds = this.getBounds(polygon2);
return bounds.getWest();
}
static getEast(polygon2) {
const bounds = this.getBounds(polygon2);
return bounds.getEast();
}
static getSqmArea(polygon2) {
const poly = new L.Polygon(polygon2);
const geoJsonPoly = poly.toGeoJSON();
const area2 = this.turfHelper.getPolygonArea(geoJsonPoly);
return area2;
}
static getPerimeter(polygon2) {
const poly = new L.Polygon(polygon2);
const geoJsonPoly = poly.toGeoJSON();
const perimeter = this.turfHelper.getPolygonPerimeter(geoJsonPoly);
return perimeter * 1e3;
}
static getPolygonChecksum(polygon2) {
const uniqueLatLngs = polygon2.filter((v, i, a) => {
const found = a.find((x) => x.lat === v.lat && x.lng === v.lng);
return found ? a.indexOf(found) === i : false;
});
return uniqueLatLngs.reduce((a, b) => +a + +b.lat, 0) * uniqueLatLngs.reduce((a, b) => +a + +b.lng, 0);
}
static getMidPoint(point1, point2) {
const midpoint2 = this.turfHelper.getMidpoint(point1, point2);
const returnPoint = {
lat: midpoint2.lat,
lng: midpoint2.lng
};
return returnPoint;
}
static getBounds(polygon2) {
const tmpLatLng = [];
polygon2.forEach((ll) => {
if (isNaN(ll.lat) || isNaN(ll.lng)) ;
tmpLatLng.push(ll);
});
const polyLine = new L.Polyline(tmpLatLng);
const bounds = polyLine.getBounds();
return bounds;
}
/**
* Calculates the center of mass of the polygon.
* @param polygon A GeoJSON polygon.
* @returns The center LatLng.
*/
static getCenterOfMass(polygon2) {
const centerOfMass2 = this.turfHelper.getCenterOfMass(polygon2);
return {
lat: centerOfMass2.geometry.coordinates[1],
lng: centerOfMass2.geometry.coordinates[0]
};
}
static getCenterOfPolygonByIndexWithOffsetFromCenterOfMass(polygon2, index) {
const centerOfMass2 = this.turfHelper.getCenterOfMass(polygon2);
const centerLatLng = {
lat: centerOfMass2.geometry.coordinates[1],
lng: centerOfMass2.geometry.coordinates[0]
};
const centerOfPolygonMarker = this.getPolygonLatLngAtIndex(polygon2, index) ?? centerLatLng;
const offset = {
lat: centerOfPolygonMarker.lat - centerLatLng.lat,
lng: centerOfPolygonMarker.lng - centerLatLng.lng
};
const offsetFraction = 0.5;
const adjustedLat = centerLatLng.lat + offset.lat * offsetFraction;
const adjustedLng = centerLatLng.lng + offset.lng * offsetFraction;
const newCenterOfMass = {
geometry: {
coordinates: [adjustedLng, adjustedLat]
}
};
return {
lat: newCenterOfMass.geometry.coordinates[1],
lng: newCenterOfMass.geometry.coordinates[0]
};
}
static getPolygonLatLngAtIndex(polygon2, index) {
const geometry = polygon2.geometry;
if (!geometry) {
return null;
}
let coordinates;
if (geometry.type === "Polygon") {
coordinates = geometry.coordinates[0];
} else if (geometry.type === "MultiPolygon") {
coordinates = geometry.coordinates[0][0];
} else {
return null;
}
if (!coordinates || coordinates.length === 0) {
return null;
}
if (coordinates.length > 1) {
const first = coordinates[0];
const last = coordinates[coordinates.length - 1];
if (first[0] === last[0] && first[1] === last[1]) {
coordinates = coordinates.slice(0, coordinates.length - 1);
}
}
if (coordinates.length === 0) {
return null;
}
const normalizedIndex = (index % coordinates.length + coordinates.length) % coordinates.length;
const selected = coordinates[normalizedIndex];
return { lat: selected[1], lng: selected[0] };
}
}
__publicField(PolygonUtil, "turfHelper", new TurfHelper(defaultConfig));
class PolygonInfo {
constructor(polygon2) {
__publicField(this, "polygon", []);
__publicField(this, "trashcanPoint", []);
__publicField(this, "sqmArea", []);
__publicField(this, "perimeter", []);
if (!Array.isArray(polygon2)) {
return;
}
if (polygon2.length > 0 && Array.isArray(polygon2[0]) && polygon2[0].length > 0 && typeof polygon2[0][0] === "object" && "lat" in polygon2[0][0]) {
const coordinateArray = polygon2[0];
this.trashcanPoint[0] = this.getTrashcanPoint(coordinateArray);
this.sqmArea[0] = this.calculatePolygonArea(coordinateArray);
this.perimeter[0] = this.calculatePolygonPerimeter(coordinateArray);
this.polygon[0] = [coordinateArray];
return;
}
if (polygon2.length > 0 && polygon2[0] && typeof polygon2[0] === "object" && "lat" in polygon2[0]) {
const currentPolygon = polygon2;
const wrappedPolygon = [[currentPolygon]];
this.trashcanPoint[0] = this.getTrashcanPoint(currentPolygon);
this.sqmArea[0] = this.calculatePolygonArea(currentPolygon);
this.perimeter[0] = this.calculatePolygonPerimeter(currentPolygon);
this.polygon[0] = wrappedPolygon[0];
return;
}
const multiPolygon2 = polygon2;
multiPolygon2.forEach((polygons, i) => {
if (!polygons || !Array.isArray(polygons)) {
return;
}
if (!polygons[0] || !Array.isArray(polygons[0])) {
return;
}
this.trashcanPoint[i] = this.getTrashcanPoint(polygons[0]);
this.sqmArea[i] = this.calculatePolygonArea(polygons[0]);
this.perimeter[i] = this.calculatePolygonPerimeter(polygons[0]);
this.polygon[i] = polygons;
});
}
setSqmArea(area2) {
this.sqmArea[0] = area2;
}
getTrashcanPoint(polygon2) {
if (!Array.isArray(polygon2) || polygon2.length === 0) {
console.warn("getTrashcanPoint: Invalid polygon array:", polygon2);
return { lat: 0, lng: 0 };
}
const validCoords = polygon2.filter(
(coord) => coord && typeof coord === "object" && typeof coord.lat === "number" && typeof coord.lng === "number" && !isNaN(coord.lat) && !isNaN(coord.lng)
);
if (validCoords.length === 0) {
console.warn("getTrashcanPoint: No valid coordinates found:", polygon2);
return { lat: 0, lng: 0 };
}
const res = Math.max(...validCoords.map((o) => o.lat));
const idx = validCoords.findIndex((o) => o.lat === res);
if (idx === -1) {
console.warn("getTrashcanPoint: Could not find max lat coordinate");
return { lat: 0, lng: 0 };
}
let previousPoint;
let nextPoint;
if (idx > 0) {
previousPoint = validCoords[idx - 1];
if (idx < validCoords.length - 1) {
nextPoint = validCoords[idx + 1];
} else {
nextPoint = validCoords[0];
}
} else {
previousPoint = validCoords[validCoords.length - 1];
nextPoint = validCoords[idx + 1];
}
if (!previousPoint || !nextPoint) {
console.warn("getTrashcanPoint: Could not determine previous/next points");
return validCoords[idx] || { lat: 0, lng: 0 };
}
const secondPoint = previousPoint.lng < nextPoint.lng ? previousPoint : nextPoint;
const midpoint2 = PolygonUtil.getMidPoint(validCoords[idx], secondPoint);
return midpoint2;
}
calculatePolygonArea(polygon2) {
const area2 = PolygonUtil.getSqmArea(polygon2);
return area2;
}
calculatePolygonPerimeter(polygon2) {
const perimeter = PolygonUtil.getPerimeter(polygon2);
return perimeter;
}
}
class PolygonInformationService {
constructor(mapStateService) {
__publicField(this, "polygonInfoListeners", []);
__publicField(this, "polygonDrawStateListeners", []);
__publicField(this, "polygonInformationStorage", []);
this.mapStateService = mapStateService;
}
onPolygonInfoUpdated(callback) {
this.polygonInfoListeners.push(callback);
}
emitPolygonInfoUpdated() {
for (const cb of this.polygonInfoListeners) {
cb(this.polygonInformationStorage);
}
}
onPolygonDrawStateUpdated(callback) {
this.polygonDrawStateListeners.push(callback);
}
// === Functions ===
/**
* Updates the polygons and notifies the map state service.
*/
updatePolygons() {
const newPolygons = [];
if (this.polygonInformationStorage.length > 0) {
this.polygonInformationStorage.forEach((v) => {
const test = [];
v.polygon.forEach((poly) => {
poly.forEach((polygon2) => {
const closedPolygon = [...polygon2];
if (polygon2.length > 0 && polygon2[0].toString() !== polygon2[polygon2.length - 1].toString()) {
closedPolygon.push(polygon2[0]);
}
test.push(closedPolygon);
});
});
newPolygons.push(test);
});
}
this.mapStateService.updatePolygons(newPolygons);
this.saveCurrentState();
}
saveCurrentState() {
this.emitPolygonInfoUpdated();
}
deleteTrashcan(polygon2) {
const idx = this.polygonInformationStorage.findIndex((v) => v.polygon[0] === polygon2);
if (idx !== -1) {
this.polygonInformationStorage.splice(idx, 1);
this.updatePolygons();
}
}
deleteTrashCanOnMulti(polygon2) {
let index = 0;
this.polygonInformationStorage.forEach((v, i) => {
const id = v.polygon.findIndex((poly) => poly.toString() === polygon2.toString());
if (id >= 0) {
index = i;
v.trashcanPoint.splice(id, 1);
v.sqmArea.splice(id, 1);
v.perimeter.splice(id, 1);
v.polygon.splice(id, 1);
}
});
this.updatePolygons();
if (this.polygonInformationStorage.length > 1) {
this.polygonInformationStorage.splice(index, 1);
}
}
deletePolygonInformationStorage() {
this.polygonInformationStorage = [];
}
createPolygonInformationStorage(arrayOfFeatureGroups) {
if (arrayOfFeatureGroups.length > 0) {
arrayOfFeatureGroups.forEach((featureGroup) => {
var _a2;
const layers = (_a2 = featureGroup.getLayers) == null ? void 0 : _a2.call(featureGroup);
if (layers == null ? void 0 : layers[0]) {
const latLngs = layers[0].getLatLngs();
const polyInfo = new PolygonInfo(latLngs);
this.polygonInformationStorage.push(polyInfo);
}
});
this.updatePolygons();
}
}
}
class MapStateService {
constructor() {
__publicField(this, "map", null);
__publicField(this, "mapListeners", []);
}
// === Observer-API ===
onMapUpdated(callback) {
if (this.map) callback(this.map);
this.mapListeners.push(callback);
}
emitMapUpdated() {
if (this.map) {
for (const cb of this.mapListeners) {
cb(this.map);
}
}
}
// === Public API ===
/**
* Updates the current map state and notifies listeners.
* @param map The Leaflet map instance.
*/
updateMapState(map) {
this.map = map;
this.emitMapUpdated();
}
/**
* Updates the polygons in the map state.
* @param polygons Array of polygons.
*/
updatePolygons(polygons) {
}
}
class EventManager {
constructor() {
__publicField(this, "eventListeners", /* @__PURE__ */ new Map());
}
/**
* Register an event listener.
* @param event - The event to listen for.
* @param callback - The callback to execute when the event is emitted.
*/
on(event, callback) {
if (!this.eventListeners.has(event)) {
this.eventListeners.set(event, []);
}
this.eventListeners.get(event).push(callback);
}
/**
* Unregister an event listener.
* @param event - The event to stop listening for.
* @param callback - The specific callback to remove.
*/
off(event, callback) {
const listeners = this.eventListeners.get(event);
if (listeners) {
const index = listeners.indexOf(callback);
if (index > -1) {
listeners.splice(index, 1);
}
}
}
/**
* Emit an event.
* @param event - The event to emit.
* @param data - The data to pass to the event listeners.
*/
emit(event, data) {
var _a2;
if (this.eventListeners.has(event)) {
(_a2 = this.eventListeners.get(event)) == null ? void 0 : _a2.forEach((callback) => {
callback(data);
});
}
}
}
class ModeManager {
constructor(config, eventManager) {
__publicField(this, "state");
__publicField(this, "config");
__publicField(this, "eventManager");
this.config = config;
this.eventManager = eventManager;
this.state = this.createInitialState();
}
/**
* Create the initial interaction state
*/
createInitialState() {
return {
// Core interactions - initially enabled based on config
polygonDragging: this.config.modes.dragPolygons ?? false,
markerDragging: this.config.modes.dragElbow ?? false,
edgeClicking: true,
// Generally always available when not drawing
polygonClicking: true,
// Drawing capabilities
canStartDrawing: true,
canCompletePolygon: false,
// UI interactions - initially enabled
mapDragging: true,
mapZooming: true,
mapDoubleClickZoom: true,
// Mode-specific states
currentMode: DrawMode.Off,
isDrawingActive: false,
isModifierKeyHeld: false,
// Visual feedback
showCrosshairCursor: false,
showDragCursor: false
};
}
/**
* Update the interaction state when the draw mode changes
*/
updateStateForMode(mode) {
this.state.currentMode = mode;
switch (mode) {
case DrawMode.Off:
this.setOffModeState();
break;
case DrawMode.Add:
this.setDrawingModeState();
break;
case DrawMode.Subtract:
this.setDrawingModeState();
break;
case DrawMode.PointToPoint:
case DrawMode.PointToPointSubtract:
this.setPointToPointModeState();
break;
case DrawMode.Clone:
this.setCloneModeState();
break;
}
this.state.isDrawingActive = mode !== DrawMode.Off && mode !== DrawMode.Clone;
this.eventManager.emit("polydraw:mode:change", { mode });
}
/**
* Set interaction state for Off mode (normal editing)
*/
setOffModeState() {
this.state.polygonDragging = this.config.modes.dragPolygons ?? false;
this.state.markerDragging = this.config.modes.dragElbow ?? false;
this.state.edgeClicking = true;
this.state.polygonClicking = true;
this.state.canStartDrawing = true;
this.state.canCompletePolygon = false;
this.state.mapDragging = true;
this.state.mapZooming = true;
this.state.mapDoubleClickZoom = true;
this.state.showCrosshairCursor = false;
this.state.showDragCursor = false;
}
/**
* Set interaction state for drawing modes (Add, Subtract)
*/
setDrawingModeState() {
this.state.polygonDragging = false;
this.state.markerDragging = false;
this.state.edgeClicking = false;
this.state.polygonClicking = false;
this.state.canStartDrawing = true;
this.state.canCompletePolygon = true;
this.state.mapDragging = false;
this.state.mapZooming = false;
this.state.mapDoubleClickZoom = false;
this.state.showCrosshairCursor = true;
this.state.showDragCursor = false;
}
/**
* Set interaction state for Point-to-Point mode
*/
setPointToPointModeState() {
this.state.polygonDragging = false;
this.state.markerDragging = false;
this.state.edgeClicking = false;
this.state.polygonClicking = false;
this.state.canStartDrawing = true;
this.state.canCompletePolygon = true;
this.state.mapDragging = false;
this.state.mapZooming = false;
this.state.mapDoubleClickZoom = false;
this.state.showCrosshairCursor = true;
this.state.showDragCursor = false;
}
/**
* Set interaction state for clone mode (drag to duplicate)
*/
setCloneModeState() {
this.state.polygonDragging = this.config.tools.clone ?? false;
this.state.markerDragging = false;
this.state.edgeClicking = false;
this.state.polygonClicking = false;
this.state.canStartDrawing = false;
this.state.canCompletePolygon = false;
this.state.mapDragging = true;
this.state.mapZooming = true;
this.state.mapDoubleClickZoom = true;
this.state.showCrosshairCursor = false;
this.state.showDragCursor = true;
}
/**
* Update modifier key state
*/
setModifierKeyState(isHeld) {
this.state.isModifierKeyHeld = isHeld;
if (this.state.currentMode === DrawMode.Off && isHeld) ;
}
/**
* Check if a specific action is currently allowed
*/
canPerformAction(action) {
switch (action) {
case "polygonDrag":
return this.state.polygonDragging;
case "markerDrag":
return this.state.markerDragging;
case "edgeClick":
return this.state.edgeClicking;
case "polygonClick":
return this.state.polygonClicking;
case "startDrawing":
return this.state.canStartDrawing;
case "completePolygon":
return this.state.canCompletePolygon;
case "mapDrag":
return this.state.mapDragging;
case "mapZoom":
return this.state.mapZooming;
case "mapDoubleClickZoom":
return this.state.mapDoubleClickZoom;
default:
return false;
}
}
/**
* Check if any drawing mode is active
*/
isInDrawingMode() {
return this.state.isDrawingActive;
}
/**
* Check if currently in Off mode (normal editing)
*/
isInOffMode() {
return this.state.currentMode === DrawMode.Off;
}
/**
* Get the current draw mode
*/
getCurrentMode() {
return this.state.currentMode;
}
/**
* Get read-only access to the current state
*/
getState() {
return { ...this.state };
}
/**
* Check if crosshair cursor should be shown
*/
shouldShowCrosshairCursor() {
return this.state.showCrosshairCursor;
}
/**
* Check if drag cursor should be shown
*/
shouldShowDragCursor() {
return this.state.showDragCursor;
}
/**
* Update configuration and recalculate state
*/
updateConfig(config) {
this.config = config;
this.updateStateForMode(this.state.currentMode);
}
}
class PolygonDrawManager {
constructor(dependencies) {
__publicField(this, "turfHelper");
__publicField(this, "map");
__publicField(this, "config");
__publicField(this, "modeManager");
__publicField(this, "eventManager");
__publicField(this, "tracer");
// Point-to-Point drawing state
__publicField(this, "p2pMarkers", []);
__publicField(this, "isModifierKeyHeld", false);
__publicField(this, "markerModifierHandlers", /* @__PURE__ */ new WeakMap());
this.turfHelper = dependencies.turfHelper;
this.map = dependencies.map;
this.config = dependencies.config;
this.modeManager = dependencies.modeManager;
this.eventManager = dependencies.eventManager;
this.tracer = dependencies.tracer;
}
/**
* Handle mouse move during freehand drawing
*/
mouseMove(event) {
if ("latlng" in event && event.latlng) {
this.tracer.addLatLng(event.latlng);
} else if ("touches" in event && event.touches && event.touches.length > 0) {
const rect = this.map.getContainer().getBoundingClientRect();
const latlng = this.map.containerPointToLatLng([
event.touches[0].clientX - rect.x,
event.touches[0].clientY - rect.y
]);
this.tracer.addLatLng(latlng);
}
}
/**
* Handle mouse up/leave to complete freehand drawing
*/
async mouseUpLeave(event) {
const tracerGeoJSON = this.tracer.toGeoJSON();
if (!tracerGeoJSON || !tracerGeoJSON.geometry || !tracerGeoJSON.geometry.coordinates || tracerGeoJSON.geometry.coordinates.length < 3) {
return {
success: false,
error: "Not enough points to form a valid polygon. Event: " + JSON.stringify(event)
};
}
let geoPos;
try {
geoPos = this.turfHelper.createPolygonFromTrace(tracerGeoJSON);
} catch (error) {
return {
success: false,
error: (error instanceof Error ? error.message : "Failed to create polygon from trace") + ". Event: " + JSON.stringify(event)
};
}
if (!geoPos || !geoPos.geometry || !geoPos.geometry.coordinates || geoPos.geometry.coordinates.length === 0) {
return {
success: false,
error: "Invalid polygon created from trace. Event: " + JSON.stringify(event)
};
}
this.eventManager.emit("polydraw:polygon:created", {
polygon: geoPos,
mode: this.modeManager.getCurrentMode()
});
return {
success: true,
polygon: geoPos
};
}
/**
* Handle point-to-point click
*/
/**
* Set the modifier key status
* @param isHeld - Whether the modifier key is held down
*/
setModifierKey(isHeld) {
this.isModifierKeyHeld = isHeld;
}
shouldAllowP2PMarkerDeletion() {
if (!this.isModifierKeyHeld || !this.config.modes.edgeDeletion) {
return false;
}
if (this.config.modifierSubtractMode) {
const mode = this.modeManager.getCurrentMode();
if (mode === DrawMode.PointToPoint || mode === DrawMode.PointToPointSubtract) {
return false;
}
}
return true;
}
/**
* Handle point-to-point click
*/
handlePointToPointClick(clickLatLng) {
if (!clickLatLng) {
return;
}
if (this.p2pMarkers.length >= 3) {
const firstPoint = this.p2pMarkers[0].getLatLng();
const isClickingFirst = this.isClickingFirstPoint(clickLatLng, firstPoint);
if (isClickingFirst) {
this.completePointToPointPolygon();
return;
}
}
try {
const isTouch = this.isTouchDevice();
const isFirstMarker = this.p2pMarkers.length === 0;
const markerClassName = isFirstMarker ? "leaflet-polydraw-p2p-marker leaflet-polydraw-p2p-first-marker" : "leaflet-polydraw-p2p-marker";
const iconSize = isFirstMarker ? isTouch ? [24, 24] : [20, 20] : isTouch ? [18, 18] : [16, 16];
const pointMarker = new L.Marker(clickLatLng, {
icon: leafletAdapter.createDivIcon({
className: markerClassName,
iconSize
}),
draggable: this.config.modes.dragElbow
});
pointMarker.on("add", () => this.applyP2PMarkerMode(pointMarker));
pointMarker.addTo(this.map);
this.applyP2PMarkerMode(pointMarker);
pointMarker.on("mousedown", (e) => {
L.DomEvent.stopPropagation(e);
});
pointMarker.on("drag", () => {
this.updateP2PTracer();
});
pointMarker.on("click", (e) => {
if (this.shouldAllowP2PMarkerDeletion()) {
this.deleteP2PMarker(pointMarker);
L.DomEvent.stopPropagation(e);
}
});
pointMarker.on("mouseover", () => this.onMarkerHoverForEdgeDeletion(pointMarker, true));
pointMarker.on("mouseout", () => this.onMarkerHoverForEdgeDeletion(pointMarker, false));
if (isFirstMarker) {
pointMarker.on("mouseover", () => {
if (this.p2pMarkers.length >= 3) {
const element = pointMarker.getElement();
if (element) {
const modeColor = this.modeManager.getCurrentMode() === DrawMode.PointToPointSubtract ? this.config.styles.ui.dragSubtract.color : this.config.styles.ui.p2pClosingMarker.color;
element.style.backgroundColor = modeColor;
element.style.borderColor = modeColor;
element.style.cursor = "pointer";
element.title = "Click to close polygon";
}
}
});
pointMarker.on("mouseout", () => {
const element = pointMarker.getElement();
if (element) {
element.style.backgroundColor = "";
element.style.borderColor = "";
element.style.cursor = "";
element.title = "";
}
});
pointMarker.on("click", (e) => {
if (this.shouldAllowP2PMarkerDeletion()) {
this.deleteP2PMarker(pointMarker);
L.DomEvent.stopPropagation(e);
return;
}
if (this.p2pMarkers.length >= 3) {
L.DomEvent.stopPropagation(e);
this.completePointToPointPolygon();
}
});
}
this.p2pMarkers.push(pointMarker);
this.updateP2PTracer();
this.updateP2PMarkerModes();
this.updateFirstMarkerReadyState();
} catch {
}
}
/**
* Handle double-click to complete point-to-point polygon
*/
handleDoubleClick(e) {
if (this.modeManager.getCurrentMode() !== DrawMode.PointToPoint && this.modeManager.getCurrentMode() !== DrawMode.PointToPointSubtract) {
return;
}
if (this.p2pMarkers.length >= 3) {
this.completePointToPointPolygon();
}
}
/**
* Handle double-tap to complete point-to-point polygon (touch devices)
*/
handleDoubleTap(event) {
if (this.modeManager.getCurrentMode() !== DrawMode.PointToPoint && this.modeManager.getCurrentMode() !== DrawMode.PointToPointSubtract) {
return;
}
if (this.p2pMarkers.length >= 3) {
this.completePointToPointPolygon();
}
}
/**
* Complete point-to-point polygon drawing
*/
completePointToPointPolygon() {
const points = this.p2pMarkers.map((marker) => marker.getLatLng());
if (points.length < 3) {
return;
}
const closedPoints = [...points];
const firstPoint = points[0];
const lastPoint = points[points.length - 1];
if (firstPoint.lat !== lastPoint.lat || firstPoint.lng !== lastPoint.lng) {
closedPoints.push(firstPoint);
}
try {
const coordinates = closedPoints.map((point2) => [point2.lng, point2.lat]);
const geoPos = this.turfHelper.getMultiPolygon([[coordinates]]);
this.clearP2pMarkers();
this.resetTracer();
this.eventManager.emit("polydraw:polygon:created", {
polygon: geoPos,
mode: this.modeManager.getCurrentMode(),
isPointToPoint: true
});
} catch (error) {
if (!isTestEnvironment()) {
console.warn("Error completing point-to-point polygon:", error);
}
this.clearP2pMarkers();
this.resetTracer();
}
}
/**
* Cancel point-to-point drawing
*/
cancelPointToPointDrawing() {
this.clearP2pMarkers();
this.resetTracer();
this.eventManager.emit("polydraw:draw:cancel", {
mode: DrawMode.PointToPoint
});
}
/**
* Clear all P2P markers
*/
clearP2pMarkers() {
this.p2pMarkers.forEach((marker) => {
this.detachMarkerModifierHandler(marker);
try {
this.map.removeLayer(marker);
} catch {
}
});
this.p2pMarkers = [];
}
/**
* Reset the tracer
*/
resetTracer() {
this.tracer.setLatLngs([]);
try {
this.tracer.setStyle({
dashArray: void 0
});
} catch {
}
}
/**
* Check if clicking on the first point to close polygon
*/
isClickingFirstPoint(clickLatLng, firstPoint) {
if (!firstPoint) return false;
const clickPt = this.map.latLngToLayerPoint(clickLatLng);
const firstPt = this.map.latLngToLayerPoint(firstPoint);
const pixelDist = clickPt.distanceTo(firstPt);
const basePxTolerance = 12;
const tolerancePx = this.isTouchDevice() ? basePxTolerance * 3 : basePxTolerance;
const isClicking = pixelDist <= tolerancePx;
return isClicking;
}
/**
* Check if the current device supports touch
*/
isTouchDevice() {
return isTouchDevice();
}
/**
* Update the tracer polyline based on P2P markers
*/
updateP2PTracer() {
const latlngs = this.p2pMarkers.map((marker) => marker.getLatLng());
this.tracer.setLatLngs(latlngs);
if (this.p2pMarkers.length >= 2) {
try {
const currentMode = this.modeManager.getCurrentMode();
const lineColor = currentMode === DrawMode.PointToPointSubtract ? this.config.styles.subtractLine.color : this.config.styles.polyline.color;
this.tracer.setStyle({
color: lineColor,
dashArray: "5, 5"
});
} catch {
}
} else {
try {
this.tracer.setStyle({
dashArray: void 0
});
} catch {
}
}
}
/**
* Delete a P2P marker
*/
deleteP2PMarker(markerToDelete) {
const markerIndex = this.p2pMarkers.findIndex((marker) => marker === markerToDelete);
if (markerIndex > -1) {
this.p2pMarkers.splice(markerIndex, 1);
this.detachMarkerModifierHandler(markerToDelete);
try {
this.map.removeLayer(markerToDelete);
} catch {
}
this.updateP2PTracer();
if (markerIndex === 0 && this.p2pMarkers.length > 0) {
this.setupFirstMarker();
}
this.updateFirstMarkerReadyState();
}
}
/**
* Get current P2P markers (for external access)
*/
getP2pMarkers() {
return [...this.p2pMarkers];
}
/**
* Check if currently in point-to-point drawing mode
*/
isInPointToPointMode() {
const mode = this.modeManager.getCurrentMode();
return mode === DrawMode.PointToPoint || mode === DrawMode.PointToPointSubtract;
}
/**
* Get current tracer points count
*/
getTracerPointsCount() {
const points = this.tracer.getLatLngs();
return points.length;
}
/**
* Set up the first marker with special properties
*/
setupFirstMarker() {
if (this.p2pMarkers.length === 0) return;
const firstMarker = this.p2pMarkers[0];
const element = firstMarker.getElement();
if (element) {
element.classList.add("leaflet-polydraw-p2p-first-marker");
element.classList.remove("leaflet-polydraw-p2p-first-marker-ready");
firstMarker.setIcon(
leafletAdapter.createDivIcon({
className: "leaflet-polydraw-p2p-marker leaflet-polydraw-p2p-first-marker",
iconSize: [20, 20]
})
);
this.applyP2PMarkerMode(firstMarker);
}
firstMarker.off("mouseover");
firstMarker.off("mouseout");
firstMarker.off("click");
firstMarker.on("mouseover", () => {
if (this.p2pMarkers.length >= 3) {
const element2 = firstMarker.getElement();
if (element2) {
element2.style.cursor = "pointer";
element2.title = "Click to close polygon";
}
}
});
firstMarker.on("mouseout", () => {
const element2 = firstMarker.getElement();
if (element2) {
element2.style.backgroundColor = "";
element2.style.borderColor = "";
element2.style.cursor = "";
element2.title = "";
}
});
firstMarker.on("click", (e) => {
if (this.shouldAllowP2PMarkerDeletion()) {
this.deleteP2PMarker(firstMarker);
L.DomEvent.stopPropagation(e);
return;
}
if (this.p2pMarkers.length >= 3) {
L.DomEvent.stopPropagation(e);
this.completePointToPointPolygon();
}
});
}
/**
* Toggle helper class for first marker when polygon can be closed.
*/
updateFirstMarkerReadyState() {
if (this.p2pMarkers.length === 0) return;
this.updateP2PMarkerModes();
const firstMarker = this.p2pMarkers[0];
const element = firstMarker.getElement();
if (!element) return;
if (this.p2pMarkers.length >= 3) {
element.classList.add("leaflet-polydraw-p2p-first-marker-ready");
element.setAttribute(
"data-p2p-mode",
this.modeManager.getCurrentMode() === DrawMode.PointToPointSubtract ? "subtract" : "add"
);
element.style.cursor = "pointer";
element.title = "Click to close polygon";
} else {
element.classList.remove("leaflet-polydraw-p2p-first-marker-ready");
element.style.cursor = "";
element.removeAttribute("title");
}
}
/**
* Refresh the first-marker ready state when draw mode changes.
*/
refreshP2PMarkerState() {
if (this.p2pMarkers.length === 0) return;
this.updateP2PMarkerModes();
this.updateFirstMarkerReadyState();
}
applyP2PMarkerMode(marker) {
const element = marker.getElement();
if (!element) return;
const mode = this.modeManager.getCurrentMode();
if (mode === DrawMode.PointToPointSubtract) {
element.setAttribute("data-p2p-mode", "subtract");
} else if (mode === DrawMode.PointToPoint) {
element.setAttribute("data-p2p-mode", "add");
} else {
element.removeAttribute("data-p2p-mode");
}
}
updateP2PMarkerModes() {
this.p2pMarkers.forEach((marker) => this.applyP2PMarkerMode(marker));
}
/**
* Handle marker hover for edge deletion feedback
*/
onMarkerHoverForEdgeDeletion(marker, isHovering) {
const element = marker.getElement();
if (!element) return;
if (isHovering) {
this.detachMarkerModifierHandler(marker);
const checkModifierAndUpdate = (e) => {
if (this.shouldAllowP2PMarkerDeletion()) {
element.classList.add("edge-deletion-hover");
try {
const container = this.map.getContainer();
container.style.cursor = "pointer";
} catch {
}
} else {
element.classList.remove("edge-deletion-hover");
try {
const container = this.map.getContainer();
container.style.cursor = "";
} catch {
}
}
};
this.markerModifierHandlers.set(marker, checkModifierAndUpdate);
document.addEventListener("keydown", checkModifierAndUpdate);
document.addEventListener("keyup", checkModifierAndUpdate);
element.addEventListener("mousemove", checkModifierAndUpdate);
} else {
element.classList.remove("edge-deletion-hover");
try {
const container = this.map.getContainer();
container.style.cursor = "";
} catch {
}
this.detachMarkerModifierHandler(marker);
}
}
detachMarkerModifierHandler(marker) {
const handler = this.markerModifierHandlers.get(marker);
if (!handler) return;
document.removeEventListener("keydown", handler);
document.removeEventListener("keyup", handler);
const element = marker.getElement();
if (element) {
element.removeEventListener("mousemove", handler);
element.classList.remove("edge-deletion-hover");
}
this.markerModifierHandlers.delete(marker);
}
}
function cloneMetadataValue(value) {
if (value === null || value === void 0) {
return value;
}
try {
return structuredClone(value);
} catch {
return cloneMetadataFallback(value);
}
}
function cloneMetadataFallback(value, seen = /* @__PURE__ */ new WeakMap()) {
if (value === null || typeof value !== "object") {
return value;
}
if (value instanceof Date) {
return new Date(value.getTime());
}
if (Array.isArray(value)) {
return value.map((item) => cloneMetadataFallback(item, seen));
}
if (seen.has(value)) {
return seen.get(value);
}
const clone2 = {};
seen.set(value, clone2);
Object.entries(value).forEach(([key, item]) => {
clone2[key] = cloneMetadataFallback(item, seen);
});
return clone2;
}
class PolygonGeometryManager {
constructor(dependencies) {
__publicField(this, "turfHelper");
__publicField(this, "config");
this.turfHelper = dependencies.turfHelper;
this.config = dependencies.config;
}
/**
* Check if two polygons intersect using multiple detection methods
*/
checkPolygonIntersection(polygon1, polygon2) {
if (this.isPolygonInsideHole(polygon1, polygon2) || this.isPolygonInsideHole(polygon2, polygon1)) {
return false;
}
try {
const poly1WithinPoly2 = this.turfHelper.isPolygonCompletelyWithin(polygon1, polygon2);
const poly2WithinPoly1 = this.turfHelper.isPolygonCompletelyWithin(polygon2, polygon1);
if (poly1WithinPoly2 || poly2WithinPoly1) {
return true;
}
} catch {
}
try {
const result = this.turfHelper.polygonIntersect(polygon1, polygon2);
if (result) {
return true;
}
} catch {
}
try {
const intersection3 = this.turfHelper.getIntersection(polygon1, polygon2);
if (intersection3 && intersection3.geometry && (intersection3.geometry.type === "Polygon" || intersection3.geometry.type === "MultiPolygon")) {
const intersectionArea2 = this.turfHelper.getPolygonArea(intersection3);
if (intersectionArea2 > 1e-6) {
return true;
}
}
} catch {
}
try {
const coords1 = this.turfHelper.getCoords(polygon1);
const coords2 = this.turfHelper.getCoords(polygon2);
for (const ring2 of coords2) {
for (const coord of ring2[0]) {
const point2 = {
type: "Feature",
geometry: { type: "Point", coordinates: coord },
properties: {}
};
if (this.turfHelper.isPointInsidePolygon(point2, polygon1)) {
return true;
}
}
}
for (const ring1 of coords1) {
for (const coord of ring1[0]) {
const point2 = {
type: "Feature",
geometry: { type: "Point", coordinates: coord },
properties: {}
};
if (this.turfHelper.isPointInsidePolygon(point2, polygon2)) {
return true;
}
}
}
} catch {
}
return false;
}
isPolygonInsideHole(innerPolygon, outerPolygon) {
try {
const outerCoords = this.turfHelper.getCoords(outerPolygon);
for (const polygonCoords of outerCoords) {
if (!polygonCoords || polygonCoords.length <= 1) {
continue;
}
const holes = polygonCoords.slice(1);
for (const holeCoords of holes) {
try {
const holePolygon = this.turfHelper.createPolygon([holeCoords]);
if (this.turfHelper.isPolygonCompletelyWithin(innerPolygon, holePolygon)) {
return true;
}
} catch {
continue;
}
}
}
} catch {
}
return false;
}
/**
* Perform union operation on multiple polygons
*/
unionPolygons(polygons, newPolygon) {
try {
let result = newPolygon;
for (const polygon2 of polygons) {
const union3 = this.turfHelper.union(result, polygon2);
if (union3) {
result = union3;
}
}
return {
success: true,
result
};
} catch (error) {
return {
success: false,
error: error instanceof Error ? error.message : "Unknown error in unionPolygons"
};
}
}
/**
* Perform subtraction operation
*/
subtractPolygon(existingPolygon, subtractPolygon) {
try {
const result = this.turfHelper.polygonDifference(existingPolygon, subtractPolygon);
if (result) {
const coords = this.turfHelper.getCoords(result);
const results = [];
for (const value of coords) {
results.push(this.turfHelper.getMultiPolygon([value]));
}
return {
success: true,
results
};
} else {
return {
success: true,
results: []
};
}
} catch (error) {
return {
success: false,
error: error instanceof Error ? error.message : "Unknown error in subtractPolygon"
};
}
}
/**
* Simplify a polygon by removing every other point
*/
simplifyPolygon(polygon2) {
var _a2, _b2;
try {
const coords = this.turfHelper.getCoords(polygon2);
if (!coords || coords.length === 0) {
return { success: false, error: "Invalid polygon coordinates" };
}
const processHoles = ((_b2 = (_a2 = this.config.polygonTools) == null ? void 0 : _a2.simplify) == null ? void 0 : _b2.processHoles) ?? true;
const simplifiedCoords = coords.map((ring) => {
const simplifiedRings = [];
for (let ringIndex = 0; ringIndex < ring.length; ringIndex++) {
const currentRing = ring[ringIndex];
const isOuterRing = ringIndex === 0;
if (isOuterRing || processHoles) {
if (currentRing.length <= 4) {
simplifiedRings.push(currentRing);
continue;
}
const simplified = [];
for (let i = 0; i < currentRing.length; i += 2) {
simplified.push(currentRing[i]);
}
const firstPoint = simplified[0];
const lastPoint = simplified[simplified.length - 1];
if (firstPoint[0] !== lastPoint[0] || firstPoint[1] !== lastPoint[1]) {
simplified.push(firstPoint);
}
if (simplified.length < 4) {
simplifiedRings.push(currentRing);
} else {
simplifiedRings.push(simplified);
}
} else {
simplifiedRings.push(currentRing);
}
}
return simplifiedRings;
});
const result = this.turfHelper.getMultiPolygon(simplifiedCoords);
return {
success: true,
result: this.turfHelper.getTurfPolygon(result)
};
} catch (error) {
return {
success: false,
error: error instanceof Error ? error.message : "Unknown error in simplifyPolygon"
};
}
}
/**
* Convert polygon to bounding box
*/
convertToBoundingBox(polygon2) {
var _a2, _b2, _c, _d;
try {
const coords = this.turfHelper.getCoords(polygon2);
if (!coords || coords.length === 0) {
return { success: false, error: "Invalid polygon coordinates" };
}
const processHoles = ((_b2 = (_a2 = this.config.polygonTools) == null ? void 0 : _a2.bbox) == null ? void 0 : _b2.processHoles) ?? true;
if (!processHoles) {
const result2 = this.turfHelper.convertToBoundingBoxPolygon(polygon2);
return {
success: true,
result: this.turfHelper.getTurfPolygon(result2)
};
}
const bboxCoords = coords.map((ring) => {
const bboxRings = [];
for (let ringIndex = 0; ringIndex < ring.length; ringIndex++) {
const currentRing = ring[ringIndex];
let minLat = Infinity;
let maxLat = -Infinity;
let minLng = Infinity;
let maxLng = -Infinity;
for (const coord of currentRing) {
if (Array.isArray(coord) && coord.length >= 2) {
const lng = coord[0];
const lat = coord[1];
if (typeof lng === "number" && typeof lat === "number" && !isNaN(lng) && !isNaN(lat)) {
minLng = Math.min(minLng, lng);
maxLng = Math.max(maxLng, lng);
minLat = Math.min(minLat, lat);
maxLat = Math.max(maxLat, lat);
}
}
}
if (minLat !== Infinity && maxLat !== -Infinity && minLng !== Infinity && maxLng !== -Infinity) {
const rectangularRing = [
[minLng, minLat],
// Bottom-left
[minLng, maxLat],
// Top-left
[maxLng, maxLat],
// Top-right
[maxLng, minLat],
// Bottom-right
[minLng, minLat]
// Close the ring
];
bboxRings.push(rectangularRing);
} else {
bboxRings.push(currentRing);
}
}
return bboxRings;
});
const multiPolygon2 = this.turfHelper.getMultiPolygon(bboxCoords);
let result = this.turfHelper.getTurfPolygon(multiPolygon2);
if ((_d = (_c = this.config.polygonTools) == null ? void 0 : _c.bbox) == null ? void 0 : _d.addMidPointMarkers) {
const doubled = this.doubleElbowsPolygon(result);
if (doubled.success && doubled.result) {
result = doubled.result;
}
}
return {
success: true,
result
};
} catch (error) {
return {
success: false,
error: error instanceof Error ? error.message : "Unknown error in convertToBoundingBox"
};
}
}
/**
* Apply bezier curve to polygon
*/
bezierifyPolygon(polygon2) {
try {
const coords = this.turfHelper.getCoords(polygon2);
const result = this.turfHelper.getBezierMultiPolygon(coords);
return {
success: true,
result: this.turfHelper.getTurfPolygon(result)
};
} catch (error) {
return {
success: false,
error: error instanceof Error ? error.message : "Unknown error in bezierifyPolygon"
};
}
}
/**
* Double the elbows of a polygon
*/
doubleElbowsPolygon(polygon2) {
var _a2, _b2;
try {
const coords = this.turfHelper.getCoords(polygon2);
if (!coords || coords.length === 0) {
return { success: false, error: "Invalid polygon coordinates" };
}
const processHoles = ((_b2 = (_a2 = this.config.polygonTools) == null ? void 0 : _a2.doubleElbows) == null ? void 0 : _b2.processHoles) ?? true;
const doubleElbowCoords = coords.map((ring) => {
const processedRings = [];
for (let ringIndex = 0; ringIndex < ring.length; ringIndex++) {
const currentRing = ring[ringIndex];
const isOuterRing = ringIndex === 0;
if (isOuterRing || processHoles) {
const latlngs = currentRing.map((coord) => ({
lat: coord[1],
lng: coord[0]
}));
const doubleLatLngs = this.turfHelper.getDoubleElbowLatLngs(latlngs);
const doubledCoords = doubleLatLngs.map(
(latlng) => [latlng.lng, latlng.lat]
);
processedRings.push(doubledCoords);
} else {
processedRings.push(currentRing);
}
}
return processedRings;
});
const result = this.turfHelper.getMultiPolygon(doubleElbowCoords);
return {
success: true,
result: this.turfHelper.getTurfPolygon(result)
};
} catch (error) {
return {
success: false,
error: error instanceof Error ? error.message : "Unknown error in doubleElbowsPolygon"
};
}
}
}
class IconFactory {
/**
* Creates a DivIcon with the given class names.
* @param classNames Array of class names to apply.
* @returns A Leaflet DivIcon.
*/
static createDivIcon(classNames) {
const classes = classNames.join(" ");
return leafletAdapter.createDivIcon({ className: classes });
}
}
class EventAdapter {
/**
* Normalizes events to handle differences between v1 and v2
* @param event - The event to normalize
* @returns The normalized event
*/
static normalizeEvent(event) {
const version = LeafletVersionDetector.getVersion();
if (version === LeafletVersion.V2) {
if (event.layer && !event.propagatedFrom) {
event.propagatedFrom = event.layer;
}
if (event.type === "mousedown" || event.type === "touchstart") {
event._isPointerEvent = true;
}
}
return event;
}
/**
* Gets the appropriate event name for the current Leaflet version
* @param eventName - The base event name
* @returns The version-appropriate event name
*/
static getEventName(eventName) {
const version = LeafletVersionDetector.getVersion();
if (version === LeafletVersion.V2) {
switch (eventName) {
case "mousedown":
return "pointerdown";
case "mouseup":
return "pointerup";
case "mousemove":
return "pointermove";
case "touchstart":
return "pointerdown";
case "touchend":
return "pointerup";
case "touchmove":
return "pointermove";
default:
return eventName;
}
}
return eventName;
}
/**
* Checks if pointer events are supported/preferred
* @returns true if pointer events should be used
*/
static shouldUsePointerEvents() {
const version = LeafletVersionDetector.getVersion();
return version === LeafletVersion.V2 && "PointerEvent" in window;
}
/**
* Extracts coordinates from various event types (mouse, touch, pointer)
* @param event - The event to extract coordinates from
* @param map - The map instance for coordinate conversion
* @returns The LatLng coordinates or null if not available
*/
static extractCoordinates(event, map) {
if (event.latlng) {
return event.latlng;
}
if (event.touches && event.touches.length > 0) {
const rect = map.getContainer().getBoundingClientRect();
return map.containerPointToLatLng([
event.touches[0].clientX - rect.x,
event.touches[0].clientY - rect.y
]);
}
if (event.pointerType && event.clientX !== void 0 && event.clientY !== void 0) {
const rect = map.getContainer().getBoundingClientRect();
return map.containerPointToLatLng([event.clientX - rect.x, event.clientY - rect.y]);
}
if (event.clientX !== void 0 && event.clientY !== void 0) {
const rect = map.getContainer().getBoundingClientRect();
return map.containerPointToLatLng([event.clientX - rect.x, event.clientY - rect.y]);
}
return null;
}
/**
* Checks if an event should be prevented from default behavior
* @param event - The event to check
* @returns true if preventDefault should be called
*/
static shouldPreventDefault(event) {
if (event.type && event.type.startsWith("touch")) {
return true;
}
if (event.pointerType && event.isPrimary === false) {
return false;
}
if ("cancelable" in event && event.cancelable) {
return true;
}
return false;
}
/**
* Gets the appropriate event listener names for the current version
* @returns Object with event names for different actions
*/
static getEventNames() {
const version = LeafletVersionDetector.getVersion();
if (version === LeafletVersion.V2 && this.shouldUsePointerEvents()) {
return {
start: ["pointerdown"],
move: ["pointermove"],
end: ["pointerup"]
};
} else {
return {
start: ["mousedown", "touchstart"],
move: ["mousemove", "touchmove"],
end: ["mouseup", "touchend"]
};
}
}
}
var TransformHandleType = /* @__PURE__ */ ((TransformHandleType2) => {
TransformHandleType2["TopLeft"] = "top-left";
TransformHandleType2["Top"] = "top";
TransformHandleType2["TopRight"] = "top-right";
TransformHandleType2["Right"] = "right";
TransformHandleType2["BottomRight"] = "bottom-right";
TransformHandleType2["Bottom"] = "bottom";
TransformHandleType2["BottomLeft"] = "bottom-left";
TransformHandleType2["Left"] = "left";
TransformHandleType2["Rotate"] = "rotate";
TransformHandleType2["Pivot"] = "pivot";
return TransformHandleType2;
})(TransformHandleType || {});
const cancelIconSvg = '<?xml version="1.0" encoding="UTF-8"?>\n<svg width="512" height="512" viewBox="0 0 512 512" xmlns="http://www.w3.org/2000/svg">\n <circle cx="256" cy="256" r="236" fill="#212A32"/>\n <circle cx="256" cy="256" r="190" fill="#ffffff"/>\n <path id="path2" fill="#800000" fill-rule="evenodd" stroke="none"\n d="M 156 373.906311 C 140.875 373.871002 127.9375 373.615112 127.25 373.337708 C 126.5625 373.060303 126 372.072937 126 371.143555 C 126 370.214203 146.628433 343.53772 171.840973 311.862488 C 197.053513 280.187286 217.991867 253.463562 218.370667 252.47644 C 218.806335 251.341125 218.221786 249.464233 216.779678 247.368073 C 215.525864 245.545563 196.612503 221.361847 174.75 193.626465 C 152.887497 165.891052 135 142.531311 135 141.715881 C 135 140.900421 136.012497 139.961914 137.25 139.63028 C 138.487503 139.298645 152.408203 139.021179 168.184906 139.013641 C 184.588272 139.005859 197.976456 139.420746 199.454529 139.982727 C 201.182831 140.639801 210.486664 152.319733 227.533463 175.232727 C 241.555283 194.079712 253.994949 210.166199 255.177185 210.980438 C 257.208252 212.379272 257.562622 212.186401 261.607452 207.480438 C 263.961853 204.74118 276.052643 189 288.47583 172.5 C 300.899048 156 312.286682 141.714874 313.781738 140.75528 C 316.198669 139.203949 319.554993 139.009949 344.058868 139.00528 C 366.27774 139.001007 371.993561 139.274811 373.557343 140.418274 C 375.458923 141.808746 375.448914 141.90155 373.046875 145.150452 C 371.699341 146.973114 352.828827 170.746307 331.112396 197.979767 C 309.395966 225.213257 291.465973 248.396271 291.267914 249.497589 C 291.012512 250.91803 304.435944 268.61731 337.453918 310.395416 C 363.05426 342.787903 384 370.077271 384 371.038422 C 384 371.999603 383.288208 373.059174 382.418213 373.393005 C 381.548218 373.726868 367.485718 373.993347 351.168213 373.98526 C 329.591888 373.974548 320.854309 373.633606 319.132629 372.73526 C 317.830597 372.055847 302.980591 353.049988 286.132629 330.5 C 260.059052 295.602051 255.12793 289.5 253 289.5 C 250.878326 289.5 245.808807 295.701355 219.5 330.479431 C 202.449997 353.018127 187.375 372.023987 186 372.714691 C 184.165009 373.636475 176.184906 373.95343 156 373.906311 Z"/>\n</svg>';
const confirmIconSvg = '<?xml version="1.0" encoding="UTF-8"?>\n<svg width="512" height="512" viewBox="0 0 512 512" xmlns="http://www.w3.org/2000/svg">\n <circle cx="256" cy="256" r="236" fill="#212A32"/>\n <circle cx="256" cy="256" r="190" fill="#ffffff"/>\n <path id="check" fill="#008000" fill-rule="evenodd" stroke="none"\n d="M 223.844696 360 C 222.79425 360 204.506973 341.170593 178.987274 313.812744 \n C 144.992401 277.369232 135.966232 267.145691 136.201645 265.351257 \n C 136.396179 263.868408 143.092361 257.073425 155.444672 245.824249 \n C 166.363129 235.880859 175.214264 228.571442 176.336426 228.571442 \n C 177.515594 228.571442 181.716553 232.304138 186.987961 238.035706 \n C 191.775406 243.241058 201.629044 254.024994 208.884933 262 \n C 216.140823 269.975006 222.682266 276.635315 223.421478 276.80072 \n C 224.16246 276.966553 247.742722 250.494751 275.976837 217.800751 \n C 304.143097 185.185333 328.050385 157.879608 329.104156 157.121368 \n C 330.785767 155.911316 331.44046 156.0336 334.457672 158.121368 \n C 336.348328 159.429626 345.368622 167.190216 354.502747 175.367126 \n C 367.688812 187.171356 371.047394 190.689819 370.805115 192.445557 \n C 370.635864 193.67218 338.242889 231.469543 298.059082 277.32843 \n C 253.148834 328.581238 224.944153 360 223.844696 360 Z"/>\n</svg>';
const warningIconSvg = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512.055 512.055">\n <g transform="translate(-1)">\n <path\n fill="#facc15"\n d="M56.879,469.361L257.028,69.064l200.149,400.297H56.879z"\n />\n <path\n fill="#7f1d1d"\n fill-rule="evenodd"\n clip-rule="evenodd"\n d="M510.775,481.154L276.109,11.82c-7.862-15.724-30.3-15.724-38.162,0L3.28,481.154 c-7.092,14.185,3.222,30.874,19.081,30.874h469.333C507.553,512.028,517.868,495.338,510.775,481.154z M56.879,469.361 L257.028,69.064l200.149,400.297H56.879z"\n />\n <path\n fill="#7f1d1d"\n d="M235.694,192.028v149.333c0,11.782,9.551,21.333,21.333,21.333s21.333-9.551,21.333-21.333V192.028 c0-11.782-9.551-21.333-21.333-21.333S235.694,180.245,235.694,192.028z"\n />\n <path\n fill="#7f1d1d"\n d="M257.028,384.028c-11.776,0-21.333,9.557-21.333,21.333s9.557,21.333,21.333,21.333s21.333-9.557,21.333-21.333 S268.804,384.028,257.028,384.028z"\n />\n </g>\n</svg>\n';
class TransformOverlay {
constructor(map, callbacks, mode, onCancel, onConfirm) {
__publicField(this, "map");
__publicField(this, "pane");
__publicField(this, "root");
__publicField(this, "draggingHandle", null);
__publicField(this, "documentMoveHandler", null);
__publicField(this, "documentUpHandler", null);
__publicField(this, "pointerCaptureTarget", null);
__publicField(this, "activePointerId", null);
__publicField(this, "callbacks");
__publicField(this, "rafId", null);
__publicField(this, "mode");
__publicField(this, "onCancel");
__publicField(this, "onConfirm");
__publicField(this, "cancelBtn", null);
__publicField(this, "confirmBtn", null);
__publicField(this, "statusLabel", null);
__publicField(this, "currentBBox", null);
__publicField(this, "currentRotation", 0);
__publicField(this, "buttonsHidden", false);
__publicField(this, "destroyed", false);
__publicField(this, "donutValidation", null);
__publicField(this, "donutWarningVisible", false);
__publicField(this, "supportsPointerEvents", typeof window !== "undefined" && "PointerEvent" in window);
var _a2;
this.map = map;
this.callbacks = callbacks;
this.mode = mode;
this.onCancel = onCancel;
this.onConfirm = onConfirm;
const paneName = "polydraw-transform";
const mapWithPane = this.map;
let pane = (_a2 = mapWithPane.getPane) == null ? void 0 : _a2.call(mapWithPane, paneName);
if (!pane) pane = this.map.createPane(paneName);
pane.style.zIndex = "650";
pane.style.pointerEvents = "auto";
this.pane = pane;
this.root = document.createElement("div");
this.root.className = "polydraw-transform-root";
this.root.style.position = "absolute";
this.root.style.left = "0px";
this.root.style.top = "0px";
this.root.style.pointerEvents = "none";
this.pane.appendChild(this.root);
}
destroy() {
if (this.destroyed) return;
this.destroyed = true;
if (this.rafId != null) {
cancelAnimationFrame(this.rafId);
this.rafId = null;
}
this.draggingHandle = null;
this.detachDocumentDragHandlers();
this.root.remove();
}
setDonutValidation(validation, warningVisible = validation.warning) {
this.donutValidation = validation;
this.donutWarningVisible = warningVisible;
}
update(bbox2, pivot, rotation = 0) {
if (this.destroyed) return;
if (this.rafId != null) return;
this.rafId = requestAnimationFrame(() => {
this.rafId = null;
if (this.destroyed) return;
this.render(bbox2, pivot, rotation);
if (this.cancelBtn || this.confirmBtn) {
const isRotateDrag = this.draggingHandle === TransformHandleType.Rotate && this.mode === "rotate";
if (!isRotateDrag) {
this.updateButtonPositions(bbox2, rotation);
}
}
});
}
render(bbox2, _pivot, rotation = 0) {
const cancelBtn = this.cancelBtn;
const confirmBtn = this.confirmBtn;
const statusLabel = this.statusLabel;
this.root.innerHTML = "";
this.currentBBox = bbox2;
this.currentRotation = rotation;
this.cancelBtn = cancelBtn;
this.confirmBtn = confirmBtn;
this.statusLabel = statusLabel;
const width = bbox2.maxX - bbox2.minX;
const height = bbox2.maxY - bbox2.minY;
const centerX = bbox2.minX + width / 2;
const centerY = bbox2.minY + height / 2;
const palette = this.getAccentPalette();
const box = document.createElement("div");
box.className = "polydraw-transform-box";
if (this.mode === "donut" && this.donutWarningVisible) {
box.classList.add("is-invalid");
}
Object.assign(box.style, {
position: "absolute",
left: `${centerX}px`,
top: `${centerY}px`,
width: `${width}px`,
height: `${height}px`,
marginLeft: `${-width / 2}px`,
marginTop: `${-height / 2}px`,
border: `1px dashed ${palette.boxBorder}`,
pointerEvents: "none",
transformOrigin: "center center",
transform: `rotate(${rotation * 180 / Math.PI}deg)`
});
this.root.appendChild(box);
const makeHandle = (type, x, y) => {
const dx = x - centerX;
const dy = y - centerY;
const cos = Math.cos(rotation);
const sin = Math.sin(rotation);
const rotX = centerX + dx * cos - dy * sin;
const rotY = centerY + dx * sin + dy * cos;
const h = document.createElement("div");
h.className = `polydraw-transform-handle handle-${type}`;
Object.assign(h.style, {
position: "absolute",
left: `${rotX - 5}px`,
top: `${rotY - 5}px`,
width: "10px",
height: "10px",
borderRadius: "2px",
background: palette.handleFill,
boxShadow: `0 0 0 1px ${palette.handleOutline}`,
cursor: this.cursorForHandle(type),
pointerEvents: "auto",
touchAction: "none"
});
if (this.mode === "donut" && this.donutWarningVisible) {
h.classList.add("is-invalid");
}
if (this.supportsPointerEvents) {
L.DomEvent.on(
h,
"pointerdown",
(e) => this.startDrag(type, { x: rotX, y: rotY }, e),
this
);
} else {
L.DomEvent.on(
h,
"mousedown",
(e) => this.startDrag(type, { x: rotX, y: rotY }, e),
this
);
L.DomEvent.on(
h,
"touchstart",
(e) => this.startDrag(type, { x: rotX, y: rotY }, e),
this
);
}
this.root.appendChild(h);
};
if (this.mode === "scale" || this.mode === "donut") {
makeHandle(TransformHandleType.TopLeft, bbox2.minX, bbox2.minY);
makeHandle(TransformHandleType.TopRight, bbox2.maxX, bbox2.minY);
makeHandle(TransformHandleType.BottomRight, bbox2.maxX, bbox2.maxY);
makeHandle(TransformHandleType.BottomLeft, bbox2.minX, bbox2.maxY);
makeHandle(TransformHandleType.Top, centerX, bbox2.minY);
makeHandle(TransformHandleType.Right, bbox2.maxX, centerY);
makeHandle(TransformHandleType.Bottom, centerX, bbox2.maxY);
makeHandle(TransformHandleType.Left, bbox2.minX, centerY);
} else {
makeHandle(TransformHandleType.Rotate, bbox2.minX, bbox2.minY);
makeHandle(TransformHandleType.Rotate, bbox2.maxX, bbox2.minY);
makeHandle(TransformHandleType.Rotate, bbox2.maxX, bbox2.maxY);
makeHandle(TransformHandleType.Rotate, bbox2.minX, bbox2.maxY);
}
if (!this.cancelBtn && !this.confirmBtn) {
this.updateButtonPositions(bbox2, rotation);
} else {
this.updateButtonPositions(bbox2, rotation);
if (this.cancelBtn) this.root.appendChild(this.cancelBtn);
if (this.confirmBtn) this.root.appendChild(this.confirmBtn);
}
}
cursorForHandle(type) {
switch (type) {
case TransformHandleType.Top:
case TransformHandleType.Bottom:
return "ns-resize";
case TransformHandleType.Left:
case TransformHandleType.Right:
return "ew-resize";
case TransformHandleType.TopLeft:
case TransformHandleType.BottomRight:
return "nwse-resize";
case TransformHandleType.TopRight:
case TransformHandleType.BottomLeft:
return "nesw-resize";
case TransformHandleType.Rotate:
return "grab";
case TransformHandleType.Pivot:
return "move";
default:
return "default";
}
}
startDrag(type, start, evt) {
var _a2, _b2;
if (this.destroyed) return;
L.DomEvent.stop(evt);
this.draggingHandle = type;
this.hideButtons();
this.documentMoveHandler = (e) => this.onDrag(e);
this.documentUpHandler = (e) => this.endDrag(e);
const doc = document;
const moveHandler = this.documentMoveHandler;
const upHandler = this.documentUpHandler;
if (this.supportsPointerEvents && evt instanceof PointerEvent) {
this.activePointerId = evt.pointerId;
this.pointerCaptureTarget = this.map.getContainer();
try {
(_b2 = (_a2 = this.pointerCaptureTarget) == null ? void 0 : _a2.setPointerCapture) == null ? void 0 : _b2.call(_a2, evt.pointerId);
} catch {
}
L.DomEvent.on(doc, "pointermove", moveHandler);
L.DomEvent.on(doc, "pointerup", upHandler);
L.DomEvent.on(doc, "pointercancel", upHandler);
} else {
L.DomEvent.on(doc, "mousemove", moveHandler);
L.DomEvent.on(doc, "mouseup", upHandler);
L.DomEvent.on(doc, "touchmove", moveHandler);
L.DomEvent.on(doc, "touchend", upHandler);
L.DomEvent.on(doc, "touchcancel", upHandler);
}
this.callbacks.onStartHandleDrag(type, start, evt);
}
onDrag(evt) {
if (this.draggingHandle == null) return;
const pos = this.getMouseLayerPoint(evt);
this.callbacks.onDragHandle(this.draggingHandle, pos, evt);
}
endDrag(evt) {
if (this.draggingHandle == null) return;
const type = this.draggingHandle;
this.draggingHandle = null;
try {
const pos = this.getMouseLayerPoint(evt);
this.callbacks.onEndHandleDrag(type, pos, evt);
if (this.currentBBox) {
this.updateButtonPositions(this.currentBBox, this.currentRotation);
}
this.showButtons();
} finally {
this.detachDocumentDragHandlers();
}
}
detachDocumentDragHandlers() {
var _a2, _b2;
if (!this.documentMoveHandler && !this.documentUpHandler && !this.pointerCaptureTarget) {
return;
}
const doc = document;
if (this.supportsPointerEvents && this.pointerCaptureTarget) {
if (this.activePointerId != null) {
try {
(_b2 = (_a2 = this.pointerCaptureTarget).releasePointerCapture) == null ? void 0 : _b2.call(_a2, this.activePointerId);
} catch {
}
}
if (this.documentMoveHandler) {
const moveHandler = this.documentMoveHandler;
L.DomEvent.off(doc, "pointermove", moveHandler);
}
if (this.documentUpHandler) {
const upHandler = this.documentUpHandler;
L.DomEvent.off(doc, "pointerup", upHandler);
L.DomEvent.off(doc, "pointercancel", upHandler);
}
this.pointerCaptureTarget = null;
this.activePointerId = null;
} else {
if (this.documentMoveHandler) {
const moveHandler = this.documentMoveHandler;
L.DomEvent.off(doc, "mousemove", moveHandler);
L.DomEvent.off(doc, "touchmove", moveHandler);
}
if (this.documentUpHandler) {
const upHandler = this.documentUpHandler;
L.DomEvent.off(doc, "mouseup", upHandler);
L.DomEvent.off(doc, "touchend", upHandler);
L.DomEvent.off(doc, "touchcancel", upHandler);
}
}
this.documentMoveHandler = null;
this.documentUpHandler = null;
}
getMouseLayerPoint(evt) {
const container = this.map.getContainer();
let clientX = null;
let clientY = null;
if (evt.touches && evt.touches.length > 0) {
const touch = evt.touches[0];
clientX = touch.clientX;
clientY = touch.clientY;
} else if (evt.changedTouches && evt.changedTouches.length > 0) {
const touch = evt.changedTouches[0];
clientX = touch.clientX;
clientY = touch.clientY;
} else if (evt.clientX != null) {
clientX = evt.clientX;
clientY = evt.clientY;
}
if (clientX == null || clientY == null) {
clientX = 0;
clientY = 0;
}
const rect = container.getBoundingClientRect();
const x = clientX - rect.left;
const y = clientY - rect.top;
const containerPoint = leafletAdapter.createPoint(x, y);
const layerPoint = this.map.containerPointToLayerPoint(containerPoint);
return { x: layerPoint.x, y: layerPoint.y };
}
updateButtonPositions(bbox2, rotation) {
var _a2, _b2;
const width = bbox2.maxX - bbox2.minX;
const height = bbox2.maxY - bbox2.minY;
const centerX = bbox2.minX + width / 2;
const centerY = bbox2.minY + height / 2;
const corners = [
{ x: bbox2.minX, y: bbox2.minY },
// top-left
{ x: bbox2.maxX, y: bbox2.minY },
// top-right
{ x: bbox2.maxX, y: bbox2.maxY },
// bottom-right
{ x: bbox2.minX, y: bbox2.maxY }
// bottom-left
];
const cos = Math.cos(rotation);
const sin = Math.sin(rotation);
let topRightCorner = { x: 0, y: Infinity };
let minY = Infinity;
for (const corner of corners) {
const dx = corner.x - centerX;
const dy = corner.y - centerY;
const rotX = centerX + dx * cos - dy * sin;
const rotY = centerY + dx * sin + dy * cos;
const isHigher = rotY < minY;
const isSameHeight = Math.abs(rotY - minY) < 1;
const isMoreRight = rotX > topRightCorner.x;
if (isHigher || isSameHeight && isMoreRight) {
minY = rotY;
topRightCorner = { x: rotX, y: rotY };
}
}
const rotTopRightX = topRightCorner.x;
const rotTopRightY = topRightCorner.y;
const buttonY = rotTopRightY - 28;
const confirmX = rotTopRightX + 5;
const cancelX = rotTopRightX - 29;
const statusX = cancelX - 12;
const statusY = buttonY + 4;
const cancelSvg = cancelIconSvg;
const confirmSvg = confirmIconSvg;
if (this.onCancel && !this.cancelBtn) {
this.cancelBtn = document.createElement("div");
this.cancelBtn.className = "polydraw-transform-cancel";
this.cancelBtn.innerHTML = cancelSvg;
L.DomEvent.on(this.cancelBtn, "mousedown", (e) => {
e.stopPropagation();
e.preventDefault();
if (this.onCancel) this.onCancel();
});
L.DomEvent.on(this.cancelBtn, "touchstart", (e) => {
e.stopPropagation();
e.preventDefault();
if (this.onCancel) this.onCancel();
});
L.DomEvent.on(this.cancelBtn, "pointerdown", (e) => {
e.stopPropagation();
e.preventDefault();
if (this.onCancel) this.onCancel();
});
this.root.appendChild(this.cancelBtn);
}
if (this.onConfirm && !this.confirmBtn) {
this.confirmBtn = document.createElement("div");
this.confirmBtn.className = "polydraw-transform-confirm";
this.confirmBtn.innerHTML = confirmSvg;
L.DomEvent.on(this.confirmBtn, "mousedown", (e) => {
e.stopPropagation();
e.preventDefault();
if (this.onConfirm && this.isConfirmEnabled()) this.onConfirm();
});
L.DomEvent.on(this.confirmBtn, "touchstart", (e) => {
e.stopPropagation();
e.preventDefault();
if (this.onConfirm && this.isConfirmEnabled()) this.onConfirm();
});
L.DomEvent.on(this.confirmBtn, "pointerdown", (e) => {
e.stopPropagation();
e.preventDefault();
if (this.onConfirm && this.isConfirmEnabled()) this.onConfirm();
});
this.root.appendChild(this.confirmBtn);
}
if (this.mode === "donut" && !this.statusLabel) {
this.statusLabel = document.createElement("div");
this.statusLabel.className = "polydraw-transform-status";
this.root.appendChild(this.statusLabel);
}
if (this.cancelBtn) {
Object.assign(this.cancelBtn.style, {
position: "absolute",
left: `${cancelX}px`,
top: `${buttonY}px`,
display: "grid",
placeItems: "center",
pointerEvents: "auto",
width: "28px",
height: "28px"
});
const svg = this.cancelBtn.querySelector("svg");
if (svg) {
svg.setAttribute("width", "24");
svg.setAttribute("height", "24");
}
this.cancelBtn.style.display = this.buttonsHidden ? "none" : "grid";
}
if (this.confirmBtn) {
const confirmEnabled = this.isConfirmEnabled();
Object.assign(this.confirmBtn.style, {
position: "absolute",
left: `${confirmX}px`,
top: `${buttonY}px`,
display: "grid",
placeItems: "center",
pointerEvents: confirmEnabled ? "auto" : "none",
width: "28px",
height: "28px"
});
const svg = this.confirmBtn.querySelector("svg");
if (svg) {
svg.setAttribute("width", "24");
svg.setAttribute("height", "24");
}
this.confirmBtn.classList.toggle("is-disabled", !confirmEnabled);
this.confirmBtn.setAttribute("aria-disabled", String(!confirmEnabled));
this.confirmBtn.style.display = this.buttonsHidden ? "none" : "grid";
}
if (this.statusLabel) {
const isInvalid = this.mode === "donut" && this.donutWarningVisible;
this.statusLabel.innerHTML = isInvalid ? warningIconSvg : "";
this.statusLabel.classList.toggle("is-invalid", isInvalid);
this.statusLabel.setAttribute(
"data-donut-validity",
((_a2 = this.donutValidation) == null ? void 0 : _a2.validity) ?? "invalid"
);
this.statusLabel.title = isInvalid ? ((_b2 = this.donutValidation) == null ? void 0 : _b2.statusText) ?? "" : "";
Object.assign(this.statusLabel.style, {
position: "absolute",
left: `${statusX}px`,
top: `${statusY}px`,
transform: "translateX(-100%)",
pointerEvents: "none",
display: isInvalid ? "grid" : "none"
});
if (isInvalid) {
this.root.appendChild(this.statusLabel);
}
}
}
hideButtons() {
this.buttonsHidden = true;
if (this.cancelBtn) this.cancelBtn.style.display = "none";
if (this.confirmBtn) this.confirmBtn.style.display = "none";
}
showButtons() {
this.buttonsHidden = false;
if (this.cancelBtn) this.cancelBtn.style.display = "grid";
if (this.confirmBtn) {
this.confirmBtn.style.display = "grid";
if (!this.isConfirmEnabled()) {
this.confirmBtn.style.display = "grid";
}
}
}
getAccentPalette() {
if (this.mode === "donut" && this.donutWarningVisible) {
return {
boxBorder: "#d97706",
handleFill: "#f59e0b",
handleOutline: "#fff7ed"
};
}
return {
boxBorder: "#2b90d9",
handleFill: "#2b90d9",
handleOutline: "#fff"
};
}
isConfirmEnabled() {
var _a2;
if (this.mode !== "donut") {
return true;
}
return ((_a2 = this.donutValidation) == null ? void 0 : _a2.submitEnabled) === true;
}
}
function latLngToPixel(map, ll) {
const p = map.latLngToLayerPoint(ll);
return { x: p.x, y: p.y };
}
function pixelToLatLng(map, p) {
const ll = map.layerPointToLatLng(leafletAdapter.createPoint(p.x, p.y));
return { lat: ll.lat, lng: ll.lng };
}
function getPixelBBox(rings) {
let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
for (const poly of rings) {
for (const ring of poly) {
for (const pt of ring) {
if (pt.x < minX) minX = pt.x;
if (pt.y < minY) minY = pt.y;
if (pt.x > maxX) maxX = pt.x;
if (pt.y > maxY) maxY = pt.y;
}
}
}
return { minX, minY, maxX, maxY };
}
function getPixelCentroid(bbox2) {
return { x: (bbox2.minX + bbox2.maxX) / 2, y: (bbox2.minY + bbox2.maxY) / 2 };
}
function rotatePointAround(p, pivot, radians) {
const cos = Math.cos(radians);
const sin = Math.sin(radians);
const dx = p.x - pivot.x;
const dy = p.y - pivot.y;
return { x: pivot.x + dx * cos - dy * sin, y: pivot.y + dx * sin + dy * cos };
}
function scalePointAround(p, pivot, scaleX, scaleY) {
const dx = p.x - pivot.x;
const dy = p.y - pivot.y;
return { x: pivot.x + dx * scaleX, y: pivot.y + dy * scaleY };
}
function applyTransform(rings, pivot, scaleX, scaleY, rotation) {
return rings.map(
(poly) => poly.map(
(ring) => ring.map(
(pt) => rotatePointAround(scalePointAround(pt, pivot, scaleX, scaleY), pivot, rotation)
)
)
);
}
function ensureClosedLatLngRing(ll) {
if (ll.length === 0) return ll;
const first = ll[0];
const last = ll[ll.length - 1];
if (first.lat === last.lat && first.lng === last.lng) return ll;
return [...ll, { lat: first.lat, lng: first.lng }];
}
function normalizeLatLngs(input) {
if (!Array.isArray(input)) {
return [[input]];
}
const a = input;
if (a.length === 0) return [];
if (!Array.isArray(a[0])) {
return [[input]];
}
if (!Array.isArray(a[0][0])) {
return [input];
}
return input;
}
function projectLatLngs(map, latLngs) {
const rings = [];
const ringLengths = [];
for (const poly of latLngs) {
const polyRings = [];
const lengths = [];
for (const ring of poly) {
const pts = ring.map((ll) => latLngToPixel(map, { lat: ll.lat, lng: ll.lng }));
polyRings.push(pts);
lengths.push(ring.length);
}
rings.push(polyRings);
ringLengths.push(lengths);
}
return { rings, ringLengths };
}
function unprojectToLatLngs(map, rings, templateLatLngs) {
const out = [];
for (let i = 0; i < rings.length; i++) {
const poly = [];
for (let j = 0; j < rings[i].length; j++) {
const ringPts = rings[i][j];
const ll = ringPts.map((p) => pixelToLatLng(map, p));
const templ = templateLatLngs[i][j];
const isClosed = templ.length > 2 && templ[0].lat === templ[templ.length - 1].lat && templ[0].lng === templ[templ.length - 1].lng;
poly.push(isClosed ? ensureClosedLatLngRing(ll) : ll);
}
out.push(poly);
}
return out;
}
function snapAngleRadians(angle, stepDeg = 15) {
const step = stepDeg * Math.PI / 180;
return Math.round(angle / step) * step;
}
const SCALE_EPSILON = 0.01;
function toClosedCoordinates(ring) {
const coords = ring.map((ll) => [ll.lng, ll.lat]);
if (coords.length === 0) {
return coords;
}
const [firstLng, firstLat] = coords[0];
const [lastLng, lastLat] = coords[coords.length - 1];
if (firstLng !== lastLng || firstLat !== lastLat) {
coords.push([firstLng, firstLat]);
}
return coords;
}
function createPolygonFeature(coordinates) {
return {
type: "Feature",
geometry: {
type: "Polygon",
coordinates: [coordinates]
},
properties: {}
};
}
function invalidResult(reason, statusText) {
return {
validity: "invalid",
reason,
submitEnabled: false,
warning: true,
statusText
};
}
function validateDonutCandidate(originalOuter, scaledOuter, scaleX, scaleY, direction = DonutDirection.Both) {
if (originalOuter.length < 3 || scaledOuter.length < 3) {
return invalidResult("invalid-rings", "Scale to create a donut");
}
if (Math.abs(scaleX - 1) < SCALE_EPSILON && Math.abs(scaleY - 1) < SCALE_EPSILON) {
return invalidResult("scale-required", "Scale to create a donut");
}
const originalCoords = toClosedCoordinates(originalOuter);
const scaledCoords = toClosedCoordinates(scaledOuter);
if (originalCoords.length < 4 || scaledCoords.length < 4) {
return invalidResult("invalid-rings", "Scale to create a donut");
}
const originalFeature = createPolygonFeature(originalCoords);
const scaledFeature = createPolygonFeature(scaledCoords);
let inward = false;
let outward = false;
try {
inward = index_default$4(scaledFeature, originalFeature);
outward = index_default$4(originalFeature, scaledFeature);
} catch {
return invalidResult("partial-overlap", "Donut requires one shape to fully contain the other");
}
if (!inward && !outward) {
return invalidResult("partial-overlap", "Donut requires one shape to fully contain the other");
}
if (outward && direction === DonutDirection.Inward) {
return invalidResult("direction-restricted", "Only inward donuts allowed");
}
const outerCoords = outward ? scaledCoords : originalCoords;
const innerCoords = outward ? originalCoords : scaledCoords;
return {
validity: outward ? "valid-outward" : "valid-inward",
reason: outward ? "nested-outward" : "nested-inward",
submitEnabled: true,
warning: false,
statusText: "Valid donut",
allowMerge: outward,
polygon: {
type: "Feature",
geometry: {
type: "Polygon",
coordinates: [outerCoords, innerCoords]
},
properties: {}
}
};
}
class PolygonTransformController {
constructor(map, featureGroup, mode = "scale", donutDirection = DonutDirection.Both, onExit) {
__publicField(this, "map");
__publicField(this, "polygon");
__publicField(this, "overlay");
__publicField(this, "state");
__publicField(this, "normalizedLatLngs");
__publicField(this, "wasMapDraggingEnabled", false);
__publicField(this, "mode");
__publicField(this, "onExit");
__publicField(this, "rotateStartAngle", null);
__publicField(this, "rotateBaseRotation", 0);
__publicField(this, "originalTouchAction", null);
__publicField(this, "mapDraggingHandler");
__publicField(this, "originalPolygonStyle");
__publicField(this, "donutDirection");
__publicField(this, "onKeyDown", (e) => {
if (e.key === "Escape") {
this.handleCancel();
}
});
__publicField(this, "updateOverlay", () => {
const scaledRings = this.state.originalPixelRings.map(
(poly) => poly.map(
(ring) => ring.map(
(pt) => scalePointAround(pt, this.state.pivot, this.state.scaleX, this.state.scaleY)
)
)
);
const bbox2 = getPixelBBox(scaledRings);
this.overlay.update(bbox2, this.state.pivot, this.state.rotation);
});
var _a2, _b2;
this.map = map;
this.mode = mode;
this.donutDirection = donutDirection;
this.onExit = onExit;
const polygon2 = featureGroup.getLayers().find((l) => l instanceof L.Polygon);
if (!polygon2) throw new Error("FeatureGroup does not contain a polygon");
this.polygon = polygon2;
this.originalPolygonStyle = {
color: this.polygon.options.color,
fillColor: this.polygon.options.fillColor,
opacity: this.polygon.options.opacity,
fillOpacity: this.polygon.options.fillOpacity
};
const originalLatLngs = this.polygon.getLatLngs();
this.normalizedLatLngs = normalizeLatLngs(originalLatLngs);
const proj = projectLatLngs(this.map, this.normalizedLatLngs);
const bbox2 = getPixelBBox(proj.rings);
const pivot = getPixelCentroid(bbox2);
this.state = {
isActive: true,
originalLatLngs,
originalPixelRings: proj.rings,
pivot,
scaleX: 1,
scaleY: 1,
rotation: 0,
uniformScale: false,
snapRotation: false,
scaleFromPivot: false
};
this.overlay = new TransformOverlay(
this.map,
{
onStartHandleDrag: (type, start, e) => this.onStartDrag(type, start, e),
onDragHandle: (type, current) => this.onDrag(type, current),
onEndHandleDrag: () => {
}
},
this.mode,
() => this.handleCancel(),
() => this.handleConfirm()
);
this.updateOverlay();
this.syncDonutValidation();
this.map.on("zoom viewreset move", this.updateOverlay, this);
this.mapDraggingHandler = this.map.dragging;
if (this.mapDraggingHandler) {
if (typeof this.mapDraggingHandler.enabled === "function") {
this.wasMapDraggingEnabled = this.mapDraggingHandler.enabled();
}
(_b2 = (_a2 = this.mapDraggingHandler).disable) == null ? void 0 : _b2.call(_a2);
}
const container = this.map.getContainer();
this.originalTouchAction = container.style.touchAction || null;
container.style.touchAction = "none";
document.addEventListener("keydown", this.onKeyDown);
}
destroy() {
document.removeEventListener("keydown", this.onKeyDown);
this.map.off("zoom viewreset move", this.updateOverlay, this);
this.restorePolygonStyle();
this.overlay.destroy();
this.state.isActive = false;
if (this.mapDraggingHandler) {
if (this.wasMapDraggingEnabled && typeof this.mapDraggingHandler.enable === "function") {
this.mapDraggingHandler.enable();
}
}
const container = this.map.getContainer();
if (this.originalTouchAction !== null) {
container.style.touchAction = this.originalTouchAction;
} else {
container.style.touchAction = "";
}
}
onStartDrag(type, start, evt) {
const isMac = navigator.userAgent.toLowerCase().includes("mac");
const ctrlOrCmd = isMac ? evt.metaKey : evt.ctrlKey;
this.state.uniformScale = !!evt.shiftKey;
this.state.scaleFromPivot = !!evt.altKey;
this.state.snapRotation = !!ctrlOrCmd;
if (type === TransformHandleType.Rotate) {
const c = this.state.scaleFromPivot ? this.state.pivot : getPixelCentroid(getPixelBBox(this.state.originalPixelRings));
const startAngle = Math.atan2(start.y - c.y, start.x - c.x);
this.rotateStartAngle = startAngle;
this.rotateBaseRotation = this.state.rotation || 0;
}
}
onDrag(type, current) {
const origBBox = getPixelBBox(this.state.originalPixelRings);
switch (type) {
case TransformHandleType.Rotate: {
const rotationCenter = this.state.scaleFromPivot ? this.state.pivot : getPixelCentroid(origBBox);
this.handleRotateDrag(current, rotationCenter);
break;
}
case TransformHandleType.Pivot:
this.handlePivotDrag(current);
break;
default: {
const scaleCenter = this.state.scaleFromPivot ? this.state.pivot : getPixelCentroid(origBBox);
this.handleScaleDrag(type, current, origBBox, scaleCenter);
break;
}
}
this.preview();
}
handleRotateDrag(current, center) {
if (this.mode !== "rotate") return;
const angle = Math.atan2(current.y - center.y, current.x - center.x);
let theta;
if (this.rotateStartAngle != null) {
const delta = angle - this.rotateStartAngle;
theta = this.rotateBaseRotation + delta;
} else {
theta = angle;
}
this.state.rotation = this.state.snapRotation ? snapAngleRadians(theta, 15) : theta;
}
handlePivotDrag(current) {
this.state.pivot = current;
}
handleScaleDrag(type, current, origBBox, center) {
if (this.mode === "rotate") return;
const width = origBBox.maxX - origBBox.minX;
const height = origBBox.maxY - origBBox.minY;
let scaleX = this.state.scaleX;
let scaleY = this.state.scaleY;
switch (type) {
case TransformHandleType.Left:
case TransformHandleType.TopLeft:
case TransformHandleType.BottomLeft:
scaleX = (center.x - current.x) / (center.x - origBBox.minX || 1);
break;
case TransformHandleType.Right:
case TransformHandleType.TopRight:
case TransformHandleType.BottomRight:
scaleX = (current.x - center.x) / (origBBox.maxX - center.x || 1);
break;
}
switch (type) {
case TransformHandleType.Top:
case TransformHandleType.TopLeft:
case TransformHandleType.TopRight:
scaleY = (center.y - current.y) / (center.y - origBBox.minY || 1);
break;
case TransformHandleType.Bottom:
case TransformHandleType.BottomLeft:
case TransformHandleType.BottomRight:
scaleY = (current.y - center.y) / (origBBox.maxY - center.y || 1);
break;
}
if (type === TransformHandleType.Left || type === TransformHandleType.Right) {
scaleY = this.state.scaleY;
}
if (type === TransformHandleType.Top || type === TransformHandleType.Bottom) {
scaleX = this.state.scaleX;
}
if (this.state.uniformScale) {
const uniform = Math.abs(width) > Math.abs(height) ? Math.sign(scaleX) * Math.abs(scaleX) : Math.sign(scaleY) * Math.abs(scaleY);
scaleX = uniform;
scaleY = uniform;
}
const hasValidScaleX = Number.isFinite(scaleX) && Math.abs(scaleX) > 1e-3;
const hasValidScaleY = Number.isFinite(scaleY) && Math.abs(scaleY) > 1e-3;
if (hasValidScaleX) {
this.state.scaleX = scaleX;
}
if (hasValidScaleY) {
this.state.scaleY = scaleY;
}
}
preview() {
var _a2, _b2;
const transformed = applyTransform(
this.state.originalPixelRings,
this.state.pivot,
this.state.scaleX,
this.state.scaleY,
this.state.rotation
);
const ll = unprojectToLatLngs(this.map, transformed, this.normalizedLatLngs);
if (this.mode === "donut") {
const originalOuter = ((_a2 = this.normalizedLatLngs[0]) == null ? void 0 : _a2[0]) ?? [];
const transformedOuter = ((_b2 = ll[0]) == null ? void 0 : _b2[0]) ?? [];
this.polygon.setLatLngs([
[originalOuter, transformedOuter]
]);
this.syncDonutValidation();
} else {
this.polygon.setLatLngs(ll);
}
this.updateOverlay();
}
apply() {
const coords = this.polygon.getLatLngs();
return normalizeLatLngs(coords);
}
cancel() {
this.restorePolygonStyle();
this.polygon.setLatLngs(this.state.originalLatLngs);
}
getMode() {
return this.mode;
}
getOriginalOuterLatLngs() {
var _a2;
return ((_a2 = this.normalizedLatLngs[0]) == null ? void 0 : _a2[0]) ?? [];
}
getScaledOuterLatLngs() {
var _a2;
const transformed = applyTransform(
this.state.originalPixelRings,
this.state.pivot,
this.state.scaleX,
this.state.scaleY,
this.state.rotation
);
const ll = unprojectToLatLngs(this.map, transformed, this.normalizedLatLngs);
return ((_a2 = ll[0]) == null ? void 0 : _a2[0]) ?? [];
}
getDonutValidation() {
return validateDonutCandidate(
this.getOriginalOuterLatLngs(),
this.getScaledOuterLatLngs(),
this.state.scaleX,
this.state.scaleY,
this.donutDirection
);
}
syncDonutValidation() {
if (this.mode !== "donut") {
return;
}
const validation = this.getDonutValidation();
const showWarning = validation.warning && validation.reason !== "scale-required";
this.overlay.setDonutValidation(validation, showWarning);
if (showWarning) {
this.polygon.setStyle({
color: "#d97706",
fillColor: "#f59e0b",
opacity: Math.max(this.originalPolygonStyle.opacity ?? 0, 0.95),
fillOpacity: Math.max(this.originalPolygonStyle.fillOpacity ?? 0, 0.2)
});
return;
}
this.restorePolygonStyle();
}
restorePolygonStyle() {
if (this.mode !== "donut") {
return;
}
this.polygon.setStyle({
color: this.originalPolygonStyle.color,
fillColor: this.originalPolygonStyle.fillColor,
opacity: this.originalPolygonStyle.opacity,
fillOpacity: this.originalPolygonStyle.fillOpacity
});
}
handleCancel() {
this.cancel();
if (this.onExit) this.onExit(false);
this.destroy();
}
handleConfirm() {
if (this.onExit) this.onExit(true);
this.destroy();
}
}
class PopupFactory {
/**
* Creates a menu button element.
* @param id Unique identifier for the button.
* @param title Tooltip text.
* @param cssClasses Additional CSS classes to apply.
* @returns HTMLDivElement representing the menu button.
*/
static createMenuButton(id, title, cssClasses) {
const button = document.createElement("div");
button.classList.add("marker-menu-button", ...cssClasses);
button.title = title;
button.setAttribute("aria-label", title);
button.setAttribute("data-action-id", id);
return button;
}
/**
* Builds a menu popup container structure.
* @param buttons Array of button elements to include.
* @returns HTMLDivElement containing the complete menu structure.
*/
static buildMenuPopup(buttons) {
const outerWrapper = document.createElement("div");
outerWrapper.classList.add("alter-marker-outer-wrapper");
const wrapper = document.createElement("div");
wrapper.classList.add("alter-marker-wrapper");
const closeButton = document.createElement("button");
closeButton.type = "button";
closeButton.className = "marker-menu-close";
closeButton.setAttribute("aria-label", "Close menu");
closeButton.innerHTML = "×";
wrapper.appendChild(closeButton);
const markerContent = document.createElement("div");
markerContent.classList.add("content");
const markerContentWrapper = document.createElement("div");
markerContentWrapper.classList.add("marker-menu-content");
buttons.forEach((button) => {
markerContentWrapper.appendChild(button);
});
markerContent.appendChild(markerContentWrapper);
wrapper.appendChild(markerContent);
outerWrapper.appendChild(wrapper);
return outerWrapper;
}
/**
* Builds an info popup container structure.
* @param content HTML element to display inside the popup.
* @returns HTMLDivElement containing the complete info popup structure.
*/
static buildInfoPopup(content) {
const outerWrapper = document.createElement("div");
outerWrapper.classList.add("info-marker-outer-wrapper");
const wrapper = document.createElement("div");
wrapper.classList.add("info-marker-wrapper");
const markerContent = document.createElement("div");
markerContent.classList.add("content");
markerContent.appendChild(content);
wrapper.appendChild(markerContent);
outerWrapper.appendChild(wrapper);
return outerWrapper;
}
}
const BUILT_IN_POLYGON_MENU_ACTION_IDS = /* @__PURE__ */ new Set([
"simplify",
"doubleElbows",
"bbox",
"bezier",
"scale",
"rotate",
"donut",
"toggleOptimization"
]);
class PolygonInteractionManager {
constructor(dependencies, featureGroupAccess) {
__publicField(this, "markerFeatureGroupMap", /* @__PURE__ */ new WeakMap());
__publicField(this, "polygonFeatureGroupMap", /* @__PURE__ */ new WeakMap());
__publicField(this, "markerModifierHandlers", /* @__PURE__ */ new WeakMap());
__publicField(this, "markerTouchListeners", /* @__PURE__ */ new WeakMap());
__publicField(this, "markerEdgeDeletionListeners", /* @__PURE__ */ new WeakMap());
__publicField(this, "_activeMarker", null);
__publicField(this, "isDraggingMarker", false);
__publicField(this, "turfHelper");
__publicField(this, "polygonInformation");
__publicField(this, "map");
__publicField(this, "config");
__publicField(this, "modeManager");
__publicField(this, "eventManager");
__publicField(this, "saveHistoryState");
__publicField(this, "layerManager");
// Polygon drag state
__publicField(this, "currentDragPolygon", null);
__publicField(this, "currentDragIsClone", false);
__publicField(this, "currentModifierDragMode", false);
__publicField(this, "isModifierKeyHeld", false);
__publicField(this, "currentCloneGhost", null);
__publicField(this, "dragCancelHandlersAttached", false);
__publicField(this, "_boundDragTouchMove", null);
__publicField(this, "_boundDragTouchEnd", null);
__publicField(this, "_boundDragMoveListener", null);
__publicField(this, "_boundDragEndListener", null);
__publicField(this, "dragUsesPointerEvents", false);
__publicField(this, "polygonTouchStartListeners", /* @__PURE__ */ new WeakMap());
__publicField(this, "polygonNativeStartListeners", /* @__PURE__ */ new WeakMap());
__publicField(this, "_openMenuPopup", null);
__publicField(this, "transformModeActive", false);
__publicField(this, "transformControllers", /* @__PURE__ */ new WeakMap());
__publicField(this, "transformAuxiliaryDisplayValues", /* @__PURE__ */ new WeakMap());
__publicField(this, "deleteMarkerSuppressUntil", 0);
__publicField(this, "onModeChange", () => {
if (this.currentCloneGhost || this.isPolygonDragActive()) {
this.cancelActivePolygonDrag();
}
});
// Read-only access to feature groups
__publicField(this, "getFeatureGroups");
__publicField(this, "removeFeatureGroup");
// Polygon dragging methods
__publicField(this, "onPolygonMouseMove", (e) => {
if (!this.currentDragPolygon || !this.currentDragPolygon._polydrawDragData || !this.currentDragPolygon._polydrawDragData.isDragging)
return;
const polygon2 = this.currentDragPolygon;
const dragData = polygon2._polydrawDragData;
if (!this.currentDragIsClone) {
const eventToCheck = e.originalEvent && "metaKey" in e.originalEvent ? e.originalEvent : e;
const currentModifierState = this.detectDragSubtractModifierKey(eventToCheck);
if (currentModifierState !== this.currentModifierDragMode) {
this.handleModifierToggleDuringDrag(eventToCheck);
}
}
const startPos = dragData.startPosition;
const currentPos = e.latlng;
const offsetLat = currentPos.lat - startPos.lat;
const offsetLng = currentPos.lng - startPos.lng;
const newLatLngs = this.offsetPolygonCoordinates(dragData.startLatLngs, offsetLat, offsetLng);
polygon2.setLatLngs(newLatLngs);
this.updateMarkersAndHoleLinesDuringDrag(polygon2, offsetLat, offsetLng);
});
__publicField(this, "onPolygonMouseUp", (e) => {
if (!this.currentDragPolygon || !this.currentDragPolygon._polydrawDragData || !this.currentDragPolygon._polydrawDragData.isDragging)
return;
const polygon2 = this.currentDragPolygon;
const dragData = polygon2._polydrawDragData;
dragData.isDragging = false;
if (this.map.dragging) {
this.map.dragging.enable();
}
if (polygon2._polydrawDragData && polygon2._polydrawDragData.originalOpacity != null) {
polygon2.setStyle({ fillOpacity: polygon2._polydrawDragData.originalOpacity });
}
this.setMarkerVisibility(polygon2, true);
try {
const container = this.map.getContainer();
container.style.cursor = "";
} catch {
}
this.clearCloneGhost();
this.detachDragTouchListeners();
this.detachDragDocumentListeners();
this.detachDragCancelHandlers();
const wasCloneDrag = this.currentDragIsClone;
this.currentDragIsClone = false;
if (wasCloneDrag) {
this.updatePolygonAfterCloneDrag(polygon2, dragData);
} else {
this.updatePolygonAfterDrag(polygon2);
}
if (polygon2._polydrawOriginalMarkerPositions) {
polygon2._polydrawOriginalMarkerPositions.clear();
delete polygon2._polydrawOriginalMarkerPositions;
}
if (polygon2._polydrawOriginalHoleLinePositions) {
polygon2._polydrawOriginalHoleLinePositions.clear();
delete polygon2._polydrawOriginalHoleLinePositions;
}
if (polygon2._polydrawCurrentDragSession) {
delete polygon2._polydrawCurrentDragSession;
}
this.currentDragPolygon = null;
});
__publicField(this, "onPolygonDragCancel", () => {
this.cancelActivePolygonDrag();
});
__publicField(this, "onPolygonDragKeyDown", (e) => {
if (e.key !== "Escape") return;
this.cancelActivePolygonDrag();
});
__publicField(this, "onDragTouchMove", (e) => {
e.preventDefault();
const latlng = EventAdapter.extractCoordinates(
e,
this.map
);
if (!latlng) return;
this.onPolygonMouseMove({ latlng });
});
__publicField(this, "onDragTouchEnd", (e) => {
e.preventDefault();
this.onPolygonMouseUp({ latlng: null });
});
__publicField(this, "onDragDocumentMove", (event) => {
const latlng = EventAdapter.extractCoordinates(
event,
this.map
);
if (!latlng) {
return;
}
this.onPolygonMouseMove({ latlng, originalEvent: event });
});
__publicField(this, "onDragDocumentEnd", (event) => {
const latlng = EventAdapter.extractCoordinates(event, this.map) ?? null;
this.onPolygonMouseUp({ latlng, originalEvent: event });
});
__publicField(this, "onMarkerHoverForEdgeDeletionEvent", (e) => {
if (!this.isModifierKeyHeld) return;
const element = e.target;
if (element) {
element.style.backgroundColor = this.config.styles.ui.edgeDeletion.color;
element.style.borderColor = this.config.styles.ui.edgeDeletion.color;
element.classList.add("edge-deletion-hover");
}
});
__publicField(this, "onMarkerLeaveForEdgeDeletionEvent", (e) => {
const element = e.target;
if (element) {
element.style.backgroundColor = "";
element.style.borderColor = "";
element.classList.remove("edge-deletion-hover");
}
});
this.turfHelper = dependencies.turfHelper;
this.polygonInformation = dependencies.polygonInformation;
this.map = dependencies.map;
this.config = dependencies.config;
this.modeManager = dependencies.modeManager;
this.eventManager = dependencies.eventManager;
this.saveHistoryState = dependencies.saveHistoryState;
this.layerManager = dependencies.layerManager;
this.getFeatureGroups = featureGroupAccess.getFeatureGroups;
this.removeFeatureGroup = featureGroupAccess.removeFeatureGroup;
this.eventManager.on("polydraw:mode:change", this.onModeChange);
}
/**
* Strongly typed helper to emit polygon updated events without casts.
* Also saves history state before emitting (except for drag operations which save on dragstart).
*/
emitPolygonUpdated(data) {
const historyAction = this.getHistoryActionForOperation(data.operation);
if (historyAction && this.saveHistoryState) {
this.saveHistoryState(historyAction);
}
this.eventManager.emit("polydraw:polygon:updated", data);
}
getHistoryActionForOperation(operation2) {
switch (operation2) {
case "addVertex":
case "removeVertex":
case "toggleOptimization":
return operation2;
case "modifierSubtract":
case "modifierSubtractFallback":
return "modifierSubtract";
default:
return null;
}
}
/**
* True when the polygon in the feature group already has holes, or is a MultiPolygon
* with more than one outer ring (donut mode only supports simple single-ring polygons).
*/
donutTargetHasHoles(featureGroup) {
const polygonLayer = featureGroup.getLayers().find((l) => l instanceof L.Polygon);
if (!polygonLayer) return true;
const latLngs = polygonLayer.getLatLngs();
if (!Array.isArray(latLngs) || latLngs.length === 0) return true;
if (!Array.isArray(latLngs[0])) return false;
const firstLevel = latLngs;
if (!Array.isArray(firstLevel[0][0])) {
return firstLevel.length > 1;
}
return true;
}
/**
* Commit handler for donut mode. Decides inward vs outward via containment, assembles a
* polygon-with-hole, and emits the updated event. On partial overlap / degenerate scale
* the polygon is restored unchanged.
*/
handleDonutCommit(controller, featureGroup, polygonLayer, optimizationLevel, originalOptimizationLevel) {
const originalOuter = controller.getOriginalOuterLatLngs();
const donutValidation = controller.getDonutValidation();
const restore = () => {
polygonLayer.setLatLngs([originalOuter]);
this.setMarkerVisibility(polygonLayer, true);
};
if (!donutValidation.submitEnabled || !donutValidation.polygon) {
restore();
return;
}
polygonLayer.setLatLngs([originalOuter]);
if (this.saveHistoryState) {
this.saveHistoryState("donut");
}
this.cleanupFeatureGroup(featureGroup);
this.removeFeatureGroup(featureGroup);
this.emitPolygonUpdated({
operation: "donut",
polygon: donutValidation.polygon,
allowMerge: donutValidation.allowMerge,
optimizationLevel,
originalOptimizationLevel
});
}
dispose() {
this.eventManager.off("polydraw:mode:change", this.onModeChange);
this.cancelActivePolygonDrag();
}
getTimestamp() {
if (typeof performance !== "undefined" && typeof performance.now === "function") {
return performance.now();
}
return Date.now();
}
shouldSuppressDeleteMarkerClick() {
return this.getTimestamp() < this.deleteMarkerSuppressUntil;
}
suppressDeleteMarkerClicks(durationMs) {
this.deleteMarkerSuppressUntil = this.getTimestamp() + Math.max(0, durationMs);
}
/**
* Add markers to a polygon feature group
*/
addMarkers(latlngs, featureGroup, options = {}) {
this.registerPolygonLayerMappings(featureGroup);
let menuMarkerIdx = this.getMarkerIndex(latlngs, this.config.markers.markerMenuIcon.position);
let deleteMarkerIdx = this.getMarkerIndex(
latlngs,
this.config.markers.markerDeleteIcon.position
);
let infoMarkerIdx = this.getMarkerIndex(latlngs, this.config.markers.markerInfoIcon.position);
const separatedIndices = this.ensureMarkerSeparation(latlngs.length, {
menu: { index: menuMarkerIdx, enabled: this.config.markers.menuMarker },
delete: { index: deleteMarkerIdx, enabled: this.config.markers.deleteMarker },
info: { index: infoMarkerIdx, enabled: this.config.markers.infoMarker }
});
menuMarkerIdx = separatedIndices.menu;
deleteMarkerIdx = separatedIndices.delete;
infoMarkerIdx = separatedIndices.info;
const optimizationLevel = options.optimizationLevel ?? 0;
const importantIndices = this.deriveImportantMarkerIndices(latlngs, {
menuIndex: menuMarkerIdx,
deleteIndex: deleteMarkerIdx,
infoIndex: infoMarkerIdx,
optimizationLevel
});
const hasClosingPoint = latlngs.length > 2 && this.latLngEquals(latlngs[0], latlngs[latlngs.length - 1]);
latlngs.forEach((latlng, i) => {
let iconClasses = this.config.markers.markerIcon.styleClasses;
if (i === menuMarkerIdx && this.config.markers.menuMarker) {
iconClasses = this.config.markers.markerMenuIcon.styleClasses;
}
if (i === deleteMarkerIdx && this.config.markers.deleteMarker) {
iconClasses = this.config.markers.markerDeleteIcon.styleClasses;
}
if (i === infoMarkerIdx && this.config.markers.infoMarker) {
iconClasses = this.config.markers.markerInfoIcon.styleClasses;
}
const processedClasses = Array.isArray(iconClasses) ? iconClasses : [iconClasses];
const isSpecialMarker = i === menuMarkerIdx && this.config.markers.menuMarker || i === deleteMarkerIdx && this.config.markers.deleteMarker || i === infoMarkerIdx && this.config.markers.infoMarker;
const normalizedIndex = this.normalizeMarkerIndex(i, latlngs.length, hasClosingPoint);
const isImportant = optimizationLevel <= 0 || normalizedIndex === null || importantIndices.has(normalizedIndex) || isSpecialMarker;
const classesForIcon = [...processedClasses];
if (!isImportant) {
classesForIcon.push("polygon-marker-faded");
}
const marker = new L.Marker(latlng, {
icon: this.createDivIcon(classesForIcon),
draggable: this.config.modes.dragElbow,
title: this.config.markers.coordsTitle ? this.getLatLngInfoString(latlng) : "",
zIndexOffset: this.config.markers.markerIcon.zIndexOffset ?? this.config.markers.zIndexOffset
});
featureGroup.addLayer(marker);
this.markerFeatureGroupMap.set(marker, featureGroup);
marker.on("add", () => {
const el2 = marker.getElement();
if (el2) el2.style.pointerEvents = "auto";
});
marker.on("click", (e) => {
var _a2, _b2;
if (this.isDraggingMarker) {
(_b2 = (_a2 = e.originalEvent) == null ? void 0 : _a2.stopPropagation) == null ? void 0 : _b2.call(_a2);
L.DomEvent.stopPropagation(e);
}
});
marker.on("dragstart", () => {
const fg = this.markerFeatureGroupMap.get(marker);
if (fg && !this.isFeatureGroupInActiveLayer(fg)) {
if (marker.dragging) {
marker.dragging.disable();
}
return;
}
if (this.saveHistoryState) {
this.saveHistoryState("markerDrag");
}
this.isDraggingMarker = true;
this._activeMarker = marker;
});
marker.on("dragend", (e) => {
const fg = this.markerFeatureGroupMap.get(marker);
if (fg && !this.isFeatureGroupInActiveLayer(fg)) {
this._activeMarker = null;
this.isDraggingMarker = false;
return;
}
if (this.modeManager.canPerformAction("markerDrag") && fg) {
this.markerDragEnd(fg);
}
this._activeMarker = null;
L.DomEvent.stopPropagation(e);
setTimeout(() => {
this.isDraggingMarker = false;
}, 10);
});
const el = marker.getElement();
if (el) {
const touchstartHandler = (e) => {
e.stopPropagation();
};
const touchendHandler = (e) => {
e.preventDefault();
e.stopPropagation();
marker.fire("click");
if (this.isDraggingMarker && !isSpecialMarker) {
const fg = this.markerFeatureGroupMap.get(marker);
if (this.modeManager.canPerformAction("markerDrag") && fg && this.isFeatureGroupInActiveLayer(fg)) {
this.markerDragEnd(fg);
}
}
this._activeMarker = null;
};
el.addEventListener("touchstart", touchstartHandler, { passive: true });
el.addEventListener("touchend", touchendHandler);
this.markerTouchListeners.set(marker, {
touchstart: touchstartHandler,
touchend: touchendHandler
});
}
if (i === menuMarkerIdx || i === deleteMarkerIdx || i === infoMarkerIdx) {
const element = marker.getElement();
if (element) {
element.style.zIndex = "10000";
}
}
if (this.config.modes.dragElbow) {
const createDragHandler = (fg) => () => {
if (this.modeManager.canPerformAction("markerDrag") && this.isFeatureGroupInActiveLayer(fg)) {
this.markerDrag(fg);
}
};
marker.on("drag", createDragHandler(featureGroup));
}
if (i === menuMarkerIdx && this.config.markers.menuMarker) {
marker.options.zIndexOffset = this.config.markers.markerMenuIcon.zIndexOffset ?? this.config.markers.zIndexOffset;
marker.on("click", () => {
if (!this.isFeatureGroupInActiveLayer(featureGroup)) return;
const polygonGeoJSON = this.getPolygonGeoJSONFromFeatureGroup(featureGroup);
const centerOfMass2 = PolygonUtil.getCenterOfPolygonByIndexWithOffsetFromCenterOfMass(
polygonGeoJSON,
menuMarkerIdx
);
const menuPopup = this.generateMenuMarkerPopup(latlngs, featureGroup);
menuPopup.setLatLng(centerOfMass2).openOn(this.map);
});
marker.on("popupopen", (e) => {
const popup = e.popup;
const popupContent = popup.getElement();
if (!popupContent) return;
setTimeout(() => {
const mapContainer = this.map.getContainer();
const popupBounds = popupContent.getBoundingClientRect();
const mapBounds = mapContainer.getBoundingClientRect();
let adjustX = 0;
let adjustY = 0;
if (popupBounds.left < mapBounds.left) {
adjustX = mapBounds.left - popupBounds.left;
} else if (popupBounds.right > mapBounds.right) {
adjustX = mapBounds.right - popupBounds.right;
}
if (popupBounds.top < mapBounds.top) {
adjustY = mapBounds.top - popupBounds.top;
} else if (popupBounds.bottom > mapBounds.bottom) {
adjustY = mapBounds.bottom - popupBounds.bottom;
}
if (adjustX !== 0 || adjustY !== 0) {
popupContent.style.transform = `translate(${adjustX}px, ${adjustY}px)`;
}
}, 0);
const container2 = this.map.getContainer();
if (container2) {
container2.style.touchAction = "manipulation";
}
});
marker.on("popupclose", () => {
const container2 = this.map.getContainer();
if (container2) {
container2.style.touchAction = "";
}
});
}
if (i === infoMarkerIdx && this.config.markers.infoMarker) {
const polygonGeoJSON = this.getPolygonGeoJSONFromFeatureGroup(featureGroup);
const area2 = this.turfHelper.getPolygonArea(polygonGeoJSON);
const perimeter = this.getTotalPolygonPerimeter(polygonGeoJSON);
marker.options.zIndexOffset = this.config.markers.markerInfoIcon.zIndexOffset ?? this.config.markers.zIndexOffset;
marker.on("click", () => {
if (!this.isFeatureGroupInActiveLayer(featureGroup)) return;
const infoPopup = this.generateInfoMarkerPopup(area2, perimeter);
const centerOfMass2 = PolygonUtil.getCenterOfPolygonByIndexWithOffsetFromCenterOfMass(
polygonGeoJSON,
infoMarkerIdx
);
infoPopup.setLatLng(centerOfMass2).openOn(this.map);
});
marker.on("popupopen", (e) => {
const popup = e.popup;
const popupContent = popup.getElement();
if (!popupContent) return;
setTimeout(() => {
const mapContainer = this.map.getContainer();
const mapBounds = mapContainer.getBoundingClientRect();
const popupBounds = popupContent.getBoundingClientRect();
if (popupBounds.left < mapBounds.left) {
popupContent.style.transform = `translateX(${mapBounds.left - popupBounds.left}px)`;
} else if (popupBounds.right > mapBounds.right) {
popupContent.style.transform = `translateX(${mapBounds.right - popupBounds.right}px)`;
}
}, 0);
const container2 = this.map.getContainer();
if (container2) {
container2.style.touchAction = "manipulation";
}
});
marker.on("popupclose", () => {
const container2 = this.map.getContainer();
if (container2) {
container2.style.touchAction = "";
}
});
}
marker.on("mousedown", (e) => {
if (!this.modeManager.isInOffMode()) {
L.DomEvent.stopPropagation(e);
this.map.fire("mousedown", e);
}
this._activeMarker = marker;
});
marker.on("click", (e) => {
var _a2, _b2;
if (!this.isFeatureGroupInActiveLayer(featureGroup)) {
return;
}
if (this.modeManager.isInOffMode()) {
if (this.isEdgeDeletionModifierKeyPressed(e.originalEvent)) {
const polygonLayer = featureGroup.getLayers().find((layer) => layer instanceof L.Polygon);
if (polygonLayer) {
this.elbowClicked(e, polygonLayer, marker.getLatLng());
}
} else {
if (i === deleteMarkerIdx && this.config.markers.deleteMarker) {
if (this.shouldSuppressDeleteMarkerClick()) {
const original = e.originalEvent;
(_a2 = original == null ? void 0 : original.preventDefault) == null ? void 0 : _a2.call(original);
(_b2 = original == null ? void 0 : original.stopPropagation) == null ? void 0 : _b2.call(original);
return;
}
this.map.closePopup();
if (this.saveHistoryState) {
this.saveHistoryState("deletePolygon");
}
this.cleanupFeatureGroup(featureGroup);
this.removeFeatureGroup(featureGroup);
this.polygonInformation.createPolygonInformationStorage(this.getFeatureGroups());
this.eventManager.emit("polydraw:polygon:deleted", void 0);
}
}
}
});
marker.on("mouseover", () => {
if (!this.isFeatureGroupInActiveLayer(featureGroup)) return;
this.onMarkerHoverForEdgeDeletion(marker, true);
});
marker.on("mouseout", () => {
this.onMarkerHoverForEdgeDeletion(marker, false);
});
});
const container = this.map.getContainer();
if (container) {
container.style.touchAction = "manipulation";
}
}
/**
* Add hole markers to a polygon feature group, with configurable special markers.
*/
addHoleMarkers(latlngs, featureGroup) {
var _a2, _b2, _c;
this.registerPolygonLayerMappings(featureGroup);
const holeMenuEnabled = ((_a2 = this.config.markers.holeMarkers) == null ? void 0 : _a2.menuMarker) ?? false;
const holeDeleteEnabled = ((_b2 = this.config.markers.holeMarkers) == null ? void 0 : _b2.deleteMarker) ?? false;
const holeInfoEnabled = ((_c = this.config.markers.holeMarkers) == null ? void 0 : _c.infoMarker) ?? false;
let menuMarkerIdx = this.getMarkerIndex(latlngs, this.config.markers.markerMenuIcon.position);
let deleteMarkerIdx = this.getMarkerIndex(
latlngs,
this.config.markers.markerDeleteIcon.position
);
let infoMarkerIdx = this.getMarkerIndex(latlngs, this.config.markers.markerInfoIcon.position);
const separatedIndices = this.ensureMarkerSeparation(latlngs.length, {
menu: { index: menuMarkerIdx, enabled: holeMenuEnabled },
delete: { index: deleteMarkerIdx, enabled: holeDeleteEnabled },
info: { index: infoMarkerIdx, enabled: holeInfoEnabled }
});
menuMarkerIdx = separatedIndices.menu;
deleteMarkerIdx = separatedIndices.delete;
infoMarkerIdx = separatedIndices.info;
latlngs.forEach((latlng, i) => {
let iconClasses = this.config.markers.holeIcon.styleClasses;
if (i === menuMarkerIdx && holeMenuEnabled) {
iconClasses = this.config.markers.markerMenuIcon.styleClasses;
}
if (i === deleteMarkerIdx && holeDeleteEnabled) {
iconClasses = this.config.markers.markerDeleteIcon.styleClasses;
}
if (i === infoMarkerIdx && holeInfoEnabled) {
iconClasses = this.config.markers.markerInfoIcon.styleClasses;
}
const processedClasses = Array.isArray(iconClasses) ? iconClasses : [iconClasses];
const isSpecialMarker = i === menuMarkerIdx && holeMenuEnabled || i === deleteMarkerIdx && holeDeleteEnabled || i === infoMarkerIdx && holeInfoEnabled;
const marker = new L.Marker(latlng, {
icon: this.createDivIcon(processedClasses),
draggable: this.config.modes.dragElbow,
title: this.config.markers.coordsTitle ? this.getLatLngInfoString(latlng) : "",
zIndexOffset: this.config.markers.holeIcon.zIndexOffset ?? this.config.markers.zIndexOffset
});
featureGroup.addLayer(marker);
this.markerFeatureGroupMap.set(marker, featureGroup);
marker.on("add", () => {
const el2 = marker.getElement();
if (el2) el2.style.pointerEvents = "auto";
});
marker.on("click", (e) => {
var _a3, _b3;
if (this.isDraggingMarker) {
(_b3 = (_a3 = e.originalEvent) == null ? void 0 : _a3.stopPropagation) == null ? void 0 : _b3.call(_a3);
L.DomEvent.stopPropagation(e);
}
});
marker.on("dragstart", () => {
const fg = this.markerFeatureGroupMap.get(marker);
if (fg && !this.isFeatureGroupInActiveLayer(fg)) {
if (marker.dragging) {
marker.dragging.disable();
}
return;
}
if (this.saveHistoryState) {
this.saveHistoryState("markerDrag");
}
this.isDraggingMarker = true;
this._activeMarker = marker;
});
marker.on("dragend", (e) => {
const fg = this.markerFeatureGroupMap.get(marker);
if (fg && !this.isFeatureGroupInActiveLayer(fg)) {
this._activeMarker = null;
this.isDraggingMarker = false;
return;
}
if (this.modeManager.canPerformAction("markerDrag") && fg) {
this.markerDragEnd(fg);
}
this._activeMarker = null;
L.DomEvent.stopPropagation(e);
setTimeout(() => {
this.isDraggingMarker = false;
}, 10);
});
const el = marker.getElement();
if (el) {
const touchstartHandler = (e) => {
e.stopPropagation();
};
const touchendHandler = (e) => {
e.preventDefault();
e.stopPropagation();
marker.fire("click");
if (this.isDraggingMarker && !isSpecialMarker) {
const fg = this.markerFeatureGroupMap.get(marker);
if (this.modeManager.canPerformAction("markerDrag") && fg && this.isFeatureGroupInActiveLayer(fg)) {
this.markerDragEnd(fg);
}
}
this._activeMarker = null;
};
el.addEventListener("touchstart", touchstartHandler, { passive: true });
el.addEventListener("touchend", touchendHandler);
this.markerTouchListeners.set(marker, {
touchstart: touchstartHandler,
touchend: touchendHandler
});
}
if (i === menuMarkerIdx || i === deleteMarkerIdx || i === infoMarkerIdx) {
const element = marker.getElement();
if (element) {
element.style.zIndex = "10000";
}
}
if (this.config.modes.dragElbow) {
const createDragHandler = (fg) => () => {
if (this.modeManager.canPerformAction("markerDrag") && this.isFeatureGroupInActiveLayer(fg)) {
this.markerDrag(fg);
}
};
marker.on("drag", createDragHandler(featureGroup));
}
if (i === menuMarkerIdx && holeMenuEnabled) {
marker.options.zIndexOffset = this.config.markers.markerMenuIcon.zIndexOffset ?? this.config.markers.zIndexOffset;
marker.on("click", () => {
const ring = latlngs.map((pt) => [pt.lng, pt.lat]);
ring.push(ring[0]);
const holePolygon = {
type: "Feature",
properties: {},
geometry: {
type: "Polygon",
coordinates: [ring]
}
};
const centerOfMass2 = PolygonUtil.getCenterOfMass(holePolygon);
const menuPopup = this.generateMenuMarkerPopup(latlngs, featureGroup);
menuPopup.setLatLng(centerOfMass2).openOn(this.map);
});
marker.on("popupopen", (e) => {
const popup = e.popup;
const popupContent = popup.getElement();
if (!popupContent) return;
setTimeout(() => {
const mapContainer = this.map.getContainer();
const mapBounds = mapContainer.getBoundingClientRect();
const popupBounds = popupContent.getBoundingClientRect();
if (popupBounds.left < mapBounds.left) {
popupContent.style.transform = `translateX(${mapBounds.left - popupBounds.left}px)`;
} else if (popupBounds.right > mapBounds.right) {
popupContent.style.transform = `translateX(${mapBounds.right - popupBounds.right}px)`;
}
}, 0);
const container = this.map.getContainer();
if (container) {
container.style.touchAction = "manipulation";
}
});
marker.on("popupclose", () => {
const container = this.map.getContainer();
if (container) {
container.style.touchAction = "";
}
});
}
if (i === infoMarkerIdx && holeInfoEnabled) {
const ring = latlngs.map((latlng2) => [latlng2.lng, latlng2.lat]);
ring.push(ring[0]);
const holePolygon = {
type: "Feature",
properties: {},
geometry: {
type: "Polygon",
coordinates: [ring]
}
};
const area2 = this.turfHelper.getPolygonArea(holePolygon);
const perimeter = this.turfHelper.getPolygonPerimeter(holePolygon) * 1e3;
marker.options.zIndexOffset = this.config.markers.markerInfoIcon.zIndexOffset ?? this.config.markers.zIndexOffset;
marker.on("click", () => {
if (!this.isFeatureGroupInActiveLayer(featureGroup)) return;
const infoPopup = this.generateInfoMarkerPopup(area2, perimeter);
infoPopup.setLatLng(latlng).openOn(this.map);
});
}
marker.on("mousedown", (e) => {
if (!this.modeManager.isInOffMode()) {
L.DomEvent.stopPropagation(e);
this.map.fire("mousedown", e);
}
this._activeMarker = marker;
});
marker.on("click", (e) => {
if (!this.isFeatureGroupInActiveLayer(featureGroup)) return;
if (this.modeManager.isInOffMode()) {
if (this.isEdgeDeletionModifierKeyPressed(e.originalEvent)) {
const polygonLayer = featureGroup.getLayers().find((layer) => layer instanceof L.Polygon);
if (polygonLayer) {
this.elbowClicked(e, polygonLayer, marker.getLatLng());
}
} else {
if (i === deleteMarkerIdx && holeDeleteEnabled) {
this.map.closePopup();
const parentPolygon = featureGroup.getLayers().find((layer) => layer instanceof L.Polygon);
if (parentPolygon) {
const rawLatLngs = parentPolygon.getLatLngs();
let rings = [];
if (Array.isArray(rawLatLngs) && rawLatLngs.length > 0) {
if (Array.isArray(rawLatLngs[0])) {
const first = rawLatLngs[0];
if (Array.isArray(first) && Array.isArray(first[0])) {
rings = rawLatLngs[0];
} else {
rings = rawLatLngs;
}
} else {
rings = [rawLatLngs];
}
}
const target = marker.getLatLng();
let holeIndex = -1;
for (let r = 1; r < rings.length; r++) {
if (rings[r].some((p) => p.lat === target.lat && p.lng === target.lng)) {
holeIndex = r;
break;
}
}
if (holeIndex > 0) {
const newRings = rings.filter((_, idx) => idx !== holeIndex);
const coords = newRings.map((ring) => {
const arr = ring.map((ll) => [ll.lng, ll.lat]);
if (arr.length > 0) {
const firstPt = arr[0];
const lastPt = arr[arr.length - 1];
if (firstPt[0] !== lastPt[0] || firstPt[1] !== lastPt[1]) {
arr.push([firstPt[0], firstPt[1]]);
}
}
return arr;
});
const { level: optimizationLevel, original: originalOptimizationLevel } = this.getOptimizationMetadataFromFeatureGroup(featureGroup);
const featureMetadataState = this.getFeatureMetadataState(featureGroup);
const newPolygon = this.turfHelper.getMultiPolygon([coords]);
if (this.saveHistoryState) {
this.saveHistoryState("removeHole");
}
this.cleanupFeatureGroup(featureGroup);
this.removeFeatureGroup(featureGroup);
this.emitPolygonUpdated({
operation: "removeHole",
polygon: newPolygon,
optimizationLevel,
originalOptimizationLevel,
featureId: featureMetadataState.featureId,
featureMetadata: featureMetadataState.metadata,
sourceFeatureIds: featureMetadataState.sourceFeatureIds,
featureInteractionOverride: featureMetadataState.interactionOverride,
featureStyleOverrides: featureMetadataState.styleOverrides,
featureCreatedAt: featureMetadataState.createdAt
});
}
}
}
}
}
});
marker.on("mouseover", () => {
if (!this.isFeatureGroupInActiveLayer(featureGroup)) return;
this.onMarkerHoverForEdgeDeletion(marker, true);
});
marker.on("mouseout", () => {
this.onMarkerHoverForEdgeDeletion(marker, false);
});
});
}
/**
* Add edge click listeners to a polygon
*/
addEdgeClickListeners(polygon2, featureGroup) {
const rawLatLngs = polygon2.getLatLngs();
let processedRings;
if (Array.isArray(rawLatLngs) && rawLatLngs.length > 0) {
if (Array.isArray(rawLatLngs[0])) {
if (Array.isArray(rawLatLngs[0][0]) && rawLatLngs[0][0].length > 0) {
const firstCoord = rawLatLngs[0][0][0];
if (firstCoord && typeof firstCoord === "object" && "lat" in firstCoord) {
processedRings = rawLatLngs[0];
} else {
processedRings = rawLatLngs[0];
}
} else if (rawLatLngs[0][0] && typeof rawLatLngs[0][0] === "object" && "lat" in rawLatLngs[0][0]) {
processedRings = rawLatLngs;
} else {
processedRings = rawLatLngs[0];
}
} else if (rawLatLngs[0] && typeof rawLatLngs[0] === "object" && "lat" in rawLatLngs[0]) {
processedRings = [rawLatLngs];
} else {
processedRings = [rawLatLngs];
}
} else {
return;
}
processedRings.forEach((ring, ringIndex) => {
for (let i = 0; i < ring.length; i++) {
const edgeStart = ring[i];
const edgeEnd = ring[(i + 1) % ring.length];
if (edgeStart.lat === edgeEnd.lat && edgeStart.lng === edgeEnd.lng) {
continue;
}
const edgePolyline = leafletAdapter.createPolyline(
[edgeStart, edgeEnd],
{
color: "transparent",
weight: 10,
opacity: 0,
interactive: true
}
);
edgePolyline._polydrawAuxiliaryLayer = true;
edgePolyline._polydrawEdgeInfo = {
ringIndex,
edgeIndex: i,
startPoint: edgeStart,
endPoint: edgeEnd,
parentPolygon: polygon2,
parentFeatureGroup: featureGroup
};
edgePolyline.on("click", (e) => {
this.onEdgeClick(e, edgePolyline);
});
edgePolyline.on("mouseover", () => {
this.highlightEdgeOnHover(edgePolyline, true);
});
edgePolyline.on("mouseout", () => {
this.highlightEdgeOnHover(edgePolyline, false);
});
featureGroup.addLayer(edgePolyline);
}
});
}
/**
* Check whether polygon dragging is allowed for the current mode.
*/
isPolygonDragModeActive() {
const mode = this.modeManager.getCurrentMode();
return mode === DrawMode.Off || mode === DrawMode.Clone;
}
isPolygonDragActive() {
return !!(this.currentDragPolygon && this.currentDragPolygon._polydrawDragData && this.currentDragPolygon._polydrawDragData.isDragging);
}
/**
* Check if a polygon belongs to the active layer.
* Returns true if no layer manager exists (backward compat) or if in active editable layer.
*/
isPolygonInActiveLayer(polygon2) {
if (!this.layerManager) return true;
const mappedFeatureGroup = this.polygonFeatureGroupMap.get(polygon2);
if (mappedFeatureGroup) {
return this.layerManager.isInActiveLayer(mappedFeatureGroup) && this.layerManager.isFeatureGroupEditable(mappedFeatureGroup);
}
const resolvedFeatureGroup = this.findFeatureGroupForPolygon(polygon2);
if (resolvedFeatureGroup) {
this.polygonFeatureGroupMap.set(polygon2, resolvedFeatureGroup);
return this.layerManager.isInActiveLayer(resolvedFeatureGroup) && this.layerManager.isFeatureGroupEditable(resolvedFeatureGroup);
}
return true;
}
/**
* Check if a feature group belongs to the active layer.
* Returns true if no layer manager exists (backward compat).
*/
isFeatureGroupInActiveLayer(fg) {
if (!this.layerManager) return true;
return this.layerManager.isInActiveLayer(fg) && this.layerManager.isFeatureGroupEditable(fg);
}
beginPolygonDrag(polygon2, startLatLng, originalEvent, options = {}) {
var _a2, _b2;
if (this.transformModeActive) return false;
if (!this.isPolygonInActiveLayer(polygon2)) return false;
if (!this.isPolygonDragModeActive()) return false;
if (!this.modeManager.canPerformAction("polygonDrag")) return false;
if (!startLatLng) return false;
(_a2 = originalEvent.stopPropagation) == null ? void 0 : _a2.call(originalEvent);
if (originalEvent.cancelable) {
(_b2 = originalEvent.preventDefault) == null ? void 0 : _b2.call(originalEvent);
}
const existingDragData = polygon2._polydrawDragData;
if (this.currentDragPolygon === polygon2 && (existingDragData == null ? void 0 : existingDragData.isDragging)) {
return true;
}
const isCloneMode = this.modeManager.getCurrentMode() === DrawMode.Clone;
const isModifierPressed = isCloneMode ? false : this.detectDragSubtractModifierKey(originalEvent);
this.currentDragIsClone = isCloneMode;
this.currentModifierDragMode = isModifierPressed;
this.isModifierKeyHeld = isModifierPressed;
if (this.saveHistoryState) {
this.saveHistoryState(isCloneMode ? "polygonClone" : "polygonDrag");
}
polygon2._polydrawDragData = {
...polygon2._polydrawDragData ?? {
originalOpacity: polygon2.options.fillOpacity
},
isDragging: true,
startPosition: startLatLng,
startLatLngs: polygon2.getLatLngs()
};
polygon2.setStyle({ fillOpacity: this.config.interaction.drag.opacity });
if (isCloneMode) {
this.showCloneGhost(polygon2._polydrawDragData.startLatLngs);
} else {
this.clearCloneGhost();
}
if (this.map.dragging) {
this.map.dragging.disable();
}
this.setSubtractVisualMode(polygon2, isModifierPressed);
this.setMarkerVisibility(polygon2, false);
try {
const container = this.map.getContainer();
container.style.cursor = this.config.interaction.drag.dragCursor || "move";
} catch {
}
if (options.attachTouchListeners) {
this.attachDragTouchListeners();
}
this.attachDragDocumentListeners();
this.attachDragCancelHandlers();
this.currentDragPolygon = polygon2;
return true;
}
attachPolygonNativeStartListeners(polygon2) {
if (this.polygonNativeStartListeners.has(polygon2)) return;
const startFromNativeEvent = (event) => {
if (!this.isPolygonDragModeActive()) return;
const latlng = EventAdapter.extractCoordinates(
event,
this.map
);
this.beginPolygonDrag(polygon2, latlng, event, {
attachTouchListeners: event.type === "touchstart"
});
};
const listeners = {
mousedown: (event) => startFromNativeEvent(event),
pointerdown: (event) => startFromNativeEvent(event),
touchstart: (event) => startFromNativeEvent(event)
};
this.polygonNativeStartListeners.set(polygon2, listeners);
const attach = () => {
const el = polygon2._path;
if (!el) return;
el.addEventListener("mousedown", listeners.mousedown, { capture: true });
el.addEventListener("pointerdown", listeners.pointerdown, { capture: true });
el.addEventListener("touchstart", listeners.touchstart, { capture: true, passive: false });
};
if (polygon2._path) {
attach();
} else {
polygon2.once("add", attach);
}
}
/**
* Enable polygon dragging functionality
*/
enablePolygonDragging(polygon2, latlngs) {
if (!this.config.modes.dragPolygons && !this.config.tools.clone) return;
polygon2._polydrawOriginalLatLngs = latlngs;
polygon2._polydrawDragData = {
isDragging: false,
startPosition: null,
startLatLngs: null,
originalOpacity: polygon2.options.fillOpacity
};
polygon2.on("mousedown", (e) => {
if (!this.isPolygonDragModeActive()) {
L.DomEvent.stopPropagation(e);
this.map.fire("mousedown", e);
return;
}
this.beginPolygonDrag(polygon2, e.latlng, e.originalEvent);
});
polygon2.on("pointerdown", ((event) => {
const e = event;
if (!this.isPolygonDragModeActive()) {
const originalEvent = e.originalEvent ?? e;
L.DomEvent.stopPropagation(originalEvent);
this.map.fire("pointerdown", e);
return;
}
const orig = e.originalEvent ?? e;
this.beginPolygonDrag(polygon2, e.latlng, orig);
}));
if (LeafletVersionDetector.isV1()) {
const onTouchStart = (e) => {
if (!this.isPolygonDragModeActive()) {
return;
}
const latlng = EventAdapter.extractCoordinates(
e,
this.map
);
this.beginPolygonDrag(polygon2, latlng, e, { attachTouchListeners: true });
};
this.polygonTouchStartListeners.set(polygon2, onTouchStart);
const attachTouch = () => {
const el = polygon2._path;
if (el) {
el.addEventListener("touchstart", onTouchStart, {
passive: false
});
}
};
if (polygon2._path) {
attachTouch();
} else {
polygon2.once("add", attachTouch);
}
}
this.attachPolygonNativeStartListeners(polygon2);
polygon2.on("mouseover", () => {
if (!this.isPolygonInActiveLayer(polygon2)) return;
if (!polygon2._polydrawDragData || !polygon2._polydrawDragData.isDragging) {
try {
const container = this.map.getContainer();
container.style.cursor = this.config.interaction.drag.hoverCursor || "grab";
} catch {
}
}
});
polygon2.on("mouseout", () => {
if (!polygon2._polydrawDragData || !polygon2._polydrawDragData.isDragging) {
try {
const container = this.map.getContainer();
container.style.cursor = "";
} catch {
}
}
});
}
/**
* Update marker draggable state based on current mode
*/
updateMarkerDraggableState() {
const shouldBeDraggable = this.modeManager.canPerformAction("markerDrag");
const markersShouldCapturePointer = this.modeManager.getCurrentMode() !== DrawMode.Clone;
this.getFeatureGroups().forEach((featureGroup) => {
const isActiveLayer = this.isFeatureGroupInActiveLayer(featureGroup);
const effectiveDraggable = shouldBeDraggable && isActiveLayer;
featureGroup.eachLayer((layer) => {
if (layer instanceof L.Marker) {
const marker = layer;
try {
marker.options.draggable = effectiveDraggable;
const element = marker.getElement();
if (element) {
element.style.pointerEvents = markersShouldCapturePointer ? "auto" : "none";
}
if (marker.dragging) {
if (effectiveDraggable) {
marker.dragging.enable();
} else {
marker.dragging.disable();
}
}
} catch {
}
}
});
});
}
/**
* Update all markers for edge deletion visual feedback
*/
updateAllMarkersForEdgeDeletion(showFeedback) {
this.getFeatureGroups().forEach((featureGroup) => {
featureGroup.eachLayer((layer) => {
if (layer instanceof L.Marker) {
this.updateMarkerForEdgeDeletion(layer, showFeedback);
}
});
});
}
/**
* Update individual marker for edge deletion visual feedback
*/
updateMarkerForEdgeDeletion(marker, showFeedback) {
const element = marker.getElement();
if (!element) return;
if (showFeedback) {
element.addEventListener("mouseenter", this.onMarkerHoverForEdgeDeletionEvent);
element.addEventListener("mouseleave", this.onMarkerLeaveForEdgeDeletionEvent);
this.markerEdgeDeletionListeners.set(marker, {
mouseenter: this.onMarkerHoverForEdgeDeletionEvent,
mouseleave: this.onMarkerLeaveForEdgeDeletionEvent
});
} else {
element.removeEventListener("mouseenter", this.onMarkerHoverForEdgeDeletionEvent);
element.removeEventListener("mouseleave", this.onMarkerLeaveForEdgeDeletionEvent);
element.style.backgroundColor = "";
element.style.borderColor = "";
this.markerEdgeDeletionListeners.delete(marker);
}
}
/**
* Set modifier key held state
*/
setModifierKeyHeld(isHeld) {
this.isModifierKeyHeld = isHeld;
}
/**
* Clean up all resources associated with a marker
* This method ensures proper cleanup of event listeners and WeakMap entries
*/
cleanupMarker(marker) {
const element = marker.getElement();
const modifierHandler = this.markerModifierHandlers.get(marker);
if (modifierHandler) {
document.removeEventListener("keydown", modifierHandler);
document.removeEventListener("keyup", modifierHandler);
if (element) {
element.removeEventListener("mousemove", modifierHandler);
}
this.markerModifierHandlers.delete(marker);
}
const touchListeners = this.markerTouchListeners.get(marker);
if (touchListeners) {
if (element) {
element.removeEventListener("touchstart", touchListeners.touchstart);
element.removeEventListener("touchend", touchListeners.touchend);
}
this.markerTouchListeners.delete(marker);
}
const edgeDeletionListeners = this.markerEdgeDeletionListeners.get(marker);
if (edgeDeletionListeners) {
if (element) {
element.removeEventListener("mouseenter", edgeDeletionListeners.mouseenter);
element.removeEventListener("mouseleave", edgeDeletionListeners.mouseleave);
}
this.markerEdgeDeletionListeners.delete(marker);
}
this.markerFeatureGroupMap.delete(marker);
}
/**
* Clean up all resources associated with a feature group
* This method ensures proper cleanup of all markers and associated resources
*/
cleanupFeatureGroup(featureGroup) {
featureGroup.eachLayer((layer) => {
if (layer instanceof L.Marker) {
this.cleanupMarker(layer);
} else if (layer instanceof L.Polygon) {
this.detachPolygonNativeStartListeners(layer);
this.detachPolygonTouchStart(layer);
this.polygonFeatureGroupMap.delete(layer);
}
});
this.destroyTransformController(featureGroup);
}
// Private methods
destroyTransformController(featureGroup) {
const transformController = this.transformControllers.get(featureGroup);
if (!transformController) {
return;
}
transformController.destroy();
this.transformControllers.delete(featureGroup);
this.transformModeActive = false;
}
onEdgeClick(e, edgePolyline) {
if (!this.config.modes.attachElbow) {
return;
}
if (!this.modeManager.isInOffMode()) {
return;
}
const edgeInfo = edgePolyline._polydrawEdgeInfo;
if (!edgeInfo) return;
if (edgeInfo.parentFeatureGroup && !this.isFeatureGroupInActiveLayer(edgeInfo.parentFeatureGroup)) {
return;
}
const newPoint = e.latlng;
const parentPolygon = edgeInfo.parentPolygon;
const parentFeatureGroup = edgeInfo.parentFeatureGroup;
if (parentPolygon && parentFeatureGroup) {
try {
if (typeof parentPolygon.toGeoJSON !== "function") {
return;
}
const poly = parentPolygon.toGeoJSON();
if (poly.geometry.type === "MultiPolygon" || poly.geometry.type === "Polygon") {
const newPolygon = this.turfHelper.injectPointToPolygon(
poly,
[newPoint.lng, newPoint.lat],
edgeInfo.ringIndex
);
if (newPolygon) {
const polydrawPolygon = parentPolygon;
const optimizationLevel = polydrawPolygon._polydrawOptimizationLevel || 0;
const originalOptimizationLevel = polydrawPolygon._polydrawOptimizationOriginalLevel || optimizationLevel;
const featureMetadataState = this.getFeatureMetadataState(parentFeatureGroup);
this.cleanupFeatureGroup(parentFeatureGroup);
this.removeFeatureGroup(parentFeatureGroup);
this.emitPolygonUpdated({
operation: "addVertex",
polygon: newPolygon,
optimizationLevel,
originalOptimizationLevel,
featureId: featureMetadataState.featureId,
featureMetadata: featureMetadataState.metadata,
sourceFeatureIds: featureMetadataState.sourceFeatureIds,
featureInteractionOverride: featureMetadataState.interactionOverride,
featureStyleOverrides: featureMetadataState.styleOverrides,
featureCreatedAt: featureMetadataState.createdAt
});
}
}
} catch {
}
}
L.DomEvent.stopPropagation(e);
}
highlightEdgeOnHover(edgePolyline, isHovering) {
if (isHovering) {
edgePolyline.setStyle({
color: this.config.styles.ui.edgeHover.color,
weight: 4,
opacity: 1
});
} else {
edgePolyline.setStyle({
color: "transparent",
weight: 10,
opacity: 0
});
}
}
deriveImportantMarkerIndices(latlngs, options) {
const important = /* @__PURE__ */ new Set();
if (!latlngs || latlngs.length === 0) {
return important;
}
const normalizedLevel = Math.min(Math.max(options.optimizationLevel ?? 0, 0), 10);
const hasClosingPoint = latlngs.length > 2 && this.latLngEquals(latlngs[0], latlngs[latlngs.length - 1]);
const ring = hasClosingPoint ? latlngs.slice(0, -1) : [...latlngs];
const vertexCount = ring.length;
const addIndex = (idx) => {
if (idx === void 0 || idx === null || idx < 0) {
return;
}
let normalized = idx;
if (hasClosingPoint && idx === latlngs.length - 1) {
normalized = 0;
}
if (normalized >= vertexCount) {
normalized = vertexCount > 0 ? normalized % vertexCount : 0;
}
if (normalized >= 0 && normalized < vertexCount) {
important.add(normalized);
}
};
addIndex(0);
addIndex(options.menuIndex);
addIndex(options.deleteIndex);
addIndex(options.infoIndex);
if (vertexCount === 0 || normalizedLevel <= 0 || vertexCount <= 3) {
for (let i = 0; i < vertexCount; i++) {
important.add(i);
}
return important;
}
const tolerance = this.getToleranceForOptimizationLevel(normalizedLevel);
let simplifiedVertices = tolerance > 0 ? this.turfHelper.simplifyLatLngRing(ring, tolerance, true) : [...ring];
if (simplifiedVertices.length > 1 && this.latLngEquals(simplifiedVertices[0], simplifiedVertices[simplifiedVertices.length - 1])) {
simplifiedVertices = simplifiedVertices.slice(0, -1);
}
if (!simplifiedVertices || simplifiedVertices.length === 0) {
for (let i = 0; i < vertexCount; i++) {
important.add(i);
}
return important;
}
simplifiedVertices.forEach((vertex) => {
const closestIndex = this.findClosestVertexIndex(vertex, ring);
if (closestIndex !== null) {
important.add(closestIndex);
}
});
if (important.size === 0) {
for (let i = 0; i < vertexCount; i++) {
important.add(i);
}
}
return important;
}
normalizeMarkerIndex(index, length2, hasClosingPoint) {
if (index === void 0 || index === null || index < 0) {
return null;
}
if (hasClosingPoint) {
const uniqueLength = Math.max(1, length2 - 1);
if (index === length2 - 1) {
return 0;
}
if (index >= uniqueLength) {
return index % uniqueLength;
}
return index;
}
if (index >= length2) {
return length2 > 0 ? length2 - 1 : null;
}
return index;
}
latLngEquals(a, b) {
if (!a || !b) return false;
return Math.abs(a.lat - b.lat) < 1e-9 && Math.abs(a.lng - b.lng) < 1e-9;
}
isLatLngLiteral(value) {
return !!value && typeof value === "object" && "lat" in value && "lng" in value && typeof value.lat === "number" && typeof value.lng === "number";
}
getOrderedMarkers(featureGroup) {
const markers = [];
featureGroup.eachLayer((layer) => {
if (layer instanceof L.Marker) {
markers.push(layer);
}
});
return markers.sort(
(a, b) => leafletAdapter.util.stamp(a) - leafletAdapter.util.stamp(b)
);
}
countLatLngNodes(latlngs) {
if (!Array.isArray(latlngs)) return 0;
if (latlngs.length === 0) return 0;
if (this.isLatLngLiteral(latlngs[0])) {
return latlngs.length;
}
return latlngs.reduce((sum2, item) => sum2 + this.countLatLngNodes(item), 0);
}
rebuildLatLngStructure(original, markerPositions, markerIndex) {
if (!Array.isArray(original)) {
return original;
}
if (original.length > 0 && this.isLatLngLiteral(original[0])) {
const ring = original;
const hasClosingPoint = ring.length > 1 && this.latLngEquals(ring[0], ring[ring.length - 1]);
const updated = ring.map((_pt, idx) => {
const pos = markerPositions[markerIndex.value] ?? ring[idx];
markerIndex.value += 1;
return leafletAdapter.createLatLng(pos.lat, pos.lng);
});
if (hasClosingPoint && updated.length > 1) {
updated[updated.length - 1] = updated[0];
}
return updated;
}
return original.map(
(child) => this.rebuildLatLngStructure(child, markerPositions, markerIndex)
);
}
getOptimizationMetadataFromFeatureGroup(featureGroup) {
let level = 0;
let original = 0;
featureGroup.eachLayer((layer) => {
if (layer instanceof L.Polygon) {
const poly = layer;
if (typeof poly._polydrawOptimizationLevel === "number") {
level = poly._polydrawOptimizationLevel || 0;
}
if (typeof poly._polydrawOptimizationOriginalLevel === "number") {
original = poly._polydrawOptimizationOriginalLevel || 0;
}
}
});
if (!original && level > 0) {
original = level;
}
return { level, original };
}
getFeatureMetadataState(featureGroup) {
if (!featureGroup) {
return {};
}
const featureMetadata = featureGroup._polydrawMetadata;
const createdAt = (featureMetadata == null ? void 0 : featureMetadata.createdAt) instanceof Date && !Number.isNaN(featureMetadata.createdAt.getTime()) ? featureMetadata.createdAt.toISOString() : void 0;
return {
featureId: featureMetadata == null ? void 0 : featureMetadata.id,
metadata: (featureMetadata == null ? void 0 : featureMetadata.metadata) ? { ...featureMetadata.metadata } : void 0,
sourceFeatureIds: Array.isArray(featureMetadata == null ? void 0 : featureMetadata.sourceFeatureIds) ? [...featureMetadata.sourceFeatureIds] : (featureMetadata == null ? void 0 : featureMetadata.id) ? [featureMetadata.id] : void 0,
interactionOverride: featureMetadata == null ? void 0 : featureMetadata.interactionOverride,
styleOverrides: (featureMetadata == null ? void 0 : featureMetadata.styleOverrides) ? { ...featureMetadata.styleOverrides } : void 0,
createdAt
};
}
getOptimizationMetadataFromPolygonLayer(polygon2) {
if (!polygon2) {
return { level: 0, original: 0 };
}
const polydrawPolygon = polygon2;
const level = polydrawPolygon._polydrawOptimizationLevel || 0;
const original = polydrawPolygon._polydrawOptimizationOriginalLevel || (level > 0 ? level : 0);
return { level, original };
}
getDistanceMeters(a, b) {
try {
const pointA = leafletAdapter.createLatLng(a.lat, a.lng);
const pointB = leafletAdapter.createLatLng(b.lat, b.lng);
if (typeof pointA.distanceTo === "function") {
return pointA.distanceTo(pointB);
}
} catch {
}
const toRad = (value) => value * Math.PI / 180;
const R = 6371e3;
const dLat = toRad(b.lat - a.lat);
const dLng = toRad(b.lng - a.lng);
const lat1 = toRad(a.lat);
const lat2 = toRad(b.lat);
const h = Math.sin(dLat / 2) * Math.sin(dLat / 2) + Math.sin(dLng / 2) * Math.sin(dLng / 2) * Math.cos(lat1) * Math.cos(lat2);
const c = 2 * Math.atan2(Math.sqrt(h), Math.sqrt(1 - h));
return R * c;
}
findClosestVertexIndex(target, vertices) {
if (!vertices || vertices.length === 0) {
return null;
}
let closestIndex = 0;
let minDistance = Number.POSITIVE_INFINITY;
for (let i = 0; i < vertices.length; i++) {
const dist = this.getDistanceMeters(target, vertices[i]);
if (dist < minDistance) {
minDistance = dist;
closestIndex = i;
if (dist === 0) {
break;
}
}
}
return closestIndex;
}
getToleranceForOptimizationLevel(level) {
const normalized = Math.min(Math.max(level, 0), 10) / 10;
const visConfig = this.config.markers.visualOptimization ?? {};
const minTolerance = Math.max(visConfig.toleranceMin ?? 5e-6, 0);
const maxTolerance = Math.max(visConfig.toleranceMax ?? 5e-3, minTolerance);
const curvePower = visConfig.curve ?? 1.35;
const curved = Math.pow(normalized, curvePower);
return minTolerance + (maxTolerance - minTolerance) * curved;
}
elbowClicked(e, polygonLayer, forcedLatLng) {
if (!this.config.modes.edgeDeletion) return;
if (!this.isEdgeDeletionModifierKeyPressed(e.originalEvent)) return;
const clickedLatLng = forcedLatLng ?? e.latlng;
const rawLatLngs = polygonLayer.getLatLngs();
let rings = [];
if (Array.isArray(rawLatLngs) && rawLatLngs.length > 0) {
if (Array.isArray(rawLatLngs[0])) {
const first = rawLatLngs[0];
if (Array.isArray(first) && Array.isArray(first[0])) {
rings = rawLatLngs[0];
} else {
rings = rawLatLngs;
}
} else {
rings = [rawLatLngs];
}
} else {
return;
}
let targetRingIndex = -1;
let targetVertexIndex = -1;
for (let r = 0; r < rings.length; r++) {
const ring = rings[r];
for (let v = 0; v < ring.length; v++) {
const p = ring[v];
if (p.lat === clickedLatLng.lat && p.lng === clickedLatLng.lng) {
targetRingIndex = r;
targetVertexIndex = v;
break;
}
}
if (targetRingIndex !== -1) break;
}
if (targetRingIndex === -1 || targetVertexIndex === -1) {
return;
}
const targetRing = rings[targetRingIndex];
if (targetRing.length <= this.config.interaction.edgeDeletion.minVertices) {
return;
}
const newRings = rings.map((ring, idx) => {
if (idx !== targetRingIndex) return ring.slice();
const nr = ring.slice();
nr.splice(targetVertexIndex, 1);
return nr;
});
const coords = newRings.map((ring) => {
const arr = ring.map((ll) => [ll.lng, ll.lat]);
if (arr.length > 0) {
const first = arr[0];
const last = arr[arr.length - 1];
if (first[0] !== last[0] || first[1] !== last[1]) {
arr.push([first[0], first[1]]);
}
}
return arr;
});
let currentFeatureGroup = null;
for (const fg of this.getFeatureGroups()) {
let found = false;
fg.eachLayer((layer) => {
if (layer === polygonLayer) found = true;
});
if (found) {
currentFeatureGroup = fg;
break;
}
}
const metadata = currentFeatureGroup ? this.getOptimizationMetadataFromFeatureGroup(currentFeatureGroup) : this.getOptimizationMetadataFromPolygonLayer(polygonLayer);
const featureMetadataState = this.getFeatureMetadataState(currentFeatureGroup ?? void 0);
const { level: optimizationLevel, original: originalOptimizationLevel } = metadata;
if (currentFeatureGroup) {
this.removeFeatureGroup(currentFeatureGroup);
}
const newPolygon = this.turfHelper.getMultiPolygon([coords]);
this.emitPolygonUpdated({
operation: "removeVertex",
polygon: newPolygon,
optimizationLevel,
originalOptimizationLevel,
featureId: featureMetadataState.featureId,
featureMetadata: featureMetadataState.metadata,
sourceFeatureIds: featureMetadataState.sourceFeatureIds,
featureInteractionOverride: featureMetadataState.interactionOverride,
featureStyleOverrides: featureMetadataState.styleOverrides,
featureCreatedAt: featureMetadataState.createdAt
});
}
markerDrag(featureGroup) {
if (!this._activeMarker) {
if (!isTestEnvironment()) {
console.warn("No active marker set for dragging.");
}
return;
}
const polygonLayer = featureGroup.getLayers().find((l) => l instanceof L.Polygon);
if (!polygonLayer) {
if (!isTestEnvironment()) {
console.warn("No polygon found in feature group for marker drag.");
}
return;
}
const markers = this.getOrderedMarkers(featureGroup);
if (markers.length === 0) {
return;
}
const markerPositions = markers.map((m) => m.getLatLng());
const expectedVertices = this.countLatLngNodes(polygonLayer.getLatLngs());
if (markerPositions.length < expectedVertices) {
if (!isTestEnvironment()) {
console.warn(
"Not enough markers to rebuild polygon during drag. Expected:",
expectedVertices,
"got:",
markerPositions.length
);
}
return;
}
const markerIndex = { value: 0 };
const updatedLatLngs = this.rebuildLatLngStructure(
polygonLayer.getLatLngs(),
markerPositions,
markerIndex
);
polygonLayer.setLatLngs(updatedLatLngs);
}
async markerDragEnd(featureGroup) {
var _a2;
this.polygonInformation.deletePolygonInformationStorage();
const featureCollection2 = featureGroup.toGeoJSON();
if (!featureCollection2.features || featureCollection2.features.length === 0) {
return;
}
const { level: optimizationLevel, original: originalOptimizationLevel } = this.getOptimizationMetadataFromFeatureGroup(featureGroup);
const featureMetadataState = this.getFeatureMetadataState(featureGroup);
const targetLayerId = (_a2 = this.layerManager) == null ? void 0 : _a2.getLayerForFeatureGroup(featureGroup);
this.cleanupFeatureGroup(featureGroup);
this.removeFeatureGroup(featureGroup);
const emitCleanedPolygon = (polygon2, allowMerge, opts) => {
const cleaned = this.turfHelper.removeDuplicateVertices(polygon2);
const parts = [];
if (!this.config.kinks) {
try {
const split = this.turfHelper.getKinks(cleaned);
if (split && split.length > 0) {
parts.push(...split);
}
} catch {
}
}
if (parts.length === 0) {
if (cleaned.geometry.type === "MultiPolygon") {
cleaned.geometry.coordinates.forEach(
(coords) => parts.push(this.turfHelper.getMultiPolygon([coords]))
);
} else {
parts.push(cleaned);
}
}
parts.forEach((part) => {
this.emitPolygonUpdated({
operation: "markerDrag",
polygon: part,
allowMerge,
optimizationLevel: opts.optimizationLevel,
originalOptimizationLevel: opts.originalOptimizationLevel,
featureId: featureMetadataState.featureId,
featureMetadata: featureMetadataState.metadata,
sourceFeatureIds: featureMetadataState.sourceFeatureIds,
featureInteractionOverride: featureMetadataState.interactionOverride,
featureStyleOverrides: featureMetadataState.styleOverrides,
targetLayerId,
featureCreatedAt: featureMetadataState.createdAt
});
});
};
if (featureCollection2.features[0].geometry.type === "MultiPolygon") {
for (const element of featureCollection2.features[0].geometry.coordinates) {
const feature2 = this.turfHelper.getMultiPolygon([element]);
emitCleanedPolygon(feature2, true, {
optimizationLevel,
originalOptimizationLevel
});
}
} else {
const feature2 = this.turfHelper.getMultiPolygon([
featureCollection2.features[0].geometry.coordinates
]);
emitCleanedPolygon(feature2, true, {
optimizationLevel,
originalOptimizationLevel
});
}
this.polygonInformation.createPolygonInformationStorage(this.getFeatureGroups());
}
attachDragDocumentListeners() {
this.detachDragDocumentListeners();
this.dragUsesPointerEvents = EventAdapter.shouldUsePointerEvents();
this._boundDragMoveListener = this.onDragDocumentMove;
this._boundDragEndListener = this.onDragDocumentEnd;
if (this.dragUsesPointerEvents) {
document.addEventListener("pointermove", this._boundDragMoveListener);
document.addEventListener("pointerup", this._boundDragEndListener);
}
document.addEventListener("mousemove", this._boundDragMoveListener);
document.addEventListener("mouseup", this._boundDragEndListener);
}
detachDragDocumentListeners() {
if (this._boundDragMoveListener) {
if (this.dragUsesPointerEvents) {
document.removeEventListener("pointermove", this._boundDragMoveListener);
}
document.removeEventListener("mousemove", this._boundDragMoveListener);
this._boundDragMoveListener = null;
}
if (this._boundDragEndListener) {
if (this.dragUsesPointerEvents) {
document.removeEventListener("pointerup", this._boundDragEndListener);
}
document.removeEventListener("mouseup", this._boundDragEndListener);
this._boundDragEndListener = null;
}
this.dragUsesPointerEvents = false;
}
attachDragTouchListeners() {
this.detachDragTouchListeners();
this._boundDragTouchMove = this.onDragTouchMove;
this._boundDragTouchEnd = this.onDragTouchEnd;
const container = this.map.getContainer();
container.addEventListener("touchmove", this._boundDragTouchMove, { passive: false });
container.addEventListener("touchend", this._boundDragTouchEnd, { passive: false });
}
detachDragTouchListeners() {
const container = this.map.getContainer();
if (this._boundDragTouchMove) {
container.removeEventListener("touchmove", this._boundDragTouchMove);
this._boundDragTouchMove = null;
}
if (this._boundDragTouchEnd) {
container.removeEventListener("touchend", this._boundDragTouchEnd);
this._boundDragTouchEnd = null;
}
}
detachPolygonTouchStart(polygon2) {
const listener = this.polygonTouchStartListeners.get(polygon2);
if (!listener) return;
const el = polygon2._path;
if (el) {
el.removeEventListener("touchstart", listener);
}
this.polygonTouchStartListeners.delete(polygon2);
}
detachPolygonNativeStartListeners(polygon2) {
const listeners = this.polygonNativeStartListeners.get(polygon2);
if (!listeners) return;
const el = polygon2._path;
if (el) {
el.removeEventListener("mousedown", listeners.mousedown, { capture: true });
el.removeEventListener("pointerdown", listeners.pointerdown, { capture: true });
el.removeEventListener("touchstart", listeners.touchstart, { capture: true });
}
this.polygonNativeStartListeners.delete(polygon2);
}
attachDragCancelHandlers() {
if (this.dragCancelHandlersAttached) return;
this.dragCancelHandlersAttached = true;
document.addEventListener("keydown", this.onPolygonDragKeyDown);
this.map.on(
"pointercancel",
this.onPolygonDragCancel,
this
);
this.map.on(
"touchcancel",
this.onPolygonDragCancel,
this
);
}
detachDragCancelHandlers() {
if (!this.dragCancelHandlersAttached) return;
this.dragCancelHandlersAttached = false;
document.removeEventListener("keydown", this.onPolygonDragKeyDown);
this.map.off(
"pointercancel",
this.onPolygonDragCancel,
this
);
this.map.off(
"touchcancel",
this.onPolygonDragCancel,
this
);
}
cancelActivePolygonDrag() {
if (!this.currentDragPolygon || !this.currentDragPolygon._polydrawDragData) {
this.clearCloneGhost();
this.detachDragDocumentListeners();
return;
}
const polygon2 = this.currentDragPolygon;
const dragData = polygon2._polydrawDragData;
if (!dragData) {
this.clearCloneGhost();
this.detachDragDocumentListeners();
return;
}
if (!dragData.isDragging) {
this.clearCloneGhost();
this.detachDragDocumentListeners();
return;
}
dragData.isDragging = false;
if (this.map.dragging) {
this.map.dragging.enable();
}
if (dragData.startLatLngs) {
polygon2.setLatLngs(dragData.startLatLngs);
}
if (polygon2._polydrawOriginalMarkerPositions) {
polygon2._polydrawOriginalMarkerPositions.forEach((pos, marker) => {
marker.setLatLng(pos);
});
polygon2._polydrawOriginalMarkerPositions.clear();
delete polygon2._polydrawOriginalMarkerPositions;
}
if (polygon2._polydrawOriginalHoleLinePositions) {
polygon2._polydrawOriginalHoleLinePositions.forEach((positions, line) => {
line.setLatLngs(positions);
});
polygon2._polydrawOriginalHoleLinePositions.clear();
delete polygon2._polydrawOriginalHoleLinePositions;
}
if (polygon2._polydrawCurrentDragSession) {
delete polygon2._polydrawCurrentDragSession;
}
if (dragData.originalOpacity != null) {
polygon2.setStyle({ fillOpacity: dragData.originalOpacity });
}
this.setSubtractVisualMode(polygon2, false);
this.setMarkerVisibility(polygon2, true);
try {
const container = this.map.getContainer();
container.style.cursor = "";
} catch {
}
this.clearCloneGhost();
this.detachDragTouchListeners();
this.detachDragDocumentListeners();
this.detachDragCancelHandlers();
this.currentDragIsClone = false;
this.currentModifierDragMode = false;
this.isModifierKeyHeld = false;
this.currentDragPolygon = null;
}
offsetPolygonCoordinates(latLngs, offsetLat, offsetLng) {
if (!latLngs) return latLngs;
if (Array.isArray(latLngs[0])) {
return latLngs.map(
(ring) => this.offsetPolygonCoordinates(ring, offsetLat, offsetLng)
);
}
return latLngs.map(
(p) => leafletAdapter.createLatLng(p.lat + offsetLat, p.lng + offsetLng)
);
}
updateMarkersAndHoleLinesDuringDrag(polygon2, offsetLat, offsetLng) {
try {
let featureGroup = null;
for (const fg of this.getFeatureGroups()) {
let foundPolygon = false;
fg.eachLayer((layer) => {
if (layer === polygon2) {
foundPolygon = true;
}
});
if (foundPolygon) {
featureGroup = fg;
break;
}
}
if (!featureGroup) {
return;
}
const dragSessionKey = "_polydrawDragSession_" + Date.now() + "_" + leafletAdapter.util.stamp(polygon2);
if (!polygon2._polydrawCurrentDragSession) {
polygon2._polydrawCurrentDragSession = dragSessionKey;
polygon2._polydrawOriginalMarkerPositions = /* @__PURE__ */ new Map();
polygon2._polydrawOriginalHoleLinePositions = /* @__PURE__ */ new Map();
featureGroup.eachLayer((layer) => {
if (layer instanceof L.Marker) {
polygon2._polydrawOriginalMarkerPositions.set(layer, layer.getLatLng());
} else if (layer instanceof L.Polyline && !(layer instanceof L.Polygon)) {
const latLngs = layer.getLatLngs();
polygon2._polydrawOriginalHoleLinePositions.set(layer, latLngs);
}
});
}
featureGroup.eachLayer((layer) => {
if (layer instanceof L.Marker) {
const originalPos = polygon2._polydrawOriginalMarkerPositions.get(layer);
if (originalPos) {
const newLatLng = {
lat: originalPos.lat + offsetLat,
lng: originalPos.lng + offsetLng
};
layer.setLatLng(newLatLng);
}
} else if (layer instanceof L.Polyline && !(layer instanceof L.Polygon)) {
const originalPositions = polygon2._polydrawOriginalHoleLinePositions.get(layer);
if (originalPositions) {
let newLatLngs;
if (Array.isArray(originalPositions[0])) {
newLatLngs = originalPositions.map(
(ring) => ring.map(
(latlng) => leafletAdapter.createLatLng(latlng.lat + offsetLat, latlng.lng + offsetLng)
)
);
} else {
newLatLngs = originalPositions.map(
(latlng) => leafletAdapter.createLatLng(latlng.lat + offsetLat, latlng.lng + offsetLng)
);
}
layer.setLatLngs(newLatLngs);
}
}
});
} catch (error) {
console.warn("Error updating markers during drag:", error);
}
}
async updatePolygonAfterDrag(polygon2) {
try {
let featureGroup = null;
for (const fg of this.getFeatureGroups()) {
fg.eachLayer((layer) => {
if (layer === polygon2) {
featureGroup = fg;
}
});
if (featureGroup) break;
}
if (!featureGroup) {
return;
}
const newGeoJSON = polygon2.toGeoJSON();
if (this.isModifierDragActive()) {
this.performModifierSubtract(newGeoJSON, featureGroup);
this.currentModifierDragMode = false;
this.isModifierKeyHeld = false;
return;
}
const { level: optimizationLevel, original: originalOptimizationLevel } = this.getOptimizationMetadataFromFeatureGroup(featureGroup);
const featureMetadataState = this.getFeatureMetadataState(featureGroup);
this.cleanupFeatureGroup(featureGroup);
this.removeFeatureGroup(featureGroup);
const feature2 = this.turfHelper.getTurfPolygon(newGeoJSON);
this.emitPolygonUpdated({
operation: "polygonDrag",
polygon: feature2,
allowMerge: true,
optimizationLevel,
originalOptimizationLevel,
featureId: featureMetadataState.featureId,
featureMetadata: featureMetadataState.metadata,
sourceFeatureIds: featureMetadataState.sourceFeatureIds,
featureInteractionOverride: featureMetadataState.interactionOverride,
featureStyleOverrides: featureMetadataState.styleOverrides,
featureCreatedAt: featureMetadataState.createdAt
});
this.polygonInformation.createPolygonInformationStorage(this.getFeatureGroups());
} catch {
}
}
updatePolygonAfterCloneDrag(polygon2, dragData) {
try {
let featureGroup = null;
for (const fg of this.getFeatureGroups()) {
fg.eachLayer((layer) => {
if (layer === polygon2) {
featureGroup = fg;
}
});
if (featureGroup) break;
}
if (!featureGroup) {
return;
}
const newGeoJSON = polygon2.toGeoJSON();
const originalLatLngs = (dragData == null ? void 0 : dragData.startLatLngs) ?? null;
const originalGeoJSON = originalLatLngs ? this.createPolygonFeatureFromLatLngs(originalLatLngs) : null;
const { level: optimizationLevel, original: originalOptimizationLevel } = this.getOptimizationMetadataFromFeatureGroup(featureGroup);
const featureMetadataState = this.getFeatureMetadataState(featureGroup);
this.cleanupFeatureGroup(featureGroup);
this.removeFeatureGroup(featureGroup);
const movedFeature = this.turfHelper.getTurfPolygon(newGeoJSON);
this.emitPolygonUpdated({
operation: "polygonDrag",
polygon: movedFeature,
allowMerge: true,
optimizationLevel,
originalOptimizationLevel,
featureId: featureMetadataState.featureId,
featureMetadata: featureMetadataState.metadata,
sourceFeatureIds: featureMetadataState.sourceFeatureIds,
featureInteractionOverride: featureMetadataState.interactionOverride,
featureStyleOverrides: featureMetadataState.styleOverrides,
featureCreatedAt: featureMetadataState.createdAt
});
if (originalGeoJSON) {
const originalFeature = this.turfHelper.getTurfPolygon(originalGeoJSON);
this.emitPolygonUpdated({
operation: "polygonClone",
polygon: originalFeature,
allowMerge: true,
optimizationLevel,
originalOptimizationLevel,
featureId: featureMetadataState.featureId,
featureMetadata: featureMetadataState.metadata,
sourceFeatureIds: featureMetadataState.sourceFeatureIds,
featureInteractionOverride: featureMetadataState.interactionOverride,
featureStyleOverrides: featureMetadataState.styleOverrides,
featureCreatedAt: featureMetadataState.createdAt
});
}
this.polygonInformation.createPolygonInformationStorage(this.getFeatureGroups());
} catch {
} finally {
this.currentModifierDragMode = false;
this.isModifierKeyHeld = false;
}
}
showCloneGhost(latLngs) {
this.clearCloneGhost();
if (!latLngs) return;
try {
const ghost = leafletAdapter.createPolygon(latLngs, {
color: this.config.styles.polygon.color,
weight: this.config.styles.polygon.weight,
opacity: 0.9,
fill: false,
fillOpacity: 0,
dashArray: "4,6",
interactive: false
});
ghost.addTo(this.map);
this.currentCloneGhost = ghost;
} catch {
}
}
clearCloneGhost() {
if (!this.currentCloneGhost) return;
try {
this.map.removeLayer(this.currentCloneGhost);
} catch {
}
this.currentCloneGhost = null;
}
createPolygonFeatureFromLatLngs(latLngs) {
try {
const polygon2 = leafletAdapter.createPolygon(latLngs);
return polygon2.toGeoJSON();
} catch {
return null;
}
}
getDragSubtractModifierKey() {
const { keys } = this.config.interaction.drag.modifierSubtract;
const userAgent = navigator.userAgent.toLowerCase();
const isMac = userAgent.includes("mac");
const isWindows = userAgent.includes("windows");
if (isMac && keys.mac) return keys.mac;
if (isWindows && keys.windows) return keys.windows;
if (keys.linux) return keys.linux;
return isMac ? "metaKey" : "ctrlKey";
}
detectDragSubtractModifierKey(event) {
if (isTouchDevice()) {
return false;
}
const modifierKey = this.getDragSubtractModifierKey();
return event[modifierKey] || false;
}
setSubtractVisualMode(polygon2, enabled) {
if (!polygon2 || !polygon2.setStyle) {
return;
}
try {
if (enabled) {
polygon2.setStyle({
color: this.config.styles.ui.dragSubtract.color
});
} else {
polygon2.setStyle({
color: this.config.styles.polygon.color
});
}
this.updateMarkerColorsForSubtractMode(polygon2, enabled);
} catch {
}
}
updateMarkerColorsForSubtractMode(polygon2, subtractMode) {
try {
let featureGroup = null;
for (const fg2 of this.getFeatureGroups()) {
fg2.eachLayer((layer) => {
if (layer === polygon2) {
featureGroup = fg2;
}
});
if (featureGroup) break;
}
if (!featureGroup) return;
const fg = featureGroup;
const hideMarkersOnDrag = this.config.interaction.drag.modifierSubtract.hideMarkersOnDrag;
fg.eachLayer((layer) => {
if (layer instanceof L.Marker) {
const marker = layer;
const element = marker.getElement();
if (element) {
if (subtractMode) {
if (hideMarkersOnDrag) {
element.style.display = "none";
element.classList.add("subtract-mode-hidden");
} else {
element.style.backgroundColor = this.config.styles.ui.dragSubtract.color;
element.style.borderColor = this.config.styles.ui.dragSubtract.color;
element.classList.add("subtract-mode");
}
} else {
if (hideMarkersOnDrag) {
element.style.display = "";
element.classList.remove("subtract-mode-hidden");
} else {
element.style.backgroundColor = "";
element.style.borderColor = "";
element.classList.remove("subtract-mode");
}
}
}
}
});
} catch {
}
}
handleModifierToggleDuringDrag(event) {
const isModifierPressed = this.detectDragSubtractModifierKey(event);
this.currentModifierDragMode = isModifierPressed;
this.isModifierKeyHeld = isModifierPressed;
if (this.currentDragPolygon) {
this.setSubtractVisualMode(this.currentDragPolygon, isModifierPressed);
}
}
isModifierDragActive() {
return this.currentModifierDragMode;
}
performModifierSubtract(draggedGeoJSON, originalFeatureGroup) {
var _a2;
try {
const draggedPolygon = this.turfHelper.getTurfPolygon(draggedGeoJSON);
const intersectingFeatureGroups = [];
const sourceLayerId = (_a2 = this.layerManager) == null ? void 0 : _a2.getLayerForFeatureGroup(originalFeatureGroup);
this.getFeatureGroups().forEach((featureGroup) => {
if (featureGroup === originalFeatureGroup) {
return;
}
if (sourceLayerId && this.layerManager) {
const candidateLayerId = this.layerManager.getLayerForFeatureGroup(featureGroup);
if (candidateLayerId !== sourceLayerId) {
return;
}
}
try {
const featureCollection2 = featureGroup.toGeoJSON();
if (!featureCollection2 || !featureCollection2.features || !featureCollection2.features[0]) {
return;
}
const firstFeature = featureCollection2.features[0];
if (!firstFeature.geometry || !firstFeature.geometry.coordinates) {
return;
}
const existingPolygon = this.turfHelper.getTurfPolygon(firstFeature);
try {
const intersection3 = this.turfHelper.getIntersection(existingPolygon, draggedPolygon);
if (intersection3 && intersection3.geometry && "coordinates" in intersection3.geometry) {
const coords = intersection3.geometry.coordinates;
if (coords.length > 0) {
intersectingFeatureGroups.push(featureGroup);
}
}
} catch {
try {
const hasIntersection = this.turfHelper.polygonIntersect(
existingPolygon,
draggedPolygon
);
if (hasIntersection) {
intersectingFeatureGroups.push(featureGroup);
}
} catch {
}
}
} catch {
}
});
this.removeFeatureGroup(originalFeatureGroup);
intersectingFeatureGroups.forEach((featureGroup) => {
try {
const featureCollection2 = featureGroup.toGeoJSON();
const existingPolygon = this.turfHelper.getTurfPolygon(featureCollection2.features[0]);
const { level: optimizationLevel, original: originalOptimizationLevel } = this.getOptimizationMetadataFromFeatureGroup(featureGroup);
const featureMetadataState = this.getFeatureMetadataState(featureGroup);
this.cleanupFeatureGroup(featureGroup);
this.removeFeatureGroup(featureGroup);
try {
const difference3 = this.turfHelper.polygonDifference(existingPolygon, draggedPolygon);
if (difference3 && difference3.geometry) {
const coords = this.turfHelper.getCoords(difference3);
for (const coordSet of coords) {
const individualPolygon = this.turfHelper.getMultiPolygon([coordSet]);
this.emitPolygonUpdated({
operation: "modifierSubtract",
polygon: this.turfHelper.getTurfPolygon(individualPolygon),
allowMerge: false,
// Don't merge the result of subtract operations
optimizationLevel,
originalOptimizationLevel,
featureId: featureMetadataState.featureId,
featureMetadata: featureMetadataState.metadata,
sourceFeatureIds: featureMetadataState.sourceFeatureIds,
featureInteractionOverride: featureMetadataState.interactionOverride,
featureStyleOverrides: featureMetadataState.styleOverrides,
featureCreatedAt: featureMetadataState.createdAt
});
}
}
} catch (differenceError) {
if (!isTestEnvironment()) {
console.warn("Failed to perform difference operation:", differenceError);
}
this.emitPolygonUpdated({
operation: "modifierSubtractFallback",
polygon: existingPolygon,
allowMerge: false,
optimizationLevel,
originalOptimizationLevel,
featureId: featureMetadataState.featureId,
featureMetadata: featureMetadataState.metadata,
sourceFeatureIds: featureMetadataState.sourceFeatureIds,
featureInteractionOverride: featureMetadataState.interactionOverride,
featureStyleOverrides: featureMetadataState.styleOverrides,
featureCreatedAt: featureMetadataState.createdAt
});
}
} catch (error) {
if (!isTestEnvironment()) {
console.warn("Error in modifier subtract operation:", error);
}
}
});
this.polygonInformation.createPolygonInformationStorage(this.getFeatureGroups());
} catch (error) {
if (!isTestEnvironment()) {
console.warn("Error in performModifierSubtract:", error);
}
}
}
getEdgeDeletionModifierKey() {
const { keys } = this.config.interaction.edgeDeletion;
const userAgent = navigator.userAgent.toLowerCase();
const isMac = userAgent.includes("mac");
const isWindows = userAgent.includes("windows");
if (isMac && keys.mac) return keys.mac;
if (isWindows && keys.windows) return keys.windows;
if (keys.linux) return keys.linux;
return isMac ? "metaKey" : "ctrlKey";
}
isEdgeDeletionModifierKeyPressed(event) {
if (isTouchDevice()) {
return false;
}
const modifierKey = this.getEdgeDeletionModifierKey();
return !!event[modifierKey];
}
registerPolygonLayerMappings(featureGroup) {
featureGroup.eachLayer((layer) => {
if (layer instanceof L.Polygon) {
this.polygonFeatureGroupMap.set(layer, featureGroup);
}
});
}
findFeatureGroupForPolygon(polygon2) {
for (const featureGroup of this.getFeatureGroups()) {
let found = false;
featureGroup.eachLayer((layer) => {
if (!found && layer === polygon2) {
found = true;
}
});
if (found) {
return featureGroup;
}
}
return void 0;
}
onMarkerHoverForEdgeDeletion(marker, isHovering) {
const element = marker.getElement();
if (!element) return;
if (isHovering) {
const checkModifierAndUpdate = (e) => {
const isModifierPressed = this.isEdgeDeletionModifierKeyPressed(e);
if (isModifierPressed) {
element.style.backgroundColor = this.config.styles.ui.edgeDeletion.color;
element.style.borderColor = this.config.styles.ui.edgeDeletion.color;
element.classList.add("edge-deletion-hover");
try {
const container = this.map.getContainer();
container.style.cursor = "pointer";
} catch {
}
} else {
element.style.backgroundColor = "";
element.style.borderColor = "";
element.classList.remove("edge-deletion-hover");
try {
const container = this.map.getContainer();
container.style.cursor = "";
} catch {
}
}
};
const initialEvent = new MouseEvent("mouseover");
checkModifierAndUpdate(initialEvent);
this.markerModifierHandlers.set(marker, checkModifierAndUpdate);
document.addEventListener("keydown", checkModifierAndUpdate);
document.addEventListener("keyup", checkModifierAndUpdate);
element.addEventListener("mousemove", checkModifierAndUpdate);
} else {
element.style.backgroundColor = "";
element.style.borderColor = "";
element.classList.remove("edge-deletion-hover");
try {
const container = this.map.getContainer();
container.style.cursor = "";
} catch {
}
const handler = this.markerModifierHandlers.get(marker);
if (handler) {
document.removeEventListener("keydown", handler);
document.removeEventListener("keyup", handler);
element.removeEventListener("mousemove", handler);
this.markerModifierHandlers.delete(marker);
}
}
}
// Helper methods
getMarkerIndex(latlngs, position) {
const bounds = PolyDrawUtil.getBounds(latlngs, Math.sqrt(2) / 2);
const compass = new Compass(
bounds.getSouth(),
bounds.getWest(),
bounds.getNorth(),
bounds.getEast()
);
const compassDirection = compass.getDirection(position);
const latLngPoint = {
lat: compassDirection.lat,
lng: compassDirection.lng
};
const targetPoint = this.turfHelper.getCoord(latLngPoint);
const fc = this.turfHelper.getFeaturePointCollection(latlngs);
const nearestPointIdx = this.turfHelper.getNearestPointIndex(
targetPoint,
fc
);
return nearestPointIdx;
}
ensureMarkerSeparation(polygonLength, markers) {
const enabledMarkers = [];
if (markers.menu.enabled) {
enabledMarkers.push({ type: "menu", index: markers.menu.index });
}
if (markers.delete.enabled) {
enabledMarkers.push({ type: "delete", index: markers.delete.index });
}
if (markers.info.enabled) {
enabledMarkers.push({ type: "info", index: markers.info.index });
}
if (enabledMarkers.length < 2) {
return {
menu: markers.menu.index,
delete: markers.delete.index,
info: markers.info.index
};
}
const resolvedIndices = { ...markers };
const usedIndices = /* @__PURE__ */ new Set();
const processingOrder = ["info", "delete", "menu"];
for (const markerType of processingOrder) {
const marker = resolvedIndices[markerType];
if (!marker.enabled) continue;
if (usedIndices.has(marker.index)) {
const newIndex = this.findAlternativeMarkerPosition(
polygonLength,
marker.index,
usedIndices
);
resolvedIndices[markerType].index = newIndex;
usedIndices.add(newIndex);
} else {
usedIndices.add(marker.index);
}
}
return {
menu: resolvedIndices.menu.index,
delete: resolvedIndices.delete.index,
info: resolvedIndices.info.index
};
}
findAlternativeMarkerPosition(polygonLength, originalIndex, usedIndices) {
const maxAttempts = polygonLength;
const step = Math.max(1, Math.floor(polygonLength / 8));
for (let attempt = 1; attempt < maxAttempts; attempt++) {
const candidates = [
(originalIndex + attempt * step) % polygonLength,
(originalIndex - attempt * step + polygonLength) % polygonLength
];
for (const candidate of candidates) {
if (!usedIndices.has(candidate)) {
return candidate;
}
}
}
for (let i = 0; i < polygonLength; i++) {
if (!usedIndices.has(i)) {
return i;
}
}
return originalIndex;
}
createDivIcon(processedClasses) {
return IconFactory.createDivIcon(processedClasses);
}
getLatLngInfoString(latlng) {
return "Latitude: " + latlng.lat + " Longitude: " + latlng.lng;
}
setMarkerVisibility(polygon2, visible) {
const featureGroup = this.getFeatureGroups().find((fg) => fg.hasLayer(polygon2));
if (!featureGroup) return;
featureGroup.eachLayer((layer) => {
if (layer instanceof L.Marker) {
const marker = layer;
const element = marker.getElement();
if (element) {
const behavior = this.config.interaction.drag.markerBehavior;
const duration = this.config.interaction.drag.markerAnimationDuration;
if (behavior === "hide") {
element.style.display = visible ? "" : "none";
} else if (behavior === "fade") {
element.style.transition = `opacity ${duration}ms ease`;
element.style.opacity = visible ? "1" : "0";
}
}
}
});
}
setTransformAuxiliaryVisibility(featureGroup, visible) {
const polygon2 = this.getPolygonLayer(featureGroup);
if (polygon2) {
this.setMarkerVisibility(polygon2, visible);
}
featureGroup.eachLayer((layer) => {
var _a2;
if (!this.isPolydrawAuxiliaryPolyline(layer)) {
return;
}
const element = (_a2 = layer.getElement) == null ? void 0 : _a2.call(layer);
if (!element) {
return;
}
if (!visible) {
if (!this.transformAuxiliaryDisplayValues.has(layer)) {
this.transformAuxiliaryDisplayValues.set(layer, element.style.display);
}
element.style.display = "none";
return;
}
if (this.transformAuxiliaryDisplayValues.has(layer)) {
element.style.display = this.transformAuxiliaryDisplayValues.get(layer) ?? "";
this.transformAuxiliaryDisplayValues.delete(layer);
}
});
}
isPolydrawAuxiliaryPolyline(layer) {
return layer instanceof L.Polyline && !(layer instanceof L.Polygon) && layer._polydrawAuxiliaryLayer === true;
}
getBuiltInMenuButtonConfigs() {
const menuOps = this.config.polygonTools;
return [
{
enabled: menuOps.simplify.enabled,
id: "simplify",
label: "Simplify",
classNames: ["simplify"]
},
{
enabled: menuOps.doubleElbows.enabled,
id: "doubleElbows",
label: "DoubleElbows",
classNames: ["double-elbows"]
},
{
enabled: menuOps.bbox.enabled,
id: "bbox",
label: "Bounding box",
classNames: ["bbox"]
},
{
enabled: menuOps.bezier.enabled,
id: "bezier",
label: "Curve",
classNames: ["bezier"]
},
{
enabled: menuOps.scale.enabled,
id: "scale",
label: "Scale",
classNames: ["transform-scale"]
},
{
enabled: menuOps.rotate.enabled,
id: "rotate",
label: "Rotate",
classNames: ["transform-rotate"]
}
];
}
getBuiltInMenuButtons(featureGroup) {
var _a2;
const buttons = this.getBuiltInMenuButtonConfigs().filter(({ enabled }) => enabled).map(({ id, label, classNames }) => PopupFactory.createMenuButton(id, label, classNames));
const menuOps = this.config.polygonTools;
if (((_a2 = menuOps.donut) == null ? void 0 : _a2.enabled) && !this.donutTargetHasHoles(featureGroup)) {
buttons.push(PopupFactory.createMenuButton("donut", "Donut", ["transform-donut"]));
}
const optimizationButton = this.getVisualOptimizationToggleButton(featureGroup);
if (optimizationButton) {
buttons.push(optimizationButton);
}
return buttons;
}
getVisualOptimizationToggleButton(featureGroup) {
var _a2;
if (!((_a2 = this.config.polygonTools.visualOptimizationToggle) == null ? void 0 : _a2.enabled)) {
return null;
}
const { level, original } = this.getOptimizationMetadataFromFeatureGroup(featureGroup);
const hasOptimization = original > 0 || level > 0;
if (!hasOptimization) {
return null;
}
const isOptimized = typeof level === "number" && level > 0;
const optimizationStateClass = isOptimized ? "visual-optimization-state-hidden" : "visual-optimization-state-visible";
const toggleTitle = isOptimized ? "Show all markers" : "Hide extra markers";
return PopupFactory.createMenuButton("toggleOptimization", toggleTitle, [
"toggle-visual-optimization",
optimizationStateClass
]);
}
getPolygonMenuActionButtons(featureGroup) {
const { menuActions } = this.config.polygonTools;
if (!Array.isArray(menuActions)) {
return [];
}
return menuActions.filter(
(polygonMenuAction) => this.shouldShowPolygonMenuActionButton(polygonMenuAction, featureGroup)
).map(
(polygonMenuAction) => PopupFactory.createMenuButton(
polygonMenuAction.id,
polygonMenuAction.label,
this.getPolygonMenuActionClassNames(polygonMenuAction)
)
);
}
shouldShowPolygonMenuActionButton(polygonMenuAction, featureGroup) {
if (!polygonMenuAction.id || BUILT_IN_POLYGON_MENU_ACTION_IDS.has(polygonMenuAction.id)) {
return false;
}
if (!polygonMenuAction.visible) {
return true;
}
try {
return polygonMenuAction.visible(this.getMenuActionContext(featureGroup));
} catch {
return false;
}
}
getPolygonMenuActionClassNames(polygonMenuAction) {
if (Array.isArray(polygonMenuAction.className)) {
return polygonMenuAction.className;
}
if (polygonMenuAction.className) {
return [polygonMenuAction.className];
}
return ["menu-action-default"];
}
closeOpenMenuPopup() {
if (this._openMenuPopup) {
this.map.closePopup(this._openMenuPopup);
this._openMenuPopup = null;
}
}
attachMenuPopupCloseHandler(outerWrapper) {
const closeBtn = outerWrapper.querySelector(".marker-menu-close");
if (!closeBtn) {
return;
}
const handleClose = (event) => {
event.stopPropagation();
event.preventDefault();
if (this._openMenuPopup) {
this.closeOpenMenuPopup();
} else {
this.map.closePopup();
}
};
closeBtn.addEventListener("click", handleClose);
closeBtn.addEventListener("touchend", handleClose, { passive: false });
}
emitBuiltInPolygonMenuAction(action, latLngs, featureGroup) {
this.eventManager.emit("polydraw:menu:action", {
action,
latLngs,
featureGroup
});
this.closeOpenMenuPopup();
}
attachBuiltInMenuActionHandler(button, action, latLngs, featureGroup) {
const handler = (event) => {
event.preventDefault();
event.stopPropagation();
this.emitBuiltInPolygonMenuAction(action, latLngs, featureGroup);
};
button.addEventListener("touchend", handler, { passive: false });
button.onclick = handler;
}
attachTransformHandler(button, mode, featureGroup) {
const handler = (event) => {
event.preventDefault();
event.stopPropagation();
this.startPolygonTransform(featureGroup, mode);
this.closeOpenMenuPopup();
};
button.addEventListener("touchend", handler, { passive: false });
button.onclick = handler;
}
attachOptimizationToggleHandler(button, featureGroup) {
const handler = (event) => {
event.preventDefault();
event.stopPropagation();
this.toggleOptimizationVisibility(featureGroup);
this.closeOpenMenuPopup();
};
button.addEventListener("touchend", handler, { passive: false });
button.onclick = handler;
}
attachCustomPolygonMenuActionHandler(button, actionId, latLngs, featureGroup) {
var _a2;
const polygonMenuAction = (_a2 = this.config.polygonTools.menuActions) == null ? void 0 : _a2.find(
({ id }) => id === actionId
);
if (!polygonMenuAction) {
return;
}
const handler = (event) => {
event.preventDefault();
event.stopPropagation();
this.eventManager.emit("polydraw:menu:action", {
action: "polygonMenuAction",
menuActionId: actionId,
latLngs,
featureGroup
});
this.closeOpenMenuPopup();
};
button.addEventListener("touchend", handler, { passive: false });
button.onclick = handler;
}
attachMenuButtonHandler(button, latLngs, featureGroup) {
const actionId = button.dataset.actionId;
switch (actionId) {
case "simplify":
case "doubleElbows":
case "bbox":
case "bezier":
this.attachBuiltInMenuActionHandler(button, actionId, latLngs, featureGroup);
return;
case "scale":
case "rotate":
case "donut":
this.attachTransformHandler(button, actionId, featureGroup);
return;
case "toggleOptimization":
this.attachOptimizationToggleHandler(button, featureGroup);
return;
default:
if (actionId) {
this.attachCustomPolygonMenuActionHandler(button, actionId, latLngs, featureGroup);
}
}
}
startPolygonTransform(featureGroup, mode) {
const existing = this.transformControllers.get(featureGroup);
if (existing) {
existing.cancel();
existing.destroy();
this.transformControllers.delete(featureGroup);
}
if (mode === "donut" && this.donutTargetHasHoles(featureGroup)) {
return;
}
try {
const controller = new PolygonTransformController(
this.map,
featureGroup,
mode,
this.config.polygonTools.donut.direction,
(confirmed) => this.handleTransformControllerResult(controller, featureGroup, mode, confirmed)
);
this.transformControllers.set(featureGroup, controller);
this.setTransformAuxiliaryVisibility(featureGroup, false);
this.transformModeActive = true;
} catch {
}
}
handleTransformControllerResult(controller, featureGroup, mode, confirmed) {
if (this.transformControllers.get(featureGroup) === controller) {
this.transformControllers.delete(featureGroup);
}
this.transformModeActive = false;
const polygonLayer = this.getPolygonLayer(featureGroup);
if (!polygonLayer) {
return;
}
if (!confirmed) {
this.setTransformAuxiliaryVisibility(featureGroup, true);
return;
}
const { level: optimizationLevel, original: originalOptimizationLevel } = this.getOptimizationMetadataFromFeatureGroup(featureGroup);
if (mode === "donut") {
this.handleDonutCommit(
controller,
featureGroup,
polygonLayer,
optimizationLevel,
originalOptimizationLevel
);
return;
}
if (this.saveHistoryState) {
this.saveHistoryState(mode);
}
const newGeoJSON = polygonLayer.toGeoJSON();
const featureMetadataState = this.getFeatureMetadataState(featureGroup);
this.cleanupFeatureGroup(featureGroup);
this.removeFeatureGroup(featureGroup);
this.emitPolygonUpdated({
operation: "transform",
polygon: this.turfHelper.getTurfPolygon(newGeoJSON),
allowMerge: true,
optimizationLevel,
originalOptimizationLevel,
featureId: featureMetadataState.featureId,
featureMetadata: featureMetadataState.metadata,
sourceFeatureIds: featureMetadataState.sourceFeatureIds,
featureInteractionOverride: featureMetadataState.interactionOverride,
featureStyleOverrides: featureMetadataState.styleOverrides,
featureCreatedAt: featureMetadataState.createdAt
});
}
getPolygonLayer(featureGroup) {
return featureGroup.getLayers().find((layer) => layer instanceof L.Polygon);
}
attachMenuButtonHandlers(buttons, latLngs, featureGroup) {
buttons.forEach((button) => this.attachMenuButtonHandler(button, latLngs, featureGroup));
}
enableMenuButtonPointerEvents(outerWrapper) {
outerWrapper.querySelectorAll(".marker-menu-button").forEach((btn) => {
btn.style.pointerEvents = "auto";
btn.addEventListener("click", (event) => event.stopPropagation());
});
}
generateMenuMarkerPopup(latLngs, featureGroup) {
const buttons = [
...this.getBuiltInMenuButtons(featureGroup),
...this.getPolygonMenuActionButtons(featureGroup)
];
const outerWrapper = PopupFactory.buildMenuPopup(buttons);
this.attachMenuPopupCloseHandler(outerWrapper);
this.attachMenuButtonHandlers(buttons, latLngs, featureGroup);
L.DomEvent.disableClickPropagation(outerWrapper);
outerWrapper.style.pointerEvents = "auto";
this.enableMenuButtonPointerEvents(outerWrapper);
const isMobile = globalThis.innerWidth <= 600;
const versionClass = LeafletVersionDetector.isV1() ? " leaflet-v1" : " leaflet-v2";
const popup = leafletAdapter.createPopup({
closeButton: false,
autoClose: true,
className: `menu-popup${isMobile ? " mobile-popup" : ""}${versionClass}`
}).setContent(outerWrapper);
this._openMenuPopup = popup;
return popup;
}
getMenuActionContext(featureGroup) {
const completePolygon = this.getPolygonGeoJSONFromFeatureGroup(featureGroup);
const polygonLayer = featureGroup.getLayers().find((layer) => layer instanceof L.Polygon);
return {
polygon: this.turfHelper.getTurfPolygon(completePolygon),
featureGroup,
bounds: polygonLayer ? polygonLayer.getBounds() : leafletAdapter.createLatLngBounds()
};
}
toggleOptimizationVisibility(featureGroup) {
const polygonLayer = featureGroup.getLayers().find((l) => l instanceof L.Polygon);
if (!polygonLayer) {
return;
}
const metadata = this.getOptimizationMetadataFromFeatureGroup(featureGroup);
if (!metadata.original && !metadata.level) {
return;
}
const targetLevel = metadata.level > 0 ? 0 : metadata.original > 0 ? metadata.original : metadata.level;
if (targetLevel === metadata.level) {
return;
}
const newGeoJSON = polygonLayer.toGeoJSON();
const featureMetadataState = this.getFeatureMetadataState(featureGroup);
this.cleanupFeatureGroup(featureGroup);
this.removeFeatureGroup(featureGroup);
this.emitPolygonUpdated({
operation: "toggleOptimization",
polygon: this.turfHelper.getTurfPolygon(newGeoJSON),
allowMerge: true,
optimizationLevel: targetLevel,
originalOptimizationLevel: metadata.original || targetLevel,
featureId: featureMetadataState.featureId,
featureMetadata: featureMetadataState.metadata,
sourceFeatureIds: featureMetadataState.sourceFeatureIds,
featureInteractionOverride: featureMetadataState.interactionOverride,
featureStyleOverrides: featureMetadataState.styleOverrides,
featureCreatedAt: featureMetadataState.createdAt
});
}
getPolygonGeoJSONFromFeatureGroup(featureGroup) {
try {
const polygonLayer = featureGroup.getLayers().find((layer) => layer instanceof L.Polygon);
if (!polygonLayer) {
throw new Error("No polygon found in feature group");
}
return polygonLayer.toGeoJSON();
} catch (error) {
if (!isTestEnvironment()) {
if (error instanceof Error) {
console.warn("Error getting polygon GeoJSON from feature group:", error.message);
} else {
console.warn("Error getting polygon GeoJSON from feature group:", error);
}
}
return {
type: "Feature",
geometry: {
type: "Polygon",
coordinates: [
[
[0, 0],
[0, 1],
[1, 1],
[1, 0],
[0, 0]
]
]
},
properties: {}
};
}
}
getTotalPolygonPerimeter(polygonGeoJSON) {
try {
if (!polygonGeoJSON || !polygonGeoJSON.geometry) {
return 0;
}
let totalPerimeter = 0;
if (polygonGeoJSON.geometry.type === "Polygon") {
const coordinates = polygonGeoJSON.geometry.coordinates;
for (const ring of coordinates) {
const ringPolygon = {
type: "Feature",
geometry: {
type: "Polygon",
coordinates: [ring]
},
properties: {}
};
const ringPerimeter = this.turfHelper.getPolygonPerimeter(ringPolygon);
totalPerimeter += ringPerimeter;
}
} else if (polygonGeoJSON.geometry.type === "MultiPolygon") {
const coordinates = polygonGeoJSON.geometry.coordinates;
for (const polygonCoords of coordinates) {
for (const ring of polygonCoords) {
const ringPolygon = {
type: "Feature",
geometry: {
type: "Polygon",
coordinates: [ring]
},
properties: {}
};
const ringPerimeter = this.turfHelper.getPolygonPerimeter(ringPolygon);
totalPerimeter += ringPerimeter;
}
}
}
return totalPerimeter * 1e3;
} catch (error) {
if (!isTestEnvironment()) {
if (error instanceof Error) {
console.warn("Error calculating total polygon perimeter:", error.message);
} else {
console.warn("Error calculating total polygon perimeter:", error);
}
}
return this.turfHelper.getPolygonPerimeter(polygonGeoJSON) * 1e3;
}
}
generateInfoMarkerPopup(area2, perimeter) {
var _a2, _b2;
const _perimeter = new Perimeter(perimeter, this.config);
const _area = new Area(area2, this.config);
const infoConfig = this.config.markers.markerInfoIcon ?? {};
const showArea = infoConfig.showArea !== false;
const showPerimeter = infoConfig.showPerimeter !== false;
const useMetricUnits = infoConfig.useMetrics !== false;
const areaLabel = (_a2 = infoConfig.areaLabel) == null ? void 0 : _a2.trim();
const perimeterLabel = (_b2 = infoConfig.perimeterLabel) == null ? void 0 : _b2.trim();
const outerWrapper = document.createElement("div");
outerWrapper.classList.add("info-marker-outer-wrapper");
const wrapper = document.createElement("div");
wrapper.classList.add("info-marker-wrapper");
const markerContent = document.createElement("div");
markerContent.classList.add("content");
const infoContentWrapper = document.createElement("div");
infoContentWrapper.classList.add("info-marker-content");
if (showArea) {
const areaValue = useMetricUnits ? `${_area.metricArea} ${_area.metricUnit}` : `${_area.imperialArea} ${_area.imperialUnit}`;
const areaDiv = document.createElement("div");
areaDiv.classList.add("info-item", "area");
areaDiv.innerHTML = areaLabel && areaLabel.length > 0 ? `<strong>${areaLabel}:</strong> ${areaValue}` : areaValue;
infoContentWrapper.appendChild(areaDiv);
}
if (showPerimeter) {
const perimeterValue = useMetricUnits ? `${_perimeter.metricLength} ${_perimeter.metricUnit}` : `${_perimeter.imperialLength} ${_perimeter.imperialUnit}`;
const perimeterDiv = document.createElement("div");
perimeterDiv.classList.add("info-item", "perimeter");
perimeterDiv.innerHTML = perimeterLabel && perimeterLabel.length > 0 ? `<strong>${perimeterLabel}:</strong> ${perimeterValue}` : perimeterValue;
infoContentWrapper.appendChild(perimeterDiv);
}
markerContent.appendChild(infoContentWrapper);
outerWrapper.appendChild(wrapper);
wrapper.appendChild(markerContent);
L.DomEvent.disableClickPropagation(outerWrapper);
outerWrapper.style.pointerEvents = "auto";
outerWrapper.querySelectorAll("button").forEach((btn) => {
btn.style.pointerEvents = "auto";
btn.addEventListener("click", (e) => e.stopPropagation());
});
const isMobile = window.innerWidth <= 600;
const popup = leafletAdapter.createPopup({
closeButton: true,
autoClose: true,
className: `info-popup${isMobile ? " mobile-popup" : ""}`
}).setContent(outerWrapper);
return popup;
}
}
class PolygonMutationManager {
constructor(dependencies) {
__publicField(this, "turfHelper");
__publicField(this, "map");
__publicField(this, "config");
__publicField(this, "eventManager");
__publicField(this, "getFeatureGroups");
__publicField(this, "saveHistoryState");
__publicField(this, "layerManager");
// Specialized managers
__publicField(this, "geometryManager");
__publicField(this, "interactionManager");
__publicField(this, "onDrawCancel", (data) => {
this.emit("drawCancelled", data);
});
__publicField(this, "onPolygonUpdated", (data) => {
void this.handlePolygonModified(data);
});
__publicField(this, "onMenuAction", (data) => {
void this.handleMenuAction(data).catch((error) => {
if (!isTestEnvironment()) {
const message = error instanceof Error ? error.message : String(error);
console.warn(`Menu action failed: ${message}`);
}
});
});
__publicField(this, "onCheckIntersection", (data) => {
const hasIntersection = this.geometryManager.checkPolygonIntersection(
data.polygon1,
data.polygon2
);
data.callback(hasIntersection);
});
__publicField(this, "onSubtract", (data) => {
void this.subtractPolygon(data.subtractPolygon);
});
__publicField(this, "onPolygonDeleted", () => {
this.emit("polygonDeleted", void 0);
});
this.turfHelper = dependencies.turfHelper;
this.map = dependencies.map;
this.config = dependencies.config;
this.eventManager = dependencies.eventManager;
this.getFeatureGroups = dependencies.getFeatureGroups;
this.saveHistoryState = dependencies.saveHistoryState;
this.layerManager = dependencies.layerManager;
this.initializeSpecializedManagers(dependencies);
}
/**
* Initialize the three specialized managers
*/
initializeSpecializedManagers(dependencies) {
this.geometryManager = new PolygonGeometryManager({
turfHelper: dependencies.turfHelper,
config: dependencies.config
});
this.interactionManager = new PolygonInteractionManager(
{
turfHelper: dependencies.turfHelper,
polygonInformation: dependencies.polygonInformation,
map: dependencies.map,
config: dependencies.config,
modeManager: dependencies.modeManager,
eventManager: this.eventManager,
saveHistoryState: dependencies.saveHistoryState,
layerManager: dependencies.layerManager
},
{
getFeatureGroups: this.getFeatureGroups,
addFeatureGroup: (fg) => this.addFeatureGroupInternal(fg),
removeFeatureGroup: (fg) => this.removeFeatureGroupInternal(fg)
}
);
this.setupEventForwarding();
}
/**
* Set up event forwarding from specialized managers to facade
*/
setupEventForwarding() {
this.eventManager.on("polydraw:draw:cancel", this.onDrawCancel);
this.eventManager.on("polydraw:polygon:updated", this.onPolygonUpdated);
this.eventManager.on("polydraw:menu:action", this.onMenuAction);
this.eventManager.on("polydraw:check:intersection", this.onCheckIntersection);
this.eventManager.on("polydraw:subtract", this.onSubtract);
this.eventManager.on("polydraw:polygon:deleted", this.onPolygonDeleted);
}
dispose() {
this.eventManager.off("polydraw:draw:cancel", this.onDrawCancel);
this.eventManager.off("polydraw:polygon:updated", this.onPolygonUpdated);
this.eventManager.off("polydraw:menu:action", this.onMenuAction);
this.eventManager.off("polydraw:check:intersection", this.onCheckIntersection);
this.eventManager.off("polydraw:subtract", this.onSubtract);
this.eventManager.off("polydraw:polygon:deleted", this.onPolygonDeleted);
this.interactionManager.dispose();
}
/**
* Handle polygon modification from interaction manager
*/
async handlePolygonModified(data) {
const skipSimplifyOperations = /* @__PURE__ */ new Set([
"addVertex",
"markerDrag",
"polygonDrag",
"polygonClone",
"toggleOptimization",
"donut"
]);
const shouldSimplify = !data.operation || !skipSimplifyOperations.has(data.operation);
let allowMerge = data.allowMerge;
if (data.intelligentMerge && data.operation === "markerDrag") {
allowMerge = this.shouldAllowIntelligentMerge(data.polygon);
}
const options = {
simplify: shouldSimplify,
noMerge: !allowMerge,
visualOptimizationLevel: data.optimizationLevel || 0,
originalOptimizationLevel: data.originalOptimizationLevel,
skipKinkProcessing: data.operation === "markerDrag",
featureId: data.featureId,
featureMetadata: data.featureMetadata,
sourceFeatureIds: data.sourceFeatureIds,
featureInteractionOverride: data.featureInteractionOverride,
featureStyleOverrides: data.featureStyleOverrides,
targetLayerId: data.targetLayerId,
featureCreatedAt: data.featureCreatedAt,
featureLastModified: data.featureLastModified
};
await this.addPolygon(data.polygon, options);
}
/**
* Handle menu actions from interaction manager
*/
async handleMenuAction(data) {
var _a2, _b2, _c, _d;
if (!(data == null ? void 0 : data.featureGroup)) {
return;
}
if (this.layerManager && !this.layerManager.isFeatureGroupEditable(data.featureGroup)) {
return;
}
let completePolygonGeoJSON;
try {
completePolygonGeoJSON = this.getCompletePolygonFromFeatureGroup(data.featureGroup);
} catch (error) {
if (!isTestEnvironment()) {
const message = error instanceof Error ? error.message : String(error);
console.warn(`Menu action skipped: unable to resolve polygon geometry (${message}).`);
}
return;
}
const { level: optimizationLevel, original: originalOptimizationLevel } = this.getOptimizationMetadataFromFeatureGroup(data.featureGroup);
const sourceMetadata = this.getFeatureMetadataStateFromFeatureGroup(data.featureGroup);
let result;
let shouldSaveHistory = true;
switch (data.action) {
case "simplify": {
result = this.geometryManager.simplifyPolygon(completePolygonGeoJSON);
break;
}
case "bbox": {
result = this.geometryManager.convertToBoundingBox(completePolygonGeoJSON);
break;
}
case "doubleElbows": {
result = this.geometryManager.doubleElbowsPolygon(completePolygonGeoJSON);
break;
}
case "bezier": {
result = this.geometryManager.bezierifyPolygon(completePolygonGeoJSON);
break;
}
case "polygonMenuAction": {
result = await this.executePolygonMenuAction(
data.menuActionId,
completePolygonGeoJSON,
data.featureGroup
);
const polygonMenuAction = data.menuActionId && this.config.polygonTools.menuActions ? this.config.polygonTools.menuActions.find((a) => a.id === data.menuActionId) : void 0;
shouldSaveHistory = (polygonMenuAction == null ? void 0 : polygonMenuAction.history) !== false;
break;
}
default:
return;
}
if (result.success && result.result) {
if (this.saveHistoryState && shouldSaveHistory) {
this.saveHistoryState(data.action);
}
this.removeFeatureGroupInternal(data.featureGroup);
const bezierLevel = data.action === "bezier" ? ((_a2 = this.config.polygonTools.bezier) == null ? void 0 : _a2.visualOptimizationLevel) ?? 10 : optimizationLevel;
const bezierOriginal = data.action === "bezier" ? ((_b2 = this.config.polygonTools.bezier) == null ? void 0 : _b2.visualOptimizationLevel) ?? 10 : originalOptimizationLevel;
const noMerge = result.allowMerge === false;
const shouldSimplify = result.simplify === true;
const resultMetadata = result.metadata ?? sourceMetadata.metadata;
const addResult = await this.addPolygon(result.result, {
simplify: shouldSimplify,
noMerge,
visualOptimizationLevel: bezierLevel,
originalOptimizationLevel: bezierOriginal,
featureId: sourceMetadata.featureId,
featureMetadata: resultMetadata,
sourceFeatureIds: sourceMetadata.sourceFeatureIds,
featureInteractionOverride: sourceMetadata.interactionOverride,
featureStyleOverrides: sourceMetadata.styleOverrides,
featureCreatedAt: sourceMetadata.createdAt
});
if (data.action === "bezier" && ((_c = this.config.polygonTools.bezier) == null ? void 0 : _c.ghostMarkers)) {
const ghostOpacity = 1e-3;
(_d = addResult.featureGroups) == null ? void 0 : _d.forEach((featureGroup) => {
featureGroup.eachLayer((layer) => {
if (layer instanceof L.Polygon) {
const polygon2 = layer;
polygon2.setStyle({ opacity: ghostOpacity, fillOpacity: ghostOpacity });
if (polygon2._polydrawDragData) {
polygon2._polydrawDragData.originalOpacity = ghostOpacity;
}
}
});
});
}
}
}
/**
* Handle polygon menu action execution
*/
async executePolygonMenuAction(menuActionId, polygon2, featureGroup) {
if (!menuActionId || !this.config.polygonTools.menuActions) {
return { success: false, error: "Polygon menu action not found" };
}
const polygonMenuAction = this.config.polygonTools.menuActions.find(
(a) => a.id === menuActionId
);
if (!polygonMenuAction) {
return { success: false, error: "Polygon menu action not found" };
}
try {
const actionResult = await polygonMenuAction.apply({
polygon: polygon2,
featureGroup,
bounds: this.getBoundsForMenuAction(featureGroup, polygon2)
});
const normalizedResult = this.normalizeMenuActionResult(actionResult);
if (!normalizedResult) {
return { success: false, error: "No change" };
}
if (!this.isPolygonFeature(normalizedResult.polygon)) {
return { success: false, error: "Invalid polygon geometry" };
}
return {
success: true,
result: normalizedResult.polygon,
allowMerge: normalizedResult.allowMerge,
simplify: normalizedResult.simplify,
metadata: normalizedResult.metadata
};
} catch (error) {
if (!isTestEnvironment()) {
const message = error instanceof Error ? error.message : String(error);
console.warn(`Polygon menu action failed: ${message}`);
}
return { success: false, error: "Polygon menu action execution failed" };
}
}
normalizeMenuActionResult(actionResult) {
if (actionResult === null || actionResult === void 0 || typeof actionResult !== "object") {
return void 0;
}
if (this.isPolygonFeature(actionResult)) {
return { polygon: actionResult };
}
if ("polygon" in actionResult && this.isPolygonFeature(actionResult.polygon)) {
return {
polygon: actionResult.polygon,
allowMerge: actionResult.allowMerge,
simplify: actionResult.simplify,
metadata: actionResult.metadata
};
}
return void 0;
}
isPolygonFeature(value) {
if (!value || typeof value !== "object") {
return false;
}
const feature2 = value;
return feature2.type === "Feature" && !!feature2.geometry && (feature2.geometry.type === "Polygon" || feature2.geometry.type === "MultiPolygon");
}
getBoundsForMenuAction(featureGroup, polygon2) {
const polygonLayer = featureGroup.getLayers().find((layer) => layer instanceof L.Polygon);
if (polygonLayer) {
return polygonLayer.getBounds();
}
const bounds = leafletAdapter.createLatLngBounds();
const extend2 = (coord) => {
if (typeof coord[0] === "number" && typeof coord[1] === "number") {
bounds.extend(leafletAdapter.createLatLng(coord[1], coord[0]));
}
};
if (polygon2.geometry.type === "Polygon") {
polygon2.geometry.coordinates.forEach((ring) => ring.forEach(extend2));
} else {
polygon2.geometry.coordinates.forEach((poly) => poly.forEach((ring) => ring.forEach(extend2)));
}
return bounds;
}
/**
* Get the polygon interaction manager (for testing)
*/
get polygonInteractionManager() {
return this.interactionManager;
}
/**
* Add event listener
*/
on(event, callback) {
this.eventManager.on(event, callback);
}
/**
* Emit event to all listeners
*/
emit(event, data) {
this.eventManager.emit(event, data);
}
/**
* Add a polygon with optional merging logic
*/
async addPolygon(latlngs, options = {}) {
if (this.layerManager && options.targetLayerId) {
const targetLayer = this.layerManager.getLayer(options.targetLayerId);
if (!targetLayer) {
return {
success: false,
error: `Layer "${options.targetLayerId}" does not exist`
};
}
}
const { polygons, skipMerge } = this.preparePolygonsForAddition(latlngs, options);
const aggregatedFeatureGroups = [];
for (const polygon2 of polygons) {
const result = await this.addPolygonWithMergeHandling(polygon2, options, skipMerge);
if (!result.success) {
return result;
}
if (result.featureGroups && result.featureGroups.length > 0) {
aggregatedFeatureGroups.push(...result.featureGroups);
}
}
return {
success: true,
featureGroups: aggregatedFeatureGroups.length > 0 ? aggregatedFeatureGroups : void 0
};
}
async addPolygonWithMergeHandling(latlngs, options, skipMerge) {
const { noMerge = false } = options;
try {
const isInsideHole = this.isPolygonInsideExistingHole(latlngs);
if (isInsideHole) {
return await this.addPolygonLayer(latlngs, options);
}
if (this.config.mergePolygons && !noMerge && this.getFeatureGroups().length > 0 && !skipMerge) {
return await this.mergePolygon(latlngs, options);
} else {
return await this.addPolygonLayer(latlngs, options);
}
} catch (error) {
return {
success: false,
error: error instanceof Error ? error.message : "Unknown error in addPolygon"
};
}
}
preparePolygonsForAddition(latlngs, options = {}) {
let hasSelfIntersections = false;
try {
hasSelfIntersections = this.turfHelper.hasKinks(latlngs);
} catch (error) {
if (!isTestEnvironment()) {
console.warn(
"Error detecting polygon kinks:",
error instanceof Error ? error.message : String(error)
);
}
}
const skipKinks = options.skipKinkProcessing === true;
if (!skipKinks && !this.config.kinks && hasSelfIntersections) {
try {
const kinkFreePolygons = this.turfHelper.getKinks(latlngs);
if (kinkFreePolygons.length > 0) {
return {
polygons: kinkFreePolygons,
skipMerge: false
};
}
} catch (error) {
if (!isTestEnvironment()) {
console.warn(
"Error splitting polygon kinks:",
error instanceof Error ? error.message : String(error)
);
}
}
}
return {
polygons: [latlngs],
skipMerge: this.config.kinks && hasSelfIntersections
};
}
/**
* Subtract a polygon from existing polygons
*/
async subtractPolygon(latlngs, options = {}) {
const { simplify: simplify3 = true, visualOptimizationLevel, originalOptimizationLevel } = options;
try {
const intersectingFeatureGroups = [];
const layerManager = this.layerManager;
const targetFgs = layerManager ? layerManager.getFeatureGroupsForLayer(options.targetLayerId || layerManager.getActiveLayerId()).filter((featureGroup) => layerManager.isFeatureGroupEditable(featureGroup)) : this.getFeatureGroups();
targetFgs.forEach((featureGroup) => {
try {
const featureCollection2 = featureGroup.toGeoJSON();
if (!featureCollection2 || !featureCollection2.features || !featureCollection2.features[0]) {
return;
}
const firstFeature = featureCollection2.features[0];
if (!firstFeature.geometry || !firstFeature.geometry.coordinates) {
return;
}
const existingPolygon = this.turfHelper.getTurfPolygon(firstFeature);
const hasIntersection = this.geometryManager.checkPolygonIntersection(
existingPolygon,
latlngs
);
if (hasIntersection) {
intersectingFeatureGroups.push(featureGroup);
}
} catch (error) {
if (!isTestEnvironment()) {
console.warn("Skipping feature group during intersection check:", error);
}
}
});
const resultFeatureGroups = [];
for (const featureGroup of intersectingFeatureGroups) {
try {
const sourceMetadata = this.getFeatureMetadataStateFromFeatureGroup(featureGroup);
const featureCollection2 = featureGroup.toGeoJSON();
const feature2 = this.turfHelper.getTurfPolygon(featureCollection2.features[0]);
const result = this.geometryManager.subtractPolygon(feature2, latlngs);
this.removeFeatureGroupInternal(featureGroup);
if (result.success && result.results) {
for (const resultPolygon of result.results) {
const addResult = await this.addPolygonLayer(resultPolygon, {
simplify: simplify3,
visualOptimizationLevel,
originalOptimizationLevel,
featureMetadata: sourceMetadata.metadata,
sourceFeatureIds: sourceMetadata.sourceFeatureIds,
featureInteractionOverride: sourceMetadata.interactionOverride,
featureStyleOverrides: sourceMetadata.styleOverrides,
featureCreatedAt: sourceMetadata.createdAt
});
if (addResult.success && addResult.featureGroups) {
resultFeatureGroups.push(...addResult.featureGroups);
}
}
}
} catch (error) {
if (!isTestEnvironment()) {
console.warn("Failed subtract operation for one feature group:", error);
}
}
}
this.emit("polygonSubtracted", {
subtractedPolygon: latlngs,
affectedFeatureGroups: intersectingFeatureGroups,
resultFeatureGroups
});
this.emit("polygonOperationComplete", {
operation: "subtract",
polygon: latlngs,
resultFeatureGroups
});
return {
success: true,
featureGroups: resultFeatureGroups
};
} catch (error) {
return {
success: false,
error: error instanceof Error ? error.message : "Unknown error in subtractPolygon"
};
}
}
/**
* Create and add a polygon layer with all markers and interactions
*/
async addPolygonLayer(latlngs, options = {}) {
const {
simplify: simplify3 = true,
dynamicTolerance = false,
visualOptimizationLevel = 0,
originalOptimizationLevel,
targetLayerId,
layerColor,
featureId: featureId2,
featureMetadata,
sourceFeatureIds,
featureInteractionOverride,
featureStyleOverrides,
featureCreatedAt,
featureLastModified
} = options;
try {
if (!latlngs || !latlngs.geometry || !latlngs.geometry.coordinates) {
return { success: false, error: "Invalid polygon data" };
}
const resolvedLayerId = this.layerManager ? targetLayerId || this.layerManager.getActiveLayerId() : void 0;
const isLayerEditable = !this.layerManager || !resolvedLayerId ? true : this.layerManager.isLayerEditable(resolvedLayerId);
const isEditableLayer = featureInteractionOverride === void 0 ? isLayerEditable : featureInteractionOverride === "editable";
let resolvedLayerColor = layerColor;
if (!resolvedLayerColor && this.layerManager && resolvedLayerId) {
const layer = this.layerManager.getLayer(resolvedLayerId);
if (layer) {
resolvedLayerColor = layer.color;
}
} else if (!resolvedLayerColor && this.layerManager) {
const activeLayer = this.layerManager.getActiveLayer();
if (activeLayer && this.layerManager.getLayerCount() > 1) {
resolvedLayerColor = activeLayer.color;
}
}
const featureGroup = new L.FeatureGroup();
const latLngs = simplify3 ? this.turfHelper.getSimplified(latlngs, dynamicTolerance) : latlngs;
const normalizedLatLngs = this.normalizeSinglePolygonFeature(latLngs);
let polygon2;
let effectiveOriginal = visualOptimizationLevel;
try {
polygon2 = this.getPolygon(normalizedLatLngs, resolvedLayerColor, {
enableDragging: isEditableLayer,
styleOverrides: featureStyleOverrides
});
if (!polygon2) {
return { success: false, error: "Failed to create polygon" };
}
polygon2._polydrawOptimizationLevel = visualOptimizationLevel;
effectiveOriginal = originalOptimizationLevel ?? polygon2._polydrawOptimizationOriginalLevel ?? visualOptimizationLevel;
polygon2._polydrawOptimizationOriginalLevel = effectiveOriginal || 0;
featureGroup.addLayer(polygon2);
this.interactionManager.suppressDeleteMarkerClicks(250);
} catch {
return { success: false, error: "Failed to create polygon layer" };
}
this.setFeatureGroupMetadata(featureGroup, {
featureId: featureId2,
metadata: featureMetadata,
sourceFeatureIds,
interactionOverride: featureInteractionOverride,
styleOverrides: featureStyleOverrides,
optimizationLevel: visualOptimizationLevel,
originalOptimizationLevel: effectiveOriginal,
hasHoles: this.featureHasHoles(normalizedLatLngs),
layerId: resolvedLayerId,
createdAt: featureCreatedAt,
lastModified: featureLastModified
});
let markerLatlngs;
try {
const rawLatLngs = polygon2.getLatLngs();
markerLatlngs = this.normalizePolygonCoordinates(rawLatLngs);
} catch {
markerLatlngs = [];
}
if (isEditableLayer) {
try {
markerLatlngs.forEach((polygonRing, ringIndex) => {
if (!polygonRing || !Array.isArray(polygonRing)) {
return;
}
const latLngLiterals = [];
polygonRing.forEach((latlng) => {
if (Array.isArray(latlng) && latlng.length >= 2) {
latLngLiterals.push({ lat: latlng[1], lng: latlng[0] });
} else if (typeof latlng === "object" && latlng !== null && "lat" in latlng && "lng" in latlng) {
latLngLiterals.push(latlng);
}
});
if (latLngLiterals.length === 0) {
return;
}
try {
if (ringIndex === 0) {
this.interactionManager.addMarkers(latLngLiterals, featureGroup, {
optimizationLevel: visualOptimizationLevel,
originalOptimizationLevel: effectiveOriginal
});
} else {
const holePolyline = leafletAdapter.createPolyline(latLngLiterals, {
color: this.config.styles.hole.color,
weight: this.config.styles.hole.weight || 2,
opacity: this.config.styles.hole.opacity || 1,
fillColor: this.config.styles.hole.fillColor,
fillOpacity: this.config.styles.hole.fillOpacity || 0.5
});
holePolyline._polydrawAuxiliaryLayer = true;
featureGroup.addLayer(holePolyline);
this.interactionManager.addHoleMarkers(latLngLiterals, featureGroup);
}
} catch {
}
});
} catch {
}
try {
this.interactionManager.addEdgeClickListeners(polygon2, featureGroup);
} catch {
}
}
this.getFeatureGroups().push(featureGroup);
if (this.layerManager) {
const assignedLayerId = resolvedLayerId || this.layerManager.getActiveLayerId();
const assigned = this.layerManager.assignFeatureGroupToLayer(featureGroup, assignedLayerId);
if (!assigned) {
this.removeFeatureGroupInternal(featureGroup);
return {
success: false,
error: `Failed to assign polygon to layer "${assignedLayerId}"`
};
}
}
try {
featureGroup.addTo(this.map);
} catch {
}
this.interactionManager.updateMarkerDraggableState();
this.emit("polygonAdded", { polygon: normalizedLatLngs, featureGroup });
this.emit("polygonOperationComplete", {
operation: "add",
polygon: normalizedLatLngs,
featureGroup
});
return {
success: true,
featureGroups: [featureGroup]
};
} catch (error) {
return {
success: false,
error: error instanceof Error ? error.message : "Unknown error in addPolygonLayer"
};
}
}
/**
* Merge a polygon with existing intersecting polygons
*/
async mergePolygon(latlngs, options = {}) {
try {
const polygonFeature = [];
const intersectingFeatureGroups = [];
let polyIntersection = false;
const mergeEditableOnly = options.mergeEditableOnly !== false;
const layerManager = this.layerManager;
const targetFgs = layerManager ? layerManager.getFeatureGroupsForLayer(options.targetLayerId || layerManager.getActiveLayerId()).filter(
(featureGroup) => mergeEditableOnly ? layerManager.isFeatureGroupEditable(featureGroup) : true
) : this.getFeatureGroups();
targetFgs.forEach((featureGroup) => {
try {
const featureCollection2 = featureGroup.toGeoJSON();
if (!featureCollection2 || !featureCollection2.features || !featureCollection2.features[0]) {
return;
}
const firstFeature = featureCollection2.features[0];
if (!firstFeature.geometry || !firstFeature.geometry.coordinates) {
return;
}
if (this.isPositionArrayofArrays(firstFeature.geometry.coordinates)) {
firstFeature.geometry.coordinates.forEach(
(element) => {
try {
const feature2 = this.turfHelper.getMultiPolygon([element]);
polyIntersection = this.geometryManager.checkPolygonIntersection(
feature2,
latlngs
);
if (polyIntersection) {
intersectingFeatureGroups.push(featureGroup);
polygonFeature.push(feature2);
}
} catch {
}
}
);
} else {
try {
const feature2 = this.turfHelper.getTurfPolygon(firstFeature);
polyIntersection = this.geometryManager.checkPolygonIntersection(feature2, latlngs);
if (polyIntersection) {
intersectingFeatureGroups.push(featureGroup);
polygonFeature.push(feature2);
}
} catch {
}
}
} catch {
}
});
if (intersectingFeatureGroups.length > 0) {
return await this.unionPolygons(
intersectingFeatureGroups,
latlngs,
polygonFeature,
options
);
} else {
return await this.addPolygonLayer(latlngs, options);
}
} catch (error) {
return {
success: false,
error: error instanceof Error ? error.message : "Unknown error in mergePolygon"
};
}
}
/**
* Union multiple polygons together using geometry manager
*/
async unionPolygons(layers, latlngs, polygonFeature, options = {}) {
try {
const mergedSourceFeatureIds = this.collectSourceFeatureIds(layers, options.sourceFeatureIds);
const mergedFeatureMetadata = options.featureMetadata ?? this.resolvePrimaryFeatureMetadata(layers);
const mergedInteractionOverride = options.featureInteractionOverride ?? this.resolvePrimaryFeatureInteractionOverride(layers);
const mergedStyleOverrides = options.featureStyleOverrides ?? this.resolvePrimaryFeatureStyleOverrides(layers);
layers.forEach((featureGroup) => {
this.removeFeatureGroupInternal(featureGroup);
});
const result = this.geometryManager.unionPolygons(polygonFeature, latlngs);
if (result.success && result.result) {
const addResult = await this.addPolygonLayer(result.result, {
...options,
featureMetadata: mergedFeatureMetadata,
sourceFeatureIds: mergedSourceFeatureIds,
featureInteractionOverride: mergedInteractionOverride,
featureStyleOverrides: mergedStyleOverrides
});
this.emit("polygonsUnioned", {
originalPolygons: polygonFeature,
resultPolygon: result.result,
featureGroups: addResult.featureGroups
});
return addResult;
} else {
return {
success: false,
error: result.error || "Failed to union polygons"
};
}
} catch (error) {
return {
success: false,
error: error instanceof Error ? error.message : "Unknown error in unionPolygons"
};
}
}
/**
* Determine if intelligent merging should be allowed for a polygon
* This checks if the polygon actually intersects with existing polygons
*/
shouldAllowIntelligentMerge(polygon2) {
try {
const layerManager = this.layerManager;
const targetFgs = layerManager ? layerManager.getFeatureGroupsForLayer(layerManager.getActiveLayerId()).filter((featureGroup) => layerManager.isFeatureGroupEditable(featureGroup)) : this.getFeatureGroups();
for (const featureGroup of targetFgs) {
try {
const featureCollection2 = featureGroup.toGeoJSON();
if (!featureCollection2 || !featureCollection2.features || !featureCollection2.features[0]) {
continue;
}
const existingFeature = featureCollection2.features[0];
if (!existingFeature.geometry || !existingFeature.geometry.coordinates) {
continue;
}
const existingPolygon = this.turfHelper.getTurfPolygon(existingFeature);
if (this.geometryManager.checkPolygonIntersection(existingPolygon, polygon2)) {
return true;
}
} catch {
continue;
}
}
return false;
} catch {
return false;
}
}
/**
* Check if a polygon is completely inside a hole of an existing polygon
* This prevents incorrect merging when drawing polygons inside holes
*/
isPolygonInsideExistingHole(newPolygon) {
try {
const layerManager = this.layerManager;
const targetFgs = layerManager ? layerManager.getFeatureGroupsForLayer(layerManager.getActiveLayerId()).filter((featureGroup) => layerManager.isFeatureGroupEditable(featureGroup)) : this.getFeatureGroups();
for (const featureGroup of targetFgs) {
try {
const featureCollection2 = featureGroup.toGeoJSON();
if (!featureCollection2 || !featureCollection2.features || !featureCollection2.features[0]) {
continue;
}
const existingFeature = featureCollection2.features[0];
if (!existingFeature.geometry || !existingFeature.geometry.coordinates) {
continue;
}
let hasHoles = false;
let holes = [];
if (existingFeature.geometry.type === "Polygon") {
hasHoles = existingFeature.geometry.coordinates.length > 1;
if (hasHoles) {
holes = existingFeature.geometry.coordinates.slice(1);
}
} else if (existingFeature.geometry.type === "MultiPolygon") {
for (const polygonCoords of existingFeature.geometry.coordinates) {
if (polygonCoords.length > 1) {
hasHoles = true;
holes.push(...polygonCoords.slice(1));
}
}
}
if (hasHoles && holes.length > 0) {
for (const holeCoords of holes) {
try {
const holePolygon = this.turfHelper.createPolygon([holeCoords]);
if (this.turfHelper.isPolygonCompletelyWithin(newPolygon, holePolygon)) {
return true;
}
} catch {
continue;
}
}
}
} catch {
continue;
}
}
return false;
} catch {
return false;
}
}
/**
* Normalize polygon coordinates to handle complex nested structures
* This fixes the bug where markers are missing when drawing polygons inside holes
*/
normalizePolygonCoordinates(rawLatLngs) {
if (!Array.isArray(rawLatLngs) || rawLatLngs.length === 0) {
return [];
}
const isLatLngLike = (obj) => {
return obj !== null && typeof obj === "object" && "lat" in obj && "lng" in obj && typeof obj.lat === "number" && typeof obj.lng === "number";
};
const flattenToRings = (coords) => {
if (!Array.isArray(coords)) {
return [];
}
if (coords.length > 0 && isLatLngLike(coords[0])) {
return [coords];
}
const result = [];
for (const item of coords) {
if (Array.isArray(item)) {
if (item.length > 0 && isLatLngLike(item[0])) {
result.push(item);
} else {
const flattened = flattenToRings(item);
result.push(...flattened);
}
}
}
return result;
};
try {
return flattenToRings(rawLatLngs);
} catch {
return [];
}
}
/**
* Create a polygon from GeoJSON feature
*/
isPositionArrayofArrays(arr) {
if (!Array.isArray(arr) || arr.length === 0) return false;
const first = arr[0];
if (!Array.isArray(first) || first.length === 0) return false;
const second = first[0];
if (!Array.isArray(second) || second.length === 0) return false;
const leaf = second[0];
return this.isPosition(leaf);
}
isPosition(val) {
return Array.isArray(val) && val.length >= 2 && val.every((n) => typeof n === "number");
}
/**
* Create a polygon from GeoJSON feature
*/
getPolygon(latlngs, layerColor, options = {}) {
const polygon2 = L.GeoJSON.geometryToLayer(latlngs);
const effectiveColor = layerColor || this.config.styles.polygon.color;
const effectiveFillColor = this.config.styles.polygon.fillColor;
const { styleOverrides } = options;
const polygonStyle = {
...this.config.styles.polygon,
color: (styleOverrides == null ? void 0 : styleOverrides.color) ?? effectiveColor,
fillColor: (styleOverrides == null ? void 0 : styleOverrides.fillColor) ?? effectiveFillColor,
// Force these values to ensure they override any default styling
weight: (styleOverrides == null ? void 0 : styleOverrides.weight) ?? this.config.styles.polygon.weight ?? 2,
opacity: this.config.styles.polygon.opacity || 1,
fillOpacity: (styleOverrides == null ? void 0 : styleOverrides.fillOpacity) ?? this.config.styles.polygon.fillOpacity ?? 0.2
};
polygon2.setStyle(polygonStyle);
polygon2._polydrawUniqueId = leafletAdapter.util.stamp(polygon2) + "_" + Date.now();
delete polygon2._polydrawDragData;
delete polygon2._polydrawOriginalLatLngs;
delete polygon2._polydrawCurrentDragSession;
delete polygon2._polydrawOriginalMarkerPositions;
delete polygon2._polydrawOriginalHoleLinePositions;
const enableDragging = options.enableDragging !== false;
if (enableDragging && (this.config.modes.dragPolygons || this.config.tools.clone)) {
this.interactionManager.enablePolygonDragging(polygon2, latlngs);
}
return polygon2;
}
normalizeSinglePolygonFeature(feature2) {
if (feature2.geometry.type === "MultiPolygon" && feature2.geometry.coordinates.length === 1) {
const [coordinates] = feature2.geometry.coordinates;
try {
return this.turfHelper.createPolygon(coordinates);
} catch {
return feature2;
}
}
return feature2;
}
/**
* Internal method to add feature group to array
*/
addFeatureGroupInternal(featureGroup) {
this.getFeatureGroups().push(featureGroup);
}
/**
* Internal method to remove feature group from array and map
*/
removeFeatureGroupInternal(featureGroup) {
this.cleanupFeatureGroup(featureGroup);
if (this.layerManager) {
this.layerManager.removeFeatureGroupFromLayer(featureGroup);
}
try {
featureGroup.clearLayers();
} catch (error) {
if (!isTestEnvironment()) {
console.warn("Error clearing layers from feature group:", error);
}
}
const featureGroups = this.getFeatureGroups();
const index = featureGroups.indexOf(featureGroup);
if (index > -1) {
featureGroups.splice(index, 1);
}
try {
this.map.removeLayer(featureGroup);
} catch (error) {
if (!isTestEnvironment()) {
console.warn("Error removing feature group layer from map:", error);
}
}
}
/**
* Get complete polygon GeoJSON including holes from a feature group
*/
getCompletePolygonFromFeatureGroup(featureGroup) {
try {
let polygon2 = null;
featureGroup.eachLayer((layer) => {
if (layer instanceof L.Polygon) {
polygon2 = layer;
}
});
if (polygon2) {
return polygon2.toGeoJSON();
}
const featureCollection2 = featureGroup.toGeoJSON();
const polygonFeature = featureCollection2.features.find(
(feature2) => {
var _a2, _b2;
return ((_a2 = feature2.geometry) == null ? void 0 : _a2.type) === "Polygon" || ((_b2 = feature2.geometry) == null ? void 0 : _b2.type) === "MultiPolygon";
}
);
if (polygonFeature) {
return polygonFeature;
}
throw new Error("No polygon geometry found in feature group");
} catch (error) {
if (!isTestEnvironment()) {
if (error instanceof Error) {
console.warn("Error getting complete polygon GeoJSON from feature group:", error.message);
}
}
throw error instanceof Error ? error : new Error("Failed to resolve polygon geometry from feature group");
}
}
getOptimizationMetadataFromFeatureGroup(featureGroup) {
if (!featureGroup) {
return { level: 0, original: 0 };
}
let level = 0;
let original = 0;
featureGroup.eachLayer((layer) => {
if (layer instanceof L.Polygon) {
const polygonLayer = layer;
if (typeof polygonLayer._polydrawOptimizationLevel === "number") {
level = polygonLayer._polydrawOptimizationLevel || 0;
}
if (typeof polygonLayer._polydrawOptimizationOriginalLevel === "number") {
original = polygonLayer._polydrawOptimizationOriginalLevel || 0;
}
}
});
if (!original && level > 0) {
original = level;
}
return { level, original };
}
getFeatureMetadataStateFromFeatureGroup(featureGroup) {
if (!featureGroup) {
return {
metadata: {},
sourceFeatureIds: []
};
}
const metadata = featureGroup._polydrawMetadata;
const featureId2 = metadata == null ? void 0 : metadata.id;
const sourceFeatureIds = Array.isArray(metadata == null ? void 0 : metadata.sourceFeatureIds) ? [...metadata.sourceFeatureIds] : featureId2 ? [featureId2] : [];
const createdAt = (metadata == null ? void 0 : metadata.createdAt) instanceof Date && !Number.isNaN(metadata.createdAt.getTime()) ? metadata.createdAt.toISOString() : void 0;
return {
featureId: featureId2,
metadata: this.cloneFeatureMetadata(metadata == null ? void 0 : metadata.metadata),
sourceFeatureIds,
interactionOverride: metadata == null ? void 0 : metadata.interactionOverride,
styleOverrides: this.cloneStyleOverrides(metadata == null ? void 0 : metadata.styleOverrides),
createdAt
};
}
collectSourceFeatureIds(featureGroups, seedSourceFeatureIds) {
const orderedIds = [];
const seenIds = /* @__PURE__ */ new Set();
const addId = (id) => {
if (!id || seenIds.has(id)) {
return;
}
seenIds.add(id);
orderedIds.push(id);
};
featureGroups.forEach((featureGroup) => {
const metadata = featureGroup._polydrawMetadata;
if (Array.isArray(metadata == null ? void 0 : metadata.sourceFeatureIds) && metadata.sourceFeatureIds.length > 0) {
metadata.sourceFeatureIds.forEach((sourceId) => addId(sourceId));
} else {
addId(metadata == null ? void 0 : metadata.id);
}
});
if (Array.isArray(seedSourceFeatureIds)) {
seedSourceFeatureIds.forEach((sourceId) => addId(sourceId));
}
return orderedIds;
}
resolvePrimaryFeatureMetadata(featureGroups) {
var _a2;
for (const featureGroup of featureGroups) {
const metadata = (_a2 = featureGroup._polydrawMetadata) == null ? void 0 : _a2.metadata;
if (metadata && Object.keys(metadata).length > 0) {
return this.cloneFeatureMetadata(metadata);
}
}
return {};
}
resolvePrimaryFeatureInteractionOverride(featureGroups) {
var _a2;
for (const featureGroup of featureGroups) {
const interaction = (_a2 = featureGroup._polydrawMetadata) == null ? void 0 : _a2.interactionOverride;
if (interaction) {
return interaction;
}
}
return void 0;
}
resolvePrimaryFeatureStyleOverrides(featureGroups) {
var _a2;
for (const featureGroup of featureGroups) {
const styleOverrides = (_a2 = featureGroup._polydrawMetadata) == null ? void 0 : _a2.styleOverrides;
if (styleOverrides) {
return this.cloneStyleOverrides(styleOverrides);
}
}
return void 0;
}
cloneFeatureMetadata(metadata) {
if (!metadata) {
return {};
}
return cloneMetadataValue(metadata);
}
cloneStyleOverrides(style) {
if (!style) {
return void 0;
}
return cloneMetadataValue(style);
}
featureHasHoles(feature2) {
if (!(feature2 == null ? void 0 : feature2.geometry)) {
return false;
}
if (feature2.geometry.type === "Polygon") {
return feature2.geometry.coordinates.length > 1;
}
if (feature2.geometry.type === "MultiPolygon") {
return feature2.geometry.coordinates.some((polygonRings) => polygonRings.length > 1);
}
return false;
}
setFeatureGroupMetadata(featureGroup, options) {
const now = /* @__PURE__ */ new Date();
const parsedCreatedAt = options.createdAt ? new Date(options.createdAt) : now;
const parsedLastModified = options.lastModified ? new Date(options.lastModified) : now;
const createdAt = Number.isNaN(parsedCreatedAt.getTime()) ? now : parsedCreatedAt;
const lastModified = Number.isNaN(parsedLastModified.getTime()) ? now : parsedLastModified;
const featureId2 = options.featureId || `fg-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`;
const sourceFeatureIds = options.sourceFeatureIds && options.sourceFeatureIds.length > 0 ? [...new Set(options.sourceFeatureIds)] : [featureId2];
featureGroup._polydrawMetadata = {
id: featureId2,
optimizationLevel: options.optimizationLevel,
originalOptimizationLevel: options.originalOptimizationLevel,
hasHoles: options.hasHoles,
createdAt,
lastModified,
layerId: options.layerId,
metadata: this.cloneFeatureMetadata(options.metadata),
sourceFeatureIds,
interactionOverride: options.interactionOverride,
styleOverrides: this.cloneStyleOverrides(options.styleOverrides)
};
}
// Public methods that delegate to interaction manager
/**
* Update marker draggable state
*/
updateMarkerDraggableState() {
this.interactionManager.updateMarkerDraggableState();
}
/**
* Update all markers for edge deletion visual feedback
*/
updateAllMarkersForEdgeDeletion(showFeedback) {
this.interactionManager.updateAllMarkersForEdgeDeletion(showFeedback);
}
/**
* Set modifier key held state
*/
setModifierKeyHeld(isHeld) {
this.interactionManager.setModifierKeyHeld(isHeld);
}
// Legacy methods for backward compatibility (delegate to appropriate managers)
/**
* Check if two polygons intersect (delegates to geometry manager)
*/
checkPolygonIntersection(polygon1, polygon2) {
return this.geometryManager.checkPolygonIntersection(polygon1, polygon2);
}
/**
* Get polygon center (delegates to geometry manager)
*/
getPolygonCenter(polygon2) {
try {
if (!polygon2 || !polygon2.geometry || !polygon2.geometry.coordinates) {
return null;
}
let coordinates;
if (polygon2.geometry.type === "Polygon") {
coordinates = polygon2.geometry.coordinates[0];
} else if (polygon2.geometry.type === "MultiPolygon") {
coordinates = polygon2.geometry.coordinates[0][0];
} else {
return null;
}
if (!Array.isArray(coordinates) || coordinates.length === 0) {
return null;
}
let latSum = 0;
let lngSum = 0;
let count = 0;
for (const coord of coordinates) {
if (Array.isArray(coord) && coord.length >= 2) {
const lng = coord[0];
const lat = coord[1];
if (typeof lng === "number" && typeof lat === "number" && !isNaN(lng) && !isNaN(lat)) {
lngSum += lng;
latSum += lat;
count++;
}
}
}
if (count === 0) {
return null;
}
return {
lat: latSum / count,
lng: lngSum / count
};
} catch {
return null;
}
}
/**
* Get bounding box from polygon (delegates to geometry manager)
*/
getBoundingBox(polygon2) {
try {
if (!polygon2 || !polygon2.geometry || !polygon2.geometry.coordinates) {
return null;
}
let coordinates;
if (polygon2.geometry.type === "Polygon") {
coordinates = polygon2.geometry.coordinates[0];
} else if (polygon2.geometry.type === "MultiPolygon") {
coordinates = polygon2.geometry.coordinates[0][0];
} else {
return null;
}
if (!Array.isArray(coordinates) || coordinates.length === 0) {
return null;
}
let minLat = Infinity;
let maxLat = -Infinity;
let minLng = Infinity;
let maxLng = -Infinity;
for (const coord of coordinates) {
if (Array.isArray(coord) && coord.length >= 2) {
const lng = coord[0];
const lat = coord[1];
if (typeof lng === "number" && typeof lat === "number" && !isNaN(lng) && !isNaN(lat)) {
minLng = Math.min(minLng, lng);
maxLng = Math.max(maxLng, lng);
minLat = Math.min(minLat, lat);
maxLat = Math.max(maxLat, lat);
}
}
}
if (minLat === Infinity || maxLat === -Infinity || minLng === Infinity || maxLng === -Infinity) {
return null;
}
return { minLat, maxLat, minLng, maxLng };
} catch {
return null;
}
}
// Legacy methods that are now handled by specialized managers but kept for compatibility
addMarker(latlngs, featureGroup, options) {
this.interactionManager.addMarkers(latlngs, featureGroup, options);
}
addHoleMarker(latlngs, featureGroup) {
this.interactionManager.addHoleMarkers(latlngs, featureGroup);
}
addEdgeClickListeners(polygon2, featureGroup) {
this.interactionManager.addEdgeClickListeners(polygon2, featureGroup);
}
enablePolygonDragging(polygon2, latlngs) {
this.interactionManager.enablePolygonDragging(polygon2, latlngs);
}
// Helper methods for backward compatibility
getMarkerIndex(latlngs, position) {
return 0;
}
ensureMarkerSeparation(polygonLength, markers) {
var _a2, _b2, _c;
return {
menu: ((_a2 = markers.menu) == null ? void 0 : _a2.index) ?? 0,
delete: ((_b2 = markers.delete) == null ? void 0 : _b2.index) ?? 1,
info: ((_c = markers.info) == null ? void 0 : _c.index) ?? 2
};
}
findAlternativeMarkerPosition(polygonLength, originalIndex, usedIndices) {
return (originalIndex + 1) % polygonLength;
}
createDivIcon(processedClasses) {
return leafletAdapter.createDivIcon({ className: processedClasses.join(" ") });
}
getLatLngInfoString(latlng) {
return "Latitude: " + latlng.lat + " Longitude: " + latlng.lng;
}
generateMenuMarkerPopup(latLngs, featureGroup) {
const div = document.createElement("div");
div.innerHTML = "Menu";
return div;
}
getPolygonGeoJSONFromFeatureGroup(featureGroup) {
return {
type: "Feature",
geometry: {
type: "Polygon",
coordinates: [
[
[0, 0],
[0, 1],
[1, 1],
[1, 0],
[0, 0]
]
]
},
properties: {}
};
}
getTotalPolygonPerimeter(polygonGeoJSON) {
return 1e3;
}
generateInfoMarkerPopup(area2, perimeter) {
const div = document.createElement("div");
div.innerHTML = `Area: ${area2}, Perimeter: ${perimeter}`;
return div;
}
onMarkerHoverForEdgeDeletion(marker, isHovering) {
}
highlightEdgeOnHover(edgePolyline, isHovering) {
}
// Additional methods for backward compatibility with tests
onEdgeClick(e, edgePolyline) {
}
removeFeatureGroup(featureGroup) {
this.removeFeatureGroupInternal(featureGroup);
}
/**
* Clean up all resources associated with a feature group
* This method ensures proper cleanup of event listeners and associated resources
*/
cleanupFeatureGroup(featureGroup) {
this.interactionManager.cleanupFeatureGroup(featureGroup);
}
onPolygonMouseMove(e) {
}
onPolygonMouseUp(e) {
}
updatePolygonAfterDrag(polygon2) {
}
setSubtractVisualMode(polygon2, enabled) {
}
performModifierSubtract(draggedGeoJSON, originalFeatureGroup) {
try {
const draggedPolygon = this.turfHelper.getTurfPolygon(draggedGeoJSON);
const intersectingFeatureGroups = [];
this.getFeatureGroups().forEach((featureGroup) => {
if (featureGroup === originalFeatureGroup) {
return;
}
try {
const featureCollection2 = featureGroup.toGeoJSON();
if (!featureCollection2 || !featureCollection2.features || !featureCollection2.features[0]) {
return;
}
const firstFeature = featureCollection2.features[0];
if (!firstFeature.geometry || !firstFeature.geometry.coordinates) {
return;
}
const existingPolygon = this.turfHelper.getTurfPolygon(firstFeature);
const hasIntersection = this.geometryManager.checkPolygonIntersection(
existingPolygon,
draggedPolygon
);
if (hasIntersection) {
intersectingFeatureGroups.push(featureGroup);
}
} catch {
}
});
this.removeFeatureGroupInternal(originalFeatureGroup);
intersectingFeatureGroups.forEach((featureGroup) => {
try {
const featureCollection2 = featureGroup.toGeoJSON();
const existingPolygon = this.turfHelper.getTurfPolygon(featureCollection2.features[0]);
this.removeFeatureGroupInternal(featureGroup);
const result = this.geometryManager.subtractPolygon(existingPolygon, draggedPolygon);
if (result.success && result.results) {
for (const resultPolygon of result.results) {
this.addPolygonLayer(resultPolygon, { simplify: true });
}
}
} catch {
}
});
} catch {
}
}
}
class HistoryManager {
constructor(eventManager, maxHistorySize = 50) {
__publicField(this, "undoStack", []);
__publicField(this, "redoStack", []);
__publicField(this, "maxHistorySize", 50);
__publicField(this, "maxSnapshotSize", 5e5);
// 500KB per snapshot
__publicField(this, "maxTotalMemory", 5e6);
// 5MB total memory budget
__publicField(this, "currentMemoryUsage", 0);
__publicField(this, "eventManager");
__publicField(this, "isRestoring", false);
this.eventManager = eventManager;
this.maxHistorySize = maxHistorySize;
}
setMaxHistorySize(maxHistorySize) {
if (!Number.isFinite(maxHistorySize) || maxHistorySize <= 0) {
return;
}
this.maxHistorySize = Math.floor(maxHistorySize);
this.enforceMemoryBudget();
this.enforceRedoMemoryBudget();
}
/**
* Save the current state to the undo stack
* @param featureGroups - Array of Leaflet feature groups to save
* @param action - Optional description of the action
* @param layerManager - Optional layer manager for capturing layer state
*/
saveState(featureGroups, action, layerManager) {
if (this.isRestoring) {
return;
}
const snapshot = this.createSnapshot(featureGroups, action, layerManager);
const snapshotSize = this.calculateSnapshotSize(snapshot);
snapshot.size = snapshotSize;
if (snapshotSize > this.maxSnapshotSize) {
console.warn(
`History snapshot too large (${snapshotSize} bytes, limit ${this.maxSnapshotSize} bytes). Skipping save.`
);
return;
}
this.redoStack = [];
this.undoStack.push(snapshot);
this.currentMemoryUsage += snapshotSize;
this.enforceMemoryBudget();
this.eventManager.emit("polydraw:history:changed", {
canUndo: this.canUndo(),
canRedo: this.canRedo()
});
}
/**
* Calculate the approximate size of a snapshot in bytes
*/
calculateSnapshotSize(snapshot) {
try {
const jsonString = JSON.stringify(snapshot);
return jsonString.length * 2;
} catch (error) {
console.warn("Error calculating snapshot size:", error);
return 0;
}
}
/**
* Enforce memory budget by removing oldest snapshots if needed
*/
enforceMemoryBudget() {
while (this.undoStack.length > this.maxHistorySize) {
const removedSnapshot = this.undoStack.shift();
if (removedSnapshot) {
this.currentMemoryUsage -= this.getSnapshotSize(removedSnapshot);
}
}
while (this.currentMemoryUsage > this.maxTotalMemory && this.undoStack.length > 0) {
const removedSnapshot = this.undoStack.shift();
if (removedSnapshot) {
this.currentMemoryUsage -= this.getSnapshotSize(removedSnapshot);
}
}
}
/**
* Enforce redo stack size (count-based, not byte-based)
*/
enforceRedoMemoryBudget() {
const maxRedoSize = Math.floor(this.maxHistorySize / 2);
while (this.redoStack.length > maxRedoSize) {
this.redoStack.shift();
}
}
/**
* Undo the last action
* @param featureGroups - Current array of feature groups
* @returns The snapshot to restore, or null if undo stack is empty
*/
undo(featureGroups, layerManager) {
if (!this.canUndo()) {
return null;
}
const currentSnapshot = this.createSnapshot(featureGroups, "redo-point", layerManager);
const currentSnapshotSize = this.calculateSnapshotSize(currentSnapshot);
currentSnapshot.size = currentSnapshotSize;
if (currentSnapshotSize <= this.maxSnapshotSize) {
this.redoStack.push(currentSnapshot);
this.enforceRedoMemoryBudget();
} else {
console.warn(`Current state too large (${currentSnapshotSize} bytes) to save to redo stack.`);
}
const previousSnapshot = this.undoStack.pop();
this.currentMemoryUsage -= this.getSnapshotSize(previousSnapshot);
this.eventManager.emit("polydraw:history:undo", {
canUndo: this.canUndo(),
canRedo: this.canRedo(),
action: previousSnapshot.action
});
return previousSnapshot;
}
/**
* Redo the last undone action
* @param featureGroups - Current array of feature groups
* @returns The snapshot to restore, or null if redo stack is empty
*/
redo(featureGroups, layerManager) {
if (!this.canRedo()) {
return null;
}
const currentSnapshot = this.createSnapshot(featureGroups, "undo-point", layerManager);
const currentSnapshotSize = this.calculateSnapshotSize(currentSnapshot);
currentSnapshot.size = currentSnapshotSize;
if (currentSnapshotSize <= this.maxSnapshotSize) {
this.undoStack.push(currentSnapshot);
this.currentMemoryUsage += currentSnapshotSize;
this.enforceMemoryBudget();
} else {
console.warn(`Current state too large (${currentSnapshotSize} bytes) to save to undo stack.`);
}
this.enforceRedoMemoryBudget();
const nextSnapshot = this.redoStack.pop();
this.eventManager.emit("polydraw:history:redo", {
canUndo: this.canUndo(),
canRedo: this.canRedo(),
action: nextSnapshot.action
});
return nextSnapshot;
}
/**
* Check if undo is available
*/
canUndo() {
return this.undoStack.length > 0;
}
/**
* Check if redo is available
*/
canRedo() {
return this.redoStack.length > 0;
}
/**
* Clear all history
*/
clear() {
this.undoStack = [];
this.redoStack = [];
this.currentMemoryUsage = 0;
this.eventManager.emit("polydraw:history:changed", {
canUndo: this.canUndo(),
canRedo: this.canRedo()
});
}
/**
* Set the restoration flag (prevents saving during restore)
*/
setRestoring(isRestoring) {
this.isRestoring = isRestoring;
}
/**
* Create a snapshot from feature groups
*/
createSnapshot(featureGroups, action, layerManager) {
const features = [];
const featureMetadataSnapshot = [];
featureGroups.forEach((fg) => {
fg.eachLayer((layer) => {
var _a2;
const layerWithGeoJSON = layer;
if (!(layerWithGeoJSON == null ? void 0 : layerWithGeoJSON.toGeoJSON)) {
return;
}
try {
const feature2 = layerWithGeoJSON.toGeoJSON();
if (!feature2) {
return;
}
const geomType = (_a2 = feature2 == null ? void 0 : feature2.geometry) == null ? void 0 : _a2.type;
if (geomType === "Polygon" || geomType === "MultiPolygon") {
features.push(feature2);
featureMetadataSnapshot.push(this.captureFeatureMetadataSnapshot(fg, layer));
}
} catch (error) {
console.warn("Error converting polygon to GeoJSON:", error);
}
});
});
const snapshot = {
features,
timestamp: Date.now(),
action
};
if (layerManager) {
snapshot.layerSnapshot = layerManager.captureLayerSnapshot(featureGroups);
}
if (featureMetadataSnapshot.length > 0) {
snapshot.featureMetadataSnapshot = featureMetadataSnapshot;
}
return snapshot;
}
captureFeatureMetadataSnapshot(featureGroup, polygonLayer) {
const snapshot = {};
const metadata = featureGroup._polydrawMetadata;
if (metadata == null ? void 0 : metadata.id) {
snapshot.id = metadata.id;
}
if (metadata == null ? void 0 : metadata.metadata) {
snapshot.metadata = cloneMetadataValue(metadata.metadata);
}
if (Array.isArray(metadata == null ? void 0 : metadata.sourceFeatureIds)) {
snapshot.sourceFeatureIds = [...metadata.sourceFeatureIds];
}
if (metadata == null ? void 0 : metadata.interactionOverride) {
snapshot.interactionOverride = metadata.interactionOverride;
}
if (metadata == null ? void 0 : metadata.styleOverrides) {
snapshot.styleOverrides = cloneMetadataValue(metadata.styleOverrides);
}
if (typeof (metadata == null ? void 0 : metadata.hasHoles) === "boolean") {
snapshot.hasHoles = metadata.hasHoles;
}
if (typeof (metadata == null ? void 0 : metadata.layerId) === "string") {
snapshot.layerId = metadata.layerId;
}
if ((metadata == null ? void 0 : metadata.createdAt) instanceof Date && !Number.isNaN(metadata.createdAt.getTime())) {
snapshot.createdAt = metadata.createdAt.toISOString();
}
if ((metadata == null ? void 0 : metadata.lastModified) instanceof Date && !Number.isNaN(metadata.lastModified.getTime())) {
snapshot.lastModified = metadata.lastModified.toISOString();
}
if (polygonLayer instanceof L.Polygon) {
const polygon2 = polygonLayer;
if (typeof polygon2._polydrawOptimizationLevel === "number") {
snapshot.optimizationLevel = polygon2._polydrawOptimizationLevel;
}
if (typeof polygon2._polydrawOptimizationOriginalLevel === "number") {
snapshot.originalOptimizationLevel = polygon2._polydrawOptimizationOriginalLevel;
}
}
return snapshot;
}
getSnapshotSize(snapshot) {
if (typeof snapshot.size === "number") {
return snapshot.size;
}
const size = this.calculateSnapshotSize(snapshot);
snapshot.size = size;
return size;
}
}
class LayerManager {
constructor(eventManager, defaultColor) {
__publicField(this, "eventManager");
__publicField(this, "layers", /* @__PURE__ */ new Map());
__publicField(this, "featureGroupToLayer", /* @__PURE__ */ new WeakMap());
__publicField(this, "activeLayerId", "default");
__publicField(this, "defaultColor");
this.eventManager = eventManager;
this.defaultColor = LayerManager.normalizeColor(defaultColor);
this.ensureDefaultLayer();
}
getLayerCount() {
return this.layers.size;
}
getAllLayers() {
return Array.from(this.layers.values()).map((layer) => this.cloneLayerState(layer));
}
getPanelLayers() {
return this.getAllLayers().filter((layer) => layer.panel !== "hidden");
}
getPanelLayerCount() {
let count = 0;
for (const layer of this.layers.values()) {
if (layer.panel !== "hidden") {
count += 1;
}
}
return count;
}
getLayer(layerId) {
const layer = this.layers.get(this.normalizeLayerId(layerId));
return layer ? this.cloneLayerState(layer) : void 0;
}
getActiveLayerId() {
return this.activeLayerId;
}
getActiveLayer() {
return this.getLayer(this.activeLayerId);
}
getOrCreateLayer(layerId, colorOrOptions) {
const normalizedId = this.normalizeLayerId(layerId);
const existing = this.layers.get(normalizedId);
if (existing) {
return this.cloneLayerState(this.layers.get(normalizedId));
}
const options = typeof colorOrOptions === "string" ? { color: colorOrOptions } : colorOrOptions || {};
const layer = {
id: normalizedId,
label: options.label,
color: options.color ? LayerManager.normalizeColor(options.color, this.defaultColor) : this.defaultColor,
visible: options.visible !== false,
interaction: this.normalizeInteraction(options.interaction),
panel: this.normalizePanel(options.panel),
metadata: this.cloneMetadata(options.metadata),
featureGroups: []
};
this.layers.set(normalizedId, layer);
this.eventManager.emit("polydraw:layer:created", {
layerId: layer.id,
color: layer.color
});
return this.cloneLayerState(layer);
}
setActiveLayer(layerId) {
return this.setActiveLayerWithResult(layerId) === "activated";
}
setActiveLayerWithResult(layerId) {
const normalizedId = this.normalizeLayerId(layerId);
const targetLayer = this.layers.get(normalizedId);
if (!targetLayer) {
return "not-found";
}
if (normalizedId === this.activeLayerId) {
return "already-active";
}
if (!targetLayer.visible) {
return "not-visible";
}
const previousLayerId = this.activeLayerId;
this.activeLayerId = normalizedId;
this.eventManager.emit("polydraw:layer:activated", {
layerId: normalizedId,
previousLayerId
});
return "activated";
}
setLayerVisibility(layerId, visible) {
const layer = this.layers.get(this.normalizeLayerId(layerId));
if (!layer || layer.visible === visible) {
return false;
}
if (!visible && layer.id === this.activeLayerId) {
const fallbackLayerId = this.getFirstVisibleLayerId(layer.id);
if (!fallbackLayerId) {
return false;
}
this.setActiveLayer(fallbackLayerId);
}
layer.visible = visible;
this.eventManager.emit("polydraw:layer:visibility", {
layerId: layer.id,
visible
});
return true;
}
setLayerColor(layerId, color) {
const layer = this.layers.get(this.normalizeLayerId(layerId));
const normalizedColor = LayerManager.normalizeColor(color, (layer == null ? void 0 : layer.color) ?? this.defaultColor);
if (!layer || layer.color === normalizedColor) {
return false;
}
layer.color = normalizedColor;
layer.featureGroups.forEach((featureGroup) => {
var _a2, _b2;
const styleColorOverride = (_b2 = (_a2 = featureGroup._polydrawMetadata) == null ? void 0 : _a2.styleOverrides) == null ? void 0 : _b2.color;
featureGroup.eachLayer((layerItem) => {
if (layerItem instanceof L.Polygon && !(layerItem instanceof L.Rectangle)) {
layerItem.setStyle({ color: styleColorOverride ?? normalizedColor });
}
});
});
this.eventManager.emit("polydraw:layer:colorChanged", {
layerId: layer.id,
color: layer.color
});
return true;
}
setLayerLabel(layerId, label) {
const layer = this.layers.get(this.normalizeLayerId(layerId));
if (!layer) {
return false;
}
const normalizedLabel = typeof label === "string" ? label.trim() : void 0;
const nextLabel = normalizedLabel && normalizedLabel.length > 0 ? normalizedLabel : void 0;
if (layer.label === nextLabel) {
return false;
}
layer.label = nextLabel;
return true;
}
setLayerInteraction(layerId, interaction) {
const layer = this.layers.get(this.normalizeLayerId(layerId));
if (!layer) {
return false;
}
const normalized = this.normalizeInteraction(interaction);
if (layer.interaction === normalized) {
return false;
}
layer.interaction = normalized;
return true;
}
setLayerPanelVisibility(layerId, panel) {
const layer = this.layers.get(this.normalizeLayerId(layerId));
if (!layer) {
return false;
}
const normalized = this.normalizePanel(panel);
if (layer.panel === normalized) {
return false;
}
layer.panel = normalized;
return true;
}
setLayerMetadata(layerId, metadata) {
const layer = this.layers.get(this.normalizeLayerId(layerId));
if (!layer) {
return false;
}
layer.metadata = this.cloneMetadata(metadata);
return true;
}
isLayerEditable(layerId) {
const layer = this.layers.get(this.normalizeLayerId(layerId));
if (!layer) {
return true;
}
return layer.interaction === "editable";
}
isFeatureGroupEditable(featureGroup) {
var _a2;
const interactionOverride = (_a2 = featureGroup._polydrawMetadata) == null ? void 0 : _a2.interactionOverride;
if (interactionOverride) {
return interactionOverride === "editable";
}
const layerId = this.getLayerForFeatureGroup(featureGroup);
if (!layerId) {
return true;
}
return this.isLayerEditable(layerId);
}
/**
* Reorder layers by moving layerId to the drop index of targetLayerId.
* This mirrors list drag/drop behavior used in the layer panel.
*/
moveLayer(layerId, targetLayerId) {
const sourceId = this.normalizeLayerId(layerId);
const targetId = this.normalizeLayerId(targetLayerId);
if (sourceId === targetId) {
return false;
}
if (sourceId === "default" || targetId === "default") {
return false;
}
if (!this.layers.has(sourceId) || !this.layers.has(targetId)) {
return false;
}
const layerOrder = Array.from(this.layers.keys());
const sourceIndex = layerOrder.indexOf(sourceId);
const targetIndex = layerOrder.indexOf(targetId);
if (sourceIndex < 0 || targetIndex < 0) {
return false;
}
const [moved] = layerOrder.splice(sourceIndex, 1);
layerOrder.splice(targetIndex, 0, moved);
const reordered = /* @__PURE__ */ new Map();
for (const id of layerOrder) {
const layer = this.layers.get(id);
if (layer) {
reordered.set(id, layer);
}
}
this.layers = reordered;
this.eventManager.emit("polydraw:layer:reordered", {
layerId: sourceId,
targetLayerId: targetId,
orderedLayerIds: layerOrder
});
return true;
}
assignFeatureGroupToLayer(featureGroup, layerId) {
const targetId = this.normalizeLayerId(layerId);
if (!this.layers.has(targetId)) {
return false;
}
const currentLayerId = this.getLayerForFeatureGroup(featureGroup);
if (currentLayerId === targetId) {
return false;
}
if (currentLayerId) {
const currentLayer = this.layers.get(currentLayerId);
if (currentLayer) {
currentLayer.featureGroups = currentLayer.featureGroups.filter((fg) => fg !== featureGroup);
}
}
const targetLayer = this.layers.get(targetId);
if (!targetLayer.featureGroups.includes(featureGroup)) {
targetLayer.featureGroups.push(featureGroup);
}
this.featureGroupToLayer.set(featureGroup, targetId);
const metadata = featureGroup._polydrawMetadata;
if (metadata) {
metadata.layerId = targetId;
}
return true;
}
removeFeatureGroupFromLayer(featureGroup) {
const layerId = this.getLayerForFeatureGroup(featureGroup);
if (!layerId) {
return;
}
const layer = this.layers.get(layerId);
if (layer) {
layer.featureGroups = layer.featureGroups.filter((fg) => fg !== featureGroup);
}
this.featureGroupToLayer.delete(featureGroup);
const metadata = featureGroup._polydrawMetadata;
if (metadata) {
delete metadata.layerId;
}
}
getLayerForFeatureGroup(featureGroup) {
const mapped = this.featureGroupToLayer.get(featureGroup);
if (mapped) {
return mapped;
}
for (const [layerId, layer] of this.layers.entries()) {
if (layer.featureGroups.includes(featureGroup)) {
this.featureGroupToLayer.set(featureGroup, layerId);
return layerId;
}
}
return void 0;
}
getFeatureGroupsForLayer(layerId) {
const layer = this.layers.get(this.normalizeLayerId(layerId));
return layer ? [...layer.featureGroups] : [];
}
isInActiveLayer(featureGroup) {
const layerId = this.getLayerForFeatureGroup(featureGroup);
if (!layerId) {
return true;
}
return layerId === this.activeLayerId;
}
deleteLayer(layerId) {
const normalizedId = this.normalizeLayerId(layerId);
if (normalizedId === "default") {
return [];
}
const layer = this.layers.get(normalizedId);
if (!layer) {
return [];
}
const removedFeatureGroups = [...layer.featureGroups];
removedFeatureGroups.forEach((fg) => {
this.featureGroupToLayer.delete(fg);
});
this.layers.delete(normalizedId);
if (this.activeLayerId === normalizedId) {
const fallbackLayerId = this.getFirstVisibleLayerId(normalizedId);
if (fallbackLayerId) {
this.setActiveLayer(fallbackLayerId);
} else {
this.activeLayerId = "default";
}
}
this.eventManager.emit("polydraw:layer:deleted", {
layerId: normalizedId,
removedFeatureGroups
});
return removedFeatureGroups;
}
/**
* Moves a layer to a specific 0-based position in the full layer order.
* The default layer always occupies index 0; the minimum valid target for
* non-default layers is 1. The index is clamped to the valid range.
*/
moveLayerToIndex(layerId, index) {
const normalizedId = this.normalizeLayerId(layerId);
if (normalizedId === "default" || !this.layers.has(normalizedId) || !Number.isFinite(index)) {
return false;
}
const layerOrder = Array.from(this.layers.keys());
const currentIndex = layerOrder.indexOf(normalizedId);
const targetIndex = Math.trunc(index);
const clampedIndex = Math.max(1, Math.min(targetIndex, layerOrder.length - 1));
if (currentIndex === clampedIndex) {
return false;
}
layerOrder.splice(currentIndex, 1);
layerOrder.splice(clampedIndex, 0, normalizedId);
const reordered = /* @__PURE__ */ new Map();
for (const id of layerOrder) {
const layer = this.layers.get(id);
if (layer) {
reordered.set(id, layer);
}
}
this.layers = reordered;
this.eventManager.emit("polydraw:layer:reordered", {
layerId: normalizedId,
targetLayerId: layerOrder[clampedIndex] ?? normalizedId,
orderedLayerIds: layerOrder
});
return true;
}
/**
* Sets the full layer order by providing an ordered list of layer IDs.
* The default layer always stays first. Any layers not included in the
* list are appended after the specified layers in their current relative order.
* Returns false if any specified ID does not exist or equals 'default'.
*/
setLayerOrder(layerIds) {
if (!layerIds || layerIds.length === 0) {
return false;
}
const normalizedIds = layerIds.map((id) => this.normalizeLayerId(id));
if (new Set(normalizedIds).size !== normalizedIds.length) {
return false;
}
for (const id of normalizedIds) {
if (id === "default" || !this.layers.has(id)) {
return false;
}
}
const currentOrder = Array.from(this.layers.keys());
const specifiedSet = new Set(normalizedIds);
const unspecified = Array.from(this.layers.keys()).filter(
(id) => id !== "default" && !specifiedSet.has(id)
);
const fullOrder = ["default", ...normalizedIds, ...unspecified];
if (currentOrder.length === fullOrder.length && currentOrder.every((id, index) => id === fullOrder[index])) {
return false;
}
const reordered = /* @__PURE__ */ new Map();
for (const id of fullOrder) {
const layer = this.layers.get(id);
if (layer) {
reordered.set(id, layer);
}
}
this.layers = reordered;
this.eventManager.emit("polydraw:layer:reordered", {
layerId: normalizedIds[0],
targetLayerId: normalizedIds[normalizedIds.length - 1],
orderedLayerIds: fullOrder
});
return true;
}
clear(defaultColor) {
if (defaultColor) {
this.defaultColor = LayerManager.normalizeColor(defaultColor, this.defaultColor);
}
this.layers = /* @__PURE__ */ new Map();
this.featureGroupToLayer = /* @__PURE__ */ new WeakMap();
this.activeLayerId = "default";
this.ensureDefaultLayer();
}
captureLayerSnapshot(featureGroups) {
const featureIndexMap = /* @__PURE__ */ new Map();
featureGroups.forEach((featureGroup, index) => {
featureIndexMap.set(featureGroup, index);
});
return {
layers: this.getAllLayers().map((layer) => ({
id: layer.id,
label: layer.label,
color: layer.color,
visible: layer.visible,
interaction: layer.interaction,
panel: layer.panel,
metadata: this.cloneMetadata(layer.metadata),
featureIndices: layer.featureGroups.map((featureGroup) => featureIndexMap.get(featureGroup)).filter((index) => typeof index === "number")
})),
activeLayerId: this.activeLayerId
};
}
restoreFromLayerSnapshot(snapshot, featureGroups) {
this.clear();
if (!snapshot || !Array.isArray(snapshot.layers)) {
return;
}
snapshot.layers.forEach((entry) => {
const layerId = this.normalizeLayerId(entry.id);
const existing = this.layers.get(layerId);
if (existing) {
existing.label = typeof entry.label === "string" ? entry.label : void 0;
existing.color = entry.color ? LayerManager.normalizeColor(entry.color) : existing.color;
existing.visible = entry.visible !== false;
existing.interaction = this.normalizeInteraction(entry.interaction);
existing.panel = this.normalizePanel(entry.panel);
existing.metadata = this.cloneMetadata(entry.metadata);
} else {
this.layers.set(layerId, {
id: layerId,
label: typeof entry.label === "string" ? entry.label : void 0,
color: entry.color ? LayerManager.normalizeColor(entry.color) : this.defaultColor,
visible: entry.visible !== false,
interaction: this.normalizeInteraction(entry.interaction),
panel: this.normalizePanel(entry.panel),
metadata: this.cloneMetadata(entry.metadata),
featureGroups: []
});
}
});
this.ensureDefaultLayer();
snapshot.layers.forEach((entry) => {
const layerId = this.normalizeLayerId(entry.id);
const layer = this.layers.get(layerId);
if (!layer) {
return;
}
entry.featureIndices.forEach((featureIndex) => {
const featureGroup = featureGroups[featureIndex];
if (!featureGroup) {
return;
}
if (!layer.featureGroups.includes(featureGroup)) {
layer.featureGroups.push(featureGroup);
}
this.featureGroupToLayer.set(featureGroup, layerId);
});
});
const desiredActive = this.normalizeLayerId(snapshot.activeLayerId || "default");
const desiredLayer = this.layers.get(desiredActive);
if (desiredLayer == null ? void 0 : desiredLayer.visible) {
this.activeLayerId = desiredActive;
return;
}
const fallbackLayerId = this.getFirstVisibleLayerId();
if (fallbackLayerId) {
this.activeLayerId = fallbackLayerId;
return;
}
const defaultLayer = this.layers.get("default");
if (defaultLayer) {
defaultLayer.visible = true;
}
this.activeLayerId = "default";
}
ensureDefaultLayer() {
if (!this.layers.has("default")) {
this.layers.set("default", {
id: "default",
color: this.defaultColor,
visible: true,
interaction: "editable",
panel: "visible",
metadata: {},
featureGroups: []
});
}
}
getFirstVisibleLayerId(excludeLayerId) {
for (const layer of this.layers.values()) {
if (layer.id === excludeLayerId) {
continue;
}
if (layer.visible) {
return layer.id;
}
}
return void 0;
}
normalizeLayerId(layerId) {
const normalized = (layerId || "").trim();
return normalized.length > 0 ? normalized : "default";
}
cloneLayerState(layer) {
return {
id: layer.id,
label: layer.label,
color: layer.color,
visible: layer.visible,
interaction: layer.interaction,
panel: layer.panel,
metadata: this.cloneMetadata(layer.metadata),
featureGroups: [...layer.featureGroups]
};
}
normalizeInteraction(interaction) {
if (interaction === "readonly" || interaction === "static") {
return interaction;
}
return "editable";
}
normalizePanel(panel) {
return panel === "hidden" ? "hidden" : "visible";
}
cloneMetadata(metadata) {
if (!metadata) {
return {};
}
return cloneMetadataValue(metadata);
}
/**
* Normalize hex colors to "#rrggbb".
* Accepts "f00", "#f00", "ff0000", "#ff0000". Returns fallback when invalid.
*/
static normalizeColor(input, fallback = "#50622b") {
const trimmed = (input || "").trim();
let hex = trimmed.replace(/^#/, "");
if (hex.length === 3) {
hex = `${hex[0]}${hex[0]}${hex[1]}${hex[1]}${hex[2]}${hex[2]}`;
}
if (!/^[0-9a-fA-F]{6}$/.test(hex)) {
return fallback;
}
return `#${hex.toLowerCase()}`;
}
}
const gripVerticalSvg = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">\n <circle cx="9" cy="5" r="1" />\n <circle cx="9" cy="12" r="1" />\n <circle cx="9" cy="19" r="1" />\n <circle cx="15" cy="5" r="1" />\n <circle cx="15" cy="12" r="1" />\n <circle cx="15" cy="19" r="1" />\n</svg>\n';
const eyeVisibleSvg = '<?xml version="1.0" encoding="UTF-8" standalone="no"?>\n<svg xmlns="http://www.w3.org/2000/svg" width="512" height="512" viewBox="0 0 512 512">\n <defs>\n <mask id="pupil-hole-hide">\n <rect width="512" height="512" fill="white"/>\n <circle cx="256" cy="220" r="60" fill="black"/>\n </mask>\n </defs>\n <g fill="none" stroke="#fff" stroke-linecap="round" stroke-linejoin="round">\n <!-- Upper eye contour -->\n <path d="M24 256 C 120 93 392 93 488 256" stroke-width="48"/>\n <!-- Lower eye contour -->\n <path d="M24 256 C 120 419 392 419 488 256" stroke-width="48"/>\n <!-- Iris highlight -->\n <circle cx="256" cy="234" r="108" fill="#fff" stroke="none" mask="url(#pupil-hole-hide)"/>\n </g>\n</svg>\n';
const eyeHiddenSvg = '<?xml version="1.0" encoding="UTF-8" standalone="no"?>\n<svg xmlns="http://www.w3.org/2000/svg" width="512" height="512" viewBox="0 0 512 512">\n <g fill="none" stroke="#fff" stroke-linecap="round" stroke-linejoin="round">\n <!-- Upper eye contour -->\n <path d="M24 256 C 120 93 392 93 488 256" stroke-width="48"/>\n <!-- Lower eye contour -->\n <path d="M24 256 C 120 419 392 419 488 256" stroke-width="48"/>\n <!-- Diagonal hide slash -->\n <path d="M48 408 L464 104" stroke-width="48"/>\n </g>\n</svg>\n';
const layerToggleIconSvg = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor">\n <path d="M3 7.5 12 3l9 4.5L12 12 3 7.5Z" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" />\n <path d="M3 12.5 12 17l9-4.5" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" />\n <path d="M3 17 12 21l9-4" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" />\n <polyline points="14 9.5 17 12 14 14.5" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" />\n</svg>\n';
const colorizeWhiteSvg = (svg) => svg.replace(/#(?:fff|ffffff)/gi, "currentColor");
const icons = {
grip: gripVerticalSvg.trim(),
eyeVisible: colorizeWhiteSvg(eyeVisibleSvg.trim()),
eyeHidden: colorizeWhiteSvg(eyeHiddenSvg.trim()),
delete: eraseSvg.trim(),
toggle: layerToggleIconSvg.trim()
};
function createLayerPanel(layerManager, eventManager) {
let isLayerPanelCollapsed = false;
let container = null;
let draggedLayerId = null;
let pendingRenderWhileDragging = false;
const renderPanel = () => {
if (!container) {
return;
}
if (draggedLayerId) {
pendingRenderWhileDragging = true;
return;
}
container.innerHTML = "";
const layers = layerManager.getPanelLayers();
const activeLayerId = layerManager.getActiveLayerId();
if (draggedLayerId && !layers.some((layer) => layer.id === draggedLayerId)) {
draggedLayerId = null;
}
container.classList.toggle("polydraw-layer-panel-collapsed", isLayerPanelCollapsed);
const header = L.DomUtil.create("div", "polydraw-layer-header", container);
const headerTitle = L.DomUtil.create("span", "polydraw-layer-header-title", header);
headerTitle.textContent = "Layers";
const headerCount = L.DomUtil.create("span", "polydraw-layer-header-count", header);
headerCount.textContent = String(layers.length);
const headerToggle = L.DomUtil.create("button", "polydraw-layer-header-toggle", header);
headerToggle.setAttribute("type", "button");
headerToggle.setAttribute(
"aria-label",
isLayerPanelCollapsed ? "Expand layer panel" : "Collapse layer panel"
);
headerToggle.setAttribute("title", isLayerPanelCollapsed ? "Expand layers" : "Collapse layers");
applySvgIcon(headerToggle, icons.toggle);
headerToggle.classList.toggle("is-collapsed", isLayerPanelCollapsed);
headerToggle.addEventListener("click", (event) => {
event.preventDefault();
event.stopPropagation();
isLayerPanelCollapsed = !isLayerPanelCollapsed;
renderPanel();
});
if (isLayerPanelCollapsed) {
return;
}
const clearDragClasses = () => {
if (!container) {
return;
}
const rows = container.querySelectorAll(".polydraw-layer-row");
rows.forEach((row) => {
row.classList.remove("polydraw-layer-row-dragging");
row.classList.remove("polydraw-layer-row-drag-over");
});
};
const flushPendingRender = () => {
if (!pendingRenderWhileDragging) {
return;
}
pendingRenderWhileDragging = false;
renderPanel();
};
const resolveDraggedLayerId = (event) => {
var _a2, _b2;
const fromTransfer = (_b2 = (_a2 = event == null ? void 0 : event.dataTransfer) == null ? void 0 : _a2.getData("text/plain")) == null ? void 0 : _b2.trim();
if (fromTransfer) {
return fromTransfer;
}
return draggedLayerId;
};
layers.forEach((layer) => {
var _a2;
const displayName = ((_a2 = layer.label) == null ? void 0 : _a2.trim()) ? layer.label : layer.id;
const row = L.DomUtil.create("div", "polydraw-layer-row", container ?? void 0);
row.setAttribute("tabindex", "0");
row.setAttribute("role", "button");
row.setAttribute("draggable", layer.id === "default" ? "false" : "true");
row.setAttribute("aria-label", `Activate layer ${displayName}`);
row.setAttribute("data-layer-id", layer.id);
if (layer.id === activeLayerId) {
row.classList.add("polydraw-layer-row-active");
}
if (!layer.visible) {
row.classList.add("polydraw-layer-hidden");
}
if (layer.id !== "default") {
const grip = L.DomUtil.create("span", "polydraw-layer-grip", row);
grip.setAttribute("aria-hidden", "true");
applySvgIcon(grip, icons.grip);
} else {
const gripSpacer = L.DomUtil.create("span", "polydraw-layer-grip-spacer", row);
gripSpacer.setAttribute("aria-hidden", "true");
}
const colorIndicator = L.DomUtil.create("span", "polydraw-layer-color-indicator", row);
colorIndicator.style.backgroundColor = layer.color;
const layerName = L.DomUtil.create("span", "polydraw-layer-name", row);
layerName.textContent = displayName;
layerName.setAttribute("title", displayName);
const visibilityButton = L.DomUtil.create(
"button",
"polydraw-layer-btn polydraw-layer-visibility",
row
);
visibilityButton.setAttribute("type", "button");
visibilityButton.setAttribute("title", layer.visible ? "Hide layer" : "Show layer");
visibilityButton.setAttribute(
"aria-label",
layer.visible ? `Hide layer ${displayName}` : `Show layer ${displayName}`
);
applySvgIcon(visibilityButton, layer.visible ? icons.eyeVisible : icons.eyeHidden);
if (!layer.visible) {
visibilityButton.classList.add("polydraw-layer-visibility-hidden");
}
visibilityButton.addEventListener("click", (event) => {
event.preventDefault();
event.stopPropagation();
layerManager.setLayerVisibility(layer.id, !layer.visible);
});
if (layer.id !== "default") {
const deleteButton = L.DomUtil.create(
"button",
"polydraw-layer-btn polydraw-layer-delete",
row
);
deleteButton.setAttribute("type", "button");
deleteButton.setAttribute("title", "Delete layer");
deleteButton.setAttribute("aria-label", `Delete layer ${displayName}`);
applySvgIcon(deleteButton, icons.delete);
deleteButton.addEventListener("click", (event) => {
event.preventDefault();
event.stopPropagation();
eventManager.emit("polydraw:layer:delete-requested", { layerId: layer.id });
});
} else {
const spacer = L.DomUtil.create("span", "polydraw-layer-btn-spacer", row);
spacer.setAttribute("aria-hidden", "true");
}
row.addEventListener("click", () => {
layerManager.setActiveLayer(layer.id);
});
row.addEventListener("keydown", (event) => {
if (event.key === "Enter" || event.key === " ") {
event.preventDefault();
layerManager.setActiveLayer(layer.id);
}
});
if (layer.id !== "default") {
row.addEventListener("dragstart", (event) => {
draggedLayerId = layer.id;
row.classList.add("polydraw-layer-row-dragging");
if (event.dataTransfer) {
event.dataTransfer.effectAllowed = "move";
event.dataTransfer.setData("text/plain", layer.id);
}
});
row.addEventListener("dragend", () => {
draggedLayerId = null;
clearDragClasses();
flushPendingRender();
});
}
row.addEventListener("dragover", (event) => {
const sourceLayerId = resolveDraggedLayerId(event);
if (!sourceLayerId || sourceLayerId === layer.id || layer.id === "default") {
return;
}
event.preventDefault();
row.classList.add("polydraw-layer-row-drag-over");
if (event.dataTransfer) {
event.dataTransfer.dropEffect = "move";
}
});
row.addEventListener("dragleave", () => {
row.classList.remove("polydraw-layer-row-drag-over");
});
row.addEventListener("drop", (event) => {
const sourceLayerId = resolveDraggedLayerId(event);
if (!sourceLayerId || sourceLayerId === layer.id || layer.id === "default") {
return;
}
event.preventDefault();
event.stopPropagation();
eventManager.emit("polydraw:layer:reorder-requested", {
layerId: sourceLayerId,
targetLayerId: layer.id
});
draggedLayerId = null;
clearDragClasses();
flushPendingRender();
});
});
};
const LayerPanelControlClass = L.Control.extend({
options: {
position: "topright"
},
onAdd() {
container = L.DomUtil.create("div", "leaflet-bar polydraw-layer-panel");
container.setAttribute("data-polydraw", "layer-panel");
L.DomEvent.disableClickPropagation(container);
L.DomEvent.disableScrollPropagation(container);
L.DomEvent.on(container, "mousedown", L.DomEvent.stopPropagation);
L.DomEvent.on(container, "touchstart", L.DomEvent.stopPropagation);
L.DomEvent.on(container, "dragstart", L.DomEvent.stopPropagation);
L.DomEvent.on(container, "dragover", L.DomEvent.stopPropagation);
L.DomEvent.on(container, "drop", L.DomEvent.stopPropagation);
renderPanel();
return container;
},
onRemove() {
if (!container) {
return;
}
L.DomEvent.off(container, "mousedown", L.DomEvent.stopPropagation);
L.DomEvent.off(container, "touchstart", L.DomEvent.stopPropagation);
L.DomEvent.off(container, "dragstart", L.DomEvent.stopPropagation);
L.DomEvent.off(container, "dragover", L.DomEvent.stopPropagation);
L.DomEvent.off(container, "drop", L.DomEvent.stopPropagation);
container = null;
draggedLayerId = null;
pendingRenderWhileDragging = false;
}
});
const panel = new LayerPanelControlClass();
panel.refresh = () => {
renderPanel();
};
return panel;
}
function injectDynamicStyles(config) {
const style = document.createElement("style");
style.innerHTML = `
.leaflet-control a { background-color: ${config.styles.ui.controlButton.backgroundColor}; color: ${config.styles.ui.controlButton.color}; display: flex; align-items: center; justify-content: center; }
.leaflet-control a:hover { background-color: ${config.styles.ui.controlButtonHover.backgroundColor}; }
.leaflet-control a.active { background-color: ${config.styles.ui.controlButtonActive.backgroundColor}; color: ${config.styles.ui.controlButtonActive.color}; }
.polydraw-indicator-active { background-color: ${config.styles.ui.indicatorActive.backgroundColor} !important; }
.crosshair-cursor-enabled { cursor: crosshair !important; }
.crosshair-cursor-enabled * { cursor: crosshair !important; }
.leaflet-polydraw-p2p-marker { background-color: ${config.styles.ui.p2pMarker.backgroundColor}; border: 2px solid ${config.styles.ui.p2pMarker.borderColor}; border-radius: 50%; box-sizing: border-box; }
.leaflet-polydraw-p2p-marker[data-p2p-mode="subtract"] { background-color: ${config.styles.ui.dragSubtract.color}; border-color: ${config.styles.ui.dragSubtract.color}; }
.leaflet-polydraw-p2p-first-marker { position: relative; }
.leaflet-polydraw-p2p-first-marker:hover { transform: scale(1.5); transition: all 0.2s ease; }
.leaflet-polydraw-p2p-first-marker-ready[data-p2p-mode="add"]:hover { background-color: ${config.styles.ui.p2pClosingMarker.color}; border-color: ${config.styles.ui.p2pClosingMarker.color}; cursor: pointer; }
.leaflet-polydraw-p2p-first-marker-ready[data-p2p-mode="subtract"]:hover { background-color: ${config.styles.ui.dragSubtract.color}; border-color: ${config.styles.ui.dragSubtract.color}; cursor: pointer; }
`;
document.head.appendChild(style);
}
class GeometryUtils {
/**
* Calculate angle between three points
*/
static calculateAngle(p1, p2, p3) {
const v1 = { x: p1.lng - p2.lng, y: p1.lat - p2.lat };
const v2 = { x: p3.lng - p2.lng, y: p3.lat - p2.lat };
const dot2 = v1.x * v2.x + v1.y * v2.y;
const mag1 = Math.sqrt(v1.x * v1.x + v1.y * v1.y);
const mag2 = Math.sqrt(v2.x * v2.x + v2.y * v2.y);
if (mag1 === 0 || mag2 === 0) return 0;
const cosAngle = dot2 / (mag1 * mag2);
return Math.acos(Math.max(-1, Math.min(1, cosAngle)));
}
/**
* Calculate distance from point to line between two other points
*/
static calculateDistanceFromLine(p1, point2, p2) {
const A = point2.lng - p1.lng;
const B2 = point2.lat - p1.lat;
const C = p2.lng - p1.lng;
const D2 = p2.lat - p1.lat;
const dot2 = A * C + B2 * D2;
const lenSq = C * C + D2 * D2;
if (lenSq === 0) return Math.sqrt(A * A + B2 * B2);
const param = dot2 / lenSq;
let xx, yy;
if (param < 0) {
xx = p1.lng;
yy = p1.lat;
} else if (param > 1) {
xx = p2.lng;
yy = p2.lat;
} else {
xx = p1.lng + param * C;
yy = p1.lat + param * D2;
}
const dx = point2.lng - xx;
const dy = point2.lat - yy;
return Math.sqrt(dx * dx + dy * dy);
}
/**
* Calculate centroid of polygon
* @deprecated Use PolygonUtil.getCenter() instead for consistency
*/
static calculateCentroid(latlngs) {
return PolygonUtil.getCenter(latlngs);
}
/**
* Calculate distance between two points
*/
static calculateDistance(p1, p2) {
const dx = p1.lng - p2.lng;
const dy = p1.lat - p2.lat;
return Math.sqrt(dx * dx + dy * dy);
}
/**
* Apply offset to polygon coordinates
* Note: Uses 'any' type for recursive coordinate structures that can be:
* - L.LatLngLiteral (single coordinate)
* - L.LatLngLiteral[] (array of coordinates)
* - L.LatLngLiteral[][] (polygon with holes)
* - L.LatLngLiteral[][][] (multipolygon)
* This is a legitimate use of 'any' for dynamic recursive data structures.
*/
// eslint-disable-next-line @typescript-eslint/no-explicit-any
static offsetPolygonCoordinates(latLngs, offsetLat, offsetLng) {
if (!latLngs) return latLngs;
if (Array.isArray(latLngs[0])) {
return latLngs.map(
(ring) => GeometryUtils.offsetPolygonCoordinates(ring, offsetLat, offsetLng)
);
} else if (latLngs.lat !== void 0 && latLngs.lng !== void 0) {
return {
lat: latLngs.lat + offsetLat,
lng: latLngs.lng + offsetLng
};
} else {
return latLngs.map(
(coord) => GeometryUtils.offsetPolygonCoordinates(coord, offsetLat, offsetLng)
);
}
}
}
class CoordinateUtils {
/**
* Convert coordinate arrays to proper format for polygon creation
*/
static convertToCoords(latlngs, turfHelper) {
const coords = [];
if (latlngs.length > 1 && latlngs.length < 3) {
const coordinates = [];
const within = turfHelper.isWithin(
L.GeoJSON.latLngsToCoords(latlngs[latlngs.length - 1]),
L.GeoJSON.latLngsToCoords(latlngs[0])
);
if (within) {
latlngs.forEach((polygon2) => {
coordinates.push(L.GeoJSON.latLngsToCoords(polygon2));
});
} else {
latlngs.forEach((polygon2) => {
coords.push([L.GeoJSON.latLngsToCoords(polygon2)]);
});
}
if (coordinates.length >= 1) {
coords.push(coordinates);
}
} else if (latlngs.length > 2) {
const coordinates = [];
for (let index = 1; index < latlngs.length - 1; index++) {
const within = turfHelper.isWithin(
L.GeoJSON.latLngsToCoords(latlngs[index]),
L.GeoJSON.latLngsToCoords(latlngs[0])
);
if (within) {
latlngs.forEach((polygon2) => {
coordinates.push(L.GeoJSON.latLngsToCoords(polygon2));
});
coords.push(coordinates);
} else {
latlngs.forEach((polygon2) => {
coords.push([L.GeoJSON.latLngsToCoords(polygon2)]);
});
}
}
} else {
coords.push([L.GeoJSON.latLngsToCoords(latlngs[0])]);
}
return coords;
}
/**
* Apply offset to polygon coordinates
*/
static offsetPolygonCoordinates(latLngs, offsetLat, offsetLng) {
return GeometryUtils.offsetPolygonCoordinates(latLngs, offsetLat, offsetLng);
}
/**
* Get latitude/longitude information string
*/
static getLatLngInfoString(latlng) {
return "Latitude: " + latlng.lat + " Longitude: " + latlng.lng;
}
/**
* Convert any coordinate format to L.LatLng
* Supports multiple input formats with smart auto-detection
*/
static convertToLatLng(coordinate) {
if (typeof coordinate === "object" && coordinate !== null) {
if ("lat" in coordinate && "lng" in coordinate) {
return leafletAdapter.createLatLng(
coordinate.lat,
coordinate.lng
);
}
if ("latitude" in coordinate && "longitude" in coordinate) {
return leafletAdapter.createLatLng(
coordinate.latitude,
coordinate.longitude
);
}
if ("longitude" in coordinate && "latitude" in coordinate) {
return leafletAdapter.createLatLng(
coordinate.latitude,
coordinate.longitude
);
}
if ("lng" in coordinate && "lat" in coordinate) {
return leafletAdapter.createLatLng(
coordinate.lat,
coordinate.lng
);
}
}
if (Array.isArray(coordinate) && coordinate.length >= 2) {
const [first, second] = coordinate;
if (typeof first === "number" && typeof second === "number") {
const absFirst = Math.abs(first);
const absSecond = Math.abs(second);
if (absFirst > 90 && absSecond <= 90) {
return leafletAdapter.createLatLng(second, first);
}
if (absSecond > 90 && absFirst <= 90) {
return leafletAdapter.createLatLng(first, second);
}
if (absFirst > 90 || absSecond > 90) {
return leafletAdapter.createLatLng(second, first);
}
return leafletAdapter.createLatLng(first, second);
}
}
if (typeof coordinate === "string") {
const coord = coordinate.trim();
const dmsMatch = coord.match(
/(\d+)°(\d+)'(?:(\d+(?:\.\d+)?)")?([NS])\s*,?\s*(\d+)°(\d+)'(?:(\d+(?:\.\d+)?)")?([EW])/i
);
if (dmsMatch) {
const [, latDeg, latMin, latSec, latDir, lngDeg, lngMin, lngSec, lngDir] = dmsMatch;
const lat = parseFloat(latDeg) + parseFloat(latMin) / 60 + parseFloat(latSec || "0") / 3600;
const lng = parseFloat(lngDeg) + parseFloat(lngMin) / 60 + parseFloat(lngSec || "0") / 3600;
const finalLat = latDir.toUpperCase() === "S" ? -lat : lat;
const finalLng = lngDir.toUpperCase() === "W" ? -lng : lng;
return leafletAdapter.createLatLng(finalLat, finalLng);
}
const ddmMatch = coord.match(
/(\d+)°(\d+(?:\.\d+)?)'([NS])\s*,?\s*(\d+)°(\d+(?:\.\d+)?)'([EW])/i
);
if (ddmMatch) {
const [, latDeg, latMin, latDir, lngDeg, lngMin, lngDir] = ddmMatch;
const lat = parseFloat(latDeg) + parseFloat(latMin) / 60;
const lng = parseFloat(lngDeg) + parseFloat(lngMin) / 60;
const finalLat = latDir.toUpperCase() === "S" ? -lat : lat;
const finalLng = lngDir.toUpperCase() === "W" ? -lng : lng;
return leafletAdapter.createLatLng(finalLat, finalLng);
}
const ddMatch = coord.match(
/([+-]?(?:\d+(?:\.\d+)?|\.\d+))°?\s*([NS])(?:\s*,\s*|\s+)([+-]?(?:\d+(?:\.\d+)?|\.\d+))°?\s*([EW])/i
);
if (ddMatch) {
const [, lat, latDir, lng, lngDir] = ddMatch;
const parsedLat = Math.abs(parseFloat(lat));
const parsedLng = Math.abs(parseFloat(lng));
const finalLat = latDir.toUpperCase() === "S" ? -parsedLat : parsedLat;
const finalLng = lngDir.toUpperCase() === "W" ? -parsedLng : parsedLng;
return leafletAdapter.createLatLng(finalLat, finalLng);
}
const directionalPrefixMatch = coord.match(
/([NS])\s*([+-]?(?:\d+(?:\.\d+)?|\.\d+))\s+([EW])\s*([+-]?(?:\d+(?:\.\d+)?|\.\d+))/i
);
if (directionalPrefixMatch) {
const [, latDir, lat, lngDir, lng] = directionalPrefixMatch;
const parsedLat = Math.abs(parseFloat(lat));
const parsedLng = Math.abs(parseFloat(lng));
const finalLat = latDir.toUpperCase() === "S" ? -parsedLat : parsedLat;
const finalLng = lngDir.toUpperCase() === "W" ? -parsedLng : parsedLng;
return leafletAdapter.createLatLng(finalLat, finalLng);
}
const commaSeparatedDecimals = coord.match(
/^\s*([+-]?(?:\d+(?:\.\d+)?|\.\d+))\s*,\s*([+-]?(?:\d+(?:\.\d+)?|\.\d+))\s*$/
);
if (commaSeparatedDecimals) {
const [, first, second] = commaSeparatedDecimals;
return this.convertToLatLng([parseFloat(first), parseFloat(second)]);
}
const utmMatch = coord.match(/(?:UTM\s+)?(\d+)([NS])\s+(\d+(?:\.\d+)?)\s+(\d+(?:\.\d+)?)/i);
if (utmMatch) {
throw new Error(
`UTM coordinates detected: ${coord}. UTM conversion requires specialized libraries like proj4js. Please convert to decimal degrees first.`
);
}
const mgrsMatch = coord.match(/(\d+)([A-Z])\s+([A-Z]{2})\s+(\d+)\s+(\d+)/i);
if (mgrsMatch) {
throw new Error(
`MGRS coordinates detected: ${coord}. MGRS conversion requires specialized libraries. Please convert to decimal degrees first.`
);
}
const plusCodeMatch = coord.match(
/^[23456789CFGHJMPQRVWX]{2,3}[23456789CFGHJMPQRVWX]{3,7}\+[23456789CFGHJMPQRVWX]{2,3}$/i
);
if (plusCodeMatch) {
throw new Error(
`Plus Code detected: ${coord}. Plus Code conversion requires specialized libraries. Please convert to decimal degrees first.`
);
}
}
throw new Error(`Unable to convert coordinate: ${JSON.stringify(coordinate)}`);
}
/**
* Convert any coordinate array format to L.LatLng[][][]
* Used by addPredefinedPolygon for flexible input handling
*/
static convertToLatLngArray(geoborders) {
return geoborders.map(
(group) => group.map((polygon2) => polygon2.map((coord) => this.convertToLatLng(coord)))
);
}
}
const LEGACY_TO_TOOLS_MAP = {
draw: "draw",
subtract: "subtract",
p2p: "p2p",
p2pSubtract: "p2pSubtract",
deleteAll: "erase",
clonePolygons: "clone"
};
function warnIfUsingDeprecatedConfiguration(config) {
var _a2, _b2, _c, _d;
if (!config) {
return;
}
const raw = config;
const legacyToolKeys = config.modes;
if (legacyToolKeys) {
const foundKeys = Object.keys(LEGACY_TO_TOOLS_MAP).filter(
(key) => legacyToolKeys[key] !== void 0
);
if (foundKeys.length > 0) {
console.warn(
"[Leaflet.Polydraw] `config.modes.*` tool toggles are deprecated in v2. Use `config.tools.*` (draw/subtract/p2p/clone/erase) instead."
);
if (!config.tools) {
config.tools = {};
}
for (const legacyKey of foundKeys) {
const newKey = LEGACY_TO_TOOLS_MAP[legacyKey];
if (config.tools[newKey] === void 0) {
config.tools[newKey] = legacyToolKeys[legacyKey];
}
}
}
}
const visualOptimization = ((_a2 = config.markers) == null ? void 0 : _a2.visualOptimization) ?? {};
const deprecatedVisualOptimizationKeys = Object.prototype.hasOwnProperty.call(visualOptimization, "useAngles") || Object.prototype.hasOwnProperty.call(visualOptimization, "useBoundingBox") || Object.prototype.hasOwnProperty.call(visualOptimization, "useDistance") || Object.prototype.hasOwnProperty.call(visualOptimization, "thresholdBoundingBox") || Object.prototype.hasOwnProperty.call(visualOptimization, "thresholdDistance") || Object.prototype.hasOwnProperty.call(visualOptimization, "sharpAngleThreshold");
if (deprecatedVisualOptimizationKeys) {
console.warn(
"[Leaflet.Polydraw] `markers.visualOptimization` is deprecated. Prefer `visualOptimizationLevel` when adding predefined polygons."
);
}
const legacyMaxHistorySize = config.maxHistorySize;
if (typeof legacyMaxHistorySize === "number") {
console.warn(
"[Leaflet.Polydraw] `config.maxHistorySize` is deprecated in v2. Use `config.history.maxSize` instead."
);
if (!config.history) {
config.history = {};
}
if (config.history.maxSize === void 0) {
config.history.maxSize = legacyMaxHistorySize;
}
}
const legacyBoundingBox = config.boundingBox;
if (legacyBoundingBox && typeof legacyBoundingBox.addMidPointMarkers === "boolean") {
console.warn(
"[Leaflet.Polydraw] `config.boundingBox` is deprecated in v2. Use `config.polygonTools.bbox` instead."
);
if (!config.polygonTools) {
config.polygonTools = {};
}
if (!config.polygonTools.bbox) {
config.polygonTools.bbox = {};
}
if (config.polygonTools.bbox.addMidPointMarkers === void 0) {
config.polygonTools.bbox.addMidPointMarkers = legacyBoundingBox.addMidPointMarkers;
}
}
const legacyBezier = config.bezier;
if (legacyBezier && (legacyBezier.resolution !== void 0 || legacyBezier.sharpness !== void 0 || legacyBezier.resampleMultiplier !== void 0 || legacyBezier.maxNodes !== void 0 || legacyBezier.visualOptimizationLevel !== void 0 || legacyBezier.ghostMarkers !== void 0)) {
console.warn(
"[Leaflet.Polydraw] `config.bezier` is deprecated in v2. Use `config.polygonTools.bezier` instead."
);
if (!config.polygonTools) {
config.polygonTools = {};
}
if (!config.polygonTools.bezier) {
config.polygonTools.bezier = {};
}
const target = config.polygonTools.bezier;
for (const key of Object.keys(legacyBezier)) {
if (legacyBezier[key] !== void 0 && target[key] === void 0) {
target[key] = legacyBezier[key];
}
}
}
const rawAlgorithm = config.polygonCreation;
const legacyPolygonCreation = raw.polygonCreation;
if ((legacyPolygonCreation == null ? void 0 : legacyPolygonCreation.method) !== void 0) {
console.warn(
"[Leaflet.Polydraw] `config.polygonCreation.method` is deprecated in v2. Use `config.polygonCreation.algorithm` instead."
);
if (!config.polygonCreation) {
config.polygonCreation = {};
}
const polygonCreationConfig = config.polygonCreation;
if (polygonCreationConfig.algorithm === void 0) {
polygonCreationConfig.algorithm = legacyPolygonCreation.method;
}
}
const legacySimplification = raw.simplification;
if ((legacySimplification == null ? void 0 : legacySimplification.mode) !== void 0) {
console.warn(
"[Leaflet.Polydraw] `config.simplification.mode` is deprecated in v2. Use `config.simplification.strategy` instead."
);
if (!config.simplification) {
config.simplification = {};
}
const simplificationConfig = config.simplification;
if (simplificationConfig.strategy === void 0) {
simplificationConfig.strategy = legacySimplification.mode;
}
}
if ((rawAlgorithm == null ? void 0 : rawAlgorithm.algorithm) === "buffer") {
console.warn(
'[Leaflet.Polydraw] `polygonCreation.algorithm: "buffer"` is deprecated and no longer supported. Falling back to "concaveman". Use "concaveman" or "direct" instead.'
);
}
const hasLegacyPolyLineOptions = raw.polyLineOptions !== void 0;
const hasLegacySubtractLineOptions = raw.subtractLineOptions !== void 0;
const hasLegacyPolygonOptions = raw.polygonOptions !== void 0;
const hasLegacyHoleOptions = raw.holeOptions !== void 0;
const hasLegacyColors = raw.colors !== void 0;
if (hasLegacyPolyLineOptions || hasLegacySubtractLineOptions || hasLegacyPolygonOptions || hasLegacyHoleOptions || hasLegacyColors) {
console.warn(
"[Leaflet.Polydraw] `polyLineOptions`, `subtractLineOptions`, `polygonOptions`, `holeOptions`, and `colors` are deprecated in v2. Use the unified `config.styles` block instead."
);
if (!config.styles) {
config.styles = {};
}
const styles = config.styles;
if (hasLegacyPolyLineOptions) {
if (!styles.polyline) {
styles.polyline = {};
}
const src = raw.polyLineOptions;
if (src.weight !== void 0 && styles.polyline.weight === void 0)
styles.polyline.weight = src.weight;
if (src.opacity !== void 0 && styles.polyline.opacity === void 0)
styles.polyline.opacity = src.opacity;
}
if (hasLegacySubtractLineOptions) {
if (!styles.subtractLine) {
styles.subtractLine = {};
}
const src = raw.subtractLineOptions;
if (src.weight !== void 0 && styles.subtractLine.weight === void 0)
styles.subtractLine.weight = src.weight;
if (src.opacity !== void 0 && styles.subtractLine.opacity === void 0)
styles.subtractLine.opacity = src.opacity;
}
if (hasLegacyPolygonOptions) {
if (!styles.polygon) {
styles.polygon = {};
}
const src = raw.polygonOptions;
if (src.weight !== void 0 && styles.polygon.weight === void 0)
styles.polygon.weight = src.weight;
if (src.opacity !== void 0 && styles.polygon.opacity === void 0)
styles.polygon.opacity = src.opacity;
if (src.fillOpacity !== void 0 && styles.polygon.fillOpacity === void 0)
styles.polygon.fillOpacity = src.fillOpacity;
if (src.smoothFactor !== void 0 && styles.polygon.smoothFactor === void 0)
styles.polygon.smoothFactor = src.smoothFactor;
if (src.noClip !== void 0 && styles.polygon.noClip === void 0)
styles.polygon.noClip = src.noClip;
}
if (hasLegacyHoleOptions) {
if (!styles.hole) {
styles.hole = {};
}
const src = raw.holeOptions;
if (src.weight !== void 0 && styles.hole.weight === void 0)
styles.hole.weight = src.weight;
if (src.opacity !== void 0 && styles.hole.opacity === void 0)
styles.hole.opacity = src.opacity;
if (src.fillOpacity !== void 0 && styles.hole.fillOpacity === void 0)
styles.hole.fillOpacity = src.fillOpacity;
}
if (hasLegacyColors) {
const colors = raw.colors;
if (colors.polyline !== void 0) {
if (!styles.polyline) styles.polyline = {};
if (styles.polyline.color === void 0) styles.polyline.color = colors.polyline;
}
if (colors.subtractLine !== void 0) {
if (!styles.subtractLine)
styles.subtractLine = {};
if (styles.subtractLine.color === void 0)
styles.subtractLine.color = colors.subtractLine;
}
if (colors.polygon) {
if (!styles.polygon) styles.polygon = {};
if (colors.polygon.border !== void 0 && styles.polygon.color === void 0)
styles.polygon.color = colors.polygon.border;
if (colors.polygon.fill !== void 0 && styles.polygon.fillColor === void 0)
styles.polygon.fillColor = colors.polygon.fill;
}
if (colors.hole) {
if (!styles.hole) styles.hole = {};
if (colors.hole.border !== void 0 && styles.hole.color === void 0)
styles.hole.color = colors.hole.border;
if (colors.hole.fill !== void 0 && styles.hole.fillColor === void 0)
styles.hole.fillColor = colors.hole.fill;
}
if (colors.styles) {
if (!styles.ui) styles.ui = {};
const uiSrc = colors.styles;
const ui = styles.ui;
if (uiSrc.controlButton && !ui.controlButton) ui.controlButton = uiSrc.controlButton;
if (uiSrc.controlButtonHover && !ui.controlButtonHover)
ui.controlButtonHover = uiSrc.controlButtonHover;
if (uiSrc.controlButtonActive && !ui.controlButtonActive)
ui.controlButtonActive = uiSrc.controlButtonActive;
if (uiSrc.indicatorActive && !ui.indicatorActive)
ui.indicatorActive = uiSrc.indicatorActive;
if (uiSrc.p2pMarker && !ui.p2pMarker) ui.p2pMarker = uiSrc.p2pMarker;
}
if (((_b2 = colors.p2p) == null ? void 0 : _b2.closingMarker) !== void 0) {
if (!styles.ui) styles.ui = {};
if (!styles.ui.p2pClosingMarker)
styles.ui.p2pClosingMarker = { color: colors.p2p.closingMarker };
}
if (colors.edgeHover !== void 0) {
if (!styles.ui) styles.ui = {};
if (!styles.ui.edgeHover) styles.ui.edgeHover = { color: colors.edgeHover };
}
if (((_c = colors.edgeDeletion) == null ? void 0 : _c.hover) !== void 0) {
if (!styles.ui) styles.ui = {};
if (!styles.ui.edgeDeletion) styles.ui.edgeDeletion = { color: colors.edgeDeletion.hover };
}
if (((_d = colors.dragPolygons) == null ? void 0 : _d.subtract) !== void 0) {
if (!styles.ui) styles.ui = {};
if (!styles.ui.dragSubtract)
styles.ui.dragSubtract = { color: colors.dragPolygons.subtract };
}
}
}
if (raw.defaultMode !== void 0) {
console.warn(
"[Leaflet.Polydraw] `config.defaultMode` is deprecated in v2. Use `config.tools.default` instead."
);
if (!config.tools) {
config.tools = {};
}
if (config.tools.default === void 0) {
config.tools.default = raw.defaultMode;
}
}
if (raw.dragPolygons !== void 0) {
console.warn(
"[Leaflet.Polydraw] `config.dragPolygons` is deprecated in v2. Use `config.interaction.drag` instead."
);
if (!config.interaction) {
config.interaction = {};
}
if (!config.interaction.drag) {
config.interaction.drag = raw.dragPolygons;
}
}
if (raw.edgeDeletion !== void 0) {
console.warn(
"[Leaflet.Polydraw] `config.edgeDeletion` is deprecated in v2. Use `config.interaction.edgeDeletion` instead."
);
if (!config.interaction) {
config.interaction = {};
}
if (!config.interaction.edgeDeletion) {
config.interaction.edgeDeletion = raw.edgeDeletion;
}
}
if (raw.menuOperations !== void 0) {
console.warn(
"[Leaflet.Polydraw] `config.menuOperations` is deprecated in v2. Use `config.polygonTools` instead."
);
if (!config.polygonTools) {
config.polygonTools = raw.menuOperations;
}
}
}
const VALID_COMPASS_MARKER_POSITIONS = /* @__PURE__ */ new Set([
MarkerPosition.SouthWest,
MarkerPosition.South,
MarkerPosition.SouthEast,
MarkerPosition.East,
MarkerPosition.NorthEast,
MarkerPosition.North,
MarkerPosition.NorthWest,
MarkerPosition.West
]);
function normalizeCompassMarkerPosition(value, fallback, path) {
if (typeof value === "number" && Number.isFinite(value)) {
if (VALID_COMPASS_MARKER_POSITIONS.has(value)) {
return value;
}
}
console.warn(
`[Leaflet.Polydraw] Invalid \`${path}\` value "${String(value)}". Using "${MarkerPosition[fallback]}" (${fallback}) as fallback.`
);
return fallback;
}
function applyRuntimeConfigFallbacks(config) {
config.markers.markerInfoIcon.position = normalizeCompassMarkerPosition(
config.markers.markerInfoIcon.position,
defaultConfig.markers.markerInfoIcon.position,
"markers.markerInfoIcon.position"
);
config.markers.markerMenuIcon.position = normalizeCompassMarkerPosition(
config.markers.markerMenuIcon.position,
defaultConfig.markers.markerMenuIcon.position,
"markers.markerMenuIcon.position"
);
config.markers.markerDeleteIcon.position = normalizeCompassMarkerPosition(
config.markers.markerDeleteIcon.position,
defaultConfig.markers.markerDeleteIcon.position,
"markers.markerDeleteIcon.position"
);
}
function normalizeConfigInput(config) {
const normalized = deepMerge({}, config ?? {});
warnIfUsingDeprecatedConfiguration(normalized);
return normalized;
}
class Polydraw extends L.Control {
constructor(options) {
super(options);
__publicField(this, "map");
__publicField(this, "tracer", null);
__publicField(this, "turfHelper");
__publicField(this, "subContainer");
__publicField(this, "config");
__publicField(this, "mapStateService");
__publicField(this, "eventManager");
__publicField(this, "polygonInformation");
__publicField(this, "modeManager");
__publicField(this, "polygonDrawManager");
__publicField(this, "polygonMutationManager");
__publicField(this, "historyManager");
__publicField(this, "layerManager");
__publicField(this, "layerPanel", null);
__publicField(this, "arrayOfFeatureGroups", []);
__publicField(this, "drawMode", DrawMode.Off);
__publicField(this, "drawModeListeners", []);
__publicField(this, "_boundKeyDownHandler");
__publicField(this, "_boundKeyUpHandler");
__publicField(this, "isModifierKeyHeld", false);
__publicField(this, "modifierModeOverride", null);
__publicField(this, "isDrawingInProgress", false);
__publicField(this, "mapEventsAttached", false);
__publicField(this, "drawEventsAttached", false);
__publicField(this, "controlEvents", /* @__PURE__ */ new WeakSet());
__publicField(this, "lastControlPointerDown", null);
__publicField(this, "_boundMouseMove");
__publicField(this, "_boundMouseUp");
__publicField(this, "_boundTouchMove");
__publicField(this, "_boundTouchEnd");
__publicField(this, "_boundTouchCancel");
__publicField(this, "_boundTouchStart");
__publicField(this, "_boundPointerDown");
__publicField(this, "_boundPointerMove");
__publicField(this, "_boundPointerUp");
__publicField(this, "_boundPointerCancel");
__publicField(this, "_boundContextMenu");
__publicField(this, "_lastTapTime", 0);
__publicField(this, "_lastTapLatLng", null);
__publicField(this, "_tapTimeout", null);
__publicField(this, "pointerEventsHandled", typeof window !== "undefined" && "PointerEvent" in window && LeafletVersionDetector.isV2());
__publicField(this, "_configReady", null);
__publicField(this, "_componentsInitialized", false);
__publicField(this, "_isControlMounted", false);
__publicField(this, "_isRestoringSnapshot", false);
__publicField(this, "_coreEventListenersAttached", false);
__publicField(this, "_coreEventSubscriptions", []);
__publicField(this, "_initRequestId", 0);
__publicField(this, "_historySuppressionDepth", 0);
__publicField(this, "_lastAppliedVisibleMapOrder", []);
__publicField(this, "originalMapTouchAction", null);
/**
* Updates map interactions based on the current drawing mode.
*/
__publicField(this, "_handleActivateToggle", () => {
const container = this.getContainer();
if (!container) return;
const activateButton = container.querySelector(".icon-activate");
if (leafletAdapter.domUtil.hasClass(activateButton, "active")) {
leafletAdapter.domUtil.removeClass(activateButton, "active");
if (this.subContainer) {
this.subContainer.style.maxHeight = "0px";
this.subContainer.style.overflow = "hidden";
}
} else {
leafletAdapter.domUtil.addClass(activateButton, "active");
if (this.subContainer) {
const targetHeight = this.subContainer.scrollHeight;
this.subContainer.style.maxHeight = `${targetHeight || 250}px`;
this.subContainer.style.overflow = "visible";
}
}
this.updateActivateButtonIndicator();
});
__publicField(this, "_handleDrawClick", (e) => {
if (e) {
e.preventDefault();
e.stopPropagation();
}
if (this.modeManager.getCurrentMode() === DrawMode.Add) {
this.setDrawMode(DrawMode.Off);
return;
}
this.setDrawMode(DrawMode.Add);
this.polygonInformation.saveCurrentState();
});
__publicField(this, "_handleSubtractClick", (e) => {
if (e) {
e.preventDefault();
e.stopPropagation();
}
if (this.modeManager.getCurrentMode() === DrawMode.Subtract) {
this.setDrawMode(DrawMode.Off);
return;
}
this.setDrawMode(DrawMode.Subtract);
this.polygonInformation.saveCurrentState();
});
__publicField(this, "_handleCloneClick", (e) => {
if (e) {
e.preventDefault();
e.stopPropagation();
}
if (this.arrayOfFeatureGroups.length === 0) {
return;
}
if (this.modeManager.getCurrentMode() === DrawMode.Clone) {
this.setDrawMode(DrawMode.Off);
return;
}
this.setDrawMode(DrawMode.Clone);
this.polygonInformation.saveCurrentState();
});
__publicField(this, "_handleEraseClick", (e) => {
if (e) {
e.preventDefault();
e.stopPropagation();
}
const eraseScope = this.config.tools.eraseScope ?? "all";
const erasableFeatureGroups = eraseScope === "defaultLayer" ? this.layerManager.getFeatureGroupsForLayer("default") : this.arrayOfFeatureGroups;
if (erasableFeatureGroups.length === 0) {
return;
}
this.map.closePopup();
this.saveHistory("eraseAll");
if (eraseScope === "defaultLayer") {
this.removeDefaultLayerFeatureGroups();
return;
}
this.removeAllFeatureGroups();
});
__publicField(this, "_handlePointToPointClick", (e) => {
if (e) {
e.preventDefault();
e.stopPropagation();
}
if (this.modeManager.getCurrentMode() === DrawMode.PointToPoint) {
this.setDrawMode(DrawMode.Off);
return;
}
this.setDrawMode(DrawMode.PointToPoint);
this.polygonInformation.saveCurrentState();
});
__publicField(this, "_handlePointToPointSubtractClick", (e) => {
if (e) {
e.preventDefault();
e.stopPropagation();
}
if (this.modeManager.getCurrentMode() === DrawMode.PointToPointSubtract) {
this.setDrawMode(DrawMode.Off);
return;
}
this.setDrawMode(DrawMode.PointToPointSubtract);
this.polygonInformation.saveCurrentState();
});
__publicField(this, "_handleUndoClick", (e) => {
if (e) {
e.preventDefault();
e.stopPropagation();
}
this.undo();
});
__publicField(this, "_handleRedoClick", (e) => {
if (e) {
e.preventDefault();
e.stopPropagation();
}
this.redo();
});
/**
* Handle marker hover when modifier key is held - event handler version
*/
__publicField(this, "onMarkerHoverForEdgeDeletionEvent", (e) => {
if (!this.isModifierKeyHeld) return;
const element = e.target;
if (element) {
element.style.backgroundColor = this.config.styles.ui.edgeDeletion.color;
element.style.borderColor = this.config.styles.ui.edgeDeletion.color;
element.classList.add("edge-deletion-hover");
}
});
/**
* Handle marker leave when modifier key is held - event handler version
*/
__publicField(this, "onMarkerLeaveForEdgeDeletionEvent", (e) => {
const element = e.target;
if (element) {
element.style.backgroundColor = "";
element.style.borderColor = "";
element.classList.remove("edge-deletion-hover");
}
});
const baseDefaults = structuredClone(defaultConfig);
const inlineConfig = normalizeConfigInput(options == null ? void 0 : options.config);
this.config = deepMerge(baseDefaults, inlineConfig);
applyRuntimeConfigFallbacks(this.config);
if (options == null ? void 0 : options.configPath) {
this._configReady = this.loadExternalConfig(options.configPath, inlineConfig);
} else {
this.initializeComponents();
this._configReady = Promise.resolve();
}
}
/**
* Method called when the control is added to the map.
* It initializes the control, creates the UI, and sets up event listeners.
* @param _map - The map instance.
* @returns The control's container element.
*/
onAdd(_map) {
const extendedMap = _map;
const browser = L.Browser;
extendedMap._onResize = () => {
};
if (browser.touch && browser.mobile) {
extendedMap.tap = false;
}
this.map = _map;
this._isControlMounted = true;
const initRequestId = ++this._initRequestId;
this.setupKeyboardHandlers();
const container = leafletAdapter.domUtil.create("div", "leaflet-control leaflet-bar");
this.initializeUI(container);
this.createTracer();
this.completeInitialization(initRequestId);
return container;
}
/**
* Completes the initialization after config is ready.
* Handles both sync (no configPath) and async (configPath) cases.
*/
completeInitialization(initRequestId) {
var _a2;
const finalizeInitialization = () => {
if (!this._isControlMounted || initRequestId !== this._initRequestId) {
return;
}
this.initializeManagers();
this.setDrawMode(this.config.tools.default);
this.setupEventListeners();
};
if (this._componentsInitialized) {
finalizeInitialization();
} else {
(_a2 = this._configReady) == null ? void 0 : _a2.then(() => {
finalizeInitialization();
}).catch(() => {
});
}
}
/**
* Method called when the control is removed from the map.
* It handles the cleanup of layers, events, and handlers.
* @param _map - The map instance, unused but required by the L.Control interface.
*/
onRemove(_map) {
this._isControlMounted = false;
this._initRequestId += 1;
this.comprehensiveCleanup();
}
/**
* Perform comprehensive cleanup of all resources
* This method ensures proper cleanup of event listeners, managers, and DOM elements
*/
comprehensiveCleanup() {
this.events(false);
this.drawStartedEvents(false);
this.setMapDrawingTouchLock(false);
this.removeKeyboardHandlers();
if (this.tracer) {
try {
if (this.map.hasLayer(this.tracer)) {
this.map.removeLayer(this.tracer);
} else {
this.tracer.remove();
}
} catch {
}
this.tracer = null;
}
this.arrayOfFeatureGroups.forEach((featureGroup) => {
try {
if (this.polygonMutationManager) {
this.polygonMutationManager.cleanupFeatureGroup(featureGroup);
}
this.map.removeLayer(featureGroup);
} catch (error) {
console.warn("Error cleaning up feature group:", error);
}
});
this.arrayOfFeatureGroups.length = 0;
this._lastAppliedVisibleMapOrder = [];
if (this.polygonMutationManager) {
this.polygonMutationManager.dispose();
}
this.removeCoreEventListeners();
if (this.polygonInformation) {
this.polygonInformation.deletePolygonInformationStorage();
}
this.drawModeListeners.length = 0;
if (this._boundKeyDownHandler) {
this._boundKeyDownHandler = void 0;
}
if (this._boundKeyUpHandler) {
this._boundKeyUpHandler = void 0;
}
if (this._boundMouseMove) {
this._boundMouseMove = void 0;
}
if (this._boundMouseUp) {
this._boundMouseUp = void 0;
}
if (this._boundTouchMove) {
this._boundTouchMove = void 0;
}
if (this._boundTouchEnd) {
this._boundTouchEnd = void 0;
}
if (this._boundTouchCancel) {
this._boundTouchCancel = void 0;
}
if (this._boundTouchStart) {
this._boundTouchStart = void 0;
}
if (this._boundPointerDown) {
this._boundPointerDown = void 0;
}
if (this._boundPointerMove) {
this._boundPointerMove = void 0;
}
if (this._boundPointerUp) {
this._boundPointerUp = void 0;
}
if (this._boundPointerCancel) {
this._boundPointerCancel = void 0;
}
if (this._boundContextMenu) {
this._boundContextMenu = void 0;
}
if (this._tapTimeout) {
clearTimeout(this._tapTimeout);
this._tapTimeout = null;
}
this.drawMode = DrawMode.Off;
this.isModifierKeyHeld = false;
this._lastTapTime = 0;
if (this.layerPanel) {
try {
this.layerPanel.remove();
} catch {
}
this.layerPanel = null;
}
this.subContainer = void 0;
this._componentsInitialized = false;
}
/**
* Adds the control to the given map.
* @param map - The map instance.
* @returns The current instance of the control.
*/
addTo(map) {
super.addTo(map);
return this;
}
/**
* Returns the array of feature groups currently managed by the control.
* @returns An array of L.FeatureGroup objects.
*/
getFeatureGroups() {
return this.arrayOfFeatureGroups;
}
/**
* Undo the last action
*/
async undo() {
if (!this.historyManager.canUndo()) {
return;
}
const snapshot = this.historyManager.undo(this.arrayOfFeatureGroups, this.layerManager);
if (snapshot) {
await this.restoreFromSnapshot(snapshot);
}
}
isPolygonAction(action) {
return action === "simplify" || action === "doubleElbows" || action === "bbox" || action === "bezier" || action === "scale" || action === "rotate" || action === "donut" || action === "toggleOptimization" || action === "polygonMenuAction";
}
shouldCaptureHistory(action) {
var _a2, _b2;
const capture = (_a2 = this.config.history) == null ? void 0 : _a2.capture;
if (!capture) {
return true;
}
if (this.isPolygonAction(action)) {
return ((_b2 = capture.polygonActions) == null ? void 0 : _b2[action]) !== false;
}
return capture[action] !== false;
}
saveHistory(action) {
if (this.isHistorySuppressed()) {
return;
}
if (!this.shouldCaptureHistory(action)) {
return;
}
this.historyManager.saveState(this.arrayOfFeatureGroups, action, this.layerManager);
}
isHistorySuppressed() {
return this._historySuppressionDepth > 0;
}
startHistoryBatch(action) {
if (!this.isHistorySuppressed()) {
this.saveHistory(action);
}
this._historySuppressionDepth += 1;
}
endHistoryBatch() {
if (this._historySuppressionDepth > 0) {
this._historySuppressionDepth -= 1;
}
}
/**
* Redo the last undone action
*/
async redo() {
if (!this.historyManager.canRedo()) {
return;
}
const snapshot = this.historyManager.redo(this.arrayOfFeatureGroups, this.layerManager);
if (snapshot) {
await this.restoreFromSnapshot(snapshot);
}
}
resolvePredefinedLayerDescriptor(inputLayer, layerColorOverride) {
if (!inputLayer) {
return null;
}
if (typeof inputLayer === "string") {
const id2 = inputLayer.trim();
if (!id2) {
throw new Error("Layer id cannot be empty");
}
return {
id: id2,
color: layerColorOverride
};
}
const id = (inputLayer.id || "").trim();
if (!id) {
throw new Error("Layer descriptor must include a non-empty id");
}
const descriptor = {
...inputLayer,
id,
color: layerColorOverride ?? inputLayer.color
};
if (descriptor.interaction === "static" && descriptor.panel === void 0) {
descriptor.panel = "hidden";
}
return descriptor;
}
ensureLayerFromDescriptor(descriptor) {
const layerId = descriptor.id.trim();
const created = this.layerManager.getOrCreateLayer(layerId, {
label: descriptor.label,
color: descriptor.color,
visible: descriptor.visibility,
interaction: descriptor.interaction,
panel: descriptor.panel ?? (descriptor.interaction === "static" ? "hidden" : void 0),
metadata: descriptor.metadata
});
if (descriptor.label !== void 0) {
this.layerManager.setLayerLabel(layerId, descriptor.label);
}
if (descriptor.color) {
this.layerManager.setLayerColor(layerId, descriptor.color);
}
if (descriptor.visibility !== void 0) {
this.layerManager.setLayerVisibility(layerId, descriptor.visibility);
}
if (descriptor.interaction !== void 0) {
this.layerManager.setLayerInteraction(layerId, descriptor.interaction);
}
if (descriptor.panel !== void 0 || descriptor.interaction === "static") {
this.layerManager.setLayerPanelVisibility(
layerId,
descriptor.panel ?? (descriptor.interaction === "static" ? "hidden" : "visible")
);
}
if (descriptor.metadata !== void 0) {
this.layerManager.setLayerMetadata(layerId, descriptor.metadata);
}
return this.layerManager.getLayer(layerId) ?? created;
}
getInteractionTargetLayerId() {
if (!this.layerManager) {
return void 0;
}
const activeLayerId = this.layerManager.getActiveLayerId();
if (this.layerManager.isLayerEditable(activeLayerId)) {
return activeLayerId;
}
if (this.layerManager.isLayerEditable("default")) {
return "default";
}
const fallback = this.layerManager.getAllLayers().find((layer) => layer.interaction === "editable");
return fallback == null ? void 0 : fallback.id;
}
ensureFeatureGroupMetadata(featureGroup) {
const polydrawFeatureGroup = featureGroup;
if (polydrawFeatureGroup._polydrawMetadata) {
return polydrawFeatureGroup._polydrawMetadata;
}
let optimizationLevel = 0;
let originalOptimizationLevel = 0;
featureGroup.eachLayer((layer) => {
if (layer instanceof L.Polygon && !(layer instanceof L.Rectangle)) {
const polygon2 = layer;
if (typeof polygon2._polydrawOptimizationLevel === "number") {
optimizationLevel = polygon2._polydrawOptimizationLevel;
}
if (typeof polygon2._polydrawOptimizationOriginalLevel === "number") {
originalOptimizationLevel = polygon2._polydrawOptimizationOriginalLevel;
}
}
});
const now = /* @__PURE__ */ new Date();
const featureId2 = `fg-${leafletAdapter.util.stamp(featureGroup)}`;
polydrawFeatureGroup._polydrawMetadata = {
id: featureId2,
optimizationLevel,
originalOptimizationLevel,
hasHoles: false,
createdAt: now,
lastModified: now,
metadata: {},
sourceFeatureIds: [featureId2]
};
return polydrawFeatureGroup._polydrawMetadata;
}
/**
* Adds a predefined polygon to the map.
* @param geoborders - Flexible coordinate format: objects ({lat, lng}), arrays ([lat, lng] or [lng, lat]), strings ("lat,lng" or "N59 E10")
* @param options - Optional parameters, including visual optimization level.
*/
async addPredefinedPolygon(geoborders, options) {
var _a2, _b2, _c;
const geographicBorders = CoordinateUtils.convertToLatLngArray(geoborders);
if (!geographicBorders || geographicBorders.length === 0) {
throw new Error("Cannot add empty polygon array");
}
if (!this.map) {
throw new Error("Map not initialized");
}
if (!this.polygonMutationManager) {
throw new Error("PolygonMutationManager not initialized");
}
if (!this.layerManager) {
throw new Error("LayerManager not initialized");
}
for (const [groupIndex, group] of geographicBorders.entries()) {
if (!group || !group[0] || group[0].length < 4) {
throw new Error(
`Invalid polygon data at index ${groupIndex}: A polygon must have at least 3 unique vertices.`
);
}
}
const visualOptimizationLevel = (options == null ? void 0 : options.visualOptimizationLevel) ?? 0;
const interactionOverride = ((_a2 = options == null ? void 0 : options.overrides) == null ? void 0 : _a2.interaction) && options.overrides.interaction !== "inherit" ? options.overrides.interaction : void 0;
const styleOverrides = ((_b2 = options == null ? void 0 : options.overrides) == null ? void 0 : _b2.style) ? { ...options.overrides.style } : void 0;
const mergeOverride = ((_c = options == null ? void 0 : options.overrides) == null ? void 0 : _c.merge) ?? "inherit";
let targetLayerId;
let layerColor;
let forceNoMerge = false;
if (options == null ? void 0 : options.layer) {
const descriptor = this.resolvePredefinedLayerDescriptor(options.layer, options.layerColor);
if (descriptor) {
const layer = this.ensureLayerFromDescriptor(descriptor);
targetLayerId = layer.id;
layerColor = layer.color;
forceNoMerge = layer.interaction !== "editable";
}
this.updateLayerPanel();
} else if (options == null ? void 0 : options.layerColor) {
layerColor = options.layerColor;
}
this.startHistoryBatch("addPredefinedPolygon");
try {
for (const group of geographicBorders) {
try {
const coords = group.map((ring) => ring.map((latlng) => [latlng.lng, latlng.lat]));
const polygon2 = this.turfHelper.getMultiPolygon([coords]);
const noMerge = mergeOverride === "allow" ? false : mergeOverride === "block" ? true : forceNoMerge;
const result = await this.polygonMutationManager.addPolygon(polygon2, {
simplify: false,
noMerge,
mergeEditableOnly: mergeOverride === "allow" ? false : true,
visualOptimizationLevel,
targetLayerId,
layerColor,
featureMetadata: options == null ? void 0 : options.metadata,
featureInteractionOverride: interactionOverride,
featureStyleOverrides: styleOverrides
});
if (!result.success) {
console.error("Error adding polygon via manager:", result.error);
throw new Error(result.error || "Failed to add polygon");
}
this.polygonInformation.createPolygonInformationStorage(this.arrayOfFeatureGroups);
} catch (error) {
console.error("Error adding auto polygon:", error);
throw error;
}
}
} finally {
this.endHistoryBatch();
}
}
/**
* Adds predefined polygons from GeoJSON to the map.
* @param geojsonFeatures - An array of GeoJSON Polygon or MultiPolygon features.
* @param options - Optional parameters, including visual optimization level.
*/
async addPredefinedGeoJSONs(geojsonFeatures, options) {
if (!Array.isArray(geojsonFeatures) || geojsonFeatures.length === 0) {
throw new Error("Cannot add empty GeoJSON feature array");
}
this.startHistoryBatch("addPredefinedPolygon");
try {
for (const geojsonFeature of geojsonFeatures) {
const { type, coordinates } = geojsonFeature.geometry;
const metadataFromFeature = (options == null ? void 0 : options.metadata) ?? (geojsonFeature.properties ? { ...geojsonFeature.properties } : void 0);
const perFeatureOptions = {
...options,
metadata: metadataFromFeature
};
if (type === "MultiPolygon") {
for (const polygonCoords of coordinates) {
const latLngs = polygonCoords.map(
(ring) => ring.map((point2) => leafletAdapter.createLatLng(point2[1], point2[0]))
);
await this.addPredefinedPolygon([latLngs], perFeatureOptions);
}
} else if (type === "Polygon") {
const latLngs = coordinates.map(
(ring) => ring.map((point2) => leafletAdapter.createLatLng(point2[1], point2[0]))
);
await this.addPredefinedPolygon([latLngs], perFeatureOptions);
}
}
} finally {
this.endHistoryBatch();
}
}
/**
* Adds multiple groups of predefined polygons, each associated with a named, colored layer.
* @param groups - Array of polygon group inputs with layer info and polygon coordinates.
*/
async addPredefinedPolygonGroups(groups) {
if (!Array.isArray(groups) || groups.length === 0) {
throw new Error("Cannot add empty polygon group array");
}
if (!this.layerManager) {
throw new Error("LayerManager not initialized");
}
this.startHistoryBatch("addPredefinedPolygon");
try {
const resolvedGroups = groups.map((group) => {
const descriptor = this.resolvePredefinedLayerDescriptor(group.layer);
if (!descriptor) {
throw new Error("Polygon group layer descriptor is missing");
}
return {
descriptor,
polygons: group.polygons,
options: group.options
};
});
for (const group of resolvedGroups) {
this.ensureLayerFromDescriptor(group.descriptor);
}
for (const group of resolvedGroups) {
for (const polygons of group.polygons) {
await this.addPredefinedPolygon(polygons, {
layer: group.descriptor,
...group.options
});
}
}
} finally {
this.endHistoryBatch();
}
this.updateLayerPanel();
}
/**
* Returns the LayerManager instance for external layer manipulation.
*/
getLayerManager() {
return this.layerManager;
}
/**
* Returns all configured layers.
*/
getAllLayers() {
return this.layerManager.getAllLayers();
}
/**
* Returns a single layer by id.
*/
getLayerById(layerId) {
return this.layerManager.getLayer(layerId);
}
/**
* Returns true if a layer exists.
*/
hasLayer(layerId) {
return !!this.layerManager.getLayer(layerId);
}
/**
* Returns the active layer state.
*/
getActiveLayer() {
return this.layerManager.getActiveLayer();
}
/**
* Returns feature groups assigned to the given layer.
*/
getFeatureGroupsByLayer(layerId) {
return this.layerManager.getFeatureGroupsForLayer(layerId);
}
/**
* Creates a new layer. Throws if the layer already exists.
*/
createLayer(input) {
const layerId = (input.id || "").trim();
if (!layerId) {
throw new Error("Layer id cannot be empty");
}
if (this.layerManager.getLayer(layerId)) {
throw new Error(`Layer "${layerId}" already exists`);
}
return this.ensureLayer(input);
}
/**
* Creates or updates a layer descriptor idempotently.
*/
ensureLayer(input) {
const layer = this.ensureLayerFromDescriptor(input);
this.updateLayerPanel();
this.updateMarkerDraggableState();
return layer;
}
/**
* Updates layer properties. Returns updated state, or undefined if not found.
*/
updateLayer(layerId, patch) {
if (!this.layerManager.getLayer(layerId)) {
return void 0;
}
let changed = false;
if ("label" in patch) {
changed = this.layerManager.setLayerLabel(layerId, patch.label) || changed;
}
if (typeof patch.color === "string") {
changed = this.layerManager.setLayerColor(layerId, patch.color) || changed;
}
if (typeof patch.visibility === "boolean") {
changed = this.layerManager.setLayerVisibility(layerId, patch.visibility) || changed;
}
if (patch.interaction !== void 0) {
changed = this.layerManager.setLayerInteraction(layerId, patch.interaction) || changed;
if (patch.interaction === "static" && patch.panel === void 0) {
changed = this.layerManager.setLayerPanelVisibility(layerId, "hidden") || changed;
}
}
if (patch.panel !== void 0) {
changed = this.layerManager.setLayerPanelVisibility(layerId, patch.panel) || changed;
}
if (patch.metadata !== void 0) {
changed = this.layerManager.setLayerMetadata(layerId, patch.metadata) || changed;
}
if (changed) {
this.updateLayerPanel();
this.updateMarkerDraggableState();
this.syncFeatureGroupOrderWithLayers();
}
return this.layerManager.getLayer(layerId);
}
/**
* Deletes a non-default layer.
*/
deleteLayer(layerId) {
const normalizedId = (layerId || "").trim();
if (!normalizedId || !this.layerManager.getLayer(normalizedId)) {
return {
success: false,
layerId: normalizedId,
removedFeatureGroups: 0,
reason: "not-found"
};
}
if (normalizedId === "default") {
return {
success: false,
layerId: normalizedId,
removedFeatureGroups: 0,
reason: "default-layer"
};
}
this.saveHistory("layerDelete");
const removedFeatureGroups = this.layerManager.deleteLayer(normalizedId);
return {
success: true,
layerId: normalizedId,
removedFeatureGroups: removedFeatureGroups.length
};
}
/**
* Activates a layer.
*/
setActiveLayer(layerId) {
return this.layerManager.setActiveLayer(layerId);
}
/**
* Updates visibility for a layer.
*/
setLayerVisibility(layerId, visible) {
return this.layerManager.setLayerVisibility(layerId, visible);
}
/**
* Shows a layer.
*/
showLayer(layerId) {
return this.setLayerVisibility(layerId, true);
}
/**
* Hides a layer.
*/
hideLayer(layerId) {
return this.setLayerVisibility(layerId, false);
}
/**
* Updates a layer color.
*/
setLayerColor(layerId, color) {
return this.layerManager.setLayerColor(layerId, color);
}
/**
* Updates interaction policy for a layer.
*/
setLayerInteraction(layerId, interaction) {
const changed = this.layerManager.setLayerInteraction(layerId, interaction);
if (changed) {
this.updateMarkerDraggableState();
this.updateLayerPanel();
}
return changed;
}
/**
* Updates panel visibility policy for a layer.
*/
setLayerPanelVisibility(layerId, panel) {
const changed = this.layerManager.setLayerPanelVisibility(layerId, panel);
if (changed) {
this.updateLayerPanel();
}
return changed;
}
/**
* Replaces layer metadata.
*/
setLayerMetadata(layerId, metadata) {
return this.layerManager.setLayerMetadata(layerId, metadata);
}
/**
* Shallow-merges metadata into existing layer metadata.
*/
patchLayerMetadata(layerId, metadataPatch) {
const layer = this.layerManager.getLayer(layerId);
if (!layer) {
return false;
}
return this.layerManager.setLayerMetadata(layerId, {
...layer.metadata,
...metadataPatch
});
}
/**
* Returns a shallow copy of feature metadata for a feature group.
*/
getFeatureMetadata(featureGroup) {
const metadataStore = featureGroup._polydrawMetadata;
if (!(metadataStore == null ? void 0 : metadataStore.metadata)) {
return void 0;
}
return cloneMetadataValue(metadataStore.metadata);
}
/**
* Replaces feature metadata for a feature group.
*/
setFeatureMetadata(featureGroup, metadata) {
if (!featureGroup) {
return false;
}
const metadataStore = this.ensureFeatureGroupMetadata(featureGroup);
metadataStore.metadata = cloneMetadataValue(metadata);
metadataStore.lastModified = /* @__PURE__ */ new Date();
return true;
}
/**
* Shallow-merges metadata into feature metadata for a feature group.
*/
patchFeatureMetadata(featureGroup, metadataPatch) {
if (!featureGroup) {
return false;
}
const current = this.getFeatureMetadata(featureGroup) ?? {};
return this.setFeatureMetadata(featureGroup, {
...current,
...metadataPatch
});
}
/**
* Reorder layers by moving one layer to another layer's position.
*/
reorderLayer(layerId, targetLayerId) {
if (layerId === "default" || targetLayerId === "default") {
return false;
}
const orderedLayerIds = this.layerManager.getAllLayers().map((layer) => layer.id);
const sourceIndex = orderedLayerIds.indexOf(layerId);
const targetIndex = orderedLayerIds.indexOf(targetLayerId);
if (sourceIndex < 0 || targetIndex < 0 || sourceIndex === targetIndex) {
return false;
}
this.saveHistory("layerReorder");
return this.layerManager.moveLayer(layerId, targetLayerId);
}
/**
* Returns the id of the currently active layer.
*/
getActiveLayerId() {
return this.layerManager.getActiveLayerId();
}
/**
* Returns the layer id that the given feature group is assigned to, or undefined.
*/
getLayerForFeatureGroup(featureGroup) {
return this.layerManager.getLayerForFeatureGroup(featureGroup);
}
/**
* Assigns a feature group to a layer. If the feature group is already
* assigned to another layer it is moved. Returns false if the target layer
* does not exist.
*/
assignFeatureGroupToLayer(featureGroup, layerId) {
const normalizedLayerId = (layerId || "").trim();
if (!featureGroup || !normalizedLayerId) {
return false;
}
if (!this.layerManager.getLayer(normalizedLayerId)) {
return false;
}
if (this.layerManager.getLayerForFeatureGroup(featureGroup) === normalizedLayerId) {
return false;
}
this.saveHistory("layerReorder");
const changed = this.layerManager.assignFeatureGroupToLayer(featureGroup, normalizedLayerId);
if (changed) {
this.refreshAfterLayerStructureChange();
}
return changed;
}
/**
* Moves a feature group from its current layer to the given layer.
* Alias for {@link assignFeatureGroupToLayer}.
*/
moveFeatureGroupToLayer(featureGroup, layerId) {
return this.assignFeatureGroupToLayer(featureGroup, layerId);
}
/**
* Removes a feature group from whatever layer it is currently assigned to.
*/
removeFeatureGroupFromLayer(featureGroup) {
if (!featureGroup || !this.layerManager.getLayerForFeatureGroup(featureGroup)) {
return;
}
this.saveHistory("layerReorder");
this.layerManager.removeFeatureGroupFromLayer(featureGroup);
this.refreshAfterLayerStructureChange();
}
/**
* Deletes all non-default layers and removes their feature groups from the map.
* The default layer is left empty. A single history snapshot is saved before clearing.
*/
clearLayers() {
const nonDefault = this.layerManager.getAllLayers().filter((l) => l.id !== "default").map((l) => l.id);
if (nonDefault.length === 0) {
return;
}
this.saveHistory("layerDelete");
const removedFeatureGroups = [];
for (const layerId of nonDefault) {
removedFeatureGroups.push(...this.layerManager.deleteLayer(layerId));
}
if (removedFeatureGroups.length > 0) {
this.removeFeatureGroupsFromMap(removedFeatureGroups);
return;
}
this.updateLayerPanel();
}
/**
* Moves a layer to a specific 0-based position in the layer order.
* The default layer always occupies index 0; valid target indices for
* non-default layers start at 1. The index is clamped to the valid range.
*/
moveLayerToIndex(layerId, index) {
const normalizedLayerId = (layerId || "").trim();
if (normalizedLayerId === "default" || !Number.isFinite(index)) {
return false;
}
const orderedLayerIds = this.layerManager.getAllLayers().map((layer) => layer.id);
const currentIndex = orderedLayerIds.indexOf(normalizedLayerId);
if (currentIndex < 0) {
return false;
}
const clampedIndex = Math.max(1, Math.min(Math.trunc(index), orderedLayerIds.length - 1));
if (currentIndex === clampedIndex) {
return false;
}
this.saveHistory("layerReorder");
return this.layerManager.moveLayerToIndex(normalizedLayerId, index);
}
/**
* Sets the full layer order by providing an ordered list of layer IDs.
* The default layer always stays first. Layers not included in the list are
* appended after the specified layers in their current relative order.
* Returns false if any specified ID does not exist or equals 'default'.
*/
setLayerOrder(layerIds) {
if (!layerIds || layerIds.length === 0) {
return false;
}
const currentOrder = this.layerManager.getAllLayers().map((layer) => layer.id);
const normalizedIds = layerIds.map((layerId) => (layerId || "").trim());
if (normalizedIds.some((layerId) => layerId.length === 0 || layerId === "default")) {
return false;
}
if (new Set(normalizedIds).size !== normalizedIds.length) {
return false;
}
if (normalizedIds.some((layerId) => !currentOrder.includes(layerId))) {
return false;
}
const specifiedIds = new Set(normalizedIds);
const fullOrder = [
"default",
...normalizedIds,
...currentOrder.filter((layerId) => layerId !== "default" && !specifiedIds.has(layerId))
];
if (currentOrder.length === fullOrder.length && currentOrder.every((id, index) => id === fullOrder[index])) {
return false;
}
this.saveHistory("layerReorder");
return this.layerManager.setLayerOrder(normalizedIds);
}
refreshAfterLayerStructureChange() {
this.syncFeatureGroupOrderWithLayers();
this.polygonInformation.deletePolygonInformationStorage();
this.polygonInformation.createPolygonInformationStorage(this.arrayOfFeatureGroups);
this.updateMarkerDraggableState();
this.updateLayerPanel();
}
/**
* Begin a batch operation. A single history snapshot is saved before the
* batch starts; all individual history saves inside the batch are suppressed.
* Must be paired with {@link endBatch}.
* @param action - Optional history action label for the snapshot.
*/
beginBatch(action = "batch") {
this.startHistoryBatch(action);
}
/**
* End a batch operation started with {@link beginBatch}.
*/
endBatch() {
this.endHistoryBatch();
}
/**
* Sets the current drawing mode.
* @param mode - The drawing mode to set.
*/
setDrawMode(mode, options) {
const previousMode = this.drawMode;
this._updateDrawModeState(mode);
const preserveActiveDraw = (options == null ? void 0 : options.preserveActiveDraw) && this.isDrawingInProgress && (previousMode === DrawMode.Add && mode === DrawMode.Subtract || previousMode === DrawMode.Subtract && mode === DrawMode.Add);
if (!preserveActiveDraw) {
const switchingToP2P = (mode === DrawMode.PointToPoint || mode === DrawMode.PointToPointSubtract) && previousMode !== DrawMode.PointToPoint && previousMode !== DrawMode.PointToPointSubtract;
if (switchingToP2P && this.isDrawingInProgress) {
this.stopDraw();
}
if ((previousMode === DrawMode.PointToPoint || previousMode === DrawMode.PointToPointSubtract) && mode !== DrawMode.PointToPoint && mode !== DrawMode.PointToPointSubtract) {
this.polygonDrawManager.clearP2pMarkers();
this.stopDraw();
} else if (mode === DrawMode.Off) {
this.stopDraw();
} else if (mode !== DrawMode.PointToPoint && mode !== DrawMode.PointToPointSubtract) {
this.stopDraw();
}
}
if (this.map) {
this._updateUIAfterDrawModeChange(mode);
this._updateMapInteractions();
}
}
/**
* Returns the current drawing mode.
* @returns The current DrawMode.
*/
getDrawMode() {
return this.modeManager.getCurrentMode();
}
/**
* Registers an event listener for a given event type.
* @param event - The event type to listen for.
* @param callback - The callback function to execute when the event is triggered.
*/
on(event, callback) {
this.eventManager.on(event, callback);
}
/**
* Unregisters an event listener for a given event type.
* @param event - The event type to stop listening for.
* @param callback - The callback function to remove.
*/
off(event, callback) {
this.eventManager.off(event, callback);
}
/**
* Removes all feature groups from the map and clears the internal storage.
*/
removeAllFeatureGroups() {
this.removeFeatureGroupsFromMap([...this.arrayOfFeatureGroups], { clearLayerState: true });
}
removeDefaultLayerFeatureGroups() {
this.removeFeatureGroupsFromMap(this.layerManager.getFeatureGroupsForLayer("default"));
}
removeFeatureGroupsFromMap(featureGroups, options = {}) {
if (featureGroups.length === 0) {
return;
}
featureGroups.forEach((featureGroup) => {
var _a2;
try {
if (this.polygonMutationManager) {
this.polygonMutationManager.cleanupFeatureGroup(featureGroup);
}
this.map.removeLayer(featureGroup);
} catch {
}
const index = this.arrayOfFeatureGroups.indexOf(featureGroup);
if (index !== -1) {
this.arrayOfFeatureGroups.splice(index, 1);
}
(_a2 = this.layerManager) == null ? void 0 : _a2.removeFeatureGroupFromLayer(featureGroup);
});
this._lastAppliedVisibleMapOrder = [];
if (options.clearLayerState && this.layerManager) {
this.layerManager.clear(this.config.styles.polygon.color);
}
this.polygonInformation.deletePolygonInformationStorage();
this.polygonInformation.updatePolygons();
this.updateActivateButtonIndicator();
this.updateLayerPanel();
this.eventManager.emit("polydraw:polygon:deleted", void 0);
}
/**
* Public method to perform comprehensive cleanup
* This can be called manually to clean up resources without removing the control from the map
*/
cleanup() {
if (!this.map) {
return;
}
this.comprehensiveCleanup();
}
/**
* Initializes the user interface, creates DOM elements, sets up buttons, and injects styles.
* @param container - The main control container element.
*/
initializeUI(container) {
injectDynamicStyles(this.config);
L.DomEvent.disableClickPropagation(container);
L.DomEvent.on(container, "mousedown", L.DomEvent.stopPropagation);
L.DomEvent.on(container, "touchstart", L.DomEvent.stopPropagation);
L.DomEvent.on(container, "pointerdown", L.DomEvent.stopPropagation);
L.DomEvent.on(container, "pointerup", L.DomEvent.stopPropagation);
L.DomEvent.on(container, "click", L.DomEvent.stopPropagation);
const markControlEvent = (event) => {
this.controlEvents.add(event);
this.markControlPointerDown(event);
};
const clearControlEvent = (event) => {
this.clearControlPointerDown(event);
};
if (this.pointerEventsHandled) {
container.addEventListener("pointerdown", markControlEvent, { capture: true, passive: true });
container.addEventListener("pointerup", clearControlEvent, { capture: true, passive: true });
container.addEventListener("pointercancel", clearControlEvent, {
capture: true,
passive: true
});
} else {
container.addEventListener("mousedown", markControlEvent, { capture: true, passive: true });
container.addEventListener("mouseup", clearControlEvent, { capture: true, passive: true });
container.addEventListener("touchstart", markControlEvent, { capture: true, passive: true });
container.addEventListener("touchend", clearControlEvent, { capture: true, passive: true });
container.addEventListener("touchcancel", clearControlEvent, {
capture: true,
passive: true
});
}
container.style.display = "flex";
container.style.flexDirection = "column-reverse";
container.style.pointerEvents = "auto";
container.style.position = "relative";
container.style.zIndex = "1000";
const tooltipConfig = this.config.tooltips;
if (tooltipConfig) {
const delayMs = Math.max(0, tooltipConfig.delayMs);
container.setAttribute("data-tooltip-enabled", tooltipConfig.enabled ? "true" : "false");
container.setAttribute("data-tooltip-direction", tooltipConfig.direction);
container.style.setProperty("--polydraw-tooltip-bg", tooltipConfig.backgroundColor);
container.style.setProperty("--polydraw-tooltip-color", tooltipConfig.color);
container.style.setProperty("--polydraw-tooltip-delay", `${delayMs}ms`);
}
this.subContainer = leafletAdapter.domUtil.create("div", "sub-buttons", container);
this.subContainer.setAttribute("data-polydraw", "sub-buttons");
this.subContainer.style.maxHeight = "0px";
this.subContainer.style.overflow = "hidden";
this.subContainer.style.transition = "max-height 0.3s ease";
this.subContainer.style.pointerEvents = "auto";
this.subContainer.style.position = "relative";
if (!this.eventManager) {
this.eventManager = new EventManager();
}
if (!this.historyManager) {
this.historyManager = new HistoryManager(this.eventManager, this.config.history.maxSize);
}
createButtons(
container,
this.subContainer,
this.config,
this._handleActivateToggle,
this._handleDrawClick,
this._handleSubtractClick,
this._handleCloneClick,
this._handleEraseClick,
this._handlePointToPointClick,
this._handlePointToPointSubtractClick,
this._handleUndoClick,
this._handleRedoClick,
this.eventManager,
this.historyManager
);
this.ensureButtonTouchResponsiveness(container);
this.updateActivateButtonIndicator();
const uiUpdateListener = (mode) => {
const drawButton = container.querySelector(".icon-draw");
const subtractButton = container.querySelector(".icon-subtract");
const p2pButton = container.querySelector(".icon-p2p");
const p2pSubtractButton = container.querySelector(".icon-p2p-subtract");
const cloneButton = container.querySelector(".icon-clone");
if (drawButton) drawButton.classList.toggle("active", mode === DrawMode.Add);
if (subtractButton) subtractButton.classList.toggle("active", mode === DrawMode.Subtract);
if (p2pButton) p2pButton.classList.toggle("active", mode === DrawMode.PointToPoint);
if (p2pSubtractButton)
p2pSubtractButton.classList.toggle("active", mode === DrawMode.PointToPointSubtract);
if (cloneButton) cloneButton.classList.toggle("active", mode === DrawMode.Clone);
};
this.drawModeListeners.push(uiUpdateListener);
}
/**
* Attaches listeners to polygonMutationManager and eventManager.
*/
addCoreEventListener(event, callback) {
this.eventManager.on(event, callback);
this._coreEventSubscriptions.push({
event,
callback
});
}
removeCoreEventListeners() {
if (!this.eventManager) {
this._coreEventListenersAttached = false;
return;
}
for (const subscription of this._coreEventSubscriptions) {
this.eventManager.off(
subscription.event,
subscription.callback
);
}
this._coreEventSubscriptions.length = 0;
this._coreEventListenersAttached = false;
}
setupEventListeners() {
if (this._coreEventListenersAttached) {
return;
}
this._coreEventListenersAttached = true;
this.addCoreEventListener("polygonOperationComplete", () => {
this.updateActivateButtonIndicator();
this.modeManager.updateStateForMode(DrawMode.Off);
this.drawMode = DrawMode.Off;
this.emitDrawModeChanged();
this.updateMarkerDraggableState();
const shouldShowCrosshair = this.modeManager.shouldShowCrosshairCursor();
const mapDragEnabled = this.modeManager.canPerformAction("mapDrag");
const mapZoomEnabled = this.modeManager.canPerformAction("mapZoom");
const mapDoubleClickEnabled = this.modeManager.canPerformAction("mapDoubleClickZoom");
if (shouldShowCrosshair) {
leafletAdapter.domUtil.addClass(this.map.getContainer(), "crosshair-cursor-enabled");
} else {
leafletAdapter.domUtil.removeClass(this.map.getContainer(), "crosshair-cursor-enabled");
}
this.events(false);
this.setLeafletMapEvents(mapDragEnabled, mapDoubleClickEnabled, mapZoomEnabled);
this.applyTracerStyle(DrawMode.Off);
this.syncFeatureGroupOrderWithLayers();
this.cleanupEmptyLayers();
});
this.addCoreEventListener("polygonDeleted", () => {
this.updateActivateButtonIndicator();
this.cleanupEmptyLayers();
});
this.addCoreEventListener("polydraw:polygon:created", async (data) => {
this.stopDraw();
const interactionLayerId = this.getInteractionTargetLayerId();
if (data.isPointToPoint) {
this.saveHistory("pointToPoint");
if (data.mode === DrawMode.PointToPointSubtract) {
await this.polygonMutationManager.subtractPolygon(data.polygon, {
simplify: false,
targetLayerId: interactionLayerId
});
} else {
await this.polygonMutationManager.addPolygon(data.polygon, {
simplify: false,
noMerge: false,
targetLayerId: interactionLayerId
});
}
} else {
await this.handleFreehandDrawCompletion(data.polygon);
}
this.polygonInformation.createPolygonInformationStorage(this.arrayOfFeatureGroups);
});
this.addCoreEventListener("polydraw:draw:cancel", () => {
this.stopDraw();
this.setDrawMode(this.config.tools.default);
});
this.addCoreEventListener("polydraw:layer:visibility", (data) => {
const layerState = this.layerManager.getLayer(data.layerId);
if (!layerState) return;
for (const fg of layerState.featureGroups) {
try {
if (data.visible) {
fg.addTo(this.map);
} else {
this.map.removeLayer(fg);
}
} catch {
}
}
this.syncFeatureGroupOrderWithLayers();
this.updateLayerPanel();
});
this.addCoreEventListener("polydraw:layer:activated", () => {
this.updateMarkerDraggableState();
this.updateLayerPanel();
});
this.addCoreEventListener("polydraw:layer:colorChanged", () => {
this.updateLayerPanel();
});
this.addCoreEventListener("polydraw:layer:created", () => {
this.updateLayerPanel();
});
this.addCoreEventListener("polydraw:layer:delete-requested", (data) => {
this.saveHistory("layerDelete");
this.layerManager.deleteLayer(data.layerId);
});
this.addCoreEventListener("polydraw:layer:reorder-requested", (data) => {
this.reorderLayer(data.layerId, data.targetLayerId);
});
this.addCoreEventListener("polydraw:layer:reordered", () => {
this.syncFeatureGroupOrderWithLayers();
this.polygonInformation.deletePolygonInformationStorage();
this.polygonInformation.createPolygonInformationStorage(this.arrayOfFeatureGroups);
this.updateLayerPanel();
});
this.addCoreEventListener("polydraw:layer:deleted", (data) => {
for (const fg of data.removedFeatureGroups) {
try {
if (this.polygonMutationManager) {
this.polygonMutationManager.cleanupFeatureGroup(fg);
}
fg.clearLayers();
this.map.removeLayer(fg);
} catch {
}
const idx = this.arrayOfFeatureGroups.indexOf(fg);
if (idx > -1) {
this.arrayOfFeatureGroups.splice(idx, 1);
}
}
this.syncFeatureGroupOrderWithLayers();
this.polygonInformation.updatePolygons();
this.updateActivateButtonIndicator();
this.updateMarkerDraggableState();
this.updateLayerPanel();
});
}
/**
* Initializes and adds the tracer polyline to the map.
*/
createTracer() {
this.tracer = leafletAdapter.createPolyline([], {
...this.config.styles.polyline
});
try {
this.tracer.addTo(this.map);
} catch {
}
}
/**
* Applies the correct weight/opacity/color/dash style to the tracer for the given mode.
*/
applyTracerStyle(mode) {
if (!this.tracer) return;
try {
const isSubtractMode = mode === DrawMode.Subtract || mode === DrawMode.PointToPointSubtract;
const baseStyle = this.getTracerBaseStyle(isSubtractMode);
let dashArray;
let color = "";
switch (mode) {
case DrawMode.Add:
color = this.config.styles.polyline.color;
dashArray = void 0;
break;
case DrawMode.Subtract:
color = this.config.styles.subtractLine.color;
dashArray = void 0;
break;
case DrawMode.PointToPoint:
color = this.config.styles.polyline.color;
dashArray = "5, 5";
break;
case DrawMode.PointToPointSubtract:
color = this.config.styles.subtractLine.color;
dashArray = "5, 5";
break;
case DrawMode.Off:
default:
color = "";
dashArray = void 0;
break;
}
this.tracer.setStyle({
...baseStyle,
color,
dashArray
});
} catch {
}
}
/**
* Returns base tracer styles depending on whether we're in subtract mode.
*/
getTracerBaseStyle(isSubtractMode) {
const options = isSubtractMode ? this.config.styles.subtractLine : this.config.styles.polyline;
return {
weight: options.weight,
opacity: options.opacity
};
}
/**
* Sets up PolygonDrawManager and PolygonMutationManager with the map.
*/
initializeManagers() {
this.polygonDrawManager = new PolygonDrawManager({
turfHelper: this.turfHelper,
map: this.map,
config: this.config,
modeManager: this.modeManager,
eventManager: this.eventManager,
tracer: this.tracer
});
this.polygonMutationManager = new PolygonMutationManager({
turfHelper: this.turfHelper,
polygonInformation: this.polygonInformation,
map: this.map,
config: this.config,
modeManager: this.modeManager,
eventManager: this.eventManager,
getFeatureGroups: () => this.arrayOfFeatureGroups,
saveHistoryState: (action) => {
this.saveHistory(action);
},
layerManager: this.layerManager
});
}
/**
* Loads an external configuration file and merges it with the default and inline configs.
* @param configPath - The path to the external configuration file.
* @param inlineConfig - An optional inline configuration object.
*/
async loadExternalConfig(configPath, inlineConfig) {
try {
const response = await fetch(configPath);
if (!response.ok) {
throw new Error(
`Failed to load config from ${configPath}: ${response.status} ${response.statusText}`
);
}
const externalConfig = normalizeConfigInput(await response.json());
this.config = deepMerge(
structuredClone(defaultConfig),
externalConfig,
inlineConfig ?? {}
);
applyRuntimeConfigFallbacks(this.config);
this.initializeComponents();
} catch (error) {
console.warn(
"Failed to load external config, falling back to default + inline config:",
error
);
this.config = deepMerge(structuredClone(defaultConfig), inlineConfig ?? {});
applyRuntimeConfigFallbacks(this.config);
this.initializeComponents();
}
}
/**
* Initializes the core components of the Polydraw control.
*/
/**
* Updates the state of the drawing mode.
* @param mode - The new drawing mode.
*/
_updateDrawModeState(mode) {
var _a2;
this.drawMode = mode;
this.modeManager.updateStateForMode(mode);
this.emitDrawModeChanged();
this.updateMarkerDraggableState();
if ((_a2 = this.polygonDrawManager) == null ? void 0 : _a2.refreshP2PMarkerState) {
this.polygonDrawManager.refreshP2PMarkerState();
}
}
/**
* Updates the UI after a change in the drawing mode.
* @param mode - The new drawing mode.
*/
_updateUIAfterDrawModeChange(mode) {
const shouldShowCrosshair = this.modeManager.shouldShowCrosshairCursor();
if (shouldShowCrosshair) {
leafletAdapter.domUtil.addClass(this.map.getContainer(), "crosshair-cursor-enabled");
} else {
leafletAdapter.domUtil.removeClass(this.map.getContainer(), "crosshair-cursor-enabled");
}
this.applyTracerStyle(mode);
}
shouldEnableDrawEvents(mode) {
return mode === DrawMode.Add || mode === DrawMode.Subtract || mode === DrawMode.PointToPoint || mode === DrawMode.PointToPointSubtract;
}
_updateMapInteractions() {
const mapDragEnabled = this.modeManager.canPerformAction("mapDrag");
const mapZoomEnabled = this.modeManager.canPerformAction("mapZoom");
const mapDoubleClickEnabled = this.modeManager.canPerformAction("mapDoubleClickZoom");
const drawEventsEnabled = this.shouldEnableDrawEvents(this.drawMode);
this.events(drawEventsEnabled);
this.setMapDrawingTouchLock(drawEventsEnabled);
this.setLeafletMapEvents(mapDragEnabled, mapDoubleClickEnabled, mapZoomEnabled);
}
setMapDrawingTouchLock(enabled) {
if (!this.map) {
return;
}
const container = this.map.getContainer();
if (enabled) {
if (this.originalMapTouchAction === null) {
this.originalMapTouchAction = container.style.touchAction || "";
}
container.style.touchAction = "none";
return;
}
if (this.originalMapTouchAction !== null) {
container.style.touchAction = this.originalMapTouchAction;
this.originalMapTouchAction = null;
}
}
/**
* Restore the map state from a history snapshot
*/
async restoreFromSnapshot(snapshot) {
var _a2, _b2, _c;
if (!snapshot || !Array.isArray(snapshot.features)) {
console.warn("Invalid history snapshot provided for restore; skipping restore operation.");
return;
}
this.historyManager.setRestoring(true);
this._isRestoringSnapshot = true;
try {
this.removeAllFeatureGroups();
for (const [featureIndex, feature2] of snapshot.features.entries()) {
const featureSnapshot = (_a2 = snapshot.featureMetadataSnapshot) == null ? void 0 : _a2[featureIndex];
const restoreLayerId = (featureSnapshot == null ? void 0 : featureSnapshot.layerId) && ((_b2 = this.layerManager) == null ? void 0 : _b2.getLayer(featureSnapshot.layerId)) ? featureSnapshot.layerId : void 0;
await this.polygonMutationManager.addPolygon(feature2, {
noMerge: true,
simplify: false,
skipKinkProcessing: true,
targetLayerId: restoreLayerId,
visualOptimizationLevel: (featureSnapshot == null ? void 0 : featureSnapshot.optimizationLevel) ?? 0,
originalOptimizationLevel: featureSnapshot == null ? void 0 : featureSnapshot.originalOptimizationLevel,
featureId: featureSnapshot == null ? void 0 : featureSnapshot.id,
featureMetadata: featureSnapshot == null ? void 0 : featureSnapshot.metadata,
sourceFeatureIds: featureSnapshot == null ? void 0 : featureSnapshot.sourceFeatureIds,
featureInteractionOverride: featureSnapshot == null ? void 0 : featureSnapshot.interactionOverride,
featureStyleOverrides: featureSnapshot == null ? void 0 : featureSnapshot.styleOverrides,
featureCreatedAt: featureSnapshot == null ? void 0 : featureSnapshot.createdAt,
featureLastModified: featureSnapshot == null ? void 0 : featureSnapshot.lastModified
});
}
if (snapshot.layerSnapshot && this.layerManager) {
this.layerManager.restoreFromLayerSnapshot(
snapshot.layerSnapshot,
this.arrayOfFeatureGroups
);
for (const layer of this.layerManager.getAllLayers()) {
for (const fg of layer.featureGroups) {
const styleOverrides = (_c = fg._polydrawMetadata) == null ? void 0 : _c.styleOverrides;
fg.eachLayer((l) => {
if (l instanceof L.Polygon && !(l instanceof L.Rectangle)) {
l.setStyle({
color: (styleOverrides == null ? void 0 : styleOverrides.color) ?? layer.color,
fillColor: (styleOverrides == null ? void 0 : styleOverrides.fillColor) ?? this.config.styles.polygon.fillColor,
fillOpacity: (styleOverrides == null ? void 0 : styleOverrides.fillOpacity) ?? this.config.styles.polygon.fillOpacity,
weight: (styleOverrides == null ? void 0 : styleOverrides.weight) ?? this.config.styles.polygon.weight
});
}
});
}
if (!layer.visible) {
for (const fg of layer.featureGroups) {
try {
this.map.removeLayer(fg);
} catch {
}
}
}
}
this.updateLayerPanel();
}
if (this.layerManager) {
this.syncFeatureGroupOrderWithLayers();
}
this.polygonInformation.updatePolygons();
this.updateMarkerDraggableState();
} finally {
this._isRestoringSnapshot = false;
this.historyManager.setRestoring(false);
}
}
initializeComponents() {
if (this._componentsInitialized) return;
this._componentsInitialized = true;
this.turfHelper = new TurfHelper(this.config);
this.mapStateService = new MapStateService();
this.eventManager = this.eventManager ?? new EventManager();
this.polygonInformation = new PolygonInformationService(this.mapStateService);
this.modeManager = new ModeManager(this.config, this.eventManager);
if (!this.historyManager) {
this.historyManager = new HistoryManager(this.eventManager, this.config.history.maxSize);
} else {
this.historyManager.setMaxHistorySize(this.config.history.maxSize);
}
this.layerManager = new LayerManager(this.eventManager, this.config.styles.polygon.color);
this.polygonInformation.onPolygonInfoUpdated((_k) => {
this.updateActivateButtonIndicator();
});
this._boundKeyDownHandler = this.handleKeyDown.bind(this);
}
/**
* Emits an event to notify listeners that the drawing mode has changed.
*/
emitDrawModeChanged() {
this.eventManager.emit("polydraw:mode:change", {
mode: this.modeManager.getCurrentMode()
});
for (const cb of this.drawModeListeners) {
cb(this.modeManager.getCurrentMode());
}
}
/**
* Update the draggable state of all existing markers when draw mode changes
*/
updateMarkerDraggableState() {
if (this.polygonMutationManager) {
this.polygonMutationManager.updateMarkerDraggableState();
}
}
/**
* Update the layer panel visibility and contents
*/
updateLayerPanel() {
if (!this.map || !this.layerManager) return;
const shouldShow = this.layerManager.getPanelLayerCount() > 1;
if (!shouldShow) {
if (this.layerPanel) {
try {
this.layerPanel.remove();
} catch {
}
this.layerPanel = null;
}
return;
}
if (!this.layerPanel) {
const panel = createLayerPanel(this.layerManager, this.eventManager);
this.layerPanel = panel;
panel.addTo(this.map);
return;
}
this.layerPanel.refresh();
}
/**
* Reorder the global feature-group array to follow the current layer order
* and re-apply map draw order accordingly.
*/
syncFeatureGroupOrderWithLayers() {
if (!this.layerManager) return;
const current = this.arrayOfFeatureGroups;
if (current.length === 0) {
return;
}
const currentSet = new Set(current);
const seen = /* @__PURE__ */ new Set();
const ordered = [];
for (const layer of this.layerManager.getAllLayers()) {
for (const featureGroup of layer.featureGroups) {
if (!currentSet.has(featureGroup) || seen.has(featureGroup)) {
continue;
}
seen.add(featureGroup);
ordered.push(featureGroup);
}
}
for (const featureGroup of current) {
if (!seen.has(featureGroup)) {
ordered.push(featureGroup);
}
}
if (!this.isSameFeatureGroupOrder(current, ordered)) {
current.splice(0, current.length, ...ordered);
}
this.reapplyFeatureGroupMapOrder();
}
isSameFeatureGroupOrder(a, b) {
if (a.length !== b.length) {
return false;
}
for (let index = 0; index < a.length; index += 1) {
if (a[index] !== b[index]) {
return false;
}
}
return true;
}
/**
* Re-apply map layer order for visible feature groups. Layer order is stored and
* shown top-to-bottom, while Leaflet renders later-added layers on top.
*/
reapplyFeatureGroupMapOrder() {
if (!this.map || !this.layerManager) {
return;
}
const visibleFeatureGroups = this.arrayOfFeatureGroups.filter((featureGroup) => {
const layerId = this.layerManager.getLayerForFeatureGroup(featureGroup);
const layerState = layerId ? this.layerManager.getLayer(layerId) : void 0;
return layerState ? layerState.visible !== false : true;
});
const layerIndex = /* @__PURE__ */ new Map();
this.layerManager.getAllLayers().forEach((layer, index) => {
layerIndex.set(layer.id, index);
});
const mapRenderOrder = [...visibleFeatureGroups].sort((left, right) => {
var _a2, _b2;
const leftLayerId = (_a2 = this.layerManager) == null ? void 0 : _a2.getLayerForFeatureGroup(left);
const rightLayerId = (_b2 = this.layerManager) == null ? void 0 : _b2.getLayerForFeatureGroup(right);
const leftIndex = leftLayerId ? layerIndex.get(leftLayerId) ?? layerIndex.size : layerIndex.size;
const rightIndex = rightLayerId ? layerIndex.get(rightLayerId) ?? layerIndex.size : layerIndex.size;
return rightIndex - leftIndex;
});
const visibleSet = new Set(visibleFeatureGroups);
const hasMatchingVisibilityState = this.arrayOfFeatureGroups.every((featureGroup) => {
const shouldBeVisible = visibleSet.has(featureGroup);
return this.map.hasLayer(featureGroup) === shouldBeVisible;
});
if (hasMatchingVisibilityState && this.isSameFeatureGroupOrder(this._lastAppliedVisibleMapOrder, mapRenderOrder)) {
return;
}
for (const featureGroup of this.arrayOfFeatureGroups) {
if (!visibleSet.has(featureGroup) && this.map.hasLayer(featureGroup)) {
try {
this.map.removeLayer(featureGroup);
} catch {
}
}
}
for (const featureGroup of mapRenderOrder) {
try {
if (this.map.hasLayer(featureGroup)) {
this.map.removeLayer(featureGroup);
}
featureGroup.addTo(this.map);
} catch {
}
}
this._lastAppliedVisibleMapOrder = [...mapRenderOrder];
}
/**
* Remove non-default layers that have no feature groups left.
*/
cleanupEmptyLayers() {
if (!this.layerManager || this._isRestoringSnapshot) return;
const emptyLayerIds = [];
for (const layer of this.layerManager.getAllLayers()) {
if (layer.id !== "default" && layer.featureGroups.length === 0) {
emptyLayerIds.push(layer.id);
}
}
for (const id of emptyLayerIds) {
this.layerManager.deleteLayer(id);
}
if (emptyLayerIds.length > 0) {
this.updateLayerPanel();
}
}
/**
* Stops the current drawing operation and resets the tracer.
*/
stopDraw() {
this.isDrawingInProgress = false;
this.resetTracker();
this.drawStartedEvents(false);
}
/**
* Enables or disables Leaflet's default map interactions.
* @param enableDragging - Whether to enable map dragging.
* @param enableDoubleClickZoom - Whether to enable double-click zoom.
* @param enableScrollWheelZoom - Whether to enable scroll wheel zoom.
*/
setLeafletMapEvents(enableDragging, enableDoubleClickZoom, enableScrollWheelZoom) {
if (enableDragging) {
this.map.dragging.enable();
} else {
this.map.dragging.disable();
}
if (enableDoubleClickZoom) {
this.map.doubleClickZoom.enable();
} else {
this.map.doubleClickZoom.disable();
}
if (enableScrollWheelZoom) {
this.map.scrollWheelZoom.enable();
} else {
this.map.scrollWheelZoom.disable();
}
}
/**
* Resets the tracer polyline by clearing its LatLngs.
*/
resetTracker() {
var _a2;
(_a2 = this.tracer) == null ? void 0 : _a2.setLatLngs([]);
}
/**
* Attaches or detaches the mouse move and mouse up event listeners for drawing.
* @param onoff - A boolean indicating whether to attach or detach the events.
*/
drawStartedEvents(onoff) {
if (this.drawEventsAttached === onoff) {
return;
}
this.drawEventsAttached = onoff;
const usePointerEvents = this.pointerEventsHandled;
const onoroff = onoff ? "on" : "off";
if (!usePointerEvents) {
this.map[onoroff]("mousemove", this.mouseMove, this);
this.map[onoroff]("mouseup", this.mouseUpLeave, this);
}
if (onoff) {
if (!this._boundMouseMove) {
this._boundMouseMove = (e) => this.mouseMove(e);
}
if (!this._boundMouseUp) {
this._boundMouseUp = (e) => this.mouseUpLeave(e);
}
document.addEventListener("mousemove", this._boundMouseMove);
document.addEventListener("mouseup", this._boundMouseUp);
if (!usePointerEvents) {
if (!this._boundTouchMove) {
this._boundTouchMove = (e) => this.mouseMove(e);
}
if (!this._boundTouchEnd) {
this._boundTouchEnd = (e) => this.mouseUpLeave(e);
}
if (!this._boundTouchCancel) {
this._boundTouchCancel = (e) => this.mouseUpLeave(e);
}
document.addEventListener("touchmove", this._boundTouchMove, { passive: false });
document.addEventListener("touchend", this._boundTouchEnd, { passive: false });
document.addEventListener("touchcancel", this._boundTouchCancel, { passive: false });
}
if (usePointerEvents) {
if (!this._boundPointerMove) {
this._boundPointerMove = (e) => this.mouseMove(e);
}
if (!this._boundPointerUp) {
this._boundPointerUp = (e) => this.mouseUpLeave(e);
}
if (!this._boundPointerCancel) {
this._boundPointerCancel = (e) => this.mouseUpLeave(e);
}
document.addEventListener("pointermove", this._boundPointerMove, { passive: false });
document.addEventListener("pointerup", this._boundPointerUp, { passive: false });
document.addEventListener("pointercancel", this._boundPointerCancel, { passive: false });
}
} else {
if (this._boundMouseMove) {
document.removeEventListener("mousemove", this._boundMouseMove);
}
if (this._boundMouseUp) {
document.removeEventListener("mouseup", this._boundMouseUp);
}
if (this._boundTouchMove) {
document.removeEventListener("touchmove", this._boundTouchMove);
}
if (this._boundTouchEnd) {
document.removeEventListener("touchend", this._boundTouchEnd);
}
if (this._boundTouchCancel) {
document.removeEventListener("touchcancel", this._boundTouchCancel);
}
if (this._boundPointerMove) {
document.removeEventListener("pointermove", this._boundPointerMove);
}
if (this._boundPointerUp) {
document.removeEventListener("pointerup", this._boundPointerUp);
}
if (this._boundPointerCancel) {
document.removeEventListener("pointercancel", this._boundPointerCancel);
}
}
}
/**
* Attaches or detaches the main drawing event listeners.
* @param onoff - A boolean indicating whether to attach or detach the events.
*/
events(onoff) {
if (this.mapEventsAttached === onoff) {
return;
}
this.mapEventsAttached = onoff;
const usePointerEvents = this.pointerEventsHandled;
const onoroff = onoff ? "on" : "off";
if (!usePointerEvents) {
this.map[onoroff]("mousedown", this.mouseDown, this);
}
this.map[onoroff]("dblclick", this.handleDoubleClick, this);
if (onoff) {
if (!this._boundContextMenu) {
this._boundContextMenu = (e) => e.preventDefault();
}
this.map.getContainer().addEventListener("contextmenu", this._boundContextMenu);
if (!usePointerEvents) {
if (!this._boundTouchStart) {
this._boundTouchStart = (e) => this.handleTouchStart(e);
}
this.map.getContainer().addEventListener("touchstart", this._boundTouchStart, { passive: false });
}
if (usePointerEvents) {
if (!this._boundPointerDown) {
this._boundPointerDown = (e) => this.mouseDown(e);
}
this.map.getContainer().addEventListener("pointerdown", this._boundPointerDown, { passive: false });
}
} else {
if (this._boundContextMenu) {
this.map.getContainer().removeEventListener("contextmenu", this._boundContextMenu);
}
if (this._boundTouchStart) {
this.map.getContainer().removeEventListener("touchstart", this._boundTouchStart);
}
if (this._boundPointerDown) {
this.map.getContainer().removeEventListener("pointerdown", this._boundPointerDown);
}
}
}
/**
* Handle touch start events with double-tap detection
* @param event - The touch event
*/
handleTouchStart(event) {
if (EventAdapter.shouldPreventDefault(event)) {
event.preventDefault();
}
const currentTime = Date.now();
const timeDiff = currentTime - this._lastTapTime;
const isPointToPointMode = this.modeManager.getCurrentMode() === DrawMode.PointToPoint || this.modeManager.getCurrentMode() === DrawMode.PointToPointSubtract;
if (isPointToPointMode) {
if (this._tapTimeout) {
clearTimeout(this._tapTimeout);
this._tapTimeout = null;
}
const tapLatLng = EventAdapter.extractCoordinates(event, this.map);
if (this.isP2PDoubleTapClose(tapLatLng, timeDiff)) {
this.handleDoubleTap(event);
this._lastTapTime = 0;
this._lastTapLatLng = null;
return;
}
this._lastTapTime = currentTime;
this._lastTapLatLng = tapLatLng;
this.mouseDown(event);
return;
}
if (this._tapTimeout) {
clearTimeout(this._tapTimeout);
this._tapTimeout = null;
}
this._lastTapTime = currentTime;
this._lastTapLatLng = EventAdapter.extractCoordinates(event, this.map);
this.mouseDown(event);
}
isP2PDoubleTapClose(tapLatLng, timeDiff) {
if (!this.map) return false;
if (!tapLatLng || !this._lastTapLatLng) return false;
if (timeDiff <= 0 || timeDiff > 300) return false;
const lastTapPoint = this.map.latLngToLayerPoint(this._lastTapLatLng);
const currentTapPoint = this.map.latLngToLayerPoint(tapLatLng);
const doubleTapTolerancePx = 36;
return lastTapPoint.distanceTo(currentTapPoint) <= doubleTapTolerancePx;
}
/**
* Handle double-tap for touch devices
* @param event - The touch event
*/
handleDoubleTap(event) {
if (this.modeManager.getCurrentMode() !== DrawMode.PointToPoint && this.modeManager.getCurrentMode() !== DrawMode.PointToPointSubtract) {
return;
}
this.polygonDrawManager.handleDoubleTap(event);
}
/**
* Handles the mouse down event to start a drawing operation.
* @param event - The mouse, touch, or pointer event.
*/
mouseDown(event) {
var _a2;
const normalizedEvent = EventAdapter.normalizeEvent(event);
if (this.isEventFromControl(normalizedEvent)) {
return;
}
if (EventAdapter.shouldPreventDefault(normalizedEvent)) {
(_a2 = normalizedEvent.preventDefault) == null ? void 0 : _a2.call(normalizedEvent);
}
if (this.modeManager.isInOffMode()) {
return;
}
const clickLatLng = EventAdapter.extractCoordinates(normalizedEvent, this.map);
if (!clickLatLng) {
return;
}
const isP2PMode = this.modeManager.getCurrentMode() === DrawMode.PointToPoint || this.modeManager.getCurrentMode() === DrawMode.PointToPointSubtract;
if (isP2PMode && normalizedEvent.pointerType === "touch") {
const now = Date.now();
const timeDiff = now - this._lastTapTime;
if (this.isP2PDoubleTapClose(clickLatLng, timeDiff)) {
this.handleDoubleTap(event);
this._lastTapTime = 0;
this._lastTapLatLng = null;
return;
}
this._lastTapTime = now;
this._lastTapLatLng = clickLatLng;
}
if (isP2PMode) {
this.polygonDrawManager.handlePointToPointClick(clickLatLng);
return;
}
if (!this.tracer) {
return;
}
if (this.isDrawingInProgress) {
return;
}
this.tracer.setLatLngs([clickLatLng]);
this.startDraw();
}
isEventFromControl(event) {
var _a2, _b2;
const container = this.getContainer();
if (!container) return false;
const originalEvent = event == null ? void 0 : event.originalEvent;
if (originalEvent && this.controlEvents.has(originalEvent)) return true;
if (event instanceof Event && this.controlEvents.has(event)) return true;
if (this.isRecentControlPointer(event)) return true;
const composedPath = ((_a2 = originalEvent == null ? void 0 : originalEvent.composedPath) == null ? void 0 : _a2.call(originalEvent)) ?? ((_b2 = event == null ? void 0 : event.composedPath) == null ? void 0 : _b2.call(event));
if (composedPath && composedPath.some((node) => node instanceof Node && container.contains(node))) {
return true;
}
const target = (originalEvent == null ? void 0 : originalEvent.target) ?? (event == null ? void 0 : event.target);
if (target instanceof Node && container.contains(target)) return true;
const extractClientPoint = (sourceEvent) => {
if (!sourceEvent || typeof sourceEvent !== "object") return null;
const withTouches = sourceEvent;
if (withTouches.touches && withTouches.touches.length > 0) {
return {
x: withTouches.touches[0].clientX,
y: withTouches.touches[0].clientY
};
}
const withClient = sourceEvent;
if (typeof withClient.clientX === "number" && typeof withClient.clientY === "number") {
return { x: withClient.clientX, y: withClient.clientY };
}
const withContainerPoint = sourceEvent;
if (withContainerPoint.containerPoint && this.map) {
const mapRect = this.map.getContainer().getBoundingClientRect();
return {
x: mapRect.left + withContainerPoint.containerPoint.x,
y: mapRect.top + withContainerPoint.containerPoint.y
};
}
const withLatLng = sourceEvent;
if (withLatLng.latlng && this.map) {
const mapPoint = this.map.latLngToContainerPoint(withLatLng.latlng);
const mapRect = this.map.getContainer().getBoundingClientRect();
return { x: mapRect.left + mapPoint.x, y: mapRect.top + mapPoint.y };
}
return null;
};
const clientPoint = extractClientPoint(event == null ? void 0 : event.originalEvent) ?? extractClientPoint(event);
if (!clientPoint) return false;
const rect = container.getBoundingClientRect();
const inRect = clientPoint.x >= rect.left && clientPoint.x <= rect.right && clientPoint.y >= rect.top && clientPoint.y <= rect.bottom;
if (inRect) return true;
const hitElement = document.elementFromPoint(clientPoint.x, clientPoint.y);
return !!(hitElement && container.contains(hitElement));
}
extractPointerInfo(event) {
if (!event || typeof event !== "object") return null;
const withPointer = event;
if (typeof withPointer.pointerId === "number") {
return {
id: withPointer.pointerId,
pointerType: typeof withPointer.pointerType === "string" ? withPointer.pointerType : "unknown"
};
}
if (typeof withPointer.type === "string") {
if (withPointer.type.startsWith("touch")) {
return { id: -2, pointerType: "touch" };
}
if (withPointer.type.startsWith("mouse")) {
return { id: -1, pointerType: "mouse" };
}
}
return null;
}
markControlPointerDown(event) {
const info = this.extractPointerInfo(event);
if (!info) return;
this.lastControlPointerDown = { ...info, time: Date.now() };
}
clearControlPointerDown(event) {
if (!this.lastControlPointerDown) return;
const info = this.extractPointerInfo(event);
if (!info) return;
if (info.id === this.lastControlPointerDown.id) {
this.lastControlPointerDown = null;
}
}
isRecentControlPointer(event) {
if (!this.lastControlPointerDown) return false;
const originalEvent = event == null ? void 0 : event.originalEvent;
const info = this.extractPointerInfo(originalEvent) ?? this.extractPointerInfo(event);
if (!info) return false;
const thresholdMs = this.lastControlPointerDown.pointerType === "touch" ? 450 : 80;
if (Date.now() - this.lastControlPointerDown.time > thresholdMs) return false;
if (info.pointerType !== this.lastControlPointerDown.pointerType) return false;
if (info.pointerType === "mouse") {
return true;
}
return info.id === this.lastControlPointerDown.id;
}
/**
* Handles the mouse move event to draw the tracer polyline.
* @param event - The mouse, touch, or pointer event.
*/
mouseMove(event) {
var _a2;
if (!this.map || !this.isDrawingInProgress) {
return;
}
const normalizedEvent = EventAdapter.normalizeEvent(event);
if (EventAdapter.shouldPreventDefault(normalizedEvent)) {
(_a2 = normalizedEvent.preventDefault) == null ? void 0 : _a2.call(normalizedEvent);
}
const latlng = EventAdapter.extractCoordinates(normalizedEvent, this.map);
if (latlng && this.tracer) {
this.tracer.addLatLng(latlng);
}
}
/**
* Handles the mouse up event to complete a drawing operation.
* @param event - The mouse, touch, or pointer event.
*/
async mouseUpLeave(event) {
var _a2;
if (!this.map || !this.isDrawingInProgress) {
return;
}
const normalizedEvent = EventAdapter.normalizeEvent(event);
if (EventAdapter.shouldPreventDefault(normalizedEvent)) {
(_a2 = normalizedEvent.preventDefault) == null ? void 0 : _a2.call(normalizedEvent);
}
this.polygonInformation.deletePolygonInformationStorage();
if (!this.tracer) {
this.stopDraw();
return;
}
const tracerGeoJSON = this.tracer.toGeoJSON();
if (!tracerGeoJSON || !tracerGeoJSON.geometry || !tracerGeoJSON.geometry.coordinates || tracerGeoJSON.geometry.coordinates.length < 3) {
this.stopDraw();
return;
}
let geoPos;
try {
geoPos = this.turfHelper.createPolygonFromTrace(tracerGeoJSON);
} catch {
this.stopDraw();
return;
}
if (!geoPos || !geoPos.geometry || !geoPos.geometry.coordinates || geoPos.geometry.coordinates.length === 0) {
this.stopDraw();
return;
}
this.stopDraw();
try {
const interactionLayerId = this.getInteractionTargetLayerId();
switch (this.modeManager.getCurrentMode()) {
case DrawMode.Add: {
const result = await this.polygonMutationManager.addPolygon(geoPos, {
simplify: true,
noMerge: false,
targetLayerId: interactionLayerId
});
if (!result.success) {
console.error("Error adding polygon via manager:", result.error);
}
break;
}
case DrawMode.Subtract: {
const subtractResult = await this.polygonMutationManager.subtractPolygon(geoPos, {
targetLayerId: interactionLayerId
});
if (!subtractResult.success) {
console.error("Error subtracting polygon via manager:", subtractResult.error);
}
break;
}
default:
break;
}
} catch (error) {
console.error("Error in mouseUpLeave polygon operation:", error);
}
this.polygonInformation.createPolygonInformationStorage(this.arrayOfFeatureGroups);
}
/**
* Handles the completion of a freehand drawing operation.
* @param geoPos - The GeoJSON feature representing the drawn polygon.
*/
async handleFreehandDrawCompletion(geoPos) {
try {
this.saveHistory("freehand");
const interactionLayerId = this.getInteractionTargetLayerId();
switch (this.modeManager.getCurrentMode()) {
case DrawMode.Add: {
const result = await this.polygonMutationManager.addPolygon(geoPos, {
simplify: true,
noMerge: false,
targetLayerId: interactionLayerId
});
if (!result.success) {
console.error("Error adding polygon via manager:", result.error);
}
break;
}
case DrawMode.Subtract: {
const subtractResult = await this.polygonMutationManager.subtractPolygon(geoPos, {
targetLayerId: interactionLayerId
});
if (!subtractResult.success) {
console.error("Error subtracting polygon via manager:", subtractResult.error);
}
break;
}
default:
break;
}
} catch (error) {
console.error("Error in mouseUpLeave polygon operation:", error);
}
}
/**
* Starts a drawing operation by attaching the necessary event listeners.
*/
startDraw() {
if (this.isDrawingInProgress) {
return;
}
this.isDrawingInProgress = true;
this.drawStartedEvents(true);
}
/**
* Sets up the keyboard event handlers for the document.
*/
setupKeyboardHandlers() {
if (!this._boundKeyDownHandler) {
this._boundKeyDownHandler = this.handleKeyDown.bind(this);
}
if (!this._boundKeyUpHandler) {
this._boundKeyUpHandler = this.handleKeyUp.bind(this);
}
document.addEventListener("keydown", this._boundKeyDownHandler);
document.addEventListener("keyup", this._boundKeyUpHandler);
}
/**
* Removes the keyboard event handlers from the document.
*/
removeKeyboardHandlers() {
if (this._boundKeyDownHandler) {
document.removeEventListener("keydown", this._boundKeyDownHandler);
}
if (this._boundKeyUpHandler) {
document.removeEventListener("keyup", this._boundKeyUpHandler);
}
}
/**
* Handles the key down event for keyboard shortcuts.
* @param e - The keyboard event.
*/
handleKeyDown(e) {
const isCtrlOrCmd = e.ctrlKey || e.metaKey;
if (isCtrlOrCmd && e.key === "z" && !e.shiftKey) {
e.preventDefault();
this.undo();
return;
}
if (isCtrlOrCmd && (e.key === "y" || e.key === "z" && e.shiftKey)) {
e.preventDefault();
this.redo();
return;
}
if (e.key === "Escape") {
if (this.modeManager.getCurrentMode() === DrawMode.PointToPoint || this.modeManager.getCurrentMode() === DrawMode.PointToPointSubtract) {
this.polygonDrawManager.cancelPointToPointDrawing();
}
}
const isModifierPressed = this.isModifierKeyPressed(e);
if (isModifierPressed && !this.isModifierKeyHeld) {
this.isModifierKeyHeld = true;
this.polygonDrawManager.setModifierKey(true);
this.updateAllMarkersForEdgeDeletion(true);
this.applyModifierModeOverride(true);
}
}
/**
* Handles the key up event for keyboard shortcuts.
* @param e - The keyboard event.
*/
handleKeyUp(e) {
const isModifierPressed = this.isModifierKeyPressed(e);
if (!isModifierPressed && this.isModifierKeyHeld) {
this.isModifierKeyHeld = false;
this.polygonDrawManager.setModifierKey(false);
this.updateAllMarkersForEdgeDeletion(false);
this.applyModifierModeOverride(false);
}
}
applyModifierModeOverride(isHeld) {
if (isHeld) {
if (this.modifierModeOverride) return;
if (!this.config.modifierSubtractMode) return;
const currentMode = this.modeManager.getCurrentMode();
if (currentMode === DrawMode.Add) {
this.modifierModeOverride = { from: currentMode, to: DrawMode.Subtract };
this.setDrawMode(DrawMode.Subtract, { preserveActiveDraw: true });
} else if (currentMode === DrawMode.PointToPoint) {
this.modifierModeOverride = { from: currentMode, to: DrawMode.PointToPointSubtract };
this.setDrawMode(DrawMode.PointToPointSubtract, { preserveActiveDraw: true });
}
return;
}
if (!this.modifierModeOverride) return;
const { from, to } = this.modifierModeOverride;
this.modifierModeOverride = null;
if (this.modeManager.getCurrentMode() === to) {
this.setDrawMode(from, { preserveActiveDraw: true });
}
}
/**
* Update all markers to show/hide edge deletion visual feedback
*/
updateAllMarkersForEdgeDeletion(showFeedback) {
this.arrayOfFeatureGroups.forEach((featureGroup) => {
featureGroup.eachLayer((layer) => {
if (layer instanceof L.Marker) {
this.updateMarkerForEdgeDeletion(layer, showFeedback);
}
});
});
}
/**
* Update individual marker for edge deletion visual feedback
*/
updateMarkerForEdgeDeletion(marker, showFeedback) {
const element = marker.getElement();
if (!element) return;
if (showFeedback) {
element.addEventListener("mouseenter", this.onMarkerHoverForEdgeDeletionEvent);
element.addEventListener("mouseleave", this.onMarkerLeaveForEdgeDeletionEvent);
} else {
element.removeEventListener("mouseenter", this.onMarkerHoverForEdgeDeletionEvent);
element.removeEventListener("mouseleave", this.onMarkerLeaveForEdgeDeletionEvent);
element.style.backgroundColor = "";
element.style.borderColor = "";
}
}
/**
* Handles the double-click event for point-to-point drawing.
* @param e - The mouse event.
*/
handleDoubleClick(e) {
if (this.modeManager.getCurrentMode() !== DrawMode.PointToPoint && this.modeManager.getCurrentMode() !== DrawMode.PointToPointSubtract) {
return;
}
L.DomEvent.stop(e);
this.polygonDrawManager.handleDoubleClick(e);
}
/**
* Detect if modifier key is pressed (Ctrl on Windows/Linux, Cmd on Mac)
*/
isModifierKeyPressed(event) {
const userAgent = navigator.userAgent.toLowerCase();
const isMac = userAgent.includes("mac");
if (isMac) {
return event.metaKey;
} else {
return event.ctrlKey;
}
}
/**
* Ensures all buttons have proper touch responsiveness for Firefox Android
* @param container - The main control container element
*/
ensureButtonTouchResponsiveness(container) {
const buttons = container.querySelectorAll("a");
buttons.forEach((button) => {
button.style.pointerEvents = "auto";
button.style.touchAction = "manipulation";
button.style.position = "relative";
button.style.zIndex = "1";
const touchHandler = (e) => {
e.stopPropagation();
e.target.style.pointerEvents = "auto";
};
button.addEventListener("touchstart", touchHandler, { passive: true });
button.addEventListener("touchend", touchHandler, { passive: true });
});
}
/**
* Updates the visual indicator on the activate button to show if there are active polygons.
*/
updateActivateButtonIndicator() {
if (typeof this.getContainer !== "function") {
return;
}
const container = this.getContainer();
if (!container) return;
const activateButton = container.querySelector(".icon-activate");
if (!activateButton) return;
const hasPolygons = this.arrayOfFeatureGroups.length > 0;
const isPanelClosed = !leafletAdapter.domUtil.hasClass(activateButton, "active");
const iconMarkup = leafletAdapter.domUtil.hasClass(activateButton, "active") ? activateButton.dataset.collapsedIcon : activateButton.dataset.activeIcon;
if (iconMarkup) {
this.applyActivateButtonIcon(activateButton, iconMarkup);
}
const hasIndicator = hasPolygons && isPanelClosed;
if (hasIndicator) {
leafletAdapter.domUtil.addClass(activateButton, "polydraw-indicator-active");
} else {
leafletAdapter.domUtil.removeClass(activateButton, "polydraw-indicator-active");
}
const baseBackground = this.config.styles.ui.controlButton.backgroundColor;
const baseColor = this.config.styles.ui.controlButton.color;
const indicatorBackground = this.config.styles.ui.indicatorActive.backgroundColor;
activateButton.style.backgroundColor = hasIndicator ? indicatorBackground : baseBackground;
activateButton.style.color = baseColor;
const setButtonEnabled = (button, enabled) => {
if (!button) return;
if (enabled) {
button.style.opacity = "1";
button.style.pointerEvents = "auto";
button.setAttribute("aria-disabled", "false");
button.tabIndex = 0;
} else {
button.style.opacity = "0.3";
button.style.pointerEvents = "none";
button.setAttribute("aria-disabled", "true");
button.tabIndex = -1;
}
};
const cloneButton = container.querySelector(".icon-clone");
setButtonEnabled(cloneButton, hasPolygons);
if (!hasPolygons && this.modeManager.getCurrentMode() === DrawMode.Clone) {
this.setDrawMode(DrawMode.Off);
}
const eraseButton = container.querySelector(".icon-erase");
const hasErasablePolygons = (this.config.tools.eraseScope ?? "all") === "defaultLayer" ? this.layerManager.getFeatureGroupsForLayer("default").length > 0 : hasPolygons;
setButtonEnabled(eraseButton, hasErasablePolygons);
}
applyActivateButtonIcon(button, svgMarkup) {
applySvgIcon(button, svgMarkup);
}
}
const registerWithLeaflet = (leafletInstance) => {
if (!leafletInstance) {
return;
}
if (typeof globalThis !== "undefined") {
const globalL = globalThis.L;
if (!globalL) {
globalThis.L = leafletInstance;
}
}
if (leafletInstance.control) {
leafletInstance.control.polydraw = function(options) {
return new Polydraw(options);
};
}
LeafletVersionDetector.reset();
};
if (typeof globalThis !== "undefined") {
const globalL = globalThis.L;
if (globalL) {
registerWithLeaflet(globalL);
}
}
export {
Polydraw as default,
defaultConfig,
leafletAdapter,
registerWithLeaflet
};
//# sourceMappingURL=polydraw.es.js.map