pwaLUPMIS2/node_modules/.vite/deps/ol_interaction.js
2026-03-04 12:59:40 +01:00

3803 lines
119 KiB
JavaScript

import {
Draw_default,
getCoordinate,
getTraceTargetUpdate,
getTraceTargets
} from "./chunk-MEQVYVYE.js";
import {
Select_default
} from "./chunk-6DXBPPKF.js";
import {
RBush_default,
Vector_default as Vector_default2
} from "./chunk-PD2E5XZ4.js";
import {
VectorEventType_default
} from "./chunk-V7WRBSQ6.js";
import "./chunk-W7BDJOQY.js";
import "./chunk-7JXPN73Q.js";
import {
Feature_default
} from "./chunk-E53S5GN6.js";
import {
Vector_default
} from "./chunk-T3TT2KJN.js";
import "./chunk-HM3IY3H4.js";
import "./chunk-JFXZSSOM.js";
import "./chunk-ZUI5NXIU.js";
import {
DoubleClickZoom_default,
DragBox_default,
DragPan_default,
DragRotate_default,
DragZoom_default,
KeyboardPan_default,
KeyboardZoom_default,
MouseWheelZoom_default,
PinchRotate_default,
PinchZoom_default,
Pointer_default,
defaults
} from "./chunk-RTVPCGIJ.js";
import {
Interaction_default,
Property_default
} from "./chunk-MSWSBYBR.js";
import {
MapBrowserEventType_default,
altKeyOnly,
always,
mouseOnly,
never,
primaryAction,
shiftKeyOnly,
singleClick
} from "./chunk-QCJTGAWF.js";
import {
CollectionEventType_default,
Collection_default
} from "./chunk-M5TTSD4C.js";
import "./chunk-ZCRXKB7J.js";
import "./chunk-RW3V7S4F.js";
import {
createEditingStyle
} from "./chunk-PAB2HIXK.js";
import "./chunk-I6K7MRGV.js";
import "./chunk-PGWX4545.js";
import "./chunk-AYBYZSAV.js";
import "./chunk-YLJGUH5Z.js";
import {
Point_default,
fromCircle,
fromExtent
} from "./chunk-AZGMK675.js";
import {
MapEventType_default
} from "./chunk-BHVDQB66.js";
import "./chunk-6EWLK2BW.js";
import "./chunk-6Y7C6NBJ.js";
import "./chunk-SHUBVYN4.js";
import "./chunk-FM44FOIC.js";
import "./chunk-LMC3RO5P.js";
import {
getIntersectionPoint
} from "./chunk-X52LGBOS.js";
import "./chunk-QFCIXVZ3.js";
import {
closestOnCircle,
closestOnSegment,
distance,
equals as equals2,
fromUserCoordinate,
fromUserExtent,
get,
getUserProjection,
squaredDistance,
squaredDistanceToSegment,
toUserCoordinate,
toUserExtent
} from "./chunk-A3RXLHYB.js";
import "./chunk-ZLPTRF2L.js";
import {
toFixed
} from "./chunk-54BTDBAD.js";
import "./chunk-UPTVWZ45.js";
import "./chunk-5XHD7RSF.js";
import {
getUid
} from "./chunk-Q5ZULJHM.js";
import {
Event_default,
listen,
unlistenByKey
} from "./chunk-NGFXCWUF.js";
import {
EventType_default,
FALSE,
TRUE,
equals
} from "./chunk-K25ZO44T.js";
import {
boundingExtent,
buffer,
containsCoordinate,
createEmpty,
createOrUpdateFromCoordinate,
getArea,
intersects
} from "./chunk-SRXHWJOY.js";
import {
clear
} from "./chunk-5RHQVMYD.js";
import "./chunk-DC5AMYBS.js";
// node_modules/ol/interaction/DblClickDragZoom.js
var DblClickDragZoom = class extends Interaction_default {
/**
* @param {Options} [opt_options] Options.
*/
constructor(opt_options) {
const options = opt_options ? opt_options : {};
super(
/** @type {import("./Interaction.js").InteractionOptions} */
options
);
if (options.stopDown) {
this.stopDown = options.stopDown;
}
this.scaleDeltaByPixel_ = options.delta ? options.delta : 0.01;
this.duration_ = options.duration !== void 0 ? options.duration : 250;
this.handlingDownUpSequence_ = false;
this.handlingDoubleDownSequence_ = false;
this.doubleTapTimeoutId_ = void 0;
this.trackedPointers_ = {};
this.down_ = null;
this.targetPointers = [];
}
/**
* Handles the {@link module:ol/MapBrowserEvent~MapBrowserEvent map browser event} and may call into
* other functions, if event sequences like e.g. 'drag' or 'down-up' etc. are
* detected.
* @param {import("../MapBrowserEvent.js").default<PointerEvent>} mapBrowserEvent Map browser event.
* @return {boolean} `false` to stop event propagation.
* @api
* @override
*/
handleEvent(mapBrowserEvent) {
if (!mapBrowserEvent.originalEvent) {
return true;
}
let stopEvent = false;
this.updateTrackedPointers_(mapBrowserEvent);
if (this.handlingDownUpSequence_) {
if (mapBrowserEvent.type == MapBrowserEventType_default.POINTERDRAG) {
this.handleDragEvent(mapBrowserEvent);
mapBrowserEvent.originalEvent.preventDefault();
} else if (mapBrowserEvent.type == MapBrowserEventType_default.POINTERUP) {
const handledUp = this.handleUpEvent(mapBrowserEvent);
this.handlingDownUpSequence_ = handledUp;
}
} else {
if (mapBrowserEvent.type == MapBrowserEventType_default.POINTERDOWN) {
if (this.handlingDoubleDownSequence_) {
this.handlingDoubleDownSequence_ = false;
const handled = this.handleDownEvent(mapBrowserEvent);
this.handlingDownUpSequence_ = handled;
stopEvent = this.stopDown(handled);
} else {
stopEvent = this.stopDown(false);
this.waitForDblTap_();
}
}
}
return !stopEvent;
}
/**
* Handle pointer drag events.
* @param {import("../MapBrowserEvent.js").default<PointerEvent>} mapBrowserEvent Event.
*/
handleDragEvent(mapBrowserEvent) {
let scaleDelta = 1;
const touch0 = this.targetPointers[0];
const touch1 = this.down_;
const distance2 = touch0.clientY - touch1.clientY;
if (this.lastDistance_ !== void 0) {
scaleDelta = 1 - (this.lastDistance_ - distance2) * this.scaleDeltaByPixel_;
}
this.lastDistance_ = distance2;
if (scaleDelta != 1) {
this.lastScaleDelta_ = scaleDelta;
}
const map = mapBrowserEvent.map;
const view = map.getView();
map.render();
view.adjustResolutionInternal(scaleDelta);
}
/**
* Handle pointer down events.
* @param {import("../MapBrowserEvent.js").default<PointerEvent>} mapBrowserEvent Event.
* @return {boolean} If the event was consumed.
*/
handleDownEvent(mapBrowserEvent) {
if (this.targetPointers.length == 1) {
const map = mapBrowserEvent.map;
this.anchor_ = null;
this.lastDistance_ = void 0;
this.lastScaleDelta_ = 1;
this.down_ = mapBrowserEvent.originalEvent;
if (!this.handlingDownUpSequence_) {
map.getView().beginInteraction();
}
return true;
}
return false;
}
/**
* Handle pointer up events zooming out.
* @param {import("../MapBrowserEvent.js").default} mapBrowserEvent Event.
* @return {boolean} If the event was consumed.
*/
handleUpEvent(mapBrowserEvent) {
if (this.targetPointers.length == 0) {
const map = mapBrowserEvent.map;
const view = map.getView();
const direction = this.lastScaleDelta_ > 1 ? 1 : -1;
view.endInteraction(this.duration_, direction);
this.handlingDownUpSequence_ = false;
this.handlingDoubleDownSequence_ = false;
return false;
}
return true;
}
/**
* This function is used to determine if "down" events should be propagated
* to other interactions or should be stopped.
* @param {boolean} handled Was the event handled by the interaction?
* @return {boolean} Should the `down` event be stopped?
*/
stopDown(handled) {
return handled;
}
/**
* @param {import("../MapBrowserEvent.js").default<PointerEvent>} mapBrowserEvent Event.
* @private
*/
updateTrackedPointers_(mapBrowserEvent) {
if (isPointerDraggingEvent(mapBrowserEvent)) {
const event = mapBrowserEvent.originalEvent;
const id = event.pointerId.toString();
if (mapBrowserEvent.type == MapBrowserEventType_default.POINTERUP) {
delete this.trackedPointers_[id];
} else if (mapBrowserEvent.type == MapBrowserEventType_default.POINTERDOWN) {
this.trackedPointers_[id] = event;
} else if (id in this.trackedPointers_) {
this.trackedPointers_[id] = event;
}
this.targetPointers = Object.values(this.trackedPointers_);
}
}
/**
* Wait the second double finger tap.
* @private
*/
waitForDblTap_() {
if (this.doubleTapTimeoutId_ !== void 0) {
clearTimeout(this.doubleTapTimeoutId_);
this.doubleTapTimeoutId_ = void 0;
} else {
this.handlingDoubleDownSequence_ = true;
this.doubleTapTimeoutId_ = setTimeout(
this.endInteraction_.bind(this),
250
);
}
}
/**
* @private
*/
endInteraction_() {
this.handlingDoubleDownSequence_ = false;
this.doubleTapTimeoutId_ = void 0;
}
};
function isPointerDraggingEvent(mapBrowserEvent) {
const type = mapBrowserEvent.type;
return type === MapBrowserEventType_default.POINTERDOWN || type === MapBrowserEventType_default.POINTERDRAG || type === MapBrowserEventType_default.POINTERUP;
}
var DblClickDragZoom_default = DblClickDragZoom;
// node_modules/ol/interaction/DragAndDrop.js
var DragAndDropEventType = {
/**
* Triggered when features are added
* @event DragAndDropEvent#addfeatures
* @api
*/
ADD_FEATURES: "addfeatures"
};
var DragAndDropEvent = class extends Event_default {
/**
* @param {DragAndDropEventType} type Type.
* @param {File} file File.
* @param {Array<import("../Feature.js").default>} [features] Features.
* @param {import("../proj/Projection.js").default} [projection] Projection.
*/
constructor(type, file, features, projection) {
super(type);
this.features = features;
this.file = file;
this.projection = projection;
}
};
var DragAndDrop = class extends Interaction_default {
/**
* @param {Options} [options] Options.
*/
constructor(options) {
options = options ? options : {};
super({
handleEvent: TRUE
});
this.on;
this.once;
this.un;
this.readAsBuffer_ = false;
this.formats_ = [];
const formatConstructors = options.formatConstructors ? options.formatConstructors : [];
for (let i = 0, ii = formatConstructors.length; i < ii; ++i) {
let format = formatConstructors[i];
if (typeof format === "function") {
format = new format();
}
this.formats_.push(format);
this.readAsBuffer_ = this.readAsBuffer_ || format.getType() === "arraybuffer";
}
this.projection_ = options.projection ? get(options.projection) : null;
this.dropListenKeys_ = null;
this.source_ = options.source || null;
this.target = options.target ? options.target : null;
}
/**
* @param {File} file File.
* @param {Event} event Load event.
* @private
*/
handleResult_(file, event) {
const result = event.target.result;
const map = this.getMap();
let projection = this.projection_;
if (!projection) {
projection = getUserProjection();
if (!projection) {
const view = map.getView();
projection = view.getProjection();
}
}
let text;
const formats = this.formats_;
for (let i = 0, ii = formats.length; i < ii; ++i) {
const format = formats[i];
let input = result;
if (this.readAsBuffer_ && format.getType() !== "arraybuffer") {
if (text === void 0) {
text = new TextDecoder().decode(result);
}
input = text;
}
const features = this.tryReadFeatures_(format, input, {
featureProjection: projection
});
if (features && features.length > 0) {
if (this.source_) {
this.source_.clear();
this.source_.addFeatures(features);
}
this.dispatchEvent(
new DragAndDropEvent(
DragAndDropEventType.ADD_FEATURES,
file,
features,
projection
)
);
break;
}
}
}
/**
* @private
*/
registerListeners_() {
const map = this.getMap();
if (map) {
const dropArea = this.target ? this.target : map.getViewport();
this.dropListenKeys_ = [
listen(dropArea, EventType_default.DROP, this.handleDrop, this),
listen(dropArea, EventType_default.DRAGENTER, this.handleStop, this),
listen(dropArea, EventType_default.DRAGOVER, this.handleStop, this),
listen(dropArea, EventType_default.DROP, this.handleStop, this)
];
}
}
/**
* Activate or deactivate the interaction.
* @param {boolean} active Active.
* @observable
* @api
* @override
*/
setActive(active) {
if (!this.getActive() && active) {
this.registerListeners_();
}
if (this.getActive() && !active) {
this.unregisterListeners_();
}
super.setActive(active);
}
/**
* Remove the interaction from its current map and attach it to the new map.
* Subclasses may set up event handlers to get notified about changes to
* the map here.
* @param {import("../Map.js").default} map Map.
* @override
*/
setMap(map) {
this.unregisterListeners_();
super.setMap(map);
if (this.getActive()) {
this.registerListeners_();
}
}
/**
* @param {import("../format/Feature.js").default} format Format.
* @param {string} text Text.
* @param {import("../format/Feature.js").ReadOptions} options Read options.
* @private
* @return {Array<import("../Feature.js").default>} Features.
*/
tryReadFeatures_(format, text, options) {
try {
return (
/** @type {Array<import("../Feature.js").default>} */
format.readFeatures(text, options)
);
} catch {
return null;
}
}
/**
* @private
*/
unregisterListeners_() {
if (this.dropListenKeys_) {
this.dropListenKeys_.forEach(unlistenByKey);
this.dropListenKeys_ = null;
}
}
/**
* @param {DragEvent} event Event.
*/
handleDrop(event) {
const files = event.dataTransfer.files;
for (let i = 0, ii = files.length; i < ii; ++i) {
const file = files.item(i);
const reader = new FileReader();
reader.addEventListener(
EventType_default.LOAD,
this.handleResult_.bind(this, file)
);
if (this.readAsBuffer_) {
reader.readAsArrayBuffer(file);
} else {
reader.readAsText(file);
}
}
}
/**
* @param {DragEvent} event Event.
*/
handleStop(event) {
event.stopPropagation();
event.preventDefault();
event.dataTransfer.dropEffect = "copy";
}
};
var DragAndDrop_default = DragAndDrop;
// node_modules/ol/interaction/DragRotateAndZoom.js
var DragRotateAndZoom = class extends Pointer_default {
/**
* @param {Options} [options] Options.
*/
constructor(options) {
options = options ? options : {};
super(
/** @type {import("./Pointer.js").Options} */
options
);
this.condition_ = options.condition ? options.condition : shiftKeyOnly;
this.lastAngle_ = void 0;
this.lastMagnitude_ = void 0;
this.lastScaleDelta_ = 0;
this.duration_ = options.duration !== void 0 ? options.duration : 400;
}
/**
* Handle pointer drag events.
* @param {import("../MapBrowserEvent.js").default} mapBrowserEvent Event.
* @override
*/
handleDragEvent(mapBrowserEvent) {
if (!mouseOnly(mapBrowserEvent)) {
return;
}
const map = mapBrowserEvent.map;
const size = map.getSize();
const offset = mapBrowserEvent.pixel;
const deltaX = offset[0] - size[0] / 2;
const deltaY = size[1] / 2 - offset[1];
const theta = Math.atan2(deltaY, deltaX);
const magnitude = Math.sqrt(deltaX * deltaX + deltaY * deltaY);
const view = map.getView();
if (this.lastAngle_ !== void 0) {
const angleDelta = this.lastAngle_ - theta;
view.adjustRotationInternal(angleDelta);
}
this.lastAngle_ = theta;
if (this.lastMagnitude_ !== void 0) {
view.adjustResolutionInternal(this.lastMagnitude_ / magnitude);
}
if (this.lastMagnitude_ !== void 0) {
this.lastScaleDelta_ = this.lastMagnitude_ / magnitude;
}
this.lastMagnitude_ = magnitude;
}
/**
* Handle pointer up events.
* @param {import("../MapBrowserEvent.js").default} mapBrowserEvent Event.
* @return {boolean} If the event was consumed.
* @override
*/
handleUpEvent(mapBrowserEvent) {
if (!mouseOnly(mapBrowserEvent)) {
return true;
}
const map = mapBrowserEvent.map;
const view = map.getView();
const direction = this.lastScaleDelta_ > 1 ? 1 : -1;
view.endInteraction(this.duration_, direction);
this.lastScaleDelta_ = 0;
return false;
}
/**
* Handle pointer down events.
* @param {import("../MapBrowserEvent.js").default} mapBrowserEvent Event.
* @return {boolean} If the event was consumed.
* @override
*/
handleDownEvent(mapBrowserEvent) {
if (!mouseOnly(mapBrowserEvent)) {
return false;
}
if (this.condition_(mapBrowserEvent)) {
mapBrowserEvent.map.getView().beginInteraction();
this.lastAngle_ = void 0;
this.lastMagnitude_ = void 0;
return true;
}
return false;
}
};
var DragRotateAndZoom_default = DragRotateAndZoom;
// node_modules/ol/interaction/Extent.js
var ExtentEventType = {
/**
* Triggered after the extent is changed
* @event ExtentEvent#extentchanged
* @api
*/
EXTENTCHANGED: "extentchanged"
};
var ExtentEvent = class extends Event_default {
/**
* @param {import("../extent.js").Extent} extent the new extent
*/
constructor(extent) {
super(ExtentEventType.EXTENTCHANGED);
this.extent = extent;
}
};
var Extent = class extends Pointer_default {
/**
* @param {Options} [options] Options.
*/
constructor(options) {
options = options || {};
super(
/** @type {import("./Pointer.js").Options} */
options
);
this.on;
this.once;
this.un;
this.condition_ = options.condition ? options.condition : always;
this.createCondition_ = options.createCondition || this.condition_;
this.drag_ = options.drag || false;
this.extent_ = null;
this.pointerHandler_ = null;
this.pixelTolerance_ = options.pixelTolerance !== void 0 ? options.pixelTolerance : 10;
this.snappedToVertex_ = false;
this.extentFeature_ = null;
this.vertexFeature_ = null;
if (!options) {
options = {};
}
this.extentOverlay_ = new Vector_default({
source: new Vector_default2({
useSpatialIndex: false,
wrapX: !!options.wrapX
}),
style: options.boxStyle ? options.boxStyle : getDefaultExtentStyleFunction(),
updateWhileAnimating: true,
updateWhileInteracting: true
});
this.vertexOverlay_ = new Vector_default({
source: new Vector_default2({
useSpatialIndex: false,
wrapX: !!options.wrapX
}),
style: options.pointerStyle ? options.pointerStyle : getDefaultPointerStyleFunction(),
updateWhileAnimating: true,
updateWhileInteracting: true
});
if (options.extent) {
this.setExtent(options.extent);
}
}
/**
* @param {import("../pixel.js").Pixel} pixel cursor location
* @param {import("../Map.js").default} map map
* @return {import("../coordinate.js").Coordinate|null} snapped vertex on extent
* @private
*/
snapToVertex_(pixel, map) {
const pixelCoordinate = map.getCoordinateFromPixelInternal(pixel);
const sortByDistance = function(a, b) {
return squaredDistanceToSegment(pixelCoordinate, a) - squaredDistanceToSegment(pixelCoordinate, b);
};
const extent = this.getExtentInternal();
if (extent) {
const segments = getSegments(extent);
segments.sort(sortByDistance);
const closestSegment = segments[0];
let vertex = closestOnSegment(pixelCoordinate, closestSegment);
const vertexPixel = map.getPixelFromCoordinateInternal(vertex);
if (distance(pixel, vertexPixel) <= this.pixelTolerance_) {
const pixel1 = map.getPixelFromCoordinateInternal(closestSegment[0]);
const pixel2 = map.getPixelFromCoordinateInternal(closestSegment[1]);
const squaredDist1 = squaredDistance(vertexPixel, pixel1);
const squaredDist2 = squaredDistance(vertexPixel, pixel2);
const dist = Math.sqrt(Math.min(squaredDist1, squaredDist2));
this.snappedToVertex_ = dist <= this.pixelTolerance_;
if (this.snappedToVertex_) {
vertex = squaredDist1 > squaredDist2 ? closestSegment[1] : closestSegment[0];
}
return vertex;
}
}
return null;
}
/**
* @param {import("../MapBrowserEvent.js").default} mapBrowserEvent pointer move event
* @return {boolean} The event was handled.
* @private
*/
handlePointerMove_(mapBrowserEvent) {
const pixel = mapBrowserEvent.pixel;
const map = mapBrowserEvent.map;
const draggable = this.drag_ && containsCoordinate(this.extent_, mapBrowserEvent.coordinate);
let vertex = this.snapToVertex_(pixel, map);
if (!vertex && this.createCondition_(mapBrowserEvent) && !draggable) {
vertex = map.getCoordinateFromPixelInternal(pixel);
}
if (draggable && !vertex) {
this.getMap().getViewport().classList.add("ol-grab");
} else {
this.getMap().getViewport().classList.remove("ol-grab");
}
if (vertex) {
this.updatePointerFeature_(vertex);
return true;
}
this.noVertexFeature_();
return false;
}
/**
* @param {import("../extent.js").Extent} extent extent
* @return {Feature} extent as featrue
* @private
*/
createOrUpdateExtentFeature_(extent) {
let extentFeature = this.extentFeature_;
if (!extentFeature) {
if (!extent) {
extentFeature = new Feature_default({});
} else {
extentFeature = new Feature_default(fromExtent(extent));
}
this.extentFeature_ = extentFeature;
this.extentOverlay_.getSource().addFeature(extentFeature);
} else {
if (!extent) {
extentFeature.setGeometry(void 0);
} else {
extentFeature.setGeometry(fromExtent(extent));
}
}
return extentFeature;
}
/**
* @param {import("../coordinate.js").Coordinate} vertex location of feature
* @param {boolean} [createIfNotExists] create the feature if it does not exist
* @return {Feature} vertex as feature
* @private
*/
updatePointerFeature_(vertex, createIfNotExists = true) {
let vertexFeature = this.vertexFeature_;
if (createIfNotExists && !vertexFeature) {
vertexFeature = new Feature_default(new Point_default(vertex));
this.vertexFeature_ = vertexFeature;
this.vertexOverlay_.getSource().addFeature(vertexFeature);
}
if (vertexFeature) {
const geometry = vertexFeature.getGeometry();
geometry.setCoordinates(vertex);
}
return vertexFeature;
}
/**
* Remove the vertex feature if it exists.
* @private
*/
noVertexFeature_() {
if (this.vertexFeature_) {
this.vertexOverlay_.getSource().removeFeature(this.vertexFeature_);
this.vertexFeature_ = null;
}
}
/**
* @param {import("../MapBrowserEvent.js").default} mapBrowserEvent Map browser event.
* @return {boolean} `false` to stop event propagation.
* @override
*/
handleEvent(mapBrowserEvent) {
if (!mapBrowserEvent.originalEvent || !this.condition_(mapBrowserEvent)) {
this.noVertexFeature_();
return true;
}
let handled = this.handlingDownUpSequence;
if (mapBrowserEvent.type == MapBrowserEventType_default.POINTERMOVE && !this.handlingDownUpSequence) {
handled = this.handlePointerMove_(mapBrowserEvent);
}
super.handleEvent(mapBrowserEvent);
return !handled;
}
/**
* Handle pointer down events.
* @param {import("../MapBrowserEvent.js").default} mapBrowserEvent Event.
* @return {boolean} If the event was consumed.
* @override
*/
handleDownEvent(mapBrowserEvent) {
const pixel = mapBrowserEvent.pixel;
const map = mapBrowserEvent.map;
const extent = this.getExtentInternal();
let vertex = this.snapToVertex_(pixel, map);
const getOpposingPoint = function(point) {
let x_ = null;
let y_ = null;
if (point[0] == extent[0]) {
x_ = extent[2];
} else if (point[0] == extent[2]) {
x_ = extent[0];
}
if (point[1] == extent[1]) {
y_ = extent[3];
} else if (point[1] == extent[3]) {
y_ = extent[1];
}
if (x_ !== null && y_ !== null) {
return [x_, y_];
}
return null;
};
if (vertex && extent) {
const x = vertex[0] == extent[0] || vertex[0] == extent[2] ? vertex[0] : null;
const y = vertex[1] == extent[1] || vertex[1] == extent[3] ? vertex[1] : null;
if (x !== null && y !== null) {
this.pointerHandler_ = getPointHandler(getOpposingPoint(vertex));
} else if (x !== null) {
this.pointerHandler_ = getEdgeHandler(
getOpposingPoint([x, extent[1]]),
getOpposingPoint([x, extent[3]])
);
} else if (y !== null) {
this.pointerHandler_ = getEdgeHandler(
getOpposingPoint([extent[0], y]),
getOpposingPoint([extent[2], y])
);
}
} else {
vertex = map.getCoordinateFromPixelInternal(pixel);
let drag = false;
if (this.drag_) {
if (containsCoordinate(extent, vertex)) {
this.pointerHandler_ = getDragHandler(extent, vertex);
drag = true;
}
}
if (!drag && this.createCondition_(mapBrowserEvent)) {
this.setExtent([vertex[0], vertex[1], vertex[0], vertex[1]]);
this.pointerHandler_ = getPointHandler(vertex);
}
}
return !!this.pointerHandler_;
}
/**
* Handle pointer drag events.
* @param {import("../MapBrowserEvent.js").default} mapBrowserEvent Event.
* @override
*/
handleDragEvent(mapBrowserEvent) {
if (this.pointerHandler_) {
const pixelCoordinate = mapBrowserEvent.coordinate;
this.setExtent(this.pointerHandler_(pixelCoordinate));
this.updatePointerFeature_(pixelCoordinate, false);
}
}
/**
* Handle pointer up events.
* @param {import("../MapBrowserEvent.js").default} mapBrowserEvent Event.
* @return {boolean} If the event was consumed.
* @override
*/
handleUpEvent(mapBrowserEvent) {
this.pointerHandler_ = null;
const extent = this.getExtentInternal();
if (!extent || getArea(extent) === 0) {
this.setExtent(null);
}
return false;
}
/**
* Remove the interaction from its current map and attach it to the new map.
* Subclasses may set up event handlers to get notified about changes to
* the map here.
* @param {import("../Map.js").default} map Map.
* @override
*/
setMap(map) {
this.extentOverlay_.setMap(map);
this.vertexOverlay_.setMap(map);
super.setMap(map);
}
/**
* Returns the current drawn extent in the view projection (or user projection if set)
*
* @return {import("../extent.js").Extent} Drawn extent in the view projection.
* @api
*/
getExtent() {
return toUserExtent(
this.getExtentInternal(),
this.getMap().getView().getProjection()
);
}
/**
* Returns the current drawn extent in the view projection
*
* @return {import("../extent.js").Extent} Drawn extent in the view projection.
* @api
* @deprecated Use {@link module:ol/interaction/Extent~Extent#getExtent} instead.
*/
getExtentInternal() {
return this.extent_;
}
/**
* Manually sets the drawn extent, using the view projection.
*
* @param {import("../extent.js").Extent} extent Extent
* @api
*/
setExtent(extent) {
this.extent_ = extent ? extent : null;
this.createOrUpdateExtentFeature_(extent);
this.dispatchEvent(new ExtentEvent(this.extent_));
}
};
function getDefaultExtentStyleFunction() {
const style = createEditingStyle();
return function(feature, resolution) {
return style["Polygon"];
};
}
function getDefaultPointerStyleFunction() {
const style = createEditingStyle();
return function(feature, resolution) {
return style["Point"];
};
}
function getPointHandler(fixedPoint) {
return function(point) {
return boundingExtent([fixedPoint, point]);
};
}
function getEdgeHandler(fixedP1, fixedP2) {
if (fixedP1[0] == fixedP2[0]) {
return function(point) {
return boundingExtent([fixedP1, [point[0], fixedP2[1]]]);
};
}
if (fixedP1[1] == fixedP2[1]) {
return function(point) {
return boundingExtent([fixedP1, [fixedP2[0], point[1]]]);
};
}
return null;
}
function getDragHandler(extent, vertex) {
return function(point) {
const deltaX = point[0] - vertex[0];
const deltaY = point[1] - vertex[1];
return [
extent[0] + deltaX,
extent[1] + deltaY,
extent[2] + deltaX,
extent[3] + deltaY
];
};
}
function getSegments(extent) {
return [
[
[extent[0], extent[1]],
[extent[0], extent[3]]
],
[
[extent[0], extent[3]],
[extent[2], extent[3]]
],
[
[extent[2], extent[3]],
[extent[2], extent[1]]
],
[
[extent[2], extent[1]],
[extent[0], extent[1]]
]
];
}
var Extent_default = Extent;
// node_modules/ol/interaction/Link.js
function to5(number) {
return toFixed(number, 5);
}
function readNumber(string) {
return parseFloat(string);
}
function writeNumber(number) {
return to5(number).toString();
}
function differentNumber(a, b) {
if (isNaN(a)) {
return false;
}
return a !== readNumber(writeNumber(b));
}
function differentArray(a, b) {
return differentNumber(a[0], b[0]) || differentNumber(a[1], b[1]);
}
var Link = class extends Interaction_default {
/**
* @param {Options} [options] Link options.
*/
constructor(options) {
super();
options = Object.assign(
{
animate: true,
params: ["x", "y", "z", "r", "l"],
replace: false,
prefix: ""
},
options || {}
);
let animationOptions;
if (options.animate === true) {
animationOptions = { duration: 250 };
} else if (!options.animate) {
animationOptions = null;
} else {
animationOptions = options.animate;
}
this.animationOptions_ = animationOptions;
this.params_ = options.params.reduce((acc, value) => {
acc[value] = true;
return acc;
}, {});
this.replace_ = options.replace;
this.prefix_ = options.prefix;
this.listenerKeys_ = [];
this.initial_ = true;
this.updateState_ = this.updateState_.bind(this);
this.trackedCallbacks_ = {};
this.trackedValues_ = {};
}
/**
* @private
* @param {string} name A parameter name.
* @return {string} A name with the prefix applied.
*/
getParamName_(name) {
if (!this.prefix_) {
return name;
}
return this.prefix_ + name;
}
/**
* @private
* @param {URLSearchParams} params The search params.
* @param {string} name The unprefixed parameter name.
* @return {string|null} The parameter value.
*/
get_(params, name) {
return params.get(this.getParamName_(name));
}
/**
* @private
* @param {URLSearchParams} params The search params.
* @param {string} name The unprefixed parameter name.
* @param {string} value The param value.
*/
set_(params, name, value) {
if (!(name in this.params_)) {
return;
}
params.set(this.getParamName_(name), value);
}
/**
* @private
* @param {URLSearchParams} params The search params.
* @param {string} name The unprefixed parameter name.
*/
delete_(params, name) {
if (!(name in this.params_)) {
return;
}
params.delete(this.getParamName_(name));
}
/**
* @param {import("../Map.js").default|null} map Map.
* @override
*/
setMap(map) {
const oldMap = this.getMap();
super.setMap(map);
if (map === oldMap) {
return;
}
if (oldMap) {
this.unregisterListeners_(oldMap);
}
if (map) {
this.initial_ = true;
this.updateState_();
this.registerListeners_(map);
}
}
/**
* @param {import("../Map.js").default} map Map.
* @private
*/
registerListeners_(map) {
this.listenerKeys_.push(
listen(map, MapEventType_default.MOVEEND, this.updateUrl_, this),
listen(map.getLayerGroup(), EventType_default.CHANGE, this.updateUrl_, this),
listen(map, "change:layergroup", this.handleChangeLayerGroup_, this)
);
if (!this.replace_) {
addEventListener("popstate", this.updateState_);
}
}
/**
* @param {import("../Map.js").default} map Map.
* @private
*/
unregisterListeners_(map) {
for (let i = 0, ii = this.listenerKeys_.length; i < ii; ++i) {
unlistenByKey(this.listenerKeys_[i]);
}
this.listenerKeys_.length = 0;
if (!this.replace_) {
removeEventListener("popstate", this.updateState_);
}
const url = new URL(window.location.href);
const params = url.searchParams;
this.delete_(params, "x");
this.delete_(params, "y");
this.delete_(params, "z");
this.delete_(params, "r");
this.delete_(params, "l");
window.history.replaceState(null, "", url);
}
/**
* @private
*/
handleChangeLayerGroup_() {
const map = this.getMap();
if (!map) {
return;
}
this.unregisterListeners_(map);
this.registerListeners_(map);
this.initial_ = true;
this.updateUrl_();
}
/**
* @private
*/
updateState_() {
const url = new URL(window.location.href);
const params = url.searchParams;
for (const key in this.trackedCallbacks_) {
const value = params.get(key);
if (key in this.trackedCallbacks_ && value !== this.trackedValues_[key]) {
this.trackedValues_[key] = value;
this.trackedCallbacks_[key](value);
}
}
const map = this.getMap();
if (!map) {
return;
}
const view = map.getView();
if (!view) {
return;
}
let updateView = false;
const viewProperties = {};
const zoom = readNumber(this.get_(params, "z"));
if ("z" in this.params_ && differentNumber(zoom, view.getZoom())) {
updateView = true;
viewProperties.zoom = zoom;
}
const rotation = readNumber(this.get_(params, "r"));
if ("r" in this.params_ && differentNumber(rotation, view.getRotation())) {
updateView = true;
viewProperties.rotation = rotation;
}
const center = [
readNumber(this.get_(params, "x")),
readNumber(this.get_(params, "y"))
];
if (("x" in this.params_ || "y" in this.params_) && differentArray(center, view.getCenter())) {
updateView = true;
viewProperties.center = center;
}
if (updateView) {
if (!this.initial_ && this.animationOptions_) {
view.animate(Object.assign(viewProperties, this.animationOptions_));
} else {
if (viewProperties.center) {
view.setCenter(viewProperties.center);
}
if ("zoom" in viewProperties) {
view.setZoom(viewProperties.zoom);
}
if ("rotation" in viewProperties) {
view.setRotation(viewProperties.rotation);
}
}
}
const layers = map.getAllLayers();
const layersParam = this.get_(params, "l");
if ("l" in this.params_ && layersParam && layersParam.length === layers.length) {
for (let i = 0, ii = layers.length; i < ii; ++i) {
const value = parseInt(layersParam[i]);
if (!isNaN(value)) {
const visible = Boolean(value);
const layer = layers[i];
if (layer.getVisible() !== visible) {
layer.setVisible(visible);
}
}
}
}
}
/**
* Register a listener for a URL search parameter. The callback will be called with a new value
* when the corresponding search parameter changes due to history events (e.g. browser navigation).
*
* @param {string} key The URL search parameter.
* @param {Callback} callback The function to call when the search parameter changes.
* @return {string|null} The initial value of the search parameter (or null if absent from the URL).
* @api
*/
track(key, callback) {
this.trackedCallbacks_[key] = callback;
const url = new URL(window.location.href);
const params = url.searchParams;
const value = params.get(key);
this.trackedValues_[key] = value;
return value;
}
/**
* Update the URL with a new search parameter value. If the value is null, it will be
* deleted from the search parameters.
*
* @param {string} key The URL search parameter.
* @param {string|null} value The updated value (or null to remove it from the URL).
* @api
*/
update(key, value) {
const url = new URL(window.location.href);
const params = url.searchParams;
if (value === null) {
params.delete(key);
} else {
params.set(key, value);
}
if (key in this.trackedValues_) {
this.trackedValues_[key] = value;
}
this.updateHistory_(url);
}
/**
* @private
*/
updateUrl_() {
const map = this.getMap();
if (!map) {
return;
}
const view = map.getView();
if (!view) {
return;
}
const center = view.getCenter();
const zoom = view.getZoom();
const rotation = view.getRotation();
const layers = map.getAllLayers();
const visibilities = new Array(layers.length);
for (let i = 0, ii = layers.length; i < ii; ++i) {
visibilities[i] = layers[i].getVisible() ? "1" : "0";
}
const url = new URL(window.location.href);
const params = url.searchParams;
this.set_(params, "x", writeNumber(center[0]));
this.set_(params, "y", writeNumber(center[1]));
this.set_(params, "z", writeNumber(zoom));
this.set_(params, "r", writeNumber(rotation));
this.set_(params, "l", visibilities.join(""));
this.updateHistory_(url);
this.initial_ = false;
}
/**
* @private
* @param {URL} url The URL.
*/
updateHistory_(url) {
if (url.href !== window.location.href) {
if (this.initial_ || this.replace_) {
window.history.replaceState(history.state, "", url);
} else {
window.history.pushState(null, "", url);
}
}
}
};
var Link_default = Link;
// node_modules/ol/interaction/Modify.js
var CIRCLE_CENTER_INDEX = 0;
var CIRCLE_CIRCUMFERENCE_INDEX = 1;
var tempExtent = [0, 0, 0, 0];
var tempSegment = [];
var ModifyEventType = {
/**
* Triggered upon feature modification start
* @event ModifyEvent#modifystart
* @api
*/
MODIFYSTART: "modifystart",
/**
* Triggered upon feature modification end
* @event ModifyEvent#modifyend
* @api
*/
MODIFYEND: "modifyend"
};
function getCoordinatesArray(coordinates, geometryType, depth) {
let coordinatesArray;
switch (geometryType) {
case "LineString":
coordinatesArray = coordinates;
break;
case "MultiLineString":
case "Polygon":
coordinatesArray = coordinates[depth[0]];
break;
case "MultiPolygon":
coordinatesArray = coordinates[depth[1]][depth[0]];
break;
default:
}
return coordinatesArray;
}
var ModifyEvent = class extends Event_default {
/**
* @param {ModifyEventType} type Type.
* @param {Collection<Feature>} features
* The features modified.
* @param {import("../MapBrowserEvent.js").default} mapBrowserEvent
* Associated {@link module:ol/MapBrowserEvent~MapBrowserEvent}.
*/
constructor(type, features, mapBrowserEvent) {
super(type);
this.features = features;
this.mapBrowserEvent = mapBrowserEvent;
}
};
var Modify = class extends Pointer_default {
/**
* @param {Options} options Options.
*/
constructor(options) {
super(
/** @type {import("./Pointer.js").Options} */
options
);
this.on;
this.once;
this.un;
this.boundHandleFeatureChange_ = this.handleFeatureChange_.bind(this);
this.condition_ = options.condition ? options.condition : primaryAction;
this.defaultDeleteCondition_ = function(mapBrowserEvent) {
return altKeyOnly(mapBrowserEvent) && singleClick(mapBrowserEvent);
};
this.deleteCondition_ = options.deleteCondition ? options.deleteCondition : this.defaultDeleteCondition_;
this.insertVertexCondition_ = options.insertVertexCondition ? options.insertVertexCondition : always;
this.vertexFeature_ = null;
this.vertexSegments_ = null;
this.lastCoordinate_ = [0, 0];
this.ignoreNextSingleClick_ = false;
this.featuresBeingModified_ = null;
this.rBush_ = new RBush_default();
this.pixelTolerance_ = options.pixelTolerance !== void 0 ? options.pixelTolerance : 10;
this.snappedToVertex_ = false;
this.changingFeature_ = false;
this.dragSegments_ = [];
this.overlay_ = new Vector_default({
source: new Vector_default2({
useSpatialIndex: false,
wrapX: !!options.wrapX
}),
style: options.style ? options.style : getDefaultStyleFunction(),
updateWhileAnimating: true,
updateWhileInteracting: true
});
this.SEGMENT_WRITERS_ = {
Point: this.writePointGeometry_.bind(this),
LineString: this.writeLineStringGeometry_.bind(this),
LinearRing: this.writeLineStringGeometry_.bind(this),
Polygon: this.writePolygonGeometry_.bind(this),
MultiPoint: this.writeMultiPointGeometry_.bind(this),
MultiLineString: this.writeMultiLineStringGeometry_.bind(this),
MultiPolygon: this.writeMultiPolygonGeometry_.bind(this),
Circle: this.writeCircleGeometry_.bind(this),
GeometryCollection: this.writeGeometryCollectionGeometry_.bind(this)
};
this.source_ = null;
this.traceSource_ = options.traceSource || options.source || null;
this.traceCondition_;
this.setTrace(options.trace || false);
this.traceState_ = { active: false };
this.traceSegments_ = null;
this.hitDetection_ = null;
let features;
if (options.features) {
features = options.features;
} else if (options.source) {
this.source_ = options.source;
features = new Collection_default(this.source_.getFeatures());
this.source_.addEventListener(
VectorEventType_default.ADDFEATURE,
this.handleSourceAdd_.bind(this)
);
this.source_.addEventListener(
VectorEventType_default.REMOVEFEATURE,
this.handleSourceRemove_.bind(this)
);
}
if (!features) {
throw new Error(
"The modify interaction requires features, a source or a layer"
);
}
if (options.hitDetection) {
this.hitDetection_ = options.hitDetection;
}
this.features_ = features;
this.features_.forEach(this.addFeature_.bind(this));
this.features_.addEventListener(
CollectionEventType_default.ADD,
this.handleFeatureAdd_.bind(this)
);
this.features_.addEventListener(
CollectionEventType_default.REMOVE,
this.handleFeatureRemove_.bind(this)
);
this.lastPointerEvent_ = null;
this.delta_ = [0, 0];
this.snapToPointer_ = options.snapToPointer === void 0 ? !this.hitDetection_ : options.snapToPointer;
}
/**
* Toggle tracing mode or set a tracing condition.
*
* @param {boolean|import("../events/condition.js").Condition} trace A boolean to toggle tracing mode or an event
* condition that will be checked when a feature is clicked to determine if tracing should be active.
*/
setTrace(trace) {
let condition;
if (!trace) {
condition = never;
} else if (trace === true) {
condition = always;
} else {
condition = trace;
}
this.traceCondition_ = condition;
}
/**
* @param {Feature} feature Feature.
* @private
*/
addFeature_(feature) {
const geometry = feature.getGeometry();
if (geometry) {
const writer = this.SEGMENT_WRITERS_[geometry.getType()];
if (writer) {
writer(feature, geometry);
}
}
const map = this.getMap();
if (map && map.isRendered() && this.getActive()) {
this.handlePointerAtPixel_(this.lastCoordinate_);
}
feature.addEventListener(EventType_default.CHANGE, this.boundHandleFeatureChange_);
}
/**
* @param {import("../MapBrowserEvent.js").default} evt Map browser event.
* @param {Array<SegmentData>} segments The segments subject to modification.
* @private
*/
willModifyFeatures_(evt, segments) {
if (!this.featuresBeingModified_) {
this.featuresBeingModified_ = new Collection_default();
const features = this.featuresBeingModified_.getArray();
for (let i = 0, ii = segments.length; i < ii; ++i) {
const feature = segments[i].feature;
if (feature && !features.includes(feature)) {
this.featuresBeingModified_.push(feature);
}
}
if (this.featuresBeingModified_.getLength() === 0) {
this.featuresBeingModified_ = null;
} else {
this.dispatchEvent(
new ModifyEvent(
ModifyEventType.MODIFYSTART,
this.featuresBeingModified_,
evt
)
);
}
}
}
/**
* @param {Feature} feature Feature.
* @private
*/
removeFeature_(feature) {
this.removeFeatureSegmentData_(feature);
if (this.vertexFeature_ && this.features_.getLength() === 0) {
this.overlay_.getSource().removeFeature(this.vertexFeature_);
this.vertexFeature_ = null;
}
feature.removeEventListener(
EventType_default.CHANGE,
this.boundHandleFeatureChange_
);
}
/**
* @param {Feature} feature Feature.
* @private
*/
removeFeatureSegmentData_(feature) {
const rBush = this.rBush_;
const nodesToRemove = [];
rBush.forEach(
/**
* @param {SegmentData} node RTree node.
*/
function(node) {
if (feature === node.feature) {
nodesToRemove.push(node);
}
}
);
for (let i = nodesToRemove.length - 1; i >= 0; --i) {
const nodeToRemove = nodesToRemove[i];
for (let j = this.dragSegments_.length - 1; j >= 0; --j) {
if (this.dragSegments_[j][0] === nodeToRemove) {
this.dragSegments_.splice(j, 1);
}
}
rBush.remove(nodeToRemove);
}
}
/**
* Activate or deactivate the interaction.
* @param {boolean} active Active.
* @observable
* @api
* @override
*/
setActive(active) {
if (this.vertexFeature_ && !active) {
this.overlay_.getSource().removeFeature(this.vertexFeature_);
this.vertexFeature_ = null;
}
super.setActive(active);
}
/**
* Remove the interaction from its current map and attach it to the new map.
* Subclasses may set up event handlers to get notified about changes to
* the map here.
* @param {import("../Map.js").default} map Map.
* @override
*/
setMap(map) {
this.overlay_.setMap(map);
super.setMap(map);
}
/**
* Get the overlay layer that this interaction renders the modification point or vertex to.
* @return {VectorLayer} Overlay layer.
* @api
*/
getOverlay() {
return this.overlay_;
}
/**
* @param {import("../source/Vector.js").VectorSourceEvent} event Event.
* @private
*/
handleSourceAdd_(event) {
if (event.feature) {
this.features_.push(event.feature);
}
}
/**
* @param {import("../source/Vector.js").VectorSourceEvent} event Event.
* @private
*/
handleSourceRemove_(event) {
if (event.feature) {
this.features_.remove(event.feature);
}
}
/**
* @param {import("../Collection.js").CollectionEvent<Feature>} evt Event.
* @private
*/
handleFeatureAdd_(evt) {
this.addFeature_(evt.element);
}
/**
* @param {import("../events/Event.js").default} evt Event.
* @private
*/
handleFeatureChange_(evt) {
if (!this.changingFeature_) {
const feature = (
/** @type {Feature} */
evt.target
);
this.removeFeature_(feature);
this.addFeature_(feature);
}
}
/**
* @param {import("../Collection.js").CollectionEvent<Feature>} evt Event.
* @private
*/
handleFeatureRemove_(evt) {
this.removeFeature_(evt.element);
}
/**
* @param {Feature} feature Feature
* @param {Point} geometry Geometry.
* @private
*/
writePointGeometry_(feature, geometry) {
const coordinates = geometry.getCoordinates();
const segmentData = {
feature,
geometry,
segment: [coordinates, coordinates]
};
this.rBush_.insert(geometry.getExtent(), segmentData);
}
/**
* @param {Feature} feature Feature
* @param {import("../geom/MultiPoint.js").default} geometry Geometry.
* @private
*/
writeMultiPointGeometry_(feature, geometry) {
const points = geometry.getCoordinates();
for (let i = 0, ii = points.length; i < ii; ++i) {
const coordinates = points[i];
const segmentData = {
feature,
geometry,
depth: [i],
index: i,
segment: [coordinates, coordinates]
};
this.rBush_.insert(geometry.getExtent(), segmentData);
}
}
/**
* @param {Feature} feature Feature
* @param {import("../geom/LineString.js").default} geometry Geometry.
* @private
*/
writeLineStringGeometry_(feature, geometry) {
const coordinates = geometry.getCoordinates();
for (let i = 0, ii = coordinates.length - 1; i < ii; ++i) {
const segment = coordinates.slice(i, i + 2);
const segmentData = {
feature,
geometry,
index: i,
segment
};
this.rBush_.insert(boundingExtent(segment), segmentData);
}
}
/**
* @param {Feature} feature Feature
* @param {import("../geom/MultiLineString.js").default} geometry Geometry.
* @private
*/
writeMultiLineStringGeometry_(feature, geometry) {
const lines = geometry.getCoordinates();
for (let j = 0, jj = lines.length; j < jj; ++j) {
const coordinates = lines[j];
for (let i = 0, ii = coordinates.length - 1; i < ii; ++i) {
const segment = coordinates.slice(i, i + 2);
const segmentData = {
feature,
geometry,
depth: [j],
index: i,
segment
};
this.rBush_.insert(boundingExtent(segment), segmentData);
}
}
}
/**
* @param {Feature} feature Feature
* @param {import("../geom/Polygon.js").default} geometry Geometry.
* @private
*/
writePolygonGeometry_(feature, geometry) {
const rings = geometry.getCoordinates();
for (let j = 0, jj = rings.length; j < jj; ++j) {
const coordinates = rings[j];
for (let i = 0, ii = coordinates.length - 1; i < ii; ++i) {
const segment = coordinates.slice(i, i + 2);
const segmentData = {
feature,
geometry,
depth: [j],
index: i,
segment
};
this.rBush_.insert(boundingExtent(segment), segmentData);
}
}
}
/**
* @param {Feature} feature Feature
* @param {import("../geom/MultiPolygon.js").default} geometry Geometry.
* @private
*/
writeMultiPolygonGeometry_(feature, geometry) {
const polygons = geometry.getCoordinates();
for (let k = 0, kk = polygons.length; k < kk; ++k) {
const rings = polygons[k];
for (let j = 0, jj = rings.length; j < jj; ++j) {
const coordinates = rings[j];
for (let i = 0, ii = coordinates.length - 1; i < ii; ++i) {
const segment = coordinates.slice(i, i + 2);
const segmentData = {
feature,
geometry,
depth: [j, k],
index: i,
segment
};
this.rBush_.insert(boundingExtent(segment), segmentData);
}
}
}
}
/**
* We convert a circle into two segments. The segment at index
* {@link CIRCLE_CENTER_INDEX} is the
* circle's center (a point). The segment at index
* {@link CIRCLE_CIRCUMFERENCE_INDEX} is
* the circumference, and is not a line segment.
*
* @param {Feature} feature Feature.
* @param {import("../geom/Circle.js").default} geometry Geometry.
* @private
*/
writeCircleGeometry_(feature, geometry) {
const coordinates = geometry.getCenter();
const centerSegmentData = {
feature,
geometry,
index: CIRCLE_CENTER_INDEX,
segment: [coordinates, coordinates]
};
const circumferenceSegmentData = {
feature,
geometry,
index: CIRCLE_CIRCUMFERENCE_INDEX,
segment: [coordinates, coordinates]
};
const featureSegments = [centerSegmentData, circumferenceSegmentData];
centerSegmentData.featureSegments = featureSegments;
circumferenceSegmentData.featureSegments = featureSegments;
this.rBush_.insert(createOrUpdateFromCoordinate(coordinates), centerSegmentData);
let circleGeometry = (
/** @type {import("../geom/Geometry.js").default} */
geometry
);
const userProjection = getUserProjection();
if (userProjection && this.getMap()) {
const projection = this.getMap().getView().getProjection();
circleGeometry = circleGeometry.clone().transform(userProjection, projection);
circleGeometry = fromCircle(
/** @type {import("../geom/Circle.js").default} */
circleGeometry
).transform(projection, userProjection);
}
this.rBush_.insert(circleGeometry.getExtent(), circumferenceSegmentData);
}
/**
* @param {Feature} feature Feature
* @param {import("../geom/GeometryCollection.js").default} geometry Geometry.
* @private
*/
writeGeometryCollectionGeometry_(feature, geometry) {
const geometries = geometry.getGeometriesArray();
for (let i = 0; i < geometries.length; ++i) {
const geometry2 = geometries[i];
const writer = this.SEGMENT_WRITERS_[geometry2.getType()];
writer(feature, geometry2);
}
}
/**
* @param {import("../coordinate.js").Coordinate} coordinates Coordinates.
* @param {Array<Feature>} features The features being modified.
* @param {Array<import("../geom/SimpleGeometry.js").default>} geometries The geometries being modified.
* @param {boolean} existing The vertex represents an existing vertex.
* @return {Feature} Vertex feature.
* @private
*/
createOrUpdateVertexFeature_(coordinates, features, geometries, existing) {
let vertexFeature = this.vertexFeature_;
if (!vertexFeature) {
vertexFeature = new Feature_default(new Point_default(coordinates));
this.vertexFeature_ = vertexFeature;
this.overlay_.getSource().addFeature(vertexFeature);
} else {
const geometry = vertexFeature.getGeometry();
geometry.setCoordinates(coordinates);
}
vertexFeature.set("features", features);
vertexFeature.set("geometries", geometries);
vertexFeature.set("existing", existing);
return vertexFeature;
}
/**
* Handles the {@link module:ol/MapBrowserEvent~MapBrowserEvent map browser event} and may modify the geometry.
* @param {import("../MapBrowserEvent.js").default} mapBrowserEvent Map browser event.
* @return {boolean} `false` to stop event propagation.
* @override
*/
handleEvent(mapBrowserEvent) {
if (!mapBrowserEvent.originalEvent) {
return true;
}
this.lastPointerEvent_ = mapBrowserEvent;
let handled;
if (!mapBrowserEvent.map.getView().getInteracting() && mapBrowserEvent.type == MapBrowserEventType_default.POINTERMOVE && !this.handlingDownUpSequence) {
this.handlePointerMove_(mapBrowserEvent);
}
if (this.vertexFeature_ && this.deleteCondition_(mapBrowserEvent)) {
if (mapBrowserEvent.type != MapBrowserEventType_default.SINGLECLICK || !this.ignoreNextSingleClick_) {
handled = this.removePoint();
} else {
handled = true;
}
}
if (mapBrowserEvent.type == MapBrowserEventType_default.SINGLECLICK) {
this.ignoreNextSingleClick_ = false;
}
return super.handleEvent(mapBrowserEvent) && !handled;
}
/**
* @param {import("../coordinate.js").Coordinate} pixelCoordinate Pixel coordinate.
* @return {Array<SegmentData>|undefined} Insert vertices and update drag segments.
* @private
*/
findInsertVerticesAndUpdateDragSegments_(pixelCoordinate) {
this.handlePointerAtPixel_(pixelCoordinate);
this.dragSegments_.length = 0;
this.featuresBeingModified_ = null;
const vertexFeature = this.vertexFeature_;
if (!vertexFeature) {
return;
}
const projection = this.getMap().getView().getProjection();
const insertVertices = [];
const vertex = this.vertexFeature_.getGeometry().getCoordinates();
const vertexExtent = boundingExtent([vertex]);
const segmentDataMatches = this.rBush_.getInExtent(vertexExtent);
const componentSegments = {};
segmentDataMatches.sort(compareIndexes);
for (let i = 0, ii = segmentDataMatches.length; i < ii; ++i) {
const segmentDataMatch = segmentDataMatches[i];
const segment = segmentDataMatch.segment;
let uid = getUid(segmentDataMatch.geometry);
const depth = segmentDataMatch.depth;
if (depth) {
uid += "-" + depth.join("-");
}
if (!componentSegments[uid]) {
componentSegments[uid] = new Array(2);
}
if (segmentDataMatch.geometry.getType() === "Circle" && segmentDataMatch.index === CIRCLE_CIRCUMFERENCE_INDEX) {
const closestVertex = closestOnSegmentData(
pixelCoordinate,
segmentDataMatch,
projection
);
if (equals2(closestVertex, vertex) && !componentSegments[uid][0]) {
this.dragSegments_.push([segmentDataMatch, 0]);
componentSegments[uid][0] = segmentDataMatch;
}
continue;
}
if (equals2(segment[0], vertex) && !componentSegments[uid][0]) {
this.dragSegments_.push([segmentDataMatch, 0]);
componentSegments[uid][0] = segmentDataMatch;
continue;
}
if (equals2(segment[1], vertex) && !componentSegments[uid][1]) {
if (componentSegments[uid][0] && componentSegments[uid][0].index === 0) {
let coordinates = segmentDataMatch.geometry.getCoordinates();
switch (segmentDataMatch.geometry.getType()) {
// prevent dragging closed linestrings by the connecting node
case "LineString":
case "MultiLineString":
continue;
// if dragging the first vertex of a polygon, ensure the other segment
// belongs to the closing vertex of the linear ring
case "MultiPolygon":
coordinates = coordinates[depth[1]];
/* falls through */
case "Polygon":
if (segmentDataMatch.index !== coordinates[depth[0]].length - 2) {
continue;
}
break;
default:
}
}
this.dragSegments_.push([segmentDataMatch, 1]);
componentSegments[uid][1] = segmentDataMatch;
continue;
}
if (getUid(segment) in this.vertexSegments_ && !componentSegments[uid][0] && !componentSegments[uid][1]) {
insertVertices.push(segmentDataMatch);
}
}
return insertVertices;
}
/**
* @private
*/
deactivateTrace_() {
this.traceState_ = { active: false };
}
/**
* Update the trace.
* @param {import("../MapBrowserEvent.js").default} event Event.
* @private
*/
updateTrace_(event) {
const traceState = this.traceState_;
if (!traceState.active) {
return;
}
if (traceState.targetIndex === -1) {
const startPx = event.map.getPixelFromCoordinate(traceState.startCoord);
if (distance(startPx, event.pixel) < this.pixelTolerance_) {
return;
}
}
const updatedTraceTarget = getTraceTargetUpdate(
event.coordinate,
traceState,
event.map,
this.pixelTolerance_
);
if (traceState.targetIndex === -1 && Math.sqrt(updatedTraceTarget.closestTargetDistance) / event.map.getView().getResolution() > this.pixelTolerance_) {
return;
}
if (traceState.targetIndex !== updatedTraceTarget.index) {
if (traceState.targetIndex !== -1) {
const oldTarget = traceState.targets[traceState.targetIndex];
this.removeTracedCoordinates_(oldTarget.startIndex, oldTarget.endIndex);
} else {
for (const traceSegment of this.traceSegments_) {
const segmentData = traceSegment[0];
const geometry = segmentData.geometry;
const index = traceSegment[1];
const coordinates = geometry.getCoordinates();
const coordinatesArray = getCoordinatesArray(
coordinates,
geometry.getType(),
segmentData.depth
);
coordinatesArray.splice(segmentData.index + index, 1);
geometry.setCoordinates(coordinates);
if (index === 0) {
segmentData.index -= 1;
}
}
}
const newTarget = traceState.targets[updatedTraceTarget.index];
this.addTracedCoordinates_(
newTarget,
newTarget.startIndex,
updatedTraceTarget.endIndex
);
} else {
const target2 = traceState.targets[traceState.targetIndex];
this.addOrRemoveTracedCoordinates_(target2, updatedTraceTarget.endIndex);
}
traceState.targetIndex = updatedTraceTarget.index;
const target = traceState.targets[traceState.targetIndex];
target.endIndex = updatedTraceTarget.endIndex;
}
getTraceCandidates_(event) {
const map = this.getMap();
const tolerance = this.pixelTolerance_;
const lowerLeft = map.getCoordinateFromPixel([
event.pixel[0] - tolerance,
event.pixel[1] + tolerance
]);
const upperRight = map.getCoordinateFromPixel([
event.pixel[0] + tolerance,
event.pixel[1] - tolerance
]);
const extent = boundingExtent([lowerLeft, upperRight]);
const features = this.traceSource_.getFeaturesInExtent(extent);
return features;
}
/**
* Activate or deactivate trace state based on a browser event.
* @param {import("../MapBrowserEvent.js").default} event Event.
* @private
*/
toggleTraceState_(event) {
if (!this.traceSource_ || !this.traceCondition_(event)) {
return;
}
if (this.traceState_.active) {
this.deactivateTrace_();
this.traceSegments_ = null;
return;
}
const features = this.getTraceCandidates_(event);
if (features.length === 0) {
return;
}
const targets = getTraceTargets(event.coordinate, features);
if (targets.length) {
this.traceState_ = {
active: true,
startCoord: event.coordinate.slice(),
targets,
targetIndex: -1
};
}
}
/**
* @param {import('./tracing.js').TraceTarget} target The trace target.
* @param {number} endIndex The new end index of the trace.
* @private
*/
addOrRemoveTracedCoordinates_(target, endIndex) {
const previouslyForward = target.startIndex <= target.endIndex;
const currentlyForward = target.startIndex <= endIndex;
if (previouslyForward === currentlyForward) {
if (previouslyForward && endIndex > target.endIndex || !previouslyForward && endIndex < target.endIndex) {
this.addTracedCoordinates_(target, target.endIndex, endIndex);
} else if (previouslyForward && endIndex < target.endIndex || !previouslyForward && endIndex > target.endIndex) {
this.removeTracedCoordinates_(endIndex, target.endIndex);
}
} else {
this.removeTracedCoordinates_(target.startIndex, target.endIndex);
this.addTracedCoordinates_(target, target.startIndex, endIndex);
}
}
/**
* @param {number} fromIndex The start index.
* @param {number} toIndex The end index.
* @private
*/
removeTracedCoordinates_(fromIndex, toIndex) {
if (fromIndex === toIndex) {
return;
}
let remove = 0;
if (fromIndex < toIndex) {
const start = Math.ceil(fromIndex);
let end = Math.floor(toIndex);
if (end === toIndex) {
end -= 1;
}
remove = end - start + 1;
} else {
const start = Math.floor(fromIndex);
let end = Math.ceil(toIndex);
if (end === toIndex) {
end += 1;
}
remove = start - end + 1;
}
if (remove > 0) {
for (const traceSegment of this.traceSegments_) {
const segmentData = traceSegment[0];
const geometry = segmentData.geometry;
const index = traceSegment[1];
let removeIndex = traceSegment[0].index + 1;
if (index === 1) {
removeIndex -= remove;
}
const coordinates = geometry.getCoordinates();
const coordinatesArray = getCoordinatesArray(
coordinates,
geometry.getType(),
segmentData.depth
);
coordinatesArray.splice(removeIndex, remove);
geometry.setCoordinates(coordinates);
if (index === 1) {
segmentData.index -= remove;
}
}
}
}
/**
* @param {import('./tracing.js').TraceTarget} target The trace target.
* @param {number} fromIndex The start index.
* @param {number} toIndex The end index.
* @private
*/
addTracedCoordinates_(target, fromIndex, toIndex) {
if (fromIndex === toIndex) {
return;
}
const newCoordinates = [];
if (fromIndex < toIndex) {
const start = Math.ceil(fromIndex);
let end = Math.floor(toIndex);
if (end === toIndex) {
end -= 1;
}
for (let i = start; i <= end; ++i) {
newCoordinates.push(getCoordinate(target.coordinates, i));
}
} else {
const start = Math.floor(fromIndex);
let end = Math.ceil(toIndex);
if (end === toIndex) {
end += 1;
}
for (let i = start; i >= end; --i) {
newCoordinates.push(getCoordinate(target.coordinates, i));
}
}
if (newCoordinates.length) {
for (const traceSegment of this.traceSegments_) {
const segmentData = traceSegment[0];
const geometry = segmentData.geometry;
const index = traceSegment[1];
const insertIndex = segmentData.index + 1;
if (index === 0) {
newCoordinates.reverse();
}
const coordinates = geometry.getCoordinates();
const coordinatesArray = getCoordinatesArray(
coordinates,
geometry.getType(),
segmentData.depth
);
coordinatesArray.splice(insertIndex, 0, ...newCoordinates);
geometry.setCoordinates(coordinates);
if (index === 1) {
segmentData.index += newCoordinates.length;
}
}
}
}
/**
* @param {import('../coordinate.js').Coordinate} vertex Vertex.
* @param {DragSegment} dragSegment Drag segment.
*/
updateGeometry_(vertex, dragSegment) {
const segmentData = dragSegment[0];
const depth = segmentData.depth;
let coordinates;
const segment = segmentData.segment;
const geometry = segmentData.geometry;
const index = dragSegment[1];
while (vertex.length < geometry.getStride()) {
vertex.push(segment[index][vertex.length]);
}
switch (geometry.getType()) {
case "Point":
coordinates = vertex;
segment[0] = vertex;
segment[1] = vertex;
break;
case "MultiPoint":
coordinates = geometry.getCoordinates();
coordinates[segmentData.index] = vertex;
segment[0] = vertex;
segment[1] = vertex;
break;
case "LineString":
coordinates = geometry.getCoordinates();
coordinates[segmentData.index + index] = vertex;
segment[index] = vertex;
break;
case "MultiLineString":
coordinates = geometry.getCoordinates();
coordinates[depth[0]][segmentData.index + index] = vertex;
segment[index] = vertex;
break;
case "Polygon":
coordinates = geometry.getCoordinates();
coordinates[depth[0]][segmentData.index + index] = vertex;
segment[index] = vertex;
break;
case "MultiPolygon":
coordinates = geometry.getCoordinates();
coordinates[depth[1]][depth[0]][segmentData.index + index] = vertex;
segment[index] = vertex;
break;
case "Circle":
const circle = (
/** @type {import("../geom/Circle.js").default} */
geometry
);
segment[0] = vertex;
segment[1] = vertex;
if (segmentData.index === CIRCLE_CENTER_INDEX) {
this.changingFeature_ = true;
circle.setCenter(vertex);
this.changingFeature_ = false;
} else {
this.changingFeature_ = true;
const projection = this.getMap().getView().getProjection();
let radius = distance(
fromUserCoordinate(circle.getCenter(), projection),
fromUserCoordinate(vertex, projection)
);
const userProjection = getUserProjection();
if (userProjection) {
const circleGeometry = circle.clone().transform(userProjection, projection);
circleGeometry.setRadius(radius);
radius = circleGeometry.transform(projection, userProjection).getRadius();
}
circle.setRadius(radius);
this.changingFeature_ = false;
}
break;
default:
}
if (coordinates) {
this.setGeometryCoordinates_(geometry, coordinates);
}
}
/**
* Handle pointer drag events.
* @param {import("../MapBrowserEvent.js").default} evt Event.
* @override
*/
handleDragEvent(evt) {
this.ignoreNextSingleClick_ = false;
this.willModifyFeatures_(
evt,
this.dragSegments_.map(([segment]) => segment)
);
const vertex = [
evt.coordinate[0] + this.delta_[0],
evt.coordinate[1] + this.delta_[1]
];
const features = [];
const geometries = [];
const startTraceCoord = this.traceState_.active && !this.traceSegments_ ? this.traceState_.startCoord : null;
if (startTraceCoord) {
this.traceSegments_ = [];
for (const dragSegment of this.dragSegments_) {
const segmentData = dragSegment[0];
const eligibleForTracing = distance(
closestOnSegment(startTraceCoord, segmentData.segment),
startTraceCoord
) / evt.map.getView().getResolution() < 1;
if (eligibleForTracing) {
this.traceSegments_.push(dragSegment);
}
}
}
for (let i = 0, ii = this.dragSegments_.length; i < ii; ++i) {
const dragSegment = this.dragSegments_[i];
const segmentData = dragSegment[0];
const feature = segmentData.feature;
if (!features.includes(feature)) {
features.push(feature);
}
const geometry = segmentData.geometry;
if (!geometries.includes(geometry)) {
geometries.push(geometry);
}
this.updateGeometry_(vertex, dragSegment);
}
this.updateTrace_(evt);
this.createOrUpdateVertexFeature_(vertex, features, geometries, true);
}
/**
* Handle pointer down events.
* @param {import("../MapBrowserEvent.js").default} evt Event.
* @return {boolean} If the event was consumed.
* @override
*/
handleDownEvent(evt) {
if (!this.condition_(evt)) {
return false;
}
const pixelCoordinate = evt.coordinate;
const insertVertices = this.findInsertVerticesAndUpdateDragSegments_(pixelCoordinate);
if ((insertVertices == null ? void 0 : insertVertices.length) && this.insertVertexCondition_(evt)) {
this.willModifyFeatures_(evt, insertVertices);
if (this.vertexFeature_) {
const vertex = this.vertexFeature_.getGeometry().getCoordinates();
for (let j = insertVertices.length - 1; j >= 0; --j) {
this.insertVertex_(insertVertices[j], vertex);
}
this.ignoreNextSingleClick_ = true;
}
}
return !!this.vertexFeature_;
}
/**
* Handle pointer up events.
* @param {import("../MapBrowserEvent.js").default} evt Event.
* @return {boolean} If the event was consumed.
* @override
*/
handleUpEvent(evt) {
for (let i = this.dragSegments_.length - 1; i >= 0; --i) {
const segmentData = this.dragSegments_[i][0];
const geometry = segmentData.geometry;
if (geometry.getType() === "Circle") {
const circle = (
/** @type {import("../geom/Circle.js").default} */
geometry
);
const coordinates = circle.getCenter();
const centerSegmentData = segmentData.featureSegments[0];
const circumferenceSegmentData = segmentData.featureSegments[1];
centerSegmentData.segment[0] = coordinates;
centerSegmentData.segment[1] = coordinates;
circumferenceSegmentData.segment[0] = coordinates;
circumferenceSegmentData.segment[1] = coordinates;
this.rBush_.update(createOrUpdateFromCoordinate(coordinates), centerSegmentData);
let circleGeometry = circle;
const userProjection = getUserProjection();
if (userProjection) {
const projection = evt.map.getView().getProjection();
circleGeometry = circleGeometry.clone().transform(userProjection, projection);
circleGeometry = fromCircle(circleGeometry).transform(
projection,
userProjection
);
}
this.rBush_.update(
circleGeometry.getExtent(),
circumferenceSegmentData
);
} else {
this.rBush_.update(boundingExtent(segmentData.segment), segmentData);
}
}
if (this.featuresBeingModified_) {
this.toggleTraceState_(evt);
this.dispatchEvent(
new ModifyEvent(
ModifyEventType.MODIFYEND,
this.featuresBeingModified_,
evt
)
);
this.featuresBeingModified_ = null;
}
return false;
}
/**
* @param {import("../MapBrowserEvent.js").default} evt Event.
* @private
*/
handlePointerMove_(evt) {
this.lastCoordinate_ = evt.coordinate;
this.handlePointerAtPixel_(this.lastCoordinate_);
}
/**
* @param {import("../coordinate.js").Coordinate} pixelCoordinate The pixel Coordinate.
* @private
*/
handlePointerAtPixel_(pixelCoordinate) {
const map = this.getMap();
const pixel = map.getPixelFromCoordinate(pixelCoordinate);
const projection = map.getView().getProjection();
const sortByDistance = function(a, b) {
return projectedDistanceToSegmentDataSquared(pixelCoordinate, a, projection) - projectedDistanceToSegmentDataSquared(pixelCoordinate, b, projection);
};
let nodes;
let hitPointGeometry;
if (this.hitDetection_) {
const layerFilter = typeof this.hitDetection_ === "object" ? (layer) => layer === this.hitDetection_ : void 0;
map.forEachFeatureAtPixel(
pixel,
(feature, layer, geometry) => {
if (geometry && geometry.getType() === "Point") {
geometry = new Point_default(
toUserCoordinate(geometry.getCoordinates(), projection)
);
}
const geom = geometry || feature.getGeometry();
if (geom && geom.getType() === "Point" && feature instanceof Feature_default && this.features_.getArray().includes(feature)) {
hitPointGeometry = /** @type {Point} */
geom;
const coordinate = (
/** @type {Point} */
feature.getGeometry().getFlatCoordinates().slice(0, 2)
);
nodes = [
{
feature,
geometry: hitPointGeometry,
segment: [coordinate, coordinate]
}
];
}
return true;
},
{ layerFilter }
);
}
if (!nodes) {
const viewExtent = fromUserExtent(
createOrUpdateFromCoordinate(pixelCoordinate, tempExtent),
projection
);
const buffer2 = map.getView().getResolution() * this.pixelTolerance_;
const box = toUserExtent(
buffer(viewExtent, buffer2, tempExtent),
projection
);
nodes = this.rBush_.getInExtent(box);
}
if (nodes && nodes.length > 0) {
const node = nodes.sort(sortByDistance)[0];
const closestSegment = node.segment;
let vertex = closestOnSegmentData(pixelCoordinate, node, projection);
const vertexPixel = map.getPixelFromCoordinate(vertex);
let dist = distance(pixel, vertexPixel);
if (hitPointGeometry || dist <= this.pixelTolerance_) {
const vertexSegments = {};
vertexSegments[getUid(closestSegment)] = true;
if (!this.snapToPointer_) {
this.delta_[0] = vertex[0] - pixelCoordinate[0];
this.delta_[1] = vertex[1] - pixelCoordinate[1];
}
if (node.geometry.getType() === "Circle" && node.index === CIRCLE_CIRCUMFERENCE_INDEX) {
this.snappedToVertex_ = true;
this.createOrUpdateVertexFeature_(
vertex,
[node.feature],
[node.geometry],
this.snappedToVertex_
);
} else {
const pixel1 = map.getPixelFromCoordinate(closestSegment[0]);
const pixel2 = map.getPixelFromCoordinate(closestSegment[1]);
const squaredDist1 = squaredDistance(vertexPixel, pixel1);
const squaredDist2 = squaredDistance(vertexPixel, pixel2);
dist = Math.sqrt(Math.min(squaredDist1, squaredDist2));
this.snappedToVertex_ = dist <= this.pixelTolerance_;
if (!this.snappedToVertex_ && !this.insertVertexCondition_(this.lastPointerEvent_)) {
if (this.vertexFeature_) {
this.overlay_.getSource().removeFeature(this.vertexFeature_);
this.vertexFeature_ = null;
}
return;
}
if (this.snappedToVertex_) {
vertex = squaredDist1 > squaredDist2 ? closestSegment[1] : closestSegment[0];
}
this.createOrUpdateVertexFeature_(
vertex,
[node.feature],
[node.geometry],
this.snappedToVertex_
);
const geometries = {};
geometries[getUid(node.geometry)] = true;
for (let i = 1, ii = nodes.length; i < ii; ++i) {
const segment = nodes[i].segment;
if (equals2(closestSegment[0], segment[0]) && equals2(closestSegment[1], segment[1]) || equals2(closestSegment[0], segment[1]) && equals2(closestSegment[1], segment[0])) {
const geometryUid = getUid(nodes[i].geometry);
if (!(geometryUid in geometries)) {
geometries[geometryUid] = true;
vertexSegments[getUid(segment)] = true;
}
} else {
break;
}
}
}
this.vertexSegments_ = vertexSegments;
return;
}
}
if (this.vertexFeature_) {
this.overlay_.getSource().removeFeature(this.vertexFeature_);
this.vertexFeature_ = null;
}
}
/**
* @param {SegmentData} segmentData Segment data.
* @param {import("../coordinate.js").Coordinate} vertex Vertex.
* @return {boolean} A vertex was inserted.
* @private
*/
insertVertex_(segmentData, vertex) {
const segment = segmentData.segment;
const feature = segmentData.feature;
const geometry = segmentData.geometry;
const depth = segmentData.depth;
const index = segmentData.index;
let coordinates;
while (vertex.length < geometry.getStride()) {
vertex.push(0);
}
switch (geometry.getType()) {
case "MultiLineString":
coordinates = geometry.getCoordinates();
coordinates[depth[0]].splice(index + 1, 0, vertex);
break;
case "Polygon":
coordinates = geometry.getCoordinates();
coordinates[depth[0]].splice(index + 1, 0, vertex);
break;
case "MultiPolygon":
coordinates = geometry.getCoordinates();
coordinates[depth[1]][depth[0]].splice(index + 1, 0, vertex);
break;
case "LineString":
coordinates = geometry.getCoordinates();
coordinates.splice(index + 1, 0, vertex);
break;
default:
return false;
}
this.setGeometryCoordinates_(geometry, coordinates);
const rTree = this.rBush_;
rTree.remove(segmentData);
this.updateSegmentIndices_(geometry, index, depth, 1);
const newSegmentData = {
segment: [segment[0], vertex],
feature,
geometry,
depth,
index
};
rTree.insert(boundingExtent(newSegmentData.segment), newSegmentData);
this.dragSegments_.push([newSegmentData, 1]);
const newSegmentData2 = {
segment: [vertex, segment[1]],
feature,
geometry,
depth,
index: index + 1
};
rTree.insert(boundingExtent(newSegmentData2.segment), newSegmentData2);
this.dragSegments_.push([newSegmentData2, 0]);
return true;
}
/**
* @param {import("../coordinate.js").Coordinate} coordinate The coordinate.
* @return {import("../coordinate.js").Coordinate} The updated pointer coordinate.
* @private
*/
updatePointer_(coordinate) {
var _a;
if (coordinate) {
this.findInsertVerticesAndUpdateDragSegments_(coordinate);
}
return (_a = this.vertexFeature_) == null ? void 0 : _a.getGeometry().getCoordinates();
}
/**
* Get the current pointer position.
* @return {import("../coordinate.js").Coordinate | null} The current pointer coordinate.
*/
getPoint() {
var _a;
const coordinate = (_a = this.vertexFeature_) == null ? void 0 : _a.getGeometry().getCoordinates();
if (!coordinate) {
return null;
}
return toUserCoordinate(
coordinate,
this.getMap().getView().getProjection()
);
}
/**
* Check if a point can be removed from the current linestring or polygon at the current
* pointer position.
* @return {boolean} A point can be deleted at the current pointer position.
* @api
*/
canRemovePoint() {
if (!this.vertexFeature_) {
return false;
}
if (this.vertexFeature_.get("geometries").every(
(geometry) => geometry.getType() === "Circle" || geometry.getType().endsWith("Point")
)) {
return false;
}
const coordinate = this.vertexFeature_.getGeometry().getCoordinates();
const segments = this.rBush_.getInExtent(boundingExtent([coordinate]));
return segments.some(
({ segment }) => equals2(segment[0], coordinate) || equals2(segment[1], coordinate)
);
}
/**
* Removes the vertex currently being pointed from the current linestring or polygon.
* @param {import('../coordinate.js').Coordinate} [coordinate] If provided, the pointer
* will be set to the provided coordinate. If not, the current pointer coordinate will be used.
* @return {boolean} True when a vertex was removed.
* @api
*/
removePoint(coordinate) {
if (coordinate) {
coordinate = fromUserCoordinate(
coordinate,
this.getMap().getView().getProjection()
);
this.updatePointer_(coordinate);
}
if (!this.lastPointerEvent_ || this.lastPointerEvent_ && this.lastPointerEvent_.type != MapBrowserEventType_default.POINTERDRAG) {
const evt = this.lastPointerEvent_;
this.willModifyFeatures_(
evt,
this.dragSegments_.map(([segment]) => segment)
);
const removed = this.removeVertex_();
if (this.featuresBeingModified_) {
this.dispatchEvent(
new ModifyEvent(
ModifyEventType.MODIFYEND,
this.featuresBeingModified_,
evt
)
);
}
this.featuresBeingModified_ = null;
return removed;
}
return false;
}
/**
* Removes a vertex from all matching features.
* @return {boolean} True when a vertex was removed.
* @private
*/
removeVertex_() {
const dragSegments = this.dragSegments_;
const segmentsByFeature = {};
let deleted = false;
let component, coordinates, dragSegment, geometry, i, index, left;
let newIndex, right, segmentData, uid;
for (i = dragSegments.length - 1; i >= 0; --i) {
dragSegment = dragSegments[i];
segmentData = dragSegment[0];
uid = getUid(segmentData.feature);
if (segmentData.depth) {
uid += "-" + segmentData.depth.join("-");
}
if (!(uid in segmentsByFeature)) {
segmentsByFeature[uid] = {};
}
if (dragSegment[1] === 0) {
segmentsByFeature[uid].right = segmentData;
segmentsByFeature[uid].index = segmentData.index;
} else if (dragSegment[1] == 1) {
segmentsByFeature[uid].left = segmentData;
segmentsByFeature[uid].index = segmentData.index + 1;
}
}
for (uid in segmentsByFeature) {
right = segmentsByFeature[uid].right;
left = segmentsByFeature[uid].left;
index = segmentsByFeature[uid].index;
newIndex = index - 1;
if (left !== void 0) {
segmentData = left;
} else {
segmentData = right;
}
if (newIndex < 0) {
newIndex = 0;
}
geometry = segmentData.geometry;
coordinates = geometry.getCoordinates();
component = coordinates;
deleted = false;
switch (geometry.getType()) {
case "MultiLineString":
if (coordinates[segmentData.depth[0]].length > 2) {
coordinates[segmentData.depth[0]].splice(index, 1);
deleted = true;
}
break;
case "LineString":
if (coordinates.length > 2) {
coordinates.splice(index, 1);
deleted = true;
}
break;
case "MultiPolygon":
component = component[segmentData.depth[1]];
/* falls through */
case "Polygon":
component = component[segmentData.depth[0]];
if (component.length > 4) {
if (index == component.length - 1) {
index = 0;
}
component.splice(index, 1);
deleted = true;
if (index === 0) {
component.pop();
component.push(component[0]);
newIndex = component.length - 1;
}
}
break;
default:
}
if (deleted) {
this.setGeometryCoordinates_(geometry, coordinates);
const segments = [];
if (left !== void 0) {
this.rBush_.remove(left);
segments.push(left.segment[0]);
}
if (right !== void 0) {
this.rBush_.remove(right);
segments.push(right.segment[1]);
}
if (left !== void 0 && right !== void 0) {
const newSegmentData = {
depth: segmentData.depth,
feature: segmentData.feature,
geometry: segmentData.geometry,
index: newIndex,
segment: segments
};
this.rBush_.insert(
boundingExtent(newSegmentData.segment),
newSegmentData
);
}
this.updateSegmentIndices_(geometry, index, segmentData.depth, -1);
if (this.vertexFeature_) {
this.overlay_.getSource().removeFeature(this.vertexFeature_);
this.vertexFeature_ = null;
}
dragSegments.length = 0;
}
}
return deleted;
}
/**
* Check if a point can be inserted to the current linestring or polygon at the current
* pointer position.
* @return {boolean} A point can be inserted at the current pointer position.
* @api
*/
canInsertPoint() {
if (!this.vertexFeature_) {
return false;
}
if (this.vertexFeature_.get("geometries").every(
(geometry) => geometry.getType() === "Circle" || geometry.getType().endsWith("Point")
)) {
return false;
}
const coordinate = this.vertexFeature_.getGeometry().getCoordinates();
const segments = this.rBush_.getInExtent(boundingExtent([coordinate]));
return segments.some(
({ segment }) => !(equals2(segment[0], coordinate) || equals2(segment[1], coordinate))
);
}
/**
* Inserts the vertex currently being pointed to the current linestring or polygon.
* @param {import('../coordinate.js').Coordinate} [coordinate] If provided, the pointer
* will be set to the provided coordinate. If not, the current pointer coordinate will be used.
* @return {boolean} A vertex was inserted.
* @api
*/
insertPoint(coordinate) {
var _a;
const pixelCoordinate = coordinate ? fromUserCoordinate(coordinate, this.getMap().getView().getProjection()) : (_a = this.vertexFeature_) == null ? void 0 : _a.getGeometry().getCoordinates();
if (!pixelCoordinate) {
return false;
}
const insertVertices = this.findInsertVerticesAndUpdateDragSegments_(pixelCoordinate);
return insertVertices.reduce(
(prev, segmentData) => prev || this.insertVertex_(segmentData, pixelCoordinate),
false
);
}
/**
* @param {import("../geom/SimpleGeometry.js").default} geometry Geometry.
* @param {Array} coordinates Coordinates.
* @private
*/
setGeometryCoordinates_(geometry, coordinates) {
this.changingFeature_ = true;
geometry.setCoordinates(coordinates);
this.changingFeature_ = false;
}
/**
* @param {import("../geom/SimpleGeometry.js").default} geometry Geometry.
* @param {number} index Index.
* @param {Array<number>|undefined} depth Depth.
* @param {number} delta Delta (1 or -1).
* @private
*/
updateSegmentIndices_(geometry, index, depth, delta) {
this.rBush_.forEachInExtent(
geometry.getExtent(),
function(segmentDataMatch) {
if (segmentDataMatch.geometry === geometry && (depth === void 0 || segmentDataMatch.depth === void 0 || equals(segmentDataMatch.depth, depth)) && segmentDataMatch.index > index) {
segmentDataMatch.index += delta;
}
}
);
}
};
function compareIndexes(a, b) {
return a.index - b.index;
}
function projectedDistanceToSegmentDataSquared(pointCoordinates, segmentData, projection) {
const geometry = segmentData.geometry;
if (geometry.getType() === "Circle") {
let circleGeometry = (
/** @type {import("../geom/Circle.js").default} */
geometry
);
if (segmentData.index === CIRCLE_CIRCUMFERENCE_INDEX) {
const userProjection = getUserProjection();
if (userProjection) {
circleGeometry = circleGeometry.clone().transform(userProjection, projection);
}
const distanceToCenterSquared = squaredDistance(
circleGeometry.getCenter(),
fromUserCoordinate(pointCoordinates, projection)
);
const distanceToCircumference = Math.sqrt(distanceToCenterSquared) - circleGeometry.getRadius();
return distanceToCircumference * distanceToCircumference;
}
}
const coordinate = fromUserCoordinate(pointCoordinates, projection);
tempSegment[0] = fromUserCoordinate(segmentData.segment[0], projection);
tempSegment[1] = fromUserCoordinate(segmentData.segment[1], projection);
return squaredDistanceToSegment(coordinate, tempSegment);
}
function closestOnSegmentData(pointCoordinates, segmentData, projection) {
const geometry = segmentData.geometry;
if (geometry.getType() === "Circle" && segmentData.index === CIRCLE_CIRCUMFERENCE_INDEX) {
let circleGeometry = (
/** @type {import("../geom/Circle.js").default} */
geometry
);
const userProjection = getUserProjection();
if (userProjection) {
circleGeometry = circleGeometry.clone().transform(userProjection, projection);
}
return toUserCoordinate(
circleGeometry.getClosestPoint(
fromUserCoordinate(pointCoordinates, projection)
),
projection
);
}
const coordinate = fromUserCoordinate(pointCoordinates, projection);
tempSegment[0] = fromUserCoordinate(segmentData.segment[0], projection);
tempSegment[1] = fromUserCoordinate(segmentData.segment[1], projection);
return toUserCoordinate(
closestOnSegment(coordinate, tempSegment),
projection
);
}
function getDefaultStyleFunction() {
const style = createEditingStyle();
return function(feature, resolution) {
return style["Point"];
};
}
var Modify_default = Modify;
// node_modules/ol/events/SnapEvent.js
var SnapEventType = {
/**
* Triggered upon snapping to vertex or edge
* @event SnapEvent#snap
* @api
*/
SNAP: "snap",
/**
* Triggered if no longer snapped
* @event SnapEvent#unsnap
* @api
*/
UNSNAP: "unsnap"
};
var SnapEvent = class extends Event_default {
/**
* @param {SnapEventType} type Type.
* @param {Object} options Options.
* @param {import("../coordinate.js").Coordinate} options.vertex The snapped vertex.
* @param {import("../coordinate.js").Coordinate} options.vertexPixel The pixel of the snapped vertex.
* @param {import("../Feature.js").default} options.feature The feature being snapped.
* @param {Array<import("../coordinate.js").Coordinate>|null} options.segment Segment, or `null` if snapped to a vertex.
*/
constructor(type, options) {
super(type);
this.vertex = options.vertex;
this.vertexPixel = options.vertexPixel;
this.feature = options.feature;
this.segment = options.segment;
}
};
// node_modules/ol/interaction/Snap.js
var GEOMETRY_SEGMENTERS = {
/**
* @param {import("../geom/Circle.js").default} geometry Geometry.
* @param {import("../proj/Projection.js").default} projection Projection.
* @return {Array<Segment>} Segments
*/
Circle(geometry, projection) {
let circleGeometry = geometry;
const userProjection = getUserProjection();
if (userProjection) {
circleGeometry = circleGeometry.clone().transform(userProjection, projection);
}
const polygon = fromCircle(circleGeometry);
if (userProjection) {
polygon.transform(projection, userProjection);
}
return GEOMETRY_SEGMENTERS.Polygon(polygon);
},
/**
* @param {import("../geom/GeometryCollection.js").default} geometry Geometry.
* @param {import("../proj/Projection.js").default} projection Projection.
* @return {Array<Segment>} Segments
*/
GeometryCollection(geometry, projection) {
const segments = [];
const geometries = geometry.getGeometriesArray();
for (let i = 0; i < geometries.length; ++i) {
const segmenter = this[geometries[i].getType()];
if (segmenter) {
segments.push(segmenter(geometries[i], projection));
}
}
return segments.flat();
},
/**
* @param {import("../geom/LineString.js").default} geometry Geometry.
* @return {Array<Segment>} Segments
*/
LineString(geometry) {
const segments = [];
const coordinates = geometry.getFlatCoordinates();
const stride = geometry.getStride();
for (let i = 0, ii = coordinates.length - stride; i < ii; i += stride) {
segments.push([
coordinates.slice(i, i + 2),
coordinates.slice(i + stride, i + stride + 2)
]);
}
return segments;
},
/**
* @param {import("../geom/MultiLineString.js").default} geometry Geometry.
* @return {Array<Segment>} Segments
*/
MultiLineString(geometry) {
const segments = [];
const coordinates = geometry.getFlatCoordinates();
const stride = geometry.getStride();
const ends = geometry.getEnds();
let offset = 0;
for (let i = 0, ii = ends.length; i < ii; ++i) {
const end = ends[i];
for (let j = offset, jj = end - stride; j < jj; j += stride) {
segments.push([
coordinates.slice(j, j + 2),
coordinates.slice(j + stride, j + stride + 2)
]);
}
offset = end;
}
return segments;
},
/**
* @param {import("../geom/MultiPoint.js").default} geometry Geometry.
* @return {Array<Segment>} Segments
*/
MultiPoint(geometry) {
const segments = [];
const coordinates = geometry.getFlatCoordinates();
const stride = geometry.getStride();
for (let i = 0, ii = coordinates.length; i < ii; i += stride) {
segments.push([coordinates.slice(i, i + 2)]);
}
return segments;
},
/**
* @param {import("../geom/MultiPolygon.js").default} geometry Geometry.
* @return {Array<Segment>} Segments
*/
MultiPolygon(geometry) {
const segments = [];
const coordinates = geometry.getFlatCoordinates();
const stride = geometry.getStride();
const endss = geometry.getEndss();
let offset = 0;
for (let i = 0, ii = endss.length; i < ii; ++i) {
const ends = endss[i];
for (let j = 0, jj = ends.length; j < jj; ++j) {
const end = ends[j];
for (let k = offset, kk = end - stride; k < kk; k += stride) {
segments.push([
coordinates.slice(k, k + 2),
coordinates.slice(k + stride, k + stride + 2)
]);
}
offset = end;
}
}
return segments;
},
/**
* @param {import("../geom/Point.js").default} geometry Geometry.
* @return {Array<Segment>} Segments
*/
Point(geometry) {
return [[geometry.getFlatCoordinates().slice(0, 2)]];
},
/**
* @param {import("../geom/Polygon.js").default} geometry Geometry.
* @return {Array<Segment>} Segments
*/
Polygon(geometry) {
const segments = [];
const coordinates = geometry.getFlatCoordinates();
const stride = geometry.getStride();
const ends = geometry.getEnds();
let offset = 0;
for (let i = 0, ii = ends.length; i < ii; ++i) {
const end = ends[i];
for (let j = offset, jj = end - stride; j < jj; j += stride) {
segments.push([
coordinates.slice(j, j + 2),
coordinates.slice(j + stride, j + stride + 2)
]);
}
offset = end;
}
return segments;
}
};
function getFeatureFromEvent(evt) {
if (
/** @type {import("../source/Vector.js").VectorSourceEvent} */
evt.feature
) {
return (
/** @type {import("../source/Vector.js").VectorSourceEvent} */
evt.feature
);
}
if (
/** @type {import("../Collection.js").CollectionEvent<import("../Feature.js").default>} */
evt.element
) {
return (
/** @type {import("../Collection.js").CollectionEvent<import("../Feature.js").default>} */
evt.element
);
}
return null;
}
var tempSegment2 = [];
var tempExtents = [];
var tempSegmentData = [];
var Snap = class extends Pointer_default {
/**
* @param {Options} [options] Options.
*/
constructor(options) {
options = options ? options : {};
super({
handleDownEvent: TRUE,
stopDown: FALSE
});
this.on;
this.once;
this.un;
this.source_ = options.source ? options.source : null;
this.vertex_ = options.vertex !== void 0 ? options.vertex : true;
this.edge_ = options.edge !== void 0 ? options.edge : true;
this.intersection_ = options.intersection !== void 0 ? options.intersection : false;
this.features_ = options.features ? options.features : null;
this.featuresListenerKeys_ = [];
this.featureChangeListenerKeys_ = {};
this.indexedFeaturesExtents_ = {};
this.pendingFeatures_ = {};
this.pixelTolerance_ = options.pixelTolerance !== void 0 ? options.pixelTolerance : 10;
this.rBush_ = new RBush_default();
this.snapped_ = null;
this.segmenters_ = Object.assign(
{},
GEOMETRY_SEGMENTERS,
options.segmenters
);
}
/**
* Add a feature to the collection of features that we may snap to.
* @param {import("../Feature.js").default} feature Feature.
* @param {boolean} [register] Whether to listen to the feature change or not
* Defaults to `true`.
* @api
*/
addFeature(feature, register) {
register = register !== void 0 ? register : true;
const feature_uid = getUid(feature);
const geometry = feature.getGeometry();
if (geometry) {
const segmenter = this.segmenters_[geometry.getType()];
if (segmenter) {
this.indexedFeaturesExtents_[feature_uid] = geometry.getExtent(createEmpty());
const segments = segmenter.call(
this.segmenters_,
geometry,
this.getMap().getView().getProjection()
);
let segmentCount = segments.length;
for (let i = 0; i < segmentCount; ++i) {
const segment = segments[i];
tempExtents[i] = boundingExtent(segment);
tempSegmentData[i] = {
feature,
segment
};
}
if (this.intersection_) {
for (let j = 0, jj = segments.length; j < jj; ++j) {
const segment = segments[j];
if (segment.length === 1) {
continue;
}
const extent = tempExtents[j];
for (let k = 0, kk = j - 1; k < kk; ++k) {
const otherSegment = segments[k];
if (!intersects(extent, tempExtents[k])) {
continue;
}
const intersection = getIntersectionPoint(segment, otherSegment);
if (!intersection) {
continue;
}
const intersectionSegment = [intersection];
tempExtents[segmentCount] = boundingExtent(intersectionSegment);
tempSegmentData[segmentCount++] = {
feature,
intersectionFeature: feature,
segment: intersectionSegment
};
}
const otherSegments = this.rBush_.getInExtent(tempExtents[j]);
for (let k = 0, kk = otherSegments.length; k < kk; ++k) {
const otherSegment = otherSegments[k].segment;
if (otherSegment.length === 1) {
continue;
}
const intersection = getIntersectionPoint(segment, otherSegment);
if (!intersection) {
continue;
}
const intersectionSegment = [intersection];
tempExtents[segmentCount] = boundingExtent(intersectionSegment);
tempSegmentData[segmentCount++] = {
feature,
intersectionFeature: otherSegments[k].feature,
segment: intersectionSegment
};
}
}
}
if (segmentCount === 1) {
this.rBush_.insert(tempExtents[0], tempSegmentData[0]);
} else {
tempExtents.length = segmentCount;
tempSegmentData.length = segmentCount;
this.rBush_.load(tempExtents, tempSegmentData);
}
}
}
if (register) {
if (this.featureChangeListenerKeys_[feature_uid]) {
unlistenByKey(this.featureChangeListenerKeys_[feature_uid]);
}
this.featureChangeListenerKeys_[feature_uid] = listen(
feature,
EventType_default.CHANGE,
this.handleFeatureChange_,
this
);
}
}
/**
* @return {import("../Collection.js").default<import("../Feature.js").default>|Array<import("../Feature.js").default>} Features.
* @private
*/
getFeatures_() {
let features;
if (this.features_) {
features = this.features_;
} else if (this.source_) {
features = this.source_.getFeatures();
}
return features;
}
/**
* Checks if two snap data sets are equal.
* Compares the segment and the feature.
*
* @param {SnappedInfo} data1 The first snap data set.
* @param {SnappedInfo} data2 The second snap data set.
* @return {boolean} `true` if the data sets are equal, otherwise `false`.
* @private
*/
areSnapDataEqual_(data1, data2) {
return data1.segment === data2.segment && data1.feature === data2.feature;
}
/**
* @param {import("../MapBrowserEvent.js").default} evt Map browser event.
* @return {boolean} `false` to stop event propagation.
* @api
* @override
*/
handleEvent(evt) {
const result = this.snapTo(evt.pixel, evt.coordinate, evt.map);
if (result) {
evt.coordinate = result.vertex.slice(0, 2);
evt.pixel = result.vertexPixel;
if (this.snapped_ && !this.areSnapDataEqual_(this.snapped_, result)) {
this.dispatchEvent(new SnapEvent(SnapEventType.UNSNAP, this.snapped_));
}
this.snapped_ = {
vertex: evt.coordinate,
vertexPixel: evt.pixel,
feature: result.feature,
segment: result.segment
};
this.dispatchEvent(new SnapEvent(SnapEventType.SNAP, this.snapped_));
} else if (this.snapped_) {
this.dispatchEvent(new SnapEvent(SnapEventType.UNSNAP, this.snapped_));
this.snapped_ = null;
}
return super.handleEvent(evt);
}
/**
* @param {import("../source/Vector.js").VectorSourceEvent|import("../Collection.js").CollectionEvent<import("../Feature.js").default>} evt Event.
* @private
*/
handleFeatureAdd_(evt) {
const feature = getFeatureFromEvent(evt);
if (feature) {
this.addFeature(feature);
}
}
/**
* @param {import("../source/Vector.js").VectorSourceEvent|import("../Collection.js").CollectionEvent<import("../Feature.js").default>} evt Event.
* @private
*/
handleFeatureRemove_(evt) {
const feature = getFeatureFromEvent(evt);
if (feature) {
this.removeFeature(feature);
delete this.pendingFeatures_[getUid(feature)];
}
}
/**
* @param {import("../events/Event.js").default} evt Event.
* @private
*/
handleFeatureChange_(evt) {
const feature = (
/** @type {import("../Feature.js").default} */
evt.target
);
if (this.handlingDownUpSequence) {
this.pendingFeatures_[getUid(feature)] = feature;
} else {
this.updateFeature_(feature);
}
}
/**
* Handle pointer up events.
* @param {import("../MapBrowserEvent.js").default} evt Event.
* @return {boolean} If the event was consumed.
* @override
*/
handleUpEvent(evt) {
const featuresToUpdate = Object.values(this.pendingFeatures_);
if (featuresToUpdate.length) {
for (const feature of featuresToUpdate) {
this.updateFeature_(feature);
}
clear(this.pendingFeatures_);
}
return false;
}
/**
* Remove a feature from the collection of features that we may snap to.
* @param {import("../Feature.js").default} feature Feature
* @param {boolean} [unlisten] Whether to unlisten to the feature change
* or not. Defaults to `true`.
* @api
*/
removeFeature(feature, unlisten) {
const unregister = unlisten !== void 0 ? unlisten : true;
const feature_uid = getUid(feature);
const extent = this.indexedFeaturesExtents_[feature_uid];
if (extent) {
const rBush = this.rBush_;
rBush.getInExtent(extent).forEach((node) => {
if (feature === node.feature || feature === node.intersectionFeature) {
rBush.remove(node);
}
});
}
if (unregister) {
unlistenByKey(this.featureChangeListenerKeys_[feature_uid]);
delete this.featureChangeListenerKeys_[feature_uid];
}
}
/**
* Remove the interaction from its current map and attach it to the new map.
* Subclasses may set up event handlers to get notified about changes to
* the map here.
* @param {import("../Map.js").default} map Map.
* @override
*/
setMap(map) {
const currentMap = this.getMap();
const keys = this.featuresListenerKeys_;
let features = this.getFeatures_();
if (!Array.isArray(features)) {
features = features.getArray();
}
if (currentMap) {
keys.forEach(unlistenByKey);
keys.length = 0;
this.rBush_.clear();
Object.values(this.featureChangeListenerKeys_).forEach(unlistenByKey);
this.featureChangeListenerKeys_ = {};
}
super.setMap(map);
if (map) {
if (this.features_) {
keys.push(
listen(
this.features_,
CollectionEventType_default.ADD,
this.handleFeatureAdd_,
this
),
listen(
this.features_,
CollectionEventType_default.REMOVE,
this.handleFeatureRemove_,
this
)
);
} else if (this.source_) {
keys.push(
listen(
this.source_,
VectorEventType_default.ADDFEATURE,
this.handleFeatureAdd_,
this
),
listen(
this.source_,
VectorEventType_default.REMOVEFEATURE,
this.handleFeatureRemove_,
this
)
);
}
for (const feature of features) {
this.addFeature(feature);
}
}
}
/**
* @param {import("../pixel.js").Pixel} pixel Pixel
* @param {import("../coordinate.js").Coordinate} pixelCoordinate Coordinate
* @param {import("../Map.js").default} map Map.
* @return {SnappedInfo|null} Snap result
*/
snapTo(pixel, pixelCoordinate, map) {
const projection = map.getView().getProjection();
const projectedCoordinate = fromUserCoordinate(pixelCoordinate, projection);
const box = toUserExtent(
buffer(
boundingExtent([projectedCoordinate]),
map.getView().getResolution() * this.pixelTolerance_
),
projection
);
const segments = this.rBush_.getInExtent(box);
const segmentsLength = segments.length;
if (segmentsLength === 0) {
return null;
}
let closestVertex;
let minSquaredDistance = Infinity;
let closestFeature;
let closestSegment = null;
const squaredPixelTolerance = this.pixelTolerance_ * this.pixelTolerance_;
const getResult = () => {
if (!closestVertex) {
return null;
}
const vertexPixel = map.getPixelFromCoordinate(closestVertex);
const squaredPixelDistance = squaredDistance(pixel, vertexPixel);
if (squaredPixelDistance > squaredPixelTolerance) {
return null;
}
return {
vertex: closestVertex,
vertexPixel: [Math.round(vertexPixel[0]), Math.round(vertexPixel[1])],
feature: closestFeature,
segment: closestSegment
};
};
if (this.vertex_ || this.intersection_) {
for (let i = 0; i < segmentsLength; ++i) {
const segmentData = segments[i];
if (segmentData.feature.getGeometry().getType() !== "Circle") {
for (const vertex of segmentData.segment) {
const tempVertexCoord = fromUserCoordinate(vertex, projection);
const delta = squaredDistance(projectedCoordinate, tempVertexCoord);
if (delta < minSquaredDistance && (this.intersection_ && segmentData.intersectionFeature || this.vertex_ && !segmentData.intersectionFeature)) {
closestVertex = vertex;
minSquaredDistance = delta;
closestFeature = segmentData.feature;
}
}
}
}
const result = getResult();
if (result) {
return result;
}
}
if (this.edge_) {
for (let i = 0; i < segmentsLength; ++i) {
let vertex = null;
const segmentData = segments[i];
if (segmentData.feature.getGeometry().getType() === "Circle") {
let circleGeometry = segmentData.feature.getGeometry();
const userProjection = getUserProjection();
if (userProjection) {
circleGeometry = circleGeometry.clone().transform(userProjection, projection);
}
vertex = closestOnCircle(
projectedCoordinate,
/** @type {import("../geom/Circle.js").default} */
circleGeometry
);
} else {
const [segmentStart, segmentEnd] = segmentData.segment;
if (segmentEnd) {
tempSegment2[0] = fromUserCoordinate(segmentStart, projection);
tempSegment2[1] = fromUserCoordinate(segmentEnd, projection);
vertex = closestOnSegment(projectedCoordinate, tempSegment2);
}
}
if (vertex) {
const delta = squaredDistance(projectedCoordinate, vertex);
if (delta < minSquaredDistance) {
closestVertex = toUserCoordinate(vertex, projection);
closestSegment = segmentData.feature.getGeometry().getType() === "Circle" ? null : segmentData.segment;
minSquaredDistance = delta;
closestFeature = segmentData.feature;
}
}
}
const result = getResult();
if (result) {
return result;
}
}
return null;
}
/**
* @param {import("../Feature.js").default} feature Feature
* @private
*/
updateFeature_(feature) {
this.removeFeature(feature, false);
this.addFeature(feature, false);
}
};
var Snap_default = Snap;
// node_modules/ol/interaction/Translate.js
var TranslateEventType = {
/**
* Triggered upon feature translation start.
* @event TranslateEvent#translatestart
* @api
*/
TRANSLATESTART: "translatestart",
/**
* Triggered upon feature translation.
* @event TranslateEvent#translating
* @api
*/
TRANSLATING: "translating",
/**
* Triggered upon feature translation end.
* @event TranslateEvent#translateend
* @api
*/
TRANSLATEEND: "translateend"
};
var TranslateEvent = class extends Event_default {
/**
* @param {TranslateEventType} type Type.
* @param {Collection<Feature>} features The features translated.
* @param {import("../coordinate.js").Coordinate} coordinate The event coordinate.
* @param {import("../coordinate.js").Coordinate} startCoordinate The original coordinates before.translation started
* @param {import("../MapBrowserEvent.js").default} mapBrowserEvent Map browser event.
*/
constructor(type, features, coordinate, startCoordinate, mapBrowserEvent) {
super(type);
this.features = features;
this.coordinate = coordinate;
this.startCoordinate = startCoordinate;
this.mapBrowserEvent = mapBrowserEvent;
}
};
var Translate = class extends Pointer_default {
/**
* @param {Options} [options] Options.
*/
constructor(options) {
options = options ? options : {};
super(
/** @type {import("./Pointer.js").Options} */
options
);
this.on;
this.once;
this.un;
this.lastCoordinate_ = null;
this.startCoordinate_ = null;
this.features_ = options.features !== void 0 ? options.features : null;
let layerFilter;
if (options.layers && !this.features_) {
if (typeof options.layers === "function") {
layerFilter = options.layers;
} else {
const layers = options.layers;
layerFilter = function(layer) {
return layers.includes(layer);
};
}
} else {
layerFilter = TRUE;
}
this.layerFilter_ = layerFilter;
this.filter_ = options.filter && !this.features_ ? options.filter : TRUE;
this.hitTolerance_ = options.hitTolerance ? options.hitTolerance : 0;
this.condition_ = options.condition ? options.condition : always;
this.lastFeature_ = null;
this.addChangeListener(
Property_default.ACTIVE,
this.handleActiveChanged_
);
}
/**
* Handle pointer down events.
* @param {import("../MapBrowserEvent.js").default} event Event.
* @return {boolean} If the event was consumed.
* @override
*/
handleDownEvent(event) {
if (!event.originalEvent || !this.condition_(event)) {
return false;
}
this.lastFeature_ = this.featuresAtPixel_(event.pixel, event.map);
if (!this.lastCoordinate_ && this.lastFeature_) {
this.startCoordinate_ = event.coordinate;
this.lastCoordinate_ = event.coordinate;
this.handleMoveEvent(event);
const features = this.features_ || new Collection_default([this.lastFeature_]);
this.dispatchEvent(
new TranslateEvent(
TranslateEventType.TRANSLATESTART,
features,
event.coordinate,
this.startCoordinate_,
event
)
);
return true;
}
return false;
}
/**
* Handle pointer up events.
* @param {import("../MapBrowserEvent.js").default} event Event.
* @return {boolean} If the event was consumed.
* @override
*/
handleUpEvent(event) {
if (this.lastCoordinate_) {
this.lastCoordinate_ = null;
this.handleMoveEvent(event);
const features = this.features_ || new Collection_default([this.lastFeature_]);
this.dispatchEvent(
new TranslateEvent(
TranslateEventType.TRANSLATEEND,
features,
event.coordinate,
this.startCoordinate_,
event
)
);
this.startCoordinate_ = null;
return true;
}
return false;
}
/**
* Handle pointer drag events.
* @param {import("../MapBrowserEvent.js").default} event Event.
* @override
*/
handleDragEvent(event) {
if (this.lastCoordinate_) {
const newCoordinate = event.coordinate;
const projection = event.map.getView().getProjection();
const newViewCoordinate = fromUserCoordinate(newCoordinate, projection);
const lastViewCoordinate = fromUserCoordinate(
this.lastCoordinate_,
projection
);
const deltaX = newViewCoordinate[0] - lastViewCoordinate[0];
const deltaY = newViewCoordinate[1] - lastViewCoordinate[1];
const features = this.features_ || new Collection_default([this.lastFeature_]);
const userProjection = getUserProjection();
features.forEach(function(feature) {
const geom = feature.getGeometry();
if (userProjection) {
geom.transform(userProjection, projection);
geom.translate(deltaX, deltaY);
geom.transform(projection, userProjection);
} else {
geom.translate(deltaX, deltaY);
}
feature.setGeometry(geom);
});
this.lastCoordinate_ = newCoordinate;
this.dispatchEvent(
new TranslateEvent(
TranslateEventType.TRANSLATING,
features,
newCoordinate,
this.startCoordinate_,
event
)
);
}
}
/**
* Handle pointer move events.
* @param {import("../MapBrowserEvent.js").default} event Event.
* @override
*/
handleMoveEvent(event) {
const elem = event.map.getViewport();
if (this.featuresAtPixel_(event.pixel, event.map)) {
elem.classList.remove(this.lastCoordinate_ ? "ol-grab" : "ol-grabbing");
elem.classList.add(this.lastCoordinate_ ? "ol-grabbing" : "ol-grab");
} else {
elem.classList.remove("ol-grab", "ol-grabbing");
}
}
/**
* Tests to see if the given coordinates intersects any of our selected
* features.
* @param {import("../pixel.js").Pixel} pixel Pixel coordinate to test for intersection.
* @param {import("../Map.js").default} map Map to test the intersection on.
* @return {Feature} Returns the feature found at the specified pixel
* coordinates.
* @private
*/
featuresAtPixel_(pixel, map) {
return map.forEachFeatureAtPixel(
pixel,
(feature, layer) => {
if (!(feature instanceof Feature_default) || !this.filter_(feature, layer)) {
return void 0;
}
if (this.features_ && !this.features_.getArray().includes(feature)) {
return void 0;
}
return feature;
},
{
layerFilter: this.layerFilter_,
hitTolerance: this.hitTolerance_
}
);
}
/**
* Returns the Hit-detection tolerance.
* @return {number} Hit tolerance in pixels.
* @api
*/
getHitTolerance() {
return this.hitTolerance_;
}
/**
* Hit-detection tolerance. Pixels inside the radius around the given position
* will be checked for features.
* @param {number} hitTolerance Hit tolerance in pixels.
* @api
*/
setHitTolerance(hitTolerance) {
this.hitTolerance_ = hitTolerance;
}
/**
* Remove the interaction from its current map and attach it to the new map.
* Subclasses may set up event handlers to get notified about changes to
* the map here.
* @param {import("../Map.js").default} map Map.
* @override
*/
setMap(map) {
const oldMap = this.getMap();
super.setMap(map);
this.updateState_(oldMap);
}
/**
* @private
*/
handleActiveChanged_() {
this.updateState_(null);
}
/**
* @param {import("../Map.js").default} oldMap Old map.
* @private
*/
updateState_(oldMap) {
let map = this.getMap();
const active = this.getActive();
if (!map || !active) {
map = map || oldMap;
if (map) {
const elem = map.getViewport();
elem.classList.remove("ol-grab", "ol-grabbing");
}
}
}
};
var Translate_default = Translate;
export {
DblClickDragZoom_default as DblClickDragZoom,
DoubleClickZoom_default as DoubleClickZoom,
DragAndDrop_default as DragAndDrop,
DragBox_default as DragBox,
DragPan_default as DragPan,
DragRotate_default as DragRotate,
DragRotateAndZoom_default as DragRotateAndZoom,
DragZoom_default as DragZoom,
Draw_default as Draw,
Extent_default as Extent,
Interaction_default as Interaction,
KeyboardPan_default as KeyboardPan,
KeyboardZoom_default as KeyboardZoom,
Link_default as Link,
Modify_default as Modify,
MouseWheelZoom_default as MouseWheelZoom,
PinchRotate_default as PinchRotate,
PinchZoom_default as PinchZoom,
Pointer_default as Pointer,
Select_default as Select,
Snap_default as Snap,
Translate_default as Translate,
defaults
};
//# sourceMappingURL=ol_interaction.js.map