planning
All checks were successful
Publish To Prod / deploy_and_publish (push) Successful in 35s

This commit is contained in:
2024-10-14 09:15:30 +02:00
parent bcba00a730
commit 6e64e138e2
21059 changed files with 2317811 additions and 1 deletions

64
node_modules/ol/src/interaction/DoubleClickZoom.js generated vendored Normal file
View File

@@ -0,0 +1,64 @@
/**
* @module ol/interaction/DoubleClickZoom
*/
import Interaction, {zoomByDelta} from './Interaction.js';
import MapBrowserEventType from '../MapBrowserEventType.js';
/**
* @typedef {Object} Options
* @property {number} [duration=250] Animation duration in milliseconds.
* @property {number} [delta=1] The zoom delta applied on each double click.
*/
/**
* @classdesc
* Allows the user to zoom by double-clicking on the map.
* @api
*/
class DoubleClickZoom extends Interaction {
/**
* @param {Options} [opt_options] Options.
*/
constructor(opt_options) {
super();
const options = opt_options ? opt_options : {};
/**
* @private
* @type {number}
*/
this.delta_ = options.delta ? options.delta : 1;
/**
* @private
* @type {number}
*/
this.duration_ = options.duration !== undefined ? options.duration : 250;
}
/**
* Handles the {@link module:ol/MapBrowserEvent~MapBrowserEvent map browser event} (if it was a
* doubleclick) and eventually zooms the map.
* @param {import("../MapBrowserEvent.js").default} mapBrowserEvent Map browser event.
* @return {boolean} `false` to stop event propagation.
*/
handleEvent(mapBrowserEvent) {
let stopEvent = false;
if (mapBrowserEvent.type == MapBrowserEventType.DBLCLICK) {
const browserEvent = /** @type {MouseEvent} */ (
mapBrowserEvent.originalEvent
);
const map = mapBrowserEvent.map;
const anchor = mapBrowserEvent.coordinate;
const delta = browserEvent.shiftKey ? -this.delta_ : this.delta_;
const view = map.getView();
zoomByDelta(view, delta, anchor, this.duration_);
browserEvent.preventDefault();
stopEvent = true;
}
return !stopEvent;
}
}
export default DoubleClickZoom;

325
node_modules/ol/src/interaction/DragAndDrop.js generated vendored Normal file
View File

@@ -0,0 +1,325 @@
/**
* @module ol/interaction/DragAndDrop
*/
// FIXME should handle all geo-referenced data, not just vector data
import Event from '../events/Event.js';
import EventType from '../events/EventType.js';
import Interaction from './Interaction.js';
import {TRUE} from '../functions.js';
import {get as getProjection} from '../proj.js';
import {listen, unlistenByKey} from '../events.js';
/**
* @typedef {Object} Options
* @property {Array<typeof import("../format/Feature.js").default|import("../format/Feature.js").default>} [formatConstructors] Format constructors
* (and/or formats pre-constructed with options).
* @property {import("../source/Vector.js").default} [source] Optional vector source where features will be added. If a source is provided
* all existing features will be removed and new features will be added when
* they are dropped on the target. If you want to add features to a vector
* source without removing the existing features (append only), instead of
* providing the source option listen for the "addfeatures" event.
* @property {import("../proj.js").ProjectionLike} [projection] Target projection. By default, the map's view's projection is used.
* @property {HTMLElement} [target] The element that is used as the drop target, default is the viewport element.
*/
/**
* @enum {string}
*/
const DragAndDropEventType = {
/**
* Triggered when features are added
* @event DragAndDropEvent#addfeatures
* @api
*/
ADD_FEATURES: 'addfeatures',
};
/**
* @classdesc
* Events emitted by {@link module:ol/interaction/DragAndDrop~DragAndDrop} instances are instances
* of this type.
*/
export class DragAndDropEvent extends Event {
/**
* @param {DragAndDropEventType} type Type.
* @param {File} file File.
* @param {Array<import("../Feature.js").default>} [opt_features] Features.
* @param {import("../proj/Projection.js").default} [opt_projection] Projection.
*/
constructor(type, file, opt_features, opt_projection) {
super(type);
/**
* The features parsed from dropped data.
* @type {Array<import("../Feature.js").FeatureLike>|undefined}
* @api
*/
this.features = opt_features;
/**
* The dropped file.
* @type {File}
* @api
*/
this.file = file;
/**
* The feature projection.
* @type {import("../proj/Projection.js").default|undefined}
* @api
*/
this.projection = opt_projection;
}
}
/***
* @template Return
* @typedef {import("../Observable").OnSignature<import("../Observable").EventTypes, import("../events/Event.js").default, Return> &
* import("../Observable").OnSignature<import("../ObjectEventType").Types|
* 'change:active', import("../Object").ObjectEvent, Return> &
* import("../Observable").OnSignature<'addfeatures', DragAndDropEvent, Return> &
* import("../Observable").CombinedOnSignature<import("../Observable").EventTypes|import("../ObjectEventType").Types|
* 'change:active'|'addfeatures', Return>} DragAndDropOnSignature
*/
/**
* @classdesc
* Handles input of vector data by drag and drop.
*
* Note that the DragAndDrop interaction uses the TextDecoder() constructor if the supplied
* combination of formats read both text string and ArrayBuffer sources. Older browsers such
* as IE which do not support this will need a TextDecoder polyfill to be loaded before use.
*
* @api
*
* @fires DragAndDropEvent
*/
class DragAndDrop extends Interaction {
/**
* @param {Options} [opt_options] Options.
*/
constructor(opt_options) {
const options = opt_options ? opt_options : {};
super({
handleEvent: TRUE,
});
/***
* @type {DragAndDropOnSignature<import("../events").EventsKey>}
*/
this.on;
/***
* @type {DragAndDropOnSignature<import("../events").EventsKey>}
*/
this.once;
/***
* @type {DragAndDropOnSignature<void>}
*/
this.un;
/**
* @private
* @type {boolean}
*/
this.readAsBuffer_ = false;
/**
* @private
* @type {Array<import("../format/Feature.js").default>}
*/
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';
}
/**
* @private
* @type {import("../proj/Projection.js").default}
*/
this.projection_ = options.projection
? getProjection(options.projection)
: null;
/**
* @private
* @type {?Array<import("../events.js").EventsKey>}
*/
this.dropListenKeys_ = null;
/**
* @private
* @type {import("../source/Vector.js").default}
*/
this.source_ = options.source || null;
/**
* @private
* @type {HTMLElement|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) {
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 === undefined) {
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.DROP, this.handleDrop, this),
listen(dropArea, EventType.DRAGENTER, this.handleStop, this),
listen(dropArea, EventType.DRAGOVER, this.handleStop, this),
listen(dropArea, EventType.DROP, this.handleStop, this),
];
}
}
/**
* Activate or deactivate the interaction.
* @param {boolean} active Active.
* @observable
* @api
*/
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("../PluggableMap.js").default} map Map.
*/
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 (e) {
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.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';
}
}
export default DragAndDrop;

275
node_modules/ol/src/interaction/DragBox.js generated vendored Normal file
View File

@@ -0,0 +1,275 @@
/**
* @module ol/interaction/DragBox
*/
// FIXME draw drag box
import Event from '../events/Event.js';
import PointerInteraction from './Pointer.js';
import RenderBox from '../render/Box.js';
import {mouseActionButton} from '../events/condition.js';
/**
* A function that takes a {@link module:ol/MapBrowserEvent~MapBrowserEvent} and two
* {@link module:ol/pixel~Pixel}s and returns a `{boolean}`. If the condition is met,
* true should be returned.
* @typedef {function(this: ?, import("../MapBrowserEvent.js").default, import("../pixel.js").Pixel, import("../pixel.js").Pixel):boolean} EndCondition
*/
/**
* @typedef {Object} Options
* @property {string} [className='ol-dragbox'] CSS class name for styling the box.
* @property {import("../events/condition.js").Condition} [condition] A function that takes an {@link module:ol/MapBrowserEvent~MapBrowserEvent} and returns a boolean
* to indicate whether that event should be handled.
* Default is {@link ol/events/condition~mouseActionButton}.
* @property {number} [minArea=64] The minimum area of the box in pixel, this value is used by the default
* `boxEndCondition` function.
* @property {EndCondition} [boxEndCondition] A function that takes a {@link module:ol/MapBrowserEvent~MapBrowserEvent} and two
* {@link module:ol/pixel~Pixel}s to indicate whether a `boxend` event should be fired.
* Default is `true` if the area of the box is bigger than the `minArea` option.
* @property {function(this:DragBox, import("../MapBrowserEvent.js").default):void} [onBoxEnd] Code to execute just
* before `boxend` is fired.
*/
/**
* @enum {string}
*/
const DragBoxEventType = {
/**
* Triggered upon drag box start.
* @event DragBoxEvent#boxstart
* @api
*/
BOXSTART: 'boxstart',
/**
* Triggered on drag when box is active.
* @event DragBoxEvent#boxdrag
* @api
*/
BOXDRAG: 'boxdrag',
/**
* Triggered upon drag box end.
* @event DragBoxEvent#boxend
* @api
*/
BOXEND: 'boxend',
/**
* Triggered upon drag box canceled.
* @event DragBoxEvent#boxcancel
* @api
*/
BOXCANCEL: 'boxcancel',
};
/**
* @classdesc
* Events emitted by {@link module:ol/interaction/DragBox~DragBox} instances are instances of
* this type.
*/
export class DragBoxEvent extends Event {
/**
* @param {string} type The event type.
* @param {import("../coordinate.js").Coordinate} coordinate The event coordinate.
* @param {import("../MapBrowserEvent.js").default} mapBrowserEvent Originating event.
*/
constructor(type, coordinate, mapBrowserEvent) {
super(type);
/**
* The coordinate of the drag event.
* @const
* @type {import("../coordinate.js").Coordinate}
* @api
*/
this.coordinate = coordinate;
/**
* @const
* @type {import("../MapBrowserEvent.js").default}
* @api
*/
this.mapBrowserEvent = mapBrowserEvent;
}
}
/***
* @template Return
* @typedef {import("../Observable").OnSignature<import("../Observable").EventTypes, import("../events/Event.js").default, Return> &
* import("../Observable").OnSignature<import("../ObjectEventType").Types|
* 'change:active', import("../Object").ObjectEvent, Return> &
* import("../Observable").OnSignature<'boxcancel'|'boxdrag'|'boxend'|'boxstart', DragBoxEvent, Return> &
* import("../Observable").CombinedOnSignature<import("../Observable").EventTypes|import("../ObjectEventType").Types|
* 'change:active'|'boxcancel'|'boxdrag'|'boxend', Return>} DragBoxOnSignature
*/
/**
* @classdesc
* Allows the user to draw a vector box by clicking and dragging on the map,
* normally combined with an {@link module:ol/events/condition} that limits
* it to when the shift or other key is held down. This is used, for example,
* for zooming to a specific area of the map
* (see {@link module:ol/interaction/DragZoom~DragZoom} and
* {@link module:ol/interaction/DragRotateAndZoom~DragRotateAndZoom}).
*
* @fires DragBoxEvent
* @api
*/
class DragBox extends PointerInteraction {
/**
* @param {Options} [opt_options] Options.
*/
constructor(opt_options) {
super();
/***
* @type {DragBoxOnSignature<import("../events").EventsKey>}
*/
this.on;
/***
* @type {DragBoxOnSignature<import("../events").EventsKey>}
*/
this.once;
/***
* @type {DragBoxOnSignature<void>}
*/
this.un;
const options = opt_options ? opt_options : {};
/**
* @type {import("../render/Box.js").default}
* @private
*/
this.box_ = new RenderBox(options.className || 'ol-dragbox');
/**
* @type {number}
* @private
*/
this.minArea_ = options.minArea !== undefined ? options.minArea : 64;
if (options.onBoxEnd) {
this.onBoxEnd = options.onBoxEnd;
}
/**
* @type {import("../pixel.js").Pixel}
* @private
*/
this.startPixel_ = null;
/**
* @private
* @type {import("../events/condition.js").Condition}
*/
this.condition_ = options.condition ? options.condition : mouseActionButton;
/**
* @private
* @type {EndCondition}
*/
this.boxEndCondition_ = options.boxEndCondition
? options.boxEndCondition
: this.defaultBoxEndCondition;
}
/**
* The default condition for determining whether the boxend event
* should fire.
* @param {import("../MapBrowserEvent.js").default} mapBrowserEvent The originating MapBrowserEvent
* leading to the box end.
* @param {import("../pixel.js").Pixel} startPixel The starting pixel of the box.
* @param {import("../pixel.js").Pixel} endPixel The end pixel of the box.
* @return {boolean} Whether or not the boxend condition should be fired.
*/
defaultBoxEndCondition(mapBrowserEvent, startPixel, endPixel) {
const width = endPixel[0] - startPixel[0];
const height = endPixel[1] - startPixel[1];
return width * width + height * height >= this.minArea_;
}
/**
* Returns geometry of last drawn box.
* @return {import("../geom/Polygon.js").default} Geometry.
* @api
*/
getGeometry() {
return this.box_.getGeometry();
}
/**
* Handle pointer drag events.
* @param {import("../MapBrowserEvent.js").default} mapBrowserEvent Event.
*/
handleDragEvent(mapBrowserEvent) {
this.box_.setPixels(this.startPixel_, mapBrowserEvent.pixel);
this.dispatchEvent(
new DragBoxEvent(
DragBoxEventType.BOXDRAG,
mapBrowserEvent.coordinate,
mapBrowserEvent
)
);
}
/**
* Handle pointer up events.
* @param {import("../MapBrowserEvent.js").default} mapBrowserEvent Event.
* @return {boolean} If the event was consumed.
*/
handleUpEvent(mapBrowserEvent) {
this.box_.setMap(null);
const completeBox = this.boxEndCondition_(
mapBrowserEvent,
this.startPixel_,
mapBrowserEvent.pixel
);
if (completeBox) {
this.onBoxEnd(mapBrowserEvent);
}
this.dispatchEvent(
new DragBoxEvent(
completeBox ? DragBoxEventType.BOXEND : DragBoxEventType.BOXCANCEL,
mapBrowserEvent.coordinate,
mapBrowserEvent
)
);
return false;
}
/**
* Handle pointer down events.
* @param {import("../MapBrowserEvent.js").default} mapBrowserEvent Event.
* @return {boolean} If the event was consumed.
*/
handleDownEvent(mapBrowserEvent) {
if (this.condition_(mapBrowserEvent)) {
this.startPixel_ = mapBrowserEvent.pixel;
this.box_.setMap(mapBrowserEvent.map);
this.box_.setPixels(this.startPixel_, this.startPixel_);
this.dispatchEvent(
new DragBoxEvent(
DragBoxEventType.BOXSTART,
mapBrowserEvent.coordinate,
mapBrowserEvent
)
);
return true;
} else {
return false;
}
}
/**
* Function to execute just before `onboxend` is fired
* @param {import("../MapBrowserEvent.js").default} event Event.
*/
onBoxEnd(event) {}
}
export default DragBox;

