This commit is contained in:
64
node_modules/ol/src/interaction/DoubleClickZoom.js
generated
vendored
Normal file
64
node_modules/ol/src/interaction/DoubleClickZoom.js
generated
vendored
Normal 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
325
node_modules/ol/src/interaction/DragAndDrop.js
generated
vendored
Normal 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
275
node_modules/ol/src/interaction/DragBox.js
generated
vendored
Normal 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
189
node_modules/ol/src/interaction/DragPan.js
generated
vendored
Normal 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
125
node_modules/ol/src/interaction/DragRotate.js
generated
vendored
Normal 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
137
node_modules/ol/src/interaction/DragRotateAndZoom.js
generated
vendored
Normal 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
84
node_modules/ol/src/interaction/DragZoom.js
generated
vendored
Normal 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
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
550
node_modules/ol/src/interaction/Extent.js
generated
vendored
Normal 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
170
node_modules/ol/src/interaction/Interaction.js
generated
vendored
Normal 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
125
node_modules/ol/src/interaction/KeyboardPan.js
generated
vendored
Normal 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
94
node_modules/ol/src/interaction/KeyboardZoom.js
generated
vendored
Normal 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
359
node_modules/ol/src/interaction/Link.js
generated
vendored
Normal 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
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
301
node_modules/ol/src/interaction/MouseWheelZoom.js
generated
vendored
Normal 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
163
node_modules/ol/src/interaction/PinchRotate.js
generated
vendored
Normal 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
139
node_modules/ol/src/interaction/PinchZoom.js
generated
vendored
Normal 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
206
node_modules/ol/src/interaction/Pointer.js
generated
vendored
Normal 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
10
node_modules/ol/src/interaction/Property.js
generated
vendored
Normal 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
582
node_modules/ol/src/interaction/Select.js
generated
vendored
Normal 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
666
node_modules/ol/src/interaction/Snap.js
generated
vendored
Normal 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
417
node_modules/ol/src/interaction/Translate.js
generated
vendored
Normal 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;
|
||||
Reference in New Issue
Block a user