merged with upstream and improved key handling

This commit is contained in:
Dmitry Fedorov
2018-11-02 17:19:10 -07:00
252 changed files with 5980 additions and 3750 deletions

View File

@@ -14,7 +14,7 @@ class AssertionError extends Error {
* @param {number} code Error code.
*/
constructor(code) {
const path = VERSION.split('-')[0];
const path = VERSION === 'latest' ? VERSION : 'v' + VERSION.split('-')[0];
const message = 'Assertion failed. See https://openlayers.org/en/' + path +
'/doc/errors/#' + code + ' for details.';

View File

@@ -24,7 +24,7 @@ const Property = {
export class CollectionEvent extends Event {
/**
* @param {import("./CollectionEventType.js").default} type Type.
* @param {CollectionEventType} type Type.
* @param {*=} opt_element Element.
*/
constructor(type, opt_element) {
@@ -56,6 +56,8 @@ export class CollectionEvent extends Event {
* Collection; they trigger events on the appropriate object, not on the
* Collection as a whole.
*
* @fires CollectionEvent
*
* @template T
* @api
*/
@@ -160,7 +162,7 @@ class Collection extends BaseObject {
* @api
*/
getLength() {
return /** @type {number} */ (this.get(Property.LENGTH));
return this.get(Property.LENGTH);
}
/**

View File

@@ -8,13 +8,13 @@
export default {
/**
* Triggered when an item is added to the collection.
* @event module:ol/Collection~CollectionEvent#add
* @event module:ol/Collection.CollectionEvent#add
* @api
*/
ADD: 'add',
/**
* Triggered when an item is removed from the collection.
* @event module:ol/Collection~CollectionEvent#remove
* @event module:ol/Collection.CollectionEvent#remove
* @api
*/
REMOVE: 'remove'

View File

@@ -5,8 +5,14 @@ import {assert} from './asserts.js';
import {listen, unlisten, unlistenByKey} from './events.js';
import EventType from './events/EventType.js';
import BaseObject, {getChangeEventType} from './Object.js';
import Geometry from './geom/Geometry.js';
import Style from './style/Style.js';
/**
* @typedef {typeof Feature|typeof import("./render/Feature.js").default} FeatureClass
*/
/**
* @typedef {Feature|import("./render/Feature.js").default} FeatureLike
*/
/**
* @classdesc
@@ -78,7 +84,7 @@ class Feature extends BaseObject {
/**
* User provided style.
* @private
* @type {import("./style/Style.js").default|Array<import("./style/Style.js").default>|import("./style/Style.js").StyleFunction}
* @type {import("./style/Style.js").StyleLike}
*/
this.style_ = null;
@@ -98,11 +104,9 @@ class Feature extends BaseObject {
this, getChangeEventType(this.geometryName_),
this.handleGeometryChanged_, this);
if (opt_geometryOrProperties !== undefined) {
if (opt_geometryOrProperties instanceof Geometry ||
!opt_geometryOrProperties) {
/** @type {?Geometry} */
const geometry = opt_geometryOrProperties;
if (opt_geometryOrProperties) {
if (typeof /** @type {?} */ (opt_geometryOrProperties).getSimplifiedGeometry === 'function') {
const geometry = /** @type {import("./geom/Geometry.js").default} */ (opt_geometryOrProperties);
this.setGeometry(geometry);
} else {
/** @type {Object<string, *>} */
@@ -171,7 +175,7 @@ class Feature extends BaseObject {
/**
* Get the feature's style. Will return what was provided to the
* {@link module:ol/Feature~Feature#setStyle} method.
* @return {import("./style/Style.js").default|Array<import("./style/Style.js").default>|import("./style/Style.js").StyleFunction} The feature style.
* @return {import("./style/Style.js").StyleLike} The feature style.
* @api
*/
getStyle() {
@@ -226,7 +230,7 @@ class Feature extends BaseObject {
* Set the style for the feature. This can be a single style object, an array
* of styles, or a function that takes a resolution and returns an array of
* styles. If it is `null` the feature has no style (a `null` style).
* @param {import("./style/Style.js").default|Array<import("./style/Style.js").default>|import("./style/Style.js").StyleFunction} style Style for this feature.
* @param {import("./style/Style.js").StyleLike} style Style for this feature.
* @api
* @fires module:ol/events/Event~Event#event:change
*/
@@ -272,9 +276,9 @@ class Feature extends BaseObject {
/**
* Convert the provided object into a feature style function. Functions passed
* through unchanged. Arrays of import("./style/Style.js").default or single style objects wrapped
* through unchanged. Arrays of Style or single style objects wrapped
* in a new feature style function.
* @param {import("./style/Style.js").StyleFunction|!Array<import("./style/Style.js").default>|!import("./style/Style.js").default} obj
* @param {!import("./style/Style.js").StyleFunction|!Array<import("./style/Style.js").default>|!import("./style/Style.js").default} obj
* A feature style function, a single style, or an array of styles.
* @return {import("./style/Style.js").StyleFunction} A style function.
*/
@@ -289,9 +293,10 @@ export function createStyleFunction(obj) {
if (Array.isArray(obj)) {
styles = obj;
} else {
assert(obj instanceof Style,
assert(typeof /** @type {?} */ (obj).getZIndex === 'function',
41); // Expected an `import("./style/Style.js").Style` or an array of `import("./style/Style.js").Style`
styles = [obj];
const style = /** @type {import("./style/Style.js").default} */ (obj);
styles = [style];
}
return function() {
return styles;

View File

@@ -4,6 +4,7 @@
import GeolocationProperty from './GeolocationProperty.js';
import BaseObject, {getChangeEventType} from './Object.js';
import {listen} from './events.js';
import Event from './events/Event.js';
import EventType from './events/EventType.js';
import {circular as circularPolygon} from './geom/Polygon.js';
import {GEOLOCATION} from './has.js';
@@ -11,6 +12,30 @@ import {toRadians} from './math.js';
import {get as getProjection, getTransformFromProjections, identityTransform} from './proj.js';
/**
* @classdesc
* Events emitted on Geolocation error.
*/
class GeolocationError extends Event {
/**
* @param {PositionError} error error object.
*/
constructor(error) {
super(EventType.ERROR);
/**
* @type {number}
*/
this.code = error.code;
/**
* @type {string}
*/
this.message = error.message;
}
}
/**
* @typedef {Object} Options
* @property {boolean} [tracking=false] Start Tracking right after
@@ -174,9 +199,8 @@ class Geolocation extends BaseObject {
* @param {PositionError} error error object.
*/
positionError_(error) {
error.type = EventType.ERROR;
this.setTracking(false);
this.dispatchEvent(/** @type {{type: string, target: undefined}} */ (error));
this.dispatchEvent(new GeolocationError(error));
}
/**

View File

@@ -17,7 +17,7 @@ import Text from './style/Text.js';
/**
* @type {import("./style/Stroke.js").default}
* @type {Stroke}
* @private
* @const
*/
@@ -26,7 +26,6 @@ const DEFAULT_STROKE_STYLE = new Stroke({
});
/**
* TODO can be configurable
* @type {Array<number>}
* @private
*/
@@ -36,7 +35,7 @@ const INTERVALS = [
/**
* @typedef {Object} GraticuleLabelDataType
* @property {import("./geom/Point.js").default} geom
* @property {Point} geom
* @property {string} text
*/
@@ -50,7 +49,7 @@ const INTERVALS = [
* appropriate for conformal projections like Spherical Mercator. If you
* increase the value, more lines will be drawn and the drawing performance will
* decrease.
* @property {import("./style/Stroke.js").default} [strokeStyle='rgba(0,0,0,0.2)'] The
* @property {Stroke} [strokeStyle='rgba(0,0,0,0.2)'] The
* stroke style to use for drawing the graticule. If not provided, a not fully
* opaque black will be used.
* @property {number} [targetSize=100] The target size of the graticule cells,
@@ -71,7 +70,7 @@ const INTERVALS = [
* @property {number} [latLabelPosition=1] Latitude label position in fractions
* (0..1) of view extent. 0 means at the left of the viewport, 1 means at the
* right.
* @property {import("./style/Text.js").default} [lonLabelStyle] Longitude label text
* @property {Text} [lonLabelStyle] Longitude label text
* style. If not provided, the following style will be used:
* ```js
* new Text({
@@ -89,7 +88,7 @@ const INTERVALS = [
* Note that the default's `textBaseline` configuration will not work well for
* `lonLabelPosition` configurations that position labels close to the top of
* the viewport.
* @property {import("./style/Text.js").default} [latLabelStyle] Latitude label text style.
* @property {Text} [latLabelStyle] Latitude label text style.
* If not provided, the following style will be used:
* ```js
* new Text({
@@ -107,6 +106,11 @@ const INTERVALS = [
* Note that the default's `textAlign` configuration will not work well for
* `latLabelPosition` configurations that position labels close to the left of
* the viewport.
* @property {Array<number>} [intervals=[90, 45, 30, 20, 10, 5, 2, 1, 0.5, 0.2, 0.1, 0.05, 0.01, 0.005, 0.002, 0.001]]
* Intervals (in degrees) for the graticule. Example to limit graticules to 30 and 10 degrees intervals:
* ```js
* [30, 10]
* ```
*/
@@ -200,19 +204,19 @@ class Graticule {
this.maxLines_ = options.maxLines !== undefined ? options.maxLines : 100;
/**
* @type {Array<import("./geom/LineString.js").default>}
* @type {Array<LineString>}
* @private
*/
this.meridians_ = [];
/**
* @type {Array<import("./geom/LineString.js").default>}
* @type {Array<LineString>}
* @private
*/
this.parallels_ = [];
/**
* @type {import("./style/Stroke.js").default}
* @type {Stroke}
* @private
*/
this.strokeStyle_ = options.strokeStyle !== undefined ? options.strokeStyle : DEFAULT_STROKE_STYLE;
@@ -282,7 +286,7 @@ class Graticule {
options.latLabelPosition;
/**
* @type {import("./style/Text.js").default}
* @type {Text}
* @private
*/
this.lonLabelStyle_ = options.lonLabelStyle !== undefined ? options.lonLabelStyle :
@@ -299,7 +303,7 @@ class Graticule {
});
/**
* @type {import("./style/Text.js").default}
* @type {Text}
* @private
*/
this.latLabelStyle_ = options.latLabelStyle !== undefined ? options.latLabelStyle :
@@ -319,6 +323,12 @@ class Graticule {
this.parallelsLabels_ = [];
}
/**
* @type {Array<number>}
* @private
*/
this.intervals_ = options.intervals !== undefined ? options.intervals : INTERVALS;
this.setMap(options.map !== undefined ? options.map : null);
}
@@ -348,10 +358,10 @@ class Graticule {
}
/**
* @param {import("./geom/LineString.js").default} lineString Meridian
* @param {LineString} lineString Meridian
* @param {import("./extent.js").Extent} extent Extent.
* @param {number} index Index.
* @return {import("./geom/Point.js").default} Meridian point.
* @return {Point} Meridian point.
* @private
*/
getMeridianPoint_(lineString, extent, index) {
@@ -398,10 +408,10 @@ class Graticule {
}
/**
* @param {import("./geom/LineString.js").default} lineString Parallels.
* @param {LineString} lineString Parallels.
* @param {import("./extent.js").Extent} extent Extent.
* @param {number} index Index.
* @return {import("./geom/Point.js").default} Parallel point.
* @return {Point} Parallel point.
* @private
*/
getParallelPoint_(lineString, extent, index) {
@@ -530,8 +540,8 @@ class Graticule {
const p1 = [];
/** @type {Array<number>} **/
const p2 = [];
for (let i = 0, ii = INTERVALS.length; i < ii; ++i) {
const delta = INTERVALS[i] / 2;
for (let i = 0, ii = this.intervals_.length; i < ii; ++i) {
const delta = this.intervals_[i] / 2;
p1[0] = centerLon - delta;
p1[1] = centerLat - delta;
p2[0] = centerLon + delta;
@@ -542,7 +552,7 @@ class Graticule {
if (dist <= target) {
break;
}
interval = INTERVALS[i];
interval = this.intervals_[i];
}
return interval;
}
@@ -561,7 +571,7 @@ class Graticule {
* @param {number} minLat Minimal latitude.
* @param {number} maxLat Maximal latitude.
* @param {number} squaredTolerance Squared tolerance.
* @return {import("./geom/LineString.js").default} The meridian line string.
* @return {LineString} The meridian line string.
* @param {number} index Index.
* @private
*/
@@ -579,7 +589,7 @@ class Graticule {
/**
* Get the list of meridians. Meridians are lines of equal longitude.
* @return {Array<import("./geom/LineString.js").default>} The meridians.
* @return {Array<LineString>} The meridians.
* @api
*/
getMeridians() {
@@ -591,7 +601,7 @@ class Graticule {
* @param {number} minLon Minimal longitude.
* @param {number} maxLon Maximal longitude.
* @param {number} squaredTolerance Squared tolerance.
* @return {import("./geom/LineString.js").default} The parallel line string.
* @return {LineString} The parallel line string.
* @param {number} index Index.
* @private
*/
@@ -609,7 +619,7 @@ class Graticule {
/**
* Get the list of parallels. Parallels are lines of equal latitude.
* @return {Array<import("./geom/LineString.js").default>} The parallels.
* @return {Array<LineString>} The parallels.
* @api
*/
getParallels() {

View File

@@ -64,7 +64,7 @@ class ImageWrapper extends ImageBase {
/**
* @protected
* @type {import("./ImageState.js").default}
* @type {ImageState}
*/
this.state = ImageState.IDLE;

View File

@@ -1,6 +1,7 @@
/**
* @module ol/ImageBase
*/
import {abstract} from './util.js';
import EventTarget from './events/Target.js';
import EventType from './events/EventType.js';
@@ -63,7 +64,9 @@ class ImageBase extends EventTarget {
* @abstract
* @return {HTMLCanvasElement|HTMLImageElement|HTMLVideoElement} Image.
*/
getImage() {}
getImage() {
return abstract();
}
/**
* @return {number} PixelRatio.
@@ -90,7 +93,9 @@ class ImageBase extends EventTarget {
* Load not yet loaded URI.
* @abstract
*/
load() {}
load() {
abstract();
}
}

View File

@@ -11,7 +11,7 @@ import ImageState from './ImageState.js';
* If any error occurs during drawing, the "done" callback should be called with
* that error.
*
* @typedef {function(function(Error))} Loader
* @typedef {function(function(Error=))} Loader
*/
@@ -62,7 +62,7 @@ class ImageCanvas extends ImageBase {
/**
* Handle async drawing complete.
* @param {Error} err Any error during drawing.
* @param {Error=} err Any error during drawing.
* @private
*/
handleLoad_(err) {

View File

@@ -7,17 +7,12 @@ import {createCanvasContext2D} from './dom.js';
import {listenOnce, unlistenByKey} from './events.js';
import EventType from './events/EventType.js';
/**
* @typedef {function(new: ImageTile, import("./tilecoord.js").TileCoord,
* import("./TileState.js").default, string, ?string, import("./Tile.js").LoadFunction)} TileClass
* @api
*/
class ImageTile extends Tile {
/**
* @param {import("./tilecoord.js").TileCoord} tileCoord Tile coordinate.
* @param {import("./TileState.js").default} state State.
* @param {TileState} state State.
* @param {string} src Image source URI.
* @param {?string} crossOrigin Cross origin.
* @param {import("./Tile.js").LoadFunction} tileLoadFunction Tile load function.
@@ -114,7 +109,8 @@ class ImageTile extends Tile {
* @private
*/
handleImageLoad_() {
if (this.image_.naturalWidth && this.image_.naturalHeight) {
const image = /** @type {HTMLImageElement} */ (this.image_);
if (image.naturalWidth && image.naturalHeight) {
this.state = TileState.LOADED;
} else {
this.state = TileState.EMPTY;

View File

@@ -27,10 +27,10 @@ class MapBrowserEventHandler extends EventTarget {
this.map_ = map;
/**
* @type {number}
* @type {any}
* @private
*/
this.clickTimeoutId_ = 0;
this.clickTimeoutId_;
/**
* @type {boolean}
@@ -118,17 +118,17 @@ class MapBrowserEventHandler extends EventTarget {
let newEvent = new MapBrowserPointerEvent(
MapBrowserEventType.CLICK, this.map_, pointerEvent);
this.dispatchEvent(newEvent);
if (this.clickTimeoutId_ !== 0) {
if (this.clickTimeoutId_ !== undefined) {
// double-click
clearTimeout(this.clickTimeoutId_);
this.clickTimeoutId_ = 0;
this.clickTimeoutId_ = undefined;
newEvent = new MapBrowserPointerEvent(
MapBrowserEventType.DBLCLICK, this.map_, pointerEvent);
this.dispatchEvent(newEvent);
} else {
// click
this.clickTimeoutId_ = setTimeout(function() {
this.clickTimeoutId_ = 0;
this.clickTimeoutId_ = undefined;
const newEvent = new MapBrowserPointerEvent(
MapBrowserEventType.SINGLECLICK, this.map_, pointerEvent);
this.dispatchEvent(newEvent);

View File

@@ -8,7 +8,7 @@
export default {
/**
* Triggered when a property is changed.
* @event module:ol/Object~ObjectEvent#propertychange
* @event module:ol/Object.ObjectEvent#propertychange
* @api
*/
PROPERTYCHANGE: 'propertychange'

View File

@@ -22,7 +22,7 @@ import {containsExtent} from './extent.js';
* shifts the overlay down.
* @property {import("./coordinate.js").Coordinate} [position] The overlay position
* in map projection.
* @property {import("./OverlayPositioning.js").default} [positioning='top-left'] Defines how
* @property {OverlayPositioning} [positioning='top-left'] Defines how
* the overlay is actually positioned with respect to its `position` property.
* Possible values are `'bottom-left'`, `'bottom-center'`, `'bottom-right'`,
* `'center-left'`, `'center-center'`, `'center-right'`, `'top-left'`,
@@ -205,7 +205,7 @@ class Overlay extends BaseObject {
this.setOffset(options.offset !== undefined ? options.offset : [0, 0]);
this.setPositioning(options.positioning !== undefined ?
/** @type {import("./OverlayPositioning.js").default} */ (options.positioning) :
/** @type {OverlayPositioning} */ (options.positioning) :
OverlayPositioning.TOP_LEFT);
if (options.position !== undefined) {
@@ -271,14 +271,14 @@ class Overlay extends BaseObject {
/**
* Get the current positioning of this overlay.
* @return {import("./OverlayPositioning.js").default} How the overlay is positioned
* @return {OverlayPositioning} How the overlay is positioned
* relative to its point on the map.
* @observable
* @api
*/
getPositioning() {
return (
/** @type {import("./OverlayPositioning.js").default} */ (this.get(Property.POSITIONING))
/** @type {OverlayPositioning} */ (this.get(Property.POSITIONING))
);
}
@@ -469,7 +469,7 @@ class Overlay extends BaseObject {
/**
* Set the positioning for this overlay.
* @param {import("./OverlayPositioning.js").default} positioning how the overlay is
* @param {OverlayPositioning} positioning how the overlay is
* positioned relative to its point on the map.
* @observable
* @api

View File

@@ -42,7 +42,7 @@ import {create as createTransform, apply as applyTransform} from './transform.js
* @property {null|import("./extent.js").Extent} extent
* @property {import("./coordinate.js").Coordinate} focus
* @property {number} index
* @property {Object<number, import("./layer/Layer.js").State>} layerStates
* @property {Object<string, import("./layer/Layer.js").State>} layerStates
* @property {Array<import("./layer/Layer.js").State>} layerStatesArray
* @property {import("./transform.js").Transform} pixelToCoordinateTransform
* @property {Array<PostRenderFunction>} postRenderFunctions
@@ -88,7 +88,7 @@ import {create as createTransform, apply as applyTransform} from './transform.js
* @typedef {Object} MapOptions
* @property {Collection<import("./control/Control.js").default>|Array<import("./control/Control.js").default>} [controls]
* Controls initially added to the map. If not specified,
* {@link module:ol/control/util~defaults} is used.
* {@link module:ol/control~defaults} is used.
* @property {number} [pixelRatio=window.devicePixelRatio] The ratio between
* physical pixels and device-independent pixels (dips) on the device.
* @property {Collection<import("./interaction/Interaction.js").default>|Array<import("./interaction/Interaction.js").default>} [interactions]
@@ -102,7 +102,7 @@ import {create as createTransform, apply as applyTransform} from './transform.js
* map target (i.e. the user-provided div for the map). If this is not
* `document`, the target element needs to be focused for key events to be
* emitted, requiring that the target element has a `tabindex` attribute.
* @property {Array<import("./layer/Base.js").default>|Collection<import("./layer/Base.js").default>} [layers]
* @property {Array<import("./layer/Base.js").default>|Collection<import("./layer/Base.js").default>|LayerGroup} [layers]
* Layers. If this is not defined, a map with no layers will be rendered. Note
* that layers are rendered in the order supplied, so if you want, for example,
* a vector layer to appear on top of a tile layer, it must come after the tile
@@ -464,6 +464,10 @@ class PluggableMap extends BaseObject {
}
/**
* @abstract
* @return {import("./renderer/Map.js").default} The map renderer
*/
createRenderer() {
throw new Error('Use a map type that has a createRenderer method');
}
@@ -546,7 +550,7 @@ class PluggableMap extends BaseObject {
* callback with each intersecting feature. Layers included in the detection can
* be configured through the `layerFilter` option in `opt_options`.
* @param {import("./pixel.js").Pixel} pixel Pixel.
* @param {function(this: S, (import("./Feature.js").default|import("./render/Feature.js").default),
* @param {function(this: S, import("./Feature.js").FeatureLike,
* import("./layer/Layer.js").default): T} callback Feature callback. The callback will be
* called with two arguments. The first argument is one
* {@link module:ol/Feature feature} or
@@ -565,7 +569,8 @@ class PluggableMap extends BaseObject {
return;
}
const coordinate = this.getCoordinateFromPixel(pixel);
opt_options = opt_options !== undefined ? opt_options : {};
opt_options = opt_options !== undefined ? opt_options :
/** @type {AtPixelOptions} */ ({});
const hitTolerance = opt_options.hitTolerance !== undefined ?
opt_options.hitTolerance * this.frameState_.pixelRatio : 0;
const layerFilter = opt_options.layerFilter !== undefined ?
@@ -579,7 +584,7 @@ class PluggableMap extends BaseObject {
* Get all features that intersect a pixel on the viewport.
* @param {import("./pixel.js").Pixel} pixel Pixel.
* @param {AtPixelOptions=} opt_options Optional options.
* @return {Array<import("./Feature.js").default|import("./render/Feature.js").default>} The detected features or
* @return {Array<import("./Feature.js").FeatureLike>} The detected features or
* `null` if none were found.
* @api
*/
@@ -637,7 +642,8 @@ class PluggableMap extends BaseObject {
return false;
}
const coordinate = this.getCoordinateFromPixel(pixel);
opt_options = opt_options !== undefined ? opt_options : {};
opt_options = opt_options !== undefined ? opt_options :
/** @type {AtPixelOptions} */ ({});
const layerFilter = opt_options.layerFilter !== undefined ? opt_options.layerFilter : TRUE;
const hitTolerance = opt_options.hitTolerance !== undefined ?
opt_options.hitTolerance * this.frameState_.pixelRatio : 0;
@@ -657,13 +663,16 @@ class PluggableMap extends BaseObject {
/**
* Returns the map pixel position for a browser event relative to the viewport.
* @param {Event} event Event.
* @param {Event|TouchEvent} event Event.
* @return {import("./pixel.js").Pixel} Pixel.
* @api
*/
getEventPixel(event) {
const viewportPosition = this.viewport_.getBoundingClientRect();
const eventPosition = event.changedTouches ? event.changedTouches[0] : event;
const eventPosition = 'changedTouches' in event ?
/** @type {TouchEvent} */ (event).changedTouches[0] :
/** @type {MouseEvent} */ (event);
return [
eventPosition.clientX - viewportPosition.left,
eventPosition.clientY - viewportPosition.top
@@ -963,7 +972,7 @@ class PluggableMap extends BaseObject {
tileQueue.loadMoreTiles(maxTotalLoading, maxNewLoads);
}
}
if (frameState && this.hasListener(MapEventType.RENDERCOMPLETE) && !frameState.animate &&
if (frameState && this.hasListener(RenderEventType.RENDERCOMPLETE) && !frameState.animate &&
!this.tileQueue_.getTilesLoading() && !getLoading(this.getLayers().getArray())) {
this.renderer_.dispatchRenderEvent(RenderEventType.RENDERCOMPLETE, frameState);
}
@@ -1295,8 +1304,7 @@ class PluggableMap extends BaseObject {
* @param {import("./Feature.js").default} feature Feature.
*/
skipFeature(feature) {
const featureUid = getUid(feature).toString();
this.skippedFeatureUids_[featureUid] = true;
this.skippedFeatureUids_[getUid(feature)] = true;
this.render();
}
@@ -1331,8 +1339,7 @@ class PluggableMap extends BaseObject {
* @param {import("./Feature.js").default} feature Feature.
*/
unskipFeature(feature) {
const featureUid = getUid(feature).toString();
delete this.skippedFeatureUids_[featureUid];
delete this.skippedFeatureUids_[getUid(feature)];
this.render();
}
}
@@ -1359,8 +1366,8 @@ function createOptionsInternal(options) {
*/
const values = {};
const layerGroup = (options.layers instanceof LayerGroup) ?
options.layers : new LayerGroup({layers: options.layers});
const layerGroup = options.layers && typeof /** @type {?} */ (options.layers).getLayers === 'function' ?
/** @type {LayerGroup} */ (options.layers) : new LayerGroup({layers: /** @type {Collection} */ (options.layers)});
values[MapProperty.LAYERGROUP] = layerGroup;
values[MapProperty.TARGET] = options.target;
@@ -1373,9 +1380,9 @@ function createOptionsInternal(options) {
if (Array.isArray(options.controls)) {
controls = new Collection(options.controls.slice());
} else {
assert(options.controls instanceof Collection,
assert(typeof /** @type {?} */ (options.controls).getArray === 'function',
47); // Expected `controls` to be an array or an `import("./Collection.js").Collection`
controls = options.controls;
controls = /** @type {Collection} */ (options.controls);
}
}
@@ -1384,9 +1391,9 @@ function createOptionsInternal(options) {
if (Array.isArray(options.interactions)) {
interactions = new Collection(options.interactions.slice());
} else {
assert(options.interactions instanceof Collection,
assert(typeof /** @type {?} */ (options.interactions).getArray === 'function',
48); // Expected `interactions` to be an array or an `import("./Collection.js").Collection`
interactions = options.interactions;
interactions = /** @type {Collection} */ (options.interactions);
}
}
@@ -1395,7 +1402,7 @@ function createOptionsInternal(options) {
if (Array.isArray(options.overlays)) {
overlays = new Collection(options.overlays.slice());
} else {
assert(options.overlays instanceof Collection,
assert(typeof /** @type {?} */ (options.overlays).getArray === 'function',
49); // Expected `overlays` to be an array or an `import("./Collection.js").Collection`
overlays = options.overlays;
}
@@ -1421,12 +1428,14 @@ export default PluggableMap;
function getLoading(layers) {
for (let i = 0, ii = layers.length; i < ii; ++i) {
const layer = layers[i];
if (layer instanceof LayerGroup) {
return getLoading(layer.getLayers().getArray());
}
const source = layers[i].getSource();
if (source && source.loading) {
return true;
if (typeof /** @type {?} */ (layer).getLayers === 'function') {
return getLoading(/** @type {LayerGroup} */ (layer).getLayers().getArray());
} else {
const source = /** @type {import("./layer/Layer.js").default} */ (
layer).getSource();
if (source && source.loading) {
return true;
}
}
}
return false;

View File

@@ -122,7 +122,7 @@ class Tile extends EventTarget {
/**
* Lookup of start times for rendering transitions. If the start time is
* equal to -1, the transition is complete.
* @type {Object<number, number>}
* @type {Object<string, number>}
*/
this.transitionStarts_ = {};
@@ -180,7 +180,7 @@ class Tile extends EventTarget {
}
let tile = this.interimTile;
let prev = this;
let prev = /** @type {Tile} */ (this);
do {
if (tile.getState() == TileState.LOADED) {
@@ -244,7 +244,7 @@ class Tile extends EventTarget {
/**
* Get the alpha value for rendering.
* @param {number} id An id for the renderer.
* @param {string} id An id for the renderer.
* @param {number} time The render frame time.
* @return {number} A number between 0 and 1.
*/
@@ -272,7 +272,7 @@ class Tile extends EventTarget {
* Determine if a tile is in an alpha transition. A tile is considered in
* transition if tile.getAlpha() has not yet been called or has been called
* and returned 1.
* @param {number} id An id for the renderer.
* @param {string} id An id for the renderer.
* @return {boolean} The tile is in transition.
*/
inTransition(id) {
@@ -284,7 +284,7 @@ class Tile extends EventTarget {
/**
* Mark a transition as complete.
* @param {number} id An id for the renderer.
* @param {string} id An id for the renderer.
*/
endTransition(id) {
if (this.transition_) {

View File

@@ -92,30 +92,30 @@ class TileRange {
}
/**
* @return {number} Height.
*/
* @return {number} Height.
*/
getHeight() {
return this.maxY - this.minY + 1;
}
/**
* @return {import("./size.js").Size} Size.
*/
* @return {import("./size.js").Size} Size.
*/
getSize() {
return [this.getWidth(), this.getHeight()];
}
/**
* @return {number} Width.
*/
* @return {number} Width.
*/
getWidth() {
return this.maxX - this.minX + 1;
}
/**
* @param {TileRange} tileRange Tile range.
* @return {boolean} Intersects.
*/
* @param {TileRange} tileRange Tile range.
* @return {boolean} Intersects.
*/
intersects(tileRange) {
return this.minX <= tileRange.maxX &&
this.maxX >= tileRange.minX &&

View File

@@ -12,7 +12,6 @@ export default {
/**
* Indicates that tile loading failed
* @type {number}
* @api
*/
ERROR: 3,
EMPTY: 4,

View File

@@ -25,7 +25,7 @@ class VectorImageTile extends Tile {
/**
* @param {import("./tilecoord.js").TileCoord} tileCoord Tile coordinate.
* @param {import("./TileState.js").default} state State.
* @param {TileState} state State.
* @param {number} sourceRevision Source revision.
* @param {import("./format/Feature.js").default} format Feature format.
* @param {import("./Tile.js").LoadFunction} tileLoadFunction Tile load function.
@@ -36,8 +36,7 @@ class VectorImageTile extends Tile {
* @param {Object<string, import("./VectorTile.js").default>} sourceTiles Source tiles.
* @param {number} pixelRatio Pixel ratio.
* @param {import("./proj/Projection.js").default} projection Projection.
* @param {function(new: import("./VectorTile.js").default, import("./tilecoord.js").TileCoord, import("./TileState.js").default, string,
* import("./format/Feature.js").default, import("./Tile.js").LoadFunction)} tileClass Class to
* @param {typeof import("./VectorTile.js").default} tileClass Class to
* instantiate for source tiles.
* @param {function(this: import("./source/VectorTile.js").default, import("./events/Event.js").default)} handleTileChange
* Function to call when a source tile's state changes.
@@ -192,7 +191,7 @@ class VectorImageTile extends Tile {
* @return {CanvasRenderingContext2D} The rendering context.
*/
getContext(layer) {
const key = getUid(layer).toString();
const key = getUid(layer);
if (!(key in this.context_)) {
this.context_[key] = createCanvasContext2D();
}
@@ -214,7 +213,7 @@ class VectorImageTile extends Tile {
* @return {ReplayState} The replay state.
*/
getReplayState(layer) {
const key = getUid(layer).toString();
const key = getUid(layer);
if (!(key in this.replayState_)) {
this.replayState_[key] = {
dirty: false,

View File

@@ -12,12 +12,6 @@ import TileState from './TileState.js';
const DEFAULT_EXTENT = [0, 0, 4096, 4096];
/**
* @typedef {function(new: VectorTile, import("./tilecoord.js").TileCoord,
* TileState, string, ?string, import("./Tile.js").LoadFunction)} TileClass
* @api
*/
class VectorTile extends Tile {
/**
@@ -120,7 +114,7 @@ class VectorTile extends Tile {
/**
* Get the features for this tile. Geometries will be in the projection returned
* by {@link module:ol/VectorTile~VectorTile#getProjection}.
* @return {Array<import("./Feature.js").default|import("./render/Feature.js").default>} Features.
* @return {Array<import("./Feature.js").FeatureLike>} Features.
* @api
*/
getFeatures() {

View File

@@ -17,7 +17,6 @@ import {inAndOut} from './easing.js';
import {getForViewAndSize, getCenter, getHeight, getWidth, isEmpty} from './extent.js';
import GeometryType from './geom/GeometryType.js';
import {fromExtent as polygonFromExtent} from './geom/Polygon.js';
import SimpleGeometry from './geom/SimpleGeometry.js';
import {clamp, modulo} from './math.js';
import {assign} from './obj.js';
import {createProjection, METERS_PER_UNIT} from './proj.js';
@@ -987,9 +986,9 @@ class View extends BaseObject {
}
/** @type {import("./geom/SimpleGeometry.js").default} */
let geometry;
if (!(geometryOrExtent instanceof SimpleGeometry)) {
assert(Array.isArray(geometryOrExtent),
24); // Invalid extent or geometry provided as `geometry`
assert(Array.isArray(geometryOrExtent) || typeof /** @type {?} */ (geometryOrExtent).getSimplifiedGeometry === 'function',
24); // Invalid extent or geometry provided as `geometry`
if (Array.isArray(geometryOrExtent)) {
assert(!isEmpty(geometryOrExtent),
25); // Cannot fit empty extent provided as `geometry`
geometry = polygonFromExtent(geometryOrExtent);
@@ -1135,7 +1134,7 @@ class View extends BaseObject {
}
/**
* @param {import("./ViewHint.js").default} hint Hint.
* @param {ViewHint} hint Hint.
* @param {number} delta Delta.
* @return {number} New value.
*/

View File

@@ -133,7 +133,7 @@ export function asArray(color) {
if (Array.isArray(color)) {
return color;
} else {
return fromString(/** @type {string} */ (color));
return fromString(color);
}
}
@@ -185,9 +185,7 @@ function fromStringInternal_(s) {
} else {
assert(false, 14); // Invalid color
}
return (
/** @type {Color} */ (color)
);
return color;
}

View File

@@ -23,22 +23,9 @@ import {toString} from './color.js';
* @api
*/
export function asColorLike(color) {
if (isColorLike(color)) {
return /** @type {string|CanvasPattern|CanvasGradient} */ (color);
if (Array.isArray(color)) {
return toString(color);
} else {
return toString(/** @type {import("./color.js").Color} */ (color));
return color;
}
}
/**
* @param {?} color The value that is potentially an {@link ol/colorlike~ColorLike}.
* @return {boolean} The color is an {@link ol/colorlike~ColorLike}.
*/
export function isColorLike(color) {
return (
typeof color === 'string' ||
color instanceof CanvasPattern ||
color instanceof CanvasGradient
);
}

View File

@@ -5,6 +5,7 @@
export {default as Attribution} from './control/Attribution.js';
export {default as Control} from './control/Control.js';
export {default as FullScreen} from './control/FullScreen.js';
export {default as MousePosition} from './control/MousePosition.js';
export {default as OverviewMap} from './control/OverviewMap.js';
export {default as Rotate} from './control/Rotate.js';
export {default as ScaleLine} from './control/ScaleLine.js';

View File

@@ -16,9 +16,9 @@ import {visibleAtResolution} from '../layer/Layer.js';
* @property {HTMLElement|string} [target] Specify a target if you
* want the control to be rendered outside of the map's
* viewport.
* @property {boolean} [collapsible=true] Specify if attributions can
* be collapsed. If you use an OSM source, should be set to `false` — see
* {@link https://www.openstreetmap.org/copyright OSM Copyright} —
* @property {boolean} [collapsible] Specify if attributions can
* be collapsed. If not specified, sources control this behavior with their
* `attributionsCollapsible` setting.
* @property {boolean} [collapsed=true] Specify if attributions should
* be collapsed at startup.
* @property {string} [tipLabel='Attributions'] Text label to use for the button tip.
@@ -70,6 +70,12 @@ class Attribution extends Control {
*/
this.collapsed_ = options.collapsed !== undefined ? options.collapsed : true;
/**
* @private
* @type {boolean}
*/
this.overrideCollapsible_ = options.collapsible !== undefined;
/**
* @private
* @type {boolean}
@@ -145,12 +151,12 @@ class Attribution extends Control {
}
/**
* Get a list of visible attributions.
* Collect a list of visible attributions and set the collapsible state.
* @param {import("../PluggableMap.js").FrameState} frameState Frame state.
* @return {Array<string>} Attributions.
* @private
*/
getSourceAttributions_(frameState) {
collectSourceAttributions_(frameState) {
/**
* Used to determine if an attribution already exists.
* @type {!Object<string, boolean>}
@@ -171,7 +177,7 @@ class Attribution extends Control {
continue;
}
const source = layerState.layer.getSource();
const source = /** @type {import("../layer/Layer.js").default} */ (layerState.layer).getSource();
if (!source) {
continue;
}
@@ -186,6 +192,10 @@ class Attribution extends Control {
continue;
}
if (!this.overrideCollapsible_ && source.getAttributionsCollapsible() === false) {
this.setCollapsible(false);
}
if (Array.isArray(attributions)) {
for (let j = 0, jj = attributions.length; j < jj; ++j) {
if (!(attributions[j] in lookup)) {
@@ -216,7 +226,7 @@ class Attribution extends Control {
return;
}
const attributions = this.getSourceAttributions_(frameState);
const attributions = this.collectSourceAttributions_(frameState);
const visible = attributions.length > 0;
if (this.renderedVisible_ != visible) {

View File

@@ -34,9 +34,9 @@ const getChangeType = (function() {
/**
* @typedef {Object} Options
* @property {string} [className='ol-full-screen'] CSS class name.
* @property {string|HTMLElement} [label='\u2922'] Text label to use for the button.
* @property {string|Text} [label='\u2922'] Text label to use for the button.
* Instead of text, also an element (e.g. a `span` element) can be used.
* @property {string|HTMLElement} [labelActive='\u00d7'] Text label to use for the
* @property {string|Text} [labelActive='\u00d7'] Text label to use for the
* button when full-screen is active.
* Instead of text, also an element (e.g. a `span` element) can be used.
* @property {string} [tipLabel='Toggle full-screen'] Text label to use for the button tip.
@@ -87,7 +87,7 @@ class FullScreen extends Control {
/**
* @private
* @type {HTMLElement}
* @type {Text}
*/
this.labelNode_ = typeof label === 'string' ?
document.createTextNode(label) : label;
@@ -96,19 +96,24 @@ class FullScreen extends Control {
/**
* @private
* @type {HTMLElement}
* @type {Text}
*/
this.labelActiveNode_ = typeof labelActive === 'string' ?
document.createTextNode(labelActive) : labelActive;
const tipLabel = options.tipLabel ? options.tipLabel : 'Toggle full-screen';
const button = document.createElement('button');
button.className = this.cssClassName_ + '-' + isFullScreen();
button.setAttribute('type', 'button');
button.title = tipLabel;
button.appendChild(this.labelNode_);
/**
* @private
* @type {HTMLElement}
*/
this.button_ = document.createElement('button');
listen(button, EventType.CLICK,
const tipLabel = options.tipLabel ? options.tipLabel : 'Toggle full-screen';
this.setClassName_(this.button_, isFullScreen());
this.button_.setAttribute('type', 'button');
this.button_.title = tipLabel;
this.button_.appendChild(this.labelNode_);
listen(this.button_, EventType.CLICK,
this.handleClick_, this);
const cssClasses = this.cssClassName_ + ' ' + CLASS_UNSELECTABLE +
@@ -116,7 +121,7 @@ class FullScreen extends Control {
(!isFullScreenSupported() ? CLASS_UNSUPPORTED : '');
const element = this.element;
element.className = cssClasses;
element.appendChild(button);
element.appendChild(this.button_);
/**
* @private
@@ -176,13 +181,12 @@ class FullScreen extends Control {
* @private
*/
handleFullScreenChange_() {
const button = this.element.firstElementChild;
const map = this.getMap();
if (isFullScreen()) {
button.className = this.cssClassName_ + '-true';
this.setClassName_(this.button_, true);
replaceNode(this.labelActiveNode_, this.labelNode_);
} else {
button.className = this.cssClassName_ + '-false';
this.setClassName_(this.button_, false);
replaceNode(this.labelNode_, this.labelActiveNode_);
}
if (map) {
@@ -190,6 +194,20 @@ class FullScreen extends Control {
}
}
/**
* @param {HTMLElement} element Target element
* @param {boolean} fullscreen True if fullscreen class name should be active
* @private
*/
setClassName_(element, fullscreen) {
const activeClassName = this.cssClassName_ + '-true';
const inactiveClassName = this.cssClassName_ + '-false';
const nextClassName = fullscreen ? activeClassName : inactiveClassName;
element.classList.remove(activeClassName);
element.classList.remove(inactiveClassName);
element.classList.add(nextClassName);
}
/**
* @inheritDoc
* @api
@@ -253,7 +271,7 @@ function requestFullScreenWithKeys(element) {
if (element.mozRequestFullScreenWithKeys) {
element.mozRequestFullScreenWithKeys();
} else if (element.webkitRequestFullscreen) {
element.webkitRequestFullscreen(Element.ALLOW_KEYBOARD_INPUT);
element.webkitRequestFullscreen();
} else {
requestFullScreen(element);
}

View File

@@ -23,11 +23,11 @@ const COORDINATE_FORMAT = 'coordinateFormat';
* @typedef {Object} Options
* @property {string} [className='ol-mouse-position'] CSS class name.
* @property {import("../coordinate.js").CoordinateFormat} [coordinateFormat] Coordinate format.
* @property {import("../proj.js").ProjectionLike} projection Projection.
* @property {import("../proj.js").ProjectionLike} [projection] Projection. Default is the view projection.
* @property {function(import("../MapEvent.js").default)} [render] Function called when the
* control should be re-rendered. This is called in a `requestAnimationFrame`
* callback.
* @property {Element|string} [target] Specify a target if you want the
* @property {HTMLElement|string} [target] Specify a target if you want the
* control to be rendered outside of the map's viewport.
* @property {string} [undefinedHTML='&#160;'] Markup to show when coordinates are not
* available (e.g. when the pointer leaves the map viewport). By default, the last position

View File

@@ -155,7 +155,7 @@ class OverviewMap extends Control {
const ovmap = this.ovmap_;
if (options.layers) {
options.layers.forEach(
/** @type {Array<import("../layer/Layer.js").default>} */ (options.layers).forEach(
/**
* @param {import("../layer/Layer.js").default} layer Layer.
*/
@@ -204,7 +204,8 @@ class OverviewMap extends Control {
};
const move = function(event) {
const coordinates = ovmap.getEventCoordinate(computeDesiredMousePosition(event));
const position = /** @type {?} */ (computeDesiredMousePosition(event));
const coordinates = ovmap.getEventCoordinate(/** @type {Event} */ (position));
overlay.setPosition(coordinates);
};

View File

@@ -121,22 +121,19 @@ class ScaleLine extends Control {
this, getChangeEventType(UNITS_PROP),
this.handleUnitsChanged_, this);
this.setUnits(/** @type {Units} */ (options.units) ||
Units.METRIC);
this.setUnits(/** @type {Units} */ (options.units) || Units.METRIC);
}
/**
* Return the units to use in the scale line.
* @return {Units|undefined} The units
* @return {Units} The units
* to use in the scale line.
* @observable
* @api
*/
getUnits() {
return (
/** @type {Units|undefined} */ (this.get(UNITS_PROP))
);
return this.get(UNITS_PROP);
}
/**

View File

@@ -5,7 +5,7 @@ import ViewHint from '../ViewHint.js';
import Control from '../control/Control.js';
import {CLASS_CONTROL, CLASS_UNSELECTABLE} from '../css.js';
import {easeOut} from '../easing.js';
import {listen} from '../events.js';
import {listen, unlistenByKey} from '../events.js';
import {stopPropagation} from '../events/Event.js';
import EventType from '../events/EventType.js';
import {clamp} from '../math.js';
@@ -57,6 +57,12 @@ class ZoomSlider extends Control {
render: options.render || render
});
/**
* @type {!Array.<import("../events.js").EventsKey>}
* @private
*/
this.dragListenerKeys_ = [];
/**
* Will hold the current resolution of the view.
*
@@ -133,7 +139,7 @@ class ZoomSlider extends Control {
containerElement.className = className + ' ' + CLASS_UNSELECTABLE + ' ' + CLASS_CONTROL;
containerElement.appendChild(thumbElement);
/**
* @type {import("../pointer/PointerEventHandler.js").default}
* @type {PointerEventHandler}
* @private
*/
this.dragger_ = new PointerEventHandler(containerElement);
@@ -231,20 +237,31 @@ class ZoomSlider extends Control {
this.previousX_ = event.clientX;
this.previousY_ = event.clientY;
this.dragging_ = true;
if (this.dragListenerKeys_.length === 0) {
const drag = this.handleDraggerDrag_;
const end = this.handleDraggerEnd_;
this.dragListenerKeys_.push(
listen(document, EventType.MOUSEMOVE, drag, this),
listen(document, PointerEventType.POINTERMOVE, drag, this),
listen(document, EventType.MOUSEUP, end, this),
listen(document, PointerEventType.POINTERUP, end, this)
);
}
}
}
/**
* Handle dragger drag events.
*
* @param {import("../pointer/PointerEvent.js").default|Event} event The drag event.
* @param {import("../pointer/PointerEvent.js").default} event The drag event.
* @private
*/
handleDraggerDrag_(event) {
if (this.dragging_) {
const element = /** @type {HTMLElement} */ (this.element.firstElementChild);
const deltaX = event.clientX - this.previousX_ + parseInt(element.style.left, 10);
const deltaY = event.clientY - this.previousY_ + parseInt(element.style.top, 10);
const deltaX = event.clientX - this.previousX_ + parseFloat(element.style.left);
const deltaY = event.clientY - this.previousY_ + parseFloat(element.style.top);
const relativePosition = this.getRelativePosition_(deltaX, deltaY);
this.currentResolution_ = this.getResolutionForPosition_(relativePosition);
this.getMap().getView().setResolution(this.currentResolution_);
@@ -256,7 +273,7 @@ class ZoomSlider extends Control {
/**
* Handle dragger end events.
* @param {import("../pointer/PointerEvent.js").default|Event} event The drag event.
* @param {import("../pointer/PointerEvent.js").default} event The drag event.
* @private
*/
handleDraggerEnd_(event) {
@@ -273,6 +290,8 @@ class ZoomSlider extends Control {
this.dragging_ = false;
this.previousX_ = undefined;
this.previousY_ = undefined;
this.dragListenerKeys_.forEach(unlistenByKey);
this.dragListenerKeys_.length = 0;
}
}

View File

@@ -34,7 +34,7 @@ import Zoom from './Zoom.js';
*
* @param {DefaultsOptions=} opt_options
* Defaults options.
* @return {import("../Collection.js").default<import("./Control.js").default>}
* @return {Collection<import("./Control.js").default>}
* Controls.
* @function module:ol/control.defaults
* @api

View File

@@ -22,7 +22,7 @@ import {clear} from './obj.js';
* Listener function. This function is called with an event object as argument.
* When the function returns `false`, event propagation will stop.
*
* @typedef {function(import("./events/Event.js").default): (void|boolean)} ListenerFunction
* @typedef {function((Event|import("./events/Event.js").default)): (void|boolean)} ListenerFunction
* @api
*/
@@ -78,27 +78,36 @@ export function findListener(listeners, listener, opt_this, opt_setDeleteIndex)
* @return {Array<EventsKey>|undefined} Listeners.
*/
export function getListeners(target, type) {
const listenerMap = target.ol_lm;
const listenerMap = getListenerMap(target);
return listenerMap ? listenerMap[type] : undefined;
}
/**
* Get the lookup of listeners. If one does not exist on the target, it is
* created.
* @param {import("./events/Target.js").EventTargetLike} target Target.
* Get the lookup of listeners.
* @param {Object} target Target.
* @param {boolean=} opt_create If a map should be created if it doesn't exist.
* @return {!Object<string, Array<EventsKey>>} Map of
* listeners by event type.
*/
function getListenerMap(target) {
function getListenerMap(target, opt_create) {
let listenerMap = target.ol_lm;
if (!listenerMap) {
if (!listenerMap && opt_create) {
listenerMap = target.ol_lm = {};
}
return listenerMap;
}
/**
* Remove the listener map from a target.
* @param {Object} target Target.
*/
function removeListenerMap(target) {
delete target.ol_lm;
}
/**
* Clean up all listener objects of the given type. All properties on the
* listener objects will be removed, and if no listeners remain in the listener
@@ -110,15 +119,16 @@ function removeListeners(target, type) {
const listeners = getListeners(target, type);
if (listeners) {
for (let i = 0, ii = listeners.length; i < ii; ++i) {
target.removeEventListener(type, listeners[i].boundListener);
/** @type {import("./events/Target.js").default} */ (target).
removeEventListener(type, listeners[i].boundListener);
clear(listeners[i]);
}
listeners.length = 0;
const listenerMap = target.ol_lm;
const listenerMap = getListenerMap(target);
if (listenerMap) {
delete listenerMap[type];
if (Object.keys(listenerMap).length === 0) {
delete target.ol_lm;
removeListenerMap(target);
}
}
}
@@ -141,7 +151,7 @@ function removeListeners(target, type) {
* @return {EventsKey} Unique key for the listener.
*/
export function listen(target, type, listener, opt_this, opt_once) {
const listenerMap = getListenerMap(target);
const listenerMap = getListenerMap(target, true);
let listeners = listenerMap[type];
if (!listeners) {
listeners = listenerMap[type] = [];
@@ -160,7 +170,8 @@ export function listen(target, type, listener, opt_this, opt_once) {
target: target,
type: type
});
target.addEventListener(type, bindListener(listenerObj));
/** @type {import("./events/Target.js").default} */ (target).
addEventListener(type, bindListener(listenerObj));
listeners.push(listenerObj);
}
@@ -228,7 +239,8 @@ export function unlisten(target, type, listener, opt_this) {
*/
export function unlistenByKey(key) {
if (key && key.target) {
key.target.removeEventListener(key.type, key.boundListener);
/** @type {import("./events/Target.js").default} */ (key.target).
removeEventListener(key.type, key.boundListener);
const listeners = getListeners(key.target, key.type);
if (listeners) {
const i = 'deleteIndex' in key ? key.deleteIndex : listeners.indexOf(key);
@@ -252,7 +264,9 @@ export function unlistenByKey(key) {
*/
export function unlistenAll(target) {
const listenerMap = getListenerMap(target);
for (const type in listenerMap) {
removeListeners(target, type);
if (listenerMap) {
for (const type in listenerMap) {
removeListeners(target, type);
}
}
}

View File

@@ -72,7 +72,8 @@ class Target extends Disposable {
* Object with a `type` property.
*
* @param {{type: string,
* target: (EventTargetLike|undefined)}|
* target: (EventTargetLike|undefined),
* propagationStopped: (boolean|undefined)}|
* import("./Event.js").default|string} event Event object.
* @return {boolean|undefined} `false` if anyone called preventDefault on the
* event object or if any of the listeners returned false.

View File

@@ -24,7 +24,7 @@ import {WEBKIT, MAC} from '../has.js';
* @api
*/
export const altKeyOnly = function(mapBrowserEvent) {
const originalEvent = mapBrowserEvent.originalEvent;
const originalEvent = /** @type {KeyboardEvent|MouseEvent|TouchEvent} */ (mapBrowserEvent.originalEvent);
return (
originalEvent.altKey &&
!(originalEvent.metaKey || originalEvent.ctrlKey) &&
@@ -41,7 +41,7 @@ export const altKeyOnly = function(mapBrowserEvent) {
* @api
*/
export const altShiftKeysOnly = function(mapBrowserEvent) {
const originalEvent = mapBrowserEvent.originalEvent;
const originalEvent = /** @type {KeyboardEvent|MouseEvent|TouchEvent} */ (mapBrowserEvent.originalEvent);
return (
originalEvent.altKey &&
!(originalEvent.metaKey || originalEvent.ctrlKey) &&
@@ -94,7 +94,7 @@ export const click = function(mapBrowserEvent) {
* @return {boolean} The result.
*/
export const mouseActionButton = function(mapBrowserEvent) {
const originalEvent = mapBrowserEvent.originalEvent;
const originalEvent = /** @type {MouseEvent} */ (mapBrowserEvent.originalEvent);
return originalEvent.button == 0 &&
!(WEBKIT && MAC && originalEvent.ctrlKey);
};
@@ -156,7 +156,7 @@ export const doubleClick = function(mapBrowserEvent) {
* @api
*/
export const noModifierKeys = function(mapBrowserEvent) {
const originalEvent = mapBrowserEvent.originalEvent;
const originalEvent = /** @type {KeyboardEvent|MouseEvent|TouchEvent} */ (mapBrowserEvent.originalEvent);
return (
!originalEvent.altKey &&
!(originalEvent.metaKey || originalEvent.ctrlKey) &&
@@ -174,7 +174,7 @@ export const noModifierKeys = function(mapBrowserEvent) {
* @api
*/
export const platformModifierKeyOnly = function(mapBrowserEvent) {
const originalEvent = mapBrowserEvent.originalEvent;
const originalEvent = /** @type {KeyboardEvent|MouseEvent|TouchEvent} */ (mapBrowserEvent.originalEvent);
return !originalEvent.altKey &&
(MAC ? originalEvent.metaKey : originalEvent.ctrlKey) &&
!originalEvent.shiftKey;
@@ -190,7 +190,7 @@ export const platformModifierKeyOnly = function(mapBrowserEvent) {
* @api
*/
export const shiftKeyOnly = function(mapBrowserEvent) {
const originalEvent = mapBrowserEvent.originalEvent;
const originalEvent = /** @type {KeyboardEvent|MouseEvent|TouchEvent} */ (mapBrowserEvent.originalEvent);
return (
!originalEvent.altKey &&
!(originalEvent.metaKey || originalEvent.ctrlKey) &&
@@ -208,7 +208,7 @@ export const shiftKeyOnly = function(mapBrowserEvent) {
*/
export const targetNotEditable = function(mapBrowserEvent) {
const target = mapBrowserEvent.originalEvent.target;
const tagName = target.tagName;
const tagName = /** @type {Element} */ (target).tagName;
return (
tagName !== 'INPUT' &&
tagName !== 'SELECT' &&
@@ -224,11 +224,10 @@ export const targetNotEditable = function(mapBrowserEvent) {
* @api
*/
export const mouseOnly = function(mapBrowserEvent) {
assert(mapBrowserEvent.pointerEvent, 56); // mapBrowserEvent must originate from a pointer event
const pointerEvent = /** @type {import("../MapBrowserPointerEvent").default} */ (mapBrowserEvent).pointerEvent;
assert(pointerEvent !== undefined, 56); // mapBrowserEvent must originate from a pointer event
// see http://www.w3.org/TR/pointerevents/#widl-PointerEvent-pointerType
return (
/** @type {import("../MapBrowserEvent.js").default} */ (mapBrowserEvent).pointerEvent.pointerType == 'mouse'
);
return pointerEvent.pointerType == 'mouse';
};
@@ -242,6 +241,7 @@ export const mouseOnly = function(mapBrowserEvent) {
* @api
*/
export const primaryAction = function(mapBrowserEvent) {
const pointerEvent = mapBrowserEvent.pointerEvent;
const pointerEvent = /** @type {import("../MapBrowserPointerEvent").default} */ (mapBrowserEvent).pointerEvent;
assert(pointerEvent !== undefined, 56); // mapBrowserEvent must originate from a pointer event
return pointerEvent.isPrimary && pointerEvent.button === 0;
};

View File

@@ -165,7 +165,7 @@ export function containsXY(extent, x, y) {
* Get the relationship between a coordinate and extent.
* @param {Extent} extent The extent.
* @param {import("./coordinate.js").Coordinate} coordinate The coordinate.
* @return {import("./extent/Relationship.js").default} The relationship (bitwise compare with
* @return {Relationship} The relationship (bitwise compare with
* import("./extent/Relationship.js").Relationship).
*/
export function coordinateRelationship(extent, coordinate) {
@@ -478,7 +478,7 @@ export function getCenter(extent) {
/**
* Get a corner coordinate of an extent.
* @param {Extent} extent Extent.
* @param {import("./extent/Corner.js").default} corner Corner.
* @param {Corner} corner Corner.
* @return {import("./coordinate.js").Coordinate} Corner coordinate.
*/
export function getCorner(extent, corner) {
@@ -494,9 +494,7 @@ export function getCorner(extent, corner) {
} else {
assert(false, 13); // Invalid corner
}
return (
/** @type {!import("./coordinate.js").Coordinate} */ (coordinate)
);
return coordinate;
}

View File

@@ -4,7 +4,6 @@
import {VOID} from './functions.js';
import FormatType from './format/FormatType.js';
/**
* {@link module:ol/source/Vector} sources use a function of this type to
* load features.
@@ -17,7 +16,7 @@ import FormatType from './format/FormatType.js';
*
* The function is responsible for loading the features and adding them to the
* source.
* @typedef {function(this:import("./source/Vector.js").default, import("./extent.js").Extent, number,
* @typedef {function(this:(import("./source/Vector").default|import("./VectorTile.js").default), import("./extent.js").Extent, number,
* import("./proj/Projection.js").default)} FeatureLoader
* @api
*/
@@ -39,10 +38,10 @@ import FormatType from './format/FormatType.js';
/**
* @param {string|FeatureUrlFunction} url Feature URL service.
* @param {import("./format/Feature.js").default} format Feature format.
* @param {function(this:import("./VectorTile.js").default, Array<import("./Feature.js").default>, import("./proj/Projection.js").default, import("./extent.js").Extent)|function(this:import("./source/Vector.js").default, Array<import("./Feature.js").default>)} success
* @param {function(this:import("./VectorTile.js").default, Array<import("./Feature.js").default>, import("./proj/Projection.js").default, import("./extent.js").Extent)|function(this:import("./source/Vector").default, Array<import("./Feature.js").default>)} success
* Function called with the loaded features and optionally with the data
* projection. Called with the vector tile or source as `this`.
* @param {function(this:import("./VectorTile.js").default)|function(this:import("./source/Vector.js").default)} failure
* @param {function(this:import("./VectorTile.js").default)|function(this:import("./source/Vector").default)} failure
* Function called when loading failed. Called with the vector tile or
* source as `this`.
* @return {FeatureLoader} The feature loader.
@@ -53,7 +52,7 @@ export function loadFeaturesXhr(url, format, success, failure) {
* @param {import("./extent.js").Extent} extent Extent.
* @param {number} resolution Resolution.
* @param {import("./proj/Projection.js").default} projection Projection.
* @this {import("./source/Vector.js").default|import("./VectorTile.js").default}
* @this {import("./source/Vector").default|import("./VectorTile.js").default}
*/
function(extent, resolution, projection) {
const xhr = new XMLHttpRequest();
@@ -121,9 +120,12 @@ export function xhr(url, format) {
* @param {Array<import("./Feature.js").default>} features The loaded features.
* @param {import("./proj/Projection.js").default} dataProjection Data
* projection.
* @this {import("./source/Vector.js").default}
* @this {import("./source/Vector").default|import("./VectorTile.js").default}
*/
function(features, dataProjection) {
this.addFeatures(features);
const sourceOrTile = /** @type {?} */ (this);
if (typeof sourceOrTile.addFeatures === 'function') {
/** @type {import("./source/Vector").default} */ (sourceOrTile).addFeatures(features);
}
}, /* FIXME handle error */ VOID);
}

View File

@@ -17,9 +17,31 @@ import Point from '../geom/Point.js';
import Polygon from '../geom/Polygon.js';
import {deflateCoordinates} from '../geom/flat/deflate.js';
import {linearRingIsClockwise} from '../geom/flat/orient.js';
import {assign, isEmpty} from '../obj.js';
import {isEmpty} from '../obj.js';
import {get as getProjection} from '../proj.js';
/**
* @typedef {import("arcgis-rest-api").Feature} EsriJSONFeature
* @typedef {import("arcgis-rest-api").FeatureSet} EsriJSONFeatureSet
* @typedef {import("arcgis-rest-api").Geometry} EsriJSONGeometry
* @typedef {import("arcgis-rest-api").Point} EsriJSONPoint
* @typedef {import("arcgis-rest-api").Polyline} EsriJSONPolyline
* @typedef {import("arcgis-rest-api").Polygon} EsriJSONPolygon
* @typedef {import("arcgis-rest-api").Multipoint} EsriJSONMultipoint
* @typedef {import("arcgis-rest-api").HasZM} EsriJSONHasZM
* @typedef {import("arcgis-rest-api").Position} EsriJSONPosition
* @typedef {import("arcgis-rest-api").SpatialReferenceWkid} EsriJSONSpatialReferenceWkid
*/
/**
* @typedef {Object} EsriJSONMultiPolygon
* @property {Array<Array<Array<Array<number>>>>} rings Rings for the MultiPolygon.
* @property {boolean} [hasM] If the polygon coordinates have an M value.
* @property {boolean} [hasZ] If the polygon coordinates have a Z value.
* @property {EsriJSONSpatialReferenceWkid} [spatialReference] The coordinate reference system.
*/
/**
* @const
@@ -104,13 +126,12 @@ class EsriJSON extends JSONFeature {
* @inheritDoc
*/
readFeaturesFromObject(object, opt_options) {
const esriJSONObject = /** @type {EsriJSONObject} */ (object);
const options = opt_options ? opt_options : {};
if (esriJSONObject.features) {
const esriJSONFeatureCollection = /** @type {EsriJSONFeatureCollection} */ (object);
if (object['features']) {
const esriJSONFeatureSet = /** @type {EsriJSONFeatureSet} */ (object);
/** @type {Array<import("../Feature.js").default>} */
const features = [];
const esriJSONFeatures = esriJSONFeatureCollection.features;
const esriJSONFeatures = esriJSONFeatureSet.features;
options.idField = object.objectIdFieldName;
for (let i = 0, ii = esriJSONFeatures.length; i < ii; ++i) {
features.push(this.readFeatureFromObject(esriJSONFeatures[i], options));
@@ -132,9 +153,9 @@ class EsriJSON extends JSONFeature {
* @inheritDoc
*/
readProjectionFromObject(object) {
const esriJSONObject = /** @type {EsriJSONObject} */ (object);
if (esriJSONObject.spatialReference && esriJSONObject.spatialReference.wkid) {
const crs = esriJSONObject.spatialReference.wkid;
if (object['spatialReference'] && object['spatialReference']['wkid'] !== undefined) {
const spatialReference = /** @type {EsriJSONSpatialReferenceWkid} */ (object['spatialReference']);
const crs = spatialReference.wkid;
return getProjection('EPSG:' + crs);
} else {
return null;
@@ -170,8 +191,8 @@ class EsriJSON extends JSONFeature {
if (geometry) {
object['geometry'] = writeGeometry(geometry, opt_options);
if (opt_options && opt_options.featureProjection) {
object['geometry']['spatialReference'] = /** @type {EsriJSONCRS} */({
wkid: getProjection(opt_options.featureProjection).getCode().split(':').pop()
object['geometry']['spatialReference'] = /** @type {EsriJSONSpatialReferenceWkid} */({
wkid: Number(getProjection(opt_options.featureProjection).getCode().split(':').pop())
});
}
}
@@ -200,7 +221,7 @@ class EsriJSON extends JSONFeature {
for (let i = 0, ii = features.length; i < ii; ++i) {
objects.push(this.writeFeatureObject(features[i], opt_options));
}
return /** @type {EsriJSONFeatureCollection} */ ({
return /** @type {EsriJSONFeatureSet} */ ({
'features': objects
});
}
@@ -218,26 +239,27 @@ function readGeometry(object, opt_options) {
}
/** @type {import("../geom/GeometryType.js").default} */
let type;
if (typeof object.x === 'number' && typeof object.y === 'number') {
if (typeof object['x'] === 'number' && typeof object['y'] === 'number') {
type = GeometryType.POINT;
} else if (object.points) {
} else if (object['points']) {
type = GeometryType.MULTI_POINT;
} else if (object.paths) {
if (object.paths.length === 1) {
} else if (object['paths']) {
const esriJSONPolyline = /** @type {EsriJSONPolyline} */ (object);
if (esriJSONPolyline.paths.length === 1) {
type = GeometryType.LINE_STRING;
} else {
type = GeometryType.MULTI_LINE_STRING;
}
} else if (object.rings) {
const layout = getGeometryLayout(object);
const rings = convertRings(object.rings, layout);
object = /** @type {EsriJSONGeometry} */(assign({}, object));
} else if (object['rings']) {
const esriJSONPolygon = /** @type {EsriJSONPolygon} */ (object);
const layout = getGeometryLayout(esriJSONPolygon);
const rings = convertRings(esriJSONPolygon.rings, layout);
if (rings.length === 1) {
type = GeometryType.POLYGON;
object.rings = rings[0];
object['rings'] = rings[0];
} else {
type = GeometryType.MULTI_POLYGON;
object.rings = rings;
object['rings'] = rings;
}
}
const geometryReader = GEOMETRY_READERS[type];
@@ -254,7 +276,7 @@ function readGeometry(object, opt_options) {
* Logic inspired by: https://github.com/Esri/terraformer-arcgis-parser
* @param {Array<!Array<!Array<number>>>} rings Rings.
* @param {import("../geom/GeometryLayout.js").default} layout Geometry layout.
* @return {Array<!Array<!Array<number>>>} Transformed rings.
* @return {Array<!Array<!Array<!Array<number>>>>} Transformed rings.
*/
function convertRings(rings, layout) {
const flatRing = [];
@@ -301,7 +323,7 @@ function convertRings(rings, layout) {
/**
* @param {EsriJSONGeometry} object Object.
* @param {EsriJSONPoint} object Object.
* @return {import("../geom/Geometry.js").default} Point.
*/
function readPointGeometry(object) {
@@ -323,7 +345,7 @@ function readPointGeometry(object) {
/**
* @param {EsriJSONGeometry} object Object.
* @param {EsriJSONPolyline} object Object.
* @return {import("../geom/Geometry.js").default} LineString.
*/
function readLineStringGeometry(object) {
@@ -333,7 +355,7 @@ function readLineStringGeometry(object) {
/**
* @param {EsriJSONGeometry} object Object.
* @param {EsriJSONPolyline} object Object.
* @return {import("../geom/Geometry.js").default} MultiLineString.
*/
function readMultiLineStringGeometry(object) {
@@ -343,7 +365,7 @@ function readMultiLineStringGeometry(object) {
/**
* @param {EsriJSONGeometry} object Object.
* @param {EsriJSONHasZM} object Object.
* @return {import("../geom/GeometryLayout.js").default} The geometry layout to use.
*/
function getGeometryLayout(object) {
@@ -360,7 +382,7 @@ function getGeometryLayout(object) {
/**
* @param {EsriJSONGeometry} object Object.
* @param {EsriJSONMultipoint} object Object.
* @return {import("../geom/Geometry.js").default} MultiPoint.
*/
function readMultiPointGeometry(object) {
@@ -370,19 +392,17 @@ function readMultiPointGeometry(object) {
/**
* @param {EsriJSONGeometry} object Object.
* @param {EsriJSONMultiPolygon} object Object.
* @return {import("../geom/Geometry.js").default} MultiPolygon.
*/
function readMultiPolygonGeometry(object) {
const layout = getGeometryLayout(object);
return new MultiPolygon(
/** @type {Array<Array<Array<Array<number>>>>} */(object.rings),
layout);
return new MultiPolygon(object.rings, layout);
}
/**
* @param {EsriJSONGeometry} object Object.
* @param {EsriJSONPolygon} object Object.
* @return {import("../geom/Geometry.js").default} Polygon.
*/
function readPolygonGeometry(object) {
@@ -452,13 +472,14 @@ function getHasZM(geometry) {
* @return {EsriJSONPolyline} EsriJSON geometry.
*/
function writeLineStringGeometry(geometry, opt_options) {
const hasZM = getHasZM(/** @type {import("../geom/LineString.js").default} */(geometry));
const lineString = /** @type {import("../geom/LineString.js").default} */ (geometry);
const hasZM = getHasZM(lineString);
return (
/** @type {EsriJSONPolyline} */ {
hasZ: hasZM.hasZ,
hasM: hasZM.hasM,
paths: [
/** @type {import("../geom/LineString.js").default} */ (geometry).getCoordinates()
/** @type {Array<EsriJSONPosition>} */ (lineString.getCoordinates())
]
}
);
@@ -471,13 +492,14 @@ function writeLineStringGeometry(geometry, opt_options) {
* @return {EsriJSONPolygon} EsriJSON geometry.
*/
function writePolygonGeometry(geometry, opt_options) {
const polygon = /** @type {import("../geom/Polygon.js").default} */ (geometry);
// Esri geometries use the left-hand rule
const hasZM = getHasZM(/** @type {import("../geom/Polygon.js").default} */(geometry));
const hasZM = getHasZM(polygon);
return (
/** @type {EsriJSONPolygon} */ {
hasZ: hasZM.hasZ,
hasM: hasZM.hasM,
rings: /** @type {import("../geom/Polygon.js").default} */ (geometry).getCoordinates(false)
rings: /** @type {Array<Array<EsriJSONPosition>>} */ (polygon.getCoordinates(false))
}
);
}
@@ -489,12 +511,13 @@ function writePolygonGeometry(geometry, opt_options) {
* @return {EsriJSONPolyline} EsriJSON geometry.
*/
function writeMultiLineStringGeometry(geometry, opt_options) {
const hasZM = getHasZM(/** @type {import("../geom/MultiLineString.js").default} */(geometry));
const multiLineString = /** @type {import("../geom/MultiLineString.js").default} */ (geometry);
const hasZM = getHasZM(multiLineString);
return (
/** @type {EsriJSONPolyline} */ {
hasZ: hasZM.hasZ,
hasM: hasZM.hasM,
paths: /** @type {import("../geom/MultiLineString.js").default} */ (geometry).getCoordinates()
paths: /** @type {Array<Array<EsriJSONPosition>>} */ (multiLineString.getCoordinates())
}
);
}
@@ -506,12 +529,13 @@ function writeMultiLineStringGeometry(geometry, opt_options) {
* @return {EsriJSONMultipoint} EsriJSON geometry.
*/
function writeMultiPointGeometry(geometry, opt_options) {
const hasZM = getHasZM(/** @type {import("../geom/MultiPoint.js").default} */(geometry));
const multiPoint = /** @type {import("../geom/MultiPoint.js").default} */ (geometry);
const hasZM = getHasZM(multiPoint);
return (
/** @type {EsriJSONMultipoint} */ {
hasZ: hasZM.hasZ,
hasM: hasZM.hasM,
points: /** @type {import("../geom/MultiPoint.js").default} */ (geometry).getCoordinates()
points: /** @type {Array<EsriJSONPosition>} */ (multiPoint.getCoordinates())
}
);
}

View File

@@ -1,8 +1,8 @@
/**
* @module ol/format/Feature
*/
import Geometry from '../geom/Geometry.js';
import {assign} from '../obj.js';
import {abstract} from '../util.js';
import {get as getProjection, equivalent as equivalentProjection, transformExtent} from '../proj.js';
@@ -123,7 +123,9 @@ class FeatureFormat {
* @abstract
* @return {import("./FormatType.js").default} Format.
*/
getType() {}
getType() {
return abstract();
}
/**
* Read a single feature from a source.
@@ -131,9 +133,11 @@ class FeatureFormat {
* @abstract
* @param {Document|Node|Object|string} source Source.
* @param {ReadOptions=} opt_options Read options.
* @return {import("../Feature.js").default} Feature.
* @return {import("../Feature.js").FeatureLike} Feature.
*/
readFeature(source, opt_options) {}
readFeature(source, opt_options) {
return abstract();
}
/**
* Read all features from a source.
@@ -141,9 +145,11 @@ class FeatureFormat {
* @abstract
* @param {Document|Node|ArrayBuffer|Object|string} source Source.
* @param {ReadOptions=} opt_options Read options.
* @return {Array<import("../Feature.js").default>} Features.
* @return {Array<import("../Feature.js").FeatureLike>} Features.
*/
readFeatures(source, opt_options) {}
readFeatures(source, opt_options) {
return abstract();
}
/**
* Read a single geometry from a source.
@@ -151,9 +157,11 @@ class FeatureFormat {
* @abstract
* @param {Document|Node|Object|string} source Source.
* @param {ReadOptions=} opt_options Read options.
* @return {Geometry} Geometry.
* @return {import("../geom/Geometry.js").default} Geometry.
*/
readGeometry(source, opt_options) {}
readGeometry(source, opt_options) {
return abstract();
}
/**
* Read the projection from a source.
@@ -162,7 +170,9 @@ class FeatureFormat {
* @param {Document|Node|Object|string} source Source.
* @return {import("../proj/Projection.js").default} Projection.
*/
readProjection(source) {}
readProjection(source) {
return abstract();
}
/**
* Encode a feature in this format.
@@ -172,7 +182,9 @@ class FeatureFormat {
* @param {WriteOptions=} opt_options Write options.
* @return {string} Result.
*/
writeFeature(feature, opt_options) {}
writeFeature(feature, opt_options) {
return abstract();
}
/**
* Encode an array of features in this format.
@@ -182,26 +194,30 @@ class FeatureFormat {
* @param {WriteOptions=} opt_options Write options.
* @return {string} Result.
*/
writeFeatures(features, opt_options) {}
writeFeatures(features, opt_options) {
return abstract();
}
/**
* Write a single geometry in this format.
*
* @abstract
* @param {Geometry} geometry Geometry.
* @param {import("../geom/Geometry.js").default} geometry Geometry.
* @param {WriteOptions=} opt_options Write options.
* @return {string} Result.
*/
writeGeometry(geometry, opt_options) {}
writeGeometry(geometry, opt_options) {
return abstract();
}
}
export default FeatureFormat;
/**
* @param {Geometry|import("../extent.js").Extent} geometry Geometry.
* @param {import("../geom/Geometry.js").default|import("../extent.js").Extent} geometry Geometry.
* @param {boolean} write Set to true for writing, false for reading.
* @param {(WriteOptions|ReadOptions)=} opt_options Options.
* @return {Geometry|import("../extent.js").Extent} Transformed geometry.
* @return {import("../geom/Geometry.js").default|import("../extent.js").Extent} Transformed geometry.
*/
export function transformWithOptions(geometry, write, opt_options) {
const featureProjection = opt_options ?
@@ -209,28 +225,29 @@ export function transformWithOptions(geometry, write, opt_options) {
const dataProjection = opt_options ?
getProjection(opt_options.dataProjection) : null;
/**
* @type {Geometry|import("../extent.js").Extent}
* @type {import("../geom/Geometry.js").default|import("../extent.js").Extent}
*/
let transformed;
if (featureProjection && dataProjection &&
!equivalentProjection(featureProjection, dataProjection)) {
if (geometry instanceof Geometry) {
transformed = (write ? geometry.clone() : geometry).transform(
write ? featureProjection : dataProjection,
write ? dataProjection : featureProjection);
} else {
if (Array.isArray(geometry)) {
// FIXME this is necessary because GML treats extents
// as geometries
transformed = transformExtent(
geometry,
dataProjection,
featureProjection);
} else {
transformed = (write ? /** @type {import("../geom/Geometry").default} */ (geometry).clone() : geometry).transform(
write ? featureProjection : dataProjection,
write ? dataProjection : featureProjection);
}
} else {
transformed = geometry;
}
if (write && opt_options && opt_options.decimals !== undefined) {
const power = Math.pow(10, opt_options.decimals);
if (write && opt_options && /** @type {WriteOptions} */ (opt_options).decimals !== undefined &&
!Array.isArray(transformed)) {
const power = Math.pow(10, /** @type {WriteOptions} */ (opt_options).decimals);
// if decimals option on write, round each coordinate appropriately
/**
* @param {Array<number>} coordinates Coordinates.
@@ -243,7 +260,7 @@ export function transformWithOptions(geometry, write, opt_options) {
return coordinates;
};
if (transformed === geometry) {
transformed = transformed.clone();
transformed = /** @type {import("../geom/Geometry").default} */ (geometry).clone();
}
transformed.applyTransform(transform);
}

View File

@@ -5,7 +5,6 @@ import {createOrUpdate} from '../extent.js';
import {transformWithOptions} from '../format/Feature.js';
import GMLBase, {GMLNS} from '../format/GMLBase.js';
import {writeStringTextNode} from '../format/xsd.js';
import Geometry from '../geom/Geometry.js';
import {assign} from '../obj.js';
import {get as getProjection, transformExtent} from '../proj.js';
import {createElementNS, getAllTextContent, makeArrayPusher, makeChildAppender,
@@ -179,7 +178,7 @@ class GML2 extends GMLBase {
writeFeatureElement(node, feature, objectStack) {
const fid = feature.getId();
if (fid) {
node.setAttribute('fid', fid);
node.setAttribute('fid', /** @type {string} */ (fid));
}
const context = /** @type {Object} */ (objectStack[objectStack.length - 1]);
const featureNS = context['featureNS'];
@@ -196,7 +195,7 @@ class GML2 extends GMLBase {
if (value !== null) {
keys.push(key);
values.push(value);
if (key == geometryName || value instanceof Geometry) {
if (key == geometryName || typeof /** @type {?} */ (value).getSimplifiedGeometry === 'function') {
if (!(key in context.serializers[featureNS])) {
context.serializers[featureNS][key] = makeChildAppender(
this.writeGeometryElement, this);
@@ -285,7 +284,7 @@ class GML2 extends GMLBase {
writeGeometryElement(node, geometry, objectStack) {
const context = /** @type {import("./Feature.js").WriteOptions} */ (objectStack[objectStack.length - 1]);
const item = assign({}, context);
item.node = node;
item['node'] = node;
let value;
if (Array.isArray(geometry)) {
if (context.dataProjection) {
@@ -588,9 +587,9 @@ class GML2 extends GMLBase {
/**
* @const
* @type {Object<string, Object<string, import("../xml.js").Parser>>}
* @private
* @protected
*/
GML2.prototype.GEOMETRY_FLAT_COORDINATES_PARSERS_ = {
GML2.prototype.GEOMETRY_FLAT_COORDINATES_PARSERS = {
'http://www.opengis.net/gml': {
'coordinates': makeReplacer(GML2.prototype.readFlatCoordinates_)
}
@@ -599,9 +598,9 @@ GML2.prototype.GEOMETRY_FLAT_COORDINATES_PARSERS_ = {
/**
* @const
* @type {Object<string, Object<string, import("../xml.js").Parser>>}
* @private
* @protected
*/
GML2.prototype.FLAT_LINEAR_RINGS_PARSERS_ = {
GML2.prototype.FLAT_LINEAR_RINGS_PARSERS = {
'http://www.opengis.net/gml': {
'innerBoundaryIs': GML2.prototype.innerBoundaryIsParser_,
'outerBoundaryIs': GML2.prototype.outerBoundaryIsParser_
@@ -623,9 +622,9 @@ GML2.prototype.BOX_PARSERS_ = {
/**
* @const
* @type {Object<string, Object<string, import("../xml.js").Parser>>}
* @private
* @protected
*/
GML2.prototype.GEOMETRY_PARSERS_ = {
GML2.prototype.GEOMETRY_PARSERS = {
'http://www.opengis.net/gml': {
'Point': makeReplacer(GMLBase.prototype.readPoint),
'MultiPoint': makeReplacer(

View File

@@ -6,7 +6,6 @@ import {createOrUpdate} from '../extent.js';
import {transformWithOptions} from '../format/Feature.js';
import GMLBase, {GMLNS} from '../format/GMLBase.js';
import {readNonNegativeIntegerString, writeStringTextNode} from '../format/xsd.js';
import Geometry from '../geom/Geometry.js';
import GeometryLayout from '../geom/GeometryLayout.js';
import LineString from '../geom/LineString.js';
import MultiLineString from '../geom/MultiLineString.js';
@@ -183,7 +182,7 @@ class GML3 extends GMLBase {
*/
readPolygonPatch_(node, objectStack) {
return pushParseAndPop([null],
this.FLAT_LINEAR_RINGS_PARSERS_, node, objectStack, this);
this.FLAT_LINEAR_RINGS_PARSERS, node, objectStack, this);
}
/**
@@ -194,7 +193,7 @@ class GML3 extends GMLBase {
*/
readLineStringSegment_(node, objectStack) {
return pushParseAndPop([null],
this.GEOMETRY_FLAT_COORDINATES_PARSERS_,
this.GEOMETRY_FLAT_COORDINATES_PARSERS,
node, objectStack, this);
}
@@ -357,9 +356,9 @@ class GML3 extends GMLBase {
} else if (node.getAttribute('dimension')) {
dim = readNonNegativeIntegerString(
node.getAttribute('dimension'));
} else if (node.parentNode.getAttribute('srsDimension')) {
} else if (/** @type {Element} */ (node.parentNode).getAttribute('srsDimension')) {
dim = readNonNegativeIntegerString(
node.parentNode.getAttribute('srsDimension'));
/** @type {Element} */ (node.parentNode).getAttribute('srsDimension'));
} else if (contextDimension) {
dim = readNonNegativeIntegerString(contextDimension);
}
@@ -387,7 +386,7 @@ class GML3 extends GMLBase {
writePos_(node, value, objectStack) {
const context = objectStack[objectStack.length - 1];
const hasZ = context['hasZ'];
const srsDimension = hasZ ? 3 : 2;
const srsDimension = hasZ ? '3' : '2';
node.setAttribute('srsDimension', srsDimension);
const srsName = context['srsName'];
let axisOrientation = 'enu';
@@ -443,7 +442,7 @@ class GML3 extends GMLBase {
writePosList_(node, value, objectStack) {
const context = objectStack[objectStack.length - 1];
const hasZ = context['hasZ'];
const srsDimension = hasZ ? 3 : 2;
const srsDimension = hasZ ? '3' : '2';
node.setAttribute('srsDimension', srsDimension);
const srsName = context['srsName'];
// only 2d for simple features profile
@@ -731,7 +730,7 @@ class GML3 extends GMLBase {
writeGeometryElement(node, geometry, objectStack) {
const context = /** @type {import("./Feature.js").WriteOptions} */ (objectStack[objectStack.length - 1]);
const item = assign({}, context);
item.node = node;
item['node'] = node;
let value;
if (Array.isArray(geometry)) {
if (context.dataProjection) {
@@ -757,7 +756,7 @@ class GML3 extends GMLBase {
writeFeatureElement(node, feature, objectStack) {
const fid = feature.getId();
if (fid) {
node.setAttribute('fid', fid);
node.setAttribute('fid', /** @type {string} */ (fid));
}
const context = /** @type {Object} */ (objectStack[objectStack.length - 1]);
const featureNS = context['featureNS'];
@@ -774,7 +773,7 @@ class GML3 extends GMLBase {
if (value !== null) {
keys.push(key);
values.push(value);
if (key == geometryName || value instanceof Geometry) {
if (key == geometryName || typeof /** @type {?} */ (value).getSimplifiedGeometry === 'function') {
if (!(key in context.serializers[featureNS])) {
context.serializers[featureNS][key] = makeChildAppender(
this.writeGeometryElement, this);
@@ -805,6 +804,7 @@ class GML3 extends GMLBase {
const context = /** @type {Object} */ (objectStack[objectStack.length - 1]);
const featureType = context['featureType'];
const featureNS = context['featureNS'];
/** @type {Object<string, Object<string, import("../xml.js").Serializer>>} */
const serializers = {};
serializers[featureNS] = {};
serializers[featureNS][featureType] = makeChildAppender(
@@ -828,7 +828,7 @@ class GML3 extends GMLBase {
*/
MULTIGEOMETRY_MEMBER_NODE_FACTORY_(value, objectStack, opt_nodeName) {
const parentNode = objectStack[objectStack.length - 1].node;
return createElementNS('http://www.opengis.net/gml',
return createElementNS(this.namespace,
MULTIGEOMETRY_TO_MEMBER_NODENAME[parentNode.nodeName]);
}
@@ -861,7 +861,7 @@ class GML3 extends GMLBase {
} else {
nodeName = 'Envelope';
}
return createElementNS('http://www.opengis.net/gml',
return createElementNS(this.namespace,
nodeName);
}
@@ -876,7 +876,7 @@ class GML3 extends GMLBase {
*/
writeGeometryNode(geometry, opt_options) {
opt_options = this.adaptOptions(opt_options);
const geom = createElementNS('http://www.opengis.net/gml', 'geom');
const geom = createElementNS(this.namespace, 'geom');
const context = {node: geom, hasZ: this.hasZ, srsName: this.srsName,
curve: this.curve_, surface: this.surface_,
multiSurface: this.multiSurface_, multiCurve: this.multiCurve_};
@@ -898,7 +898,7 @@ class GML3 extends GMLBase {
*/
writeFeaturesNode(features, opt_options) {
opt_options = this.adaptOptions(opt_options);
const node = createElementNS('http://www.opengis.net/gml', 'featureMembers');
const node = createElementNS(this.namespace, 'featureMembers');
node.setAttributeNS(XML_SCHEMA_INSTANCE_URI, 'xsi:schemaLocation', this.schemaLocation);
const context = {
srsName: this.srsName,
@@ -921,9 +921,9 @@ class GML3 extends GMLBase {
/**
* @const
* @type {Object<string, Object<string, import("../xml.js").Parser>>}
* @private
* @protected
*/
GML3.prototype.GEOMETRY_FLAT_COORDINATES_PARSERS_ = {
GML3.prototype.GEOMETRY_FLAT_COORDINATES_PARSERS = {
'http://www.opengis.net/gml': {
'pos': makeReplacer(GML3.prototype.readFlatPos_),
'posList': makeReplacer(GML3.prototype.readFlatPosList_)
@@ -934,9 +934,9 @@ GML3.prototype.GEOMETRY_FLAT_COORDINATES_PARSERS_ = {
/**
* @const
* @type {Object<string, Object<string, import("../xml.js").Parser>>}
* @private
* @protected
*/
GML3.prototype.FLAT_LINEAR_RINGS_PARSERS_ = {
GML3.prototype.FLAT_LINEAR_RINGS_PARSERS = {
'http://www.opengis.net/gml': {
'interior': GML3.prototype.interiorParser_,
'exterior': GML3.prototype.exteriorParser_
@@ -947,9 +947,9 @@ GML3.prototype.FLAT_LINEAR_RINGS_PARSERS_ = {
/**
* @const
* @type {Object<string, Object<string, import("../xml.js").Parser>>}
* @private
* @protected
*/
GML3.prototype.GEOMETRY_PARSERS_ = {
GML3.prototype.GEOMETRY_PARSERS = {
'http://www.opengis.net/gml': {
'Point': makeReplacer(GMLBase.prototype.readPoint),
'MultiPoint': makeReplacer(

386
src/ol/format/GML32.js Normal file
View File

@@ -0,0 +1,386 @@
/**
* @module ol/format/GML32
*/
import GML3 from './GML3.js';
import GMLBase from './GMLBase.js';
import {makeArrayPusher, makeReplacer, makeChildAppender} from '../xml.js';
import {writeStringTextNode} from '../format/xsd.js';
/**
* @classdesc Feature format for reading and writing data in the GML format
* version 3.2.1.
* @api
*/
class GML32 extends GML3 {
/**
* @param {import("./GMLBase.js").Options=} opt_options Optional configuration object.
*/
constructor(opt_options) {
const options = /** @type {import("./GMLBase.js").Options} */ (opt_options ? opt_options : {});
super(options);
/**
* @inheritDoc
*/
this.schemaLocation = options.schemaLocation ?
options.schemaLocation : this.namespace + ' http://schemas.opengis.net/gml/3.2.1/gml.xsd';
}
}
GML32.prototype.namespace = 'http://www.opengis.net/gml/3.2';
/**
* @const
* @type {Object<string, Object<string, import("../xml.js").Parser>>}
* @protected
*/
GML32.prototype.GEOMETRY_FLAT_COORDINATES_PARSERS = {
'http://www.opengis.net/gml/3.2': {
'pos': makeReplacer(GML3.prototype.readFlatPos_),
'posList': makeReplacer(GML3.prototype.readFlatPosList_)
}
};
/**
* @const
* @type {Object<string, Object<string, import("../xml.js").Parser>>}
* @protected
*/
GML32.prototype.FLAT_LINEAR_RINGS_PARSERS = {
'http://www.opengis.net/gml/3.2': {
'interior': GML3.prototype.interiorParser_,
'exterior': GML3.prototype.exteriorParser_
}
};
/**
* @const
* @type {Object<string, Object<string, import("../xml.js").Parser>>}
* @protected
*/
GML32.prototype.GEOMETRY_PARSERS = {
'http://www.opengis.net/gml/3.2': {
'Point': makeReplacer(GMLBase.prototype.readPoint),
'MultiPoint': makeReplacer(
GMLBase.prototype.readMultiPoint),
'LineString': makeReplacer(
GMLBase.prototype.readLineString),
'MultiLineString': makeReplacer(
GMLBase.prototype.readMultiLineString),
'LinearRing': makeReplacer(
GMLBase.prototype.readLinearRing),
'Polygon': makeReplacer(GMLBase.prototype.readPolygon),
'MultiPolygon': makeReplacer(
GMLBase.prototype.readMultiPolygon),
'Surface': makeReplacer(GML32.prototype.readSurface_),
'MultiSurface': makeReplacer(
GML3.prototype.readMultiSurface_),
'Curve': makeReplacer(GML32.prototype.readCurve_),
'MultiCurve': makeReplacer(
GML3.prototype.readMultiCurve_),
'Envelope': makeReplacer(GML32.prototype.readEnvelope_)
}
};
/**
* @const
* @type {Object<string, Object<string, import("../xml.js").Parser>>}
* @private
*/
GML32.prototype.MULTICURVE_PARSERS_ = {
'http://www.opengis.net/gml/3.2': {
'curveMember': makeArrayPusher(
GML3.prototype.curveMemberParser_),
'curveMembers': makeArrayPusher(
GML3.prototype.curveMemberParser_)
}
};
/**
* @const
* @type {Object<string, Object<string, import("../xml.js").Parser>>}
* @private
*/
GML32.prototype.MULTISURFACE_PARSERS_ = {
'http://www.opengis.net/gml/3.2': {
'surfaceMember': makeArrayPusher(
GML3.prototype.surfaceMemberParser_),
'surfaceMembers': makeArrayPusher(
GML3.prototype.surfaceMemberParser_)
}
};
/**
* @const
* @type {Object<string, Object<string, import("../xml.js").Parser>>}
* @private
*/
GML32.prototype.CURVEMEMBER_PARSERS_ = {
'http://www.opengis.net/gml/3.2': {
'LineString': makeArrayPusher(
GMLBase.prototype.readLineString),
'Curve': makeArrayPusher(GML3.prototype.readCurve_)
}
};
/**
* @const
* @type {Object<string, Object<string, import("../xml.js").Parser>>}
* @private
*/
GML32.prototype.SURFACEMEMBER_PARSERS_ = {
'http://www.opengis.net/gml/3.2': {
'Polygon': makeArrayPusher(GMLBase.prototype.readPolygon),
'Surface': makeArrayPusher(GML3.prototype.readSurface_)
}
};
/**
* @const
* @type {Object<string, Object<string, import("../xml.js").Parser>>}
* @private
*/
GML32.prototype.SURFACE_PARSERS_ = {
'http://www.opengis.net/gml/3.2': {
'patches': makeReplacer(GML3.prototype.readPatch_)
}
};
/**
* @const
* @type {Object<string, Object<string, import("../xml.js").Parser>>}
* @private
*/
GML32.prototype.CURVE_PARSERS_ = {
'http://www.opengis.net/gml/3.2': {
'segments': makeReplacer(GML3.prototype.readSegment_)
}
};
/**
* @const
* @type {Object<string, Object<string, import("../xml.js").Parser>>}
* @private
*/
GML32.prototype.ENVELOPE_PARSERS_ = {
'http://www.opengis.net/gml/3.2': {
'lowerCorner': makeArrayPusher(
GML3.prototype.readFlatPosList_),
'upperCorner': makeArrayPusher(
GML3.prototype.readFlatPosList_)
}
};
/**
* @const
* @type {Object<string, Object<string, import("../xml.js").Parser>>}
* @private
*/
GML32.prototype.PATCHES_PARSERS_ = {
'http://www.opengis.net/gml/3.2': {
'PolygonPatch': makeReplacer(
GML3.prototype.readPolygonPatch_)
}
};
/**
* @const
* @type {Object<string, Object<string, import("../xml.js").Parser>>}
* @private
*/
GML32.prototype.SEGMENTS_PARSERS_ = {
'http://www.opengis.net/gml/3.2': {
'LineStringSegment': makeReplacer(
GML3.prototype.readLineStringSegment_)
}
};
/**
* @const
* @type {Object<string, Object<string, import("../xml.js").Parser>>}
* @private
*/
GML32.prototype.MULTIPOINT_PARSERS_ = {
'http://www.opengis.net/gml/3.2': {
'pointMember': makeArrayPusher(
GMLBase.prototype.pointMemberParser_),
'pointMembers': makeArrayPusher(
GMLBase.prototype.pointMemberParser_)
}
};
/**
* @const
* @type {Object<string, Object<string, import("../xml.js").Parser>>}
* @private
*/
GML32.prototype.MULTILINESTRING_PARSERS_ = {
'http://www.opengis.net/gml/3.2': {
'lineStringMember': makeArrayPusher(
GMLBase.prototype.lineStringMemberParser_),
'lineStringMembers': makeArrayPusher(
GMLBase.prototype.lineStringMemberParser_)
}
};
/**
* @const
* @type {Object<string, Object<string, import("../xml.js").Parser>>}
* @private
*/
GML32.prototype.MULTIPOLYGON_PARSERS_ = {
'http://www.opengis.net/gml/3.2': {
'polygonMember': makeArrayPusher(
GMLBase.prototype.polygonMemberParser_),
'polygonMembers': makeArrayPusher(
GMLBase.prototype.polygonMemberParser_)
}
};
/**
* @const
* @type {Object<string, Object<string, import("../xml.js").Parser>>}
* @private
*/
GML32.prototype.POINTMEMBER_PARSERS_ = {
'http://www.opengis.net/gml/3.2': {
'Point': makeArrayPusher(
GMLBase.prototype.readFlatCoordinatesFromNode_)
}
};
/**
* @const
* @type {Object<string, Object<string, import("../xml.js").Parser>>}
* @private
*/
GML32.prototype.LINESTRINGMEMBER_PARSERS_ = {
'http://www.opengis.net/gml/3.2': {
'LineString': makeArrayPusher(
GMLBase.prototype.readLineString)
}
};
/**
* @const
* @type {Object<string, Object<string, import("../xml.js").Parser>>}
* @private
*/
GML32.prototype.POLYGONMEMBER_PARSERS_ = {
'http://www.opengis.net/gml/3.2': {
'Polygon': makeArrayPusher(
GMLBase.prototype.readPolygon)
}
};
/**
* @const
* @type {Object<string, Object<string, import("../xml.js").Parser>>}
* @protected
*/
GML32.prototype.RING_PARSERS = {
'http://www.opengis.net/gml/3.2': {
'LinearRing': makeReplacer(
GMLBase.prototype.readFlatLinearRing_)
}
};
/**
* @type {Object<string, Object<string, import("../xml.js").Serializer>>}
* @private
*/
GML32.prototype.RING_SERIALIZERS_ = {
'http://www.opengis.net/gml/3.2': {
'exterior': makeChildAppender(GML3.prototype.writeRing_),
'interior': makeChildAppender(GML3.prototype.writeRing_)
}
};
/**
* @type {Object<string, Object<string, import("../xml.js").Serializer>>}
* @private
*/
GML32.prototype.ENVELOPE_SERIALIZERS_ = {
'http://www.opengis.net/gml/3.2': {
'lowerCorner': makeChildAppender(writeStringTextNode),
'upperCorner': makeChildAppender(writeStringTextNode)
}
};
/**
* @type {Object<string, Object<string, import("../xml.js").Serializer>>}
* @private
*/
GML32.prototype.SURFACEORPOLYGONMEMBER_SERIALIZERS_ = {
'http://www.opengis.net/gml/3.2': {
'surfaceMember': makeChildAppender(
GML3.prototype.writeSurfaceOrPolygonMember_),
'polygonMember': makeChildAppender(
GML3.prototype.writeSurfaceOrPolygonMember_)
}
};
/**
* @type {Object<string, Object<string, import("../xml.js").Serializer>>}
* @private
*/
GML32.prototype.POINTMEMBER_SERIALIZERS_ = {
'http://www.opengis.net/gml/3.2': {
'pointMember': makeChildAppender(
GML3.prototype.writePointMember_)
}
};
/**
* @type {Object<string, Object<string, import("../xml.js").Serializer>>}
* @private
*/
GML32.prototype.LINESTRINGORCURVEMEMBER_SERIALIZERS_ = {
'http://www.opengis.net/gml/3.2': {
'lineStringMember': makeChildAppender(
GML3.prototype.writeLineStringOrCurveMember_),
'curveMember': makeChildAppender(
GML3.prototype.writeLineStringOrCurveMember_)
}
};
/**
* @type {Object<string, Object<string, import("../xml.js").Serializer>>}
* @private
*/
GML32.prototype.GEOMETRY_SERIALIZERS_ = {
'http://www.opengis.net/gml/3.2': {
'Curve': makeChildAppender(
GML3.prototype.writeCurveOrLineString_),
'MultiCurve': makeChildAppender(
GML3.prototype.writeMultiCurveOrLineString_),
'Point': makeChildAppender(GML32.prototype.writePoint_),
'MultiPoint': makeChildAppender(
GML3.prototype.writeMultiPoint_),
'LineString': makeChildAppender(
GML3.prototype.writeCurveOrLineString_),
'MultiLineString': makeChildAppender(
GML3.prototype.writeMultiCurveOrLineString_),
'LinearRing': makeChildAppender(
GML3.prototype.writeLinearRing_),
'Polygon': makeChildAppender(
GML3.prototype.writeSurfaceOrPolygon_),
'MultiPolygon': makeChildAppender(
GML3.prototype.writeMultiSurfaceOrPolygon_),
'Surface': makeChildAppender(
GML3.prototype.writeSurfaceOrPolygon_),
'MultiSurface': makeChildAppender(
GML3.prototype.writeMultiSurfaceOrPolygon_),
'Envelope': makeChildAppender(
GML3.prototype.writeEnvelope)
}
};
export default GML32;

View File

@@ -71,6 +71,7 @@ const ONLY_WHITESPACE_RE = /^[\s\xa0]*$/;
* gml:MultiPolygon. Since the latter is deprecated in GML 3.
* @property {string} [schemaLocation] Optional schemaLocation to use when
* writing out the GML, this will override the default provided.
* @property {boolean} [hasZ=false] If coordinates have a Z value.
*/
@@ -122,11 +123,10 @@ class GMLBase extends XMLFeature {
* @type {Object<string, Object<string, Object>>}
*/
this.FEATURE_COLLECTION_PARSERS = {};
this.FEATURE_COLLECTION_PARSERS[GMLNS] = {
'featureMember': makeReplacer(this.readFeaturesInternal),
this.FEATURE_COLLECTION_PARSERS[this.namespace] = {
'featureMember': makeArrayPusher(this.readFeaturesInternal),
'featureMembers': makeReplacer(this.readFeaturesInternal)
};
}
/**
@@ -138,15 +138,9 @@ class GMLBase extends XMLFeature {
const localName = node.localName;
let features = null;
if (localName == 'FeatureCollection') {
if (node.namespaceURI === 'http://www.opengis.net/wfs') {
features = pushParseAndPop([],
this.FEATURE_COLLECTION_PARSERS, node,
objectStack, this);
} else {
features = pushParseAndPop(null,
this.FEATURE_COLLECTION_PARSERS, node,
objectStack, this);
}
features = pushParseAndPop([],
this.FEATURE_COLLECTION_PARSERS, node,
objectStack, this);
} else if (localName == 'featureMembers' || localName == 'featureMember') {
const context = objectStack[0];
let featureType = context['featureType'];
@@ -189,9 +183,11 @@ class GMLBase extends XMLFeature {
featureNS = {};
featureNS[defaultPrefix] = ns;
}
/** @type {Object<string, Object<string, import("../xml.js").Parser>>} */
const parsersNS = {};
const featureTypes = Array.isArray(featureType) ? featureType : [featureType];
for (const p in featureNS) {
/** @type {Object<string, import("../xml.js").Parser>} */
const parsers = {};
for (let i = 0, ii = featureTypes.length; i < ii; ++i) {
const featurePrefix = featureTypes[i].indexOf(':') === -1 ?
@@ -227,7 +223,7 @@ class GMLBase extends XMLFeature {
context['srsName'] = node.firstElementChild.getAttribute('srsName');
context['srsDimension'] = node.firstElementChild.getAttribute('srsDimension');
/** @type {import("../geom/Geometry.js").default} */
const geometry = pushParseAndPop(null, this.GEOMETRY_PARSERS_, node, objectStack, this);
const geometry = pushParseAndPop(null, this.GEOMETRY_PARSERS, node, objectStack, this);
if (geometry) {
return (
/** @type {import("../geom/Geometry.js").default} */ (transformWithOptions(geometry, false, context))
@@ -240,42 +236,77 @@ class GMLBase extends XMLFeature {
/**
* @param {Element} node Node.
* @param {Array<*>} objectStack Object stack.
* @return {Feature} Feature.
* @param {boolean} asFeature whether result should be wrapped as a feature.
* @return {Feature|Object} Feature
*/
readFeatureElement(node, objectStack) {
let n;
const fid = node.getAttribute('fid') || getAttributeNS(node, GMLNS, 'id');
const values = {};
readFeatureElementInternal(node, objectStack, asFeature) {
let geometryName;
for (n = node.firstElementChild; n; n = n.nextElementSibling) {
const values = {};
for (let n = node.firstElementChild; n; n = n.nextElementSibling) {
let value;
const localName = n.localName;
// Assume attribute elements have one child node and that the child
// is a text or CDATA node (to be treated as text).
// Otherwise assume it is a geometry node.
if (n.childNodes.length === 0 ||
(n.childNodes.length === 1 &&
(n.firstChild.nodeType === 3 || n.firstChild.nodeType === 4))) {
let value = getAllTextContent(n, false);
// first, check if it is simple attribute
if (n.childNodes.length === 0
|| (n.childNodes.length === 1 && (n.firstChild.nodeType === 3 || n.firstChild.nodeType === 4))) {
value = getAllTextContent(n, false);
if (ONLY_WHITESPACE_RE.test(value)) {
value = undefined;
}
values[localName] = value;
} else {
// boundedBy is an extent and must not be considered as a geometry
if (localName !== 'boundedBy') {
if (asFeature) {
//if feature, try it as a geometry
value = this.readGeometryElement(n, objectStack);
}
if (!value) { //if not a geometry or not a feature, treat it as a complex attribute
value = this.readFeatureElementInternal(n, objectStack, false);
} else if (localName !== 'boundedBy') {
// boundedBy is an extent and must not be considered as a geometry
geometryName = localName;
}
values[localName] = this.readGeometryElement(n, objectStack);
}
if (values[localName]) {
if (!(values[localName] instanceof Array)) {
values[localName] = [values[localName]];
}
values[localName].push(value);
} else {
values[localName] = value;
}
const len = n.attributes.length;
if (len > 0) {
values[localName] = {_content_: values[localName]};
for (let i = 0; i < len; i++) {
const attName = n.attributes[i].name;
values[localName][attName] = n.attributes[i].value;
}
}
}
const feature = new Feature(values);
if (geometryName) {
feature.setGeometryName(geometryName);
if (!asFeature) {
return values;
} else {
const feature = new Feature(values);
if (geometryName) {
feature.setGeometryName(geometryName);
}
const fid = node.getAttribute('fid') ||
getAttributeNS(node, this.namespace, 'id');
if (fid) {
feature.setId(fid);
}
return feature;
}
if (fid) {
feature.setId(fid);
}
return feature;
}
/**
* @param {Element} node Node.
* @param {Array<*>} objectStack Object stack.
* @return {Feature} Feature.
*/
readFeatureElement(node, objectStack) {
return this.readFeatureElementInternal(node, objectStack, true);
}
/**
@@ -383,7 +414,7 @@ class GMLBase extends XMLFeature {
*/
readFlatLinearRing_(node, objectStack) {
const ring = pushParseAndPop(null,
this.GEOMETRY_FLAT_COORDINATES_PARSERS_, node,
this.GEOMETRY_FLAT_COORDINATES_PARSERS, node,
objectStack, this);
if (ring) {
return ring;
@@ -412,7 +443,7 @@ class GMLBase extends XMLFeature {
readPolygon(node, objectStack) {
/** @type {Array<Array<number>>} */
const flatLinearRings = pushParseAndPop([null],
this.FLAT_LINEAR_RINGS_PARSERS_, node, objectStack, this);
this.FLAT_LINEAR_RINGS_PARSERS, node, objectStack, this);
if (flatLinearRings && flatLinearRings[0]) {
const flatCoordinates = flatLinearRings[0];
const ends = [flatCoordinates.length];
@@ -434,7 +465,7 @@ class GMLBase extends XMLFeature {
* @return {Array<number>} Flat coordinates.
*/
readFlatCoordinatesFromNode_(node, objectStack) {
return pushParseAndPop(null, this.GEOMETRY_FLAT_COORDINATES_PARSERS_, node, objectStack, this);
return pushParseAndPop(null, this.GEOMETRY_FLAT_COORDINATES_PARSERS, node, objectStack, this);
}
/**
@@ -469,6 +500,40 @@ class GMLBase extends XMLFeature {
}
}
GMLBase.prototype.namespace = GMLNS;
/**
* @const
* @type {Object<string, Object<string, import("../xml.js").Parser>>}
* @protected
*/
GMLBase.prototype.FLAT_LINEAR_RINGS_PARSERS = {
'http://www.opengis.net/gml': {}
};
/**
* @const
* @type {Object<string, Object<string, import("../xml.js").Parser>>}
* @protected
*/
GMLBase.prototype.GEOMETRY_FLAT_COORDINATES_PARSERS = {
'http://www.opengis.net/gml': {}
};
/**
* @const
* @type {Object<string, Object<string, import("../xml.js").Parser>>}
* @protected
*/
GMLBase.prototype.GEOMETRY_PARSERS = {
'http://www.opengis.net/gml': {}
};
/**
* @const
* @type {Object<string, Object<string, import("../xml.js").Parser>>}

View File

@@ -7,6 +7,7 @@ import {transformWithOptions} from '../format/Feature.js';
import XMLFeature from '../format/XMLFeature.js';
import {readString, readDecimal, readNonNegativeInteger, readDateTime, writeStringTextNode, writeNonNegativeIntegerTextNode, writeDecimalTextNode, writeDateTimeTextNode} from '../format/xsd.js';
import GeometryLayout from '../geom/GeometryLayout.js';
import GeometryType from '../geom/GeometryType.js';
import LineString from '../geom/LineString.js';
import MultiLineString from '../geom/MultiLineString.js';
import Point from '../geom/Point.js';
@@ -738,8 +739,8 @@ function writeWptType(node, coordinate, objectStack) {
const namespaceURI = parentNode.namespaceURI;
const properties = context['properties'];
//FIXME Projection handling
node.setAttributeNS(null, 'lat', coordinate[1]);
node.setAttributeNS(null, 'lon', coordinate[0]);
node.setAttributeNS(null, 'lat', String(coordinate[1]));
node.setAttributeNS(null, 'lon', String(coordinate[0]));
const geometryLayout = context['geometryLayout'];
switch (geometryLayout) {
case GeometryLayout.XYZM:
@@ -779,12 +780,13 @@ function writeWptType(node, coordinate, objectStack) {
function writeRte(node, feature, objectStack) {
const options = /** @type {import("./Feature.js").WriteOptions} */ (objectStack[0]);
const properties = feature.getProperties();
const context = {node: node, 'properties': properties};
let geometry = feature.getGeometry();
if (geometry) {
geometry = /** @type {LineString} */ (transformWithOptions(geometry, true, options));
context['geometryLayout'] = geometry.getLayout();
properties['rtept'] = geometry.getCoordinates();
const context = {node: node};
context['properties'] = properties;
const geometry = feature.getGeometry();
if (geometry.getType() == GeometryType.LINE_STRING) {
const lineString = /** @type {LineString} */ (transformWithOptions(geometry, true, options));
context['geometryLayout'] = lineString.getLayout();
properties['rtept'] = lineString.getCoordinates();
}
const parentNode = objectStack[objectStack.length - 1].node;
const orderedKeys = RTE_SEQUENCE[parentNode.namespaceURI];
@@ -804,12 +806,12 @@ function writeTrk(node, feature, objectStack) {
const options = /** @type {import("./Feature.js").WriteOptions} */ (objectStack[0]);
const properties = feature.getProperties();
/** @type {import("../xml.js").NodeStackItem} */
const context = {node: node, 'properties': properties};
let geometry = feature.getGeometry();
if (geometry) {
geometry = /** @type {MultiLineString} */
(transformWithOptions(geometry, true, options));
properties['trkseg'] = geometry.getLineStrings();
const context = {node: node};
context['properties'] = properties;
const geometry = feature.getGeometry();
if (geometry.getType() == GeometryType.MULTI_LINE_STRING) {
const multiLineString = /** @type {MultiLineString} */ (transformWithOptions(geometry, true, options));
properties['trkseg'] = multiLineString.getLineStrings();
}
const parentNode = objectStack[objectStack.length - 1].node;
const orderedKeys = TRK_SEQUENCE[parentNode.namespaceURI];
@@ -827,8 +829,9 @@ function writeTrk(node, feature, objectStack) {
*/
function writeTrkSeg(node, lineString, objectStack) {
/** @type {import("../xml.js").NodeStackItem} */
const context = {node: node, 'geometryLayout': lineString.getLayout(),
'properties': {}};
const context = {node: node};
context['geometryLayout'] = lineString.getLayout();
context['properties'] = {};
pushSerializeAndPop(context,
TRKSEG_SERIALIZERS, TRKSEG_NODE_FACTORY,
lineString.getCoordinates(), objectStack);
@@ -844,12 +847,11 @@ function writeWpt(node, feature, objectStack) {
const options = /** @type {import("./Feature.js").WriteOptions} */ (objectStack[0]);
const context = objectStack[objectStack.length - 1];
context['properties'] = feature.getProperties();
let geometry = feature.getGeometry();
if (geometry) {
geometry = /** @type {Point} */
(transformWithOptions(geometry, true, options));
context['geometryLayout'] = geometry.getLayout();
writeWptType(node, geometry.getCoordinates(), objectStack);
const geometry = feature.getGeometry();
if (geometry.getType() == GeometryType.POINT) {
const point = /** @type {Point} */ (transformWithOptions(geometry, true, options));
context['geometryLayout'] = point.getLayout();
writeWptType(node, point.getCoordinates(), objectStack);
}
}

View File

@@ -293,14 +293,14 @@ function readGeometry(object, opt_options) {
throw new Error('Unsupported GeoJSON type: ' + object.type);
}
}
return transformWithOptions(geometry, false, opt_options);
return /** @type {import("../geom/Geometry.js").default} */ (transformWithOptions(geometry, false, opt_options));
}
/**
* @param {GeoJSONGeometryCollection} object Object.
* @param {import("./Feature.js").ReadOptions=} opt_options Read options.
* @return {import("../geom/GeometryCollection.js").default} Geometry collection.
* @return {GeometryCollection} Geometry collection.
*/
function readGeometryCollectionGeometry(object, opt_options) {
const geometries = object['geometries'].map(
@@ -317,7 +317,7 @@ function readGeometryCollectionGeometry(object, opt_options) {
/**
* @param {GeoJSONPoint} object Object.
* @return {import("../geom/Point.js").default} Point.
* @return {Point} Point.
*/
function readPointGeometry(object) {
return new Point(object['coordinates']);
@@ -326,7 +326,7 @@ function readPointGeometry(object) {
/**
* @param {GeoJSONLineString} object Object.
* @return {import("../geom/LineString.js").default} LineString.
* @return {LineString} LineString.
*/
function readLineStringGeometry(object) {
return new LineString(object['coordinates']);
@@ -335,7 +335,7 @@ function readLineStringGeometry(object) {
/**
* @param {GeoJSONMultiLineString} object Object.
* @return {import("../geom/MultiLineString.js").default} MultiLineString.
* @return {MultiLineString} MultiLineString.
*/
function readMultiLineStringGeometry(object) {
return new MultiLineString(object['coordinates']);
@@ -344,7 +344,7 @@ function readMultiLineStringGeometry(object) {
/**
* @param {GeoJSONMultiPoint} object Object.
* @return {import("../geom/MultiPoint.js").default} MultiPoint.
* @return {MultiPoint} MultiPoint.
*/
function readMultiPointGeometry(object) {
return new MultiPoint(object['coordinates']);
@@ -353,7 +353,7 @@ function readMultiPointGeometry(object) {
/**
* @param {GeoJSONMultiPolygon} object Object.
* @return {import("../geom/MultiPolygon.js").default} MultiPolygon.
* @return {MultiPolygon} MultiPolygon.
*/
function readMultiPolygonGeometry(object) {
return new MultiPolygon(object['coordinates']);
@@ -362,7 +362,7 @@ function readMultiPolygonGeometry(object) {
/**
* @param {GeoJSONPolygon} object Object.
* @return {import("../geom/Polygon.js").default} Polygon.
* @return {Polygon} Polygon.
*/
function readPolygonGeometry(object) {
return new Polygon(object['coordinates']);
@@ -375,38 +375,38 @@ function readPolygonGeometry(object) {
* @return {GeoJSONGeometry} GeoJSON geometry.
*/
function writeGeometry(geometry, opt_options) {
geometry = transformWithOptions(geometry, true, opt_options);
geometry = /** @type {import("../geom/Geometry.js").default} */ (transformWithOptions(geometry, true, opt_options));
const type = geometry.getType();
/** @type {GeoJSONGeometry} */
let geoJSON;
switch (type) {
case GeometryType.POINT: {
geoJSON = writePointGeometry(/** @type {import("../geom/Point.js").default} */ (geometry), opt_options);
geoJSON = writePointGeometry(/** @type {Point} */ (geometry), opt_options);
break;
}
case GeometryType.LINE_STRING: {
geoJSON = writeLineStringGeometry(/** @type {import("../geom/LineString.js").default} */ (geometry), opt_options);
geoJSON = writeLineStringGeometry(/** @type {LineString} */ (geometry), opt_options);
break;
}
case GeometryType.POLYGON: {
geoJSON = writePolygonGeometry(/** @type {import("../geom/Polygon.js").default} */ (geometry), opt_options);
geoJSON = writePolygonGeometry(/** @type {Polygon} */ (geometry), opt_options);
break;
}
case GeometryType.MULTI_POINT: {
geoJSON = writeMultiPointGeometry(/** @type {import("../geom/MultiPoint.js").default} */ (geometry), opt_options);
geoJSON = writeMultiPointGeometry(/** @type {MultiPoint} */ (geometry), opt_options);
break;
}
case GeometryType.MULTI_LINE_STRING: {
geoJSON = writeMultiLineStringGeometry(/** @type {import("../geom/MultiLineString.js").default} */ (geometry), opt_options);
geoJSON = writeMultiLineStringGeometry(/** @type {MultiLineString} */ (geometry), opt_options);
break;
}
case GeometryType.MULTI_POLYGON: {
geoJSON = writeMultiPolygonGeometry(/** @type {import("../geom/MultiPolygon.js").default} */ (geometry), opt_options);
geoJSON = writeMultiPolygonGeometry(/** @type {MultiPolygon} */ (geometry), opt_options);
break;
}
case GeometryType.GEOMETRY_COLLECTION: {
geoJSON = writeGeometryCollectionGeometry(/** @type {import("../geom/GeometryCollection.js").default} */ (geometry), opt_options);
geoJSON = writeGeometryCollectionGeometry(/** @type {GeometryCollection} */ (geometry), opt_options);
break;
}
case GeometryType.CIRCLE: {
@@ -425,7 +425,7 @@ function writeGeometry(geometry, opt_options) {
/**
* @param {import("../geom/GeometryCollection.js").default} geometry Geometry.
* @param {GeometryCollection} geometry Geometry.
* @param {import("./Feature.js").WriteOptions=} opt_options Write options.
* @return {GeoJSONGeometryCollection} GeoJSON geometry collection.
*/
@@ -443,7 +443,7 @@ function writeGeometryCollectionGeometry(geometry, opt_options) {
/**
* @param {import("../geom/LineString.js").default} geometry Geometry.
* @param {LineString} geometry Geometry.
* @param {import("./Feature.js").WriteOptions=} opt_options Write options.
* @return {GeoJSONGeometry} GeoJSON geometry.
*/
@@ -456,7 +456,7 @@ function writeLineStringGeometry(geometry, opt_options) {
/**
* @param {import("../geom/MultiLineString.js").default} geometry Geometry.
* @param {MultiLineString} geometry Geometry.
* @param {import("./Feature.js").WriteOptions=} opt_options Write options.
* @return {GeoJSONGeometry} GeoJSON geometry.
*/
@@ -469,7 +469,7 @@ function writeMultiLineStringGeometry(geometry, opt_options) {
/**
* @param {import("../geom/MultiPoint.js").default} geometry Geometry.
* @param {MultiPoint} geometry Geometry.
* @param {import("./Feature.js").WriteOptions=} opt_options Write options.
* @return {GeoJSONGeometry} GeoJSON geometry.
*/
@@ -482,7 +482,7 @@ function writeMultiPointGeometry(geometry, opt_options) {
/**
* @param {import("../geom/MultiPolygon.js").default} geometry Geometry.
* @param {MultiPolygon} geometry Geometry.
* @param {import("./Feature.js").WriteOptions=} opt_options Write options.
* @return {GeoJSONGeometry} GeoJSON geometry.
*/
@@ -499,7 +499,7 @@ function writeMultiPolygonGeometry(geometry, opt_options) {
/**
* @param {import("../geom/Point.js").default} geometry Geometry.
* @param {Point} geometry Geometry.
* @param {import("./Feature.js").WriteOptions=} opt_options Write options.
* @return {GeoJSONGeometry} GeoJSON geometry.
*/
@@ -512,7 +512,7 @@ function writePointGeometry(geometry, opt_options) {
/**
* @param {import("../geom/Polygon.js").default} geometry Geometry.
* @param {Polygon} geometry Geometry.
* @param {import("./Feature.js").WriteOptions=} opt_options Write options.
* @return {GeoJSONGeometry} GeoJSON geometry.
*/

View File

@@ -1,6 +1,7 @@
/**
* @module ol/format/JSONFeature
*/
import {abstract} from '../util.js';
import FeatureFormat from '../format/Feature.js';
import FormatType from '../format/FormatType.js';
@@ -59,7 +60,9 @@ class JSONFeature extends FeatureFormat {
* @protected
* @return {import("../Feature.js").default} Feature.
*/
readFeatureFromObject(object, opt_options) {}
readFeatureFromObject(object, opt_options) {
return abstract();
}
/**
* @abstract
@@ -68,7 +71,9 @@ class JSONFeature extends FeatureFormat {
* @protected
* @return {Array<import("../Feature.js").default>} Features.
*/
readFeaturesFromObject(object, opt_options) {}
readFeaturesFromObject(object, opt_options) {
return abstract();
}
/**
* Read a geometry.
@@ -90,7 +95,9 @@ class JSONFeature extends FeatureFormat {
* @protected
* @return {import("../geom/Geometry.js").default} Geometry.
*/
readGeometryFromObject(object, opt_options) {}
readGeometryFromObject(object, opt_options) {
return abstract();
}
/**
* Read the projection.
@@ -109,7 +116,9 @@ class JSONFeature extends FeatureFormat {
* @protected
* @return {import("../proj/Projection.js").default} Projection.
*/
readProjectionFromObject(object) {}
readProjectionFromObject(object) {
return abstract();
}
/**
* Encode a feature as string.
@@ -129,7 +138,9 @@ class JSONFeature extends FeatureFormat {
* @param {import("./Feature.js").WriteOptions=} opt_options Write options.
* @return {Object} Object.
*/
writeFeatureObject(feature, opt_options) {}
writeFeatureObject(feature, opt_options) {
return abstract();
}
/**
* Encode an array of features as string.
@@ -149,7 +160,9 @@ class JSONFeature extends FeatureFormat {
* @param {import("./Feature.js").WriteOptions=} opt_options Write options.
* @return {Object} Object.
*/
writeFeaturesObject(features, opt_options) {}
writeFeaturesObject(features, opt_options) {
return abstract();
}
/**
* Encode a geometry as string.
@@ -169,7 +182,9 @@ class JSONFeature extends FeatureFormat {
* @param {import("./Feature.js").WriteOptions=} opt_options Write options.
* @return {Object} Object.
*/
writeGeometryObject(geometry, opt_options) {}
writeGeometryObject(geometry, opt_options) {
return abstract();
}
}

View File

@@ -26,7 +26,7 @@ import IconOrigin from '../style/IconOrigin.js';
import Stroke from '../style/Stroke.js';
import Style from '../style/Style.js';
import Text from '../style/Text.js';
import {createElementNS, getAllTextContent, isDocument, isNode, makeArrayExtender,
import {createElementNS, getAllTextContent, isDocument, makeArrayExtender,
makeArrayPusher, makeChildAppender, makeObjectPropertySetter,
makeReplacer, makeSequence, makeSimpleNodeFactory, makeStructureNS,
OBJECT_PROPERTY_NODE_FACTORY, parse, parseNode, pushParseAndPop,
@@ -639,15 +639,15 @@ class KML extends XMLFeature {
* @api
*/
readName(source) {
if (isDocument(source)) {
return this.readNameFromDocument(/** @type {Document} */ (source));
} else if (isNode(source)) {
return this.readNameFromNode(/** @type {Element} */ (source));
if (!source) {
return undefined;
} else if (typeof source === 'string') {
const doc = parse(source);
return this.readNameFromDocument(doc);
} else if (isDocument(source)) {
return this.readNameFromDocument(/** @type {Document} */ (source));
} else {
return undefined;
return this.readNameFromNode(/** @type {Element} */ (source));
}
}
@@ -656,7 +656,7 @@ class KML extends XMLFeature {
* @return {string|undefined} Name.
*/
readNameFromDocument(doc) {
for (let n = doc.firstChild; n; n = n.nextSibling) {
for (let n = /** @type {Node} */ (doc.firstChild); n; n = n.nextSibling) {
if (n.nodeType == Node.ELEMENT_NODE) {
const name = this.readNameFromNode(/** @type {Element} */ (n));
if (name) {
@@ -703,15 +703,15 @@ class KML extends XMLFeature {
*/
readNetworkLinks(source) {
const networkLinks = [];
if (isDocument(source)) {
extend(networkLinks, this.readNetworkLinksFromDocument(
/** @type {Document} */ (source)));
} else if (isNode(source)) {
extend(networkLinks, this.readNetworkLinksFromNode(
/** @type {Element} */ (source)));
} else if (typeof source === 'string') {
if (typeof source === 'string') {
const doc = parse(source);
extend(networkLinks, this.readNetworkLinksFromDocument(doc));
} else if (isDocument(source)) {
extend(networkLinks, this.readNetworkLinksFromDocument(
/** @type {Document} */ (source)));
} else {
extend(networkLinks, this.readNetworkLinksFromNode(
/** @type {Element} */ (source)));
}
return networkLinks;
}
@@ -722,7 +722,7 @@ class KML extends XMLFeature {
*/
readNetworkLinksFromDocument(doc) {
const networkLinks = [];
for (let n = doc.firstChild; n; n = n.nextSibling) {
for (let n = /** @type {Node} */ (doc.firstChild); n; n = n.nextSibling) {
if (n.nodeType == Node.ELEMENT_NODE) {
extend(networkLinks, this.readNetworkLinksFromNode(/** @type {Element} */ (n)));
}
@@ -765,15 +765,15 @@ class KML extends XMLFeature {
*/
readRegion(source) {
const regions = [];
if (isDocument(source)) {
extend(regions, this.readRegionFromDocument(
/** @type {Document} */ (source)));
} else if (isNode(source)) {
extend(regions, this.readRegionFromNode(
/** @type {Element} */ (source)));
} else if (typeof source === 'string') {
if (typeof source === 'string') {
const doc = parse(source);
extend(regions, this.readRegionFromDocument(doc));
} else if (isDocument(source)) {
extend(regions, this.readRegionFromDocument(
/** @type {Document} */ (source)));
} else {
extend(regions, this.readRegionFromNode(
/** @type {Element} */ (source)));
}
return regions;
}
@@ -784,7 +784,7 @@ class KML extends XMLFeature {
*/
readRegionFromDocument(doc) {
const regions = [];
for (let n = doc.firstChild; n; n = n.nextSibling) {
for (let n = /** @type {Node} */ (doc.firstChild); n; n = n.nextSibling) {
if (n.nodeType == Node.ELEMENT_NODE) {
extend(regions, this.readRegionFromNode(/** @type {Element} */ (n)));
}
@@ -838,6 +838,7 @@ class KML extends XMLFeature {
kml.setAttributeNS(XML_SCHEMA_INSTANCE_URI, 'xsi:schemaLocation', SCHEMA_LOCATION);
const /** @type {import("../xml.js").NodeStackItem} */ context = {node: kml};
/** @type {!Object<string, (Array<Feature>|Feature|undefined)>} */
const properties = {};
if (features.length > 1) {
properties['Document'] = features;
@@ -931,7 +932,7 @@ function createFeatureStyleFunction(style, styleUrl, defaultStyle, sharedStyles,
if (drawName) {
name = /** @type {string} */ (feature.get('name'));
drawName = drawName && name;
drawName = drawName && !!name;
}
if (style) {
@@ -1714,11 +1715,13 @@ function readStyle(node, objectStack) {
if (fill !== undefined && !fill) {
fillStyle = null;
}
let imageStyle = /** @type {import("../style/Image.js").default} */
('imageStyle' in styleObject ?
styleObject['imageStyle'] : DEFAULT_IMAGE_STYLE);
if (imageStyle == DEFAULT_NO_IMAGE_STYLE) {
imageStyle = undefined;
let imageStyle;
if ('imageStyle' in styleObject) {
if (styleObject['imageStyle'] != DEFAULT_NO_IMAGE_STYLE) {
imageStyle = styleObject['imageStyle'];
}
} else {
imageStyle = DEFAULT_IMAGE_STYLE;
}
const textStyle = /** @type {Text} */
('textStyle' in styleObject ?
@@ -2073,9 +2076,10 @@ function whenParser(node, objectStack) {
function writeColorTextNode(node, color) {
const rgba = asArray(color);
const opacity = (rgba.length == 4) ? rgba[3] : 1;
/** @type {Array<string|number>} */
const abgr = [opacity * 255, rgba[2], rgba[1], rgba[0]];
for (let i = 0; i < 4; ++i) {
const hex = parseInt(abgr[i], 10).toString(16);
const hex = Math.floor(/** @type {number} */ (abgr[i])).toString(16);
abgr[i] = (hex.length == 1) ? '0' + hex : hex;
}
writeStringTextNode(node, abgr.join(''));
@@ -2666,7 +2670,7 @@ function writePlacemark(node, feature, objectStack) {
// set id
if (feature.getId()) {
node.setAttribute('id', feature.getId());
node.setAttribute('id', /** @type {string} */ (feature.getId()));
}
// serialize properties (properties unknown to KML are not serialized)
@@ -2713,7 +2717,7 @@ function writePlacemark(node, feature, objectStack) {
const options = /** @type {import("./Feature.js").WriteOptions} */ (objectStack[0]);
let geometry = feature.getGeometry();
if (geometry) {
geometry = transformWithOptions(geometry, true, options);
geometry = /** @type {import("../geom/Geometry.js").default} */ (transformWithOptions(geometry, true, options));
}
pushSerializeAndPop(context, PLACEMARK_SERIALIZERS,
GEOMETRY_NODE_FACTORY, [geometry], objectStack);
@@ -2893,7 +2897,7 @@ function writeStyle(node, style, objectStack) {
const strokeStyle = style.getStroke();
const imageStyle = style.getImage();
const textStyle = style.getText();
if (imageStyle instanceof Icon) {
if (imageStyle && typeof /** @type {?} */ (imageStyle).getSrc === 'function') {
properties['IconStyle'] = imageStyle;
}
if (textStyle) {
@@ -2918,8 +2922,8 @@ function writeStyle(node, style, objectStack) {
* @param {Vec2} vec2 Vec2.
*/
function writeVec2(node, vec2) {
node.setAttribute('x', vec2.x);
node.setAttribute('y', vec2.y);
node.setAttribute('x', String(vec2.x));
node.setAttribute('y', String(vec2.y));
node.setAttribute('xunits', vec2.xunits);
node.setAttribute('yunits', vec2.yunits);
}

View File

@@ -5,8 +5,8 @@
import {assert} from '../asserts.js';
import PBF from 'pbf';
import FeatureFormat, {transformWithOptions} from '../format/Feature.js';
import FormatType from '../format/FormatType.js';
import FeatureFormat, {transformWithOptions} from './Feature.js';
import FormatType from './FormatType.js';
import GeometryLayout from '../geom/GeometryLayout.js';
import GeometryType from '../geom/GeometryType.js';
import LineString from '../geom/LineString.js';
@@ -23,17 +23,14 @@ import RenderFeature from '../render/Feature.js';
/**
* @typedef {Object} Options
* @property {function((import("../geom/Geometry.js").default|Object<string,*>)=)|function(GeometryType,Array<number>,(Array<number>|Array<Array<number>>),Object<string,*>,number)} [featureClass]
* Class for features returned by {@link module:ol/format/MVT#readFeatures}. Set to
* {@link module:ol/Feature~Feature} to get full editing and geometry support at the cost of
* decreased rendering performance. The default is {@link module:ol/render/Feature~RenderFeature},
* which is optimized for rendering and hit detection.
* @property {string} [geometryName='geometry'] Geometry name to use when creating
* features.
* @property {string} [layerName='layer'] Name of the feature attribute that
* holds the layer name.
* @property {Array<string>} [layers] Layers to read features from. If not
* provided, features will be read from all layers.
* @property {import("../Feature.js").FeatureClass} [featureClass] Class for features returned by
* {@link module:ol/format/MVT#readFeatures}. Set to {@link module:ol/Feature~Feature} to get full editing and geometry
* support at the cost of decreased rendering performance. The default is
* {@link module:ol/render/Feature~RenderFeature}, which is optimized for rendering and hit detection.
* @property {string} [geometryName='geometry'] Geometry name to use when creating features.
* @property {string} [layerName='layer'] Name of the feature attribute that holds the layer name.
* @property {Array<string>} [layers] Layers to read features from. If not provided, features will be read from all
* layers.
*/
@@ -64,12 +61,9 @@ class MVT extends FeatureFormat {
/**
* @private
* @type {function((import("../geom/Geometry.js").default|Object<string,*>)=)|
* function(GeometryType,Array<number>,
* (Array<number>|Array<Array<number>>),Object<string,*>,number)}
* @type {import("../Feature.js").FeatureClass}
*/
this.featureClass_ = options.featureClass ?
options.featureClass : RenderFeature;
this.featureClass_ = options.featureClass ? options.featureClass : RenderFeature;
/**
* @private
@@ -167,7 +161,7 @@ class MVT extends FeatureFormat {
* @param {Object} pbf PBF
* @param {Object} rawFeature Raw Mapbox feature.
* @param {import("./Feature.js").ReadOptions=} opt_options Read options.
* @return {import("../Feature.js").default|RenderFeature} Feature.
* @return {import("../Feature.js").FeatureLike} Feature.
*/
createFeature_(pbf, rawFeature, opt_options) {
const type = rawFeature.type;
@@ -215,11 +209,13 @@ class MVT extends FeatureFormat {
geometryType === GeometryType.MULTI_LINE_STRING ? new MultiLineString(flatCoordinates, GeometryLayout.XY, ends) :
null;
}
feature = new this.featureClass_();
const ctor = /** @type {typeof import("../Feature.js").default} */ (this.featureClass_);
feature = new ctor();
if (this.geometryName_) {
feature.setGeometryName(this.geometryName_);
}
const geometry = transformWithOptions(geom, false, this.adaptOptions(opt_options));
const geometry = /** @type {import("../geom/Geometry.js").default} */ (transformWithOptions(geom, false,
this.adaptOptions(opt_options)));
feature.setGeometry(geometry);
feature.setId(id);
feature.setProperties(values);
@@ -252,7 +248,7 @@ class MVT extends FeatureFormat {
const pbf = new PBF(/** @type {ArrayBuffer} */ (source));
const pbfLayers = pbf.readFields(layersPBFReader, {});
/** @type {Array<import("../Feature.js").default|RenderFeature>} */
/** @type {Array<import("../Feature.js").FeatureLike>} */
const features = [];
for (const name in pbfLayers) {
if (layers && layers.indexOf(name) == -1) {

View File

@@ -1,6 +1,7 @@
/**
* @module ol/format/TextFeature
*/
import {abstract} from '../util.js';
import FeatureFormat from '../format/Feature.js';
import FormatType from '../format/FormatType.js';
@@ -43,7 +44,9 @@ class TextFeature extends FeatureFormat {
* @protected
* @return {import("../Feature.js").default} Feature.
*/
readFeatureFromText(text, opt_options) {}
readFeatureFromText(text, opt_options) {
return abstract();
}
/**
* Read the features from the source.
@@ -64,7 +67,9 @@ class TextFeature extends FeatureFormat {
* @protected
* @return {Array<import("../Feature.js").default>} Features.
*/
readFeaturesFromText(text, opt_options) {}
readFeaturesFromText(text, opt_options) {
return abstract();
}
/**
* Read the geometry from the source.
@@ -85,7 +90,9 @@ class TextFeature extends FeatureFormat {
* @protected
* @return {import("../geom/Geometry.js").default} Geometry.
*/
readGeometryFromText(text, opt_options) {}
readGeometryFromText(text, opt_options) {
return abstract();
}
/**
* Read the projection from the source.
@@ -126,7 +133,9 @@ class TextFeature extends FeatureFormat {
* @protected
* @return {string} Text.
*/
writeFeatureText(feature, opt_options) {}
writeFeatureText(feature, opt_options) {
return abstract();
}
/**
* Encode an array of features as string.
@@ -147,7 +156,9 @@ class TextFeature extends FeatureFormat {
* @protected
* @return {string} Text.
*/
writeFeaturesText(features, opt_options) {}
writeFeaturesText(features, opt_options) {
return abstract();
}
/**
* Write a single geometry.
@@ -168,7 +179,9 @@ class TextFeature extends FeatureFormat {
* @protected
* @return {string} Text.
*/
writeGeometryText(geometry, opt_options) {}
writeGeometryText(geometry, opt_options) {
return abstract();
}
}

View File

@@ -139,7 +139,7 @@ class TopoJSON extends JSONFeature {
/**
* @const
* @type {Object<string, function(TopoJSONGeometry, Array, ...Array): import("../geom/Geometry.js").default>}
* @type {Object<string, function(TopoJSONGeometry, Array, ...Array=): import("../geom/Geometry.js").default>}
*/
const GEOMETRY_READERS = {
'Point': readPointGeometry,

View File

@@ -8,10 +8,9 @@ import GMLBase, {GMLNS} from '../format/GMLBase.js';
import {and as andFilter, bbox as bboxFilter} from '../format/filter.js';
import XMLFeature from '../format/XMLFeature.js';
import {readNonNegativeIntegerString, readNonNegativeInteger, writeStringTextNode} from '../format/xsd.js';
import Geometry from '../geom/Geometry.js';
import {assign} from '../obj.js';
import {get as getProjection} from '../proj.js';
import {createElementNS, isDocument, isNode, makeArrayPusher, makeChildAppender,
import {createElementNS, isDocument, makeArrayPusher, makeChildAppender,
makeObjectPropertySetter, makeSimpleNodeFactory, parse, parseNode,
pushParseAndPop, pushSerializeAndPop, XML_SCHEMA_INSTANCE_URI} from '../xml.js';
@@ -257,10 +256,15 @@ class WFS extends XMLFeature {
* @inheritDoc
*/
readFeaturesFromNode(node, opt_options) {
const context = /** @type {import("../xml.js").NodeStackItem} */ ({
/** @type {import("../xml.js").NodeStackItem} */
const context = {
node: node
};
assign(context, {
'featureType': this.featureType_,
'featureNS': this.featureNS_
});
assign(context, this.getReadOptions(node, opt_options ? opt_options : {}));
const objectStack = [context];
this.gmlFormat_.FEATURE_COLLECTION_PARSERS[GMLNS][
@@ -283,16 +287,16 @@ class WFS extends XMLFeature {
* @api
*/
readTransactionResponse(source) {
if (isDocument(source)) {
return this.readTransactionResponseFromDocument(
/** @type {Document} */ (source));
} else if (isNode(source)) {
return this.readTransactionResponseFromNode(/** @type {Element} */ (source));
if (!source) {
return undefined;
} else if (typeof source === 'string') {
const doc = parse(source);
return this.readTransactionResponseFromDocument(doc);
} else if (isDocument(source)) {
return this.readTransactionResponseFromDocument(
/** @type {Document} */ (source));
} else {
return undefined;
return this.readTransactionResponseFromNode(/** @type {Element} */ (source));
}
}
@@ -305,17 +309,17 @@ class WFS extends XMLFeature {
* @api
*/
readFeatureCollectionMetadata(source) {
if (isDocument(source)) {
return this.readFeatureCollectionMetadataFromDocument(
/** @type {Document} */ (source));
} else if (isNode(source)) {
return this.readFeatureCollectionMetadataFromNode(
/** @type {Element} */ (source));
if (!source) {
return undefined;
} else if (typeof source === 'string') {
const doc = parse(source);
return this.readFeatureCollectionMetadataFromDocument(doc);
} else if (isDocument(source)) {
return this.readFeatureCollectionMetadataFromDocument(
/** @type {Document} */ (source));
} else {
return undefined;
return this.readFeatureCollectionMetadataFromNode(
/** @type {Element} */ (source));
}
}
@@ -325,7 +329,7 @@ class WFS extends XMLFeature {
* FeatureCollection metadata.
*/
readFeatureCollectionMetadataFromDocument(doc) {
for (let n = doc.firstChild; n; n = n.nextSibling) {
for (let n = /** @type {Node} */ (doc.firstChild); n; n = n.nextSibling) {
if (n.nodeType == Node.ELEMENT_NODE) {
return this.readFeatureCollectionMetadataFromNode(/** @type {Element} */ (n));
}
@@ -353,7 +357,7 @@ class WFS extends XMLFeature {
* @return {TransactionResponse|undefined} Transaction response.
*/
readTransactionResponseFromDocument(doc) {
for (let n = doc.firstChild; n; n = n.nextSibling) {
for (let n = /** @type {Node} */ (doc.firstChild); n; n = n.nextSibling) {
if (n.nodeType == Node.ELEMENT_NODE) {
return this.readTransactionResponseFromNode(/** @type {Element} */ (n));
}
@@ -391,16 +395,16 @@ class WFS extends XMLFeature {
node.setAttribute('outputFormat', options.outputFormat);
}
if (options.maxFeatures !== undefined) {
node.setAttribute('maxFeatures', options.maxFeatures);
node.setAttribute('maxFeatures', String(options.maxFeatures));
}
if (options.resultType) {
node.setAttribute('resultType', options.resultType);
}
if (options.startIndex !== undefined) {
node.setAttribute('startIndex', options.startIndex);
node.setAttribute('startIndex', String(options.startIndex));
}
if (options.count !== undefined) {
node.setAttribute('count', options.count);
node.setAttribute('count', String(options.count));
}
filter = options.filter;
if (options.bbox) {
@@ -419,14 +423,17 @@ class WFS extends XMLFeature {
node.setAttributeNS(XML_SCHEMA_INSTANCE_URI, 'xsi:schemaLocation', this.schemaLocation_);
/** @type {import("../xml.js").NodeStackItem} */
const context = {
node: node,
node: node
};
assign(context, {
'srsName': options.srsName,
'featureNS': options.featureNS ? options.featureNS : this.featureNS_,
'featurePrefix': options.featurePrefix,
'geometryName': options.geometryName,
'filter': filter,
'propertyNames': options.propertyNames ? options.propertyNames : []
};
});
assert(Array.isArray(options.featureTypes),
11); // `options.featureTypes` should be an Array
writeGetFeature(node, /** @type {!Array<string>} */ (options.featureTypes), [context]);
@@ -463,9 +470,9 @@ class WFS extends XMLFeature {
node.setAttributeNS(XML_SCHEMA_INSTANCE_URI, 'xsi:schemaLocation', schemaLocation);
const featurePrefix = options.featurePrefix ? options.featurePrefix : FEATURE_PREFIX;
if (inserts) {
obj = {node: node, 'featureNS': options.featureNS,
obj = assign({node: node}, {'featureNS': options.featureNS,
'featureType': options.featureType, 'featurePrefix': featurePrefix,
'gmlVersion': gmlVersion, 'hasZ': options.hasZ, 'srsName': options.srsName};
'gmlVersion': gmlVersion, 'hasZ': options.hasZ, 'srsName': options.srsName});
assign(obj, baseObj);
pushSerializeAndPop(obj,
TRANSACTION_SERIALIZERS,
@@ -473,9 +480,9 @@ class WFS extends XMLFeature {
objectStack);
}
if (updates) {
obj = {node: node, 'featureNS': options.featureNS,
obj = assign({node: node}, {'featureNS': options.featureNS,
'featureType': options.featureType, 'featurePrefix': featurePrefix,
'gmlVersion': gmlVersion, 'hasZ': options.hasZ, 'srsName': options.srsName};
'gmlVersion': gmlVersion, 'hasZ': options.hasZ, 'srsName': options.srsName});
assign(obj, baseObj);
pushSerializeAndPop(obj,
TRANSACTION_SERIALIZERS,
@@ -505,7 +512,7 @@ class WFS extends XMLFeature {
* @inheritDoc
*/
readProjectionFromDocument(doc) {
for (let n = doc.firstChild; n; n = n.nextSibling) {
for (let n = /** @type {Node} */ (doc.firstChild); n; n = n.nextSibling) {
if (n.nodeType == Node.ELEMENT_NODE) {
return this.readProjectionFromNode(n);
}
@@ -620,7 +627,7 @@ function writeOgcFidFilter(node, fid, objectStack) {
const filter = createElementNS(OGCNS, 'Filter');
const child = createElementNS(OGCNS, 'FeatureId');
filter.appendChild(child);
child.setAttribute('fid', fid);
child.setAttribute('fid', /** @type {string} */ (fid));
node.appendChild(filter);
}
@@ -686,7 +693,7 @@ function writeUpdate(node, feature, objectStack) {
const value = feature.get(keys[i]);
if (value !== undefined) {
let name = keys[i];
if (value instanceof Geometry) {
if (value && typeof /** @type {?} */ (value).getSimplifiedGeometry === 'function') {
name = geometryName;
}
values.push({name: name, value: value});
@@ -717,7 +724,7 @@ function writeProperty(node, pair, objectStack) {
if (pair.value !== undefined && pair.value !== null) {
const value = createElementNS(WFSNS, 'Value');
node.appendChild(value);
if (pair.value instanceof Geometry) {
if (pair.value && typeof /** @type {?} */ (pair.value).getSimplifiedGeometry === 'function') {
if (gmlVersion === 2) {
GML2.prototype.writeGeometryElement(value,
pair.value, objectStack);
@@ -742,7 +749,7 @@ function writeNative(node, nativeElement, objectStack) {
node.setAttribute('vendorId', nativeElement.vendorId);
}
if (nativeElement.safeToIgnore !== undefined) {
node.setAttribute('safeToIgnore', nativeElement.safeToIgnore);
node.setAttribute('safeToIgnore', String(nativeElement.safeToIgnore));
}
if (nativeElement.value !== undefined) {
writeStringTextNode(node, nativeElement.value);

View File

@@ -13,10 +13,10 @@ import MultiPoint from '../geom/MultiPoint.js';
import MultiPolygon from '../geom/MultiPolygon.js';
import Point from '../geom/Point.js';
import Polygon from '../geom/Polygon.js';
import SimpleGeometry from '../geom/SimpleGeometry.js';
/**
* Geometry constructors
* @enum {function (new:import("../geom/Geometry.js").default, Array, GeometryLayout)}
*/
const GeometryConstructor = {
@@ -159,29 +159,32 @@ class Lexer {
*/
nextToken() {
const c = this.nextChar_();
const token = {position: this.index_, value: c};
const position = this.index_;
/** @type {number|string} */
let value = c;
let type;
if (c == '(') {
token.type = TokenType.LEFT_PAREN;
type = TokenType.LEFT_PAREN;
} else if (c == ',') {
token.type = TokenType.COMMA;
type = TokenType.COMMA;
} else if (c == ')') {
token.type = TokenType.RIGHT_PAREN;
type = TokenType.RIGHT_PAREN;
} else if (this.isNumeric_(c) || c == '-') {
token.type = TokenType.NUMBER;
token.value = this.readNumber_();
type = TokenType.NUMBER;
value = this.readNumber_();
} else if (this.isAlpha_(c)) {
token.type = TokenType.TEXT;
token.value = this.readText_();
type = TokenType.TEXT;
value = this.readText_();
} else if (this.isWhiteSpace_(c)) {
return this.nextToken();
} else if (c === '') {
token.type = TokenType.EOF;
type = TokenType.EOF;
} else {
throw new Error('Unexpected character: ' + c);
}
return token;
return {position: position, value: value, type: type};
}
/**
@@ -372,7 +375,7 @@ class Parser {
}
/**
* @return {!Array<!Array<number>>} All points in a polygon.
* @return {!Array<!Array<!Array<number>>>} All points in a polygon.
* @private
*/
parsePolygonText_() {
@@ -409,8 +412,8 @@ class Parser {
}
/**
* @return {!Array<!Array<number>>} All linestring points
* in a multilinestring.
* @return {!Array<!Array<!Array<number>>>} All linestring points
* in a multilinestring.
* @private
*/
parseMultiLineStringText_() {
@@ -426,7 +429,7 @@ class Parser {
}
/**
* @return {!Array<!Array<number>>} All polygon points in a multipolygon.
* @return {!Array<!Array<!Array<!Array<number>>>>} All polygon points in a multipolygon.
* @private
*/
parseMultiPolygonText_() {
@@ -451,7 +454,7 @@ class Parser {
for (let i = 0; i < dimensions; ++i) {
const token = this.token_;
if (this.match(TokenType.NUMBER)) {
coordinates.push(token.value);
coordinates.push(/** @type {number} */ (token.value));
} else {
break;
}
@@ -487,7 +490,7 @@ class Parser {
}
/**
* @return {!Array<!Array<number>>} An array of points.
* @return {!Array<!Array<!Array<number>>>} An array of points.
* @private
*/
parseLineStringTextList_() {
@@ -499,7 +502,7 @@ class Parser {
}
/**
* @return {!Array<!Array<number>>} An array of points.
* @return {!Array<!Array<!Array<!Array<number>>>>} An array of points.
* @private
*/
parsePolygonTextList_() {
@@ -820,7 +823,7 @@ function encodeMultiPolygonGeometry(geom) {
}
/**
* @param {SimpleGeometry} geom SimpleGeometry geometry.
* @param {import("../geom/SimpleGeometry.js").default} geom SimpleGeometry geometry.
* @return {string} Potential dimensional information for WKT type.
*/
function encodeGeometryLayout(geom) {
@@ -853,7 +856,7 @@ const GeometryEncoder = {
/**
* Encode a geometry as WKT.
* @param {import("../geom/Geometry.js").default} geom The geometry to encode.
* @param {!import("../geom/Geometry.js").default} geom The geometry to encode.
* @return {string} WKT string for the geometry.
*/
function encode(geom) {
@@ -861,8 +864,8 @@ function encode(geom) {
const geometryEncoder = GeometryEncoder[type];
const enc = geometryEncoder(geom);
type = type.toUpperCase();
if (geom instanceof SimpleGeometry) {
const dimInfo = encodeGeometryLayout(geom);
if (typeof /** @type {?} */ (geom).getFlatCoordinates === 'function') {
const dimInfo = encodeGeometryLayout(/** @type {import("../geom/SimpleGeometry.js").default} */ (geom));
if (dimInfo.length > 0) {
type += ' ' + dimInfo;
}

View File

@@ -100,10 +100,12 @@ class WMSGetFeatureInfo extends XMLFeature {
if (layer.nodeType !== Node.ELEMENT_NODE) {
continue;
}
const layerElement = /** @type {Element} */ (layer);
const context = objectStack[0];
const toRemove = layerIdentifier;
const layerName = layer.localName.replace(toRemove, '');
const layerName = layerElement.localName.replace(toRemove, '');
if (this.layers_ && !includes(this.layers_, layerName)) {
continue;
@@ -115,14 +117,15 @@ class WMSGetFeatureInfo extends XMLFeature {
context['featureType'] = featureType;
context['featureNS'] = this.featureNS_;
/** @type {Object<string, import("../xml.js").Parser>} */
const parsers = {};
parsers[featureType] = makeArrayPusher(
this.gmlFormat_.readFeatureElement, this.gmlFormat_);
const parsersNS = makeStructureNS(
[context['featureNS'], null], parsers);
layer.setAttribute('namespaceURI', this.featureNS_);
layerElement.setAttribute('namespaceURI', this.featureNS_);
const layerFeatures = pushParseAndPop(
[], parsersNS, layer, objectStack, this.gmlFormat_);
[], parsersNS, layerElement, objectStack, this.gmlFormat_);
if (layerFeatures) {
extend(features, layerFeatures);
}

View File

@@ -44,7 +44,7 @@ const PARSERS = makeStructureNS(
* @classdesc
* Format for reading WMTS capabilities data.
*
* @api
* @api
*/
class WMTSCapabilities extends XML {
constructor() {

View File

@@ -1,7 +1,7 @@
/**
* @module ol/format/XML
*/
import {isDocument, isNode, parse} from '../xml.js';
import {isDocument, parse} from '../xml.js';
/**
* @classdesc
@@ -18,15 +18,15 @@ class XML {
* @api
*/
read(source) {
if (isDocument(source)) {
return this.readFromDocument(/** @type {Document} */ (source));
} else if (isNode(source)) {
return this.readFromNode(/** @type {Element} */ (source));
if (!source) {
return null;
} else if (typeof source === 'string') {
const doc = parse(source);
return this.readFromDocument(doc);
} else if (isDocument(source)) {
return this.readFromDocument(/** @type {Document} */ (source));
} else {
return null;
return this.readFromNode(/** @type {Element} */ (source));
}
}

View File

@@ -1,10 +1,11 @@
/**
* @module ol/format/XMLFeature
*/
import {abstract} from '../util.js';
import {extend} from '../array.js';
import FeatureFormat from '../format/Feature.js';
import FormatType from '../format/FormatType.js';
import {isDocument, isNode, parse} from '../xml.js';
import {isDocument, parse} from '../xml.js';
/**
* @classdesc
@@ -41,15 +42,15 @@ class XMLFeature extends FeatureFormat {
* @api
*/
readFeature(source, opt_options) {
if (isDocument(source)) {
return this.readFeatureFromDocument(/** @type {Document} */ (source), opt_options);
} else if (isNode(source)) {
return this.readFeatureFromNode(/** @type {Node} */ (source), opt_options);
if (!source) {
return null;
} else if (typeof source === 'string') {
const doc = parse(source);
return this.readFeatureFromDocument(doc, opt_options);
} else if (isDocument(source)) {
return this.readFeatureFromDocument(/** @type {Document} */ (source), opt_options);
} else {
return null;
return this.readFeatureFromNode(/** @type {Node} */ (source), opt_options);
}
}
@@ -85,16 +86,16 @@ class XMLFeature extends FeatureFormat {
* @api
*/
readFeatures(source, opt_options) {
if (isDocument(source)) {
return this.readFeaturesFromDocument(
/** @type {Document} */ (source), opt_options);
} else if (isNode(source)) {
return this.readFeaturesFromNode(/** @type {Node} */ (source), opt_options);
if (!source) {
return [];
} else if (typeof source === 'string') {
const doc = parse(source);
return this.readFeaturesFromDocument(doc, opt_options);
} else if (isDocument(source)) {
return this.readFeaturesFromDocument(
/** @type {Document} */ (source), opt_options);
} else {
return [];
return this.readFeaturesFromNode(/** @type {Node} */ (source), opt_options);
}
}
@@ -107,7 +108,7 @@ class XMLFeature extends FeatureFormat {
readFeaturesFromDocument(doc, opt_options) {
/** @type {Array<import("../Feature.js").default>} */
const features = [];
for (let n = doc.firstChild; n; n = n.nextSibling) {
for (let n = /** @type {Node} */ (doc.firstChild); n; n = n.nextSibling) {
if (n.nodeType == Node.ELEMENT_NODE) {
extend(features, this.readFeaturesFromNode(n, opt_options));
}
@@ -122,22 +123,24 @@ class XMLFeature extends FeatureFormat {
* @protected
* @return {Array<import("../Feature.js").default>} Features.
*/
readFeaturesFromNode(node, opt_options) {}
readFeaturesFromNode(node, opt_options) {
return abstract();
}
/**
* @inheritDoc
*/
readGeometry(source, opt_options) {
if (isDocument(source)) {
return this.readGeometryFromDocument(
/** @type {Document} */ (source), opt_options);
} else if (isNode(source)) {
return this.readGeometryFromNode(/** @type {Node} */ (source), opt_options);
if (!source) {
return null;
} else if (typeof source === 'string') {
const doc = parse(source);
return this.readGeometryFromDocument(doc, opt_options);
} else if (isDocument(source)) {
return this.readGeometryFromDocument(
/** @type {Document} */ (source), opt_options);
} else {
return null;
return this.readGeometryFromNode(/** @type {Node} */ (source), opt_options);
}
}
@@ -169,15 +172,15 @@ class XMLFeature extends FeatureFormat {
* @api
*/
readProjection(source) {
if (isDocument(source)) {
return this.readProjectionFromDocument(/** @type {Document} */ (source));
} else if (isNode(source)) {
return this.readProjectionFromNode(/** @type {Node} */ (source));
if (!source) {
return null;
} else if (typeof source === 'string') {
const doc = parse(source);
return this.readProjectionFromDocument(doc);
} else if (isDocument(source)) {
return this.readProjectionFromDocument(/** @type {Document} */ (source));
} else {
return null;
return this.readProjectionFromNode(/** @type {Node} */ (source));
}
}

View File

@@ -24,7 +24,7 @@ import Within from '../format/filter/Within.js';
* Create a logical `<And>` operator between two or more filter conditions.
*
* @param {...import("./filter/Filter.js").default} conditions Filter conditions.
* @returns {!import("./filter/And.js").default} `<And>` operator.
* @returns {!And} `<And>` operator.
* @api
*/
export function and(conditions) {
@@ -37,7 +37,7 @@ export function and(conditions) {
* Create a logical `<Or>` operator between two or more filter conditions.
*
* @param {...import("./filter/Filter.js").default} conditions Filter conditions.
* @returns {!import("./filter/Or.js").default} `<Or>` operator.
* @returns {!Or} `<Or>` operator.
* @api
*/
export function or(conditions) {
@@ -50,7 +50,7 @@ export function or(conditions) {
* Represents a logical `<Not>` operator for a filter condition.
*
* @param {!import("./filter/Filter.js").default} condition Filter condition.
* @returns {!import("./filter/Not.js").default} `<Not>` operator.
* @returns {!Not} `<Not>` operator.
* @api
*/
export function not(condition) {
@@ -66,7 +66,7 @@ export function not(condition) {
* @param {!import("../extent.js").Extent} extent Extent.
* @param {string=} opt_srsName SRS name. No srsName attribute will be
* set on geometries when this is not provided.
* @returns {!import("./filter/Bbox.js").default} `<BBOX>` operator.
* @returns {!Bbox} `<BBOX>` operator.
* @api
*/
export function bbox(geometryName, extent, opt_srsName) {
@@ -81,7 +81,7 @@ export function bbox(geometryName, extent, opt_srsName) {
* @param {!import("../geom/Geometry.js").default} geometry Geometry.
* @param {string=} opt_srsName SRS name. No srsName attribute will be
* set on geometries when this is not provided.
* @returns {!import("./filter/Contains.js").default} `<Contains>` operator.
* @returns {!Contains} `<Contains>` operator.
* @api
*/
export function contains(geometryName, geometry, opt_srsName) {
@@ -96,7 +96,7 @@ export function contains(geometryName, geometry, opt_srsName) {
* @param {!import("../geom/Geometry.js").default} geometry Geometry.
* @param {string=} opt_srsName SRS name. No srsName attribute will be
* set on geometries when this is not provided.
* @returns {!import("./filter/Intersects.js").default} `<Intersects>` operator.
* @returns {!Intersects} `<Intersects>` operator.
* @api
*/
export function intersects(geometryName, geometry, opt_srsName) {
@@ -111,7 +111,7 @@ export function intersects(geometryName, geometry, opt_srsName) {
* @param {!import("../geom/Geometry.js").default} geometry Geometry.
* @param {string=} opt_srsName SRS name. No srsName attribute will be
* set on geometries when this is not provided.
* @returns {!import("./filter/Within.js").default} `<Within>` operator.
* @returns {!Within} `<Within>` operator.
* @api
*/
export function within(geometryName, geometry, opt_srsName) {
@@ -125,7 +125,7 @@ export function within(geometryName, geometry, opt_srsName) {
* @param {!string} propertyName Name of the context property to compare.
* @param {!(string|number)} expression The value to compare.
* @param {boolean=} opt_matchCase Case-sensitive?
* @returns {!import("./filter/EqualTo.js").default} `<PropertyIsEqualTo>` operator.
* @returns {!EqualTo} `<PropertyIsEqualTo>` operator.
* @api
*/
export function equalTo(propertyName, expression, opt_matchCase) {
@@ -139,7 +139,7 @@ export function equalTo(propertyName, expression, opt_matchCase) {
* @param {!string} propertyName Name of the context property to compare.
* @param {!(string|number)} expression The value to compare.
* @param {boolean=} opt_matchCase Case-sensitive?
* @returns {!import("./filter/NotEqualTo.js").default} `<PropertyIsNotEqualTo>` operator.
* @returns {!NotEqualTo} `<PropertyIsNotEqualTo>` operator.
* @api
*/
export function notEqualTo(propertyName, expression, opt_matchCase) {
@@ -152,7 +152,7 @@ export function notEqualTo(propertyName, expression, opt_matchCase) {
*
* @param {!string} propertyName Name of the context property to compare.
* @param {!number} expression The value to compare.
* @returns {!import("./filter/LessThan.js").default} `<PropertyIsLessThan>` operator.
* @returns {!LessThan} `<PropertyIsLessThan>` operator.
* @api
*/
export function lessThan(propertyName, expression) {
@@ -165,7 +165,7 @@ export function lessThan(propertyName, expression) {
*
* @param {!string} propertyName Name of the context property to compare.
* @param {!number} expression The value to compare.
* @returns {!import("./filter/LessThanOrEqualTo.js").default} `<PropertyIsLessThanOrEqualTo>` operator.
* @returns {!LessThanOrEqualTo} `<PropertyIsLessThanOrEqualTo>` operator.
* @api
*/
export function lessThanOrEqualTo(propertyName, expression) {
@@ -178,7 +178,7 @@ export function lessThanOrEqualTo(propertyName, expression) {
*
* @param {!string} propertyName Name of the context property to compare.
* @param {!number} expression The value to compare.
* @returns {!import("./filter/GreaterThan.js").default} `<PropertyIsGreaterThan>` operator.
* @returns {!GreaterThan} `<PropertyIsGreaterThan>` operator.
* @api
*/
export function greaterThan(propertyName, expression) {
@@ -191,7 +191,7 @@ export function greaterThan(propertyName, expression) {
*
* @param {!string} propertyName Name of the context property to compare.
* @param {!number} expression The value to compare.
* @returns {!import("./filter/GreaterThanOrEqualTo.js").default} `<PropertyIsGreaterThanOrEqualTo>` operator.
* @returns {!GreaterThanOrEqualTo} `<PropertyIsGreaterThanOrEqualTo>` operator.
* @api
*/
export function greaterThanOrEqualTo(propertyName, expression) {
@@ -204,7 +204,7 @@ export function greaterThanOrEqualTo(propertyName, expression) {
* is null.
*
* @param {!string} propertyName Name of the context property to compare.
* @returns {!import("./filter/IsNull.js").default} `<PropertyIsNull>` operator.
* @returns {!IsNull} `<PropertyIsNull>` operator.
* @api
*/
export function isNull(propertyName) {
@@ -219,7 +219,7 @@ export function isNull(propertyName) {
* @param {!string} propertyName Name of the context property to compare.
* @param {!number} lowerBoundary The lower bound of the range.
* @param {!number} upperBoundary The upper bound of the range.
* @returns {!import("./filter/IsBetween.js").default} `<PropertyIsBetween>` operator.
* @returns {!IsBetween} `<PropertyIsBetween>` operator.
* @api
*/
export function between(propertyName, lowerBoundary, upperBoundary) {
@@ -240,7 +240,7 @@ export function between(propertyName, lowerBoundary, upperBoundary) {
* @param {string=} opt_escapeChar Escape character which can be used to escape
* the pattern characters. Default is '!'.
* @param {boolean=} opt_matchCase Case-sensitive?
* @returns {!import("./filter/IsLike.js").default} `<PropertyIsLike>` operator.
* @returns {!IsLike} `<PropertyIsLike>` operator.
* @api
*/
export function like(propertyName, pattern,
@@ -256,7 +256,7 @@ export function like(propertyName, pattern,
* @param {!string} propertyName Name of the context property to compare.
* @param {!string} begin The begin date in ISO-8601 format.
* @param {!string} end The end date in ISO-8601 format.
* @returns {!import("./filter/During.js").default} `<During>` operator.
* @returns {!During} `<During>` operator.
* @api
*/
export function during(propertyName, begin, end) {

View File

@@ -15,8 +15,7 @@ class And extends LogicalNary {
* @param {...import("./Filter.js").default} conditions Conditions.
*/
constructor(conditions) {
const params = ['And'].concat(Array.prototype.slice.call(arguments));
super(...params);
super('And', Array.prototype.slice.call(arguments));
}
}

View File

@@ -15,7 +15,7 @@ class LogicalNary extends Filter {
/**
* @param {!string} tagName The XML tag name for this filter.
* @param {...import("./Filter.js").default} conditions Conditions.
* @param {Array<import("./Filter.js").default>} conditions Conditions.
*/
constructor(tagName, conditions) {
@@ -24,7 +24,7 @@ class LogicalNary extends Filter {
/**
* @type {Array<import("./Filter.js").default>}
*/
this.conditions = Array.prototype.slice.call(arguments, 1);
this.conditions = conditions;
assert(this.conditions.length >= 2, 57); // At least 2 conditions are required.
}

View File

@@ -14,8 +14,7 @@ class Or extends LogicalNary {
* @param {...import("./Filter.js").default} conditions Conditions.
*/
constructor(conditions) {
const params = ['Or'].concat(Array.prototype.slice.call(arguments));
super(...params);
super('Or', Array.prototype.slice.call(arguments));
}
}

View File

@@ -194,7 +194,9 @@ class Circle extends SimpleGeometry {
/**
* @inheritDoc
*/
getCoordinates() {}
getCoordinates() {
return null;
}
/**
* @inheritDoc

View File

@@ -1,9 +1,9 @@
/**
* @module ol/geom/Geometry
*/
import {abstract} from '../util.js';
import BaseObject from '../Object.js';
import {createEmpty, getHeight, returnOrUpdate} from '../extent.js';
import {FALSE} from '../functions.js';
import {transform2D} from '../geom/flat/transform.js';
import {get as getProjection, getTransform} from '../proj.js';
import Units from '../proj/Units.js';
@@ -70,7 +70,9 @@ class Geometry extends BaseObject {
* @abstract
* @return {!Geometry} Clone.
*/
clone() {}
clone() {
return abstract();
}
/**
* @abstract
@@ -80,7 +82,18 @@ class Geometry extends BaseObject {
* @param {number} minSquaredDistance Minimum squared distance.
* @return {number} Minimum squared distance.
*/
closestPointXY(x, y, closestPoint, minSquaredDistance) {}
closestPointXY(x, y, closestPoint, minSquaredDistance) {
return abstract();
}
/**
* @param {number} x X.
* @param {number} y Y.
* @return {boolean} Contains (x, y).
*/
containsXY(x, y) {
return false;
}
/**
* Return the closest point of the geometry to the passed point as
@@ -113,7 +126,9 @@ class Geometry extends BaseObject {
* @protected
* @return {import("../extent.js").Extent} extent Extent.
*/
computeExtent(extent) {}
computeExtent(extent) {
return abstract();
}
/**
* Get the extent of the geometry.
@@ -137,7 +152,9 @@ class Geometry extends BaseObject {
* @param {import("../coordinate.js").Coordinate} anchor The rotation center.
* @api
*/
rotate(angle, anchor) {}
rotate(angle, anchor) {
abstract();
}
/**
* Scale the geometry (with an optional origin). This modifies the geometry
@@ -150,7 +167,9 @@ class Geometry extends BaseObject {
* of the geometry extent).
* @api
*/
scale(sx, opt_sy, opt_anchor) {}
scale(sx, opt_sy, opt_anchor) {
abstract();
}
/**
* Create a simplified version of this geometry. For linestrings, this uses
@@ -174,14 +193,18 @@ class Geometry extends BaseObject {
* @param {number} squaredTolerance Squared tolerance.
* @return {Geometry} Simplified geometry.
*/
getSimplifiedGeometry(squaredTolerance) {}
getSimplifiedGeometry(squaredTolerance) {
return abstract();
}
/**
* Get the type of this geometry.
* @abstract
* @return {import("./GeometryType.js").default} Geometry type.
*/
getType() {}
getType() {
return abstract();
}
/**
* Apply a transform function to each coordinate of the geometry.
@@ -191,7 +214,9 @@ class Geometry extends BaseObject {
* @abstract
* @param {import("../proj.js").TransformFunction} transformFn Transform.
*/
applyTransform(transformFn) {}
applyTransform(transformFn) {
abstract();
}
/**
* Test if the geometry and the passed extent intersect.
@@ -199,7 +224,9 @@ class Geometry extends BaseObject {
* @param {import("../extent.js").Extent} extent Extent.
* @return {boolean} `true` if the geometry and the extent intersect.
*/
intersectsExtent(extent) {}
intersectsExtent(extent) {
return abstract();
}
/**
* Translate the geometry. This modifies the geometry coordinates in place. If
@@ -209,7 +236,9 @@ class Geometry extends BaseObject {
* @param {number} deltaY Delta Y.
* @api
*/
translate(deltaX, deltaY) {}
translate(deltaX, deltaY) {
abstract();
}
/**
* Transform each coordinate of the geometry from one coordinate reference
@@ -227,11 +256,12 @@ class Geometry extends BaseObject {
* @api
*/
transform(source, destination) {
source = getProjection(source);
const transformFn = source.getUnits() == Units.TILE_PIXELS ?
/** @type {import("../proj/Projection.js").default} */
const sourceProj = getProjection(source);
const transformFn = sourceProj.getUnits() == Units.TILE_PIXELS ?
function(inCoordinates, outCoordinates, stride) {
const pixelExtent = source.getExtent();
const projectedExtent = source.getWorldExtent();
const pixelExtent = sourceProj.getExtent();
const projectedExtent = sourceProj.getWorldExtent();
const scale = getHeight(projectedExtent) / getHeight(pixelExtent);
composeTransform(tmpTransform,
projectedExtent[0], projectedExtent[3],
@@ -239,21 +269,14 @@ class Geometry extends BaseObject {
0, 0);
transform2D(inCoordinates, 0, inCoordinates.length, stride,
tmpTransform, outCoordinates);
return getTransform(source, destination)(inCoordinates, outCoordinates, stride);
return getTransform(sourceProj, destination)(inCoordinates, outCoordinates, stride);
} :
getTransform(source, destination);
getTransform(sourceProj, destination);
this.applyTransform(transformFn);
return this;
}
}
/**
* @param {number} x X.
* @param {number} y Y.
* @return {boolean} Contains (x, y).
*/
Geometry.prototype.containsXY = FALSE;
export default Geometry;

View File

@@ -57,9 +57,9 @@ class LineString extends SimpleGeometry {
this.maxDeltaRevision_ = -1;
if (opt_layout !== undefined && !Array.isArray(coordinates[0])) {
this.setFlatCoordinates(opt_layout, coordinates);
this.setFlatCoordinates(opt_layout, /** @type {Array<number>} */ (coordinates));
} else {
this.setCoordinates(coordinates, opt_layout);
this.setCoordinates(/** @type {Array<import("../coordinate.js").Coordinate>} */ (coordinates), opt_layout);
}
}

View File

@@ -42,9 +42,9 @@ class LinearRing extends SimpleGeometry {
this.maxDeltaRevision_ = -1;
if (opt_layout !== undefined && !Array.isArray(coordinates[0])) {
this.setFlatCoordinates(opt_layout, coordinates);
this.setFlatCoordinates(opt_layout, /** @type {Array<number>} */ (coordinates));
} else {
this.setCoordinates(coordinates, opt_layout);
this.setCoordinates(/** @type {Array<import("../coordinate.js").Coordinate>} */ (coordinates), opt_layout);
}
}
@@ -118,7 +118,9 @@ class LinearRing extends SimpleGeometry {
/**
* @inheritDoc
*/
intersectsExtent(extent) {}
intersectsExtent(extent) {
return false;
}
/**
* Set the coordinates of the linear ring.

View File

@@ -23,7 +23,7 @@ import {douglasPeuckerArray} from '../geom/flat/simplify.js';
class MultiLineString extends SimpleGeometry {
/**
* @param {Array<Array<import("../coordinate.js").Coordinate>|import("../geom.js").MultiLineString>|Array<number>} coordinates
* @param {Array<Array<import("../coordinate.js").Coordinate>|LineString>|Array<number>} coordinates
* Coordinates or LineString geometries. (For internal use, flat coordinates in
* combination with `opt_layout` and `opt_ends` are also accepted.)
* @param {GeometryLayout=} opt_layout Layout.
@@ -52,16 +52,17 @@ class MultiLineString extends SimpleGeometry {
this.maxDeltaRevision_ = -1;
if (Array.isArray(coordinates[0])) {
this.setCoordinates(coordinates, opt_layout);
this.setCoordinates(/** @type {Array<Array<import("../coordinate.js").Coordinate>>} */ (coordinates), opt_layout);
} else if (opt_layout !== undefined && opt_ends) {
this.setFlatCoordinates(opt_layout, coordinates);
this.setFlatCoordinates(opt_layout, /** @type {Array<number>} */ (coordinates));
this.ends_ = opt_ends;
} else {
let layout = this.getLayout();
const lineStrings = /** @type {Array<LineString>} */ (coordinates);
const flatCoordinates = [];
const ends = [];
for (let i = 0, ii = coordinates.length; i < ii; ++i) {
const lineString = coordinates[i];
for (let i = 0, ii = lineStrings.length; i < ii; ++i) {
const lineString = lineStrings[i];
if (i === 0) {
layout = lineString.getLayout();
}

View File

@@ -28,10 +28,10 @@ import {quantizeMultiArray} from '../geom/flat/simplify.js';
class MultiPolygon extends SimpleGeometry {
/**
* @param {Array<Array<Array<import("../coordinate.js").Coordinate>>>|Array<number>} coordinates Coordinates.
* For internal use, flat coordinats in combination with `opt_layout` and `opt_endss` are also accepted.
* @param {Array<Array<Array<import("../coordinate.js").Coordinate>>|Polygon>|Array<number>} coordinates Coordinates.
* For internal use, flat coordinates in combination with `opt_layout` and `opt_endss` are also accepted.
* @param {GeometryLayout=} opt_layout Layout.
* @param {Array<number>=} opt_endss Array of ends for internal use with flat coordinates.
* @param {Array<Array<number>>=} opt_endss Array of ends for internal use with flat coordinates.
*/
constructor(coordinates, opt_layout, opt_endss) {
@@ -81,10 +81,11 @@ class MultiPolygon extends SimpleGeometry {
if (!opt_endss && !Array.isArray(coordinates[0])) {
let layout = this.getLayout();
const polygons = /** @type {Array<Polygon>} */ (coordinates);
const flatCoordinates = [];
const endss = [];
for (let i = 0, ii = coordinates.length; i < ii; ++i) {
const polygon = coordinates[i];
for (let i = 0, ii = polygons.length; i < ii; ++i) {
const polygon = polygons[i];
if (i === 0) {
layout = polygon.getLayout();
}
@@ -101,10 +102,11 @@ class MultiPolygon extends SimpleGeometry {
opt_endss = endss;
}
if (opt_layout !== undefined && opt_endss) {
this.setFlatCoordinates(opt_layout, coordinates);
this.setFlatCoordinates(opt_layout, /** @type {Array<number>} */ (coordinates));
this.endss_ = opt_endss;
} else {
this.setCoordinates(coordinates, opt_layout);
this.setCoordinates(/** @type {Array<Array<Array<import("../coordinate.js").Coordinate>>>} */ (coordinates),
opt_layout);
}
}

View File

@@ -86,10 +86,10 @@ class Polygon extends SimpleGeometry {
this.orientedFlatCoordinates_ = null;
if (opt_layout !== undefined && opt_ends) {
this.setFlatCoordinates(opt_layout, coordinates);
this.setFlatCoordinates(opt_layout, /** @type {Array<number>} */ (coordinates));
this.ends_ = opt_ends;
} else {
this.setCoordinates(coordinates, opt_layout);
this.setCoordinates(/** @type {Array<Array<import("../coordinate.js").Coordinate>>} */ (coordinates), opt_layout);
}
}

View File

@@ -1,7 +1,7 @@
/**
* @module ol/geom/SimpleGeometry
*/
import {FALSE} from '../functions.js';
import {abstract} from '../util.js';
import {createOrUpdateFromFlatCoordinates, getCenter} from '../extent.js';
import Geometry from '../geom/Geometry.js';
import GeometryLayout from '../geom/GeometryLayout.js';
@@ -53,7 +53,9 @@ class SimpleGeometry extends Geometry {
* @abstract
* @return {Array} Coordinates.
*/
getCoordinates() {}
getCoordinates() {
return abstract();
}
/**
* Return the first coordinate of the geometry.
@@ -81,7 +83,7 @@ class SimpleGeometry extends Geometry {
}
/**
* Return the {@link module:ol/geom/GeometryLayout~GeometryLayout layout} of the geometry.
* Return the {@link module:ol/geom/GeometryLayout layout} of the geometry.
* @return {GeometryLayout} Layout.
* @api
*/
@@ -147,7 +149,7 @@ class SimpleGeometry extends Geometry {
/**
* @param {GeometryLayout} layout Layout.
* @param {Array<number>} flatCoordinates Flat coordinates.
*/
*/
setFlatCoordinates(layout, flatCoordinates) {
this.stride = getStrideForLayout(layout);
this.layout = layout;
@@ -159,7 +161,9 @@ class SimpleGeometry extends Geometry {
* @param {!Array} coordinates Coordinates.
* @param {GeometryLayout=} opt_layout Layout.
*/
setCoordinates(coordinates, opt_layout) {}
setCoordinates(coordinates, opt_layout) {
abstract();
}
/**
* @param {GeometryLayout|undefined} layout Layout.
@@ -291,12 +295,6 @@ export function getStrideForLayout(layout) {
}
/**
* @inheritDoc
*/
SimpleGeometry.prototype.containsXY = FALSE;
/**
* @param {SimpleGeometry} simpleGeometry Simple geometry.
* @param {import("../transform.js").Transform} transform Transform.

View File

@@ -111,7 +111,9 @@ export function intersectsLinearRingArray(flatCoordinates, offset, ends, stride,
}
for (let i = 1, ii = ends.length; i < ii; ++i) {
if (linearRingContainsExtent(flatCoordinates, ends[i - 1], ends[i], stride, extent)) {
return false;
if (!intersectsLineString(flatCoordinates, ends[i - 1], ends[i], stride, extent)) {
return false;
}
}
}
return true;

View File

@@ -13,14 +13,14 @@ import {get as getProjection} from '../proj.js';
/**
* @typedef {Object} Options
* @property {Array<function(new: import("../format/Feature.js").default)>} [formatConstructors] Format constructors.
* @property {Array<typeof import("../format/Feature.js").default>} [formatConstructors] Format constructors.
* @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 {Element} [target] The element that is used as the drop target, default is the viewport element.
* @property {HTMLElement} [target] The element that is used as the drop target, default is the viewport element.
*/
@@ -56,7 +56,7 @@ class DragAndDropEvent extends Event {
/**
* The features parsed from dropped data.
* @type {Array<import("../Feature.js").default>|undefined}
* @type {Array<import("../Feature.js").FeatureLike>|undefined}
* @api
*/
this.features = opt_features;
@@ -101,7 +101,7 @@ class DragAndDrop extends Interaction {
/**
* @private
* @type {Array<function(new: import("../format/Feature.js").default)>}
* @type {Array<typeof import("../format/Feature.js").default>}
*/
this.formatConstructors_ = options.formatConstructors ?
options.formatConstructors : [];
@@ -127,7 +127,7 @@ class DragAndDrop extends Interaction {
/**
* @private
* @type {Element}
* @type {HTMLElement}
*/
this.target = options.target ? options.target : null;
@@ -150,15 +150,7 @@ class DragAndDrop extends Interaction {
const formatConstructors = this.formatConstructors_;
let features = [];
for (let i = 0, ii = formatConstructors.length; i < ii; ++i) {
/**
* Avoid "cannot instantiate abstract class" error.
* @type {Function}
*/
const formatConstructor = formatConstructors[i];
/**
* @type {import("../format/Feature.js").default}
*/
const format = new formatConstructor();
const format = new formatConstructors[i]();
features = this.tryReadFeatures_(format, result, {
featureProjection: projection
});
@@ -220,7 +212,7 @@ class DragAndDrop extends Interaction {
* @param {string} text Text.
* @param {import("../format/Feature.js").ReadOptions} options Read options.
* @private
* @return {Array<import("../Feature.js").default>} Features.
* @return {Array<import("../Feature.js").FeatureLike>} Features.
*/
tryReadFeatures_(format, text, options) {
try {

View File

@@ -115,11 +115,7 @@ class DragBox extends PointerInteraction {
*/
constructor(opt_options) {
super({
handleDownEvent: handleDownEvent,
handleDragEvent: handleDragEvent,
handleUpEvent: handleUpEvent
});
super();
const options = opt_options ? opt_options : {};
@@ -159,7 +155,22 @@ class DragBox extends PointerInteraction {
* @type {EndCondition}
*/
this.boxEndCondition_ = options.boxEndCondition ?
options.boxEndCondition : defaultBoxEndCondition;
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_;
}
/**
@@ -170,83 +181,58 @@ class DragBox extends PointerInteraction {
getGeometry() {
return this.box_.getGeometry();
}
}
/**
* @inheritDoc
*/
handleDragEvent(mapBrowserEvent) {
if (!mouseOnly(mapBrowserEvent)) {
return;
}
/**
* 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.
* @this {DragBox}
*/
function defaultBoxEndCondition(mapBrowserEvent, startPixel, endPixel) {
const width = endPixel[0] - startPixel[0];
const height = endPixel[1] - startPixel[1];
return width * width + height * height >= this.minArea_;
}
this.box_.setPixels(this.startPixel_, mapBrowserEvent.pixel);
/**
* @param {import("../MapBrowserPointerEvent.js").default} mapBrowserEvent Event.
* @this {DragBox}
*/
function handleDragEvent(mapBrowserEvent) {
if (!mouseOnly(mapBrowserEvent)) {
return;
}
this.box_.setPixels(this.startPixel_, mapBrowserEvent.pixel);
this.dispatchEvent(new DragBoxEvent(DragBoxEventType.BOXDRAG,
mapBrowserEvent.coordinate, mapBrowserEvent));
}
/**
* @param {import("../MapBrowserPointerEvent.js").default} mapBrowserEvent Event.
* @return {boolean} Stop drag sequence?
* @this {DragBox}
*/
function handleUpEvent(mapBrowserEvent) {
if (!mouseOnly(mapBrowserEvent)) {
return true;
}
this.box_.setMap(null);
if (this.boxEndCondition_(mapBrowserEvent, this.startPixel_, mapBrowserEvent.pixel)) {
this.onBoxEnd_(mapBrowserEvent);
this.dispatchEvent(new DragBoxEvent(DragBoxEventType.BOXEND,
this.dispatchEvent(new DragBoxEvent(DragBoxEventType.BOXDRAG,
mapBrowserEvent.coordinate, mapBrowserEvent));
}
return false;
}
/**
* @inheritDoc
*/
handleUpEvent(mapBrowserEvent) {
if (!mouseOnly(mapBrowserEvent)) {
return true;
}
/**
* @param {import("../MapBrowserPointerEvent.js").default} mapBrowserEvent Event.
* @return {boolean} Start drag sequence?
* @this {DragBox}
*/
function handleDownEvent(mapBrowserEvent) {
if (!mouseOnly(mapBrowserEvent)) {
this.box_.setMap(null);
if (this.boxEndCondition_(mapBrowserEvent, this.startPixel_, mapBrowserEvent.pixel)) {
this.onBoxEnd_(mapBrowserEvent);
this.dispatchEvent(new DragBoxEvent(DragBoxEventType.BOXEND,
mapBrowserEvent.coordinate, mapBrowserEvent));
}
return false;
}
if (mouseActionButton(mapBrowserEvent) &&
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;
/**
* @inheritDoc
*/
handleDownEvent(mapBrowserEvent) {
if (!mouseOnly(mapBrowserEvent)) {
return false;
}
if (mouseActionButton(mapBrowserEvent) &&
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;
}
}
}

View File

@@ -30,9 +30,6 @@ class DragPan extends PointerInteraction {
constructor(opt_options) {
super({
handleDownEvent: handleDownEvent,
handleDragEvent: handleDragEvent,
handleUpEvent: handleUpEvent,
stopDown: FALSE
});
@@ -73,112 +70,102 @@ class DragPan extends PointerInteraction {
}
}
/**
* @param {import("../MapBrowserPointerEvent.js").default} mapBrowserEvent Event.
* @this {DragPan}
*/
function handleDragEvent(mapBrowserEvent) {
if (!this.panning_) {
this.panning_ = true;
this.getMap().getView().setHint(ViewHint.INTERACTING, 1);
}
const targetPointers = this.targetPointers;
const centroid = centroidFromPointers(targetPointers);
if (targetPointers.length == this.lastPointersCount_) {
if (this.kinetic_) {
this.kinetic_.update(centroid[0], centroid[1]);
/**
* @inheritDoc
*/
handleDragEvent(mapBrowserEvent) {
if (!this.panning_) {
this.panning_ = true;
this.getMap().getView().setHint(ViewHint.INTERACTING, 1);
}
if (this.lastCentroid) {
const deltaX = this.lastCentroid[0] - centroid[0];
const deltaY = centroid[1] - this.lastCentroid[1];
const map = mapBrowserEvent.map;
const view = map.getView();
let center = [deltaX, deltaY];
scaleCoordinate(center, view.getResolution());
rotateCoordinate(center, view.getRotation());
addCoordinate(center, view.getCenter());
center = view.constrainCenter(center);
view.setCenter(center);
}
} 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;
}
/**
* @param {import("../MapBrowserPointerEvent.js").default} mapBrowserEvent Event.
* @return {boolean} Stop drag sequence?
* @this {DragPan}
*/
function 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 = /** @type {!import("../coordinate.js").Coordinate} */ (view.getCenter());
const centerpx = map.getPixelFromCoordinate(center);
const dest = map.getCoordinateFromPixel([
centerpx[0] - distance * Math.cos(angle),
centerpx[1] - distance * Math.sin(angle)
]);
view.animate({
center: view.constrainCenter(dest),
duration: 500,
easing: easeOut
});
}
if (this.panning_) {
this.panning_ = false;
view.setHint(ViewHint.INTERACTING, -1);
}
return false;
} else {
if (this.kinetic_) {
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 deltaX = this.lastCentroid[0] - centroid[0];
const deltaY = centroid[1] - this.lastCentroid[1];
const map = mapBrowserEvent.map;
const view = map.getView();
let center = [deltaX, deltaY];
scaleCoordinate(center, view.getResolution());
rotateCoordinate(center, view.getRotation());
addCoordinate(center, view.getCenter());
center = view.constrainCenter(center);
view.setCenter(center);
}
} else if (this.kinetic_) {
// reset so we don't overestimate the kinetic energy after
// after one finger up, tiny drag, second finger up
// after one finger down, tiny drag, second finger down
this.kinetic_.begin();
}
this.lastCentroid = null;
return true;
this.lastCentroid = centroid;
this.lastPointersCount_ = targetPointers.length;
}
}
/**
* @param {import("../MapBrowserPointerEvent.js").default} mapBrowserEvent Event.
* @return {boolean} Start drag sequence?
* @this {DragPan}
*/
function handleDownEvent(mapBrowserEvent) {
if (this.targetPointers.length > 0 && this.condition_(mapBrowserEvent)) {
/**
* @inheritDoc
*/
handleUpEvent(mapBrowserEvent) {
const map = mapBrowserEvent.map;
const view = map.getView();
this.lastCentroid = null;
// stop any current animation
if (view.getAnimating()) {
view.setCenter(mapBrowserEvent.frameState.viewState.center);
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 = /** @type {!import("../coordinate.js").Coordinate} */ (view.getCenter());
const centerpx = map.getPixelFromCoordinate(center);
const dest = map.getCoordinateFromPixel([
centerpx[0] - distance * Math.cos(angle),
centerpx[1] - distance * Math.sin(angle)
]);
view.animate({
center: view.constrainCenter(dest),
duration: 500,
easing: easeOut
});
}
if (this.panning_) {
this.panning_ = false;
view.setHint(ViewHint.INTERACTING, -1);
}
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;
}
if (this.kinetic_) {
this.kinetic_.begin();
}
/**
* @inheritDoc
*/
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.setCenter(mapBrowserEvent.frameState.viewState.center);
}
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;
}
// 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;

View File

@@ -38,9 +38,6 @@ class DragRotate extends PointerInteraction {
const options = opt_options ? opt_options : {};
super({
handleDownEvent: handleDownEvent,
handleDragEvent: handleDragEvent,
handleUpEvent: handleUpEvent,
stopDown: FALSE
});
@@ -64,73 +61,66 @@ class DragRotate extends PointerInteraction {
}
}
/**
* @inheritDoc
*/
handleDragEvent(mapBrowserEvent) {
if (!mouseOnly(mapBrowserEvent)) {
return;
}
/**
* @param {import("../MapBrowserPointerEvent.js").default} mapBrowserEvent Event.
* @this {DragRotate}
*/
function 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_;
const rotation = view.getRotation();
rotateWithoutConstraints(view, rotation - delta);
}
this.lastAngle_ = theta;
}
/**
* @param {import("../MapBrowserPointerEvent.js").default} mapBrowserEvent Event.
* @return {boolean} Stop drag sequence?
* @this {DragRotate}
*/
function handleUpEvent(mapBrowserEvent) {
if (!mouseOnly(mapBrowserEvent)) {
return true;
}
const map = mapBrowserEvent.map;
const view = map.getView();
view.setHint(ViewHint.INTERACTING, -1);
const rotation = view.getRotation();
rotate(view, rotation, undefined, this.duration_);
return false;
}
/**
* @param {import("../MapBrowserPointerEvent.js").default} mapBrowserEvent Event.
* @return {boolean} Start drag sequence?
* @this {DragRotate}
*/
function handleDownEvent(mapBrowserEvent) {
if (!mouseOnly(mapBrowserEvent)) {
return false;
}
if (mouseActionButton(mapBrowserEvent) && this.condition_(mapBrowserEvent)) {
const map = mapBrowserEvent.map;
map.getView().setHint(ViewHint.INTERACTING, 1);
this.lastAngle_ = undefined;
return true;
} else {
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_;
const rotation = view.getRotation();
rotateWithoutConstraints(view, rotation - delta);
}
this.lastAngle_ = theta;
}
/**
* @inheritDoc
*/
handleUpEvent(mapBrowserEvent) {
if (!mouseOnly(mapBrowserEvent)) {
return true;
}
const map = mapBrowserEvent.map;
const view = map.getView();
view.setHint(ViewHint.INTERACTING, -1);
const rotation = view.getRotation();
rotate(view, rotation, undefined, this.duration_);
return false;
}
/**
* @inheritDoc
*/
handleDownEvent(mapBrowserEvent) {
if (!mouseOnly(mapBrowserEvent)) {
return false;
}
if (mouseActionButton(mapBrowserEvent) && this.condition_(mapBrowserEvent)) {
const map = mapBrowserEvent.map;
map.getView().setHint(ViewHint.INTERACTING, 1);
this.lastAngle_ = undefined;
return true;
} else {
return false;
}
}
}
export default DragRotate;

View File

@@ -38,11 +38,7 @@ class DragRotateAndZoom extends PointerInteraction {
const options = opt_options ? opt_options : {};
super({
handleDownEvent: handleDownEvent,
handleDragEvent: handleDragEvent,
handleUpEvent: handleUpEvent
});
super(/** @type {import("./Pointer.js").Options} */ (options));
/**
* @private
@@ -76,80 +72,71 @@ class DragRotateAndZoom extends PointerInteraction {
}
}
/**
* @inheritDoc
*/
handleDragEvent(mapBrowserEvent) {
if (!mouseOnly(mapBrowserEvent)) {
return;
}
/**
* @param {import("../MapBrowserPointerEvent.js").default} mapBrowserEvent Event.
* @this {DragRotateAndZoom}
*/
function 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 (view.getConstraints().rotation !== disable && this.lastAngle_ !== undefined) {
const angleDelta = theta - this.lastAngle_;
rotateWithoutConstraints(view, view.getRotation() - angleDelta);
}
this.lastAngle_ = theta;
if (this.lastMagnitude_ !== undefined) {
const resolution = this.lastMagnitude_ * (view.getResolution() / magnitude);
zoomWithoutConstraints(view, resolution);
}
if (this.lastMagnitude_ !== undefined) {
this.lastScaleDelta_ = this.lastMagnitude_ / magnitude;
}
this.lastMagnitude_ = magnitude;
}
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 (view.getConstraints().rotation !== disable && this.lastAngle_ !== undefined) {
const angleDelta = theta - this.lastAngle_;
rotateWithoutConstraints(view, view.getRotation() - angleDelta);
}
this.lastAngle_ = theta;
if (this.lastMagnitude_ !== undefined) {
const resolution = this.lastMagnitude_ * (view.getResolution() / magnitude);
zoomWithoutConstraints(view, resolution);
}
if (this.lastMagnitude_ !== undefined) {
this.lastScaleDelta_ = this.lastMagnitude_ / magnitude;
}
this.lastMagnitude_ = magnitude;
}
/**
* @inheritDoc
*/
handleUpEvent(mapBrowserEvent) {
if (!mouseOnly(mapBrowserEvent)) {
return true;
}
/**
* @param {import("../MapBrowserPointerEvent.js").default} mapBrowserEvent Event.
* @return {boolean} Stop drag sequence?
* @this {DragRotateAndZoom}
*/
function handleUpEvent(mapBrowserEvent) {
if (!mouseOnly(mapBrowserEvent)) {
return true;
}
const map = mapBrowserEvent.map;
const view = map.getView();
view.setHint(ViewHint.INTERACTING, -1);
const direction = this.lastScaleDelta_ - 1;
rotate(view, view.getRotation());
zoom(view, view.getResolution(), undefined, this.duration_, direction);
this.lastScaleDelta_ = 0;
return false;
}
/**
* @param {import("../MapBrowserPointerEvent.js").default} mapBrowserEvent Event.
* @return {boolean} Start drag sequence?
* @this {DragRotateAndZoom}
*/
function handleDownEvent(mapBrowserEvent) {
if (!mouseOnly(mapBrowserEvent)) {
const map = mapBrowserEvent.map;
const view = map.getView();
view.setHint(ViewHint.INTERACTING, -1);
const direction = this.lastScaleDelta_ - 1;
rotate(view, view.getRotation());
zoom(view, view.getResolution(), undefined, this.duration_, direction);
this.lastScaleDelta_ = 0;
return false;
}
if (this.condition_(mapBrowserEvent)) {
mapBrowserEvent.map.getView().setHint(ViewHint.INTERACTING, 1);
this.lastAngle_ = undefined;
this.lastMagnitude_ = undefined;
return true;
} else {
return false;
/**
* @inheritDoc
*/
handleDownEvent(mapBrowserEvent) {
if (!mouseOnly(mapBrowserEvent)) {
return false;
}
if (this.condition_(mapBrowserEvent)) {
mapBrowserEvent.map.getView().setHint(ViewHint.INTERACTING, 1);
this.lastAngle_ = undefined;
this.lastMagnitude_ = undefined;
return true;
} else {
return false;
}
}
}

View File

@@ -21,7 +21,7 @@ import MultiPolygon from '../geom/MultiPolygon.js';
import {POINTER_TYPE} from '../pointer/MouseSource.js';
import Point from '../geom/Point.js';
import Polygon, {fromCircle, makeRegular} from '../geom/Polygon.js';
import PointerInteraction, {handleEvent as handlePointerEvent} from '../interaction/Pointer.js';
import PointerInteraction from '../interaction/Pointer.js';
import InteractionProperty from '../interaction/Property.js';
import VectorLayer from '../layer/Vector.js';
import VectorSource from '../source/Vector.js';
@@ -56,7 +56,7 @@ import {createEditingStyle} from '../style/Style.js';
* @property {import("../events/condition.js").Condition} [finishCondition] A function
* that takes an {@link module:ol/MapBrowserEvent~MapBrowserEvent} and returns a
* boolean to indicate whether the drawing can be finished.
* @property {import("../style/Style.js").default|Array<import("../style/Style.js").default>|import("../style/Style.js").StyleFunction} [style]
* @property {import("../style/Style.js").StyleLike} [style]
* Style for sketch features.
* @property {GeometryFunction} [geometryFunction]
* Function that is called when a geometry's coordinates are updated.
@@ -81,12 +81,36 @@ import {createEditingStyle} from '../style/Style.js';
*/
/**
* Coordinate type when drawing points.
* @typedef {import("../coordinate.js").Coordinate} PointCoordType
*/
/**
* Coordinate type when drawing lines.
* @typedef {Array<import("../coordinate.js").Coordinate>} LineCoordType
*/
/**
* Coordinate type when drawing polygons.
* @typedef {Array<Array<import("../coordinate.js").Coordinate>>} PolyCoordType
*/
/**
* Types used for drawing coordinates.
* @typedef {PointCoordType|LineCoordType|PolyCoordType} SketchCoordType
*/
/**
* Function that takes an array of coordinates and an optional existing geometry as
* arguments, and returns a geometry. The optional existing geometry is the
* geometry that is returned when the function is called without a second
* argument.
* @typedef {function(!Array<import("../coordinate.js").Coordinate>, import("../geom/SimpleGeometry.js").default=):
* @typedef {function(!SketchCoordType, import("../geom/SimpleGeometry.js").default=):
* import("../geom/SimpleGeometry.js").default} GeometryFunction
*/
@@ -162,12 +186,12 @@ class Draw extends PointerInteraction {
*/
constructor(options) {
super({
handleDownEvent: handleDownEvent,
handleEvent: handleEvent,
handleUpEvent: handleUpEvent,
stopDown: FALSE
});
const pointerOptions = /** @type {import("./Pointer.js").Options} */ (options);
if (!pointerOptions.stopDown) {
pointerOptions.stopDown = FALSE;
}
super(pointerOptions);
/**
* @type {boolean}
@@ -182,7 +206,7 @@ class Draw extends PointerInteraction {
this.downPx_ = null;
/**
* @type {number|undefined}
* @type {?}
* @private
*/
this.downTimeout_;
@@ -272,8 +296,7 @@ class Draw extends PointerInteraction {
if (!geometryFunction) {
if (this.type_ === GeometryType.CIRCLE) {
/**
* @param {!Array<import("../coordinate.js").Coordinate>} coordinates
* The coordinates.
* @param {!LineCoordType} coordinates The coordinates.
* @param {import("../geom/SimpleGeometry.js").default=} opt_geometry Optional geometry.
* @return {import("../geom/SimpleGeometry.js").default} A geometry.
*/
@@ -296,8 +319,7 @@ class Draw extends PointerInteraction {
Constructor = Polygon;
}
/**
* @param {!Array<import("../coordinate.js").Coordinate>} coordinates
* The coordinates.
* @param {!LineCoordType} coordinates The coordinates.
* @param {import("../geom/SimpleGeometry.js").default=} opt_geometry Optional geometry.
* @return {import("../geom/SimpleGeometry.js").default} A geometry.
*/
@@ -358,7 +380,7 @@ class Draw extends PointerInteraction {
/**
* Sketch coordinates. Used when drawing a line or polygon.
* @type {import("../coordinate.js").Coordinate|Array<import("../coordinate.js").Coordinate>|Array<Array<import("../coordinate.js").Coordinate>>}
* @type {SketchCoordType}
* @private
*/
this.sketchCoords_ = null;
@@ -372,7 +394,7 @@ class Draw extends PointerInteraction {
/**
* Sketch line coordinates. Used when drawing a polygon or circle.
* @type {Array<import("../coordinate.js").Coordinate>}
* @type {LineCoordType}
* @private
*/
this.sketchLineCoords_ = null;
@@ -450,6 +472,123 @@ class Draw extends PointerInteraction {
return this.overlay_;
}
/**
* Handles the {@link module:ol/MapBrowserEvent map browser event} and may actually draw or finish the drawing.
* @override
* @api
*/
handleEvent(event) {
if (event.originalEvent.type === EventType.CONTEXTMENU) {
// Avoid context menu for long taps when drawing on mobile
event.preventDefault();
}
this.freehand_ = this.mode_ !== Mode.POINT && this.freehandCondition_(event);
let move = event.type === MapBrowserEventType.POINTERMOVE;
let pass = true;
if (!this.freehand_ && this.lastDragTime_ && event.type === MapBrowserEventType.POINTERDRAG) {
const now = Date.now();
if (now - this.lastDragTime_ >= this.dragVertexDelay_) {
this.downPx_ = event.pixel;
this.shouldHandle_ = !this.freehand_;
move = true;
} else {
this.lastDragTime_ = undefined;
}
if (this.shouldHandle_ && this.downTimeout_ !== undefined) {
clearTimeout(this.downTimeout_);
this.downTimeout_ = undefined;
}
}
if (this.freehand_ &&
event.type === MapBrowserEventType.POINTERDRAG &&
this.sketchFeature_ !== null) {
this.addToDrawing_(event);
pass = false;
} else if (this.freehand_ &&
event.type === MapBrowserEventType.POINTERDOWN) {
pass = false;
} else if (move) {
pass = event.type === MapBrowserEventType.POINTERMOVE;
if (pass && this.freehand_) {
pass = this.handlePointerMove_(event);
} else if (/** @type {MapBrowserPointerEvent} */ (event).pointerEvent.pointerType == POINTER_TYPE ||
(event.type === MapBrowserEventType.POINTERDRAG && this.downTimeout_ === undefined)) {
this.handlePointerMove_(event);
}
} else if (event.type === MapBrowserEventType.DBLCLICK) {
pass = false;
}
return super.handleEvent(event) && pass;
}
/**
* @inheritDoc
*/
handleDownEvent(event) {
this.shouldHandle_ = !this.freehand_;
if (this.freehand_) {
this.downPx_ = event.pixel;
if (!this.finishCoordinate_) {
this.startDrawing_(event);
}
return true;
} else if (this.condition_(event)) {
this.lastDragTime_ = Date.now();
this.downTimeout_ = setTimeout(function() {
this.handlePointerMove_(new MapBrowserPointerEvent(
MapBrowserEventType.POINTERMOVE, event.map, event.pointerEvent, false, event.frameState));
}.bind(this), this.dragVertexDelay_);
this.downPx_ = event.pixel;
return true;
} else {
return false;
}
}
/**
* @inheritDoc
*/
handleUpEvent(event) {
let pass = true;
if (this.downTimeout_) {
clearTimeout(this.downTimeout_);
this.downTimeout_ = undefined;
}
this.handlePointerMove_(event);
const circleMode = this.mode_ === Mode.CIRCLE;
if (this.shouldHandle_) {
if (!this.finishCoordinate_) {
this.startDrawing_(event);
if (this.mode_ === Mode.POINT) {
this.finishDrawing();
}
} else if (this.freehand_ || circleMode) {
this.finishDrawing();
} else if (this.atFinish_(event)) {
if (this.finishCondition_(event)) {
this.finishDrawing();
}
} else {
this.addToDrawing_(event);
}
pass = false;
} else if (this.freehand_) {
this.finishCoordinate_ = null;
this.abortDrawing_();
}
if (!pass && this.stopClick_) {
event.stopPropagation();
}
return pass;
}
/**
* Handle move events.
* @param {import("../MapBrowserEvent.js").default} event A move event.
@@ -495,10 +634,9 @@ class Draw extends PointerInteraction {
if (this.mode_ === Mode.LINE_STRING) {
potentiallyDone = this.sketchCoords_.length > this.minPoints_;
} else if (this.mode_ === Mode.POLYGON) {
potentiallyDone = this.sketchCoords_[0].length >
this.minPoints_;
potentiallyFinishCoordinates = [this.sketchCoords_[0][0],
this.sketchCoords_[0][this.sketchCoords_[0].length - 2]];
const sketchCoords = /** @type {PolyCoordType} */ (this.sketchCoords_);
potentiallyDone = sketchCoords[0].length > this.minPoints_;
potentiallyFinishCoordinates = [sketchCoords[0][0], sketchCoords[0][sketchCoords[0].length - 2]];
}
if (potentiallyDone) {
const map = event.map;
@@ -577,7 +715,7 @@ class Draw extends PointerInteraction {
if (this.mode_ === Mode.POINT) {
last = this.sketchCoords_;
} else if (this.mode_ === Mode.POLYGON) {
coordinates = this.sketchCoords_[0];
coordinates = /** @type {PolyCoordType} */ (this.sketchCoords_)[0];
last = coordinates[coordinates.length - 1];
if (this.atFinish_(event)) {
// snap to finish
@@ -589,18 +727,19 @@ class Draw extends PointerInteraction {
}
last[0] = coordinate[0];
last[1] = coordinate[1];
this.geometryFunction_(/** @type {!Array<import("../coordinate.js").Coordinate>} */ (this.sketchCoords_), geometry);
this.geometryFunction_(/** @type {!LineCoordType} */ (this.sketchCoords_), geometry);
if (this.sketchPoint_) {
const sketchPointGeom = /** @type {Point} */ (this.sketchPoint_.getGeometry());
sketchPointGeom.setCoordinates(coordinate);
}
/** @type {LineString} */
let sketchLineGeom;
if (geometry instanceof Polygon &&
if (geometry.getType() == GeometryType.POLYGON &&
this.mode_ !== Mode.POLYGON) {
if (!this.sketchLine_) {
this.sketchLine_ = new Feature();
}
const ring = geometry.getLinearRing(0);
const ring = /** @type {Polygon} */ (geometry).getLinearRing(0);
sketchLineGeom = /** @type {LineString} */ (this.sketchLine_.getGeometry());
if (!sketchLineGeom) {
sketchLineGeom = new LineString(ring.getFlatCoordinates(), ring.getLayout());
@@ -629,7 +768,7 @@ class Draw extends PointerInteraction {
let coordinates;
if (this.mode_ === Mode.LINE_STRING) {
this.finishCoordinate_ = coordinate.slice();
coordinates = this.sketchCoords_;
coordinates = /** @type {LineCoordType} */ (this.sketchCoords_);
if (coordinates.length >= this.maxPoints_) {
if (this.freehand_) {
coordinates.pop();
@@ -640,7 +779,7 @@ class Draw extends PointerInteraction {
coordinates.push(coordinate.slice());
this.geometryFunction_(coordinates, geometry);
} else if (this.mode_ === Mode.POLYGON) {
coordinates = this.sketchCoords_[0];
coordinates = /** @type {PolyCoordType} */ (this.sketchCoords_)[0];
if (coordinates.length >= this.maxPoints_) {
if (this.freehand_) {
coordinates.pop();
@@ -669,16 +808,18 @@ class Draw extends PointerInteraction {
return;
}
const geometry = /** @type {import("../geom/SimpleGeometry.js").default} */ (this.sketchFeature_.getGeometry());
let coordinates, sketchLineGeom;
let coordinates;
/** @type {LineString} */
let sketchLineGeom;
if (this.mode_ === Mode.LINE_STRING) {
coordinates = this.sketchCoords_;
coordinates = /** @type {LineCoordType} */ (this.sketchCoords_);
coordinates.splice(-2, 1);
this.geometryFunction_(coordinates, geometry);
if (coordinates.length >= 2) {
this.finishCoordinate_ = coordinates[coordinates.length - 2].slice();
}
} else if (this.mode_ === Mode.POLYGON) {
coordinates = this.sketchCoords_[0];
coordinates = /** @type {PolyCoordType} */ (this.sketchCoords_)[0];
coordinates.splice(-2, 1);
sketchLineGeom = /** @type {LineString} */ (this.sketchLine_.getGeometry());
sketchLineGeom.setCoordinates(coordinates);
@@ -711,18 +852,18 @@ class Draw extends PointerInteraction {
this.geometryFunction_(coordinates, geometry);
} else if (this.mode_ === Mode.POLYGON) {
// remove the redundant last point in ring
coordinates[0].pop();
/** @type {PolyCoordType} */ (coordinates)[0].pop();
this.geometryFunction_(coordinates, geometry);
coordinates = geometry.getCoordinates();
}
// cast multi-part geometries
if (this.type_ === GeometryType.MULTI_POINT) {
sketchFeature.setGeometry(new MultiPoint([coordinates]));
sketchFeature.setGeometry(new MultiPoint([/** @type {PointCoordType} */(coordinates)]));
} else if (this.type_ === GeometryType.MULTI_LINE_STRING) {
sketchFeature.setGeometry(new MultiLineString([coordinates]));
sketchFeature.setGeometry(new MultiLineString([/** @type {LineCoordType} */(coordinates)]));
} else if (this.type_ === GeometryType.MULTI_POLYGON) {
sketchFeature.setGeometry(new MultiPolygon([coordinates]));
sketchFeature.setGeometry(new MultiPolygon([/** @type {PolyCoordType} */(coordinates)]));
}
// First dispatch event to allow full set up of feature
@@ -749,7 +890,7 @@ class Draw extends PointerInteraction {
this.sketchFeature_ = null;
this.sketchPoint_ = null;
this.sketchLine_ = null;
this.overlay_.getSource().clear(true);
/** @type {VectorSource} */ (this.overlay_.getSource()).clear(true);
}
return sketchFeature;
}
@@ -788,7 +929,7 @@ class Draw extends PointerInteraction {
if (this.sketchPoint_) {
sketchFeatures.push(this.sketchPoint_);
}
const overlaySource = this.overlay_.getSource();
const overlaySource = /** @type {VectorSource} */ (this.overlay_.getSource());
overlaySource.clear(true);
overlaySource.addFeatures(sketchFeatures);
}
@@ -818,132 +959,6 @@ function getDefaultStyleFunction() {
}
/**
* Handles the {@link module:ol/MapBrowserEvent map browser event} and may actually
* draw or finish the drawing.
* @param {import("../MapBrowserEvent.js").default} event Map browser event.
* @return {boolean} `false` to stop event propagation.
* @this {Draw}
* @api
*/
export function handleEvent(event) {
if (event.originalEvent.type === EventType.CONTEXTMENU) {
// Avoid context menu for long taps when drawing on mobile
event.preventDefault();
}
this.freehand_ = this.mode_ !== Mode.POINT && this.freehandCondition_(event);
let move = event.type === MapBrowserEventType.POINTERMOVE;
let pass = true;
if (!this.freehand_ && this.lastDragTime_ && event.type === MapBrowserEventType.POINTERDRAG) {
const now = Date.now();
if (now - this.lastDragTime_ >= this.dragVertexDelay_) {
this.downPx_ = event.pixel;
this.shouldHandle_ = !this.freehand_;
move = true;
} else {
this.lastDragTime_ = undefined;
}
if (this.shouldHandle_ && this.downTimeout_) {
clearTimeout(this.downTimeout_);
this.downTimeout_ = undefined;
}
}
if (this.freehand_ &&
event.type === MapBrowserEventType.POINTERDRAG &&
this.sketchFeature_ !== null) {
this.addToDrawing_(event);
pass = false;
} else if (this.freehand_ &&
event.type === MapBrowserEventType.POINTERDOWN) {
pass = false;
} else if (move) {
pass = event.type === MapBrowserEventType.POINTERMOVE;
if (pass && this.freehand_) {
pass = this.handlePointerMove_(event);
} else if (event.pointerEvent.pointerType == POINTER_TYPE ||
(event.type === MapBrowserEventType.POINTERDRAG && !this.downTimeout_)) {
this.handlePointerMove_(event);
}
} else if (event.type === MapBrowserEventType.DBLCLICK) {
pass = false;
}
return handlePointerEvent.call(this, event) && pass;
}
/**
* @param {MapBrowserPointerEvent} event Event.
* @return {boolean} Start drag sequence?
* @this {Draw}
*/
function handleDownEvent(event) {
this.shouldHandle_ = !this.freehand_;
if (this.freehand_) {
this.downPx_ = event.pixel;
if (!this.finishCoordinate_) {
this.startDrawing_(event);
}
return true;
} else if (this.condition_(event)) {
this.lastDragTime_ = Date.now();
this.downTimeout_ = setTimeout(function() {
this.handlePointerMove_(new MapBrowserPointerEvent(
MapBrowserEventType.POINTERMOVE, event.map, event.pointerEvent, event.frameState));
}.bind(this), this.dragVertexDelay_);
this.downPx_ = event.pixel;
return true;
} else {
return false;
}
}
/**
* @param {MapBrowserPointerEvent} event Event.
* @return {boolean} Stop drag sequence?
* @this {Draw}
*/
function handleUpEvent(event) {
let pass = true;
if (this.downTimeout_) {
clearTimeout(this.downTimeout_);
this.downTimeout_ = undefined;
}
this.handlePointerMove_(event);
const circleMode = this.mode_ === Mode.CIRCLE;
if (this.shouldHandle_) {
if (!this.finishCoordinate_) {
this.startDrawing_(event);
if (this.mode_ === Mode.POINT) {
this.finishDrawing();
}
} else if (this.freehand_ || circleMode) {
this.finishDrawing();
} else if (this.atFinish_(event)) {
if (this.finishCondition_(event)) {
this.finishDrawing();
}
} else {
this.addToDrawing_(event);
}
pass = false;
} else if (this.freehand_) {
this.finishCoordinate_ = null;
this.abortDrawing_();
}
if (!pass && this.stopClick_) {
event.stopPropagation();
}
return pass;
}
/**
* Create a `geometryFunction` for `type: 'Circle'` that will create a regular
* polygon with a user specified number of sides and start angle instead of an
@@ -959,8 +974,8 @@ function handleUpEvent(event) {
*/
export function createRegularPolygon(opt_sides, opt_angle) {
return function(coordinates, opt_geometry) {
const center = coordinates[0];
const end = coordinates[1];
const center = /** @type {LineCoordType} */ (coordinates)[0];
const end = /** @type {LineCoordType} */ (coordinates)[1];
const radius = Math.sqrt(
squaredCoordinateDistance(center, end));
const geometry = opt_geometry ? /** @type {Polygon} */ (opt_geometry) :
@@ -987,7 +1002,7 @@ export function createRegularPolygon(opt_sides, opt_angle) {
export function createBox() {
return (
function(coordinates, opt_geometry) {
const extent = boundingExtent(coordinates);
const extent = boundingExtent(/** @type {LineCoordType} */ (coordinates));
const boxCoordinates = [[
getBottomLeft(extent),
getBottomRight(extent),

View File

@@ -3,14 +3,13 @@
*/
import Feature from '../Feature.js';
import MapBrowserEventType from '../MapBrowserEventType.js';
import MapBrowserPointerEvent from '../MapBrowserPointerEvent.js';
import {squaredDistanceToSegment, closestOnSegment, distance as coordinateDistance, squaredDistance as squaredCoordinateDistance} from '../coordinate.js';
import Event from '../events/Event.js';
import {boundingExtent, getArea} from '../extent.js';
import GeometryType from '../geom/GeometryType.js';
import Point from '../geom/Point.js';
import {fromExtent as polygonFromExtent} from '../geom/Polygon.js';
import PointerInteraction, {handleEvent as handlePointerEvent} from '../interaction/Pointer.js';
import PointerInteraction from '../interaction/Pointer.js';
import VectorLayer from '../layer/Vector.js';
import VectorSource from '../source/Vector.js';
import {createEditingStyle} from '../style/Style.js';
@@ -20,12 +19,12 @@ import {createEditingStyle} from '../style/Style.js';
* @typedef {Object} Options
* @property {import("../extent.js").Extent} [extent] Initial extent. Defaults to no
* initial extent.
* @property {import("../style/Style.js").default|Array<import("../style/Style.js").default>|import("../style/Style.js").StyleFunction} [boxStyle]
* @property {import("../style/Style.js").StyleLike} [boxStyle]
* Style for the drawn extent box. Defaults to
* {@link module:ol/style/Style~createEditing()['Polygon']}
* @property {number} [pixelTolerance=10] Pixel tolerance for considering the
* pointer close enough to a segment or vertex for editing.
* @property {import("../style/Style.js").default|Array<import("../style/Style.js").default>|import("../style/Style.js").StyleFunction} [pointerStyle]
* @property {import("../style/Style.js").StyleLike} [pointerStyle]
* Style for the cursor used to draw the extent. Defaults to
* {@link module:ol/style/Style~createEditing()['Point']}
* @property {boolean} [wrapX=false] Wrap the drawn extent across multiple maps
@@ -85,15 +84,10 @@ class ExtentInteraction extends PointerInteraction {
*/
constructor(opt_options) {
super({
handleDownEvent: handleDownEvent,
handleDragEvent: handleDragEvent,
handleEvent: handleEvent,
handleUpEvent: handleUpEvent
});
const options = opt_options || {};
super(/** @type {import("./Pointer.js").Options} */ (options));
/**
* Extent of the drawn box
* @type {import("../extent.js").Extent}
@@ -248,7 +242,7 @@ class ExtentInteraction extends PointerInteraction {
extentFeature = new Feature(polygonFromExtent(extent));
}
this.extentFeature_ = extentFeature;
this.extentOverlay_.getSource().addFeature(extentFeature);
/** @type {VectorSource} */ (this.extentOverlay_.getSource()).addFeature(extentFeature);
} else {
if (!extent) {
extentFeature.setGeometry(undefined);
@@ -269,7 +263,7 @@ class ExtentInteraction extends PointerInteraction {
if (!vertexFeature) {
vertexFeature = new Feature(new Point(vertex));
this.vertexFeature_ = vertexFeature;
this.vertexOverlay_.getSource().addFeature(vertexFeature);
/** @type {VectorSource} */ (this.vertexOverlay_.getSource()).addFeature(vertexFeature);
} else {
const geometry = /** @type {Point} */ (vertexFeature.getGeometry());
geometry.setCoordinates(vertex);
@@ -277,6 +271,105 @@ class ExtentInteraction extends PointerInteraction {
return vertexFeature;
}
/**
* @inheritDoc
*/
handleEvent(mapBrowserEvent) {
if (!(/** @type {import("../MapBrowserPointerEvent.js").default} */ (mapBrowserEvent).pointerEvent)) {
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;
}
/**
* @inheritDoc
*/
handleDownEvent(mapBrowserEvent) {
const pixel = mapBrowserEvent.pixel;
const map = mapBrowserEvent.map;
const extent = this.getExtent();
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.getCoordinateFromPixel(pixel);
this.setExtent([vertex[0], vertex[1], vertex[0], vertex[1]]);
this.pointerHandler_ = getPointHandler(vertex);
}
return true; //event handled; start downup sequence
}
/**
* @inheritDoc
*/
handleDragEvent(mapBrowserEvent) {
if (this.pointerHandler_) {
const pixelCoordinate = mapBrowserEvent.coordinate;
this.setExtent(this.pointerHandler_(pixelCoordinate));
this.createOrUpdatePointerFeature_(pixelCoordinate);
}
return true;
}
/**
* @inheritDoc
*/
handleUpEvent(mapBrowserEvent) {
this.pointerHandler_ = null;
//If bbox is zero area, set to null;
const extent = this.getExtent();
if (!extent || getArea(extent) === 0) {
this.setExtent(null);
}
return false; //Stop handling downup sequence
}
/**
* @inheritDoc
*/
@@ -310,113 +403,6 @@ class ExtentInteraction extends PointerInteraction {
}
}
/**
* @param {import("../MapBrowserEvent.js").default} mapBrowserEvent Event.
* @return {boolean} Propagate event?
* @this {ExtentInteraction}
*/
function handleEvent(mapBrowserEvent) {
if (!(mapBrowserEvent instanceof MapBrowserPointerEvent)) {
return true;
}
//display pointer (if not dragging)
if (mapBrowserEvent.type == MapBrowserEventType.POINTERMOVE && !this.handlingDownUpSequence) {
this.handlePointerMove_(mapBrowserEvent);
}
//call pointer to determine up/down/drag
handlePointerEvent.call(this, mapBrowserEvent);
//return false to stop propagation
return false;
}
/**
* @param {import("../MapBrowserPointerEvent.js").default} mapBrowserEvent Event.
* @return {boolean} Event handled?
* @this {ExtentInteraction}
*/
function handleDownEvent(mapBrowserEvent) {
const pixel = mapBrowserEvent.pixel;
const map = mapBrowserEvent.map;
const extent = this.getExtent();
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.getCoordinateFromPixel(pixel);
this.setExtent([vertex[0], vertex[1], vertex[0], vertex[1]]);
this.pointerHandler_ = getPointHandler(vertex);
}
return true; //event handled; start downup sequence
}
/**
* @param {import("../MapBrowserPointerEvent.js").default} mapBrowserEvent Event.
* @return {boolean} Event handled?
* @this {ExtentInteraction}
*/
function handleDragEvent(mapBrowserEvent) {
if (this.pointerHandler_) {
const pixelCoordinate = mapBrowserEvent.coordinate;
this.setExtent(this.pointerHandler_(pixelCoordinate));
this.createOrUpdatePointerFeature_(pixelCoordinate);
}
return true;
}
/**
* @param {import("../MapBrowserPointerEvent.js").default} mapBrowserEvent Event.
* @return {boolean} Stop drag sequence?
* @this {ExtentInteraction}
*/
function handleUpEvent(mapBrowserEvent) {
this.pointerHandler_ = null;
//If bbox is zero area, set to null;
const extent = this.getExtent();
if (!extent || getArea(extent) === 0) {
this.setExtent(null);
}
return false; //Stop handling downup sequence
}
/**
* Returns the default style for the drawn bbox
*

View File

@@ -38,6 +38,10 @@ class Interaction extends BaseObject {
constructor(options) {
super();
if (options.handleEvent) {
this.handleEvent = options.handleEvent;
}
/**
* @private
* @type {import("../PluggableMap.js").default}
@@ -45,12 +49,6 @@ class Interaction extends BaseObject {
this.map_ = null;
this.setActive(true);
/**
* @type {function(import("../MapBrowserEvent.js").default):boolean}
*/
this.handleEvent = options.handleEvent;
}
/**
@@ -72,6 +70,16 @@ class Interaction extends BaseObject {
return this.map_;
}
/**
* Handles the {@link module:ol/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.

View File

@@ -27,7 +27,7 @@ import Interaction, {zoomByDelta} from '../interaction/Interaction.js';
* {@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 moudle:ol/interaction/KeyboardPan~KeyboardPan}.
* See also {@link module:ol/interaction/KeyboardPan~KeyboardPan}.
* @api
*/
class KeyboardZoom extends Interaction {

View File

@@ -6,7 +6,6 @@ import Collection from '../Collection.js';
import CollectionEventType from '../CollectionEventType.js';
import Feature from '../Feature.js';
import MapBrowserEventType from '../MapBrowserEventType.js';
import MapBrowserPointerEvent from '../MapBrowserPointerEvent.js';
import {equals} from '../array.js';
import {equals as coordinatesEqual, distance as coordinateDistance, squaredDistance as squaredCoordinateDistance, squaredDistanceToSegment, closestOnSegment} from '../coordinate.js';
import {listen, unlisten} from '../events.js';
@@ -16,7 +15,7 @@ import {always, primaryAction, altKeyOnly, singleClick} from '../events/conditio
import {boundingExtent, buffer, createOrUpdateFromCoordinate} from '../extent.js';
import GeometryType from '../geom/GeometryType.js';
import Point from '../geom/Point.js';
import PointerInteraction, {handleEvent as handlePointerEvent} from '../interaction/Pointer.js';
import PointerInteraction from '../interaction/Pointer.js';
import VectorLayer from '../layer/Vector.js';
import VectorSource from '../source/Vector.js';
import VectorEventType from '../source/VectorEventType.js';
@@ -63,7 +62,7 @@ const ModifyEventType = {
* @property {Array<number>} [depth]
* @property {Feature} feature
* @property {import("../geom/SimpleGeometry.js").default} geometry
* @property {number} index
* @property {number} [index]
* @property {Array<import("../extent.js").Extent>} segment
* @property {Array<SegmentData>} [featureSegments]
*/
@@ -87,7 +86,7 @@ const ModifyEventType = {
* features. Default is {@link module:ol/events/condition~always}.
* @property {number} [pixelTolerance=10] Pixel tolerance for considering the
* pointer close enough to a segment or vertex for editing.
* @property {import("../style/Style.js").default|Array<import("../style/Style.js").default>|import("../style/Style.js").StyleFunction} [style]
* @property {import("../style/Style.js").StyleLike} [style]
* Style used for the features being modified. By default the default edit
* style is used (see {@link module:ol/style}).
* @property {VectorSource} [source] The vector source with
@@ -111,7 +110,7 @@ export class ModifyEvent extends Event {
* @param {ModifyEventType} type Type.
* @param {Collection<Feature>} features
* The features modified.
* @param {MapBrowserPointerEvent} mapBrowserPointerEvent
* @param {import("../MapBrowserPointerEvent.js").default} mapBrowserPointerEvent
* Associated {@link module:ol/MapBrowserPointerEvent}.
*/
constructor(type, features, mapBrowserPointerEvent) {
@@ -157,12 +156,7 @@ class Modify extends PointerInteraction {
*/
constructor(options) {
super({
handleDownEvent: handleDownEvent,
handleDragEvent: handleDragEvent,
handleEvent: handleEvent,
handleUpEvent: handleUpEvent
});
super(/** @type {import("./Pointer.js").Options} */ (options));
/**
* @private
@@ -170,7 +164,6 @@ class Modify extends PointerInteraction {
*/
this.condition_ = options.condition ? options.condition : primaryAction;
/**
* @private
* @param {import("../MapBrowserEvent.js").default} mapBrowserEvent Browser event.
@@ -330,7 +323,7 @@ class Modify extends PointerInteraction {
this.handleFeatureRemove_, this);
/**
* @type {MapBrowserPointerEvent}
* @type {import("../MapBrowserPointerEvent.js").default}
* @private
*/
this.lastPointerEvent_ = null;
@@ -355,7 +348,7 @@ class Modify extends PointerInteraction {
}
/**
* @param {MapBrowserPointerEvent} evt Map browser event
* @param {import("../MapBrowserPointerEvent.js").default} evt Map browser event
* @private
*/
willModifyFeatures_(evt) {
@@ -375,7 +368,7 @@ class Modify extends PointerInteraction {
// Remove the vertex feature if the collection of canditate features
// is empty.
if (this.vertexFeature_ && this.features_.getLength() === 0) {
this.overlay_.getSource().removeFeature(this.vertexFeature_);
/** @type {VectorSource} */ (this.overlay_.getSource()).removeFeature(this.vertexFeature_);
this.vertexFeature_ = null;
}
unlisten(feature, EventType.CHANGE,
@@ -408,7 +401,7 @@ class Modify extends PointerInteraction {
*/
setActive(active) {
if (this.vertexFeature_ && !active) {
this.overlay_.getSource().removeFeature(this.vertexFeature_);
/** @type {VectorSource} */ (this.overlay_.getSource()).removeFeature(this.vertexFeature_);
this.vertexFeature_ = null;
}
super.setActive(active);
@@ -659,7 +652,7 @@ class Modify extends PointerInteraction {
if (!vertexFeature) {
vertexFeature = new Feature(new Point(coordinates));
this.vertexFeature_ = vertexFeature;
this.overlay_.getSource().addFeature(vertexFeature);
/** @type {VectorSource} */ (this.overlay_.getSource()).addFeature(vertexFeature);
} else {
const geometry = /** @type {Point} */ (vertexFeature.getGeometry());
geometry.setCoordinates(coordinates);
@@ -667,6 +660,211 @@ class Modify extends PointerInteraction {
return vertexFeature;
}
/**
* Handles the {@link module:ol/MapBrowserEvent map browser event} and may modify the geometry.
* @override
*/
handleEvent(mapBrowserEvent) {
if (!(/** @type {import("../MapBrowserPointerEvent.js").default} */ (mapBrowserEvent).pointerEvent)) {
return true;
}
this.lastPointerEvent_ = mapBrowserEvent;
let handled;
if (!mapBrowserEvent.map.getView().getInteracting() &&
mapBrowserEvent.type == MapBrowserEventType.POINTERMOVE &&
!this.handlingDownUpSequence) {
this.handlePointerMove_(mapBrowserEvent);
}
if (this.vertexFeature_ && this.deleteCondition_(mapBrowserEvent)) {
if (mapBrowserEvent.type != MapBrowserEventType.SINGLECLICK || !this.ignoreNextSingleClick_) {
handled = this.removePoint();
} else {
handled = true;
}
}
if (mapBrowserEvent.type == MapBrowserEventType.SINGLECLICK) {
this.ignoreNextSingleClick_ = false;
}
return super.handleEvent(mapBrowserEvent) && !handled;
}
/**
* @inheritDoc
*/
handleDragEvent(evt) {
this.ignoreNextSingleClick_ = false;
this.willModifyFeatures_(evt);
const vertex = evt.coordinate;
for (let i = 0, ii = this.dragSegments_.length; i < ii; ++i) {
const dragSegment = this.dragSegments_[i];
const segmentData = dragSegment[0];
const depth = segmentData.depth;
const geometry = segmentData.geometry;
let coordinates;
const segment = segmentData.segment;
const index = dragSegment[1];
while (vertex.length < geometry.getStride()) {
vertex.push(segment[index][vertex.length]);
}
switch (geometry.getType()) {
case GeometryType.POINT:
coordinates = vertex;
segment[0] = segment[1] = vertex;
break;
case GeometryType.MULTI_POINT:
coordinates = geometry.getCoordinates();
coordinates[segmentData.index] = vertex;
segment[0] = segment[1] = vertex;
break;
case GeometryType.LINE_STRING:
coordinates = geometry.getCoordinates();
coordinates[segmentData.index + index] = vertex;
segment[index] = vertex;
break;
case GeometryType.MULTI_LINE_STRING:
coordinates = geometry.getCoordinates();
coordinates[depth[0]][segmentData.index + index] = vertex;
segment[index] = vertex;
break;
case GeometryType.POLYGON:
coordinates = geometry.getCoordinates();
coordinates[depth[0]][segmentData.index + index] = vertex;
segment[index] = vertex;
break;
case GeometryType.MULTI_POLYGON:
coordinates = geometry.getCoordinates();
coordinates[depth[1]][depth[0]][segmentData.index + index] = vertex;
segment[index] = vertex;
break;
case GeometryType.CIRCLE:
segment[0] = segment[1] = vertex;
if (segmentData.index === CIRCLE_CENTER_INDEX) {
this.changingFeature_ = true;
geometry.setCenter(vertex);
this.changingFeature_ = false;
} else { // We're dragging the circle's circumference:
this.changingFeature_ = true;
geometry.setRadius(coordinateDistance(geometry.getCenter(), vertex));
this.changingFeature_ = false;
}
break;
default:
// pass
}
if (coordinates) {
this.setGeometryCoordinates_(geometry, coordinates);
}
}
this.createOrUpdateVertexFeature_(vertex);
}
/**
* @inheritDoc
*/
handleDownEvent(evt) {
if (!this.condition_(evt)) {
return false;
}
this.handlePointerAtPixel_(evt.pixel, evt.map);
const pixelCoordinate = evt.map.getCoordinateFromPixel(evt.pixel);
this.dragSegments_.length = 0;
this.modified_ = false;
const vertexFeature = this.vertexFeature_;
if (vertexFeature) {
const insertVertices = [];
const geometry = /** @type {Point} */ (vertexFeature.getGeometry());
const vertex = geometry.getCoordinates();
const vertexExtent = boundingExtent([vertex]);
const segmentDataMatches = this.rBush_.getInExtent(vertexExtent);
const componentSegments = {};
segmentDataMatches.sort(compareIndexes);
for (let i = 0, ii = segmentDataMatches.length; i < ii; ++i) {
const segmentDataMatch = segmentDataMatches[i];
const segment = segmentDataMatch.segment;
let uid = getUid(segmentDataMatch.feature);
const depth = segmentDataMatch.depth;
if (depth) {
uid += '-' + depth.join('-'); // separate feature components
}
if (!componentSegments[uid]) {
componentSegments[uid] = new Array(2);
}
if (segmentDataMatch.geometry.getType() === GeometryType.CIRCLE &&
segmentDataMatch.index === CIRCLE_CIRCUMFERENCE_INDEX) {
const closestVertex = closestOnSegmentData(pixelCoordinate, segmentDataMatch);
if (coordinatesEqual(closestVertex, vertex) && !componentSegments[uid][0]) {
this.dragSegments_.push([segmentDataMatch, 0]);
componentSegments[uid][0] = segmentDataMatch;
}
} else if (coordinatesEqual(segment[0], vertex) &&
!componentSegments[uid][0]) {
this.dragSegments_.push([segmentDataMatch, 0]);
componentSegments[uid][0] = segmentDataMatch;
} else if (coordinatesEqual(segment[1], vertex) &&
!componentSegments[uid][1]) {
// prevent dragging closed linestrings by the connecting node
if ((segmentDataMatch.geometry.getType() ===
GeometryType.LINE_STRING ||
segmentDataMatch.geometry.getType() ===
GeometryType.MULTI_LINE_STRING) &&
componentSegments[uid][0] &&
componentSegments[uid][0].index === 0) {
continue;
}
this.dragSegments_.push([segmentDataMatch, 1]);
componentSegments[uid][1] = segmentDataMatch;
} else if (this.insertVertexCondition_(evt) && getUid(segment) in this.vertexSegments_ &&
(!componentSegments[uid][0] && !componentSegments[uid][1])) {
insertVertices.push([segmentDataMatch, vertex]);
}
}
if (insertVertices.length) {
this.willModifyFeatures_(evt);
}
for (let j = insertVertices.length - 1; j >= 0; --j) {
this.insertVertex_.apply(this, insertVertices[j]);
}
}
return !!this.vertexFeature_;
}
/**
* @inheritDoc
*/
handleUpEvent(evt) {
for (let i = this.dragSegments_.length - 1; i >= 0; --i) {
const segmentData = this.dragSegments_[i][0];
const geometry = segmentData.geometry;
if (geometry.getType() === GeometryType.CIRCLE) {
// Update a circle object in the R* bush:
const coordinates = geometry.getCenter();
const centerSegmentData = segmentData.featureSegments[0];
const circumferenceSegmentData = segmentData.featureSegments[1];
centerSegmentData.segment[0] = centerSegmentData.segment[1] = coordinates;
circumferenceSegmentData.segment[0] = circumferenceSegmentData.segment[1] = coordinates;
this.rBush_.update(createOrUpdateFromCoordinate(coordinates), centerSegmentData);
this.rBush_.update(geometry.getExtent(), circumferenceSegmentData);
} else {
this.rBush_.update(boundingExtent(segmentData.segment), segmentData);
}
}
if (this.modified_) {
this.dispatchEvent(new ModifyEvent(ModifyEventType.MODIFYEND, this.features_, evt));
this.modified_ = false;
}
return false;
}
/**
* @param {import("../MapBrowserEvent.js").default} evt Event.
* @private
@@ -701,6 +899,7 @@ class Modify extends PointerInteraction {
const vertexPixel = map.getPixelFromCoordinate(vertex);
let dist = coordinateDistance(pixel, vertexPixel);
if (dist <= this.pixelTolerance_) {
/** @type {Object<string, boolean>} */
const vertexSegments = {};
if (node.geometry.getType() === GeometryType.CIRCLE &&
@@ -738,7 +937,7 @@ class Modify extends PointerInteraction {
}
}
if (this.vertexFeature_) {
this.overlay_.getSource().removeFeature(this.vertexFeature_);
/** @type {VectorSource} */ (this.overlay_.getSource()).removeFeature(this.vertexFeature_);
this.vertexFeature_ = null;
}
}
@@ -933,7 +1132,7 @@ class Modify extends PointerInteraction {
}
this.updateSegmentIndices_(geometry, index, segmentData.depth, -1);
if (this.vertexFeature_) {
this.overlay_.getSource().removeFeature(this.vertexFeature_);
/** @type {VectorSource} */ (this.overlay_.getSource()).removeFeature(this.vertexFeature_);
this.vertexFeature_ = null;
}
dragSegments.length = 0;
@@ -984,223 +1183,6 @@ function compareIndexes(a, b) {
}
/**
* @param {MapBrowserPointerEvent} evt Event.
* @return {boolean} Start drag sequence?
* @this {Modify}
*/
function handleDownEvent(evt) {
if (!this.condition_(evt)) {
return false;
}
this.handlePointerAtPixel_(evt.pixel, evt.map);
const pixelCoordinate = evt.map.getCoordinateFromPixel(evt.pixel);
this.dragSegments_.length = 0;
this.modified_ = false;
const vertexFeature = this.vertexFeature_;
if (vertexFeature) {
const insertVertices = [];
const geometry = /** @type {Point} */ (vertexFeature.getGeometry());
const vertex = geometry.getCoordinates();
const vertexExtent = boundingExtent([vertex]);
const segmentDataMatches = this.rBush_.getInExtent(vertexExtent);
const componentSegments = {};
segmentDataMatches.sort(compareIndexes);
for (let i = 0, ii = segmentDataMatches.length; i < ii; ++i) {
const segmentDataMatch = segmentDataMatches[i];
const segment = segmentDataMatch.segment;
let uid = getUid(segmentDataMatch.feature);
const depth = segmentDataMatch.depth;
if (depth) {
uid += '-' + depth.join('-'); // separate feature components
}
if (!componentSegments[uid]) {
componentSegments[uid] = new Array(2);
}
if (segmentDataMatch.geometry.getType() === GeometryType.CIRCLE &&
segmentDataMatch.index === CIRCLE_CIRCUMFERENCE_INDEX) {
const closestVertex = closestOnSegmentData(pixelCoordinate, segmentDataMatch);
if (coordinatesEqual(closestVertex, vertex) && !componentSegments[uid][0]) {
this.dragSegments_.push([segmentDataMatch, 0]);
componentSegments[uid][0] = segmentDataMatch;
}
} else if (coordinatesEqual(segment[0], vertex) &&
!componentSegments[uid][0]) {
this.dragSegments_.push([segmentDataMatch, 0]);
componentSegments[uid][0] = segmentDataMatch;
} else if (coordinatesEqual(segment[1], vertex) &&
!componentSegments[uid][1]) {
// prevent dragging closed linestrings by the connecting node
if ((segmentDataMatch.geometry.getType() ===
GeometryType.LINE_STRING ||
segmentDataMatch.geometry.getType() ===
GeometryType.MULTI_LINE_STRING) &&
componentSegments[uid][0] &&
componentSegments[uid][0].index === 0) {
continue;
}
this.dragSegments_.push([segmentDataMatch, 1]);
componentSegments[uid][1] = segmentDataMatch;
} else if (this.insertVertexCondition_(evt) && getUid(segment) in this.vertexSegments_ &&
(!componentSegments[uid][0] && !componentSegments[uid][1])) {
insertVertices.push([segmentDataMatch, vertex]);
}
}
if (insertVertices.length) {
this.willModifyFeatures_(evt);
}
for (let j = insertVertices.length - 1; j >= 0; --j) {
this.insertVertex_.apply(this, insertVertices[j]);
}
}
return !!this.vertexFeature_;
}
/**
* @param {MapBrowserPointerEvent} evt Event.
* @this {Modify}
*/
function handleDragEvent(evt) {
this.ignoreNextSingleClick_ = false;
this.willModifyFeatures_(evt);
const vertex = evt.coordinate;
for (let i = 0, ii = this.dragSegments_.length; i < ii; ++i) {
const dragSegment = this.dragSegments_[i];
const segmentData = dragSegment[0];
const depth = segmentData.depth;
const geometry = segmentData.geometry;
let coordinates;
const segment = segmentData.segment;
const index = dragSegment[1];
while (vertex.length < geometry.getStride()) {
vertex.push(segment[index][vertex.length]);
}
switch (geometry.getType()) {
case GeometryType.POINT:
coordinates = vertex;
segment[0] = segment[1] = vertex;
break;
case GeometryType.MULTI_POINT:
coordinates = geometry.getCoordinates();
coordinates[segmentData.index] = vertex;
segment[0] = segment[1] = vertex;
break;
case GeometryType.LINE_STRING:
coordinates = geometry.getCoordinates();
coordinates[segmentData.index + index] = vertex;
segment[index] = vertex;
break;
case GeometryType.MULTI_LINE_STRING:
coordinates = geometry.getCoordinates();
coordinates[depth[0]][segmentData.index + index] = vertex;
segment[index] = vertex;
break;
case GeometryType.POLYGON:
coordinates = geometry.getCoordinates();
coordinates[depth[0]][segmentData.index + index] = vertex;
segment[index] = vertex;
break;
case GeometryType.MULTI_POLYGON:
coordinates = geometry.getCoordinates();
coordinates[depth[1]][depth[0]][segmentData.index + index] = vertex;
segment[index] = vertex;
break;
case GeometryType.CIRCLE:
segment[0] = segment[1] = vertex;
if (segmentData.index === CIRCLE_CENTER_INDEX) {
this.changingFeature_ = true;
geometry.setCenter(vertex);
this.changingFeature_ = false;
} else { // We're dragging the circle's circumference:
this.changingFeature_ = true;
geometry.setRadius(coordinateDistance(geometry.getCenter(), vertex));
this.changingFeature_ = false;
}
break;
default:
// pass
}
if (coordinates) {
this.setGeometryCoordinates_(geometry, coordinates);
}
}
this.createOrUpdateVertexFeature_(vertex);
}
/**
* @param {MapBrowserPointerEvent} evt Event.
* @return {boolean} Stop drag sequence?
* @this {Modify}
*/
function handleUpEvent(evt) {
for (let i = this.dragSegments_.length - 1; i >= 0; --i) {
const segmentData = this.dragSegments_[i][0];
const geometry = segmentData.geometry;
if (geometry.getType() === GeometryType.CIRCLE) {
// Update a circle object in the R* bush:
const coordinates = geometry.getCenter();
const centerSegmentData = segmentData.featureSegments[0];
const circumferenceSegmentData = segmentData.featureSegments[1];
centerSegmentData.segment[0] = centerSegmentData.segment[1] = coordinates;
circumferenceSegmentData.segment[0] = circumferenceSegmentData.segment[1] = coordinates;
this.rBush_.update(createOrUpdateFromCoordinate(coordinates), centerSegmentData);
this.rBush_.update(geometry.getExtent(), circumferenceSegmentData);
} else {
this.rBush_.update(boundingExtent(segmentData.segment), segmentData);
}
}
if (this.modified_) {
this.dispatchEvent(new ModifyEvent(ModifyEventType.MODIFYEND, this.features_, evt));
this.modified_ = false;
}
return false;
}
/**
* Handles the {@link module:ol/MapBrowserEvent map browser event} and may modify the
* geometry.
* @param {import("../MapBrowserEvent.js").default} mapBrowserEvent Map browser event.
* @return {boolean} `false` to stop event propagation.
* @this {Modify}
*/
function handleEvent(mapBrowserEvent) {
if (!(mapBrowserEvent instanceof MapBrowserPointerEvent)) {
return true;
}
this.lastPointerEvent_ = mapBrowserEvent;
let handled;
if (!mapBrowserEvent.map.getView().getInteracting() &&
mapBrowserEvent.type == MapBrowserEventType.POINTERMOVE &&
!this.handlingDownUpSequence) {
this.handlePointerMove_(mapBrowserEvent);
}
if (this.vertexFeature_ && this.deleteCondition_(mapBrowserEvent)) {
if (mapBrowserEvent.type != MapBrowserEventType.SINGLECLICK || !this.ignoreNextSingleClick_) {
handled = this.removePoint();
} else {
handled = true;
}
}
if (mapBrowserEvent.type == MapBrowserEventType.SINGLECLICK) {
this.ignoreNextSingleClick_ = false;
}
return handlePointerEvent.call(this, mapBrowserEvent) && !handled;
}
/**
* Returns the distance from a point to a line segment.
*

View File

@@ -54,11 +54,9 @@ class MouseWheelZoom extends Interaction {
*/
constructor(opt_options) {
super({
handleEvent: handleEvent
});
const options = opt_options ? opt_options : {};
const options = opt_options || {};
super(/** @type {import("./Interaction.js").InteractionOptions} */ (options));
/**
* @private
@@ -110,9 +108,9 @@ class MouseWheelZoom extends Interaction {
/**
* @private
* @type {number|undefined}
* @type {?}
*/
this.timeoutId_ = undefined;
this.timeoutId_;
/**
* @private
@@ -128,9 +126,9 @@ class MouseWheelZoom extends Interaction {
this.trackpadEventGap_ = 400;
/**
* @type {number|undefined}
* @type {?}
*/
this.trackpadTimeoutId_ = undefined;
this.trackpadTimeoutId_;
/**
* The number of delta values per zoom level
@@ -157,6 +155,127 @@ class MouseWheelZoom extends Interaction {
view.setHint(ViewHint.INTERACTING, -1);
}
/**
* Handles the {@link module:ol/MapBrowserEvent map browser event} (if it was a mousewheel-event) and eventually
* zooms the map.
* @override
*/
handleEvent(mapBrowserEvent) {
if (!this.condition_(mapBrowserEvent)) {
return true;
}
const type = mapBrowserEvent.type;
if (type !== EventType.WHEEL && type !== EventType.MOUSEWHEEL) {
return true;
}
mapBrowserEvent.preventDefault();
const map = mapBrowserEvent.map;
const wheelEvent = /** @type {WheelEvent} */ (mapBrowserEvent.originalEvent);
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;
}
} else if (mapBrowserEvent.type == EventType.MOUSEWHEEL) {
delta = -wheelEvent.wheelDeltaY;
if (SAFARI) {
delta /= 3;
}
}
if (delta === 0) {
return false;
}
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;
}
if (this.mode_ === Mode.TRACKPAD) {
const view = map.getView();
if (this.trackpadTimeoutId_) {
clearTimeout(this.trackpadTimeoutId_);
} else {
view.setHint(ViewHint.INTERACTING, 1);
}
this.trackpadTimeoutId_ = setTimeout(this.decrementInteractingHint_.bind(this), this.trackpadEventGap_);
let resolution = view.getResolution() * Math.pow(2, delta / this.trackpadDeltaPerZoom_);
const minResolution = view.getMinResolution();
const maxResolution = view.getMaxResolution();
let rebound = 0;
if (resolution < minResolution) {
resolution = Math.max(resolution, minResolution / this.trackpadZoomBuffer_);
rebound = 1;
} else if (resolution > maxResolution) {
resolution = Math.min(resolution, maxResolution * this.trackpadZoomBuffer_);
rebound = -1;
}
if (this.lastAnchor_) {
const center = view.calculateCenterZoom(resolution, this.lastAnchor_);
view.setCenter(view.constrainCenter(center));
}
view.setResolution(resolution);
if (rebound === 0 && this.constrainResolution_) {
view.animate({
resolution: view.constrainResolution(resolution, delta > 0 ? -1 : 1),
easing: easeOut,
anchor: this.lastAnchor_,
duration: this.duration_
});
}
if (rebound > 0) {
view.animate({
resolution: minResolution,
easing: easeOut,
anchor: this.lastAnchor_,
duration: 500
});
} else if (rebound < 0) {
view.animate({
resolution: maxResolution,
easing: easeOut,
anchor: this.lastAnchor_,
duration: 500
});
}
this.startTime_ = now;
return false;
}
this.delta_ += 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.
@@ -190,129 +309,4 @@ class MouseWheelZoom extends Interaction {
}
}
/**
* Handles the {@link module:ol/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} Allow event propagation.
* @this {MouseWheelZoom}
*/
function handleEvent(mapBrowserEvent) {
if (!this.condition_(mapBrowserEvent)) {
return true;
}
const type = mapBrowserEvent.type;
if (type !== EventType.WHEEL && type !== EventType.MOUSEWHEEL) {
return true;
}
mapBrowserEvent.preventDefault();
const map = mapBrowserEvent.map;
const wheelEvent = /** @type {WheelEvent} */ (mapBrowserEvent.originalEvent);
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;
}
} else if (mapBrowserEvent.type == EventType.MOUSEWHEEL) {
delta = -wheelEvent.wheelDeltaY;
if (SAFARI) {
delta /= 3;
}
}
if (delta === 0) {
return false;
}
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;
}
if (this.mode_ === Mode.TRACKPAD) {
const view = map.getView();
if (this.trackpadTimeoutId_) {
clearTimeout(this.trackpadTimeoutId_);
} else {
view.setHint(ViewHint.INTERACTING, 1);
}
this.trackpadTimeoutId_ = setTimeout(this.decrementInteractingHint_.bind(this), this.trackpadEventGap_);
let resolution = view.getResolution() * Math.pow(2, delta / this.trackpadDeltaPerZoom_);
const minResolution = view.getMinResolution();
const maxResolution = view.getMaxResolution();
let rebound = 0;
if (resolution < minResolution) {
resolution = Math.max(resolution, minResolution / this.trackpadZoomBuffer_);
rebound = 1;
} else if (resolution > maxResolution) {
resolution = Math.min(resolution, maxResolution * this.trackpadZoomBuffer_);
rebound = -1;
}
if (this.lastAnchor_) {
const center = view.calculateCenterZoom(resolution, this.lastAnchor_);
view.setCenter(view.constrainCenter(center));
}
view.setResolution(resolution);
if (rebound === 0 && this.constrainResolution_) {
view.animate({
resolution: view.constrainResolution(resolution, delta > 0 ? -1 : 1),
easing: easeOut,
anchor: this.lastAnchor_,
duration: this.duration_
});
}
if (rebound > 0) {
view.animate({
resolution: minResolution,
easing: easeOut,
anchor: this.lastAnchor_,
duration: 500
});
} else if (rebound < 0) {
view.animate({
resolution: maxResolution,
easing: easeOut,
anchor: this.lastAnchor_,
duration: 500
});
}
this.startTime_ = now;
return false;
}
this.delta_ += 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;
}
export default MouseWheelZoom;

View File

@@ -28,14 +28,15 @@ class PinchRotate extends PointerInteraction {
*/
constructor(opt_options) {
super({
handleDownEvent: handleDownEvent,
handleDragEvent: handleDragEvent,
handleUpEvent: handleUpEvent,
stopDown: FALSE
});
const options = opt_options ? opt_options : {};
const options = opt_options || {};
const pointerOptions = /** @type {import("./Pointer.js").Options} */ (options);
if (!pointerOptions.stopDown) {
pointerOptions.stopDown = FALSE;
}
super(pointerOptions);
/**
* @private
@@ -75,98 +76,89 @@ class PinchRotate extends PointerInteraction {
}
}
/**
* @inheritDoc
*/
handleDragEvent(mapBrowserEvent) {
let rotationDelta = 0.0;
const touch0 = this.targetPointers[0];
const touch1 = this.targetPointers[1];
/**
* @param {import("../MapBrowserPointerEvent.js").default} mapBrowserEvent Event.
* @this {PinchRotate}
*/
function handleDragEvent(mapBrowserEvent) {
let rotationDelta = 0.0;
// angle between touches
const angle = Math.atan2(
touch1.clientY - touch0.clientY,
touch1.clientX - touch0.clientX);
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;
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;
}
rotationDelta = delta;
}
this.lastAngle_ = angle;
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.getCoordinateFromPixel(centroid);
// rotate
if (this.rotating_) {
const rotation = view.getRotation();
map.render();
rotateWithoutConstraints(view, rotation + rotationDelta, this.anchor_);
}
}
/**
* @param {import("../MapBrowserPointerEvent.js").default} mapBrowserEvent Event.
* @return {boolean} Stop drag sequence?
* @this {PinchRotate}
*/
function handleUpEvent(mapBrowserEvent) {
if (this.targetPointers.length < 2) {
const map = mapBrowserEvent.map;
const view = map.getView();
view.setHint(ViewHint.INTERACTING, -1);
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.getCoordinateFromPixel(centroid);
// rotate
if (this.rotating_) {
const rotation = view.getRotation();
rotate(view, rotation, this.anchor_, this.duration_);
map.render();
rotateWithoutConstraints(view, rotation + rotationDelta, this.anchor_);
}
return false;
} else {
return true;
}
}
/**
* @param {import("../MapBrowserPointerEvent.js").default} mapBrowserEvent Event.
* @return {boolean} Start drag sequence?
* @this {PinchRotate}
*/
function 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().setHint(ViewHint.INTERACTING, 1);
/**
* @inheritDoc
*/
handleUpEvent(mapBrowserEvent) {
if (this.targetPointers.length < 2) {
const map = mapBrowserEvent.map;
const view = map.getView();
view.setHint(ViewHint.INTERACTING, -1);
if (this.rotating_) {
const rotation = view.getRotation();
rotate(view, rotation, this.anchor_, this.duration_);
}
return false;
} else {
return true;
}
}
/**
* @inheritDoc
*/
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().setHint(ViewHint.INTERACTING, 1);
}
return true;
} else {
return false;
}
return true;
} else {
return false;
}
}

View File

@@ -27,15 +27,16 @@ class PinchZoom extends PointerInteraction {
*/
constructor(opt_options) {
super({
handleDownEvent: handleDownEvent,
handleDragEvent: handleDragEvent,
handleUpEvent: handleUpEvent,
stopDown: FALSE
});
const options = opt_options ? opt_options : {};
const pointerOptions = /** @type {import("./Pointer.js").Options} */ (options);
if (!pointerOptions.stopDown) {
pointerOptions.stopDown = FALSE;
}
super(pointerOptions);
/**
* @private
* @type {boolean}
@@ -68,105 +69,96 @@ class PinchZoom extends PointerInteraction {
}
}
/**
* @inheritDoc
*/
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;
/**
* @param {import("../MapBrowserPointerEvent.js").default} mapBrowserEvent Event.
* @this {PinchZoom}
*/
function 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();
const resolution = view.getResolution();
const maxResolution = view.getMaxResolution();
const minResolution = view.getMinResolution();
let newResolution = resolution * scaleDelta;
if (newResolution > maxResolution) {
scaleDelta = maxResolution / resolution;
newResolution = maxResolution;
} else if (newResolution < minResolution) {
scaleDelta = minResolution / resolution;
newResolution = minResolution;
}
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.getCoordinateFromPixel(centroid);
// scale, bypass the resolution constraint
map.render();
zoomWithoutConstraints(view, newResolution, this.anchor_);
}
/**
* @param {import("../MapBrowserPointerEvent.js").default} mapBrowserEvent Event.
* @return {boolean} Stop drag sequence?
* @this {PinchZoom}
*/
function handleUpEvent(mapBrowserEvent) {
if (this.targetPointers.length < 2) {
const map = mapBrowserEvent.map;
const view = map.getView();
view.setHint(ViewHint.INTERACTING, -1);
const resolution = view.getResolution();
if (this.constrainResolution_ ||
resolution < view.getMinResolution() ||
resolution > view.getMaxResolution()) {
// Zoom to final resolution, with an animation, and provide a
// direction not to zoom out/in if user was pinching in/out.
// Direction is > 0 if pinching out, and < 0 if pinching in.
const direction = this.lastScaleDelta_ - 1;
zoom(view, resolution, this.anchor_, this.duration_, direction);
const maxResolution = view.getMaxResolution();
const minResolution = view.getMinResolution();
let newResolution = resolution * scaleDelta;
if (newResolution > maxResolution) {
scaleDelta = maxResolution / resolution;
newResolution = maxResolution;
} else if (newResolution < minResolution) {
scaleDelta = minResolution / resolution;
newResolution = minResolution;
}
return false;
} else {
return true;
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.getCoordinateFromPixel(centroid);
// scale, bypass the resolution constraint
map.render();
zoomWithoutConstraints(view, newResolution, this.anchor_);
}
}
/**
* @param {import("../MapBrowserPointerEvent.js").default} mapBrowserEvent Event.
* @return {boolean} Start drag sequence?
* @this {PinchZoom}
*/
function 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().setHint(ViewHint.INTERACTING, 1);
/**
* @inheritDoc
*/
handleUpEvent(mapBrowserEvent) {
if (this.targetPointers.length < 2) {
const map = mapBrowserEvent.map;
const view = map.getView();
view.setHint(ViewHint.INTERACTING, -1);
const resolution = view.getResolution();
if (this.constrainResolution_ ||
resolution < view.getMinResolution() ||
resolution > view.getMaxResolution()) {
// Zoom to final resolution, with an animation, and provide a
// direction not to zoom out/in if user was pinching in/out.
// Direction is > 0 if pinching out, and < 0 if pinching in.
const direction = this.lastScaleDelta_ - 1;
zoom(view, resolution, this.anchor_, this.duration_, direction);
}
return false;
} else {
return true;
}
}
/**
* @inheritDoc
*/
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().setHint(ViewHint.INTERACTING, 1);
}
return true;
} else {
return false;
}
return true;
} else {
return false;
}
}

View File

@@ -1,49 +1,17 @@
/**
* @module ol/interaction/Pointer
*/
import {FALSE, VOID} from '../functions.js';
import MapBrowserEventType from '../MapBrowserEventType.js';
import MapBrowserPointerEvent from '../MapBrowserPointerEvent.js';
import Interaction from '../interaction/Interaction.js';
import {getValues} from '../obj.js';
/**
* @param {MapBrowserPointerEvent} mapBrowserEvent Event.
* @this {PointerInteraction}
*/
const handleDragEvent = VOID;
/**
* @param {MapBrowserPointerEvent} mapBrowserEvent Event.
* @return {boolean} Capture dragging.
* @this {PointerInteraction}
*/
const handleUpEvent = FALSE;
/**
* @param {MapBrowserPointerEvent} mapBrowserEvent Event.
* @return {boolean} Capture dragging.
* @this {PointerInteraction}
*/
const handleDownEvent = FALSE;
/**
* @param {MapBrowserPointerEvent} mapBrowserEvent Event.
* @this {PointerInteraction}
*/
const handleMoveEvent = VOID;
/**
* @typedef {Object} Options
* @property {function(MapBrowserPointerEvent):boolean} [handleDownEvent]
* @property {function(import("../MapBrowserPointerEvent.js").default):boolean} [handleDownEvent]
* Function handling "down" events. If the function returns `true` then a drag
* sequence is started.
* @property {function(MapBrowserPointerEvent)} [handleDragEvent]
* @property {function(import("../MapBrowserPointerEvent.js").default)} [handleDragEvent]
* Function handling "drag" events. This function is called on "move" events
* during a drag sequence.
* @property {function(import("../MapBrowserEvent.js").default):boolean} [handleEvent]
@@ -51,14 +19,14 @@ const handleMoveEvent = VOID;
* 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(MapBrowserPointerEvent)} [handleMoveEvent]
* @property {function(import("../MapBrowserPointerEvent.js").default)} [handleMoveEvent]
* Function handling "move" events. This function is called on "move" events,
* also during a drag sequence (so during a drag sequence both the
* `handleDragEvent` function and this function are called).
* @property {function(MapBrowserPointerEvent):boolean} [handleUpEvent]
* @property {function(import("../MapBrowserPointerEvent.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
* @property {function(boolean):boolean} [stopDown]
* Should the down event be propagated to other interactions, or should be
* stopped?
*/
@@ -83,37 +51,27 @@ class PointerInteraction extends Interaction {
const options = opt_options ? opt_options : {};
super({
handleEvent: options.handleEvent || handleEvent
});
super(/** @type {import("./Interaction.js").InteractionOptions} */ (options));
/**
* @type {function(MapBrowserPointerEvent):boolean}
* @private
*/
this.handleDownEvent_ = options.handleDownEvent ?
options.handleDownEvent : handleDownEvent;
if (options.handleDownEvent) {
this.handleDownEvent = options.handleDownEvent;
}
/**
* @type {function(MapBrowserPointerEvent)}
* @private
*/
this.handleDragEvent_ = options.handleDragEvent ?
options.handleDragEvent : handleDragEvent;
if (options.handleDragEvent) {
this.handleDragEvent = options.handleDragEvent;
}
/**
* @type {function(MapBrowserPointerEvent)}
* @private
*/
this.handleMoveEvent_ = options.handleMoveEvent ?
options.handleMoveEvent : handleMoveEvent;
if (options.handleMoveEvent) {
this.handleMoveEvent = options.handleMoveEvent;
}
/**
* @type {function(MapBrowserPointerEvent):boolean}
* @private
*/
this.handleUpEvent_ = options.handleUpEvent ?
options.handleUpEvent : handleUpEvent;
if (options.handleUpEvent) {
this.handleUpEvent = options.handleUpEvent;
}
if (options.stopDown) {
this.stopDown = options.stopDown;
}
/**
* @type {boolean}
@@ -121,14 +79,6 @@ class PointerInteraction extends Interaction {
*/
this.handlingDownUpSequence = false;
/**
* This function is used to determine if "down" events should be propagated
* to other interactions or should be stopped.
* @type {function(boolean):boolean}
* @protected
*/
this.stopDown = options.stopDown ? options.stopDown : stopDown;
/**
* @type {!Object<string, import("../pointer/PointerEvent.js").default>}
* @private
@@ -144,7 +94,87 @@ class PointerInteraction extends Interaction {
}
/**
* @param {MapBrowserPointerEvent} mapBrowserEvent Event.
* Handle pointer down events.
* @param {import("../MapBrowserPointerEvent.js").default} mapBrowserEvent Event.
* @return {boolean} If the event was consumed.
* @protected
*/
handleDownEvent(mapBrowserEvent) {
return false;
}
/**
* Handle pointer drag events.
* @param {import("../MapBrowserPointerEvent.js").default} mapBrowserEvent Event.
* @protected
*/
handleDragEvent(mapBrowserEvent) {}
/**
* Handles the {@link module:ol/MapBrowserEvent map browser event} and may call into
* other functions, if event sequences like e.g. 'drag' or 'down-up' etc. are
* detected.
* @override
* @api
*/
handleEvent(mapBrowserEvent) {
if (!(/** @type {import("../MapBrowserPointerEvent.js").default} */ (mapBrowserEvent).pointerEvent)) {
return true;
}
let stopEvent = false;
this.updateTrackedPointers_(mapBrowserEvent);
if (this.handlingDownUpSequence) {
if (mapBrowserEvent.type == MapBrowserEventType.POINTERDRAG) {
this.handleDragEvent(mapBrowserEvent);
} 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);
if (handled) {
mapBrowserEvent.preventDefault();
}
this.handlingDownUpSequence = handled;
stopEvent = this.stopDown(handled);
} else if (mapBrowserEvent.type == MapBrowserEventType.POINTERMOVE) {
this.handleMoveEvent(mapBrowserEvent);
}
}
return !stopEvent;
}
/**
* Handle pointer move events.
* @param {import("../MapBrowserPointerEvent.js").default} mapBrowserEvent Event.
* @protected
*/
handleMoveEvent(mapBrowserEvent) {}
/**
* Handle pointer up events.
* @param {import("../MapBrowserPointerEvent.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("../MapBrowserPointerEvent.js").default} mapBrowserEvent Event.
* @private
*/
updateTrackedPointers_(mapBrowserEvent) {
@@ -185,7 +215,7 @@ export function centroid(pointerEvents) {
/**
* @param {MapBrowserPointerEvent} mapBrowserEvent Event.
* @param {import("../MapBrowserPointerEvent.js").default} mapBrowserEvent Event.
* @return {boolean} Whether the event is a pointerdown, pointerdrag
* or pointerup event.
*/
@@ -197,51 +227,4 @@ function isPointerDraggingEvent(mapBrowserEvent) {
}
/**
* Handles the {@link module:ol/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.
* @this {PointerInteraction}
* @api
*/
export function handleEvent(mapBrowserEvent) {
if (!(mapBrowserEvent instanceof MapBrowserPointerEvent)) {
return true;
}
let stopEvent = false;
this.updateTrackedPointers_(mapBrowserEvent);
if (this.handlingDownUpSequence) {
if (mapBrowserEvent.type == MapBrowserEventType.POINTERDRAG) {
this.handleDragEvent_(mapBrowserEvent);
} 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);
if (handled) {
mapBrowserEvent.preventDefault();
}
this.handlingDownUpSequence = handled;
stopEvent = this.stopDown(handled);
} else if (mapBrowserEvent.type == MapBrowserEventType.POINTERMOVE) {
this.handleMoveEvent_(mapBrowserEvent);
}
}
return !stopEvent;
}
export default PointerInteraction;
/**
* @param {boolean} handled Was the event handled by the interaction?
* @return {boolean} Should the `down` event be stopped?
*/
function stopDown(handled) {
return handled;
}

View File

@@ -34,8 +34,7 @@ const SelectEventType = {
* {@link module:ol/render/Feature} and an
* {@link module:ol/layer/Layer} and returns `true` if the feature may be
* selected or `false` otherwise.
* @typedef {function((import("../Feature.js").default|import("../render/Feature.js").default), import("../layer/Layer.js").default):
* boolean} FilterFunction
* @typedef {function(import("../Feature.js").FeatureLike, import("../layer/Layer.js").default):boolean} FilterFunction
*/
@@ -61,7 +60,7 @@ const SelectEventType = {
* 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").default|Array<import("../style/Style.js").default>|import("../style/Style.js").StyleFunction} [style]
* @property {import("../style/Style.js").StyleLike} [style]
* Style for the selected features. By default the default edit style is used
* (see {@link module:ol/style}).
* @property {import("../events/condition.js").Condition} [removeCondition] A function
@@ -251,11 +250,11 @@ class Select extends Interaction {
* An association between selected feature (key)
* and layer (value)
* @private
* @type {Object<number, import("../layer/Layer.js").default>}
* @type {Object<string, import("../layer/Layer.js").default>}
*/
this.featureLayerAssociation_ = {};
const features = this.featureOverlay_.getSource().getFeaturesCollection();
const features = this.getFeatures();
listen(features, CollectionEventType.ADD,
this.addFeature_, this);
listen(features, CollectionEventType.REMOVE,
@@ -263,13 +262,12 @@ class Select extends Interaction {
}
/**
* @param {import("../Feature.js").default|import("../render/Feature.js").default} feature Feature.
* @param {import("../Feature.js").FeatureLike} feature Feature.
* @param {import("../layer/Layer.js").default} layer Layer.
* @private
*/
addFeatureLayerAssociation_(feature, layer) {
const key = getUid(feature);
this.featureLayerAssociation_[key] = layer;
this.featureLayerAssociation_[getUid(feature)] = layer;
}
/**
@@ -278,7 +276,7 @@ class Select extends Interaction {
* @api
*/
getFeatures() {
return this.featureOverlay_.getSource().getFeaturesCollection();
return /** @type {VectorSource} */ (this.featureOverlay_.getSource()).getFeaturesCollection();
}
/**
@@ -295,14 +293,13 @@ class Select extends Interaction {
* the (last) selected feature. Note that this will not work with any
* programmatic method like pushing features to
* {@link module:ol/interaction/Select~Select#getFeatures collection}.
* @param {import("../Feature.js").default|import("../render/Feature.js").default} feature Feature
* @param {import("../Feature.js").FeatureLike} feature Feature
* @return {VectorLayer} Layer.
* @api
*/
getLayer(feature) {
const key = getUid(feature);
return (
/** @type {VectorLayer} */ (this.featureLayerAssociation_[key])
/** @type {VectorLayer} */ (this.featureLayerAssociation_[getUid(feature)])
);
}
@@ -335,8 +332,7 @@ class Select extends Interaction {
*/
setMap(map) {
const currentMap = this.getMap();
const selectedFeatures =
this.featureOverlay_.getSource().getFeaturesCollection();
const selectedFeatures = this.getFeatures();
if (currentMap) {
selectedFeatures.forEach(currentMap.unskipFeature.bind(currentMap));
}
@@ -370,12 +366,11 @@ class Select extends Interaction {
}
/**
* @param {import("../Feature.js").default|import("../render/Feature.js").default} feature Feature.
* @param {import("../Feature.js").FeatureLike} feature Feature.
* @private
*/
removeFeatureLayerAssociation_(feature) {
const key = getUid(feature);
delete this.featureLayerAssociation_[key];
delete this.featureLayerAssociation_[getUid(feature)];
}
}
@@ -396,7 +391,7 @@ function handleEvent(mapBrowserEvent) {
const toggle = this.toggleCondition_(mapBrowserEvent);
const set = !add && !remove && !toggle;
const map = mapBrowserEvent.map;
const features = this.featureOverlay_.getSource().getFeaturesCollection();
const features = this.getFeatures();
const deselected = [];
const selected = [];
if (set) {
@@ -407,7 +402,7 @@ function handleEvent(mapBrowserEvent) {
map.forEachFeatureAtPixel(mapBrowserEvent.pixel,
(
/**
* @param {import("../Feature.js").default|import("../render/Feature.js").default} feature Feature.
* @param {import("../Feature.js").FeatureLike} feature Feature.
* @param {import("../layer/Layer.js").default} layer Layer.
* @return {boolean|undefined} Continue to iterate over the features.
*/
@@ -440,7 +435,7 @@ function handleEvent(mapBrowserEvent) {
map.forEachFeatureAtPixel(mapBrowserEvent.pixel,
(
/**
* @param {import("../Feature.js").default|import("../render/Feature.js").default} feature Feature.
* @param {import("../Feature.js").FeatureLike} feature Feature.
* @param {import("../layer/Layer.js").default} layer Layer.
* @return {boolean|undefined} Continue to iterate over the features.
*/

View File

@@ -2,7 +2,6 @@
* @module ol/interaction/Snap
*/
import {getUid} from '../util.js';
import {CollectionEvent} from '../Collection.js';
import CollectionEventType from '../CollectionEventType.js';
import {distance as coordinateDistance, squaredDistance as squaredCoordinateDistance, closestOnCircle, closestOnSegment, squaredDistanceToSegment} from '../coordinate.js';
import {listen, unlistenByKey} from '../events.js';
@@ -11,9 +10,8 @@ import {boundingExtent, createEmpty} from '../extent.js';
import {TRUE, FALSE} from '../functions.js';
import GeometryType from '../geom/GeometryType.js';
import {fromCircle} from '../geom/Polygon.js';
import PointerInteraction, {handleEvent as handlePointerEvent} from '../interaction/Pointer.js';
import PointerInteraction from '../interaction/Pointer.js';
import {getValues} from '../obj.js';
import {VectorSourceEvent} from '../source/Vector.js';
import VectorEventType from '../source/VectorEventType.js';
import RBush from '../structs/RBush.js';
@@ -44,6 +42,19 @@ import RBush from '../structs/RBush.js';
*/
/**
* @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);
}
}
/**
* @classdesc
* Handles snapping of vector features while modifying or drawing them. The
@@ -71,15 +82,20 @@ class Snap extends PointerInteraction {
*/
constructor(opt_options) {
super({
handleEvent: handleEvent,
handleDownEvent: TRUE,
handleUpEvent: handleUpEvent,
stopDown: FALSE
});
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}
* @private
@@ -111,7 +127,7 @@ class Snap extends PointerInteraction {
this.featuresListenerKeys_ = [];
/**
* @type {Object<number, import("../events.js").EventsKey>}
* @type {Object<string, import("../events.js").EventsKey>}
* @private
*/
this.featureChangeListenerKeys_ = {};
@@ -119,7 +135,7 @@ class Snap extends PointerInteraction {
/**
* Extents are preserved so indexed segment can be quickly removed
* when its feature geometry changes
* @type {Object<number, import("../extent.js").Extent>}
* @type {Object<string, import("../extent.js").Extent>}
* @private
*/
this.indexedFeaturesExtents_ = {};
@@ -128,7 +144,7 @@ class Snap extends PointerInteraction {
* 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<number, import("../Feature.js").default>}
* @type {!Object<string, import("../Feature.js").default>}
* @private
*/
this.pendingFeatures_ = {};
@@ -234,37 +250,37 @@ class Snap extends PointerInteraction {
} else if (this.source_) {
features = this.source_.getFeatures();
}
return (
/** @type {!Array<import("../Feature.js").default>|!import("../Collection.js").default<import("../Feature.js").default>} */ (features)
);
return features;
}
/**
* @param {import("../source/Vector.js").default|import("../Collection.js").CollectionEvent} evt Event.
* @inheritDoc
*/
handleEvent(evt) {
const result = this.snapTo(evt.pixel, evt.coordinate, evt.map);
if (result.snapped) {
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) {
let feature;
if (evt instanceof VectorSourceEvent) {
feature = evt.feature;
} else if (evt instanceof CollectionEvent) {
feature = evt.element;
}
this.addFeature(/** @type {import("../Feature.js").default} */ (feature));
const feature = getFeatureFromEvent(evt);
this.addFeature(feature);
}
/**
* @param {import("../source/Vector.js").default|import("../Collection.js").CollectionEvent} evt Event.
* @param {import("../source/Vector.js").VectorSourceEvent|import("../Collection.js").CollectionEvent} evt Event.
* @private
*/
handleFeatureRemove_(evt) {
let feature;
if (evt instanceof VectorSourceEvent) {
feature = evt.feature;
} else if (evt instanceof CollectionEvent) {
feature = evt.element;
}
this.removeFeature(/** @type {import("../Feature.js").default} */ (feature));
const feature = getFeatureFromEvent(evt);
this.removeFeature(feature);
}
/**
@@ -283,6 +299,18 @@ class Snap extends PointerInteraction {
}
}
/**
* @inheritDoc
*/
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
@@ -319,7 +347,7 @@ class Snap extends PointerInteraction {
setMap(map) {
const currentMap = this.getMap();
const keys = this.featuresListenerKeys_;
const features = this.getFeatures_();
const features = /** @type {Array<import("../Feature.js").default>} */ (this.getFeatures_());
if (currentMap) {
keys.forEach(unlistenByKey);
@@ -587,37 +615,6 @@ class Snap extends PointerInteraction {
}
/**
* Handle all pointer events events.
* @param {import("../MapBrowserEvent.js").default} evt A move event.
* @return {boolean} Pass the event to other interactions.
* @this {Snap}
*/
export function handleEvent(evt) {
const result = this.snapTo(evt.pixel, evt.coordinate, evt.map);
if (result.snapped) {
evt.coordinate = result.vertex.slice(0, 2);
evt.pixel = result.vertexPixel;
}
return handlePointerEvent.call(this, evt);
}
/**
* @param {import("../MapBrowserPointerEvent.js").default} evt Event.
* @return {boolean} Stop drag sequence?
* @this {Snap}
*/
function handleUpEvent(evt) {
const featuresToUpdate = getValues(this.pendingFeatures_);
if (featuresToUpdate.length) {
featuresToUpdate.forEach(this.updateFeature_.bind(this));
this.pendingFeatures_ = {};
}
return false;
}
/**
* Sort segments by distance, helper function
* @param {SegmentData} a The first segment data.

View File

@@ -98,15 +98,10 @@ class Translate extends PointerInteraction {
* @param {Options=} opt_options Options.
*/
constructor(opt_options) {
super({
handleDownEvent: handleDownEvent,
handleDragEvent: handleDragEvent,
handleMoveEvent: handleMoveEvent,
handleUpEvent: handleUpEvent
});
const options = opt_options ? opt_options : {};
super(/** @type {import("./Pointer.js").Options} */ (options));
/**
* The last position we translated to.
* @type {import("../coordinate.js").Coordinate}
@@ -160,6 +155,86 @@ class Translate extends PointerInteraction {
}
/**
* @inheritDoc
*/
handleDownEvent(event) {
this.lastFeature_ = this.featuresAtPixel_(event.pixel, event.map);
if (!this.lastCoordinate_ && this.lastFeature_) {
this.lastCoordinate_ = event.coordinate;
this.handleMoveEvent(event);
const features = this.features_ || new Collection([this.lastFeature_]);
this.dispatchEvent(
new TranslateEvent(
TranslateEventType.TRANSLATESTART, features,
event.coordinate));
return true;
}
return false;
}
/**
* @inheritDoc
*/
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));
return true;
}
return false;
}
/**
* @inheritDoc
*/
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));
}
}
/**
* @inheritDoc
*/
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.
@@ -234,95 +309,4 @@ class Translate extends PointerInteraction {
}
}
/**
* @param {import("../MapBrowserPointerEvent.js").default} event Event.
* @return {boolean} Start drag sequence?
* @this {Translate}
*/
function handleDownEvent(event) {
this.lastFeature_ = this.featuresAtPixel_(event.pixel, event.map);
if (!this.lastCoordinate_ && this.lastFeature_) {
this.lastCoordinate_ = event.coordinate;
handleMoveEvent.call(this, event);
const features = this.features_ || new Collection([this.lastFeature_]);
this.dispatchEvent(
new TranslateEvent(
TranslateEventType.TRANSLATESTART, features,
event.coordinate));
return true;
}
return false;
}
/**
* @param {import("../MapBrowserPointerEvent.js").default} event Event.
* @return {boolean} Stop drag sequence?
* @this {Translate}
*/
function handleUpEvent(event) {
if (this.lastCoordinate_) {
this.lastCoordinate_ = null;
handleMoveEvent.call(this, event);
const features = this.features_ || new Collection([this.lastFeature_]);
this.dispatchEvent(
new TranslateEvent(
TranslateEventType.TRANSLATEEND, features,
event.coordinate));
return true;
}
return false;
}
/**
* @param {import("../MapBrowserPointerEvent.js").default} event Event.
* @this {Translate}
*/
function 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));
}
}
/**
* @param {import("../MapBrowserEvent.js").default} event Event.
* @this {Translate}
*/
function 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');
}
}
export default Translate;

View File

@@ -1,6 +1,7 @@
/**
* @module ol/layer/Base
*/
import {abstract} from '../util.js';
import BaseObject from '../Object.js';
import LayerProperty from '../layer/Property.js';
import {clamp} from '../math.js';
@@ -43,8 +44,8 @@ class BaseLayer extends BaseObject {
super();
/**
* @type {Object<string, *>}
*/
* @type {Object<string, *>}
*/
const properties = assign({}, options);
properties[LayerProperty.OPACITY] =
options.opacity !== undefined ? options.opacity : 1;
@@ -59,190 +60,199 @@ class BaseLayer extends BaseObject {
this.setProperties(properties);
/**
* @type {import("./Layer.js").State}
* @private
*/
this.state_ = /** @type {import("./Layer.js").State} */ ({
layer: /** @type {import("./Layer.js").default} */ (this),
managed: true
});
* @type {import("./Layer.js").State}
* @private
*/
this.state_ = null;
/**
* The layer type.
* @type {import("../LayerType.js").default}
* @protected;
*/
* The layer type.
* @type {import("../LayerType.js").default}
* @protected;
*/
this.type;
}
/**
* Get the layer type (used when creating a layer renderer).
* @return {import("../LayerType.js").default} The layer type.
*/
* Get the layer type (used when creating a layer renderer).
* @return {import("../LayerType.js").default} The layer type.
*/
getType() {
return this.type;
}
/**
* @return {import("./Layer.js").State} Layer state.
*/
* @return {import("./Layer.js").State} Layer state.
*/
getLayerState() {
this.state_.opacity = clamp(this.getOpacity(), 0, 1);
this.state_.sourceState = this.getSourceState();
this.state_.visible = this.getVisible();
this.state_.extent = this.getExtent();
this.state_.zIndex = this.getZIndex() || 0;
this.state_.maxResolution = this.getMaxResolution();
this.state_.minResolution = Math.max(this.getMinResolution(), 0);
/** @type {import("./Layer.js").State} */
const state = this.state_ || /** @type {?} */ ({
layer: this,
managed: true
});
state.opacity = clamp(this.getOpacity(), 0, 1);
state.sourceState = this.getSourceState();
state.visible = this.getVisible();
state.extent = this.getExtent();
state.zIndex = this.getZIndex() || 0;
state.maxResolution = this.getMaxResolution();
state.minResolution = Math.max(this.getMinResolution(), 0);
this.state_ = state;
return this.state_;
return state;
}
/**
* @abstract
* @param {Array<import("./Layer.js").default>=} opt_array Array of layers (to be
* modified in place).
* @return {Array<import("./Layer.js").default>} Array of layers.
*/
getLayersArray(opt_array) {}
* @abstract
* @param {Array<import("./Layer.js").default>=} opt_array Array of layers (to be
* modified in place).
* @return {Array<import("./Layer.js").default>} Array of layers.
*/
getLayersArray(opt_array) {
return abstract();
}
/**
* @abstract
* @param {Array<import("./Layer.js").State>=} opt_states Optional list of layer
* states (to be modified in place).
* @return {Array<import("./Layer.js").State>} List of layer states.
*/
getLayerStatesArray(opt_states) {}
* @abstract
* @param {Array<import("./Layer.js").State>=} opt_states Optional list of layer
* states (to be modified in place).
* @return {Array<import("./Layer.js").State>} List of layer states.
*/
getLayerStatesArray(opt_states) {
return abstract();
}
/**
* Return the {@link module:ol/extent~Extent extent} of the layer or `undefined` if it
* will be visible regardless of extent.
* @return {import("../extent.js").Extent|undefined} The layer extent.
* @observable
* @api
*/
* Return the {@link module:ol/extent~Extent extent} of the layer or `undefined` if it
* will be visible regardless of extent.
* @return {import("../extent.js").Extent|undefined} The layer extent.
* @observable
* @api
*/
getExtent() {
return (
/** @type {import("../extent.js").Extent|undefined} */ (this.get(LayerProperty.EXTENT))
/** @type {import("../extent.js").Extent|undefined} */ (this.get(LayerProperty.EXTENT))
);
}
/**
* Return the maximum resolution of the layer.
* @return {number} The maximum resolution of the layer.
* @observable
* @api
*/
* Return the maximum resolution of the layer.
* @return {number} The maximum resolution of the layer.
* @observable
* @api
*/
getMaxResolution() {
return /** @type {number} */ (this.get(LayerProperty.MAX_RESOLUTION));
}
/**
* Return the minimum resolution of the layer.
* @return {number} The minimum resolution of the layer.
* @observable
* @api
*/
* Return the minimum resolution of the layer.
* @return {number} The minimum resolution of the layer.
* @observable
* @api
*/
getMinResolution() {
return /** @type {number} */ (this.get(LayerProperty.MIN_RESOLUTION));
}
/**
* Return the opacity of the layer (between 0 and 1).
* @return {number} The opacity of the layer.
* @observable
* @api
*/
* Return the opacity of the layer (between 0 and 1).
* @return {number} The opacity of the layer.
* @observable
* @api
*/
getOpacity() {
return /** @type {number} */ (this.get(LayerProperty.OPACITY));
}
/**
* @abstract
* @return {import("../source/State.js").default} Source state.
*/
getSourceState() {}
* @abstract
* @return {import("../source/State.js").default} Source state.
*/
getSourceState() {
return abstract();
}
/**
* Return the visibility of the layer (`true` or `false`).
* @return {boolean} The visibility of the layer.
* @observable
* @api
*/
* Return the visibility of the layer (`true` or `false`).
* @return {boolean} The visibility of the layer.
* @observable
* @api
*/
getVisible() {
return /** @type {boolean} */ (this.get(LayerProperty.VISIBLE));
}
/**
* Return the Z-index of the layer, which is used to order layers before
* rendering. The default Z-index is 0.
* @return {number} The Z-index of the layer.
* @observable
* @api
*/
* Return the Z-index of the layer, which is used to order layers before
* rendering. The default Z-index is 0.
* @return {number} The Z-index of the layer.
* @observable
* @api
*/
getZIndex() {
return /** @type {number} */ (this.get(LayerProperty.Z_INDEX));
}
/**
* Set the extent at which the layer is visible. If `undefined`, the layer
* will be visible at all extents.
* @param {import("../extent.js").Extent|undefined} extent The extent of the layer.
* @observable
* @api
*/
* Set the extent at which the layer is visible. If `undefined`, the layer
* will be visible at all extents.
* @param {import("../extent.js").Extent|undefined} extent The extent of the layer.
* @observable
* @api
*/
setExtent(extent) {
this.set(LayerProperty.EXTENT, extent);
}
/**
* Set the maximum resolution at which the layer is visible.
* @param {number} maxResolution The maximum resolution of the layer.
* @observable
* @api
*/
* Set the maximum resolution at which the layer is visible.
* @param {number} maxResolution The maximum resolution of the layer.
* @observable
* @api
*/
setMaxResolution(maxResolution) {
this.set(LayerProperty.MAX_RESOLUTION, maxResolution);
}
/**
* Set the minimum resolution at which the layer is visible.
* @param {number} minResolution The minimum resolution of the layer.
* @observable
* @api
*/
* Set the minimum resolution at which the layer is visible.
* @param {number} minResolution The minimum resolution of the layer.
* @observable
* @api
*/
setMinResolution(minResolution) {
this.set(LayerProperty.MIN_RESOLUTION, minResolution);
}
/**
* Set the opacity of the layer, allowed values range from 0 to 1.
* @param {number} opacity The opacity of the layer.
* @observable
* @api
*/
* Set the opacity of the layer, allowed values range from 0 to 1.
* @param {number} opacity The opacity of the layer.
* @observable
* @api
*/
setOpacity(opacity) {
this.set(LayerProperty.OPACITY, opacity);
}
/**
* Set the visibility of the layer (`true` or `false`).
* @param {boolean} visible The visibility of the layer.
* @observable
* @api
*/
* Set the visibility of the layer (`true` or `false`).
* @param {boolean} visible The visibility of the layer.
* @observable
* @api
*/
setVisible(visible) {
this.set(LayerProperty.VISIBLE, visible);
}
/**
* Set Z-index of the layer, which is used to order layers before rendering.
* The default Z-index is 0.
* @param {number} zindex The z-index of the layer.
* @observable
* @api
*/
* Set Z-index of the layer, which is used to order layers before rendering.
* The default Z-index is 0.
* @param {number} zindex The z-index of the layer.
* @observable
* @api
*/
setZIndex(zindex) {
this.set(LayerProperty.Z_INDEX, zindex);
}

View File

@@ -84,9 +84,8 @@ class LayerGroup extends BaseLayer {
if (Array.isArray(layers)) {
layers = new Collection(layers.slice(), {unique: true});
} else {
assert(layers instanceof Collection,
assert(typeof /** @type {?} */ (layers).getArray === 'function',
43); // Expected `layers` to be an array or a `Collection`
layers = layers;
}
} else {
layers = new Collection(undefined, {unique: true});
@@ -104,7 +103,6 @@ class LayerGroup extends BaseLayer {
}
/**
* @param {import("../events/Event.js").default} event Event.
* @private
*/
handleLayersChanged_() {
@@ -125,7 +123,7 @@ class LayerGroup extends BaseLayer {
const layersArray = layers.getArray();
for (let i = 0, ii = layersArray.length; i < ii; i++) {
const layer = layersArray[i];
this.listenerKeys_[getUid(layer).toString()] = [
this.listenerKeys_[getUid(layer)] = [
listen(layer, ObjectEventType.PROPERTYCHANGE, this.handleLayerChange_, this),
listen(layer, EventType.CHANGE, this.handleLayerChange_, this)
];
@@ -140,8 +138,7 @@ class LayerGroup extends BaseLayer {
*/
handleLayersAdd_(collectionEvent) {
const layer = /** @type {import("./Base.js").default} */ (collectionEvent.element);
const key = getUid(layer).toString();
this.listenerKeys_[key] = [
this.listenerKeys_[getUid(layer)] = [
listen(layer, ObjectEventType.PROPERTYCHANGE, this.handleLayerChange_, this),
listen(layer, EventType.CHANGE, this.handleLayerChange_, this)
];
@@ -154,7 +151,7 @@ class LayerGroup extends BaseLayer {
*/
handleLayersRemove_(collectionEvent) {
const layer = /** @type {import("./Base.js").default} */ (collectionEvent.element);
const key = getUid(layer).toString();
const key = getUid(layer);
this.listenerKeys_[key].forEach(unlistenByKey);
delete this.listenerKeys_[key];
this.changed();

View File

@@ -35,7 +35,7 @@ import SourceState from '../source/State.js';
/**
* @typedef {Object} State
* @property {import("./Layer.js").default} layer
* @property {import("./Base.js").default} layer
* @property {number} opacity
* @property {SourceState} sourceState
* @property {boolean} visible
@@ -192,13 +192,14 @@ class Layer extends BaseLayer {
}
if (map) {
this.mapPrecomposeKey_ = listen(map, RenderEventType.PRECOMPOSE, function(evt) {
const renderEvent = /** @type {import("../render/Event.js").default} */ (evt);
const layerState = this.getLayerState();
layerState.managed = false;
if (this.getZIndex() === undefined) {
layerState.zIndex = Infinity;
}
evt.frameState.layerStatesArray.push(layerState);
evt.frameState.layerStates[getUid(this)] = layerState;
renderEvent.frameState.layerStatesArray.push(layerState);
renderEvent.frameState.layerStates[getUid(this)] = layerState;
}, this);
this.mapRenderKey_ = listen(this, EventType.CHANGE, map.render, map);
this.changed();

View File

@@ -41,7 +41,7 @@ import {createDefaultStyle, toFunction as toStyleFunction} from '../style/Style.
* @property {boolean} [declutter=false] Declutter images and text. Decluttering is applied to all
* image and text styles, and the priority is defined by the z-index of the style. Lower z-index
* means higher priority.
* @property {import("../style/Style.js").default|Array<import("../style/Style.js").default>|import("../style/Style.js").StyleFunction} [style] Layer style. See
* @property {import("../style/Style.js").StyleLike} [style] Layer style. See
* {@link module:ol/style} for default style which will be used if this is not defined.
* @property {boolean} [updateWhileAnimating=false] When set to `true` and `renderMode`
* is `vector`, feature batches will be recreated during animations. This means that no
@@ -54,22 +54,6 @@ import {createDefaultStyle, toFunction as toStyleFunction} from '../style/Style.
*/
/**
* @enum {string}
* Render mode for vector layers:
* * `'image'`: Vector layers are rendered as images. Great performance, but
* point symbols and texts are always rotated with the view and pixels are
* scaled during zoom animations.
* * `'vector'`: Vector layers are rendered as vectors. Most accurate rendering
* even during animations, but slower performance.
* @api
*/
export const RenderType = {
IMAGE: 'image',
VECTOR: 'vector'
};
/**
* @enum {string}
* @private
@@ -119,7 +103,7 @@ class VectorLayer extends Layer {
/**
* User provided style.
* @type {import("../style/Style.js").default|Array<import("../style/Style.js").default>|import("../style/Style.js").StyleFunction}
* @type {import("../style/Style.js").StyleLike}
* @private
*/
this.style_ = null;
@@ -196,7 +180,7 @@ class VectorLayer extends Layer {
/**
* Get the style for features. This returns whatever was passed to the `style`
* option at construction or to the `setStyle` method.
* @return {import("../style/Style.js").default|Array<import("../style/Style.js").default>|import("../style/Style.js").StyleFunction}
* @return {import("../style/Style.js").StyleLike}
* Layer style.
* @api
*/

View File

@@ -70,7 +70,7 @@ export const RenderType = {
* image and text styles, and the priority is defined by the z-index of the style. Lower z-index
* means higher priority. When set to `true`, a `renderMode` of `'image'` will be overridden with
* `'hybrid'`.
* @property {import("../style/Style.js").default|Array<import("../style/Style.js").default>|import("../style/Style.js").StyleFunction} [style] Layer style. See
* @property {import("../style/Style.js").StyleLike} [style] Layer style. See
* {@link module:ol/style} for default style which will be used if this is not defined.
* @property {boolean} [updateWhileAnimating=false] When set to `true`, feature batches will be
* recreated during animations. This means that no vectors will be shown clipped, but the setting
@@ -82,7 +82,7 @@ export const RenderType = {
* means no preloading.
* @property {import("../render.js").OrderFunction} [renderOrder] Render order. Function to be used when sorting
* features before rendering. By default features are drawn in the order that they are created.
* @property {import("../style/Style.js").default|Array<import("../style/Style.js").default>|import("../style/Style.js").StyleFunction} [style] Layer style. See
* @property {import("../style/Style.js").StyleLike} [style] Layer style. See
* {@link module:ol/style} for default style which will be used if this is not defined.
* @property {boolean} [useInterimTilesOnError=true] Use interim tiles on error.
*/
@@ -116,11 +116,11 @@ class VectorTileLayer extends VectorLayer {
}
options.renderMode = renderMode;
const baseOptions = assign({}, options);
const baseOptions = /** @type {Object} */ (assign({}, options));
delete baseOptions.preload;
delete baseOptions.useInterimTilesOnError;
super(baseOptions);
super(/** @type {import("./Vector.js").Options} */ (baseOptions));
this.setPreload(options.preload ? options.preload : 0);
this.setUseInterimTilesOnError(options.useInterimTilesOnError !== undefined ?

View File

@@ -35,7 +35,7 @@ export const cosh = (function() {
} else {
// … else, use the reference implementation of MDN:
cosh = function(x) {
const y = Math.exp(x);
const y = /** @type {Math} */ (Math).exp(x);
return (y + 1 / y) / 2;
};
}

View File

@@ -1,3 +1,7 @@
/**
* @module ol/pixel
*/
/**
* An array with two elements, representing a pixel. The first element is the
* x-coordinate, the second the y-coordinate of the pixel.

View File

@@ -226,7 +226,7 @@ class MouseSource extends EventSource {
* @param {import("./PointerEventHandler.js").default} dispatcher Event handler.
* @return {Object} The copied event.
*/
function prepareEvent(inEvent, dispatcher) {
export function prepareEvent(inEvent, dispatcher) {
const e = dispatcher.cloneEvent(inEvent, inEvent);
// forward mouse preventDefault

View File

@@ -171,6 +171,7 @@ class MsSource extends EventSource {
* @return {Object} The copied event.
*/
prepareEvent_(inEvent) {
/** @type {MSPointerEvent|Object} */
let e = inEvent;
if (typeof inEvent.pointerType === 'number') {
e = this.dispatcher.cloneEvent(inEvent, inEvent);

View File

@@ -32,7 +32,7 @@
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
import Event from '../events/Event.js';
import _Event from '../events/Event.js';
/**
@@ -42,7 +42,7 @@ import Event from '../events/Event.js';
let HAS_BUTTONS = false;
class PointerEvent extends Event {
class PointerEvent extends _Event {
/**
* A class for pointer events.
@@ -69,12 +69,12 @@ class PointerEvent extends Event {
/**
* @type {number}
*/
this.buttons = this.getButtons_(eventDict);
this.buttons = getButtons(eventDict);
/**
* @type {number}
*/
this.pressure = this.getPressure_(eventDict, this.buttons);
this.pressure = getPressure(eventDict, this.buttons);
// MouseEvent related properties
@@ -200,64 +200,65 @@ class PointerEvent extends Event {
}
}
/**
* @private
* @param {Object<string, ?>} eventDict The event dictionary.
* @return {number} Button indicator.
*/
getButtons_(eventDict) {
// According to the w3c spec,
// http://www.w3.org/TR/DOM-Level-3-Events/#events-MouseEvent-button
// MouseEvent.button == 0 can mean either no mouse button depressed, or the
// left mouse button depressed.
//
// As of now, the only way to distinguish between the two states of
// MouseEvent.button is by using the deprecated MouseEvent.which property, as
// this maps mouse buttons to positive integers > 0, and uses 0 to mean that
// no mouse button is held.
//
// MouseEvent.which is derived from MouseEvent.button at MouseEvent creation,
// but initMouseEvent does not expose an argument with which to set
// MouseEvent.which. Calling initMouseEvent with a buttonArg of 0 will set
// MouseEvent.button == 0 and MouseEvent.which == 1, breaking the expectations
// of app developers.
//
// The only way to propagate the correct state of MouseEvent.which and
// MouseEvent.button to a new MouseEvent.button == 0 and MouseEvent.which == 0
// is to call initMouseEvent with a buttonArg value of -1.
//
// This is fixed with DOM Level 4's use of buttons
let buttons;
if (eventDict.buttons || HAS_BUTTONS) {
buttons = eventDict.buttons;
} else {
switch (eventDict.which) {
case 1: buttons = 1; break;
case 2: buttons = 4; break;
case 3: buttons = 2; break;
default: buttons = 0;
}
}
return buttons;
}
}
/**
* @private
* @param {Object<string, ?>} eventDict The event dictionary.
* @param {number} buttons Button indicator.
* @return {number} The pressure.
*/
getPressure_(eventDict, buttons) {
// Spec requires that pointers without pressure specified use 0.5 for down
// state and 0 for up state.
let pressure = 0;
if (eventDict.pressure) {
pressure = eventDict.pressure;
} else {
pressure = buttons ? 0.5 : 0;
/**
* @param {Object<string, ?>} eventDict The event dictionary.
* @return {number} Button indicator.
*/
function getButtons(eventDict) {
// According to the w3c spec,
// http://www.w3.org/TR/DOM-Level-3-Events/#events-MouseEvent-button
// MouseEvent.button == 0 can mean either no mouse button depressed, or the
// left mouse button depressed.
//
// As of now, the only way to distinguish between the two states of
// MouseEvent.button is by using the deprecated MouseEvent.which property, as
// this maps mouse buttons to positive integers > 0, and uses 0 to mean that
// no mouse button is held.
//
// MouseEvent.which is derived from MouseEvent.button at MouseEvent creation,
// but initMouseEvent does not expose an argument with which to set
// MouseEvent.which. Calling initMouseEvent with a buttonArg of 0 will set
// MouseEvent.button == 0 and MouseEvent.which == 1, breaking the expectations
// of app developers.
//
// The only way to propagate the correct state of MouseEvent.which and
// MouseEvent.button to a new MouseEvent.button == 0 and MouseEvent.which == 0
// is to call initMouseEvent with a buttonArg value of -1.
//
// This is fixed with DOM Level 4's use of buttons
let buttons;
if (eventDict.buttons || HAS_BUTTONS) {
buttons = eventDict.buttons;
} else {
switch (eventDict.which) {
case 1: buttons = 1; break;
case 2: buttons = 4; break;
case 3: buttons = 2; break;
default: buttons = 0;
}
return pressure;
}
return buttons;
}
/**
* @param {Object<string, ?>} eventDict The event dictionary.
* @param {number} buttons Button indicator.
* @return {number} The pressure.
*/
function getPressure(eventDict, buttons) {
// Spec requires that pointers without pressure specified use 0.5 for down
// state and 0 for up state.
let pressure = 0;
if (eventDict.pressure) {
pressure = eventDict.pressure;
} else {
pressure = buttons ? 0.5 : 0;
}
return pressure;
}

View File

@@ -36,7 +36,7 @@ import {listen, unlisten} from '../events.js';
import EventTarget from '../events/Target.js';
import {POINTER, MSPOINTER, TOUCH} from '../has.js';
import PointerEventType from '../pointer/EventType.js';
import MouseSource from '../pointer/MouseSource.js';
import MouseSource, {prepareEvent as prepareMouseEvent} from '../pointer/MouseSource.js';
import MsSource from '../pointer/MsSource.js';
import NativeSource from '../pointer/NativeSource.js';
import PointerEvent from '../pointer/PointerEvent.js';
@@ -366,7 +366,7 @@ class PointerEventHandler extends EventTarget {
* @param {string} inType A string representing the type of event to create.
* @param {Object} data Pointer event data.
* @param {Event} event The event.
* @return {import("./PointerEvent.js").default} A PointerEvent of type `inType`.
* @return {PointerEvent} A PointerEvent of type `inType`.
*/
makeEvent(inType, data, event) {
return new PointerEvent(inType, event, data);
@@ -398,11 +398,11 @@ class PointerEventHandler extends EventTarget {
* This proxy method is required for the legacy IE support.
* @param {string} eventType The pointer event type.
* @param {Event} event The event.
* @return {import("./PointerEvent.js").default} The wrapped event.
* @return {PointerEvent} The wrapped event.
*/
wrapMouseEvent(eventType, event) {
const pointerEvent = this.makeEvent(
eventType, MouseSource.prepareEvent(event, this), event);
eventType, prepareMouseEvent(event, this), event);
return pointerEvent;
}

View File

@@ -137,9 +137,9 @@ class TouchSource extends EventSource {
/**
* @private
* @type {number|undefined}
* @type {?}
*/
this.resetId_ = undefined;
this.resetId_;
/**
* Mouse event timeout: This should be long enough to
@@ -228,9 +228,9 @@ class TouchSource extends EventSource {
e.detail = this.clickCount_;
e.button = 0;
e.buttons = 1;
e.width = inTouch.webkitRadiusX || inTouch.radiusX || 0;
e.height = inTouch.webkitRadiusY || inTouch.radiusY || 0;
e.pressure = inTouch.webkitForce || inTouch.force || 0.5;
e.width = inTouch.radiusX || 0;
e.height = inTouch.radiusY || 0;
e.pressure = inTouch.force || 0.5;
e.isPrimary = this.isPrimaryTouch_(inTouch);
e.pointerType = POINTER_TYPE;
@@ -300,12 +300,12 @@ class TouchSource extends EventSource {
if (count >= touchList.length) {
const d = [];
for (let i = 0; i < count; ++i) {
const key = keys[i];
const key = Number(keys[i]);
const value = this.pointerMap[key];
// Never remove pointerId == 1, which is mouse.
// Touch identifiers are 2 smaller than their pointerId, which is the
// index in pointermap.
if (key != /** @type {string} */ (POINTER_ID) && !this.findTouch_(touchList, key - 2)) {
if (key != POINTER_ID && !this.findTouch_(touchList, key - 2)) {
d.push(value.out);
}
}
@@ -348,7 +348,7 @@ class TouchSource extends EventSource {
this.dispatcher.move(event, browserEvent);
if (outEvent && outTarget !== event.target) {
outEvent.relatedTarget = event.target;
event.relatedTarget = outTarget;
/** @type {Object} */ (event).relatedTarget = outTarget;
// recover from retargeting by shadow
outEvent.target = outTarget;
if (event.target) {
@@ -356,8 +356,8 @@ class TouchSource extends EventSource {
this.dispatcher.enterOver(event, browserEvent);
} else {
// clean up case when finger leaves the screen
event.target = outTarget;
event.relatedTarget = null;
/** @type {Object} */ (event).target = outTarget;
/** @type {Object} */ (event).relatedTarget = null;
this.cancelOut_(browserEvent, event);
}
}

View File

@@ -67,7 +67,7 @@ import {add as addTransformFunc, clear as clearTransformFuncs, get as getTransfo
/**
* A projection as {@link module:ol/proj/Projection}, SRS identifier
* string or undefined.
* @typedef {import("./proj/Projection.js").default|string|undefined} ProjectionLike
* @typedef {Projection|string|undefined} ProjectionLike
* @api
*/
@@ -129,7 +129,7 @@ export function identityTransform(input, opt_output, opt_dimension) {
* Add a Projection object to the list of supported projections that can be
* looked up by their code.
*
* @param {import("./proj/Projection.js").default} projection Projection instance.
* @param {Projection} projection Projection instance.
* @api
*/
export function addProjection(projection) {
@@ -139,7 +139,7 @@ export function addProjection(projection) {
/**
* @param {Array<import("./proj/Projection.js").default>} projections Projections.
* @param {Array<Projection>} projections Projections.
*/
export function addProjections(projections) {
projections.forEach(addProjection);
@@ -152,13 +152,13 @@ export function addProjections(projections) {
* @param {ProjectionLike} projectionLike Either a code string which is
* a combination of authority and identifier such as "EPSG:4326", or an
* existing projection object, or undefined.
* @return {import("./proj/Projection.js").default} Projection object, or null if not in list.
* @return {Projection} Projection object, or null if not in list.
* @api
*/
export function get(projectionLike) {
return typeof projectionLike === 'string' ?
projections.get(/** @type {string} */ (projectionLike)) :
(/** @type {import("./proj/Projection.js").default} */ (projectionLike) || null);
(/** @type {Projection} */ (projectionLike) || null);
}
@@ -177,7 +177,7 @@ export function get(projectionLike) {
* @param {ProjectionLike} projection The projection.
* @param {number} resolution Nominal resolution in projection units.
* @param {import("./coordinate.js").Coordinate} point Point to find adjusted resolution at.
* @param {import("./proj/Units.js").default=} opt_units Units to get the point resolution in.
* @param {Units=} opt_units Units to get the point resolution in.
* Default is the projection's units.
* @return {number} Point resolution.
* @api
@@ -223,7 +223,7 @@ export function getPointResolution(projection, resolution, point, opt_units) {
* Registers transformation functions that don't alter coordinates. Those allow
* to transform between projections with equal meaning.
*
* @param {Array<import("./proj/Projection.js").default>} projections Projections.
* @param {Array<Projection>} projections Projections.
* @api
*/
export function addEquivalentProjections(projections) {
@@ -242,9 +242,9 @@ export function addEquivalentProjections(projections) {
* Registers transformation functions to convert coordinates in any projection
* in projection1 to any projection in projection2.
*
* @param {Array<import("./proj/Projection.js").default>} projections1 Projections with equal
* @param {Array<Projection>} projections1 Projections with equal
* meaning.
* @param {Array<import("./proj/Projection.js").default>} projections2 Projections with equal
* @param {Array<Projection>} projections2 Projections with equal
* meaning.
* @param {TransformFunction} forwardTransform Transformation from any
* projection in projection1 to any projection in projection2.
@@ -271,9 +271,9 @@ export function clearAllProjections() {
/**
* @param {import("./proj/Projection.js").default|string|undefined} projection Projection.
* @param {Projection|string|undefined} projection Projection.
* @param {string} defaultCode Default code.
* @return {import("./proj/Projection.js").default} Projection.
* @return {Projection} Projection.
*/
export function createProjection(projection, defaultCode) {
if (!projection) {
@@ -282,7 +282,7 @@ export function createProjection(projection, defaultCode) {
return get(projection);
} else {
return (
/** @type {import("./proj/Projection.js").default} */ (projection)
/** @type {Projection} */ (projection)
);
}
}
@@ -387,8 +387,8 @@ export function toLonLat(coordinate, opt_projection) {
* projection does represent the same geographic point as the same coordinate in
* the other projection.
*
* @param {import("./proj/Projection.js").default} projection1 Projection 1.
* @param {import("./proj/Projection.js").default} projection2 Projection 2.
* @param {Projection} projection1 Projection 1.
* @param {Projection} projection2 Projection 2.
* @return {boolean} Equivalent.
* @api
*/
@@ -410,8 +410,8 @@ export function equivalent(projection1, projection2) {
* Searches in the list of transform functions for the function for converting
* coordinates from the source projection to the destination projection.
*
* @param {import("./proj/Projection.js").default} sourceProjection Source Projection object.
* @param {import("./proj/Projection.js").default} destinationProjection Destination Projection
* @param {Projection} sourceProjection Source Projection object.
* @param {Projection} destinationProjection Destination Projection
* object.
* @return {TransformFunction} Transform function.
*/
@@ -483,8 +483,8 @@ export function transformExtent(extent, source, destination) {
* Transforms the given point to the destination projection.
*
* @param {import("./coordinate.js").Coordinate} point Point.
* @param {import("./proj/Projection.js").default} sourceProjection Source projection.
* @param {import("./proj/Projection.js").default} destinationProjection Destination projection.
* @param {Projection} sourceProjection Source projection.
* @param {Projection} destinationProjection Destination projection.
* @return {import("./coordinate.js").Coordinate} Point.
*/
export function transformWithProjections(point, sourceProjection, destinationProjection) {

View File

@@ -9,7 +9,7 @@ import CanvasImmediateRenderer from './render/canvas/Immediate.js';
/**
* @typedef {Object} State
* @property {CanvasRenderingContext2D} context Canvas context that the layer is being rendered to.
* @property {import("./Feature.js").default|import("./render/Feature.js").default} feature
* @property {import("./Feature.js").FeatureLike} feature
* @property {import("./geom/SimpleGeometry.js").default} geometry
* @property {number} pixelRatio Pixel ratio used by the layer renderer.
* @property {number} resolution Resolution that the render batch was created and optimized for.
@@ -23,8 +23,7 @@ import CanvasImmediateRenderer from './render/canvas/Immediate.js';
* It takes two instances of {@link module:ol/Feature} or
* {@link module:ol/render/Feature} and returns a `{number}`.
*
* @typedef {function((import("./Feature.js").default|import("./render/Feature.js").default),
* (import("./Feature.js").default|import("./render/Feature.js").default)):number} OrderFunction
* @typedef {function(import("./Feature.js").FeatureLike, import("./Feature.js").FeatureLike):number} OrderFunction
*/
@@ -60,7 +59,7 @@ import CanvasImmediateRenderer from './render/canvas/Immediate.js';
*
* @param {CanvasRenderingContext2D} context Canvas context.
* @param {ToContextOptions=} opt_options Options.
* @return {import("./render/canvas/Immediate.js").default} Canvas Immediate.
* @return {CanvasImmediateRenderer} Canvas Immediate.
* @api
*/
export function toContext(context, opt_options) {

View File

@@ -1,7 +1,6 @@
/**
* @module ol/render/Feature
*/
import {VOID} from '../functions.js';
import {extend} from '../array.js';
import {createOrUpdateFromCoordinate, createOrUpdateFromFlatCoordinates, getCenter, getHeight} from '../extent.js';
import GeometryType from '../geom/GeometryType.js';
@@ -24,7 +23,7 @@ const tmpTransform = createTransform();
* structure, optimized for vector tile rendering and styling. Geometry access
* through the API is limited to getting the type and extent of the geometry.
*
* @param {import("../geom/GeometryType.js").default} type Geometry type.
* @param {GeometryType} type Geometry type.
* @param {Array<number>} flatCoordinates Flat coordinates. These always need
* to be right-handed for polygons.
* @param {Array<number>|Array<Array<number>>} ends Ends or Endss.
@@ -47,7 +46,7 @@ class RenderFeature {
/**
* @private
* @type {import("../geom/GeometryType.js").default}
* @type {GeometryType}
*/
this.type_ = type;
@@ -116,7 +115,7 @@ class RenderFeature {
if (!this.flatInteriorPoints_) {
const flatCenter = getCenter(this.getExtent());
this.flatInteriorPoints_ = getInteriorPointOfArray(
this.flatCoordinates_, 0, this.ends_, 2, flatCenter, 0);
this.flatCoordinates_, 0, /** @type {Array<number>} */ (this.ends_), 2, flatCenter, 0);
}
return this.flatInteriorPoints_;
}
@@ -127,9 +126,9 @@ class RenderFeature {
getFlatInteriorPoints() {
if (!this.flatInteriorPoints_) {
const flatCenters = linearRingssCenter(
this.flatCoordinates_, 0, this.ends_, 2);
this.flatCoordinates_, 0, /** @type {Array<Array<number>>} */ (this.ends_), 2);
this.flatInteriorPoints_ = getInteriorPointsOfMultiArray(
this.flatCoordinates_, 0, this.ends_, 2, flatCenters);
this.flatCoordinates_, 0, /** @type {Array<Array<number>>} */ (this.ends_), 2, flatCenters);
}
return this.flatInteriorPoints_;
}
@@ -153,7 +152,7 @@ class RenderFeature {
this.flatMidpoints_ = [];
const flatCoordinates = this.flatCoordinates_;
let offset = 0;
const ends = this.ends_;
const ends = /** @type {Array<number>} */ (this.ends_);
for (let i = 0, ii = ends.length; i < ii; ++i) {
const end = ends[i];
const midpoint = interpolatePoint(
@@ -192,6 +191,14 @@ class RenderFeature {
return this;
}
/**
* @param {number} squaredTolerance Squared tolerance.
* @return {RenderFeature} Simplified geometry.
*/
getSimplifiedGeometry(squaredTolerance) {
return this;
}
/**
* Get the feature properties.
* @return {Object<string, *>} Feature properties.
@@ -208,9 +215,16 @@ class RenderFeature {
return 2;
}
/**
* @return {undefined}
*/
getStyleFunction() {
return undefined;
}
/**
* Get the type of this feature's geometry.
* @return {import("../geom/GeometryType.js").default} Geometry type.
* @return {GeometryType} Geometry type.
* @api
*/
getType() {
@@ -255,18 +269,4 @@ RenderFeature.prototype.getFlatCoordinates =
RenderFeature.prototype.getOrientedFlatCoordinates;
/**
* Get the feature for working with its geometry.
* @return {RenderFeature} Feature.
*/
RenderFeature.prototype.getSimplifiedGeometry =
RenderFeature.prototype.getGeometry;
/**
* @return {undefined}
*/
RenderFeature.prototype.getStyleFunction = VOID;
export default RenderFeature;

View File

@@ -1,6 +1,8 @@
/**
* @module ol/render/ReplayGroup
*/
import {abstract} from '../util.js';
/**
* Base class for replay groups.
*/
@@ -11,13 +13,26 @@ class ReplayGroup {
* @param {import("./ReplayType.js").default} replayType Replay type.
* @return {import("./VectorContext.js").default} Replay.
*/
getReplay(zIndex, replayType) {}
getReplay(zIndex, replayType) {
return abstract();
}
/**
* @abstract
* @return {boolean} Is empty.
*/
isEmpty() {}
isEmpty() {
return abstract();
}
/**
* @abstract
* @param {boolean} group Group with previous replay
* @return {Array<*>} The resulting instruction group
*/
addDeclutter(group) {
return abstract();
}
}
export default ReplayGroup;

Some files were not shown because too many files have changed in this diff Show More