189
node_modules/ol/src/interaction/DragPan.js generated vendored Normal file
View File

@@ -0,0 +1,189 @@
/**
* @module ol/interaction/DragPan
*/
import PointerInteraction, {
centroid as centroidFromPointers,
} from './Pointer.js';
import {FALSE} from '../functions.js';
import {
all,
focusWithTabindex,
noModifierKeys,
primaryAction,
} from '../events/condition.js';
import {easeOut} from '../easing.js';
import {
rotate as rotateCoordinate,
scale as scaleCoordinate,
} from '../coordinate.js';
/**
* @typedef {Object} Options
* @property {import("../events/condition.js").Condition} [condition] A function that takes an {@link module:ol/MapBrowserEvent~MapBrowserEvent} and returns a boolean
* to indicate whether that event should be handled.
* Default is {@link module:ol/events/condition.noModifierKeys} and {@link module:ol/events/condition.primaryAction}.
* @property {boolean} [onFocusOnly=false] When the map's target has a `tabindex` attribute set,
* the interaction will only handle events when the map has the focus.
* @property {import("../Kinetic.js").default} [kinetic] Kinetic inertia to apply to the pan.
*/
/**
* @classdesc
* Allows the user to pan the map by dragging the map.
* @api
*/
class DragPan extends PointerInteraction {
/**
* @param {Options} [opt_options] Options.
*/
constructor(opt_options) {
super({
stopDown: FALSE,
});
const options = opt_options ? opt_options : {};
/**
* @private
* @type {import("../Kinetic.js").default|undefined}
*/
this.kinetic_ = options.kinetic;
/**
* @type {import("../pixel.js").Pixel}
*/
this.lastCentroid = null;
/**
* @type {number}
*/
this.lastPointersCount_;
/**
* @type {boolean}
*/
this.panning_ = false;
const condition = options.condition
? options.condition
: all(noModifierKeys, primaryAction);
/**
* @private
* @type {import("../events/condition.js").Condition}
*/
this.condition_ = options.onFocusOnly
? all(focusWithTabindex, condition)
: condition;
/**
* @private
* @type {boolean}
*/
this.noKinetic_ = false;
}
/**
* Handle pointer drag events.
* @param {import("../MapBrowserEvent.js").default} mapBrowserEvent Event.
*/
handleDragEvent(mapBrowserEvent) {
if (!this.panning_) {
this.panning_ = true;
this.getMap().getView().beginInteraction();
}
const targetPointers = this.targetPointers;
const centroid = centroidFromPointers(targetPointers);
if (targetPointers.length == this.lastPointersCount_) {
if (this.kinetic_) {
this.kinetic_.update(centroid[0], centroid[1]);
}
if (this.lastCentroid) {
const delta = [
this.lastCentroid[0] - centroid[0],
centroid[1] - this.lastCentroid[1],
];
const map = mapBrowserEvent.map;
const view = map.getView();
scaleCoordinate(delta, view.getResolution());
rotateCoordinate(delta, view.getRotation());
view.adjustCenterInternal(delta);
}
} else if (this.kinetic_) {
// reset so we don't overestimate the kinetic energy after
// after one finger down, tiny drag, second finger down
this.kinetic_.begin();
}
this.lastCentroid = centroid;
this.lastPointersCount_ = targetPointers.length;
mapBrowserEvent.originalEvent.preventDefault();
}
/**
* Handle pointer up events.
* @param {import("../MapBrowserEvent.js").default} mapBrowserEvent Event.
* @return {boolean} If the event was consumed.
*/
handleUpEvent(mapBrowserEvent) {
const map = mapBrowserEvent.map;
const view = map.getView();
if (this.targetPointers.length === 0) {
if (!this.noKinetic_ && this.kinetic_ && this.kinetic_.end()) {
const distance = this.kinetic_.getDistance();
const angle = this.kinetic_.getAngle();
const center = view.getCenterInternal();
const centerpx = map.getPixelFromCoordinateInternal(center);
const dest = map.getCoordinateFromPixelInternal([
centerpx[0] - distance * Math.cos(angle),
centerpx[1] - distance * Math.sin(angle),
]);
view.animateInternal({
center: view.getConstrainedCenter(dest),
duration: 500,
easing: easeOut,
});
}
if (this.panning_) {
this.panning_ = false;
view.endInteraction();
}
return false;
} else {
if (this.kinetic_) {
// reset so we don't overestimate the kinetic energy after
// after one finger up, tiny drag, second finger up
this.kinetic_.begin();
}
this.lastCentroid = null;
return true;
}
}
/**
* Handle pointer down events.
* @param {import("../MapBrowserEvent.js").default} mapBrowserEvent Event.
* @return {boolean} If the event was consumed.
*/
handleDownEvent(mapBrowserEvent) {
if (this.targetPointers.length > 0 && this.condition_(mapBrowserEvent)) {
const map = mapBrowserEvent.map;
const view = map.getView();
this.lastCentroid = null;
// stop any current animation
if (view.getAnimating()) {
view.cancelAnimations();
}
if (this.kinetic_) {
this.kinetic_.begin();
}
// No kinetic as soon as more than one pointer on the screen is
// detected. This is to prevent nasty pans after pinch.
this.noKinetic_ = this.targetPointers.length > 1;
return true;
} else {
return false;
}
}
}
export default DragPan;

125
node_modules/ol/src/interaction/DragRotate.js generated vendored Normal file
View File

@@ -0,0 +1,125 @@
/**
* @module ol/interaction/DragRotate
*/
import PointerInteraction from './Pointer.js';
import {FALSE} from '../functions.js';
import {
altShiftKeysOnly,
mouseActionButton,
mouseOnly,
} from '../events/condition.js';
import {disable} from '../rotationconstraint.js';
/**
* @typedef {Object} Options
* @property {import("../events/condition.js").Condition} [condition] A function that takes an
* {@link module:ol/MapBrowserEvent~MapBrowserEvent} and returns a boolean
* to indicate whether that event should be handled.
* Default is {@link module:ol/events/condition.altShiftKeysOnly}.
* @property {number} [duration=250] Animation duration in milliseconds.
*/
/**
* @classdesc
* Allows the user to rotate the map by clicking and dragging on the map,
* normally combined with an {@link module:ol/events/condition} that limits
* it to when the alt and shift keys are held down.
*
* This interaction is only supported for mouse devices.
* @api
*/
class DragRotate extends PointerInteraction {
/**
* @param {Options} [opt_options] Options.
*/
constructor(opt_options) {
const options = opt_options ? opt_options : {};
super({
stopDown: FALSE,
});
/**
* @private
* @type {import("../events/condition.js").Condition}
*/
this.condition_ = options.condition ? options.condition : altShiftKeysOnly;
/**
* @private
* @type {number|undefined}
*/
this.lastAngle_ = undefined;
/**
* @private
* @type {number}
*/
this.duration_ = options.duration !== undefined ? options.duration : 250;
}
/**
* Handle pointer drag events.
* @param {import("../MapBrowserEvent.js").default} mapBrowserEvent Event.
*/
handleDragEvent(mapBrowserEvent) {
if (!mouseOnly(mapBrowserEvent)) {
return;
}
const map = mapBrowserEvent.map;
const view = map.getView();
if (view.getConstraints().rotation === disable) {
return;
}
const size = map.getSize();
const offset = mapBrowserEvent.pixel;
const theta = Math.atan2(size[1] / 2 - offset[1], offset[0] - size[0] / 2);
if (this.lastAngle_ !== undefined) {
const delta = theta - this.lastAngle_;
view.adjustRotationInternal(-delta);
}
this.lastAngle_ = theta;
}
/**
* Handle pointer up events.
* @param {import("../MapBrowserEvent.js").default} mapBrowserEvent Event.
* @return {boolean} If the event was consumed.
*/
handleUpEvent(mapBrowserEvent) {
if (!mouseOnly(mapBrowserEvent)) {
return true;
}
const map = mapBrowserEvent.map;
const view = map.getView();
view.endInteraction(this.duration_);
return false;
}
/**
* Handle pointer down events.
* @param {import("../MapBrowserEvent.js").default} mapBrowserEvent Event.
* @return {boolean} If the event was consumed.
*/
handleDownEvent(mapBrowserEvent) {
if (!mouseOnly(mapBrowserEvent)) {
return false;
}
if (
mouseActionButton(mapBrowserEvent) &&
this.condition_(mapBrowserEvent)
) {
const map = mapBrowserEvent.map;
map.getView().beginInteraction();
this.lastAngle_ = undefined;
return true;
} else {
return false;
}
}
}
export default DragRotate;

137
node_modules/ol/src/interaction/DragRotateAndZoom.js generated vendored Normal file
View File

@@ -0,0 +1,137 @@
/**
* @module ol/interaction/DragRotateAndZoom
*/
import PointerInteraction from './Pointer.js';
import {mouseOnly, shiftKeyOnly} from '../events/condition.js';
/**
* @typedef {Object} Options
* @property {import("../events/condition.js").Condition} [condition] A function that
* takes an {@link module:ol/MapBrowserEvent~MapBrowserEvent} and returns a
* boolean to indicate whether that event should be handled.
* Default is {@link module:ol/events/condition.shiftKeyOnly}.
* @property {number} [duration=400] Animation duration in milliseconds.
*/
/**
* @classdesc
* Allows the user to zoom and rotate the map by clicking and dragging
* on the map. By default, this interaction is limited to when the shift
* key is held down.
*
* This interaction is only supported for mouse devices.
*
* And this interaction is not included in the default interactions.
* @api
*/
class DragRotateAndZoom extends PointerInteraction {
/**
* @param {Options} [opt_options] Options.
*/
constructor(opt_options) {
const options = opt_options ? opt_options : {};
super(/** @type {import("./Pointer.js").Options} */ (options));
/**
* @private
* @type {import("../events/condition.js").Condition}
*/
this.condition_ = options.condition ? options.condition : shiftKeyOnly;
/**
* @private
* @type {number|undefined}
*/
this.lastAngle_ = undefined;
/**
* @private
* @type {number|undefined}
*/
this.lastMagnitude_ = undefined;
/**
* @private
* @type {number}
*/
this.lastScaleDelta_ = 0;
/**
* @private
* @type {number}
*/
this.duration_ = options.duration !== undefined ? options.duration : 400;
}
/**
* Handle pointer drag events.
* @param {import("../MapBrowserEvent.js").default} mapBrowserEvent Event.
*/
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_ !== undefined) {
const angleDelta = this.lastAngle_ - theta;
view.adjustRotationInternal(angleDelta);
}
this.lastAngle_ = theta;
if (this.lastMagnitude_ !== undefined) {
view.adjustResolutionInternal(this.lastMagnitude_ / magnitude);
}
if (this.lastMagnitude_ !== undefined) {
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.
*/
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.
*/
handleDownEvent(mapBrowserEvent) {
if (!mouseOnly(mapBrowserEvent)) {
return false;
}
if (this.condition_(mapBrowserEvent)) {
mapBrowserEvent.map.getView().beginInteraction();
this.lastAngle_ = undefined;
this.lastMagnitude_ = undefined;
return true;
} else {
return false;
}
}
}
export default DragRotateAndZoom;

84
node_modules/ol/src/interaction/DragZoom.js generated vendored Normal file
View File

@@ -0,0 +1,84 @@
/**
* @module ol/interaction/DragZoom
*/
import DragBox from './DragBox.js';
import {easeOut} from '../easing.js';
import {shiftKeyOnly} from '../events/condition.js';
/**
* @typedef {Object} Options
* @property {string} [className='ol-dragzoom'] CSS class name for styling the
* box.
* @property {import("../events/condition.js").Condition} [condition] A function that
* takes an {@link module:ol/MapBrowserEvent~MapBrowserEvent} and returns a
* boolean to indicate whether that event should be handled.
* Default is {@link module:ol/events/condition.shiftKeyOnly}.
* @property {number} [duration=200] Animation duration in milliseconds.
* @property {boolean} [out=false] Use interaction for zooming out.
* @property {number} [minArea=64] The minimum area of the box in pixel, this value is used by the parent default
* `boxEndCondition` function.
*/
/**
* @classdesc
* Allows the user to zoom the map by clicking and dragging on the map,
* normally combined with an {@link module:ol/events/condition} that limits
* it to when a key, shift by default, is held down.
*
* To change the style of the box, use CSS and the `.ol-dragzoom` selector, or
* your custom one configured with `className`.
* @api
*/
class DragZoom extends DragBox {
/**
* @param {Options} [opt_options] Options.
*/
constructor(opt_options) {
const options = opt_options ? opt_options : {};
const condition = options.condition ? options.condition : shiftKeyOnly;
super({
condition: condition,
className: options.className || 'ol-dragzoom',
minArea: options.minArea,
});
/**
* @private
* @type {number}
*/
this.duration_ = options.duration !== undefined ? options.duration : 200;
/**
* @private
* @type {boolean}
*/
this.out_ = options.out !== undefined ? options.out : false;
}
/**
* Function to execute just before `onboxend` is fired
* @param {import("../MapBrowserEvent.js").default} event Event.
*/
onBoxEnd(event) {
const map = this.getMap();
const view = /** @type {!import("../View.js").default} */ (map.getView());
let geometry = this.getGeometry();
if (this.out_) {
const rotatedExtent = view.rotatedExtentForGeometry(geometry);
const resolution = view.getResolutionForExtentInternal(rotatedExtent);
const factor = view.getResolution() / resolution;
geometry = geometry.clone();
geometry.scale(factor * factor);
}
view.fitInternal(geometry, {
duration: this.duration_,
easing: easeOut,
});
}
}
export default DragZoom;

1294
node_modules/ol/src/interaction/Draw.js generated vendored Normal file

File diff suppressed because it is too large Load Diff

550
node_modules/ol/src/interaction/Extent.js generated vendored Normal file
View File

@@ -0,0 +1,550 @@
/**
* @module ol/interaction/Extent
*/
import Event from '../events/Event.js';
import Feature from '../Feature.js';
import MapBrowserEventType from '../MapBrowserEventType.js';
import Point from '../geom/Point.js';
import PointerInteraction from './Pointer.js';
import VectorLayer from '../layer/Vector.js';
import VectorSource from '../source/Vector.js';
import {always} from '../events/condition.js';
import {boundingExtent, getArea} from '../extent.js';
import {
closestOnSegment,
distance as coordinateDistance,
squaredDistance as squaredCoordinateDistance,
squaredDistanceToSegment,
} from '../coordinate.js';
import {createEditingStyle} from '../style/Style.js';
import {fromExtent as polygonFromExtent} from '../geom/Polygon.js';
import {toUserExtent} from '../proj.js';
/**
* @typedef {Object} Options
* @property {import("../events/condition.js").Condition} [condition] A function that
* takes an {@link module:ol/MapBrowserEvent~MapBrowserEvent} and returns a
* boolean to indicate whether that event should be handled.
* Default is {@link module:ol/events/condition.always}.
* @property {import("../extent.js").Extent} [extent] Initial extent. Defaults to no
* initial extent.
* @property {import("../style/Style.js").StyleLike} [boxStyle]
* Style for the drawn extent box. Defaults to the `Polygon` editing style
* documented in {@link module:ol/style/Style~Style}
* @property {number} [pixelTolerance=10] Pixel tolerance for considering the
* pointer close enough to a segment or vertex for editing.
* @property {import("../style/Style.js").StyleLike} [pointerStyle]
* Style for the cursor used to draw the extent. Defaults to the `Point` editing style
* documented in {@link module:ol/style/Style~Style}
* @property {boolean} [wrapX=false] Wrap the drawn extent across multiple maps
* in the X direction? Only affects visuals, not functionality.
*/
/**
* @enum {string}
*/
const ExtentEventType = {
/**
* Triggered after the extent is changed
* @event ExtentEvent#extentchanged
* @api
*/
EXTENTCHANGED: 'extentchanged',
};
/**
* @classdesc
* Events emitted by {@link module:ol/interaction/Extent~Extent} instances are
* instances of this type.
*/
export class ExtentEvent extends Event {
/**
* @param {import("../extent.js").Extent} extent the new extent
*/
constructor(extent) {
super(ExtentEventType.EXTENTCHANGED);
/**
* The current extent.
* @type {import("../extent.js").Extent}
* @api
*/
this.extent = extent;
}
}
/***
* @template Return
* @typedef {import("../Observable").OnSignature<import("../Observable").EventTypes, import("../events/Event.js").default, Return> &
* import("../Observable").OnSignature<import("../ObjectEventType").Types|
* 'change:active', import("../Object").ObjectEvent, Return> &
* import("../Observable").OnSignature<'extentchanged', ExtentEvent, Return> &
* import("../Observable").CombinedOnSignature<import("../Observable").EventTypes|import("../ObjectEventType").Types|
* 'change:active'|'extentchanged', Return>} ExtentOnSignature
*/
/**
* @classdesc
* Allows the user to draw a vector box by clicking and dragging on the map.
* Once drawn, the vector box can be modified by dragging its vertices or edges.
* This interaction is only supported for mouse devices.
*
* @fires ExtentEvent
* @api
*/
class Extent extends PointerInteraction {
/**
* @param {Options} [opt_options] Options.
*/
constructor(opt_options) {
const options = opt_options || {};
super(/** @type {import("./Pointer.js").Options} */ (options));
/***
* @type {ExtentOnSignature<import("../events").EventsKey>}
*/
this.on;
/***
* @type {ExtentOnSignature<import("../events").EventsKey>}
*/
this.once;
/***
* @type {ExtentOnSignature<void>}
*/
this.un;
/**
* Condition
* @type {import("../events/condition.js").Condition}
* @private
*/
this.condition_ = options.condition ? options.condition : always;
/**
* Extent of the drawn box
* @type {import("../extent.js").Extent}
* @private
*/
this.extent_ = null;
/**
* Handler for pointer move events
* @type {function (import("../coordinate.js").Coordinate): import("../extent.js").Extent|null}
* @private
*/
this.pointerHandler_ = null;
/**
* Pixel threshold to snap to extent
* @type {number}
* @private
*/
this.pixelTolerance_ =
options.pixelTolerance !== undefined ? options.pixelTolerance : 10;
/**
* Is the pointer snapped to an extent vertex
* @type {boolean}
* @private
*/
this.snappedToVertex_ = false;
/**
* Feature for displaying the visible extent
* @type {Feature}
* @private
*/
this.extentFeature_ = null;
/**
* Feature for displaying the visible pointer
* @type {Feature<Point>}
* @private
*/
this.vertexFeature_ = null;
if (!opt_options) {
opt_options = {};
}
/**
* Layer for the extentFeature
* @type {VectorLayer}
* @private
*/
this.extentOverlay_ = new VectorLayer({
source: new VectorSource({
useSpatialIndex: false,
wrapX: !!opt_options.wrapX,
}),
style: opt_options.boxStyle
? opt_options.boxStyle
: getDefaultExtentStyleFunction(),
updateWhileAnimating: true,
updateWhileInteracting: true,
});
/**
* Layer for the vertexFeature
* @type {VectorLayer}
* @private
*/
this.vertexOverlay_ = new VectorLayer({
source: new VectorSource({
useSpatialIndex: false,
wrapX: !!opt_options.wrapX,
}),
style: opt_options.pointerStyle
? opt_options.pointerStyle
: getDefaultPointerStyleFunction(),
updateWhileAnimating: true,
updateWhileInteracting: true,
});
if (opt_options.extent) {
this.setExtent(opt_options.extent);
}
}
/**
* @param {import("../pixel.js").Pixel} pixel cursor location
* @param {import("../PluggableMap.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) {
//convert extents to line segments and find the segment closest to pixelCoordinate
const segments = getSegments(extent);
segments.sort(sortByDistance);
const closestSegment = segments[0];
let vertex = closestOnSegment(pixelCoordinate, closestSegment);
const vertexPixel = map.getPixelFromCoordinateInternal(vertex);
//if the distance is within tolerance, snap to the segment
if (coordinateDistance(pixel, vertexPixel) <= this.pixelTolerance_) {
//test if we should further snap to a vertex
const pixel1 = map.getPixelFromCoordinateInternal(closestSegment[0]);
const pixel2 = map.getPixelFromCoordinateInternal(closestSegment[1]);
const squaredDist1 = squaredCoordinateDistance(vertexPixel, pixel1);
const squaredDist2 = squaredCoordinateDistance(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
* @private
*/
handlePointerMove_(mapBrowserEvent) {
const pixel = mapBrowserEvent.pixel;
const map = mapBrowserEvent.map;
let vertex = this.snapToVertex_(pixel, map);
if (!vertex) {
vertex = map.getCoordinateFromPixelInternal(pixel);
}
this.createOrUpdatePointerFeature_(vertex);
}
/**
* @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({});
} else {
extentFeature = new Feature(polygonFromExtent(extent));
}
this.extentFeature_ = extentFeature;
this.extentOverlay_.getSource().addFeature(extentFeature);
} else {
if (!extent) {
extentFeature.setGeometry(undefined);
} else {
extentFeature.setGeometry(polygonFromExtent(extent));
}
}
return extentFeature;
}
/**
* @param {import("../coordinate.js").Coordinate} vertex location of feature
* @return {Feature} vertex as feature
* @private
*/
createOrUpdatePointerFeature_(vertex) {
let vertexFeature = this.vertexFeature_;
if (!vertexFeature) {
vertexFeature = new Feature(new Point(vertex));
this.vertexFeature_ = vertexFeature;
this.vertexOverlay_.getSource().addFeature(vertexFeature);
} else {
const geometry = vertexFeature.getGeometry();
geometry.setCoordinates(vertex);
}
return vertexFeature;
}
/**
* @param {import("../MapBrowserEvent.js").default} mapBrowserEvent Map browser event.
* @return {boolean} `false` to stop event propagation.
*/
handleEvent(mapBrowserEvent) {
if (!mapBrowserEvent.originalEvent || !this.condition_(mapBrowserEvent)) {
return true;
}
//display pointer (if not dragging)
if (
mapBrowserEvent.type == MapBrowserEventType.POINTERMOVE &&
!this.handlingDownUpSequence
) {
this.handlePointerMove_(mapBrowserEvent);
}
//call pointer to determine up/down/drag
super.handleEvent(mapBrowserEvent);
//return false to stop propagation
return false;
}
/**
* Handle pointer down events.
* @param {import("../MapBrowserEvent.js").default} mapBrowserEvent Event.
* @return {boolean} If the event was consumed.
*/
handleDownEvent(mapBrowserEvent) {
const pixel = mapBrowserEvent.pixel;
const map = mapBrowserEvent.map;
const extent = this.getExtentInternal();
let vertex = this.snapToVertex_(pixel, map);
//find the extent corner opposite the passed corner
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;
//snap to point
if (x !== null && y !== null) {
this.pointerHandler_ = getPointHandler(getOpposingPoint(vertex));
//snap to edge
} 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])
);
}
//no snap - new bbox
} else {
vertex = map.getCoordinateFromPixelInternal(pixel);
this.setExtent([vertex[0], vertex[1], vertex[0], vertex[1]]);
this.pointerHandler_ = getPointHandler(vertex);
}
return true; //event handled; start downup sequence
}
/**
* Handle pointer drag events.
* @param {import("../MapBrowserEvent.js").default} mapBrowserEvent Event.
*/
handleDragEvent(mapBrowserEvent) {
if (this.pointerHandler_) {
const pixelCoordinate = mapBrowserEvent.coordinate;
this.setExtent(this.pointerHandler_(pixelCoordinate));
this.createOrUpdatePointerFeature_(pixelCoordinate);
}
}
/**
* Handle pointer up events.
* @param {import("../MapBrowserEvent.js").default} mapBrowserEvent Event.
* @return {boolean} If the event was consumed.
*/
handleUpEvent(mapBrowserEvent) {
this.pointerHandler_ = null;
//If bbox is zero area, set to null;
const extent = this.getExtentInternal();
if (!extent || getArea(extent) === 0) {
this.setExtent(null);
}
return false; //Stop handling downup sequence
}
/**
* 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("../PluggableMap.js").default} map Map.
*/
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
*/
getExtentInternal() {
return this.extent_;
}
/**
* Manually sets the drawn extent, using the view projection.
*
* @param {import("../extent.js").Extent} extent Extent
* @api
*/
setExtent(extent) {
//Null extent means no bbox
this.extent_ = extent ? extent : null;
this.createOrUpdateExtentFeature_(extent);
this.dispatchEvent(new ExtentEvent(this.extent_));
}
}
/**
* Returns the default style for the drawn bbox
*
* @return {import("../style/Style.js").StyleFunction} Default Extent style
*/
function getDefaultExtentStyleFunction() {
const style = createEditingStyle();
return function (feature, resolution) {
return style['Polygon'];
};
}
/**
* Returns the default style for the pointer
*
* @return {import("../style/Style.js").StyleFunction} Default pointer style
*/
function getDefaultPointerStyleFunction() {
const style = createEditingStyle();
return function (feature, resolution) {
return style['Point'];
};
}
/**
* @param {import("../coordinate.js").Coordinate} fixedPoint corner that will be unchanged in the new extent
* @return {function (import("../coordinate.js").Coordinate): import("../extent.js").Extent} event handler
*/
function getPointHandler(fixedPoint) {
return function (point) {
return boundingExtent([fixedPoint, point]);
};
}
/**
* @param {import("../coordinate.js").Coordinate} fixedP1 first corner that will be unchanged in the new extent
* @param {import("../coordinate.js").Coordinate} fixedP2 second corner that will be unchanged in the new extent
* @return {function (import("../coordinate.js").Coordinate): import("../extent.js").Extent|null} event handler
*/
function getEdgeHandler(fixedP1, fixedP2) {
if (fixedP1[0] == fixedP2[0]) {
return function (point) {
return boundingExtent([fixedP1, [point[0], fixedP2[1]]]);
};
} else if (fixedP1[1] == fixedP2[1]) {
return function (point) {
return boundingExtent([fixedP1, [fixedP2[0], point[1]]]);
};
} else {
return null;
}
}
/**
* @param {import("../extent.js").Extent} extent extent
* @return {Array<Array<import("../coordinate.js").Coordinate>>} extent line segments
*/
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]],
],
];
}
export default Extent;

170
node_modules/ol/src/interaction/Interaction.js generated vendored Normal file
View File

@@ -0,0 +1,170 @@
/**
* @module ol/interaction/Interaction
*/
import BaseObject from '../Object.js';
import InteractionProperty from './Property.js';
import {easeOut, linear} from '../easing.js';
/***
* @template Return
* @typedef {import("../Observable").OnSignature<import("../Observable").EventTypes, import("../events/Event.js").default, Return> &
* import("../Observable").OnSignature<import("../ObjectEventType").Types|
* 'change:active', import("../Object").ObjectEvent, Return> &
* import("../Observable").CombinedOnSignature<import("../Observable").EventTypes|import("../ObjectEventType").Types|
* 'change:active', Return>} InteractionOnSignature
*/
/**
* Object literal with config options for interactions.
* @typedef {Object} InteractionOptions
* @property {function(import("../MapBrowserEvent.js").default):boolean} handleEvent
* Method called by the map to notify the interaction that a browser event was
* dispatched to the map. If the function returns a falsy value, propagation of
* the event to other interactions in the map's interactions chain will be
* prevented (this includes functions with no explicit return). The interactions
* are traversed in reverse order of the interactions collection of the map.
*/
/**
* @classdesc
* Abstract base class; normally only used for creating subclasses and not
* instantiated in apps.
* User actions that change the state of the map. Some are similar to controls,
* but are not associated with a DOM element.
* For example, {@link module:ol/interaction/KeyboardZoom~KeyboardZoom} is
* functionally the same as {@link module:ol/control/Zoom~Zoom}, but triggered
* by a keyboard event not a button element event.
* Although interactions do not have a DOM element, some of them do render
* vectors and so are visible on the screen.
* @api
*/
class Interaction extends BaseObject {
/**
* @param {InteractionOptions} [opt_options] Options.
*/
constructor(opt_options) {
super();
/***
* @type {InteractionOnSignature<import("../events").EventsKey>}
*/
this.on;
/***
* @type {InteractionOnSignature<import("../events").EventsKey>}
*/
this.once;
/***
* @type {InteractionOnSignature<void>}
*/
this.un;
if (opt_options && opt_options.handleEvent) {
this.handleEvent = opt_options.handleEvent;
}
/**
* @private
* @type {import("../PluggableMap.js").default|null}
*/
this.map_ = null;
this.setActive(true);
}
/**
* Return whether the interaction is currently active.
* @return {boolean} `true` if the interaction is active, `false` otherwise.
* @observable
* @api
*/
getActive() {
return /** @type {boolean} */ (this.get(InteractionProperty.ACTIVE));
}
/**
* Get the map associated with this interaction.
* @return {import("../PluggableMap.js").default|null} Map.
* @api
*/
getMap() {
return this.map_;
}
/**
* Handles the {@link module:ol/MapBrowserEvent~MapBrowserEvent map browser event}.
* @param {import("../MapBrowserEvent.js").default} mapBrowserEvent Map browser event.
* @return {boolean} `false` to stop event propagation.
* @api
*/
handleEvent(mapBrowserEvent) {
return true;
}
/**
* Activate or deactivate the interaction.
* @param {boolean} active Active.
* @observable
* @api
*/
setActive(active) {
this.set(InteractionProperty.ACTIVE, 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("../PluggableMap.js").default|null} map Map.
*/
setMap(map) {
this.map_ = map;
}
}
/**
* @param {import("../View.js").default} view View.
* @param {import("../coordinate.js").Coordinate} delta Delta.
* @param {number} [opt_duration] Duration.
*/
export function pan(view, delta, opt_duration) {
const currentCenter = view.getCenterInternal();
if (currentCenter) {
const center = [currentCenter[0] + delta[0], currentCenter[1] + delta[1]];
view.animateInternal({
duration: opt_duration !== undefined ? opt_duration : 250,
easing: linear,
center: view.getConstrainedCenter(center),
});
}
}
/**
* @param {import("../View.js").default} view View.
* @param {number} delta Delta from previous zoom level.
* @param {import("../coordinate.js").Coordinate} [opt_anchor] Anchor coordinate in the user projection.
* @param {number} [opt_duration] Duration.
*/
export function zoomByDelta(view, delta, opt_anchor, opt_duration) {
const currentZoom = view.getZoom();
if (currentZoom === undefined) {
return;
}
const newZoom = view.getConstrainedZoom(currentZoom + delta);
const newResolution = view.getResolutionForZoom(newZoom);
if (view.getAnimating()) {
view.cancelAnimations();
}
view.animate({
resolution: newResolution,
anchor: opt_anchor,
duration: opt_duration !== undefined ? opt_duration : 250,
easing: easeOut,
});
}
export default Interaction;

125
node_modules/ol/src/interaction/KeyboardPan.js generated vendored Normal file
View File

@@ -0,0 +1,125 @@
/**
* @module ol/interaction/KeyboardPan
*/
import EventType from '../events/EventType.js';
import Interaction, {pan} from './Interaction.js';
import KeyCode from '../events/KeyCode.js';
import {noModifierKeys, targetNotEditable} from '../events/condition.js';
import {rotate as rotateCoordinate} from '../coordinate.js';
/**
* @typedef {Object} Options
* @property {import("../events/condition.js").Condition} [condition] A function that
* takes an {@link module:ol/MapBrowserEvent~MapBrowserEvent} and returns a
* boolean to indicate whether that event should be handled. Default is
* {@link module:ol/events/condition.noModifierKeys} and
* {@link module:ol/events/condition.targetNotEditable}.
* @property {number} [duration=100] Animation duration in milliseconds.
* @property {number} [pixelDelta=128] The amount of pixels to pan on each key
* press.
*/
/**
* @classdesc
* Allows the user to pan the map using keyboard arrows.
* Note that, although this interaction is by default included in maps,
* the keys can only be used when browser focus is on the element to which
* the keyboard events are attached. By default, this is the map div,
* though you can change this with the `keyboardEventTarget` in
* {@link module:ol/Map~Map}. `document` never loses focus but, for any other
* element, focus will have to be on, and returned to, this element if the keys
* are to function.
* See also {@link module:ol/interaction/KeyboardZoom~KeyboardZoom}.
* @api
*/
class KeyboardPan extends Interaction {
/**
* @param {Options} [opt_options] Options.
*/
constructor(opt_options) {
super();
const options = opt_options || {};
/**
* @private
* @param {import("../MapBrowserEvent.js").default} mapBrowserEvent Browser event.
* @return {boolean} Combined condition result.
*/
this.defaultCondition_ = function (mapBrowserEvent) {
return (
noModifierKeys(mapBrowserEvent) && targetNotEditable(mapBrowserEvent)
);
};
/**
* @private
* @type {import("../events/condition.js").Condition}
*/
this.condition_ =
options.condition !== undefined
? options.condition
: this.defaultCondition_;
/**
* @private
* @type {number}
*/
this.duration_ = options.duration !== undefined ? options.duration : 100;
/**
* @private
* @type {number}
*/
this.pixelDelta_ =
options.pixelDelta !== undefined ? options.pixelDelta : 128;
}
/**
* Handles the {@link module:ol/MapBrowserEvent~MapBrowserEvent map browser event} if it was a
* `KeyEvent`, and decides the direction to pan to (if an arrow key was
* pressed).
* @param {import("../MapBrowserEvent.js").default} mapBrowserEvent Map browser event.
* @return {boolean} `false` to stop event propagation.
* @this {KeyboardPan}
*/
handleEvent(mapBrowserEvent) {
let stopEvent = false;
if (mapBrowserEvent.type == EventType.KEYDOWN) {
const keyEvent = /** @type {KeyboardEvent} */ (
mapBrowserEvent.originalEvent
);
const keyCode = keyEvent.keyCode;
if (
this.condition_(mapBrowserEvent) &&
(keyCode == KeyCode.DOWN ||
keyCode == KeyCode.LEFT ||
keyCode == KeyCode.RIGHT ||
keyCode == KeyCode.UP)
) {
const map = mapBrowserEvent.map;
const view = map.getView();
const mapUnitsDelta = view.getResolution() * this.pixelDelta_;
let deltaX = 0,
deltaY = 0;
if (keyCode == KeyCode.DOWN) {
deltaY = -mapUnitsDelta;
} else if (keyCode == KeyCode.LEFT) {
deltaX = -mapUnitsDelta;
} else if (keyCode == KeyCode.RIGHT) {
deltaX = mapUnitsDelta;
} else {
deltaY = mapUnitsDelta;
}
const delta = [deltaX, deltaY];
rotateCoordinate(delta, view.getRotation());
pan(view, delta, this.duration_);
keyEvent.preventDefault();
stopEvent = true;
}
}
return !stopEvent;
}
}
export default KeyboardPan;

94
node_modules/ol/src/interaction/KeyboardZoom.js generated vendored Normal file
View File

@@ -0,0 +1,94 @@
/**
* @module ol/interaction/KeyboardZoom
*/
import EventType from '../events/EventType.js';
import Interaction, {zoomByDelta} from './Interaction.js';
import {targetNotEditable} from '../events/condition.js';
/**
* @typedef {Object} Options
* @property {number} [duration=100] Animation duration in milliseconds.
* @property {import("../events/condition.js").Condition} [condition] A function that
* takes an {@link module:ol/MapBrowserEvent~MapBrowserEvent} and returns a
* boolean to indicate whether that event should be handled. Default is
* {@link module:ol/events/condition.targetNotEditable}.
* @property {number} [delta=1] The zoom level delta on each key press.
*/
/**
* @classdesc
* Allows the user to zoom the map using keyboard + and -.
* Note that, although this interaction is by default included in maps,
* the keys can only be used when browser focus is on the element to which
* the keyboard events are attached. By default, this is the map div,
* though you can change this with the `keyboardEventTarget` in
* {@link module:ol/Map~Map}. `document` never loses focus but, for any other
* element, focus will have to be on, and returned to, this element if the keys
* are to function.
* See also {@link module:ol/interaction/KeyboardPan~KeyboardPan}.
* @api
*/
class KeyboardZoom extends Interaction {
/**
* @param {Options} [opt_options] Options.
*/
constructor(opt_options) {
super();
const options = opt_options ? opt_options : {};
/**
* @private
* @type {import("../events/condition.js").Condition}
*/
this.condition_ = options.condition ? options.condition : targetNotEditable;
/**
* @private
* @type {number}
*/
this.delta_ = options.delta ? options.delta : 1;
/**
* @private
* @type {number}
*/
this.duration_ = options.duration !== undefined ? options.duration : 100;
}
/**
* Handles the {@link module:ol/MapBrowserEvent~MapBrowserEvent map browser event} if it was a
* `KeyEvent`, and decides whether to zoom in or out (depending on whether the
* key pressed was '+' or '-').
* @param {import("../MapBrowserEvent.js").default} mapBrowserEvent Map browser event.
* @return {boolean} `false` to stop event propagation.
* @this {KeyboardZoom}
*/
handleEvent(mapBrowserEvent) {
let stopEvent = false;
if (
mapBrowserEvent.type == EventType.KEYDOWN ||
mapBrowserEvent.type == EventType.KEYPRESS
) {
const keyEvent = /** @type {KeyboardEvent} */ (
mapBrowserEvent.originalEvent
);
const charCode = keyEvent.charCode;
if (
this.condition_(mapBrowserEvent) &&
(charCode == '+'.charCodeAt(0) || charCode == '-'.charCodeAt(0))
) {
const map = mapBrowserEvent.map;
const delta =
charCode == '+'.charCodeAt(0) ? this.delta_ : -this.delta_;
const view = map.getView();
zoomByDelta(view, delta, undefined, this.duration_);
keyEvent.preventDefault();
stopEvent = true;
}
}
return !stopEvent;
}
}
export default KeyboardZoom;

359
node_modules/ol/src/interaction/Link.js generated vendored Normal file
View File

@@ -0,0 +1,359 @@
/**
* @module ol/interaction/Link
*/
import EventType from '../events/EventType.js';
import Interaction from './Interaction.js';
import MapEventType from '../MapEventType.js';
import {assign} from '../obj.js';
import {listen, unlistenByKey} from '../events.js';
import {toFixed} from '../math.js';
/**
* @param {number} number A number.
* @return {number} A number with at most 5 decimal places.
*/
function to5(number) {
return toFixed(number, 5);
}
/**
* @param {string} string A string.
* @return {number} A number representing the string.
*/
function readNumber(string) {
return parseFloat(string);
}
/**
* @param {number} number A number.
* @return {string} A string representing the number.
*/
function writeNumber(number) {
return to5(number).toString();
}
/**
* @param {number} a A number.
* @param {number} b A number.
* @return {boolean} The numbers are different.
*/
function differentNumber(a, b) {
if (isNaN(a)) {
return false;
}
return a !== readNumber(writeNumber(b));
}
/**
* @param {Array<number>} a An array of two numbers.
* @param {Array<number>} b An array of two numbers.
* @return {boolean} The arrays are different.
*/
function differentArray(a, b) {
return differentNumber(a[0], b[0]) || differentNumber(a[1], b[1]);
}
/**
* @typedef {Object} Options
* @property {boolean|import('../View.js').AnimationOptions} [animate=true] Animate view transitions.
* @property {boolean} [replace=false] Replace the current URL without creating the new entry in browser history.
* By default, changes in the map state result in a new entry being added to the browser history.
* @property {string} [prefix=''] By default, the URL will be updated with search parameters x, y, z, and r. To
* avoid collisions with existing search parameters that your application uses, you can supply a custom prefix for
* the ones used by this interaction (e.g. 'ol:').
*/
/**
* @classdesc
* An interaction that synchronizes the map state with the URL.
*
* @api
*/
class Link extends Interaction {
/**
* @param {Options} [opt_options] Link options.
*/
constructor(opt_options) {
super();
const options = assign(
{animate: true, replace: false, prefix: ''},
opt_options || {}
);
let animationOptions;
if (options.animate === true) {
animationOptions = {duration: 250};
} else if (!options.animate) {
animationOptions = null;
} else {
animationOptions = options.animate;
}
/**
* @type {import('../View.js').AnimationOptions|null}
* @private
*/
this.animationOptions_ = animationOptions;
/**
* @private
* @type {boolean}
*/
this.replace_ = options.replace;
/**
* @private
* @type {string}
*/
this.prefix_ = options.prefix;
/**
* @private
* @type {!Array<import("../events.js").EventsKey>}
*/
this.listenerKeys_ = [];
/**
* @private
* @type {boolean}
*/
this.initial_ = true;
this.updateState_ = this.updateState_.bind(this);
}
/**
* @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) {
params.set(this.getParamName_(name), value);
}
/**
* @private
* @param {URLSearchParams} params The search params.
* @param {string} name The unprefixed parameter name.
*/
delete_(params, name) {
params.delete(this.getParamName_(name));
}
/**
* @param {import("../PluggableMap.js").default|null} map Map.
*/
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("../PluggableMap.js").default} map Map.
* @private
*/
registerListeners_(map) {
this.listenerKeys_.push(
listen(map, MapEventType.MOVEEND, this.updateUrl_, this),
listen(map.getLayerGroup(), EventType.CHANGE, this.updateUrl_, this),
listen(map, 'change:layergroup', this.handleChangeLayerGroup_, this)
);
if (!this.replace_) {
addEventListener('popstate', this.updateState_);
}
}
/**
* @param {import("../PluggableMap.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 map = this.getMap();
if (!map) {
return;
}
const view = map.getView();
if (!view) {
return;
}
const url = new URL(window.location.href);
const params = url.searchParams;
let updateView = false;
/**
* @type {import('../View.js').AnimationOptions}
*/
const viewProperties = {};
const zoom = readNumber(this.get_(params, 'z'));
if (differentNumber(zoom, view.getZoom())) {
updateView = true;
viewProperties.zoom = zoom;
}
const rotation = readNumber(this.get_(params, 'r'));
if (differentNumber(rotation, view.getRotation())) {
updateView = true;
viewProperties.rotation = rotation;
}
const center = [
readNumber(this.get_(params, 'x')),
readNumber(this.get_(params, 'y')),
];
if (differentArray(center, view.getCenter())) {
updateView = true;
viewProperties.center = center;
}
if (updateView) {
if (!this.initial_ && this.animationOptions_) {
view.animate(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 (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);
}
}
}
}
}
/**
* @private
*/
updateUrl_() {
const map = this.getMap();
if (!map) {
return;
}
const view = map.getView();
if (!view) {
return;
}
const initial = this.initial_;
this.initial_ = false;
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(''));
if (url.href !== window.location.href) {
if (initial || this.replace_) {
window.history.replaceState(null, '', url);
} else {
window.history.pushState(null, '', url);
}
}
}
}
export default Link;

1657
node_modules/ol/src/interaction/Modify.js generated vendored Normal file

File diff suppressed because it is too large Load Diff

301
node_modules/ol/src/interaction/MouseWheelZoom.js generated vendored Normal file
View File

@@ -0,0 +1,301 @@
/**
* @module ol/interaction/MouseWheelZoom
*/
import EventType from '../events/EventType.js';
import Interaction, {zoomByDelta} from './Interaction.js';
import {DEVICE_PIXEL_RATIO, FIREFOX} from '../has.js';
import {all, always, focusWithTabindex} from '../events/condition.js';
import {clamp} from '../math.js';
/**
* @enum {string}
*/
export const Mode = {
TRACKPAD: 'trackpad',
WHEEL: 'wheel',
};
/**
* @typedef {Object} Options
* @property {import("../events/condition.js").Condition} [condition] A function that
* takes an {@link module:ol/MapBrowserEvent~MapBrowserEvent} and returns a
* boolean to indicate whether that event should be handled. Default is
* {@link module:ol/events/condition.always}.
* @property {boolean} [onFocusOnly=false] When the map's target has a `tabindex` attribute set,
* the interaction will only handle events when the map has the focus.
* @property {number} [maxDelta=1] Maximum mouse wheel delta.
* @property {number} [duration=250] Animation duration in milliseconds.
* @property {number} [timeout=80] Mouse wheel timeout duration in milliseconds.
* @property {boolean} [useAnchor=true] Enable zooming using the mouse's
* location as the anchor. When set to `false`, zooming in and out will zoom to
* the center of the screen instead of zooming on the mouse's location.
* @property {boolean} [constrainResolution=false] If true, the mouse wheel zoom
* event will always animate to the closest zoom level after an interaction;
* false means intermediary zoom levels are allowed.
*/
/**
* @classdesc
* Allows the user to zoom the map by scrolling the mouse wheel.
* @api
*/
class MouseWheelZoom extends Interaction {
/**
* @param {Options} [opt_options] Options.
*/
constructor(opt_options) {
const options = opt_options ? opt_options : {};
super(
/** @type {import("./Interaction.js").InteractionOptions} */ (options)
);
/**
* @private
* @type {number}
*/
this.totalDelta_ = 0;
/**
* @private
* @type {number}
*/
this.lastDelta_ = 0;
/**
* @private
* @type {number}
*/
this.maxDelta_ = options.maxDelta !== undefined ? options.maxDelta : 1;
/**
* @private
* @type {number}
*/
this.duration_ = options.duration !== undefined ? options.duration : 250;
/**
* @private
* @type {number}
*/
this.timeout_ = options.timeout !== undefined ? options.timeout : 80;
/**
* @private
* @type {boolean}
*/
this.useAnchor_ =
options.useAnchor !== undefined ? options.useAnchor : true;
/**
* @private
* @type {boolean}
*/
this.constrainResolution_ =
options.constrainResolution !== undefined
? options.constrainResolution
: false;
const condition = options.condition ? options.condition : always;
/**
* @private
* @type {import("../events/condition.js").Condition}
*/
this.condition_ = options.onFocusOnly
? all(focusWithTabindex, condition)
: condition;
/**
* @private
* @type {?import("../coordinate.js").Coordinate}
*/
this.lastAnchor_ = null;
/**
* @private
* @type {number|undefined}
*/
this.startTime_ = undefined;
/**
* @private
* @type {?}
*/
this.timeoutId_;
/**
* @private
* @type {Mode|undefined}
*/
this.mode_ = undefined;
/**
* Trackpad events separated by this delay will be considered separate
* interactions.
* @type {number}
*/
this.trackpadEventGap_ = 400;
/**
* @type {?}
*/
this.trackpadTimeoutId_;
/**
* The number of delta values per zoom level
* @private
* @type {number}
*/
this.deltaPerZoom_ = 300;
}
/**
* @private
*/
endInteraction_() {
this.trackpadTimeoutId_ = undefined;
const map = this.getMap();
if (!map) {
return;
}
const view = map.getView();
view.endInteraction(
undefined,
this.lastDelta_ ? (this.lastDelta_ > 0 ? 1 : -1) : 0,
this.lastAnchor_
);
}
/**
* Handles the {@link module:ol/MapBrowserEvent~MapBrowserEvent map browser event} (if it was a mousewheel-event) and eventually
* zooms the map.
* @param {import("../MapBrowserEvent.js").default} mapBrowserEvent Map browser event.
* @return {boolean} `false` to stop event propagation.
*/
handleEvent(mapBrowserEvent) {
if (!this.condition_(mapBrowserEvent)) {
return true;
}
const type = mapBrowserEvent.type;
if (type !== EventType.WHEEL) {
return true;
}
const map = mapBrowserEvent.map;
const wheelEvent = /** @type {WheelEvent} */ (
mapBrowserEvent.originalEvent
);
wheelEvent.preventDefault();
if (this.useAnchor_) {
this.lastAnchor_ = mapBrowserEvent.coordinate;
}
// Delta normalisation inspired by
// https://github.com/mapbox/mapbox-gl-js/blob/001c7b9/js/ui/handler/scroll_zoom.js
let delta;
if (mapBrowserEvent.type == EventType.WHEEL) {
delta = wheelEvent.deltaY;
if (FIREFOX && wheelEvent.deltaMode === WheelEvent.DOM_DELTA_PIXEL) {
delta /= DEVICE_PIXEL_RATIO;
}
if (wheelEvent.deltaMode === WheelEvent.DOM_DELTA_LINE) {
delta *= 40;
}
}
if (delta === 0) {
return false;
} else {
this.lastDelta_ = delta;
}
const now = Date.now();
if (this.startTime_ === undefined) {
this.startTime_ = now;
}
if (!this.mode_ || now - this.startTime_ > this.trackpadEventGap_) {
this.mode_ = Math.abs(delta) < 4 ? Mode.TRACKPAD : Mode.WHEEL;
}
const view = map.getView();
if (
this.mode_ === Mode.TRACKPAD &&
!(view.getConstrainResolution() || this.constrainResolution_)
) {
if (this.trackpadTimeoutId_) {
clearTimeout(this.trackpadTimeoutId_);
} else {
if (view.getAnimating()) {
view.cancelAnimations();
}
view.beginInteraction();
}
this.trackpadTimeoutId_ = setTimeout(
this.endInteraction_.bind(this),
this.timeout_
);
view.adjustZoom(-delta / this.deltaPerZoom_, this.lastAnchor_);
this.startTime_ = now;
return false;
}
this.totalDelta_ += delta;
const timeLeft = Math.max(this.timeout_ - (now - this.startTime_), 0);
clearTimeout(this.timeoutId_);
this.timeoutId_ = setTimeout(
this.handleWheelZoom_.bind(this, map),
timeLeft
);
return false;
}
/**
* @private
* @param {import("../PluggableMap.js").default} map Map.
*/
handleWheelZoom_(map) {
const view = map.getView();
if (view.getAnimating()) {
view.cancelAnimations();
}
let delta =
-clamp(
this.totalDelta_,
-this.maxDelta_ * this.deltaPerZoom_,
this.maxDelta_ * this.deltaPerZoom_
) / this.deltaPerZoom_;
if (view.getConstrainResolution() || this.constrainResolution_) {
// view has a zoom constraint, zoom by 1
delta = delta ? (delta > 0 ? 1 : -1) : 0;
}
zoomByDelta(view, delta, this.lastAnchor_, this.duration_);
this.mode_ = undefined;
this.totalDelta_ = 0;
this.lastAnchor_ = null;
this.startTime_ = undefined;
this.timeoutId_ = undefined;
}
/**
* Enable or disable using the mouse's location as an anchor when zooming
* @param {boolean} useAnchor true to zoom to the mouse's location, false
* to zoom to the center of the map
* @api
*/
setMouseAnchor(useAnchor) {
this.useAnchor_ = useAnchor;
if (!useAnchor) {
this.lastAnchor_ = null;
}
}
}
export default MouseWheelZoom;

163
node_modules/ol/src/interaction/PinchRotate.js generated vendored Normal file
View File

@@ -0,0 +1,163 @@
/**
* @module ol/interaction/PinchRotate
*/
import PointerInteraction, {
centroid as centroidFromPointers,
} from './Pointer.js';
import {FALSE} from '../functions.js';
import {disable} from '../rotationconstraint.js';
/**
* @typedef {Object} Options
* @property {number} [duration=250] The duration of the animation in
* milliseconds.
* @property {number} [threshold=0.3] Minimal angle in radians to start a rotation.
*/
/**
* @classdesc
* Allows the user to rotate the map by twisting with two fingers
* on a touch screen.
* @api
*/
class PinchRotate extends PointerInteraction {
/**
* @param {Options} [opt_options] Options.
*/
constructor(opt_options) {
const options = opt_options ? opt_options : {};
const pointerOptions = /** @type {import("./Pointer.js").Options} */ (
options
);
if (!pointerOptions.stopDown) {
pointerOptions.stopDown = FALSE;
}
super(pointerOptions);
/**
* @private
* @type {import("../coordinate.js").Coordinate}
*/
this.anchor_ = null;
/**
* @private
* @type {number|undefined}
*/
this.lastAngle_ = undefined;
/**
* @private
* @type {boolean}
*/
this.rotating_ = false;
/**
* @private
* @type {number}
*/
this.rotationDelta_ = 0.0;
/**
* @private
* @type {number}
*/
this.threshold_ = options.threshold !== undefined ? options.threshold : 0.3;
/**
* @private
* @type {number}
*/
this.duration_ = options.duration !== undefined ? options.duration : 250;
}
/**
* Handle pointer drag events.
* @param {import("../MapBrowserEvent.js").default} mapBrowserEvent Event.
*/
handleDragEvent(mapBrowserEvent) {
let rotationDelta = 0.0;
const touch0 = this.targetPointers[0];
const touch1 = this.targetPointers[1];
// angle between touches
const angle = Math.atan2(
touch1.clientY - touch0.clientY,
touch1.clientX - touch0.clientX
);
if (this.lastAngle_ !== undefined) {
const delta = angle - this.lastAngle_;
this.rotationDelta_ += delta;
if (!this.rotating_ && Math.abs(this.rotationDelta_) > this.threshold_) {
this.rotating_ = true;
}
rotationDelta = delta;
}
this.lastAngle_ = angle;
const map = mapBrowserEvent.map;
const view = map.getView();
if (view.getConstraints().rotation === disable) {
return;
}
// rotate anchor point.
// FIXME: should be the intersection point between the lines:
// touch0,touch1 and previousTouch0,previousTouch1
const viewportPosition = map.getViewport().getBoundingClientRect();
const centroid = centroidFromPointers(this.targetPointers);
centroid[0] -= viewportPosition.left;
centroid[1] -= viewportPosition.top;
this.anchor_ = map.getCoordinateFromPixelInternal(centroid);
// rotate
if (this.rotating_) {
map.render();
view.adjustRotationInternal(rotationDelta, this.anchor_);
}
}
/**
* Handle pointer up events.
* @param {import("../MapBrowserEvent.js").default} mapBrowserEvent Event.
* @return {boolean} If the event was consumed.
*/
handleUpEvent(mapBrowserEvent) {
if (this.targetPointers.length < 2) {
const map = mapBrowserEvent.map;
const view = map.getView();
view.endInteraction(this.duration_);
return false;
} else {
return true;
}
}
/**
* Handle pointer down events.
* @param {import("../MapBrowserEvent.js").default} mapBrowserEvent Event.
* @return {boolean} If the event was consumed.
*/
handleDownEvent(mapBrowserEvent) {
if (this.targetPointers.length >= 2) {
const map = mapBrowserEvent.map;
this.anchor_ = null;
this.lastAngle_ = undefined;
this.rotating_ = false;
this.rotationDelta_ = 0.0;
if (!this.handlingDownUpSequence) {
map.getView().beginInteraction();
}
return true;
} else {
return false;
}
}
}
export default PinchRotate;

139
node_modules/ol/src/interaction/PinchZoom.js generated vendored Normal file
View File

@@ -0,0 +1,139 @@
/**
* @module ol/interaction/PinchZoom
*/
import PointerInteraction, {
centroid as centroidFromPointers,
} from './Pointer.js';
import {FALSE} from '../functions.js';
/**
* @typedef {Object} Options
* @property {number} [duration=400] Animation duration in milliseconds.
*/
/**
* @classdesc
* Allows the user to zoom the map by pinching with two fingers
* on a touch screen.
* @api
*/
class PinchZoom extends PointerInteraction {
/**
* @param {Options} [opt_options] Options.
*/
constructor(opt_options) {
const options = opt_options ? opt_options : {};
const pointerOptions = /** @type {import("./Pointer.js").Options} */ (
options
);
if (!pointerOptions.stopDown) {
pointerOptions.stopDown = FALSE;
}
super(pointerOptions);
/**
* @private
* @type {import("../coordinate.js").Coordinate}
*/
this.anchor_ = null;
/**
* @private
* @type {number}
*/
this.duration_ = options.duration !== undefined ? options.duration : 400;
/**
* @private
* @type {number|undefined}
*/
this.lastDistance_ = undefined;
/**
* @private
* @type {number}
*/
this.lastScaleDelta_ = 1;
}
/**
* Handle pointer drag events.
* @param {import("../MapBrowserEvent.js").default} mapBrowserEvent Event.
*/
handleDragEvent(mapBrowserEvent) {
let scaleDelta = 1.0;
const touch0 = this.targetPointers[0];
const touch1 = this.targetPointers[1];
const dx = touch0.clientX - touch1.clientX;
const dy = touch0.clientY - touch1.clientY;
// distance between touches
const distance = Math.sqrt(dx * dx + dy * dy);
if (this.lastDistance_ !== undefined) {
scaleDelta = this.lastDistance_ / distance;
}
this.lastDistance_ = distance;
const map = mapBrowserEvent.map;
const view = map.getView();
if (scaleDelta != 1.0) {
this.lastScaleDelta_ = scaleDelta;
}
// scale anchor point.
const viewportPosition = map.getViewport().getBoundingClientRect();
const centroid = centroidFromPointers(this.targetPointers);
centroid[0] -= viewportPosition.left;
centroid[1] -= viewportPosition.top;
this.anchor_ = map.getCoordinateFromPixelInternal(centroid);
// scale, bypass the resolution constraint
map.render();
view.adjustResolutionInternal(scaleDelta, this.anchor_);
}
/**
* Handle pointer up events.
* @param {import("../MapBrowserEvent.js").default} mapBrowserEvent Event.
* @return {boolean} If the event was consumed.
*/
handleUpEvent(mapBrowserEvent) {
if (this.targetPointers.length < 2) {
const map = mapBrowserEvent.map;
const view = map.getView();
const direction = this.lastScaleDelta_ > 1 ? 1 : -1;
view.endInteraction(this.duration_, direction);
return false;
} else {
return true;
}
}
/**
* Handle pointer down events.
* @param {import("../MapBrowserEvent.js").default} mapBrowserEvent Event.
* @return {boolean} If the event was consumed.
*/
handleDownEvent(mapBrowserEvent) {
if (this.targetPointers.length >= 2) {
const map = mapBrowserEvent.map;
this.anchor_ = null;
this.lastDistance_ = undefined;
this.lastScaleDelta_ = 1;
if (!this.handlingDownUpSequence) {
map.getView().beginInteraction();
}
return true;
} else {
return false;
}
}
}
export default PinchZoom;

206
node_modules/ol/src/interaction/Pointer.js generated vendored Normal file
View File

@@ -0,0 +1,206 @@
/**
* @module ol/interaction/Pointer
*/
import Interaction from './Interaction.js';
import MapBrowserEventType from '../MapBrowserEventType.js';
/**
* @typedef {Object} Options
* @property {function(import("../MapBrowserEvent.js").default):boolean} [handleDownEvent]
* Function handling "down" events. If the function returns `true` then a drag
* sequence is started.
* @property {function(import("../MapBrowserEvent.js").default):void} [handleDragEvent]
* Function handling "drag" events. This function is called on "move" events
* during a drag sequence.
* @property {function(import("../MapBrowserEvent.js").default):boolean} [handleEvent]
* Method called by the map to notify the interaction that a browser event was
* dispatched to the map. The function may return `false` to prevent the
* propagation of the event to other interactions in the map's interactions
* chain.
* @property {function(import("../MapBrowserEvent.js").default):void} [handleMoveEvent]
* Function handling "move" events. This function is called on "move" events.
* This functions is also called during a drag sequence, so during a drag
* sequence both the `handleDragEvent` function and this function are called.
* If `handleDownEvent` is defined and it returns true this function will not
* be called during a drag sequence.
* @property {function(import("../MapBrowserEvent.js").default):boolean} [handleUpEvent]
* Function handling "up" events. If the function returns `false` then the
* current drag sequence is stopped.
* @property {function(boolean):boolean} [stopDown]
* Should the down event be propagated to other interactions, or should be
* stopped?
*/
/**
* @classdesc
* Base class that calls user-defined functions on `down`, `move` and `up`
* events. This class also manages "drag sequences".
*
* When the `handleDownEvent` user function returns `true` a drag sequence is
* started. During a drag sequence the `handleDragEvent` user function is
* called on `move` events. The drag sequence ends when the `handleUpEvent`
* user function is called and returns `false`.
* @api
*/
class PointerInteraction extends Interaction {
/**
* @param {Options} [opt_options] Options.
*/
constructor(opt_options) {
const options = opt_options ? opt_options : {};
super(
/** @type {import("./Interaction.js").InteractionOptions} */ (options)
);
if (options.handleDownEvent) {
this.handleDownEvent = options.handleDownEvent;
}
if (options.handleDragEvent) {
this.handleDragEvent = options.handleDragEvent;
}
if (options.handleMoveEvent) {
this.handleMoveEvent = options.handleMoveEvent;
}
if (options.handleUpEvent) {
this.handleUpEvent = options.handleUpEvent;
}
if (options.stopDown) {
this.stopDown = options.stopDown;
}
/**
* @type {boolean}
* @protected
*/
this.handlingDownUpSequence = false;
/**
* @type {Array<PointerEvent>}
* @protected
*/
this.targetPointers = [];
}
/**
* Returns the current number of pointers involved in the interaction,
* e.g. `2` when two fingers are used.
* @return {number} The number of pointers.
* @api
*/
getPointerCount() {
return this.targetPointers.length;
}
/**
* Handle pointer down events.
* @param {import("../MapBrowserEvent.js").default} mapBrowserEvent Event.
* @return {boolean} If the event was consumed.
* @protected
*/
handleDownEvent(mapBrowserEvent) {
return false;
}
/**
* Handle pointer drag events.
* @param {import("../MapBrowserEvent.js").default} mapBrowserEvent Event.
* @protected
*/
handleDragEvent(mapBrowserEvent) {}
/**
* 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} mapBrowserEvent Map browser event.
* @return {boolean} `false` to stop event propagation.
* @api
*/
handleEvent(mapBrowserEvent) {
if (!mapBrowserEvent.originalEvent) {
return true;
}
let stopEvent = false;
this.updateTrackedPointers_(mapBrowserEvent);
if (this.handlingDownUpSequence) {
if (mapBrowserEvent.type == MapBrowserEventType.POINTERDRAG) {
this.handleDragEvent(mapBrowserEvent);
// prevent page scrolling during dragging
mapBrowserEvent.originalEvent.preventDefault();
} else if (mapBrowserEvent.type == MapBrowserEventType.POINTERUP) {
const handledUp = this.handleUpEvent(mapBrowserEvent);
this.handlingDownUpSequence =
handledUp && this.targetPointers.length > 0;
}
} else {
if (mapBrowserEvent.type == MapBrowserEventType.POINTERDOWN) {
const handled = this.handleDownEvent(mapBrowserEvent);
this.handlingDownUpSequence = handled;
stopEvent = this.stopDown(handled);
} else if (mapBrowserEvent.type == MapBrowserEventType.POINTERMOVE) {
this.handleMoveEvent(mapBrowserEvent);
}
}
return !stopEvent;
}
/**
* Handle pointer move events.
* @param {import("../MapBrowserEvent.js").default} mapBrowserEvent Event.
* @protected
*/
handleMoveEvent(mapBrowserEvent) {}
/**
* Handle pointer up events.
* @param {import("../MapBrowserEvent.js").default} mapBrowserEvent Event.
* @return {boolean} If the event was consumed.
* @protected
*/
handleUpEvent(mapBrowserEvent) {
return false;
}
/**
* 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} mapBrowserEvent Event.
* @private
*/
updateTrackedPointers_(mapBrowserEvent) {
if (mapBrowserEvent.activePointers) {
this.targetPointers = mapBrowserEvent.activePointers;
}
}
}
/**
* @param {Array<PointerEvent>} pointerEvents List of events.
* @return {import("../pixel.js").Pixel} Centroid pixel.
*/
export function centroid(pointerEvents) {
const length = pointerEvents.length;
let clientX = 0;
let clientY = 0;
for (let i = 0; i < length; i++) {
clientX += pointerEvents[i].clientX;
clientY += pointerEvents[i].clientY;
}
return [clientX / length, clientY / length];
}
export default PointerInteraction;

10
node_modules/ol/src/interaction/Property.js generated vendored Normal file
View File

@@ -0,0 +1,10 @@
/**
* @module ol/interaction/Property
*/
/**
* @enum {string}
*/
export default {
ACTIVE: 'active',
};

582
node_modules/ol/src/interaction/Select.js generated vendored Normal file
View File

@@ -0,0 +1,582 @@
/**
* @module ol/interaction/Select
*/
import Collection from '../Collection.js';
import CollectionEventType from '../CollectionEventType.js';
import Event from '../events/Event.js';
import Interaction from './Interaction.js';
import VectorLayer from '../layer/Vector.js';
import {TRUE} from '../functions.js';
import {clear} from '../obj.js';
import {createEditingStyle} from '../style/Style.js';
import {extend, includes} from '../array.js';
import {getUid} from '../util.js';
import {never, shiftKeyOnly, singleClick} from '../events/condition.js';
/**
* @enum {string}
*/
const SelectEventType = {
/**
* Triggered when feature(s) has been (de)selected.
* @event SelectEvent#select
* @api
*/
SELECT: 'select',
};
/**
* A function that takes an {@link module:ol/Feature~Feature} or
* {@link module:ol/render/Feature~RenderFeature} and an
* {@link module:ol/layer/Layer~Layer} and returns `true` if the feature may be
* selected or `false` otherwise.
* @typedef {function(import("../Feature.js").FeatureLike, import("../layer/Layer.js").default<import("../source/Source").default>):boolean} FilterFunction
*/
/**
* @typedef {Object} Options
* @property {import("../events/condition.js").Condition} [addCondition] A function
* that takes an {@link module:ol/MapBrowserEvent~MapBrowserEvent} and returns a
* boolean to indicate whether that event should be handled.
* By default, this is {@link module:ol/events/condition.never}. Use this if you
* want to use different events for add and remove instead of `toggle`.
* @property {import("../events/condition.js").Condition} [condition] A function that
* takes an {@link module:ol/MapBrowserEvent~MapBrowserEvent} and returns a
* boolean to indicate whether that event should be handled. This is the event
* for the selected features as a whole. By default, this is
* {@link module:ol/events/condition.singleClick}. Clicking on a feature selects that
* feature and removes any that were in the selection. Clicking outside any
* feature removes all from the selection.
* See `toggle`, `add`, `remove` options for adding/removing extra features to/
* from the selection.
* @property {Array<import("../layer/Layer.js").default>|function(import("../layer/Layer.js").default<import("../source/Source").default>): boolean} [layers]
* A list of layers from which features should be selected. Alternatively, a
* filter function can be provided. The function will be called for each layer
* in the map and should return `true` for layers that you want to be
* selectable. If the option is absent, all visible layers will be considered
* selectable.
* @property {import("../style/Style.js").StyleLike|null} [style]
* Style for the selected features. By default the default edit style is used
* (see {@link module:ol/style/Style~Style}). Set to `null` if this interaction should not apply
* any style changes for selected features.
* If set to a falsey value, the selected feature's style will not change.
* @property {import("../events/condition.js").Condition} [removeCondition] A function
* that takes an {@link module:ol/MapBrowserEvent~MapBrowserEvent} and returns a
* boolean to indicate whether that event should be handled.
* By default, this is {@link module:ol/events/condition.never}. Use this if you
* want to use different events for add and remove instead of `toggle`.
* @property {import("../events/condition.js").Condition} [toggleCondition] A function
* that takes an {@link module:ol/MapBrowserEvent~MapBrowserEvent} and returns a
* boolean to indicate whether that event should be handled. This is in addition
* to the `condition` event. By default,
* {@link module:ol/events/condition.shiftKeyOnly}, i.e. pressing `shift` as
* well as the `condition` event, adds that feature to the current selection if
* it is not currently selected, and removes it if it is. See `add` and `remove`
* if you want to use different events instead of a toggle.
* @property {boolean} [multi=false] A boolean that determines if the default
* behaviour should select only single features or all (overlapping) features at
* the clicked map position. The default of `false` means single select.
* @property {import("../Collection.js").default<import("../Feature.js").default>} [features]
* Collection where the interaction will place selected features. Optional. If
* not set the interaction will create a collection. In any case the collection
* used by the interaction is returned by
* {@link module:ol/interaction/Select~Select#getFeatures}.
* @property {FilterFunction} [filter] A function
* that takes an {@link module:ol/Feature~Feature} and an
* {@link module:ol/layer/Layer~Layer} and returns `true` if the feature may be
* selected or `false` otherwise.
* @property {number} [hitTolerance=0] Hit-detection tolerance. Pixels inside
* the radius around the given position will be checked for features.
*/
/**
* @classdesc
* Events emitted by {@link module:ol/interaction/Select~Select} instances are instances of
* this type.
*/
export class SelectEvent extends Event {
/**
* @param {SelectEventType} type The event type.
* @param {Array<import("../Feature.js").default>} selected Selected features.
* @param {Array<import("../Feature.js").default>} deselected Deselected features.
* @param {import("../MapBrowserEvent.js").default} mapBrowserEvent Associated
* {@link module:ol/MapBrowserEvent~MapBrowserEvent}.
*/
constructor(type, selected, deselected, mapBrowserEvent) {
super(type);
/**
* Selected features array.
* @type {Array<import("../Feature.js").default>}
* @api
*/
this.selected = selected;
/**
* Deselected features array.
* @type {Array<import("../Feature.js").default>}
* @api
*/
this.deselected = deselected;
/**
* Associated {@link module:ol/MapBrowserEvent~MapBrowserEvent}.
* @type {import("../MapBrowserEvent.js").default}
* @api
*/
this.mapBrowserEvent = mapBrowserEvent;
}
}
/**
* Original feature styles to reset to when features are no longer selected.
* @type {Object<number, import("../style/Style.js").default|Array<import("../style/Style.js").default>|import("../style/Style.js").StyleFunction>}
*/
const originalFeatureStyles = {};
/***
* @template Return
* @typedef {import("../Observable").OnSignature<import("../Observable").EventTypes, import("../events/Event.js").default, Return> &
* import("../Observable").OnSignature<import("../ObjectEventType").Types|
* 'change:active', import("../Object").ObjectEvent, Return> &
* import("../Observable").OnSignature<'select', SelectEvent, Return> &
* import("../Observable").CombinedOnSignature<import("../Observable").EventTypes|import("../ObjectEventType").Types|
* 'change:active'|'select', Return>} SelectOnSignature
*/
/**
* @classdesc
* Interaction for selecting vector features. By default, selected features are
* styled differently, so this interaction can be used for visual highlighting,
* as well as selecting features for other actions, such as modification or
* output. There are three ways of controlling which features are selected:
* using the browser event as defined by the `condition` and optionally the
* `toggle`, `add`/`remove`, and `multi` options; a `layers` filter; and a
* further feature filter using the `filter` option.
*
* @fires SelectEvent
* @api
*/
class Select extends Interaction {
/**
* @param {Options} [opt_options] Options.
*/
constructor(opt_options) {
super();
/***
* @type {SelectOnSignature<import("../events").EventsKey>}
*/
this.on;
/***
* @type {SelectOnSignature<import("../events").EventsKey>}
*/
this.once;
/***
* @type {SelectOnSignature<void>}
*/
this.un;
const options = opt_options ? opt_options : {};
/**
* @private
*/
this.boundAddFeature_ = this.addFeature_.bind(this);
/**
* @private
*/
this.boundRemoveFeature_ = this.removeFeature_.bind(this);
/**
* @private
* @type {import("../events/condition.js").Condition}
*/
this.condition_ = options.condition ? options.condition : singleClick;
/**
* @private
* @type {import("../events/condition.js").Condition}
*/
this.addCondition_ = options.addCondition ? options.addCondition : never;
/**
* @private
* @type {import("../events/condition.js").Condition}
*/
this.removeCondition_ = options.removeCondition
? options.removeCondition
: never;
/**
* @private
* @type {import("../events/condition.js").Condition}
*/
this.toggleCondition_ = options.toggleCondition
? options.toggleCondition
: shiftKeyOnly;
/**
* @private
* @type {boolean}
*/
this.multi_ = options.multi ? options.multi : false;
/**
* @private
* @type {FilterFunction}
*/
this.filter_ = options.filter ? options.filter : TRUE;
/**
* @private
* @type {number}
*/
this.hitTolerance_ = options.hitTolerance ? options.hitTolerance : 0;
/**
* @private
* @type {import("../style/Style.js").default|Array<import("../style/Style.js").default>|import("../style/Style.js").StyleFunction|null}
*/
this.style_ =
options.style !== undefined ? options.style : getDefaultStyleFunction();
/**
* @private
* @type {import("../Collection.js").default}
*/
this.features_ = options.features || new Collection();
/** @type {function(import("../layer/Layer.js").default<import("../source/Source").default>): boolean} */
let layerFilter;
if (options.layers) {
if (typeof options.layers === 'function') {
layerFilter = options.layers;
} else {
const layers = options.layers;
layerFilter = function (layer) {
return includes(layers, layer);
};
}
} else {
layerFilter = TRUE;
}
/**
* @private
* @type {function(import("../layer/Layer.js").default<import("../source/Source").default>): boolean}
*/
this.layerFilter_ = layerFilter;
/**
* An association between selected feature (key)
* and layer (value)
* @private
* @type {Object<string, import("../layer/Layer.js").default>}
*/
this.featureLayerAssociation_ = {};
}
/**
* @param {import("../Feature.js").FeatureLike} feature Feature.
* @param {import("../layer/Layer.js").default} layer Layer.
* @private
*/
addFeatureLayerAssociation_(feature, layer) {
this.featureLayerAssociation_[getUid(feature)] = layer;
}
/**
* Get the selected features.
* @return {import("../Collection.js").default<import("../Feature.js").default>} Features collection.
* @api
*/
getFeatures() {
return this.features_;
}
/**
* Returns the Hit-detection tolerance.
* @return {number} Hit tolerance in pixels.
* @api
*/
getHitTolerance() {
return this.hitTolerance_;
}
/**
* Returns the associated {@link module:ol/layer/Vector~VectorLayer vector layer} of
* a selected feature.
* @param {import("../Feature.js").FeatureLike} feature Feature
* @return {import('../layer/Vector.js').default} Layer.
* @api
*/
getLayer(feature) {
return /** @type {import('../layer/Vector.js').default} */ (
this.featureLayerAssociation_[getUid(feature)]
);
}
/**
* 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, if any, and attach it to a new
* map, if any. Pass `null` to just remove the interaction from the current map.
* @param {import("../PluggableMap.js").default|null} map Map.
* @api
*/
setMap(map) {
const currentMap = this.getMap();
if (currentMap && this.style_) {
this.features_.forEach(this.restorePreviousStyle_.bind(this));
}
super.setMap(map);
if (map) {
this.features_.addEventListener(
CollectionEventType.ADD,
this.boundAddFeature_
);
this.features_.addEventListener(
CollectionEventType.REMOVE,
this.boundRemoveFeature_
);
if (this.style_) {
this.features_.forEach(this.applySelectedStyle_.bind(this));
}
} else {
this.features_.removeEventListener(
CollectionEventType.ADD,
this.boundAddFeature_
);
this.features_.removeEventListener(
CollectionEventType.REMOVE,
this.boundRemoveFeature_
);
}
}
/**
* @param {import("../Collection.js").CollectionEvent} evt Event.
* @private
*/
addFeature_(evt) {
const feature = evt.element;
if (this.style_) {
this.applySelectedStyle_(feature);
}
if (!this.getLayer(feature)) {
const layer = /** @type {VectorLayer} */ (
this.getMap()
.getAllLayers()
.find(function (layer) {
if (
layer instanceof VectorLayer &&
layer.getSource() &&
layer.getSource().hasFeature(feature)
) {
return layer;
}
})
);
if (layer) {
this.addFeatureLayerAssociation_(feature, layer);
}
}
}
/**
* @param {import("../Collection.js").CollectionEvent} evt Event.
* @private
*/
removeFeature_(evt) {
const feature = evt.element;
if (this.style_) {
this.restorePreviousStyle_(feature);
}
}
/**
* @return {import("../style/Style.js").StyleLike|null} Select style.
*/
getStyle() {
return this.style_;
}
/**
* @param {import("../Feature.js").default} feature Feature
* @private
*/
applySelectedStyle_(feature) {
const key = getUid(feature);
if (!(key in originalFeatureStyles)) {
originalFeatureStyles[key] = feature.getStyle();
}
feature.setStyle(this.style_);
}
/**
* @param {import("../Feature.js").default} feature Feature
* @private
*/
restorePreviousStyle_(feature) {
const interactions = this.getMap().getInteractions().getArray();
for (let i = interactions.length - 1; i >= 0; --i) {
const interaction = interactions[i];
if (
interaction !== this &&
interaction instanceof Select &&
interaction.getStyle() &&
interaction.getFeatures().getArray().lastIndexOf(feature) !== -1
) {
feature.setStyle(interaction.getStyle());
return;
}
}
const key = getUid(feature);
feature.setStyle(originalFeatureStyles[key]);
delete originalFeatureStyles[key];
}
/**
* @param {import("../Feature.js").FeatureLike} feature Feature.
* @private
*/
removeFeatureLayerAssociation_(feature) {
delete this.featureLayerAssociation_[getUid(feature)];
}
/**
* Handles the {@link module:ol/MapBrowserEvent~MapBrowserEvent map browser event} and may change the
* selected state of features.
* @param {import("../MapBrowserEvent.js").default} mapBrowserEvent Map browser event.
* @return {boolean} `false` to stop event propagation.
* @this {Select}
*/
handleEvent(mapBrowserEvent) {
if (!this.condition_(mapBrowserEvent)) {
return true;
}
const add = this.addCondition_(mapBrowserEvent);
const remove = this.removeCondition_(mapBrowserEvent);
const toggle = this.toggleCondition_(mapBrowserEvent);
const set = !add && !remove && !toggle;
const map = mapBrowserEvent.map;
const features = this.getFeatures();
const deselected = [];
const selected = [];
if (set) {
// Replace the currently selected feature(s) with the feature(s) at the
// pixel, or clear the selected feature(s) if there is no feature at
// the pixel.
clear(this.featureLayerAssociation_);
map.forEachFeatureAtPixel(
mapBrowserEvent.pixel,
/**
* @param {import("../Feature.js").FeatureLike} feature Feature.
* @param {import("../layer/Layer.js").default} layer Layer.
* @return {boolean|undefined} Continue to iterate over the features.
*/
function (feature, layer) {
if (this.filter_(feature, layer)) {
this.addFeatureLayerAssociation_(feature, layer);
selected.push(feature);
return !this.multi_;
}
}.bind(this),
{
layerFilter: this.layerFilter_,
hitTolerance: this.hitTolerance_,
}
);
for (let i = features.getLength() - 1; i >= 0; --i) {
const feature = features.item(i);
const index = selected.indexOf(feature);
if (index > -1) {
// feature is already selected
selected.splice(index, 1);
} else {
features.remove(feature);
deselected.push(feature);
}
}
if (selected.length !== 0) {
features.extend(selected);
}
} else {
// Modify the currently selected feature(s).
map.forEachFeatureAtPixel(
mapBrowserEvent.pixel,
/**
* @param {import("../Feature.js").FeatureLike} feature Feature.
* @param {import("../layer/Layer.js").default} layer Layer.
* @return {boolean|undefined} Continue to iterate over the features.
*/
function (feature, layer) {
if (this.filter_(feature, layer)) {
if ((add || toggle) && !includes(features.getArray(), feature)) {
this.addFeatureLayerAssociation_(feature, layer);
selected.push(feature);
} else if (
(remove || toggle) &&
includes(features.getArray(), feature)
) {
deselected.push(feature);
this.removeFeatureLayerAssociation_(feature);
}
return !this.multi_;
}
}.bind(this),
{
layerFilter: this.layerFilter_,
hitTolerance: this.hitTolerance_,
}
);
for (let j = deselected.length - 1; j >= 0; --j) {
features.remove(deselected[j]);
}
features.extend(selected);
}
if (selected.length > 0 || deselected.length > 0) {
this.dispatchEvent(
new SelectEvent(
SelectEventType.SELECT,
selected,
deselected,
mapBrowserEvent
)
);
}
return true;
}
}
/**
* @return {import("../style/Style.js").StyleFunction} Styles.
*/
function getDefaultStyleFunction() {
const styles = createEditingStyle();
extend(styles['Polygon'], styles['LineString']);
extend(styles['GeometryCollection'], styles['LineString']);
return function (feature) {
if (!feature.getGeometry()) {
return null;
}
return styles[feature.getGeometry().getType()];
};
}
export default Select;

666
node_modules/ol/src/interaction/Snap.js generated vendored Normal file
View File

@@ -0,0 +1,666 @@
/**
* @module ol/interaction/Snap
*/
import CollectionEventType from '../CollectionEventType.js';
import EventType from '../events/EventType.js';
import PointerInteraction from './Pointer.js';
import RBush from '../structs/RBush.js';
import VectorEventType from '../source/VectorEventType.js';
import {FALSE, TRUE} from '../functions.js';
import {boundingExtent, createEmpty} from '../extent.js';
import {
closestOnCircle,
closestOnSegment,
squaredDistance,
} from '../coordinate.js';
import {fromCircle} from '../geom/Polygon.js';
import {
fromUserCoordinate,
getUserProjection,
toUserCoordinate,
} from '../proj.js';
import {getUid} from '../util.js';
import {getValues} from '../obj.js';
import {listen, unlistenByKey} from '../events.js';
/**
* @typedef {Object} Result
* @property {import("../coordinate.js").Coordinate|null} vertex Vertex.
* @property {import("../pixel.js").Pixel|null} vertexPixel VertexPixel.
*/
/**
* @typedef {Object} SegmentData
* @property {import("../Feature.js").default} feature Feature.
* @property {Array<import("../coordinate.js").Coordinate>} segment Segment.
*/
/**
* @typedef {Object} Options
* @property {import("../Collection.js").default<import("../Feature.js").default>} [features] Snap to these features. Either this option or source should be provided.
* @property {boolean} [edge=true] Snap to edges.
* @property {boolean} [vertex=true] Snap to vertices.
* @property {number} [pixelTolerance=10] Pixel tolerance for considering the pointer close enough to a segment or
* vertex for snapping.
* @property {import("../source/Vector.js").default} [source] Snap to features from this source. Either this option or features should be provided
*/
/**
* @param {import("../source/Vector.js").VectorSourceEvent|import("../Collection.js").CollectionEvent} evt Event.
* @return {import("../Feature.js").default} Feature.
*/
function getFeatureFromEvent(evt) {
if (
/** @type {import("../source/Vector.js").VectorSourceEvent} */ (evt).feature
) {
return /** @type {import("../source/Vector.js").VectorSourceEvent} */ (evt)
.feature;
} else if (
/** @type {import("../Collection.js").CollectionEvent} */ (evt).element
) {
return /** @type {import("../Feature.js").default} */ (
/** @type {import("../Collection.js").CollectionEvent} */ (evt).element
);
}
}
const tempSegment = [];
/**
* @classdesc
* Handles snapping of vector features while modifying or drawing them. The
* features can come from a {@link module:ol/source/Vector~VectorSource} or {@link module:ol/Collection~Collection}
* Any interaction object that allows the user to interact
* with the features using the mouse can benefit from the snapping, as long
* as it is added before.
*
* The snap interaction modifies map browser event `coordinate` and `pixel`
* properties to force the snap to occur to any interaction that them.
*
* Example:
*
* import Snap from 'ol/interaction/Snap';
*
* const snap = new Snap({
* source: source
* });
*
* map.addInteraction(snap);
*
* @api
*/
class Snap extends PointerInteraction {
/**
* @param {Options} [opt_options] Options.
*/
constructor(opt_options) {
const options = opt_options ? opt_options : {};
const pointerOptions = /** @type {import("./Pointer.js").Options} */ (
options
);
if (!pointerOptions.handleDownEvent) {
pointerOptions.handleDownEvent = TRUE;
}
if (!pointerOptions.stopDown) {
pointerOptions.stopDown = FALSE;
}
super(pointerOptions);
/**
* @type {import("../source/Vector.js").default|null}
* @private
*/
this.source_ = options.source ? options.source : null;
/**
* @private
* @type {boolean}
*/
this.vertex_ = options.vertex !== undefined ? options.vertex : true;
/**
* @private
* @type {boolean}
*/
this.edge_ = options.edge !== undefined ? options.edge : true;
/**
* @type {import("../Collection.js").default<import("../Feature.js").default>|null}
* @private
*/
this.features_ = options.features ? options.features : null;
/**
* @type {Array<import("../events.js").EventsKey>}
* @private
*/
this.featuresListenerKeys_ = [];
/**
* @type {Object<string, import("../events.js").EventsKey>}
* @private
*/
this.featureChangeListenerKeys_ = {};
/**
* Extents are preserved so indexed segment can be quickly removed
* when its feature geometry changes
* @type {Object<string, import("../extent.js").Extent>}
* @private
*/
this.indexedFeaturesExtents_ = {};
/**
* If a feature geometry changes while a pointer drag|move event occurs, the
* feature doesn't get updated right away. It will be at the next 'pointerup'
* event fired.
* @type {!Object<string, import("../Feature.js").default>}
* @private
*/
this.pendingFeatures_ = {};
/**
* @type {number}
* @private
*/
this.pixelTolerance_ =
options.pixelTolerance !== undefined ? options.pixelTolerance : 10;
/**
* Segment RTree for each layer
* @type {import("../structs/RBush.js").default<SegmentData>}
* @private
*/
this.rBush_ = new RBush();
/**
* @const
* @private
* @type {Object<string, function(Array<Array<import('../coordinate.js').Coordinate>>, import("../geom/Geometry.js").default): void>}
*/
this.GEOMETRY_SEGMENTERS_ = {
'Point': this.segmentPointGeometry_.bind(this),
'LineString': this.segmentLineStringGeometry_.bind(this),
'LinearRing': this.segmentLineStringGeometry_.bind(this),
'Polygon': this.segmentPolygonGeometry_.bind(this),
'MultiPoint': this.segmentMultiPointGeometry_.bind(this),
'MultiLineString': this.segmentMultiLineStringGeometry_.bind(this),
'MultiPolygon': this.segmentMultiPolygonGeometry_.bind(this),
'GeometryCollection': this.segmentGeometryCollectionGeometry_.bind(this),
'Circle': this.segmentCircleGeometry_.bind(this),
};
}
/**
* Add a feature to the collection of features that we may snap to.
* @param {import("../Feature.js").default} feature Feature.
* @param {boolean} [opt_listen] Whether to listen to the feature change or not
* Defaults to `true`.
* @api
*/
addFeature(feature, opt_listen) {
const register = opt_listen !== undefined ? opt_listen : true;
const feature_uid = getUid(feature);
const geometry = feature.getGeometry();
if (geometry) {
const segmenter = this.GEOMETRY_SEGMENTERS_[geometry.getType()];
if (segmenter) {
this.indexedFeaturesExtents_[feature_uid] = geometry.getExtent(
createEmpty()
);
const segments =
/** @type {Array<Array<import('../coordinate.js').Coordinate>>} */ ([]);
segmenter(segments, geometry);
if (segments.length === 1) {
this.rBush_.insert(boundingExtent(segments[0]), {
feature: feature,
segment: segments[0],
});
} else if (segments.length > 1) {
const extents = segments.map((s) => boundingExtent(s));
const segmentsData = segments.map((segment) => ({
feature: feature,
segment: segment,
}));
this.rBush_.load(extents, segmentsData);
}
}
}
if (register) {
this.featureChangeListenerKeys_[feature_uid] = listen(
feature,
EventType.CHANGE,
this.handleFeatureChange_,
this
);
}
}
/**
* @param {import("../Feature.js").default} feature Feature.
* @private
*/
forEachFeatureAdd_(feature) {
this.addFeature(feature);
}
/**
* @param {import("../Feature.js").default} feature Feature.
* @private
*/
forEachFeatureRemove_(feature) {
this.removeFeature(feature);
}
/**
* @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;
}
/**
* @param {import("../MapBrowserEvent.js").default} evt Map browser event.
* @return {boolean} `false` to stop event propagation.
*/
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;
}
return super.handleEvent(evt);
}
/**
* @param {import("../source/Vector.js").VectorSourceEvent|import("../Collection.js").CollectionEvent} evt Event.
* @private
*/
handleFeatureAdd_(evt) {
const feature = getFeatureFromEvent(evt);
this.addFeature(feature);
}
/**
* @param {import("../source/Vector.js").VectorSourceEvent|import("../Collection.js").CollectionEvent} evt Event.
* @private
*/
handleFeatureRemove_(evt) {
const feature = getFeatureFromEvent(evt);
this.removeFeature(feature);
}
/**
* @param {import("../events/Event.js").default} evt Event.
* @private
*/
handleFeatureChange_(evt) {
const feature = /** @type {import("../Feature.js").default} */ (evt.target);
if (this.handlingDownUpSequence) {
const uid = getUid(feature);
if (!(uid in this.pendingFeatures_)) {
this.pendingFeatures_[uid] = feature;
}
} else {
this.updateFeature_(feature);
}
}
/**
* Handle pointer up events.
* @param {import("../MapBrowserEvent.js").default} evt Event.
* @return {boolean} If the event was consumed.
*/
handleUpEvent(evt) {
const featuresToUpdate = getValues(this.pendingFeatures_);
if (featuresToUpdate.length) {
featuresToUpdate.forEach(this.updateFeature_.bind(this));
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} [opt_unlisten] Whether to unlisten to the feature change
* or not. Defaults to `true`.
* @api
*/
removeFeature(feature, opt_unlisten) {
const unregister = opt_unlisten !== undefined ? opt_unlisten : true;
const feature_uid = getUid(feature);
const extent = this.indexedFeaturesExtents_[feature_uid];
if (extent) {
const rBush = this.rBush_;
const nodesToRemove = [];
rBush.forEachInExtent(extent, function (node) {
if (feature === node.feature) {
nodesToRemove.push(node);
}
});
for (let i = nodesToRemove.length - 1; i >= 0; --i) {
rBush.remove(nodesToRemove[i]);
}
}
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("../PluggableMap.js").default} map Map.
*/
setMap(map) {
const currentMap = this.getMap();
const keys = this.featuresListenerKeys_;
const features = /** @type {Array<import("../Feature.js").default>} */ (
this.getFeatures_()
);
if (currentMap) {
keys.forEach(unlistenByKey);
keys.length = 0;
features.forEach(this.forEachFeatureRemove_.bind(this));
}
super.setMap(map);
if (map) {
if (this.features_) {
keys.push(
listen(
this.features_,
CollectionEventType.ADD,
this.handleFeatureAdd_,
this
),
listen(
this.features_,
CollectionEventType.REMOVE,
this.handleFeatureRemove_,
this
)
);
} else if (this.source_) {
keys.push(
listen(
this.source_,
VectorEventType.ADDFEATURE,
this.handleFeatureAdd_,
this
),
listen(
this.source_,
VectorEventType.REMOVEFEATURE,
this.handleFeatureRemove_,
this
)
);
}
features.forEach(this.forEachFeatureAdd_.bind(this));
}
}
/**
* @param {import("../pixel.js").Pixel} pixel Pixel
* @param {import("../coordinate.js").Coordinate} pixelCoordinate Coordinate
* @param {import("../PluggableMap.js").default} map Map.
* @return {Result|null} Snap result
*/
snapTo(pixel, pixelCoordinate, map) {
const lowerLeft = map.getCoordinateFromPixel([
pixel[0] - this.pixelTolerance_,
pixel[1] + this.pixelTolerance_,
]);
const upperRight = map.getCoordinateFromPixel([
pixel[0] + this.pixelTolerance_,
pixel[1] - this.pixelTolerance_,
]);
const box = boundingExtent([lowerLeft, upperRight]);
const segments = this.rBush_.getInExtent(box);
const segmentsLength = segments.length;
if (segmentsLength === 0) {
return null;
}
const projection = map.getView().getProjection();
const projectedCoordinate = fromUserCoordinate(pixelCoordinate, projection);
let closestVertex;
let minSquaredDistance = Infinity;
const squaredPixelTolerance = this.pixelTolerance_ * this.pixelTolerance_;
const getResult = () => {
if (closestVertex) {
const vertexPixel = map.getPixelFromCoordinate(closestVertex);
const squaredPixelDistance = squaredDistance(pixel, vertexPixel);
if (squaredPixelDistance <= squaredPixelTolerance) {
return {
vertex: closestVertex,
vertexPixel: [
Math.round(vertexPixel[0]),
Math.round(vertexPixel[1]),
],
};
}
}
return null;
};
if (this.vertex_) {
for (let i = 0; i < segmentsLength; ++i) {
const segmentData = segments[i];
if (segmentData.feature.getGeometry().getType() !== 'Circle') {
segmentData.segment.forEach((vertex) => {
const tempVertexCoord = fromUserCoordinate(vertex, projection);
const delta = squaredDistance(projectedCoordinate, tempVertexCoord);
if (delta < minSquaredDistance) {
closestVertex = vertex;
minSquaredDistance = delta;
}
});
}
}
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 = toUserCoordinate(
closestOnCircle(
projectedCoordinate,
/** @type {import("../geom/Circle.js").default} */ (
circleGeometry
)
),
projection
);
} else {
const [segmentStart, segmentEnd] = segmentData.segment;
// points have only one coordinate
if (segmentEnd) {
tempSegment[0] = fromUserCoordinate(segmentStart, projection);
tempSegment[1] = fromUserCoordinate(segmentEnd, projection);
vertex = closestOnSegment(projectedCoordinate, tempSegment);
}
}
if (vertex) {
const delta = squaredDistance(projectedCoordinate, vertex);
if (delta < minSquaredDistance) {
closestVertex = vertex;
minSquaredDistance = delta;
}
}
}
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);
}
/**
* @param {Array<Array<import('../coordinate.js').Coordinate>>} segments Segments
* @param {import("../geom/Circle.js").default} geometry Geometry.
* @private
*/
segmentCircleGeometry_(segments, geometry) {
const projection = this.getMap().getView().getProjection();
let circleGeometry = geometry;
const userProjection = getUserProjection();
if (userProjection) {
circleGeometry = /** @type {import("../geom/Circle.js").default} */ (
circleGeometry.clone().transform(userProjection, projection)
);
}
const polygon = fromCircle(circleGeometry);
if (userProjection) {
polygon.transform(projection, userProjection);
}
const coordinates = polygon.getCoordinates()[0];
for (let i = 0, ii = coordinates.length - 1; i < ii; ++i) {
segments.push(coordinates.slice(i, i + 2));
}
}
/**
* @param {Array<Array<import('../coordinate.js').Coordinate>>} segments Segments
* @param {import("../geom/GeometryCollection.js").default} geometry Geometry.
* @private
*/
segmentGeometryCollectionGeometry_(segments, geometry) {
const geometries = geometry.getGeometriesArray();
for (let i = 0; i < geometries.length; ++i) {
const segmenter = this.GEOMETRY_SEGMENTERS_[geometries[i].getType()];
if (segmenter) {
segmenter(segments, geometries[i]);
}
}
}
/**
* @param {Array<Array<import('../coordinate.js').Coordinate>>} segments Segments
* @param {import("../geom/LineString.js").default} geometry Geometry.
* @private
*/
segmentLineStringGeometry_(segments, geometry) {
const coordinates = geometry.getCoordinates();
for (let i = 0, ii = coordinates.length - 1; i < ii; ++i) {
segments.push(coordinates.slice(i, i + 2));
}
}
/**
* @param {Array<Array<import('../coordinate.js').Coordinate>>} segments Segments
* @param {import("../geom/MultiLineString.js").default} geometry Geometry.
* @private
*/
segmentMultiLineStringGeometry_(segments, 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) {
segments.push(coordinates.slice(i, i + 2));
}
}
}
/**
* @param {Array<Array<import('../coordinate.js').Coordinate>>} segments Segments
* @param {import("../geom/MultiPoint.js").default} geometry Geometry.
* @private
*/
segmentMultiPointGeometry_(segments, geometry) {
geometry.getCoordinates().forEach((point) => {
segments.push([point]);
});
}
/**
* @param {Array<Array<import('../coordinate.js').Coordinate>>} segments Segments
* @param {import("../geom/MultiPolygon.js").default} geometry Geometry.
* @private
*/
segmentMultiPolygonGeometry_(segments, 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) {
segments.push(coordinates.slice(i, i + 2));
}
}
}
}
/**
* @param {Array<Array<import('../coordinate.js').Coordinate>>} segments Segments
* @param {import("../geom/Point.js").default} geometry Geometry.
* @private
*/
segmentPointGeometry_(segments, geometry) {
segments.push([geometry.getCoordinates()]);
}
/**
* @param {Array<Array<import('../coordinate.js').Coordinate>>} segments Segments
* @param {import("../geom/Polygon.js").default} geometry Geometry.
* @private
*/
segmentPolygonGeometry_(segments, 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) {
segments.push(coordinates.slice(i, i + 2));
}
}
}
}
export default Snap;

417
node_modules/ol/src/interaction/Translate.js generated vendored Normal file
View File

@@ -0,0 +1,417 @@
/**
* @module ol/interaction/Translate
*/
import Collection from '../Collection.js';
import Event from '../events/Event.js';
import InteractionProperty from './Property.js';
import PointerInteraction from './Pointer.js';
import {TRUE} from '../functions.js';
import {always} from '../events/condition.js';
import {includes} from '../array.js';
/**
* @enum {string}
*/
const 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',
};
/**
* A function that takes an {@link module:ol/Feature~Feature} or
* {@link module:ol/render/Feature~RenderFeature} and an
* {@link module:ol/layer/Layer~Layer} and returns `true` if the feature may be
* translated or `false` otherwise.
* @typedef {function(import("../Feature.js").FeatureLike, import("../layer/Layer.js").default<import("../source/Source").default>):boolean} FilterFunction
*/
/**
* @typedef {Object} Options
* @property {import("../events/condition.js").Condition} [condition] A function that
* takes an {@link module:ol/MapBrowserEvent~MapBrowserEvent} and returns a
* boolean to indicate whether that event should be handled.
* Default is {@link module:ol/events/condition.always}.
* @property {Collection<import("../Feature.js").default>} [features] Features contained in this collection will be able to be translated together.
* @property {Array<import("../layer/Layer.js").default>|function(import("../layer/Layer.js").default<import("../source/Source").default>): boolean} [layers] A list of layers from which features should be
* translated. Alternatively, a filter function can be provided. The
* function will be called for each layer in the map and should return
* `true` for layers that you want to be translatable. If the option is
* absent, all visible layers will be considered translatable.
* Not used if `features` is provided.
* @property {FilterFunction} [filter] A function
* that takes an {@link module:ol/Feature~Feature} and an
* {@link module:ol/layer/Layer~Layer} and returns `true` if the feature may be
* translated or `false` otherwise. Not used if `features` is provided.
* @property {number} [hitTolerance=0] Hit-detection tolerance. Pixels inside the radius around the given position
* will be checked for features.
*/
/**
* @classdesc
* Events emitted by {@link module:ol/interaction/Translate~Translate} instances
* are instances of this type.
*/
export class TranslateEvent extends Event {
/**
* @param {TranslateEventType} type Type.
* @param {Collection<import("../Feature.js").default>} 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);
/**
* The features being translated.
* @type {Collection<import("../Feature.js").default>}
* @api
*/
this.features = features;
/**
* The coordinate of the drag event.
* @const
* @type {import("../coordinate.js").Coordinate}
* @api
*/
this.coordinate = coordinate;
/**
* The coordinate of the start position before translation started.
* @const
* @type {import("../coordinate.js").Coordinate}
* @api
*/
this.startCoordinate = startCoordinate;
/**
* Associated {@link module:ol/MapBrowserEvent~MapBrowserEvent}.
* @type {import("../MapBrowserEvent.js").default}
* @api
*/
this.mapBrowserEvent = mapBrowserEvent;
}
}
/***
* @template Return
* @typedef {import("../Observable").OnSignature<import("../Observable").EventTypes, import("../events/Event.js").default, Return> &
* import("../Observable").OnSignature<import("../ObjectEventType").Types|
* 'change:active', import("../Object").ObjectEvent, Return> &
* import("../Observable").OnSignature<'translateend'|'translatestart'|'translating', TranslateEvent, Return> &
* import("../Observable").CombinedOnSignature<import("../Observable").EventTypes|import("../ObjectEventType").Types|
* 'change:active'|'translateend'|'translatestart'|'translating', Return>} TranslateOnSignature
*/
/**
* @classdesc
* Interaction for translating (moving) features.
* If you want to translate multiple features in a single action (for example,
* the collection used by a select interaction), construct the interaction with
* the `features` option.
*
* @fires TranslateEvent
* @api
*/
class Translate extends PointerInteraction {
/**
* @param {Options} [opt_options] Options.
*/
constructor(opt_options) {
const options = opt_options ? opt_options : {};
super(/** @type {import("./Pointer.js").Options} */ (options));
/***
* @type {TranslateOnSignature<import("../events").EventsKey>}
*/
this.on;
/***
* @type {TranslateOnSignature<import("../events").EventsKey>}
*/
this.once;
/***
* @type {TranslateOnSignature<void>}
*/
this.un;
/**
* The last position we translated to.
* @type {import("../coordinate.js").Coordinate}
* @private
*/
this.lastCoordinate_ = null;
/**
* The start position before translation started.
* @type {import("../coordinate.js").Coordinate}
* @private
*/
this.startCoordinate_ = null;
/**
* @type {Collection<import("../Feature.js").default>|null}
* @private
*/
this.features_ = options.features !== undefined ? options.features : null;
/** @type {function(import("../layer/Layer.js").default<import("../source/Source").default>): boolean} */
let layerFilter;
if (options.layers && !this.features_) {
if (typeof options.layers === 'function') {
layerFilter = options.layers;
} else {
const layers = options.layers;
layerFilter = function (layer) {
return includes(layers, layer);
};
}
} else {
layerFilter = TRUE;
}
/**
* @private
* @type {function(import("../layer/Layer.js").default<import("../source/Source").default>): boolean}
*/
this.layerFilter_ = layerFilter;
/**
* @private
* @type {FilterFunction}
*/
this.filter_ = options.filter && !this.features_ ? options.filter : TRUE;
/**
* @private
* @type {number}
*/
this.hitTolerance_ = options.hitTolerance ? options.hitTolerance : 0;
/**
* @private
* @type {import("../events/condition.js").Condition}
*/
this.condition_ = options.condition ? options.condition : always;
/**
* @type {import("../Feature.js").default}
* @private
*/
this.lastFeature_ = null;
this.addChangeListener(
InteractionProperty.ACTIVE,
this.handleActiveChanged_
);
}
/**
* Handle pointer down events.
* @param {import("../MapBrowserEvent.js").default} event Event.
* @return {boolean} If the event was consumed.
*/
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([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.
*/
handleUpEvent(event) {
if (this.lastCoordinate_) {
this.lastCoordinate_ = null;
this.handleMoveEvent(event);
const features = this.features_ || new Collection([this.lastFeature_]);
this.dispatchEvent(
new TranslateEvent(
TranslateEventType.TRANSLATEEND,
features,
event.coordinate,
this.startCoordinate_,
event
)
);
// cleanup
this.startCoordinate_ = null;
return true;
}
return false;
}
/**
* Handle pointer drag events.
* @param {import("../MapBrowserEvent.js").default} event Event.
*/
handleDragEvent(event) {
if (this.lastCoordinate_) {
const newCoordinate = event.coordinate;
const deltaX = newCoordinate[0] - this.lastCoordinate_[0];
const deltaY = newCoordinate[1] - this.lastCoordinate_[1];
const features = this.features_ || new Collection([this.lastFeature_]);
features.forEach(function (feature) {
const geom = feature.getGeometry();
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.
*/
handleMoveEvent(event) {
const elem = event.map.getViewport();
// Change the cursor to grab/grabbing if hovering any of the features managed
// by the interaction
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("../PluggableMap.js").default} map Map to test the intersection on.
* @return {import("../Feature.js").default} Returns the feature found at the specified pixel
* coordinates.
* @private
*/
featuresAtPixel_(pixel, map) {
return map.forEachFeatureAtPixel(
pixel,
function (feature, layer) {
if (this.filter_(feature, layer)) {
if (!this.features_ || includes(this.features_.getArray(), feature)) {
return feature;
}
}
}.bind(this),
{
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("../PluggableMap.js").default} map Map.
*/
setMap(map) {
const oldMap = this.getMap();
super.setMap(map);
this.updateState_(oldMap);
}
/**
* @private
*/
handleActiveChanged_() {
this.updateState_(null);
}
/**
* @param {import("../PluggableMap.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');
}
}
}
}
export default Translate;