From 7b4a73f3b93c56f0f9fe69ba6aad1b462ff40274 Mon Sep 17 00:00:00 2001 From: Tim Schaub Date: Mon, 16 Jul 2018 16:18:16 -0600 Subject: [PATCH 001/107] Automated class transform npx lebab --replace src --transform class --- src/ol/Collection.js | 427 ++-- src/ol/Disposable.js | 22 +- src/ol/Feature.js | 408 ++-- src/ol/Geolocation.js | 555 +++-- src/ol/Graticule.js | 1192 +++++---- src/ol/Image.js | 211 +- src/ol/ImageBase.js | 148 +- src/ol/ImageCanvas.js | 126 +- src/ol/ImageTile.js | 245 +- src/ol/Kinetic.js | 192 +- src/ol/Map.js | 42 +- src/ol/MapBrowserEvent.js | 106 +- src/ol/MapBrowserEventHandler.js | 571 +++-- src/ol/Object.js | 221 +- src/ol/Observable.js | 177 +- src/ol/Overlay.js | 942 ++++--- src/ol/PluggableMap.js | 2295 +++++++++--------- src/ol/control/Attribution.js | 484 ++-- src/ol/control/Control.js | 184 +- src/ol/control/FullScreen.js | 251 +- src/ol/control/MousePosition.js | 337 ++- src/ol/control/OverviewMap.js | 950 ++++---- src/ol/control/Rotate.js | 206 +- src/ol/control/ScaleLine.js | 403 ++- src/ol/control/Zoom.js | 162 +- src/ol/control/ZoomSlider.js | 532 ++-- src/ol/control/ZoomToExtent.js | 92 +- src/ol/events/EventTarget.js | 234 +- src/ol/format/EsriJSON.js | 276 ++- src/ol/format/Feature.js | 251 +- src/ol/format/GML2.js | 1052 ++++---- src/ol/format/GML3.js | 1722 +++++++------ src/ol/format/GMLBase.js | 740 +++--- src/ol/format/GPX.js | 188 +- src/ol/format/GeoJSON.js | 343 ++- src/ol/format/IGC.js | 244 +- src/ol/format/JSONFeature.js | 257 +- src/ol/format/KML.js | 865 ++++--- src/ol/format/MVT.js | 504 ++-- src/ol/format/OSMXML.js | 128 +- src/ol/format/OWS.js | 52 +- src/ol/format/Polyline.js | 162 +- src/ol/format/TextFeature.js | 259 +- src/ol/format/TopoJSON.js | 197 +- src/ol/format/WFS.js | 639 +++-- src/ol/format/WKT.js | 1067 ++++---- src/ol/format/WMSCapabilities.js | 60 +- src/ol/format/WMSGetFeatureInfo.js | 239 +- src/ol/format/WMTSCapabilities.js | 68 +- src/ol/format/XML.js | 63 +- src/ol/format/XMLFeature.js | 455 ++-- src/ol/format/filter/Filter.js | 30 +- src/ol/geom/Circle.js | 392 ++- src/ol/geom/Geometry.js | 435 ++-- src/ol/geom/GeometryCollection.js | 527 ++-- src/ol/geom/LineString.js | 416 ++-- src/ol/geom/LinearRing.js | 218 +- src/ol/geom/MultiLineString.js | 473 ++-- src/ol/geom/MultiPoint.js | 291 ++- src/ol/geom/MultiPolygon.js | 664 +++-- src/ol/geom/Point.js | 168 +- src/ol/geom/Polygon.js | 572 +++-- src/ol/geom/SimpleGeometry.js | 466 ++-- src/ol/interaction/DragAndDrop.js | 284 ++- src/ol/interaction/DragBox.js | 89 +- src/ol/interaction/DragZoom.js | 109 +- src/ol/interaction/Draw.js | 1223 +++++----- src/ol/interaction/Extent.js | 422 ++-- src/ol/interaction/Interaction.js | 104 +- src/ol/interaction/Modify.js | 1580 ++++++------ src/ol/interaction/MouseWheelZoom.js | 251 +- src/ol/interaction/Pointer.js | 171 +- src/ol/interaction/Select.js | 385 ++- src/ol/interaction/Snap.js | 949 ++++---- src/ol/interaction/Translate.js | 246 +- src/ol/layer/Base.js | 423 ++-- src/ol/layer/Group.js | 350 ++- src/ol/layer/Heatmap.js | 389 ++- src/ol/layer/Layer.js | 280 ++- src/ol/layer/Tile.js | 116 +- src/ol/layer/Vector.js | 327 ++- src/ol/layer/VectorTile.js | 138 +- src/ol/pointer/EventSource.js | 57 +- src/ol/pointer/MouseSource.js | 296 ++- src/ol/pointer/MsSource.js | 264 +- src/ol/pointer/NativeSource.js | 178 +- src/ol/pointer/PointerEvent.js | 352 +-- src/ol/pointer/PointerEventHandler.js | 646 +++-- src/ol/pointer/TouchSource.js | 708 +++--- src/ol/proj/Projection.js | 400 ++- src/ol/render/Box.js | 216 +- src/ol/render/Feature.js | 403 ++- src/ol/render/ReplayGroup.js | 29 +- src/ol/render/VectorContext.js | 192 +- src/ol/render/canvas/ImageReplay.js | 388 ++- src/ol/render/canvas/Immediate.js | 1691 +++++++------ src/ol/render/canvas/LineStringReplay.js | 204 +- src/ol/render/canvas/PolygonReplay.js | 387 ++- src/ol/render/canvas/Replay.js | 1990 +++++++-------- src/ol/render/canvas/ReplayGroup.js | 645 ++--- src/ol/render/canvas/TextReplay.js | 960 ++++---- src/ol/render/webgl/CircleReplay.js | 672 +++-- src/ol/render/webgl/ImageReplay.js | 276 ++- src/ol/render/webgl/Immediate.js | 669 +++-- src/ol/render/webgl/LineStringReplay.js | 1120 +++++---- src/ol/render/webgl/PolygonReplay.js | 1902 +++++++-------- src/ol/render/webgl/Replay.js | 639 ++--- src/ol/render/webgl/ReplayGroup.js | 545 +++-- src/ol/render/webgl/TextReplay.js | 808 +++--- src/ol/render/webgl/TextureReplay.js | 849 ++++--- src/ol/renderer/Layer.js | 391 +-- src/ol/renderer/Map.js | 578 +++-- src/ol/renderer/canvas/ImageLayer.js | 315 ++- src/ol/renderer/canvas/IntermediateCanvas.js | 240 +- src/ol/renderer/canvas/Layer.js | 310 ++- src/ol/renderer/canvas/Map.js | 353 ++- src/ol/renderer/canvas/TileLayer.js | 625 +++-- src/ol/renderer/canvas/VectorLayer.js | 752 +++--- src/ol/renderer/canvas/VectorTileLayer.js | 809 +++--- src/ol/renderer/webgl/ImageLayer.js | 532 ++-- src/ol/renderer/webgl/Layer.js | 435 ++-- src/ol/renderer/webgl/Map.js | 1030 ++++---- src/ol/renderer/webgl/TileLayer.js | 659 +++-- src/ol/renderer/webgl/VectorLayer.js | 518 ++-- src/ol/reproj/Image.js | 294 +-- src/ol/reproj/Tile.js | 500 ++-- src/ol/reproj/Triangulation.js | 554 +++-- src/ol/source/BingMaps.js | 309 +-- src/ol/source/CartoDB.js | 257 +- src/ol/source/Cluster.js | 297 ++- src/ol/source/Image.js | 251 +- src/ol/source/ImageArcGISRest.js | 416 ++-- src/ol/source/ImageCanvas.js | 123 +- src/ol/source/ImageMapGuide.js | 348 ++- src/ol/source/ImageStatic.js | 122 +- src/ol/source/ImageWMS.js | 640 +++-- src/ol/source/Raster.js | 503 ++-- src/ol/source/Source.js | 242 +- src/ol/source/Tile.js | 462 ++-- src/ol/source/TileArcGISRest.js | 285 ++- src/ol/source/TileDebug.js | 158 +- src/ol/source/TileImage.js | 616 +++-- src/ol/source/TileJSON.js | 243 +- src/ol/source/TileWMS.js | 595 +++-- src/ol/source/UrlTile.js | 319 ++- 145 files changed, 32887 insertions(+), 33714 deletions(-) diff --git a/src/ol/Collection.js b/src/ol/Collection.js index 9dd5a7ff78..b50ed0d40c 100644 --- a/src/ol/Collection.js +++ b/src/ol/Collection.js @@ -65,236 +65,225 @@ inherits(CollectionEvent, Event); * @template T * @api */ -const Collection = function(opt_array, opt_options) { +class Collection { + constructor(opt_array, opt_options) { - BaseObject.call(this); + BaseObject.call(this); - const options = opt_options || {}; + const options = opt_options || {}; + + /** + * @private + * @type {boolean} + */ + this.unique_ = !!options.unique; + + /** + * @private + * @type {!Array.} + */ + this.array_ = opt_array ? opt_array : []; + + if (this.unique_) { + for (let i = 0, ii = this.array_.length; i < ii; ++i) { + this.assertUnique_(this.array_[i], i); + } + } + + this.updateLength_(); + + } /** - * @private - * @type {boolean} + * Remove all elements from the collection. + * @api */ - this.unique_ = !!options.unique; - - /** - * @private - * @type {!Array.} - */ - this.array_ = opt_array ? opt_array : []; - - if (this.unique_) { - for (let i = 0, ii = this.array_.length; i < ii; ++i) { - this.assertUnique_(this.array_[i], i); + clear() { + while (this.getLength() > 0) { + this.pop(); } } - this.updateLength_(); + /** + * Add elements to the collection. This pushes each item in the provided array + * to the end of the collection. + * @param {!Array.} arr Array. + * @return {module:ol/Collection.} This collection. + * @api + */ + extend(arr) { + for (let i = 0, ii = arr.length; i < ii; ++i) { + this.push(arr[i]); + } + return this; + } -}; + /** + * Iterate over each element, calling the provided callback. + * @param {function(T, number, Array.): *} f The function to call + * for every element. This function takes 3 arguments (the element, the + * index and the array). The return value is ignored. + * @api + */ + forEach(f) { + const array = this.array_; + for (let i = 0, ii = array.length; i < ii; ++i) { + f(array[i], i, array); + } + } + + /** + * Get a reference to the underlying Array object. Warning: if the array + * is mutated, no events will be dispatched by the collection, and the + * collection's "length" property won't be in sync with the actual length + * of the array. + * @return {!Array.} Array. + * @api + */ + getArray() { + return this.array_; + } + + /** + * Get the element at the provided index. + * @param {number} index Index. + * @return {T} Element. + * @api + */ + item(index) { + return this.array_[index]; + } + + /** + * Get the length of this collection. + * @return {number} The length of the array. + * @observable + * @api + */ + getLength() { + return /** @type {number} */ (this.get(Property.LENGTH)); + } + + /** + * Insert an element at the provided index. + * @param {number} index Index. + * @param {T} elem Element. + * @api + */ + insertAt(index, elem) { + if (this.unique_) { + this.assertUnique_(elem); + } + this.array_.splice(index, 0, elem); + this.updateLength_(); + this.dispatchEvent( + new CollectionEvent(CollectionEventType.ADD, elem)); + } + + /** + * Remove the last element of the collection and return it. + * Return `undefined` if the collection is empty. + * @return {T|undefined} Element. + * @api + */ + pop() { + return this.removeAt(this.getLength() - 1); + } + + /** + * Insert the provided element at the end of the collection. + * @param {T} elem Element. + * @return {number} New length of the collection. + * @api + */ + push(elem) { + if (this.unique_) { + this.assertUnique_(elem); + } + const n = this.getLength(); + this.insertAt(n, elem); + return this.getLength(); + } + + /** + * Remove the first occurrence of an element from the collection. + * @param {T} elem Element. + * @return {T|undefined} The removed element or undefined if none found. + * @api + */ + remove(elem) { + const arr = this.array_; + for (let i = 0, ii = arr.length; i < ii; ++i) { + if (arr[i] === elem) { + return this.removeAt(i); + } + } + return undefined; + } + + /** + * Remove the element at the provided index and return it. + * Return `undefined` if the collection does not contain this index. + * @param {number} index Index. + * @return {T|undefined} Value. + * @api + */ + removeAt(index) { + const prev = this.array_[index]; + this.array_.splice(index, 1); + this.updateLength_(); + this.dispatchEvent(new CollectionEvent(CollectionEventType.REMOVE, prev)); + return prev; + } + + /** + * Set the element at the provided index. + * @param {number} index Index. + * @param {T} elem Element. + * @api + */ + setAt(index, elem) { + const n = this.getLength(); + if (index < n) { + if (this.unique_) { + this.assertUnique_(elem, index); + } + const prev = this.array_[index]; + this.array_[index] = elem; + this.dispatchEvent( + new CollectionEvent(CollectionEventType.REMOVE, prev)); + this.dispatchEvent( + new CollectionEvent(CollectionEventType.ADD, elem)); + } else { + for (let j = n; j < index; ++j) { + this.insertAt(j, undefined); + } + this.insertAt(index, elem); + } + } + + /** + * @private + */ + updateLength_() { + this.set(Property.LENGTH, this.array_.length); + } + + /** + * @private + * @param {T} elem Element. + * @param {number=} opt_except Optional index to ignore. + */ + assertUnique_(elem, opt_except) { + for (let i = 0, ii = this.array_.length; i < ii; ++i) { + if (this.array_[i] === elem && i !== opt_except) { + throw new AssertionError(58); + } + } + } +} inherits(Collection, BaseObject); -/** - * Remove all elements from the collection. - * @api - */ -Collection.prototype.clear = function() { - while (this.getLength() > 0) { - this.pop(); - } -}; - - -/** - * Add elements to the collection. This pushes each item in the provided array - * to the end of the collection. - * @param {!Array.} arr Array. - * @return {module:ol/Collection.} This collection. - * @api - */ -Collection.prototype.extend = function(arr) { - for (let i = 0, ii = arr.length; i < ii; ++i) { - this.push(arr[i]); - } - return this; -}; - - -/** - * Iterate over each element, calling the provided callback. - * @param {function(T, number, Array.): *} f The function to call - * for every element. This function takes 3 arguments (the element, the - * index and the array). The return value is ignored. - * @api - */ -Collection.prototype.forEach = function(f) { - const array = this.array_; - for (let i = 0, ii = array.length; i < ii; ++i) { - f(array[i], i, array); - } -}; - - -/** - * Get a reference to the underlying Array object. Warning: if the array - * is mutated, no events will be dispatched by the collection, and the - * collection's "length" property won't be in sync with the actual length - * of the array. - * @return {!Array.} Array. - * @api - */ -Collection.prototype.getArray = function() { - return this.array_; -}; - - -/** - * Get the element at the provided index. - * @param {number} index Index. - * @return {T} Element. - * @api - */ -Collection.prototype.item = function(index) { - return this.array_[index]; -}; - - -/** - * Get the length of this collection. - * @return {number} The length of the array. - * @observable - * @api - */ -Collection.prototype.getLength = function() { - return /** @type {number} */ (this.get(Property.LENGTH)); -}; - - -/** - * Insert an element at the provided index. - * @param {number} index Index. - * @param {T} elem Element. - * @api - */ -Collection.prototype.insertAt = function(index, elem) { - if (this.unique_) { - this.assertUnique_(elem); - } - this.array_.splice(index, 0, elem); - this.updateLength_(); - this.dispatchEvent( - new CollectionEvent(CollectionEventType.ADD, elem)); -}; - - -/** - * Remove the last element of the collection and return it. - * Return `undefined` if the collection is empty. - * @return {T|undefined} Element. - * @api - */ -Collection.prototype.pop = function() { - return this.removeAt(this.getLength() - 1); -}; - - -/** - * Insert the provided element at the end of the collection. - * @param {T} elem Element. - * @return {number} New length of the collection. - * @api - */ -Collection.prototype.push = function(elem) { - if (this.unique_) { - this.assertUnique_(elem); - } - const n = this.getLength(); - this.insertAt(n, elem); - return this.getLength(); -}; - - -/** - * Remove the first occurrence of an element from the collection. - * @param {T} elem Element. - * @return {T|undefined} The removed element or undefined if none found. - * @api - */ -Collection.prototype.remove = function(elem) { - const arr = this.array_; - for (let i = 0, ii = arr.length; i < ii; ++i) { - if (arr[i] === elem) { - return this.removeAt(i); - } - } - return undefined; -}; - - -/** - * Remove the element at the provided index and return it. - * Return `undefined` if the collection does not contain this index. - * @param {number} index Index. - * @return {T|undefined} Value. - * @api - */ -Collection.prototype.removeAt = function(index) { - const prev = this.array_[index]; - this.array_.splice(index, 1); - this.updateLength_(); - this.dispatchEvent(new CollectionEvent(CollectionEventType.REMOVE, prev)); - return prev; -}; - - -/** - * Set the element at the provided index. - * @param {number} index Index. - * @param {T} elem Element. - * @api - */ -Collection.prototype.setAt = function(index, elem) { - const n = this.getLength(); - if (index < n) { - if (this.unique_) { - this.assertUnique_(elem, index); - } - const prev = this.array_[index]; - this.array_[index] = elem; - this.dispatchEvent( - new CollectionEvent(CollectionEventType.REMOVE, prev)); - this.dispatchEvent( - new CollectionEvent(CollectionEventType.ADD, elem)); - } else { - for (let j = n; j < index; ++j) { - this.insertAt(j, undefined); - } - this.insertAt(index, elem); - } -}; - - -/** - * @private - */ -Collection.prototype.updateLength_ = function() { - this.set(Property.LENGTH, this.array_.length); -}; - - -/** - * @private - * @param {T} elem Element. - * @param {number=} opt_except Optional index to ignore. - */ -Collection.prototype.assertUnique_ = function(elem, opt_except) { - for (let i = 0, ii = this.array_.length; i < ii; ++i) { - if (this.array_[i] === elem && i !== opt_except) { - throw new AssertionError(58); - } - } -}; - export default Collection; diff --git a/src/ol/Disposable.js b/src/ol/Disposable.js index b3d9ee28ac..4b377d5c5c 100644 --- a/src/ol/Disposable.js +++ b/src/ol/Disposable.js @@ -7,7 +7,17 @@ import {UNDEFINED} from './functions.js'; * Objects that need to clean up after themselves. * @constructor */ -const Disposable = function() {}; +class Disposable { + /** + * Clean up. + */ + dispose() { + if (!this.disposed_) { + this.disposed_ = true; + this.disposeInternal(); + } + } +} /** * The object has already been disposed. @@ -16,16 +26,6 @@ const Disposable = function() {}; */ Disposable.prototype.disposed_ = false; -/** - * Clean up. - */ -Disposable.prototype.dispose = function() { - if (!this.disposed_) { - this.disposed_ = true; - this.disposeInternal(); - } -}; - /** * Extension point for disposable objects. * @protected diff --git a/src/ol/Feature.js b/src/ol/Feature.js index d6d2e025a3..fcd14ebb19 100644 --- a/src/ol/Feature.js +++ b/src/ol/Feature.js @@ -59,229 +59,219 @@ import Style from './style/Style.js'; * associated with a `geometry` key. * @api */ -const Feature = function(opt_geometryOrProperties) { +class Feature { + constructor(opt_geometryOrProperties) { - BaseObject.call(this); + BaseObject.call(this); - /** - * @private - * @type {number|string|undefined} - */ - this.id_ = undefined; + /** + * @private + * @type {number|string|undefined} + */ + this.id_ = undefined; - /** - * @type {string} - * @private - */ - this.geometryName_ = 'geometry'; + /** + * @type {string} + * @private + */ + this.geometryName_ = 'geometry'; - /** - * User provided style. - * @private - * @type {module:ol/style/Style|Array.|module:ol/style/Style~StyleFunction} - */ - this.style_ = null; + /** + * User provided style. + * @private + * @type {module:ol/style/Style|Array.|module:ol/style/Style~StyleFunction} + */ + this.style_ = null; - /** - * @private - * @type {module:ol/style/Style~StyleFunction|undefined} - */ - this.styleFunction_ = undefined; + /** + * @private + * @type {module:ol/style/Style~StyleFunction|undefined} + */ + this.styleFunction_ = undefined; - /** - * @private - * @type {?module:ol/events~EventsKey} - */ - this.geometryChangeKey_ = null; + /** + * @private + * @type {?module:ol/events~EventsKey} + */ + this.geometryChangeKey_ = null; - listen( - this, getChangeEventType(this.geometryName_), - this.handleGeometryChanged_, this); + listen( + this, getChangeEventType(this.geometryName_), + this.handleGeometryChanged_, this); - if (opt_geometryOrProperties !== undefined) { - if (opt_geometryOrProperties instanceof Geometry || - !opt_geometryOrProperties) { - const geometry = opt_geometryOrProperties; - this.setGeometry(geometry); - } else { - /** @type {Object.} */ - const properties = opt_geometryOrProperties; - this.setProperties(properties); + if (opt_geometryOrProperties !== undefined) { + if (opt_geometryOrProperties instanceof Geometry || + !opt_geometryOrProperties) { + const geometry = opt_geometryOrProperties; + this.setGeometry(geometry); + } else { + /** @type {Object.} */ + const properties = opt_geometryOrProperties; + this.setProperties(properties); + } } } -}; + + /** + * Clone this feature. If the original feature has a geometry it + * is also cloned. The feature id is not set in the clone. + * @return {module:ol/Feature} The clone. + * @api + */ + clone() { + const clone = new Feature(this.getProperties()); + clone.setGeometryName(this.getGeometryName()); + const geometry = this.getGeometry(); + if (geometry) { + clone.setGeometry(geometry.clone()); + } + const style = this.getStyle(); + if (style) { + clone.setStyle(style); + } + return clone; + } + + /** + * Get the feature's default geometry. A feature may have any number of named + * geometries. The "default" geometry (the one that is rendered by default) is + * set when calling {@link module:ol/Feature~Feature#setGeometry}. + * @return {module:ol/geom/Geometry|undefined} The default geometry for the feature. + * @api + * @observable + */ + getGeometry() { + return ( + /** @type {module:ol/geom/Geometry|undefined} */ (this.get(this.geometryName_)) + ); + } + + /** + * Get the feature identifier. This is a stable identifier for the feature and + * is either set when reading data from a remote source or set explicitly by + * calling {@link module:ol/Feature~Feature#setId}. + * @return {number|string|undefined} Id. + * @api + */ + getId() { + return this.id_; + } + + /** + * Get the name of the feature's default geometry. By default, the default + * geometry is named `geometry`. + * @return {string} Get the property name associated with the default geometry + * for this feature. + * @api + */ + getGeometryName() { + return this.geometryName_; + } + + /** + * Get the feature's style. Will return what was provided to the + * {@link module:ol/Feature~Feature#setStyle} method. + * @return {module:ol/style/Style|Array.|module:ol/style/Style~StyleFunction} The feature style. + * @api + */ + getStyle() { + return this.style_; + } + + /** + * Get the feature's style function. + * @return {module:ol/style/Style~StyleFunction|undefined} Return a function + * representing the current style of this feature. + * @api + */ + getStyleFunction() { + return this.styleFunction_; + } + + /** + * @private + */ + handleGeometryChange_() { + this.changed(); + } + + /** + * @private + */ + handleGeometryChanged_() { + if (this.geometryChangeKey_) { + unlistenByKey(this.geometryChangeKey_); + this.geometryChangeKey_ = null; + } + const geometry = this.getGeometry(); + if (geometry) { + this.geometryChangeKey_ = listen(geometry, + EventType.CHANGE, this.handleGeometryChange_, this); + } + this.changed(); + } + + /** + * Set the default geometry for the feature. This will update the property + * with the name returned by {@link module:ol/Feature~Feature#getGeometryName}. + * @param {module:ol/geom/Geometry|undefined} geometry The new geometry. + * @api + * @observable + */ + setGeometry(geometry) { + this.set(this.geometryName_, geometry); + } + + /** + * 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 {module:ol/style/Style|Array.|module:ol/style/Style~StyleFunction} style Style for this feature. + * @api + * @fires module:ol/events/Event~Event#event:change + */ + setStyle(style) { + this.style_ = style; + this.styleFunction_ = !style ? undefined : createStyleFunction(style); + this.changed(); + } + + /** + * Set the feature id. The feature id is considered stable and may be used when + * requesting features or comparing identifiers returned from a remote source. + * The feature id can be used with the + * {@link module:ol/source/Vector~VectorSource#getFeatureById} method. + * @param {number|string|undefined} id The feature id. + * @api + * @fires module:ol/events/Event~Event#event:change + */ + setId(id) { + this.id_ = id; + this.changed(); + } + + /** + * Set the property name to be used when getting the feature's default geometry. + * When calling {@link module:ol/Feature~Feature#getGeometry}, the value of the property with + * this name will be returned. + * @param {string} name The property name of the default geometry. + * @api + */ + setGeometryName(name) { + unlisten( + this, getChangeEventType(this.geometryName_), + this.handleGeometryChanged_, this); + this.geometryName_ = name; + listen( + this, getChangeEventType(this.geometryName_), + this.handleGeometryChanged_, this); + this.handleGeometryChanged_(); + } +} inherits(Feature, BaseObject); -/** - * Clone this feature. If the original feature has a geometry it - * is also cloned. The feature id is not set in the clone. - * @return {module:ol/Feature} The clone. - * @api - */ -Feature.prototype.clone = function() { - const clone = new Feature(this.getProperties()); - clone.setGeometryName(this.getGeometryName()); - const geometry = this.getGeometry(); - if (geometry) { - clone.setGeometry(geometry.clone()); - } - const style = this.getStyle(); - if (style) { - clone.setStyle(style); - } - return clone; -}; - - -/** - * Get the feature's default geometry. A feature may have any number of named - * geometries. The "default" geometry (the one that is rendered by default) is - * set when calling {@link module:ol/Feature~Feature#setGeometry}. - * @return {module:ol/geom/Geometry|undefined} The default geometry for the feature. - * @api - * @observable - */ -Feature.prototype.getGeometry = function() { - return ( - /** @type {module:ol/geom/Geometry|undefined} */ (this.get(this.geometryName_)) - ); -}; - - -/** - * Get the feature identifier. This is a stable identifier for the feature and - * is either set when reading data from a remote source or set explicitly by - * calling {@link module:ol/Feature~Feature#setId}. - * @return {number|string|undefined} Id. - * @api - */ -Feature.prototype.getId = function() { - return this.id_; -}; - - -/** - * Get the name of the feature's default geometry. By default, the default - * geometry is named `geometry`. - * @return {string} Get the property name associated with the default geometry - * for this feature. - * @api - */ -Feature.prototype.getGeometryName = function() { - return this.geometryName_; -}; - - -/** - * Get the feature's style. Will return what was provided to the - * {@link module:ol/Feature~Feature#setStyle} method. - * @return {module:ol/style/Style|Array.|module:ol/style/Style~StyleFunction} The feature style. - * @api - */ -Feature.prototype.getStyle = function() { - return this.style_; -}; - - -/** - * Get the feature's style function. - * @return {module:ol/style/Style~StyleFunction|undefined} Return a function - * representing the current style of this feature. - * @api - */ -Feature.prototype.getStyleFunction = function() { - return this.styleFunction_; -}; - - -/** - * @private - */ -Feature.prototype.handleGeometryChange_ = function() { - this.changed(); -}; - - -/** - * @private - */ -Feature.prototype.handleGeometryChanged_ = function() { - if (this.geometryChangeKey_) { - unlistenByKey(this.geometryChangeKey_); - this.geometryChangeKey_ = null; - } - const geometry = this.getGeometry(); - if (geometry) { - this.geometryChangeKey_ = listen(geometry, - EventType.CHANGE, this.handleGeometryChange_, this); - } - this.changed(); -}; - - -/** - * Set the default geometry for the feature. This will update the property - * with the name returned by {@link module:ol/Feature~Feature#getGeometryName}. - * @param {module:ol/geom/Geometry|undefined} geometry The new geometry. - * @api - * @observable - */ -Feature.prototype.setGeometry = function(geometry) { - this.set(this.geometryName_, geometry); -}; - - -/** - * 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 {module:ol/style/Style|Array.|module:ol/style/Style~StyleFunction} style Style for this feature. - * @api - * @fires module:ol/events/Event~Event#event:change - */ -Feature.prototype.setStyle = function(style) { - this.style_ = style; - this.styleFunction_ = !style ? undefined : createStyleFunction(style); - this.changed(); -}; - - -/** - * Set the feature id. The feature id is considered stable and may be used when - * requesting features or comparing identifiers returned from a remote source. - * The feature id can be used with the - * {@link module:ol/source/Vector~VectorSource#getFeatureById} method. - * @param {number|string|undefined} id The feature id. - * @api - * @fires module:ol/events/Event~Event#event:change - */ -Feature.prototype.setId = function(id) { - this.id_ = id; - this.changed(); -}; - - -/** - * Set the property name to be used when getting the feature's default geometry. - * When calling {@link module:ol/Feature~Feature#getGeometry}, the value of the property with - * this name will be returned. - * @param {string} name The property name of the default geometry. - * @api - */ -Feature.prototype.setGeometryName = function(name) { - unlisten( - this, getChangeEventType(this.geometryName_), - this.handleGeometryChanged_, this); - this.geometryName_ = name; - listen( - this, getChangeEventType(this.geometryName_), - this.handleGeometryChanged_, this); - this.handleGeometryChanged_(); -}; - - /** * Convert the provided object into a feature style function. Functions passed * through unchanged. Arrays of module:ol/style/Style or single style objects wrapped diff --git a/src/ol/Geolocation.js b/src/ol/Geolocation.js index 43a1d55fd7..80a3902162 100644 --- a/src/ol/Geolocation.js +++ b/src/ol/Geolocation.js @@ -49,302 +49,289 @@ import {get as getProjection, getTransformFromProjections, identityTransform} fr * @param {module:ol/Geolocation~Options=} opt_options Options. * @api */ -const Geolocation = function(opt_options) { +class Geolocation { + constructor(opt_options) { - BaseObject.call(this); + BaseObject.call(this); - const options = opt_options || {}; + const options = opt_options || {}; - /** - * The unprojected (EPSG:4326) device position. - * @private - * @type {module:ol/coordinate~Coordinate} - */ - this.position_ = null; + /** + * The unprojected (EPSG:4326) device position. + * @private + * @type {module:ol/coordinate~Coordinate} + */ + this.position_ = null; - /** - * @private - * @type {module:ol/proj~TransformFunction} - */ - this.transform_ = identityTransform; + /** + * @private + * @type {module:ol/proj~TransformFunction} + */ + this.transform_ = identityTransform; - /** - * @private - * @type {number|undefined} - */ - this.watchId_ = undefined; + /** + * @private + * @type {number|undefined} + */ + this.watchId_ = undefined; - listen( - this, getChangeEventType(GeolocationProperty.PROJECTION), - this.handleProjectionChanged_, this); - listen( - this, getChangeEventType(GeolocationProperty.TRACKING), - this.handleTrackingChanged_, this); + listen( + this, getChangeEventType(GeolocationProperty.PROJECTION), + this.handleProjectionChanged_, this); + listen( + this, getChangeEventType(GeolocationProperty.TRACKING), + this.handleTrackingChanged_, this); + + if (options.projection !== undefined) { + this.setProjection(options.projection); + } + if (options.trackingOptions !== undefined) { + this.setTrackingOptions(options.trackingOptions); + } + + this.setTracking(options.tracking !== undefined ? options.tracking : false); - if (options.projection !== undefined) { - this.setProjection(options.projection); - } - if (options.trackingOptions !== undefined) { - this.setTrackingOptions(options.trackingOptions); } - this.setTracking(options.tracking !== undefined ? options.tracking : false); + /** + * @inheritDoc + */ + disposeInternal() { + this.setTracking(false); + BaseObject.prototype.disposeInternal.call(this); + } -}; + /** + * @private + */ + handleProjectionChanged_() { + const projection = this.getProjection(); + if (projection) { + this.transform_ = getTransformFromProjections( + getProjection('EPSG:4326'), projection); + if (this.position_) { + this.set(GeolocationProperty.POSITION, this.transform_(this.position_)); + } + } + } + + /** + * @private + */ + handleTrackingChanged_() { + if (GEOLOCATION) { + const tracking = this.getTracking(); + if (tracking && this.watchId_ === undefined) { + this.watchId_ = navigator.geolocation.watchPosition( + this.positionChange_.bind(this), + this.positionError_.bind(this), + this.getTrackingOptions()); + } else if (!tracking && this.watchId_ !== undefined) { + navigator.geolocation.clearWatch(this.watchId_); + this.watchId_ = undefined; + } + } + } + + /** + * @private + * @param {GeolocationPosition} position position event. + */ + positionChange_(position) { + const coords = position.coords; + this.set(GeolocationProperty.ACCURACY, coords.accuracy); + this.set(GeolocationProperty.ALTITUDE, + coords.altitude === null ? undefined : coords.altitude); + this.set(GeolocationProperty.ALTITUDE_ACCURACY, + coords.altitudeAccuracy === null ? + undefined : coords.altitudeAccuracy); + this.set(GeolocationProperty.HEADING, coords.heading === null ? + undefined : toRadians(coords.heading)); + if (!this.position_) { + this.position_ = [coords.longitude, coords.latitude]; + } else { + this.position_[0] = coords.longitude; + this.position_[1] = coords.latitude; + } + const projectedPosition = this.transform_(this.position_); + this.set(GeolocationProperty.POSITION, projectedPosition); + this.set(GeolocationProperty.SPEED, + coords.speed === null ? undefined : coords.speed); + const geometry = circularPolygon(this.position_, coords.accuracy); + geometry.applyTransform(this.transform_); + this.set(GeolocationProperty.ACCURACY_GEOMETRY, geometry); + this.changed(); + } + + /** + * Triggered when the Geolocation returns an error. + * @event error + * @api + */ + + /** + * @private + * @param {GeolocationPositionError} error error object. + */ + positionError_(error) { + error.type = EventType.ERROR; + this.setTracking(false); + this.dispatchEvent(/** @type {{type: string, target: undefined}} */ (error)); + } + + /** + * Get the accuracy of the position in meters. + * @return {number|undefined} The accuracy of the position measurement in + * meters. + * @observable + * @api + */ + getAccuracy() { + return /** @type {number|undefined} */ (this.get(GeolocationProperty.ACCURACY)); + } + + /** + * Get a geometry of the position accuracy. + * @return {?module:ol/geom/Polygon} A geometry of the position accuracy. + * @observable + * @api + */ + getAccuracyGeometry() { + return ( + /** @type {?module:ol/geom/Polygon} */ (this.get(GeolocationProperty.ACCURACY_GEOMETRY) || null) + ); + } + + /** + * Get the altitude associated with the position. + * @return {number|undefined} The altitude of the position in meters above mean + * sea level. + * @observable + * @api + */ + getAltitude() { + return /** @type {number|undefined} */ (this.get(GeolocationProperty.ALTITUDE)); + } + + /** + * Get the altitude accuracy of the position. + * @return {number|undefined} The accuracy of the altitude measurement in + * meters. + * @observable + * @api + */ + getAltitudeAccuracy() { + return /** @type {number|undefined} */ (this.get(GeolocationProperty.ALTITUDE_ACCURACY)); + } + + /** + * Get the heading as radians clockwise from North. + * Note: depending on the browser, the heading is only defined if the `enableHighAccuracy` + * is set to `true` in the tracking options. + * @return {number|undefined} The heading of the device in radians from north. + * @observable + * @api + */ + getHeading() { + return /** @type {number|undefined} */ (this.get(GeolocationProperty.HEADING)); + } + + /** + * Get the position of the device. + * @return {module:ol/coordinate~Coordinate|undefined} The current position of the device reported + * in the current projection. + * @observable + * @api + */ + getPosition() { + return ( + /** @type {module:ol/coordinate~Coordinate|undefined} */ (this.get(GeolocationProperty.POSITION)) + ); + } + + /** + * Get the projection associated with the position. + * @return {module:ol/proj/Projection|undefined} The projection the position is + * reported in. + * @observable + * @api + */ + getProjection() { + return ( + /** @type {module:ol/proj/Projection|undefined} */ (this.get(GeolocationProperty.PROJECTION)) + ); + } + + /** + * Get the speed in meters per second. + * @return {number|undefined} The instantaneous speed of the device in meters + * per second. + * @observable + * @api + */ + getSpeed() { + return /** @type {number|undefined} */ (this.get(GeolocationProperty.SPEED)); + } + + /** + * Determine if the device location is being tracked. + * @return {boolean} The device location is being tracked. + * @observable + * @api + */ + getTracking() { + return /** @type {boolean} */ (this.get(GeolocationProperty.TRACKING)); + } + + /** + * Get the tracking options. + * @see http://www.w3.org/TR/geolocation-API/#position-options + * @return {GeolocationPositionOptions|undefined} PositionOptions as defined by + * the [HTML5 Geolocation spec + * ](http://www.w3.org/TR/geolocation-API/#position_options_interface). + * @observable + * @api + */ + getTrackingOptions() { + return /** @type {GeolocationPositionOptions|undefined} */ (this.get(GeolocationProperty.TRACKING_OPTIONS)); + } + + /** + * Set the projection to use for transforming the coordinates. + * @param {module:ol/proj~ProjectionLike} projection The projection the position is + * reported in. + * @observable + * @api + */ + setProjection(projection) { + this.set(GeolocationProperty.PROJECTION, getProjection(projection)); + } + + /** + * Enable or disable tracking. + * @param {boolean} tracking Enable tracking. + * @observable + * @api + */ + setTracking(tracking) { + this.set(GeolocationProperty.TRACKING, tracking); + } + + /** + * Set the tracking options. + * @see http://www.w3.org/TR/geolocation-API/#position-options + * @param {GeolocationPositionOptions} options PositionOptions as defined by the + * [HTML5 Geolocation spec + * ](http://www.w3.org/TR/geolocation-API/#position_options_interface). + * @observable + * @api + */ + setTrackingOptions(options) { + this.set(GeolocationProperty.TRACKING_OPTIONS, options); + } +} inherits(Geolocation, BaseObject); -/** - * @inheritDoc - */ -Geolocation.prototype.disposeInternal = function() { - this.setTracking(false); - BaseObject.prototype.disposeInternal.call(this); -}; - - -/** - * @private - */ -Geolocation.prototype.handleProjectionChanged_ = function() { - const projection = this.getProjection(); - if (projection) { - this.transform_ = getTransformFromProjections( - getProjection('EPSG:4326'), projection); - if (this.position_) { - this.set(GeolocationProperty.POSITION, this.transform_(this.position_)); - } - } -}; - - -/** - * @private - */ -Geolocation.prototype.handleTrackingChanged_ = function() { - if (GEOLOCATION) { - const tracking = this.getTracking(); - if (tracking && this.watchId_ === undefined) { - this.watchId_ = navigator.geolocation.watchPosition( - this.positionChange_.bind(this), - this.positionError_.bind(this), - this.getTrackingOptions()); - } else if (!tracking && this.watchId_ !== undefined) { - navigator.geolocation.clearWatch(this.watchId_); - this.watchId_ = undefined; - } - } -}; - - -/** - * @private - * @param {GeolocationPosition} position position event. - */ -Geolocation.prototype.positionChange_ = function(position) { - const coords = position.coords; - this.set(GeolocationProperty.ACCURACY, coords.accuracy); - this.set(GeolocationProperty.ALTITUDE, - coords.altitude === null ? undefined : coords.altitude); - this.set(GeolocationProperty.ALTITUDE_ACCURACY, - coords.altitudeAccuracy === null ? - undefined : coords.altitudeAccuracy); - this.set(GeolocationProperty.HEADING, coords.heading === null ? - undefined : toRadians(coords.heading)); - if (!this.position_) { - this.position_ = [coords.longitude, coords.latitude]; - } else { - this.position_[0] = coords.longitude; - this.position_[1] = coords.latitude; - } - const projectedPosition = this.transform_(this.position_); - this.set(GeolocationProperty.POSITION, projectedPosition); - this.set(GeolocationProperty.SPEED, - coords.speed === null ? undefined : coords.speed); - const geometry = circularPolygon(this.position_, coords.accuracy); - geometry.applyTransform(this.transform_); - this.set(GeolocationProperty.ACCURACY_GEOMETRY, geometry); - this.changed(); -}; - -/** - * Triggered when the Geolocation returns an error. - * @event error - * @api - */ - -/** - * @private - * @param {GeolocationPositionError} error error object. - */ -Geolocation.prototype.positionError_ = function(error) { - error.type = EventType.ERROR; - this.setTracking(false); - this.dispatchEvent(/** @type {{type: string, target: undefined}} */ (error)); -}; - - -/** - * Get the accuracy of the position in meters. - * @return {number|undefined} The accuracy of the position measurement in - * meters. - * @observable - * @api - */ -Geolocation.prototype.getAccuracy = function() { - return /** @type {number|undefined} */ (this.get(GeolocationProperty.ACCURACY)); -}; - - -/** - * Get a geometry of the position accuracy. - * @return {?module:ol/geom/Polygon} A geometry of the position accuracy. - * @observable - * @api - */ -Geolocation.prototype.getAccuracyGeometry = function() { - return ( - /** @type {?module:ol/geom/Polygon} */ (this.get(GeolocationProperty.ACCURACY_GEOMETRY) || null) - ); -}; - - -/** - * Get the altitude associated with the position. - * @return {number|undefined} The altitude of the position in meters above mean - * sea level. - * @observable - * @api - */ -Geolocation.prototype.getAltitude = function() { - return /** @type {number|undefined} */ (this.get(GeolocationProperty.ALTITUDE)); -}; - - -/** - * Get the altitude accuracy of the position. - * @return {number|undefined} The accuracy of the altitude measurement in - * meters. - * @observable - * @api - */ -Geolocation.prototype.getAltitudeAccuracy = function() { - return /** @type {number|undefined} */ (this.get(GeolocationProperty.ALTITUDE_ACCURACY)); -}; - - -/** - * Get the heading as radians clockwise from North. - * Note: depending on the browser, the heading is only defined if the `enableHighAccuracy` - * is set to `true` in the tracking options. - * @return {number|undefined} The heading of the device in radians from north. - * @observable - * @api - */ -Geolocation.prototype.getHeading = function() { - return /** @type {number|undefined} */ (this.get(GeolocationProperty.HEADING)); -}; - - -/** - * Get the position of the device. - * @return {module:ol/coordinate~Coordinate|undefined} The current position of the device reported - * in the current projection. - * @observable - * @api - */ -Geolocation.prototype.getPosition = function() { - return ( - /** @type {module:ol/coordinate~Coordinate|undefined} */ (this.get(GeolocationProperty.POSITION)) - ); -}; - - -/** - * Get the projection associated with the position. - * @return {module:ol/proj/Projection|undefined} The projection the position is - * reported in. - * @observable - * @api - */ -Geolocation.prototype.getProjection = function() { - return ( - /** @type {module:ol/proj/Projection|undefined} */ (this.get(GeolocationProperty.PROJECTION)) - ); -}; - - -/** - * Get the speed in meters per second. - * @return {number|undefined} The instantaneous speed of the device in meters - * per second. - * @observable - * @api - */ -Geolocation.prototype.getSpeed = function() { - return /** @type {number|undefined} */ (this.get(GeolocationProperty.SPEED)); -}; - - -/** - * Determine if the device location is being tracked. - * @return {boolean} The device location is being tracked. - * @observable - * @api - */ -Geolocation.prototype.getTracking = function() { - return /** @type {boolean} */ (this.get(GeolocationProperty.TRACKING)); -}; - - -/** - * Get the tracking options. - * @see http://www.w3.org/TR/geolocation-API/#position-options - * @return {GeolocationPositionOptions|undefined} PositionOptions as defined by - * the [HTML5 Geolocation spec - * ](http://www.w3.org/TR/geolocation-API/#position_options_interface). - * @observable - * @api - */ -Geolocation.prototype.getTrackingOptions = function() { - return /** @type {GeolocationPositionOptions|undefined} */ (this.get(GeolocationProperty.TRACKING_OPTIONS)); -}; - - -/** - * Set the projection to use for transforming the coordinates. - * @param {module:ol/proj~ProjectionLike} projection The projection the position is - * reported in. - * @observable - * @api - */ -Geolocation.prototype.setProjection = function(projection) { - this.set(GeolocationProperty.PROJECTION, getProjection(projection)); -}; - - -/** - * Enable or disable tracking. - * @param {boolean} tracking Enable tracking. - * @observable - * @api - */ -Geolocation.prototype.setTracking = function(tracking) { - this.set(GeolocationProperty.TRACKING, tracking); -}; - - -/** - * Set the tracking options. - * @see http://www.w3.org/TR/geolocation-API/#position-options - * @param {GeolocationPositionOptions} options PositionOptions as defined by the - * [HTML5 Geolocation spec - * ](http://www.w3.org/TR/geolocation-API/#position_options_interface). - * @observable - * @api - */ -Geolocation.prototype.setTrackingOptions = function(options) { - this.set(GeolocationProperty.TRACKING_OPTIONS, options); -}; export default Geolocation; diff --git a/src/ol/Graticule.js b/src/ol/Graticule.js index 4682476256..375d2df7a0 100644 --- a/src/ol/Graticule.js +++ b/src/ol/Graticule.js @@ -116,616 +116,606 @@ const INTERVALS = [ * @param {module:ol/Graticule~Options=} opt_options Options. * @api */ -const Graticule = function(opt_options) { - const options = opt_options || {}; - - /** - * @type {module:ol/PluggableMap} - * @private - */ - this.map_ = null; - - /** - * @type {?module:ol/events~EventsKey} - * @private - */ - this.postcomposeListenerKey_ = null; - - /** - * @type {module:ol/proj/Projection} - */ - this.projection_ = null; - - /** - * @type {number} - * @private - */ - this.maxLat_ = Infinity; - - /** - * @type {number} - * @private - */ - this.maxLon_ = Infinity; - - /** - * @type {number} - * @private - */ - this.minLat_ = -Infinity; - - /** - * @type {number} - * @private - */ - this.minLon_ = -Infinity; - - /** - * @type {number} - * @private - */ - this.maxLatP_ = Infinity; - - /** - * @type {number} - * @private - */ - this.maxLonP_ = Infinity; - - /** - * @type {number} - * @private - */ - this.minLatP_ = -Infinity; - - /** - * @type {number} - * @private - */ - this.minLonP_ = -Infinity; - - /** - * @type {number} - * @private - */ - this.targetSize_ = options.targetSize !== undefined ? options.targetSize : 100; - - /** - * @type {number} - * @private - */ - this.maxLines_ = options.maxLines !== undefined ? options.maxLines : 100; - - /** - * @type {Array.} - * @private - */ - this.meridians_ = []; - - /** - * @type {Array.} - * @private - */ - this.parallels_ = []; - - /** - * @type {module:ol/style/Stroke} - * @private - */ - this.strokeStyle_ = options.strokeStyle !== undefined ? options.strokeStyle : DEFAULT_STROKE_STYLE; - - /** - * @type {module:ol/proj~TransformFunction|undefined} - * @private - */ - this.fromLonLatTransform_ = undefined; - - /** - * @type {module:ol/proj~TransformFunction|undefined} - * @private - */ - this.toLonLatTransform_ = undefined; - - /** - * @type {module:ol/coordinate~Coordinate} - * @private - */ - this.projectionCenterLonLat_ = null; - - /** - * @type {Array.} - * @private - */ - this.meridiansLabels_ = null; - - /** - * @type {Array.} - * @private - */ - this.parallelsLabels_ = null; - - if (options.showLabels == true) { +class Graticule { + constructor(opt_options) { + const options = opt_options || {}; /** - * @type {null|function(number):string} + * @type {module:ol/PluggableMap} * @private */ - this.lonLabelFormatter_ = options.lonLabelFormatter == undefined ? - degreesToStringHDMS.bind(this, 'EW') : options.lonLabelFormatter; + this.map_ = null; /** - * @type {function(number):string} + * @type {?module:ol/events~EventsKey} * @private */ - this.latLabelFormatter_ = options.latLabelFormatter == undefined ? - degreesToStringHDMS.bind(this, 'NS') : options.latLabelFormatter; - - /** - * Longitude label position in fractions (0..1) of view extent. 0 means - * bottom, 1 means top. - * @type {number} - * @private - */ - this.lonLabelPosition_ = options.lonLabelPosition == undefined ? 0 : - options.lonLabelPosition; - - /** - * Latitude Label position in fractions (0..1) of view extent. 0 means left, 1 - * means right. - * @type {number} - * @private - */ - this.latLabelPosition_ = options.latLabelPosition == undefined ? 1 : - options.latLabelPosition; - - /** - * @type {module:ol/style/Text} - * @private - */ - this.lonLabelStyle_ = options.lonLabelStyle !== undefined ? options.lonLabelStyle : - new Text({ - font: '12px Calibri,sans-serif', - textBaseline: 'bottom', - fill: new Fill({ - color: 'rgba(0,0,0,1)' - }), - stroke: new Stroke({ - color: 'rgba(255,255,255,1)', - width: 3 - }) - }); - - /** - * @type {module:ol/style/Text} - * @private - */ - this.latLabelStyle_ = options.latLabelStyle !== undefined ? options.latLabelStyle : - new Text({ - font: '12px Calibri,sans-serif', - textAlign: 'end', - fill: new Fill({ - color: 'rgba(0,0,0,1)' - }), - stroke: new Stroke({ - color: 'rgba(255,255,255,1)', - width: 3 - }) - }); - - this.meridiansLabels_ = []; - this.parallelsLabels_ = []; - } - - this.setMap(options.map !== undefined ? options.map : null); -}; - - -/** - * @param {number} lon Longitude. - * @param {number} minLat Minimal latitude. - * @param {number} maxLat Maximal latitude. - * @param {number} squaredTolerance Squared tolerance. - * @param {module:ol/extent~Extent} extent Extent. - * @param {number} index Index. - * @return {number} Index. - * @private - */ -Graticule.prototype.addMeridian_ = function(lon, minLat, maxLat, squaredTolerance, extent, index) { - const lineString = this.getMeridian_(lon, minLat, maxLat, squaredTolerance, index); - if (intersects(lineString.getExtent(), extent)) { - if (this.meridiansLabels_) { - const textPoint = this.getMeridianPoint_(lineString, extent, index); - this.meridiansLabels_[index] = { - geom: textPoint, - text: this.lonLabelFormatter_(lon) - }; - } - this.meridians_[index++] = lineString; - } - return index; -}; - -/** - * @param {module:ol/geom/LineString} lineString Meridian - * @param {module:ol/extent~Extent} extent Extent. - * @param {number} index Index. - * @return {module:ol/geom/Point} Meridian point. - * @private - */ -Graticule.prototype.getMeridianPoint_ = function(lineString, extent, index) { - const flatCoordinates = lineString.getFlatCoordinates(); - const clampedBottom = Math.max(extent[1], flatCoordinates[1]); - const clampedTop = Math.min(extent[3], flatCoordinates[flatCoordinates.length - 1]); - const lat = clamp( - extent[1] + Math.abs(extent[1] - extent[3]) * this.lonLabelPosition_, - clampedBottom, clampedTop); - const coordinate = [flatCoordinates[0], lat]; - let point; - if (index in this.meridiansLabels_) { - point = this.meridiansLabels_[index]; - point.setCoordinates(coordinate); - } else { - point = new Point(coordinate); - } - return point; -}; - - -/** - * @param {number} lat Latitude. - * @param {number} minLon Minimal longitude. - * @param {number} maxLon Maximal longitude. - * @param {number} squaredTolerance Squared tolerance. - * @param {module:ol/extent~Extent} extent Extent. - * @param {number} index Index. - * @return {number} Index. - * @private - */ -Graticule.prototype.addParallel_ = function(lat, minLon, maxLon, squaredTolerance, extent, index) { - const lineString = this.getParallel_(lat, minLon, maxLon, squaredTolerance, index); - if (intersects(lineString.getExtent(), extent)) { - if (this.parallelsLabels_) { - const textPoint = this.getParallelPoint_(lineString, extent, index); - this.parallelsLabels_[index] = { - geom: textPoint, - text: this.latLabelFormatter_(lat) - }; - } - this.parallels_[index++] = lineString; - } - return index; -}; - - -/** - * @param {module:ol/geom/LineString} lineString Parallels. - * @param {module:ol/extent~Extent} extent Extent. - * @param {number} index Index. - * @return {module:ol/geom/Point} Parallel point. - * @private - */ -Graticule.prototype.getParallelPoint_ = function(lineString, extent, index) { - const flatCoordinates = lineString.getFlatCoordinates(); - const clampedLeft = Math.max(extent[0], flatCoordinates[0]); - const clampedRight = Math.min(extent[2], flatCoordinates[flatCoordinates.length - 2]); - const lon = clamp( - extent[0] + Math.abs(extent[0] - extent[2]) * this.latLabelPosition_, - clampedLeft, clampedRight); - const coordinate = [lon, flatCoordinates[1]]; - let point; - if (index in this.parallelsLabels_) { - point = this.parallelsLabels_[index]; - point.setCoordinates(coordinate); - } else { - point = new Point(coordinate); - } - return point; -}; - - -/** - * @param {module:ol/extent~Extent} extent Extent. - * @param {module:ol/coordinate~Coordinate} center Center. - * @param {number} resolution Resolution. - * @param {number} squaredTolerance Squared tolerance. - * @private - */ -Graticule.prototype.createGraticule_ = function(extent, center, resolution, squaredTolerance) { - - const interval = this.getInterval_(resolution); - if (interval == -1) { - this.meridians_.length = this.parallels_.length = 0; - if (this.meridiansLabels_) { - this.meridiansLabels_.length = 0; - } - if (this.parallelsLabels_) { - this.parallelsLabels_.length = 0; - } - return; - } - - const centerLonLat = this.toLonLatTransform_(center); - let centerLon = centerLonLat[0]; - let centerLat = centerLonLat[1]; - const maxLines = this.maxLines_; - let cnt, idx, lat, lon; - - let validExtent = [ - Math.max(extent[0], this.minLonP_), - Math.max(extent[1], this.minLatP_), - Math.min(extent[2], this.maxLonP_), - Math.min(extent[3], this.maxLatP_) - ]; - - validExtent = transformExtent(validExtent, this.projection_, 'EPSG:4326'); - const maxLat = validExtent[3]; - const maxLon = validExtent[2]; - const minLat = validExtent[1]; - const minLon = validExtent[0]; - - // Create meridians - - centerLon = Math.floor(centerLon / interval) * interval; - lon = clamp(centerLon, this.minLon_, this.maxLon_); - - idx = this.addMeridian_(lon, minLat, maxLat, squaredTolerance, extent, 0); - - cnt = 0; - while (lon != this.minLon_ && cnt++ < maxLines) { - lon = Math.max(lon - interval, this.minLon_); - idx = this.addMeridian_(lon, minLat, maxLat, squaredTolerance, extent, idx); - } - - lon = clamp(centerLon, this.minLon_, this.maxLon_); - - cnt = 0; - while (lon != this.maxLon_ && cnt++ < maxLines) { - lon = Math.min(lon + interval, this.maxLon_); - idx = this.addMeridian_(lon, minLat, maxLat, squaredTolerance, extent, idx); - } - - this.meridians_.length = idx; - if (this.meridiansLabels_) { - this.meridiansLabels_.length = idx; - } - - // Create parallels - - centerLat = Math.floor(centerLat / interval) * interval; - lat = clamp(centerLat, this.minLat_, this.maxLat_); - - idx = this.addParallel_(lat, minLon, maxLon, squaredTolerance, extent, 0); - - cnt = 0; - while (lat != this.minLat_ && cnt++ < maxLines) { - lat = Math.max(lat - interval, this.minLat_); - idx = this.addParallel_(lat, minLon, maxLon, squaredTolerance, extent, idx); - } - - lat = clamp(centerLat, this.minLat_, this.maxLat_); - - cnt = 0; - while (lat != this.maxLat_ && cnt++ < maxLines) { - lat = Math.min(lat + interval, this.maxLat_); - idx = this.addParallel_(lat, minLon, maxLon, squaredTolerance, extent, idx); - } - - this.parallels_.length = idx; - if (this.parallelsLabels_) { - this.parallelsLabels_.length = idx; - } - -}; - - -/** - * @param {number} resolution Resolution. - * @return {number} The interval in degrees. - * @private - */ -Graticule.prototype.getInterval_ = function(resolution) { - const centerLon = this.projectionCenterLonLat_[0]; - const centerLat = this.projectionCenterLonLat_[1]; - let interval = -1; - const target = Math.pow(this.targetSize_ * resolution, 2); - /** @type {Array.} **/ - const p1 = []; - /** @type {Array.} **/ - const p2 = []; - for (let i = 0, ii = INTERVALS.length; i < ii; ++i) { - const delta = INTERVALS[i] / 2; - p1[0] = centerLon - delta; - p1[1] = centerLat - delta; - p2[0] = centerLon + delta; - p2[1] = centerLat + delta; - this.fromLonLatTransform_(p1, p1); - this.fromLonLatTransform_(p2, p2); - const dist = Math.pow(p2[0] - p1[0], 2) + Math.pow(p2[1] - p1[1], 2); - if (dist <= target) { - break; - } - interval = INTERVALS[i]; - } - return interval; -}; - - -/** - * Get the map associated with this graticule. - * @return {module:ol/PluggableMap} The map. - * @api - */ -Graticule.prototype.getMap = function() { - return this.map_; -}; - - -/** - * @param {number} lon Longitude. - * @param {number} minLat Minimal latitude. - * @param {number} maxLat Maximal latitude. - * @param {number} squaredTolerance Squared tolerance. - * @return {module:ol/geom/LineString} The meridian line string. - * @param {number} index Index. - * @private - */ -Graticule.prototype.getMeridian_ = function(lon, minLat, maxLat, squaredTolerance, index) { - const flatCoordinates = meridian(lon, minLat, maxLat, this.projection_, squaredTolerance); - let lineString = this.meridians_[index]; - if (!lineString) { - lineString = this.meridians_[index] = new LineString(flatCoordinates, GeometryLayout.XY); - } else { - lineString.setFlatCoordinates(GeometryLayout.XY, flatCoordinates); - lineString.changed(); - } - return lineString; -}; - - -/** - * Get the list of meridians. Meridians are lines of equal longitude. - * @return {Array.} The meridians. - * @api - */ -Graticule.prototype.getMeridians = function() { - return this.meridians_; -}; - - -/** - * @param {number} lat Latitude. - * @param {number} minLon Minimal longitude. - * @param {number} maxLon Maximal longitude. - * @param {number} squaredTolerance Squared tolerance. - * @return {module:ol/geom/LineString} The parallel line string. - * @param {number} index Index. - * @private - */ -Graticule.prototype.getParallel_ = function(lat, minLon, maxLon, squaredTolerance, index) { - const flatCoordinates = parallel(lat, minLon, maxLon, this.projection_, squaredTolerance); - let lineString = this.parallels_[index]; - if (!lineString) { - lineString = new LineString(flatCoordinates, GeometryLayout.XY); - } else { - lineString.setFlatCoordinates(GeometryLayout.XY, flatCoordinates); - lineString.changed(); - } - return lineString; -}; - - -/** - * Get the list of parallels. Parallels are lines of equal latitude. - * @return {Array.} The parallels. - * @api - */ -Graticule.prototype.getParallels = function() { - return this.parallels_; -}; - - -/** - * @param {module:ol/render/Event} e Event. - * @private - */ -Graticule.prototype.handlePostCompose_ = function(e) { - const vectorContext = e.vectorContext; - const frameState = e.frameState; - const extent = frameState.extent; - const viewState = frameState.viewState; - const center = viewState.center; - const projection = viewState.projection; - const resolution = viewState.resolution; - const pixelRatio = frameState.pixelRatio; - const squaredTolerance = - resolution * resolution / (4 * pixelRatio * pixelRatio); - - const updateProjectionInfo = !this.projection_ || - !equivalentProjection(this.projection_, projection); - - if (updateProjectionInfo) { - this.updateProjectionInfo_(projection); - } - - this.createGraticule_(extent, center, resolution, squaredTolerance); - - // Draw the lines - vectorContext.setFillStrokeStyle(null, this.strokeStyle_); - let i, l, line; - for (i = 0, l = this.meridians_.length; i < l; ++i) { - line = this.meridians_[i]; - vectorContext.drawGeometry(line); - } - for (i = 0, l = this.parallels_.length; i < l; ++i) { - line = this.parallels_[i]; - vectorContext.drawGeometry(line); - } - let labelData; - if (this.meridiansLabels_) { - for (i = 0, l = this.meridiansLabels_.length; i < l; ++i) { - labelData = this.meridiansLabels_[i]; - this.lonLabelStyle_.setText(labelData.text); - vectorContext.setTextStyle(this.lonLabelStyle_); - vectorContext.drawGeometry(labelData.geom); - } - } - if (this.parallelsLabels_) { - for (i = 0, l = this.parallelsLabels_.length; i < l; ++i) { - labelData = this.parallelsLabels_[i]; - this.latLabelStyle_.setText(labelData.text); - vectorContext.setTextStyle(this.latLabelStyle_); - vectorContext.drawGeometry(labelData.geom); - } - } -}; - - -/** - * @param {module:ol/proj/Projection} projection Projection. - * @private - */ -Graticule.prototype.updateProjectionInfo_ = function(projection) { - const epsg4326Projection = getProjection('EPSG:4326'); - - const worldExtent = projection.getWorldExtent(); - const worldExtentP = transformExtent(worldExtent, epsg4326Projection, projection); - - this.maxLat_ = worldExtent[3]; - this.maxLon_ = worldExtent[2]; - this.minLat_ = worldExtent[1]; - this.minLon_ = worldExtent[0]; - - this.maxLatP_ = worldExtentP[3]; - this.maxLonP_ = worldExtentP[2]; - this.minLatP_ = worldExtentP[1]; - this.minLonP_ = worldExtentP[0]; - - this.fromLonLatTransform_ = getTransform(epsg4326Projection, projection); - - this.toLonLatTransform_ = getTransform(projection, epsg4326Projection); - - this.projectionCenterLonLat_ = this.toLonLatTransform_(getCenter(projection.getExtent())); - - this.projection_ = projection; -}; - - -/** - * Set the map for this graticule. The graticule will be rendered on the - * provided map. - * @param {module:ol/PluggableMap} map Map. - * @api - */ -Graticule.prototype.setMap = function(map) { - if (this.map_) { - unlistenByKey(this.postcomposeListenerKey_); this.postcomposeListenerKey_ = null; - this.map_.render(); + + /** + * @type {module:ol/proj/Projection} + */ + this.projection_ = null; + + /** + * @type {number} + * @private + */ + this.maxLat_ = Infinity; + + /** + * @type {number} + * @private + */ + this.maxLon_ = Infinity; + + /** + * @type {number} + * @private + */ + this.minLat_ = -Infinity; + + /** + * @type {number} + * @private + */ + this.minLon_ = -Infinity; + + /** + * @type {number} + * @private + */ + this.maxLatP_ = Infinity; + + /** + * @type {number} + * @private + */ + this.maxLonP_ = Infinity; + + /** + * @type {number} + * @private + */ + this.minLatP_ = -Infinity; + + /** + * @type {number} + * @private + */ + this.minLonP_ = -Infinity; + + /** + * @type {number} + * @private + */ + this.targetSize_ = options.targetSize !== undefined ? options.targetSize : 100; + + /** + * @type {number} + * @private + */ + this.maxLines_ = options.maxLines !== undefined ? options.maxLines : 100; + + /** + * @type {Array.} + * @private + */ + this.meridians_ = []; + + /** + * @type {Array.} + * @private + */ + this.parallels_ = []; + + /** + * @type {module:ol/style/Stroke} + * @private + */ + this.strokeStyle_ = options.strokeStyle !== undefined ? options.strokeStyle : DEFAULT_STROKE_STYLE; + + /** + * @type {module:ol/proj~TransformFunction|undefined} + * @private + */ + this.fromLonLatTransform_ = undefined; + + /** + * @type {module:ol/proj~TransformFunction|undefined} + * @private + */ + this.toLonLatTransform_ = undefined; + + /** + * @type {module:ol/coordinate~Coordinate} + * @private + */ + this.projectionCenterLonLat_ = null; + + /** + * @type {Array.} + * @private + */ + this.meridiansLabels_ = null; + + /** + * @type {Array.} + * @private + */ + this.parallelsLabels_ = null; + + if (options.showLabels == true) { + + /** + * @type {null|function(number):string} + * @private + */ + this.lonLabelFormatter_ = options.lonLabelFormatter == undefined ? + degreesToStringHDMS.bind(this, 'EW') : options.lonLabelFormatter; + + /** + * @type {function(number):string} + * @private + */ + this.latLabelFormatter_ = options.latLabelFormatter == undefined ? + degreesToStringHDMS.bind(this, 'NS') : options.latLabelFormatter; + + /** + * Longitude label position in fractions (0..1) of view extent. 0 means + * bottom, 1 means top. + * @type {number} + * @private + */ + this.lonLabelPosition_ = options.lonLabelPosition == undefined ? 0 : + options.lonLabelPosition; + + /** + * Latitude Label position in fractions (0..1) of view extent. 0 means left, 1 + * means right. + * @type {number} + * @private + */ + this.latLabelPosition_ = options.latLabelPosition == undefined ? 1 : + options.latLabelPosition; + + /** + * @type {module:ol/style/Text} + * @private + */ + this.lonLabelStyle_ = options.lonLabelStyle !== undefined ? options.lonLabelStyle : + new Text({ + font: '12px Calibri,sans-serif', + textBaseline: 'bottom', + fill: new Fill({ + color: 'rgba(0,0,0,1)' + }), + stroke: new Stroke({ + color: 'rgba(255,255,255,1)', + width: 3 + }) + }); + + /** + * @type {module:ol/style/Text} + * @private + */ + this.latLabelStyle_ = options.latLabelStyle !== undefined ? options.latLabelStyle : + new Text({ + font: '12px Calibri,sans-serif', + textAlign: 'end', + fill: new Fill({ + color: 'rgba(0,0,0,1)' + }), + stroke: new Stroke({ + color: 'rgba(255,255,255,1)', + width: 3 + }) + }); + + this.meridiansLabels_ = []; + this.parallelsLabels_ = []; + } + + this.setMap(options.map !== undefined ? options.map : null); } - if (map) { - this.postcomposeListenerKey_ = listen(map, RenderEventType.POSTCOMPOSE, this.handlePostCompose_, this); - map.render(); + + /** + * @param {number} lon Longitude. + * @param {number} minLat Minimal latitude. + * @param {number} maxLat Maximal latitude. + * @param {number} squaredTolerance Squared tolerance. + * @param {module:ol/extent~Extent} extent Extent. + * @param {number} index Index. + * @return {number} Index. + * @private + */ + addMeridian_(lon, minLat, maxLat, squaredTolerance, extent, index) { + const lineString = this.getMeridian_(lon, minLat, maxLat, squaredTolerance, index); + if (intersects(lineString.getExtent(), extent)) { + if (this.meridiansLabels_) { + const textPoint = this.getMeridianPoint_(lineString, extent, index); + this.meridiansLabels_[index] = { + geom: textPoint, + text: this.lonLabelFormatter_(lon) + }; + } + this.meridians_[index++] = lineString; + } + return index; } - this.map_ = map; -}; + + /** + * @param {module:ol/geom/LineString} lineString Meridian + * @param {module:ol/extent~Extent} extent Extent. + * @param {number} index Index. + * @return {module:ol/geom/Point} Meridian point. + * @private + */ + getMeridianPoint_(lineString, extent, index) { + const flatCoordinates = lineString.getFlatCoordinates(); + const clampedBottom = Math.max(extent[1], flatCoordinates[1]); + const clampedTop = Math.min(extent[3], flatCoordinates[flatCoordinates.length - 1]); + const lat = clamp( + extent[1] + Math.abs(extent[1] - extent[3]) * this.lonLabelPosition_, + clampedBottom, clampedTop); + const coordinate = [flatCoordinates[0], lat]; + let point; + if (index in this.meridiansLabels_) { + point = this.meridiansLabels_[index]; + point.setCoordinates(coordinate); + } else { + point = new Point(coordinate); + } + return point; + } + + /** + * @param {number} lat Latitude. + * @param {number} minLon Minimal longitude. + * @param {number} maxLon Maximal longitude. + * @param {number} squaredTolerance Squared tolerance. + * @param {module:ol/extent~Extent} extent Extent. + * @param {number} index Index. + * @return {number} Index. + * @private + */ + addParallel_(lat, minLon, maxLon, squaredTolerance, extent, index) { + const lineString = this.getParallel_(lat, minLon, maxLon, squaredTolerance, index); + if (intersects(lineString.getExtent(), extent)) { + if (this.parallelsLabels_) { + const textPoint = this.getParallelPoint_(lineString, extent, index); + this.parallelsLabels_[index] = { + geom: textPoint, + text: this.latLabelFormatter_(lat) + }; + } + this.parallels_[index++] = lineString; + } + return index; + } + + /** + * @param {module:ol/geom/LineString} lineString Parallels. + * @param {module:ol/extent~Extent} extent Extent. + * @param {number} index Index. + * @return {module:ol/geom/Point} Parallel point. + * @private + */ + getParallelPoint_(lineString, extent, index) { + const flatCoordinates = lineString.getFlatCoordinates(); + const clampedLeft = Math.max(extent[0], flatCoordinates[0]); + const clampedRight = Math.min(extent[2], flatCoordinates[flatCoordinates.length - 2]); + const lon = clamp( + extent[0] + Math.abs(extent[0] - extent[2]) * this.latLabelPosition_, + clampedLeft, clampedRight); + const coordinate = [lon, flatCoordinates[1]]; + let point; + if (index in this.parallelsLabels_) { + point = this.parallelsLabels_[index]; + point.setCoordinates(coordinate); + } else { + point = new Point(coordinate); + } + return point; + } + + /** + * @param {module:ol/extent~Extent} extent Extent. + * @param {module:ol/coordinate~Coordinate} center Center. + * @param {number} resolution Resolution. + * @param {number} squaredTolerance Squared tolerance. + * @private + */ + createGraticule_(extent, center, resolution, squaredTolerance) { + + const interval = this.getInterval_(resolution); + if (interval == -1) { + this.meridians_.length = this.parallels_.length = 0; + if (this.meridiansLabels_) { + this.meridiansLabels_.length = 0; + } + if (this.parallelsLabels_) { + this.parallelsLabels_.length = 0; + } + return; + } + + const centerLonLat = this.toLonLatTransform_(center); + let centerLon = centerLonLat[0]; + let centerLat = centerLonLat[1]; + const maxLines = this.maxLines_; + let cnt, idx, lat, lon; + + let validExtent = [ + Math.max(extent[0], this.minLonP_), + Math.max(extent[1], this.minLatP_), + Math.min(extent[2], this.maxLonP_), + Math.min(extent[3], this.maxLatP_) + ]; + + validExtent = transformExtent(validExtent, this.projection_, 'EPSG:4326'); + const maxLat = validExtent[3]; + const maxLon = validExtent[2]; + const minLat = validExtent[1]; + const minLon = validExtent[0]; + + // Create meridians + + centerLon = Math.floor(centerLon / interval) * interval; + lon = clamp(centerLon, this.minLon_, this.maxLon_); + + idx = this.addMeridian_(lon, minLat, maxLat, squaredTolerance, extent, 0); + + cnt = 0; + while (lon != this.minLon_ && cnt++ < maxLines) { + lon = Math.max(lon - interval, this.minLon_); + idx = this.addMeridian_(lon, minLat, maxLat, squaredTolerance, extent, idx); + } + + lon = clamp(centerLon, this.minLon_, this.maxLon_); + + cnt = 0; + while (lon != this.maxLon_ && cnt++ < maxLines) { + lon = Math.min(lon + interval, this.maxLon_); + idx = this.addMeridian_(lon, minLat, maxLat, squaredTolerance, extent, idx); + } + + this.meridians_.length = idx; + if (this.meridiansLabels_) { + this.meridiansLabels_.length = idx; + } + + // Create parallels + + centerLat = Math.floor(centerLat / interval) * interval; + lat = clamp(centerLat, this.minLat_, this.maxLat_); + + idx = this.addParallel_(lat, minLon, maxLon, squaredTolerance, extent, 0); + + cnt = 0; + while (lat != this.minLat_ && cnt++ < maxLines) { + lat = Math.max(lat - interval, this.minLat_); + idx = this.addParallel_(lat, minLon, maxLon, squaredTolerance, extent, idx); + } + + lat = clamp(centerLat, this.minLat_, this.maxLat_); + + cnt = 0; + while (lat != this.maxLat_ && cnt++ < maxLines) { + lat = Math.min(lat + interval, this.maxLat_); + idx = this.addParallel_(lat, minLon, maxLon, squaredTolerance, extent, idx); + } + + this.parallels_.length = idx; + if (this.parallelsLabels_) { + this.parallelsLabels_.length = idx; + } + + } + + /** + * @param {number} resolution Resolution. + * @return {number} The interval in degrees. + * @private + */ + getInterval_(resolution) { + const centerLon = this.projectionCenterLonLat_[0]; + const centerLat = this.projectionCenterLonLat_[1]; + let interval = -1; + const target = Math.pow(this.targetSize_ * resolution, 2); + /** @type {Array.} **/ + const p1 = []; + /** @type {Array.} **/ + const p2 = []; + for (let i = 0, ii = INTERVALS.length; i < ii; ++i) { + const delta = INTERVALS[i] / 2; + p1[0] = centerLon - delta; + p1[1] = centerLat - delta; + p2[0] = centerLon + delta; + p2[1] = centerLat + delta; + this.fromLonLatTransform_(p1, p1); + this.fromLonLatTransform_(p2, p2); + const dist = Math.pow(p2[0] - p1[0], 2) + Math.pow(p2[1] - p1[1], 2); + if (dist <= target) { + break; + } + interval = INTERVALS[i]; + } + return interval; + } + + /** + * Get the map associated with this graticule. + * @return {module:ol/PluggableMap} The map. + * @api + */ + getMap() { + return this.map_; + } + + /** + * @param {number} lon Longitude. + * @param {number} minLat Minimal latitude. + * @param {number} maxLat Maximal latitude. + * @param {number} squaredTolerance Squared tolerance. + * @return {module:ol/geom/LineString} The meridian line string. + * @param {number} index Index. + * @private + */ + getMeridian_(lon, minLat, maxLat, squaredTolerance, index) { + const flatCoordinates = meridian(lon, minLat, maxLat, this.projection_, squaredTolerance); + let lineString = this.meridians_[index]; + if (!lineString) { + lineString = this.meridians_[index] = new LineString(flatCoordinates, GeometryLayout.XY); + } else { + lineString.setFlatCoordinates(GeometryLayout.XY, flatCoordinates); + lineString.changed(); + } + return lineString; + } + + /** + * Get the list of meridians. Meridians are lines of equal longitude. + * @return {Array.} The meridians. + * @api + */ + getMeridians() { + return this.meridians_; + } + + /** + * @param {number} lat Latitude. + * @param {number} minLon Minimal longitude. + * @param {number} maxLon Maximal longitude. + * @param {number} squaredTolerance Squared tolerance. + * @return {module:ol/geom/LineString} The parallel line string. + * @param {number} index Index. + * @private + */ + getParallel_(lat, minLon, maxLon, squaredTolerance, index) { + const flatCoordinates = parallel(lat, minLon, maxLon, this.projection_, squaredTolerance); + let lineString = this.parallels_[index]; + if (!lineString) { + lineString = new LineString(flatCoordinates, GeometryLayout.XY); + } else { + lineString.setFlatCoordinates(GeometryLayout.XY, flatCoordinates); + lineString.changed(); + } + return lineString; + } + + /** + * Get the list of parallels. Parallels are lines of equal latitude. + * @return {Array.} The parallels. + * @api + */ + getParallels() { + return this.parallels_; + } + + /** + * @param {module:ol/render/Event} e Event. + * @private + */ + handlePostCompose_(e) { + const vectorContext = e.vectorContext; + const frameState = e.frameState; + const extent = frameState.extent; + const viewState = frameState.viewState; + const center = viewState.center; + const projection = viewState.projection; + const resolution = viewState.resolution; + const pixelRatio = frameState.pixelRatio; + const squaredTolerance = + resolution * resolution / (4 * pixelRatio * pixelRatio); + + const updateProjectionInfo = !this.projection_ || + !equivalentProjection(this.projection_, projection); + + if (updateProjectionInfo) { + this.updateProjectionInfo_(projection); + } + + this.createGraticule_(extent, center, resolution, squaredTolerance); + + // Draw the lines + vectorContext.setFillStrokeStyle(null, this.strokeStyle_); + let i, l, line; + for (i = 0, l = this.meridians_.length; i < l; ++i) { + line = this.meridians_[i]; + vectorContext.drawGeometry(line); + } + for (i = 0, l = this.parallels_.length; i < l; ++i) { + line = this.parallels_[i]; + vectorContext.drawGeometry(line); + } + let labelData; + if (this.meridiansLabels_) { + for (i = 0, l = this.meridiansLabels_.length; i < l; ++i) { + labelData = this.meridiansLabels_[i]; + this.lonLabelStyle_.setText(labelData.text); + vectorContext.setTextStyle(this.lonLabelStyle_); + vectorContext.drawGeometry(labelData.geom); + } + } + if (this.parallelsLabels_) { + for (i = 0, l = this.parallelsLabels_.length; i < l; ++i) { + labelData = this.parallelsLabels_[i]; + this.latLabelStyle_.setText(labelData.text); + vectorContext.setTextStyle(this.latLabelStyle_); + vectorContext.drawGeometry(labelData.geom); + } + } + } + + /** + * @param {module:ol/proj/Projection} projection Projection. + * @private + */ + updateProjectionInfo_(projection) { + const epsg4326Projection = getProjection('EPSG:4326'); + + const worldExtent = projection.getWorldExtent(); + const worldExtentP = transformExtent(worldExtent, epsg4326Projection, projection); + + this.maxLat_ = worldExtent[3]; + this.maxLon_ = worldExtent[2]; + this.minLat_ = worldExtent[1]; + this.minLon_ = worldExtent[0]; + + this.maxLatP_ = worldExtentP[3]; + this.maxLonP_ = worldExtentP[2]; + this.minLatP_ = worldExtentP[1]; + this.minLonP_ = worldExtentP[0]; + + this.fromLonLatTransform_ = getTransform(epsg4326Projection, projection); + + this.toLonLatTransform_ = getTransform(projection, epsg4326Projection); + + this.projectionCenterLonLat_ = this.toLonLatTransform_(getCenter(projection.getExtent())); + + this.projection_ = projection; + } + + /** + * Set the map for this graticule. The graticule will be rendered on the + * provided map. + * @param {module:ol/PluggableMap} map Map. + * @api + */ + setMap(map) { + if (this.map_) { + unlistenByKey(this.postcomposeListenerKey_); + this.postcomposeListenerKey_ = null; + this.map_.render(); + } + if (map) { + this.postcomposeListenerKey_ = listen(map, RenderEventType.POSTCOMPOSE, this.handlePostCompose_, this); + map.render(); + } + this.map_ = map; + } +} + export default Graticule; diff --git a/src/ol/Image.js b/src/ol/Image.js index ac9f4e8f82..30c48ca8bc 100644 --- a/src/ol/Image.js +++ b/src/ol/Image.js @@ -38,122 +38,119 @@ import {getHeight} from './extent.js'; * @param {?string} crossOrigin Cross origin. * @param {module:ol/Image~LoadFunction} imageLoadFunction Image load function. */ -const ImageWrapper = function(extent, resolution, pixelRatio, src, crossOrigin, imageLoadFunction) { +class ImageWrapper { + constructor(extent, resolution, pixelRatio, src, crossOrigin, imageLoadFunction) { - ImageBase.call(this, extent, resolution, pixelRatio, ImageState.IDLE); + ImageBase.call(this, extent, resolution, pixelRatio, ImageState.IDLE); - /** - * @private - * @type {string} - */ - this.src_ = src; + /** + * @private + * @type {string} + */ + this.src_ = src; - /** - * @private - * @type {HTMLCanvasElement|HTMLImageElement|HTMLVideoElement} - */ - this.image_ = new Image(); - if (crossOrigin !== null) { - this.image_.crossOrigin = crossOrigin; - } + /** + * @private + * @type {HTMLCanvasElement|HTMLImageElement|HTMLVideoElement} + */ + this.image_ = new Image(); + if (crossOrigin !== null) { + this.image_.crossOrigin = crossOrigin; + } - /** - * @private - * @type {Array.} - */ - this.imageListenerKeys_ = null; + /** + * @private + * @type {Array.} + */ + this.imageListenerKeys_ = null; - /** - * @protected - * @type {module:ol/ImageState} - */ - this.state = ImageState.IDLE; + /** + * @protected + * @type {module:ol/ImageState} + */ + this.state = ImageState.IDLE; - /** - * @private - * @type {module:ol/Image~LoadFunction} - */ - this.imageLoadFunction_ = imageLoadFunction; + /** + * @private + * @type {module:ol/Image~LoadFunction} + */ + this.imageLoadFunction_ = imageLoadFunction; -}; + } + + /** + * @inheritDoc + * @api + */ + getImage() { + return this.image_; + } + + /** + * Tracks loading or read errors. + * + * @private + */ + handleImageError_() { + this.state = ImageState.ERROR; + this.unlistenImage_(); + this.changed(); + } + + /** + * Tracks successful image load. + * + * @private + */ + handleImageLoad_() { + if (this.resolution === undefined) { + this.resolution = getHeight(this.extent) / this.image_.height; + } + this.state = ImageState.LOADED; + this.unlistenImage_(); + this.changed(); + } + + /** + * Load the image or retry if loading previously failed. + * Loading is taken care of by the tile queue, and calling this method is + * only needed for preloading or for reloading in case of an error. + * @override + * @api + */ + load() { + if (this.state == ImageState.IDLE || this.state == ImageState.ERROR) { + this.state = ImageState.LOADING; + this.changed(); + this.imageListenerKeys_ = [ + listenOnce(this.image_, EventType.ERROR, + this.handleImageError_, this), + listenOnce(this.image_, EventType.LOAD, + this.handleImageLoad_, this) + ]; + this.imageLoadFunction_(this, this.src_); + } + } + + /** + * @param {HTMLCanvasElement|HTMLImageElement|HTMLVideoElement} image Image. + */ + setImage(image) { + this.image_ = image; + } + + /** + * Discards event handlers which listen for load completion or errors. + * + * @private + */ + unlistenImage_() { + this.imageListenerKeys_.forEach(unlistenByKey); + this.imageListenerKeys_ = null; + } +} inherits(ImageWrapper, ImageBase); -/** - * @inheritDoc - * @api - */ -ImageWrapper.prototype.getImage = function() { - return this.image_; -}; - - -/** - * Tracks loading or read errors. - * - * @private - */ -ImageWrapper.prototype.handleImageError_ = function() { - this.state = ImageState.ERROR; - this.unlistenImage_(); - this.changed(); -}; - - -/** - * Tracks successful image load. - * - * @private - */ -ImageWrapper.prototype.handleImageLoad_ = function() { - if (this.resolution === undefined) { - this.resolution = getHeight(this.extent) / this.image_.height; - } - this.state = ImageState.LOADED; - this.unlistenImage_(); - this.changed(); -}; - - -/** - * Load the image or retry if loading previously failed. - * Loading is taken care of by the tile queue, and calling this method is - * only needed for preloading or for reloading in case of an error. - * @override - * @api - */ -ImageWrapper.prototype.load = function() { - if (this.state == ImageState.IDLE || this.state == ImageState.ERROR) { - this.state = ImageState.LOADING; - this.changed(); - this.imageListenerKeys_ = [ - listenOnce(this.image_, EventType.ERROR, - this.handleImageError_, this), - listenOnce(this.image_, EventType.LOAD, - this.handleImageLoad_, this) - ]; - this.imageLoadFunction_(this, this.src_); - } -}; - - -/** - * @param {HTMLCanvasElement|HTMLImageElement|HTMLVideoElement} image Image. - */ -ImageWrapper.prototype.setImage = function(image) { - this.image_ = image; -}; - - -/** - * Discards event handlers which listen for load completion or errors. - * - * @private - */ -ImageWrapper.prototype.unlistenImage_ = function() { - this.imageListenerKeys_.forEach(unlistenByKey); - this.imageListenerKeys_ = null; -}; - export default ImageWrapper; diff --git a/src/ol/ImageBase.js b/src/ol/ImageBase.js index 1f557906cf..bcc4a7adcc 100644 --- a/src/ol/ImageBase.js +++ b/src/ol/ImageBase.js @@ -14,90 +14,86 @@ import EventType from './events/EventType.js'; * @param {number} pixelRatio Pixel ratio. * @param {module:ol/ImageState} state State. */ -const ImageBase = function(extent, resolution, pixelRatio, state) { +class ImageBase { + constructor(extent, resolution, pixelRatio, state) { - EventTarget.call(this); + EventTarget.call(this); - /** - * @protected - * @type {module:ol/extent~Extent} - */ - this.extent = extent; + /** + * @protected + * @type {module:ol/extent~Extent} + */ + this.extent = extent; - /** - * @private - * @type {number} - */ - this.pixelRatio_ = pixelRatio; + /** + * @private + * @type {number} + */ + this.pixelRatio_ = pixelRatio; - /** - * @protected - * @type {number|undefined} - */ - this.resolution = resolution; + /** + * @protected + * @type {number|undefined} + */ + this.resolution = resolution; - /** - * @protected - * @type {module:ol/ImageState} - */ - this.state = state; + /** + * @protected + * @type {module:ol/ImageState} + */ + this.state = state; -}; + } + + /** + * @protected + */ + changed() { + this.dispatchEvent(EventType.CHANGE); + } + + /** + * @return {module:ol/extent~Extent} Extent. + */ + getExtent() { + return this.extent; + } + + /** + * @abstract + * @return {HTMLCanvasElement|HTMLImageElement|HTMLVideoElement} Image. + */ + getImage() {} + + /** + * @return {number} PixelRatio. + */ + getPixelRatio() { + return this.pixelRatio_; + } + + /** + * @return {number} Resolution. + */ + getResolution() { + return /** @type {number} */ (this.resolution); + } + + /** + * @return {module:ol/ImageState} State. + */ + getState() { + return this.state; + } + + /** + * Load not yet loaded URI. + * @abstract + */ + load() {} +} inherits(ImageBase, EventTarget); -/** - * @protected - */ -ImageBase.prototype.changed = function() { - this.dispatchEvent(EventType.CHANGE); -}; - - -/** - * @return {module:ol/extent~Extent} Extent. - */ -ImageBase.prototype.getExtent = function() { - return this.extent; -}; - - -/** - * @abstract - * @return {HTMLCanvasElement|HTMLImageElement|HTMLVideoElement} Image. - */ -ImageBase.prototype.getImage = function() {}; - - -/** - * @return {number} PixelRatio. - */ -ImageBase.prototype.getPixelRatio = function() { - return this.pixelRatio_; -}; - - -/** - * @return {number} Resolution. - */ -ImageBase.prototype.getResolution = function() { - return /** @type {number} */ (this.resolution); -}; - - -/** - * @return {module:ol/ImageState} State. - */ -ImageBase.prototype.getState = function() { - return this.state; -}; - - -/** - * Load not yet loaded URI. - * @abstract - */ -ImageBase.prototype.load = function() {}; - export default ImageBase; diff --git a/src/ol/ImageCanvas.js b/src/ol/ImageCanvas.js index 524d1fa754..989503d928 100644 --- a/src/ol/ImageCanvas.js +++ b/src/ol/ImageCanvas.js @@ -26,77 +26,77 @@ import ImageState from './ImageState.js'; * @param {module:ol/ImageCanvas~Loader=} opt_loader Optional loader function to * support asynchronous canvas drawing. */ -const ImageCanvas = function(extent, resolution, pixelRatio, canvas, opt_loader) { +class ImageCanvas { + constructor(extent, resolution, pixelRatio, canvas, opt_loader) { - /** - * Optional canvas loader function. - * @type {?module:ol/ImageCanvas~Loader} - * @private - */ - this.loader_ = opt_loader !== undefined ? opt_loader : null; + /** + * Optional canvas loader function. + * @type {?module:ol/ImageCanvas~Loader} + * @private + */ + this.loader_ = opt_loader !== undefined ? opt_loader : null; - const state = opt_loader !== undefined ? ImageState.IDLE : ImageState.LOADED; + const state = opt_loader !== undefined ? ImageState.IDLE : ImageState.LOADED; - ImageBase.call(this, extent, resolution, pixelRatio, state); + ImageBase.call(this, extent, resolution, pixelRatio, state); - /** - * @private - * @type {HTMLCanvasElement} - */ - this.canvas_ = canvas; + /** + * @private + * @type {HTMLCanvasElement} + */ + this.canvas_ = canvas; - /** - * @private - * @type {Error} - */ - this.error_ = null; + /** + * @private + * @type {Error} + */ + this.error_ = null; -}; + } + + /** + * Get any error associated with asynchronous rendering. + * @return {Error} Any error that occurred during rendering. + */ + getError() { + return this.error_; + } + + /** + * Handle async drawing complete. + * @param {Error} err Any error during drawing. + * @private + */ + handleLoad_(err) { + if (err) { + this.error_ = err; + this.state = ImageState.ERROR; + } else { + this.state = ImageState.LOADED; + } + this.changed(); + } + + /** + * @inheritDoc + */ + load() { + if (this.state == ImageState.IDLE) { + this.state = ImageState.LOADING; + this.changed(); + this.loader_(this.handleLoad_.bind(this)); + } + } + + /** + * @return {HTMLCanvasElement} Canvas element. + */ + getImage() { + return this.canvas_; + } +} inherits(ImageCanvas, ImageBase); -/** - * Get any error associated with asynchronous rendering. - * @return {Error} Any error that occurred during rendering. - */ -ImageCanvas.prototype.getError = function() { - return this.error_; -}; - - -/** - * Handle async drawing complete. - * @param {Error} err Any error during drawing. - * @private - */ -ImageCanvas.prototype.handleLoad_ = function(err) { - if (err) { - this.error_ = err; - this.state = ImageState.ERROR; - } else { - this.state = ImageState.LOADED; - } - this.changed(); -}; - - -/** - * @inheritDoc - */ -ImageCanvas.prototype.load = function() { - if (this.state == ImageState.IDLE) { - this.state = ImageState.LOADING; - this.changed(); - this.loader_(this.handleLoad_.bind(this)); - } -}; - - -/** - * @return {HTMLCanvasElement} Canvas element. - */ -ImageCanvas.prototype.getImage = function() { - return this.canvas_; -}; export default ImageCanvas; diff --git a/src/ol/ImageTile.js b/src/ol/ImageTile.js index 2e9748c87d..7ac535885a 100644 --- a/src/ol/ImageTile.js +++ b/src/ol/ImageTile.js @@ -24,149 +24,144 @@ import EventType from './events/EventType.js'; * @param {module:ol/Tile~LoadFunction} tileLoadFunction Tile load function. * @param {module:ol/Tile~Options=} opt_options Tile options. */ -const ImageTile = function(tileCoord, state, src, crossOrigin, tileLoadFunction, opt_options) { +class ImageTile { + constructor(tileCoord, state, src, crossOrigin, tileLoadFunction, opt_options) { - Tile.call(this, tileCoord, state, opt_options); + Tile.call(this, tileCoord, state, opt_options); + + /** + * @private + * @type {?string} + */ + this.crossOrigin_ = crossOrigin; + + /** + * Image URI + * + * @private + * @type {string} + */ + this.src_ = src; + + /** + * @private + * @type {HTMLImageElement|HTMLCanvasElement} + */ + this.image_ = new Image(); + if (crossOrigin !== null) { + this.image_.crossOrigin = crossOrigin; + } + + /** + * @private + * @type {Array.} + */ + this.imageListenerKeys_ = null; + + /** + * @private + * @type {module:ol/Tile~LoadFunction} + */ + this.tileLoadFunction_ = tileLoadFunction; + + } /** - * @private - * @type {?string} + * @inheritDoc */ - this.crossOrigin_ = crossOrigin; + disposeInternal() { + if (this.state == TileState.LOADING) { + this.unlistenImage_(); + this.image_ = getBlankImage(); + } + if (this.interimTile) { + this.interimTile.dispose(); + } + this.state = TileState.ABORT; + this.changed(); + Tile.prototype.disposeInternal.call(this); + } /** - * Image URI + * Get the HTML image element for this tile (may be a Canvas, Image, or Video). + * @return {HTMLCanvasElement|HTMLImageElement|HTMLVideoElement} Image. + * @api + */ + getImage() { + return this.image_; + } + + /** + * @inheritDoc + */ + getKey() { + return this.src_; + } + + /** + * Tracks loading or read errors. * * @private - * @type {string} */ - this.src_ = src; - - /** - * @private - * @type {HTMLImageElement|HTMLCanvasElement} - */ - this.image_ = new Image(); - if (crossOrigin !== null) { - this.image_.crossOrigin = crossOrigin; - } - - /** - * @private - * @type {Array.} - */ - this.imageListenerKeys_ = null; - - /** - * @private - * @type {module:ol/Tile~LoadFunction} - */ - this.tileLoadFunction_ = tileLoadFunction; - -}; - -inherits(ImageTile, Tile); - - -/** - * @inheritDoc - */ -ImageTile.prototype.disposeInternal = function() { - if (this.state == TileState.LOADING) { + handleImageError_() { + this.state = TileState.ERROR; this.unlistenImage_(); this.image_ = getBlankImage(); + this.changed(); } - if (this.interimTile) { - this.interimTile.dispose(); + + /** + * Tracks successful image load. + * + * @private + */ + handleImageLoad_() { + if (this.image_.naturalWidth && this.image_.naturalHeight) { + this.state = TileState.LOADED; + } else { + this.state = TileState.EMPTY; + } + this.unlistenImage_(); + this.changed(); } - this.state = TileState.ABORT; - this.changed(); - Tile.prototype.disposeInternal.call(this); -}; - -/** - * Get the HTML image element for this tile (may be a Canvas, Image, or Video). - * @return {HTMLCanvasElement|HTMLImageElement|HTMLVideoElement} Image. - * @api - */ -ImageTile.prototype.getImage = function() { - return this.image_; -}; - - -/** - * @inheritDoc - */ -ImageTile.prototype.getKey = function() { - return this.src_; -}; - - -/** - * Tracks loading or read errors. - * - * @private - */ -ImageTile.prototype.handleImageError_ = function() { - this.state = TileState.ERROR; - this.unlistenImage_(); - this.image_ = getBlankImage(); - this.changed(); -}; - - -/** - * Tracks successful image load. - * - * @private - */ -ImageTile.prototype.handleImageLoad_ = function() { - if (this.image_.naturalWidth && this.image_.naturalHeight) { - this.state = TileState.LOADED; - } else { - this.state = TileState.EMPTY; - } - this.unlistenImage_(); - this.changed(); -}; - - -/** - * @inheritDoc - * @api - */ -ImageTile.prototype.load = function() { - if (this.state == TileState.ERROR) { - this.state = TileState.IDLE; - this.image_ = new Image(); - if (this.crossOrigin_ !== null) { - this.image_.crossOrigin = this.crossOrigin_; + /** + * @inheritDoc + * @api + */ + load() { + if (this.state == TileState.ERROR) { + this.state = TileState.IDLE; + this.image_ = new Image(); + if (this.crossOrigin_ !== null) { + this.image_.crossOrigin = this.crossOrigin_; + } + } + if (this.state == TileState.IDLE) { + this.state = TileState.LOADING; + this.changed(); + this.imageListenerKeys_ = [ + listenOnce(this.image_, EventType.ERROR, + this.handleImageError_, this), + listenOnce(this.image_, EventType.LOAD, + this.handleImageLoad_, this) + ]; + this.tileLoadFunction_(this, this.src_); } } - if (this.state == TileState.IDLE) { - this.state = TileState.LOADING; - this.changed(); - this.imageListenerKeys_ = [ - listenOnce(this.image_, EventType.ERROR, - this.handleImageError_, this), - listenOnce(this.image_, EventType.LOAD, - this.handleImageLoad_, this) - ]; - this.tileLoadFunction_(this, this.src_); + + /** + * Discards event handlers which listen for load completion or errors. + * + * @private + */ + unlistenImage_() { + this.imageListenerKeys_.forEach(unlistenByKey); + this.imageListenerKeys_ = null; } -}; +} - -/** - * Discards event handlers which listen for load completion or errors. - * - * @private - */ -ImageTile.prototype.unlistenImage_ = function() { - this.imageListenerKeys_.forEach(unlistenByKey); - this.imageListenerKeys_ = null; -}; +inherits(ImageTile, Tile); /** diff --git a/src/ol/Kinetic.js b/src/ol/Kinetic.js index 8449e1b690..4b6a5e470c 100644 --- a/src/ol/Kinetic.js +++ b/src/ol/Kinetic.js @@ -14,116 +14,114 @@ * @struct * @api */ -const Kinetic = function(decay, minVelocity, delay) { +class Kinetic { + constructor(decay, minVelocity, delay) { - /** - * @private - * @type {number} - */ - this.decay_ = decay; + /** + * @private + * @type {number} + */ + this.decay_ = decay; - /** - * @private - * @type {number} - */ - this.minVelocity_ = minVelocity; + /** + * @private + * @type {number} + */ + this.minVelocity_ = minVelocity; - /** - * @private - * @type {number} - */ - this.delay_ = delay; + /** + * @private + * @type {number} + */ + this.delay_ = delay; - /** - * @private - * @type {Array.} - */ - this.points_ = []; + /** + * @private + * @type {Array.} + */ + this.points_ = []; - /** - * @private - * @type {number} - */ - this.angle_ = 0; + /** + * @private + * @type {number} + */ + this.angle_ = 0; - /** - * @private - * @type {number} - */ - this.initialVelocity_ = 0; -}; + /** + * @private + * @type {number} + */ + this.initialVelocity_ = 0; + } + /** + * FIXME empty description for jsdoc + */ + begin() { + this.points_.length = 0; + this.angle_ = 0; + this.initialVelocity_ = 0; + } -/** - * FIXME empty description for jsdoc - */ -Kinetic.prototype.begin = function() { - this.points_.length = 0; - this.angle_ = 0; - this.initialVelocity_ = 0; -}; + /** + * @param {number} x X. + * @param {number} y Y. + */ + update(x, y) { + this.points_.push(x, y, Date.now()); + } + /** + * @return {boolean} Whether we should do kinetic animation. + */ + end() { + if (this.points_.length < 6) { + // at least 2 points are required (i.e. there must be at least 6 elements + // in the array) + return false; + } + const delay = Date.now() - this.delay_; + const lastIndex = this.points_.length - 3; + if (this.points_[lastIndex + 2] < delay) { + // the last tracked point is too old, which means that the user stopped + // panning before releasing the map + return false; + } -/** - * @param {number} x X. - * @param {number} y Y. - */ -Kinetic.prototype.update = function(x, y) { - this.points_.push(x, y, Date.now()); -}; + // get the first point which still falls into the delay time + let firstIndex = lastIndex - 3; + while (firstIndex > 0 && this.points_[firstIndex + 2] > delay) { + firstIndex -= 3; + } + const duration = this.points_[lastIndex + 2] - this.points_[firstIndex + 2]; + // we don't want a duration of 0 (divide by zero) + // we also make sure the user panned for a duration of at least one frame + // (1/60s) to compute sane displacement values + if (duration < 1000 / 60) { + return false; + } -/** - * @return {boolean} Whether we should do kinetic animation. - */ -Kinetic.prototype.end = function() { - if (this.points_.length < 6) { - // at least 2 points are required (i.e. there must be at least 6 elements - // in the array) - return false; - } - const delay = Date.now() - this.delay_; - const lastIndex = this.points_.length - 3; - if (this.points_[lastIndex + 2] < delay) { - // the last tracked point is too old, which means that the user stopped - // panning before releasing the map - return false; - } + const dx = this.points_[lastIndex] - this.points_[firstIndex]; + const dy = this.points_[lastIndex + 1] - this.points_[firstIndex + 1]; + this.angle_ = Math.atan2(dy, dx); + this.initialVelocity_ = Math.sqrt(dx * dx + dy * dy) / duration; + return this.initialVelocity_ > this.minVelocity_; + } - // get the first point which still falls into the delay time - let firstIndex = lastIndex - 3; - while (firstIndex > 0 && this.points_[firstIndex + 2] > delay) { - firstIndex -= 3; - } + /** + * @return {number} Total distance travelled (pixels). + */ + getDistance() { + return (this.minVelocity_ - this.initialVelocity_) / this.decay_; + } - const duration = this.points_[lastIndex + 2] - this.points_[firstIndex + 2]; - // we don't want a duration of 0 (divide by zero) - // we also make sure the user panned for a duration of at least one frame - // (1/60s) to compute sane displacement values - if (duration < 1000 / 60) { - return false; - } + /** + * @return {number} Angle of the kinetic panning animation (radians). + */ + getAngle() { + return this.angle_; + } +} - const dx = this.points_[lastIndex] - this.points_[firstIndex]; - const dy = this.points_[lastIndex + 1] - this.points_[firstIndex + 1]; - this.angle_ = Math.atan2(dy, dx); - this.initialVelocity_ = Math.sqrt(dx * dx + dy * dy) / duration; - return this.initialVelocity_ > this.minVelocity_; -}; - - -/** - * @return {number} Total distance travelled (pixels). - */ -Kinetic.prototype.getDistance = function() { - return (this.minVelocity_ - this.initialVelocity_) / this.decay_; -}; - - -/** - * @return {number} Angle of the kinetic panning animation (radians). - */ -Kinetic.prototype.getAngle = function() { - return this.angle_; -}; export default Kinetic; diff --git a/src/ol/Map.js b/src/ol/Map.js index 3fb24d0871..23bb531540 100644 --- a/src/ol/Map.js +++ b/src/ol/Map.js @@ -66,29 +66,31 @@ import CanvasVectorTileLayerRenderer from './renderer/canvas/VectorTileLayer.js' * @fires module:ol/render/Event~RenderEvent#precompose * @api */ -const Map = function(options) { - options = assign({}, options); - if (!options.controls) { - options.controls = defaultControls(); - } - if (!options.interactions) { - options.interactions = defaultInteractions(); +class Map { + constructor(options) { + options = assign({}, options); + if (!options.controls) { + options.controls = defaultControls(); + } + if (!options.interactions) { + options.interactions = defaultInteractions(); + } + + PluggableMap.call(this, options); } - PluggableMap.call(this, options); -}; + createRenderer() { + const renderer = new CanvasMapRenderer(this); + renderer.registerLayerRenderers([ + CanvasImageLayerRenderer, + CanvasTileLayerRenderer, + CanvasVectorLayerRenderer, + CanvasVectorTileLayerRenderer + ]); + return renderer; + } +} inherits(Map, PluggableMap); -Map.prototype.createRenderer = function() { - const renderer = new CanvasMapRenderer(this); - renderer.registerLayerRenderers([ - CanvasImageLayerRenderer, - CanvasTileLayerRenderer, - CanvasVectorLayerRenderer, - CanvasVectorTileLayerRenderer - ]); - return renderer; -}; - export default Map; diff --git a/src/ol/MapBrowserEvent.js b/src/ol/MapBrowserEvent.js index f326ceec64..3043c4e7d5 100644 --- a/src/ol/MapBrowserEvent.js +++ b/src/ol/MapBrowserEvent.js @@ -17,66 +17,68 @@ import MapEvent from './MapEvent.js'; * @param {boolean=} opt_dragging Is the map currently being dragged? * @param {?module:ol/PluggableMap~FrameState=} opt_frameState Frame state. */ -const MapBrowserEvent = function(type, map, browserEvent, opt_dragging, opt_frameState) { +class MapBrowserEvent { + constructor(type, map, browserEvent, opt_dragging, opt_frameState) { - MapEvent.call(this, type, map, opt_frameState); + MapEvent.call(this, type, map, opt_frameState); - /** - * The original browser event. - * @const - * @type {Event} - * @api - */ - this.originalEvent = browserEvent; + /** + * The original browser event. + * @const + * @type {Event} + * @api + */ + this.originalEvent = browserEvent; - /** - * The map pixel relative to the viewport corresponding to the original browser event. - * @type {module:ol~Pixel} - * @api - */ - this.pixel = map.getEventPixel(browserEvent); + /** + * The map pixel relative to the viewport corresponding to the original browser event. + * @type {module:ol~Pixel} + * @api + */ + this.pixel = map.getEventPixel(browserEvent); - /** - * The coordinate in view projection corresponding to the original browser event. - * @type {module:ol/coordinate~Coordinate} - * @api - */ - this.coordinate = map.getCoordinateFromPixel(this.pixel); + /** + * The coordinate in view projection corresponding to the original browser event. + * @type {module:ol/coordinate~Coordinate} + * @api + */ + this.coordinate = map.getCoordinateFromPixel(this.pixel); - /** - * Indicates if the map is currently being dragged. Only set for - * `POINTERDRAG` and `POINTERMOVE` events. Default is `false`. - * - * @type {boolean} - * @api - */ - this.dragging = opt_dragging !== undefined ? opt_dragging : false; + /** + * Indicates if the map is currently being dragged. Only set for + * `POINTERDRAG` and `POINTERMOVE` events. Default is `false`. + * + * @type {boolean} + * @api + */ + this.dragging = opt_dragging !== undefined ? opt_dragging : false; -}; + } + + /** + * Prevents the default browser action. + * @see https://developer.mozilla.org/en-US/docs/Web/API/event.preventDefault + * @override + * @api + */ + preventDefault() { + MapEvent.prototype.preventDefault.call(this); + this.originalEvent.preventDefault(); + } + + /** + * Prevents further propagation of the current event. + * @see https://developer.mozilla.org/en-US/docs/Web/API/event.stopPropagation + * @override + * @api + */ + stopPropagation() { + MapEvent.prototype.stopPropagation.call(this); + this.originalEvent.stopPropagation(); + } +} inherits(MapBrowserEvent, MapEvent); -/** - * Prevents the default browser action. - * @see https://developer.mozilla.org/en-US/docs/Web/API/event.preventDefault - * @override - * @api - */ -MapBrowserEvent.prototype.preventDefault = function() { - MapEvent.prototype.preventDefault.call(this); - this.originalEvent.preventDefault(); -}; - - -/** - * Prevents further propagation of the current event. - * @see https://developer.mozilla.org/en-US/docs/Web/API/event.stopPropagation - * @override - * @api - */ -MapBrowserEvent.prototype.stopPropagation = function() { - MapEvent.prototype.stopPropagation.call(this); - this.originalEvent.stopPropagation(); -}; export default MapBrowserEvent; diff --git a/src/ol/MapBrowserEventHandler.js b/src/ol/MapBrowserEventHandler.js index 6718d1e556..48dc9109a2 100644 --- a/src/ol/MapBrowserEventHandler.js +++ b/src/ol/MapBrowserEventHandler.js @@ -18,319 +18,314 @@ import PointerEventHandler from './pointer/PointerEventHandler.js'; * @constructor * @extends {module:ol/events/EventTarget} */ -const MapBrowserEventHandler = function(map, moveTolerance) { +class MapBrowserEventHandler { + constructor(map, moveTolerance) { - EventTarget.call(this); + EventTarget.call(this); + + /** + * This is the element that we will listen to the real events on. + * @type {module:ol/PluggableMap} + * @private + */ + this.map_ = map; + + /** + * @type {number} + * @private + */ + this.clickTimeoutId_ = 0; + + /** + * @type {boolean} + * @private + */ + this.dragging_ = false; + + /** + * @type {!Array.} + * @private + */ + this.dragListenerKeys_ = []; + + /** + * @type {number} + * @private + */ + this.moveTolerance_ = moveTolerance ? + moveTolerance * DEVICE_PIXEL_RATIO : DEVICE_PIXEL_RATIO; + + /** + * The most recent "down" type event (or null if none have occurred). + * Set on pointerdown. + * @type {module:ol/pointer/PointerEvent} + * @private + */ + this.down_ = null; + + const element = this.map_.getViewport(); + + /** + * @type {number} + * @private + */ + this.activePointers_ = 0; + + /** + * @type {!Object.} + * @private + */ + this.trackedTouches_ = {}; + + /** + * Event handler which generates pointer events for + * the viewport element. + * + * @type {module:ol/pointer/PointerEventHandler} + * @private + */ + this.pointerEventHandler_ = new PointerEventHandler(element); + + /** + * Event handler which generates pointer events for + * the document (used when dragging). + * + * @type {module:ol/pointer/PointerEventHandler} + * @private + */ + this.documentPointerEventHandler_ = null; + + /** + * @type {?module:ol/events~EventsKey} + * @private + */ + this.pointerdownListenerKey_ = listen(this.pointerEventHandler_, + PointerEventType.POINTERDOWN, + this.handlePointerDown_, this); + + /** + * @type {?module:ol/events~EventsKey} + * @private + */ + this.relayedListenerKey_ = listen(this.pointerEventHandler_, + PointerEventType.POINTERMOVE, + this.relayEvent_, this); + + } /** - * This is the element that we will listen to the real events on. - * @type {module:ol/PluggableMap} + * @param {module:ol/pointer/PointerEvent} pointerEvent Pointer + * event. * @private */ - this.map_ = map; + emulateClick_(pointerEvent) { + let newEvent = new MapBrowserPointerEvent( + MapBrowserEventType.CLICK, this.map_, pointerEvent); + this.dispatchEvent(newEvent); + if (this.clickTimeoutId_ !== 0) { + // double-click + clearTimeout(this.clickTimeoutId_); + this.clickTimeoutId_ = 0; + newEvent = new MapBrowserPointerEvent( + MapBrowserEventType.DBLCLICK, this.map_, pointerEvent); + this.dispatchEvent(newEvent); + } else { + // click + this.clickTimeoutId_ = setTimeout(function() { + this.clickTimeoutId_ = 0; + const newEvent = new MapBrowserPointerEvent( + MapBrowserEventType.SINGLECLICK, this.map_, pointerEvent); + this.dispatchEvent(newEvent); + }.bind(this), 250); + } + } /** - * @type {number} - * @private - */ - this.clickTimeoutId_ = 0; - - /** - * @type {boolean} - * @private - */ - this.dragging_ = false; - - /** - * @type {!Array.} - * @private - */ - this.dragListenerKeys_ = []; - - /** - * @type {number} - * @private - */ - this.moveTolerance_ = moveTolerance ? - moveTolerance * DEVICE_PIXEL_RATIO : DEVICE_PIXEL_RATIO; - - /** - * The most recent "down" type event (or null if none have occurred). - * Set on pointerdown. - * @type {module:ol/pointer/PointerEvent} - * @private - */ - this.down_ = null; - - const element = this.map_.getViewport(); - - /** - * @type {number} - * @private - */ - this.activePointers_ = 0; - - /** - * @type {!Object.} - * @private - */ - this.trackedTouches_ = {}; - - /** - * Event handler which generates pointer events for - * the viewport element. + * Keeps track on how many pointers are currently active. * - * @type {module:ol/pointer/PointerEventHandler} + * @param {module:ol/pointer/PointerEvent} pointerEvent Pointer + * event. * @private */ - this.pointerEventHandler_ = new PointerEventHandler(element); + updateActivePointers_(pointerEvent) { + const event = pointerEvent; + + if (event.type == MapBrowserEventType.POINTERUP || + event.type == MapBrowserEventType.POINTERCANCEL) { + delete this.trackedTouches_[event.pointerId]; + } else if (event.type == MapBrowserEventType.POINTERDOWN) { + this.trackedTouches_[event.pointerId] = true; + } + this.activePointers_ = Object.keys(this.trackedTouches_).length; + } /** - * Event handler which generates pointer events for - * the document (used when dragging). - * - * @type {module:ol/pointer/PointerEventHandler} + * @param {module:ol/pointer/PointerEvent} pointerEvent Pointer + * event. * @private */ - this.documentPointerEventHandler_ = null; + handlePointerUp_(pointerEvent) { + this.updateActivePointers_(pointerEvent); + const newEvent = new MapBrowserPointerEvent( + MapBrowserEventType.POINTERUP, this.map_, pointerEvent); + this.dispatchEvent(newEvent); + + // We emulate click events on left mouse button click, touch contact, and pen + // contact. isMouseActionButton returns true in these cases (evt.button is set + // to 0). + // See http://www.w3.org/TR/pointerevents/#button-states + // We only fire click, singleclick, and doubleclick if nobody has called + // event.stopPropagation() or event.preventDefault(). + if (!newEvent.propagationStopped && !this.dragging_ && this.isMouseActionButton_(pointerEvent)) { + this.emulateClick_(this.down_); + } + + if (this.activePointers_ === 0) { + this.dragListenerKeys_.forEach(unlistenByKey); + this.dragListenerKeys_.length = 0; + this.dragging_ = false; + this.down_ = null; + this.documentPointerEventHandler_.dispose(); + this.documentPointerEventHandler_ = null; + } + } /** - * @type {?module:ol/events~EventsKey} + * @param {module:ol/pointer/PointerEvent} pointerEvent Pointer + * event. + * @return {boolean} If the left mouse button was pressed. * @private */ - this.pointerdownListenerKey_ = listen(this.pointerEventHandler_, - PointerEventType.POINTERDOWN, - this.handlePointerDown_, this); + isMouseActionButton_(pointerEvent) { + return pointerEvent.button === 0; + } /** - * @type {?module:ol/events~EventsKey} + * @param {module:ol/pointer/PointerEvent} pointerEvent Pointer + * event. * @private */ - this.relayedListenerKey_ = listen(this.pointerEventHandler_, - PointerEventType.POINTERMOVE, - this.relayEvent_, this); + handlePointerDown_(pointerEvent) { + this.updateActivePointers_(pointerEvent); + const newEvent = new MapBrowserPointerEvent( + MapBrowserEventType.POINTERDOWN, this.map_, pointerEvent); + this.dispatchEvent(newEvent); -}; + this.down_ = pointerEvent; + + if (this.dragListenerKeys_.length === 0) { + /* Set up a pointer event handler on the `document`, + * which is required when the pointer is moved outside + * the viewport when dragging. + */ + this.documentPointerEventHandler_ = + new PointerEventHandler(document); + + this.dragListenerKeys_.push( + listen(this.documentPointerEventHandler_, + MapBrowserEventType.POINTERMOVE, + this.handlePointerMove_, this), + listen(this.documentPointerEventHandler_, + MapBrowserEventType.POINTERUP, + this.handlePointerUp_, this), + /* Note that the listener for `pointercancel is set up on + * `pointerEventHandler_` and not `documentPointerEventHandler_` like + * the `pointerup` and `pointermove` listeners. + * + * The reason for this is the following: `TouchSource.vacuumTouches_()` + * issues `pointercancel` events, when there was no `touchend` for a + * `touchstart`. Now, let's say a first `touchstart` is registered on + * `pointerEventHandler_`. The `documentPointerEventHandler_` is set up. + * But `documentPointerEventHandler_` doesn't know about the first + * `touchstart`. If there is no `touchend` for the `touchstart`, we can + * only receive a `touchcancel` from `pointerEventHandler_`, because it is + * only registered there. + */ + listen(this.pointerEventHandler_, + MapBrowserEventType.POINTERCANCEL, + this.handlePointerUp_, this) + ); + } + } + + /** + * @param {module:ol/pointer/PointerEvent} pointerEvent Pointer + * event. + * @private + */ + handlePointerMove_(pointerEvent) { + // Between pointerdown and pointerup, pointermove events are triggered. + // To avoid a 'false' touchmove event to be dispatched, we test if the pointer + // moved a significant distance. + if (this.isMoving_(pointerEvent)) { + this.dragging_ = true; + const newEvent = new MapBrowserPointerEvent( + MapBrowserEventType.POINTERDRAG, this.map_, pointerEvent, + this.dragging_); + this.dispatchEvent(newEvent); + } + + // Some native android browser triggers mousemove events during small period + // of time. See: https://code.google.com/p/android/issues/detail?id=5491 or + // https://code.google.com/p/android/issues/detail?id=19827 + // ex: Galaxy Tab P3110 + Android 4.1.1 + pointerEvent.preventDefault(); + } + + /** + * Wrap and relay a pointer event. Note that this requires that the type + * string for the MapBrowserPointerEvent matches the PointerEvent type. + * @param {module:ol/pointer/PointerEvent} pointerEvent Pointer + * event. + * @private + */ + relayEvent_(pointerEvent) { + const dragging = !!(this.down_ && this.isMoving_(pointerEvent)); + this.dispatchEvent(new MapBrowserPointerEvent( + pointerEvent.type, this.map_, pointerEvent, dragging)); + } + + /** + * @param {module:ol/pointer/PointerEvent} pointerEvent Pointer + * event. + * @return {boolean} Is moving. + * @private + */ + isMoving_(pointerEvent) { + return this.dragging_ || + Math.abs(pointerEvent.clientX - this.down_.clientX) > this.moveTolerance_ || + Math.abs(pointerEvent.clientY - this.down_.clientY) > this.moveTolerance_; + } + + /** + * @inheritDoc + */ + disposeInternal() { + if (this.relayedListenerKey_) { + unlistenByKey(this.relayedListenerKey_); + this.relayedListenerKey_ = null; + } + if (this.pointerdownListenerKey_) { + unlistenByKey(this.pointerdownListenerKey_); + this.pointerdownListenerKey_ = null; + } + + this.dragListenerKeys_.forEach(unlistenByKey); + this.dragListenerKeys_.length = 0; + + if (this.documentPointerEventHandler_) { + this.documentPointerEventHandler_.dispose(); + this.documentPointerEventHandler_ = null; + } + if (this.pointerEventHandler_) { + this.pointerEventHandler_.dispose(); + this.pointerEventHandler_ = null; + } + EventTarget.prototype.disposeInternal.call(this); + } +} inherits(MapBrowserEventHandler, EventTarget); -/** - * @param {module:ol/pointer/PointerEvent} pointerEvent Pointer - * event. - * @private - */ -MapBrowserEventHandler.prototype.emulateClick_ = function(pointerEvent) { - let newEvent = new MapBrowserPointerEvent( - MapBrowserEventType.CLICK, this.map_, pointerEvent); - this.dispatchEvent(newEvent); - if (this.clickTimeoutId_ !== 0) { - // double-click - clearTimeout(this.clickTimeoutId_); - this.clickTimeoutId_ = 0; - newEvent = new MapBrowserPointerEvent( - MapBrowserEventType.DBLCLICK, this.map_, pointerEvent); - this.dispatchEvent(newEvent); - } else { - // click - this.clickTimeoutId_ = setTimeout(function() { - this.clickTimeoutId_ = 0; - const newEvent = new MapBrowserPointerEvent( - MapBrowserEventType.SINGLECLICK, this.map_, pointerEvent); - this.dispatchEvent(newEvent); - }.bind(this), 250); - } -}; - - -/** - * Keeps track on how many pointers are currently active. - * - * @param {module:ol/pointer/PointerEvent} pointerEvent Pointer - * event. - * @private - */ -MapBrowserEventHandler.prototype.updateActivePointers_ = function(pointerEvent) { - const event = pointerEvent; - - if (event.type == MapBrowserEventType.POINTERUP || - event.type == MapBrowserEventType.POINTERCANCEL) { - delete this.trackedTouches_[event.pointerId]; - } else if (event.type == MapBrowserEventType.POINTERDOWN) { - this.trackedTouches_[event.pointerId] = true; - } - this.activePointers_ = Object.keys(this.trackedTouches_).length; -}; - - -/** - * @param {module:ol/pointer/PointerEvent} pointerEvent Pointer - * event. - * @private - */ -MapBrowserEventHandler.prototype.handlePointerUp_ = function(pointerEvent) { - this.updateActivePointers_(pointerEvent); - const newEvent = new MapBrowserPointerEvent( - MapBrowserEventType.POINTERUP, this.map_, pointerEvent); - this.dispatchEvent(newEvent); - - // We emulate click events on left mouse button click, touch contact, and pen - // contact. isMouseActionButton returns true in these cases (evt.button is set - // to 0). - // See http://www.w3.org/TR/pointerevents/#button-states - // We only fire click, singleclick, and doubleclick if nobody has called - // event.stopPropagation() or event.preventDefault(). - if (!newEvent.propagationStopped && !this.dragging_ && this.isMouseActionButton_(pointerEvent)) { - this.emulateClick_(this.down_); - } - - if (this.activePointers_ === 0) { - this.dragListenerKeys_.forEach(unlistenByKey); - this.dragListenerKeys_.length = 0; - this.dragging_ = false; - this.down_ = null; - this.documentPointerEventHandler_.dispose(); - this.documentPointerEventHandler_ = null; - } -}; - - -/** - * @param {module:ol/pointer/PointerEvent} pointerEvent Pointer - * event. - * @return {boolean} If the left mouse button was pressed. - * @private - */ -MapBrowserEventHandler.prototype.isMouseActionButton_ = function(pointerEvent) { - return pointerEvent.button === 0; -}; - - -/** - * @param {module:ol/pointer/PointerEvent} pointerEvent Pointer - * event. - * @private - */ -MapBrowserEventHandler.prototype.handlePointerDown_ = function(pointerEvent) { - this.updateActivePointers_(pointerEvent); - const newEvent = new MapBrowserPointerEvent( - MapBrowserEventType.POINTERDOWN, this.map_, pointerEvent); - this.dispatchEvent(newEvent); - - this.down_ = pointerEvent; - - if (this.dragListenerKeys_.length === 0) { - /* Set up a pointer event handler on the `document`, - * which is required when the pointer is moved outside - * the viewport when dragging. - */ - this.documentPointerEventHandler_ = - new PointerEventHandler(document); - - this.dragListenerKeys_.push( - listen(this.documentPointerEventHandler_, - MapBrowserEventType.POINTERMOVE, - this.handlePointerMove_, this), - listen(this.documentPointerEventHandler_, - MapBrowserEventType.POINTERUP, - this.handlePointerUp_, this), - /* Note that the listener for `pointercancel is set up on - * `pointerEventHandler_` and not `documentPointerEventHandler_` like - * the `pointerup` and `pointermove` listeners. - * - * The reason for this is the following: `TouchSource.vacuumTouches_()` - * issues `pointercancel` events, when there was no `touchend` for a - * `touchstart`. Now, let's say a first `touchstart` is registered on - * `pointerEventHandler_`. The `documentPointerEventHandler_` is set up. - * But `documentPointerEventHandler_` doesn't know about the first - * `touchstart`. If there is no `touchend` for the `touchstart`, we can - * only receive a `touchcancel` from `pointerEventHandler_`, because it is - * only registered there. - */ - listen(this.pointerEventHandler_, - MapBrowserEventType.POINTERCANCEL, - this.handlePointerUp_, this) - ); - } -}; - - -/** - * @param {module:ol/pointer/PointerEvent} pointerEvent Pointer - * event. - * @private - */ -MapBrowserEventHandler.prototype.handlePointerMove_ = function(pointerEvent) { - // Between pointerdown and pointerup, pointermove events are triggered. - // To avoid a 'false' touchmove event to be dispatched, we test if the pointer - // moved a significant distance. - if (this.isMoving_(pointerEvent)) { - this.dragging_ = true; - const newEvent = new MapBrowserPointerEvent( - MapBrowserEventType.POINTERDRAG, this.map_, pointerEvent, - this.dragging_); - this.dispatchEvent(newEvent); - } - - // Some native android browser triggers mousemove events during small period - // of time. See: https://code.google.com/p/android/issues/detail?id=5491 or - // https://code.google.com/p/android/issues/detail?id=19827 - // ex: Galaxy Tab P3110 + Android 4.1.1 - pointerEvent.preventDefault(); -}; - - -/** - * Wrap and relay a pointer event. Note that this requires that the type - * string for the MapBrowserPointerEvent matches the PointerEvent type. - * @param {module:ol/pointer/PointerEvent} pointerEvent Pointer - * event. - * @private - */ -MapBrowserEventHandler.prototype.relayEvent_ = function(pointerEvent) { - const dragging = !!(this.down_ && this.isMoving_(pointerEvent)); - this.dispatchEvent(new MapBrowserPointerEvent( - pointerEvent.type, this.map_, pointerEvent, dragging)); -}; - - -/** - * @param {module:ol/pointer/PointerEvent} pointerEvent Pointer - * event. - * @return {boolean} Is moving. - * @private - */ -MapBrowserEventHandler.prototype.isMoving_ = function(pointerEvent) { - return this.dragging_ || - Math.abs(pointerEvent.clientX - this.down_.clientX) > this.moveTolerance_ || - Math.abs(pointerEvent.clientY - this.down_.clientY) > this.moveTolerance_; -}; - - -/** - * @inheritDoc - */ -MapBrowserEventHandler.prototype.disposeInternal = function() { - if (this.relayedListenerKey_) { - unlistenByKey(this.relayedListenerKey_); - this.relayedListenerKey_ = null; - } - if (this.pointerdownListenerKey_) { - unlistenByKey(this.pointerdownListenerKey_); - this.pointerdownListenerKey_ = null; - } - - this.dragListenerKeys_.forEach(unlistenByKey); - this.dragListenerKeys_.length = 0; - - if (this.documentPointerEventHandler_) { - this.documentPointerEventHandler_.dispose(); - this.documentPointerEventHandler_ = null; - } - if (this.pointerEventHandler_) { - this.pointerEventHandler_.dispose(); - this.pointerEventHandler_ = null; - } - EventTarget.prototype.disposeInternal.call(this); -}; export default MapBrowserEventHandler; diff --git a/src/ol/Object.js b/src/ol/Object.js index 8dc4adb4b6..39747bbb3e 100644 --- a/src/ol/Object.js +++ b/src/ol/Object.js @@ -87,25 +87,119 @@ inherits(ObjectEvent, Event); * @fires module:ol/Object~ObjectEvent * @api */ -const BaseObject = function(opt_values) { - Observable.call(this); +class BaseObject { + constructor(opt_values) { + Observable.call(this); - // Call {@link module:ol~getUid} to ensure that the order of objects' ids is - // the same as the order in which they were created. This also helps to - // ensure that object properties are always added in the same order, which - // helps many JavaScript engines generate faster code. - getUid(this); + // Call {@link module:ol~getUid} to ensure that the order of objects' ids is + // the same as the order in which they were created. This also helps to + // ensure that object properties are always added in the same order, which + // helps many JavaScript engines generate faster code. + getUid(this); + + /** + * @private + * @type {!Object.} + */ + this.values_ = {}; + + if (opt_values !== undefined) { + this.setProperties(opt_values); + } + } /** - * @private - * @type {!Object.} + * Gets a value. + * @param {string} key Key name. + * @return {*} Value. + * @api */ - this.values_ = {}; - - if (opt_values !== undefined) { - this.setProperties(opt_values); + get(key) { + let value; + if (this.values_.hasOwnProperty(key)) { + value = this.values_[key]; + } + return value; } -}; + + /** + * Get a list of object property names. + * @return {Array.} List of property names. + * @api + */ + getKeys() { + return Object.keys(this.values_); + } + + /** + * Get an object of all property names and values. + * @return {Object.} Object. + * @api + */ + getProperties() { + return assign({}, this.values_); + } + + /** + * @param {string} key Key name. + * @param {*} oldValue Old value. + */ + notify(key, oldValue) { + let eventType; + eventType = getChangeEventType(key); + this.dispatchEvent(new ObjectEvent(eventType, key, oldValue)); + eventType = ObjectEventType.PROPERTYCHANGE; + this.dispatchEvent(new ObjectEvent(eventType, key, oldValue)); + } + + /** + * Sets a value. + * @param {string} key Key name. + * @param {*} value Value. + * @param {boolean=} opt_silent Update without triggering an event. + * @api + */ + set(key, value, opt_silent) { + if (opt_silent) { + this.values_[key] = value; + } else { + const oldValue = this.values_[key]; + this.values_[key] = value; + if (oldValue !== value) { + this.notify(key, oldValue); + } + } + } + + /** + * Sets a collection of key-value pairs. Note that this changes any existing + * properties and adds new ones (it does not remove any existing properties). + * @param {Object.} values Values. + * @param {boolean=} opt_silent Update without triggering an event. + * @api + */ + setProperties(values, opt_silent) { + for (const key in values) { + this.set(key, values[key], opt_silent); + } + } + + /** + * Unsets a property. + * @param {string} key Key name. + * @param {boolean=} opt_silent Unset without triggering an event. + * @api + */ + unset(key, opt_silent) { + if (key in this.values_) { + const oldValue = this.values_[key]; + delete this.values_[key]; + if (!opt_silent) { + this.notify(key, oldValue); + } + } + } +} inherits(BaseObject, Observable); @@ -127,103 +221,4 @@ export function getChangeEventType(key) { } -/** - * Gets a value. - * @param {string} key Key name. - * @return {*} Value. - * @api - */ -BaseObject.prototype.get = function(key) { - let value; - if (this.values_.hasOwnProperty(key)) { - value = this.values_[key]; - } - return value; -}; - - -/** - * Get a list of object property names. - * @return {Array.} List of property names. - * @api - */ -BaseObject.prototype.getKeys = function() { - return Object.keys(this.values_); -}; - - -/** - * Get an object of all property names and values. - * @return {Object.} Object. - * @api - */ -BaseObject.prototype.getProperties = function() { - return assign({}, this.values_); -}; - - -/** - * @param {string} key Key name. - * @param {*} oldValue Old value. - */ -BaseObject.prototype.notify = function(key, oldValue) { - let eventType; - eventType = getChangeEventType(key); - this.dispatchEvent(new ObjectEvent(eventType, key, oldValue)); - eventType = ObjectEventType.PROPERTYCHANGE; - this.dispatchEvent(new ObjectEvent(eventType, key, oldValue)); -}; - - -/** - * Sets a value. - * @param {string} key Key name. - * @param {*} value Value. - * @param {boolean=} opt_silent Update without triggering an event. - * @api - */ -BaseObject.prototype.set = function(key, value, opt_silent) { - if (opt_silent) { - this.values_[key] = value; - } else { - const oldValue = this.values_[key]; - this.values_[key] = value; - if (oldValue !== value) { - this.notify(key, oldValue); - } - } -}; - - -/** - * Sets a collection of key-value pairs. Note that this changes any existing - * properties and adds new ones (it does not remove any existing properties). - * @param {Object.} values Values. - * @param {boolean=} opt_silent Update without triggering an event. - * @api - */ -BaseObject.prototype.setProperties = function(values, opt_silent) { - for (const key in values) { - this.set(key, values[key], opt_silent); - } -}; - - -/** - * Unsets a property. - * @param {string} key Key name. - * @param {boolean=} opt_silent Unset without triggering an event. - * @api - */ -BaseObject.prototype.unset = function(key, opt_silent) { - if (key in this.values_) { - const oldValue = this.values_[key]; - delete this.values_[key]; - if (!opt_silent) { - this.notify(key, oldValue); - } - } -}; - - export default BaseObject; diff --git a/src/ol/Observable.js b/src/ol/Observable.js index a1052e3025..30edbbbc09 100644 --- a/src/ol/Observable.js +++ b/src/ol/Observable.js @@ -20,17 +20,99 @@ import EventType from './events/EventType.js'; * @struct * @api */ -const Observable = function() { +class Observable { + constructor() { - EventTarget.call(this); + EventTarget.call(this); + + /** + * @private + * @type {number} + */ + this.revision_ = 0; + + } /** - * @private - * @type {number} + * Increases the revision counter and dispatches a 'change' event. + * @api */ - this.revision_ = 0; + changed() { + ++this.revision_; + this.dispatchEvent(EventType.CHANGE); + } -}; + /** + * Get the version number for this object. Each time the object is modified, + * its version number will be incremented. + * @return {number} Revision. + * @api + */ + getRevision() { + return this.revision_; + } + + /** + * Listen for a certain type of event. + * @param {string|Array.} type The event type or array of event types. + * @param {function(?): ?} listener The listener function. + * @return {module:ol/events~EventsKey|Array.} Unique key for the listener. If + * called with an array of event types as the first argument, the return + * will be an array of keys. + * @api + */ + on(type, listener) { + if (Array.isArray(type)) { + const len = type.length; + const keys = new Array(len); + for (let i = 0; i < len; ++i) { + keys[i] = listen(this, type[i], listener); + } + return keys; + } else { + return listen(this, /** @type {string} */ (type), listener); + } + } + + /** + * Listen once for a certain type of event. + * @param {string|Array.} type The event type or array of event types. + * @param {function(?): ?} listener The listener function. + * @return {module:ol/events~EventsKey|Array.} Unique key for the listener. If + * called with an array of event types as the first argument, the return + * will be an array of keys. + * @api + */ + once(type, listener) { + if (Array.isArray(type)) { + const len = type.length; + const keys = new Array(len); + for (let i = 0; i < len; ++i) { + keys[i] = listenOnce(this, type[i], listener); + } + return keys; + } else { + return listenOnce(this, /** @type {string} */ (type), listener); + } + } + + /** + * Unlisten for a certain type of event. + * @param {string|Array.} type The event type or array of event types. + * @param {function(?): ?} listener The listener function. + * @api + */ + un(type, listener) { + if (Array.isArray(type)) { + for (let i = 0, ii = type.length; i < ii; ++i) { + unlisten(this, type[i], listener); + } + return; + } else { + unlisten(this, /** @type {string} */ (type), listener); + } + } +} inherits(Observable, EventTarget); @@ -52,16 +134,6 @@ export function unByKey(key) { } -/** - * Increases the revision counter and dispatches a 'change' event. - * @api - */ -Observable.prototype.changed = function() { - ++this.revision_; - this.dispatchEvent(EventType.CHANGE); -}; - - /** * Dispatches an event and calls all listeners listening for events * of this type. The event parameter can either be a string or an @@ -76,77 +148,4 @@ Observable.prototype.changed = function() { Observable.prototype.dispatchEvent; -/** - * Get the version number for this object. Each time the object is modified, - * its version number will be incremented. - * @return {number} Revision. - * @api - */ -Observable.prototype.getRevision = function() { - return this.revision_; -}; - - -/** - * Listen for a certain type of event. - * @param {string|Array.} type The event type or array of event types. - * @param {function(?): ?} listener The listener function. - * @return {module:ol/events~EventsKey|Array.} Unique key for the listener. If - * called with an array of event types as the first argument, the return - * will be an array of keys. - * @api - */ -Observable.prototype.on = function(type, listener) { - if (Array.isArray(type)) { - const len = type.length; - const keys = new Array(len); - for (let i = 0; i < len; ++i) { - keys[i] = listen(this, type[i], listener); - } - return keys; - } else { - return listen(this, /** @type {string} */ (type), listener); - } -}; - - -/** - * Listen once for a certain type of event. - * @param {string|Array.} type The event type or array of event types. - * @param {function(?): ?} listener The listener function. - * @return {module:ol/events~EventsKey|Array.} Unique key for the listener. If - * called with an array of event types as the first argument, the return - * will be an array of keys. - * @api - */ -Observable.prototype.once = function(type, listener) { - if (Array.isArray(type)) { - const len = type.length; - const keys = new Array(len); - for (let i = 0; i < len; ++i) { - keys[i] = listenOnce(this, type[i], listener); - } - return keys; - } else { - return listenOnce(this, /** @type {string} */ (type), listener); - } -}; - - -/** - * Unlisten for a certain type of event. - * @param {string|Array.} type The event type or array of event types. - * @param {function(?): ?} listener The listener function. - * @api - */ -Observable.prototype.un = function(type, listener) { - if (Array.isArray(type)) { - for (let i = 0, ii = type.length; i < ii; ++i) { - unlisten(this, type[i], listener); - } - return; - } else { - unlisten(this, /** @type {string} */ (type), listener); - } -}; export default Observable; diff --git a/src/ol/Overlay.js b/src/ol/Overlay.js index 102fa04685..b5fc6d3d0b 100644 --- a/src/ol/Overlay.js +++ b/src/ol/Overlay.js @@ -98,510 +98,490 @@ const Property = { * @param {module:ol/Overlay~Options} options Overlay options. * @api */ -const Overlay = function(options) { +class Overlay { + constructor(options) { - BaseObject.call(this); + BaseObject.call(this); - /** - * @protected - * @type {module:ol/Overlay~Options} - */ - this.options = options; + /** + * @protected + * @type {module:ol/Overlay~Options} + */ + this.options = options; - /** - * @protected - * @type {number|string|undefined} - */ - this.id = options.id; + /** + * @protected + * @type {number|string|undefined} + */ + this.id = options.id; - /** - * @protected - * @type {boolean} - */ - this.insertFirst = options.insertFirst !== undefined ? - options.insertFirst : true; + /** + * @protected + * @type {boolean} + */ + this.insertFirst = options.insertFirst !== undefined ? + options.insertFirst : true; - /** - * @protected - * @type {boolean} - */ - this.stopEvent = options.stopEvent !== undefined ? options.stopEvent : true; + /** + * @protected + * @type {boolean} + */ + this.stopEvent = options.stopEvent !== undefined ? options.stopEvent : true; - /** - * @protected - * @type {HTMLElement} - */ - this.element = document.createElement('DIV'); - this.element.className = options.className !== undefined ? - options.className : 'ol-overlay-container ' + CLASS_SELECTABLE; - this.element.style.position = 'absolute'; + /** + * @protected + * @type {HTMLElement} + */ + this.element = document.createElement('DIV'); + this.element.className = options.className !== undefined ? + options.className : 'ol-overlay-container ' + CLASS_SELECTABLE; + this.element.style.position = 'absolute'; - /** - * @protected - * @type {boolean} - */ - this.autoPan = options.autoPan !== undefined ? options.autoPan : false; + /** + * @protected + * @type {boolean} + */ + this.autoPan = options.autoPan !== undefined ? options.autoPan : false; - /** - * @protected - * @type {module:ol/Overlay~PanOptions} - */ - this.autoPanAnimation = options.autoPanAnimation || /** @type {module:ol/Overlay~PanOptions} */ ({}); + /** + * @protected + * @type {module:ol/Overlay~PanOptions} + */ + this.autoPanAnimation = options.autoPanAnimation || /** @type {module:ol/Overlay~PanOptions} */ ({}); - /** - * @protected - * @type {number} - */ - this.autoPanMargin = options.autoPanMargin !== undefined ? - options.autoPanMargin : 20; + /** + * @protected + * @type {number} + */ + this.autoPanMargin = options.autoPanMargin !== undefined ? + options.autoPanMargin : 20; - /** - * @protected - * @type {{bottom_: string, - * left_: string, - * right_: string, - * top_: string, - * visible: boolean}} - */ - this.rendered = { - bottom_: '', - left_: '', - right_: '', - top_: '', - visible: true - }; + /** + * @protected + * @type {{bottom_: string, + * left_: string, + * right_: string, + * top_: string, + * visible: boolean}} + */ + this.rendered = { + bottom_: '', + left_: '', + right_: '', + top_: '', + visible: true + }; - /** - * @protected - * @type {?module:ol/events~EventsKey} - */ - this.mapPostrenderListenerKey = null; + /** + * @protected + * @type {?module:ol/events~EventsKey} + */ + this.mapPostrenderListenerKey = null; - listen( - this, getChangeEventType(Property.ELEMENT), - this.handleElementChanged, this); + listen( + this, getChangeEventType(Property.ELEMENT), + this.handleElementChanged, this); - listen( - this, getChangeEventType(Property.MAP), - this.handleMapChanged, this); + listen( + this, getChangeEventType(Property.MAP), + this.handleMapChanged, this); - listen( - this, getChangeEventType(Property.OFFSET), - this.handleOffsetChanged, this); + listen( + this, getChangeEventType(Property.OFFSET), + this.handleOffsetChanged, this); - listen( - this, getChangeEventType(Property.POSITION), - this.handlePositionChanged, this); + listen( + this, getChangeEventType(Property.POSITION), + this.handlePositionChanged, this); - listen( - this, getChangeEventType(Property.POSITIONING), - this.handlePositioningChanged, this); + listen( + this, getChangeEventType(Property.POSITIONING), + this.handlePositioningChanged, this); + + if (options.element !== undefined) { + this.setElement(options.element); + } + + this.setOffset(options.offset !== undefined ? options.offset : [0, 0]); + + this.setPositioning(options.positioning !== undefined ? + /** @type {module:ol/OverlayPositioning} */ (options.positioning) : + OverlayPositioning.TOP_LEFT); + + if (options.position !== undefined) { + this.setPosition(options.position); + } - if (options.element !== undefined) { - this.setElement(options.element); } - this.setOffset(options.offset !== undefined ? options.offset : [0, 0]); - - this.setPositioning(options.positioning !== undefined ? - /** @type {module:ol/OverlayPositioning} */ (options.positioning) : - OverlayPositioning.TOP_LEFT); - - if (options.position !== undefined) { - this.setPosition(options.position); + /** + * Get the DOM element of this overlay. + * @return {HTMLElement|undefined} The Element containing the overlay. + * @observable + * @api + */ + getElement() { + return /** @type {HTMLElement|undefined} */ (this.get(Property.ELEMENT)); } -}; + /** + * Get the overlay identifier which is set on constructor. + * @return {number|string|undefined} Id. + * @api + */ + getId() { + return this.id; + } + + /** + * Get the map associated with this overlay. + * @return {module:ol/PluggableMap|undefined} The map that the + * overlay is part of. + * @observable + * @api + */ + getMap() { + return ( + /** @type {module:ol/PluggableMap|undefined} */ (this.get(Property.MAP)) + ); + } + + /** + * Get the offset of this overlay. + * @return {Array.} The offset. + * @observable + * @api + */ + getOffset() { + return /** @type {Array.} */ (this.get(Property.OFFSET)); + } + + /** + * Get the current position of this overlay. + * @return {module:ol/coordinate~Coordinate|undefined} The spatial point that the overlay is + * anchored at. + * @observable + * @api + */ + getPosition() { + return ( + /** @type {module:ol/coordinate~Coordinate|undefined} */ (this.get(Property.POSITION)) + ); + } + + /** + * Get the current positioning of this overlay. + * @return {module:ol/OverlayPositioning} How the overlay is positioned + * relative to its point on the map. + * @observable + * @api + */ + getPositioning() { + return ( + /** @type {module:ol/OverlayPositioning} */ (this.get(Property.POSITIONING)) + ); + } + + /** + * @protected + */ + handleElementChanged() { + removeChildren(this.element); + const element = this.getElement(); + if (element) { + this.element.appendChild(element); + } + } + + /** + * @protected + */ + handleMapChanged() { + if (this.mapPostrenderListenerKey) { + removeNode(this.element); + unlistenByKey(this.mapPostrenderListenerKey); + this.mapPostrenderListenerKey = null; + } + const map = this.getMap(); + if (map) { + this.mapPostrenderListenerKey = listen(map, + MapEventType.POSTRENDER, this.render, this); + this.updatePixelPosition(); + const container = this.stopEvent ? + map.getOverlayContainerStopEvent() : map.getOverlayContainer(); + if (this.insertFirst) { + container.insertBefore(this.element, container.childNodes[0] || null); + } else { + container.appendChild(this.element); + } + } + } + + /** + * @protected + */ + render() { + this.updatePixelPosition(); + } + + /** + * @protected + */ + handleOffsetChanged() { + this.updatePixelPosition(); + } + + /** + * @protected + */ + handlePositionChanged() { + this.updatePixelPosition(); + if (this.get(Property.POSITION) && this.autoPan) { + this.panIntoView(); + } + } + + /** + * @protected + */ + handlePositioningChanged() { + this.updatePixelPosition(); + } + + /** + * Set the DOM element to be associated with this overlay. + * @param {HTMLElement|undefined} element The Element containing the overlay. + * @observable + * @api + */ + setElement(element) { + this.set(Property.ELEMENT, element); + } + + /** + * Set the map to be associated with this overlay. + * @param {module:ol/PluggableMap|undefined} map The map that the + * overlay is part of. + * @observable + * @api + */ + setMap(map) { + this.set(Property.MAP, map); + } + + /** + * Set the offset for this overlay. + * @param {Array.} offset Offset. + * @observable + * @api + */ + setOffset(offset) { + this.set(Property.OFFSET, offset); + } + + /** + * Set the position for this overlay. If the position is `undefined` the + * overlay is hidden. + * @param {module:ol/coordinate~Coordinate|undefined} position The spatial point that the overlay + * is anchored at. + * @observable + * @api + */ + setPosition(position) { + this.set(Property.POSITION, position); + } + + /** + * Pan the map so that the overlay is entirely visible in the current viewport + * (if necessary). + * @protected + */ + panIntoView() { + const map = this.getMap(); + + if (!map || !map.getTargetElement()) { + return; + } + + const mapRect = this.getRect(map.getTargetElement(), map.getSize()); + const element = this.getElement(); + const overlayRect = this.getRect(element, [outerWidth(element), outerHeight(element)]); + + const margin = this.autoPanMargin; + if (!containsExtent(mapRect, overlayRect)) { + // the overlay is not completely inside the viewport, so pan the map + const offsetLeft = overlayRect[0] - mapRect[0]; + const offsetRight = mapRect[2] - overlayRect[2]; + const offsetTop = overlayRect[1] - mapRect[1]; + const offsetBottom = mapRect[3] - overlayRect[3]; + + const delta = [0, 0]; + if (offsetLeft < 0) { + // move map to the left + delta[0] = offsetLeft - margin; + } else if (offsetRight < 0) { + // move map to the right + delta[0] = Math.abs(offsetRight) + margin; + } + if (offsetTop < 0) { + // move map up + delta[1] = offsetTop - margin; + } else if (offsetBottom < 0) { + // move map down + delta[1] = Math.abs(offsetBottom) + margin; + } + + if (delta[0] !== 0 || delta[1] !== 0) { + const center = /** @type {module:ol/coordinate~Coordinate} */ (map.getView().getCenter()); + const centerPx = map.getPixelFromCoordinate(center); + const newCenterPx = [ + centerPx[0] + delta[0], + centerPx[1] + delta[1] + ]; + + map.getView().animate({ + center: map.getCoordinateFromPixel(newCenterPx), + duration: this.autoPanAnimation.duration, + easing: this.autoPanAnimation.easing + }); + } + } + } + + /** + * Get the extent of an element relative to the document + * @param {HTMLElement|undefined} element The element. + * @param {module:ol/size~Size|undefined} size The size of the element. + * @return {module:ol/extent~Extent} The extent. + * @protected + */ + getRect(element, size) { + const box = element.getBoundingClientRect(); + const offsetX = box.left + window.pageXOffset; + const offsetY = box.top + window.pageYOffset; + return [ + offsetX, + offsetY, + offsetX + size[0], + offsetY + size[1] + ]; + } + + /** + * Set the positioning for this overlay. + * @param {module:ol/OverlayPositioning} positioning how the overlay is + * positioned relative to its point on the map. + * @observable + * @api + */ + setPositioning(positioning) { + this.set(Property.POSITIONING, positioning); + } + + /** + * Modify the visibility of the element. + * @param {boolean} visible Element visibility. + * @protected + */ + setVisible(visible) { + if (this.rendered.visible !== visible) { + this.element.style.display = visible ? '' : 'none'; + this.rendered.visible = visible; + } + } + + /** + * Update pixel position. + * @protected + */ + updatePixelPosition() { + const map = this.getMap(); + const position = this.getPosition(); + if (!map || !map.isRendered() || !position) { + this.setVisible(false); + return; + } + + const pixel = map.getPixelFromCoordinate(position); + const mapSize = map.getSize(); + this.updateRenderedPosition(pixel, mapSize); + } + + /** + * @param {module:ol~Pixel} pixel The pixel location. + * @param {module:ol/size~Size|undefined} mapSize The map size. + * @protected + */ + updateRenderedPosition(pixel, mapSize) { + const style = this.element.style; + const offset = this.getOffset(); + + const positioning = this.getPositioning(); + + this.setVisible(true); + + let offsetX = offset[0]; + let offsetY = offset[1]; + if (positioning == OverlayPositioning.BOTTOM_RIGHT || + positioning == OverlayPositioning.CENTER_RIGHT || + positioning == OverlayPositioning.TOP_RIGHT) { + if (this.rendered.left_ !== '') { + this.rendered.left_ = style.left = ''; + } + const right = Math.round(mapSize[0] - pixel[0] - offsetX) + 'px'; + if (this.rendered.right_ != right) { + this.rendered.right_ = style.right = right; + } + } else { + if (this.rendered.right_ !== '') { + this.rendered.right_ = style.right = ''; + } + if (positioning == OverlayPositioning.BOTTOM_CENTER || + positioning == OverlayPositioning.CENTER_CENTER || + positioning == OverlayPositioning.TOP_CENTER) { + offsetX -= this.element.offsetWidth / 2; + } + const left = Math.round(pixel[0] + offsetX) + 'px'; + if (this.rendered.left_ != left) { + this.rendered.left_ = style.left = left; + } + } + if (positioning == OverlayPositioning.BOTTOM_LEFT || + positioning == OverlayPositioning.BOTTOM_CENTER || + positioning == OverlayPositioning.BOTTOM_RIGHT) { + if (this.rendered.top_ !== '') { + this.rendered.top_ = style.top = ''; + } + const bottom = Math.round(mapSize[1] - pixel[1] - offsetY) + 'px'; + if (this.rendered.bottom_ != bottom) { + this.rendered.bottom_ = style.bottom = bottom; + } + } else { + if (this.rendered.bottom_ !== '') { + this.rendered.bottom_ = style.bottom = ''; + } + if (positioning == OverlayPositioning.CENTER_LEFT || + positioning == OverlayPositioning.CENTER_CENTER || + positioning == OverlayPositioning.CENTER_RIGHT) { + offsetY -= this.element.offsetHeight / 2; + } + const top = Math.round(pixel[1] + offsetY) + 'px'; + if (this.rendered.top_ != top) { + this.rendered.top_ = style.top = top; + } + } + } + + /** + * returns the options this Overlay has been created with + * @return {module:ol/Overlay~Options} overlay options + */ + getOptions() { + return this.options; + } +} inherits(Overlay, BaseObject); -/** - * Get the DOM element of this overlay. - * @return {HTMLElement|undefined} The Element containing the overlay. - * @observable - * @api - */ -Overlay.prototype.getElement = function() { - return /** @type {HTMLElement|undefined} */ (this.get(Property.ELEMENT)); -}; - - -/** - * Get the overlay identifier which is set on constructor. - * @return {number|string|undefined} Id. - * @api - */ -Overlay.prototype.getId = function() { - return this.id; -}; - - -/** - * Get the map associated with this overlay. - * @return {module:ol/PluggableMap|undefined} The map that the - * overlay is part of. - * @observable - * @api - */ -Overlay.prototype.getMap = function() { - return ( - /** @type {module:ol/PluggableMap|undefined} */ (this.get(Property.MAP)) - ); -}; - - -/** - * Get the offset of this overlay. - * @return {Array.} The offset. - * @observable - * @api - */ -Overlay.prototype.getOffset = function() { - return /** @type {Array.} */ (this.get(Property.OFFSET)); -}; - - -/** - * Get the current position of this overlay. - * @return {module:ol/coordinate~Coordinate|undefined} The spatial point that the overlay is - * anchored at. - * @observable - * @api - */ -Overlay.prototype.getPosition = function() { - return ( - /** @type {module:ol/coordinate~Coordinate|undefined} */ (this.get(Property.POSITION)) - ); -}; - - -/** - * Get the current positioning of this overlay. - * @return {module:ol/OverlayPositioning} How the overlay is positioned - * relative to its point on the map. - * @observable - * @api - */ -Overlay.prototype.getPositioning = function() { - return ( - /** @type {module:ol/OverlayPositioning} */ (this.get(Property.POSITIONING)) - ); -}; - - -/** - * @protected - */ -Overlay.prototype.handleElementChanged = function() { - removeChildren(this.element); - const element = this.getElement(); - if (element) { - this.element.appendChild(element); - } -}; - - -/** - * @protected - */ -Overlay.prototype.handleMapChanged = function() { - if (this.mapPostrenderListenerKey) { - removeNode(this.element); - unlistenByKey(this.mapPostrenderListenerKey); - this.mapPostrenderListenerKey = null; - } - const map = this.getMap(); - if (map) { - this.mapPostrenderListenerKey = listen(map, - MapEventType.POSTRENDER, this.render, this); - this.updatePixelPosition(); - const container = this.stopEvent ? - map.getOverlayContainerStopEvent() : map.getOverlayContainer(); - if (this.insertFirst) { - container.insertBefore(this.element, container.childNodes[0] || null); - } else { - container.appendChild(this.element); - } - } -}; - - -/** - * @protected - */ -Overlay.prototype.render = function() { - this.updatePixelPosition(); -}; - - -/** - * @protected - */ -Overlay.prototype.handleOffsetChanged = function() { - this.updatePixelPosition(); -}; - - -/** - * @protected - */ -Overlay.prototype.handlePositionChanged = function() { - this.updatePixelPosition(); - if (this.get(Property.POSITION) && this.autoPan) { - this.panIntoView(); - } -}; - - -/** - * @protected - */ -Overlay.prototype.handlePositioningChanged = function() { - this.updatePixelPosition(); -}; - - -/** - * Set the DOM element to be associated with this overlay. - * @param {HTMLElement|undefined} element The Element containing the overlay. - * @observable - * @api - */ -Overlay.prototype.setElement = function(element) { - this.set(Property.ELEMENT, element); -}; - - -/** - * Set the map to be associated with this overlay. - * @param {module:ol/PluggableMap|undefined} map The map that the - * overlay is part of. - * @observable - * @api - */ -Overlay.prototype.setMap = function(map) { - this.set(Property.MAP, map); -}; - - -/** - * Set the offset for this overlay. - * @param {Array.} offset Offset. - * @observable - * @api - */ -Overlay.prototype.setOffset = function(offset) { - this.set(Property.OFFSET, offset); -}; - - -/** - * Set the position for this overlay. If the position is `undefined` the - * overlay is hidden. - * @param {module:ol/coordinate~Coordinate|undefined} position The spatial point that the overlay - * is anchored at. - * @observable - * @api - */ -Overlay.prototype.setPosition = function(position) { - this.set(Property.POSITION, position); -}; - - -/** - * Pan the map so that the overlay is entirely visible in the current viewport - * (if necessary). - * @protected - */ -Overlay.prototype.panIntoView = function() { - const map = this.getMap(); - - if (!map || !map.getTargetElement()) { - return; - } - - const mapRect = this.getRect(map.getTargetElement(), map.getSize()); - const element = this.getElement(); - const overlayRect = this.getRect(element, [outerWidth(element), outerHeight(element)]); - - const margin = this.autoPanMargin; - if (!containsExtent(mapRect, overlayRect)) { - // the overlay is not completely inside the viewport, so pan the map - const offsetLeft = overlayRect[0] - mapRect[0]; - const offsetRight = mapRect[2] - overlayRect[2]; - const offsetTop = overlayRect[1] - mapRect[1]; - const offsetBottom = mapRect[3] - overlayRect[3]; - - const delta = [0, 0]; - if (offsetLeft < 0) { - // move map to the left - delta[0] = offsetLeft - margin; - } else if (offsetRight < 0) { - // move map to the right - delta[0] = Math.abs(offsetRight) + margin; - } - if (offsetTop < 0) { - // move map up - delta[1] = offsetTop - margin; - } else if (offsetBottom < 0) { - // move map down - delta[1] = Math.abs(offsetBottom) + margin; - } - - if (delta[0] !== 0 || delta[1] !== 0) { - const center = /** @type {module:ol/coordinate~Coordinate} */ (map.getView().getCenter()); - const centerPx = map.getPixelFromCoordinate(center); - const newCenterPx = [ - centerPx[0] + delta[0], - centerPx[1] + delta[1] - ]; - - map.getView().animate({ - center: map.getCoordinateFromPixel(newCenterPx), - duration: this.autoPanAnimation.duration, - easing: this.autoPanAnimation.easing - }); - } - } -}; - - -/** - * Get the extent of an element relative to the document - * @param {HTMLElement|undefined} element The element. - * @param {module:ol/size~Size|undefined} size The size of the element. - * @return {module:ol/extent~Extent} The extent. - * @protected - */ -Overlay.prototype.getRect = function(element, size) { - const box = element.getBoundingClientRect(); - const offsetX = box.left + window.pageXOffset; - const offsetY = box.top + window.pageYOffset; - return [ - offsetX, - offsetY, - offsetX + size[0], - offsetY + size[1] - ]; -}; - - -/** - * Set the positioning for this overlay. - * @param {module:ol/OverlayPositioning} positioning how the overlay is - * positioned relative to its point on the map. - * @observable - * @api - */ -Overlay.prototype.setPositioning = function(positioning) { - this.set(Property.POSITIONING, positioning); -}; - - -/** - * Modify the visibility of the element. - * @param {boolean} visible Element visibility. - * @protected - */ -Overlay.prototype.setVisible = function(visible) { - if (this.rendered.visible !== visible) { - this.element.style.display = visible ? '' : 'none'; - this.rendered.visible = visible; - } -}; - - -/** - * Update pixel position. - * @protected - */ -Overlay.prototype.updatePixelPosition = function() { - const map = this.getMap(); - const position = this.getPosition(); - if (!map || !map.isRendered() || !position) { - this.setVisible(false); - return; - } - - const pixel = map.getPixelFromCoordinate(position); - const mapSize = map.getSize(); - this.updateRenderedPosition(pixel, mapSize); -}; - - -/** - * @param {module:ol~Pixel} pixel The pixel location. - * @param {module:ol/size~Size|undefined} mapSize The map size. - * @protected - */ -Overlay.prototype.updateRenderedPosition = function(pixel, mapSize) { - const style = this.element.style; - const offset = this.getOffset(); - - const positioning = this.getPositioning(); - - this.setVisible(true); - - let offsetX = offset[0]; - let offsetY = offset[1]; - if (positioning == OverlayPositioning.BOTTOM_RIGHT || - positioning == OverlayPositioning.CENTER_RIGHT || - positioning == OverlayPositioning.TOP_RIGHT) { - if (this.rendered.left_ !== '') { - this.rendered.left_ = style.left = ''; - } - const right = Math.round(mapSize[0] - pixel[0] - offsetX) + 'px'; - if (this.rendered.right_ != right) { - this.rendered.right_ = style.right = right; - } - } else { - if (this.rendered.right_ !== '') { - this.rendered.right_ = style.right = ''; - } - if (positioning == OverlayPositioning.BOTTOM_CENTER || - positioning == OverlayPositioning.CENTER_CENTER || - positioning == OverlayPositioning.TOP_CENTER) { - offsetX -= this.element.offsetWidth / 2; - } - const left = Math.round(pixel[0] + offsetX) + 'px'; - if (this.rendered.left_ != left) { - this.rendered.left_ = style.left = left; - } - } - if (positioning == OverlayPositioning.BOTTOM_LEFT || - positioning == OverlayPositioning.BOTTOM_CENTER || - positioning == OverlayPositioning.BOTTOM_RIGHT) { - if (this.rendered.top_ !== '') { - this.rendered.top_ = style.top = ''; - } - const bottom = Math.round(mapSize[1] - pixel[1] - offsetY) + 'px'; - if (this.rendered.bottom_ != bottom) { - this.rendered.bottom_ = style.bottom = bottom; - } - } else { - if (this.rendered.bottom_ !== '') { - this.rendered.bottom_ = style.bottom = ''; - } - if (positioning == OverlayPositioning.CENTER_LEFT || - positioning == OverlayPositioning.CENTER_CENTER || - positioning == OverlayPositioning.CENTER_RIGHT) { - offsetY -= this.element.offsetHeight / 2; - } - const top = Math.round(pixel[1] + offsetY) + 'px'; - if (this.rendered.top_ != top) { - this.rendered.top_ = style.top = top; - } - } -}; - - -/** - * returns the options this Overlay has been created with - * @return {module:ol/Overlay~Options} overlay options - */ -Overlay.prototype.getOptions = function() { - return this.options; -}; - export default Overlay; diff --git a/src/ol/PluggableMap.js b/src/ol/PluggableMap.js index 665c412f37..9e8bb2fbc7 100644 --- a/src/ol/PluggableMap.js +++ b/src/ol/PluggableMap.js @@ -140,1255 +140,1204 @@ import {create as createTransform, apply as applyTransform} from './transform.js * @fires module:ol/render/Event~RenderEvent#precompose * @api */ -const PluggableMap = function(options) { +class PluggableMap { + constructor(options) { - BaseObject.call(this); + BaseObject.call(this); - const optionsInternal = createOptionsInternal(options); + const optionsInternal = createOptionsInternal(options); - /** - * @type {number} - * @private - */ - this.maxTilesLoading_ = options.maxTilesLoading !== undefined ? options.maxTilesLoading : 16; - - /** - * @type {boolean} - * @private - */ - this.loadTilesWhileAnimating_ = - options.loadTilesWhileAnimating !== undefined ? - options.loadTilesWhileAnimating : false; - - /** - * @type {boolean} - * @private - */ - this.loadTilesWhileInteracting_ = - options.loadTilesWhileInteracting !== undefined ? - options.loadTilesWhileInteracting : false; - - /** - * @private - * @type {number} - */ - this.pixelRatio_ = options.pixelRatio !== undefined ? - options.pixelRatio : DEVICE_PIXEL_RATIO; - - /** - * @private - * @type {number|undefined} - */ - this.animationDelayKey_; - - /** - * @private - */ - this.animationDelay_ = function() { - this.animationDelayKey_ = undefined; - this.renderFrame_.call(this, Date.now()); - }.bind(this); - - /** - * @private - * @type {module:ol/transform~Transform} - */ - this.coordinateToPixelTransform_ = createTransform(); - - /** - * @private - * @type {module:ol/transform~Transform} - */ - this.pixelToCoordinateTransform_ = createTransform(); - - /** - * @private - * @type {number} - */ - this.frameIndex_ = 0; - - /** - * @private - * @type {?module:ol/PluggableMap~FrameState} - */ - this.frameState_ = null; - - /** - * The extent at the previous 'moveend' event. - * @private - * @type {module:ol/extent~Extent} - */ - this.previousExtent_ = null; - - /** - * @private - * @type {?module:ol/events~EventsKey} - */ - this.viewPropertyListenerKey_ = null; - - /** - * @private - * @type {?module:ol/events~EventsKey} - */ - this.viewChangeListenerKey_ = null; - - /** - * @private - * @type {Array.} - */ - this.layerGroupPropertyListenerKeys_ = null; - - /** - * @private - * @type {!HTMLElement} - */ - this.viewport_ = document.createElement('DIV'); - this.viewport_.className = 'ol-viewport' + (TOUCH ? ' ol-touch' : ''); - this.viewport_.style.position = 'relative'; - this.viewport_.style.overflow = 'hidden'; - this.viewport_.style.width = '100%'; - this.viewport_.style.height = '100%'; - // prevent page zoom on IE >= 10 browsers - this.viewport_.style.msTouchAction = 'none'; - this.viewport_.style.touchAction = 'none'; - - /** - * @private - * @type {!HTMLElement} - */ - this.overlayContainer_ = document.createElement('DIV'); - this.overlayContainer_.className = 'ol-overlaycontainer'; - this.viewport_.appendChild(this.overlayContainer_); - - /** - * @private - * @type {!HTMLElement} - */ - this.overlayContainerStopEvent_ = document.createElement('DIV'); - this.overlayContainerStopEvent_.className = 'ol-overlaycontainer-stopevent'; - const overlayEvents = [ - EventType.CLICK, - EventType.DBLCLICK, - EventType.MOUSEDOWN, - EventType.TOUCHSTART, - EventType.MSPOINTERDOWN, - MapBrowserEventType.POINTERDOWN, - EventType.MOUSEWHEEL, - EventType.WHEEL - ]; - for (let i = 0, ii = overlayEvents.length; i < ii; ++i) { - listen(this.overlayContainerStopEvent_, overlayEvents[i], stopPropagation); - } - this.viewport_.appendChild(this.overlayContainerStopEvent_); - - /** - * @private - * @type {module:ol/MapBrowserEventHandler} - */ - this.mapBrowserEventHandler_ = new MapBrowserEventHandler(this, options.moveTolerance); - for (const key in MapBrowserEventType) { - listen(this.mapBrowserEventHandler_, MapBrowserEventType[key], - this.handleMapBrowserEvent, this); - } - - /** - * @private - * @type {HTMLElement|Document} - */ - this.keyboardEventTarget_ = optionsInternal.keyboardEventTarget; - - /** - * @private - * @type {Array.} - */ - this.keyHandlerKeys_ = null; - - listen(this.viewport_, EventType.CONTEXTMENU, this.handleBrowserEvent, this); - listen(this.viewport_, EventType.WHEEL, this.handleBrowserEvent, this); - listen(this.viewport_, EventType.MOUSEWHEEL, this.handleBrowserEvent, this); - - /** - * @type {module:ol/Collection.} - * @protected - */ - this.controls = optionsInternal.controls || new Collection(); - - /** - * @type {module:ol/Collection.} - * @protected - */ - this.interactions = optionsInternal.interactions || new Collection(); - - /** - * @type {module:ol/Collection.} - * @private - */ - this.overlays_ = optionsInternal.overlays; - - /** - * A lookup of overlays by id. - * @private - * @type {Object.} - */ - this.overlayIdIndex_ = {}; - - /** - * @type {module:ol/renderer/Map} - * @private - */ - this.renderer_ = this.createRenderer(); - - /** - * @type {function(Event)|undefined} - * @private - */ - this.handleResize_; - - /** - * @private - * @type {module:ol/coordinate~Coordinate} - */ - this.focus_ = null; - - /** - * @private - * @type {!Array.} - */ - this.postRenderFunctions_ = []; - - /** - * @private - * @type {module:ol/TileQueue} - */ - this.tileQueue_ = new TileQueue( - this.getTilePriority.bind(this), - this.handleTileChange_.bind(this)); - - /** - * Uids of features to skip at rendering time. - * @type {Object.} - * @private - */ - this.skippedFeatureUids_ = {}; - - listen( - this, getChangeEventType(MapProperty.LAYERGROUP), - this.handleLayerGroupChanged_, this); - listen(this, getChangeEventType(MapProperty.VIEW), - this.handleViewChanged_, this); - listen(this, getChangeEventType(MapProperty.SIZE), - this.handleSizeChanged_, this); - listen(this, getChangeEventType(MapProperty.TARGET), - this.handleTargetChanged_, this); - - // setProperties will trigger the rendering of the map if the map - // is "defined" already. - this.setProperties(optionsInternal.values); - - this.controls.forEach( /** - * @param {module:ol/control/Control} control Control. - * @this {module:ol/PluggableMap} + * @type {number} + * @private */ - (function(control) { - control.setMap(this); - }).bind(this)); + this.maxTilesLoading_ = options.maxTilesLoading !== undefined ? options.maxTilesLoading : 16; - listen(this.controls, CollectionEventType.ADD, /** - * @param {module:ol/Collection~CollectionEvent} event CollectionEvent. + * @type {boolean} + * @private */ - function(event) { - event.element.setMap(this); - }, this); + this.loadTilesWhileAnimating_ = + options.loadTilesWhileAnimating !== undefined ? + options.loadTilesWhileAnimating : false; - listen(this.controls, CollectionEventType.REMOVE, /** - * @param {module:ol/Collection~CollectionEvent} event CollectionEvent. + * @type {boolean} + * @private */ - function(event) { - event.element.setMap(null); - }, this); + this.loadTilesWhileInteracting_ = + options.loadTilesWhileInteracting !== undefined ? + options.loadTilesWhileInteracting : false; - this.interactions.forEach( /** - * @param {module:ol/interaction/Interaction} interaction Interaction. - * @this {module:ol/PluggableMap} + * @private + * @type {number} */ - (function(interaction) { - interaction.setMap(this); - }).bind(this)); + this.pixelRatio_ = options.pixelRatio !== undefined ? + options.pixelRatio : DEVICE_PIXEL_RATIO; - listen(this.interactions, CollectionEventType.ADD, /** - * @param {module:ol/Collection~CollectionEvent} event CollectionEvent. + * @private + * @type {number|undefined} */ - function(event) { - event.element.setMap(this); - }, this); + this.animationDelayKey_; - listen(this.interactions, CollectionEventType.REMOVE, /** - * @param {module:ol/Collection~CollectionEvent} event CollectionEvent. + * @private */ - function(event) { - event.element.setMap(null); - }, this); + this.animationDelay_ = function() { + this.animationDelayKey_ = undefined; + this.renderFrame_.call(this, Date.now()); + }.bind(this); - this.overlays_.forEach(this.addOverlayInternal_.bind(this)); - - listen(this.overlays_, CollectionEventType.ADD, /** - * @param {module:ol/Collection~CollectionEvent} event CollectionEvent. + * @private + * @type {module:ol/transform~Transform} */ - function(event) { - this.addOverlayInternal_(/** @type {module:ol/Overlay} */ (event.element)); - }, this); + this.coordinateToPixelTransform_ = createTransform(); - listen(this.overlays_, CollectionEventType.REMOVE, /** - * @param {module:ol/Collection~CollectionEvent} event CollectionEvent. + * @private + * @type {module:ol/transform~Transform} */ - function(event) { - const overlay = /** @type {module:ol/Overlay} */ (event.element); - const id = overlay.getId(); - if (id !== undefined) { - delete this.overlayIdIndex_[id.toString()]; - } - event.element.setMap(null); - }, this); + this.pixelToCoordinateTransform_ = createTransform(); -}; + /** + * @private + * @type {number} + */ + this.frameIndex_ = 0; -inherits(PluggableMap, BaseObject); + /** + * @private + * @type {?module:ol/PluggableMap~FrameState} + */ + this.frameState_ = null; + /** + * The extent at the previous 'moveend' event. + * @private + * @type {module:ol/extent~Extent} + */ + this.previousExtent_ = null; -PluggableMap.prototype.createRenderer = function() { - throw new Error('Use a map type that has a createRenderer method'); -}; + /** + * @private + * @type {?module:ol/events~EventsKey} + */ + this.viewPropertyListenerKey_ = null; + /** + * @private + * @type {?module:ol/events~EventsKey} + */ + this.viewChangeListenerKey_ = null; -/** - * Add the given control to the map. - * @param {module:ol/control/Control} control Control. - * @api - */ -PluggableMap.prototype.addControl = function(control) { - this.getControls().push(control); -}; + /** + * @private + * @type {Array.} + */ + this.layerGroupPropertyListenerKeys_ = null; + /** + * @private + * @type {!HTMLElement} + */ + this.viewport_ = document.createElement('DIV'); + this.viewport_.className = 'ol-viewport' + (TOUCH ? ' ol-touch' : ''); + this.viewport_.style.position = 'relative'; + this.viewport_.style.overflow = 'hidden'; + this.viewport_.style.width = '100%'; + this.viewport_.style.height = '100%'; + // prevent page zoom on IE >= 10 browsers + this.viewport_.style.msTouchAction = 'none'; + this.viewport_.style.touchAction = 'none'; -/** - * Add the given interaction to the map. - * @param {module:ol/interaction/Interaction} interaction Interaction to add. - * @api - */ -PluggableMap.prototype.addInteraction = function(interaction) { - this.getInteractions().push(interaction); -}; + /** + * @private + * @type {!HTMLElement} + */ + this.overlayContainer_ = document.createElement('DIV'); + this.overlayContainer_.className = 'ol-overlaycontainer'; + this.viewport_.appendChild(this.overlayContainer_); - -/** - * Adds the given layer to the top of this map. If you want to add a layer - * elsewhere in the stack, use `getLayers()` and the methods available on - * {@link module:ol/Collection~Collection}. - * @param {module:ol/layer/Base} layer Layer. - * @api - */ -PluggableMap.prototype.addLayer = function(layer) { - const layers = this.getLayerGroup().getLayers(); - layers.push(layer); -}; - - -/** - * Add the given overlay to the map. - * @param {module:ol/Overlay} overlay Overlay. - * @api - */ -PluggableMap.prototype.addOverlay = function(overlay) { - this.getOverlays().push(overlay); -}; - - -/** - * This deals with map's overlay collection changes. - * @param {module:ol/Overlay} overlay Overlay. - * @private - */ -PluggableMap.prototype.addOverlayInternal_ = function(overlay) { - const id = overlay.getId(); - if (id !== undefined) { - this.overlayIdIndex_[id.toString()] = overlay; - } - overlay.setMap(this); -}; - - -/** - * - * @inheritDoc - */ -PluggableMap.prototype.disposeInternal = function() { - this.mapBrowserEventHandler_.dispose(); - unlisten(this.viewport_, EventType.CONTEXTMENU, this.handleBrowserEvent, this); - unlisten(this.viewport_, EventType.WHEEL, this.handleBrowserEvent, this); - unlisten(this.viewport_, EventType.MOUSEWHEEL, this.handleBrowserEvent, this); - if (this.handleResize_ !== undefined) { - removeEventListener(EventType.RESIZE, this.handleResize_, false); - this.handleResize_ = undefined; - } - if (this.animationDelayKey_) { - cancelAnimationFrame(this.animationDelayKey_); - this.animationDelayKey_ = undefined; - } - this.setTarget(null); - BaseObject.prototype.disposeInternal.call(this); -}; - - -/** - * Detect features that intersect a pixel on the viewport, and execute a - * callback with each intersecting feature. Layers included in the detection can - * be configured through the `layerFilter` option in `opt_options`. - * @param {module:ol~Pixel} pixel Pixel. - * @param {function(this: S, (module:ol/Feature|module:ol/render/Feature), - * module:ol/layer/Layer): T} callback Feature callback. The callback will be - * called with two arguments. The first argument is one - * {@link module:ol/Feature feature} or - * {@link module:ol/render/Feature render feature} at the pixel, the second is - * the {@link module:ol/layer/Layer layer} of the feature and will be null for - * unmanaged layers. To stop detection, callback functions can return a - * truthy value. - * @param {module:ol/PluggableMap~AtPixelOptions=} opt_options Optional options. - * @return {T|undefined} Callback result, i.e. the return value of last - * callback execution, or the first truthy callback return value. - * @template S,T - * @api - */ -PluggableMap.prototype.forEachFeatureAtPixel = function(pixel, callback, opt_options) { - if (!this.frameState_) { - return; - } - const coordinate = this.getCoordinateFromPixel(pixel); - opt_options = opt_options !== undefined ? opt_options : {}; - const hitTolerance = opt_options.hitTolerance !== undefined ? - opt_options.hitTolerance * this.frameState_.pixelRatio : 0; - const layerFilter = opt_options.layerFilter !== undefined ? - opt_options.layerFilter : TRUE; - return this.renderer_.forEachFeatureAtCoordinate( - coordinate, this.frameState_, hitTolerance, callback, null, - layerFilter, null); -}; - - -/** - * Get all features that intersect a pixel on the viewport. - * @param {module:ol~Pixel} pixel Pixel. - * @param {module:ol/PluggableMap~AtPixelOptions=} opt_options Optional options. - * @return {Array.} The detected features or - * `null` if none were found. - * @api - */ -PluggableMap.prototype.getFeaturesAtPixel = function(pixel, opt_options) { - let features = null; - this.forEachFeatureAtPixel(pixel, function(feature) { - if (!features) { - features = []; + /** + * @private + * @type {!HTMLElement} + */ + this.overlayContainerStopEvent_ = document.createElement('DIV'); + this.overlayContainerStopEvent_.className = 'ol-overlaycontainer-stopevent'; + const overlayEvents = [ + EventType.CLICK, + EventType.DBLCLICK, + EventType.MOUSEDOWN, + EventType.TOUCHSTART, + EventType.MSPOINTERDOWN, + MapBrowserEventType.POINTERDOWN, + EventType.MOUSEWHEEL, + EventType.WHEEL + ]; + for (let i = 0, ii = overlayEvents.length; i < ii; ++i) { + listen(this.overlayContainerStopEvent_, overlayEvents[i], stopPropagation); } - features.push(feature); - }, opt_options); - return features; -}; + this.viewport_.appendChild(this.overlayContainerStopEvent_); -/** - * Detect layers that have a color value at a pixel on the viewport, and - * execute a callback with each matching layer. Layers included in the - * detection can be configured through `opt_layerFilter`. - * @param {module:ol~Pixel} pixel Pixel. - * @param {function(this: S, module:ol/layer/Layer, (Uint8ClampedArray|Uint8Array)): T} callback - * Layer callback. This callback will receive two arguments: first is the - * {@link module:ol/layer/Layer layer}, second argument is an array representing - * [R, G, B, A] pixel values (0 - 255) and will be `null` for layer types - * that do not currently support this argument. To stop detection, callback - * functions can return a truthy value. - * @param {module:ol/PluggableMap~AtPixelOptions=} opt_options Configuration options. - * @return {T|undefined} Callback result, i.e. the return value of last - * callback execution, or the first truthy callback return value. - * @template S,T - * @api - */ -PluggableMap.prototype.forEachLayerAtPixel = function(pixel, callback, opt_options) { - if (!this.frameState_) { - return; - } - const options = opt_options || {}; - const hitTolerance = options.hitTolerance !== undefined ? - opt_options.hitTolerance * this.frameState_.pixelRatio : 0; - const layerFilter = options.layerFilter || TRUE; - return this.renderer_.forEachLayerAtPixel( - pixel, this.frameState_, hitTolerance, callback, null, layerFilter, null); -}; - - -/** - * Detect if features intersect a pixel on the viewport. Layers included in the - * detection can be configured through `opt_layerFilter`. - * @param {module:ol~Pixel} pixel Pixel. - * @param {module:ol/PluggableMap~AtPixelOptions=} opt_options Optional options. - * @return {boolean} Is there a feature at the given pixel? - * @template U - * @api - */ -PluggableMap.prototype.hasFeatureAtPixel = function(pixel, opt_options) { - if (!this.frameState_) { - return false; - } - const coordinate = this.getCoordinateFromPixel(pixel); - opt_options = opt_options !== undefined ? opt_options : {}; - const layerFilter = opt_options.layerFilter !== undefined ? opt_options.layerFilter : TRUE; - const hitTolerance = opt_options.hitTolerance !== undefined ? - opt_options.hitTolerance * this.frameState_.pixelRatio : 0; - return this.renderer_.hasFeatureAtCoordinate( - coordinate, this.frameState_, hitTolerance, layerFilter, null); -}; - - -/** - * Returns the coordinate in view projection for a browser event. - * @param {Event} event Event. - * @return {module:ol/coordinate~Coordinate} Coordinate. - * @api - */ -PluggableMap.prototype.getEventCoordinate = function(event) { - return this.getCoordinateFromPixel(this.getEventPixel(event)); -}; - - -/** - * Returns the map pixel position for a browser event relative to the viewport. - * @param {Event} event Event. - * @return {module:ol~Pixel} Pixel. - * @api - */ -PluggableMap.prototype.getEventPixel = function(event) { - const viewportPosition = this.viewport_.getBoundingClientRect(); - const eventPosition = event.changedTouches ? event.changedTouches[0] : event; - return [ - eventPosition.clientX - viewportPosition.left, - eventPosition.clientY - viewportPosition.top - ]; -}; - - -/** - * Get the target in which this map is rendered. - * Note that this returns what is entered as an option or in setTarget: - * if that was an element, it returns an element; if a string, it returns that. - * @return {HTMLElement|string|undefined} The Element or id of the Element that the - * map is rendered in. - * @observable - * @api - */ -PluggableMap.prototype.getTarget = function() { - return /** @type {HTMLElement|string|undefined} */ (this.get(MapProperty.TARGET)); -}; - - -/** - * Get the DOM element into which this map is rendered. In contrast to - * `getTarget` this method always return an `Element`, or `null` if the - * map has no target. - * @return {HTMLElement} The element that the map is rendered in. - * @api - */ -PluggableMap.prototype.getTargetElement = function() { - const target = this.getTarget(); - if (target !== undefined) { - return typeof target === 'string' ? document.getElementById(target) : target; - } else { - return null; - } -}; - - -/** - * Get the coordinate for a given pixel. This returns a coordinate in the - * map view projection. - * @param {module:ol~Pixel} pixel Pixel position in the map viewport. - * @return {module:ol/coordinate~Coordinate} The coordinate for the pixel position. - * @api - */ -PluggableMap.prototype.getCoordinateFromPixel = function(pixel) { - const frameState = this.frameState_; - if (!frameState) { - return null; - } else { - return applyTransform(frameState.pixelToCoordinateTransform, pixel.slice()); - } -}; - - -/** - * Get the map controls. Modifying this collection changes the controls - * associated with the map. - * @return {module:ol/Collection.} Controls. - * @api - */ -PluggableMap.prototype.getControls = function() { - return this.controls; -}; - - -/** - * Get the map overlays. Modifying this collection changes the overlays - * associated with the map. - * @return {module:ol/Collection.} Overlays. - * @api - */ -PluggableMap.prototype.getOverlays = function() { - return this.overlays_; -}; - - -/** - * Get an overlay by its identifier (the value returned by overlay.getId()). - * Note that the index treats string and numeric identifiers as the same. So - * `map.getOverlayById(2)` will return an overlay with id `'2'` or `2`. - * @param {string|number} id Overlay identifier. - * @return {module:ol/Overlay} Overlay. - * @api - */ -PluggableMap.prototype.getOverlayById = function(id) { - const overlay = this.overlayIdIndex_[id.toString()]; - return overlay !== undefined ? overlay : null; -}; - - -/** - * Get the map interactions. Modifying this collection changes the interactions - * associated with the map. - * - * Interactions are used for e.g. pan, zoom and rotate. - * @return {module:ol/Collection.} Interactions. - * @api - */ -PluggableMap.prototype.getInteractions = function() { - return this.interactions; -}; - - -/** - * Get the layergroup associated with this map. - * @return {module:ol/layer/Group} A layer group containing the layers in this map. - * @observable - * @api - */ -PluggableMap.prototype.getLayerGroup = function() { - return ( - /** @type {module:ol/layer/Group} */ (this.get(MapProperty.LAYERGROUP)) - ); -}; - - -/** - * Get the collection of layers associated with this map. - * @return {!module:ol/Collection.} Layers. - * @api - */ -PluggableMap.prototype.getLayers = function() { - const layers = this.getLayerGroup().getLayers(); - return layers; -}; - - -/** - * Get the pixel for a coordinate. This takes a coordinate in the map view - * projection and returns the corresponding pixel. - * @param {module:ol/coordinate~Coordinate} coordinate A map coordinate. - * @return {module:ol~Pixel} A pixel position in the map viewport. - * @api - */ -PluggableMap.prototype.getPixelFromCoordinate = function(coordinate) { - const frameState = this.frameState_; - if (!frameState) { - return null; - } else { - return applyTransform(frameState.coordinateToPixelTransform, coordinate.slice(0, 2)); - } -}; - - -/** - * Get the map renderer. - * @return {module:ol/renderer/Map} Renderer - */ -PluggableMap.prototype.getRenderer = function() { - return this.renderer_; -}; - - -/** - * Get the size of this map. - * @return {module:ol/size~Size|undefined} The size in pixels of the map in the DOM. - * @observable - * @api - */ -PluggableMap.prototype.getSize = function() { - return ( - /** @type {module:ol/size~Size|undefined} */ (this.get(MapProperty.SIZE)) - ); -}; - - -/** - * Get the view associated with this map. A view manages properties such as - * center and resolution. - * @return {module:ol/View} The view that controls this map. - * @observable - * @api - */ -PluggableMap.prototype.getView = function() { - return ( - /** @type {module:ol/View} */ (this.get(MapProperty.VIEW)) - ); -}; - - -/** - * Get the element that serves as the map viewport. - * @return {HTMLElement} Viewport. - * @api - */ -PluggableMap.prototype.getViewport = function() { - return this.viewport_; -}; - - -/** - * Get the element that serves as the container for overlays. Elements added to - * this container will let mousedown and touchstart events through to the map, - * so clicks and gestures on an overlay will trigger {@link module:ol/MapBrowserEvent~MapBrowserEvent} - * events. - * @return {!HTMLElement} The map's overlay container. - */ -PluggableMap.prototype.getOverlayContainer = function() { - return this.overlayContainer_; -}; - - -/** - * Get the element that serves as a container for overlays that don't allow - * event propagation. Elements added to this container won't let mousedown and - * touchstart events through to the map, so clicks and gestures on an overlay - * don't trigger any {@link module:ol/MapBrowserEvent~MapBrowserEvent}. - * @return {!HTMLElement} The map's overlay container that stops events. - */ -PluggableMap.prototype.getOverlayContainerStopEvent = function() { - return this.overlayContainerStopEvent_; -}; - - -/** - * @param {module:ol/Tile} tile Tile. - * @param {string} tileSourceKey Tile source key. - * @param {module:ol/coordinate~Coordinate} tileCenter Tile center. - * @param {number} tileResolution Tile resolution. - * @return {number} Tile priority. - */ -PluggableMap.prototype.getTilePriority = function(tile, tileSourceKey, tileCenter, tileResolution) { - // Filter out tiles at higher zoom levels than the current zoom level, or that - // are outside the visible extent. - const frameState = this.frameState_; - if (!frameState || !(tileSourceKey in frameState.wantedTiles)) { - return DROP; - } - if (!frameState.wantedTiles[tileSourceKey][tile.getKey()]) { - return DROP; - } - // Prioritize the highest zoom level tiles closest to the focus. - // Tiles at higher zoom levels are prioritized using Math.log(tileResolution). - // Within a zoom level, tiles are prioritized by the distance in pixels - // between the center of the tile and the focus. The factor of 65536 means - // that the prioritization should behave as desired for tiles up to - // 65536 * Math.log(2) = 45426 pixels from the focus. - const deltaX = tileCenter[0] - frameState.focus[0]; - const deltaY = tileCenter[1] - frameState.focus[1]; - return 65536 * Math.log(tileResolution) + - Math.sqrt(deltaX * deltaX + deltaY * deltaY) / tileResolution; -}; - - -/** - * @param {Event} browserEvent Browser event. - * @param {string=} opt_type Type. - */ -PluggableMap.prototype.handleBrowserEvent = function(browserEvent, opt_type) { - const type = opt_type || browserEvent.type; - const mapBrowserEvent = new MapBrowserEvent(type, this, browserEvent); - this.handleMapBrowserEvent(mapBrowserEvent); -}; - - -/** - * @param {module:ol/MapBrowserEvent} mapBrowserEvent The event to handle. - */ -PluggableMap.prototype.handleMapBrowserEvent = function(mapBrowserEvent) { - if (!this.frameState_) { - // With no view defined, we cannot translate pixels into geographical - // coordinates so interactions cannot be used. - return; - } - this.focus_ = mapBrowserEvent.coordinate; - mapBrowserEvent.frameState = this.frameState_; - const interactionsArray = this.getInteractions().getArray(); - if (this.dispatchEvent(mapBrowserEvent) !== false) { - for (let i = interactionsArray.length - 1; i >= 0; i--) { - const interaction = interactionsArray[i]; - if (!interaction.getActive()) { - continue; - } - const cont = interaction.handleEvent(mapBrowserEvent); - if (!cont) { - break; - } + /** + * @private + * @type {module:ol/MapBrowserEventHandler} + */ + this.mapBrowserEventHandler_ = new MapBrowserEventHandler(this, options.moveTolerance); + for (const key in MapBrowserEventType) { + listen(this.mapBrowserEventHandler_, MapBrowserEventType[key], + this.handleMapBrowserEvent, this); } - } -}; + /** + * @private + * @type {HTMLElement|Document} + */ + this.keyboardEventTarget_ = optionsInternal.keyboardEventTarget; -/** - * @protected - */ -PluggableMap.prototype.handlePostRender = function() { - - const frameState = this.frameState_; - - // Manage the tile queue - // Image loads are expensive and a limited resource, so try to use them - // efficiently: - // * When the view is static we allow a large number of parallel tile loads - // to complete the frame as quickly as possible. - // * When animating or interacting, image loads can cause janks, so we reduce - // the maximum number of loads per frame and limit the number of parallel - // tile loads to remain reactive to view changes and to reduce the chance of - // loading tiles that will quickly disappear from view. - const tileQueue = this.tileQueue_; - if (!tileQueue.isEmpty()) { - let maxTotalLoading = this.maxTilesLoading_; - let maxNewLoads = maxTotalLoading; - if (frameState) { - const hints = frameState.viewHints; - if (hints[ViewHint.ANIMATING]) { - maxTotalLoading = this.loadTilesWhileAnimating_ ? 8 : 0; - maxNewLoads = 2; - } - if (hints[ViewHint.INTERACTING]) { - maxTotalLoading = this.loadTilesWhileInteracting_ ? 8 : 0; - maxNewLoads = 2; - } - } - if (tileQueue.getTilesLoading() < maxTotalLoading) { - tileQueue.reprioritize(); // FIXME only call if view has changed - tileQueue.loadMoreTiles(maxTotalLoading, maxNewLoads); - } - } - - const postRenderFunctions = this.postRenderFunctions_; - for (let i = 0, ii = postRenderFunctions.length; i < ii; ++i) { - postRenderFunctions[i](this, frameState); - } - postRenderFunctions.length = 0; -}; - - -/** - * @private - */ -PluggableMap.prototype.handleSizeChanged_ = function() { - this.render(); -}; - - -/** - * @private - */ -PluggableMap.prototype.handleTargetChanged_ = function() { - // target may be undefined, null, a string or an Element. - // If it's a string we convert it to an Element before proceeding. - // If it's not now an Element we remove the viewport from the DOM. - // If it's an Element we append the viewport element to it. - - let targetElement; - if (this.getTarget()) { - targetElement = this.getTargetElement(); - } - - if (this.keyHandlerKeys_) { - for (let i = 0, ii = this.keyHandlerKeys_.length; i < ii; ++i) { - unlistenByKey(this.keyHandlerKeys_[i]); - } + /** + * @private + * @type {Array.} + */ this.keyHandlerKeys_ = null; + + listen(this.viewport_, EventType.CONTEXTMENU, this.handleBrowserEvent, this); + listen(this.viewport_, EventType.WHEEL, this.handleBrowserEvent, this); + listen(this.viewport_, EventType.MOUSEWHEEL, this.handleBrowserEvent, this); + + /** + * @type {module:ol/Collection.} + * @protected + */ + this.controls = optionsInternal.controls || new Collection(); + + /** + * @type {module:ol/Collection.} + * @protected + */ + this.interactions = optionsInternal.interactions || new Collection(); + + /** + * @type {module:ol/Collection.} + * @private + */ + this.overlays_ = optionsInternal.overlays; + + /** + * A lookup of overlays by id. + * @private + * @type {Object.} + */ + this.overlayIdIndex_ = {}; + + /** + * @type {module:ol/renderer/Map} + * @private + */ + this.renderer_ = this.createRenderer(); + + /** + * @type {function(Event)|undefined} + * @private + */ + this.handleResize_; + + /** + * @private + * @type {module:ol/coordinate~Coordinate} + */ + this.focus_ = null; + + /** + * @private + * @type {!Array.} + */ + this.postRenderFunctions_ = []; + + /** + * @private + * @type {module:ol/TileQueue} + */ + this.tileQueue_ = new TileQueue( + this.getTilePriority.bind(this), + this.handleTileChange_.bind(this)); + + /** + * Uids of features to skip at rendering time. + * @type {Object.} + * @private + */ + this.skippedFeatureUids_ = {}; + + listen( + this, getChangeEventType(MapProperty.LAYERGROUP), + this.handleLayerGroupChanged_, this); + listen(this, getChangeEventType(MapProperty.VIEW), + this.handleViewChanged_, this); + listen(this, getChangeEventType(MapProperty.SIZE), + this.handleSizeChanged_, this); + listen(this, getChangeEventType(MapProperty.TARGET), + this.handleTargetChanged_, this); + + // setProperties will trigger the rendering of the map if the map + // is "defined" already. + this.setProperties(optionsInternal.values); + + this.controls.forEach( + /** + * @param {module:ol/control/Control} control Control. + * @this {module:ol/PluggableMap} + */ + (function(control) { + control.setMap(this); + }).bind(this)); + + listen(this.controls, CollectionEventType.ADD, + /** + * @param {module:ol/Collection~CollectionEvent} event CollectionEvent. + */ + function(event) { + event.element.setMap(this); + }, this); + + listen(this.controls, CollectionEventType.REMOVE, + /** + * @param {module:ol/Collection~CollectionEvent} event CollectionEvent. + */ + function(event) { + event.element.setMap(null); + }, this); + + this.interactions.forEach( + /** + * @param {module:ol/interaction/Interaction} interaction Interaction. + * @this {module:ol/PluggableMap} + */ + (function(interaction) { + interaction.setMap(this); + }).bind(this)); + + listen(this.interactions, CollectionEventType.ADD, + /** + * @param {module:ol/Collection~CollectionEvent} event CollectionEvent. + */ + function(event) { + event.element.setMap(this); + }, this); + + listen(this.interactions, CollectionEventType.REMOVE, + /** + * @param {module:ol/Collection~CollectionEvent} event CollectionEvent. + */ + function(event) { + event.element.setMap(null); + }, this); + + this.overlays_.forEach(this.addOverlayInternal_.bind(this)); + + listen(this.overlays_, CollectionEventType.ADD, + /** + * @param {module:ol/Collection~CollectionEvent} event CollectionEvent. + */ + function(event) { + this.addOverlayInternal_(/** @type {module:ol/Overlay} */ (event.element)); + }, this); + + listen(this.overlays_, CollectionEventType.REMOVE, + /** + * @param {module:ol/Collection~CollectionEvent} event CollectionEvent. + */ + function(event) { + const overlay = /** @type {module:ol/Overlay} */ (event.element); + const id = overlay.getId(); + if (id !== undefined) { + delete this.overlayIdIndex_[id.toString()]; + } + event.element.setMap(null); + }, this); + } - if (!targetElement) { - this.renderer_.removeLayerRenderers(); - removeNode(this.viewport_); + createRenderer() { + throw new Error('Use a map type that has a createRenderer method'); + } + + /** + * Add the given control to the map. + * @param {module:ol/control/Control} control Control. + * @api + */ + addControl(control) { + this.getControls().push(control); + } + + /** + * Add the given interaction to the map. + * @param {module:ol/interaction/Interaction} interaction Interaction to add. + * @api + */ + addInteraction(interaction) { + this.getInteractions().push(interaction); + } + + /** + * Adds the given layer to the top of this map. If you want to add a layer + * elsewhere in the stack, use `getLayers()` and the methods available on + * {@link module:ol/Collection~Collection}. + * @param {module:ol/layer/Base} layer Layer. + * @api + */ + addLayer(layer) { + const layers = this.getLayerGroup().getLayers(); + layers.push(layer); + } + + /** + * Add the given overlay to the map. + * @param {module:ol/Overlay} overlay Overlay. + * @api + */ + addOverlay(overlay) { + this.getOverlays().push(overlay); + } + + /** + * This deals with map's overlay collection changes. + * @param {module:ol/Overlay} overlay Overlay. + * @private + */ + addOverlayInternal_(overlay) { + const id = overlay.getId(); + if (id !== undefined) { + this.overlayIdIndex_[id.toString()] = overlay; + } + overlay.setMap(this); + } + + /** + * + * @inheritDoc + */ + disposeInternal() { + this.mapBrowserEventHandler_.dispose(); + unlisten(this.viewport_, EventType.CONTEXTMENU, this.handleBrowserEvent, this); + unlisten(this.viewport_, EventType.WHEEL, this.handleBrowserEvent, this); + unlisten(this.viewport_, EventType.MOUSEWHEEL, this.handleBrowserEvent, this); if (this.handleResize_ !== undefined) { removeEventListener(EventType.RESIZE, this.handleResize_, false); this.handleResize_ = undefined; } - } else { - targetElement.appendChild(this.viewport_); - - const keyboardEventTarget = !this.keyboardEventTarget_ ? - targetElement : this.keyboardEventTarget_; - this.keyHandlerKeys_ = [ - listen(keyboardEventTarget, EventType.KEYDOWN, this.handleBrowserEvent, this), - listen(keyboardEventTarget, EventType.KEYPRESS, this.handleBrowserEvent, this) - ]; - - if (!this.handleResize_) { - this.handleResize_ = this.updateSize.bind(this); - addEventListener(EventType.RESIZE, this.handleResize_, false); + if (this.animationDelayKey_) { + cancelAnimationFrame(this.animationDelayKey_); + this.animationDelayKey_ = undefined; } + this.setTarget(null); + BaseObject.prototype.disposeInternal.call(this); } - this.updateSize(); - // updateSize calls setSize, so no need to call this.render - // ourselves here. -}; - - -/** - * @private - */ -PluggableMap.prototype.handleTileChange_ = function() { - this.render(); -}; - - -/** - * @private - */ -PluggableMap.prototype.handleViewPropertyChanged_ = function() { - this.render(); -}; - - -/** - * @private - */ -PluggableMap.prototype.handleViewChanged_ = function() { - if (this.viewPropertyListenerKey_) { - unlistenByKey(this.viewPropertyListenerKey_); - this.viewPropertyListenerKey_ = null; + /** + * Detect features that intersect a pixel on the viewport, and execute a + * callback with each intersecting feature. Layers included in the detection can + * be configured through the `layerFilter` option in `opt_options`. + * @param {module:ol~Pixel} pixel Pixel. + * @param {function(this: S, (module:ol/Feature|module:ol/render/Feature), + * module:ol/layer/Layer): T} callback Feature callback. The callback will be + * called with two arguments. The first argument is one + * {@link module:ol/Feature feature} or + * {@link module:ol/render/Feature render feature} at the pixel, the second is + * the {@link module:ol/layer/Layer layer} of the feature and will be null for + * unmanaged layers. To stop detection, callback functions can return a + * truthy value. + * @param {module:ol/PluggableMap~AtPixelOptions=} opt_options Optional options. + * @return {T|undefined} Callback result, i.e. the return value of last + * callback execution, or the first truthy callback return value. + * @template S,T + * @api + */ + forEachFeatureAtPixel(pixel, callback, opt_options) { + if (!this.frameState_) { + return; + } + const coordinate = this.getCoordinateFromPixel(pixel); + opt_options = opt_options !== undefined ? opt_options : {}; + const hitTolerance = opt_options.hitTolerance !== undefined ? + opt_options.hitTolerance * this.frameState_.pixelRatio : 0; + const layerFilter = opt_options.layerFilter !== undefined ? + opt_options.layerFilter : TRUE; + return this.renderer_.forEachFeatureAtCoordinate( + coordinate, this.frameState_, hitTolerance, callback, null, + layerFilter, null); } - if (this.viewChangeListenerKey_) { - unlistenByKey(this.viewChangeListenerKey_); - this.viewChangeListenerKey_ = null; - } - const view = this.getView(); - if (view) { - this.viewport_.setAttribute('data-view', getUid(view)); - this.viewPropertyListenerKey_ = listen( - view, ObjectEventType.PROPERTYCHANGE, - this.handleViewPropertyChanged_, this); - this.viewChangeListenerKey_ = listen( - view, EventType.CHANGE, - this.handleViewPropertyChanged_, this); - } - this.render(); -}; - -/** - * @private - */ -PluggableMap.prototype.handleLayerGroupChanged_ = function() { - if (this.layerGroupPropertyListenerKeys_) { - this.layerGroupPropertyListenerKeys_.forEach(unlistenByKey); - this.layerGroupPropertyListenerKeys_ = null; + /** + * Get all features that intersect a pixel on the viewport. + * @param {module:ol~Pixel} pixel Pixel. + * @param {module:ol/PluggableMap~AtPixelOptions=} opt_options Optional options. + * @return {Array.} The detected features or + * `null` if none were found. + * @api + */ + getFeaturesAtPixel(pixel, opt_options) { + let features = null; + this.forEachFeatureAtPixel(pixel, function(feature) { + if (!features) { + features = []; + } + features.push(feature); + }, opt_options); + return features; } - const layerGroup = this.getLayerGroup(); - if (layerGroup) { - this.layerGroupPropertyListenerKeys_ = [ - listen( - layerGroup, ObjectEventType.PROPERTYCHANGE, - this.render, this), - listen( - layerGroup, EventType.CHANGE, - this.render, this) + + /** + * Detect layers that have a color value at a pixel on the viewport, and + * execute a callback with each matching layer. Layers included in the + * detection can be configured through `opt_layerFilter`. + * @param {module:ol~Pixel} pixel Pixel. + * @param {function(this: S, module:ol/layer/Layer, (Uint8ClampedArray|Uint8Array)): T} callback + * Layer callback. This callback will receive two arguments: first is the + * {@link module:ol/layer/Layer layer}, second argument is an array representing + * [R, G, B, A] pixel values (0 - 255) and will be `null` for layer types + * that do not currently support this argument. To stop detection, callback + * functions can return a truthy value. + * @param {module:ol/PluggableMap~AtPixelOptions=} opt_options Configuration options. + * @return {T|undefined} Callback result, i.e. the return value of last + * callback execution, or the first truthy callback return value. + * @template S,T + * @api + */ + forEachLayerAtPixel(pixel, callback, opt_options) { + if (!this.frameState_) { + return; + } + const options = opt_options || {}; + const hitTolerance = options.hitTolerance !== undefined ? + opt_options.hitTolerance * this.frameState_.pixelRatio : 0; + const layerFilter = options.layerFilter || TRUE; + return this.renderer_.forEachLayerAtPixel( + pixel, this.frameState_, hitTolerance, callback, null, layerFilter, null); + } + + /** + * Detect if features intersect a pixel on the viewport. Layers included in the + * detection can be configured through `opt_layerFilter`. + * @param {module:ol~Pixel} pixel Pixel. + * @param {module:ol/PluggableMap~AtPixelOptions=} opt_options Optional options. + * @return {boolean} Is there a feature at the given pixel? + * @template U + * @api + */ + hasFeatureAtPixel(pixel, opt_options) { + if (!this.frameState_) { + return false; + } + const coordinate = this.getCoordinateFromPixel(pixel); + opt_options = opt_options !== undefined ? opt_options : {}; + const layerFilter = opt_options.layerFilter !== undefined ? opt_options.layerFilter : TRUE; + const hitTolerance = opt_options.hitTolerance !== undefined ? + opt_options.hitTolerance * this.frameState_.pixelRatio : 0; + return this.renderer_.hasFeatureAtCoordinate( + coordinate, this.frameState_, hitTolerance, layerFilter, null); + } + + /** + * Returns the coordinate in view projection for a browser event. + * @param {Event} event Event. + * @return {module:ol/coordinate~Coordinate} Coordinate. + * @api + */ + getEventCoordinate(event) { + return this.getCoordinateFromPixel(this.getEventPixel(event)); + } + + /** + * Returns the map pixel position for a browser event relative to the viewport. + * @param {Event} event Event. + * @return {module:ol~Pixel} Pixel. + * @api + */ + getEventPixel(event) { + const viewportPosition = this.viewport_.getBoundingClientRect(); + const eventPosition = event.changedTouches ? event.changedTouches[0] : event; + return [ + eventPosition.clientX - viewportPosition.left, + eventPosition.clientY - viewportPosition.top ]; } - this.render(); -}; - -/** - * @return {boolean} Is rendered. - */ -PluggableMap.prototype.isRendered = function() { - return !!this.frameState_; -}; - - -/** - * Requests an immediate render in a synchronous manner. - * @api - */ -PluggableMap.prototype.renderSync = function() { - if (this.animationDelayKey_) { - cancelAnimationFrame(this.animationDelayKey_); + /** + * Get the target in which this map is rendered. + * Note that this returns what is entered as an option or in setTarget: + * if that was an element, it returns an element; if a string, it returns that. + * @return {HTMLElement|string|undefined} The Element or id of the Element that the + * map is rendered in. + * @observable + * @api + */ + getTarget() { + return /** @type {HTMLElement|string|undefined} */ (this.get(MapProperty.TARGET)); } - this.animationDelay_(); -}; - -/** - * Request a map rendering (at the next animation frame). - * @api - */ -PluggableMap.prototype.render = function() { - if (this.animationDelayKey_ === undefined) { - this.animationDelayKey_ = requestAnimationFrame(this.animationDelay_); - } -}; - - -/** - * Remove the given control from the map. - * @param {module:ol/control/Control} control Control. - * @return {module:ol/control/Control|undefined} The removed control (or undefined - * if the control was not found). - * @api - */ -PluggableMap.prototype.removeControl = function(control) { - return this.getControls().remove(control); -}; - - -/** - * Remove the given interaction from the map. - * @param {module:ol/interaction/Interaction} interaction Interaction to remove. - * @return {module:ol/interaction/Interaction|undefined} The removed interaction (or - * undefined if the interaction was not found). - * @api - */ -PluggableMap.prototype.removeInteraction = function(interaction) { - return this.getInteractions().remove(interaction); -}; - - -/** - * Removes the given layer from the map. - * @param {module:ol/layer/Base} layer Layer. - * @return {module:ol/layer/Base|undefined} The removed layer (or undefined if the - * layer was not found). - * @api - */ -PluggableMap.prototype.removeLayer = function(layer) { - const layers = this.getLayerGroup().getLayers(); - return layers.remove(layer); -}; - - -/** - * Remove the given overlay from the map. - * @param {module:ol/Overlay} overlay Overlay. - * @return {module:ol/Overlay|undefined} The removed overlay (or undefined - * if the overlay was not found). - * @api - */ -PluggableMap.prototype.removeOverlay = function(overlay) { - return this.getOverlays().remove(overlay); -}; - - -/** - * @param {number} time Time. - * @private - */ -PluggableMap.prototype.renderFrame_ = function(time) { - let viewState; - - const size = this.getSize(); - const view = this.getView(); - const extent = createEmpty(); - const previousFrameState = this.frameState_; - /** @type {?module:ol/PluggableMap~FrameState} */ - let frameState = null; - if (size !== undefined && hasArea(size) && view && view.isDef()) { - const viewHints = view.getHints(this.frameState_ ? this.frameState_.viewHints : undefined); - const layerStatesArray = this.getLayerGroup().getLayerStatesArray(); - const layerStates = {}; - for (let i = 0, ii = layerStatesArray.length; i < ii; ++i) { - layerStates[getUid(layerStatesArray[i].layer)] = layerStatesArray[i]; + /** + * Get the DOM element into which this map is rendered. In contrast to + * `getTarget` this method always return an `Element`, or `null` if the + * map has no target. + * @return {HTMLElement} The element that the map is rendered in. + * @api + */ + getTargetElement() { + const target = this.getTarget(); + if (target !== undefined) { + return typeof target === 'string' ? document.getElementById(target) : target; + } else { + return null; } - viewState = view.getState(); - let focus = this.focus_; - if (!focus) { - focus = viewState.center; - const pixelResolution = viewState.resolution / this.pixelRatio_; - focus[0] = Math.round(focus[0] / pixelResolution) * pixelResolution; - focus[1] = Math.round(focus[1] / pixelResolution) * pixelResolution; - } - frameState = /** @type {module:ol/PluggableMap~FrameState} */ ({ - animate: false, - coordinateToPixelTransform: this.coordinateToPixelTransform_, - extent: extent, - focus: focus, - index: this.frameIndex_++, - layerStates: layerStates, - layerStatesArray: layerStatesArray, - pixelRatio: this.pixelRatio_, - pixelToCoordinateTransform: this.pixelToCoordinateTransform_, - postRenderFunctions: [], - size: size, - skippedFeatureUids: this.skippedFeatureUids_, - tileQueue: this.tileQueue_, - time: time, - usedTiles: {}, - viewState: viewState, - viewHints: viewHints, - wantedTiles: {} - }); } - if (frameState) { - frameState.extent = getForViewAndSize(viewState.center, - viewState.resolution, viewState.rotation, frameState.size, extent); + /** + * Get the coordinate for a given pixel. This returns a coordinate in the + * map view projection. + * @param {module:ol~Pixel} pixel Pixel position in the map viewport. + * @return {module:ol/coordinate~Coordinate} The coordinate for the pixel position. + * @api + */ + getCoordinateFromPixel(pixel) { + const frameState = this.frameState_; + if (!frameState) { + return null; + } else { + return applyTransform(frameState.pixelToCoordinateTransform, pixel.slice()); + } } - this.frameState_ = frameState; - this.renderer_.renderFrame(frameState); + /** + * Get the map controls. Modifying this collection changes the controls + * associated with the map. + * @return {module:ol/Collection.} Controls. + * @api + */ + getControls() { + return this.controls; + } - if (frameState) { - if (frameState.animate) { - this.render(); + /** + * Get the map overlays. Modifying this collection changes the overlays + * associated with the map. + * @return {module:ol/Collection.} Overlays. + * @api + */ + getOverlays() { + return this.overlays_; + } + + /** + * Get an overlay by its identifier (the value returned by overlay.getId()). + * Note that the index treats string and numeric identifiers as the same. So + * `map.getOverlayById(2)` will return an overlay with id `'2'` or `2`. + * @param {string|number} id Overlay identifier. + * @return {module:ol/Overlay} Overlay. + * @api + */ + getOverlayById(id) { + const overlay = this.overlayIdIndex_[id.toString()]; + return overlay !== undefined ? overlay : null; + } + + /** + * Get the map interactions. Modifying this collection changes the interactions + * associated with the map. + * + * Interactions are used for e.g. pan, zoom and rotate. + * @return {module:ol/Collection.} Interactions. + * @api + */ + getInteractions() { + return this.interactions; + } + + /** + * Get the layergroup associated with this map. + * @return {module:ol/layer/Group} A layer group containing the layers in this map. + * @observable + * @api + */ + getLayerGroup() { + return ( + /** @type {module:ol/layer/Group} */ (this.get(MapProperty.LAYERGROUP)) + ); + } + + /** + * Get the collection of layers associated with this map. + * @return {!module:ol/Collection.} Layers. + * @api + */ + getLayers() { + const layers = this.getLayerGroup().getLayers(); + return layers; + } + + /** + * Get the pixel for a coordinate. This takes a coordinate in the map view + * projection and returns the corresponding pixel. + * @param {module:ol/coordinate~Coordinate} coordinate A map coordinate. + * @return {module:ol~Pixel} A pixel position in the map viewport. + * @api + */ + getPixelFromCoordinate(coordinate) { + const frameState = this.frameState_; + if (!frameState) { + return null; + } else { + return applyTransform(frameState.coordinateToPixelTransform, coordinate.slice(0, 2)); } - Array.prototype.push.apply(this.postRenderFunctions_, frameState.postRenderFunctions); + } - if (previousFrameState) { - const moveStart = !this.previousExtent_ || - (!isEmpty(this.previousExtent_) && - !equals(frameState.extent, this.previousExtent_)); - if (moveStart) { - this.dispatchEvent( - new MapEvent(MapEventType.MOVESTART, this, previousFrameState)); - this.previousExtent_ = createOrUpdateEmpty(this.previousExtent_); + /** + * Get the map renderer. + * @return {module:ol/renderer/Map} Renderer + */ + getRenderer() { + return this.renderer_; + } + + /** + * Get the size of this map. + * @return {module:ol/size~Size|undefined} The size in pixels of the map in the DOM. + * @observable + * @api + */ + getSize() { + return ( + /** @type {module:ol/size~Size|undefined} */ (this.get(MapProperty.SIZE)) + ); + } + + /** + * Get the view associated with this map. A view manages properties such as + * center and resolution. + * @return {module:ol/View} The view that controls this map. + * @observable + * @api + */ + getView() { + return ( + /** @type {module:ol/View} */ (this.get(MapProperty.VIEW)) + ); + } + + /** + * Get the element that serves as the map viewport. + * @return {HTMLElement} Viewport. + * @api + */ + getViewport() { + return this.viewport_; + } + + /** + * Get the element that serves as the container for overlays. Elements added to + * this container will let mousedown and touchstart events through to the map, + * so clicks and gestures on an overlay will trigger {@link module:ol/MapBrowserEvent~MapBrowserEvent} + * events. + * @return {!HTMLElement} The map's overlay container. + */ + getOverlayContainer() { + return this.overlayContainer_; + } + + /** + * Get the element that serves as a container for overlays that don't allow + * event propagation. Elements added to this container won't let mousedown and + * touchstart events through to the map, so clicks and gestures on an overlay + * don't trigger any {@link module:ol/MapBrowserEvent~MapBrowserEvent}. + * @return {!HTMLElement} The map's overlay container that stops events. + */ + getOverlayContainerStopEvent() { + return this.overlayContainerStopEvent_; + } + + /** + * @param {module:ol/Tile} tile Tile. + * @param {string} tileSourceKey Tile source key. + * @param {module:ol/coordinate~Coordinate} tileCenter Tile center. + * @param {number} tileResolution Tile resolution. + * @return {number} Tile priority. + */ + getTilePriority(tile, tileSourceKey, tileCenter, tileResolution) { + // Filter out tiles at higher zoom levels than the current zoom level, or that + // are outside the visible extent. + const frameState = this.frameState_; + if (!frameState || !(tileSourceKey in frameState.wantedTiles)) { + return DROP; + } + if (!frameState.wantedTiles[tileSourceKey][tile.getKey()]) { + return DROP; + } + // Prioritize the highest zoom level tiles closest to the focus. + // Tiles at higher zoom levels are prioritized using Math.log(tileResolution). + // Within a zoom level, tiles are prioritized by the distance in pixels + // between the center of the tile and the focus. The factor of 65536 means + // that the prioritization should behave as desired for tiles up to + // 65536 * Math.log(2) = 45426 pixels from the focus. + const deltaX = tileCenter[0] - frameState.focus[0]; + const deltaY = tileCenter[1] - frameState.focus[1]; + return 65536 * Math.log(tileResolution) + + Math.sqrt(deltaX * deltaX + deltaY * deltaY) / tileResolution; + } + + /** + * @param {Event} browserEvent Browser event. + * @param {string=} opt_type Type. + */ + handleBrowserEvent(browserEvent, opt_type) { + const type = opt_type || browserEvent.type; + const mapBrowserEvent = new MapBrowserEvent(type, this, browserEvent); + this.handleMapBrowserEvent(mapBrowserEvent); + } + + /** + * @param {module:ol/MapBrowserEvent} mapBrowserEvent The event to handle. + */ + handleMapBrowserEvent(mapBrowserEvent) { + if (!this.frameState_) { + // With no view defined, we cannot translate pixels into geographical + // coordinates so interactions cannot be used. + return; + } + this.focus_ = mapBrowserEvent.coordinate; + mapBrowserEvent.frameState = this.frameState_; + const interactionsArray = this.getInteractions().getArray(); + if (this.dispatchEvent(mapBrowserEvent) !== false) { + for (let i = interactionsArray.length - 1; i >= 0; i--) { + const interaction = interactionsArray[i]; + if (!interaction.getActive()) { + continue; + } + const cont = interaction.handleEvent(mapBrowserEvent); + if (!cont) { + break; + } + } + } + } + + /** + * @protected + */ + handlePostRender() { + + const frameState = this.frameState_; + + // Manage the tile queue + // Image loads are expensive and a limited resource, so try to use them + // efficiently: + // * When the view is static we allow a large number of parallel tile loads + // to complete the frame as quickly as possible. + // * When animating or interacting, image loads can cause janks, so we reduce + // the maximum number of loads per frame and limit the number of parallel + // tile loads to remain reactive to view changes and to reduce the chance of + // loading tiles that will quickly disappear from view. + const tileQueue = this.tileQueue_; + if (!tileQueue.isEmpty()) { + let maxTotalLoading = this.maxTilesLoading_; + let maxNewLoads = maxTotalLoading; + if (frameState) { + const hints = frameState.viewHints; + if (hints[ViewHint.ANIMATING]) { + maxTotalLoading = this.loadTilesWhileAnimating_ ? 8 : 0; + maxNewLoads = 2; + } + if (hints[ViewHint.INTERACTING]) { + maxTotalLoading = this.loadTilesWhileInteracting_ ? 8 : 0; + maxNewLoads = 2; + } + } + if (tileQueue.getTilesLoading() < maxTotalLoading) { + tileQueue.reprioritize(); // FIXME only call if view has changed + tileQueue.loadMoreTiles(maxTotalLoading, maxNewLoads); } } - const idle = this.previousExtent_ && - !frameState.viewHints[ViewHint.ANIMATING] && - !frameState.viewHints[ViewHint.INTERACTING] && - !equals(frameState.extent, this.previousExtent_); + const postRenderFunctions = this.postRenderFunctions_; + for (let i = 0, ii = postRenderFunctions.length; i < ii; ++i) { + postRenderFunctions[i](this, frameState); + } + postRenderFunctions.length = 0; + } - if (idle) { - this.dispatchEvent(new MapEvent(MapEventType.MOVEEND, this, frameState)); - clone(frameState.extent, this.previousExtent_); + /** + * @private + */ + handleSizeChanged_() { + this.render(); + } + + /** + * @private + */ + handleTargetChanged_() { + // target may be undefined, null, a string or an Element. + // If it's a string we convert it to an Element before proceeding. + // If it's not now an Element we remove the viewport from the DOM. + // If it's an Element we append the viewport element to it. + + let targetElement; + if (this.getTarget()) { + targetElement = this.getTargetElement(); + } + + if (this.keyHandlerKeys_) { + for (let i = 0, ii = this.keyHandlerKeys_.length; i < ii; ++i) { + unlistenByKey(this.keyHandlerKeys_[i]); + } + this.keyHandlerKeys_ = null; + } + + if (!targetElement) { + this.renderer_.removeLayerRenderers(); + removeNode(this.viewport_); + if (this.handleResize_ !== undefined) { + removeEventListener(EventType.RESIZE, this.handleResize_, false); + this.handleResize_ = undefined; + } + } else { + targetElement.appendChild(this.viewport_); + + const keyboardEventTarget = !this.keyboardEventTarget_ ? + targetElement : this.keyboardEventTarget_; + this.keyHandlerKeys_ = [ + listen(keyboardEventTarget, EventType.KEYDOWN, this.handleBrowserEvent, this), + listen(keyboardEventTarget, EventType.KEYPRESS, this.handleBrowserEvent, this) + ]; + + if (!this.handleResize_) { + this.handleResize_ = this.updateSize.bind(this); + addEventListener(EventType.RESIZE, this.handleResize_, false); + } + } + + this.updateSize(); + // updateSize calls setSize, so no need to call this.render + // ourselves here. + } + + /** + * @private + */ + handleTileChange_() { + this.render(); + } + + /** + * @private + */ + handleViewPropertyChanged_() { + this.render(); + } + + /** + * @private + */ + handleViewChanged_() { + if (this.viewPropertyListenerKey_) { + unlistenByKey(this.viewPropertyListenerKey_); + this.viewPropertyListenerKey_ = null; + } + if (this.viewChangeListenerKey_) { + unlistenByKey(this.viewChangeListenerKey_); + this.viewChangeListenerKey_ = null; + } + const view = this.getView(); + if (view) { + this.viewport_.setAttribute('data-view', getUid(view)); + this.viewPropertyListenerKey_ = listen( + view, ObjectEventType.PROPERTYCHANGE, + this.handleViewPropertyChanged_, this); + this.viewChangeListenerKey_ = listen( + view, EventType.CHANGE, + this.handleViewPropertyChanged_, this); + } + this.render(); + } + + /** + * @private + */ + handleLayerGroupChanged_() { + if (this.layerGroupPropertyListenerKeys_) { + this.layerGroupPropertyListenerKeys_.forEach(unlistenByKey); + this.layerGroupPropertyListenerKeys_ = null; + } + const layerGroup = this.getLayerGroup(); + if (layerGroup) { + this.layerGroupPropertyListenerKeys_ = [ + listen( + layerGroup, ObjectEventType.PROPERTYCHANGE, + this.render, this), + listen( + layerGroup, EventType.CHANGE, + this.render, this) + ]; + } + this.render(); + } + + /** + * @return {boolean} Is rendered. + */ + isRendered() { + return !!this.frameState_; + } + + /** + * Requests an immediate render in a synchronous manner. + * @api + */ + renderSync() { + if (this.animationDelayKey_) { + cancelAnimationFrame(this.animationDelayKey_); + } + this.animationDelay_(); + } + + /** + * Request a map rendering (at the next animation frame). + * @api + */ + render() { + if (this.animationDelayKey_ === undefined) { + this.animationDelayKey_ = requestAnimationFrame(this.animationDelay_); } } - this.dispatchEvent(new MapEvent(MapEventType.POSTRENDER, this, frameState)); - - setTimeout(this.handlePostRender.bind(this), 0); - -}; - - -/** - * Sets the layergroup of this map. - * @param {module:ol/layer/Group} layerGroup A layer group containing the layers in this map. - * @observable - * @api - */ -PluggableMap.prototype.setLayerGroup = function(layerGroup) { - this.set(MapProperty.LAYERGROUP, layerGroup); -}; - - -/** - * Set the size of this map. - * @param {module:ol/size~Size|undefined} size The size in pixels of the map in the DOM. - * @observable - * @api - */ -PluggableMap.prototype.setSize = function(size) { - this.set(MapProperty.SIZE, size); -}; - - -/** - * Set the target element to render this map into. - * @param {HTMLElement|string|undefined} target The Element or id of the Element - * that the map is rendered in. - * @observable - * @api - */ -PluggableMap.prototype.setTarget = function(target) { - this.set(MapProperty.TARGET, target); -}; - - -/** - * Set the view for this map. - * @param {module:ol/View} view The view that controls this map. - * @observable - * @api - */ -PluggableMap.prototype.setView = function(view) { - this.set(MapProperty.VIEW, view); -}; - - -/** - * @param {module:ol/Feature} feature Feature. - */ -PluggableMap.prototype.skipFeature = function(feature) { - const featureUid = getUid(feature).toString(); - this.skippedFeatureUids_[featureUid] = true; - this.render(); -}; - - -/** - * Force a recalculation of the map viewport size. This should be called when - * third-party code changes the size of the map viewport. - * @api - */ -PluggableMap.prototype.updateSize = function() { - const targetElement = this.getTargetElement(); - - if (!targetElement) { - this.setSize(undefined); - } else { - const computedStyle = getComputedStyle(targetElement); - this.setSize([ - targetElement.offsetWidth - - parseFloat(computedStyle['borderLeftWidth']) - - parseFloat(computedStyle['paddingLeft']) - - parseFloat(computedStyle['paddingRight']) - - parseFloat(computedStyle['borderRightWidth']), - targetElement.offsetHeight - - parseFloat(computedStyle['borderTopWidth']) - - parseFloat(computedStyle['paddingTop']) - - parseFloat(computedStyle['paddingBottom']) - - parseFloat(computedStyle['borderBottomWidth']) - ]); + /** + * Remove the given control from the map. + * @param {module:ol/control/Control} control Control. + * @return {module:ol/control/Control|undefined} The removed control (or undefined + * if the control was not found). + * @api + */ + removeControl(control) { + return this.getControls().remove(control); } -}; + /** + * Remove the given interaction from the map. + * @param {module:ol/interaction/Interaction} interaction Interaction to remove. + * @return {module:ol/interaction/Interaction|undefined} The removed interaction (or + * undefined if the interaction was not found). + * @api + */ + removeInteraction(interaction) { + return this.getInteractions().remove(interaction); + } -/** - * @param {module:ol/Feature} feature Feature. - */ -PluggableMap.prototype.unskipFeature = function(feature) { - const featureUid = getUid(feature).toString(); - delete this.skippedFeatureUids_[featureUid]; - this.render(); -}; + /** + * Removes the given layer from the map. + * @param {module:ol/layer/Base} layer Layer. + * @return {module:ol/layer/Base|undefined} The removed layer (or undefined if the + * layer was not found). + * @api + */ + removeLayer(layer) { + const layers = this.getLayerGroup().getLayers(); + return layers.remove(layer); + } + + /** + * Remove the given overlay from the map. + * @param {module:ol/Overlay} overlay Overlay. + * @return {module:ol/Overlay|undefined} The removed overlay (or undefined + * if the overlay was not found). + * @api + */ + removeOverlay(overlay) { + return this.getOverlays().remove(overlay); + } + + /** + * @param {number} time Time. + * @private + */ + renderFrame_(time) { + let viewState; + + const size = this.getSize(); + const view = this.getView(); + const extent = createEmpty(); + const previousFrameState = this.frameState_; + /** @type {?module:ol/PluggableMap~FrameState} */ + let frameState = null; + if (size !== undefined && hasArea(size) && view && view.isDef()) { + const viewHints = view.getHints(this.frameState_ ? this.frameState_.viewHints : undefined); + const layerStatesArray = this.getLayerGroup().getLayerStatesArray(); + const layerStates = {}; + for (let i = 0, ii = layerStatesArray.length; i < ii; ++i) { + layerStates[getUid(layerStatesArray[i].layer)] = layerStatesArray[i]; + } + viewState = view.getState(); + let focus = this.focus_; + if (!focus) { + focus = viewState.center; + const pixelResolution = viewState.resolution / this.pixelRatio_; + focus[0] = Math.round(focus[0] / pixelResolution) * pixelResolution; + focus[1] = Math.round(focus[1] / pixelResolution) * pixelResolution; + } + frameState = /** @type {module:ol/PluggableMap~FrameState} */ ({ + animate: false, + coordinateToPixelTransform: this.coordinateToPixelTransform_, + extent: extent, + focus: focus, + index: this.frameIndex_++, + layerStates: layerStates, + layerStatesArray: layerStatesArray, + pixelRatio: this.pixelRatio_, + pixelToCoordinateTransform: this.pixelToCoordinateTransform_, + postRenderFunctions: [], + size: size, + skippedFeatureUids: this.skippedFeatureUids_, + tileQueue: this.tileQueue_, + time: time, + usedTiles: {}, + viewState: viewState, + viewHints: viewHints, + wantedTiles: {} + }); + } + + if (frameState) { + frameState.extent = getForViewAndSize(viewState.center, + viewState.resolution, viewState.rotation, frameState.size, extent); + } + + this.frameState_ = frameState; + this.renderer_.renderFrame(frameState); + + if (frameState) { + if (frameState.animate) { + this.render(); + } + Array.prototype.push.apply(this.postRenderFunctions_, frameState.postRenderFunctions); + + if (previousFrameState) { + const moveStart = !this.previousExtent_ || + (!isEmpty(this.previousExtent_) && + !equals(frameState.extent, this.previousExtent_)); + if (moveStart) { + this.dispatchEvent( + new MapEvent(MapEventType.MOVESTART, this, previousFrameState)); + this.previousExtent_ = createOrUpdateEmpty(this.previousExtent_); + } + } + + const idle = this.previousExtent_ && + !frameState.viewHints[ViewHint.ANIMATING] && + !frameState.viewHints[ViewHint.INTERACTING] && + !equals(frameState.extent, this.previousExtent_); + + if (idle) { + this.dispatchEvent(new MapEvent(MapEventType.MOVEEND, this, frameState)); + clone(frameState.extent, this.previousExtent_); + } + } + + this.dispatchEvent(new MapEvent(MapEventType.POSTRENDER, this, frameState)); + + setTimeout(this.handlePostRender.bind(this), 0); + + } + + /** + * Sets the layergroup of this map. + * @param {module:ol/layer/Group} layerGroup A layer group containing the layers in this map. + * @observable + * @api + */ + setLayerGroup(layerGroup) { + this.set(MapProperty.LAYERGROUP, layerGroup); + } + + /** + * Set the size of this map. + * @param {module:ol/size~Size|undefined} size The size in pixels of the map in the DOM. + * @observable + * @api + */ + setSize(size) { + this.set(MapProperty.SIZE, size); + } + + /** + * Set the target element to render this map into. + * @param {HTMLElement|string|undefined} target The Element or id of the Element + * that the map is rendered in. + * @observable + * @api + */ + setTarget(target) { + this.set(MapProperty.TARGET, target); + } + + /** + * Set the view for this map. + * @param {module:ol/View} view The view that controls this map. + * @observable + * @api + */ + setView(view) { + this.set(MapProperty.VIEW, view); + } + + /** + * @param {module:ol/Feature} feature Feature. + */ + skipFeature(feature) { + const featureUid = getUid(feature).toString(); + this.skippedFeatureUids_[featureUid] = true; + this.render(); + } + + /** + * Force a recalculation of the map viewport size. This should be called when + * third-party code changes the size of the map viewport. + * @api + */ + updateSize() { + const targetElement = this.getTargetElement(); + + if (!targetElement) { + this.setSize(undefined); + } else { + const computedStyle = getComputedStyle(targetElement); + this.setSize([ + targetElement.offsetWidth - + parseFloat(computedStyle['borderLeftWidth']) - + parseFloat(computedStyle['paddingLeft']) - + parseFloat(computedStyle['paddingRight']) - + parseFloat(computedStyle['borderRightWidth']), + targetElement.offsetHeight - + parseFloat(computedStyle['borderTopWidth']) - + parseFloat(computedStyle['paddingTop']) - + parseFloat(computedStyle['paddingBottom']) - + parseFloat(computedStyle['borderBottomWidth']) + ]); + } + } + + /** + * @param {module:ol/Feature} feature Feature. + */ + unskipFeature(feature) { + const featureUid = getUid(feature).toString(); + delete this.skippedFeatureUids_[featureUid]; + this.render(); + } +} + +inherits(PluggableMap, BaseObject); /** diff --git a/src/ol/control/Attribution.js b/src/ol/control/Attribution.js index 2f000ed2c9..2699c0d489 100644 --- a/src/ol/control/Attribution.js +++ b/src/ol/control/Attribution.js @@ -47,163 +47,272 @@ import {visibleAtResolution} from '../layer/Layer.js'; * @param {module:ol/control/Attribution~Options=} opt_options Attribution options. * @api */ -const Attribution = function(opt_options) { +class Attribution { + constructor(opt_options) { - const options = opt_options ? opt_options : {}; + const options = opt_options ? opt_options : {}; - /** - * @private - * @type {Element} - */ - this.ulElement_ = document.createElement('UL'); - - /** - * @private - * @type {boolean} - */ - this.collapsed_ = options.collapsed !== undefined ? options.collapsed : true; - - /** - * @private - * @type {boolean} - */ - this.collapsible_ = options.collapsible !== undefined ? - options.collapsible : true; - - if (!this.collapsible_) { - this.collapsed_ = false; - } - - const className = options.className !== undefined ? options.className : 'ol-attribution'; - - const tipLabel = options.tipLabel !== undefined ? options.tipLabel : 'Attributions'; - - const collapseLabel = options.collapseLabel !== undefined ? options.collapseLabel : '\u00BB'; - - if (typeof collapseLabel === 'string') { /** * @private * @type {Element} */ - this.collapseLabel_ = document.createElement('span'); - this.collapseLabel_.textContent = collapseLabel; - } else { - this.collapseLabel_ = collapseLabel; - } + this.ulElement_ = document.createElement('UL'); - const label = options.label !== undefined ? options.label : 'i'; - - if (typeof label === 'string') { /** * @private - * @type {Element} + * @type {boolean} */ - this.label_ = document.createElement('span'); - this.label_.textContent = label; - } else { - this.label_ = label; + this.collapsed_ = options.collapsed !== undefined ? options.collapsed : true; + + /** + * @private + * @type {boolean} + */ + this.collapsible_ = options.collapsible !== undefined ? + options.collapsible : true; + + if (!this.collapsible_) { + this.collapsed_ = false; + } + + const className = options.className !== undefined ? options.className : 'ol-attribution'; + + const tipLabel = options.tipLabel !== undefined ? options.tipLabel : 'Attributions'; + + const collapseLabel = options.collapseLabel !== undefined ? options.collapseLabel : '\u00BB'; + + if (typeof collapseLabel === 'string') { + /** + * @private + * @type {Element} + */ + this.collapseLabel_ = document.createElement('span'); + this.collapseLabel_.textContent = collapseLabel; + } else { + this.collapseLabel_ = collapseLabel; + } + + const label = options.label !== undefined ? options.label : 'i'; + + if (typeof label === 'string') { + /** + * @private + * @type {Element} + */ + this.label_ = document.createElement('span'); + this.label_.textContent = label; + } else { + this.label_ = label; + } + + + const activeLabel = (this.collapsible_ && !this.collapsed_) ? + this.collapseLabel_ : this.label_; + const button = document.createElement('button'); + button.setAttribute('type', 'button'); + button.title = tipLabel; + button.appendChild(activeLabel); + + listen(button, EventType.CLICK, this.handleClick_, this); + + const cssClasses = className + ' ' + CLASS_UNSELECTABLE + ' ' + CLASS_CONTROL + + (this.collapsed_ && this.collapsible_ ? ' ' + CLASS_COLLAPSED : '') + + (this.collapsible_ ? '' : ' ol-uncollapsible'); + const element = document.createElement('div'); + element.className = cssClasses; + element.appendChild(this.ulElement_); + element.appendChild(button); + + Control.call(this, { + element: element, + render: options.render || render, + target: options.target + }); + + /** + * A list of currently rendered resolutions. + * @type {Array.} + * @private + */ + this.renderedAttributions_ = []; + + /** + * @private + * @type {boolean} + */ + this.renderedVisible_ = true; + } - - const activeLabel = (this.collapsible_ && !this.collapsed_) ? - this.collapseLabel_ : this.label_; - const button = document.createElement('button'); - button.setAttribute('type', 'button'); - button.title = tipLabel; - button.appendChild(activeLabel); - - listen(button, EventType.CLICK, this.handleClick_, this); - - const cssClasses = className + ' ' + CLASS_UNSELECTABLE + ' ' + CLASS_CONTROL + - (this.collapsed_ && this.collapsible_ ? ' ' + CLASS_COLLAPSED : '') + - (this.collapsible_ ? '' : ' ol-uncollapsible'); - const element = document.createElement('div'); - element.className = cssClasses; - element.appendChild(this.ulElement_); - element.appendChild(button); - - Control.call(this, { - element: element, - render: options.render || render, - target: options.target - }); - /** - * A list of currently rendered resolutions. - * @type {Array.} + * Get a list of visible attributions. + * @param {module:ol/PluggableMap~FrameState} frameState Frame state. + * @return {Array.} Attributions. * @private */ - this.renderedAttributions_ = []; + getSourceAttributions_(frameState) { + /** + * Used to determine if an attribution already exists. + * @type {!Object.} + */ + const lookup = {}; - /** - * @private - * @type {boolean} - */ - this.renderedVisible_ = true; + /** + * A list of visible attributions. + * @type {Array.} + */ + const visibleAttributions = []; -}; + const layerStatesArray = frameState.layerStatesArray; + const resolution = frameState.viewState.resolution; + for (let i = 0, ii = layerStatesArray.length; i < ii; ++i) { + const layerState = layerStatesArray[i]; + if (!visibleAtResolution(layerState, resolution)) { + continue; + } -inherits(Attribution, Control); + const source = layerState.layer.getSource(); + if (!source) { + continue; + } + const attributionGetter = source.getAttributions(); + if (!attributionGetter) { + continue; + } -/** - * Get a list of visible attributions. - * @param {module:ol/PluggableMap~FrameState} frameState Frame state. - * @return {Array.} Attributions. - * @private - */ -Attribution.prototype.getSourceAttributions_ = function(frameState) { - /** - * Used to determine if an attribution already exists. - * @type {!Object.} - */ - const lookup = {}; + const attributions = attributionGetter(frameState); + if (!attributions) { + continue; + } - /** - * A list of visible attributions. - * @type {Array.} - */ - const visibleAttributions = []; - - const layerStatesArray = frameState.layerStatesArray; - const resolution = frameState.viewState.resolution; - for (let i = 0, ii = layerStatesArray.length; i < ii; ++i) { - const layerState = layerStatesArray[i]; - if (!visibleAtResolution(layerState, resolution)) { - continue; - } - - const source = layerState.layer.getSource(); - if (!source) { - continue; - } - - const attributionGetter = source.getAttributions(); - if (!attributionGetter) { - continue; - } - - const attributions = attributionGetter(frameState); - if (!attributions) { - continue; - } - - if (Array.isArray(attributions)) { - for (let j = 0, jj = attributions.length; j < jj; ++j) { - if (!(attributions[j] in lookup)) { - visibleAttributions.push(attributions[j]); - lookup[attributions[j]] = true; + if (Array.isArray(attributions)) { + for (let j = 0, jj = attributions.length; j < jj; ++j) { + if (!(attributions[j] in lookup)) { + visibleAttributions.push(attributions[j]); + lookup[attributions[j]] = true; + } + } + } else { + if (!(attributions in lookup)) { + visibleAttributions.push(attributions); + lookup[attributions] = true; } } - } else { - if (!(attributions in lookup)) { - visibleAttributions.push(attributions); - lookup[attributions] = true; + } + return visibleAttributions; + } + + /** + * @private + * @param {?module:ol/PluggableMap~FrameState} frameState Frame state. + */ + updateElement_(frameState) { + if (!frameState) { + if (this.renderedVisible_) { + this.element.style.display = 'none'; + this.renderedVisible_ = false; } + return; + } + + const attributions = this.getSourceAttributions_(frameState); + + const visible = attributions.length > 0; + if (this.renderedVisible_ != visible) { + this.element.style.display = visible ? '' : 'none'; + this.renderedVisible_ = visible; + } + + if (equals(attributions, this.renderedAttributions_)) { + return; + } + + removeChildren(this.ulElement_); + + // append the attributions + for (let i = 0, ii = attributions.length; i < ii; ++i) { + const element = document.createElement('LI'); + element.innerHTML = attributions[i]; + this.ulElement_.appendChild(element); + } + + this.renderedAttributions_ = attributions; + } + + /** + * @param {MouseEvent} event The event to handle + * @private + */ + handleClick_(event) { + event.preventDefault(); + this.handleToggle_(); + } + + /** + * @private + */ + handleToggle_() { + this.element.classList.toggle(CLASS_COLLAPSED); + if (this.collapsed_) { + replaceNode(this.collapseLabel_, this.label_); + } else { + replaceNode(this.label_, this.collapseLabel_); + } + this.collapsed_ = !this.collapsed_; + } + + /** + * Return `true` if the attribution is collapsible, `false` otherwise. + * @return {boolean} True if the widget is collapsible. + * @api + */ + getCollapsible() { + return this.collapsible_; + } + + /** + * Set whether the attribution should be collapsible. + * @param {boolean} collapsible True if the widget is collapsible. + * @api + */ + setCollapsible(collapsible) { + if (this.collapsible_ === collapsible) { + return; + } + this.collapsible_ = collapsible; + this.element.classList.toggle('ol-uncollapsible'); + if (!collapsible && this.collapsed_) { + this.handleToggle_(); } } - return visibleAttributions; -}; + + /** + * Collapse or expand the attribution according to the passed parameter. Will + * not do anything if the attribution isn't collapsible or if the current + * collapsed state is already the one requested. + * @param {boolean} collapsed True if the widget is collapsed. + * @api + */ + setCollapsed(collapsed) { + if (!this.collapsible_ || this.collapsed_ === collapsed) { + return; + } + this.handleToggle_(); + } + + /** + * Return `true` when the attribution is currently collapsed or `false` + * otherwise. + * @return {boolean} True if the widget is collapsed. + * @api + */ + getCollapsed() { + return this.collapsed_; + } +} + +inherits(Attribution, Control); /** @@ -217,117 +326,4 @@ export function render(mapEvent) { } -/** - * @private - * @param {?module:ol/PluggableMap~FrameState} frameState Frame state. - */ -Attribution.prototype.updateElement_ = function(frameState) { - if (!frameState) { - if (this.renderedVisible_) { - this.element.style.display = 'none'; - this.renderedVisible_ = false; - } - return; - } - - const attributions = this.getSourceAttributions_(frameState); - - const visible = attributions.length > 0; - if (this.renderedVisible_ != visible) { - this.element.style.display = visible ? '' : 'none'; - this.renderedVisible_ = visible; - } - - if (equals(attributions, this.renderedAttributions_)) { - return; - } - - removeChildren(this.ulElement_); - - // append the attributions - for (let i = 0, ii = attributions.length; i < ii; ++i) { - const element = document.createElement('LI'); - element.innerHTML = attributions[i]; - this.ulElement_.appendChild(element); - } - - this.renderedAttributions_ = attributions; -}; - - -/** - * @param {MouseEvent} event The event to handle - * @private - */ -Attribution.prototype.handleClick_ = function(event) { - event.preventDefault(); - this.handleToggle_(); -}; - - -/** - * @private - */ -Attribution.prototype.handleToggle_ = function() { - this.element.classList.toggle(CLASS_COLLAPSED); - if (this.collapsed_) { - replaceNode(this.collapseLabel_, this.label_); - } else { - replaceNode(this.label_, this.collapseLabel_); - } - this.collapsed_ = !this.collapsed_; -}; - - -/** - * Return `true` if the attribution is collapsible, `false` otherwise. - * @return {boolean} True if the widget is collapsible. - * @api - */ -Attribution.prototype.getCollapsible = function() { - return this.collapsible_; -}; - - -/** - * Set whether the attribution should be collapsible. - * @param {boolean} collapsible True if the widget is collapsible. - * @api - */ -Attribution.prototype.setCollapsible = function(collapsible) { - if (this.collapsible_ === collapsible) { - return; - } - this.collapsible_ = collapsible; - this.element.classList.toggle('ol-uncollapsible'); - if (!collapsible && this.collapsed_) { - this.handleToggle_(); - } -}; - - -/** - * Collapse or expand the attribution according to the passed parameter. Will - * not do anything if the attribution isn't collapsible or if the current - * collapsed state is already the one requested. - * @param {boolean} collapsed True if the widget is collapsed. - * @api - */ -Attribution.prototype.setCollapsed = function(collapsed) { - if (!this.collapsible_ || this.collapsed_ === collapsed) { - return; - } - this.handleToggle_(); -}; - - -/** - * Return `true` when the attribution is currently collapsed or `false` - * otherwise. - * @return {boolean} True if the widget is collapsed. - * @api - */ -Attribution.prototype.getCollapsed = function() { - return this.collapsed_; -}; export default Attribution; diff --git a/src/ol/control/Control.js b/src/ol/control/Control.js index 0d63ca7b6c..4ce8985d46 100644 --- a/src/ol/control/Control.js +++ b/src/ol/control/Control.js @@ -49,108 +49,108 @@ import {listen, unlistenByKey} from '../events.js'; * @param {module:ol/control/Control~Options} options Control options. * @api */ -const Control = function(options) { +class Control { + constructor(options) { - BaseObject.call(this); + BaseObject.call(this); - /** - * @protected - * @type {Element} - */ - this.element = options.element ? options.element : null; + /** + * @protected + * @type {Element} + */ + this.element = options.element ? options.element : null; - /** - * @private - * @type {Element} - */ - this.target_ = null; + /** + * @private + * @type {Element} + */ + this.target_ = null; - /** - * @private - * @type {module:ol/PluggableMap} - */ - this.map_ = null; + /** + * @private + * @type {module:ol/PluggableMap} + */ + this.map_ = null; - /** - * @protected - * @type {!Array.} - */ - this.listenerKeys = []; + /** + * @protected + * @type {!Array.} + */ + this.listenerKeys = []; - /** - * @type {function(module:ol/MapEvent)} - */ - this.render = options.render ? options.render : UNDEFINED; + /** + * @type {function(module:ol/MapEvent)} + */ + this.render = options.render ? options.render : UNDEFINED; - if (options.target) { - this.setTarget(options.target); - } + if (options.target) { + this.setTarget(options.target); + } -}; + } + + /** + * @inheritDoc + */ + disposeInternal() { + removeNode(this.element); + BaseObject.prototype.disposeInternal.call(this); + } + + /** + * Get the map associated with this control. + * @return {module:ol/PluggableMap} Map. + * @api + */ + getMap() { + return this.map_; + } + + /** + * Remove the control from its current map and attach it to the new map. + * Subclasses may set up event handlers to get notified about changes to + * the map here. + * @param {module:ol/PluggableMap} map Map. + * @api + */ + setMap(map) { + if (this.map_) { + removeNode(this.element); + } + for (let i = 0, ii = this.listenerKeys.length; i < ii; ++i) { + unlistenByKey(this.listenerKeys[i]); + } + this.listenerKeys.length = 0; + this.map_ = map; + if (this.map_) { + const target = this.target_ ? + this.target_ : map.getOverlayContainerStopEvent(); + target.appendChild(this.element); + if (this.render !== UNDEFINED) { + this.listenerKeys.push(listen(map, + MapEventType.POSTRENDER, this.render, this)); + } + map.render(); + } + } + + /** + * This function is used to set a target element for the control. It has no + * effect if it is called after the control has been added to the map (i.e. + * after `setMap` is called on the control). If no `target` is set in the + * options passed to the control constructor and if `setTarget` is not called + * then the control is added to the map's overlay container. + * @param {Element|string} target Target. + * @api + */ + setTarget(target) { + this.target_ = typeof target === 'string' ? + document.getElementById(target) : + target; + } +} inherits(Control, BaseObject); -/** - * @inheritDoc - */ -Control.prototype.disposeInternal = function() { - removeNode(this.element); - BaseObject.prototype.disposeInternal.call(this); -}; - - -/** - * Get the map associated with this control. - * @return {module:ol/PluggableMap} Map. - * @api - */ -Control.prototype.getMap = function() { - return this.map_; -}; - - -/** - * Remove the control from its current map and attach it to the new map. - * Subclasses may set up event handlers to get notified about changes to - * the map here. - * @param {module:ol/PluggableMap} map Map. - * @api - */ -Control.prototype.setMap = function(map) { - if (this.map_) { - removeNode(this.element); - } - for (let i = 0, ii = this.listenerKeys.length; i < ii; ++i) { - unlistenByKey(this.listenerKeys[i]); - } - this.listenerKeys.length = 0; - this.map_ = map; - if (this.map_) { - const target = this.target_ ? - this.target_ : map.getOverlayContainerStopEvent(); - target.appendChild(this.element); - if (this.render !== UNDEFINED) { - this.listenerKeys.push(listen(map, - MapEventType.POSTRENDER, this.render, this)); - } - map.render(); - } -}; - - -/** - * This function is used to set a target element for the control. It has no - * effect if it is called after the control has been added to the map (i.e. - * after `setMap` is called on the control). If no `target` is set in the - * options passed to the control constructor and if `setTarget` is not called - * then the control is added to the map's overlay container. - * @param {Element|string} target Target. - * @api - */ -Control.prototype.setTarget = function(target) { - this.target_ = typeof target === 'string' ? - document.getElementById(target) : - target; -}; export default Control; diff --git a/src/ol/control/FullScreen.js b/src/ol/control/FullScreen.js index 74b0459f40..2abd9ac2d9 100644 --- a/src/ol/control/FullScreen.js +++ b/src/ol/control/FullScreen.js @@ -67,149 +67,148 @@ const getChangeType = (function() { * @param {module:ol/control/FullScreen~Options=} opt_options Options. * @api */ -const FullScreen = function(opt_options) { +class FullScreen { + constructor(opt_options) { - const options = opt_options ? opt_options : {}; + const options = opt_options ? opt_options : {}; + + /** + * @private + * @type {string} + */ + this.cssClassName_ = options.className !== undefined ? options.className : + 'ol-full-screen'; + + const label = options.label !== undefined ? options.label : '\u2922'; + + /** + * @private + * @type {Element} + */ + this.labelNode_ = typeof label === 'string' ? + document.createTextNode(label) : label; + + const labelActive = options.labelActive !== undefined ? options.labelActive : '\u00d7'; + + /** + * @private + * @type {Element} + */ + 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_); + + listen(button, EventType.CLICK, + this.handleClick_, this); + + const cssClasses = this.cssClassName_ + ' ' + CLASS_UNSELECTABLE + + ' ' + CLASS_CONTROL + ' ' + + (!isFullScreenSupported() ? CLASS_UNSUPPORTED : ''); + const element = document.createElement('div'); + element.className = cssClasses; + element.appendChild(button); + + Control.call(this, { + element: element, + target: options.target + }); + + /** + * @private + * @type {boolean} + */ + this.keys_ = options.keys !== undefined ? options.keys : false; + + /** + * @private + * @type {Element|string|undefined} + */ + this.source_ = options.source; + + } + + /** + * @param {MouseEvent} event The event to handle + * @private + */ + handleClick_(event) { + event.preventDefault(); + this.handleFullScreen_(); + } /** * @private - * @type {string} */ - this.cssClassName_ = options.className !== undefined ? options.className : - 'ol-full-screen'; + handleFullScreen_() { + if (!isFullScreenSupported()) { + return; + } + const map = this.getMap(); + if (!map) { + return; + } + if (isFullScreen()) { + exitFullScreen(); + } else { + let element; + if (this.source_) { + element = typeof this.source_ === 'string' ? + document.getElementById(this.source_) : + this.source_; + } else { + element = map.getTargetElement(); + } + if (this.keys_) { + requestFullScreenWithKeys(element); - const label = options.label !== undefined ? options.label : '\u2922'; + } else { + requestFullScreen(element); + } + } + } /** * @private - * @type {Element} */ - this.labelNode_ = typeof label === 'string' ? - document.createTextNode(label) : label; - - const labelActive = options.labelActive !== undefined ? options.labelActive : '\u00d7'; + handleFullScreenChange_() { + const button = this.element.firstElementChild; + const map = this.getMap(); + if (isFullScreen()) { + button.className = this.cssClassName_ + '-true'; + replaceNode(this.labelActiveNode_, this.labelNode_); + } else { + button.className = this.cssClassName_ + '-false'; + replaceNode(this.labelNode_, this.labelActiveNode_); + } + if (map) { + map.updateSize(); + } + } /** - * @private - * @type {Element} + * @inheritDoc + * @api */ - 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_); - - listen(button, EventType.CLICK, - this.handleClick_, this); - - const cssClasses = this.cssClassName_ + ' ' + CLASS_UNSELECTABLE + - ' ' + CLASS_CONTROL + ' ' + - (!isFullScreenSupported() ? CLASS_UNSUPPORTED : ''); - const element = document.createElement('div'); - element.className = cssClasses; - element.appendChild(button); - - Control.call(this, { - element: element, - target: options.target - }); - - /** - * @private - * @type {boolean} - */ - this.keys_ = options.keys !== undefined ? options.keys : false; - - /** - * @private - * @type {Element|string|undefined} - */ - this.source_ = options.source; - -}; + setMap(map) { + Control.prototype.setMap.call(this, map); + if (map) { + this.listenerKeys.push(listen(document, + getChangeType(), + this.handleFullScreenChange_, this) + ); + } + } +} inherits(FullScreen, Control); -/** - * @param {MouseEvent} event The event to handle - * @private - */ -FullScreen.prototype.handleClick_ = function(event) { - event.preventDefault(); - this.handleFullScreen_(); -}; - - -/** - * @private - */ -FullScreen.prototype.handleFullScreen_ = function() { - if (!isFullScreenSupported()) { - return; - } - const map = this.getMap(); - if (!map) { - return; - } - if (isFullScreen()) { - exitFullScreen(); - } else { - let element; - if (this.source_) { - element = typeof this.source_ === 'string' ? - document.getElementById(this.source_) : - this.source_; - } else { - element = map.getTargetElement(); - } - if (this.keys_) { - requestFullScreenWithKeys(element); - - } else { - requestFullScreen(element); - } - } -}; - - -/** - * @private - */ -FullScreen.prototype.handleFullScreenChange_ = function() { - const button = this.element.firstElementChild; - const map = this.getMap(); - if (isFullScreen()) { - button.className = this.cssClassName_ + '-true'; - replaceNode(this.labelActiveNode_, this.labelNode_); - } else { - button.className = this.cssClassName_ + '-false'; - replaceNode(this.labelNode_, this.labelActiveNode_); - } - if (map) { - map.updateSize(); - } -}; - - -/** - * @inheritDoc - * @api - */ -FullScreen.prototype.setMap = function(map) { - Control.prototype.setMap.call(this, map); - if (map) { - this.listenerKeys.push(listen(document, - getChangeType(), - this.handleFullScreenChange_, this) - ); - } -}; - /** * @return {boolean} Fullscreen is supported by the current platform. */ diff --git a/src/ol/control/MousePosition.js b/src/ol/control/MousePosition.js index 811d7b7170..b4c17da69c 100644 --- a/src/ol/control/MousePosition.js +++ b/src/ol/control/MousePosition.js @@ -52,67 +52,197 @@ const COORDINATE_FORMAT = 'coordinateFormat'; * options. * @api */ -const MousePosition = function(opt_options) { +class MousePosition { + constructor(opt_options) { - const options = opt_options ? opt_options : {}; + const options = opt_options ? opt_options : {}; - const element = document.createElement('DIV'); - element.className = options.className !== undefined ? options.className : 'ol-mouse-position'; + const element = document.createElement('DIV'); + element.className = options.className !== undefined ? options.className : 'ol-mouse-position'; - Control.call(this, { - element: element, - render: options.render || render, - target: options.target - }); + Control.call(this, { + element: element, + render: options.render || render, + target: options.target + }); - listen(this, - getChangeEventType(PROJECTION), - this.handleProjectionChanged_, this); + listen(this, + getChangeEventType(PROJECTION), + this.handleProjectionChanged_, this); + + if (options.coordinateFormat) { + this.setCoordinateFormat(options.coordinateFormat); + } + if (options.projection) { + this.setProjection(options.projection); + } + + /** + * @private + * @type {string} + */ + this.undefinedHTML_ = 'undefinedHTML' in options ? options.undefinedHTML : ' '; + + /** + * @private + * @type {boolean} + */ + this.renderOnMouseOut_ = !!this.undefinedHTML_; + + /** + * @private + * @type {string} + */ + this.renderedHTML_ = element.innerHTML; + + /** + * @private + * @type {module:ol/proj/Projection} + */ + this.mapProjection_ = null; + + /** + * @private + * @type {?module:ol/proj~TransformFunction} + */ + this.transform_ = null; + + /** + * @private + * @type {module:ol~Pixel} + */ + this.lastMouseMovePixel_ = null; - if (options.coordinateFormat) { - this.setCoordinateFormat(options.coordinateFormat); - } - if (options.projection) { - this.setProjection(options.projection); } /** * @private - * @type {string} */ - this.undefinedHTML_ = 'undefinedHTML' in options ? options.undefinedHTML : ' '; + handleProjectionChanged_() { + this.transform_ = null; + } /** - * @private - * @type {boolean} + * Return the coordinate format type used to render the current position or + * undefined. + * @return {module:ol/coordinate~CoordinateFormat|undefined} The format to render the current + * position in. + * @observable + * @api */ - this.renderOnMouseOut_ = !!this.undefinedHTML_; + getCoordinateFormat() { + return ( + /** @type {module:ol/coordinate~CoordinateFormat|undefined} */ (this.get(COORDINATE_FORMAT)) + ); + } /** - * @private - * @type {string} + * Return the projection that is used to report the mouse position. + * @return {module:ol/proj/Projection|undefined} The projection to report mouse + * position in. + * @observable + * @api */ - this.renderedHTML_ = element.innerHTML; + getProjection() { + return ( + /** @type {module:ol/proj/Projection|undefined} */ (this.get(PROJECTION)) + ); + } /** - * @private - * @type {module:ol/proj/Projection} + * @param {Event} event Browser event. + * @protected */ - this.mapProjection_ = null; + handleMouseMove(event) { + const map = this.getMap(); + this.lastMouseMovePixel_ = map.getEventPixel(event); + this.updateHTML_(this.lastMouseMovePixel_); + } /** - * @private - * @type {?module:ol/proj~TransformFunction} + * @param {Event} event Browser event. + * @protected */ - this.transform_ = null; + handleMouseOut(event) { + this.updateHTML_(null); + this.lastMouseMovePixel_ = null; + } /** - * @private - * @type {module:ol~Pixel} + * @inheritDoc + * @api */ - this.lastMouseMovePixel_ = null; + setMap(map) { + Control.prototype.setMap.call(this, map); + if (map) { + const viewport = map.getViewport(); + this.listenerKeys.push( + listen(viewport, EventType.MOUSEMOVE, this.handleMouseMove, this) + ); + if (this.renderOnMouseOut_) { + this.listenerKeys.push( + listen(viewport, EventType.MOUSEOUT, this.handleMouseOut, this) + ); + } + } + } -}; + /** + * Set the coordinate format type used to render the current position. + * @param {module:ol/coordinate~CoordinateFormat} format The format to render the current + * position in. + * @observable + * @api + */ + setCoordinateFormat(format) { + this.set(COORDINATE_FORMAT, format); + } + + /** + * Set the projection that is used to report the mouse position. + * @param {module:ol/proj~ProjectionLike} projection The projection to report mouse + * position in. + * @observable + * @api + */ + setProjection(projection) { + this.set(PROJECTION, getProjection(projection)); + } + + /** + * @param {?module:ol~Pixel} pixel Pixel. + * @private + */ + updateHTML_(pixel) { + let html = this.undefinedHTML_; + if (pixel && this.mapProjection_) { + if (!this.transform_) { + const projection = this.getProjection(); + if (projection) { + this.transform_ = getTransformFromProjections( + this.mapProjection_, projection); + } else { + this.transform_ = identityTransform; + } + } + const map = this.getMap(); + const coordinate = map.getCoordinateFromPixel(pixel); + if (coordinate) { + this.transform_(coordinate, coordinate); + const coordinateFormat = this.getCoordinateFormat(); + if (coordinateFormat) { + html = coordinateFormat(coordinate); + } else { + html = coordinate.toString(); + } + } + } + if (!this.renderedHTML_ || html !== this.renderedHTML_) { + this.element.innerHTML = html; + this.renderedHTML_ = html; + } + } +} inherits(MousePosition, Control); @@ -137,141 +267,4 @@ export function render(mapEvent) { } -/** - * @private - */ -MousePosition.prototype.handleProjectionChanged_ = function() { - this.transform_ = null; -}; - - -/** - * Return the coordinate format type used to render the current position or - * undefined. - * @return {module:ol/coordinate~CoordinateFormat|undefined} The format to render the current - * position in. - * @observable - * @api - */ -MousePosition.prototype.getCoordinateFormat = function() { - return ( - /** @type {module:ol/coordinate~CoordinateFormat|undefined} */ (this.get(COORDINATE_FORMAT)) - ); -}; - - -/** - * Return the projection that is used to report the mouse position. - * @return {module:ol/proj/Projection|undefined} The projection to report mouse - * position in. - * @observable - * @api - */ -MousePosition.prototype.getProjection = function() { - return ( - /** @type {module:ol/proj/Projection|undefined} */ (this.get(PROJECTION)) - ); -}; - - -/** - * @param {Event} event Browser event. - * @protected - */ -MousePosition.prototype.handleMouseMove = function(event) { - const map = this.getMap(); - this.lastMouseMovePixel_ = map.getEventPixel(event); - this.updateHTML_(this.lastMouseMovePixel_); -}; - - -/** - * @param {Event} event Browser event. - * @protected - */ -MousePosition.prototype.handleMouseOut = function(event) { - this.updateHTML_(null); - this.lastMouseMovePixel_ = null; -}; - - -/** - * @inheritDoc - * @api - */ -MousePosition.prototype.setMap = function(map) { - Control.prototype.setMap.call(this, map); - if (map) { - const viewport = map.getViewport(); - this.listenerKeys.push( - listen(viewport, EventType.MOUSEMOVE, this.handleMouseMove, this) - ); - if (this.renderOnMouseOut_) { - this.listenerKeys.push( - listen(viewport, EventType.MOUSEOUT, this.handleMouseOut, this) - ); - } - } -}; - - -/** - * Set the coordinate format type used to render the current position. - * @param {module:ol/coordinate~CoordinateFormat} format The format to render the current - * position in. - * @observable - * @api - */ -MousePosition.prototype.setCoordinateFormat = function(format) { - this.set(COORDINATE_FORMAT, format); -}; - - -/** - * Set the projection that is used to report the mouse position. - * @param {module:ol/proj~ProjectionLike} projection The projection to report mouse - * position in. - * @observable - * @api - */ -MousePosition.prototype.setProjection = function(projection) { - this.set(PROJECTION, getProjection(projection)); -}; - - -/** - * @param {?module:ol~Pixel} pixel Pixel. - * @private - */ -MousePosition.prototype.updateHTML_ = function(pixel) { - let html = this.undefinedHTML_; - if (pixel && this.mapProjection_) { - if (!this.transform_) { - const projection = this.getProjection(); - if (projection) { - this.transform_ = getTransformFromProjections( - this.mapProjection_, projection); - } else { - this.transform_ = identityTransform; - } - } - const map = this.getMap(); - const coordinate = map.getCoordinateFromPixel(pixel); - if (coordinate) { - this.transform_(coordinate, coordinate); - const coordinateFormat = this.getCoordinateFormat(); - if (coordinateFormat) { - html = coordinateFormat(coordinate); - } else { - html = coordinate.toString(); - } - } - } - if (!this.renderedHTML_ || html !== this.renderedHTML_) { - this.element.innerHTML = html; - this.renderedHTML_ = html; - } -}; - - export default MousePosition; diff --git a/src/ol/control/OverviewMap.js b/src/ol/control/OverviewMap.js index 6241ff3374..9849dcb538 100644 --- a/src/ol/control/OverviewMap.js +++ b/src/ol/control/OverviewMap.js @@ -66,258 +66,506 @@ const MIN_RATIO = 0.1; * @param {module:ol/control/OverviewMap~Options=} opt_options OverviewMap options. * @api */ -const OverviewMap = function(opt_options) { +class OverviewMap { + constructor(opt_options) { - const options = opt_options ? opt_options : {}; + const options = opt_options ? opt_options : {}; - /** - * @type {boolean} - * @private - */ - this.collapsed_ = options.collapsed !== undefined ? options.collapsed : true; + /** + * @type {boolean} + * @private + */ + this.collapsed_ = options.collapsed !== undefined ? options.collapsed : true; - /** - * @private - * @type {boolean} - */ - this.collapsible_ = options.collapsible !== undefined ? - options.collapsible : true; - - if (!this.collapsible_) { - this.collapsed_ = false; - } - - const className = options.className !== undefined ? options.className : 'ol-overviewmap'; - - const tipLabel = options.tipLabel !== undefined ? options.tipLabel : 'Overview map'; - - const collapseLabel = options.collapseLabel !== undefined ? options.collapseLabel : '\u00AB'; - - if (typeof collapseLabel === 'string') { /** * @private - * @type {Element} + * @type {boolean} */ - this.collapseLabel_ = document.createElement('span'); - this.collapseLabel_.textContent = collapseLabel; - } else { - this.collapseLabel_ = collapseLabel; - } + this.collapsible_ = options.collapsible !== undefined ? + options.collapsible : true; - const label = options.label !== undefined ? options.label : '\u00BB'; + if (!this.collapsible_) { + this.collapsed_ = false; + } + const className = options.className !== undefined ? options.className : 'ol-overviewmap'; - if (typeof label === 'string') { - /** - * @private - * @type {Element} - */ - this.label_ = document.createElement('span'); - this.label_.textContent = label; - } else { - this.label_ = label; - } + const tipLabel = options.tipLabel !== undefined ? options.tipLabel : 'Overview map'; - const activeLabel = (this.collapsible_ && !this.collapsed_) ? - this.collapseLabel_ : this.label_; - const button = document.createElement('button'); - button.setAttribute('type', 'button'); - button.title = tipLabel; - button.appendChild(activeLabel); + const collapseLabel = options.collapseLabel !== undefined ? options.collapseLabel : '\u00AB'; - listen(button, EventType.CLICK, - this.handleClick_, this); - - /** - * @type {Element} - * @private - */ - this.ovmapDiv_ = document.createElement('DIV'); - this.ovmapDiv_.className = 'ol-overviewmap-map'; - - /** - * @type {module:ol/Map} - * @private - */ - this.ovmap_ = new Map({ - controls: new Collection(), - interactions: new Collection(), - view: options.view - }); - const ovmap = this.ovmap_; - - if (options.layers) { - options.layers.forEach( + if (typeof collapseLabel === 'string') { /** - * @param {module:ol/layer/Layer} layer Layer. + * @private + * @type {Element} */ - (function(layer) { - ovmap.addLayer(layer); - }).bind(this)); - } + this.collapseLabel_ = document.createElement('span'); + this.collapseLabel_.textContent = collapseLabel; + } else { + this.collapseLabel_ = collapseLabel; + } - const box = document.createElement('DIV'); - box.className = 'ol-overviewmap-box'; - box.style.boxSizing = 'border-box'; + const label = options.label !== undefined ? options.label : '\u00BB'; + + + if (typeof label === 'string') { + /** + * @private + * @type {Element} + */ + this.label_ = document.createElement('span'); + this.label_.textContent = label; + } else { + this.label_ = label; + } + + const activeLabel = (this.collapsible_ && !this.collapsed_) ? + this.collapseLabel_ : this.label_; + const button = document.createElement('button'); + button.setAttribute('type', 'button'); + button.title = tipLabel; + button.appendChild(activeLabel); + + listen(button, EventType.CLICK, + this.handleClick_, this); + + /** + * @type {Element} + * @private + */ + this.ovmapDiv_ = document.createElement('DIV'); + this.ovmapDiv_.className = 'ol-overviewmap-map'; + + /** + * @type {module:ol/Map} + * @private + */ + this.ovmap_ = new Map({ + controls: new Collection(), + interactions: new Collection(), + view: options.view + }); + const ovmap = this.ovmap_; + + if (options.layers) { + options.layers.forEach( + /** + * @param {module:ol/layer/Layer} layer Layer. + */ + (function(layer) { + ovmap.addLayer(layer); + }).bind(this)); + } + + const box = document.createElement('DIV'); + box.className = 'ol-overviewmap-box'; + box.style.boxSizing = 'border-box'; + + /** + * @type {module:ol/Overlay} + * @private + */ + this.boxOverlay_ = new Overlay({ + position: [0, 0], + positioning: OverlayPositioning.BOTTOM_LEFT, + element: box + }); + this.ovmap_.addOverlay(this.boxOverlay_); + + const cssClasses = className + ' ' + CLASS_UNSELECTABLE + ' ' + CLASS_CONTROL + + (this.collapsed_ && this.collapsible_ ? ' ' + CLASS_COLLAPSED : '') + + (this.collapsible_ ? '' : ' ol-uncollapsible'); + const element = document.createElement('div'); + element.className = cssClasses; + element.appendChild(this.ovmapDiv_); + element.appendChild(button); + + Control.call(this, { + element: element, + render: options.render || render, + target: options.target + }); + + /* Interactive map */ + + const scope = this; + + const overlay = this.boxOverlay_; + const overlayBox = this.boxOverlay_.getElement(); + + /* Functions definition */ + + const computeDesiredMousePosition = function(mousePosition) { + return { + clientX: mousePosition.clientX - (overlayBox.offsetWidth / 2), + clientY: mousePosition.clientY + (overlayBox.offsetHeight / 2) + }; + }; + + const move = function(event) { + const coordinates = ovmap.getEventCoordinate(computeDesiredMousePosition(event)); + + overlay.setPosition(coordinates); + }; + + const endMoving = function(event) { + const coordinates = ovmap.getEventCoordinate(event); + + scope.getMap().getView().setCenter(coordinates); + + window.removeEventListener('mousemove', move); + window.removeEventListener('mouseup', endMoving); + }; + + /* Binding */ + + overlayBox.addEventListener('mousedown', function() { + window.addEventListener('mousemove', move); + window.addEventListener('mouseup', endMoving); + }); + } /** - * @type {module:ol/Overlay} - * @private + * @inheritDoc + * @api */ - this.boxOverlay_ = new Overlay({ - position: [0, 0], - positioning: OverlayPositioning.BOTTOM_LEFT, - element: box - }); - this.ovmap_.addOverlay(this.boxOverlay_); - - const cssClasses = className + ' ' + CLASS_UNSELECTABLE + ' ' + CLASS_CONTROL + - (this.collapsed_ && this.collapsible_ ? ' ' + CLASS_COLLAPSED : '') + - (this.collapsible_ ? '' : ' ol-uncollapsible'); - const element = document.createElement('div'); - element.className = cssClasses; - element.appendChild(this.ovmapDiv_); - element.appendChild(button); - - Control.call(this, { - element: element, - render: options.render || render, - target: options.target - }); - - /* Interactive map */ - - const scope = this; - - const overlay = this.boxOverlay_; - const overlayBox = this.boxOverlay_.getElement(); - - /* Functions definition */ - - const computeDesiredMousePosition = function(mousePosition) { - return { - clientX: mousePosition.clientX - (overlayBox.offsetWidth / 2), - clientY: mousePosition.clientY + (overlayBox.offsetHeight / 2) - }; - }; - - const move = function(event) { - const coordinates = ovmap.getEventCoordinate(computeDesiredMousePosition(event)); - - overlay.setPosition(coordinates); - }; - - const endMoving = function(event) { - const coordinates = ovmap.getEventCoordinate(event); - - scope.getMap().getView().setCenter(coordinates); - - window.removeEventListener('mousemove', move); - window.removeEventListener('mouseup', endMoving); - }; - - /* Binding */ - - overlayBox.addEventListener('mousedown', function() { - window.addEventListener('mousemove', move); - window.addEventListener('mouseup', endMoving); - }); -}; - -inherits(OverviewMap, Control); - - -/** - * @inheritDoc - * @api - */ -OverviewMap.prototype.setMap = function(map) { - const oldMap = this.getMap(); - if (map === oldMap) { - return; - } - if (oldMap) { - const oldView = oldMap.getView(); - if (oldView) { - this.unbindView_(oldView); + setMap(map) { + const oldMap = this.getMap(); + if (map === oldMap) { + return; } - this.ovmap_.setTarget(null); - } - Control.prototype.setMap.call(this, map); - - if (map) { - this.ovmap_.setTarget(this.ovmapDiv_); - this.listenerKeys.push(listen( - map, ObjectEventType.PROPERTYCHANGE, - this.handleMapPropertyChange_, this)); - - // TODO: to really support map switching, this would need to be reworked - if (this.ovmap_.getLayers().getLength() === 0) { - this.ovmap_.setLayerGroup(map.getLayerGroup()); + if (oldMap) { + const oldView = oldMap.getView(); + if (oldView) { + this.unbindView_(oldView); + } + this.ovmap_.setTarget(null); } + Control.prototype.setMap.call(this, map); - const view = map.getView(); - if (view) { - this.bindView_(view); - if (view.isDef()) { - this.ovmap_.updateSize(); - this.resetExtent_(); + if (map) { + this.ovmap_.setTarget(this.ovmapDiv_); + this.listenerKeys.push(listen( + map, ObjectEventType.PROPERTYCHANGE, + this.handleMapPropertyChange_, this)); + + // TODO: to really support map switching, this would need to be reworked + if (this.ovmap_.getLayers().getLength() === 0) { + this.ovmap_.setLayerGroup(map.getLayerGroup()); + } + + const view = map.getView(); + if (view) { + this.bindView_(view); + if (view.isDef()) { + this.ovmap_.updateSize(); + this.resetExtent_(); + } } } } -}; - -/** - * Handle map property changes. This only deals with changes to the map's view. - * @param {module:ol/Object~ObjectEvent} event The propertychange event. - * @private - */ -OverviewMap.prototype.handleMapPropertyChange_ = function(event) { - if (event.key === MapProperty.VIEW) { - const oldView = /** @type {module:ol/View} */ (event.oldValue); - if (oldView) { - this.unbindView_(oldView); + /** + * Handle map property changes. This only deals with changes to the map's view. + * @param {module:ol/Object~ObjectEvent} event The propertychange event. + * @private + */ + handleMapPropertyChange_(event) { + if (event.key === MapProperty.VIEW) { + const oldView = /** @type {module:ol/View} */ (event.oldValue); + if (oldView) { + this.unbindView_(oldView); + } + const newView = this.getMap().getView(); + this.bindView_(newView); } - const newView = this.getMap().getView(); - this.bindView_(newView); } -}; + /** + * Register listeners for view property changes. + * @param {module:ol/View} view The view. + * @private + */ + bindView_(view) { + listen(view, + getChangeEventType(ViewProperty.ROTATION), + this.handleRotationChanged_, this); + } -/** - * Register listeners for view property changes. - * @param {module:ol/View} view The view. - * @private - */ -OverviewMap.prototype.bindView_ = function(view) { - listen(view, - getChangeEventType(ViewProperty.ROTATION), - this.handleRotationChanged_, this); -}; + /** + * Unregister listeners for view property changes. + * @param {module:ol/View} view The view. + * @private + */ + unbindView_(view) { + unlisten(view, + getChangeEventType(ViewProperty.ROTATION), + this.handleRotationChanged_, this); + } + /** + * Handle rotation changes to the main map. + * TODO: This should rotate the extent rectrangle instead of the + * overview map's view. + * @private + */ + handleRotationChanged_() { + this.ovmap_.getView().setRotation(this.getMap().getView().getRotation()); + } -/** - * Unregister listeners for view property changes. - * @param {module:ol/View} view The view. - * @private - */ -OverviewMap.prototype.unbindView_ = function(view) { - unlisten(view, - getChangeEventType(ViewProperty.ROTATION), - this.handleRotationChanged_, this); -}; + /** + * Reset the overview map extent if the box size (width or + * height) is less than the size of the overview map size times minRatio + * or is greater than the size of the overview size times maxRatio. + * + * If the map extent was not reset, the box size can fits in the defined + * ratio sizes. This method then checks if is contained inside the overview + * map current extent. If not, recenter the overview map to the current + * main map center location. + * @private + */ + validateExtent_() { + const map = this.getMap(); + const ovmap = this.ovmap_; + if (!map.isRendered() || !ovmap.isRendered()) { + return; + } -/** - * Handle rotation changes to the main map. - * TODO: This should rotate the extent rectrangle instead of the - * overview map's view. - * @private - */ -OverviewMap.prototype.handleRotationChanged_ = function() { - this.ovmap_.getView().setRotation(this.getMap().getView().getRotation()); -}; + const mapSize = /** @type {module:ol/size~Size} */ (map.getSize()); + + const view = map.getView(); + const extent = view.calculateExtent(mapSize); + + const ovmapSize = /** @type {module:ol/size~Size} */ (ovmap.getSize()); + + const ovview = ovmap.getView(); + const ovextent = ovview.calculateExtent(ovmapSize); + + const topLeftPixel = + ovmap.getPixelFromCoordinate(getTopLeft(extent)); + const bottomRightPixel = + ovmap.getPixelFromCoordinate(getBottomRight(extent)); + + const boxWidth = Math.abs(topLeftPixel[0] - bottomRightPixel[0]); + const boxHeight = Math.abs(topLeftPixel[1] - bottomRightPixel[1]); + + const ovmapWidth = ovmapSize[0]; + const ovmapHeight = ovmapSize[1]; + + if (boxWidth < ovmapWidth * MIN_RATIO || + boxHeight < ovmapHeight * MIN_RATIO || + boxWidth > ovmapWidth * MAX_RATIO || + boxHeight > ovmapHeight * MAX_RATIO) { + this.resetExtent_(); + } else if (!containsExtent(ovextent, extent)) { + this.recenter_(); + } + } + + /** + * Reset the overview map extent to half calculated min and max ratio times + * the extent of the main map. + * @private + */ + resetExtent_() { + if (MAX_RATIO === 0 || MIN_RATIO === 0) { + return; + } + + const map = this.getMap(); + const ovmap = this.ovmap_; + + const mapSize = /** @type {module:ol/size~Size} */ (map.getSize()); + + const view = map.getView(); + const extent = view.calculateExtent(mapSize); + + const ovview = ovmap.getView(); + + // get how many times the current map overview could hold different + // box sizes using the min and max ratio, pick the step in the middle used + // to calculate the extent from the main map to set it to the overview map, + const steps = Math.log( + MAX_RATIO / MIN_RATIO) / Math.LN2; + const ratio = 1 / (Math.pow(2, steps / 2) * MIN_RATIO); + scaleFromCenter(extent, ratio); + ovview.fit(extent); + } + + /** + * Set the center of the overview map to the map center without changing its + * resolution. + * @private + */ + recenter_() { + const map = this.getMap(); + const ovmap = this.ovmap_; + + const view = map.getView(); + + const ovview = ovmap.getView(); + + ovview.setCenter(view.getCenter()); + } + + /** + * Update the box using the main map extent + * @private + */ + updateBox_() { + const map = this.getMap(); + const ovmap = this.ovmap_; + + if (!map.isRendered() || !ovmap.isRendered()) { + return; + } + + const mapSize = /** @type {module:ol/size~Size} */ (map.getSize()); + + const view = map.getView(); + + const ovview = ovmap.getView(); + + const rotation = view.getRotation(); + + const overlay = this.boxOverlay_; + const box = this.boxOverlay_.getElement(); + const extent = view.calculateExtent(mapSize); + const ovresolution = ovview.getResolution(); + const bottomLeft = getBottomLeft(extent); + const topRight = getTopRight(extent); + + // set position using bottom left coordinates + const rotateBottomLeft = this.calculateCoordinateRotate_(rotation, bottomLeft); + overlay.setPosition(rotateBottomLeft); + + // set box size calculated from map extent size and overview map resolution + if (box) { + box.style.width = Math.abs((bottomLeft[0] - topRight[0]) / ovresolution) + 'px'; + box.style.height = Math.abs((topRight[1] - bottomLeft[1]) / ovresolution) + 'px'; + } + } + + /** + * @param {number} rotation Target rotation. + * @param {module:ol/coordinate~Coordinate} coordinate Coordinate. + * @return {module:ol/coordinate~Coordinate|undefined} Coordinate for rotation and center anchor. + * @private + */ + calculateCoordinateRotate_(rotation, coordinate) { + let coordinateRotate; + + const map = this.getMap(); + const view = map.getView(); + + const currentCenter = view.getCenter(); + + if (currentCenter) { + coordinateRotate = [ + coordinate[0] - currentCenter[0], + coordinate[1] - currentCenter[1] + ]; + rotateCoordinate(coordinateRotate, rotation); + addCoordinate(coordinateRotate, currentCenter); + } + return coordinateRotate; + } + + /** + * @param {MouseEvent} event The event to handle + * @private + */ + handleClick_(event) { + event.preventDefault(); + this.handleToggle_(); + } + + /** + * @private + */ + handleToggle_() { + this.element.classList.toggle(CLASS_COLLAPSED); + if (this.collapsed_) { + replaceNode(this.collapseLabel_, this.label_); + } else { + replaceNode(this.label_, this.collapseLabel_); + } + this.collapsed_ = !this.collapsed_; + + // manage overview map if it had not been rendered before and control + // is expanded + const ovmap = this.ovmap_; + if (!this.collapsed_ && !ovmap.isRendered()) { + ovmap.updateSize(); + this.resetExtent_(); + listenOnce(ovmap, MapEventType.POSTRENDER, + function(event) { + this.updateBox_(); + }, + this); + } + } + + /** + * Return `true` if the overview map is collapsible, `false` otherwise. + * @return {boolean} True if the widget is collapsible. + * @api + */ + getCollapsible() { + return this.collapsible_; + } + + /** + * Set whether the overview map should be collapsible. + * @param {boolean} collapsible True if the widget is collapsible. + * @api + */ + setCollapsible(collapsible) { + if (this.collapsible_ === collapsible) { + return; + } + this.collapsible_ = collapsible; + this.element.classList.toggle('ol-uncollapsible'); + if (!collapsible && this.collapsed_) { + this.handleToggle_(); + } + } + + /** + * Collapse or expand the overview map according to the passed parameter. Will + * not do anything if the overview map isn't collapsible or if the current + * collapsed state is already the one requested. + * @param {boolean} collapsed True if the widget is collapsed. + * @api + */ + setCollapsed(collapsed) { + if (!this.collapsible_ || this.collapsed_ === collapsed) { + return; + } + this.handleToggle_(); + } + + /** + * Determine if the overview map is collapsed. + * @return {boolean} The overview map is collapsed. + * @api + */ + getCollapsed() { + return this.collapsed_; + } + + /** + * Return the overview map. + * @return {module:ol/PluggableMap} Overview map. + * @api + */ + getOverviewMap() { + return this.ovmap_; + } +} + +inherits(OverviewMap, Control); /** @@ -332,266 +580,4 @@ export function render(mapEvent) { } -/** - * Reset the overview map extent if the box size (width or - * height) is less than the size of the overview map size times minRatio - * or is greater than the size of the overview size times maxRatio. - * - * If the map extent was not reset, the box size can fits in the defined - * ratio sizes. This method then checks if is contained inside the overview - * map current extent. If not, recenter the overview map to the current - * main map center location. - * @private - */ -OverviewMap.prototype.validateExtent_ = function() { - const map = this.getMap(); - const ovmap = this.ovmap_; - - if (!map.isRendered() || !ovmap.isRendered()) { - return; - } - - const mapSize = /** @type {module:ol/size~Size} */ (map.getSize()); - - const view = map.getView(); - const extent = view.calculateExtent(mapSize); - - const ovmapSize = /** @type {module:ol/size~Size} */ (ovmap.getSize()); - - const ovview = ovmap.getView(); - const ovextent = ovview.calculateExtent(ovmapSize); - - const topLeftPixel = - ovmap.getPixelFromCoordinate(getTopLeft(extent)); - const bottomRightPixel = - ovmap.getPixelFromCoordinate(getBottomRight(extent)); - - const boxWidth = Math.abs(topLeftPixel[0] - bottomRightPixel[0]); - const boxHeight = Math.abs(topLeftPixel[1] - bottomRightPixel[1]); - - const ovmapWidth = ovmapSize[0]; - const ovmapHeight = ovmapSize[1]; - - if (boxWidth < ovmapWidth * MIN_RATIO || - boxHeight < ovmapHeight * MIN_RATIO || - boxWidth > ovmapWidth * MAX_RATIO || - boxHeight > ovmapHeight * MAX_RATIO) { - this.resetExtent_(); - } else if (!containsExtent(ovextent, extent)) { - this.recenter_(); - } -}; - - -/** - * Reset the overview map extent to half calculated min and max ratio times - * the extent of the main map. - * @private - */ -OverviewMap.prototype.resetExtent_ = function() { - if (MAX_RATIO === 0 || MIN_RATIO === 0) { - return; - } - - const map = this.getMap(); - const ovmap = this.ovmap_; - - const mapSize = /** @type {module:ol/size~Size} */ (map.getSize()); - - const view = map.getView(); - const extent = view.calculateExtent(mapSize); - - const ovview = ovmap.getView(); - - // get how many times the current map overview could hold different - // box sizes using the min and max ratio, pick the step in the middle used - // to calculate the extent from the main map to set it to the overview map, - const steps = Math.log( - MAX_RATIO / MIN_RATIO) / Math.LN2; - const ratio = 1 / (Math.pow(2, steps / 2) * MIN_RATIO); - scaleFromCenter(extent, ratio); - ovview.fit(extent); -}; - - -/** - * Set the center of the overview map to the map center without changing its - * resolution. - * @private - */ -OverviewMap.prototype.recenter_ = function() { - const map = this.getMap(); - const ovmap = this.ovmap_; - - const view = map.getView(); - - const ovview = ovmap.getView(); - - ovview.setCenter(view.getCenter()); -}; - - -/** - * Update the box using the main map extent - * @private - */ -OverviewMap.prototype.updateBox_ = function() { - const map = this.getMap(); - const ovmap = this.ovmap_; - - if (!map.isRendered() || !ovmap.isRendered()) { - return; - } - - const mapSize = /** @type {module:ol/size~Size} */ (map.getSize()); - - const view = map.getView(); - - const ovview = ovmap.getView(); - - const rotation = view.getRotation(); - - const overlay = this.boxOverlay_; - const box = this.boxOverlay_.getElement(); - const extent = view.calculateExtent(mapSize); - const ovresolution = ovview.getResolution(); - const bottomLeft = getBottomLeft(extent); - const topRight = getTopRight(extent); - - // set position using bottom left coordinates - const rotateBottomLeft = this.calculateCoordinateRotate_(rotation, bottomLeft); - overlay.setPosition(rotateBottomLeft); - - // set box size calculated from map extent size and overview map resolution - if (box) { - box.style.width = Math.abs((bottomLeft[0] - topRight[0]) / ovresolution) + 'px'; - box.style.height = Math.abs((topRight[1] - bottomLeft[1]) / ovresolution) + 'px'; - } -}; - - -/** - * @param {number} rotation Target rotation. - * @param {module:ol/coordinate~Coordinate} coordinate Coordinate. - * @return {module:ol/coordinate~Coordinate|undefined} Coordinate for rotation and center anchor. - * @private - */ -OverviewMap.prototype.calculateCoordinateRotate_ = function( - rotation, coordinate) { - let coordinateRotate; - - const map = this.getMap(); - const view = map.getView(); - - const currentCenter = view.getCenter(); - - if (currentCenter) { - coordinateRotate = [ - coordinate[0] - currentCenter[0], - coordinate[1] - currentCenter[1] - ]; - rotateCoordinate(coordinateRotate, rotation); - addCoordinate(coordinateRotate, currentCenter); - } - return coordinateRotate; -}; - - -/** - * @param {MouseEvent} event The event to handle - * @private - */ -OverviewMap.prototype.handleClick_ = function(event) { - event.preventDefault(); - this.handleToggle_(); -}; - - -/** - * @private - */ -OverviewMap.prototype.handleToggle_ = function() { - this.element.classList.toggle(CLASS_COLLAPSED); - if (this.collapsed_) { - replaceNode(this.collapseLabel_, this.label_); - } else { - replaceNode(this.label_, this.collapseLabel_); - } - this.collapsed_ = !this.collapsed_; - - // manage overview map if it had not been rendered before and control - // is expanded - const ovmap = this.ovmap_; - if (!this.collapsed_ && !ovmap.isRendered()) { - ovmap.updateSize(); - this.resetExtent_(); - listenOnce(ovmap, MapEventType.POSTRENDER, - function(event) { - this.updateBox_(); - }, - this); - } -}; - - -/** - * Return `true` if the overview map is collapsible, `false` otherwise. - * @return {boolean} True if the widget is collapsible. - * @api - */ -OverviewMap.prototype.getCollapsible = function() { - return this.collapsible_; -}; - - -/** - * Set whether the overview map should be collapsible. - * @param {boolean} collapsible True if the widget is collapsible. - * @api - */ -OverviewMap.prototype.setCollapsible = function(collapsible) { - if (this.collapsible_ === collapsible) { - return; - } - this.collapsible_ = collapsible; - this.element.classList.toggle('ol-uncollapsible'); - if (!collapsible && this.collapsed_) { - this.handleToggle_(); - } -}; - - -/** - * Collapse or expand the overview map according to the passed parameter. Will - * not do anything if the overview map isn't collapsible or if the current - * collapsed state is already the one requested. - * @param {boolean} collapsed True if the widget is collapsed. - * @api - */ -OverviewMap.prototype.setCollapsed = function(collapsed) { - if (!this.collapsible_ || this.collapsed_ === collapsed) { - return; - } - this.handleToggle_(); -}; - - -/** - * Determine if the overview map is collapsed. - * @return {boolean} The overview map is collapsed. - * @api - */ -OverviewMap.prototype.getCollapsed = function() { - return this.collapsed_; -}; - - -/** - * Return the overview map. - * @return {module:ol/PluggableMap} Overview map. - * @api - */ -OverviewMap.prototype.getOverviewMap = function() { - return this.ovmap_; -}; export default OverviewMap; diff --git a/src/ol/control/Rotate.js b/src/ol/control/Rotate.js index 8d73e37ab4..6d8336827c 100644 --- a/src/ol/control/Rotate.js +++ b/src/ol/control/Rotate.js @@ -38,117 +38,117 @@ import {inherits} from '../util.js'; * @param {module:ol/control/Rotate~Options=} opt_options Rotate options. * @api */ -const Rotate = function(opt_options) { +class Rotate { + constructor(opt_options) { - const options = opt_options ? opt_options : {}; + const options = opt_options ? opt_options : {}; - const className = options.className !== undefined ? options.className : 'ol-rotate'; + const className = options.className !== undefined ? options.className : 'ol-rotate'; - const label = options.label !== undefined ? options.label : '\u21E7'; + const label = options.label !== undefined ? options.label : '\u21E7'; - /** - * @type {Element} - * @private - */ - this.label_ = null; + /** + * @type {Element} + * @private + */ + this.label_ = null; - if (typeof label === 'string') { - this.label_ = document.createElement('span'); - this.label_.className = 'ol-compass'; - this.label_.textContent = label; - } else { - this.label_ = label; - this.label_.classList.add('ol-compass'); - } - - const tipLabel = options.tipLabel ? options.tipLabel : 'Reset rotation'; - - const button = document.createElement('button'); - button.className = className + '-reset'; - button.setAttribute('type', 'button'); - button.title = tipLabel; - button.appendChild(this.label_); - - listen(button, EventType.CLICK, - Rotate.prototype.handleClick_, this); - - const cssClasses = className + ' ' + CLASS_UNSELECTABLE + ' ' + CLASS_CONTROL; - const element = document.createElement('div'); - element.className = cssClasses; - element.appendChild(button); - - this.callResetNorth_ = options.resetNorth ? options.resetNorth : undefined; - - Control.call(this, { - element: element, - render: options.render || render, - target: options.target - }); - - /** - * @type {number} - * @private - */ - this.duration_ = options.duration !== undefined ? options.duration : 250; - - /** - * @type {boolean} - * @private - */ - this.autoHide_ = options.autoHide !== undefined ? options.autoHide : true; - - /** - * @private - * @type {number|undefined} - */ - this.rotation_ = undefined; - - if (this.autoHide_) { - this.element.classList.add(CLASS_HIDDEN); - } - -}; - -inherits(Rotate, Control); - - -/** - * @param {MouseEvent} event The event to handle - * @private - */ -Rotate.prototype.handleClick_ = function(event) { - event.preventDefault(); - if (this.callResetNorth_ !== undefined) { - this.callResetNorth_(); - } else { - this.resetNorth_(); - } -}; - - -/** - * @private - */ -Rotate.prototype.resetNorth_ = function() { - const map = this.getMap(); - const view = map.getView(); - if (!view) { - // the map does not have a view, so we can't act - // upon it - return; - } - if (view.getRotation() !== undefined) { - if (this.duration_ > 0) { - view.animate({ - rotation: 0, - duration: this.duration_, - easing: easeOut - }); + if (typeof label === 'string') { + this.label_ = document.createElement('span'); + this.label_.className = 'ol-compass'; + this.label_.textContent = label; } else { - view.setRotation(0); + this.label_ = label; + this.label_.classList.add('ol-compass'); + } + + const tipLabel = options.tipLabel ? options.tipLabel : 'Reset rotation'; + + const button = document.createElement('button'); + button.className = className + '-reset'; + button.setAttribute('type', 'button'); + button.title = tipLabel; + button.appendChild(this.label_); + + listen(button, EventType.CLICK, + Rotate.prototype.handleClick_, this); + + const cssClasses = className + ' ' + CLASS_UNSELECTABLE + ' ' + CLASS_CONTROL; + const element = document.createElement('div'); + element.className = cssClasses; + element.appendChild(button); + + this.callResetNorth_ = options.resetNorth ? options.resetNorth : undefined; + + Control.call(this, { + element: element, + render: options.render || render, + target: options.target + }); + + /** + * @type {number} + * @private + */ + this.duration_ = options.duration !== undefined ? options.duration : 250; + + /** + * @type {boolean} + * @private + */ + this.autoHide_ = options.autoHide !== undefined ? options.autoHide : true; + + /** + * @private + * @type {number|undefined} + */ + this.rotation_ = undefined; + + if (this.autoHide_) { + this.element.classList.add(CLASS_HIDDEN); + } + + } + + /** + * @param {MouseEvent} event The event to handle + * @private + */ + handleClick_(event) { + event.preventDefault(); + if (this.callResetNorth_ !== undefined) { + this.callResetNorth_(); + } else { + this.resetNorth_(); } } -}; + + /** + * @private + */ + resetNorth_() { + const map = this.getMap(); + const view = map.getView(); + if (!view) { + // the map does not have a view, so we can't act + // upon it + return; + } + if (view.getRotation() !== undefined) { + if (this.duration_ > 0) { + view.animate({ + rotation: 0, + duration: this.duration_, + easing: easeOut + }); + } else { + view.setRotation(0); + } + } + } +} + +inherits(Rotate, Control); /** diff --git a/src/ol/control/ScaleLine.js b/src/ol/control/ScaleLine.js index 732cb73419..1b6000e377 100644 --- a/src/ol/control/ScaleLine.js +++ b/src/ol/control/ScaleLine.js @@ -64,89 +64,229 @@ const LEADING_DIGITS = [1, 2, 5]; * @param {module:ol/control/ScaleLine~Options=} opt_options Scale line options. * @api */ -const ScaleLine = function(opt_options) { +class ScaleLine { + constructor(opt_options) { - const options = opt_options ? opt_options : {}; + const options = opt_options ? opt_options : {}; - const className = options.className !== undefined ? options.className : 'ol-scale-line'; + const className = options.className !== undefined ? options.className : 'ol-scale-line'; + + /** + * @private + * @type {HTMLElement} + */ + this.innerElement_ = document.createElement('DIV'); + this.innerElement_.className = className + '-inner'; + + /** + * @private + * @type {HTMLElement} + */ + this.element_ = document.createElement('DIV'); + this.element_.className = className + ' ' + CLASS_UNSELECTABLE; + this.element_.appendChild(this.innerElement_); + + /** + * @private + * @type {?module:ol/View~State} + */ + this.viewState_ = null; + + /** + * @private + * @type {number} + */ + this.minWidth_ = options.minWidth !== undefined ? options.minWidth : 64; + + /** + * @private + * @type {boolean} + */ + this.renderedVisible_ = false; + + /** + * @private + * @type {number|undefined} + */ + this.renderedWidth_ = undefined; + + /** + * @private + * @type {string} + */ + this.renderedHTML_ = ''; + + Control.call(this, { + element: this.element_, + render: options.render || render, + target: options.target + }); + + listen( + this, getChangeEventType(UNITS_PROP), + this.handleUnitsChanged_, this); + + this.setUnits(/** @type {module:ol/control/ScaleLine~Units} */ (options.units) || + Units.METRIC); + + } + + /** + * Return the units to use in the scale line. + * @return {module:ol/control/ScaleLine~Units|undefined} The units + * to use in the scale line. + * @observable + * @api + */ + getUnits() { + return ( + /** @type {module:ol/control/ScaleLine~Units|undefined} */ (this.get(UNITS_PROP)) + ); + } /** * @private - * @type {HTMLElement} */ - this.innerElement_ = document.createElement('DIV'); - this.innerElement_.className = className + '-inner'; + handleUnitsChanged_() { + this.updateElement_(); + } + + /** + * Set the units to use in the scale line. + * @param {module:ol/control/ScaleLine~Units} units The units to use in the scale line. + * @observable + * @api + */ + setUnits(units) { + this.set(UNITS_PROP, units); + } /** * @private - * @type {HTMLElement} */ - this.element_ = document.createElement('DIV'); - this.element_.className = className + ' ' + CLASS_UNSELECTABLE; - this.element_.appendChild(this.innerElement_); + updateElement_() { + const viewState = this.viewState_; - /** - * @private - * @type {?module:ol/View~State} - */ - this.viewState_ = null; + if (!viewState) { + if (this.renderedVisible_) { + this.element_.style.display = 'none'; + this.renderedVisible_ = false; + } + return; + } - /** - * @private - * @type {number} - */ - this.minWidth_ = options.minWidth !== undefined ? options.minWidth : 64; + const center = viewState.center; + const projection = viewState.projection; + const units = this.getUnits(); + const pointResolutionUnits = units == Units.DEGREES ? + ProjUnits.DEGREES : + ProjUnits.METERS; + let pointResolution = + getPointResolution(projection, viewState.resolution, center, pointResolutionUnits); + if (projection.getUnits() != ProjUnits.DEGREES && projection.getMetersPerUnit() + && pointResolutionUnits == ProjUnits.METERS) { + pointResolution *= projection.getMetersPerUnit(); + } - /** - * @private - * @type {boolean} - */ - this.renderedVisible_ = false; + let nominalCount = this.minWidth_ * pointResolution; + let suffix = ''; + if (units == Units.DEGREES) { + const metersPerDegree = METERS_PER_UNIT[ProjUnits.DEGREES]; + if (projection.getUnits() == ProjUnits.DEGREES) { + nominalCount *= metersPerDegree; + } else { + pointResolution /= metersPerDegree; + } + if (nominalCount < metersPerDegree / 60) { + suffix = '\u2033'; // seconds + pointResolution *= 3600; + } else if (nominalCount < metersPerDegree) { + suffix = '\u2032'; // minutes + pointResolution *= 60; + } else { + suffix = '\u00b0'; // degrees + } + } else if (units == Units.IMPERIAL) { + if (nominalCount < 0.9144) { + suffix = 'in'; + pointResolution /= 0.0254; + } else if (nominalCount < 1609.344) { + suffix = 'ft'; + pointResolution /= 0.3048; + } else { + suffix = 'mi'; + pointResolution /= 1609.344; + } + } else if (units == Units.NAUTICAL) { + pointResolution /= 1852; + suffix = 'nm'; + } else if (units == Units.METRIC) { + if (nominalCount < 0.001) { + suffix = 'μm'; + pointResolution *= 1000000; + } else if (nominalCount < 1) { + suffix = 'mm'; + pointResolution *= 1000; + } else if (nominalCount < 1000) { + suffix = 'm'; + } else { + suffix = 'km'; + pointResolution /= 1000; + } + } else if (units == Units.US) { + if (nominalCount < 0.9144) { + suffix = 'in'; + pointResolution *= 39.37; + } else if (nominalCount < 1609.344) { + suffix = 'ft'; + pointResolution /= 0.30480061; + } else { + suffix = 'mi'; + pointResolution /= 1609.3472; + } + } else { + assert(false, 33); // Invalid units + } - /** - * @private - * @type {number|undefined} - */ - this.renderedWidth_ = undefined; + let i = 3 * Math.floor( + Math.log(this.minWidth_ * pointResolution) / Math.log(10)); + let count, width; + while (true) { + count = LEADING_DIGITS[((i % 3) + 3) % 3] * + Math.pow(10, Math.floor(i / 3)); + width = Math.round(count / pointResolution); + if (isNaN(width)) { + this.element_.style.display = 'none'; + this.renderedVisible_ = false; + return; + } else if (width >= this.minWidth_) { + break; + } + ++i; + } - /** - * @private - * @type {string} - */ - this.renderedHTML_ = ''; + const html = count + ' ' + suffix; + if (this.renderedHTML_ != html) { + this.innerElement_.innerHTML = html; + this.renderedHTML_ = html; + } - Control.call(this, { - element: this.element_, - render: options.render || render, - target: options.target - }); + if (this.renderedWidth_ != width) { + this.innerElement_.style.width = width + 'px'; + this.renderedWidth_ = width; + } - listen( - this, getChangeEventType(UNITS_PROP), - this.handleUnitsChanged_, this); + if (!this.renderedVisible_) { + this.element_.style.display = ''; + this.renderedVisible_ = true; + } - this.setUnits(/** @type {module:ol/control/ScaleLine~Units} */ (options.units) || - Units.METRIC); - -}; + } +} inherits(ScaleLine, Control); -/** - * Return the units to use in the scale line. - * @return {module:ol/control/ScaleLine~Units|undefined} The units - * to use in the scale line. - * @observable - * @api - */ -ScaleLine.prototype.getUnits = function() { - return ( - /** @type {module:ol/control/ScaleLine~Units|undefined} */ (this.get(UNITS_PROP)) - ); -}; - - /** * Update the scale line element. * @param {module:ol/MapEvent} mapEvent Map event. @@ -164,145 +304,4 @@ export function render(mapEvent) { } -/** - * @private - */ -ScaleLine.prototype.handleUnitsChanged_ = function() { - this.updateElement_(); -}; - - -/** - * Set the units to use in the scale line. - * @param {module:ol/control/ScaleLine~Units} units The units to use in the scale line. - * @observable - * @api - */ -ScaleLine.prototype.setUnits = function(units) { - this.set(UNITS_PROP, units); -}; - - -/** - * @private - */ -ScaleLine.prototype.updateElement_ = function() { - const viewState = this.viewState_; - - if (!viewState) { - if (this.renderedVisible_) { - this.element_.style.display = 'none'; - this.renderedVisible_ = false; - } - return; - } - - const center = viewState.center; - const projection = viewState.projection; - const units = this.getUnits(); - const pointResolutionUnits = units == Units.DEGREES ? - ProjUnits.DEGREES : - ProjUnits.METERS; - let pointResolution = - getPointResolution(projection, viewState.resolution, center, pointResolutionUnits); - if (projection.getUnits() != ProjUnits.DEGREES && projection.getMetersPerUnit() - && pointResolutionUnits == ProjUnits.METERS) { - pointResolution *= projection.getMetersPerUnit(); - } - - let nominalCount = this.minWidth_ * pointResolution; - let suffix = ''; - if (units == Units.DEGREES) { - const metersPerDegree = METERS_PER_UNIT[ProjUnits.DEGREES]; - if (projection.getUnits() == ProjUnits.DEGREES) { - nominalCount *= metersPerDegree; - } else { - pointResolution /= metersPerDegree; - } - if (nominalCount < metersPerDegree / 60) { - suffix = '\u2033'; // seconds - pointResolution *= 3600; - } else if (nominalCount < metersPerDegree) { - suffix = '\u2032'; // minutes - pointResolution *= 60; - } else { - suffix = '\u00b0'; // degrees - } - } else if (units == Units.IMPERIAL) { - if (nominalCount < 0.9144) { - suffix = 'in'; - pointResolution /= 0.0254; - } else if (nominalCount < 1609.344) { - suffix = 'ft'; - pointResolution /= 0.3048; - } else { - suffix = 'mi'; - pointResolution /= 1609.344; - } - } else if (units == Units.NAUTICAL) { - pointResolution /= 1852; - suffix = 'nm'; - } else if (units == Units.METRIC) { - if (nominalCount < 0.001) { - suffix = 'μm'; - pointResolution *= 1000000; - } else if (nominalCount < 1) { - suffix = 'mm'; - pointResolution *= 1000; - } else if (nominalCount < 1000) { - suffix = 'm'; - } else { - suffix = 'km'; - pointResolution /= 1000; - } - } else if (units == Units.US) { - if (nominalCount < 0.9144) { - suffix = 'in'; - pointResolution *= 39.37; - } else if (nominalCount < 1609.344) { - suffix = 'ft'; - pointResolution /= 0.30480061; - } else { - suffix = 'mi'; - pointResolution /= 1609.3472; - } - } else { - assert(false, 33); // Invalid units - } - - let i = 3 * Math.floor( - Math.log(this.minWidth_ * pointResolution) / Math.log(10)); - let count, width; - while (true) { - count = LEADING_DIGITS[((i % 3) + 3) % 3] * - Math.pow(10, Math.floor(i / 3)); - width = Math.round(count / pointResolution); - if (isNaN(width)) { - this.element_.style.display = 'none'; - this.renderedVisible_ = false; - return; - } else if (width >= this.minWidth_) { - break; - } - ++i; - } - - const html = count + ' ' + suffix; - if (this.renderedHTML_ != html) { - this.innerElement_.innerHTML = html; - this.renderedHTML_ = html; - } - - if (this.renderedWidth_ != width) { - this.innerElement_.style.width = width + 'px'; - this.renderedWidth_ = width; - } - - if (!this.renderedVisible_) { - this.element_.style.display = ''; - this.renderedVisible_ = true; - } - -}; - export default ScaleLine; diff --git a/src/ol/control/Zoom.js b/src/ol/control/Zoom.js index 235e216d80..e8ecbe4f65 100644 --- a/src/ol/control/Zoom.js +++ b/src/ol/control/Zoom.js @@ -36,104 +36,106 @@ import {easeOut} from '../easing.js'; * @param {module:ol/control/Zoom~Options=} opt_options Zoom options. * @api */ -const Zoom = function(opt_options) { +class Zoom { + constructor(opt_options) { - const options = opt_options ? opt_options : {}; + const options = opt_options ? opt_options : {}; - const className = options.className !== undefined ? options.className : 'ol-zoom'; + const className = options.className !== undefined ? options.className : 'ol-zoom'; - const delta = options.delta !== undefined ? options.delta : 1; + const delta = options.delta !== undefined ? options.delta : 1; - const zoomInLabel = options.zoomInLabel !== undefined ? options.zoomInLabel : '+'; - const zoomOutLabel = options.zoomOutLabel !== undefined ? options.zoomOutLabel : '\u2212'; + const zoomInLabel = options.zoomInLabel !== undefined ? options.zoomInLabel : '+'; + const zoomOutLabel = options.zoomOutLabel !== undefined ? options.zoomOutLabel : '\u2212'; - const zoomInTipLabel = options.zoomInTipLabel !== undefined ? - options.zoomInTipLabel : 'Zoom in'; - const zoomOutTipLabel = options.zoomOutTipLabel !== undefined ? - options.zoomOutTipLabel : 'Zoom out'; + const zoomInTipLabel = options.zoomInTipLabel !== undefined ? + options.zoomInTipLabel : 'Zoom in'; + const zoomOutTipLabel = options.zoomOutTipLabel !== undefined ? + options.zoomOutTipLabel : 'Zoom out'; - const inElement = document.createElement('button'); - inElement.className = className + '-in'; - inElement.setAttribute('type', 'button'); - inElement.title = zoomInTipLabel; - inElement.appendChild( - typeof zoomInLabel === 'string' ? document.createTextNode(zoomInLabel) : zoomInLabel - ); + const inElement = document.createElement('button'); + inElement.className = className + '-in'; + inElement.setAttribute('type', 'button'); + inElement.title = zoomInTipLabel; + inElement.appendChild( + typeof zoomInLabel === 'string' ? document.createTextNode(zoomInLabel) : zoomInLabel + ); - listen(inElement, EventType.CLICK, - Zoom.prototype.handleClick_.bind(this, delta)); + listen(inElement, EventType.CLICK, + Zoom.prototype.handleClick_.bind(this, delta)); - const outElement = document.createElement('button'); - outElement.className = className + '-out'; - outElement.setAttribute('type', 'button'); - outElement.title = zoomOutTipLabel; - outElement.appendChild( - typeof zoomOutLabel === 'string' ? document.createTextNode(zoomOutLabel) : zoomOutLabel - ); + const outElement = document.createElement('button'); + outElement.className = className + '-out'; + outElement.setAttribute('type', 'button'); + outElement.title = zoomOutTipLabel; + outElement.appendChild( + typeof zoomOutLabel === 'string' ? document.createTextNode(zoomOutLabel) : zoomOutLabel + ); - listen(outElement, EventType.CLICK, - Zoom.prototype.handleClick_.bind(this, -delta)); + listen(outElement, EventType.CLICK, + Zoom.prototype.handleClick_.bind(this, -delta)); - const cssClasses = className + ' ' + CLASS_UNSELECTABLE + ' ' + CLASS_CONTROL; - const element = document.createElement('div'); - element.className = cssClasses; - element.appendChild(inElement); - element.appendChild(outElement); + const cssClasses = className + ' ' + CLASS_UNSELECTABLE + ' ' + CLASS_CONTROL; + const element = document.createElement('div'); + element.className = cssClasses; + element.appendChild(inElement); + element.appendChild(outElement); - Control.call(this, { - element: element, - target: options.target - }); + Control.call(this, { + element: element, + target: options.target + }); + + /** + * @type {number} + * @private + */ + this.duration_ = options.duration !== undefined ? options.duration : 250; + + } /** - * @type {number} + * @param {number} delta Zoom delta. + * @param {MouseEvent} event The event to handle * @private */ - this.duration_ = options.duration !== undefined ? options.duration : 250; + handleClick_(delta, event) { + event.preventDefault(); + this.zoomByDelta_(delta); + } -}; + /** + * @param {number} delta Zoom delta. + * @private + */ + zoomByDelta_(delta) { + const map = this.getMap(); + const view = map.getView(); + if (!view) { + // the map does not have a view, so we can't act + // upon it + return; + } + const currentResolution = view.getResolution(); + if (currentResolution) { + const newResolution = view.constrainResolution(currentResolution, delta); + if (this.duration_ > 0) { + if (view.getAnimating()) { + view.cancelAnimations(); + } + view.animate({ + resolution: newResolution, + duration: this.duration_, + easing: easeOut + }); + } else { + view.setResolution(newResolution); + } + } + } +} inherits(Zoom, Control); -/** - * @param {number} delta Zoom delta. - * @param {MouseEvent} event The event to handle - * @private - */ -Zoom.prototype.handleClick_ = function(delta, event) { - event.preventDefault(); - this.zoomByDelta_(delta); -}; - - -/** - * @param {number} delta Zoom delta. - * @private - */ -Zoom.prototype.zoomByDelta_ = function(delta) { - const map = this.getMap(); - const view = map.getView(); - if (!view) { - // the map does not have a view, so we can't act - // upon it - return; - } - const currentResolution = view.getResolution(); - if (currentResolution) { - const newResolution = view.constrainResolution(currentResolution, delta); - if (this.duration_ > 0) { - if (view.getAnimating()) { - view.cancelAnimations(); - } - view.animate({ - resolution: newResolution, - duration: this.duration_, - easing: easeOut - }); - } else { - view.setResolution(newResolution); - } - } -}; export default Zoom; diff --git a/src/ol/control/ZoomSlider.js b/src/ol/control/ZoomSlider.js index 982e946c08..9cea621fcb 100644 --- a/src/ol/control/ZoomSlider.js +++ b/src/ol/control/ZoomSlider.js @@ -47,164 +47,303 @@ const Direction = { * @param {module:ol/control/ZoomSlider~Options=} opt_options Zoom slider options. * @api */ -const ZoomSlider = function(opt_options) { +class ZoomSlider { + constructor(opt_options) { - const options = opt_options ? opt_options : {}; + const options = opt_options ? opt_options : {}; + + /** + * Will hold the current resolution of the view. + * + * @type {number|undefined} + * @private + */ + this.currentResolution_ = undefined; + + /** + * The direction of the slider. Will be determined from actual display of the + * container and defaults to Direction.VERTICAL. + * + * @type {Direction} + * @private + */ + this.direction_ = Direction.VERTICAL; + + /** + * @type {boolean} + * @private + */ + this.dragging_; + + /** + * @type {number} + * @private + */ + this.heightLimit_ = 0; + + /** + * @type {number} + * @private + */ + this.widthLimit_ = 0; + + /** + * @type {number|undefined} + * @private + */ + this.previousX_; + + /** + * @type {number|undefined} + * @private + */ + this.previousY_; + + /** + * The calculated thumb size (border box plus margins). Set when initSlider_ + * is called. + * @type {module:ol/size~Size} + * @private + */ + this.thumbSize_ = null; + + /** + * Whether the slider is initialized. + * @type {boolean} + * @private + */ + this.sliderInitialized_ = false; + + /** + * @type {number} + * @private + */ + this.duration_ = options.duration !== undefined ? options.duration : 200; + + const className = options.className !== undefined ? options.className : 'ol-zoomslider'; + const thumbElement = document.createElement('button'); + thumbElement.setAttribute('type', 'button'); + thumbElement.className = className + '-thumb ' + CLASS_UNSELECTABLE; + const containerElement = document.createElement('div'); + containerElement.className = className + ' ' + CLASS_UNSELECTABLE + ' ' + CLASS_CONTROL; + containerElement.appendChild(thumbElement); + /** + * @type {module:ol/pointer/PointerEventHandler} + * @private + */ + this.dragger_ = new PointerEventHandler(containerElement); + + listen(this.dragger_, PointerEventType.POINTERDOWN, + this.handleDraggerStart_, this); + listen(this.dragger_, PointerEventType.POINTERMOVE, + this.handleDraggerDrag_, this); + listen(this.dragger_, PointerEventType.POINTERUP, + this.handleDraggerEnd_, this); + + listen(containerElement, EventType.CLICK, this.handleContainerClick_, this); + listen(thumbElement, EventType.CLICK, stopPropagation); + + Control.call(this, { + element: containerElement, + render: options.render || render + }); + } /** - * Will hold the current resolution of the view. + * @inheritDoc + */ + disposeInternal() { + this.dragger_.dispose(); + Control.prototype.disposeInternal.call(this); + } + + /** + * @inheritDoc + */ + setMap(map) { + Control.prototype.setMap.call(this, map); + if (map) { + map.render(); + } + } + + /** + * Initializes the slider element. This will determine and set this controls + * direction_ and also constrain the dragging of the thumb to always be within + * the bounds of the container. * - * @type {number|undefined} * @private */ - this.currentResolution_ = undefined; + initSlider_() { + const container = this.element; + const containerSize = { + width: container.offsetWidth, height: container.offsetHeight + }; + + const thumb = container.firstElementChild; + const computedStyle = getComputedStyle(thumb); + const thumbWidth = thumb.offsetWidth + + parseFloat(computedStyle['marginRight']) + + parseFloat(computedStyle['marginLeft']); + const thumbHeight = thumb.offsetHeight + + parseFloat(computedStyle['marginTop']) + + parseFloat(computedStyle['marginBottom']); + this.thumbSize_ = [thumbWidth, thumbHeight]; + + if (containerSize.width > containerSize.height) { + this.direction_ = Direction.HORIZONTAL; + this.widthLimit_ = containerSize.width - thumbWidth; + } else { + this.direction_ = Direction.VERTICAL; + this.heightLimit_ = containerSize.height - thumbHeight; + } + this.sliderInitialized_ = true; + } /** - * The direction of the slider. Will be determined from actual display of the - * container and defaults to Direction.VERTICAL. + * @param {MouseEvent} event The browser event to handle. + * @private + */ + handleContainerClick_(event) { + const view = this.getMap().getView(); + + const relativePosition = this.getRelativePosition_( + event.offsetX - this.thumbSize_[0] / 2, + event.offsetY - this.thumbSize_[1] / 2); + + const resolution = this.getResolutionForPosition_(relativePosition); + + view.animate({ + resolution: view.constrainResolution(resolution), + duration: this.duration_, + easing: easeOut + }); + } + + /** + * Handle dragger start events. + * @param {module:ol/pointer/PointerEvent} event The drag event. + * @private + */ + handleDraggerStart_(event) { + if (!this.dragging_ && event.originalEvent.target === this.element.firstElementChild) { + this.getMap().getView().setHint(ViewHint.INTERACTING, 1); + this.previousX_ = event.clientX; + this.previousY_ = event.clientY; + this.dragging_ = true; + } + } + + /** + * Handle dragger drag events. * - * @type {Direction} + * @param {module:ol/pointer/PointerEvent|Event} event The drag event. * @private */ - this.direction_ = Direction.VERTICAL; + handleDraggerDrag_(event) { + if (this.dragging_) { + const element = 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 relativePosition = this.getRelativePosition_(deltaX, deltaY); + this.currentResolution_ = this.getResolutionForPosition_(relativePosition); + this.getMap().getView().setResolution(this.currentResolution_); + this.setThumbPosition_(this.currentResolution_); + this.previousX_ = event.clientX; + this.previousY_ = event.clientY; + } + } /** - * @type {boolean} + * Handle dragger end events. + * @param {module:ol/pointer/PointerEvent|Event} event The drag event. * @private */ - this.dragging_; + handleDraggerEnd_(event) { + if (this.dragging_) { + const view = this.getMap().getView(); + view.setHint(ViewHint.INTERACTING, -1); + + view.animate({ + resolution: view.constrainResolution(this.currentResolution_), + duration: this.duration_, + easing: easeOut + }); + + this.dragging_ = false; + this.previousX_ = undefined; + this.previousY_ = undefined; + } + } /** - * @type {number} + * Positions the thumb inside its container according to the given resolution. + * + * @param {number} res The res. * @private */ - this.heightLimit_ = 0; + setThumbPosition_(res) { + const position = this.getPositionForResolution_(res); + const thumb = this.element.firstElementChild; + + if (this.direction_ == Direction.HORIZONTAL) { + thumb.style.left = this.widthLimit_ * position + 'px'; + } else { + thumb.style.top = this.heightLimit_ * position + 'px'; + } + } /** - * @type {number} + * Calculates the relative position of the thumb given x and y offsets. The + * relative position scales from 0 to 1. The x and y offsets are assumed to be + * in pixel units within the dragger limits. + * + * @param {number} x Pixel position relative to the left of the slider. + * @param {number} y Pixel position relative to the top of the slider. + * @return {number} The relative position of the thumb. * @private */ - this.widthLimit_ = 0; + getRelativePosition_(x, y) { + let amount; + if (this.direction_ === Direction.HORIZONTAL) { + amount = x / this.widthLimit_; + } else { + amount = y / this.heightLimit_; + } + return clamp(amount, 0, 1); + } /** - * @type {number|undefined} + * Calculates the corresponding resolution of the thumb given its relative + * position (where 0 is the minimum and 1 is the maximum). + * + * @param {number} position The relative position of the thumb. + * @return {number} The corresponding resolution. * @private */ - this.previousX_; + getResolutionForPosition_(position) { + const fn = this.getMap().getView().getResolutionForValueFunction(); + return fn(1 - position); + } /** - * @type {number|undefined} + * Determines the relative position of the slider for the given resolution. A + * relative position of 0 corresponds to the minimum view resolution. A + * relative position of 1 corresponds to the maximum view resolution. + * + * @param {number} res The resolution. + * @return {number} The relative position value (between 0 and 1). * @private */ - this.previousY_; - - /** - * The calculated thumb size (border box plus margins). Set when initSlider_ - * is called. - * @type {module:ol/size~Size} - * @private - */ - this.thumbSize_ = null; - - /** - * Whether the slider is initialized. - * @type {boolean} - * @private - */ - this.sliderInitialized_ = false; - - /** - * @type {number} - * @private - */ - this.duration_ = options.duration !== undefined ? options.duration : 200; - - const className = options.className !== undefined ? options.className : 'ol-zoomslider'; - const thumbElement = document.createElement('button'); - thumbElement.setAttribute('type', 'button'); - thumbElement.className = className + '-thumb ' + CLASS_UNSELECTABLE; - const containerElement = document.createElement('div'); - containerElement.className = className + ' ' + CLASS_UNSELECTABLE + ' ' + CLASS_CONTROL; - containerElement.appendChild(thumbElement); - /** - * @type {module:ol/pointer/PointerEventHandler} - * @private - */ - this.dragger_ = new PointerEventHandler(containerElement); - - listen(this.dragger_, PointerEventType.POINTERDOWN, - this.handleDraggerStart_, this); - listen(this.dragger_, PointerEventType.POINTERMOVE, - this.handleDraggerDrag_, this); - listen(this.dragger_, PointerEventType.POINTERUP, - this.handleDraggerEnd_, this); - - listen(containerElement, EventType.CLICK, this.handleContainerClick_, this); - listen(thumbElement, EventType.CLICK, stopPropagation); - - Control.call(this, { - element: containerElement, - render: options.render || render - }); -}; + getPositionForResolution_(res) { + const fn = this.getMap().getView().getValueForResolutionFunction(); + return 1 - fn(res); + } +} inherits(ZoomSlider, Control); -/** - * @inheritDoc - */ -ZoomSlider.prototype.disposeInternal = function() { - this.dragger_.dispose(); - Control.prototype.disposeInternal.call(this); -}; - - -/** - * @inheritDoc - */ -ZoomSlider.prototype.setMap = function(map) { - Control.prototype.setMap.call(this, map); - if (map) { - map.render(); - } -}; - - -/** - * Initializes the slider element. This will determine and set this controls - * direction_ and also constrain the dragging of the thumb to always be within - * the bounds of the container. - * - * @private - */ -ZoomSlider.prototype.initSlider_ = function() { - const container = this.element; - const containerSize = { - width: container.offsetWidth, height: container.offsetHeight - }; - - const thumb = container.firstElementChild; - const computedStyle = getComputedStyle(thumb); - const thumbWidth = thumb.offsetWidth + - parseFloat(computedStyle['marginRight']) + - parseFloat(computedStyle['marginLeft']); - const thumbHeight = thumb.offsetHeight + - parseFloat(computedStyle['marginTop']) + - parseFloat(computedStyle['marginBottom']); - this.thumbSize_ = [thumbWidth, thumbHeight]; - - if (containerSize.width > containerSize.height) { - this.direction_ = Direction.HORIZONTAL; - this.widthLimit_ = containerSize.width - thumbWidth; - } else { - this.direction_ = Direction.VERTICAL; - this.heightLimit_ = containerSize.height - thumbHeight; - } - this.sliderInitialized_ = true; -}; - - /** * Update the zoomslider element. * @param {module:ol/MapEvent} mapEvent Map event. @@ -226,151 +365,4 @@ export function render(mapEvent) { } -/** - * @param {MouseEvent} event The browser event to handle. - * @private - */ -ZoomSlider.prototype.handleContainerClick_ = function(event) { - const view = this.getMap().getView(); - - const relativePosition = this.getRelativePosition_( - event.offsetX - this.thumbSize_[0] / 2, - event.offsetY - this.thumbSize_[1] / 2); - - const resolution = this.getResolutionForPosition_(relativePosition); - - view.animate({ - resolution: view.constrainResolution(resolution), - duration: this.duration_, - easing: easeOut - }); -}; - - -/** - * Handle dragger start events. - * @param {module:ol/pointer/PointerEvent} event The drag event. - * @private - */ -ZoomSlider.prototype.handleDraggerStart_ = function(event) { - if (!this.dragging_ && event.originalEvent.target === this.element.firstElementChild) { - this.getMap().getView().setHint(ViewHint.INTERACTING, 1); - this.previousX_ = event.clientX; - this.previousY_ = event.clientY; - this.dragging_ = true; - } -}; - - -/** - * Handle dragger drag events. - * - * @param {module:ol/pointer/PointerEvent|Event} event The drag event. - * @private - */ -ZoomSlider.prototype.handleDraggerDrag_ = function(event) { - if (this.dragging_) { - const element = 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 relativePosition = this.getRelativePosition_(deltaX, deltaY); - this.currentResolution_ = this.getResolutionForPosition_(relativePosition); - this.getMap().getView().setResolution(this.currentResolution_); - this.setThumbPosition_(this.currentResolution_); - this.previousX_ = event.clientX; - this.previousY_ = event.clientY; - } -}; - - -/** - * Handle dragger end events. - * @param {module:ol/pointer/PointerEvent|Event} event The drag event. - * @private - */ -ZoomSlider.prototype.handleDraggerEnd_ = function(event) { - if (this.dragging_) { - const view = this.getMap().getView(); - view.setHint(ViewHint.INTERACTING, -1); - - view.animate({ - resolution: view.constrainResolution(this.currentResolution_), - duration: this.duration_, - easing: easeOut - }); - - this.dragging_ = false; - this.previousX_ = undefined; - this.previousY_ = undefined; - } -}; - - -/** - * Positions the thumb inside its container according to the given resolution. - * - * @param {number} res The res. - * @private - */ -ZoomSlider.prototype.setThumbPosition_ = function(res) { - const position = this.getPositionForResolution_(res); - const thumb = this.element.firstElementChild; - - if (this.direction_ == Direction.HORIZONTAL) { - thumb.style.left = this.widthLimit_ * position + 'px'; - } else { - thumb.style.top = this.heightLimit_ * position + 'px'; - } -}; - - -/** - * Calculates the relative position of the thumb given x and y offsets. The - * relative position scales from 0 to 1. The x and y offsets are assumed to be - * in pixel units within the dragger limits. - * - * @param {number} x Pixel position relative to the left of the slider. - * @param {number} y Pixel position relative to the top of the slider. - * @return {number} The relative position of the thumb. - * @private - */ -ZoomSlider.prototype.getRelativePosition_ = function(x, y) { - let amount; - if (this.direction_ === Direction.HORIZONTAL) { - amount = x / this.widthLimit_; - } else { - amount = y / this.heightLimit_; - } - return clamp(amount, 0, 1); -}; - - -/** - * Calculates the corresponding resolution of the thumb given its relative - * position (where 0 is the minimum and 1 is the maximum). - * - * @param {number} position The relative position of the thumb. - * @return {number} The corresponding resolution. - * @private - */ -ZoomSlider.prototype.getResolutionForPosition_ = function(position) { - const fn = this.getMap().getView().getResolutionForValueFunction(); - return fn(1 - position); -}; - - -/** - * Determines the relative position of the slider for the given resolution. A - * relative position of 0 corresponds to the minimum view resolution. A - * relative position of 1 corresponds to the maximum view resolution. - * - * @param {number} res The resolution. - * @return {number} The relative position value (between 0 and 1). - * @private - */ -ZoomSlider.prototype.getPositionForResolution_ = function(res) { - const fn = this.getMap().getView().getValueForResolutionFunction(); - return 1 - fn(res); -}; - export default ZoomSlider; diff --git a/src/ol/control/ZoomToExtent.js b/src/ol/control/ZoomToExtent.js index 49c8cf1b8b..cafa6d7190 100644 --- a/src/ol/control/ZoomToExtent.js +++ b/src/ol/control/ZoomToExtent.js @@ -31,59 +31,61 @@ import {CLASS_CONTROL, CLASS_UNSELECTABLE} from '../css.js'; * @param {module:ol/control/ZoomToExtent~Options=} opt_options Options. * @api */ -const ZoomToExtent = function(opt_options) { - const options = opt_options ? opt_options : {}; +class ZoomToExtent { + constructor(opt_options) { + const options = opt_options ? opt_options : {}; - /** - * @type {module:ol/extent~Extent} - * @protected - */ - this.extent = options.extent ? options.extent : null; + /** + * @type {module:ol/extent~Extent} + * @protected + */ + this.extent = options.extent ? options.extent : null; - const className = options.className !== undefined ? options.className : 'ol-zoom-extent'; + const className = options.className !== undefined ? options.className : 'ol-zoom-extent'; - const label = options.label !== undefined ? options.label : 'E'; - const tipLabel = options.tipLabel !== undefined ? options.tipLabel : 'Fit to extent'; - const button = document.createElement('button'); - button.setAttribute('type', 'button'); - button.title = tipLabel; - button.appendChild( - typeof label === 'string' ? document.createTextNode(label) : label - ); + const label = options.label !== undefined ? options.label : 'E'; + const tipLabel = options.tipLabel !== undefined ? options.tipLabel : 'Fit to extent'; + const button = document.createElement('button'); + button.setAttribute('type', 'button'); + button.title = tipLabel; + button.appendChild( + typeof label === 'string' ? document.createTextNode(label) : label + ); - listen(button, EventType.CLICK, this.handleClick_, this); + listen(button, EventType.CLICK, this.handleClick_, this); - const cssClasses = className + ' ' + CLASS_UNSELECTABLE + ' ' + CLASS_CONTROL; - const element = document.createElement('div'); - element.className = cssClasses; - element.appendChild(button); + const cssClasses = className + ' ' + CLASS_UNSELECTABLE + ' ' + CLASS_CONTROL; + const element = document.createElement('div'); + element.className = cssClasses; + element.appendChild(button); - Control.call(this, { - element: element, - target: options.target - }); -}; + Control.call(this, { + element: element, + target: options.target + }); + } + + /** + * @param {MouseEvent} event The event to handle + * @private + */ + handleClick_(event) { + event.preventDefault(); + this.handleZoomToExtent(); + } + + /** + * @protected + */ + handleZoomToExtent() { + const map = this.getMap(); + const view = map.getView(); + const extent = !this.extent ? view.getProjection().getExtent() : this.extent; + view.fit(extent); + } +} inherits(ZoomToExtent, Control); -/** - * @param {MouseEvent} event The event to handle - * @private - */ -ZoomToExtent.prototype.handleClick_ = function(event) { - event.preventDefault(); - this.handleZoomToExtent(); -}; - - -/** - * @protected - */ -ZoomToExtent.prototype.handleZoomToExtent = function() { - const map = this.getMap(); - const view = map.getView(); - const extent = !this.extent ? view.getProjection().getExtent() : this.extent; - view.fit(extent); -}; export default ZoomToExtent; diff --git a/src/ol/events/EventTarget.js b/src/ol/events/EventTarget.js index 1eaae0d5a0..e7cbaa9db4 100644 --- a/src/ol/events/EventTarget.js +++ b/src/ol/events/EventTarget.js @@ -31,137 +31,135 @@ import Event from '../events/Event.js'; * @constructor * @extends {module:ol/Disposable} */ -const EventTarget = function() { +class EventTarget { + constructor() { - Disposable.call(this); + Disposable.call(this); + + /** + * @private + * @type {!Object.} + */ + this.pendingRemovals_ = {}; + + /** + * @private + * @type {!Object.} + */ + this.dispatching_ = {}; + + /** + * @private + * @type {!Object.>} + */ + this.listeners_ = {}; + + } /** - * @private - * @type {!Object.} + * @param {string} type Type. + * @param {module:ol/events~ListenerFunction} listener Listener. */ - this.pendingRemovals_ = {}; + addEventListener(type, listener) { + let listeners = this.listeners_[type]; + if (!listeners) { + listeners = this.listeners_[type] = []; + } + if (listeners.indexOf(listener) === -1) { + listeners.push(listener); + } + } /** - * @private - * @type {!Object.} + * @param {{type: string, + * target: (EventTarget|module:ol/events/EventTarget|undefined)}|module:ol/events/Event| + * string} event Event or event type. + * @return {boolean|undefined} `false` if anyone called preventDefault on the + * event object or if any of the listeners returned false. */ - this.dispatching_ = {}; + dispatchEvent(event) { + const evt = typeof event === 'string' ? new Event(event) : event; + const type = evt.type; + evt.target = this; + const listeners = this.listeners_[type]; + let propagate; + if (listeners) { + if (!(type in this.dispatching_)) { + this.dispatching_[type] = 0; + this.pendingRemovals_[type] = 0; + } + ++this.dispatching_[type]; + for (let i = 0, ii = listeners.length; i < ii; ++i) { + if (listeners[i].call(this, evt) === false || evt.propagationStopped) { + propagate = false; + break; + } + } + --this.dispatching_[type]; + if (this.dispatching_[type] === 0) { + let pendingRemovals = this.pendingRemovals_[type]; + delete this.pendingRemovals_[type]; + while (pendingRemovals--) { + this.removeEventListener(type, UNDEFINED); + } + delete this.dispatching_[type]; + } + return propagate; + } + } /** - * @private - * @type {!Object.>} + * @inheritDoc */ - this.listeners_ = {}; + disposeInternal() { + unlistenAll(this); + } -}; + /** + * Get the listeners for a specified event type. Listeners are returned in the + * order that they will be called in. + * + * @param {string} type Type. + * @return {Array.} Listeners. + */ + getListeners(type) { + return this.listeners_[type]; + } + + /** + * @param {string=} opt_type Type. If not provided, + * `true` will be returned if this EventTarget has any listeners. + * @return {boolean} Has listeners. + */ + hasListener(opt_type) { + return opt_type ? + opt_type in this.listeners_ : + Object.keys(this.listeners_).length > 0; + } + + /** + * @param {string} type Type. + * @param {module:ol/events~ListenerFunction} listener Listener. + */ + removeEventListener(type, listener) { + const listeners = this.listeners_[type]; + if (listeners) { + const index = listeners.indexOf(listener); + if (type in this.pendingRemovals_) { + // make listener a no-op, and remove later in #dispatchEvent() + listeners[index] = UNDEFINED; + ++this.pendingRemovals_[type]; + } else { + listeners.splice(index, 1); + if (listeners.length === 0) { + delete this.listeners_[type]; + } + } + } + } +} inherits(EventTarget, Disposable); -/** - * @param {string} type Type. - * @param {module:ol/events~ListenerFunction} listener Listener. - */ -EventTarget.prototype.addEventListener = function(type, listener) { - let listeners = this.listeners_[type]; - if (!listeners) { - listeners = this.listeners_[type] = []; - } - if (listeners.indexOf(listener) === -1) { - listeners.push(listener); - } -}; - - -/** - * @param {{type: string, - * target: (EventTarget|module:ol/events/EventTarget|undefined)}|module:ol/events/Event| - * string} event Event or event type. - * @return {boolean|undefined} `false` if anyone called preventDefault on the - * event object or if any of the listeners returned false. - */ -EventTarget.prototype.dispatchEvent = function(event) { - const evt = typeof event === 'string' ? new Event(event) : event; - const type = evt.type; - evt.target = this; - const listeners = this.listeners_[type]; - let propagate; - if (listeners) { - if (!(type in this.dispatching_)) { - this.dispatching_[type] = 0; - this.pendingRemovals_[type] = 0; - } - ++this.dispatching_[type]; - for (let i = 0, ii = listeners.length; i < ii; ++i) { - if (listeners[i].call(this, evt) === false || evt.propagationStopped) { - propagate = false; - break; - } - } - --this.dispatching_[type]; - if (this.dispatching_[type] === 0) { - let pendingRemovals = this.pendingRemovals_[type]; - delete this.pendingRemovals_[type]; - while (pendingRemovals--) { - this.removeEventListener(type, UNDEFINED); - } - delete this.dispatching_[type]; - } - return propagate; - } -}; - - -/** - * @inheritDoc - */ -EventTarget.prototype.disposeInternal = function() { - unlistenAll(this); -}; - - -/** - * Get the listeners for a specified event type. Listeners are returned in the - * order that they will be called in. - * - * @param {string} type Type. - * @return {Array.} Listeners. - */ -EventTarget.prototype.getListeners = function(type) { - return this.listeners_[type]; -}; - - -/** - * @param {string=} opt_type Type. If not provided, - * `true` will be returned if this EventTarget has any listeners. - * @return {boolean} Has listeners. - */ -EventTarget.prototype.hasListener = function(opt_type) { - return opt_type ? - opt_type in this.listeners_ : - Object.keys(this.listeners_).length > 0; -}; - - -/** - * @param {string} type Type. - * @param {module:ol/events~ListenerFunction} listener Listener. - */ -EventTarget.prototype.removeEventListener = function(type, listener) { - const listeners = this.listeners_[type]; - if (listeners) { - const index = listeners.indexOf(listener); - if (type in this.pendingRemovals_) { - // make listener a no-op, and remove later in #dispatchEvent() - listeners[index] = UNDEFINED; - ++this.pendingRemovals_[type]; - } else { - listeners.splice(index, 1); - if (listeners.length === 0) { - delete this.listeners_[type]; - } - } - } -}; export default EventTarget; diff --git a/src/ol/format/EsriJSON.js b/src/ol/format/EsriJSON.js index 0ea7cd55a5..4b0ad55729 100644 --- a/src/ol/format/EsriJSON.js +++ b/src/ol/format/EsriJSON.js @@ -63,20 +63,148 @@ GEOMETRY_WRITERS[GeometryType.MULTI_POLYGON] = writeMultiPolygonGeometry; * @param {module:ol/format/EsriJSON~Options=} opt_options Options. * @api */ -const EsriJSON = function(opt_options) { +class EsriJSON { + constructor(opt_options) { - const options = opt_options ? opt_options : {}; + const options = opt_options ? opt_options : {}; - JSONFeature.call(this); + JSONFeature.call(this); + + /** + * Name of the geometry attribute for features. + * @type {string|undefined} + * @private + */ + this.geometryName_ = options.geometryName; + + } /** - * Name of the geometry attribute for features. - * @type {string|undefined} - * @private + * @inheritDoc */ - this.geometryName_ = options.geometryName; + readFeatureFromObject(object, opt_options) { + const esriJSONFeature = /** @type {EsriJSONFeature} */ (object); + const geometry = readGeometry(esriJSONFeature.geometry, opt_options); + const feature = new Feature(); + if (this.geometryName_) { + feature.setGeometryName(this.geometryName_); + } + feature.setGeometry(geometry); + if (opt_options && opt_options.idField && + esriJSONFeature.attributes[opt_options.idField]) { + feature.setId(/** @type {number} */(esriJSONFeature.attributes[opt_options.idField])); + } + if (esriJSONFeature.attributes) { + feature.setProperties(esriJSONFeature.attributes); + } + return feature; + } -}; + /** + * @inheritDoc + */ + readFeaturesFromObject(object, opt_options) { + const esriJSONObject = /** @type {EsriJSONObject} */ (object); + const options = opt_options ? opt_options : {}; + if (esriJSONObject.features) { + const esriJSONFeatureCollection = /** @type {EsriJSONFeatureCollection} */ (object); + /** @type {Array.} */ + const features = []; + const esriJSONFeatures = esriJSONFeatureCollection.features; + options.idField = object.objectIdFieldName; + for (let i = 0, ii = esriJSONFeatures.length; i < ii; ++i) { + features.push(this.readFeatureFromObject(esriJSONFeatures[i], options)); + } + return features; + } else { + return [this.readFeatureFromObject(object, options)]; + } + } + + /** + * @inheritDoc + */ + readGeometryFromObject(object, opt_options) { + return readGeometry(/** @type {EsriJSONGeometry} */(object), opt_options); + } + + /** + * @inheritDoc + */ + readProjectionFromObject(object) { + const esriJSONObject = /** @type {EsriJSONObject} */ (object); + if (esriJSONObject.spatialReference && esriJSONObject.spatialReference.wkid) { + const crs = esriJSONObject.spatialReference.wkid; + return getProjection('EPSG:' + crs); + } else { + return null; + } + } + + /** + * Encode a geometry as a EsriJSON object. + * + * @param {module:ol/geom/Geometry} geometry Geometry. + * @param {module:ol/format/Feature~WriteOptions=} opt_options Write options. + * @return {EsriJSONGeometry} Object. + * @override + * @api + */ + writeGeometryObject(geometry, opt_options) { + return writeGeometry(geometry, this.adaptOptions(opt_options)); + } + + /** + * Encode a feature as a esriJSON Feature object. + * + * @param {module:ol/Feature} feature Feature. + * @param {module:ol/format/Feature~WriteOptions=} opt_options Write options. + * @return {Object} Object. + * @override + * @api + */ + writeFeatureObject(feature, opt_options) { + opt_options = this.adaptOptions(opt_options); + const object = {}; + const geometry = feature.getGeometry(); + 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() + }); + } + } + const properties = feature.getProperties(); + delete properties[feature.getGeometryName()]; + if (!isEmpty(properties)) { + object['attributes'] = properties; + } else { + object['attributes'] = {}; + } + return object; + } + + /** + * Encode an array of features as a EsriJSON object. + * + * @param {Array.} features Features. + * @param {module:ol/format/Feature~WriteOptions=} opt_options Write options. + * @return {Object} EsriJSON Object. + * @override + * @api + */ + writeFeaturesObject(features, opt_options) { + opt_options = this.adaptOptions(opt_options); + const objects = []; + for (let i = 0, ii = features.length; i < ii; ++i) { + objects.push(this.writeFeatureObject(features[i], opt_options)); + } + return /** @type {EsriJSONFeatureCollection} */ ({ + 'features': objects + }); + } +} inherits(EsriJSON, JSONFeature); @@ -439,50 +567,6 @@ EsriJSON.prototype.readFeature; EsriJSON.prototype.readFeatures; -/** - * @inheritDoc - */ -EsriJSON.prototype.readFeatureFromObject = function(object, opt_options) { - const esriJSONFeature = /** @type {EsriJSONFeature} */ (object); - const geometry = readGeometry(esriJSONFeature.geometry, opt_options); - const feature = new Feature(); - if (this.geometryName_) { - feature.setGeometryName(this.geometryName_); - } - feature.setGeometry(geometry); - if (opt_options && opt_options.idField && - esriJSONFeature.attributes[opt_options.idField]) { - feature.setId(/** @type {number} */(esriJSONFeature.attributes[opt_options.idField])); - } - if (esriJSONFeature.attributes) { - feature.setProperties(esriJSONFeature.attributes); - } - return feature; -}; - - -/** - * @inheritDoc - */ -EsriJSON.prototype.readFeaturesFromObject = function(object, opt_options) { - const esriJSONObject = /** @type {EsriJSONObject} */ (object); - const options = opt_options ? opt_options : {}; - if (esriJSONObject.features) { - const esriJSONFeatureCollection = /** @type {EsriJSONFeatureCollection} */ (object); - /** @type {Array.} */ - const features = []; - const esriJSONFeatures = esriJSONFeatureCollection.features; - options.idField = object.objectIdFieldName; - for (let i = 0, ii = esriJSONFeatures.length; i < ii; ++i) { - features.push(this.readFeatureFromObject(esriJSONFeatures[i], options)); - } - return features; - } else { - return [this.readFeatureFromObject(object, options)]; - } -}; - - /** * Read a geometry from a EsriJSON source. * @@ -495,14 +579,6 @@ EsriJSON.prototype.readFeaturesFromObject = function(object, opt_options) { EsriJSON.prototype.readGeometry; -/** - * @inheritDoc - */ -EsriJSON.prototype.readGeometryFromObject = function(object, opt_options) { - return readGeometry(/** @type {EsriJSONGeometry} */(object), opt_options); -}; - - /** * Read the projection from a EsriJSON source. * @@ -514,20 +590,6 @@ EsriJSON.prototype.readGeometryFromObject = function(object, opt_options) { EsriJSON.prototype.readProjection; -/** - * @inheritDoc - */ -EsriJSON.prototype.readProjectionFromObject = function(object) { - const esriJSONObject = /** @type {EsriJSONObject} */ (object); - if (esriJSONObject.spatialReference && esriJSONObject.spatialReference.wkid) { - const crs = esriJSONObject.spatialReference.wkid; - return getProjection('EPSG:' + crs); - } else { - return null; - } -}; - - /** * @param {module:ol/geom/Geometry} geometry Geometry. * @param {module:ol/format/Feature~WriteOptions=} opt_options Write options. @@ -552,20 +614,6 @@ function writeGeometry(geometry, opt_options) { EsriJSON.prototype.writeGeometry; -/** - * Encode a geometry as a EsriJSON object. - * - * @param {module:ol/geom/Geometry} geometry Geometry. - * @param {module:ol/format/Feature~WriteOptions=} opt_options Write options. - * @return {EsriJSONGeometry} Object. - * @override - * @api - */ -EsriJSON.prototype.writeGeometryObject = function(geometry, opt_options) { - return writeGeometry(geometry, this.adaptOptions(opt_options)); -}; - - /** * Encode a feature as a EsriJSON Feature string. * @@ -578,38 +626,6 @@ EsriJSON.prototype.writeGeometryObject = function(geometry, opt_options) { EsriJSON.prototype.writeFeature; -/** - * Encode a feature as a esriJSON Feature object. - * - * @param {module:ol/Feature} feature Feature. - * @param {module:ol/format/Feature~WriteOptions=} opt_options Write options. - * @return {Object} Object. - * @override - * @api - */ -EsriJSON.prototype.writeFeatureObject = function(feature, opt_options) { - opt_options = this.adaptOptions(opt_options); - const object = {}; - const geometry = feature.getGeometry(); - 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() - }); - } - } - const properties = feature.getProperties(); - delete properties[feature.getGeometryName()]; - if (!isEmpty(properties)) { - object['attributes'] = properties; - } else { - object['attributes'] = {}; - } - return object; -}; - - /** * Encode an array of features as EsriJSON. * @@ -622,24 +638,4 @@ EsriJSON.prototype.writeFeatureObject = function(feature, opt_options) { EsriJSON.prototype.writeFeatures; -/** - * Encode an array of features as a EsriJSON object. - * - * @param {Array.} features Features. - * @param {module:ol/format/Feature~WriteOptions=} opt_options Write options. - * @return {Object} EsriJSON Object. - * @override - * @api - */ -EsriJSON.prototype.writeFeaturesObject = function(features, opt_options) { - opt_options = this.adaptOptions(opt_options); - const objects = []; - for (let i = 0, ii = features.length; i < ii; ++i) { - objects.push(this.writeFeatureObject(features[i], opt_options)); - } - return /** @type {EsriJSONFeatureCollection} */ ({ - 'features': objects - }); -}; - export default EsriJSON; diff --git a/src/ol/format/Feature.js b/src/ol/format/Feature.js index 6c770c93a9..089cbc518b 100644 --- a/src/ol/format/Feature.js +++ b/src/ol/format/Feature.js @@ -60,150 +60,141 @@ import {get as getProjection, equivalent as equivalentProjection, transformExten * @abstract * @api */ -const FeatureFormat = function() { +class FeatureFormat { + constructor() { - /** - * @protected - * @type {module:ol/proj/Projection} - */ - this.dataProjection = null; + /** + * @protected + * @type {module:ol/proj/Projection} + */ + this.dataProjection = null; - /** - * @protected - * @type {module:ol/proj/Projection} - */ - this.defaultFeatureProjection = null; + /** + * @protected + * @type {module:ol/proj/Projection} + */ + this.defaultFeatureProjection = null; -}; + } + /** + * Adds the data projection to the read options. + * @param {Document|Node|Object|string} source Source. + * @param {module:ol/format/Feature~ReadOptions=} opt_options Options. + * @return {module:ol/format/Feature~ReadOptions|undefined} Options. + * @protected + */ + getReadOptions(source, opt_options) { + let options; + if (opt_options) { + options = { + dataProjection: opt_options.dataProjection ? + opt_options.dataProjection : this.readProjection(source), + featureProjection: opt_options.featureProjection + }; + } + return this.adaptOptions(options); + } -/** - * Adds the data projection to the read options. - * @param {Document|Node|Object|string} source Source. - * @param {module:ol/format/Feature~ReadOptions=} opt_options Options. - * @return {module:ol/format/Feature~ReadOptions|undefined} Options. - * @protected - */ -FeatureFormat.prototype.getReadOptions = function(source, opt_options) { - let options; - if (opt_options) { - options = { - dataProjection: opt_options.dataProjection ? - opt_options.dataProjection : this.readProjection(source), - featureProjection: opt_options.featureProjection - }; - } - return this.adaptOptions(options); -}; + /** + * Sets the `dataProjection` on the options, if no `dataProjection` + * is set. + * @param {module:ol/format/Feature~WriteOptions|module:ol/format/Feature~ReadOptions|undefined} options + * Options. + * @protected + * @return {module:ol/format/Feature~WriteOptions|module:ol/format/Feature~ReadOptions|undefined} + * Updated options. + */ + adaptOptions(options) { + return assign({ + dataProjection: this.dataProjection, + featureProjection: this.defaultFeatureProjection + }, options); + } + /** + * Get the extent from the source of the last {@link readFeatures} call. + * @return {module:ol/extent~Extent} Tile extent. + */ + getLastExtent() { + return null; + } -/** - * Sets the `dataProjection` on the options, if no `dataProjection` - * is set. - * @param {module:ol/format/Feature~WriteOptions|module:ol/format/Feature~ReadOptions|undefined} options - * Options. - * @protected - * @return {module:ol/format/Feature~WriteOptions|module:ol/format/Feature~ReadOptions|undefined} - * Updated options. - */ -FeatureFormat.prototype.adaptOptions = function(options) { - return assign({ - dataProjection: this.dataProjection, - featureProjection: this.defaultFeatureProjection - }, options); -}; + /** + * @abstract + * @return {module:ol/format/FormatType} Format. + */ + getType() {} + /** + * Read a single feature from a source. + * + * @abstract + * @param {Document|Node|Object|string} source Source. + * @param {module:ol/format/Feature~ReadOptions=} opt_options Read options. + * @return {module:ol/Feature} Feature. + */ + readFeature(source, opt_options) {} -/** - * Get the extent from the source of the last {@link readFeatures} call. - * @return {module:ol/extent~Extent} Tile extent. - */ -FeatureFormat.prototype.getLastExtent = function() { - return null; -}; + /** + * Read all features from a source. + * + * @abstract + * @param {Document|Node|ArrayBuffer|Object|string} source Source. + * @param {module:ol/format/Feature~ReadOptions=} opt_options Read options. + * @return {Array.} Features. + */ + readFeatures(source, opt_options) {} + /** + * Read a single geometry from a source. + * + * @abstract + * @param {Document|Node|Object|string} source Source. + * @param {module:ol/format/Feature~ReadOptions=} opt_options Read options. + * @return {module:ol/geom/Geometry} Geometry. + */ + readGeometry(source, opt_options) {} -/** - * @abstract - * @return {module:ol/format/FormatType} Format. - */ -FeatureFormat.prototype.getType = function() {}; + /** + * Read the projection from a source. + * + * @abstract + * @param {Document|Node|Object|string} source Source. + * @return {module:ol/proj/Projection} Projection. + */ + readProjection(source) {} + /** + * Encode a feature in this format. + * + * @abstract + * @param {module:ol/Feature} feature Feature. + * @param {module:ol/format/Feature~WriteOptions=} opt_options Write options. + * @return {string} Result. + */ + writeFeature(feature, opt_options) {} -/** - * Read a single feature from a source. - * - * @abstract - * @param {Document|Node|Object|string} source Source. - * @param {module:ol/format/Feature~ReadOptions=} opt_options Read options. - * @return {module:ol/Feature} Feature. - */ -FeatureFormat.prototype.readFeature = function(source, opt_options) {}; + /** + * Encode an array of features in this format. + * + * @abstract + * @param {Array.} features Features. + * @param {module:ol/format/Feature~WriteOptions=} opt_options Write options. + * @return {string} Result. + */ + writeFeatures(features, opt_options) {} - -/** - * Read all features from a source. - * - * @abstract - * @param {Document|Node|ArrayBuffer|Object|string} source Source. - * @param {module:ol/format/Feature~ReadOptions=} opt_options Read options. - * @return {Array.} Features. - */ -FeatureFormat.prototype.readFeatures = function(source, opt_options) {}; - - -/** - * Read a single geometry from a source. - * - * @abstract - * @param {Document|Node|Object|string} source Source. - * @param {module:ol/format/Feature~ReadOptions=} opt_options Read options. - * @return {module:ol/geom/Geometry} Geometry. - */ -FeatureFormat.prototype.readGeometry = function(source, opt_options) {}; - - -/** - * Read the projection from a source. - * - * @abstract - * @param {Document|Node|Object|string} source Source. - * @return {module:ol/proj/Projection} Projection. - */ -FeatureFormat.prototype.readProjection = function(source) {}; - - -/** - * Encode a feature in this format. - * - * @abstract - * @param {module:ol/Feature} feature Feature. - * @param {module:ol/format/Feature~WriteOptions=} opt_options Write options. - * @return {string} Result. - */ -FeatureFormat.prototype.writeFeature = function(feature, opt_options) {}; - - -/** - * Encode an array of features in this format. - * - * @abstract - * @param {Array.} features Features. - * @param {module:ol/format/Feature~WriteOptions=} opt_options Write options. - * @return {string} Result. - */ -FeatureFormat.prototype.writeFeatures = function(features, opt_options) {}; - - -/** - * Write a single geometry in this format. - * - * @abstract - * @param {module:ol/geom/Geometry} geometry Geometry. - * @param {module:ol/format/Feature~WriteOptions=} opt_options Write options. - * @return {string} Result. - */ -FeatureFormat.prototype.writeGeometry = function(geometry, opt_options) {}; + /** + * Write a single geometry in this format. + * + * @abstract + * @param {module:ol/geom/Geometry} geometry Geometry. + * @param {module:ol/format/Feature~WriteOptions=} opt_options Write options. + * @return {string} Result. + */ + writeGeometry(geometry, opt_options) {} +} export default FeatureFormat; diff --git a/src/ol/format/GML2.js b/src/ol/format/GML2.js index 0cdd72c3ea..b16365f1e0 100644 --- a/src/ol/format/GML2.js +++ b/src/ol/format/GML2.js @@ -30,559 +30,550 @@ const schemaLocation = GMLNS + ' http://schemas.opengis.net/gml/2.1.2/feature.xs * @extends {module:ol/format/GMLBase} * @api */ -const GML2 = function(opt_options) { - const options = /** @type {module:ol/format/GMLBase~Options} */ - (opt_options ? opt_options : {}); +class GML2 { + constructor(opt_options) { + const options = /** @type {module:ol/format/GMLBase~Options} */ + (opt_options ? opt_options : {}); - GMLBase.call(this, options); + GMLBase.call(this, options); - this.FEATURE_COLLECTION_PARSERS[GMLNS][ - 'featureMember'] = - makeArrayPusher(GMLBase.prototype.readFeaturesInternal); + this.FEATURE_COLLECTION_PARSERS[GMLNS][ + 'featureMember'] = + makeArrayPusher(GMLBase.prototype.readFeaturesInternal); + + /** + * @inheritDoc + */ + this.schemaLocation = options.schemaLocation ? + options.schemaLocation : schemaLocation; + + } /** - * @inheritDoc + * @param {Node} node Node. + * @param {Array.<*>} objectStack Object stack. + * @private + * @return {Array.|undefined} Flat coordinates. */ - this.schemaLocation = options.schemaLocation ? - options.schemaLocation : schemaLocation; - -}; - -inherits(GML2, GMLBase); - - -/** - * @param {Node} node Node. - * @param {Array.<*>} objectStack Object stack. - * @private - * @return {Array.|undefined} Flat coordinates. - */ -GML2.prototype.readFlatCoordinates_ = function(node, objectStack) { - const s = getAllTextContent(node, false).replace(/^\s*|\s*$/g, ''); - const context = /** @type {module:ol/xml~NodeStackItem} */ (objectStack[0]); - const containerSrs = context['srsName']; - let axisOrientation = 'enu'; - if (containerSrs) { - const proj = getProjection(containerSrs); - if (proj) { - axisOrientation = proj.getAxisOrientation(); + readFlatCoordinates_(node, objectStack) { + const s = getAllTextContent(node, false).replace(/^\s*|\s*$/g, ''); + const context = /** @type {module:ol/xml~NodeStackItem} */ (objectStack[0]); + const containerSrs = context['srsName']; + let axisOrientation = 'enu'; + if (containerSrs) { + const proj = getProjection(containerSrs); + if (proj) { + axisOrientation = proj.getAxisOrientation(); + } } - } - const coordsGroups = s.trim().split(/\s+/); - const flatCoordinates = []; - for (let i = 0, ii = coordsGroups.length; i < ii; i++) { - const coords = coordsGroups[i].split(/,+/); - const x = parseFloat(coords[0]); - const y = parseFloat(coords[1]); - const z = (coords.length === 3) ? parseFloat(coords[2]) : 0; - if (axisOrientation.substr(0, 2) === 'en') { - flatCoordinates.push(x, y, z); - } else { - flatCoordinates.push(y, x, z); - } - } - return flatCoordinates; -}; - - -/** - * @param {Node} node Node. - * @param {Array.<*>} objectStack Object stack. - * @private - * @return {module:ol/extent~Extent|undefined} Envelope. - */ -GML2.prototype.readBox_ = function(node, objectStack) { - /** @type {Array.} */ - const flatCoordinates = pushParseAndPop([null], - this.BOX_PARSERS_, node, objectStack, this); - return createOrUpdate(flatCoordinates[1][0], - flatCoordinates[1][1], flatCoordinates[1][3], - flatCoordinates[1][4]); -}; - - -/** - * @param {Node} node Node. - * @param {Array.<*>} objectStack Object stack. - * @private - */ -GML2.prototype.innerBoundaryIsParser_ = function(node, objectStack) { - /** @type {Array.|undefined} */ - const flatLinearRing = pushParseAndPop(undefined, - this.RING_PARSERS, node, objectStack, this); - if (flatLinearRing) { - const flatLinearRings = /** @type {Array.>} */ - (objectStack[objectStack.length - 1]); - flatLinearRings.push(flatLinearRing); - } -}; - - -/** - * @param {Node} node Node. - * @param {Array.<*>} objectStack Object stack. - * @private - */ -GML2.prototype.outerBoundaryIsParser_ = function(node, objectStack) { - /** @type {Array.|undefined} */ - const flatLinearRing = pushParseAndPop(undefined, - this.RING_PARSERS, node, objectStack, this); - if (flatLinearRing) { - const flatLinearRings = /** @type {Array.>} */ - (objectStack[objectStack.length - 1]); - flatLinearRings[0] = flatLinearRing; - } -}; - - -/** - * @const - * @param {*} value Value. - * @param {Array.<*>} objectStack Object stack. - * @param {string=} opt_nodeName Node name. - * @return {Node|undefined} Node. - * @private - */ -GML2.prototype.GEOMETRY_NODE_FACTORY_ = function(value, objectStack, opt_nodeName) { - const context = objectStack[objectStack.length - 1]; - const multiSurface = context['multiSurface']; - const surface = context['surface']; - const multiCurve = context['multiCurve']; - let nodeName; - if (!Array.isArray(value)) { - nodeName = /** @type {module:ol/geom/Geometry} */ (value).getType(); - if (nodeName === 'MultiPolygon' && multiSurface === true) { - nodeName = 'MultiSurface'; - } else if (nodeName === 'Polygon' && surface === true) { - nodeName = 'Surface'; - } else if (nodeName === 'MultiLineString' && multiCurve === true) { - nodeName = 'MultiCurve'; - } - } else { - nodeName = 'Envelope'; - } - return createElementNS('http://www.opengis.net/gml', - nodeName); -}; - - -/** - * @param {Node} node Node. - * @param {module:ol/Feature} feature Feature. - * @param {Array.<*>} objectStack Node stack. - */ -GML2.prototype.writeFeatureElement = function(node, feature, objectStack) { - const fid = feature.getId(); - if (fid) { - node.setAttribute('fid', fid); - } - const context = /** @type {Object} */ (objectStack[objectStack.length - 1]); - const featureNS = context['featureNS']; - const geometryName = feature.getGeometryName(); - if (!context.serializers) { - context.serializers = {}; - context.serializers[featureNS] = {}; - } - const properties = feature.getProperties(); - const keys = []; - const values = []; - for (const key in properties) { - const value = properties[key]; - if (value !== null) { - keys.push(key); - values.push(value); - if (key == geometryName || value instanceof Geometry) { - if (!(key in context.serializers[featureNS])) { - context.serializers[featureNS][key] = makeChildAppender( - this.writeGeometryElement, this); - } + const coordsGroups = s.trim().split(/\s+/); + const flatCoordinates = []; + for (let i = 0, ii = coordsGroups.length; i < ii; i++) { + const coords = coordsGroups[i].split(/,+/); + const x = parseFloat(coords[0]); + const y = parseFloat(coords[1]); + const z = (coords.length === 3) ? parseFloat(coords[2]) : 0; + if (axisOrientation.substr(0, 2) === 'en') { + flatCoordinates.push(x, y, z); } else { - if (!(key in context.serializers[featureNS])) { - context.serializers[featureNS][key] = makeChildAppender(writeStringTextNode); + flatCoordinates.push(y, x, z); + } + } + return flatCoordinates; + } + + /** + * @param {Node} node Node. + * @param {Array.<*>} objectStack Object stack. + * @private + * @return {module:ol/extent~Extent|undefined} Envelope. + */ + readBox_(node, objectStack) { + /** @type {Array.} */ + const flatCoordinates = pushParseAndPop([null], + this.BOX_PARSERS_, node, objectStack, this); + return createOrUpdate(flatCoordinates[1][0], + flatCoordinates[1][1], flatCoordinates[1][3], + flatCoordinates[1][4]); + } + + /** + * @param {Node} node Node. + * @param {Array.<*>} objectStack Object stack. + * @private + */ + innerBoundaryIsParser_(node, objectStack) { + /** @type {Array.|undefined} */ + const flatLinearRing = pushParseAndPop(undefined, + this.RING_PARSERS, node, objectStack, this); + if (flatLinearRing) { + const flatLinearRings = /** @type {Array.>} */ + (objectStack[objectStack.length - 1]); + flatLinearRings.push(flatLinearRing); + } + } + + /** + * @param {Node} node Node. + * @param {Array.<*>} objectStack Object stack. + * @private + */ + outerBoundaryIsParser_(node, objectStack) { + /** @type {Array.|undefined} */ + const flatLinearRing = pushParseAndPop(undefined, + this.RING_PARSERS, node, objectStack, this); + if (flatLinearRing) { + const flatLinearRings = /** @type {Array.>} */ + (objectStack[objectStack.length - 1]); + flatLinearRings[0] = flatLinearRing; + } + } + + /** + * @const + * @param {*} value Value. + * @param {Array.<*>} objectStack Object stack. + * @param {string=} opt_nodeName Node name. + * @return {Node|undefined} Node. + * @private + */ + GEOMETRY_NODE_FACTORY_(value, objectStack, opt_nodeName) { + const context = objectStack[objectStack.length - 1]; + const multiSurface = context['multiSurface']; + const surface = context['surface']; + const multiCurve = context['multiCurve']; + let nodeName; + if (!Array.isArray(value)) { + nodeName = /** @type {module:ol/geom/Geometry} */ (value).getType(); + if (nodeName === 'MultiPolygon' && multiSurface === true) { + nodeName = 'MultiSurface'; + } else if (nodeName === 'Polygon' && surface === true) { + nodeName = 'Surface'; + } else if (nodeName === 'MultiLineString' && multiCurve === true) { + nodeName = 'MultiCurve'; + } + } else { + nodeName = 'Envelope'; + } + return createElementNS('http://www.opengis.net/gml', + nodeName); + } + + /** + * @param {Node} node Node. + * @param {module:ol/Feature} feature Feature. + * @param {Array.<*>} objectStack Node stack. + */ + writeFeatureElement(node, feature, objectStack) { + const fid = feature.getId(); + if (fid) { + node.setAttribute('fid', fid); + } + const context = /** @type {Object} */ (objectStack[objectStack.length - 1]); + const featureNS = context['featureNS']; + const geometryName = feature.getGeometryName(); + if (!context.serializers) { + context.serializers = {}; + context.serializers[featureNS] = {}; + } + const properties = feature.getProperties(); + const keys = []; + const values = []; + for (const key in properties) { + const value = properties[key]; + if (value !== null) { + keys.push(key); + values.push(value); + if (key == geometryName || value instanceof Geometry) { + if (!(key in context.serializers[featureNS])) { + context.serializers[featureNS][key] = makeChildAppender( + this.writeGeometryElement, this); + } + } else { + if (!(key in context.serializers[featureNS])) { + context.serializers[featureNS][key] = makeChildAppender(writeStringTextNode); + } } } } + const item = assign({}, context); + item.node = node; + pushSerializeAndPop(/** @type {module:ol/xml~NodeStackItem} */ + (item), context.serializers, + makeSimpleNodeFactory(undefined, featureNS), + values, + objectStack, keys); } - const item = assign({}, context); - item.node = node; - pushSerializeAndPop(/** @type {module:ol/xml~NodeStackItem} */ - (item), context.serializers, - makeSimpleNodeFactory(undefined, featureNS), - values, - objectStack, keys); -}; - -/** - * @param {Node} node Node. - * @param {module:ol/geom/LineString} geometry LineString geometry. - * @param {Array.<*>} objectStack Node stack. - * @private - */ -GML2.prototype.writeCurveOrLineString_ = function(node, geometry, objectStack) { - const context = objectStack[objectStack.length - 1]; - const srsName = context['srsName']; - if (node.nodeName !== 'LineStringSegment' && srsName) { - node.setAttribute('srsName', srsName); + /** + * @param {Node} node Node. + * @param {module:ol/geom/LineString} geometry LineString geometry. + * @param {Array.<*>} objectStack Node stack. + * @private + */ + writeCurveOrLineString_(node, geometry, objectStack) { + const context = objectStack[objectStack.length - 1]; + const srsName = context['srsName']; + if (node.nodeName !== 'LineStringSegment' && srsName) { + node.setAttribute('srsName', srsName); + } + if (node.nodeName === 'LineString' || + node.nodeName === 'LineStringSegment') { + const coordinates = this.createCoordinatesNode_(node.namespaceURI); + node.appendChild(coordinates); + this.writeCoordinates_(coordinates, geometry, objectStack); + } else if (node.nodeName === 'Curve') { + const segments = createElementNS(node.namespaceURI, 'segments'); + node.appendChild(segments); + this.writeCurveSegments_(segments, + geometry, objectStack); + } } - if (node.nodeName === 'LineString' || - node.nodeName === 'LineStringSegment') { - const coordinates = this.createCoordinatesNode_(node.namespaceURI); - node.appendChild(coordinates); - this.writeCoordinates_(coordinates, geometry, objectStack); - } else if (node.nodeName === 'Curve') { - const segments = createElementNS(node.namespaceURI, 'segments'); - node.appendChild(segments); - this.writeCurveSegments_(segments, - geometry, objectStack); + + /** + * @param {Node} node Node. + * @param {module:ol/geom/LineString} line LineString geometry. + * @param {Array.<*>} objectStack Node stack. + * @private + */ + writeLineStringOrCurveMember_(node, line, objectStack) { + const child = this.GEOMETRY_NODE_FACTORY_(line, objectStack); + if (child) { + node.appendChild(child); + this.writeCurveOrLineString_(child, line, objectStack); + } } -}; + /** + * @param {Node} node Node. + * @param {module:ol/geom/MultiLineString} geometry MultiLineString geometry. + * @param {Array.<*>} objectStack Node stack. + * @private + */ + writeMultiCurveOrLineString_(node, geometry, objectStack) { + const context = objectStack[objectStack.length - 1]; + const hasZ = context['hasZ']; + const srsName = context['srsName']; + const curve = context['curve']; + if (srsName) { + node.setAttribute('srsName', srsName); + } + const lines = geometry.getLineStrings(); + pushSerializeAndPop({node: node, hasZ: hasZ, srsName: srsName, curve: curve}, + this.LINESTRINGORCURVEMEMBER_SERIALIZERS_, + this.MULTIGEOMETRY_MEMBER_NODE_FACTORY_, lines, + objectStack, undefined, this); + } -/** - * @param {Node} node Node. - * @param {module:ol/geom/LineString} line LineString geometry. - * @param {Array.<*>} objectStack Node stack. - * @private - */ -GML2.prototype.writeLineStringOrCurveMember_ = function(node, line, objectStack) { - const child = this.GEOMETRY_NODE_FACTORY_(line, objectStack); - if (child) { + /** + * @param {Node} node Node. + * @param {module:ol/geom/Geometry|module:ol/extent~Extent} geometry Geometry. + * @param {Array.<*>} objectStack Node stack. + */ + writeGeometryElement(node, geometry, objectStack) { + const context = /** @type {module:ol/format/Feature~WriteOptions} */ (objectStack[objectStack.length - 1]); + const item = assign({}, context); + item.node = node; + let value; + if (Array.isArray(geometry)) { + if (context.dataProjection) { + value = transformExtent( + geometry, context.featureProjection, context.dataProjection); + } else { + value = geometry; + } + } else { + value = transformWithOptions(/** @type {module:ol/geom/Geometry} */ (geometry), true, context); + } + pushSerializeAndPop(/** @type {module:ol/xml~NodeStackItem} */ + (item), this.GEOMETRY_SERIALIZERS_, + this.GEOMETRY_NODE_FACTORY_, [value], + objectStack, undefined, this); + } + + /** + * @param {string} namespaceURI XML namespace. + * @returns {Node} coordinates node. + * @private + */ + createCoordinatesNode_(namespaceURI) { + const coordinates = createElementNS(namespaceURI, 'coordinates'); + coordinates.setAttribute('decimal', '.'); + coordinates.setAttribute('cs', ','); + coordinates.setAttribute('ts', ' '); + + return coordinates; + } + + /** + * @param {Node} node Node. + * @param {module:ol/geom/LineString|module:ol/geom/LinearRing} value Geometry. + * @param {Array.<*>} objectStack Node stack. + * @private + */ + writeCoordinates_(node, value, objectStack) { + const context = objectStack[objectStack.length - 1]; + const hasZ = context['hasZ']; + const srsName = context['srsName']; + // only 2d for simple features profile + const points = value.getCoordinates(); + const len = points.length; + const parts = new Array(len); + for (let i = 0; i < len; ++i) { + const point = points[i]; + parts[i] = this.getCoords_(point, srsName, hasZ); + } + writeStringTextNode(node, parts.join(' ')); + } + + /** + * @param {Node} node Node. + * @param {module:ol/geom/LineString} line LineString geometry. + * @param {Array.<*>} objectStack Node stack. + * @private + */ + writeCurveSegments_(node, line, objectStack) { + const child = createElementNS(node.namespaceURI, 'LineStringSegment'); node.appendChild(child); this.writeCurveOrLineString_(child, line, objectStack); } -}; - -/** - * @param {Node} node Node. - * @param {module:ol/geom/MultiLineString} geometry MultiLineString geometry. - * @param {Array.<*>} objectStack Node stack. - * @private - */ -GML2.prototype.writeMultiCurveOrLineString_ = function(node, geometry, objectStack) { - const context = objectStack[objectStack.length - 1]; - const hasZ = context['hasZ']; - const srsName = context['srsName']; - const curve = context['curve']; - if (srsName) { - node.setAttribute('srsName', srsName); - } - const lines = geometry.getLineStrings(); - pushSerializeAndPop({node: node, hasZ: hasZ, srsName: srsName, curve: curve}, - this.LINESTRINGORCURVEMEMBER_SERIALIZERS_, - this.MULTIGEOMETRY_MEMBER_NODE_FACTORY_, lines, - objectStack, undefined, this); -}; - - -/** - * @param {Node} node Node. - * @param {module:ol/geom/Geometry|module:ol/extent~Extent} geometry Geometry. - * @param {Array.<*>} objectStack Node stack. - */ -GML2.prototype.writeGeometryElement = function(node, geometry, objectStack) { - const context = /** @type {module:ol/format/Feature~WriteOptions} */ (objectStack[objectStack.length - 1]); - const item = assign({}, context); - item.node = node; - let value; - if (Array.isArray(geometry)) { - if (context.dataProjection) { - value = transformExtent( - geometry, context.featureProjection, context.dataProjection); - } else { - value = geometry; + /** + * @param {Node} node Node. + * @param {module:ol/geom/Polygon} geometry Polygon geometry. + * @param {Array.<*>} objectStack Node stack. + * @private + */ + writeSurfaceOrPolygon_(node, geometry, objectStack) { + const context = objectStack[objectStack.length - 1]; + const hasZ = context['hasZ']; + const srsName = context['srsName']; + if (node.nodeName !== 'PolygonPatch' && srsName) { + node.setAttribute('srsName', srsName); + } + if (node.nodeName === 'Polygon' || node.nodeName === 'PolygonPatch') { + const rings = geometry.getLinearRings(); + pushSerializeAndPop( + {node: node, hasZ: hasZ, srsName: srsName}, + this.RING_SERIALIZERS_, + this.RING_NODE_FACTORY_, + rings, objectStack, undefined, this); + } else if (node.nodeName === 'Surface') { + const patches = createElementNS(node.namespaceURI, 'patches'); + node.appendChild(patches); + this.writeSurfacePatches_( + patches, geometry, objectStack); } - } else { - value = transformWithOptions(/** @type {module:ol/geom/Geometry} */ (geometry), true, context); - } - pushSerializeAndPop(/** @type {module:ol/xml~NodeStackItem} */ - (item), this.GEOMETRY_SERIALIZERS_, - this.GEOMETRY_NODE_FACTORY_, [value], - objectStack, undefined, this); -}; - - -/** - * @param {string} namespaceURI XML namespace. - * @returns {Node} coordinates node. - * @private - */ -GML2.prototype.createCoordinatesNode_ = function(namespaceURI) { - const coordinates = createElementNS(namespaceURI, 'coordinates'); - coordinates.setAttribute('decimal', '.'); - coordinates.setAttribute('cs', ','); - coordinates.setAttribute('ts', ' '); - - return coordinates; -}; - - -/** - * @param {Node} node Node. - * @param {module:ol/geom/LineString|module:ol/geom/LinearRing} value Geometry. - * @param {Array.<*>} objectStack Node stack. - * @private - */ -GML2.prototype.writeCoordinates_ = function(node, value, objectStack) { - const context = objectStack[objectStack.length - 1]; - const hasZ = context['hasZ']; - const srsName = context['srsName']; - // only 2d for simple features profile - const points = value.getCoordinates(); - const len = points.length; - const parts = new Array(len); - for (let i = 0; i < len; ++i) { - const point = points[i]; - parts[i] = this.getCoords_(point, srsName, hasZ); - } - writeStringTextNode(node, parts.join(' ')); -}; - - -/** - * @param {Node} node Node. - * @param {module:ol/geom/LineString} line LineString geometry. - * @param {Array.<*>} objectStack Node stack. - * @private - */ -GML2.prototype.writeCurveSegments_ = function(node, line, objectStack) { - const child = createElementNS(node.namespaceURI, 'LineStringSegment'); - node.appendChild(child); - this.writeCurveOrLineString_(child, line, objectStack); -}; - - -/** - * @param {Node} node Node. - * @param {module:ol/geom/Polygon} geometry Polygon geometry. - * @param {Array.<*>} objectStack Node stack. - * @private - */ -GML2.prototype.writeSurfaceOrPolygon_ = function(node, geometry, objectStack) { - const context = objectStack[objectStack.length - 1]; - const hasZ = context['hasZ']; - const srsName = context['srsName']; - if (node.nodeName !== 'PolygonPatch' && srsName) { - node.setAttribute('srsName', srsName); - } - if (node.nodeName === 'Polygon' || node.nodeName === 'PolygonPatch') { - const rings = geometry.getLinearRings(); - pushSerializeAndPop( - {node: node, hasZ: hasZ, srsName: srsName}, - this.RING_SERIALIZERS_, - this.RING_NODE_FACTORY_, - rings, objectStack, undefined, this); - } else if (node.nodeName === 'Surface') { - const patches = createElementNS(node.namespaceURI, 'patches'); - node.appendChild(patches); - this.writeSurfacePatches_( - patches, geometry, objectStack); - } -}; - - -/** - * @param {*} value Value. - * @param {Array.<*>} objectStack Object stack. - * @param {string=} opt_nodeName Node name. - * @return {Node} Node. - * @private - */ -GML2.prototype.RING_NODE_FACTORY_ = function(value, objectStack, opt_nodeName) { - const context = objectStack[objectStack.length - 1]; - const parentNode = context.node; - const exteriorWritten = context['exteriorWritten']; - if (exteriorWritten === undefined) { - context['exteriorWritten'] = true; - } - return createElementNS(parentNode.namespaceURI, - exteriorWritten !== undefined ? 'innerBoundaryIs' : 'outerBoundaryIs'); -}; - - -/** - * @param {Node} node Node. - * @param {module:ol/geom/Polygon} polygon Polygon geometry. - * @param {Array.<*>} objectStack Node stack. - * @private - */ -GML2.prototype.writeSurfacePatches_ = function(node, polygon, objectStack) { - const child = createElementNS(node.namespaceURI, 'PolygonPatch'); - node.appendChild(child); - this.writeSurfaceOrPolygon_(child, polygon, objectStack); -}; - - -/** - * @param {Node} node Node. - * @param {module:ol/geom/LinearRing} ring LinearRing geometry. - * @param {Array.<*>} objectStack Node stack. - * @private - */ -GML2.prototype.writeRing_ = function(node, ring, objectStack) { - const linearRing = createElementNS(node.namespaceURI, 'LinearRing'); - node.appendChild(linearRing); - this.writeLinearRing_(linearRing, ring, objectStack); -}; - - -/** - * @param {Array.} point Point geometry. - * @param {string=} opt_srsName Optional srsName - * @param {boolean=} opt_hasZ whether the geometry has a Z coordinate (is 3D) or not. - * @return {string} The coords string. - * @private - */ -GML2.prototype.getCoords_ = function(point, opt_srsName, opt_hasZ) { - let axisOrientation = 'enu'; - if (opt_srsName) { - axisOrientation = getProjection(opt_srsName).getAxisOrientation(); - } - let coords = ((axisOrientation.substr(0, 2) === 'en') ? - point[0] + ',' + point[1] : - point[1] + ',' + point[0]); - if (opt_hasZ) { - // For newly created points, Z can be undefined. - const z = point[2] || 0; - coords += ',' + z; } - return coords; -}; - - -/** - * @param {Node} node Node. - * @param {module:ol/geom/Point} geometry Point geometry. - * @param {Array.<*>} objectStack Node stack. - * @private - */ -GML2.prototype.writePoint_ = function(node, geometry, objectStack) { - const context = objectStack[objectStack.length - 1]; - const hasZ = context['hasZ']; - const srsName = context['srsName']; - if (srsName) { - node.setAttribute('srsName', srsName); + /** + * @param {*} value Value. + * @param {Array.<*>} objectStack Object stack. + * @param {string=} opt_nodeName Node name. + * @return {Node} Node. + * @private + */ + RING_NODE_FACTORY_(value, objectStack, opt_nodeName) { + const context = objectStack[objectStack.length - 1]; + const parentNode = context.node; + const exteriorWritten = context['exteriorWritten']; + if (exteriorWritten === undefined) { + context['exteriorWritten'] = true; + } + return createElementNS(parentNode.namespaceURI, + exteriorWritten !== undefined ? 'innerBoundaryIs' : 'outerBoundaryIs'); } - const coordinates = this.createCoordinatesNode_(node.namespaceURI); - node.appendChild(coordinates); - const point = geometry.getCoordinates(); - const coord = this.getCoords_(point, srsName, hasZ); - writeStringTextNode(coordinates, coord); -}; - -/** - * @param {Node} node Node. - * @param {module:ol/geom/MultiPoint} geometry MultiPoint geometry. - * @param {Array.<*>} objectStack Node stack. - * @private - */ -GML2.prototype.writeMultiPoint_ = function(node, geometry, objectStack) { - const context = objectStack[objectStack.length - 1]; - const hasZ = context['hasZ']; - const srsName = context['srsName']; - if (srsName) { - node.setAttribute('srsName', srsName); - } - const points = geometry.getPoints(); - pushSerializeAndPop({node: node, hasZ: hasZ, srsName: srsName}, - this.POINTMEMBER_SERIALIZERS_, - makeSimpleNodeFactory('pointMember'), points, - objectStack, undefined, this); -}; - - -/** - * @param {Node} node Node. - * @param {module:ol/geom/Point} point Point geometry. - * @param {Array.<*>} objectStack Node stack. - * @private - */ -GML2.prototype.writePointMember_ = function(node, point, objectStack) { - const child = createElementNS(node.namespaceURI, 'Point'); - node.appendChild(child); - this.writePoint_(child, point, objectStack); -}; - - -/** - * @param {Node} node Node. - * @param {module:ol/geom/LinearRing} geometry LinearRing geometry. - * @param {Array.<*>} objectStack Node stack. - * @private - */ -GML2.prototype.writeLinearRing_ = function(node, geometry, objectStack) { - const context = objectStack[objectStack.length - 1]; - const srsName = context['srsName']; - if (srsName) { - node.setAttribute('srsName', srsName); - } - const coordinates = this.createCoordinatesNode_(node.namespaceURI); - node.appendChild(coordinates); - this.writeCoordinates_(coordinates, geometry, objectStack); -}; - - -/** - * @param {Node} node Node. - * @param {module:ol/geom/MultiPolygon} geometry MultiPolygon geometry. - * @param {Array.<*>} objectStack Node stack. - * @private - */ -GML2.prototype.writeMultiSurfaceOrPolygon_ = function(node, geometry, objectStack) { - const context = objectStack[objectStack.length - 1]; - const hasZ = context['hasZ']; - const srsName = context['srsName']; - const surface = context['surface']; - if (srsName) { - node.setAttribute('srsName', srsName); - } - const polygons = geometry.getPolygons(); - pushSerializeAndPop({node: node, hasZ: hasZ, srsName: srsName, surface: surface}, - this.SURFACEORPOLYGONMEMBER_SERIALIZERS_, - this.MULTIGEOMETRY_MEMBER_NODE_FACTORY_, polygons, - objectStack, undefined, this); -}; - - -/** - * @param {Node} node Node. - * @param {module:ol/geom/Polygon} polygon Polygon geometry. - * @param {Array.<*>} objectStack Node stack. - * @private - */ -GML2.prototype.writeSurfaceOrPolygonMember_ = function(node, polygon, objectStack) { - const child = this.GEOMETRY_NODE_FACTORY_( - polygon, objectStack); - if (child) { + /** + * @param {Node} node Node. + * @param {module:ol/geom/Polygon} polygon Polygon geometry. + * @param {Array.<*>} objectStack Node stack. + * @private + */ + writeSurfacePatches_(node, polygon, objectStack) { + const child = createElementNS(node.namespaceURI, 'PolygonPatch'); node.appendChild(child); this.writeSurfaceOrPolygon_(child, polygon, objectStack); } -}; - -/** - * @param {Node} node Node. - * @param {module:ol/extent~Extent} extent Extent. - * @param {Array.<*>} objectStack Node stack. - * @private - */ -GML2.prototype.writeEnvelope = function(node, extent, objectStack) { - const context = objectStack[objectStack.length - 1]; - const srsName = context['srsName']; - if (srsName) { - node.setAttribute('srsName', srsName); + /** + * @param {Node} node Node. + * @param {module:ol/geom/LinearRing} ring LinearRing geometry. + * @param {Array.<*>} objectStack Node stack. + * @private + */ + writeRing_(node, ring, objectStack) { + const linearRing = createElementNS(node.namespaceURI, 'LinearRing'); + node.appendChild(linearRing); + this.writeLinearRing_(linearRing, ring, objectStack); } - const keys = ['lowerCorner', 'upperCorner']; - const values = [extent[0] + ' ' + extent[1], extent[2] + ' ' + extent[3]]; - pushSerializeAndPop(/** @type {module:ol/xml~NodeStackItem} */ - ({node: node}), this.ENVELOPE_SERIALIZERS_, - OBJECT_PROPERTY_NODE_FACTORY, - values, - objectStack, keys, this); -}; + + /** + * @param {Array.} point Point geometry. + * @param {string=} opt_srsName Optional srsName + * @param {boolean=} opt_hasZ whether the geometry has a Z coordinate (is 3D) or not. + * @return {string} The coords string. + * @private + */ + getCoords_(point, opt_srsName, opt_hasZ) { + let axisOrientation = 'enu'; + if (opt_srsName) { + axisOrientation = getProjection(opt_srsName).getAxisOrientation(); + } + let coords = ((axisOrientation.substr(0, 2) === 'en') ? + point[0] + ',' + point[1] : + point[1] + ',' + point[0]); + if (opt_hasZ) { + // For newly created points, Z can be undefined. + const z = point[2] || 0; + coords += ',' + z; + } + + return coords; + } + + /** + * @param {Node} node Node. + * @param {module:ol/geom/Point} geometry Point geometry. + * @param {Array.<*>} objectStack Node stack. + * @private + */ + writePoint_(node, geometry, objectStack) { + const context = objectStack[objectStack.length - 1]; + const hasZ = context['hasZ']; + const srsName = context['srsName']; + if (srsName) { + node.setAttribute('srsName', srsName); + } + const coordinates = this.createCoordinatesNode_(node.namespaceURI); + node.appendChild(coordinates); + const point = geometry.getCoordinates(); + const coord = this.getCoords_(point, srsName, hasZ); + writeStringTextNode(coordinates, coord); + } + + /** + * @param {Node} node Node. + * @param {module:ol/geom/MultiPoint} geometry MultiPoint geometry. + * @param {Array.<*>} objectStack Node stack. + * @private + */ + writeMultiPoint_(node, geometry, objectStack) { + const context = objectStack[objectStack.length - 1]; + const hasZ = context['hasZ']; + const srsName = context['srsName']; + if (srsName) { + node.setAttribute('srsName', srsName); + } + const points = geometry.getPoints(); + pushSerializeAndPop({node: node, hasZ: hasZ, srsName: srsName}, + this.POINTMEMBER_SERIALIZERS_, + makeSimpleNodeFactory('pointMember'), points, + objectStack, undefined, this); + } + + /** + * @param {Node} node Node. + * @param {module:ol/geom/Point} point Point geometry. + * @param {Array.<*>} objectStack Node stack. + * @private + */ + writePointMember_(node, point, objectStack) { + const child = createElementNS(node.namespaceURI, 'Point'); + node.appendChild(child); + this.writePoint_(child, point, objectStack); + } + + /** + * @param {Node} node Node. + * @param {module:ol/geom/LinearRing} geometry LinearRing geometry. + * @param {Array.<*>} objectStack Node stack. + * @private + */ + writeLinearRing_(node, geometry, objectStack) { + const context = objectStack[objectStack.length - 1]; + const srsName = context['srsName']; + if (srsName) { + node.setAttribute('srsName', srsName); + } + const coordinates = this.createCoordinatesNode_(node.namespaceURI); + node.appendChild(coordinates); + this.writeCoordinates_(coordinates, geometry, objectStack); + } + + /** + * @param {Node} node Node. + * @param {module:ol/geom/MultiPolygon} geometry MultiPolygon geometry. + * @param {Array.<*>} objectStack Node stack. + * @private + */ + writeMultiSurfaceOrPolygon_(node, geometry, objectStack) { + const context = objectStack[objectStack.length - 1]; + const hasZ = context['hasZ']; + const srsName = context['srsName']; + const surface = context['surface']; + if (srsName) { + node.setAttribute('srsName', srsName); + } + const polygons = geometry.getPolygons(); + pushSerializeAndPop({node: node, hasZ: hasZ, srsName: srsName, surface: surface}, + this.SURFACEORPOLYGONMEMBER_SERIALIZERS_, + this.MULTIGEOMETRY_MEMBER_NODE_FACTORY_, polygons, + objectStack, undefined, this); + } + + /** + * @param {Node} node Node. + * @param {module:ol/geom/Polygon} polygon Polygon geometry. + * @param {Array.<*>} objectStack Node stack. + * @private + */ + writeSurfaceOrPolygonMember_(node, polygon, objectStack) { + const child = this.GEOMETRY_NODE_FACTORY_( + polygon, objectStack); + if (child) { + node.appendChild(child); + this.writeSurfaceOrPolygon_(child, polygon, objectStack); + } + } + + /** + * @param {Node} node Node. + * @param {module:ol/extent~Extent} extent Extent. + * @param {Array.<*>} objectStack Node stack. + * @private + */ + writeEnvelope(node, extent, objectStack) { + const context = objectStack[objectStack.length - 1]; + const srsName = context['srsName']; + if (srsName) { + node.setAttribute('srsName', srsName); + } + const keys = ['lowerCorner', 'upperCorner']; + const values = [extent[0] + ' ' + extent[1], extent[2] + ' ' + extent[3]]; + pushSerializeAndPop(/** @type {module:ol/xml~NodeStackItem} */ + ({node: node}), this.ENVELOPE_SERIALIZERS_, + OBJECT_PROPERTY_NODE_FACTORY, + values, + objectStack, keys, this); + } + + /** + * @const + * @param {*} value Value. + * @param {Array.<*>} objectStack Object stack. + * @param {string=} opt_nodeName Node name. + * @return {Node|undefined} Node. + * @private + */ + MULTIGEOMETRY_MEMBER_NODE_FACTORY_(value, objectStack, opt_nodeName) { + const parentNode = objectStack[objectStack.length - 1].node; + return createElementNS('http://www.opengis.net/gml', + MULTIGEOMETRY_TO_MEMBER_NODENAME[parentNode.nodeName]); + } +} + +inherits(GML2, GMLBase); /** @@ -597,21 +588,6 @@ const MULTIGEOMETRY_TO_MEMBER_NODENAME = { }; -/** - * @const - * @param {*} value Value. - * @param {Array.<*>} objectStack Object stack. - * @param {string=} opt_nodeName Node name. - * @return {Node|undefined} Node. - * @private - */ -GML2.prototype.MULTIGEOMETRY_MEMBER_NODE_FACTORY_ = function(value, objectStack, opt_nodeName) { - const parentNode = objectStack[objectStack.length - 1].node; - return createElementNS('http://www.opengis.net/gml', - MULTIGEOMETRY_TO_MEMBER_NODENAME[parentNode.nodeName]); -}; - - /** * @const * @type {Object.>} diff --git a/src/ol/format/GML3.js b/src/ol/format/GML3.js index 78887c527e..34de9e9d86 100644 --- a/src/ol/format/GML3.js +++ b/src/ol/format/GML3.js @@ -42,348 +42,874 @@ const schemaLocation = GMLNS + * @extends {module:ol/format/GMLBase} * @api */ -const GML3 = function(opt_options) { - const options = /** @type {module:ol/format/GMLBase~Options} */ - (opt_options ? opt_options : {}); +class GML3 { + constructor(opt_options) { + const options = /** @type {module:ol/format/GMLBase~Options} */ + (opt_options ? opt_options : {}); - GMLBase.call(this, options); + GMLBase.call(this, options); + + /** + * @private + * @type {boolean} + */ + this.surface_ = options.surface !== undefined ? options.surface : false; + + /** + * @private + * @type {boolean} + */ + this.curve_ = options.curve !== undefined ? options.curve : false; + + /** + * @private + * @type {boolean} + */ + this.multiCurve_ = options.multiCurve !== undefined ? + options.multiCurve : true; + + /** + * @private + * @type {boolean} + */ + this.multiSurface_ = options.multiSurface !== undefined ? + options.multiSurface : true; + + /** + * @inheritDoc + */ + this.schemaLocation = options.schemaLocation ? + options.schemaLocation : schemaLocation; + + /** + * @private + * @type {boolean} + */ + this.hasZ = options.hasZ !== undefined ? + options.hasZ : false; + + } /** + * @param {Node} node Node. + * @param {Array.<*>} objectStack Object stack. * @private - * @type {boolean} + * @return {module:ol/geom/MultiLineString|undefined} MultiLineString. */ - this.surface_ = options.surface !== undefined ? options.surface : false; + readMultiCurve_(node, objectStack) { + /** @type {Array.} */ + const lineStrings = pushParseAndPop([], + this.MULTICURVE_PARSERS_, node, objectStack, this); + if (lineStrings) { + const multiLineString = new MultiLineString(lineStrings); + return multiLineString; + } else { + return undefined; + } + } /** + * @param {Node} node Node. + * @param {Array.<*>} objectStack Object stack. * @private - * @type {boolean} + * @return {module:ol/geom/MultiPolygon|undefined} MultiPolygon. */ - this.curve_ = options.curve !== undefined ? options.curve : false; + readMultiSurface_(node, objectStack) { + /** @type {Array.} */ + const polygons = pushParseAndPop([], + this.MULTISURFACE_PARSERS_, node, objectStack, this); + if (polygons) { + return new MultiPolygon(polygons); + } + } /** + * @param {Node} node Node. + * @param {Array.<*>} objectStack Object stack. * @private - * @type {boolean} */ - this.multiCurve_ = options.multiCurve !== undefined ? - options.multiCurve : true; + curveMemberParser_(node, objectStack) { + parseNode(this.CURVEMEMBER_PARSERS_, node, objectStack, this); + } /** + * @param {Node} node Node. + * @param {Array.<*>} objectStack Object stack. * @private - * @type {boolean} */ - this.multiSurface_ = options.multiSurface !== undefined ? - options.multiSurface : true; - - /** - * @inheritDoc - */ - this.schemaLocation = options.schemaLocation ? - options.schemaLocation : schemaLocation; + surfaceMemberParser_(node, objectStack) { + parseNode(this.SURFACEMEMBER_PARSERS_, + node, objectStack, this); + } /** + * @param {Node} node Node. + * @param {Array.<*>} objectStack Object stack. * @private - * @type {boolean} + * @return {Array.<(Array.)>|undefined} flat coordinates. */ - this.hasZ = options.hasZ !== undefined ? - options.hasZ : false; + readPatch_(node, objectStack) { + return pushParseAndPop([null], + this.PATCHES_PARSERS_, node, objectStack, this); + } -}; + /** + * @param {Node} node Node. + * @param {Array.<*>} objectStack Object stack. + * @private + * @return {Array.|undefined} flat coordinates. + */ + readSegment_(node, objectStack) { + return pushParseAndPop([null], + this.SEGMENTS_PARSERS_, node, objectStack, this); + } + + /** + * @param {Node} node Node. + * @param {Array.<*>} objectStack Object stack. + * @private + * @return {Array.<(Array.)>|undefined} flat coordinates. + */ + readPolygonPatch_(node, objectStack) { + return pushParseAndPop([null], + this.FLAT_LINEAR_RINGS_PARSERS_, node, objectStack, this); + } + + /** + * @param {Node} node Node. + * @param {Array.<*>} objectStack Object stack. + * @private + * @return {Array.|undefined} flat coordinates. + */ + readLineStringSegment_(node, objectStack) { + return pushParseAndPop([null], + this.GEOMETRY_FLAT_COORDINATES_PARSERS_, + node, objectStack, this); + } + + /** + * @param {Node} node Node. + * @param {Array.<*>} objectStack Object stack. + * @private + */ + interiorParser_(node, objectStack) { + /** @type {Array.|undefined} */ + const flatLinearRing = pushParseAndPop(undefined, + this.RING_PARSERS, node, objectStack, this); + if (flatLinearRing) { + const flatLinearRings = /** @type {Array.>} */ + (objectStack[objectStack.length - 1]); + flatLinearRings.push(flatLinearRing); + } + } + + /** + * @param {Node} node Node. + * @param {Array.<*>} objectStack Object stack. + * @private + */ + exteriorParser_(node, objectStack) { + /** @type {Array.|undefined} */ + const flatLinearRing = pushParseAndPop(undefined, + this.RING_PARSERS, node, objectStack, this); + if (flatLinearRing) { + const flatLinearRings = /** @type {Array.>} */ + (objectStack[objectStack.length - 1]); + flatLinearRings[0] = flatLinearRing; + } + } + + /** + * @param {Node} node Node. + * @param {Array.<*>} objectStack Object stack. + * @private + * @return {module:ol/geom/Polygon|undefined} Polygon. + */ + readSurface_(node, objectStack) { + /** @type {Array.>} */ + const flatLinearRings = pushParseAndPop([null], + this.SURFACE_PARSERS_, node, objectStack, this); + if (flatLinearRings && flatLinearRings[0]) { + const flatCoordinates = flatLinearRings[0]; + const ends = [flatCoordinates.length]; + let i, ii; + for (i = 1, ii = flatLinearRings.length; i < ii; ++i) { + extend(flatCoordinates, flatLinearRings[i]); + ends.push(flatCoordinates.length); + } + return new Polygon(flatCoordinates, GeometryLayout.XYZ, ends); + } else { + return undefined; + } + } + + /** + * @param {Node} node Node. + * @param {Array.<*>} objectStack Object stack. + * @private + * @return {module:ol/geom/LineString|undefined} LineString. + */ + readCurve_(node, objectStack) { + /** @type {Array.} */ + const flatCoordinates = pushParseAndPop([null], + this.CURVE_PARSERS_, node, objectStack, this); + if (flatCoordinates) { + const lineString = new LineString(flatCoordinates, GeometryLayout.XYZ); + return lineString; + } else { + return undefined; + } + } + + /** + * @param {Node} node Node. + * @param {Array.<*>} objectStack Object stack. + * @private + * @return {module:ol/extent~Extent|undefined} Envelope. + */ + readEnvelope_(node, objectStack) { + /** @type {Array.} */ + const flatCoordinates = pushParseAndPop([null], + this.ENVELOPE_PARSERS_, node, objectStack, this); + return createOrUpdate(flatCoordinates[1][0], + flatCoordinates[1][1], flatCoordinates[2][0], + flatCoordinates[2][1]); + } + + /** + * @param {Node} node Node. + * @param {Array.<*>} objectStack Object stack. + * @private + * @return {Array.|undefined} Flat coordinates. + */ + readFlatPos_(node, objectStack) { + let s = getAllTextContent(node, false); + const re = /^\s*([+\-]?\d*\.?\d+(?:[eE][+\-]?\d+)?)\s*/; + /** @type {Array.} */ + const flatCoordinates = []; + let m; + while ((m = re.exec(s))) { + flatCoordinates.push(parseFloat(m[1])); + s = s.substr(m[0].length); + } + if (s !== '') { + return undefined; + } + const context = objectStack[0]; + const containerSrs = context['srsName']; + let axisOrientation = 'enu'; + if (containerSrs) { + const proj = getProjection(containerSrs); + axisOrientation = proj.getAxisOrientation(); + } + if (axisOrientation === 'neu') { + let i, ii; + for (i = 0, ii = flatCoordinates.length; i < ii; i += 3) { + const y = flatCoordinates[i]; + const x = flatCoordinates[i + 1]; + flatCoordinates[i] = x; + flatCoordinates[i + 1] = y; + } + } + const len = flatCoordinates.length; + if (len == 2) { + flatCoordinates.push(0); + } + if (len === 0) { + return undefined; + } + return flatCoordinates; + } + + /** + * @param {Node} node Node. + * @param {Array.<*>} objectStack Object stack. + * @private + * @return {Array.|undefined} Flat coordinates. + */ + readFlatPosList_(node, objectStack) { + const s = getAllTextContent(node, false).replace(/^\s*|\s*$/g, ''); + const context = objectStack[0]; + const containerSrs = context['srsName']; + const contextDimension = context['srsDimension']; + let axisOrientation = 'enu'; + if (containerSrs) { + const proj = getProjection(containerSrs); + axisOrientation = proj.getAxisOrientation(); + } + const coords = s.split(/\s+/); + // The "dimension" attribute is from the GML 3.0.1 spec. + let dim = 2; + if (node.getAttribute('srsDimension')) { + dim = readNonNegativeIntegerString( + node.getAttribute('srsDimension')); + } else if (node.getAttribute('dimension')) { + dim = readNonNegativeIntegerString( + node.getAttribute('dimension')); + } else if (node.parentNode.getAttribute('srsDimension')) { + dim = readNonNegativeIntegerString( + node.parentNode.getAttribute('srsDimension')); + } else if (contextDimension) { + dim = readNonNegativeIntegerString(contextDimension); + } + let x, y, z; + const flatCoordinates = []; + for (let i = 0, ii = coords.length; i < ii; i += dim) { + x = parseFloat(coords[i]); + y = parseFloat(coords[i + 1]); + z = (dim === 3) ? parseFloat(coords[i + 2]) : 0; + if (axisOrientation.substr(0, 2) === 'en') { + flatCoordinates.push(x, y, z); + } else { + flatCoordinates.push(y, x, z); + } + } + return flatCoordinates; + } + + /** + * @param {Node} node Node. + * @param {module:ol/geom/Point} value Point geometry. + * @param {Array.<*>} objectStack Node stack. + * @private + */ + writePos_(node, value, objectStack) { + const context = objectStack[objectStack.length - 1]; + const hasZ = context['hasZ']; + const srsDimension = hasZ ? 3 : 2; + node.setAttribute('srsDimension', srsDimension); + const srsName = context['srsName']; + let axisOrientation = 'enu'; + if (srsName) { + axisOrientation = getProjection(srsName).getAxisOrientation(); + } + const point = value.getCoordinates(); + let coords; + // only 2d for simple features profile + if (axisOrientation.substr(0, 2) === 'en') { + coords = (point[0] + ' ' + point[1]); + } else { + coords = (point[1] + ' ' + point[0]); + } + if (hasZ) { + // For newly created points, Z can be undefined. + const z = point[2] || 0; + coords += ' ' + z; + } + writeStringTextNode(node, coords); + } + + /** + * @param {Array.} point Point geometry. + * @param {string=} opt_srsName Optional srsName + * @param {boolean=} opt_hasZ whether the geometry has a Z coordinate (is 3D) or not. + * @return {string} The coords string. + * @private + */ + getCoords_(point, opt_srsName, opt_hasZ) { + let axisOrientation = 'enu'; + if (opt_srsName) { + axisOrientation = getProjection(opt_srsName).getAxisOrientation(); + } + let coords = ((axisOrientation.substr(0, 2) === 'en') ? + point[0] + ' ' + point[1] : + point[1] + ' ' + point[0]); + if (opt_hasZ) { + // For newly created points, Z can be undefined. + const z = point[2] || 0; + coords += ' ' + z; + } + + return coords; + } + + /** + * @param {Node} node Node. + * @param {module:ol/geom/LineString|module:ol/geom/LinearRing} value Geometry. + * @param {Array.<*>} objectStack Node stack. + * @private + */ + writePosList_(node, value, objectStack) { + const context = objectStack[objectStack.length - 1]; + const hasZ = context['hasZ']; + const srsDimension = hasZ ? 3 : 2; + node.setAttribute('srsDimension', srsDimension); + const srsName = context['srsName']; + // only 2d for simple features profile + const points = value.getCoordinates(); + const len = points.length; + const parts = new Array(len); + let point; + for (let i = 0; i < len; ++i) { + point = points[i]; + parts[i] = this.getCoords_(point, srsName, hasZ); + } + writeStringTextNode(node, parts.join(' ')); + } + + /** + * @param {Node} node Node. + * @param {module:ol/geom/Point} geometry Point geometry. + * @param {Array.<*>} objectStack Node stack. + * @private + */ + writePoint_(node, geometry, objectStack) { + const context = objectStack[objectStack.length - 1]; + const srsName = context['srsName']; + if (srsName) { + node.setAttribute('srsName', srsName); + } + const pos = createElementNS(node.namespaceURI, 'pos'); + node.appendChild(pos); + this.writePos_(pos, geometry, objectStack); + } + + /** + * @param {Node} node Node. + * @param {module:ol/extent~Extent} extent Extent. + * @param {Array.<*>} objectStack Node stack. + */ + writeEnvelope(node, extent, objectStack) { + const context = objectStack[objectStack.length - 1]; + const srsName = context['srsName']; + if (srsName) { + node.setAttribute('srsName', srsName); + } + const keys = ['lowerCorner', 'upperCorner']; + const values = [extent[0] + ' ' + extent[1], extent[2] + ' ' + extent[3]]; + pushSerializeAndPop(/** @type {module:ol/xml~NodeStackItem} */ + ({node: node}), this.ENVELOPE_SERIALIZERS_, + OBJECT_PROPERTY_NODE_FACTORY, + values, + objectStack, keys, this); + } + + /** + * @param {Node} node Node. + * @param {module:ol/geom/LinearRing} geometry LinearRing geometry. + * @param {Array.<*>} objectStack Node stack. + * @private + */ + writeLinearRing_(node, geometry, objectStack) { + const context = objectStack[objectStack.length - 1]; + const srsName = context['srsName']; + if (srsName) { + node.setAttribute('srsName', srsName); + } + const posList = createElementNS(node.namespaceURI, 'posList'); + node.appendChild(posList); + this.writePosList_(posList, geometry, objectStack); + } + + /** + * @param {*} value Value. + * @param {Array.<*>} objectStack Object stack. + * @param {string=} opt_nodeName Node name. + * @return {Node} Node. + * @private + */ + RING_NODE_FACTORY_(value, objectStack, opt_nodeName) { + const context = objectStack[objectStack.length - 1]; + const parentNode = context.node; + const exteriorWritten = context['exteriorWritten']; + if (exteriorWritten === undefined) { + context['exteriorWritten'] = true; + } + return createElementNS(parentNode.namespaceURI, + exteriorWritten !== undefined ? 'interior' : 'exterior'); + } + + /** + * @param {Node} node Node. + * @param {module:ol/geom/Polygon} geometry Polygon geometry. + * @param {Array.<*>} objectStack Node stack. + * @private + */ + writeSurfaceOrPolygon_(node, geometry, objectStack) { + const context = objectStack[objectStack.length - 1]; + const hasZ = context['hasZ']; + const srsName = context['srsName']; + if (node.nodeName !== 'PolygonPatch' && srsName) { + node.setAttribute('srsName', srsName); + } + if (node.nodeName === 'Polygon' || node.nodeName === 'PolygonPatch') { + const rings = geometry.getLinearRings(); + pushSerializeAndPop( + {node: node, hasZ: hasZ, srsName: srsName}, + this.RING_SERIALIZERS_, + this.RING_NODE_FACTORY_, + rings, objectStack, undefined, this); + } else if (node.nodeName === 'Surface') { + const patches = createElementNS(node.namespaceURI, 'patches'); + node.appendChild(patches); + this.writeSurfacePatches_( + patches, geometry, objectStack); + } + } + + /** + * @param {Node} node Node. + * @param {module:ol/geom/LineString} geometry LineString geometry. + * @param {Array.<*>} objectStack Node stack. + * @private + */ + writeCurveOrLineString_(node, geometry, objectStack) { + const context = objectStack[objectStack.length - 1]; + const srsName = context['srsName']; + if (node.nodeName !== 'LineStringSegment' && srsName) { + node.setAttribute('srsName', srsName); + } + if (node.nodeName === 'LineString' || + node.nodeName === 'LineStringSegment') { + const posList = createElementNS(node.namespaceURI, 'posList'); + node.appendChild(posList); + this.writePosList_(posList, geometry, objectStack); + } else if (node.nodeName === 'Curve') { + const segments = createElementNS(node.namespaceURI, 'segments'); + node.appendChild(segments); + this.writeCurveSegments_(segments, + geometry, objectStack); + } + } + + /** + * @param {Node} node Node. + * @param {module:ol/geom/MultiPolygon} geometry MultiPolygon geometry. + * @param {Array.<*>} objectStack Node stack. + * @private + */ + writeMultiSurfaceOrPolygon_(node, geometry, objectStack) { + const context = objectStack[objectStack.length - 1]; + const hasZ = context['hasZ']; + const srsName = context['srsName']; + const surface = context['surface']; + if (srsName) { + node.setAttribute('srsName', srsName); + } + const polygons = geometry.getPolygons(); + pushSerializeAndPop({node: node, hasZ: hasZ, srsName: srsName, surface: surface}, + this.SURFACEORPOLYGONMEMBER_SERIALIZERS_, + this.MULTIGEOMETRY_MEMBER_NODE_FACTORY_, polygons, + objectStack, undefined, this); + } + + /** + * @param {Node} node Node. + * @param {module:ol/geom/MultiPoint} geometry MultiPoint geometry. + * @param {Array.<*>} objectStack Node stack. + * @private + */ + writeMultiPoint_(node, geometry, objectStack) { + const context = objectStack[objectStack.length - 1]; + const srsName = context['srsName']; + const hasZ = context['hasZ']; + if (srsName) { + node.setAttribute('srsName', srsName); + } + const points = geometry.getPoints(); + pushSerializeAndPop({node: node, hasZ: hasZ, srsName: srsName}, + this.POINTMEMBER_SERIALIZERS_, + makeSimpleNodeFactory('pointMember'), points, + objectStack, undefined, this); + } + + /** + * @param {Node} node Node. + * @param {module:ol/geom/MultiLineString} geometry MultiLineString geometry. + * @param {Array.<*>} objectStack Node stack. + * @private + */ + writeMultiCurveOrLineString_(node, geometry, objectStack) { + const context = objectStack[objectStack.length - 1]; + const hasZ = context['hasZ']; + const srsName = context['srsName']; + const curve = context['curve']; + if (srsName) { + node.setAttribute('srsName', srsName); + } + const lines = geometry.getLineStrings(); + pushSerializeAndPop({node: node, hasZ: hasZ, srsName: srsName, curve: curve}, + this.LINESTRINGORCURVEMEMBER_SERIALIZERS_, + this.MULTIGEOMETRY_MEMBER_NODE_FACTORY_, lines, + objectStack, undefined, this); + } + + /** + * @param {Node} node Node. + * @param {module:ol/geom/LinearRing} ring LinearRing geometry. + * @param {Array.<*>} objectStack Node stack. + * @private + */ + writeRing_(node, ring, objectStack) { + const linearRing = createElementNS(node.namespaceURI, 'LinearRing'); + node.appendChild(linearRing); + this.writeLinearRing_(linearRing, ring, objectStack); + } + + /** + * @param {Node} node Node. + * @param {module:ol/geom/Polygon} polygon Polygon geometry. + * @param {Array.<*>} objectStack Node stack. + * @private + */ + writeSurfaceOrPolygonMember_(node, polygon, objectStack) { + const child = this.GEOMETRY_NODE_FACTORY_( + polygon, objectStack); + if (child) { + node.appendChild(child); + this.writeSurfaceOrPolygon_(child, polygon, objectStack); + } + } + + /** + * @param {Node} node Node. + * @param {module:ol/geom/Point} point Point geometry. + * @param {Array.<*>} objectStack Node stack. + * @private + */ + writePointMember_(node, point, objectStack) { + const child = createElementNS(node.namespaceURI, 'Point'); + node.appendChild(child); + this.writePoint_(child, point, objectStack); + } + + /** + * @param {Node} node Node. + * @param {module:ol/geom/LineString} line LineString geometry. + * @param {Array.<*>} objectStack Node stack. + * @private + */ + writeLineStringOrCurveMember_(node, line, objectStack) { + const child = this.GEOMETRY_NODE_FACTORY_(line, objectStack); + if (child) { + node.appendChild(child); + this.writeCurveOrLineString_(child, line, objectStack); + } + } + + /** + * @param {Node} node Node. + * @param {module:ol/geom/Polygon} polygon Polygon geometry. + * @param {Array.<*>} objectStack Node stack. + * @private + */ + writeSurfacePatches_(node, polygon, objectStack) { + const child = createElementNS(node.namespaceURI, 'PolygonPatch'); + node.appendChild(child); + this.writeSurfaceOrPolygon_(child, polygon, objectStack); + } + + /** + * @param {Node} node Node. + * @param {module:ol/geom/LineString} line LineString geometry. + * @param {Array.<*>} objectStack Node stack. + * @private + */ + writeCurveSegments_(node, line, objectStack) { + const child = createElementNS(node.namespaceURI, + 'LineStringSegment'); + node.appendChild(child); + this.writeCurveOrLineString_(child, line, objectStack); + } + + /** + * @param {Node} node Node. + * @param {module:ol/geom/Geometry|module:ol/extent~Extent} geometry Geometry. + * @param {Array.<*>} objectStack Node stack. + */ + writeGeometryElement(node, geometry, objectStack) { + const context = /** @type {module:ol/format/Feature~WriteOptions} */ (objectStack[objectStack.length - 1]); + const item = assign({}, context); + item.node = node; + let value; + if (Array.isArray(geometry)) { + if (context.dataProjection) { + value = transformExtent( + geometry, context.featureProjection, context.dataProjection); + } else { + value = geometry; + } + } else { + value = transformWithOptions(/** @type {module:ol/geom/Geometry} */ (geometry), true, context); + } + pushSerializeAndPop(/** @type {module:ol/xml~NodeStackItem} */ + (item), this.GEOMETRY_SERIALIZERS_, + this.GEOMETRY_NODE_FACTORY_, [value], + objectStack, undefined, this); + } + + /** + * @param {Node} node Node. + * @param {module:ol/Feature} feature Feature. + * @param {Array.<*>} objectStack Node stack. + */ + writeFeatureElement(node, feature, objectStack) { + const fid = feature.getId(); + if (fid) { + node.setAttribute('fid', fid); + } + const context = /** @type {Object} */ (objectStack[objectStack.length - 1]); + const featureNS = context['featureNS']; + const geometryName = feature.getGeometryName(); + if (!context.serializers) { + context.serializers = {}; + context.serializers[featureNS] = {}; + } + const properties = feature.getProperties(); + const keys = []; + const values = []; + for (const key in properties) { + const value = properties[key]; + if (value !== null) { + keys.push(key); + values.push(value); + if (key == geometryName || value instanceof Geometry) { + if (!(key in context.serializers[featureNS])) { + context.serializers[featureNS][key] = makeChildAppender( + this.writeGeometryElement, this); + } + } else { + if (!(key in context.serializers[featureNS])) { + context.serializers[featureNS][key] = makeChildAppender(writeStringTextNode); + } + } + } + } + const item = assign({}, context); + item.node = node; + pushSerializeAndPop(/** @type {module:ol/xml~NodeStackItem} */ + (item), context.serializers, + makeSimpleNodeFactory(undefined, featureNS), + values, + objectStack, keys); + } + + /** + * @param {Node} node Node. + * @param {Array.} features Features. + * @param {Array.<*>} objectStack Node stack. + * @private + */ + writeFeatureMembers_(node, features, objectStack) { + const context = /** @type {Object} */ (objectStack[objectStack.length - 1]); + const featureType = context['featureType']; + const featureNS = context['featureNS']; + const serializers = {}; + serializers[featureNS] = {}; + serializers[featureNS][featureType] = makeChildAppender( + this.writeFeatureElement, this); + const item = assign({}, context); + item.node = node; + pushSerializeAndPop(/** @type {module:ol/xml~NodeStackItem} */ + (item), + serializers, + makeSimpleNodeFactory(featureType, featureNS), features, + objectStack); + } + + /** + * @const + * @param {*} value Value. + * @param {Array.<*>} objectStack Object stack. + * @param {string=} opt_nodeName Node name. + * @return {Node|undefined} Node. + * @private + */ + MULTIGEOMETRY_MEMBER_NODE_FACTORY_(value, objectStack, opt_nodeName) { + const parentNode = objectStack[objectStack.length - 1].node; + return createElementNS('http://www.opengis.net/gml', + MULTIGEOMETRY_TO_MEMBER_NODENAME[parentNode.nodeName]); + } + + /** + * @const + * @param {*} value Value. + * @param {Array.<*>} objectStack Object stack. + * @param {string=} opt_nodeName Node name. + * @return {Node|undefined} Node. + * @private + */ + GEOMETRY_NODE_FACTORY_(value, objectStack, opt_nodeName) { + const context = objectStack[objectStack.length - 1]; + const multiSurface = context['multiSurface']; + const surface = context['surface']; + const curve = context['curve']; + const multiCurve = context['multiCurve']; + let nodeName; + if (!Array.isArray(value)) { + nodeName = /** @type {module:ol/geom/Geometry} */ (value).getType(); + if (nodeName === 'MultiPolygon' && multiSurface === true) { + nodeName = 'MultiSurface'; + } else if (nodeName === 'Polygon' && surface === true) { + nodeName = 'Surface'; + } else if (nodeName === 'LineString' && curve === true) { + nodeName = 'Curve'; + } else if (nodeName === 'MultiLineString' && multiCurve === true) { + nodeName = 'MultiCurve'; + } + } else { + nodeName = 'Envelope'; + } + return createElementNS('http://www.opengis.net/gml', + nodeName); + } + + /** + * Encode a geometry in GML 3.1.1 Simple Features. + * + * @param {module:ol/geom/Geometry} geometry Geometry. + * @param {module:ol/format/Feature~WriteOptions=} opt_options Options. + * @return {Node} Node. + * @override + * @api + */ + writeGeometryNode(geometry, opt_options) { + opt_options = this.adaptOptions(opt_options); + const geom = createElementNS('http://www.opengis.net/gml', 'geom'); + const context = {node: geom, hasZ: this.hasZ, srsName: this.srsName, + curve: this.curve_, surface: this.surface_, + multiSurface: this.multiSurface_, multiCurve: this.multiCurve_}; + if (opt_options) { + assign(context, opt_options); + } + this.writeGeometryElement(geom, geometry, [context]); + return geom; + } + + /** + * Encode an array of features in the GML 3.1.1 format as an XML node. + * + * @param {Array.} features Features. + * @param {module:ol/format/Feature~WriteOptions=} opt_options Options. + * @return {Node} Node. + * @override + * @api + */ + writeFeaturesNode(features, opt_options) { + opt_options = this.adaptOptions(opt_options); + const node = createElementNS('http://www.opengis.net/gml', 'featureMembers'); + node.setAttributeNS(XML_SCHEMA_INSTANCE_URI, 'xsi:schemaLocation', this.schemaLocation); + const context = { + srsName: this.srsName, + hasZ: this.hasZ, + curve: this.curve_, + surface: this.surface_, + multiSurface: this.multiSurface_, + multiCurve: this.multiCurve_, + featureNS: this.featureNS, + featureType: this.featureType + }; + if (opt_options) { + assign(context, opt_options); + } + this.writeFeatureMembers_(node, features, [context]); + return node; + } +} inherits(GML3, GMLBase); -/** - * @param {Node} node Node. - * @param {Array.<*>} objectStack Object stack. - * @private - * @return {module:ol/geom/MultiLineString|undefined} MultiLineString. - */ -GML3.prototype.readMultiCurve_ = function(node, objectStack) { - /** @type {Array.} */ - const lineStrings = pushParseAndPop([], - this.MULTICURVE_PARSERS_, node, objectStack, this); - if (lineStrings) { - const multiLineString = new MultiLineString(lineStrings); - return multiLineString; - } else { - return undefined; - } -}; - - -/** - * @param {Node} node Node. - * @param {Array.<*>} objectStack Object stack. - * @private - * @return {module:ol/geom/MultiPolygon|undefined} MultiPolygon. - */ -GML3.prototype.readMultiSurface_ = function(node, objectStack) { - /** @type {Array.} */ - const polygons = pushParseAndPop([], - this.MULTISURFACE_PARSERS_, node, objectStack, this); - if (polygons) { - return new MultiPolygon(polygons); - } -}; - - -/** - * @param {Node} node Node. - * @param {Array.<*>} objectStack Object stack. - * @private - */ -GML3.prototype.curveMemberParser_ = function(node, objectStack) { - parseNode(this.CURVEMEMBER_PARSERS_, node, objectStack, this); -}; - - -/** - * @param {Node} node Node. - * @param {Array.<*>} objectStack Object stack. - * @private - */ -GML3.prototype.surfaceMemberParser_ = function(node, objectStack) { - parseNode(this.SURFACEMEMBER_PARSERS_, - node, objectStack, this); -}; - - -/** - * @param {Node} node Node. - * @param {Array.<*>} objectStack Object stack. - * @private - * @return {Array.<(Array.)>|undefined} flat coordinates. - */ -GML3.prototype.readPatch_ = function(node, objectStack) { - return pushParseAndPop([null], - this.PATCHES_PARSERS_, node, objectStack, this); -}; - - -/** - * @param {Node} node Node. - * @param {Array.<*>} objectStack Object stack. - * @private - * @return {Array.|undefined} flat coordinates. - */ -GML3.prototype.readSegment_ = function(node, objectStack) { - return pushParseAndPop([null], - this.SEGMENTS_PARSERS_, node, objectStack, this); -}; - - -/** - * @param {Node} node Node. - * @param {Array.<*>} objectStack Object stack. - * @private - * @return {Array.<(Array.)>|undefined} flat coordinates. - */ -GML3.prototype.readPolygonPatch_ = function(node, objectStack) { - return pushParseAndPop([null], - this.FLAT_LINEAR_RINGS_PARSERS_, node, objectStack, this); -}; - - -/** - * @param {Node} node Node. - * @param {Array.<*>} objectStack Object stack. - * @private - * @return {Array.|undefined} flat coordinates. - */ -GML3.prototype.readLineStringSegment_ = function(node, objectStack) { - return pushParseAndPop([null], - this.GEOMETRY_FLAT_COORDINATES_PARSERS_, - node, objectStack, this); -}; - - -/** - * @param {Node} node Node. - * @param {Array.<*>} objectStack Object stack. - * @private - */ -GML3.prototype.interiorParser_ = function(node, objectStack) { - /** @type {Array.|undefined} */ - const flatLinearRing = pushParseAndPop(undefined, - this.RING_PARSERS, node, objectStack, this); - if (flatLinearRing) { - const flatLinearRings = /** @type {Array.>} */ - (objectStack[objectStack.length - 1]); - flatLinearRings.push(flatLinearRing); - } -}; - - -/** - * @param {Node} node Node. - * @param {Array.<*>} objectStack Object stack. - * @private - */ -GML3.prototype.exteriorParser_ = function(node, objectStack) { - /** @type {Array.|undefined} */ - const flatLinearRing = pushParseAndPop(undefined, - this.RING_PARSERS, node, objectStack, this); - if (flatLinearRing) { - const flatLinearRings = /** @type {Array.>} */ - (objectStack[objectStack.length - 1]); - flatLinearRings[0] = flatLinearRing; - } -}; - - -/** - * @param {Node} node Node. - * @param {Array.<*>} objectStack Object stack. - * @private - * @return {module:ol/geom/Polygon|undefined} Polygon. - */ -GML3.prototype.readSurface_ = function(node, objectStack) { - /** @type {Array.>} */ - const flatLinearRings = pushParseAndPop([null], - this.SURFACE_PARSERS_, node, objectStack, this); - if (flatLinearRings && flatLinearRings[0]) { - const flatCoordinates = flatLinearRings[0]; - const ends = [flatCoordinates.length]; - let i, ii; - for (i = 1, ii = flatLinearRings.length; i < ii; ++i) { - extend(flatCoordinates, flatLinearRings[i]); - ends.push(flatCoordinates.length); - } - return new Polygon(flatCoordinates, GeometryLayout.XYZ, ends); - } else { - return undefined; - } -}; - - -/** - * @param {Node} node Node. - * @param {Array.<*>} objectStack Object stack. - * @private - * @return {module:ol/geom/LineString|undefined} LineString. - */ -GML3.prototype.readCurve_ = function(node, objectStack) { - /** @type {Array.} */ - const flatCoordinates = pushParseAndPop([null], - this.CURVE_PARSERS_, node, objectStack, this); - if (flatCoordinates) { - const lineString = new LineString(flatCoordinates, GeometryLayout.XYZ); - return lineString; - } else { - return undefined; - } -}; - - -/** - * @param {Node} node Node. - * @param {Array.<*>} objectStack Object stack. - * @private - * @return {module:ol/extent~Extent|undefined} Envelope. - */ -GML3.prototype.readEnvelope_ = function(node, objectStack) { - /** @type {Array.} */ - const flatCoordinates = pushParseAndPop([null], - this.ENVELOPE_PARSERS_, node, objectStack, this); - return createOrUpdate(flatCoordinates[1][0], - flatCoordinates[1][1], flatCoordinates[2][0], - flatCoordinates[2][1]); -}; - - -/** - * @param {Node} node Node. - * @param {Array.<*>} objectStack Object stack. - * @private - * @return {Array.|undefined} Flat coordinates. - */ -GML3.prototype.readFlatPos_ = function(node, objectStack) { - let s = getAllTextContent(node, false); - const re = /^\s*([+\-]?\d*\.?\d+(?:[eE][+\-]?\d+)?)\s*/; - /** @type {Array.} */ - const flatCoordinates = []; - let m; - while ((m = re.exec(s))) { - flatCoordinates.push(parseFloat(m[1])); - s = s.substr(m[0].length); - } - if (s !== '') { - return undefined; - } - const context = objectStack[0]; - const containerSrs = context['srsName']; - let axisOrientation = 'enu'; - if (containerSrs) { - const proj = getProjection(containerSrs); - axisOrientation = proj.getAxisOrientation(); - } - if (axisOrientation === 'neu') { - let i, ii; - for (i = 0, ii = flatCoordinates.length; i < ii; i += 3) { - const y = flatCoordinates[i]; - const x = flatCoordinates[i + 1]; - flatCoordinates[i] = x; - flatCoordinates[i + 1] = y; - } - } - const len = flatCoordinates.length; - if (len == 2) { - flatCoordinates.push(0); - } - if (len === 0) { - return undefined; - } - return flatCoordinates; -}; - - -/** - * @param {Node} node Node. - * @param {Array.<*>} objectStack Object stack. - * @private - * @return {Array.|undefined} Flat coordinates. - */ -GML3.prototype.readFlatPosList_ = function(node, objectStack) { - const s = getAllTextContent(node, false).replace(/^\s*|\s*$/g, ''); - const context = objectStack[0]; - const containerSrs = context['srsName']; - const contextDimension = context['srsDimension']; - let axisOrientation = 'enu'; - if (containerSrs) { - const proj = getProjection(containerSrs); - axisOrientation = proj.getAxisOrientation(); - } - const coords = s.split(/\s+/); - // The "dimension" attribute is from the GML 3.0.1 spec. - let dim = 2; - if (node.getAttribute('srsDimension')) { - dim = readNonNegativeIntegerString( - node.getAttribute('srsDimension')); - } else if (node.getAttribute('dimension')) { - dim = readNonNegativeIntegerString( - node.getAttribute('dimension')); - } else if (node.parentNode.getAttribute('srsDimension')) { - dim = readNonNegativeIntegerString( - node.parentNode.getAttribute('srsDimension')); - } else if (contextDimension) { - dim = readNonNegativeIntegerString(contextDimension); - } - let x, y, z; - const flatCoordinates = []; - for (let i = 0, ii = coords.length; i < ii; i += dim) { - x = parseFloat(coords[i]); - y = parseFloat(coords[i + 1]); - z = (dim === 3) ? parseFloat(coords[i + 2]) : 0; - if (axisOrientation.substr(0, 2) === 'en') { - flatCoordinates.push(x, y, z); - } else { - flatCoordinates.push(y, x, z); - } - } - return flatCoordinates; -}; - - /** * @const * @type {Object.>} @@ -562,467 +1088,6 @@ GML3.prototype.SEGMENTS_PARSERS_ = { }; -/** - * @param {Node} node Node. - * @param {module:ol/geom/Point} value Point geometry. - * @param {Array.<*>} objectStack Node stack. - * @private - */ -GML3.prototype.writePos_ = function(node, value, objectStack) { - const context = objectStack[objectStack.length - 1]; - const hasZ = context['hasZ']; - const srsDimension = hasZ ? 3 : 2; - node.setAttribute('srsDimension', srsDimension); - const srsName = context['srsName']; - let axisOrientation = 'enu'; - if (srsName) { - axisOrientation = getProjection(srsName).getAxisOrientation(); - } - const point = value.getCoordinates(); - let coords; - // only 2d for simple features profile - if (axisOrientation.substr(0, 2) === 'en') { - coords = (point[0] + ' ' + point[1]); - } else { - coords = (point[1] + ' ' + point[0]); - } - if (hasZ) { - // For newly created points, Z can be undefined. - const z = point[2] || 0; - coords += ' ' + z; - } - writeStringTextNode(node, coords); -}; - - -/** - * @param {Array.} point Point geometry. - * @param {string=} opt_srsName Optional srsName - * @param {boolean=} opt_hasZ whether the geometry has a Z coordinate (is 3D) or not. - * @return {string} The coords string. - * @private - */ -GML3.prototype.getCoords_ = function(point, opt_srsName, opt_hasZ) { - let axisOrientation = 'enu'; - if (opt_srsName) { - axisOrientation = getProjection(opt_srsName).getAxisOrientation(); - } - let coords = ((axisOrientation.substr(0, 2) === 'en') ? - point[0] + ' ' + point[1] : - point[1] + ' ' + point[0]); - if (opt_hasZ) { - // For newly created points, Z can be undefined. - const z = point[2] || 0; - coords += ' ' + z; - } - - return coords; -}; - - -/** - * @param {Node} node Node. - * @param {module:ol/geom/LineString|module:ol/geom/LinearRing} value Geometry. - * @param {Array.<*>} objectStack Node stack. - * @private - */ -GML3.prototype.writePosList_ = function(node, value, objectStack) { - const context = objectStack[objectStack.length - 1]; - const hasZ = context['hasZ']; - const srsDimension = hasZ ? 3 : 2; - node.setAttribute('srsDimension', srsDimension); - const srsName = context['srsName']; - // only 2d for simple features profile - const points = value.getCoordinates(); - const len = points.length; - const parts = new Array(len); - let point; - for (let i = 0; i < len; ++i) { - point = points[i]; - parts[i] = this.getCoords_(point, srsName, hasZ); - } - writeStringTextNode(node, parts.join(' ')); -}; - - -/** - * @param {Node} node Node. - * @param {module:ol/geom/Point} geometry Point geometry. - * @param {Array.<*>} objectStack Node stack. - * @private - */ -GML3.prototype.writePoint_ = function(node, geometry, objectStack) { - const context = objectStack[objectStack.length - 1]; - const srsName = context['srsName']; - if (srsName) { - node.setAttribute('srsName', srsName); - } - const pos = createElementNS(node.namespaceURI, 'pos'); - node.appendChild(pos); - this.writePos_(pos, geometry, objectStack); -}; - - -/** - * @param {Node} node Node. - * @param {module:ol/extent~Extent} extent Extent. - * @param {Array.<*>} objectStack Node stack. - */ -GML3.prototype.writeEnvelope = function(node, extent, objectStack) { - const context = objectStack[objectStack.length - 1]; - const srsName = context['srsName']; - if (srsName) { - node.setAttribute('srsName', srsName); - } - const keys = ['lowerCorner', 'upperCorner']; - const values = [extent[0] + ' ' + extent[1], extent[2] + ' ' + extent[3]]; - pushSerializeAndPop(/** @type {module:ol/xml~NodeStackItem} */ - ({node: node}), this.ENVELOPE_SERIALIZERS_, - OBJECT_PROPERTY_NODE_FACTORY, - values, - objectStack, keys, this); -}; - - -/** - * @param {Node} node Node. - * @param {module:ol/geom/LinearRing} geometry LinearRing geometry. - * @param {Array.<*>} objectStack Node stack. - * @private - */ -GML3.prototype.writeLinearRing_ = function(node, geometry, objectStack) { - const context = objectStack[objectStack.length - 1]; - const srsName = context['srsName']; - if (srsName) { - node.setAttribute('srsName', srsName); - } - const posList = createElementNS(node.namespaceURI, 'posList'); - node.appendChild(posList); - this.writePosList_(posList, geometry, objectStack); -}; - - -/** - * @param {*} value Value. - * @param {Array.<*>} objectStack Object stack. - * @param {string=} opt_nodeName Node name. - * @return {Node} Node. - * @private - */ -GML3.prototype.RING_NODE_FACTORY_ = function(value, objectStack, opt_nodeName) { - const context = objectStack[objectStack.length - 1]; - const parentNode = context.node; - const exteriorWritten = context['exteriorWritten']; - if (exteriorWritten === undefined) { - context['exteriorWritten'] = true; - } - return createElementNS(parentNode.namespaceURI, - exteriorWritten !== undefined ? 'interior' : 'exterior'); -}; - - -/** - * @param {Node} node Node. - * @param {module:ol/geom/Polygon} geometry Polygon geometry. - * @param {Array.<*>} objectStack Node stack. - * @private - */ -GML3.prototype.writeSurfaceOrPolygon_ = function(node, geometry, objectStack) { - const context = objectStack[objectStack.length - 1]; - const hasZ = context['hasZ']; - const srsName = context['srsName']; - if (node.nodeName !== 'PolygonPatch' && srsName) { - node.setAttribute('srsName', srsName); - } - if (node.nodeName === 'Polygon' || node.nodeName === 'PolygonPatch') { - const rings = geometry.getLinearRings(); - pushSerializeAndPop( - {node: node, hasZ: hasZ, srsName: srsName}, - this.RING_SERIALIZERS_, - this.RING_NODE_FACTORY_, - rings, objectStack, undefined, this); - } else if (node.nodeName === 'Surface') { - const patches = createElementNS(node.namespaceURI, 'patches'); - node.appendChild(patches); - this.writeSurfacePatches_( - patches, geometry, objectStack); - } -}; - - -/** - * @param {Node} node Node. - * @param {module:ol/geom/LineString} geometry LineString geometry. - * @param {Array.<*>} objectStack Node stack. - * @private - */ -GML3.prototype.writeCurveOrLineString_ = function(node, geometry, objectStack) { - const context = objectStack[objectStack.length - 1]; - const srsName = context['srsName']; - if (node.nodeName !== 'LineStringSegment' && srsName) { - node.setAttribute('srsName', srsName); - } - if (node.nodeName === 'LineString' || - node.nodeName === 'LineStringSegment') { - const posList = createElementNS(node.namespaceURI, 'posList'); - node.appendChild(posList); - this.writePosList_(posList, geometry, objectStack); - } else if (node.nodeName === 'Curve') { - const segments = createElementNS(node.namespaceURI, 'segments'); - node.appendChild(segments); - this.writeCurveSegments_(segments, - geometry, objectStack); - } -}; - - -/** - * @param {Node} node Node. - * @param {module:ol/geom/MultiPolygon} geometry MultiPolygon geometry. - * @param {Array.<*>} objectStack Node stack. - * @private - */ -GML3.prototype.writeMultiSurfaceOrPolygon_ = function(node, geometry, objectStack) { - const context = objectStack[objectStack.length - 1]; - const hasZ = context['hasZ']; - const srsName = context['srsName']; - const surface = context['surface']; - if (srsName) { - node.setAttribute('srsName', srsName); - } - const polygons = geometry.getPolygons(); - pushSerializeAndPop({node: node, hasZ: hasZ, srsName: srsName, surface: surface}, - this.SURFACEORPOLYGONMEMBER_SERIALIZERS_, - this.MULTIGEOMETRY_MEMBER_NODE_FACTORY_, polygons, - objectStack, undefined, this); -}; - - -/** - * @param {Node} node Node. - * @param {module:ol/geom/MultiPoint} geometry MultiPoint geometry. - * @param {Array.<*>} objectStack Node stack. - * @private - */ -GML3.prototype.writeMultiPoint_ = function(node, geometry, objectStack) { - const context = objectStack[objectStack.length - 1]; - const srsName = context['srsName']; - const hasZ = context['hasZ']; - if (srsName) { - node.setAttribute('srsName', srsName); - } - const points = geometry.getPoints(); - pushSerializeAndPop({node: node, hasZ: hasZ, srsName: srsName}, - this.POINTMEMBER_SERIALIZERS_, - makeSimpleNodeFactory('pointMember'), points, - objectStack, undefined, this); -}; - - -/** - * @param {Node} node Node. - * @param {module:ol/geom/MultiLineString} geometry MultiLineString geometry. - * @param {Array.<*>} objectStack Node stack. - * @private - */ -GML3.prototype.writeMultiCurveOrLineString_ = function(node, geometry, objectStack) { - const context = objectStack[objectStack.length - 1]; - const hasZ = context['hasZ']; - const srsName = context['srsName']; - const curve = context['curve']; - if (srsName) { - node.setAttribute('srsName', srsName); - } - const lines = geometry.getLineStrings(); - pushSerializeAndPop({node: node, hasZ: hasZ, srsName: srsName, curve: curve}, - this.LINESTRINGORCURVEMEMBER_SERIALIZERS_, - this.MULTIGEOMETRY_MEMBER_NODE_FACTORY_, lines, - objectStack, undefined, this); -}; - - -/** - * @param {Node} node Node. - * @param {module:ol/geom/LinearRing} ring LinearRing geometry. - * @param {Array.<*>} objectStack Node stack. - * @private - */ -GML3.prototype.writeRing_ = function(node, ring, objectStack) { - const linearRing = createElementNS(node.namespaceURI, 'LinearRing'); - node.appendChild(linearRing); - this.writeLinearRing_(linearRing, ring, objectStack); -}; - - -/** - * @param {Node} node Node. - * @param {module:ol/geom/Polygon} polygon Polygon geometry. - * @param {Array.<*>} objectStack Node stack. - * @private - */ -GML3.prototype.writeSurfaceOrPolygonMember_ = function(node, polygon, objectStack) { - const child = this.GEOMETRY_NODE_FACTORY_( - polygon, objectStack); - if (child) { - node.appendChild(child); - this.writeSurfaceOrPolygon_(child, polygon, objectStack); - } -}; - - -/** - * @param {Node} node Node. - * @param {module:ol/geom/Point} point Point geometry. - * @param {Array.<*>} objectStack Node stack. - * @private - */ -GML3.prototype.writePointMember_ = function(node, point, objectStack) { - const child = createElementNS(node.namespaceURI, 'Point'); - node.appendChild(child); - this.writePoint_(child, point, objectStack); -}; - - -/** - * @param {Node} node Node. - * @param {module:ol/geom/LineString} line LineString geometry. - * @param {Array.<*>} objectStack Node stack. - * @private - */ -GML3.prototype.writeLineStringOrCurveMember_ = function(node, line, objectStack) { - const child = this.GEOMETRY_NODE_FACTORY_(line, objectStack); - if (child) { - node.appendChild(child); - this.writeCurveOrLineString_(child, line, objectStack); - } -}; - - -/** - * @param {Node} node Node. - * @param {module:ol/geom/Polygon} polygon Polygon geometry. - * @param {Array.<*>} objectStack Node stack. - * @private - */ -GML3.prototype.writeSurfacePatches_ = function(node, polygon, objectStack) { - const child = createElementNS(node.namespaceURI, 'PolygonPatch'); - node.appendChild(child); - this.writeSurfaceOrPolygon_(child, polygon, objectStack); -}; - - -/** - * @param {Node} node Node. - * @param {module:ol/geom/LineString} line LineString geometry. - * @param {Array.<*>} objectStack Node stack. - * @private - */ -GML3.prototype.writeCurveSegments_ = function(node, line, objectStack) { - const child = createElementNS(node.namespaceURI, - 'LineStringSegment'); - node.appendChild(child); - this.writeCurveOrLineString_(child, line, objectStack); -}; - - -/** - * @param {Node} node Node. - * @param {module:ol/geom/Geometry|module:ol/extent~Extent} geometry Geometry. - * @param {Array.<*>} objectStack Node stack. - */ -GML3.prototype.writeGeometryElement = function(node, geometry, objectStack) { - const context = /** @type {module:ol/format/Feature~WriteOptions} */ (objectStack[objectStack.length - 1]); - const item = assign({}, context); - item.node = node; - let value; - if (Array.isArray(geometry)) { - if (context.dataProjection) { - value = transformExtent( - geometry, context.featureProjection, context.dataProjection); - } else { - value = geometry; - } - } else { - value = transformWithOptions(/** @type {module:ol/geom/Geometry} */ (geometry), true, context); - } - pushSerializeAndPop(/** @type {module:ol/xml~NodeStackItem} */ - (item), this.GEOMETRY_SERIALIZERS_, - this.GEOMETRY_NODE_FACTORY_, [value], - objectStack, undefined, this); -}; - - -/** - * @param {Node} node Node. - * @param {module:ol/Feature} feature Feature. - * @param {Array.<*>} objectStack Node stack. - */ -GML3.prototype.writeFeatureElement = function(node, feature, objectStack) { - const fid = feature.getId(); - if (fid) { - node.setAttribute('fid', fid); - } - const context = /** @type {Object} */ (objectStack[objectStack.length - 1]); - const featureNS = context['featureNS']; - const geometryName = feature.getGeometryName(); - if (!context.serializers) { - context.serializers = {}; - context.serializers[featureNS] = {}; - } - const properties = feature.getProperties(); - const keys = []; - const values = []; - for (const key in properties) { - const value = properties[key]; - if (value !== null) { - keys.push(key); - values.push(value); - if (key == geometryName || value instanceof Geometry) { - if (!(key in context.serializers[featureNS])) { - context.serializers[featureNS][key] = makeChildAppender( - this.writeGeometryElement, this); - } - } else { - if (!(key in context.serializers[featureNS])) { - context.serializers[featureNS][key] = makeChildAppender(writeStringTextNode); - } - } - } - } - const item = assign({}, context); - item.node = node; - pushSerializeAndPop(/** @type {module:ol/xml~NodeStackItem} */ - (item), context.serializers, - makeSimpleNodeFactory(undefined, featureNS), - values, - objectStack, keys); -}; - - -/** - * @param {Node} node Node. - * @param {Array.} features Features. - * @param {Array.<*>} objectStack Node stack. - * @private - */ -GML3.prototype.writeFeatureMembers_ = function(node, features, objectStack) { - const context = /** @type {Object} */ (objectStack[objectStack.length - 1]); - const featureType = context['featureType']; - const featureNS = context['featureNS']; - const serializers = {}; - serializers[featureNS] = {}; - serializers[featureNS][featureType] = makeChildAppender( - this.writeFeatureElement, this); - const item = assign({}, context); - item.node = node; - pushSerializeAndPop(/** @type {module:ol/xml~NodeStackItem} */ - (item), - serializers, - makeSimpleNodeFactory(featureType, featureNS), features, - objectStack); -}; - - /** * @const * @type {Object.} @@ -1035,78 +1100,6 @@ const MULTIGEOMETRY_TO_MEMBER_NODENAME = { }; -/** - * @const - * @param {*} value Value. - * @param {Array.<*>} objectStack Object stack. - * @param {string=} opt_nodeName Node name. - * @return {Node|undefined} Node. - * @private - */ -GML3.prototype.MULTIGEOMETRY_MEMBER_NODE_FACTORY_ = function(value, objectStack, opt_nodeName) { - const parentNode = objectStack[objectStack.length - 1].node; - return createElementNS('http://www.opengis.net/gml', - MULTIGEOMETRY_TO_MEMBER_NODENAME[parentNode.nodeName]); -}; - - -/** - * @const - * @param {*} value Value. - * @param {Array.<*>} objectStack Object stack. - * @param {string=} opt_nodeName Node name. - * @return {Node|undefined} Node. - * @private - */ -GML3.prototype.GEOMETRY_NODE_FACTORY_ = function(value, objectStack, opt_nodeName) { - const context = objectStack[objectStack.length - 1]; - const multiSurface = context['multiSurface']; - const surface = context['surface']; - const curve = context['curve']; - const multiCurve = context['multiCurve']; - let nodeName; - if (!Array.isArray(value)) { - nodeName = /** @type {module:ol/geom/Geometry} */ (value).getType(); - if (nodeName === 'MultiPolygon' && multiSurface === true) { - nodeName = 'MultiSurface'; - } else if (nodeName === 'Polygon' && surface === true) { - nodeName = 'Surface'; - } else if (nodeName === 'LineString' && curve === true) { - nodeName = 'Curve'; - } else if (nodeName === 'MultiLineString' && multiCurve === true) { - nodeName = 'MultiCurve'; - } - } else { - nodeName = 'Envelope'; - } - return createElementNS('http://www.opengis.net/gml', - nodeName); -}; - - -/** - * Encode a geometry in GML 3.1.1 Simple Features. - * - * @param {module:ol/geom/Geometry} geometry Geometry. - * @param {module:ol/format/Feature~WriteOptions=} opt_options Options. - * @return {Node} Node. - * @override - * @api - */ -GML3.prototype.writeGeometryNode = function(geometry, opt_options) { - opt_options = this.adaptOptions(opt_options); - const geom = createElementNS('http://www.opengis.net/gml', 'geom'); - const context = {node: geom, hasZ: this.hasZ, srsName: this.srsName, - curve: this.curve_, surface: this.surface_, - multiSurface: this.multiSurface_, multiCurve: this.multiCurve_}; - if (opt_options) { - assign(context, opt_options); - } - this.writeGeometryElement(geom, geometry, [context]); - return geom; -}; - - /** * Encode an array of features in GML 3.1.1 Simple Features. * @@ -1119,37 +1112,6 @@ GML3.prototype.writeGeometryNode = function(geometry, opt_options) { GML3.prototype.writeFeatures; -/** - * Encode an array of features in the GML 3.1.1 format as an XML node. - * - * @param {Array.} features Features. - * @param {module:ol/format/Feature~WriteOptions=} opt_options Options. - * @return {Node} Node. - * @override - * @api - */ -GML3.prototype.writeFeaturesNode = function(features, opt_options) { - opt_options = this.adaptOptions(opt_options); - const node = createElementNS('http://www.opengis.net/gml', 'featureMembers'); - node.setAttributeNS(XML_SCHEMA_INSTANCE_URI, 'xsi:schemaLocation', this.schemaLocation); - const context = { - srsName: this.srsName, - hasZ: this.hasZ, - curve: this.curve_, - surface: this.surface_, - multiSurface: this.multiSurface_, - multiCurve: this.multiCurve_, - featureNS: this.featureNS, - featureType: this.featureType - }; - if (opt_options) { - assign(context, opt_options); - } - this.writeFeatureMembers_(node, features, [context]); - return node; -}; - - /** * @type {Object.>} * @private diff --git a/src/ol/format/GMLBase.js b/src/ol/format/GMLBase.js index 4965f4782c..6761aac1a6 100644 --- a/src/ol/format/GMLBase.js +++ b/src/ol/format/GMLBase.js @@ -75,44 +75,385 @@ export const GMLNS = 'http://www.opengis.net/gml'; * Optional configuration object. * @extends {module:ol/format/XMLFeature} */ -const GMLBase = function(opt_options) { - const options = /** @type {module:ol/format/GMLBase~Options} */ (opt_options ? opt_options : {}); +class GMLBase { + constructor(opt_options) { + const options = /** @type {module:ol/format/GMLBase~Options} */ (opt_options ? opt_options : {}); + + /** + * @protected + * @type {Array.|string|undefined} + */ + this.featureType = options.featureType; + + /** + * @protected + * @type {Object.|string|undefined} + */ + this.featureNS = options.featureNS; + + /** + * @protected + * @type {string} + */ + this.srsName = options.srsName; + + /** + * @protected + * @type {string} + */ + this.schemaLocation = ''; + + /** + * @type {Object.>} + */ + this.FEATURE_COLLECTION_PARSERS = {}; + this.FEATURE_COLLECTION_PARSERS[GMLNS] = { + 'featureMember': makeReplacer(GMLBase.prototype.readFeaturesInternal), + 'featureMembers': makeReplacer(GMLBase.prototype.readFeaturesInternal) + }; + + XMLFeature.call(this); + } /** - * @protected - * @type {Array.|string|undefined} + * @param {Node} node Node. + * @param {Array.<*>} objectStack Object stack. + * @return {Array. | undefined} Features. */ - this.featureType = options.featureType; + readFeaturesInternal(node, objectStack) { + 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); + } + } else if (localName == 'featureMembers' || localName == 'featureMember') { + const context = objectStack[0]; + let featureType = context['featureType']; + let featureNS = context['featureNS']; + const prefix = 'p'; + const defaultPrefix = 'p0'; + if (!featureType && node.childNodes) { + featureType = [], featureNS = {}; + for (let i = 0, ii = node.childNodes.length; i < ii; ++i) { + const child = node.childNodes[i]; + if (child.nodeType === 1) { + const ft = child.nodeName.split(':').pop(); + if (featureType.indexOf(ft) === -1) { + let key = ''; + let count = 0; + const uri = child.namespaceURI; + for (const candidate in featureNS) { + if (featureNS[candidate] === uri) { + key = candidate; + break; + } + ++count; + } + if (!key) { + key = prefix + count; + featureNS[key] = uri; + } + featureType.push(key + ':' + ft); + } + } + } + if (localName != 'featureMember') { + // recheck featureType for each featureMember + context['featureType'] = featureType; + context['featureNS'] = featureNS; + } + } + if (typeof featureNS === 'string') { + const ns = featureNS; + featureNS = {}; + featureNS[defaultPrefix] = ns; + } + const parsersNS = {}; + const featureTypes = Array.isArray(featureType) ? featureType : [featureType]; + for (const p in featureNS) { + const parsers = {}; + for (let i = 0, ii = featureTypes.length; i < ii; ++i) { + const featurePrefix = featureTypes[i].indexOf(':') === -1 ? + defaultPrefix : featureTypes[i].split(':')[0]; + if (featurePrefix === p) { + parsers[featureTypes[i].split(':').pop()] = + (localName == 'featureMembers') ? + makeArrayPusher(this.readFeatureElement, this) : + makeReplacer(this.readFeatureElement, this); + } + } + parsersNS[featureNS[p]] = parsers; + } + if (localName == 'featureMember') { + features = pushParseAndPop(undefined, parsersNS, node, objectStack); + } else { + features = pushParseAndPop([], parsersNS, node, objectStack); + } + } + if (features === null) { + features = []; + } + return features; + } /** - * @protected - * @type {Object.|string|undefined} + * @param {Node} node Node. + * @param {Array.<*>} objectStack Object stack. + * @return {module:ol/geom/Geometry|undefined} Geometry. */ - this.featureNS = options.featureNS; + readGeometryElement(node, objectStack) { + const context = /** @type {Object} */ (objectStack[0]); + context['srsName'] = node.firstElementChild.getAttribute('srsName'); + context['srsDimension'] = node.firstElementChild.getAttribute('srsDimension'); + /** @type {module:ol/geom/Geometry} */ + const geometry = pushParseAndPop(null, this.GEOMETRY_PARSERS_, node, objectStack, this); + if (geometry) { + return ( + /** @type {module:ol/geom/Geometry} */ (transformWithOptions(geometry, false, context)) + ); + } else { + return undefined; + } + } /** - * @protected - * @type {string} + * @param {Node} node Node. + * @param {Array.<*>} objectStack Object stack. + * @return {module:ol/Feature} Feature. */ - this.srsName = options.srsName; + readFeatureElement(node, objectStack) { + let n; + const fid = node.getAttribute('fid') || getAttributeNS(node, GMLNS, 'id'); + const values = {}; + let geometryName; + for (n = node.firstElementChild; n; n = n.nextElementSibling) { + 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); + 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') { + geometryName = localName; + } + values[localName] = this.readGeometryElement(n, objectStack); + } + } + const feature = new Feature(values); + if (geometryName) { + feature.setGeometryName(geometryName); + } + if (fid) { + feature.setId(fid); + } + return feature; + } /** - * @protected - * @type {string} + * @param {Node} node Node. + * @param {Array.<*>} objectStack Object stack. + * @return {module:ol/geom/Point|undefined} Point. */ - this.schemaLocation = ''; + readPoint(node, objectStack) { + const flatCoordinates = this.readFlatCoordinatesFromNode_(node, objectStack); + if (flatCoordinates) { + return new Point(flatCoordinates, GeometryLayout.XYZ); + } + } /** - * @type {Object.>} + * @param {Node} node Node. + * @param {Array.<*>} objectStack Object stack. + * @return {module:ol/geom/MultiPoint|undefined} MultiPoint. */ - this.FEATURE_COLLECTION_PARSERS = {}; - this.FEATURE_COLLECTION_PARSERS[GMLNS] = { - 'featureMember': makeReplacer(GMLBase.prototype.readFeaturesInternal), - 'featureMembers': makeReplacer(GMLBase.prototype.readFeaturesInternal) - }; + readMultiPoint(node, objectStack) { + /** @type {Array.>} */ + const coordinates = pushParseAndPop([], + this.MULTIPOINT_PARSERS_, node, objectStack, this); + if (coordinates) { + return new MultiPoint(coordinates); + } else { + return undefined; + } + } - XMLFeature.call(this); -}; + /** + * @param {Node} node Node. + * @param {Array.<*>} objectStack Object stack. + * @return {module:ol/geom/MultiLineString|undefined} MultiLineString. + */ + readMultiLineString(node, objectStack) { + /** @type {Array.} */ + const lineStrings = pushParseAndPop([], + this.MULTILINESTRING_PARSERS_, node, objectStack, this); + if (lineStrings) { + return new MultiLineString(lineStrings); + } + } + + /** + * @param {Node} node Node. + * @param {Array.<*>} objectStack Object stack. + * @return {module:ol/geom/MultiPolygon|undefined} MultiPolygon. + */ + readMultiPolygon(node, objectStack) { + /** @type {Array.} */ + const polygons = pushParseAndPop([], this.MULTIPOLYGON_PARSERS_, node, objectStack, this); + if (polygons) { + return new MultiPolygon(polygons); + } + } + + /** + * @param {Node} node Node. + * @param {Array.<*>} objectStack Object stack. + * @private + */ + pointMemberParser_(node, objectStack) { + parseNode(this.POINTMEMBER_PARSERS_, node, objectStack, this); + } + + /** + * @param {Node} node Node. + * @param {Array.<*>} objectStack Object stack. + * @private + */ + lineStringMemberParser_(node, objectStack) { + parseNode(this.LINESTRINGMEMBER_PARSERS_, node, objectStack, this); + } + + /** + * @param {Node} node Node. + * @param {Array.<*>} objectStack Object stack. + * @private + */ + polygonMemberParser_(node, objectStack) { + parseNode(this.POLYGONMEMBER_PARSERS_, node, objectStack, this); + } + + /** + * @param {Node} node Node. + * @param {Array.<*>} objectStack Object stack. + * @return {module:ol/geom/LineString|undefined} LineString. + */ + readLineString(node, objectStack) { + const flatCoordinates = this.readFlatCoordinatesFromNode_(node, objectStack); + if (flatCoordinates) { + const lineString = new LineString(flatCoordinates, GeometryLayout.XYZ); + return lineString; + } else { + return undefined; + } + } + + /** + * @param {Node} node Node. + * @param {Array.<*>} objectStack Object stack. + * @private + * @return {Array.|undefined} LinearRing flat coordinates. + */ + readFlatLinearRing_(node, objectStack) { + const ring = pushParseAndPop(null, + this.GEOMETRY_FLAT_COORDINATES_PARSERS_, node, + objectStack, this); + if (ring) { + return ring; + } else { + return undefined; + } + } + + /** + * @param {Node} node Node. + * @param {Array.<*>} objectStack Object stack. + * @return {module:ol/geom/LinearRing|undefined} LinearRing. + */ + readLinearRing(node, objectStack) { + const flatCoordinates = this.readFlatCoordinatesFromNode_(node, objectStack); + if (flatCoordinates) { + return new LinearRing(flatCoordinates, GeometryLayout.XYZ); + } + } + + /** + * @param {Node} node Node. + * @param {Array.<*>} objectStack Object stack. + * @return {module:ol/geom/Polygon|undefined} Polygon. + */ + readPolygon(node, objectStack) { + /** @type {Array.>} */ + const flatLinearRings = pushParseAndPop([null], + this.FLAT_LINEAR_RINGS_PARSERS_, node, objectStack, this); + if (flatLinearRings && flatLinearRings[0]) { + const flatCoordinates = flatLinearRings[0]; + const ends = [flatCoordinates.length]; + let i, ii; + for (i = 1, ii = flatLinearRings.length; i < ii; ++i) { + extend(flatCoordinates, flatLinearRings[i]); + ends.push(flatCoordinates.length); + } + return new Polygon(flatCoordinates, GeometryLayout.XYZ, ends); + } else { + return undefined; + } + } + + /** + * @param {Node} node Node. + * @param {Array.<*>} objectStack Object stack. + * @private + * @return {Array.} Flat coordinates. + */ + readFlatCoordinatesFromNode_(node, objectStack) { + return pushParseAndPop(null, this.GEOMETRY_FLAT_COORDINATES_PARSERS_, node, objectStack, this); + } + + /** + * @inheritDoc + */ + readGeometryFromNode(node, opt_options) { + const geometry = this.readGeometryElement(node, + [this.getReadOptions(node, opt_options ? opt_options : {})]); + return geometry ? geometry : null; + } + + /** + * @inheritDoc + */ + readFeaturesFromNode(node, opt_options) { + const options = { + featureType: this.featureType, + featureNS: this.featureNS + }; + if (opt_options) { + assign(options, this.getReadOptions(node, opt_options)); + } + const features = this.readFeaturesInternal(node, [options]); + return features || []; + } + + /** + * @inheritDoc + */ + readProjectionFromNode(node) { + return getProjection(this.srsName ? this.srsName : node.firstElementChild.getAttribute('srsName')); + } +} inherits(GMLBase, XMLFeature); @@ -131,329 +472,6 @@ inherits(GMLBase, XMLFeature); const ONLY_WHITESPACE_RE = /^[\s\xa0]*$/; -/** - * @param {Node} node Node. - * @param {Array.<*>} objectStack Object stack. - * @return {Array. | undefined} Features. - */ -GMLBase.prototype.readFeaturesInternal = function(node, objectStack) { - 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); - } - } else if (localName == 'featureMembers' || localName == 'featureMember') { - const context = objectStack[0]; - let featureType = context['featureType']; - let featureNS = context['featureNS']; - const prefix = 'p'; - const defaultPrefix = 'p0'; - if (!featureType && node.childNodes) { - featureType = [], featureNS = {}; - for (let i = 0, ii = node.childNodes.length; i < ii; ++i) { - const child = node.childNodes[i]; - if (child.nodeType === 1) { - const ft = child.nodeName.split(':').pop(); - if (featureType.indexOf(ft) === -1) { - let key = ''; - let count = 0; - const uri = child.namespaceURI; - for (const candidate in featureNS) { - if (featureNS[candidate] === uri) { - key = candidate; - break; - } - ++count; - } - if (!key) { - key = prefix + count; - featureNS[key] = uri; - } - featureType.push(key + ':' + ft); - } - } - } - if (localName != 'featureMember') { - // recheck featureType for each featureMember - context['featureType'] = featureType; - context['featureNS'] = featureNS; - } - } - if (typeof featureNS === 'string') { - const ns = featureNS; - featureNS = {}; - featureNS[defaultPrefix] = ns; - } - const parsersNS = {}; - const featureTypes = Array.isArray(featureType) ? featureType : [featureType]; - for (const p in featureNS) { - const parsers = {}; - for (let i = 0, ii = featureTypes.length; i < ii; ++i) { - const featurePrefix = featureTypes[i].indexOf(':') === -1 ? - defaultPrefix : featureTypes[i].split(':')[0]; - if (featurePrefix === p) { - parsers[featureTypes[i].split(':').pop()] = - (localName == 'featureMembers') ? - makeArrayPusher(this.readFeatureElement, this) : - makeReplacer(this.readFeatureElement, this); - } - } - parsersNS[featureNS[p]] = parsers; - } - if (localName == 'featureMember') { - features = pushParseAndPop(undefined, parsersNS, node, objectStack); - } else { - features = pushParseAndPop([], parsersNS, node, objectStack); - } - } - if (features === null) { - features = []; - } - return features; -}; - - -/** - * @param {Node} node Node. - * @param {Array.<*>} objectStack Object stack. - * @return {module:ol/geom/Geometry|undefined} Geometry. - */ -GMLBase.prototype.readGeometryElement = function(node, objectStack) { - const context = /** @type {Object} */ (objectStack[0]); - context['srsName'] = node.firstElementChild.getAttribute('srsName'); - context['srsDimension'] = node.firstElementChild.getAttribute('srsDimension'); - /** @type {module:ol/geom/Geometry} */ - const geometry = pushParseAndPop(null, this.GEOMETRY_PARSERS_, node, objectStack, this); - if (geometry) { - return ( - /** @type {module:ol/geom/Geometry} */ (transformWithOptions(geometry, false, context)) - ); - } else { - return undefined; - } -}; - - -/** - * @param {Node} node Node. - * @param {Array.<*>} objectStack Object stack. - * @return {module:ol/Feature} Feature. - */ -GMLBase.prototype.readFeatureElement = function(node, objectStack) { - let n; - const fid = node.getAttribute('fid') || getAttributeNS(node, GMLNS, 'id'); - const values = {}; - let geometryName; - for (n = node.firstElementChild; n; n = n.nextElementSibling) { - 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); - 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') { - geometryName = localName; - } - values[localName] = this.readGeometryElement(n, objectStack); - } - } - const feature = new Feature(values); - if (geometryName) { - feature.setGeometryName(geometryName); - } - if (fid) { - feature.setId(fid); - } - return feature; -}; - - -/** - * @param {Node} node Node. - * @param {Array.<*>} objectStack Object stack. - * @return {module:ol/geom/Point|undefined} Point. - */ -GMLBase.prototype.readPoint = function(node, objectStack) { - const flatCoordinates = this.readFlatCoordinatesFromNode_(node, objectStack); - if (flatCoordinates) { - return new Point(flatCoordinates, GeometryLayout.XYZ); - } -}; - - -/** - * @param {Node} node Node. - * @param {Array.<*>} objectStack Object stack. - * @return {module:ol/geom/MultiPoint|undefined} MultiPoint. - */ -GMLBase.prototype.readMultiPoint = function(node, objectStack) { - /** @type {Array.>} */ - const coordinates = pushParseAndPop([], - this.MULTIPOINT_PARSERS_, node, objectStack, this); - if (coordinates) { - return new MultiPoint(coordinates); - } else { - return undefined; - } -}; - - -/** - * @param {Node} node Node. - * @param {Array.<*>} objectStack Object stack. - * @return {module:ol/geom/MultiLineString|undefined} MultiLineString. - */ -GMLBase.prototype.readMultiLineString = function(node, objectStack) { - /** @type {Array.} */ - const lineStrings = pushParseAndPop([], - this.MULTILINESTRING_PARSERS_, node, objectStack, this); - if (lineStrings) { - return new MultiLineString(lineStrings); - } -}; - - -/** - * @param {Node} node Node. - * @param {Array.<*>} objectStack Object stack. - * @return {module:ol/geom/MultiPolygon|undefined} MultiPolygon. - */ -GMLBase.prototype.readMultiPolygon = function(node, objectStack) { - /** @type {Array.} */ - const polygons = pushParseAndPop([], this.MULTIPOLYGON_PARSERS_, node, objectStack, this); - if (polygons) { - return new MultiPolygon(polygons); - } -}; - - -/** - * @param {Node} node Node. - * @param {Array.<*>} objectStack Object stack. - * @private - */ -GMLBase.prototype.pointMemberParser_ = function(node, objectStack) { - parseNode(this.POINTMEMBER_PARSERS_, node, objectStack, this); -}; - - -/** - * @param {Node} node Node. - * @param {Array.<*>} objectStack Object stack. - * @private - */ -GMLBase.prototype.lineStringMemberParser_ = function(node, objectStack) { - parseNode(this.LINESTRINGMEMBER_PARSERS_, node, objectStack, this); -}; - - -/** - * @param {Node} node Node. - * @param {Array.<*>} objectStack Object stack. - * @private - */ -GMLBase.prototype.polygonMemberParser_ = function(node, objectStack) { - parseNode(this.POLYGONMEMBER_PARSERS_, node, objectStack, this); -}; - - -/** - * @param {Node} node Node. - * @param {Array.<*>} objectStack Object stack. - * @return {module:ol/geom/LineString|undefined} LineString. - */ -GMLBase.prototype.readLineString = function(node, objectStack) { - const flatCoordinates = this.readFlatCoordinatesFromNode_(node, objectStack); - if (flatCoordinates) { - const lineString = new LineString(flatCoordinates, GeometryLayout.XYZ); - return lineString; - } else { - return undefined; - } -}; - - -/** - * @param {Node} node Node. - * @param {Array.<*>} objectStack Object stack. - * @private - * @return {Array.|undefined} LinearRing flat coordinates. - */ -GMLBase.prototype.readFlatLinearRing_ = function(node, objectStack) { - const ring = pushParseAndPop(null, - this.GEOMETRY_FLAT_COORDINATES_PARSERS_, node, - objectStack, this); - if (ring) { - return ring; - } else { - return undefined; - } -}; - - -/** - * @param {Node} node Node. - * @param {Array.<*>} objectStack Object stack. - * @return {module:ol/geom/LinearRing|undefined} LinearRing. - */ -GMLBase.prototype.readLinearRing = function(node, objectStack) { - const flatCoordinates = this.readFlatCoordinatesFromNode_(node, objectStack); - if (flatCoordinates) { - return new LinearRing(flatCoordinates, GeometryLayout.XYZ); - } -}; - - -/** - * @param {Node} node Node. - * @param {Array.<*>} objectStack Object stack. - * @return {module:ol/geom/Polygon|undefined} Polygon. - */ -GMLBase.prototype.readPolygon = function(node, objectStack) { - /** @type {Array.>} */ - const flatLinearRings = pushParseAndPop([null], - this.FLAT_LINEAR_RINGS_PARSERS_, node, objectStack, this); - if (flatLinearRings && flatLinearRings[0]) { - const flatCoordinates = flatLinearRings[0]; - const ends = [flatCoordinates.length]; - let i, ii; - for (i = 1, ii = flatLinearRings.length; i < ii; ++i) { - extend(flatCoordinates, flatLinearRings[i]); - ends.push(flatCoordinates.length); - } - return new Polygon(flatCoordinates, GeometryLayout.XYZ, ends); - } else { - return undefined; - } -}; - - -/** - * @param {Node} node Node. - * @param {Array.<*>} objectStack Object stack. - * @private - * @return {Array.} Flat coordinates. - */ -GMLBase.prototype.readFlatCoordinatesFromNode_ = function(node, objectStack) { - return pushParseAndPop(null, this.GEOMETRY_FLAT_COORDINATES_PARSERS_, node, objectStack, this); -}; - - /** * @const * @type {Object.>} @@ -541,16 +559,6 @@ GMLBase.prototype.RING_PARSERS = { }; -/** - * @inheritDoc - */ -GMLBase.prototype.readGeometryFromNode = function(node, opt_options) { - const geometry = this.readGeometryElement(node, - [this.getReadOptions(node, opt_options ? opt_options : {})]); - return geometry ? geometry : null; -}; - - /** * Read all features from a GML FeatureCollection. * @@ -563,26 +571,4 @@ GMLBase.prototype.readGeometryFromNode = function(node, opt_options) { GMLBase.prototype.readFeatures; -/** - * @inheritDoc - */ -GMLBase.prototype.readFeaturesFromNode = function(node, opt_options) { - const options = { - featureType: this.featureType, - featureNS: this.featureNS - }; - if (opt_options) { - assign(options, this.getReadOptions(node, opt_options)); - } - const features = this.readFeaturesInternal(node, [options]); - return features || []; -}; - - -/** - * @inheritDoc - */ -GMLBase.prototype.readProjectionFromNode = function(node) { - return getProjection(this.srsName ? this.srsName : node.firstElementChild.getAttribute('srsName')); -}; export default GMLBase; diff --git a/src/ol/format/GPX.js b/src/ol/format/GPX.js index c2c696a0cb..dbcc8fd9e2 100644 --- a/src/ol/format/GPX.js +++ b/src/ol/format/GPX.js @@ -43,23 +43,109 @@ import {createElementNS, makeArrayPusher, makeArraySerializer, makeChildAppender * @param {module:ol/format/GPX~Options=} opt_options Options. * @api */ -const GPX = function(opt_options) { +class GPX { + constructor(opt_options) { - const options = opt_options ? opt_options : {}; + const options = opt_options ? opt_options : {}; - XMLFeature.call(this); + XMLFeature.call(this); + + /** + * @inheritDoc + */ + this.dataProjection = getProjection('EPSG:4326'); + + /** + * @type {function(module:ol/Feature, Node)|undefined} + * @private + */ + this.readExtensions_ = options.readExtensions; + } + + /** + * @param {Array.} features List of features. + * @private + */ + handleReadExtensions_(features) { + if (!features) { + features = []; + } + for (let i = 0, ii = features.length; i < ii; ++i) { + const feature = features[i]; + if (this.readExtensions_) { + const extensionsNode = feature.get('extensionsNode_') || null; + this.readExtensions_(feature, extensionsNode); + } + feature.set('extensionsNode_', undefined); + } + } /** * @inheritDoc */ - this.dataProjection = getProjection('EPSG:4326'); + readFeatureFromNode(node, opt_options) { + if (!includes(NAMESPACE_URIS, node.namespaceURI)) { + return null; + } + const featureReader = FEATURE_READER[node.localName]; + if (!featureReader) { + return null; + } + const feature = featureReader(node, [this.getReadOptions(node, opt_options)]); + if (!feature) { + return null; + } + this.handleReadExtensions_([feature]); + return feature; + } /** - * @type {function(module:ol/Feature, Node)|undefined} - * @private + * @inheritDoc */ - this.readExtensions_ = options.readExtensions; -}; + readFeaturesFromNode(node, opt_options) { + if (!includes(NAMESPACE_URIS, node.namespaceURI)) { + return []; + } + if (node.localName == 'gpx') { + /** @type {Array.} */ + const features = pushParseAndPop([], GPX_PARSERS, + node, [this.getReadOptions(node, opt_options)]); + if (features) { + this.handleReadExtensions_(features); + return features; + } else { + return []; + } + } + return []; + } + + /** + * Encode an array of features in the GPX format as an XML node. + * LineString geometries are output as routes (``), and MultiLineString + * as tracks (``). + * + * @param {Array.} features Features. + * @param {module:ol/format/Feature~WriteOptions=} opt_options Options. + * @return {Node} Node. + * @override + * @api + */ + writeFeaturesNode(features, opt_options) { + opt_options = this.adaptOptions(opt_options); + //FIXME Serialize metadata + const gpx = createElementNS('http://www.topografix.com/GPX/1/1', 'gpx'); + const xmlnsUri = 'http://www.w3.org/2000/xmlns/'; + gpx.setAttributeNS(xmlnsUri, 'xmlns:xsi', XML_SCHEMA_INSTANCE_URI); + gpx.setAttributeNS(XML_SCHEMA_INSTANCE_URI, 'xsi:schemaLocation', SCHEMA_LOCATION); + gpx.setAttribute('version', '1.1'); + gpx.setAttribute('creator', 'OpenLayers'); + + pushSerializeAndPop(/** @type {module:ol/xml~NodeStackItem} */ + ({node: gpx}), GPX_SERIALIZERS, GPX_NODE_FACTORY, features, [opt_options]); + return gpx; + } +} inherits(GPX, XMLFeature); @@ -614,25 +700,6 @@ function readWpt(node, objectStack) { } -/** - * @param {Array.} features List of features. - * @private - */ -GPX.prototype.handleReadExtensions_ = function(features) { - if (!features) { - features = []; - } - for (let i = 0, ii = features.length; i < ii; ++i) { - const feature = features[i]; - if (this.readExtensions_) { - const extensionsNode = feature.get('extensionsNode_') || null; - this.readExtensions_(feature, extensionsNode); - } - feature.set('extensionsNode_', undefined); - } -}; - - /** * Read the first feature from a GPX source. * Routes (``) are converted into LineString geometries, and tracks (``) @@ -647,26 +714,6 @@ GPX.prototype.handleReadExtensions_ = function(features) { GPX.prototype.readFeature; -/** - * @inheritDoc - */ -GPX.prototype.readFeatureFromNode = function(node, opt_options) { - if (!includes(NAMESPACE_URIS, node.namespaceURI)) { - return null; - } - const featureReader = FEATURE_READER[node.localName]; - if (!featureReader) { - return null; - } - const feature = featureReader(node, [this.getReadOptions(node, opt_options)]); - if (!feature) { - return null; - } - this.handleReadExtensions_([feature]); - return feature; -}; - - /** * Read all features from a GPX source. * Routes (``) are converted into LineString geometries, and tracks (``) @@ -681,28 +728,6 @@ GPX.prototype.readFeatureFromNode = function(node, opt_options) { GPX.prototype.readFeatures; -/** - * @inheritDoc - */ -GPX.prototype.readFeaturesFromNode = function(node, opt_options) { - if (!includes(NAMESPACE_URIS, node.namespaceURI)) { - return []; - } - if (node.localName == 'gpx') { - /** @type {Array.} */ - const features = pushParseAndPop([], GPX_PARSERS, - node, [this.getReadOptions(node, opt_options)]); - if (features) { - this.handleReadExtensions_(features); - return features; - } else { - return []; - } - } - return []; -}; - - /** * Read the projection from a GPX source. * @@ -874,29 +899,4 @@ function writeWpt(node, feature, objectStack) { GPX.prototype.writeFeatures; -/** - * Encode an array of features in the GPX format as an XML node. - * LineString geometries are output as routes (``), and MultiLineString - * as tracks (``). - * - * @param {Array.} features Features. - * @param {module:ol/format/Feature~WriteOptions=} opt_options Options. - * @return {Node} Node. - * @override - * @api - */ -GPX.prototype.writeFeaturesNode = function(features, opt_options) { - opt_options = this.adaptOptions(opt_options); - //FIXME Serialize metadata - const gpx = createElementNS('http://www.topografix.com/GPX/1/1', 'gpx'); - const xmlnsUri = 'http://www.w3.org/2000/xmlns/'; - gpx.setAttributeNS(xmlnsUri, 'xmlns:xsi', XML_SCHEMA_INSTANCE_URI); - gpx.setAttributeNS(XML_SCHEMA_INSTANCE_URI, 'xsi:schemaLocation', SCHEMA_LOCATION); - gpx.setAttribute('version', '1.1'); - gpx.setAttribute('creator', 'OpenLayers'); - - pushSerializeAndPop(/** @type {module:ol/xml~NodeStackItem} */ - ({node: gpx}), GPX_SERIALIZERS, GPX_NODE_FACTORY, features, [opt_options]); - return gpx; -}; export default GPX; diff --git a/src/ol/format/GeoJSON.js b/src/ol/format/GeoJSON.js index dadfc2ab1e..f3392fdfdf 100644 --- a/src/ol/format/GeoJSON.js +++ b/src/ol/format/GeoJSON.js @@ -42,38 +42,191 @@ import {get as getProjection} from '../proj.js'; * @param {module:ol/format/GeoJSON~Options=} opt_options Options. * @api */ -const GeoJSON = function(opt_options) { +class GeoJSON { + constructor(opt_options) { - const options = opt_options ? opt_options : {}; + const options = opt_options ? opt_options : {}; - JSONFeature.call(this); + JSONFeature.call(this); + + /** + * @inheritDoc + */ + this.dataProjection = getProjection( + options.dataProjection ? + options.dataProjection : 'EPSG:4326'); + + if (options.featureProjection) { + this.defaultFeatureProjection = getProjection(options.featureProjection); + } + + /** + * Name of the geometry attribute for features. + * @type {string|undefined} + * @private + */ + this.geometryName_ = options.geometryName; + + /** + * Look for the geometry name in the feature GeoJSON + * @type {boolean|undefined} + * @private + */ + this.extractGeometryName_ = options.extractGeometryName; + + } /** * @inheritDoc */ - this.dataProjection = getProjection( - options.dataProjection ? - options.dataProjection : 'EPSG:4326'); + readFeatureFromObject(object, opt_options) { + /** + * @type {GeoJSONFeature} + */ + let geoJSONFeature = null; + if (object.type === 'Feature') { + geoJSONFeature = /** @type {GeoJSONFeature} */ (object); + } else { + geoJSONFeature = /** @type {GeoJSONFeature} */ ({ + type: 'Feature', + geometry: /** @type {GeoJSONGeometry|GeoJSONGeometryCollection} */ (object) + }); + } - if (options.featureProjection) { - this.defaultFeatureProjection = getProjection(options.featureProjection); + const geometry = readGeometry(geoJSONFeature.geometry, opt_options); + const feature = new Feature(); + if (this.geometryName_) { + feature.setGeometryName(this.geometryName_); + } else if (this.extractGeometryName_ && geoJSONFeature.geometry_name !== undefined) { + feature.setGeometryName(geoJSONFeature.geometry_name); + } + feature.setGeometry(geometry); + if (geoJSONFeature.id !== undefined) { + feature.setId(geoJSONFeature.id); + } + if (geoJSONFeature.properties) { + feature.setProperties(geoJSONFeature.properties); + } + return feature; } /** - * Name of the geometry attribute for features. - * @type {string|undefined} - * @private + * @inheritDoc */ - this.geometryName_ = options.geometryName; + readFeaturesFromObject(object, opt_options) { + const geoJSONObject = /** @type {GeoJSONObject} */ (object); + /** @type {Array.} */ + let features = null; + if (geoJSONObject.type === 'FeatureCollection') { + const geoJSONFeatureCollection = /** @type {GeoJSONFeatureCollection} */ (object); + features = []; + const geoJSONFeatures = geoJSONFeatureCollection.features; + for (let i = 0, ii = geoJSONFeatures.length; i < ii; ++i) { + features.push(this.readFeatureFromObject(geoJSONFeatures[i], opt_options)); + } + } else { + features = [this.readFeatureFromObject(object, opt_options)]; + } + return features; + } /** - * Look for the geometry name in the feature GeoJSON - * @type {boolean|undefined} - * @private + * @inheritDoc */ - this.extractGeometryName_ = options.extractGeometryName; + readGeometryFromObject(object, opt_options) { + return readGeometry(/** @type {GeoJSONGeometry} */ (object), opt_options); + } -}; + /** + * @inheritDoc + */ + readProjectionFromObject(object) { + const geoJSONObject = /** @type {GeoJSONObject} */ (object); + const crs = geoJSONObject.crs; + let projection; + if (crs) { + if (crs.type == 'name') { + projection = getProjection(crs.properties.name); + } else { + assert(false, 36); // Unknown SRS type + } + } else { + projection = this.dataProjection; + } + return ( + /** @type {module:ol/proj/Projection} */ (projection) + ); + } + + /** + * Encode a feature as a GeoJSON Feature object. + * + * @param {module:ol/Feature} feature Feature. + * @param {module:ol/format/Feature~WriteOptions=} opt_options Write options. + * @return {GeoJSONFeature} Object. + * @override + * @api + */ + writeFeatureObject(feature, opt_options) { + opt_options = this.adaptOptions(opt_options); + + const object = /** @type {GeoJSONFeature} */ ({ + 'type': 'Feature' + }); + const id = feature.getId(); + if (id !== undefined) { + object.id = id; + } + const geometry = feature.getGeometry(); + if (geometry) { + object.geometry = writeGeometry(geometry, opt_options); + } else { + object.geometry = null; + } + const properties = feature.getProperties(); + delete properties[feature.getGeometryName()]; + if (!isEmpty(properties)) { + object.properties = properties; + } else { + object.properties = null; + } + return object; + } + + /** + * Encode an array of features as a GeoJSON object. + * + * @param {Array.} features Features. + * @param {module:ol/format/Feature~WriteOptions=} opt_options Write options. + * @return {GeoJSONFeatureCollection} GeoJSON Object. + * @override + * @api + */ + writeFeaturesObject(features, opt_options) { + opt_options = this.adaptOptions(opt_options); + const objects = []; + for (let i = 0, ii = features.length; i < ii; ++i) { + objects.push(this.writeFeatureObject(features[i], opt_options)); + } + return /** @type {GeoJSONFeatureCollection} */ ({ + type: 'FeatureCollection', + features: objects + }); + } + + /** + * Encode a geometry as a GeoJSON object. + * + * @param {module:ol/geom/Geometry} geometry Geometry. + * @param {module:ol/format/Feature~WriteOptions=} opt_options Write options. + * @return {GeoJSONGeometry|GeoJSONGeometryCollection} Object. + * @override + * @api + */ + writeGeometryObject(geometry, opt_options) { + return writeGeometry(geometry, this.adaptOptions(opt_options)); + } +} inherits(GeoJSON, JSONFeature); @@ -354,62 +507,6 @@ GeoJSON.prototype.readFeature; GeoJSON.prototype.readFeatures; -/** - * @inheritDoc - */ -GeoJSON.prototype.readFeatureFromObject = function(object, opt_options) { - /** - * @type {GeoJSONFeature} - */ - let geoJSONFeature = null; - if (object.type === 'Feature') { - geoJSONFeature = /** @type {GeoJSONFeature} */ (object); - } else { - geoJSONFeature = /** @type {GeoJSONFeature} */ ({ - type: 'Feature', - geometry: /** @type {GeoJSONGeometry|GeoJSONGeometryCollection} */ (object) - }); - } - - const geometry = readGeometry(geoJSONFeature.geometry, opt_options); - const feature = new Feature(); - if (this.geometryName_) { - feature.setGeometryName(this.geometryName_); - } else if (this.extractGeometryName_ && geoJSONFeature.geometry_name !== undefined) { - feature.setGeometryName(geoJSONFeature.geometry_name); - } - feature.setGeometry(geometry); - if (geoJSONFeature.id !== undefined) { - feature.setId(geoJSONFeature.id); - } - if (geoJSONFeature.properties) { - feature.setProperties(geoJSONFeature.properties); - } - return feature; -}; - - -/** - * @inheritDoc - */ -GeoJSON.prototype.readFeaturesFromObject = function(object, opt_options) { - const geoJSONObject = /** @type {GeoJSONObject} */ (object); - /** @type {Array.} */ - let features = null; - if (geoJSONObject.type === 'FeatureCollection') { - const geoJSONFeatureCollection = /** @type {GeoJSONFeatureCollection} */ (object); - features = []; - const geoJSONFeatures = geoJSONFeatureCollection.features; - for (let i = 0, ii = geoJSONFeatures.length; i < ii; ++i) { - features.push(this.readFeatureFromObject(geoJSONFeatures[i], opt_options)); - } - } else { - features = [this.readFeatureFromObject(object, opt_options)]; - } - return features; -}; - - /** * Read a geometry from a GeoJSON source. * @@ -422,14 +519,6 @@ GeoJSON.prototype.readFeaturesFromObject = function(object, opt_options) { GeoJSON.prototype.readGeometry; -/** - * @inheritDoc - */ -GeoJSON.prototype.readGeometryFromObject = function(object, opt_options) { - return readGeometry(/** @type {GeoJSONGeometry} */ (object), opt_options); -}; - - /** * Read the projection from a GeoJSON source. * @@ -441,28 +530,6 @@ GeoJSON.prototype.readGeometryFromObject = function(object, opt_options) { GeoJSON.prototype.readProjection; -/** - * @inheritDoc - */ -GeoJSON.prototype.readProjectionFromObject = function(object) { - const geoJSONObject = /** @type {GeoJSONObject} */ (object); - const crs = geoJSONObject.crs; - let projection; - if (crs) { - if (crs.type == 'name') { - projection = getProjection(crs.properties.name); - } else { - assert(false, 36); // Unknown SRS type - } - } else { - projection = this.dataProjection; - } - return ( - /** @type {module:ol/proj/Projection} */ (projection) - ); -}; - - /** * Encode a feature as a GeoJSON Feature string. * @@ -476,42 +543,6 @@ GeoJSON.prototype.readProjectionFromObject = function(object) { GeoJSON.prototype.writeFeature; -/** - * Encode a feature as a GeoJSON Feature object. - * - * @param {module:ol/Feature} feature Feature. - * @param {module:ol/format/Feature~WriteOptions=} opt_options Write options. - * @return {GeoJSONFeature} Object. - * @override - * @api - */ -GeoJSON.prototype.writeFeatureObject = function(feature, opt_options) { - opt_options = this.adaptOptions(opt_options); - - const object = /** @type {GeoJSONFeature} */ ({ - 'type': 'Feature' - }); - const id = feature.getId(); - if (id !== undefined) { - object.id = id; - } - const geometry = feature.getGeometry(); - if (geometry) { - object.geometry = writeGeometry(geometry, opt_options); - } else { - object.geometry = null; - } - const properties = feature.getProperties(); - delete properties[feature.getGeometryName()]; - if (!isEmpty(properties)) { - object.properties = properties; - } else { - object.properties = null; - } - return object; -}; - - /** * Encode an array of features as GeoJSON. * @@ -524,28 +555,6 @@ GeoJSON.prototype.writeFeatureObject = function(feature, opt_options) { GeoJSON.prototype.writeFeatures; -/** - * Encode an array of features as a GeoJSON object. - * - * @param {Array.} features Features. - * @param {module:ol/format/Feature~WriteOptions=} opt_options Write options. - * @return {GeoJSONFeatureCollection} GeoJSON Object. - * @override - * @api - */ -GeoJSON.prototype.writeFeaturesObject = function(features, opt_options) { - opt_options = this.adaptOptions(opt_options); - const objects = []; - for (let i = 0, ii = features.length; i < ii; ++i) { - objects.push(this.writeFeatureObject(features[i], opt_options)); - } - return /** @type {GeoJSONFeatureCollection} */ ({ - type: 'FeatureCollection', - features: objects - }); -}; - - /** * Encode a geometry as a GeoJSON string. * @@ -558,16 +567,4 @@ GeoJSON.prototype.writeFeaturesObject = function(features, opt_options) { GeoJSON.prototype.writeGeometry; -/** - * Encode a geometry as a GeoJSON object. - * - * @param {module:ol/geom/Geometry} geometry Geometry. - * @param {module:ol/format/Feature~WriteOptions=} opt_options Write options. - * @return {GeoJSONGeometry|GeoJSONGeometryCollection} Object. - * @override - * @api - */ -GeoJSON.prototype.writeGeometryObject = function(geometry, opt_options) { - return writeGeometry(geometry, this.adaptOptions(opt_options)); -}; export default GeoJSON; diff --git a/src/ol/format/IGC.js b/src/ol/format/IGC.js index 3730fce6ac..e67a08dc9e 100644 --- a/src/ol/format/IGC.js +++ b/src/ol/format/IGC.js @@ -36,23 +36,136 @@ const IGCZ = { * @param {module:ol/format/IGC~Options=} opt_options Options. * @api */ -const IGC = function(opt_options) { +class IGC { + constructor(opt_options) { - const options = opt_options ? opt_options : {}; + const options = opt_options ? opt_options : {}; - TextFeature.call(this); + TextFeature.call(this); + + /** + * @inheritDoc + */ + this.dataProjection = getProjection('EPSG:4326'); + + /** + * @private + * @type {IGCZ} + */ + this.altitudeMode_ = options.altitudeMode ? options.altitudeMode : IGCZ.NONE; + } /** * @inheritDoc */ - this.dataProjection = getProjection('EPSG:4326'); + readFeatureFromText(text, opt_options) { + const altitudeMode = this.altitudeMode_; + const lines = text.split(NEWLINE_RE); + /** @type {Object.} */ + const properties = {}; + const flatCoordinates = []; + let year = 2000; + let month = 0; + let day = 1; + let lastDateTime = -1; + let i, ii; + for (i = 0, ii = lines.length; i < ii; ++i) { + const line = lines[i]; + let m; + if (line.charAt(0) == 'B') { + m = B_RECORD_RE.exec(line); + if (m) { + const hour = parseInt(m[1], 10); + const minute = parseInt(m[2], 10); + const second = parseInt(m[3], 10); + let y = parseInt(m[4], 10) + parseInt(m[5], 10) / 60000; + if (m[6] == 'S') { + y = -y; + } + let x = parseInt(m[7], 10) + parseInt(m[8], 10) / 60000; + if (m[9] == 'W') { + x = -x; + } + flatCoordinates.push(x, y); + if (altitudeMode != IGCZ.NONE) { + let z; + if (altitudeMode == IGCZ.GPS) { + z = parseInt(m[11], 10); + } else if (altitudeMode == IGCZ.BAROMETRIC) { + z = parseInt(m[12], 10); + } else { + z = 0; + } + flatCoordinates.push(z); + } + let dateTime = Date.UTC(year, month, day, hour, minute, second); + // Detect UTC midnight wrap around. + if (dateTime < lastDateTime) { + dateTime = Date.UTC(year, month, day + 1, hour, minute, second); + } + flatCoordinates.push(dateTime / 1000); + lastDateTime = dateTime; + } + } else if (line.charAt(0) == 'H') { + m = HFDTE_RECORD_RE.exec(line); + if (m) { + day = parseInt(m[1], 10); + month = parseInt(m[2], 10) - 1; + year = 2000 + parseInt(m[3], 10); + } else { + m = H_RECORD_RE.exec(line); + if (m) { + properties[m[1]] = m[2].trim(); + } + } + } + } + if (flatCoordinates.length === 0) { + return null; + } + const layout = altitudeMode == IGCZ.NONE ? GeometryLayout.XYM : GeometryLayout.XYZM; + const lineString = new LineString(flatCoordinates, layout); + const feature = new Feature(transformWithOptions(lineString, false, opt_options)); + feature.setProperties(properties); + return feature; + } /** - * @private - * @type {IGCZ} + * @inheritDoc */ - this.altitudeMode_ = options.altitudeMode ? options.altitudeMode : IGCZ.NONE; -}; + readFeaturesFromText(text, opt_options) { + const feature = this.readFeatureFromText(text, opt_options); + if (feature) { + return [feature]; + } else { + return []; + } + } + + /** + * Not implemented. + * @inheritDoc + */ + writeFeatureText(feature, opt_options) {} + + /** + * Not implemented. + * @inheritDoc + */ + writeFeaturesText(features, opt_options) {} + + /** + * Not implemented. + * @inheritDoc + */ + writeGeometryText(geometry, opt_options) {} + + /** + * Not implemented. + * @inheritDoc + */ + readGeometryFromText(text, opt_options) {} +} inherits(IGC, TextFeature); @@ -100,82 +213,6 @@ const NEWLINE_RE = /\r\n|\r|\n/; IGC.prototype.readFeature; -/** - * @inheritDoc - */ -IGC.prototype.readFeatureFromText = function(text, opt_options) { - const altitudeMode = this.altitudeMode_; - const lines = text.split(NEWLINE_RE); - /** @type {Object.} */ - const properties = {}; - const flatCoordinates = []; - let year = 2000; - let month = 0; - let day = 1; - let lastDateTime = -1; - let i, ii; - for (i = 0, ii = lines.length; i < ii; ++i) { - const line = lines[i]; - let m; - if (line.charAt(0) == 'B') { - m = B_RECORD_RE.exec(line); - if (m) { - const hour = parseInt(m[1], 10); - const minute = parseInt(m[2], 10); - const second = parseInt(m[3], 10); - let y = parseInt(m[4], 10) + parseInt(m[5], 10) / 60000; - if (m[6] == 'S') { - y = -y; - } - let x = parseInt(m[7], 10) + parseInt(m[8], 10) / 60000; - if (m[9] == 'W') { - x = -x; - } - flatCoordinates.push(x, y); - if (altitudeMode != IGCZ.NONE) { - let z; - if (altitudeMode == IGCZ.GPS) { - z = parseInt(m[11], 10); - } else if (altitudeMode == IGCZ.BAROMETRIC) { - z = parseInt(m[12], 10); - } else { - z = 0; - } - flatCoordinates.push(z); - } - let dateTime = Date.UTC(year, month, day, hour, minute, second); - // Detect UTC midnight wrap around. - if (dateTime < lastDateTime) { - dateTime = Date.UTC(year, month, day + 1, hour, minute, second); - } - flatCoordinates.push(dateTime / 1000); - lastDateTime = dateTime; - } - } else if (line.charAt(0) == 'H') { - m = HFDTE_RECORD_RE.exec(line); - if (m) { - day = parseInt(m[1], 10); - month = parseInt(m[2], 10) - 1; - year = 2000 + parseInt(m[3], 10); - } else { - m = H_RECORD_RE.exec(line); - if (m) { - properties[m[1]] = m[2].trim(); - } - } - } - } - if (flatCoordinates.length === 0) { - return null; - } - const layout = altitudeMode == IGCZ.NONE ? GeometryLayout.XYM : GeometryLayout.XYZM; - const lineString = new LineString(flatCoordinates, layout); - const feature = new Feature(transformWithOptions(lineString, false, opt_options)); - feature.setProperties(properties); - return feature; -}; - - /** * Read the feature from the source. As IGC sources contain a single * feature, this will return the feature in an array. @@ -189,19 +226,6 @@ IGC.prototype.readFeatureFromText = function(text, opt_options) { IGC.prototype.readFeatures; -/** - * @inheritDoc - */ -IGC.prototype.readFeaturesFromText = function(text, opt_options) { - const feature = this.readFeatureFromText(text, opt_options); - if (feature) { - return [feature]; - } else { - return []; - } -}; - - /** * Read the projection from the IGC source. * @@ -213,30 +237,4 @@ IGC.prototype.readFeaturesFromText = function(text, opt_options) { IGC.prototype.readProjection; -/** - * Not implemented. - * @inheritDoc - */ -IGC.prototype.writeFeatureText = function(feature, opt_options) {}; - - -/** - * Not implemented. - * @inheritDoc - */ -IGC.prototype.writeFeaturesText = function(features, opt_options) {}; - - -/** - * Not implemented. - * @inheritDoc - */ -IGC.prototype.writeGeometryText = function(geometry, opt_options) {}; - - -/** - * Not implemented. - * @inheritDoc - */ -IGC.prototype.readGeometryFromText = function(text, opt_options) {}; export default IGC; diff --git a/src/ol/format/JSONFeature.js b/src/ol/format/JSONFeature.js index 35e1033969..5a55a3f44c 100644 --- a/src/ol/format/JSONFeature.js +++ b/src/ol/format/JSONFeature.js @@ -15,9 +15,129 @@ import FormatType from '../format/FormatType.js'; * @abstract * @extends {module:ol/format/Feature} */ -const JSONFeature = function() { - FeatureFormat.call(this); -}; +class JSONFeature { + constructor() { + FeatureFormat.call(this); + } + + /** + * @inheritDoc + */ + getType() { + return FormatType.JSON; + } + + /** + * @inheritDoc + */ + readFeature(source, opt_options) { + return this.readFeatureFromObject( + getObject(source), this.getReadOptions(source, opt_options)); + } + + /** + * @inheritDoc + */ + readFeatures(source, opt_options) { + return this.readFeaturesFromObject( + getObject(source), this.getReadOptions(source, opt_options)); + } + + /** + * @abstract + * @param {Object} object Object. + * @param {module:ol/format/Feature~ReadOptions=} opt_options Read options. + * @protected + * @return {module:ol/Feature} Feature. + */ + readFeatureFromObject(object, opt_options) {} + + /** + * @abstract + * @param {Object} object Object. + * @param {module:ol/format/Feature~ReadOptions=} opt_options Read options. + * @protected + * @return {Array.} Features. + */ + readFeaturesFromObject(object, opt_options) {} + + /** + * @inheritDoc + */ + readGeometry(source, opt_options) { + return this.readGeometryFromObject( + getObject(source), this.getReadOptions(source, opt_options)); + } + + /** + * @abstract + * @param {Object} object Object. + * @param {module:ol/format/Feature~ReadOptions=} opt_options Read options. + * @protected + * @return {module:ol/geom/Geometry} Geometry. + */ + readGeometryFromObject(object, opt_options) {} + + /** + * @inheritDoc + */ + readProjection(source) { + return this.readProjectionFromObject(getObject(source)); + } + + /** + * @abstract + * @param {Object} object Object. + * @protected + * @return {module:ol/proj/Projection} Projection. + */ + readProjectionFromObject(object) {} + + /** + * @inheritDoc + */ + writeFeature(feature, opt_options) { + return JSON.stringify(this.writeFeatureObject(feature, opt_options)); + } + + /** + * @abstract + * @param {module:ol/Feature} feature Feature. + * @param {module:ol/format/Feature~WriteOptions=} opt_options Write options. + * @return {Object} Object. + */ + writeFeatureObject(feature, opt_options) {} + + /** + * @inheritDoc + */ + writeFeatures(features, opt_options) { + return JSON.stringify(this.writeFeaturesObject(features, opt_options)); + } + + /** + * @abstract + * @param {Array.} features Features. + * @param {module:ol/format/Feature~WriteOptions=} opt_options Write options. + * @return {Object} Object. + */ + writeFeaturesObject(features, opt_options) {} + + /** + * @inheritDoc + */ + writeGeometry(geometry, opt_options) { + return JSON.stringify(this.writeGeometryObject(geometry, opt_options)); + } + + /** + * @abstract + * @param {module:ol/geom/Geometry} geometry Geometry. + * @param {module:ol/format/Feature~WriteOptions=} opt_options Write options. + * @return {Object} Object. + */ + writeGeometryObject(geometry, opt_options) {} +} inherits(JSONFeature, FeatureFormat); @@ -38,135 +158,4 @@ function getObject(source) { } -/** - * @inheritDoc - */ -JSONFeature.prototype.getType = function() { - return FormatType.JSON; -}; - - -/** - * @inheritDoc - */ -JSONFeature.prototype.readFeature = function(source, opt_options) { - return this.readFeatureFromObject( - getObject(source), this.getReadOptions(source, opt_options)); -}; - - -/** - * @inheritDoc - */ -JSONFeature.prototype.readFeatures = function(source, opt_options) { - return this.readFeaturesFromObject( - getObject(source), this.getReadOptions(source, opt_options)); -}; - - -/** - * @abstract - * @param {Object} object Object. - * @param {module:ol/format/Feature~ReadOptions=} opt_options Read options. - * @protected - * @return {module:ol/Feature} Feature. - */ -JSONFeature.prototype.readFeatureFromObject = function(object, opt_options) {}; - - -/** - * @abstract - * @param {Object} object Object. - * @param {module:ol/format/Feature~ReadOptions=} opt_options Read options. - * @protected - * @return {Array.} Features. - */ -JSONFeature.prototype.readFeaturesFromObject = function(object, opt_options) {}; - - -/** - * @inheritDoc - */ -JSONFeature.prototype.readGeometry = function(source, opt_options) { - return this.readGeometryFromObject( - getObject(source), this.getReadOptions(source, opt_options)); -}; - - -/** - * @abstract - * @param {Object} object Object. - * @param {module:ol/format/Feature~ReadOptions=} opt_options Read options. - * @protected - * @return {module:ol/geom/Geometry} Geometry. - */ -JSONFeature.prototype.readGeometryFromObject = function(object, opt_options) {}; - - -/** - * @inheritDoc - */ -JSONFeature.prototype.readProjection = function(source) { - return this.readProjectionFromObject(getObject(source)); -}; - - -/** - * @abstract - * @param {Object} object Object. - * @protected - * @return {module:ol/proj/Projection} Projection. - */ -JSONFeature.prototype.readProjectionFromObject = function(object) {}; - - -/** - * @inheritDoc - */ -JSONFeature.prototype.writeFeature = function(feature, opt_options) { - return JSON.stringify(this.writeFeatureObject(feature, opt_options)); -}; - - -/** - * @abstract - * @param {module:ol/Feature} feature Feature. - * @param {module:ol/format/Feature~WriteOptions=} opt_options Write options. - * @return {Object} Object. - */ -JSONFeature.prototype.writeFeatureObject = function(feature, opt_options) {}; - - -/** - * @inheritDoc - */ -JSONFeature.prototype.writeFeatures = function(features, opt_options) { - return JSON.stringify(this.writeFeaturesObject(features, opt_options)); -}; - - -/** - * @abstract - * @param {Array.} features Features. - * @param {module:ol/format/Feature~WriteOptions=} opt_options Write options. - * @return {Object} Object. - */ -JSONFeature.prototype.writeFeaturesObject = function(features, opt_options) {}; - - -/** - * @inheritDoc - */ -JSONFeature.prototype.writeGeometry = function(geometry, opt_options) { - return JSON.stringify(this.writeGeometryObject(geometry, opt_options)); -}; - - -/** - * @abstract - * @param {module:ol/geom/Geometry} geometry Geometry. - * @param {module:ol/format/Feature~WriteOptions=} opt_options Write options. - * @return {Object} Object. - */ -JSONFeature.prototype.writeGeometryObject = function(geometry, opt_options) {}; export default JSONFeature; diff --git a/src/ol/format/KML.js b/src/ol/format/KML.js index 215e93c280..55385b8419 100644 --- a/src/ol/format/KML.js +++ b/src/ol/format/KML.js @@ -259,56 +259,456 @@ function createStyleDefaults() { * @param {module:ol/format/KML~Options=} opt_options Options. * @api */ -const KML = function(opt_options) { +class KML { + constructor(opt_options) { - const options = opt_options ? opt_options : {}; + const options = opt_options ? opt_options : {}; - XMLFeature.call(this); + XMLFeature.call(this); - if (!DEFAULT_STYLE_ARRAY) { - createStyleDefaults(); + if (!DEFAULT_STYLE_ARRAY) { + createStyleDefaults(); + } + + /** + * @inheritDoc + */ + this.dataProjection = getProjection('EPSG:4326'); + + /** + * @private + * @type {Array.} + */ + this.defaultStyle_ = options.defaultStyle ? + options.defaultStyle : DEFAULT_STYLE_ARRAY; + + /** + * @private + * @type {boolean} + */ + this.extractStyles_ = options.extractStyles !== undefined ? + options.extractStyles : true; + + /** + * @private + * @type {boolean} + */ + this.writeStyles_ = options.writeStyles !== undefined ? + options.writeStyles : true; + + /** + * @private + * @type {!Object.|string)>} + */ + this.sharedStyles_ = {}; + + /** + * @private + * @type {boolean} + */ + this.showPointNames_ = options.showPointNames !== undefined ? + options.showPointNames : true; + + } + + /** + * @param {Node} node Node. + * @param {Array.<*>} objectStack Object stack. + * @private + * @return {Array.|undefined} Features. + */ + readDocumentOrFolder_(node, objectStack) { + // FIXME use scope somehow + const parsersNS = makeStructureNS( + NAMESPACE_URIS, { + 'Document': makeArrayExtender(this.readDocumentOrFolder_, this), + 'Folder': makeArrayExtender(this.readDocumentOrFolder_, this), + 'Placemark': makeArrayPusher(this.readPlacemark_, this), + 'Style': this.readSharedStyle_.bind(this), + 'StyleMap': this.readSharedStyleMap_.bind(this) + }); + /** @type {Array.} */ + const features = pushParseAndPop([], parsersNS, node, objectStack, this); + if (features) { + return features; + } else { + return undefined; + } + } + + /** + * @param {Node} node Node. + * @param {Array.<*>} objectStack Object stack. + * @private + * @return {module:ol/Feature|undefined} Feature. + */ + readPlacemark_(node, objectStack) { + const object = pushParseAndPop({'geometry': null}, + PLACEMARK_PARSERS, node, objectStack); + if (!object) { + return undefined; + } + const feature = new Feature(); + const id = node.getAttribute('id'); + if (id !== null) { + feature.setId(id); + } + const options = /** @type {module:ol/format/Feature~ReadOptions} */ (objectStack[0]); + + const geometry = object['geometry']; + if (geometry) { + transformWithOptions(geometry, false, options); + } + feature.setGeometry(geometry); + delete object['geometry']; + + if (this.extractStyles_) { + const style = object['Style']; + const styleUrl = object['styleUrl']; + const styleFunction = createFeatureStyleFunction( + style, styleUrl, this.defaultStyle_, this.sharedStyles_, + this.showPointNames_); + feature.setStyle(styleFunction); + } + delete object['Style']; + // we do not remove the styleUrl property from the object, so it + // gets stored on feature when setProperties is called + + feature.setProperties(object); + + return feature; + } + + /** + * @param {Node} node Node. + * @param {Array.<*>} objectStack Object stack. + * @private + */ + readSharedStyle_(node, objectStack) { + const id = node.getAttribute('id'); + if (id !== null) { + const style = readStyle(node, objectStack); + if (style) { + let styleUri; + let baseURI = node.baseURI; + if (!baseURI || baseURI == 'about:blank') { + baseURI = window.location.href; + } + if (baseURI) { + const url = new URL('#' + id, baseURI); + styleUri = url.href; + } else { + styleUri = '#' + id; + } + this.sharedStyles_[styleUri] = style; + } + } + } + + /** + * @param {Node} node Node. + * @param {Array.<*>} objectStack Object stack. + * @private + */ + readSharedStyleMap_(node, objectStack) { + const id = node.getAttribute('id'); + if (id === null) { + return; + } + const styleMapValue = readStyleMapValue(node, objectStack); + if (!styleMapValue) { + return; + } + let styleUri; + let baseURI = node.baseURI; + if (!baseURI || baseURI == 'about:blank') { + baseURI = window.location.href; + } + if (baseURI) { + const url = new URL('#' + id, baseURI); + styleUri = url.href; + } else { + styleUri = '#' + id; + } + this.sharedStyles_[styleUri] = styleMapValue; } /** * @inheritDoc */ - this.dataProjection = getProjection('EPSG:4326'); + readFeatureFromNode(node, opt_options) { + if (!includes(NAMESPACE_URIS, node.namespaceURI)) { + return null; + } + const feature = this.readPlacemark_( + node, [this.getReadOptions(node, opt_options)]); + if (feature) { + return feature; + } else { + return null; + } + } /** - * @private - * @type {Array.} + * @inheritDoc */ - this.defaultStyle_ = options.defaultStyle ? - options.defaultStyle : DEFAULT_STYLE_ARRAY; + readFeaturesFromNode(node, opt_options) { + if (!includes(NAMESPACE_URIS, node.namespaceURI)) { + return []; + } + let features; + const localName = node.localName; + if (localName == 'Document' || localName == 'Folder') { + features = this.readDocumentOrFolder_( + node, [this.getReadOptions(node, opt_options)]); + if (features) { + return features; + } else { + return []; + } + } else if (localName == 'Placemark') { + const feature = this.readPlacemark_( + node, [this.getReadOptions(node, opt_options)]); + if (feature) { + return [feature]; + } else { + return []; + } + } else if (localName == 'kml') { + features = []; + for (let n = node.firstElementChild; n; n = n.nextElementSibling) { + const fs = this.readFeaturesFromNode(n, opt_options); + if (fs) { + extend(features, fs); + } + } + return features; + } else { + return []; + } + } /** - * @private - * @type {boolean} + * Read the name of the KML. + * + * @param {Document|Node|string} source Source. + * @return {string|undefined} Name. + * @api */ - this.extractStyles_ = options.extractStyles !== undefined ? - options.extractStyles : true; + readName(source) { + if (isDocument(source)) { + return this.readNameFromDocument(/** @type {Document} */ (source)); + } else if (isNode(source)) { + return this.readNameFromNode(/** @type {Node} */ (source)); + } else if (typeof source === 'string') { + const doc = parse(source); + return this.readNameFromDocument(doc); + } else { + return undefined; + } + } /** - * @private - * @type {boolean} + * @param {Document} doc Document. + * @return {string|undefined} Name. */ - this.writeStyles_ = options.writeStyles !== undefined ? - options.writeStyles : true; + readNameFromDocument(doc) { + for (let n = doc.firstChild; n; n = n.nextSibling) { + if (n.nodeType == Node.ELEMENT_NODE) { + const name = this.readNameFromNode(n); + if (name) { + return name; + } + } + } + return undefined; + } /** - * @private - * @type {!Object.|string)>} + * @param {Node} node Node. + * @return {string|undefined} Name. */ - this.sharedStyles_ = {}; + readNameFromNode(node) { + for (let n = node.firstElementChild; n; n = n.nextElementSibling) { + if (includes(NAMESPACE_URIS, n.namespaceURI) && + n.localName == 'name') { + return readString(n); + } + } + for (let n = node.firstElementChild; n; n = n.nextElementSibling) { + const localName = n.localName; + if (includes(NAMESPACE_URIS, n.namespaceURI) && + (localName == 'Document' || + localName == 'Folder' || + localName == 'Placemark' || + localName == 'kml')) { + const name = this.readNameFromNode(n); + if (name) { + return name; + } + } + } + return undefined; + } /** - * @private - * @type {boolean} + * Read the network links of the KML. + * + * @param {Document|Node|string} source Source. + * @return {Array.} Network links. + * @api */ - this.showPointNames_ = options.showPointNames !== undefined ? - options.showPointNames : true; + readNetworkLinks(source) { + const networkLinks = []; + if (isDocument(source)) { + extend(networkLinks, this.readNetworkLinksFromDocument( + /** @type {Document} */ (source))); + } else if (isNode(source)) { + extend(networkLinks, this.readNetworkLinksFromNode( + /** @type {Node} */ (source))); + } else if (typeof source === 'string') { + const doc = parse(source); + extend(networkLinks, this.readNetworkLinksFromDocument(doc)); + } + return networkLinks; + } -}; + /** + * @param {Document} doc Document. + * @return {Array.} Network links. + */ + readNetworkLinksFromDocument(doc) { + const networkLinks = []; + for (let n = doc.firstChild; n; n = n.nextSibling) { + if (n.nodeType == Node.ELEMENT_NODE) { + extend(networkLinks, this.readNetworkLinksFromNode(n)); + } + } + return networkLinks; + } + + /** + * @param {Node} node Node. + * @return {Array.} Network links. + */ + readNetworkLinksFromNode(node) { + const networkLinks = []; + for (let n = node.firstElementChild; n; n = n.nextElementSibling) { + if (includes(NAMESPACE_URIS, n.namespaceURI) && + n.localName == 'NetworkLink') { + const obj = pushParseAndPop({}, NETWORK_LINK_PARSERS, + n, []); + networkLinks.push(obj); + } + } + for (let n = node.firstElementChild; n; n = n.nextElementSibling) { + const localName = n.localName; + if (includes(NAMESPACE_URIS, n.namespaceURI) && + (localName == 'Document' || + localName == 'Folder' || + localName == 'kml')) { + extend(networkLinks, this.readNetworkLinksFromNode(n)); + } + } + return networkLinks; + } + + /** + * Read the regions of the KML. + * + * @param {Document|Node|string} source Source. + * @return {Array.} Regions. + * @api + */ + readRegion(source) { + const regions = []; + if (isDocument(source)) { + extend(regions, this.readRegionFromDocument( + /** @type {Document} */ (source))); + } else if (isNode(source)) { + extend(regions, this.readRegionFromNode( + /** @type {Node} */ (source))); + } else if (typeof source === 'string') { + const doc = parse(source); + extend(regions, this.readRegionFromDocument(doc)); + } + return regions; + } + + /** + * @param {Document} doc Document. + * @return {Array.} Region. + */ + readRegionFromDocument(doc) { + const regions = []; + for (let n = doc.firstChild; n; n = n.nextSibling) { + if (n.nodeType == Node.ELEMENT_NODE) { + extend(regions, this.readRegionFromNode(n)); + } + } + return regions; + } + + /** + * @param {Node} node Node. + * @return {Array.} Region. + * @api + */ + readRegionFromNode(node) { + const regions = []; + for (let n = node.firstElementChild; n; n = n.nextElementSibling) { + if (includes(NAMESPACE_URIS, n.namespaceURI) && + n.localName == 'Region') { + const obj = pushParseAndPop({}, REGION_PARSERS, + n, []); + regions.push(obj); + } + } + for (let n = node.firstElementChild; n; n = n.nextElementSibling) { + const localName = n.localName; + if (includes(NAMESPACE_URIS, n.namespaceURI) && + (localName == 'Document' || + localName == 'Folder' || + localName == 'kml')) { + extend(regions, this.readRegionFromNode(n)); + } + } + return regions; + } + + /** + * Encode an array of features in the KML format as an XML node. GeometryCollections, + * MultiPoints, MultiLineStrings, and MultiPolygons are output as MultiGeometries. + * + * @param {Array.} features Features. + * @param {module:ol/format/Feature~WriteOptions=} opt_options Options. + * @return {Node} Node. + * @override + * @api + */ + writeFeaturesNode(features, opt_options) { + opt_options = this.adaptOptions(opt_options); + const kml = createElementNS(NAMESPACE_URIS[4], 'kml'); + const xmlnsUri = 'http://www.w3.org/2000/xmlns/'; + kml.setAttributeNS(xmlnsUri, 'xmlns:gx', GX_NAMESPACE_URIS[0]); + kml.setAttributeNS(xmlnsUri, 'xmlns:xsi', XML_SCHEMA_INSTANCE_URI); + kml.setAttributeNS(XML_SCHEMA_INSTANCE_URI, 'xsi:schemaLocation', SCHEMA_LOCATION); + + const /** @type {module:ol/xml~NodeStackItem} */ context = {node: kml}; + const properties = {}; + if (features.length > 1) { + properties['Document'] = features; + } else if (features.length == 1) { + properties['Placemark'] = features[0]; + } + const orderedKeys = KML_SEQUENCE[kml.namespaceURI]; + const values = makeSequence(properties, orderedKeys); + pushSerializeAndPop(context, KML_SERIALIZERS, + OBJECT_PROPERTY_NODE_FACTORY, values, [opt_options], orderedKeys, + this); + return kml; + } +} inherits(KML, XMLFeature); @@ -1643,132 +2043,6 @@ const PLACEMARK_PARSERS = makeStructureNS( )); -/** - * @param {Node} node Node. - * @param {Array.<*>} objectStack Object stack. - * @private - * @return {Array.|undefined} Features. - */ -KML.prototype.readDocumentOrFolder_ = function(node, objectStack) { - // FIXME use scope somehow - const parsersNS = makeStructureNS( - NAMESPACE_URIS, { - 'Document': makeArrayExtender(this.readDocumentOrFolder_, this), - 'Folder': makeArrayExtender(this.readDocumentOrFolder_, this), - 'Placemark': makeArrayPusher(this.readPlacemark_, this), - 'Style': this.readSharedStyle_.bind(this), - 'StyleMap': this.readSharedStyleMap_.bind(this) - }); - /** @type {Array.} */ - const features = pushParseAndPop([], parsersNS, node, objectStack, this); - if (features) { - return features; - } else { - return undefined; - } -}; - - -/** - * @param {Node} node Node. - * @param {Array.<*>} objectStack Object stack. - * @private - * @return {module:ol/Feature|undefined} Feature. - */ -KML.prototype.readPlacemark_ = function(node, objectStack) { - const object = pushParseAndPop({'geometry': null}, - PLACEMARK_PARSERS, node, objectStack); - if (!object) { - return undefined; - } - const feature = new Feature(); - const id = node.getAttribute('id'); - if (id !== null) { - feature.setId(id); - } - const options = /** @type {module:ol/format/Feature~ReadOptions} */ (objectStack[0]); - - const geometry = object['geometry']; - if (geometry) { - transformWithOptions(geometry, false, options); - } - feature.setGeometry(geometry); - delete object['geometry']; - - if (this.extractStyles_) { - const style = object['Style']; - const styleUrl = object['styleUrl']; - const styleFunction = createFeatureStyleFunction( - style, styleUrl, this.defaultStyle_, this.sharedStyles_, - this.showPointNames_); - feature.setStyle(styleFunction); - } - delete object['Style']; - // we do not remove the styleUrl property from the object, so it - // gets stored on feature when setProperties is called - - feature.setProperties(object); - - return feature; -}; - - -/** - * @param {Node} node Node. - * @param {Array.<*>} objectStack Object stack. - * @private - */ -KML.prototype.readSharedStyle_ = function(node, objectStack) { - const id = node.getAttribute('id'); - if (id !== null) { - const style = readStyle(node, objectStack); - if (style) { - let styleUri; - let baseURI = node.baseURI; - if (!baseURI || baseURI == 'about:blank') { - baseURI = window.location.href; - } - if (baseURI) { - const url = new URL('#' + id, baseURI); - styleUri = url.href; - } else { - styleUri = '#' + id; - } - this.sharedStyles_[styleUri] = style; - } - } -}; - - -/** - * @param {Node} node Node. - * @param {Array.<*>} objectStack Object stack. - * @private - */ -KML.prototype.readSharedStyleMap_ = function(node, objectStack) { - const id = node.getAttribute('id'); - if (id === null) { - return; - } - const styleMapValue = readStyleMapValue(node, objectStack); - if (!styleMapValue) { - return; - } - let styleUri; - let baseURI = node.baseURI; - if (!baseURI || baseURI == 'about:blank') { - baseURI = window.location.href; - } - if (baseURI) { - const url = new URL('#' + id, baseURI); - styleUri = url.href; - } else { - styleUri = '#' + id; - } - this.sharedStyles_[styleUri] = styleMapValue; -}; - - /** * Read the first feature from a KML source. MultiGeometries are converted into * GeometryCollections if they are a mix of geometry types, and into MultiPoint/ @@ -1783,23 +2057,6 @@ KML.prototype.readSharedStyleMap_ = function(node, objectStack) { KML.prototype.readFeature; -/** - * @inheritDoc - */ -KML.prototype.readFeatureFromNode = function(node, opt_options) { - if (!includes(NAMESPACE_URIS, node.namespaceURI)) { - return null; - } - const feature = this.readPlacemark_( - node, [this.getReadOptions(node, opt_options)]); - if (feature) { - return feature; - } else { - return null; - } -}; - - /** * Read all features from a KML source. MultiGeometries are converted into * GeometryCollections if they are a mix of geometry types, and into MultiPoint/ @@ -1814,243 +2071,6 @@ KML.prototype.readFeatureFromNode = function(node, opt_options) { KML.prototype.readFeatures; -/** - * @inheritDoc - */ -KML.prototype.readFeaturesFromNode = function(node, opt_options) { - if (!includes(NAMESPACE_URIS, node.namespaceURI)) { - return []; - } - let features; - const localName = node.localName; - if (localName == 'Document' || localName == 'Folder') { - features = this.readDocumentOrFolder_( - node, [this.getReadOptions(node, opt_options)]); - if (features) { - return features; - } else { - return []; - } - } else if (localName == 'Placemark') { - const feature = this.readPlacemark_( - node, [this.getReadOptions(node, opt_options)]); - if (feature) { - return [feature]; - } else { - return []; - } - } else if (localName == 'kml') { - features = []; - for (let n = node.firstElementChild; n; n = n.nextElementSibling) { - const fs = this.readFeaturesFromNode(n, opt_options); - if (fs) { - extend(features, fs); - } - } - return features; - } else { - return []; - } -}; - - -/** - * Read the name of the KML. - * - * @param {Document|Node|string} source Source. - * @return {string|undefined} Name. - * @api - */ -KML.prototype.readName = function(source) { - if (isDocument(source)) { - return this.readNameFromDocument(/** @type {Document} */ (source)); - } else if (isNode(source)) { - return this.readNameFromNode(/** @type {Node} */ (source)); - } else if (typeof source === 'string') { - const doc = parse(source); - return this.readNameFromDocument(doc); - } else { - return undefined; - } -}; - - -/** - * @param {Document} doc Document. - * @return {string|undefined} Name. - */ -KML.prototype.readNameFromDocument = function(doc) { - for (let n = doc.firstChild; n; n = n.nextSibling) { - if (n.nodeType == Node.ELEMENT_NODE) { - const name = this.readNameFromNode(n); - if (name) { - return name; - } - } - } - return undefined; -}; - - -/** - * @param {Node} node Node. - * @return {string|undefined} Name. - */ -KML.prototype.readNameFromNode = function(node) { - for (let n = node.firstElementChild; n; n = n.nextElementSibling) { - if (includes(NAMESPACE_URIS, n.namespaceURI) && - n.localName == 'name') { - return readString(n); - } - } - for (let n = node.firstElementChild; n; n = n.nextElementSibling) { - const localName = n.localName; - if (includes(NAMESPACE_URIS, n.namespaceURI) && - (localName == 'Document' || - localName == 'Folder' || - localName == 'Placemark' || - localName == 'kml')) { - const name = this.readNameFromNode(n); - if (name) { - return name; - } - } - } - return undefined; -}; - - -/** - * Read the network links of the KML. - * - * @param {Document|Node|string} source Source. - * @return {Array.} Network links. - * @api - */ -KML.prototype.readNetworkLinks = function(source) { - const networkLinks = []; - if (isDocument(source)) { - extend(networkLinks, this.readNetworkLinksFromDocument( - /** @type {Document} */ (source))); - } else if (isNode(source)) { - extend(networkLinks, this.readNetworkLinksFromNode( - /** @type {Node} */ (source))); - } else if (typeof source === 'string') { - const doc = parse(source); - extend(networkLinks, this.readNetworkLinksFromDocument(doc)); - } - return networkLinks; -}; - - -/** - * @param {Document} doc Document. - * @return {Array.} Network links. - */ -KML.prototype.readNetworkLinksFromDocument = function(doc) { - const networkLinks = []; - for (let n = doc.firstChild; n; n = n.nextSibling) { - if (n.nodeType == Node.ELEMENT_NODE) { - extend(networkLinks, this.readNetworkLinksFromNode(n)); - } - } - return networkLinks; -}; - - -/** - * @param {Node} node Node. - * @return {Array.} Network links. - */ -KML.prototype.readNetworkLinksFromNode = function(node) { - const networkLinks = []; - for (let n = node.firstElementChild; n; n = n.nextElementSibling) { - if (includes(NAMESPACE_URIS, n.namespaceURI) && - n.localName == 'NetworkLink') { - const obj = pushParseAndPop({}, NETWORK_LINK_PARSERS, - n, []); - networkLinks.push(obj); - } - } - for (let n = node.firstElementChild; n; n = n.nextElementSibling) { - const localName = n.localName; - if (includes(NAMESPACE_URIS, n.namespaceURI) && - (localName == 'Document' || - localName == 'Folder' || - localName == 'kml')) { - extend(networkLinks, this.readNetworkLinksFromNode(n)); - } - } - return networkLinks; -}; - - -/** - * Read the regions of the KML. - * - * @param {Document|Node|string} source Source. - * @return {Array.} Regions. - * @api - */ -KML.prototype.readRegion = function(source) { - const regions = []; - if (isDocument(source)) { - extend(regions, this.readRegionFromDocument( - /** @type {Document} */ (source))); - } else if (isNode(source)) { - extend(regions, this.readRegionFromNode( - /** @type {Node} */ (source))); - } else if (typeof source === 'string') { - const doc = parse(source); - extend(regions, this.readRegionFromDocument(doc)); - } - return regions; -}; - - -/** - * @param {Document} doc Document. - * @return {Array.} Region. - */ -KML.prototype.readRegionFromDocument = function(doc) { - const regions = []; - for (let n = doc.firstChild; n; n = n.nextSibling) { - if (n.nodeType == Node.ELEMENT_NODE) { - extend(regions, this.readRegionFromNode(n)); - } - } - return regions; -}; - - -/** - * @param {Node} node Node. - * @return {Array.} Region. - * @api - */ -KML.prototype.readRegionFromNode = function(node) { - const regions = []; - for (let n = node.firstElementChild; n; n = n.nextElementSibling) { - if (includes(NAMESPACE_URIS, n.namespaceURI) && - n.localName == 'Region') { - const obj = pushParseAndPop({}, REGION_PARSERS, - n, []); - regions.push(obj); - } - } - for (let n = node.firstElementChild; n; n = n.nextElementSibling) { - const localName = n.localName; - if (includes(NAMESPACE_URIS, n.namespaceURI) && - (localName == 'Document' || - localName == 'Folder' || - localName == 'kml')) { - extend(regions, this.readRegionFromNode(n)); - } - } - return regions; -}; - - /** * Read the projection from a KML source. * @@ -2955,37 +2975,4 @@ const KML_SERIALIZERS = makeStructureNS( KML.prototype.writeFeatures; -/** - * Encode an array of features in the KML format as an XML node. GeometryCollections, - * MultiPoints, MultiLineStrings, and MultiPolygons are output as MultiGeometries. - * - * @param {Array.} features Features. - * @param {module:ol/format/Feature~WriteOptions=} opt_options Options. - * @return {Node} Node. - * @override - * @api - */ -KML.prototype.writeFeaturesNode = function(features, opt_options) { - opt_options = this.adaptOptions(opt_options); - const kml = createElementNS(NAMESPACE_URIS[4], 'kml'); - const xmlnsUri = 'http://www.w3.org/2000/xmlns/'; - kml.setAttributeNS(xmlnsUri, 'xmlns:gx', GX_NAMESPACE_URIS[0]); - kml.setAttributeNS(xmlnsUri, 'xmlns:xsi', XML_SCHEMA_INSTANCE_URI); - kml.setAttributeNS(XML_SCHEMA_INSTANCE_URI, 'xsi:schemaLocation', SCHEMA_LOCATION); - - const /** @type {module:ol/xml~NodeStackItem} */ context = {node: kml}; - const properties = {}; - if (features.length > 1) { - properties['Document'] = features; - } else if (features.length == 1) { - properties['Placemark'] = features[0]; - } - const orderedKeys = KML_SEQUENCE[kml.namespaceURI]; - const values = makeSequence(properties, orderedKeys); - pushSerializeAndPop(context, KML_SERIALIZERS, - OBJECT_PROPERTY_NODE_FACTORY, values, [opt_options], orderedKeys, - this); - return kml; -}; - export default KML; diff --git a/src/ol/format/MVT.js b/src/ol/format/MVT.js index 618b5aea81..2e09508cbc 100644 --- a/src/ol/format/MVT.js +++ b/src/ol/format/MVT.js @@ -47,54 +47,276 @@ import RenderFeature from '../render/Feature.js'; * @param {module:ol/format/MVT~Options=} opt_options Options. * @api */ -const MVT = function(opt_options) { +class MVT { + constructor(opt_options) { - FeatureFormat.call(this); + FeatureFormat.call(this); - const options = opt_options ? opt_options : {}; + const options = opt_options ? opt_options : {}; + + /** + * @type {module:ol/proj/Projection} + */ + this.dataProjection = new Projection({ + code: '', + units: Units.TILE_PIXELS + }); + + /** + * @private + * @type {function((module:ol/geom/Geometry|Object.)=)| + * function(module:ol/geom/GeometryType,Array., + * (Array.|Array.>),Object.,number)} + */ + this.featureClass_ = options.featureClass ? + options.featureClass : RenderFeature; + + /** + * @private + * @type {string|undefined} + */ + this.geometryName_ = options.geometryName; + + /** + * @private + * @type {string} + */ + this.layerName_ = options.layerName ? options.layerName : 'layer'; + + /** + * @private + * @type {Array.} + */ + this.layers_ = options.layers ? options.layers : null; + + /** + * @private + * @type {module:ol/extent~Extent} + */ + this.extent_ = null; + + } /** - * @type {module:ol/proj/Projection} + * Read the raw geometry from the pbf offset stored in a raw feature's geometry + * property. + * @suppress {missingProperties} + * @param {Object} pbf PBF. + * @param {Object} feature Raw feature. + * @param {Array.} flatCoordinates Array to store flat coordinates in. + * @param {Array.} ends Array to store ends in. + * @private */ - this.dataProjection = new Projection({ - code: '', - units: Units.TILE_PIXELS - }); + readRawGeometry_(pbf, feature, flatCoordinates, ends) { + pbf.pos = feature.geometry; + + const end = pbf.readVarint() + pbf.pos; + let cmd = 1; + let length = 0; + let x = 0; + let y = 0; + let coordsLen = 0; + let currentEnd = 0; + + while (pbf.pos < end) { + if (!length) { + const cmdLen = pbf.readVarint(); + cmd = cmdLen & 0x7; + length = cmdLen >> 3; + } + + length--; + + if (cmd === 1 || cmd === 2) { + x += pbf.readSVarint(); + y += pbf.readSVarint(); + + if (cmd === 1) { // moveTo + if (coordsLen > currentEnd) { + ends.push(coordsLen); + currentEnd = coordsLen; + } + } + + flatCoordinates.push(x, y); + coordsLen += 2; + + } else if (cmd === 7) { + + if (coordsLen > currentEnd) { + // close polygon + flatCoordinates.push( + flatCoordinates[currentEnd], flatCoordinates[currentEnd + 1]); + coordsLen += 2; + } + + } else { + assert(false, 59); // Invalid command found in the PBF + } + } + + if (coordsLen > currentEnd) { + ends.push(coordsLen); + currentEnd = coordsLen; + } + + } /** * @private - * @type {function((module:ol/geom/Geometry|Object.)=)| - * function(module:ol/geom/GeometryType,Array., - * (Array.|Array.>),Object.,number)} + * @param {Object} pbf PBF + * @param {Object} rawFeature Raw Mapbox feature. + * @param {module:ol/format/Feature~ReadOptions=} opt_options Read options. + * @return {module:ol/Feature|module:ol/render/Feature} Feature. */ - this.featureClass_ = options.featureClass ? - options.featureClass : RenderFeature; + createFeature_(pbf, rawFeature, opt_options) { + const type = rawFeature.type; + if (type === 0) { + return null; + } + + let feature; + const id = rawFeature.id; + const values = rawFeature.properties; + values[this.layerName_] = rawFeature.layer.name; + + const flatCoordinates = []; + const ends = []; + this.readRawGeometry_(pbf, rawFeature, flatCoordinates, ends); + + const geometryType = getGeometryType(type, ends.length); + + if (this.featureClass_ === RenderFeature) { + feature = new this.featureClass_(geometryType, flatCoordinates, ends, values, id); + } else { + let geom; + if (geometryType == GeometryType.POLYGON) { + const endss = []; + let offset = 0; + let prevEndIndex = 0; + for (let i = 0, ii = ends.length; i < ii; ++i) { + const end = ends[i]; + if (!linearRingIsClockwise(flatCoordinates, offset, end, 2)) { + endss.push(ends.slice(prevEndIndex, i)); + prevEndIndex = i; + } + offset = end; + } + if (endss.length > 1) { + geom = new MultiPolygon(flatCoordinates, GeometryLayout.XY, endss); + } else { + geom = new Polygon(flatCoordinates, GeometryLayout.XY, ends); + } + } else { + geom = geometryType === GeometryType.POINT ? new Point(flatCoordinates, GeometryLayout.XY) : + geometryType === GeometryType.LINE_STRING ? new LineString(flatCoordinates, GeometryLayout.XY) : + geometryType === GeometryType.POLYGON ? new Polygon(flatCoordinates, GeometryLayout.XY, ends) : + geometryType === GeometryType.MULTI_POINT ? new MultiPoint(flatCoordinates, GeometryLayout.XY) : + geometryType === GeometryType.MULTI_LINE_STRING ? new MultiLineString(flatCoordinates, GeometryLayout.XY, ends) : + null; + } + feature = new this.featureClass_(); + if (this.geometryName_) { + feature.setGeometryName(this.geometryName_); + } + const geometry = transformWithOptions(geom, false, this.adaptOptions(opt_options)); + feature.setGeometry(geometry); + feature.setId(id); + feature.setProperties(values); + } + + return feature; + } /** - * @private - * @type {string|undefined} + * @inheritDoc + * @api */ - this.geometryName_ = options.geometryName; + getLastExtent() { + return this.extent_; + } /** - * @private - * @type {string} + * @inheritDoc */ - this.layerName_ = options.layerName ? options.layerName : 'layer'; + getType() { + return FormatType.ARRAY_BUFFER; + } /** - * @private - * @type {Array.} + * @inheritDoc + * @api */ - this.layers_ = options.layers ? options.layers : null; + readFeatures(source, opt_options) { + const layers = this.layers_; + + const pbf = new PBF(/** @type {ArrayBuffer} */ (source)); + const pbfLayers = pbf.readFields(layersPBFReader, {}); + /** @type {Array.} */ + const features = []; + for (const name in pbfLayers) { + if (layers && layers.indexOf(name) == -1) { + continue; + } + const pbfLayer = pbfLayers[name]; + + for (let i = 0, ii = pbfLayer.length; i < ii; ++i) { + const rawFeature = readRawFeature(pbf, pbfLayer, i); + features.push(this.createFeature_(pbf, rawFeature)); + } + this.extent_ = pbfLayer ? [0, 0, pbfLayer.extent, pbfLayer.extent] : null; + } + + return features; + } /** - * @private - * @type {module:ol/extent~Extent} + * @inheritDoc + * @api */ - this.extent_ = null; + readProjection(source) { + return this.dataProjection; + } -}; + /** + * Sets the layers that features will be read from. + * @param {Array.} layers Layers. + * @api + */ + setLayers(layers) { + this.layers_ = layers; + } + + /** + * Not implemented. + * @override + */ + readFeature() {} + + /** + * Not implemented. + * @override + */ + readGeometry() {} + + /** + * Not implemented. + * @override + */ + writeFeature() {} + + /** + * Not implemented. + * @override + */ + writeGeometry() {} + + /** + * Not implemented. + * @override + */ + writeFeatures() {} +} inherits(MVT, FeatureFormat); @@ -201,72 +423,6 @@ function readRawFeature(pbf, layer, i) { } -/** - * Read the raw geometry from the pbf offset stored in a raw feature's geometry - * property. - * @suppress {missingProperties} - * @param {Object} pbf PBF. - * @param {Object} feature Raw feature. - * @param {Array.} flatCoordinates Array to store flat coordinates in. - * @param {Array.} ends Array to store ends in. - * @private - */ -MVT.prototype.readRawGeometry_ = function(pbf, feature, flatCoordinates, ends) { - pbf.pos = feature.geometry; - - const end = pbf.readVarint() + pbf.pos; - let cmd = 1; - let length = 0; - let x = 0; - let y = 0; - let coordsLen = 0; - let currentEnd = 0; - - while (pbf.pos < end) { - if (!length) { - const cmdLen = pbf.readVarint(); - cmd = cmdLen & 0x7; - length = cmdLen >> 3; - } - - length--; - - if (cmd === 1 || cmd === 2) { - x += pbf.readSVarint(); - y += pbf.readSVarint(); - - if (cmd === 1) { // moveTo - if (coordsLen > currentEnd) { - ends.push(coordsLen); - currentEnd = coordsLen; - } - } - - flatCoordinates.push(x, y); - coordsLen += 2; - - } else if (cmd === 7) { - - if (coordsLen > currentEnd) { - // close polygon - flatCoordinates.push( - flatCoordinates[currentEnd], flatCoordinates[currentEnd + 1]); - coordsLen += 2; - } - - } else { - assert(false, 59); // Invalid command found in the PBF - } - } - - if (coordsLen > currentEnd) { - ends.push(coordsLen); - currentEnd = coordsLen; - } - -}; - - /** * @suppress {missingProperties} * @param {number} type The raw feature's geometry type @@ -292,168 +448,4 @@ function getGeometryType(type, numEnds) { return geometryType; } -/** - * @private - * @param {Object} pbf PBF - * @param {Object} rawFeature Raw Mapbox feature. - * @param {module:ol/format/Feature~ReadOptions=} opt_options Read options. - * @return {module:ol/Feature|module:ol/render/Feature} Feature. - */ -MVT.prototype.createFeature_ = function(pbf, rawFeature, opt_options) { - const type = rawFeature.type; - if (type === 0) { - return null; - } - - let feature; - const id = rawFeature.id; - const values = rawFeature.properties; - values[this.layerName_] = rawFeature.layer.name; - - const flatCoordinates = []; - const ends = []; - this.readRawGeometry_(pbf, rawFeature, flatCoordinates, ends); - - const geometryType = getGeometryType(type, ends.length); - - if (this.featureClass_ === RenderFeature) { - feature = new this.featureClass_(geometryType, flatCoordinates, ends, values, id); - } else { - let geom; - if (geometryType == GeometryType.POLYGON) { - const endss = []; - let offset = 0; - let prevEndIndex = 0; - for (let i = 0, ii = ends.length; i < ii; ++i) { - const end = ends[i]; - if (!linearRingIsClockwise(flatCoordinates, offset, end, 2)) { - endss.push(ends.slice(prevEndIndex, i)); - prevEndIndex = i; - } - offset = end; - } - if (endss.length > 1) { - geom = new MultiPolygon(flatCoordinates, GeometryLayout.XY, endss); - } else { - geom = new Polygon(flatCoordinates, GeometryLayout.XY, ends); - } - } else { - geom = geometryType === GeometryType.POINT ? new Point(flatCoordinates, GeometryLayout.XY) : - geometryType === GeometryType.LINE_STRING ? new LineString(flatCoordinates, GeometryLayout.XY) : - geometryType === GeometryType.POLYGON ? new Polygon(flatCoordinates, GeometryLayout.XY, ends) : - geometryType === GeometryType.MULTI_POINT ? new MultiPoint(flatCoordinates, GeometryLayout.XY) : - geometryType === GeometryType.MULTI_LINE_STRING ? new MultiLineString(flatCoordinates, GeometryLayout.XY, ends) : - null; - } - feature = new this.featureClass_(); - if (this.geometryName_) { - feature.setGeometryName(this.geometryName_); - } - const geometry = transformWithOptions(geom, false, this.adaptOptions(opt_options)); - feature.setGeometry(geometry); - feature.setId(id); - feature.setProperties(values); - } - - return feature; -}; - - -/** - * @inheritDoc - * @api - */ -MVT.prototype.getLastExtent = function() { - return this.extent_; -}; - - -/** - * @inheritDoc - */ -MVT.prototype.getType = function() { - return FormatType.ARRAY_BUFFER; -}; - - -/** - * @inheritDoc - * @api - */ -MVT.prototype.readFeatures = function(source, opt_options) { - const layers = this.layers_; - - const pbf = new PBF(/** @type {ArrayBuffer} */ (source)); - const pbfLayers = pbf.readFields(layersPBFReader, {}); - /** @type {Array.} */ - const features = []; - for (const name in pbfLayers) { - if (layers && layers.indexOf(name) == -1) { - continue; - } - const pbfLayer = pbfLayers[name]; - - for (let i = 0, ii = pbfLayer.length; i < ii; ++i) { - const rawFeature = readRawFeature(pbf, pbfLayer, i); - features.push(this.createFeature_(pbf, rawFeature)); - } - this.extent_ = pbfLayer ? [0, 0, pbfLayer.extent, pbfLayer.extent] : null; - } - - return features; -}; - - -/** - * @inheritDoc - * @api - */ -MVT.prototype.readProjection = function(source) { - return this.dataProjection; -}; - - -/** - * Sets the layers that features will be read from. - * @param {Array.} layers Layers. - * @api - */ -MVT.prototype.setLayers = function(layers) { - this.layers_ = layers; -}; - - -/** - * Not implemented. - * @override - */ -MVT.prototype.readFeature = function() {}; - - -/** - * Not implemented. - * @override - */ -MVT.prototype.readGeometry = function() {}; - - -/** - * Not implemented. - * @override - */ -MVT.prototype.writeFeature = function() {}; - - -/** - * Not implemented. - * @override - */ -MVT.prototype.writeGeometry = function() {}; - - -/** - * Not implemented. - * @override - */ -MVT.prototype.writeFeatures = function() {}; export default MVT; diff --git a/src/ol/format/OSMXML.js b/src/ol/format/OSMXML.js index cb43d9ca99..57cb4d88cb 100644 --- a/src/ol/format/OSMXML.js +++ b/src/ol/format/OSMXML.js @@ -24,14 +24,74 @@ import {pushParseAndPop, makeStructureNS} from '../xml.js'; * @extends {module:ol/format/XMLFeature} * @api */ -const OSMXML = function() { - XMLFeature.call(this); +class OSMXML { + constructor() { + XMLFeature.call(this); + + /** + * @inheritDoc + */ + this.dataProjection = getProjection('EPSG:4326'); + } /** * @inheritDoc */ - this.dataProjection = getProjection('EPSG:4326'); -}; + readFeaturesFromNode(node, opt_options) { + const options = this.getReadOptions(node, opt_options); + if (node.localName == 'osm') { + const state = pushParseAndPop({ + nodes: {}, + ways: [], + features: [] + }, PARSERS, node, [options]); + // parse nodes in ways + for (let j = 0; j < state.ways.length; j++) { + const values = /** @type {Object} */ (state.ways[j]); + /** @type {Array.} */ + const flatCoordinates = []; + for (let i = 0, ii = values.ndrefs.length; i < ii; i++) { + const point = state.nodes[values.ndrefs[i]]; + extend(flatCoordinates, point); + } + let geometry; + if (values.ndrefs[0] == values.ndrefs[values.ndrefs.length - 1]) { + // closed way + geometry = new Polygon(flatCoordinates, GeometryLayout.XY, [flatCoordinates.length]); + } else { + geometry = new LineString(flatCoordinates, GeometryLayout.XY); + } + transformWithOptions(geometry, false, options); + const feature = new Feature(geometry); + feature.setId(values.id); + feature.setProperties(values.tags); + state.features.push(feature); + } + if (state.features) { + return state.features; + } + } + return []; + } + + /** + * Not implemented. + * @inheritDoc + */ + writeFeatureNode(feature, opt_options) {} + + /** + * Not implemented. + * @inheritDoc + */ + writeFeaturesNode(features, opt_options) {} + + /** + * Not implemented. + * @inheritDoc + */ + writeGeometryNode(geometry, opt_options) {} +} inherits(OSMXML, XMLFeature); @@ -152,47 +212,6 @@ function readTag(node, objectStack) { OSMXML.prototype.readFeatures; -/** - * @inheritDoc - */ -OSMXML.prototype.readFeaturesFromNode = function(node, opt_options) { - const options = this.getReadOptions(node, opt_options); - if (node.localName == 'osm') { - const state = pushParseAndPop({ - nodes: {}, - ways: [], - features: [] - }, PARSERS, node, [options]); - // parse nodes in ways - for (let j = 0; j < state.ways.length; j++) { - const values = /** @type {Object} */ (state.ways[j]); - /** @type {Array.} */ - const flatCoordinates = []; - for (let i = 0, ii = values.ndrefs.length; i < ii; i++) { - const point = state.nodes[values.ndrefs[i]]; - extend(flatCoordinates, point); - } - let geometry; - if (values.ndrefs[0] == values.ndrefs[values.ndrefs.length - 1]) { - // closed way - geometry = new Polygon(flatCoordinates, GeometryLayout.XY, [flatCoordinates.length]); - } else { - geometry = new LineString(flatCoordinates, GeometryLayout.XY); - } - transformWithOptions(geometry, false, options); - const feature = new Feature(geometry); - feature.setId(values.id); - feature.setProperties(values.tags); - state.features.push(feature); - } - if (state.features) { - return state.features; - } - } - return []; -}; - - /** * Read the projection from an OSM source. * @@ -204,23 +223,4 @@ OSMXML.prototype.readFeaturesFromNode = function(node, opt_options) { OSMXML.prototype.readProjection; -/** - * Not implemented. - * @inheritDoc - */ -OSMXML.prototype.writeFeatureNode = function(feature, opt_options) {}; - - -/** - * Not implemented. - * @inheritDoc - */ -OSMXML.prototype.writeFeaturesNode = function(features, opt_options) {}; - - -/** - * Not implemented. - * @inheritDoc - */ -OSMXML.prototype.writeGeometryNode = function(geometry, opt_options) {}; export default OSMXML; diff --git a/src/ol/format/OWS.js b/src/ol/format/OWS.js index d78b893fdc..f92b7b6559 100644 --- a/src/ol/format/OWS.js +++ b/src/ol/format/OWS.js @@ -11,9 +11,32 @@ import {makeObjectPropertyPusher, makeObjectPropertySetter, makeStructureNS, pus * @constructor * @extends {module:ol/format/XML} */ -const OWS = function() { - XML.call(this); -}; +class OWS { + constructor() { + XML.call(this); + } + + /** + * @inheritDoc + */ + readFromDocument(doc) { + for (let n = doc.firstChild; n; n = n.nextSibling) { + if (n.nodeType == Node.ELEMENT_NODE) { + return this.readFromNode(n); + } + } + return null; + } + + /** + * @inheritDoc + */ + readFromNode(node) { + const owsObject = pushParseAndPop({}, + PARSERS, node, []); + return owsObject ? owsObject : null; + } +} inherits(OWS, XML); @@ -187,29 +210,6 @@ const SERVICE_PROVIDER_PARSERS = }); -/** - * @inheritDoc - */ -OWS.prototype.readFromDocument = function(doc) { - for (let n = doc.firstChild; n; n = n.nextSibling) { - if (n.nodeType == Node.ELEMENT_NODE) { - return this.readFromNode(n); - } - } - return null; -}; - - -/** - * @inheritDoc - */ -OWS.prototype.readFromNode = function(node) { - const owsObject = pushParseAndPop({}, - PARSERS, node, []); - return owsObject ? owsObject : null; -}; - - /** * @param {Node} node Node. * @param {Array.<*>} objectStack Object stack. diff --git a/src/ol/format/Polyline.js b/src/ol/format/Polyline.js index ed7267de69..d7f57cee60 100644 --- a/src/ol/format/Polyline.js +++ b/src/ol/format/Polyline.js @@ -32,30 +32,98 @@ import {get as getProjection} from '../proj.js'; * @param {module:ol/format/Polyline~Options=} opt_options Optional configuration object. * @api */ -const Polyline = function(opt_options) { +class Polyline { + constructor(opt_options) { - const options = opt_options ? opt_options : {}; + const options = opt_options ? opt_options : {}; - TextFeature.call(this); + TextFeature.call(this); + + /** + * @inheritDoc + */ + this.dataProjection = getProjection('EPSG:4326'); + + /** + * @private + * @type {number} + */ + this.factor_ = options.factor ? options.factor : 1e5; + + /** + * @private + * @type {module:ol/geom/GeometryLayout} + */ + this.geometryLayout_ = options.geometryLayout ? + options.geometryLayout : GeometryLayout.XY; + } /** * @inheritDoc */ - this.dataProjection = getProjection('EPSG:4326'); + readFeatureFromText(text, opt_options) { + const geometry = this.readGeometryFromText(text, opt_options); + return new Feature(geometry); + } /** - * @private - * @type {number} + * @inheritDoc */ - this.factor_ = options.factor ? options.factor : 1e5; + readFeaturesFromText(text, opt_options) { + const feature = this.readFeatureFromText(text, opt_options); + return [feature]; + } /** - * @private - * @type {module:ol/geom/GeometryLayout} + * @inheritDoc */ - this.geometryLayout_ = options.geometryLayout ? - options.geometryLayout : GeometryLayout.XY; -}; + readGeometryFromText(text, opt_options) { + const stride = getStrideForLayout(this.geometryLayout_); + const flatCoordinates = decodeDeltas(text, stride, this.factor_); + flipXY(flatCoordinates, 0, flatCoordinates.length, stride, flatCoordinates); + const coordinates = inflateCoordinates(flatCoordinates, 0, flatCoordinates.length, stride); + + return ( + /** @type {module:ol/geom/Geometry} */ (transformWithOptions( + new LineString(coordinates, this.geometryLayout_), + false, + this.adaptOptions(opt_options) + )) + ); + } + + /** + * @inheritDoc + */ + writeFeatureText(feature, opt_options) { + const geometry = feature.getGeometry(); + if (geometry) { + return this.writeGeometryText(geometry, opt_options); + } else { + assert(false, 40); // Expected `feature` to have a geometry + return ''; + } + } + + /** + * @inheritDoc + */ + writeFeaturesText(features, opt_options) { + return this.writeFeatureText(features[0], opt_options); + } + + /** + * @inheritDoc + */ + writeGeometryText(geometry, opt_options) { + geometry = /** @type {module:ol/geom/LineString} */ + (transformWithOptions(geometry, true, this.adaptOptions(opt_options))); + const flatCoordinates = geometry.getFlatCoordinates(); + const stride = geometry.getStride(); + flipXY(flatCoordinates, 0, flatCoordinates.length, stride, flatCoordinates); + return encodeDeltas(flatCoordinates, stride, this.factor_); + } +} inherits(Polyline, TextFeature); @@ -277,15 +345,6 @@ export function encodeUnsignedInteger(num) { Polyline.prototype.readFeature; -/** - * @inheritDoc - */ -Polyline.prototype.readFeatureFromText = function(text, opt_options) { - const geometry = this.readGeometryFromText(text, opt_options); - return new Feature(geometry); -}; - - /** * Read the feature from the source. As Polyline sources contain a single * feature, this will return the feature in an array. @@ -299,15 +358,6 @@ Polyline.prototype.readFeatureFromText = function(text, opt_options) { Polyline.prototype.readFeatures; -/** - * @inheritDoc - */ -Polyline.prototype.readFeaturesFromText = function(text, opt_options) { - const feature = this.readFeatureFromText(text, opt_options); - return [feature]; -}; - - /** * Read the geometry from the source. * @@ -320,25 +370,6 @@ Polyline.prototype.readFeaturesFromText = function(text, opt_options) { Polyline.prototype.readGeometry; -/** - * @inheritDoc - */ -Polyline.prototype.readGeometryFromText = function(text, opt_options) { - const stride = getStrideForLayout(this.geometryLayout_); - const flatCoordinates = decodeDeltas(text, stride, this.factor_); - flipXY(flatCoordinates, 0, flatCoordinates.length, stride, flatCoordinates); - const coordinates = inflateCoordinates(flatCoordinates, 0, flatCoordinates.length, stride); - - return ( - /** @type {module:ol/geom/Geometry} */ (transformWithOptions( - new LineString(coordinates, this.geometryLayout_), - false, - this.adaptOptions(opt_options) - )) - ); -}; - - /** * Read the projection from a Polyline source. * @@ -350,28 +381,6 @@ Polyline.prototype.readGeometryFromText = function(text, opt_options) { Polyline.prototype.readProjection; -/** - * @inheritDoc - */ -Polyline.prototype.writeFeatureText = function(feature, opt_options) { - const geometry = feature.getGeometry(); - if (geometry) { - return this.writeGeometryText(geometry, opt_options); - } else { - assert(false, 40); // Expected `feature` to have a geometry - return ''; - } -}; - - -/** - * @inheritDoc - */ -Polyline.prototype.writeFeaturesText = function(features, opt_options) { - return this.writeFeatureText(features[0], opt_options); -}; - - /** * Write a single geometry in Polyline format. * @@ -384,15 +393,4 @@ Polyline.prototype.writeFeaturesText = function(features, opt_options) { Polyline.prototype.writeGeometry; -/** - * @inheritDoc - */ -Polyline.prototype.writeGeometryText = function(geometry, opt_options) { - geometry = /** @type {module:ol/geom/LineString} */ - (transformWithOptions(geometry, true, this.adaptOptions(opt_options))); - const flatCoordinates = geometry.getFlatCoordinates(); - const stride = geometry.getStride(); - flipXY(flatCoordinates, 0, flatCoordinates.length, stride, flatCoordinates); - return encodeDeltas(flatCoordinates, stride, this.factor_); -}; export default Polyline; diff --git a/src/ol/format/TextFeature.js b/src/ol/format/TextFeature.js index 33d6eac33d..801becd8b4 100644 --- a/src/ol/format/TextFeature.js +++ b/src/ol/format/TextFeature.js @@ -15,9 +15,130 @@ import FormatType from '../format/FormatType.js'; * @abstract * @extends {module:ol/format/Feature} */ -const TextFeature = function() { - FeatureFormat.call(this); -}; +class TextFeature { + constructor() { + FeatureFormat.call(this); + } + + /** + * @inheritDoc + */ + getType() { + return FormatType.TEXT; + } + + /** + * @inheritDoc + */ + readFeature(source, opt_options) { + return this.readFeatureFromText(getText(source), this.adaptOptions(opt_options)); + } + + /** + * @abstract + * @param {string} text Text. + * @param {module:ol/format/Feature~ReadOptions=} opt_options Read options. + * @protected + * @return {module:ol/Feature} Feature. + */ + readFeatureFromText(text, opt_options) {} + + /** + * @inheritDoc + */ + readFeatures(source, opt_options) { + return this.readFeaturesFromText(getText(source), this.adaptOptions(opt_options)); + } + + /** + * @abstract + * @param {string} text Text. + * @param {module:ol/format/Feature~ReadOptions=} opt_options Read options. + * @protected + * @return {Array.} Features. + */ + readFeaturesFromText(text, opt_options) {} + + /** + * @inheritDoc + */ + readGeometry(source, opt_options) { + return this.readGeometryFromText(getText(source), this.adaptOptions(opt_options)); + } + + /** + * @abstract + * @param {string} text Text. + * @param {module:ol/format/Feature~ReadOptions=} opt_options Read options. + * @protected + * @return {module:ol/geom/Geometry} Geometry. + */ + readGeometryFromText(text, opt_options) {} + + /** + * @inheritDoc + */ + readProjection(source) { + return this.readProjectionFromText(getText(source)); + } + + /** + * @param {string} text Text. + * @protected + * @return {module:ol/proj/Projection} Projection. + */ + readProjectionFromText(text) { + return this.dataProjection; + } + + /** + * @inheritDoc + */ + writeFeature(feature, opt_options) { + return this.writeFeatureText(feature, this.adaptOptions(opt_options)); + } + + /** + * @abstract + * @param {module:ol/Feature} feature Features. + * @param {module:ol/format/Feature~WriteOptions=} opt_options Write options. + * @protected + * @return {string} Text. + */ + writeFeatureText(feature, opt_options) {} + + /** + * @inheritDoc + */ + writeFeatures(features, opt_options) { + return this.writeFeaturesText(features, this.adaptOptions(opt_options)); + } + + /** + * @abstract + * @param {Array.} features Features. + * @param {module:ol/format/Feature~WriteOptions=} opt_options Write options. + * @protected + * @return {string} Text. + */ + writeFeaturesText(features, opt_options) {} + + /** + * @inheritDoc + */ + writeGeometry(geometry, opt_options) { + return this.writeGeometryText(geometry, this.adaptOptions(opt_options)); + } + + /** + * @abstract + * @param {module:ol/geom/Geometry} geometry Geometry. + * @param {module:ol/format/Feature~WriteOptions=} opt_options Write options. + * @protected + * @return {string} Text. + */ + writeGeometryText(geometry, opt_options) {} +} inherits(TextFeature, FeatureFormat); @@ -35,136 +156,4 @@ function getText(source) { } -/** - * @inheritDoc - */ -TextFeature.prototype.getType = function() { - return FormatType.TEXT; -}; - - -/** - * @inheritDoc - */ -TextFeature.prototype.readFeature = function(source, opt_options) { - return this.readFeatureFromText(getText(source), this.adaptOptions(opt_options)); -}; - - -/** - * @abstract - * @param {string} text Text. - * @param {module:ol/format/Feature~ReadOptions=} opt_options Read options. - * @protected - * @return {module:ol/Feature} Feature. - */ -TextFeature.prototype.readFeatureFromText = function(text, opt_options) {}; - - -/** - * @inheritDoc - */ -TextFeature.prototype.readFeatures = function(source, opt_options) { - return this.readFeaturesFromText(getText(source), this.adaptOptions(opt_options)); -}; - - -/** - * @abstract - * @param {string} text Text. - * @param {module:ol/format/Feature~ReadOptions=} opt_options Read options. - * @protected - * @return {Array.} Features. - */ -TextFeature.prototype.readFeaturesFromText = function(text, opt_options) {}; - - -/** - * @inheritDoc - */ -TextFeature.prototype.readGeometry = function(source, opt_options) { - return this.readGeometryFromText(getText(source), this.adaptOptions(opt_options)); -}; - - -/** - * @abstract - * @param {string} text Text. - * @param {module:ol/format/Feature~ReadOptions=} opt_options Read options. - * @protected - * @return {module:ol/geom/Geometry} Geometry. - */ -TextFeature.prototype.readGeometryFromText = function(text, opt_options) {}; - - -/** - * @inheritDoc - */ -TextFeature.prototype.readProjection = function(source) { - return this.readProjectionFromText(getText(source)); -}; - - -/** - * @param {string} text Text. - * @protected - * @return {module:ol/proj/Projection} Projection. - */ -TextFeature.prototype.readProjectionFromText = function(text) { - return this.dataProjection; -}; - - -/** - * @inheritDoc - */ -TextFeature.prototype.writeFeature = function(feature, opt_options) { - return this.writeFeatureText(feature, this.adaptOptions(opt_options)); -}; - - -/** - * @abstract - * @param {module:ol/Feature} feature Features. - * @param {module:ol/format/Feature~WriteOptions=} opt_options Write options. - * @protected - * @return {string} Text. - */ -TextFeature.prototype.writeFeatureText = function(feature, opt_options) {}; - - -/** - * @inheritDoc - */ -TextFeature.prototype.writeFeatures = function(features, opt_options) { - return this.writeFeaturesText(features, this.adaptOptions(opt_options)); -}; - - -/** - * @abstract - * @param {Array.} features Features. - * @param {module:ol/format/Feature~WriteOptions=} opt_options Write options. - * @protected - * @return {string} Text. - */ -TextFeature.prototype.writeFeaturesText = function(features, opt_options) {}; - - -/** - * @inheritDoc - */ -TextFeature.prototype.writeGeometry = function(geometry, opt_options) { - return this.writeGeometryText(geometry, this.adaptOptions(opt_options)); -}; - - -/** - * @abstract - * @param {module:ol/geom/Geometry} geometry Geometry. - * @param {module:ol/format/Feature~WriteOptions=} opt_options Write options. - * @protected - * @return {string} Text. - */ -TextFeature.prototype.writeGeometryText = function(geometry, opt_options) {}; export default TextFeature; diff --git a/src/ol/format/TopoJSON.js b/src/ol/format/TopoJSON.js index b2b761e524..5ed2121b70 100644 --- a/src/ol/format/TopoJSON.js +++ b/src/ol/format/TopoJSON.js @@ -48,32 +48,112 @@ import {get as getProjection} from '../proj.js'; * @param {module:ol/format/TopoJSON~Options=} opt_options Options. * @api */ -const TopoJSON = function(opt_options) { +class TopoJSON { + constructor(opt_options) { - const options = opt_options ? opt_options : {}; + const options = opt_options ? opt_options : {}; - JSONFeature.call(this); + JSONFeature.call(this); - /** - * @private - * @type {string|undefined} - */ - this.layerName_ = options.layerName; + /** + * @private + * @type {string|undefined} + */ + this.layerName_ = options.layerName; - /** - * @private - * @type {Array.} - */ - this.layers_ = options.layers ? options.layers : null; + /** + * @private + * @type {Array.} + */ + this.layers_ = options.layers ? options.layers : null; + + /** + * @inheritDoc + */ + this.dataProjection = getProjection( + options.dataProjection ? + options.dataProjection : 'EPSG:4326'); + + } /** * @inheritDoc */ - this.dataProjection = getProjection( - options.dataProjection ? - options.dataProjection : 'EPSG:4326'); + readFeaturesFromObject(object, opt_options) { + if (object.type == 'Topology') { + const topoJSONTopology = /** @type {TopoJSONTopology} */ (object); + let transform, scale = null, translate = null; + if (topoJSONTopology.transform) { + transform = topoJSONTopology.transform; + scale = transform.scale; + translate = transform.translate; + } + const arcs = topoJSONTopology.arcs; + if (transform) { + transformArcs(arcs, scale, translate); + } + /** @type {Array.} */ + const features = []; + const topoJSONFeatures = topoJSONTopology.objects; + const property = this.layerName_; + let feature; + for (const objectName in topoJSONFeatures) { + if (this.layers_ && this.layers_.indexOf(objectName) == -1) { + continue; + } + if (topoJSONFeatures[objectName].type === 'GeometryCollection') { + feature = /** @type {TopoJSONGeometryCollection} */ (topoJSONFeatures[objectName]); + features.push.apply(features, readFeaturesFromGeometryCollection( + feature, arcs, scale, translate, property, objectName, opt_options)); + } else { + feature = /** @type {TopoJSONGeometry} */ (topoJSONFeatures[objectName]); + features.push(readFeatureFromGeometry( + feature, arcs, scale, translate, property, objectName, opt_options)); + } + } + return features; + } else { + return []; + } + } -}; + /** + * @inheritDoc + */ + readProjectionFromObject(object) { + return this.dataProjection; + } + + /** + * Not implemented. + * @inheritDoc + */ + writeFeatureObject(feature, opt_options) {} + + /** + * Not implemented. + * @inheritDoc + */ + writeFeaturesObject(features, opt_options) {} + + /** + * Not implemented. + * @inheritDoc + */ + writeGeometryObject(geometry, opt_options) {} + + /** + * Not implemented. + * @override + */ + readGeometryFromObject() {} + + /** + * Not implemented. + * @override + */ + readFeatureFromObject() {} +} inherits(TopoJSON, JSONFeature); @@ -309,48 +389,6 @@ function readFeatureFromGeometry(object, arcs, scale, translate, property, name, TopoJSON.prototype.readFeatures; -/** - * @inheritDoc - */ -TopoJSON.prototype.readFeaturesFromObject = function(object, opt_options) { - if (object.type == 'Topology') { - const topoJSONTopology = /** @type {TopoJSONTopology} */ (object); - let transform, scale = null, translate = null; - if (topoJSONTopology.transform) { - transform = topoJSONTopology.transform; - scale = transform.scale; - translate = transform.translate; - } - const arcs = topoJSONTopology.arcs; - if (transform) { - transformArcs(arcs, scale, translate); - } - /** @type {Array.} */ - const features = []; - const topoJSONFeatures = topoJSONTopology.objects; - const property = this.layerName_; - let feature; - for (const objectName in topoJSONFeatures) { - if (this.layers_ && this.layers_.indexOf(objectName) == -1) { - continue; - } - if (topoJSONFeatures[objectName].type === 'GeometryCollection') { - feature = /** @type {TopoJSONGeometryCollection} */ (topoJSONFeatures[objectName]); - features.push.apply(features, readFeaturesFromGeometryCollection( - feature, arcs, scale, translate, property, objectName, opt_options)); - } else { - feature = /** @type {TopoJSONGeometry} */ (topoJSONFeatures[objectName]); - features.push(readFeatureFromGeometry( - feature, arcs, scale, translate, property, objectName, opt_options)); - } - } - return features; - } else { - return []; - } -}; - - /** * Apply a linear transform to array of arcs. The provided array of arcs is * modified in place. @@ -412,45 +450,4 @@ function transformVertex(vertex, scale, translate) { TopoJSON.prototype.readProjection; -/** - * @inheritDoc - */ -TopoJSON.prototype.readProjectionFromObject = function(object) { - return this.dataProjection; -}; - - -/** - * Not implemented. - * @inheritDoc - */ -TopoJSON.prototype.writeFeatureObject = function(feature, opt_options) {}; - - -/** - * Not implemented. - * @inheritDoc - */ -TopoJSON.prototype.writeFeaturesObject = function(features, opt_options) {}; - - -/** - * Not implemented. - * @inheritDoc - */ -TopoJSON.prototype.writeGeometryObject = function(geometry, opt_options) {}; - - -/** - * Not implemented. - * @override - */ -TopoJSON.prototype.readGeometryFromObject = function() {}; - - -/** - * Not implemented. - * @override - */ -TopoJSON.prototype.readFeatureFromObject = function() {}; export default TopoJSON; diff --git a/src/ol/format/WFS.js b/src/ol/format/WFS.js index 0e6c27f86b..045a483317 100644 --- a/src/ol/format/WFS.js +++ b/src/ol/format/WFS.js @@ -143,57 +143,338 @@ const DEFAULT_VERSION = '1.1.0'; * @extends {module:ol/format/XMLFeature} * @api */ -const WFS = function(opt_options) { - const options = opt_options ? opt_options : {}; +class WFS { + constructor(opt_options) { + const options = opt_options ? opt_options : {}; + + /** + * @private + * @type {Array.|string|undefined} + */ + this.featureType_ = options.featureType; + + /** + * @private + * @type {Object.|string|undefined} + */ + this.featureNS_ = options.featureNS; + + /** + * @private + * @type {module:ol/format/GMLBase} + */ + this.gmlFormat_ = options.gmlFormat ? + options.gmlFormat : new GML3(); + + /** + * @private + * @type {string} + */ + this.schemaLocation_ = options.schemaLocation ? + options.schemaLocation : SCHEMA_LOCATIONS[DEFAULT_VERSION]; + + XMLFeature.call(this); + } /** - * @private - * @type {Array.|string|undefined} + * @return {Array.|string|undefined} featureType */ - this.featureType_ = options.featureType; + getFeatureType() { + return this.featureType_; + } /** - * @private - * @type {Object.|string|undefined} + * @param {Array.|string|undefined} featureType Feature type(s) to parse. */ - this.featureNS_ = options.featureNS; + setFeatureType(featureType) { + this.featureType_ = featureType; + } /** - * @private - * @type {module:ol/format/GMLBase} + * @inheritDoc */ - this.gmlFormat_ = options.gmlFormat ? - options.gmlFormat : new GML3(); + readFeaturesFromNode(node, opt_options) { + const context = /** @type {module:ol/xml~NodeStackItem} */ ({ + 'featureType': this.featureType_, + 'featureNS': this.featureNS_ + }); + assign(context, this.getReadOptions(node, opt_options ? opt_options : {})); + const objectStack = [context]; + this.gmlFormat_.FEATURE_COLLECTION_PARSERS[GMLNS][ + 'featureMember'] = + makeArrayPusher(GMLBase.prototype.readFeaturesInternal); + let features = pushParseAndPop([], + this.gmlFormat_.FEATURE_COLLECTION_PARSERS, node, + objectStack, this.gmlFormat_); + if (!features) { + features = []; + } + return features; + } /** - * @private - * @type {string} + * Read transaction response of the source. + * + * @param {Document|Node|Object|string} source Source. + * @return {module:ol/format/WFS~TransactionResponse|undefined} Transaction response. + * @api */ - this.schemaLocation_ = options.schemaLocation ? - options.schemaLocation : SCHEMA_LOCATIONS[DEFAULT_VERSION]; + readTransactionResponse(source) { + if (isDocument(source)) { + return this.readTransactionResponseFromDocument( + /** @type {Document} */ (source)); + } else if (isNode(source)) { + return this.readTransactionResponseFromNode(/** @type {Node} */ (source)); + } else if (typeof source === 'string') { + const doc = parse(source); + return this.readTransactionResponseFromDocument(doc); + } else { + return undefined; + } + } - XMLFeature.call(this); -}; + /** + * Read feature collection metadata of the source. + * + * @param {Document|Node|Object|string} source Source. + * @return {module:ol/format/WFS~FeatureCollectionMetadata|undefined} + * FeatureCollection metadata. + * @api + */ + readFeatureCollectionMetadata(source) { + if (isDocument(source)) { + return this.readFeatureCollectionMetadataFromDocument( + /** @type {Document} */ (source)); + } else if (isNode(source)) { + return this.readFeatureCollectionMetadataFromNode( + /** @type {Node} */ (source)); + } else if (typeof source === 'string') { + const doc = parse(source); + return this.readFeatureCollectionMetadataFromDocument(doc); + } else { + return undefined; + } + } + + /** + * @param {Document} doc Document. + * @return {module:ol/format/WFS~FeatureCollectionMetadata|undefined} + * FeatureCollection metadata. + */ + readFeatureCollectionMetadataFromDocument(doc) { + for (let n = doc.firstChild; n; n = n.nextSibling) { + if (n.nodeType == Node.ELEMENT_NODE) { + return this.readFeatureCollectionMetadataFromNode(n); + } + } + return undefined; + } + + /** + * @param {Node} node Node. + * @return {module:ol/format/WFS~FeatureCollectionMetadata|undefined} + * FeatureCollection metadata. + */ + readFeatureCollectionMetadataFromNode(node) { + const result = {}; + const value = readNonNegativeIntegerString( + node.getAttribute('numberOfFeatures')); + result['numberOfFeatures'] = value; + return pushParseAndPop( + /** @type {module:ol/format/WFS~FeatureCollectionMetadata} */ (result), + FEATURE_COLLECTION_PARSERS, node, [], this.gmlFormat_); + } + + /** + * @param {Document} doc Document. + * @return {module:ol/format/WFS~TransactionResponse|undefined} Transaction response. + */ + readTransactionResponseFromDocument(doc) { + for (let n = doc.firstChild; n; n = n.nextSibling) { + if (n.nodeType == Node.ELEMENT_NODE) { + return this.readTransactionResponseFromNode(n); + } + } + return undefined; + } + + /** + * @param {Node} node Node. + * @return {module:ol/format/WFS~TransactionResponse|undefined} Transaction response. + */ + readTransactionResponseFromNode(node) { + return pushParseAndPop( + /** @type {module:ol/format/WFS~TransactionResponse} */({}), + TRANSACTION_RESPONSE_PARSERS, node, []); + } + + /** + * Encode format as WFS `GetFeature` and return the Node. + * + * @param {module:ol/format/WFS~WriteGetFeatureOptions} options Options. + * @return {Node} Result. + * @api + */ + writeGetFeature(options) { + const node = createElementNS(WFSNS, 'GetFeature'); + node.setAttribute('service', 'WFS'); + node.setAttribute('version', '1.1.0'); + let filter; + if (options) { + if (options.handle) { + node.setAttribute('handle', options.handle); + } + if (options.outputFormat) { + node.setAttribute('outputFormat', options.outputFormat); + } + if (options.maxFeatures !== undefined) { + node.setAttribute('maxFeatures', options.maxFeatures); + } + if (options.resultType) { + node.setAttribute('resultType', options.resultType); + } + if (options.startIndex !== undefined) { + node.setAttribute('startIndex', options.startIndex); + } + if (options.count !== undefined) { + node.setAttribute('count', options.count); + } + filter = options.filter; + if (options.bbox) { + assert(options.geometryName, + 12); // `options.geometryName` must also be provided when `options.bbox` is set + const bbox = bboxFilter( + /** @type {string} */ (options.geometryName), options.bbox, options.srsName); + if (filter) { + // if bbox and filter are both set, combine the two into a single filter + filter = andFilter(filter, bbox); + } else { + filter = bbox; + } + } + } + node.setAttributeNS(XML_SCHEMA_INSTANCE_URI, 'xsi:schemaLocation', this.schemaLocation_); + /** @type {module:ol/xml~NodeStackItem} */ + const context = { + node: node, + '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.} */ (options.featureTypes), [context]); + return node; + } + + /** + * Encode format as WFS `Transaction` and return the Node. + * + * @param {Array.} inserts The features to insert. + * @param {Array.} updates The features to update. + * @param {Array.} deletes The features to delete. + * @param {module:ol/format/WFS~WriteTransactionOptions} options Write options. + * @return {Node} Result. + * @api + */ + writeTransaction(inserts, updates, deletes, options) { + const objectStack = []; + const node = createElementNS(WFSNS, 'Transaction'); + const version = options.version ? options.version : DEFAULT_VERSION; + const gmlVersion = version === '1.0.0' ? 2 : 3; + node.setAttribute('service', 'WFS'); + node.setAttribute('version', version); + let baseObj; + /** @type {module:ol/xml~NodeStackItem} */ + let obj; + if (options) { + baseObj = options.gmlOptions ? options.gmlOptions : {}; + if (options.handle) { + node.setAttribute('handle', options.handle); + } + } + const schemaLocation = SCHEMA_LOCATIONS[version]; + 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, + 'featureType': options.featureType, 'featurePrefix': featurePrefix, + 'gmlVersion': gmlVersion, 'hasZ': options.hasZ, 'srsName': options.srsName}; + assign(obj, baseObj); + pushSerializeAndPop(obj, + TRANSACTION_SERIALIZERS, + makeSimpleNodeFactory('Insert'), inserts, + objectStack); + } + if (updates) { + obj = {node: node, 'featureNS': options.featureNS, + 'featureType': options.featureType, 'featurePrefix': featurePrefix, + 'gmlVersion': gmlVersion, 'hasZ': options.hasZ, 'srsName': options.srsName}; + assign(obj, baseObj); + pushSerializeAndPop(obj, + TRANSACTION_SERIALIZERS, + makeSimpleNodeFactory('Update'), updates, + objectStack); + } + if (deletes) { + pushSerializeAndPop({node: node, 'featureNS': options.featureNS, + 'featureType': options.featureType, 'featurePrefix': featurePrefix, + 'gmlVersion': gmlVersion, 'srsName': options.srsName}, + TRANSACTION_SERIALIZERS, + makeSimpleNodeFactory('Delete'), deletes, + objectStack); + } + if (options.nativeElements) { + pushSerializeAndPop({node: node, 'featureNS': options.featureNS, + 'featureType': options.featureType, 'featurePrefix': featurePrefix, + 'gmlVersion': gmlVersion, 'srsName': options.srsName}, + TRANSACTION_SERIALIZERS, + makeSimpleNodeFactory('Native'), options.nativeElements, + objectStack); + } + return node; + } + + /** + * @inheritDoc + */ + readProjectionFromDocument(doc) { + for (let n = doc.firstChild; n; n = n.nextSibling) { + if (n.nodeType == Node.ELEMENT_NODE) { + return this.readProjectionFromNode(n); + } + } + return null; + } + + /** + * @inheritDoc + */ + readProjectionFromNode(node) { + if (node.firstElementChild && + node.firstElementChild.firstElementChild) { + node = node.firstElementChild.firstElementChild; + for (let n = node.firstElementChild; n; n = n.nextElementSibling) { + if (!(n.childNodes.length === 0 || + (n.childNodes.length === 1 && + n.firstChild.nodeType === 3))) { + const objectStack = [{}]; + this.gmlFormat_.readGeometryElement(n, objectStack); + return getProjection(objectStack.pop().srsName); + } + } + } + + return null; + } +} inherits(WFS, XMLFeature); -/** - * @return {Array.|string|undefined} featureType - */ -WFS.prototype.getFeatureType = function() { - return this.featureType_; -}; - - -/** - * @param {Array.|string|undefined} featureType Feature type(s) to parse. - */ -WFS.prototype.setFeatureType = function(featureType) { - this.featureType_ = featureType; -}; - - /** * Read all features from a WFS FeatureCollection. * @@ -206,90 +487,6 @@ WFS.prototype.setFeatureType = function(featureType) { WFS.prototype.readFeatures; -/** - * @inheritDoc - */ -WFS.prototype.readFeaturesFromNode = function(node, opt_options) { - const context = /** @type {module:ol/xml~NodeStackItem} */ ({ - 'featureType': this.featureType_, - 'featureNS': this.featureNS_ - }); - assign(context, this.getReadOptions(node, opt_options ? opt_options : {})); - const objectStack = [context]; - this.gmlFormat_.FEATURE_COLLECTION_PARSERS[GMLNS][ - 'featureMember'] = - makeArrayPusher(GMLBase.prototype.readFeaturesInternal); - let features = pushParseAndPop([], - this.gmlFormat_.FEATURE_COLLECTION_PARSERS, node, - objectStack, this.gmlFormat_); - if (!features) { - features = []; - } - return features; -}; - - -/** - * Read transaction response of the source. - * - * @param {Document|Node|Object|string} source Source. - * @return {module:ol/format/WFS~TransactionResponse|undefined} Transaction response. - * @api - */ -WFS.prototype.readTransactionResponse = function(source) { - if (isDocument(source)) { - return this.readTransactionResponseFromDocument( - /** @type {Document} */ (source)); - } else if (isNode(source)) { - return this.readTransactionResponseFromNode(/** @type {Node} */ (source)); - } else if (typeof source === 'string') { - const doc = parse(source); - return this.readTransactionResponseFromDocument(doc); - } else { - return undefined; - } -}; - - -/** - * Read feature collection metadata of the source. - * - * @param {Document|Node|Object|string} source Source. - * @return {module:ol/format/WFS~FeatureCollectionMetadata|undefined} - * FeatureCollection metadata. - * @api - */ -WFS.prototype.readFeatureCollectionMetadata = function(source) { - if (isDocument(source)) { - return this.readFeatureCollectionMetadataFromDocument( - /** @type {Document} */ (source)); - } else if (isNode(source)) { - return this.readFeatureCollectionMetadataFromNode( - /** @type {Node} */ (source)); - } else if (typeof source === 'string') { - const doc = parse(source); - return this.readFeatureCollectionMetadataFromDocument(doc); - } else { - return undefined; - } -}; - - -/** - * @param {Document} doc Document. - * @return {module:ol/format/WFS~FeatureCollectionMetadata|undefined} - * FeatureCollection metadata. - */ -WFS.prototype.readFeatureCollectionMetadataFromDocument = function(doc) { - for (let n = doc.firstChild; n; n = n.nextSibling) { - if (n.nodeType == Node.ELEMENT_NODE) { - return this.readFeatureCollectionMetadataFromNode(n); - } - } - return undefined; -}; - - /** * @const * @type {Object.>} @@ -302,22 +499,6 @@ const FEATURE_COLLECTION_PARSERS = { }; -/** - * @param {Node} node Node. - * @return {module:ol/format/WFS~FeatureCollectionMetadata|undefined} - * FeatureCollection metadata. - */ -WFS.prototype.readFeatureCollectionMetadataFromNode = function(node) { - const result = {}; - const value = readNonNegativeIntegerString( - node.getAttribute('numberOfFeatures')); - result['numberOfFeatures'] = value; - return pushParseAndPop( - /** @type {module:ol/format/WFS~FeatureCollectionMetadata} */ (result), - FEATURE_COLLECTION_PARSERS, node, [], this.gmlFormat_); -}; - - /** * @const * @type {Object.>} @@ -400,31 +581,6 @@ const TRANSACTION_RESPONSE_PARSERS = { }; -/** - * @param {Document} doc Document. - * @return {module:ol/format/WFS~TransactionResponse|undefined} Transaction response. - */ -WFS.prototype.readTransactionResponseFromDocument = function(doc) { - for (let n = doc.firstChild; n; n = n.nextSibling) { - if (n.nodeType == Node.ELEMENT_NODE) { - return this.readTransactionResponseFromNode(n); - } - } - return undefined; -}; - - -/** - * @param {Node} node Node. - * @return {module:ol/format/WFS~TransactionResponse|undefined} Transaction response. - */ -WFS.prototype.readTransactionResponseFromNode = function(node) { - return pushParseAndPop( - /** @type {module:ol/format/WFS~TransactionResponse} */({}), - TRANSACTION_RESPONSE_PARSERS, node, []); -}; - - /** * @type {Object.>} */ @@ -942,138 +1098,6 @@ function writeGetFeature(node, featureTypes, objectStack) { } -/** - * Encode format as WFS `GetFeature` and return the Node. - * - * @param {module:ol/format/WFS~WriteGetFeatureOptions} options Options. - * @return {Node} Result. - * @api - */ -WFS.prototype.writeGetFeature = function(options) { - const node = createElementNS(WFSNS, 'GetFeature'); - node.setAttribute('service', 'WFS'); - node.setAttribute('version', '1.1.0'); - let filter; - if (options) { - if (options.handle) { - node.setAttribute('handle', options.handle); - } - if (options.outputFormat) { - node.setAttribute('outputFormat', options.outputFormat); - } - if (options.maxFeatures !== undefined) { - node.setAttribute('maxFeatures', options.maxFeatures); - } - if (options.resultType) { - node.setAttribute('resultType', options.resultType); - } - if (options.startIndex !== undefined) { - node.setAttribute('startIndex', options.startIndex); - } - if (options.count !== undefined) { - node.setAttribute('count', options.count); - } - filter = options.filter; - if (options.bbox) { - assert(options.geometryName, - 12); // `options.geometryName` must also be provided when `options.bbox` is set - const bbox = bboxFilter( - /** @type {string} */ (options.geometryName), options.bbox, options.srsName); - if (filter) { - // if bbox and filter are both set, combine the two into a single filter - filter = andFilter(filter, bbox); - } else { - filter = bbox; - } - } - } - node.setAttributeNS(XML_SCHEMA_INSTANCE_URI, 'xsi:schemaLocation', this.schemaLocation_); - /** @type {module:ol/xml~NodeStackItem} */ - const context = { - node: node, - '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.} */ (options.featureTypes), [context]); - return node; -}; - - -/** - * Encode format as WFS `Transaction` and return the Node. - * - * @param {Array.} inserts The features to insert. - * @param {Array.} updates The features to update. - * @param {Array.} deletes The features to delete. - * @param {module:ol/format/WFS~WriteTransactionOptions} options Write options. - * @return {Node} Result. - * @api - */ -WFS.prototype.writeTransaction = function(inserts, updates, deletes, options) { - const objectStack = []; - const node = createElementNS(WFSNS, 'Transaction'); - const version = options.version ? options.version : DEFAULT_VERSION; - const gmlVersion = version === '1.0.0' ? 2 : 3; - node.setAttribute('service', 'WFS'); - node.setAttribute('version', version); - let baseObj; - /** @type {module:ol/xml~NodeStackItem} */ - let obj; - if (options) { - baseObj = options.gmlOptions ? options.gmlOptions : {}; - if (options.handle) { - node.setAttribute('handle', options.handle); - } - } - const schemaLocation = SCHEMA_LOCATIONS[version]; - 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, - 'featureType': options.featureType, 'featurePrefix': featurePrefix, - 'gmlVersion': gmlVersion, 'hasZ': options.hasZ, 'srsName': options.srsName}; - assign(obj, baseObj); - pushSerializeAndPop(obj, - TRANSACTION_SERIALIZERS, - makeSimpleNodeFactory('Insert'), inserts, - objectStack); - } - if (updates) { - obj = {node: node, 'featureNS': options.featureNS, - 'featureType': options.featureType, 'featurePrefix': featurePrefix, - 'gmlVersion': gmlVersion, 'hasZ': options.hasZ, 'srsName': options.srsName}; - assign(obj, baseObj); - pushSerializeAndPop(obj, - TRANSACTION_SERIALIZERS, - makeSimpleNodeFactory('Update'), updates, - objectStack); - } - if (deletes) { - pushSerializeAndPop({node: node, 'featureNS': options.featureNS, - 'featureType': options.featureType, 'featurePrefix': featurePrefix, - 'gmlVersion': gmlVersion, 'srsName': options.srsName}, - TRANSACTION_SERIALIZERS, - makeSimpleNodeFactory('Delete'), deletes, - objectStack); - } - if (options.nativeElements) { - pushSerializeAndPop({node: node, 'featureNS': options.featureNS, - 'featureType': options.featureType, 'featurePrefix': featurePrefix, - 'gmlVersion': gmlVersion, 'srsName': options.srsName}, - TRANSACTION_SERIALIZERS, - makeSimpleNodeFactory('Native'), options.nativeElements, - objectStack); - } - return node; -}; - - /** * Read the projection from a WFS source. * @@ -1085,37 +1109,4 @@ WFS.prototype.writeTransaction = function(inserts, updates, deletes, options) { WFS.prototype.readProjection; -/** - * @inheritDoc - */ -WFS.prototype.readProjectionFromDocument = function(doc) { - for (let n = doc.firstChild; n; n = n.nextSibling) { - if (n.nodeType == Node.ELEMENT_NODE) { - return this.readProjectionFromNode(n); - } - } - return null; -}; - - -/** - * @inheritDoc - */ -WFS.prototype.readProjectionFromNode = function(node) { - if (node.firstElementChild && - node.firstElementChild.firstElementChild) { - node = node.firstElementChild.firstElementChild; - for (let n = node.firstElementChild; n; n = n.nextElementSibling) { - if (!(n.childNodes.length === 0 || - (n.childNodes.length === 1 && - n.firstChild.nodeType === 3))) { - const objectStack = [{}]; - this.gmlFormat_.readGeometryElement(n, objectStack); - return getProjection(objectStack.pop().srsName); - } - } - } - - return null; -}; export default WFS; diff --git a/src/ol/format/WKT.js b/src/ol/format/WKT.js index fb3f33fc8f..5e149705ab 100644 --- a/src/ol/format/WKT.js +++ b/src/ol/format/WKT.js @@ -77,460 +77,469 @@ const TokenType = { * @param {string} wkt WKT string. * @constructor */ -const Lexer = function(wkt) { +class Lexer { + constructor(wkt) { - /** - * @type {string} - */ - this.wkt = wkt; + /** + * @type {string} + */ + this.wkt = wkt; - /** - * @type {number} - * @private - */ - this.index_ = -1; -}; - - -/** - * @param {string} c Character. - * @return {boolean} Whether the character is alphabetic. - * @private - */ -Lexer.prototype.isAlpha_ = function(c) { - return c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z'; -}; - - -/** - * @param {string} c Character. - * @param {boolean=} opt_decimal Whether the string number - * contains a dot, i.e. is a decimal number. - * @return {boolean} Whether the character is numeric. - * @private - */ -Lexer.prototype.isNumeric_ = function(c, opt_decimal) { - const decimal = opt_decimal !== undefined ? opt_decimal : false; - return c >= '0' && c <= '9' || c == '.' && !decimal; -}; - - -/** - * @param {string} c Character. - * @return {boolean} Whether the character is whitespace. - * @private - */ -Lexer.prototype.isWhiteSpace_ = function(c) { - return c == ' ' || c == '\t' || c == '\r' || c == '\n'; -}; - - -/** - * @return {string} Next string character. - * @private - */ -Lexer.prototype.nextChar_ = function() { - return this.wkt.charAt(++this.index_); -}; - - -/** - * Fetch and return the next token. - * @return {!module:ol/format/WKT~Token} Next string token. - */ -Lexer.prototype.nextToken = function() { - const c = this.nextChar_(); - const token = {position: this.index_, value: c}; - - if (c == '(') { - token.type = TokenType.LEFT_PAREN; - } else if (c == ',') { - token.type = TokenType.COMMA; - } else if (c == ')') { - token.type = TokenType.RIGHT_PAREN; - } else if (this.isNumeric_(c) || c == '-') { - token.type = TokenType.NUMBER; - token.value = this.readNumber_(); - } else if (this.isAlpha_(c)) { - token.type = TokenType.TEXT; - token.value = this.readText_(); - } else if (this.isWhiteSpace_(c)) { - return this.nextToken(); - } else if (c === '') { - token.type = TokenType.EOF; - } else { - throw new Error('Unexpected character: ' + c); + /** + * @type {number} + * @private + */ + this.index_ = -1; } - return token; -}; + /** + * @param {string} c Character. + * @return {boolean} Whether the character is alphabetic. + * @private + */ + isAlpha_(c) { + return c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z'; + } + /** + * @param {string} c Character. + * @param {boolean=} opt_decimal Whether the string number + * contains a dot, i.e. is a decimal number. + * @return {boolean} Whether the character is numeric. + * @private + */ + isNumeric_(c, opt_decimal) { + const decimal = opt_decimal !== undefined ? opt_decimal : false; + return c >= '0' && c <= '9' || c == '.' && !decimal; + } -/** - * @return {number} Numeric token value. - * @private - */ -Lexer.prototype.readNumber_ = function() { - let c; - const index = this.index_; - let decimal = false; - let scientificNotation = false; - do { - if (c == '.') { - decimal = true; - } else if (c == 'e' || c == 'E') { - scientificNotation = true; + /** + * @param {string} c Character. + * @return {boolean} Whether the character is whitespace. + * @private + */ + isWhiteSpace_(c) { + return c == ' ' || c == '\t' || c == '\r' || c == '\n'; + } + + /** + * @return {string} Next string character. + * @private + */ + nextChar_() { + return this.wkt.charAt(++this.index_); + } + + /** + * Fetch and return the next token. + * @return {!module:ol/format/WKT~Token} Next string token. + */ + nextToken() { + const c = this.nextChar_(); + const token = {position: this.index_, value: c}; + + if (c == '(') { + token.type = TokenType.LEFT_PAREN; + } else if (c == ',') { + token.type = TokenType.COMMA; + } else if (c == ')') { + token.type = TokenType.RIGHT_PAREN; + } else if (this.isNumeric_(c) || c == '-') { + token.type = TokenType.NUMBER; + token.value = this.readNumber_(); + } else if (this.isAlpha_(c)) { + token.type = TokenType.TEXT; + token.value = this.readText_(); + } else if (this.isWhiteSpace_(c)) { + return this.nextToken(); + } else if (c === '') { + token.type = TokenType.EOF; + } else { + throw new Error('Unexpected character: ' + c); } - c = this.nextChar_(); - } while ( - this.isNumeric_(c, decimal) || - // if we haven't detected a scientific number before, 'e' or 'E' - // hint that we should continue to read - !scientificNotation && (c == 'e' || c == 'E') || - // once we know that we have a scientific number, both '-' and '+' - // are allowed - scientificNotation && (c == '-' || c == '+') - ); - return parseFloat(this.wkt.substring(index, this.index_--)); -}; + return token; + } -/** - * @return {string} String token value. - * @private - */ -Lexer.prototype.readText_ = function() { - let c; - const index = this.index_; - do { - c = this.nextChar_(); - } while (this.isAlpha_(c)); - return this.wkt.substring(index, this.index_--).toUpperCase(); -}; + /** + * @return {number} Numeric token value. + * @private + */ + readNumber_() { + let c; + const index = this.index_; + let decimal = false; + let scientificNotation = false; + do { + if (c == '.') { + decimal = true; + } else if (c == 'e' || c == 'E') { + scientificNotation = true; + } + c = this.nextChar_(); + } while ( + this.isNumeric_(c, decimal) || + // if we haven't detected a scientific number before, 'e' or 'E' + // hint that we should continue to read + !scientificNotation && (c == 'e' || c == 'E') || + // once we know that we have a scientific number, both '-' and '+' + // are allowed + scientificNotation && (c == '-' || c == '+') + ); + return parseFloat(this.wkt.substring(index, this.index_--)); + } + /** + * @return {string} String token value. + * @private + */ + readText_() { + let c; + const index = this.index_; + do { + c = this.nextChar_(); + } while (this.isAlpha_(c)); + return this.wkt.substring(index, this.index_--).toUpperCase(); + } +} /** * Class to parse the tokens from the WKT string. * @param {module:ol/format/WKT~Lexer} lexer The lexer. * @constructor */ -const Parser = function(lexer) { +class Parser { + constructor(lexer) { - /** - * @type {module:ol/format/WKT~Lexer} - * @private - */ - this.lexer_ = lexer; + /** + * @type {module:ol/format/WKT~Lexer} + * @private + */ + this.lexer_ = lexer; - /** - * @type {module:ol/format/WKT~Token} - * @private - */ - this.token_; + /** + * @type {module:ol/format/WKT~Token} + * @private + */ + this.token_; - /** - * @type {module:ol/geom/GeometryLayout} - * @private - */ - this.layout_ = GeometryLayout.XY; -}; - - -/** - * Fetch the next token form the lexer and replace the active token. - * @private - */ -Parser.prototype.consume_ = function() { - this.token_ = this.lexer_.nextToken(); -}; - -/** - * Tests if the given type matches the type of the current token. - * @param {module:ol/format/WKT~TokenType} type Token type. - * @return {boolean} Whether the token matches the given type. - */ -Parser.prototype.isTokenType = function(type) { - const isMatch = this.token_.type == type; - return isMatch; -}; - - -/** - * If the given type matches the current token, consume it. - * @param {module:ol/format/WKT~TokenType} type Token type. - * @return {boolean} Whether the token matches the given type. - */ -Parser.prototype.match = function(type) { - const isMatch = this.isTokenType(type); - if (isMatch) { - this.consume_(); + /** + * @type {module:ol/geom/GeometryLayout} + * @private + */ + this.layout_ = GeometryLayout.XY; } - return isMatch; -}; + /** + * Fetch the next token form the lexer and replace the active token. + * @private + */ + consume_() { + this.token_ = this.lexer_.nextToken(); + } -/** - * Try to parse the tokens provided by the lexer. - * @return {module:ol/geom/Geometry} The geometry. - */ -Parser.prototype.parse = function() { - this.consume_(); - const geometry = this.parseGeometry_(); - return geometry; -}; + /** + * Tests if the given type matches the type of the current token. + * @param {module:ol/format/WKT~TokenType} type Token type. + * @return {boolean} Whether the token matches the given type. + */ + isTokenType(type) { + const isMatch = this.token_.type == type; + return isMatch; + } - -/** - * Try to parse the dimensional info. - * @return {module:ol/geom/GeometryLayout} The layout. - * @private - */ -Parser.prototype.parseGeometryLayout_ = function() { - let layout = GeometryLayout.XY; - const dimToken = this.token_; - if (this.isTokenType(TokenType.TEXT)) { - const dimInfo = dimToken.value; - if (dimInfo === Z) { - layout = GeometryLayout.XYZ; - } else if (dimInfo === M) { - layout = GeometryLayout.XYM; - } else if (dimInfo === ZM) { - layout = GeometryLayout.XYZM; - } - if (layout !== GeometryLayout.XY) { + /** + * If the given type matches the current token, consume it. + * @param {module:ol/format/WKT~TokenType} type Token type. + * @return {boolean} Whether the token matches the given type. + */ + match(type) { + const isMatch = this.isTokenType(type); + if (isMatch) { this.consume_(); } + return isMatch; } - return layout; -}; + /** + * Try to parse the tokens provided by the lexer. + * @return {module:ol/geom/Geometry} The geometry. + */ + parse() { + this.consume_(); + const geometry = this.parseGeometry_(); + return geometry; + } -/** - * @return {!Array.} A collection of geometries. - * @private - */ -Parser.prototype.parseGeometryCollectionText_ = function() { - if (this.match(TokenType.LEFT_PAREN)) { - const geometries = []; - do { - geometries.push(this.parseGeometry_()); - } while (this.match(TokenType.COMMA)); - if (this.match(TokenType.RIGHT_PAREN)) { - return geometries; + /** + * Try to parse the dimensional info. + * @return {module:ol/geom/GeometryLayout} The layout. + * @private + */ + parseGeometryLayout_() { + let layout = GeometryLayout.XY; + const dimToken = this.token_; + if (this.isTokenType(TokenType.TEXT)) { + const dimInfo = dimToken.value; + if (dimInfo === Z) { + layout = GeometryLayout.XYZ; + } else if (dimInfo === M) { + layout = GeometryLayout.XYM; + } else if (dimInfo === ZM) { + layout = GeometryLayout.XYZM; + } + if (layout !== GeometryLayout.XY) { + this.consume_(); + } } - } else if (this.isEmptyGeometry_()) { - return []; + return layout; } - throw new Error(this.formatErrorMessage_()); -}; + /** + * @return {!Array.} A collection of geometries. + * @private + */ + parseGeometryCollectionText_() { + if (this.match(TokenType.LEFT_PAREN)) { + const geometries = []; + do { + geometries.push(this.parseGeometry_()); + } while (this.match(TokenType.COMMA)); + if (this.match(TokenType.RIGHT_PAREN)) { + return geometries; + } + } else if (this.isEmptyGeometry_()) { + return []; + } + throw new Error(this.formatErrorMessage_()); + } -/** - * @return {Array.} All values in a point. - * @private - */ -Parser.prototype.parsePointText_ = function() { - if (this.match(TokenType.LEFT_PAREN)) { - const coordinates = this.parsePoint_(); - if (this.match(TokenType.RIGHT_PAREN)) { + /** + * @return {Array.} All values in a point. + * @private + */ + parsePointText_() { + if (this.match(TokenType.LEFT_PAREN)) { + const coordinates = this.parsePoint_(); + if (this.match(TokenType.RIGHT_PAREN)) { + return coordinates; + } + } else if (this.isEmptyGeometry_()) { + return null; + } + throw new Error(this.formatErrorMessage_()); + } + + /** + * @return {!Array.>} All points in a linestring. + * @private + */ + parseLineStringText_() { + if (this.match(TokenType.LEFT_PAREN)) { + const coordinates = this.parsePointList_(); + if (this.match(TokenType.RIGHT_PAREN)) { + return coordinates; + } + } else if (this.isEmptyGeometry_()) { + return []; + } + throw new Error(this.formatErrorMessage_()); + } + + /** + * @return {!Array.>} All points in a polygon. + * @private + */ + parsePolygonText_() { + if (this.match(TokenType.LEFT_PAREN)) { + const coordinates = this.parseLineStringTextList_(); + if (this.match(TokenType.RIGHT_PAREN)) { + return coordinates; + } + } else if (this.isEmptyGeometry_()) { + return []; + } + throw new Error(this.formatErrorMessage_()); + } + + /** + * @return {!Array.>} All points in a multipoint. + * @private + */ + parseMultiPointText_() { + if (this.match(TokenType.LEFT_PAREN)) { + let coordinates; + if (this.token_.type == TokenType.LEFT_PAREN) { + coordinates = this.parsePointTextList_(); + } else { + coordinates = this.parsePointList_(); + } + if (this.match(TokenType.RIGHT_PAREN)) { + return coordinates; + } + } else if (this.isEmptyGeometry_()) { + return []; + } + throw new Error(this.formatErrorMessage_()); + } + + /** + * @return {!Array.>} All linestring points + * in a multilinestring. + * @private + */ + parseMultiLineStringText_() { + if (this.match(TokenType.LEFT_PAREN)) { + const coordinates = this.parseLineStringTextList_(); + if (this.match(TokenType.RIGHT_PAREN)) { + return coordinates; + } + } else if (this.isEmptyGeometry_()) { + return []; + } + throw new Error(this.formatErrorMessage_()); + } + + /** + * @return {!Array.>} All polygon points in a multipolygon. + * @private + */ + parseMultiPolygonText_() { + if (this.match(TokenType.LEFT_PAREN)) { + const coordinates = this.parsePolygonTextList_(); + if (this.match(TokenType.RIGHT_PAREN)) { + return coordinates; + } + } else if (this.isEmptyGeometry_()) { + return []; + } + throw new Error(this.formatErrorMessage_()); + } + + /** + * @return {!Array.} A point. + * @private + */ + parsePoint_() { + const coordinates = []; + const dimensions = this.layout_.length; + for (let i = 0; i < dimensions; ++i) { + const token = this.token_; + if (this.match(TokenType.NUMBER)) { + coordinates.push(token.value); + } else { + break; + } + } + if (coordinates.length == dimensions) { return coordinates; } - } else if (this.isEmptyGeometry_()) { - return null; + throw new Error(this.formatErrorMessage_()); } - throw new Error(this.formatErrorMessage_()); -}; - -/** - * @return {!Array.>} All points in a linestring. - * @private - */ -Parser.prototype.parseLineStringText_ = function() { - if (this.match(TokenType.LEFT_PAREN)) { - const coordinates = this.parsePointList_(); - if (this.match(TokenType.RIGHT_PAREN)) { - return coordinates; + /** + * @return {!Array.>} An array of points. + * @private + */ + parsePointList_() { + const coordinates = [this.parsePoint_()]; + while (this.match(TokenType.COMMA)) { + coordinates.push(this.parsePoint_()); } - } else if (this.isEmptyGeometry_()) { - return []; - } - throw new Error(this.formatErrorMessage_()); -}; - - -/** - * @return {!Array.>} All points in a polygon. - * @private - */ -Parser.prototype.parsePolygonText_ = function() { - if (this.match(TokenType.LEFT_PAREN)) { - const coordinates = this.parseLineStringTextList_(); - if (this.match(TokenType.RIGHT_PAREN)) { - return coordinates; - } - } else if (this.isEmptyGeometry_()) { - return []; - } - throw new Error(this.formatErrorMessage_()); -}; - - -/** - * @return {!Array.>} All points in a multipoint. - * @private - */ -Parser.prototype.parseMultiPointText_ = function() { - if (this.match(TokenType.LEFT_PAREN)) { - let coordinates; - if (this.token_.type == TokenType.LEFT_PAREN) { - coordinates = this.parsePointTextList_(); - } else { - coordinates = this.parsePointList_(); - } - if (this.match(TokenType.RIGHT_PAREN)) { - return coordinates; - } - } else if (this.isEmptyGeometry_()) { - return []; - } - throw new Error(this.formatErrorMessage_()); -}; - - -/** - * @return {!Array.>} All linestring points - * in a multilinestring. - * @private - */ -Parser.prototype.parseMultiLineStringText_ = function() { - if (this.match(TokenType.LEFT_PAREN)) { - const coordinates = this.parseLineStringTextList_(); - if (this.match(TokenType.RIGHT_PAREN)) { - return coordinates; - } - } else if (this.isEmptyGeometry_()) { - return []; - } - throw new Error(this.formatErrorMessage_()); -}; - - -/** - * @return {!Array.>} All polygon points in a multipolygon. - * @private - */ -Parser.prototype.parseMultiPolygonText_ = function() { - if (this.match(TokenType.LEFT_PAREN)) { - const coordinates = this.parsePolygonTextList_(); - if (this.match(TokenType.RIGHT_PAREN)) { - return coordinates; - } - } else if (this.isEmptyGeometry_()) { - return []; - } - throw new Error(this.formatErrorMessage_()); -}; - - -/** - * @return {!Array.} A point. - * @private - */ -Parser.prototype.parsePoint_ = function() { - const coordinates = []; - const dimensions = this.layout_.length; - for (let i = 0; i < dimensions; ++i) { - const token = this.token_; - if (this.match(TokenType.NUMBER)) { - coordinates.push(token.value); - } else { - break; - } - } - if (coordinates.length == dimensions) { return coordinates; } - throw new Error(this.formatErrorMessage_()); -}; - -/** - * @return {!Array.>} An array of points. - * @private - */ -Parser.prototype.parsePointList_ = function() { - const coordinates = [this.parsePoint_()]; - while (this.match(TokenType.COMMA)) { - coordinates.push(this.parsePoint_()); + /** + * @return {!Array.>} An array of points. + * @private + */ + parsePointTextList_() { + const coordinates = [this.parsePointText_()]; + while (this.match(TokenType.COMMA)) { + coordinates.push(this.parsePointText_()); + } + return coordinates; } - return coordinates; -}; - -/** - * @return {!Array.>} An array of points. - * @private - */ -Parser.prototype.parsePointTextList_ = function() { - const coordinates = [this.parsePointText_()]; - while (this.match(TokenType.COMMA)) { - coordinates.push(this.parsePointText_()); + /** + * @return {!Array.>} An array of points. + * @private + */ + parseLineStringTextList_() { + const coordinates = [this.parseLineStringText_()]; + while (this.match(TokenType.COMMA)) { + coordinates.push(this.parseLineStringText_()); + } + return coordinates; } - return coordinates; -}; - -/** - * @return {!Array.>} An array of points. - * @private - */ -Parser.prototype.parseLineStringTextList_ = function() { - const coordinates = [this.parseLineStringText_()]; - while (this.match(TokenType.COMMA)) { - coordinates.push(this.parseLineStringText_()); + /** + * @return {!Array.>} An array of points. + * @private + */ + parsePolygonTextList_() { + const coordinates = [this.parsePolygonText_()]; + while (this.match(TokenType.COMMA)) { + coordinates.push(this.parsePolygonText_()); + } + return coordinates; } - return coordinates; -}; - -/** - * @return {!Array.>} An array of points. - * @private - */ -Parser.prototype.parsePolygonTextList_ = function() { - const coordinates = [this.parsePolygonText_()]; - while (this.match(TokenType.COMMA)) { - coordinates.push(this.parsePolygonText_()); + /** + * @return {boolean} Whether the token implies an empty geometry. + * @private + */ + isEmptyGeometry_() { + const isEmpty = this.isTokenType(TokenType.TEXT) && + this.token_.value == EMPTY; + if (isEmpty) { + this.consume_(); + } + return isEmpty; } - return coordinates; -}; - -/** - * @return {boolean} Whether the token implies an empty geometry. - * @private - */ -Parser.prototype.isEmptyGeometry_ = function() { - const isEmpty = this.isTokenType(TokenType.TEXT) && - this.token_.value == EMPTY; - if (isEmpty) { - this.consume_(); + /** + * Create an error message for an unexpected token error. + * @return {string} Error message. + * @private + */ + formatErrorMessage_() { + return 'Unexpected `' + this.token_.value + '` at position ' + + this.token_.position + ' in `' + this.lexer_.wkt + '`'; } - return isEmpty; -}; - - -/** - * Create an error message for an unexpected token error. - * @return {string} Error message. - * @private - */ -Parser.prototype.formatErrorMessage_ = function() { - return 'Unexpected `' + this.token_.value + '` at position ' + - this.token_.position + ' in `' + this.lexer_.wkt + '`'; -}; + /** + * @return {!module:ol/geom/Geometry} The geometry. + * @private + */ + parseGeometry_() { + const token = this.token_; + if (this.match(TokenType.TEXT)) { + const geomType = token.value; + this.layout_ = this.parseGeometryLayout_(); + if (geomType == GeometryType.GEOMETRY_COLLECTION.toUpperCase()) { + const geometries = this.parseGeometryCollectionText_(); + return new GeometryCollection(geometries); + } else { + const parser = GeometryParser[geomType]; + const ctor = GeometryConstructor[geomType]; + if (!parser || !ctor) { + throw new Error('Invalid geometry type: ' + geomType); + } + let coordinates = parser.call(this); + if (!coordinates) { + if (ctor === GeometryConstructor[GeometryType.POINT]) { + coordinates = [NaN, NaN]; + } else { + coordinates = []; + } + } + return new ctor(coordinates, this.layout_); + } + } + throw new Error(this.formatErrorMessage_()); + } +} /** * @classdesc @@ -542,21 +551,119 @@ Parser.prototype.formatErrorMessage_ = function() { * @param {module:ol/format/WKT~Options=} opt_options Options. * @api */ -const WKT = function(opt_options) { +class WKT { + constructor(opt_options) { - const options = opt_options ? opt_options : {}; + const options = opt_options ? opt_options : {}; - TextFeature.call(this); + TextFeature.call(this); + + /** + * Split GeometryCollection into multiple features. + * @type {boolean} + * @private + */ + this.splitCollection_ = options.splitCollection !== undefined ? + options.splitCollection : false; + + } /** - * Split GeometryCollection into multiple features. - * @type {boolean} + * Parse a WKT string. + * @param {string} wkt WKT string. + * @return {module:ol/geom/Geometry|undefined} + * The geometry created. * @private */ - this.splitCollection_ = options.splitCollection !== undefined ? - options.splitCollection : false; + parse_(wkt) { + const lexer = new Lexer(wkt); + const parser = new Parser(lexer); + return parser.parse(); + } -}; + /** + * @inheritDoc + */ + readFeatureFromText(text, opt_options) { + const geom = this.readGeometryFromText(text, opt_options); + if (geom) { + const feature = new Feature(); + feature.setGeometry(geom); + return feature; + } + return null; + } + + /** + * @inheritDoc + */ + readFeaturesFromText(text, opt_options) { + let geometries = []; + const geometry = this.readGeometryFromText(text, opt_options); + if (this.splitCollection_ && + geometry.getType() == GeometryType.GEOMETRY_COLLECTION) { + geometries = (/** @type {module:ol/geom/GeometryCollection} */ (geometry)) + .getGeometriesArray(); + } else { + geometries = [geometry]; + } + const features = []; + for (let i = 0, ii = geometries.length; i < ii; ++i) { + const feature = new Feature(); + feature.setGeometry(geometries[i]); + features.push(feature); + } + return features; + } + + /** + * @inheritDoc + */ + readGeometryFromText(text, opt_options) { + const geometry = this.parse_(text); + if (geometry) { + return ( + /** @type {module:ol/geom/Geometry} */ (transformWithOptions(geometry, false, opt_options)) + ); + } else { + return null; + } + } + + /** + * @inheritDoc + */ + writeFeatureText(feature, opt_options) { + const geometry = feature.getGeometry(); + if (geometry) { + return this.writeGeometryText(geometry, opt_options); + } + return ''; + } + + /** + * @inheritDoc + */ + writeFeaturesText(features, opt_options) { + if (features.length == 1) { + return this.writeFeatureText(features[0], opt_options); + } + const geometries = []; + for (let i = 0, ii = features.length; i < ii; ++i) { + geometries.push(features[i].getGeometry()); + } + const collection = new GeometryCollection(geometries); + return this.writeGeometryText(collection, opt_options); + } + + /** + * @inheritDoc + */ + writeGeometryText(geometry, opt_options) { + return encode(/** @type {module:ol/geom/Geometry} */ ( + transformWithOptions(geometry, true, opt_options))); + } +} inherits(WKT, TextFeature); @@ -712,20 +819,6 @@ function encode(geom) { } -/** - * Parse a WKT string. - * @param {string} wkt WKT string. - * @return {module:ol/geom/Geometry|undefined} - * The geometry created. - * @private - */ -WKT.prototype.parse_ = function(wkt) { - const lexer = new Lexer(wkt); - const parser = new Parser(lexer); - return parser.parse(); -}; - - /** * Read a feature from a WKT source. * @@ -738,20 +831,6 @@ WKT.prototype.parse_ = function(wkt) { WKT.prototype.readFeature; -/** - * @inheritDoc - */ -WKT.prototype.readFeatureFromText = function(text, opt_options) { - const geom = this.readGeometryFromText(text, opt_options); - if (geom) { - const feature = new Feature(); - feature.setGeometry(geom); - return feature; - } - return null; -}; - - /** * Read all features from a WKT source. * @@ -764,29 +843,6 @@ WKT.prototype.readFeatureFromText = function(text, opt_options) { WKT.prototype.readFeatures; -/** - * @inheritDoc - */ -WKT.prototype.readFeaturesFromText = function(text, opt_options) { - let geometries = []; - const geometry = this.readGeometryFromText(text, opt_options); - if (this.splitCollection_ && - geometry.getType() == GeometryType.GEOMETRY_COLLECTION) { - geometries = (/** @type {module:ol/geom/GeometryCollection} */ (geometry)) - .getGeometriesArray(); - } else { - geometries = [geometry]; - } - const features = []; - for (let i = 0, ii = geometries.length; i < ii; ++i) { - const feature = new Feature(); - feature.setGeometry(geometries[i]); - features.push(feature); - } - return features; -}; - - /** * Read a single geometry from a WKT source. * @@ -799,21 +855,6 @@ WKT.prototype.readFeaturesFromText = function(text, opt_options) { WKT.prototype.readGeometry; -/** - * @inheritDoc - */ -WKT.prototype.readGeometryFromText = function(text, opt_options) { - const geometry = this.parse_(text); - if (geometry) { - return ( - /** @type {module:ol/geom/Geometry} */ (transformWithOptions(geometry, false, opt_options)) - ); - } else { - return null; - } -}; - - /** * @enum {function (new:module:ol/geom/Geometry, Array, module:ol/geom/GeometryLayout)} */ @@ -840,39 +881,6 @@ const GeometryParser = { }; -/** - * @return {!module:ol/geom/Geometry} The geometry. - * @private - */ -Parser.prototype.parseGeometry_ = function() { - const token = this.token_; - if (this.match(TokenType.TEXT)) { - const geomType = token.value; - this.layout_ = this.parseGeometryLayout_(); - if (geomType == GeometryType.GEOMETRY_COLLECTION.toUpperCase()) { - const geometries = this.parseGeometryCollectionText_(); - return new GeometryCollection(geometries); - } else { - const parser = GeometryParser[geomType]; - const ctor = GeometryConstructor[geomType]; - if (!parser || !ctor) { - throw new Error('Invalid geometry type: ' + geomType); - } - let coordinates = parser.call(this); - if (!coordinates) { - if (ctor === GeometryConstructor[GeometryType.POINT]) { - coordinates = [NaN, NaN]; - } else { - coordinates = []; - } - } - return new ctor(coordinates, this.layout_); - } - } - throw new Error(this.formatErrorMessage_()); -}; - - /** * Encode a feature as a WKT string. * @@ -885,18 +893,6 @@ Parser.prototype.parseGeometry_ = function() { WKT.prototype.writeFeature; -/** - * @inheritDoc - */ -WKT.prototype.writeFeatureText = function(feature, opt_options) { - const geometry = feature.getGeometry(); - if (geometry) { - return this.writeGeometryText(geometry, opt_options); - } - return ''; -}; - - /** * Encode an array of features as a WKT string. * @@ -909,22 +905,6 @@ WKT.prototype.writeFeatureText = function(feature, opt_options) { WKT.prototype.writeFeatures; -/** - * @inheritDoc - */ -WKT.prototype.writeFeaturesText = function(features, opt_options) { - if (features.length == 1) { - return this.writeFeatureText(features[0], opt_options); - } - const geometries = []; - for (let i = 0, ii = features.length; i < ii; ++i) { - geometries.push(features[i].getGeometry()); - } - const collection = new GeometryCollection(geometries); - return this.writeGeometryText(collection, opt_options); -}; - - /** * Write a single geometry as a WKT string. * @@ -937,13 +917,4 @@ WKT.prototype.writeFeaturesText = function(features, opt_options) { WKT.prototype.writeGeometry; -/** - * @inheritDoc - */ -WKT.prototype.writeGeometryText = function(geometry, opt_options) { - return encode(/** @type {module:ol/geom/Geometry} */ ( - transformWithOptions(geometry, true, opt_options))); -}; - - export default WKT; diff --git a/src/ol/format/WMSCapabilities.js b/src/ol/format/WMSCapabilities.js index 59e43e9aa7..b87177196a 100644 --- a/src/ol/format/WMSCapabilities.js +++ b/src/ol/format/WMSCapabilities.js @@ -17,15 +17,40 @@ import {makeArrayPusher, makeObjectPropertyPusher, makeObjectPropertySetter, * @extends {module:ol/format/XML} * @api */ -const WMSCapabilities = function() { +class WMSCapabilities { + constructor() { - XML.call(this); + XML.call(this); + + /** + * @type {string|undefined} + */ + this.version = undefined; + } /** - * @type {string|undefined} + * @inheritDoc */ - this.version = undefined; -}; + readFromDocument(doc) { + for (let n = doc.firstChild; n; n = n.nextSibling) { + if (n.nodeType == Node.ELEMENT_NODE) { + return this.readFromNode(n); + } + } + return null; + } + + /** + * @inheritDoc + */ + readFromNode(node) { + this.version = node.getAttribute('version').trim(); + const wmsCapabilityObject = pushParseAndPop({ + 'version': this.version + }, PARSERS, node, []); + return wmsCapabilityObject ? wmsCapabilityObject : null; + } +} inherits(WMSCapabilities, XML); @@ -277,31 +302,6 @@ const KEYWORDLIST_PARSERS = makeStructureNS( WMSCapabilities.prototype.read; -/** - * @inheritDoc - */ -WMSCapabilities.prototype.readFromDocument = function(doc) { - for (let n = doc.firstChild; n; n = n.nextSibling) { - if (n.nodeType == Node.ELEMENT_NODE) { - return this.readFromNode(n); - } - } - return null; -}; - - -/** - * @inheritDoc - */ -WMSCapabilities.prototype.readFromNode = function(node) { - this.version = node.getAttribute('version').trim(); - const wmsCapabilityObject = pushParseAndPop({ - 'version': this.version - }, PARSERS, node, []); - return wmsCapabilityObject ? wmsCapabilityObject : null; -}; - - /** * @param {Node} node Node. * @param {Array.<*>} objectStack Object stack. diff --git a/src/ol/format/WMSGetFeatureInfo.js b/src/ol/format/WMSGetFeatureInfo.js index 4591bbdc8a..7f84c1f99f 100644 --- a/src/ol/format/WMSGetFeatureInfo.js +++ b/src/ol/format/WMSGetFeatureInfo.js @@ -25,32 +25,136 @@ import {makeArrayPusher, makeStructureNS, pushParseAndPop} from '../xml.js'; * @param {module:ol/format/WMSGetFeatureInfo~Options=} opt_options Options. * @api */ -const WMSGetFeatureInfo = function(opt_options) { +class WMSGetFeatureInfo { + constructor(opt_options) { - const options = opt_options ? opt_options : {}; + const options = opt_options ? opt_options : {}; + + /** + * @private + * @type {string} + */ + this.featureNS_ = 'http://mapserver.gis.umn.edu/mapserver'; + + + /** + * @private + * @type {module:ol/format/GML2} + */ + this.gmlFormat_ = new GML2(); + + + /** + * @private + * @type {Array.} + */ + this.layers_ = options.layers ? options.layers : null; + + XMLFeature.call(this); + } /** - * @private - * @type {string} + * @return {Array.} layers */ - this.featureNS_ = 'http://mapserver.gis.umn.edu/mapserver'; - + getLayers() { + return this.layers_; + } /** - * @private - * @type {module:ol/format/GML2} + * @param {Array.} layers Layers to parse. */ - this.gmlFormat_ = new GML2(); - + setLayers(layers) { + this.layers_ = layers; + } /** + * @param {Node} node Node. + * @param {Array.<*>} objectStack Object stack. + * @return {Array.} Features. * @private - * @type {Array.} */ - this.layers_ = options.layers ? options.layers : null; + readFeatures_(node, objectStack) { + node.setAttribute('namespaceURI', this.featureNS_); + const localName = node.localName; + /** @type {Array.} */ + let features = []; + if (node.childNodes.length === 0) { + return features; + } + if (localName == 'msGMLOutput') { + for (let i = 0, ii = node.childNodes.length; i < ii; i++) { + const layer = node.childNodes[i]; + if (layer.nodeType !== Node.ELEMENT_NODE) { + continue; + } + const context = objectStack[0]; - XMLFeature.call(this); -}; + const toRemove = layerIdentifier; + const layerName = layer.localName.replace(toRemove, ''); + + if (this.layers_ && !includes(this.layers_, layerName)) { + continue; + } + + const featureType = layerName + + featureIdentifier; + + context['featureType'] = featureType; + context['featureNS'] = this.featureNS_; + + const parsers = {}; + parsers[featureType] = makeArrayPusher( + this.gmlFormat_.readFeatureElement, this.gmlFormat_); + const parsersNS = makeStructureNS( + [context['featureNS'], null], parsers); + layer.setAttribute('namespaceURI', this.featureNS_); + const layerFeatures = pushParseAndPop( + [], parsersNS, layer, objectStack, this.gmlFormat_); + if (layerFeatures) { + extend(features, layerFeatures); + } + } + } + if (localName == 'FeatureCollection') { + const gmlFeatures = pushParseAndPop([], + this.gmlFormat_.FEATURE_COLLECTION_PARSERS, node, + [{}], this.gmlFormat_); + if (gmlFeatures) { + features = gmlFeatures; + } + } + return features; + } + + /** + * @inheritDoc + */ + readFeaturesFromNode(node, opt_options) { + const options = {}; + if (opt_options) { + assign(options, this.getReadOptions(node, opt_options)); + } + return this.readFeatures_(node, [options]); + } + + /** + * Not implemented. + * @inheritDoc + */ + writeFeatureNode(feature, opt_options) {} + + /** + * Not implemented. + * @inheritDoc + */ + writeFeaturesNode(features, opt_options) {} + + /** + * Not implemented. + * @inheritDoc + */ + writeGeometryNode(geometry, opt_options) {} +} inherits(WMSGetFeatureInfo, XMLFeature); @@ -69,82 +173,6 @@ const featureIdentifier = '_feature'; const layerIdentifier = '_layer'; -/** - * @return {Array.} layers - */ -WMSGetFeatureInfo.prototype.getLayers = function() { - return this.layers_; -}; - - -/** - * @param {Array.} layers Layers to parse. - */ -WMSGetFeatureInfo.prototype.setLayers = function(layers) { - this.layers_ = layers; -}; - - -/** - * @param {Node} node Node. - * @param {Array.<*>} objectStack Object stack. - * @return {Array.} Features. - * @private - */ -WMSGetFeatureInfo.prototype.readFeatures_ = function(node, objectStack) { - node.setAttribute('namespaceURI', this.featureNS_); - const localName = node.localName; - /** @type {Array.} */ - let features = []; - if (node.childNodes.length === 0) { - return features; - } - if (localName == 'msGMLOutput') { - for (let i = 0, ii = node.childNodes.length; i < ii; i++) { - const layer = node.childNodes[i]; - if (layer.nodeType !== Node.ELEMENT_NODE) { - continue; - } - const context = objectStack[0]; - - const toRemove = layerIdentifier; - const layerName = layer.localName.replace(toRemove, ''); - - if (this.layers_ && !includes(this.layers_, layerName)) { - continue; - } - - const featureType = layerName + - featureIdentifier; - - context['featureType'] = featureType; - context['featureNS'] = this.featureNS_; - - const parsers = {}; - parsers[featureType] = makeArrayPusher( - this.gmlFormat_.readFeatureElement, this.gmlFormat_); - const parsersNS = makeStructureNS( - [context['featureNS'], null], parsers); - layer.setAttribute('namespaceURI', this.featureNS_); - const layerFeatures = pushParseAndPop( - [], parsersNS, layer, objectStack, this.gmlFormat_); - if (layerFeatures) { - extend(features, layerFeatures); - } - } - } - if (localName == 'FeatureCollection') { - const gmlFeatures = pushParseAndPop([], - this.gmlFormat_.FEATURE_COLLECTION_PARSERS, node, - [{}], this.gmlFormat_); - if (gmlFeatures) { - features = gmlFeatures; - } - } - return features; -}; - - /** * Read all features from a WMSGetFeatureInfo response. * @@ -157,35 +185,4 @@ WMSGetFeatureInfo.prototype.readFeatures_ = function(node, objectStack) { WMSGetFeatureInfo.prototype.readFeatures; -/** - * @inheritDoc - */ -WMSGetFeatureInfo.prototype.readFeaturesFromNode = function(node, opt_options) { - const options = {}; - if (opt_options) { - assign(options, this.getReadOptions(node, opt_options)); - } - return this.readFeatures_(node, [options]); -}; - - -/** - * Not implemented. - * @inheritDoc - */ -WMSGetFeatureInfo.prototype.writeFeatureNode = function(feature, opt_options) {}; - - -/** - * Not implemented. - * @inheritDoc - */ -WMSGetFeatureInfo.prototype.writeFeaturesNode = function(features, opt_options) {}; - - -/** - * Not implemented. - * @inheritDoc - */ -WMSGetFeatureInfo.prototype.writeGeometryNode = function(geometry, opt_options) {}; export default WMSGetFeatureInfo; diff --git a/src/ol/format/WMTSCapabilities.js b/src/ol/format/WMTSCapabilities.js index 30463e93d7..fa46d1f218 100644 --- a/src/ol/format/WMTSCapabilities.js +++ b/src/ol/format/WMTSCapabilities.js @@ -18,15 +18,43 @@ import {pushParseAndPop, makeStructureNS, * @extends {module:ol/format/XML} * @api */ -const WMTSCapabilities = function() { - XML.call(this); +class WMTSCapabilities { + constructor() { + XML.call(this); + + /** + * @type {module:ol/format/OWS} + * @private + */ + this.owsParser_ = new OWS(); + } /** - * @type {module:ol/format/OWS} - * @private + * @inheritDoc */ - this.owsParser_ = new OWS(); -}; + readFromDocument(doc) { + for (let n = doc.firstChild; n; n = n.nextSibling) { + if (n.nodeType == Node.ELEMENT_NODE) { + return this.readFromNode(n); + } + } + return null; + } + + /** + * @inheritDoc + */ + readFromNode(node) { + const version = node.getAttribute('version').trim(); + let WMTSCapabilityObject = this.owsParser_.readFromNode(node); + if (!WMTSCapabilityObject) { + return null; + } + WMTSCapabilityObject['version'] = version; + WMTSCapabilityObject = pushParseAndPop(WMTSCapabilityObject, PARSERS, node, []); + return WMTSCapabilityObject ? WMTSCapabilityObject : null; + } +} inherits(WMTSCapabilities, XML); @@ -204,34 +232,6 @@ const TM_PARSERS = makeStructureNS( WMTSCapabilities.prototype.read; -/** - * @inheritDoc - */ -WMTSCapabilities.prototype.readFromDocument = function(doc) { - for (let n = doc.firstChild; n; n = n.nextSibling) { - if (n.nodeType == Node.ELEMENT_NODE) { - return this.readFromNode(n); - } - } - return null; -}; - - -/** - * @inheritDoc - */ -WMTSCapabilities.prototype.readFromNode = function(node) { - const version = node.getAttribute('version').trim(); - let WMTSCapabilityObject = this.owsParser_.readFromNode(node); - if (!WMTSCapabilityObject) { - return null; - } - WMTSCapabilityObject['version'] = version; - WMTSCapabilityObject = pushParseAndPop(WMTSCapabilityObject, PARSERS, node, []); - return WMTSCapabilityObject ? WMTSCapabilityObject : null; -}; - - /** * @param {Node} node Node. * @param {Array.<*>} objectStack Object stack. diff --git a/src/ol/format/XML.js b/src/ol/format/XML.js index 4f828ae17e..1dd06cfd9f 100644 --- a/src/ol/format/XML.js +++ b/src/ol/format/XML.js @@ -11,40 +11,37 @@ import {isDocument, isNode, parse} from '../xml.js'; * @abstract * @struct */ -const XML = function() { -}; +class XML { + /** + * @param {Document|Node|string} source Source. + * @return {Object} The parsed result. + */ + read(source) { + if (isDocument(source)) { + return this.readFromDocument(/** @type {Document} */ (source)); + } else if (isNode(source)) { + return this.readFromNode(/** @type {Node} */ (source)); + } else if (typeof source === 'string') { + const doc = parse(source); + return this.readFromDocument(doc); + } else { + return null; + } + } + /** + * @abstract + * @param {Document} doc Document. + * @return {Object} Object + */ + readFromDocument(doc) {} -/** - * @param {Document|Node|string} source Source. - * @return {Object} The parsed result. - */ -XML.prototype.read = function(source) { - if (isDocument(source)) { - return this.readFromDocument(/** @type {Document} */ (source)); - } else if (isNode(source)) { - return this.readFromNode(/** @type {Node} */ (source)); - } else if (typeof source === 'string') { - const doc = parse(source); - return this.readFromDocument(doc); - } else { - return null; - } -}; + /** + * @abstract + * @param {Node} node Node. + * @return {Object} Object + */ + readFromNode(node) {} +} - -/** - * @abstract - * @param {Document} doc Document. - * @return {Object} Object - */ -XML.prototype.readFromDocument = function(doc) {}; - - -/** - * @abstract - * @param {Node} node Node. - * @return {Object} Object - */ -XML.prototype.readFromNode = function(node) {}; export default XML; diff --git a/src/ol/format/XMLFeature.js b/src/ol/format/XMLFeature.js index ec3adb5bf1..5365257d39 100644 --- a/src/ol/format/XMLFeature.js +++ b/src/ol/format/XMLFeature.js @@ -17,247 +17,232 @@ import {isDocument, isNode, parse} from '../xml.js'; * @abstract * @extends {module:ol/format/Feature} */ -const XMLFeature = function() { +class XMLFeature { + constructor() { + + /** + * @type {XMLSerializer} + * @private + */ + this.xmlSerializer_ = new XMLSerializer(); + + FeatureFormat.call(this); + } /** - * @type {XMLSerializer} - * @private + * @inheritDoc */ - this.xmlSerializer_ = new XMLSerializer(); + getType() { + return FormatType.XML; + } - FeatureFormat.call(this); -}; + /** + * @inheritDoc + */ + 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); + } else if (typeof source === 'string') { + const doc = parse(source); + return this.readFeatureFromDocument(doc, opt_options); + } else { + return null; + } + } + + /** + * @param {Document} doc Document. + * @param {module:ol/format/Feature~ReadOptions=} opt_options Options. + * @return {module:ol/Feature} Feature. + */ + readFeatureFromDocument(doc, opt_options) { + const features = this.readFeaturesFromDocument(doc, opt_options); + if (features.length > 0) { + return features[0]; + } else { + return null; + } + } + + /** + * @param {Node} node Node. + * @param {module:ol/format/Feature~ReadOptions=} opt_options Options. + * @return {module:ol/Feature} Feature. + */ + readFeatureFromNode(node, opt_options) { + return null; // not implemented + } + + /** + * @inheritDoc + */ + 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); + } else if (typeof source === 'string') { + const doc = parse(source); + return this.readFeaturesFromDocument(doc, opt_options); + } else { + return []; + } + } + + /** + * @param {Document} doc Document. + * @param {module:ol/format/Feature~ReadOptions=} opt_options Options. + * @protected + * @return {Array.} Features. + */ + readFeaturesFromDocument(doc, opt_options) { + /** @type {Array.} */ + const features = []; + for (let n = doc.firstChild; n; n = n.nextSibling) { + if (n.nodeType == Node.ELEMENT_NODE) { + extend(features, this.readFeaturesFromNode(n, opt_options)); + } + } + return features; + } + + /** + * @abstract + * @param {Node} node Node. + * @param {module:ol/format/Feature~ReadOptions=} opt_options Options. + * @protected + * @return {Array.} Features. + */ + readFeaturesFromNode(node, opt_options) {} + + /** + * @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); + } else if (typeof source === 'string') { + const doc = parse(source); + return this.readGeometryFromDocument(doc, opt_options); + } else { + return null; + } + } + + /** + * @param {Document} doc Document. + * @param {module:ol/format/Feature~ReadOptions=} opt_options Options. + * @protected + * @return {module:ol/geom/Geometry} Geometry. + */ + readGeometryFromDocument(doc, opt_options) { + return null; // not implemented + } + + /** + * @param {Node} node Node. + * @param {module:ol/format/Feature~ReadOptions=} opt_options Options. + * @protected + * @return {module:ol/geom/Geometry} Geometry. + */ + readGeometryFromNode(node, opt_options) { + return null; // not implemented + } + + /** + * @inheritDoc + */ + readProjection(source) { + if (isDocument(source)) { + return this.readProjectionFromDocument(/** @type {Document} */ (source)); + } else if (isNode(source)) { + return this.readProjectionFromNode(/** @type {Node} */ (source)); + } else if (typeof source === 'string') { + const doc = parse(source); + return this.readProjectionFromDocument(doc); + } else { + return null; + } + } + + /** + * @param {Document} doc Document. + * @protected + * @return {module:ol/proj/Projection} Projection. + */ + readProjectionFromDocument(doc) { + return this.dataProjection; + } + + /** + * @param {Node} node Node. + * @protected + * @return {module:ol/proj/Projection} Projection. + */ + readProjectionFromNode(node) { + return this.dataProjection; + } + + /** + * @inheritDoc + */ + writeFeature(feature, opt_options) { + const node = this.writeFeatureNode(feature, opt_options); + return this.xmlSerializer_.serializeToString(node); + } + + /** + * @param {module:ol/Feature} feature Feature. + * @param {module:ol/format/Feature~WriteOptions=} opt_options Options. + * @protected + * @return {Node} Node. + */ + writeFeatureNode(feature, opt_options) { + return null; // not implemented + } + + /** + * @inheritDoc + */ + writeFeatures(features, opt_options) { + const node = this.writeFeaturesNode(features, opt_options); + return this.xmlSerializer_.serializeToString(node); + } + + /** + * @param {Array.} features Features. + * @param {module:ol/format/Feature~WriteOptions=} opt_options Options. + * @return {Node} Node. + */ + writeFeaturesNode(features, opt_options) { + return null; // not implemented + } + + /** + * @inheritDoc + */ + writeGeometry(geometry, opt_options) { + const node = this.writeGeometryNode(geometry, opt_options); + return this.xmlSerializer_.serializeToString(node); + } + + /** + * @param {module:ol/geom/Geometry} geometry Geometry. + * @param {module:ol/format/Feature~WriteOptions=} opt_options Options. + * @return {Node} Node. + */ + writeGeometryNode(geometry, opt_options) { + return null; // not implemented + } +} inherits(XMLFeature, FeatureFormat); -/** - * @inheritDoc - */ -XMLFeature.prototype.getType = function() { - return FormatType.XML; -}; - - -/** - * @inheritDoc - */ -XMLFeature.prototype.readFeature = function(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); - } else if (typeof source === 'string') { - const doc = parse(source); - return this.readFeatureFromDocument(doc, opt_options); - } else { - return null; - } -}; - - -/** - * @param {Document} doc Document. - * @param {module:ol/format/Feature~ReadOptions=} opt_options Options. - * @return {module:ol/Feature} Feature. - */ -XMLFeature.prototype.readFeatureFromDocument = function(doc, opt_options) { - const features = this.readFeaturesFromDocument(doc, opt_options); - if (features.length > 0) { - return features[0]; - } else { - return null; - } -}; - - -/** - * @param {Node} node Node. - * @param {module:ol/format/Feature~ReadOptions=} opt_options Options. - * @return {module:ol/Feature} Feature. - */ -XMLFeature.prototype.readFeatureFromNode = function(node, opt_options) { - return null; // not implemented -}; - - -/** - * @inheritDoc - */ -XMLFeature.prototype.readFeatures = function(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); - } else if (typeof source === 'string') { - const doc = parse(source); - return this.readFeaturesFromDocument(doc, opt_options); - } else { - return []; - } -}; - - -/** - * @param {Document} doc Document. - * @param {module:ol/format/Feature~ReadOptions=} opt_options Options. - * @protected - * @return {Array.} Features. - */ -XMLFeature.prototype.readFeaturesFromDocument = function(doc, opt_options) { - /** @type {Array.} */ - const features = []; - for (let n = doc.firstChild; n; n = n.nextSibling) { - if (n.nodeType == Node.ELEMENT_NODE) { - extend(features, this.readFeaturesFromNode(n, opt_options)); - } - } - return features; -}; - - -/** - * @abstract - * @param {Node} node Node. - * @param {module:ol/format/Feature~ReadOptions=} opt_options Options. - * @protected - * @return {Array.} Features. - */ -XMLFeature.prototype.readFeaturesFromNode = function(node, opt_options) {}; - - -/** - * @inheritDoc - */ -XMLFeature.prototype.readGeometry = function(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); - } else if (typeof source === 'string') { - const doc = parse(source); - return this.readGeometryFromDocument(doc, opt_options); - } else { - return null; - } -}; - - -/** - * @param {Document} doc Document. - * @param {module:ol/format/Feature~ReadOptions=} opt_options Options. - * @protected - * @return {module:ol/geom/Geometry} Geometry. - */ -XMLFeature.prototype.readGeometryFromDocument = function(doc, opt_options) { - return null; // not implemented -}; - - -/** - * @param {Node} node Node. - * @param {module:ol/format/Feature~ReadOptions=} opt_options Options. - * @protected - * @return {module:ol/geom/Geometry} Geometry. - */ -XMLFeature.prototype.readGeometryFromNode = function(node, opt_options) { - return null; // not implemented -}; - - -/** - * @inheritDoc - */ -XMLFeature.prototype.readProjection = function(source) { - if (isDocument(source)) { - return this.readProjectionFromDocument(/** @type {Document} */ (source)); - } else if (isNode(source)) { - return this.readProjectionFromNode(/** @type {Node} */ (source)); - } else if (typeof source === 'string') { - const doc = parse(source); - return this.readProjectionFromDocument(doc); - } else { - return null; - } -}; - - -/** - * @param {Document} doc Document. - * @protected - * @return {module:ol/proj/Projection} Projection. - */ -XMLFeature.prototype.readProjectionFromDocument = function(doc) { - return this.dataProjection; -}; - - -/** - * @param {Node} node Node. - * @protected - * @return {module:ol/proj/Projection} Projection. - */ -XMLFeature.prototype.readProjectionFromNode = function(node) { - return this.dataProjection; -}; - - -/** - * @inheritDoc - */ -XMLFeature.prototype.writeFeature = function(feature, opt_options) { - const node = this.writeFeatureNode(feature, opt_options); - return this.xmlSerializer_.serializeToString(node); -}; - - -/** - * @param {module:ol/Feature} feature Feature. - * @param {module:ol/format/Feature~WriteOptions=} opt_options Options. - * @protected - * @return {Node} Node. - */ -XMLFeature.prototype.writeFeatureNode = function(feature, opt_options) { - return null; // not implemented -}; - - -/** - * @inheritDoc - */ -XMLFeature.prototype.writeFeatures = function(features, opt_options) { - const node = this.writeFeaturesNode(features, opt_options); - return this.xmlSerializer_.serializeToString(node); -}; - - -/** - * @param {Array.} features Features. - * @param {module:ol/format/Feature~WriteOptions=} opt_options Options. - * @return {Node} Node. - */ -XMLFeature.prototype.writeFeaturesNode = function(features, opt_options) { - return null; // not implemented -}; - - -/** - * @inheritDoc - */ -XMLFeature.prototype.writeGeometry = function(geometry, opt_options) { - const node = this.writeGeometryNode(geometry, opt_options); - return this.xmlSerializer_.serializeToString(node); -}; - - -/** - * @param {module:ol/geom/Geometry} geometry Geometry. - * @param {module:ol/format/Feature~WriteOptions=} opt_options Options. - * @return {Node} Node. - */ -XMLFeature.prototype.writeGeometryNode = function(geometry, opt_options) { - return null; // not implemented -}; export default XMLFeature; diff --git a/src/ol/format/filter/Filter.js b/src/ol/format/filter/Filter.js index 7ec8c2f36e..0f6f538124 100644 --- a/src/ol/format/filter/Filter.js +++ b/src/ol/format/filter/Filter.js @@ -13,21 +13,23 @@ * @param {!string} tagName The XML tag name for this filter. * @struct */ -const Filter = function(tagName) { +class Filter { + constructor(tagName) { - /** - * @private - * @type {!string} - */ - this.tagName_ = tagName; -}; + /** + * @private + * @type {!string} + */ + this.tagName_ = tagName; + } -/** - * The XML tag name for a filter. - * @returns {!string} Name. - */ -Filter.prototype.getTagName = function() { - return this.tagName_; -}; + /** + * The XML tag name for a filter. + * @returns {!string} Name. + */ + getTagName() { + return this.tagName_; + } +} export default Filter; diff --git a/src/ol/geom/Circle.js b/src/ol/geom/Circle.js index 2864866be9..a3528fd277 100644 --- a/src/ol/geom/Circle.js +++ b/src/ol/geom/Circle.js @@ -20,213 +20,201 @@ import {deflateCoordinate} from '../geom/flat/deflate.js'; * @param {module:ol/geom/GeometryLayout=} opt_layout Layout. * @api */ -const Circle = function(center, opt_radius, opt_layout) { - SimpleGeometry.call(this); - if (opt_layout !== undefined && opt_radius === undefined) { - this.setFlatCoordinates(opt_layout, center); - } else { - const radius = opt_radius ? opt_radius : 0; - this.setCenterAndRadius(center, radius, opt_layout); +class Circle { + constructor(center, opt_radius, opt_layout) { + SimpleGeometry.call(this); + if (opt_layout !== undefined && opt_radius === undefined) { + this.setFlatCoordinates(opt_layout, center); + } else { + const radius = opt_radius ? opt_radius : 0; + this.setCenterAndRadius(center, radius, opt_layout); + } } -}; + + /** + * Make a complete copy of the geometry. + * @return {!module:ol/geom/Circle} Clone. + * @override + * @api + */ + clone() { + return new Circle(this.flatCoordinates.slice(), undefined, this.layout); + } + + /** + * @inheritDoc + */ + closestPointXY(x, y, closestPoint, minSquaredDistance) { + const flatCoordinates = this.flatCoordinates; + const dx = x - flatCoordinates[0]; + const dy = y - flatCoordinates[1]; + const squaredDistance = dx * dx + dy * dy; + if (squaredDistance < minSquaredDistance) { + if (squaredDistance === 0) { + for (let i = 0; i < this.stride; ++i) { + closestPoint[i] = flatCoordinates[i]; + } + } else { + const delta = this.getRadius() / Math.sqrt(squaredDistance); + closestPoint[0] = flatCoordinates[0] + delta * dx; + closestPoint[1] = flatCoordinates[1] + delta * dy; + for (let i = 2; i < this.stride; ++i) { + closestPoint[i] = flatCoordinates[i]; + } + } + closestPoint.length = this.stride; + return squaredDistance; + } else { + return minSquaredDistance; + } + } + + /** + * @inheritDoc + */ + containsXY(x, y) { + const flatCoordinates = this.flatCoordinates; + const dx = x - flatCoordinates[0]; + const dy = y - flatCoordinates[1]; + return dx * dx + dy * dy <= this.getRadiusSquared_(); + } + + /** + * Return the center of the circle as {@link module:ol/coordinate~Coordinate coordinate}. + * @return {module:ol/coordinate~Coordinate} Center. + * @api + */ + getCenter() { + return this.flatCoordinates.slice(0, this.stride); + } + + /** + * @inheritDoc + */ + computeExtent(extent) { + const flatCoordinates = this.flatCoordinates; + const radius = flatCoordinates[this.stride] - flatCoordinates[0]; + return createOrUpdate( + flatCoordinates[0] - radius, flatCoordinates[1] - radius, + flatCoordinates[0] + radius, flatCoordinates[1] + radius, + extent); + } + + /** + * Return the radius of the circle. + * @return {number} Radius. + * @api + */ + getRadius() { + return Math.sqrt(this.getRadiusSquared_()); + } + + /** + * @private + * @return {number} Radius squared. + */ + getRadiusSquared_() { + const dx = this.flatCoordinates[this.stride] - this.flatCoordinates[0]; + const dy = this.flatCoordinates[this.stride + 1] - this.flatCoordinates[1]; + return dx * dx + dy * dy; + } + + /** + * @inheritDoc + * @api + */ + getType() { + return GeometryType.CIRCLE; + } + + /** + * @inheritDoc + * @api + */ + intersectsExtent(extent) { + const circleExtent = this.getExtent(); + if (intersects(extent, circleExtent)) { + const center = this.getCenter(); + + if (extent[0] <= center[0] && extent[2] >= center[0]) { + return true; + } + if (extent[1] <= center[1] && extent[3] >= center[1]) { + return true; + } + + return forEachCorner(extent, this.intersectsCoordinate, this); + } + return false; + + } + + /** + * Set the center of the circle as {@link module:ol/coordinate~Coordinate coordinate}. + * @param {module:ol/coordinate~Coordinate} center Center. + * @api + */ + setCenter(center) { + const stride = this.stride; + const radius = this.flatCoordinates[stride] - this.flatCoordinates[0]; + const flatCoordinates = center.slice(); + flatCoordinates[stride] = flatCoordinates[0] + radius; + for (let i = 1; i < stride; ++i) { + flatCoordinates[stride + i] = center[i]; + } + this.setFlatCoordinates(this.layout, flatCoordinates); + this.changed(); + } + + /** + * Set the center (as {@link module:ol/coordinate~Coordinate coordinate}) and the radius (as + * number) of the circle. + * @param {!module:ol/coordinate~Coordinate} center Center. + * @param {number} radius Radius. + * @param {module:ol/geom/GeometryLayout=} opt_layout Layout. + * @api + */ + setCenterAndRadius(center, radius, opt_layout) { + this.setLayout(opt_layout, center, 0); + if (!this.flatCoordinates) { + this.flatCoordinates = []; + } + /** @type {Array.} */ + const flatCoordinates = this.flatCoordinates; + let offset = deflateCoordinate( + flatCoordinates, 0, center, this.stride); + flatCoordinates[offset++] = flatCoordinates[0] + radius; + for (let i = 1, ii = this.stride; i < ii; ++i) { + flatCoordinates[offset++] = flatCoordinates[i]; + } + flatCoordinates.length = offset; + this.changed(); + } + + /** + * @inheritDoc + */ + getCoordinates() {} + + /** + * @inheritDoc + */ + setCoordinates(coordinates, opt_layout) {} + + /** + * Set the radius of the circle. The radius is in the units of the projection. + * @param {number} radius Radius. + * @api + */ + setRadius(radius) { + this.flatCoordinates[this.stride] = this.flatCoordinates[0] + radius; + this.changed(); + } +} inherits(Circle, SimpleGeometry); -/** - * Make a complete copy of the geometry. - * @return {!module:ol/geom/Circle} Clone. - * @override - * @api - */ -Circle.prototype.clone = function() { - return new Circle(this.flatCoordinates.slice(), undefined, this.layout); -}; - - -/** - * @inheritDoc - */ -Circle.prototype.closestPointXY = function(x, y, closestPoint, minSquaredDistance) { - const flatCoordinates = this.flatCoordinates; - const dx = x - flatCoordinates[0]; - const dy = y - flatCoordinates[1]; - const squaredDistance = dx * dx + dy * dy; - if (squaredDistance < minSquaredDistance) { - if (squaredDistance === 0) { - for (let i = 0; i < this.stride; ++i) { - closestPoint[i] = flatCoordinates[i]; - } - } else { - const delta = this.getRadius() / Math.sqrt(squaredDistance); - closestPoint[0] = flatCoordinates[0] + delta * dx; - closestPoint[1] = flatCoordinates[1] + delta * dy; - for (let i = 2; i < this.stride; ++i) { - closestPoint[i] = flatCoordinates[i]; - } - } - closestPoint.length = this.stride; - return squaredDistance; - } else { - return minSquaredDistance; - } -}; - - -/** - * @inheritDoc - */ -Circle.prototype.containsXY = function(x, y) { - const flatCoordinates = this.flatCoordinates; - const dx = x - flatCoordinates[0]; - const dy = y - flatCoordinates[1]; - return dx * dx + dy * dy <= this.getRadiusSquared_(); -}; - - -/** - * Return the center of the circle as {@link module:ol/coordinate~Coordinate coordinate}. - * @return {module:ol/coordinate~Coordinate} Center. - * @api - */ -Circle.prototype.getCenter = function() { - return this.flatCoordinates.slice(0, this.stride); -}; - - -/** - * @inheritDoc - */ -Circle.prototype.computeExtent = function(extent) { - const flatCoordinates = this.flatCoordinates; - const radius = flatCoordinates[this.stride] - flatCoordinates[0]; - return createOrUpdate( - flatCoordinates[0] - radius, flatCoordinates[1] - radius, - flatCoordinates[0] + radius, flatCoordinates[1] + radius, - extent); -}; - - -/** - * Return the radius of the circle. - * @return {number} Radius. - * @api - */ -Circle.prototype.getRadius = function() { - return Math.sqrt(this.getRadiusSquared_()); -}; - - -/** - * @private - * @return {number} Radius squared. - */ -Circle.prototype.getRadiusSquared_ = function() { - const dx = this.flatCoordinates[this.stride] - this.flatCoordinates[0]; - const dy = this.flatCoordinates[this.stride + 1] - this.flatCoordinates[1]; - return dx * dx + dy * dy; -}; - - -/** - * @inheritDoc - * @api - */ -Circle.prototype.getType = function() { - return GeometryType.CIRCLE; -}; - - -/** - * @inheritDoc - * @api - */ -Circle.prototype.intersectsExtent = function(extent) { - const circleExtent = this.getExtent(); - if (intersects(extent, circleExtent)) { - const center = this.getCenter(); - - if (extent[0] <= center[0] && extent[2] >= center[0]) { - return true; - } - if (extent[1] <= center[1] && extent[3] >= center[1]) { - return true; - } - - return forEachCorner(extent, this.intersectsCoordinate, this); - } - return false; - -}; - - -/** - * Set the center of the circle as {@link module:ol/coordinate~Coordinate coordinate}. - * @param {module:ol/coordinate~Coordinate} center Center. - * @api - */ -Circle.prototype.setCenter = function(center) { - const stride = this.stride; - const radius = this.flatCoordinates[stride] - this.flatCoordinates[0]; - const flatCoordinates = center.slice(); - flatCoordinates[stride] = flatCoordinates[0] + radius; - for (let i = 1; i < stride; ++i) { - flatCoordinates[stride + i] = center[i]; - } - this.setFlatCoordinates(this.layout, flatCoordinates); - this.changed(); -}; - - -/** - * Set the center (as {@link module:ol/coordinate~Coordinate coordinate}) and the radius (as - * number) of the circle. - * @param {!module:ol/coordinate~Coordinate} center Center. - * @param {number} radius Radius. - * @param {module:ol/geom/GeometryLayout=} opt_layout Layout. - * @api - */ -Circle.prototype.setCenterAndRadius = function(center, radius, opt_layout) { - this.setLayout(opt_layout, center, 0); - if (!this.flatCoordinates) { - this.flatCoordinates = []; - } - /** @type {Array.} */ - const flatCoordinates = this.flatCoordinates; - let offset = deflateCoordinate( - flatCoordinates, 0, center, this.stride); - flatCoordinates[offset++] = flatCoordinates[0] + radius; - for (let i = 1, ii = this.stride; i < ii; ++i) { - flatCoordinates[offset++] = flatCoordinates[i]; - } - flatCoordinates.length = offset; - this.changed(); -}; - - -/** - * @inheritDoc - */ -Circle.prototype.getCoordinates = function() {}; - - -/** - * @inheritDoc - */ -Circle.prototype.setCoordinates = function(coordinates, opt_layout) {}; - - -/** - * Set the radius of the circle. The radius is in the units of the projection. - * @param {number} radius Radius. - * @api - */ -Circle.prototype.setRadius = function(radius) { - this.flatCoordinates[this.stride] = this.flatCoordinates[0] + radius; - this.changed(); -}; - - /** * Transform each coordinate of the circle from one coordinate reference system * to another. The geometry is modified in place. diff --git a/src/ol/geom/Geometry.js b/src/ol/geom/Geometry.js index 9389ecc0ed..a71d3ca775 100644 --- a/src/ol/geom/Geometry.js +++ b/src/ol/geom/Geometry.js @@ -25,41 +25,225 @@ import {create as createTransform, compose as composeTransform} from '../transfo * @extends {module:ol/Object} * @api */ -const Geometry = function() { +class Geometry { + constructor() { - BaseObject.call(this); + BaseObject.call(this); - /** - * @private - * @type {module:ol/extent~Extent} - */ - this.extent_ = createEmpty(); + /** + * @private + * @type {module:ol/extent~Extent} + */ + this.extent_ = createEmpty(); - /** - * @private - * @type {number} - */ - this.extentRevision_ = -1; + /** + * @private + * @type {number} + */ + this.extentRevision_ = -1; - /** - * @protected - * @type {Object.} - */ - this.simplifiedGeometryCache = {}; + /** + * @protected + * @type {Object.} + */ + this.simplifiedGeometryCache = {}; - /** - * @protected - * @type {number} - */ - this.simplifiedGeometryMaxMinSquaredTolerance = 0; + /** + * @protected + * @type {number} + */ + this.simplifiedGeometryMaxMinSquaredTolerance = 0; - /** - * @protected - * @type {number} - */ - this.simplifiedGeometryRevision = 0; + /** + * @protected + * @type {number} + */ + this.simplifiedGeometryRevision = 0; -}; + } + + /** + * Make a complete copy of the geometry. + * @abstract + * @return {!module:ol/geom/Geometry} Clone. + */ + clone() {} + + /** + * @abstract + * @param {number} x X. + * @param {number} y Y. + * @param {module:ol/coordinate~Coordinate} closestPoint Closest point. + * @param {number} minSquaredDistance Minimum squared distance. + * @return {number} Minimum squared distance. + */ + closestPointXY(x, y, closestPoint, minSquaredDistance) {} + + /** + * Return the closest point of the geometry to the passed point as + * {@link module:ol/coordinate~Coordinate coordinate}. + * @param {module:ol/coordinate~Coordinate} point Point. + * @param {module:ol/coordinate~Coordinate=} opt_closestPoint Closest point. + * @return {module:ol/coordinate~Coordinate} Closest point. + * @api + */ + getClosestPoint(point, opt_closestPoint) { + const closestPoint = opt_closestPoint ? opt_closestPoint : [NaN, NaN]; + this.closestPointXY(point[0], point[1], closestPoint, Infinity); + return closestPoint; + } + + /** + * Returns true if this geometry includes the specified coordinate. If the + * coordinate is on the boundary of the geometry, returns false. + * @param {module:ol/coordinate~Coordinate} coordinate Coordinate. + * @return {boolean} Contains coordinate. + * @api + */ + intersectsCoordinate(coordinate) { + return this.containsXY(coordinate[0], coordinate[1]); + } + + /** + * @abstract + * @param {module:ol/extent~Extent} extent Extent. + * @protected + * @return {module:ol/extent~Extent} extent Extent. + */ + computeExtent(extent) {} + + /** + * Get the extent of the geometry. + * @param {module:ol/extent~Extent=} opt_extent Extent. + * @return {module:ol/extent~Extent} extent Extent. + * @api + */ + getExtent(opt_extent) { + if (this.extentRevision_ != this.getRevision()) { + this.extent_ = this.computeExtent(this.extent_); + this.extentRevision_ = this.getRevision(); + } + return returnOrUpdate(this.extent_, opt_extent); + } + + /** + * Rotate the geometry around a given coordinate. This modifies the geometry + * coordinates in place. + * @abstract + * @param {number} angle Rotation angle in radians. + * @param {module:ol/coordinate~Coordinate} anchor The rotation center. + * @api + */ + rotate(angle, anchor) {} + + /** + * Scale the geometry (with an optional origin). This modifies the geometry + * coordinates in place. + * @abstract + * @param {number} sx The scaling factor in the x-direction. + * @param {number=} opt_sy The scaling factor in the y-direction (defaults to + * sx). + * @param {module:ol/coordinate~Coordinate=} opt_anchor The scale origin (defaults to the center + * of the geometry extent). + * @api + */ + scale(sx, opt_sy, opt_anchor) {} + + /** + * Create a simplified version of this geometry. For linestrings, this uses + * the the {@link + * https://en.wikipedia.org/wiki/Ramer-Douglas-Peucker_algorithm + * Douglas Peucker} algorithm. For polygons, a quantization-based + * simplification is used to preserve topology. + * @function + * @param {number} tolerance The tolerance distance for simplification. + * @return {module:ol/geom/Geometry} A new, simplified version of the original + * geometry. + * @api + */ + simplify(tolerance) { + return this.getSimplifiedGeometry(tolerance * tolerance); + } + + /** + * Create a simplified version of this geometry using the Douglas Peucker + * algorithm. + * @see https://en.wikipedia.org/wiki/Ramer-Douglas-Peucker_algorithm + * @abstract + * @param {number} squaredTolerance Squared tolerance. + * @return {module:ol/geom/Geometry} Simplified geometry. + */ + getSimplifiedGeometry(squaredTolerance) {} + + /** + * Get the type of this geometry. + * @abstract + * @return {module:ol/geom/GeometryType} Geometry type. + */ + getType() {} + + /** + * Apply a transform function to each coordinate of the geometry. + * The geometry is modified in place. + * If you do not want the geometry modified in place, first `clone()` it and + * then use this function on the clone. + * @abstract + * @param {module:ol/proj~TransformFunction} transformFn Transform. + */ + applyTransform(transformFn) {} + + /** + * Test if the geometry and the passed extent intersect. + * @abstract + * @param {module:ol/extent~Extent} extent Extent. + * @return {boolean} `true` if the geometry and the extent intersect. + */ + intersectsExtent(extent) {} + + /** + * Translate the geometry. This modifies the geometry coordinates in place. If + * instead you want a new geometry, first `clone()` this geometry. + * @abstract + * @param {number} deltaX Delta X. + * @param {number} deltaY Delta Y. + */ + translate(deltaX, deltaY) {} + + /** + * Transform each coordinate of the geometry from one coordinate reference + * system to another. The geometry is modified in place. + * For example, a line will be transformed to a line and a circle to a circle. + * If you do not want the geometry modified in place, first `clone()` it and + * then use this function on the clone. + * + * @param {module:ol/proj~ProjectionLike} source The current projection. Can be a + * string identifier or a {@link module:ol/proj/Projection~Projection} object. + * @param {module:ol/proj~ProjectionLike} destination The desired projection. Can be a + * string identifier or a {@link module:ol/proj/Projection~Projection} object. + * @return {module:ol/geom/Geometry} This geometry. Note that original geometry is + * modified in place. + * @api + */ + transform(source, destination) { + source = getProjection(source); + const transformFn = source.getUnits() == Units.TILE_PIXELS ? + function(inCoordinates, outCoordinates, stride) { + const pixelExtent = source.getExtent(); + const projectedExtent = source.getWorldExtent(); + const scale = getHeight(projectedExtent) / getHeight(pixelExtent); + composeTransform(tmpTransform, + projectedExtent[0], projectedExtent[3], + scale, -scale, 0, + 0, 0); + transform2D(inCoordinates, 0, inCoordinates.length, stride, + tmpTransform, outCoordinates); + return getTransform(source, destination)(inCoordinates, outCoordinates, stride); + } : + getTransform(source, destination); + this.applyTransform(transformFn); + return this; + } +} inherits(Geometry, BaseObject); @@ -70,61 +254,6 @@ inherits(Geometry, BaseObject); const tmpTransform = createTransform(); -/** - * Make a complete copy of the geometry. - * @abstract - * @return {!module:ol/geom/Geometry} Clone. - */ -Geometry.prototype.clone = function() {}; - - -/** - * @abstract - * @param {number} x X. - * @param {number} y Y. - * @param {module:ol/coordinate~Coordinate} closestPoint Closest point. - * @param {number} minSquaredDistance Minimum squared distance. - * @return {number} Minimum squared distance. - */ -Geometry.prototype.closestPointXY = function(x, y, closestPoint, minSquaredDistance) {}; - - -/** - * Return the closest point of the geometry to the passed point as - * {@link module:ol/coordinate~Coordinate coordinate}. - * @param {module:ol/coordinate~Coordinate} point Point. - * @param {module:ol/coordinate~Coordinate=} opt_closestPoint Closest point. - * @return {module:ol/coordinate~Coordinate} Closest point. - * @api - */ -Geometry.prototype.getClosestPoint = function(point, opt_closestPoint) { - const closestPoint = opt_closestPoint ? opt_closestPoint : [NaN, NaN]; - this.closestPointXY(point[0], point[1], closestPoint, Infinity); - return closestPoint; -}; - - -/** - * Returns true if this geometry includes the specified coordinate. If the - * coordinate is on the boundary of the geometry, returns false. - * @param {module:ol/coordinate~Coordinate} coordinate Coordinate. - * @return {boolean} Contains coordinate. - * @api - */ -Geometry.prototype.intersectsCoordinate = function(coordinate) { - return this.containsXY(coordinate[0], coordinate[1]); -}; - - -/** - * @abstract - * @param {module:ol/extent~Extent} extent Extent. - * @protected - * @return {module:ol/extent~Extent} extent Extent. - */ -Geometry.prototype.computeExtent = function(extent) {}; - - /** * @param {number} x X. * @param {number} y Y. @@ -133,144 +262,4 @@ Geometry.prototype.computeExtent = function(extent) {}; Geometry.prototype.containsXY = FALSE; -/** - * Get the extent of the geometry. - * @param {module:ol/extent~Extent=} opt_extent Extent. - * @return {module:ol/extent~Extent} extent Extent. - * @api - */ -Geometry.prototype.getExtent = function(opt_extent) { - if (this.extentRevision_ != this.getRevision()) { - this.extent_ = this.computeExtent(this.extent_); - this.extentRevision_ = this.getRevision(); - } - return returnOrUpdate(this.extent_, opt_extent); -}; - - -/** - * Rotate the geometry around a given coordinate. This modifies the geometry - * coordinates in place. - * @abstract - * @param {number} angle Rotation angle in radians. - * @param {module:ol/coordinate~Coordinate} anchor The rotation center. - * @api - */ -Geometry.prototype.rotate = function(angle, anchor) {}; - - -/** - * Scale the geometry (with an optional origin). This modifies the geometry - * coordinates in place. - * @abstract - * @param {number} sx The scaling factor in the x-direction. - * @param {number=} opt_sy The scaling factor in the y-direction (defaults to - * sx). - * @param {module:ol/coordinate~Coordinate=} opt_anchor The scale origin (defaults to the center - * of the geometry extent). - * @api - */ -Geometry.prototype.scale = function(sx, opt_sy, opt_anchor) {}; - - -/** - * Create a simplified version of this geometry. For linestrings, this uses - * the the {@link - * https://en.wikipedia.org/wiki/Ramer-Douglas-Peucker_algorithm - * Douglas Peucker} algorithm. For polygons, a quantization-based - * simplification is used to preserve topology. - * @function - * @param {number} tolerance The tolerance distance for simplification. - * @return {module:ol/geom/Geometry} A new, simplified version of the original - * geometry. - * @api - */ -Geometry.prototype.simplify = function(tolerance) { - return this.getSimplifiedGeometry(tolerance * tolerance); -}; - - -/** - * Create a simplified version of this geometry using the Douglas Peucker - * algorithm. - * @see https://en.wikipedia.org/wiki/Ramer-Douglas-Peucker_algorithm - * @abstract - * @param {number} squaredTolerance Squared tolerance. - * @return {module:ol/geom/Geometry} Simplified geometry. - */ -Geometry.prototype.getSimplifiedGeometry = function(squaredTolerance) {}; - - -/** - * Get the type of this geometry. - * @abstract - * @return {module:ol/geom/GeometryType} Geometry type. - */ -Geometry.prototype.getType = function() {}; - - -/** - * Apply a transform function to each coordinate of the geometry. - * The geometry is modified in place. - * If you do not want the geometry modified in place, first `clone()` it and - * then use this function on the clone. - * @abstract - * @param {module:ol/proj~TransformFunction} transformFn Transform. - */ -Geometry.prototype.applyTransform = function(transformFn) {}; - - -/** - * Test if the geometry and the passed extent intersect. - * @abstract - * @param {module:ol/extent~Extent} extent Extent. - * @return {boolean} `true` if the geometry and the extent intersect. - */ -Geometry.prototype.intersectsExtent = function(extent) {}; - - -/** - * Translate the geometry. This modifies the geometry coordinates in place. If - * instead you want a new geometry, first `clone()` this geometry. - * @abstract - * @param {number} deltaX Delta X. - * @param {number} deltaY Delta Y. - */ -Geometry.prototype.translate = function(deltaX, deltaY) {}; - - -/** - * Transform each coordinate of the geometry from one coordinate reference - * system to another. The geometry is modified in place. - * For example, a line will be transformed to a line and a circle to a circle. - * If you do not want the geometry modified in place, first `clone()` it and - * then use this function on the clone. - * - * @param {module:ol/proj~ProjectionLike} source The current projection. Can be a - * string identifier or a {@link module:ol/proj/Projection~Projection} object. - * @param {module:ol/proj~ProjectionLike} destination The desired projection. Can be a - * string identifier or a {@link module:ol/proj/Projection~Projection} object. - * @return {module:ol/geom/Geometry} This geometry. Note that original geometry is - * modified in place. - * @api - */ -Geometry.prototype.transform = function(source, destination) { - source = getProjection(source); - const transformFn = source.getUnits() == Units.TILE_PIXELS ? - function(inCoordinates, outCoordinates, stride) { - const pixelExtent = source.getExtent(); - const projectedExtent = source.getWorldExtent(); - const scale = getHeight(projectedExtent) / getHeight(pixelExtent); - composeTransform(tmpTransform, - projectedExtent[0], projectedExtent[3], - scale, -scale, 0, - 0, 0); - transform2D(inCoordinates, 0, inCoordinates.length, stride, - tmpTransform, outCoordinates); - return getTransform(source, destination)(inCoordinates, outCoordinates, stride); - } : - getTransform(source, destination); - this.applyTransform(transformFn); - return this; -}; export default Geometry; diff --git a/src/ol/geom/GeometryCollection.js b/src/ol/geom/GeometryCollection.js index 6e4a1ee673..d7a85c4a6e 100644 --- a/src/ol/geom/GeometryCollection.js +++ b/src/ol/geom/GeometryCollection.js @@ -18,18 +18,268 @@ import {clear} from '../obj.js'; * @param {Array.=} opt_geometries Geometries. * @api */ -const GeometryCollection = function(opt_geometries) { +class GeometryCollection { + constructor(opt_geometries) { - Geometry.call(this); + Geometry.call(this); + + /** + * @private + * @type {Array.} + */ + this.geometries_ = opt_geometries ? opt_geometries : null; + + this.listenGeometriesChange_(); + } /** * @private - * @type {Array.} */ - this.geometries_ = opt_geometries ? opt_geometries : null; + unlistenGeometriesChange_() { + if (!this.geometries_) { + return; + } + for (let i = 0, ii = this.geometries_.length; i < ii; ++i) { + unlisten( + this.geometries_[i], EventType.CHANGE, + this.changed, this); + } + } - this.listenGeometriesChange_(); -}; + /** + * @private + */ + listenGeometriesChange_() { + if (!this.geometries_) { + return; + } + for (let i = 0, ii = this.geometries_.length; i < ii; ++i) { + listen( + this.geometries_[i], EventType.CHANGE, + this.changed, this); + } + } + + /** + * Make a complete copy of the geometry. + * @return {!module:ol/geom/GeometryCollection} Clone. + * @override + * @api + */ + clone() { + const geometryCollection = new GeometryCollection(null); + geometryCollection.setGeometries(this.geometries_); + return geometryCollection; + } + + /** + * @inheritDoc + */ + closestPointXY(x, y, closestPoint, minSquaredDistance) { + if (minSquaredDistance < closestSquaredDistanceXY(this.getExtent(), x, y)) { + return minSquaredDistance; + } + const geometries = this.geometries_; + for (let i = 0, ii = geometries.length; i < ii; ++i) { + minSquaredDistance = geometries[i].closestPointXY( + x, y, closestPoint, minSquaredDistance); + } + return minSquaredDistance; + } + + /** + * @inheritDoc + */ + containsXY(x, y) { + const geometries = this.geometries_; + for (let i = 0, ii = geometries.length; i < ii; ++i) { + if (geometries[i].containsXY(x, y)) { + return true; + } + } + return false; + } + + /** + * @inheritDoc + */ + computeExtent(extent) { + createOrUpdateEmpty(extent); + const geometries = this.geometries_; + for (let i = 0, ii = geometries.length; i < ii; ++i) { + extend(extent, geometries[i].getExtent()); + } + return extent; + } + + /** + * Return the geometries that make up this geometry collection. + * @return {Array.} Geometries. + * @api + */ + getGeometries() { + return cloneGeometries(this.geometries_); + } + + /** + * @return {Array.} Geometries. + */ + getGeometriesArray() { + return this.geometries_; + } + + /** + * @inheritDoc + */ + getSimplifiedGeometry(squaredTolerance) { + if (this.simplifiedGeometryRevision != this.getRevision()) { + clear(this.simplifiedGeometryCache); + this.simplifiedGeometryMaxMinSquaredTolerance = 0; + this.simplifiedGeometryRevision = this.getRevision(); + } + if (squaredTolerance < 0 || + (this.simplifiedGeometryMaxMinSquaredTolerance !== 0 && + squaredTolerance < this.simplifiedGeometryMaxMinSquaredTolerance)) { + return this; + } + const key = squaredTolerance.toString(); + if (this.simplifiedGeometryCache.hasOwnProperty(key)) { + return this.simplifiedGeometryCache[key]; + } else { + const simplifiedGeometries = []; + const geometries = this.geometries_; + let simplified = false; + for (let i = 0, ii = geometries.length; i < ii; ++i) { + const geometry = geometries[i]; + const simplifiedGeometry = geometry.getSimplifiedGeometry(squaredTolerance); + simplifiedGeometries.push(simplifiedGeometry); + if (simplifiedGeometry !== geometry) { + simplified = true; + } + } + if (simplified) { + const simplifiedGeometryCollection = new GeometryCollection(null); + simplifiedGeometryCollection.setGeometriesArray(simplifiedGeometries); + this.simplifiedGeometryCache[key] = simplifiedGeometryCollection; + return simplifiedGeometryCollection; + } else { + this.simplifiedGeometryMaxMinSquaredTolerance = squaredTolerance; + return this; + } + } + } + + /** + * @inheritDoc + * @api + */ + getType() { + return GeometryType.GEOMETRY_COLLECTION; + } + + /** + * @inheritDoc + * @api + */ + intersectsExtent(extent) { + const geometries = this.geometries_; + for (let i = 0, ii = geometries.length; i < ii; ++i) { + if (geometries[i].intersectsExtent(extent)) { + return true; + } + } + return false; + } + + /** + * @return {boolean} Is empty. + */ + isEmpty() { + return this.geometries_.length === 0; + } + + /** + * @inheritDoc + * @api + */ + rotate(angle, anchor) { + const geometries = this.geometries_; + for (let i = 0, ii = geometries.length; i < ii; ++i) { + geometries[i].rotate(angle, anchor); + } + this.changed(); + } + + /** + * @inheritDoc + * @api + */ + scale(sx, opt_sy, opt_anchor) { + let anchor = opt_anchor; + if (!anchor) { + anchor = getCenter(this.getExtent()); + } + const geometries = this.geometries_; + for (let i = 0, ii = geometries.length; i < ii; ++i) { + geometries[i].scale(sx, opt_sy, anchor); + } + this.changed(); + } + + /** + * Set the geometries that make up this geometry collection. + * @param {Array.} geometries Geometries. + * @api + */ + setGeometries(geometries) { + this.setGeometriesArray(cloneGeometries(geometries)); + } + + /** + * @param {Array.} geometries Geometries. + */ + setGeometriesArray(geometries) { + this.unlistenGeometriesChange_(); + this.geometries_ = geometries; + this.listenGeometriesChange_(); + this.changed(); + } + + /** + * @inheritDoc + * @api + */ + applyTransform(transformFn) { + const geometries = this.geometries_; + for (let i = 0, ii = geometries.length; i < ii; ++i) { + geometries[i].applyTransform(transformFn); + } + this.changed(); + } + + /** + * Translate the geometry. + * @param {number} deltaX Delta X. + * @param {number} deltaY Delta Y. + * @override + * @api + */ + translate(deltaX, deltaY) { + const geometries = this.geometries_; + for (let i = 0, ii = geometries.length; i < ii; ++i) { + geometries[i].translate(deltaX, deltaY); + } + this.changed(); + } + + /** + * @inheritDoc + */ + disposeInternal() { + this.unlistenGeometriesChange_(); + Geometry.prototype.disposeInternal.call(this); + } +} inherits(GeometryCollection, Geometry); @@ -47,269 +297,4 @@ function cloneGeometries(geometries) { } -/** - * @private - */ -GeometryCollection.prototype.unlistenGeometriesChange_ = function() { - if (!this.geometries_) { - return; - } - for (let i = 0, ii = this.geometries_.length; i < ii; ++i) { - unlisten( - this.geometries_[i], EventType.CHANGE, - this.changed, this); - } -}; - - -/** - * @private - */ -GeometryCollection.prototype.listenGeometriesChange_ = function() { - if (!this.geometries_) { - return; - } - for (let i = 0, ii = this.geometries_.length; i < ii; ++i) { - listen( - this.geometries_[i], EventType.CHANGE, - this.changed, this); - } -}; - - -/** - * Make a complete copy of the geometry. - * @return {!module:ol/geom/GeometryCollection} Clone. - * @override - * @api - */ -GeometryCollection.prototype.clone = function() { - const geometryCollection = new GeometryCollection(null); - geometryCollection.setGeometries(this.geometries_); - return geometryCollection; -}; - - -/** - * @inheritDoc - */ -GeometryCollection.prototype.closestPointXY = function(x, y, closestPoint, minSquaredDistance) { - if (minSquaredDistance < closestSquaredDistanceXY(this.getExtent(), x, y)) { - return minSquaredDistance; - } - const geometries = this.geometries_; - for (let i = 0, ii = geometries.length; i < ii; ++i) { - minSquaredDistance = geometries[i].closestPointXY( - x, y, closestPoint, minSquaredDistance); - } - return minSquaredDistance; -}; - - -/** - * @inheritDoc - */ -GeometryCollection.prototype.containsXY = function(x, y) { - const geometries = this.geometries_; - for (let i = 0, ii = geometries.length; i < ii; ++i) { - if (geometries[i].containsXY(x, y)) { - return true; - } - } - return false; -}; - - -/** - * @inheritDoc - */ -GeometryCollection.prototype.computeExtent = function(extent) { - createOrUpdateEmpty(extent); - const geometries = this.geometries_; - for (let i = 0, ii = geometries.length; i < ii; ++i) { - extend(extent, geometries[i].getExtent()); - } - return extent; -}; - - -/** - * Return the geometries that make up this geometry collection. - * @return {Array.} Geometries. - * @api - */ -GeometryCollection.prototype.getGeometries = function() { - return cloneGeometries(this.geometries_); -}; - - -/** - * @return {Array.} Geometries. - */ -GeometryCollection.prototype.getGeometriesArray = function() { - return this.geometries_; -}; - - -/** - * @inheritDoc - */ -GeometryCollection.prototype.getSimplifiedGeometry = function(squaredTolerance) { - if (this.simplifiedGeometryRevision != this.getRevision()) { - clear(this.simplifiedGeometryCache); - this.simplifiedGeometryMaxMinSquaredTolerance = 0; - this.simplifiedGeometryRevision = this.getRevision(); - } - if (squaredTolerance < 0 || - (this.simplifiedGeometryMaxMinSquaredTolerance !== 0 && - squaredTolerance < this.simplifiedGeometryMaxMinSquaredTolerance)) { - return this; - } - const key = squaredTolerance.toString(); - if (this.simplifiedGeometryCache.hasOwnProperty(key)) { - return this.simplifiedGeometryCache[key]; - } else { - const simplifiedGeometries = []; - const geometries = this.geometries_; - let simplified = false; - for (let i = 0, ii = geometries.length; i < ii; ++i) { - const geometry = geometries[i]; - const simplifiedGeometry = geometry.getSimplifiedGeometry(squaredTolerance); - simplifiedGeometries.push(simplifiedGeometry); - if (simplifiedGeometry !== geometry) { - simplified = true; - } - } - if (simplified) { - const simplifiedGeometryCollection = new GeometryCollection(null); - simplifiedGeometryCollection.setGeometriesArray(simplifiedGeometries); - this.simplifiedGeometryCache[key] = simplifiedGeometryCollection; - return simplifiedGeometryCollection; - } else { - this.simplifiedGeometryMaxMinSquaredTolerance = squaredTolerance; - return this; - } - } -}; - - -/** - * @inheritDoc - * @api - */ -GeometryCollection.prototype.getType = function() { - return GeometryType.GEOMETRY_COLLECTION; -}; - - -/** - * @inheritDoc - * @api - */ -GeometryCollection.prototype.intersectsExtent = function(extent) { - const geometries = this.geometries_; - for (let i = 0, ii = geometries.length; i < ii; ++i) { - if (geometries[i].intersectsExtent(extent)) { - return true; - } - } - return false; -}; - - -/** - * @return {boolean} Is empty. - */ -GeometryCollection.prototype.isEmpty = function() { - return this.geometries_.length === 0; -}; - - -/** - * @inheritDoc - * @api - */ -GeometryCollection.prototype.rotate = function(angle, anchor) { - const geometries = this.geometries_; - for (let i = 0, ii = geometries.length; i < ii; ++i) { - geometries[i].rotate(angle, anchor); - } - this.changed(); -}; - - -/** - * @inheritDoc - * @api - */ -GeometryCollection.prototype.scale = function(sx, opt_sy, opt_anchor) { - let anchor = opt_anchor; - if (!anchor) { - anchor = getCenter(this.getExtent()); - } - const geometries = this.geometries_; - for (let i = 0, ii = geometries.length; i < ii; ++i) { - geometries[i].scale(sx, opt_sy, anchor); - } - this.changed(); -}; - - -/** - * Set the geometries that make up this geometry collection. - * @param {Array.} geometries Geometries. - * @api - */ -GeometryCollection.prototype.setGeometries = function(geometries) { - this.setGeometriesArray(cloneGeometries(geometries)); -}; - - -/** - * @param {Array.} geometries Geometries. - */ -GeometryCollection.prototype.setGeometriesArray = function(geometries) { - this.unlistenGeometriesChange_(); - this.geometries_ = geometries; - this.listenGeometriesChange_(); - this.changed(); -}; - - -/** - * @inheritDoc - * @api - */ -GeometryCollection.prototype.applyTransform = function(transformFn) { - const geometries = this.geometries_; - for (let i = 0, ii = geometries.length; i < ii; ++i) { - geometries[i].applyTransform(transformFn); - } - this.changed(); -}; - - -/** - * Translate the geometry. - * @param {number} deltaX Delta X. - * @param {number} deltaY Delta Y. - * @override - * @api - */ -GeometryCollection.prototype.translate = function(deltaX, deltaY) { - const geometries = this.geometries_; - for (let i = 0, ii = geometries.length; i < ii; ++i) { - geometries[i].translate(deltaX, deltaY); - } - this.changed(); -}; - - -/** - * @inheritDoc - */ -GeometryCollection.prototype.disposeInternal = function() { - this.unlistenGeometriesChange_(); - Geometry.prototype.disposeInternal.call(this); -}; export default GeometryCollection; diff --git a/src/ol/geom/LineString.js b/src/ol/geom/LineString.js index 5154767dec..69faab0d62 100644 --- a/src/ol/geom/LineString.js +++ b/src/ol/geom/LineString.js @@ -28,229 +28,219 @@ import {douglasPeucker} from '../geom/flat/simplify.js'; * @param {module:ol/geom/GeometryLayout=} opt_layout Layout. * @api */ -const LineString = function(coordinates, opt_layout) { +class LineString { + constructor(coordinates, opt_layout) { - SimpleGeometry.call(this); + SimpleGeometry.call(this); - /** - * @private - * @type {module:ol/coordinate~Coordinate} - */ - this.flatMidpoint_ = null; + /** + * @private + * @type {module:ol/coordinate~Coordinate} + */ + this.flatMidpoint_ = null; - /** - * @private - * @type {number} - */ - this.flatMidpointRevision_ = -1; + /** + * @private + * @type {number} + */ + this.flatMidpointRevision_ = -1; - /** - * @private - * @type {number} - */ - this.maxDelta_ = -1; + /** + * @private + * @type {number} + */ + this.maxDelta_ = -1; - /** - * @private - * @type {number} - */ - this.maxDeltaRevision_ = -1; + /** + * @private + * @type {number} + */ + this.maxDeltaRevision_ = -1; + + if (opt_layout !== undefined && !Array.isArray(coordinates[0])) { + this.setFlatCoordinates(opt_layout, coordinates); + } else { + this.setCoordinates(coordinates, opt_layout); + } - if (opt_layout !== undefined && !Array.isArray(coordinates[0])) { - this.setFlatCoordinates(opt_layout, coordinates); - } else { - this.setCoordinates(coordinates, opt_layout); } -}; + /** + * Append the passed coordinate to the coordinates of the linestring. + * @param {module:ol/coordinate~Coordinate} coordinate Coordinate. + * @api + */ + appendCoordinate(coordinate) { + if (!this.flatCoordinates) { + this.flatCoordinates = coordinate.slice(); + } else { + extend(this.flatCoordinates, coordinate); + } + this.changed(); + } + + /** + * Make a complete copy of the geometry. + * @return {!module:ol/geom/LineString} Clone. + * @override + * @api + */ + clone() { + return new LineString(this.flatCoordinates.slice(), this.layout); + } + + /** + * @inheritDoc + */ + closestPointXY(x, y, closestPoint, minSquaredDistance) { + if (minSquaredDistance < closestSquaredDistanceXY(this.getExtent(), x, y)) { + return minSquaredDistance; + } + if (this.maxDeltaRevision_ != this.getRevision()) { + this.maxDelta_ = Math.sqrt(maxSquaredDelta( + this.flatCoordinates, 0, this.flatCoordinates.length, this.stride, 0)); + this.maxDeltaRevision_ = this.getRevision(); + } + return assignClosestPoint( + this.flatCoordinates, 0, this.flatCoordinates.length, this.stride, + this.maxDelta_, false, x, y, closestPoint, minSquaredDistance); + } + + /** + * Iterate over each segment, calling the provided callback. + * If the callback returns a truthy value the function returns that + * value immediately. Otherwise the function returns `false`. + * + * @param {function(this: S, module:ol/coordinate~Coordinate, module:ol/coordinate~Coordinate): T} callback Function + * called for each segment. + * @return {T|boolean} Value. + * @template T,S + * @api + */ + forEachSegment(callback) { + return forEachSegment(this.flatCoordinates, 0, this.flatCoordinates.length, this.stride, callback); + } + + /** + * Returns the coordinate at `m` using linear interpolation, or `null` if no + * such coordinate exists. + * + * `opt_extrapolate` controls extrapolation beyond the range of Ms in the + * MultiLineString. If `opt_extrapolate` is `true` then Ms less than the first + * M will return the first coordinate and Ms greater than the last M will + * return the last coordinate. + * + * @param {number} m M. + * @param {boolean=} opt_extrapolate Extrapolate. Default is `false`. + * @return {module:ol/coordinate~Coordinate} Coordinate. + * @api + */ + getCoordinateAtM(m, opt_extrapolate) { + if (this.layout != GeometryLayout.XYM && + this.layout != GeometryLayout.XYZM) { + return null; + } + const extrapolate = opt_extrapolate !== undefined ? opt_extrapolate : false; + return lineStringCoordinateAtM(this.flatCoordinates, 0, + this.flatCoordinates.length, this.stride, m, extrapolate); + } + + /** + * Return the coordinates of the linestring. + * @return {Array.} Coordinates. + * @override + * @api + */ + getCoordinates() { + return inflateCoordinates( + this.flatCoordinates, 0, this.flatCoordinates.length, this.stride); + } + + /** + * Return the coordinate at the provided fraction along the linestring. + * The `fraction` is a number between 0 and 1, where 0 is the start of the + * linestring and 1 is the end. + * @param {number} fraction Fraction. + * @param {module:ol/coordinate~Coordinate=} opt_dest Optional coordinate whose values will + * be modified. If not provided, a new coordinate will be returned. + * @return {module:ol/coordinate~Coordinate} Coordinate of the interpolated point. + * @api + */ + getCoordinateAt(fraction, opt_dest) { + return interpolatePoint( + this.flatCoordinates, 0, this.flatCoordinates.length, this.stride, + fraction, opt_dest); + } + + /** + * Return the length of the linestring on projected plane. + * @return {number} Length (on projected plane). + * @api + */ + getLength() { + return lineStringLength( + this.flatCoordinates, 0, this.flatCoordinates.length, this.stride); + } + + /** + * @return {Array.} Flat midpoint. + */ + getFlatMidpoint() { + if (this.flatMidpointRevision_ != this.getRevision()) { + this.flatMidpoint_ = this.getCoordinateAt(0.5, this.flatMidpoint_); + this.flatMidpointRevision_ = this.getRevision(); + } + return this.flatMidpoint_; + } + + /** + * @inheritDoc + */ + getSimplifiedGeometryInternal(squaredTolerance) { + const simplifiedFlatCoordinates = []; + simplifiedFlatCoordinates.length = douglasPeucker( + this.flatCoordinates, 0, this.flatCoordinates.length, this.stride, + squaredTolerance, simplifiedFlatCoordinates, 0); + return new LineString(simplifiedFlatCoordinates, GeometryLayout.XY); + } + + /** + * @inheritDoc + * @api + */ + getType() { + return GeometryType.LINE_STRING; + } + + /** + * @inheritDoc + * @api + */ + intersectsExtent(extent) { + return intersectsLineString( + this.flatCoordinates, 0, this.flatCoordinates.length, this.stride, + extent); + } + + /** + * Set the coordinates of the linestring. + * @param {!Array.} coordinates Coordinates. + * @param {module:ol/geom/GeometryLayout=} opt_layout Layout. + * @override + * @api + */ + setCoordinates(coordinates, opt_layout) { + this.setLayout(opt_layout, coordinates, 1); + if (!this.flatCoordinates) { + this.flatCoordinates = []; + } + this.flatCoordinates.length = deflateCoordinates( + this.flatCoordinates, 0, coordinates, this.stride); + this.changed(); + } +} inherits(LineString, SimpleGeometry); -/** - * Append the passed coordinate to the coordinates of the linestring. - * @param {module:ol/coordinate~Coordinate} coordinate Coordinate. - * @api - */ -LineString.prototype.appendCoordinate = function(coordinate) { - if (!this.flatCoordinates) { - this.flatCoordinates = coordinate.slice(); - } else { - extend(this.flatCoordinates, coordinate); - } - this.changed(); -}; - - -/** - * Make a complete copy of the geometry. - * @return {!module:ol/geom/LineString} Clone. - * @override - * @api - */ -LineString.prototype.clone = function() { - return new LineString(this.flatCoordinates.slice(), this.layout); -}; - - -/** - * @inheritDoc - */ -LineString.prototype.closestPointXY = function(x, y, closestPoint, minSquaredDistance) { - if (minSquaredDistance < closestSquaredDistanceXY(this.getExtent(), x, y)) { - return minSquaredDistance; - } - if (this.maxDeltaRevision_ != this.getRevision()) { - this.maxDelta_ = Math.sqrt(maxSquaredDelta( - this.flatCoordinates, 0, this.flatCoordinates.length, this.stride, 0)); - this.maxDeltaRevision_ = this.getRevision(); - } - return assignClosestPoint( - this.flatCoordinates, 0, this.flatCoordinates.length, this.stride, - this.maxDelta_, false, x, y, closestPoint, minSquaredDistance); -}; - - -/** - * Iterate over each segment, calling the provided callback. - * If the callback returns a truthy value the function returns that - * value immediately. Otherwise the function returns `false`. - * - * @param {function(this: S, module:ol/coordinate~Coordinate, module:ol/coordinate~Coordinate): T} callback Function - * called for each segment. - * @return {T|boolean} Value. - * @template T,S - * @api - */ -LineString.prototype.forEachSegment = function(callback) { - return forEachSegment(this.flatCoordinates, 0, this.flatCoordinates.length, this.stride, callback); -}; - - -/** - * Returns the coordinate at `m` using linear interpolation, or `null` if no - * such coordinate exists. - * - * `opt_extrapolate` controls extrapolation beyond the range of Ms in the - * MultiLineString. If `opt_extrapolate` is `true` then Ms less than the first - * M will return the first coordinate and Ms greater than the last M will - * return the last coordinate. - * - * @param {number} m M. - * @param {boolean=} opt_extrapolate Extrapolate. Default is `false`. - * @return {module:ol/coordinate~Coordinate} Coordinate. - * @api - */ -LineString.prototype.getCoordinateAtM = function(m, opt_extrapolate) { - if (this.layout != GeometryLayout.XYM && - this.layout != GeometryLayout.XYZM) { - return null; - } - const extrapolate = opt_extrapolate !== undefined ? opt_extrapolate : false; - return lineStringCoordinateAtM(this.flatCoordinates, 0, - this.flatCoordinates.length, this.stride, m, extrapolate); -}; - - -/** - * Return the coordinates of the linestring. - * @return {Array.} Coordinates. - * @override - * @api - */ -LineString.prototype.getCoordinates = function() { - return inflateCoordinates( - this.flatCoordinates, 0, this.flatCoordinates.length, this.stride); -}; - - -/** - * Return the coordinate at the provided fraction along the linestring. - * The `fraction` is a number between 0 and 1, where 0 is the start of the - * linestring and 1 is the end. - * @param {number} fraction Fraction. - * @param {module:ol/coordinate~Coordinate=} opt_dest Optional coordinate whose values will - * be modified. If not provided, a new coordinate will be returned. - * @return {module:ol/coordinate~Coordinate} Coordinate of the interpolated point. - * @api - */ -LineString.prototype.getCoordinateAt = function(fraction, opt_dest) { - return interpolatePoint( - this.flatCoordinates, 0, this.flatCoordinates.length, this.stride, - fraction, opt_dest); -}; - - -/** - * Return the length of the linestring on projected plane. - * @return {number} Length (on projected plane). - * @api - */ -LineString.prototype.getLength = function() { - return lineStringLength( - this.flatCoordinates, 0, this.flatCoordinates.length, this.stride); -}; - - -/** - * @return {Array.} Flat midpoint. - */ -LineString.prototype.getFlatMidpoint = function() { - if (this.flatMidpointRevision_ != this.getRevision()) { - this.flatMidpoint_ = this.getCoordinateAt(0.5, this.flatMidpoint_); - this.flatMidpointRevision_ = this.getRevision(); - } - return this.flatMidpoint_; -}; - - -/** - * @inheritDoc - */ -LineString.prototype.getSimplifiedGeometryInternal = function(squaredTolerance) { - const simplifiedFlatCoordinates = []; - simplifiedFlatCoordinates.length = douglasPeucker( - this.flatCoordinates, 0, this.flatCoordinates.length, this.stride, - squaredTolerance, simplifiedFlatCoordinates, 0); - return new LineString(simplifiedFlatCoordinates, GeometryLayout.XY); -}; - - -/** - * @inheritDoc - * @api - */ -LineString.prototype.getType = function() { - return GeometryType.LINE_STRING; -}; - - -/** - * @inheritDoc - * @api - */ -LineString.prototype.intersectsExtent = function(extent) { - return intersectsLineString( - this.flatCoordinates, 0, this.flatCoordinates.length, this.stride, - extent); -}; - - -/** - * Set the coordinates of the linestring. - * @param {!Array.} coordinates Coordinates. - * @param {module:ol/geom/GeometryLayout=} opt_layout Layout. - * @override - * @api - */ -LineString.prototype.setCoordinates = function(coordinates, opt_layout) { - this.setLayout(opt_layout, coordinates, 1); - if (!this.flatCoordinates) { - this.flatCoordinates = []; - } - this.flatCoordinates.length = deflateCoordinates( - this.flatCoordinates, 0, coordinates, this.stride); - this.changed(); -}; - export default LineString; diff --git a/src/ol/geom/LinearRing.js b/src/ol/geom/LinearRing.js index dadfe3ac2f..9cb2c5149f 100644 --- a/src/ol/geom/LinearRing.js +++ b/src/ol/geom/LinearRing.js @@ -25,125 +25,121 @@ import {douglasPeucker} from '../geom/flat/simplify.js'; * @param {module:ol/geom/GeometryLayout=} opt_layout Layout. * @api */ -const LinearRing = function(coordinates, opt_layout) { +class LinearRing { + constructor(coordinates, opt_layout) { - SimpleGeometry.call(this); + SimpleGeometry.call(this); - /** - * @private - * @type {number} - */ - this.maxDelta_ = -1; + /** + * @private + * @type {number} + */ + this.maxDelta_ = -1; - /** - * @private - * @type {number} - */ - this.maxDeltaRevision_ = -1; + /** + * @private + * @type {number} + */ + this.maxDeltaRevision_ = -1; + + if (opt_layout !== undefined && !Array.isArray(coordinates[0])) { + this.setFlatCoordinates(opt_layout, coordinates); + } else { + this.setCoordinates(coordinates, opt_layout); + } - if (opt_layout !== undefined && !Array.isArray(coordinates[0])) { - this.setFlatCoordinates(opt_layout, coordinates); - } else { - this.setCoordinates(coordinates, opt_layout); } -}; + /** + * Make a complete copy of the geometry. + * @return {!module:ol/geom/LinearRing} Clone. + * @override + * @api + */ + clone() { + return new LinearRing(this.flatCoordinates.slice(), this.layout); + } + + /** + * @inheritDoc + */ + closestPointXY(x, y, closestPoint, minSquaredDistance) { + if (minSquaredDistance < closestSquaredDistanceXY(this.getExtent(), x, y)) { + return minSquaredDistance; + } + if (this.maxDeltaRevision_ != this.getRevision()) { + this.maxDelta_ = Math.sqrt(maxSquaredDelta( + this.flatCoordinates, 0, this.flatCoordinates.length, this.stride, 0)); + this.maxDeltaRevision_ = this.getRevision(); + } + return assignClosestPoint( + this.flatCoordinates, 0, this.flatCoordinates.length, this.stride, + this.maxDelta_, true, x, y, closestPoint, minSquaredDistance); + } + + /** + * Return the area of the linear ring on projected plane. + * @return {number} Area (on projected plane). + * @api + */ + getArea() { + return linearRingArea(this.flatCoordinates, 0, this.flatCoordinates.length, this.stride); + } + + /** + * Return the coordinates of the linear ring. + * @return {Array.} Coordinates. + * @override + * @api + */ + getCoordinates() { + return inflateCoordinates( + this.flatCoordinates, 0, this.flatCoordinates.length, this.stride); + } + + /** + * @inheritDoc + */ + getSimplifiedGeometryInternal(squaredTolerance) { + const simplifiedFlatCoordinates = []; + simplifiedFlatCoordinates.length = douglasPeucker( + this.flatCoordinates, 0, this.flatCoordinates.length, this.stride, + squaredTolerance, simplifiedFlatCoordinates, 0); + return new LinearRing(simplifiedFlatCoordinates, GeometryLayout.XY); + } + + /** + * @inheritDoc + * @api + */ + getType() { + return GeometryType.LINEAR_RING; + } + + /** + * @inheritDoc + */ + intersectsExtent(extent) {} + + /** + * Set the coordinates of the linear ring. + * @param {!Array.} coordinates Coordinates. + * @param {module:ol/geom/GeometryLayout=} opt_layout Layout. + * @override + * @api + */ + setCoordinates(coordinates, opt_layout) { + this.setLayout(opt_layout, coordinates, 1); + if (!this.flatCoordinates) { + this.flatCoordinates = []; + } + this.flatCoordinates.length = deflateCoordinates( + this.flatCoordinates, 0, coordinates, this.stride); + this.changed(); + } +} inherits(LinearRing, SimpleGeometry); -/** - * Make a complete copy of the geometry. - * @return {!module:ol/geom/LinearRing} Clone. - * @override - * @api - */ -LinearRing.prototype.clone = function() { - return new LinearRing(this.flatCoordinates.slice(), this.layout); -}; - - -/** - * @inheritDoc - */ -LinearRing.prototype.closestPointXY = function(x, y, closestPoint, minSquaredDistance) { - if (minSquaredDistance < closestSquaredDistanceXY(this.getExtent(), x, y)) { - return minSquaredDistance; - } - if (this.maxDeltaRevision_ != this.getRevision()) { - this.maxDelta_ = Math.sqrt(maxSquaredDelta( - this.flatCoordinates, 0, this.flatCoordinates.length, this.stride, 0)); - this.maxDeltaRevision_ = this.getRevision(); - } - return assignClosestPoint( - this.flatCoordinates, 0, this.flatCoordinates.length, this.stride, - this.maxDelta_, true, x, y, closestPoint, minSquaredDistance); -}; - - -/** - * Return the area of the linear ring on projected plane. - * @return {number} Area (on projected plane). - * @api - */ -LinearRing.prototype.getArea = function() { - return linearRingArea(this.flatCoordinates, 0, this.flatCoordinates.length, this.stride); -}; - - -/** - * Return the coordinates of the linear ring. - * @return {Array.} Coordinates. - * @override - * @api - */ -LinearRing.prototype.getCoordinates = function() { - return inflateCoordinates( - this.flatCoordinates, 0, this.flatCoordinates.length, this.stride); -}; - - -/** - * @inheritDoc - */ -LinearRing.prototype.getSimplifiedGeometryInternal = function(squaredTolerance) { - const simplifiedFlatCoordinates = []; - simplifiedFlatCoordinates.length = douglasPeucker( - this.flatCoordinates, 0, this.flatCoordinates.length, this.stride, - squaredTolerance, simplifiedFlatCoordinates, 0); - return new LinearRing(simplifiedFlatCoordinates, GeometryLayout.XY); -}; - - -/** - * @inheritDoc - * @api - */ -LinearRing.prototype.getType = function() { - return GeometryType.LINEAR_RING; -}; - - -/** - * @inheritDoc - */ -LinearRing.prototype.intersectsExtent = function(extent) {}; - - -/** - * Set the coordinates of the linear ring. - * @param {!Array.} coordinates Coordinates. - * @param {module:ol/geom/GeometryLayout=} opt_layout Layout. - * @override - * @api - */ -LinearRing.prototype.setCoordinates = function(coordinates, opt_layout) { - this.setLayout(opt_layout, coordinates, 1); - if (!this.flatCoordinates) { - this.flatCoordinates = []; - } - this.flatCoordinates.length = deflateCoordinates( - this.flatCoordinates, 0, coordinates, this.stride); - this.changed(); -}; export default LinearRing; diff --git a/src/ol/geom/MultiLineString.js b/src/ol/geom/MultiLineString.js index fcff60af99..ab9e92b952 100644 --- a/src/ol/geom/MultiLineString.js +++ b/src/ol/geom/MultiLineString.js @@ -28,258 +28,249 @@ import {douglasPeuckerArray} from '../geom/flat/simplify.js'; * @param {Array.} opt_ends Flat coordinate ends for internal use. * @api */ -const MultiLineString = function(coordinates, opt_layout, opt_ends) { +class MultiLineString { + constructor(coordinates, opt_layout, opt_ends) { - SimpleGeometry.call(this); + SimpleGeometry.call(this); - /** - * @type {Array.} - * @private - */ - this.ends_ = []; + /** + * @type {Array.} + * @private + */ + this.ends_ = []; - /** - * @private - * @type {number} - */ - this.maxDelta_ = -1; + /** + * @private + * @type {number} + */ + this.maxDelta_ = -1; - /** - * @private - * @type {number} - */ - this.maxDeltaRevision_ = -1; + /** + * @private + * @type {number} + */ + this.maxDeltaRevision_ = -1; - if (Array.isArray(coordinates[0])) { - this.setCoordinates(coordinates, opt_layout); - } else if (opt_layout !== undefined && opt_ends) { - this.setFlatCoordinates(opt_layout, coordinates); - this.ends_ = opt_ends; - } else { - let layout = this.getLayout(); - const flatCoordinates = []; - const ends = []; - for (let i = 0, ii = coordinates.length; i < ii; ++i) { - const lineString = coordinates[i]; - if (i === 0) { - layout = lineString.getLayout(); + if (Array.isArray(coordinates[0])) { + this.setCoordinates(coordinates, opt_layout); + } else if (opt_layout !== undefined && opt_ends) { + this.setFlatCoordinates(opt_layout, coordinates); + this.ends_ = opt_ends; + } else { + let layout = this.getLayout(); + const flatCoordinates = []; + const ends = []; + for (let i = 0, ii = coordinates.length; i < ii; ++i) { + const lineString = coordinates[i]; + if (i === 0) { + layout = lineString.getLayout(); + } + extend(flatCoordinates, lineString.getFlatCoordinates()); + ends.push(flatCoordinates.length); } - extend(flatCoordinates, lineString.getFlatCoordinates()); - ends.push(flatCoordinates.length); + this.setFlatCoordinates(layout, flatCoordinates); + this.ends_ = ends; } - this.setFlatCoordinates(layout, flatCoordinates); - this.ends_ = ends; + } -}; + /** + * Append the passed linestring to the multilinestring. + * @param {module:ol/geom/LineString} lineString LineString. + * @api + */ + appendLineString(lineString) { + if (!this.flatCoordinates) { + this.flatCoordinates = lineString.getFlatCoordinates().slice(); + } else { + extend(this.flatCoordinates, lineString.getFlatCoordinates().slice()); + } + this.ends_.push(this.flatCoordinates.length); + this.changed(); + } + + /** + * Make a complete copy of the geometry. + * @return {!module:ol/geom/MultiLineString} Clone. + * @override + * @api + */ + clone() { + return new MultiLineString(this.flatCoordinates.slice(), this.layout, this.ends_.slice()); + } + + /** + * @inheritDoc + */ + closestPointXY(x, y, closestPoint, minSquaredDistance) { + if (minSquaredDistance < closestSquaredDistanceXY(this.getExtent(), x, y)) { + return minSquaredDistance; + } + if (this.maxDeltaRevision_ != this.getRevision()) { + this.maxDelta_ = Math.sqrt(arrayMaxSquaredDelta( + this.flatCoordinates, 0, this.ends_, this.stride, 0)); + this.maxDeltaRevision_ = this.getRevision(); + } + return assignClosestArrayPoint( + this.flatCoordinates, 0, this.ends_, this.stride, + this.maxDelta_, false, x, y, closestPoint, minSquaredDistance); + } + + /** + * Returns the coordinate at `m` using linear interpolation, or `null` if no + * such coordinate exists. + * + * `opt_extrapolate` controls extrapolation beyond the range of Ms in the + * MultiLineString. If `opt_extrapolate` is `true` then Ms less than the first + * M will return the first coordinate and Ms greater than the last M will + * return the last coordinate. + * + * `opt_interpolate` controls interpolation between consecutive LineStrings + * within the MultiLineString. If `opt_interpolate` is `true` the coordinates + * will be linearly interpolated between the last coordinate of one LineString + * and the first coordinate of the next LineString. If `opt_interpolate` is + * `false` then the function will return `null` for Ms falling between + * LineStrings. + * + * @param {number} m M. + * @param {boolean=} opt_extrapolate Extrapolate. Default is `false`. + * @param {boolean=} opt_interpolate Interpolate. Default is `false`. + * @return {module:ol/coordinate~Coordinate} Coordinate. + * @api + */ + getCoordinateAtM(m, opt_extrapolate, opt_interpolate) { + if ((this.layout != GeometryLayout.XYM && + this.layout != GeometryLayout.XYZM) || + this.flatCoordinates.length === 0) { + return null; + } + const extrapolate = opt_extrapolate !== undefined ? opt_extrapolate : false; + const interpolate = opt_interpolate !== undefined ? opt_interpolate : false; + return lineStringsCoordinateAtM(this.flatCoordinates, 0, + this.ends_, this.stride, m, extrapolate, interpolate); + } + + /** + * Return the coordinates of the multilinestring. + * @return {Array.>} Coordinates. + * @override + * @api + */ + getCoordinates() { + return inflateCoordinatesArray( + this.flatCoordinates, 0, this.ends_, this.stride); + } + + /** + * @return {Array.} Ends. + */ + getEnds() { + return this.ends_; + } + + /** + * Return the linestring at the specified index. + * @param {number} index Index. + * @return {module:ol/geom/LineString} LineString. + * @api + */ + getLineString(index) { + if (index < 0 || this.ends_.length <= index) { + return null; + } + return new LineString(this.flatCoordinates.slice( + index === 0 ? 0 : this.ends_[index - 1], this.ends_[index]), this.layout); + } + + /** + * Return the linestrings of this multilinestring. + * @return {Array.} LineStrings. + * @api + */ + getLineStrings() { + const flatCoordinates = this.flatCoordinates; + const ends = this.ends_; + const layout = this.layout; + /** @type {Array.} */ + const lineStrings = []; + let offset = 0; + for (let i = 0, ii = ends.length; i < ii; ++i) { + const end = ends[i]; + const lineString = new LineString(flatCoordinates.slice(offset, end), layout); + lineStrings.push(lineString); + offset = end; + } + return lineStrings; + } + + /** + * @return {Array.} Flat midpoints. + */ + getFlatMidpoints() { + const midpoints = []; + const flatCoordinates = this.flatCoordinates; + let offset = 0; + const ends = this.ends_; + const stride = this.stride; + for (let i = 0, ii = ends.length; i < ii; ++i) { + const end = ends[i]; + const midpoint = interpolatePoint( + flatCoordinates, offset, end, stride, 0.5); + extend(midpoints, midpoint); + offset = end; + } + return midpoints; + } + + /** + * @inheritDoc + */ + getSimplifiedGeometryInternal(squaredTolerance) { + const simplifiedFlatCoordinates = []; + const simplifiedEnds = []; + simplifiedFlatCoordinates.length = douglasPeuckerArray( + this.flatCoordinates, 0, this.ends_, this.stride, squaredTolerance, + simplifiedFlatCoordinates, 0, simplifiedEnds); + return new MultiLineString(simplifiedFlatCoordinates, GeometryLayout.XY, simplifiedEnds); + } + + /** + * @inheritDoc + * @api + */ + getType() { + return GeometryType.MULTI_LINE_STRING; + } + + /** + * @inheritDoc + * @api + */ + intersectsExtent(extent) { + return intersectsLineStringArray( + this.flatCoordinates, 0, this.ends_, this.stride, extent); + } + + /** + * Set the coordinates of the multilinestring. + * @param {!Array.>} coordinates Coordinates. + * @param {module:ol/geom/GeometryLayout=} opt_layout Layout. + * @override + * @api + */ + setCoordinates(coordinates, opt_layout) { + this.setLayout(opt_layout, coordinates, 2); + if (!this.flatCoordinates) { + this.flatCoordinates = []; + } + const ends = deflateCoordinatesArray( + this.flatCoordinates, 0, coordinates, this.stride, this.ends_); + this.flatCoordinates.length = ends.length === 0 ? 0 : ends[ends.length - 1]; + this.changed(); + } +} inherits(MultiLineString, SimpleGeometry); -/** - * Append the passed linestring to the multilinestring. - * @param {module:ol/geom/LineString} lineString LineString. - * @api - */ -MultiLineString.prototype.appendLineString = function(lineString) { - if (!this.flatCoordinates) { - this.flatCoordinates = lineString.getFlatCoordinates().slice(); - } else { - extend(this.flatCoordinates, lineString.getFlatCoordinates().slice()); - } - this.ends_.push(this.flatCoordinates.length); - this.changed(); -}; - - -/** - * Make a complete copy of the geometry. - * @return {!module:ol/geom/MultiLineString} Clone. - * @override - * @api - */ -MultiLineString.prototype.clone = function() { - return new MultiLineString(this.flatCoordinates.slice(), this.layout, this.ends_.slice()); -}; - - -/** - * @inheritDoc - */ -MultiLineString.prototype.closestPointXY = function(x, y, closestPoint, minSquaredDistance) { - if (minSquaredDistance < closestSquaredDistanceXY(this.getExtent(), x, y)) { - return minSquaredDistance; - } - if (this.maxDeltaRevision_ != this.getRevision()) { - this.maxDelta_ = Math.sqrt(arrayMaxSquaredDelta( - this.flatCoordinates, 0, this.ends_, this.stride, 0)); - this.maxDeltaRevision_ = this.getRevision(); - } - return assignClosestArrayPoint( - this.flatCoordinates, 0, this.ends_, this.stride, - this.maxDelta_, false, x, y, closestPoint, minSquaredDistance); -}; - - -/** - * Returns the coordinate at `m` using linear interpolation, or `null` if no - * such coordinate exists. - * - * `opt_extrapolate` controls extrapolation beyond the range of Ms in the - * MultiLineString. If `opt_extrapolate` is `true` then Ms less than the first - * M will return the first coordinate and Ms greater than the last M will - * return the last coordinate. - * - * `opt_interpolate` controls interpolation between consecutive LineStrings - * within the MultiLineString. If `opt_interpolate` is `true` the coordinates - * will be linearly interpolated between the last coordinate of one LineString - * and the first coordinate of the next LineString. If `opt_interpolate` is - * `false` then the function will return `null` for Ms falling between - * LineStrings. - * - * @param {number} m M. - * @param {boolean=} opt_extrapolate Extrapolate. Default is `false`. - * @param {boolean=} opt_interpolate Interpolate. Default is `false`. - * @return {module:ol/coordinate~Coordinate} Coordinate. - * @api - */ -MultiLineString.prototype.getCoordinateAtM = function(m, opt_extrapolate, opt_interpolate) { - if ((this.layout != GeometryLayout.XYM && - this.layout != GeometryLayout.XYZM) || - this.flatCoordinates.length === 0) { - return null; - } - const extrapolate = opt_extrapolate !== undefined ? opt_extrapolate : false; - const interpolate = opt_interpolate !== undefined ? opt_interpolate : false; - return lineStringsCoordinateAtM(this.flatCoordinates, 0, - this.ends_, this.stride, m, extrapolate, interpolate); -}; - - -/** - * Return the coordinates of the multilinestring. - * @return {Array.>} Coordinates. - * @override - * @api - */ -MultiLineString.prototype.getCoordinates = function() { - return inflateCoordinatesArray( - this.flatCoordinates, 0, this.ends_, this.stride); -}; - - -/** - * @return {Array.} Ends. - */ -MultiLineString.prototype.getEnds = function() { - return this.ends_; -}; - - -/** - * Return the linestring at the specified index. - * @param {number} index Index. - * @return {module:ol/geom/LineString} LineString. - * @api - */ -MultiLineString.prototype.getLineString = function(index) { - if (index < 0 || this.ends_.length <= index) { - return null; - } - return new LineString(this.flatCoordinates.slice( - index === 0 ? 0 : this.ends_[index - 1], this.ends_[index]), this.layout); -}; - - -/** - * Return the linestrings of this multilinestring. - * @return {Array.} LineStrings. - * @api - */ -MultiLineString.prototype.getLineStrings = function() { - const flatCoordinates = this.flatCoordinates; - const ends = this.ends_; - const layout = this.layout; - /** @type {Array.} */ - const lineStrings = []; - let offset = 0; - for (let i = 0, ii = ends.length; i < ii; ++i) { - const end = ends[i]; - const lineString = new LineString(flatCoordinates.slice(offset, end), layout); - lineStrings.push(lineString); - offset = end; - } - return lineStrings; -}; - - -/** - * @return {Array.} Flat midpoints. - */ -MultiLineString.prototype.getFlatMidpoints = function() { - const midpoints = []; - const flatCoordinates = this.flatCoordinates; - let offset = 0; - const ends = this.ends_; - const stride = this.stride; - for (let i = 0, ii = ends.length; i < ii; ++i) { - const end = ends[i]; - const midpoint = interpolatePoint( - flatCoordinates, offset, end, stride, 0.5); - extend(midpoints, midpoint); - offset = end; - } - return midpoints; -}; - - -/** - * @inheritDoc - */ -MultiLineString.prototype.getSimplifiedGeometryInternal = function(squaredTolerance) { - const simplifiedFlatCoordinates = []; - const simplifiedEnds = []; - simplifiedFlatCoordinates.length = douglasPeuckerArray( - this.flatCoordinates, 0, this.ends_, this.stride, squaredTolerance, - simplifiedFlatCoordinates, 0, simplifiedEnds); - return new MultiLineString(simplifiedFlatCoordinates, GeometryLayout.XY, simplifiedEnds); -}; - - -/** - * @inheritDoc - * @api - */ -MultiLineString.prototype.getType = function() { - return GeometryType.MULTI_LINE_STRING; -}; - - -/** - * @inheritDoc - * @api - */ -MultiLineString.prototype.intersectsExtent = function(extent) { - return intersectsLineStringArray( - this.flatCoordinates, 0, this.ends_, this.stride, extent); -}; - - -/** - * Set the coordinates of the multilinestring. - * @param {!Array.>} coordinates Coordinates. - * @param {module:ol/geom/GeometryLayout=} opt_layout Layout. - * @override - * @api - */ -MultiLineString.prototype.setCoordinates = function(coordinates, opt_layout) { - this.setLayout(opt_layout, coordinates, 2); - if (!this.flatCoordinates) { - this.flatCoordinates = []; - } - const ends = deflateCoordinatesArray( - this.flatCoordinates, 0, coordinates, this.stride, this.ends_); - this.flatCoordinates.length = ends.length === 0 ? 0 : ends[ends.length - 1]; - this.changed(); -}; export default MultiLineString; diff --git a/src/ol/geom/MultiPoint.js b/src/ol/geom/MultiPoint.js index 9ae1828b75..b366817415 100644 --- a/src/ol/geom/MultiPoint.js +++ b/src/ol/geom/MultiPoint.js @@ -23,157 +23,152 @@ import {squaredDistance as squaredDx} from '../math.js'; * @param {module:ol/geom/GeometryLayout=} opt_layout Layout. * @api */ -const MultiPoint = function(coordinates, opt_layout) { - SimpleGeometry.call(this); - if (opt_layout && !Array.isArray(coordinates[0])) { - this.setFlatCoordinates(opt_layout, coordinates); - } else { - this.setCoordinates(coordinates, opt_layout); +class MultiPoint { + constructor(coordinates, opt_layout) { + SimpleGeometry.call(this); + if (opt_layout && !Array.isArray(coordinates[0])) { + this.setFlatCoordinates(opt_layout, coordinates); + } else { + this.setCoordinates(coordinates, opt_layout); + } } -}; + + /** + * Append the passed point to this multipoint. + * @param {module:ol/geom/Point} point Point. + * @api + */ + appendPoint(point) { + if (!this.flatCoordinates) { + this.flatCoordinates = point.getFlatCoordinates().slice(); + } else { + extend(this.flatCoordinates, point.getFlatCoordinates()); + } + this.changed(); + } + + /** + * Make a complete copy of the geometry. + * @return {!module:ol/geom/MultiPoint} Clone. + * @override + * @api + */ + clone() { + const multiPoint = new MultiPoint(this.flatCoordinates.slice(), this.layout); + return multiPoint; + } + + /** + * @inheritDoc + */ + closestPointXY(x, y, closestPoint, minSquaredDistance) { + if (minSquaredDistance < closestSquaredDistanceXY(this.getExtent(), x, y)) { + return minSquaredDistance; + } + const flatCoordinates = this.flatCoordinates; + const stride = this.stride; + for (let i = 0, ii = flatCoordinates.length; i < ii; i += stride) { + const squaredDistance = squaredDx( + x, y, flatCoordinates[i], flatCoordinates[i + 1]); + if (squaredDistance < minSquaredDistance) { + minSquaredDistance = squaredDistance; + for (let j = 0; j < stride; ++j) { + closestPoint[j] = flatCoordinates[i + j]; + } + closestPoint.length = stride; + } + } + return minSquaredDistance; + } + + /** + * Return the coordinates of the multipoint. + * @return {Array.} Coordinates. + * @override + * @api + */ + getCoordinates() { + return inflateCoordinates( + this.flatCoordinates, 0, this.flatCoordinates.length, this.stride); + } + + /** + * Return the point at the specified index. + * @param {number} index Index. + * @return {module:ol/geom/Point} Point. + * @api + */ + getPoint(index) { + const n = !this.flatCoordinates ? 0 : this.flatCoordinates.length / this.stride; + if (index < 0 || n <= index) { + return null; + } + return new Point(this.flatCoordinates.slice( + index * this.stride, (index + 1) * this.stride), this.layout); + } + + /** + * Return the points of this multipoint. + * @return {Array.} Points. + * @api + */ + getPoints() { + const flatCoordinates = this.flatCoordinates; + const layout = this.layout; + const stride = this.stride; + /** @type {Array.} */ + const points = []; + for (let i = 0, ii = flatCoordinates.length; i < ii; i += stride) { + const point = new Point(flatCoordinates.slice(i, i + stride), layout); + points.push(point); + } + return points; + } + + /** + * @inheritDoc + * @api + */ + getType() { + return GeometryType.MULTI_POINT; + } + + /** + * @inheritDoc + * @api + */ + intersectsExtent(extent) { + const flatCoordinates = this.flatCoordinates; + const stride = this.stride; + for (let i = 0, ii = flatCoordinates.length; i < ii; i += stride) { + const x = flatCoordinates[i]; + const y = flatCoordinates[i + 1]; + if (containsXY(extent, x, y)) { + return true; + } + } + return false; + } + + /** + * Set the coordinates of the multipoint. + * @param {!Array.} coordinates Coordinates. + * @param {module:ol/geom/GeometryLayout=} opt_layout Layout. + * @override + * @api + */ + setCoordinates(coordinates, opt_layout) { + this.setLayout(opt_layout, coordinates, 1); + if (!this.flatCoordinates) { + this.flatCoordinates = []; + } + this.flatCoordinates.length = deflateCoordinates( + this.flatCoordinates, 0, coordinates, this.stride); + this.changed(); + } +} inherits(MultiPoint, SimpleGeometry); -/** - * Append the passed point to this multipoint. - * @param {module:ol/geom/Point} point Point. - * @api - */ -MultiPoint.prototype.appendPoint = function(point) { - if (!this.flatCoordinates) { - this.flatCoordinates = point.getFlatCoordinates().slice(); - } else { - extend(this.flatCoordinates, point.getFlatCoordinates()); - } - this.changed(); -}; - - -/** - * Make a complete copy of the geometry. - * @return {!module:ol/geom/MultiPoint} Clone. - * @override - * @api - */ -MultiPoint.prototype.clone = function() { - const multiPoint = new MultiPoint(this.flatCoordinates.slice(), this.layout); - return multiPoint; -}; - - -/** - * @inheritDoc - */ -MultiPoint.prototype.closestPointXY = function(x, y, closestPoint, minSquaredDistance) { - if (minSquaredDistance < closestSquaredDistanceXY(this.getExtent(), x, y)) { - return minSquaredDistance; - } - const flatCoordinates = this.flatCoordinates; - const stride = this.stride; - for (let i = 0, ii = flatCoordinates.length; i < ii; i += stride) { - const squaredDistance = squaredDx( - x, y, flatCoordinates[i], flatCoordinates[i + 1]); - if (squaredDistance < minSquaredDistance) { - minSquaredDistance = squaredDistance; - for (let j = 0; j < stride; ++j) { - closestPoint[j] = flatCoordinates[i + j]; - } - closestPoint.length = stride; - } - } - return minSquaredDistance; -}; - - -/** - * Return the coordinates of the multipoint. - * @return {Array.} Coordinates. - * @override - * @api - */ -MultiPoint.prototype.getCoordinates = function() { - return inflateCoordinates( - this.flatCoordinates, 0, this.flatCoordinates.length, this.stride); -}; - - -/** - * Return the point at the specified index. - * @param {number} index Index. - * @return {module:ol/geom/Point} Point. - * @api - */ -MultiPoint.prototype.getPoint = function(index) { - const n = !this.flatCoordinates ? 0 : this.flatCoordinates.length / this.stride; - if (index < 0 || n <= index) { - return null; - } - return new Point(this.flatCoordinates.slice( - index * this.stride, (index + 1) * this.stride), this.layout); -}; - - -/** - * Return the points of this multipoint. - * @return {Array.} Points. - * @api - */ -MultiPoint.prototype.getPoints = function() { - const flatCoordinates = this.flatCoordinates; - const layout = this.layout; - const stride = this.stride; - /** @type {Array.} */ - const points = []; - for (let i = 0, ii = flatCoordinates.length; i < ii; i += stride) { - const point = new Point(flatCoordinates.slice(i, i + stride), layout); - points.push(point); - } - return points; -}; - - -/** - * @inheritDoc - * @api - */ -MultiPoint.prototype.getType = function() { - return GeometryType.MULTI_POINT; -}; - - -/** - * @inheritDoc - * @api - */ -MultiPoint.prototype.intersectsExtent = function(extent) { - const flatCoordinates = this.flatCoordinates; - const stride = this.stride; - for (let i = 0, ii = flatCoordinates.length; i < ii; i += stride) { - const x = flatCoordinates[i]; - const y = flatCoordinates[i + 1]; - if (containsXY(extent, x, y)) { - return true; - } - } - return false; -}; - - -/** - * Set the coordinates of the multipoint. - * @param {!Array.} coordinates Coordinates. - * @param {module:ol/geom/GeometryLayout=} opt_layout Layout. - * @override - * @api - */ -MultiPoint.prototype.setCoordinates = function(coordinates, opt_layout) { - this.setLayout(opt_layout, coordinates, 1); - if (!this.flatCoordinates) { - this.flatCoordinates = []; - } - this.flatCoordinates.length = deflateCoordinates( - this.flatCoordinates, 0, coordinates, this.stride); - this.changed(); -}; export default MultiPoint; diff --git a/src/ol/geom/MultiPolygon.js b/src/ol/geom/MultiPolygon.js index 990dd159c2..a5bb32c476 100644 --- a/src/ol/geom/MultiPolygon.js +++ b/src/ol/geom/MultiPolygon.js @@ -34,360 +34,346 @@ import {quantizeMultiArray} from '../geom/flat/simplify.js'; * coordinates. * @api */ -const MultiPolygon = function(coordinates, opt_layout, opt_endss) { +class MultiPolygon { + constructor(coordinates, opt_layout, opt_endss) { - SimpleGeometry.call(this); + SimpleGeometry.call(this); - /** - * @type {Array.>} - * @private - */ - this.endss_ = []; + /** + * @type {Array.>} + * @private + */ + this.endss_ = []; - /** - * @private - * @type {number} - */ - this.flatInteriorPointsRevision_ = -1; + /** + * @private + * @type {number} + */ + this.flatInteriorPointsRevision_ = -1; - /** - * @private - * @type {Array.} - */ - this.flatInteriorPoints_ = null; + /** + * @private + * @type {Array.} + */ + this.flatInteriorPoints_ = null; - /** - * @private - * @type {number} - */ - this.maxDelta_ = -1; + /** + * @private + * @type {number} + */ + this.maxDelta_ = -1; - /** - * @private - * @type {number} - */ - this.maxDeltaRevision_ = -1; + /** + * @private + * @type {number} + */ + this.maxDeltaRevision_ = -1; - /** - * @private - * @type {number} - */ - this.orientedRevision_ = -1; + /** + * @private + * @type {number} + */ + this.orientedRevision_ = -1; - /** - * @private - * @type {Array.} - */ - this.orientedFlatCoordinates_ = null; + /** + * @private + * @type {Array.} + */ + this.orientedFlatCoordinates_ = null; - if (!opt_endss && !Array.isArray(coordinates[0])) { - let layout = this.getLayout(); - const flatCoordinates = []; - const endss = []; - for (let i = 0, ii = coordinates.length; i < ii; ++i) { - const polygon = coordinates[i]; - if (i === 0) { - layout = polygon.getLayout(); + if (!opt_endss && !Array.isArray(coordinates[0])) { + let layout = this.getLayout(); + const flatCoordinates = []; + const endss = []; + for (let i = 0, ii = coordinates.length; i < ii; ++i) { + const polygon = coordinates[i]; + if (i === 0) { + layout = polygon.getLayout(); + } + const offset = flatCoordinates.length; + const ends = polygon.getEnds(); + for (let j = 0, jj = ends.length; j < jj; ++j) { + ends[j] += offset; + } + extend(flatCoordinates, polygon.getFlatCoordinates()); + endss.push(ends); } - const offset = flatCoordinates.length; - const ends = polygon.getEnds(); - for (let j = 0, jj = ends.length; j < jj; ++j) { - ends[j] += offset; - } - extend(flatCoordinates, polygon.getFlatCoordinates()); - endss.push(ends); + opt_layout = layout; + coordinates = flatCoordinates; + opt_endss = endss; } - opt_layout = layout; - coordinates = flatCoordinates; - opt_endss = endss; - } - if (opt_layout !== undefined && opt_endss) { - this.setFlatCoordinates(opt_layout, coordinates); - this.endss_ = opt_endss; - } else { - this.setCoordinates(coordinates, opt_layout); + if (opt_layout !== undefined && opt_endss) { + this.setFlatCoordinates(opt_layout, coordinates); + this.endss_ = opt_endss; + } else { + this.setCoordinates(coordinates, opt_layout); + } + } -}; + /** + * Append the passed polygon to this multipolygon. + * @param {module:ol/geom/Polygon} polygon Polygon. + * @api + */ + appendPolygon(polygon) { + /** @type {Array.} */ + let ends; + if (!this.flatCoordinates) { + this.flatCoordinates = polygon.getFlatCoordinates().slice(); + ends = polygon.getEnds().slice(); + this.endss_.push(); + } else { + const offset = this.flatCoordinates.length; + extend(this.flatCoordinates, polygon.getFlatCoordinates()); + ends = polygon.getEnds().slice(); + for (let i = 0, ii = ends.length; i < ii; ++i) { + ends[i] += offset; + } + } + this.endss_.push(ends); + this.changed(); + } + + /** + * Make a complete copy of the geometry. + * @return {!module:ol/geom/MultiPolygon} Clone. + * @override + * @api + */ + clone() { + const len = this.endss_.length; + const newEndss = new Array(len); + for (let i = 0; i < len; ++i) { + newEndss[i] = this.endss_[i].slice(); + } + + return new MultiPolygon( + this.flatCoordinates.slice(), this.layout, newEndss); + } + + /** + * @inheritDoc + */ + closestPointXY(x, y, closestPoint, minSquaredDistance) { + if (minSquaredDistance < closestSquaredDistanceXY(this.getExtent(), x, y)) { + return minSquaredDistance; + } + if (this.maxDeltaRevision_ != this.getRevision()) { + this.maxDelta_ = Math.sqrt(multiArrayMaxSquaredDelta( + this.flatCoordinates, 0, this.endss_, this.stride, 0)); + this.maxDeltaRevision_ = this.getRevision(); + } + return assignClosestMultiArrayPoint( + this.getOrientedFlatCoordinates(), 0, this.endss_, this.stride, + this.maxDelta_, true, x, y, closestPoint, minSquaredDistance); + } + + /** + * @inheritDoc + */ + containsXY(x, y) { + return linearRingssContainsXY(this.getOrientedFlatCoordinates(), 0, this.endss_, this.stride, x, y); + } + + /** + * Return the area of the multipolygon on projected plane. + * @return {number} Area (on projected plane). + * @api + */ + getArea() { + return linearRingssArea(this.getOrientedFlatCoordinates(), 0, this.endss_, this.stride); + } + + /** + * Get the coordinate array for this geometry. This array has the structure + * of a GeoJSON coordinate array for multi-polygons. + * + * @param {boolean=} opt_right Orient coordinates according to the right-hand + * rule (counter-clockwise for exterior and clockwise for interior rings). + * If `false`, coordinates will be oriented according to the left-hand rule + * (clockwise for exterior and counter-clockwise for interior rings). + * By default, coordinate orientation will depend on how the geometry was + * constructed. + * @return {Array.>>} Coordinates. + * @override + * @api + */ + getCoordinates(opt_right) { + let flatCoordinates; + if (opt_right !== undefined) { + flatCoordinates = this.getOrientedFlatCoordinates().slice(); + orientLinearRingsArray( + flatCoordinates, 0, this.endss_, this.stride, opt_right); + } else { + flatCoordinates = this.flatCoordinates; + } + + return inflateMultiCoordinatesArray( + flatCoordinates, 0, this.endss_, this.stride); + } + + /** + * @return {Array.>} Endss. + */ + getEndss() { + return this.endss_; + } + + /** + * @return {Array.} Flat interior points. + */ + getFlatInteriorPoints() { + if (this.flatInteriorPointsRevision_ != this.getRevision()) { + const flatCenters = linearRingssCenter( + this.flatCoordinates, 0, this.endss_, this.stride); + this.flatInteriorPoints_ = getInteriorPointsOfMultiArray( + this.getOrientedFlatCoordinates(), 0, this.endss_, this.stride, + flatCenters); + this.flatInteriorPointsRevision_ = this.getRevision(); + } + return this.flatInteriorPoints_; + } + + /** + * Return the interior points as {@link module:ol/geom/MultiPoint multipoint}. + * @return {module:ol/geom/MultiPoint} Interior points as XYM coordinates, where M is + * the length of the horizontal intersection that the point belongs to. + * @api + */ + getInteriorPoints() { + return new MultiPoint(this.getFlatInteriorPoints().slice(), GeometryLayout.XYM); + } + + /** + * @return {Array.} Oriented flat coordinates. + */ + getOrientedFlatCoordinates() { + if (this.orientedRevision_ != this.getRevision()) { + const flatCoordinates = this.flatCoordinates; + if (linearRingsAreOriented( + flatCoordinates, 0, this.endss_, this.stride)) { + this.orientedFlatCoordinates_ = flatCoordinates; + } else { + this.orientedFlatCoordinates_ = flatCoordinates.slice(); + this.orientedFlatCoordinates_.length = + orientLinearRingsArray( + this.orientedFlatCoordinates_, 0, this.endss_, this.stride); + } + this.orientedRevision_ = this.getRevision(); + } + return this.orientedFlatCoordinates_; + } + + /** + * @inheritDoc + */ + getSimplifiedGeometryInternal(squaredTolerance) { + const simplifiedFlatCoordinates = []; + const simplifiedEndss = []; + simplifiedFlatCoordinates.length = quantizeMultiArray( + this.flatCoordinates, 0, this.endss_, this.stride, + Math.sqrt(squaredTolerance), + simplifiedFlatCoordinates, 0, simplifiedEndss); + return new MultiPolygon(simplifiedFlatCoordinates, GeometryLayout.XY, simplifiedEndss); + } + + /** + * Return the polygon at the specified index. + * @param {number} index Index. + * @return {module:ol/geom/Polygon} Polygon. + * @api + */ + getPolygon(index) { + if (index < 0 || this.endss_.length <= index) { + return null; + } + let offset; + if (index === 0) { + offset = 0; + } else { + const prevEnds = this.endss_[index - 1]; + offset = prevEnds[prevEnds.length - 1]; + } + const ends = this.endss_[index].slice(); + const end = ends[ends.length - 1]; + if (offset !== 0) { + for (let i = 0, ii = ends.length; i < ii; ++i) { + ends[i] -= offset; + } + } + return new Polygon(this.flatCoordinates.slice(offset, end), this.layout, ends); + } + + /** + * Return the polygons of this multipolygon. + * @return {Array.} Polygons. + * @api + */ + getPolygons() { + const layout = this.layout; + const flatCoordinates = this.flatCoordinates; + const endss = this.endss_; + const polygons = []; + let offset = 0; + for (let i = 0, ii = endss.length; i < ii; ++i) { + const ends = endss[i].slice(); + const end = ends[ends.length - 1]; + if (offset !== 0) { + for (let j = 0, jj = ends.length; j < jj; ++j) { + ends[j] -= offset; + } + } + const polygon = new Polygon(flatCoordinates.slice(offset, end), layout, ends); + polygons.push(polygon); + offset = end; + } + return polygons; + } + + /** + * @inheritDoc + * @api + */ + getType() { + return GeometryType.MULTI_POLYGON; + } + + /** + * @inheritDoc + * @api + */ + intersectsExtent(extent) { + return intersectsLinearRingMultiArray( + this.getOrientedFlatCoordinates(), 0, this.endss_, this.stride, extent); + } + + /** + * Set the coordinates of the multipolygon. + * @param {!Array.>>} coordinates Coordinates. + * @param {module:ol/geom/GeometryLayout=} opt_layout Layout. + * @override + * @api + */ + setCoordinates(coordinates, opt_layout) { + this.setLayout(opt_layout, coordinates, 3); + if (!this.flatCoordinates) { + this.flatCoordinates = []; + } + const endss = deflateMultiCoordinatesArray( + this.flatCoordinates, 0, coordinates, this.stride, this.endss_); + if (endss.length === 0) { + this.flatCoordinates.length = 0; + } else { + const lastEnds = endss[endss.length - 1]; + this.flatCoordinates.length = lastEnds.length === 0 ? + 0 : lastEnds[lastEnds.length - 1]; + } + this.changed(); + } +} inherits(MultiPolygon, SimpleGeometry); -/** - * Append the passed polygon to this multipolygon. - * @param {module:ol/geom/Polygon} polygon Polygon. - * @api - */ -MultiPolygon.prototype.appendPolygon = function(polygon) { - /** @type {Array.} */ - let ends; - if (!this.flatCoordinates) { - this.flatCoordinates = polygon.getFlatCoordinates().slice(); - ends = polygon.getEnds().slice(); - this.endss_.push(); - } else { - const offset = this.flatCoordinates.length; - extend(this.flatCoordinates, polygon.getFlatCoordinates()); - ends = polygon.getEnds().slice(); - for (let i = 0, ii = ends.length; i < ii; ++i) { - ends[i] += offset; - } - } - this.endss_.push(ends); - this.changed(); -}; - - -/** - * Make a complete copy of the geometry. - * @return {!module:ol/geom/MultiPolygon} Clone. - * @override - * @api - */ -MultiPolygon.prototype.clone = function() { - const len = this.endss_.length; - const newEndss = new Array(len); - for (let i = 0; i < len; ++i) { - newEndss[i] = this.endss_[i].slice(); - } - - return new MultiPolygon( - this.flatCoordinates.slice(), this.layout, newEndss); -}; - - -/** - * @inheritDoc - */ -MultiPolygon.prototype.closestPointXY = function(x, y, closestPoint, minSquaredDistance) { - if (minSquaredDistance < closestSquaredDistanceXY(this.getExtent(), x, y)) { - return minSquaredDistance; - } - if (this.maxDeltaRevision_ != this.getRevision()) { - this.maxDelta_ = Math.sqrt(multiArrayMaxSquaredDelta( - this.flatCoordinates, 0, this.endss_, this.stride, 0)); - this.maxDeltaRevision_ = this.getRevision(); - } - return assignClosestMultiArrayPoint( - this.getOrientedFlatCoordinates(), 0, this.endss_, this.stride, - this.maxDelta_, true, x, y, closestPoint, minSquaredDistance); -}; - - -/** - * @inheritDoc - */ -MultiPolygon.prototype.containsXY = function(x, y) { - return linearRingssContainsXY(this.getOrientedFlatCoordinates(), 0, this.endss_, this.stride, x, y); -}; - - -/** - * Return the area of the multipolygon on projected plane. - * @return {number} Area (on projected plane). - * @api - */ -MultiPolygon.prototype.getArea = function() { - return linearRingssArea(this.getOrientedFlatCoordinates(), 0, this.endss_, this.stride); -}; - - -/** - * Get the coordinate array for this geometry. This array has the structure - * of a GeoJSON coordinate array for multi-polygons. - * - * @param {boolean=} opt_right Orient coordinates according to the right-hand - * rule (counter-clockwise for exterior and clockwise for interior rings). - * If `false`, coordinates will be oriented according to the left-hand rule - * (clockwise for exterior and counter-clockwise for interior rings). - * By default, coordinate orientation will depend on how the geometry was - * constructed. - * @return {Array.>>} Coordinates. - * @override - * @api - */ -MultiPolygon.prototype.getCoordinates = function(opt_right) { - let flatCoordinates; - if (opt_right !== undefined) { - flatCoordinates = this.getOrientedFlatCoordinates().slice(); - orientLinearRingsArray( - flatCoordinates, 0, this.endss_, this.stride, opt_right); - } else { - flatCoordinates = this.flatCoordinates; - } - - return inflateMultiCoordinatesArray( - flatCoordinates, 0, this.endss_, this.stride); -}; - - -/** - * @return {Array.>} Endss. - */ -MultiPolygon.prototype.getEndss = function() { - return this.endss_; -}; - - -/** - * @return {Array.} Flat interior points. - */ -MultiPolygon.prototype.getFlatInteriorPoints = function() { - if (this.flatInteriorPointsRevision_ != this.getRevision()) { - const flatCenters = linearRingssCenter( - this.flatCoordinates, 0, this.endss_, this.stride); - this.flatInteriorPoints_ = getInteriorPointsOfMultiArray( - this.getOrientedFlatCoordinates(), 0, this.endss_, this.stride, - flatCenters); - this.flatInteriorPointsRevision_ = this.getRevision(); - } - return this.flatInteriorPoints_; -}; - - -/** - * Return the interior points as {@link module:ol/geom/MultiPoint multipoint}. - * @return {module:ol/geom/MultiPoint} Interior points as XYM coordinates, where M is - * the length of the horizontal intersection that the point belongs to. - * @api - */ -MultiPolygon.prototype.getInteriorPoints = function() { - return new MultiPoint(this.getFlatInteriorPoints().slice(), GeometryLayout.XYM); -}; - - -/** - * @return {Array.} Oriented flat coordinates. - */ -MultiPolygon.prototype.getOrientedFlatCoordinates = function() { - if (this.orientedRevision_ != this.getRevision()) { - const flatCoordinates = this.flatCoordinates; - if (linearRingsAreOriented( - flatCoordinates, 0, this.endss_, this.stride)) { - this.orientedFlatCoordinates_ = flatCoordinates; - } else { - this.orientedFlatCoordinates_ = flatCoordinates.slice(); - this.orientedFlatCoordinates_.length = - orientLinearRingsArray( - this.orientedFlatCoordinates_, 0, this.endss_, this.stride); - } - this.orientedRevision_ = this.getRevision(); - } - return this.orientedFlatCoordinates_; -}; - - -/** - * @inheritDoc - */ -MultiPolygon.prototype.getSimplifiedGeometryInternal = function(squaredTolerance) { - const simplifiedFlatCoordinates = []; - const simplifiedEndss = []; - simplifiedFlatCoordinates.length = quantizeMultiArray( - this.flatCoordinates, 0, this.endss_, this.stride, - Math.sqrt(squaredTolerance), - simplifiedFlatCoordinates, 0, simplifiedEndss); - return new MultiPolygon(simplifiedFlatCoordinates, GeometryLayout.XY, simplifiedEndss); -}; - - -/** - * Return the polygon at the specified index. - * @param {number} index Index. - * @return {module:ol/geom/Polygon} Polygon. - * @api - */ -MultiPolygon.prototype.getPolygon = function(index) { - if (index < 0 || this.endss_.length <= index) { - return null; - } - let offset; - if (index === 0) { - offset = 0; - } else { - const prevEnds = this.endss_[index - 1]; - offset = prevEnds[prevEnds.length - 1]; - } - const ends = this.endss_[index].slice(); - const end = ends[ends.length - 1]; - if (offset !== 0) { - for (let i = 0, ii = ends.length; i < ii; ++i) { - ends[i] -= offset; - } - } - return new Polygon(this.flatCoordinates.slice(offset, end), this.layout, ends); -}; - - -/** - * Return the polygons of this multipolygon. - * @return {Array.} Polygons. - * @api - */ -MultiPolygon.prototype.getPolygons = function() { - const layout = this.layout; - const flatCoordinates = this.flatCoordinates; - const endss = this.endss_; - const polygons = []; - let offset = 0; - for (let i = 0, ii = endss.length; i < ii; ++i) { - const ends = endss[i].slice(); - const end = ends[ends.length - 1]; - if (offset !== 0) { - for (let j = 0, jj = ends.length; j < jj; ++j) { - ends[j] -= offset; - } - } - const polygon = new Polygon(flatCoordinates.slice(offset, end), layout, ends); - polygons.push(polygon); - offset = end; - } - return polygons; -}; - - -/** - * @inheritDoc - * @api - */ -MultiPolygon.prototype.getType = function() { - return GeometryType.MULTI_POLYGON; -}; - - -/** - * @inheritDoc - * @api - */ -MultiPolygon.prototype.intersectsExtent = function(extent) { - return intersectsLinearRingMultiArray( - this.getOrientedFlatCoordinates(), 0, this.endss_, this.stride, extent); -}; - - -/** - * Set the coordinates of the multipolygon. - * @param {!Array.>>} coordinates Coordinates. - * @param {module:ol/geom/GeometryLayout=} opt_layout Layout. - * @override - * @api - */ -MultiPolygon.prototype.setCoordinates = function(coordinates, opt_layout) { - this.setLayout(opt_layout, coordinates, 3); - if (!this.flatCoordinates) { - this.flatCoordinates = []; - } - const endss = deflateMultiCoordinatesArray( - this.flatCoordinates, 0, coordinates, this.stride, this.endss_); - if (endss.length === 0) { - this.flatCoordinates.length = 0; - } else { - const lastEnds = endss[endss.length - 1]; - this.flatCoordinates.length = lastEnds.length === 0 ? - 0 : lastEnds[lastEnds.length - 1]; - } - this.changed(); -}; - - export default MultiPolygon; diff --git a/src/ol/geom/Point.js b/src/ol/geom/Point.js index dd1548189e..e93b7a5514 100644 --- a/src/ol/geom/Point.js +++ b/src/ol/geom/Point.js @@ -18,94 +18,90 @@ import {squaredDistance as squaredDx} from '../math.js'; * @param {module:ol/geom/GeometryLayout=} opt_layout Layout. * @api */ -const Point = function(coordinates, opt_layout) { - SimpleGeometry.call(this); - this.setCoordinates(coordinates, opt_layout); -}; +class Point { + constructor(coordinates, opt_layout) { + SimpleGeometry.call(this); + this.setCoordinates(coordinates, opt_layout); + } + + /** + * Make a complete copy of the geometry. + * @return {!module:ol/geom/Point} Clone. + * @override + * @api + */ + clone() { + const point = new Point(this.flatCoordinates.slice(), this.layout); + return point; + } + + /** + * @inheritDoc + */ + closestPointXY(x, y, closestPoint, minSquaredDistance) { + const flatCoordinates = this.flatCoordinates; + const squaredDistance = squaredDx(x, y, flatCoordinates[0], flatCoordinates[1]); + if (squaredDistance < minSquaredDistance) { + const stride = this.stride; + for (let i = 0; i < stride; ++i) { + closestPoint[i] = flatCoordinates[i]; + } + closestPoint.length = stride; + return squaredDistance; + } else { + return minSquaredDistance; + } + } + + /** + * Return the coordinate of the point. + * @return {module:ol/coordinate~Coordinate} Coordinates. + * @override + * @api + */ + getCoordinates() { + return !this.flatCoordinates ? [] : this.flatCoordinates.slice(); + } + + /** + * @inheritDoc + */ + computeExtent(extent) { + return createOrUpdateFromCoordinate(this.flatCoordinates, extent); + } + + /** + * @inheritDoc + * @api + */ + getType() { + return GeometryType.POINT; + } + + /** + * @inheritDoc + * @api + */ + intersectsExtent(extent) { + return containsXY(extent, this.flatCoordinates[0], this.flatCoordinates[1]); + } + + /** + * @inheritDoc + * @api + */ + setCoordinates(coordinates, opt_layout) { + this.setLayout(opt_layout, coordinates, 0); + if (!this.flatCoordinates) { + this.flatCoordinates = []; + } + this.flatCoordinates.length = deflateCoordinate( + this.flatCoordinates, 0, coordinates, this.stride); + this.changed(); + } +} inherits(Point, SimpleGeometry); -/** - * Make a complete copy of the geometry. - * @return {!module:ol/geom/Point} Clone. - * @override - * @api - */ -Point.prototype.clone = function() { - const point = new Point(this.flatCoordinates.slice(), this.layout); - return point; -}; - - -/** - * @inheritDoc - */ -Point.prototype.closestPointXY = function(x, y, closestPoint, minSquaredDistance) { - const flatCoordinates = this.flatCoordinates; - const squaredDistance = squaredDx(x, y, flatCoordinates[0], flatCoordinates[1]); - if (squaredDistance < minSquaredDistance) { - const stride = this.stride; - for (let i = 0; i < stride; ++i) { - closestPoint[i] = flatCoordinates[i]; - } - closestPoint.length = stride; - return squaredDistance; - } else { - return minSquaredDistance; - } -}; - - -/** - * Return the coordinate of the point. - * @return {module:ol/coordinate~Coordinate} Coordinates. - * @override - * @api - */ -Point.prototype.getCoordinates = function() { - return !this.flatCoordinates ? [] : this.flatCoordinates.slice(); -}; - - -/** - * @inheritDoc - */ -Point.prototype.computeExtent = function(extent) { - return createOrUpdateFromCoordinate(this.flatCoordinates, extent); -}; - - -/** - * @inheritDoc - * @api - */ -Point.prototype.getType = function() { - return GeometryType.POINT; -}; - - -/** - * @inheritDoc - * @api - */ -Point.prototype.intersectsExtent = function(extent) { - return containsXY(extent, this.flatCoordinates[0], this.flatCoordinates[1]); -}; - - -/** - * @inheritDoc - * @api - */ -Point.prototype.setCoordinates = function(coordinates, opt_layout) { - this.setLayout(opt_layout, coordinates, 0); - if (!this.flatCoordinates) { - this.flatCoordinates = []; - } - this.flatCoordinates.length = deflateCoordinate( - this.flatCoordinates, 0, coordinates, this.stride); - this.changed(); -}; - export default Point; diff --git a/src/ol/geom/Polygon.js b/src/ol/geom/Polygon.js index eeca8df751..87fa4215c0 100644 --- a/src/ol/geom/Polygon.js +++ b/src/ol/geom/Polygon.js @@ -39,314 +39,300 @@ import {modulo} from '../math.js'; * coordinates). * @api */ -const Polygon = function(coordinates, opt_layout, opt_ends) { +class Polygon { + constructor(coordinates, opt_layout, opt_ends) { - SimpleGeometry.call(this); + SimpleGeometry.call(this); - /** - * @type {Array.} - * @private - */ - this.ends_ = []; + /** + * @type {Array.} + * @private + */ + this.ends_ = []; - /** - * @private - * @type {number} - */ - this.flatInteriorPointRevision_ = -1; + /** + * @private + * @type {number} + */ + this.flatInteriorPointRevision_ = -1; - /** - * @private - * @type {module:ol/coordinate~Coordinate} - */ - this.flatInteriorPoint_ = null; + /** + * @private + * @type {module:ol/coordinate~Coordinate} + */ + this.flatInteriorPoint_ = null; - /** - * @private - * @type {number} - */ - this.maxDelta_ = -1; + /** + * @private + * @type {number} + */ + this.maxDelta_ = -1; - /** - * @private - * @type {number} - */ - this.maxDeltaRevision_ = -1; + /** + * @private + * @type {number} + */ + this.maxDeltaRevision_ = -1; - /** - * @private - * @type {number} - */ - this.orientedRevision_ = -1; + /** + * @private + * @type {number} + */ + this.orientedRevision_ = -1; - /** - * @private - * @type {Array.} - */ - this.orientedFlatCoordinates_ = null; + /** + * @private + * @type {Array.} + */ + this.orientedFlatCoordinates_ = null; + + if (opt_layout !== undefined && opt_ends) { + this.setFlatCoordinates(opt_layout, coordinates); + this.ends_ = opt_ends; + } else { + this.setCoordinates(coordinates, opt_layout); + } - if (opt_layout !== undefined && opt_ends) { - this.setFlatCoordinates(opt_layout, coordinates); - this.ends_ = opt_ends; - } else { - this.setCoordinates(coordinates, opt_layout); } -}; + /** + * Append the passed linear ring to this polygon. + * @param {module:ol/geom/LinearRing} linearRing Linear ring. + * @api + */ + appendLinearRing(linearRing) { + if (!this.flatCoordinates) { + this.flatCoordinates = linearRing.getFlatCoordinates().slice(); + } else { + extend(this.flatCoordinates, linearRing.getFlatCoordinates()); + } + this.ends_.push(this.flatCoordinates.length); + this.changed(); + } + + /** + * Make a complete copy of the geometry. + * @return {!module:ol/geom/Polygon} Clone. + * @override + * @api + */ + clone() { + return new Polygon(this.flatCoordinates.slice(), this.layout, this.ends_.slice()); + } + + /** + * @inheritDoc + */ + closestPointXY(x, y, closestPoint, minSquaredDistance) { + if (minSquaredDistance < closestSquaredDistanceXY(this.getExtent(), x, y)) { + return minSquaredDistance; + } + if (this.maxDeltaRevision_ != this.getRevision()) { + this.maxDelta_ = Math.sqrt(arrayMaxSquaredDelta( + this.flatCoordinates, 0, this.ends_, this.stride, 0)); + this.maxDeltaRevision_ = this.getRevision(); + } + return assignClosestArrayPoint( + this.flatCoordinates, 0, this.ends_, this.stride, + this.maxDelta_, true, x, y, closestPoint, minSquaredDistance); + } + + /** + * @inheritDoc + */ + containsXY(x, y) { + return linearRingsContainsXY(this.getOrientedFlatCoordinates(), 0, this.ends_, this.stride, x, y); + } + + /** + * Return the area of the polygon on projected plane. + * @return {number} Area (on projected plane). + * @api + */ + getArea() { + return linearRingsArea(this.getOrientedFlatCoordinates(), 0, this.ends_, this.stride); + } + + /** + * Get the coordinate array for this geometry. This array has the structure + * of a GeoJSON coordinate array for polygons. + * + * @param {boolean=} opt_right Orient coordinates according to the right-hand + * rule (counter-clockwise for exterior and clockwise for interior rings). + * If `false`, coordinates will be oriented according to the left-hand rule + * (clockwise for exterior and counter-clockwise for interior rings). + * By default, coordinate orientation will depend on how the geometry was + * constructed. + * @return {Array.>} Coordinates. + * @override + * @api + */ + getCoordinates(opt_right) { + let flatCoordinates; + if (opt_right !== undefined) { + flatCoordinates = this.getOrientedFlatCoordinates().slice(); + orientLinearRings( + flatCoordinates, 0, this.ends_, this.stride, opt_right); + } else { + flatCoordinates = this.flatCoordinates; + } + + return inflateCoordinatesArray( + flatCoordinates, 0, this.ends_, this.stride); + } + + /** + * @return {Array.} Ends. + */ + getEnds() { + return this.ends_; + } + + /** + * @return {Array.} Interior point. + */ + getFlatInteriorPoint() { + if (this.flatInteriorPointRevision_ != this.getRevision()) { + const flatCenter = getCenter(this.getExtent()); + this.flatInteriorPoint_ = getInteriorPointOfArray( + this.getOrientedFlatCoordinates(), 0, this.ends_, this.stride, + flatCenter, 0); + this.flatInteriorPointRevision_ = this.getRevision(); + } + return this.flatInteriorPoint_; + } + + /** + * Return an interior point of the polygon. + * @return {module:ol/geom/Point} Interior point as XYM coordinate, where M is the + * length of the horizontal intersection that the point belongs to. + * @api + */ + getInteriorPoint() { + return new Point(this.getFlatInteriorPoint(), GeometryLayout.XYM); + } + + /** + * Return the number of rings of the polygon, this includes the exterior + * ring and any interior rings. + * + * @return {number} Number of rings. + * @api + */ + getLinearRingCount() { + return this.ends_.length; + } + + /** + * Return the Nth linear ring of the polygon geometry. Return `null` if the + * given index is out of range. + * The exterior linear ring is available at index `0` and the interior rings + * at index `1` and beyond. + * + * @param {number} index Index. + * @return {module:ol/geom/LinearRing} Linear ring. + * @api + */ + getLinearRing(index) { + if (index < 0 || this.ends_.length <= index) { + return null; + } + return new LinearRing(this.flatCoordinates.slice( + index === 0 ? 0 : this.ends_[index - 1], this.ends_[index]), this.layout); + } + + /** + * Return the linear rings of the polygon. + * @return {Array.} Linear rings. + * @api + */ + getLinearRings() { + const layout = this.layout; + const flatCoordinates = this.flatCoordinates; + const ends = this.ends_; + const linearRings = []; + let offset = 0; + for (let i = 0, ii = ends.length; i < ii; ++i) { + const end = ends[i]; + const linearRing = new LinearRing(flatCoordinates.slice(offset, end), layout); + linearRings.push(linearRing); + offset = end; + } + return linearRings; + } + + /** + * @return {Array.} Oriented flat coordinates. + */ + getOrientedFlatCoordinates() { + if (this.orientedRevision_ != this.getRevision()) { + const flatCoordinates = this.flatCoordinates; + if (linearRingIsOriented( + flatCoordinates, 0, this.ends_, this.stride)) { + this.orientedFlatCoordinates_ = flatCoordinates; + } else { + this.orientedFlatCoordinates_ = flatCoordinates.slice(); + this.orientedFlatCoordinates_.length = + orientLinearRings( + this.orientedFlatCoordinates_, 0, this.ends_, this.stride); + } + this.orientedRevision_ = this.getRevision(); + } + return this.orientedFlatCoordinates_; + } + + /** + * @inheritDoc + */ + getSimplifiedGeometryInternal(squaredTolerance) { + const simplifiedFlatCoordinates = []; + const simplifiedEnds = []; + simplifiedFlatCoordinates.length = quantizeArray( + this.flatCoordinates, 0, this.ends_, this.stride, + Math.sqrt(squaredTolerance), + simplifiedFlatCoordinates, 0, simplifiedEnds); + return new Polygon(simplifiedFlatCoordinates, GeometryLayout.XY, simplifiedEnds); + } + + /** + * @inheritDoc + * @api + */ + getType() { + return GeometryType.POLYGON; + } + + /** + * @inheritDoc + * @api + */ + intersectsExtent(extent) { + return intersectsLinearRingArray( + this.getOrientedFlatCoordinates(), 0, this.ends_, this.stride, extent); + } + + /** + * Set the coordinates of the polygon. + * @param {!Array.>} coordinates Coordinates. + * @param {module:ol/geom/GeometryLayout=} opt_layout Layout. + * @override + * @api + */ + setCoordinates(coordinates, opt_layout) { + this.setLayout(opt_layout, coordinates, 2); + if (!this.flatCoordinates) { + this.flatCoordinates = []; + } + const ends = deflateCoordinatesArray( + this.flatCoordinates, 0, coordinates, this.stride, this.ends_); + this.flatCoordinates.length = ends.length === 0 ? 0 : ends[ends.length - 1]; + this.changed(); + } +} inherits(Polygon, SimpleGeometry); -/** - * Append the passed linear ring to this polygon. - * @param {module:ol/geom/LinearRing} linearRing Linear ring. - * @api - */ -Polygon.prototype.appendLinearRing = function(linearRing) { - if (!this.flatCoordinates) { - this.flatCoordinates = linearRing.getFlatCoordinates().slice(); - } else { - extend(this.flatCoordinates, linearRing.getFlatCoordinates()); - } - this.ends_.push(this.flatCoordinates.length); - this.changed(); -}; - - -/** - * Make a complete copy of the geometry. - * @return {!module:ol/geom/Polygon} Clone. - * @override - * @api - */ -Polygon.prototype.clone = function() { - return new Polygon(this.flatCoordinates.slice(), this.layout, this.ends_.slice()); -}; - - -/** - * @inheritDoc - */ -Polygon.prototype.closestPointXY = function(x, y, closestPoint, minSquaredDistance) { - if (minSquaredDistance < closestSquaredDistanceXY(this.getExtent(), x, y)) { - return minSquaredDistance; - } - if (this.maxDeltaRevision_ != this.getRevision()) { - this.maxDelta_ = Math.sqrt(arrayMaxSquaredDelta( - this.flatCoordinates, 0, this.ends_, this.stride, 0)); - this.maxDeltaRevision_ = this.getRevision(); - } - return assignClosestArrayPoint( - this.flatCoordinates, 0, this.ends_, this.stride, - this.maxDelta_, true, x, y, closestPoint, minSquaredDistance); -}; - - -/** - * @inheritDoc - */ -Polygon.prototype.containsXY = function(x, y) { - return linearRingsContainsXY(this.getOrientedFlatCoordinates(), 0, this.ends_, this.stride, x, y); -}; - - -/** - * Return the area of the polygon on projected plane. - * @return {number} Area (on projected plane). - * @api - */ -Polygon.prototype.getArea = function() { - return linearRingsArea(this.getOrientedFlatCoordinates(), 0, this.ends_, this.stride); -}; - - -/** - * Get the coordinate array for this geometry. This array has the structure - * of a GeoJSON coordinate array for polygons. - * - * @param {boolean=} opt_right Orient coordinates according to the right-hand - * rule (counter-clockwise for exterior and clockwise for interior rings). - * If `false`, coordinates will be oriented according to the left-hand rule - * (clockwise for exterior and counter-clockwise for interior rings). - * By default, coordinate orientation will depend on how the geometry was - * constructed. - * @return {Array.>} Coordinates. - * @override - * @api - */ -Polygon.prototype.getCoordinates = function(opt_right) { - let flatCoordinates; - if (opt_right !== undefined) { - flatCoordinates = this.getOrientedFlatCoordinates().slice(); - orientLinearRings( - flatCoordinates, 0, this.ends_, this.stride, opt_right); - } else { - flatCoordinates = this.flatCoordinates; - } - - return inflateCoordinatesArray( - flatCoordinates, 0, this.ends_, this.stride); -}; - - -/** - * @return {Array.} Ends. - */ -Polygon.prototype.getEnds = function() { - return this.ends_; -}; - - -/** - * @return {Array.} Interior point. - */ -Polygon.prototype.getFlatInteriorPoint = function() { - if (this.flatInteriorPointRevision_ != this.getRevision()) { - const flatCenter = getCenter(this.getExtent()); - this.flatInteriorPoint_ = getInteriorPointOfArray( - this.getOrientedFlatCoordinates(), 0, this.ends_, this.stride, - flatCenter, 0); - this.flatInteriorPointRevision_ = this.getRevision(); - } - return this.flatInteriorPoint_; -}; - - -/** - * Return an interior point of the polygon. - * @return {module:ol/geom/Point} Interior point as XYM coordinate, where M is the - * length of the horizontal intersection that the point belongs to. - * @api - */ -Polygon.prototype.getInteriorPoint = function() { - return new Point(this.getFlatInteriorPoint(), GeometryLayout.XYM); -}; - - -/** - * Return the number of rings of the polygon, this includes the exterior - * ring and any interior rings. - * - * @return {number} Number of rings. - * @api - */ -Polygon.prototype.getLinearRingCount = function() { - return this.ends_.length; -}; - - -/** - * Return the Nth linear ring of the polygon geometry. Return `null` if the - * given index is out of range. - * The exterior linear ring is available at index `0` and the interior rings - * at index `1` and beyond. - * - * @param {number} index Index. - * @return {module:ol/geom/LinearRing} Linear ring. - * @api - */ -Polygon.prototype.getLinearRing = function(index) { - if (index < 0 || this.ends_.length <= index) { - return null; - } - return new LinearRing(this.flatCoordinates.slice( - index === 0 ? 0 : this.ends_[index - 1], this.ends_[index]), this.layout); -}; - - -/** - * Return the linear rings of the polygon. - * @return {Array.} Linear rings. - * @api - */ -Polygon.prototype.getLinearRings = function() { - const layout = this.layout; - const flatCoordinates = this.flatCoordinates; - const ends = this.ends_; - const linearRings = []; - let offset = 0; - for (let i = 0, ii = ends.length; i < ii; ++i) { - const end = ends[i]; - const linearRing = new LinearRing(flatCoordinates.slice(offset, end), layout); - linearRings.push(linearRing); - offset = end; - } - return linearRings; -}; - - -/** - * @return {Array.} Oriented flat coordinates. - */ -Polygon.prototype.getOrientedFlatCoordinates = function() { - if (this.orientedRevision_ != this.getRevision()) { - const flatCoordinates = this.flatCoordinates; - if (linearRingIsOriented( - flatCoordinates, 0, this.ends_, this.stride)) { - this.orientedFlatCoordinates_ = flatCoordinates; - } else { - this.orientedFlatCoordinates_ = flatCoordinates.slice(); - this.orientedFlatCoordinates_.length = - orientLinearRings( - this.orientedFlatCoordinates_, 0, this.ends_, this.stride); - } - this.orientedRevision_ = this.getRevision(); - } - return this.orientedFlatCoordinates_; -}; - - -/** - * @inheritDoc - */ -Polygon.prototype.getSimplifiedGeometryInternal = function(squaredTolerance) { - const simplifiedFlatCoordinates = []; - const simplifiedEnds = []; - simplifiedFlatCoordinates.length = quantizeArray( - this.flatCoordinates, 0, this.ends_, this.stride, - Math.sqrt(squaredTolerance), - simplifiedFlatCoordinates, 0, simplifiedEnds); - return new Polygon(simplifiedFlatCoordinates, GeometryLayout.XY, simplifiedEnds); -}; - - -/** - * @inheritDoc - * @api - */ -Polygon.prototype.getType = function() { - return GeometryType.POLYGON; -}; - - -/** - * @inheritDoc - * @api - */ -Polygon.prototype.intersectsExtent = function(extent) { - return intersectsLinearRingArray( - this.getOrientedFlatCoordinates(), 0, this.ends_, this.stride, extent); -}; - - -/** - * Set the coordinates of the polygon. - * @param {!Array.>} coordinates Coordinates. - * @param {module:ol/geom/GeometryLayout=} opt_layout Layout. - * @override - * @api - */ -Polygon.prototype.setCoordinates = function(coordinates, opt_layout) { - this.setLayout(opt_layout, coordinates, 2); - if (!this.flatCoordinates) { - this.flatCoordinates = []; - } - const ends = deflateCoordinatesArray( - this.flatCoordinates, 0, coordinates, this.stride, this.ends_); - this.flatCoordinates.length = ends.length === 0 ? 0 : ends[ends.length - 1]; - this.changed(); -}; - export default Polygon; diff --git a/src/ol/geom/SimpleGeometry.js b/src/ol/geom/SimpleGeometry.js index 52edc56423..e456a5e2ef 100644 --- a/src/ol/geom/SimpleGeometry.js +++ b/src/ol/geom/SimpleGeometry.js @@ -19,29 +19,243 @@ import {clear} from '../obj.js'; * @extends {module:ol/geom/Geometry} * @api */ -const SimpleGeometry = function() { +class SimpleGeometry { + constructor() { - Geometry.call(this); + Geometry.call(this); + + /** + * @protected + * @type {module:ol/geom/GeometryLayout} + */ + this.layout = GeometryLayout.XY; + + /** + * @protected + * @type {number} + */ + this.stride = 2; + + /** + * @protected + * @type {Array.} + */ + this.flatCoordinates = null; + + } /** - * @protected - * @type {module:ol/geom/GeometryLayout} + * @inheritDoc */ - this.layout = GeometryLayout.XY; + computeExtent(extent) { + return createOrUpdateFromFlatCoordinates(this.flatCoordinates, + 0, this.flatCoordinates.length, this.stride, extent); + } /** - * @protected - * @type {number} + * @abstract + * @return {Array} Coordinates. */ - this.stride = 2; + getCoordinates() {} /** - * @protected - * @type {Array.} + * Return the first coordinate of the geometry. + * @return {module:ol/coordinate~Coordinate} First coordinate. + * @api */ - this.flatCoordinates = null; + getFirstCoordinate() { + return this.flatCoordinates.slice(0, this.stride); + } -}; + /** + * @return {Array.} Flat coordinates. + */ + getFlatCoordinates() { + return this.flatCoordinates; + } + + /** + * Return the last coordinate of the geometry. + * @return {module:ol/coordinate~Coordinate} Last point. + * @api + */ + getLastCoordinate() { + return this.flatCoordinates.slice(this.flatCoordinates.length - this.stride); + } + + /** + * Return the {@link module:ol/geom/GeometryLayout~GeometryLayout layout} of the geometry. + * @return {module:ol/geom/GeometryLayout} Layout. + * @api + */ + getLayout() { + return this.layout; + } + + /** + * @inheritDoc + */ + getSimplifiedGeometry(squaredTolerance) { + if (this.simplifiedGeometryRevision != this.getRevision()) { + clear(this.simplifiedGeometryCache); + this.simplifiedGeometryMaxMinSquaredTolerance = 0; + this.simplifiedGeometryRevision = this.getRevision(); + } + // If squaredTolerance is negative or if we know that simplification will not + // have any effect then just return this. + if (squaredTolerance < 0 || + (this.simplifiedGeometryMaxMinSquaredTolerance !== 0 && + squaredTolerance <= this.simplifiedGeometryMaxMinSquaredTolerance)) { + return this; + } + const key = squaredTolerance.toString(); + if (this.simplifiedGeometryCache.hasOwnProperty(key)) { + return this.simplifiedGeometryCache[key]; + } else { + const simplifiedGeometry = + this.getSimplifiedGeometryInternal(squaredTolerance); + const simplifiedFlatCoordinates = simplifiedGeometry.getFlatCoordinates(); + if (simplifiedFlatCoordinates.length < this.flatCoordinates.length) { + this.simplifiedGeometryCache[key] = simplifiedGeometry; + return simplifiedGeometry; + } else { + // Simplification did not actually remove any coordinates. We now know + // that any calls to getSimplifiedGeometry with a squaredTolerance less + // than or equal to the current squaredTolerance will also not have any + // effect. This allows us to short circuit simplification (saving CPU + // cycles) and prevents the cache of simplified geometries from filling + // up with useless identical copies of this geometry (saving memory). + this.simplifiedGeometryMaxMinSquaredTolerance = squaredTolerance; + return this; + } + } + } + + /** + * @param {number} squaredTolerance Squared tolerance. + * @return {module:ol/geom/SimpleGeometry} Simplified geometry. + * @protected + */ + getSimplifiedGeometryInternal(squaredTolerance) { + return this; + } + + /** + * @return {number} Stride. + */ + getStride() { + return this.stride; + } + + /** + * @param {module:ol/geom/GeometryLayout} layout Layout. + * @param {Array.} flatCoordinates Flat coordinates. + */ + setFlatCoordinates(layout, flatCoordinates) { + this.stride = getStrideForLayout(layout); + this.layout = layout; + this.flatCoordinates = flatCoordinates; + } + + /** + * @abstract + * @param {!Array} coordinates Coordinates. + * @param {module:ol/geom/GeometryLayout=} opt_layout Layout. + */ + setCoordinates(coordinates, opt_layout) {} + + /** + * @param {module:ol/geom/GeometryLayout|undefined} layout Layout. + * @param {Array} coordinates Coordinates. + * @param {number} nesting Nesting. + * @protected + */ + setLayout(layout, coordinates, nesting) { + /** @type {number} */ + let stride; + if (layout) { + stride = getStrideForLayout(layout); + } else { + for (let i = 0; i < nesting; ++i) { + if (coordinates.length === 0) { + this.layout = GeometryLayout.XY; + this.stride = 2; + return; + } else { + coordinates = /** @type {Array} */ (coordinates[0]); + } + } + stride = coordinates.length; + layout = getLayoutForStride(stride); + } + this.layout = layout; + this.stride = stride; + } + + /** + * @inheritDoc + * @api + */ + applyTransform(transformFn) { + if (this.flatCoordinates) { + transformFn(this.flatCoordinates, this.flatCoordinates, this.stride); + this.changed(); + } + } + + /** + * @inheritDoc + * @api + */ + rotate(angle, anchor) { + const flatCoordinates = this.getFlatCoordinates(); + if (flatCoordinates) { + const stride = this.getStride(); + rotate( + flatCoordinates, 0, flatCoordinates.length, + stride, angle, anchor, flatCoordinates); + this.changed(); + } + } + + /** + * @inheritDoc + * @api + */ + scale(sx, opt_sy, opt_anchor) { + let sy = opt_sy; + if (sy === undefined) { + sy = sx; + } + let anchor = opt_anchor; + if (!anchor) { + anchor = getCenter(this.getExtent()); + } + const flatCoordinates = this.getFlatCoordinates(); + if (flatCoordinates) { + const stride = this.getStride(); + scale( + flatCoordinates, 0, flatCoordinates.length, + stride, sx, sy, anchor, flatCoordinates); + this.changed(); + } + } + + /** + * @inheritDoc + * @api + */ + translate(deltaX, deltaY) { + const flatCoordinates = this.getFlatCoordinates(); + if (flatCoordinates) { + const stride = this.getStride(); + translate( + flatCoordinates, 0, flatCoordinates.length, stride, + deltaX, deltaY, flatCoordinates); + this.changed(); + } + } +} inherits(SimpleGeometry, Geometry); @@ -88,234 +302,6 @@ export function getStrideForLayout(layout) { SimpleGeometry.prototype.containsXY = FALSE; -/** - * @inheritDoc - */ -SimpleGeometry.prototype.computeExtent = function(extent) { - return createOrUpdateFromFlatCoordinates(this.flatCoordinates, - 0, this.flatCoordinates.length, this.stride, extent); -}; - - -/** - * @abstract - * @return {Array} Coordinates. - */ -SimpleGeometry.prototype.getCoordinates = function() {}; - - -/** - * Return the first coordinate of the geometry. - * @return {module:ol/coordinate~Coordinate} First coordinate. - * @api - */ -SimpleGeometry.prototype.getFirstCoordinate = function() { - return this.flatCoordinates.slice(0, this.stride); -}; - - -/** - * @return {Array.} Flat coordinates. - */ -SimpleGeometry.prototype.getFlatCoordinates = function() { - return this.flatCoordinates; -}; - - -/** - * Return the last coordinate of the geometry. - * @return {module:ol/coordinate~Coordinate} Last point. - * @api - */ -SimpleGeometry.prototype.getLastCoordinate = function() { - return this.flatCoordinates.slice(this.flatCoordinates.length - this.stride); -}; - - -/** - * Return the {@link module:ol/geom/GeometryLayout~GeometryLayout layout} of the geometry. - * @return {module:ol/geom/GeometryLayout} Layout. - * @api - */ -SimpleGeometry.prototype.getLayout = function() { - return this.layout; -}; - - -/** - * @inheritDoc - */ -SimpleGeometry.prototype.getSimplifiedGeometry = function(squaredTolerance) { - if (this.simplifiedGeometryRevision != this.getRevision()) { - clear(this.simplifiedGeometryCache); - this.simplifiedGeometryMaxMinSquaredTolerance = 0; - this.simplifiedGeometryRevision = this.getRevision(); - } - // If squaredTolerance is negative or if we know that simplification will not - // have any effect then just return this. - if (squaredTolerance < 0 || - (this.simplifiedGeometryMaxMinSquaredTolerance !== 0 && - squaredTolerance <= this.simplifiedGeometryMaxMinSquaredTolerance)) { - return this; - } - const key = squaredTolerance.toString(); - if (this.simplifiedGeometryCache.hasOwnProperty(key)) { - return this.simplifiedGeometryCache[key]; - } else { - const simplifiedGeometry = - this.getSimplifiedGeometryInternal(squaredTolerance); - const simplifiedFlatCoordinates = simplifiedGeometry.getFlatCoordinates(); - if (simplifiedFlatCoordinates.length < this.flatCoordinates.length) { - this.simplifiedGeometryCache[key] = simplifiedGeometry; - return simplifiedGeometry; - } else { - // Simplification did not actually remove any coordinates. We now know - // that any calls to getSimplifiedGeometry with a squaredTolerance less - // than or equal to the current squaredTolerance will also not have any - // effect. This allows us to short circuit simplification (saving CPU - // cycles) and prevents the cache of simplified geometries from filling - // up with useless identical copies of this geometry (saving memory). - this.simplifiedGeometryMaxMinSquaredTolerance = squaredTolerance; - return this; - } - } -}; - - -/** - * @param {number} squaredTolerance Squared tolerance. - * @return {module:ol/geom/SimpleGeometry} Simplified geometry. - * @protected - */ -SimpleGeometry.prototype.getSimplifiedGeometryInternal = function(squaredTolerance) { - return this; -}; - - -/** - * @return {number} Stride. - */ -SimpleGeometry.prototype.getStride = function() { - return this.stride; -}; - - -/** - * @param {module:ol/geom/GeometryLayout} layout Layout. - * @param {Array.} flatCoordinates Flat coordinates. - */ -SimpleGeometry.prototype.setFlatCoordinates = function(layout, flatCoordinates) { - this.stride = getStrideForLayout(layout); - this.layout = layout; - this.flatCoordinates = flatCoordinates; -}; - - -/** - * @abstract - * @param {!Array} coordinates Coordinates. - * @param {module:ol/geom/GeometryLayout=} opt_layout Layout. - */ -SimpleGeometry.prototype.setCoordinates = function(coordinates, opt_layout) {}; - - -/** - * @param {module:ol/geom/GeometryLayout|undefined} layout Layout. - * @param {Array} coordinates Coordinates. - * @param {number} nesting Nesting. - * @protected - */ -SimpleGeometry.prototype.setLayout = function(layout, coordinates, nesting) { - /** @type {number} */ - let stride; - if (layout) { - stride = getStrideForLayout(layout); - } else { - for (let i = 0; i < nesting; ++i) { - if (coordinates.length === 0) { - this.layout = GeometryLayout.XY; - this.stride = 2; - return; - } else { - coordinates = /** @type {Array} */ (coordinates[0]); - } - } - stride = coordinates.length; - layout = getLayoutForStride(stride); - } - this.layout = layout; - this.stride = stride; -}; - - -/** - * @inheritDoc - * @api - */ -SimpleGeometry.prototype.applyTransform = function(transformFn) { - if (this.flatCoordinates) { - transformFn(this.flatCoordinates, this.flatCoordinates, this.stride); - this.changed(); - } -}; - - -/** - * @inheritDoc - * @api - */ -SimpleGeometry.prototype.rotate = function(angle, anchor) { - const flatCoordinates = this.getFlatCoordinates(); - if (flatCoordinates) { - const stride = this.getStride(); - rotate( - flatCoordinates, 0, flatCoordinates.length, - stride, angle, anchor, flatCoordinates); - this.changed(); - } -}; - - -/** - * @inheritDoc - * @api - */ -SimpleGeometry.prototype.scale = function(sx, opt_sy, opt_anchor) { - let sy = opt_sy; - if (sy === undefined) { - sy = sx; - } - let anchor = opt_anchor; - if (!anchor) { - anchor = getCenter(this.getExtent()); - } - const flatCoordinates = this.getFlatCoordinates(); - if (flatCoordinates) { - const stride = this.getStride(); - scale( - flatCoordinates, 0, flatCoordinates.length, - stride, sx, sy, anchor, flatCoordinates); - this.changed(); - } -}; - - -/** - * @inheritDoc - * @api - */ -SimpleGeometry.prototype.translate = function(deltaX, deltaY) { - const flatCoordinates = this.getFlatCoordinates(); - if (flatCoordinates) { - const stride = this.getStride(); - translate( - flatCoordinates, 0, flatCoordinates.length, stride, - deltaX, deltaY, flatCoordinates); - this.changed(); - } -}; - - /** * @param {module:ol/geom/SimpleGeometry} simpleGeometry Simple geometry. * @param {module:ol/transform~Transform} transform Transform. diff --git a/src/ol/interaction/DragAndDrop.js b/src/ol/interaction/DragAndDrop.js index 42db71deb2..c34e1cf5e6 100644 --- a/src/ol/interaction/DragAndDrop.js +++ b/src/ol/interaction/DragAndDrop.js @@ -89,47 +89,156 @@ inherits(DragAndDropEvent, Event); * @param {module:ol/interaction/DragAndDrop~Options=} opt_options Options. * @api */ -const DragAndDrop = function(opt_options) { +class DragAndDrop { + constructor(opt_options) { - const options = opt_options ? opt_options : {}; + const options = opt_options ? opt_options : {}; - Interaction.call(this, { - handleEvent: TRUE - }); + Interaction.call(this, { + handleEvent: TRUE + }); + + /** + * @private + * @type {Array.} + */ + this.formatConstructors_ = options.formatConstructors ? + options.formatConstructors : []; + + /** + * @private + * @type {module:ol/proj/Projection} + */ + this.projection_ = options.projection ? + getProjection(options.projection) : null; + + /** + * @private + * @type {Array.} + */ + this.dropListenKeys_ = null; + + /** + * @private + * @type {module:ol/source/Vector} + */ + this.source_ = options.source || null; + + /** + * @private + * @type {Element} + */ + this.target = options.target ? options.target : null; + + } + + /** + * @param {File} file File. + * @param {Event} event Load event. + * @private + */ + handleResult_(file, event) { + const result = event.target.result; + const map = this.getMap(); + let projection = this.projection_; + if (!projection) { + const view = map.getView(); + projection = view.getProjection(); + } + + 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 {module:ol/format/Feature} + */ + const format = new formatConstructor(); + features = this.tryReadFeatures_(format, result, { + featureProjection: projection + }); + if (features && features.length > 0) { + break; + } + } + if (this.source_) { + this.source_.clear(); + this.source_.addFeatures(features); + } + this.dispatchEvent( + new DragAndDropEvent( + DragAndDropEventType.ADD_FEATURES, file, + features, projection)); + } /** * @private - * @type {Array.} */ - this.formatConstructors_ = options.formatConstructors ? - options.formatConstructors : []; + registerListeners_() { + const map = this.getMap(); + if (map) { + const dropArea = this.target ? this.target : map.getViewport(); + this.dropListenKeys_ = [ + listen(dropArea, EventType.DROP, handleDrop, this), + listen(dropArea, EventType.DRAGENTER, handleStop, this), + listen(dropArea, EventType.DRAGOVER, handleStop, this), + listen(dropArea, EventType.DROP, handleStop, this) + ]; + } + } + + /** + * @inheritDoc + */ + setActive(active) { + Interaction.prototype.setActive.call(this, active); + if (active) { + this.registerListeners_(); + } else { + this.unregisterListeners_(); + } + } + + /** + * @inheritDoc + */ + setMap(map) { + this.unregisterListeners_(); + Interaction.prototype.setMap.call(this, map); + if (this.getActive()) { + this.registerListeners_(); + } + } + + /** + * @param {module:ol/format/Feature} format Format. + * @param {string} text Text. + * @param {module:ol/format/Feature~ReadOptions} options Read options. + * @private + * @return {Array.} Features. + */ + tryReadFeatures_(format, text, options) { + try { + return format.readFeatures(text, options); + } catch (e) { + return null; + } + } /** * @private - * @type {module:ol/proj/Projection} */ - this.projection_ = options.projection ? - getProjection(options.projection) : null; - - /** - * @private - * @type {Array.} - */ - this.dropListenKeys_ = null; - - /** - * @private - * @type {module:ol/source/Vector} - */ - this.source_ = options.source || null; - - /** - * @private - * @type {Element} - */ - this.target = options.target ? options.target : null; - -}; + unregisterListeners_() { + if (this.dropListenKeys_) { + this.dropListenKeys_.forEach(unlistenByKey); + this.dropListenKeys_ = null; + } + } +} inherits(DragAndDrop, Interaction); @@ -159,117 +268,4 @@ function handleStop(event) { } -/** - * @param {File} file File. - * @param {Event} event Load event. - * @private - */ -DragAndDrop.prototype.handleResult_ = function(file, event) { - const result = event.target.result; - const map = this.getMap(); - let projection = this.projection_; - if (!projection) { - const view = map.getView(); - projection = view.getProjection(); - } - - 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 {module:ol/format/Feature} - */ - const format = new formatConstructor(); - features = this.tryReadFeatures_(format, result, { - featureProjection: projection - }); - if (features && features.length > 0) { - break; - } - } - if (this.source_) { - this.source_.clear(); - this.source_.addFeatures(features); - } - this.dispatchEvent( - new DragAndDropEvent( - DragAndDropEventType.ADD_FEATURES, file, - features, projection)); -}; - - -/** - * @private - */ -DragAndDrop.prototype.registerListeners_ = function() { - const map = this.getMap(); - if (map) { - const dropArea = this.target ? this.target : map.getViewport(); - this.dropListenKeys_ = [ - listen(dropArea, EventType.DROP, handleDrop, this), - listen(dropArea, EventType.DRAGENTER, handleStop, this), - listen(dropArea, EventType.DRAGOVER, handleStop, this), - listen(dropArea, EventType.DROP, handleStop, this) - ]; - } -}; - - -/** - * @inheritDoc - */ -DragAndDrop.prototype.setActive = function(active) { - Interaction.prototype.setActive.call(this, active); - if (active) { - this.registerListeners_(); - } else { - this.unregisterListeners_(); - } -}; - - -/** - * @inheritDoc - */ -DragAndDrop.prototype.setMap = function(map) { - this.unregisterListeners_(); - Interaction.prototype.setMap.call(this, map); - if (this.getActive()) { - this.registerListeners_(); - } -}; - - -/** - * @param {module:ol/format/Feature} format Format. - * @param {string} text Text. - * @param {module:ol/format/Feature~ReadOptions} options Read options. - * @private - * @return {Array.} Features. - */ -DragAndDrop.prototype.tryReadFeatures_ = function(format, text, options) { - try { - return format.readFeatures(text, options); - } catch (e) { - return null; - } -}; - - -/** - * @private - */ -DragAndDrop.prototype.unregisterListeners_ = function() { - if (this.dropListenKeys_) { - this.dropListenKeys_.forEach(unlistenByKey); - this.dropListenKeys_ = null; - } -}; - - export default DragAndDrop; diff --git a/src/ol/interaction/DragBox.js b/src/ol/interaction/DragBox.js index e5f38f5e32..6befab8280 100644 --- a/src/ol/interaction/DragBox.js +++ b/src/ol/interaction/DragBox.js @@ -110,47 +110,58 @@ inherits(DragBoxEvent, Event); * @param {module:ol/interaction/DragBox~Options=} opt_options Options. * @api */ -const DragBox = function(opt_options) { +class DragBox { + constructor(opt_options) { - PointerInteraction.call(this, { - handleDownEvent: handleDownEvent, - handleDragEvent: handleDragEvent, - handleUpEvent: handleUpEvent - }); + PointerInteraction.call(this, { + handleDownEvent: handleDownEvent, + handleDragEvent: handleDragEvent, + handleUpEvent: handleUpEvent + }); - const options = opt_options ? opt_options : {}; + const options = opt_options ? opt_options : {}; - /** - * @type {module:ol/render/Box} - * @private - */ - this.box_ = new RenderBox(options.className || 'ol-dragbox'); + /** + * @type {module:ol/render/Box} + * @private + */ + this.box_ = new RenderBox(options.className || 'ol-dragbox'); - /** - * @type {number} - * @private - */ - this.minArea_ = options.minArea !== undefined ? options.minArea : 64; + /** + * @type {number} + * @private + */ + this.minArea_ = options.minArea !== undefined ? options.minArea : 64; - /** - * @type {module:ol~Pixel} - * @private - */ - this.startPixel_ = null; + /** + * @type {module:ol~Pixel} + * @private + */ + this.startPixel_ = null; - /** - * @private - * @type {module:ol/events/condition~Condition} - */ - this.condition_ = options.condition ? options.condition : always; + /** + * @private + * @type {module:ol/events/condition~Condition} + */ + this.condition_ = options.condition ? options.condition : always; - /** - * @private - * @type {module:ol/interaction/DragBox~EndCondition} - */ - this.boxEndCondition_ = options.boxEndCondition ? - options.boxEndCondition : defaultBoxEndCondition; -}; + /** + * @private + * @type {module:ol/interaction/DragBox~EndCondition} + */ + this.boxEndCondition_ = options.boxEndCondition ? + options.boxEndCondition : defaultBoxEndCondition; + } + + /** + * Returns geometry of last drawn box. + * @return {module:ol/geom/Polygon} Geometry. + * @api + */ + getGeometry() { + return this.box_.getGeometry(); + } +} inherits(DragBox, PointerInteraction); @@ -188,16 +199,6 @@ function handleDragEvent(mapBrowserEvent) { } -/** - * Returns geometry of last drawn box. - * @return {module:ol/geom/Polygon} Geometry. - * @api - */ -DragBox.prototype.getGeometry = function() { - return this.box_.getGeometry(); -}; - - /** * To be overridden by child classes. * FIXME: use constructor option instead of relying on overriding. diff --git a/src/ol/interaction/DragZoom.js b/src/ol/interaction/DragZoom.js index eb7e312b93..ff0b2ade1e 100644 --- a/src/ol/interaction/DragZoom.js +++ b/src/ol/interaction/DragZoom.js @@ -35,68 +35,71 @@ import DragBox from '../interaction/DragBox.js'; * @param {module:ol/interaction/DragZoom~Options=} opt_options Options. * @api */ -const DragZoom = function(opt_options) { - const options = opt_options ? opt_options : {}; +class DragZoom { + constructor(opt_options) { + const options = opt_options ? opt_options : {}; - const condition = options.condition ? options.condition : shiftKeyOnly; + const condition = options.condition ? options.condition : shiftKeyOnly; + + /** + * @private + * @type {number} + */ + this.duration_ = options.duration !== undefined ? options.duration : 200; + + /** + * @private + * @type {boolean} + */ + this.out_ = options.out !== undefined ? options.out : false; + + DragBox.call(this, { + condition: condition, + className: options.className || 'ol-dragzoom' + }); + + } /** - * @private - * @type {number} + * @inheritDoc */ - this.duration_ = options.duration !== undefined ? options.duration : 200; + onBoxEnd() { + const map = this.getMap(); - /** - * @private - * @type {boolean} - */ - this.out_ = options.out !== undefined ? options.out : false; + const view = /** @type {!module:ol/View} */ (map.getView()); - DragBox.call(this, { - condition: condition, - className: options.className || 'ol-dragzoom' - }); + const size = /** @type {!module:ol/size~Size} */ (map.getSize()); -}; + let extent = this.getGeometry().getExtent(); + + if (this.out_) { + const mapExtent = view.calculateExtent(size); + const boxPixelExtent = createOrUpdateFromCoordinates([ + map.getPixelFromCoordinate(getBottomLeft(extent)), + map.getPixelFromCoordinate(getTopRight(extent))]); + const factor = view.getResolutionForExtent(boxPixelExtent, size); + + scaleFromCenter(mapExtent, 1 / factor); + extent = mapExtent; + } + + const resolution = view.constrainResolution( + view.getResolutionForExtent(extent, size)); + + let center = getCenter(extent); + center = view.constrainCenter(center); + + view.animate({ + resolution: resolution, + center: center, + duration: this.duration_, + easing: easeOut + }); + + } +} inherits(DragZoom, DragBox); -/** - * @inheritDoc - */ -DragZoom.prototype.onBoxEnd = function() { - const map = this.getMap(); - - const view = /** @type {!module:ol/View} */ (map.getView()); - - const size = /** @type {!module:ol/size~Size} */ (map.getSize()); - - let extent = this.getGeometry().getExtent(); - - if (this.out_) { - const mapExtent = view.calculateExtent(size); - const boxPixelExtent = createOrUpdateFromCoordinates([ - map.getPixelFromCoordinate(getBottomLeft(extent)), - map.getPixelFromCoordinate(getTopRight(extent))]); - const factor = view.getResolutionForExtent(boxPixelExtent, size); - - scaleFromCenter(mapExtent, 1 / factor); - extent = mapExtent; - } - - const resolution = view.constrainResolution( - view.getResolutionForExtent(extent, size)); - - let center = getCenter(extent); - center = view.constrainCenter(center); - - view.animate({ - resolution: resolution, - center: center, - duration: this.duration_, - easing: easeOut - }); - -}; export default DragZoom; diff --git a/src/ol/interaction/Draw.js b/src/ol/interaction/Draw.js index cd42fe7137..e30dd6d24b 100644 --- a/src/ol/interaction/Draw.js +++ b/src/ol/interaction/Draw.js @@ -160,277 +160,642 @@ inherits(DrawEvent, Event); * @param {module:ol/interaction/Draw~Options} options Options. * @api */ -const Draw = function(options) { +class Draw { + constructor(options) { - PointerInteraction.call(this, { - handleDownEvent: handleDownEvent, - handleEvent: handleEvent, - handleUpEvent: handleUpEvent - }); + PointerInteraction.call(this, { + handleDownEvent: handleDownEvent, + handleEvent: handleEvent, + handleUpEvent: handleUpEvent + }); - /** - * @type {boolean} - * @private - */ - this.shouldHandle_ = false; + /** + * @type {boolean} + * @private + */ + this.shouldHandle_ = false; - /** - * @type {module:ol~Pixel} - * @private - */ - this.downPx_ = null; + /** + * @type {module:ol~Pixel} + * @private + */ + this.downPx_ = null; - /** - * @type {number|undefined} - * @private - */ - this.downTimeout_; + /** + * @type {number|undefined} + * @private + */ + this.downTimeout_; - /** - * @type {number|undefined} - * @private - */ - this.lastDragTime_; + /** + * @type {number|undefined} + * @private + */ + this.lastDragTime_; - /** - * @type {boolean} - * @private - */ - this.freehand_ = false; + /** + * @type {boolean} + * @private + */ + this.freehand_ = false; - /** - * Target source for drawn features. - * @type {module:ol/source/Vector} - * @private - */ - this.source_ = options.source ? options.source : null; + /** + * Target source for drawn features. + * @type {module:ol/source/Vector} + * @private + */ + this.source_ = options.source ? options.source : null; - /** - * Target collection for drawn features. - * @type {module:ol/Collection.} - * @private - */ - this.features_ = options.features ? options.features : null; + /** + * Target collection for drawn features. + * @type {module:ol/Collection.} + * @private + */ + this.features_ = options.features ? options.features : null; - /** - * Pixel distance for snapping. - * @type {number} - * @private - */ - this.snapTolerance_ = options.snapTolerance ? options.snapTolerance : 12; + /** + * Pixel distance for snapping. + * @type {number} + * @private + */ + this.snapTolerance_ = options.snapTolerance ? options.snapTolerance : 12; - /** - * Geometry type. - * @type {module:ol/geom/GeometryType} - * @private - */ - this.type_ = /** @type {module:ol/geom/GeometryType} */ (options.type); + /** + * Geometry type. + * @type {module:ol/geom/GeometryType} + * @private + */ + this.type_ = /** @type {module:ol/geom/GeometryType} */ (options.type); - /** - * Drawing mode (derived from geometry type. - * @type {module:ol/interaction/Draw~Mode} - * @private - */ - this.mode_ = getMode(this.type_); + /** + * Drawing mode (derived from geometry type. + * @type {module:ol/interaction/Draw~Mode} + * @private + */ + this.mode_ = getMode(this.type_); - /** - * Stop click, singleclick, and doubleclick events from firing during drawing. - * Default is `false`. - * @type {boolean} - * @private - */ - this.stopClick_ = !!options.stopClick; + /** + * Stop click, singleclick, and doubleclick events from firing during drawing. + * Default is `false`. + * @type {boolean} + * @private + */ + this.stopClick_ = !!options.stopClick; - /** - * The number of points that must be drawn before a polygon ring or line - * string can be finished. The default is 3 for polygon rings and 2 for - * line strings. - * @type {number} - * @private - */ - this.minPoints_ = options.minPoints ? - options.minPoints : - (this.mode_ === Mode.POLYGON ? 3 : 2); + /** + * The number of points that must be drawn before a polygon ring or line + * string can be finished. The default is 3 for polygon rings and 2 for + * line strings. + * @type {number} + * @private + */ + this.minPoints_ = options.minPoints ? + options.minPoints : + (this.mode_ === Mode.POLYGON ? 3 : 2); - /** - * The number of points that can be drawn before a polygon ring or line string - * is finished. The default is no restriction. - * @type {number} - * @private - */ - this.maxPoints_ = options.maxPoints ? options.maxPoints : Infinity; + /** + * The number of points that can be drawn before a polygon ring or line string + * is finished. The default is no restriction. + * @type {number} + * @private + */ + this.maxPoints_ = options.maxPoints ? options.maxPoints : Infinity; - /** - * A function to decide if a potential finish coordinate is permissible - * @private - * @type {module:ol/events/condition~Condition} - */ - this.finishCondition_ = options.finishCondition ? options.finishCondition : TRUE; + /** + * A function to decide if a potential finish coordinate is permissible + * @private + * @type {module:ol/events/condition~Condition} + */ + this.finishCondition_ = options.finishCondition ? options.finishCondition : TRUE; - let geometryFunction = options.geometryFunction; - if (!geometryFunction) { - if (this.type_ === GeometryType.CIRCLE) { - /** - * @param {!Array.} coordinates - * The coordinates. - * @param {module:ol/geom/SimpleGeometry=} opt_geometry Optional geometry. - * @return {module:ol/geom/SimpleGeometry} A geometry. - */ - geometryFunction = function(coordinates, opt_geometry) { - const circle = opt_geometry ? /** @type {module:ol/geom/Circle} */ (opt_geometry) : - new Circle([NaN, NaN]); - const squaredLength = squaredCoordinateDistance( - coordinates[0], coordinates[1]); - circle.setCenterAndRadius(coordinates[0], Math.sqrt(squaredLength)); - return circle; - }; - } else { - let Constructor; - const mode = this.mode_; - if (mode === Mode.POINT) { - Constructor = Point; - } else if (mode === Mode.LINE_STRING) { - Constructor = LineString; - } else if (mode === Mode.POLYGON) { - Constructor = Polygon; - } - /** - * @param {!Array.} coordinates - * The coordinates. - * @param {module:ol/geom/SimpleGeometry=} opt_geometry Optional geometry. - * @return {module:ol/geom/SimpleGeometry} A geometry. - */ - geometryFunction = function(coordinates, opt_geometry) { - let geometry = opt_geometry; - if (geometry) { - if (mode === Mode.POLYGON) { - if (coordinates[0].length) { - // Add a closing coordinate to match the first - geometry.setCoordinates([coordinates[0].concat([coordinates[0][0]])]); + let geometryFunction = options.geometryFunction; + if (!geometryFunction) { + if (this.type_ === GeometryType.CIRCLE) { + /** + * @param {!Array.} coordinates + * The coordinates. + * @param {module:ol/geom/SimpleGeometry=} opt_geometry Optional geometry. + * @return {module:ol/geom/SimpleGeometry} A geometry. + */ + geometryFunction = function(coordinates, opt_geometry) { + const circle = opt_geometry ? /** @type {module:ol/geom/Circle} */ (opt_geometry) : + new Circle([NaN, NaN]); + const squaredLength = squaredCoordinateDistance( + coordinates[0], coordinates[1]); + circle.setCenterAndRadius(coordinates[0], Math.sqrt(squaredLength)); + return circle; + }; + } else { + let Constructor; + const mode = this.mode_; + if (mode === Mode.POINT) { + Constructor = Point; + } else if (mode === Mode.LINE_STRING) { + Constructor = LineString; + } else if (mode === Mode.POLYGON) { + Constructor = Polygon; + } + /** + * @param {!Array.} coordinates + * The coordinates. + * @param {module:ol/geom/SimpleGeometry=} opt_geometry Optional geometry. + * @return {module:ol/geom/SimpleGeometry} A geometry. + */ + geometryFunction = function(coordinates, opt_geometry) { + let geometry = opt_geometry; + if (geometry) { + if (mode === Mode.POLYGON) { + if (coordinates[0].length) { + // Add a closing coordinate to match the first + geometry.setCoordinates([coordinates[0].concat([coordinates[0][0]])]); + } else { + geometry.setCoordinates([]); + } } else { - geometry.setCoordinates([]); + geometry.setCoordinates(coordinates); } } else { - geometry.setCoordinates(coordinates); + geometry = new Constructor(coordinates); + } + return geometry; + }; + } + } + + /** + * @type {module:ol/interaction/Draw~GeometryFunction} + * @private + */ + this.geometryFunction_ = geometryFunction; + + /** + * @type {number} + * @private + */ + this.dragVertexDelay_ = options.dragVertexDelay !== undefined ? options.dragVertexDelay : 500; + + /** + * Finish coordinate for the feature (first point for polygons, last point for + * linestrings). + * @type {module:ol/coordinate~Coordinate} + * @private + */ + this.finishCoordinate_ = null; + + /** + * Sketch feature. + * @type {module:ol/Feature} + * @private + */ + this.sketchFeature_ = null; + + /** + * Sketch point. + * @type {module:ol/Feature} + * @private + */ + this.sketchPoint_ = null; + + /** + * Sketch coordinates. Used when drawing a line or polygon. + * @type {module:ol/coordinate~Coordinate|Array.|Array.>} + * @private + */ + this.sketchCoords_ = null; + + /** + * Sketch line. Used when drawing polygon. + * @type {module:ol/Feature} + * @private + */ + this.sketchLine_ = null; + + /** + * Sketch line coordinates. Used when drawing a polygon or circle. + * @type {Array.} + * @private + */ + this.sketchLineCoords_ = null; + + /** + * Squared tolerance for handling up events. If the squared distance + * between a down and up event is greater than this tolerance, up events + * will not be handled. + * @type {number} + * @private + */ + this.squaredClickTolerance_ = options.clickTolerance ? + options.clickTolerance * options.clickTolerance : 36; + + /** + * Draw overlay where our sketch features are drawn. + * @type {module:ol/layer/Vector} + * @private + */ + this.overlay_ = new VectorLayer({ + source: new VectorSource({ + useSpatialIndex: false, + wrapX: options.wrapX ? options.wrapX : false + }), + style: options.style ? options.style : + getDefaultStyleFunction(), + updateWhileInteracting: true + }); + + /** + * Name of the geometry attribute for newly created features. + * @type {string|undefined} + * @private + */ + this.geometryName_ = options.geometryName; + + /** + * @private + * @type {module:ol/events/condition~Condition} + */ + this.condition_ = options.condition ? options.condition : noModifierKeys; + + /** + * @private + * @type {module:ol/events/condition~Condition} + */ + this.freehandCondition_; + if (options.freehand) { + this.freehandCondition_ = always; + } else { + this.freehandCondition_ = options.freehandCondition ? + options.freehandCondition : shiftKeyOnly; + } + + listen(this, + getChangeEventType(InteractionProperty.ACTIVE), + this.updateState_, this); + + } + + /** + * @inheritDoc + */ + setMap(map) { + PointerInteraction.prototype.setMap.call(this, map); + this.updateState_(); + } + + /** + * Handle move events. + * @param {module:ol/MapBrowserEvent} event A move event. + * @return {boolean} Pass the event to other interactions. + * @private + */ + handlePointerMove_(event) { + if (this.downPx_ && + ((!this.freehand_ && this.shouldHandle_) || + (this.freehand_ && !this.shouldHandle_))) { + const downPx = this.downPx_; + const clickPx = event.pixel; + const dx = downPx[0] - clickPx[0]; + const dy = downPx[1] - clickPx[1]; + const squaredDistance = dx * dx + dy * dy; + this.shouldHandle_ = this.freehand_ ? + squaredDistance > this.squaredClickTolerance_ : + squaredDistance <= this.squaredClickTolerance_; + if (!this.shouldHandle_) { + return true; + } + } + + if (this.finishCoordinate_) { + this.modifyDrawing_(event); + } else { + this.createOrUpdateSketchPoint_(event); + } + return true; + } + + /** + * Determine if an event is within the snapping tolerance of the start coord. + * @param {module:ol/MapBrowserEvent} event Event. + * @return {boolean} The event is within the snapping tolerance of the start. + * @private + */ + atFinish_(event) { + let at = false; + if (this.sketchFeature_) { + let potentiallyDone = false; + let potentiallyFinishCoordinates = [this.finishCoordinate_]; + 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]]; + } + if (potentiallyDone) { + const map = event.map; + for (let i = 0, ii = potentiallyFinishCoordinates.length; i < ii; i++) { + const finishCoordinate = potentiallyFinishCoordinates[i]; + const finishPixel = map.getPixelFromCoordinate(finishCoordinate); + const pixel = event.pixel; + const dx = pixel[0] - finishPixel[0]; + const dy = pixel[1] - finishPixel[1]; + const snapTolerance = this.freehand_ ? 1 : this.snapTolerance_; + at = Math.sqrt(dx * dx + dy * dy) <= snapTolerance; + if (at) { + this.finishCoordinate_ = finishCoordinate; + break; } - } else { - geometry = new Constructor(coordinates); } - return geometry; - }; + } + } + return at; + } + + /** + * @param {module:ol/MapBrowserEvent} event Event. + * @private + */ + createOrUpdateSketchPoint_(event) { + const coordinates = event.coordinate.slice(); + if (!this.sketchPoint_) { + this.sketchPoint_ = new Feature(new Point(coordinates)); + this.updateSketchFeatures_(); + } else { + const sketchPointGeom = /** @type {module:ol/geom/Point} */ (this.sketchPoint_.getGeometry()); + sketchPointGeom.setCoordinates(coordinates); } } /** - * @type {module:ol/interaction/Draw~GeometryFunction} + * Start the drawing. + * @param {module:ol/MapBrowserEvent} event Event. * @private */ - this.geometryFunction_ = geometryFunction; - - /** - * @type {number} - * @private - */ - this.dragVertexDelay_ = options.dragVertexDelay !== undefined ? options.dragVertexDelay : 500; - - /** - * Finish coordinate for the feature (first point for polygons, last point for - * linestrings). - * @type {module:ol/coordinate~Coordinate} - * @private - */ - this.finishCoordinate_ = null; - - /** - * Sketch feature. - * @type {module:ol/Feature} - * @private - */ - this.sketchFeature_ = null; - - /** - * Sketch point. - * @type {module:ol/Feature} - * @private - */ - this.sketchPoint_ = null; - - /** - * Sketch coordinates. Used when drawing a line or polygon. - * @type {module:ol/coordinate~Coordinate|Array.|Array.>} - * @private - */ - this.sketchCoords_ = null; - - /** - * Sketch line. Used when drawing polygon. - * @type {module:ol/Feature} - * @private - */ - this.sketchLine_ = null; - - /** - * Sketch line coordinates. Used when drawing a polygon or circle. - * @type {Array.} - * @private - */ - this.sketchLineCoords_ = null; - - /** - * Squared tolerance for handling up events. If the squared distance - * between a down and up event is greater than this tolerance, up events - * will not be handled. - * @type {number} - * @private - */ - this.squaredClickTolerance_ = options.clickTolerance ? - options.clickTolerance * options.clickTolerance : 36; - - /** - * Draw overlay where our sketch features are drawn. - * @type {module:ol/layer/Vector} - * @private - */ - this.overlay_ = new VectorLayer({ - source: new VectorSource({ - useSpatialIndex: false, - wrapX: options.wrapX ? options.wrapX : false - }), - style: options.style ? options.style : - getDefaultStyleFunction(), - updateWhileInteracting: true - }); - - /** - * Name of the geometry attribute for newly created features. - * @type {string|undefined} - * @private - */ - this.geometryName_ = options.geometryName; - - /** - * @private - * @type {module:ol/events/condition~Condition} - */ - this.condition_ = options.condition ? options.condition : noModifierKeys; - - /** - * @private - * @type {module:ol/events/condition~Condition} - */ - this.freehandCondition_; - if (options.freehand) { - this.freehandCondition_ = always; - } else { - this.freehandCondition_ = options.freehandCondition ? - options.freehandCondition : shiftKeyOnly; + startDrawing_(event) { + const start = event.coordinate; + this.finishCoordinate_ = start; + if (this.mode_ === Mode.POINT) { + this.sketchCoords_ = start.slice(); + } else if (this.mode_ === Mode.POLYGON) { + this.sketchCoords_ = [[start.slice(), start.slice()]]; + this.sketchLineCoords_ = this.sketchCoords_[0]; + } else { + this.sketchCoords_ = [start.slice(), start.slice()]; + } + if (this.sketchLineCoords_) { + this.sketchLine_ = new Feature( + new LineString(this.sketchLineCoords_)); + } + const geometry = this.geometryFunction_(this.sketchCoords_); + this.sketchFeature_ = new Feature(); + if (this.geometryName_) { + this.sketchFeature_.setGeometryName(this.geometryName_); + } + this.sketchFeature_.setGeometry(geometry); + this.updateSketchFeatures_(); + this.dispatchEvent(new DrawEvent(DrawEventType.DRAWSTART, this.sketchFeature_)); } - listen(this, - getChangeEventType(InteractionProperty.ACTIVE), - this.updateState_, this); + /** + * Modify the drawing. + * @param {module:ol/MapBrowserEvent} event Event. + * @private + */ + modifyDrawing_(event) { + let coordinate = event.coordinate; + const geometry = /** @type {module:ol/geom/SimpleGeometry} */ (this.sketchFeature_.getGeometry()); + let coordinates, last; + if (this.mode_ === Mode.POINT) { + last = this.sketchCoords_; + } else if (this.mode_ === Mode.POLYGON) { + coordinates = this.sketchCoords_[0]; + last = coordinates[coordinates.length - 1]; + if (this.atFinish_(event)) { + // snap to finish + coordinate = this.finishCoordinate_.slice(); + } + } else { + coordinates = this.sketchCoords_; + last = coordinates[coordinates.length - 1]; + } + last[0] = coordinate[0]; + last[1] = coordinate[1]; + this.geometryFunction_(/** @type {!Array.} */ (this.sketchCoords_), geometry); + if (this.sketchPoint_) { + const sketchPointGeom = /** @type {module:ol/geom/Point} */ (this.sketchPoint_.getGeometry()); + sketchPointGeom.setCoordinates(coordinate); + } + let sketchLineGeom; + if (geometry instanceof Polygon && + this.mode_ !== Mode.POLYGON) { + if (!this.sketchLine_) { + this.sketchLine_ = new Feature(); + } + const ring = geometry.getLinearRing(0); + sketchLineGeom = /** @type {module:ol/geom/LineString} */ (this.sketchLine_.getGeometry()); + if (!sketchLineGeom) { + sketchLineGeom = new LineString(ring.getFlatCoordinates(), ring.getLayout()); + this.sketchLine_.setGeometry(sketchLineGeom); + } else { + sketchLineGeom.setFlatCoordinates( + ring.getLayout(), ring.getFlatCoordinates()); + sketchLineGeom.changed(); + } + } else if (this.sketchLineCoords_) { + sketchLineGeom = /** @type {module:ol/geom/LineString} */ (this.sketchLine_.getGeometry()); + sketchLineGeom.setCoordinates(this.sketchLineCoords_); + } + this.updateSketchFeatures_(); + } -}; + /** + * Add a new coordinate to the drawing. + * @param {module:ol/MapBrowserEvent} event Event. + * @private + */ + addToDrawing_(event) { + const coordinate = event.coordinate; + const geometry = /** @type {module:ol/geom/SimpleGeometry} */ (this.sketchFeature_.getGeometry()); + let done; + let coordinates; + if (this.mode_ === Mode.LINE_STRING) { + this.finishCoordinate_ = coordinate.slice(); + coordinates = this.sketchCoords_; + if (coordinates.length >= this.maxPoints_) { + if (this.freehand_) { + coordinates.pop(); + } else { + done = true; + } + } + coordinates.push(coordinate.slice()); + this.geometryFunction_(coordinates, geometry); + } else if (this.mode_ === Mode.POLYGON) { + coordinates = this.sketchCoords_[0]; + if (coordinates.length >= this.maxPoints_) { + if (this.freehand_) { + coordinates.pop(); + } else { + done = true; + } + } + coordinates.push(coordinate.slice()); + if (done) { + this.finishCoordinate_ = coordinates[0]; + } + this.geometryFunction_(this.sketchCoords_, geometry); + } + this.updateSketchFeatures_(); + if (done) { + this.finishDrawing(); + } + } + + /** + * Remove last point of the feature currently being drawn. + * @api + */ + removeLastPoint() { + if (!this.sketchFeature_) { + return; + } + const geometry = /** @type {module:ol/geom/SimpleGeometry} */ (this.sketchFeature_.getGeometry()); + let coordinates, sketchLineGeom; + if (this.mode_ === Mode.LINE_STRING) { + coordinates = 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.splice(-2, 1); + sketchLineGeom = /** @type {module:ol/geom/LineString} */ (this.sketchLine_.getGeometry()); + sketchLineGeom.setCoordinates(coordinates); + this.geometryFunction_(this.sketchCoords_, geometry); + } + + if (coordinates.length === 0) { + this.finishCoordinate_ = null; + } + + this.updateSketchFeatures_(); + } + + /** + * Stop drawing and add the sketch feature to the target layer. + * The {@link module:ol/interaction/Draw~DrawEventType.DRAWEND} event is + * dispatched before inserting the feature. + * @api + */ + finishDrawing() { + const sketchFeature = this.abortDrawing_(); + if (!sketchFeature) { + return; + } + let coordinates = this.sketchCoords_; + const geometry = /** @type {module:ol/geom/SimpleGeometry} */ (sketchFeature.getGeometry()); + if (this.mode_ === Mode.LINE_STRING) { + // remove the redundant last point + coordinates.pop(); + this.geometryFunction_(coordinates, geometry); + } else if (this.mode_ === Mode.POLYGON) { + // remove the redundant last point in ring + 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])); + } else if (this.type_ === GeometryType.MULTI_LINE_STRING) { + sketchFeature.setGeometry(new MultiLineString([coordinates])); + } else if (this.type_ === GeometryType.MULTI_POLYGON) { + sketchFeature.setGeometry(new MultiPolygon([coordinates])); + } + + // First dispatch event to allow full set up of feature + this.dispatchEvent(new DrawEvent(DrawEventType.DRAWEND, sketchFeature)); + + // Then insert feature + if (this.features_) { + this.features_.push(sketchFeature); + } + if (this.source_) { + this.source_.addFeature(sketchFeature); + } + } + + /** + * Stop drawing without adding the sketch feature to the target layer. + * @return {module:ol/Feature} The sketch feature (or null if none). + * @private + */ + abortDrawing_() { + this.finishCoordinate_ = null; + const sketchFeature = this.sketchFeature_; + if (sketchFeature) { + this.sketchFeature_ = null; + this.sketchPoint_ = null; + this.sketchLine_ = null; + this.overlay_.getSource().clear(true); + } + return sketchFeature; + } + + /** + * Extend an existing geometry by adding additional points. This only works + * on features with `LineString` geometries, where the interaction will + * extend lines by adding points to the end of the coordinates array. + * @param {!module:ol/Feature} feature Feature to be extended. + * @api + */ + extend(feature) { + const geometry = feature.getGeometry(); + const lineString = /** @type {module:ol/geom/LineString} */ (geometry); + this.sketchFeature_ = feature; + this.sketchCoords_ = lineString.getCoordinates(); + const last = this.sketchCoords_[this.sketchCoords_.length - 1]; + this.finishCoordinate_ = last.slice(); + this.sketchCoords_.push(last.slice()); + this.updateSketchFeatures_(); + this.dispatchEvent(new DrawEvent(DrawEventType.DRAWSTART, this.sketchFeature_)); + } + + /** + * Redraw the sketch features. + * @private + */ + updateSketchFeatures_() { + const sketchFeatures = []; + if (this.sketchFeature_) { + sketchFeatures.push(this.sketchFeature_); + } + if (this.sketchLine_) { + sketchFeatures.push(this.sketchLine_); + } + if (this.sketchPoint_) { + sketchFeatures.push(this.sketchPoint_); + } + const overlaySource = this.overlay_.getSource(); + overlaySource.clear(true); + overlaySource.addFeatures(sketchFeatures); + } + + /** + * @private + */ + updateState_() { + const map = this.getMap(); + const active = this.getActive(); + if (!map || !active) { + this.abortDrawing_(); + } + this.overlay_.setMap(active ? map : null); + } +} inherits(Draw, PointerInteraction); @@ -446,15 +811,6 @@ function getDefaultStyleFunction() { } -/** - * @inheritDoc - */ -Draw.prototype.setMap = function(map) { - PointerInteraction.prototype.setMap.call(this, map); - this.updateState_(); -}; - - /** * Handles the {@link module:ol/MapBrowserEvent map browser event} and may actually * draw or finish the drawing. @@ -581,379 +937,12 @@ function handleUpEvent(event) { } -/** - * Handle move events. - * @param {module:ol/MapBrowserEvent} event A move event. - * @return {boolean} Pass the event to other interactions. - * @private - */ -Draw.prototype.handlePointerMove_ = function(event) { - if (this.downPx_ && - ((!this.freehand_ && this.shouldHandle_) || - (this.freehand_ && !this.shouldHandle_))) { - const downPx = this.downPx_; - const clickPx = event.pixel; - const dx = downPx[0] - clickPx[0]; - const dy = downPx[1] - clickPx[1]; - const squaredDistance = dx * dx + dy * dy; - this.shouldHandle_ = this.freehand_ ? - squaredDistance > this.squaredClickTolerance_ : - squaredDistance <= this.squaredClickTolerance_; - if (!this.shouldHandle_) { - return true; - } - } - - if (this.finishCoordinate_) { - this.modifyDrawing_(event); - } else { - this.createOrUpdateSketchPoint_(event); - } - return true; -}; - - -/** - * Determine if an event is within the snapping tolerance of the start coord. - * @param {module:ol/MapBrowserEvent} event Event. - * @return {boolean} The event is within the snapping tolerance of the start. - * @private - */ -Draw.prototype.atFinish_ = function(event) { - let at = false; - if (this.sketchFeature_) { - let potentiallyDone = false; - let potentiallyFinishCoordinates = [this.finishCoordinate_]; - 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]]; - } - if (potentiallyDone) { - const map = event.map; - for (let i = 0, ii = potentiallyFinishCoordinates.length; i < ii; i++) { - const finishCoordinate = potentiallyFinishCoordinates[i]; - const finishPixel = map.getPixelFromCoordinate(finishCoordinate); - const pixel = event.pixel; - const dx = pixel[0] - finishPixel[0]; - const dy = pixel[1] - finishPixel[1]; - const snapTolerance = this.freehand_ ? 1 : this.snapTolerance_; - at = Math.sqrt(dx * dx + dy * dy) <= snapTolerance; - if (at) { - this.finishCoordinate_ = finishCoordinate; - break; - } - } - } - } - return at; -}; - - -/** - * @param {module:ol/MapBrowserEvent} event Event. - * @private - */ -Draw.prototype.createOrUpdateSketchPoint_ = function(event) { - const coordinates = event.coordinate.slice(); - if (!this.sketchPoint_) { - this.sketchPoint_ = new Feature(new Point(coordinates)); - this.updateSketchFeatures_(); - } else { - const sketchPointGeom = /** @type {module:ol/geom/Point} */ (this.sketchPoint_.getGeometry()); - sketchPointGeom.setCoordinates(coordinates); - } -}; - - -/** - * Start the drawing. - * @param {module:ol/MapBrowserEvent} event Event. - * @private - */ -Draw.prototype.startDrawing_ = function(event) { - const start = event.coordinate; - this.finishCoordinate_ = start; - if (this.mode_ === Mode.POINT) { - this.sketchCoords_ = start.slice(); - } else if (this.mode_ === Mode.POLYGON) { - this.sketchCoords_ = [[start.slice(), start.slice()]]; - this.sketchLineCoords_ = this.sketchCoords_[0]; - } else { - this.sketchCoords_ = [start.slice(), start.slice()]; - } - if (this.sketchLineCoords_) { - this.sketchLine_ = new Feature( - new LineString(this.sketchLineCoords_)); - } - const geometry = this.geometryFunction_(this.sketchCoords_); - this.sketchFeature_ = new Feature(); - if (this.geometryName_) { - this.sketchFeature_.setGeometryName(this.geometryName_); - } - this.sketchFeature_.setGeometry(geometry); - this.updateSketchFeatures_(); - this.dispatchEvent(new DrawEvent(DrawEventType.DRAWSTART, this.sketchFeature_)); -}; - - -/** - * Modify the drawing. - * @param {module:ol/MapBrowserEvent} event Event. - * @private - */ -Draw.prototype.modifyDrawing_ = function(event) { - let coordinate = event.coordinate; - const geometry = /** @type {module:ol/geom/SimpleGeometry} */ (this.sketchFeature_.getGeometry()); - let coordinates, last; - if (this.mode_ === Mode.POINT) { - last = this.sketchCoords_; - } else if (this.mode_ === Mode.POLYGON) { - coordinates = this.sketchCoords_[0]; - last = coordinates[coordinates.length - 1]; - if (this.atFinish_(event)) { - // snap to finish - coordinate = this.finishCoordinate_.slice(); - } - } else { - coordinates = this.sketchCoords_; - last = coordinates[coordinates.length - 1]; - } - last[0] = coordinate[0]; - last[1] = coordinate[1]; - this.geometryFunction_(/** @type {!Array.} */ (this.sketchCoords_), geometry); - if (this.sketchPoint_) { - const sketchPointGeom = /** @type {module:ol/geom/Point} */ (this.sketchPoint_.getGeometry()); - sketchPointGeom.setCoordinates(coordinate); - } - let sketchLineGeom; - if (geometry instanceof Polygon && - this.mode_ !== Mode.POLYGON) { - if (!this.sketchLine_) { - this.sketchLine_ = new Feature(); - } - const ring = geometry.getLinearRing(0); - sketchLineGeom = /** @type {module:ol/geom/LineString} */ (this.sketchLine_.getGeometry()); - if (!sketchLineGeom) { - sketchLineGeom = new LineString(ring.getFlatCoordinates(), ring.getLayout()); - this.sketchLine_.setGeometry(sketchLineGeom); - } else { - sketchLineGeom.setFlatCoordinates( - ring.getLayout(), ring.getFlatCoordinates()); - sketchLineGeom.changed(); - } - } else if (this.sketchLineCoords_) { - sketchLineGeom = /** @type {module:ol/geom/LineString} */ (this.sketchLine_.getGeometry()); - sketchLineGeom.setCoordinates(this.sketchLineCoords_); - } - this.updateSketchFeatures_(); -}; - - -/** - * Add a new coordinate to the drawing. - * @param {module:ol/MapBrowserEvent} event Event. - * @private - */ -Draw.prototype.addToDrawing_ = function(event) { - const coordinate = event.coordinate; - const geometry = /** @type {module:ol/geom/SimpleGeometry} */ (this.sketchFeature_.getGeometry()); - let done; - let coordinates; - if (this.mode_ === Mode.LINE_STRING) { - this.finishCoordinate_ = coordinate.slice(); - coordinates = this.sketchCoords_; - if (coordinates.length >= this.maxPoints_) { - if (this.freehand_) { - coordinates.pop(); - } else { - done = true; - } - } - coordinates.push(coordinate.slice()); - this.geometryFunction_(coordinates, geometry); - } else if (this.mode_ === Mode.POLYGON) { - coordinates = this.sketchCoords_[0]; - if (coordinates.length >= this.maxPoints_) { - if (this.freehand_) { - coordinates.pop(); - } else { - done = true; - } - } - coordinates.push(coordinate.slice()); - if (done) { - this.finishCoordinate_ = coordinates[0]; - } - this.geometryFunction_(this.sketchCoords_, geometry); - } - this.updateSketchFeatures_(); - if (done) { - this.finishDrawing(); - } -}; - - -/** - * Remove last point of the feature currently being drawn. - * @api - */ -Draw.prototype.removeLastPoint = function() { - if (!this.sketchFeature_) { - return; - } - const geometry = /** @type {module:ol/geom/SimpleGeometry} */ (this.sketchFeature_.getGeometry()); - let coordinates, sketchLineGeom; - if (this.mode_ === Mode.LINE_STRING) { - coordinates = 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.splice(-2, 1); - sketchLineGeom = /** @type {module:ol/geom/LineString} */ (this.sketchLine_.getGeometry()); - sketchLineGeom.setCoordinates(coordinates); - this.geometryFunction_(this.sketchCoords_, geometry); - } - - if (coordinates.length === 0) { - this.finishCoordinate_ = null; - } - - this.updateSketchFeatures_(); -}; - - -/** - * Stop drawing and add the sketch feature to the target layer. - * The {@link module:ol/interaction/Draw~DrawEventType.DRAWEND} event is - * dispatched before inserting the feature. - * @api - */ -Draw.prototype.finishDrawing = function() { - const sketchFeature = this.abortDrawing_(); - if (!sketchFeature) { - return; - } - let coordinates = this.sketchCoords_; - const geometry = /** @type {module:ol/geom/SimpleGeometry} */ (sketchFeature.getGeometry()); - if (this.mode_ === Mode.LINE_STRING) { - // remove the redundant last point - coordinates.pop(); - this.geometryFunction_(coordinates, geometry); - } else if (this.mode_ === Mode.POLYGON) { - // remove the redundant last point in ring - 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])); - } else if (this.type_ === GeometryType.MULTI_LINE_STRING) { - sketchFeature.setGeometry(new MultiLineString([coordinates])); - } else if (this.type_ === GeometryType.MULTI_POLYGON) { - sketchFeature.setGeometry(new MultiPolygon([coordinates])); - } - - // First dispatch event to allow full set up of feature - this.dispatchEvent(new DrawEvent(DrawEventType.DRAWEND, sketchFeature)); - - // Then insert feature - if (this.features_) { - this.features_.push(sketchFeature); - } - if (this.source_) { - this.source_.addFeature(sketchFeature); - } -}; - - -/** - * Stop drawing without adding the sketch feature to the target layer. - * @return {module:ol/Feature} The sketch feature (or null if none). - * @private - */ -Draw.prototype.abortDrawing_ = function() { - this.finishCoordinate_ = null; - const sketchFeature = this.sketchFeature_; - if (sketchFeature) { - this.sketchFeature_ = null; - this.sketchPoint_ = null; - this.sketchLine_ = null; - this.overlay_.getSource().clear(true); - } - return sketchFeature; -}; - - -/** - * Extend an existing geometry by adding additional points. This only works - * on features with `LineString` geometries, where the interaction will - * extend lines by adding points to the end of the coordinates array. - * @param {!module:ol/Feature} feature Feature to be extended. - * @api - */ -Draw.prototype.extend = function(feature) { - const geometry = feature.getGeometry(); - const lineString = /** @type {module:ol/geom/LineString} */ (geometry); - this.sketchFeature_ = feature; - this.sketchCoords_ = lineString.getCoordinates(); - const last = this.sketchCoords_[this.sketchCoords_.length - 1]; - this.finishCoordinate_ = last.slice(); - this.sketchCoords_.push(last.slice()); - this.updateSketchFeatures_(); - this.dispatchEvent(new DrawEvent(DrawEventType.DRAWSTART, this.sketchFeature_)); -}; - - /** * @inheritDoc */ Draw.prototype.shouldStopEvent = FALSE; -/** - * Redraw the sketch features. - * @private - */ -Draw.prototype.updateSketchFeatures_ = function() { - const sketchFeatures = []; - if (this.sketchFeature_) { - sketchFeatures.push(this.sketchFeature_); - } - if (this.sketchLine_) { - sketchFeatures.push(this.sketchLine_); - } - if (this.sketchPoint_) { - sketchFeatures.push(this.sketchPoint_); - } - const overlaySource = this.overlay_.getSource(); - overlaySource.clear(true); - overlaySource.addFeatures(sketchFeatures); -}; - - -/** - * @private - */ -Draw.prototype.updateState_ = function() { - const map = this.getMap(); - const active = this.getActive(); - if (!map || !active) { - this.abortDrawing_(); - } - this.overlay_.setMap(active ? map : null); -}; - - /** * 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 diff --git a/src/ol/interaction/Extent.js b/src/ol/interaction/Extent.js index 3520decb0e..a3b7fcaf38 100644 --- a/src/ol/interaction/Extent.js +++ b/src/ol/interaction/Extent.js @@ -82,98 +82,233 @@ inherits(ExtentInteractionEvent, Event); * @param {module:ol/interaction/Extent~Options=} opt_options Options. * @api */ -const ExtentInteraction = function(opt_options) { +class ExtentInteraction { + constructor(opt_options) { - const options = opt_options || {}; + const options = opt_options || {}; - /** - * Extent of the drawn box - * @type {module:ol/extent~Extent} - * @private - */ - this.extent_ = null; + /** + * Extent of the drawn box + * @type {module:ol/extent~Extent} + * @private + */ + this.extent_ = null; - /** - * Handler for pointer move events - * @type {function (module:ol/coordinate~Coordinate): module:ol/extent~Extent|null} - * @private - */ - this.pointerHandler_ = null; + /** + * Handler for pointer move events + * @type {function (module:ol/coordinate~Coordinate): module:ol/extent~Extent|null} + * @private + */ + this.pointerHandler_ = null; - /** - * Pixel threshold to snap to extent - * @type {number} - * @private - */ - this.pixelTolerance_ = options.pixelTolerance !== undefined ? - options.pixelTolerance : 10; + /** + * Pixel threshold to snap to extent + * @type {number} + * @private + */ + this.pixelTolerance_ = options.pixelTolerance !== undefined ? + options.pixelTolerance : 10; - /** - * Is the pointer snapped to an extent vertex - * @type {boolean} - * @private - */ - this.snappedToVertex_ = false; + /** + * Is the pointer snapped to an extent vertex + * @type {boolean} + * @private + */ + this.snappedToVertex_ = false; - /** - * Feature for displaying the visible extent - * @type {module:ol/Feature} - * @private - */ - this.extentFeature_ = null; + /** + * Feature for displaying the visible extent + * @type {module:ol/Feature} + * @private + */ + this.extentFeature_ = null; - /** - * Feature for displaying the visible pointer - * @type {module:ol/Feature} - * @private - */ - this.vertexFeature_ = null; + /** + * Feature for displaying the visible pointer + * @type {module:ol/Feature} + * @private + */ + this.vertexFeature_ = null; - if (!opt_options) { - opt_options = {}; + if (!opt_options) { + opt_options = {}; + } + + PointerInteraction.call(this, { + handleDownEvent: handleDownEvent, + handleDragEvent: handleDragEvent, + handleEvent: handleEvent, + handleUpEvent: handleUpEvent + }); + + /** + * Layer for the extentFeature + * @type {module:ol/layer/Vector} + * @private + */ + this.extentOverlay_ = new VectorLayer({ + source: new VectorSource({ + useSpatialIndex: false, + wrapX: !!opt_options.wrapX + }), + style: opt_options.boxStyle ? opt_options.boxStyle : getDefaultExtentStyleFunction(), + updateWhileAnimating: true, + updateWhileInteracting: true + }); + + /** + * Layer for the vertexFeature + * @type {module:ol/layer/Vector} + * @private + */ + this.vertexOverlay_ = new VectorLayer({ + source: new VectorSource({ + useSpatialIndex: false, + wrapX: !!opt_options.wrapX + }), + style: opt_options.pointerStyle ? opt_options.pointerStyle : getDefaultPointerStyleFunction(), + updateWhileAnimating: true, + updateWhileInteracting: true + }); + + if (opt_options.extent) { + this.setExtent(opt_options.extent); + } } - PointerInteraction.call(this, { - handleDownEvent: handleDownEvent, - handleDragEvent: handleDragEvent, - handleEvent: handleEvent, - handleUpEvent: handleUpEvent - }); - /** - * Layer for the extentFeature - * @type {module:ol/layer/Vector} + * @param {module:ol~Pixel} pixel cursor location + * @param {module:ol/PluggableMap} map map + * @returns {module:ol/coordinate~Coordinate|null} snapped vertex on extent * @private */ - this.extentOverlay_ = new VectorLayer({ - source: new VectorSource({ - useSpatialIndex: false, - wrapX: !!opt_options.wrapX - }), - style: opt_options.boxStyle ? opt_options.boxStyle : getDefaultExtentStyleFunction(), - updateWhileAnimating: true, - updateWhileInteracting: true - }); + snapToVertex_(pixel, map) { + const pixelCoordinate = map.getCoordinateFromPixel(pixel); + const sortByDistance = function(a, b) { + return squaredDistanceToSegment(pixelCoordinate, a) - + squaredDistanceToSegment(pixelCoordinate, b); + }; + const extent = this.getExtent(); + if (extent) { + //convert extents to line segments and find the segment closest to pixelCoordinate + const segments = getSegments(extent); + segments.sort(sortByDistance); + const closestSegment = segments[0]; - /** - * Layer for the vertexFeature - * @type {module:ol/layer/Vector} - * @private - */ - this.vertexOverlay_ = new VectorLayer({ - source: new VectorSource({ - useSpatialIndex: false, - wrapX: !!opt_options.wrapX - }), - style: opt_options.pointerStyle ? opt_options.pointerStyle : getDefaultPointerStyleFunction(), - updateWhileAnimating: true, - updateWhileInteracting: true - }); + let vertex = (closestOnSegment(pixelCoordinate, + closestSegment)); + const vertexPixel = map.getPixelFromCoordinate(vertex); - if (opt_options.extent) { - this.setExtent(opt_options.extent); + //if the distance is within tolerance, snap to the segment + if (coordinateDistance(pixel, vertexPixel) <= this.pixelTolerance_) { + //test if we should further snap to a vertex + const pixel1 = map.getPixelFromCoordinate(closestSegment[0]); + const pixel2 = map.getPixelFromCoordinate(closestSegment[1]); + const squaredDist1 = squaredCoordinateDistance(vertexPixel, pixel1); + const squaredDist2 = squaredCoordinateDistance(vertexPixel, pixel2); + const dist = Math.sqrt(Math.min(squaredDist1, squaredDist2)); + this.snappedToVertex_ = dist <= this.pixelTolerance_; + if (this.snappedToVertex_) { + vertex = squaredDist1 > squaredDist2 ? + closestSegment[1] : closestSegment[0]; + } + return vertex; + } + } + return null; } -}; + + /** + * @param {module:ol/MapBrowserEvent} mapBrowserEvent pointer move event + * @private + */ + handlePointerMove_(mapBrowserEvent) { + const pixel = mapBrowserEvent.pixel; + const map = mapBrowserEvent.map; + + let vertex = this.snapToVertex_(pixel, map); + if (!vertex) { + vertex = map.getCoordinateFromPixel(pixel); + } + this.createOrUpdatePointerFeature_(vertex); + } + + /** + * @param {module:ol/extent~Extent} extent extent + * @returns {module:ol/Feature} extent as featrue + * @private + */ + createOrUpdateExtentFeature_(extent) { + let extentFeature = this.extentFeature_; + + if (!extentFeature) { + if (!extent) { + extentFeature = new Feature({}); + } else { + extentFeature = new Feature(polygonFromExtent(extent)); + } + this.extentFeature_ = extentFeature; + this.extentOverlay_.getSource().addFeature(extentFeature); + } else { + if (!extent) { + extentFeature.setGeometry(undefined); + } else { + extentFeature.setGeometry(polygonFromExtent(extent)); + } + } + return extentFeature; + } + + /** + * @param {module:ol/coordinate~Coordinate} vertex location of feature + * @returns {module:ol/Feature} vertex as feature + * @private + */ + createOrUpdatePointerFeature_(vertex) { + let vertexFeature = this.vertexFeature_; + if (!vertexFeature) { + vertexFeature = new Feature(new Point(vertex)); + this.vertexFeature_ = vertexFeature; + this.vertexOverlay_.getSource().addFeature(vertexFeature); + } else { + const geometry = /** @type {module:ol/geom/Point} */ (vertexFeature.getGeometry()); + geometry.setCoordinates(vertex); + } + return vertexFeature; + } + + /** + * @inheritDoc + */ + setMap(map) { + this.extentOverlay_.setMap(map); + this.vertexOverlay_.setMap(map); + PointerInteraction.prototype.setMap.call(this, map); + } + + /** + * Returns the current drawn extent in the view projection + * + * @return {module:ol/extent~Extent} Drawn extent in the view projection. + * @api + */ + getExtent() { + return this.extent_; + } + + /** + * Manually sets the drawn extent, using the view projection. + * + * @param {module:ol/extent~Extent} extent Extent + * @api + */ + setExtent(extent) { + //Null extent means no bbox + this.extent_ = extent ? extent : null; + this.createOrUpdateExtentFeature_(extent); + this.dispatchEvent(new ExtentInteractionEvent(this.extent_)); + } +} inherits(ExtentInteraction, PointerInteraction); @@ -350,140 +485,5 @@ function getSegments(extent) { ]; } -/** - * @param {module:ol~Pixel} pixel cursor location - * @param {module:ol/PluggableMap} map map - * @returns {module:ol/coordinate~Coordinate|null} snapped vertex on extent - * @private - */ -ExtentInteraction.prototype.snapToVertex_ = function(pixel, map) { - const pixelCoordinate = map.getCoordinateFromPixel(pixel); - const sortByDistance = function(a, b) { - return squaredDistanceToSegment(pixelCoordinate, a) - - squaredDistanceToSegment(pixelCoordinate, b); - }; - const extent = this.getExtent(); - if (extent) { - //convert extents to line segments and find the segment closest to pixelCoordinate - const segments = getSegments(extent); - segments.sort(sortByDistance); - const closestSegment = segments[0]; - - let vertex = (closestOnSegment(pixelCoordinate, - closestSegment)); - const vertexPixel = map.getPixelFromCoordinate(vertex); - - //if the distance is within tolerance, snap to the segment - if (coordinateDistance(pixel, vertexPixel) <= this.pixelTolerance_) { - //test if we should further snap to a vertex - const pixel1 = map.getPixelFromCoordinate(closestSegment[0]); - const pixel2 = map.getPixelFromCoordinate(closestSegment[1]); - const squaredDist1 = squaredCoordinateDistance(vertexPixel, pixel1); - const squaredDist2 = squaredCoordinateDistance(vertexPixel, pixel2); - const dist = Math.sqrt(Math.min(squaredDist1, squaredDist2)); - this.snappedToVertex_ = dist <= this.pixelTolerance_; - if (this.snappedToVertex_) { - vertex = squaredDist1 > squaredDist2 ? - closestSegment[1] : closestSegment[0]; - } - return vertex; - } - } - return null; -}; - -/** - * @param {module:ol/MapBrowserEvent} mapBrowserEvent pointer move event - * @private - */ -ExtentInteraction.prototype.handlePointerMove_ = function(mapBrowserEvent) { - const pixel = mapBrowserEvent.pixel; - const map = mapBrowserEvent.map; - - let vertex = this.snapToVertex_(pixel, map); - if (!vertex) { - vertex = map.getCoordinateFromPixel(pixel); - } - this.createOrUpdatePointerFeature_(vertex); -}; - -/** - * @param {module:ol/extent~Extent} extent extent - * @returns {module:ol/Feature} extent as featrue - * @private - */ -ExtentInteraction.prototype.createOrUpdateExtentFeature_ = function(extent) { - let extentFeature = this.extentFeature_; - - if (!extentFeature) { - if (!extent) { - extentFeature = new Feature({}); - } else { - extentFeature = new Feature(polygonFromExtent(extent)); - } - this.extentFeature_ = extentFeature; - this.extentOverlay_.getSource().addFeature(extentFeature); - } else { - if (!extent) { - extentFeature.setGeometry(undefined); - } else { - extentFeature.setGeometry(polygonFromExtent(extent)); - } - } - return extentFeature; -}; - - -/** - * @param {module:ol/coordinate~Coordinate} vertex location of feature - * @returns {module:ol/Feature} vertex as feature - * @private - */ -ExtentInteraction.prototype.createOrUpdatePointerFeature_ = function(vertex) { - let vertexFeature = this.vertexFeature_; - if (!vertexFeature) { - vertexFeature = new Feature(new Point(vertex)); - this.vertexFeature_ = vertexFeature; - this.vertexOverlay_.getSource().addFeature(vertexFeature); - } else { - const geometry = /** @type {module:ol/geom/Point} */ (vertexFeature.getGeometry()); - geometry.setCoordinates(vertex); - } - return vertexFeature; -}; - - -/** - * @inheritDoc - */ -ExtentInteraction.prototype.setMap = function(map) { - this.extentOverlay_.setMap(map); - this.vertexOverlay_.setMap(map); - PointerInteraction.prototype.setMap.call(this, map); -}; - -/** - * Returns the current drawn extent in the view projection - * - * @return {module:ol/extent~Extent} Drawn extent in the view projection. - * @api - */ -ExtentInteraction.prototype.getExtent = function() { - return this.extent_; -}; - -/** - * Manually sets the drawn extent, using the view projection. - * - * @param {module:ol/extent~Extent} extent Extent - * @api - */ -ExtentInteraction.prototype.setExtent = function(extent) { - //Null extent means no bbox - this.extent_ = extent ? extent : null; - this.createOrUpdateExtentFeature_(extent); - this.dispatchEvent(new ExtentInteractionEvent(this.extent_)); -}; - export default ExtentInteraction; diff --git a/src/ol/interaction/Interaction.js b/src/ol/interaction/Interaction.js index df61855b82..7bd2e432f8 100644 --- a/src/ol/interaction/Interaction.js +++ b/src/ol/interaction/Interaction.js @@ -36,71 +36,69 @@ import {clamp} from '../math.js'; * @extends {module:ol/Object} * @api */ -const Interaction = function(options) { +class Interaction { + constructor(options) { - BaseObject.call(this); + BaseObject.call(this); + + /** + * @private + * @type {module:ol/PluggableMap} + */ + this.map_ = null; + + this.setActive(true); + + /** + * @type {function(module:ol/MapBrowserEvent):boolean} + */ + this.handleEvent = options.handleEvent; + + } /** - * @private - * @type {module:ol/PluggableMap} + * Return whether the interaction is currently active. + * @return {boolean} `true` if the interaction is active, `false` otherwise. + * @observable + * @api */ - this.map_ = null; - - this.setActive(true); + getActive() { + return /** @type {boolean} */ (this.get(InteractionProperty.ACTIVE)); + } /** - * @type {function(module:ol/MapBrowserEvent):boolean} + * Get the map associated with this interaction. + * @return {module:ol/PluggableMap} Map. + * @api */ - this.handleEvent = options.handleEvent; + getMap() { + return this.map_; + } -}; + /** + * Activate or deactivate the interaction. + * @param {boolean} active Active. + * @observable + * @api + */ + setActive(active) { + this.set(InteractionProperty.ACTIVE, active); + } + + /** + * Remove the interaction from its current map and attach it to the new map. + * Subclasses may set up event handlers to get notified about changes to + * the map here. + * @param {module:ol/PluggableMap} map Map. + */ + setMap(map) { + this.map_ = map; + } +} inherits(Interaction, BaseObject); -/** - * Return whether the interaction is currently active. - * @return {boolean} `true` if the interaction is active, `false` otherwise. - * @observable - * @api - */ -Interaction.prototype.getActive = function() { - return /** @type {boolean} */ (this.get(InteractionProperty.ACTIVE)); -}; - - -/** - * Get the map associated with this interaction. - * @return {module:ol/PluggableMap} Map. - * @api - */ -Interaction.prototype.getMap = function() { - return this.map_; -}; - - -/** - * Activate or deactivate the interaction. - * @param {boolean} active Active. - * @observable - * @api - */ -Interaction.prototype.setActive = function(active) { - this.set(InteractionProperty.ACTIVE, active); -}; - - -/** - * Remove the interaction from its current map and attach it to the new map. - * Subclasses may set up event handlers to get notified about changes to - * the map here. - * @param {module:ol/PluggableMap} map Map. - */ -Interaction.prototype.setMap = function(map) { - this.map_ = map; -}; - - /** * @param {module:ol/View} view View. * @param {module:ol/coordinate~Coordinate} delta Delta. diff --git a/src/ol/interaction/Modify.js b/src/ol/interaction/Modify.js index f1d2f8d005..bff27bc18d 100644 --- a/src/ol/interaction/Modify.js +++ b/src/ol/interaction/Modify.js @@ -140,187 +140,815 @@ inherits(ModifyEvent, Event); * @fires module:ol/interaction/Modify~ModifyEvent * @api */ -const Modify = function(options) { +class Modify { + constructor(options) { - PointerInteraction.call(this, { - handleDownEvent: handleDownEvent, - handleDragEvent: handleDragEvent, - handleEvent: handleEvent, - handleUpEvent: handleUpEvent - }); + PointerInteraction.call(this, { + handleDownEvent: handleDownEvent, + handleDragEvent: handleDragEvent, + handleEvent: handleEvent, + handleUpEvent: handleUpEvent + }); - /** - * @private - * @type {module:ol/events/condition~Condition} - */ - this.condition_ = options.condition ? options.condition : primaryAction; + /** + * @private + * @type {module:ol/events/condition~Condition} + */ + this.condition_ = options.condition ? options.condition : primaryAction; - /** - * @private - * @param {module:ol/MapBrowserEvent} mapBrowserEvent Browser event. - * @return {boolean} Combined condition result. - */ - this.defaultDeleteCondition_ = function(mapBrowserEvent) { - return altKeyOnly(mapBrowserEvent) && singleClick(mapBrowserEvent); - }; + /** + * @private + * @param {module:ol/MapBrowserEvent} mapBrowserEvent Browser event. + * @return {boolean} Combined condition result. + */ + this.defaultDeleteCondition_ = function(mapBrowserEvent) { + return altKeyOnly(mapBrowserEvent) && singleClick(mapBrowserEvent); + }; - /** - * @type {module:ol/events/condition~Condition} - * @private - */ - this.deleteCondition_ = options.deleteCondition ? - options.deleteCondition : this.defaultDeleteCondition_; + /** + * @type {module:ol/events/condition~Condition} + * @private + */ + this.deleteCondition_ = options.deleteCondition ? + options.deleteCondition : this.defaultDeleteCondition_; - /** - * @type {module:ol/events/condition~Condition} - * @private - */ - this.insertVertexCondition_ = options.insertVertexCondition ? - options.insertVertexCondition : always; + /** + * @type {module:ol/events/condition~Condition} + * @private + */ + this.insertVertexCondition_ = options.insertVertexCondition ? + options.insertVertexCondition : always; - /** - * Editing vertex. - * @type {module:ol/Feature} - * @private - */ - this.vertexFeature_ = null; + /** + * Editing vertex. + * @type {module:ol/Feature} + * @private + */ + this.vertexFeature_ = null; - /** - * Segments intersecting {@link this.vertexFeature_} by segment uid. - * @type {Object.} - * @private - */ - this.vertexSegments_ = null; + /** + * Segments intersecting {@link this.vertexFeature_} by segment uid. + * @type {Object.} + * @private + */ + this.vertexSegments_ = null; - /** - * @type {module:ol~Pixel} - * @private - */ - this.lastPixel_ = [0, 0]; + /** + * @type {module:ol~Pixel} + * @private + */ + this.lastPixel_ = [0, 0]; - /** - * Tracks if the next `singleclick` event should be ignored to prevent - * accidental deletion right after vertex creation. - * @type {boolean} - * @private - */ - this.ignoreNextSingleClick_ = false; + /** + * Tracks if the next `singleclick` event should be ignored to prevent + * accidental deletion right after vertex creation. + * @type {boolean} + * @private + */ + this.ignoreNextSingleClick_ = false; - /** - * @type {boolean} - * @private - */ - this.modified_ = false; + /** + * @type {boolean} + * @private + */ + this.modified_ = false; - /** - * Segment RTree for each layer - * @type {module:ol/structs/RBush.} - * @private - */ - this.rBush_ = new RBush(); + /** + * Segment RTree for each layer + * @type {module:ol/structs/RBush.} + * @private + */ + this.rBush_ = new RBush(); - /** - * @type {number} - * @private - */ - this.pixelTolerance_ = options.pixelTolerance !== undefined ? - options.pixelTolerance : 10; + /** + * @type {number} + * @private + */ + this.pixelTolerance_ = options.pixelTolerance !== undefined ? + options.pixelTolerance : 10; - /** - * @type {boolean} - * @private - */ - this.snappedToVertex_ = false; + /** + * @type {boolean} + * @private + */ + this.snappedToVertex_ = false; - /** - * Indicate whether the interaction is currently changing a feature's - * coordinates. - * @type {boolean} - * @private - */ - this.changingFeature_ = false; + /** + * Indicate whether the interaction is currently changing a feature's + * coordinates. + * @type {boolean} + * @private + */ + this.changingFeature_ = false; - /** - * @type {Array} - * @private - */ - this.dragSegments_ = []; + /** + * @type {Array} + * @private + */ + this.dragSegments_ = []; - /** - * Draw overlay where sketch features are drawn. - * @type {module:ol/layer/Vector} - * @private - */ - this.overlay_ = new VectorLayer({ - source: new VectorSource({ - useSpatialIndex: false, - wrapX: !!options.wrapX - }), - style: options.style ? options.style : - getDefaultStyleFunction(), - updateWhileAnimating: true, - updateWhileInteracting: true - }); + /** + * Draw overlay where sketch features are drawn. + * @type {module:ol/layer/Vector} + * @private + */ + this.overlay_ = new VectorLayer({ + source: new VectorSource({ + useSpatialIndex: false, + wrapX: !!options.wrapX + }), + style: options.style ? options.style : + getDefaultStyleFunction(), + updateWhileAnimating: true, + updateWhileInteracting: true + }); - /** - * @const - * @private - * @type {!Object.} - */ - this.SEGMENT_WRITERS_ = { - 'Point': this.writePointGeometry_, - 'LineString': this.writeLineStringGeometry_, - 'LinearRing': this.writeLineStringGeometry_, - 'Polygon': this.writePolygonGeometry_, - 'MultiPoint': this.writeMultiPointGeometry_, - 'MultiLineString': this.writeMultiLineStringGeometry_, - 'MultiPolygon': this.writeMultiPolygonGeometry_, - 'Circle': this.writeCircleGeometry_, - 'GeometryCollection': this.writeGeometryCollectionGeometry_ - }; + /** + * @const + * @private + * @type {!Object.} + */ + this.SEGMENT_WRITERS_ = { + 'Point': this.writePointGeometry_, + 'LineString': this.writeLineStringGeometry_, + 'LinearRing': this.writeLineStringGeometry_, + 'Polygon': this.writePolygonGeometry_, + 'MultiPoint': this.writeMultiPointGeometry_, + 'MultiLineString': this.writeMultiLineStringGeometry_, + 'MultiPolygon': this.writeMultiPolygonGeometry_, + 'Circle': this.writeCircleGeometry_, + 'GeometryCollection': this.writeGeometryCollectionGeometry_ + }; - /** - * @type {module:ol/source/Vector} - * @private - */ - this.source_ = null; + /** + * @type {module:ol/source/Vector} + * @private + */ + this.source_ = null; + + let features; + if (options.source) { + this.source_ = options.source; + features = new Collection(this.source_.getFeatures()); + listen(this.source_, VectorEventType.ADDFEATURE, + this.handleSourceAdd_, this); + listen(this.source_, VectorEventType.REMOVEFEATURE, + this.handleSourceRemove_, this); + } else { + features = options.features; + } + if (!features) { + throw new Error('The modify interaction requires features or a source'); + } + + /** + * @type {module:ol/Collection.} + * @private + */ + this.features_ = features; + + this.features_.forEach(this.addFeature_.bind(this)); + listen(this.features_, CollectionEventType.ADD, + this.handleFeatureAdd_, this); + listen(this.features_, CollectionEventType.REMOVE, + this.handleFeatureRemove_, this); + + /** + * @type {module:ol/MapBrowserPointerEvent} + * @private + */ + this.lastPointerEvent_ = null; - let features; - if (options.source) { - this.source_ = options.source; - features = new Collection(this.source_.getFeatures()); - listen(this.source_, VectorEventType.ADDFEATURE, - this.handleSourceAdd_, this); - listen(this.source_, VectorEventType.REMOVEFEATURE, - this.handleSourceRemove_, this); - } else { - features = options.features; - } - if (!features) { - throw new Error('The modify interaction requires features or a source'); } /** - * @type {module:ol/Collection.} + * @param {module:ol/Feature} feature Feature. * @private */ - this.features_ = features; - - this.features_.forEach(this.addFeature_.bind(this)); - listen(this.features_, CollectionEventType.ADD, - this.handleFeatureAdd_, this); - listen(this.features_, CollectionEventType.REMOVE, - this.handleFeatureRemove_, this); + addFeature_(feature) { + const geometry = feature.getGeometry(); + if (geometry && geometry.getType() in this.SEGMENT_WRITERS_) { + this.SEGMENT_WRITERS_[geometry.getType()].call(this, feature, geometry); + } + const map = this.getMap(); + if (map && map.isRendered() && this.getActive()) { + this.handlePointerAtPixel_(this.lastPixel_, map); + } + listen(feature, EventType.CHANGE, + this.handleFeatureChange_, this); + } /** - * @type {module:ol/MapBrowserPointerEvent} + * @param {module:ol/MapBrowserPointerEvent} evt Map browser event * @private */ - this.lastPointerEvent_ = null; + willModifyFeatures_(evt) { + if (!this.modified_) { + this.modified_ = true; + this.dispatchEvent(new ModifyEvent( + ModifyEventType.MODIFYSTART, this.features_, evt)); + } + } -}; + /** + * @param {module:ol/Feature} feature Feature. + * @private + */ + removeFeature_(feature) { + this.removeFeatureSegmentData_(feature); + // 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_); + this.vertexFeature_ = null; + } + unlisten(feature, EventType.CHANGE, + this.handleFeatureChange_, this); + } + + /** + * @param {module:ol/Feature} feature Feature. + * @private + */ + removeFeatureSegmentData_(feature) { + const rBush = this.rBush_; + const /** @type {Array.} */ nodesToRemove = []; + rBush.forEach( + /** + * @param {module:ol/interaction/Modify~SegmentData} node RTree node. + */ + function(node) { + if (feature === node.feature) { + nodesToRemove.push(node); + } + }); + for (let i = nodesToRemove.length - 1; i >= 0; --i) { + rBush.remove(nodesToRemove[i]); + } + } + + /** + * @inheritDoc + */ + setActive(active) { + if (this.vertexFeature_ && !active) { + this.overlay_.getSource().removeFeature(this.vertexFeature_); + this.vertexFeature_ = null; + } + PointerInteraction.prototype.setActive.call(this, active); + } + + /** + * @inheritDoc + */ + setMap(map) { + this.overlay_.setMap(map); + PointerInteraction.prototype.setMap.call(this, map); + } + + /** + * @param {module:ol/source/Vector~VectorSourceEvent} event Event. + * @private + */ + handleSourceAdd_(event) { + if (event.feature) { + this.features_.push(event.feature); + } + } + + /** + * @param {module:ol/source/Vector~VectorSourceEvent} event Event. + * @private + */ + handleSourceRemove_(event) { + if (event.feature) { + this.features_.remove(event.feature); + } + } + + /** + * @param {module:ol/Collection~CollectionEvent} evt Event. + * @private + */ + handleFeatureAdd_(evt) { + this.addFeature_(/** @type {module:ol/Feature} */ (evt.element)); + } + + /** + * @param {module:ol/events/Event} evt Event. + * @private + */ + handleFeatureChange_(evt) { + if (!this.changingFeature_) { + const feature = /** @type {module:ol/Feature} */ (evt.target); + this.removeFeature_(feature); + this.addFeature_(feature); + } + } + + /** + * @param {module:ol/Collection~CollectionEvent} evt Event. + * @private + */ + handleFeatureRemove_(evt) { + const feature = /** @type {module:ol/Feature} */ (evt.element); + this.removeFeature_(feature); + } + + /** + * @param {module:ol/Feature} feature Feature + * @param {module:ol/geom/Point} geometry Geometry. + * @private + */ + writePointGeometry_(feature, geometry) { + const coordinates = geometry.getCoordinates(); + const segmentData = /** @type {module:ol/interaction/Modify~SegmentData} */ ({ + feature: feature, + geometry: geometry, + segment: [coordinates, coordinates] + }); + this.rBush_.insert(geometry.getExtent(), segmentData); + } + + /** + * @param {module:ol/Feature} feature Feature + * @param {module:ol/geom/MultiPoint} geometry Geometry. + * @private + */ + writeMultiPointGeometry_(feature, geometry) { + const points = geometry.getCoordinates(); + for (let i = 0, ii = points.length; i < ii; ++i) { + const coordinates = points[i]; + const segmentData = /** @type {module:ol/interaction/Modify~SegmentData} */ ({ + feature: feature, + geometry: geometry, + depth: [i], + index: i, + segment: [coordinates, coordinates] + }); + this.rBush_.insert(geometry.getExtent(), segmentData); + } + } + + /** + * @param {module:ol/Feature} feature Feature + * @param {module:ol/geom/LineString} geometry Geometry. + * @private + */ + writeLineStringGeometry_(feature, geometry) { + const coordinates = geometry.getCoordinates(); + for (let i = 0, ii = coordinates.length - 1; i < ii; ++i) { + const segment = coordinates.slice(i, i + 2); + const segmentData = /** @type {module:ol/interaction/Modify~SegmentData} */ ({ + feature: feature, + geometry: geometry, + index: i, + segment: segment + }); + this.rBush_.insert(boundingExtent(segment), segmentData); + } + } + + /** + * @param {module:ol/Feature} feature Feature + * @param {module:ol/geom/MultiLineString} geometry Geometry. + * @private + */ + writeMultiLineStringGeometry_(feature, geometry) { + const lines = geometry.getCoordinates(); + for (let j = 0, jj = lines.length; j < jj; ++j) { + const coordinates = lines[j]; + for (let i = 0, ii = coordinates.length - 1; i < ii; ++i) { + const segment = coordinates.slice(i, i + 2); + const segmentData = /** @type {module:ol/interaction/Modify~SegmentData} */ ({ + feature: feature, + geometry: geometry, + depth: [j], + index: i, + segment: segment + }); + this.rBush_.insert(boundingExtent(segment), segmentData); + } + } + } + + /** + * @param {module:ol/Feature} feature Feature + * @param {module:ol/geom/Polygon} geometry Geometry. + * @private + */ + writePolygonGeometry_(feature, geometry) { + const rings = geometry.getCoordinates(); + for (let j = 0, jj = rings.length; j < jj; ++j) { + const coordinates = rings[j]; + for (let i = 0, ii = coordinates.length - 1; i < ii; ++i) { + const segment = coordinates.slice(i, i + 2); + const segmentData = /** @type {module:ol/interaction/Modify~SegmentData} */ ({ + feature: feature, + geometry: geometry, + depth: [j], + index: i, + segment: segment + }); + this.rBush_.insert(boundingExtent(segment), segmentData); + } + } + } + + /** + * @param {module:ol/Feature} feature Feature + * @param {module:ol/geom/MultiPolygon} geometry Geometry. + * @private + */ + writeMultiPolygonGeometry_(feature, geometry) { + const polygons = geometry.getCoordinates(); + for (let k = 0, kk = polygons.length; k < kk; ++k) { + const rings = polygons[k]; + for (let j = 0, jj = rings.length; j < jj; ++j) { + const coordinates = rings[j]; + for (let i = 0, ii = coordinates.length - 1; i < ii; ++i) { + const segment = coordinates.slice(i, i + 2); + const segmentData = /** @type {module:ol/interaction/Modify~SegmentData} */ ({ + feature: feature, + geometry: geometry, + depth: [j, k], + index: i, + segment: segment + }); + this.rBush_.insert(boundingExtent(segment), segmentData); + } + } + } + } + + /** + * We convert a circle into two segments. The segment at index + * {@link CIRCLE_CENTER_INDEX} is the + * circle's center (a point). The segment at index + * {@link CIRCLE_CIRCUMFERENCE_INDEX} is + * the circumference, and is not a line segment. + * + * @param {module:ol/Feature} feature Feature. + * @param {module:ol/geom/Circle} geometry Geometry. + * @private + */ + writeCircleGeometry_(feature, geometry) { + const coordinates = geometry.getCenter(); + const centerSegmentData = /** @type {module:ol/interaction/Modify~SegmentData} */ ({ + feature: feature, + geometry: geometry, + index: CIRCLE_CENTER_INDEX, + segment: [coordinates, coordinates] + }); + const circumferenceSegmentData = /** @type {module:ol/interaction/Modify~SegmentData} */ ({ + feature: feature, + geometry: geometry, + index: CIRCLE_CIRCUMFERENCE_INDEX, + segment: [coordinates, coordinates] + }); + const featureSegments = [centerSegmentData, circumferenceSegmentData]; + centerSegmentData.featureSegments = circumferenceSegmentData.featureSegments = featureSegments; + this.rBush_.insert(createOrUpdateFromCoordinate(coordinates), centerSegmentData); + this.rBush_.insert(geometry.getExtent(), circumferenceSegmentData); + } + + /** + * @param {module:ol/Feature} feature Feature + * @param {module:ol/geom/GeometryCollection} geometry Geometry. + * @private + */ + writeGeometryCollectionGeometry_(feature, geometry) { + const geometries = geometry.getGeometriesArray(); + for (let i = 0; i < geometries.length; ++i) { + this.SEGMENT_WRITERS_[geometries[i].getType()].call(this, feature, geometries[i]); + } + } + + /** + * @param {module:ol/coordinate~Coordinate} coordinates Coordinates. + * @return {module:ol/Feature} Vertex feature. + * @private + */ + createOrUpdateVertexFeature_(coordinates) { + let vertexFeature = this.vertexFeature_; + if (!vertexFeature) { + vertexFeature = new Feature(new Point(coordinates)); + this.vertexFeature_ = vertexFeature; + this.overlay_.getSource().addFeature(vertexFeature); + } else { + const geometry = /** @type {module:ol/geom/Point} */ (vertexFeature.getGeometry()); + geometry.setCoordinates(coordinates); + } + return vertexFeature; + } + + /** + * @param {module:ol/MapBrowserEvent} evt Event. + * @private + */ + handlePointerMove_(evt) { + this.lastPixel_ = evt.pixel; + this.handlePointerAtPixel_(evt.pixel, evt.map); + } + + /** + * @param {module:ol~Pixel} pixel Pixel + * @param {module:ol/PluggableMap} map Map. + * @private + */ + handlePointerAtPixel_(pixel, map) { + const pixelCoordinate = map.getCoordinateFromPixel(pixel); + const sortByDistance = function(a, b) { + return pointDistanceToSegmentDataSquared(pixelCoordinate, a) - + pointDistanceToSegmentDataSquared(pixelCoordinate, b); + }; + + const box = buffer(createOrUpdateFromCoordinate(pixelCoordinate), + map.getView().getResolution() * this.pixelTolerance_); + + const rBush = this.rBush_; + const nodes = rBush.getInExtent(box); + if (nodes.length > 0) { + nodes.sort(sortByDistance); + const node = nodes[0]; + const closestSegment = node.segment; + let vertex = closestOnSegmentData(pixelCoordinate, node); + const vertexPixel = map.getPixelFromCoordinate(vertex); + let dist = coordinateDistance(pixel, vertexPixel); + if (dist <= this.pixelTolerance_) { + const vertexSegments = {}; + + if (node.geometry.getType() === GeometryType.CIRCLE && + node.index === CIRCLE_CIRCUMFERENCE_INDEX) { + + this.snappedToVertex_ = true; + this.createOrUpdateVertexFeature_(vertex); + } else { + const pixel1 = map.getPixelFromCoordinate(closestSegment[0]); + const pixel2 = map.getPixelFromCoordinate(closestSegment[1]); + const squaredDist1 = squaredCoordinateDistance(vertexPixel, pixel1); + const squaredDist2 = squaredCoordinateDistance(vertexPixel, pixel2); + dist = Math.sqrt(Math.min(squaredDist1, squaredDist2)); + this.snappedToVertex_ = dist <= this.pixelTolerance_; + if (this.snappedToVertex_) { + vertex = squaredDist1 > squaredDist2 ? closestSegment[1] : closestSegment[0]; + } + this.createOrUpdateVertexFeature_(vertex); + for (let i = 1, ii = nodes.length; i < ii; ++i) { + const segment = nodes[i].segment; + if ((coordinatesEqual(closestSegment[0], segment[0]) && + coordinatesEqual(closestSegment[1], segment[1]) || + (coordinatesEqual(closestSegment[0], segment[1]) && + coordinatesEqual(closestSegment[1], segment[0])))) { + vertexSegments[getUid(segment)] = true; + } else { + break; + } + } + } + + vertexSegments[getUid(closestSegment)] = true; + this.vertexSegments_ = vertexSegments; + return; + } + } + if (this.vertexFeature_) { + this.overlay_.getSource().removeFeature(this.vertexFeature_); + this.vertexFeature_ = null; + } + } + + /** + * @param {module:ol/interaction/Modify~SegmentData} segmentData Segment data. + * @param {module:ol/coordinate~Coordinate} vertex Vertex. + * @private + */ + insertVertex_(segmentData, vertex) { + const segment = segmentData.segment; + const feature = segmentData.feature; + const geometry = segmentData.geometry; + const depth = segmentData.depth; + const index = /** @type {number} */ (segmentData.index); + let coordinates; + + while (vertex.length < geometry.getStride()) { + vertex.push(0); + } + + switch (geometry.getType()) { + case GeometryType.MULTI_LINE_STRING: + coordinates = geometry.getCoordinates(); + coordinates[depth[0]].splice(index + 1, 0, vertex); + break; + case GeometryType.POLYGON: + coordinates = geometry.getCoordinates(); + coordinates[depth[0]].splice(index + 1, 0, vertex); + break; + case GeometryType.MULTI_POLYGON: + coordinates = geometry.getCoordinates(); + coordinates[depth[1]][depth[0]].splice(index + 1, 0, vertex); + break; + case GeometryType.LINE_STRING: + coordinates = geometry.getCoordinates(); + coordinates.splice(index + 1, 0, vertex); + break; + default: + return; + } + + this.setGeometryCoordinates_(geometry, coordinates); + const rTree = this.rBush_; + rTree.remove(segmentData); + this.updateSegmentIndices_(geometry, index, depth, 1); + const newSegmentData = /** @type {module:ol/interaction/Modify~SegmentData} */ ({ + segment: [segment[0], vertex], + feature: feature, + geometry: geometry, + depth: depth, + index: index + }); + rTree.insert(boundingExtent(newSegmentData.segment), + newSegmentData); + this.dragSegments_.push([newSegmentData, 1]); + + const newSegmentData2 = /** @type {module:ol/interaction/Modify~SegmentData} */ ({ + segment: [vertex, segment[1]], + feature: feature, + geometry: geometry, + depth: depth, + index: index + 1 + }); + rTree.insert(boundingExtent(newSegmentData2.segment), newSegmentData2); + this.dragSegments_.push([newSegmentData2, 0]); + this.ignoreNextSingleClick_ = true; + } + + /** + * Removes the vertex currently being pointed. + * @return {boolean} True when a vertex was removed. + * @api + */ + removePoint() { + if (this.lastPointerEvent_ && this.lastPointerEvent_.type != MapBrowserEventType.POINTERDRAG) { + const evt = this.lastPointerEvent_; + this.willModifyFeatures_(evt); + this.removeVertex_(); + this.dispatchEvent(new ModifyEvent(ModifyEventType.MODIFYEND, this.features_, evt)); + this.modified_ = false; + return true; + } + return false; + } + + /** + * Removes a vertex from all matching features. + * @return {boolean} True when a vertex was removed. + * @private + */ + removeVertex_() { + const dragSegments = this.dragSegments_; + const segmentsByFeature = {}; + let deleted = false; + let component, coordinates, dragSegment, geometry, i, index, left; + let newIndex, right, segmentData, uid; + for (i = dragSegments.length - 1; i >= 0; --i) { + dragSegment = dragSegments[i]; + segmentData = dragSegment[0]; + uid = getUid(segmentData.feature); + if (segmentData.depth) { + // separate feature components + uid += '-' + segmentData.depth.join('-'); + } + if (!(uid in segmentsByFeature)) { + segmentsByFeature[uid] = {}; + } + if (dragSegment[1] === 0) { + segmentsByFeature[uid].right = segmentData; + segmentsByFeature[uid].index = segmentData.index; + } else if (dragSegment[1] == 1) { + segmentsByFeature[uid].left = segmentData; + segmentsByFeature[uid].index = segmentData.index + 1; + } + + } + for (uid in segmentsByFeature) { + right = segmentsByFeature[uid].right; + left = segmentsByFeature[uid].left; + index = segmentsByFeature[uid].index; + newIndex = index - 1; + if (left !== undefined) { + segmentData = left; + } else { + segmentData = right; + } + if (newIndex < 0) { + newIndex = 0; + } + geometry = segmentData.geometry; + coordinates = geometry.getCoordinates(); + component = coordinates; + deleted = false; + switch (geometry.getType()) { + case GeometryType.MULTI_LINE_STRING: + if (coordinates[segmentData.depth[0]].length > 2) { + coordinates[segmentData.depth[0]].splice(index, 1); + deleted = true; + } + break; + case GeometryType.LINE_STRING: + if (coordinates.length > 2) { + coordinates.splice(index, 1); + deleted = true; + } + break; + case GeometryType.MULTI_POLYGON: + component = component[segmentData.depth[1]]; + /* falls through */ + case GeometryType.POLYGON: + component = component[segmentData.depth[0]]; + if (component.length > 4) { + if (index == component.length - 1) { + index = 0; + } + component.splice(index, 1); + deleted = true; + if (index === 0) { + // close the ring again + component.pop(); + component.push(component[0]); + newIndex = component.length - 1; + } + } + break; + default: + // pass + } + + if (deleted) { + this.setGeometryCoordinates_(geometry, coordinates); + const segments = []; + if (left !== undefined) { + this.rBush_.remove(left); + segments.push(left.segment[0]); + } + if (right !== undefined) { + this.rBush_.remove(right); + segments.push(right.segment[1]); + } + if (left !== undefined && right !== undefined) { + const newSegmentData = /** @type {module:ol/interaction/Modify~SegmentData} */ ({ + depth: segmentData.depth, + feature: segmentData.feature, + geometry: segmentData.geometry, + index: newIndex, + segment: segments + }); + this.rBush_.insert(boundingExtent(newSegmentData.segment), + newSegmentData); + } + this.updateSegmentIndices_(geometry, index, segmentData.depth, -1); + if (this.vertexFeature_) { + this.overlay_.getSource().removeFeature(this.vertexFeature_); + this.vertexFeature_ = null; + } + dragSegments.length = 0; + } + + } + return deleted; + } + + /** + * @param {module:ol/geom/SimpleGeometry} geometry Geometry. + * @param {Array} coordinates Coordinates. + * @private + */ + setGeometryCoordinates_(geometry, coordinates) { + this.changingFeature_ = true; + geometry.setCoordinates(coordinates); + this.changingFeature_ = false; + } + + /** + * @param {module:ol/geom/SimpleGeometry} geometry Geometry. + * @param {number} index Index. + * @param {Array.|undefined} depth Depth. + * @param {number} delta Delta (1 or -1). + * @private + */ + updateSegmentIndices_(geometry, index, depth, delta) { + this.rBush_.forEachInExtent(geometry.getExtent(), function(segmentDataMatch) { + if (segmentDataMatch.geometry === geometry && + (depth === undefined || segmentDataMatch.depth === undefined || + equals(segmentDataMatch.depth, depth)) && + segmentDataMatch.index > index) { + segmentDataMatch.index += delta; + } + }); + } +} inherits(Modify, PointerInteraction); @@ -340,347 +968,6 @@ const CIRCLE_CENTER_INDEX = 0; const CIRCLE_CIRCUMFERENCE_INDEX = 1; -/** - * @param {module:ol/Feature} feature Feature. - * @private - */ -Modify.prototype.addFeature_ = function(feature) { - const geometry = feature.getGeometry(); - if (geometry && geometry.getType() in this.SEGMENT_WRITERS_) { - this.SEGMENT_WRITERS_[geometry.getType()].call(this, feature, geometry); - } - const map = this.getMap(); - if (map && map.isRendered() && this.getActive()) { - this.handlePointerAtPixel_(this.lastPixel_, map); - } - listen(feature, EventType.CHANGE, - this.handleFeatureChange_, this); -}; - - -/** - * @param {module:ol/MapBrowserPointerEvent} evt Map browser event - * @private - */ -Modify.prototype.willModifyFeatures_ = function(evt) { - if (!this.modified_) { - this.modified_ = true; - this.dispatchEvent(new ModifyEvent( - ModifyEventType.MODIFYSTART, this.features_, evt)); - } -}; - - -/** - * @param {module:ol/Feature} feature Feature. - * @private - */ -Modify.prototype.removeFeature_ = function(feature) { - this.removeFeatureSegmentData_(feature); - // 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_); - this.vertexFeature_ = null; - } - unlisten(feature, EventType.CHANGE, - this.handleFeatureChange_, this); -}; - - -/** - * @param {module:ol/Feature} feature Feature. - * @private - */ -Modify.prototype.removeFeatureSegmentData_ = function(feature) { - const rBush = this.rBush_; - const /** @type {Array.} */ nodesToRemove = []; - rBush.forEach( - /** - * @param {module:ol/interaction/Modify~SegmentData} node RTree node. - */ - function(node) { - if (feature === node.feature) { - nodesToRemove.push(node); - } - }); - for (let i = nodesToRemove.length - 1; i >= 0; --i) { - rBush.remove(nodesToRemove[i]); - } -}; - - -/** - * @inheritDoc - */ -Modify.prototype.setActive = function(active) { - if (this.vertexFeature_ && !active) { - this.overlay_.getSource().removeFeature(this.vertexFeature_); - this.vertexFeature_ = null; - } - PointerInteraction.prototype.setActive.call(this, active); -}; - - -/** - * @inheritDoc - */ -Modify.prototype.setMap = function(map) { - this.overlay_.setMap(map); - PointerInteraction.prototype.setMap.call(this, map); -}; - - -/** - * @param {module:ol/source/Vector~VectorSourceEvent} event Event. - * @private - */ -Modify.prototype.handleSourceAdd_ = function(event) { - if (event.feature) { - this.features_.push(event.feature); - } -}; - - -/** - * @param {module:ol/source/Vector~VectorSourceEvent} event Event. - * @private - */ -Modify.prototype.handleSourceRemove_ = function(event) { - if (event.feature) { - this.features_.remove(event.feature); - } -}; - - -/** - * @param {module:ol/Collection~CollectionEvent} evt Event. - * @private - */ -Modify.prototype.handleFeatureAdd_ = function(evt) { - this.addFeature_(/** @type {module:ol/Feature} */ (evt.element)); -}; - - -/** - * @param {module:ol/events/Event} evt Event. - * @private - */ -Modify.prototype.handleFeatureChange_ = function(evt) { - if (!this.changingFeature_) { - const feature = /** @type {module:ol/Feature} */ (evt.target); - this.removeFeature_(feature); - this.addFeature_(feature); - } -}; - - -/** - * @param {module:ol/Collection~CollectionEvent} evt Event. - * @private - */ -Modify.prototype.handleFeatureRemove_ = function(evt) { - const feature = /** @type {module:ol/Feature} */ (evt.element); - this.removeFeature_(feature); -}; - - -/** - * @param {module:ol/Feature} feature Feature - * @param {module:ol/geom/Point} geometry Geometry. - * @private - */ -Modify.prototype.writePointGeometry_ = function(feature, geometry) { - const coordinates = geometry.getCoordinates(); - const segmentData = /** @type {module:ol/interaction/Modify~SegmentData} */ ({ - feature: feature, - geometry: geometry, - segment: [coordinates, coordinates] - }); - this.rBush_.insert(geometry.getExtent(), segmentData); -}; - - -/** - * @param {module:ol/Feature} feature Feature - * @param {module:ol/geom/MultiPoint} geometry Geometry. - * @private - */ -Modify.prototype.writeMultiPointGeometry_ = function(feature, geometry) { - const points = geometry.getCoordinates(); - for (let i = 0, ii = points.length; i < ii; ++i) { - const coordinates = points[i]; - const segmentData = /** @type {module:ol/interaction/Modify~SegmentData} */ ({ - feature: feature, - geometry: geometry, - depth: [i], - index: i, - segment: [coordinates, coordinates] - }); - this.rBush_.insert(geometry.getExtent(), segmentData); - } -}; - - -/** - * @param {module:ol/Feature} feature Feature - * @param {module:ol/geom/LineString} geometry Geometry. - * @private - */ -Modify.prototype.writeLineStringGeometry_ = function(feature, geometry) { - const coordinates = geometry.getCoordinates(); - for (let i = 0, ii = coordinates.length - 1; i < ii; ++i) { - const segment = coordinates.slice(i, i + 2); - const segmentData = /** @type {module:ol/interaction/Modify~SegmentData} */ ({ - feature: feature, - geometry: geometry, - index: i, - segment: segment - }); - this.rBush_.insert(boundingExtent(segment), segmentData); - } -}; - - -/** - * @param {module:ol/Feature} feature Feature - * @param {module:ol/geom/MultiLineString} geometry Geometry. - * @private - */ -Modify.prototype.writeMultiLineStringGeometry_ = function(feature, geometry) { - const lines = geometry.getCoordinates(); - for (let j = 0, jj = lines.length; j < jj; ++j) { - const coordinates = lines[j]; - for (let i = 0, ii = coordinates.length - 1; i < ii; ++i) { - const segment = coordinates.slice(i, i + 2); - const segmentData = /** @type {module:ol/interaction/Modify~SegmentData} */ ({ - feature: feature, - geometry: geometry, - depth: [j], - index: i, - segment: segment - }); - this.rBush_.insert(boundingExtent(segment), segmentData); - } - } -}; - - -/** - * @param {module:ol/Feature} feature Feature - * @param {module:ol/geom/Polygon} geometry Geometry. - * @private - */ -Modify.prototype.writePolygonGeometry_ = function(feature, geometry) { - const rings = geometry.getCoordinates(); - for (let j = 0, jj = rings.length; j < jj; ++j) { - const coordinates = rings[j]; - for (let i = 0, ii = coordinates.length - 1; i < ii; ++i) { - const segment = coordinates.slice(i, i + 2); - const segmentData = /** @type {module:ol/interaction/Modify~SegmentData} */ ({ - feature: feature, - geometry: geometry, - depth: [j], - index: i, - segment: segment - }); - this.rBush_.insert(boundingExtent(segment), segmentData); - } - } -}; - - -/** - * @param {module:ol/Feature} feature Feature - * @param {module:ol/geom/MultiPolygon} geometry Geometry. - * @private - */ -Modify.prototype.writeMultiPolygonGeometry_ = function(feature, geometry) { - const polygons = geometry.getCoordinates(); - for (let k = 0, kk = polygons.length; k < kk; ++k) { - const rings = polygons[k]; - for (let j = 0, jj = rings.length; j < jj; ++j) { - const coordinates = rings[j]; - for (let i = 0, ii = coordinates.length - 1; i < ii; ++i) { - const segment = coordinates.slice(i, i + 2); - const segmentData = /** @type {module:ol/interaction/Modify~SegmentData} */ ({ - feature: feature, - geometry: geometry, - depth: [j, k], - index: i, - segment: segment - }); - this.rBush_.insert(boundingExtent(segment), segmentData); - } - } - } -}; - - -/** - * We convert a circle into two segments. The segment at index - * {@link CIRCLE_CENTER_INDEX} is the - * circle's center (a point). The segment at index - * {@link CIRCLE_CIRCUMFERENCE_INDEX} is - * the circumference, and is not a line segment. - * - * @param {module:ol/Feature} feature Feature. - * @param {module:ol/geom/Circle} geometry Geometry. - * @private - */ -Modify.prototype.writeCircleGeometry_ = function(feature, geometry) { - const coordinates = geometry.getCenter(); - const centerSegmentData = /** @type {module:ol/interaction/Modify~SegmentData} */ ({ - feature: feature, - geometry: geometry, - index: CIRCLE_CENTER_INDEX, - segment: [coordinates, coordinates] - }); - const circumferenceSegmentData = /** @type {module:ol/interaction/Modify~SegmentData} */ ({ - feature: feature, - geometry: geometry, - index: CIRCLE_CIRCUMFERENCE_INDEX, - segment: [coordinates, coordinates] - }); - const featureSegments = [centerSegmentData, circumferenceSegmentData]; - centerSegmentData.featureSegments = circumferenceSegmentData.featureSegments = featureSegments; - this.rBush_.insert(createOrUpdateFromCoordinate(coordinates), centerSegmentData); - this.rBush_.insert(geometry.getExtent(), circumferenceSegmentData); -}; - - -/** - * @param {module:ol/Feature} feature Feature - * @param {module:ol/geom/GeometryCollection} geometry Geometry. - * @private - */ -Modify.prototype.writeGeometryCollectionGeometry_ = function(feature, geometry) { - const geometries = geometry.getGeometriesArray(); - for (let i = 0; i < geometries.length; ++i) { - this.SEGMENT_WRITERS_[geometries[i].getType()].call(this, feature, geometries[i]); - } -}; - - -/** - * @param {module:ol/coordinate~Coordinate} coordinates Coordinates. - * @return {module:ol/Feature} Vertex feature. - * @private - */ -Modify.prototype.createOrUpdateVertexFeature_ = function(coordinates) { - let vertexFeature = this.vertexFeature_; - if (!vertexFeature) { - vertexFeature = new Feature(new Point(coordinates)); - this.vertexFeature_ = vertexFeature; - this.overlay_.getSource().addFeature(vertexFeature); - } else { - const geometry = /** @type {module:ol/geom/Point} */ (vertexFeature.getGeometry()); - geometry.setCoordinates(coordinates); - } - return vertexFeature; -}; - - /** * @param {module:ol/interaction/Modify~SegmentData} a The first segment data. * @param {module:ol/interaction/Modify~SegmentData} b The second segment data. @@ -908,84 +1195,6 @@ function handleEvent(mapBrowserEvent) { } -/** - * @param {module:ol/MapBrowserEvent} evt Event. - * @private - */ -Modify.prototype.handlePointerMove_ = function(evt) { - this.lastPixel_ = evt.pixel; - this.handlePointerAtPixel_(evt.pixel, evt.map); -}; - - -/** - * @param {module:ol~Pixel} pixel Pixel - * @param {module:ol/PluggableMap} map Map. - * @private - */ -Modify.prototype.handlePointerAtPixel_ = function(pixel, map) { - const pixelCoordinate = map.getCoordinateFromPixel(pixel); - const sortByDistance = function(a, b) { - return pointDistanceToSegmentDataSquared(pixelCoordinate, a) - - pointDistanceToSegmentDataSquared(pixelCoordinate, b); - }; - - const box = buffer(createOrUpdateFromCoordinate(pixelCoordinate), - map.getView().getResolution() * this.pixelTolerance_); - - const rBush = this.rBush_; - const nodes = rBush.getInExtent(box); - if (nodes.length > 0) { - nodes.sort(sortByDistance); - const node = nodes[0]; - const closestSegment = node.segment; - let vertex = closestOnSegmentData(pixelCoordinate, node); - const vertexPixel = map.getPixelFromCoordinate(vertex); - let dist = coordinateDistance(pixel, vertexPixel); - if (dist <= this.pixelTolerance_) { - const vertexSegments = {}; - - if (node.geometry.getType() === GeometryType.CIRCLE && - node.index === CIRCLE_CIRCUMFERENCE_INDEX) { - - this.snappedToVertex_ = true; - this.createOrUpdateVertexFeature_(vertex); - } else { - const pixel1 = map.getPixelFromCoordinate(closestSegment[0]); - const pixel2 = map.getPixelFromCoordinate(closestSegment[1]); - const squaredDist1 = squaredCoordinateDistance(vertexPixel, pixel1); - const squaredDist2 = squaredCoordinateDistance(vertexPixel, pixel2); - dist = Math.sqrt(Math.min(squaredDist1, squaredDist2)); - this.snappedToVertex_ = dist <= this.pixelTolerance_; - if (this.snappedToVertex_) { - vertex = squaredDist1 > squaredDist2 ? closestSegment[1] : closestSegment[0]; - } - this.createOrUpdateVertexFeature_(vertex); - for (let i = 1, ii = nodes.length; i < ii; ++i) { - const segment = nodes[i].segment; - if ((coordinatesEqual(closestSegment[0], segment[0]) && - coordinatesEqual(closestSegment[1], segment[1]) || - (coordinatesEqual(closestSegment[0], segment[1]) && - coordinatesEqual(closestSegment[1], segment[0])))) { - vertexSegments[getUid(segment)] = true; - } else { - break; - } - } - } - - vertexSegments[getUid(closestSegment)] = true; - this.vertexSegments_ = vertexSegments; - return; - } - } - if (this.vertexFeature_) { - this.overlay_.getSource().removeFeature(this.vertexFeature_); - this.vertexFeature_ = null; - } -}; - - /** * Returns the distance from a point to a line segment. * @@ -1032,239 +1241,6 @@ function closestOnSegmentData(pointCoordinates, segmentData) { } -/** - * @param {module:ol/interaction/Modify~SegmentData} segmentData Segment data. - * @param {module:ol/coordinate~Coordinate} vertex Vertex. - * @private - */ -Modify.prototype.insertVertex_ = function(segmentData, vertex) { - const segment = segmentData.segment; - const feature = segmentData.feature; - const geometry = segmentData.geometry; - const depth = segmentData.depth; - const index = /** @type {number} */ (segmentData.index); - let coordinates; - - while (vertex.length < geometry.getStride()) { - vertex.push(0); - } - - switch (geometry.getType()) { - case GeometryType.MULTI_LINE_STRING: - coordinates = geometry.getCoordinates(); - coordinates[depth[0]].splice(index + 1, 0, vertex); - break; - case GeometryType.POLYGON: - coordinates = geometry.getCoordinates(); - coordinates[depth[0]].splice(index + 1, 0, vertex); - break; - case GeometryType.MULTI_POLYGON: - coordinates = geometry.getCoordinates(); - coordinates[depth[1]][depth[0]].splice(index + 1, 0, vertex); - break; - case GeometryType.LINE_STRING: - coordinates = geometry.getCoordinates(); - coordinates.splice(index + 1, 0, vertex); - break; - default: - return; - } - - this.setGeometryCoordinates_(geometry, coordinates); - const rTree = this.rBush_; - rTree.remove(segmentData); - this.updateSegmentIndices_(geometry, index, depth, 1); - const newSegmentData = /** @type {module:ol/interaction/Modify~SegmentData} */ ({ - segment: [segment[0], vertex], - feature: feature, - geometry: geometry, - depth: depth, - index: index - }); - rTree.insert(boundingExtent(newSegmentData.segment), - newSegmentData); - this.dragSegments_.push([newSegmentData, 1]); - - const newSegmentData2 = /** @type {module:ol/interaction/Modify~SegmentData} */ ({ - segment: [vertex, segment[1]], - feature: feature, - geometry: geometry, - depth: depth, - index: index + 1 - }); - rTree.insert(boundingExtent(newSegmentData2.segment), newSegmentData2); - this.dragSegments_.push([newSegmentData2, 0]); - this.ignoreNextSingleClick_ = true; -}; - -/** - * Removes the vertex currently being pointed. - * @return {boolean} True when a vertex was removed. - * @api - */ -Modify.prototype.removePoint = function() { - if (this.lastPointerEvent_ && this.lastPointerEvent_.type != MapBrowserEventType.POINTERDRAG) { - const evt = this.lastPointerEvent_; - this.willModifyFeatures_(evt); - this.removeVertex_(); - this.dispatchEvent(new ModifyEvent(ModifyEventType.MODIFYEND, this.features_, evt)); - this.modified_ = false; - return true; - } - return false; -}; - -/** - * Removes a vertex from all matching features. - * @return {boolean} True when a vertex was removed. - * @private - */ -Modify.prototype.removeVertex_ = function() { - const dragSegments = this.dragSegments_; - const segmentsByFeature = {}; - let deleted = false; - let component, coordinates, dragSegment, geometry, i, index, left; - let newIndex, right, segmentData, uid; - for (i = dragSegments.length - 1; i >= 0; --i) { - dragSegment = dragSegments[i]; - segmentData = dragSegment[0]; - uid = getUid(segmentData.feature); - if (segmentData.depth) { - // separate feature components - uid += '-' + segmentData.depth.join('-'); - } - if (!(uid in segmentsByFeature)) { - segmentsByFeature[uid] = {}; - } - if (dragSegment[1] === 0) { - segmentsByFeature[uid].right = segmentData; - segmentsByFeature[uid].index = segmentData.index; - } else if (dragSegment[1] == 1) { - segmentsByFeature[uid].left = segmentData; - segmentsByFeature[uid].index = segmentData.index + 1; - } - - } - for (uid in segmentsByFeature) { - right = segmentsByFeature[uid].right; - left = segmentsByFeature[uid].left; - index = segmentsByFeature[uid].index; - newIndex = index - 1; - if (left !== undefined) { - segmentData = left; - } else { - segmentData = right; - } - if (newIndex < 0) { - newIndex = 0; - } - geometry = segmentData.geometry; - coordinates = geometry.getCoordinates(); - component = coordinates; - deleted = false; - switch (geometry.getType()) { - case GeometryType.MULTI_LINE_STRING: - if (coordinates[segmentData.depth[0]].length > 2) { - coordinates[segmentData.depth[0]].splice(index, 1); - deleted = true; - } - break; - case GeometryType.LINE_STRING: - if (coordinates.length > 2) { - coordinates.splice(index, 1); - deleted = true; - } - break; - case GeometryType.MULTI_POLYGON: - component = component[segmentData.depth[1]]; - /* falls through */ - case GeometryType.POLYGON: - component = component[segmentData.depth[0]]; - if (component.length > 4) { - if (index == component.length - 1) { - index = 0; - } - component.splice(index, 1); - deleted = true; - if (index === 0) { - // close the ring again - component.pop(); - component.push(component[0]); - newIndex = component.length - 1; - } - } - break; - default: - // pass - } - - if (deleted) { - this.setGeometryCoordinates_(geometry, coordinates); - const segments = []; - if (left !== undefined) { - this.rBush_.remove(left); - segments.push(left.segment[0]); - } - if (right !== undefined) { - this.rBush_.remove(right); - segments.push(right.segment[1]); - } - if (left !== undefined && right !== undefined) { - const newSegmentData = /** @type {module:ol/interaction/Modify~SegmentData} */ ({ - depth: segmentData.depth, - feature: segmentData.feature, - geometry: segmentData.geometry, - index: newIndex, - segment: segments - }); - this.rBush_.insert(boundingExtent(newSegmentData.segment), - newSegmentData); - } - this.updateSegmentIndices_(geometry, index, segmentData.depth, -1); - if (this.vertexFeature_) { - this.overlay_.getSource().removeFeature(this.vertexFeature_); - this.vertexFeature_ = null; - } - dragSegments.length = 0; - } - - } - return deleted; -}; - - -/** - * @param {module:ol/geom/SimpleGeometry} geometry Geometry. - * @param {Array} coordinates Coordinates. - * @private - */ -Modify.prototype.setGeometryCoordinates_ = function(geometry, coordinates) { - this.changingFeature_ = true; - geometry.setCoordinates(coordinates); - this.changingFeature_ = false; -}; - - -/** - * @param {module:ol/geom/SimpleGeometry} geometry Geometry. - * @param {number} index Index. - * @param {Array.|undefined} depth Depth. - * @param {number} delta Delta (1 or -1). - * @private - */ -Modify.prototype.updateSegmentIndices_ = function( - geometry, index, depth, delta) { - this.rBush_.forEachInExtent(geometry.getExtent(), function(segmentDataMatch) { - if (segmentDataMatch.geometry === geometry && - (depth === undefined || segmentDataMatch.depth === undefined || - equals(segmentDataMatch.depth, depth)) && - segmentDataMatch.index > index) { - segmentDataMatch.index += delta; - } - }); -}; - - /** * @return {module:ol/style/Style~StyleFunction} Styles. */ diff --git a/src/ol/interaction/MouseWheelZoom.js b/src/ol/interaction/MouseWheelZoom.js index d22e004721..e5ab1e3dce 100644 --- a/src/ol/interaction/MouseWheelZoom.js +++ b/src/ol/interaction/MouseWheelZoom.js @@ -53,101 +53,144 @@ export const Mode = { * @param {module:ol/interaction/MouseWheelZoom~Options=} opt_options Options. * @api */ -const MouseWheelZoom = function(opt_options) { +class MouseWheelZoom { + constructor(opt_options) { - Interaction.call(this, { - handleEvent: handleEvent - }); + Interaction.call(this, { + handleEvent: handleEvent + }); - const options = opt_options || {}; + const options = opt_options || {}; + + /** + * @private + * @type {number} + */ + this.delta_ = 0; + + /** + * @private + * @type {number} + */ + this.duration_ = options.duration !== undefined ? options.duration : 250; + + /** + * @private + * @type {number} + */ + this.timeout_ = options.timeout !== undefined ? options.timeout : 80; + + /** + * @private + * @type {boolean} + */ + this.useAnchor_ = options.useAnchor !== undefined ? options.useAnchor : true; + + /** + * @private + * @type {boolean} + */ + this.constrainResolution_ = options.constrainResolution || false; + + /** + * @private + * @type {module:ol/events/condition~Condition} + */ + this.condition_ = options.condition ? options.condition : always; + + /** + * @private + * @type {?module:ol/coordinate~Coordinate} + */ + this.lastAnchor_ = null; + + /** + * @private + * @type {number|undefined} + */ + this.startTime_ = undefined; + + /** + * @private + * @type {number|undefined} + */ + this.timeoutId_ = undefined; + + /** + * @private + * @type {module:ol/interaction/MouseWheelZoom~Mode|undefined} + */ + this.mode_ = undefined; + + /** + * Trackpad events separated by this delay will be considered separate + * interactions. + * @type {number} + */ + this.trackpadEventGap_ = 400; + + /** + * @type {number|undefined} + */ + this.trackpadTimeoutId_ = undefined; + + /** + * The number of delta values per zoom level + * @private + * @type {number} + */ + this.trackpadDeltaPerZoom_ = 300; + + /** + * The zoom factor by which scroll zooming is allowed to exceed the limits. + * @private + * @type {number} + */ + this.trackpadZoomBuffer_ = 1.5; + + } /** * @private - * @type {number} */ - this.delta_ = 0; + decrementInteractingHint_() { + this.trackpadTimeoutId_ = undefined; + const view = this.getMap().getView(); + view.setHint(ViewHint.INTERACTING, -1); + } /** * @private - * @type {number} + * @param {module:ol/PluggableMap} map Map. */ - this.duration_ = options.duration !== undefined ? options.duration : 250; + handleWheelZoom_(map) { + const view = map.getView(); + if (view.getAnimating()) { + view.cancelAnimations(); + } + const maxDelta = MAX_DELTA; + const delta = clamp(this.delta_, -maxDelta, maxDelta); + zoomByDelta(view, -delta, this.lastAnchor_, this.duration_); + this.mode_ = undefined; + this.delta_ = 0; + this.lastAnchor_ = null; + this.startTime_ = undefined; + this.timeoutId_ = undefined; + } /** - * @private - * @type {number} + * Enable or disable using the mouse's location as an anchor when zooming + * @param {boolean} useAnchor true to zoom to the mouse's location, false + * to zoom to the center of the map + * @api */ - this.timeout_ = options.timeout !== undefined ? options.timeout : 80; - - /** - * @private - * @type {boolean} - */ - this.useAnchor_ = options.useAnchor !== undefined ? options.useAnchor : true; - - /** - * @private - * @type {boolean} - */ - this.constrainResolution_ = options.constrainResolution || false; - - /** - * @private - * @type {module:ol/events/condition~Condition} - */ - this.condition_ = options.condition ? options.condition : always; - - /** - * @private - * @type {?module:ol/coordinate~Coordinate} - */ - this.lastAnchor_ = null; - - /** - * @private - * @type {number|undefined} - */ - this.startTime_ = undefined; - - /** - * @private - * @type {number|undefined} - */ - this.timeoutId_ = undefined; - - /** - * @private - * @type {module:ol/interaction/MouseWheelZoom~Mode|undefined} - */ - this.mode_ = undefined; - - /** - * Trackpad events separated by this delay will be considered separate - * interactions. - * @type {number} - */ - this.trackpadEventGap_ = 400; - - /** - * @type {number|undefined} - */ - this.trackpadTimeoutId_ = undefined; - - /** - * The number of delta values per zoom level - * @private - * @type {number} - */ - this.trackpadDeltaPerZoom_ = 300; - - /** - * The zoom factor by which scroll zooming is allowed to exceed the limits. - * @private - * @type {number} - */ - this.trackpadZoomBuffer_ = 1.5; - -}; + setMouseAnchor(useAnchor) { + this.useAnchor_ = useAnchor; + if (!useAnchor) { + this.lastAnchor_ = null; + } + } +} inherits(MouseWheelZoom, Interaction); @@ -276,48 +319,4 @@ function handleEvent(mapBrowserEvent) { } -/** - * @private - */ -MouseWheelZoom.prototype.decrementInteractingHint_ = function() { - this.trackpadTimeoutId_ = undefined; - const view = this.getMap().getView(); - view.setHint(ViewHint.INTERACTING, -1); -}; - - -/** - * @private - * @param {module:ol/PluggableMap} map Map. - */ -MouseWheelZoom.prototype.handleWheelZoom_ = function(map) { - const view = map.getView(); - if (view.getAnimating()) { - view.cancelAnimations(); - } - const maxDelta = MAX_DELTA; - const delta = clamp(this.delta_, -maxDelta, maxDelta); - zoomByDelta(view, -delta, this.lastAnchor_, this.duration_); - this.mode_ = undefined; - this.delta_ = 0; - this.lastAnchor_ = null; - this.startTime_ = undefined; - this.timeoutId_ = undefined; -}; - - -/** - * Enable or disable using the mouse's location as an anchor when zooming - * @param {boolean} useAnchor true to zoom to the mouse's location, false - * to zoom to the center of the map - * @api - */ -MouseWheelZoom.prototype.setMouseAnchor = function(useAnchor) { - this.useAnchor_ = useAnchor; - if (!useAnchor) { - this.lastAnchor_ = null; - } -}; - - export default MouseWheelZoom; diff --git a/src/ol/interaction/Pointer.js b/src/ol/interaction/Pointer.js index c0a2f4df51..928a026508 100644 --- a/src/ol/interaction/Pointer.js +++ b/src/ol/interaction/Pointer.js @@ -77,61 +77,102 @@ const handleMoveEvent = UNDEFINED; * @extends {module:ol/interaction/Interaction} * @api */ -const PointerInteraction = function(opt_options) { +class PointerInteraction { + constructor(opt_options) { - const options = opt_options ? opt_options : {}; + const options = opt_options ? opt_options : {}; - Interaction.call(this, { - handleEvent: options.handleEvent || handleEvent - }); + Interaction.call(this, { + handleEvent: options.handleEvent || handleEvent + }); + + /** + * @type {function(module:ol/MapBrowserPointerEvent):boolean} + * @private + */ + this.handleDownEvent_ = options.handleDownEvent ? + options.handleDownEvent : handleDownEvent; + + /** + * @type {function(module:ol/MapBrowserPointerEvent)} + * @private + */ + this.handleDragEvent_ = options.handleDragEvent ? + options.handleDragEvent : handleDragEvent; + + /** + * @type {function(module:ol/MapBrowserPointerEvent)} + * @private + */ + this.handleMoveEvent_ = options.handleMoveEvent ? + options.handleMoveEvent : handleMoveEvent; + + /** + * @type {function(module:ol/MapBrowserPointerEvent):boolean} + * @private + */ + this.handleUpEvent_ = options.handleUpEvent ? + options.handleUpEvent : handleUpEvent; + + /** + * @type {boolean} + * @protected + */ + this.handlingDownUpSequence = false; + + /** + * @type {!Object.} + * @private + */ + this.trackedPointers_ = {}; + + /** + * @type {Array.} + * @protected + */ + this.targetPointers = []; + + } /** - * @type {function(module:ol/MapBrowserPointerEvent):boolean} + * @param {module:ol/MapBrowserPointerEvent} mapBrowserEvent Event. * @private */ - this.handleDownEvent_ = options.handleDownEvent ? - options.handleDownEvent : handleDownEvent; + updateTrackedPointers_(mapBrowserEvent) { + if (isPointerDraggingEvent(mapBrowserEvent)) { + const event = mapBrowserEvent.pointerEvent; + + const id = event.pointerId.toString(); + if (mapBrowserEvent.type == MapBrowserEventType.POINTERUP) { + delete this.trackedPointers_[id]; + } else if (mapBrowserEvent.type == + MapBrowserEventType.POINTERDOWN) { + this.trackedPointers_[id] = event; + } else if (id in this.trackedPointers_) { + // update only when there was a pointerdown event for this pointer + this.trackedPointers_[id] = event; + } + this.targetPointers = getValues(this.trackedPointers_); + } + } /** - * @type {function(module:ol/MapBrowserPointerEvent)} - * @private - */ - this.handleDragEvent_ = options.handleDragEvent ? - options.handleDragEvent : handleDragEvent; - - /** - * @type {function(module:ol/MapBrowserPointerEvent)} - * @private - */ - this.handleMoveEvent_ = options.handleMoveEvent ? - options.handleMoveEvent : handleMoveEvent; - - /** - * @type {function(module:ol/MapBrowserPointerEvent):boolean} - * @private - */ - this.handleUpEvent_ = options.handleUpEvent ? - options.handleUpEvent : handleUpEvent; - - /** - * @type {boolean} + * This method is used to determine if "down" events should be propagated to + * other interactions or should be stopped. + * + * The method receives the return code of the "handleDownEvent" function. + * + * By default this function is the "identity" function. It's overridden in + * child classes. + * + * @param {boolean} handled Was the event handled by the interaction? + * @return {boolean} Should the event be stopped? * @protected */ - this.handlingDownUpSequence = false; - - /** - * @type {!Object.} - * @private - */ - this.trackedPointers_ = {}; - - /** - * @type {Array.} - * @protected - */ - this.targetPointers = []; - -}; + shouldStopEvent(handled) { + return handled; + } +} inherits(PointerInteraction, Interaction); @@ -165,29 +206,6 @@ function isPointerDraggingEvent(mapBrowserEvent) { } -/** - * @param {module:ol/MapBrowserPointerEvent} mapBrowserEvent Event. - * @private - */ -PointerInteraction.prototype.updateTrackedPointers_ = function(mapBrowserEvent) { - if (isPointerDraggingEvent(mapBrowserEvent)) { - const event = mapBrowserEvent.pointerEvent; - - const id = event.pointerId.toString(); - if (mapBrowserEvent.type == MapBrowserEventType.POINTERUP) { - delete this.trackedPointers_[id]; - } else if (mapBrowserEvent.type == - MapBrowserEventType.POINTERDOWN) { - this.trackedPointers_[id] = event; - } else if (id in this.trackedPointers_) { - // update only when there was a pointerdown event for this pointer - this.trackedPointers_[id] = event; - } - this.targetPointers = getValues(this.trackedPointers_); - } -}; - - /** * 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 @@ -224,21 +242,4 @@ export function handleEvent(mapBrowserEvent) { } -/** - * This method is used to determine if "down" events should be propagated to - * other interactions or should be stopped. - * - * The method receives the return code of the "handleDownEvent" function. - * - * By default this function is the "identity" function. It's overridden in - * child classes. - * - * @param {boolean} handled Was the event handled by the interaction? - * @return {boolean} Should the event be stopped? - * @protected - */ -PointerInteraction.prototype.shouldStopEvent = function(handled) { - return handled; -}; - export default PointerInteraction; diff --git a/src/ol/interaction/Select.js b/src/ol/interaction/Select.js index d42403da7d..1fb87e9063 100644 --- a/src/ol/interaction/Select.js +++ b/src/ol/interaction/Select.js @@ -156,162 +156,223 @@ inherits(SelectEvent, Event); * @fires SelectEvent * @api */ -const Select = function(opt_options) { +class Select { + constructor(opt_options) { - Interaction.call(this, { - handleEvent: handleEvent - }); + Interaction.call(this, { + handleEvent: handleEvent + }); - const options = opt_options ? opt_options : {}; + const options = opt_options ? opt_options : {}; - /** - * @private - * @type {module:ol/events/condition~Condition} - */ - this.condition_ = options.condition ? options.condition : singleClick; + /** + * @private + * @type {module:ol/events/condition~Condition} + */ + this.condition_ = options.condition ? options.condition : singleClick; - /** - * @private - * @type {module:ol/events/condition~Condition} - */ - this.addCondition_ = options.addCondition ? options.addCondition : never; + /** + * @private + * @type {module:ol/events/condition~Condition} + */ + this.addCondition_ = options.addCondition ? options.addCondition : never; - /** - * @private - * @type {module:ol/events/condition~Condition} - */ - this.removeCondition_ = options.removeCondition ? options.removeCondition : never; + /** + * @private + * @type {module:ol/events/condition~Condition} + */ + this.removeCondition_ = options.removeCondition ? options.removeCondition : never; - /** - * @private - * @type {module:ol/events/condition~Condition} - */ - this.toggleCondition_ = options.toggleCondition ? options.toggleCondition : shiftKeyOnly; + /** + * @private + * @type {module:ol/events/condition~Condition} + */ + this.toggleCondition_ = options.toggleCondition ? options.toggleCondition : shiftKeyOnly; - /** - * @private - * @type {boolean} - */ - this.multi_ = options.multi ? options.multi : false; + /** + * @private + * @type {boolean} + */ + this.multi_ = options.multi ? options.multi : false; - /** - * @private - * @type {module:ol/interaction/Select~FilterFunction} - */ - this.filter_ = options.filter ? options.filter : TRUE; + /** + * @private + * @type {module:ol/interaction/Select~FilterFunction} + */ + this.filter_ = options.filter ? options.filter : TRUE; - /** - * @private - * @type {number} - */ - this.hitTolerance_ = options.hitTolerance ? options.hitTolerance : 0; + /** + * @private + * @type {number} + */ + this.hitTolerance_ = options.hitTolerance ? options.hitTolerance : 0; - const featureOverlay = new VectorLayer({ - source: new VectorSource({ - useSpatialIndex: false, - features: options.features, - wrapX: options.wrapX - }), - style: options.style ? options.style : - getDefaultStyleFunction(), - updateWhileAnimating: true, - updateWhileInteracting: true - }); + const featureOverlay = new VectorLayer({ + source: new VectorSource({ + useSpatialIndex: false, + features: options.features, + wrapX: options.wrapX + }), + style: options.style ? options.style : + getDefaultStyleFunction(), + updateWhileAnimating: true, + updateWhileInteracting: true + }); - /** - * @private - * @type {module:ol/layer/Vector} - */ - this.featureOverlay_ = featureOverlay; + /** + * @private + * @type {module:ol/layer/Vector} + */ + this.featureOverlay_ = featureOverlay; - /** @type {function(module:ol/layer/Layer): boolean} */ - let layerFilter; - if (options.layers) { - if (typeof options.layers === 'function') { - layerFilter = options.layers; + /** @type {function(module:ol/layer/Layer): boolean} */ + let layerFilter; + if (options.layers) { + if (typeof options.layers === 'function') { + layerFilter = options.layers; + } else { + const layers = options.layers; + layerFilter = function(layer) { + return includes(layers, layer); + }; + } } else { - const layers = options.layers; - layerFilter = function(layer) { - return includes(layers, layer); - }; + layerFilter = TRUE; } - } else { - layerFilter = TRUE; + + /** + * @private + * @type {function(module:ol/layer/Layer): boolean} + */ + this.layerFilter_ = layerFilter; + + /** + * An association between selected feature (key) + * and layer (value) + * @private + * @type {Object.} + */ + this.featureLayerAssociation_ = {}; + + const features = this.featureOverlay_.getSource().getFeaturesCollection(); + listen(features, CollectionEventType.ADD, + this.addFeature_, this); + listen(features, CollectionEventType.REMOVE, + this.removeFeature_, this); + } /** + * @param {module:ol/Feature|module:ol/render/Feature} feature Feature. + * @param {module:ol/layer/Layer} layer Layer. * @private - * @type {function(module:ol/layer/Layer): boolean} */ - this.layerFilter_ = layerFilter; + addFeatureLayerAssociation_(feature, layer) { + const key = getUid(feature); + this.featureLayerAssociation_[key] = layer; + } /** - * An association between selected feature (key) - * and layer (value) - * @private - * @type {Object.} + * Get the selected features. + * @return {module:ol/Collection.} Features collection. + * @api */ - this.featureLayerAssociation_ = {}; + getFeatures() { + return this.featureOverlay_.getSource().getFeaturesCollection(); + } - const features = this.featureOverlay_.getSource().getFeaturesCollection(); - listen(features, CollectionEventType.ADD, - this.addFeature_, this); - listen(features, CollectionEventType.REMOVE, - this.removeFeature_, this); + /** + * Returns the Hit-detection tolerance. + * @returns {number} Hit tolerance in pixels. + * @api + */ + getHitTolerance() { + return this.hitTolerance_; + } -}; + /** + * Returns the associated {@link module:ol/layer/Vector~Vector vectorlayer} of + * 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 {module:ol/Feature|module:ol/render/Feature} feature Feature + * @return {module:ol/layer/Vector} Layer. + * @api + */ + getLayer(feature) { + const key = getUid(feature); + return ( + /** @type {module:ol/layer/Vector} */ (this.featureLayerAssociation_[key]) + ); + } + + /** + * Hit-detection tolerance. Pixels inside the radius around the given position + * will be checked for features. This only works for the canvas renderer and + * not for WebGL. + * @param {number} hitTolerance Hit tolerance in pixels. + * @api + */ + setHitTolerance(hitTolerance) { + this.hitTolerance_ = hitTolerance; + } + + /** + * Remove the interaction from its current map, if any, and attach it to a new + * map, if any. Pass `null` to just remove the interaction from the current map. + * @param {module:ol/PluggableMap} map Map. + * @override + * @api + */ + setMap(map) { + const currentMap = this.getMap(); + const selectedFeatures = + this.featureOverlay_.getSource().getFeaturesCollection(); + if (currentMap) { + selectedFeatures.forEach(currentMap.unskipFeature.bind(currentMap)); + } + Interaction.prototype.setMap.call(this, map); + this.featureOverlay_.setMap(map); + if (map) { + selectedFeatures.forEach(map.skipFeature.bind(map)); + } + } + + /** + * @param {module:ol/Collection~CollectionEvent} evt Event. + * @private + */ + addFeature_(evt) { + const map = this.getMap(); + if (map) { + map.skipFeature(/** @type {module:ol/Feature} */ (evt.element)); + } + } + + /** + * @param {module:ol/Collection~CollectionEvent} evt Event. + * @private + */ + removeFeature_(evt) { + const map = this.getMap(); + if (map) { + map.unskipFeature(/** @type {module:ol/Feature} */ (evt.element)); + } + } + + /** + * @param {module:ol/Feature|module:ol/render/Feature} feature Feature. + * @private + */ + removeFeatureLayerAssociation_(feature) { + const key = getUid(feature); + delete this.featureLayerAssociation_[key]; + } +} inherits(Select, Interaction); -/** - * @param {module:ol/Feature|module:ol/render/Feature} feature Feature. - * @param {module:ol/layer/Layer} layer Layer. - * @private - */ -Select.prototype.addFeatureLayerAssociation_ = function(feature, layer) { - const key = getUid(feature); - this.featureLayerAssociation_[key] = layer; -}; - - -/** - * Get the selected features. - * @return {module:ol/Collection.} Features collection. - * @api - */ -Select.prototype.getFeatures = function() { - return this.featureOverlay_.getSource().getFeaturesCollection(); -}; - - -/** - * Returns the Hit-detection tolerance. - * @returns {number} Hit tolerance in pixels. - * @api - */ -Select.prototype.getHitTolerance = function() { - return this.hitTolerance_; -}; - - -/** - * Returns the associated {@link module:ol/layer/Vector~Vector vectorlayer} of - * 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 {module:ol/Feature|module:ol/render/Feature} feature Feature - * @return {module:ol/layer/Vector} Layer. - * @api - */ -Select.prototype.getLayer = function(feature) { - const key = getUid(feature); - return ( - /** @type {module:ol/layer/Vector} */ (this.featureLayerAssociation_[key]) - ); -}; - - /** * Handles the {@link module:ol/MapBrowserEvent map browser event} and may change the * selected state of features. @@ -405,40 +466,6 @@ function handleEvent(mapBrowserEvent) { } -/** - * Hit-detection tolerance. Pixels inside the radius around the given position - * will be checked for features. This only works for the canvas renderer and - * not for WebGL. - * @param {number} hitTolerance Hit tolerance in pixels. - * @api - */ -Select.prototype.setHitTolerance = function(hitTolerance) { - this.hitTolerance_ = hitTolerance; -}; - - -/** - * Remove the interaction from its current map, if any, and attach it to a new - * map, if any. Pass `null` to just remove the interaction from the current map. - * @param {module:ol/PluggableMap} map Map. - * @override - * @api - */ -Select.prototype.setMap = function(map) { - const currentMap = this.getMap(); - const selectedFeatures = - this.featureOverlay_.getSource().getFeaturesCollection(); - if (currentMap) { - selectedFeatures.forEach(currentMap.unskipFeature.bind(currentMap)); - } - Interaction.prototype.setMap.call(this, map); - this.featureOverlay_.setMap(map); - if (map) { - selectedFeatures.forEach(map.skipFeature.bind(map)); - } -}; - - /** * @return {module:ol/style/Style~StyleFunction} Styles. */ @@ -456,38 +483,4 @@ function getDefaultStyleFunction() { } -/** - * @param {module:ol/Collection~CollectionEvent} evt Event. - * @private - */ -Select.prototype.addFeature_ = function(evt) { - const map = this.getMap(); - if (map) { - map.skipFeature(/** @type {module:ol/Feature} */ (evt.element)); - } -}; - - -/** - * @param {module:ol/Collection~CollectionEvent} evt Event. - * @private - */ -Select.prototype.removeFeature_ = function(evt) { - const map = this.getMap(); - if (map) { - map.unskipFeature(/** @type {module:ol/Feature} */ (evt.element)); - } -}; - - -/** - * @param {module:ol/Feature|module:ol/render/Feature} feature Feature. - * @private - */ -Select.prototype.removeFeatureLayerAssociation_ = function(feature) { - const key = getUid(feature); - delete this.featureLayerAssociation_[key]; -}; - - export default Select; diff --git a/src/ol/interaction/Snap.js b/src/ol/interaction/Snap.js index f8eff9024d..85c9bff6fc 100644 --- a/src/ol/interaction/Snap.js +++ b/src/ol/interaction/Snap.js @@ -68,459 +68,386 @@ import RBush from '../structs/RBush.js'; * @param {module:ol/interaction/Snap~Options=} opt_options Options. * @api */ -const Snap = function(opt_options) { +class Snap { + constructor(opt_options) { - PointerInteraction.call(this, { - handleEvent: handleEvent, - handleDownEvent: TRUE, - handleUpEvent: handleUpEvent - }); - - const options = opt_options ? opt_options : {}; - - /** - * @type {module:ol/source/Vector} - * @private - */ - this.source_ = options.source ? options.source : null; - - /** - * @private - * @type {boolean} - */ - this.vertex_ = options.vertex !== undefined ? options.vertex : true; - - /** - * @private - * @type {boolean} - */ - this.edge_ = options.edge !== undefined ? options.edge : true; - - /** - * @type {module:ol/Collection.} - * @private - */ - this.features_ = options.features ? options.features : null; - - /** - * @type {Array.} - * @private - */ - this.featuresListenerKeys_ = []; - - /** - * @type {Object.} - * @private - */ - this.featureChangeListenerKeys_ = {}; - - /** - * Extents are preserved so indexed segment can be quickly removed - * when its feature geometry changes - * @type {Object.} - * @private - */ - this.indexedFeaturesExtents_ = {}; - - /** - * If a feature geometry changes while a pointer drag|move event occurs, the - * feature doesn't get updated right away. It will be at the next 'pointerup' - * event fired. - * @type {!Object.} - * @private - */ - this.pendingFeatures_ = {}; - - /** - * Used for distance sorting in sortByDistance_ - * @type {module:ol/coordinate~Coordinate} - * @private - */ - this.pixelCoordinate_ = null; - - /** - * @type {number} - * @private - */ - this.pixelTolerance_ = options.pixelTolerance !== undefined ? - options.pixelTolerance : 10; - - /** - * @type {function(module:ol/interaction/Snap~SegmentData, module:ol/interaction/Snap~SegmentData): number} - * @private - */ - this.sortByDistance_ = sortByDistance.bind(this); - - - /** - * Segment RTree for each layer - * @type {module:ol/structs/RBush.} - * @private - */ - this.rBush_ = new RBush(); - - - /** - * @const - * @private - * @type {Object.} - */ - this.SEGMENT_WRITERS_ = { - 'Point': this.writePointGeometry_, - 'LineString': this.writeLineStringGeometry_, - 'LinearRing': this.writeLineStringGeometry_, - 'Polygon': this.writePolygonGeometry_, - 'MultiPoint': this.writeMultiPointGeometry_, - 'MultiLineString': this.writeMultiLineStringGeometry_, - 'MultiPolygon': this.writeMultiPolygonGeometry_, - 'GeometryCollection': this.writeGeometryCollectionGeometry_, - 'Circle': this.writeCircleGeometry_ - }; -}; - -inherits(Snap, PointerInteraction); - - -/** - * Add a feature to the collection of features that we may snap to. - * @param {module:ol/Feature} feature Feature. - * @param {boolean=} opt_listen Whether to listen to the feature change or not - * Defaults to `true`. - * @api - */ -Snap.prototype.addFeature = function(feature, opt_listen) { - const register = opt_listen !== undefined ? opt_listen : true; - const feature_uid = getUid(feature); - const geometry = feature.getGeometry(); - if (geometry) { - const segmentWriter = this.SEGMENT_WRITERS_[geometry.getType()]; - if (segmentWriter) { - this.indexedFeaturesExtents_[feature_uid] = geometry.getExtent(createEmpty()); - segmentWriter.call(this, feature, geometry); - } - } - - if (register) { - this.featureChangeListenerKeys_[feature_uid] = listen( - feature, - EventType.CHANGE, - this.handleFeatureChange_, this); - } -}; - - -/** - * @param {module:ol/Feature} feature Feature. - * @private - */ -Snap.prototype.forEachFeatureAdd_ = function(feature) { - this.addFeature(feature); -}; - - -/** - * @param {module:ol/Feature} feature Feature. - * @private - */ -Snap.prototype.forEachFeatureRemove_ = function(feature) { - this.removeFeature(feature); -}; - - -/** - * @return {module:ol/Collection.|Array.} Features. - * @private - */ -Snap.prototype.getFeatures_ = function() { - let features; - if (this.features_) { - features = this.features_; - } else if (this.source_) { - features = this.source_.getFeatures(); - } - return ( - /** @type {!Array.|!module:ol/Collection.} */ (features) - ); -}; - - -/** - * @param {module:ol/source/Vector|module:ol/Collection~CollectionEvent} evt Event. - * @private - */ -Snap.prototype.handleFeatureAdd_ = function(evt) { - let feature; - if (evt instanceof VectorSourceEvent) { - feature = evt.feature; - } else if (evt instanceof CollectionEvent) { - feature = evt.element; - } - this.addFeature(/** @type {module:ol/Feature} */ (feature)); -}; - - -/** - * @param {module:ol/source/Vector|module:ol/Collection~CollectionEvent} evt Event. - * @private - */ -Snap.prototype.handleFeatureRemove_ = function(evt) { - let feature; - if (evt instanceof VectorSourceEvent) { - feature = evt.feature; - } else if (evt instanceof CollectionEvent) { - feature = evt.element; - } - this.removeFeature(/** @type {module:ol/Feature} */ (feature)); -}; - - -/** - * @param {module:ol/events/Event} evt Event. - * @private - */ -Snap.prototype.handleFeatureChange_ = function(evt) { - const feature = /** @type {module:ol/Feature} */ (evt.target); - if (this.handlingDownUpSequence) { - const uid = getUid(feature); - if (!(uid in this.pendingFeatures_)) { - this.pendingFeatures_[uid] = feature; - } - } else { - this.updateFeature_(feature); - } -}; - - -/** - * Remove a feature from the collection of features that we may snap to. - * @param {module:ol/Feature} feature Feature - * @param {boolean=} opt_unlisten Whether to unlisten to the feature change - * or not. Defaults to `true`. - * @api - */ -Snap.prototype.removeFeature = function(feature, opt_unlisten) { - const unregister = opt_unlisten !== undefined ? opt_unlisten : true; - const feature_uid = getUid(feature); - const extent = this.indexedFeaturesExtents_[feature_uid]; - if (extent) { - const rBush = this.rBush_; - const nodesToRemove = []; - rBush.forEachInExtent(extent, function(node) { - if (feature === node.feature) { - nodesToRemove.push(node); - } + PointerInteraction.call(this, { + handleEvent: handleEvent, + handleDownEvent: TRUE, + handleUpEvent: handleUpEvent }); - for (let i = nodesToRemove.length - 1; i >= 0; --i) { - rBush.remove(nodesToRemove[i]); + + const options = opt_options ? opt_options : {}; + + /** + * @type {module:ol/source/Vector} + * @private + */ + this.source_ = options.source ? options.source : null; + + /** + * @private + * @type {boolean} + */ + this.vertex_ = options.vertex !== undefined ? options.vertex : true; + + /** + * @private + * @type {boolean} + */ + this.edge_ = options.edge !== undefined ? options.edge : true; + + /** + * @type {module:ol/Collection.} + * @private + */ + this.features_ = options.features ? options.features : null; + + /** + * @type {Array.} + * @private + */ + this.featuresListenerKeys_ = []; + + /** + * @type {Object.} + * @private + */ + this.featureChangeListenerKeys_ = {}; + + /** + * Extents are preserved so indexed segment can be quickly removed + * when its feature geometry changes + * @type {Object.} + * @private + */ + this.indexedFeaturesExtents_ = {}; + + /** + * If a feature geometry changes while a pointer drag|move event occurs, the + * feature doesn't get updated right away. It will be at the next 'pointerup' + * event fired. + * @type {!Object.} + * @private + */ + this.pendingFeatures_ = {}; + + /** + * Used for distance sorting in sortByDistance_ + * @type {module:ol/coordinate~Coordinate} + * @private + */ + this.pixelCoordinate_ = null; + + /** + * @type {number} + * @private + */ + this.pixelTolerance_ = options.pixelTolerance !== undefined ? + options.pixelTolerance : 10; + + /** + * @type {function(module:ol/interaction/Snap~SegmentData, module:ol/interaction/Snap~SegmentData): number} + * @private + */ + this.sortByDistance_ = sortByDistance.bind(this); + + + /** + * Segment RTree for each layer + * @type {module:ol/structs/RBush.} + * @private + */ + this.rBush_ = new RBush(); + + + /** + * @const + * @private + * @type {Object.} + */ + this.SEGMENT_WRITERS_ = { + 'Point': this.writePointGeometry_, + 'LineString': this.writeLineStringGeometry_, + 'LinearRing': this.writeLineStringGeometry_, + 'Polygon': this.writePolygonGeometry_, + 'MultiPoint': this.writeMultiPointGeometry_, + 'MultiLineString': this.writeMultiLineStringGeometry_, + 'MultiPolygon': this.writeMultiPolygonGeometry_, + 'GeometryCollection': this.writeGeometryCollectionGeometry_, + 'Circle': this.writeCircleGeometry_ + }; + } + + /** + * Add a feature to the collection of features that we may snap to. + * @param {module:ol/Feature} feature Feature. + * @param {boolean=} opt_listen Whether to listen to the feature change or not + * Defaults to `true`. + * @api + */ + addFeature(feature, opt_listen) { + const register = opt_listen !== undefined ? opt_listen : true; + const feature_uid = getUid(feature); + const geometry = feature.getGeometry(); + if (geometry) { + const segmentWriter = this.SEGMENT_WRITERS_[geometry.getType()]; + if (segmentWriter) { + this.indexedFeaturesExtents_[feature_uid] = geometry.getExtent(createEmpty()); + segmentWriter.call(this, feature, geometry); + } + } + + if (register) { + this.featureChangeListenerKeys_[feature_uid] = listen( + feature, + EventType.CHANGE, + this.handleFeatureChange_, this); } } - if (unregister) { - unlistenByKey(this.featureChangeListenerKeys_[feature_uid]); - delete this.featureChangeListenerKeys_[feature_uid]; + /** + * @param {module:ol/Feature} feature Feature. + * @private + */ + forEachFeatureAdd_(feature) { + this.addFeature(feature); } -}; - -/** - * @inheritDoc - */ -Snap.prototype.setMap = function(map) { - const currentMap = this.getMap(); - const keys = this.featuresListenerKeys_; - const features = this.getFeatures_(); - - if (currentMap) { - keys.forEach(unlistenByKey); - keys.length = 0; - features.forEach(this.forEachFeatureRemove_.bind(this)); + /** + * @param {module:ol/Feature} feature Feature. + * @private + */ + forEachFeatureRemove_(feature) { + this.removeFeature(feature); } - PointerInteraction.prototype.setMap.call(this, map); - if (map) { + /** + * @return {module:ol/Collection.|Array.} Features. + * @private + */ + getFeatures_() { + let features; if (this.features_) { - keys.push( - listen(this.features_, CollectionEventType.ADD, - this.handleFeatureAdd_, this), - listen(this.features_, CollectionEventType.REMOVE, - this.handleFeatureRemove_, this) - ); + features = this.features_; } else if (this.source_) { - keys.push( - listen(this.source_, VectorEventType.ADDFEATURE, - this.handleFeatureAdd_, this), - listen(this.source_, VectorEventType.REMOVEFEATURE, - this.handleFeatureRemove_, this) - ); + features = this.source_.getFeatures(); } - features.forEach(this.forEachFeatureAdd_.bind(this)); - } -}; - - -/** - * @inheritDoc - */ -Snap.prototype.shouldStopEvent = FALSE; - - -/** - * @param {module:ol~Pixel} pixel Pixel - * @param {module:ol/coordinate~Coordinate} pixelCoordinate Coordinate - * @param {module:ol/PluggableMap} map Map. - * @return {module:ol/interaction/Snap~Result} Snap result - */ -Snap.prototype.snapTo = function(pixel, pixelCoordinate, map) { - - const lowerLeft = map.getCoordinateFromPixel( - [pixel[0] - this.pixelTolerance_, pixel[1] + this.pixelTolerance_]); - const upperRight = map.getCoordinateFromPixel( - [pixel[0] + this.pixelTolerance_, pixel[1] - this.pixelTolerance_]); - const box = boundingExtent([lowerLeft, upperRight]); - - let segments = this.rBush_.getInExtent(box); - - // If snapping on vertices only, don't consider circles - if (this.vertex_ && !this.edge_) { - segments = segments.filter(function(segment) { - return segment.feature.getGeometry().getType() !== - GeometryType.CIRCLE; - }); + return ( + /** @type {!Array.|!module:ol/Collection.} */ (features) + ); } - let snappedToVertex = false; - let snapped = false; - let vertex = null; - let vertexPixel = null; - let dist, pixel1, pixel2, squaredDist1, squaredDist2; - if (segments.length > 0) { - this.pixelCoordinate_ = pixelCoordinate; - segments.sort(this.sortByDistance_); - const closestSegment = segments[0].segment; - const isCircle = segments[0].feature.getGeometry().getType() === - GeometryType.CIRCLE; + /** + * @param {module:ol/source/Vector|module:ol/Collection~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 {module:ol/Feature} */ (feature)); + } + + /** + * @param {module:ol/source/Vector|module:ol/Collection~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 {module:ol/Feature} */ (feature)); + } + + /** + * @param {module:ol/events/Event} evt Event. + * @private + */ + handleFeatureChange_(evt) { + const feature = /** @type {module:ol/Feature} */ (evt.target); + if (this.handlingDownUpSequence) { + const uid = getUid(feature); + if (!(uid in this.pendingFeatures_)) { + this.pendingFeatures_[uid] = feature; + } + } else { + this.updateFeature_(feature); + } + } + + /** + * Remove a feature from the collection of features that we may snap to. + * @param {module:ol/Feature} feature Feature + * @param {boolean=} opt_unlisten Whether to unlisten to the feature change + * or not. Defaults to `true`. + * @api + */ + removeFeature(feature, opt_unlisten) { + const unregister = opt_unlisten !== undefined ? opt_unlisten : true; + const feature_uid = getUid(feature); + const extent = this.indexedFeaturesExtents_[feature_uid]; + if (extent) { + const rBush = this.rBush_; + const nodesToRemove = []; + rBush.forEachInExtent(extent, function(node) { + if (feature === node.feature) { + nodesToRemove.push(node); + } + }); + for (let i = nodesToRemove.length - 1; i >= 0; --i) { + rBush.remove(nodesToRemove[i]); + } + } + + if (unregister) { + unlistenByKey(this.featureChangeListenerKeys_[feature_uid]); + delete this.featureChangeListenerKeys_[feature_uid]; + } + } + + /** + * @inheritDoc + */ + setMap(map) { + const currentMap = this.getMap(); + const keys = this.featuresListenerKeys_; + const features = this.getFeatures_(); + + if (currentMap) { + keys.forEach(unlistenByKey); + keys.length = 0; + features.forEach(this.forEachFeatureRemove_.bind(this)); + } + PointerInteraction.prototype.setMap.call(this, map); + + if (map) { + if (this.features_) { + keys.push( + listen(this.features_, CollectionEventType.ADD, + this.handleFeatureAdd_, this), + listen(this.features_, CollectionEventType.REMOVE, + this.handleFeatureRemove_, this) + ); + } else if (this.source_) { + keys.push( + listen(this.source_, VectorEventType.ADDFEATURE, + this.handleFeatureAdd_, this), + listen(this.source_, VectorEventType.REMOVEFEATURE, + this.handleFeatureRemove_, this) + ); + } + features.forEach(this.forEachFeatureAdd_.bind(this)); + } + } + + /** + * @param {module:ol~Pixel} pixel Pixel + * @param {module:ol/coordinate~Coordinate} pixelCoordinate Coordinate + * @param {module:ol/PluggableMap} map Map. + * @return {module:ol/interaction/Snap~Result} Snap result + */ + snapTo(pixel, pixelCoordinate, map) { + + const lowerLeft = map.getCoordinateFromPixel( + [pixel[0] - this.pixelTolerance_, pixel[1] + this.pixelTolerance_]); + const upperRight = map.getCoordinateFromPixel( + [pixel[0] + this.pixelTolerance_, pixel[1] - this.pixelTolerance_]); + const box = boundingExtent([lowerLeft, upperRight]); + + let segments = this.rBush_.getInExtent(box); + + // If snapping on vertices only, don't consider circles if (this.vertex_ && !this.edge_) { - pixel1 = map.getPixelFromCoordinate(closestSegment[0]); - pixel2 = map.getPixelFromCoordinate(closestSegment[1]); - squaredDist1 = squaredCoordinateDistance(pixel, pixel1); - squaredDist2 = squaredCoordinateDistance(pixel, pixel2); - dist = Math.sqrt(Math.min(squaredDist1, squaredDist2)); - snappedToVertex = dist <= this.pixelTolerance_; - if (snappedToVertex) { - snapped = true; - vertex = squaredDist1 > squaredDist2 ? closestSegment[1] : closestSegment[0]; + segments = segments.filter(function(segment) { + return segment.feature.getGeometry().getType() !== + GeometryType.CIRCLE; + }); + } + + let snappedToVertex = false; + let snapped = false; + let vertex = null; + let vertexPixel = null; + let dist, pixel1, pixel2, squaredDist1, squaredDist2; + if (segments.length > 0) { + this.pixelCoordinate_ = pixelCoordinate; + segments.sort(this.sortByDistance_); + const closestSegment = segments[0].segment; + const isCircle = segments[0].feature.getGeometry().getType() === + GeometryType.CIRCLE; + if (this.vertex_ && !this.edge_) { + pixel1 = map.getPixelFromCoordinate(closestSegment[0]); + pixel2 = map.getPixelFromCoordinate(closestSegment[1]); + squaredDist1 = squaredCoordinateDistance(pixel, pixel1); + squaredDist2 = squaredCoordinateDistance(pixel, pixel2); + dist = Math.sqrt(Math.min(squaredDist1, squaredDist2)); + snappedToVertex = dist <= this.pixelTolerance_; + if (snappedToVertex) { + snapped = true; + vertex = squaredDist1 > squaredDist2 ? closestSegment[1] : closestSegment[0]; + vertexPixel = map.getPixelFromCoordinate(vertex); + } + } else if (this.edge_) { + if (isCircle) { + vertex = closestOnCircle(pixelCoordinate, + /** @type {module:ol/geom/Circle} */ (segments[0].feature.getGeometry())); + } else { + vertex = closestOnSegment(pixelCoordinate, closestSegment); + } vertexPixel = map.getPixelFromCoordinate(vertex); - } - } else if (this.edge_) { - if (isCircle) { - vertex = closestOnCircle(pixelCoordinate, - /** @type {module:ol/geom/Circle} */ (segments[0].feature.getGeometry())); - } else { - vertex = closestOnSegment(pixelCoordinate, closestSegment); - } - vertexPixel = map.getPixelFromCoordinate(vertex); - if (coordinateDistance(pixel, vertexPixel) <= this.pixelTolerance_) { - snapped = true; - if (this.vertex_ && !isCircle) { - pixel1 = map.getPixelFromCoordinate(closestSegment[0]); - pixel2 = map.getPixelFromCoordinate(closestSegment[1]); - squaredDist1 = squaredCoordinateDistance(vertexPixel, pixel1); - squaredDist2 = squaredCoordinateDistance(vertexPixel, pixel2); - dist = Math.sqrt(Math.min(squaredDist1, squaredDist2)); - snappedToVertex = dist <= this.pixelTolerance_; - if (snappedToVertex) { - vertex = squaredDist1 > squaredDist2 ? closestSegment[1] : closestSegment[0]; - vertexPixel = map.getPixelFromCoordinate(vertex); + if (coordinateDistance(pixel, vertexPixel) <= this.pixelTolerance_) { + snapped = true; + if (this.vertex_ && !isCircle) { + pixel1 = map.getPixelFromCoordinate(closestSegment[0]); + pixel2 = map.getPixelFromCoordinate(closestSegment[1]); + squaredDist1 = squaredCoordinateDistance(vertexPixel, pixel1); + squaredDist2 = squaredCoordinateDistance(vertexPixel, pixel2); + dist = Math.sqrt(Math.min(squaredDist1, squaredDist2)); + snappedToVertex = dist <= this.pixelTolerance_; + if (snappedToVertex) { + vertex = squaredDist1 > squaredDist2 ? closestSegment[1] : closestSegment[0]; + vertexPixel = map.getPixelFromCoordinate(vertex); + } } } } + if (snapped) { + vertexPixel = [Math.round(vertexPixel[0]), Math.round(vertexPixel[1])]; + } } - if (snapped) { - vertexPixel = [Math.round(vertexPixel[0]), Math.round(vertexPixel[1])]; - } + return ( + /** @type {module:ol/interaction/Snap~Result} */ ({ + snapped: snapped, + vertex: vertex, + vertexPixel: vertexPixel + }) + ); } - return ( - /** @type {module:ol/interaction/Snap~Result} */ ({ - snapped: snapped, - vertex: vertex, - vertexPixel: vertexPixel - }) - ); -}; - -/** - * @param {module:ol/Feature} feature Feature - * @private - */ -Snap.prototype.updateFeature_ = function(feature) { - this.removeFeature(feature, false); - this.addFeature(feature, false); -}; - - -/** - * @param {module:ol/Feature} feature Feature - * @param {module:ol/geom/Circle} geometry Geometry. - * @private - */ -Snap.prototype.writeCircleGeometry_ = function(feature, geometry) { - const polygon = fromCircle(geometry); - const coordinates = polygon.getCoordinates()[0]; - for (let i = 0, ii = coordinates.length - 1; i < ii; ++i) { - const segment = coordinates.slice(i, i + 2); - const segmentData = /** @type {module:ol/interaction/Snap~SegmentData} */ ({ - feature: feature, - segment: segment - }); - this.rBush_.insert(boundingExtent(segment), segmentData); + /** + * @param {module:ol/Feature} feature Feature + * @private + */ + updateFeature_(feature) { + this.removeFeature(feature, false); + this.addFeature(feature, false); } -}; - -/** - * @param {module:ol/Feature} feature Feature - * @param {module:ol/geom/GeometryCollection} geometry Geometry. - * @private - */ -Snap.prototype.writeGeometryCollectionGeometry_ = function(feature, geometry) { - const geometries = geometry.getGeometriesArray(); - for (let i = 0; i < geometries.length; ++i) { - const segmentWriter = this.SEGMENT_WRITERS_[geometries[i].getType()]; - if (segmentWriter) { - segmentWriter.call(this, feature, geometries[i]); - } - } -}; - - -/** - * @param {module:ol/Feature} feature Feature - * @param {module:ol/geom/LineString} geometry Geometry. - * @private - */ -Snap.prototype.writeLineStringGeometry_ = function(feature, geometry) { - const coordinates = geometry.getCoordinates(); - for (let i = 0, ii = coordinates.length - 1; i < ii; ++i) { - const segment = coordinates.slice(i, i + 2); - const segmentData = /** @type {module:ol/interaction/Snap~SegmentData} */ ({ - feature: feature, - segment: segment - }); - this.rBush_.insert(boundingExtent(segment), segmentData); - } -}; - - -/** - * @param {module:ol/Feature} feature Feature - * @param {module:ol/geom/MultiLineString} geometry Geometry. - * @private - */ -Snap.prototype.writeMultiLineStringGeometry_ = function(feature, geometry) { - const lines = geometry.getCoordinates(); - for (let j = 0, jj = lines.length; j < jj; ++j) { - const coordinates = lines[j]; + /** + * @param {module:ol/Feature} feature Feature + * @param {module:ol/geom/Circle} geometry Geometry. + * @private + */ + writeCircleGeometry_(feature, geometry) { + const polygon = fromCircle(geometry); + const coordinates = polygon.getCoordinates()[0]; for (let i = 0, ii = coordinates.length - 1; i < ii; ++i) { const segment = coordinates.slice(i, i + 2); const segmentData = /** @type {module:ol/interaction/Snap~SegmentData} */ ({ @@ -530,36 +457,120 @@ Snap.prototype.writeMultiLineStringGeometry_ = function(feature, geometry) { this.rBush_.insert(boundingExtent(segment), segmentData); } } -}; + /** + * @param {module:ol/Feature} feature Feature + * @param {module:ol/geom/GeometryCollection} geometry Geometry. + * @private + */ + writeGeometryCollectionGeometry_(feature, geometry) { + const geometries = geometry.getGeometriesArray(); + for (let i = 0; i < geometries.length; ++i) { + const segmentWriter = this.SEGMENT_WRITERS_[geometries[i].getType()]; + if (segmentWriter) { + segmentWriter.call(this, feature, geometries[i]); + } + } + } -/** - * @param {module:ol/Feature} feature Feature - * @param {module:ol/geom/MultiPoint} geometry Geometry. - * @private - */ -Snap.prototype.writeMultiPointGeometry_ = function(feature, geometry) { - const points = geometry.getCoordinates(); - for (let i = 0, ii = points.length; i < ii; ++i) { - const coordinates = points[i]; + /** + * @param {module:ol/Feature} feature Feature + * @param {module:ol/geom/LineString} geometry Geometry. + * @private + */ + writeLineStringGeometry_(feature, geometry) { + const coordinates = geometry.getCoordinates(); + for (let i = 0, ii = coordinates.length - 1; i < ii; ++i) { + const segment = coordinates.slice(i, i + 2); + const segmentData = /** @type {module:ol/interaction/Snap~SegmentData} */ ({ + feature: feature, + segment: segment + }); + this.rBush_.insert(boundingExtent(segment), segmentData); + } + } + + /** + * @param {module:ol/Feature} feature Feature + * @param {module:ol/geom/MultiLineString} geometry Geometry. + * @private + */ + writeMultiLineStringGeometry_(feature, geometry) { + const lines = geometry.getCoordinates(); + for (let j = 0, jj = lines.length; j < jj; ++j) { + const coordinates = lines[j]; + for (let i = 0, ii = coordinates.length - 1; i < ii; ++i) { + const segment = coordinates.slice(i, i + 2); + const segmentData = /** @type {module:ol/interaction/Snap~SegmentData} */ ({ + feature: feature, + segment: segment + }); + this.rBush_.insert(boundingExtent(segment), segmentData); + } + } + } + + /** + * @param {module:ol/Feature} feature Feature + * @param {module:ol/geom/MultiPoint} geometry Geometry. + * @private + */ + writeMultiPointGeometry_(feature, geometry) { + const points = geometry.getCoordinates(); + for (let i = 0, ii = points.length; i < ii; ++i) { + const coordinates = points[i]; + const segmentData = /** @type {module:ol/interaction/Snap~SegmentData} */ ({ + feature: feature, + segment: [coordinates, coordinates] + }); + this.rBush_.insert(geometry.getExtent(), segmentData); + } + } + + /** + * @param {module:ol/Feature} feature Feature + * @param {module:ol/geom/MultiPolygon} geometry Geometry. + * @private + */ + writeMultiPolygonGeometry_(feature, geometry) { + const polygons = geometry.getCoordinates(); + for (let k = 0, kk = polygons.length; k < kk; ++k) { + const rings = polygons[k]; + for (let j = 0, jj = rings.length; j < jj; ++j) { + const coordinates = rings[j]; + for (let i = 0, ii = coordinates.length - 1; i < ii; ++i) { + const segment = coordinates.slice(i, i + 2); + const segmentData = /** @type {module:ol/interaction/Snap~SegmentData} */ ({ + feature: feature, + segment: segment + }); + this.rBush_.insert(boundingExtent(segment), segmentData); + } + } + } + } + + /** + * @param {module:ol/Feature} feature Feature + * @param {module:ol/geom/Point} geometry Geometry. + * @private + */ + writePointGeometry_(feature, geometry) { + const coordinates = geometry.getCoordinates(); const segmentData = /** @type {module:ol/interaction/Snap~SegmentData} */ ({ feature: feature, segment: [coordinates, coordinates] }); this.rBush_.insert(geometry.getExtent(), segmentData); } -}; - -/** - * @param {module:ol/Feature} feature Feature - * @param {module:ol/geom/MultiPolygon} geometry Geometry. - * @private - */ -Snap.prototype.writeMultiPolygonGeometry_ = function(feature, geometry) { - const polygons = geometry.getCoordinates(); - for (let k = 0, kk = polygons.length; k < kk; ++k) { - const rings = polygons[k]; + /** + * @param {module:ol/Feature} feature Feature + * @param {module:ol/geom/Polygon} geometry Geometry. + * @private + */ + writePolygonGeometry_(feature, geometry) { + const rings = geometry.getCoordinates(); for (let j = 0, jj = rings.length; j < jj; ++j) { const coordinates = rings[j]; for (let i = 0, ii = coordinates.length - 1; i < ii; ++i) { @@ -572,43 +583,15 @@ Snap.prototype.writeMultiPolygonGeometry_ = function(feature, geometry) { } } } -}; +} + +inherits(Snap, PointerInteraction); /** - * @param {module:ol/Feature} feature Feature - * @param {module:ol/geom/Point} geometry Geometry. - * @private + * @inheritDoc */ -Snap.prototype.writePointGeometry_ = function(feature, geometry) { - const coordinates = geometry.getCoordinates(); - const segmentData = /** @type {module:ol/interaction/Snap~SegmentData} */ ({ - feature: feature, - segment: [coordinates, coordinates] - }); - this.rBush_.insert(geometry.getExtent(), segmentData); -}; - - -/** - * @param {module:ol/Feature} feature Feature - * @param {module:ol/geom/Polygon} geometry Geometry. - * @private - */ -Snap.prototype.writePolygonGeometry_ = function(feature, geometry) { - const rings = geometry.getCoordinates(); - for (let j = 0, jj = rings.length; j < jj; ++j) { - const coordinates = rings[j]; - for (let i = 0, ii = coordinates.length - 1; i < ii; ++i) { - const segment = coordinates.slice(i, i + 2); - const segmentData = /** @type {module:ol/interaction/Snap~SegmentData} */ ({ - feature: feature, - segment: segment - }); - this.rBush_.insert(boundingExtent(segment), segmentData); - } - } -}; +Snap.prototype.shouldStopEvent = FALSE; /** diff --git a/src/ol/interaction/Translate.js b/src/ol/interaction/Translate.js index 2e0c71a09f..31ab39650e 100644 --- a/src/ol/interaction/Translate.js +++ b/src/ol/interaction/Translate.js @@ -96,68 +96,143 @@ inherits(TranslateEvent, Event); * @param {module:ol/interaction/Translate~Options=} opt_options Options. * @api */ -const Translate = function(opt_options) { - PointerInteraction.call(this, { - handleDownEvent: handleDownEvent, - handleDragEvent: handleDragEvent, - handleMoveEvent: handleMoveEvent, - handleUpEvent: handleUpEvent - }); +class Translate { + constructor(opt_options) { + PointerInteraction.call(this, { + handleDownEvent: handleDownEvent, + handleDragEvent: handleDragEvent, + handleMoveEvent: handleMoveEvent, + handleUpEvent: handleUpEvent + }); - const options = opt_options ? opt_options : {}; + const options = opt_options ? opt_options : {}; - /** - * The last position we translated to. - * @type {module:ol/coordinate~Coordinate} - * @private - */ - this.lastCoordinate_ = null; + /** + * The last position we translated to. + * @type {module:ol/coordinate~Coordinate} + * @private + */ + this.lastCoordinate_ = null; - /** - * @type {module:ol/Collection.} - * @private - */ - this.features_ = options.features !== undefined ? options.features : null; + /** + * @type {module:ol/Collection.} + * @private + */ + this.features_ = options.features !== undefined ? options.features : null; - /** @type {function(module:ol/layer/Layer): boolean} */ - let layerFilter; - if (options.layers) { - if (typeof options.layers === 'function') { - layerFilter = options.layers; + /** @type {function(module:ol/layer/Layer): boolean} */ + let layerFilter; + if (options.layers) { + if (typeof options.layers === 'function') { + layerFilter = options.layers; + } else { + const layers = options.layers; + layerFilter = function(layer) { + return includes(layers, layer); + }; + } } else { - const layers = options.layers; - layerFilter = function(layer) { - return includes(layers, layer); - }; + layerFilter = TRUE; } - } else { - layerFilter = TRUE; + + /** + * @private + * @type {function(module:ol/layer/Layer): boolean} + */ + this.layerFilter_ = layerFilter; + + /** + * @private + * @type {number} + */ + this.hitTolerance_ = options.hitTolerance ? options.hitTolerance : 0; + + /** + * @type {module:ol/Feature} + * @private + */ + this.lastFeature_ = null; + + listen(this, + getChangeEventType(InteractionProperty.ACTIVE), + this.handleActiveChanged_, this); + + } + + /** + * Tests to see if the given coordinates intersects any of our selected + * features. + * @param {module:ol~Pixel} pixel Pixel coordinate to test for intersection. + * @param {module:ol/PluggableMap} map Map to test the intersection on. + * @return {module:ol/Feature} Returns the feature found at the specified pixel + * coordinates. + * @private + */ + featuresAtPixel_(pixel, map) { + return map.forEachFeatureAtPixel(pixel, + function(feature) { + if (!this.features_ || includes(this.features_.getArray(), feature)) { + return feature; + } + }.bind(this), { + layerFilter: this.layerFilter_, + hitTolerance: this.hitTolerance_ + }); + } + + /** + * Returns the Hit-detection tolerance. + * @returns {number} Hit tolerance in pixels. + * @api + */ + getHitTolerance() { + return this.hitTolerance_; + } + + /** + * Hit-detection tolerance. Pixels inside the radius around the given position + * will be checked for features. This only works for the canvas renderer and + * not for WebGL. + * @param {number} hitTolerance Hit tolerance in pixels. + * @api + */ + setHitTolerance(hitTolerance) { + this.hitTolerance_ = hitTolerance; + } + + /** + * @inheritDoc + */ + setMap(map) { + const oldMap = this.getMap(); + PointerInteraction.prototype.setMap.call(this, map); + this.updateState_(oldMap); } /** * @private - * @type {function(module:ol/layer/Layer): boolean} */ - this.layerFilter_ = layerFilter; + handleActiveChanged_() { + this.updateState_(null); + } /** - * @private - * @type {number} - */ - this.hitTolerance_ = options.hitTolerance ? options.hitTolerance : 0; - - /** - * @type {module:ol/Feature} + * @param {module:ol/PluggableMap} oldMap Old map. * @private */ - this.lastFeature_ = null; - - listen(this, - getChangeEventType(InteractionProperty.ACTIVE), - this.handleActiveChanged_, this); - -}; + updateState_(oldMap) { + let map = this.getMap(); + const active = this.getActive(); + if (!map || !active) { + map = map || oldMap; + if (map) { + const elem = map.getViewport(); + elem.classList.remove('ol-grab', 'ol-grabbing'); + } + } + } +} inherits(Translate, PointerInteraction); @@ -252,83 +327,4 @@ function handleMoveEvent(event) { } -/** - * Tests to see if the given coordinates intersects any of our selected - * features. - * @param {module:ol~Pixel} pixel Pixel coordinate to test for intersection. - * @param {module:ol/PluggableMap} map Map to test the intersection on. - * @return {module:ol/Feature} Returns the feature found at the specified pixel - * coordinates. - * @private - */ -Translate.prototype.featuresAtPixel_ = function(pixel, map) { - return map.forEachFeatureAtPixel(pixel, - function(feature) { - if (!this.features_ || includes(this.features_.getArray(), feature)) { - return feature; - } - }.bind(this), { - layerFilter: this.layerFilter_, - hitTolerance: this.hitTolerance_ - }); -}; - - -/** - * Returns the Hit-detection tolerance. - * @returns {number} Hit tolerance in pixels. - * @api - */ -Translate.prototype.getHitTolerance = function() { - return this.hitTolerance_; -}; - - -/** - * Hit-detection tolerance. Pixels inside the radius around the given position - * will be checked for features. This only works for the canvas renderer and - * not for WebGL. - * @param {number} hitTolerance Hit tolerance in pixels. - * @api - */ -Translate.prototype.setHitTolerance = function(hitTolerance) { - this.hitTolerance_ = hitTolerance; -}; - - -/** - * @inheritDoc - */ -Translate.prototype.setMap = function(map) { - const oldMap = this.getMap(); - PointerInteraction.prototype.setMap.call(this, map); - this.updateState_(oldMap); -}; - - -/** - * @private - */ -Translate.prototype.handleActiveChanged_ = function() { - this.updateState_(null); -}; - - -/** - * @param {module:ol/PluggableMap} oldMap Old map. - * @private - */ -Translate.prototype.updateState_ = function(oldMap) { - let map = this.getMap(); - const active = this.getActive(); - if (!map || !active) { - map = map || oldMap; - if (map) { - const elem = map.getViewport(); - elem.classList.remove('ol-grab', 'ol-grabbing'); - } - } -}; - - export default Translate; diff --git a/src/ol/layer/Base.js b/src/ol/layer/Base.js index 71c414b432..0218288a4f 100644 --- a/src/ol/layer/Base.js +++ b/src/ol/layer/Base.js @@ -37,232 +37,219 @@ import {assign} from '../obj.js'; * @param {module:ol/layer/Base~Options} options Layer options. * @api */ -const BaseLayer = function(options) { +class BaseLayer { + constructor(options) { - BaseObject.call(this); + BaseObject.call(this); - /** - * @type {Object.} - */ - const properties = assign({}, options); - properties[LayerProperty.OPACITY] = - options.opacity !== undefined ? options.opacity : 1; - properties[LayerProperty.VISIBLE] = - options.visible !== undefined ? options.visible : true; - properties[LayerProperty.Z_INDEX] = - options.zIndex !== undefined ? options.zIndex : 0; - properties[LayerProperty.MAX_RESOLUTION] = - options.maxResolution !== undefined ? options.maxResolution : Infinity; - properties[LayerProperty.MIN_RESOLUTION] = - options.minResolution !== undefined ? options.minResolution : 0; + /** + * @type {Object.} + */ + const properties = assign({}, options); + properties[LayerProperty.OPACITY] = + options.opacity !== undefined ? options.opacity : 1; + properties[LayerProperty.VISIBLE] = + options.visible !== undefined ? options.visible : true; + properties[LayerProperty.Z_INDEX] = + options.zIndex !== undefined ? options.zIndex : 0; + properties[LayerProperty.MAX_RESOLUTION] = + options.maxResolution !== undefined ? options.maxResolution : Infinity; + properties[LayerProperty.MIN_RESOLUTION] = + options.minResolution !== undefined ? options.minResolution : 0; - this.setProperties(properties); + this.setProperties(properties); - /** - * @type {module:ol/layer/Layer~State} - * @private - */ - this.state_ = /** @type {module:ol/layer/Layer~State} */ ({ - layer: /** @type {module:ol/layer/Layer} */ (this), - managed: true - }); + /** + * @type {module:ol/layer/Layer~State} + * @private + */ + this.state_ = /** @type {module:ol/layer/Layer~State} */ ({ + layer: /** @type {module:ol/layer/Layer} */ (this), + managed: true + }); - /** - * The layer type. - * @type {module:ol/LayerType} - * @protected; - */ - this.type; + /** + * The layer type. + * @type {module:ol/LayerType} + * @protected; + */ + this.type; -}; + } + + /** + * Get the layer type (used when creating a layer renderer). + * @return {module:ol/LayerType} The layer type. + */ + getType() { + return this.type; + } + + /** + * @return {module:ol/layer/Layer~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(); + this.state_.maxResolution = this.getMaxResolution(); + this.state_.minResolution = Math.max(this.getMinResolution(), 0); + + return this.state_; + } + + /** + * @abstract + * @param {Array.=} opt_array Array of layers (to be + * modified in place). + * @return {Array.} Array of layers. + */ + getLayersArray(opt_array) {} + + /** + * @abstract + * @param {Array.=} opt_states Optional list of layer + * states (to be modified in place). + * @return {Array.} List of layer states. + */ + getLayerStatesArray(opt_states) {} + + /** + * Return the {@link module:ol/extent~Extent extent} of the layer or `undefined` if it + * will be visible regardless of extent. + * @return {module:ol/extent~Extent|undefined} The layer extent. + * @observable + * @api + */ + getExtent() { + return ( + /** @type {module:ol/extent~Extent|undefined} */ (this.get(LayerProperty.EXTENT)) + ); + } + + /** + * 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 + */ + 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 + */ + getOpacity() { + return /** @type {number} */ (this.get(LayerProperty.OPACITY)); + } + + /** + * @abstract + * @return {module:ol/source/State} Source state. + */ + getSourceState() {} + + /** + * 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 + */ + 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 {module:ol/extent~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 + */ + 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 + */ + 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 + */ + 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 + */ + 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 + */ + setZIndex(zindex) { + this.set(LayerProperty.Z_INDEX, zindex); + } +} inherits(BaseLayer, BaseObject); -/** - * Get the layer type (used when creating a layer renderer). - * @return {module:ol/LayerType} The layer type. - */ -BaseLayer.prototype.getType = function() { - return this.type; -}; - - -/** - * @return {module:ol/layer/Layer~State} Layer state. - */ -BaseLayer.prototype.getLayerState = function() { - 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(); - this.state_.maxResolution = this.getMaxResolution(); - this.state_.minResolution = Math.max(this.getMinResolution(), 0); - - return this.state_; -}; - - -/** - * @abstract - * @param {Array.=} opt_array Array of layers (to be - * modified in place). - * @return {Array.} Array of layers. - */ -BaseLayer.prototype.getLayersArray = function(opt_array) {}; - - -/** - * @abstract - * @param {Array.=} opt_states Optional list of layer - * states (to be modified in place). - * @return {Array.} List of layer states. - */ -BaseLayer.prototype.getLayerStatesArray = function(opt_states) {}; - - -/** - * Return the {@link module:ol/extent~Extent extent} of the layer or `undefined` if it - * will be visible regardless of extent. - * @return {module:ol/extent~Extent|undefined} The layer extent. - * @observable - * @api - */ -BaseLayer.prototype.getExtent = function() { - return ( - /** @type {module:ol/extent~Extent|undefined} */ (this.get(LayerProperty.EXTENT)) - ); -}; - - -/** - * Return the maximum resolution of the layer. - * @return {number} The maximum resolution of the layer. - * @observable - * @api - */ -BaseLayer.prototype.getMaxResolution = function() { - 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 - */ -BaseLayer.prototype.getMinResolution = function() { - 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 - */ -BaseLayer.prototype.getOpacity = function() { - return /** @type {number} */ (this.get(LayerProperty.OPACITY)); -}; - - -/** - * @abstract - * @return {module:ol/source/State} Source state. - */ -BaseLayer.prototype.getSourceState = function() {}; - - -/** - * Return the visibility of the layer (`true` or `false`). - * @return {boolean} The visibility of the layer. - * @observable - * @api - */ -BaseLayer.prototype.getVisible = function() { - 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 - */ -BaseLayer.prototype.getZIndex = function() { - 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 {module:ol/extent~Extent|undefined} extent The extent of the layer. - * @observable - * @api - */ -BaseLayer.prototype.setExtent = function(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 - */ -BaseLayer.prototype.setMaxResolution = function(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 - */ -BaseLayer.prototype.setMinResolution = function(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 - */ -BaseLayer.prototype.setOpacity = function(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 - */ -BaseLayer.prototype.setVisible = function(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 - */ -BaseLayer.prototype.setZIndex = function(zindex) { - this.set(LayerProperty.Z_INDEX, zindex); -}; export default BaseLayer; diff --git a/src/ol/layer/Group.js b/src/ol/layer/Group.js index bb39810349..411c078d61 100644 --- a/src/ol/layer/Group.js +++ b/src/ol/layer/Group.js @@ -51,198 +51,192 @@ const Property = { * @param {module:ol/layer/Group~Options=} opt_options Layer options. * @api */ -const LayerGroup = function(opt_options) { +class LayerGroup { + constructor(opt_options) { - const options = opt_options || {}; - const baseOptions = /** @type {module:ol/layer/Group~Options} */ (assign({}, options)); - delete baseOptions.layers; + const options = opt_options || {}; + const baseOptions = /** @type {module:ol/layer/Group~Options} */ (assign({}, options)); + delete baseOptions.layers; - let layers = options.layers; + let layers = options.layers; - BaseLayer.call(this, baseOptions); + BaseLayer.call(this, baseOptions); - /** - * @private - * @type {Array.} - */ - this.layersListenerKeys_ = []; + /** + * @private + * @type {Array.} + */ + this.layersListenerKeys_ = []; - /** - * @private - * @type {Object.>} - */ - this.listenerKeys_ = {}; + /** + * @private + * @type {Object.>} + */ + this.listenerKeys_ = {}; - listen(this, - getChangeEventType(Property.LAYERS), - this.handleLayersChanged_, this); + listen(this, + getChangeEventType(Property.LAYERS), + this.handleLayersChanged_, this); - if (layers) { - if (Array.isArray(layers)) { - layers = new Collection(layers.slice(), {unique: true}); + if (layers) { + if (Array.isArray(layers)) { + layers = new Collection(layers.slice(), {unique: true}); + } else { + assert(layers instanceof Collection, + 43); // Expected `layers` to be an array or a `Collection` + layers = layers; + } } else { - assert(layers instanceof Collection, - 43); // Expected `layers` to be an array or a `Collection` - layers = layers; + layers = new Collection(undefined, {unique: true}); } - } else { - layers = new Collection(undefined, {unique: true}); + + this.setLayers(layers); + } - this.setLayers(layers); + /** + * @private + */ + handleLayerChange_() { + this.changed(); + } -}; + /** + * @param {module:ol/events/Event} event Event. + * @private + */ + handleLayersChanged_(event) { + this.layersListenerKeys_.forEach(unlistenByKey); + this.layersListenerKeys_.length = 0; + + const layers = this.getLayers(); + this.layersListenerKeys_.push( + listen(layers, CollectionEventType.ADD, this.handleLayersAdd_, this), + listen(layers, CollectionEventType.REMOVE, this.handleLayersRemove_, this) + ); + + for (const id in this.listenerKeys_) { + this.listenerKeys_[id].forEach(unlistenByKey); + } + clear(this.listenerKeys_); + + const layersArray = layers.getArray(); + for (let i = 0, ii = layersArray.length; i < ii; i++) { + const layer = layersArray[i]; + this.listenerKeys_[getUid(layer).toString()] = [ + listen(layer, ObjectEventType.PROPERTYCHANGE, this.handleLayerChange_, this), + listen(layer, EventType.CHANGE, this.handleLayerChange_, this) + ]; + } + + this.changed(); + } + + /** + * @param {module:ol/Collection~CollectionEvent} collectionEvent CollectionEvent. + * @private + */ + handleLayersAdd_(collectionEvent) { + const layer = /** @type {module:ol/layer/Base} */ (collectionEvent.element); + const key = getUid(layer).toString(); + this.listenerKeys_[key] = [ + listen(layer, ObjectEventType.PROPERTYCHANGE, this.handleLayerChange_, this), + listen(layer, EventType.CHANGE, this.handleLayerChange_, this) + ]; + this.changed(); + } + + /** + * @param {module:ol/Collection~CollectionEvent} collectionEvent CollectionEvent. + * @private + */ + handleLayersRemove_(collectionEvent) { + const layer = /** @type {module:ol/layer/Base} */ (collectionEvent.element); + const key = getUid(layer).toString(); + this.listenerKeys_[key].forEach(unlistenByKey); + delete this.listenerKeys_[key]; + this.changed(); + } + + /** + * Returns the {@link module:ol/Collection collection} of {@link module:ol/layer/Layer~Layer layers} + * in this group. + * @return {!module:ol/Collection.} Collection of + * {@link module:ol/layer/Base layers} that are part of this group. + * @observable + * @api + */ + getLayers() { + return ( + /** @type {!module:ol/Collection.} */ (this.get(Property.LAYERS)) + ); + } + + /** + * Set the {@link module:ol/Collection collection} of {@link module:ol/layer/Layer~Layer layers} + * in this group. + * @param {!module:ol/Collection.} layers Collection of + * {@link module:ol/layer/Base layers} that are part of this group. + * @observable + * @api + */ + setLayers(layers) { + this.set(Property.LAYERS, layers); + } + + /** + * @inheritDoc + */ + getLayersArray(opt_array) { + const array = opt_array !== undefined ? opt_array : []; + this.getLayers().forEach(function(layer) { + layer.getLayersArray(array); + }); + return array; + } + + /** + * @inheritDoc + */ + getLayerStatesArray(opt_states) { + const states = opt_states !== undefined ? opt_states : []; + + const pos = states.length; + + this.getLayers().forEach(function(layer) { + layer.getLayerStatesArray(states); + }); + + const ownLayerState = this.getLayerState(); + for (let i = pos, ii = states.length; i < ii; i++) { + const layerState = states[i]; + layerState.opacity *= ownLayerState.opacity; + layerState.visible = layerState.visible && ownLayerState.visible; + layerState.maxResolution = Math.min( + layerState.maxResolution, ownLayerState.maxResolution); + layerState.minResolution = Math.max( + layerState.minResolution, ownLayerState.minResolution); + if (ownLayerState.extent !== undefined) { + if (layerState.extent !== undefined) { + layerState.extent = getIntersection(layerState.extent, ownLayerState.extent); + } else { + layerState.extent = ownLayerState.extent; + } + } + } + + return states; + } + + /** + * @inheritDoc + */ + getSourceState() { + return SourceState.READY; + } +} inherits(LayerGroup, BaseLayer); -/** - * @private - */ -LayerGroup.prototype.handleLayerChange_ = function() { - this.changed(); -}; - - -/** - * @param {module:ol/events/Event} event Event. - * @private - */ -LayerGroup.prototype.handleLayersChanged_ = function(event) { - this.layersListenerKeys_.forEach(unlistenByKey); - this.layersListenerKeys_.length = 0; - - const layers = this.getLayers(); - this.layersListenerKeys_.push( - listen(layers, CollectionEventType.ADD, this.handleLayersAdd_, this), - listen(layers, CollectionEventType.REMOVE, this.handleLayersRemove_, this) - ); - - for (const id in this.listenerKeys_) { - this.listenerKeys_[id].forEach(unlistenByKey); - } - clear(this.listenerKeys_); - - const layersArray = layers.getArray(); - for (let i = 0, ii = layersArray.length; i < ii; i++) { - const layer = layersArray[i]; - this.listenerKeys_[getUid(layer).toString()] = [ - listen(layer, ObjectEventType.PROPERTYCHANGE, this.handleLayerChange_, this), - listen(layer, EventType.CHANGE, this.handleLayerChange_, this) - ]; - } - - this.changed(); -}; - - -/** - * @param {module:ol/Collection~CollectionEvent} collectionEvent CollectionEvent. - * @private - */ -LayerGroup.prototype.handleLayersAdd_ = function(collectionEvent) { - const layer = /** @type {module:ol/layer/Base} */ (collectionEvent.element); - const key = getUid(layer).toString(); - this.listenerKeys_[key] = [ - listen(layer, ObjectEventType.PROPERTYCHANGE, this.handleLayerChange_, this), - listen(layer, EventType.CHANGE, this.handleLayerChange_, this) - ]; - this.changed(); -}; - - -/** - * @param {module:ol/Collection~CollectionEvent} collectionEvent CollectionEvent. - * @private - */ -LayerGroup.prototype.handleLayersRemove_ = function(collectionEvent) { - const layer = /** @type {module:ol/layer/Base} */ (collectionEvent.element); - const key = getUid(layer).toString(); - this.listenerKeys_[key].forEach(unlistenByKey); - delete this.listenerKeys_[key]; - this.changed(); -}; - - -/** - * Returns the {@link module:ol/Collection collection} of {@link module:ol/layer/Layer~Layer layers} - * in this group. - * @return {!module:ol/Collection.} Collection of - * {@link module:ol/layer/Base layers} that are part of this group. - * @observable - * @api - */ -LayerGroup.prototype.getLayers = function() { - return ( - /** @type {!module:ol/Collection.} */ (this.get(Property.LAYERS)) - ); -}; - - -/** - * Set the {@link module:ol/Collection collection} of {@link module:ol/layer/Layer~Layer layers} - * in this group. - * @param {!module:ol/Collection.} layers Collection of - * {@link module:ol/layer/Base layers} that are part of this group. - * @observable - * @api - */ -LayerGroup.prototype.setLayers = function(layers) { - this.set(Property.LAYERS, layers); -}; - - -/** - * @inheritDoc - */ -LayerGroup.prototype.getLayersArray = function(opt_array) { - const array = opt_array !== undefined ? opt_array : []; - this.getLayers().forEach(function(layer) { - layer.getLayersArray(array); - }); - return array; -}; - - -/** - * @inheritDoc - */ -LayerGroup.prototype.getLayerStatesArray = function(opt_states) { - const states = opt_states !== undefined ? opt_states : []; - - const pos = states.length; - - this.getLayers().forEach(function(layer) { - layer.getLayerStatesArray(states); - }); - - const ownLayerState = this.getLayerState(); - for (let i = pos, ii = states.length; i < ii; i++) { - const layerState = states[i]; - layerState.opacity *= ownLayerState.opacity; - layerState.visible = layerState.visible && ownLayerState.visible; - layerState.maxResolution = Math.min( - layerState.maxResolution, ownLayerState.maxResolution); - layerState.minResolution = Math.max( - layerState.minResolution, ownLayerState.minResolution); - if (ownLayerState.extent !== undefined) { - if (layerState.extent !== undefined) { - layerState.extent = getIntersection(layerState.extent, ownLayerState.extent); - } else { - layerState.extent = ownLayerState.extent; - } - } - } - - return states; -}; - - -/** - * @inheritDoc - */ -LayerGroup.prototype.getSourceState = function() { - return SourceState.READY; -}; - export default LayerGroup; diff --git a/src/ol/layer/Heatmap.js b/src/ol/layer/Heatmap.js index 50b02c1633..5eafc51eee 100644 --- a/src/ol/layer/Heatmap.js +++ b/src/ol/layer/Heatmap.js @@ -73,97 +73,215 @@ const DEFAULT_GRADIENT = ['#00f', '#0ff', '#0f0', '#ff0', '#f00']; * @param {module:ol/layer/Heatmap~Options=} opt_options Options. * @api */ -const Heatmap = function(opt_options) { - const options = opt_options ? opt_options : {}; +class Heatmap { + constructor(opt_options) { + const options = opt_options ? opt_options : {}; - const baseOptions = assign({}, options); + const baseOptions = assign({}, options); - delete baseOptions.gradient; - delete baseOptions.radius; - delete baseOptions.blur; - delete baseOptions.shadow; - delete baseOptions.weight; - VectorLayer.call(this, /** @type {module:ol/layer/Vector~Options} */ (baseOptions)); + delete baseOptions.gradient; + delete baseOptions.radius; + delete baseOptions.blur; + delete baseOptions.shadow; + delete baseOptions.weight; + VectorLayer.call(this, /** @type {module:ol/layer/Vector~Options} */ (baseOptions)); - /** - * @private - * @type {Uint8ClampedArray} - */ - this.gradient_ = null; + /** + * @private + * @type {Uint8ClampedArray} + */ + this.gradient_ = null; - /** - * @private - * @type {number} - */ - this.shadow_ = options.shadow !== undefined ? options.shadow : 250; + /** + * @private + * @type {number} + */ + this.shadow_ = options.shadow !== undefined ? options.shadow : 250; - /** - * @private - * @type {string|undefined} - */ - this.circleImage_ = undefined; + /** + * @private + * @type {string|undefined} + */ + this.circleImage_ = undefined; - /** - * @private - * @type {Array.>} - */ - this.styleCache_ = null; + /** + * @private + * @type {Array.>} + */ + this.styleCache_ = null; - listen(this, - getChangeEventType(Property.GRADIENT), - this.handleGradientChanged_, this); + listen(this, + getChangeEventType(Property.GRADIENT), + this.handleGradientChanged_, this); - this.setGradient(options.gradient ? options.gradient : DEFAULT_GRADIENT); + this.setGradient(options.gradient ? options.gradient : DEFAULT_GRADIENT); - this.setBlur(options.blur !== undefined ? options.blur : 15); + this.setBlur(options.blur !== undefined ? options.blur : 15); - this.setRadius(options.radius !== undefined ? options.radius : 8); + this.setRadius(options.radius !== undefined ? options.radius : 8); - listen(this, - getChangeEventType(Property.BLUR), - this.handleStyleChanged_, this); - listen(this, - getChangeEventType(Property.RADIUS), - this.handleStyleChanged_, this); + listen(this, + getChangeEventType(Property.BLUR), + this.handleStyleChanged_, this); + listen(this, + getChangeEventType(Property.RADIUS), + this.handleStyleChanged_, this); - this.handleStyleChanged_(); + this.handleStyleChanged_(); - const weight = options.weight ? options.weight : 'weight'; - let weightFunction; - if (typeof weight === 'string') { - weightFunction = function(feature) { - return feature.get(weight); - }; - } else { - weightFunction = weight; + const weight = options.weight ? options.weight : 'weight'; + let weightFunction; + if (typeof weight === 'string') { + weightFunction = function(feature) { + return feature.get(weight); + }; + } else { + weightFunction = weight; + } + + this.setStyle(function(feature, resolution) { + const weight = weightFunction(feature); + const opacity = weight !== undefined ? clamp(weight, 0, 1) : 1; + // cast to 8 bits + const index = (255 * opacity) | 0; + let style = this.styleCache_[index]; + if (!style) { + style = [ + new Style({ + image: new Icon({ + opacity: opacity, + src: this.circleImage_ + }) + }) + ]; + this.styleCache_[index] = style; + } + return style; + }.bind(this)); + + // For performance reasons, don't sort the features before rendering. + // The render order is not relevant for a heatmap representation. + this.setRenderOrder(null); + + listen(this, RenderEventType.RENDER, this.handleRender_, this); } - this.setStyle(function(feature, resolution) { - const weight = weightFunction(feature); - const opacity = weight !== undefined ? clamp(weight, 0, 1) : 1; - // cast to 8 bits - const index = (255 * opacity) | 0; - let style = this.styleCache_[index]; - if (!style) { - style = [ - new Style({ - image: new Icon({ - opacity: opacity, - src: this.circleImage_ - }) - }) - ]; - this.styleCache_[index] = style; + /** + * @return {string} Data URL for a circle. + * @private + */ + createCircle_() { + const radius = this.getRadius(); + const blur = this.getBlur(); + const halfSize = radius + blur + 1; + const size = 2 * halfSize; + const context = createCanvasContext2D(size, size); + context.shadowOffsetX = context.shadowOffsetY = this.shadow_; + context.shadowBlur = blur; + context.shadowColor = '#000'; + context.beginPath(); + const center = halfSize - this.shadow_; + context.arc(center, center, radius, 0, Math.PI * 2, true); + context.fill(); + return context.canvas.toDataURL(); + } + + /** + * Return the blur size in pixels. + * @return {number} Blur size in pixels. + * @api + * @observable + */ + getBlur() { + return /** @type {number} */ (this.get(Property.BLUR)); + } + + /** + * Return the gradient colors as array of strings. + * @return {Array.} Colors. + * @api + * @observable + */ + getGradient() { + return /** @type {Array.} */ (this.get(Property.GRADIENT)); + } + + /** + * Return the size of the radius in pixels. + * @return {number} Radius size in pixel. + * @api + * @observable + */ + getRadius() { + return /** @type {number} */ (this.get(Property.RADIUS)); + } + + /** + * @private + */ + handleGradientChanged_() { + this.gradient_ = createGradient(this.getGradient()); + } + + /** + * @private + */ + handleStyleChanged_() { + this.circleImage_ = this.createCircle_(); + this.styleCache_ = new Array(256); + this.changed(); + } + + /** + * @param {module:ol/render/Event} event Post compose event + * @private + */ + handleRender_(event) { + const context = event.context; + const canvas = context.canvas; + const image = context.getImageData(0, 0, canvas.width, canvas.height); + const view8 = image.data; + for (let i = 0, ii = view8.length; i < ii; i += 4) { + const alpha = view8[i + 3] * 4; + if (alpha) { + view8[i] = this.gradient_[alpha]; + view8[i + 1] = this.gradient_[alpha + 1]; + view8[i + 2] = this.gradient_[alpha + 2]; + } } - return style; - }.bind(this)); + context.putImageData(image, 0, 0); + } - // For performance reasons, don't sort the features before rendering. - // The render order is not relevant for a heatmap representation. - this.setRenderOrder(null); + /** + * Set the blur size in pixels. + * @param {number} blur Blur size in pixels. + * @api + * @observable + */ + setBlur(blur) { + this.set(Property.BLUR, blur); + } - listen(this, RenderEventType.RENDER, this.handleRender_, this); -}; + /** + * Set the gradient colors as array of strings. + * @param {Array.} colors Gradient. + * @api + * @observable + */ + setGradient(colors) { + this.set(Property.GRADIENT, colors); + } + + /** + * Set the size of the radius in pixels. + * @param {number} radius Radius size in pixel. + * @api + * @observable + */ + setRadius(radius) { + this.set(Property.RADIUS, radius); + } +} inherits(Heatmap, VectorLayer); @@ -191,129 +309,4 @@ const createGradient = function(colors) { }; -/** - * @return {string} Data URL for a circle. - * @private - */ -Heatmap.prototype.createCircle_ = function() { - const radius = this.getRadius(); - const blur = this.getBlur(); - const halfSize = radius + blur + 1; - const size = 2 * halfSize; - const context = createCanvasContext2D(size, size); - context.shadowOffsetX = context.shadowOffsetY = this.shadow_; - context.shadowBlur = blur; - context.shadowColor = '#000'; - context.beginPath(); - const center = halfSize - this.shadow_; - context.arc(center, center, radius, 0, Math.PI * 2, true); - context.fill(); - return context.canvas.toDataURL(); -}; - - -/** - * Return the blur size in pixels. - * @return {number} Blur size in pixels. - * @api - * @observable - */ -Heatmap.prototype.getBlur = function() { - return /** @type {number} */ (this.get(Property.BLUR)); -}; - - -/** - * Return the gradient colors as array of strings. - * @return {Array.} Colors. - * @api - * @observable - */ -Heatmap.prototype.getGradient = function() { - return /** @type {Array.} */ (this.get(Property.GRADIENT)); -}; - - -/** - * Return the size of the radius in pixels. - * @return {number} Radius size in pixel. - * @api - * @observable - */ -Heatmap.prototype.getRadius = function() { - return /** @type {number} */ (this.get(Property.RADIUS)); -}; - - -/** - * @private - */ -Heatmap.prototype.handleGradientChanged_ = function() { - this.gradient_ = createGradient(this.getGradient()); -}; - - -/** - * @private - */ -Heatmap.prototype.handleStyleChanged_ = function() { - this.circleImage_ = this.createCircle_(); - this.styleCache_ = new Array(256); - this.changed(); -}; - - -/** - * @param {module:ol/render/Event} event Post compose event - * @private - */ -Heatmap.prototype.handleRender_ = function(event) { - const context = event.context; - const canvas = context.canvas; - const image = context.getImageData(0, 0, canvas.width, canvas.height); - const view8 = image.data; - for (let i = 0, ii = view8.length; i < ii; i += 4) { - const alpha = view8[i + 3] * 4; - if (alpha) { - view8[i] = this.gradient_[alpha]; - view8[i + 1] = this.gradient_[alpha + 1]; - view8[i + 2] = this.gradient_[alpha + 2]; - } - } - context.putImageData(image, 0, 0); -}; - - -/** - * Set the blur size in pixels. - * @param {number} blur Blur size in pixels. - * @api - * @observable - */ -Heatmap.prototype.setBlur = function(blur) { - this.set(Property.BLUR, blur); -}; - - -/** - * Set the gradient colors as array of strings. - * @param {Array.} colors Gradient. - * @api - * @observable - */ -Heatmap.prototype.setGradient = function(colors) { - this.set(Property.GRADIENT, colors); -}; - - -/** - * Set the size of the radius in pixels. - * @param {number} radius Radius size in pixel. - * @api - * @observable - */ -Heatmap.prototype.setRadius = function(radius) { - this.set(Property.RADIUS, radius); -}; - export default Heatmap; diff --git a/src/ol/layer/Layer.js b/src/ol/layer/Layer.js index 8654a63550..d3a2a80aef 100644 --- a/src/ol/layer/Layer.js +++ b/src/ol/layer/Layer.js @@ -66,42 +66,153 @@ import SourceState from '../source/State.js'; * @param {module:ol/layer/Layer~Options} options Layer options. * @api */ -const Layer = function(options) { +class Layer { + constructor(options) { - const baseOptions = assign({}, options); - delete baseOptions.source; + const baseOptions = assign({}, options); + delete baseOptions.source; - BaseLayer.call(this, /** @type {module:ol/layer/Base~Options} */ (baseOptions)); + BaseLayer.call(this, /** @type {module:ol/layer/Base~Options} */ (baseOptions)); - /** - * @private - * @type {?module:ol/events~EventsKey} - */ - this.mapPrecomposeKey_ = null; + /** + * @private + * @type {?module:ol/events~EventsKey} + */ + this.mapPrecomposeKey_ = null; - /** - * @private - * @type {?module:ol/events~EventsKey} - */ - this.mapRenderKey_ = null; + /** + * @private + * @type {?module:ol/events~EventsKey} + */ + this.mapRenderKey_ = null; - /** - * @private - * @type {?module:ol/events~EventsKey} - */ - this.sourceChangeKey_ = null; + /** + * @private + * @type {?module:ol/events~EventsKey} + */ + this.sourceChangeKey_ = null; - if (options.map) { - this.setMap(options.map); + if (options.map) { + this.setMap(options.map); + } + + listen(this, + getChangeEventType(LayerProperty.SOURCE), + this.handleSourcePropertyChange_, this); + + const source = options.source ? options.source : null; + this.setSource(source); } - listen(this, - getChangeEventType(LayerProperty.SOURCE), - this.handleSourcePropertyChange_, this); + /** + * @inheritDoc + */ + getLayersArray(opt_array) { + const array = opt_array ? opt_array : []; + array.push(this); + return array; + } - const source = options.source ? options.source : null; - this.setSource(source); -}; + /** + * @inheritDoc + */ + getLayerStatesArray(opt_states) { + const states = opt_states ? opt_states : []; + states.push(this.getLayerState()); + return states; + } + + /** + * Get the layer source. + * @return {module:ol/source/Source} The layer source (or `null` if not yet set). + * @observable + * @api + */ + getSource() { + const source = this.get(LayerProperty.SOURCE); + return ( + /** @type {module:ol/source/Source} */ (source) || null + ); + } + + /** + * @inheritDoc + */ + getSourceState() { + const source = this.getSource(); + return !source ? SourceState.UNDEFINED : source.getState(); + } + + /** + * @private + */ + handleSourceChange_() { + this.changed(); + } + + /** + * @private + */ + handleSourcePropertyChange_() { + if (this.sourceChangeKey_) { + unlistenByKey(this.sourceChangeKey_); + this.sourceChangeKey_ = null; + } + const source = this.getSource(); + if (source) { + this.sourceChangeKey_ = listen(source, + EventType.CHANGE, this.handleSourceChange_, this); + } + this.changed(); + } + + /** + * Sets the layer to be rendered on top of other layers on a map. The map will + * not manage this layer in its layers collection, and the callback in + * {@link module:ol/Map#forEachLayerAtPixel} will receive `null` as layer. This + * is useful for temporary layers. To remove an unmanaged layer from the map, + * use `#setMap(null)`. + * + * To add the layer to a map and have it managed by the map, use + * {@link module:ol/Map#addLayer} instead. + * @param {module:ol/PluggableMap} map Map. + * @api + */ + setMap(map) { + if (this.mapPrecomposeKey_) { + unlistenByKey(this.mapPrecomposeKey_); + this.mapPrecomposeKey_ = null; + } + if (!map) { + this.changed(); + } + if (this.mapRenderKey_) { + unlistenByKey(this.mapRenderKey_); + this.mapRenderKey_ = null; + } + if (map) { + this.mapPrecomposeKey_ = listen(map, RenderEventType.PRECOMPOSE, function(evt) { + const layerState = this.getLayerState(); + layerState.managed = false; + layerState.zIndex = Infinity; + evt.frameState.layerStatesArray.push(layerState); + evt.frameState.layerStates[getUid(this)] = layerState; + }, this); + this.mapRenderKey_ = listen(this, EventType.CHANGE, map.render, map); + this.changed(); + } + } + + /** + * Set the layer source. + * @param {module:ol/source/Source} source The layer source. + * @observable + * @api + */ + setSource(source) { + this.set(LayerProperty.SOURCE, source); + } +} inherits(Layer, BaseLayer); @@ -120,119 +231,4 @@ export function visibleAtResolution(layerState, resolution) { } -/** - * @inheritDoc - */ -Layer.prototype.getLayersArray = function(opt_array) { - const array = opt_array ? opt_array : []; - array.push(this); - return array; -}; - - -/** - * @inheritDoc - */ -Layer.prototype.getLayerStatesArray = function(opt_states) { - const states = opt_states ? opt_states : []; - states.push(this.getLayerState()); - return states; -}; - - -/** - * Get the layer source. - * @return {module:ol/source/Source} The layer source (or `null` if not yet set). - * @observable - * @api - */ -Layer.prototype.getSource = function() { - const source = this.get(LayerProperty.SOURCE); - return ( - /** @type {module:ol/source/Source} */ (source) || null - ); -}; - - -/** - * @inheritDoc - */ -Layer.prototype.getSourceState = function() { - const source = this.getSource(); - return !source ? SourceState.UNDEFINED : source.getState(); -}; - - -/** - * @private - */ -Layer.prototype.handleSourceChange_ = function() { - this.changed(); -}; - - -/** - * @private - */ -Layer.prototype.handleSourcePropertyChange_ = function() { - if (this.sourceChangeKey_) { - unlistenByKey(this.sourceChangeKey_); - this.sourceChangeKey_ = null; - } - const source = this.getSource(); - if (source) { - this.sourceChangeKey_ = listen(source, - EventType.CHANGE, this.handleSourceChange_, this); - } - this.changed(); -}; - - -/** - * Sets the layer to be rendered on top of other layers on a map. The map will - * not manage this layer in its layers collection, and the callback in - * {@link module:ol/Map#forEachLayerAtPixel} will receive `null` as layer. This - * is useful for temporary layers. To remove an unmanaged layer from the map, - * use `#setMap(null)`. - * - * To add the layer to a map and have it managed by the map, use - * {@link module:ol/Map#addLayer} instead. - * @param {module:ol/PluggableMap} map Map. - * @api - */ -Layer.prototype.setMap = function(map) { - if (this.mapPrecomposeKey_) { - unlistenByKey(this.mapPrecomposeKey_); - this.mapPrecomposeKey_ = null; - } - if (!map) { - this.changed(); - } - if (this.mapRenderKey_) { - unlistenByKey(this.mapRenderKey_); - this.mapRenderKey_ = null; - } - if (map) { - this.mapPrecomposeKey_ = listen(map, RenderEventType.PRECOMPOSE, function(evt) { - const layerState = this.getLayerState(); - layerState.managed = false; - layerState.zIndex = Infinity; - evt.frameState.layerStatesArray.push(layerState); - evt.frameState.layerStates[getUid(this)] = layerState; - }, this); - this.mapRenderKey_ = listen(this, EventType.CHANGE, map.render, map); - this.changed(); - } -}; - - -/** - * Set the layer source. - * @param {module:ol/source/Source} source The layer source. - * @observable - * @api - */ -Layer.prototype.setSource = function(source) { - this.set(LayerProperty.SOURCE, source); -}; export default Layer; diff --git a/src/ol/layer/Tile.js b/src/ol/layer/Tile.js index fa0529039f..5cfa7a9935 100644 --- a/src/ol/layer/Tile.js +++ b/src/ol/layer/Tile.js @@ -45,42 +45,73 @@ import {assign} from '../obj.js'; * @param {module:ol/layer/Tile~Options=} opt_options Tile layer options. * @api */ -const TileLayer = function(opt_options) { - const options = opt_options ? opt_options : {}; +class TileLayer { + constructor(opt_options) { + const options = opt_options ? opt_options : {}; - const baseOptions = assign({}, options); + const baseOptions = assign({}, options); - delete baseOptions.preload; - delete baseOptions.useInterimTilesOnError; - Layer.call(this, /** @type {module:ol/layer/Layer~Options} */ (baseOptions)); + delete baseOptions.preload; + delete baseOptions.useInterimTilesOnError; + Layer.call(this, /** @type {module:ol/layer/Layer~Options} */ (baseOptions)); - this.setPreload(options.preload !== undefined ? options.preload : 0); - this.setUseInterimTilesOnError(options.useInterimTilesOnError !== undefined ? - options.useInterimTilesOnError : true); + this.setPreload(options.preload !== undefined ? options.preload : 0); + this.setUseInterimTilesOnError(options.useInterimTilesOnError !== undefined ? + options.useInterimTilesOnError : true); - /** - * The layer type. - * @protected - * @type {module:ol/LayerType} - */ - this.type = LayerType.TILE; + /** + * The layer type. + * @protected + * @type {module:ol/LayerType} + */ + this.type = LayerType.TILE; -}; + } + + /** + * Return the level as number to which we will preload tiles up to. + * @return {number} The level to preload tiles up to. + * @observable + * @api + */ + getPreload() { + return /** @type {number} */ (this.get(TileProperty.PRELOAD)); + } + + /** + * Set the level as number to which we will preload tiles up to. + * @param {number} preload The level to preload tiles up to. + * @observable + * @api + */ + setPreload(preload) { + this.set(TileProperty.PRELOAD, preload); + } + + /** + * Whether we use interim tiles on error. + * @return {boolean} Use interim tiles on error. + * @observable + * @api + */ + getUseInterimTilesOnError() { + return /** @type {boolean} */ (this.get(TileProperty.USE_INTERIM_TILES_ON_ERROR)); + } + + /** + * Set whether we use interim tiles on error. + * @param {boolean} useInterimTilesOnError Use interim tiles on error. + * @observable + * @api + */ + setUseInterimTilesOnError(useInterimTilesOnError) { + this.set(TileProperty.USE_INTERIM_TILES_ON_ERROR, useInterimTilesOnError); + } +} inherits(TileLayer, Layer); -/** - * Return the level as number to which we will preload tiles up to. - * @return {number} The level to preload tiles up to. - * @observable - * @api - */ -TileLayer.prototype.getPreload = function() { - return /** @type {number} */ (this.get(TileProperty.PRELOAD)); -}; - - /** * Return the associated {@link module:ol/source/Tile tilesource} of the layer. * @function @@ -90,35 +121,4 @@ TileLayer.prototype.getPreload = function() { TileLayer.prototype.getSource; -/** - * Set the level as number to which we will preload tiles up to. - * @param {number} preload The level to preload tiles up to. - * @observable - * @api - */ -TileLayer.prototype.setPreload = function(preload) { - this.set(TileProperty.PRELOAD, preload); -}; - - -/** - * Whether we use interim tiles on error. - * @return {boolean} Use interim tiles on error. - * @observable - * @api - */ -TileLayer.prototype.getUseInterimTilesOnError = function() { - return /** @type {boolean} */ (this.get(TileProperty.USE_INTERIM_TILES_ON_ERROR)); -}; - - -/** - * Set whether we use interim tiles on error. - * @param {boolean} useInterimTilesOnError Use interim tiles on error. - * @observable - * @api - */ -TileLayer.prototype.setUseInterimTilesOnError = function(useInterimTilesOnError) { - this.set(TileProperty.USE_INTERIM_TILES_ON_ERROR, useInterimTilesOnError); -}; export default TileLayer; diff --git a/src/ol/layer/Vector.js b/src/ol/layer/Vector.js index 7d1d56c304..2cc7f33089 100644 --- a/src/ol/layer/Vector.js +++ b/src/ol/layer/Vector.js @@ -91,114 +91,181 @@ const Property = { * @param {module:ol/layer/Vector~Options=} opt_options Options. * @api */ -const VectorLayer = function(opt_options) { - const options = opt_options ? - opt_options : /** @type {module:ol/layer/Vector~Options} */ ({}); +class VectorLayer { + constructor(opt_options) { + const options = opt_options ? + opt_options : /** @type {module:ol/layer/Vector~Options} */ ({}); - const baseOptions = assign({}, options); + const baseOptions = assign({}, options); - delete baseOptions.style; - delete baseOptions.renderBuffer; - delete baseOptions.updateWhileAnimating; - delete baseOptions.updateWhileInteracting; - Layer.call(this, /** @type {module:ol/layer/Layer~Options} */ (baseOptions)); + delete baseOptions.style; + delete baseOptions.renderBuffer; + delete baseOptions.updateWhileAnimating; + delete baseOptions.updateWhileInteracting; + Layer.call(this, /** @type {module:ol/layer/Layer~Options} */ (baseOptions)); - /** - * @private - * @type {boolean} - */ - this.declutter_ = options.declutter !== undefined ? options.declutter : false; + /** + * @private + * @type {boolean} + */ + this.declutter_ = options.declutter !== undefined ? options.declutter : false; - /** - * @type {number} - * @private - */ - this.renderBuffer_ = options.renderBuffer !== undefined ? - options.renderBuffer : 100; + /** + * @type {number} + * @private + */ + this.renderBuffer_ = options.renderBuffer !== undefined ? + options.renderBuffer : 100; - /** - * User provided style. - * @type {module:ol/style/Style|Array.|module:ol/style/Style~StyleFunction} - * @private - */ - this.style_ = null; + /** + * User provided style. + * @type {module:ol/style/Style|Array.|module:ol/style/Style~StyleFunction} + * @private + */ + this.style_ = null; - /** - * Style function for use within the library. - * @type {module:ol/style/Style~StyleFunction|undefined} - * @private - */ - this.styleFunction_ = undefined; + /** + * Style function for use within the library. + * @type {module:ol/style/Style~StyleFunction|undefined} + * @private + */ + this.styleFunction_ = undefined; - this.setStyle(options.style); + this.setStyle(options.style); - /** - * @type {boolean} - * @private - */ - this.updateWhileAnimating_ = options.updateWhileAnimating !== undefined ? - options.updateWhileAnimating : false; + /** + * @type {boolean} + * @private + */ + this.updateWhileAnimating_ = options.updateWhileAnimating !== undefined ? + options.updateWhileAnimating : false; - /** - * @type {boolean} - * @private - */ - this.updateWhileInteracting_ = options.updateWhileInteracting !== undefined ? - options.updateWhileInteracting : false; + /** + * @type {boolean} + * @private + */ + this.updateWhileInteracting_ = options.updateWhileInteracting !== undefined ? + options.updateWhileInteracting : false; - /** - * @private - * @type {module:ol/layer/VectorTileRenderType|string} - */ - this.renderMode_ = options.renderMode || VectorRenderType.VECTOR; + /** + * @private + * @type {module:ol/layer/VectorTileRenderType|string} + */ + this.renderMode_ = options.renderMode || VectorRenderType.VECTOR; - /** - * The layer type. - * @protected - * @type {module:ol/LayerType} - */ - this.type = LayerType.VECTOR; + /** + * The layer type. + * @protected + * @type {module:ol/LayerType} + */ + this.type = LayerType.VECTOR; -}; + } + + /** + * @return {boolean} Declutter. + */ + getDeclutter() { + return this.declutter_; + } + + /** + * @param {boolean} declutter Declutter. + */ + setDeclutter(declutter) { + this.declutter_ = declutter; + } + + /** + * @return {number|undefined} Render buffer. + */ + getRenderBuffer() { + return this.renderBuffer_; + } + + /** + * @return {function(module:ol/Feature, module:ol/Feature): number|null|undefined} Render + * order. + */ + getRenderOrder() { + return ( + /** @type {module:ol/render~OrderFunction|null|undefined} */ (this.get(Property.RENDER_ORDER)) + ); + } + + /** + * Get the style for features. This returns whatever was passed to the `style` + * option at construction or to the `setStyle` method. + * @return {module:ol/style/Style|Array.|module:ol/style/Style~StyleFunction} + * Layer style. + * @api + */ + getStyle() { + return this.style_; + } + + /** + * Get the style function. + * @return {module:ol/style/Style~StyleFunction|undefined} Layer style function. + * @api + */ + getStyleFunction() { + return this.styleFunction_; + } + + /** + * @return {boolean} Whether the rendered layer should be updated while + * animating. + */ + getUpdateWhileAnimating() { + return this.updateWhileAnimating_; + } + + /** + * @return {boolean} Whether the rendered layer should be updated while + * interacting. + */ + getUpdateWhileInteracting() { + return this.updateWhileInteracting_; + } + + /** + * @param {module:ol/render~OrderFunction|null|undefined} renderOrder + * Render order. + */ + setRenderOrder(renderOrder) { + this.set(Property.RENDER_ORDER, renderOrder); + } + + /** + * Set the style for features. This can be a single style object, an array + * of styles, or a function that takes a feature and resolution and returns + * an array of styles. If it is `undefined` the default style is used. If + * it is `null` the layer has no style (a `null` style), so only features + * that have their own styles will be rendered in the layer. See + * {@link module:ol/style} for information on the default style. + * @param {module:ol/style/Style|Array.|module:ol/style/Style~StyleFunction|null|undefined} + * style Layer style. + * @api + */ + setStyle(style) { + this.style_ = style !== undefined ? style : createDefaultStyle; + this.styleFunction_ = style === null ? + undefined : toStyleFunction(this.style_); + this.changed(); + } + + /** + * @return {module:ol/layer/VectorRenderType|string} The render mode. + */ + getRenderMode() { + return this.renderMode_; + } +} inherits(VectorLayer, Layer); -/** - * @return {boolean} Declutter. - */ -VectorLayer.prototype.getDeclutter = function() { - return this.declutter_; -}; - - -/** - * @param {boolean} declutter Declutter. - */ -VectorLayer.prototype.setDeclutter = function(declutter) { - this.declutter_ = declutter; -}; - - -/** - * @return {number|undefined} Render buffer. - */ -VectorLayer.prototype.getRenderBuffer = function() { - return this.renderBuffer_; -}; - - -/** - * @return {function(module:ol/Feature, module:ol/Feature): number|null|undefined} Render - * order. - */ -VectorLayer.prototype.getRenderOrder = function() { - return ( - /** @type {module:ol/render~OrderFunction|null|undefined} */ (this.get(Property.RENDER_ORDER)) - ); -}; - - /** * Return the associated {@link module:ol/source/Vector vectorsource} of the layer. * @function @@ -208,80 +275,4 @@ VectorLayer.prototype.getRenderOrder = function() { VectorLayer.prototype.getSource; -/** - * Get the style for features. This returns whatever was passed to the `style` - * option at construction or to the `setStyle` method. - * @return {module:ol/style/Style|Array.|module:ol/style/Style~StyleFunction} - * Layer style. - * @api - */ -VectorLayer.prototype.getStyle = function() { - return this.style_; -}; - - -/** - * Get the style function. - * @return {module:ol/style/Style~StyleFunction|undefined} Layer style function. - * @api - */ -VectorLayer.prototype.getStyleFunction = function() { - return this.styleFunction_; -}; - - -/** - * @return {boolean} Whether the rendered layer should be updated while - * animating. - */ -VectorLayer.prototype.getUpdateWhileAnimating = function() { - return this.updateWhileAnimating_; -}; - - -/** - * @return {boolean} Whether the rendered layer should be updated while - * interacting. - */ -VectorLayer.prototype.getUpdateWhileInteracting = function() { - return this.updateWhileInteracting_; -}; - - -/** - * @param {module:ol/render~OrderFunction|null|undefined} renderOrder - * Render order. - */ -VectorLayer.prototype.setRenderOrder = function(renderOrder) { - this.set(Property.RENDER_ORDER, renderOrder); -}; - - -/** - * Set the style for features. This can be a single style object, an array - * of styles, or a function that takes a feature and resolution and returns - * an array of styles. If it is `undefined` the default style is used. If - * it is `null` the layer has no style (a `null` style), so only features - * that have their own styles will be rendered in the layer. See - * {@link module:ol/style} for information on the default style. - * @param {module:ol/style/Style|Array.|module:ol/style/Style~StyleFunction|null|undefined} - * style Layer style. - * @api - */ -VectorLayer.prototype.setStyle = function(style) { - this.style_ = style !== undefined ? style : createDefaultStyle; - this.styleFunction_ = style === null ? - undefined : toStyleFunction(this.style_); - this.changed(); -}; - - -/** - * @return {module:ol/layer/VectorRenderType|string} The render mode. - */ -VectorLayer.prototype.getRenderMode = function() { - return this.renderMode_; -}; - - export default VectorLayer; diff --git a/src/ol/layer/VectorTile.js b/src/ol/layer/VectorTile.js index 265435afec..2f0ac36950 100644 --- a/src/ol/layer/VectorTile.js +++ b/src/ol/layer/VectorTile.js @@ -99,86 +99,84 @@ export const RenderType = { * @param {module:ol/layer/VectorTile~Options=} opt_options Options. * @api */ -const VectorTileLayer = function(opt_options) { - const options = opt_options ? opt_options : {}; +class VectorTileLayer { + constructor(opt_options) { + const options = opt_options ? opt_options : {}; - let renderMode = options.renderMode || VectorTileRenderType.HYBRID; - assert(renderMode == undefined || - renderMode == VectorTileRenderType.IMAGE || - renderMode == VectorTileRenderType.HYBRID || - renderMode == VectorTileRenderType.VECTOR, - 28); // `renderMode` must be `'image'`, `'hybrid'` or `'vector'` - if (options.declutter && renderMode == VectorTileRenderType.IMAGE) { - renderMode = VectorTileRenderType.HYBRID; - } - options.renderMode = renderMode; + let renderMode = options.renderMode || VectorTileRenderType.HYBRID; + assert(renderMode == undefined || + renderMode == VectorTileRenderType.IMAGE || + renderMode == VectorTileRenderType.HYBRID || + renderMode == VectorTileRenderType.VECTOR, + 28); // `renderMode` must be `'image'`, `'hybrid'` or `'vector'` + if (options.declutter && renderMode == VectorTileRenderType.IMAGE) { + renderMode = VectorTileRenderType.HYBRID; + } + options.renderMode = renderMode; - const baseOptions = assign({}, options); + const baseOptions = assign({}, options); - delete baseOptions.preload; - delete baseOptions.useInterimTilesOnError; - VectorLayer.call(this, /** @type {module:ol/layer/Vector~Options} */ (baseOptions)); + delete baseOptions.preload; + delete baseOptions.useInterimTilesOnError; + VectorLayer.call(this, /** @type {module:ol/layer/Vector~Options} */ (baseOptions)); - this.setPreload(options.preload ? options.preload : 0); - this.setUseInterimTilesOnError(options.useInterimTilesOnError !== undefined ? - options.useInterimTilesOnError : true); + this.setPreload(options.preload ? options.preload : 0); + this.setUseInterimTilesOnError(options.useInterimTilesOnError !== undefined ? + options.useInterimTilesOnError : true); - /** - * The layer type. - * @protected - * @type {module:ol/LayerType} - */ - this.type = LayerType.VECTOR_TILE; + /** + * The layer type. + * @protected + * @type {module:ol/LayerType} + */ + this.type = LayerType.VECTOR_TILE; -}; + } + + /** + * Return the level as number to which we will preload tiles up to. + * @return {number} The level to preload tiles up to. + * @observable + * @api + */ + getPreload() { + return /** @type {number} */ (this.get(TileProperty.PRELOAD)); + } + + /** + * Whether we use interim tiles on error. + * @return {boolean} Use interim tiles on error. + * @observable + * @api + */ + getUseInterimTilesOnError() { + return /** @type {boolean} */ (this.get(TileProperty.USE_INTERIM_TILES_ON_ERROR)); + } + + /** + * Set the level as number to which we will preload tiles up to. + * @param {number} preload The level to preload tiles up to. + * @observable + * @api + */ + setPreload(preload) { + this.set(TileProperty.PRELOAD, preload); + } + + /** + * Set whether we use interim tiles on error. + * @param {boolean} useInterimTilesOnError Use interim tiles on error. + * @observable + * @api + */ + setUseInterimTilesOnError(useInterimTilesOnError) { + this.set(TileProperty.USE_INTERIM_TILES_ON_ERROR, useInterimTilesOnError); + } +} inherits(VectorTileLayer, VectorLayer); -/** - * Return the level as number to which we will preload tiles up to. - * @return {number} The level to preload tiles up to. - * @observable - * @api - */ -VectorTileLayer.prototype.getPreload = function() { - return /** @type {number} */ (this.get(TileProperty.PRELOAD)); -}; - - -/** - * Whether we use interim tiles on error. - * @return {boolean} Use interim tiles on error. - * @observable - * @api - */ -VectorTileLayer.prototype.getUseInterimTilesOnError = function() { - return /** @type {boolean} */ (this.get(TileProperty.USE_INTERIM_TILES_ON_ERROR)); -}; - - -/** - * Set the level as number to which we will preload tiles up to. - * @param {number} preload The level to preload tiles up to. - * @observable - * @api - */ -VectorTileLayer.prototype.setPreload = function(preload) { - this.set(TileProperty.PRELOAD, preload); -}; - - -/** - * Set whether we use interim tiles on error. - * @param {boolean} useInterimTilesOnError Use interim tiles on error. - * @observable - * @api - */ -VectorTileLayer.prototype.setUseInterimTilesOnError = function(useInterimTilesOnError) { - this.set(TileProperty.USE_INTERIM_TILES_ON_ERROR, useInterimTilesOnError); -}; - - /** * Return the associated {@link module:ol/source/VectorTile vectortilesource} of the layer. * @function diff --git a/src/ol/pointer/EventSource.js b/src/ol/pointer/EventSource.js index 591171076b..e7ea944ce2 100644 --- a/src/ol/pointer/EventSource.js +++ b/src/ol/pointer/EventSource.js @@ -6,36 +6,37 @@ * @param {!Object.} mapping Event mapping. * @constructor */ -const EventSource = function(dispatcher, mapping) { - /** - * @type {module:ol/pointer/PointerEventHandler} - */ - this.dispatcher = dispatcher; +class EventSource { + constructor(dispatcher, mapping) { + /** + * @type {module:ol/pointer/PointerEventHandler} + */ + this.dispatcher = dispatcher; - /** - * @private - * @const - * @type {!Object.} - */ - this.mapping_ = mapping; -}; + /** + * @private + * @const + * @type {!Object.} + */ + this.mapping_ = mapping; + } + /** + * List of events supported by this source. + * @return {Array.} Event names + */ + getEvents() { + return Object.keys(this.mapping_); + } -/** - * List of events supported by this source. - * @return {Array.} Event names - */ -EventSource.prototype.getEvents = function() { - return Object.keys(this.mapping_); -}; + /** + * Returns the handler that should handle a given event type. + * @param {string} eventType The event type. + * @return {function(Event)} Handler + */ + getHandlerForEvent(eventType) { + return this.mapping_[eventType]; + } +} - -/** - * Returns the handler that should handle a given event type. - * @param {string} eventType The event type. - * @return {function(Event)} Handler - */ -EventSource.prototype.getHandlerForEvent = function(eventType) { - return this.mapping_[eventType]; -}; export default EventSource; diff --git a/src/ol/pointer/MouseSource.js b/src/ol/pointer/MouseSource.js index f9a00a40ce..c8f12bde3f 100644 --- a/src/ol/pointer/MouseSource.js +++ b/src/ol/pointer/MouseSource.js @@ -39,28 +39,158 @@ import EventSource from '../pointer/EventSource.js'; * @constructor * @extends {module:ol/pointer/EventSource} */ -const MouseSource = function(dispatcher) { - const mapping = { - 'mousedown': this.mousedown, - 'mousemove': this.mousemove, - 'mouseup': this.mouseup, - 'mouseover': this.mouseover, - 'mouseout': this.mouseout - }; - EventSource.call(this, dispatcher, mapping); +class MouseSource { + constructor(dispatcher) { + const mapping = { + 'mousedown': this.mousedown, + 'mousemove': this.mousemove, + 'mouseup': this.mouseup, + 'mouseover': this.mouseover, + 'mouseout': this.mouseout + }; + EventSource.call(this, dispatcher, mapping); + + /** + * @const + * @type {!Object.} + */ + this.pointerMap = dispatcher.pointerMap; + + /** + * @const + * @type {Array.} + */ + this.lastTouches = []; + } /** - * @const - * @type {!Object.} + * Detect if a mouse event was simulated from a touch by + * checking if previously there was a touch event at the + * same position. + * + * FIXME - Known problem with the native Android browser on + * Samsung GT-I9100 (Android 4.1.2): + * In case the page is scrolled, this function does not work + * correctly when a canvas is used (WebGL or canvas renderer). + * Mouse listeners on canvas elements (for this browser), create + * two mouse events: One 'good' and one 'bad' one (on other browsers or + * when a div is used, there is only one event). For the 'bad' one, + * clientX/clientY and also pageX/pageY are wrong when the page + * is scrolled. Because of that, this function can not detect if + * the events were simulated from a touch event. As result, a + * pointer event at a wrong position is dispatched, which confuses + * the map interactions. + * It is unclear, how one can get the correct position for the event + * or detect that the positions are invalid. + * + * @private + * @param {MouseEvent} inEvent The in event. + * @return {boolean} True, if the event was generated by a touch. */ - this.pointerMap = dispatcher.pointerMap; + isEventSimulatedFromTouch_(inEvent) { + const lts = this.lastTouches; + const x = inEvent.clientX; + const y = inEvent.clientY; + for (let i = 0, l = lts.length, t; i < l && (t = lts[i]); i++) { + // simulated mouse events will be swallowed near a primary touchend + const dx = Math.abs(x - t[0]); + const dy = Math.abs(y - t[1]); + if (dx <= DEDUP_DIST && dy <= DEDUP_DIST) { + return true; + } + } + return false; + } /** - * @const - * @type {Array.} + * Handler for `mousedown`. + * + * @param {MouseEvent} inEvent The in event. */ - this.lastTouches = []; -}; + mousedown(inEvent) { + if (!this.isEventSimulatedFromTouch_(inEvent)) { + // TODO(dfreedman) workaround for some elements not sending mouseup + // http://crbug/149091 + if (POINTER_ID.toString() in this.pointerMap) { + this.cancel(inEvent); + } + const e = prepareEvent(inEvent, this.dispatcher); + this.pointerMap[POINTER_ID.toString()] = inEvent; + this.dispatcher.down(e, inEvent); + } + } + + /** + * Handler for `mousemove`. + * + * @param {MouseEvent} inEvent The in event. + */ + mousemove(inEvent) { + if (!this.isEventSimulatedFromTouch_(inEvent)) { + const e = prepareEvent(inEvent, this.dispatcher); + this.dispatcher.move(e, inEvent); + } + } + + /** + * Handler for `mouseup`. + * + * @param {MouseEvent} inEvent The in event. + */ + mouseup(inEvent) { + if (!this.isEventSimulatedFromTouch_(inEvent)) { + const p = this.pointerMap[POINTER_ID.toString()]; + + if (p && p.button === inEvent.button) { + const e = prepareEvent(inEvent, this.dispatcher); + this.dispatcher.up(e, inEvent); + this.cleanupMouse(); + } + } + } + + /** + * Handler for `mouseover`. + * + * @param {MouseEvent} inEvent The in event. + */ + mouseover(inEvent) { + if (!this.isEventSimulatedFromTouch_(inEvent)) { + const e = prepareEvent(inEvent, this.dispatcher); + this.dispatcher.enterOver(e, inEvent); + } + } + + /** + * Handler for `mouseout`. + * + * @param {MouseEvent} inEvent The in event. + */ + mouseout(inEvent) { + if (!this.isEventSimulatedFromTouch_(inEvent)) { + const e = prepareEvent(inEvent, this.dispatcher); + this.dispatcher.leaveOut(e, inEvent); + } + } + + /** + * Dispatches a `pointercancel` event. + * + * @param {Event} inEvent The in event. + */ + cancel(inEvent) { + const e = prepareEvent(inEvent, this.dispatcher); + this.dispatcher.cancel(e, inEvent); + this.cleanupMouse(); + } + + /** + * Remove the mouse from the list of active pointers. + */ + cleanupMouse() { + delete this.pointerMap[POINTER_ID.toString()]; + } +} inherits(MouseSource, EventSource); @@ -85,46 +215,6 @@ export const POINTER_TYPE = 'mouse'; const DEDUP_DIST = 25; -/** - * Detect if a mouse event was simulated from a touch by - * checking if previously there was a touch event at the - * same position. - * - * FIXME - Known problem with the native Android browser on - * Samsung GT-I9100 (Android 4.1.2): - * In case the page is scrolled, this function does not work - * correctly when a canvas is used (WebGL or canvas renderer). - * Mouse listeners on canvas elements (for this browser), create - * two mouse events: One 'good' and one 'bad' one (on other browsers or - * when a div is used, there is only one event). For the 'bad' one, - * clientX/clientY and also pageX/pageY are wrong when the page - * is scrolled. Because of that, this function can not detect if - * the events were simulated from a touch event. As result, a - * pointer event at a wrong position is dispatched, which confuses - * the map interactions. - * It is unclear, how one can get the correct position for the event - * or detect that the positions are invalid. - * - * @private - * @param {MouseEvent} inEvent The in event. - * @return {boolean} True, if the event was generated by a touch. - */ -MouseSource.prototype.isEventSimulatedFromTouch_ = function(inEvent) { - const lts = this.lastTouches; - const x = inEvent.clientX; - const y = inEvent.clientY; - for (let i = 0, l = lts.length, t; i < l && (t = lts[i]); i++) { - // simulated mouse events will be swallowed near a primary touchend - const dx = Math.abs(x - t[0]); - const dy = Math.abs(y - t[1]); - if (dx <= DEDUP_DIST && dy <= DEDUP_DIST) { - return true; - } - } - return false; -}; - - /** * Creates a copy of the original event that will be used * for the fake pointer event. @@ -151,98 +241,4 @@ function prepareEvent(inEvent, dispatcher) { } -/** - * Handler for `mousedown`. - * - * @param {MouseEvent} inEvent The in event. - */ -MouseSource.prototype.mousedown = function(inEvent) { - if (!this.isEventSimulatedFromTouch_(inEvent)) { - // TODO(dfreedman) workaround for some elements not sending mouseup - // http://crbug/149091 - if (POINTER_ID.toString() in this.pointerMap) { - this.cancel(inEvent); - } - const e = prepareEvent(inEvent, this.dispatcher); - this.pointerMap[POINTER_ID.toString()] = inEvent; - this.dispatcher.down(e, inEvent); - } -}; - - -/** - * Handler for `mousemove`. - * - * @param {MouseEvent} inEvent The in event. - */ -MouseSource.prototype.mousemove = function(inEvent) { - if (!this.isEventSimulatedFromTouch_(inEvent)) { - const e = prepareEvent(inEvent, this.dispatcher); - this.dispatcher.move(e, inEvent); - } -}; - - -/** - * Handler for `mouseup`. - * - * @param {MouseEvent} inEvent The in event. - */ -MouseSource.prototype.mouseup = function(inEvent) { - if (!this.isEventSimulatedFromTouch_(inEvent)) { - const p = this.pointerMap[POINTER_ID.toString()]; - - if (p && p.button === inEvent.button) { - const e = prepareEvent(inEvent, this.dispatcher); - this.dispatcher.up(e, inEvent); - this.cleanupMouse(); - } - } -}; - - -/** - * Handler for `mouseover`. - * - * @param {MouseEvent} inEvent The in event. - */ -MouseSource.prototype.mouseover = function(inEvent) { - if (!this.isEventSimulatedFromTouch_(inEvent)) { - const e = prepareEvent(inEvent, this.dispatcher); - this.dispatcher.enterOver(e, inEvent); - } -}; - - -/** - * Handler for `mouseout`. - * - * @param {MouseEvent} inEvent The in event. - */ -MouseSource.prototype.mouseout = function(inEvent) { - if (!this.isEventSimulatedFromTouch_(inEvent)) { - const e = prepareEvent(inEvent, this.dispatcher); - this.dispatcher.leaveOut(e, inEvent); - } -}; - - -/** - * Dispatches a `pointercancel` event. - * - * @param {Event} inEvent The in event. - */ -MouseSource.prototype.cancel = function(inEvent) { - const e = prepareEvent(inEvent, this.dispatcher); - this.dispatcher.cancel(e, inEvent); - this.cleanupMouse(); -}; - - -/** - * Remove the mouse from the list of active pointers. - */ -MouseSource.prototype.cleanupMouse = function() { - delete this.pointerMap[POINTER_ID.toString()]; -}; export default MouseSource; diff --git a/src/ol/pointer/MsSource.js b/src/ol/pointer/MsSource.js index a8ad9d64e2..402ff54b7f 100644 --- a/src/ol/pointer/MsSource.js +++ b/src/ol/pointer/MsSource.js @@ -39,25 +39,136 @@ import EventSource from '../pointer/EventSource.js'; * @constructor * @extends {module:ol/pointer/EventSource} */ -const MsSource = function(dispatcher) { - const mapping = { - 'MSPointerDown': this.msPointerDown, - 'MSPointerMove': this.msPointerMove, - 'MSPointerUp': this.msPointerUp, - 'MSPointerOut': this.msPointerOut, - 'MSPointerOver': this.msPointerOver, - 'MSPointerCancel': this.msPointerCancel, - 'MSGotPointerCapture': this.msGotPointerCapture, - 'MSLostPointerCapture': this.msLostPointerCapture - }; - EventSource.call(this, dispatcher, mapping); +class MsSource { + constructor(dispatcher) { + const mapping = { + 'MSPointerDown': this.msPointerDown, + 'MSPointerMove': this.msPointerMove, + 'MSPointerUp': this.msPointerUp, + 'MSPointerOut': this.msPointerOut, + 'MSPointerOver': this.msPointerOver, + 'MSPointerCancel': this.msPointerCancel, + 'MSGotPointerCapture': this.msGotPointerCapture, + 'MSLostPointerCapture': this.msLostPointerCapture + }; + EventSource.call(this, dispatcher, mapping); - /** - * @const - * @type {!Object.} - */ - this.pointerMap = dispatcher.pointerMap; -}; + /** + * @const + * @type {!Object.} + */ + this.pointerMap = dispatcher.pointerMap; + } + + /** + * Creates a copy of the original event that will be used + * for the fake pointer event. + * + * @private + * @param {MSPointerEvent} inEvent The in event. + * @return {Object} The copied event. + */ + prepareEvent_(inEvent) { + let e = inEvent; + if (typeof inEvent.pointerType === 'number') { + e = this.dispatcher.cloneEvent(inEvent, inEvent); + e.pointerType = POINTER_TYPES[inEvent.pointerType]; + } + + return e; + } + + /** + * Remove this pointer from the list of active pointers. + * @param {number} pointerId Pointer identifier. + */ + cleanup(pointerId) { + delete this.pointerMap[pointerId.toString()]; + } + + /** + * Handler for `msPointerDown`. + * + * @param {MSPointerEvent} inEvent The in event. + */ + msPointerDown(inEvent) { + this.pointerMap[inEvent.pointerId.toString()] = inEvent; + const e = this.prepareEvent_(inEvent); + this.dispatcher.down(e, inEvent); + } + + /** + * Handler for `msPointerMove`. + * + * @param {MSPointerEvent} inEvent The in event. + */ + msPointerMove(inEvent) { + const e = this.prepareEvent_(inEvent); + this.dispatcher.move(e, inEvent); + } + + /** + * Handler for `msPointerUp`. + * + * @param {MSPointerEvent} inEvent The in event. + */ + msPointerUp(inEvent) { + const e = this.prepareEvent_(inEvent); + this.dispatcher.up(e, inEvent); + this.cleanup(inEvent.pointerId); + } + + /** + * Handler for `msPointerOut`. + * + * @param {MSPointerEvent} inEvent The in event. + */ + msPointerOut(inEvent) { + const e = this.prepareEvent_(inEvent); + this.dispatcher.leaveOut(e, inEvent); + } + + /** + * Handler for `msPointerOver`. + * + * @param {MSPointerEvent} inEvent The in event. + */ + msPointerOver(inEvent) { + const e = this.prepareEvent_(inEvent); + this.dispatcher.enterOver(e, inEvent); + } + + /** + * Handler for `msPointerCancel`. + * + * @param {MSPointerEvent} inEvent The in event. + */ + msPointerCancel(inEvent) { + const e = this.prepareEvent_(inEvent); + this.dispatcher.cancel(e, inEvent); + this.cleanup(inEvent.pointerId); + } + + /** + * Handler for `msLostPointerCapture`. + * + * @param {MSPointerEvent} inEvent The in event. + */ + msLostPointerCapture(inEvent) { + const e = this.dispatcher.makeEvent('lostpointercapture', inEvent, inEvent); + this.dispatcher.dispatchEvent(e); + } + + /** + * Handler for `msGotPointerCapture`. + * + * @param {MSPointerEvent} inEvent The in event. + */ + msGotPointerCapture(inEvent) { + const e = this.dispatcher.makeEvent('gotpointercapture', inEvent, inEvent); + this.dispatcher.dispatchEvent(e); + } +} inherits(MsSource, EventSource); @@ -74,121 +185,4 @@ const POINTER_TYPES = [ ]; -/** - * Creates a copy of the original event that will be used - * for the fake pointer event. - * - * @private - * @param {MSPointerEvent} inEvent The in event. - * @return {Object} The copied event. - */ -MsSource.prototype.prepareEvent_ = function(inEvent) { - let e = inEvent; - if (typeof inEvent.pointerType === 'number') { - e = this.dispatcher.cloneEvent(inEvent, inEvent); - e.pointerType = POINTER_TYPES[inEvent.pointerType]; - } - - return e; -}; - - -/** - * Remove this pointer from the list of active pointers. - * @param {number} pointerId Pointer identifier. - */ -MsSource.prototype.cleanup = function(pointerId) { - delete this.pointerMap[pointerId.toString()]; -}; - - -/** - * Handler for `msPointerDown`. - * - * @param {MSPointerEvent} inEvent The in event. - */ -MsSource.prototype.msPointerDown = function(inEvent) { - this.pointerMap[inEvent.pointerId.toString()] = inEvent; - const e = this.prepareEvent_(inEvent); - this.dispatcher.down(e, inEvent); -}; - - -/** - * Handler for `msPointerMove`. - * - * @param {MSPointerEvent} inEvent The in event. - */ -MsSource.prototype.msPointerMove = function(inEvent) { - const e = this.prepareEvent_(inEvent); - this.dispatcher.move(e, inEvent); -}; - - -/** - * Handler for `msPointerUp`. - * - * @param {MSPointerEvent} inEvent The in event. - */ -MsSource.prototype.msPointerUp = function(inEvent) { - const e = this.prepareEvent_(inEvent); - this.dispatcher.up(e, inEvent); - this.cleanup(inEvent.pointerId); -}; - - -/** - * Handler for `msPointerOut`. - * - * @param {MSPointerEvent} inEvent The in event. - */ -MsSource.prototype.msPointerOut = function(inEvent) { - const e = this.prepareEvent_(inEvent); - this.dispatcher.leaveOut(e, inEvent); -}; - - -/** - * Handler for `msPointerOver`. - * - * @param {MSPointerEvent} inEvent The in event. - */ -MsSource.prototype.msPointerOver = function(inEvent) { - const e = this.prepareEvent_(inEvent); - this.dispatcher.enterOver(e, inEvent); -}; - - -/** - * Handler for `msPointerCancel`. - * - * @param {MSPointerEvent} inEvent The in event. - */ -MsSource.prototype.msPointerCancel = function(inEvent) { - const e = this.prepareEvent_(inEvent); - this.dispatcher.cancel(e, inEvent); - this.cleanup(inEvent.pointerId); -}; - - -/** - * Handler for `msLostPointerCapture`. - * - * @param {MSPointerEvent} inEvent The in event. - */ -MsSource.prototype.msLostPointerCapture = function(inEvent) { - const e = this.dispatcher.makeEvent('lostpointercapture', inEvent, inEvent); - this.dispatcher.dispatchEvent(e); -}; - - -/** - * Handler for `msGotPointerCapture`. - * - * @param {MSPointerEvent} inEvent The in event. - */ -MsSource.prototype.msGotPointerCapture = function(inEvent) { - const e = this.dispatcher.makeEvent('gotpointercapture', inEvent, inEvent); - this.dispatcher.dispatchEvent(e); -}; export default MsSource; diff --git a/src/ol/pointer/NativeSource.js b/src/ol/pointer/NativeSource.js index a745900ba0..a28740ee7b 100644 --- a/src/ol/pointer/NativeSource.js +++ b/src/ol/pointer/NativeSource.js @@ -39,99 +39,95 @@ import EventSource from '../pointer/EventSource.js'; * @constructor * @extends {module:ol/pointer/EventSource} */ -const NativeSource = function(dispatcher) { - const mapping = { - 'pointerdown': this.pointerDown, - 'pointermove': this.pointerMove, - 'pointerup': this.pointerUp, - 'pointerout': this.pointerOut, - 'pointerover': this.pointerOver, - 'pointercancel': this.pointerCancel, - 'gotpointercapture': this.gotPointerCapture, - 'lostpointercapture': this.lostPointerCapture - }; - EventSource.call(this, dispatcher, mapping); -}; +class NativeSource { + constructor(dispatcher) { + const mapping = { + 'pointerdown': this.pointerDown, + 'pointermove': this.pointerMove, + 'pointerup': this.pointerUp, + 'pointerout': this.pointerOut, + 'pointerover': this.pointerOver, + 'pointercancel': this.pointerCancel, + 'gotpointercapture': this.gotPointerCapture, + 'lostpointercapture': this.lostPointerCapture + }; + EventSource.call(this, dispatcher, mapping); + } + + /** + * Handler for `pointerdown`. + * + * @param {Event} inEvent The in event. + */ + pointerDown(inEvent) { + this.dispatcher.fireNativeEvent(inEvent); + } + + /** + * Handler for `pointermove`. + * + * @param {Event} inEvent The in event. + */ + pointerMove(inEvent) { + this.dispatcher.fireNativeEvent(inEvent); + } + + /** + * Handler for `pointerup`. + * + * @param {Event} inEvent The in event. + */ + pointerUp(inEvent) { + this.dispatcher.fireNativeEvent(inEvent); + } + + /** + * Handler for `pointerout`. + * + * @param {Event} inEvent The in event. + */ + pointerOut(inEvent) { + this.dispatcher.fireNativeEvent(inEvent); + } + + /** + * Handler for `pointerover`. + * + * @param {Event} inEvent The in event. + */ + pointerOver(inEvent) { + this.dispatcher.fireNativeEvent(inEvent); + } + + /** + * Handler for `pointercancel`. + * + * @param {Event} inEvent The in event. + */ + pointerCancel(inEvent) { + this.dispatcher.fireNativeEvent(inEvent); + } + + /** + * Handler for `lostpointercapture`. + * + * @param {Event} inEvent The in event. + */ + lostPointerCapture(inEvent) { + this.dispatcher.fireNativeEvent(inEvent); + } + + /** + * Handler for `gotpointercapture`. + * + * @param {Event} inEvent The in event. + */ + gotPointerCapture(inEvent) { + this.dispatcher.fireNativeEvent(inEvent); + } +} inherits(NativeSource, EventSource); -/** - * Handler for `pointerdown`. - * - * @param {Event} inEvent The in event. - */ -NativeSource.prototype.pointerDown = function(inEvent) { - this.dispatcher.fireNativeEvent(inEvent); -}; - - -/** - * Handler for `pointermove`. - * - * @param {Event} inEvent The in event. - */ -NativeSource.prototype.pointerMove = function(inEvent) { - this.dispatcher.fireNativeEvent(inEvent); -}; - - -/** - * Handler for `pointerup`. - * - * @param {Event} inEvent The in event. - */ -NativeSource.prototype.pointerUp = function(inEvent) { - this.dispatcher.fireNativeEvent(inEvent); -}; - - -/** - * Handler for `pointerout`. - * - * @param {Event} inEvent The in event. - */ -NativeSource.prototype.pointerOut = function(inEvent) { - this.dispatcher.fireNativeEvent(inEvent); -}; - - -/** - * Handler for `pointerover`. - * - * @param {Event} inEvent The in event. - */ -NativeSource.prototype.pointerOver = function(inEvent) { - this.dispatcher.fireNativeEvent(inEvent); -}; - - -/** - * Handler for `pointercancel`. - * - * @param {Event} inEvent The in event. - */ -NativeSource.prototype.pointerCancel = function(inEvent) { - this.dispatcher.fireNativeEvent(inEvent); -}; - - -/** - * Handler for `lostpointercapture`. - * - * @param {Event} inEvent The in event. - */ -NativeSource.prototype.lostPointerCapture = function(inEvent) { - this.dispatcher.fireNativeEvent(inEvent); -}; - - -/** - * Handler for `gotpointercapture`. - * - * @param {Event} inEvent The in event. - */ -NativeSource.prototype.gotPointerCapture = function(inEvent) { - this.dispatcher.fireNativeEvent(inEvent); -}; export default NativeSource; diff --git a/src/ol/pointer/PointerEvent.js b/src/ol/pointer/PointerEvent.js index 079cfc1e40..56957d9943 100644 --- a/src/ol/pointer/PointerEvent.js +++ b/src/ol/pointer/PointerEvent.js @@ -47,150 +47,211 @@ import Event from '../events/Event.js'; * @param {Object.=} opt_eventDict An optional dictionary of * initial event properties. */ -const PointerEvent = function(type, originalEvent, opt_eventDict) { - Event.call(this, type); +class PointerEvent { + constructor(type, originalEvent, opt_eventDict) { + Event.call(this, type); - /** - * @const - * @type {Event} - */ - this.originalEvent = originalEvent; + /** + * @const + * @type {Event} + */ + this.originalEvent = originalEvent; - const eventDict = opt_eventDict ? opt_eventDict : {}; + const eventDict = opt_eventDict ? opt_eventDict : {}; - /** - * @type {number} - */ - this.buttons = this.getButtons_(eventDict); + /** + * @type {number} + */ + this.buttons = this.getButtons_(eventDict); - /** - * @type {number} - */ - this.pressure = this.getPressure_(eventDict, this.buttons); + /** + * @type {number} + */ + this.pressure = this.getPressure_(eventDict, this.buttons); - // MouseEvent related properties + // MouseEvent related properties - /** - * @type {boolean} - */ - this.bubbles = 'bubbles' in eventDict ? eventDict['bubbles'] : false; + /** + * @type {boolean} + */ + this.bubbles = 'bubbles' in eventDict ? eventDict['bubbles'] : false; - /** - * @type {boolean} - */ - this.cancelable = 'cancelable' in eventDict ? eventDict['cancelable'] : false; + /** + * @type {boolean} + */ + this.cancelable = 'cancelable' in eventDict ? eventDict['cancelable'] : false; - /** - * @type {Object} - */ - this.view = 'view' in eventDict ? eventDict['view'] : null; + /** + * @type {Object} + */ + this.view = 'view' in eventDict ? eventDict['view'] : null; - /** - * @type {number} - */ - this.detail = 'detail' in eventDict ? eventDict['detail'] : null; + /** + * @type {number} + */ + this.detail = 'detail' in eventDict ? eventDict['detail'] : null; - /** - * @type {number} - */ - this.screenX = 'screenX' in eventDict ? eventDict['screenX'] : 0; + /** + * @type {number} + */ + this.screenX = 'screenX' in eventDict ? eventDict['screenX'] : 0; - /** - * @type {number} - */ - this.screenY = 'screenY' in eventDict ? eventDict['screenY'] : 0; + /** + * @type {number} + */ + this.screenY = 'screenY' in eventDict ? eventDict['screenY'] : 0; - /** - * @type {number} - */ - this.clientX = 'clientX' in eventDict ? eventDict['clientX'] : 0; + /** + * @type {number} + */ + this.clientX = 'clientX' in eventDict ? eventDict['clientX'] : 0; - /** - * @type {number} - */ - this.clientY = 'clientY' in eventDict ? eventDict['clientY'] : 0; + /** + * @type {number} + */ + this.clientY = 'clientY' in eventDict ? eventDict['clientY'] : 0; - /** - * @type {boolean} - */ - this.ctrlKey = 'ctrlKey' in eventDict ? eventDict['ctrlKey'] : false; + /** + * @type {boolean} + */ + this.ctrlKey = 'ctrlKey' in eventDict ? eventDict['ctrlKey'] : false; - /** - * @type {boolean} - */ - this.altKey = 'altKey' in eventDict ? eventDict['altKey'] : false; + /** + * @type {boolean} + */ + this.altKey = 'altKey' in eventDict ? eventDict['altKey'] : false; - /** - * @type {boolean} - */ - this.shiftKey = 'shiftKey' in eventDict ? eventDict['shiftKey'] : false; + /** + * @type {boolean} + */ + this.shiftKey = 'shiftKey' in eventDict ? eventDict['shiftKey'] : false; - /** - * @type {boolean} - */ - this.metaKey = 'metaKey' in eventDict ? eventDict['metaKey'] : false; + /** + * @type {boolean} + */ + this.metaKey = 'metaKey' in eventDict ? eventDict['metaKey'] : false; - /** - * @type {number} - */ - this.button = 'button' in eventDict ? eventDict['button'] : 0; + /** + * @type {number} + */ + this.button = 'button' in eventDict ? eventDict['button'] : 0; - /** - * @type {Node} - */ - this.relatedTarget = 'relatedTarget' in eventDict ? - eventDict['relatedTarget'] : null; + /** + * @type {Node} + */ + this.relatedTarget = 'relatedTarget' in eventDict ? + eventDict['relatedTarget'] : null; - // PointerEvent related properties + // PointerEvent related properties - /** - * @const - * @type {number} - */ - this.pointerId = 'pointerId' in eventDict ? eventDict['pointerId'] : 0; + /** + * @const + * @type {number} + */ + this.pointerId = 'pointerId' in eventDict ? eventDict['pointerId'] : 0; - /** - * @type {number} - */ - this.width = 'width' in eventDict ? eventDict['width'] : 0; + /** + * @type {number} + */ + this.width = 'width' in eventDict ? eventDict['width'] : 0; - /** - * @type {number} - */ - this.height = 'height' in eventDict ? eventDict['height'] : 0; + /** + * @type {number} + */ + this.height = 'height' in eventDict ? eventDict['height'] : 0; - /** - * @type {number} - */ - this.tiltX = 'tiltX' in eventDict ? eventDict['tiltX'] : 0; + /** + * @type {number} + */ + this.tiltX = 'tiltX' in eventDict ? eventDict['tiltX'] : 0; - /** - * @type {number} - */ - this.tiltY = 'tiltY' in eventDict ? eventDict['tiltY'] : 0; + /** + * @type {number} + */ + this.tiltY = 'tiltY' in eventDict ? eventDict['tiltY'] : 0; - /** - * @type {string} - */ - this.pointerType = 'pointerType' in eventDict ? eventDict['pointerType'] : ''; + /** + * @type {string} + */ + this.pointerType = 'pointerType' in eventDict ? eventDict['pointerType'] : ''; - /** - * @type {number} - */ - this.hwTimestamp = 'hwTimestamp' in eventDict ? eventDict['hwTimestamp'] : 0; + /** + * @type {number} + */ + this.hwTimestamp = 'hwTimestamp' in eventDict ? eventDict['hwTimestamp'] : 0; - /** - * @type {boolean} - */ - this.isPrimary = 'isPrimary' in eventDict ? eventDict['isPrimary'] : false; + /** + * @type {boolean} + */ + this.isPrimary = 'isPrimary' in eventDict ? eventDict['isPrimary'] : false; - // keep the semantics of preventDefault - if (originalEvent.preventDefault) { - this.preventDefault = function() { - originalEvent.preventDefault(); - }; - } -}; + // keep the semantics of preventDefault + if (originalEvent.preventDefault) { + this.preventDefault = function() { + originalEvent.preventDefault(); + }; + } + } + + /** + * @private + * @param {Object.} 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.} 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; + } + return pressure; + } +} inherits(PointerEvent, Event); @@ -202,67 +263,6 @@ inherits(PointerEvent, Event); let HAS_BUTTONS = false; -/** - * @private - * @param {Object.} eventDict The event dictionary. - * @return {number} Button indicator. - */ -PointerEvent.prototype.getButtons_ = function(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.} eventDict The event dictionary. - * @param {number} buttons Button indicator. - * @return {number} The pressure. - */ -PointerEvent.prototype.getPressure_ = function(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; -}; - - /** * Checks if the `buttons` property is supported. */ diff --git a/src/ol/pointer/PointerEventHandler.js b/src/ol/pointer/PointerEventHandler.js index ffa139d290..2be6d9ca34 100644 --- a/src/ol/pointer/PointerEventHandler.js +++ b/src/ol/pointer/PointerEventHandler.js @@ -47,36 +47,333 @@ import TouchSource from '../pointer/TouchSource.js'; * @extends {module:ol/events/EventTarget} * @param {Element|HTMLDocument} element Viewport element. */ -const PointerEventHandler = function(element) { - EventTarget.call(this); +class PointerEventHandler { + constructor(element) { + EventTarget.call(this); + + /** + * @const + * @private + * @type {Element|HTMLDocument} + */ + this.element_ = element; + + /** + * @const + * @type {!Object.} + */ + this.pointerMap = {}; + + /** + * @type {Object.} + * @private + */ + this.eventMap_ = {}; + + /** + * @type {Array.} + * @private + */ + this.eventSourceList_ = []; + + this.registerSources(); + } /** - * @const - * @private - * @type {Element|HTMLDocument} + * Set up the event sources (mouse, touch and native pointers) + * that generate pointer events. */ - this.element_ = element; + registerSources() { + if (POINTER) { + this.registerSource('native', new NativeSource(this)); + } else if (MSPOINTER) { + this.registerSource('ms', new MsSource(this)); + } else { + const mouseSource = new MouseSource(this); + this.registerSource('mouse', mouseSource); + + if (TOUCH) { + this.registerSource('touch', new TouchSource(this, mouseSource)); + } + } + + // register events on the viewport element + this.register_(); + } /** - * @const - * @type {!Object.} + * Add a new event source that will generate pointer events. + * + * @param {string} name A name for the event source + * @param {module:ol/pointer/EventSource} source The source event. */ - this.pointerMap = {}; + registerSource(name, source) { + const s = source; + const newEvents = s.getEvents(); + + if (newEvents) { + newEvents.forEach(function(e) { + const handler = s.getHandlerForEvent(e); + + if (handler) { + this.eventMap_[e] = handler.bind(s); + } + }.bind(this)); + this.eventSourceList_.push(s); + } + } /** - * @type {Object.} + * Set up the events for all registered event sources. * @private */ - this.eventMap_ = {}; + register_() { + const l = this.eventSourceList_.length; + for (let i = 0; i < l; i++) { + const eventSource = this.eventSourceList_[i]; + this.addEvents_(eventSource.getEvents()); + } + } /** - * @type {Array.} + * Remove all registered events. * @private */ - this.eventSourceList_ = []; + unregister_() { + const l = this.eventSourceList_.length; + for (let i = 0; i < l; i++) { + const eventSource = this.eventSourceList_[i]; + this.removeEvents_(eventSource.getEvents()); + } + } - this.registerSources(); -}; + /** + * Calls the right handler for a new event. + * @private + * @param {Event} inEvent Browser event. + */ + eventHandler_(inEvent) { + const type = inEvent.type; + const handler = this.eventMap_[type]; + if (handler) { + handler(inEvent); + } + } + + /** + * Setup listeners for the given events. + * @private + * @param {Array.} events List of events. + */ + addEvents_(events) { + events.forEach(function(eventName) { + listen(this.element_, eventName, this.eventHandler_, this); + }.bind(this)); + } + + /** + * Unregister listeners for the given events. + * @private + * @param {Array.} events List of events. + */ + removeEvents_(events) { + events.forEach(function(e) { + unlisten(this.element_, e, this.eventHandler_, this); + }.bind(this)); + } + + /** + * Returns a snapshot of inEvent, with writable properties. + * + * @param {Event} event Browser event. + * @param {Event|Touch} inEvent An event that contains + * properties to copy. + * @return {Object} An object containing shallow copies of + * `inEvent`'s properties. + */ + cloneEvent(event, inEvent) { + const eventCopy = {}; + for (let i = 0, ii = CLONE_PROPS.length; i < ii; i++) { + const p = CLONE_PROPS[i][0]; + eventCopy[p] = event[p] || inEvent[p] || CLONE_PROPS[i][1]; + } + + return eventCopy; + } + + // EVENTS + + + /** + * Triggers a 'pointerdown' event. + * @param {Object} data Pointer event data. + * @param {Event} event The event. + */ + down(data, event) { + this.fireEvent(PointerEventType.POINTERDOWN, data, event); + } + + /** + * Triggers a 'pointermove' event. + * @param {Object} data Pointer event data. + * @param {Event} event The event. + */ + move(data, event) { + this.fireEvent(PointerEventType.POINTERMOVE, data, event); + } + + /** + * Triggers a 'pointerup' event. + * @param {Object} data Pointer event data. + * @param {Event} event The event. + */ + up(data, event) { + this.fireEvent(PointerEventType.POINTERUP, data, event); + } + + /** + * Triggers a 'pointerenter' event. + * @param {Object} data Pointer event data. + * @param {Event} event The event. + */ + enter(data, event) { + data.bubbles = false; + this.fireEvent(PointerEventType.POINTERENTER, data, event); + } + + /** + * Triggers a 'pointerleave' event. + * @param {Object} data Pointer event data. + * @param {Event} event The event. + */ + leave(data, event) { + data.bubbles = false; + this.fireEvent(PointerEventType.POINTERLEAVE, data, event); + } + + /** + * Triggers a 'pointerover' event. + * @param {Object} data Pointer event data. + * @param {Event} event The event. + */ + over(data, event) { + data.bubbles = true; + this.fireEvent(PointerEventType.POINTEROVER, data, event); + } + + /** + * Triggers a 'pointerout' event. + * @param {Object} data Pointer event data. + * @param {Event} event The event. + */ + out(data, event) { + data.bubbles = true; + this.fireEvent(PointerEventType.POINTEROUT, data, event); + } + + /** + * Triggers a 'pointercancel' event. + * @param {Object} data Pointer event data. + * @param {Event} event The event. + */ + cancel(data, event) { + this.fireEvent(PointerEventType.POINTERCANCEL, data, event); + } + + /** + * Triggers a combination of 'pointerout' and 'pointerleave' events. + * @param {Object} data Pointer event data. + * @param {Event} event The event. + */ + leaveOut(data, event) { + this.out(data, event); + if (!this.contains_(data.target, data.relatedTarget)) { + this.leave(data, event); + } + } + + /** + * Triggers a combination of 'pointerover' and 'pointerevents' events. + * @param {Object} data Pointer event data. + * @param {Event} event The event. + */ + enterOver(data, event) { + this.over(data, event); + if (!this.contains_(data.target, data.relatedTarget)) { + this.enter(data, event); + } + } + + /** + * @private + * @param {Element} container The container element. + * @param {Element} contained The contained element. + * @return {boolean} Returns true if the container element + * contains the other element. + */ + contains_(container, contained) { + if (!container || !contained) { + return false; + } + return container.contains(contained); + } + + // EVENT CREATION AND TRACKING + /** + * Creates a new Event of type `inType`, based on the information in + * `data`. + * + * @param {string} inType A string representing the type of event to create. + * @param {Object} data Pointer event data. + * @param {Event} event The event. + * @return {module:ol/pointer/PointerEvent} A PointerEvent of type `inType`. + */ + makeEvent(inType, data, event) { + return new PointerEvent(inType, event, data); + } + + /** + * Make and dispatch an event in one call. + * @param {string} inType A string representing the type of event. + * @param {Object} data Pointer event data. + * @param {Event} event The event. + */ + fireEvent(inType, data, event) { + const e = this.makeEvent(inType, data, event); + this.dispatchEvent(e); + } + + /** + * Creates a pointer event from a native pointer event + * and dispatches this event. + * @param {Event} event A platform event with a target. + */ + fireNativeEvent(event) { + const e = this.makeEvent(event.type, event, event); + this.dispatchEvent(e); + } + + /** + * Wrap a native mouse event into a pointer event. + * This proxy method is required for the legacy IE support. + * @param {string} eventType The pointer event type. + * @param {Event} event The event. + * @return {module:ol/pointer/PointerEvent} The wrapped event. + */ + wrapMouseEvent(eventType, event) { + const pointerEvent = this.makeEvent( + eventType, MouseSource.prepareEvent(event, this), event); + return pointerEvent; + } + + /** + * @inheritDoc + */ + disposeInternal() { + this.unregister_(); + EventTarget.prototype.disposeInternal.call(this); + } +} inherits(PointerEventHandler, EventTarget); @@ -120,323 +417,4 @@ const CLONE_PROPS = [ ]; -/** - * Set up the event sources (mouse, touch and native pointers) - * that generate pointer events. - */ -PointerEventHandler.prototype.registerSources = function() { - if (POINTER) { - this.registerSource('native', new NativeSource(this)); - } else if (MSPOINTER) { - this.registerSource('ms', new MsSource(this)); - } else { - const mouseSource = new MouseSource(this); - this.registerSource('mouse', mouseSource); - - if (TOUCH) { - this.registerSource('touch', new TouchSource(this, mouseSource)); - } - } - - // register events on the viewport element - this.register_(); -}; - - -/** - * Add a new event source that will generate pointer events. - * - * @param {string} name A name for the event source - * @param {module:ol/pointer/EventSource} source The source event. - */ -PointerEventHandler.prototype.registerSource = function(name, source) { - const s = source; - const newEvents = s.getEvents(); - - if (newEvents) { - newEvents.forEach(function(e) { - const handler = s.getHandlerForEvent(e); - - if (handler) { - this.eventMap_[e] = handler.bind(s); - } - }.bind(this)); - this.eventSourceList_.push(s); - } -}; - - -/** - * Set up the events for all registered event sources. - * @private - */ -PointerEventHandler.prototype.register_ = function() { - const l = this.eventSourceList_.length; - for (let i = 0; i < l; i++) { - const eventSource = this.eventSourceList_[i]; - this.addEvents_(eventSource.getEvents()); - } -}; - - -/** - * Remove all registered events. - * @private - */ -PointerEventHandler.prototype.unregister_ = function() { - const l = this.eventSourceList_.length; - for (let i = 0; i < l; i++) { - const eventSource = this.eventSourceList_[i]; - this.removeEvents_(eventSource.getEvents()); - } -}; - - -/** - * Calls the right handler for a new event. - * @private - * @param {Event} inEvent Browser event. - */ -PointerEventHandler.prototype.eventHandler_ = function(inEvent) { - const type = inEvent.type; - const handler = this.eventMap_[type]; - if (handler) { - handler(inEvent); - } -}; - - -/** - * Setup listeners for the given events. - * @private - * @param {Array.} events List of events. - */ -PointerEventHandler.prototype.addEvents_ = function(events) { - events.forEach(function(eventName) { - listen(this.element_, eventName, this.eventHandler_, this); - }.bind(this)); -}; - - -/** - * Unregister listeners for the given events. - * @private - * @param {Array.} events List of events. - */ -PointerEventHandler.prototype.removeEvents_ = function(events) { - events.forEach(function(e) { - unlisten(this.element_, e, this.eventHandler_, this); - }.bind(this)); -}; - - -/** - * Returns a snapshot of inEvent, with writable properties. - * - * @param {Event} event Browser event. - * @param {Event|Touch} inEvent An event that contains - * properties to copy. - * @return {Object} An object containing shallow copies of - * `inEvent`'s properties. - */ -PointerEventHandler.prototype.cloneEvent = function(event, inEvent) { - const eventCopy = {}; - for (let i = 0, ii = CLONE_PROPS.length; i < ii; i++) { - const p = CLONE_PROPS[i][0]; - eventCopy[p] = event[p] || inEvent[p] || CLONE_PROPS[i][1]; - } - - return eventCopy; -}; - - -// EVENTS - - -/** - * Triggers a 'pointerdown' event. - * @param {Object} data Pointer event data. - * @param {Event} event The event. - */ -PointerEventHandler.prototype.down = function(data, event) { - this.fireEvent(PointerEventType.POINTERDOWN, data, event); -}; - - -/** - * Triggers a 'pointermove' event. - * @param {Object} data Pointer event data. - * @param {Event} event The event. - */ -PointerEventHandler.prototype.move = function(data, event) { - this.fireEvent(PointerEventType.POINTERMOVE, data, event); -}; - - -/** - * Triggers a 'pointerup' event. - * @param {Object} data Pointer event data. - * @param {Event} event The event. - */ -PointerEventHandler.prototype.up = function(data, event) { - this.fireEvent(PointerEventType.POINTERUP, data, event); -}; - - -/** - * Triggers a 'pointerenter' event. - * @param {Object} data Pointer event data. - * @param {Event} event The event. - */ -PointerEventHandler.prototype.enter = function(data, event) { - data.bubbles = false; - this.fireEvent(PointerEventType.POINTERENTER, data, event); -}; - - -/** - * Triggers a 'pointerleave' event. - * @param {Object} data Pointer event data. - * @param {Event} event The event. - */ -PointerEventHandler.prototype.leave = function(data, event) { - data.bubbles = false; - this.fireEvent(PointerEventType.POINTERLEAVE, data, event); -}; - - -/** - * Triggers a 'pointerover' event. - * @param {Object} data Pointer event data. - * @param {Event} event The event. - */ -PointerEventHandler.prototype.over = function(data, event) { - data.bubbles = true; - this.fireEvent(PointerEventType.POINTEROVER, data, event); -}; - - -/** - * Triggers a 'pointerout' event. - * @param {Object} data Pointer event data. - * @param {Event} event The event. - */ -PointerEventHandler.prototype.out = function(data, event) { - data.bubbles = true; - this.fireEvent(PointerEventType.POINTEROUT, data, event); -}; - - -/** - * Triggers a 'pointercancel' event. - * @param {Object} data Pointer event data. - * @param {Event} event The event. - */ -PointerEventHandler.prototype.cancel = function(data, event) { - this.fireEvent(PointerEventType.POINTERCANCEL, data, event); -}; - - -/** - * Triggers a combination of 'pointerout' and 'pointerleave' events. - * @param {Object} data Pointer event data. - * @param {Event} event The event. - */ -PointerEventHandler.prototype.leaveOut = function(data, event) { - this.out(data, event); - if (!this.contains_(data.target, data.relatedTarget)) { - this.leave(data, event); - } -}; - - -/** - * Triggers a combination of 'pointerover' and 'pointerevents' events. - * @param {Object} data Pointer event data. - * @param {Event} event The event. - */ -PointerEventHandler.prototype.enterOver = function(data, event) { - this.over(data, event); - if (!this.contains_(data.target, data.relatedTarget)) { - this.enter(data, event); - } -}; - - -/** - * @private - * @param {Element} container The container element. - * @param {Element} contained The contained element. - * @return {boolean} Returns true if the container element - * contains the other element. - */ -PointerEventHandler.prototype.contains_ = function(container, contained) { - if (!container || !contained) { - return false; - } - return container.contains(contained); -}; - - -// EVENT CREATION AND TRACKING -/** - * Creates a new Event of type `inType`, based on the information in - * `data`. - * - * @param {string} inType A string representing the type of event to create. - * @param {Object} data Pointer event data. - * @param {Event} event The event. - * @return {module:ol/pointer/PointerEvent} A PointerEvent of type `inType`. - */ -PointerEventHandler.prototype.makeEvent = function(inType, data, event) { - return new PointerEvent(inType, event, data); -}; - - -/** - * Make and dispatch an event in one call. - * @param {string} inType A string representing the type of event. - * @param {Object} data Pointer event data. - * @param {Event} event The event. - */ -PointerEventHandler.prototype.fireEvent = function(inType, data, event) { - const e = this.makeEvent(inType, data, event); - this.dispatchEvent(e); -}; - - -/** - * Creates a pointer event from a native pointer event - * and dispatches this event. - * @param {Event} event A platform event with a target. - */ -PointerEventHandler.prototype.fireNativeEvent = function(event) { - const e = this.makeEvent(event.type, event, event); - this.dispatchEvent(e); -}; - - -/** - * Wrap a native mouse event into a pointer event. - * This proxy method is required for the legacy IE support. - * @param {string} eventType The pointer event type. - * @param {Event} event The event. - * @return {module:ol/pointer/PointerEvent} The wrapped event. - */ -PointerEventHandler.prototype.wrapMouseEvent = function(eventType, event) { - const pointerEvent = this.makeEvent( - eventType, MouseSource.prepareEvent(event, this), event); - return pointerEvent; -}; - - -/** - * @inheritDoc - */ -PointerEventHandler.prototype.disposeInternal = function() { - this.unregister_(); - EventTarget.prototype.disposeInternal.call(this); -}; - - export default PointerEventHandler; diff --git a/src/ol/pointer/TouchSource.js b/src/ol/pointer/TouchSource.js index fd54196370..a9c2b05e62 100644 --- a/src/ol/pointer/TouchSource.js +++ b/src/ol/pointer/TouchSource.js @@ -43,53 +43,370 @@ import {POINTER_ID} from '../pointer/MouseSource.js'; * @param {module:ol/pointer/MouseSource} mouseSource Mouse source. * @extends {module:ol/pointer/EventSource} */ -const TouchSource = function(dispatcher, mouseSource) { - const mapping = { - 'touchstart': this.touchstart, - 'touchmove': this.touchmove, - 'touchend': this.touchend, - 'touchcancel': this.touchcancel - }; - EventSource.call(this, dispatcher, mapping); +class TouchSource { + constructor(dispatcher, mouseSource) { + const mapping = { + 'touchstart': this.touchstart, + 'touchmove': this.touchmove, + 'touchend': this.touchend, + 'touchcancel': this.touchcancel + }; + EventSource.call(this, dispatcher, mapping); - /** - * @const - * @type {!Object.} - */ - this.pointerMap = dispatcher.pointerMap; + /** + * @const + * @type {!Object.} + */ + this.pointerMap = dispatcher.pointerMap; - /** - * @const - * @type {module:ol/pointer/MouseSource} - */ - this.mouseSource = mouseSource; + /** + * @const + * @type {module:ol/pointer/MouseSource} + */ + this.mouseSource = mouseSource; + + /** + * @private + * @type {number|undefined} + */ + this.firstTouchId_ = undefined; + + /** + * @private + * @type {number} + */ + this.clickCount_ = 0; + + /** + * @private + * @type {number|undefined} + */ + this.resetId_ = undefined; + + /** + * Mouse event timeout: This should be long enough to + * ignore compat mouse events made by touch. + * @private + * @type {number} + */ + this.dedupTimeout_ = 2500; + } /** * @private - * @type {number|undefined} + * @param {Touch} inTouch The in touch. + * @return {boolean} True, if this is the primary touch. */ - this.firstTouchId_ = undefined; + isPrimaryTouch_(inTouch) { + return this.firstTouchId_ === inTouch.identifier; + } + + /** + * Set primary touch if there are no pointers, or the only pointer is the mouse. + * @param {Touch} inTouch The in touch. + * @private + */ + setPrimaryTouch_(inTouch) { + const count = Object.keys(this.pointerMap).length; + if (count === 0 || (count === 1 && POINTER_ID.toString() in this.pointerMap)) { + this.firstTouchId_ = inTouch.identifier; + this.cancelResetClickCount_(); + } + } /** * @private - * @type {number} + * @param {PointerEvent} inPointer The in pointer object. */ - this.clickCount_ = 0; + removePrimaryPointer_(inPointer) { + if (inPointer.isPrimary) { + this.firstTouchId_ = undefined; + this.resetClickCount_(); + } + } /** * @private - * @type {number|undefined} */ - this.resetId_ = undefined; + resetClickCount_() { + this.resetId_ = setTimeout( + this.resetClickCountHandler_.bind(this), + CLICK_COUNT_TIMEOUT); + } /** - * Mouse event timeout: This should be long enough to - * ignore compat mouse events made by touch. * @private - * @type {number} */ - this.dedupTimeout_ = 2500; -}; + resetClickCountHandler_() { + this.clickCount_ = 0; + this.resetId_ = undefined; + } + + /** + * @private + */ + cancelResetClickCount_() { + if (this.resetId_ !== undefined) { + clearTimeout(this.resetId_); + } + } + + /** + * @private + * @param {TouchEvent} browserEvent Browser event + * @param {Touch} inTouch Touch event + * @return {PointerEvent} A pointer object. + */ + touchToPointer_(browserEvent, inTouch) { + const e = this.dispatcher.cloneEvent(browserEvent, inTouch); + // Spec specifies that pointerId 1 is reserved for Mouse. + // Touch identifiers can start at 0. + // Add 2 to the touch identifier for compatibility. + e.pointerId = inTouch.identifier + 2; + // TODO: check if this is necessary? + //e.target = findTarget(e); + e.bubbles = true; + e.cancelable = true; + 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.isPrimary = this.isPrimaryTouch_(inTouch); + e.pointerType = POINTER_TYPE; + + // make sure that the properties that are different for + // each `Touch` object are not copied from the BrowserEvent object + e.clientX = inTouch.clientX; + e.clientY = inTouch.clientY; + e.screenX = inTouch.screenX; + e.screenY = inTouch.screenY; + + return e; + } + + /** + * @private + * @param {TouchEvent} inEvent Touch event + * @param {function(TouchEvent, PointerEvent)} inFunction In function. + */ + processTouches_(inEvent, inFunction) { + const touches = Array.prototype.slice.call(inEvent.changedTouches); + const count = touches.length; + function preventDefault() { + inEvent.preventDefault(); + } + for (let i = 0; i < count; ++i) { + const pointer = this.touchToPointer_(inEvent, touches[i]); + // forward touch preventDefaults + pointer.preventDefault = preventDefault; + inFunction.call(this, inEvent, pointer); + } + } + + /** + * @private + * @param {TouchList} touchList The touch list. + * @param {number} searchId Search identifier. + * @return {boolean} True, if the `Touch` with the given id is in the list. + */ + findTouch_(touchList, searchId) { + const l = touchList.length; + for (let i = 0; i < l; i++) { + const touch = touchList[i]; + if (touch.identifier === searchId) { + return true; + } + } + return false; + } + + /** + * In some instances, a touchstart can happen without a touchend. This + * leaves the pointermap in a broken state. + * Therefore, on every touchstart, we remove the touches that did not fire a + * touchend event. + * To keep state globally consistent, we fire a pointercancel for + * this "abandoned" touch + * + * @private + * @param {TouchEvent} inEvent The in event. + */ + vacuumTouches_(inEvent) { + const touchList = inEvent.touches; + // pointerMap.getCount() should be < touchList.length here, + // as the touchstart has not been processed yet. + const keys = Object.keys(this.pointerMap); + const count = keys.length; + if (count >= touchList.length) { + const d = []; + for (let i = 0; i < count; ++i) { + const key = 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 != POINTER_ID && !this.findTouch_(touchList, key - 2)) { + d.push(value.out); + } + } + for (let i = 0; i < d.length; ++i) { + this.cancelOut_(inEvent, d[i]); + } + } + } + + /** + * Handler for `touchstart`, triggers `pointerover`, + * `pointerenter` and `pointerdown` events. + * + * @param {TouchEvent} inEvent The in event. + */ + touchstart(inEvent) { + this.vacuumTouches_(inEvent); + this.setPrimaryTouch_(inEvent.changedTouches[0]); + this.dedupSynthMouse_(inEvent); + this.clickCount_++; + this.processTouches_(inEvent, this.overDown_); + } + + /** + * @private + * @param {TouchEvent} browserEvent The event. + * @param {PointerEvent} inPointer The in pointer object. + */ + overDown_(browserEvent, inPointer) { + this.pointerMap[inPointer.pointerId] = { + target: inPointer.target, + out: inPointer, + outTarget: inPointer.target + }; + this.dispatcher.over(inPointer, browserEvent); + this.dispatcher.enter(inPointer, browserEvent); + this.dispatcher.down(inPointer, browserEvent); + } + + /** + * Handler for `touchmove`. + * + * @param {TouchEvent} inEvent The in event. + */ + touchmove(inEvent) { + inEvent.preventDefault(); + this.processTouches_(inEvent, this.moveOverOut_); + } + + /** + * @private + * @param {TouchEvent} browserEvent The event. + * @param {PointerEvent} inPointer The in pointer. + */ + moveOverOut_(browserEvent, inPointer) { + const event = inPointer; + const pointer = this.pointerMap[event.pointerId]; + // a finger drifted off the screen, ignore it + if (!pointer) { + return; + } + const outEvent = pointer.out; + const outTarget = pointer.outTarget; + this.dispatcher.move(event, browserEvent); + if (outEvent && outTarget !== event.target) { + outEvent.relatedTarget = event.target; + event.relatedTarget = outTarget; + // recover from retargeting by shadow + outEvent.target = outTarget; + if (event.target) { + this.dispatcher.leaveOut(outEvent, browserEvent); + this.dispatcher.enterOver(event, browserEvent); + } else { + // clean up case when finger leaves the screen + event.target = outTarget; + event.relatedTarget = null; + this.cancelOut_(browserEvent, event); + } + } + pointer.out = event; + pointer.outTarget = event.target; + } + + /** + * Handler for `touchend`, triggers `pointerup`, + * `pointerout` and `pointerleave` events. + * + * @param {TouchEvent} inEvent The event. + */ + touchend(inEvent) { + this.dedupSynthMouse_(inEvent); + this.processTouches_(inEvent, this.upOut_); + } + + /** + * @private + * @param {TouchEvent} browserEvent An event. + * @param {PointerEvent} inPointer The inPointer object. + */ + upOut_(browserEvent, inPointer) { + this.dispatcher.up(inPointer, browserEvent); + this.dispatcher.out(inPointer, browserEvent); + this.dispatcher.leave(inPointer, browserEvent); + this.cleanUpPointer_(inPointer); + } + + /** + * Handler for `touchcancel`, triggers `pointercancel`, + * `pointerout` and `pointerleave` events. + * + * @param {TouchEvent} inEvent The in event. + */ + touchcancel(inEvent) { + this.processTouches_(inEvent, this.cancelOut_); + } + + /** + * @private + * @param {TouchEvent} browserEvent The event. + * @param {PointerEvent} inPointer The in pointer. + */ + cancelOut_(browserEvent, inPointer) { + this.dispatcher.cancel(inPointer, browserEvent); + this.dispatcher.out(inPointer, browserEvent); + this.dispatcher.leave(inPointer, browserEvent); + this.cleanUpPointer_(inPointer); + } + + /** + * @private + * @param {PointerEvent} inPointer The inPointer object. + */ + cleanUpPointer_(inPointer) { + delete this.pointerMap[inPointer.pointerId]; + this.removePrimaryPointer_(inPointer); + } + + /** + * Prevent synth mouse events from creating pointer events. + * + * @private + * @param {TouchEvent} inEvent The in event. + */ + dedupSynthMouse_(inEvent) { + const lts = this.mouseSource.lastTouches; + const t = inEvent.changedTouches[0]; + // only the primary finger will synth mouse events + if (this.isPrimaryTouch_(t)) { + // remember x/y of last touch + const lt = [t.clientX, t.clientY]; + lts.push(lt); + + setTimeout(function() { + // remove touch after timeout + remove(lts, lt); + }, this.dedupTimeout_); + } + } +} inherits(TouchSource, EventSource); @@ -105,337 +422,4 @@ const CLICK_COUNT_TIMEOUT = 200; */ const POINTER_TYPE = 'touch'; -/** - * @private - * @param {Touch} inTouch The in touch. - * @return {boolean} True, if this is the primary touch. - */ -TouchSource.prototype.isPrimaryTouch_ = function(inTouch) { - return this.firstTouchId_ === inTouch.identifier; -}; - - -/** - * Set primary touch if there are no pointers, or the only pointer is the mouse. - * @param {Touch} inTouch The in touch. - * @private - */ -TouchSource.prototype.setPrimaryTouch_ = function(inTouch) { - const count = Object.keys(this.pointerMap).length; - if (count === 0 || (count === 1 && POINTER_ID.toString() in this.pointerMap)) { - this.firstTouchId_ = inTouch.identifier; - this.cancelResetClickCount_(); - } -}; - - -/** - * @private - * @param {PointerEvent} inPointer The in pointer object. - */ -TouchSource.prototype.removePrimaryPointer_ = function(inPointer) { - if (inPointer.isPrimary) { - this.firstTouchId_ = undefined; - this.resetClickCount_(); - } -}; - - -/** - * @private - */ -TouchSource.prototype.resetClickCount_ = function() { - this.resetId_ = setTimeout( - this.resetClickCountHandler_.bind(this), - CLICK_COUNT_TIMEOUT); -}; - - -/** - * @private - */ -TouchSource.prototype.resetClickCountHandler_ = function() { - this.clickCount_ = 0; - this.resetId_ = undefined; -}; - - -/** - * @private - */ -TouchSource.prototype.cancelResetClickCount_ = function() { - if (this.resetId_ !== undefined) { - clearTimeout(this.resetId_); - } -}; - - -/** - * @private - * @param {TouchEvent} browserEvent Browser event - * @param {Touch} inTouch Touch event - * @return {PointerEvent} A pointer object. - */ -TouchSource.prototype.touchToPointer_ = function(browserEvent, inTouch) { - const e = this.dispatcher.cloneEvent(browserEvent, inTouch); - // Spec specifies that pointerId 1 is reserved for Mouse. - // Touch identifiers can start at 0. - // Add 2 to the touch identifier for compatibility. - e.pointerId = inTouch.identifier + 2; - // TODO: check if this is necessary? - //e.target = findTarget(e); - e.bubbles = true; - e.cancelable = true; - 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.isPrimary = this.isPrimaryTouch_(inTouch); - e.pointerType = POINTER_TYPE; - - // make sure that the properties that are different for - // each `Touch` object are not copied from the BrowserEvent object - e.clientX = inTouch.clientX; - e.clientY = inTouch.clientY; - e.screenX = inTouch.screenX; - e.screenY = inTouch.screenY; - - return e; -}; - - -/** - * @private - * @param {TouchEvent} inEvent Touch event - * @param {function(TouchEvent, PointerEvent)} inFunction In function. - */ -TouchSource.prototype.processTouches_ = function(inEvent, inFunction) { - const touches = Array.prototype.slice.call(inEvent.changedTouches); - const count = touches.length; - function preventDefault() { - inEvent.preventDefault(); - } - for (let i = 0; i < count; ++i) { - const pointer = this.touchToPointer_(inEvent, touches[i]); - // forward touch preventDefaults - pointer.preventDefault = preventDefault; - inFunction.call(this, inEvent, pointer); - } -}; - - -/** - * @private - * @param {TouchList} touchList The touch list. - * @param {number} searchId Search identifier. - * @return {boolean} True, if the `Touch` with the given id is in the list. - */ -TouchSource.prototype.findTouch_ = function(touchList, searchId) { - const l = touchList.length; - for (let i = 0; i < l; i++) { - const touch = touchList[i]; - if (touch.identifier === searchId) { - return true; - } - } - return false; -}; - - -/** - * In some instances, a touchstart can happen without a touchend. This - * leaves the pointermap in a broken state. - * Therefore, on every touchstart, we remove the touches that did not fire a - * touchend event. - * To keep state globally consistent, we fire a pointercancel for - * this "abandoned" touch - * - * @private - * @param {TouchEvent} inEvent The in event. - */ -TouchSource.prototype.vacuumTouches_ = function(inEvent) { - const touchList = inEvent.touches; - // pointerMap.getCount() should be < touchList.length here, - // as the touchstart has not been processed yet. - const keys = Object.keys(this.pointerMap); - const count = keys.length; - if (count >= touchList.length) { - const d = []; - for (let i = 0; i < count; ++i) { - const key = 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 != POINTER_ID && !this.findTouch_(touchList, key - 2)) { - d.push(value.out); - } - } - for (let i = 0; i < d.length; ++i) { - this.cancelOut_(inEvent, d[i]); - } - } -}; - - -/** - * Handler for `touchstart`, triggers `pointerover`, - * `pointerenter` and `pointerdown` events. - * - * @param {TouchEvent} inEvent The in event. - */ -TouchSource.prototype.touchstart = function(inEvent) { - this.vacuumTouches_(inEvent); - this.setPrimaryTouch_(inEvent.changedTouches[0]); - this.dedupSynthMouse_(inEvent); - this.clickCount_++; - this.processTouches_(inEvent, this.overDown_); -}; - - -/** - * @private - * @param {TouchEvent} browserEvent The event. - * @param {PointerEvent} inPointer The in pointer object. - */ -TouchSource.prototype.overDown_ = function(browserEvent, inPointer) { - this.pointerMap[inPointer.pointerId] = { - target: inPointer.target, - out: inPointer, - outTarget: inPointer.target - }; - this.dispatcher.over(inPointer, browserEvent); - this.dispatcher.enter(inPointer, browserEvent); - this.dispatcher.down(inPointer, browserEvent); -}; - - -/** - * Handler for `touchmove`. - * - * @param {TouchEvent} inEvent The in event. - */ -TouchSource.prototype.touchmove = function(inEvent) { - inEvent.preventDefault(); - this.processTouches_(inEvent, this.moveOverOut_); -}; - - -/** - * @private - * @param {TouchEvent} browserEvent The event. - * @param {PointerEvent} inPointer The in pointer. - */ -TouchSource.prototype.moveOverOut_ = function(browserEvent, inPointer) { - const event = inPointer; - const pointer = this.pointerMap[event.pointerId]; - // a finger drifted off the screen, ignore it - if (!pointer) { - return; - } - const outEvent = pointer.out; - const outTarget = pointer.outTarget; - this.dispatcher.move(event, browserEvent); - if (outEvent && outTarget !== event.target) { - outEvent.relatedTarget = event.target; - event.relatedTarget = outTarget; - // recover from retargeting by shadow - outEvent.target = outTarget; - if (event.target) { - this.dispatcher.leaveOut(outEvent, browserEvent); - this.dispatcher.enterOver(event, browserEvent); - } else { - // clean up case when finger leaves the screen - event.target = outTarget; - event.relatedTarget = null; - this.cancelOut_(browserEvent, event); - } - } - pointer.out = event; - pointer.outTarget = event.target; -}; - - -/** - * Handler for `touchend`, triggers `pointerup`, - * `pointerout` and `pointerleave` events. - * - * @param {TouchEvent} inEvent The event. - */ -TouchSource.prototype.touchend = function(inEvent) { - this.dedupSynthMouse_(inEvent); - this.processTouches_(inEvent, this.upOut_); -}; - - -/** - * @private - * @param {TouchEvent} browserEvent An event. - * @param {PointerEvent} inPointer The inPointer object. - */ -TouchSource.prototype.upOut_ = function(browserEvent, inPointer) { - this.dispatcher.up(inPointer, browserEvent); - this.dispatcher.out(inPointer, browserEvent); - this.dispatcher.leave(inPointer, browserEvent); - this.cleanUpPointer_(inPointer); -}; - - -/** - * Handler for `touchcancel`, triggers `pointercancel`, - * `pointerout` and `pointerleave` events. - * - * @param {TouchEvent} inEvent The in event. - */ -TouchSource.prototype.touchcancel = function(inEvent) { - this.processTouches_(inEvent, this.cancelOut_); -}; - - -/** - * @private - * @param {TouchEvent} browserEvent The event. - * @param {PointerEvent} inPointer The in pointer. - */ -TouchSource.prototype.cancelOut_ = function(browserEvent, inPointer) { - this.dispatcher.cancel(inPointer, browserEvent); - this.dispatcher.out(inPointer, browserEvent); - this.dispatcher.leave(inPointer, browserEvent); - this.cleanUpPointer_(inPointer); -}; - - -/** - * @private - * @param {PointerEvent} inPointer The inPointer object. - */ -TouchSource.prototype.cleanUpPointer_ = function(inPointer) { - delete this.pointerMap[inPointer.pointerId]; - this.removePrimaryPointer_(inPointer); -}; - - -/** - * Prevent synth mouse events from creating pointer events. - * - * @private - * @param {TouchEvent} inEvent The in event. - */ -TouchSource.prototype.dedupSynthMouse_ = function(inEvent) { - const lts = this.mouseSource.lastTouches; - const t = inEvent.changedTouches[0]; - // only the primary finger will synth mouse events - if (this.isPrimaryTouch_(t)) { - // remember x/y of last touch - const lt = [t.clientX, t.clientY]; - lts.push(lt); - - setTimeout(function() { - // remove touch after timeout - remove(lts, lt); - }, this.dedupTimeout_); - } -}; export default TouchSource; diff --git a/src/ol/proj/Projection.js b/src/ol/proj/Projection.js index 72621fb66d..91ccf3a2e9 100644 --- a/src/ol/proj/Projection.js +++ b/src/ol/proj/Projection.js @@ -54,232 +54,220 @@ import {METERS_PER_UNIT} from '../proj/Units.js'; * @struct * @api */ -const Projection = function(options) { - /** - * @private - * @type {string} - */ - this.code_ = options.code; +class Projection { + constructor(options) { + /** + * @private + * @type {string} + */ + this.code_ = options.code; - /** - * Units of projected coordinates. When set to `TILE_PIXELS`, a - * `this.extent_` and `this.worldExtent_` must be configured properly for each - * tile. - * @private - * @type {module:ol/proj/Units} - */ - this.units_ = /** @type {module:ol/proj/Units} */ (options.units); + /** + * Units of projected coordinates. When set to `TILE_PIXELS`, a + * `this.extent_` and `this.worldExtent_` must be configured properly for each + * tile. + * @private + * @type {module:ol/proj/Units} + */ + this.units_ = /** @type {module:ol/proj/Units} */ (options.units); - /** - * Validity extent of the projection in projected coordinates. For projections - * with `TILE_PIXELS` units, this is the extent of the tile in - * tile pixel space. - * @private - * @type {module:ol/extent~Extent} - */ - this.extent_ = options.extent !== undefined ? options.extent : null; + /** + * Validity extent of the projection in projected coordinates. For projections + * with `TILE_PIXELS` units, this is the extent of the tile in + * tile pixel space. + * @private + * @type {module:ol/extent~Extent} + */ + this.extent_ = options.extent !== undefined ? options.extent : null; - /** - * Extent of the world in EPSG:4326. For projections with - * `TILE_PIXELS` units, this is the extent of the tile in - * projected coordinate space. - * @private - * @type {module:ol/extent~Extent} - */ - this.worldExtent_ = options.worldExtent !== undefined ? - options.worldExtent : null; + /** + * Extent of the world in EPSG:4326. For projections with + * `TILE_PIXELS` units, this is the extent of the tile in + * projected coordinate space. + * @private + * @type {module:ol/extent~Extent} + */ + this.worldExtent_ = options.worldExtent !== undefined ? + options.worldExtent : null; - /** - * @private - * @type {string} - */ - this.axisOrientation_ = options.axisOrientation !== undefined ? - options.axisOrientation : 'enu'; + /** + * @private + * @type {string} + */ + this.axisOrientation_ = options.axisOrientation !== undefined ? + options.axisOrientation : 'enu'; - /** - * @private - * @type {boolean} - */ - this.global_ = options.global !== undefined ? options.global : false; + /** + * @private + * @type {boolean} + */ + this.global_ = options.global !== undefined ? options.global : false; - /** - * @private - * @type {boolean} - */ - this.canWrapX_ = !!(this.global_ && this.extent_); + /** + * @private + * @type {boolean} + */ + this.canWrapX_ = !!(this.global_ && this.extent_); - /** - * @private - * @type {function(number, module:ol/coordinate~Coordinate):number|undefined} - */ - this.getPointResolutionFunc_ = options.getPointResolution; + /** + * @private + * @type {function(number, module:ol/coordinate~Coordinate):number|undefined} + */ + this.getPointResolutionFunc_ = options.getPointResolution; - /** - * @private - * @type {module:ol/tilegrid/TileGrid} - */ - this.defaultTileGrid_ = null; + /** + * @private + * @type {module:ol/tilegrid/TileGrid} + */ + this.defaultTileGrid_ = null; - /** - * @private - * @type {number|undefined} - */ - this.metersPerUnit_ = options.metersPerUnit; -}; + /** + * @private + * @type {number|undefined} + */ + this.metersPerUnit_ = options.metersPerUnit; + } + /** + * @return {boolean} The projection is suitable for wrapping the x-axis + */ + canWrapX() { + return this.canWrapX_; + } -/** - * @return {boolean} The projection is suitable for wrapping the x-axis - */ -Projection.prototype.canWrapX = function() { - return this.canWrapX_; -}; + /** + * Get the code for this projection, e.g. 'EPSG:4326'. + * @return {string} Code. + * @api + */ + getCode() { + return this.code_; + } + /** + * Get the validity extent for this projection. + * @return {module:ol/extent~Extent} Extent. + * @api + */ + getExtent() { + return this.extent_; + } -/** - * Get the code for this projection, e.g. 'EPSG:4326'. - * @return {string} Code. + /** + * Get the units of this projection. + * @return {module:ol/proj/Units} Units. + * @api + */ + getUnits() { + return this.units_; + } + + /** + * Get the amount of meters per unit of this projection. If the projection is + * not configured with `metersPerUnit` or a units identifier, the return is + * `undefined`. + * @return {number|undefined} Meters. + * @api + */ + getMetersPerUnit() { + return this.metersPerUnit_ || METERS_PER_UNIT[this.units_]; + } + + /** + * Get the world extent for this projection. + * @return {module:ol/extent~Extent} Extent. + * @api + */ + getWorldExtent() { + return this.worldExtent_; + } + + /** + * Get the axis orientation of this projection. + * Example values are: + * enu - the default easting, northing, elevation. + * neu - northing, easting, up - useful for "lat/long" geographic coordinates, + * or south orientated transverse mercator. + * wnu - westing, northing, up - some planetary coordinate systems have + * "west positive" coordinate systems + * @return {string} Axis orientation. + * @api + */ + getAxisOrientation() { + return this.axisOrientation_; + } + + /** + * Is this projection a global projection which spans the whole world? + * @return {boolean} Whether the projection is global. + * @api + */ + isGlobal() { + return this.global_; + } + + /** + * Set if the projection is a global projection which spans the whole world + * @param {boolean} global Whether the projection is global. * @api */ -Projection.prototype.getCode = function() { - return this.code_; -}; + setGlobal(global) { + this.global_ = global; + this.canWrapX_ = !!(global && this.extent_); + } + /** + * @return {module:ol/tilegrid/TileGrid} The default tile grid. + */ + getDefaultTileGrid() { + return this.defaultTileGrid_; + } -/** - * Get the validity extent for this projection. - * @return {module:ol/extent~Extent} Extent. - * @api - */ -Projection.prototype.getExtent = function() { - return this.extent_; -}; + /** + * @param {module:ol/tilegrid/TileGrid} tileGrid The default tile grid. + */ + setDefaultTileGrid(tileGrid) { + this.defaultTileGrid_ = tileGrid; + } + /** + * Set the validity extent for this projection. + * @param {module:ol/extent~Extent} extent Extent. + * @api + */ + setExtent(extent) { + this.extent_ = extent; + this.canWrapX_ = !!(this.global_ && extent); + } -/** - * Get the units of this projection. - * @return {module:ol/proj/Units} Units. - * @api - */ -Projection.prototype.getUnits = function() { - return this.units_; -}; + /** + * Set the world extent for this projection. + * @param {module:ol/extent~Extent} worldExtent World extent + * [minlon, minlat, maxlon, maxlat]. + * @api + */ + setWorldExtent(worldExtent) { + this.worldExtent_ = worldExtent; + } + /** + * Set the getPointResolution function (see {@link module:ol/proj~getPointResolution} + * for this projection. + * @param {function(number, module:ol/coordinate~Coordinate):number} func Function + * @api + */ + setGetPointResolution(func) { + this.getPointResolutionFunc_ = func; + } -/** - * Get the amount of meters per unit of this projection. If the projection is - * not configured with `metersPerUnit` or a units identifier, the return is - * `undefined`. - * @return {number|undefined} Meters. - * @api - */ -Projection.prototype.getMetersPerUnit = function() { - return this.metersPerUnit_ || METERS_PER_UNIT[this.units_]; -}; + /** + * Get the custom point resolution function for this projection (if set). + * @return {function(number, module:ol/coordinate~Coordinate):number|undefined} The custom point + * resolution function (if set). + */ + getPointResolutionFunc() { + return this.getPointResolutionFunc_; + } +} - -/** - * Get the world extent for this projection. - * @return {module:ol/extent~Extent} Extent. - * @api - */ -Projection.prototype.getWorldExtent = function() { - return this.worldExtent_; -}; - - -/** - * Get the axis orientation of this projection. - * Example values are: - * enu - the default easting, northing, elevation. - * neu - northing, easting, up - useful for "lat/long" geographic coordinates, - * or south orientated transverse mercator. - * wnu - westing, northing, up - some planetary coordinate systems have - * "west positive" coordinate systems - * @return {string} Axis orientation. - * @api - */ -Projection.prototype.getAxisOrientation = function() { - return this.axisOrientation_; -}; - - -/** - * Is this projection a global projection which spans the whole world? - * @return {boolean} Whether the projection is global. - * @api - */ -Projection.prototype.isGlobal = function() { - return this.global_; -}; - - -/** -* Set if the projection is a global projection which spans the whole world -* @param {boolean} global Whether the projection is global. -* @api -*/ -Projection.prototype.setGlobal = function(global) { - this.global_ = global; - this.canWrapX_ = !!(global && this.extent_); -}; - - -/** - * @return {module:ol/tilegrid/TileGrid} The default tile grid. - */ -Projection.prototype.getDefaultTileGrid = function() { - return this.defaultTileGrid_; -}; - - -/** - * @param {module:ol/tilegrid/TileGrid} tileGrid The default tile grid. - */ -Projection.prototype.setDefaultTileGrid = function(tileGrid) { - this.defaultTileGrid_ = tileGrid; -}; - - -/** - * Set the validity extent for this projection. - * @param {module:ol/extent~Extent} extent Extent. - * @api - */ -Projection.prototype.setExtent = function(extent) { - this.extent_ = extent; - this.canWrapX_ = !!(this.global_ && extent); -}; - - -/** - * Set the world extent for this projection. - * @param {module:ol/extent~Extent} worldExtent World extent - * [minlon, minlat, maxlon, maxlat]. - * @api - */ -Projection.prototype.setWorldExtent = function(worldExtent) { - this.worldExtent_ = worldExtent; -}; - - -/** - * Set the getPointResolution function (see {@link module:ol/proj~getPointResolution} - * for this projection. - * @param {function(number, module:ol/coordinate~Coordinate):number} func Function - * @api - */ -Projection.prototype.setGetPointResolution = function(func) { - this.getPointResolutionFunc_ = func; -}; - - -/** - * Get the custom point resolution function for this projection (if set). - * @return {function(number, module:ol/coordinate~Coordinate):number|undefined} The custom point - * resolution function (if set). - */ -Projection.prototype.getPointResolutionFunc = function() { - return this.getPointResolutionFunc_; -}; export default Projection; diff --git a/src/ol/render/Box.js b/src/ol/render/Box.js index 31f0811328..59892c8cd5 100644 --- a/src/ol/render/Box.js +++ b/src/ol/render/Box.js @@ -12,123 +12,121 @@ import Polygon from '../geom/Polygon.js'; * @extends {module:ol/Disposable} * @param {string} className CSS class name. */ -const RenderBox = function(className) { +class RenderBox { + constructor(className) { - /** - * @type {module:ol/geom/Polygon} - * @private - */ - this.geometry_ = null; + /** + * @type {module:ol/geom/Polygon} + * @private + */ + this.geometry_ = null; - /** - * @type {HTMLDivElement} - * @private - */ - this.element_ = /** @type {HTMLDivElement} */ (document.createElement('div')); - this.element_.style.position = 'absolute'; - this.element_.className = 'ol-box ' + className; + /** + * @type {HTMLDivElement} + * @private + */ + this.element_ = /** @type {HTMLDivElement} */ (document.createElement('div')); + this.element_.style.position = 'absolute'; + this.element_.className = 'ol-box ' + className; - /** - * @private - * @type {module:ol/PluggableMap} - */ - this.map_ = null; + /** + * @private + * @type {module:ol/PluggableMap} + */ + this.map_ = null; - /** - * @private - * @type {module:ol~Pixel} - */ - this.startPixel_ = null; + /** + * @private + * @type {module:ol~Pixel} + */ + this.startPixel_ = null; - /** - * @private - * @type {module:ol~Pixel} - */ - this.endPixel_ = null; + /** + * @private + * @type {module:ol~Pixel} + */ + this.endPixel_ = null; -}; + } + + /** + * @inheritDoc + */ + disposeInternal() { + this.setMap(null); + } + + /** + * @private + */ + render_() { + const startPixel = this.startPixel_; + const endPixel = this.endPixel_; + const px = 'px'; + const style = this.element_.style; + style.left = Math.min(startPixel[0], endPixel[0]) + px; + style.top = Math.min(startPixel[1], endPixel[1]) + px; + style.width = Math.abs(endPixel[0] - startPixel[0]) + px; + style.height = Math.abs(endPixel[1] - startPixel[1]) + px; + } + + /** + * @param {module:ol/PluggableMap} map Map. + */ + setMap(map) { + if (this.map_) { + this.map_.getOverlayContainer().removeChild(this.element_); + const style = this.element_.style; + style.left = style.top = style.width = style.height = 'inherit'; + } + this.map_ = map; + if (this.map_) { + this.map_.getOverlayContainer().appendChild(this.element_); + } + } + + /** + * @param {module:ol~Pixel} startPixel Start pixel. + * @param {module:ol~Pixel} endPixel End pixel. + */ + setPixels(startPixel, endPixel) { + this.startPixel_ = startPixel; + this.endPixel_ = endPixel; + this.createOrUpdateGeometry(); + this.render_(); + } + + /** + * Creates or updates the cached geometry. + */ + createOrUpdateGeometry() { + const startPixel = this.startPixel_; + const endPixel = this.endPixel_; + const pixels = [ + startPixel, + [startPixel[0], endPixel[1]], + endPixel, + [endPixel[0], startPixel[1]] + ]; + const coordinates = pixels.map(this.map_.getCoordinateFromPixel, this.map_); + // close the polygon + coordinates[4] = coordinates[0].slice(); + if (!this.geometry_) { + this.geometry_ = new Polygon([coordinates]); + } else { + this.geometry_.setCoordinates([coordinates]); + } + } + + /** + * @return {module:ol/geom/Polygon} Geometry. + */ + getGeometry() { + return this.geometry_; + } +} inherits(RenderBox, Disposable); -/** - * @inheritDoc - */ -RenderBox.prototype.disposeInternal = function() { - this.setMap(null); -}; - - -/** - * @private - */ -RenderBox.prototype.render_ = function() { - const startPixel = this.startPixel_; - const endPixel = this.endPixel_; - const px = 'px'; - const style = this.element_.style; - style.left = Math.min(startPixel[0], endPixel[0]) + px; - style.top = Math.min(startPixel[1], endPixel[1]) + px; - style.width = Math.abs(endPixel[0] - startPixel[0]) + px; - style.height = Math.abs(endPixel[1] - startPixel[1]) + px; -}; - - -/** - * @param {module:ol/PluggableMap} map Map. - */ -RenderBox.prototype.setMap = function(map) { - if (this.map_) { - this.map_.getOverlayContainer().removeChild(this.element_); - const style = this.element_.style; - style.left = style.top = style.width = style.height = 'inherit'; - } - this.map_ = map; - if (this.map_) { - this.map_.getOverlayContainer().appendChild(this.element_); - } -}; - - -/** - * @param {module:ol~Pixel} startPixel Start pixel. - * @param {module:ol~Pixel} endPixel End pixel. - */ -RenderBox.prototype.setPixels = function(startPixel, endPixel) { - this.startPixel_ = startPixel; - this.endPixel_ = endPixel; - this.createOrUpdateGeometry(); - this.render_(); -}; - - -/** - * Creates or updates the cached geometry. - */ -RenderBox.prototype.createOrUpdateGeometry = function() { - const startPixel = this.startPixel_; - const endPixel = this.endPixel_; - const pixels = [ - startPixel, - [startPixel[0], endPixel[1]], - endPixel, - [endPixel[0], startPixel[1]] - ]; - const coordinates = pixels.map(this.map_.getCoordinateFromPixel, this.map_); - // close the polygon - coordinates[4] = coordinates[0].slice(); - if (!this.geometry_) { - this.geometry_ = new Polygon([coordinates]); - } else { - this.geometry_.setCoordinates([coordinates]); - } -}; - - -/** - * @return {module:ol/geom/Polygon} Geometry. - */ -RenderBox.prototype.getGeometry = function() { - return this.geometry_; -}; export default RenderBox; diff --git a/src/ol/render/Feature.js b/src/ol/render/Feature.js index 8fde1792ea..c305f03eee 100644 --- a/src/ol/render/Feature.js +++ b/src/ol/render/Feature.js @@ -25,56 +25,212 @@ import {create as createTransform, compose as composeTransform} from '../transfo * @param {Object.} properties Properties. * @param {number|string|undefined} id Feature id. */ -const RenderFeature = function(type, flatCoordinates, ends, properties, id) { - /** - * @private - * @type {module:ol/extent~Extent|undefined} - */ - this.extent_; +class RenderFeature { + constructor(type, flatCoordinates, ends, properties, id) { + /** + * @private + * @type {module:ol/extent~Extent|undefined} + */ + this.extent_; - /** - * @private - * @type {number|string|undefined} - */ - this.id_ = id; + /** + * @private + * @type {number|string|undefined} + */ + this.id_ = id; - /** - * @private - * @type {module:ol/geom/GeometryType} - */ - this.type_ = type; + /** + * @private + * @type {module:ol/geom/GeometryType} + */ + this.type_ = type; - /** - * @private - * @type {Array.} - */ - this.flatCoordinates_ = flatCoordinates; + /** + * @private + * @type {Array.} + */ + this.flatCoordinates_ = flatCoordinates; - /** - * @private - * @type {Array.} - */ - this.flatInteriorPoints_ = null; + /** + * @private + * @type {Array.} + */ + this.flatInteriorPoints_ = null; - /** - * @private - * @type {Array.} - */ - this.flatMidpoints_ = null; + /** + * @private + * @type {Array.} + */ + this.flatMidpoints_ = null; - /** - * @private - * @type {Array.|Array.>} - */ - this.ends_ = ends; + /** + * @private + * @type {Array.|Array.>} + */ + this.ends_ = ends; - /** - * @private - * @type {Object.} - */ - this.properties_ = properties; + /** + * @private + * @type {Object.} + */ + this.properties_ = properties; -}; + } + + /** + * Get a feature property by its key. + * @param {string} key Key + * @return {*} Value for the requested key. + * @api + */ + get(key) { + return this.properties_[key]; + } + + /** + * Get the extent of this feature's geometry. + * @return {module:ol/extent~Extent} Extent. + * @api + */ + getExtent() { + if (!this.extent_) { + this.extent_ = this.type_ === GeometryType.POINT ? + createOrUpdateFromCoordinate(this.flatCoordinates_) : + createOrUpdateFromFlatCoordinates( + this.flatCoordinates_, 0, this.flatCoordinates_.length, 2); + + } + return this.extent_; + } + + /** + * @return {Array.} Flat interior points. + */ + getFlatInteriorPoint() { + if (!this.flatInteriorPoints_) { + const flatCenter = getCenter(this.getExtent()); + this.flatInteriorPoints_ = getInteriorPointOfArray( + this.flatCoordinates_, 0, this.ends_, 2, flatCenter, 0); + } + return this.flatInteriorPoints_; + } + + /** + * @return {Array.} Flat interior points. + */ + getFlatInteriorPoints() { + if (!this.flatInteriorPoints_) { + const flatCenters = linearRingssCenter( + this.flatCoordinates_, 0, this.ends_, 2); + this.flatInteriorPoints_ = getInteriorPointsOfMultiArray( + this.flatCoordinates_, 0, this.ends_, 2, flatCenters); + } + return this.flatInteriorPoints_; + } + + /** + * @return {Array.} Flat midpoint. + */ + getFlatMidpoint() { + if (!this.flatMidpoints_) { + this.flatMidpoints_ = interpolatePoint( + this.flatCoordinates_, 0, this.flatCoordinates_.length, 2, 0.5); + } + return this.flatMidpoints_; + } + + /** + * @return {Array.} Flat midpoints. + */ + getFlatMidpoints() { + if (!this.flatMidpoints_) { + this.flatMidpoints_ = []; + const flatCoordinates = this.flatCoordinates_; + let offset = 0; + const ends = this.ends_; + for (let i = 0, ii = ends.length; i < ii; ++i) { + const end = ends[i]; + const midpoint = interpolatePoint( + flatCoordinates, offset, end, 2, 0.5); + extend(this.flatMidpoints_, midpoint); + offset = end; + } + } + return this.flatMidpoints_; + } + + /** + * Get the feature identifier. This is a stable identifier for the feature and + * is set when reading data from a remote source. + * @return {number|string|undefined} Id. + * @api + */ + getId() { + return this.id_; + } + + /** + * @return {Array.} Flat coordinates. + */ + getOrientedFlatCoordinates() { + return this.flatCoordinates_; + } + + /** + * For API compatibility with {@link module:ol/Feature~Feature}, this method is useful when + * determining the geometry type in style function (see {@link #getType}). + * @return {module:ol/render/Feature} Feature. + * @api + */ + getGeometry() { + return this; + } + + /** + * Get the feature properties. + * @return {Object.} Feature properties. + * @api + */ + getProperties() { + return this.properties_; + } + + /** + * @return {number} Stride. + */ + getStride() { + return 2; + } + + /** + * Get the type of this feature's geometry. + * @return {module:ol/geom/GeometryType} Geometry type. + * @api + */ + getType() { + return this.type_; + } + + /** + * Transform geometry coordinates from tile pixel space to projected. + * The SRS of the source and destination are expected to be the same. + * + * @param {module:ol/proj~ProjectionLike} source The current projection + * @param {module:ol/proj~ProjectionLike} destination The desired projection. + */ + transform(source, destination) { + source = getProjection(source); + const pixelExtent = source.getExtent(); + const projectedExtent = source.getWorldExtent(); + const scale = getHeight(projectedExtent) / getHeight(pixelExtent); + composeTransform(tmpTransform, + projectedExtent[0], projectedExtent[3], + scale, -scale, 0, + 0, 0); + transform2D(this.flatCoordinates_, 0, this.flatCoordinates_.length, 2, + tmpTransform, this.flatCoordinates_); + } +} /** @@ -83,17 +239,6 @@ const RenderFeature = function(type, flatCoordinates, ends, properties, id) { const tmpTransform = createTransform(); -/** - * Get a feature property by its key. - * @param {string} key Key - * @return {*} Value for the requested key. - * @api - */ -RenderFeature.prototype.get = function(key) { - return this.properties_[key]; -}; - - /** * @return {Array.|Array.>} Ends or endss. */ @@ -103,101 +248,6 @@ RenderFeature.prototype.getEndss = function() { }; -/** - * Get the extent of this feature's geometry. - * @return {module:ol/extent~Extent} Extent. - * @api - */ -RenderFeature.prototype.getExtent = function() { - if (!this.extent_) { - this.extent_ = this.type_ === GeometryType.POINT ? - createOrUpdateFromCoordinate(this.flatCoordinates_) : - createOrUpdateFromFlatCoordinates( - this.flatCoordinates_, 0, this.flatCoordinates_.length, 2); - - } - return this.extent_; -}; - - -/** - * @return {Array.} Flat interior points. - */ -RenderFeature.prototype.getFlatInteriorPoint = function() { - if (!this.flatInteriorPoints_) { - const flatCenter = getCenter(this.getExtent()); - this.flatInteriorPoints_ = getInteriorPointOfArray( - this.flatCoordinates_, 0, this.ends_, 2, flatCenter, 0); - } - return this.flatInteriorPoints_; -}; - - -/** - * @return {Array.} Flat interior points. - */ -RenderFeature.prototype.getFlatInteriorPoints = function() { - if (!this.flatInteriorPoints_) { - const flatCenters = linearRingssCenter( - this.flatCoordinates_, 0, this.ends_, 2); - this.flatInteriorPoints_ = getInteriorPointsOfMultiArray( - this.flatCoordinates_, 0, this.ends_, 2, flatCenters); - } - return this.flatInteriorPoints_; -}; - - -/** - * @return {Array.} Flat midpoint. - */ -RenderFeature.prototype.getFlatMidpoint = function() { - if (!this.flatMidpoints_) { - this.flatMidpoints_ = interpolatePoint( - this.flatCoordinates_, 0, this.flatCoordinates_.length, 2, 0.5); - } - return this.flatMidpoints_; -}; - - -/** - * @return {Array.} Flat midpoints. - */ -RenderFeature.prototype.getFlatMidpoints = function() { - if (!this.flatMidpoints_) { - this.flatMidpoints_ = []; - const flatCoordinates = this.flatCoordinates_; - let offset = 0; - const ends = this.ends_; - for (let i = 0, ii = ends.length; i < ii; ++i) { - const end = ends[i]; - const midpoint = interpolatePoint( - flatCoordinates, offset, end, 2, 0.5); - extend(this.flatMidpoints_, midpoint); - offset = end; - } - } - return this.flatMidpoints_; -}; - -/** - * Get the feature identifier. This is a stable identifier for the feature and - * is set when reading data from a remote source. - * @return {number|string|undefined} Id. - * @api - */ -RenderFeature.prototype.getId = function() { - return this.id_; -}; - - -/** - * @return {Array.} Flat coordinates. - */ -RenderFeature.prototype.getOrientedFlatCoordinates = function() { - return this.flatCoordinates_; -}; - - /** * @return {Array.} Flat coordinates. */ @@ -205,27 +255,6 @@ RenderFeature.prototype.getFlatCoordinates = RenderFeature.prototype.getOrientedFlatCoordinates; -/** - * For API compatibility with {@link module:ol/Feature~Feature}, this method is useful when - * determining the geometry type in style function (see {@link #getType}). - * @return {module:ol/render/Feature} Feature. - * @api - */ -RenderFeature.prototype.getGeometry = function() { - return this; -}; - - -/** - * Get the feature properties. - * @return {Object.} Feature properties. - * @api - */ -RenderFeature.prototype.getProperties = function() { - return this.properties_; -}; - - /** * Get the feature for working with its geometry. * @return {module:ol/render/Feature} Feature. @@ -234,46 +263,10 @@ RenderFeature.prototype.getSimplifiedGeometry = RenderFeature.prototype.getGeometry; -/** - * @return {number} Stride. - */ -RenderFeature.prototype.getStride = function() { - return 2; -}; - - /** * @return {undefined} */ RenderFeature.prototype.getStyleFunction = UNDEFINED; -/** - * Get the type of this feature's geometry. - * @return {module:ol/geom/GeometryType} Geometry type. - * @api - */ -RenderFeature.prototype.getType = function() { - return this.type_; -}; - -/** - * Transform geometry coordinates from tile pixel space to projected. - * The SRS of the source and destination are expected to be the same. - * - * @param {module:ol/proj~ProjectionLike} source The current projection - * @param {module:ol/proj~ProjectionLike} destination The desired projection. - */ -RenderFeature.prototype.transform = function(source, destination) { - source = getProjection(source); - const pixelExtent = source.getExtent(); - const projectedExtent = source.getWorldExtent(); - const scale = getHeight(projectedExtent) / getHeight(pixelExtent); - composeTransform(tmpTransform, - projectedExtent[0], projectedExtent[3], - scale, -scale, 0, - 0, 0); - transform2D(this.flatCoordinates_, 0, this.flatCoordinates_.length, 2, - tmpTransform, this.flatCoordinates_); -}; export default RenderFeature; diff --git a/src/ol/render/ReplayGroup.js b/src/ol/render/ReplayGroup.js index 264179e08e..c0b3c37fcb 100644 --- a/src/ol/render/ReplayGroup.js +++ b/src/ol/render/ReplayGroup.js @@ -6,21 +6,20 @@ * @constructor * @abstract */ -const ReplayGroup = function() {}; +class ReplayGroup { + /** + * @abstract + * @param {number|undefined} zIndex Z index. + * @param {module:ol/render/ReplayType} replayType Replay type. + * @return {module:ol/render/VectorContext} Replay. + */ + getReplay(zIndex, replayType) {} + /** + * @abstract + * @return {boolean} Is empty. + */ + isEmpty() {} +} -/** - * @abstract - * @param {number|undefined} zIndex Z index. - * @param {module:ol/render/ReplayType} replayType Replay type. - * @return {module:ol/render/VectorContext} Replay. - */ -ReplayGroup.prototype.getReplay = function(zIndex, replayType) {}; - - -/** - * @abstract - * @return {boolean} Is empty. - */ -ReplayGroup.prototype.isEmpty = function() {}; export default ReplayGroup; diff --git a/src/ol/render/VectorContext.js b/src/ol/render/VectorContext.js index d9317cc65a..0d160c6547 100644 --- a/src/ol/render/VectorContext.js +++ b/src/ol/render/VectorContext.js @@ -9,124 +9,108 @@ * @struct * @api */ -const VectorContext = function() { -}; +class VectorContext { + /** + * Render a geometry with a custom renderer. + * + * @param {module:ol/geom/SimpleGeometry} geometry Geometry. + * @param {module:ol/Feature|module:ol/render/Feature} feature Feature. + * @param {Function} renderer Renderer. + */ + drawCustom(geometry, feature, renderer) {} + /** + * Render a geometry. + * + * @param {module:ol/geom/Geometry} geometry The geometry to render. + */ + drawGeometry(geometry) {} -/** - * Render a geometry with a custom renderer. - * - * @param {module:ol/geom/SimpleGeometry} geometry Geometry. - * @param {module:ol/Feature|module:ol/render/Feature} feature Feature. - * @param {Function} renderer Renderer. - */ -VectorContext.prototype.drawCustom = function(geometry, feature, renderer) {}; + /** + * Set the rendering style. + * + * @param {module:ol/style/Style} style The rendering style. + */ + setStyle(style) {} + /** + * @param {module:ol/geom/Circle} circleGeometry Circle geometry. + * @param {module:ol/Feature} feature Feature. + */ + drawCircle(circleGeometry, feature) {} -/** - * Render a geometry. - * - * @param {module:ol/geom/Geometry} geometry The geometry to render. - */ -VectorContext.prototype.drawGeometry = function(geometry) {}; + /** + * @param {module:ol/Feature} feature Feature. + * @param {module:ol/style/Style} style Style. + */ + drawFeature(feature, style) {} + /** + * @param {module:ol/geom/GeometryCollection} geometryCollectionGeometry Geometry + * collection. + * @param {module:ol/Feature} feature Feature. + */ + drawGeometryCollection(geometryCollectionGeometry, feature) {} -/** - * Set the rendering style. - * - * @param {module:ol/style/Style} style The rendering style. - */ -VectorContext.prototype.setStyle = function(style) {}; + /** + * @param {module:ol/geom/LineString|module:ol/render/Feature} lineStringGeometry Line string geometry. + * @param {module:ol/Feature|module:ol/render/Feature} feature Feature. + */ + drawLineString(lineStringGeometry, feature) {} + /** + * @param {module:ol/geom/MultiLineString|module:ol/render/Feature} multiLineStringGeometry MultiLineString geometry. + * @param {module:ol/Feature|module:ol/render/Feature} feature Feature. + */ + drawMultiLineString(multiLineStringGeometry, feature) {} -/** - * @param {module:ol/geom/Circle} circleGeometry Circle geometry. - * @param {module:ol/Feature} feature Feature. - */ -VectorContext.prototype.drawCircle = function(circleGeometry, feature) {}; + /** + * @param {module:ol/geom/MultiPoint|module:ol/render/Feature} multiPointGeometry MultiPoint geometry. + * @param {module:ol/Feature|module:ol/render/Feature} feature Feature. + */ + drawMultiPoint(multiPointGeometry, feature) {} + /** + * @param {module:ol/geom/MultiPolygon} multiPolygonGeometry MultiPolygon geometry. + * @param {module:ol/Feature|module:ol/render/Feature} feature Feature. + */ + drawMultiPolygon(multiPolygonGeometry, feature) {} -/** - * @param {module:ol/Feature} feature Feature. - * @param {module:ol/style/Style} style Style. - */ -VectorContext.prototype.drawFeature = function(feature, style) {}; + /** + * @param {module:ol/geom/Point|module:ol/render/Feature} pointGeometry Point geometry. + * @param {module:ol/Feature|module:ol/render/Feature} feature Feature. + */ + drawPoint(pointGeometry, feature) {} + /** + * @param {module:ol/geom/Polygon|module:ol/render/Feature} polygonGeometry Polygon geometry. + * @param {module:ol/Feature|module:ol/render/Feature} feature Feature. + */ + drawPolygon(polygonGeometry, feature) {} -/** - * @param {module:ol/geom/GeometryCollection} geometryCollectionGeometry Geometry - * collection. - * @param {module:ol/Feature} feature Feature. - */ -VectorContext.prototype.drawGeometryCollection = function(geometryCollectionGeometry, feature) {}; + /** + * @param {module:ol/geom/Geometry|module:ol/render/Feature} geometry Geometry. + * @param {module:ol/Feature|module:ol/render/Feature} feature Feature. + */ + drawText(geometry, feature) {} + /** + * @param {module:ol/style/Fill} fillStyle Fill style. + * @param {module:ol/style/Stroke} strokeStyle Stroke style. + */ + setFillStrokeStyle(fillStyle, strokeStyle) {} -/** - * @param {module:ol/geom/LineString|module:ol/render/Feature} lineStringGeometry Line string geometry. - * @param {module:ol/Feature|module:ol/render/Feature} feature Feature. - */ -VectorContext.prototype.drawLineString = function(lineStringGeometry, feature) {}; + /** + * @param {module:ol/style/Image} imageStyle Image style. + * @param {module:ol/render/canvas~DeclutterGroup=} opt_declutterGroup Declutter. + */ + setImageStyle(imageStyle, opt_declutterGroup) {} + /** + * @param {module:ol/style/Text} textStyle Text style. + * @param {module:ol/render/canvas~DeclutterGroup=} opt_declutterGroup Declutter. + */ + setTextStyle(textStyle, opt_declutterGroup) {} +} -/** - * @param {module:ol/geom/MultiLineString|module:ol/render/Feature} multiLineStringGeometry MultiLineString geometry. - * @param {module:ol/Feature|module:ol/render/Feature} feature Feature. - */ -VectorContext.prototype.drawMultiLineString = function(multiLineStringGeometry, feature) {}; - - -/** - * @param {module:ol/geom/MultiPoint|module:ol/render/Feature} multiPointGeometry MultiPoint geometry. - * @param {module:ol/Feature|module:ol/render/Feature} feature Feature. - */ -VectorContext.prototype.drawMultiPoint = function(multiPointGeometry, feature) {}; - - -/** - * @param {module:ol/geom/MultiPolygon} multiPolygonGeometry MultiPolygon geometry. - * @param {module:ol/Feature|module:ol/render/Feature} feature Feature. - */ -VectorContext.prototype.drawMultiPolygon = function(multiPolygonGeometry, feature) {}; - - -/** - * @param {module:ol/geom/Point|module:ol/render/Feature} pointGeometry Point geometry. - * @param {module:ol/Feature|module:ol/render/Feature} feature Feature. - */ -VectorContext.prototype.drawPoint = function(pointGeometry, feature) {}; - - -/** - * @param {module:ol/geom/Polygon|module:ol/render/Feature} polygonGeometry Polygon geometry. - * @param {module:ol/Feature|module:ol/render/Feature} feature Feature. - */ -VectorContext.prototype.drawPolygon = function(polygonGeometry, feature) {}; - - -/** - * @param {module:ol/geom/Geometry|module:ol/render/Feature} geometry Geometry. - * @param {module:ol/Feature|module:ol/render/Feature} feature Feature. - */ -VectorContext.prototype.drawText = function(geometry, feature) {}; - - -/** - * @param {module:ol/style/Fill} fillStyle Fill style. - * @param {module:ol/style/Stroke} strokeStyle Stroke style. - */ -VectorContext.prototype.setFillStrokeStyle = function(fillStyle, strokeStyle) {}; - - -/** - * @param {module:ol/style/Image} imageStyle Image style. - * @param {module:ol/render/canvas~DeclutterGroup=} opt_declutterGroup Declutter. - */ -VectorContext.prototype.setImageStyle = function(imageStyle, opt_declutterGroup) {}; - - -/** - * @param {module:ol/style/Text} textStyle Text style. - * @param {module:ol/render/canvas~DeclutterGroup=} opt_declutterGroup Declutter. - */ -VectorContext.prototype.setTextStyle = function(textStyle, opt_declutterGroup) {}; export default VectorContext; diff --git a/src/ol/render/canvas/ImageReplay.js b/src/ol/render/canvas/ImageReplay.js index c17d3f2e44..e39e7009c4 100644 --- a/src/ol/render/canvas/ImageReplay.js +++ b/src/ol/render/canvas/ImageReplay.js @@ -16,218 +16,216 @@ import CanvasReplay from '../canvas/Replay.js'; * @param {?} declutterTree Declutter tree. * @struct */ -const CanvasImageReplay = function( - tolerance, maxExtent, resolution, pixelRatio, overlaps, declutterTree) { - CanvasReplay.call(this, - tolerance, maxExtent, resolution, pixelRatio, overlaps, declutterTree); +class CanvasImageReplay { + constructor(tolerance, maxExtent, resolution, pixelRatio, overlaps, declutterTree) { + CanvasReplay.call(this, + tolerance, maxExtent, resolution, pixelRatio, overlaps, declutterTree); - /** - * @private - * @type {module:ol/render/canvas~DeclutterGroup} - */ - this.declutterGroup_ = null; + /** + * @private + * @type {module:ol/render/canvas~DeclutterGroup} + */ + this.declutterGroup_ = null; - /** - * @private - * @type {HTMLCanvasElement|HTMLVideoElement|HTMLImageElement} - */ - this.hitDetectionImage_ = null; + /** + * @private + * @type {HTMLCanvasElement|HTMLVideoElement|HTMLImageElement} + */ + this.hitDetectionImage_ = null; - /** - * @private - * @type {HTMLCanvasElement|HTMLVideoElement|HTMLImageElement} - */ - this.image_ = null; + /** + * @private + * @type {HTMLCanvasElement|HTMLVideoElement|HTMLImageElement} + */ + this.image_ = null; - /** - * @private - * @type {number|undefined} - */ - this.anchorX_ = undefined; + /** + * @private + * @type {number|undefined} + */ + this.anchorX_ = undefined; - /** - * @private - * @type {number|undefined} - */ - this.anchorY_ = undefined; + /** + * @private + * @type {number|undefined} + */ + this.anchorY_ = undefined; - /** - * @private - * @type {number|undefined} - */ - this.height_ = undefined; + /** + * @private + * @type {number|undefined} + */ + this.height_ = undefined; - /** - * @private - * @type {number|undefined} - */ - this.opacity_ = undefined; + /** + * @private + * @type {number|undefined} + */ + this.opacity_ = undefined; - /** - * @private - * @type {number|undefined} - */ - this.originX_ = undefined; + /** + * @private + * @type {number|undefined} + */ + this.originX_ = undefined; - /** - * @private - * @type {number|undefined} - */ - this.originY_ = undefined; + /** + * @private + * @type {number|undefined} + */ + this.originY_ = undefined; - /** - * @private - * @type {boolean|undefined} - */ - this.rotateWithView_ = undefined; + /** + * @private + * @type {boolean|undefined} + */ + this.rotateWithView_ = undefined; - /** - * @private - * @type {number|undefined} - */ - this.rotation_ = undefined; + /** + * @private + * @type {number|undefined} + */ + this.rotation_ = undefined; - /** - * @private - * @type {number|undefined} - */ - this.scale_ = undefined; + /** + * @private + * @type {number|undefined} + */ + this.scale_ = undefined; - /** - * @private - * @type {boolean|undefined} - */ - this.snapToPixel_ = undefined; + /** + * @private + * @type {boolean|undefined} + */ + this.snapToPixel_ = undefined; - /** - * @private - * @type {number|undefined} - */ - this.width_ = undefined; + /** + * @private + * @type {number|undefined} + */ + this.width_ = undefined; -}; + } + + /** + * @param {Array.} flatCoordinates Flat coordinates. + * @param {number} offset Offset. + * @param {number} end End. + * @param {number} stride Stride. + * @private + * @return {number} My end. + */ + drawCoordinates_(flatCoordinates, offset, end, stride) { + return this.appendFlatCoordinates(flatCoordinates, offset, end, stride, false, false); + } + + /** + * @inheritDoc + */ + drawPoint(pointGeometry, feature) { + if (!this.image_) { + return; + } + this.beginGeometry(pointGeometry, feature); + const flatCoordinates = pointGeometry.getFlatCoordinates(); + const stride = pointGeometry.getStride(); + const myBegin = this.coordinates.length; + const myEnd = this.drawCoordinates_(flatCoordinates, 0, flatCoordinates.length, stride); + this.instructions.push([ + CanvasInstruction.DRAW_IMAGE, myBegin, myEnd, this.image_, + // Remaining arguments to DRAW_IMAGE are in alphabetical order + this.anchorX_, this.anchorY_, this.declutterGroup_, this.height_, this.opacity_, + this.originX_, this.originY_, this.rotateWithView_, this.rotation_, + this.scale_ * this.pixelRatio, this.snapToPixel_, this.width_ + ]); + this.hitDetectionInstructions.push([ + CanvasInstruction.DRAW_IMAGE, myBegin, myEnd, this.hitDetectionImage_, + // Remaining arguments to DRAW_IMAGE are in alphabetical order + this.anchorX_, this.anchorY_, this.declutterGroup_, this.height_, this.opacity_, + this.originX_, this.originY_, this.rotateWithView_, this.rotation_, + this.scale_, this.snapToPixel_, this.width_ + ]); + this.endGeometry(pointGeometry, feature); + } + + /** + * @inheritDoc + */ + drawMultiPoint(multiPointGeometry, feature) { + if (!this.image_) { + return; + } + this.beginGeometry(multiPointGeometry, feature); + const flatCoordinates = multiPointGeometry.getFlatCoordinates(); + const stride = multiPointGeometry.getStride(); + const myBegin = this.coordinates.length; + const myEnd = this.drawCoordinates_( + flatCoordinates, 0, flatCoordinates.length, stride); + this.instructions.push([ + CanvasInstruction.DRAW_IMAGE, myBegin, myEnd, this.image_, + // Remaining arguments to DRAW_IMAGE are in alphabetical order + this.anchorX_, this.anchorY_, this.declutterGroup_, this.height_, this.opacity_, + this.originX_, this.originY_, this.rotateWithView_, this.rotation_, + this.scale_ * this.pixelRatio, this.snapToPixel_, this.width_ + ]); + this.hitDetectionInstructions.push([ + CanvasInstruction.DRAW_IMAGE, myBegin, myEnd, this.hitDetectionImage_, + // Remaining arguments to DRAW_IMAGE are in alphabetical order + this.anchorX_, this.anchorY_, this.declutterGroup_, this.height_, this.opacity_, + this.originX_, this.originY_, this.rotateWithView_, this.rotation_, + this.scale_, this.snapToPixel_, this.width_ + ]); + this.endGeometry(multiPointGeometry, feature); + } + + /** + * @inheritDoc + */ + finish() { + this.reverseHitDetectionInstructions(); + // FIXME this doesn't really protect us against further calls to draw*Geometry + this.anchorX_ = undefined; + this.anchorY_ = undefined; + this.hitDetectionImage_ = null; + this.image_ = null; + this.height_ = undefined; + this.scale_ = undefined; + this.opacity_ = undefined; + this.originX_ = undefined; + this.originY_ = undefined; + this.rotateWithView_ = undefined; + this.rotation_ = undefined; + this.snapToPixel_ = undefined; + this.width_ = undefined; + } + + /** + * @inheritDoc + */ + setImageStyle(imageStyle, declutterGroup) { + const anchor = imageStyle.getAnchor(); + const size = imageStyle.getSize(); + const hitDetectionImage = imageStyle.getHitDetectionImage(1); + const image = imageStyle.getImage(1); + const origin = imageStyle.getOrigin(); + this.anchorX_ = anchor[0]; + this.anchorY_ = anchor[1]; + this.declutterGroup_ = /** @type {module:ol/render/canvas~DeclutterGroup} */ (declutterGroup); + this.hitDetectionImage_ = hitDetectionImage; + this.image_ = image; + this.height_ = size[1]; + this.opacity_ = imageStyle.getOpacity(); + this.originX_ = origin[0]; + this.originY_ = origin[1]; + this.rotateWithView_ = imageStyle.getRotateWithView(); + this.rotation_ = imageStyle.getRotation(); + this.scale_ = imageStyle.getScale(); + this.snapToPixel_ = imageStyle.getSnapToPixel(); + this.width_ = size[0]; + } +} inherits(CanvasImageReplay, CanvasReplay); -/** - * @param {Array.} flatCoordinates Flat coordinates. - * @param {number} offset Offset. - * @param {number} end End. - * @param {number} stride Stride. - * @private - * @return {number} My end. - */ -CanvasImageReplay.prototype.drawCoordinates_ = function(flatCoordinates, offset, end, stride) { - return this.appendFlatCoordinates(flatCoordinates, offset, end, stride, false, false); -}; - - -/** - * @inheritDoc - */ -CanvasImageReplay.prototype.drawPoint = function(pointGeometry, feature) { - if (!this.image_) { - return; - } - this.beginGeometry(pointGeometry, feature); - const flatCoordinates = pointGeometry.getFlatCoordinates(); - const stride = pointGeometry.getStride(); - const myBegin = this.coordinates.length; - const myEnd = this.drawCoordinates_(flatCoordinates, 0, flatCoordinates.length, stride); - this.instructions.push([ - CanvasInstruction.DRAW_IMAGE, myBegin, myEnd, this.image_, - // Remaining arguments to DRAW_IMAGE are in alphabetical order - this.anchorX_, this.anchorY_, this.declutterGroup_, this.height_, this.opacity_, - this.originX_, this.originY_, this.rotateWithView_, this.rotation_, - this.scale_ * this.pixelRatio, this.snapToPixel_, this.width_ - ]); - this.hitDetectionInstructions.push([ - CanvasInstruction.DRAW_IMAGE, myBegin, myEnd, this.hitDetectionImage_, - // Remaining arguments to DRAW_IMAGE are in alphabetical order - this.anchorX_, this.anchorY_, this.declutterGroup_, this.height_, this.opacity_, - this.originX_, this.originY_, this.rotateWithView_, this.rotation_, - this.scale_, this.snapToPixel_, this.width_ - ]); - this.endGeometry(pointGeometry, feature); -}; - - -/** - * @inheritDoc - */ -CanvasImageReplay.prototype.drawMultiPoint = function(multiPointGeometry, feature) { - if (!this.image_) { - return; - } - this.beginGeometry(multiPointGeometry, feature); - const flatCoordinates = multiPointGeometry.getFlatCoordinates(); - const stride = multiPointGeometry.getStride(); - const myBegin = this.coordinates.length; - const myEnd = this.drawCoordinates_( - flatCoordinates, 0, flatCoordinates.length, stride); - this.instructions.push([ - CanvasInstruction.DRAW_IMAGE, myBegin, myEnd, this.image_, - // Remaining arguments to DRAW_IMAGE are in alphabetical order - this.anchorX_, this.anchorY_, this.declutterGroup_, this.height_, this.opacity_, - this.originX_, this.originY_, this.rotateWithView_, this.rotation_, - this.scale_ * this.pixelRatio, this.snapToPixel_, this.width_ - ]); - this.hitDetectionInstructions.push([ - CanvasInstruction.DRAW_IMAGE, myBegin, myEnd, this.hitDetectionImage_, - // Remaining arguments to DRAW_IMAGE are in alphabetical order - this.anchorX_, this.anchorY_, this.declutterGroup_, this.height_, this.opacity_, - this.originX_, this.originY_, this.rotateWithView_, this.rotation_, - this.scale_, this.snapToPixel_, this.width_ - ]); - this.endGeometry(multiPointGeometry, feature); -}; - - -/** - * @inheritDoc - */ -CanvasImageReplay.prototype.finish = function() { - this.reverseHitDetectionInstructions(); - // FIXME this doesn't really protect us against further calls to draw*Geometry - this.anchorX_ = undefined; - this.anchorY_ = undefined; - this.hitDetectionImage_ = null; - this.image_ = null; - this.height_ = undefined; - this.scale_ = undefined; - this.opacity_ = undefined; - this.originX_ = undefined; - this.originY_ = undefined; - this.rotateWithView_ = undefined; - this.rotation_ = undefined; - this.snapToPixel_ = undefined; - this.width_ = undefined; -}; - - -/** - * @inheritDoc - */ -CanvasImageReplay.prototype.setImageStyle = function(imageStyle, declutterGroup) { - const anchor = imageStyle.getAnchor(); - const size = imageStyle.getSize(); - const hitDetectionImage = imageStyle.getHitDetectionImage(1); - const image = imageStyle.getImage(1); - const origin = imageStyle.getOrigin(); - this.anchorX_ = anchor[0]; - this.anchorY_ = anchor[1]; - this.declutterGroup_ = /** @type {module:ol/render/canvas~DeclutterGroup} */ (declutterGroup); - this.hitDetectionImage_ = hitDetectionImage; - this.image_ = image; - this.height_ = size[1]; - this.opacity_ = imageStyle.getOpacity(); - this.originX_ = origin[0]; - this.originY_ = origin[1]; - this.rotateWithView_ = imageStyle.getRotateWithView(); - this.rotation_ = imageStyle.getRotation(); - this.scale_ = imageStyle.getScale(); - this.snapToPixel_ = imageStyle.getSnapToPixel(); - this.width_ = size[0]; -}; export default CanvasImageReplay; diff --git a/src/ol/render/canvas/Immediate.js b/src/ol/render/canvas/Immediate.js index e27881e1f5..8843869e91 100644 --- a/src/ol/render/canvas/Immediate.js +++ b/src/ol/render/canvas/Immediate.js @@ -35,932 +35,915 @@ import {create as createTransform, compose as composeTransform} from '../../tran * @param {number} viewRotation View rotation. * @struct */ -const CanvasImmediateRenderer = function(context, pixelRatio, extent, transform, viewRotation) { - VectorContext.call(this); +class CanvasImmediateRenderer { + constructor(context, pixelRatio, extent, transform, viewRotation) { + VectorContext.call(this); + + /** + * @private + * @type {CanvasRenderingContext2D} + */ + this.context_ = context; + + /** + * @private + * @type {number} + */ + this.pixelRatio_ = pixelRatio; + + /** + * @private + * @type {module:ol/extent~Extent} + */ + this.extent_ = extent; + + /** + * @private + * @type {module:ol/transform~Transform} + */ + this.transform_ = transform; + + /** + * @private + * @type {number} + */ + this.viewRotation_ = viewRotation; + + /** + * @private + * @type {?module:ol/render/canvas~FillState} + */ + this.contextFillState_ = null; + + /** + * @private + * @type {?module:ol/render/canvas~StrokeState} + */ + this.contextStrokeState_ = null; + + /** + * @private + * @type {?module:ol/render/canvas~TextState} + */ + this.contextTextState_ = null; + + /** + * @private + * @type {?module:ol/render/canvas~FillState} + */ + this.fillState_ = null; + + /** + * @private + * @type {?module:ol/render/canvas~StrokeState} + */ + this.strokeState_ = null; + + /** + * @private + * @type {HTMLCanvasElement|HTMLVideoElement|HTMLImageElement} + */ + this.image_ = null; + + /** + * @private + * @type {number} + */ + this.imageAnchorX_ = 0; + + /** + * @private + * @type {number} + */ + this.imageAnchorY_ = 0; + + /** + * @private + * @type {number} + */ + this.imageHeight_ = 0; + + /** + * @private + * @type {number} + */ + this.imageOpacity_ = 0; + + /** + * @private + * @type {number} + */ + this.imageOriginX_ = 0; + + /** + * @private + * @type {number} + */ + this.imageOriginY_ = 0; + + /** + * @private + * @type {boolean} + */ + this.imageRotateWithView_ = false; + + /** + * @private + * @type {number} + */ + this.imageRotation_ = 0; + + /** + * @private + * @type {number} + */ + this.imageScale_ = 0; + + /** + * @private + * @type {boolean} + */ + this.imageSnapToPixel_ = false; + + /** + * @private + * @type {number} + */ + this.imageWidth_ = 0; + + /** + * @private + * @type {string} + */ + this.text_ = ''; + + /** + * @private + * @type {number} + */ + this.textOffsetX_ = 0; + + /** + * @private + * @type {number} + */ + this.textOffsetY_ = 0; + + /** + * @private + * @type {boolean} + */ + this.textRotateWithView_ = false; + + /** + * @private + * @type {number} + */ + this.textRotation_ = 0; + + /** + * @private + * @type {number} + */ + this.textScale_ = 0; + + /** + * @private + * @type {?module:ol/render/canvas~FillState} + */ + this.textFillState_ = null; + + /** + * @private + * @type {?module:ol/render/canvas~StrokeState} + */ + this.textStrokeState_ = null; + + /** + * @private + * @type {?module:ol/render/canvas~TextState} + */ + this.textState_ = null; + + /** + * @private + * @type {Array.} + */ + this.pixelCoordinates_ = []; + + /** + * @private + * @type {module:ol/transform~Transform} + */ + this.tmpLocalTransform_ = createTransform(); + + } /** + * @param {Array.} flatCoordinates Flat coordinates. + * @param {number} offset Offset. + * @param {number} end End. + * @param {number} stride Stride. * @private - * @type {CanvasRenderingContext2D} */ - this.context_ = context; + drawImages_(flatCoordinates, offset, end, stride) { + if (!this.image_) { + return; + } + const pixelCoordinates = transform2D( + flatCoordinates, offset, end, 2, this.transform_, + this.pixelCoordinates_); + const context = this.context_; + const localTransform = this.tmpLocalTransform_; + const alpha = context.globalAlpha; + if (this.imageOpacity_ != 1) { + context.globalAlpha = alpha * this.imageOpacity_; + } + let rotation = this.imageRotation_; + if (this.imageRotateWithView_) { + rotation += this.viewRotation_; + } + for (let i = 0, ii = pixelCoordinates.length; i < ii; i += 2) { + let x = pixelCoordinates[i] - this.imageAnchorX_; + let y = pixelCoordinates[i + 1] - this.imageAnchorY_; + if (this.imageSnapToPixel_) { + x = Math.round(x); + y = Math.round(y); + } + if (rotation !== 0 || this.imageScale_ != 1) { + const centerX = x + this.imageAnchorX_; + const centerY = y + this.imageAnchorY_; + composeTransform(localTransform, + centerX, centerY, + this.imageScale_, this.imageScale_, + rotation, + -centerX, -centerY); + context.setTransform.apply(context, localTransform); + } + context.drawImage(this.image_, this.imageOriginX_, this.imageOriginY_, + this.imageWidth_, this.imageHeight_, x, y, + this.imageWidth_, this.imageHeight_); + } + if (rotation !== 0 || this.imageScale_ != 1) { + context.setTransform(1, 0, 0, 1, 0, 0); + } + if (this.imageOpacity_ != 1) { + context.globalAlpha = alpha; + } + } /** + * @param {Array.} flatCoordinates Flat coordinates. + * @param {number} offset Offset. + * @param {number} end End. + * @param {number} stride Stride. * @private - * @type {number} */ - this.pixelRatio_ = pixelRatio; + drawText_(flatCoordinates, offset, end, stride) { + if (!this.textState_ || this.text_ === '') { + return; + } + if (this.textFillState_) { + this.setContextFillState_(this.textFillState_); + } + if (this.textStrokeState_) { + this.setContextStrokeState_(this.textStrokeState_); + } + this.setContextTextState_(this.textState_); + const pixelCoordinates = transform2D( + flatCoordinates, offset, end, stride, this.transform_, + this.pixelCoordinates_); + const context = this.context_; + let rotation = this.textRotation_; + if (this.textRotateWithView_) { + rotation += this.viewRotation_; + } + for (; offset < end; offset += stride) { + const x = pixelCoordinates[offset] + this.textOffsetX_; + const y = pixelCoordinates[offset + 1] + this.textOffsetY_; + if (rotation !== 0 || this.textScale_ != 1) { + const localTransform = composeTransform(this.tmpLocalTransform_, + x, y, + this.textScale_, this.textScale_, + rotation, + -x, -y); + context.setTransform.apply(context, localTransform); + } + if (this.textStrokeState_) { + context.strokeText(this.text_, x, y); + } + if (this.textFillState_) { + context.fillText(this.text_, x, y); + } + } + if (rotation !== 0 || this.textScale_ != 1) { + context.setTransform(1, 0, 0, 1, 0, 0); + } + } /** + * @param {Array.} flatCoordinates Flat coordinates. + * @param {number} offset Offset. + * @param {number} end End. + * @param {number} stride Stride. + * @param {boolean} close Close. * @private - * @type {module:ol/extent~Extent} + * @return {number} end End. */ - this.extent_ = extent; + moveToLineTo_(flatCoordinates, offset, end, stride, close) { + const context = this.context_; + const pixelCoordinates = transform2D( + flatCoordinates, offset, end, stride, this.transform_, + this.pixelCoordinates_); + context.moveTo(pixelCoordinates[0], pixelCoordinates[1]); + let length = pixelCoordinates.length; + if (close) { + length -= 2; + } + for (let i = 2; i < length; i += 2) { + context.lineTo(pixelCoordinates[i], pixelCoordinates[i + 1]); + } + if (close) { + context.closePath(); + } + return end; + } /** + * @param {Array.} flatCoordinates Flat coordinates. + * @param {number} offset Offset. + * @param {Array.} ends Ends. + * @param {number} stride Stride. * @private - * @type {module:ol/transform~Transform} + * @return {number} End. */ - this.transform_ = transform; + drawRings_(flatCoordinates, offset, ends, stride) { + for (let i = 0, ii = ends.length; i < ii; ++i) { + offset = this.moveToLineTo_(flatCoordinates, offset, ends[i], stride, true); + } + return offset; + } /** - * @private - * @type {number} + * Render a circle geometry into the canvas. Rendering is immediate and uses + * the current fill and stroke styles. + * + * @param {module:ol/geom/Circle} geometry Circle geometry. + * @override + * @api */ - this.viewRotation_ = viewRotation; + drawCircle(geometry) { + if (!intersects(this.extent_, geometry.getExtent())) { + return; + } + if (this.fillState_ || this.strokeState_) { + if (this.fillState_) { + this.setContextFillState_(this.fillState_); + } + if (this.strokeState_) { + this.setContextStrokeState_(this.strokeState_); + } + const pixelCoordinates = transformGeom2D( + geometry, this.transform_, this.pixelCoordinates_); + const dx = pixelCoordinates[2] - pixelCoordinates[0]; + const dy = pixelCoordinates[3] - pixelCoordinates[1]; + const radius = Math.sqrt(dx * dx + dy * dy); + const context = this.context_; + context.beginPath(); + context.arc( + pixelCoordinates[0], pixelCoordinates[1], radius, 0, 2 * Math.PI); + if (this.fillState_) { + context.fill(); + } + if (this.strokeState_) { + context.stroke(); + } + } + if (this.text_ !== '') { + this.drawText_(geometry.getCenter(), 0, 2, 2); + } + } /** - * @private - * @type {?module:ol/render/canvas~FillState} + * Set the rendering style. Note that since this is an immediate rendering API, + * any `zIndex` on the provided style will be ignored. + * + * @param {module:ol/style/Style} style The rendering style. + * @override + * @api */ - this.contextFillState_ = null; + setStyle(style) { + this.setFillStrokeStyle(style.getFill(), style.getStroke()); + this.setImageStyle(style.getImage()); + this.setTextStyle(style.getText()); + } /** - * @private - * @type {?module:ol/render/canvas~StrokeState} + * Render a geometry into the canvas. Call + * {@link module:ol/render/canvas/Immediate#setStyle} first to set the rendering style. + * + * @param {module:ol/geom/Geometry|module:ol/render/Feature} geometry The geometry to render. + * @override + * @api */ - this.contextStrokeState_ = null; + drawGeometry(geometry) { + const type = geometry.getType(); + switch (type) { + case GeometryType.POINT: + this.drawPoint(/** @type {module:ol/geom/Point} */ (geometry)); + break; + case GeometryType.LINE_STRING: + this.drawLineString(/** @type {module:ol/geom/LineString} */ (geometry)); + break; + case GeometryType.POLYGON: + this.drawPolygon(/** @type {module:ol/geom/Polygon} */ (geometry)); + break; + case GeometryType.MULTI_POINT: + this.drawMultiPoint(/** @type {module:ol/geom/MultiPoint} */ (geometry)); + break; + case GeometryType.MULTI_LINE_STRING: + this.drawMultiLineString(/** @type {module:ol/geom/MultiLineString} */ (geometry)); + break; + case GeometryType.MULTI_POLYGON: + this.drawMultiPolygon(/** @type {module:ol/geom/MultiPolygon} */ (geometry)); + break; + case GeometryType.GEOMETRY_COLLECTION: + this.drawGeometryCollection(/** @type {module:ol/geom/GeometryCollection} */ (geometry)); + break; + case GeometryType.CIRCLE: + this.drawCircle(/** @type {module:ol/geom/Circle} */ (geometry)); + break; + default: + } + } /** - * @private - * @type {?module:ol/render/canvas~TextState} + * Render a feature into the canvas. Note that any `zIndex` on the provided + * style will be ignored - features are rendered immediately in the order that + * this method is called. If you need `zIndex` support, you should be using an + * {@link module:ol/layer/Vector~VectorLayer} instead. + * + * @param {module:ol/Feature} feature Feature. + * @param {module:ol/style/Style} style Style. + * @override + * @api */ - this.contextTextState_ = null; + drawFeature(feature, style) { + const geometry = style.getGeometryFunction()(feature); + if (!geometry || !intersects(this.extent_, geometry.getExtent())) { + return; + } + this.setStyle(style); + this.drawGeometry(geometry); + } /** - * @private - * @type {?module:ol/render/canvas~FillState} + * Render a GeometryCollection to the canvas. Rendering is immediate and + * uses the current styles appropriate for each geometry in the collection. + * + * @param {module:ol/geom/GeometryCollection} geometry Geometry collection. + * @override */ - this.fillState_ = null; + drawGeometryCollection(geometry) { + const geometries = geometry.getGeometriesArray(); + for (let i = 0, ii = geometries.length; i < ii; ++i) { + this.drawGeometry(geometries[i]); + } + } /** - * @private - * @type {?module:ol/render/canvas~StrokeState} + * Render a Point geometry into the canvas. Rendering is immediate and uses + * the current style. + * + * @param {module:ol/geom/Point|module:ol/render/Feature} geometry Point geometry. + * @override */ - this.strokeState_ = null; + drawPoint(geometry) { + const flatCoordinates = geometry.getFlatCoordinates(); + const stride = geometry.getStride(); + if (this.image_) { + this.drawImages_(flatCoordinates, 0, flatCoordinates.length, stride); + } + if (this.text_ !== '') { + this.drawText_(flatCoordinates, 0, flatCoordinates.length, stride); + } + } /** - * @private - * @type {HTMLCanvasElement|HTMLVideoElement|HTMLImageElement} + * Render a MultiPoint geometry into the canvas. Rendering is immediate and + * uses the current style. + * + * @param {module:ol/geom/MultiPoint|module:ol/render/Feature} geometry MultiPoint geometry. + * @override */ - this.image_ = null; + drawMultiPoint(geometry) { + const flatCoordinates = geometry.getFlatCoordinates(); + const stride = geometry.getStride(); + if (this.image_) { + this.drawImages_(flatCoordinates, 0, flatCoordinates.length, stride); + } + if (this.text_ !== '') { + this.drawText_(flatCoordinates, 0, flatCoordinates.length, stride); + } + } /** - * @private - * @type {number} + * Render a LineString into the canvas. Rendering is immediate and uses + * the current style. + * + * @param {module:ol/geom/LineString|module:ol/render/Feature} geometry LineString geometry. + * @override */ - this.imageAnchorX_ = 0; + drawLineString(geometry) { + if (!intersects(this.extent_, geometry.getExtent())) { + return; + } + if (this.strokeState_) { + this.setContextStrokeState_(this.strokeState_); + const context = this.context_; + const flatCoordinates = geometry.getFlatCoordinates(); + context.beginPath(); + this.moveToLineTo_(flatCoordinates, 0, flatCoordinates.length, + geometry.getStride(), false); + context.stroke(); + } + if (this.text_ !== '') { + const flatMidpoint = geometry.getFlatMidpoint(); + this.drawText_(flatMidpoint, 0, 2, 2); + } + } /** - * @private - * @type {number} + * Render a MultiLineString geometry into the canvas. Rendering is immediate + * and uses the current style. + * + * @param {module:ol/geom/MultiLineString|module:ol/render/Feature} geometry MultiLineString geometry. + * @override */ - this.imageAnchorY_ = 0; + drawMultiLineString(geometry) { + const geometryExtent = geometry.getExtent(); + if (!intersects(this.extent_, geometryExtent)) { + return; + } + if (this.strokeState_) { + this.setContextStrokeState_(this.strokeState_); + const context = this.context_; + const flatCoordinates = geometry.getFlatCoordinates(); + let offset = 0; + const ends = geometry.getEnds(); + const stride = geometry.getStride(); + context.beginPath(); + for (let i = 0, ii = ends.length; i < ii; ++i) { + offset = this.moveToLineTo_(flatCoordinates, offset, ends[i], stride, false); + } + context.stroke(); + } + if (this.text_ !== '') { + const flatMidpoints = geometry.getFlatMidpoints(); + this.drawText_(flatMidpoints, 0, flatMidpoints.length, 2); + } + } /** - * @private - * @type {number} + * Render a Polygon geometry into the canvas. Rendering is immediate and uses + * the current style. + * + * @param {module:ol/geom/Polygon|module:ol/render/Feature} geometry Polygon geometry. + * @override */ - this.imageHeight_ = 0; + drawPolygon(geometry) { + if (!intersects(this.extent_, geometry.getExtent())) { + return; + } + if (this.strokeState_ || this.fillState_) { + if (this.fillState_) { + this.setContextFillState_(this.fillState_); + } + if (this.strokeState_) { + this.setContextStrokeState_(this.strokeState_); + } + const context = this.context_; + context.beginPath(); + this.drawRings_(geometry.getOrientedFlatCoordinates(), + 0, geometry.getEnds(), geometry.getStride()); + if (this.fillState_) { + context.fill(); + } + if (this.strokeState_) { + context.stroke(); + } + } + if (this.text_ !== '') { + const flatInteriorPoint = geometry.getFlatInteriorPoint(); + this.drawText_(flatInteriorPoint, 0, 2, 2); + } + } /** - * @private - * @type {number} + * Render MultiPolygon geometry into the canvas. Rendering is immediate and + * uses the current style. + * @param {module:ol/geom/MultiPolygon} geometry MultiPolygon geometry. + * @override */ - this.imageOpacity_ = 0; + drawMultiPolygon(geometry) { + if (!intersects(this.extent_, geometry.getExtent())) { + return; + } + if (this.strokeState_ || this.fillState_) { + if (this.fillState_) { + this.setContextFillState_(this.fillState_); + } + if (this.strokeState_) { + this.setContextStrokeState_(this.strokeState_); + } + const context = this.context_; + const flatCoordinates = geometry.getOrientedFlatCoordinates(); + let offset = 0; + const endss = geometry.getEndss(); + const stride = geometry.getStride(); + context.beginPath(); + for (let i = 0, ii = endss.length; i < ii; ++i) { + const ends = endss[i]; + offset = this.drawRings_(flatCoordinates, offset, ends, stride); + } + if (this.fillState_) { + context.fill(); + } + if (this.strokeState_) { + context.stroke(); + } + } + if (this.text_ !== '') { + const flatInteriorPoints = geometry.getFlatInteriorPoints(); + this.drawText_(flatInteriorPoints, 0, flatInteriorPoints.length, 2); + } + } /** + * @param {module:ol/render/canvas~FillState} fillState Fill state. * @private - * @type {number} */ - this.imageOriginX_ = 0; + setContextFillState_(fillState) { + const context = this.context_; + const contextFillState = this.contextFillState_; + if (!contextFillState) { + context.fillStyle = fillState.fillStyle; + this.contextFillState_ = { + fillStyle: fillState.fillStyle + }; + } else { + if (contextFillState.fillStyle != fillState.fillStyle) { + contextFillState.fillStyle = context.fillStyle = fillState.fillStyle; + } + } + } /** + * @param {module:ol/render/canvas~StrokeState} strokeState Stroke state. * @private - * @type {number} */ - this.imageOriginY_ = 0; + setContextStrokeState_(strokeState) { + const context = this.context_; + const contextStrokeState = this.contextStrokeState_; + if (!contextStrokeState) { + context.lineCap = strokeState.lineCap; + if (CANVAS_LINE_DASH) { + context.setLineDash(strokeState.lineDash); + context.lineDashOffset = strokeState.lineDashOffset; + } + context.lineJoin = strokeState.lineJoin; + context.lineWidth = strokeState.lineWidth; + context.miterLimit = strokeState.miterLimit; + context.strokeStyle = strokeState.strokeStyle; + this.contextStrokeState_ = { + lineCap: strokeState.lineCap, + lineDash: strokeState.lineDash, + lineDashOffset: strokeState.lineDashOffset, + lineJoin: strokeState.lineJoin, + lineWidth: strokeState.lineWidth, + miterLimit: strokeState.miterLimit, + strokeStyle: strokeState.strokeStyle + }; + } else { + if (contextStrokeState.lineCap != strokeState.lineCap) { + contextStrokeState.lineCap = context.lineCap = strokeState.lineCap; + } + if (CANVAS_LINE_DASH) { + if (!equals(contextStrokeState.lineDash, strokeState.lineDash)) { + context.setLineDash(contextStrokeState.lineDash = strokeState.lineDash); + } + if (contextStrokeState.lineDashOffset != strokeState.lineDashOffset) { + contextStrokeState.lineDashOffset = context.lineDashOffset = + strokeState.lineDashOffset; + } + } + if (contextStrokeState.lineJoin != strokeState.lineJoin) { + contextStrokeState.lineJoin = context.lineJoin = strokeState.lineJoin; + } + if (contextStrokeState.lineWidth != strokeState.lineWidth) { + contextStrokeState.lineWidth = context.lineWidth = strokeState.lineWidth; + } + if (contextStrokeState.miterLimit != strokeState.miterLimit) { + contextStrokeState.miterLimit = context.miterLimit = + strokeState.miterLimit; + } + if (contextStrokeState.strokeStyle != strokeState.strokeStyle) { + contextStrokeState.strokeStyle = context.strokeStyle = + strokeState.strokeStyle; + } + } + } /** + * @param {module:ol/render/canvas~TextState} textState Text state. * @private - * @type {boolean} */ - this.imageRotateWithView_ = false; + setContextTextState_(textState) { + const context = this.context_; + const contextTextState = this.contextTextState_; + const textAlign = textState.textAlign ? + textState.textAlign : defaultTextAlign; + if (!contextTextState) { + context.font = textState.font; + context.textAlign = textAlign; + context.textBaseline = textState.textBaseline; + this.contextTextState_ = { + font: textState.font, + textAlign: textAlign, + textBaseline: textState.textBaseline + }; + } else { + if (contextTextState.font != textState.font) { + contextTextState.font = context.font = textState.font; + } + if (contextTextState.textAlign != textAlign) { + contextTextState.textAlign = context.textAlign = textAlign; + } + if (contextTextState.textBaseline != textState.textBaseline) { + contextTextState.textBaseline = context.textBaseline = + textState.textBaseline; + } + } + } /** - * @private - * @type {number} + * Set the fill and stroke style for subsequent draw operations. To clear + * either fill or stroke styles, pass null for the appropriate parameter. + * + * @param {module:ol/style/Fill} fillStyle Fill style. + * @param {module:ol/style/Stroke} strokeStyle Stroke style. + * @override */ - this.imageRotation_ = 0; + setFillStrokeStyle(fillStyle, strokeStyle) { + if (!fillStyle) { + this.fillState_ = null; + } else { + const fillStyleColor = fillStyle.getColor(); + this.fillState_ = { + fillStyle: asColorLike(fillStyleColor ? + fillStyleColor : defaultFillStyle) + }; + } + if (!strokeStyle) { + this.strokeState_ = null; + } else { + const strokeStyleColor = strokeStyle.getColor(); + const strokeStyleLineCap = strokeStyle.getLineCap(); + const strokeStyleLineDash = strokeStyle.getLineDash(); + const strokeStyleLineDashOffset = strokeStyle.getLineDashOffset(); + const strokeStyleLineJoin = strokeStyle.getLineJoin(); + const strokeStyleWidth = strokeStyle.getWidth(); + const strokeStyleMiterLimit = strokeStyle.getMiterLimit(); + this.strokeState_ = { + lineCap: strokeStyleLineCap !== undefined ? + strokeStyleLineCap : defaultLineCap, + lineDash: strokeStyleLineDash ? + strokeStyleLineDash : defaultLineDash, + lineDashOffset: strokeStyleLineDashOffset ? + strokeStyleLineDashOffset : defaultLineDashOffset, + lineJoin: strokeStyleLineJoin !== undefined ? + strokeStyleLineJoin : defaultLineJoin, + lineWidth: this.pixelRatio_ * (strokeStyleWidth !== undefined ? + strokeStyleWidth : defaultLineWidth), + miterLimit: strokeStyleMiterLimit !== undefined ? + strokeStyleMiterLimit : defaultMiterLimit, + strokeStyle: asColorLike(strokeStyleColor ? + strokeStyleColor : defaultStrokeStyle) + }; + } + } /** - * @private - * @type {number} + * Set the image style for subsequent draw operations. Pass null to remove + * the image style. + * + * @param {module:ol/style/Image} imageStyle Image style. + * @override */ - this.imageScale_ = 0; + setImageStyle(imageStyle) { + if (!imageStyle) { + this.image_ = null; + } else { + const imageAnchor = imageStyle.getAnchor(); + // FIXME pixel ratio + const imageImage = imageStyle.getImage(1); + const imageOrigin = imageStyle.getOrigin(); + const imageSize = imageStyle.getSize(); + this.imageAnchorX_ = imageAnchor[0]; + this.imageAnchorY_ = imageAnchor[1]; + this.imageHeight_ = imageSize[1]; + this.image_ = imageImage; + this.imageOpacity_ = imageStyle.getOpacity(); + this.imageOriginX_ = imageOrigin[0]; + this.imageOriginY_ = imageOrigin[1]; + this.imageRotateWithView_ = imageStyle.getRotateWithView(); + this.imageRotation_ = imageStyle.getRotation(); + this.imageScale_ = imageStyle.getScale() * this.pixelRatio_; + this.imageSnapToPixel_ = imageStyle.getSnapToPixel(); + this.imageWidth_ = imageSize[0]; + } + } /** - * @private - * @type {boolean} + * Set the text style for subsequent draw operations. Pass null to + * remove the text style. + * + * @param {module:ol/style/Text} textStyle Text style. + * @override */ - this.imageSnapToPixel_ = false; - - /** - * @private - * @type {number} - */ - this.imageWidth_ = 0; - - /** - * @private - * @type {string} - */ - this.text_ = ''; - - /** - * @private - * @type {number} - */ - this.textOffsetX_ = 0; - - /** - * @private - * @type {number} - */ - this.textOffsetY_ = 0; - - /** - * @private - * @type {boolean} - */ - this.textRotateWithView_ = false; - - /** - * @private - * @type {number} - */ - this.textRotation_ = 0; - - /** - * @private - * @type {number} - */ - this.textScale_ = 0; - - /** - * @private - * @type {?module:ol/render/canvas~FillState} - */ - this.textFillState_ = null; - - /** - * @private - * @type {?module:ol/render/canvas~StrokeState} - */ - this.textStrokeState_ = null; - - /** - * @private - * @type {?module:ol/render/canvas~TextState} - */ - this.textState_ = null; - - /** - * @private - * @type {Array.} - */ - this.pixelCoordinates_ = []; - - /** - * @private - * @type {module:ol/transform~Transform} - */ - this.tmpLocalTransform_ = createTransform(); - -}; + setTextStyle(textStyle) { + if (!textStyle) { + this.text_ = ''; + } else { + const textFillStyle = textStyle.getFill(); + if (!textFillStyle) { + this.textFillState_ = null; + } else { + const textFillStyleColor = textFillStyle.getColor(); + this.textFillState_ = { + fillStyle: asColorLike(textFillStyleColor ? + textFillStyleColor : defaultFillStyle) + }; + } + const textStrokeStyle = textStyle.getStroke(); + if (!textStrokeStyle) { + this.textStrokeState_ = null; + } else { + const textStrokeStyleColor = textStrokeStyle.getColor(); + const textStrokeStyleLineCap = textStrokeStyle.getLineCap(); + const textStrokeStyleLineDash = textStrokeStyle.getLineDash(); + const textStrokeStyleLineDashOffset = textStrokeStyle.getLineDashOffset(); + const textStrokeStyleLineJoin = textStrokeStyle.getLineJoin(); + const textStrokeStyleWidth = textStrokeStyle.getWidth(); + const textStrokeStyleMiterLimit = textStrokeStyle.getMiterLimit(); + this.textStrokeState_ = { + lineCap: textStrokeStyleLineCap !== undefined ? + textStrokeStyleLineCap : defaultLineCap, + lineDash: textStrokeStyleLineDash ? + textStrokeStyleLineDash : defaultLineDash, + lineDashOffset: textStrokeStyleLineDashOffset ? + textStrokeStyleLineDashOffset : defaultLineDashOffset, + lineJoin: textStrokeStyleLineJoin !== undefined ? + textStrokeStyleLineJoin : defaultLineJoin, + lineWidth: textStrokeStyleWidth !== undefined ? + textStrokeStyleWidth : defaultLineWidth, + miterLimit: textStrokeStyleMiterLimit !== undefined ? + textStrokeStyleMiterLimit : defaultMiterLimit, + strokeStyle: asColorLike(textStrokeStyleColor ? + textStrokeStyleColor : defaultStrokeStyle) + }; + } + const textFont = textStyle.getFont(); + const textOffsetX = textStyle.getOffsetX(); + const textOffsetY = textStyle.getOffsetY(); + const textRotateWithView = textStyle.getRotateWithView(); + const textRotation = textStyle.getRotation(); + const textScale = textStyle.getScale(); + const textText = textStyle.getText(); + const textTextAlign = textStyle.getTextAlign(); + const textTextBaseline = textStyle.getTextBaseline(); + this.textState_ = { + font: textFont !== undefined ? + textFont : defaultFont, + textAlign: textTextAlign !== undefined ? + textTextAlign : defaultTextAlign, + textBaseline: textTextBaseline !== undefined ? + textTextBaseline : defaultTextBaseline + }; + this.text_ = textText !== undefined ? textText : ''; + this.textOffsetX_ = + textOffsetX !== undefined ? (this.pixelRatio_ * textOffsetX) : 0; + this.textOffsetY_ = + textOffsetY !== undefined ? (this.pixelRatio_ * textOffsetY) : 0; + this.textRotateWithView_ = textRotateWithView !== undefined ? textRotateWithView : false; + this.textRotation_ = textRotation !== undefined ? textRotation : 0; + this.textScale_ = this.pixelRatio_ * (textScale !== undefined ? + textScale : 1); + } + } +} inherits(CanvasImmediateRenderer, VectorContext); -/** - * @param {Array.} flatCoordinates Flat coordinates. - * @param {number} offset Offset. - * @param {number} end End. - * @param {number} stride Stride. - * @private - */ -CanvasImmediateRenderer.prototype.drawImages_ = function(flatCoordinates, offset, end, stride) { - if (!this.image_) { - return; - } - const pixelCoordinates = transform2D( - flatCoordinates, offset, end, 2, this.transform_, - this.pixelCoordinates_); - const context = this.context_; - const localTransform = this.tmpLocalTransform_; - const alpha = context.globalAlpha; - if (this.imageOpacity_ != 1) { - context.globalAlpha = alpha * this.imageOpacity_; - } - let rotation = this.imageRotation_; - if (this.imageRotateWithView_) { - rotation += this.viewRotation_; - } - for (let i = 0, ii = pixelCoordinates.length; i < ii; i += 2) { - let x = pixelCoordinates[i] - this.imageAnchorX_; - let y = pixelCoordinates[i + 1] - this.imageAnchorY_; - if (this.imageSnapToPixel_) { - x = Math.round(x); - y = Math.round(y); - } - if (rotation !== 0 || this.imageScale_ != 1) { - const centerX = x + this.imageAnchorX_; - const centerY = y + this.imageAnchorY_; - composeTransform(localTransform, - centerX, centerY, - this.imageScale_, this.imageScale_, - rotation, - -centerX, -centerY); - context.setTransform.apply(context, localTransform); - } - context.drawImage(this.image_, this.imageOriginX_, this.imageOriginY_, - this.imageWidth_, this.imageHeight_, x, y, - this.imageWidth_, this.imageHeight_); - } - if (rotation !== 0 || this.imageScale_ != 1) { - context.setTransform(1, 0, 0, 1, 0, 0); - } - if (this.imageOpacity_ != 1) { - context.globalAlpha = alpha; - } -}; - - -/** - * @param {Array.} flatCoordinates Flat coordinates. - * @param {number} offset Offset. - * @param {number} end End. - * @param {number} stride Stride. - * @private - */ -CanvasImmediateRenderer.prototype.drawText_ = function(flatCoordinates, offset, end, stride) { - if (!this.textState_ || this.text_ === '') { - return; - } - if (this.textFillState_) { - this.setContextFillState_(this.textFillState_); - } - if (this.textStrokeState_) { - this.setContextStrokeState_(this.textStrokeState_); - } - this.setContextTextState_(this.textState_); - const pixelCoordinates = transform2D( - flatCoordinates, offset, end, stride, this.transform_, - this.pixelCoordinates_); - const context = this.context_; - let rotation = this.textRotation_; - if (this.textRotateWithView_) { - rotation += this.viewRotation_; - } - for (; offset < end; offset += stride) { - const x = pixelCoordinates[offset] + this.textOffsetX_; - const y = pixelCoordinates[offset + 1] + this.textOffsetY_; - if (rotation !== 0 || this.textScale_ != 1) { - const localTransform = composeTransform(this.tmpLocalTransform_, - x, y, - this.textScale_, this.textScale_, - rotation, - -x, -y); - context.setTransform.apply(context, localTransform); - } - if (this.textStrokeState_) { - context.strokeText(this.text_, x, y); - } - if (this.textFillState_) { - context.fillText(this.text_, x, y); - } - } - if (rotation !== 0 || this.textScale_ != 1) { - context.setTransform(1, 0, 0, 1, 0, 0); - } -}; - - -/** - * @param {Array.} flatCoordinates Flat coordinates. - * @param {number} offset Offset. - * @param {number} end End. - * @param {number} stride Stride. - * @param {boolean} close Close. - * @private - * @return {number} end End. - */ -CanvasImmediateRenderer.prototype.moveToLineTo_ = function(flatCoordinates, offset, end, stride, close) { - const context = this.context_; - const pixelCoordinates = transform2D( - flatCoordinates, offset, end, stride, this.transform_, - this.pixelCoordinates_); - context.moveTo(pixelCoordinates[0], pixelCoordinates[1]); - let length = pixelCoordinates.length; - if (close) { - length -= 2; - } - for (let i = 2; i < length; i += 2) { - context.lineTo(pixelCoordinates[i], pixelCoordinates[i + 1]); - } - if (close) { - context.closePath(); - } - return end; -}; - - -/** - * @param {Array.} flatCoordinates Flat coordinates. - * @param {number} offset Offset. - * @param {Array.} ends Ends. - * @param {number} stride Stride. - * @private - * @return {number} End. - */ -CanvasImmediateRenderer.prototype.drawRings_ = function(flatCoordinates, offset, ends, stride) { - for (let i = 0, ii = ends.length; i < ii; ++i) { - offset = this.moveToLineTo_(flatCoordinates, offset, ends[i], stride, true); - } - return offset; -}; - - -/** - * Render a circle geometry into the canvas. Rendering is immediate and uses - * the current fill and stroke styles. - * - * @param {module:ol/geom/Circle} geometry Circle geometry. - * @override - * @api - */ -CanvasImmediateRenderer.prototype.drawCircle = function(geometry) { - if (!intersects(this.extent_, geometry.getExtent())) { - return; - } - if (this.fillState_ || this.strokeState_) { - if (this.fillState_) { - this.setContextFillState_(this.fillState_); - } - if (this.strokeState_) { - this.setContextStrokeState_(this.strokeState_); - } - const pixelCoordinates = transformGeom2D( - geometry, this.transform_, this.pixelCoordinates_); - const dx = pixelCoordinates[2] - pixelCoordinates[0]; - const dy = pixelCoordinates[3] - pixelCoordinates[1]; - const radius = Math.sqrt(dx * dx + dy * dy); - const context = this.context_; - context.beginPath(); - context.arc( - pixelCoordinates[0], pixelCoordinates[1], radius, 0, 2 * Math.PI); - if (this.fillState_) { - context.fill(); - } - if (this.strokeState_) { - context.stroke(); - } - } - if (this.text_ !== '') { - this.drawText_(geometry.getCenter(), 0, 2, 2); - } -}; - - -/** - * Set the rendering style. Note that since this is an immediate rendering API, - * any `zIndex` on the provided style will be ignored. - * - * @param {module:ol/style/Style} style The rendering style. - * @override - * @api - */ -CanvasImmediateRenderer.prototype.setStyle = function(style) { - this.setFillStrokeStyle(style.getFill(), style.getStroke()); - this.setImageStyle(style.getImage()); - this.setTextStyle(style.getText()); -}; - - -/** - * Render a geometry into the canvas. Call - * {@link module:ol/render/canvas/Immediate#setStyle} first to set the rendering style. - * - * @param {module:ol/geom/Geometry|module:ol/render/Feature} geometry The geometry to render. - * @override - * @api - */ -CanvasImmediateRenderer.prototype.drawGeometry = function(geometry) { - const type = geometry.getType(); - switch (type) { - case GeometryType.POINT: - this.drawPoint(/** @type {module:ol/geom/Point} */ (geometry)); - break; - case GeometryType.LINE_STRING: - this.drawLineString(/** @type {module:ol/geom/LineString} */ (geometry)); - break; - case GeometryType.POLYGON: - this.drawPolygon(/** @type {module:ol/geom/Polygon} */ (geometry)); - break; - case GeometryType.MULTI_POINT: - this.drawMultiPoint(/** @type {module:ol/geom/MultiPoint} */ (geometry)); - break; - case GeometryType.MULTI_LINE_STRING: - this.drawMultiLineString(/** @type {module:ol/geom/MultiLineString} */ (geometry)); - break; - case GeometryType.MULTI_POLYGON: - this.drawMultiPolygon(/** @type {module:ol/geom/MultiPolygon} */ (geometry)); - break; - case GeometryType.GEOMETRY_COLLECTION: - this.drawGeometryCollection(/** @type {module:ol/geom/GeometryCollection} */ (geometry)); - break; - case GeometryType.CIRCLE: - this.drawCircle(/** @type {module:ol/geom/Circle} */ (geometry)); - break; - default: - } -}; - - -/** - * Render a feature into the canvas. Note that any `zIndex` on the provided - * style will be ignored - features are rendered immediately in the order that - * this method is called. If you need `zIndex` support, you should be using an - * {@link module:ol/layer/Vector~VectorLayer} instead. - * - * @param {module:ol/Feature} feature Feature. - * @param {module:ol/style/Style} style Style. - * @override - * @api - */ -CanvasImmediateRenderer.prototype.drawFeature = function(feature, style) { - const geometry = style.getGeometryFunction()(feature); - if (!geometry || !intersects(this.extent_, geometry.getExtent())) { - return; - } - this.setStyle(style); - this.drawGeometry(geometry); -}; - - -/** - * Render a GeometryCollection to the canvas. Rendering is immediate and - * uses the current styles appropriate for each geometry in the collection. - * - * @param {module:ol/geom/GeometryCollection} geometry Geometry collection. - * @override - */ -CanvasImmediateRenderer.prototype.drawGeometryCollection = function(geometry) { - const geometries = geometry.getGeometriesArray(); - for (let i = 0, ii = geometries.length; i < ii; ++i) { - this.drawGeometry(geometries[i]); - } -}; - - -/** - * Render a Point geometry into the canvas. Rendering is immediate and uses - * the current style. - * - * @param {module:ol/geom/Point|module:ol/render/Feature} geometry Point geometry. - * @override - */ -CanvasImmediateRenderer.prototype.drawPoint = function(geometry) { - const flatCoordinates = geometry.getFlatCoordinates(); - const stride = geometry.getStride(); - if (this.image_) { - this.drawImages_(flatCoordinates, 0, flatCoordinates.length, stride); - } - if (this.text_ !== '') { - this.drawText_(flatCoordinates, 0, flatCoordinates.length, stride); - } -}; - - -/** - * Render a MultiPoint geometry into the canvas. Rendering is immediate and - * uses the current style. - * - * @param {module:ol/geom/MultiPoint|module:ol/render/Feature} geometry MultiPoint geometry. - * @override - */ -CanvasImmediateRenderer.prototype.drawMultiPoint = function(geometry) { - const flatCoordinates = geometry.getFlatCoordinates(); - const stride = geometry.getStride(); - if (this.image_) { - this.drawImages_(flatCoordinates, 0, flatCoordinates.length, stride); - } - if (this.text_ !== '') { - this.drawText_(flatCoordinates, 0, flatCoordinates.length, stride); - } -}; - - -/** - * Render a LineString into the canvas. Rendering is immediate and uses - * the current style. - * - * @param {module:ol/geom/LineString|module:ol/render/Feature} geometry LineString geometry. - * @override - */ -CanvasImmediateRenderer.prototype.drawLineString = function(geometry) { - if (!intersects(this.extent_, geometry.getExtent())) { - return; - } - if (this.strokeState_) { - this.setContextStrokeState_(this.strokeState_); - const context = this.context_; - const flatCoordinates = geometry.getFlatCoordinates(); - context.beginPath(); - this.moveToLineTo_(flatCoordinates, 0, flatCoordinates.length, - geometry.getStride(), false); - context.stroke(); - } - if (this.text_ !== '') { - const flatMidpoint = geometry.getFlatMidpoint(); - this.drawText_(flatMidpoint, 0, 2, 2); - } -}; - - -/** - * Render a MultiLineString geometry into the canvas. Rendering is immediate - * and uses the current style. - * - * @param {module:ol/geom/MultiLineString|module:ol/render/Feature} geometry MultiLineString geometry. - * @override - */ -CanvasImmediateRenderer.prototype.drawMultiLineString = function(geometry) { - const geometryExtent = geometry.getExtent(); - if (!intersects(this.extent_, geometryExtent)) { - return; - } - if (this.strokeState_) { - this.setContextStrokeState_(this.strokeState_); - const context = this.context_; - const flatCoordinates = geometry.getFlatCoordinates(); - let offset = 0; - const ends = geometry.getEnds(); - const stride = geometry.getStride(); - context.beginPath(); - for (let i = 0, ii = ends.length; i < ii; ++i) { - offset = this.moveToLineTo_(flatCoordinates, offset, ends[i], stride, false); - } - context.stroke(); - } - if (this.text_ !== '') { - const flatMidpoints = geometry.getFlatMidpoints(); - this.drawText_(flatMidpoints, 0, flatMidpoints.length, 2); - } -}; - - -/** - * Render a Polygon geometry into the canvas. Rendering is immediate and uses - * the current style. - * - * @param {module:ol/geom/Polygon|module:ol/render/Feature} geometry Polygon geometry. - * @override - */ -CanvasImmediateRenderer.prototype.drawPolygon = function(geometry) { - if (!intersects(this.extent_, geometry.getExtent())) { - return; - } - if (this.strokeState_ || this.fillState_) { - if (this.fillState_) { - this.setContextFillState_(this.fillState_); - } - if (this.strokeState_) { - this.setContextStrokeState_(this.strokeState_); - } - const context = this.context_; - context.beginPath(); - this.drawRings_(geometry.getOrientedFlatCoordinates(), - 0, geometry.getEnds(), geometry.getStride()); - if (this.fillState_) { - context.fill(); - } - if (this.strokeState_) { - context.stroke(); - } - } - if (this.text_ !== '') { - const flatInteriorPoint = geometry.getFlatInteriorPoint(); - this.drawText_(flatInteriorPoint, 0, 2, 2); - } -}; - - -/** - * Render MultiPolygon geometry into the canvas. Rendering is immediate and - * uses the current style. - * @param {module:ol/geom/MultiPolygon} geometry MultiPolygon geometry. - * @override - */ -CanvasImmediateRenderer.prototype.drawMultiPolygon = function(geometry) { - if (!intersects(this.extent_, geometry.getExtent())) { - return; - } - if (this.strokeState_ || this.fillState_) { - if (this.fillState_) { - this.setContextFillState_(this.fillState_); - } - if (this.strokeState_) { - this.setContextStrokeState_(this.strokeState_); - } - const context = this.context_; - const flatCoordinates = geometry.getOrientedFlatCoordinates(); - let offset = 0; - const endss = geometry.getEndss(); - const stride = geometry.getStride(); - context.beginPath(); - for (let i = 0, ii = endss.length; i < ii; ++i) { - const ends = endss[i]; - offset = this.drawRings_(flatCoordinates, offset, ends, stride); - } - if (this.fillState_) { - context.fill(); - } - if (this.strokeState_) { - context.stroke(); - } - } - if (this.text_ !== '') { - const flatInteriorPoints = geometry.getFlatInteriorPoints(); - this.drawText_(flatInteriorPoints, 0, flatInteriorPoints.length, 2); - } -}; - - -/** - * @param {module:ol/render/canvas~FillState} fillState Fill state. - * @private - */ -CanvasImmediateRenderer.prototype.setContextFillState_ = function(fillState) { - const context = this.context_; - const contextFillState = this.contextFillState_; - if (!contextFillState) { - context.fillStyle = fillState.fillStyle; - this.contextFillState_ = { - fillStyle: fillState.fillStyle - }; - } else { - if (contextFillState.fillStyle != fillState.fillStyle) { - contextFillState.fillStyle = context.fillStyle = fillState.fillStyle; - } - } -}; - - -/** - * @param {module:ol/render/canvas~StrokeState} strokeState Stroke state. - * @private - */ -CanvasImmediateRenderer.prototype.setContextStrokeState_ = function(strokeState) { - const context = this.context_; - const contextStrokeState = this.contextStrokeState_; - if (!contextStrokeState) { - context.lineCap = strokeState.lineCap; - if (CANVAS_LINE_DASH) { - context.setLineDash(strokeState.lineDash); - context.lineDashOffset = strokeState.lineDashOffset; - } - context.lineJoin = strokeState.lineJoin; - context.lineWidth = strokeState.lineWidth; - context.miterLimit = strokeState.miterLimit; - context.strokeStyle = strokeState.strokeStyle; - this.contextStrokeState_ = { - lineCap: strokeState.lineCap, - lineDash: strokeState.lineDash, - lineDashOffset: strokeState.lineDashOffset, - lineJoin: strokeState.lineJoin, - lineWidth: strokeState.lineWidth, - miterLimit: strokeState.miterLimit, - strokeStyle: strokeState.strokeStyle - }; - } else { - if (contextStrokeState.lineCap != strokeState.lineCap) { - contextStrokeState.lineCap = context.lineCap = strokeState.lineCap; - } - if (CANVAS_LINE_DASH) { - if (!equals(contextStrokeState.lineDash, strokeState.lineDash)) { - context.setLineDash(contextStrokeState.lineDash = strokeState.lineDash); - } - if (contextStrokeState.lineDashOffset != strokeState.lineDashOffset) { - contextStrokeState.lineDashOffset = context.lineDashOffset = - strokeState.lineDashOffset; - } - } - if (contextStrokeState.lineJoin != strokeState.lineJoin) { - contextStrokeState.lineJoin = context.lineJoin = strokeState.lineJoin; - } - if (contextStrokeState.lineWidth != strokeState.lineWidth) { - contextStrokeState.lineWidth = context.lineWidth = strokeState.lineWidth; - } - if (contextStrokeState.miterLimit != strokeState.miterLimit) { - contextStrokeState.miterLimit = context.miterLimit = - strokeState.miterLimit; - } - if (contextStrokeState.strokeStyle != strokeState.strokeStyle) { - contextStrokeState.strokeStyle = context.strokeStyle = - strokeState.strokeStyle; - } - } -}; - - -/** - * @param {module:ol/render/canvas~TextState} textState Text state. - * @private - */ -CanvasImmediateRenderer.prototype.setContextTextState_ = function(textState) { - const context = this.context_; - const contextTextState = this.contextTextState_; - const textAlign = textState.textAlign ? - textState.textAlign : defaultTextAlign; - if (!contextTextState) { - context.font = textState.font; - context.textAlign = textAlign; - context.textBaseline = textState.textBaseline; - this.contextTextState_ = { - font: textState.font, - textAlign: textAlign, - textBaseline: textState.textBaseline - }; - } else { - if (contextTextState.font != textState.font) { - contextTextState.font = context.font = textState.font; - } - if (contextTextState.textAlign != textAlign) { - contextTextState.textAlign = context.textAlign = textAlign; - } - if (contextTextState.textBaseline != textState.textBaseline) { - contextTextState.textBaseline = context.textBaseline = - textState.textBaseline; - } - } -}; - - -/** - * Set the fill and stroke style for subsequent draw operations. To clear - * either fill or stroke styles, pass null for the appropriate parameter. - * - * @param {module:ol/style/Fill} fillStyle Fill style. - * @param {module:ol/style/Stroke} strokeStyle Stroke style. - * @override - */ -CanvasImmediateRenderer.prototype.setFillStrokeStyle = function(fillStyle, strokeStyle) { - if (!fillStyle) { - this.fillState_ = null; - } else { - const fillStyleColor = fillStyle.getColor(); - this.fillState_ = { - fillStyle: asColorLike(fillStyleColor ? - fillStyleColor : defaultFillStyle) - }; - } - if (!strokeStyle) { - this.strokeState_ = null; - } else { - const strokeStyleColor = strokeStyle.getColor(); - const strokeStyleLineCap = strokeStyle.getLineCap(); - const strokeStyleLineDash = strokeStyle.getLineDash(); - const strokeStyleLineDashOffset = strokeStyle.getLineDashOffset(); - const strokeStyleLineJoin = strokeStyle.getLineJoin(); - const strokeStyleWidth = strokeStyle.getWidth(); - const strokeStyleMiterLimit = strokeStyle.getMiterLimit(); - this.strokeState_ = { - lineCap: strokeStyleLineCap !== undefined ? - strokeStyleLineCap : defaultLineCap, - lineDash: strokeStyleLineDash ? - strokeStyleLineDash : defaultLineDash, - lineDashOffset: strokeStyleLineDashOffset ? - strokeStyleLineDashOffset : defaultLineDashOffset, - lineJoin: strokeStyleLineJoin !== undefined ? - strokeStyleLineJoin : defaultLineJoin, - lineWidth: this.pixelRatio_ * (strokeStyleWidth !== undefined ? - strokeStyleWidth : defaultLineWidth), - miterLimit: strokeStyleMiterLimit !== undefined ? - strokeStyleMiterLimit : defaultMiterLimit, - strokeStyle: asColorLike(strokeStyleColor ? - strokeStyleColor : defaultStrokeStyle) - }; - } -}; - - -/** - * Set the image style for subsequent draw operations. Pass null to remove - * the image style. - * - * @param {module:ol/style/Image} imageStyle Image style. - * @override - */ -CanvasImmediateRenderer.prototype.setImageStyle = function(imageStyle) { - if (!imageStyle) { - this.image_ = null; - } else { - const imageAnchor = imageStyle.getAnchor(); - // FIXME pixel ratio - const imageImage = imageStyle.getImage(1); - const imageOrigin = imageStyle.getOrigin(); - const imageSize = imageStyle.getSize(); - this.imageAnchorX_ = imageAnchor[0]; - this.imageAnchorY_ = imageAnchor[1]; - this.imageHeight_ = imageSize[1]; - this.image_ = imageImage; - this.imageOpacity_ = imageStyle.getOpacity(); - this.imageOriginX_ = imageOrigin[0]; - this.imageOriginY_ = imageOrigin[1]; - this.imageRotateWithView_ = imageStyle.getRotateWithView(); - this.imageRotation_ = imageStyle.getRotation(); - this.imageScale_ = imageStyle.getScale() * this.pixelRatio_; - this.imageSnapToPixel_ = imageStyle.getSnapToPixel(); - this.imageWidth_ = imageSize[0]; - } -}; - - -/** - * Set the text style for subsequent draw operations. Pass null to - * remove the text style. - * - * @param {module:ol/style/Text} textStyle Text style. - * @override - */ -CanvasImmediateRenderer.prototype.setTextStyle = function(textStyle) { - if (!textStyle) { - this.text_ = ''; - } else { - const textFillStyle = textStyle.getFill(); - if (!textFillStyle) { - this.textFillState_ = null; - } else { - const textFillStyleColor = textFillStyle.getColor(); - this.textFillState_ = { - fillStyle: asColorLike(textFillStyleColor ? - textFillStyleColor : defaultFillStyle) - }; - } - const textStrokeStyle = textStyle.getStroke(); - if (!textStrokeStyle) { - this.textStrokeState_ = null; - } else { - const textStrokeStyleColor = textStrokeStyle.getColor(); - const textStrokeStyleLineCap = textStrokeStyle.getLineCap(); - const textStrokeStyleLineDash = textStrokeStyle.getLineDash(); - const textStrokeStyleLineDashOffset = textStrokeStyle.getLineDashOffset(); - const textStrokeStyleLineJoin = textStrokeStyle.getLineJoin(); - const textStrokeStyleWidth = textStrokeStyle.getWidth(); - const textStrokeStyleMiterLimit = textStrokeStyle.getMiterLimit(); - this.textStrokeState_ = { - lineCap: textStrokeStyleLineCap !== undefined ? - textStrokeStyleLineCap : defaultLineCap, - lineDash: textStrokeStyleLineDash ? - textStrokeStyleLineDash : defaultLineDash, - lineDashOffset: textStrokeStyleLineDashOffset ? - textStrokeStyleLineDashOffset : defaultLineDashOffset, - lineJoin: textStrokeStyleLineJoin !== undefined ? - textStrokeStyleLineJoin : defaultLineJoin, - lineWidth: textStrokeStyleWidth !== undefined ? - textStrokeStyleWidth : defaultLineWidth, - miterLimit: textStrokeStyleMiterLimit !== undefined ? - textStrokeStyleMiterLimit : defaultMiterLimit, - strokeStyle: asColorLike(textStrokeStyleColor ? - textStrokeStyleColor : defaultStrokeStyle) - }; - } - const textFont = textStyle.getFont(); - const textOffsetX = textStyle.getOffsetX(); - const textOffsetY = textStyle.getOffsetY(); - const textRotateWithView = textStyle.getRotateWithView(); - const textRotation = textStyle.getRotation(); - const textScale = textStyle.getScale(); - const textText = textStyle.getText(); - const textTextAlign = textStyle.getTextAlign(); - const textTextBaseline = textStyle.getTextBaseline(); - this.textState_ = { - font: textFont !== undefined ? - textFont : defaultFont, - textAlign: textTextAlign !== undefined ? - textTextAlign : defaultTextAlign, - textBaseline: textTextBaseline !== undefined ? - textTextBaseline : defaultTextBaseline - }; - this.text_ = textText !== undefined ? textText : ''; - this.textOffsetX_ = - textOffsetX !== undefined ? (this.pixelRatio_ * textOffsetX) : 0; - this.textOffsetY_ = - textOffsetY !== undefined ? (this.pixelRatio_ * textOffsetY) : 0; - this.textRotateWithView_ = textRotateWithView !== undefined ? textRotateWithView : false; - this.textRotation_ = textRotation !== undefined ? textRotation : 0; - this.textScale_ = this.pixelRatio_ * (textScale !== undefined ? - textScale : 1); - } -}; export default CanvasImmediateRenderer; diff --git a/src/ol/render/canvas/LineStringReplay.js b/src/ol/render/canvas/LineStringReplay.js index c68da1b111..1e99544fcc 100644 --- a/src/ol/render/canvas/LineStringReplay.js +++ b/src/ol/render/canvas/LineStringReplay.js @@ -16,111 +16,109 @@ import CanvasReplay from '../canvas/Replay.js'; * @param {?} declutterTree Declutter tree. * @struct */ -const CanvasLineStringReplay = function( - tolerance, maxExtent, resolution, pixelRatio, overlaps, declutterTree) { - CanvasReplay.call(this, - tolerance, maxExtent, resolution, pixelRatio, overlaps, declutterTree); -}; +class CanvasLineStringReplay { + constructor(tolerance, maxExtent, resolution, pixelRatio, overlaps, declutterTree) { + CanvasReplay.call(this, + tolerance, maxExtent, resolution, pixelRatio, overlaps, declutterTree); + } + + /** + * @param {Array.} flatCoordinates Flat coordinates. + * @param {number} offset Offset. + * @param {number} end End. + * @param {number} stride Stride. + * @private + * @return {number} end. + */ + drawFlatCoordinates_(flatCoordinates, offset, end, stride) { + const myBegin = this.coordinates.length; + const myEnd = this.appendFlatCoordinates( + flatCoordinates, offset, end, stride, false, false); + const moveToLineToInstruction = [CanvasInstruction.MOVE_TO_LINE_TO, myBegin, myEnd]; + this.instructions.push(moveToLineToInstruction); + this.hitDetectionInstructions.push(moveToLineToInstruction); + return end; + } + + /** + * @inheritDoc + */ + drawLineString(lineStringGeometry, feature) { + const state = this.state; + const strokeStyle = state.strokeStyle; + const lineWidth = state.lineWidth; + if (strokeStyle === undefined || lineWidth === undefined) { + return; + } + this.updateStrokeStyle(state, this.applyStroke); + this.beginGeometry(lineStringGeometry, feature); + this.hitDetectionInstructions.push([ + CanvasInstruction.SET_STROKE_STYLE, + state.strokeStyle, state.lineWidth, state.lineCap, state.lineJoin, + state.miterLimit, state.lineDash, state.lineDashOffset + ], beginPathInstruction); + const flatCoordinates = lineStringGeometry.getFlatCoordinates(); + const stride = lineStringGeometry.getStride(); + this.drawFlatCoordinates_(flatCoordinates, 0, flatCoordinates.length, stride); + this.hitDetectionInstructions.push(strokeInstruction); + this.endGeometry(lineStringGeometry, feature); + } + + /** + * @inheritDoc + */ + drawMultiLineString(multiLineStringGeometry, feature) { + const state = this.state; + const strokeStyle = state.strokeStyle; + const lineWidth = state.lineWidth; + if (strokeStyle === undefined || lineWidth === undefined) { + return; + } + this.updateStrokeStyle(state, this.applyStroke); + this.beginGeometry(multiLineStringGeometry, feature); + this.hitDetectionInstructions.push([ + CanvasInstruction.SET_STROKE_STYLE, + state.strokeStyle, state.lineWidth, state.lineCap, state.lineJoin, + state.miterLimit, state.lineDash, state.lineDashOffset + ], beginPathInstruction); + const ends = multiLineStringGeometry.getEnds(); + const flatCoordinates = multiLineStringGeometry.getFlatCoordinates(); + const stride = multiLineStringGeometry.getStride(); + let offset = 0; + for (let i = 0, ii = ends.length; i < ii; ++i) { + offset = this.drawFlatCoordinates_(flatCoordinates, offset, ends[i], stride); + } + this.hitDetectionInstructions.push(strokeInstruction); + this.endGeometry(multiLineStringGeometry, feature); + } + + /** + * @inheritDoc + */ + finish() { + const state = this.state; + if (state.lastStroke != undefined && state.lastStroke != this.coordinates.length) { + this.instructions.push(strokeInstruction); + } + this.reverseHitDetectionInstructions(); + this.state = null; + } + + /** + * @inheritDoc. + */ + applyStroke(state) { + if (state.lastStroke != undefined && state.lastStroke != this.coordinates.length) { + this.instructions.push(strokeInstruction); + state.lastStroke = this.coordinates.length; + } + state.lastStroke = 0; + CanvasReplay.prototype.applyStroke.call(this, state); + this.instructions.push(beginPathInstruction); + } +} inherits(CanvasLineStringReplay, CanvasReplay); -/** - * @param {Array.} flatCoordinates Flat coordinates. - * @param {number} offset Offset. - * @param {number} end End. - * @param {number} stride Stride. - * @private - * @return {number} end. - */ -CanvasLineStringReplay.prototype.drawFlatCoordinates_ = function(flatCoordinates, offset, end, stride) { - const myBegin = this.coordinates.length; - const myEnd = this.appendFlatCoordinates( - flatCoordinates, offset, end, stride, false, false); - const moveToLineToInstruction = [CanvasInstruction.MOVE_TO_LINE_TO, myBegin, myEnd]; - this.instructions.push(moveToLineToInstruction); - this.hitDetectionInstructions.push(moveToLineToInstruction); - return end; -}; - - -/** - * @inheritDoc - */ -CanvasLineStringReplay.prototype.drawLineString = function(lineStringGeometry, feature) { - const state = this.state; - const strokeStyle = state.strokeStyle; - const lineWidth = state.lineWidth; - if (strokeStyle === undefined || lineWidth === undefined) { - return; - } - this.updateStrokeStyle(state, this.applyStroke); - this.beginGeometry(lineStringGeometry, feature); - this.hitDetectionInstructions.push([ - CanvasInstruction.SET_STROKE_STYLE, - state.strokeStyle, state.lineWidth, state.lineCap, state.lineJoin, - state.miterLimit, state.lineDash, state.lineDashOffset - ], beginPathInstruction); - const flatCoordinates = lineStringGeometry.getFlatCoordinates(); - const stride = lineStringGeometry.getStride(); - this.drawFlatCoordinates_(flatCoordinates, 0, flatCoordinates.length, stride); - this.hitDetectionInstructions.push(strokeInstruction); - this.endGeometry(lineStringGeometry, feature); -}; - - -/** - * @inheritDoc - */ -CanvasLineStringReplay.prototype.drawMultiLineString = function(multiLineStringGeometry, feature) { - const state = this.state; - const strokeStyle = state.strokeStyle; - const lineWidth = state.lineWidth; - if (strokeStyle === undefined || lineWidth === undefined) { - return; - } - this.updateStrokeStyle(state, this.applyStroke); - this.beginGeometry(multiLineStringGeometry, feature); - this.hitDetectionInstructions.push([ - CanvasInstruction.SET_STROKE_STYLE, - state.strokeStyle, state.lineWidth, state.lineCap, state.lineJoin, - state.miterLimit, state.lineDash, state.lineDashOffset - ], beginPathInstruction); - const ends = multiLineStringGeometry.getEnds(); - const flatCoordinates = multiLineStringGeometry.getFlatCoordinates(); - const stride = multiLineStringGeometry.getStride(); - let offset = 0; - for (let i = 0, ii = ends.length; i < ii; ++i) { - offset = this.drawFlatCoordinates_(flatCoordinates, offset, ends[i], stride); - } - this.hitDetectionInstructions.push(strokeInstruction); - this.endGeometry(multiLineStringGeometry, feature); -}; - - -/** - * @inheritDoc - */ -CanvasLineStringReplay.prototype.finish = function() { - const state = this.state; - if (state.lastStroke != undefined && state.lastStroke != this.coordinates.length) { - this.instructions.push(strokeInstruction); - } - this.reverseHitDetectionInstructions(); - this.state = null; -}; - - -/** - * @inheritDoc. - */ -CanvasLineStringReplay.prototype.applyStroke = function(state) { - if (state.lastStroke != undefined && state.lastStroke != this.coordinates.length) { - this.instructions.push(strokeInstruction); - state.lastStroke = this.coordinates.length; - } - state.lastStroke = 0; - CanvasReplay.prototype.applyStroke.call(this, state); - this.instructions.push(beginPathInstruction); -}; export default CanvasLineStringReplay; diff --git a/src/ol/render/canvas/PolygonReplay.js b/src/ol/render/canvas/PolygonReplay.js index d73453a8d0..efb597f3dd 100644 --- a/src/ol/render/canvas/PolygonReplay.js +++ b/src/ol/render/canvas/PolygonReplay.js @@ -22,203 +22,200 @@ import CanvasReplay from '../canvas/Replay.js'; * @param {?} declutterTree Declutter tree. * @struct */ -const CanvasPolygonReplay = function( - tolerance, maxExtent, resolution, pixelRatio, overlaps, declutterTree) { - CanvasReplay.call(this, - tolerance, maxExtent, resolution, pixelRatio, overlaps, declutterTree); -}; +class CanvasPolygonReplay { + constructor(tolerance, maxExtent, resolution, pixelRatio, overlaps, declutterTree) { + CanvasReplay.call(this, + tolerance, maxExtent, resolution, pixelRatio, overlaps, declutterTree); + } + + /** + * @param {Array.} flatCoordinates Flat coordinates. + * @param {number} offset Offset. + * @param {Array.} ends Ends. + * @param {number} stride Stride. + * @private + * @return {number} End. + */ + drawFlatCoordinatess_(flatCoordinates, offset, ends, stride) { + const state = this.state; + const fill = state.fillStyle !== undefined; + const stroke = state.strokeStyle != undefined; + const numEnds = ends.length; + this.instructions.push(beginPathInstruction); + this.hitDetectionInstructions.push(beginPathInstruction); + for (let i = 0; i < numEnds; ++i) { + const end = ends[i]; + const myBegin = this.coordinates.length; + const myEnd = this.appendFlatCoordinates(flatCoordinates, offset, end, stride, true, !stroke); + const moveToLineToInstruction = [CanvasInstruction.MOVE_TO_LINE_TO, myBegin, myEnd]; + this.instructions.push(moveToLineToInstruction); + this.hitDetectionInstructions.push(moveToLineToInstruction); + if (stroke) { + // Performance optimization: only call closePath() when we have a stroke. + // Otherwise the ring is closed already (see appendFlatCoordinates above). + this.instructions.push(closePathInstruction); + this.hitDetectionInstructions.push(closePathInstruction); + } + offset = end; + } + if (fill) { + this.instructions.push(fillInstruction); + this.hitDetectionInstructions.push(fillInstruction); + } + if (stroke) { + this.instructions.push(strokeInstruction); + this.hitDetectionInstructions.push(strokeInstruction); + } + return offset; + } + + /** + * @inheritDoc + */ + drawCircle(circleGeometry, feature) { + const state = this.state; + const fillStyle = state.fillStyle; + const strokeStyle = state.strokeStyle; + if (fillStyle === undefined && strokeStyle === undefined) { + return; + } + this.setFillStrokeStyles_(circleGeometry); + this.beginGeometry(circleGeometry, feature); + if (state.fillStyle !== undefined) { + this.hitDetectionInstructions.push([ + CanvasInstruction.SET_FILL_STYLE, + asString(defaultFillStyle) + ]); + } + if (state.strokeStyle !== undefined) { + this.hitDetectionInstructions.push([ + CanvasInstruction.SET_STROKE_STYLE, + state.strokeStyle, state.lineWidth, state.lineCap, state.lineJoin, + state.miterLimit, state.lineDash, state.lineDashOffset + ]); + } + const flatCoordinates = circleGeometry.getFlatCoordinates(); + const stride = circleGeometry.getStride(); + const myBegin = this.coordinates.length; + this.appendFlatCoordinates( + flatCoordinates, 0, flatCoordinates.length, stride, false, false); + const circleInstruction = [CanvasInstruction.CIRCLE, myBegin]; + this.instructions.push(beginPathInstruction, circleInstruction); + this.hitDetectionInstructions.push(beginPathInstruction, circleInstruction); + this.hitDetectionInstructions.push(fillInstruction); + if (state.fillStyle !== undefined) { + this.instructions.push(fillInstruction); + } + if (state.strokeStyle !== undefined) { + this.instructions.push(strokeInstruction); + this.hitDetectionInstructions.push(strokeInstruction); + } + this.endGeometry(circleGeometry, feature); + } + + /** + * @inheritDoc + */ + drawPolygon(polygonGeometry, feature) { + const state = this.state; + const fillStyle = state.fillStyle; + const strokeStyle = state.strokeStyle; + if (fillStyle === undefined && strokeStyle === undefined) { + return; + } + this.setFillStrokeStyles_(polygonGeometry); + this.beginGeometry(polygonGeometry, feature); + if (state.fillStyle !== undefined) { + this.hitDetectionInstructions.push([ + CanvasInstruction.SET_FILL_STYLE, + asString(defaultFillStyle) + ]); + } + if (state.strokeStyle !== undefined) { + this.hitDetectionInstructions.push([ + CanvasInstruction.SET_STROKE_STYLE, + state.strokeStyle, state.lineWidth, state.lineCap, state.lineJoin, + state.miterLimit, state.lineDash, state.lineDashOffset + ]); + } + const ends = polygonGeometry.getEnds(); + const flatCoordinates = polygonGeometry.getOrientedFlatCoordinates(); + const stride = polygonGeometry.getStride(); + this.drawFlatCoordinatess_(flatCoordinates, 0, ends, stride); + this.endGeometry(polygonGeometry, feature); + } + + /** + * @inheritDoc + */ + drawMultiPolygon(multiPolygonGeometry, feature) { + const state = this.state; + const fillStyle = state.fillStyle; + const strokeStyle = state.strokeStyle; + if (fillStyle === undefined && strokeStyle === undefined) { + return; + } + this.setFillStrokeStyles_(multiPolygonGeometry); + this.beginGeometry(multiPolygonGeometry, feature); + if (state.fillStyle !== undefined) { + this.hitDetectionInstructions.push([ + CanvasInstruction.SET_FILL_STYLE, + asString(defaultFillStyle) + ]); + } + if (state.strokeStyle !== undefined) { + this.hitDetectionInstructions.push([ + CanvasInstruction.SET_STROKE_STYLE, + state.strokeStyle, state.lineWidth, state.lineCap, state.lineJoin, + state.miterLimit, state.lineDash, state.lineDashOffset + ]); + } + const endss = multiPolygonGeometry.getEndss(); + const flatCoordinates = multiPolygonGeometry.getOrientedFlatCoordinates(); + const stride = multiPolygonGeometry.getStride(); + let offset = 0; + for (let i = 0, ii = endss.length; i < ii; ++i) { + offset = this.drawFlatCoordinatess_(flatCoordinates, offset, endss[i], stride); + } + this.endGeometry(multiPolygonGeometry, feature); + } + + /** + * @inheritDoc + */ + finish() { + this.reverseHitDetectionInstructions(); + this.state = null; + // We want to preserve topology when drawing polygons. Polygons are + // simplified using quantization and point elimination. However, we might + // have received a mix of quantized and non-quantized geometries, so ensure + // that all are quantized by quantizing all coordinates in the batch. + const tolerance = this.tolerance; + if (tolerance !== 0) { + const coordinates = this.coordinates; + for (let i = 0, ii = coordinates.length; i < ii; ++i) { + coordinates[i] = snap(coordinates[i], tolerance); + } + } + } + + /** + * @private + * @param {module:ol/geom/Geometry|module:ol/render/Feature} geometry Geometry. + */ + setFillStrokeStyles_(geometry) { + const state = this.state; + const fillStyle = state.fillStyle; + if (fillStyle !== undefined) { + this.updateFillStyle(state, this.createFill, geometry); + } + if (state.strokeStyle !== undefined) { + this.updateStrokeStyle(state, this.applyStroke); + } + } +} inherits(CanvasPolygonReplay, CanvasReplay); -/** - * @param {Array.} flatCoordinates Flat coordinates. - * @param {number} offset Offset. - * @param {Array.} ends Ends. - * @param {number} stride Stride. - * @private - * @return {number} End. - */ -CanvasPolygonReplay.prototype.drawFlatCoordinatess_ = function(flatCoordinates, offset, ends, stride) { - const state = this.state; - const fill = state.fillStyle !== undefined; - const stroke = state.strokeStyle != undefined; - const numEnds = ends.length; - this.instructions.push(beginPathInstruction); - this.hitDetectionInstructions.push(beginPathInstruction); - for (let i = 0; i < numEnds; ++i) { - const end = ends[i]; - const myBegin = this.coordinates.length; - const myEnd = this.appendFlatCoordinates(flatCoordinates, offset, end, stride, true, !stroke); - const moveToLineToInstruction = [CanvasInstruction.MOVE_TO_LINE_TO, myBegin, myEnd]; - this.instructions.push(moveToLineToInstruction); - this.hitDetectionInstructions.push(moveToLineToInstruction); - if (stroke) { - // Performance optimization: only call closePath() when we have a stroke. - // Otherwise the ring is closed already (see appendFlatCoordinates above). - this.instructions.push(closePathInstruction); - this.hitDetectionInstructions.push(closePathInstruction); - } - offset = end; - } - if (fill) { - this.instructions.push(fillInstruction); - this.hitDetectionInstructions.push(fillInstruction); - } - if (stroke) { - this.instructions.push(strokeInstruction); - this.hitDetectionInstructions.push(strokeInstruction); - } - return offset; -}; - - -/** - * @inheritDoc - */ -CanvasPolygonReplay.prototype.drawCircle = function(circleGeometry, feature) { - const state = this.state; - const fillStyle = state.fillStyle; - const strokeStyle = state.strokeStyle; - if (fillStyle === undefined && strokeStyle === undefined) { - return; - } - this.setFillStrokeStyles_(circleGeometry); - this.beginGeometry(circleGeometry, feature); - if (state.fillStyle !== undefined) { - this.hitDetectionInstructions.push([ - CanvasInstruction.SET_FILL_STYLE, - asString(defaultFillStyle) - ]); - } - if (state.strokeStyle !== undefined) { - this.hitDetectionInstructions.push([ - CanvasInstruction.SET_STROKE_STYLE, - state.strokeStyle, state.lineWidth, state.lineCap, state.lineJoin, - state.miterLimit, state.lineDash, state.lineDashOffset - ]); - } - const flatCoordinates = circleGeometry.getFlatCoordinates(); - const stride = circleGeometry.getStride(); - const myBegin = this.coordinates.length; - this.appendFlatCoordinates( - flatCoordinates, 0, flatCoordinates.length, stride, false, false); - const circleInstruction = [CanvasInstruction.CIRCLE, myBegin]; - this.instructions.push(beginPathInstruction, circleInstruction); - this.hitDetectionInstructions.push(beginPathInstruction, circleInstruction); - this.hitDetectionInstructions.push(fillInstruction); - if (state.fillStyle !== undefined) { - this.instructions.push(fillInstruction); - } - if (state.strokeStyle !== undefined) { - this.instructions.push(strokeInstruction); - this.hitDetectionInstructions.push(strokeInstruction); - } - this.endGeometry(circleGeometry, feature); -}; - - -/** - * @inheritDoc - */ -CanvasPolygonReplay.prototype.drawPolygon = function(polygonGeometry, feature) { - const state = this.state; - const fillStyle = state.fillStyle; - const strokeStyle = state.strokeStyle; - if (fillStyle === undefined && strokeStyle === undefined) { - return; - } - this.setFillStrokeStyles_(polygonGeometry); - this.beginGeometry(polygonGeometry, feature); - if (state.fillStyle !== undefined) { - this.hitDetectionInstructions.push([ - CanvasInstruction.SET_FILL_STYLE, - asString(defaultFillStyle) - ]); - } - if (state.strokeStyle !== undefined) { - this.hitDetectionInstructions.push([ - CanvasInstruction.SET_STROKE_STYLE, - state.strokeStyle, state.lineWidth, state.lineCap, state.lineJoin, - state.miterLimit, state.lineDash, state.lineDashOffset - ]); - } - const ends = polygonGeometry.getEnds(); - const flatCoordinates = polygonGeometry.getOrientedFlatCoordinates(); - const stride = polygonGeometry.getStride(); - this.drawFlatCoordinatess_(flatCoordinates, 0, ends, stride); - this.endGeometry(polygonGeometry, feature); -}; - - -/** - * @inheritDoc - */ -CanvasPolygonReplay.prototype.drawMultiPolygon = function(multiPolygonGeometry, feature) { - const state = this.state; - const fillStyle = state.fillStyle; - const strokeStyle = state.strokeStyle; - if (fillStyle === undefined && strokeStyle === undefined) { - return; - } - this.setFillStrokeStyles_(multiPolygonGeometry); - this.beginGeometry(multiPolygonGeometry, feature); - if (state.fillStyle !== undefined) { - this.hitDetectionInstructions.push([ - CanvasInstruction.SET_FILL_STYLE, - asString(defaultFillStyle) - ]); - } - if (state.strokeStyle !== undefined) { - this.hitDetectionInstructions.push([ - CanvasInstruction.SET_STROKE_STYLE, - state.strokeStyle, state.lineWidth, state.lineCap, state.lineJoin, - state.miterLimit, state.lineDash, state.lineDashOffset - ]); - } - const endss = multiPolygonGeometry.getEndss(); - const flatCoordinates = multiPolygonGeometry.getOrientedFlatCoordinates(); - const stride = multiPolygonGeometry.getStride(); - let offset = 0; - for (let i = 0, ii = endss.length; i < ii; ++i) { - offset = this.drawFlatCoordinatess_(flatCoordinates, offset, endss[i], stride); - } - this.endGeometry(multiPolygonGeometry, feature); -}; - - -/** - * @inheritDoc - */ -CanvasPolygonReplay.prototype.finish = function() { - this.reverseHitDetectionInstructions(); - this.state = null; - // We want to preserve topology when drawing polygons. Polygons are - // simplified using quantization and point elimination. However, we might - // have received a mix of quantized and non-quantized geometries, so ensure - // that all are quantized by quantizing all coordinates in the batch. - const tolerance = this.tolerance; - if (tolerance !== 0) { - const coordinates = this.coordinates; - for (let i = 0, ii = coordinates.length; i < ii; ++i) { - coordinates[i] = snap(coordinates[i], tolerance); - } - } -}; - - -/** - * @private - * @param {module:ol/geom/Geometry|module:ol/render/Feature} geometry Geometry. - */ -CanvasPolygonReplay.prototype.setFillStrokeStyles_ = function(geometry) { - const state = this.state; - const fillStyle = state.fillStyle; - if (fillStyle !== undefined) { - this.updateFillStyle(state, this.createFill, geometry); - } - if (state.strokeStyle !== undefined) { - this.updateStrokeStyle(state, this.applyStroke); - } -}; export default CanvasPolygonReplay; diff --git a/src/ol/render/canvas/Replay.js b/src/ol/render/canvas/Replay.js index c54c63edec..398b34bbda 100644 --- a/src/ol/render/canvas/Replay.js +++ b/src/ol/render/canvas/Replay.js @@ -39,125 +39,1035 @@ import { * @param {?} declutterTree Declutter tree. * @struct */ -const CanvasReplay = function(tolerance, maxExtent, resolution, pixelRatio, overlaps, declutterTree) { - VectorContext.call(this); +class CanvasReplay { + constructor(tolerance, maxExtent, resolution, pixelRatio, overlaps, declutterTree) { + VectorContext.call(this); + + /** + * @type {?} + */ + this.declutterTree = declutterTree; + + /** + * @protected + * @type {number} + */ + this.tolerance = tolerance; + + /** + * @protected + * @const + * @type {module:ol/extent~Extent} + */ + this.maxExtent = maxExtent; + + /** + * @protected + * @type {boolean} + */ + this.overlaps = overlaps; + + /** + * @protected + * @type {number} + */ + this.pixelRatio = pixelRatio; + + /** + * @protected + * @type {number} + */ + this.maxLineWidth = 0; + + /** + * @protected + * @const + * @type {number} + */ + this.resolution = resolution; + + /** + * @private + * @type {boolean} + */ + this.alignFill_; + + /** + * @private + * @type {Array.<*>} + */ + this.beginGeometryInstruction1_ = null; + + /** + * @private + * @type {Array.<*>} + */ + this.beginGeometryInstruction2_ = null; + + /** + * @private + * @type {module:ol/extent~Extent} + */ + this.bufferedMaxExtent_ = null; + + /** + * @protected + * @type {Array.<*>} + */ + this.instructions = []; + + /** + * @protected + * @type {Array.} + */ + this.coordinates = []; + + /** + * @private + * @type {!Object.|Array.>>} + */ + this.coordinateCache_ = {}; + + /** + * @private + * @type {!module:ol/transform~Transform} + */ + this.renderedTransform_ = createTransform(); + + /** + * @protected + * @type {Array.<*>} + */ + this.hitDetectionInstructions = []; + + /** + * @private + * @type {Array.} + */ + this.pixelCoordinates_ = null; + + /** + * @protected + * @type {module:ol/render/canvas~FillStrokeState} + */ + this.state = /** @type {module:ol/render/canvas~FillStrokeState} */ ({}); + + /** + * @private + * @type {number} + */ + this.viewRotation_ = 0; + + } /** - * @type {?} + * @param {CanvasRenderingContext2D} context Context. + * @param {module:ol/coordinate~Coordinate} p1 1st point of the background box. + * @param {module:ol/coordinate~Coordinate} p2 2nd point of the background box. + * @param {module:ol/coordinate~Coordinate} p3 3rd point of the background box. + * @param {module:ol/coordinate~Coordinate} p4 4th point of the background box. + * @param {Array.<*>} fillInstruction Fill instruction. + * @param {Array.<*>} strokeInstruction Stroke instruction. */ - this.declutterTree = declutterTree; + replayTextBackground_(context, p1, p2, p3, p4, fillInstruction, strokeInstruction) { + context.beginPath(); + context.moveTo.apply(context, p1); + context.lineTo.apply(context, p2); + context.lineTo.apply(context, p3); + context.lineTo.apply(context, p4); + context.lineTo.apply(context, p1); + if (fillInstruction) { + this.alignFill_ = /** @type {boolean} */ (fillInstruction[2]); + this.fill_(context); + } + if (strokeInstruction) { + this.setStrokeStyle_(context, /** @type {Array.<*>} */ (strokeInstruction)); + context.stroke(); + } + } + + /** + * @param {CanvasRenderingContext2D} context Context. + * @param {number} x X. + * @param {number} y Y. + * @param {HTMLImageElement|HTMLCanvasElement|HTMLVideoElement} image Image. + * @param {number} anchorX Anchor X. + * @param {number} anchorY Anchor Y. + * @param {module:ol/render/canvas~DeclutterGroup} declutterGroup Declutter group. + * @param {number} height Height. + * @param {number} opacity Opacity. + * @param {number} originX Origin X. + * @param {number} originY Origin Y. + * @param {number} rotation Rotation. + * @param {number} scale Scale. + * @param {boolean} snapToPixel Snap to pixel. + * @param {number} width Width. + * @param {Array.} padding Padding. + * @param {Array.<*>} fillInstruction Fill instruction. + * @param {Array.<*>} strokeInstruction Stroke instruction. + */ + replayImage_( + context, + x, + y, + image, + anchorX, + anchorY, + declutterGroup, + height, + opacity, + originX, + originY, + rotation, + scale, + snapToPixel, + width, + padding, + fillInstruction, + strokeInstruction + ) { + const fillStroke = fillInstruction || strokeInstruction; + anchorX *= scale; + anchorY *= scale; + x -= anchorX; + y -= anchorY; + + const w = (width + originX > image.width) ? image.width - originX : width; + const h = (height + originY > image.height) ? image.height - originY : height; + const boxW = padding[3] + w * scale + padding[1]; + const boxH = padding[0] + h * scale + padding[2]; + const boxX = x - padding[3]; + const boxY = y - padding[0]; + + /** @type {module:ol/coordinate~Coordinate} */ + let p1; + /** @type {module:ol/coordinate~Coordinate} */ + let p2; + /** @type {module:ol/coordinate~Coordinate} */ + let p3; + /** @type {module:ol/coordinate~Coordinate} */ + let p4; + if (fillStroke || rotation !== 0) { + p1 = [boxX, boxY]; + p2 = [boxX + boxW, boxY]; + p3 = [boxX + boxW, boxY + boxH]; + p4 = [boxX, boxY + boxH]; + } + + let transform = null; + if (rotation !== 0) { + const centerX = x + anchorX; + const centerY = y + anchorY; + transform = composeTransform(tmpTransform, centerX, centerY, 1, 1, rotation, -centerX, -centerY); + + createOrUpdateEmpty(tmpExtent); + extendCoordinate(tmpExtent, applyTransform(tmpTransform, p1)); + extendCoordinate(tmpExtent, applyTransform(tmpTransform, p2)); + extendCoordinate(tmpExtent, applyTransform(tmpTransform, p3)); + extendCoordinate(tmpExtent, applyTransform(tmpTransform, p4)); + } else { + createOrUpdate(boxX, boxY, boxX + boxW, boxY + boxH, tmpExtent); + } + const canvas = context.canvas; + const strokePadding = strokeInstruction ? (strokeInstruction[2] * scale / 2) : 0; + const intersects = + tmpExtent[0] - strokePadding <= canvas.width && tmpExtent[2] + strokePadding >= 0 && + tmpExtent[1] - strokePadding <= canvas.height && tmpExtent[3] + strokePadding >= 0; + + if (snapToPixel) { + x = Math.round(x); + y = Math.round(y); + } + + if (declutterGroup) { + if (!intersects && declutterGroup[4] == 1) { + return; + } + extend(declutterGroup, tmpExtent); + const declutterArgs = intersects ? + [context, transform ? transform.slice(0) : null, opacity, image, originX, originY, w, h, x, y, scale] : + null; + if (declutterArgs && fillStroke) { + declutterArgs.push(fillInstruction, strokeInstruction, p1, p2, p3, p4); + } + declutterGroup.push(declutterArgs); + } else if (intersects) { + if (fillStroke) { + this.replayTextBackground_(context, p1, p2, p3, p4, + /** @type {Array.<*>} */ (fillInstruction), + /** @type {Array.<*>} */ (strokeInstruction)); + } + drawImage(context, transform, opacity, image, originX, originY, w, h, x, y, scale); + } + } /** * @protected - * @type {number} + * @param {Array.} dashArray Dash array. + * @return {Array.} Dash array with pixel ratio applied */ - this.tolerance = tolerance; + applyPixelRatio(dashArray) { + const pixelRatio = this.pixelRatio; + return pixelRatio == 1 ? dashArray : dashArray.map(function(dash) { + return dash * pixelRatio; + }); + } + + /** + * @param {Array.} flatCoordinates Flat coordinates. + * @param {number} offset Offset. + * @param {number} end End. + * @param {number} stride Stride. + * @param {boolean} closed Last input coordinate equals first. + * @param {boolean} skipFirst Skip first coordinate. + * @protected + * @return {number} My end. + */ + appendFlatCoordinates(flatCoordinates, offset, end, stride, closed, skipFirst) { + + let myEnd = this.coordinates.length; + const extent = this.getBufferedMaxExtent(); + if (skipFirst) { + offset += stride; + } + const lastCoord = [flatCoordinates[offset], flatCoordinates[offset + 1]]; + const nextCoord = [NaN, NaN]; + let skipped = true; + + let i, lastRel, nextRel; + for (i = offset + stride; i < end; i += stride) { + nextCoord[0] = flatCoordinates[i]; + nextCoord[1] = flatCoordinates[i + 1]; + nextRel = coordinateRelationship(extent, nextCoord); + if (nextRel !== lastRel) { + if (skipped) { + this.coordinates[myEnd++] = lastCoord[0]; + this.coordinates[myEnd++] = lastCoord[1]; + } + this.coordinates[myEnd++] = nextCoord[0]; + this.coordinates[myEnd++] = nextCoord[1]; + skipped = false; + } else if (nextRel === Relationship.INTERSECTING) { + this.coordinates[myEnd++] = nextCoord[0]; + this.coordinates[myEnd++] = nextCoord[1]; + skipped = false; + } else { + skipped = true; + } + lastCoord[0] = nextCoord[0]; + lastCoord[1] = nextCoord[1]; + lastRel = nextRel; + } + + // Last coordinate equals first or only one point to append: + if ((closed && skipped) || i === offset + stride) { + this.coordinates[myEnd++] = lastCoord[0]; + this.coordinates[myEnd++] = lastCoord[1]; + } + return myEnd; + } + + /** + * @param {Array.} flatCoordinates Flat coordinates. + * @param {number} offset Offset. + * @param {Array.} ends Ends. + * @param {number} stride Stride. + * @param {Array.} replayEnds Replay ends. + * @return {number} Offset. + */ + drawCustomCoordinates_(flatCoordinates, offset, ends, stride, replayEnds) { + for (let i = 0, ii = ends.length; i < ii; ++i) { + const end = ends[i]; + const replayEnd = this.appendFlatCoordinates(flatCoordinates, offset, end, stride, false, false); + replayEnds.push(replayEnd); + offset = end; + } + return offset; + } + + /** + * @inheritDoc. + */ + drawCustom(geometry, feature, renderer) { + this.beginGeometry(geometry, feature); + const type = geometry.getType(); + const stride = geometry.getStride(); + const replayBegin = this.coordinates.length; + let flatCoordinates, replayEnd, replayEnds, replayEndss; + let offset; + if (type == GeometryType.MULTI_POLYGON) { + geometry = /** @type {module:ol/geom/MultiPolygon} */ (geometry); + flatCoordinates = geometry.getOrientedFlatCoordinates(); + replayEndss = []; + const endss = geometry.getEndss(); + offset = 0; + for (let i = 0, ii = endss.length; i < ii; ++i) { + const myEnds = []; + offset = this.drawCustomCoordinates_(flatCoordinates, offset, endss[i], stride, myEnds); + replayEndss.push(myEnds); + } + this.instructions.push([CanvasInstruction.CUSTOM, + replayBegin, replayEndss, geometry, renderer, inflateMultiCoordinatesArray]); + } else if (type == GeometryType.POLYGON || type == GeometryType.MULTI_LINE_STRING) { + replayEnds = []; + flatCoordinates = (type == GeometryType.POLYGON) ? + /** @type {module:ol/geom/Polygon} */ (geometry).getOrientedFlatCoordinates() : + geometry.getFlatCoordinates(); + offset = this.drawCustomCoordinates_(flatCoordinates, 0, + /** @type {module:ol/geom/Polygon|module:ol/geom/MultiLineString} */ (geometry).getEnds(), + stride, replayEnds); + this.instructions.push([CanvasInstruction.CUSTOM, + replayBegin, replayEnds, geometry, renderer, inflateCoordinatesArray]); + } else if (type == GeometryType.LINE_STRING || type == GeometryType.MULTI_POINT) { + flatCoordinates = geometry.getFlatCoordinates(); + replayEnd = this.appendFlatCoordinates( + flatCoordinates, 0, flatCoordinates.length, stride, false, false); + this.instructions.push([CanvasInstruction.CUSTOM, + replayBegin, replayEnd, geometry, renderer, inflateCoordinates]); + } else if (type == GeometryType.POINT) { + flatCoordinates = geometry.getFlatCoordinates(); + this.coordinates.push(flatCoordinates[0], flatCoordinates[1]); + replayEnd = this.coordinates.length; + this.instructions.push([CanvasInstruction.CUSTOM, + replayBegin, replayEnd, geometry, renderer]); + } + this.endGeometry(geometry, feature); + } /** * @protected - * @const - * @type {module:ol/extent~Extent} + * @param {module:ol/geom/Geometry|module:ol/render/Feature} geometry Geometry. + * @param {module:ol/Feature|module:ol/render/Feature} feature Feature. */ - this.maxExtent = maxExtent; - - /** - * @protected - * @type {boolean} - */ - this.overlaps = overlaps; - - /** - * @protected - * @type {number} - */ - this.pixelRatio = pixelRatio; - - /** - * @protected - * @type {number} - */ - this.maxLineWidth = 0; - - /** - * @protected - * @const - * @type {number} - */ - this.resolution = resolution; + beginGeometry(geometry, feature) { + this.beginGeometryInstruction1_ = [CanvasInstruction.BEGIN_GEOMETRY, feature, 0]; + this.instructions.push(this.beginGeometryInstruction1_); + this.beginGeometryInstruction2_ = [CanvasInstruction.BEGIN_GEOMETRY, feature, 0]; + this.hitDetectionInstructions.push(this.beginGeometryInstruction2_); + } /** * @private - * @type {boolean} + * @param {CanvasRenderingContext2D} context Context. */ - this.alignFill_; + fill_(context) { + if (this.alignFill_) { + const origin = applyTransform(this.renderedTransform_, [0, 0]); + const repeatSize = 512 * this.pixelRatio; + context.translate(origin[0] % repeatSize, origin[1] % repeatSize); + context.rotate(this.viewRotation_); + } + context.fill(); + if (this.alignFill_) { + context.setTransform.apply(context, resetTransform); + } + } /** * @private - * @type {Array.<*>} + * @param {CanvasRenderingContext2D} context Context. + * @param {Array.<*>} instruction Instruction. */ - this.beginGeometryInstruction1_ = null; + setStrokeStyle_(context, instruction) { + context.strokeStyle = /** @type {module:ol/colorlike~ColorLike} */ (instruction[1]); + context.lineWidth = /** @type {number} */ (instruction[2]); + context.lineCap = /** @type {string} */ (instruction[3]); + context.lineJoin = /** @type {string} */ (instruction[4]); + context.miterLimit = /** @type {number} */ (instruction[5]); + if (CANVAS_LINE_DASH) { + context.lineDashOffset = /** @type {number} */ (instruction[7]); + context.setLineDash(/** @type {Array.} */ (instruction[6])); + } + } + + /** + * @param {module:ol/render/canvas~DeclutterGroup} declutterGroup Declutter group. + * @param {module:ol/Feature|module:ol/render/Feature} feature Feature. + */ + renderDeclutter_(declutterGroup, feature) { + if (declutterGroup && declutterGroup.length > 5) { + const groupCount = declutterGroup[4]; + if (groupCount == 1 || groupCount == declutterGroup.length - 5) { + /** @type {module:ol/structs/RBush~Entry} */ + const box = { + minX: /** @type {number} */ (declutterGroup[0]), + minY: /** @type {number} */ (declutterGroup[1]), + maxX: /** @type {number} */ (declutterGroup[2]), + maxY: /** @type {number} */ (declutterGroup[3]), + value: feature + }; + if (!this.declutterTree.collides(box)) { + this.declutterTree.insert(box); + for (let j = 5, jj = declutterGroup.length; j < jj; ++j) { + const declutterData = /** @type {Array} */ (declutterGroup[j]); + if (declutterData) { + if (declutterData.length > 11) { + this.replayTextBackground_(declutterData[0], + declutterData[13], declutterData[14], declutterData[15], declutterData[16], + declutterData[11], declutterData[12]); + } + drawImage.apply(undefined, declutterData); + } + } + } + declutterGroup.length = 5; + createOrUpdateEmpty(declutterGroup); + } + } + } /** * @private - * @type {Array.<*>} + * @param {CanvasRenderingContext2D} context Context. + * @param {module:ol/transform~Transform} transform Transform. + * @param {Object.} skippedFeaturesHash Ids of features + * to skip. + * @param {Array.<*>} instructions Instructions array. + * @param {function((module:ol/Feature|module:ol/render/Feature)): T|undefined} + * featureCallback Feature callback. + * @param {module:ol/extent~Extent=} opt_hitExtent Only check features that intersect this + * extent. + * @return {T|undefined} Callback result. + * @template T */ - this.beginGeometryInstruction2_ = null; + replay_( + context, + transform, + skippedFeaturesHash, + instructions, + featureCallback, + opt_hitExtent + ) { + /** @type {Array.} */ + let pixelCoordinates; + if (this.pixelCoordinates_ && equals(transform, this.renderedTransform_)) { + pixelCoordinates = this.pixelCoordinates_; + } else { + if (!this.pixelCoordinates_) { + this.pixelCoordinates_ = []; + } + pixelCoordinates = transform2D( + this.coordinates, 0, this.coordinates.length, 2, + transform, this.pixelCoordinates_); + transformSetFromArray(this.renderedTransform_, transform); + } + const skipFeatures = !isEmpty(skippedFeaturesHash); + let i = 0; // instruction index + const ii = instructions.length; // end of instructions + let d = 0; // data index + let dd; // end of per-instruction data + let anchorX, anchorY, prevX, prevY, roundX, roundY, declutterGroup, image; + let pendingFill = 0; + let pendingStroke = 0; + let lastFillInstruction = null; + let lastStrokeInstruction = null; + const coordinateCache = this.coordinateCache_; + const viewRotation = this.viewRotation_; + + const state = /** @type {module:ol/render~State} */ ({ + context: context, + pixelRatio: this.pixelRatio, + resolution: this.resolution, + rotation: viewRotation + }); + + // When the batch size gets too big, performance decreases. 200 is a good + // balance between batch size and number of fill/stroke instructions. + const batchSize = this.instructions != instructions || this.overlaps ? 0 : 200; + let /** @type {module:ol/Feature|module:ol/render/Feature} */ feature; + let x, y; + while (i < ii) { + const instruction = instructions[i]; + const type = /** @type {module:ol/render/canvas/Instruction} */ (instruction[0]); + switch (type) { + case CanvasInstruction.BEGIN_GEOMETRY: + feature = /** @type {module:ol/Feature|module:ol/render/Feature} */ (instruction[1]); + if ((skipFeatures && + skippedFeaturesHash[getUid(feature).toString()]) || + !feature.getGeometry()) { + i = /** @type {number} */ (instruction[2]); + } else if (opt_hitExtent !== undefined && !intersects( + opt_hitExtent, feature.getGeometry().getExtent())) { + i = /** @type {number} */ (instruction[2]) + 1; + } else { + ++i; + } + break; + case CanvasInstruction.BEGIN_PATH: + if (pendingFill > batchSize) { + this.fill_(context); + pendingFill = 0; + } + if (pendingStroke > batchSize) { + context.stroke(); + pendingStroke = 0; + } + if (!pendingFill && !pendingStroke) { + context.beginPath(); + prevX = prevY = NaN; + } + ++i; + break; + case CanvasInstruction.CIRCLE: + d = /** @type {number} */ (instruction[1]); + const x1 = pixelCoordinates[d]; + const y1 = pixelCoordinates[d + 1]; + const x2 = pixelCoordinates[d + 2]; + const y2 = pixelCoordinates[d + 3]; + const dx = x2 - x1; + const dy = y2 - y1; + const r = Math.sqrt(dx * dx + dy * dy); + context.moveTo(x1 + r, y1); + context.arc(x1, y1, r, 0, 2 * Math.PI, true); + ++i; + break; + case CanvasInstruction.CLOSE_PATH: + context.closePath(); + ++i; + break; + case CanvasInstruction.CUSTOM: + d = /** @type {number} */ (instruction[1]); + dd = instruction[2]; + const geometry = /** @type {module:ol/geom/SimpleGeometry} */ (instruction[3]); + const renderer = instruction[4]; + const fn = instruction.length == 6 ? instruction[5] : undefined; + state.geometry = geometry; + state.feature = feature; + if (!(i in coordinateCache)) { + coordinateCache[i] = []; + } + const coords = coordinateCache[i]; + if (fn) { + fn(pixelCoordinates, d, dd, 2, coords); + } else { + coords[0] = pixelCoordinates[d]; + coords[1] = pixelCoordinates[d + 1]; + coords.length = 2; + } + renderer(coords, state); + ++i; + break; + case CanvasInstruction.DRAW_IMAGE: + d = /** @type {number} */ (instruction[1]); + dd = /** @type {number} */ (instruction[2]); + image = /** @type {HTMLCanvasElement|HTMLVideoElement|HTMLImageElement} */ + (instruction[3]); + // Remaining arguments in DRAW_IMAGE are in alphabetical order + anchorX = /** @type {number} */ (instruction[4]); + anchorY = /** @type {number} */ (instruction[5]); + declutterGroup = featureCallback ? null : /** @type {module:ol/render/canvas~DeclutterGroup} */ (instruction[6]); + const height = /** @type {number} */ (instruction[7]); + const opacity = /** @type {number} */ (instruction[8]); + const originX = /** @type {number} */ (instruction[9]); + const originY = /** @type {number} */ (instruction[10]); + const rotateWithView = /** @type {boolean} */ (instruction[11]); + let rotation = /** @type {number} */ (instruction[12]); + const scale = /** @type {number} */ (instruction[13]); + const snapToPixel = /** @type {boolean} */ (instruction[14]); + const width = /** @type {number} */ (instruction[15]); + + let padding, backgroundFill, backgroundStroke; + if (instruction.length > 16) { + padding = /** @type {Array.} */ (instruction[16]); + backgroundFill = /** @type {boolean} */ (instruction[17]); + backgroundStroke = /** @type {boolean} */ (instruction[18]); + } else { + padding = defaultPadding; + backgroundFill = backgroundStroke = false; + } + + if (rotateWithView) { + rotation += viewRotation; + } + for (; d < dd; d += 2) { + this.replayImage_(context, + pixelCoordinates[d], pixelCoordinates[d + 1], image, anchorX, anchorY, + declutterGroup, height, opacity, originX, originY, rotation, scale, + snapToPixel, width, padding, + backgroundFill ? /** @type {Array.<*>} */ (lastFillInstruction) : null, + backgroundStroke ? /** @type {Array.<*>} */ (lastStrokeInstruction) : null); + } + this.renderDeclutter_(declutterGroup, feature); + ++i; + break; + case CanvasInstruction.DRAW_CHARS: + const begin = /** @type {number} */ (instruction[1]); + const end = /** @type {number} */ (instruction[2]); + const baseline = /** @type {number} */ (instruction[3]); + declutterGroup = featureCallback ? null : /** @type {module:ol/render/canvas~DeclutterGroup} */ (instruction[4]); + const overflow = /** @type {number} */ (instruction[5]); + const fillKey = /** @type {string} */ (instruction[6]); + const maxAngle = /** @type {number} */ (instruction[7]); + const measure = /** @type {function(string):number} */ (instruction[8]); + const offsetY = /** @type {number} */ (instruction[9]); + const strokeKey = /** @type {string} */ (instruction[10]); + const strokeWidth = /** @type {number} */ (instruction[11]); + const text = /** @type {string} */ (instruction[12]); + const textKey = /** @type {string} */ (instruction[13]); + const textScale = /** @type {number} */ (instruction[14]); + + const pathLength = lineStringLength(pixelCoordinates, begin, end, 2); + const textLength = measure(text); + if (overflow || textLength <= pathLength) { + const textAlign = /** @type {module:ol~render} */ (this).textStates[textKey].textAlign; + const startM = (pathLength - textLength) * TEXT_ALIGN[textAlign]; + const parts = drawTextOnPath( + pixelCoordinates, begin, end, 2, text, measure, startM, maxAngle); + if (parts) { + let c, cc, chars, label, part; + if (strokeKey) { + for (c = 0, cc = parts.length; c < cc; ++c) { + part = parts[c]; // x, y, anchorX, rotation, chunk + chars = /** @type {string} */ (part[4]); + label = /** @type {module:ol~render} */ (this).getImage(chars, textKey, '', strokeKey); + anchorX = /** @type {number} */ (part[2]) + strokeWidth; + anchorY = baseline * label.height + (0.5 - baseline) * 2 * strokeWidth - offsetY; + this.replayImage_(context, + /** @type {number} */ (part[0]), /** @type {number} */ (part[1]), label, + anchorX, anchorY, declutterGroup, label.height, 1, 0, 0, + /** @type {number} */ (part[3]), textScale, false, label.width, + defaultPadding, null, null); + } + } + if (fillKey) { + for (c = 0, cc = parts.length; c < cc; ++c) { + part = parts[c]; // x, y, anchorX, rotation, chunk + chars = /** @type {string} */ (part[4]); + label = /** @type {module:ol~render} */ (this).getImage(chars, textKey, fillKey, ''); + anchorX = /** @type {number} */ (part[2]); + anchorY = baseline * label.height - offsetY; + this.replayImage_(context, + /** @type {number} */ (part[0]), /** @type {number} */ (part[1]), label, + anchorX, anchorY, declutterGroup, label.height, 1, 0, 0, + /** @type {number} */ (part[3]), textScale, false, label.width, + defaultPadding, null, null); + } + } + } + } + this.renderDeclutter_(declutterGroup, feature); + ++i; + break; + case CanvasInstruction.END_GEOMETRY: + if (featureCallback !== undefined) { + feature = /** @type {module:ol/Feature|module:ol/render/Feature} */ (instruction[1]); + const result = featureCallback(feature); + if (result) { + return result; + } + } + ++i; + break; + case CanvasInstruction.FILL: + if (batchSize) { + pendingFill++; + } else { + this.fill_(context); + } + ++i; + break; + case CanvasInstruction.MOVE_TO_LINE_TO: + d = /** @type {number} */ (instruction[1]); + dd = /** @type {number} */ (instruction[2]); + x = pixelCoordinates[d]; + y = pixelCoordinates[d + 1]; + roundX = (x + 0.5) | 0; + roundY = (y + 0.5) | 0; + if (roundX !== prevX || roundY !== prevY) { + context.moveTo(x, y); + prevX = roundX; + prevY = roundY; + } + for (d += 2; d < dd; d += 2) { + x = pixelCoordinates[d]; + y = pixelCoordinates[d + 1]; + roundX = (x + 0.5) | 0; + roundY = (y + 0.5) | 0; + if (d == dd - 2 || roundX !== prevX || roundY !== prevY) { + context.lineTo(x, y); + prevX = roundX; + prevY = roundY; + } + } + ++i; + break; + case CanvasInstruction.SET_FILL_STYLE: + lastFillInstruction = instruction; + this.alignFill_ = instruction[2]; + + if (pendingFill) { + this.fill_(context); + pendingFill = 0; + if (pendingStroke) { + context.stroke(); + pendingStroke = 0; + } + } + + context.fillStyle = /** @type {module:ol/colorlike~ColorLike} */ (instruction[1]); + ++i; + break; + case CanvasInstruction.SET_STROKE_STYLE: + lastStrokeInstruction = instruction; + if (pendingStroke) { + context.stroke(); + pendingStroke = 0; + } + this.setStrokeStyle_(context, /** @type {Array.<*>} */ (instruction)); + ++i; + break; + case CanvasInstruction.STROKE: + if (batchSize) { + pendingStroke++; + } else { + context.stroke(); + } + ++i; + break; + default: + ++i; // consume the instruction anyway, to avoid an infinite loop + break; + } + } + if (pendingFill) { + this.fill_(context); + } + if (pendingStroke) { + context.stroke(); + } + return undefined; + } /** - * @private - * @type {module:ol/extent~Extent} + * @param {CanvasRenderingContext2D} context Context. + * @param {module:ol/transform~Transform} transform Transform. + * @param {number} viewRotation View rotation. + * @param {Object.} skippedFeaturesHash Ids of features + * to skip. */ - this.bufferedMaxExtent_ = null; + replay(context, transform, viewRotation, skippedFeaturesHash) { + this.viewRotation_ = viewRotation; + this.replay_(context, transform, + skippedFeaturesHash, this.instructions, undefined, undefined); + } /** + * @param {CanvasRenderingContext2D} context Context. + * @param {module:ol/transform~Transform} transform Transform. + * @param {number} viewRotation View rotation. + * @param {Object.} skippedFeaturesHash Ids of features + * to skip. + * @param {function((module:ol/Feature|module:ol/render/Feature)): T=} opt_featureCallback + * Feature callback. + * @param {module:ol/extent~Extent=} opt_hitExtent Only check features that intersect this + * extent. + * @return {T|undefined} Callback result. + * @template T + */ + replayHitDetection( + context, + transform, + viewRotation, + skippedFeaturesHash, + opt_featureCallback, + opt_hitExtent + ) { + this.viewRotation_ = viewRotation; + return this.replay_(context, transform, skippedFeaturesHash, + this.hitDetectionInstructions, opt_featureCallback, opt_hitExtent); + } + + /** + * Reverse the hit detection instructions. + */ + reverseHitDetectionInstructions() { + const hitDetectionInstructions = this.hitDetectionInstructions; + // step 1 - reverse array + hitDetectionInstructions.reverse(); + // step 2 - reverse instructions within geometry blocks + let i; + const n = hitDetectionInstructions.length; + let instruction; + let type; + let begin = -1; + for (i = 0; i < n; ++i) { + instruction = hitDetectionInstructions[i]; + type = /** @type {module:ol/render/canvas/Instruction} */ (instruction[0]); + if (type == CanvasInstruction.END_GEOMETRY) { + begin = i; + } else if (type == CanvasInstruction.BEGIN_GEOMETRY) { + instruction[2] = i; + reverseSubArray(this.hitDetectionInstructions, begin, i); + begin = -1; + } + } + } + + /** + * @inheritDoc + */ + setFillStrokeStyle(fillStyle, strokeStyle) { + const state = this.state; + if (fillStyle) { + const fillStyleColor = fillStyle.getColor(); + state.fillStyle = asColorLike(fillStyleColor ? + fillStyleColor : defaultFillStyle); + } else { + state.fillStyle = undefined; + } + if (strokeStyle) { + const strokeStyleColor = strokeStyle.getColor(); + state.strokeStyle = asColorLike(strokeStyleColor ? + strokeStyleColor : defaultStrokeStyle); + const strokeStyleLineCap = strokeStyle.getLineCap(); + state.lineCap = strokeStyleLineCap !== undefined ? + strokeStyleLineCap : defaultLineCap; + const strokeStyleLineDash = strokeStyle.getLineDash(); + state.lineDash = strokeStyleLineDash ? + strokeStyleLineDash.slice() : defaultLineDash; + const strokeStyleLineDashOffset = strokeStyle.getLineDashOffset(); + state.lineDashOffset = strokeStyleLineDashOffset ? + strokeStyleLineDashOffset : defaultLineDashOffset; + const strokeStyleLineJoin = strokeStyle.getLineJoin(); + state.lineJoin = strokeStyleLineJoin !== undefined ? + strokeStyleLineJoin : defaultLineJoin; + const strokeStyleWidth = strokeStyle.getWidth(); + state.lineWidth = strokeStyleWidth !== undefined ? + strokeStyleWidth : defaultLineWidth; + const strokeStyleMiterLimit = strokeStyle.getMiterLimit(); + state.miterLimit = strokeStyleMiterLimit !== undefined ? + strokeStyleMiterLimit : defaultMiterLimit; + + if (state.lineWidth > this.maxLineWidth) { + this.maxLineWidth = state.lineWidth; + // invalidate the buffered max extent cache + this.bufferedMaxExtent_ = null; + } + } else { + state.strokeStyle = undefined; + state.lineCap = undefined; + state.lineDash = null; + state.lineDashOffset = undefined; + state.lineJoin = undefined; + state.lineWidth = undefined; + state.miterLimit = undefined; + } + } + + /** + * @param {module:ol/render/canvas~FillStrokeState} state State. + * @param {module:ol/geom/Geometry|module:ol/render/Feature} geometry Geometry. + * @return {Array.<*>} Fill instruction. + */ + createFill(state, geometry) { + const fillStyle = state.fillStyle; + const fillInstruction = [CanvasInstruction.SET_FILL_STYLE, fillStyle]; + if (typeof fillStyle !== 'string') { + // Fill is a pattern or gradient - align it! + fillInstruction.push(true); + } + return fillInstruction; + } + + /** + * @param {module:ol/render/canvas~FillStrokeState} state State. + */ + applyStroke(state) { + this.instructions.push(this.createStroke(state)); + } + + /** + * @param {module:ol/render/canvas~FillStrokeState} state State. + * @return {Array.<*>} Stroke instruction. + */ + createStroke(state) { + return [ + CanvasInstruction.SET_STROKE_STYLE, + state.strokeStyle, state.lineWidth * this.pixelRatio, state.lineCap, + state.lineJoin, state.miterLimit, + this.applyPixelRatio(state.lineDash), state.lineDashOffset * this.pixelRatio + ]; + } + + /** + * @param {module:ol/render/canvas~FillStrokeState} state State. + * @param {function(this:module:ol/render/canvas/Replay, module:ol/render/canvas~FillStrokeState, (module:ol/geom/Geometry|module:ol/render/Feature)):Array.<*>} createFill Create fill. + * @param {module:ol/geom/Geometry|module:ol/render/Feature} geometry Geometry. + */ + updateFillStyle(state, createFill, geometry) { + const fillStyle = state.fillStyle; + if (typeof fillStyle !== 'string' || state.currentFillStyle != fillStyle) { + if (fillStyle !== undefined) { + this.instructions.push(createFill.call(this, state, geometry)); + } + state.currentFillStyle = fillStyle; + } + } + + /** + * @param {module:ol/render/canvas~FillStrokeState} state State. + * @param {function(this:module:ol/render/canvas/Replay, module:ol/render/canvas~FillStrokeState)} applyStroke Apply stroke. + */ + updateStrokeStyle(state, applyStroke) { + const strokeStyle = state.strokeStyle; + const lineCap = state.lineCap; + const lineDash = state.lineDash; + const lineDashOffset = state.lineDashOffset; + const lineJoin = state.lineJoin; + const lineWidth = state.lineWidth; + const miterLimit = state.miterLimit; + if (state.currentStrokeStyle != strokeStyle || + state.currentLineCap != lineCap || + (lineDash != state.currentLineDash && !equals(state.currentLineDash, lineDash)) || + state.currentLineDashOffset != lineDashOffset || + state.currentLineJoin != lineJoin || + state.currentLineWidth != lineWidth || + state.currentMiterLimit != miterLimit) { + if (strokeStyle !== undefined) { + applyStroke.call(this, state); + } + state.currentStrokeStyle = strokeStyle; + state.currentLineCap = lineCap; + state.currentLineDash = lineDash; + state.currentLineDashOffset = lineDashOffset; + state.currentLineJoin = lineJoin; + state.currentLineWidth = lineWidth; + state.currentMiterLimit = miterLimit; + } + } + + /** + * @param {module:ol/geom/Geometry|module:ol/render/Feature} geometry Geometry. + * @param {module:ol/Feature|module:ol/render/Feature} feature Feature. + */ + endGeometry(geometry, feature) { + this.beginGeometryInstruction1_[2] = this.instructions.length; + this.beginGeometryInstruction1_ = null; + this.beginGeometryInstruction2_[2] = this.hitDetectionInstructions.length; + this.beginGeometryInstruction2_ = null; + const endGeometryInstruction = [CanvasInstruction.END_GEOMETRY, feature]; + this.instructions.push(endGeometryInstruction); + this.hitDetectionInstructions.push(endGeometryInstruction); + } + + /** + * Get the buffered rendering extent. Rendering will be clipped to the extent + * provided to the constructor. To account for symbolizers that may intersect + * this extent, we calculate a buffered extent (e.g. based on stroke width). + * @return {module:ol/extent~Extent} The buffered rendering extent. * @protected - * @type {Array.<*>} */ - this.instructions = []; - - /** - * @protected - * @type {Array.} - */ - this.coordinates = []; - - /** - * @private - * @type {!Object.|Array.>>} - */ - this.coordinateCache_ = {}; - - /** - * @private - * @type {!module:ol/transform~Transform} - */ - this.renderedTransform_ = createTransform(); - - /** - * @protected - * @type {Array.<*>} - */ - this.hitDetectionInstructions = []; - - /** - * @private - * @type {Array.} - */ - this.pixelCoordinates_ = null; - - /** - * @protected - * @type {module:ol/render/canvas~FillStrokeState} - */ - this.state = /** @type {module:ol/render/canvas~FillStrokeState} */ ({}); - - /** - * @private - * @type {number} - */ - this.viewRotation_ = 0; - -}; + getBufferedMaxExtent() { + if (!this.bufferedMaxExtent_) { + this.bufferedMaxExtent_ = clone(this.maxExtent); + if (this.maxLineWidth > 0) { + const width = this.resolution * (this.maxLineWidth + 1) / 2; + buffer(this.bufferedMaxExtent_, width, this.bufferedMaxExtent_); + } + } + return this.bufferedMaxExtent_; + } +} inherits(CanvasReplay, VectorContext); @@ -173,891 +1083,6 @@ const tmpExtent = createEmpty(); */ const tmpTransform = createTransform(); -/** - * @param {CanvasRenderingContext2D} context Context. - * @param {module:ol/coordinate~Coordinate} p1 1st point of the background box. - * @param {module:ol/coordinate~Coordinate} p2 2nd point of the background box. - * @param {module:ol/coordinate~Coordinate} p3 3rd point of the background box. - * @param {module:ol/coordinate~Coordinate} p4 4th point of the background box. - * @param {Array.<*>} fillInstruction Fill instruction. - * @param {Array.<*>} strokeInstruction Stroke instruction. - */ -CanvasReplay.prototype.replayTextBackground_ = function(context, p1, p2, p3, p4, - fillInstruction, strokeInstruction) { - context.beginPath(); - context.moveTo.apply(context, p1); - context.lineTo.apply(context, p2); - context.lineTo.apply(context, p3); - context.lineTo.apply(context, p4); - context.lineTo.apply(context, p1); - if (fillInstruction) { - this.alignFill_ = /** @type {boolean} */ (fillInstruction[2]); - this.fill_(context); - } - if (strokeInstruction) { - this.setStrokeStyle_(context, /** @type {Array.<*>} */ (strokeInstruction)); - context.stroke(); - } -}; - - -/** - * @param {CanvasRenderingContext2D} context Context. - * @param {number} x X. - * @param {number} y Y. - * @param {HTMLImageElement|HTMLCanvasElement|HTMLVideoElement} image Image. - * @param {number} anchorX Anchor X. - * @param {number} anchorY Anchor Y. - * @param {module:ol/render/canvas~DeclutterGroup} declutterGroup Declutter group. - * @param {number} height Height. - * @param {number} opacity Opacity. - * @param {number} originX Origin X. - * @param {number} originY Origin Y. - * @param {number} rotation Rotation. - * @param {number} scale Scale. - * @param {boolean} snapToPixel Snap to pixel. - * @param {number} width Width. - * @param {Array.} padding Padding. - * @param {Array.<*>} fillInstruction Fill instruction. - * @param {Array.<*>} strokeInstruction Stroke instruction. - */ -CanvasReplay.prototype.replayImage_ = function(context, x, y, image, - anchorX, anchorY, declutterGroup, height, opacity, originX, originY, - rotation, scale, snapToPixel, width, padding, fillInstruction, strokeInstruction) { - const fillStroke = fillInstruction || strokeInstruction; - anchorX *= scale; - anchorY *= scale; - x -= anchorX; - y -= anchorY; - - const w = (width + originX > image.width) ? image.width - originX : width; - const h = (height + originY > image.height) ? image.height - originY : height; - const boxW = padding[3] + w * scale + padding[1]; - const boxH = padding[0] + h * scale + padding[2]; - const boxX = x - padding[3]; - const boxY = y - padding[0]; - - /** @type {module:ol/coordinate~Coordinate} */ - let p1; - /** @type {module:ol/coordinate~Coordinate} */ - let p2; - /** @type {module:ol/coordinate~Coordinate} */ - let p3; - /** @type {module:ol/coordinate~Coordinate} */ - let p4; - if (fillStroke || rotation !== 0) { - p1 = [boxX, boxY]; - p2 = [boxX + boxW, boxY]; - p3 = [boxX + boxW, boxY + boxH]; - p4 = [boxX, boxY + boxH]; - } - - let transform = null; - if (rotation !== 0) { - const centerX = x + anchorX; - const centerY = y + anchorY; - transform = composeTransform(tmpTransform, centerX, centerY, 1, 1, rotation, -centerX, -centerY); - - createOrUpdateEmpty(tmpExtent); - extendCoordinate(tmpExtent, applyTransform(tmpTransform, p1)); - extendCoordinate(tmpExtent, applyTransform(tmpTransform, p2)); - extendCoordinate(tmpExtent, applyTransform(tmpTransform, p3)); - extendCoordinate(tmpExtent, applyTransform(tmpTransform, p4)); - } else { - createOrUpdate(boxX, boxY, boxX + boxW, boxY + boxH, tmpExtent); - } - const canvas = context.canvas; - const strokePadding = strokeInstruction ? (strokeInstruction[2] * scale / 2) : 0; - const intersects = - tmpExtent[0] - strokePadding <= canvas.width && tmpExtent[2] + strokePadding >= 0 && - tmpExtent[1] - strokePadding <= canvas.height && tmpExtent[3] + strokePadding >= 0; - - if (snapToPixel) { - x = Math.round(x); - y = Math.round(y); - } - - if (declutterGroup) { - if (!intersects && declutterGroup[4] == 1) { - return; - } - extend(declutterGroup, tmpExtent); - const declutterArgs = intersects ? - [context, transform ? transform.slice(0) : null, opacity, image, originX, originY, w, h, x, y, scale] : - null; - if (declutterArgs && fillStroke) { - declutterArgs.push(fillInstruction, strokeInstruction, p1, p2, p3, p4); - } - declutterGroup.push(declutterArgs); - } else if (intersects) { - if (fillStroke) { - this.replayTextBackground_(context, p1, p2, p3, p4, - /** @type {Array.<*>} */ (fillInstruction), - /** @type {Array.<*>} */ (strokeInstruction)); - } - drawImage(context, transform, opacity, image, originX, originY, w, h, x, y, scale); - } -}; - - -/** - * @protected - * @param {Array.} dashArray Dash array. - * @return {Array.} Dash array with pixel ratio applied - */ -CanvasReplay.prototype.applyPixelRatio = function(dashArray) { - const pixelRatio = this.pixelRatio; - return pixelRatio == 1 ? dashArray : dashArray.map(function(dash) { - return dash * pixelRatio; - }); -}; - - -/** - * @param {Array.} flatCoordinates Flat coordinates. - * @param {number} offset Offset. - * @param {number} end End. - * @param {number} stride Stride. - * @param {boolean} closed Last input coordinate equals first. - * @param {boolean} skipFirst Skip first coordinate. - * @protected - * @return {number} My end. - */ -CanvasReplay.prototype.appendFlatCoordinates = function(flatCoordinates, offset, end, stride, closed, skipFirst) { - - let myEnd = this.coordinates.length; - const extent = this.getBufferedMaxExtent(); - if (skipFirst) { - offset += stride; - } - const lastCoord = [flatCoordinates[offset], flatCoordinates[offset + 1]]; - const nextCoord = [NaN, NaN]; - let skipped = true; - - let i, lastRel, nextRel; - for (i = offset + stride; i < end; i += stride) { - nextCoord[0] = flatCoordinates[i]; - nextCoord[1] = flatCoordinates[i + 1]; - nextRel = coordinateRelationship(extent, nextCoord); - if (nextRel !== lastRel) { - if (skipped) { - this.coordinates[myEnd++] = lastCoord[0]; - this.coordinates[myEnd++] = lastCoord[1]; - } - this.coordinates[myEnd++] = nextCoord[0]; - this.coordinates[myEnd++] = nextCoord[1]; - skipped = false; - } else if (nextRel === Relationship.INTERSECTING) { - this.coordinates[myEnd++] = nextCoord[0]; - this.coordinates[myEnd++] = nextCoord[1]; - skipped = false; - } else { - skipped = true; - } - lastCoord[0] = nextCoord[0]; - lastCoord[1] = nextCoord[1]; - lastRel = nextRel; - } - - // Last coordinate equals first or only one point to append: - if ((closed && skipped) || i === offset + stride) { - this.coordinates[myEnd++] = lastCoord[0]; - this.coordinates[myEnd++] = lastCoord[1]; - } - return myEnd; -}; - - -/** - * @param {Array.} flatCoordinates Flat coordinates. - * @param {number} offset Offset. - * @param {Array.} ends Ends. - * @param {number} stride Stride. - * @param {Array.} replayEnds Replay ends. - * @return {number} Offset. - */ -CanvasReplay.prototype.drawCustomCoordinates_ = function(flatCoordinates, offset, ends, stride, replayEnds) { - for (let i = 0, ii = ends.length; i < ii; ++i) { - const end = ends[i]; - const replayEnd = this.appendFlatCoordinates(flatCoordinates, offset, end, stride, false, false); - replayEnds.push(replayEnd); - offset = end; - } - return offset; -}; - - -/** - * @inheritDoc. - */ -CanvasReplay.prototype.drawCustom = function(geometry, feature, renderer) { - this.beginGeometry(geometry, feature); - const type = geometry.getType(); - const stride = geometry.getStride(); - const replayBegin = this.coordinates.length; - let flatCoordinates, replayEnd, replayEnds, replayEndss; - let offset; - if (type == GeometryType.MULTI_POLYGON) { - geometry = /** @type {module:ol/geom/MultiPolygon} */ (geometry); - flatCoordinates = geometry.getOrientedFlatCoordinates(); - replayEndss = []; - const endss = geometry.getEndss(); - offset = 0; - for (let i = 0, ii = endss.length; i < ii; ++i) { - const myEnds = []; - offset = this.drawCustomCoordinates_(flatCoordinates, offset, endss[i], stride, myEnds); - replayEndss.push(myEnds); - } - this.instructions.push([CanvasInstruction.CUSTOM, - replayBegin, replayEndss, geometry, renderer, inflateMultiCoordinatesArray]); - } else if (type == GeometryType.POLYGON || type == GeometryType.MULTI_LINE_STRING) { - replayEnds = []; - flatCoordinates = (type == GeometryType.POLYGON) ? - /** @type {module:ol/geom/Polygon} */ (geometry).getOrientedFlatCoordinates() : - geometry.getFlatCoordinates(); - offset = this.drawCustomCoordinates_(flatCoordinates, 0, - /** @type {module:ol/geom/Polygon|module:ol/geom/MultiLineString} */ (geometry).getEnds(), - stride, replayEnds); - this.instructions.push([CanvasInstruction.CUSTOM, - replayBegin, replayEnds, geometry, renderer, inflateCoordinatesArray]); - } else if (type == GeometryType.LINE_STRING || type == GeometryType.MULTI_POINT) { - flatCoordinates = geometry.getFlatCoordinates(); - replayEnd = this.appendFlatCoordinates( - flatCoordinates, 0, flatCoordinates.length, stride, false, false); - this.instructions.push([CanvasInstruction.CUSTOM, - replayBegin, replayEnd, geometry, renderer, inflateCoordinates]); - } else if (type == GeometryType.POINT) { - flatCoordinates = geometry.getFlatCoordinates(); - this.coordinates.push(flatCoordinates[0], flatCoordinates[1]); - replayEnd = this.coordinates.length; - this.instructions.push([CanvasInstruction.CUSTOM, - replayBegin, replayEnd, geometry, renderer]); - } - this.endGeometry(geometry, feature); -}; - - -/** - * @protected - * @param {module:ol/geom/Geometry|module:ol/render/Feature} geometry Geometry. - * @param {module:ol/Feature|module:ol/render/Feature} feature Feature. - */ -CanvasReplay.prototype.beginGeometry = function(geometry, feature) { - this.beginGeometryInstruction1_ = [CanvasInstruction.BEGIN_GEOMETRY, feature, 0]; - this.instructions.push(this.beginGeometryInstruction1_); - this.beginGeometryInstruction2_ = [CanvasInstruction.BEGIN_GEOMETRY, feature, 0]; - this.hitDetectionInstructions.push(this.beginGeometryInstruction2_); -}; - - -/** - * @private - * @param {CanvasRenderingContext2D} context Context. - */ -CanvasReplay.prototype.fill_ = function(context) { - if (this.alignFill_) { - const origin = applyTransform(this.renderedTransform_, [0, 0]); - const repeatSize = 512 * this.pixelRatio; - context.translate(origin[0] % repeatSize, origin[1] % repeatSize); - context.rotate(this.viewRotation_); - } - context.fill(); - if (this.alignFill_) { - context.setTransform.apply(context, resetTransform); - } -}; - - -/** - * @private - * @param {CanvasRenderingContext2D} context Context. - * @param {Array.<*>} instruction Instruction. - */ -CanvasReplay.prototype.setStrokeStyle_ = function(context, instruction) { - context.strokeStyle = /** @type {module:ol/colorlike~ColorLike} */ (instruction[1]); - context.lineWidth = /** @type {number} */ (instruction[2]); - context.lineCap = /** @type {string} */ (instruction[3]); - context.lineJoin = /** @type {string} */ (instruction[4]); - context.miterLimit = /** @type {number} */ (instruction[5]); - if (CANVAS_LINE_DASH) { - context.lineDashOffset = /** @type {number} */ (instruction[7]); - context.setLineDash(/** @type {Array.} */ (instruction[6])); - } -}; - - -/** - * @param {module:ol/render/canvas~DeclutterGroup} declutterGroup Declutter group. - * @param {module:ol/Feature|module:ol/render/Feature} feature Feature. - */ -CanvasReplay.prototype.renderDeclutter_ = function(declutterGroup, feature) { - if (declutterGroup && declutterGroup.length > 5) { - const groupCount = declutterGroup[4]; - if (groupCount == 1 || groupCount == declutterGroup.length - 5) { - /** @type {module:ol/structs/RBush~Entry} */ - const box = { - minX: /** @type {number} */ (declutterGroup[0]), - minY: /** @type {number} */ (declutterGroup[1]), - maxX: /** @type {number} */ (declutterGroup[2]), - maxY: /** @type {number} */ (declutterGroup[3]), - value: feature - }; - if (!this.declutterTree.collides(box)) { - this.declutterTree.insert(box); - for (let j = 5, jj = declutterGroup.length; j < jj; ++j) { - const declutterData = /** @type {Array} */ (declutterGroup[j]); - if (declutterData) { - if (declutterData.length > 11) { - this.replayTextBackground_(declutterData[0], - declutterData[13], declutterData[14], declutterData[15], declutterData[16], - declutterData[11], declutterData[12]); - } - drawImage.apply(undefined, declutterData); - } - } - } - declutterGroup.length = 5; - createOrUpdateEmpty(declutterGroup); - } - } -}; - - -/** - * @private - * @param {CanvasRenderingContext2D} context Context. - * @param {module:ol/transform~Transform} transform Transform. - * @param {Object.} skippedFeaturesHash Ids of features - * to skip. - * @param {Array.<*>} instructions Instructions array. - * @param {function((module:ol/Feature|module:ol/render/Feature)): T|undefined} - * featureCallback Feature callback. - * @param {module:ol/extent~Extent=} opt_hitExtent Only check features that intersect this - * extent. - * @return {T|undefined} Callback result. - * @template T - */ -CanvasReplay.prototype.replay_ = function( - context, transform, skippedFeaturesHash, - instructions, featureCallback, opt_hitExtent) { - /** @type {Array.} */ - let pixelCoordinates; - if (this.pixelCoordinates_ && equals(transform, this.renderedTransform_)) { - pixelCoordinates = this.pixelCoordinates_; - } else { - if (!this.pixelCoordinates_) { - this.pixelCoordinates_ = []; - } - pixelCoordinates = transform2D( - this.coordinates, 0, this.coordinates.length, 2, - transform, this.pixelCoordinates_); - transformSetFromArray(this.renderedTransform_, transform); - } - const skipFeatures = !isEmpty(skippedFeaturesHash); - let i = 0; // instruction index - const ii = instructions.length; // end of instructions - let d = 0; // data index - let dd; // end of per-instruction data - let anchorX, anchorY, prevX, prevY, roundX, roundY, declutterGroup, image; - let pendingFill = 0; - let pendingStroke = 0; - let lastFillInstruction = null; - let lastStrokeInstruction = null; - const coordinateCache = this.coordinateCache_; - const viewRotation = this.viewRotation_; - - const state = /** @type {module:ol/render~State} */ ({ - context: context, - pixelRatio: this.pixelRatio, - resolution: this.resolution, - rotation: viewRotation - }); - - // When the batch size gets too big, performance decreases. 200 is a good - // balance between batch size and number of fill/stroke instructions. - const batchSize = this.instructions != instructions || this.overlaps ? 0 : 200; - let /** @type {module:ol/Feature|module:ol/render/Feature} */ feature; - let x, y; - while (i < ii) { - const instruction = instructions[i]; - const type = /** @type {module:ol/render/canvas/Instruction} */ (instruction[0]); - switch (type) { - case CanvasInstruction.BEGIN_GEOMETRY: - feature = /** @type {module:ol/Feature|module:ol/render/Feature} */ (instruction[1]); - if ((skipFeatures && - skippedFeaturesHash[getUid(feature).toString()]) || - !feature.getGeometry()) { - i = /** @type {number} */ (instruction[2]); - } else if (opt_hitExtent !== undefined && !intersects( - opt_hitExtent, feature.getGeometry().getExtent())) { - i = /** @type {number} */ (instruction[2]) + 1; - } else { - ++i; - } - break; - case CanvasInstruction.BEGIN_PATH: - if (pendingFill > batchSize) { - this.fill_(context); - pendingFill = 0; - } - if (pendingStroke > batchSize) { - context.stroke(); - pendingStroke = 0; - } - if (!pendingFill && !pendingStroke) { - context.beginPath(); - prevX = prevY = NaN; - } - ++i; - break; - case CanvasInstruction.CIRCLE: - d = /** @type {number} */ (instruction[1]); - const x1 = pixelCoordinates[d]; - const y1 = pixelCoordinates[d + 1]; - const x2 = pixelCoordinates[d + 2]; - const y2 = pixelCoordinates[d + 3]; - const dx = x2 - x1; - const dy = y2 - y1; - const r = Math.sqrt(dx * dx + dy * dy); - context.moveTo(x1 + r, y1); - context.arc(x1, y1, r, 0, 2 * Math.PI, true); - ++i; - break; - case CanvasInstruction.CLOSE_PATH: - context.closePath(); - ++i; - break; - case CanvasInstruction.CUSTOM: - d = /** @type {number} */ (instruction[1]); - dd = instruction[2]; - const geometry = /** @type {module:ol/geom/SimpleGeometry} */ (instruction[3]); - const renderer = instruction[4]; - const fn = instruction.length == 6 ? instruction[5] : undefined; - state.geometry = geometry; - state.feature = feature; - if (!(i in coordinateCache)) { - coordinateCache[i] = []; - } - const coords = coordinateCache[i]; - if (fn) { - fn(pixelCoordinates, d, dd, 2, coords); - } else { - coords[0] = pixelCoordinates[d]; - coords[1] = pixelCoordinates[d + 1]; - coords.length = 2; - } - renderer(coords, state); - ++i; - break; - case CanvasInstruction.DRAW_IMAGE: - d = /** @type {number} */ (instruction[1]); - dd = /** @type {number} */ (instruction[2]); - image = /** @type {HTMLCanvasElement|HTMLVideoElement|HTMLImageElement} */ - (instruction[3]); - // Remaining arguments in DRAW_IMAGE are in alphabetical order - anchorX = /** @type {number} */ (instruction[4]); - anchorY = /** @type {number} */ (instruction[5]); - declutterGroup = featureCallback ? null : /** @type {module:ol/render/canvas~DeclutterGroup} */ (instruction[6]); - const height = /** @type {number} */ (instruction[7]); - const opacity = /** @type {number} */ (instruction[8]); - const originX = /** @type {number} */ (instruction[9]); - const originY = /** @type {number} */ (instruction[10]); - const rotateWithView = /** @type {boolean} */ (instruction[11]); - let rotation = /** @type {number} */ (instruction[12]); - const scale = /** @type {number} */ (instruction[13]); - const snapToPixel = /** @type {boolean} */ (instruction[14]); - const width = /** @type {number} */ (instruction[15]); - - let padding, backgroundFill, backgroundStroke; - if (instruction.length > 16) { - padding = /** @type {Array.} */ (instruction[16]); - backgroundFill = /** @type {boolean} */ (instruction[17]); - backgroundStroke = /** @type {boolean} */ (instruction[18]); - } else { - padding = defaultPadding; - backgroundFill = backgroundStroke = false; - } - - if (rotateWithView) { - rotation += viewRotation; - } - for (; d < dd; d += 2) { - this.replayImage_(context, - pixelCoordinates[d], pixelCoordinates[d + 1], image, anchorX, anchorY, - declutterGroup, height, opacity, originX, originY, rotation, scale, - snapToPixel, width, padding, - backgroundFill ? /** @type {Array.<*>} */ (lastFillInstruction) : null, - backgroundStroke ? /** @type {Array.<*>} */ (lastStrokeInstruction) : null); - } - this.renderDeclutter_(declutterGroup, feature); - ++i; - break; - case CanvasInstruction.DRAW_CHARS: - const begin = /** @type {number} */ (instruction[1]); - const end = /** @type {number} */ (instruction[2]); - const baseline = /** @type {number} */ (instruction[3]); - declutterGroup = featureCallback ? null : /** @type {module:ol/render/canvas~DeclutterGroup} */ (instruction[4]); - const overflow = /** @type {number} */ (instruction[5]); - const fillKey = /** @type {string} */ (instruction[6]); - const maxAngle = /** @type {number} */ (instruction[7]); - const measure = /** @type {function(string):number} */ (instruction[8]); - const offsetY = /** @type {number} */ (instruction[9]); - const strokeKey = /** @type {string} */ (instruction[10]); - const strokeWidth = /** @type {number} */ (instruction[11]); - const text = /** @type {string} */ (instruction[12]); - const textKey = /** @type {string} */ (instruction[13]); - const textScale = /** @type {number} */ (instruction[14]); - - const pathLength = lineStringLength(pixelCoordinates, begin, end, 2); - const textLength = measure(text); - if (overflow || textLength <= pathLength) { - const textAlign = /** @type {module:ol~render} */ (this).textStates[textKey].textAlign; - const startM = (pathLength - textLength) * TEXT_ALIGN[textAlign]; - const parts = drawTextOnPath( - pixelCoordinates, begin, end, 2, text, measure, startM, maxAngle); - if (parts) { - let c, cc, chars, label, part; - if (strokeKey) { - for (c = 0, cc = parts.length; c < cc; ++c) { - part = parts[c]; // x, y, anchorX, rotation, chunk - chars = /** @type {string} */ (part[4]); - label = /** @type {module:ol~render} */ (this).getImage(chars, textKey, '', strokeKey); - anchorX = /** @type {number} */ (part[2]) + strokeWidth; - anchorY = baseline * label.height + (0.5 - baseline) * 2 * strokeWidth - offsetY; - this.replayImage_(context, - /** @type {number} */ (part[0]), /** @type {number} */ (part[1]), label, - anchorX, anchorY, declutterGroup, label.height, 1, 0, 0, - /** @type {number} */ (part[3]), textScale, false, label.width, - defaultPadding, null, null); - } - } - if (fillKey) { - for (c = 0, cc = parts.length; c < cc; ++c) { - part = parts[c]; // x, y, anchorX, rotation, chunk - chars = /** @type {string} */ (part[4]); - label = /** @type {module:ol~render} */ (this).getImage(chars, textKey, fillKey, ''); - anchorX = /** @type {number} */ (part[2]); - anchorY = baseline * label.height - offsetY; - this.replayImage_(context, - /** @type {number} */ (part[0]), /** @type {number} */ (part[1]), label, - anchorX, anchorY, declutterGroup, label.height, 1, 0, 0, - /** @type {number} */ (part[3]), textScale, false, label.width, - defaultPadding, null, null); - } - } - } - } - this.renderDeclutter_(declutterGroup, feature); - ++i; - break; - case CanvasInstruction.END_GEOMETRY: - if (featureCallback !== undefined) { - feature = /** @type {module:ol/Feature|module:ol/render/Feature} */ (instruction[1]); - const result = featureCallback(feature); - if (result) { - return result; - } - } - ++i; - break; - case CanvasInstruction.FILL: - if (batchSize) { - pendingFill++; - } else { - this.fill_(context); - } - ++i; - break; - case CanvasInstruction.MOVE_TO_LINE_TO: - d = /** @type {number} */ (instruction[1]); - dd = /** @type {number} */ (instruction[2]); - x = pixelCoordinates[d]; - y = pixelCoordinates[d + 1]; - roundX = (x + 0.5) | 0; - roundY = (y + 0.5) | 0; - if (roundX !== prevX || roundY !== prevY) { - context.moveTo(x, y); - prevX = roundX; - prevY = roundY; - } - for (d += 2; d < dd; d += 2) { - x = pixelCoordinates[d]; - y = pixelCoordinates[d + 1]; - roundX = (x + 0.5) | 0; - roundY = (y + 0.5) | 0; - if (d == dd - 2 || roundX !== prevX || roundY !== prevY) { - context.lineTo(x, y); - prevX = roundX; - prevY = roundY; - } - } - ++i; - break; - case CanvasInstruction.SET_FILL_STYLE: - lastFillInstruction = instruction; - this.alignFill_ = instruction[2]; - - if (pendingFill) { - this.fill_(context); - pendingFill = 0; - if (pendingStroke) { - context.stroke(); - pendingStroke = 0; - } - } - - context.fillStyle = /** @type {module:ol/colorlike~ColorLike} */ (instruction[1]); - ++i; - break; - case CanvasInstruction.SET_STROKE_STYLE: - lastStrokeInstruction = instruction; - if (pendingStroke) { - context.stroke(); - pendingStroke = 0; - } - this.setStrokeStyle_(context, /** @type {Array.<*>} */ (instruction)); - ++i; - break; - case CanvasInstruction.STROKE: - if (batchSize) { - pendingStroke++; - } else { - context.stroke(); - } - ++i; - break; - default: - ++i; // consume the instruction anyway, to avoid an infinite loop - break; - } - } - if (pendingFill) { - this.fill_(context); - } - if (pendingStroke) { - context.stroke(); - } - return undefined; -}; - - -/** - * @param {CanvasRenderingContext2D} context Context. - * @param {module:ol/transform~Transform} transform Transform. - * @param {number} viewRotation View rotation. - * @param {Object.} skippedFeaturesHash Ids of features - * to skip. - */ -CanvasReplay.prototype.replay = function( - context, transform, viewRotation, skippedFeaturesHash) { - this.viewRotation_ = viewRotation; - this.replay_(context, transform, - skippedFeaturesHash, this.instructions, undefined, undefined); -}; - - -/** - * @param {CanvasRenderingContext2D} context Context. - * @param {module:ol/transform~Transform} transform Transform. - * @param {number} viewRotation View rotation. - * @param {Object.} skippedFeaturesHash Ids of features - * to skip. - * @param {function((module:ol/Feature|module:ol/render/Feature)): T=} opt_featureCallback - * Feature callback. - * @param {module:ol/extent~Extent=} opt_hitExtent Only check features that intersect this - * extent. - * @return {T|undefined} Callback result. - * @template T - */ -CanvasReplay.prototype.replayHitDetection = function( - context, transform, viewRotation, skippedFeaturesHash, - opt_featureCallback, opt_hitExtent) { - this.viewRotation_ = viewRotation; - return this.replay_(context, transform, skippedFeaturesHash, - this.hitDetectionInstructions, opt_featureCallback, opt_hitExtent); -}; - - -/** - * Reverse the hit detection instructions. - */ -CanvasReplay.prototype.reverseHitDetectionInstructions = function() { - const hitDetectionInstructions = this.hitDetectionInstructions; - // step 1 - reverse array - hitDetectionInstructions.reverse(); - // step 2 - reverse instructions within geometry blocks - let i; - const n = hitDetectionInstructions.length; - let instruction; - let type; - let begin = -1; - for (i = 0; i < n; ++i) { - instruction = hitDetectionInstructions[i]; - type = /** @type {module:ol/render/canvas/Instruction} */ (instruction[0]); - if (type == CanvasInstruction.END_GEOMETRY) { - begin = i; - } else if (type == CanvasInstruction.BEGIN_GEOMETRY) { - instruction[2] = i; - reverseSubArray(this.hitDetectionInstructions, begin, i); - begin = -1; - } - } -}; - - -/** - * @inheritDoc - */ -CanvasReplay.prototype.setFillStrokeStyle = function(fillStyle, strokeStyle) { - const state = this.state; - if (fillStyle) { - const fillStyleColor = fillStyle.getColor(); - state.fillStyle = asColorLike(fillStyleColor ? - fillStyleColor : defaultFillStyle); - } else { - state.fillStyle = undefined; - } - if (strokeStyle) { - const strokeStyleColor = strokeStyle.getColor(); - state.strokeStyle = asColorLike(strokeStyleColor ? - strokeStyleColor : defaultStrokeStyle); - const strokeStyleLineCap = strokeStyle.getLineCap(); - state.lineCap = strokeStyleLineCap !== undefined ? - strokeStyleLineCap : defaultLineCap; - const strokeStyleLineDash = strokeStyle.getLineDash(); - state.lineDash = strokeStyleLineDash ? - strokeStyleLineDash.slice() : defaultLineDash; - const strokeStyleLineDashOffset = strokeStyle.getLineDashOffset(); - state.lineDashOffset = strokeStyleLineDashOffset ? - strokeStyleLineDashOffset : defaultLineDashOffset; - const strokeStyleLineJoin = strokeStyle.getLineJoin(); - state.lineJoin = strokeStyleLineJoin !== undefined ? - strokeStyleLineJoin : defaultLineJoin; - const strokeStyleWidth = strokeStyle.getWidth(); - state.lineWidth = strokeStyleWidth !== undefined ? - strokeStyleWidth : defaultLineWidth; - const strokeStyleMiterLimit = strokeStyle.getMiterLimit(); - state.miterLimit = strokeStyleMiterLimit !== undefined ? - strokeStyleMiterLimit : defaultMiterLimit; - - if (state.lineWidth > this.maxLineWidth) { - this.maxLineWidth = state.lineWidth; - // invalidate the buffered max extent cache - this.bufferedMaxExtent_ = null; - } - } else { - state.strokeStyle = undefined; - state.lineCap = undefined; - state.lineDash = null; - state.lineDashOffset = undefined; - state.lineJoin = undefined; - state.lineWidth = undefined; - state.miterLimit = undefined; - } -}; - - -/** - * @param {module:ol/render/canvas~FillStrokeState} state State. - * @param {module:ol/geom/Geometry|module:ol/render/Feature} geometry Geometry. - * @return {Array.<*>} Fill instruction. - */ -CanvasReplay.prototype.createFill = function(state, geometry) { - const fillStyle = state.fillStyle; - const fillInstruction = [CanvasInstruction.SET_FILL_STYLE, fillStyle]; - if (typeof fillStyle !== 'string') { - // Fill is a pattern or gradient - align it! - fillInstruction.push(true); - } - return fillInstruction; -}; - - -/** - * @param {module:ol/render/canvas~FillStrokeState} state State. - */ -CanvasReplay.prototype.applyStroke = function(state) { - this.instructions.push(this.createStroke(state)); -}; - - -/** - * @param {module:ol/render/canvas~FillStrokeState} state State. - * @return {Array.<*>} Stroke instruction. - */ -CanvasReplay.prototype.createStroke = function(state) { - return [ - CanvasInstruction.SET_STROKE_STYLE, - state.strokeStyle, state.lineWidth * this.pixelRatio, state.lineCap, - state.lineJoin, state.miterLimit, - this.applyPixelRatio(state.lineDash), state.lineDashOffset * this.pixelRatio - ]; -}; - - -/** - * @param {module:ol/render/canvas~FillStrokeState} state State. - * @param {function(this:module:ol/render/canvas/Replay, module:ol/render/canvas~FillStrokeState, (module:ol/geom/Geometry|module:ol/render/Feature)):Array.<*>} createFill Create fill. - * @param {module:ol/geom/Geometry|module:ol/render/Feature} geometry Geometry. - */ -CanvasReplay.prototype.updateFillStyle = function(state, createFill, geometry) { - const fillStyle = state.fillStyle; - if (typeof fillStyle !== 'string' || state.currentFillStyle != fillStyle) { - if (fillStyle !== undefined) { - this.instructions.push(createFill.call(this, state, geometry)); - } - state.currentFillStyle = fillStyle; - } -}; - - -/** - * @param {module:ol/render/canvas~FillStrokeState} state State. - * @param {function(this:module:ol/render/canvas/Replay, module:ol/render/canvas~FillStrokeState)} applyStroke Apply stroke. - */ -CanvasReplay.prototype.updateStrokeStyle = function(state, applyStroke) { - const strokeStyle = state.strokeStyle; - const lineCap = state.lineCap; - const lineDash = state.lineDash; - const lineDashOffset = state.lineDashOffset; - const lineJoin = state.lineJoin; - const lineWidth = state.lineWidth; - const miterLimit = state.miterLimit; - if (state.currentStrokeStyle != strokeStyle || - state.currentLineCap != lineCap || - (lineDash != state.currentLineDash && !equals(state.currentLineDash, lineDash)) || - state.currentLineDashOffset != lineDashOffset || - state.currentLineJoin != lineJoin || - state.currentLineWidth != lineWidth || - state.currentMiterLimit != miterLimit) { - if (strokeStyle !== undefined) { - applyStroke.call(this, state); - } - state.currentStrokeStyle = strokeStyle; - state.currentLineCap = lineCap; - state.currentLineDash = lineDash; - state.currentLineDashOffset = lineDashOffset; - state.currentLineJoin = lineJoin; - state.currentLineWidth = lineWidth; - state.currentMiterLimit = miterLimit; - } -}; - - -/** - * @param {module:ol/geom/Geometry|module:ol/render/Feature} geometry Geometry. - * @param {module:ol/Feature|module:ol/render/Feature} feature Feature. - */ -CanvasReplay.prototype.endGeometry = function(geometry, feature) { - this.beginGeometryInstruction1_[2] = this.instructions.length; - this.beginGeometryInstruction1_ = null; - this.beginGeometryInstruction2_[2] = this.hitDetectionInstructions.length; - this.beginGeometryInstruction2_ = null; - const endGeometryInstruction = [CanvasInstruction.END_GEOMETRY, feature]; - this.instructions.push(endGeometryInstruction); - this.hitDetectionInstructions.push(endGeometryInstruction); -}; - /** * FIXME empty description for jsdoc @@ -1065,21 +1090,4 @@ CanvasReplay.prototype.endGeometry = function(geometry, feature) { CanvasReplay.prototype.finish = UNDEFINED; -/** - * Get the buffered rendering extent. Rendering will be clipped to the extent - * provided to the constructor. To account for symbolizers that may intersect - * this extent, we calculate a buffered extent (e.g. based on stroke width). - * @return {module:ol/extent~Extent} The buffered rendering extent. - * @protected - */ -CanvasReplay.prototype.getBufferedMaxExtent = function() { - if (!this.bufferedMaxExtent_) { - this.bufferedMaxExtent_ = clone(this.maxExtent); - if (this.maxLineWidth > 0) { - const width = this.resolution * (this.maxLineWidth + 1) / 2; - buffer(this.bufferedMaxExtent_, width, this.bufferedMaxExtent_); - } - } - return this.bufferedMaxExtent_; -}; export default CanvasReplay; diff --git a/src/ol/render/canvas/ReplayGroup.js b/src/ol/render/canvas/ReplayGroup.js index 2096b6d6c5..2aa3f5cfeb 100644 --- a/src/ol/render/canvas/ReplayGroup.js +++ b/src/ol/render/canvas/ReplayGroup.js @@ -46,76 +46,366 @@ const BATCH_CONSTRUCTORS = { * @param {number=} opt_renderBuffer Optional rendering buffer. * @struct */ -const CanvasReplayGroup = function( - tolerance, maxExtent, resolution, pixelRatio, overlaps, declutterTree, opt_renderBuffer) { - ReplayGroup.call(this); +class CanvasReplayGroup { + constructor( + tolerance, + maxExtent, + resolution, + pixelRatio, + overlaps, + declutterTree, + opt_renderBuffer + ) { + ReplayGroup.call(this); + + /** + * Declutter tree. + * @private + */ + this.declutterTree_ = declutterTree; + + /** + * @type {module:ol/render/canvas~DeclutterGroup} + * @private + */ + this.declutterGroup_ = null; + + /** + * @private + * @type {number} + */ + this.tolerance_ = tolerance; + + /** + * @private + * @type {module:ol/extent~Extent} + */ + this.maxExtent_ = maxExtent; + + /** + * @private + * @type {boolean} + */ + this.overlaps_ = overlaps; + + /** + * @private + * @type {number} + */ + this.pixelRatio_ = pixelRatio; + + /** + * @private + * @type {number} + */ + this.resolution_ = resolution; + + /** + * @private + * @type {number|undefined} + */ + this.renderBuffer_ = opt_renderBuffer; + + /** + * @private + * @type {!Object.>} + */ + this.replaysByZIndex_ = {}; + + /** + * @private + * @type {CanvasRenderingContext2D} + */ + this.hitDetectionContext_ = createCanvasContext2D(1, 1); + + /** + * @private + * @type {module:ol/transform~Transform} + */ + this.hitDetectionTransform_ = createTransform(); + } /** - * Declutter tree. - * @private + * @param {boolean} group Group with previous replay. + * @return {module:ol/render/canvas~DeclutterGroup} Declutter instruction group. */ - this.declutterTree_ = declutterTree; + addDeclutter(group) { + let declutter = null; + if (this.declutterTree_) { + if (group) { + declutter = this.declutterGroup_; + /** @type {number} */ (declutter[4])++; + } else { + declutter = this.declutterGroup_ = createEmpty(); + declutter.push(1); + } + } + return declutter; + } /** - * @type {module:ol/render/canvas~DeclutterGroup} - * @private + * @param {CanvasRenderingContext2D} context Context. + * @param {module:ol/transform~Transform} transform Transform. */ - this.declutterGroup_ = null; + clip(context, transform) { + const flatClipCoords = this.getClipCoords(transform); + context.beginPath(); + context.moveTo(flatClipCoords[0], flatClipCoords[1]); + context.lineTo(flatClipCoords[2], flatClipCoords[3]); + context.lineTo(flatClipCoords[4], flatClipCoords[5]); + context.lineTo(flatClipCoords[6], flatClipCoords[7]); + context.clip(); + } /** - * @private - * @type {number} + * @param {Array.} replays Replays. + * @return {boolean} Has replays of the provided types. */ - this.tolerance_ = tolerance; + hasReplays(replays) { + for (const zIndex in this.replaysByZIndex_) { + const candidates = this.replaysByZIndex_[zIndex]; + for (let i = 0, ii = replays.length; i < ii; ++i) { + if (replays[i] in candidates) { + return true; + } + } + } + return false; + } /** - * @private - * @type {module:ol/extent~Extent} + * FIXME empty description for jsdoc */ - this.maxExtent_ = maxExtent; + finish() { + for (const zKey in this.replaysByZIndex_) { + const replays = this.replaysByZIndex_[zKey]; + for (const replayKey in replays) { + replays[replayKey].finish(); + } + } + } /** - * @private - * @type {boolean} + * @param {module:ol/coordinate~Coordinate} coordinate Coordinate. + * @param {number} resolution Resolution. + * @param {number} rotation Rotation. + * @param {number} hitTolerance Hit tolerance in pixels. + * @param {Object.} skippedFeaturesHash Ids of features to skip. + * @param {function((module:ol/Feature|module:ol/render/Feature)): T} callback Feature callback. + * @param {Object.} declutterReplays Declutter replays. + * @return {T|undefined} Callback result. + * @template T */ - this.overlaps_ = overlaps; + forEachFeatureAtCoordinate( + coordinate, + resolution, + rotation, + hitTolerance, + skippedFeaturesHash, + callback, + declutterReplays + ) { + + hitTolerance = Math.round(hitTolerance); + const contextSize = hitTolerance * 2 + 1; + const transform = composeTransform(this.hitDetectionTransform_, + hitTolerance + 0.5, hitTolerance + 0.5, + 1 / resolution, -1 / resolution, + -rotation, + -coordinate[0], -coordinate[1]); + const context = this.hitDetectionContext_; + + if (context.canvas.width !== contextSize || context.canvas.height !== contextSize) { + context.canvas.width = contextSize; + context.canvas.height = contextSize; + } else { + context.clearRect(0, 0, contextSize, contextSize); + } + + /** + * @type {module:ol/extent~Extent} + */ + let hitExtent; + if (this.renderBuffer_ !== undefined) { + hitExtent = createEmpty(); + extendCoordinate(hitExtent, coordinate); + buffer(hitExtent, resolution * (this.renderBuffer_ + hitTolerance), hitExtent); + } + + const mask = getCircleArray(hitTolerance); + let declutteredFeatures; + if (this.declutterTree_) { + declutteredFeatures = this.declutterTree_.all().map(function(entry) { + return entry.value; + }); + } + + let replayType; + + /** + * @param {module:ol/Feature|module:ol/render/Feature} feature Feature. + * @return {?} Callback result. + */ + function featureCallback(feature) { + const imageData = context.getImageData(0, 0, contextSize, contextSize).data; + for (let i = 0; i < contextSize; i++) { + for (let j = 0; j < contextSize; j++) { + if (mask[i][j]) { + if (imageData[(j * contextSize + i) * 4 + 3] > 0) { + let result; + if (!(declutteredFeatures && (replayType == ReplayType.IMAGE || replayType == ReplayType.TEXT)) || + declutteredFeatures.indexOf(feature) !== -1) { + result = callback(feature); + } + if (result) { + return result; + } else { + context.clearRect(0, 0, contextSize, contextSize); + return undefined; + } + } + } + } + } + } + + /** @type {Array.} */ + const zs = Object.keys(this.replaysByZIndex_).map(Number); + zs.sort(numberSafeCompareFunction); + + let i, j, replays, replay, result; + for (i = zs.length - 1; i >= 0; --i) { + const zIndexKey = zs[i].toString(); + replays = this.replaysByZIndex_[zIndexKey]; + for (j = ORDER.length - 1; j >= 0; --j) { + replayType = ORDER[j]; + replay = replays[replayType]; + if (replay !== undefined) { + if (declutterReplays && + (replayType == ReplayType.IMAGE || replayType == ReplayType.TEXT)) { + const declutter = declutterReplays[zIndexKey]; + if (!declutter) { + declutterReplays[zIndexKey] = [replay, transform.slice(0)]; + } else { + declutter.push(replay, transform.slice(0)); + } + } else { + result = replay.replayHitDetection(context, transform, rotation, + skippedFeaturesHash, featureCallback, hitExtent); + if (result) { + return result; + } + } + } + } + } + return undefined; + } /** - * @private - * @type {number} + * @param {module:ol/transform~Transform} transform Transform. + * @return {Array.} Clip coordinates. */ - this.pixelRatio_ = pixelRatio; + getClipCoords(transform) { + const maxExtent = this.maxExtent_; + const minX = maxExtent[0]; + const minY = maxExtent[1]; + const maxX = maxExtent[2]; + const maxY = maxExtent[3]; + const flatClipCoords = [minX, minY, minX, maxY, maxX, maxY, maxX, minY]; + transform2D( + flatClipCoords, 0, 8, 2, transform, flatClipCoords); + return flatClipCoords; + } /** - * @private - * @type {number} + * @inheritDoc */ - this.resolution_ = resolution; + getReplay(zIndex, replayType) { + const zIndexKey = zIndex !== undefined ? zIndex.toString() : '0'; + let replays = this.replaysByZIndex_[zIndexKey]; + if (replays === undefined) { + replays = {}; + this.replaysByZIndex_[zIndexKey] = replays; + } + let replay = replays[replayType]; + if (replay === undefined) { + const Constructor = BATCH_CONSTRUCTORS[replayType]; + replay = new Constructor(this.tolerance_, this.maxExtent_, + this.resolution_, this.pixelRatio_, this.overlaps_, this.declutterTree_); + replays[replayType] = replay; + } + return replay; + } /** - * @private - * @type {number|undefined} + * @return {Object.>} Replays. */ - this.renderBuffer_ = opt_renderBuffer; + getReplays() { + return this.replaysByZIndex_; + } /** - * @private - * @type {!Object.>} + * @inheritDoc */ - this.replaysByZIndex_ = {}; + isEmpty() { + return isEmpty(this.replaysByZIndex_); + } /** - * @private - * @type {CanvasRenderingContext2D} + * @param {CanvasRenderingContext2D} context Context. + * @param {module:ol/transform~Transform} transform Transform. + * @param {number} viewRotation View rotation. + * @param {Object.} skippedFeaturesHash Ids of features to skip. + * @param {Array.=} opt_replayTypes Ordered replay types to replay. + * Default is {@link module:ol/render/replay~ORDER} + * @param {Object.=} opt_declutterReplays Declutter replays. */ - this.hitDetectionContext_ = createCanvasContext2D(1, 1); + replay( + context, + transform, + viewRotation, + skippedFeaturesHash, + opt_replayTypes, + opt_declutterReplays + ) { - /** - * @private - * @type {module:ol/transform~Transform} - */ - this.hitDetectionTransform_ = createTransform(); -}; + /** @type {Array.} */ + const zs = Object.keys(this.replaysByZIndex_).map(Number); + zs.sort(numberSafeCompareFunction); + + // setup clipping so that the parts of over-simplified geometries are not + // visible outside the current extent when panning + context.save(); + this.clip(context, transform); + + const replayTypes = opt_replayTypes ? opt_replayTypes : ORDER; + let i, ii, j, jj, replays, replay; + for (i = 0, ii = zs.length; i < ii; ++i) { + const zIndexKey = zs[i].toString(); + replays = this.replaysByZIndex_[zIndexKey]; + for (j = 0, jj = replayTypes.length; j < jj; ++j) { + const replayType = replayTypes[j]; + replay = replays[replayType]; + if (replay !== undefined) { + if (opt_declutterReplays && + (replayType == ReplayType.IMAGE || replayType == ReplayType.TEXT)) { + const declutter = opt_declutterReplays[zIndexKey]; + if (!declutter) { + opt_declutterReplays[zIndexKey] = [replay, transform.slice(0)]; + } else { + declutter.push(replay, transform.slice(0)); + } + } else { + replay.replay(context, transform, viewRotation, skippedFeaturesHash); + } + } + } + } + + context.restore(); + } +} inherits(CanvasReplayGroup, ReplayGroup); @@ -217,281 +507,4 @@ export function replayDeclutter(declutterReplays, context, rotation) { } -/** - * @param {boolean} group Group with previous replay. - * @return {module:ol/render/canvas~DeclutterGroup} Declutter instruction group. - */ -CanvasReplayGroup.prototype.addDeclutter = function(group) { - let declutter = null; - if (this.declutterTree_) { - if (group) { - declutter = this.declutterGroup_; - /** @type {number} */ (declutter[4])++; - } else { - declutter = this.declutterGroup_ = createEmpty(); - declutter.push(1); - } - } - return declutter; -}; - - -/** - * @param {CanvasRenderingContext2D} context Context. - * @param {module:ol/transform~Transform} transform Transform. - */ -CanvasReplayGroup.prototype.clip = function(context, transform) { - const flatClipCoords = this.getClipCoords(transform); - context.beginPath(); - context.moveTo(flatClipCoords[0], flatClipCoords[1]); - context.lineTo(flatClipCoords[2], flatClipCoords[3]); - context.lineTo(flatClipCoords[4], flatClipCoords[5]); - context.lineTo(flatClipCoords[6], flatClipCoords[7]); - context.clip(); -}; - - -/** - * @param {Array.} replays Replays. - * @return {boolean} Has replays of the provided types. - */ -CanvasReplayGroup.prototype.hasReplays = function(replays) { - for (const zIndex in this.replaysByZIndex_) { - const candidates = this.replaysByZIndex_[zIndex]; - for (let i = 0, ii = replays.length; i < ii; ++i) { - if (replays[i] in candidates) { - return true; - } - } - } - return false; -}; - - -/** - * FIXME empty description for jsdoc - */ -CanvasReplayGroup.prototype.finish = function() { - for (const zKey in this.replaysByZIndex_) { - const replays = this.replaysByZIndex_[zKey]; - for (const replayKey in replays) { - replays[replayKey].finish(); - } - } -}; - - -/** - * @param {module:ol/coordinate~Coordinate} coordinate Coordinate. - * @param {number} resolution Resolution. - * @param {number} rotation Rotation. - * @param {number} hitTolerance Hit tolerance in pixels. - * @param {Object.} skippedFeaturesHash Ids of features to skip. - * @param {function((module:ol/Feature|module:ol/render/Feature)): T} callback Feature callback. - * @param {Object.} declutterReplays Declutter replays. - * @return {T|undefined} Callback result. - * @template T - */ -CanvasReplayGroup.prototype.forEachFeatureAtCoordinate = function( - coordinate, resolution, rotation, hitTolerance, skippedFeaturesHash, callback, declutterReplays) { - - hitTolerance = Math.round(hitTolerance); - const contextSize = hitTolerance * 2 + 1; - const transform = composeTransform(this.hitDetectionTransform_, - hitTolerance + 0.5, hitTolerance + 0.5, - 1 / resolution, -1 / resolution, - -rotation, - -coordinate[0], -coordinate[1]); - const context = this.hitDetectionContext_; - - if (context.canvas.width !== contextSize || context.canvas.height !== contextSize) { - context.canvas.width = contextSize; - context.canvas.height = contextSize; - } else { - context.clearRect(0, 0, contextSize, contextSize); - } - - /** - * @type {module:ol/extent~Extent} - */ - let hitExtent; - if (this.renderBuffer_ !== undefined) { - hitExtent = createEmpty(); - extendCoordinate(hitExtent, coordinate); - buffer(hitExtent, resolution * (this.renderBuffer_ + hitTolerance), hitExtent); - } - - const mask = getCircleArray(hitTolerance); - let declutteredFeatures; - if (this.declutterTree_) { - declutteredFeatures = this.declutterTree_.all().map(function(entry) { - return entry.value; - }); - } - - let replayType; - - /** - * @param {module:ol/Feature|module:ol/render/Feature} feature Feature. - * @return {?} Callback result. - */ - function featureCallback(feature) { - const imageData = context.getImageData(0, 0, contextSize, contextSize).data; - for (let i = 0; i < contextSize; i++) { - for (let j = 0; j < contextSize; j++) { - if (mask[i][j]) { - if (imageData[(j * contextSize + i) * 4 + 3] > 0) { - let result; - if (!(declutteredFeatures && (replayType == ReplayType.IMAGE || replayType == ReplayType.TEXT)) || - declutteredFeatures.indexOf(feature) !== -1) { - result = callback(feature); - } - if (result) { - return result; - } else { - context.clearRect(0, 0, contextSize, contextSize); - return undefined; - } - } - } - } - } - } - - /** @type {Array.} */ - const zs = Object.keys(this.replaysByZIndex_).map(Number); - zs.sort(numberSafeCompareFunction); - - let i, j, replays, replay, result; - for (i = zs.length - 1; i >= 0; --i) { - const zIndexKey = zs[i].toString(); - replays = this.replaysByZIndex_[zIndexKey]; - for (j = ORDER.length - 1; j >= 0; --j) { - replayType = ORDER[j]; - replay = replays[replayType]; - if (replay !== undefined) { - if (declutterReplays && - (replayType == ReplayType.IMAGE || replayType == ReplayType.TEXT)) { - const declutter = declutterReplays[zIndexKey]; - if (!declutter) { - declutterReplays[zIndexKey] = [replay, transform.slice(0)]; - } else { - declutter.push(replay, transform.slice(0)); - } - } else { - result = replay.replayHitDetection(context, transform, rotation, - skippedFeaturesHash, featureCallback, hitExtent); - if (result) { - return result; - } - } - } - } - } - return undefined; -}; - - -/** - * @param {module:ol/transform~Transform} transform Transform. - * @return {Array.} Clip coordinates. - */ -CanvasReplayGroup.prototype.getClipCoords = function(transform) { - const maxExtent = this.maxExtent_; - const minX = maxExtent[0]; - const minY = maxExtent[1]; - const maxX = maxExtent[2]; - const maxY = maxExtent[3]; - const flatClipCoords = [minX, minY, minX, maxY, maxX, maxY, maxX, minY]; - transform2D( - flatClipCoords, 0, 8, 2, transform, flatClipCoords); - return flatClipCoords; -}; - - -/** - * @inheritDoc - */ -CanvasReplayGroup.prototype.getReplay = function(zIndex, replayType) { - const zIndexKey = zIndex !== undefined ? zIndex.toString() : '0'; - let replays = this.replaysByZIndex_[zIndexKey]; - if (replays === undefined) { - replays = {}; - this.replaysByZIndex_[zIndexKey] = replays; - } - let replay = replays[replayType]; - if (replay === undefined) { - const Constructor = BATCH_CONSTRUCTORS[replayType]; - replay = new Constructor(this.tolerance_, this.maxExtent_, - this.resolution_, this.pixelRatio_, this.overlaps_, this.declutterTree_); - replays[replayType] = replay; - } - return replay; -}; - - -/** - * @return {Object.>} Replays. - */ -CanvasReplayGroup.prototype.getReplays = function() { - return this.replaysByZIndex_; -}; - - -/** - * @inheritDoc - */ -CanvasReplayGroup.prototype.isEmpty = function() { - return isEmpty(this.replaysByZIndex_); -}; - - -/** - * @param {CanvasRenderingContext2D} context Context. - * @param {module:ol/transform~Transform} transform Transform. - * @param {number} viewRotation View rotation. - * @param {Object.} skippedFeaturesHash Ids of features to skip. - * @param {Array.=} opt_replayTypes Ordered replay types to replay. - * Default is {@link module:ol/render/replay~ORDER} - * @param {Object.=} opt_declutterReplays Declutter replays. - */ -CanvasReplayGroup.prototype.replay = function(context, - transform, viewRotation, skippedFeaturesHash, opt_replayTypes, opt_declutterReplays) { - - /** @type {Array.} */ - const zs = Object.keys(this.replaysByZIndex_).map(Number); - zs.sort(numberSafeCompareFunction); - - // setup clipping so that the parts of over-simplified geometries are not - // visible outside the current extent when panning - context.save(); - this.clip(context, transform); - - const replayTypes = opt_replayTypes ? opt_replayTypes : ORDER; - let i, ii, j, jj, replays, replay; - for (i = 0, ii = zs.length; i < ii; ++i) { - const zIndexKey = zs[i].toString(); - replays = this.replaysByZIndex_[zIndexKey]; - for (j = 0, jj = replayTypes.length; j < jj; ++j) { - const replayType = replayTypes[j]; - replay = replays[replayType]; - if (replay !== undefined) { - if (opt_declutterReplays && - (replayType == ReplayType.IMAGE || replayType == ReplayType.TEXT)) { - const declutter = opt_declutterReplays[zIndexKey]; - if (!declutter) { - opt_declutterReplays[zIndexKey] = [replay, transform.slice(0)]; - } else { - declutter.push(replay, transform.slice(0)); - } - } else { - replay.replay(context, transform, viewRotation, skippedFeaturesHash); - } - } - } - } - - context.restore(); -}; - export default CanvasReplayGroup; diff --git a/src/ol/render/canvas/TextReplay.js b/src/ol/render/canvas/TextReplay.js index f398b7d196..945243bad1 100644 --- a/src/ol/render/canvas/TextReplay.js +++ b/src/ol/render/canvas/TextReplay.js @@ -25,113 +25,501 @@ import TextPlacement from '../../style/TextPlacement.js'; * @param {?} declutterTree Declutter tree. * @struct */ -const CanvasTextReplay = function( - tolerance, maxExtent, resolution, pixelRatio, overlaps, declutterTree) { - CanvasReplay.call(this, - tolerance, maxExtent, resolution, pixelRatio, overlaps, declutterTree); +class CanvasTextReplay { + constructor(tolerance, maxExtent, resolution, pixelRatio, overlaps, declutterTree) { + CanvasReplay.call(this, + tolerance, maxExtent, resolution, pixelRatio, overlaps, declutterTree); + + /** + * @private + * @type {module:ol/render/canvas~DeclutterGroup} + */ + this.declutterGroup_; + + /** + * @private + * @type {Array.} + */ + this.labels_ = null; + + /** + * @private + * @type {string} + */ + this.text_ = ''; + + /** + * @private + * @type {number} + */ + this.textOffsetX_ = 0; + + /** + * @private + * @type {number} + */ + this.textOffsetY_ = 0; + + /** + * @private + * @type {boolean|undefined} + */ + this.textRotateWithView_ = undefined; + + /** + * @private + * @type {number} + */ + this.textRotation_ = 0; + + /** + * @private + * @type {?module:ol/render/canvas~FillState} + */ + this.textFillState_ = null; + + /** + * @type {!Object.} + */ + this.fillStates = {}; + + /** + * @private + * @type {?module:ol/render/canvas~StrokeState} + */ + this.textStrokeState_ = null; + + /** + * @type {!Object.} + */ + this.strokeStates = {}; + + /** + * @private + * @type {module:ol/render/canvas~TextState} + */ + this.textState_ = /** @type {module:ol/render/canvas~TextState} */ ({}); + + /** + * @type {!Object.} + */ + this.textStates = {}; + + /** + * @private + * @type {string} + */ + this.textKey_ = ''; + + /** + * @private + * @type {string} + */ + this.fillKey_ = ''; + + /** + * @private + * @type {string} + */ + this.strokeKey_ = ''; + + /** + * @private + * @type {Object.>} + */ + this.widths_ = {}; + + labelCache.prune(); + + } + + /** + * @inheritDoc + */ + drawText(geometry, feature) { + const fillState = this.textFillState_; + const strokeState = this.textStrokeState_; + const textState = this.textState_; + if (this.text_ === '' || !textState || (!fillState && !strokeState)) { + return; + } + + let begin = this.coordinates.length; + + const geometryType = geometry.getType(); + let flatCoordinates = null; + let end = 2; + let stride = 2; + let i, ii; + + if (textState.placement === TextPlacement.LINE) { + if (!intersects(this.getBufferedMaxExtent(), geometry.getExtent())) { + return; + } + let ends; + flatCoordinates = geometry.getFlatCoordinates(); + stride = geometry.getStride(); + if (geometryType == GeometryType.LINE_STRING) { + ends = [flatCoordinates.length]; + } else if (geometryType == GeometryType.MULTI_LINE_STRING) { + ends = geometry.getEnds(); + } else if (geometryType == GeometryType.POLYGON) { + ends = geometry.getEnds().slice(0, 1); + } else if (geometryType == GeometryType.MULTI_POLYGON) { + const endss = geometry.getEndss(); + ends = []; + for (i = 0, ii = endss.length; i < ii; ++i) { + ends.push(endss[i][0]); + } + } + this.beginGeometry(geometry, feature); + const textAlign = textState.textAlign; + let flatOffset = 0; + let flatEnd; + for (let o = 0, oo = ends.length; o < oo; ++o) { + if (textAlign == undefined) { + const range = matchingChunk(textState.maxAngle, flatCoordinates, flatOffset, ends[o], stride); + flatOffset = range[0]; + flatEnd = range[1]; + } else { + flatEnd = ends[o]; + } + for (i = flatOffset; i < flatEnd; i += stride) { + this.coordinates.push(flatCoordinates[i], flatCoordinates[i + 1]); + } + end = this.coordinates.length; + flatOffset = ends[o]; + this.drawChars_(begin, end, this.declutterGroup_); + begin = end; + } + this.endGeometry(geometry, feature); + + } else { + const label = this.getImage(this.text_, this.textKey_, this.fillKey_, this.strokeKey_); + const width = label.width / this.pixelRatio; + switch (geometryType) { + case GeometryType.POINT: + case GeometryType.MULTI_POINT: + flatCoordinates = geometry.getFlatCoordinates(); + end = flatCoordinates.length; + break; + case GeometryType.LINE_STRING: + flatCoordinates = /** @type {module:ol/geom/LineString} */ (geometry).getFlatMidpoint(); + break; + case GeometryType.CIRCLE: + flatCoordinates = /** @type {module:ol/geom/Circle} */ (geometry).getCenter(); + break; + case GeometryType.MULTI_LINE_STRING: + flatCoordinates = /** @type {module:ol/geom/MultiLineString} */ (geometry).getFlatMidpoints(); + end = flatCoordinates.length; + break; + case GeometryType.POLYGON: + flatCoordinates = /** @type {module:ol/geom/Polygon} */ (geometry).getFlatInteriorPoint(); + if (!textState.overflow && flatCoordinates[2] / this.resolution < width) { + return; + } + stride = 3; + break; + case GeometryType.MULTI_POLYGON: + const interiorPoints = /** @type {module:ol/geom/MultiPolygon} */ (geometry).getFlatInteriorPoints(); + flatCoordinates = []; + for (i = 0, ii = interiorPoints.length; i < ii; i += 3) { + if (textState.overflow || interiorPoints[i + 2] / this.resolution >= width) { + flatCoordinates.push(interiorPoints[i], interiorPoints[i + 1]); + } + } + end = flatCoordinates.length; + if (end == 0) { + return; + } + break; + default: + } + end = this.appendFlatCoordinates(flatCoordinates, 0, end, stride, false, false); + if (textState.backgroundFill || textState.backgroundStroke) { + this.setFillStrokeStyle(textState.backgroundFill, textState.backgroundStroke); + if (textState.backgroundFill) { + this.updateFillStyle(this.state, this.createFill, geometry); + this.hitDetectionInstructions.push(this.createFill(this.state, geometry)); + } + if (textState.backgroundStroke) { + this.updateStrokeStyle(this.state, this.applyStroke); + this.hitDetectionInstructions.push(this.createStroke(this.state)); + } + } + this.beginGeometry(geometry, feature); + this.drawTextImage_(label, begin, end); + this.endGeometry(geometry, feature); + } + } + + /** + * @param {string} text Text. + * @param {string} textKey Text style key. + * @param {string} fillKey Fill style key. + * @param {string} strokeKey Stroke style key. + * @return {HTMLCanvasElement} Image. + */ + getImage(text, textKey, fillKey, strokeKey) { + let label; + const key = strokeKey + textKey + text + fillKey + this.pixelRatio; + + if (!labelCache.containsKey(key)) { + const strokeState = strokeKey ? this.strokeStates[strokeKey] || this.textStrokeState_ : null; + const fillState = fillKey ? this.fillStates[fillKey] || this.textFillState_ : null; + const textState = this.textStates[textKey] || this.textState_; + const pixelRatio = this.pixelRatio; + const scale = textState.scale * pixelRatio; + const align = TEXT_ALIGN[textState.textAlign || defaultTextAlign]; + const strokeWidth = strokeKey && strokeState.lineWidth ? strokeState.lineWidth : 0; + + const lines = text.split('\n'); + const numLines = lines.length; + const widths = []; + const width = measureTextWidths(textState.font, lines, widths); + const lineHeight = measureTextHeight(textState.font); + const height = lineHeight * numLines; + const renderWidth = (width + strokeWidth); + const context = createCanvasContext2D( + Math.ceil(renderWidth * scale), + Math.ceil((height + strokeWidth) * scale)); + label = context.canvas; + labelCache.set(key, label); + if (scale != 1) { + context.scale(scale, scale); + } + context.font = textState.font; + if (strokeKey) { + context.strokeStyle = strokeState.strokeStyle; + context.lineWidth = strokeWidth; + context.lineCap = strokeState.lineCap; + context.lineJoin = strokeState.lineJoin; + context.miterLimit = strokeState.miterLimit; + if (CANVAS_LINE_DASH && strokeState.lineDash.length) { + context.setLineDash(strokeState.lineDash); + context.lineDashOffset = strokeState.lineDashOffset; + } + } + if (fillKey) { + context.fillStyle = fillState.fillStyle; + } + context.textBaseline = 'middle'; + context.textAlign = 'center'; + const leftRight = (0.5 - align); + const x = align * label.width / scale + leftRight * strokeWidth; + let i; + if (strokeKey) { + for (i = 0; i < numLines; ++i) { + context.strokeText(lines[i], x + leftRight * widths[i], 0.5 * (strokeWidth + lineHeight) + i * lineHeight); + } + } + if (fillKey) { + for (i = 0; i < numLines; ++i) { + context.fillText(lines[i], x + leftRight * widths[i], 0.5 * (strokeWidth + lineHeight) + i * lineHeight); + } + } + } + return labelCache.get(key); + } /** * @private - * @type {module:ol/render/canvas~DeclutterGroup} + * @param {HTMLCanvasElement} label Label. + * @param {number} begin Begin. + * @param {number} end End. */ - this.declutterGroup_; + drawTextImage_(label, begin, end) { + const textState = this.textState_; + const strokeState = this.textStrokeState_; + const pixelRatio = this.pixelRatio; + const align = TEXT_ALIGN[textState.textAlign || defaultTextAlign]; + const baseline = TEXT_ALIGN[textState.textBaseline]; + const strokeWidth = strokeState && strokeState.lineWidth ? strokeState.lineWidth : 0; + + const anchorX = align * label.width / pixelRatio + 2 * (0.5 - align) * strokeWidth; + const anchorY = baseline * label.height / pixelRatio + 2 * (0.5 - baseline) * strokeWidth; + this.instructions.push([CanvasInstruction.DRAW_IMAGE, begin, end, + label, (anchorX - this.textOffsetX_) * pixelRatio, (anchorY - this.textOffsetY_) * pixelRatio, + this.declutterGroup_, label.height, 1, 0, 0, this.textRotateWithView_, this.textRotation_, + 1, true, label.width, + textState.padding == defaultPadding ? + defaultPadding : textState.padding.map(function(p) { + return p * pixelRatio; + }), + !!textState.backgroundFill, !!textState.backgroundStroke + ]); + this.hitDetectionInstructions.push([CanvasInstruction.DRAW_IMAGE, begin, end, + label, (anchorX - this.textOffsetX_) * pixelRatio, (anchorY - this.textOffsetY_) * pixelRatio, + this.declutterGroup_, label.height, 1, 0, 0, this.textRotateWithView_, this.textRotation_, + 1 / pixelRatio, true, label.width, textState.padding, + !!textState.backgroundFill, !!textState.backgroundStroke + ]); + } /** * @private - * @type {Array.} + * @param {number} begin Begin. + * @param {number} end End. + * @param {module:ol/render/canvas~DeclutterGroup} declutterGroup Declutter group. */ - this.labels_ = null; + drawChars_(begin, end, declutterGroup) { + const strokeState = this.textStrokeState_; + const textState = this.textState_; + const fillState = this.textFillState_; + + const strokeKey = this.strokeKey_; + if (strokeState) { + if (!(strokeKey in this.strokeStates)) { + this.strokeStates[strokeKey] = /** @type {module:ol/render/canvas~StrokeState} */ ({ + strokeStyle: strokeState.strokeStyle, + lineCap: strokeState.lineCap, + lineDashOffset: strokeState.lineDashOffset, + lineWidth: strokeState.lineWidth, + lineJoin: strokeState.lineJoin, + miterLimit: strokeState.miterLimit, + lineDash: strokeState.lineDash + }); + } + } + const textKey = this.textKey_; + if (!(this.textKey_ in this.textStates)) { + this.textStates[this.textKey_] = /** @type {module:ol/render/canvas~TextState} */ ({ + font: textState.font, + textAlign: textState.textAlign || defaultTextAlign, + scale: textState.scale + }); + } + const fillKey = this.fillKey_; + if (fillState) { + if (!(fillKey in this.fillStates)) { + this.fillStates[fillKey] = /** @type {module:ol/render/canvas~FillState} */ ({ + fillStyle: fillState.fillStyle + }); + } + } + + const pixelRatio = this.pixelRatio; + const baseline = TEXT_ALIGN[textState.textBaseline]; + + const offsetY = this.textOffsetY_ * pixelRatio; + const text = this.text_; + const font = textState.font; + const textScale = textState.scale; + const strokeWidth = strokeState ? strokeState.lineWidth * textScale / 2 : 0; + let widths = this.widths_[font]; + if (!widths) { + this.widths_[font] = widths = {}; + } + this.instructions.push([CanvasInstruction.DRAW_CHARS, + begin, end, baseline, declutterGroup, + textState.overflow, fillKey, textState.maxAngle, + function(text) { + let width = widths[text]; + if (!width) { + width = widths[text] = measureTextWidth(font, text); + } + return width * textScale * pixelRatio; + }, + offsetY, strokeKey, strokeWidth * pixelRatio, text, textKey, 1 + ]); + this.hitDetectionInstructions.push([CanvasInstruction.DRAW_CHARS, + begin, end, baseline, declutterGroup, + textState.overflow, fillKey, textState.maxAngle, + function(text) { + let width = widths[text]; + if (!width) { + width = widths[text] = measureTextWidth(font, text); + } + return width * textScale; + }, + offsetY, strokeKey, strokeWidth, text, textKey, 1 / pixelRatio + ]); + } /** - * @private - * @type {string} + * @inheritDoc */ - this.text_ = ''; + setTextStyle(textStyle, declutterGroup) { + let textState, fillState, strokeState; + if (!textStyle) { + this.text_ = ''; + } else { + this.declutterGroup_ = /** @type {module:ol/render/canvas~DeclutterGroup} */ (declutterGroup); - /** - * @private - * @type {number} - */ - this.textOffsetX_ = 0; + const textFillStyle = textStyle.getFill(); + if (!textFillStyle) { + fillState = this.textFillState_ = null; + } else { + fillState = this.textFillState_; + if (!fillState) { + fillState = this.textFillState_ = /** @type {module:ol/render/canvas~FillState} */ ({}); + } + fillState.fillStyle = asColorLike( + textFillStyle.getColor() || defaultFillStyle); + } - /** - * @private - * @type {number} - */ - this.textOffsetY_ = 0; + const textStrokeStyle = textStyle.getStroke(); + if (!textStrokeStyle) { + strokeState = this.textStrokeState_ = null; + } else { + strokeState = this.textStrokeState_; + if (!strokeState) { + strokeState = this.textStrokeState_ = /** @type {module:ol/render/canvas~StrokeState} */ ({}); + } + const lineDash = textStrokeStyle.getLineDash(); + const lineDashOffset = textStrokeStyle.getLineDashOffset(); + const lineWidth = textStrokeStyle.getWidth(); + const miterLimit = textStrokeStyle.getMiterLimit(); + strokeState.lineCap = textStrokeStyle.getLineCap() || defaultLineCap; + strokeState.lineDash = lineDash ? lineDash.slice() : defaultLineDash; + strokeState.lineDashOffset = + lineDashOffset === undefined ? defaultLineDashOffset : lineDashOffset; + strokeState.lineJoin = textStrokeStyle.getLineJoin() || defaultLineJoin; + strokeState.lineWidth = + lineWidth === undefined ? defaultLineWidth : lineWidth; + strokeState.miterLimit = + miterLimit === undefined ? defaultMiterLimit : miterLimit; + strokeState.strokeStyle = asColorLike( + textStrokeStyle.getColor() || defaultStrokeStyle); + } - /** - * @private - * @type {boolean|undefined} - */ - this.textRotateWithView_ = undefined; + textState = this.textState_; + const font = textStyle.getFont() || defaultFont; + checkFont(font); + const textScale = textStyle.getScale(); + textState.overflow = textStyle.getOverflow(); + textState.font = font; + textState.maxAngle = textStyle.getMaxAngle(); + textState.placement = textStyle.getPlacement(); + textState.textAlign = textStyle.getTextAlign(); + textState.textBaseline = textStyle.getTextBaseline() || defaultTextBaseline; + textState.backgroundFill = textStyle.getBackgroundFill(); + textState.backgroundStroke = textStyle.getBackgroundStroke(); + textState.padding = textStyle.getPadding() || defaultPadding; + textState.scale = textScale === undefined ? 1 : textScale; - /** - * @private - * @type {number} - */ - this.textRotation_ = 0; + const textOffsetX = textStyle.getOffsetX(); + const textOffsetY = textStyle.getOffsetY(); + const textRotateWithView = textStyle.getRotateWithView(); + const textRotation = textStyle.getRotation(); + this.text_ = textStyle.getText() || ''; + this.textOffsetX_ = textOffsetX === undefined ? 0 : textOffsetX; + this.textOffsetY_ = textOffsetY === undefined ? 0 : textOffsetY; + this.textRotateWithView_ = textRotateWithView === undefined ? false : textRotateWithView; + this.textRotation_ = textRotation === undefined ? 0 : textRotation; - /** - * @private - * @type {?module:ol/render/canvas~FillState} - */ - this.textFillState_ = null; - - /** - * @type {!Object.} - */ - this.fillStates = {}; - - /** - * @private - * @type {?module:ol/render/canvas~StrokeState} - */ - this.textStrokeState_ = null; - - /** - * @type {!Object.} - */ - this.strokeStates = {}; - - /** - * @private - * @type {module:ol/render/canvas~TextState} - */ - this.textState_ = /** @type {module:ol/render/canvas~TextState} */ ({}); - - /** - * @type {!Object.} - */ - this.textStates = {}; - - /** - * @private - * @type {string} - */ - this.textKey_ = ''; - - /** - * @private - * @type {string} - */ - this.fillKey_ = ''; - - /** - * @private - * @type {string} - */ - this.strokeKey_ = ''; - - /** - * @private - * @type {Object.>} - */ - this.widths_ = {}; - - labelCache.prune(); - -}; + this.strokeKey_ = strokeState ? + (typeof strokeState.strokeStyle == 'string' ? strokeState.strokeStyle : getUid(strokeState.strokeStyle)) + + strokeState.lineCap + strokeState.lineDashOffset + '|' + strokeState.lineWidth + + strokeState.lineJoin + strokeState.miterLimit + '[' + strokeState.lineDash.join() + ']' : + ''; + this.textKey_ = textState.font + textState.scale + (textState.textAlign || '?'); + this.fillKey_ = fillState ? + (typeof fillState.fillStyle == 'string' ? fillState.fillStyle : ('|' + getUid(fillState.fillStyle))) : + ''; + } + } +} inherits(CanvasTextReplay, CanvasReplay); @@ -155,394 +543,4 @@ export function measureTextWidths(font, lines, widths) { } -/** - * @inheritDoc - */ -CanvasTextReplay.prototype.drawText = function(geometry, feature) { - const fillState = this.textFillState_; - const strokeState = this.textStrokeState_; - const textState = this.textState_; - if (this.text_ === '' || !textState || (!fillState && !strokeState)) { - return; - } - - let begin = this.coordinates.length; - - const geometryType = geometry.getType(); - let flatCoordinates = null; - let end = 2; - let stride = 2; - let i, ii; - - if (textState.placement === TextPlacement.LINE) { - if (!intersects(this.getBufferedMaxExtent(), geometry.getExtent())) { - return; - } - let ends; - flatCoordinates = geometry.getFlatCoordinates(); - stride = geometry.getStride(); - if (geometryType == GeometryType.LINE_STRING) { - ends = [flatCoordinates.length]; - } else if (geometryType == GeometryType.MULTI_LINE_STRING) { - ends = geometry.getEnds(); - } else if (geometryType == GeometryType.POLYGON) { - ends = geometry.getEnds().slice(0, 1); - } else if (geometryType == GeometryType.MULTI_POLYGON) { - const endss = geometry.getEndss(); - ends = []; - for (i = 0, ii = endss.length; i < ii; ++i) { - ends.push(endss[i][0]); - } - } - this.beginGeometry(geometry, feature); - const textAlign = textState.textAlign; - let flatOffset = 0; - let flatEnd; - for (let o = 0, oo = ends.length; o < oo; ++o) { - if (textAlign == undefined) { - const range = matchingChunk(textState.maxAngle, flatCoordinates, flatOffset, ends[o], stride); - flatOffset = range[0]; - flatEnd = range[1]; - } else { - flatEnd = ends[o]; - } - for (i = flatOffset; i < flatEnd; i += stride) { - this.coordinates.push(flatCoordinates[i], flatCoordinates[i + 1]); - } - end = this.coordinates.length; - flatOffset = ends[o]; - this.drawChars_(begin, end, this.declutterGroup_); - begin = end; - } - this.endGeometry(geometry, feature); - - } else { - const label = this.getImage(this.text_, this.textKey_, this.fillKey_, this.strokeKey_); - const width = label.width / this.pixelRatio; - switch (geometryType) { - case GeometryType.POINT: - case GeometryType.MULTI_POINT: - flatCoordinates = geometry.getFlatCoordinates(); - end = flatCoordinates.length; - break; - case GeometryType.LINE_STRING: - flatCoordinates = /** @type {module:ol/geom/LineString} */ (geometry).getFlatMidpoint(); - break; - case GeometryType.CIRCLE: - flatCoordinates = /** @type {module:ol/geom/Circle} */ (geometry).getCenter(); - break; - case GeometryType.MULTI_LINE_STRING: - flatCoordinates = /** @type {module:ol/geom/MultiLineString} */ (geometry).getFlatMidpoints(); - end = flatCoordinates.length; - break; - case GeometryType.POLYGON: - flatCoordinates = /** @type {module:ol/geom/Polygon} */ (geometry).getFlatInteriorPoint(); - if (!textState.overflow && flatCoordinates[2] / this.resolution < width) { - return; - } - stride = 3; - break; - case GeometryType.MULTI_POLYGON: - const interiorPoints = /** @type {module:ol/geom/MultiPolygon} */ (geometry).getFlatInteriorPoints(); - flatCoordinates = []; - for (i = 0, ii = interiorPoints.length; i < ii; i += 3) { - if (textState.overflow || interiorPoints[i + 2] / this.resolution >= width) { - flatCoordinates.push(interiorPoints[i], interiorPoints[i + 1]); - } - } - end = flatCoordinates.length; - if (end == 0) { - return; - } - break; - default: - } - end = this.appendFlatCoordinates(flatCoordinates, 0, end, stride, false, false); - if (textState.backgroundFill || textState.backgroundStroke) { - this.setFillStrokeStyle(textState.backgroundFill, textState.backgroundStroke); - if (textState.backgroundFill) { - this.updateFillStyle(this.state, this.createFill, geometry); - this.hitDetectionInstructions.push(this.createFill(this.state, geometry)); - } - if (textState.backgroundStroke) { - this.updateStrokeStyle(this.state, this.applyStroke); - this.hitDetectionInstructions.push(this.createStroke(this.state)); - } - } - this.beginGeometry(geometry, feature); - this.drawTextImage_(label, begin, end); - this.endGeometry(geometry, feature); - } -}; - - -/** - * @param {string} text Text. - * @param {string} textKey Text style key. - * @param {string} fillKey Fill style key. - * @param {string} strokeKey Stroke style key. - * @return {HTMLCanvasElement} Image. - */ -CanvasTextReplay.prototype.getImage = function(text, textKey, fillKey, strokeKey) { - let label; - const key = strokeKey + textKey + text + fillKey + this.pixelRatio; - - if (!labelCache.containsKey(key)) { - const strokeState = strokeKey ? this.strokeStates[strokeKey] || this.textStrokeState_ : null; - const fillState = fillKey ? this.fillStates[fillKey] || this.textFillState_ : null; - const textState = this.textStates[textKey] || this.textState_; - const pixelRatio = this.pixelRatio; - const scale = textState.scale * pixelRatio; - const align = TEXT_ALIGN[textState.textAlign || defaultTextAlign]; - const strokeWidth = strokeKey && strokeState.lineWidth ? strokeState.lineWidth : 0; - - const lines = text.split('\n'); - const numLines = lines.length; - const widths = []; - const width = measureTextWidths(textState.font, lines, widths); - const lineHeight = measureTextHeight(textState.font); - const height = lineHeight * numLines; - const renderWidth = (width + strokeWidth); - const context = createCanvasContext2D( - Math.ceil(renderWidth * scale), - Math.ceil((height + strokeWidth) * scale)); - label = context.canvas; - labelCache.set(key, label); - if (scale != 1) { - context.scale(scale, scale); - } - context.font = textState.font; - if (strokeKey) { - context.strokeStyle = strokeState.strokeStyle; - context.lineWidth = strokeWidth; - context.lineCap = strokeState.lineCap; - context.lineJoin = strokeState.lineJoin; - context.miterLimit = strokeState.miterLimit; - if (CANVAS_LINE_DASH && strokeState.lineDash.length) { - context.setLineDash(strokeState.lineDash); - context.lineDashOffset = strokeState.lineDashOffset; - } - } - if (fillKey) { - context.fillStyle = fillState.fillStyle; - } - context.textBaseline = 'middle'; - context.textAlign = 'center'; - const leftRight = (0.5 - align); - const x = align * label.width / scale + leftRight * strokeWidth; - let i; - if (strokeKey) { - for (i = 0; i < numLines; ++i) { - context.strokeText(lines[i], x + leftRight * widths[i], 0.5 * (strokeWidth + lineHeight) + i * lineHeight); - } - } - if (fillKey) { - for (i = 0; i < numLines; ++i) { - context.fillText(lines[i], x + leftRight * widths[i], 0.5 * (strokeWidth + lineHeight) + i * lineHeight); - } - } - } - return labelCache.get(key); -}; - - -/** - * @private - * @param {HTMLCanvasElement} label Label. - * @param {number} begin Begin. - * @param {number} end End. - */ -CanvasTextReplay.prototype.drawTextImage_ = function(label, begin, end) { - const textState = this.textState_; - const strokeState = this.textStrokeState_; - const pixelRatio = this.pixelRatio; - const align = TEXT_ALIGN[textState.textAlign || defaultTextAlign]; - const baseline = TEXT_ALIGN[textState.textBaseline]; - const strokeWidth = strokeState && strokeState.lineWidth ? strokeState.lineWidth : 0; - - const anchorX = align * label.width / pixelRatio + 2 * (0.5 - align) * strokeWidth; - const anchorY = baseline * label.height / pixelRatio + 2 * (0.5 - baseline) * strokeWidth; - this.instructions.push([CanvasInstruction.DRAW_IMAGE, begin, end, - label, (anchorX - this.textOffsetX_) * pixelRatio, (anchorY - this.textOffsetY_) * pixelRatio, - this.declutterGroup_, label.height, 1, 0, 0, this.textRotateWithView_, this.textRotation_, - 1, true, label.width, - textState.padding == defaultPadding ? - defaultPadding : textState.padding.map(function(p) { - return p * pixelRatio; - }), - !!textState.backgroundFill, !!textState.backgroundStroke - ]); - this.hitDetectionInstructions.push([CanvasInstruction.DRAW_IMAGE, begin, end, - label, (anchorX - this.textOffsetX_) * pixelRatio, (anchorY - this.textOffsetY_) * pixelRatio, - this.declutterGroup_, label.height, 1, 0, 0, this.textRotateWithView_, this.textRotation_, - 1 / pixelRatio, true, label.width, textState.padding, - !!textState.backgroundFill, !!textState.backgroundStroke - ]); -}; - - -/** - * @private - * @param {number} begin Begin. - * @param {number} end End. - * @param {module:ol/render/canvas~DeclutterGroup} declutterGroup Declutter group. - */ -CanvasTextReplay.prototype.drawChars_ = function(begin, end, declutterGroup) { - const strokeState = this.textStrokeState_; - const textState = this.textState_; - const fillState = this.textFillState_; - - const strokeKey = this.strokeKey_; - if (strokeState) { - if (!(strokeKey in this.strokeStates)) { - this.strokeStates[strokeKey] = /** @type {module:ol/render/canvas~StrokeState} */ ({ - strokeStyle: strokeState.strokeStyle, - lineCap: strokeState.lineCap, - lineDashOffset: strokeState.lineDashOffset, - lineWidth: strokeState.lineWidth, - lineJoin: strokeState.lineJoin, - miterLimit: strokeState.miterLimit, - lineDash: strokeState.lineDash - }); - } - } - const textKey = this.textKey_; - if (!(this.textKey_ in this.textStates)) { - this.textStates[this.textKey_] = /** @type {module:ol/render/canvas~TextState} */ ({ - font: textState.font, - textAlign: textState.textAlign || defaultTextAlign, - scale: textState.scale - }); - } - const fillKey = this.fillKey_; - if (fillState) { - if (!(fillKey in this.fillStates)) { - this.fillStates[fillKey] = /** @type {module:ol/render/canvas~FillState} */ ({ - fillStyle: fillState.fillStyle - }); - } - } - - const pixelRatio = this.pixelRatio; - const baseline = TEXT_ALIGN[textState.textBaseline]; - - const offsetY = this.textOffsetY_ * pixelRatio; - const text = this.text_; - const font = textState.font; - const textScale = textState.scale; - const strokeWidth = strokeState ? strokeState.lineWidth * textScale / 2 : 0; - let widths = this.widths_[font]; - if (!widths) { - this.widths_[font] = widths = {}; - } - this.instructions.push([CanvasInstruction.DRAW_CHARS, - begin, end, baseline, declutterGroup, - textState.overflow, fillKey, textState.maxAngle, - function(text) { - let width = widths[text]; - if (!width) { - width = widths[text] = measureTextWidth(font, text); - } - return width * textScale * pixelRatio; - }, - offsetY, strokeKey, strokeWidth * pixelRatio, text, textKey, 1 - ]); - this.hitDetectionInstructions.push([CanvasInstruction.DRAW_CHARS, - begin, end, baseline, declutterGroup, - textState.overflow, fillKey, textState.maxAngle, - function(text) { - let width = widths[text]; - if (!width) { - width = widths[text] = measureTextWidth(font, text); - } - return width * textScale; - }, - offsetY, strokeKey, strokeWidth, text, textKey, 1 / pixelRatio - ]); -}; - - -/** - * @inheritDoc - */ -CanvasTextReplay.prototype.setTextStyle = function(textStyle, declutterGroup) { - let textState, fillState, strokeState; - if (!textStyle) { - this.text_ = ''; - } else { - this.declutterGroup_ = /** @type {module:ol/render/canvas~DeclutterGroup} */ (declutterGroup); - - const textFillStyle = textStyle.getFill(); - if (!textFillStyle) { - fillState = this.textFillState_ = null; - } else { - fillState = this.textFillState_; - if (!fillState) { - fillState = this.textFillState_ = /** @type {module:ol/render/canvas~FillState} */ ({}); - } - fillState.fillStyle = asColorLike( - textFillStyle.getColor() || defaultFillStyle); - } - - const textStrokeStyle = textStyle.getStroke(); - if (!textStrokeStyle) { - strokeState = this.textStrokeState_ = null; - } else { - strokeState = this.textStrokeState_; - if (!strokeState) { - strokeState = this.textStrokeState_ = /** @type {module:ol/render/canvas~StrokeState} */ ({}); - } - const lineDash = textStrokeStyle.getLineDash(); - const lineDashOffset = textStrokeStyle.getLineDashOffset(); - const lineWidth = textStrokeStyle.getWidth(); - const miterLimit = textStrokeStyle.getMiterLimit(); - strokeState.lineCap = textStrokeStyle.getLineCap() || defaultLineCap; - strokeState.lineDash = lineDash ? lineDash.slice() : defaultLineDash; - strokeState.lineDashOffset = - lineDashOffset === undefined ? defaultLineDashOffset : lineDashOffset; - strokeState.lineJoin = textStrokeStyle.getLineJoin() || defaultLineJoin; - strokeState.lineWidth = - lineWidth === undefined ? defaultLineWidth : lineWidth; - strokeState.miterLimit = - miterLimit === undefined ? defaultMiterLimit : miterLimit; - strokeState.strokeStyle = asColorLike( - textStrokeStyle.getColor() || defaultStrokeStyle); - } - - textState = this.textState_; - const font = textStyle.getFont() || defaultFont; - checkFont(font); - const textScale = textStyle.getScale(); - textState.overflow = textStyle.getOverflow(); - textState.font = font; - textState.maxAngle = textStyle.getMaxAngle(); - textState.placement = textStyle.getPlacement(); - textState.textAlign = textStyle.getTextAlign(); - textState.textBaseline = textStyle.getTextBaseline() || defaultTextBaseline; - textState.backgroundFill = textStyle.getBackgroundFill(); - textState.backgroundStroke = textStyle.getBackgroundStroke(); - textState.padding = textStyle.getPadding() || defaultPadding; - textState.scale = textScale === undefined ? 1 : textScale; - - const textOffsetX = textStyle.getOffsetX(); - const textOffsetY = textStyle.getOffsetY(); - const textRotateWithView = textStyle.getRotateWithView(); - const textRotation = textStyle.getRotation(); - this.text_ = textStyle.getText() || ''; - this.textOffsetX_ = textOffsetX === undefined ? 0 : textOffsetX; - this.textOffsetY_ = textOffsetY === undefined ? 0 : textOffsetY; - this.textRotateWithView_ = textRotateWithView === undefined ? false : textRotateWithView; - this.textRotation_ = textRotation === undefined ? 0 : textRotation; - - this.strokeKey_ = strokeState ? - (typeof strokeState.strokeStyle == 'string' ? strokeState.strokeStyle : getUid(strokeState.strokeStyle)) + - strokeState.lineCap + strokeState.lineDashOffset + '|' + strokeState.lineWidth + - strokeState.lineJoin + strokeState.miterLimit + '[' + strokeState.lineDash.join() + ']' : - ''; - this.textKey_ = textState.font + textState.scale + (textState.textAlign || '?'); - this.fillKey_ = fillState ? - (typeof fillState.fillStyle == 'string' ? fillState.fillStyle : ('|' + getUid(fillState.fillStyle))) : - ''; - } -}; export default CanvasTextReplay; diff --git a/src/ol/render/webgl/CircleReplay.js b/src/ol/render/webgl/CircleReplay.js index 2be78ff8e4..6d98db4d2c 100644 --- a/src/ol/render/webgl/CircleReplay.js +++ b/src/ol/render/webgl/CircleReplay.js @@ -22,399 +22,389 @@ import WebGLBuffer from '../../webgl/Buffer.js'; * @param {module:ol/extent~Extent} maxExtent Max extent. * @struct */ -const WebGLCircleReplay = function(tolerance, maxExtent) { - WebGLReplay.call(this, tolerance, maxExtent); +class WebGLCircleReplay { + constructor(tolerance, maxExtent) { + WebGLReplay.call(this, tolerance, maxExtent); - /** - * @private - * @type {module:ol/render/webgl/circlereplay/defaultshader/Locations} - */ - this.defaultLocations_ = null; + /** + * @private + * @type {module:ol/render/webgl/circlereplay/defaultshader/Locations} + */ + this.defaultLocations_ = null; - /** - * @private - * @type {Array.|number>>} - */ - this.styles_ = []; + /** + * @private + * @type {Array.|number>>} + */ + this.styles_ = []; - /** - * @private - * @type {Array.} - */ - this.styleIndices_ = []; + /** + * @private + * @type {Array.} + */ + this.styleIndices_ = []; - /** - * @private - * @type {number} - */ - this.radius_ = 0; + /** + * @private + * @type {number} + */ + this.radius_ = 0; - /** - * @private - * @type {{fillColor: (Array.|null), - * strokeColor: (Array.|null), - * lineDash: Array., - * lineDashOffset: (number|undefined), - * lineWidth: (number|undefined), - * changed: boolean}|null} - */ - this.state_ = { - fillColor: null, - strokeColor: null, - lineDash: null, - lineDashOffset: undefined, - lineWidth: undefined, - changed: false - }; + /** + * @private + * @type {{fillColor: (Array.|null), + * strokeColor: (Array.|null), + * lineDash: Array., + * lineDashOffset: (number|undefined), + * lineWidth: (number|undefined), + * changed: boolean}|null} + */ + this.state_ = { + fillColor: null, + strokeColor: null, + lineDash: null, + lineDashOffset: undefined, + lineWidth: undefined, + changed: false + }; -}; - -inherits(WebGLCircleReplay, WebGLReplay); - - -/** - * @private - * @param {Array.} flatCoordinates Flat coordinates. - * @param {number} offset Offset. - * @param {number} end End. - * @param {number} stride Stride. - */ -WebGLCircleReplay.prototype.drawCoordinates_ = function( - flatCoordinates, offset, end, stride) { - let numVertices = this.vertices.length; - let numIndices = this.indices.length; - let n = numVertices / 4; - let i, ii; - for (i = offset, ii = end; i < ii; i += stride) { - this.vertices[numVertices++] = flatCoordinates[i]; - this.vertices[numVertices++] = flatCoordinates[i + 1]; - this.vertices[numVertices++] = 0; - this.vertices[numVertices++] = this.radius_; - - this.vertices[numVertices++] = flatCoordinates[i]; - this.vertices[numVertices++] = flatCoordinates[i + 1]; - this.vertices[numVertices++] = 1; - this.vertices[numVertices++] = this.radius_; - - this.vertices[numVertices++] = flatCoordinates[i]; - this.vertices[numVertices++] = flatCoordinates[i + 1]; - this.vertices[numVertices++] = 2; - this.vertices[numVertices++] = this.radius_; - - this.vertices[numVertices++] = flatCoordinates[i]; - this.vertices[numVertices++] = flatCoordinates[i + 1]; - this.vertices[numVertices++] = 3; - this.vertices[numVertices++] = this.radius_; - - this.indices[numIndices++] = n; - this.indices[numIndices++] = n + 1; - this.indices[numIndices++] = n + 2; - - this.indices[numIndices++] = n + 2; - this.indices[numIndices++] = n + 3; - this.indices[numIndices++] = n; - - n += 4; } -}; + /** + * @private + * @param {Array.} flatCoordinates Flat coordinates. + * @param {number} offset Offset. + * @param {number} end End. + * @param {number} stride Stride. + */ + drawCoordinates_(flatCoordinates, offset, end, stride) { + let numVertices = this.vertices.length; + let numIndices = this.indices.length; + let n = numVertices / 4; + let i, ii; + for (i = offset, ii = end; i < ii; i += stride) { + this.vertices[numVertices++] = flatCoordinates[i]; + this.vertices[numVertices++] = flatCoordinates[i + 1]; + this.vertices[numVertices++] = 0; + this.vertices[numVertices++] = this.radius_; -/** - * @inheritDoc - */ -WebGLCircleReplay.prototype.drawCircle = function(circleGeometry, feature) { - const radius = circleGeometry.getRadius(); - const stride = circleGeometry.getStride(); - if (radius) { - this.startIndices.push(this.indices.length); - this.startIndicesFeature.push(feature); - if (this.state_.changed) { - this.styleIndices_.push(this.indices.length); - this.state_.changed = false; + this.vertices[numVertices++] = flatCoordinates[i]; + this.vertices[numVertices++] = flatCoordinates[i + 1]; + this.vertices[numVertices++] = 1; + this.vertices[numVertices++] = this.radius_; + + this.vertices[numVertices++] = flatCoordinates[i]; + this.vertices[numVertices++] = flatCoordinates[i + 1]; + this.vertices[numVertices++] = 2; + this.vertices[numVertices++] = this.radius_; + + this.vertices[numVertices++] = flatCoordinates[i]; + this.vertices[numVertices++] = flatCoordinates[i + 1]; + this.vertices[numVertices++] = 3; + this.vertices[numVertices++] = this.radius_; + + this.indices[numIndices++] = n; + this.indices[numIndices++] = n + 1; + this.indices[numIndices++] = n + 2; + + this.indices[numIndices++] = n + 2; + this.indices[numIndices++] = n + 3; + this.indices[numIndices++] = n; + + n += 4; } + } - this.radius_ = radius; - let flatCoordinates = circleGeometry.getFlatCoordinates(); - flatCoordinates = translate(flatCoordinates, 0, 2, - stride, -this.origin[0], -this.origin[1]); - this.drawCoordinates_(flatCoordinates, 0, 2, stride); - } else { - if (this.state_.changed) { - this.styles_.pop(); - if (this.styles_.length) { - const lastState = this.styles_[this.styles_.length - 1]; - this.state_.fillColor = /** @type {Array.} */ (lastState[0]); - this.state_.strokeColor = /** @type {Array.} */ (lastState[1]); - this.state_.lineWidth = /** @type {number} */ (lastState[2]); + /** + * @inheritDoc + */ + drawCircle(circleGeometry, feature) { + const radius = circleGeometry.getRadius(); + const stride = circleGeometry.getStride(); + if (radius) { + this.startIndices.push(this.indices.length); + this.startIndicesFeature.push(feature); + if (this.state_.changed) { + this.styleIndices_.push(this.indices.length); this.state_.changed = false; } + + this.radius_ = radius; + let flatCoordinates = circleGeometry.getFlatCoordinates(); + flatCoordinates = translate(flatCoordinates, 0, 2, + stride, -this.origin[0], -this.origin[1]); + this.drawCoordinates_(flatCoordinates, 0, 2, stride); + } else { + if (this.state_.changed) { + this.styles_.pop(); + if (this.styles_.length) { + const lastState = this.styles_[this.styles_.length - 1]; + this.state_.fillColor = /** @type {Array.} */ (lastState[0]); + this.state_.strokeColor = /** @type {Array.} */ (lastState[1]); + this.state_.lineWidth = /** @type {number} */ (lastState[2]); + this.state_.changed = false; + } + } } } -}; + /** + * @inheritDoc + **/ + finish(context) { + // create, bind, and populate the vertices buffer + this.verticesBuffer = new WebGLBuffer(this.vertices); -/** - * @inheritDoc - **/ -WebGLCircleReplay.prototype.finish = function(context) { - // create, bind, and populate the vertices buffer - this.verticesBuffer = new WebGLBuffer(this.vertices); + // create, bind, and populate the indices buffer + this.indicesBuffer = new WebGLBuffer(this.indices); - // create, bind, and populate the indices buffer - this.indicesBuffer = new WebGLBuffer(this.indices); + this.startIndices.push(this.indices.length); - this.startIndices.push(this.indices.length); + //Clean up, if there is nothing to draw + if (this.styleIndices_.length === 0 && this.styles_.length > 0) { + this.styles_ = []; + } - //Clean up, if there is nothing to draw - if (this.styleIndices_.length === 0 && this.styles_.length > 0) { - this.styles_ = []; + this.vertices = null; + this.indices = null; } - this.vertices = null; - this.indices = null; -}; - - -/** - * @inheritDoc - */ -WebGLCircleReplay.prototype.getDeleteResourcesFunction = function(context) { - // We only delete our stuff here. The shaders and the program may - // be used by other CircleReplay instances (for other layers). And - // they will be deleted when disposing of the module:ol/webgl/Context~WebGLContext - // object. - const verticesBuffer = this.verticesBuffer; - const indicesBuffer = this.indicesBuffer; - return function() { - context.deleteBuffer(verticesBuffer); - context.deleteBuffer(indicesBuffer); - }; -}; - - -/** - * @inheritDoc - */ -WebGLCircleReplay.prototype.setUpProgram = function(gl, context, size, pixelRatio) { - // get the program - const program = context.getProgram(fragment, vertex); - - // get the locations - let locations; - if (!this.defaultLocations_) { - locations = new Locations(gl, program); - this.defaultLocations_ = locations; - } else { - locations = this.defaultLocations_; + /** + * @inheritDoc + */ + getDeleteResourcesFunction(context) { + // We only delete our stuff here. The shaders and the program may + // be used by other CircleReplay instances (for other layers). And + // they will be deleted when disposing of the module:ol/webgl/Context~WebGLContext + // object. + const verticesBuffer = this.verticesBuffer; + const indicesBuffer = this.indicesBuffer; + return function() { + context.deleteBuffer(verticesBuffer); + context.deleteBuffer(indicesBuffer); + }; } - context.useProgram(program); + /** + * @inheritDoc + */ + setUpProgram(gl, context, size, pixelRatio) { + // get the program + const program = context.getProgram(fragment, vertex); - // enable the vertex attrib arrays - gl.enableVertexAttribArray(locations.a_position); - gl.vertexAttribPointer(locations.a_position, 2, FLOAT, - false, 16, 0); + // get the locations + let locations; + if (!this.defaultLocations_) { + locations = new Locations(gl, program); + this.defaultLocations_ = locations; + } else { + locations = this.defaultLocations_; + } - gl.enableVertexAttribArray(locations.a_instruction); - gl.vertexAttribPointer(locations.a_instruction, 1, FLOAT, - false, 16, 8); + context.useProgram(program); - gl.enableVertexAttribArray(locations.a_radius); - gl.vertexAttribPointer(locations.a_radius, 1, FLOAT, - false, 16, 12); + // enable the vertex attrib arrays + gl.enableVertexAttribArray(locations.a_position); + gl.vertexAttribPointer(locations.a_position, 2, FLOAT, + false, 16, 0); - // Enable renderer specific uniforms. - gl.uniform2fv(locations.u_size, size); - gl.uniform1f(locations.u_pixelRatio, pixelRatio); + gl.enableVertexAttribArray(locations.a_instruction); + gl.vertexAttribPointer(locations.a_instruction, 1, FLOAT, + false, 16, 8); - return locations; -}; + gl.enableVertexAttribArray(locations.a_radius); + gl.vertexAttribPointer(locations.a_radius, 1, FLOAT, + false, 16, 12); + // Enable renderer specific uniforms. + gl.uniform2fv(locations.u_size, size); + gl.uniform1f(locations.u_pixelRatio, pixelRatio); -/** - * @inheritDoc - */ -WebGLCircleReplay.prototype.shutDownProgram = function(gl, locations) { - gl.disableVertexAttribArray(locations.a_position); - gl.disableVertexAttribArray(locations.a_instruction); - gl.disableVertexAttribArray(locations.a_radius); -}; + return locations; + } + /** + * @inheritDoc + */ + shutDownProgram(gl, locations) { + gl.disableVertexAttribArray(locations.a_position); + gl.disableVertexAttribArray(locations.a_instruction); + gl.disableVertexAttribArray(locations.a_radius); + } -/** - * @inheritDoc - */ -WebGLCircleReplay.prototype.drawReplay = function(gl, context, skippedFeaturesHash, hitDetection) { - if (!isEmpty(skippedFeaturesHash)) { - this.drawReplaySkipping_(gl, context, skippedFeaturesHash); - } else { - //Draw by style groups to minimize drawElements() calls. - let i, start, end, nextStyle; - end = this.startIndices[this.startIndices.length - 1]; + /** + * @inheritDoc + */ + drawReplay(gl, context, skippedFeaturesHash, hitDetection) { + if (!isEmpty(skippedFeaturesHash)) { + this.drawReplaySkipping_(gl, context, skippedFeaturesHash); + } else { + //Draw by style groups to minimize drawElements() calls. + let i, start, end, nextStyle; + end = this.startIndices[this.startIndices.length - 1]; + for (i = this.styleIndices_.length - 1; i >= 0; --i) { + start = this.styleIndices_[i]; + nextStyle = this.styles_[i]; + this.setFillStyle_(gl, /** @type {Array.} */ (nextStyle[0])); + this.setStrokeStyle_(gl, /** @type {Array.} */ (nextStyle[1]), + /** @type {number} */ (nextStyle[2])); + this.drawElements(gl, context, start, end); + end = start; + } + } + } + + /** + * @inheritDoc + */ + drawHitDetectionReplayOneByOne(gl, context, skippedFeaturesHash, featureCallback, opt_hitExtent) { + let i, start, end, nextStyle, groupStart, feature, featureUid, featureIndex; + featureIndex = this.startIndices.length - 2; + end = this.startIndices[featureIndex + 1]; for (i = this.styleIndices_.length - 1; i >= 0; --i) { - start = this.styleIndices_[i]; nextStyle = this.styles_[i]; this.setFillStyle_(gl, /** @type {Array.} */ (nextStyle[0])); this.setStrokeStyle_(gl, /** @type {Array.} */ (nextStyle[1]), /** @type {number} */ (nextStyle[2])); - this.drawElements(gl, context, start, end); - end = start; - } - } -}; + groupStart = this.styleIndices_[i]; + while (featureIndex >= 0 && + this.startIndices[featureIndex] >= groupStart) { + start = this.startIndices[featureIndex]; + feature = this.startIndicesFeature[featureIndex]; + featureUid = getUid(feature).toString(); -/** - * @inheritDoc - */ -WebGLCircleReplay.prototype.drawHitDetectionReplayOneByOne = function(gl, context, skippedFeaturesHash, - featureCallback, opt_hitExtent) { - let i, start, end, nextStyle, groupStart, feature, featureUid, featureIndex; - featureIndex = this.startIndices.length - 2; - end = this.startIndices[featureIndex + 1]; - for (i = this.styleIndices_.length - 1; i >= 0; --i) { - nextStyle = this.styles_[i]; - this.setFillStyle_(gl, /** @type {Array.} */ (nextStyle[0])); - this.setStrokeStyle_(gl, /** @type {Array.} */ (nextStyle[1]), - /** @type {number} */ (nextStyle[2])); - groupStart = this.styleIndices_[i]; - - while (featureIndex >= 0 && - this.startIndices[featureIndex] >= groupStart) { - start = this.startIndices[featureIndex]; - feature = this.startIndicesFeature[featureIndex]; - featureUid = getUid(feature).toString(); - - if (skippedFeaturesHash[featureUid] === undefined && - feature.getGeometry() && - (opt_hitExtent === undefined || intersects( - /** @type {Array} */ (opt_hitExtent), - feature.getGeometry().getExtent()))) { - gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); - this.drawElements(gl, context, start, end); - - const result = featureCallback(feature); - - if (result) { - return result; - } - - } - featureIndex--; - end = start; - } - } - return undefined; -}; - - -/** - * @private - * @param {WebGLRenderingContext} gl gl. - * @param {module:ol/webgl/Context} context Context. - * @param {Object} skippedFeaturesHash Ids of features to skip. - */ -WebGLCircleReplay.prototype.drawReplaySkipping_ = function(gl, context, skippedFeaturesHash) { - let i, start, end, nextStyle, groupStart, feature, featureUid, featureIndex, featureStart; - featureIndex = this.startIndices.length - 2; - end = start = this.startIndices[featureIndex + 1]; - for (i = this.styleIndices_.length - 1; i >= 0; --i) { - nextStyle = this.styles_[i]; - this.setFillStyle_(gl, /** @type {Array.} */ (nextStyle[0])); - this.setStrokeStyle_(gl, /** @type {Array.} */ (nextStyle[1]), - /** @type {number} */ (nextStyle[2])); - groupStart = this.styleIndices_[i]; - - while (featureIndex >= 0 && - this.startIndices[featureIndex] >= groupStart) { - featureStart = this.startIndices[featureIndex]; - feature = this.startIndicesFeature[featureIndex]; - featureUid = getUid(feature).toString(); - - if (skippedFeaturesHash[featureUid]) { - if (start !== end) { + if (skippedFeaturesHash[featureUid] === undefined && + feature.getGeometry() && + (opt_hitExtent === undefined || intersects( + /** @type {Array} */ (opt_hitExtent), + feature.getGeometry().getExtent()))) { + gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); this.drawElements(gl, context, start, end); + + const result = featureCallback(feature); + + if (result) { + return result; + } + } - end = featureStart; + featureIndex--; + end = start; } - featureIndex--; - start = featureStart; } - if (start !== end) { - this.drawElements(gl, context, start, end); - } - start = end = groupStart; + return undefined; } -}; + /** + * @private + * @param {WebGLRenderingContext} gl gl. + * @param {module:ol/webgl/Context} context Context. + * @param {Object} skippedFeaturesHash Ids of features to skip. + */ + drawReplaySkipping_(gl, context, skippedFeaturesHash) { + let i, start, end, nextStyle, groupStart, feature, featureUid, featureIndex, featureStart; + featureIndex = this.startIndices.length - 2; + end = start = this.startIndices[featureIndex + 1]; + for (i = this.styleIndices_.length - 1; i >= 0; --i) { + nextStyle = this.styles_[i]; + this.setFillStyle_(gl, /** @type {Array.} */ (nextStyle[0])); + this.setStrokeStyle_(gl, /** @type {Array.} */ (nextStyle[1]), + /** @type {number} */ (nextStyle[2])); + groupStart = this.styleIndices_[i]; -/** - * @private - * @param {WebGLRenderingContext} gl gl. - * @param {Array.} color Color. - */ -WebGLCircleReplay.prototype.setFillStyle_ = function(gl, color) { - gl.uniform4fv(this.defaultLocations_.u_fillColor, color); -}; + while (featureIndex >= 0 && + this.startIndices[featureIndex] >= groupStart) { + featureStart = this.startIndices[featureIndex]; + feature = this.startIndicesFeature[featureIndex]; + featureUid = getUid(feature).toString(); + if (skippedFeaturesHash[featureUid]) { + if (start !== end) { + this.drawElements(gl, context, start, end); + } + end = featureStart; + } + featureIndex--; + start = featureStart; + } + if (start !== end) { + this.drawElements(gl, context, start, end); + } + start = end = groupStart; + } + } -/** - * @private - * @param {WebGLRenderingContext} gl gl. - * @param {Array.} color Color. - * @param {number} lineWidth Line width. - */ -WebGLCircleReplay.prototype.setStrokeStyle_ = function(gl, color, lineWidth) { - gl.uniform4fv(this.defaultLocations_.u_strokeColor, color); - gl.uniform1f(this.defaultLocations_.u_lineWidth, lineWidth); -}; + /** + * @private + * @param {WebGLRenderingContext} gl gl. + * @param {Array.} color Color. + */ + setFillStyle_(gl, color) { + gl.uniform4fv(this.defaultLocations_.u_fillColor, color); + } + /** + * @private + * @param {WebGLRenderingContext} gl gl. + * @param {Array.} color Color. + * @param {number} lineWidth Line width. + */ + setStrokeStyle_(gl, color, lineWidth) { + gl.uniform4fv(this.defaultLocations_.u_strokeColor, color); + gl.uniform1f(this.defaultLocations_.u_lineWidth, lineWidth); + } -/** - * @inheritDoc - */ -WebGLCircleReplay.prototype.setFillStrokeStyle = function(fillStyle, strokeStyle) { - let strokeStyleColor, strokeStyleWidth; - if (strokeStyle) { - const strokeStyleLineDash = strokeStyle.getLineDash(); - this.state_.lineDash = strokeStyleLineDash ? - strokeStyleLineDash : DEFAULT_LINEDASH; - const strokeStyleLineDashOffset = strokeStyle.getLineDashOffset(); - this.state_.lineDashOffset = strokeStyleLineDashOffset ? - strokeStyleLineDashOffset : DEFAULT_LINEDASHOFFSET; - strokeStyleColor = strokeStyle.getColor(); - if (!(strokeStyleColor instanceof CanvasGradient) && - !(strokeStyleColor instanceof CanvasPattern)) { - strokeStyleColor = asArray(strokeStyleColor).map(function(c, i) { - return i != 3 ? c / 255 : c; - }) || DEFAULT_STROKESTYLE; + /** + * @inheritDoc + */ + setFillStrokeStyle(fillStyle, strokeStyle) { + let strokeStyleColor, strokeStyleWidth; + if (strokeStyle) { + const strokeStyleLineDash = strokeStyle.getLineDash(); + this.state_.lineDash = strokeStyleLineDash ? + strokeStyleLineDash : DEFAULT_LINEDASH; + const strokeStyleLineDashOffset = strokeStyle.getLineDashOffset(); + this.state_.lineDashOffset = strokeStyleLineDashOffset ? + strokeStyleLineDashOffset : DEFAULT_LINEDASHOFFSET; + strokeStyleColor = strokeStyle.getColor(); + if (!(strokeStyleColor instanceof CanvasGradient) && + !(strokeStyleColor instanceof CanvasPattern)) { + strokeStyleColor = asArray(strokeStyleColor).map(function(c, i) { + return i != 3 ? c / 255 : c; + }) || DEFAULT_STROKESTYLE; + } else { + strokeStyleColor = DEFAULT_STROKESTYLE; + } + strokeStyleWidth = strokeStyle.getWidth(); + strokeStyleWidth = strokeStyleWidth !== undefined ? + strokeStyleWidth : DEFAULT_LINEWIDTH; } else { - strokeStyleColor = DEFAULT_STROKESTYLE; + strokeStyleColor = [0, 0, 0, 0]; + strokeStyleWidth = 0; + } + let fillStyleColor = fillStyle ? fillStyle.getColor() : [0, 0, 0, 0]; + if (!(fillStyleColor instanceof CanvasGradient) && + !(fillStyleColor instanceof CanvasPattern)) { + fillStyleColor = asArray(fillStyleColor).map(function(c, i) { + return i != 3 ? c / 255 : c; + }) || DEFAULT_FILLSTYLE; + } else { + fillStyleColor = DEFAULT_FILLSTYLE; + } + if (!this.state_.strokeColor || !equals(this.state_.strokeColor, strokeStyleColor) || + !this.state_.fillColor || !equals(this.state_.fillColor, fillStyleColor) || + this.state_.lineWidth !== strokeStyleWidth) { + this.state_.changed = true; + this.state_.fillColor = fillStyleColor; + this.state_.strokeColor = strokeStyleColor; + this.state_.lineWidth = strokeStyleWidth; + this.styles_.push([fillStyleColor, strokeStyleColor, strokeStyleWidth]); } - strokeStyleWidth = strokeStyle.getWidth(); - strokeStyleWidth = strokeStyleWidth !== undefined ? - strokeStyleWidth : DEFAULT_LINEWIDTH; - } else { - strokeStyleColor = [0, 0, 0, 0]; - strokeStyleWidth = 0; } - let fillStyleColor = fillStyle ? fillStyle.getColor() : [0, 0, 0, 0]; - if (!(fillStyleColor instanceof CanvasGradient) && - !(fillStyleColor instanceof CanvasPattern)) { - fillStyleColor = asArray(fillStyleColor).map(function(c, i) { - return i != 3 ? c / 255 : c; - }) || DEFAULT_FILLSTYLE; - } else { - fillStyleColor = DEFAULT_FILLSTYLE; - } - if (!this.state_.strokeColor || !equals(this.state_.strokeColor, strokeStyleColor) || - !this.state_.fillColor || !equals(this.state_.fillColor, fillStyleColor) || - this.state_.lineWidth !== strokeStyleWidth) { - this.state_.changed = true; - this.state_.fillColor = fillStyleColor; - this.state_.strokeColor = strokeStyleColor; - this.state_.lineWidth = strokeStyleWidth; - this.styles_.push([fillStyleColor, strokeStyleColor, strokeStyleWidth]); - } -}; +} + +inherits(WebGLCircleReplay, WebGLReplay); + + export default WebGLCircleReplay; diff --git a/src/ol/render/webgl/ImageReplay.js b/src/ol/render/webgl/ImageReplay.js index 111454549c..03f6350fdf 100644 --- a/src/ol/render/webgl/ImageReplay.js +++ b/src/ol/render/webgl/ImageReplay.js @@ -12,160 +12,158 @@ import WebGLBuffer from '../../webgl/Buffer.js'; * @param {module:ol/extent~Extent} maxExtent Max extent. * @struct */ -const WebGLImageReplay = function(tolerance, maxExtent) { - WebGLTextureReplay.call(this, tolerance, maxExtent); +class WebGLImageReplay { + constructor(tolerance, maxExtent) { + WebGLTextureReplay.call(this, tolerance, maxExtent); + + /** + * @type {Array.} + * @protected + */ + this.images_ = []; + + /** + * @type {Array.} + * @protected + */ + this.hitDetectionImages_ = []; + + /** + * @type {Array.} + * @private + */ + this.textures_ = []; + + /** + * @type {Array.} + * @private + */ + this.hitDetectionTextures_ = []; + + } /** - * @type {Array.} - * @protected + * @inheritDoc */ - this.images_ = []; + drawMultiPoint(multiPointGeometry, feature) { + this.startIndices.push(this.indices.length); + this.startIndicesFeature.push(feature); + const flatCoordinates = multiPointGeometry.getFlatCoordinates(); + const stride = multiPointGeometry.getStride(); + this.drawCoordinates( + flatCoordinates, 0, flatCoordinates.length, stride); + } /** - * @type {Array.} - * @protected + * @inheritDoc */ - this.hitDetectionImages_ = []; + drawPoint(pointGeometry, feature) { + this.startIndices.push(this.indices.length); + this.startIndicesFeature.push(feature); + const flatCoordinates = pointGeometry.getFlatCoordinates(); + const stride = pointGeometry.getStride(); + this.drawCoordinates( + flatCoordinates, 0, flatCoordinates.length, stride); + } /** - * @type {Array.} - * @private + * @inheritDoc */ - this.textures_ = []; + finish(context) { + const gl = context.getGL(); + + this.groupIndices.push(this.indices.length); + this.hitDetectionGroupIndices.push(this.indices.length); + + // create, bind, and populate the vertices buffer + this.verticesBuffer = new WebGLBuffer(this.vertices); + + const indices = this.indices; + + // create, bind, and populate the indices buffer + this.indicesBuffer = new WebGLBuffer(indices); + + // create textures + /** @type {Object.} */ + const texturePerImage = {}; + + this.createTextures(this.textures_, this.images_, texturePerImage, gl); + + this.createTextures(this.hitDetectionTextures_, this.hitDetectionImages_, + texturePerImage, gl); + + this.images_ = null; + this.hitDetectionImages_ = null; + WebGLTextureReplay.prototype.finish.call(this, context); + } /** - * @type {Array.} - * @private + * @inheritDoc */ - this.hitDetectionTextures_ = []; + setImageStyle(imageStyle) { + const anchor = imageStyle.getAnchor(); + const image = imageStyle.getImage(1); + const imageSize = imageStyle.getImageSize(); + const hitDetectionImage = imageStyle.getHitDetectionImage(1); + const opacity = imageStyle.getOpacity(); + const origin = imageStyle.getOrigin(); + const rotateWithView = imageStyle.getRotateWithView(); + const rotation = imageStyle.getRotation(); + const size = imageStyle.getSize(); + const scale = imageStyle.getScale(); -}; + let currentImage; + if (this.images_.length === 0) { + this.images_.push(image); + } else { + currentImage = this.images_[this.images_.length - 1]; + if (getUid(currentImage) != getUid(image)) { + this.groupIndices.push(this.indices.length); + this.images_.push(image); + } + } + + if (this.hitDetectionImages_.length === 0) { + this.hitDetectionImages_.push(hitDetectionImage); + } else { + currentImage = + this.hitDetectionImages_[this.hitDetectionImages_.length - 1]; + if (getUid(currentImage) != getUid(hitDetectionImage)) { + this.hitDetectionGroupIndices.push(this.indices.length); + this.hitDetectionImages_.push(hitDetectionImage); + } + } + + this.anchorX = anchor[0]; + this.anchorY = anchor[1]; + this.height = size[1]; + this.imageHeight = imageSize[1]; + this.imageWidth = imageSize[0]; + this.opacity = opacity; + this.originX = origin[0]; + this.originY = origin[1]; + this.rotation = rotation; + this.rotateWithView = rotateWithView; + this.scale = scale; + this.width = size[0]; + } + + /** + * @inheritDoc + */ + getTextures(opt_all) { + return opt_all ? this.textures_.concat(this.hitDetectionTextures_) : this.textures_; + } + + /** + * @inheritDoc + */ + getHitDetectionTextures() { + return this.hitDetectionTextures_; + } +} inherits(WebGLImageReplay, WebGLTextureReplay); -/** - * @inheritDoc - */ -WebGLImageReplay.prototype.drawMultiPoint = function(multiPointGeometry, feature) { - this.startIndices.push(this.indices.length); - this.startIndicesFeature.push(feature); - const flatCoordinates = multiPointGeometry.getFlatCoordinates(); - const stride = multiPointGeometry.getStride(); - this.drawCoordinates( - flatCoordinates, 0, flatCoordinates.length, stride); -}; - - -/** - * @inheritDoc - */ -WebGLImageReplay.prototype.drawPoint = function(pointGeometry, feature) { - this.startIndices.push(this.indices.length); - this.startIndicesFeature.push(feature); - const flatCoordinates = pointGeometry.getFlatCoordinates(); - const stride = pointGeometry.getStride(); - this.drawCoordinates( - flatCoordinates, 0, flatCoordinates.length, stride); -}; - - -/** - * @inheritDoc - */ -WebGLImageReplay.prototype.finish = function(context) { - const gl = context.getGL(); - - this.groupIndices.push(this.indices.length); - this.hitDetectionGroupIndices.push(this.indices.length); - - // create, bind, and populate the vertices buffer - this.verticesBuffer = new WebGLBuffer(this.vertices); - - const indices = this.indices; - - // create, bind, and populate the indices buffer - this.indicesBuffer = new WebGLBuffer(indices); - - // create textures - /** @type {Object.} */ - const texturePerImage = {}; - - this.createTextures(this.textures_, this.images_, texturePerImage, gl); - - this.createTextures(this.hitDetectionTextures_, this.hitDetectionImages_, - texturePerImage, gl); - - this.images_ = null; - this.hitDetectionImages_ = null; - WebGLTextureReplay.prototype.finish.call(this, context); -}; - - -/** - * @inheritDoc - */ -WebGLImageReplay.prototype.setImageStyle = function(imageStyle) { - const anchor = imageStyle.getAnchor(); - const image = imageStyle.getImage(1); - const imageSize = imageStyle.getImageSize(); - const hitDetectionImage = imageStyle.getHitDetectionImage(1); - const opacity = imageStyle.getOpacity(); - const origin = imageStyle.getOrigin(); - const rotateWithView = imageStyle.getRotateWithView(); - const rotation = imageStyle.getRotation(); - const size = imageStyle.getSize(); - const scale = imageStyle.getScale(); - - let currentImage; - if (this.images_.length === 0) { - this.images_.push(image); - } else { - currentImage = this.images_[this.images_.length - 1]; - if (getUid(currentImage) != getUid(image)) { - this.groupIndices.push(this.indices.length); - this.images_.push(image); - } - } - - if (this.hitDetectionImages_.length === 0) { - this.hitDetectionImages_.push(hitDetectionImage); - } else { - currentImage = - this.hitDetectionImages_[this.hitDetectionImages_.length - 1]; - if (getUid(currentImage) != getUid(hitDetectionImage)) { - this.hitDetectionGroupIndices.push(this.indices.length); - this.hitDetectionImages_.push(hitDetectionImage); - } - } - - this.anchorX = anchor[0]; - this.anchorY = anchor[1]; - this.height = size[1]; - this.imageHeight = imageSize[1]; - this.imageWidth = imageSize[0]; - this.opacity = opacity; - this.originX = origin[0]; - this.originY = origin[1]; - this.rotation = rotation; - this.rotateWithView = rotateWithView; - this.scale = scale; - this.width = size[0]; -}; - - -/** - * @inheritDoc - */ -WebGLImageReplay.prototype.getTextures = function(opt_all) { - return opt_all ? this.textures_.concat(this.hitDetectionTextures_) : this.textures_; -}; - - -/** - * @inheritDoc - */ -WebGLImageReplay.prototype.getHitDetectionTextures = function() { - return this.hitDetectionTextures_; -}; export default WebGLImageReplay; diff --git a/src/ol/render/webgl/Immediate.js b/src/ol/render/webgl/Immediate.js index c50614b697..ef281f2510 100644 --- a/src/ol/render/webgl/Immediate.js +++ b/src/ol/render/webgl/Immediate.js @@ -20,383 +20,372 @@ import WebGLReplayGroup from '../webgl/ReplayGroup.js'; * @param {number} pixelRatio Pixel ratio. * @struct */ -const WebGLImmediateRenderer = function(context, center, resolution, rotation, size, extent, pixelRatio) { - VectorContext.call(this); +class WebGLImmediateRenderer { + constructor(context, center, resolution, rotation, size, extent, pixelRatio) { + VectorContext.call(this); + + /** + * @private + */ + this.context_ = context; + + /** + * @private + */ + this.center_ = center; + + /** + * @private + */ + this.extent_ = extent; + + /** + * @private + */ + this.pixelRatio_ = pixelRatio; + + /** + * @private + */ + this.size_ = size; + + /** + * @private + */ + this.rotation_ = rotation; + + /** + * @private + */ + this.resolution_ = resolution; + + /** + * @private + * @type {module:ol/style/Image} + */ + this.imageStyle_ = null; + + /** + * @private + * @type {module:ol/style/Fill} + */ + this.fillStyle_ = null; + + /** + * @private + * @type {module:ol/style/Stroke} + */ + this.strokeStyle_ = null; + + /** + * @private + * @type {module:ol/style/Text} + */ + this.textStyle_ = null; + + } /** + * @param {module:ol/render/webgl/ReplayGroup} replayGroup Replay group. + * @param {module:ol/geom/Geometry|module:ol/render/Feature} geometry Geometry. * @private */ - this.context_ = context; + drawText_(replayGroup, geometry) { + const context = this.context_; + const replay = /** @type {module:ol/render/webgl/TextReplay} */ ( + replayGroup.getReplay(0, ReplayType.TEXT)); + replay.setTextStyle(this.textStyle_); + replay.drawText(geometry, null); + replay.finish(context); + // default colors + const opacity = 1; + const skippedFeatures = {}; + let featureCallback; + const oneByOne = false; + replay.replay(this.context_, this.center_, this.resolution_, this.rotation_, + this.size_, this.pixelRatio_, opacity, skippedFeatures, featureCallback, + oneByOne); + replay.getDeleteResourcesFunction(context)(); + } /** - * @private + * Set the rendering style. Note that since this is an immediate rendering API, + * any `zIndex` on the provided style will be ignored. + * + * @param {module:ol/style/Style} style The rendering style. + * @override + * @api */ - this.center_ = center; + setStyle(style) { + this.setFillStrokeStyle(style.getFill(), style.getStroke()); + this.setImageStyle(style.getImage()); + this.setTextStyle(style.getText()); + } /** - * @private + * Render a geometry into the canvas. Call + * {@link ol/render/webgl/Immediate#setStyle} first to set the rendering style. + * + * @param {module:ol/geom/Geometry|module:ol/render/Feature} geometry The geometry to render. + * @override + * @api */ - this.extent_ = extent; + drawGeometry(geometry) { + const type = geometry.getType(); + switch (type) { + case GeometryType.POINT: + this.drawPoint(/** @type {module:ol/geom/Point} */ (geometry), null); + break; + case GeometryType.LINE_STRING: + this.drawLineString(/** @type {module:ol/geom/LineString} */ (geometry), null); + break; + case GeometryType.POLYGON: + this.drawPolygon(/** @type {module:ol/geom/Polygon} */ (geometry), null); + break; + case GeometryType.MULTI_POINT: + this.drawMultiPoint(/** @type {module:ol/geom/MultiPoint} */ (geometry), null); + break; + case GeometryType.MULTI_LINE_STRING: + this.drawMultiLineString(/** @type {module:ol/geom/MultiLineString} */ (geometry), null); + break; + case GeometryType.MULTI_POLYGON: + this.drawMultiPolygon(/** @type {module:ol/geom/MultiPolygon} */ (geometry), null); + break; + case GeometryType.GEOMETRY_COLLECTION: + this.drawGeometryCollection(/** @type {module:ol/geom/GeometryCollection} */ (geometry), null); + break; + case GeometryType.CIRCLE: + this.drawCircle(/** @type {module:ol/geom/Circle} */ (geometry), null); + break; + default: + // pass + } + } /** - * @private + * @inheritDoc + * @api */ - this.pixelRatio_ = pixelRatio; + drawFeature(feature, style) { + const geometry = style.getGeometryFunction()(feature); + if (!geometry || !intersects(this.extent_, geometry.getExtent())) { + return; + } + this.setStyle(style); + this.drawGeometry(geometry); + } /** - * @private + * @inheritDoc */ - this.size_ = size; + drawGeometryCollection(geometry, data) { + const geometries = geometry.getGeometriesArray(); + let i, ii; + for (i = 0, ii = geometries.length; i < ii; ++i) { + this.drawGeometry(geometries[i]); + } + } /** - * @private + * @inheritDoc */ - this.rotation_ = rotation; + drawPoint(geometry, data) { + const context = this.context_; + const replayGroup = new WebGLReplayGroup(1, this.extent_); + const replay = /** @type {module:ol/render/webgl/ImageReplay} */ ( + replayGroup.getReplay(0, ReplayType.IMAGE)); + replay.setImageStyle(this.imageStyle_); + replay.drawPoint(geometry, data); + replay.finish(context); + // default colors + const opacity = 1; + const skippedFeatures = {}; + let featureCallback; + const oneByOne = false; + replay.replay(this.context_, this.center_, this.resolution_, this.rotation_, + this.size_, this.pixelRatio_, opacity, skippedFeatures, featureCallback, + oneByOne); + replay.getDeleteResourcesFunction(context)(); + + if (this.textStyle_) { + this.drawText_(replayGroup, geometry); + } + } /** - * @private + * @inheritDoc */ - this.resolution_ = resolution; + drawMultiPoint(geometry, data) { + const context = this.context_; + const replayGroup = new WebGLReplayGroup(1, this.extent_); + const replay = /** @type {module:ol/render/webgl/ImageReplay} */ ( + replayGroup.getReplay(0, ReplayType.IMAGE)); + replay.setImageStyle(this.imageStyle_); + replay.drawMultiPoint(geometry, data); + replay.finish(context); + const opacity = 1; + const skippedFeatures = {}; + let featureCallback; + const oneByOne = false; + replay.replay(this.context_, this.center_, this.resolution_, this.rotation_, + this.size_, this.pixelRatio_, opacity, skippedFeatures, featureCallback, + oneByOne); + replay.getDeleteResourcesFunction(context)(); + + if (this.textStyle_) { + this.drawText_(replayGroup, geometry); + } + } /** - * @private - * @type {module:ol/style/Image} + * @inheritDoc */ - this.imageStyle_ = null; + drawLineString(geometry, data) { + const context = this.context_; + const replayGroup = new WebGLReplayGroup(1, this.extent_); + const replay = /** @type {module:ol/render/webgl/LineStringReplay} */ ( + replayGroup.getReplay(0, ReplayType.LINE_STRING)); + replay.setFillStrokeStyle(null, this.strokeStyle_); + replay.drawLineString(geometry, data); + replay.finish(context); + const opacity = 1; + const skippedFeatures = {}; + let featureCallback; + const oneByOne = false; + replay.replay(this.context_, this.center_, this.resolution_, this.rotation_, + this.size_, this.pixelRatio_, opacity, skippedFeatures, featureCallback, + oneByOne); + replay.getDeleteResourcesFunction(context)(); + + if (this.textStyle_) { + this.drawText_(replayGroup, geometry); + } + } /** - * @private - * @type {module:ol/style/Fill} + * @inheritDoc */ - this.fillStyle_ = null; + drawMultiLineString(geometry, data) { + const context = this.context_; + const replayGroup = new WebGLReplayGroup(1, this.extent_); + const replay = /** @type {module:ol/render/webgl/LineStringReplay} */ ( + replayGroup.getReplay(0, ReplayType.LINE_STRING)); + replay.setFillStrokeStyle(null, this.strokeStyle_); + replay.drawMultiLineString(geometry, data); + replay.finish(context); + const opacity = 1; + const skippedFeatures = {}; + let featureCallback; + const oneByOne = false; + replay.replay(this.context_, this.center_, this.resolution_, this.rotation_, + this.size_, this.pixelRatio_, opacity, skippedFeatures, featureCallback, + oneByOne); + replay.getDeleteResourcesFunction(context)(); + + if (this.textStyle_) { + this.drawText_(replayGroup, geometry); + } + } /** - * @private - * @type {module:ol/style/Stroke} + * @inheritDoc */ - this.strokeStyle_ = null; + drawPolygon(geometry, data) { + const context = this.context_; + const replayGroup = new WebGLReplayGroup(1, this.extent_); + const replay = /** @type {module:ol/render/webgl/PolygonReplay} */ ( + replayGroup.getReplay(0, ReplayType.POLYGON)); + replay.setFillStrokeStyle(this.fillStyle_, this.strokeStyle_); + replay.drawPolygon(geometry, data); + replay.finish(context); + const opacity = 1; + const skippedFeatures = {}; + let featureCallback; + const oneByOne = false; + replay.replay(this.context_, this.center_, this.resolution_, this.rotation_, + this.size_, this.pixelRatio_, opacity, skippedFeatures, featureCallback, + oneByOne); + replay.getDeleteResourcesFunction(context)(); + + if (this.textStyle_) { + this.drawText_(replayGroup, geometry); + } + } /** - * @private - * @type {module:ol/style/Text} + * @inheritDoc */ - this.textStyle_ = null; + drawMultiPolygon(geometry, data) { + const context = this.context_; + const replayGroup = new WebGLReplayGroup(1, this.extent_); + const replay = /** @type {module:ol/render/webgl/PolygonReplay} */ ( + replayGroup.getReplay(0, ReplayType.POLYGON)); + replay.setFillStrokeStyle(this.fillStyle_, this.strokeStyle_); + replay.drawMultiPolygon(geometry, data); + replay.finish(context); + const opacity = 1; + const skippedFeatures = {}; + let featureCallback; + const oneByOne = false; + replay.replay(this.context_, this.center_, this.resolution_, this.rotation_, + this.size_, this.pixelRatio_, opacity, skippedFeatures, featureCallback, + oneByOne); + replay.getDeleteResourcesFunction(context)(); -}; + if (this.textStyle_) { + this.drawText_(replayGroup, geometry); + } + } + + /** + * @inheritDoc + */ + drawCircle(geometry, data) { + const context = this.context_; + const replayGroup = new WebGLReplayGroup(1, this.extent_); + const replay = /** @type {module:ol/render/webgl/CircleReplay} */ ( + replayGroup.getReplay(0, ReplayType.CIRCLE)); + replay.setFillStrokeStyle(this.fillStyle_, this.strokeStyle_); + replay.drawCircle(geometry, data); + replay.finish(context); + const opacity = 1; + const skippedFeatures = {}; + let featureCallback; + const oneByOne = false; + replay.replay(this.context_, this.center_, this.resolution_, this.rotation_, + this.size_, this.pixelRatio_, opacity, skippedFeatures, featureCallback, + oneByOne); + replay.getDeleteResourcesFunction(context)(); + + if (this.textStyle_) { + this.drawText_(replayGroup, geometry); + } + } + + /** + * @inheritDoc + */ + setImageStyle(imageStyle) { + this.imageStyle_ = imageStyle; + } + + /** + * @inheritDoc + */ + setFillStrokeStyle(fillStyle, strokeStyle) { + this.fillStyle_ = fillStyle; + this.strokeStyle_ = strokeStyle; + } + + /** + * @inheritDoc + */ + setTextStyle(textStyle) { + this.textStyle_ = textStyle; + } +} inherits(WebGLImmediateRenderer, VectorContext); -/** - * @param {module:ol/render/webgl/ReplayGroup} replayGroup Replay group. - * @param {module:ol/geom/Geometry|module:ol/render/Feature} geometry Geometry. - * @private - */ -WebGLImmediateRenderer.prototype.drawText_ = function(replayGroup, geometry) { - const context = this.context_; - const replay = /** @type {module:ol/render/webgl/TextReplay} */ ( - replayGroup.getReplay(0, ReplayType.TEXT)); - replay.setTextStyle(this.textStyle_); - replay.drawText(geometry, null); - replay.finish(context); - // default colors - const opacity = 1; - const skippedFeatures = {}; - let featureCallback; - const oneByOne = false; - replay.replay(this.context_, this.center_, this.resolution_, this.rotation_, - this.size_, this.pixelRatio_, opacity, skippedFeatures, featureCallback, - oneByOne); - replay.getDeleteResourcesFunction(context)(); -}; - - -/** - * Set the rendering style. Note that since this is an immediate rendering API, - * any `zIndex` on the provided style will be ignored. - * - * @param {module:ol/style/Style} style The rendering style. - * @override - * @api - */ -WebGLImmediateRenderer.prototype.setStyle = function(style) { - this.setFillStrokeStyle(style.getFill(), style.getStroke()); - this.setImageStyle(style.getImage()); - this.setTextStyle(style.getText()); -}; - - -/** - * Render a geometry into the canvas. Call - * {@link ol/render/webgl/Immediate#setStyle} first to set the rendering style. - * - * @param {module:ol/geom/Geometry|module:ol/render/Feature} geometry The geometry to render. - * @override - * @api - */ -WebGLImmediateRenderer.prototype.drawGeometry = function(geometry) { - const type = geometry.getType(); - switch (type) { - case GeometryType.POINT: - this.drawPoint(/** @type {module:ol/geom/Point} */ (geometry), null); - break; - case GeometryType.LINE_STRING: - this.drawLineString(/** @type {module:ol/geom/LineString} */ (geometry), null); - break; - case GeometryType.POLYGON: - this.drawPolygon(/** @type {module:ol/geom/Polygon} */ (geometry), null); - break; - case GeometryType.MULTI_POINT: - this.drawMultiPoint(/** @type {module:ol/geom/MultiPoint} */ (geometry), null); - break; - case GeometryType.MULTI_LINE_STRING: - this.drawMultiLineString(/** @type {module:ol/geom/MultiLineString} */ (geometry), null); - break; - case GeometryType.MULTI_POLYGON: - this.drawMultiPolygon(/** @type {module:ol/geom/MultiPolygon} */ (geometry), null); - break; - case GeometryType.GEOMETRY_COLLECTION: - this.drawGeometryCollection(/** @type {module:ol/geom/GeometryCollection} */ (geometry), null); - break; - case GeometryType.CIRCLE: - this.drawCircle(/** @type {module:ol/geom/Circle} */ (geometry), null); - break; - default: - // pass - } -}; - - -/** - * @inheritDoc - * @api - */ -WebGLImmediateRenderer.prototype.drawFeature = function(feature, style) { - const geometry = style.getGeometryFunction()(feature); - if (!geometry || !intersects(this.extent_, geometry.getExtent())) { - return; - } - this.setStyle(style); - this.drawGeometry(geometry); -}; - - -/** - * @inheritDoc - */ -WebGLImmediateRenderer.prototype.drawGeometryCollection = function(geometry, data) { - const geometries = geometry.getGeometriesArray(); - let i, ii; - for (i = 0, ii = geometries.length; i < ii; ++i) { - this.drawGeometry(geometries[i]); - } -}; - - -/** - * @inheritDoc - */ -WebGLImmediateRenderer.prototype.drawPoint = function(geometry, data) { - const context = this.context_; - const replayGroup = new WebGLReplayGroup(1, this.extent_); - const replay = /** @type {module:ol/render/webgl/ImageReplay} */ ( - replayGroup.getReplay(0, ReplayType.IMAGE)); - replay.setImageStyle(this.imageStyle_); - replay.drawPoint(geometry, data); - replay.finish(context); - // default colors - const opacity = 1; - const skippedFeatures = {}; - let featureCallback; - const oneByOne = false; - replay.replay(this.context_, this.center_, this.resolution_, this.rotation_, - this.size_, this.pixelRatio_, opacity, skippedFeatures, featureCallback, - oneByOne); - replay.getDeleteResourcesFunction(context)(); - - if (this.textStyle_) { - this.drawText_(replayGroup, geometry); - } -}; - - -/** - * @inheritDoc - */ -WebGLImmediateRenderer.prototype.drawMultiPoint = function(geometry, data) { - const context = this.context_; - const replayGroup = new WebGLReplayGroup(1, this.extent_); - const replay = /** @type {module:ol/render/webgl/ImageReplay} */ ( - replayGroup.getReplay(0, ReplayType.IMAGE)); - replay.setImageStyle(this.imageStyle_); - replay.drawMultiPoint(geometry, data); - replay.finish(context); - const opacity = 1; - const skippedFeatures = {}; - let featureCallback; - const oneByOne = false; - replay.replay(this.context_, this.center_, this.resolution_, this.rotation_, - this.size_, this.pixelRatio_, opacity, skippedFeatures, featureCallback, - oneByOne); - replay.getDeleteResourcesFunction(context)(); - - if (this.textStyle_) { - this.drawText_(replayGroup, geometry); - } -}; - - -/** - * @inheritDoc - */ -WebGLImmediateRenderer.prototype.drawLineString = function(geometry, data) { - const context = this.context_; - const replayGroup = new WebGLReplayGroup(1, this.extent_); - const replay = /** @type {module:ol/render/webgl/LineStringReplay} */ ( - replayGroup.getReplay(0, ReplayType.LINE_STRING)); - replay.setFillStrokeStyle(null, this.strokeStyle_); - replay.drawLineString(geometry, data); - replay.finish(context); - const opacity = 1; - const skippedFeatures = {}; - let featureCallback; - const oneByOne = false; - replay.replay(this.context_, this.center_, this.resolution_, this.rotation_, - this.size_, this.pixelRatio_, opacity, skippedFeatures, featureCallback, - oneByOne); - replay.getDeleteResourcesFunction(context)(); - - if (this.textStyle_) { - this.drawText_(replayGroup, geometry); - } -}; - - -/** - * @inheritDoc - */ -WebGLImmediateRenderer.prototype.drawMultiLineString = function(geometry, data) { - const context = this.context_; - const replayGroup = new WebGLReplayGroup(1, this.extent_); - const replay = /** @type {module:ol/render/webgl/LineStringReplay} */ ( - replayGroup.getReplay(0, ReplayType.LINE_STRING)); - replay.setFillStrokeStyle(null, this.strokeStyle_); - replay.drawMultiLineString(geometry, data); - replay.finish(context); - const opacity = 1; - const skippedFeatures = {}; - let featureCallback; - const oneByOne = false; - replay.replay(this.context_, this.center_, this.resolution_, this.rotation_, - this.size_, this.pixelRatio_, opacity, skippedFeatures, featureCallback, - oneByOne); - replay.getDeleteResourcesFunction(context)(); - - if (this.textStyle_) { - this.drawText_(replayGroup, geometry); - } -}; - - -/** - * @inheritDoc - */ -WebGLImmediateRenderer.prototype.drawPolygon = function(geometry, data) { - const context = this.context_; - const replayGroup = new WebGLReplayGroup(1, this.extent_); - const replay = /** @type {module:ol/render/webgl/PolygonReplay} */ ( - replayGroup.getReplay(0, ReplayType.POLYGON)); - replay.setFillStrokeStyle(this.fillStyle_, this.strokeStyle_); - replay.drawPolygon(geometry, data); - replay.finish(context); - const opacity = 1; - const skippedFeatures = {}; - let featureCallback; - const oneByOne = false; - replay.replay(this.context_, this.center_, this.resolution_, this.rotation_, - this.size_, this.pixelRatio_, opacity, skippedFeatures, featureCallback, - oneByOne); - replay.getDeleteResourcesFunction(context)(); - - if (this.textStyle_) { - this.drawText_(replayGroup, geometry); - } -}; - - -/** - * @inheritDoc - */ -WebGLImmediateRenderer.prototype.drawMultiPolygon = function(geometry, data) { - const context = this.context_; - const replayGroup = new WebGLReplayGroup(1, this.extent_); - const replay = /** @type {module:ol/render/webgl/PolygonReplay} */ ( - replayGroup.getReplay(0, ReplayType.POLYGON)); - replay.setFillStrokeStyle(this.fillStyle_, this.strokeStyle_); - replay.drawMultiPolygon(geometry, data); - replay.finish(context); - const opacity = 1; - const skippedFeatures = {}; - let featureCallback; - const oneByOne = false; - replay.replay(this.context_, this.center_, this.resolution_, this.rotation_, - this.size_, this.pixelRatio_, opacity, skippedFeatures, featureCallback, - oneByOne); - replay.getDeleteResourcesFunction(context)(); - - if (this.textStyle_) { - this.drawText_(replayGroup, geometry); - } -}; - - -/** - * @inheritDoc - */ -WebGLImmediateRenderer.prototype.drawCircle = function(geometry, data) { - const context = this.context_; - const replayGroup = new WebGLReplayGroup(1, this.extent_); - const replay = /** @type {module:ol/render/webgl/CircleReplay} */ ( - replayGroup.getReplay(0, ReplayType.CIRCLE)); - replay.setFillStrokeStyle(this.fillStyle_, this.strokeStyle_); - replay.drawCircle(geometry, data); - replay.finish(context); - const opacity = 1; - const skippedFeatures = {}; - let featureCallback; - const oneByOne = false; - replay.replay(this.context_, this.center_, this.resolution_, this.rotation_, - this.size_, this.pixelRatio_, opacity, skippedFeatures, featureCallback, - oneByOne); - replay.getDeleteResourcesFunction(context)(); - - if (this.textStyle_) { - this.drawText_(replayGroup, geometry); - } -}; - - -/** - * @inheritDoc - */ -WebGLImmediateRenderer.prototype.setImageStyle = function(imageStyle) { - this.imageStyle_ = imageStyle; -}; - - -/** - * @inheritDoc - */ -WebGLImmediateRenderer.prototype.setFillStrokeStyle = function(fillStyle, strokeStyle) { - this.fillStyle_ = fillStyle; - this.strokeStyle_ = strokeStyle; -}; - - -/** - * @inheritDoc - */ -WebGLImmediateRenderer.prototype.setTextStyle = function(textStyle) { - this.textStyle_ = textStyle; -}; export default WebGLImmediateRenderer; diff --git a/src/ol/render/webgl/LineStringReplay.js b/src/ol/render/webgl/LineStringReplay.js index c5932a5baf..db18c94472 100644 --- a/src/ol/render/webgl/LineStringReplay.js +++ b/src/ol/render/webgl/LineStringReplay.js @@ -42,645 +42,631 @@ const Instruction = { * @param {module:ol/extent~Extent} maxExtent Max extent. * @struct */ -const WebGLLineStringReplay = function(tolerance, maxExtent) { - WebGLReplay.call(this, tolerance, maxExtent); +class WebGLLineStringReplay { + constructor(tolerance, maxExtent) { + WebGLReplay.call(this, tolerance, maxExtent); + + /** + * @private + * @type {module:ol/render/webgl/linestringreplay/defaultshader/Locations} + */ + this.defaultLocations_ = null; + + /** + * @private + * @type {Array.>} + */ + this.styles_ = []; + + /** + * @private + * @type {Array.} + */ + this.styleIndices_ = []; + + /** + * @private + * @type {{strokeColor: (Array.|null), + * lineCap: (string|undefined), + * lineDash: Array., + * lineDashOffset: (number|undefined), + * lineJoin: (string|undefined), + * lineWidth: (number|undefined), + * miterLimit: (number|undefined), + * changed: boolean}|null} + */ + this.state_ = { + strokeColor: null, + lineCap: undefined, + lineDash: null, + lineDashOffset: undefined, + lineJoin: undefined, + lineWidth: undefined, + miterLimit: undefined, + changed: false + }; + + } /** + * Draw one segment. * @private - * @type {module:ol/render/webgl/linestringreplay/defaultshader/Locations} + * @param {Array.} flatCoordinates Flat coordinates. + * @param {number} offset Offset. + * @param {number} end End. + * @param {number} stride Stride. */ - this.defaultLocations_ = null; + drawCoordinates_(flatCoordinates, offset, end, stride) { - /** - * @private - * @type {Array.>} - */ - this.styles_ = []; + let i, ii; + let numVertices = this.vertices.length; + let numIndices = this.indices.length; + //To save a vertex, the direction of a point is a product of the sign (1 or -1), a prime from + //Instruction, and a rounding factor (1 or 2). If the product is even, + //we round it. If it is odd, we don't. + const lineJoin = this.state_.lineJoin === 'bevel' ? 0 : + this.state_.lineJoin === 'miter' ? 1 : 2; + const lineCap = this.state_.lineCap === 'butt' ? 0 : + this.state_.lineCap === 'square' ? 1 : 2; + const closed = lineStringIsClosed(flatCoordinates, offset, end, stride); + let startCoords, sign, n; + let lastIndex = numIndices; + let lastSign = 1; + //We need the adjacent vertices to define normals in joins. p0 = last, p1 = current, p2 = next. + let p0, p1, p2; - /** - * @private - * @type {Array.} - */ - this.styleIndices_ = []; + for (i = offset, ii = end; i < ii; i += stride) { - /** - * @private - * @type {{strokeColor: (Array.|null), - * lineCap: (string|undefined), - * lineDash: Array., - * lineDashOffset: (number|undefined), - * lineJoin: (string|undefined), - * lineWidth: (number|undefined), - * miterLimit: (number|undefined), - * changed: boolean}|null} - */ - this.state_ = { - strokeColor: null, - lineCap: undefined, - lineDash: null, - lineDashOffset: undefined, - lineJoin: undefined, - lineWidth: undefined, - miterLimit: undefined, - changed: false - }; - -}; - -inherits(WebGLLineStringReplay, WebGLReplay); - - -/** - * Draw one segment. - * @private - * @param {Array.} flatCoordinates Flat coordinates. - * @param {number} offset Offset. - * @param {number} end End. - * @param {number} stride Stride. - */ -WebGLLineStringReplay.prototype.drawCoordinates_ = function(flatCoordinates, offset, end, stride) { - - let i, ii; - let numVertices = this.vertices.length; - let numIndices = this.indices.length; - //To save a vertex, the direction of a point is a product of the sign (1 or -1), a prime from - //Instruction, and a rounding factor (1 or 2). If the product is even, - //we round it. If it is odd, we don't. - const lineJoin = this.state_.lineJoin === 'bevel' ? 0 : - this.state_.lineJoin === 'miter' ? 1 : 2; - const lineCap = this.state_.lineCap === 'butt' ? 0 : - this.state_.lineCap === 'square' ? 1 : 2; - const closed = lineStringIsClosed(flatCoordinates, offset, end, stride); - let startCoords, sign, n; - let lastIndex = numIndices; - let lastSign = 1; - //We need the adjacent vertices to define normals in joins. p0 = last, p1 = current, p2 = next. - let p0, p1, p2; - - for (i = offset, ii = end; i < ii; i += stride) { - - n = numVertices / 7; - - p0 = p1; - p1 = p2 || [flatCoordinates[i], flatCoordinates[i + 1]]; - //First vertex. - if (i === offset) { - p2 = [flatCoordinates[i + stride], flatCoordinates[i + stride + 1]]; - if (end - offset === stride * 2 && equals(p1, p2)) { - break; - } - if (closed) { - //A closed line! Complete the circle. - p0 = [flatCoordinates[end - stride * 2], - flatCoordinates[end - stride * 2 + 1]]; - - startCoords = p2; - } else { - //Add the first two/four vertices. - - if (lineCap) { - numVertices = this.addVertices_([0, 0], p1, p2, - lastSign * Instruction.BEGIN_LINE_CAP * lineCap, numVertices); - - numVertices = this.addVertices_([0, 0], p1, p2, - -lastSign * Instruction.BEGIN_LINE_CAP * lineCap, numVertices); - - this.indices[numIndices++] = n + 2; - this.indices[numIndices++] = n; - this.indices[numIndices++] = n + 1; - - this.indices[numIndices++] = n + 1; - this.indices[numIndices++] = n + 3; - this.indices[numIndices++] = n + 2; + n = numVertices / 7; + p0 = p1; + p1 = p2 || [flatCoordinates[i], flatCoordinates[i + 1]]; + //First vertex. + if (i === offset) { + p2 = [flatCoordinates[i + stride], flatCoordinates[i + stride + 1]]; + if (end - offset === stride * 2 && equals(p1, p2)) { + break; } + if (closed) { + //A closed line! Complete the circle. + p0 = [flatCoordinates[end - stride * 2], + flatCoordinates[end - stride * 2 + 1]]; - numVertices = this.addVertices_([0, 0], p1, p2, - lastSign * Instruction.BEGIN_LINE * (lineCap || 1), numVertices); + startCoords = p2; + } else { + //Add the first two/four vertices. - numVertices = this.addVertices_([0, 0], p1, p2, - -lastSign * Instruction.BEGIN_LINE * (lineCap || 1), numVertices); + if (lineCap) { + numVertices = this.addVertices_([0, 0], p1, p2, + lastSign * Instruction.BEGIN_LINE_CAP * lineCap, numVertices); - lastIndex = numVertices / 7 - 1; + numVertices = this.addVertices_([0, 0], p1, p2, + -lastSign * Instruction.BEGIN_LINE_CAP * lineCap, numVertices); - continue; - } - } else if (i === end - stride) { - //Last vertex. - if (closed) { - //Same as the first vertex. - p2 = startCoords; - break; + this.indices[numIndices++] = n + 2; + this.indices[numIndices++] = n; + this.indices[numIndices++] = n + 1; + + this.indices[numIndices++] = n + 1; + this.indices[numIndices++] = n + 3; + this.indices[numIndices++] = n + 2; + + } + + numVertices = this.addVertices_([0, 0], p1, p2, + lastSign * Instruction.BEGIN_LINE * (lineCap || 1), numVertices); + + numVertices = this.addVertices_([0, 0], p1, p2, + -lastSign * Instruction.BEGIN_LINE * (lineCap || 1), numVertices); + + lastIndex = numVertices / 7 - 1; + + continue; + } + } else if (i === end - stride) { + //Last vertex. + if (closed) { + //Same as the first vertex. + p2 = startCoords; + break; + } else { + p0 = p0 || [0, 0]; + + numVertices = this.addVertices_(p0, p1, [0, 0], + lastSign * Instruction.END_LINE * (lineCap || 1), numVertices); + + numVertices = this.addVertices_(p0, p1, [0, 0], + -lastSign * Instruction.END_LINE * (lineCap || 1), numVertices); + + this.indices[numIndices++] = n; + this.indices[numIndices++] = lastIndex - 1; + this.indices[numIndices++] = lastIndex; + + this.indices[numIndices++] = lastIndex; + this.indices[numIndices++] = n + 1; + this.indices[numIndices++] = n; + + if (lineCap) { + numVertices = this.addVertices_(p0, p1, [0, 0], + lastSign * Instruction.END_LINE_CAP * lineCap, numVertices); + + numVertices = this.addVertices_(p0, p1, [0, 0], + -lastSign * Instruction.END_LINE_CAP * lineCap, numVertices); + + this.indices[numIndices++] = n + 2; + this.indices[numIndices++] = n; + this.indices[numIndices++] = n + 1; + + this.indices[numIndices++] = n + 1; + this.indices[numIndices++] = n + 3; + this.indices[numIndices++] = n + 2; + + } + + break; + } } else { - p0 = p0 || [0, 0]; + p2 = [flatCoordinates[i + stride], flatCoordinates[i + stride + 1]]; + } - numVertices = this.addVertices_(p0, p1, [0, 0], - lastSign * Instruction.END_LINE * (lineCap || 1), numVertices); + // We group CW and straight lines, thus the not so inituitive CCW checking function. + sign = triangleIsCounterClockwise(p0[0], p0[1], p1[0], p1[1], p2[0], p2[1]) + ? -1 : 1; - numVertices = this.addVertices_(p0, p1, [0, 0], - -lastSign * Instruction.END_LINE * (lineCap || 1), numVertices); + numVertices = this.addVertices_(p0, p1, p2, + sign * Instruction.BEVEL_FIRST * (lineJoin || 1), numVertices); + numVertices = this.addVertices_(p0, p1, p2, + sign * Instruction.BEVEL_SECOND * (lineJoin || 1), numVertices); + + numVertices = this.addVertices_(p0, p1, p2, + -sign * Instruction.MITER_BOTTOM * (lineJoin || 1), numVertices); + + if (i > offset) { this.indices[numIndices++] = n; this.indices[numIndices++] = lastIndex - 1; this.indices[numIndices++] = lastIndex; - this.indices[numIndices++] = lastIndex; - this.indices[numIndices++] = n + 1; + this.indices[numIndices++] = n + 2; + this.indices[numIndices++] = n; + this.indices[numIndices++] = lastSign * sign > 0 ? lastIndex : lastIndex - 1; + } + + this.indices[numIndices++] = n; + this.indices[numIndices++] = n + 2; + this.indices[numIndices++] = n + 1; + + lastIndex = n + 2; + lastSign = sign; + + //Add miter + if (lineJoin) { + numVertices = this.addVertices_(p0, p1, p2, + sign * Instruction.MITER_TOP * lineJoin, numVertices); + + this.indices[numIndices++] = n + 1; + this.indices[numIndices++] = n + 3; this.indices[numIndices++] = n; - - if (lineCap) { - numVertices = this.addVertices_(p0, p1, [0, 0], - lastSign * Instruction.END_LINE_CAP * lineCap, numVertices); - - numVertices = this.addVertices_(p0, p1, [0, 0], - -lastSign * Instruction.END_LINE_CAP * lineCap, numVertices); - - this.indices[numIndices++] = n + 2; - this.indices[numIndices++] = n; - this.indices[numIndices++] = n + 1; - - this.indices[numIndices++] = n + 1; - this.indices[numIndices++] = n + 3; - this.indices[numIndices++] = n + 2; - - } - - break; } - } else { - p2 = [flatCoordinates[i + stride], flatCoordinates[i + stride + 1]]; } - // We group CW and straight lines, thus the not so inituitive CCW checking function. - sign = triangleIsCounterClockwise(p0[0], p0[1], p1[0], p1[1], p2[0], p2[1]) - ? -1 : 1; + if (closed) { + n = n || numVertices / 7; + sign = linearRingIsClockwise([p0[0], p0[1], p1[0], p1[1], p2[0], p2[1]], 0, 6, 2) + ? 1 : -1; - numVertices = this.addVertices_(p0, p1, p2, - sign * Instruction.BEVEL_FIRST * (lineJoin || 1), numVertices); + numVertices = this.addVertices_(p0, p1, p2, + sign * Instruction.BEVEL_FIRST * (lineJoin || 1), numVertices); - numVertices = this.addVertices_(p0, p1, p2, - sign * Instruction.BEVEL_SECOND * (lineJoin || 1), numVertices); + numVertices = this.addVertices_(p0, p1, p2, + -sign * Instruction.MITER_BOTTOM * (lineJoin || 1), numVertices); - numVertices = this.addVertices_(p0, p1, p2, - -sign * Instruction.MITER_BOTTOM * (lineJoin || 1), numVertices); - - if (i > offset) { this.indices[numIndices++] = n; this.indices[numIndices++] = lastIndex - 1; this.indices[numIndices++] = lastIndex; - this.indices[numIndices++] = n + 2; + this.indices[numIndices++] = n + 1; this.indices[numIndices++] = n; this.indices[numIndices++] = lastSign * sign > 0 ? lastIndex : lastIndex - 1; } + } - this.indices[numIndices++] = n; - this.indices[numIndices++] = n + 2; - this.indices[numIndices++] = n + 1; + /** + * @param {Array.} p0 Last coordinates. + * @param {Array.} p1 Current coordinates. + * @param {Array.} p2 Next coordinates. + * @param {number} product Sign, instruction, and rounding product. + * @param {number} numVertices Vertex counter. + * @return {number} Vertex counter. + * @private + */ + addVertices_(p0, p1, p2, product, numVertices) { + this.vertices[numVertices++] = p0[0]; + this.vertices[numVertices++] = p0[1]; + this.vertices[numVertices++] = p1[0]; + this.vertices[numVertices++] = p1[1]; + this.vertices[numVertices++] = p2[0]; + this.vertices[numVertices++] = p2[1]; + this.vertices[numVertices++] = product; - lastIndex = n + 2; - lastSign = sign; + return numVertices; + } - //Add miter - if (lineJoin) { - numVertices = this.addVertices_(p0, p1, p2, - sign * Instruction.MITER_TOP * lineJoin, numVertices); - - this.indices[numIndices++] = n + 1; - this.indices[numIndices++] = n + 3; - this.indices[numIndices++] = n; + /** + * Check if the linestring can be drawn (i. e. valid). + * @param {Array.} flatCoordinates Flat coordinates. + * @param {number} offset Offset. + * @param {number} end End. + * @param {number} stride Stride. + * @return {boolean} The linestring can be drawn. + * @private + */ + isValid_(flatCoordinates, offset, end, stride) { + const range = end - offset; + if (range < stride * 2) { + return false; + } else if (range === stride * 2) { + const firstP = [flatCoordinates[offset], flatCoordinates[offset + 1]]; + const lastP = [flatCoordinates[offset + stride], flatCoordinates[offset + stride + 1]]; + return !equals(firstP, lastP); } + + return true; } - if (closed) { - n = n || numVertices / 7; - sign = linearRingIsClockwise([p0[0], p0[1], p1[0], p1[1], p2[0], p2[1]], 0, 6, 2) - ? 1 : -1; - - numVertices = this.addVertices_(p0, p1, p2, - sign * Instruction.BEVEL_FIRST * (lineJoin || 1), numVertices); - - numVertices = this.addVertices_(p0, p1, p2, - -sign * Instruction.MITER_BOTTOM * (lineJoin || 1), numVertices); - - this.indices[numIndices++] = n; - this.indices[numIndices++] = lastIndex - 1; - this.indices[numIndices++] = lastIndex; - - this.indices[numIndices++] = n + 1; - this.indices[numIndices++] = n; - this.indices[numIndices++] = lastSign * sign > 0 ? lastIndex : lastIndex - 1; - } -}; - -/** - * @param {Array.} p0 Last coordinates. - * @param {Array.} p1 Current coordinates. - * @param {Array.} p2 Next coordinates. - * @param {number} product Sign, instruction, and rounding product. - * @param {number} numVertices Vertex counter. - * @return {number} Vertex counter. - * @private - */ -WebGLLineStringReplay.prototype.addVertices_ = function(p0, p1, p2, product, numVertices) { - this.vertices[numVertices++] = p0[0]; - this.vertices[numVertices++] = p0[1]; - this.vertices[numVertices++] = p1[0]; - this.vertices[numVertices++] = p1[1]; - this.vertices[numVertices++] = p2[0]; - this.vertices[numVertices++] = p2[1]; - this.vertices[numVertices++] = product; - - return numVertices; -}; - -/** - * Check if the linestring can be drawn (i. e. valid). - * @param {Array.} flatCoordinates Flat coordinates. - * @param {number} offset Offset. - * @param {number} end End. - * @param {number} stride Stride. - * @return {boolean} The linestring can be drawn. - * @private - */ -WebGLLineStringReplay.prototype.isValid_ = function(flatCoordinates, offset, end, stride) { - const range = end - offset; - if (range < stride * 2) { - return false; - } else if (range === stride * 2) { - const firstP = [flatCoordinates[offset], flatCoordinates[offset + 1]]; - const lastP = [flatCoordinates[offset + stride], flatCoordinates[offset + stride + 1]]; - return !equals(firstP, lastP); - } - - return true; -}; - - -/** - * @inheritDoc - */ -WebGLLineStringReplay.prototype.drawLineString = function(lineStringGeometry, feature) { - let flatCoordinates = lineStringGeometry.getFlatCoordinates(); - const stride = lineStringGeometry.getStride(); - if (this.isValid_(flatCoordinates, 0, flatCoordinates.length, stride)) { - flatCoordinates = translate(flatCoordinates, 0, flatCoordinates.length, - stride, -this.origin[0], -this.origin[1]); - if (this.state_.changed) { - this.styleIndices_.push(this.indices.length); - this.state_.changed = false; - } - this.startIndices.push(this.indices.length); - this.startIndicesFeature.push(feature); - this.drawCoordinates_( - flatCoordinates, 0, flatCoordinates.length, stride); - } -}; - - -/** - * @inheritDoc - */ -WebGLLineStringReplay.prototype.drawMultiLineString = function(multiLineStringGeometry, feature) { - const indexCount = this.indices.length; - const ends = multiLineStringGeometry.getEnds(); - ends.unshift(0); - const flatCoordinates = multiLineStringGeometry.getFlatCoordinates(); - const stride = multiLineStringGeometry.getStride(); - let i, ii; - if (ends.length > 1) { - for (i = 1, ii = ends.length; i < ii; ++i) { - if (this.isValid_(flatCoordinates, ends[i - 1], ends[i], stride)) { - const lineString = translate(flatCoordinates, ends[i - 1], ends[i], - stride, -this.origin[0], -this.origin[1]); - this.drawCoordinates_( - lineString, 0, lineString.length, stride); + /** + * @inheritDoc + */ + drawLineString(lineStringGeometry, feature) { + let flatCoordinates = lineStringGeometry.getFlatCoordinates(); + const stride = lineStringGeometry.getStride(); + if (this.isValid_(flatCoordinates, 0, flatCoordinates.length, stride)) { + flatCoordinates = translate(flatCoordinates, 0, flatCoordinates.length, + stride, -this.origin[0], -this.origin[1]); + if (this.state_.changed) { + this.styleIndices_.push(this.indices.length); + this.state_.changed = false; } + this.startIndices.push(this.indices.length); + this.startIndicesFeature.push(feature); + this.drawCoordinates_( + flatCoordinates, 0, flatCoordinates.length, stride); } } - if (this.indices.length > indexCount) { - this.startIndices.push(indexCount); - this.startIndicesFeature.push(feature); - if (this.state_.changed) { - this.styleIndices_.push(indexCount); - this.state_.changed = false; - } - } -}; - -/** - * @param {Array.} flatCoordinates Flat coordinates. - * @param {Array.>} holeFlatCoordinates Hole flat coordinates. - * @param {number} stride Stride. - */ -WebGLLineStringReplay.prototype.drawPolygonCoordinates = function( - flatCoordinates, holeFlatCoordinates, stride) { - if (!lineStringIsClosed(flatCoordinates, 0, flatCoordinates.length, stride)) { - flatCoordinates.push(flatCoordinates[0]); - flatCoordinates.push(flatCoordinates[1]); - } - this.drawCoordinates_(flatCoordinates, 0, flatCoordinates.length, stride); - if (holeFlatCoordinates.length) { + /** + * @inheritDoc + */ + drawMultiLineString(multiLineStringGeometry, feature) { + const indexCount = this.indices.length; + const ends = multiLineStringGeometry.getEnds(); + ends.unshift(0); + const flatCoordinates = multiLineStringGeometry.getFlatCoordinates(); + const stride = multiLineStringGeometry.getStride(); let i, ii; - for (i = 0, ii = holeFlatCoordinates.length; i < ii; ++i) { - if (!lineStringIsClosed(holeFlatCoordinates[i], 0, holeFlatCoordinates[i].length, stride)) { - holeFlatCoordinates[i].push(holeFlatCoordinates[i][0]); - holeFlatCoordinates[i].push(holeFlatCoordinates[i][1]); + if (ends.length > 1) { + for (i = 1, ii = ends.length; i < ii; ++i) { + if (this.isValid_(flatCoordinates, ends[i - 1], ends[i], stride)) { + const lineString = translate(flatCoordinates, ends[i - 1], ends[i], + stride, -this.origin[0], -this.origin[1]); + this.drawCoordinates_( + lineString, 0, lineString.length, stride); + } + } + } + if (this.indices.length > indexCount) { + this.startIndices.push(indexCount); + this.startIndicesFeature.push(feature); + if (this.state_.changed) { + this.styleIndices_.push(indexCount); + this.state_.changed = false; } - this.drawCoordinates_(holeFlatCoordinates[i], 0, - holeFlatCoordinates[i].length, stride); } } -}; - -/** - * @param {module:ol/Feature|module:ol/render/Feature} feature Feature. - * @param {number=} opt_index Index count. - */ -WebGLLineStringReplay.prototype.setPolygonStyle = function(feature, opt_index) { - const index = opt_index === undefined ? this.indices.length : opt_index; - this.startIndices.push(index); - this.startIndicesFeature.push(feature); - if (this.state_.changed) { - this.styleIndices_.push(index); - this.state_.changed = false; - } -}; - - -/** - * @return {number} Current index. - */ -WebGLLineStringReplay.prototype.getCurrentIndex = function() { - return this.indices.length; -}; - - -/** - * @inheritDoc - **/ -WebGLLineStringReplay.prototype.finish = function(context) { - // create, bind, and populate the vertices buffer - this.verticesBuffer = new WebGLBuffer(this.vertices); - - // create, bind, and populate the indices buffer - this.indicesBuffer = new WebGLBuffer(this.indices); - - this.startIndices.push(this.indices.length); - - //Clean up, if there is nothing to draw - if (this.styleIndices_.length === 0 && this.styles_.length > 0) { - this.styles_ = []; + /** + * @param {Array.} flatCoordinates Flat coordinates. + * @param {Array.>} holeFlatCoordinates Hole flat coordinates. + * @param {number} stride Stride. + */ + drawPolygonCoordinates(flatCoordinates, holeFlatCoordinates, stride) { + if (!lineStringIsClosed(flatCoordinates, 0, flatCoordinates.length, stride)) { + flatCoordinates.push(flatCoordinates[0]); + flatCoordinates.push(flatCoordinates[1]); + } + this.drawCoordinates_(flatCoordinates, 0, flatCoordinates.length, stride); + if (holeFlatCoordinates.length) { + let i, ii; + for (i = 0, ii = holeFlatCoordinates.length; i < ii; ++i) { + if (!lineStringIsClosed(holeFlatCoordinates[i], 0, holeFlatCoordinates[i].length, stride)) { + holeFlatCoordinates[i].push(holeFlatCoordinates[i][0]); + holeFlatCoordinates[i].push(holeFlatCoordinates[i][1]); + } + this.drawCoordinates_(holeFlatCoordinates[i], 0, + holeFlatCoordinates[i].length, stride); + } + } } - this.vertices = null; - this.indices = null; -}; - - -/** - * @inheritDoc - */ -WebGLLineStringReplay.prototype.getDeleteResourcesFunction = function(context) { - const verticesBuffer = this.verticesBuffer; - const indicesBuffer = this.indicesBuffer; - return function() { - context.deleteBuffer(verticesBuffer); - context.deleteBuffer(indicesBuffer); - }; -}; - - -/** - * @inheritDoc - */ -WebGLLineStringReplay.prototype.setUpProgram = function(gl, context, size, pixelRatio) { - // get the program - const program = context.getProgram(fragment, vertex); - - // get the locations - let locations; - if (!this.defaultLocations_) { - locations = new Locations(gl, program); - this.defaultLocations_ = locations; - } else { - locations = this.defaultLocations_; + /** + * @param {module:ol/Feature|module:ol/render/Feature} feature Feature. + * @param {number=} opt_index Index count. + */ + setPolygonStyle(feature, opt_index) { + const index = opt_index === undefined ? this.indices.length : opt_index; + this.startIndices.push(index); + this.startIndicesFeature.push(feature); + if (this.state_.changed) { + this.styleIndices_.push(index); + this.state_.changed = false; + } } - context.useProgram(program); - - // enable the vertex attrib arrays - gl.enableVertexAttribArray(locations.a_lastPos); - gl.vertexAttribPointer(locations.a_lastPos, 2, FLOAT, - false, 28, 0); - - gl.enableVertexAttribArray(locations.a_position); - gl.vertexAttribPointer(locations.a_position, 2, FLOAT, - false, 28, 8); - - gl.enableVertexAttribArray(locations.a_nextPos); - gl.vertexAttribPointer(locations.a_nextPos, 2, FLOAT, - false, 28, 16); - - gl.enableVertexAttribArray(locations.a_direction); - gl.vertexAttribPointer(locations.a_direction, 1, FLOAT, - false, 28, 24); - - // Enable renderer specific uniforms. - gl.uniform2fv(locations.u_size, size); - gl.uniform1f(locations.u_pixelRatio, pixelRatio); - - return locations; -}; - - -/** - * @inheritDoc - */ -WebGLLineStringReplay.prototype.shutDownProgram = function(gl, locations) { - gl.disableVertexAttribArray(locations.a_lastPos); - gl.disableVertexAttribArray(locations.a_position); - gl.disableVertexAttribArray(locations.a_nextPos); - gl.disableVertexAttribArray(locations.a_direction); -}; - - -/** - * @inheritDoc - */ -WebGLLineStringReplay.prototype.drawReplay = function(gl, context, skippedFeaturesHash, hitDetection) { - //Save GL parameters. - const tmpDepthFunc = /** @type {number} */ (gl.getParameter(gl.DEPTH_FUNC)); - const tmpDepthMask = /** @type {boolean} */ (gl.getParameter(gl.DEPTH_WRITEMASK)); - - if (!hitDetection) { - gl.enable(gl.DEPTH_TEST); - gl.depthMask(true); - gl.depthFunc(gl.NOTEQUAL); + /** + * @return {number} Current index. + */ + getCurrentIndex() { + return this.indices.length; } - if (!isEmpty(skippedFeaturesHash)) { - this.drawReplaySkipping_(gl, context, skippedFeaturesHash); - } else { - //Draw by style groups to minimize drawElements() calls. - let i, start, end, nextStyle; - end = this.startIndices[this.startIndices.length - 1]; + /** + * @inheritDoc + **/ + finish(context) { + // create, bind, and populate the vertices buffer + this.verticesBuffer = new WebGLBuffer(this.vertices); + + // create, bind, and populate the indices buffer + this.indicesBuffer = new WebGLBuffer(this.indices); + + this.startIndices.push(this.indices.length); + + //Clean up, if there is nothing to draw + if (this.styleIndices_.length === 0 && this.styles_.length > 0) { + this.styles_ = []; + } + + this.vertices = null; + this.indices = null; + } + + /** + * @inheritDoc + */ + getDeleteResourcesFunction(context) { + const verticesBuffer = this.verticesBuffer; + const indicesBuffer = this.indicesBuffer; + return function() { + context.deleteBuffer(verticesBuffer); + context.deleteBuffer(indicesBuffer); + }; + } + + /** + * @inheritDoc + */ + setUpProgram(gl, context, size, pixelRatio) { + // get the program + const program = context.getProgram(fragment, vertex); + + // get the locations + let locations; + if (!this.defaultLocations_) { + locations = new Locations(gl, program); + this.defaultLocations_ = locations; + } else { + locations = this.defaultLocations_; + } + + context.useProgram(program); + + // enable the vertex attrib arrays + gl.enableVertexAttribArray(locations.a_lastPos); + gl.vertexAttribPointer(locations.a_lastPos, 2, FLOAT, + false, 28, 0); + + gl.enableVertexAttribArray(locations.a_position); + gl.vertexAttribPointer(locations.a_position, 2, FLOAT, + false, 28, 8); + + gl.enableVertexAttribArray(locations.a_nextPos); + gl.vertexAttribPointer(locations.a_nextPos, 2, FLOAT, + false, 28, 16); + + gl.enableVertexAttribArray(locations.a_direction); + gl.vertexAttribPointer(locations.a_direction, 1, FLOAT, + false, 28, 24); + + // Enable renderer specific uniforms. + gl.uniform2fv(locations.u_size, size); + gl.uniform1f(locations.u_pixelRatio, pixelRatio); + + return locations; + } + + /** + * @inheritDoc + */ + shutDownProgram(gl, locations) { + gl.disableVertexAttribArray(locations.a_lastPos); + gl.disableVertexAttribArray(locations.a_position); + gl.disableVertexAttribArray(locations.a_nextPos); + gl.disableVertexAttribArray(locations.a_direction); + } + + /** + * @inheritDoc + */ + drawReplay(gl, context, skippedFeaturesHash, hitDetection) { + //Save GL parameters. + const tmpDepthFunc = /** @type {number} */ (gl.getParameter(gl.DEPTH_FUNC)); + const tmpDepthMask = /** @type {boolean} */ (gl.getParameter(gl.DEPTH_WRITEMASK)); + + if (!hitDetection) { + gl.enable(gl.DEPTH_TEST); + gl.depthMask(true); + gl.depthFunc(gl.NOTEQUAL); + } + + if (!isEmpty(skippedFeaturesHash)) { + this.drawReplaySkipping_(gl, context, skippedFeaturesHash); + } else { + //Draw by style groups to minimize drawElements() calls. + let i, start, end, nextStyle; + end = this.startIndices[this.startIndices.length - 1]; + for (i = this.styleIndices_.length - 1; i >= 0; --i) { + start = this.styleIndices_[i]; + nextStyle = this.styles_[i]; + this.setStrokeStyle_(gl, nextStyle[0], nextStyle[1], nextStyle[2]); + this.drawElements(gl, context, start, end); + gl.clear(gl.DEPTH_BUFFER_BIT); + end = start; + } + } + if (!hitDetection) { + gl.disable(gl.DEPTH_TEST); + gl.clear(gl.DEPTH_BUFFER_BIT); + //Restore GL parameters. + gl.depthMask(tmpDepthMask); + gl.depthFunc(tmpDepthFunc); + } + } + + /** + * @private + * @param {WebGLRenderingContext} gl gl. + * @param {module:ol/webgl/Context} context Context. + * @param {Object} skippedFeaturesHash Ids of features to skip. + */ + drawReplaySkipping_(gl, context, skippedFeaturesHash) { + let i, start, end, nextStyle, groupStart, feature, featureUid, featureIndex, featureStart; + featureIndex = this.startIndices.length - 2; + end = start = this.startIndices[featureIndex + 1]; for (i = this.styleIndices_.length - 1; i >= 0; --i) { - start = this.styleIndices_[i]; nextStyle = this.styles_[i]; this.setStrokeStyle_(gl, nextStyle[0], nextStyle[1], nextStyle[2]); - this.drawElements(gl, context, start, end); - gl.clear(gl.DEPTH_BUFFER_BIT); - end = start; - } - } - if (!hitDetection) { - gl.disable(gl.DEPTH_TEST); - gl.clear(gl.DEPTH_BUFFER_BIT); - //Restore GL parameters. - gl.depthMask(tmpDepthMask); - gl.depthFunc(tmpDepthFunc); - } -}; + groupStart = this.styleIndices_[i]; + while (featureIndex >= 0 && + this.startIndices[featureIndex] >= groupStart) { + featureStart = this.startIndices[featureIndex]; + feature = this.startIndicesFeature[featureIndex]; + featureUid = getUid(feature).toString(); -/** - * @private - * @param {WebGLRenderingContext} gl gl. - * @param {module:ol/webgl/Context} context Context. - * @param {Object} skippedFeaturesHash Ids of features to skip. - */ -WebGLLineStringReplay.prototype.drawReplaySkipping_ = function(gl, context, skippedFeaturesHash) { - let i, start, end, nextStyle, groupStart, feature, featureUid, featureIndex, featureStart; - featureIndex = this.startIndices.length - 2; - end = start = this.startIndices[featureIndex + 1]; - for (i = this.styleIndices_.length - 1; i >= 0; --i) { - nextStyle = this.styles_[i]; - this.setStrokeStyle_(gl, nextStyle[0], nextStyle[1], nextStyle[2]); - groupStart = this.styleIndices_[i]; - - while (featureIndex >= 0 && - this.startIndices[featureIndex] >= groupStart) { - featureStart = this.startIndices[featureIndex]; - feature = this.startIndicesFeature[featureIndex]; - featureUid = getUid(feature).toString(); - - if (skippedFeaturesHash[featureUid]) { - if (start !== end) { - this.drawElements(gl, context, start, end); - gl.clear(gl.DEPTH_BUFFER_BIT); + if (skippedFeaturesHash[featureUid]) { + if (start !== end) { + this.drawElements(gl, context, start, end); + gl.clear(gl.DEPTH_BUFFER_BIT); + } + end = featureStart; } - end = featureStart; + featureIndex--; + start = featureStart; } - featureIndex--; - start = featureStart; - } - if (start !== end) { - this.drawElements(gl, context, start, end); - gl.clear(gl.DEPTH_BUFFER_BIT); - } - start = end = groupStart; - } -}; - - -/** - * @inheritDoc - */ -WebGLLineStringReplay.prototype.drawHitDetectionReplayOneByOne = function(gl, context, skippedFeaturesHash, - featureCallback, opt_hitExtent) { - let i, start, end, nextStyle, groupStart, feature, featureUid, featureIndex; - featureIndex = this.startIndices.length - 2; - end = this.startIndices[featureIndex + 1]; - for (i = this.styleIndices_.length - 1; i >= 0; --i) { - nextStyle = this.styles_[i]; - this.setStrokeStyle_(gl, nextStyle[0], nextStyle[1], nextStyle[2]); - groupStart = this.styleIndices_[i]; - - while (featureIndex >= 0 && - this.startIndices[featureIndex] >= groupStart) { - start = this.startIndices[featureIndex]; - feature = this.startIndicesFeature[featureIndex]; - featureUid = getUid(feature).toString(); - - if (skippedFeaturesHash[featureUid] === undefined && - feature.getGeometry() && - (opt_hitExtent === undefined || intersects( - /** @type {Array} */ (opt_hitExtent), - feature.getGeometry().getExtent()))) { - gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); + if (start !== end) { this.drawElements(gl, context, start, end); - - const result = featureCallback(feature); - - if (result) { - return result; - } - + gl.clear(gl.DEPTH_BUFFER_BIT); } - featureIndex--; - end = start; + start = end = groupStart; } } - return undefined; -}; + /** + * @inheritDoc + */ + drawHitDetectionReplayOneByOne(gl, context, skippedFeaturesHash, featureCallback, opt_hitExtent) { + let i, start, end, nextStyle, groupStart, feature, featureUid, featureIndex; + featureIndex = this.startIndices.length - 2; + end = this.startIndices[featureIndex + 1]; + for (i = this.styleIndices_.length - 1; i >= 0; --i) { + nextStyle = this.styles_[i]; + this.setStrokeStyle_(gl, nextStyle[0], nextStyle[1], nextStyle[2]); + groupStart = this.styleIndices_[i]; -/** - * @private - * @param {WebGLRenderingContext} gl gl. - * @param {Array.} color Color. - * @param {number} lineWidth Line width. - * @param {number} miterLimit Miter limit. - */ -WebGLLineStringReplay.prototype.setStrokeStyle_ = function(gl, color, lineWidth, miterLimit) { - gl.uniform4fv(this.defaultLocations_.u_color, color); - gl.uniform1f(this.defaultLocations_.u_lineWidth, lineWidth); - gl.uniform1f(this.defaultLocations_.u_miterLimit, miterLimit); -}; + while (featureIndex >= 0 && + this.startIndices[featureIndex] >= groupStart) { + start = this.startIndices[featureIndex]; + feature = this.startIndicesFeature[featureIndex]; + featureUid = getUid(feature).toString(); + if (skippedFeaturesHash[featureUid] === undefined && + feature.getGeometry() && + (opt_hitExtent === undefined || intersects( + /** @type {Array} */ (opt_hitExtent), + feature.getGeometry().getExtent()))) { + gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); + this.drawElements(gl, context, start, end); -/** - * @inheritDoc - */ -WebGLLineStringReplay.prototype.setFillStrokeStyle = function(fillStyle, strokeStyle) { - const strokeStyleLineCap = strokeStyle.getLineCap(); - this.state_.lineCap = strokeStyleLineCap !== undefined ? - strokeStyleLineCap : DEFAULT_LINECAP; - const strokeStyleLineDash = strokeStyle.getLineDash(); - this.state_.lineDash = strokeStyleLineDash ? - strokeStyleLineDash : DEFAULT_LINEDASH; - const strokeStyleLineDashOffset = strokeStyle.getLineDashOffset(); - this.state_.lineDashOffset = strokeStyleLineDashOffset ? - strokeStyleLineDashOffset : DEFAULT_LINEDASHOFFSET; - const strokeStyleLineJoin = strokeStyle.getLineJoin(); - this.state_.lineJoin = strokeStyleLineJoin !== undefined ? - strokeStyleLineJoin : DEFAULT_LINEJOIN; - let strokeStyleColor = strokeStyle.getColor(); - if (!(strokeStyleColor instanceof CanvasGradient) && - !(strokeStyleColor instanceof CanvasPattern)) { - strokeStyleColor = asArray(strokeStyleColor).map(function(c, i) { - return i != 3 ? c / 255 : c; - }) || DEFAULT_STROKESTYLE; - } else { - strokeStyleColor = DEFAULT_STROKESTYLE; + const result = featureCallback(feature); + + if (result) { + return result; + } + + } + featureIndex--; + end = start; + } + } + return undefined; } - let strokeStyleWidth = strokeStyle.getWidth(); - strokeStyleWidth = strokeStyleWidth !== undefined ? - strokeStyleWidth : DEFAULT_LINEWIDTH; - let strokeStyleMiterLimit = strokeStyle.getMiterLimit(); - strokeStyleMiterLimit = strokeStyleMiterLimit !== undefined ? - strokeStyleMiterLimit : DEFAULT_MITERLIMIT; - if (!this.state_.strokeColor || !equals(this.state_.strokeColor, strokeStyleColor) || - this.state_.lineWidth !== strokeStyleWidth || this.state_.miterLimit !== strokeStyleMiterLimit) { - this.state_.changed = true; - this.state_.strokeColor = strokeStyleColor; - this.state_.lineWidth = strokeStyleWidth; - this.state_.miterLimit = strokeStyleMiterLimit; - this.styles_.push([strokeStyleColor, strokeStyleWidth, strokeStyleMiterLimit]); + + /** + * @private + * @param {WebGLRenderingContext} gl gl. + * @param {Array.} color Color. + * @param {number} lineWidth Line width. + * @param {number} miterLimit Miter limit. + */ + setStrokeStyle_(gl, color, lineWidth, miterLimit) { + gl.uniform4fv(this.defaultLocations_.u_color, color); + gl.uniform1f(this.defaultLocations_.u_lineWidth, lineWidth); + gl.uniform1f(this.defaultLocations_.u_miterLimit, miterLimit); } -}; + + /** + * @inheritDoc + */ + setFillStrokeStyle(fillStyle, strokeStyle) { + const strokeStyleLineCap = strokeStyle.getLineCap(); + this.state_.lineCap = strokeStyleLineCap !== undefined ? + strokeStyleLineCap : DEFAULT_LINECAP; + const strokeStyleLineDash = strokeStyle.getLineDash(); + this.state_.lineDash = strokeStyleLineDash ? + strokeStyleLineDash : DEFAULT_LINEDASH; + const strokeStyleLineDashOffset = strokeStyle.getLineDashOffset(); + this.state_.lineDashOffset = strokeStyleLineDashOffset ? + strokeStyleLineDashOffset : DEFAULT_LINEDASHOFFSET; + const strokeStyleLineJoin = strokeStyle.getLineJoin(); + this.state_.lineJoin = strokeStyleLineJoin !== undefined ? + strokeStyleLineJoin : DEFAULT_LINEJOIN; + let strokeStyleColor = strokeStyle.getColor(); + if (!(strokeStyleColor instanceof CanvasGradient) && + !(strokeStyleColor instanceof CanvasPattern)) { + strokeStyleColor = asArray(strokeStyleColor).map(function(c, i) { + return i != 3 ? c / 255 : c; + }) || DEFAULT_STROKESTYLE; + } else { + strokeStyleColor = DEFAULT_STROKESTYLE; + } + let strokeStyleWidth = strokeStyle.getWidth(); + strokeStyleWidth = strokeStyleWidth !== undefined ? + strokeStyleWidth : DEFAULT_LINEWIDTH; + let strokeStyleMiterLimit = strokeStyle.getMiterLimit(); + strokeStyleMiterLimit = strokeStyleMiterLimit !== undefined ? + strokeStyleMiterLimit : DEFAULT_MITERLIMIT; + if (!this.state_.strokeColor || !equals(this.state_.strokeColor, strokeStyleColor) || + this.state_.lineWidth !== strokeStyleWidth || this.state_.miterLimit !== strokeStyleMiterLimit) { + this.state_.changed = true; + this.state_.strokeColor = strokeStyleColor; + this.state_.lineWidth = strokeStyleWidth; + this.state_.miterLimit = strokeStyleMiterLimit; + this.styles_.push([strokeStyleColor, strokeStyleWidth, strokeStyleMiterLimit]); + } + } +} + +inherits(WebGLLineStringReplay, WebGLReplay); + export default WebGLLineStringReplay; diff --git a/src/ol/render/webgl/PolygonReplay.js b/src/ol/render/webgl/PolygonReplay.js index 580363bd88..14bb136170 100644 --- a/src/ol/render/webgl/PolygonReplay.js +++ b/src/ol/render/webgl/PolygonReplay.js @@ -43,1039 +43,1009 @@ import WebGLBuffer from '../../webgl/Buffer.js'; * @param {module:ol/extent~Extent} maxExtent Max extent. * @struct */ -const WebGLPolygonReplay = function(tolerance, maxExtent) { - WebGLReplay.call(this, tolerance, maxExtent); +class WebGLPolygonReplay { + constructor(tolerance, maxExtent) { + WebGLReplay.call(this, tolerance, maxExtent); - this.lineStringReplay = new WebGLLineStringReplay( - tolerance, maxExtent); + this.lineStringReplay = new WebGLLineStringReplay( + tolerance, maxExtent); + + /** + * @private + * @type {module:ol/render/webgl/polygonreplay/defaultshader/Locations} + */ + this.defaultLocations_ = null; + + /** + * @private + * @type {Array.>} + */ + this.styles_ = []; + + /** + * @private + * @type {Array.} + */ + this.styleIndices_ = []; + + /** + * @private + * @type {{fillColor: (Array.|null), + * changed: boolean}|null} + */ + this.state_ = { + fillColor: null, + changed: false + }; + + } /** + * Draw one polygon. + * @param {Array.} flatCoordinates Flat coordinates. + * @param {Array.>} holeFlatCoordinates Hole flat coordinates. + * @param {number} stride Stride. * @private - * @type {module:ol/render/webgl/polygonreplay/defaultshader/Locations} */ - this.defaultLocations_ = null; + drawCoordinates_(flatCoordinates, holeFlatCoordinates, stride) { + // Triangulate the polygon + const outerRing = new LinkedList(); + const rtree = new RBush(); + // Initialize the outer ring + this.processFlatCoordinates_(flatCoordinates, stride, outerRing, rtree, true); + const maxCoords = this.getMaxCoords_(outerRing); - /** - * @private - * @type {Array.>} - */ - this.styles_ = []; - - /** - * @private - * @type {Array.} - */ - this.styleIndices_ = []; - - /** - * @private - * @type {{fillColor: (Array.|null), - * changed: boolean}|null} - */ - this.state_ = { - fillColor: null, - changed: false - }; - -}; - -inherits(WebGLPolygonReplay, WebGLReplay); - - -/** - * Draw one polygon. - * @param {Array.} flatCoordinates Flat coordinates. - * @param {Array.>} holeFlatCoordinates Hole flat coordinates. - * @param {number} stride Stride. - * @private - */ -WebGLPolygonReplay.prototype.drawCoordinates_ = function( - flatCoordinates, holeFlatCoordinates, stride) { - // Triangulate the polygon - const outerRing = new LinkedList(); - const rtree = new RBush(); - // Initialize the outer ring - this.processFlatCoordinates_(flatCoordinates, stride, outerRing, rtree, true); - const maxCoords = this.getMaxCoords_(outerRing); - - // Eliminate holes, if there are any - if (holeFlatCoordinates.length) { - let i, ii; - const holeLists = []; - for (i = 0, ii = holeFlatCoordinates.length; i < ii; ++i) { - const holeList = { - list: new LinkedList(), - maxCoords: undefined, - rtree: new RBush() - }; - holeLists.push(holeList); - this.processFlatCoordinates_(holeFlatCoordinates[i], - stride, holeList.list, holeList.rtree, false); - this.classifyPoints_(holeList.list, holeList.rtree, true); - holeList.maxCoords = this.getMaxCoords_(holeList.list); - } - holeLists.sort(function(a, b) { - return b.maxCoords[0] === a.maxCoords[0] ? - a.maxCoords[1] - b.maxCoords[1] : b.maxCoords[0] - a.maxCoords[0]; - }); - for (i = 0; i < holeLists.length; ++i) { - const currList = holeLists[i].list; - const start = currList.firstItem(); - let currItem = start; - let intersection; - do { - //TODO: Triangulate holes when they intersect the outer ring. - if (this.getIntersections_(currItem, rtree).length) { - intersection = true; - break; - } - currItem = currList.nextItem(); - } while (start !== currItem); - if (!intersection) { - if (this.bridgeHole_(currList, holeLists[i].maxCoords[0], outerRing, maxCoords[0], rtree)) { - rtree.concat(holeLists[i].rtree); - this.classifyPoints_(outerRing, rtree, false); - } + // Eliminate holes, if there are any + if (holeFlatCoordinates.length) { + let i, ii; + const holeLists = []; + for (i = 0, ii = holeFlatCoordinates.length; i < ii; ++i) { + const holeList = { + list: new LinkedList(), + maxCoords: undefined, + rtree: new RBush() + }; + holeLists.push(holeList); + this.processFlatCoordinates_(holeFlatCoordinates[i], + stride, holeList.list, holeList.rtree, false); + this.classifyPoints_(holeList.list, holeList.rtree, true); + holeList.maxCoords = this.getMaxCoords_(holeList.list); } - } - } else { - this.classifyPoints_(outerRing, rtree, false); - } - this.triangulate_(outerRing, rtree); -}; - - -/** - * Inserts flat coordinates in a linked list and adds them to the vertex buffer. - * @private - * @param {Array.} flatCoordinates Flat coordinates. - * @param {number} stride Stride. - * @param {module:ol/structs/LinkedList} list Linked list. - * @param {module:ol/structs/RBush} rtree R-Tree of the polygon. - * @param {boolean} clockwise Coordinate order should be clockwise. - */ -WebGLPolygonReplay.prototype.processFlatCoordinates_ = function( - flatCoordinates, stride, list, rtree, clockwise) { - const isClockwise = linearRingIsClockwise(flatCoordinates, - 0, flatCoordinates.length, stride); - let i, ii; - let n = this.vertices.length / 2; - /** @type {module:ol/render/webgl/PolygonReplay~PolygonVertex} */ - let start; - /** @type {module:ol/render/webgl/PolygonReplay~PolygonVertex} */ - let p0; - /** @type {module:ol/render/webgl/PolygonReplay~PolygonVertex} */ - let p1; - const extents = []; - const segments = []; - if (clockwise === isClockwise) { - start = this.createPoint_(flatCoordinates[0], flatCoordinates[1], n++); - p0 = start; - for (i = stride, ii = flatCoordinates.length; i < ii; i += stride) { - p1 = this.createPoint_(flatCoordinates[i], flatCoordinates[i + 1], n++); - segments.push(this.insertItem_(p0, p1, list)); - extents.push([Math.min(p0.x, p1.x), Math.min(p0.y, p1.y), Math.max(p0.x, p1.x), - Math.max(p0.y, p1.y)]); - p0 = p1; - } - segments.push(this.insertItem_(p1, start, list)); - extents.push([Math.min(p0.x, p1.x), Math.min(p0.y, p1.y), Math.max(p0.x, p1.x), - Math.max(p0.y, p1.y)]); - } else { - const end = flatCoordinates.length - stride; - start = this.createPoint_(flatCoordinates[end], flatCoordinates[end + 1], n++); - p0 = start; - for (i = end - stride, ii = 0; i >= ii; i -= stride) { - p1 = this.createPoint_(flatCoordinates[i], flatCoordinates[i + 1], n++); - segments.push(this.insertItem_(p0, p1, list)); - extents.push([Math.min(p0.x, p1.x), Math.min(p0.y, p1.y), Math.max(p0.x, p1.x), - Math.max(p0.y, p1.y)]); - p0 = p1; - } - segments.push(this.insertItem_(p1, start, list)); - extents.push([Math.min(p0.x, p1.x), Math.min(p0.y, p1.y), Math.max(p0.x, p1.x), - Math.max(p0.y, p1.y)]); - } - rtree.load(extents, segments); -}; - - -/** - * Returns the rightmost coordinates of a polygon on the X axis. - * @private - * @param {module:ol/structs/LinkedList} list Polygons ring. - * @return {Array.} Max X coordinates. - */ -WebGLPolygonReplay.prototype.getMaxCoords_ = function(list) { - const start = list.firstItem(); - let seg = start; - let maxCoords = [seg.p0.x, seg.p0.y]; - - do { - seg = list.nextItem(); - if (seg.p0.x > maxCoords[0]) { - maxCoords = [seg.p0.x, seg.p0.y]; - } - } while (seg !== start); - - return maxCoords; -}; - - -/** - * Classifies the points of a polygon list as convex, reflex. Removes collinear vertices. - * @private - * @param {module:ol/structs/LinkedList} list Polygon ring. - * @param {module:ol/structs/RBush} rtree R-Tree of the polygon. - * @param {boolean} ccw The orientation of the polygon is counter-clockwise. - * @return {boolean} There were reclassified points. - */ -WebGLPolygonReplay.prototype.classifyPoints_ = function(list, rtree, ccw) { - let start = list.firstItem(); - let s0 = start; - let s1 = list.nextItem(); - let pointsReclassified = false; - do { - const reflex = ccw ? triangleIsCounterClockwise(s1.p1.x, - s1.p1.y, s0.p1.x, s0.p1.y, s0.p0.x, s0.p0.y) : - triangleIsCounterClockwise(s0.p0.x, s0.p0.y, s0.p1.x, - s0.p1.y, s1.p1.x, s1.p1.y); - if (reflex === undefined) { - this.removeItem_(s0, s1, list, rtree); - pointsReclassified = true; - if (s1 === start) { - start = list.getNextItem(); - } - s1 = s0; - list.prevItem(); - } else if (s0.p1.reflex !== reflex) { - s0.p1.reflex = reflex; - pointsReclassified = true; - } - s0 = s1; - s1 = list.nextItem(); - } while (s0 !== start); - return pointsReclassified; -}; - - -/** - * @private - * @param {module:ol/structs/LinkedList} hole Linked list of the hole. - * @param {number} holeMaxX Maximum X value of the hole. - * @param {module:ol/structs/LinkedList} list Linked list of the polygon. - * @param {number} listMaxX Maximum X value of the polygon. - * @param {module:ol/structs/RBush} rtree R-Tree of the polygon. - * @return {boolean} Bridging was successful. - */ -WebGLPolygonReplay.prototype.bridgeHole_ = function(hole, holeMaxX, - list, listMaxX, rtree) { - let seg = hole.firstItem(); - while (seg.p1.x !== holeMaxX) { - seg = hole.nextItem(); - } - - const p1 = seg.p1; - /** @type {module:ol/render/webgl/PolygonReplay~PolygonVertex} */ - const p2 = {x: listMaxX, y: p1.y, i: -1}; - let minDist = Infinity; - let i, ii, bestPoint; - /** @type {module:ol/render/webgl/PolygonReplay~PolygonVertex} */ - let p5; - - const intersectingSegments = this.getIntersections_({p0: p1, p1: p2}, rtree, true); - for (i = 0, ii = intersectingSegments.length; i < ii; ++i) { - const currSeg = intersectingSegments[i]; - const intersection = this.calculateIntersection_(p1, p2, currSeg.p0, - currSeg.p1, true); - const dist = Math.abs(p1.x - intersection[0]); - if (dist < minDist && triangleIsCounterClockwise(p1.x, p1.y, - currSeg.p0.x, currSeg.p0.y, currSeg.p1.x, currSeg.p1.y) !== undefined) { - minDist = dist; - p5 = {x: intersection[0], y: intersection[1], i: -1}; - seg = currSeg; - } - } - if (minDist === Infinity) { - return false; - } - bestPoint = seg.p1; - - if (minDist > 0) { - const pointsInTriangle = this.getPointsInTriangle_(p1, p5, seg.p1, rtree); - if (pointsInTriangle.length) { - let theta = Infinity; - for (i = 0, ii = pointsInTriangle.length; i < ii; ++i) { - const currPoint = pointsInTriangle[i]; - const currTheta = Math.atan2(p1.y - currPoint.y, p2.x - currPoint.x); - if (currTheta < theta || (currTheta === theta && currPoint.x < bestPoint.x)) { - theta = currTheta; - bestPoint = currPoint; - } - } - } - } - - seg = list.firstItem(); - while (seg.p1.x !== bestPoint.x || seg.p1.y !== bestPoint.y) { - seg = list.nextItem(); - } - - //We clone the bridge points as they can have different convexity. - const p0Bridge = {x: p1.x, y: p1.y, i: p1.i, reflex: undefined}; - const p1Bridge = {x: seg.p1.x, y: seg.p1.y, i: seg.p1.i, reflex: undefined}; - - hole.getNextItem().p0 = p0Bridge; - this.insertItem_(p1, seg.p1, hole, rtree); - this.insertItem_(p1Bridge, p0Bridge, hole, rtree); - seg.p1 = p1Bridge; - hole.setFirstItem(); - list.concat(hole); - - return true; -}; - - -/** - * @private - * @param {module:ol/structs/LinkedList} list Linked list of the polygon. - * @param {module:ol/structs/RBush} rtree R-Tree of the polygon. - */ -WebGLPolygonReplay.prototype.triangulate_ = function(list, rtree) { - let ccw = false; - let simple = this.isSimple_(list, rtree); - - // Start clipping ears - while (list.getLength() > 3) { - if (simple) { - if (!this.clipEars_(list, rtree, simple, ccw)) { - if (!this.classifyPoints_(list, rtree, ccw)) { - // Due to the behavior of OL's PIP algorithm, the ear clipping cannot - // introduce touching segments. However, the original data may have some. - if (!this.resolveSelfIntersections_(list, rtree, true)) { + holeLists.sort(function(a, b) { + return b.maxCoords[0] === a.maxCoords[0] ? + a.maxCoords[1] - b.maxCoords[1] : b.maxCoords[0] - a.maxCoords[0]; + }); + for (i = 0; i < holeLists.length; ++i) { + const currList = holeLists[i].list; + const start = currList.firstItem(); + let currItem = start; + let intersection; + do { + //TODO: Triangulate holes when they intersect the outer ring. + if (this.getIntersections_(currItem, rtree).length) { + intersection = true; break; } + currItem = currList.nextItem(); + } while (start !== currItem); + if (!intersection) { + if (this.bridgeHole_(currList, holeLists[i].maxCoords[0], outerRing, maxCoords[0], rtree)) { + rtree.concat(holeLists[i].rtree); + this.classifyPoints_(outerRing, rtree, false); + } } } } else { - if (!this.clipEars_(list, rtree, simple, ccw)) { - // We ran out of ears, try to reclassify. - if (!this.classifyPoints_(list, rtree, ccw)) { - // We have a bad polygon, try to resolve local self-intersections. - if (!this.resolveSelfIntersections_(list, rtree)) { - simple = this.isSimple_(list, rtree); - if (!simple) { - // We have a really bad polygon, try more time consuming methods. - this.splitPolygon_(list, rtree); + this.classifyPoints_(outerRing, rtree, false); + } + this.triangulate_(outerRing, rtree); + } + + /** + * Inserts flat coordinates in a linked list and adds them to the vertex buffer. + * @private + * @param {Array.} flatCoordinates Flat coordinates. + * @param {number} stride Stride. + * @param {module:ol/structs/LinkedList} list Linked list. + * @param {module:ol/structs/RBush} rtree R-Tree of the polygon. + * @param {boolean} clockwise Coordinate order should be clockwise. + */ + processFlatCoordinates_(flatCoordinates, stride, list, rtree, clockwise) { + const isClockwise = linearRingIsClockwise(flatCoordinates, + 0, flatCoordinates.length, stride); + let i, ii; + let n = this.vertices.length / 2; + /** @type {module:ol/render/webgl/PolygonReplay~PolygonVertex} */ + let start; + /** @type {module:ol/render/webgl/PolygonReplay~PolygonVertex} */ + let p0; + /** @type {module:ol/render/webgl/PolygonReplay~PolygonVertex} */ + let p1; + const extents = []; + const segments = []; + if (clockwise === isClockwise) { + start = this.createPoint_(flatCoordinates[0], flatCoordinates[1], n++); + p0 = start; + for (i = stride, ii = flatCoordinates.length; i < ii; i += stride) { + p1 = this.createPoint_(flatCoordinates[i], flatCoordinates[i + 1], n++); + segments.push(this.insertItem_(p0, p1, list)); + extents.push([Math.min(p0.x, p1.x), Math.min(p0.y, p1.y), Math.max(p0.x, p1.x), + Math.max(p0.y, p1.y)]); + p0 = p1; + } + segments.push(this.insertItem_(p1, start, list)); + extents.push([Math.min(p0.x, p1.x), Math.min(p0.y, p1.y), Math.max(p0.x, p1.x), + Math.max(p0.y, p1.y)]); + } else { + const end = flatCoordinates.length - stride; + start = this.createPoint_(flatCoordinates[end], flatCoordinates[end + 1], n++); + p0 = start; + for (i = end - stride, ii = 0; i >= ii; i -= stride) { + p1 = this.createPoint_(flatCoordinates[i], flatCoordinates[i + 1], n++); + segments.push(this.insertItem_(p0, p1, list)); + extents.push([Math.min(p0.x, p1.x), Math.min(p0.y, p1.y), Math.max(p0.x, p1.x), + Math.max(p0.y, p1.y)]); + p0 = p1; + } + segments.push(this.insertItem_(p1, start, list)); + extents.push([Math.min(p0.x, p1.x), Math.min(p0.y, p1.y), Math.max(p0.x, p1.x), + Math.max(p0.y, p1.y)]); + } + rtree.load(extents, segments); + } + + /** + * Returns the rightmost coordinates of a polygon on the X axis. + * @private + * @param {module:ol/structs/LinkedList} list Polygons ring. + * @return {Array.} Max X coordinates. + */ + getMaxCoords_(list) { + const start = list.firstItem(); + let seg = start; + let maxCoords = [seg.p0.x, seg.p0.y]; + + do { + seg = list.nextItem(); + if (seg.p0.x > maxCoords[0]) { + maxCoords = [seg.p0.x, seg.p0.y]; + } + } while (seg !== start); + + return maxCoords; + } + + /** + * Classifies the points of a polygon list as convex, reflex. Removes collinear vertices. + * @private + * @param {module:ol/structs/LinkedList} list Polygon ring. + * @param {module:ol/structs/RBush} rtree R-Tree of the polygon. + * @param {boolean} ccw The orientation of the polygon is counter-clockwise. + * @return {boolean} There were reclassified points. + */ + classifyPoints_(list, rtree, ccw) { + let start = list.firstItem(); + let s0 = start; + let s1 = list.nextItem(); + let pointsReclassified = false; + do { + const reflex = ccw ? triangleIsCounterClockwise(s1.p1.x, + s1.p1.y, s0.p1.x, s0.p1.y, s0.p0.x, s0.p0.y) : + triangleIsCounterClockwise(s0.p0.x, s0.p0.y, s0.p1.x, + s0.p1.y, s1.p1.x, s1.p1.y); + if (reflex === undefined) { + this.removeItem_(s0, s1, list, rtree); + pointsReclassified = true; + if (s1 === start) { + start = list.getNextItem(); + } + s1 = s0; + list.prevItem(); + } else if (s0.p1.reflex !== reflex) { + s0.p1.reflex = reflex; + pointsReclassified = true; + } + s0 = s1; + s1 = list.nextItem(); + } while (s0 !== start); + return pointsReclassified; + } + + /** + * @private + * @param {module:ol/structs/LinkedList} hole Linked list of the hole. + * @param {number} holeMaxX Maximum X value of the hole. + * @param {module:ol/structs/LinkedList} list Linked list of the polygon. + * @param {number} listMaxX Maximum X value of the polygon. + * @param {module:ol/structs/RBush} rtree R-Tree of the polygon. + * @return {boolean} Bridging was successful. + */ + bridgeHole_(hole, holeMaxX, list, listMaxX, rtree) { + let seg = hole.firstItem(); + while (seg.p1.x !== holeMaxX) { + seg = hole.nextItem(); + } + + const p1 = seg.p1; + /** @type {module:ol/render/webgl/PolygonReplay~PolygonVertex} */ + const p2 = {x: listMaxX, y: p1.y, i: -1}; + let minDist = Infinity; + let i, ii, bestPoint; + /** @type {module:ol/render/webgl/PolygonReplay~PolygonVertex} */ + let p5; + + const intersectingSegments = this.getIntersections_({p0: p1, p1: p2}, rtree, true); + for (i = 0, ii = intersectingSegments.length; i < ii; ++i) { + const currSeg = intersectingSegments[i]; + const intersection = this.calculateIntersection_(p1, p2, currSeg.p0, + currSeg.p1, true); + const dist = Math.abs(p1.x - intersection[0]); + if (dist < minDist && triangleIsCounterClockwise(p1.x, p1.y, + currSeg.p0.x, currSeg.p0.y, currSeg.p1.x, currSeg.p1.y) !== undefined) { + minDist = dist; + p5 = {x: intersection[0], y: intersection[1], i: -1}; + seg = currSeg; + } + } + if (minDist === Infinity) { + return false; + } + bestPoint = seg.p1; + + if (minDist > 0) { + const pointsInTriangle = this.getPointsInTriangle_(p1, p5, seg.p1, rtree); + if (pointsInTriangle.length) { + let theta = Infinity; + for (i = 0, ii = pointsInTriangle.length; i < ii; ++i) { + const currPoint = pointsInTriangle[i]; + const currTheta = Math.atan2(p1.y - currPoint.y, p2.x - currPoint.x); + if (currTheta < theta || (currTheta === theta && currPoint.x < bestPoint.x)) { + theta = currTheta; + bestPoint = currPoint; + } + } + } + } + + seg = list.firstItem(); + while (seg.p1.x !== bestPoint.x || seg.p1.y !== bestPoint.y) { + seg = list.nextItem(); + } + + //We clone the bridge points as they can have different convexity. + const p0Bridge = {x: p1.x, y: p1.y, i: p1.i, reflex: undefined}; + const p1Bridge = {x: seg.p1.x, y: seg.p1.y, i: seg.p1.i, reflex: undefined}; + + hole.getNextItem().p0 = p0Bridge; + this.insertItem_(p1, seg.p1, hole, rtree); + this.insertItem_(p1Bridge, p0Bridge, hole, rtree); + seg.p1 = p1Bridge; + hole.setFirstItem(); + list.concat(hole); + + return true; + } + + /** + * @private + * @param {module:ol/structs/LinkedList} list Linked list of the polygon. + * @param {module:ol/structs/RBush} rtree R-Tree of the polygon. + */ + triangulate_(list, rtree) { + let ccw = false; + let simple = this.isSimple_(list, rtree); + + // Start clipping ears + while (list.getLength() > 3) { + if (simple) { + if (!this.clipEars_(list, rtree, simple, ccw)) { + if (!this.classifyPoints_(list, rtree, ccw)) { + // Due to the behavior of OL's PIP algorithm, the ear clipping cannot + // introduce touching segments. However, the original data may have some. + if (!this.resolveSelfIntersections_(list, rtree, true)) { break; - } else { - ccw = !this.isClockwise_(list); - this.classifyPoints_(list, rtree, ccw); + } + } + } + } else { + if (!this.clipEars_(list, rtree, simple, ccw)) { + // We ran out of ears, try to reclassify. + if (!this.classifyPoints_(list, rtree, ccw)) { + // We have a bad polygon, try to resolve local self-intersections. + if (!this.resolveSelfIntersections_(list, rtree)) { + simple = this.isSimple_(list, rtree); + if (!simple) { + // We have a really bad polygon, try more time consuming methods. + this.splitPolygon_(list, rtree); + break; + } else { + ccw = !this.isClockwise_(list); + this.classifyPoints_(list, rtree, ccw); + } } } } } } - } - if (list.getLength() === 3) { - let numIndices = this.indices.length; - this.indices[numIndices++] = list.getPrevItem().p0.i; - this.indices[numIndices++] = list.getCurrItem().p0.i; - this.indices[numIndices++] = list.getNextItem().p0.i; - } -}; - - -/** - * @private - * @param {module:ol/structs/LinkedList} list Linked list of the polygon. - * @param {module:ol/structs/RBush} rtree R-Tree of the polygon. - * @param {boolean} simple The polygon is simple. - * @param {boolean} ccw Orientation of the polygon is counter-clockwise. - * @return {boolean} There were processed ears. - */ -WebGLPolygonReplay.prototype.clipEars_ = function(list, rtree, simple, ccw) { - let numIndices = this.indices.length; - let start = list.firstItem(); - let s0 = list.getPrevItem(); - let s1 = start; - let s2 = list.nextItem(); - let s3 = list.getNextItem(); - let p0, p1, p2; - let processedEars = false; - do { - p0 = s1.p0; - p1 = s1.p1; - p2 = s2.p1; - if (p1.reflex === false) { - // We might have a valid ear - let variableCriterion; - if (simple) { - variableCriterion = this.getPointsInTriangle_(p0, p1, p2, rtree, true).length === 0; - } else { - variableCriterion = ccw ? this.diagonalIsInside_(s3.p1, p2, p1, p0, - s0.p0) : this.diagonalIsInside_(s0.p0, p0, p1, p2, s3.p1); - } - if ((simple || this.getIntersections_({p0: p0, p1: p2}, rtree).length === 0) && - variableCriterion) { - //The diagonal is completely inside the polygon - if (simple || p0.reflex === false || p2.reflex === false || - linearRingIsClockwise([s0.p0.x, s0.p0.y, p0.x, - p0.y, p1.x, p1.y, p2.x, p2.y, s3.p1.x, s3.p1.y], 0, 10, 2) === !ccw) { - //The diagonal is persumably valid, we have an ear - this.indices[numIndices++] = p0.i; - this.indices[numIndices++] = p1.i; - this.indices[numIndices++] = p2.i; - this.removeItem_(s1, s2, list, rtree); - if (s2 === start) { - start = s3; - } - processedEars = true; - } - } - } - // Else we have a reflex point. - s0 = list.getPrevItem(); - s1 = list.getCurrItem(); - s2 = list.nextItem(); - s3 = list.getNextItem(); - } while (s1 !== start && list.getLength() > 3); - - return processedEars; -}; - - -/** - * @private - * @param {module:ol/structs/LinkedList} list Linked list of the polygon. - * @param {module:ol/structs/RBush} rtree R-Tree of the polygon. - * @param {boolean=} opt_touch Resolve touching segments. - * @return {boolean} There were resolved intersections. -*/ -WebGLPolygonReplay.prototype.resolveSelfIntersections_ = function( - list, rtree, opt_touch) { - const start = list.firstItem(); - list.nextItem(); - let s0 = start; - let s1 = list.nextItem(); - let resolvedIntersections = false; - - do { - const intersection = this.calculateIntersection_(s0.p0, s0.p1, s1.p0, s1.p1, - opt_touch); - if (intersection) { - let breakCond = false; - const numVertices = this.vertices.length; + if (list.getLength() === 3) { let numIndices = this.indices.length; - const n = numVertices / 2; - const seg = list.prevItem(); - list.removeItem(); - rtree.remove(seg); - breakCond = (seg === start); - let p; - if (opt_touch) { - if (intersection[0] === s0.p0.x && intersection[1] === s0.p0.y) { - list.prevItem(); - p = s0.p0; - s1.p0 = p; - rtree.remove(s0); - breakCond = breakCond || (s0 === start); + this.indices[numIndices++] = list.getPrevItem().p0.i; + this.indices[numIndices++] = list.getCurrItem().p0.i; + this.indices[numIndices++] = list.getNextItem().p0.i; + } + } + + /** + * @private + * @param {module:ol/structs/LinkedList} list Linked list of the polygon. + * @param {module:ol/structs/RBush} rtree R-Tree of the polygon. + * @param {boolean} simple The polygon is simple. + * @param {boolean} ccw Orientation of the polygon is counter-clockwise. + * @return {boolean} There were processed ears. + */ + clipEars_(list, rtree, simple, ccw) { + let numIndices = this.indices.length; + let start = list.firstItem(); + let s0 = list.getPrevItem(); + let s1 = start; + let s2 = list.nextItem(); + let s3 = list.getNextItem(); + let p0, p1, p2; + let processedEars = false; + do { + p0 = s1.p0; + p1 = s1.p1; + p2 = s2.p1; + if (p1.reflex === false) { + // We might have a valid ear + let variableCriterion; + if (simple) { + variableCriterion = this.getPointsInTriangle_(p0, p1, p2, rtree, true).length === 0; } else { - p = s1.p1; - s0.p1 = p; - rtree.remove(s1); - breakCond = breakCond || (s1 === start); + variableCriterion = ccw ? this.diagonalIsInside_(s3.p1, p2, p1, p0, + s0.p0) : this.diagonalIsInside_(s0.p0, p0, p1, p2, s3.p1); } + if ((simple || this.getIntersections_({p0: p0, p1: p2}, rtree).length === 0) && + variableCriterion) { + //The diagonal is completely inside the polygon + if (simple || p0.reflex === false || p2.reflex === false || + linearRingIsClockwise([s0.p0.x, s0.p0.y, p0.x, + p0.y, p1.x, p1.y, p2.x, p2.y, s3.p1.x, s3.p1.y], 0, 10, 2) === !ccw) { + //The diagonal is persumably valid, we have an ear + this.indices[numIndices++] = p0.i; + this.indices[numIndices++] = p1.i; + this.indices[numIndices++] = p2.i; + this.removeItem_(s1, s2, list, rtree); + if (s2 === start) { + start = s3; + } + processedEars = true; + } + } + } + // Else we have a reflex point. + s0 = list.getPrevItem(); + s1 = list.getCurrItem(); + s2 = list.nextItem(); + s3 = list.getNextItem(); + } while (s1 !== start && list.getLength() > 3); + + return processedEars; + } + + /** + * @private + * @param {module:ol/structs/LinkedList} list Linked list of the polygon. + * @param {module:ol/structs/RBush} rtree R-Tree of the polygon. + * @param {boolean=} opt_touch Resolve touching segments. + * @return {boolean} There were resolved intersections. + */ + resolveSelfIntersections_(list, rtree, opt_touch) { + const start = list.firstItem(); + list.nextItem(); + let s0 = start; + let s1 = list.nextItem(); + let resolvedIntersections = false; + + do { + const intersection = this.calculateIntersection_(s0.p0, s0.p1, s1.p0, s1.p1, + opt_touch); + if (intersection) { + let breakCond = false; + const numVertices = this.vertices.length; + let numIndices = this.indices.length; + const n = numVertices / 2; + const seg = list.prevItem(); list.removeItem(); - } else { - p = this.createPoint_(intersection[0], intersection[1], n); - s0.p1 = p; - s1.p0 = p; - rtree.update([Math.min(s0.p0.x, s0.p1.x), Math.min(s0.p0.y, s0.p1.y), - Math.max(s0.p0.x, s0.p1.x), Math.max(s0.p0.y, s0.p1.y)], s0); - rtree.update([Math.min(s1.p0.x, s1.p1.x), Math.min(s1.p0.y, s1.p1.y), - Math.max(s1.p0.x, s1.p1.x), Math.max(s1.p0.y, s1.p1.y)], s1); + rtree.remove(seg); + breakCond = (seg === start); + let p; + if (opt_touch) { + if (intersection[0] === s0.p0.x && intersection[1] === s0.p0.y) { + list.prevItem(); + p = s0.p0; + s1.p0 = p; + rtree.remove(s0); + breakCond = breakCond || (s0 === start); + } else { + p = s1.p1; + s0.p1 = p; + rtree.remove(s1); + breakCond = breakCond || (s1 === start); + } + list.removeItem(); + } else { + p = this.createPoint_(intersection[0], intersection[1], n); + s0.p1 = p; + s1.p0 = p; + rtree.update([Math.min(s0.p0.x, s0.p1.x), Math.min(s0.p0.y, s0.p1.y), + Math.max(s0.p0.x, s0.p1.x), Math.max(s0.p0.y, s0.p1.y)], s0); + rtree.update([Math.min(s1.p0.x, s1.p1.x), Math.min(s1.p0.y, s1.p1.y), + Math.max(s1.p0.x, s1.p1.x), Math.max(s1.p0.y, s1.p1.y)], s1); + } + + this.indices[numIndices++] = seg.p0.i; + this.indices[numIndices++] = seg.p1.i; + this.indices[numIndices++] = p.i; + + resolvedIntersections = true; + if (breakCond) { + break; + } } - this.indices[numIndices++] = seg.p0.i; - this.indices[numIndices++] = seg.p1.i; - this.indices[numIndices++] = p.i; + s0 = list.getPrevItem(); + s1 = list.nextItem(); + } while (s0 !== start); + return resolvedIntersections; + } - resolvedIntersections = true; - if (breakCond) { + /** + * @private + * @param {module:ol/structs/LinkedList} list Linked list of the polygon. + * @param {module:ol/structs/RBush} rtree R-Tree of the polygon. + * @return {boolean} The polygon is simple. + */ + isSimple_(list, rtree) { + const start = list.firstItem(); + let seg = start; + do { + if (this.getIntersections_(seg, rtree).length) { + return false; + } + seg = list.nextItem(); + } while (seg !== start); + return true; + } + + /** + * @private + * @param {module:ol/structs/LinkedList} list Linked list of the polygon. + * @return {boolean} Orientation is clockwise. + */ + isClockwise_(list) { + const length = list.getLength() * 2; + const flatCoordinates = new Array(length); + const start = list.firstItem(); + let seg = start; + let i = 0; + do { + flatCoordinates[i++] = seg.p0.x; + flatCoordinates[i++] = seg.p0.y; + seg = list.nextItem(); + } while (seg !== start); + return linearRingIsClockwise(flatCoordinates, 0, length, 2); + } + + /** + * @private + * @param {module:ol/structs/LinkedList} list Linked list of the polygon. + * @param {module:ol/structs/RBush} rtree R-Tree of the polygon. + */ + splitPolygon_(list, rtree) { + const start = list.firstItem(); + let s0 = start; + do { + const intersections = this.getIntersections_(s0, rtree); + if (intersections.length) { + const s1 = intersections[0]; + const n = this.vertices.length / 2; + const intersection = this.calculateIntersection_(s0.p0, + s0.p1, s1.p0, s1.p1); + const p = this.createPoint_(intersection[0], intersection[1], n); + const newPolygon = new LinkedList(); + const newRtree = new RBush(); + this.insertItem_(p, s0.p1, newPolygon, newRtree); + s0.p1 = p; + rtree.update([Math.min(s0.p0.x, p.x), Math.min(s0.p0.y, p.y), + Math.max(s0.p0.x, p.x), Math.max(s0.p0.y, p.y)], s0); + let currItem = list.nextItem(); + while (currItem !== s1) { + this.insertItem_(currItem.p0, currItem.p1, newPolygon, newRtree); + rtree.remove(currItem); + list.removeItem(); + currItem = list.getCurrItem(); + } + this.insertItem_(s1.p0, p, newPolygon, newRtree); + s1.p0 = p; + rtree.update([Math.min(s1.p1.x, p.x), Math.min(s1.p1.y, p.y), + Math.max(s1.p1.x, p.x), Math.max(s1.p1.y, p.y)], s1); + this.classifyPoints_(list, rtree, false); + this.triangulate_(list, rtree); + this.classifyPoints_(newPolygon, newRtree, false); + this.triangulate_(newPolygon, newRtree); break; } - } - - s0 = list.getPrevItem(); - s1 = list.nextItem(); - } while (s0 !== start); - return resolvedIntersections; -}; - - -/** - * @private - * @param {module:ol/structs/LinkedList} list Linked list of the polygon. - * @param {module:ol/structs/RBush} rtree R-Tree of the polygon. - * @return {boolean} The polygon is simple. - */ -WebGLPolygonReplay.prototype.isSimple_ = function(list, rtree) { - const start = list.firstItem(); - let seg = start; - do { - if (this.getIntersections_(seg, rtree).length) { - return false; - } - seg = list.nextItem(); - } while (seg !== start); - return true; -}; - - -/** - * @private - * @param {module:ol/structs/LinkedList} list Linked list of the polygon. - * @return {boolean} Orientation is clockwise. - */ -WebGLPolygonReplay.prototype.isClockwise_ = function(list) { - const length = list.getLength() * 2; - const flatCoordinates = new Array(length); - const start = list.firstItem(); - let seg = start; - let i = 0; - do { - flatCoordinates[i++] = seg.p0.x; - flatCoordinates[i++] = seg.p0.y; - seg = list.nextItem(); - } while (seg !== start); - return linearRingIsClockwise(flatCoordinates, 0, length, 2); -}; - - -/** - * @private - * @param {module:ol/structs/LinkedList} list Linked list of the polygon. - * @param {module:ol/structs/RBush} rtree R-Tree of the polygon. - */ -WebGLPolygonReplay.prototype.splitPolygon_ = function(list, rtree) { - const start = list.firstItem(); - let s0 = start; - do { - const intersections = this.getIntersections_(s0, rtree); - if (intersections.length) { - const s1 = intersections[0]; - const n = this.vertices.length / 2; - const intersection = this.calculateIntersection_(s0.p0, - s0.p1, s1.p0, s1.p1); - const p = this.createPoint_(intersection[0], intersection[1], n); - const newPolygon = new LinkedList(); - const newRtree = new RBush(); - this.insertItem_(p, s0.p1, newPolygon, newRtree); - s0.p1 = p; - rtree.update([Math.min(s0.p0.x, p.x), Math.min(s0.p0.y, p.y), - Math.max(s0.p0.x, p.x), Math.max(s0.p0.y, p.y)], s0); - let currItem = list.nextItem(); - while (currItem !== s1) { - this.insertItem_(currItem.p0, currItem.p1, newPolygon, newRtree); - rtree.remove(currItem); - list.removeItem(); - currItem = list.getCurrItem(); - } - this.insertItem_(s1.p0, p, newPolygon, newRtree); - s1.p0 = p; - rtree.update([Math.min(s1.p1.x, p.x), Math.min(s1.p1.y, p.y), - Math.max(s1.p1.x, p.x), Math.max(s1.p1.y, p.y)], s1); - this.classifyPoints_(list, rtree, false); - this.triangulate_(list, rtree); - this.classifyPoints_(newPolygon, newRtree, false); - this.triangulate_(newPolygon, newRtree); - break; - } - s0 = list.nextItem(); - } while (s0 !== start); -}; - - -/** - * @private - * @param {number} x X coordinate. - * @param {number} y Y coordinate. - * @param {number} i Index. - * @return {module:ol/render/webgl/PolygonReplay~PolygonVertex} List item. - */ -WebGLPolygonReplay.prototype.createPoint_ = function(x, y, i) { - let numVertices = this.vertices.length; - this.vertices[numVertices++] = x; - this.vertices[numVertices++] = y; - /** @type {module:ol/render/webgl/PolygonReplay~PolygonVertex} */ - const p = { - x: x, - y: y, - i: i, - reflex: undefined - }; - return p; -}; - - -/** - * @private - * @param {module:ol/render/webgl/PolygonReplay~PolygonVertex} p0 First point of segment. - * @param {module:ol/render/webgl/PolygonReplay~PolygonVertex} p1 Second point of segment. - * @param {module:ol/structs/LinkedList} list Polygon ring. - * @param {module:ol/structs/RBush=} opt_rtree Insert the segment into the R-Tree. - * @return {module:ol/render/webgl/PolygonReplay~PolygonSegment} segment. - */ -WebGLPolygonReplay.prototype.insertItem_ = function(p0, p1, list, opt_rtree) { - const seg = { - p0: p0, - p1: p1 - }; - list.insertItem(seg); - if (opt_rtree) { - opt_rtree.insert([Math.min(p0.x, p1.x), Math.min(p0.y, p1.y), - Math.max(p0.x, p1.x), Math.max(p0.y, p1.y)], seg); + s0 = list.nextItem(); + } while (s0 !== start); } - return seg; -}; - -/** - * @private - * @param {module:ol/render/webgl/PolygonReplay~PolygonSegment} s0 Segment before the remove candidate. - * @param {module:ol/render/webgl/PolygonReplay~PolygonSegment} s1 Remove candidate segment. - * @param {module:ol/structs/LinkedList} list Polygon ring. - * @param {module:ol/structs/RBush} rtree R-Tree of the polygon. - */ -WebGLPolygonReplay.prototype.removeItem_ = function(s0, s1, list, rtree) { - if (list.getCurrItem() === s1) { - list.removeItem(); - s0.p1 = s1.p1; - rtree.remove(s1); - rtree.update([Math.min(s0.p0.x, s0.p1.x), Math.min(s0.p0.y, s0.p1.y), - Math.max(s0.p0.x, s0.p1.x), Math.max(s0.p0.y, s0.p1.y)], s0); + /** + * @private + * @param {number} x X coordinate. + * @param {number} y Y coordinate. + * @param {number} i Index. + * @return {module:ol/render/webgl/PolygonReplay~PolygonVertex} List item. + */ + createPoint_(x, y, i) { + let numVertices = this.vertices.length; + this.vertices[numVertices++] = x; + this.vertices[numVertices++] = y; + /** @type {module:ol/render/webgl/PolygonReplay~PolygonVertex} */ + const p = { + x: x, + y: y, + i: i, + reflex: undefined + }; + return p; } -}; + /** + * @private + * @param {module:ol/render/webgl/PolygonReplay~PolygonVertex} p0 First point of segment. + * @param {module:ol/render/webgl/PolygonReplay~PolygonVertex} p1 Second point of segment. + * @param {module:ol/structs/LinkedList} list Polygon ring. + * @param {module:ol/structs/RBush=} opt_rtree Insert the segment into the R-Tree. + * @return {module:ol/render/webgl/PolygonReplay~PolygonSegment} segment. + */ + insertItem_(p0, p1, list, opt_rtree) { + const seg = { + p0: p0, + p1: p1 + }; + list.insertItem(seg); + if (opt_rtree) { + opt_rtree.insert([Math.min(p0.x, p1.x), Math.min(p0.y, p1.y), + Math.max(p0.x, p1.x), Math.max(p0.y, p1.y)], seg); + } + return seg; + } -/** - * @private - * @param {module:ol/render/webgl/PolygonReplay~PolygonVertex} p0 First point. - * @param {module:ol/render/webgl/PolygonReplay~PolygonVertex} p1 Second point. - * @param {module:ol/render/webgl/PolygonReplay~PolygonVertex} p2 Third point. - * @param {module:ol/structs/RBush} rtree R-Tree of the polygon. - * @param {boolean=} opt_reflex Only include reflex points. - * @return {Array.} Points in the triangle. - */ -WebGLPolygonReplay.prototype.getPointsInTriangle_ = function(p0, p1, p2, rtree, opt_reflex) { - const result = []; - const segmentsInExtent = rtree.getInExtent([Math.min(p0.x, p1.x, p2.x), - Math.min(p0.y, p1.y, p2.y), Math.max(p0.x, p1.x, p2.x), Math.max(p0.y, - p1.y, p2.y)]); - for (let i = 0, ii = segmentsInExtent.length; i < ii; ++i) { - for (const j in segmentsInExtent[i]) { - const p = segmentsInExtent[i][j]; - if (typeof p === 'object' && (!opt_reflex || p.reflex)) { - if ((p.x !== p0.x || p.y !== p0.y) && (p.x !== p1.x || p.y !== p1.y) && - (p.x !== p2.x || p.y !== p2.y) && result.indexOf(p) === -1 && - linearRingContainsXY([p0.x, p0.y, p1.x, p1.y, p2.x, p2.y], 0, 6, 2, p.x, p.y)) { - result.push(p); + /** + * @private + * @param {module:ol/render/webgl/PolygonReplay~PolygonSegment} s0 Segment before the remove candidate. + * @param {module:ol/render/webgl/PolygonReplay~PolygonSegment} s1 Remove candidate segment. + * @param {module:ol/structs/LinkedList} list Polygon ring. + * @param {module:ol/structs/RBush} rtree R-Tree of the polygon. + */ + removeItem_(s0, s1, list, rtree) { + if (list.getCurrItem() === s1) { + list.removeItem(); + s0.p1 = s1.p1; + rtree.remove(s1); + rtree.update([Math.min(s0.p0.x, s0.p1.x), Math.min(s0.p0.y, s0.p1.y), + Math.max(s0.p0.x, s0.p1.x), Math.max(s0.p0.y, s0.p1.y)], s0); + } + } + + /** + * @private + * @param {module:ol/render/webgl/PolygonReplay~PolygonVertex} p0 First point. + * @param {module:ol/render/webgl/PolygonReplay~PolygonVertex} p1 Second point. + * @param {module:ol/render/webgl/PolygonReplay~PolygonVertex} p2 Third point. + * @param {module:ol/structs/RBush} rtree R-Tree of the polygon. + * @param {boolean=} opt_reflex Only include reflex points. + * @return {Array.} Points in the triangle. + */ + getPointsInTriangle_(p0, p1, p2, rtree, opt_reflex) { + const result = []; + const segmentsInExtent = rtree.getInExtent([Math.min(p0.x, p1.x, p2.x), + Math.min(p0.y, p1.y, p2.y), Math.max(p0.x, p1.x, p2.x), Math.max(p0.y, + p1.y, p2.y)]); + for (let i = 0, ii = segmentsInExtent.length; i < ii; ++i) { + for (const j in segmentsInExtent[i]) { + const p = segmentsInExtent[i][j]; + if (typeof p === 'object' && (!opt_reflex || p.reflex)) { + if ((p.x !== p0.x || p.y !== p0.y) && (p.x !== p1.x || p.y !== p1.y) && + (p.x !== p2.x || p.y !== p2.y) && result.indexOf(p) === -1 && + linearRingContainsXY([p0.x, p0.y, p1.x, p1.y, p2.x, p2.y], 0, 6, 2, p.x, p.y)) { + result.push(p); + } } } } + return result; } - return result; -}; + /** + * @private + * @param {module:ol/render/webgl/PolygonReplay~PolygonSegment} segment Segment. + * @param {module:ol/structs/RBush} rtree R-Tree of the polygon. + * @param {boolean=} opt_touch Touching segments should be considered an intersection. + * @return {Array.} Intersecting segments. + */ + getIntersections_(segment, rtree, opt_touch) { + const p0 = segment.p0; + const p1 = segment.p1; + const segmentsInExtent = rtree.getInExtent([Math.min(p0.x, p1.x), + Math.min(p0.y, p1.y), Math.max(p0.x, p1.x), Math.max(p0.y, p1.y)]); + const result = []; + for (let i = 0, ii = segmentsInExtent.length; i < ii; ++i) { + const currSeg = segmentsInExtent[i]; + if (segment !== currSeg && (opt_touch || currSeg.p0 !== p1 || currSeg.p1 !== p0) && + this.calculateIntersection_(p0, p1, currSeg.p0, currSeg.p1, opt_touch)) { + result.push(currSeg); + } + } + return result; + } -/** - * @private - * @param {module:ol/render/webgl/PolygonReplay~PolygonSegment} segment Segment. - * @param {module:ol/structs/RBush} rtree R-Tree of the polygon. - * @param {boolean=} opt_touch Touching segments should be considered an intersection. - * @return {Array.} Intersecting segments. - */ -WebGLPolygonReplay.prototype.getIntersections_ = function(segment, rtree, opt_touch) { - const p0 = segment.p0; - const p1 = segment.p1; - const segmentsInExtent = rtree.getInExtent([Math.min(p0.x, p1.x), - Math.min(p0.y, p1.y), Math.max(p0.x, p1.x), Math.max(p0.y, p1.y)]); - const result = []; - for (let i = 0, ii = segmentsInExtent.length; i < ii; ++i) { - const currSeg = segmentsInExtent[i]; - if (segment !== currSeg && (opt_touch || currSeg.p0 !== p1 || currSeg.p1 !== p0) && - this.calculateIntersection_(p0, p1, currSeg.p0, currSeg.p1, opt_touch)) { - result.push(currSeg); + /** + * Line intersection algorithm by Paul Bourke. + * @see http://paulbourke.net/geometry/pointlineplane/ + * + * @private + * @param {module:ol/render/webgl/PolygonReplay~PolygonVertex} p0 First point. + * @param {module:ol/render/webgl/PolygonReplay~PolygonVertex} p1 Second point. + * @param {module:ol/render/webgl/PolygonReplay~PolygonVertex} p2 Third point. + * @param {module:ol/render/webgl/PolygonReplay~PolygonVertex} p3 Fourth point. + * @param {boolean=} opt_touch Touching segments should be considered an intersection. + * @return {Array.|undefined} Intersection coordinates. + */ + calculateIntersection_(p0, p1, p2, p3, opt_touch) { + const denom = (p3.y - p2.y) * (p1.x - p0.x) - (p3.x - p2.x) * (p1.y - p0.y); + if (denom !== 0) { + const ua = ((p3.x - p2.x) * (p0.y - p2.y) - (p3.y - p2.y) * (p0.x - p2.x)) / denom; + const ub = ((p1.x - p0.x) * (p0.y - p2.y) - (p1.y - p0.y) * (p0.x - p2.x)) / denom; + if ((!opt_touch && ua > EPSILON && ua < 1 - EPSILON && + ub > EPSILON && ub < 1 - EPSILON) || (opt_touch && + ua >= 0 && ua <= 1 && ub >= 0 && ub <= 1)) { + return [p0.x + ua * (p1.x - p0.x), p0.y + ua * (p1.y - p0.y)]; + } + } + return undefined; + } + + /** + * @private + * @param {module:ol/render/webgl/PolygonReplay~PolygonVertex} p0 Point before the start of the diagonal. + * @param {module:ol/render/webgl/PolygonReplay~PolygonVertex} p1 Start point of the diagonal. + * @param {module:ol/render/webgl/PolygonReplay~PolygonVertex} p2 Ear candidate. + * @param {module:ol/render/webgl/PolygonReplay~PolygonVertex} p3 End point of the diagonal. + * @param {module:ol/render/webgl/PolygonReplay~PolygonVertex} p4 Point after the end of the diagonal. + * @return {boolean} Diagonal is inside the polygon. + */ + diagonalIsInside_(p0, p1, p2, p3, p4) { + if (p1.reflex === undefined || p3.reflex === undefined) { + return false; + } + const p1IsLeftOf = (p2.x - p3.x) * (p1.y - p3.y) > (p2.y - p3.y) * (p1.x - p3.x); + const p1IsRightOf = (p4.x - p3.x) * (p1.y - p3.y) < (p4.y - p3.y) * (p1.x - p3.x); + const p3IsLeftOf = (p0.x - p1.x) * (p3.y - p1.y) > (p0.y - p1.y) * (p3.x - p1.x); + const p3IsRightOf = (p2.x - p1.x) * (p3.y - p1.y) < (p2.y - p1.y) * (p3.x - p1.x); + const p1InCone = p3.reflex ? p1IsRightOf || p1IsLeftOf : p1IsRightOf && p1IsLeftOf; + const p3InCone = p1.reflex ? p3IsRightOf || p3IsLeftOf : p3IsRightOf && p3IsLeftOf; + return p1InCone && p3InCone; + } + + /** + * @inheritDoc + */ + drawMultiPolygon(multiPolygonGeometry, feature) { + const endss = multiPolygonGeometry.getEndss(); + const stride = multiPolygonGeometry.getStride(); + const currIndex = this.indices.length; + const currLineIndex = this.lineStringReplay.getCurrentIndex(); + const flatCoordinates = multiPolygonGeometry.getFlatCoordinates(); + let i, ii, j, jj; + let start = 0; + for (i = 0, ii = endss.length; i < ii; ++i) { + const ends = endss[i]; + if (ends.length > 0) { + const outerRing = translate(flatCoordinates, start, ends[0], + stride, -this.origin[0], -this.origin[1]); + if (outerRing.length) { + const holes = []; + let holeFlatCoords; + for (j = 1, jj = ends.length; j < jj; ++j) { + if (ends[j] !== ends[j - 1]) { + holeFlatCoords = translate(flatCoordinates, ends[j - 1], + ends[j], stride, -this.origin[0], -this.origin[1]); + holes.push(holeFlatCoords); + } + } + this.lineStringReplay.drawPolygonCoordinates(outerRing, holes, stride); + this.drawCoordinates_(outerRing, holes, stride); + } + } + start = ends[ends.length - 1]; + } + if (this.indices.length > currIndex) { + this.startIndices.push(currIndex); + this.startIndicesFeature.push(feature); + if (this.state_.changed) { + this.styleIndices_.push(currIndex); + this.state_.changed = false; + } + } + if (this.lineStringReplay.getCurrentIndex() > currLineIndex) { + this.lineStringReplay.setPolygonStyle(feature, currLineIndex); } } - return result; -}; - -/** - * Line intersection algorithm by Paul Bourke. - * @see http://paulbourke.net/geometry/pointlineplane/ - * - * @private - * @param {module:ol/render/webgl/PolygonReplay~PolygonVertex} p0 First point. - * @param {module:ol/render/webgl/PolygonReplay~PolygonVertex} p1 Second point. - * @param {module:ol/render/webgl/PolygonReplay~PolygonVertex} p2 Third point. - * @param {module:ol/render/webgl/PolygonReplay~PolygonVertex} p3 Fourth point. - * @param {boolean=} opt_touch Touching segments should be considered an intersection. - * @return {Array.|undefined} Intersection coordinates. - */ -WebGLPolygonReplay.prototype.calculateIntersection_ = function(p0, p1, p2, p3, opt_touch) { - const denom = (p3.y - p2.y) * (p1.x - p0.x) - (p3.x - p2.x) * (p1.y - p0.y); - if (denom !== 0) { - const ua = ((p3.x - p2.x) * (p0.y - p2.y) - (p3.y - p2.y) * (p0.x - p2.x)) / denom; - const ub = ((p1.x - p0.x) * (p0.y - p2.y) - (p1.y - p0.y) * (p0.x - p2.x)) / denom; - if ((!opt_touch && ua > EPSILON && ua < 1 - EPSILON && - ub > EPSILON && ub < 1 - EPSILON) || (opt_touch && - ua >= 0 && ua <= 1 && ub >= 0 && ub <= 1)) { - return [p0.x + ua * (p1.x - p0.x), p0.y + ua * (p1.y - p0.y)]; - } - } - return undefined; -}; - - -/** - * @private - * @param {module:ol/render/webgl/PolygonReplay~PolygonVertex} p0 Point before the start of the diagonal. - * @param {module:ol/render/webgl/PolygonReplay~PolygonVertex} p1 Start point of the diagonal. - * @param {module:ol/render/webgl/PolygonReplay~PolygonVertex} p2 Ear candidate. - * @param {module:ol/render/webgl/PolygonReplay~PolygonVertex} p3 End point of the diagonal. - * @param {module:ol/render/webgl/PolygonReplay~PolygonVertex} p4 Point after the end of the diagonal. - * @return {boolean} Diagonal is inside the polygon. - */ -WebGLPolygonReplay.prototype.diagonalIsInside_ = function(p0, p1, p2, p3, p4) { - if (p1.reflex === undefined || p3.reflex === undefined) { - return false; - } - const p1IsLeftOf = (p2.x - p3.x) * (p1.y - p3.y) > (p2.y - p3.y) * (p1.x - p3.x); - const p1IsRightOf = (p4.x - p3.x) * (p1.y - p3.y) < (p4.y - p3.y) * (p1.x - p3.x); - const p3IsLeftOf = (p0.x - p1.x) * (p3.y - p1.y) > (p0.y - p1.y) * (p3.x - p1.x); - const p3IsRightOf = (p2.x - p1.x) * (p3.y - p1.y) < (p2.y - p1.y) * (p3.x - p1.x); - const p1InCone = p3.reflex ? p1IsRightOf || p1IsLeftOf : p1IsRightOf && p1IsLeftOf; - const p3InCone = p1.reflex ? p3IsRightOf || p3IsLeftOf : p3IsRightOf && p3IsLeftOf; - return p1InCone && p3InCone; -}; - - -/** - * @inheritDoc - */ -WebGLPolygonReplay.prototype.drawMultiPolygon = function(multiPolygonGeometry, feature) { - const endss = multiPolygonGeometry.getEndss(); - const stride = multiPolygonGeometry.getStride(); - const currIndex = this.indices.length; - const currLineIndex = this.lineStringReplay.getCurrentIndex(); - const flatCoordinates = multiPolygonGeometry.getFlatCoordinates(); - let i, ii, j, jj; - let start = 0; - for (i = 0, ii = endss.length; i < ii; ++i) { - const ends = endss[i]; + /** + * @inheritDoc + */ + drawPolygon(polygonGeometry, feature) { + const ends = polygonGeometry.getEnds(); + const stride = polygonGeometry.getStride(); if (ends.length > 0) { - const outerRing = translate(flatCoordinates, start, ends[0], + const flatCoordinates = polygonGeometry.getFlatCoordinates().map(Number); + const outerRing = translate(flatCoordinates, 0, ends[0], stride, -this.origin[0], -this.origin[1]); if (outerRing.length) { const holes = []; - let holeFlatCoords; - for (j = 1, jj = ends.length; j < jj; ++j) { - if (ends[j] !== ends[j - 1]) { - holeFlatCoords = translate(flatCoordinates, ends[j - 1], - ends[j], stride, -this.origin[0], -this.origin[1]); + let i, ii, holeFlatCoords; + for (i = 1, ii = ends.length; i < ii; ++i) { + if (ends[i] !== ends[i - 1]) { + holeFlatCoords = translate(flatCoordinates, ends[i - 1], + ends[i], stride, -this.origin[0], -this.origin[1]); holes.push(holeFlatCoords); } } + + this.startIndices.push(this.indices.length); + this.startIndicesFeature.push(feature); + if (this.state_.changed) { + this.styleIndices_.push(this.indices.length); + this.state_.changed = false; + } + this.lineStringReplay.setPolygonStyle(feature); + this.lineStringReplay.drawPolygonCoordinates(outerRing, holes, stride); this.drawCoordinates_(outerRing, holes, stride); } } - start = ends[ends.length - 1]; } - if (this.indices.length > currIndex) { - this.startIndices.push(currIndex); - this.startIndicesFeature.push(feature); - if (this.state_.changed) { - this.styleIndices_.push(currIndex); - this.state_.changed = false; + + /** + * @inheritDoc + **/ + finish(context) { + // create, bind, and populate the vertices buffer + this.verticesBuffer = new WebGLBuffer(this.vertices); + + // create, bind, and populate the indices buffer + this.indicesBuffer = new WebGLBuffer(this.indices); + + this.startIndices.push(this.indices.length); + + this.lineStringReplay.finish(context); + + //Clean up, if there is nothing to draw + if (this.styleIndices_.length === 0 && this.styles_.length > 0) { + this.styles_ = []; + } + + this.vertices = null; + this.indices = null; + } + + /** + * @inheritDoc + */ + getDeleteResourcesFunction(context) { + const verticesBuffer = this.verticesBuffer; + const indicesBuffer = this.indicesBuffer; + const lineDeleter = this.lineStringReplay.getDeleteResourcesFunction(context); + return function() { + context.deleteBuffer(verticesBuffer); + context.deleteBuffer(indicesBuffer); + lineDeleter(); + }; + } + + /** + * @inheritDoc + */ + setUpProgram(gl, context, size, pixelRatio) { + // get the program + const program = context.getProgram(fragment, vertex); + + // get the locations + let locations; + if (!this.defaultLocations_) { + locations = new Locations(gl, program); + this.defaultLocations_ = locations; + } else { + locations = this.defaultLocations_; + } + + context.useProgram(program); + + // enable the vertex attrib arrays + gl.enableVertexAttribArray(locations.a_position); + gl.vertexAttribPointer(locations.a_position, 2, FLOAT, + false, 8, 0); + + return locations; + } + + /** + * @inheritDoc + */ + shutDownProgram(gl, locations) { + gl.disableVertexAttribArray(locations.a_position); + } + + /** + * @inheritDoc + */ + drawReplay(gl, context, skippedFeaturesHash, hitDetection) { + //Save GL parameters. + const tmpDepthFunc = /** @type {number} */ (gl.getParameter(gl.DEPTH_FUNC)); + const tmpDepthMask = /** @type {boolean} */ (gl.getParameter(gl.DEPTH_WRITEMASK)); + + if (!hitDetection) { + gl.enable(gl.DEPTH_TEST); + gl.depthMask(true); + gl.depthFunc(gl.NOTEQUAL); + } + + if (!isEmpty(skippedFeaturesHash)) { + this.drawReplaySkipping_(gl, context, skippedFeaturesHash); + } else { + //Draw by style groups to minimize drawElements() calls. + let i, start, end, nextStyle; + end = this.startIndices[this.startIndices.length - 1]; + for (i = this.styleIndices_.length - 1; i >= 0; --i) { + start = this.styleIndices_[i]; + nextStyle = this.styles_[i]; + this.setFillStyle_(gl, nextStyle); + this.drawElements(gl, context, start, end); + end = start; + } + } + if (!hitDetection) { + gl.disable(gl.DEPTH_TEST); + gl.clear(gl.DEPTH_BUFFER_BIT); + //Restore GL parameters. + gl.depthMask(tmpDepthMask); + gl.depthFunc(tmpDepthFunc); } } - if (this.lineStringReplay.getCurrentIndex() > currLineIndex) { - this.lineStringReplay.setPolygonStyle(feature, currLineIndex); - } -}; - -/** - * @inheritDoc - */ -WebGLPolygonReplay.prototype.drawPolygon = function(polygonGeometry, feature) { - const ends = polygonGeometry.getEnds(); - const stride = polygonGeometry.getStride(); - if (ends.length > 0) { - const flatCoordinates = polygonGeometry.getFlatCoordinates().map(Number); - const outerRing = translate(flatCoordinates, 0, ends[0], - stride, -this.origin[0], -this.origin[1]); - if (outerRing.length) { - const holes = []; - let i, ii, holeFlatCoords; - for (i = 1, ii = ends.length; i < ii; ++i) { - if (ends[i] !== ends[i - 1]) { - holeFlatCoords = translate(flatCoordinates, ends[i - 1], - ends[i], stride, -this.origin[0], -this.origin[1]); - holes.push(holeFlatCoords); - } - } - - this.startIndices.push(this.indices.length); - this.startIndicesFeature.push(feature); - if (this.state_.changed) { - this.styleIndices_.push(this.indices.length); - this.state_.changed = false; - } - this.lineStringReplay.setPolygonStyle(feature); - - this.lineStringReplay.drawPolygonCoordinates(outerRing, holes, stride); - this.drawCoordinates_(outerRing, holes, stride); - } - } -}; - - -/** - * @inheritDoc - **/ -WebGLPolygonReplay.prototype.finish = function(context) { - // create, bind, and populate the vertices buffer - this.verticesBuffer = new WebGLBuffer(this.vertices); - - // create, bind, and populate the indices buffer - this.indicesBuffer = new WebGLBuffer(this.indices); - - this.startIndices.push(this.indices.length); - - this.lineStringReplay.finish(context); - - //Clean up, if there is nothing to draw - if (this.styleIndices_.length === 0 && this.styles_.length > 0) { - this.styles_ = []; - } - - this.vertices = null; - this.indices = null; -}; - - -/** - * @inheritDoc - */ -WebGLPolygonReplay.prototype.getDeleteResourcesFunction = function(context) { - const verticesBuffer = this.verticesBuffer; - const indicesBuffer = this.indicesBuffer; - const lineDeleter = this.lineStringReplay.getDeleteResourcesFunction(context); - return function() { - context.deleteBuffer(verticesBuffer); - context.deleteBuffer(indicesBuffer); - lineDeleter(); - }; -}; - - -/** - * @inheritDoc - */ -WebGLPolygonReplay.prototype.setUpProgram = function(gl, context, size, pixelRatio) { - // get the program - const program = context.getProgram(fragment, vertex); - - // get the locations - let locations; - if (!this.defaultLocations_) { - locations = new Locations(gl, program); - this.defaultLocations_ = locations; - } else { - locations = this.defaultLocations_; - } - - context.useProgram(program); - - // enable the vertex attrib arrays - gl.enableVertexAttribArray(locations.a_position); - gl.vertexAttribPointer(locations.a_position, 2, FLOAT, - false, 8, 0); - - return locations; -}; - - -/** - * @inheritDoc - */ -WebGLPolygonReplay.prototype.shutDownProgram = function(gl, locations) { - gl.disableVertexAttribArray(locations.a_position); -}; - - -/** - * @inheritDoc - */ -WebGLPolygonReplay.prototype.drawReplay = function(gl, context, skippedFeaturesHash, hitDetection) { - //Save GL parameters. - const tmpDepthFunc = /** @type {number} */ (gl.getParameter(gl.DEPTH_FUNC)); - const tmpDepthMask = /** @type {boolean} */ (gl.getParameter(gl.DEPTH_WRITEMASK)); - - if (!hitDetection) { - gl.enable(gl.DEPTH_TEST); - gl.depthMask(true); - gl.depthFunc(gl.NOTEQUAL); - } - - if (!isEmpty(skippedFeaturesHash)) { - this.drawReplaySkipping_(gl, context, skippedFeaturesHash); - } else { - //Draw by style groups to minimize drawElements() calls. - let i, start, end, nextStyle; - end = this.startIndices[this.startIndices.length - 1]; + /** + * @inheritDoc + */ + drawHitDetectionReplayOneByOne(gl, context, skippedFeaturesHash, featureCallback, opt_hitExtent) { + let i, start, end, nextStyle, groupStart, feature, featureUid, featureIndex; + featureIndex = this.startIndices.length - 2; + end = this.startIndices[featureIndex + 1]; for (i = this.styleIndices_.length - 1; i >= 0; --i) { - start = this.styleIndices_[i]; nextStyle = this.styles_[i]; this.setFillStyle_(gl, nextStyle); - this.drawElements(gl, context, start, end); - end = start; - } - } - if (!hitDetection) { - gl.disable(gl.DEPTH_TEST); - gl.clear(gl.DEPTH_BUFFER_BIT); - //Restore GL parameters. - gl.depthMask(tmpDepthMask); - gl.depthFunc(tmpDepthFunc); - } -}; + groupStart = this.styleIndices_[i]; + while (featureIndex >= 0 && + this.startIndices[featureIndex] >= groupStart) { + start = this.startIndices[featureIndex]; + feature = this.startIndicesFeature[featureIndex]; + featureUid = getUid(feature).toString(); -/** - * @inheritDoc - */ -WebGLPolygonReplay.prototype.drawHitDetectionReplayOneByOne = function(gl, context, skippedFeaturesHash, - featureCallback, opt_hitExtent) { - let i, start, end, nextStyle, groupStart, feature, featureUid, featureIndex; - featureIndex = this.startIndices.length - 2; - end = this.startIndices[featureIndex + 1]; - for (i = this.styleIndices_.length - 1; i >= 0; --i) { - nextStyle = this.styles_[i]; - this.setFillStyle_(gl, nextStyle); - groupStart = this.styleIndices_[i]; - - while (featureIndex >= 0 && - this.startIndices[featureIndex] >= groupStart) { - start = this.startIndices[featureIndex]; - feature = this.startIndicesFeature[featureIndex]; - featureUid = getUid(feature).toString(); - - if (skippedFeaturesHash[featureUid] === undefined && - feature.getGeometry() && - (opt_hitExtent === undefined || intersects( - /** @type {Array} */ (opt_hitExtent), - feature.getGeometry().getExtent()))) { - gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); - this.drawElements(gl, context, start, end); - - const result = featureCallback(feature); - - if (result) { - return result; - } - - } - featureIndex--; - end = start; - } - } - return undefined; -}; - - -/** - * @private - * @param {WebGLRenderingContext} gl gl. - * @param {module:ol/webgl/Context} context Context. - * @param {Object} skippedFeaturesHash Ids of features to skip. - */ -WebGLPolygonReplay.prototype.drawReplaySkipping_ = function(gl, context, skippedFeaturesHash) { - let i, start, end, nextStyle, groupStart, feature, featureUid, featureIndex, featureStart; - featureIndex = this.startIndices.length - 2; - end = start = this.startIndices[featureIndex + 1]; - for (i = this.styleIndices_.length - 1; i >= 0; --i) { - nextStyle = this.styles_[i]; - this.setFillStyle_(gl, nextStyle); - groupStart = this.styleIndices_[i]; - - while (featureIndex >= 0 && - this.startIndices[featureIndex] >= groupStart) { - featureStart = this.startIndices[featureIndex]; - feature = this.startIndicesFeature[featureIndex]; - featureUid = getUid(feature).toString(); - - if (skippedFeaturesHash[featureUid]) { - if (start !== end) { + if (skippedFeaturesHash[featureUid] === undefined && + feature.getGeometry() && + (opt_hitExtent === undefined || intersects( + /** @type {Array} */ (opt_hitExtent), + feature.getGeometry().getExtent()))) { + gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); this.drawElements(gl, context, start, end); - gl.clear(gl.DEPTH_BUFFER_BIT); + + const result = featureCallback(feature); + + if (result) { + return result; + } + } - end = featureStart; + featureIndex--; + end = start; } - featureIndex--; - start = featureStart; } - if (start !== end) { - this.drawElements(gl, context, start, end); - gl.clear(gl.DEPTH_BUFFER_BIT); + return undefined; + } + + /** + * @private + * @param {WebGLRenderingContext} gl gl. + * @param {module:ol/webgl/Context} context Context. + * @param {Object} skippedFeaturesHash Ids of features to skip. + */ + drawReplaySkipping_(gl, context, skippedFeaturesHash) { + let i, start, end, nextStyle, groupStart, feature, featureUid, featureIndex, featureStart; + featureIndex = this.startIndices.length - 2; + end = start = this.startIndices[featureIndex + 1]; + for (i = this.styleIndices_.length - 1; i >= 0; --i) { + nextStyle = this.styles_[i]; + this.setFillStyle_(gl, nextStyle); + groupStart = this.styleIndices_[i]; + + while (featureIndex >= 0 && + this.startIndices[featureIndex] >= groupStart) { + featureStart = this.startIndices[featureIndex]; + feature = this.startIndicesFeature[featureIndex]; + featureUid = getUid(feature).toString(); + + if (skippedFeaturesHash[featureUid]) { + if (start !== end) { + this.drawElements(gl, context, start, end); + gl.clear(gl.DEPTH_BUFFER_BIT); + } + end = featureStart; + } + featureIndex--; + start = featureStart; + } + if (start !== end) { + this.drawElements(gl, context, start, end); + gl.clear(gl.DEPTH_BUFFER_BIT); + } + start = end = groupStart; } - start = end = groupStart; } -}; + + /** + * @private + * @param {WebGLRenderingContext} gl gl. + * @param {Array.} color Color. + */ + setFillStyle_(gl, color) { + gl.uniform4fv(this.defaultLocations_.u_color, color); + } + + /** + * @inheritDoc + */ + setFillStrokeStyle(fillStyle, strokeStyle) { + let fillStyleColor = fillStyle ? fillStyle.getColor() : [0, 0, 0, 0]; + if (!(fillStyleColor instanceof CanvasGradient) && + !(fillStyleColor instanceof CanvasPattern)) { + fillStyleColor = asArray(fillStyleColor).map(function(c, i) { + return i != 3 ? c / 255 : c; + }) || DEFAULT_FILLSTYLE; + } else { + fillStyleColor = DEFAULT_FILLSTYLE; + } + if (!this.state_.fillColor || !equals(fillStyleColor, this.state_.fillColor)) { + this.state_.fillColor = fillStyleColor; + this.state_.changed = true; + this.styles_.push(fillStyleColor); + } + //Provide a null stroke style, if no strokeStyle is provided. Required for the draw interaction to work. + if (strokeStyle) { + this.lineStringReplay.setFillStrokeStyle(null, strokeStyle); + } else { + const nullStrokeStyle = new Stroke({ + color: [0, 0, 0, 0], + lineWidth: 0 + }); + this.lineStringReplay.setFillStrokeStyle(null, nullStrokeStyle); + } + } +} + +inherits(WebGLPolygonReplay, WebGLReplay); -/** - * @private - * @param {WebGLRenderingContext} gl gl. - * @param {Array.} color Color. - */ -WebGLPolygonReplay.prototype.setFillStyle_ = function(gl, color) { - gl.uniform4fv(this.defaultLocations_.u_color, color); -}; - - -/** - * @inheritDoc - */ -WebGLPolygonReplay.prototype.setFillStrokeStyle = function(fillStyle, strokeStyle) { - let fillStyleColor = fillStyle ? fillStyle.getColor() : [0, 0, 0, 0]; - if (!(fillStyleColor instanceof CanvasGradient) && - !(fillStyleColor instanceof CanvasPattern)) { - fillStyleColor = asArray(fillStyleColor).map(function(c, i) { - return i != 3 ? c / 255 : c; - }) || DEFAULT_FILLSTYLE; - } else { - fillStyleColor = DEFAULT_FILLSTYLE; - } - if (!this.state_.fillColor || !equals(fillStyleColor, this.state_.fillColor)) { - this.state_.fillColor = fillStyleColor; - this.state_.changed = true; - this.styles_.push(fillStyleColor); - } - //Provide a null stroke style, if no strokeStyle is provided. Required for the draw interaction to work. - if (strokeStyle) { - this.lineStringReplay.setFillStrokeStyle(null, strokeStyle); - } else { - const nullStrokeStyle = new Stroke({ - color: [0, 0, 0, 0], - lineWidth: 0 - }); - this.lineStringReplay.setFillStrokeStyle(null, nullStrokeStyle); - } -}; export default WebGLPolygonReplay; diff --git a/src/ol/render/webgl/Replay.js b/src/ol/render/webgl/Replay.js index 339b8eeefe..9f82854f69 100644 --- a/src/ol/render/webgl/Replay.js +++ b/src/ol/render/webgl/Replay.js @@ -23,342 +23,343 @@ import {ARRAY_BUFFER, ELEMENT_ARRAY_BUFFER, TRIANGLES, * @param {module:ol/extent~Extent} maxExtent Max extent. * @struct */ -const WebGLReplay = function(tolerance, maxExtent) { - VectorContext.call(this); +class WebGLReplay { + constructor(tolerance, maxExtent) { + VectorContext.call(this); - /** - * @protected - * @type {number} - */ - this.tolerance = tolerance; + /** + * @protected + * @type {number} + */ + this.tolerance = tolerance; - /** - * @protected - * @const - * @type {module:ol/extent~Extent} - */ - this.maxExtent = maxExtent; + /** + * @protected + * @const + * @type {module:ol/extent~Extent} + */ + this.maxExtent = maxExtent; - /** - * The origin of the coordinate system for the point coordinates sent to - * the GPU. To eliminate jitter caused by precision problems in the GPU - * we use the "Rendering Relative to Eye" technique described in the "3D - * Engine Design for Virtual Globes" book. - * @protected - * @type {module:ol/coordinate~Coordinate} - */ - this.origin = getCenter(maxExtent); + /** + * The origin of the coordinate system for the point coordinates sent to + * the GPU. To eliminate jitter caused by precision problems in the GPU + * we use the "Rendering Relative to Eye" technique described in the "3D + * Engine Design for Virtual Globes" book. + * @protected + * @type {module:ol/coordinate~Coordinate} + */ + this.origin = getCenter(maxExtent); - /** - * @private - * @type {module:ol/transform~Transform} - */ - this.projectionMatrix_ = createTransform(); + /** + * @private + * @type {module:ol/transform~Transform} + */ + this.projectionMatrix_ = createTransform(); - /** - * @private - * @type {module:ol/transform~Transform} - */ - this.offsetRotateMatrix_ = createTransform(); + /** + * @private + * @type {module:ol/transform~Transform} + */ + this.offsetRotateMatrix_ = createTransform(); - /** - * @private - * @type {module:ol/transform~Transform} - */ - this.offsetScaleMatrix_ = createTransform(); + /** + * @private + * @type {module:ol/transform~Transform} + */ + this.offsetScaleMatrix_ = createTransform(); - /** - * @private - * @type {Array.} - */ - this.tmpMat4_ = create(); + /** + * @private + * @type {Array.} + */ + this.tmpMat4_ = create(); - /** - * @protected - * @type {Array.} - */ - this.indices = []; + /** + * @protected + * @type {Array.} + */ + this.indices = []; - /** - * @protected - * @type {?module:ol/webgl/Buffer} - */ - this.indicesBuffer = null; + /** + * @protected + * @type {?module:ol/webgl/Buffer} + */ + this.indicesBuffer = null; - /** - * Start index per feature (the index). - * @protected - * @type {Array.} - */ - this.startIndices = []; + /** + * Start index per feature (the index). + * @protected + * @type {Array.} + */ + this.startIndices = []; - /** - * Start index per feature (the feature). - * @protected - * @type {Array.} - */ - this.startIndicesFeature = []; + /** + * Start index per feature (the feature). + * @protected + * @type {Array.} + */ + this.startIndicesFeature = []; - /** - * @protected - * @type {Array.} - */ - this.vertices = []; + /** + * @protected + * @type {Array.} + */ + this.vertices = []; - /** - * @protected - * @type {?module:ol/webgl/Buffer} - */ - this.verticesBuffer = null; + /** + * @protected + * @type {?module:ol/webgl/Buffer} + */ + this.verticesBuffer = null; - /** - * Optional parameter for PolygonReplay instances. - * @protected - * @type {module:ol/render/webgl/LineStringReplay|undefined} - */ - this.lineStringReplay = undefined; + /** + * Optional parameter for PolygonReplay instances. + * @protected + * @type {module:ol/render/webgl/LineStringReplay|undefined} + */ + this.lineStringReplay = undefined; -}; + } + + /** + * @abstract + * @param {module:ol/webgl/Context} context WebGL context. + * @return {function()} Delete resources function. + */ + getDeleteResourcesFunction(context) {} + + /** + * @abstract + * @param {module:ol/webgl/Context} context Context. + */ + finish(context) {} + + /** + * @abstract + * @protected + * @param {WebGLRenderingContext} gl gl. + * @param {module:ol/webgl/Context} context Context. + * @param {module:ol/size~Size} size Size. + * @param {number} pixelRatio Pixel ratio. + * @return {module:ol/render/webgl/circlereplay/defaultshader/Locations| + module:ol/render/webgl/linestringreplay/defaultshader/Locations| + module:ol/render/webgl/polygonreplay/defaultshader/Locations| + module:ol/render/webgl/texturereplay/defaultshader/Locations} Locations. + */ + setUpProgram(gl, context, size, pixelRatio) {} + + /** + * @abstract + * @protected + * @param {WebGLRenderingContext} gl gl. + * @param {module:ol/render/webgl/circlereplay/defaultshader/Locations| + module:ol/render/webgl/linestringreplay/defaultshader/Locations| + module:ol/render/webgl/polygonreplay/defaultshader/Locations| + module:ol/render/webgl/texturereplay/defaultshader/Locations} locations Locations. + */ + shutDownProgram(gl, locations) {} + + /** + * @abstract + * @protected + * @param {WebGLRenderingContext} gl gl. + * @param {module:ol/webgl/Context} context Context. + * @param {Object.} skippedFeaturesHash Ids of features to skip. + * @param {boolean} hitDetection Hit detection mode. + */ + drawReplay(gl, context, skippedFeaturesHash, hitDetection) {} + + /** + * @abstract + * @protected + * @param {WebGLRenderingContext} gl gl. + * @param {module:ol/webgl/Context} context Context. + * @param {Object.} skippedFeaturesHash Ids of features to skip. + * @param {function((module:ol/Feature|module:ol/render/Feature)): T|undefined} featureCallback Feature callback. + * @param {module:ol/extent~Extent=} opt_hitExtent Hit extent: Only features intersecting this extent are checked. + * @return {T|undefined} Callback result. + * @template T + */ + drawHitDetectionReplayOneByOne(gl, context, skippedFeaturesHash, featureCallback, opt_hitExtent) {} + + /** + * @protected + * @param {WebGLRenderingContext} gl gl. + * @param {module:ol/webgl/Context} context Context. + * @param {Object.} skippedFeaturesHash Ids of features to skip. + * @param {function((module:ol/Feature|module:ol/render/Feature)): T|undefined} featureCallback Feature callback. + * @param {boolean} oneByOne Draw features one-by-one for the hit-detecion. + * @param {module:ol/extent~Extent=} opt_hitExtent Hit extent: Only features intersecting + * this extent are checked. + * @return {T|undefined} Callback result. + * @template T + */ + drawHitDetectionReplay(gl, context, skippedFeaturesHash, featureCallback, oneByOne, opt_hitExtent) { + if (!oneByOne) { + // draw all hit-detection features in "once" (by texture group) + return this.drawHitDetectionReplayAll(gl, context, + skippedFeaturesHash, featureCallback); + } else { + // draw hit-detection features one by one + return this.drawHitDetectionReplayOneByOne(gl, context, + skippedFeaturesHash, featureCallback, opt_hitExtent); + } + } + + /** + * @protected + * @param {WebGLRenderingContext} gl gl. + * @param {module:ol/webgl/Context} context Context. + * @param {Object.} skippedFeaturesHash Ids of features to skip. + * @param {function((module:ol/Feature|module:ol/render/Feature)): T|undefined} featureCallback Feature callback. + * @return {T|undefined} Callback result. + * @template T + */ + drawHitDetectionReplayAll(gl, context, skippedFeaturesHash, featureCallback) { + gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); + this.drawReplay(gl, context, skippedFeaturesHash, true); + + const result = featureCallback(null); + if (result) { + return result; + } else { + return undefined; + } + } + + /** + * @param {module:ol/webgl/Context} context Context. + * @param {module:ol/coordinate~Coordinate} center Center. + * @param {number} resolution Resolution. + * @param {number} rotation Rotation. + * @param {module:ol/size~Size} size Size. + * @param {number} pixelRatio Pixel ratio. + * @param {number} opacity Global opacity. + * @param {Object.} skippedFeaturesHash Ids of features to skip. + * @param {function((module:ol/Feature|module:ol/render/Feature)): T|undefined} featureCallback Feature callback. + * @param {boolean} oneByOne Draw features one-by-one for the hit-detecion. + * @param {module:ol/extent~Extent=} opt_hitExtent Hit extent: Only features intersecting + * this extent are checked. + * @return {T|undefined} Callback result. + * @template T + */ + replay( + context, + center, + resolution, + rotation, + size, + pixelRatio, + opacity, + skippedFeaturesHash, + featureCallback, + oneByOne, + opt_hitExtent + ) { + const gl = context.getGL(); + let tmpStencil, tmpStencilFunc, tmpStencilMaskVal, tmpStencilRef, tmpStencilMask, + tmpStencilOpFail, tmpStencilOpPass, tmpStencilOpZFail; + + if (this.lineStringReplay) { + tmpStencil = gl.isEnabled(gl.STENCIL_TEST); + tmpStencilFunc = gl.getParameter(gl.STENCIL_FUNC); + tmpStencilMaskVal = gl.getParameter(gl.STENCIL_VALUE_MASK); + tmpStencilRef = gl.getParameter(gl.STENCIL_REF); + tmpStencilMask = gl.getParameter(gl.STENCIL_WRITEMASK); + tmpStencilOpFail = gl.getParameter(gl.STENCIL_FAIL); + tmpStencilOpPass = gl.getParameter(gl.STENCIL_PASS_DEPTH_PASS); + tmpStencilOpZFail = gl.getParameter(gl.STENCIL_PASS_DEPTH_FAIL); + + gl.enable(gl.STENCIL_TEST); + gl.clear(gl.STENCIL_BUFFER_BIT); + gl.stencilMask(255); + gl.stencilFunc(gl.ALWAYS, 1, 255); + gl.stencilOp(gl.KEEP, gl.KEEP, gl.REPLACE); + + this.lineStringReplay.replay(context, + center, resolution, rotation, size, pixelRatio, + opacity, skippedFeaturesHash, + featureCallback, oneByOne, opt_hitExtent); + + gl.stencilMask(0); + gl.stencilFunc(gl.NOTEQUAL, 1, 255); + } + + context.bindBuffer(ARRAY_BUFFER, this.verticesBuffer); + + context.bindBuffer(ELEMENT_ARRAY_BUFFER, this.indicesBuffer); + + const locations = this.setUpProgram(gl, context, size, pixelRatio); + + // set the "uniform" values + const projectionMatrix = resetTransform(this.projectionMatrix_); + scaleTransform(projectionMatrix, 2 / (resolution * size[0]), 2 / (resolution * size[1])); + rotateTransform(projectionMatrix, -rotation); + translateTransform(projectionMatrix, -(center[0] - this.origin[0]), -(center[1] - this.origin[1])); + + const offsetScaleMatrix = resetTransform(this.offsetScaleMatrix_); + scaleTransform(offsetScaleMatrix, 2 / size[0], 2 / size[1]); + + const offsetRotateMatrix = resetTransform(this.offsetRotateMatrix_); + if (rotation !== 0) { + rotateTransform(offsetRotateMatrix, -rotation); + } + + gl.uniformMatrix4fv(locations.u_projectionMatrix, false, + fromTransform(this.tmpMat4_, projectionMatrix)); + gl.uniformMatrix4fv(locations.u_offsetScaleMatrix, false, + fromTransform(this.tmpMat4_, offsetScaleMatrix)); + gl.uniformMatrix4fv(locations.u_offsetRotateMatrix, false, + fromTransform(this.tmpMat4_, offsetRotateMatrix)); + gl.uniform1f(locations.u_opacity, opacity); + + // draw! + let result; + if (featureCallback === undefined) { + this.drawReplay(gl, context, skippedFeaturesHash, false); + } else { + // draw feature by feature for the hit-detection + result = this.drawHitDetectionReplay(gl, context, skippedFeaturesHash, + featureCallback, oneByOne, opt_hitExtent); + } + + // disable the vertex attrib arrays + this.shutDownProgram(gl, locations); + + if (this.lineStringReplay) { + if (!tmpStencil) { + gl.disable(gl.STENCIL_TEST); + } + gl.clear(gl.STENCIL_BUFFER_BIT); + gl.stencilFunc(/** @type {number} */ (tmpStencilFunc), + /** @type {number} */ (tmpStencilRef), /** @type {number} */ (tmpStencilMaskVal)); + gl.stencilMask(/** @type {number} */ (tmpStencilMask)); + gl.stencilOp(/** @type {number} */ (tmpStencilOpFail), + /** @type {number} */ (tmpStencilOpZFail), /** @type {number} */ (tmpStencilOpPass)); + } + + return result; + } + + /** + * @protected + * @param {WebGLRenderingContext} gl gl. + * @param {module:ol/webgl/Context} context Context. + * @param {number} start Start index. + * @param {number} end End index. + */ + drawElements(gl, context, start, end) { + const elementType = context.hasOESElementIndexUint ? + UNSIGNED_INT : UNSIGNED_SHORT; + const elementSize = context.hasOESElementIndexUint ? 4 : 2; + + const numItems = end - start; + const offsetInBytes = start * elementSize; + gl.drawElements(TRIANGLES, numItems, elementType, offsetInBytes); + } +} inherits(WebGLReplay, VectorContext); -/** - * @abstract - * @param {module:ol/webgl/Context} context WebGL context. - * @return {function()} Delete resources function. - */ -WebGLReplay.prototype.getDeleteResourcesFunction = function(context) {}; - - -/** - * @abstract - * @param {module:ol/webgl/Context} context Context. - */ -WebGLReplay.prototype.finish = function(context) {}; - - -/** - * @abstract - * @protected - * @param {WebGLRenderingContext} gl gl. - * @param {module:ol/webgl/Context} context Context. - * @param {module:ol/size~Size} size Size. - * @param {number} pixelRatio Pixel ratio. - * @return {module:ol/render/webgl/circlereplay/defaultshader/Locations| - module:ol/render/webgl/linestringreplay/defaultshader/Locations| - module:ol/render/webgl/polygonreplay/defaultshader/Locations| - module:ol/render/webgl/texturereplay/defaultshader/Locations} Locations. - */ -WebGLReplay.prototype.setUpProgram = function(gl, context, size, pixelRatio) {}; - - -/** - * @abstract - * @protected - * @param {WebGLRenderingContext} gl gl. - * @param {module:ol/render/webgl/circlereplay/defaultshader/Locations| - module:ol/render/webgl/linestringreplay/defaultshader/Locations| - module:ol/render/webgl/polygonreplay/defaultshader/Locations| - module:ol/render/webgl/texturereplay/defaultshader/Locations} locations Locations. - */ -WebGLReplay.prototype.shutDownProgram = function(gl, locations) {}; - - -/** - * @abstract - * @protected - * @param {WebGLRenderingContext} gl gl. - * @param {module:ol/webgl/Context} context Context. - * @param {Object.} skippedFeaturesHash Ids of features to skip. - * @param {boolean} hitDetection Hit detection mode. - */ -WebGLReplay.prototype.drawReplay = function(gl, context, skippedFeaturesHash, hitDetection) {}; - - -/** - * @abstract - * @protected - * @param {WebGLRenderingContext} gl gl. - * @param {module:ol/webgl/Context} context Context. - * @param {Object.} skippedFeaturesHash Ids of features to skip. - * @param {function((module:ol/Feature|module:ol/render/Feature)): T|undefined} featureCallback Feature callback. - * @param {module:ol/extent~Extent=} opt_hitExtent Hit extent: Only features intersecting this extent are checked. - * @return {T|undefined} Callback result. - * @template T - */ -WebGLReplay.prototype.drawHitDetectionReplayOneByOne = function(gl, context, skippedFeaturesHash, featureCallback, opt_hitExtent) {}; - - -/** - * @protected - * @param {WebGLRenderingContext} gl gl. - * @param {module:ol/webgl/Context} context Context. - * @param {Object.} skippedFeaturesHash Ids of features to skip. - * @param {function((module:ol/Feature|module:ol/render/Feature)): T|undefined} featureCallback Feature callback. - * @param {boolean} oneByOne Draw features one-by-one for the hit-detecion. - * @param {module:ol/extent~Extent=} opt_hitExtent Hit extent: Only features intersecting - * this extent are checked. - * @return {T|undefined} Callback result. - * @template T - */ -WebGLReplay.prototype.drawHitDetectionReplay = function(gl, context, skippedFeaturesHash, - featureCallback, oneByOne, opt_hitExtent) { - if (!oneByOne) { - // draw all hit-detection features in "once" (by texture group) - return this.drawHitDetectionReplayAll(gl, context, - skippedFeaturesHash, featureCallback); - } else { - // draw hit-detection features one by one - return this.drawHitDetectionReplayOneByOne(gl, context, - skippedFeaturesHash, featureCallback, opt_hitExtent); - } -}; - - -/** - * @protected - * @param {WebGLRenderingContext} gl gl. - * @param {module:ol/webgl/Context} context Context. - * @param {Object.} skippedFeaturesHash Ids of features to skip. - * @param {function((module:ol/Feature|module:ol/render/Feature)): T|undefined} featureCallback Feature callback. - * @return {T|undefined} Callback result. - * @template T - */ -WebGLReplay.prototype.drawHitDetectionReplayAll = function(gl, context, skippedFeaturesHash, - featureCallback) { - gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); - this.drawReplay(gl, context, skippedFeaturesHash, true); - - const result = featureCallback(null); - if (result) { - return result; - } else { - return undefined; - } -}; - - -/** - * @param {module:ol/webgl/Context} context Context. - * @param {module:ol/coordinate~Coordinate} center Center. - * @param {number} resolution Resolution. - * @param {number} rotation Rotation. - * @param {module:ol/size~Size} size Size. - * @param {number} pixelRatio Pixel ratio. - * @param {number} opacity Global opacity. - * @param {Object.} skippedFeaturesHash Ids of features to skip. - * @param {function((module:ol/Feature|module:ol/render/Feature)): T|undefined} featureCallback Feature callback. - * @param {boolean} oneByOne Draw features one-by-one for the hit-detecion. - * @param {module:ol/extent~Extent=} opt_hitExtent Hit extent: Only features intersecting - * this extent are checked. - * @return {T|undefined} Callback result. - * @template T - */ -WebGLReplay.prototype.replay = function(context, - center, resolution, rotation, size, pixelRatio, - opacity, skippedFeaturesHash, - featureCallback, oneByOne, opt_hitExtent) { - const gl = context.getGL(); - let tmpStencil, tmpStencilFunc, tmpStencilMaskVal, tmpStencilRef, tmpStencilMask, - tmpStencilOpFail, tmpStencilOpPass, tmpStencilOpZFail; - - if (this.lineStringReplay) { - tmpStencil = gl.isEnabled(gl.STENCIL_TEST); - tmpStencilFunc = gl.getParameter(gl.STENCIL_FUNC); - tmpStencilMaskVal = gl.getParameter(gl.STENCIL_VALUE_MASK); - tmpStencilRef = gl.getParameter(gl.STENCIL_REF); - tmpStencilMask = gl.getParameter(gl.STENCIL_WRITEMASK); - tmpStencilOpFail = gl.getParameter(gl.STENCIL_FAIL); - tmpStencilOpPass = gl.getParameter(gl.STENCIL_PASS_DEPTH_PASS); - tmpStencilOpZFail = gl.getParameter(gl.STENCIL_PASS_DEPTH_FAIL); - - gl.enable(gl.STENCIL_TEST); - gl.clear(gl.STENCIL_BUFFER_BIT); - gl.stencilMask(255); - gl.stencilFunc(gl.ALWAYS, 1, 255); - gl.stencilOp(gl.KEEP, gl.KEEP, gl.REPLACE); - - this.lineStringReplay.replay(context, - center, resolution, rotation, size, pixelRatio, - opacity, skippedFeaturesHash, - featureCallback, oneByOne, opt_hitExtent); - - gl.stencilMask(0); - gl.stencilFunc(gl.NOTEQUAL, 1, 255); - } - - context.bindBuffer(ARRAY_BUFFER, this.verticesBuffer); - - context.bindBuffer(ELEMENT_ARRAY_BUFFER, this.indicesBuffer); - - const locations = this.setUpProgram(gl, context, size, pixelRatio); - - // set the "uniform" values - const projectionMatrix = resetTransform(this.projectionMatrix_); - scaleTransform(projectionMatrix, 2 / (resolution * size[0]), 2 / (resolution * size[1])); - rotateTransform(projectionMatrix, -rotation); - translateTransform(projectionMatrix, -(center[0] - this.origin[0]), -(center[1] - this.origin[1])); - - const offsetScaleMatrix = resetTransform(this.offsetScaleMatrix_); - scaleTransform(offsetScaleMatrix, 2 / size[0], 2 / size[1]); - - const offsetRotateMatrix = resetTransform(this.offsetRotateMatrix_); - if (rotation !== 0) { - rotateTransform(offsetRotateMatrix, -rotation); - } - - gl.uniformMatrix4fv(locations.u_projectionMatrix, false, - fromTransform(this.tmpMat4_, projectionMatrix)); - gl.uniformMatrix4fv(locations.u_offsetScaleMatrix, false, - fromTransform(this.tmpMat4_, offsetScaleMatrix)); - gl.uniformMatrix4fv(locations.u_offsetRotateMatrix, false, - fromTransform(this.tmpMat4_, offsetRotateMatrix)); - gl.uniform1f(locations.u_opacity, opacity); - - // draw! - let result; - if (featureCallback === undefined) { - this.drawReplay(gl, context, skippedFeaturesHash, false); - } else { - // draw feature by feature for the hit-detection - result = this.drawHitDetectionReplay(gl, context, skippedFeaturesHash, - featureCallback, oneByOne, opt_hitExtent); - } - - // disable the vertex attrib arrays - this.shutDownProgram(gl, locations); - - if (this.lineStringReplay) { - if (!tmpStencil) { - gl.disable(gl.STENCIL_TEST); - } - gl.clear(gl.STENCIL_BUFFER_BIT); - gl.stencilFunc(/** @type {number} */ (tmpStencilFunc), - /** @type {number} */ (tmpStencilRef), /** @type {number} */ (tmpStencilMaskVal)); - gl.stencilMask(/** @type {number} */ (tmpStencilMask)); - gl.stencilOp(/** @type {number} */ (tmpStencilOpFail), - /** @type {number} */ (tmpStencilOpZFail), /** @type {number} */ (tmpStencilOpPass)); - } - - return result; -}; - -/** - * @protected - * @param {WebGLRenderingContext} gl gl. - * @param {module:ol/webgl/Context} context Context. - * @param {number} start Start index. - * @param {number} end End index. - */ -WebGLReplay.prototype.drawElements = function( - gl, context, start, end) { - const elementType = context.hasOESElementIndexUint ? - UNSIGNED_INT : UNSIGNED_SHORT; - const elementSize = context.hasOESElementIndexUint ? 4 : 2; - - const numItems = end - start; - const offsetInBytes = start * elementSize; - gl.drawElements(TRIANGLES, numItems, elementType, offsetInBytes); -}; export default WebGLReplay; diff --git a/src/ol/render/webgl/ReplayGroup.js b/src/ol/render/webgl/ReplayGroup.js index 1589f03193..3bf6cc030a 100644 --- a/src/ol/render/webgl/ReplayGroup.js +++ b/src/ol/render/webgl/ReplayGroup.js @@ -40,281 +40,308 @@ const BATCH_CONSTRUCTORS = { * @param {number=} opt_renderBuffer Render buffer. * @struct */ -const WebGLReplayGroup = function(tolerance, maxExtent, opt_renderBuffer) { - ReplayGroup.call(this); +class WebGLReplayGroup { + constructor(tolerance, maxExtent, opt_renderBuffer) { + ReplayGroup.call(this); + + /** + * @type {module:ol/extent~Extent} + * @private + */ + this.maxExtent_ = maxExtent; + + /** + * @type {number} + * @private + */ + this.tolerance_ = tolerance; + + /** + * @type {number|undefined} + * @private + */ + this.renderBuffer_ = opt_renderBuffer; + + /** + * @private + * @type {!Object.>} + */ + this.replaysByZIndex_ = {}; + + } /** - * @type {module:ol/extent~Extent} - * @private + * @param {module:ol/style/Style} style Style. + * @param {boolean} group Group with previous replay. */ - this.maxExtent_ = maxExtent; + addDeclutter(style, group) {} /** - * @type {number} - * @private + * @param {module:ol/webgl/Context} context WebGL context. + * @return {function()} Delete resources function. */ - this.tolerance_ = tolerance; + getDeleteResourcesFunction(context) { + const functions = []; + let zKey; + for (zKey in this.replaysByZIndex_) { + const replays = this.replaysByZIndex_[zKey]; + for (const replayKey in replays) { + functions.push( + replays[replayKey].getDeleteResourcesFunction(context)); + } + } + return function() { + const length = functions.length; + let result; + for (let i = 0; i < length; i++) { + result = functions[i].apply(this, arguments); + } + return result; + }; + } /** - * @type {number|undefined} - * @private + * @param {module:ol/webgl/Context} context Context. */ - this.renderBuffer_ = opt_renderBuffer; + finish(context) { + let zKey; + for (zKey in this.replaysByZIndex_) { + const replays = this.replaysByZIndex_[zKey]; + for (const replayKey in replays) { + replays[replayKey].finish(context); + } + } + } + + /** + * @inheritDoc + */ + getReplay(zIndex, replayType) { + const zIndexKey = zIndex !== undefined ? zIndex.toString() : '0'; + let replays = this.replaysByZIndex_[zIndexKey]; + if (replays === undefined) { + replays = {}; + this.replaysByZIndex_[zIndexKey] = replays; + } + let replay = replays[replayType]; + if (replay === undefined) { + /** + * @type {Function} + */ + const Constructor = BATCH_CONSTRUCTORS[replayType]; + replay = new Constructor(this.tolerance_, this.maxExtent_); + replays[replayType] = replay; + } + return replay; + } + + /** + * @inheritDoc + */ + isEmpty() { + return isEmpty(this.replaysByZIndex_); + } + + /** + * @param {module:ol/webgl/Context} context Context. + * @param {module:ol/coordinate~Coordinate} center Center. + * @param {number} resolution Resolution. + * @param {number} rotation Rotation. + * @param {module:ol/size~Size} size Size. + * @param {number} pixelRatio Pixel ratio. + * @param {number} opacity Global opacity. + * @param {Object.} skippedFeaturesHash Ids of features to skip. + */ + replay( + context, + center, + resolution, + rotation, + size, + pixelRatio, + opacity, + skippedFeaturesHash + ) { + /** @type {Array.} */ + const zs = Object.keys(this.replaysByZIndex_).map(Number); + zs.sort(numberSafeCompareFunction); + + let i, ii, j, jj, replays, replay; + for (i = 0, ii = zs.length; i < ii; ++i) { + replays = this.replaysByZIndex_[zs[i].toString()]; + for (j = 0, jj = ORDER.length; j < jj; ++j) { + replay = replays[ORDER[j]]; + if (replay !== undefined) { + replay.replay(context, + center, resolution, rotation, size, pixelRatio, + opacity, skippedFeaturesHash, + undefined, false); + } + } + } + } /** * @private - * @type {!Object.>} + * @param {module:ol/webgl/Context} context Context. + * @param {module:ol/coordinate~Coordinate} center Center. + * @param {number} resolution Resolution. + * @param {number} rotation Rotation. + * @param {module:ol/size~Size} size Size. + * @param {number} pixelRatio Pixel ratio. + * @param {number} opacity Global opacity. + * @param {Object.} skippedFeaturesHash Ids of features to skip. + * @param {function((module:ol/Feature|module:ol/render/Feature)): T|undefined} featureCallback Feature callback. + * @param {boolean} oneByOne Draw features one-by-one for the hit-detecion. + * @param {module:ol/extent~Extent=} opt_hitExtent Hit extent: Only features intersecting + * this extent are checked. + * @return {T|undefined} Callback result. + * @template T */ - this.replaysByZIndex_ = {}; + replayHitDetection_( + context, + center, + resolution, + rotation, + size, + pixelRatio, + opacity, + skippedFeaturesHash, + featureCallback, + oneByOne, + opt_hitExtent + ) { + /** @type {Array.} */ + const zs = Object.keys(this.replaysByZIndex_).map(Number); + zs.sort(function(a, b) { + return b - a; + }); -}; + let i, ii, j, replays, replay, result; + for (i = 0, ii = zs.length; i < ii; ++i) { + replays = this.replaysByZIndex_[zs[i].toString()]; + for (j = ORDER.length - 1; j >= 0; --j) { + replay = replays[ORDER[j]]; + if (replay !== undefined) { + result = replay.replay(context, + center, resolution, rotation, size, pixelRatio, opacity, + skippedFeaturesHash, featureCallback, oneByOne, opt_hitExtent); + if (result) { + return result; + } + } + } + } + return undefined; + } + + /** + * @param {module:ol/coordinate~Coordinate} coordinate Coordinate. + * @param {module:ol/webgl/Context} context Context. + * @param {module:ol/coordinate~Coordinate} center Center. + * @param {number} resolution Resolution. + * @param {number} rotation Rotation. + * @param {module:ol/size~Size} size Size. + * @param {number} pixelRatio Pixel ratio. + * @param {number} opacity Global opacity. + * @param {Object.} skippedFeaturesHash Ids of features to skip. + * @param {function((module:ol/Feature|module:ol/render/Feature)): T|undefined} callback Feature callback. + * @return {T|undefined} Callback result. + * @template T + */ + forEachFeatureAtCoordinate( + coordinate, + context, + center, + resolution, + rotation, + size, + pixelRatio, + opacity, + skippedFeaturesHash, + callback + ) { + const gl = context.getGL(); + gl.bindFramebuffer( + gl.FRAMEBUFFER, context.getHitDetectionFramebuffer()); + + + /** + * @type {module:ol/extent~Extent} + */ + let hitExtent; + if (this.renderBuffer_ !== undefined) { + // build an extent around the coordinate, so that only features that + // intersect this extent are checked + hitExtent = buffer(createOrUpdateFromCoordinate(coordinate), resolution * this.renderBuffer_); + } + + return this.replayHitDetection_(context, + coordinate, resolution, rotation, HIT_DETECTION_SIZE, + pixelRatio, opacity, skippedFeaturesHash, + /** + * @param {module:ol/Feature|module:ol/render/Feature} feature Feature. + * @return {?} Callback result. + */ + function(feature) { + const imageData = new Uint8Array(4); + gl.readPixels(0, 0, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, imageData); + + if (imageData[3] > 0) { + const result = callback(feature); + if (result) { + return result; + } + } + }, true, hitExtent); + } + + /** + * @param {module:ol/coordinate~Coordinate} coordinate Coordinate. + * @param {module:ol/webgl/Context} context Context. + * @param {module:ol/coordinate~Coordinate} center Center. + * @param {number} resolution Resolution. + * @param {number} rotation Rotation. + * @param {module:ol/size~Size} size Size. + * @param {number} pixelRatio Pixel ratio. + * @param {number} opacity Global opacity. + * @param {Object.} skippedFeaturesHash Ids of features to skip. + * @return {boolean} Is there a feature at the given coordinate? + */ + hasFeatureAtCoordinate( + coordinate, + context, + center, + resolution, + rotation, + size, + pixelRatio, + opacity, + skippedFeaturesHash + ) { + const gl = context.getGL(); + gl.bindFramebuffer( + gl.FRAMEBUFFER, context.getHitDetectionFramebuffer()); + + const hasFeature = this.replayHitDetection_(context, + coordinate, resolution, rotation, HIT_DETECTION_SIZE, + pixelRatio, opacity, skippedFeaturesHash, + /** + * @param {module:ol/Feature|module:ol/render/Feature} feature Feature. + * @return {boolean} Is there a feature? + */ + function(feature) { + const imageData = new Uint8Array(4); + gl.readPixels(0, 0, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, imageData); + return imageData[3] > 0; + }, false); + + return hasFeature !== undefined; + } +} inherits(WebGLReplayGroup, ReplayGroup); -/** - * @param {module:ol/style/Style} style Style. - * @param {boolean} group Group with previous replay. - */ -WebGLReplayGroup.prototype.addDeclutter = function(style, group) {}; - - -/** - * @param {module:ol/webgl/Context} context WebGL context. - * @return {function()} Delete resources function. - */ -WebGLReplayGroup.prototype.getDeleteResourcesFunction = function(context) { - const functions = []; - let zKey; - for (zKey in this.replaysByZIndex_) { - const replays = this.replaysByZIndex_[zKey]; - for (const replayKey in replays) { - functions.push( - replays[replayKey].getDeleteResourcesFunction(context)); - } - } - return function() { - const length = functions.length; - let result; - for (let i = 0; i < length; i++) { - result = functions[i].apply(this, arguments); - } - return result; - }; -}; - - -/** - * @param {module:ol/webgl/Context} context Context. - */ -WebGLReplayGroup.prototype.finish = function(context) { - let zKey; - for (zKey in this.replaysByZIndex_) { - const replays = this.replaysByZIndex_[zKey]; - for (const replayKey in replays) { - replays[replayKey].finish(context); - } - } -}; - - -/** - * @inheritDoc - */ -WebGLReplayGroup.prototype.getReplay = function(zIndex, replayType) { - const zIndexKey = zIndex !== undefined ? zIndex.toString() : '0'; - let replays = this.replaysByZIndex_[zIndexKey]; - if (replays === undefined) { - replays = {}; - this.replaysByZIndex_[zIndexKey] = replays; - } - let replay = replays[replayType]; - if (replay === undefined) { - /** - * @type {Function} - */ - const Constructor = BATCH_CONSTRUCTORS[replayType]; - replay = new Constructor(this.tolerance_, this.maxExtent_); - replays[replayType] = replay; - } - return replay; -}; - - -/** - * @inheritDoc - */ -WebGLReplayGroup.prototype.isEmpty = function() { - return isEmpty(this.replaysByZIndex_); -}; - - -/** - * @param {module:ol/webgl/Context} context Context. - * @param {module:ol/coordinate~Coordinate} center Center. - * @param {number} resolution Resolution. - * @param {number} rotation Rotation. - * @param {module:ol/size~Size} size Size. - * @param {number} pixelRatio Pixel ratio. - * @param {number} opacity Global opacity. - * @param {Object.} skippedFeaturesHash Ids of features to skip. - */ -WebGLReplayGroup.prototype.replay = function(context, - center, resolution, rotation, size, pixelRatio, - opacity, skippedFeaturesHash) { - /** @type {Array.} */ - const zs = Object.keys(this.replaysByZIndex_).map(Number); - zs.sort(numberSafeCompareFunction); - - let i, ii, j, jj, replays, replay; - for (i = 0, ii = zs.length; i < ii; ++i) { - replays = this.replaysByZIndex_[zs[i].toString()]; - for (j = 0, jj = ORDER.length; j < jj; ++j) { - replay = replays[ORDER[j]]; - if (replay !== undefined) { - replay.replay(context, - center, resolution, rotation, size, pixelRatio, - opacity, skippedFeaturesHash, - undefined, false); - } - } - } -}; - - -/** - * @private - * @param {module:ol/webgl/Context} context Context. - * @param {module:ol/coordinate~Coordinate} center Center. - * @param {number} resolution Resolution. - * @param {number} rotation Rotation. - * @param {module:ol/size~Size} size Size. - * @param {number} pixelRatio Pixel ratio. - * @param {number} opacity Global opacity. - * @param {Object.} skippedFeaturesHash Ids of features to skip. - * @param {function((module:ol/Feature|module:ol/render/Feature)): T|undefined} featureCallback Feature callback. - * @param {boolean} oneByOne Draw features one-by-one for the hit-detecion. - * @param {module:ol/extent~Extent=} opt_hitExtent Hit extent: Only features intersecting - * this extent are checked. - * @return {T|undefined} Callback result. - * @template T - */ -WebGLReplayGroup.prototype.replayHitDetection_ = function(context, - center, resolution, rotation, size, pixelRatio, opacity, - skippedFeaturesHash, featureCallback, oneByOne, opt_hitExtent) { - /** @type {Array.} */ - const zs = Object.keys(this.replaysByZIndex_).map(Number); - zs.sort(function(a, b) { - return b - a; - }); - - let i, ii, j, replays, replay, result; - for (i = 0, ii = zs.length; i < ii; ++i) { - replays = this.replaysByZIndex_[zs[i].toString()]; - for (j = ORDER.length - 1; j >= 0; --j) { - replay = replays[ORDER[j]]; - if (replay !== undefined) { - result = replay.replay(context, - center, resolution, rotation, size, pixelRatio, opacity, - skippedFeaturesHash, featureCallback, oneByOne, opt_hitExtent); - if (result) { - return result; - } - } - } - } - return undefined; -}; - - -/** - * @param {module:ol/coordinate~Coordinate} coordinate Coordinate. - * @param {module:ol/webgl/Context} context Context. - * @param {module:ol/coordinate~Coordinate} center Center. - * @param {number} resolution Resolution. - * @param {number} rotation Rotation. - * @param {module:ol/size~Size} size Size. - * @param {number} pixelRatio Pixel ratio. - * @param {number} opacity Global opacity. - * @param {Object.} skippedFeaturesHash Ids of features to skip. - * @param {function((module:ol/Feature|module:ol/render/Feature)): T|undefined} callback Feature callback. - * @return {T|undefined} Callback result. - * @template T - */ -WebGLReplayGroup.prototype.forEachFeatureAtCoordinate = function( - coordinate, context, center, resolution, rotation, size, pixelRatio, - opacity, skippedFeaturesHash, - callback) { - const gl = context.getGL(); - gl.bindFramebuffer( - gl.FRAMEBUFFER, context.getHitDetectionFramebuffer()); - - - /** - * @type {module:ol/extent~Extent} - */ - let hitExtent; - if (this.renderBuffer_ !== undefined) { - // build an extent around the coordinate, so that only features that - // intersect this extent are checked - hitExtent = buffer(createOrUpdateFromCoordinate(coordinate), resolution * this.renderBuffer_); - } - - return this.replayHitDetection_(context, - coordinate, resolution, rotation, HIT_DETECTION_SIZE, - pixelRatio, opacity, skippedFeaturesHash, - /** - * @param {module:ol/Feature|module:ol/render/Feature} feature Feature. - * @return {?} Callback result. - */ - function(feature) { - const imageData = new Uint8Array(4); - gl.readPixels(0, 0, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, imageData); - - if (imageData[3] > 0) { - const result = callback(feature); - if (result) { - return result; - } - } - }, true, hitExtent); -}; - - -/** - * @param {module:ol/coordinate~Coordinate} coordinate Coordinate. - * @param {module:ol/webgl/Context} context Context. - * @param {module:ol/coordinate~Coordinate} center Center. - * @param {number} resolution Resolution. - * @param {number} rotation Rotation. - * @param {module:ol/size~Size} size Size. - * @param {number} pixelRatio Pixel ratio. - * @param {number} opacity Global opacity. - * @param {Object.} skippedFeaturesHash Ids of features to skip. - * @return {boolean} Is there a feature at the given coordinate? - */ -WebGLReplayGroup.prototype.hasFeatureAtCoordinate = function( - coordinate, context, center, resolution, rotation, size, pixelRatio, - opacity, skippedFeaturesHash) { - const gl = context.getGL(); - gl.bindFramebuffer( - gl.FRAMEBUFFER, context.getHitDetectionFramebuffer()); - - const hasFeature = this.replayHitDetection_(context, - coordinate, resolution, rotation, HIT_DETECTION_SIZE, - pixelRatio, opacity, skippedFeaturesHash, - /** - * @param {module:ol/Feature|module:ol/render/Feature} feature Feature. - * @return {boolean} Is there a feature? - */ - function(feature) { - const imageData = new Uint8Array(4); - gl.readPixels(0, 0, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, imageData); - return imageData[3] > 0; - }, false); - - return hasFeature !== undefined; -}; - export default WebGLReplayGroup; diff --git a/src/ol/render/webgl/TextReplay.js b/src/ol/render/webgl/TextReplay.js index 13094b9946..7f4e9a37db 100644 --- a/src/ol/render/webgl/TextReplay.js +++ b/src/ol/render/webgl/TextReplay.js @@ -29,441 +29,435 @@ import WebGLBuffer from '../../webgl/Buffer.js'; * @param {module:ol/extent~Extent} maxExtent Max extent. * @struct */ -const WebGLTextReplay = function(tolerance, maxExtent) { - WebGLTextureReplay.call(this, tolerance, maxExtent); +class WebGLTextReplay { + constructor(tolerance, maxExtent) { + WebGLTextureReplay.call(this, tolerance, maxExtent); + + /** + * @private + * @type {Array.} + */ + this.images_ = []; + + /** + * @private + * @type {Array.} + */ + this.textures_ = []; + + /** + * @private + * @type {HTMLCanvasElement} + */ + this.measureCanvas_ = createCanvasContext2D(0, 0).canvas; + + /** + * @private + * @type {{strokeColor: (module:ol/colorlike~ColorLike|null), + * lineCap: (string|undefined), + * lineDash: Array., + * lineDashOffset: (number|undefined), + * lineJoin: (string|undefined), + * lineWidth: number, + * miterLimit: (number|undefined), + * fillColor: (module:ol/colorlike~ColorLike|null), + * font: (string|undefined), + * scale: (number|undefined)}} + */ + this.state_ = { + strokeColor: null, + lineCap: undefined, + lineDash: null, + lineDashOffset: undefined, + lineJoin: undefined, + lineWidth: 0, + miterLimit: undefined, + fillColor: null, + font: undefined, + scale: undefined + }; + + /** + * @private + * @type {string} + */ + this.text_ = ''; + + /** + * @private + * @type {number|undefined} + */ + this.textAlign_ = undefined; + + /** + * @private + * @type {number|undefined} + */ + this.textBaseline_ = undefined; + + /** + * @private + * @type {number|undefined} + */ + this.offsetX_ = undefined; + + /** + * @private + * @type {number|undefined} + */ + this.offsetY_ = undefined; + + /** + * @private + * @type {Object.} + */ + this.atlases_ = {}; + + /** + * @private + * @type {module:ol/render/webgl/TextReplay~GlyphAtlas|undefined} + */ + this.currAtlas_ = undefined; + + this.scale = 1; + + this.opacity = 1; + + } + + /** + * @inheritDoc + */ + drawText(geometry, feature) { + if (this.text_) { + let flatCoordinates = null; + const offset = 0; + let end = 2; + let stride = 2; + switch (geometry.getType()) { + case GeometryType.POINT: + case GeometryType.MULTI_POINT: + flatCoordinates = geometry.getFlatCoordinates(); + end = flatCoordinates.length; + stride = geometry.getStride(); + break; + case GeometryType.CIRCLE: + flatCoordinates = /** @type {module:ol/geom/Circle} */ (geometry).getCenter(); + break; + case GeometryType.LINE_STRING: + flatCoordinates = /** @type {module:ol/geom/LineString} */ (geometry).getFlatMidpoint(); + break; + case GeometryType.MULTI_LINE_STRING: + flatCoordinates = /** @type {module:ol/geom/MultiLineString} */ (geometry).getFlatMidpoints(); + end = flatCoordinates.length; + break; + case GeometryType.POLYGON: + flatCoordinates = /** @type {module:ol/geom/Polygon} */ (geometry).getFlatInteriorPoint(); + break; + case GeometryType.MULTI_POLYGON: + flatCoordinates = /** @type {module:ol/geom/MultiPolygon} */ (geometry).getFlatInteriorPoints(); + end = flatCoordinates.length; + break; + default: + } + this.startIndices.push(this.indices.length); + this.startIndicesFeature.push(feature); + + const glyphAtlas = this.currAtlas_; + const lines = this.text_.split('\n'); + const textSize = this.getTextSize_(lines); + let i, ii, j, jj, currX, currY, charArr, charInfo; + const anchorX = Math.round(textSize[0] * this.textAlign_ - this.offsetX_); + const anchorY = Math.round(textSize[1] * this.textBaseline_ - this.offsetY_); + const lineWidth = (this.state_.lineWidth / 2) * this.state_.scale; + + for (i = 0, ii = lines.length; i < ii; ++i) { + currX = 0; + currY = glyphAtlas.height * i; + charArr = lines[i].split(''); + + for (j = 0, jj = charArr.length; j < jj; ++j) { + charInfo = glyphAtlas.atlas.getInfo(charArr[j]); + + if (charInfo) { + const image = charInfo.image; + + this.anchorX = anchorX - currX; + this.anchorY = anchorY - currY; + this.originX = j === 0 ? charInfo.offsetX - lineWidth : charInfo.offsetX; + this.originY = charInfo.offsetY; + this.height = glyphAtlas.height; + this.width = j === 0 || j === charArr.length - 1 ? + glyphAtlas.width[charArr[j]] + lineWidth : glyphAtlas.width[charArr[j]]; + this.imageHeight = image.height; + this.imageWidth = image.width; + + if (this.images_.length === 0) { + this.images_.push(image); + } else { + const currentImage = this.images_[this.images_.length - 1]; + if (getUid(currentImage) != getUid(image)) { + this.groupIndices.push(this.indices.length); + this.images_.push(image); + } + } + + this.drawText_(flatCoordinates, offset, end, stride); + } + currX += this.width; + } + } + } + } /** * @private - * @type {Array.} + * @param {Array.} lines Label to draw split to lines. + * @return {Array.} Size of the label in pixels. */ - this.images_ = []; + getTextSize_(lines) { + const self = this; + const glyphAtlas = this.currAtlas_; + const textHeight = lines.length * glyphAtlas.height; + //Split every line to an array of chars, sum up their width, and select the longest. + const textWidth = lines.map(function(str) { + let sum = 0; + for (let i = 0, ii = str.length; i < ii; ++i) { + const curr = str[i]; + if (!glyphAtlas.width[curr]) { + self.addCharToAtlas_(curr); + } + sum += glyphAtlas.width[curr] ? glyphAtlas.width[curr] : 0; + } + return sum; + }).reduce(function(max, curr) { + return Math.max(max, curr); + }); + + return [textWidth, textHeight]; + } /** * @private - * @type {Array.} + * @param {Array.} flatCoordinates Flat coordinates. + * @param {number} offset Offset. + * @param {number} end End. + * @param {number} stride Stride. */ - this.textures_ = []; + drawText_(flatCoordinates, offset, end, stride) { + for (let i = offset, ii = end; i < ii; i += stride) { + this.drawCoordinates(flatCoordinates, offset, end, stride); + } + } /** * @private - * @type {HTMLCanvasElement} + * @param {string} char Character. */ - this.measureCanvas_ = createCanvasContext2D(0, 0).canvas; + addCharToAtlas_(char) { + if (char.length === 1) { + const glyphAtlas = this.currAtlas_; + const state = this.state_; + const mCtx = this.measureCanvas_.getContext('2d'); + mCtx.font = state.font; + const width = Math.ceil(mCtx.measureText(char).width * state.scale); + + const info = glyphAtlas.atlas.add(char, width, glyphAtlas.height, + function(ctx, x, y) { + //Parameterize the canvas + ctx.font = /** @type {string} */ (state.font); + ctx.fillStyle = state.fillColor; + ctx.strokeStyle = state.strokeColor; + ctx.lineWidth = state.lineWidth; + ctx.lineCap = /*** @type {string} */ (state.lineCap); + ctx.lineJoin = /** @type {string} */ (state.lineJoin); + ctx.miterLimit = /** @type {number} */ (state.miterLimit); + ctx.textAlign = 'left'; + ctx.textBaseline = 'top'; + if (CANVAS_LINE_DASH && state.lineDash) { + //FIXME: use pixelRatio + ctx.setLineDash(state.lineDash); + ctx.lineDashOffset = /** @type {number} */ (state.lineDashOffset); + } + if (state.scale !== 1) { + //FIXME: use pixelRatio + ctx.setTransform(/** @type {number} */ (state.scale), 0, 0, + /** @type {number} */ (state.scale), 0, 0); + } + + //Draw the character on the canvas + if (state.strokeColor) { + ctx.strokeText(char, x, y); + } + if (state.fillColor) { + ctx.fillText(char, x, y); + } + }); + + if (info) { + glyphAtlas.width[char] = width; + } + } + } + + /** + * @inheritDoc + */ + finish(context) { + const gl = context.getGL(); + + this.groupIndices.push(this.indices.length); + this.hitDetectionGroupIndices = this.groupIndices; + + // create, bind, and populate the vertices buffer + this.verticesBuffer = new WebGLBuffer(this.vertices); + + // create, bind, and populate the indices buffer + this.indicesBuffer = new WebGLBuffer(this.indices); + + // create textures + /** @type {Object.} */ + const texturePerImage = {}; + + this.createTextures(this.textures_, this.images_, texturePerImage, gl); + + this.state_ = { + strokeColor: null, + lineCap: undefined, + lineDash: null, + lineDashOffset: undefined, + lineJoin: undefined, + lineWidth: 0, + miterLimit: undefined, + fillColor: null, + font: undefined, + scale: undefined + }; + this.text_ = ''; + this.textAlign_ = undefined; + this.textBaseline_ = undefined; + this.offsetX_ = undefined; + this.offsetY_ = undefined; + this.images_ = null; + this.atlases_ = {}; + this.currAtlas_ = undefined; + WebGLTextureReplay.prototype.finish.call(this, context); + } + + /** + * @inheritDoc + */ + setTextStyle(textStyle) { + const state = this.state_; + const textFillStyle = textStyle.getFill(); + const textStrokeStyle = textStyle.getStroke(); + if (!textStyle || !textStyle.getText() || (!textFillStyle && !textStrokeStyle)) { + this.text_ = ''; + } else { + if (!textFillStyle) { + state.fillColor = null; + } else { + const textFillStyleColor = textFillStyle.getColor(); + state.fillColor = asColorLike(textFillStyleColor ? + textFillStyleColor : DEFAULT_FILLSTYLE); + } + if (!textStrokeStyle) { + state.strokeColor = null; + state.lineWidth = 0; + } else { + const textStrokeStyleColor = textStrokeStyle.getColor(); + state.strokeColor = asColorLike(textStrokeStyleColor ? + textStrokeStyleColor : DEFAULT_STROKESTYLE); + state.lineWidth = textStrokeStyle.getWidth() || DEFAULT_LINEWIDTH; + state.lineCap = textStrokeStyle.getLineCap() || DEFAULT_LINECAP; + state.lineDashOffset = textStrokeStyle.getLineDashOffset() || DEFAULT_LINEDASHOFFSET; + state.lineJoin = textStrokeStyle.getLineJoin() || DEFAULT_LINEJOIN; + state.miterLimit = textStrokeStyle.getMiterLimit() || DEFAULT_MITERLIMIT; + const lineDash = textStrokeStyle.getLineDash(); + state.lineDash = lineDash ? lineDash.slice() : DEFAULT_LINEDASH; + } + state.font = textStyle.getFont() || DEFAULT_FONT; + state.scale = textStyle.getScale() || 1; + this.text_ = /** @type {string} */ (textStyle.getText()); + const textAlign = TEXT_ALIGN[textStyle.getTextAlign()]; + const textBaseline = TEXT_ALIGN[textStyle.getTextBaseline()]; + this.textAlign_ = textAlign === undefined ? + DEFAULT_TEXTALIGN : textAlign; + this.textBaseline_ = textBaseline === undefined ? + DEFAULT_TEXTBASELINE : textBaseline; + this.offsetX_ = textStyle.getOffsetX() || 0; + this.offsetY_ = textStyle.getOffsetY() || 0; + this.rotateWithView = !!textStyle.getRotateWithView(); + this.rotation = textStyle.getRotation() || 0; + + this.currAtlas_ = this.getAtlas_(state); + } + } /** * @private - * @type {{strokeColor: (module:ol/colorlike~ColorLike|null), - * lineCap: (string|undefined), - * lineDash: Array., - * lineDashOffset: (number|undefined), - * lineJoin: (string|undefined), - * lineWidth: number, - * miterLimit: (number|undefined), - * fillColor: (module:ol/colorlike~ColorLike|null), - * font: (string|undefined), - * scale: (number|undefined)}} + * @param {Object} state Font attributes. + * @return {module:ol/render/webgl/TextReplay~GlyphAtlas} Glyph atlas. */ - this.state_ = { - strokeColor: null, - lineCap: undefined, - lineDash: null, - lineDashOffset: undefined, - lineJoin: undefined, - lineWidth: 0, - miterLimit: undefined, - fillColor: null, - font: undefined, - scale: undefined - }; + getAtlas_(state) { + let params = []; + for (const i in state) { + if (state[i] || state[i] === 0) { + if (Array.isArray(state[i])) { + params = params.concat(state[i]); + } else { + params.push(state[i]); + } + } + } + const hash = this.calculateHash_(params); + if (!this.atlases_[hash]) { + const mCtx = this.measureCanvas_.getContext('2d'); + mCtx.font = state.font; + const height = Math.ceil((mCtx.measureText('M').width * 1.5 + + state.lineWidth / 2) * state.scale); + + this.atlases_[hash] = { + atlas: new AtlasManager({ + space: state.lineWidth + 1 + }), + width: {}, + height: height + }; + } + return this.atlases_[hash]; + } /** * @private - * @type {string} + * @param {Array.} params Array of parameters. + * @return {string} Hash string. */ - this.text_ = ''; + calculateHash_(params) { + //TODO: Create a more performant, reliable, general hash function. + let hash = ''; + for (let i = 0, ii = params.length; i < ii; ++i) { + hash += params[i]; + } + return hash; + } /** - * @private - * @type {number|undefined} + * @inheritDoc */ - this.textAlign_ = undefined; + getTextures(opt_all) { + return this.textures_; + } /** - * @private - * @type {number|undefined} + * @inheritDoc */ - this.textBaseline_ = undefined; - - /** - * @private - * @type {number|undefined} - */ - this.offsetX_ = undefined; - - /** - * @private - * @type {number|undefined} - */ - this.offsetY_ = undefined; - - /** - * @private - * @type {Object.} - */ - this.atlases_ = {}; - - /** - * @private - * @type {module:ol/render/webgl/TextReplay~GlyphAtlas|undefined} - */ - this.currAtlas_ = undefined; - - this.scale = 1; - - this.opacity = 1; - -}; + getHitDetectionTextures() { + return this.textures_; + } +} inherits(WebGLTextReplay, WebGLTextureReplay); -/** - * @inheritDoc - */ -WebGLTextReplay.prototype.drawText = function(geometry, feature) { - if (this.text_) { - let flatCoordinates = null; - const offset = 0; - let end = 2; - let stride = 2; - switch (geometry.getType()) { - case GeometryType.POINT: - case GeometryType.MULTI_POINT: - flatCoordinates = geometry.getFlatCoordinates(); - end = flatCoordinates.length; - stride = geometry.getStride(); - break; - case GeometryType.CIRCLE: - flatCoordinates = /** @type {module:ol/geom/Circle} */ (geometry).getCenter(); - break; - case GeometryType.LINE_STRING: - flatCoordinates = /** @type {module:ol/geom/LineString} */ (geometry).getFlatMidpoint(); - break; - case GeometryType.MULTI_LINE_STRING: - flatCoordinates = /** @type {module:ol/geom/MultiLineString} */ (geometry).getFlatMidpoints(); - end = flatCoordinates.length; - break; - case GeometryType.POLYGON: - flatCoordinates = /** @type {module:ol/geom/Polygon} */ (geometry).getFlatInteriorPoint(); - break; - case GeometryType.MULTI_POLYGON: - flatCoordinates = /** @type {module:ol/geom/MultiPolygon} */ (geometry).getFlatInteriorPoints(); - end = flatCoordinates.length; - break; - default: - } - this.startIndices.push(this.indices.length); - this.startIndicesFeature.push(feature); - - const glyphAtlas = this.currAtlas_; - const lines = this.text_.split('\n'); - const textSize = this.getTextSize_(lines); - let i, ii, j, jj, currX, currY, charArr, charInfo; - const anchorX = Math.round(textSize[0] * this.textAlign_ - this.offsetX_); - const anchorY = Math.round(textSize[1] * this.textBaseline_ - this.offsetY_); - const lineWidth = (this.state_.lineWidth / 2) * this.state_.scale; - - for (i = 0, ii = lines.length; i < ii; ++i) { - currX = 0; - currY = glyphAtlas.height * i; - charArr = lines[i].split(''); - - for (j = 0, jj = charArr.length; j < jj; ++j) { - charInfo = glyphAtlas.atlas.getInfo(charArr[j]); - - if (charInfo) { - const image = charInfo.image; - - this.anchorX = anchorX - currX; - this.anchorY = anchorY - currY; - this.originX = j === 0 ? charInfo.offsetX - lineWidth : charInfo.offsetX; - this.originY = charInfo.offsetY; - this.height = glyphAtlas.height; - this.width = j === 0 || j === charArr.length - 1 ? - glyphAtlas.width[charArr[j]] + lineWidth : glyphAtlas.width[charArr[j]]; - this.imageHeight = image.height; - this.imageWidth = image.width; - - if (this.images_.length === 0) { - this.images_.push(image); - } else { - const currentImage = this.images_[this.images_.length - 1]; - if (getUid(currentImage) != getUid(image)) { - this.groupIndices.push(this.indices.length); - this.images_.push(image); - } - } - - this.drawText_(flatCoordinates, offset, end, stride); - } - currX += this.width; - } - } - } -}; - - -/** - * @private - * @param {Array.} lines Label to draw split to lines. - * @return {Array.} Size of the label in pixels. - */ -WebGLTextReplay.prototype.getTextSize_ = function(lines) { - const self = this; - const glyphAtlas = this.currAtlas_; - const textHeight = lines.length * glyphAtlas.height; - //Split every line to an array of chars, sum up their width, and select the longest. - const textWidth = lines.map(function(str) { - let sum = 0; - for (let i = 0, ii = str.length; i < ii; ++i) { - const curr = str[i]; - if (!glyphAtlas.width[curr]) { - self.addCharToAtlas_(curr); - } - sum += glyphAtlas.width[curr] ? glyphAtlas.width[curr] : 0; - } - return sum; - }).reduce(function(max, curr) { - return Math.max(max, curr); - }); - - return [textWidth, textHeight]; -}; - - -/** - * @private - * @param {Array.} flatCoordinates Flat coordinates. - * @param {number} offset Offset. - * @param {number} end End. - * @param {number} stride Stride. - */ -WebGLTextReplay.prototype.drawText_ = function(flatCoordinates, offset, end, stride) { - for (let i = offset, ii = end; i < ii; i += stride) { - this.drawCoordinates(flatCoordinates, offset, end, stride); - } -}; - - -/** - * @private - * @param {string} char Character. - */ -WebGLTextReplay.prototype.addCharToAtlas_ = function(char) { - if (char.length === 1) { - const glyphAtlas = this.currAtlas_; - const state = this.state_; - const mCtx = this.measureCanvas_.getContext('2d'); - mCtx.font = state.font; - const width = Math.ceil(mCtx.measureText(char).width * state.scale); - - const info = glyphAtlas.atlas.add(char, width, glyphAtlas.height, - function(ctx, x, y) { - //Parameterize the canvas - ctx.font = /** @type {string} */ (state.font); - ctx.fillStyle = state.fillColor; - ctx.strokeStyle = state.strokeColor; - ctx.lineWidth = state.lineWidth; - ctx.lineCap = /*** @type {string} */ (state.lineCap); - ctx.lineJoin = /** @type {string} */ (state.lineJoin); - ctx.miterLimit = /** @type {number} */ (state.miterLimit); - ctx.textAlign = 'left'; - ctx.textBaseline = 'top'; - if (CANVAS_LINE_DASH && state.lineDash) { - //FIXME: use pixelRatio - ctx.setLineDash(state.lineDash); - ctx.lineDashOffset = /** @type {number} */ (state.lineDashOffset); - } - if (state.scale !== 1) { - //FIXME: use pixelRatio - ctx.setTransform(/** @type {number} */ (state.scale), 0, 0, - /** @type {number} */ (state.scale), 0, 0); - } - - //Draw the character on the canvas - if (state.strokeColor) { - ctx.strokeText(char, x, y); - } - if (state.fillColor) { - ctx.fillText(char, x, y); - } - }); - - if (info) { - glyphAtlas.width[char] = width; - } - } -}; - - -/** - * @inheritDoc - */ -WebGLTextReplay.prototype.finish = function(context) { - const gl = context.getGL(); - - this.groupIndices.push(this.indices.length); - this.hitDetectionGroupIndices = this.groupIndices; - - // create, bind, and populate the vertices buffer - this.verticesBuffer = new WebGLBuffer(this.vertices); - - // create, bind, and populate the indices buffer - this.indicesBuffer = new WebGLBuffer(this.indices); - - // create textures - /** @type {Object.} */ - const texturePerImage = {}; - - this.createTextures(this.textures_, this.images_, texturePerImage, gl); - - this.state_ = { - strokeColor: null, - lineCap: undefined, - lineDash: null, - lineDashOffset: undefined, - lineJoin: undefined, - lineWidth: 0, - miterLimit: undefined, - fillColor: null, - font: undefined, - scale: undefined - }; - this.text_ = ''; - this.textAlign_ = undefined; - this.textBaseline_ = undefined; - this.offsetX_ = undefined; - this.offsetY_ = undefined; - this.images_ = null; - this.atlases_ = {}; - this.currAtlas_ = undefined; - WebGLTextureReplay.prototype.finish.call(this, context); -}; - - -/** - * @inheritDoc - */ -WebGLTextReplay.prototype.setTextStyle = function(textStyle) { - const state = this.state_; - const textFillStyle = textStyle.getFill(); - const textStrokeStyle = textStyle.getStroke(); - if (!textStyle || !textStyle.getText() || (!textFillStyle && !textStrokeStyle)) { - this.text_ = ''; - } else { - if (!textFillStyle) { - state.fillColor = null; - } else { - const textFillStyleColor = textFillStyle.getColor(); - state.fillColor = asColorLike(textFillStyleColor ? - textFillStyleColor : DEFAULT_FILLSTYLE); - } - if (!textStrokeStyle) { - state.strokeColor = null; - state.lineWidth = 0; - } else { - const textStrokeStyleColor = textStrokeStyle.getColor(); - state.strokeColor = asColorLike(textStrokeStyleColor ? - textStrokeStyleColor : DEFAULT_STROKESTYLE); - state.lineWidth = textStrokeStyle.getWidth() || DEFAULT_LINEWIDTH; - state.lineCap = textStrokeStyle.getLineCap() || DEFAULT_LINECAP; - state.lineDashOffset = textStrokeStyle.getLineDashOffset() || DEFAULT_LINEDASHOFFSET; - state.lineJoin = textStrokeStyle.getLineJoin() || DEFAULT_LINEJOIN; - state.miterLimit = textStrokeStyle.getMiterLimit() || DEFAULT_MITERLIMIT; - const lineDash = textStrokeStyle.getLineDash(); - state.lineDash = lineDash ? lineDash.slice() : DEFAULT_LINEDASH; - } - state.font = textStyle.getFont() || DEFAULT_FONT; - state.scale = textStyle.getScale() || 1; - this.text_ = /** @type {string} */ (textStyle.getText()); - const textAlign = TEXT_ALIGN[textStyle.getTextAlign()]; - const textBaseline = TEXT_ALIGN[textStyle.getTextBaseline()]; - this.textAlign_ = textAlign === undefined ? - DEFAULT_TEXTALIGN : textAlign; - this.textBaseline_ = textBaseline === undefined ? - DEFAULT_TEXTBASELINE : textBaseline; - this.offsetX_ = textStyle.getOffsetX() || 0; - this.offsetY_ = textStyle.getOffsetY() || 0; - this.rotateWithView = !!textStyle.getRotateWithView(); - this.rotation = textStyle.getRotation() || 0; - - this.currAtlas_ = this.getAtlas_(state); - } -}; - - -/** - * @private - * @param {Object} state Font attributes. - * @return {module:ol/render/webgl/TextReplay~GlyphAtlas} Glyph atlas. - */ -WebGLTextReplay.prototype.getAtlas_ = function(state) { - let params = []; - for (const i in state) { - if (state[i] || state[i] === 0) { - if (Array.isArray(state[i])) { - params = params.concat(state[i]); - } else { - params.push(state[i]); - } - } - } - const hash = this.calculateHash_(params); - if (!this.atlases_[hash]) { - const mCtx = this.measureCanvas_.getContext('2d'); - mCtx.font = state.font; - const height = Math.ceil((mCtx.measureText('M').width * 1.5 + - state.lineWidth / 2) * state.scale); - - this.atlases_[hash] = { - atlas: new AtlasManager({ - space: state.lineWidth + 1 - }), - width: {}, - height: height - }; - } - return this.atlases_[hash]; -}; - - -/** - * @private - * @param {Array.} params Array of parameters. - * @return {string} Hash string. - */ -WebGLTextReplay.prototype.calculateHash_ = function(params) { - //TODO: Create a more performant, reliable, general hash function. - let hash = ''; - for (let i = 0, ii = params.length; i < ii; ++i) { - hash += params[i]; - } - return hash; -}; - - -/** - * @inheritDoc - */ -WebGLTextReplay.prototype.getTextures = function(opt_all) { - return this.textures_; -}; - - -/** - * @inheritDoc - */ -WebGLTextReplay.prototype.getHitDetectionTextures = function() { - return this.textures_; -}; export default WebGLTextReplay; diff --git a/src/ol/render/webgl/TextureReplay.js b/src/ol/render/webgl/TextureReplay.js index c98812d1fc..3ce621d0b3 100644 --- a/src/ol/render/webgl/TextureReplay.js +++ b/src/ol/render/webgl/TextureReplay.js @@ -18,476 +18,467 @@ import {createTexture} from '../../webgl/Context.js'; * @param {module:ol/extent~Extent} maxExtent Max extent. * @struct */ -const WebGLTextureReplay = function(tolerance, maxExtent) { - WebGLReplay.call(this, tolerance, maxExtent); +class WebGLTextureReplay { + constructor(tolerance, maxExtent) { + WebGLReplay.call(this, tolerance, maxExtent); + + /** + * @type {number|undefined} + * @protected + */ + this.anchorX = undefined; + + /** + * @type {number|undefined} + * @protected + */ + this.anchorY = undefined; + + /** + * @type {Array.} + * @protected + */ + this.groupIndices = []; + + /** + * @type {Array.} + * @protected + */ + this.hitDetectionGroupIndices = []; + + /** + * @type {number|undefined} + * @protected + */ + this.height = undefined; + + /** + * @type {number|undefined} + * @protected + */ + this.imageHeight = undefined; + + /** + * @type {number|undefined} + * @protected + */ + this.imageWidth = undefined; + + /** + * @protected + * @type {module:ol/render/webgl/texturereplay/defaultshader/Locations} + */ + this.defaultLocations = null; + + /** + * @protected + * @type {number|undefined} + */ + this.opacity = undefined; + + /** + * @type {number|undefined} + * @protected + */ + this.originX = undefined; + + /** + * @type {number|undefined} + * @protected + */ + this.originY = undefined; + + /** + * @protected + * @type {boolean|undefined} + */ + this.rotateWithView = undefined; + + /** + * @protected + * @type {number|undefined} + */ + this.rotation = undefined; + + /** + * @protected + * @type {number|undefined} + */ + this.scale = undefined; + + /** + * @type {number|undefined} + * @protected + */ + this.width = undefined; + } /** - * @type {number|undefined} - * @protected + * @inheritDoc */ - this.anchorX = undefined; + getDeleteResourcesFunction(context) { + const verticesBuffer = this.verticesBuffer; + const indicesBuffer = this.indicesBuffer; + const textures = this.getTextures(true); + const gl = context.getGL(); + return function() { + if (!gl.isContextLost()) { + let i, ii; + for (i = 0, ii = textures.length; i < ii; ++i) { + gl.deleteTexture(textures[i]); + } + } + context.deleteBuffer(verticesBuffer); + context.deleteBuffer(indicesBuffer); + }; + } /** - * @type {number|undefined} + * @param {Array.} flatCoordinates Flat coordinates. + * @param {number} offset Offset. + * @param {number} end End. + * @param {number} stride Stride. + * @return {number} My end. * @protected */ - this.anchorY = undefined; + drawCoordinates(flatCoordinates, offset, end, stride) { + const anchorX = /** @type {number} */ (this.anchorX); + const anchorY = /** @type {number} */ (this.anchorY); + const height = /** @type {number} */ (this.height); + const imageHeight = /** @type {number} */ (this.imageHeight); + const imageWidth = /** @type {number} */ (this.imageWidth); + const opacity = /** @type {number} */ (this.opacity); + const originX = /** @type {number} */ (this.originX); + const originY = /** @type {number} */ (this.originY); + const rotateWithView = this.rotateWithView ? 1.0 : 0.0; + // this.rotation_ is anti-clockwise, but rotation is clockwise + const rotation = /** @type {number} */ (-this.rotation); + const scale = /** @type {number} */ (this.scale); + const width = /** @type {number} */ (this.width); + const cos = Math.cos(rotation); + const sin = Math.sin(rotation); + let numIndices = this.indices.length; + let numVertices = this.vertices.length; + let i, n, offsetX, offsetY, x, y; + for (i = offset; i < end; i += stride) { + x = flatCoordinates[i] - this.origin[0]; + y = flatCoordinates[i + 1] - this.origin[1]; - /** - * @type {Array.} - * @protected - */ - this.groupIndices = []; + // There are 4 vertices per [x, y] point, one for each corner of the + // rectangle we're going to draw. We'd use 1 vertex per [x, y] point if + // WebGL supported Geometry Shaders (which can emit new vertices), but that + // is not currently the case. + // + // And each vertex includes 8 values: the x and y coordinates, the x and + // y offsets used to calculate the position of the corner, the u and + // v texture coordinates for the corner, the opacity, and whether the + // the image should be rotated with the view (rotateWithView). - /** - * @type {Array.} - * @protected - */ - this.hitDetectionGroupIndices = []; + n = numVertices / 8; - /** - * @type {number|undefined} - * @protected - */ - this.height = undefined; + // bottom-left corner + offsetX = -scale * anchorX; + offsetY = -scale * (height - anchorY); + this.vertices[numVertices++] = x; + this.vertices[numVertices++] = y; + this.vertices[numVertices++] = offsetX * cos - offsetY * sin; + this.vertices[numVertices++] = offsetX * sin + offsetY * cos; + this.vertices[numVertices++] = originX / imageWidth; + this.vertices[numVertices++] = (originY + height) / imageHeight; + this.vertices[numVertices++] = opacity; + this.vertices[numVertices++] = rotateWithView; - /** - * @type {number|undefined} - * @protected - */ - this.imageHeight = undefined; + // bottom-right corner + offsetX = scale * (width - anchorX); + offsetY = -scale * (height - anchorY); + this.vertices[numVertices++] = x; + this.vertices[numVertices++] = y; + this.vertices[numVertices++] = offsetX * cos - offsetY * sin; + this.vertices[numVertices++] = offsetX * sin + offsetY * cos; + this.vertices[numVertices++] = (originX + width) / imageWidth; + this.vertices[numVertices++] = (originY + height) / imageHeight; + this.vertices[numVertices++] = opacity; + this.vertices[numVertices++] = rotateWithView; - /** - * @type {number|undefined} - * @protected - */ - this.imageWidth = undefined; + // top-right corner + offsetX = scale * (width - anchorX); + offsetY = scale * anchorY; + this.vertices[numVertices++] = x; + this.vertices[numVertices++] = y; + this.vertices[numVertices++] = offsetX * cos - offsetY * sin; + this.vertices[numVertices++] = offsetX * sin + offsetY * cos; + this.vertices[numVertices++] = (originX + width) / imageWidth; + this.vertices[numVertices++] = originY / imageHeight; + this.vertices[numVertices++] = opacity; + this.vertices[numVertices++] = rotateWithView; + + // top-left corner + offsetX = -scale * anchorX; + offsetY = scale * anchorY; + this.vertices[numVertices++] = x; + this.vertices[numVertices++] = y; + this.vertices[numVertices++] = offsetX * cos - offsetY * sin; + this.vertices[numVertices++] = offsetX * sin + offsetY * cos; + this.vertices[numVertices++] = originX / imageWidth; + this.vertices[numVertices++] = originY / imageHeight; + this.vertices[numVertices++] = opacity; + this.vertices[numVertices++] = rotateWithView; + + this.indices[numIndices++] = n; + this.indices[numIndices++] = n + 1; + this.indices[numIndices++] = n + 2; + this.indices[numIndices++] = n; + this.indices[numIndices++] = n + 2; + this.indices[numIndices++] = n + 3; + } + + return numVertices; + } /** * @protected - * @type {module:ol/render/webgl/texturereplay/defaultshader/Locations} + * @param {Array.} textures Textures. + * @param {Array.} images Images. + * @param {!Object.} texturePerImage Texture cache. + * @param {WebGLRenderingContext} gl Gl. */ - this.defaultLocations = null; + createTextures(textures, images, texturePerImage, gl) { + let texture, image, uid, i; + const ii = images.length; + for (i = 0; i < ii; ++i) { + image = images[i]; + + uid = getUid(image).toString(); + if (uid in texturePerImage) { + texture = texturePerImage[uid]; + } else { + texture = createTexture( + gl, image, CLAMP_TO_EDGE, CLAMP_TO_EDGE); + texturePerImage[uid] = texture; + } + textures[i] = texture; + } + } /** - * @protected - * @type {number|undefined} + * @inheritDoc */ - this.opacity = undefined; + setUpProgram(gl, context, size, pixelRatio) { + // get the program + const program = context.getProgram(fragment, vertex); + + // get the locations + let locations; + if (!this.defaultLocations) { + locations = new Locations(gl, program); + this.defaultLocations = locations; + } else { + locations = this.defaultLocations; + } + + // use the program (FIXME: use the return value) + context.useProgram(program); + + // enable the vertex attrib arrays + gl.enableVertexAttribArray(locations.a_position); + gl.vertexAttribPointer(locations.a_position, 2, FLOAT, + false, 32, 0); + + gl.enableVertexAttribArray(locations.a_offsets); + gl.vertexAttribPointer(locations.a_offsets, 2, FLOAT, + false, 32, 8); + + gl.enableVertexAttribArray(locations.a_texCoord); + gl.vertexAttribPointer(locations.a_texCoord, 2, FLOAT, + false, 32, 16); + + gl.enableVertexAttribArray(locations.a_opacity); + gl.vertexAttribPointer(locations.a_opacity, 1, FLOAT, + false, 32, 24); + + gl.enableVertexAttribArray(locations.a_rotateWithView); + gl.vertexAttribPointer(locations.a_rotateWithView, 1, FLOAT, + false, 32, 28); + + return locations; + } /** - * @type {number|undefined} - * @protected + * @inheritDoc */ - this.originX = undefined; + shutDownProgram(gl, locations) { + gl.disableVertexAttribArray(locations.a_position); + gl.disableVertexAttribArray(locations.a_offsets); + gl.disableVertexAttribArray(locations.a_texCoord); + gl.disableVertexAttribArray(locations.a_opacity); + gl.disableVertexAttribArray(locations.a_rotateWithView); + } /** - * @type {number|undefined} - * @protected + * @inheritDoc */ - this.originY = undefined; + drawReplay(gl, context, skippedFeaturesHash, hitDetection) { + const textures = hitDetection ? this.getHitDetectionTextures() : this.getTextures(); + const groupIndices = hitDetection ? this.hitDetectionGroupIndices : this.groupIndices; + + if (!isEmpty(skippedFeaturesHash)) { + this.drawReplaySkipping(gl, context, skippedFeaturesHash, textures, groupIndices); + } else { + let i, ii, start; + for (i = 0, ii = textures.length, start = 0; i < ii; ++i) { + gl.bindTexture(TEXTURE_2D, textures[i]); + const end = groupIndices[i]; + this.drawElements(gl, context, start, end); + start = end; + } + } + } /** + * Draw the replay while paying attention to skipped features. + * + * This functions creates groups of features that can be drawn to together, + * so that the number of `drawElements` calls is minimized. + * + * For example given the following texture groups: + * + * Group 1: A B C + * Group 2: D [E] F G + * + * If feature E should be skipped, the following `drawElements` calls will be + * made: + * + * drawElements with feature A, B and C + * drawElements with feature D + * drawElements with feature F and G + * * @protected - * @type {boolean|undefined} + * @param {WebGLRenderingContext} gl gl. + * @param {module:ol/webgl/Context} context Context. + * @param {Object.} skippedFeaturesHash Ids of features + * to skip. + * @param {Array.} textures Textures. + * @param {Array.} groupIndices Texture group indices. */ - this.rotateWithView = undefined; + drawReplaySkipping(gl, context, skippedFeaturesHash, textures, groupIndices) { + let featureIndex = 0; + + let i, ii; + for (i = 0, ii = textures.length; i < ii; ++i) { + gl.bindTexture(TEXTURE_2D, textures[i]); + const groupStart = (i > 0) ? groupIndices[i - 1] : 0; + const groupEnd = groupIndices[i]; + + let start = groupStart; + let end = groupStart; + while (featureIndex < this.startIndices.length && + this.startIndices[featureIndex] <= groupEnd) { + const feature = this.startIndicesFeature[featureIndex]; + + const featureUid = getUid(feature).toString(); + if (skippedFeaturesHash[featureUid] !== undefined) { + // feature should be skipped + if (start !== end) { + // draw the features so far + this.drawElements(gl, context, start, end); + } + // continue with the next feature + start = (featureIndex === this.startIndices.length - 1) ? + groupEnd : this.startIndices[featureIndex + 1]; + end = start; + } else { + // the feature is not skipped, augment the end index + end = (featureIndex === this.startIndices.length - 1) ? + groupEnd : this.startIndices[featureIndex + 1]; + } + featureIndex++; + } + + if (start !== end) { + // draw the remaining features (in case there was no skipped feature + // in this texture group, all features of a group are drawn together) + this.drawElements(gl, context, start, end); + } + } + } /** - * @protected - * @type {number|undefined} + * @inheritDoc */ - this.rotation = undefined; + drawHitDetectionReplayOneByOne(gl, context, skippedFeaturesHash, featureCallback, opt_hitExtent) { + let i, groupStart, start, end, feature, featureUid; + let featureIndex = this.startIndices.length - 1; + const hitDetectionTextures = this.getHitDetectionTextures(); + for (i = hitDetectionTextures.length - 1; i >= 0; --i) { + gl.bindTexture(TEXTURE_2D, hitDetectionTextures[i]); + groupStart = (i > 0) ? this.hitDetectionGroupIndices[i - 1] : 0; + end = this.hitDetectionGroupIndices[i]; + + // draw all features for this texture group + while (featureIndex >= 0 && + this.startIndices[featureIndex] >= groupStart) { + start = this.startIndices[featureIndex]; + feature = this.startIndicesFeature[featureIndex]; + featureUid = getUid(feature).toString(); + + if (skippedFeaturesHash[featureUid] === undefined && + feature.getGeometry() && + (opt_hitExtent === undefined || intersects( + /** @type {Array} */ (opt_hitExtent), + feature.getGeometry().getExtent()))) { + gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); + this.drawElements(gl, context, start, end); + + const result = featureCallback(feature); + if (result) { + return result; + } + } + + end = start; + featureIndex--; + } + } + return undefined; + } /** - * @protected - * @type {number|undefined} + * @inheritDoc */ - this.scale = undefined; + finish(context) { + this.anchorX = undefined; + this.anchorY = undefined; + this.height = undefined; + this.imageHeight = undefined; + this.imageWidth = undefined; + this.indices = null; + this.opacity = undefined; + this.originX = undefined; + this.originY = undefined; + this.rotateWithView = undefined; + this.rotation = undefined; + this.scale = undefined; + this.vertices = null; + this.width = undefined; + } /** - * @type {number|undefined} + * @abstract * @protected + * @param {boolean=} opt_all Return hit detection textures with regular ones. + * @returns {Array.} Textures. */ - this.width = undefined; -}; + getTextures(opt_all) {} + + /** + * @abstract + * @protected + * @returns {Array.} Textures. + */ + getHitDetectionTextures() {} +} inherits(WebGLTextureReplay, WebGLReplay); -/** - * @inheritDoc - */ -WebGLTextureReplay.prototype.getDeleteResourcesFunction = function(context) { - const verticesBuffer = this.verticesBuffer; - const indicesBuffer = this.indicesBuffer; - const textures = this.getTextures(true); - const gl = context.getGL(); - return function() { - if (!gl.isContextLost()) { - let i, ii; - for (i = 0, ii = textures.length; i < ii; ++i) { - gl.deleteTexture(textures[i]); - } - } - context.deleteBuffer(verticesBuffer); - context.deleteBuffer(indicesBuffer); - }; -}; - - -/** - * @param {Array.} flatCoordinates Flat coordinates. - * @param {number} offset Offset. - * @param {number} end End. - * @param {number} stride Stride. - * @return {number} My end. - * @protected - */ -WebGLTextureReplay.prototype.drawCoordinates = function(flatCoordinates, offset, end, stride) { - const anchorX = /** @type {number} */ (this.anchorX); - const anchorY = /** @type {number} */ (this.anchorY); - const height = /** @type {number} */ (this.height); - const imageHeight = /** @type {number} */ (this.imageHeight); - const imageWidth = /** @type {number} */ (this.imageWidth); - const opacity = /** @type {number} */ (this.opacity); - const originX = /** @type {number} */ (this.originX); - const originY = /** @type {number} */ (this.originY); - const rotateWithView = this.rotateWithView ? 1.0 : 0.0; - // this.rotation_ is anti-clockwise, but rotation is clockwise - const rotation = /** @type {number} */ (-this.rotation); - const scale = /** @type {number} */ (this.scale); - const width = /** @type {number} */ (this.width); - const cos = Math.cos(rotation); - const sin = Math.sin(rotation); - let numIndices = this.indices.length; - let numVertices = this.vertices.length; - let i, n, offsetX, offsetY, x, y; - for (i = offset; i < end; i += stride) { - x = flatCoordinates[i] - this.origin[0]; - y = flatCoordinates[i + 1] - this.origin[1]; - - // There are 4 vertices per [x, y] point, one for each corner of the - // rectangle we're going to draw. We'd use 1 vertex per [x, y] point if - // WebGL supported Geometry Shaders (which can emit new vertices), but that - // is not currently the case. - // - // And each vertex includes 8 values: the x and y coordinates, the x and - // y offsets used to calculate the position of the corner, the u and - // v texture coordinates for the corner, the opacity, and whether the - // the image should be rotated with the view (rotateWithView). - - n = numVertices / 8; - - // bottom-left corner - offsetX = -scale * anchorX; - offsetY = -scale * (height - anchorY); - this.vertices[numVertices++] = x; - this.vertices[numVertices++] = y; - this.vertices[numVertices++] = offsetX * cos - offsetY * sin; - this.vertices[numVertices++] = offsetX * sin + offsetY * cos; - this.vertices[numVertices++] = originX / imageWidth; - this.vertices[numVertices++] = (originY + height) / imageHeight; - this.vertices[numVertices++] = opacity; - this.vertices[numVertices++] = rotateWithView; - - // bottom-right corner - offsetX = scale * (width - anchorX); - offsetY = -scale * (height - anchorY); - this.vertices[numVertices++] = x; - this.vertices[numVertices++] = y; - this.vertices[numVertices++] = offsetX * cos - offsetY * sin; - this.vertices[numVertices++] = offsetX * sin + offsetY * cos; - this.vertices[numVertices++] = (originX + width) / imageWidth; - this.vertices[numVertices++] = (originY + height) / imageHeight; - this.vertices[numVertices++] = opacity; - this.vertices[numVertices++] = rotateWithView; - - // top-right corner - offsetX = scale * (width - anchorX); - offsetY = scale * anchorY; - this.vertices[numVertices++] = x; - this.vertices[numVertices++] = y; - this.vertices[numVertices++] = offsetX * cos - offsetY * sin; - this.vertices[numVertices++] = offsetX * sin + offsetY * cos; - this.vertices[numVertices++] = (originX + width) / imageWidth; - this.vertices[numVertices++] = originY / imageHeight; - this.vertices[numVertices++] = opacity; - this.vertices[numVertices++] = rotateWithView; - - // top-left corner - offsetX = -scale * anchorX; - offsetY = scale * anchorY; - this.vertices[numVertices++] = x; - this.vertices[numVertices++] = y; - this.vertices[numVertices++] = offsetX * cos - offsetY * sin; - this.vertices[numVertices++] = offsetX * sin + offsetY * cos; - this.vertices[numVertices++] = originX / imageWidth; - this.vertices[numVertices++] = originY / imageHeight; - this.vertices[numVertices++] = opacity; - this.vertices[numVertices++] = rotateWithView; - - this.indices[numIndices++] = n; - this.indices[numIndices++] = n + 1; - this.indices[numIndices++] = n + 2; - this.indices[numIndices++] = n; - this.indices[numIndices++] = n + 2; - this.indices[numIndices++] = n + 3; - } - - return numVertices; -}; - - -/** - * @protected - * @param {Array.} textures Textures. - * @param {Array.} images Images. - * @param {!Object.} texturePerImage Texture cache. - * @param {WebGLRenderingContext} gl Gl. - */ -WebGLTextureReplay.prototype.createTextures = function(textures, images, texturePerImage, gl) { - let texture, image, uid, i; - const ii = images.length; - for (i = 0; i < ii; ++i) { - image = images[i]; - - uid = getUid(image).toString(); - if (uid in texturePerImage) { - texture = texturePerImage[uid]; - } else { - texture = createTexture( - gl, image, CLAMP_TO_EDGE, CLAMP_TO_EDGE); - texturePerImage[uid] = texture; - } - textures[i] = texture; - } -}; - - -/** - * @inheritDoc - */ -WebGLTextureReplay.prototype.setUpProgram = function(gl, context, size, pixelRatio) { - // get the program - const program = context.getProgram(fragment, vertex); - - // get the locations - let locations; - if (!this.defaultLocations) { - locations = new Locations(gl, program); - this.defaultLocations = locations; - } else { - locations = this.defaultLocations; - } - - // use the program (FIXME: use the return value) - context.useProgram(program); - - // enable the vertex attrib arrays - gl.enableVertexAttribArray(locations.a_position); - gl.vertexAttribPointer(locations.a_position, 2, FLOAT, - false, 32, 0); - - gl.enableVertexAttribArray(locations.a_offsets); - gl.vertexAttribPointer(locations.a_offsets, 2, FLOAT, - false, 32, 8); - - gl.enableVertexAttribArray(locations.a_texCoord); - gl.vertexAttribPointer(locations.a_texCoord, 2, FLOAT, - false, 32, 16); - - gl.enableVertexAttribArray(locations.a_opacity); - gl.vertexAttribPointer(locations.a_opacity, 1, FLOAT, - false, 32, 24); - - gl.enableVertexAttribArray(locations.a_rotateWithView); - gl.vertexAttribPointer(locations.a_rotateWithView, 1, FLOAT, - false, 32, 28); - - return locations; -}; - - -/** - * @inheritDoc - */ -WebGLTextureReplay.prototype.shutDownProgram = function(gl, locations) { - gl.disableVertexAttribArray(locations.a_position); - gl.disableVertexAttribArray(locations.a_offsets); - gl.disableVertexAttribArray(locations.a_texCoord); - gl.disableVertexAttribArray(locations.a_opacity); - gl.disableVertexAttribArray(locations.a_rotateWithView); -}; - - -/** - * @inheritDoc - */ -WebGLTextureReplay.prototype.drawReplay = function(gl, context, skippedFeaturesHash, hitDetection) { - const textures = hitDetection ? this.getHitDetectionTextures() : this.getTextures(); - const groupIndices = hitDetection ? this.hitDetectionGroupIndices : this.groupIndices; - - if (!isEmpty(skippedFeaturesHash)) { - this.drawReplaySkipping(gl, context, skippedFeaturesHash, textures, groupIndices); - } else { - let i, ii, start; - for (i = 0, ii = textures.length, start = 0; i < ii; ++i) { - gl.bindTexture(TEXTURE_2D, textures[i]); - const end = groupIndices[i]; - this.drawElements(gl, context, start, end); - start = end; - } - } -}; - - -/** - * Draw the replay while paying attention to skipped features. - * - * This functions creates groups of features that can be drawn to together, - * so that the number of `drawElements` calls is minimized. - * - * For example given the following texture groups: - * - * Group 1: A B C - * Group 2: D [E] F G - * - * If feature E should be skipped, the following `drawElements` calls will be - * made: - * - * drawElements with feature A, B and C - * drawElements with feature D - * drawElements with feature F and G - * - * @protected - * @param {WebGLRenderingContext} gl gl. - * @param {module:ol/webgl/Context} context Context. - * @param {Object.} skippedFeaturesHash Ids of features - * to skip. - * @param {Array.} textures Textures. - * @param {Array.} groupIndices Texture group indices. - */ -WebGLTextureReplay.prototype.drawReplaySkipping = function(gl, context, skippedFeaturesHash, textures, - groupIndices) { - let featureIndex = 0; - - let i, ii; - for (i = 0, ii = textures.length; i < ii; ++i) { - gl.bindTexture(TEXTURE_2D, textures[i]); - const groupStart = (i > 0) ? groupIndices[i - 1] : 0; - const groupEnd = groupIndices[i]; - - let start = groupStart; - let end = groupStart; - while (featureIndex < this.startIndices.length && - this.startIndices[featureIndex] <= groupEnd) { - const feature = this.startIndicesFeature[featureIndex]; - - const featureUid = getUid(feature).toString(); - if (skippedFeaturesHash[featureUid] !== undefined) { - // feature should be skipped - if (start !== end) { - // draw the features so far - this.drawElements(gl, context, start, end); - } - // continue with the next feature - start = (featureIndex === this.startIndices.length - 1) ? - groupEnd : this.startIndices[featureIndex + 1]; - end = start; - } else { - // the feature is not skipped, augment the end index - end = (featureIndex === this.startIndices.length - 1) ? - groupEnd : this.startIndices[featureIndex + 1]; - } - featureIndex++; - } - - if (start !== end) { - // draw the remaining features (in case there was no skipped feature - // in this texture group, all features of a group are drawn together) - this.drawElements(gl, context, start, end); - } - } -}; - - -/** - * @inheritDoc - */ -WebGLTextureReplay.prototype.drawHitDetectionReplayOneByOne = function(gl, context, skippedFeaturesHash, - featureCallback, opt_hitExtent) { - let i, groupStart, start, end, feature, featureUid; - let featureIndex = this.startIndices.length - 1; - const hitDetectionTextures = this.getHitDetectionTextures(); - for (i = hitDetectionTextures.length - 1; i >= 0; --i) { - gl.bindTexture(TEXTURE_2D, hitDetectionTextures[i]); - groupStart = (i > 0) ? this.hitDetectionGroupIndices[i - 1] : 0; - end = this.hitDetectionGroupIndices[i]; - - // draw all features for this texture group - while (featureIndex >= 0 && - this.startIndices[featureIndex] >= groupStart) { - start = this.startIndices[featureIndex]; - feature = this.startIndicesFeature[featureIndex]; - featureUid = getUid(feature).toString(); - - if (skippedFeaturesHash[featureUid] === undefined && - feature.getGeometry() && - (opt_hitExtent === undefined || intersects( - /** @type {Array} */ (opt_hitExtent), - feature.getGeometry().getExtent()))) { - gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); - this.drawElements(gl, context, start, end); - - const result = featureCallback(feature); - if (result) { - return result; - } - } - - end = start; - featureIndex--; - } - } - return undefined; -}; - - -/** - * @inheritDoc - */ -WebGLTextureReplay.prototype.finish = function(context) { - this.anchorX = undefined; - this.anchorY = undefined; - this.height = undefined; - this.imageHeight = undefined; - this.imageWidth = undefined; - this.indices = null; - this.opacity = undefined; - this.originX = undefined; - this.originY = undefined; - this.rotateWithView = undefined; - this.rotation = undefined; - this.scale = undefined; - this.vertices = null; - this.width = undefined; -}; - - -/** - * @abstract - * @protected - * @param {boolean=} opt_all Return hit detection textures with regular ones. - * @returns {Array.} Textures. - */ -WebGLTextureReplay.prototype.getTextures = function(opt_all) {}; - - -/** - * @abstract - * @protected - * @returns {Array.} Textures. - */ -WebGLTextureReplay.prototype.getHitDetectionTextures = function() {}; export default WebGLTextureReplay; diff --git a/src/ol/renderer/Layer.js b/src/ol/renderer/Layer.js index 5e23741585..f6014665f3 100644 --- a/src/ol/renderer/Layer.js +++ b/src/ol/renderer/Layer.js @@ -16,18 +16,210 @@ import SourceState from '../source/State.js'; * @param {module:ol/layer/Layer} layer Layer. * @struct */ -const LayerRenderer = function(layer) { +class LayerRenderer { + constructor(layer) { - Observable.call(this); + Observable.call(this); + + /** + * @private + * @type {module:ol/layer/Layer} + */ + this.layer_ = layer; + + + } /** - * @private - * @type {module:ol/layer/Layer} + * Create a function that adds loaded tiles to the tile lookup. + * @param {module:ol/source/Tile} source Tile source. + * @param {module:ol/proj/Projection} projection Projection of the tiles. + * @param {Object.>} tiles Lookup of loaded tiles by zoom level. + * @return {function(number, module:ol/TileRange):boolean} A function that can be + * called with a zoom level and a tile range to add loaded tiles to the lookup. + * @protected */ - this.layer_ = layer; + createLoadedTileFinder(source, projection, tiles) { + return ( + /** + * @param {number} zoom Zoom level. + * @param {module:ol/TileRange} tileRange Tile range. + * @return {boolean} The tile range is fully loaded. + */ + function(zoom, tileRange) { + function callback(tile) { + if (!tiles[zoom]) { + tiles[zoom] = {}; + } + tiles[zoom][tile.tileCoord.toString()] = tile; + } + return source.forEachLoadedTile(projection, zoom, tileRange, callback); + } + ); + } + /** + * @return {module:ol/layer/Layer} Layer. + */ + getLayer() { + return this.layer_; + } -}; + /** + * Handle changes in image state. + * @param {module:ol/events/Event} event Image change event. + * @private + */ + handleImageChange_(event) { + const image = /** @type {module:ol/Image} */ (event.target); + if (image.getState() === ImageState.LOADED) { + this.renderIfReadyAndVisible(); + } + } + + /** + * Load the image if not already loaded, and register the image change + * listener if needed. + * @param {module:ol/ImageBase} image Image. + * @return {boolean} `true` if the image is already loaded, `false` otherwise. + * @protected + */ + loadImage(image) { + let imageState = image.getState(); + if (imageState != ImageState.LOADED && imageState != ImageState.ERROR) { + listen(image, EventType.CHANGE, this.handleImageChange_, this); + } + if (imageState == ImageState.IDLE) { + image.load(); + imageState = image.getState(); + } + return imageState == ImageState.LOADED; + } + + /** + * @protected + */ + renderIfReadyAndVisible() { + const layer = this.getLayer(); + if (layer.getVisible() && layer.getSourceState() == SourceState.READY) { + this.changed(); + } + } + + /** + * @param {module:ol/PluggableMap~FrameState} frameState Frame state. + * @param {module:ol/source/Tile} tileSource Tile source. + * @protected + */ + scheduleExpireCache(frameState, tileSource) { + if (tileSource.canExpireCache()) { + /** + * @param {module:ol/source/Tile} tileSource Tile source. + * @param {module:ol/PluggableMap} map Map. + * @param {module:ol/PluggableMap~FrameState} frameState Frame state. + */ + const postRenderFunction = function(tileSource, map, frameState) { + const tileSourceKey = getUid(tileSource).toString(); + if (tileSourceKey in frameState.usedTiles) { + tileSource.expireCache(frameState.viewState.projection, + frameState.usedTiles[tileSourceKey]); + } + }.bind(null, tileSource); + + frameState.postRenderFunctions.push( + /** @type {module:ol/PluggableMap~PostRenderFunction} */ (postRenderFunction) + ); + } + } + + /** + * @param {!Object.>} usedTiles Used tiles. + * @param {module:ol/source/Tile} tileSource Tile source. + * @param {number} z Z. + * @param {module:ol/TileRange} tileRange Tile range. + * @protected + */ + updateUsedTiles(usedTiles, tileSource, z, tileRange) { + // FIXME should we use tilesToDrawByZ instead? + const tileSourceKey = getUid(tileSource).toString(); + const zKey = z.toString(); + if (tileSourceKey in usedTiles) { + if (zKey in usedTiles[tileSourceKey]) { + usedTiles[tileSourceKey][zKey].extend(tileRange); + } else { + usedTiles[tileSourceKey][zKey] = tileRange; + } + } else { + usedTiles[tileSourceKey] = {}; + usedTiles[tileSourceKey][zKey] = tileRange; + } + } + + /** + * Manage tile pyramid. + * This function performs a number of functions related to the tiles at the + * current zoom and lower zoom levels: + * - registers idle tiles in frameState.wantedTiles so that they are not + * discarded by the tile queue + * - enqueues missing tiles + * @param {module:ol/PluggableMap~FrameState} frameState Frame state. + * @param {module:ol/source/Tile} tileSource Tile source. + * @param {module:ol/tilegrid/TileGrid} tileGrid Tile grid. + * @param {number} pixelRatio Pixel ratio. + * @param {module:ol/proj/Projection} projection Projection. + * @param {module:ol/extent~Extent} extent Extent. + * @param {number} currentZ Current Z. + * @param {number} preload Load low resolution tiles up to 'preload' levels. + * @param {function(this: T, module:ol/Tile)=} opt_tileCallback Tile callback. + * @param {T=} opt_this Object to use as `this` in `opt_tileCallback`. + * @protected + * @template T + */ + manageTilePyramid( + frameState, + tileSource, + tileGrid, + pixelRatio, + projection, + extent, + currentZ, + preload, + opt_tileCallback, + opt_this + ) { + const tileSourceKey = getUid(tileSource).toString(); + if (!(tileSourceKey in frameState.wantedTiles)) { + frameState.wantedTiles[tileSourceKey] = {}; + } + const wantedTiles = frameState.wantedTiles[tileSourceKey]; + const tileQueue = frameState.tileQueue; + const minZoom = tileGrid.getMinZoom(); + let tile, tileRange, tileResolution, x, y, z; + for (z = minZoom; z <= currentZ; ++z) { + tileRange = tileGrid.getTileRangeForExtentAndZ(extent, z, tileRange); + tileResolution = tileGrid.getResolution(z); + for (x = tileRange.minX; x <= tileRange.maxX; ++x) { + for (y = tileRange.minY; y <= tileRange.maxY; ++y) { + if (currentZ - z <= preload) { + tile = tileSource.getTile(z, x, y, pixelRatio, projection); + if (tile.getState() == TileState.IDLE) { + wantedTiles[tile.getKey()] = true; + if (!tileQueue.isKeyQueued(tile.getKey())) { + tileQueue.enqueue([tile, tileSourceKey, + tileGrid.getTileCoordCenter(tile.tileCoord), tileResolution]); + } + } + if (opt_tileCallback !== undefined) { + opt_tileCallback.call(opt_this, tile); + } + } else { + tileSource.useTile(z, x, y, projection); + } + } + } + } + } +} inherits(LayerRenderer, Observable); @@ -53,191 +245,4 @@ LayerRenderer.prototype.forEachFeatureAtCoordinate = UNDEFINED; LayerRenderer.prototype.hasFeatureAtCoordinate = FALSE; -/** - * Create a function that adds loaded tiles to the tile lookup. - * @param {module:ol/source/Tile} source Tile source. - * @param {module:ol/proj/Projection} projection Projection of the tiles. - * @param {Object.>} tiles Lookup of loaded tiles by zoom level. - * @return {function(number, module:ol/TileRange):boolean} A function that can be - * called with a zoom level and a tile range to add loaded tiles to the lookup. - * @protected - */ -LayerRenderer.prototype.createLoadedTileFinder = function(source, projection, tiles) { - return ( - /** - * @param {number} zoom Zoom level. - * @param {module:ol/TileRange} tileRange Tile range. - * @return {boolean} The tile range is fully loaded. - */ - function(zoom, tileRange) { - function callback(tile) { - if (!tiles[zoom]) { - tiles[zoom] = {}; - } - tiles[zoom][tile.tileCoord.toString()] = tile; - } - return source.forEachLoadedTile(projection, zoom, tileRange, callback); - } - ); -}; - - -/** - * @return {module:ol/layer/Layer} Layer. - */ -LayerRenderer.prototype.getLayer = function() { - return this.layer_; -}; - - -/** - * Handle changes in image state. - * @param {module:ol/events/Event} event Image change event. - * @private - */ -LayerRenderer.prototype.handleImageChange_ = function(event) { - const image = /** @type {module:ol/Image} */ (event.target); - if (image.getState() === ImageState.LOADED) { - this.renderIfReadyAndVisible(); - } -}; - - -/** - * Load the image if not already loaded, and register the image change - * listener if needed. - * @param {module:ol/ImageBase} image Image. - * @return {boolean} `true` if the image is already loaded, `false` otherwise. - * @protected - */ -LayerRenderer.prototype.loadImage = function(image) { - let imageState = image.getState(); - if (imageState != ImageState.LOADED && imageState != ImageState.ERROR) { - listen(image, EventType.CHANGE, this.handleImageChange_, this); - } - if (imageState == ImageState.IDLE) { - image.load(); - imageState = image.getState(); - } - return imageState == ImageState.LOADED; -}; - - -/** - * @protected - */ -LayerRenderer.prototype.renderIfReadyAndVisible = function() { - const layer = this.getLayer(); - if (layer.getVisible() && layer.getSourceState() == SourceState.READY) { - this.changed(); - } -}; - - -/** - * @param {module:ol/PluggableMap~FrameState} frameState Frame state. - * @param {module:ol/source/Tile} tileSource Tile source. - * @protected - */ -LayerRenderer.prototype.scheduleExpireCache = function(frameState, tileSource) { - if (tileSource.canExpireCache()) { - /** - * @param {module:ol/source/Tile} tileSource Tile source. - * @param {module:ol/PluggableMap} map Map. - * @param {module:ol/PluggableMap~FrameState} frameState Frame state. - */ - const postRenderFunction = function(tileSource, map, frameState) { - const tileSourceKey = getUid(tileSource).toString(); - if (tileSourceKey in frameState.usedTiles) { - tileSource.expireCache(frameState.viewState.projection, - frameState.usedTiles[tileSourceKey]); - } - }.bind(null, tileSource); - - frameState.postRenderFunctions.push( - /** @type {module:ol/PluggableMap~PostRenderFunction} */ (postRenderFunction) - ); - } -}; - - -/** - * @param {!Object.>} usedTiles Used tiles. - * @param {module:ol/source/Tile} tileSource Tile source. - * @param {number} z Z. - * @param {module:ol/TileRange} tileRange Tile range. - * @protected - */ -LayerRenderer.prototype.updateUsedTiles = function(usedTiles, tileSource, z, tileRange) { - // FIXME should we use tilesToDrawByZ instead? - const tileSourceKey = getUid(tileSource).toString(); - const zKey = z.toString(); - if (tileSourceKey in usedTiles) { - if (zKey in usedTiles[tileSourceKey]) { - usedTiles[tileSourceKey][zKey].extend(tileRange); - } else { - usedTiles[tileSourceKey][zKey] = tileRange; - } - } else { - usedTiles[tileSourceKey] = {}; - usedTiles[tileSourceKey][zKey] = tileRange; - } -}; - - -/** - * Manage tile pyramid. - * This function performs a number of functions related to the tiles at the - * current zoom and lower zoom levels: - * - registers idle tiles in frameState.wantedTiles so that they are not - * discarded by the tile queue - * - enqueues missing tiles - * @param {module:ol/PluggableMap~FrameState} frameState Frame state. - * @param {module:ol/source/Tile} tileSource Tile source. - * @param {module:ol/tilegrid/TileGrid} tileGrid Tile grid. - * @param {number} pixelRatio Pixel ratio. - * @param {module:ol/proj/Projection} projection Projection. - * @param {module:ol/extent~Extent} extent Extent. - * @param {number} currentZ Current Z. - * @param {number} preload Load low resolution tiles up to 'preload' levels. - * @param {function(this: T, module:ol/Tile)=} opt_tileCallback Tile callback. - * @param {T=} opt_this Object to use as `this` in `opt_tileCallback`. - * @protected - * @template T - */ -LayerRenderer.prototype.manageTilePyramid = function( - frameState, tileSource, tileGrid, pixelRatio, projection, extent, - currentZ, preload, opt_tileCallback, opt_this) { - const tileSourceKey = getUid(tileSource).toString(); - if (!(tileSourceKey in frameState.wantedTiles)) { - frameState.wantedTiles[tileSourceKey] = {}; - } - const wantedTiles = frameState.wantedTiles[tileSourceKey]; - const tileQueue = frameState.tileQueue; - const minZoom = tileGrid.getMinZoom(); - let tile, tileRange, tileResolution, x, y, z; - for (z = minZoom; z <= currentZ; ++z) { - tileRange = tileGrid.getTileRangeForExtentAndZ(extent, z, tileRange); - tileResolution = tileGrid.getResolution(z); - for (x = tileRange.minX; x <= tileRange.maxX; ++x) { - for (y = tileRange.minY; y <= tileRange.maxY; ++y) { - if (currentZ - z <= preload) { - tile = tileSource.getTile(z, x, y, pixelRatio, projection); - if (tile.getState() == TileState.IDLE) { - wantedTiles[tile.getKey()] = true; - if (!tileQueue.isKeyQueued(tile.getKey())) { - tileQueue.enqueue([tile, tileSourceKey, - tileGrid.getTileCoordCenter(tile.tileCoord), tileResolution]); - } - } - if (opt_tileCallback !== undefined) { - opt_tileCallback.call(opt_this, tile); - } - } else { - tileSource.useTile(z, x, y, projection); - } - } - } - } -}; export default LayerRenderer; diff --git a/src/ol/renderer/Map.js b/src/ol/renderer/Map.js index e1e64a40c1..f914b0b9ba 100644 --- a/src/ol/renderer/Map.js +++ b/src/ol/renderer/Map.js @@ -19,86 +19,308 @@ import {compose as composeTransform, invert as invertTransform, setFromArray as * @param {module:ol/PluggableMap} map Map. * @struct */ -const MapRenderer = function(map) { - Disposable.call(this); +class MapRenderer { + constructor(map) { + Disposable.call(this); + + /** + * @private + * @type {module:ol/PluggableMap} + */ + this.map_ = map; + + /** + * @private + * @type {!Object.} + */ + this.layerRenderers_ = {}; + + /** + * @private + * @type {Object.} + */ + this.layerRendererListeners_ = {}; + + /** + * @private + * @type {Array.} + */ + this.layerRendererConstructors_ = []; + + } /** - * @private - * @type {module:ol/PluggableMap} + * Register layer renderer constructors. + * @param {Array.} constructors Layer renderers. */ - this.map_ = map; + registerLayerRenderers(constructors) { + this.layerRendererConstructors_.push.apply(this.layerRendererConstructors_, constructors); + } /** - * @private - * @type {!Object.} + * Get the registered layer renderer constructors. + * @return {Array.} Registered layer renderers. */ - this.layerRenderers_ = {}; + getLayerRendererConstructors() { + return this.layerRendererConstructors_; + } /** - * @private - * @type {Object.} + * @param {module:ol/PluggableMap~FrameState} frameState FrameState. + * @protected */ - this.layerRendererListeners_ = {}; + calculateMatrices2D(frameState) { + const viewState = frameState.viewState; + const coordinateToPixelTransform = frameState.coordinateToPixelTransform; + const pixelToCoordinateTransform = frameState.pixelToCoordinateTransform; + + composeTransform(coordinateToPixelTransform, + frameState.size[0] / 2, frameState.size[1] / 2, + 1 / viewState.resolution, -1 / viewState.resolution, + -viewState.rotation, + -viewState.center[0], -viewState.center[1]); + + invertTransform( + transformSetFromArray(pixelToCoordinateTransform, coordinateToPixelTransform)); + } /** - * @private - * @type {Array.} + * Removes all layer renderers. */ - this.layerRendererConstructors_ = []; + removeLayerRenderers() { + for (const key in this.layerRenderers_) { + this.removeLayerRendererByKey_(key).dispose(); + } + } -}; + /** + * @param {module:ol/coordinate~Coordinate} coordinate Coordinate. + * @param {module:ol/PluggableMap~FrameState} frameState FrameState. + * @param {number} hitTolerance Hit tolerance in pixels. + * @param {function(this: S, (module:ol/Feature|module:ol/render/Feature), + * module:ol/layer/Layer): T} callback Feature callback. + * @param {S} thisArg Value to use as `this` when executing `callback`. + * @param {function(this: U, module:ol/layer/Layer): boolean} layerFilter Layer filter + * function, only layers which are visible and for which this function + * returns `true` will be tested for features. By default, all visible + * layers will be tested. + * @param {U} thisArg2 Value to use as `this` when executing `layerFilter`. + * @return {T|undefined} Callback result. + * @template S,T,U + */ + forEachFeatureAtCoordinate( + coordinate, + frameState, + hitTolerance, + callback, + thisArg, + layerFilter, + thisArg2 + ) { + let result; + const viewState = frameState.viewState; + const viewResolution = viewState.resolution; + + /** + * @param {module:ol/Feature|module:ol/render/Feature} feature Feature. + * @param {module:ol/layer/Layer} layer Layer. + * @return {?} Callback result. + */ + function forEachFeatureAtCoordinate(feature, layer) { + const key = getUid(feature).toString(); + const managed = frameState.layerStates[getUid(layer)].managed; + if (!(key in frameState.skippedFeatureUids && !managed)) { + return callback.call(thisArg, feature, managed ? layer : null); + } + } + + const projection = viewState.projection; + + let translatedCoordinate = coordinate; + if (projection.canWrapX()) { + const projectionExtent = projection.getExtent(); + const worldWidth = getWidth(projectionExtent); + const x = coordinate[0]; + if (x < projectionExtent[0] || x > projectionExtent[2]) { + const worldsAway = Math.ceil((projectionExtent[0] - x) / worldWidth); + translatedCoordinate = [x + worldWidth * worldsAway, coordinate[1]]; + } + } + + const layerStates = frameState.layerStatesArray; + const numLayers = layerStates.length; + let i; + for (i = numLayers - 1; i >= 0; --i) { + const layerState = layerStates[i]; + const layer = layerState.layer; + if (visibleAtResolution(layerState, viewResolution) && layerFilter.call(thisArg2, layer)) { + const layerRenderer = this.getLayerRenderer(layer); + if (layer.getSource()) { + result = layerRenderer.forEachFeatureAtCoordinate( + layer.getSource().getWrapX() ? translatedCoordinate : coordinate, + frameState, hitTolerance, forEachFeatureAtCoordinate, thisArg); + } + if (result) { + return result; + } + } + } + return undefined; + } + + /** + * @abstract + * @param {module:ol~Pixel} pixel Pixel. + * @param {module:ol/PluggableMap~FrameState} frameState FrameState. + * @param {number} hitTolerance Hit tolerance in pixels. + * @param {function(this: S, module:ol/layer/Layer, (Uint8ClampedArray|Uint8Array)): T} callback Layer + * callback. + * @param {S} thisArg Value to use as `this` when executing `callback`. + * @param {function(this: U, module:ol/layer/Layer): boolean} layerFilter Layer filter + * function, only layers which are visible and for which this function + * returns `true` will be tested for features. By default, all visible + * layers will be tested. + * @param {U} thisArg2 Value to use as `this` when executing `layerFilter`. + * @return {T|undefined} Callback result. + * @template S,T,U + */ + forEachLayerAtPixel(pixel, frameState, hitTolerance, callback, thisArg, layerFilter, thisArg2) {} + + /** + * @param {module:ol/coordinate~Coordinate} coordinate Coordinate. + * @param {module:ol/PluggableMap~FrameState} frameState FrameState. + * @param {number} hitTolerance Hit tolerance in pixels. + * @param {function(this: U, module:ol/layer/Layer): boolean} layerFilter Layer filter + * function, only layers which are visible and for which this function + * returns `true` will be tested for features. By default, all visible + * layers will be tested. + * @param {U} thisArg Value to use as `this` when executing `layerFilter`. + * @return {boolean} Is there a feature at the given coordinate? + * @template U + */ + hasFeatureAtCoordinate(coordinate, frameState, hitTolerance, layerFilter, thisArg) { + const hasFeature = this.forEachFeatureAtCoordinate( + coordinate, frameState, hitTolerance, TRUE, this, layerFilter, thisArg); + + return hasFeature !== undefined; + } + + /** + * @param {module:ol/layer/Layer} layer Layer. + * @protected + * @return {module:ol/renderer/Layer} Layer renderer. + */ + getLayerRenderer(layer) { + const layerKey = getUid(layer).toString(); + if (layerKey in this.layerRenderers_) { + return this.layerRenderers_[layerKey]; + } else { + let renderer; + for (let i = 0, ii = this.layerRendererConstructors_.length; i < ii; ++i) { + const candidate = this.layerRendererConstructors_[i]; + if (candidate['handles'](layer)) { + renderer = candidate['create'](this, layer); + break; + } + } + if (renderer) { + this.layerRenderers_[layerKey] = renderer; + this.layerRendererListeners_[layerKey] = listen(renderer, + EventType.CHANGE, this.handleLayerRendererChange_, this); + } else { + throw new Error('Unable to create renderer for layer: ' + layer.getType()); + } + return renderer; + } + } + + /** + * @param {string} layerKey Layer key. + * @protected + * @return {module:ol/renderer/Layer} Layer renderer. + */ + getLayerRendererByKey(layerKey) { + return this.layerRenderers_[layerKey]; + } + + /** + * @protected + * @return {Object.} Layer renderers. + */ + getLayerRenderers() { + return this.layerRenderers_; + } + + /** + * @return {module:ol/PluggableMap} Map. + */ + getMap() { + return this.map_; + } + + /** + * Handle changes in a layer renderer. + * @private + */ + handleLayerRendererChange_() { + this.map_.render(); + } + + /** + * @param {string} layerKey Layer key. + * @return {module:ol/renderer/Layer} Layer renderer. + * @private + */ + removeLayerRendererByKey_(layerKey) { + const layerRenderer = this.layerRenderers_[layerKey]; + delete this.layerRenderers_[layerKey]; + + unlistenByKey(this.layerRendererListeners_[layerKey]); + delete this.layerRendererListeners_[layerKey]; + + return layerRenderer; + } + + /** + * @param {module:ol/PluggableMap} map Map. + * @param {module:ol/PluggableMap~FrameState} frameState Frame state. + * @private + */ + removeUnusedLayerRenderers_(map, frameState) { + for (const layerKey in this.layerRenderers_) { + if (!frameState || !(layerKey in frameState.layerStates)) { + this.removeLayerRendererByKey_(layerKey).dispose(); + } + } + } + + /** + * @param {module:ol/PluggableMap~FrameState} frameState Frame state. + * @protected + */ + scheduleExpireIconCache(frameState) { + frameState.postRenderFunctions.push(/** @type {module:ol/PluggableMap~PostRenderFunction} */ (expireIconCache)); + } + + /** + * @param {!module:ol/PluggableMap~FrameState} frameState Frame state. + * @protected + */ + scheduleRemoveUnusedLayerRenderers(frameState) { + for (const layerKey in this.layerRenderers_) { + if (!(layerKey in frameState.layerStates)) { + frameState.postRenderFunctions.push( + /** @type {module:ol/PluggableMap~PostRenderFunction} */ (this.removeUnusedLayerRenderers_.bind(this)) + ); + return; + } + } + } +} inherits(MapRenderer, Disposable); -/** - * Register layer renderer constructors. - * @param {Array.} constructors Layer renderers. - */ -MapRenderer.prototype.registerLayerRenderers = function(constructors) { - this.layerRendererConstructors_.push.apply(this.layerRendererConstructors_, constructors); -}; - - -/** - * Get the registered layer renderer constructors. - * @return {Array.} Registered layer renderers. - */ -MapRenderer.prototype.getLayerRendererConstructors = function() { - return this.layerRendererConstructors_; -}; - - -/** - * @param {module:ol/PluggableMap~FrameState} frameState FrameState. - * @protected - */ -MapRenderer.prototype.calculateMatrices2D = function(frameState) { - const viewState = frameState.viewState; - const coordinateToPixelTransform = frameState.coordinateToPixelTransform; - const pixelToCoordinateTransform = frameState.pixelToCoordinateTransform; - - composeTransform(coordinateToPixelTransform, - frameState.size[0] / 2, frameState.size[1] / 2, - 1 / viewState.resolution, -1 / viewState.resolution, - -viewState.rotation, - -viewState.center[0], -viewState.center[1]); - - invertTransform( - transformSetFromArray(pixelToCoordinateTransform, coordinateToPixelTransform)); -}; - - -/** - * Removes all layer renderers. - */ -MapRenderer.prototype.removeLayerRenderers = function() { - for (const key in this.layerRenderers_) { - this.removeLayerRendererByKey_(key).dispose(); - } -}; - - /** * @param {module:ol/PluggableMap} map Map. * @param {module:ol/PluggableMap~FrameState} frameState Frame state. @@ -108,197 +330,6 @@ function expireIconCache(map, frameState) { } -/** - * @param {module:ol/coordinate~Coordinate} coordinate Coordinate. - * @param {module:ol/PluggableMap~FrameState} frameState FrameState. - * @param {number} hitTolerance Hit tolerance in pixels. - * @param {function(this: S, (module:ol/Feature|module:ol/render/Feature), - * module:ol/layer/Layer): T} callback Feature callback. - * @param {S} thisArg Value to use as `this` when executing `callback`. - * @param {function(this: U, module:ol/layer/Layer): boolean} layerFilter Layer filter - * function, only layers which are visible and for which this function - * returns `true` will be tested for features. By default, all visible - * layers will be tested. - * @param {U} thisArg2 Value to use as `this` when executing `layerFilter`. - * @return {T|undefined} Callback result. - * @template S,T,U - */ -MapRenderer.prototype.forEachFeatureAtCoordinate = function(coordinate, frameState, hitTolerance, callback, thisArg, - layerFilter, thisArg2) { - let result; - const viewState = frameState.viewState; - const viewResolution = viewState.resolution; - - /** - * @param {module:ol/Feature|module:ol/render/Feature} feature Feature. - * @param {module:ol/layer/Layer} layer Layer. - * @return {?} Callback result. - */ - function forEachFeatureAtCoordinate(feature, layer) { - const key = getUid(feature).toString(); - const managed = frameState.layerStates[getUid(layer)].managed; - if (!(key in frameState.skippedFeatureUids && !managed)) { - return callback.call(thisArg, feature, managed ? layer : null); - } - } - - const projection = viewState.projection; - - let translatedCoordinate = coordinate; - if (projection.canWrapX()) { - const projectionExtent = projection.getExtent(); - const worldWidth = getWidth(projectionExtent); - const x = coordinate[0]; - if (x < projectionExtent[0] || x > projectionExtent[2]) { - const worldsAway = Math.ceil((projectionExtent[0] - x) / worldWidth); - translatedCoordinate = [x + worldWidth * worldsAway, coordinate[1]]; - } - } - - const layerStates = frameState.layerStatesArray; - const numLayers = layerStates.length; - let i; - for (i = numLayers - 1; i >= 0; --i) { - const layerState = layerStates[i]; - const layer = layerState.layer; - if (visibleAtResolution(layerState, viewResolution) && layerFilter.call(thisArg2, layer)) { - const layerRenderer = this.getLayerRenderer(layer); - if (layer.getSource()) { - result = layerRenderer.forEachFeatureAtCoordinate( - layer.getSource().getWrapX() ? translatedCoordinate : coordinate, - frameState, hitTolerance, forEachFeatureAtCoordinate, thisArg); - } - if (result) { - return result; - } - } - } - return undefined; -}; - - -/** - * @abstract - * @param {module:ol~Pixel} pixel Pixel. - * @param {module:ol/PluggableMap~FrameState} frameState FrameState. - * @param {number} hitTolerance Hit tolerance in pixels. - * @param {function(this: S, module:ol/layer/Layer, (Uint8ClampedArray|Uint8Array)): T} callback Layer - * callback. - * @param {S} thisArg Value to use as `this` when executing `callback`. - * @param {function(this: U, module:ol/layer/Layer): boolean} layerFilter Layer filter - * function, only layers which are visible and for which this function - * returns `true` will be tested for features. By default, all visible - * layers will be tested. - * @param {U} thisArg2 Value to use as `this` when executing `layerFilter`. - * @return {T|undefined} Callback result. - * @template S,T,U - */ -MapRenderer.prototype.forEachLayerAtPixel = function(pixel, frameState, hitTolerance, callback, thisArg, - layerFilter, thisArg2) {}; - - -/** - * @param {module:ol/coordinate~Coordinate} coordinate Coordinate. - * @param {module:ol/PluggableMap~FrameState} frameState FrameState. - * @param {number} hitTolerance Hit tolerance in pixels. - * @param {function(this: U, module:ol/layer/Layer): boolean} layerFilter Layer filter - * function, only layers which are visible and for which this function - * returns `true` will be tested for features. By default, all visible - * layers will be tested. - * @param {U} thisArg Value to use as `this` when executing `layerFilter`. - * @return {boolean} Is there a feature at the given coordinate? - * @template U - */ -MapRenderer.prototype.hasFeatureAtCoordinate = function(coordinate, frameState, hitTolerance, layerFilter, thisArg) { - const hasFeature = this.forEachFeatureAtCoordinate( - coordinate, frameState, hitTolerance, TRUE, this, layerFilter, thisArg); - - return hasFeature !== undefined; -}; - - -/** - * @param {module:ol/layer/Layer} layer Layer. - * @protected - * @return {module:ol/renderer/Layer} Layer renderer. - */ -MapRenderer.prototype.getLayerRenderer = function(layer) { - const layerKey = getUid(layer).toString(); - if (layerKey in this.layerRenderers_) { - return this.layerRenderers_[layerKey]; - } else { - let renderer; - for (let i = 0, ii = this.layerRendererConstructors_.length; i < ii; ++i) { - const candidate = this.layerRendererConstructors_[i]; - if (candidate['handles'](layer)) { - renderer = candidate['create'](this, layer); - break; - } - } - if (renderer) { - this.layerRenderers_[layerKey] = renderer; - this.layerRendererListeners_[layerKey] = listen(renderer, - EventType.CHANGE, this.handleLayerRendererChange_, this); - } else { - throw new Error('Unable to create renderer for layer: ' + layer.getType()); - } - return renderer; - } -}; - - -/** - * @param {string} layerKey Layer key. - * @protected - * @return {module:ol/renderer/Layer} Layer renderer. - */ -MapRenderer.prototype.getLayerRendererByKey = function(layerKey) { - return this.layerRenderers_[layerKey]; -}; - - -/** - * @protected - * @return {Object.} Layer renderers. - */ -MapRenderer.prototype.getLayerRenderers = function() { - return this.layerRenderers_; -}; - - -/** - * @return {module:ol/PluggableMap} Map. - */ -MapRenderer.prototype.getMap = function() { - return this.map_; -}; - - -/** - * Handle changes in a layer renderer. - * @private - */ -MapRenderer.prototype.handleLayerRendererChange_ = function() { - this.map_.render(); -}; - - -/** - * @param {string} layerKey Layer key. - * @return {module:ol/renderer/Layer} Layer renderer. - * @private - */ -MapRenderer.prototype.removeLayerRendererByKey_ = function(layerKey) { - const layerRenderer = this.layerRenderers_[layerKey]; - delete this.layerRenderers_[layerKey]; - - unlistenByKey(this.layerRendererListeners_[layerKey]); - delete this.layerRendererListeners_[layerKey]; - - return layerRenderer; -}; - - /** * Render. * @param {?module:ol/PluggableMap~FrameState} frameState Frame state. @@ -306,45 +337,6 @@ MapRenderer.prototype.removeLayerRendererByKey_ = function(layerKey) { MapRenderer.prototype.renderFrame = UNDEFINED; -/** - * @param {module:ol/PluggableMap} map Map. - * @param {module:ol/PluggableMap~FrameState} frameState Frame state. - * @private - */ -MapRenderer.prototype.removeUnusedLayerRenderers_ = function(map, frameState) { - for (const layerKey in this.layerRenderers_) { - if (!frameState || !(layerKey in frameState.layerStates)) { - this.removeLayerRendererByKey_(layerKey).dispose(); - } - } -}; - - -/** - * @param {module:ol/PluggableMap~FrameState} frameState Frame state. - * @protected - */ -MapRenderer.prototype.scheduleExpireIconCache = function(frameState) { - frameState.postRenderFunctions.push(/** @type {module:ol/PluggableMap~PostRenderFunction} */ (expireIconCache)); -}; - - -/** - * @param {!module:ol/PluggableMap~FrameState} frameState Frame state. - * @protected - */ -MapRenderer.prototype.scheduleRemoveUnusedLayerRenderers = function(frameState) { - for (const layerKey in this.layerRenderers_) { - if (!(layerKey in frameState.layerStates)) { - frameState.postRenderFunctions.push( - /** @type {module:ol/PluggableMap~PostRenderFunction} */ (this.removeUnusedLayerRenderers_.bind(this)) - ); - return; - } - } -}; - - /** * @param {module:ol/layer/Layer~State} state1 First layer state. * @param {module:ol/layer/Layer~State} state2 Second layer state. diff --git a/src/ol/renderer/canvas/ImageLayer.js b/src/ol/renderer/canvas/ImageLayer.js index a9f4b414b6..f0006e4621 100644 --- a/src/ol/renderer/canvas/ImageLayer.js +++ b/src/ol/renderer/canvas/ImageLayer.js @@ -20,44 +20,172 @@ import {create as createTransform, compose as composeTransform} from '../../tran * @param {module:ol/layer/Image|module:ol/layer/Vector} imageLayer Image or vector layer. * @api */ -const CanvasImageLayerRenderer = function(imageLayer) { +class CanvasImageLayerRenderer { + constructor(imageLayer) { - IntermediateCanvasRenderer.call(this, imageLayer); + IntermediateCanvasRenderer.call(this, imageLayer); - /** - * @private - * @type {?module:ol/ImageBase} - */ - this.image_ = null; + /** + * @private + * @type {?module:ol/ImageBase} + */ + this.image_ = null; - /** - * @private - * @type {module:ol/transform~Transform} - */ - this.imageTransform_ = createTransform(); + /** + * @private + * @type {module:ol/transform~Transform} + */ + this.imageTransform_ = createTransform(); - /** - * @type {!Array.} - */ - this.skippedFeatures_ = []; + /** + * @type {!Array.} + */ + this.skippedFeatures_ = []; - /** - * @private - * @type {module:ol/renderer/canvas/VectorLayer} - */ - this.vectorRenderer_ = null; + /** + * @private + * @type {module:ol/renderer/canvas/VectorLayer} + */ + this.vectorRenderer_ = null; - if (imageLayer.getType() === LayerType.VECTOR) { - for (let i = 0, ii = layerRendererConstructors.length; i < ii; ++i) { - const ctor = layerRendererConstructors[i]; - if (ctor !== CanvasImageLayerRenderer && ctor['handles'](imageLayer)) { - this.vectorRenderer_ = new ctor(imageLayer); - break; + if (imageLayer.getType() === LayerType.VECTOR) { + for (let i = 0, ii = layerRendererConstructors.length; i < ii; ++i) { + const ctor = layerRendererConstructors[i]; + if (ctor !== CanvasImageLayerRenderer && ctor['handles'](imageLayer)) { + this.vectorRenderer_ = new ctor(imageLayer); + break; + } } } + } -}; + /** + * @inheritDoc + */ + disposeInternal() { + if (this.vectorRenderer_) { + this.vectorRenderer_.dispose(); + } + IntermediateCanvasRenderer.prototype.disposeInternal.call(this); + } + + /** + * @inheritDoc + */ + getImage() { + return !this.image_ ? null : this.image_.getImage(); + } + + /** + * @inheritDoc + */ + getImageTransform() { + return this.imageTransform_; + } + + /** + * @inheritDoc + */ + prepareFrame(frameState, layerState) { + + const pixelRatio = frameState.pixelRatio; + const size = frameState.size; + const viewState = frameState.viewState; + const viewCenter = viewState.center; + const viewResolution = viewState.resolution; + + let image; + const imageLayer = /** @type {module:ol/layer/Image} */ (this.getLayer()); + const imageSource = imageLayer.getSource(); + + const hints = frameState.viewHints; + + const vectorRenderer = this.vectorRenderer_; + let renderedExtent = frameState.extent; + if (!vectorRenderer && layerState.extent !== undefined) { + renderedExtent = getIntersection(renderedExtent, layerState.extent); + } + + if (!hints[ViewHint.ANIMATING] && !hints[ViewHint.INTERACTING] && + !isEmpty(renderedExtent)) { + let projection = viewState.projection; + if (!ENABLE_RASTER_REPROJECTION) { + const sourceProjection = imageSource.getProjection(); + if (sourceProjection) { + projection = sourceProjection; + } + } + let skippedFeatures = this.skippedFeatures_; + if (vectorRenderer) { + const context = vectorRenderer.context; + const imageFrameState = /** @type {module:ol/PluggableMap~FrameState} */ (assign({}, frameState, { + size: [ + getWidth(renderedExtent) / viewResolution, + getHeight(renderedExtent) / viewResolution + ], + viewState: /** @type {module:ol/View~State} */ (assign({}, frameState.viewState, { + rotation: 0 + })) + })); + const newSkippedFeatures = Object.keys(imageFrameState.skippedFeatureUids).sort(); + image = new ImageCanvas(renderedExtent, viewResolution, pixelRatio, context.canvas, function(callback) { + if (vectorRenderer.prepareFrame(imageFrameState, layerState) && + (vectorRenderer.replayGroupChanged || + !equals(skippedFeatures, newSkippedFeatures))) { + context.canvas.width = imageFrameState.size[0] * pixelRatio; + context.canvas.height = imageFrameState.size[1] * pixelRatio; + vectorRenderer.compose(context, imageFrameState, layerState); + skippedFeatures = newSkippedFeatures; + callback(); + } + }); + } else { + image = imageSource.getImage( + renderedExtent, viewResolution, pixelRatio, projection); + } + if (image && this.loadImage(image)) { + this.image_ = image; + this.skippedFeatures_ = skippedFeatures; + } + } + + if (this.image_) { + image = this.image_; + const imageExtent = image.getExtent(); + const imageResolution = image.getResolution(); + const imagePixelRatio = image.getPixelRatio(); + const scale = pixelRatio * imageResolution / + (viewResolution * imagePixelRatio); + const transform = composeTransform(this.imageTransform_, + pixelRatio * size[0] / 2, pixelRatio * size[1] / 2, + scale, scale, + 0, + imagePixelRatio * (imageExtent[0] - viewCenter[0]) / imageResolution, + imagePixelRatio * (viewCenter[1] - imageExtent[3]) / imageResolution); + composeTransform(this.coordinateToCanvasPixelTransform, + pixelRatio * size[0] / 2 - transform[4], pixelRatio * size[1] / 2 - transform[5], + pixelRatio / viewResolution, -pixelRatio / viewResolution, + 0, + -viewCenter[0], -viewCenter[1]); + + this.renderedResolution = imageResolution * pixelRatio / imagePixelRatio; + } + + return !!this.image_; + } + + /** + * @inheritDoc + */ + forEachFeatureAtCoordinate(coordinate, frameState, hitTolerance, callback, thisArg) { + if (this.vectorRenderer_) { + return this.vectorRenderer_.forEachFeatureAtCoordinate(coordinate, frameState, hitTolerance, callback, thisArg); + } else { + return IntermediateCanvasRenderer.prototype.forEachFeatureAtCoordinate.call(this, coordinate, frameState, hitTolerance, callback, thisArg); + } + } +} inherits(CanvasImageLayerRenderer, IntermediateCanvasRenderer); @@ -85,135 +213,4 @@ CanvasImageLayerRenderer['create'] = function(mapRenderer, layer) { }; -/** - * @inheritDoc - */ -CanvasImageLayerRenderer.prototype.disposeInternal = function() { - if (this.vectorRenderer_) { - this.vectorRenderer_.dispose(); - } - IntermediateCanvasRenderer.prototype.disposeInternal.call(this); -}; - - -/** - * @inheritDoc - */ -CanvasImageLayerRenderer.prototype.getImage = function() { - return !this.image_ ? null : this.image_.getImage(); -}; - - -/** - * @inheritDoc - */ -CanvasImageLayerRenderer.prototype.getImageTransform = function() { - return this.imageTransform_; -}; - - -/** - * @inheritDoc - */ -CanvasImageLayerRenderer.prototype.prepareFrame = function(frameState, layerState) { - - const pixelRatio = frameState.pixelRatio; - const size = frameState.size; - const viewState = frameState.viewState; - const viewCenter = viewState.center; - const viewResolution = viewState.resolution; - - let image; - const imageLayer = /** @type {module:ol/layer/Image} */ (this.getLayer()); - const imageSource = imageLayer.getSource(); - - const hints = frameState.viewHints; - - const vectorRenderer = this.vectorRenderer_; - let renderedExtent = frameState.extent; - if (!vectorRenderer && layerState.extent !== undefined) { - renderedExtent = getIntersection(renderedExtent, layerState.extent); - } - - if (!hints[ViewHint.ANIMATING] && !hints[ViewHint.INTERACTING] && - !isEmpty(renderedExtent)) { - let projection = viewState.projection; - if (!ENABLE_RASTER_REPROJECTION) { - const sourceProjection = imageSource.getProjection(); - if (sourceProjection) { - projection = sourceProjection; - } - } - let skippedFeatures = this.skippedFeatures_; - if (vectorRenderer) { - const context = vectorRenderer.context; - const imageFrameState = /** @type {module:ol/PluggableMap~FrameState} */ (assign({}, frameState, { - size: [ - getWidth(renderedExtent) / viewResolution, - getHeight(renderedExtent) / viewResolution - ], - viewState: /** @type {module:ol/View~State} */ (assign({}, frameState.viewState, { - rotation: 0 - })) - })); - const newSkippedFeatures = Object.keys(imageFrameState.skippedFeatureUids).sort(); - image = new ImageCanvas(renderedExtent, viewResolution, pixelRatio, context.canvas, function(callback) { - if (vectorRenderer.prepareFrame(imageFrameState, layerState) && - (vectorRenderer.replayGroupChanged || - !equals(skippedFeatures, newSkippedFeatures))) { - context.canvas.width = imageFrameState.size[0] * pixelRatio; - context.canvas.height = imageFrameState.size[1] * pixelRatio; - vectorRenderer.compose(context, imageFrameState, layerState); - skippedFeatures = newSkippedFeatures; - callback(); - } - }); - } else { - image = imageSource.getImage( - renderedExtent, viewResolution, pixelRatio, projection); - } - if (image && this.loadImage(image)) { - this.image_ = image; - this.skippedFeatures_ = skippedFeatures; - } - } - - if (this.image_) { - image = this.image_; - const imageExtent = image.getExtent(); - const imageResolution = image.getResolution(); - const imagePixelRatio = image.getPixelRatio(); - const scale = pixelRatio * imageResolution / - (viewResolution * imagePixelRatio); - const transform = composeTransform(this.imageTransform_, - pixelRatio * size[0] / 2, pixelRatio * size[1] / 2, - scale, scale, - 0, - imagePixelRatio * (imageExtent[0] - viewCenter[0]) / imageResolution, - imagePixelRatio * (viewCenter[1] - imageExtent[3]) / imageResolution); - composeTransform(this.coordinateToCanvasPixelTransform, - pixelRatio * size[0] / 2 - transform[4], pixelRatio * size[1] / 2 - transform[5], - pixelRatio / viewResolution, -pixelRatio / viewResolution, - 0, - -viewCenter[0], -viewCenter[1]); - - this.renderedResolution = imageResolution * pixelRatio / imagePixelRatio; - } - - return !!this.image_; -}; - - -/** - * @inheritDoc - */ -CanvasImageLayerRenderer.prototype.forEachFeatureAtCoordinate = function(coordinate, frameState, hitTolerance, callback, thisArg) { - if (this.vectorRenderer_) { - return this.vectorRenderer_.forEachFeatureAtCoordinate(coordinate, frameState, hitTolerance, callback, thisArg); - } else { - return IntermediateCanvasRenderer.prototype.forEachFeatureAtCoordinate.call(this, coordinate, frameState, hitTolerance, callback, thisArg); - } -}; - - export default CanvasImageLayerRenderer; diff --git a/src/ol/renderer/canvas/IntermediateCanvas.js b/src/ol/renderer/canvas/IntermediateCanvas.js index 2ccfdb8feb..f2147f829d 100644 --- a/src/ol/renderer/canvas/IntermediateCanvas.js +++ b/src/ol/renderer/canvas/IntermediateCanvas.js @@ -15,137 +15,135 @@ import {create as createTransform, apply as applyTransform} from '../../transfor * @extends {module:ol/renderer/canvas/Layer} * @param {module:ol/layer/Layer} layer Layer. */ -const IntermediateCanvasRenderer = function(layer) { +class IntermediateCanvasRenderer { + constructor(layer) { - CanvasLayerRenderer.call(this, layer); + CanvasLayerRenderer.call(this, layer); + + /** + * @protected + * @type {module:ol/transform~Transform} + */ + this.coordinateToCanvasPixelTransform = createTransform(); + + /** + * @private + * @type {CanvasRenderingContext2D} + */ + this.hitCanvasContext_ = null; + + } /** - * @protected - * @type {module:ol/transform~Transform} + * @inheritDoc */ - this.coordinateToCanvasPixelTransform = createTransform(); + composeFrame(frameState, layerState, context) { + + this.preCompose(context, frameState); + + const image = this.getImage(); + if (image) { + + // clipped rendering if layer extent is set + const extent = layerState.extent; + const clipped = extent !== undefined && + !containsExtent(extent, frameState.extent) && + intersects(extent, frameState.extent); + if (clipped) { + this.clip(context, frameState, /** @type {module:ol/extent~Extent} */ (extent)); + } + + const imageTransform = this.getImageTransform(); + // for performance reasons, context.save / context.restore is not used + // to save and restore the transformation matrix and the opacity. + // see http://jsperf.com/context-save-restore-versus-variable + const alpha = context.globalAlpha; + context.globalAlpha = layerState.opacity; + + // for performance reasons, context.setTransform is only used + // when the view is rotated. see http://jsperf.com/canvas-transform + const dx = imageTransform[4]; + const dy = imageTransform[5]; + const dw = image.width * imageTransform[0]; + const dh = image.height * imageTransform[3]; + context.drawImage(image, 0, 0, +image.width, +image.height, + Math.round(dx), Math.round(dy), Math.round(dw), Math.round(dh)); + context.globalAlpha = alpha; + + if (clipped) { + context.restore(); + } + } + + this.postCompose(context, frameState, layerState); + } /** - * @private - * @type {CanvasRenderingContext2D} + * @abstract + * @return {HTMLCanvasElement|HTMLVideoElement|HTMLImageElement} Canvas. */ - this.hitCanvasContext_ = null; + getImage() {} -}; + /** + * @abstract + * @return {!module:ol/transform~Transform} Image transform. + */ + getImageTransform() {} + + /** + * @inheritDoc + */ + forEachFeatureAtCoordinate(coordinate, frameState, hitTolerance, callback, thisArg) { + const layer = this.getLayer(); + const source = layer.getSource(); + const resolution = frameState.viewState.resolution; + const rotation = frameState.viewState.rotation; + const skippedFeatureUids = frameState.skippedFeatureUids; + return source.forEachFeatureAtCoordinate( + coordinate, resolution, rotation, hitTolerance, skippedFeatureUids, + /** + * @param {module:ol/Feature|module:ol/render/Feature} feature Feature. + * @return {?} Callback result. + */ + function(feature) { + return callback.call(thisArg, feature, layer); + }); + } + + /** + * @inheritDoc + */ + forEachLayerAtCoordinate(coordinate, frameState, hitTolerance, callback, thisArg) { + if (!this.getImage()) { + return undefined; + } + + if (this.getLayer().getSource().forEachFeatureAtCoordinate !== UNDEFINED) { + // for ImageCanvas sources use the original hit-detection logic, + // so that for example also transparent polygons are detected + return CanvasLayerRenderer.prototype.forEachLayerAtCoordinate.apply(this, arguments); + } else { + const pixel = applyTransform(this.coordinateToCanvasPixelTransform, coordinate.slice()); + scaleCoordinate(pixel, frameState.viewState.resolution / this.renderedResolution); + + if (!this.hitCanvasContext_) { + this.hitCanvasContext_ = createCanvasContext2D(1, 1); + } + + this.hitCanvasContext_.clearRect(0, 0, 1, 1); + this.hitCanvasContext_.drawImage(this.getImage(), pixel[0], pixel[1], 1, 1, 0, 0, 1, 1); + + const imageData = this.hitCanvasContext_.getImageData(0, 0, 1, 1).data; + if (imageData[3] > 0) { + return callback.call(thisArg, this.getLayer(), imageData); + } else { + return undefined; + } + } + } +} inherits(IntermediateCanvasRenderer, CanvasLayerRenderer); -/** - * @inheritDoc - */ -IntermediateCanvasRenderer.prototype.composeFrame = function(frameState, layerState, context) { - - this.preCompose(context, frameState); - - const image = this.getImage(); - if (image) { - - // clipped rendering if layer extent is set - const extent = layerState.extent; - const clipped = extent !== undefined && - !containsExtent(extent, frameState.extent) && - intersects(extent, frameState.extent); - if (clipped) { - this.clip(context, frameState, /** @type {module:ol/extent~Extent} */ (extent)); - } - - const imageTransform = this.getImageTransform(); - // for performance reasons, context.save / context.restore is not used - // to save and restore the transformation matrix and the opacity. - // see http://jsperf.com/context-save-restore-versus-variable - const alpha = context.globalAlpha; - context.globalAlpha = layerState.opacity; - - // for performance reasons, context.setTransform is only used - // when the view is rotated. see http://jsperf.com/canvas-transform - const dx = imageTransform[4]; - const dy = imageTransform[5]; - const dw = image.width * imageTransform[0]; - const dh = image.height * imageTransform[3]; - context.drawImage(image, 0, 0, +image.width, +image.height, - Math.round(dx), Math.round(dy), Math.round(dw), Math.round(dh)); - context.globalAlpha = alpha; - - if (clipped) { - context.restore(); - } - } - - this.postCompose(context, frameState, layerState); -}; - - -/** - * @abstract - * @return {HTMLCanvasElement|HTMLVideoElement|HTMLImageElement} Canvas. - */ -IntermediateCanvasRenderer.prototype.getImage = function() {}; - - -/** - * @abstract - * @return {!module:ol/transform~Transform} Image transform. - */ -IntermediateCanvasRenderer.prototype.getImageTransform = function() {}; - - -/** - * @inheritDoc - */ -IntermediateCanvasRenderer.prototype.forEachFeatureAtCoordinate = function(coordinate, frameState, hitTolerance, callback, thisArg) { - const layer = this.getLayer(); - const source = layer.getSource(); - const resolution = frameState.viewState.resolution; - const rotation = frameState.viewState.rotation; - const skippedFeatureUids = frameState.skippedFeatureUids; - return source.forEachFeatureAtCoordinate( - coordinate, resolution, rotation, hitTolerance, skippedFeatureUids, - /** - * @param {module:ol/Feature|module:ol/render/Feature} feature Feature. - * @return {?} Callback result. - */ - function(feature) { - return callback.call(thisArg, feature, layer); - }); -}; - - -/** - * @inheritDoc - */ -IntermediateCanvasRenderer.prototype.forEachLayerAtCoordinate = function(coordinate, frameState, hitTolerance, callback, thisArg) { - if (!this.getImage()) { - return undefined; - } - - if (this.getLayer().getSource().forEachFeatureAtCoordinate !== UNDEFINED) { - // for ImageCanvas sources use the original hit-detection logic, - // so that for example also transparent polygons are detected - return CanvasLayerRenderer.prototype.forEachLayerAtCoordinate.apply(this, arguments); - } else { - const pixel = applyTransform(this.coordinateToCanvasPixelTransform, coordinate.slice()); - scaleCoordinate(pixel, frameState.viewState.resolution / this.renderedResolution); - - if (!this.hitCanvasContext_) { - this.hitCanvasContext_ = createCanvasContext2D(1, 1); - } - - this.hitCanvasContext_.clearRect(0, 0, 1, 1); - this.hitCanvasContext_.drawImage(this.getImage(), pixel[0], pixel[1], 1, 1, 0, 0, 1, 1); - - const imageData = this.hitCanvasContext_.getImageData(0, 0, 1, 1).data; - if (imageData[3] > 0) { - return callback.call(thisArg, this.getLayer(), imageData); - } else { - return undefined; - } - } -}; - export default IntermediateCanvasRenderer; diff --git a/src/ol/renderer/canvas/Layer.js b/src/ol/renderer/canvas/Layer.js index f07024db51..30d1d166b8 100644 --- a/src/ol/renderer/canvas/Layer.js +++ b/src/ol/renderer/canvas/Layer.js @@ -17,175 +17,171 @@ import {create as createTransform, apply as applyTransform, compose as composeTr * @extends {module:ol/renderer/Layer} * @param {module:ol/layer/Layer} layer Layer. */ -const CanvasLayerRenderer = function(layer) { +class CanvasLayerRenderer { + constructor(layer) { - LayerRenderer.call(this, layer); + LayerRenderer.call(this, layer); + + /** + * @protected + * @type {number} + */ + this.renderedResolution; + + /** + * @private + * @type {module:ol/transform~Transform} + */ + this.transform_ = createTransform(); + + } /** + * @param {CanvasRenderingContext2D} context Context. + * @param {module:ol/PluggableMap~FrameState} frameState Frame state. + * @param {module:ol/extent~Extent} extent Clip extent. * @protected - * @type {number} */ - this.renderedResolution; + clip(context, frameState, extent) { + const pixelRatio = frameState.pixelRatio; + const width = frameState.size[0] * pixelRatio; + const height = frameState.size[1] * pixelRatio; + const rotation = frameState.viewState.rotation; + const topLeft = getTopLeft(/** @type {module:ol/extent~Extent} */ (extent)); + const topRight = getTopRight(/** @type {module:ol/extent~Extent} */ (extent)); + const bottomRight = getBottomRight(/** @type {module:ol/extent~Extent} */ (extent)); + const bottomLeft = getBottomLeft(/** @type {module:ol/extent~Extent} */ (extent)); + + applyTransform(frameState.coordinateToPixelTransform, topLeft); + applyTransform(frameState.coordinateToPixelTransform, topRight); + applyTransform(frameState.coordinateToPixelTransform, bottomRight); + applyTransform(frameState.coordinateToPixelTransform, bottomLeft); + + context.save(); + rotateAtOffset(context, -rotation, width / 2, height / 2); + context.beginPath(); + context.moveTo(topLeft[0] * pixelRatio, topLeft[1] * pixelRatio); + context.lineTo(topRight[0] * pixelRatio, topRight[1] * pixelRatio); + context.lineTo(bottomRight[0] * pixelRatio, bottomRight[1] * pixelRatio); + context.lineTo(bottomLeft[0] * pixelRatio, bottomLeft[1] * pixelRatio); + context.clip(); + rotateAtOffset(context, rotation, width / 2, height / 2); + } /** + * @param {module:ol/render/EventType} type Event type. + * @param {CanvasRenderingContext2D} context Context. + * @param {module:ol/PluggableMap~FrameState} frameState Frame state. + * @param {module:ol/transform~Transform=} opt_transform Transform. * @private - * @type {module:ol/transform~Transform} */ - this.transform_ = createTransform(); + dispatchComposeEvent_(type, context, frameState, opt_transform) { + const layer = this.getLayer(); + if (layer.hasListener(type)) { + const width = frameState.size[0] * frameState.pixelRatio; + const height = frameState.size[1] * frameState.pixelRatio; + const rotation = frameState.viewState.rotation; + rotateAtOffset(context, -rotation, width / 2, height / 2); + const transform = opt_transform !== undefined ? + opt_transform : this.getTransform(frameState, 0); + const render = new CanvasImmediateRenderer( + context, frameState.pixelRatio, frameState.extent, transform, + frameState.viewState.rotation); + const composeEvent = new RenderEvent(type, render, frameState, + context, null); + layer.dispatchEvent(composeEvent); + rotateAtOffset(context, rotation, width / 2, height / 2); + } + } -}; + /** + * @param {module:ol/coordinate~Coordinate} coordinate Coordinate. + * @param {module:ol/PluggableMap~FrameState} frameState FrameState. + * @param {number} hitTolerance Hit tolerance in pixels. + * @param {function(this: S, module:ol/layer/Layer, (Uint8ClampedArray|Uint8Array)): T} callback Layer + * callback. + * @param {S} thisArg Value to use as `this` when executing `callback`. + * @return {T|undefined} Callback result. + * @template S,T,U + */ + forEachLayerAtCoordinate(coordinate, frameState, hitTolerance, callback, thisArg) { + const hasFeature = this.forEachFeatureAtCoordinate(coordinate, frameState, hitTolerance, TRUE, this); + + if (hasFeature) { + return callback.call(thisArg, this.getLayer(), null); + } else { + return undefined; + } + } + + /** + * @param {CanvasRenderingContext2D} context Context. + * @param {module:ol/PluggableMap~FrameState} frameState Frame state. + * @param {module:ol/layer/Layer~State} layerState Layer state. + * @param {module:ol/transform~Transform=} opt_transform Transform. + * @protected + */ + postCompose(context, frameState, layerState, opt_transform) { + this.dispatchComposeEvent_(RenderEventType.POSTCOMPOSE, context, frameState, opt_transform); + } + + /** + * @param {CanvasRenderingContext2D} context Context. + * @param {module:ol/PluggableMap~FrameState} frameState Frame state. + * @param {module:ol/transform~Transform=} opt_transform Transform. + * @protected + */ + preCompose(context, frameState, opt_transform) { + this.dispatchComposeEvent_(RenderEventType.PRECOMPOSE, context, frameState, opt_transform); + } + + /** + * @param {CanvasRenderingContext2D} context Context. + * @param {module:ol/PluggableMap~FrameState} frameState Frame state. + * @param {module:ol/transform~Transform=} opt_transform Transform. + * @protected + */ + dispatchRenderEvent(context, frameState, opt_transform) { + this.dispatchComposeEvent_(RenderEventType.RENDER, context, frameState, opt_transform); + } + + /** + * @param {module:ol/PluggableMap~FrameState} frameState Frame state. + * @param {number} offsetX Offset on the x-axis in view coordinates. + * @protected + * @return {!module:ol/transform~Transform} Transform. + */ + getTransform(frameState, offsetX) { + const viewState = frameState.viewState; + const pixelRatio = frameState.pixelRatio; + const dx1 = pixelRatio * frameState.size[0] / 2; + const dy1 = pixelRatio * frameState.size[1] / 2; + const sx = pixelRatio / viewState.resolution; + const sy = -sx; + const angle = -viewState.rotation; + const dx2 = -viewState.center[0] + offsetX; + const dy2 = -viewState.center[1]; + return composeTransform(this.transform_, dx1, dy1, sx, sy, angle, dx2, dy2); + } + + /** + * @abstract + * @param {module:ol/PluggableMap~FrameState} frameState Frame state. + * @param {module:ol/layer/Layer~State} layerState Layer state. + * @param {CanvasRenderingContext2D} context Context. + */ + composeFrame(frameState, layerState, context) {} + + /** + * @abstract + * @param {module:ol/PluggableMap~FrameState} frameState Frame state. + * @param {module:ol/layer/Layer~State} layerState Layer state. + * @return {boolean} whether composeFrame should be called. + */ + prepareFrame(frameState, layerState) {} +} inherits(CanvasLayerRenderer, LayerRenderer); -/** - * @param {CanvasRenderingContext2D} context Context. - * @param {module:ol/PluggableMap~FrameState} frameState Frame state. - * @param {module:ol/extent~Extent} extent Clip extent. - * @protected - */ -CanvasLayerRenderer.prototype.clip = function(context, frameState, extent) { - const pixelRatio = frameState.pixelRatio; - const width = frameState.size[0] * pixelRatio; - const height = frameState.size[1] * pixelRatio; - const rotation = frameState.viewState.rotation; - const topLeft = getTopLeft(/** @type {module:ol/extent~Extent} */ (extent)); - const topRight = getTopRight(/** @type {module:ol/extent~Extent} */ (extent)); - const bottomRight = getBottomRight(/** @type {module:ol/extent~Extent} */ (extent)); - const bottomLeft = getBottomLeft(/** @type {module:ol/extent~Extent} */ (extent)); - - applyTransform(frameState.coordinateToPixelTransform, topLeft); - applyTransform(frameState.coordinateToPixelTransform, topRight); - applyTransform(frameState.coordinateToPixelTransform, bottomRight); - applyTransform(frameState.coordinateToPixelTransform, bottomLeft); - - context.save(); - rotateAtOffset(context, -rotation, width / 2, height / 2); - context.beginPath(); - context.moveTo(topLeft[0] * pixelRatio, topLeft[1] * pixelRatio); - context.lineTo(topRight[0] * pixelRatio, topRight[1] * pixelRatio); - context.lineTo(bottomRight[0] * pixelRatio, bottomRight[1] * pixelRatio); - context.lineTo(bottomLeft[0] * pixelRatio, bottomLeft[1] * pixelRatio); - context.clip(); - rotateAtOffset(context, rotation, width / 2, height / 2); -}; - - -/** - * @param {module:ol/render/EventType} type Event type. - * @param {CanvasRenderingContext2D} context Context. - * @param {module:ol/PluggableMap~FrameState} frameState Frame state. - * @param {module:ol/transform~Transform=} opt_transform Transform. - * @private - */ -CanvasLayerRenderer.prototype.dispatchComposeEvent_ = function(type, context, frameState, opt_transform) { - const layer = this.getLayer(); - if (layer.hasListener(type)) { - const width = frameState.size[0] * frameState.pixelRatio; - const height = frameState.size[1] * frameState.pixelRatio; - const rotation = frameState.viewState.rotation; - rotateAtOffset(context, -rotation, width / 2, height / 2); - const transform = opt_transform !== undefined ? - opt_transform : this.getTransform(frameState, 0); - const render = new CanvasImmediateRenderer( - context, frameState.pixelRatio, frameState.extent, transform, - frameState.viewState.rotation); - const composeEvent = new RenderEvent(type, render, frameState, - context, null); - layer.dispatchEvent(composeEvent); - rotateAtOffset(context, rotation, width / 2, height / 2); - } -}; - - -/** - * @param {module:ol/coordinate~Coordinate} coordinate Coordinate. - * @param {module:ol/PluggableMap~FrameState} frameState FrameState. - * @param {number} hitTolerance Hit tolerance in pixels. - * @param {function(this: S, module:ol/layer/Layer, (Uint8ClampedArray|Uint8Array)): T} callback Layer - * callback. - * @param {S} thisArg Value to use as `this` when executing `callback`. - * @return {T|undefined} Callback result. - * @template S,T,U - */ -CanvasLayerRenderer.prototype.forEachLayerAtCoordinate = function(coordinate, frameState, hitTolerance, callback, thisArg) { - const hasFeature = this.forEachFeatureAtCoordinate(coordinate, frameState, hitTolerance, TRUE, this); - - if (hasFeature) { - return callback.call(thisArg, this.getLayer(), null); - } else { - return undefined; - } -}; - - -/** - * @param {CanvasRenderingContext2D} context Context. - * @param {module:ol/PluggableMap~FrameState} frameState Frame state. - * @param {module:ol/layer/Layer~State} layerState Layer state. - * @param {module:ol/transform~Transform=} opt_transform Transform. - * @protected - */ -CanvasLayerRenderer.prototype.postCompose = function(context, frameState, layerState, opt_transform) { - this.dispatchComposeEvent_(RenderEventType.POSTCOMPOSE, context, frameState, opt_transform); -}; - - -/** - * @param {CanvasRenderingContext2D} context Context. - * @param {module:ol/PluggableMap~FrameState} frameState Frame state. - * @param {module:ol/transform~Transform=} opt_transform Transform. - * @protected - */ -CanvasLayerRenderer.prototype.preCompose = function(context, frameState, opt_transform) { - this.dispatchComposeEvent_(RenderEventType.PRECOMPOSE, context, frameState, opt_transform); -}; - - -/** - * @param {CanvasRenderingContext2D} context Context. - * @param {module:ol/PluggableMap~FrameState} frameState Frame state. - * @param {module:ol/transform~Transform=} opt_transform Transform. - * @protected - */ -CanvasLayerRenderer.prototype.dispatchRenderEvent = function(context, frameState, opt_transform) { - this.dispatchComposeEvent_(RenderEventType.RENDER, context, frameState, opt_transform); -}; - - -/** - * @param {module:ol/PluggableMap~FrameState} frameState Frame state. - * @param {number} offsetX Offset on the x-axis in view coordinates. - * @protected - * @return {!module:ol/transform~Transform} Transform. - */ -CanvasLayerRenderer.prototype.getTransform = function(frameState, offsetX) { - const viewState = frameState.viewState; - const pixelRatio = frameState.pixelRatio; - const dx1 = pixelRatio * frameState.size[0] / 2; - const dy1 = pixelRatio * frameState.size[1] / 2; - const sx = pixelRatio / viewState.resolution; - const sy = -sx; - const angle = -viewState.rotation; - const dx2 = -viewState.center[0] + offsetX; - const dy2 = -viewState.center[1]; - return composeTransform(this.transform_, dx1, dy1, sx, sy, angle, dx2, dy2); -}; - - -/** - * @abstract - * @param {module:ol/PluggableMap~FrameState} frameState Frame state. - * @param {module:ol/layer/Layer~State} layerState Layer state. - * @param {CanvasRenderingContext2D} context Context. - */ -CanvasLayerRenderer.prototype.composeFrame = function(frameState, layerState, context) {}; - -/** - * @abstract - * @param {module:ol/PluggableMap~FrameState} frameState Frame state. - * @param {module:ol/layer/Layer~State} layerState Layer state. - * @return {boolean} whether composeFrame should be called. - */ -CanvasLayerRenderer.prototype.prepareFrame = function(frameState, layerState) {}; export default CanvasLayerRenderer; diff --git a/src/ol/renderer/canvas/Map.js b/src/ol/renderer/canvas/Map.js index e544b2e513..7e5ea1ad2a 100644 --- a/src/ol/renderer/canvas/Map.js +++ b/src/ol/renderer/canvas/Map.js @@ -27,201 +27,198 @@ export const layerRendererConstructors = []; * @param {module:ol/PluggableMap} map Map. * @api */ -const CanvasMapRenderer = function(map) { - MapRenderer.call(this, map); +class CanvasMapRenderer { + constructor(map) { + MapRenderer.call(this, map); - const container = map.getViewport(); + const container = map.getViewport(); + + /** + * @private + * @type {CanvasRenderingContext2D} + */ + this.context_ = createCanvasContext2D(); + + /** + * @private + * @type {HTMLCanvasElement} + */ + this.canvas_ = this.context_.canvas; + + this.canvas_.style.width = '100%'; + this.canvas_.style.height = '100%'; + this.canvas_.style.display = 'block'; + this.canvas_.className = CLASS_UNSELECTABLE; + container.insertBefore(this.canvas_, container.childNodes[0] || null); + + /** + * @private + * @type {boolean} + */ + this.renderedVisible_ = true; + + /** + * @private + * @type {module:ol/transform~Transform} + */ + this.transform_ = createTransform(); + + } /** + * @param {module:ol/render/EventType} type Event type. + * @param {module:ol/PluggableMap~FrameState} frameState Frame state. * @private - * @type {CanvasRenderingContext2D} */ - this.context_ = createCanvasContext2D(); + dispatchComposeEvent_(type, frameState) { + const map = this.getMap(); + const context = this.context_; + if (map.hasListener(type)) { + const extent = frameState.extent; + const pixelRatio = frameState.pixelRatio; + const viewState = frameState.viewState; + const rotation = viewState.rotation; + + const transform = this.getTransform(frameState); + + const vectorContext = new CanvasImmediateRenderer(context, pixelRatio, + extent, transform, rotation); + const composeEvent = new RenderEvent(type, vectorContext, + frameState, context, null); + map.dispatchEvent(composeEvent); + } + } /** - * @private - * @type {HTMLCanvasElement} + * @param {module:ol/PluggableMap~FrameState} frameState Frame state. + * @protected + * @return {!module:ol/transform~Transform} Transform. */ - this.canvas_ = this.context_.canvas; - - this.canvas_.style.width = '100%'; - this.canvas_.style.height = '100%'; - this.canvas_.style.display = 'block'; - this.canvas_.className = CLASS_UNSELECTABLE; - container.insertBefore(this.canvas_, container.childNodes[0] || null); + getTransform(frameState) { + const viewState = frameState.viewState; + const dx1 = this.canvas_.width / 2; + const dy1 = this.canvas_.height / 2; + const sx = frameState.pixelRatio / viewState.resolution; + const sy = -sx; + const angle = -viewState.rotation; + const dx2 = -viewState.center[0]; + const dy2 = -viewState.center[1]; + return composeTransform(this.transform_, dx1, dy1, sx, sy, angle, dx2, dy2); + } /** - * @private - * @type {boolean} + * @inheritDoc */ - this.renderedVisible_ = true; + renderFrame(frameState) { + + if (!frameState) { + if (this.renderedVisible_) { + this.canvas_.style.display = 'none'; + this.renderedVisible_ = false; + } + return; + } + + const context = this.context_; + const pixelRatio = frameState.pixelRatio; + const width = Math.round(frameState.size[0] * pixelRatio); + const height = Math.round(frameState.size[1] * pixelRatio); + if (this.canvas_.width != width || this.canvas_.height != height) { + this.canvas_.width = width; + this.canvas_.height = height; + } else { + context.clearRect(0, 0, width, height); + } + + const rotation = frameState.viewState.rotation; + + this.calculateMatrices2D(frameState); + + this.dispatchComposeEvent_(RenderEventType.PRECOMPOSE, frameState); + + const layerStatesArray = frameState.layerStatesArray; + stableSort(layerStatesArray, sortByZIndex); + + if (rotation) { + context.save(); + rotateAtOffset(context, rotation, width / 2, height / 2); + } + + const viewResolution = frameState.viewState.resolution; + let i, ii, layer, layerRenderer, layerState; + for (i = 0, ii = layerStatesArray.length; i < ii; ++i) { + layerState = layerStatesArray[i]; + layer = layerState.layer; + layerRenderer = /** @type {module:ol/renderer/canvas/Layer} */ (this.getLayerRenderer(layer)); + if (!visibleAtResolution(layerState, viewResolution) || + layerState.sourceState != SourceState.READY) { + continue; + } + if (layerRenderer.prepareFrame(frameState, layerState)) { + layerRenderer.composeFrame(frameState, layerState, context); + } + } + + if (rotation) { + context.restore(); + } + + this.dispatchComposeEvent_(RenderEventType.POSTCOMPOSE, frameState); + + if (!this.renderedVisible_) { + this.canvas_.style.display = ''; + this.renderedVisible_ = true; + } + + this.scheduleRemoveUnusedLayerRenderers(frameState); + this.scheduleExpireIconCache(frameState); + } /** - * @private - * @type {module:ol/transform~Transform} + * @inheritDoc */ - this.transform_ = createTransform(); + forEachLayerAtPixel(pixel, frameState, hitTolerance, callback, thisArg, layerFilter, thisArg2) { + let result; + const viewState = frameState.viewState; + const viewResolution = viewState.resolution; -}; + const layerStates = frameState.layerStatesArray; + const numLayers = layerStates.length; + + const coordinate = applyTransform( + frameState.pixelToCoordinateTransform, pixel.slice()); + + let i; + for (i = numLayers - 1; i >= 0; --i) { + const layerState = layerStates[i]; + const layer = layerState.layer; + if (visibleAtResolution(layerState, viewResolution) && layerFilter.call(thisArg2, layer)) { + const layerRenderer = /** @type {module:ol/renderer/canvas/Layer} */ (this.getLayerRenderer(layer)); + result = layerRenderer.forEachLayerAtCoordinate( + coordinate, frameState, hitTolerance, callback, thisArg); + if (result) { + return result; + } + } + } + return undefined; + } + + /** + * @inheritDoc + */ + registerLayerRenderers(constructors) { + MapRenderer.prototype.registerLayerRenderers.call(this, constructors); + for (let i = 0, ii = constructors.length; i < ii; ++i) { + const ctor = constructors[i]; + if (!includes(layerRendererConstructors, ctor)) { + layerRendererConstructors.push(ctor); + } + } + } +} inherits(CanvasMapRenderer, MapRenderer); -/** - * @param {module:ol/render/EventType} type Event type. - * @param {module:ol/PluggableMap~FrameState} frameState Frame state. - * @private - */ -CanvasMapRenderer.prototype.dispatchComposeEvent_ = function(type, frameState) { - const map = this.getMap(); - const context = this.context_; - if (map.hasListener(type)) { - const extent = frameState.extent; - const pixelRatio = frameState.pixelRatio; - const viewState = frameState.viewState; - const rotation = viewState.rotation; - - const transform = this.getTransform(frameState); - - const vectorContext = new CanvasImmediateRenderer(context, pixelRatio, - extent, transform, rotation); - const composeEvent = new RenderEvent(type, vectorContext, - frameState, context, null); - map.dispatchEvent(composeEvent); - } -}; - - -/** - * @param {module:ol/PluggableMap~FrameState} frameState Frame state. - * @protected - * @return {!module:ol/transform~Transform} Transform. - */ -CanvasMapRenderer.prototype.getTransform = function(frameState) { - const viewState = frameState.viewState; - const dx1 = this.canvas_.width / 2; - const dy1 = this.canvas_.height / 2; - const sx = frameState.pixelRatio / viewState.resolution; - const sy = -sx; - const angle = -viewState.rotation; - const dx2 = -viewState.center[0]; - const dy2 = -viewState.center[1]; - return composeTransform(this.transform_, dx1, dy1, sx, sy, angle, dx2, dy2); -}; - - -/** - * @inheritDoc - */ -CanvasMapRenderer.prototype.renderFrame = function(frameState) { - - if (!frameState) { - if (this.renderedVisible_) { - this.canvas_.style.display = 'none'; - this.renderedVisible_ = false; - } - return; - } - - const context = this.context_; - const pixelRatio = frameState.pixelRatio; - const width = Math.round(frameState.size[0] * pixelRatio); - const height = Math.round(frameState.size[1] * pixelRatio); - if (this.canvas_.width != width || this.canvas_.height != height) { - this.canvas_.width = width; - this.canvas_.height = height; - } else { - context.clearRect(0, 0, width, height); - } - - const rotation = frameState.viewState.rotation; - - this.calculateMatrices2D(frameState); - - this.dispatchComposeEvent_(RenderEventType.PRECOMPOSE, frameState); - - const layerStatesArray = frameState.layerStatesArray; - stableSort(layerStatesArray, sortByZIndex); - - if (rotation) { - context.save(); - rotateAtOffset(context, rotation, width / 2, height / 2); - } - - const viewResolution = frameState.viewState.resolution; - let i, ii, layer, layerRenderer, layerState; - for (i = 0, ii = layerStatesArray.length; i < ii; ++i) { - layerState = layerStatesArray[i]; - layer = layerState.layer; - layerRenderer = /** @type {module:ol/renderer/canvas/Layer} */ (this.getLayerRenderer(layer)); - if (!visibleAtResolution(layerState, viewResolution) || - layerState.sourceState != SourceState.READY) { - continue; - } - if (layerRenderer.prepareFrame(frameState, layerState)) { - layerRenderer.composeFrame(frameState, layerState, context); - } - } - - if (rotation) { - context.restore(); - } - - this.dispatchComposeEvent_(RenderEventType.POSTCOMPOSE, frameState); - - if (!this.renderedVisible_) { - this.canvas_.style.display = ''; - this.renderedVisible_ = true; - } - - this.scheduleRemoveUnusedLayerRenderers(frameState); - this.scheduleExpireIconCache(frameState); -}; - - -/** - * @inheritDoc - */ -CanvasMapRenderer.prototype.forEachLayerAtPixel = function(pixel, frameState, hitTolerance, callback, thisArg, - layerFilter, thisArg2) { - let result; - const viewState = frameState.viewState; - const viewResolution = viewState.resolution; - - const layerStates = frameState.layerStatesArray; - const numLayers = layerStates.length; - - const coordinate = applyTransform( - frameState.pixelToCoordinateTransform, pixel.slice()); - - let i; - for (i = numLayers - 1; i >= 0; --i) { - const layerState = layerStates[i]; - const layer = layerState.layer; - if (visibleAtResolution(layerState, viewResolution) && layerFilter.call(thisArg2, layer)) { - const layerRenderer = /** @type {module:ol/renderer/canvas/Layer} */ (this.getLayerRenderer(layer)); - result = layerRenderer.forEachLayerAtCoordinate( - coordinate, frameState, hitTolerance, callback, thisArg); - if (result) { - return result; - } - } - } - return undefined; -}; - - -/** - * @inheritDoc - */ -CanvasMapRenderer.prototype.registerLayerRenderers = function(constructors) { - MapRenderer.prototype.registerLayerRenderers.call(this, constructors); - for (let i = 0, ii = constructors.length; i < ii; ++i) { - const ctor = constructors[i]; - if (!includes(layerRendererConstructors, ctor)) { - layerRendererConstructors.push(ctor); - } - } -}; - export default CanvasMapRenderer; diff --git a/src/ol/renderer/canvas/TileLayer.js b/src/ol/renderer/canvas/TileLayer.js index 68d6423880..b0069fa973 100644 --- a/src/ol/renderer/canvas/TileLayer.js +++ b/src/ol/renderer/canvas/TileLayer.js @@ -17,71 +17,339 @@ import {create as createTransform, compose as composeTransform} from '../../tran * @param {module:ol/layer/Tile|module:ol/layer/VectorTile} tileLayer Tile layer. * @api */ -const CanvasTileLayerRenderer = function(tileLayer) { +class CanvasTileLayerRenderer { + constructor(tileLayer) { - IntermediateCanvasRenderer.call(this, tileLayer); + IntermediateCanvasRenderer.call(this, tileLayer); - /** - * @protected - * @type {CanvasRenderingContext2D} - */ - this.context = this.context === null ? null : createCanvasContext2D(); + /** + * @protected + * @type {CanvasRenderingContext2D} + */ + this.context = this.context === null ? null : createCanvasContext2D(); + + /** + * @private + * @type {number} + */ + this.oversampling_; + + /** + * @private + * @type {module:ol/extent~Extent} + */ + this.renderedExtent_ = null; + + /** + * @protected + * @type {number} + */ + this.renderedRevision; + + /** + * @protected + * @type {!Array.} + */ + this.renderedTiles = []; + + /** + * @private + * @type {boolean} + */ + this.newTiles_ = false; + + /** + * @protected + * @type {module:ol/extent~Extent} + */ + this.tmpExtent = createEmpty(); + + /** + * @private + * @type {module:ol/TileRange} + */ + this.tmpTileRange_ = new TileRange(0, 0, 0, 0); + + /** + * @private + * @type {module:ol/transform~Transform} + */ + this.imageTransform_ = createTransform(); + + /** + * @protected + * @type {number} + */ + this.zDirection = 0; + + } /** * @private - * @type {number} + * @param {module:ol/Tile} tile Tile. + * @return {boolean} Tile is drawable. */ - this.oversampling_; + isDrawableTile_(tile) { + const tileState = tile.getState(); + const useInterimTilesOnError = this.getLayer().getUseInterimTilesOnError(); + return tileState == TileState.LOADED || + tileState == TileState.EMPTY || + tileState == TileState.ERROR && !useInterimTilesOnError; + } /** - * @private - * @type {module:ol/extent~Extent} + * @param {number} z Tile coordinate z. + * @param {number} x Tile coordinate x. + * @param {number} y Tile coordinate y. + * @param {number} pixelRatio Pixel ratio. + * @param {module:ol/proj/Projection} projection Projection. + * @return {!module:ol/Tile} Tile. */ - this.renderedExtent_ = null; + getTile(z, x, y, pixelRatio, projection) { + const layer = this.getLayer(); + const source = /** @type {module:ol/source/Tile} */ (layer.getSource()); + let tile = source.getTile(z, x, y, pixelRatio, projection); + if (tile.getState() == TileState.ERROR) { + if (!layer.getUseInterimTilesOnError()) { + // When useInterimTilesOnError is false, we consider the error tile as loaded. + tile.setState(TileState.LOADED); + } else if (layer.getPreload() > 0) { + // Preloaded tiles for lower resolutions might have finished loading. + this.newTiles_ = true; + } + } + if (!this.isDrawableTile_(tile)) { + tile = tile.getInterimTile(); + } + return tile; + } /** - * @protected - * @type {number} + * @inheritDoc */ - this.renderedRevision; + prepareFrame(frameState, layerState) { + + const pixelRatio = frameState.pixelRatio; + const size = frameState.size; + const viewState = frameState.viewState; + const projection = viewState.projection; + const viewResolution = viewState.resolution; + const viewCenter = viewState.center; + + const tileLayer = this.getLayer(); + const tileSource = /** @type {module:ol/source/Tile} */ (tileLayer.getSource()); + const sourceRevision = tileSource.getRevision(); + const tileGrid = tileSource.getTileGridForProjection(projection); + const z = tileGrid.getZForResolution(viewResolution, this.zDirection); + const tileResolution = tileGrid.getResolution(z); + let oversampling = Math.round(viewResolution / tileResolution) || 1; + let extent = frameState.extent; + + if (layerState.extent !== undefined) { + extent = getIntersection(extent, layerState.extent); + } + if (isEmpty(extent)) { + // Return false to prevent the rendering of the layer. + return false; + } + + const tileRange = tileGrid.getTileRangeForExtentAndZ(extent, z); + const imageExtent = tileGrid.getTileRangeExtent(z, tileRange); + + const tilePixelRatio = tileSource.getTilePixelRatio(pixelRatio); + + /** + * @type {Object.>} + */ + const tilesToDrawByZ = {}; + tilesToDrawByZ[z] = {}; + + const findLoadedTiles = this.createLoadedTileFinder( + tileSource, projection, tilesToDrawByZ); + + const hints = frameState.viewHints; + const animatingOrInteracting = hints[ViewHint.ANIMATING] || hints[ViewHint.INTERACTING]; + + const tmpExtent = this.tmpExtent; + const tmpTileRange = this.tmpTileRange_; + this.newTiles_ = false; + let tile, x, y; + for (x = tileRange.minX; x <= tileRange.maxX; ++x) { + for (y = tileRange.minY; y <= tileRange.maxY; ++y) { + if (Date.now() - frameState.time > 16 && animatingOrInteracting) { + continue; + } + tile = this.getTile(z, x, y, pixelRatio, projection); + if (this.isDrawableTile_(tile)) { + const uid = getUid(this); + if (tile.getState() == TileState.LOADED) { + tilesToDrawByZ[z][tile.tileCoord.toString()] = tile; + const inTransition = tile.inTransition(uid); + if (!this.newTiles_ && (inTransition || this.renderedTiles.indexOf(tile) === -1)) { + this.newTiles_ = true; + } + } + if (tile.getAlpha(uid, frameState.time) === 1) { + // don't look for alt tiles if alpha is 1 + continue; + } + } + + const childTileRange = tileGrid.getTileCoordChildTileRange( + tile.tileCoord, tmpTileRange, tmpExtent); + let covered = false; + if (childTileRange) { + covered = findLoadedTiles(z + 1, childTileRange); + } + if (!covered) { + tileGrid.forEachTileCoordParentTileRange( + tile.tileCoord, findLoadedTiles, null, tmpTileRange, tmpExtent); + } + + } + } + + const renderedResolution = tileResolution * pixelRatio / tilePixelRatio * oversampling; + if (!(this.renderedResolution && Date.now() - frameState.time > 16 && animatingOrInteracting) && ( + this.newTiles_ || + !(this.renderedExtent_ && containsExtent(this.renderedExtent_, extent)) || + this.renderedRevision != sourceRevision || + oversampling != this.oversampling_ || + !animatingOrInteracting && renderedResolution != this.renderedResolution + )) { + + const context = this.context; + if (context) { + const tilePixelSize = tileSource.getTilePixelSize(z, pixelRatio, projection); + const width = Math.round(tileRange.getWidth() * tilePixelSize[0] / oversampling); + const height = Math.round(tileRange.getHeight() * tilePixelSize[1] / oversampling); + const canvas = context.canvas; + if (canvas.width != width || canvas.height != height) { + this.oversampling_ = oversampling; + canvas.width = width; + canvas.height = height; + } else { + if (this.renderedExtent_ && !equals(imageExtent, this.renderedExtent_)) { + context.clearRect(0, 0, width, height); + } + oversampling = this.oversampling_; + } + } + + this.renderedTiles.length = 0; + /** @type {Array.} */ + const zs = Object.keys(tilesToDrawByZ).map(Number); + zs.sort(function(a, b) { + if (a === z) { + return 1; + } else if (b === z) { + return -1; + } else { + return a > b ? 1 : a < b ? -1 : 0; + } + }); + let currentResolution, currentScale, currentTilePixelSize, currentZ, i, ii; + let tileExtent, tileGutter, tilesToDraw, w, h; + for (i = 0, ii = zs.length; i < ii; ++i) { + currentZ = zs[i]; + currentTilePixelSize = tileSource.getTilePixelSize(currentZ, pixelRatio, projection); + currentResolution = tileGrid.getResolution(currentZ); + currentScale = currentResolution / tileResolution; + tileGutter = tilePixelRatio * tileSource.getGutter(projection); + tilesToDraw = tilesToDrawByZ[currentZ]; + for (const tileCoordKey in tilesToDraw) { + tile = tilesToDraw[tileCoordKey]; + tileExtent = tileGrid.getTileCoordExtent(tile.getTileCoord(), tmpExtent); + x = (tileExtent[0] - imageExtent[0]) / tileResolution * tilePixelRatio / oversampling; + y = (imageExtent[3] - tileExtent[3]) / tileResolution * tilePixelRatio / oversampling; + w = currentTilePixelSize[0] * currentScale / oversampling; + h = currentTilePixelSize[1] * currentScale / oversampling; + this.drawTileImage(tile, frameState, layerState, x, y, w, h, tileGutter, z === currentZ); + this.renderedTiles.push(tile); + } + } + + this.renderedRevision = sourceRevision; + this.renderedResolution = tileResolution * pixelRatio / tilePixelRatio * oversampling; + this.renderedExtent_ = imageExtent; + } + + const scale = this.renderedResolution / viewResolution; + const transform = composeTransform(this.imageTransform_, + pixelRatio * size[0] / 2, pixelRatio * size[1] / 2, + scale, scale, + 0, + (this.renderedExtent_[0] - viewCenter[0]) / this.renderedResolution * pixelRatio, + (viewCenter[1] - this.renderedExtent_[3]) / this.renderedResolution * pixelRatio); + composeTransform(this.coordinateToCanvasPixelTransform, + pixelRatio * size[0] / 2 - transform[4], pixelRatio * size[1] / 2 - transform[5], + pixelRatio / viewResolution, -pixelRatio / viewResolution, + 0, + -viewCenter[0], -viewCenter[1]); + + + this.updateUsedTiles(frameState.usedTiles, tileSource, z, tileRange); + this.manageTilePyramid(frameState, tileSource, tileGrid, pixelRatio, + projection, extent, z, tileLayer.getPreload()); + this.scheduleExpireCache(frameState, tileSource); + + return this.renderedTiles.length > 0; + } /** - * @protected - * @type {!Array.} + * @param {module:ol/Tile} tile Tile. + * @param {module:ol/PluggableMap~FrameState} frameState Frame state. + * @param {module:ol/layer/Layer~State} layerState Layer state. + * @param {number} x Left of the tile. + * @param {number} y Top of the tile. + * @param {number} w Width of the tile. + * @param {number} h Height of the tile. + * @param {number} gutter Tile gutter. + * @param {boolean} transition Apply an alpha transition. */ - this.renderedTiles = []; + drawTileImage(tile, frameState, layerState, x, y, w, h, gutter, transition) { + const image = tile.getImage(this.getLayer()); + if (!image) { + return; + } + const uid = getUid(this); + const alpha = transition ? tile.getAlpha(uid, frameState.time) : 1; + if (alpha === 1 && !this.getLayer().getSource().getOpaque(frameState.viewState.projection)) { + this.context.clearRect(x, y, w, h); + } + const alphaChanged = alpha !== this.context.globalAlpha; + if (alphaChanged) { + this.context.save(); + this.context.globalAlpha = alpha; + } + this.context.drawImage(image, gutter, gutter, + image.width - 2 * gutter, image.height - 2 * gutter, x, y, w, h); + + if (alphaChanged) { + this.context.restore(); + } + if (alpha !== 1) { + frameState.animate = true; + } else if (transition) { + tile.endTransition(uid); + } + } /** - * @private - * @type {boolean} + * @inheritDoc */ - this.newTiles_ = false; + getImage() { + const context = this.context; + return context ? context.canvas : null; + } /** - * @protected - * @type {module:ol/extent~Extent} + * @inheritDoc */ - this.tmpExtent = createEmpty(); - - /** - * @private - * @type {module:ol/TileRange} - */ - this.tmpTileRange_ = new TileRange(0, 0, 0, 0); - - /** - * @private - * @type {module:ol/transform~Transform} - */ - this.imageTransform_ = createTransform(); - - /** - * @protected - * @type {number} - */ - this.zDirection = 0; - -}; + getImageTransform() { + return this.imageTransform_; + } +} inherits(CanvasTileLayerRenderer, IntermediateCanvasRenderer); @@ -107,269 +375,6 @@ CanvasTileLayerRenderer['create'] = function(mapRenderer, layer) { }; -/** - * @private - * @param {module:ol/Tile} tile Tile. - * @return {boolean} Tile is drawable. - */ -CanvasTileLayerRenderer.prototype.isDrawableTile_ = function(tile) { - const tileState = tile.getState(); - const useInterimTilesOnError = this.getLayer().getUseInterimTilesOnError(); - return tileState == TileState.LOADED || - tileState == TileState.EMPTY || - tileState == TileState.ERROR && !useInterimTilesOnError; -}; - - -/** - * @param {number} z Tile coordinate z. - * @param {number} x Tile coordinate x. - * @param {number} y Tile coordinate y. - * @param {number} pixelRatio Pixel ratio. - * @param {module:ol/proj/Projection} projection Projection. - * @return {!module:ol/Tile} Tile. - */ -CanvasTileLayerRenderer.prototype.getTile = function(z, x, y, pixelRatio, projection) { - const layer = this.getLayer(); - const source = /** @type {module:ol/source/Tile} */ (layer.getSource()); - let tile = source.getTile(z, x, y, pixelRatio, projection); - if (tile.getState() == TileState.ERROR) { - if (!layer.getUseInterimTilesOnError()) { - // When useInterimTilesOnError is false, we consider the error tile as loaded. - tile.setState(TileState.LOADED); - } else if (layer.getPreload() > 0) { - // Preloaded tiles for lower resolutions might have finished loading. - this.newTiles_ = true; - } - } - if (!this.isDrawableTile_(tile)) { - tile = tile.getInterimTile(); - } - return tile; -}; - -/** - * @inheritDoc - */ -CanvasTileLayerRenderer.prototype.prepareFrame = function(frameState, layerState) { - - const pixelRatio = frameState.pixelRatio; - const size = frameState.size; - const viewState = frameState.viewState; - const projection = viewState.projection; - const viewResolution = viewState.resolution; - const viewCenter = viewState.center; - - const tileLayer = this.getLayer(); - const tileSource = /** @type {module:ol/source/Tile} */ (tileLayer.getSource()); - const sourceRevision = tileSource.getRevision(); - const tileGrid = tileSource.getTileGridForProjection(projection); - const z = tileGrid.getZForResolution(viewResolution, this.zDirection); - const tileResolution = tileGrid.getResolution(z); - let oversampling = Math.round(viewResolution / tileResolution) || 1; - let extent = frameState.extent; - - if (layerState.extent !== undefined) { - extent = getIntersection(extent, layerState.extent); - } - if (isEmpty(extent)) { - // Return false to prevent the rendering of the layer. - return false; - } - - const tileRange = tileGrid.getTileRangeForExtentAndZ(extent, z); - const imageExtent = tileGrid.getTileRangeExtent(z, tileRange); - - const tilePixelRatio = tileSource.getTilePixelRatio(pixelRatio); - - /** - * @type {Object.>} - */ - const tilesToDrawByZ = {}; - tilesToDrawByZ[z] = {}; - - const findLoadedTiles = this.createLoadedTileFinder( - tileSource, projection, tilesToDrawByZ); - - const hints = frameState.viewHints; - const animatingOrInteracting = hints[ViewHint.ANIMATING] || hints[ViewHint.INTERACTING]; - - const tmpExtent = this.tmpExtent; - const tmpTileRange = this.tmpTileRange_; - this.newTiles_ = false; - let tile, x, y; - for (x = tileRange.minX; x <= tileRange.maxX; ++x) { - for (y = tileRange.minY; y <= tileRange.maxY; ++y) { - if (Date.now() - frameState.time > 16 && animatingOrInteracting) { - continue; - } - tile = this.getTile(z, x, y, pixelRatio, projection); - if (this.isDrawableTile_(tile)) { - const uid = getUid(this); - if (tile.getState() == TileState.LOADED) { - tilesToDrawByZ[z][tile.tileCoord.toString()] = tile; - const inTransition = tile.inTransition(uid); - if (!this.newTiles_ && (inTransition || this.renderedTiles.indexOf(tile) === -1)) { - this.newTiles_ = true; - } - } - if (tile.getAlpha(uid, frameState.time) === 1) { - // don't look for alt tiles if alpha is 1 - continue; - } - } - - const childTileRange = tileGrid.getTileCoordChildTileRange( - tile.tileCoord, tmpTileRange, tmpExtent); - let covered = false; - if (childTileRange) { - covered = findLoadedTiles(z + 1, childTileRange); - } - if (!covered) { - tileGrid.forEachTileCoordParentTileRange( - tile.tileCoord, findLoadedTiles, null, tmpTileRange, tmpExtent); - } - - } - } - - const renderedResolution = tileResolution * pixelRatio / tilePixelRatio * oversampling; - if (!(this.renderedResolution && Date.now() - frameState.time > 16 && animatingOrInteracting) && ( - this.newTiles_ || - !(this.renderedExtent_ && containsExtent(this.renderedExtent_, extent)) || - this.renderedRevision != sourceRevision || - oversampling != this.oversampling_ || - !animatingOrInteracting && renderedResolution != this.renderedResolution - )) { - - const context = this.context; - if (context) { - const tilePixelSize = tileSource.getTilePixelSize(z, pixelRatio, projection); - const width = Math.round(tileRange.getWidth() * tilePixelSize[0] / oversampling); - const height = Math.round(tileRange.getHeight() * tilePixelSize[1] / oversampling); - const canvas = context.canvas; - if (canvas.width != width || canvas.height != height) { - this.oversampling_ = oversampling; - canvas.width = width; - canvas.height = height; - } else { - if (this.renderedExtent_ && !equals(imageExtent, this.renderedExtent_)) { - context.clearRect(0, 0, width, height); - } - oversampling = this.oversampling_; - } - } - - this.renderedTiles.length = 0; - /** @type {Array.} */ - const zs = Object.keys(tilesToDrawByZ).map(Number); - zs.sort(function(a, b) { - if (a === z) { - return 1; - } else if (b === z) { - return -1; - } else { - return a > b ? 1 : a < b ? -1 : 0; - } - }); - let currentResolution, currentScale, currentTilePixelSize, currentZ, i, ii; - let tileExtent, tileGutter, tilesToDraw, w, h; - for (i = 0, ii = zs.length; i < ii; ++i) { - currentZ = zs[i]; - currentTilePixelSize = tileSource.getTilePixelSize(currentZ, pixelRatio, projection); - currentResolution = tileGrid.getResolution(currentZ); - currentScale = currentResolution / tileResolution; - tileGutter = tilePixelRatio * tileSource.getGutter(projection); - tilesToDraw = tilesToDrawByZ[currentZ]; - for (const tileCoordKey in tilesToDraw) { - tile = tilesToDraw[tileCoordKey]; - tileExtent = tileGrid.getTileCoordExtent(tile.getTileCoord(), tmpExtent); - x = (tileExtent[0] - imageExtent[0]) / tileResolution * tilePixelRatio / oversampling; - y = (imageExtent[3] - tileExtent[3]) / tileResolution * tilePixelRatio / oversampling; - w = currentTilePixelSize[0] * currentScale / oversampling; - h = currentTilePixelSize[1] * currentScale / oversampling; - this.drawTileImage(tile, frameState, layerState, x, y, w, h, tileGutter, z === currentZ); - this.renderedTiles.push(tile); - } - } - - this.renderedRevision = sourceRevision; - this.renderedResolution = tileResolution * pixelRatio / tilePixelRatio * oversampling; - this.renderedExtent_ = imageExtent; - } - - const scale = this.renderedResolution / viewResolution; - const transform = composeTransform(this.imageTransform_, - pixelRatio * size[0] / 2, pixelRatio * size[1] / 2, - scale, scale, - 0, - (this.renderedExtent_[0] - viewCenter[0]) / this.renderedResolution * pixelRatio, - (viewCenter[1] - this.renderedExtent_[3]) / this.renderedResolution * pixelRatio); - composeTransform(this.coordinateToCanvasPixelTransform, - pixelRatio * size[0] / 2 - transform[4], pixelRatio * size[1] / 2 - transform[5], - pixelRatio / viewResolution, -pixelRatio / viewResolution, - 0, - -viewCenter[0], -viewCenter[1]); - - - this.updateUsedTiles(frameState.usedTiles, tileSource, z, tileRange); - this.manageTilePyramid(frameState, tileSource, tileGrid, pixelRatio, - projection, extent, z, tileLayer.getPreload()); - this.scheduleExpireCache(frameState, tileSource); - - return this.renderedTiles.length > 0; -}; - - -/** - * @param {module:ol/Tile} tile Tile. - * @param {module:ol/PluggableMap~FrameState} frameState Frame state. - * @param {module:ol/layer/Layer~State} layerState Layer state. - * @param {number} x Left of the tile. - * @param {number} y Top of the tile. - * @param {number} w Width of the tile. - * @param {number} h Height of the tile. - * @param {number} gutter Tile gutter. - * @param {boolean} transition Apply an alpha transition. - */ -CanvasTileLayerRenderer.prototype.drawTileImage = function(tile, frameState, layerState, x, y, w, h, gutter, transition) { - const image = tile.getImage(this.getLayer()); - if (!image) { - return; - } - const uid = getUid(this); - const alpha = transition ? tile.getAlpha(uid, frameState.time) : 1; - if (alpha === 1 && !this.getLayer().getSource().getOpaque(frameState.viewState.projection)) { - this.context.clearRect(x, y, w, h); - } - const alphaChanged = alpha !== this.context.globalAlpha; - if (alphaChanged) { - this.context.save(); - this.context.globalAlpha = alpha; - } - this.context.drawImage(image, gutter, gutter, - image.width - 2 * gutter, image.height - 2 * gutter, x, y, w, h); - - if (alphaChanged) { - this.context.restore(); - } - if (alpha !== 1) { - frameState.animate = true; - } else if (transition) { - tile.endTransition(uid); - } -}; - - -/** - * @inheritDoc - */ -CanvasTileLayerRenderer.prototype.getImage = function() { - const context = this.context; - return context ? context.canvas : null; -}; - - /** * @function * @return {module:ol/layer/Tile|module:ol/layer/VectorTile} @@ -377,10 +382,4 @@ CanvasTileLayerRenderer.prototype.getImage = function() { CanvasTileLayerRenderer.prototype.getLayer; -/** - * @inheritDoc - */ -CanvasTileLayerRenderer.prototype.getImageTransform = function() { - return this.imageTransform_; -}; export default CanvasTileLayerRenderer; diff --git a/src/ol/renderer/canvas/VectorLayer.js b/src/ol/renderer/canvas/VectorLayer.js index f2e2fe9235..206c5f5c49 100644 --- a/src/ol/renderer/canvas/VectorLayer.js +++ b/src/ol/renderer/canvas/VectorLayer.js @@ -21,66 +21,391 @@ import {defaultOrder as defaultRenderOrder, getTolerance as getRenderTolerance, * @param {module:ol/layer/Vector} vectorLayer Vector layer. * @api */ -const CanvasVectorLayerRenderer = function(vectorLayer) { +class CanvasVectorLayerRenderer { + constructor(vectorLayer) { - CanvasLayerRenderer.call(this, vectorLayer); + CanvasLayerRenderer.call(this, vectorLayer); + + /** + * Declutter tree. + * @private + */ + this.declutterTree_ = vectorLayer.getDeclutter() ? rbush(9, undefined) : null; + + /** + * @private + * @type {boolean} + */ + this.dirty_ = false; + + /** + * @private + * @type {number} + */ + this.renderedRevision_ = -1; + + /** + * @private + * @type {number} + */ + this.renderedResolution_ = NaN; + + /** + * @private + * @type {module:ol/extent~Extent} + */ + this.renderedExtent_ = createEmpty(); + + /** + * @private + * @type {function(module:ol/Feature, module:ol/Feature): number|null} + */ + this.renderedRenderOrder_ = null; + + /** + * @private + * @type {module:ol/render/canvas/ReplayGroup} + */ + this.replayGroup_ = null; + + /** + * A new replay group had to be created by `prepareFrame()` + * @type {boolean} + */ + this.replayGroupChanged = true; + + /** + * @type {CanvasRenderingContext2D} + */ + this.context = createCanvasContext2D(); + + listen(labelCache, EventType.CLEAR, this.handleFontsChanged_, this); + + } /** - * Declutter tree. + * @inheritDoc + */ + disposeInternal() { + unlisten(labelCache, EventType.CLEAR, this.handleFontsChanged_, this); + CanvasLayerRenderer.prototype.disposeInternal.call(this); + } + + /** + * @param {CanvasRenderingContext2D} context Context. + * @param {module:ol/PluggableMap~FrameState} frameState Frame state. + * @param {module:ol/layer/Layer~State} layerState Layer state. + */ + compose(context, frameState, layerState) { + const extent = frameState.extent; + const pixelRatio = frameState.pixelRatio; + const skippedFeatureUids = layerState.managed ? + frameState.skippedFeatureUids : {}; + const viewState = frameState.viewState; + const projection = viewState.projection; + const rotation = viewState.rotation; + const projectionExtent = projection.getExtent(); + const vectorSource = /** @type {module:ol/source/Vector} */ (this.getLayer().getSource()); + + let transform = this.getTransform(frameState, 0); + + // clipped rendering if layer extent is set + const clipExtent = layerState.extent; + const clipped = clipExtent !== undefined; + if (clipped) { + this.clip(context, frameState, /** @type {module:ol/extent~Extent} */ (clipExtent)); + } + const replayGroup = this.replayGroup_; + if (replayGroup && !replayGroup.isEmpty()) { + if (this.declutterTree_) { + this.declutterTree_.clear(); + } + const layer = /** @type {module:ol/layer/Vector} */ (this.getLayer()); + let drawOffsetX = 0; + let drawOffsetY = 0; + let replayContext; + const transparentLayer = layerState.opacity !== 1; + const hasRenderListeners = layer.hasListener(RenderEventType.RENDER); + if (transparentLayer || hasRenderListeners) { + let drawWidth = context.canvas.width; + let drawHeight = context.canvas.height; + if (rotation) { + const drawSize = Math.round(Math.sqrt(drawWidth * drawWidth + drawHeight * drawHeight)); + drawOffsetX = (drawSize - drawWidth) / 2; + drawOffsetY = (drawSize - drawHeight) / 2; + drawWidth = drawHeight = drawSize; + } + // resize and clear + this.context.canvas.width = drawWidth; + this.context.canvas.height = drawHeight; + replayContext = this.context; + } else { + replayContext = context; + } + + const alpha = replayContext.globalAlpha; + if (!transparentLayer) { + // for performance reasons, context.save / context.restore is not used + // to save and restore the transformation matrix and the opacity. + // see http://jsperf.com/context-save-restore-versus-variable + replayContext.globalAlpha = layerState.opacity; + } + + if (replayContext != context) { + replayContext.translate(drawOffsetX, drawOffsetY); + } + + const width = frameState.size[0] * pixelRatio; + const height = frameState.size[1] * pixelRatio; + rotateAtOffset(replayContext, -rotation, + width / 2, height / 2); + replayGroup.replay(replayContext, transform, rotation, skippedFeatureUids); + if (vectorSource.getWrapX() && projection.canWrapX() && + !containsExtent(projectionExtent, extent)) { + let startX = extent[0]; + const worldWidth = getWidth(projectionExtent); + let world = 0; + let offsetX; + while (startX < projectionExtent[0]) { + --world; + offsetX = worldWidth * world; + transform = this.getTransform(frameState, offsetX); + replayGroup.replay(replayContext, transform, rotation, skippedFeatureUids); + startX += worldWidth; + } + world = 0; + startX = extent[2]; + while (startX > projectionExtent[2]) { + ++world; + offsetX = worldWidth * world; + transform = this.getTransform(frameState, offsetX); + replayGroup.replay(replayContext, transform, rotation, skippedFeatureUids); + startX -= worldWidth; + } + } + rotateAtOffset(replayContext, rotation, + width / 2, height / 2); + + if (hasRenderListeners) { + this.dispatchRenderEvent(replayContext, frameState, transform); + } + if (replayContext != context) { + if (transparentLayer) { + const mainContextAlpha = context.globalAlpha; + context.globalAlpha = layerState.opacity; + context.drawImage(replayContext.canvas, -drawOffsetX, -drawOffsetY); + context.globalAlpha = mainContextAlpha; + } else { + context.drawImage(replayContext.canvas, -drawOffsetX, -drawOffsetY); + } + replayContext.translate(-drawOffsetX, -drawOffsetY); + } + + if (!transparentLayer) { + replayContext.globalAlpha = alpha; + } + } + + if (clipped) { + context.restore(); + } + } + + /** + * @inheritDoc + */ + composeFrame(frameState, layerState, context) { + const transform = this.getTransform(frameState, 0); + this.preCompose(context, frameState, transform); + this.compose(context, frameState, layerState); + this.postCompose(context, frameState, layerState, transform); + } + + /** + * @inheritDoc + */ + forEachFeatureAtCoordinate(coordinate, frameState, hitTolerance, callback, thisArg) { + if (!this.replayGroup_) { + return undefined; + } else { + const resolution = frameState.viewState.resolution; + const rotation = frameState.viewState.rotation; + const layer = /** @type {module:ol/layer/Vector} */ (this.getLayer()); + /** @type {!Object.} */ + const features = {}; + const result = this.replayGroup_.forEachFeatureAtCoordinate(coordinate, resolution, rotation, hitTolerance, {}, + /** + * @param {module:ol/Feature|module:ol/render/Feature} feature Feature. + * @return {?} Callback result. + */ + function(feature) { + const key = getUid(feature).toString(); + if (!(key in features)) { + features[key] = true; + return callback.call(thisArg, feature, layer); + } + }, null); + return result; + } + } + + /** + * @param {module:ol/events/Event} event Event. + */ + handleFontsChanged_(event) { + const layer = this.getLayer(); + if (layer.getVisible() && this.replayGroup_) { + layer.changed(); + } + } + + /** + * Handle changes in image style state. + * @param {module:ol/events/Event} event Image style change event. * @private */ - this.declutterTree_ = vectorLayer.getDeclutter() ? rbush(9, undefined) : null; + handleStyleImageChange_(event) { + this.renderIfReadyAndVisible(); + } /** - * @private - * @type {boolean} + * @inheritDoc */ - this.dirty_ = false; + prepareFrame(frameState, layerState) { + const vectorLayer = /** @type {module:ol/layer/Vector} */ (this.getLayer()); + const vectorSource = vectorLayer.getSource(); + + const animating = frameState.viewHints[ViewHint.ANIMATING]; + const interacting = frameState.viewHints[ViewHint.INTERACTING]; + const updateWhileAnimating = vectorLayer.getUpdateWhileAnimating(); + const updateWhileInteracting = vectorLayer.getUpdateWhileInteracting(); + + if (!this.dirty_ && (!updateWhileAnimating && animating) || + (!updateWhileInteracting && interacting)) { + return true; + } + + const frameStateExtent = frameState.extent; + const viewState = frameState.viewState; + const projection = viewState.projection; + const resolution = viewState.resolution; + const pixelRatio = frameState.pixelRatio; + const vectorLayerRevision = vectorLayer.getRevision(); + const vectorLayerRenderBuffer = vectorLayer.getRenderBuffer(); + let vectorLayerRenderOrder = vectorLayer.getRenderOrder(); + + if (vectorLayerRenderOrder === undefined) { + vectorLayerRenderOrder = defaultRenderOrder; + } + + const extent = buffer(frameStateExtent, + vectorLayerRenderBuffer * resolution); + const projectionExtent = viewState.projection.getExtent(); + + if (vectorSource.getWrapX() && viewState.projection.canWrapX() && + !containsExtent(projectionExtent, frameState.extent)) { + // For the replay group, we need an extent that intersects the real world + // (-180° to +180°). To support geometries in a coordinate range from -540° + // to +540°, we add at least 1 world width on each side of the projection + // extent. If the viewport is wider than the world, we need to add half of + // the viewport width to make sure we cover the whole viewport. + const worldWidth = getWidth(projectionExtent); + const gutter = Math.max(getWidth(extent) / 2, worldWidth); + extent[0] = projectionExtent[0] - gutter; + extent[2] = projectionExtent[2] + gutter; + } + + if (!this.dirty_ && + this.renderedResolution_ == resolution && + this.renderedRevision_ == vectorLayerRevision && + this.renderedRenderOrder_ == vectorLayerRenderOrder && + containsExtent(this.renderedExtent_, extent)) { + this.replayGroupChanged = false; + return true; + } + + this.replayGroup_ = null; + + this.dirty_ = false; + + const replayGroup = new CanvasReplayGroup( + getRenderTolerance(resolution, pixelRatio), extent, resolution, + pixelRatio, vectorSource.getOverlaps(), this.declutterTree_, vectorLayer.getRenderBuffer()); + vectorSource.loadFeatures(extent, resolution, projection); + /** + * @param {module:ol/Feature} feature Feature. + * @this {module:ol/renderer/canvas/VectorLayer} + */ + const render = function(feature) { + let styles; + const styleFunction = feature.getStyleFunction() || vectorLayer.getStyleFunction(); + if (styleFunction) { + styles = styleFunction(feature, resolution); + } + if (styles) { + const dirty = this.renderFeature( + feature, resolution, pixelRatio, styles, replayGroup); + this.dirty_ = this.dirty_ || dirty; + } + }.bind(this); + if (vectorLayerRenderOrder) { + /** @type {Array.} */ + const features = []; + vectorSource.forEachFeatureInExtent(extent, + /** + * @param {module:ol/Feature} feature Feature. + */ + function(feature) { + features.push(feature); + }, this); + features.sort(vectorLayerRenderOrder); + for (let i = 0, ii = features.length; i < ii; ++i) { + render(features[i]); + } + } else { + vectorSource.forEachFeatureInExtent(extent, render, this); + } + replayGroup.finish(); + + this.renderedResolution_ = resolution; + this.renderedRevision_ = vectorLayerRevision; + this.renderedRenderOrder_ = vectorLayerRenderOrder; + this.renderedExtent_ = extent; + this.replayGroup_ = replayGroup; + + this.replayGroupChanged = true; + return true; + } /** - * @private - * @type {number} + * @param {module:ol/Feature} feature Feature. + * @param {number} resolution Resolution. + * @param {number} pixelRatio Pixel ratio. + * @param {(module:ol/style/Style|Array.)} styles The style or array of styles. + * @param {module:ol/render/canvas/ReplayGroup} replayGroup Replay group. + * @return {boolean} `true` if an image is loading. */ - this.renderedRevision_ = -1; - - /** - * @private - * @type {number} - */ - this.renderedResolution_ = NaN; - - /** - * @private - * @type {module:ol/extent~Extent} - */ - this.renderedExtent_ = createEmpty(); - - /** - * @private - * @type {function(module:ol/Feature, module:ol/Feature): number|null} - */ - this.renderedRenderOrder_ = null; - - /** - * @private - * @type {module:ol/render/canvas/ReplayGroup} - */ - this.replayGroup_ = null; - - /** - * A new replay group had to be created by `prepareFrame()` - * @type {boolean} - */ - this.replayGroupChanged = true; - - /** - * @type {CanvasRenderingContext2D} - */ - this.context = createCanvasContext2D(); - - listen(labelCache, EventType.CLEAR, this.handleFontsChanged_, this); - -}; + renderFeature(feature, resolution, pixelRatio, styles, replayGroup) { + if (!styles) { + return false; + } + let loading = false; + if (Array.isArray(styles)) { + for (let i = 0, ii = styles.length; i < ii; ++i) { + loading = renderFeature( + replayGroup, feature, styles[i], + getSquaredRenderTolerance(resolution, pixelRatio), + this.handleStyleImageChange_, this) || loading; + } + } else { + loading = renderFeature( + replayGroup, feature, styles, + getSquaredRenderTolerance(resolution, pixelRatio), + this.handleStyleImageChange_, this); + } + return loading; + } +} inherits(CanvasVectorLayerRenderer, CanvasLayerRenderer); @@ -106,333 +431,4 @@ CanvasVectorLayerRenderer['create'] = function(mapRenderer, layer) { }; -/** - * @inheritDoc - */ -CanvasVectorLayerRenderer.prototype.disposeInternal = function() { - unlisten(labelCache, EventType.CLEAR, this.handleFontsChanged_, this); - CanvasLayerRenderer.prototype.disposeInternal.call(this); -}; - - -/** - * @param {CanvasRenderingContext2D} context Context. - * @param {module:ol/PluggableMap~FrameState} frameState Frame state. - * @param {module:ol/layer/Layer~State} layerState Layer state. - */ -CanvasVectorLayerRenderer.prototype.compose = function(context, frameState, layerState) { - const extent = frameState.extent; - const pixelRatio = frameState.pixelRatio; - const skippedFeatureUids = layerState.managed ? - frameState.skippedFeatureUids : {}; - const viewState = frameState.viewState; - const projection = viewState.projection; - const rotation = viewState.rotation; - const projectionExtent = projection.getExtent(); - const vectorSource = /** @type {module:ol/source/Vector} */ (this.getLayer().getSource()); - - let transform = this.getTransform(frameState, 0); - - // clipped rendering if layer extent is set - const clipExtent = layerState.extent; - const clipped = clipExtent !== undefined; - if (clipped) { - this.clip(context, frameState, /** @type {module:ol/extent~Extent} */ (clipExtent)); - } - const replayGroup = this.replayGroup_; - if (replayGroup && !replayGroup.isEmpty()) { - if (this.declutterTree_) { - this.declutterTree_.clear(); - } - const layer = /** @type {module:ol/layer/Vector} */ (this.getLayer()); - let drawOffsetX = 0; - let drawOffsetY = 0; - let replayContext; - const transparentLayer = layerState.opacity !== 1; - const hasRenderListeners = layer.hasListener(RenderEventType.RENDER); - if (transparentLayer || hasRenderListeners) { - let drawWidth = context.canvas.width; - let drawHeight = context.canvas.height; - if (rotation) { - const drawSize = Math.round(Math.sqrt(drawWidth * drawWidth + drawHeight * drawHeight)); - drawOffsetX = (drawSize - drawWidth) / 2; - drawOffsetY = (drawSize - drawHeight) / 2; - drawWidth = drawHeight = drawSize; - } - // resize and clear - this.context.canvas.width = drawWidth; - this.context.canvas.height = drawHeight; - replayContext = this.context; - } else { - replayContext = context; - } - - const alpha = replayContext.globalAlpha; - if (!transparentLayer) { - // for performance reasons, context.save / context.restore is not used - // to save and restore the transformation matrix and the opacity. - // see http://jsperf.com/context-save-restore-versus-variable - replayContext.globalAlpha = layerState.opacity; - } - - if (replayContext != context) { - replayContext.translate(drawOffsetX, drawOffsetY); - } - - const width = frameState.size[0] * pixelRatio; - const height = frameState.size[1] * pixelRatio; - rotateAtOffset(replayContext, -rotation, - width / 2, height / 2); - replayGroup.replay(replayContext, transform, rotation, skippedFeatureUids); - if (vectorSource.getWrapX() && projection.canWrapX() && - !containsExtent(projectionExtent, extent)) { - let startX = extent[0]; - const worldWidth = getWidth(projectionExtent); - let world = 0; - let offsetX; - while (startX < projectionExtent[0]) { - --world; - offsetX = worldWidth * world; - transform = this.getTransform(frameState, offsetX); - replayGroup.replay(replayContext, transform, rotation, skippedFeatureUids); - startX += worldWidth; - } - world = 0; - startX = extent[2]; - while (startX > projectionExtent[2]) { - ++world; - offsetX = worldWidth * world; - transform = this.getTransform(frameState, offsetX); - replayGroup.replay(replayContext, transform, rotation, skippedFeatureUids); - startX -= worldWidth; - } - } - rotateAtOffset(replayContext, rotation, - width / 2, height / 2); - - if (hasRenderListeners) { - this.dispatchRenderEvent(replayContext, frameState, transform); - } - if (replayContext != context) { - if (transparentLayer) { - const mainContextAlpha = context.globalAlpha; - context.globalAlpha = layerState.opacity; - context.drawImage(replayContext.canvas, -drawOffsetX, -drawOffsetY); - context.globalAlpha = mainContextAlpha; - } else { - context.drawImage(replayContext.canvas, -drawOffsetX, -drawOffsetY); - } - replayContext.translate(-drawOffsetX, -drawOffsetY); - } - - if (!transparentLayer) { - replayContext.globalAlpha = alpha; - } - } - - if (clipped) { - context.restore(); - } -}; - - -/** - * @inheritDoc - */ -CanvasVectorLayerRenderer.prototype.composeFrame = function(frameState, layerState, context) { - const transform = this.getTransform(frameState, 0); - this.preCompose(context, frameState, transform); - this.compose(context, frameState, layerState); - this.postCompose(context, frameState, layerState, transform); -}; - - -/** - * @inheritDoc - */ -CanvasVectorLayerRenderer.prototype.forEachFeatureAtCoordinate = function(coordinate, frameState, hitTolerance, callback, thisArg) { - if (!this.replayGroup_) { - return undefined; - } else { - const resolution = frameState.viewState.resolution; - const rotation = frameState.viewState.rotation; - const layer = /** @type {module:ol/layer/Vector} */ (this.getLayer()); - /** @type {!Object.} */ - const features = {}; - const result = this.replayGroup_.forEachFeatureAtCoordinate(coordinate, resolution, rotation, hitTolerance, {}, - /** - * @param {module:ol/Feature|module:ol/render/Feature} feature Feature. - * @return {?} Callback result. - */ - function(feature) { - const key = getUid(feature).toString(); - if (!(key in features)) { - features[key] = true; - return callback.call(thisArg, feature, layer); - } - }, null); - return result; - } -}; - - -/** - * @param {module:ol/events/Event} event Event. - */ -CanvasVectorLayerRenderer.prototype.handleFontsChanged_ = function(event) { - const layer = this.getLayer(); - if (layer.getVisible() && this.replayGroup_) { - layer.changed(); - } -}; - - -/** - * Handle changes in image style state. - * @param {module:ol/events/Event} event Image style change event. - * @private - */ -CanvasVectorLayerRenderer.prototype.handleStyleImageChange_ = function(event) { - this.renderIfReadyAndVisible(); -}; - - -/** - * @inheritDoc - */ -CanvasVectorLayerRenderer.prototype.prepareFrame = function(frameState, layerState) { - const vectorLayer = /** @type {module:ol/layer/Vector} */ (this.getLayer()); - const vectorSource = vectorLayer.getSource(); - - const animating = frameState.viewHints[ViewHint.ANIMATING]; - const interacting = frameState.viewHints[ViewHint.INTERACTING]; - const updateWhileAnimating = vectorLayer.getUpdateWhileAnimating(); - const updateWhileInteracting = vectorLayer.getUpdateWhileInteracting(); - - if (!this.dirty_ && (!updateWhileAnimating && animating) || - (!updateWhileInteracting && interacting)) { - return true; - } - - const frameStateExtent = frameState.extent; - const viewState = frameState.viewState; - const projection = viewState.projection; - const resolution = viewState.resolution; - const pixelRatio = frameState.pixelRatio; - const vectorLayerRevision = vectorLayer.getRevision(); - const vectorLayerRenderBuffer = vectorLayer.getRenderBuffer(); - let vectorLayerRenderOrder = vectorLayer.getRenderOrder(); - - if (vectorLayerRenderOrder === undefined) { - vectorLayerRenderOrder = defaultRenderOrder; - } - - const extent = buffer(frameStateExtent, - vectorLayerRenderBuffer * resolution); - const projectionExtent = viewState.projection.getExtent(); - - if (vectorSource.getWrapX() && viewState.projection.canWrapX() && - !containsExtent(projectionExtent, frameState.extent)) { - // For the replay group, we need an extent that intersects the real world - // (-180° to +180°). To support geometries in a coordinate range from -540° - // to +540°, we add at least 1 world width on each side of the projection - // extent. If the viewport is wider than the world, we need to add half of - // the viewport width to make sure we cover the whole viewport. - const worldWidth = getWidth(projectionExtent); - const gutter = Math.max(getWidth(extent) / 2, worldWidth); - extent[0] = projectionExtent[0] - gutter; - extent[2] = projectionExtent[2] + gutter; - } - - if (!this.dirty_ && - this.renderedResolution_ == resolution && - this.renderedRevision_ == vectorLayerRevision && - this.renderedRenderOrder_ == vectorLayerRenderOrder && - containsExtent(this.renderedExtent_, extent)) { - this.replayGroupChanged = false; - return true; - } - - this.replayGroup_ = null; - - this.dirty_ = false; - - const replayGroup = new CanvasReplayGroup( - getRenderTolerance(resolution, pixelRatio), extent, resolution, - pixelRatio, vectorSource.getOverlaps(), this.declutterTree_, vectorLayer.getRenderBuffer()); - vectorSource.loadFeatures(extent, resolution, projection); - /** - * @param {module:ol/Feature} feature Feature. - * @this {module:ol/renderer/canvas/VectorLayer} - */ - const render = function(feature) { - let styles; - const styleFunction = feature.getStyleFunction() || vectorLayer.getStyleFunction(); - if (styleFunction) { - styles = styleFunction(feature, resolution); - } - if (styles) { - const dirty = this.renderFeature( - feature, resolution, pixelRatio, styles, replayGroup); - this.dirty_ = this.dirty_ || dirty; - } - }.bind(this); - if (vectorLayerRenderOrder) { - /** @type {Array.} */ - const features = []; - vectorSource.forEachFeatureInExtent(extent, - /** - * @param {module:ol/Feature} feature Feature. - */ - function(feature) { - features.push(feature); - }, this); - features.sort(vectorLayerRenderOrder); - for (let i = 0, ii = features.length; i < ii; ++i) { - render(features[i]); - } - } else { - vectorSource.forEachFeatureInExtent(extent, render, this); - } - replayGroup.finish(); - - this.renderedResolution_ = resolution; - this.renderedRevision_ = vectorLayerRevision; - this.renderedRenderOrder_ = vectorLayerRenderOrder; - this.renderedExtent_ = extent; - this.replayGroup_ = replayGroup; - - this.replayGroupChanged = true; - return true; -}; - - -/** - * @param {module:ol/Feature} feature Feature. - * @param {number} resolution Resolution. - * @param {number} pixelRatio Pixel ratio. - * @param {(module:ol/style/Style|Array.)} styles The style or array of styles. - * @param {module:ol/render/canvas/ReplayGroup} replayGroup Replay group. - * @return {boolean} `true` if an image is loading. - */ -CanvasVectorLayerRenderer.prototype.renderFeature = function(feature, resolution, pixelRatio, styles, replayGroup) { - if (!styles) { - return false; - } - let loading = false; - if (Array.isArray(styles)) { - for (let i = 0, ii = styles.length; i < ii; ++i) { - loading = renderFeature( - replayGroup, feature, styles[i], - getSquaredRenderTolerance(resolution, pixelRatio), - this.handleStyleImageChange_, this) || loading; - } - } else { - loading = renderFeature( - replayGroup, feature, styles, - getSquaredRenderTolerance(resolution, pixelRatio), - this.handleStyleImageChange_, this); - } - return loading; -}; export default CanvasVectorLayerRenderer; diff --git a/src/ol/renderer/canvas/VectorTileLayer.js b/src/ol/renderer/canvas/VectorTileLayer.js index afe459dc2a..deb400bafd 100644 --- a/src/ol/renderer/canvas/VectorTileLayer.js +++ b/src/ol/renderer/canvas/VectorTileLayer.js @@ -53,45 +53,426 @@ const VECTOR_REPLAYS = { * @param {module:ol/layer/VectorTile} layer VectorTile layer. * @api */ -const CanvasVectorTileLayerRenderer = function(layer) { +class CanvasVectorTileLayerRenderer { + constructor(layer) { + + /** + * @type {CanvasRenderingContext2D} + */ + this.context = null; + + CanvasTileLayerRenderer.call(this, layer); + + /** + * Declutter tree. + * @private + */ + this.declutterTree_ = layer.getDeclutter() ? rbush(9, undefined) : null; + + /** + * @private + * @type {boolean} + */ + this.dirty_ = false; + + /** + * @private + * @type {number} + */ + this.renderedLayerRevision_; + + /** + * @private + * @type {module:ol/transform~Transform} + */ + this.tmpTransform_ = createTransform(); + + // Use lower resolution for pure vector rendering. Closest resolution otherwise. + this.zDirection = layer.getRenderMode() == VectorTileRenderType.VECTOR ? 1 : 0; + + listen(labelCache, EventType.CLEAR, this.handleFontsChanged_, this); + + } /** - * @type {CanvasRenderingContext2D} + * @inheritDoc */ - this.context = null; - - CanvasTileLayerRenderer.call(this, layer); + disposeInternal() { + unlisten(labelCache, EventType.CLEAR, this.handleFontsChanged_, this); + CanvasTileLayerRenderer.prototype.disposeInternal.call(this); + } /** - * Declutter tree. + * @inheritDoc + */ + getTile(z, x, y, pixelRatio, projection) { + const tile = CanvasTileLayerRenderer.prototype.getTile.call(this, z, x, y, pixelRatio, projection); + if (tile.getState() === TileState.LOADED) { + this.createReplayGroup_(tile, pixelRatio, projection); + if (this.context) { + this.renderTileImage_(tile, pixelRatio, projection); + } + } + return tile; + } + + /** + * @inheritDoc + */ + prepareFrame(frameState, layerState) { + const layer = this.getLayer(); + const layerRevision = layer.getRevision(); + if (this.renderedLayerRevision_ != layerRevision) { + this.renderedTiles.length = 0; + const renderMode = layer.getRenderMode(); + if (!this.context && renderMode != VectorTileRenderType.VECTOR) { + this.context = createCanvasContext2D(); + } + if (this.context && renderMode == VectorTileRenderType.VECTOR) { + this.context = null; + } + } + this.renderedLayerRevision_ = layerRevision; + return CanvasTileLayerRenderer.prototype.prepareFrame.apply(this, arguments); + } + + /** + * @param {module:ol/VectorImageTile} tile Tile. + * @param {number} pixelRatio Pixel ratio. + * @param {module:ol/proj/Projection} projection Projection. * @private */ - this.declutterTree_ = layer.getDeclutter() ? rbush(9, undefined) : null; + createReplayGroup_(tile, pixelRatio, projection) { + const layer = this.getLayer(); + const revision = layer.getRevision(); + const renderOrder = /** @type {module:ol/render~OrderFunction} */ (layer.getRenderOrder()) || null; + + const replayState = tile.getReplayState(layer); + if (!replayState.dirty && replayState.renderedRevision == revision && + replayState.renderedRenderOrder == renderOrder) { + return; + } + + const source = /** @type {module:ol/source/VectorTile} */ (layer.getSource()); + const sourceTileGrid = source.getTileGrid(); + const tileGrid = source.getTileGridForProjection(projection); + const resolution = tileGrid.getResolution(tile.tileCoord[0]); + const tileExtent = tile.extent; + + const zIndexKeys = {}; + for (let t = 0, tt = tile.tileKeys.length; t < tt; ++t) { + const sourceTile = tile.getTile(tile.tileKeys[t]); + if (sourceTile.getState() != TileState.LOADED) { + continue; + } + + const sourceTileCoord = sourceTile.tileCoord; + const sourceTileExtent = sourceTileGrid.getTileCoordExtent(sourceTileCoord); + const sharedExtent = getIntersection(tileExtent, sourceTileExtent); + const bufferedExtent = equals(sourceTileExtent, sharedExtent) ? null : + buffer(sharedExtent, layer.getRenderBuffer() * resolution, this.tmpExtent); + const tileProjection = sourceTile.getProjection(); + let reproject = false; + if (!equivalentProjection(projection, tileProjection)) { + reproject = true; + sourceTile.setProjection(projection); + } + replayState.dirty = false; + const replayGroup = new CanvasReplayGroup(0, sharedExtent, resolution, + pixelRatio, source.getOverlaps(), this.declutterTree_, layer.getRenderBuffer()); + const squaredTolerance = getSquaredRenderTolerance(resolution, pixelRatio); + + /** + * @param {module:ol/Feature|module:ol/render/Feature} feature Feature. + * @this {module:ol/renderer/canvas/VectorTileLayer} + */ + const render = function(feature) { + let styles; + const styleFunction = feature.getStyleFunction() || layer.getStyleFunction(); + if (styleFunction) { + styles = styleFunction(feature, resolution); + } + if (styles) { + const dirty = this.renderFeature(feature, squaredTolerance, styles, replayGroup); + this.dirty_ = this.dirty_ || dirty; + replayState.dirty = replayState.dirty || dirty; + } + }; + + const features = sourceTile.getFeatures(); + if (renderOrder && renderOrder !== replayState.renderedRenderOrder) { + features.sort(renderOrder); + } + for (let i = 0, ii = features.length; i < ii; ++i) { + const feature = features[i]; + if (reproject) { + if (tileProjection.getUnits() == Units.TILE_PIXELS) { + // projected tile extent + tileProjection.setWorldExtent(sourceTileExtent); + // tile extent in tile pixel space + tileProjection.setExtent(sourceTile.getExtent()); + } + feature.getGeometry().transform(tileProjection, projection); + } + if (!bufferedExtent || intersects(bufferedExtent, feature.getGeometry().getExtent())) { + render.call(this, feature); + } + } + replayGroup.finish(); + for (const r in replayGroup.getReplays()) { + zIndexKeys[r] = true; + } + sourceTile.setReplayGroup(layer, tile.tileCoord.toString(), replayGroup); + } + replayState.renderedRevision = revision; + replayState.renderedRenderOrder = renderOrder; + } /** - * @private - * @type {boolean} + * @inheritDoc */ - this.dirty_ = false; + forEachFeatureAtCoordinate(coordinate, frameState, hitTolerance, callback, thisArg) { + const resolution = frameState.viewState.resolution; + const rotation = frameState.viewState.rotation; + hitTolerance = hitTolerance == undefined ? 0 : hitTolerance; + const layer = this.getLayer(); + /** @type {!Object.} */ + const features = {}; + + /** @type {Array.} */ + const renderedTiles = this.renderedTiles; + + let bufferedExtent, found; + let i, ii, replayGroup; + for (i = 0, ii = renderedTiles.length; i < ii; ++i) { + const tile = renderedTiles[i]; + bufferedExtent = buffer(tile.extent, hitTolerance * resolution, bufferedExtent); + if (!containsCoordinate(bufferedExtent, coordinate)) { + continue; + } + for (let t = 0, tt = tile.tileKeys.length; t < tt; ++t) { + const sourceTile = tile.getTile(tile.tileKeys[t]); + if (sourceTile.getState() != TileState.LOADED) { + continue; + } + replayGroup = sourceTile.getReplayGroup(layer, tile.tileCoord.toString()); + found = found || replayGroup.forEachFeatureAtCoordinate(coordinate, resolution, rotation, hitTolerance, {}, + /** + * @param {module:ol/Feature|module:ol/render/Feature} feature Feature. + * @return {?} Callback result. + */ + function(feature) { + const key = getUid(feature).toString(); + if (!(key in features)) { + features[key] = true; + return callback.call(thisArg, feature, layer); + } + }, null); + } + } + return found; + } /** + * @param {module:ol/VectorTile} tile Tile. + * @param {module:ol/PluggableMap~FrameState} frameState Frame state. + * @return {module:ol/transform~Transform} transform Transform. * @private - * @type {number} */ - this.renderedLayerRevision_; + getReplayTransform_(tile, frameState) { + const layer = this.getLayer(); + const source = /** @type {module:ol/source/VectorTile} */ (layer.getSource()); + const tileGrid = source.getTileGrid(); + const tileCoord = tile.tileCoord; + const tileResolution = tileGrid.getResolution(tileCoord[0]); + const viewState = frameState.viewState; + const pixelRatio = frameState.pixelRatio; + const renderResolution = viewState.resolution / pixelRatio; + const tileExtent = tileGrid.getTileCoordExtent(tileCoord, this.tmpExtent); + const center = viewState.center; + const origin = getTopLeft(tileExtent); + const size = frameState.size; + const offsetX = Math.round(pixelRatio * size[0] / 2); + const offsetY = Math.round(pixelRatio * size[1] / 2); + return composeTransform(this.tmpTransform_, + offsetX, offsetY, + tileResolution / renderResolution, tileResolution / renderResolution, + viewState.rotation, + (origin[0] - center[0]) / tileResolution, + (center[1] - origin[1]) / tileResolution); + } /** - * @private - * @type {module:ol/transform~Transform} + * @param {module:ol/events/Event} event Event. */ - this.tmpTransform_ = createTransform(); + handleFontsChanged_(event) { + const layer = this.getLayer(); + if (layer.getVisible() && this.renderedLayerRevision_ !== undefined) { + layer.changed(); + } + } - // Use lower resolution for pure vector rendering. Closest resolution otherwise. - this.zDirection = layer.getRenderMode() == VectorTileRenderType.VECTOR ? 1 : 0; + /** + * Handle changes in image style state. + * @param {module:ol/events/Event} event Image style change event. + * @private + */ + handleStyleImageChange_(event) { + this.renderIfReadyAndVisible(); + } - listen(labelCache, EventType.CLEAR, this.handleFontsChanged_, this); + /** + * @inheritDoc + */ + postCompose(context, frameState, layerState) { + const layer = this.getLayer(); + const renderMode = layer.getRenderMode(); + if (renderMode != VectorTileRenderType.IMAGE) { + const declutterReplays = layer.getDeclutter() ? {} : null; + const source = /** @type {module:ol/source/VectorTile} */ (layer.getSource()); + const replayTypes = VECTOR_REPLAYS[renderMode]; + const pixelRatio = frameState.pixelRatio; + const rotation = frameState.viewState.rotation; + const size = frameState.size; + let offsetX, offsetY; + if (rotation) { + offsetX = Math.round(pixelRatio * size[0] / 2); + offsetY = Math.round(pixelRatio * size[1] / 2); + rotateAtOffset(context, -rotation, offsetX, offsetY); + } + if (declutterReplays) { + this.declutterTree_.clear(); + } + const tiles = this.renderedTiles; + const tileGrid = source.getTileGridForProjection(frameState.viewState.projection); + const clips = []; + const zs = []; + for (let i = tiles.length - 1; i >= 0; --i) { + const tile = /** @type {module:ol/VectorImageTile} */ (tiles[i]); + if (tile.getState() == TileState.ABORT) { + continue; + } + const tileCoord = tile.tileCoord; + const worldOffset = tileGrid.getTileCoordExtent(tileCoord, this.tmpExtent)[0] - tile.extent[0]; + let transform = undefined; + for (let t = 0, tt = tile.tileKeys.length; t < tt; ++t) { + const sourceTile = tile.getTile(tile.tileKeys[t]); + if (sourceTile.getState() != TileState.LOADED) { + continue; + } + const replayGroup = sourceTile.getReplayGroup(layer, tileCoord.toString()); + if (!replayGroup || !replayGroup.hasReplays(replayTypes)) { + // sourceTile was not yet loaded when this.createReplayGroup_() was + // called, or it has no replays of the types we want to render + continue; + } + if (!transform) { + transform = this.getTransform(frameState, worldOffset); + } + const currentZ = sourceTile.tileCoord[0]; + const currentClip = replayGroup.getClipCoords(transform); + context.save(); + context.globalAlpha = layerState.opacity; + // Create a clip mask for regions in this low resolution tile that are + // already filled by a higher resolution tile + for (let j = 0, jj = clips.length; j < jj; ++j) { + const clip = clips[j]; + if (currentZ < zs[j]) { + context.beginPath(); + // counter-clockwise (outer ring) for current tile + context.moveTo(currentClip[0], currentClip[1]); + context.lineTo(currentClip[2], currentClip[3]); + context.lineTo(currentClip[4], currentClip[5]); + context.lineTo(currentClip[6], currentClip[7]); + // clockwise (inner ring) for higher resolution tile + context.moveTo(clip[6], clip[7]); + context.lineTo(clip[4], clip[5]); + context.lineTo(clip[2], clip[3]); + context.lineTo(clip[0], clip[1]); + context.clip(); + } + } + replayGroup.replay(context, transform, rotation, {}, replayTypes, declutterReplays); + context.restore(); + clips.push(currentClip); + zs.push(currentZ); + } + } + if (declutterReplays) { + replayDeclutter(declutterReplays, context, rotation); + } + if (rotation) { + rotateAtOffset(context, rotation, + /** @type {number} */ (offsetX), /** @type {number} */ (offsetY)); + } + } + CanvasTileLayerRenderer.prototype.postCompose.apply(this, arguments); + } -}; + /** + * @param {module:ol/Feature|module:ol/render/Feature} feature Feature. + * @param {number} squaredTolerance Squared tolerance. + * @param {(module:ol/style/Style|Array.)} styles The style or array of styles. + * @param {module:ol/render/canvas/ReplayGroup} replayGroup Replay group. + * @return {boolean} `true` if an image is loading. + */ + renderFeature(feature, squaredTolerance, styles, replayGroup) { + if (!styles) { + return false; + } + let loading = false; + if (Array.isArray(styles)) { + for (let i = 0, ii = styles.length; i < ii; ++i) { + loading = renderFeature( + replayGroup, feature, styles[i], squaredTolerance, + this.handleStyleImageChange_, this) || loading; + } + } else { + loading = renderFeature( + replayGroup, feature, styles, squaredTolerance, + this.handleStyleImageChange_, this); + } + return loading; + } + + /** + * @param {module:ol/VectorImageTile} tile Tile. + * @param {number} pixelRatio Pixel ratio. + * @param {module:ol/proj/Projection} projection Projection. + * @private + */ + renderTileImage_(tile, pixelRatio, projection) { + const layer = this.getLayer(); + const replayState = tile.getReplayState(layer); + const revision = layer.getRevision(); + const replays = IMAGE_REPLAYS[layer.getRenderMode()]; + if (replays && replayState.renderedTileRevision !== revision) { + replayState.renderedTileRevision = revision; + const tileCoord = tile.wrappedTileCoord; + const z = tileCoord[0]; + const source = /** @type {module:ol/source/VectorTile} */ (layer.getSource()); + const tileGrid = source.getTileGridForProjection(projection); + const resolution = tileGrid.getResolution(z); + const context = tile.getContext(layer); + const size = source.getTilePixelSize(z, pixelRatio, projection); + context.canvas.width = size[0]; + context.canvas.height = size[1]; + const tileExtent = tileGrid.getTileCoordExtent(tileCoord, this.tmpExtent); + for (let i = 0, ii = tile.tileKeys.length; i < ii; ++i) { + const sourceTile = tile.getTile(tile.tileKeys[i]); + if (sourceTile.getState() != TileState.LOADED) { + continue; + } + const pixelScale = pixelRatio / resolution; + const transform = resetTransform(this.tmpTransform_); + scaleTransform(transform, pixelScale, -pixelScale); + translateTransform(transform, -tileExtent[0], -tileExtent[3]); + const replayGroup = sourceTile.getReplayGroup(layer, tile.tileCoord.toString()); + replayGroup.replay(context, transform, 0, {}, replays); + } + } + } +} inherits(CanvasVectorTileLayerRenderer, CanvasTileLayerRenderer); @@ -117,394 +498,4 @@ CanvasVectorTileLayerRenderer['create'] = function(mapRenderer, layer) { }; -/** - * @inheritDoc - */ -CanvasVectorTileLayerRenderer.prototype.disposeInternal = function() { - unlisten(labelCache, EventType.CLEAR, this.handleFontsChanged_, this); - CanvasTileLayerRenderer.prototype.disposeInternal.call(this); -}; - - -/** - * @inheritDoc - */ -CanvasVectorTileLayerRenderer.prototype.getTile = function(z, x, y, pixelRatio, projection) { - const tile = CanvasTileLayerRenderer.prototype.getTile.call(this, z, x, y, pixelRatio, projection); - if (tile.getState() === TileState.LOADED) { - this.createReplayGroup_(tile, pixelRatio, projection); - if (this.context) { - this.renderTileImage_(tile, pixelRatio, projection); - } - } - return tile; -}; - - -/** - * @inheritDoc - */ -CanvasVectorTileLayerRenderer.prototype.prepareFrame = function(frameState, layerState) { - const layer = this.getLayer(); - const layerRevision = layer.getRevision(); - if (this.renderedLayerRevision_ != layerRevision) { - this.renderedTiles.length = 0; - const renderMode = layer.getRenderMode(); - if (!this.context && renderMode != VectorTileRenderType.VECTOR) { - this.context = createCanvasContext2D(); - } - if (this.context && renderMode == VectorTileRenderType.VECTOR) { - this.context = null; - } - } - this.renderedLayerRevision_ = layerRevision; - return CanvasTileLayerRenderer.prototype.prepareFrame.apply(this, arguments); -}; - - -/** - * @param {module:ol/VectorImageTile} tile Tile. - * @param {number} pixelRatio Pixel ratio. - * @param {module:ol/proj/Projection} projection Projection. - * @private - */ -CanvasVectorTileLayerRenderer.prototype.createReplayGroup_ = function(tile, pixelRatio, projection) { - const layer = this.getLayer(); - const revision = layer.getRevision(); - const renderOrder = /** @type {module:ol/render~OrderFunction} */ (layer.getRenderOrder()) || null; - - const replayState = tile.getReplayState(layer); - if (!replayState.dirty && replayState.renderedRevision == revision && - replayState.renderedRenderOrder == renderOrder) { - return; - } - - const source = /** @type {module:ol/source/VectorTile} */ (layer.getSource()); - const sourceTileGrid = source.getTileGrid(); - const tileGrid = source.getTileGridForProjection(projection); - const resolution = tileGrid.getResolution(tile.tileCoord[0]); - const tileExtent = tile.extent; - - const zIndexKeys = {}; - for (let t = 0, tt = tile.tileKeys.length; t < tt; ++t) { - const sourceTile = tile.getTile(tile.tileKeys[t]); - if (sourceTile.getState() != TileState.LOADED) { - continue; - } - - const sourceTileCoord = sourceTile.tileCoord; - const sourceTileExtent = sourceTileGrid.getTileCoordExtent(sourceTileCoord); - const sharedExtent = getIntersection(tileExtent, sourceTileExtent); - const bufferedExtent = equals(sourceTileExtent, sharedExtent) ? null : - buffer(sharedExtent, layer.getRenderBuffer() * resolution, this.tmpExtent); - const tileProjection = sourceTile.getProjection(); - let reproject = false; - if (!equivalentProjection(projection, tileProjection)) { - reproject = true; - sourceTile.setProjection(projection); - } - replayState.dirty = false; - const replayGroup = new CanvasReplayGroup(0, sharedExtent, resolution, - pixelRatio, source.getOverlaps(), this.declutterTree_, layer.getRenderBuffer()); - const squaredTolerance = getSquaredRenderTolerance(resolution, pixelRatio); - - /** - * @param {module:ol/Feature|module:ol/render/Feature} feature Feature. - * @this {module:ol/renderer/canvas/VectorTileLayer} - */ - const render = function(feature) { - let styles; - const styleFunction = feature.getStyleFunction() || layer.getStyleFunction(); - if (styleFunction) { - styles = styleFunction(feature, resolution); - } - if (styles) { - const dirty = this.renderFeature(feature, squaredTolerance, styles, replayGroup); - this.dirty_ = this.dirty_ || dirty; - replayState.dirty = replayState.dirty || dirty; - } - }; - - const features = sourceTile.getFeatures(); - if (renderOrder && renderOrder !== replayState.renderedRenderOrder) { - features.sort(renderOrder); - } - for (let i = 0, ii = features.length; i < ii; ++i) { - const feature = features[i]; - if (reproject) { - if (tileProjection.getUnits() == Units.TILE_PIXELS) { - // projected tile extent - tileProjection.setWorldExtent(sourceTileExtent); - // tile extent in tile pixel space - tileProjection.setExtent(sourceTile.getExtent()); - } - feature.getGeometry().transform(tileProjection, projection); - } - if (!bufferedExtent || intersects(bufferedExtent, feature.getGeometry().getExtent())) { - render.call(this, feature); - } - } - replayGroup.finish(); - for (const r in replayGroup.getReplays()) { - zIndexKeys[r] = true; - } - sourceTile.setReplayGroup(layer, tile.tileCoord.toString(), replayGroup); - } - replayState.renderedRevision = revision; - replayState.renderedRenderOrder = renderOrder; -}; - - -/** - * @inheritDoc - */ -CanvasVectorTileLayerRenderer.prototype.forEachFeatureAtCoordinate = function(coordinate, frameState, hitTolerance, callback, thisArg) { - const resolution = frameState.viewState.resolution; - const rotation = frameState.viewState.rotation; - hitTolerance = hitTolerance == undefined ? 0 : hitTolerance; - const layer = this.getLayer(); - /** @type {!Object.} */ - const features = {}; - - /** @type {Array.} */ - const renderedTiles = this.renderedTiles; - - let bufferedExtent, found; - let i, ii, replayGroup; - for (i = 0, ii = renderedTiles.length; i < ii; ++i) { - const tile = renderedTiles[i]; - bufferedExtent = buffer(tile.extent, hitTolerance * resolution, bufferedExtent); - if (!containsCoordinate(bufferedExtent, coordinate)) { - continue; - } - for (let t = 0, tt = tile.tileKeys.length; t < tt; ++t) { - const sourceTile = tile.getTile(tile.tileKeys[t]); - if (sourceTile.getState() != TileState.LOADED) { - continue; - } - replayGroup = sourceTile.getReplayGroup(layer, tile.tileCoord.toString()); - found = found || replayGroup.forEachFeatureAtCoordinate(coordinate, resolution, rotation, hitTolerance, {}, - /** - * @param {module:ol/Feature|module:ol/render/Feature} feature Feature. - * @return {?} Callback result. - */ - function(feature) { - const key = getUid(feature).toString(); - if (!(key in features)) { - features[key] = true; - return callback.call(thisArg, feature, layer); - } - }, null); - } - } - return found; -}; - - -/** - * @param {module:ol/VectorTile} tile Tile. - * @param {module:ol/PluggableMap~FrameState} frameState Frame state. - * @return {module:ol/transform~Transform} transform Transform. - * @private - */ -CanvasVectorTileLayerRenderer.prototype.getReplayTransform_ = function(tile, frameState) { - const layer = this.getLayer(); - const source = /** @type {module:ol/source/VectorTile} */ (layer.getSource()); - const tileGrid = source.getTileGrid(); - const tileCoord = tile.tileCoord; - const tileResolution = tileGrid.getResolution(tileCoord[0]); - const viewState = frameState.viewState; - const pixelRatio = frameState.pixelRatio; - const renderResolution = viewState.resolution / pixelRatio; - const tileExtent = tileGrid.getTileCoordExtent(tileCoord, this.tmpExtent); - const center = viewState.center; - const origin = getTopLeft(tileExtent); - const size = frameState.size; - const offsetX = Math.round(pixelRatio * size[0] / 2); - const offsetY = Math.round(pixelRatio * size[1] / 2); - return composeTransform(this.tmpTransform_, - offsetX, offsetY, - tileResolution / renderResolution, tileResolution / renderResolution, - viewState.rotation, - (origin[0] - center[0]) / tileResolution, - (center[1] - origin[1]) / tileResolution); -}; - - -/** - * @param {module:ol/events/Event} event Event. - */ -CanvasVectorTileLayerRenderer.prototype.handleFontsChanged_ = function(event) { - const layer = this.getLayer(); - if (layer.getVisible() && this.renderedLayerRevision_ !== undefined) { - layer.changed(); - } -}; - - -/** - * Handle changes in image style state. - * @param {module:ol/events/Event} event Image style change event. - * @private - */ -CanvasVectorTileLayerRenderer.prototype.handleStyleImageChange_ = function(event) { - this.renderIfReadyAndVisible(); -}; - - -/** - * @inheritDoc - */ -CanvasVectorTileLayerRenderer.prototype.postCompose = function(context, frameState, layerState) { - const layer = this.getLayer(); - const renderMode = layer.getRenderMode(); - if (renderMode != VectorTileRenderType.IMAGE) { - const declutterReplays = layer.getDeclutter() ? {} : null; - const source = /** @type {module:ol/source/VectorTile} */ (layer.getSource()); - const replayTypes = VECTOR_REPLAYS[renderMode]; - const pixelRatio = frameState.pixelRatio; - const rotation = frameState.viewState.rotation; - const size = frameState.size; - let offsetX, offsetY; - if (rotation) { - offsetX = Math.round(pixelRatio * size[0] / 2); - offsetY = Math.round(pixelRatio * size[1] / 2); - rotateAtOffset(context, -rotation, offsetX, offsetY); - } - if (declutterReplays) { - this.declutterTree_.clear(); - } - const tiles = this.renderedTiles; - const tileGrid = source.getTileGridForProjection(frameState.viewState.projection); - const clips = []; - const zs = []; - for (let i = tiles.length - 1; i >= 0; --i) { - const tile = /** @type {module:ol/VectorImageTile} */ (tiles[i]); - if (tile.getState() == TileState.ABORT) { - continue; - } - const tileCoord = tile.tileCoord; - const worldOffset = tileGrid.getTileCoordExtent(tileCoord, this.tmpExtent)[0] - tile.extent[0]; - let transform = undefined; - for (let t = 0, tt = tile.tileKeys.length; t < tt; ++t) { - const sourceTile = tile.getTile(tile.tileKeys[t]); - if (sourceTile.getState() != TileState.LOADED) { - continue; - } - const replayGroup = sourceTile.getReplayGroup(layer, tileCoord.toString()); - if (!replayGroup || !replayGroup.hasReplays(replayTypes)) { - // sourceTile was not yet loaded when this.createReplayGroup_() was - // called, or it has no replays of the types we want to render - continue; - } - if (!transform) { - transform = this.getTransform(frameState, worldOffset); - } - const currentZ = sourceTile.tileCoord[0]; - const currentClip = replayGroup.getClipCoords(transform); - context.save(); - context.globalAlpha = layerState.opacity; - // Create a clip mask for regions in this low resolution tile that are - // already filled by a higher resolution tile - for (let j = 0, jj = clips.length; j < jj; ++j) { - const clip = clips[j]; - if (currentZ < zs[j]) { - context.beginPath(); - // counter-clockwise (outer ring) for current tile - context.moveTo(currentClip[0], currentClip[1]); - context.lineTo(currentClip[2], currentClip[3]); - context.lineTo(currentClip[4], currentClip[5]); - context.lineTo(currentClip[6], currentClip[7]); - // clockwise (inner ring) for higher resolution tile - context.moveTo(clip[6], clip[7]); - context.lineTo(clip[4], clip[5]); - context.lineTo(clip[2], clip[3]); - context.lineTo(clip[0], clip[1]); - context.clip(); - } - } - replayGroup.replay(context, transform, rotation, {}, replayTypes, declutterReplays); - context.restore(); - clips.push(currentClip); - zs.push(currentZ); - } - } - if (declutterReplays) { - replayDeclutter(declutterReplays, context, rotation); - } - if (rotation) { - rotateAtOffset(context, rotation, - /** @type {number} */ (offsetX), /** @type {number} */ (offsetY)); - } - } - CanvasTileLayerRenderer.prototype.postCompose.apply(this, arguments); -}; - - -/** - * @param {module:ol/Feature|module:ol/render/Feature} feature Feature. - * @param {number} squaredTolerance Squared tolerance. - * @param {(module:ol/style/Style|Array.)} styles The style or array of styles. - * @param {module:ol/render/canvas/ReplayGroup} replayGroup Replay group. - * @return {boolean} `true` if an image is loading. - */ -CanvasVectorTileLayerRenderer.prototype.renderFeature = function(feature, squaredTolerance, styles, replayGroup) { - if (!styles) { - return false; - } - let loading = false; - if (Array.isArray(styles)) { - for (let i = 0, ii = styles.length; i < ii; ++i) { - loading = renderFeature( - replayGroup, feature, styles[i], squaredTolerance, - this.handleStyleImageChange_, this) || loading; - } - } else { - loading = renderFeature( - replayGroup, feature, styles, squaredTolerance, - this.handleStyleImageChange_, this); - } - return loading; -}; - - -/** - * @param {module:ol/VectorImageTile} tile Tile. - * @param {number} pixelRatio Pixel ratio. - * @param {module:ol/proj/Projection} projection Projection. - * @private - */ -CanvasVectorTileLayerRenderer.prototype.renderTileImage_ = function( - tile, pixelRatio, projection) { - const layer = this.getLayer(); - const replayState = tile.getReplayState(layer); - const revision = layer.getRevision(); - const replays = IMAGE_REPLAYS[layer.getRenderMode()]; - if (replays && replayState.renderedTileRevision !== revision) { - replayState.renderedTileRevision = revision; - const tileCoord = tile.wrappedTileCoord; - const z = tileCoord[0]; - const source = /** @type {module:ol/source/VectorTile} */ (layer.getSource()); - const tileGrid = source.getTileGridForProjection(projection); - const resolution = tileGrid.getResolution(z); - const context = tile.getContext(layer); - const size = source.getTilePixelSize(z, pixelRatio, projection); - context.canvas.width = size[0]; - context.canvas.height = size[1]; - const tileExtent = tileGrid.getTileCoordExtent(tileCoord, this.tmpExtent); - for (let i = 0, ii = tile.tileKeys.length; i < ii; ++i) { - const sourceTile = tile.getTile(tile.tileKeys[i]); - if (sourceTile.getState() != TileState.LOADED) { - continue; - } - const pixelScale = pixelRatio / resolution; - const transform = resetTransform(this.tmpTransform_); - scaleTransform(transform, pixelScale, -pixelScale); - translateTransform(transform, -tileExtent[0], -tileExtent[3]); - const replayGroup = sourceTile.getReplayGroup(layer, tile.tileCoord.toString()); - replayGroup.replay(context, transform, 0, {}, replays); - } - } -}; - export default CanvasVectorTileLayerRenderer; diff --git a/src/ol/renderer/webgl/ImageLayer.js b/src/ol/renderer/webgl/ImageLayer.js index 68bd42b483..644d9c0c76 100644 --- a/src/ol/renderer/webgl/ImageLayer.js +++ b/src/ol/renderer/webgl/ImageLayer.js @@ -29,30 +29,286 @@ import {createTexture} from '../../webgl/Context.js'; * @param {module:ol/layer/Image} imageLayer Tile layer. * @api */ -const WebGLImageLayerRenderer = function(mapRenderer, imageLayer) { +class WebGLImageLayerRenderer { + constructor(mapRenderer, imageLayer) { - WebGLLayerRenderer.call(this, mapRenderer, imageLayer); + WebGLLayerRenderer.call(this, mapRenderer, imageLayer); + + /** + * The last rendered image. + * @private + * @type {?module:ol/ImageBase} + */ + this.image_ = null; + + /** + * @private + * @type {CanvasRenderingContext2D} + */ + this.hitCanvasContext_ = null; + + /** + * @private + * @type {?module:ol/transform~Transform} + */ + this.hitTransformationMatrix_ = null; + + } /** - * The last rendered image. + * @param {module:ol/ImageBase} image Image. * @private - * @type {?module:ol/ImageBase} + * @return {WebGLTexture} Texture. */ - this.image_ = null; + createTexture_(image) { + + // We meet the conditions to work with non-power of two textures. + // http://www.khronos.org/webgl/wiki/WebGL_and_OpenGL_Differences#Non-Power_of_Two_Texture_Support + // http://learningwebgl.com/blog/?p=2101 + + const imageElement = image.getImage(); + const gl = this.mapRenderer.getGL(); + + return createTexture( + gl, imageElement, CLAMP_TO_EDGE, CLAMP_TO_EDGE); + } /** - * @private - * @type {CanvasRenderingContext2D} + * @inheritDoc */ - this.hitCanvasContext_ = null; + forEachFeatureAtCoordinate(coordinate, frameState, hitTolerance, callback, thisArg) { + const layer = this.getLayer(); + const source = layer.getSource(); + const resolution = frameState.viewState.resolution; + const rotation = frameState.viewState.rotation; + const skippedFeatureUids = frameState.skippedFeatureUids; + return source.forEachFeatureAtCoordinate( + coordinate, resolution, rotation, hitTolerance, skippedFeatureUids, + + /** + * @param {module:ol/Feature|module:ol/render/Feature} feature Feature. + * @return {?} Callback result. + */ + function(feature) { + return callback.call(thisArg, feature, layer); + }); + } /** - * @private - * @type {?module:ol/transform~Transform} + * @inheritDoc */ - this.hitTransformationMatrix_ = null; + prepareFrame(frameState, layerState, context) { -}; + const gl = this.mapRenderer.getGL(); + + const pixelRatio = frameState.pixelRatio; + const viewState = frameState.viewState; + const viewCenter = viewState.center; + const viewResolution = viewState.resolution; + const viewRotation = viewState.rotation; + + let image = this.image_; + let texture = this.texture; + const imageLayer = /** @type {module:ol/layer/Image} */ (this.getLayer()); + const imageSource = imageLayer.getSource(); + + const hints = frameState.viewHints; + + let renderedExtent = frameState.extent; + if (layerState.extent !== undefined) { + renderedExtent = getIntersection(renderedExtent, layerState.extent); + } + if (!hints[ViewHint.ANIMATING] && !hints[ViewHint.INTERACTING] && + !isEmpty(renderedExtent)) { + let projection = viewState.projection; + if (!ENABLE_RASTER_REPROJECTION) { + const sourceProjection = imageSource.getProjection(); + if (sourceProjection) { + projection = sourceProjection; + } + } + const image_ = imageSource.getImage(renderedExtent, viewResolution, + pixelRatio, projection); + if (image_) { + const loaded = this.loadImage(image_); + if (loaded) { + image = image_; + texture = this.createTexture_(image_); + if (this.texture) { + /** + * @param {WebGLRenderingContext} gl GL. + * @param {WebGLTexture} texture Texture. + */ + const postRenderFunction = function(gl, texture) { + if (!gl.isContextLost()) { + gl.deleteTexture(texture); + } + }.bind(null, gl, this.texture); + frameState.postRenderFunctions.push( + /** @type {module:ol/PluggableMap~PostRenderFunction} */ (postRenderFunction) + ); + } + } + } + } + + if (image) { + const canvas = this.mapRenderer.getContext().getCanvas(); + + this.updateProjectionMatrix_(canvas.width, canvas.height, + pixelRatio, viewCenter, viewResolution, viewRotation, + image.getExtent()); + this.hitTransformationMatrix_ = null; + + // Translate and scale to flip the Y coord. + const texCoordMatrix = this.texCoordMatrix; + resetTransform(texCoordMatrix); + scaleTransform(texCoordMatrix, 1, -1); + translateTransform(texCoordMatrix, 0, -1); + + this.image_ = image; + this.texture = texture; + } + + return !!image; + } + + /** + * @param {number} canvasWidth Canvas width. + * @param {number} canvasHeight Canvas height. + * @param {number} pixelRatio Pixel ratio. + * @param {module:ol/coordinate~Coordinate} viewCenter View center. + * @param {number} viewResolution View resolution. + * @param {number} viewRotation View rotation. + * @param {module:ol/extent~Extent} imageExtent Image extent. + * @private + */ + updateProjectionMatrix_( + canvasWidth, + canvasHeight, + pixelRatio, + viewCenter, + viewResolution, + viewRotation, + imageExtent + ) { + + const canvasExtentWidth = canvasWidth * viewResolution; + const canvasExtentHeight = canvasHeight * viewResolution; + + const projectionMatrix = this.projectionMatrix; + resetTransform(projectionMatrix); + scaleTransform(projectionMatrix, + pixelRatio * 2 / canvasExtentWidth, + pixelRatio * 2 / canvasExtentHeight); + rotateTransform(projectionMatrix, -viewRotation); + translateTransform(projectionMatrix, + imageExtent[0] - viewCenter[0], + imageExtent[1] - viewCenter[1]); + scaleTransform(projectionMatrix, + (imageExtent[2] - imageExtent[0]) / 2, + (imageExtent[3] - imageExtent[1]) / 2); + translateTransform(projectionMatrix, 1, 1); + + } + + /** + * @inheritDoc + */ + hasFeatureAtCoordinate(coordinate, frameState) { + const hasFeature = this.forEachFeatureAtCoordinate(coordinate, frameState, 0, TRUE, this); + return hasFeature !== undefined; + } + + /** + * @inheritDoc + */ + forEachLayerAtPixel(pixel, frameState, callback, thisArg) { + if (!this.image_ || !this.image_.getImage()) { + return undefined; + } + + if (this.getLayer().getSource().forEachFeatureAtCoordinate !== UNDEFINED) { + // for ImageCanvas sources use the original hit-detection logic, + // so that for example also transparent polygons are detected + const coordinate = applyTransform( + frameState.pixelToCoordinateTransform, pixel.slice()); + const hasFeature = this.forEachFeatureAtCoordinate(coordinate, frameState, 0, TRUE, this); + + if (hasFeature) { + return callback.call(thisArg, this.getLayer(), null); + } else { + return undefined; + } + } else { + const imageSize = + [this.image_.getImage().width, this.image_.getImage().height]; + + if (!this.hitTransformationMatrix_) { + this.hitTransformationMatrix_ = this.getHitTransformationMatrix_( + frameState.size, imageSize); + } + + const pixelOnFrameBuffer = applyTransform( + this.hitTransformationMatrix_, pixel.slice()); + + if (pixelOnFrameBuffer[0] < 0 || pixelOnFrameBuffer[0] > imageSize[0] || + pixelOnFrameBuffer[1] < 0 || pixelOnFrameBuffer[1] > imageSize[1]) { + // outside the image, no need to check + return undefined; + } + + if (!this.hitCanvasContext_) { + this.hitCanvasContext_ = createCanvasContext2D(1, 1); + } + + this.hitCanvasContext_.clearRect(0, 0, 1, 1); + this.hitCanvasContext_.drawImage(this.image_.getImage(), + pixelOnFrameBuffer[0], pixelOnFrameBuffer[1], 1, 1, 0, 0, 1, 1); + + const imageData = this.hitCanvasContext_.getImageData(0, 0, 1, 1).data; + if (imageData[3] > 0) { + return callback.call(thisArg, this.getLayer(), imageData); + } else { + return undefined; + } + } + } + + /** + * The transformation matrix to get the pixel on the image for a + * pixel on the map. + * @param {module:ol/size~Size} mapSize The map size. + * @param {module:ol/size~Size} imageSize The image size. + * @return {module:ol/transform~Transform} The transformation matrix. + * @private + */ + getHitTransformationMatrix_(mapSize, imageSize) { + // the first matrix takes a map pixel, flips the y-axis and scales to + // a range between -1 ... 1 + const mapCoordTransform = createTransform(); + translateTransform(mapCoordTransform, -1, -1); + scaleTransform(mapCoordTransform, 2 / mapSize[0], 2 / mapSize[1]); + translateTransform(mapCoordTransform, 0, mapSize[1]); + scaleTransform(mapCoordTransform, 1, -1); + + // the second matrix is the inverse of the projection matrix used in the + // shader for drawing + const projectionMatrixInv = invertTransform(this.projectionMatrix.slice()); + + // the third matrix scales to the image dimensions and flips the y-axis again + const transform = createTransform(); + translateTransform(transform, 0, imageSize[1]); + scaleTransform(transform, 1, -1); + scaleTransform(transform, imageSize[0] / 2, imageSize[1] / 2); + translateTransform(transform, 1, 1); + + multiplyTransform(transform, projectionMatrixInv); + multiplyTransform(transform, mapCoordTransform); + + return transform; + } +} inherits(WebGLImageLayerRenderer, WebGLLayerRenderer); @@ -81,256 +337,4 @@ WebGLImageLayerRenderer['create'] = function(mapRenderer, layer) { }; -/** - * @param {module:ol/ImageBase} image Image. - * @private - * @return {WebGLTexture} Texture. - */ -WebGLImageLayerRenderer.prototype.createTexture_ = function(image) { - - // We meet the conditions to work with non-power of two textures. - // http://www.khronos.org/webgl/wiki/WebGL_and_OpenGL_Differences#Non-Power_of_Two_Texture_Support - // http://learningwebgl.com/blog/?p=2101 - - const imageElement = image.getImage(); - const gl = this.mapRenderer.getGL(); - - return createTexture( - gl, imageElement, CLAMP_TO_EDGE, CLAMP_TO_EDGE); -}; - - -/** - * @inheritDoc - */ -WebGLImageLayerRenderer.prototype.forEachFeatureAtCoordinate = function(coordinate, frameState, hitTolerance, callback, thisArg) { - const layer = this.getLayer(); - const source = layer.getSource(); - const resolution = frameState.viewState.resolution; - const rotation = frameState.viewState.rotation; - const skippedFeatureUids = frameState.skippedFeatureUids; - return source.forEachFeatureAtCoordinate( - coordinate, resolution, rotation, hitTolerance, skippedFeatureUids, - - /** - * @param {module:ol/Feature|module:ol/render/Feature} feature Feature. - * @return {?} Callback result. - */ - function(feature) { - return callback.call(thisArg, feature, layer); - }); -}; - - -/** - * @inheritDoc - */ -WebGLImageLayerRenderer.prototype.prepareFrame = function(frameState, layerState, context) { - - const gl = this.mapRenderer.getGL(); - - const pixelRatio = frameState.pixelRatio; - const viewState = frameState.viewState; - const viewCenter = viewState.center; - const viewResolution = viewState.resolution; - const viewRotation = viewState.rotation; - - let image = this.image_; - let texture = this.texture; - const imageLayer = /** @type {module:ol/layer/Image} */ (this.getLayer()); - const imageSource = imageLayer.getSource(); - - const hints = frameState.viewHints; - - let renderedExtent = frameState.extent; - if (layerState.extent !== undefined) { - renderedExtent = getIntersection(renderedExtent, layerState.extent); - } - if (!hints[ViewHint.ANIMATING] && !hints[ViewHint.INTERACTING] && - !isEmpty(renderedExtent)) { - let projection = viewState.projection; - if (!ENABLE_RASTER_REPROJECTION) { - const sourceProjection = imageSource.getProjection(); - if (sourceProjection) { - projection = sourceProjection; - } - } - const image_ = imageSource.getImage(renderedExtent, viewResolution, - pixelRatio, projection); - if (image_) { - const loaded = this.loadImage(image_); - if (loaded) { - image = image_; - texture = this.createTexture_(image_); - if (this.texture) { - /** - * @param {WebGLRenderingContext} gl GL. - * @param {WebGLTexture} texture Texture. - */ - const postRenderFunction = function(gl, texture) { - if (!gl.isContextLost()) { - gl.deleteTexture(texture); - } - }.bind(null, gl, this.texture); - frameState.postRenderFunctions.push( - /** @type {module:ol/PluggableMap~PostRenderFunction} */ (postRenderFunction) - ); - } - } - } - } - - if (image) { - const canvas = this.mapRenderer.getContext().getCanvas(); - - this.updateProjectionMatrix_(canvas.width, canvas.height, - pixelRatio, viewCenter, viewResolution, viewRotation, - image.getExtent()); - this.hitTransformationMatrix_ = null; - - // Translate and scale to flip the Y coord. - const texCoordMatrix = this.texCoordMatrix; - resetTransform(texCoordMatrix); - scaleTransform(texCoordMatrix, 1, -1); - translateTransform(texCoordMatrix, 0, -1); - - this.image_ = image; - this.texture = texture; - } - - return !!image; -}; - - -/** - * @param {number} canvasWidth Canvas width. - * @param {number} canvasHeight Canvas height. - * @param {number} pixelRatio Pixel ratio. - * @param {module:ol/coordinate~Coordinate} viewCenter View center. - * @param {number} viewResolution View resolution. - * @param {number} viewRotation View rotation. - * @param {module:ol/extent~Extent} imageExtent Image extent. - * @private - */ -WebGLImageLayerRenderer.prototype.updateProjectionMatrix_ = function(canvasWidth, canvasHeight, pixelRatio, - viewCenter, viewResolution, viewRotation, imageExtent) { - - const canvasExtentWidth = canvasWidth * viewResolution; - const canvasExtentHeight = canvasHeight * viewResolution; - - const projectionMatrix = this.projectionMatrix; - resetTransform(projectionMatrix); - scaleTransform(projectionMatrix, - pixelRatio * 2 / canvasExtentWidth, - pixelRatio * 2 / canvasExtentHeight); - rotateTransform(projectionMatrix, -viewRotation); - translateTransform(projectionMatrix, - imageExtent[0] - viewCenter[0], - imageExtent[1] - viewCenter[1]); - scaleTransform(projectionMatrix, - (imageExtent[2] - imageExtent[0]) / 2, - (imageExtent[3] - imageExtent[1]) / 2); - translateTransform(projectionMatrix, 1, 1); - -}; - - -/** - * @inheritDoc - */ -WebGLImageLayerRenderer.prototype.hasFeatureAtCoordinate = function(coordinate, frameState) { - const hasFeature = this.forEachFeatureAtCoordinate(coordinate, frameState, 0, TRUE, this); - return hasFeature !== undefined; -}; - - -/** - * @inheritDoc - */ -WebGLImageLayerRenderer.prototype.forEachLayerAtPixel = function(pixel, frameState, callback, thisArg) { - if (!this.image_ || !this.image_.getImage()) { - return undefined; - } - - if (this.getLayer().getSource().forEachFeatureAtCoordinate !== UNDEFINED) { - // for ImageCanvas sources use the original hit-detection logic, - // so that for example also transparent polygons are detected - const coordinate = applyTransform( - frameState.pixelToCoordinateTransform, pixel.slice()); - const hasFeature = this.forEachFeatureAtCoordinate(coordinate, frameState, 0, TRUE, this); - - if (hasFeature) { - return callback.call(thisArg, this.getLayer(), null); - } else { - return undefined; - } - } else { - const imageSize = - [this.image_.getImage().width, this.image_.getImage().height]; - - if (!this.hitTransformationMatrix_) { - this.hitTransformationMatrix_ = this.getHitTransformationMatrix_( - frameState.size, imageSize); - } - - const pixelOnFrameBuffer = applyTransform( - this.hitTransformationMatrix_, pixel.slice()); - - if (pixelOnFrameBuffer[0] < 0 || pixelOnFrameBuffer[0] > imageSize[0] || - pixelOnFrameBuffer[1] < 0 || pixelOnFrameBuffer[1] > imageSize[1]) { - // outside the image, no need to check - return undefined; - } - - if (!this.hitCanvasContext_) { - this.hitCanvasContext_ = createCanvasContext2D(1, 1); - } - - this.hitCanvasContext_.clearRect(0, 0, 1, 1); - this.hitCanvasContext_.drawImage(this.image_.getImage(), - pixelOnFrameBuffer[0], pixelOnFrameBuffer[1], 1, 1, 0, 0, 1, 1); - - const imageData = this.hitCanvasContext_.getImageData(0, 0, 1, 1).data; - if (imageData[3] > 0) { - return callback.call(thisArg, this.getLayer(), imageData); - } else { - return undefined; - } - } -}; - - -/** - * The transformation matrix to get the pixel on the image for a - * pixel on the map. - * @param {module:ol/size~Size} mapSize The map size. - * @param {module:ol/size~Size} imageSize The image size. - * @return {module:ol/transform~Transform} The transformation matrix. - * @private - */ -WebGLImageLayerRenderer.prototype.getHitTransformationMatrix_ = function(mapSize, imageSize) { - // the first matrix takes a map pixel, flips the y-axis and scales to - // a range between -1 ... 1 - const mapCoordTransform = createTransform(); - translateTransform(mapCoordTransform, -1, -1); - scaleTransform(mapCoordTransform, 2 / mapSize[0], 2 / mapSize[1]); - translateTransform(mapCoordTransform, 0, mapSize[1]); - scaleTransform(mapCoordTransform, 1, -1); - - // the second matrix is the inverse of the projection matrix used in the - // shader for drawing - const projectionMatrixInv = invertTransform(this.projectionMatrix.slice()); - - // the third matrix scales to the image dimensions and flips the y-axis again - const transform = createTransform(); - translateTransform(transform, 0, imageSize[1]); - scaleTransform(transform, 1, -1); - scaleTransform(transform, imageSize[0] / 2, imageSize[1] / 2); - translateTransform(transform, 1, 1); - - multiplyTransform(transform, projectionMatrixInv); - multiplyTransform(transform, mapCoordTransform); - - return transform; -}; export default WebGLImageLayerRenderer; diff --git a/src/ol/renderer/webgl/Layer.js b/src/ol/renderer/webgl/Layer.js index 6f15a1779e..0fffb2fe21 100644 --- a/src/ol/renderer/webgl/Layer.js +++ b/src/ol/renderer/webgl/Layer.js @@ -22,244 +22,239 @@ import {createEmptyTexture} from '../../webgl/Context.js'; * @param {module:ol/renderer/webgl/Map} mapRenderer Map renderer. * @param {module:ol/layer/Layer} layer Layer. */ -const WebGLLayerRenderer = function(mapRenderer, layer) { +class WebGLLayerRenderer { + constructor(mapRenderer, layer) { - LayerRenderer.call(this, layer); + LayerRenderer.call(this, layer); + + /** + * @protected + * @type {module:ol/renderer/webgl/Map} + */ + this.mapRenderer = mapRenderer; + + /** + * @private + * @type {module:ol/webgl/Buffer} + */ + this.arrayBuffer_ = new WebGLBuffer([ + -1, -1, 0, 0, + 1, -1, 1, 0, + -1, 1, 0, 1, + 1, 1, 1, 1 + ]); + + /** + * @protected + * @type {WebGLTexture} + */ + this.texture = null; + + /** + * @protected + * @type {WebGLFramebuffer} + */ + this.framebuffer = null; + + /** + * @protected + * @type {number|undefined} + */ + this.framebufferDimension = undefined; + + /** + * @protected + * @type {module:ol/transform~Transform} + */ + this.texCoordMatrix = createTransform(); + + /** + * @protected + * @type {module:ol/transform~Transform} + */ + this.projectionMatrix = createTransform(); + + /** + * @type {Array.} + * @private + */ + this.tmpMat4_ = create(); + + /** + * @private + * @type {module:ol/renderer/webgl/defaultmapshader/Locations} + */ + this.defaultLocations_ = null; + + } /** + * @param {module:ol/PluggableMap~FrameState} frameState Frame state. + * @param {number} framebufferDimension Framebuffer dimension. * @protected - * @type {module:ol/renderer/webgl/Map} */ - this.mapRenderer = mapRenderer; + bindFramebuffer(frameState, framebufferDimension) { + + const gl = this.mapRenderer.getGL(); + + if (this.framebufferDimension === undefined || + this.framebufferDimension != framebufferDimension) { + /** + * @param {WebGLRenderingContext} gl GL. + * @param {WebGLFramebuffer} framebuffer Framebuffer. + * @param {WebGLTexture} texture Texture. + */ + const postRenderFunction = function(gl, framebuffer, texture) { + if (!gl.isContextLost()) { + gl.deleteFramebuffer(framebuffer); + gl.deleteTexture(texture); + } + }.bind(null, gl, this.framebuffer, this.texture); + + frameState.postRenderFunctions.push( + /** @type {module:ol/PluggableMap~PostRenderFunction} */ (postRenderFunction) + ); + + const texture = createEmptyTexture( + gl, framebufferDimension, framebufferDimension); + + const framebuffer = gl.createFramebuffer(); + gl.bindFramebuffer(FRAMEBUFFER, framebuffer); + gl.framebufferTexture2D(FRAMEBUFFER, + COLOR_ATTACHMENT0, TEXTURE_2D, texture, 0); + + this.texture = texture; + this.framebuffer = framebuffer; + this.framebufferDimension = framebufferDimension; + + } else { + gl.bindFramebuffer(FRAMEBUFFER, this.framebuffer); + } + + } /** - * @private - * @type {module:ol/webgl/Buffer} + * @param {module:ol/PluggableMap~FrameState} frameState Frame state. + * @param {module:ol/layer/Layer~State} layerState Layer state. + * @param {module:ol/webgl/Context} context Context. */ - this.arrayBuffer_ = new WebGLBuffer([ - -1, -1, 0, 0, - 1, -1, 1, 0, - -1, 1, 0, 1, - 1, 1, 1, 1 - ]); + composeFrame(frameState, layerState, context) { + + this.dispatchComposeEvent_(RenderEventType.PRECOMPOSE, context, frameState); + + context.bindBuffer(ARRAY_BUFFER, this.arrayBuffer_); + + const gl = context.getGL(); + + const program = context.getProgram(fragment, vertex); + + let locations; + if (!this.defaultLocations_) { + locations = new Locations(gl, program); + this.defaultLocations_ = locations; + } else { + locations = this.defaultLocations_; + } + + if (context.useProgram(program)) { + gl.enableVertexAttribArray(locations.a_position); + gl.vertexAttribPointer( + locations.a_position, 2, FLOAT, false, 16, 0); + gl.enableVertexAttribArray(locations.a_texCoord); + gl.vertexAttribPointer( + locations.a_texCoord, 2, FLOAT, false, 16, 8); + gl.uniform1i(locations.u_texture, 0); + } + + gl.uniformMatrix4fv(locations.u_texCoordMatrix, false, + fromTransform(this.tmpMat4_, this.getTexCoordMatrix())); + gl.uniformMatrix4fv(locations.u_projectionMatrix, false, + fromTransform(this.tmpMat4_, this.getProjectionMatrix())); + gl.uniform1f(locations.u_opacity, layerState.opacity); + gl.bindTexture(TEXTURE_2D, this.getTexture()); + gl.drawArrays(TRIANGLE_STRIP, 0, 4); + + this.dispatchComposeEvent_(RenderEventType.POSTCOMPOSE, context, frameState); + } /** - * @protected - * @type {WebGLTexture} - */ - this.texture = null; - - /** - * @protected - * @type {WebGLFramebuffer} - */ - this.framebuffer = null; - - /** - * @protected - * @type {number|undefined} - */ - this.framebufferDimension = undefined; - - /** - * @protected - * @type {module:ol/transform~Transform} - */ - this.texCoordMatrix = createTransform(); - - /** - * @protected - * @type {module:ol/transform~Transform} - */ - this.projectionMatrix = createTransform(); - - /** - * @type {Array.} + * @param {module:ol/render/EventType} type Event type. + * @param {module:ol/webgl/Context} context WebGL context. + * @param {module:ol/PluggableMap~FrameState} frameState Frame state. * @private */ - this.tmpMat4_ = create(); + dispatchComposeEvent_(type, context, frameState) { + const layer = this.getLayer(); + if (layer.hasListener(type)) { + const viewState = frameState.viewState; + const resolution = viewState.resolution; + const pixelRatio = frameState.pixelRatio; + const extent = frameState.extent; + const center = viewState.center; + const rotation = viewState.rotation; + const size = frameState.size; + + const render = new WebGLImmediateRenderer( + context, center, resolution, rotation, size, extent, pixelRatio); + const composeEvent = new RenderEvent( + type, render, frameState, null, context); + layer.dispatchEvent(composeEvent); + } + } /** - * @private - * @type {module:ol/renderer/webgl/defaultmapshader/Locations} + * @return {!module:ol/transform~Transform} Matrix. */ - this.defaultLocations_ = null; + getTexCoordMatrix() { + return this.texCoordMatrix; + } -}; + /** + * @return {WebGLTexture} Texture. + */ + getTexture() { + return this.texture; + } + + /** + * @return {!module:ol/transform~Transform} Matrix. + */ + getProjectionMatrix() { + return this.projectionMatrix; + } + + /** + * Handle webglcontextlost. + */ + handleWebGLContextLost() { + this.texture = null; + this.framebuffer = null; + this.framebufferDimension = undefined; + } + + /** + * @abstract + * @param {module:ol/PluggableMap~FrameState} frameState Frame state. + * @param {module:ol/layer/Layer~State} layerState Layer state. + * @param {module:ol/webgl/Context} context Context. + * @return {boolean} whether composeFrame should be called. + */ + prepareFrame(frameState, layerState, context) {} + + /** + * @abstract + * @param {module:ol~Pixel} pixel Pixel. + * @param {module:ol/PluggableMap~FrameState} frameState FrameState. + * @param {function(this: S, module:ol/layer/Layer, (Uint8ClampedArray|Uint8Array)): T} callback Layer + * callback. + * @param {S} thisArg Value to use as `this` when executing `callback`. + * @return {T|undefined} Callback result. + * @template S,T,U + */ + forEachLayerAtPixel(pixel, frameState, callback, thisArg) {} +} inherits(WebGLLayerRenderer, LayerRenderer); -/** - * @param {module:ol/PluggableMap~FrameState} frameState Frame state. - * @param {number} framebufferDimension Framebuffer dimension. - * @protected - */ -WebGLLayerRenderer.prototype.bindFramebuffer = function(frameState, framebufferDimension) { - - const gl = this.mapRenderer.getGL(); - - if (this.framebufferDimension === undefined || - this.framebufferDimension != framebufferDimension) { - /** - * @param {WebGLRenderingContext} gl GL. - * @param {WebGLFramebuffer} framebuffer Framebuffer. - * @param {WebGLTexture} texture Texture. - */ - const postRenderFunction = function(gl, framebuffer, texture) { - if (!gl.isContextLost()) { - gl.deleteFramebuffer(framebuffer); - gl.deleteTexture(texture); - } - }.bind(null, gl, this.framebuffer, this.texture); - - frameState.postRenderFunctions.push( - /** @type {module:ol/PluggableMap~PostRenderFunction} */ (postRenderFunction) - ); - - const texture = createEmptyTexture( - gl, framebufferDimension, framebufferDimension); - - const framebuffer = gl.createFramebuffer(); - gl.bindFramebuffer(FRAMEBUFFER, framebuffer); - gl.framebufferTexture2D(FRAMEBUFFER, - COLOR_ATTACHMENT0, TEXTURE_2D, texture, 0); - - this.texture = texture; - this.framebuffer = framebuffer; - this.framebufferDimension = framebufferDimension; - - } else { - gl.bindFramebuffer(FRAMEBUFFER, this.framebuffer); - } - -}; - - -/** - * @param {module:ol/PluggableMap~FrameState} frameState Frame state. - * @param {module:ol/layer/Layer~State} layerState Layer state. - * @param {module:ol/webgl/Context} context Context. - */ -WebGLLayerRenderer.prototype.composeFrame = function(frameState, layerState, context) { - - this.dispatchComposeEvent_(RenderEventType.PRECOMPOSE, context, frameState); - - context.bindBuffer(ARRAY_BUFFER, this.arrayBuffer_); - - const gl = context.getGL(); - - const program = context.getProgram(fragment, vertex); - - let locations; - if (!this.defaultLocations_) { - locations = new Locations(gl, program); - this.defaultLocations_ = locations; - } else { - locations = this.defaultLocations_; - } - - if (context.useProgram(program)) { - gl.enableVertexAttribArray(locations.a_position); - gl.vertexAttribPointer( - locations.a_position, 2, FLOAT, false, 16, 0); - gl.enableVertexAttribArray(locations.a_texCoord); - gl.vertexAttribPointer( - locations.a_texCoord, 2, FLOAT, false, 16, 8); - gl.uniform1i(locations.u_texture, 0); - } - - gl.uniformMatrix4fv(locations.u_texCoordMatrix, false, - fromTransform(this.tmpMat4_, this.getTexCoordMatrix())); - gl.uniformMatrix4fv(locations.u_projectionMatrix, false, - fromTransform(this.tmpMat4_, this.getProjectionMatrix())); - gl.uniform1f(locations.u_opacity, layerState.opacity); - gl.bindTexture(TEXTURE_2D, this.getTexture()); - gl.drawArrays(TRIANGLE_STRIP, 0, 4); - - this.dispatchComposeEvent_(RenderEventType.POSTCOMPOSE, context, frameState); -}; - - -/** - * @param {module:ol/render/EventType} type Event type. - * @param {module:ol/webgl/Context} context WebGL context. - * @param {module:ol/PluggableMap~FrameState} frameState Frame state. - * @private - */ -WebGLLayerRenderer.prototype.dispatchComposeEvent_ = function(type, context, frameState) { - const layer = this.getLayer(); - if (layer.hasListener(type)) { - const viewState = frameState.viewState; - const resolution = viewState.resolution; - const pixelRatio = frameState.pixelRatio; - const extent = frameState.extent; - const center = viewState.center; - const rotation = viewState.rotation; - const size = frameState.size; - - const render = new WebGLImmediateRenderer( - context, center, resolution, rotation, size, extent, pixelRatio); - const composeEvent = new RenderEvent( - type, render, frameState, null, context); - layer.dispatchEvent(composeEvent); - } -}; - - -/** - * @return {!module:ol/transform~Transform} Matrix. - */ -WebGLLayerRenderer.prototype.getTexCoordMatrix = function() { - return this.texCoordMatrix; -}; - - -/** - * @return {WebGLTexture} Texture. - */ -WebGLLayerRenderer.prototype.getTexture = function() { - return this.texture; -}; - - -/** - * @return {!module:ol/transform~Transform} Matrix. - */ -WebGLLayerRenderer.prototype.getProjectionMatrix = function() { - return this.projectionMatrix; -}; - - -/** - * Handle webglcontextlost. - */ -WebGLLayerRenderer.prototype.handleWebGLContextLost = function() { - this.texture = null; - this.framebuffer = null; - this.framebufferDimension = undefined; -}; - - -/** - * @abstract - * @param {module:ol/PluggableMap~FrameState} frameState Frame state. - * @param {module:ol/layer/Layer~State} layerState Layer state. - * @param {module:ol/webgl/Context} context Context. - * @return {boolean} whether composeFrame should be called. - */ -WebGLLayerRenderer.prototype.prepareFrame = function(frameState, layerState, context) {}; - - -/** - * @abstract - * @param {module:ol~Pixel} pixel Pixel. - * @param {module:ol/PluggableMap~FrameState} frameState FrameState. - * @param {function(this: S, module:ol/layer/Layer, (Uint8ClampedArray|Uint8Array)): T} callback Layer - * callback. - * @param {S} thisArg Value to use as `this` when executing `callback`. - * @return {T|undefined} Callback result. - * @template S,T,U - */ -WebGLLayerRenderer.prototype.forEachLayerAtPixel = function(pixel, frameState, callback, thisArg) {}; export default WebGLLayerRenderer; diff --git a/src/ol/renderer/webgl/Map.js b/src/ol/renderer/webgl/Map.js index 960e1a093d..8d4c254aa8 100644 --- a/src/ol/renderer/webgl/Map.js +++ b/src/ol/renderer/webgl/Map.js @@ -44,545 +44,539 @@ const WEBGL_TEXTURE_CACHE_HIGH_WATER_MARK = 1024; * @param {module:ol/PluggableMap} map Map. * @api */ -const WebGLMapRenderer = function(map) { - MapRenderer.call(this, map); +class WebGLMapRenderer { + constructor(map) { + MapRenderer.call(this, map); - const container = map.getViewport(); + const container = map.getViewport(); - /** - * @private - * @type {HTMLCanvasElement} - */ - this.canvas_ = /** @type {HTMLCanvasElement} */ - (document.createElement('CANVAS')); - this.canvas_.style.width = '100%'; - this.canvas_.style.height = '100%'; - this.canvas_.style.display = 'block'; - this.canvas_.className = CLASS_UNSELECTABLE; - container.insertBefore(this.canvas_, container.childNodes[0] || null); - - /** - * @private - * @type {number} - */ - this.clipTileCanvasWidth_ = 0; - - /** - * @private - * @type {number} - */ - this.clipTileCanvasHeight_ = 0; - - /** - * @private - * @type {CanvasRenderingContext2D} - */ - this.clipTileContext_ = createCanvasContext2D(); - - /** - * @private - * @type {boolean} - */ - this.renderedVisible_ = true; - - /** - * @private - * @type {WebGLRenderingContext} - */ - this.gl_ = getContext(this.canvas_, { - antialias: true, - depth: true, - failIfMajorPerformanceCaveat: true, - preserveDrawingBuffer: false, - stencil: true - }); - - /** - * @private - * @type {module:ol/webgl/Context} - */ - this.context_ = new WebGLContext(this.canvas_, this.gl_); - - listen(this.canvas_, ContextEventType.LOST, - this.handleWebGLContextLost, this); - listen(this.canvas_, ContextEventType.RESTORED, - this.handleWebGLContextRestored, this); - - /** - * @private - * @type {module:ol/structs/LRUCache.} - */ - this.textureCache_ = new LRUCache(); - - /** - * @private - * @type {module:ol/coordinate~Coordinate} - */ - this.focus_ = null; - - /** - * @private - * @type {module:ol/structs/PriorityQueue.} - */ - this.tileTextureQueue_ = new PriorityQueue( /** - * @param {Array.<*>} element Element. - * @return {number} Priority. - * @this {module:ol/renderer/webgl/Map} + * @private + * @type {HTMLCanvasElement} */ - (function(element) { - const tileCenter = /** @type {module:ol/coordinate~Coordinate} */ (element[1]); - const tileResolution = /** @type {number} */ (element[2]); - const deltaX = tileCenter[0] - this.focus_[0]; - const deltaY = tileCenter[1] - this.focus_[1]; - return 65536 * Math.log(tileResolution) + - Math.sqrt(deltaX * deltaX + deltaY * deltaY) / tileResolution; - }).bind(this), + this.canvas_ = /** @type {HTMLCanvasElement} */ + (document.createElement('CANVAS')); + this.canvas_.style.width = '100%'; + this.canvas_.style.height = '100%'; + this.canvas_.style.display = 'block'; + this.canvas_.className = CLASS_UNSELECTABLE; + container.insertBefore(this.canvas_, container.childNodes[0] || null); + /** - * @param {Array.<*>} element Element. - * @return {string} Key. + * @private + * @type {number} */ - function(element) { - return ( - /** @type {module:ol/Tile} */ (element[0]).getKey() - ); + this.clipTileCanvasWidth_ = 0; + + /** + * @private + * @type {number} + */ + this.clipTileCanvasHeight_ = 0; + + /** + * @private + * @type {CanvasRenderingContext2D} + */ + this.clipTileContext_ = createCanvasContext2D(); + + /** + * @private + * @type {boolean} + */ + this.renderedVisible_ = true; + + /** + * @private + * @type {WebGLRenderingContext} + */ + this.gl_ = getContext(this.canvas_, { + antialias: true, + depth: true, + failIfMajorPerformanceCaveat: true, + preserveDrawingBuffer: false, + stencil: true }); + /** + * @private + * @type {module:ol/webgl/Context} + */ + this.context_ = new WebGLContext(this.canvas_, this.gl_); + + listen(this.canvas_, ContextEventType.LOST, + this.handleWebGLContextLost, this); + listen(this.canvas_, ContextEventType.RESTORED, + this.handleWebGLContextRestored, this); + + /** + * @private + * @type {module:ol/structs/LRUCache.} + */ + this.textureCache_ = new LRUCache(); + + /** + * @private + * @type {module:ol/coordinate~Coordinate} + */ + this.focus_ = null; + + /** + * @private + * @type {module:ol/structs/PriorityQueue.} + */ + this.tileTextureQueue_ = new PriorityQueue( + /** + * @param {Array.<*>} element Element. + * @return {number} Priority. + * @this {module:ol/renderer/webgl/Map} + */ + (function(element) { + const tileCenter = /** @type {module:ol/coordinate~Coordinate} */ (element[1]); + const tileResolution = /** @type {number} */ (element[2]); + const deltaX = tileCenter[0] - this.focus_[0]; + const deltaY = tileCenter[1] - this.focus_[1]; + return 65536 * Math.log(tileResolution) + + Math.sqrt(deltaX * deltaX + deltaY * deltaY) / tileResolution; + }).bind(this), + /** + * @param {Array.<*>} element Element. + * @return {string} Key. + */ + function(element) { + return ( + /** @type {module:ol/Tile} */ (element[0]).getKey() + ); + }); + + + /** + * @param {module:ol/PluggableMap} map Map. + * @param {?module:ol/PluggableMap~FrameState} frameState Frame state. + * @return {boolean} false. + * @this {module:ol/renderer/webgl/Map} + */ + this.loadNextTileTexture_ = + function(map, frameState) { + if (!this.tileTextureQueue_.isEmpty()) { + this.tileTextureQueue_.reprioritize(); + const element = this.tileTextureQueue_.dequeue(); + const tile = /** @type {module:ol/Tile} */ (element[0]); + const tileSize = /** @type {module:ol/size~Size} */ (element[3]); + const tileGutter = /** @type {number} */ (element[4]); + this.bindTileTexture( + tile, tileSize, tileGutter, LINEAR, LINEAR); + } + return false; + }.bind(this); + + + /** + * @private + * @type {number} + */ + this.textureCacheFrameMarkerCount_ = 0; + + this.initializeGL_(); + } + + /** + * @param {module:ol/Tile} tile Tile. + * @param {module:ol/size~Size} tileSize Tile size. + * @param {number} tileGutter Tile gutter. + * @param {number} magFilter Mag filter. + * @param {number} minFilter Min filter. + */ + bindTileTexture(tile, tileSize, tileGutter, magFilter, minFilter) { + const gl = this.getGL(); + const tileKey = tile.getKey(); + if (this.textureCache_.containsKey(tileKey)) { + const textureCacheEntry = this.textureCache_.get(tileKey); + gl.bindTexture(TEXTURE_2D, textureCacheEntry.texture); + if (textureCacheEntry.magFilter != magFilter) { + gl.texParameteri( + TEXTURE_2D, TEXTURE_MAG_FILTER, magFilter); + textureCacheEntry.magFilter = magFilter; + } + if (textureCacheEntry.minFilter != minFilter) { + gl.texParameteri( + TEXTURE_2D, TEXTURE_MIN_FILTER, minFilter); + textureCacheEntry.minFilter = minFilter; + } + } else { + const texture = gl.createTexture(); + gl.bindTexture(TEXTURE_2D, texture); + if (tileGutter > 0) { + const clipTileCanvas = this.clipTileContext_.canvas; + const clipTileContext = this.clipTileContext_; + if (this.clipTileCanvasWidth_ !== tileSize[0] || + this.clipTileCanvasHeight_ !== tileSize[1]) { + clipTileCanvas.width = tileSize[0]; + clipTileCanvas.height = tileSize[1]; + this.clipTileCanvasWidth_ = tileSize[0]; + this.clipTileCanvasHeight_ = tileSize[1]; + } else { + clipTileContext.clearRect(0, 0, tileSize[0], tileSize[1]); + } + clipTileContext.drawImage(tile.getImage(), tileGutter, tileGutter, + tileSize[0], tileSize[1], 0, 0, tileSize[0], tileSize[1]); + gl.texImage2D(TEXTURE_2D, 0, + RGBA, RGBA, + UNSIGNED_BYTE, clipTileCanvas); + } else { + gl.texImage2D(TEXTURE_2D, 0, + RGBA, RGBA, + UNSIGNED_BYTE, tile.getImage()); + } + gl.texParameteri( + TEXTURE_2D, TEXTURE_MAG_FILTER, magFilter); + gl.texParameteri( + TEXTURE_2D, TEXTURE_MIN_FILTER, minFilter); + gl.texParameteri(TEXTURE_2D, TEXTURE_WRAP_S, + CLAMP_TO_EDGE); + gl.texParameteri(TEXTURE_2D, TEXTURE_WRAP_T, + CLAMP_TO_EDGE); + this.textureCache_.set(tileKey, { + texture: texture, + magFilter: magFilter, + minFilter: minFilter + }); + } + } + + /** + * @param {module:ol/render/EventType} type Event type. + * @param {module:ol/PluggableMap~FrameState} frameState Frame state. + * @private + */ + dispatchComposeEvent_(type, frameState) { + const map = this.getMap(); + if (map.hasListener(type)) { + const context = this.context_; + + const extent = frameState.extent; + const size = frameState.size; + const viewState = frameState.viewState; + const pixelRatio = frameState.pixelRatio; + + const resolution = viewState.resolution; + const center = viewState.center; + const rotation = viewState.rotation; + + const vectorContext = new WebGLImmediateRenderer(context, + center, resolution, rotation, size, extent, pixelRatio); + const composeEvent = new RenderEvent(type, vectorContext, + frameState, null, context); + map.dispatchEvent(composeEvent); + } + } + + /** + * @inheritDoc + */ + disposeInternal() { + const gl = this.getGL(); + if (!gl.isContextLost()) { + this.textureCache_.forEach( + /** + * @param {?module:ol/renderer/webgl/Map~TextureCacheEntry} textureCacheEntry + * Texture cache entry. + */ + function(textureCacheEntry) { + if (textureCacheEntry) { + gl.deleteTexture(textureCacheEntry.texture); + } + }); + } + this.context_.dispose(); + MapRenderer.prototype.disposeInternal.call(this); + } /** * @param {module:ol/PluggableMap} map Map. - * @param {?module:ol/PluggableMap~FrameState} frameState Frame state. - * @return {boolean} false. - * @this {module:ol/renderer/webgl/Map} + * @param {module:ol/PluggableMap~FrameState} frameState Frame state. + * @private */ - this.loadNextTileTexture_ = - function(map, frameState) { - if (!this.tileTextureQueue_.isEmpty()) { - this.tileTextureQueue_.reprioritize(); - const element = this.tileTextureQueue_.dequeue(); - const tile = /** @type {module:ol/Tile} */ (element[0]); - const tileSize = /** @type {module:ol/size~Size} */ (element[3]); - const tileGutter = /** @type {number} */ (element[4]); - this.bindTileTexture( - tile, tileSize, tileGutter, LINEAR, LINEAR); + expireCache_(map, frameState) { + const gl = this.getGL(); + let textureCacheEntry; + while (this.textureCache_.getCount() - this.textureCacheFrameMarkerCount_ > + WEBGL_TEXTURE_CACHE_HIGH_WATER_MARK) { + textureCacheEntry = this.textureCache_.peekLast(); + if (!textureCacheEntry) { + if (+this.textureCache_.peekLastKey() == frameState.index) { + break; + } else { + --this.textureCacheFrameMarkerCount_; } - return false; - }.bind(this); + } else { + gl.deleteTexture(textureCacheEntry.texture); + } + this.textureCache_.pop(); + } + } + /** + * @return {module:ol/webgl/Context} The context. + */ + getContext() { + return this.context_; + } + + /** + * @return {WebGLRenderingContext} GL. + */ + getGL() { + return this.gl_; + } + + /** + * @return {module:ol/structs/PriorityQueue.} Tile texture queue. + */ + getTileTextureQueue() { + return this.tileTextureQueue_; + } + + /** + * @param {module:ol/events/Event} event Event. + * @protected + */ + handleWebGLContextLost(event) { + event.preventDefault(); + this.textureCache_.clear(); + this.textureCacheFrameMarkerCount_ = 0; + + const renderers = this.getLayerRenderers(); + for (const id in renderers) { + const renderer = /** @type {module:ol/renderer/webgl/Layer} */ (renderers[id]); + renderer.handleWebGLContextLost(); + } + } + + /** + * @protected + */ + handleWebGLContextRestored() { + this.initializeGL_(); + this.getMap().render(); + } /** * @private - * @type {number} */ - this.textureCacheFrameMarkerCount_ = 0; + initializeGL_() { + const gl = this.gl_; + gl.activeTexture(TEXTURE0); + gl.blendFuncSeparate( + SRC_ALPHA, ONE_MINUS_SRC_ALPHA, + ONE, ONE_MINUS_SRC_ALPHA); + gl.disable(CULL_FACE); + gl.disable(DEPTH_TEST); + gl.disable(SCISSOR_TEST); + gl.disable(STENCIL_TEST); + } - this.initializeGL_(); -}; + /** + * @param {module:ol/Tile} tile Tile. + * @return {boolean} Is tile texture loaded. + */ + isTileTextureLoaded(tile) { + return this.textureCache_.containsKey(tile.getKey()); + } + + /** + * @inheritDoc + */ + renderFrame(frameState) { + + const context = this.getContext(); + const gl = this.getGL(); + + if (gl.isContextLost()) { + return false; + } + + if (!frameState) { + if (this.renderedVisible_) { + this.canvas_.style.display = 'none'; + this.renderedVisible_ = false; + } + return false; + } + + this.focus_ = frameState.focus; + + this.textureCache_.set((-frameState.index).toString(), null); + ++this.textureCacheFrameMarkerCount_; + + this.dispatchComposeEvent_(RenderEventType.PRECOMPOSE, frameState); + + /** @type {Array.} */ + const layerStatesToDraw = []; + const layerStatesArray = frameState.layerStatesArray; + stableSort(layerStatesArray, sortByZIndex); + + const viewResolution = frameState.viewState.resolution; + let i, ii, layerRenderer, layerState; + for (i = 0, ii = layerStatesArray.length; i < ii; ++i) { + layerState = layerStatesArray[i]; + if (visibleAtResolution(layerState, viewResolution) && + layerState.sourceState == SourceState.READY) { + layerRenderer = /** @type {module:ol/renderer/webgl/Layer} */ (this.getLayerRenderer(layerState.layer)); + if (layerRenderer.prepareFrame(frameState, layerState, context)) { + layerStatesToDraw.push(layerState); + } + } + } + + const width = frameState.size[0] * frameState.pixelRatio; + const height = frameState.size[1] * frameState.pixelRatio; + if (this.canvas_.width != width || this.canvas_.height != height) { + this.canvas_.width = width; + this.canvas_.height = height; + } + + gl.bindFramebuffer(FRAMEBUFFER, null); + + gl.clearColor(0, 0, 0, 0); + gl.clear(COLOR_BUFFER_BIT); + gl.enable(BLEND); + gl.viewport(0, 0, this.canvas_.width, this.canvas_.height); + + for (i = 0, ii = layerStatesToDraw.length; i < ii; ++i) { + layerState = layerStatesToDraw[i]; + layerRenderer = /** @type {module:ol/renderer/webgl/Layer} */ (this.getLayerRenderer(layerState.layer)); + layerRenderer.composeFrame(frameState, layerState, context); + } + + if (!this.renderedVisible_) { + this.canvas_.style.display = ''; + this.renderedVisible_ = true; + } + + this.calculateMatrices2D(frameState); + + if (this.textureCache_.getCount() - this.textureCacheFrameMarkerCount_ > + WEBGL_TEXTURE_CACHE_HIGH_WATER_MARK) { + frameState.postRenderFunctions.push( + /** @type {module:ol/PluggableMap~PostRenderFunction} */ (this.expireCache_.bind(this)) + ); + } + + if (!this.tileTextureQueue_.isEmpty()) { + frameState.postRenderFunctions.push(this.loadNextTileTexture_); + frameState.animate = true; + } + + this.dispatchComposeEvent_(RenderEventType.POSTCOMPOSE, frameState); + + this.scheduleRemoveUnusedLayerRenderers(frameState); + this.scheduleExpireIconCache(frameState); + + } + + /** + * @inheritDoc + */ + forEachFeatureAtCoordinate( + coordinate, + frameState, + hitTolerance, + callback, + thisArg, + layerFilter, + thisArg2 + ) { + let result; + + if (this.getGL().isContextLost()) { + return false; + } + + const viewState = frameState.viewState; + + const layerStates = frameState.layerStatesArray; + const numLayers = layerStates.length; + let i; + for (i = numLayers - 1; i >= 0; --i) { + const layerState = layerStates[i]; + const layer = layerState.layer; + if (visibleAtResolution(layerState, viewState.resolution) && + layerFilter.call(thisArg2, layer)) { + const layerRenderer = this.getLayerRenderer(layer); + result = layerRenderer.forEachFeatureAtCoordinate( + coordinate, frameState, hitTolerance, callback, thisArg); + if (result) { + return result; + } + } + } + return undefined; + } + + /** + * @inheritDoc + */ + hasFeatureAtCoordinate(coordinate, frameState, hitTolerance, layerFilter, thisArg) { + let hasFeature = false; + + if (this.getGL().isContextLost()) { + return false; + } + + const viewState = frameState.viewState; + + const layerStates = frameState.layerStatesArray; + const numLayers = layerStates.length; + let i; + for (i = numLayers - 1; i >= 0; --i) { + const layerState = layerStates[i]; + const layer = layerState.layer; + if (visibleAtResolution(layerState, viewState.resolution) && + layerFilter.call(thisArg, layer)) { + const layerRenderer = this.getLayerRenderer(layer); + hasFeature = + layerRenderer.hasFeatureAtCoordinate(coordinate, frameState); + if (hasFeature) { + return true; + } + } + } + return hasFeature; + } + + /** + * @inheritDoc + */ + forEachLayerAtPixel(pixel, frameState, hitTolerance, callback, thisArg, layerFilter, thisArg2) { + if (this.getGL().isContextLost()) { + return false; + } + + const viewState = frameState.viewState; + let result; + + const layerStates = frameState.layerStatesArray; + const numLayers = layerStates.length; + let i; + for (i = numLayers - 1; i >= 0; --i) { + const layerState = layerStates[i]; + const layer = layerState.layer; + if (visibleAtResolution(layerState, viewState.resolution) && + layerFilter.call(thisArg, layer)) { + const layerRenderer = /** @type {module:ol/renderer/webgl/Layer} */ (this.getLayerRenderer(layer)); + result = layerRenderer.forEachLayerAtPixel( + pixel, frameState, callback, thisArg); + if (result) { + return result; + } + } + } + return undefined; + } +} inherits(WebGLMapRenderer, MapRenderer); -/** - * @param {module:ol/Tile} tile Tile. - * @param {module:ol/size~Size} tileSize Tile size. - * @param {number} tileGutter Tile gutter. - * @param {number} magFilter Mag filter. - * @param {number} minFilter Min filter. - */ -WebGLMapRenderer.prototype.bindTileTexture = function(tile, tileSize, tileGutter, magFilter, minFilter) { - const gl = this.getGL(); - const tileKey = tile.getKey(); - if (this.textureCache_.containsKey(tileKey)) { - const textureCacheEntry = this.textureCache_.get(tileKey); - gl.bindTexture(TEXTURE_2D, textureCacheEntry.texture); - if (textureCacheEntry.magFilter != magFilter) { - gl.texParameteri( - TEXTURE_2D, TEXTURE_MAG_FILTER, magFilter); - textureCacheEntry.magFilter = magFilter; - } - if (textureCacheEntry.minFilter != minFilter) { - gl.texParameteri( - TEXTURE_2D, TEXTURE_MIN_FILTER, minFilter); - textureCacheEntry.minFilter = minFilter; - } - } else { - const texture = gl.createTexture(); - gl.bindTexture(TEXTURE_2D, texture); - if (tileGutter > 0) { - const clipTileCanvas = this.clipTileContext_.canvas; - const clipTileContext = this.clipTileContext_; - if (this.clipTileCanvasWidth_ !== tileSize[0] || - this.clipTileCanvasHeight_ !== tileSize[1]) { - clipTileCanvas.width = tileSize[0]; - clipTileCanvas.height = tileSize[1]; - this.clipTileCanvasWidth_ = tileSize[0]; - this.clipTileCanvasHeight_ = tileSize[1]; - } else { - clipTileContext.clearRect(0, 0, tileSize[0], tileSize[1]); - } - clipTileContext.drawImage(tile.getImage(), tileGutter, tileGutter, - tileSize[0], tileSize[1], 0, 0, tileSize[0], tileSize[1]); - gl.texImage2D(TEXTURE_2D, 0, - RGBA, RGBA, - UNSIGNED_BYTE, clipTileCanvas); - } else { - gl.texImage2D(TEXTURE_2D, 0, - RGBA, RGBA, - UNSIGNED_BYTE, tile.getImage()); - } - gl.texParameteri( - TEXTURE_2D, TEXTURE_MAG_FILTER, magFilter); - gl.texParameteri( - TEXTURE_2D, TEXTURE_MIN_FILTER, minFilter); - gl.texParameteri(TEXTURE_2D, TEXTURE_WRAP_S, - CLAMP_TO_EDGE); - gl.texParameteri(TEXTURE_2D, TEXTURE_WRAP_T, - CLAMP_TO_EDGE); - this.textureCache_.set(tileKey, { - texture: texture, - magFilter: magFilter, - minFilter: minFilter - }); - } -}; - - -/** - * @param {module:ol/render/EventType} type Event type. - * @param {module:ol/PluggableMap~FrameState} frameState Frame state. - * @private - */ -WebGLMapRenderer.prototype.dispatchComposeEvent_ = function(type, frameState) { - const map = this.getMap(); - if (map.hasListener(type)) { - const context = this.context_; - - const extent = frameState.extent; - const size = frameState.size; - const viewState = frameState.viewState; - const pixelRatio = frameState.pixelRatio; - - const resolution = viewState.resolution; - const center = viewState.center; - const rotation = viewState.rotation; - - const vectorContext = new WebGLImmediateRenderer(context, - center, resolution, rotation, size, extent, pixelRatio); - const composeEvent = new RenderEvent(type, vectorContext, - frameState, null, context); - map.dispatchEvent(composeEvent); - } -}; - - -/** - * @inheritDoc - */ -WebGLMapRenderer.prototype.disposeInternal = function() { - const gl = this.getGL(); - if (!gl.isContextLost()) { - this.textureCache_.forEach( - /** - * @param {?module:ol/renderer/webgl/Map~TextureCacheEntry} textureCacheEntry - * Texture cache entry. - */ - function(textureCacheEntry) { - if (textureCacheEntry) { - gl.deleteTexture(textureCacheEntry.texture); - } - }); - } - this.context_.dispose(); - MapRenderer.prototype.disposeInternal.call(this); -}; - - -/** - * @param {module:ol/PluggableMap} map Map. - * @param {module:ol/PluggableMap~FrameState} frameState Frame state. - * @private - */ -WebGLMapRenderer.prototype.expireCache_ = function(map, frameState) { - const gl = this.getGL(); - let textureCacheEntry; - while (this.textureCache_.getCount() - this.textureCacheFrameMarkerCount_ > - WEBGL_TEXTURE_CACHE_HIGH_WATER_MARK) { - textureCacheEntry = this.textureCache_.peekLast(); - if (!textureCacheEntry) { - if (+this.textureCache_.peekLastKey() == frameState.index) { - break; - } else { - --this.textureCacheFrameMarkerCount_; - } - } else { - gl.deleteTexture(textureCacheEntry.texture); - } - this.textureCache_.pop(); - } -}; - - -/** - * @return {module:ol/webgl/Context} The context. - */ -WebGLMapRenderer.prototype.getContext = function() { - return this.context_; -}; - - -/** - * @return {WebGLRenderingContext} GL. - */ -WebGLMapRenderer.prototype.getGL = function() { - return this.gl_; -}; - - -/** - * @return {module:ol/structs/PriorityQueue.} Tile texture queue. - */ -WebGLMapRenderer.prototype.getTileTextureQueue = function() { - return this.tileTextureQueue_; -}; - - -/** - * @param {module:ol/events/Event} event Event. - * @protected - */ -WebGLMapRenderer.prototype.handleWebGLContextLost = function(event) { - event.preventDefault(); - this.textureCache_.clear(); - this.textureCacheFrameMarkerCount_ = 0; - - const renderers = this.getLayerRenderers(); - for (const id in renderers) { - const renderer = /** @type {module:ol/renderer/webgl/Layer} */ (renderers[id]); - renderer.handleWebGLContextLost(); - } -}; - - -/** - * @protected - */ -WebGLMapRenderer.prototype.handleWebGLContextRestored = function() { - this.initializeGL_(); - this.getMap().render(); -}; - - -/** - * @private - */ -WebGLMapRenderer.prototype.initializeGL_ = function() { - const gl = this.gl_; - gl.activeTexture(TEXTURE0); - gl.blendFuncSeparate( - SRC_ALPHA, ONE_MINUS_SRC_ALPHA, - ONE, ONE_MINUS_SRC_ALPHA); - gl.disable(CULL_FACE); - gl.disable(DEPTH_TEST); - gl.disable(SCISSOR_TEST); - gl.disable(STENCIL_TEST); -}; - - -/** - * @param {module:ol/Tile} tile Tile. - * @return {boolean} Is tile texture loaded. - */ -WebGLMapRenderer.prototype.isTileTextureLoaded = function(tile) { - return this.textureCache_.containsKey(tile.getKey()); -}; - - -/** - * @inheritDoc - */ -WebGLMapRenderer.prototype.renderFrame = function(frameState) { - - const context = this.getContext(); - const gl = this.getGL(); - - if (gl.isContextLost()) { - return false; - } - - if (!frameState) { - if (this.renderedVisible_) { - this.canvas_.style.display = 'none'; - this.renderedVisible_ = false; - } - return false; - } - - this.focus_ = frameState.focus; - - this.textureCache_.set((-frameState.index).toString(), null); - ++this.textureCacheFrameMarkerCount_; - - this.dispatchComposeEvent_(RenderEventType.PRECOMPOSE, frameState); - - /** @type {Array.} */ - const layerStatesToDraw = []; - const layerStatesArray = frameState.layerStatesArray; - stableSort(layerStatesArray, sortByZIndex); - - const viewResolution = frameState.viewState.resolution; - let i, ii, layerRenderer, layerState; - for (i = 0, ii = layerStatesArray.length; i < ii; ++i) { - layerState = layerStatesArray[i]; - if (visibleAtResolution(layerState, viewResolution) && - layerState.sourceState == SourceState.READY) { - layerRenderer = /** @type {module:ol/renderer/webgl/Layer} */ (this.getLayerRenderer(layerState.layer)); - if (layerRenderer.prepareFrame(frameState, layerState, context)) { - layerStatesToDraw.push(layerState); - } - } - } - - const width = frameState.size[0] * frameState.pixelRatio; - const height = frameState.size[1] * frameState.pixelRatio; - if (this.canvas_.width != width || this.canvas_.height != height) { - this.canvas_.width = width; - this.canvas_.height = height; - } - - gl.bindFramebuffer(FRAMEBUFFER, null); - - gl.clearColor(0, 0, 0, 0); - gl.clear(COLOR_BUFFER_BIT); - gl.enable(BLEND); - gl.viewport(0, 0, this.canvas_.width, this.canvas_.height); - - for (i = 0, ii = layerStatesToDraw.length; i < ii; ++i) { - layerState = layerStatesToDraw[i]; - layerRenderer = /** @type {module:ol/renderer/webgl/Layer} */ (this.getLayerRenderer(layerState.layer)); - layerRenderer.composeFrame(frameState, layerState, context); - } - - if (!this.renderedVisible_) { - this.canvas_.style.display = ''; - this.renderedVisible_ = true; - } - - this.calculateMatrices2D(frameState); - - if (this.textureCache_.getCount() - this.textureCacheFrameMarkerCount_ > - WEBGL_TEXTURE_CACHE_HIGH_WATER_MARK) { - frameState.postRenderFunctions.push( - /** @type {module:ol/PluggableMap~PostRenderFunction} */ (this.expireCache_.bind(this)) - ); - } - - if (!this.tileTextureQueue_.isEmpty()) { - frameState.postRenderFunctions.push(this.loadNextTileTexture_); - frameState.animate = true; - } - - this.dispatchComposeEvent_(RenderEventType.POSTCOMPOSE, frameState); - - this.scheduleRemoveUnusedLayerRenderers(frameState); - this.scheduleExpireIconCache(frameState); - -}; - - -/** - * @inheritDoc - */ -WebGLMapRenderer.prototype.forEachFeatureAtCoordinate = function(coordinate, frameState, hitTolerance, callback, thisArg, - layerFilter, thisArg2) { - let result; - - if (this.getGL().isContextLost()) { - return false; - } - - const viewState = frameState.viewState; - - const layerStates = frameState.layerStatesArray; - const numLayers = layerStates.length; - let i; - for (i = numLayers - 1; i >= 0; --i) { - const layerState = layerStates[i]; - const layer = layerState.layer; - if (visibleAtResolution(layerState, viewState.resolution) && - layerFilter.call(thisArg2, layer)) { - const layerRenderer = this.getLayerRenderer(layer); - result = layerRenderer.forEachFeatureAtCoordinate( - coordinate, frameState, hitTolerance, callback, thisArg); - if (result) { - return result; - } - } - } - return undefined; -}; - - -/** - * @inheritDoc - */ -WebGLMapRenderer.prototype.hasFeatureAtCoordinate = function(coordinate, frameState, hitTolerance, layerFilter, thisArg) { - let hasFeature = false; - - if (this.getGL().isContextLost()) { - return false; - } - - const viewState = frameState.viewState; - - const layerStates = frameState.layerStatesArray; - const numLayers = layerStates.length; - let i; - for (i = numLayers - 1; i >= 0; --i) { - const layerState = layerStates[i]; - const layer = layerState.layer; - if (visibleAtResolution(layerState, viewState.resolution) && - layerFilter.call(thisArg, layer)) { - const layerRenderer = this.getLayerRenderer(layer); - hasFeature = - layerRenderer.hasFeatureAtCoordinate(coordinate, frameState); - if (hasFeature) { - return true; - } - } - } - return hasFeature; -}; - - -/** - * @inheritDoc - */ -WebGLMapRenderer.prototype.forEachLayerAtPixel = function(pixel, frameState, hitTolerance, callback, thisArg, - layerFilter, thisArg2) { - if (this.getGL().isContextLost()) { - return false; - } - - const viewState = frameState.viewState; - let result; - - const layerStates = frameState.layerStatesArray; - const numLayers = layerStates.length; - let i; - for (i = numLayers - 1; i >= 0; --i) { - const layerState = layerStates[i]; - const layer = layerState.layer; - if (visibleAtResolution(layerState, viewState.resolution) && - layerFilter.call(thisArg, layer)) { - const layerRenderer = /** @type {module:ol/renderer/webgl/Layer} */ (this.getLayerRenderer(layer)); - result = layerRenderer.forEachLayerAtPixel( - pixel, frameState, callback, thisArg); - if (result) { - return result; - } - } - } - return undefined; -}; - export default WebGLMapRenderer; diff --git a/src/ol/renderer/webgl/TileLayer.js b/src/ol/renderer/webgl/TileLayer.js index 265408e816..dfc9161f8d 100644 --- a/src/ol/renderer/webgl/TileLayer.js +++ b/src/ol/renderer/webgl/TileLayer.js @@ -32,64 +32,355 @@ import WebGLBuffer from '../../webgl/Buffer.js'; * @param {module:ol/layer/Tile} tileLayer Tile layer. * @api */ -const WebGLTileLayerRenderer = function(mapRenderer, tileLayer) { +class WebGLTileLayerRenderer { + constructor(mapRenderer, tileLayer) { - WebGLLayerRenderer.call(this, mapRenderer, tileLayer); + WebGLLayerRenderer.call(this, mapRenderer, tileLayer); + + /** + * @private + * @type {module:ol/webgl/Fragment} + */ + this.fragmentShader_ = fragment; + + /** + * @private + * @type {module:ol/webgl/Vertex} + */ + this.vertexShader_ = vertex; + + /** + * @private + * @type {module:ol/renderer/webgl/tilelayershader/Locations} + */ + this.locations_ = null; + + /** + * @private + * @type {module:ol/webgl/Buffer} + */ + this.renderArrayBuffer_ = new WebGLBuffer([ + 0, 0, 0, 1, + 1, 0, 1, 1, + 0, 1, 0, 0, + 1, 1, 1, 0 + ]); + + /** + * @private + * @type {module:ol/TileRange} + */ + this.renderedTileRange_ = null; + + /** + * @private + * @type {module:ol/extent~Extent} + */ + this.renderedFramebufferExtent_ = null; + + /** + * @private + * @type {number} + */ + this.renderedRevision_ = -1; + + /** + * @private + * @type {module:ol/size~Size} + */ + this.tmpSize_ = [0, 0]; + + } /** - * @private - * @type {module:ol/webgl/Fragment} + * @inheritDoc */ - this.fragmentShader_ = fragment; + disposeInternal() { + const context = this.mapRenderer.getContext(); + context.deleteBuffer(this.renderArrayBuffer_); + WebGLLayerRenderer.prototype.disposeInternal.call(this); + } /** - * @private - * @type {module:ol/webgl/Vertex} + * @inheritDoc */ - this.vertexShader_ = vertex; + createLoadedTileFinder(source, projection, tiles) { + const mapRenderer = this.mapRenderer; + + return ( + /** + * @param {number} zoom Zoom level. + * @param {module:ol/TileRange} tileRange Tile range. + * @return {boolean} The tile range is fully loaded. + */ + function(zoom, tileRange) { + function callback(tile) { + const loaded = mapRenderer.isTileTextureLoaded(tile); + if (loaded) { + if (!tiles[zoom]) { + tiles[zoom] = {}; + } + tiles[zoom][tile.tileCoord.toString()] = tile; + } + return loaded; + } + return source.forEachLoadedTile(projection, zoom, tileRange, callback); + } + ); + } /** - * @private - * @type {module:ol/renderer/webgl/tilelayershader/Locations} + * @inheritDoc */ - this.locations_ = null; + handleWebGLContextLost() { + WebGLLayerRenderer.prototype.handleWebGLContextLost.call(this); + this.locations_ = null; + } /** - * @private - * @type {module:ol/webgl/Buffer} + * @inheritDoc */ - this.renderArrayBuffer_ = new WebGLBuffer([ - 0, 0, 0, 1, - 1, 0, 1, 1, - 0, 1, 0, 0, - 1, 1, 1, 0 - ]); + prepareFrame(frameState, layerState, context) { + + const mapRenderer = this.mapRenderer; + const gl = context.getGL(); + + const viewState = frameState.viewState; + const projection = viewState.projection; + + const tileLayer = /** @type {module:ol/layer/Tile} */ (this.getLayer()); + const tileSource = tileLayer.getSource(); + const tileGrid = tileSource.getTileGridForProjection(projection); + const z = tileGrid.getZForResolution(viewState.resolution); + const tileResolution = tileGrid.getResolution(z); + + const tilePixelSize = + tileSource.getTilePixelSize(z, frameState.pixelRatio, projection); + const pixelRatio = tilePixelSize[0] / + toSize(tileGrid.getTileSize(z), this.tmpSize_)[0]; + const tilePixelResolution = tileResolution / pixelRatio; + const tileGutter = tileSource.getTilePixelRatio(pixelRatio) * tileSource.getGutter(projection); + + const center = viewState.center; + const extent = frameState.extent; + const tileRange = tileGrid.getTileRangeForExtentAndZ(extent, z); + + let framebufferExtent; + if (this.renderedTileRange_ && + this.renderedTileRange_.equals(tileRange) && + this.renderedRevision_ == tileSource.getRevision()) { + framebufferExtent = this.renderedFramebufferExtent_; + } else { + + const tileRangeSize = tileRange.getSize(); + + const maxDimension = Math.max( + tileRangeSize[0] * tilePixelSize[0], + tileRangeSize[1] * tilePixelSize[1]); + const framebufferDimension = roundUpToPowerOfTwo(maxDimension); + const framebufferExtentDimension = tilePixelResolution * framebufferDimension; + const origin = tileGrid.getOrigin(z); + const minX = origin[0] + + tileRange.minX * tilePixelSize[0] * tilePixelResolution; + const minY = origin[1] + + tileRange.minY * tilePixelSize[1] * tilePixelResolution; + framebufferExtent = [ + minX, minY, + minX + framebufferExtentDimension, minY + framebufferExtentDimension + ]; + + this.bindFramebuffer(frameState, framebufferDimension); + gl.viewport(0, 0, framebufferDimension, framebufferDimension); + + gl.clearColor(0, 0, 0, 0); + gl.clear(COLOR_BUFFER_BIT); + gl.disable(BLEND); + + const program = context.getProgram(this.fragmentShader_, this.vertexShader_); + context.useProgram(program); + if (!this.locations_) { + this.locations_ = new Locations(gl, program); + } + + context.bindBuffer(ARRAY_BUFFER, this.renderArrayBuffer_); + gl.enableVertexAttribArray(this.locations_.a_position); + gl.vertexAttribPointer( + this.locations_.a_position, 2, FLOAT, false, 16, 0); + gl.enableVertexAttribArray(this.locations_.a_texCoord); + gl.vertexAttribPointer( + this.locations_.a_texCoord, 2, FLOAT, false, 16, 8); + gl.uniform1i(this.locations_.u_texture, 0); + + /** + * @type {Object.>} + */ + const tilesToDrawByZ = {}; + tilesToDrawByZ[z] = {}; + + const findLoadedTiles = this.createLoadedTileFinder( + tileSource, projection, tilesToDrawByZ); + + const useInterimTilesOnError = tileLayer.getUseInterimTilesOnError(); + let allTilesLoaded = true; + const tmpExtent = createEmpty(); + const tmpTileRange = new TileRange(0, 0, 0, 0); + let childTileRange, drawable, fullyLoaded, tile, tileState; + let x, y, tileExtent; + for (x = tileRange.minX; x <= tileRange.maxX; ++x) { + for (y = tileRange.minY; y <= tileRange.maxY; ++y) { + + tile = tileSource.getTile(z, x, y, pixelRatio, projection); + if (layerState.extent !== undefined) { + // ignore tiles outside layer extent + tileExtent = tileGrid.getTileCoordExtent(tile.tileCoord, tmpExtent); + if (!intersects(tileExtent, layerState.extent)) { + continue; + } + } + tileState = tile.getState(); + drawable = tileState == TileState.LOADED || + tileState == TileState.EMPTY || + tileState == TileState.ERROR && !useInterimTilesOnError; + if (!drawable) { + tile = tile.getInterimTile(); + } + tileState = tile.getState(); + if (tileState == TileState.LOADED) { + if (mapRenderer.isTileTextureLoaded(tile)) { + tilesToDrawByZ[z][tile.tileCoord.toString()] = tile; + continue; + } + } else if (tileState == TileState.EMPTY || + (tileState == TileState.ERROR && + !useInterimTilesOnError)) { + continue; + } + + allTilesLoaded = false; + fullyLoaded = tileGrid.forEachTileCoordParentTileRange( + tile.tileCoord, findLoadedTiles, null, tmpTileRange, tmpExtent); + if (!fullyLoaded) { + childTileRange = tileGrid.getTileCoordChildTileRange( + tile.tileCoord, tmpTileRange, tmpExtent); + if (childTileRange) { + findLoadedTiles(z + 1, childTileRange); + } + } + + } + + } + + /** @type {Array.} */ + const zs = Object.keys(tilesToDrawByZ).map(Number); + zs.sort(numberSafeCompareFunction); + const u_tileOffset = new Float32Array(4); + for (let i = 0, ii = zs.length; i < ii; ++i) { + const tilesToDraw = tilesToDrawByZ[zs[i]]; + for (const tileKey in tilesToDraw) { + tile = tilesToDraw[tileKey]; + tileExtent = tileGrid.getTileCoordExtent(tile.tileCoord, tmpExtent); + u_tileOffset[0] = 2 * (tileExtent[2] - tileExtent[0]) / + framebufferExtentDimension; + u_tileOffset[1] = 2 * (tileExtent[3] - tileExtent[1]) / + framebufferExtentDimension; + u_tileOffset[2] = 2 * (tileExtent[0] - framebufferExtent[0]) / + framebufferExtentDimension - 1; + u_tileOffset[3] = 2 * (tileExtent[1] - framebufferExtent[1]) / + framebufferExtentDimension - 1; + gl.uniform4fv(this.locations_.u_tileOffset, u_tileOffset); + mapRenderer.bindTileTexture(tile, tilePixelSize, + tileGutter * pixelRatio, LINEAR, LINEAR); + gl.drawArrays(TRIANGLE_STRIP, 0, 4); + } + } + + if (allTilesLoaded) { + this.renderedTileRange_ = tileRange; + this.renderedFramebufferExtent_ = framebufferExtent; + this.renderedRevision_ = tileSource.getRevision(); + } else { + this.renderedTileRange_ = null; + this.renderedFramebufferExtent_ = null; + this.renderedRevision_ = -1; + frameState.animate = true; + } + + } + + this.updateUsedTiles(frameState.usedTiles, tileSource, z, tileRange); + const tileTextureQueue = mapRenderer.getTileTextureQueue(); + this.manageTilePyramid( + frameState, tileSource, tileGrid, pixelRatio, projection, extent, z, + tileLayer.getPreload(), + /** + * @param {module:ol/Tile} tile Tile. + */ + function(tile) { + if (tile.getState() == TileState.LOADED && + !mapRenderer.isTileTextureLoaded(tile) && + !tileTextureQueue.isKeyQueued(tile.getKey())) { + tileTextureQueue.enqueue([ + tile, + tileGrid.getTileCoordCenter(tile.tileCoord), + tileGrid.getResolution(tile.tileCoord[0]), + tilePixelSize, tileGutter * pixelRatio + ]); + } + }, this); + this.scheduleExpireCache(frameState, tileSource); + + const texCoordMatrix = this.texCoordMatrix; + resetTransform(texCoordMatrix); + translateTransform(texCoordMatrix, + (Math.round(center[0] / tileResolution) * tileResolution - framebufferExtent[0]) / + (framebufferExtent[2] - framebufferExtent[0]), + (Math.round(center[1] / tileResolution) * tileResolution - framebufferExtent[1]) / + (framebufferExtent[3] - framebufferExtent[1])); + if (viewState.rotation !== 0) { + rotateTransform(texCoordMatrix, viewState.rotation); + } + scaleTransform(texCoordMatrix, + frameState.size[0] * viewState.resolution / + (framebufferExtent[2] - framebufferExtent[0]), + frameState.size[1] * viewState.resolution / + (framebufferExtent[3] - framebufferExtent[1])); + translateTransform(texCoordMatrix, -0.5, -0.5); + + return true; + } /** - * @private - * @type {module:ol/TileRange} + * @inheritDoc */ - this.renderedTileRange_ = null; + forEachLayerAtPixel(pixel, frameState, callback, thisArg) { + if (!this.framebuffer) { + return undefined; + } - /** - * @private - * @type {module:ol/extent~Extent} - */ - this.renderedFramebufferExtent_ = null; + const pixelOnMapScaled = [ + pixel[0] / frameState.size[0], + (frameState.size[1] - pixel[1]) / frameState.size[1]]; - /** - * @private - * @type {number} - */ - this.renderedRevision_ = -1; + const pixelOnFrameBufferScaled = applyTransform( + this.texCoordMatrix, pixelOnMapScaled.slice()); + const pixelOnFrameBuffer = [ + pixelOnFrameBufferScaled[0] * this.framebufferDimension, + pixelOnFrameBufferScaled[1] * this.framebufferDimension]; - /** - * @private - * @type {module:ol/size~Size} - */ - this.tmpSize_ = [0, 0]; + const gl = this.mapRenderer.getContext().getGL(); + gl.bindFramebuffer(gl.FRAMEBUFFER, this.framebuffer); + const imageData = new Uint8Array(4); + gl.readPixels(pixelOnFrameBuffer[0], pixelOnFrameBuffer[1], 1, 1, + gl.RGBA, gl.UNSIGNED_BYTE, imageData); -}; + if (imageData[3] > 0) { + return callback.call(thisArg, this.getLayer(), imageData); + } else { + return undefined; + } + } +} inherits(WebGLTileLayerRenderer, WebGLLayerRenderer); @@ -118,296 +409,4 @@ WebGLTileLayerRenderer['create'] = function(mapRenderer, layer) { }; -/** - * @inheritDoc - */ -WebGLTileLayerRenderer.prototype.disposeInternal = function() { - const context = this.mapRenderer.getContext(); - context.deleteBuffer(this.renderArrayBuffer_); - WebGLLayerRenderer.prototype.disposeInternal.call(this); -}; - - -/** - * @inheritDoc - */ -WebGLTileLayerRenderer.prototype.createLoadedTileFinder = function(source, projection, tiles) { - const mapRenderer = this.mapRenderer; - - return ( - /** - * @param {number} zoom Zoom level. - * @param {module:ol/TileRange} tileRange Tile range. - * @return {boolean} The tile range is fully loaded. - */ - function(zoom, tileRange) { - function callback(tile) { - const loaded = mapRenderer.isTileTextureLoaded(tile); - if (loaded) { - if (!tiles[zoom]) { - tiles[zoom] = {}; - } - tiles[zoom][tile.tileCoord.toString()] = tile; - } - return loaded; - } - return source.forEachLoadedTile(projection, zoom, tileRange, callback); - } - ); -}; - - -/** - * @inheritDoc - */ -WebGLTileLayerRenderer.prototype.handleWebGLContextLost = function() { - WebGLLayerRenderer.prototype.handleWebGLContextLost.call(this); - this.locations_ = null; -}; - - -/** - * @inheritDoc - */ -WebGLTileLayerRenderer.prototype.prepareFrame = function(frameState, layerState, context) { - - const mapRenderer = this.mapRenderer; - const gl = context.getGL(); - - const viewState = frameState.viewState; - const projection = viewState.projection; - - const tileLayer = /** @type {module:ol/layer/Tile} */ (this.getLayer()); - const tileSource = tileLayer.getSource(); - const tileGrid = tileSource.getTileGridForProjection(projection); - const z = tileGrid.getZForResolution(viewState.resolution); - const tileResolution = tileGrid.getResolution(z); - - const tilePixelSize = - tileSource.getTilePixelSize(z, frameState.pixelRatio, projection); - const pixelRatio = tilePixelSize[0] / - toSize(tileGrid.getTileSize(z), this.tmpSize_)[0]; - const tilePixelResolution = tileResolution / pixelRatio; - const tileGutter = tileSource.getTilePixelRatio(pixelRatio) * tileSource.getGutter(projection); - - const center = viewState.center; - const extent = frameState.extent; - const tileRange = tileGrid.getTileRangeForExtentAndZ(extent, z); - - let framebufferExtent; - if (this.renderedTileRange_ && - this.renderedTileRange_.equals(tileRange) && - this.renderedRevision_ == tileSource.getRevision()) { - framebufferExtent = this.renderedFramebufferExtent_; - } else { - - const tileRangeSize = tileRange.getSize(); - - const maxDimension = Math.max( - tileRangeSize[0] * tilePixelSize[0], - tileRangeSize[1] * tilePixelSize[1]); - const framebufferDimension = roundUpToPowerOfTwo(maxDimension); - const framebufferExtentDimension = tilePixelResolution * framebufferDimension; - const origin = tileGrid.getOrigin(z); - const minX = origin[0] + - tileRange.minX * tilePixelSize[0] * tilePixelResolution; - const minY = origin[1] + - tileRange.minY * tilePixelSize[1] * tilePixelResolution; - framebufferExtent = [ - minX, minY, - minX + framebufferExtentDimension, minY + framebufferExtentDimension - ]; - - this.bindFramebuffer(frameState, framebufferDimension); - gl.viewport(0, 0, framebufferDimension, framebufferDimension); - - gl.clearColor(0, 0, 0, 0); - gl.clear(COLOR_BUFFER_BIT); - gl.disable(BLEND); - - const program = context.getProgram(this.fragmentShader_, this.vertexShader_); - context.useProgram(program); - if (!this.locations_) { - this.locations_ = new Locations(gl, program); - } - - context.bindBuffer(ARRAY_BUFFER, this.renderArrayBuffer_); - gl.enableVertexAttribArray(this.locations_.a_position); - gl.vertexAttribPointer( - this.locations_.a_position, 2, FLOAT, false, 16, 0); - gl.enableVertexAttribArray(this.locations_.a_texCoord); - gl.vertexAttribPointer( - this.locations_.a_texCoord, 2, FLOAT, false, 16, 8); - gl.uniform1i(this.locations_.u_texture, 0); - - /** - * @type {Object.>} - */ - const tilesToDrawByZ = {}; - tilesToDrawByZ[z] = {}; - - const findLoadedTiles = this.createLoadedTileFinder( - tileSource, projection, tilesToDrawByZ); - - const useInterimTilesOnError = tileLayer.getUseInterimTilesOnError(); - let allTilesLoaded = true; - const tmpExtent = createEmpty(); - const tmpTileRange = new TileRange(0, 0, 0, 0); - let childTileRange, drawable, fullyLoaded, tile, tileState; - let x, y, tileExtent; - for (x = tileRange.minX; x <= tileRange.maxX; ++x) { - for (y = tileRange.minY; y <= tileRange.maxY; ++y) { - - tile = tileSource.getTile(z, x, y, pixelRatio, projection); - if (layerState.extent !== undefined) { - // ignore tiles outside layer extent - tileExtent = tileGrid.getTileCoordExtent(tile.tileCoord, tmpExtent); - if (!intersects(tileExtent, layerState.extent)) { - continue; - } - } - tileState = tile.getState(); - drawable = tileState == TileState.LOADED || - tileState == TileState.EMPTY || - tileState == TileState.ERROR && !useInterimTilesOnError; - if (!drawable) { - tile = tile.getInterimTile(); - } - tileState = tile.getState(); - if (tileState == TileState.LOADED) { - if (mapRenderer.isTileTextureLoaded(tile)) { - tilesToDrawByZ[z][tile.tileCoord.toString()] = tile; - continue; - } - } else if (tileState == TileState.EMPTY || - (tileState == TileState.ERROR && - !useInterimTilesOnError)) { - continue; - } - - allTilesLoaded = false; - fullyLoaded = tileGrid.forEachTileCoordParentTileRange( - tile.tileCoord, findLoadedTiles, null, tmpTileRange, tmpExtent); - if (!fullyLoaded) { - childTileRange = tileGrid.getTileCoordChildTileRange( - tile.tileCoord, tmpTileRange, tmpExtent); - if (childTileRange) { - findLoadedTiles(z + 1, childTileRange); - } - } - - } - - } - - /** @type {Array.} */ - const zs = Object.keys(tilesToDrawByZ).map(Number); - zs.sort(numberSafeCompareFunction); - const u_tileOffset = new Float32Array(4); - for (let i = 0, ii = zs.length; i < ii; ++i) { - const tilesToDraw = tilesToDrawByZ[zs[i]]; - for (const tileKey in tilesToDraw) { - tile = tilesToDraw[tileKey]; - tileExtent = tileGrid.getTileCoordExtent(tile.tileCoord, tmpExtent); - u_tileOffset[0] = 2 * (tileExtent[2] - tileExtent[0]) / - framebufferExtentDimension; - u_tileOffset[1] = 2 * (tileExtent[3] - tileExtent[1]) / - framebufferExtentDimension; - u_tileOffset[2] = 2 * (tileExtent[0] - framebufferExtent[0]) / - framebufferExtentDimension - 1; - u_tileOffset[3] = 2 * (tileExtent[1] - framebufferExtent[1]) / - framebufferExtentDimension - 1; - gl.uniform4fv(this.locations_.u_tileOffset, u_tileOffset); - mapRenderer.bindTileTexture(tile, tilePixelSize, - tileGutter * pixelRatio, LINEAR, LINEAR); - gl.drawArrays(TRIANGLE_STRIP, 0, 4); - } - } - - if (allTilesLoaded) { - this.renderedTileRange_ = tileRange; - this.renderedFramebufferExtent_ = framebufferExtent; - this.renderedRevision_ = tileSource.getRevision(); - } else { - this.renderedTileRange_ = null; - this.renderedFramebufferExtent_ = null; - this.renderedRevision_ = -1; - frameState.animate = true; - } - - } - - this.updateUsedTiles(frameState.usedTiles, tileSource, z, tileRange); - const tileTextureQueue = mapRenderer.getTileTextureQueue(); - this.manageTilePyramid( - frameState, tileSource, tileGrid, pixelRatio, projection, extent, z, - tileLayer.getPreload(), - /** - * @param {module:ol/Tile} tile Tile. - */ - function(tile) { - if (tile.getState() == TileState.LOADED && - !mapRenderer.isTileTextureLoaded(tile) && - !tileTextureQueue.isKeyQueued(tile.getKey())) { - tileTextureQueue.enqueue([ - tile, - tileGrid.getTileCoordCenter(tile.tileCoord), - tileGrid.getResolution(tile.tileCoord[0]), - tilePixelSize, tileGutter * pixelRatio - ]); - } - }, this); - this.scheduleExpireCache(frameState, tileSource); - - const texCoordMatrix = this.texCoordMatrix; - resetTransform(texCoordMatrix); - translateTransform(texCoordMatrix, - (Math.round(center[0] / tileResolution) * tileResolution - framebufferExtent[0]) / - (framebufferExtent[2] - framebufferExtent[0]), - (Math.round(center[1] / tileResolution) * tileResolution - framebufferExtent[1]) / - (framebufferExtent[3] - framebufferExtent[1])); - if (viewState.rotation !== 0) { - rotateTransform(texCoordMatrix, viewState.rotation); - } - scaleTransform(texCoordMatrix, - frameState.size[0] * viewState.resolution / - (framebufferExtent[2] - framebufferExtent[0]), - frameState.size[1] * viewState.resolution / - (framebufferExtent[3] - framebufferExtent[1])); - translateTransform(texCoordMatrix, -0.5, -0.5); - - return true; -}; - - -/** - * @inheritDoc - */ -WebGLTileLayerRenderer.prototype.forEachLayerAtPixel = function(pixel, frameState, callback, thisArg) { - if (!this.framebuffer) { - return undefined; - } - - const pixelOnMapScaled = [ - pixel[0] / frameState.size[0], - (frameState.size[1] - pixel[1]) / frameState.size[1]]; - - const pixelOnFrameBufferScaled = applyTransform( - this.texCoordMatrix, pixelOnMapScaled.slice()); - const pixelOnFrameBuffer = [ - pixelOnFrameBufferScaled[0] * this.framebufferDimension, - pixelOnFrameBufferScaled[1] * this.framebufferDimension]; - - const gl = this.mapRenderer.getContext().getGL(); - gl.bindFramebuffer(gl.FRAMEBUFFER, this.framebuffer); - const imageData = new Uint8Array(4); - gl.readPixels(pixelOnFrameBuffer[0], pixelOnFrameBuffer[1], 1, 1, - gl.RGBA, gl.UNSIGNED_BYTE, imageData); - - if (imageData[3] > 0) { - return callback.call(thisArg, this.getLayer(), imageData); - } else { - return undefined; - } -}; export default WebGLTileLayerRenderer; diff --git a/src/ol/renderer/webgl/VectorLayer.js b/src/ol/renderer/webgl/VectorLayer.js index bf4a5d2476..02016204b3 100644 --- a/src/ol/renderer/webgl/VectorLayer.js +++ b/src/ol/renderer/webgl/VectorLayer.js @@ -17,54 +17,287 @@ import {apply as applyTransform} from '../../transform.js'; * @param {module:ol/layer/Vector} vectorLayer Vector layer. * @api */ -const WebGLVectorLayerRenderer = function(mapRenderer, vectorLayer) { +class WebGLVectorLayerRenderer { + constructor(mapRenderer, vectorLayer) { - WebGLLayerRenderer.call(this, mapRenderer, vectorLayer); + WebGLLayerRenderer.call(this, mapRenderer, vectorLayer); + + /** + * @private + * @type {boolean} + */ + this.dirty_ = false; + + /** + * @private + * @type {number} + */ + this.renderedRevision_ = -1; + + /** + * @private + * @type {number} + */ + this.renderedResolution_ = NaN; + + /** + * @private + * @type {module:ol/extent~Extent} + */ + this.renderedExtent_ = createEmpty(); + + /** + * @private + * @type {function(module:ol/Feature, module:ol/Feature): number|null} + */ + this.renderedRenderOrder_ = null; + + /** + * @private + * @type {module:ol/render/webgl/ReplayGroup} + */ + this.replayGroup_ = null; + + /** + * The last layer state. + * @private + * @type {?module:ol/layer/Layer~State} + */ + this.layerState_ = null; + + } /** - * @private - * @type {boolean} + * @inheritDoc */ - this.dirty_ = false; + composeFrame(frameState, layerState, context) { + this.layerState_ = layerState; + const viewState = frameState.viewState; + const replayGroup = this.replayGroup_; + const size = frameState.size; + const pixelRatio = frameState.pixelRatio; + const gl = this.mapRenderer.getGL(); + if (replayGroup && !replayGroup.isEmpty()) { + gl.enable(gl.SCISSOR_TEST); + gl.scissor(0, 0, size[0] * pixelRatio, size[1] * pixelRatio); + replayGroup.replay(context, + viewState.center, viewState.resolution, viewState.rotation, + size, pixelRatio, layerState.opacity, + layerState.managed ? frameState.skippedFeatureUids : {}); + gl.disable(gl.SCISSOR_TEST); + } + + } /** - * @private - * @type {number} + * @inheritDoc */ - this.renderedRevision_ = -1; + disposeInternal() { + const replayGroup = this.replayGroup_; + if (replayGroup) { + const context = this.mapRenderer.getContext(); + replayGroup.getDeleteResourcesFunction(context)(); + this.replayGroup_ = null; + } + WebGLLayerRenderer.prototype.disposeInternal.call(this); + } /** - * @private - * @type {number} + * @inheritDoc */ - this.renderedResolution_ = NaN; + forEachFeatureAtCoordinate(coordinate, frameState, hitTolerance, callback, thisArg) { + if (!this.replayGroup_ || !this.layerState_) { + return undefined; + } else { + const context = this.mapRenderer.getContext(); + const viewState = frameState.viewState; + const layer = this.getLayer(); + const layerState = this.layerState_; + /** @type {!Object.} */ + const features = {}; + return this.replayGroup_.forEachFeatureAtCoordinate(coordinate, + context, viewState.center, viewState.resolution, viewState.rotation, + frameState.size, frameState.pixelRatio, layerState.opacity, + {}, + /** + * @param {module:ol/Feature|module:ol/render/Feature} feature Feature. + * @return {?} Callback result. + */ + function(feature) { + const key = getUid(feature).toString(); + if (!(key in features)) { + features[key] = true; + return callback.call(thisArg, feature, layer); + } + }); + } + } /** - * @private - * @type {module:ol/extent~Extent} + * @inheritDoc */ - this.renderedExtent_ = createEmpty(); + hasFeatureAtCoordinate(coordinate, frameState) { + if (!this.replayGroup_ || !this.layerState_) { + return false; + } else { + const context = this.mapRenderer.getContext(); + const viewState = frameState.viewState; + const layerState = this.layerState_; + return this.replayGroup_.hasFeatureAtCoordinate(coordinate, + context, viewState.center, viewState.resolution, viewState.rotation, + frameState.size, frameState.pixelRatio, layerState.opacity, + frameState.skippedFeatureUids); + } + } /** - * @private - * @type {function(module:ol/Feature, module:ol/Feature): number|null} + * @inheritDoc */ - this.renderedRenderOrder_ = null; + forEachLayerAtPixel(pixel, frameState, callback, thisArg) { + const coordinate = applyTransform( + frameState.pixelToCoordinateTransform, pixel.slice()); + const hasFeature = this.hasFeatureAtCoordinate(coordinate, frameState); + + if (hasFeature) { + return callback.call(thisArg, this.getLayer(), null); + } else { + return undefined; + } + } /** + * Handle changes in image style state. + * @param {module:ol/events/Event} event Image style change event. * @private - * @type {module:ol/render/webgl/ReplayGroup} */ - this.replayGroup_ = null; + handleStyleImageChange_(event) { + this.renderIfReadyAndVisible(); + } /** - * The last layer state. - * @private - * @type {?module:ol/layer/Layer~State} + * @inheritDoc */ - this.layerState_ = null; + prepareFrame(frameState, layerState, context) { + const vectorLayer = /** @type {module:ol/layer/Vector} */ (this.getLayer()); + const vectorSource = vectorLayer.getSource(); -}; + const animating = frameState.viewHints[ViewHint.ANIMATING]; + const interacting = frameState.viewHints[ViewHint.INTERACTING]; + const updateWhileAnimating = vectorLayer.getUpdateWhileAnimating(); + const updateWhileInteracting = vectorLayer.getUpdateWhileInteracting(); + + if (!this.dirty_ && (!updateWhileAnimating && animating) || + (!updateWhileInteracting && interacting)) { + return true; + } + + const frameStateExtent = frameState.extent; + const viewState = frameState.viewState; + const projection = viewState.projection; + const resolution = viewState.resolution; + const pixelRatio = frameState.pixelRatio; + const vectorLayerRevision = vectorLayer.getRevision(); + const vectorLayerRenderBuffer = vectorLayer.getRenderBuffer(); + let vectorLayerRenderOrder = vectorLayer.getRenderOrder(); + + if (vectorLayerRenderOrder === undefined) { + vectorLayerRenderOrder = defaultRenderOrder; + } + + const extent = buffer(frameStateExtent, + vectorLayerRenderBuffer * resolution); + + if (!this.dirty_ && + this.renderedResolution_ == resolution && + this.renderedRevision_ == vectorLayerRevision && + this.renderedRenderOrder_ == vectorLayerRenderOrder && + containsExtent(this.renderedExtent_, extent)) { + return true; + } + + if (this.replayGroup_) { + frameState.postRenderFunctions.push( + this.replayGroup_.getDeleteResourcesFunction(context)); + } + + this.dirty_ = false; + + const replayGroup = new WebGLReplayGroup( + getRenderTolerance(resolution, pixelRatio), + extent, vectorLayer.getRenderBuffer()); + vectorSource.loadFeatures(extent, resolution, projection); + /** + * @param {module:ol/Feature} feature Feature. + * @this {module:ol/renderer/webgl/VectorLayer} + */ + const render = function(feature) { + let styles; + const styleFunction = feature.getStyleFunction() || vectorLayer.getStyleFunction(); + if (styleFunction) { + styles = styleFunction(feature, resolution); + } + if (styles) { + const dirty = this.renderFeature( + feature, resolution, pixelRatio, styles, replayGroup); + this.dirty_ = this.dirty_ || dirty; + } + }; + if (vectorLayerRenderOrder) { + /** @type {Array.} */ + const features = []; + vectorSource.forEachFeatureInExtent(extent, + /** + * @param {module:ol/Feature} feature Feature. + */ + function(feature) { + features.push(feature); + }, this); + features.sort(vectorLayerRenderOrder); + features.forEach(render.bind(this)); + } else { + vectorSource.forEachFeatureInExtent(extent, render, this); + } + replayGroup.finish(context); + + this.renderedResolution_ = resolution; + this.renderedRevision_ = vectorLayerRevision; + this.renderedRenderOrder_ = vectorLayerRenderOrder; + this.renderedExtent_ = extent; + this.replayGroup_ = replayGroup; + + return true; + } + + /** + * @param {module:ol/Feature} feature Feature. + * @param {number} resolution Resolution. + * @param {number} pixelRatio Pixel ratio. + * @param {(module:ol/style/Style|Array.)} styles The style or array of + * styles. + * @param {module:ol/render/webgl/ReplayGroup} replayGroup Replay group. + * @return {boolean} `true` if an image is loading. + */ + renderFeature(feature, resolution, pixelRatio, styles, replayGroup) { + if (!styles) { + return false; + } + let loading = false; + if (Array.isArray(styles)) { + for (let i = styles.length - 1, ii = 0; i >= ii; --i) { + loading = renderFeature( + replayGroup, feature, styles[i], + getSquaredRenderTolerance(resolution, pixelRatio), + this.handleStyleImageChange_, this) || loading; + } + } else { + loading = renderFeature( + replayGroup, feature, styles, + getSquaredRenderTolerance(resolution, pixelRatio), + this.handleStyleImageChange_, this) || loading; + } + return loading; + } +} inherits(WebGLVectorLayerRenderer, WebGLLayerRenderer); @@ -93,241 +326,4 @@ WebGLVectorLayerRenderer['create'] = function(mapRenderer, layer) { }; -/** - * @inheritDoc - */ -WebGLVectorLayerRenderer.prototype.composeFrame = function(frameState, layerState, context) { - this.layerState_ = layerState; - const viewState = frameState.viewState; - const replayGroup = this.replayGroup_; - const size = frameState.size; - const pixelRatio = frameState.pixelRatio; - const gl = this.mapRenderer.getGL(); - if (replayGroup && !replayGroup.isEmpty()) { - gl.enable(gl.SCISSOR_TEST); - gl.scissor(0, 0, size[0] * pixelRatio, size[1] * pixelRatio); - replayGroup.replay(context, - viewState.center, viewState.resolution, viewState.rotation, - size, pixelRatio, layerState.opacity, - layerState.managed ? frameState.skippedFeatureUids : {}); - gl.disable(gl.SCISSOR_TEST); - } - -}; - - -/** - * @inheritDoc - */ -WebGLVectorLayerRenderer.prototype.disposeInternal = function() { - const replayGroup = this.replayGroup_; - if (replayGroup) { - const context = this.mapRenderer.getContext(); - replayGroup.getDeleteResourcesFunction(context)(); - this.replayGroup_ = null; - } - WebGLLayerRenderer.prototype.disposeInternal.call(this); -}; - - -/** - * @inheritDoc - */ -WebGLVectorLayerRenderer.prototype.forEachFeatureAtCoordinate = function(coordinate, frameState, hitTolerance, callback, thisArg) { - if (!this.replayGroup_ || !this.layerState_) { - return undefined; - } else { - const context = this.mapRenderer.getContext(); - const viewState = frameState.viewState; - const layer = this.getLayer(); - const layerState = this.layerState_; - /** @type {!Object.} */ - const features = {}; - return this.replayGroup_.forEachFeatureAtCoordinate(coordinate, - context, viewState.center, viewState.resolution, viewState.rotation, - frameState.size, frameState.pixelRatio, layerState.opacity, - {}, - /** - * @param {module:ol/Feature|module:ol/render/Feature} feature Feature. - * @return {?} Callback result. - */ - function(feature) { - const key = getUid(feature).toString(); - if (!(key in features)) { - features[key] = true; - return callback.call(thisArg, feature, layer); - } - }); - } -}; - - -/** - * @inheritDoc - */ -WebGLVectorLayerRenderer.prototype.hasFeatureAtCoordinate = function(coordinate, frameState) { - if (!this.replayGroup_ || !this.layerState_) { - return false; - } else { - const context = this.mapRenderer.getContext(); - const viewState = frameState.viewState; - const layerState = this.layerState_; - return this.replayGroup_.hasFeatureAtCoordinate(coordinate, - context, viewState.center, viewState.resolution, viewState.rotation, - frameState.size, frameState.pixelRatio, layerState.opacity, - frameState.skippedFeatureUids); - } -}; - - -/** - * @inheritDoc - */ -WebGLVectorLayerRenderer.prototype.forEachLayerAtPixel = function(pixel, frameState, callback, thisArg) { - const coordinate = applyTransform( - frameState.pixelToCoordinateTransform, pixel.slice()); - const hasFeature = this.hasFeatureAtCoordinate(coordinate, frameState); - - if (hasFeature) { - return callback.call(thisArg, this.getLayer(), null); - } else { - return undefined; - } -}; - - -/** - * Handle changes in image style state. - * @param {module:ol/events/Event} event Image style change event. - * @private - */ -WebGLVectorLayerRenderer.prototype.handleStyleImageChange_ = function(event) { - this.renderIfReadyAndVisible(); -}; - - -/** - * @inheritDoc - */ -WebGLVectorLayerRenderer.prototype.prepareFrame = function(frameState, layerState, context) { - const vectorLayer = /** @type {module:ol/layer/Vector} */ (this.getLayer()); - const vectorSource = vectorLayer.getSource(); - - const animating = frameState.viewHints[ViewHint.ANIMATING]; - const interacting = frameState.viewHints[ViewHint.INTERACTING]; - const updateWhileAnimating = vectorLayer.getUpdateWhileAnimating(); - const updateWhileInteracting = vectorLayer.getUpdateWhileInteracting(); - - if (!this.dirty_ && (!updateWhileAnimating && animating) || - (!updateWhileInteracting && interacting)) { - return true; - } - - const frameStateExtent = frameState.extent; - const viewState = frameState.viewState; - const projection = viewState.projection; - const resolution = viewState.resolution; - const pixelRatio = frameState.pixelRatio; - const vectorLayerRevision = vectorLayer.getRevision(); - const vectorLayerRenderBuffer = vectorLayer.getRenderBuffer(); - let vectorLayerRenderOrder = vectorLayer.getRenderOrder(); - - if (vectorLayerRenderOrder === undefined) { - vectorLayerRenderOrder = defaultRenderOrder; - } - - const extent = buffer(frameStateExtent, - vectorLayerRenderBuffer * resolution); - - if (!this.dirty_ && - this.renderedResolution_ == resolution && - this.renderedRevision_ == vectorLayerRevision && - this.renderedRenderOrder_ == vectorLayerRenderOrder && - containsExtent(this.renderedExtent_, extent)) { - return true; - } - - if (this.replayGroup_) { - frameState.postRenderFunctions.push( - this.replayGroup_.getDeleteResourcesFunction(context)); - } - - this.dirty_ = false; - - const replayGroup = new WebGLReplayGroup( - getRenderTolerance(resolution, pixelRatio), - extent, vectorLayer.getRenderBuffer()); - vectorSource.loadFeatures(extent, resolution, projection); - /** - * @param {module:ol/Feature} feature Feature. - * @this {module:ol/renderer/webgl/VectorLayer} - */ - const render = function(feature) { - let styles; - const styleFunction = feature.getStyleFunction() || vectorLayer.getStyleFunction(); - if (styleFunction) { - styles = styleFunction(feature, resolution); - } - if (styles) { - const dirty = this.renderFeature( - feature, resolution, pixelRatio, styles, replayGroup); - this.dirty_ = this.dirty_ || dirty; - } - }; - if (vectorLayerRenderOrder) { - /** @type {Array.} */ - const features = []; - vectorSource.forEachFeatureInExtent(extent, - /** - * @param {module:ol/Feature} feature Feature. - */ - function(feature) { - features.push(feature); - }, this); - features.sort(vectorLayerRenderOrder); - features.forEach(render.bind(this)); - } else { - vectorSource.forEachFeatureInExtent(extent, render, this); - } - replayGroup.finish(context); - - this.renderedResolution_ = resolution; - this.renderedRevision_ = vectorLayerRevision; - this.renderedRenderOrder_ = vectorLayerRenderOrder; - this.renderedExtent_ = extent; - this.replayGroup_ = replayGroup; - - return true; -}; - - -/** - * @param {module:ol/Feature} feature Feature. - * @param {number} resolution Resolution. - * @param {number} pixelRatio Pixel ratio. - * @param {(module:ol/style/Style|Array.)} styles The style or array of - * styles. - * @param {module:ol/render/webgl/ReplayGroup} replayGroup Replay group. - * @return {boolean} `true` if an image is loading. - */ -WebGLVectorLayerRenderer.prototype.renderFeature = function(feature, resolution, pixelRatio, styles, replayGroup) { - if (!styles) { - return false; - } - let loading = false; - if (Array.isArray(styles)) { - for (let i = styles.length - 1, ii = 0; i >= ii; --i) { - loading = renderFeature( - replayGroup, feature, styles[i], - getSquaredRenderTolerance(resolution, pixelRatio), - this.handleStyleImageChange_, this) || loading; - } - } else { - loading = renderFeature( - replayGroup, feature, styles, - getSquaredRenderTolerance(resolution, pixelRatio), - this.handleStyleImageChange_, this) || loading; - } - return loading; -}; export default WebGLVectorLayerRenderer; diff --git a/src/ol/reproj/Image.js b/src/ol/reproj/Image.js index f73ad9915f..4fc11ccec8 100644 --- a/src/ol/reproj/Image.js +++ b/src/ol/reproj/Image.js @@ -32,171 +32,175 @@ import Triangulation from '../reproj/Triangulation.js'; * @param {module:ol/reproj/Image~FunctionType} getImageFunction * Function returning source images (extent, resolution, pixelRatio). */ -const ReprojImage = function(sourceProj, targetProj, - targetExtent, targetResolution, pixelRatio, getImageFunction) { +class ReprojImage { + constructor( + sourceProj, + targetProj, + targetExtent, + targetResolution, + pixelRatio, + getImageFunction + ) { - /** - * @private - * @type {module:ol/proj/Projection} - */ - this.targetProj_ = targetProj; + /** + * @private + * @type {module:ol/proj/Projection} + */ + this.targetProj_ = targetProj; - /** - * @private - * @type {module:ol/extent~Extent} - */ - this.maxSourceExtent_ = sourceProj.getExtent(); - const maxTargetExtent = targetProj.getExtent(); + /** + * @private + * @type {module:ol/extent~Extent} + */ + this.maxSourceExtent_ = sourceProj.getExtent(); + const maxTargetExtent = targetProj.getExtent(); - const limitedTargetExtent = maxTargetExtent ? - getIntersection(targetExtent, maxTargetExtent) : targetExtent; + const limitedTargetExtent = maxTargetExtent ? + getIntersection(targetExtent, maxTargetExtent) : targetExtent; - const targetCenter = getCenter(limitedTargetExtent); - const sourceResolution = calculateSourceResolution( - sourceProj, targetProj, targetCenter, targetResolution); + const targetCenter = getCenter(limitedTargetExtent); + const sourceResolution = calculateSourceResolution( + sourceProj, targetProj, targetCenter, targetResolution); - const errorThresholdInPixels = ERROR_THRESHOLD; + const errorThresholdInPixels = ERROR_THRESHOLD; - /** - * @private - * @type {!module:ol/reproj/Triangulation} - */ - this.triangulation_ = new Triangulation( - sourceProj, targetProj, limitedTargetExtent, this.maxSourceExtent_, - sourceResolution * errorThresholdInPixels); + /** + * @private + * @type {!module:ol/reproj/Triangulation} + */ + this.triangulation_ = new Triangulation( + sourceProj, targetProj, limitedTargetExtent, this.maxSourceExtent_, + sourceResolution * errorThresholdInPixels); - /** - * @private - * @type {number} - */ - this.targetResolution_ = targetResolution; + /** + * @private + * @type {number} + */ + this.targetResolution_ = targetResolution; - /** - * @private - * @type {module:ol/extent~Extent} - */ - this.targetExtent_ = targetExtent; + /** + * @private + * @type {module:ol/extent~Extent} + */ + this.targetExtent_ = targetExtent; - const sourceExtent = this.triangulation_.calculateSourceExtent(); + const sourceExtent = this.triangulation_.calculateSourceExtent(); - /** - * @private - * @type {module:ol/ImageBase} - */ - this.sourceImage_ = - getImageFunction(sourceExtent, sourceResolution, pixelRatio); + /** + * @private + * @type {module:ol/ImageBase} + */ + this.sourceImage_ = + getImageFunction(sourceExtent, sourceResolution, pixelRatio); - /** - * @private - * @type {number} - */ - this.sourcePixelRatio_ = - this.sourceImage_ ? this.sourceImage_.getPixelRatio() : 1; + /** + * @private + * @type {number} + */ + this.sourcePixelRatio_ = + this.sourceImage_ ? this.sourceImage_.getPixelRatio() : 1; - /** - * @private - * @type {HTMLCanvasElement} - */ - this.canvas_ = null; + /** + * @private + * @type {HTMLCanvasElement} + */ + this.canvas_ = null; - /** - * @private - * @type {?module:ol/events~EventsKey} - */ - this.sourceListenerKey_ = null; + /** + * @private + * @type {?module:ol/events~EventsKey} + */ + this.sourceListenerKey_ = null; - let state = ImageState.LOADED; + let state = ImageState.LOADED; - if (this.sourceImage_) { - state = ImageState.IDLE; + if (this.sourceImage_) { + state = ImageState.IDLE; + } + + ImageBase.call(this, targetExtent, targetResolution, this.sourcePixelRatio_, state); } - ImageBase.call(this, targetExtent, targetResolution, this.sourcePixelRatio_, state); -}; + /** + * @inheritDoc + */ + disposeInternal() { + if (this.state == ImageState.LOADING) { + this.unlistenSource_(); + } + ImageBase.prototype.disposeInternal.call(this); + } + + /** + * @inheritDoc + */ + getImage() { + return this.canvas_; + } + + /** + * @return {module:ol/proj/Projection} Projection. + */ + getProjection() { + return this.targetProj_; + } + + /** + * @private + */ + reproject_() { + const sourceState = this.sourceImage_.getState(); + if (sourceState == ImageState.LOADED) { + const width = getWidth(this.targetExtent_) / this.targetResolution_; + const height = getHeight(this.targetExtent_) / this.targetResolution_; + + this.canvas_ = renderReprojected(width, height, this.sourcePixelRatio_, + this.sourceImage_.getResolution(), this.maxSourceExtent_, + this.targetResolution_, this.targetExtent_, this.triangulation_, [{ + extent: this.sourceImage_.getExtent(), + image: this.sourceImage_.getImage() + }], 0); + } + this.state = sourceState; + this.changed(); + } + + /** + * @inheritDoc + */ + load() { + if (this.state == ImageState.IDLE) { + this.state = ImageState.LOADING; + this.changed(); + + const sourceState = this.sourceImage_.getState(); + if (sourceState == ImageState.LOADED || sourceState == ImageState.ERROR) { + this.reproject_(); + } else { + this.sourceListenerKey_ = listen(this.sourceImage_, + EventType.CHANGE, function(e) { + const sourceState = this.sourceImage_.getState(); + if (sourceState == ImageState.LOADED || sourceState == ImageState.ERROR) { + this.unlistenSource_(); + this.reproject_(); + } + }, this); + this.sourceImage_.load(); + } + } + } + + /** + * @private + */ + unlistenSource_() { + unlistenByKey(/** @type {!module:ol/events~EventsKey} */ (this.sourceListenerKey_)); + this.sourceListenerKey_ = null; + } +} inherits(ReprojImage, ImageBase); -/** - * @inheritDoc - */ -ReprojImage.prototype.disposeInternal = function() { - if (this.state == ImageState.LOADING) { - this.unlistenSource_(); - } - ImageBase.prototype.disposeInternal.call(this); -}; - - -/** - * @inheritDoc - */ -ReprojImage.prototype.getImage = function() { - return this.canvas_; -}; - - -/** - * @return {module:ol/proj/Projection} Projection. - */ -ReprojImage.prototype.getProjection = function() { - return this.targetProj_; -}; - - -/** - * @private - */ -ReprojImage.prototype.reproject_ = function() { - const sourceState = this.sourceImage_.getState(); - if (sourceState == ImageState.LOADED) { - const width = getWidth(this.targetExtent_) / this.targetResolution_; - const height = getHeight(this.targetExtent_) / this.targetResolution_; - - this.canvas_ = renderReprojected(width, height, this.sourcePixelRatio_, - this.sourceImage_.getResolution(), this.maxSourceExtent_, - this.targetResolution_, this.targetExtent_, this.triangulation_, [{ - extent: this.sourceImage_.getExtent(), - image: this.sourceImage_.getImage() - }], 0); - } - this.state = sourceState; - this.changed(); -}; - - -/** - * @inheritDoc - */ -ReprojImage.prototype.load = function() { - if (this.state == ImageState.IDLE) { - this.state = ImageState.LOADING; - this.changed(); - - const sourceState = this.sourceImage_.getState(); - if (sourceState == ImageState.LOADED || sourceState == ImageState.ERROR) { - this.reproject_(); - } else { - this.sourceListenerKey_ = listen(this.sourceImage_, - EventType.CHANGE, function(e) { - const sourceState = this.sourceImage_.getState(); - if (sourceState == ImageState.LOADED || sourceState == ImageState.ERROR) { - this.unlistenSource_(); - this.reproject_(); - } - }, this); - this.sourceImage_.load(); - } - } -}; - - -/** - * @private - */ -ReprojImage.prototype.unlistenSource_ = function() { - unlistenByKey(/** @type {!module:ol/events~EventsKey} */ (this.sourceListenerKey_)); - this.sourceListenerKey_ = null; -}; export default ReprojImage; diff --git a/src/ol/reproj/Tile.js b/src/ol/reproj/Tile.js index 81c3bd2907..c399cabacf 100644 --- a/src/ol/reproj/Tile.js +++ b/src/ol/reproj/Tile.js @@ -38,275 +38,283 @@ import Triangulation from '../reproj/Triangulation.js'; * @param {number=} opt_errorThreshold Acceptable reprojection error (in px). * @param {boolean=} opt_renderEdges Render reprojection edges. */ -const ReprojTile = function(sourceProj, sourceTileGrid, - targetProj, targetTileGrid, tileCoord, wrappedTileCoord, - pixelRatio, gutter, getTileFunction, - opt_errorThreshold, opt_renderEdges) { - Tile.call(this, tileCoord, TileState.IDLE); +class ReprojTile { + constructor( + sourceProj, + sourceTileGrid, + targetProj, + targetTileGrid, + tileCoord, + wrappedTileCoord, + pixelRatio, + gutter, + getTileFunction, + opt_errorThreshold, + opt_renderEdges + ) { + Tile.call(this, tileCoord, TileState.IDLE); - /** - * @private - * @type {boolean} - */ - this.renderEdges_ = opt_renderEdges !== undefined ? opt_renderEdges : false; + /** + * @private + * @type {boolean} + */ + this.renderEdges_ = opt_renderEdges !== undefined ? opt_renderEdges : false; - /** - * @private - * @type {number} - */ - this.pixelRatio_ = pixelRatio; + /** + * @private + * @type {number} + */ + this.pixelRatio_ = pixelRatio; - /** - * @private - * @type {number} - */ - this.gutter_ = gutter; + /** + * @private + * @type {number} + */ + this.gutter_ = gutter; - /** - * @private - * @type {HTMLCanvasElement} - */ - this.canvas_ = null; + /** + * @private + * @type {HTMLCanvasElement} + */ + this.canvas_ = null; - /** - * @private - * @type {module:ol/tilegrid/TileGrid} - */ - this.sourceTileGrid_ = sourceTileGrid; + /** + * @private + * @type {module:ol/tilegrid/TileGrid} + */ + this.sourceTileGrid_ = sourceTileGrid; - /** - * @private - * @type {module:ol/tilegrid/TileGrid} - */ - this.targetTileGrid_ = targetTileGrid; + /** + * @private + * @type {module:ol/tilegrid/TileGrid} + */ + this.targetTileGrid_ = targetTileGrid; - /** - * @private - * @type {module:ol/tilecoord~TileCoord} - */ - this.wrappedTileCoord_ = wrappedTileCoord ? wrappedTileCoord : tileCoord; + /** + * @private + * @type {module:ol/tilecoord~TileCoord} + */ + this.wrappedTileCoord_ = wrappedTileCoord ? wrappedTileCoord : tileCoord; - /** - * @private - * @type {!Array.} - */ - this.sourceTiles_ = []; + /** + * @private + * @type {!Array.} + */ + this.sourceTiles_ = []; - /** - * @private - * @type {Array.} - */ - this.sourcesListenerKeys_ = null; + /** + * @private + * @type {Array.} + */ + this.sourcesListenerKeys_ = null; - /** - * @private - * @type {number} - */ - this.sourceZ_ = 0; + /** + * @private + * @type {number} + */ + this.sourceZ_ = 0; - const targetExtent = targetTileGrid.getTileCoordExtent(this.wrappedTileCoord_); - const maxTargetExtent = this.targetTileGrid_.getExtent(); - let maxSourceExtent = this.sourceTileGrid_.getExtent(); + const targetExtent = targetTileGrid.getTileCoordExtent(this.wrappedTileCoord_); + const maxTargetExtent = this.targetTileGrid_.getExtent(); + let maxSourceExtent = this.sourceTileGrid_.getExtent(); - const limitedTargetExtent = maxTargetExtent ? - getIntersection(targetExtent, maxTargetExtent) : targetExtent; + const limitedTargetExtent = maxTargetExtent ? + getIntersection(targetExtent, maxTargetExtent) : targetExtent; - if (getArea(limitedTargetExtent) === 0) { - // Tile is completely outside range -> EMPTY - // TODO: is it actually correct that the source even creates the tile ? - this.state = TileState.EMPTY; - return; - } - - const sourceProjExtent = sourceProj.getExtent(); - if (sourceProjExtent) { - if (!maxSourceExtent) { - maxSourceExtent = sourceProjExtent; - } else { - maxSourceExtent = getIntersection(maxSourceExtent, sourceProjExtent); + if (getArea(limitedTargetExtent) === 0) { + // Tile is completely outside range -> EMPTY + // TODO: is it actually correct that the source even creates the tile ? + this.state = TileState.EMPTY; + return; } - } - const targetResolution = targetTileGrid.getResolution( - this.wrappedTileCoord_[0]); - - const targetCenter = getCenter(limitedTargetExtent); - const sourceResolution = calculateSourceResolution( - sourceProj, targetProj, targetCenter, targetResolution); - - if (!isFinite(sourceResolution) || sourceResolution <= 0) { - // invalid sourceResolution -> EMPTY - // probably edges of the projections when no extent is defined - this.state = TileState.EMPTY; - return; - } - - const errorThresholdInPixels = opt_errorThreshold !== undefined ? - opt_errorThreshold : ERROR_THRESHOLD; - - /** - * @private - * @type {!module:ol/reproj/Triangulation} - */ - this.triangulation_ = new Triangulation( - sourceProj, targetProj, limitedTargetExtent, maxSourceExtent, - sourceResolution * errorThresholdInPixels); - - if (this.triangulation_.getTriangles().length === 0) { - // no valid triangles -> EMPTY - this.state = TileState.EMPTY; - return; - } - - this.sourceZ_ = sourceTileGrid.getZForResolution(sourceResolution); - let sourceExtent = this.triangulation_.calculateSourceExtent(); - - if (maxSourceExtent) { - if (sourceProj.canWrapX()) { - sourceExtent[1] = clamp( - sourceExtent[1], maxSourceExtent[1], maxSourceExtent[3]); - sourceExtent[3] = clamp( - sourceExtent[3], maxSourceExtent[1], maxSourceExtent[3]); - } else { - sourceExtent = getIntersection(sourceExtent, maxSourceExtent); - } - } - - if (!getArea(sourceExtent)) { - this.state = TileState.EMPTY; - } else { - const sourceRange = sourceTileGrid.getTileRangeForExtentAndZ( - sourceExtent, this.sourceZ_); - - for (let srcX = sourceRange.minX; srcX <= sourceRange.maxX; srcX++) { - for (let srcY = sourceRange.minY; srcY <= sourceRange.maxY; srcY++) { - const tile = getTileFunction(this.sourceZ_, srcX, srcY, pixelRatio); - if (tile) { - this.sourceTiles_.push(tile); - } + const sourceProjExtent = sourceProj.getExtent(); + if (sourceProjExtent) { + if (!maxSourceExtent) { + maxSourceExtent = sourceProjExtent; + } else { + maxSourceExtent = getIntersection(maxSourceExtent, sourceProjExtent); } } - if (this.sourceTiles_.length === 0) { + const targetResolution = targetTileGrid.getResolution( + this.wrappedTileCoord_[0]); + + const targetCenter = getCenter(limitedTargetExtent); + const sourceResolution = calculateSourceResolution( + sourceProj, targetProj, targetCenter, targetResolution); + + if (!isFinite(sourceResolution) || sourceResolution <= 0) { + // invalid sourceResolution -> EMPTY + // probably edges of the projections when no extent is defined this.state = TileState.EMPTY; + return; + } + + const errorThresholdInPixels = opt_errorThreshold !== undefined ? + opt_errorThreshold : ERROR_THRESHOLD; + + /** + * @private + * @type {!module:ol/reproj/Triangulation} + */ + this.triangulation_ = new Triangulation( + sourceProj, targetProj, limitedTargetExtent, maxSourceExtent, + sourceResolution * errorThresholdInPixels); + + if (this.triangulation_.getTriangles().length === 0) { + // no valid triangles -> EMPTY + this.state = TileState.EMPTY; + return; + } + + this.sourceZ_ = sourceTileGrid.getZForResolution(sourceResolution); + let sourceExtent = this.triangulation_.calculateSourceExtent(); + + if (maxSourceExtent) { + if (sourceProj.canWrapX()) { + sourceExtent[1] = clamp( + sourceExtent[1], maxSourceExtent[1], maxSourceExtent[3]); + sourceExtent[3] = clamp( + sourceExtent[3], maxSourceExtent[1], maxSourceExtent[3]); + } else { + sourceExtent = getIntersection(sourceExtent, maxSourceExtent); + } + } + + if (!getArea(sourceExtent)) { + this.state = TileState.EMPTY; + } else { + const sourceRange = sourceTileGrid.getTileRangeForExtentAndZ( + sourceExtent, this.sourceZ_); + + for (let srcX = sourceRange.minX; srcX <= sourceRange.maxX; srcX++) { + for (let srcY = sourceRange.minY; srcY <= sourceRange.maxY; srcY++) { + const tile = getTileFunction(this.sourceZ_, srcX, srcY, pixelRatio); + if (tile) { + this.sourceTiles_.push(tile); + } + } + } + + if (this.sourceTiles_.length === 0) { + this.state = TileState.EMPTY; + } } } -}; + + /** + * @inheritDoc + */ + disposeInternal() { + if (this.state == TileState.LOADING) { + this.unlistenSources_(); + } + Tile.prototype.disposeInternal.call(this); + } + + /** + * Get the HTML Canvas element for this tile. + * @return {HTMLCanvasElement} Canvas. + */ + getImage() { + return this.canvas_; + } + + /** + * @private + */ + reproject_() { + const sources = []; + this.sourceTiles_.forEach(function(tile, i, arr) { + if (tile && tile.getState() == TileState.LOADED) { + sources.push({ + extent: this.sourceTileGrid_.getTileCoordExtent(tile.tileCoord), + image: tile.getImage() + }); + } + }.bind(this)); + this.sourceTiles_.length = 0; + + if (sources.length === 0) { + this.state = TileState.ERROR; + } else { + const z = this.wrappedTileCoord_[0]; + const size = this.targetTileGrid_.getTileSize(z); + const width = typeof size === 'number' ? size : size[0]; + const height = typeof size === 'number' ? size : size[1]; + const targetResolution = this.targetTileGrid_.getResolution(z); + const sourceResolution = this.sourceTileGrid_.getResolution(this.sourceZ_); + + const targetExtent = this.targetTileGrid_.getTileCoordExtent( + this.wrappedTileCoord_); + this.canvas_ = renderReprojected(width, height, this.pixelRatio_, + sourceResolution, this.sourceTileGrid_.getExtent(), + targetResolution, targetExtent, this.triangulation_, sources, + this.gutter_, this.renderEdges_); + + this.state = TileState.LOADED; + } + this.changed(); + } + + /** + * @inheritDoc + */ + load() { + if (this.state == TileState.IDLE) { + this.state = TileState.LOADING; + this.changed(); + + let leftToLoad = 0; + + this.sourcesListenerKeys_ = []; + this.sourceTiles_.forEach(function(tile, i, arr) { + const state = tile.getState(); + if (state == TileState.IDLE || state == TileState.LOADING) { + leftToLoad++; + + const sourceListenKey = listen(tile, EventType.CHANGE, + function(e) { + const state = tile.getState(); + if (state == TileState.LOADED || + state == TileState.ERROR || + state == TileState.EMPTY) { + unlistenByKey(sourceListenKey); + leftToLoad--; + if (leftToLoad === 0) { + this.unlistenSources_(); + this.reproject_(); + } + } + }, this); + this.sourcesListenerKeys_.push(sourceListenKey); + } + }.bind(this)); + + this.sourceTiles_.forEach(function(tile, i, arr) { + const state = tile.getState(); + if (state == TileState.IDLE) { + tile.load(); + } + }); + + if (leftToLoad === 0) { + setTimeout(this.reproject_.bind(this), 0); + } + } + } + + /** + * @private + */ + unlistenSources_() { + this.sourcesListenerKeys_.forEach(unlistenByKey); + this.sourcesListenerKeys_ = null; + } +} inherits(ReprojTile, Tile); -/** - * @inheritDoc - */ -ReprojTile.prototype.disposeInternal = function() { - if (this.state == TileState.LOADING) { - this.unlistenSources_(); - } - Tile.prototype.disposeInternal.call(this); -}; - - -/** - * Get the HTML Canvas element for this tile. - * @return {HTMLCanvasElement} Canvas. - */ -ReprojTile.prototype.getImage = function() { - return this.canvas_; -}; - - -/** - * @private - */ -ReprojTile.prototype.reproject_ = function() { - const sources = []; - this.sourceTiles_.forEach(function(tile, i, arr) { - if (tile && tile.getState() == TileState.LOADED) { - sources.push({ - extent: this.sourceTileGrid_.getTileCoordExtent(tile.tileCoord), - image: tile.getImage() - }); - } - }.bind(this)); - this.sourceTiles_.length = 0; - - if (sources.length === 0) { - this.state = TileState.ERROR; - } else { - const z = this.wrappedTileCoord_[0]; - const size = this.targetTileGrid_.getTileSize(z); - const width = typeof size === 'number' ? size : size[0]; - const height = typeof size === 'number' ? size : size[1]; - const targetResolution = this.targetTileGrid_.getResolution(z); - const sourceResolution = this.sourceTileGrid_.getResolution(this.sourceZ_); - - const targetExtent = this.targetTileGrid_.getTileCoordExtent( - this.wrappedTileCoord_); - this.canvas_ = renderReprojected(width, height, this.pixelRatio_, - sourceResolution, this.sourceTileGrid_.getExtent(), - targetResolution, targetExtent, this.triangulation_, sources, - this.gutter_, this.renderEdges_); - - this.state = TileState.LOADED; - } - this.changed(); -}; - - -/** - * @inheritDoc - */ -ReprojTile.prototype.load = function() { - if (this.state == TileState.IDLE) { - this.state = TileState.LOADING; - this.changed(); - - let leftToLoad = 0; - - this.sourcesListenerKeys_ = []; - this.sourceTiles_.forEach(function(tile, i, arr) { - const state = tile.getState(); - if (state == TileState.IDLE || state == TileState.LOADING) { - leftToLoad++; - - const sourceListenKey = listen(tile, EventType.CHANGE, - function(e) { - const state = tile.getState(); - if (state == TileState.LOADED || - state == TileState.ERROR || - state == TileState.EMPTY) { - unlistenByKey(sourceListenKey); - leftToLoad--; - if (leftToLoad === 0) { - this.unlistenSources_(); - this.reproject_(); - } - } - }, this); - this.sourcesListenerKeys_.push(sourceListenKey); - } - }.bind(this)); - - this.sourceTiles_.forEach(function(tile, i, arr) { - const state = tile.getState(); - if (state == TileState.IDLE) { - tile.load(); - } - }); - - if (leftToLoad === 0) { - setTimeout(this.reproject_.bind(this), 0); - } - } -}; - - -/** - * @private - */ -ReprojTile.prototype.unlistenSources_ = function() { - this.sourcesListenerKeys_.forEach(unlistenByKey); - this.sourcesListenerKeys_ = null; -}; export default ReprojTile; diff --git a/src/ol/reproj/Triangulation.js b/src/ol/reproj/Triangulation.js index a511d0ccdb..838c653ca7 100644 --- a/src/ol/reproj/Triangulation.js +++ b/src/ol/reproj/Triangulation.js @@ -48,309 +48,305 @@ const MAX_TRIANGLE_WIDTH = 0.25; * @param {number} errorThreshold Acceptable error (in source units). * @constructor */ -const Triangulation = function(sourceProj, targetProj, targetExtent, - maxSourceExtent, errorThreshold) { +class Triangulation { + constructor(sourceProj, targetProj, targetExtent, maxSourceExtent, errorThreshold) { - /** - * @type {module:ol/proj/Projection} - * @private - */ - this.sourceProj_ = sourceProj; + /** + * @type {module:ol/proj/Projection} + * @private + */ + this.sourceProj_ = sourceProj; - /** - * @type {module:ol/proj/Projection} - * @private - */ - this.targetProj_ = targetProj; + /** + * @type {module:ol/proj/Projection} + * @private + */ + this.targetProj_ = targetProj; - /** @type {!Object.} */ - let transformInvCache = {}; - const transformInv = getTransform(this.targetProj_, this.sourceProj_); + /** @type {!Object.} */ + let transformInvCache = {}; + const transformInv = getTransform(this.targetProj_, this.sourceProj_); - /** - * @param {module:ol/coordinate~Coordinate} c A coordinate. - * @return {module:ol/coordinate~Coordinate} Transformed coordinate. - * @private - */ - this.transformInv_ = function(c) { - const key = c[0] + '/' + c[1]; - if (!transformInvCache[key]) { - transformInvCache[key] = transformInv(c); - } - return transformInvCache[key]; - }; - - /** - * @type {module:ol/extent~Extent} - * @private - */ - this.maxSourceExtent_ = maxSourceExtent; - - /** - * @type {number} - * @private - */ - this.errorThresholdSquared_ = errorThreshold * errorThreshold; - - /** - * @type {Array.} - * @private - */ - this.triangles_ = []; - - /** - * Indicates that the triangulation crosses edge of the source projection. - * @type {boolean} - * @private - */ - this.wrapsXInSource_ = false; - - /** - * @type {boolean} - * @private - */ - this.canWrapXInSource_ = this.sourceProj_.canWrapX() && - !!maxSourceExtent && - !!this.sourceProj_.getExtent() && - (getWidth(maxSourceExtent) == getWidth(this.sourceProj_.getExtent())); - - /** - * @type {?number} - * @private - */ - this.sourceWorldWidth_ = this.sourceProj_.getExtent() ? - getWidth(this.sourceProj_.getExtent()) : null; - - /** - * @type {?number} - * @private - */ - this.targetWorldWidth_ = this.targetProj_.getExtent() ? - getWidth(this.targetProj_.getExtent()) : null; - - const destinationTopLeft = getTopLeft(targetExtent); - const destinationTopRight = getTopRight(targetExtent); - const destinationBottomRight = getBottomRight(targetExtent); - const destinationBottomLeft = getBottomLeft(targetExtent); - const sourceTopLeft = this.transformInv_(destinationTopLeft); - const sourceTopRight = this.transformInv_(destinationTopRight); - const sourceBottomRight = this.transformInv_(destinationBottomRight); - const sourceBottomLeft = this.transformInv_(destinationBottomLeft); - - this.addQuad_( - destinationTopLeft, destinationTopRight, - destinationBottomRight, destinationBottomLeft, - sourceTopLeft, sourceTopRight, sourceBottomRight, sourceBottomLeft, - MAX_SUBDIVISION); - - if (this.wrapsXInSource_) { - let leftBound = Infinity; - this.triangles_.forEach(function(triangle, i, arr) { - leftBound = Math.min(leftBound, - triangle.source[0][0], triangle.source[1][0], triangle.source[2][0]); - }); - - // Shift triangles to be as close to `leftBound` as possible - // (if the distance is more than `worldWidth / 2` it can be closer. - this.triangles_.forEach(function(triangle) { - if (Math.max(triangle.source[0][0], triangle.source[1][0], - triangle.source[2][0]) - leftBound > this.sourceWorldWidth_ / 2) { - const newTriangle = [[triangle.source[0][0], triangle.source[0][1]], - [triangle.source[1][0], triangle.source[1][1]], - [triangle.source[2][0], triangle.source[2][1]]]; - if ((newTriangle[0][0] - leftBound) > this.sourceWorldWidth_ / 2) { - newTriangle[0][0] -= this.sourceWorldWidth_; - } - if ((newTriangle[1][0] - leftBound) > this.sourceWorldWidth_ / 2) { - newTriangle[1][0] -= this.sourceWorldWidth_; - } - if ((newTriangle[2][0] - leftBound) > this.sourceWorldWidth_ / 2) { - newTriangle[2][0] -= this.sourceWorldWidth_; - } - - // Rarely (if the extent contains both the dateline and prime meridian) - // the shift can in turn break some triangles. - // Detect this here and don't shift in such cases. - const minX = Math.min( - newTriangle[0][0], newTriangle[1][0], newTriangle[2][0]); - const maxX = Math.max( - newTriangle[0][0], newTriangle[1][0], newTriangle[2][0]); - if ((maxX - minX) < this.sourceWorldWidth_ / 2) { - triangle.source = newTriangle; - } + /** + * @param {module:ol/coordinate~Coordinate} c A coordinate. + * @return {module:ol/coordinate~Coordinate} Transformed coordinate. + * @private + */ + this.transformInv_ = function(c) { + const key = c[0] + '/' + c[1]; + if (!transformInvCache[key]) { + transformInvCache[key] = transformInv(c); } - }.bind(this)); + return transformInvCache[key]; + }; + + /** + * @type {module:ol/extent~Extent} + * @private + */ + this.maxSourceExtent_ = maxSourceExtent; + + /** + * @type {number} + * @private + */ + this.errorThresholdSquared_ = errorThreshold * errorThreshold; + + /** + * @type {Array.} + * @private + */ + this.triangles_ = []; + + /** + * Indicates that the triangulation crosses edge of the source projection. + * @type {boolean} + * @private + */ + this.wrapsXInSource_ = false; + + /** + * @type {boolean} + * @private + */ + this.canWrapXInSource_ = this.sourceProj_.canWrapX() && + !!maxSourceExtent && + !!this.sourceProj_.getExtent() && + (getWidth(maxSourceExtent) == getWidth(this.sourceProj_.getExtent())); + + /** + * @type {?number} + * @private + */ + this.sourceWorldWidth_ = this.sourceProj_.getExtent() ? + getWidth(this.sourceProj_.getExtent()) : null; + + /** + * @type {?number} + * @private + */ + this.targetWorldWidth_ = this.targetProj_.getExtent() ? + getWidth(this.targetProj_.getExtent()) : null; + + const destinationTopLeft = getTopLeft(targetExtent); + const destinationTopRight = getTopRight(targetExtent); + const destinationBottomRight = getBottomRight(targetExtent); + const destinationBottomLeft = getBottomLeft(targetExtent); + const sourceTopLeft = this.transformInv_(destinationTopLeft); + const sourceTopRight = this.transformInv_(destinationTopRight); + const sourceBottomRight = this.transformInv_(destinationBottomRight); + const sourceBottomLeft = this.transformInv_(destinationBottomLeft); + + this.addQuad_( + destinationTopLeft, destinationTopRight, + destinationBottomRight, destinationBottomLeft, + sourceTopLeft, sourceTopRight, sourceBottomRight, sourceBottomLeft, + MAX_SUBDIVISION); + + if (this.wrapsXInSource_) { + let leftBound = Infinity; + this.triangles_.forEach(function(triangle, i, arr) { + leftBound = Math.min(leftBound, + triangle.source[0][0], triangle.source[1][0], triangle.source[2][0]); + }); + + // Shift triangles to be as close to `leftBound` as possible + // (if the distance is more than `worldWidth / 2` it can be closer. + this.triangles_.forEach(function(triangle) { + if (Math.max(triangle.source[0][0], triangle.source[1][0], + triangle.source[2][0]) - leftBound > this.sourceWorldWidth_ / 2) { + const newTriangle = [[triangle.source[0][0], triangle.source[0][1]], + [triangle.source[1][0], triangle.source[1][1]], + [triangle.source[2][0], triangle.source[2][1]]]; + if ((newTriangle[0][0] - leftBound) > this.sourceWorldWidth_ / 2) { + newTriangle[0][0] -= this.sourceWorldWidth_; + } + if ((newTriangle[1][0] - leftBound) > this.sourceWorldWidth_ / 2) { + newTriangle[1][0] -= this.sourceWorldWidth_; + } + if ((newTriangle[2][0] - leftBound) > this.sourceWorldWidth_ / 2) { + newTriangle[2][0] -= this.sourceWorldWidth_; + } + + // Rarely (if the extent contains both the dateline and prime meridian) + // the shift can in turn break some triangles. + // Detect this here and don't shift in such cases. + const minX = Math.min( + newTriangle[0][0], newTriangle[1][0], newTriangle[2][0]); + const maxX = Math.max( + newTriangle[0][0], newTriangle[1][0], newTriangle[2][0]); + if ((maxX - minX) < this.sourceWorldWidth_ / 2) { + triangle.source = newTriangle; + } + } + }.bind(this)); + } + + transformInvCache = {}; } - transformInvCache = {}; -}; - - -/** - * Adds triangle to the triangulation. - * @param {module:ol/coordinate~Coordinate} a The target a coordinate. - * @param {module:ol/coordinate~Coordinate} b The target b coordinate. - * @param {module:ol/coordinate~Coordinate} c The target c coordinate. - * @param {module:ol/coordinate~Coordinate} aSrc The source a coordinate. - * @param {module:ol/coordinate~Coordinate} bSrc The source b coordinate. - * @param {module:ol/coordinate~Coordinate} cSrc The source c coordinate. - * @private - */ -Triangulation.prototype.addTriangle_ = function(a, b, c, - aSrc, bSrc, cSrc) { - this.triangles_.push({ - source: [aSrc, bSrc, cSrc], - target: [a, b, c] - }); -}; - - -/** - * Adds quad (points in clock-wise order) to the triangulation - * (and reprojects the vertices) if valid. - * Performs quad subdivision if needed to increase precision. - * - * @param {module:ol/coordinate~Coordinate} a The target a coordinate. - * @param {module:ol/coordinate~Coordinate} b The target b coordinate. - * @param {module:ol/coordinate~Coordinate} c The target c coordinate. - * @param {module:ol/coordinate~Coordinate} d The target d coordinate. - * @param {module:ol/coordinate~Coordinate} aSrc The source a coordinate. - * @param {module:ol/coordinate~Coordinate} bSrc The source b coordinate. - * @param {module:ol/coordinate~Coordinate} cSrc The source c coordinate. - * @param {module:ol/coordinate~Coordinate} dSrc The source d coordinate. - * @param {number} maxSubdivision Maximal allowed subdivision of the quad. - * @private - */ -Triangulation.prototype.addQuad_ = function(a, b, c, d, - aSrc, bSrc, cSrc, dSrc, maxSubdivision) { - - const sourceQuadExtent = boundingExtent([aSrc, bSrc, cSrc, dSrc]); - const sourceCoverageX = this.sourceWorldWidth_ ? - getWidth(sourceQuadExtent) / this.sourceWorldWidth_ : null; - const sourceWorldWidth = /** @type {number} */ (this.sourceWorldWidth_); - - // when the quad is wrapped in the source projection - // it covers most of the projection extent, but not fully - const wrapsX = this.sourceProj_.canWrapX() && - sourceCoverageX > 0.5 && sourceCoverageX < 1; - - let needsSubdivision = false; - - if (maxSubdivision > 0) { - if (this.targetProj_.isGlobal() && this.targetWorldWidth_) { - const targetQuadExtent = boundingExtent([a, b, c, d]); - const targetCoverageX = getWidth(targetQuadExtent) / this.targetWorldWidth_; - needsSubdivision |= - targetCoverageX > MAX_TRIANGLE_WIDTH; - } - if (!wrapsX && this.sourceProj_.isGlobal() && sourceCoverageX) { - needsSubdivision |= - sourceCoverageX > MAX_TRIANGLE_WIDTH; - } + /** + * Adds triangle to the triangulation. + * @param {module:ol/coordinate~Coordinate} a The target a coordinate. + * @param {module:ol/coordinate~Coordinate} b The target b coordinate. + * @param {module:ol/coordinate~Coordinate} c The target c coordinate. + * @param {module:ol/coordinate~Coordinate} aSrc The source a coordinate. + * @param {module:ol/coordinate~Coordinate} bSrc The source b coordinate. + * @param {module:ol/coordinate~Coordinate} cSrc The source c coordinate. + * @private + */ + addTriangle_(a, b, c, aSrc, bSrc, cSrc) { + this.triangles_.push({ + source: [aSrc, bSrc, cSrc], + target: [a, b, c] + }); } - if (!needsSubdivision && this.maxSourceExtent_) { - if (!intersects(sourceQuadExtent, this.maxSourceExtent_)) { - // whole quad outside source projection extent -> ignore - return; - } - } + /** + * Adds quad (points in clock-wise order) to the triangulation + * (and reprojects the vertices) if valid. + * Performs quad subdivision if needed to increase precision. + * + * @param {module:ol/coordinate~Coordinate} a The target a coordinate. + * @param {module:ol/coordinate~Coordinate} b The target b coordinate. + * @param {module:ol/coordinate~Coordinate} c The target c coordinate. + * @param {module:ol/coordinate~Coordinate} d The target d coordinate. + * @param {module:ol/coordinate~Coordinate} aSrc The source a coordinate. + * @param {module:ol/coordinate~Coordinate} bSrc The source b coordinate. + * @param {module:ol/coordinate~Coordinate} cSrc The source c coordinate. + * @param {module:ol/coordinate~Coordinate} dSrc The source d coordinate. + * @param {number} maxSubdivision Maximal allowed subdivision of the quad. + * @private + */ + addQuad_(a, b, c, d, aSrc, bSrc, cSrc, dSrc, maxSubdivision) { - if (!needsSubdivision) { - if (!isFinite(aSrc[0]) || !isFinite(aSrc[1]) || - !isFinite(bSrc[0]) || !isFinite(bSrc[1]) || - !isFinite(cSrc[0]) || !isFinite(cSrc[1]) || - !isFinite(dSrc[0]) || !isFinite(dSrc[1])) { - if (maxSubdivision > 0) { - needsSubdivision = true; - } else { + const sourceQuadExtent = boundingExtent([aSrc, bSrc, cSrc, dSrc]); + const sourceCoverageX = this.sourceWorldWidth_ ? + getWidth(sourceQuadExtent) / this.sourceWorldWidth_ : null; + const sourceWorldWidth = /** @type {number} */ (this.sourceWorldWidth_); + + // when the quad is wrapped in the source projection + // it covers most of the projection extent, but not fully + const wrapsX = this.sourceProj_.canWrapX() && + sourceCoverageX > 0.5 && sourceCoverageX < 1; + + let needsSubdivision = false; + + if (maxSubdivision > 0) { + if (this.targetProj_.isGlobal() && this.targetWorldWidth_) { + const targetQuadExtent = boundingExtent([a, b, c, d]); + const targetCoverageX = getWidth(targetQuadExtent) / this.targetWorldWidth_; + needsSubdivision |= + targetCoverageX > MAX_TRIANGLE_WIDTH; + } + if (!wrapsX && this.sourceProj_.isGlobal() && sourceCoverageX) { + needsSubdivision |= + sourceCoverageX > MAX_TRIANGLE_WIDTH; + } + } + + if (!needsSubdivision && this.maxSourceExtent_) { + if (!intersects(sourceQuadExtent, this.maxSourceExtent_)) { + // whole quad outside source projection extent -> ignore return; } } - } - if (maxSubdivision > 0) { if (!needsSubdivision) { - const center = [(a[0] + c[0]) / 2, (a[1] + c[1]) / 2]; - const centerSrc = this.transformInv_(center); - - let dx; - if (wrapsX) { - const centerSrcEstimX = - (modulo(aSrc[0], sourceWorldWidth) + - modulo(cSrc[0], sourceWorldWidth)) / 2; - dx = centerSrcEstimX - - modulo(centerSrc[0], sourceWorldWidth); - } else { - dx = (aSrc[0] + cSrc[0]) / 2 - centerSrc[0]; + if (!isFinite(aSrc[0]) || !isFinite(aSrc[1]) || + !isFinite(bSrc[0]) || !isFinite(bSrc[1]) || + !isFinite(cSrc[0]) || !isFinite(cSrc[1]) || + !isFinite(dSrc[0]) || !isFinite(dSrc[1])) { + if (maxSubdivision > 0) { + needsSubdivision = true; + } else { + return; + } } - const dy = (aSrc[1] + cSrc[1]) / 2 - centerSrc[1]; - const centerSrcErrorSquared = dx * dx + dy * dy; - needsSubdivision = centerSrcErrorSquared > this.errorThresholdSquared_; } - if (needsSubdivision) { - if (Math.abs(a[0] - c[0]) <= Math.abs(a[1] - c[1])) { - // split horizontally (top & bottom) - const bc = [(b[0] + c[0]) / 2, (b[1] + c[1]) / 2]; - const bcSrc = this.transformInv_(bc); - const da = [(d[0] + a[0]) / 2, (d[1] + a[1]) / 2]; - const daSrc = this.transformInv_(da); - this.addQuad_( - a, b, bc, da, aSrc, bSrc, bcSrc, daSrc, maxSubdivision - 1); - this.addQuad_( - da, bc, c, d, daSrc, bcSrc, cSrc, dSrc, maxSubdivision - 1); - } else { - // split vertically (left & right) - const ab = [(a[0] + b[0]) / 2, (a[1] + b[1]) / 2]; - const abSrc = this.transformInv_(ab); - const cd = [(c[0] + d[0]) / 2, (c[1] + d[1]) / 2]; - const cdSrc = this.transformInv_(cd); + if (maxSubdivision > 0) { + if (!needsSubdivision) { + const center = [(a[0] + c[0]) / 2, (a[1] + c[1]) / 2]; + const centerSrc = this.transformInv_(center); - this.addQuad_( - a, ab, cd, d, aSrc, abSrc, cdSrc, dSrc, maxSubdivision - 1); - this.addQuad_( - ab, b, c, cd, abSrc, bSrc, cSrc, cdSrc, maxSubdivision - 1); + let dx; + if (wrapsX) { + const centerSrcEstimX = + (modulo(aSrc[0], sourceWorldWidth) + + modulo(cSrc[0], sourceWorldWidth)) / 2; + dx = centerSrcEstimX - + modulo(centerSrc[0], sourceWorldWidth); + } else { + dx = (aSrc[0] + cSrc[0]) / 2 - centerSrc[0]; + } + const dy = (aSrc[1] + cSrc[1]) / 2 - centerSrc[1]; + const centerSrcErrorSquared = dx * dx + dy * dy; + needsSubdivision = centerSrcErrorSquared > this.errorThresholdSquared_; + } + if (needsSubdivision) { + if (Math.abs(a[0] - c[0]) <= Math.abs(a[1] - c[1])) { + // split horizontally (top & bottom) + const bc = [(b[0] + c[0]) / 2, (b[1] + c[1]) / 2]; + const bcSrc = this.transformInv_(bc); + const da = [(d[0] + a[0]) / 2, (d[1] + a[1]) / 2]; + const daSrc = this.transformInv_(da); + + this.addQuad_( + a, b, bc, da, aSrc, bSrc, bcSrc, daSrc, maxSubdivision - 1); + this.addQuad_( + da, bc, c, d, daSrc, bcSrc, cSrc, dSrc, maxSubdivision - 1); + } else { + // split vertically (left & right) + const ab = [(a[0] + b[0]) / 2, (a[1] + b[1]) / 2]; + const abSrc = this.transformInv_(ab); + const cd = [(c[0] + d[0]) / 2, (c[1] + d[1]) / 2]; + const cdSrc = this.transformInv_(cd); + + this.addQuad_( + a, ab, cd, d, aSrc, abSrc, cdSrc, dSrc, maxSubdivision - 1); + this.addQuad_( + ab, b, c, cd, abSrc, bSrc, cSrc, cdSrc, maxSubdivision - 1); + } + return; } - return; } + + if (wrapsX) { + if (!this.canWrapXInSource_) { + return; + } + this.wrapsXInSource_ = true; + } + + this.addTriangle_(a, c, d, aSrc, cSrc, dSrc); + this.addTriangle_(a, b, c, aSrc, bSrc, cSrc); } - if (wrapsX) { - if (!this.canWrapXInSource_) { - return; - } - this.wrapsXInSource_ = true; + /** + * Calculates extent of the 'source' coordinates from all the triangles. + * + * @return {module:ol/extent~Extent} Calculated extent. + */ + calculateSourceExtent() { + const extent = createEmpty(); + + this.triangles_.forEach(function(triangle, i, arr) { + const src = triangle.source; + extendCoordinate(extent, src[0]); + extendCoordinate(extent, src[1]); + extendCoordinate(extent, src[2]); + }); + + return extent; } - this.addTriangle_(a, c, d, aSrc, cSrc, dSrc); - this.addTriangle_(a, b, c, aSrc, bSrc, cSrc); -}; + /** + * @return {Array.} Array of the calculated triangles. + */ + getTriangles() { + return this.triangles_; + } +} - -/** - * Calculates extent of the 'source' coordinates from all the triangles. - * - * @return {module:ol/extent~Extent} Calculated extent. - */ -Triangulation.prototype.calculateSourceExtent = function() { - const extent = createEmpty(); - - this.triangles_.forEach(function(triangle, i, arr) { - const src = triangle.source; - extendCoordinate(extent, src[0]); - extendCoordinate(extent, src[1]); - extendCoordinate(extent, src[2]); - }); - - return extent; -}; - - -/** - * @return {Array.} Array of the calculated triangles. - */ -Triangulation.prototype.getTriangles = function() { - return this.triangles_; -}; export default Triangulation; diff --git a/src/ol/source/BingMaps.js b/src/ol/source/BingMaps.js index 414d971ebc..928befb475 100644 --- a/src/ol/source/BingMaps.js +++ b/src/ol/source/BingMaps.js @@ -41,60 +41,175 @@ import {createXYZ, extentFromProjection} from '../tilegrid.js'; * @param {module:ol/source/BingMaps~Options=} options Bing Maps options. * @api */ -const BingMaps = function(options) { +class BingMaps { + constructor(options) { + + /** + * @private + * @type {boolean} + */ + this.hidpi_ = options.hidpi !== undefined ? options.hidpi : false; + + TileImage.call(this, { + cacheSize: options.cacheSize, + crossOrigin: 'anonymous', + opaque: true, + projection: getProjection('EPSG:3857'), + reprojectionErrorThreshold: options.reprojectionErrorThreshold, + state: SourceState.LOADING, + tileLoadFunction: options.tileLoadFunction, + tilePixelRatio: this.hidpi_ ? 2 : 1, + wrapX: options.wrapX !== undefined ? options.wrapX : true, + transition: options.transition + }); + + /** + * @private + * @type {string} + */ + this.culture_ = options.culture !== undefined ? options.culture : 'en-us'; + + /** + * @private + * @type {number} + */ + this.maxZoom_ = options.maxZoom !== undefined ? options.maxZoom : -1; + + /** + * @private + * @type {string} + */ + this.apiKey_ = options.key; + + /** + * @private + * @type {string} + */ + this.imagerySet_ = options.imagerySet; + + const url = 'https://dev.virtualearth.net/REST/v1/Imagery/Metadata/' + + this.imagerySet_ + + '?uriScheme=https&include=ImageryProviders&key=' + this.apiKey_ + + '&c=' + this.culture_; + + requestJSONP(url, this.handleImageryMetadataResponse.bind(this), undefined, + 'jsonp'); + + } /** - * @private - * @type {boolean} + * Get the api key used for this source. + * + * @return {string} The api key. + * @api */ - this.hidpi_ = options.hidpi !== undefined ? options.hidpi : false; - - TileImage.call(this, { - cacheSize: options.cacheSize, - crossOrigin: 'anonymous', - opaque: true, - projection: getProjection('EPSG:3857'), - reprojectionErrorThreshold: options.reprojectionErrorThreshold, - state: SourceState.LOADING, - tileLoadFunction: options.tileLoadFunction, - tilePixelRatio: this.hidpi_ ? 2 : 1, - wrapX: options.wrapX !== undefined ? options.wrapX : true, - transition: options.transition - }); + getApiKey() { + return this.apiKey_; + } /** - * @private - * @type {string} + * Get the imagery set associated with this source. + * + * @return {string} The imagery set. + * @api */ - this.culture_ = options.culture !== undefined ? options.culture : 'en-us'; + getImagerySet() { + return this.imagerySet_; + } /** - * @private - * @type {number} + * @param {BingMapsImageryMetadataResponse} response Response. */ - this.maxZoom_ = options.maxZoom !== undefined ? options.maxZoom : -1; + handleImageryMetadataResponse(response) { + if (response.statusCode != 200 || + response.statusDescription != 'OK' || + response.authenticationResultCode != 'ValidCredentials' || + response.resourceSets.length != 1 || + response.resourceSets[0].resources.length != 1) { + this.setState(SourceState.ERROR); + return; + } - /** - * @private - * @type {string} - */ - this.apiKey_ = options.key; + const resource = response.resourceSets[0].resources[0]; + const maxZoom = this.maxZoom_ == -1 ? resource.zoomMax : this.maxZoom_; - /** - * @private - * @type {string} - */ - this.imagerySet_ = options.imagerySet; + const sourceProjection = this.getProjection(); + const extent = extentFromProjection(sourceProjection); + const tileSize = resource.imageWidth == resource.imageHeight ? + resource.imageWidth : [resource.imageWidth, resource.imageHeight]; + const tileGrid = createXYZ({ + extent: extent, + minZoom: resource.zoomMin, + maxZoom: maxZoom, + tileSize: tileSize / (this.hidpi_ ? 2 : 1) + }); + this.tileGrid = tileGrid; - const url = 'https://dev.virtualearth.net/REST/v1/Imagery/Metadata/' + - this.imagerySet_ + - '?uriScheme=https&include=ImageryProviders&key=' + this.apiKey_ + - '&c=' + this.culture_; + const culture = this.culture_; + const hidpi = this.hidpi_; + this.tileUrlFunction = createFromTileUrlFunctions( + resource.imageUrlSubdomains.map(function(subdomain) { + const quadKeyTileCoord = [0, 0, 0]; + const imageUrl = resource.imageUrl + .replace('{subdomain}', subdomain) + .replace('{culture}', culture); + return ( + /** + * @param {module:ol/tilecoord~TileCoord} tileCoord Tile coordinate. + * @param {number} pixelRatio Pixel ratio. + * @param {module:ol/proj/Projection} projection Projection. + * @return {string|undefined} Tile URL. + */ + function(tileCoord, pixelRatio, projection) { + if (!tileCoord) { + return undefined; + } else { + createOrUpdate(tileCoord[0], tileCoord[1], -tileCoord[2] - 1, quadKeyTileCoord); + let url = imageUrl; + if (hidpi) { + url += '&dpi=d1&device=mobile'; + } + return url.replace('{quadkey}', quadKey(quadKeyTileCoord)); + } + } + ); + })); - requestJSONP(url, this.handleImageryMetadataResponse.bind(this), undefined, - 'jsonp'); + if (resource.imageryProviders) { + const transform = getTransformFromProjections( + getProjection('EPSG:4326'), this.getProjection()); -}; + this.setAttributions(function(frameState) { + const attributions = []; + const zoom = frameState.viewState.zoom; + resource.imageryProviders.map(function(imageryProvider) { + let intersecting = false; + const coverageAreas = imageryProvider.coverageAreas; + for (let i = 0, ii = coverageAreas.length; i < ii; ++i) { + const coverageArea = coverageAreas[i]; + if (zoom >= coverageArea.zoomMin && zoom <= coverageArea.zoomMax) { + const bbox = coverageArea.bbox; + const epsg4326Extent = [bbox[1], bbox[0], bbox[3], bbox[2]]; + const extent = applyTransform(epsg4326Extent, transform); + if (intersects(extent, frameState.extent)) { + intersecting = true; + break; + } + } + } + if (intersecting) { + attributions.push(imageryProvider.attribution); + } + }); + + attributions.push(TOS_ATTRIBUTION); + return attributions; + }); + } + + this.setState(SourceState.READY); + } +} inherits(BingMaps, TileImage); @@ -110,118 +225,4 @@ const TOS_ATTRIBUTION = ''; -/** - * Get the api key used for this source. - * - * @return {string} The api key. - * @api - */ -BingMaps.prototype.getApiKey = function() { - return this.apiKey_; -}; - - -/** - * Get the imagery set associated with this source. - * - * @return {string} The imagery set. - * @api - */ -BingMaps.prototype.getImagerySet = function() { - return this.imagerySet_; -}; - - -/** - * @param {BingMapsImageryMetadataResponse} response Response. - */ -BingMaps.prototype.handleImageryMetadataResponse = function(response) { - if (response.statusCode != 200 || - response.statusDescription != 'OK' || - response.authenticationResultCode != 'ValidCredentials' || - response.resourceSets.length != 1 || - response.resourceSets[0].resources.length != 1) { - this.setState(SourceState.ERROR); - return; - } - - const resource = response.resourceSets[0].resources[0]; - const maxZoom = this.maxZoom_ == -1 ? resource.zoomMax : this.maxZoom_; - - const sourceProjection = this.getProjection(); - const extent = extentFromProjection(sourceProjection); - const tileSize = resource.imageWidth == resource.imageHeight ? - resource.imageWidth : [resource.imageWidth, resource.imageHeight]; - const tileGrid = createXYZ({ - extent: extent, - minZoom: resource.zoomMin, - maxZoom: maxZoom, - tileSize: tileSize / (this.hidpi_ ? 2 : 1) - }); - this.tileGrid = tileGrid; - - const culture = this.culture_; - const hidpi = this.hidpi_; - this.tileUrlFunction = createFromTileUrlFunctions( - resource.imageUrlSubdomains.map(function(subdomain) { - const quadKeyTileCoord = [0, 0, 0]; - const imageUrl = resource.imageUrl - .replace('{subdomain}', subdomain) - .replace('{culture}', culture); - return ( - /** - * @param {module:ol/tilecoord~TileCoord} tileCoord Tile coordinate. - * @param {number} pixelRatio Pixel ratio. - * @param {module:ol/proj/Projection} projection Projection. - * @return {string|undefined} Tile URL. - */ - function(tileCoord, pixelRatio, projection) { - if (!tileCoord) { - return undefined; - } else { - createOrUpdate(tileCoord[0], tileCoord[1], -tileCoord[2] - 1, quadKeyTileCoord); - let url = imageUrl; - if (hidpi) { - url += '&dpi=d1&device=mobile'; - } - return url.replace('{quadkey}', quadKey(quadKeyTileCoord)); - } - } - ); - })); - - if (resource.imageryProviders) { - const transform = getTransformFromProjections( - getProjection('EPSG:4326'), this.getProjection()); - - this.setAttributions(function(frameState) { - const attributions = []; - const zoom = frameState.viewState.zoom; - resource.imageryProviders.map(function(imageryProvider) { - let intersecting = false; - const coverageAreas = imageryProvider.coverageAreas; - for (let i = 0, ii = coverageAreas.length; i < ii; ++i) { - const coverageArea = coverageAreas[i]; - if (zoom >= coverageArea.zoomMin && zoom <= coverageArea.zoomMax) { - const bbox = coverageArea.bbox; - const epsg4326Extent = [bbox[1], bbox[0], bbox[3], bbox[2]]; - const extent = applyTransform(epsg4326Extent, transform); - if (intersects(extent, frameState.extent)) { - intersecting = true; - break; - } - } - } - if (intersecting) { - attributions.push(imageryProvider.attribution); - } - }); - - attributions.push(TOS_ATTRIBUTION); - return attributions; - }); - } - - this.setState(SourceState.READY); -}; export default BingMaps; diff --git a/src/ol/source/CartoDB.js b/src/ol/source/CartoDB.js index a23cb38a55..5b8af63d6a 100644 --- a/src/ol/source/CartoDB.js +++ b/src/ol/source/CartoDB.js @@ -40,152 +40,149 @@ import XYZ from '../source/XYZ.js'; * @param {module:ol/source/CartoDB~Options=} options CartoDB options. * @api */ -const CartoDB = function(options) { +class CartoDB { + constructor(options) { + + /** + * @type {string} + * @private + */ + this.account_ = options.account; + + /** + * @type {string} + * @private + */ + this.mapId_ = options.map || ''; + + /** + * @type {!Object} + * @private + */ + this.config_ = options.config || {}; + + /** + * @type {!Object.} + * @private + */ + this.templateCache_ = {}; + + XYZ.call(this, { + attributions: options.attributions, + cacheSize: options.cacheSize, + crossOrigin: options.crossOrigin, + maxZoom: options.maxZoom !== undefined ? options.maxZoom : 18, + minZoom: options.minZoom, + projection: options.projection, + state: SourceState.LOADING, + wrapX: options.wrapX + }); + this.initializeMap_(); + } /** - * @type {string} - * @private + * Returns the current config. + * @return {!Object} The current configuration. + * @api */ - this.account_ = options.account; + getConfig() { + return this.config_; + } /** - * @type {string} - * @private + * Updates the carto db config. + * @param {Object} config a key-value lookup. Values will replace current values + * in the config. + * @api */ - this.mapId_ = options.map || ''; + updateConfig(config) { + assign(this.config_, config); + this.initializeMap_(); + } /** - * @type {!Object} - * @private + * Sets the CartoDB config + * @param {Object} config In the case of anonymous maps, a CartoDB configuration + * object. + * If using named maps, a key-value lookup with the template parameters. + * @api */ - this.config_ = options.config || {}; + setConfig(config) { + this.config_ = config || {}; + this.initializeMap_(); + } /** - * @type {!Object.} + * Issue a request to initialize the CartoDB map. * @private */ - this.templateCache_ = {}; + initializeMap_() { + const paramHash = JSON.stringify(this.config_); + if (this.templateCache_[paramHash]) { + this.applyTemplate_(this.templateCache_[paramHash]); + return; + } + let mapUrl = 'https://' + this.account_ + '.carto.com/api/v1/map'; - XYZ.call(this, { - attributions: options.attributions, - cacheSize: options.cacheSize, - crossOrigin: options.crossOrigin, - maxZoom: options.maxZoom !== undefined ? options.maxZoom : 18, - minZoom: options.minZoom, - projection: options.projection, - state: SourceState.LOADING, - wrapX: options.wrapX - }); - this.initializeMap_(); -}; + if (this.mapId_) { + mapUrl += '/named/' + this.mapId_; + } + + const client = new XMLHttpRequest(); + client.addEventListener('load', this.handleInitResponse_.bind(this, paramHash)); + client.addEventListener('error', this.handleInitError_.bind(this)); + client.open('POST', mapUrl); + client.setRequestHeader('Content-type', 'application/json'); + client.send(JSON.stringify(this.config_)); + } + + /** + * Handle map initialization response. + * @param {string} paramHash a hash representing the parameter set that was used + * for the request + * @param {Event} event Event. + * @private + */ + handleInitResponse_(paramHash, event) { + const client = /** @type {XMLHttpRequest} */ (event.target); + // status will be 0 for file:// urls + if (!client.status || client.status >= 200 && client.status < 300) { + let response; + try { + response = /** @type {CartoDBLayerInfo} */(JSON.parse(client.responseText)); + } catch (err) { + this.setState(SourceState.ERROR); + return; + } + this.applyTemplate_(response); + this.templateCache_[paramHash] = response; + this.setState(SourceState.READY); + } else { + this.setState(SourceState.ERROR); + } + } + + /** + * @private + * @param {Event} event Event. + */ + handleInitError_(event) { + this.setState(SourceState.ERROR); + } + + /** + * Apply the new tile urls returned by carto db + * @param {CartoDBLayerInfo} data Result of carto db call. + * @private + */ + applyTemplate_(data) { + const tilesUrl = 'https://' + data.cdn_url.https + '/' + this.account_ + + '/api/v1/map/' + data.layergroupid + '/{z}/{x}/{y}.png'; + this.setUrl(tilesUrl); + } +} inherits(CartoDB, XYZ); -/** - * Returns the current config. - * @return {!Object} The current configuration. - * @api - */ -CartoDB.prototype.getConfig = function() { - return this.config_; -}; - - -/** - * Updates the carto db config. - * @param {Object} config a key-value lookup. Values will replace current values - * in the config. - * @api - */ -CartoDB.prototype.updateConfig = function(config) { - assign(this.config_, config); - this.initializeMap_(); -}; - - -/** - * Sets the CartoDB config - * @param {Object} config In the case of anonymous maps, a CartoDB configuration - * object. - * If using named maps, a key-value lookup with the template parameters. - * @api - */ -CartoDB.prototype.setConfig = function(config) { - this.config_ = config || {}; - this.initializeMap_(); -}; - - -/** - * Issue a request to initialize the CartoDB map. - * @private - */ -CartoDB.prototype.initializeMap_ = function() { - const paramHash = JSON.stringify(this.config_); - if (this.templateCache_[paramHash]) { - this.applyTemplate_(this.templateCache_[paramHash]); - return; - } - let mapUrl = 'https://' + this.account_ + '.carto.com/api/v1/map'; - - if (this.mapId_) { - mapUrl += '/named/' + this.mapId_; - } - - const client = new XMLHttpRequest(); - client.addEventListener('load', this.handleInitResponse_.bind(this, paramHash)); - client.addEventListener('error', this.handleInitError_.bind(this)); - client.open('POST', mapUrl); - client.setRequestHeader('Content-type', 'application/json'); - client.send(JSON.stringify(this.config_)); -}; - - -/** - * Handle map initialization response. - * @param {string} paramHash a hash representing the parameter set that was used - * for the request - * @param {Event} event Event. - * @private - */ -CartoDB.prototype.handleInitResponse_ = function(paramHash, event) { - const client = /** @type {XMLHttpRequest} */ (event.target); - // status will be 0 for file:// urls - if (!client.status || client.status >= 200 && client.status < 300) { - let response; - try { - response = /** @type {CartoDBLayerInfo} */(JSON.parse(client.responseText)); - } catch (err) { - this.setState(SourceState.ERROR); - return; - } - this.applyTemplate_(response); - this.templateCache_[paramHash] = response; - this.setState(SourceState.READY); - } else { - this.setState(SourceState.ERROR); - } -}; - - -/** - * @private - * @param {Event} event Event. - */ -CartoDB.prototype.handleInitError_ = function(event) { - this.setState(SourceState.ERROR); -}; - - -/** - * Apply the new tile urls returned by carto db - * @param {CartoDBLayerInfo} data Result of carto db call. - * @private - */ -CartoDB.prototype.applyTemplate_ = function(data) { - const tilesUrl = 'https://' + data.cdn_url.https + '/' + this.account_ + - '/api/v1/map/' + data.layergroupid + '/{z}/{x}/{y}.png'; - this.setUrl(tilesUrl); -}; export default CartoDB; diff --git a/src/ol/source/Cluster.js b/src/ol/source/Cluster.js index 0dd242e719..5df37479c2 100644 --- a/src/ol/source/Cluster.js +++ b/src/ol/source/Cluster.js @@ -47,175 +47,172 @@ import VectorSource from '../source/Vector.js'; * @extends {module:ol/source/Vector} * @api */ -const Cluster = function(options) { - VectorSource.call(this, { - attributions: options.attributions, - extent: options.extent, - projection: options.projection, - wrapX: options.wrapX - }); +class Cluster { + constructor(options) { + VectorSource.call(this, { + attributions: options.attributions, + extent: options.extent, + projection: options.projection, + wrapX: options.wrapX + }); + + /** + * @type {number|undefined} + * @protected + */ + this.resolution = undefined; + + /** + * @type {number} + * @protected + */ + this.distance = options.distance !== undefined ? options.distance : 20; + + /** + * @type {Array.} + * @protected + */ + this.features = []; + + /** + * @param {module:ol/Feature} feature Feature. + * @return {module:ol/geom/Point} Cluster calculation point. + * @protected + */ + this.geometryFunction = options.geometryFunction || function(feature) { + const geometry = /** @type {module:ol/geom/Point} */ (feature.getGeometry()); + assert(geometry instanceof Point, + 10); // The default `geometryFunction` can only handle `module:ol/geom/Point~Point` geometries + return geometry; + }; + + /** + * @type {module:ol/source/Vector} + * @protected + */ + this.source = options.source; + + listen(this.source, EventType.CHANGE, this.refresh, this); + } /** - * @type {number|undefined} - * @protected + * Get the distance in pixels between clusters. + * @return {number} Distance. + * @api */ - this.resolution = undefined; + getDistance() { + return this.distance; + } /** - * @type {number} - * @protected + * Get a reference to the wrapped source. + * @return {module:ol/source/Vector} Source. + * @api */ - this.distance = options.distance !== undefined ? options.distance : 20; + getSource() { + return this.source; + } /** - * @type {Array.} - * @protected + * @inheritDoc */ - this.features = []; + loadFeatures(extent, resolution, projection) { + this.source.loadFeatures(extent, resolution, projection); + if (resolution !== this.resolution) { + this.clear(); + this.resolution = resolution; + this.cluster(); + this.addFeatures(this.features); + } + } /** - * @param {module:ol/Feature} feature Feature. - * @return {module:ol/geom/Point} Cluster calculation point. - * @protected + * Set the distance in pixels between clusters. + * @param {number} distance The distance in pixels. + * @api */ - this.geometryFunction = options.geometryFunction || function(feature) { - const geometry = /** @type {module:ol/geom/Point} */ (feature.getGeometry()); - assert(geometry instanceof Point, - 10); // The default `geometryFunction` can only handle `module:ol/geom/Point~Point` geometries - return geometry; - }; + setDistance(distance) { + this.distance = distance; + this.refresh(); + } + + /** + * handle the source changing + * @override + */ + refresh() { + this.clear(); + this.cluster(); + this.addFeatures(this.features); + VectorSource.prototype.refresh.call(this); + } /** - * @type {module:ol/source/Vector} * @protected */ - this.source = options.source; + cluster() { + if (this.resolution === undefined) { + return; + } + this.features.length = 0; + const extent = createEmpty(); + const mapDistance = this.distance * this.resolution; + const features = this.source.getFeatures(); - listen(this.source, EventType.CHANGE, this.refresh, this); -}; + /** + * @type {!Object.} + */ + const clustered = {}; + + for (let i = 0, ii = features.length; i < ii; i++) { + const feature = features[i]; + if (!(getUid(feature).toString() in clustered)) { + const geometry = this.geometryFunction(feature); + if (geometry) { + const coordinates = geometry.getCoordinates(); + createOrUpdateFromCoordinate(coordinates, extent); + buffer(extent, mapDistance, extent); + + let neighbors = this.source.getFeaturesInExtent(extent); + neighbors = neighbors.filter(function(neighbor) { + const uid = getUid(neighbor).toString(); + if (!(uid in clustered)) { + clustered[uid] = true; + return true; + } else { + return false; + } + }); + this.features.push(this.createCluster(neighbors)); + } + } + } + } + + /** + * @param {Array.} features Features + * @return {module:ol/Feature} The cluster feature. + * @protected + */ + createCluster(features) { + const centroid = [0, 0]; + for (let i = features.length - 1; i >= 0; --i) { + const geometry = this.geometryFunction(features[i]); + if (geometry) { + addCoordinate(centroid, geometry.getCoordinates()); + } else { + features.splice(i, 1); + } + } + scaleCoordinate(centroid, 1 / features.length); + + const cluster = new Feature(new Point(centroid)); + cluster.set('features', features); + return cluster; + } +} inherits(Cluster, VectorSource); -/** - * Get the distance in pixels between clusters. - * @return {number} Distance. - * @api - */ -Cluster.prototype.getDistance = function() { - return this.distance; -}; - - -/** - * Get a reference to the wrapped source. - * @return {module:ol/source/Vector} Source. - * @api - */ -Cluster.prototype.getSource = function() { - return this.source; -}; - - -/** - * @inheritDoc - */ -Cluster.prototype.loadFeatures = function(extent, resolution, projection) { - this.source.loadFeatures(extent, resolution, projection); - if (resolution !== this.resolution) { - this.clear(); - this.resolution = resolution; - this.cluster(); - this.addFeatures(this.features); - } -}; - - -/** - * Set the distance in pixels between clusters. - * @param {number} distance The distance in pixels. - * @api - */ -Cluster.prototype.setDistance = function(distance) { - this.distance = distance; - this.refresh(); -}; - - -/** - * handle the source changing - * @override - */ -Cluster.prototype.refresh = function() { - this.clear(); - this.cluster(); - this.addFeatures(this.features); - VectorSource.prototype.refresh.call(this); -}; - - -/** - * @protected - */ -Cluster.prototype.cluster = function() { - if (this.resolution === undefined) { - return; - } - this.features.length = 0; - const extent = createEmpty(); - const mapDistance = this.distance * this.resolution; - const features = this.source.getFeatures(); - - /** - * @type {!Object.} - */ - const clustered = {}; - - for (let i = 0, ii = features.length; i < ii; i++) { - const feature = features[i]; - if (!(getUid(feature).toString() in clustered)) { - const geometry = this.geometryFunction(feature); - if (geometry) { - const coordinates = geometry.getCoordinates(); - createOrUpdateFromCoordinate(coordinates, extent); - buffer(extent, mapDistance, extent); - - let neighbors = this.source.getFeaturesInExtent(extent); - neighbors = neighbors.filter(function(neighbor) { - const uid = getUid(neighbor).toString(); - if (!(uid in clustered)) { - clustered[uid] = true; - return true; - } else { - return false; - } - }); - this.features.push(this.createCluster(neighbors)); - } - } - } -}; - - -/** - * @param {Array.} features Features - * @return {module:ol/Feature} The cluster feature. - * @protected - */ -Cluster.prototype.createCluster = function(features) { - const centroid = [0, 0]; - for (let i = features.length - 1; i >= 0; --i) { - const geometry = this.geometryFunction(features[i]); - if (geometry) { - addCoordinate(centroid, geometry.getCoordinates()); - } else { - features.splice(i, 1); - } - } - scaleCoordinate(centroid, 1 / features.length); - - const cluster = new Feature(new Point(centroid)); - cluster.set('features', features); - return cluster; -}; export default Cluster; diff --git a/src/ol/source/Image.js b/src/ol/source/Image.js index 75a63b7136..d5bcea2eb7 100644 --- a/src/ol/source/Image.js +++ b/src/ol/source/Image.js @@ -88,146 +88,143 @@ inherits(ImageSourceEvent, Event); * @param {module:ol/source/Image~Options} options Single image source options. * @api */ -const ImageSource = function(options) { - Source.call(this, { - attributions: options.attributions, - extent: options.extent, - projection: options.projection, - state: options.state - }); +class ImageSource { + constructor(options) { + Source.call(this, { + attributions: options.attributions, + extent: options.extent, + projection: options.projection, + state: options.state + }); + + /** + * @private + * @type {Array.} + */ + this.resolutions_ = options.resolutions !== undefined ? + options.resolutions : null; + + + /** + * @private + * @type {module:ol/reproj/Image} + */ + this.reprojectedImage_ = null; + + + /** + * @private + * @type {number} + */ + this.reprojectedRevision_ = 0; + } /** - * @private - * @type {Array.} + * @return {Array.} Resolutions. + * @override */ - this.resolutions_ = options.resolutions !== undefined ? - options.resolutions : null; - + getResolutions() { + return this.resolutions_; + } /** - * @private - * @type {module:ol/reproj/Image} + * @protected + * @param {number} resolution Resolution. + * @return {number} Resolution. */ - this.reprojectedImage_ = null; - + findNearestResolution(resolution) { + if (this.resolutions_) { + const idx = linearFindNearest(this.resolutions_, resolution, 0); + resolution = this.resolutions_[idx]; + } + return resolution; + } /** - * @private - * @type {number} + * @param {module:ol/extent~Extent} extent Extent. + * @param {number} resolution Resolution. + * @param {number} pixelRatio Pixel ratio. + * @param {module:ol/proj/Projection} projection Projection. + * @return {module:ol/ImageBase} Single image. */ - this.reprojectedRevision_ = 0; -}; + getImage(extent, resolution, pixelRatio, projection) { + const sourceProjection = this.getProjection(); + if (!ENABLE_RASTER_REPROJECTION || + !sourceProjection || + !projection || + equivalent(sourceProjection, projection)) { + if (sourceProjection) { + projection = sourceProjection; + } + return this.getImageInternal(extent, resolution, pixelRatio, projection); + } else { + if (this.reprojectedImage_) { + if (this.reprojectedRevision_ == this.getRevision() && + equivalent( + this.reprojectedImage_.getProjection(), projection) && + this.reprojectedImage_.getResolution() == resolution && + equals(this.reprojectedImage_.getExtent(), extent)) { + return this.reprojectedImage_; + } + this.reprojectedImage_.dispose(); + this.reprojectedImage_ = null; + } + + this.reprojectedImage_ = new ReprojImage( + sourceProjection, projection, extent, resolution, pixelRatio, + function(extent, resolution, pixelRatio) { + return this.getImageInternal(extent, resolution, + pixelRatio, sourceProjection); + }.bind(this)); + this.reprojectedRevision_ = this.getRevision(); + + return this.reprojectedImage_; + } + } + + /** + * @abstract + * @param {module:ol/extent~Extent} extent Extent. + * @param {number} resolution Resolution. + * @param {number} pixelRatio Pixel ratio. + * @param {module:ol/proj/Projection} projection Projection. + * @return {module:ol/ImageBase} Single image. + * @protected + */ + getImageInternal(extent, resolution, pixelRatio, projection) {} + + /** + * Handle image change events. + * @param {module:ol/events/Event} event Event. + * @protected + */ + handleImageChange(event) { + const image = /** @type {module:ol/Image} */ (event.target); + switch (image.getState()) { + case ImageState.LOADING: + this.dispatchEvent( + new ImageSourceEvent(ImageSourceEventType.IMAGELOADSTART, + image)); + break; + case ImageState.LOADED: + this.dispatchEvent( + new ImageSourceEvent(ImageSourceEventType.IMAGELOADEND, + image)); + break; + case ImageState.ERROR: + this.dispatchEvent( + new ImageSourceEvent(ImageSourceEventType.IMAGELOADERROR, + image)); + break; + default: + // pass + } + } +} inherits(ImageSource, Source); -/** - * @return {Array.} Resolutions. - * @override - */ -ImageSource.prototype.getResolutions = function() { - return this.resolutions_; -}; - - -/** - * @protected - * @param {number} resolution Resolution. - * @return {number} Resolution. - */ -ImageSource.prototype.findNearestResolution = function(resolution) { - if (this.resolutions_) { - const idx = linearFindNearest(this.resolutions_, resolution, 0); - resolution = this.resolutions_[idx]; - } - return resolution; -}; - - -/** - * @param {module:ol/extent~Extent} extent Extent. - * @param {number} resolution Resolution. - * @param {number} pixelRatio Pixel ratio. - * @param {module:ol/proj/Projection} projection Projection. - * @return {module:ol/ImageBase} Single image. - */ -ImageSource.prototype.getImage = function(extent, resolution, pixelRatio, projection) { - const sourceProjection = this.getProjection(); - if (!ENABLE_RASTER_REPROJECTION || - !sourceProjection || - !projection || - equivalent(sourceProjection, projection)) { - if (sourceProjection) { - projection = sourceProjection; - } - return this.getImageInternal(extent, resolution, pixelRatio, projection); - } else { - if (this.reprojectedImage_) { - if (this.reprojectedRevision_ == this.getRevision() && - equivalent( - this.reprojectedImage_.getProjection(), projection) && - this.reprojectedImage_.getResolution() == resolution && - equals(this.reprojectedImage_.getExtent(), extent)) { - return this.reprojectedImage_; - } - this.reprojectedImage_.dispose(); - this.reprojectedImage_ = null; - } - - this.reprojectedImage_ = new ReprojImage( - sourceProjection, projection, extent, resolution, pixelRatio, - function(extent, resolution, pixelRatio) { - return this.getImageInternal(extent, resolution, - pixelRatio, sourceProjection); - }.bind(this)); - this.reprojectedRevision_ = this.getRevision(); - - return this.reprojectedImage_; - } -}; - - -/** - * @abstract - * @param {module:ol/extent~Extent} extent Extent. - * @param {number} resolution Resolution. - * @param {number} pixelRatio Pixel ratio. - * @param {module:ol/proj/Projection} projection Projection. - * @return {module:ol/ImageBase} Single image. - * @protected - */ -ImageSource.prototype.getImageInternal = function(extent, resolution, pixelRatio, projection) {}; - - -/** - * Handle image change events. - * @param {module:ol/events/Event} event Event. - * @protected - */ -ImageSource.prototype.handleImageChange = function(event) { - const image = /** @type {module:ol/Image} */ (event.target); - switch (image.getState()) { - case ImageState.LOADING: - this.dispatchEvent( - new ImageSourceEvent(ImageSourceEventType.IMAGELOADSTART, - image)); - break; - case ImageState.LOADED: - this.dispatchEvent( - new ImageSourceEvent(ImageSourceEventType.IMAGELOADEND, - image)); - break; - case ImageState.ERROR: - this.dispatchEvent( - new ImageSourceEvent(ImageSourceEventType.IMAGELOADERROR, - image)); - break; - default: - // pass - } -}; - - /** * Default image load function for image sources that use module:ol/Image~Image image * instances. diff --git a/src/ol/source/ImageArcGISRest.js b/src/ol/source/ImageArcGISRest.js index 6c5af41d6c..78d5a42ac5 100644 --- a/src/ol/source/ImageArcGISRest.js +++ b/src/ol/source/ImageArcGISRest.js @@ -54,246 +54,242 @@ import {appendParams} from '../uri.js'; * @param {module:ol/source/ImageArcGISRest~Options=} opt_options Image ArcGIS Rest Options. * @api */ -const ImageArcGISRest = function(opt_options) { +class ImageArcGISRest { + constructor(opt_options) { - const options = opt_options || {}; + const options = opt_options || {}; - ImageSource.call(this, { - attributions: options.attributions, - projection: options.projection, - resolutions: options.resolutions - }); + ImageSource.call(this, { + attributions: options.attributions, + projection: options.projection, + resolutions: options.resolutions + }); + + /** + * @private + * @type {?string} + */ + this.crossOrigin_ = + options.crossOrigin !== undefined ? options.crossOrigin : null; + + /** + * @private + * @type {boolean} + */ + this.hidpi_ = options.hidpi !== undefined ? options.hidpi : true; + + /** + * @private + * @type {string|undefined} + */ + this.url_ = options.url; + + /** + * @private + * @type {module:ol/Image~LoadFunction} + */ + this.imageLoadFunction_ = options.imageLoadFunction !== undefined ? + options.imageLoadFunction : defaultImageLoadFunction; + + + /** + * @private + * @type {!Object} + */ + this.params_ = options.params || {}; + + /** + * @private + * @type {module:ol/Image} + */ + this.image_ = null; + + /** + * @private + * @type {module:ol/size~Size} + */ + this.imageSize_ = [0, 0]; + + + /** + * @private + * @type {number} + */ + this.renderedRevision_ = 0; + + /** + * @private + * @type {number} + */ + this.ratio_ = options.ratio !== undefined ? options.ratio : 1.5; + + } /** - * @private - * @type {?string} + * Get the user-provided params, i.e. those passed to the constructor through + * the "params" option, and possibly updated using the updateParams method. + * @return {Object} Params. + * @api */ - this.crossOrigin_ = - options.crossOrigin !== undefined ? options.crossOrigin : null; + getParams() { + return this.params_; + } /** - * @private - * @type {boolean} + * @inheritDoc */ - this.hidpi_ = options.hidpi !== undefined ? options.hidpi : true; + getImageInternal(extent, resolution, pixelRatio, projection) { + + if (this.url_ === undefined) { + return null; + } + + resolution = this.findNearestResolution(resolution); + pixelRatio = this.hidpi_ ? pixelRatio : 1; + + const image = this.image_; + if (image && + this.renderedRevision_ == this.getRevision() && + image.getResolution() == resolution && + image.getPixelRatio() == pixelRatio && + containsExtent(image.getExtent(), extent)) { + return image; + } + + const params = { + 'F': 'image', + 'FORMAT': 'PNG32', + 'TRANSPARENT': true + }; + assign(params, this.params_); + + extent = extent.slice(); + const centerX = (extent[0] + extent[2]) / 2; + const centerY = (extent[1] + extent[3]) / 2; + if (this.ratio_ != 1) { + const halfWidth = this.ratio_ * getWidth(extent) / 2; + const halfHeight = this.ratio_ * getHeight(extent) / 2; + extent[0] = centerX - halfWidth; + extent[1] = centerY - halfHeight; + extent[2] = centerX + halfWidth; + extent[3] = centerY + halfHeight; + } + + const imageResolution = resolution / pixelRatio; + + // Compute an integer width and height. + const width = Math.ceil(getWidth(extent) / imageResolution); + const height = Math.ceil(getHeight(extent) / imageResolution); + + // Modify the extent to match the integer width and height. + extent[0] = centerX - imageResolution * width / 2; + extent[2] = centerX + imageResolution * width / 2; + extent[1] = centerY - imageResolution * height / 2; + extent[3] = centerY + imageResolution * height / 2; + + this.imageSize_[0] = width; + this.imageSize_[1] = height; + + const url = this.getRequestUrl_(extent, this.imageSize_, pixelRatio, + projection, params); + + this.image_ = new ImageWrapper(extent, resolution, pixelRatio, + url, this.crossOrigin_, this.imageLoadFunction_); + + this.renderedRevision_ = this.getRevision(); + + listen(this.image_, EventType.CHANGE, + this.handleImageChange, this); + + return this.image_; + + } /** - * @private - * @type {string|undefined} + * Return the image load function of the source. + * @return {module:ol/Image~LoadFunction} The image load function. + * @api */ - this.url_ = options.url; + getImageLoadFunction() { + return this.imageLoadFunction_; + } /** + * @param {module:ol/extent~Extent} extent Extent. + * @param {module:ol/size~Size} size Size. + * @param {number} pixelRatio Pixel ratio. + * @param {module:ol/proj/Projection} projection Projection. + * @param {Object} params Params. + * @return {string} Request URL. * @private - * @type {module:ol/Image~LoadFunction} */ - this.imageLoadFunction_ = options.imageLoadFunction !== undefined ? - options.imageLoadFunction : defaultImageLoadFunction; + getRequestUrl_(extent, size, pixelRatio, projection, params) { + // ArcGIS Server only wants the numeric portion of the projection ID. + const srid = projection.getCode().split(':').pop(); + params['SIZE'] = size[0] + ',' + size[1]; + params['BBOX'] = extent.join(','); + params['BBOXSR'] = srid; + params['IMAGESR'] = srid; + params['DPI'] = Math.round(90 * pixelRatio); + + const url = this.url_; + + const modifiedUrl = url + .replace(/MapServer\/?$/, 'MapServer/export') + .replace(/ImageServer\/?$/, 'ImageServer/exportImage'); + if (modifiedUrl == url) { + assert(false, 50); // `options.featureTypes` should be an Array + } + return appendParams(modifiedUrl, params); + } /** - * @private - * @type {!Object} + * Return the URL used for this ArcGIS source. + * @return {string|undefined} URL. + * @api */ - this.params_ = options.params || {}; + getUrl() { + return this.url_; + } /** - * @private - * @type {module:ol/Image} + * Set the image load function of the source. + * @param {module:ol/Image~LoadFunction} imageLoadFunction Image load function. + * @api */ - this.image_ = null; + setImageLoadFunction(imageLoadFunction) { + this.image_ = null; + this.imageLoadFunction_ = imageLoadFunction; + this.changed(); + } /** - * @private - * @type {module:ol/size~Size} + * Set the URL to use for requests. + * @param {string|undefined} url URL. + * @api */ - this.imageSize_ = [0, 0]; - + setUrl(url) { + if (url != this.url_) { + this.url_ = url; + this.image_ = null; + this.changed(); + } + } /** - * @private - * @type {number} + * Update the user-provided params. + * @param {Object} params Params. + * @api */ - this.renderedRevision_ = 0; - - /** - * @private - * @type {number} - */ - this.ratio_ = options.ratio !== undefined ? options.ratio : 1.5; - -}; + updateParams(params) { + assign(this.params_, params); + this.image_ = null; + this.changed(); + } +} inherits(ImageArcGISRest, ImageSource); -/** - * Get the user-provided params, i.e. those passed to the constructor through - * the "params" option, and possibly updated using the updateParams method. - * @return {Object} Params. - * @api - */ -ImageArcGISRest.prototype.getParams = function() { - return this.params_; -}; - - -/** - * @inheritDoc - */ -ImageArcGISRest.prototype.getImageInternal = function(extent, resolution, pixelRatio, projection) { - - if (this.url_ === undefined) { - return null; - } - - resolution = this.findNearestResolution(resolution); - pixelRatio = this.hidpi_ ? pixelRatio : 1; - - const image = this.image_; - if (image && - this.renderedRevision_ == this.getRevision() && - image.getResolution() == resolution && - image.getPixelRatio() == pixelRatio && - containsExtent(image.getExtent(), extent)) { - return image; - } - - const params = { - 'F': 'image', - 'FORMAT': 'PNG32', - 'TRANSPARENT': true - }; - assign(params, this.params_); - - extent = extent.slice(); - const centerX = (extent[0] + extent[2]) / 2; - const centerY = (extent[1] + extent[3]) / 2; - if (this.ratio_ != 1) { - const halfWidth = this.ratio_ * getWidth(extent) / 2; - const halfHeight = this.ratio_ * getHeight(extent) / 2; - extent[0] = centerX - halfWidth; - extent[1] = centerY - halfHeight; - extent[2] = centerX + halfWidth; - extent[3] = centerY + halfHeight; - } - - const imageResolution = resolution / pixelRatio; - - // Compute an integer width and height. - const width = Math.ceil(getWidth(extent) / imageResolution); - const height = Math.ceil(getHeight(extent) / imageResolution); - - // Modify the extent to match the integer width and height. - extent[0] = centerX - imageResolution * width / 2; - extent[2] = centerX + imageResolution * width / 2; - extent[1] = centerY - imageResolution * height / 2; - extent[3] = centerY + imageResolution * height / 2; - - this.imageSize_[0] = width; - this.imageSize_[1] = height; - - const url = this.getRequestUrl_(extent, this.imageSize_, pixelRatio, - projection, params); - - this.image_ = new ImageWrapper(extent, resolution, pixelRatio, - url, this.crossOrigin_, this.imageLoadFunction_); - - this.renderedRevision_ = this.getRevision(); - - listen(this.image_, EventType.CHANGE, - this.handleImageChange, this); - - return this.image_; - -}; - - -/** - * Return the image load function of the source. - * @return {module:ol/Image~LoadFunction} The image load function. - * @api - */ -ImageArcGISRest.prototype.getImageLoadFunction = function() { - return this.imageLoadFunction_; -}; - - -/** - * @param {module:ol/extent~Extent} extent Extent. - * @param {module:ol/size~Size} size Size. - * @param {number} pixelRatio Pixel ratio. - * @param {module:ol/proj/Projection} projection Projection. - * @param {Object} params Params. - * @return {string} Request URL. - * @private - */ -ImageArcGISRest.prototype.getRequestUrl_ = function(extent, size, pixelRatio, projection, params) { - // ArcGIS Server only wants the numeric portion of the projection ID. - const srid = projection.getCode().split(':').pop(); - - params['SIZE'] = size[0] + ',' + size[1]; - params['BBOX'] = extent.join(','); - params['BBOXSR'] = srid; - params['IMAGESR'] = srid; - params['DPI'] = Math.round(90 * pixelRatio); - - const url = this.url_; - - const modifiedUrl = url - .replace(/MapServer\/?$/, 'MapServer/export') - .replace(/ImageServer\/?$/, 'ImageServer/exportImage'); - if (modifiedUrl == url) { - assert(false, 50); // `options.featureTypes` should be an Array - } - return appendParams(modifiedUrl, params); -}; - - -/** - * Return the URL used for this ArcGIS source. - * @return {string|undefined} URL. - * @api - */ -ImageArcGISRest.prototype.getUrl = function() { - return this.url_; -}; - - -/** - * Set the image load function of the source. - * @param {module:ol/Image~LoadFunction} imageLoadFunction Image load function. - * @api - */ -ImageArcGISRest.prototype.setImageLoadFunction = function(imageLoadFunction) { - this.image_ = null; - this.imageLoadFunction_ = imageLoadFunction; - this.changed(); -}; - - -/** - * Set the URL to use for requests. - * @param {string|undefined} url URL. - * @api - */ -ImageArcGISRest.prototype.setUrl = function(url) { - if (url != this.url_) { - this.url_ = url; - this.image_ = null; - this.changed(); - } -}; - - -/** - * Update the user-provided params. - * @param {Object} params Params. - * @api - */ -ImageArcGISRest.prototype.updateParams = function(params) { - assign(this.params_, params); - this.image_ = null; - this.changed(); -}; export default ImageArcGISRest; diff --git a/src/ol/source/ImageCanvas.js b/src/ol/source/ImageCanvas.js index dcf48da40e..4080b6fe06 100644 --- a/src/ol/source/ImageCanvas.js +++ b/src/ol/source/ImageCanvas.js @@ -51,74 +51,77 @@ import ImageSource from '../source/Image.js'; * @param {module:ol/source/ImageCanvas~Options=} options ImageCanvas options. * @api */ -const ImageCanvasSource = function(options) { +class ImageCanvasSource { + constructor(options) { - ImageSource.call(this, { - attributions: options.attributions, - projection: options.projection, - resolutions: options.resolutions, - state: options.state - }); + ImageSource.call(this, { + attributions: options.attributions, + projection: options.projection, + resolutions: options.resolutions, + state: options.state + }); - /** - * @private - * @type {module:ol/source/ImageCanvas~FunctionType} - */ - this.canvasFunction_ = options.canvasFunction; + /** + * @private + * @type {module:ol/source/ImageCanvas~FunctionType} + */ + this.canvasFunction_ = options.canvasFunction; - /** - * @private - * @type {module:ol/ImageCanvas} - */ - this.canvas_ = null; + /** + * @private + * @type {module:ol/ImageCanvas} + */ + this.canvas_ = null; - /** - * @private - * @type {number} - */ - this.renderedRevision_ = 0; + /** + * @private + * @type {number} + */ + this.renderedRevision_ = 0; - /** - * @private - * @type {number} - */ - this.ratio_ = options.ratio !== undefined ? - options.ratio : 1.5; + /** + * @private + * @type {number} + */ + this.ratio_ = options.ratio !== undefined ? + options.ratio : 1.5; -}; + } + + /** + * @inheritDoc + */ + getImageInternal(extent, resolution, pixelRatio, projection) { + resolution = this.findNearestResolution(resolution); + + let canvas = this.canvas_; + if (canvas && + this.renderedRevision_ == this.getRevision() && + canvas.getResolution() == resolution && + canvas.getPixelRatio() == pixelRatio && + containsExtent(canvas.getExtent(), extent)) { + return canvas; + } + + extent = extent.slice(); + scaleFromCenter(extent, this.ratio_); + const width = getWidth(extent) / resolution; + const height = getHeight(extent) / resolution; + const size = [width * pixelRatio, height * pixelRatio]; + + const canvasElement = this.canvasFunction_( + extent, resolution, pixelRatio, size, projection); + if (canvasElement) { + canvas = new ImageCanvas(extent, resolution, pixelRatio, canvasElement); + } + this.canvas_ = canvas; + this.renderedRevision_ = this.getRevision(); + + return canvas; + } +} inherits(ImageCanvasSource, ImageSource); -/** - * @inheritDoc - */ -ImageCanvasSource.prototype.getImageInternal = function(extent, resolution, pixelRatio, projection) { - resolution = this.findNearestResolution(resolution); - - let canvas = this.canvas_; - if (canvas && - this.renderedRevision_ == this.getRevision() && - canvas.getResolution() == resolution && - canvas.getPixelRatio() == pixelRatio && - containsExtent(canvas.getExtent(), extent)) { - return canvas; - } - - extent = extent.slice(); - scaleFromCenter(extent, this.ratio_); - const width = getWidth(extent) / resolution; - const height = getHeight(extent) / resolution; - const size = [width * pixelRatio, height * pixelRatio]; - - const canvasElement = this.canvasFunction_( - extent, resolution, pixelRatio, size, projection); - if (canvasElement) { - canvas = new ImageCanvas(extent, resolution, pixelRatio, canvasElement); - } - this.canvas_ = canvas; - this.renderedRevision_ = this.getRevision(); - - return canvas; -}; export default ImageCanvasSource; diff --git a/src/ol/source/ImageMapGuide.js b/src/ol/source/ImageMapGuide.js index 84470ce419..4c04944e3c 100644 --- a/src/ol/source/ImageMapGuide.js +++ b/src/ol/source/ImageMapGuide.js @@ -42,150 +42,199 @@ import {appendParams} from '../uri.js'; * @param {module:ol/source/ImageMapGuide~Options=} options ImageMapGuide options. * @api */ -const ImageMapGuide = function(options) { +class ImageMapGuide { + constructor(options) { - ImageSource.call(this, { - projection: options.projection, - resolutions: options.resolutions - }); + ImageSource.call(this, { + projection: options.projection, + resolutions: options.resolutions + }); + + /** + * @private + * @type {?string} + */ + this.crossOrigin_ = + options.crossOrigin !== undefined ? options.crossOrigin : null; + + /** + * @private + * @type {number} + */ + this.displayDpi_ = options.displayDpi !== undefined ? + options.displayDpi : 96; + + /** + * @private + * @type {!Object} + */ + this.params_ = options.params || {}; + + /** + * @private + * @type {string|undefined} + */ + this.url_ = options.url; + + /** + * @private + * @type {module:ol/Image~LoadFunction} + */ + this.imageLoadFunction_ = options.imageLoadFunction !== undefined ? + options.imageLoadFunction : defaultImageLoadFunction; + + /** + * @private + * @type {boolean} + */ + this.hidpi_ = options.hidpi !== undefined ? options.hidpi : true; + + /** + * @private + * @type {number} + */ + this.metersPerUnit_ = options.metersPerUnit !== undefined ? + options.metersPerUnit : 1; + + /** + * @private + * @type {number} + */ + this.ratio_ = options.ratio !== undefined ? options.ratio : 1; + + /** + * @private + * @type {boolean} + */ + this.useOverlay_ = options.useOverlay !== undefined ? + options.useOverlay : false; + + /** + * @private + * @type {module:ol/Image} + */ + this.image_ = null; + + /** + * @private + * @type {number} + */ + this.renderedRevision_ = 0; + + } /** - * @private - * @type {?string} + * Get the user-provided params, i.e. those passed to the constructor through + * the "params" option, and possibly updated using the updateParams method. + * @return {Object} Params. + * @api */ - this.crossOrigin_ = - options.crossOrigin !== undefined ? options.crossOrigin : null; + getParams() { + return this.params_; + } /** - * @private - * @type {number} + * @inheritDoc */ - this.displayDpi_ = options.displayDpi !== undefined ? - options.displayDpi : 96; + getImageInternal(extent, resolution, pixelRatio, projection) { + resolution = this.findNearestResolution(resolution); + pixelRatio = this.hidpi_ ? pixelRatio : 1; - /** - * @private - * @type {!Object} - */ - this.params_ = options.params || {}; + let image = this.image_; + if (image && + this.renderedRevision_ == this.getRevision() && + image.getResolution() == resolution && + image.getPixelRatio() == pixelRatio && + containsExtent(image.getExtent(), extent)) { + return image; + } - /** - * @private - * @type {string|undefined} - */ - this.url_ = options.url; + if (this.ratio_ != 1) { + extent = extent.slice(); + scaleFromCenter(extent, this.ratio_); + } + const width = getWidth(extent) / resolution; + const height = getHeight(extent) / resolution; + const size = [width * pixelRatio, height * pixelRatio]; - /** - * @private - * @type {module:ol/Image~LoadFunction} - */ - this.imageLoadFunction_ = options.imageLoadFunction !== undefined ? - options.imageLoadFunction : defaultImageLoadFunction; + if (this.url_ !== undefined) { + const imageUrl = this.getUrl(this.url_, this.params_, extent, size, + projection); + image = new ImageWrapper(extent, resolution, pixelRatio, + imageUrl, this.crossOrigin_, + this.imageLoadFunction_); + listen(image, EventType.CHANGE, + this.handleImageChange, this); + } else { + image = null; + } + this.image_ = image; + this.renderedRevision_ = this.getRevision(); - /** - * @private - * @type {boolean} - */ - this.hidpi_ = options.hidpi !== undefined ? options.hidpi : true; - - /** - * @private - * @type {number} - */ - this.metersPerUnit_ = options.metersPerUnit !== undefined ? - options.metersPerUnit : 1; - - /** - * @private - * @type {number} - */ - this.ratio_ = options.ratio !== undefined ? options.ratio : 1; - - /** - * @private - * @type {boolean} - */ - this.useOverlay_ = options.useOverlay !== undefined ? - options.useOverlay : false; - - /** - * @private - * @type {module:ol/Image} - */ - this.image_ = null; - - /** - * @private - * @type {number} - */ - this.renderedRevision_ = 0; - -}; - -inherits(ImageMapGuide, ImageSource); - - -/** - * Get the user-provided params, i.e. those passed to the constructor through - * the "params" option, and possibly updated using the updateParams method. - * @return {Object} Params. - * @api - */ -ImageMapGuide.prototype.getParams = function() { - return this.params_; -}; - - -/** - * @inheritDoc - */ -ImageMapGuide.prototype.getImageInternal = function(extent, resolution, pixelRatio, projection) { - resolution = this.findNearestResolution(resolution); - pixelRatio = this.hidpi_ ? pixelRatio : 1; - - let image = this.image_; - if (image && - this.renderedRevision_ == this.getRevision() && - image.getResolution() == resolution && - image.getPixelRatio() == pixelRatio && - containsExtent(image.getExtent(), extent)) { return image; } - if (this.ratio_ != 1) { - extent = extent.slice(); - scaleFromCenter(extent, this.ratio_); + /** + * Return the image load function of the source. + * @return {module:ol/Image~LoadFunction} The image load function. + * @api + */ + getImageLoadFunction() { + return this.imageLoadFunction_; } - const width = getWidth(extent) / resolution; - const height = getHeight(extent) / resolution; - const size = [width * pixelRatio, height * pixelRatio]; - if (this.url_ !== undefined) { - const imageUrl = this.getUrl(this.url_, this.params_, extent, size, - projection); - image = new ImageWrapper(extent, resolution, pixelRatio, - imageUrl, this.crossOrigin_, - this.imageLoadFunction_); - listen(image, EventType.CHANGE, - this.handleImageChange, this); - } else { - image = null; + /** + * Update the user-provided params. + * @param {Object} params Params. + * @api + */ + updateParams(params) { + assign(this.params_, params); + this.changed(); } - this.image_ = image; - this.renderedRevision_ = this.getRevision(); - return image; -}; + /** + * @param {string} baseUrl The mapagent url. + * @param {Object.} params Request parameters. + * @param {module:ol/extent~Extent} extent Extent. + * @param {module:ol/size~Size} size Size. + * @param {module:ol/proj/Projection} projection Projection. + * @return {string} The mapagent map image request URL. + */ + getUrl(baseUrl, params, extent, size, projection) { + const scale = getScale(extent, size, + this.metersPerUnit_, this.displayDpi_); + const center = getCenter(extent); + const baseParams = { + 'OPERATION': this.useOverlay_ ? 'GETDYNAMICMAPOVERLAYIMAGE' : 'GETMAPIMAGE', + 'VERSION': '2.0.0', + 'LOCALE': 'en', + 'CLIENTAGENT': 'ol/source/ImageMapGuide source', + 'CLIP': '1', + 'SETDISPLAYDPI': this.displayDpi_, + 'SETDISPLAYWIDTH': Math.round(size[0]), + 'SETDISPLAYHEIGHT': Math.round(size[1]), + 'SETVIEWSCALE': scale, + 'SETVIEWCENTERX': center[0], + 'SETVIEWCENTERY': center[1] + }; + assign(baseParams, params); + return appendParams(baseUrl, baseParams); + } + /** + * Set the image load function of the MapGuide source. + * @param {module:ol/Image~LoadFunction} imageLoadFunction Image load function. + * @api + */ + setImageLoadFunction(imageLoadFunction) { + this.image_ = null; + this.imageLoadFunction_ = imageLoadFunction; + this.changed(); + } +} -/** - * Return the image load function of the source. - * @return {module:ol/Image~LoadFunction} The image load function. - * @api - */ -ImageMapGuide.prototype.getImageLoadFunction = function() { - return this.imageLoadFunction_; -}; +inherits(ImageMapGuide, ImageSource); /** @@ -209,55 +258,4 @@ function getScale(extent, size, metersPerUnit, dpi) { } -/** - * Update the user-provided params. - * @param {Object} params Params. - * @api - */ -ImageMapGuide.prototype.updateParams = function(params) { - assign(this.params_, params); - this.changed(); -}; - - -/** - * @param {string} baseUrl The mapagent url. - * @param {Object.} params Request parameters. - * @param {module:ol/extent~Extent} extent Extent. - * @param {module:ol/size~Size} size Size. - * @param {module:ol/proj/Projection} projection Projection. - * @return {string} The mapagent map image request URL. - */ -ImageMapGuide.prototype.getUrl = function(baseUrl, params, extent, size, projection) { - const scale = getScale(extent, size, - this.metersPerUnit_, this.displayDpi_); - const center = getCenter(extent); - const baseParams = { - 'OPERATION': this.useOverlay_ ? 'GETDYNAMICMAPOVERLAYIMAGE' : 'GETMAPIMAGE', - 'VERSION': '2.0.0', - 'LOCALE': 'en', - 'CLIENTAGENT': 'ol/source/ImageMapGuide source', - 'CLIP': '1', - 'SETDISPLAYDPI': this.displayDpi_, - 'SETDISPLAYWIDTH': Math.round(size[0]), - 'SETDISPLAYHEIGHT': Math.round(size[1]), - 'SETVIEWSCALE': scale, - 'SETVIEWCENTERX': center[0], - 'SETVIEWCENTERY': center[1] - }; - assign(baseParams, params); - return appendParams(baseUrl, baseParams); -}; - - -/** - * Set the image load function of the MapGuide source. - * @param {module:ol/Image~LoadFunction} imageLoadFunction Image load function. - * @api - */ -ImageMapGuide.prototype.setImageLoadFunction = function(imageLoadFunction) { - this.image_ = null; - this.imageLoadFunction_ = imageLoadFunction; - this.changed(); -}; export default ImageMapGuide; diff --git a/src/ol/source/ImageStatic.js b/src/ol/source/ImageStatic.js index 6075a0271e..d1ad988bdd 100644 --- a/src/ol/source/ImageStatic.js +++ b/src/ol/source/ImageStatic.js @@ -37,77 +37,79 @@ import ImageSource, {defaultImageLoadFunction} from '../source/Image.js'; * @param {module:ol/source/ImageStatic~Options=} options ImageStatic options. * @api */ -const Static = function(options) { - const imageExtent = options.imageExtent; +class Static { + constructor(options) { + const imageExtent = options.imageExtent; - const crossOrigin = options.crossOrigin !== undefined ? - options.crossOrigin : null; + const crossOrigin = options.crossOrigin !== undefined ? + options.crossOrigin : null; - const /** @type {module:ol/Image~LoadFunction} */ imageLoadFunction = - options.imageLoadFunction !== undefined ? - options.imageLoadFunction : defaultImageLoadFunction; + const /** @type {module:ol/Image~LoadFunction} */ imageLoadFunction = + options.imageLoadFunction !== undefined ? + options.imageLoadFunction : defaultImageLoadFunction; - ImageSource.call(this, { - attributions: options.attributions, - projection: getProjection(options.projection) - }); + ImageSource.call(this, { + attributions: options.attributions, + projection: getProjection(options.projection) + }); + + /** + * @private + * @type {module:ol/Image} + */ + this.image_ = new ImageWrapper(imageExtent, undefined, 1, options.url, crossOrigin, imageLoadFunction); + + /** + * @private + * @type {module:ol/size~Size} + */ + this.imageSize_ = options.imageSize ? options.imageSize : null; + + listen(this.image_, EventType.CHANGE, + this.handleImageChange, this); + + } /** - * @private - * @type {module:ol/Image} + * @inheritDoc */ - this.image_ = new ImageWrapper(imageExtent, undefined, 1, options.url, crossOrigin, imageLoadFunction); + getImageInternal(extent, resolution, pixelRatio, projection) { + if (intersects(extent, this.image_.getExtent())) { + return this.image_; + } + return null; + } /** - * @private - * @type {module:ol/size~Size} + * @inheritDoc */ - this.imageSize_ = options.imageSize ? options.imageSize : null; - - listen(this.image_, EventType.CHANGE, - this.handleImageChange, this); - -}; + handleImageChange(evt) { + if (this.image_.getState() == ImageState.LOADED) { + const imageExtent = this.image_.getExtent(); + const image = this.image_.getImage(); + let imageWidth, imageHeight; + if (this.imageSize_) { + imageWidth = this.imageSize_[0]; + imageHeight = this.imageSize_[1]; + } else { + imageWidth = image.width; + imageHeight = image.height; + } + const resolution = getHeight(imageExtent) / imageHeight; + const targetWidth = Math.ceil(getWidth(imageExtent) / resolution); + if (targetWidth != imageWidth) { + const context = createCanvasContext2D(targetWidth, imageHeight); + const canvas = context.canvas; + context.drawImage(image, 0, 0, imageWidth, imageHeight, + 0, 0, canvas.width, canvas.height); + this.image_.setImage(canvas); + } + } + ImageSource.prototype.handleImageChange.call(this, evt); + } +} inherits(Static, ImageSource); -/** - * @inheritDoc - */ -Static.prototype.getImageInternal = function(extent, resolution, pixelRatio, projection) { - if (intersects(extent, this.image_.getExtent())) { - return this.image_; - } - return null; -}; - - -/** - * @inheritDoc - */ -Static.prototype.handleImageChange = function(evt) { - if (this.image_.getState() == ImageState.LOADED) { - const imageExtent = this.image_.getExtent(); - const image = this.image_.getImage(); - let imageWidth, imageHeight; - if (this.imageSize_) { - imageWidth = this.imageSize_[0]; - imageHeight = this.imageSize_[1]; - } else { - imageWidth = image.width; - imageHeight = image.height; - } - const resolution = getHeight(imageExtent) / imageHeight; - const targetWidth = Math.ceil(getWidth(imageExtent) / resolution); - if (targetWidth != imageWidth) { - const context = createCanvasContext2D(targetWidth, imageHeight); - const canvas = context.canvas; - context.drawImage(image, 0, 0, imageWidth, imageHeight, - 0, 0, canvas.width, canvas.height); - this.image_.setImage(canvas); - } - } - ImageSource.prototype.handleImageChange.call(this, evt); -}; export default Static; diff --git a/src/ol/source/ImageWMS.js b/src/ol/source/ImageWMS.js index 842578a950..963f66fda8 100644 --- a/src/ol/source/ImageWMS.js +++ b/src/ol/source/ImageWMS.js @@ -53,86 +53,329 @@ import {appendParams} from '../uri.js'; * @param {module:ol/source/ImageWMS~Options=} [opt_options] ImageWMS options. * @api */ -const ImageWMS = function(opt_options) { +class ImageWMS { + constructor(opt_options) { - const options = opt_options || {}; + const options = opt_options || {}; - ImageSource.call(this, { - attributions: options.attributions, - projection: options.projection, - resolutions: options.resolutions - }); + ImageSource.call(this, { + attributions: options.attributions, + projection: options.projection, + resolutions: options.resolutions + }); + + /** + * @private + * @type {?string} + */ + this.crossOrigin_ = + options.crossOrigin !== undefined ? options.crossOrigin : null; + + /** + * @private + * @type {string|undefined} + */ + this.url_ = options.url; + + /** + * @private + * @type {module:ol/Image~LoadFunction} + */ + this.imageLoadFunction_ = options.imageLoadFunction !== undefined ? + options.imageLoadFunction : defaultImageLoadFunction; + + /** + * @private + * @type {!Object} + */ + this.params_ = options.params || {}; + + /** + * @private + * @type {boolean} + */ + this.v13_ = true; + this.updateV13_(); + + /** + * @private + * @type {module:ol/source/WMSServerType|undefined} + */ + this.serverType_ = /** @type {module:ol/source/WMSServerType|undefined} */ (options.serverType); + + /** + * @private + * @type {boolean} + */ + this.hidpi_ = options.hidpi !== undefined ? options.hidpi : true; + + /** + * @private + * @type {module:ol/Image} + */ + this.image_ = null; + + /** + * @private + * @type {module:ol/size~Size} + */ + this.imageSize_ = [0, 0]; + + /** + * @private + * @type {number} + */ + this.renderedRevision_ = 0; + + /** + * @private + * @type {number} + */ + this.ratio_ = options.ratio !== undefined ? options.ratio : 1.5; + + } + + /** + * Return the GetFeatureInfo URL for the passed coordinate, resolution, and + * projection. Return `undefined` if the GetFeatureInfo URL cannot be + * constructed. + * @param {module:ol/coordinate~Coordinate} coordinate Coordinate. + * @param {number} resolution Resolution. + * @param {module:ol/proj~ProjectionLike} projection Projection. + * @param {!Object} params GetFeatureInfo params. `INFO_FORMAT` at least should + * be provided. If `QUERY_LAYERS` is not provided then the layers specified + * in the `LAYERS` parameter will be used. `VERSION` should not be + * specified here. + * @return {string|undefined} GetFeatureInfo URL. + * @api + */ + getGetFeatureInfoUrl(coordinate, resolution, projection, params) { + if (this.url_ === undefined) { + return undefined; + } + const projectionObj = getProjection(projection); + const sourceProjectionObj = this.getProjection(); + + if (sourceProjectionObj && sourceProjectionObj !== projectionObj) { + resolution = calculateSourceResolution(sourceProjectionObj, projectionObj, coordinate, resolution); + coordinate = transform(coordinate, projectionObj, sourceProjectionObj); + } + + const extent = getForViewAndSize(coordinate, resolution, 0, + GETFEATUREINFO_IMAGE_SIZE); + + const baseParams = { + 'SERVICE': 'WMS', + 'VERSION': DEFAULT_WMS_VERSION, + 'REQUEST': 'GetFeatureInfo', + 'FORMAT': 'image/png', + 'TRANSPARENT': true, + 'QUERY_LAYERS': this.params_['LAYERS'] + }; + assign(baseParams, this.params_, params); + + const x = Math.floor((coordinate[0] - extent[0]) / resolution); + const y = Math.floor((extent[3] - coordinate[1]) / resolution); + baseParams[this.v13_ ? 'I' : 'X'] = x; + baseParams[this.v13_ ? 'J' : 'Y'] = y; + + return this.getRequestUrl_( + extent, GETFEATUREINFO_IMAGE_SIZE, + 1, sourceProjectionObj || projectionObj, baseParams); + } + + /** + * Get the user-provided params, i.e. those passed to the constructor through + * the "params" option, and possibly updated using the updateParams method. + * @return {Object} Params. + * @api + */ + getParams() { + return this.params_; + } + + /** + * @inheritDoc + */ + getImageInternal(extent, resolution, pixelRatio, projection) { + + if (this.url_ === undefined) { + return null; + } + + resolution = this.findNearestResolution(resolution); + + if (pixelRatio != 1 && (!this.hidpi_ || this.serverType_ === undefined)) { + pixelRatio = 1; + } + + const imageResolution = resolution / pixelRatio; + + const center = getCenter(extent); + const viewWidth = Math.ceil(getWidth(extent) / imageResolution); + const viewHeight = Math.ceil(getHeight(extent) / imageResolution); + const viewExtent = getForViewAndSize(center, imageResolution, 0, + [viewWidth, viewHeight]); + const requestWidth = Math.ceil(this.ratio_ * getWidth(extent) / imageResolution); + const requestHeight = Math.ceil(this.ratio_ * getHeight(extent) / imageResolution); + const requestExtent = getForViewAndSize(center, imageResolution, 0, + [requestWidth, requestHeight]); + + const image = this.image_; + if (image && + this.renderedRevision_ == this.getRevision() && + image.getResolution() == resolution && + image.getPixelRatio() == pixelRatio && + containsExtent(image.getExtent(), viewExtent)) { + return image; + } + + const params = { + 'SERVICE': 'WMS', + 'VERSION': DEFAULT_WMS_VERSION, + 'REQUEST': 'GetMap', + 'FORMAT': 'image/png', + 'TRANSPARENT': true + }; + assign(params, this.params_); + + this.imageSize_[0] = Math.round(getWidth(requestExtent) / imageResolution); + this.imageSize_[1] = Math.round(getHeight(requestExtent) / imageResolution); + + const url = this.getRequestUrl_(requestExtent, this.imageSize_, pixelRatio, + projection, params); + + this.image_ = new ImageWrapper(requestExtent, resolution, pixelRatio, + url, this.crossOrigin_, this.imageLoadFunction_); + + this.renderedRevision_ = this.getRevision(); + + listen(this.image_, EventType.CHANGE, + this.handleImageChange, this); + + return this.image_; + + } + + /** + * Return the image load function of the source. + * @return {module:ol/Image~LoadFunction} The image load function. + * @api + */ + getImageLoadFunction() { + return this.imageLoadFunction_; + } + + /** + * @param {module:ol/extent~Extent} extent Extent. + * @param {module:ol/size~Size} size Size. + * @param {number} pixelRatio Pixel ratio. + * @param {module:ol/proj/Projection} projection Projection. + * @param {Object} params Params. + * @return {string} Request URL. + * @private + */ + getRequestUrl_(extent, size, pixelRatio, projection, params) { + + assert(this.url_ !== undefined, 9); // `url` must be configured or set using `#setUrl()` + + params[this.v13_ ? 'CRS' : 'SRS'] = projection.getCode(); + + if (!('STYLES' in this.params_)) { + params['STYLES'] = ''; + } + + if (pixelRatio != 1) { + switch (this.serverType_) { + case WMSServerType.GEOSERVER: + const dpi = (90 * pixelRatio + 0.5) | 0; + if ('FORMAT_OPTIONS' in params) { + params['FORMAT_OPTIONS'] += ';dpi:' + dpi; + } else { + params['FORMAT_OPTIONS'] = 'dpi:' + dpi; + } + break; + case WMSServerType.MAPSERVER: + params['MAP_RESOLUTION'] = 90 * pixelRatio; + break; + case WMSServerType.CARMENTA_SERVER: + case WMSServerType.QGIS: + params['DPI'] = 90 * pixelRatio; + break; + default: + assert(false, 8); // Unknown `serverType` configured + break; + } + } + + params['WIDTH'] = size[0]; + params['HEIGHT'] = size[1]; + + const axisOrientation = projection.getAxisOrientation(); + let bbox; + if (this.v13_ && axisOrientation.substr(0, 2) == 'ne') { + bbox = [extent[1], extent[0], extent[3], extent[2]]; + } else { + bbox = extent; + } + params['BBOX'] = bbox.join(','); + + return appendParams(/** @type {string} */ (this.url_), params); + } + + /** + * Return the URL used for this WMS source. + * @return {string|undefined} URL. + * @api + */ + getUrl() { + return this.url_; + } + + /** + * Set the image load function of the source. + * @param {module:ol/Image~LoadFunction} imageLoadFunction Image load function. + * @api + */ + setImageLoadFunction(imageLoadFunction) { + this.image_ = null; + this.imageLoadFunction_ = imageLoadFunction; + this.changed(); + } + + /** + * Set the URL to use for requests. + * @param {string|undefined} url URL. + * @api + */ + setUrl(url) { + if (url != this.url_) { + this.url_ = url; + this.image_ = null; + this.changed(); + } + } + + /** + * Update the user-provided params. + * @param {Object} params Params. + * @api + */ + updateParams(params) { + assign(this.params_, params); + this.updateV13_(); + this.image_ = null; + this.changed(); + } /** * @private - * @type {?string} */ - this.crossOrigin_ = - options.crossOrigin !== undefined ? options.crossOrigin : null; - - /** - * @private - * @type {string|undefined} - */ - this.url_ = options.url; - - /** - * @private - * @type {module:ol/Image~LoadFunction} - */ - this.imageLoadFunction_ = options.imageLoadFunction !== undefined ? - options.imageLoadFunction : defaultImageLoadFunction; - - /** - * @private - * @type {!Object} - */ - this.params_ = options.params || {}; - - /** - * @private - * @type {boolean} - */ - this.v13_ = true; - this.updateV13_(); - - /** - * @private - * @type {module:ol/source/WMSServerType|undefined} - */ - this.serverType_ = /** @type {module:ol/source/WMSServerType|undefined} */ (options.serverType); - - /** - * @private - * @type {boolean} - */ - this.hidpi_ = options.hidpi !== undefined ? options.hidpi : true; - - /** - * @private - * @type {module:ol/Image} - */ - this.image_ = null; - - /** - * @private - * @type {module:ol/size~Size} - */ - this.imageSize_ = [0, 0]; - - /** - * @private - * @type {number} - */ - this.renderedRevision_ = 0; - - /** - * @private - * @type {number} - */ - this.ratio_ = options.ratio !== undefined ? options.ratio : 1.5; - -}; + updateV13_() { + const version = this.params_['VERSION'] || DEFAULT_WMS_VERSION; + this.v13_ = compareVersions(version, '1.3') >= 0; + } +} inherits(ImageWMS, ImageSource); @@ -144,253 +387,4 @@ inherits(ImageWMS, ImageSource); const GETFEATUREINFO_IMAGE_SIZE = [101, 101]; -/** - * Return the GetFeatureInfo URL for the passed coordinate, resolution, and - * projection. Return `undefined` if the GetFeatureInfo URL cannot be - * constructed. - * @param {module:ol/coordinate~Coordinate} coordinate Coordinate. - * @param {number} resolution Resolution. - * @param {module:ol/proj~ProjectionLike} projection Projection. - * @param {!Object} params GetFeatureInfo params. `INFO_FORMAT` at least should - * be provided. If `QUERY_LAYERS` is not provided then the layers specified - * in the `LAYERS` parameter will be used. `VERSION` should not be - * specified here. - * @return {string|undefined} GetFeatureInfo URL. - * @api - */ -ImageWMS.prototype.getGetFeatureInfoUrl = function(coordinate, resolution, projection, params) { - if (this.url_ === undefined) { - return undefined; - } - const projectionObj = getProjection(projection); - const sourceProjectionObj = this.getProjection(); - - if (sourceProjectionObj && sourceProjectionObj !== projectionObj) { - resolution = calculateSourceResolution(sourceProjectionObj, projectionObj, coordinate, resolution); - coordinate = transform(coordinate, projectionObj, sourceProjectionObj); - } - - const extent = getForViewAndSize(coordinate, resolution, 0, - GETFEATUREINFO_IMAGE_SIZE); - - const baseParams = { - 'SERVICE': 'WMS', - 'VERSION': DEFAULT_WMS_VERSION, - 'REQUEST': 'GetFeatureInfo', - 'FORMAT': 'image/png', - 'TRANSPARENT': true, - 'QUERY_LAYERS': this.params_['LAYERS'] - }; - assign(baseParams, this.params_, params); - - const x = Math.floor((coordinate[0] - extent[0]) / resolution); - const y = Math.floor((extent[3] - coordinate[1]) / resolution); - baseParams[this.v13_ ? 'I' : 'X'] = x; - baseParams[this.v13_ ? 'J' : 'Y'] = y; - - return this.getRequestUrl_( - extent, GETFEATUREINFO_IMAGE_SIZE, - 1, sourceProjectionObj || projectionObj, baseParams); -}; - - -/** - * Get the user-provided params, i.e. those passed to the constructor through - * the "params" option, and possibly updated using the updateParams method. - * @return {Object} Params. - * @api - */ -ImageWMS.prototype.getParams = function() { - return this.params_; -}; - - -/** - * @inheritDoc - */ -ImageWMS.prototype.getImageInternal = function(extent, resolution, pixelRatio, projection) { - - if (this.url_ === undefined) { - return null; - } - - resolution = this.findNearestResolution(resolution); - - if (pixelRatio != 1 && (!this.hidpi_ || this.serverType_ === undefined)) { - pixelRatio = 1; - } - - const imageResolution = resolution / pixelRatio; - - const center = getCenter(extent); - const viewWidth = Math.ceil(getWidth(extent) / imageResolution); - const viewHeight = Math.ceil(getHeight(extent) / imageResolution); - const viewExtent = getForViewAndSize(center, imageResolution, 0, - [viewWidth, viewHeight]); - const requestWidth = Math.ceil(this.ratio_ * getWidth(extent) / imageResolution); - const requestHeight = Math.ceil(this.ratio_ * getHeight(extent) / imageResolution); - const requestExtent = getForViewAndSize(center, imageResolution, 0, - [requestWidth, requestHeight]); - - const image = this.image_; - if (image && - this.renderedRevision_ == this.getRevision() && - image.getResolution() == resolution && - image.getPixelRatio() == pixelRatio && - containsExtent(image.getExtent(), viewExtent)) { - return image; - } - - const params = { - 'SERVICE': 'WMS', - 'VERSION': DEFAULT_WMS_VERSION, - 'REQUEST': 'GetMap', - 'FORMAT': 'image/png', - 'TRANSPARENT': true - }; - assign(params, this.params_); - - this.imageSize_[0] = Math.round(getWidth(requestExtent) / imageResolution); - this.imageSize_[1] = Math.round(getHeight(requestExtent) / imageResolution); - - const url = this.getRequestUrl_(requestExtent, this.imageSize_, pixelRatio, - projection, params); - - this.image_ = new ImageWrapper(requestExtent, resolution, pixelRatio, - url, this.crossOrigin_, this.imageLoadFunction_); - - this.renderedRevision_ = this.getRevision(); - - listen(this.image_, EventType.CHANGE, - this.handleImageChange, this); - - return this.image_; - -}; - - -/** - * Return the image load function of the source. - * @return {module:ol/Image~LoadFunction} The image load function. - * @api - */ -ImageWMS.prototype.getImageLoadFunction = function() { - return this.imageLoadFunction_; -}; - - -/** - * @param {module:ol/extent~Extent} extent Extent. - * @param {module:ol/size~Size} size Size. - * @param {number} pixelRatio Pixel ratio. - * @param {module:ol/proj/Projection} projection Projection. - * @param {Object} params Params. - * @return {string} Request URL. - * @private - */ -ImageWMS.prototype.getRequestUrl_ = function(extent, size, pixelRatio, projection, params) { - - assert(this.url_ !== undefined, 9); // `url` must be configured or set using `#setUrl()` - - params[this.v13_ ? 'CRS' : 'SRS'] = projection.getCode(); - - if (!('STYLES' in this.params_)) { - params['STYLES'] = ''; - } - - if (pixelRatio != 1) { - switch (this.serverType_) { - case WMSServerType.GEOSERVER: - const dpi = (90 * pixelRatio + 0.5) | 0; - if ('FORMAT_OPTIONS' in params) { - params['FORMAT_OPTIONS'] += ';dpi:' + dpi; - } else { - params['FORMAT_OPTIONS'] = 'dpi:' + dpi; - } - break; - case WMSServerType.MAPSERVER: - params['MAP_RESOLUTION'] = 90 * pixelRatio; - break; - case WMSServerType.CARMENTA_SERVER: - case WMSServerType.QGIS: - params['DPI'] = 90 * pixelRatio; - break; - default: - assert(false, 8); // Unknown `serverType` configured - break; - } - } - - params['WIDTH'] = size[0]; - params['HEIGHT'] = size[1]; - - const axisOrientation = projection.getAxisOrientation(); - let bbox; - if (this.v13_ && axisOrientation.substr(0, 2) == 'ne') { - bbox = [extent[1], extent[0], extent[3], extent[2]]; - } else { - bbox = extent; - } - params['BBOX'] = bbox.join(','); - - return appendParams(/** @type {string} */ (this.url_), params); -}; - - -/** - * Return the URL used for this WMS source. - * @return {string|undefined} URL. - * @api - */ -ImageWMS.prototype.getUrl = function() { - return this.url_; -}; - - -/** - * Set the image load function of the source. - * @param {module:ol/Image~LoadFunction} imageLoadFunction Image load function. - * @api - */ -ImageWMS.prototype.setImageLoadFunction = function(imageLoadFunction) { - this.image_ = null; - this.imageLoadFunction_ = imageLoadFunction; - this.changed(); -}; - - -/** - * Set the URL to use for requests. - * @param {string|undefined} url URL. - * @api - */ -ImageWMS.prototype.setUrl = function(url) { - if (url != this.url_) { - this.url_ = url; - this.image_ = null; - this.changed(); - } -}; - - -/** - * Update the user-provided params. - * @param {Object} params Params. - * @api - */ -ImageWMS.prototype.updateParams = function(params) { - assign(this.params_, params); - this.updateV13_(); - this.image_ = null; - this.changed(); -}; - - -/** - * @private - */ -ImageWMS.prototype.updateV13_ = function() { - const version = this.params_['VERSION'] || DEFAULT_WMS_VERSION; - this.v13_ = compareVersions(version, '1.3') >= 0; -}; export default ImageWMS; diff --git a/src/ol/source/Raster.js b/src/ol/source/Raster.js index c848144181..74a76700af 100644 --- a/src/ol/source/Raster.js +++ b/src/ol/source/Raster.js @@ -144,276 +144,279 @@ inherits(RasterSourceEvent, Event); * @param {module:ol/source/Raster~Options=} options Options. * @api */ -const RasterSource = function(options) { +class RasterSource { + constructor(options) { - /** - * @private - * @type {*} - */ - this.worker_ = null; + /** + * @private + * @type {*} + */ + this.worker_ = null; - /** - * @private - * @type {module:ol/source/Raster~RasterOperationType} - */ - this.operationType_ = options.operationType !== undefined ? - options.operationType : RasterOperationType.PIXEL; + /** + * @private + * @type {module:ol/source/Raster~RasterOperationType} + */ + this.operationType_ = options.operationType !== undefined ? + options.operationType : RasterOperationType.PIXEL; - /** - * @private - * @type {number} - */ - this.threads_ = options.threads !== undefined ? options.threads : 1; + /** + * @private + * @type {number} + */ + this.threads_ = options.threads !== undefined ? options.threads : 1; - /** - * @private - * @type {Array.} - */ - this.renderers_ = createRenderers(options.sources); + /** + * @private + * @type {Array.} + */ + this.renderers_ = createRenderers(options.sources); - for (let r = 0, rr = this.renderers_.length; r < rr; ++r) { - listen(this.renderers_[r], EventType.CHANGE, - this.changed, this); - } - - /** - * @private - * @type {module:ol/TileQueue} - */ - this.tileQueue_ = new TileQueue( - function() { - return 1; - }, - this.changed.bind(this)); - - const layerStatesArray = getLayerStatesArray(this.renderers_); - const layerStates = {}; - for (let i = 0, ii = layerStatesArray.length; i < ii; ++i) { - layerStates[getUid(layerStatesArray[i].layer)] = layerStatesArray[i]; - } - - /** - * The most recently requested frame state. - * @type {module:ol/PluggableMap~FrameState} - * @private - */ - this.requestedFrameState_; - - /** - * The most recently rendered image canvas. - * @type {module:ol/ImageCanvas} - * @private - */ - this.renderedImageCanvas_ = null; - - /** - * The most recently rendered revision. - * @type {number} - */ - this.renderedRevision_; - - /** - * @private - * @type {module:ol/PluggableMap~FrameState} - */ - this.frameState_ = { - animate: false, - coordinateToPixelTransform: createTransform(), - extent: null, - focus: null, - index: 0, - layerStates: layerStates, - layerStatesArray: layerStatesArray, - pixelRatio: 1, - pixelToCoordinateTransform: createTransform(), - postRenderFunctions: [], - size: [0, 0], - skippedFeatureUids: {}, - tileQueue: this.tileQueue_, - time: Date.now(), - usedTiles: {}, - viewState: /** @type {module:ol/View~State} */ ({ - rotation: 0 - }), - viewHints: [], - wantedTiles: {} - }; - - ImageSource.call(this, {}); - - if (options.operation !== undefined) { - this.setOperation(options.operation, options.lib); - } - -}; - -inherits(RasterSource, ImageSource); - - -/** - * Set the operation. - * @param {module:ol/source/Raster~Operation} operation New operation. - * @param {Object=} opt_lib Functions that will be available to operations run - * in a worker. - * @api - */ -RasterSource.prototype.setOperation = function(operation, opt_lib) { - this.worker_ = new Processor({ - operation: operation, - imageOps: this.operationType_ === RasterOperationType.IMAGE, - queue: 1, - lib: opt_lib, - threads: this.threads_ - }); - this.changed(); -}; - - -/** - * Update the stored frame state. - * @param {module:ol/extent~Extent} extent The view extent (in map units). - * @param {number} resolution The view resolution. - * @param {module:ol/proj/Projection} projection The view projection. - * @return {module:ol/PluggableMap~FrameState} The updated frame state. - * @private - */ -RasterSource.prototype.updateFrameState_ = function(extent, resolution, projection) { - - const frameState = /** @type {module:ol/PluggableMap~FrameState} */ (assign({}, this.frameState_)); - - frameState.viewState = /** @type {module:ol/View~State} */ (assign({}, frameState.viewState)); - - const center = getCenter(extent); - - frameState.extent = extent.slice(); - frameState.focus = center; - frameState.size[0] = Math.round(getWidth(extent) / resolution); - frameState.size[1] = Math.round(getHeight(extent) / resolution); - frameState.time = Date.now(); - frameState.animate = false; - - const viewState = frameState.viewState; - viewState.center = center; - viewState.projection = projection; - viewState.resolution = resolution; - return frameState; -}; - - -/** - * Determine if all sources are ready. - * @return {boolean} All sources are ready. - * @private - */ -RasterSource.prototype.allSourcesReady_ = function() { - let ready = true; - let source; - for (let i = 0, ii = this.renderers_.length; i < ii; ++i) { - source = this.renderers_[i].getLayer().getSource(); - if (source.getState() !== SourceState.READY) { - ready = false; - break; + for (let r = 0, rr = this.renderers_.length; r < rr; ++r) { + listen(this.renderers_[r], EventType.CHANGE, + this.changed, this); } - } - return ready; -}; + /** + * @private + * @type {module:ol/TileQueue} + */ + this.tileQueue_ = new TileQueue( + function() { + return 1; + }, + this.changed.bind(this)); -/** - * @inheritDoc - */ -RasterSource.prototype.getImage = function(extent, resolution, pixelRatio, projection) { - if (!this.allSourcesReady_()) { - return null; - } - - const frameState = this.updateFrameState_(extent, resolution, projection); - this.requestedFrameState_ = frameState; - - // check if we can't reuse the existing ol/ImageCanvas - if (this.renderedImageCanvas_) { - const renderedResolution = this.renderedImageCanvas_.getResolution(); - const renderedExtent = this.renderedImageCanvas_.getExtent(); - if (resolution !== renderedResolution || !equals(extent, renderedExtent)) { - this.renderedImageCanvas_ = null; + const layerStatesArray = getLayerStatesArray(this.renderers_); + const layerStates = {}; + for (let i = 0, ii = layerStatesArray.length; i < ii; ++i) { + layerStates[getUid(layerStatesArray[i].layer)] = layerStatesArray[i]; } + + /** + * The most recently requested frame state. + * @type {module:ol/PluggableMap~FrameState} + * @private + */ + this.requestedFrameState_; + + /** + * The most recently rendered image canvas. + * @type {module:ol/ImageCanvas} + * @private + */ + this.renderedImageCanvas_ = null; + + /** + * The most recently rendered revision. + * @type {number} + */ + this.renderedRevision_; + + /** + * @private + * @type {module:ol/PluggableMap~FrameState} + */ + this.frameState_ = { + animate: false, + coordinateToPixelTransform: createTransform(), + extent: null, + focus: null, + index: 0, + layerStates: layerStates, + layerStatesArray: layerStatesArray, + pixelRatio: 1, + pixelToCoordinateTransform: createTransform(), + postRenderFunctions: [], + size: [0, 0], + skippedFeatureUids: {}, + tileQueue: this.tileQueue_, + time: Date.now(), + usedTiles: {}, + viewState: /** @type {module:ol/View~State} */ ({ + rotation: 0 + }), + viewHints: [], + wantedTiles: {} + }; + + ImageSource.call(this, {}); + + if (options.operation !== undefined) { + this.setOperation(options.operation, options.lib); + } + } - if (!this.renderedImageCanvas_ || this.getRevision() !== this.renderedRevision_) { - this.processSources_(); + /** + * Set the operation. + * @param {module:ol/source/Raster~Operation} operation New operation. + * @param {Object=} opt_lib Functions that will be available to operations run + * in a worker. + * @api + */ + setOperation(operation, opt_lib) { + this.worker_ = new Processor({ + operation: operation, + imageOps: this.operationType_ === RasterOperationType.IMAGE, + queue: 1, + lib: opt_lib, + threads: this.threads_ + }); + this.changed(); } - frameState.tileQueue.loadMoreTiles(16, 16); + /** + * Update the stored frame state. + * @param {module:ol/extent~Extent} extent The view extent (in map units). + * @param {number} resolution The view resolution. + * @param {module:ol/proj/Projection} projection The view projection. + * @return {module:ol/PluggableMap~FrameState} The updated frame state. + * @private + */ + updateFrameState_(extent, resolution, projection) { - if (frameState.animate) { - requestAnimationFrame(this.changed.bind(this)); + const frameState = /** @type {module:ol/PluggableMap~FrameState} */ (assign({}, this.frameState_)); + + frameState.viewState = /** @type {module:ol/View~State} */ (assign({}, frameState.viewState)); + + const center = getCenter(extent); + + frameState.extent = extent.slice(); + frameState.focus = center; + frameState.size[0] = Math.round(getWidth(extent) / resolution); + frameState.size[1] = Math.round(getHeight(extent) / resolution); + frameState.time = Date.now(); + frameState.animate = false; + + const viewState = frameState.viewState; + viewState.center = center; + viewState.projection = projection; + viewState.resolution = resolution; + return frameState; } - return this.renderedImageCanvas_; -}; + /** + * Determine if all sources are ready. + * @return {boolean} All sources are ready. + * @private + */ + allSourcesReady_() { + let ready = true; + let source; + for (let i = 0, ii = this.renderers_.length; i < ii; ++i) { + source = this.renderers_[i].getLayer().getSource(); + if (source.getState() !== SourceState.READY) { + ready = false; + break; + } + } + return ready; + } + /** + * @inheritDoc + */ + getImage(extent, resolution, pixelRatio, projection) { + if (!this.allSourcesReady_()) { + return null; + } -/** - * Start processing source data. - * @private - */ -RasterSource.prototype.processSources_ = function() { - const frameState = this.requestedFrameState_; - const len = this.renderers_.length; - const imageDatas = new Array(len); - for (let i = 0; i < len; ++i) { - const imageData = getImageData( - this.renderers_[i], frameState, frameState.layerStatesArray[i]); - if (imageData) { - imageDatas[i] = imageData; - } else { + const frameState = this.updateFrameState_(extent, resolution, projection); + this.requestedFrameState_ = frameState; + + // check if we can't reuse the existing ol/ImageCanvas + if (this.renderedImageCanvas_) { + const renderedResolution = this.renderedImageCanvas_.getResolution(); + const renderedExtent = this.renderedImageCanvas_.getExtent(); + if (resolution !== renderedResolution || !equals(extent, renderedExtent)) { + this.renderedImageCanvas_ = null; + } + } + + if (!this.renderedImageCanvas_ || this.getRevision() !== this.renderedRevision_) { + this.processSources_(); + } + + frameState.tileQueue.loadMoreTiles(16, 16); + + if (frameState.animate) { + requestAnimationFrame(this.changed.bind(this)); + } + + return this.renderedImageCanvas_; + } + + /** + * Start processing source data. + * @private + */ + processSources_() { + const frameState = this.requestedFrameState_; + const len = this.renderers_.length; + const imageDatas = new Array(len); + for (let i = 0; i < len; ++i) { + const imageData = getImageData( + this.renderers_[i], frameState, frameState.layerStatesArray[i]); + if (imageData) { + imageDatas[i] = imageData; + } else { + return; + } + } + + const data = {}; + this.dispatchEvent(new RasterSourceEvent(RasterEventType.BEFOREOPERATIONS, frameState, data)); + this.worker_.process(imageDatas, data, this.onWorkerComplete_.bind(this, frameState)); + } + + /** + * Called when pixel processing is complete. + * @param {module:ol/PluggableMap~FrameState} frameState The frame state. + * @param {Error} err Any error during processing. + * @param {ImageData} output The output image data. + * @param {Object} data The user data. + * @private + */ + onWorkerComplete_(frameState, err, output, data) { + if (err || !output) { return; } + + // do nothing if extent or resolution changed + const extent = frameState.extent; + const resolution = frameState.viewState.resolution; + if (resolution !== this.requestedFrameState_.viewState.resolution || + !equals(extent, this.requestedFrameState_.extent)) { + return; + } + + let context; + if (this.renderedImageCanvas_) { + context = this.renderedImageCanvas_.getImage().getContext('2d'); + } else { + const width = Math.round(getWidth(extent) / resolution); + const height = Math.round(getHeight(extent) / resolution); + context = createCanvasContext2D(width, height); + this.renderedImageCanvas_ = new ImageCanvas(extent, resolution, 1, context.canvas); + } + context.putImageData(output, 0, 0); + + this.changed(); + this.renderedRevision_ = this.getRevision(); + + this.dispatchEvent(new RasterSourceEvent(RasterEventType.AFTEROPERATIONS, frameState, data)); } - const data = {}; - this.dispatchEvent(new RasterSourceEvent(RasterEventType.BEFOREOPERATIONS, frameState, data)); - this.worker_.process(imageDatas, data, this.onWorkerComplete_.bind(this, frameState)); -}; - - -/** - * Called when pixel processing is complete. - * @param {module:ol/PluggableMap~FrameState} frameState The frame state. - * @param {Error} err Any error during processing. - * @param {ImageData} output The output image data. - * @param {Object} data The user data. - * @private - */ -RasterSource.prototype.onWorkerComplete_ = function(frameState, err, output, data) { - if (err || !output) { - return; + /** + * @override + */ + getImageInternal() { + return null; // not implemented } +} - // do nothing if extent or resolution changed - const extent = frameState.extent; - const resolution = frameState.viewState.resolution; - if (resolution !== this.requestedFrameState_.viewState.resolution || - !equals(extent, this.requestedFrameState_.extent)) { - return; - } - - let context; - if (this.renderedImageCanvas_) { - context = this.renderedImageCanvas_.getImage().getContext('2d'); - } else { - const width = Math.round(getWidth(extent) / resolution); - const height = Math.round(getHeight(extent) / resolution); - context = createCanvasContext2D(width, height); - this.renderedImageCanvas_ = new ImageCanvas(extent, resolution, 1, context.canvas); - } - context.putImageData(output, 0, 0); - - this.changed(); - this.renderedRevision_ = this.getRevision(); - - this.dispatchEvent(new RasterSourceEvent(RasterEventType.AFTEROPERATIONS, frameState, data)); -}; +inherits(RasterSource, ImageSource); /** @@ -522,12 +525,4 @@ function createTileRenderer(source) { } -/** - * @override - */ -RasterSource.prototype.getImageInternal = function() { - return null; // not implemented -}; - - export default RasterSource; diff --git a/src/ol/source/Source.js b/src/ol/source/Source.js index a07c81c15f..c16e41a232 100644 --- a/src/ol/source/Source.js +++ b/src/ol/source/Source.js @@ -51,63 +51,134 @@ import SourceState from '../source/State.js'; * @param {module:ol/source/Source~Options} options Source options. * @api */ -const Source = function(options) { +class Source { + constructor(options) { - BaseObject.call(this); + BaseObject.call(this); - /** - * @private - * @type {module:ol/proj/Projection} - */ - this.projection_ = getProjection(options.projection); + /** + * @private + * @type {module:ol/proj/Projection} + */ + this.projection_ = getProjection(options.projection); - /** - * @private - * @type {?module:ol/source/Source~Attribution} - */ - this.attributions_ = this.adaptAttributions_(options.attributions); + /** + * @private + * @type {?module:ol/source/Source~Attribution} + */ + this.attributions_ = this.adaptAttributions_(options.attributions); - /** - * @private - * @type {module:ol/source/State} - */ - this.state_ = options.state !== undefined ? - options.state : SourceState.READY; + /** + * @private + * @type {module:ol/source/State} + */ + this.state_ = options.state !== undefined ? + options.state : SourceState.READY; - /** - * @private - * @type {boolean} - */ - this.wrapX_ = options.wrapX !== undefined ? options.wrapX : false; + /** + * @private + * @type {boolean} + */ + this.wrapX_ = options.wrapX !== undefined ? options.wrapX : false; -}; + } + + /** + * Turns the attributions option into an attributions function. + * @param {module:ol/source/Source~AttributionLike|undefined} attributionLike The attribution option. + * @return {?module:ol/source/Source~Attribution} An attribution function (or null). + */ + adaptAttributions_(attributionLike) { + if (!attributionLike) { + return null; + } + if (Array.isArray(attributionLike)) { + return function(frameState) { + return attributionLike; + }; + } + + if (typeof attributionLike === 'function') { + return attributionLike; + } + + return function(frameState) { + return [attributionLike]; + }; + } + + /** + * Get the attribution function for the source. + * @return {?module:ol/source/Source~Attribution} Attribution function. + */ + getAttributions() { + return this.attributions_; + } + + /** + * Get the projection of the source. + * @return {module:ol/proj/Projection} Projection. + * @api + */ + getProjection() { + return this.projection_; + } + + /** + * @abstract + * @return {Array.|undefined} Resolutions. + */ + getResolutions() {} + + /** + * Get the state of the source, see {@link module:ol/source/State~State} for possible states. + * @return {module:ol/source/State} State. + * @api + */ + getState() { + return this.state_; + } + + /** + * @return {boolean|undefined} Wrap X. + */ + getWrapX() { + return this.wrapX_; + } + + /** + * Refreshes the source and finally dispatches a 'change' event. + * @api + */ + refresh() { + this.changed(); + } + + /** + * Set the attributions of the source. + * @param {module:ol/source/Source~AttributionLike|undefined} attributions Attributions. + * Can be passed as `string`, `Array`, `{@link module:ol/source/Source~Attribution}`, + * or `undefined`. + * @api + */ + setAttributions(attributions) { + this.attributions_ = this.adaptAttributions_(attributions); + this.changed(); + } + + /** + * Set the state of the source. + * @param {module:ol/source/State} state State. + * @protected + */ + setState(state) { + this.state_ = state; + this.changed(); + } +} inherits(Source, BaseObject); -/** - * Turns the attributions option into an attributions function. - * @param {module:ol/source/Source~AttributionLike|undefined} attributionLike The attribution option. - * @return {?module:ol/source/Source~Attribution} An attribution function (or null). - */ -Source.prototype.adaptAttributions_ = function(attributionLike) { - if (!attributionLike) { - return null; - } - if (Array.isArray(attributionLike)) { - return function(frameState) { - return attributionLike; - }; - } - - if (typeof attributionLike === 'function') { - return attributionLike; - } - - return function(frameState) { - return [attributionLike]; - }; -}; - /** * @param {module:ol/coordinate~Coordinate} coordinate Coordinate. * @param {number} resolution Resolution. @@ -121,79 +192,4 @@ Source.prototype.adaptAttributions_ = function(attributionLike) { Source.prototype.forEachFeatureAtCoordinate = UNDEFINED; -/** - * Get the attribution function for the source. - * @return {?module:ol/source/Source~Attribution} Attribution function. - */ -Source.prototype.getAttributions = function() { - return this.attributions_; -}; - - -/** - * Get the projection of the source. - * @return {module:ol/proj/Projection} Projection. - * @api - */ -Source.prototype.getProjection = function() { - return this.projection_; -}; - - -/** - * @abstract - * @return {Array.|undefined} Resolutions. - */ -Source.prototype.getResolutions = function() {}; - - -/** - * Get the state of the source, see {@link module:ol/source/State~State} for possible states. - * @return {module:ol/source/State} State. - * @api - */ -Source.prototype.getState = function() { - return this.state_; -}; - - -/** - * @return {boolean|undefined} Wrap X. - */ -Source.prototype.getWrapX = function() { - return this.wrapX_; -}; - - -/** - * Refreshes the source and finally dispatches a 'change' event. - * @api - */ -Source.prototype.refresh = function() { - this.changed(); -}; - - -/** - * Set the attributions of the source. - * @param {module:ol/source/Source~AttributionLike|undefined} attributions Attributions. - * Can be passed as `string`, `Array`, `{@link module:ol/source/Source~Attribution}`, - * or `undefined`. - * @api - */ -Source.prototype.setAttributions = function(attributions) { - this.attributions_ = this.adaptAttributions_(attributions); - this.changed(); -}; - - -/** - * Set the state of the source. - * @param {module:ol/source/State} state State. - * @protected - */ -Source.prototype.setState = function(state) { - this.state_ = state; - this.changed(); -}; export default Source; diff --git a/src/ol/source/Tile.js b/src/ol/source/Tile.js index 1f3d7f44ae..7435278073 100644 --- a/src/ol/source/Tile.js +++ b/src/ol/source/Tile.js @@ -39,277 +39,263 @@ import {wrapX, getForProjection as getTileGridForProjection} from '../tilegrid.j * @param {module:ol/source/Tile~Options=} options SourceTile source options. * @api */ -const TileSource = function(options) { +class TileSource { + constructor(options) { - Source.call(this, { - attributions: options.attributions, - extent: options.extent, - projection: options.projection, - state: options.state, - wrapX: options.wrapX - }); + Source.call(this, { + attributions: options.attributions, + extent: options.extent, + projection: options.projection, + state: options.state, + wrapX: options.wrapX + }); - /** - * @private - * @type {boolean} - */ - this.opaque_ = options.opaque !== undefined ? options.opaque : false; + /** + * @private + * @type {boolean} + */ + this.opaque_ = options.opaque !== undefined ? options.opaque : false; - /** - * @private - * @type {number} - */ - this.tilePixelRatio_ = options.tilePixelRatio !== undefined ? - options.tilePixelRatio : 1; + /** + * @private + * @type {number} + */ + this.tilePixelRatio_ = options.tilePixelRatio !== undefined ? + options.tilePixelRatio : 1; - /** - * @protected - * @type {module:ol/tilegrid/TileGrid} - */ - this.tileGrid = options.tileGrid !== undefined ? options.tileGrid : null; + /** + * @protected + * @type {module:ol/tilegrid/TileGrid} + */ + this.tileGrid = options.tileGrid !== undefined ? options.tileGrid : null; - /** - * @protected - * @type {module:ol/TileCache} - */ - this.tileCache = new TileCache(options.cacheSize); + /** + * @protected + * @type {module:ol/TileCache} + */ + this.tileCache = new TileCache(options.cacheSize); - /** - * @protected - * @type {module:ol/size~Size} - */ - this.tmpSize = [0, 0]; + /** + * @protected + * @type {module:ol/size~Size} + */ + this.tmpSize = [0, 0]; - /** - * @private - * @type {string} - */ - this.key_ = ''; + /** + * @private + * @type {string} + */ + this.key_ = ''; - /** - * @protected - * @type {module:ol/Tile~Options} - */ - this.tileOptions = {transition: options.transition}; + /** + * @protected + * @type {module:ol/Tile~Options} + */ + this.tileOptions = {transition: options.transition}; -}; - -inherits(TileSource, Source); - - -/** - * @return {boolean} Can expire cache. - */ -TileSource.prototype.canExpireCache = function() { - return this.tileCache.canExpireCache(); -}; - - -/** - * @param {module:ol/proj/Projection} projection Projection. - * @param {!Object.} usedTiles Used tiles. - */ -TileSource.prototype.expireCache = function(projection, usedTiles) { - const tileCache = this.getTileCacheForProjection(projection); - if (tileCache) { - tileCache.expireCache(usedTiles); - } -}; - - -/** - * @param {module:ol/proj/Projection} projection Projection. - * @param {number} z Zoom level. - * @param {module:ol/TileRange} tileRange Tile range. - * @param {function(module:ol/Tile):(boolean|undefined)} callback Called with each - * loaded tile. If the callback returns `false`, the tile will not be - * considered loaded. - * @return {boolean} The tile range is fully covered with loaded tiles. - */ -TileSource.prototype.forEachLoadedTile = function(projection, z, tileRange, callback) { - const tileCache = this.getTileCacheForProjection(projection); - if (!tileCache) { - return false; } - let covered = true; - let tile, tileCoordKey, loaded; - for (let x = tileRange.minX; x <= tileRange.maxX; ++x) { - for (let y = tileRange.minY; y <= tileRange.maxY; ++y) { - tileCoordKey = getKeyZXY(z, x, y); - loaded = false; - if (tileCache.containsKey(tileCoordKey)) { - tile = /** @type {!module:ol/Tile} */ (tileCache.get(tileCoordKey)); - loaded = tile.getState() === TileState.LOADED; - if (loaded) { - loaded = (callback(tile) !== false); - } - } - if (!loaded) { - covered = false; - } + /** + * @return {boolean} Can expire cache. + */ + canExpireCache() { + return this.tileCache.canExpireCache(); + } + + /** + * @param {module:ol/proj/Projection} projection Projection. + * @param {!Object.} usedTiles Used tiles. + */ + expireCache(projection, usedTiles) { + const tileCache = this.getTileCacheForProjection(projection); + if (tileCache) { + tileCache.expireCache(usedTiles); } } - return covered; -}; + /** + * @param {module:ol/proj/Projection} projection Projection. + * @param {number} z Zoom level. + * @param {module:ol/TileRange} tileRange Tile range. + * @param {function(module:ol/Tile):(boolean|undefined)} callback Called with each + * loaded tile. If the callback returns `false`, the tile will not be + * considered loaded. + * @return {boolean} The tile range is fully covered with loaded tiles. + */ + forEachLoadedTile(projection, z, tileRange, callback) { + const tileCache = this.getTileCacheForProjection(projection); + if (!tileCache) { + return false; + } -/** - * @param {module:ol/proj/Projection} projection Projection. - * @return {number} Gutter. - */ -TileSource.prototype.getGutter = function(projection) { - return 0; -}; - - -/** - * Return the key to be used for all tiles in the source. - * @return {string} The key for all tiles. - * @protected - */ -TileSource.prototype.getKey = function() { - return this.key_; -}; - - -/** - * Set the value to be used as the key for all tiles in the source. - * @param {string} key The key for tiles. - * @protected - */ -TileSource.prototype.setKey = function(key) { - if (this.key_ !== key) { - this.key_ = key; - this.changed(); + let covered = true; + let tile, tileCoordKey, loaded; + for (let x = tileRange.minX; x <= tileRange.maxX; ++x) { + for (let y = tileRange.minY; y <= tileRange.maxY; ++y) { + tileCoordKey = getKeyZXY(z, x, y); + loaded = false; + if (tileCache.containsKey(tileCoordKey)) { + tile = /** @type {!module:ol/Tile} */ (tileCache.get(tileCoordKey)); + loaded = tile.getState() === TileState.LOADED; + if (loaded) { + loaded = (callback(tile) !== false); + } + } + if (!loaded) { + covered = false; + } + } + } + return covered; } -}; + /** + * @param {module:ol/proj/Projection} projection Projection. + * @return {number} Gutter. + */ + getGutter(projection) { + return 0; + } -/** - * @param {module:ol/proj/Projection} projection Projection. - * @return {boolean} Opaque. - */ -TileSource.prototype.getOpaque = function(projection) { - return this.opaque_; -}; + /** + * Return the key to be used for all tiles in the source. + * @return {string} The key for all tiles. + * @protected + */ + getKey() { + return this.key_; + } + /** + * Set the value to be used as the key for all tiles in the source. + * @param {string} key The key for tiles. + * @protected + */ + setKey(key) { + if (this.key_ !== key) { + this.key_ = key; + this.changed(); + } + } -/** - * @inheritDoc - */ -TileSource.prototype.getResolutions = function() { - return this.tileGrid.getResolutions(); -}; + /** + * @param {module:ol/proj/Projection} projection Projection. + * @return {boolean} Opaque. + */ + getOpaque(projection) { + return this.opaque_; + } + /** + * @inheritDoc + */ + getResolutions() { + return this.tileGrid.getResolutions(); + } -/** - * @abstract - * @param {number} z Tile coordinate z. - * @param {number} x Tile coordinate x. - * @param {number} y Tile coordinate y. - * @param {number} pixelRatio Pixel ratio. - * @param {module:ol/proj/Projection} projection Projection. - * @return {!module:ol/Tile} Tile. - */ -TileSource.prototype.getTile = function(z, x, y, pixelRatio, projection) {}; + /** + * @abstract + * @param {number} z Tile coordinate z. + * @param {number} x Tile coordinate x. + * @param {number} y Tile coordinate y. + * @param {number} pixelRatio Pixel ratio. + * @param {module:ol/proj/Projection} projection Projection. + * @return {!module:ol/Tile} Tile. + */ + getTile(z, x, y, pixelRatio, projection) {} - -/** - * Return the tile grid of the tile source. - * @return {module:ol/tilegrid/TileGrid} Tile grid. - * @api - */ -TileSource.prototype.getTileGrid = function() { - return this.tileGrid; -}; - - -/** - * @param {module:ol/proj/Projection} projection Projection. - * @return {!module:ol/tilegrid/TileGrid} Tile grid. - */ -TileSource.prototype.getTileGridForProjection = function(projection) { - if (!this.tileGrid) { - return getTileGridForProjection(projection); - } else { + /** + * Return the tile grid of the tile source. + * @return {module:ol/tilegrid/TileGrid} Tile grid. + * @api + */ + getTileGrid() { return this.tileGrid; } -}; - -/** - * @param {module:ol/proj/Projection} projection Projection. - * @return {module:ol/TileCache} Tile cache. - * @protected - */ -TileSource.prototype.getTileCacheForProjection = function(projection) { - const thisProj = this.getProjection(); - if (thisProj && !equivalent(thisProj, projection)) { - return null; - } else { - return this.tileCache; + /** + * @param {module:ol/proj/Projection} projection Projection. + * @return {!module:ol/tilegrid/TileGrid} Tile grid. + */ + getTileGridForProjection(projection) { + if (!this.tileGrid) { + return getTileGridForProjection(projection); + } else { + return this.tileGrid; + } } -}; - -/** - * Get the tile pixel ratio for this source. Subclasses may override this - * method, which is meant to return a supported pixel ratio that matches the - * provided `pixelRatio` as close as possible. - * @param {number} pixelRatio Pixel ratio. - * @return {number} Tile pixel ratio. - */ -TileSource.prototype.getTilePixelRatio = function(pixelRatio) { - return this.tilePixelRatio_; -}; - - -/** - * @param {number} z Z. - * @param {number} pixelRatio Pixel ratio. - * @param {module:ol/proj/Projection} projection Projection. - * @return {module:ol/size~Size} Tile size. - */ -TileSource.prototype.getTilePixelSize = function(z, pixelRatio, projection) { - const tileGrid = this.getTileGridForProjection(projection); - const tilePixelRatio = this.getTilePixelRatio(pixelRatio); - const tileSize = toSize(tileGrid.getTileSize(z), this.tmpSize); - if (tilePixelRatio == 1) { - return tileSize; - } else { - return scaleSize(tileSize, tilePixelRatio, this.tmpSize); + /** + * @param {module:ol/proj/Projection} projection Projection. + * @return {module:ol/TileCache} Tile cache. + * @protected + */ + getTileCacheForProjection(projection) { + const thisProj = this.getProjection(); + if (thisProj && !equivalent(thisProj, projection)) { + return null; + } else { + return this.tileCache; + } } -}; - -/** - * Returns a tile coordinate wrapped around the x-axis. When the tile coordinate - * is outside the resolution and extent range of the tile grid, `null` will be - * returned. - * @param {module:ol/tilecoord~TileCoord} tileCoord Tile coordinate. - * @param {module:ol/proj/Projection=} opt_projection Projection. - * @return {module:ol/tilecoord~TileCoord} Tile coordinate to be passed to the tileUrlFunction or - * null if no tile URL should be created for the passed `tileCoord`. - */ -TileSource.prototype.getTileCoordForTileUrlFunction = function(tileCoord, opt_projection) { - const projection = opt_projection !== undefined ? - opt_projection : this.getProjection(); - const tileGrid = this.getTileGridForProjection(projection); - if (this.getWrapX() && projection.isGlobal()) { - tileCoord = wrapX(tileGrid, tileCoord, projection); + /** + * Get the tile pixel ratio for this source. Subclasses may override this + * method, which is meant to return a supported pixel ratio that matches the + * provided `pixelRatio` as close as possible. + * @param {number} pixelRatio Pixel ratio. + * @return {number} Tile pixel ratio. + */ + getTilePixelRatio(pixelRatio) { + return this.tilePixelRatio_; } - return withinExtentAndZ(tileCoord, tileGrid) ? tileCoord : null; -}; + /** + * @param {number} z Z. + * @param {number} pixelRatio Pixel ratio. + * @param {module:ol/proj/Projection} projection Projection. + * @return {module:ol/size~Size} Tile size. + */ + getTilePixelSize(z, pixelRatio, projection) { + const tileGrid = this.getTileGridForProjection(projection); + const tilePixelRatio = this.getTilePixelRatio(pixelRatio); + const tileSize = toSize(tileGrid.getTileSize(z), this.tmpSize); + if (tilePixelRatio == 1) { + return tileSize; + } else { + return scaleSize(tileSize, tilePixelRatio, this.tmpSize); + } + } -/** - * @inheritDoc - */ -TileSource.prototype.refresh = function() { - this.tileCache.clear(); - this.changed(); -}; + /** + * Returns a tile coordinate wrapped around the x-axis. When the tile coordinate + * is outside the resolution and extent range of the tile grid, `null` will be + * returned. + * @param {module:ol/tilecoord~TileCoord} tileCoord Tile coordinate. + * @param {module:ol/proj/Projection=} opt_projection Projection. + * @return {module:ol/tilecoord~TileCoord} Tile coordinate to be passed to the tileUrlFunction or + * null if no tile URL should be created for the passed `tileCoord`. + */ + getTileCoordForTileUrlFunction(tileCoord, opt_projection) { + const projection = opt_projection !== undefined ? + opt_projection : this.getProjection(); + const tileGrid = this.getTileGridForProjection(projection); + if (this.getWrapX() && projection.isGlobal()) { + tileCoord = wrapX(tileGrid, tileCoord, projection); + } + return withinExtentAndZ(tileCoord, tileGrid) ? tileCoord : null; + } + + /** + * @inheritDoc + */ + refresh() { + this.tileCache.clear(); + this.changed(); + } +} + +inherits(TileSource, Source); /** diff --git a/src/ol/source/TileArcGISRest.js b/src/ol/source/TileArcGISRest.js index 5fa0056cbf..b06e72493c 100644 --- a/src/ol/source/TileArcGISRest.js +++ b/src/ol/source/TileArcGISRest.js @@ -64,162 +64,159 @@ import {appendParams} from '../uri.js'; * @param {module:ol/source/TileArcGISRest~Options=} opt_options Tile ArcGIS Rest options. * @api */ -const TileArcGISRest = function(opt_options) { +class TileArcGISRest { + constructor(opt_options) { - const options = opt_options || {}; + const options = opt_options || {}; - TileImage.call(this, { - attributions: options.attributions, - cacheSize: options.cacheSize, - crossOrigin: options.crossOrigin, - projection: options.projection, - reprojectionErrorThreshold: options.reprojectionErrorThreshold, - tileGrid: options.tileGrid, - tileLoadFunction: options.tileLoadFunction, - url: options.url, - urls: options.urls, - wrapX: options.wrapX !== undefined ? options.wrapX : true, - transition: options.transition - }); + TileImage.call(this, { + attributions: options.attributions, + cacheSize: options.cacheSize, + crossOrigin: options.crossOrigin, + projection: options.projection, + reprojectionErrorThreshold: options.reprojectionErrorThreshold, + tileGrid: options.tileGrid, + tileLoadFunction: options.tileLoadFunction, + url: options.url, + urls: options.urls, + wrapX: options.wrapX !== undefined ? options.wrapX : true, + transition: options.transition + }); + + /** + * @private + * @type {!Object} + */ + this.params_ = options.params || {}; + + /** + * @private + * @type {module:ol/extent~Extent} + */ + this.tmpExtent_ = createEmpty(); + + this.setKey(this.getKeyForParams_()); + } /** * @private - * @type {!Object} + * @return {string} The key for the current params. */ - this.params_ = options.params || {}; + getKeyForParams_() { + let i = 0; + const res = []; + for (const key in this.params_) { + res[i++] = key + '-' + this.params_[key]; + } + return res.join('/'); + } /** - * @private - * @type {module:ol/extent~Extent} + * Get the user-provided params, i.e. those passed to the constructor through + * the "params" option, and possibly updated using the updateParams method. + * @return {Object} Params. + * @api */ - this.tmpExtent_ = createEmpty(); + getParams() { + return this.params_; + } - this.setKey(this.getKeyForParams_()); -}; + /** + * @param {module:ol/tilecoord~TileCoord} tileCoord Tile coordinate. + * @param {module:ol/size~Size} tileSize Tile size. + * @param {module:ol/extent~Extent} tileExtent Tile extent. + * @param {number} pixelRatio Pixel ratio. + * @param {module:ol/proj/Projection} projection Projection. + * @param {Object} params Params. + * @return {string|undefined} Request URL. + * @private + */ + getRequestUrl_(tileCoord, tileSize, tileExtent, pixelRatio, projection, params) { + + const urls = this.urls; + if (!urls) { + return undefined; + } + + // ArcGIS Server only wants the numeric portion of the projection ID. + const srid = projection.getCode().split(':').pop(); + + params['SIZE'] = tileSize[0] + ',' + tileSize[1]; + params['BBOX'] = tileExtent.join(','); + params['BBOXSR'] = srid; + params['IMAGESR'] = srid; + params['DPI'] = Math.round( + params['DPI'] ? params['DPI'] * pixelRatio : 90 * pixelRatio + ); + + let url; + if (urls.length == 1) { + url = urls[0]; + } else { + const index = modulo(tileCoordHash(tileCoord), urls.length); + url = urls[index]; + } + + const modifiedUrl = url + .replace(/MapServer\/?$/, 'MapServer/export') + .replace(/ImageServer\/?$/, 'ImageServer/exportImage'); + return appendParams(modifiedUrl, params); + } + + /** + * @inheritDoc + */ + getTilePixelRatio(pixelRatio) { + return /** @type {number} */ (pixelRatio); + } + + /** + * @inheritDoc + */ + fixedTileUrlFunction(tileCoord, pixelRatio, projection) { + + let tileGrid = this.getTileGrid(); + if (!tileGrid) { + tileGrid = this.getTileGridForProjection(projection); + } + + if (tileGrid.getResolutions().length <= tileCoord[0]) { + return undefined; + } + + const tileExtent = tileGrid.getTileCoordExtent( + tileCoord, this.tmpExtent_); + let tileSize = toSize( + tileGrid.getTileSize(tileCoord[0]), this.tmpSize); + + if (pixelRatio != 1) { + tileSize = scaleSize(tileSize, pixelRatio, this.tmpSize); + } + + // Apply default params and override with user specified values. + const baseParams = { + 'F': 'image', + 'FORMAT': 'PNG32', + 'TRANSPARENT': true + }; + assign(baseParams, this.params_); + + return this.getRequestUrl_(tileCoord, tileSize, tileExtent, + pixelRatio, projection, baseParams); + } + + /** + * Update the user-provided params. + * @param {Object} params Params. + * @api + */ + updateParams(params) { + assign(this.params_, params); + this.setKey(this.getKeyForParams_()); + } +} inherits(TileArcGISRest, TileImage); -/** - * @private - * @return {string} The key for the current params. - */ -TileArcGISRest.prototype.getKeyForParams_ = function() { - let i = 0; - const res = []; - for (const key in this.params_) { - res[i++] = key + '-' + this.params_[key]; - } - return res.join('/'); -}; - - -/** - * Get the user-provided params, i.e. those passed to the constructor through - * the "params" option, and possibly updated using the updateParams method. - * @return {Object} Params. - * @api - */ -TileArcGISRest.prototype.getParams = function() { - return this.params_; -}; - - -/** - * @param {module:ol/tilecoord~TileCoord} tileCoord Tile coordinate. - * @param {module:ol/size~Size} tileSize Tile size. - * @param {module:ol/extent~Extent} tileExtent Tile extent. - * @param {number} pixelRatio Pixel ratio. - * @param {module:ol/proj/Projection} projection Projection. - * @param {Object} params Params. - * @return {string|undefined} Request URL. - * @private - */ -TileArcGISRest.prototype.getRequestUrl_ = function(tileCoord, tileSize, tileExtent, - pixelRatio, projection, params) { - - const urls = this.urls; - if (!urls) { - return undefined; - } - - // ArcGIS Server only wants the numeric portion of the projection ID. - const srid = projection.getCode().split(':').pop(); - - params['SIZE'] = tileSize[0] + ',' + tileSize[1]; - params['BBOX'] = tileExtent.join(','); - params['BBOXSR'] = srid; - params['IMAGESR'] = srid; - params['DPI'] = Math.round( - params['DPI'] ? params['DPI'] * pixelRatio : 90 * pixelRatio - ); - - let url; - if (urls.length == 1) { - url = urls[0]; - } else { - const index = modulo(tileCoordHash(tileCoord), urls.length); - url = urls[index]; - } - - const modifiedUrl = url - .replace(/MapServer\/?$/, 'MapServer/export') - .replace(/ImageServer\/?$/, 'ImageServer/exportImage'); - return appendParams(modifiedUrl, params); -}; - - -/** - * @inheritDoc - */ -TileArcGISRest.prototype.getTilePixelRatio = function(pixelRatio) { - return /** @type {number} */ (pixelRatio); -}; - - -/** - * @inheritDoc - */ -TileArcGISRest.prototype.fixedTileUrlFunction = function(tileCoord, pixelRatio, projection) { - - let tileGrid = this.getTileGrid(); - if (!tileGrid) { - tileGrid = this.getTileGridForProjection(projection); - } - - if (tileGrid.getResolutions().length <= tileCoord[0]) { - return undefined; - } - - const tileExtent = tileGrid.getTileCoordExtent( - tileCoord, this.tmpExtent_); - let tileSize = toSize( - tileGrid.getTileSize(tileCoord[0]), this.tmpSize); - - if (pixelRatio != 1) { - tileSize = scaleSize(tileSize, pixelRatio, this.tmpSize); - } - - // Apply default params and override with user specified values. - const baseParams = { - 'F': 'image', - 'FORMAT': 'PNG32', - 'TRANSPARENT': true - }; - assign(baseParams, this.params_); - - return this.getRequestUrl_(tileCoord, tileSize, tileExtent, - pixelRatio, projection, baseParams); -}; - - -/** - * Update the user-provided params. - * @param {Object} params Params. - * @api - */ -TileArcGISRest.prototype.updateParams = function(params) { - assign(this.params_, params); - this.setKey(this.getKeyForParams_()); -}; export default TileArcGISRest; diff --git a/src/ol/source/TileDebug.js b/src/ol/source/TileDebug.js index 4b78d9ecde..20eefc9d78 100644 --- a/src/ol/source/TileDebug.js +++ b/src/ol/source/TileDebug.js @@ -17,64 +17,65 @@ import {getKeyZXY} from '../tilecoord.js'; * @param {module:ol/size~Size} tileSize Tile size. * @param {string} text Text. */ -const LabeledTile = function(tileCoord, tileSize, text) { +class LabeledTile { + constructor(tileCoord, tileSize, text) { - Tile.call(this, tileCoord, TileState.LOADED); + Tile.call(this, tileCoord, TileState.LOADED); - /** - * @private - * @type {module:ol/size~Size} - */ - this.tileSize_ = tileSize; + /** + * @private + * @type {module:ol/size~Size} + */ + this.tileSize_ = tileSize; - /** - * @private - * @type {string} - */ - this.text_ = text; + /** + * @private + * @type {string} + */ + this.text_ = text; - /** - * @private - * @type {HTMLCanvasElement} - */ - this.canvas_ = null; + /** + * @private + * @type {HTMLCanvasElement} + */ + this.canvas_ = null; + + } + + /** + * Get the image element for this tile. + * @return {HTMLCanvasElement} Image. + */ + getImage() { + if (this.canvas_) { + return this.canvas_; + } else { + const tileSize = this.tileSize_; + const context = createCanvasContext2D(tileSize[0], tileSize[1]); + + context.strokeStyle = 'black'; + context.strokeRect(0.5, 0.5, tileSize[0] + 0.5, tileSize[1] + 0.5); + + context.fillStyle = 'black'; + context.textAlign = 'center'; + context.textBaseline = 'middle'; + context.font = '24px sans-serif'; + context.fillText(this.text_, tileSize[0] / 2, tileSize[1] / 2); + + this.canvas_ = context.canvas; + return context.canvas; + } + } + + /** + * @override + */ + load() {} +} -}; inherits(LabeledTile, Tile); -/** - * Get the image element for this tile. - * @return {HTMLCanvasElement} Image. - */ -LabeledTile.prototype.getImage = function() { - if (this.canvas_) { - return this.canvas_; - } else { - const tileSize = this.tileSize_; - const context = createCanvasContext2D(tileSize[0], tileSize[1]); - - context.strokeStyle = 'black'; - context.strokeRect(0.5, 0.5, tileSize[0] + 0.5, tileSize[1] + 0.5); - - context.fillStyle = 'black'; - context.textAlign = 'center'; - context.textBaseline = 'middle'; - context.font = '24px sans-serif'; - context.fillText(this.text_, tileSize[0] / 2, tileSize[1] / 2); - - this.canvas_ = context.canvas; - return context.canvas; - } -}; - - -/** - * @override - */ -LabeledTile.prototype.load = function() {}; - - /** * @typedef {Object} Options * @property {module:ol/proj~ProjectionLike} projection Projection. @@ -96,38 +97,39 @@ LabeledTile.prototype.load = function() {}; * @param {module:ol/source/TileDebug~Options=} options Debug tile options. * @api */ -const TileDebug = function(options) { +class TileDebug { + constructor(options) { - TileSource.call(this, { - opaque: false, - projection: options.projection, - tileGrid: options.tileGrid, - wrapX: options.wrapX !== undefined ? options.wrapX : true - }); + TileSource.call(this, { + opaque: false, + projection: options.projection, + tileGrid: options.tileGrid, + wrapX: options.wrapX !== undefined ? options.wrapX : true + }); -}; + } + + /** + * @inheritDoc + */ + getTile(z, x, y) { + const tileCoordKey = getKeyZXY(z, x, y); + if (this.tileCache.containsKey(tileCoordKey)) { + return /** @type {!module:ol/source/TileDebug~LabeledTile} */ (this.tileCache.get(tileCoordKey)); + } else { + const tileSize = toSize(this.tileGrid.getTileSize(z)); + const tileCoord = [z, x, y]; + const textTileCoord = this.getTileCoordForTileUrlFunction(tileCoord); + const text = !textTileCoord ? '' : + this.getTileCoordForTileUrlFunction(textTileCoord).toString(); + const tile = new LabeledTile(tileCoord, tileSize, text); + this.tileCache.set(tileCoordKey, tile); + return tile; + } + } +} inherits(TileDebug, TileSource); -/** - * @inheritDoc - */ -TileDebug.prototype.getTile = function(z, x, y) { - const tileCoordKey = getKeyZXY(z, x, y); - if (this.tileCache.containsKey(tileCoordKey)) { - return /** @type {!module:ol/source/TileDebug~LabeledTile} */ (this.tileCache.get(tileCoordKey)); - } else { - const tileSize = toSize(this.tileGrid.getTileSize(z)); - const tileCoord = [z, x, y]; - const textTileCoord = this.getTileCoordForTileUrlFunction(tileCoord); - const text = !textTileCoord ? '' : - this.getTileCoordForTileUrlFunction(textTileCoord).toString(); - const tile = new LabeledTile(tileCoord, tileSize, text); - this.tileCache.set(tileCoordKey, tile); - return tile; - } -}; - - export default TileDebug; diff --git a/src/ol/source/TileImage.js b/src/ol/source/TileImage.js index 887412c2f8..f59a06ec41 100644 --- a/src/ol/source/TileImage.js +++ b/src/ol/source/TileImage.js @@ -64,344 +64,334 @@ import {getForProjection as getTileGridForProjection} from '../tilegrid.js'; * @param {module:ol/source/TileImage~Options=} options Image tile options. * @api */ -const TileImage = function(options) { +class TileImage { + constructor(options) { - UrlTile.call(this, { - attributions: options.attributions, - cacheSize: options.cacheSize, - extent: options.extent, - opaque: options.opaque, - projection: options.projection, - state: options.state, - tileGrid: options.tileGrid, - tileLoadFunction: options.tileLoadFunction ? - options.tileLoadFunction : defaultTileLoadFunction, - tilePixelRatio: options.tilePixelRatio, - tileUrlFunction: options.tileUrlFunction, - url: options.url, - urls: options.urls, - wrapX: options.wrapX, - transition: options.transition - }); + UrlTile.call(this, { + attributions: options.attributions, + cacheSize: options.cacheSize, + extent: options.extent, + opaque: options.opaque, + projection: options.projection, + state: options.state, + tileGrid: options.tileGrid, + tileLoadFunction: options.tileLoadFunction ? + options.tileLoadFunction : defaultTileLoadFunction, + tilePixelRatio: options.tilePixelRatio, + tileUrlFunction: options.tileUrlFunction, + url: options.url, + urls: options.urls, + wrapX: options.wrapX, + transition: options.transition + }); + + /** + * @protected + * @type {?string} + */ + this.crossOrigin = + options.crossOrigin !== undefined ? options.crossOrigin : null; + + /** + * @protected + * @type {function(new: module:ol/ImageTile, module:ol/tilecoord~TileCoord, module:ol/TileState, string, + * ?string, module:ol/Tile~LoadFunction, module:ol/Tile~Options=)} + */ + this.tileClass = options.tileClass !== undefined ? + options.tileClass : ImageTile; + + /** + * @protected + * @type {!Object.} + */ + this.tileCacheForProjection = {}; + + /** + * @protected + * @type {!Object.} + */ + this.tileGridForProjection = {}; + + /** + * @private + * @type {number|undefined} + */ + this.reprojectionErrorThreshold_ = options.reprojectionErrorThreshold; + + /** + * @private + * @type {boolean} + */ + this.renderReprojectionEdges_ = false; + } + + /** + * @inheritDoc + */ + canExpireCache() { + if (!ENABLE_RASTER_REPROJECTION) { + return UrlTile.prototype.canExpireCache.call(this); + } + if (this.tileCache.canExpireCache()) { + return true; + } else { + for (const key in this.tileCacheForProjection) { + if (this.tileCacheForProjection[key].canExpireCache()) { + return true; + } + } + } + return false; + } + + /** + * @inheritDoc + */ + expireCache(projection, usedTiles) { + if (!ENABLE_RASTER_REPROJECTION) { + UrlTile.prototype.expireCache.call(this, projection, usedTiles); + return; + } + const usedTileCache = this.getTileCacheForProjection(projection); + + this.tileCache.expireCache(this.tileCache == usedTileCache ? usedTiles : {}); + for (const id in this.tileCacheForProjection) { + const tileCache = this.tileCacheForProjection[id]; + tileCache.expireCache(tileCache == usedTileCache ? usedTiles : {}); + } + } + + /** + * @inheritDoc + */ + getGutter(projection) { + if (ENABLE_RASTER_REPROJECTION && + this.getProjection() && projection && !equivalent(this.getProjection(), projection)) { + return 0; + } else { + return this.getGutterInternal(); + } + } /** * @protected - * @type {?string} + * @return {number} Gutter. */ - this.crossOrigin = - options.crossOrigin !== undefined ? options.crossOrigin : null; + getGutterInternal() { + return 0; + } /** - * @protected - * @type {function(new: module:ol/ImageTile, module:ol/tilecoord~TileCoord, module:ol/TileState, string, - * ?string, module:ol/Tile~LoadFunction, module:ol/Tile~Options=)} + * @inheritDoc */ - this.tileClass = options.tileClass !== undefined ? - options.tileClass : ImageTile; + getOpaque(projection) { + if (ENABLE_RASTER_REPROJECTION && + this.getProjection() && projection && !equivalent(this.getProjection(), projection)) { + return false; + } else { + return UrlTile.prototype.getOpaque.call(this, projection); + } + } /** - * @protected - * @type {!Object.} + * @inheritDoc */ - this.tileCacheForProjection = {}; + getTileGridForProjection(projection) { + if (!ENABLE_RASTER_REPROJECTION) { + return UrlTile.prototype.getTileGridForProjection.call(this, projection); + } + const thisProj = this.getProjection(); + if (this.tileGrid && (!thisProj || equivalent(thisProj, projection))) { + return this.tileGrid; + } else { + const projKey = getUid(projection).toString(); + if (!(projKey in this.tileGridForProjection)) { + this.tileGridForProjection[projKey] = getTileGridForProjection(projection); + } + return ( + /** @type {!module:ol/tilegrid/TileGrid} */ (this.tileGridForProjection[projKey]) + ); + } + } /** - * @protected - * @type {!Object.} + * @inheritDoc */ - this.tileGridForProjection = {}; + getTileCacheForProjection(projection) { + if (!ENABLE_RASTER_REPROJECTION) { + return UrlTile.prototype.getTileCacheForProjection.call(this, projection); + } + const thisProj = this.getProjection(); if (!thisProj || equivalent(thisProj, projection)) { + return this.tileCache; + } else { + const projKey = getUid(projection).toString(); + if (!(projKey in this.tileCacheForProjection)) { + this.tileCacheForProjection[projKey] = new TileCache(this.tileCache.highWaterMark); + } + return this.tileCacheForProjection[projKey]; + } + } /** + * @param {number} z Tile coordinate z. + * @param {number} x Tile coordinate x. + * @param {number} y Tile coordinate y. + * @param {number} pixelRatio Pixel ratio. + * @param {module:ol/proj/Projection} projection Projection. + * @param {string} key The key set on the tile. + * @return {!module:ol/Tile} Tile. * @private - * @type {number|undefined} */ - this.reprojectionErrorThreshold_ = options.reprojectionErrorThreshold; + createTile_(z, x, y, pixelRatio, projection, key) { + const tileCoord = [z, x, y]; + const urlTileCoord = this.getTileCoordForTileUrlFunction( + tileCoord, projection); + const tileUrl = urlTileCoord ? + this.tileUrlFunction(urlTileCoord, pixelRatio, projection) : undefined; + const tile = new this.tileClass( + tileCoord, + tileUrl !== undefined ? TileState.IDLE : TileState.EMPTY, + tileUrl !== undefined ? tileUrl : '', + this.crossOrigin, + this.tileLoadFunction, + this.tileOptions); + tile.key = key; + listen(tile, EventType.CHANGE, + this.handleTileChange, this); + return tile; + } /** - * @private - * @type {boolean} + * @inheritDoc */ - this.renderReprojectionEdges_ = false; -}; + getTile(z, x, y, pixelRatio, projection) { + const sourceProjection = /** @type {!module:ol/proj/Projection} */ (this.getProjection()); + if (!ENABLE_RASTER_REPROJECTION || + !sourceProjection || !projection || equivalent(sourceProjection, projection)) { + return this.getTileInternal(z, x, y, pixelRatio, sourceProjection || projection); + } else { + const cache = this.getTileCacheForProjection(projection); + const tileCoord = [z, x, y]; + let tile; + const tileCoordKey = getKey(tileCoord); + if (cache.containsKey(tileCoordKey)) { + tile = /** @type {!module:ol/Tile} */ (cache.get(tileCoordKey)); + } + const key = this.getKey(); + if (tile && tile.key == key) { + return tile; + } else { + const sourceTileGrid = this.getTileGridForProjection(sourceProjection); + const targetTileGrid = this.getTileGridForProjection(projection); + const wrappedTileCoord = + this.getTileCoordForTileUrlFunction(tileCoord, projection); + const newTile = new ReprojTile( + sourceProjection, sourceTileGrid, + projection, targetTileGrid, + tileCoord, wrappedTileCoord, this.getTilePixelRatio(pixelRatio), + this.getGutterInternal(), + function(z, x, y, pixelRatio) { + return this.getTileInternal(z, x, y, pixelRatio, sourceProjection); + }.bind(this), this.reprojectionErrorThreshold_, + this.renderReprojectionEdges_); + newTile.key = key; + + if (tile) { + newTile.interimTile = tile; + newTile.refreshInterimChain(); + cache.replace(tileCoordKey, newTile); + } else { + cache.set(tileCoordKey, newTile); + } + return newTile; + } + } + } + + /** + * @param {number} z Tile coordinate z. + * @param {number} x Tile coordinate x. + * @param {number} y Tile coordinate y. + * @param {number} pixelRatio Pixel ratio. + * @param {!module:ol/proj/Projection} projection Projection. + * @return {!module:ol/Tile} Tile. + * @protected + */ + getTileInternal(z, x, y, pixelRatio, projection) { + let tile = null; + const tileCoordKey = getKeyZXY(z, x, y); + const key = this.getKey(); + if (!this.tileCache.containsKey(tileCoordKey)) { + tile = this.createTile_(z, x, y, pixelRatio, projection, key); + this.tileCache.set(tileCoordKey, tile); + } else { + tile = this.tileCache.get(tileCoordKey); + if (tile.key != key) { + // The source's params changed. If the tile has an interim tile and if we + // can use it then we use it. Otherwise we create a new tile. In both + // cases we attempt to assign an interim tile to the new tile. + const interimTile = tile; + tile = this.createTile_(z, x, y, pixelRatio, projection, key); + + //make the new tile the head of the list, + if (interimTile.getState() == TileState.IDLE) { + //the old tile hasn't begun loading yet, and is now outdated, so we can simply discard it + tile.interimTile = interimTile.interimTile; + } else { + tile.interimTile = interimTile; + } + tile.refreshInterimChain(); + this.tileCache.replace(tileCoordKey, tile); + } + } + return tile; + } + + /** + * Sets whether to render reprojection edges or not (usually for debugging). + * @param {boolean} render Render the edges. + * @api + */ + setRenderReprojectionEdges(render) { + if (!ENABLE_RASTER_REPROJECTION || + this.renderReprojectionEdges_ == render) { + return; + } + this.renderReprojectionEdges_ = render; + for (const id in this.tileCacheForProjection) { + this.tileCacheForProjection[id].clear(); + } + this.changed(); + } + + /** + * Sets the tile grid to use when reprojecting the tiles to the given + * projection instead of the default tile grid for the projection. + * + * This can be useful when the default tile grid cannot be created + * (e.g. projection has no extent defined) or + * for optimization reasons (custom tile size, resolutions, ...). + * + * @param {module:ol/proj~ProjectionLike} projection Projection. + * @param {module:ol/tilegrid/TileGrid} tilegrid Tile grid to use for the projection. + * @api + */ + setTileGridForProjection(projection, tilegrid) { + if (ENABLE_RASTER_REPROJECTION) { + const proj = getProjection(projection); + if (proj) { + const projKey = getUid(proj).toString(); + if (!(projKey in this.tileGridForProjection)) { + this.tileGridForProjection[projKey] = tilegrid; + } + } + } + } +} inherits(TileImage, UrlTile); -/** - * @inheritDoc - */ -TileImage.prototype.canExpireCache = function() { - if (!ENABLE_RASTER_REPROJECTION) { - return UrlTile.prototype.canExpireCache.call(this); - } - if (this.tileCache.canExpireCache()) { - return true; - } else { - for (const key in this.tileCacheForProjection) { - if (this.tileCacheForProjection[key].canExpireCache()) { - return true; - } - } - } - return false; -}; - - -/** - * @inheritDoc - */ -TileImage.prototype.expireCache = function(projection, usedTiles) { - if (!ENABLE_RASTER_REPROJECTION) { - UrlTile.prototype.expireCache.call(this, projection, usedTiles); - return; - } - const usedTileCache = this.getTileCacheForProjection(projection); - - this.tileCache.expireCache(this.tileCache == usedTileCache ? usedTiles : {}); - for (const id in this.tileCacheForProjection) { - const tileCache = this.tileCacheForProjection[id]; - tileCache.expireCache(tileCache == usedTileCache ? usedTiles : {}); - } -}; - - -/** - * @inheritDoc - */ -TileImage.prototype.getGutter = function(projection) { - if (ENABLE_RASTER_REPROJECTION && - this.getProjection() && projection && !equivalent(this.getProjection(), projection)) { - return 0; - } else { - return this.getGutterInternal(); - } -}; - - -/** - * @protected - * @return {number} Gutter. - */ -TileImage.prototype.getGutterInternal = function() { - return 0; -}; - - -/** - * @inheritDoc - */ -TileImage.prototype.getOpaque = function(projection) { - if (ENABLE_RASTER_REPROJECTION && - this.getProjection() && projection && !equivalent(this.getProjection(), projection)) { - return false; - } else { - return UrlTile.prototype.getOpaque.call(this, projection); - } -}; - - -/** - * @inheritDoc - */ -TileImage.prototype.getTileGridForProjection = function(projection) { - if (!ENABLE_RASTER_REPROJECTION) { - return UrlTile.prototype.getTileGridForProjection.call(this, projection); - } - const thisProj = this.getProjection(); - if (this.tileGrid && (!thisProj || equivalent(thisProj, projection))) { - return this.tileGrid; - } else { - const projKey = getUid(projection).toString(); - if (!(projKey in this.tileGridForProjection)) { - this.tileGridForProjection[projKey] = getTileGridForProjection(projection); - } - return ( - /** @type {!module:ol/tilegrid/TileGrid} */ (this.tileGridForProjection[projKey]) - ); - } -}; - - -/** - * @inheritDoc - */ -TileImage.prototype.getTileCacheForProjection = function(projection) { - if (!ENABLE_RASTER_REPROJECTION) { - return UrlTile.prototype.getTileCacheForProjection.call(this, projection); - } - const thisProj = this.getProjection(); if (!thisProj || equivalent(thisProj, projection)) { - return this.tileCache; - } else { - const projKey = getUid(projection).toString(); - if (!(projKey in this.tileCacheForProjection)) { - this.tileCacheForProjection[projKey] = new TileCache(this.tileCache.highWaterMark); - } - return this.tileCacheForProjection[projKey]; - } -}; - - -/** - * @param {number} z Tile coordinate z. - * @param {number} x Tile coordinate x. - * @param {number} y Tile coordinate y. - * @param {number} pixelRatio Pixel ratio. - * @param {module:ol/proj/Projection} projection Projection. - * @param {string} key The key set on the tile. - * @return {!module:ol/Tile} Tile. - * @private - */ -TileImage.prototype.createTile_ = function(z, x, y, pixelRatio, projection, key) { - const tileCoord = [z, x, y]; - const urlTileCoord = this.getTileCoordForTileUrlFunction( - tileCoord, projection); - const tileUrl = urlTileCoord ? - this.tileUrlFunction(urlTileCoord, pixelRatio, projection) : undefined; - const tile = new this.tileClass( - tileCoord, - tileUrl !== undefined ? TileState.IDLE : TileState.EMPTY, - tileUrl !== undefined ? tileUrl : '', - this.crossOrigin, - this.tileLoadFunction, - this.tileOptions); - tile.key = key; - listen(tile, EventType.CHANGE, - this.handleTileChange, this); - return tile; -}; - - -/** - * @inheritDoc - */ -TileImage.prototype.getTile = function(z, x, y, pixelRatio, projection) { - const sourceProjection = /** @type {!module:ol/proj/Projection} */ (this.getProjection()); - if (!ENABLE_RASTER_REPROJECTION || - !sourceProjection || !projection || equivalent(sourceProjection, projection)) { - return this.getTileInternal(z, x, y, pixelRatio, sourceProjection || projection); - } else { - const cache = this.getTileCacheForProjection(projection); - const tileCoord = [z, x, y]; - let tile; - const tileCoordKey = getKey(tileCoord); - if (cache.containsKey(tileCoordKey)) { - tile = /** @type {!module:ol/Tile} */ (cache.get(tileCoordKey)); - } - const key = this.getKey(); - if (tile && tile.key == key) { - return tile; - } else { - const sourceTileGrid = this.getTileGridForProjection(sourceProjection); - const targetTileGrid = this.getTileGridForProjection(projection); - const wrappedTileCoord = - this.getTileCoordForTileUrlFunction(tileCoord, projection); - const newTile = new ReprojTile( - sourceProjection, sourceTileGrid, - projection, targetTileGrid, - tileCoord, wrappedTileCoord, this.getTilePixelRatio(pixelRatio), - this.getGutterInternal(), - function(z, x, y, pixelRatio) { - return this.getTileInternal(z, x, y, pixelRatio, sourceProjection); - }.bind(this), this.reprojectionErrorThreshold_, - this.renderReprojectionEdges_); - newTile.key = key; - - if (tile) { - newTile.interimTile = tile; - newTile.refreshInterimChain(); - cache.replace(tileCoordKey, newTile); - } else { - cache.set(tileCoordKey, newTile); - } - return newTile; - } - } -}; - - -/** - * @param {number} z Tile coordinate z. - * @param {number} x Tile coordinate x. - * @param {number} y Tile coordinate y. - * @param {number} pixelRatio Pixel ratio. - * @param {!module:ol/proj/Projection} projection Projection. - * @return {!module:ol/Tile} Tile. - * @protected - */ -TileImage.prototype.getTileInternal = function(z, x, y, pixelRatio, projection) { - let tile = null; - const tileCoordKey = getKeyZXY(z, x, y); - const key = this.getKey(); - if (!this.tileCache.containsKey(tileCoordKey)) { - tile = this.createTile_(z, x, y, pixelRatio, projection, key); - this.tileCache.set(tileCoordKey, tile); - } else { - tile = this.tileCache.get(tileCoordKey); - if (tile.key != key) { - // The source's params changed. If the tile has an interim tile and if we - // can use it then we use it. Otherwise we create a new tile. In both - // cases we attempt to assign an interim tile to the new tile. - const interimTile = tile; - tile = this.createTile_(z, x, y, pixelRatio, projection, key); - - //make the new tile the head of the list, - if (interimTile.getState() == TileState.IDLE) { - //the old tile hasn't begun loading yet, and is now outdated, so we can simply discard it - tile.interimTile = interimTile.interimTile; - } else { - tile.interimTile = interimTile; - } - tile.refreshInterimChain(); - this.tileCache.replace(tileCoordKey, tile); - } - } - return tile; -}; - - -/** - * Sets whether to render reprojection edges or not (usually for debugging). - * @param {boolean} render Render the edges. - * @api - */ -TileImage.prototype.setRenderReprojectionEdges = function(render) { - if (!ENABLE_RASTER_REPROJECTION || - this.renderReprojectionEdges_ == render) { - return; - } - this.renderReprojectionEdges_ = render; - for (const id in this.tileCacheForProjection) { - this.tileCacheForProjection[id].clear(); - } - this.changed(); -}; - - -/** - * Sets the tile grid to use when reprojecting the tiles to the given - * projection instead of the default tile grid for the projection. - * - * This can be useful when the default tile grid cannot be created - * (e.g. projection has no extent defined) or - * for optimization reasons (custom tile size, resolutions, ...). - * - * @param {module:ol/proj~ProjectionLike} projection Projection. - * @param {module:ol/tilegrid/TileGrid} tilegrid Tile grid to use for the projection. - * @api - */ -TileImage.prototype.setTileGridForProjection = function(projection, tilegrid) { - if (ENABLE_RASTER_REPROJECTION) { - const proj = getProjection(projection); - if (proj) { - const projKey = getUid(proj).toString(); - if (!(projKey in this.tileGridForProjection)) { - this.tileGridForProjection[projKey] = tilegrid; - } - } - } -}; - - /** * @param {module:ol/ImageTile} imageTile Image tile. * @param {string} src Source. diff --git a/src/ol/source/TileJSON.js b/src/ol/source/TileJSON.js index 0602f0f61a..7a6ee11678 100644 --- a/src/ol/source/TileJSON.js +++ b/src/ol/source/TileJSON.js @@ -53,137 +53,136 @@ import {createXYZ, extentFromProjection} from '../tilegrid.js'; * @param {module:ol/source/TileJSON~Options=} options TileJSON options. * @api */ -const TileJSON = function(options) { +class TileJSON { + constructor(options) { - /** - * @type {TileJSON} - * @private - */ - this.tileJSON_ = null; + /** + * @type {TileJSON} + * @private + */ + this.tileJSON_ = null; - TileImage.call(this, { - attributions: options.attributions, - cacheSize: options.cacheSize, - crossOrigin: options.crossOrigin, - projection: getProjection('EPSG:3857'), - reprojectionErrorThreshold: options.reprojectionErrorThreshold, - state: SourceState.LOADING, - tileLoadFunction: options.tileLoadFunction, - wrapX: options.wrapX !== undefined ? options.wrapX : true, - transition: options.transition - }); + TileImage.call(this, { + attributions: options.attributions, + cacheSize: options.cacheSize, + crossOrigin: options.crossOrigin, + projection: getProjection('EPSG:3857'), + reprojectionErrorThreshold: options.reprojectionErrorThreshold, + state: SourceState.LOADING, + tileLoadFunction: options.tileLoadFunction, + wrapX: options.wrapX !== undefined ? options.wrapX : true, + transition: options.transition + }); - if (options.url) { - if (options.jsonp) { - requestJSONP(options.url, this.handleTileJSONResponse.bind(this), - this.handleTileJSONError.bind(this)); + if (options.url) { + if (options.jsonp) { + requestJSONP(options.url, this.handleTileJSONResponse.bind(this), + this.handleTileJSONError.bind(this)); + } else { + const client = new XMLHttpRequest(); + client.addEventListener('load', this.onXHRLoad_.bind(this)); + client.addEventListener('error', this.onXHRError_.bind(this)); + client.open('GET', options.url); + client.send(); + } + } else if (options.tileJSON) { + this.handleTileJSONResponse(options.tileJSON); } else { - const client = new XMLHttpRequest(); - client.addEventListener('load', this.onXHRLoad_.bind(this)); - client.addEventListener('error', this.onXHRError_.bind(this)); - client.open('GET', options.url); - client.send(); + assert(false, 51); // Either `url` or `tileJSON` options must be provided } - } else if (options.tileJSON) { - this.handleTileJSONResponse(options.tileJSON); - } else { - assert(false, 51); // Either `url` or `tileJSON` options must be provided + } -}; + /** + * @private + * @param {Event} event The load event. + */ + onXHRLoad_(event) { + const client = /** @type {XMLHttpRequest} */ (event.target); + // status will be 0 for file:// urls + if (!client.status || client.status >= 200 && client.status < 300) { + let response; + try { + response = /** @type {TileJSON} */(JSON.parse(client.responseText)); + } catch (err) { + this.handleTileJSONError(); + return; + } + this.handleTileJSONResponse(response); + } else { + this.handleTileJSONError(); + } + } + + /** + * @private + * @param {Event} event The error event. + */ + onXHRError_(event) { + this.handleTileJSONError(); + } + + /** + * @return {TileJSON} The tilejson object. + * @api + */ + getTileJSON() { + return this.tileJSON_; + } + + /** + * @protected + * @param {TileJSON} tileJSON Tile JSON. + */ + handleTileJSONResponse(tileJSON) { + + const epsg4326Projection = getProjection('EPSG:4326'); + + const sourceProjection = this.getProjection(); + let extent; + if (tileJSON.bounds !== undefined) { + const transform = getTransformFromProjections( + epsg4326Projection, sourceProjection); + extent = applyTransform(tileJSON.bounds, transform); + } + + const minZoom = tileJSON.minzoom || 0; + const maxZoom = tileJSON.maxzoom || 22; + const tileGrid = createXYZ({ + extent: extentFromProjection(sourceProjection), + maxZoom: maxZoom, + minZoom: minZoom + }); + this.tileGrid = tileGrid; + + this.tileUrlFunction = createFromTemplates(tileJSON.tiles, tileGrid); + + if (tileJSON.attribution !== undefined && !this.getAttributions()) { + const attributionExtent = extent !== undefined ? + extent : epsg4326Projection.getExtent(); + + this.setAttributions(function(frameState) { + if (intersects(attributionExtent, frameState.extent)) { + return [tileJSON.attribution]; + } + return null; + }); + + } + this.tileJSON_ = tileJSON; + this.setState(SourceState.READY); + + } + + /** + * @protected + */ + handleTileJSONError() { + this.setState(SourceState.ERROR); + } +} inherits(TileJSON, TileImage); -/** - * @private - * @param {Event} event The load event. - */ -TileJSON.prototype.onXHRLoad_ = function(event) { - const client = /** @type {XMLHttpRequest} */ (event.target); - // status will be 0 for file:// urls - if (!client.status || client.status >= 200 && client.status < 300) { - let response; - try { - response = /** @type {TileJSON} */(JSON.parse(client.responseText)); - } catch (err) { - this.handleTileJSONError(); - return; - } - this.handleTileJSONResponse(response); - } else { - this.handleTileJSONError(); - } -}; - - -/** - * @private - * @param {Event} event The error event. - */ -TileJSON.prototype.onXHRError_ = function(event) { - this.handleTileJSONError(); -}; - - -/** - * @return {TileJSON} The tilejson object. - * @api - */ -TileJSON.prototype.getTileJSON = function() { - return this.tileJSON_; -}; - - -/** - * @protected - * @param {TileJSON} tileJSON Tile JSON. - */ -TileJSON.prototype.handleTileJSONResponse = function(tileJSON) { - - const epsg4326Projection = getProjection('EPSG:4326'); - - const sourceProjection = this.getProjection(); - let extent; - if (tileJSON.bounds !== undefined) { - const transform = getTransformFromProjections( - epsg4326Projection, sourceProjection); - extent = applyTransform(tileJSON.bounds, transform); - } - - const minZoom = tileJSON.minzoom || 0; - const maxZoom = tileJSON.maxzoom || 22; - const tileGrid = createXYZ({ - extent: extentFromProjection(sourceProjection), - maxZoom: maxZoom, - minZoom: minZoom - }); - this.tileGrid = tileGrid; - - this.tileUrlFunction = createFromTemplates(tileJSON.tiles, tileGrid); - - if (tileJSON.attribution !== undefined && !this.getAttributions()) { - const attributionExtent = extent !== undefined ? - extent : epsg4326Projection.getExtent(); - - this.setAttributions(function(frameState) { - if (intersects(attributionExtent, frameState.extent)) { - return [tileJSON.attribution]; - } - return null; - }); - - } - this.tileJSON_ = tileJSON; - this.setState(SourceState.READY); - -}; - - -/** - * @protected - */ -TileJSON.prototype.handleTileJSONError = function() { - this.setState(SourceState.ERROR); -}; export default TileJSON; diff --git a/src/ol/source/TileWMS.js b/src/ol/source/TileWMS.js index c1f40149a9..1e053d19c0 100644 --- a/src/ol/source/TileWMS.js +++ b/src/ol/source/TileWMS.js @@ -80,320 +80,315 @@ import {appendParams} from '../uri.js'; * @param {module:ol/source/TileWMS~Options=} [opt_options] Tile WMS options. * @api */ -const TileWMS = function(opt_options) { +class TileWMS { + constructor(opt_options) { - const options = opt_options || {}; + const options = opt_options || {}; - const params = options.params || {}; + const params = options.params || {}; - const transparent = 'TRANSPARENT' in params ? params['TRANSPARENT'] : true; + const transparent = 'TRANSPARENT' in params ? params['TRANSPARENT'] : true; - TileImage.call(this, { - attributions: options.attributions, - cacheSize: options.cacheSize, - crossOrigin: options.crossOrigin, - opaque: !transparent, - projection: options.projection, - reprojectionErrorThreshold: options.reprojectionErrorThreshold, - tileClass: options.tileClass, - tileGrid: options.tileGrid, - tileLoadFunction: options.tileLoadFunction, - url: options.url, - urls: options.urls, - wrapX: options.wrapX !== undefined ? options.wrapX : true, - transition: options.transition - }); + TileImage.call(this, { + attributions: options.attributions, + cacheSize: options.cacheSize, + crossOrigin: options.crossOrigin, + opaque: !transparent, + projection: options.projection, + reprojectionErrorThreshold: options.reprojectionErrorThreshold, + tileClass: options.tileClass, + tileGrid: options.tileGrid, + tileLoadFunction: options.tileLoadFunction, + url: options.url, + urls: options.urls, + wrapX: options.wrapX !== undefined ? options.wrapX : true, + transition: options.transition + }); + + /** + * @private + * @type {number} + */ + this.gutter_ = options.gutter !== undefined ? options.gutter : 0; + + /** + * @private + * @type {!Object} + */ + this.params_ = params; + + /** + * @private + * @type {boolean} + */ + this.v13_ = true; + + /** + * @private + * @type {module:ol/source/WMSServerType|undefined} + */ + this.serverType_ = /** @type {module:ol/source/WMSServerType|undefined} */ (options.serverType); + + /** + * @private + * @type {boolean} + */ + this.hidpi_ = options.hidpi !== undefined ? options.hidpi : true; + + /** + * @private + * @type {module:ol/extent~Extent} + */ + this.tmpExtent_ = createEmpty(); + + this.updateV13_(); + this.setKey(this.getKeyForParams_()); + + } + + /** + * Return the GetFeatureInfo URL for the passed coordinate, resolution, and + * projection. Return `undefined` if the GetFeatureInfo URL cannot be + * constructed. + * @param {module:ol/coordinate~Coordinate} coordinate Coordinate. + * @param {number} resolution Resolution. + * @param {module:ol/proj~ProjectionLike} projection Projection. + * @param {!Object} params GetFeatureInfo params. `INFO_FORMAT` at least should + * be provided. If `QUERY_LAYERS` is not provided then the layers specified + * in the `LAYERS` parameter will be used. `VERSION` should not be + * specified here. + * @return {string|undefined} GetFeatureInfo URL. + * @api + */ + getGetFeatureInfoUrl(coordinate, resolution, projection, params) { + const projectionObj = getProjection(projection); + const sourceProjectionObj = this.getProjection(); + + let tileGrid = this.getTileGrid(); + if (!tileGrid) { + tileGrid = this.getTileGridForProjection(projectionObj); + } + + const tileCoord = tileGrid.getTileCoordForCoordAndResolution(coordinate, resolution); + + if (tileGrid.getResolutions().length <= tileCoord[0]) { + return undefined; + } + + let tileResolution = tileGrid.getResolution(tileCoord[0]); + let tileExtent = tileGrid.getTileCoordExtent(tileCoord, this.tmpExtent_); + let tileSize = toSize(tileGrid.getTileSize(tileCoord[0]), this.tmpSize); + + + const gutter = this.gutter_; + if (gutter !== 0) { + tileSize = bufferSize(tileSize, gutter, this.tmpSize); + tileExtent = buffer(tileExtent, tileResolution * gutter, tileExtent); + } + + if (sourceProjectionObj && sourceProjectionObj !== projectionObj) { + tileResolution = calculateSourceResolution(sourceProjectionObj, projectionObj, coordinate, tileResolution); + tileExtent = transformExtent(tileExtent, projectionObj, sourceProjectionObj); + coordinate = transform(coordinate, projectionObj, sourceProjectionObj); + } + + const baseParams = { + 'SERVICE': 'WMS', + 'VERSION': DEFAULT_WMS_VERSION, + 'REQUEST': 'GetFeatureInfo', + 'FORMAT': 'image/png', + 'TRANSPARENT': true, + 'QUERY_LAYERS': this.params_['LAYERS'] + }; + assign(baseParams, this.params_, params); + + const x = Math.floor((coordinate[0] - tileExtent[0]) / tileResolution); + const y = Math.floor((tileExtent[3] - coordinate[1]) / tileResolution); + + baseParams[this.v13_ ? 'I' : 'X'] = x; + baseParams[this.v13_ ? 'J' : 'Y'] = y; + + return this.getRequestUrl_(tileCoord, tileSize, tileExtent, + 1, sourceProjectionObj || projectionObj, baseParams); + } + + /** + * @inheritDoc + */ + getGutterInternal() { + return this.gutter_; + } + + /** + * Get the user-provided params, i.e. those passed to the constructor through + * the "params" option, and possibly updated using the updateParams method. + * @return {Object} Params. + * @api + */ + getParams() { + return this.params_; + } + + /** + * @param {module:ol/tilecoord~TileCoord} tileCoord Tile coordinate. + * @param {module:ol/size~Size} tileSize Tile size. + * @param {module:ol/extent~Extent} tileExtent Tile extent. + * @param {number} pixelRatio Pixel ratio. + * @param {module:ol/proj/Projection} projection Projection. + * @param {Object} params Params. + * @return {string|undefined} Request URL. + * @private + */ + getRequestUrl_(tileCoord, tileSize, tileExtent, pixelRatio, projection, params) { + + const urls = this.urls; + if (!urls) { + return undefined; + } + + params['WIDTH'] = tileSize[0]; + params['HEIGHT'] = tileSize[1]; + + params[this.v13_ ? 'CRS' : 'SRS'] = projection.getCode(); + + if (!('STYLES' in this.params_)) { + params['STYLES'] = ''; + } + + if (pixelRatio != 1) { + switch (this.serverType_) { + case WMSServerType.GEOSERVER: + const dpi = (90 * pixelRatio + 0.5) | 0; + if ('FORMAT_OPTIONS' in params) { + params['FORMAT_OPTIONS'] += ';dpi:' + dpi; + } else { + params['FORMAT_OPTIONS'] = 'dpi:' + dpi; + } + break; + case WMSServerType.MAPSERVER: + params['MAP_RESOLUTION'] = 90 * pixelRatio; + break; + case WMSServerType.CARMENTA_SERVER: + case WMSServerType.QGIS: + params['DPI'] = 90 * pixelRatio; + break; + default: + assert(false, 52); // Unknown `serverType` configured + break; + } + } + + const axisOrientation = projection.getAxisOrientation(); + const bbox = tileExtent; + if (this.v13_ && axisOrientation.substr(0, 2) == 'ne') { + let tmp; + tmp = tileExtent[0]; + bbox[0] = tileExtent[1]; + bbox[1] = tmp; + tmp = tileExtent[2]; + bbox[2] = tileExtent[3]; + bbox[3] = tmp; + } + params['BBOX'] = bbox.join(','); + + let url; + if (urls.length == 1) { + url = urls[0]; + } else { + const index = modulo(tileCoordHash(tileCoord), urls.length); + url = urls[index]; + } + return appendParams(url, params); + } + + /** + * @inheritDoc + */ + getTilePixelRatio(pixelRatio) { + return (!this.hidpi_ || this.serverType_ === undefined) ? 1 : + /** @type {number} */ (pixelRatio); + } /** * @private - * @type {number} + * @return {string} The key for the current params. */ - this.gutter_ = options.gutter !== undefined ? options.gutter : 0; + getKeyForParams_() { + let i = 0; + const res = []; + for (const key in this.params_) { + res[i++] = key + '-' + this.params_[key]; + } + return res.join('/'); + } + + /** + * @inheritDoc + */ + fixedTileUrlFunction(tileCoord, pixelRatio, projection) { + + let tileGrid = this.getTileGrid(); + if (!tileGrid) { + tileGrid = this.getTileGridForProjection(projection); + } + + if (tileGrid.getResolutions().length <= tileCoord[0]) { + return undefined; + } + + if (pixelRatio != 1 && (!this.hidpi_ || this.serverType_ === undefined)) { + pixelRatio = 1; + } + + const tileResolution = tileGrid.getResolution(tileCoord[0]); + let tileExtent = tileGrid.getTileCoordExtent(tileCoord, this.tmpExtent_); + let tileSize = toSize( + tileGrid.getTileSize(tileCoord[0]), this.tmpSize); + + const gutter = this.gutter_; + if (gutter !== 0) { + tileSize = bufferSize(tileSize, gutter, this.tmpSize); + tileExtent = buffer(tileExtent, tileResolution * gutter, tileExtent); + } + + if (pixelRatio != 1) { + tileSize = scaleSize(tileSize, pixelRatio, this.tmpSize); + } + + const baseParams = { + 'SERVICE': 'WMS', + 'VERSION': DEFAULT_WMS_VERSION, + 'REQUEST': 'GetMap', + 'FORMAT': 'image/png', + 'TRANSPARENT': true + }; + assign(baseParams, this.params_); + + return this.getRequestUrl_(tileCoord, tileSize, tileExtent, + pixelRatio, projection, baseParams); + } + + /** + * Update the user-provided params. + * @param {Object} params Params. + * @api + */ + updateParams(params) { + assign(this.params_, params); + this.updateV13_(); + this.setKey(this.getKeyForParams_()); + } /** * @private - * @type {!Object} */ - this.params_ = params; - - /** - * @private - * @type {boolean} - */ - this.v13_ = true; - - /** - * @private - * @type {module:ol/source/WMSServerType|undefined} - */ - this.serverType_ = /** @type {module:ol/source/WMSServerType|undefined} */ (options.serverType); - - /** - * @private - * @type {boolean} - */ - this.hidpi_ = options.hidpi !== undefined ? options.hidpi : true; - - /** - * @private - * @type {module:ol/extent~Extent} - */ - this.tmpExtent_ = createEmpty(); - - this.updateV13_(); - this.setKey(this.getKeyForParams_()); - -}; + updateV13_() { + const version = this.params_['VERSION'] || DEFAULT_WMS_VERSION; + this.v13_ = compareVersions(version, '1.3') >= 0; + } +} inherits(TileWMS, TileImage); -/** - * Return the GetFeatureInfo URL for the passed coordinate, resolution, and - * projection. Return `undefined` if the GetFeatureInfo URL cannot be - * constructed. - * @param {module:ol/coordinate~Coordinate} coordinate Coordinate. - * @param {number} resolution Resolution. - * @param {module:ol/proj~ProjectionLike} projection Projection. - * @param {!Object} params GetFeatureInfo params. `INFO_FORMAT` at least should - * be provided. If `QUERY_LAYERS` is not provided then the layers specified - * in the `LAYERS` parameter will be used. `VERSION` should not be - * specified here. - * @return {string|undefined} GetFeatureInfo URL. - * @api - */ -TileWMS.prototype.getGetFeatureInfoUrl = function(coordinate, resolution, projection, params) { - const projectionObj = getProjection(projection); - const sourceProjectionObj = this.getProjection(); - - let tileGrid = this.getTileGrid(); - if (!tileGrid) { - tileGrid = this.getTileGridForProjection(projectionObj); - } - - const tileCoord = tileGrid.getTileCoordForCoordAndResolution(coordinate, resolution); - - if (tileGrid.getResolutions().length <= tileCoord[0]) { - return undefined; - } - - let tileResolution = tileGrid.getResolution(tileCoord[0]); - let tileExtent = tileGrid.getTileCoordExtent(tileCoord, this.tmpExtent_); - let tileSize = toSize(tileGrid.getTileSize(tileCoord[0]), this.tmpSize); - - - const gutter = this.gutter_; - if (gutter !== 0) { - tileSize = bufferSize(tileSize, gutter, this.tmpSize); - tileExtent = buffer(tileExtent, tileResolution * gutter, tileExtent); - } - - if (sourceProjectionObj && sourceProjectionObj !== projectionObj) { - tileResolution = calculateSourceResolution(sourceProjectionObj, projectionObj, coordinate, tileResolution); - tileExtent = transformExtent(tileExtent, projectionObj, sourceProjectionObj); - coordinate = transform(coordinate, projectionObj, sourceProjectionObj); - } - - const baseParams = { - 'SERVICE': 'WMS', - 'VERSION': DEFAULT_WMS_VERSION, - 'REQUEST': 'GetFeatureInfo', - 'FORMAT': 'image/png', - 'TRANSPARENT': true, - 'QUERY_LAYERS': this.params_['LAYERS'] - }; - assign(baseParams, this.params_, params); - - const x = Math.floor((coordinate[0] - tileExtent[0]) / tileResolution); - const y = Math.floor((tileExtent[3] - coordinate[1]) / tileResolution); - - baseParams[this.v13_ ? 'I' : 'X'] = x; - baseParams[this.v13_ ? 'J' : 'Y'] = y; - - return this.getRequestUrl_(tileCoord, tileSize, tileExtent, - 1, sourceProjectionObj || projectionObj, baseParams); -}; - - -/** - * @inheritDoc - */ -TileWMS.prototype.getGutterInternal = function() { - return this.gutter_; -}; - - -/** - * Get the user-provided params, i.e. those passed to the constructor through - * the "params" option, and possibly updated using the updateParams method. - * @return {Object} Params. - * @api - */ -TileWMS.prototype.getParams = function() { - return this.params_; -}; - - -/** - * @param {module:ol/tilecoord~TileCoord} tileCoord Tile coordinate. - * @param {module:ol/size~Size} tileSize Tile size. - * @param {module:ol/extent~Extent} tileExtent Tile extent. - * @param {number} pixelRatio Pixel ratio. - * @param {module:ol/proj/Projection} projection Projection. - * @param {Object} params Params. - * @return {string|undefined} Request URL. - * @private - */ -TileWMS.prototype.getRequestUrl_ = function(tileCoord, tileSize, tileExtent, - pixelRatio, projection, params) { - - const urls = this.urls; - if (!urls) { - return undefined; - } - - params['WIDTH'] = tileSize[0]; - params['HEIGHT'] = tileSize[1]; - - params[this.v13_ ? 'CRS' : 'SRS'] = projection.getCode(); - - if (!('STYLES' in this.params_)) { - params['STYLES'] = ''; - } - - if (pixelRatio != 1) { - switch (this.serverType_) { - case WMSServerType.GEOSERVER: - const dpi = (90 * pixelRatio + 0.5) | 0; - if ('FORMAT_OPTIONS' in params) { - params['FORMAT_OPTIONS'] += ';dpi:' + dpi; - } else { - params['FORMAT_OPTIONS'] = 'dpi:' + dpi; - } - break; - case WMSServerType.MAPSERVER: - params['MAP_RESOLUTION'] = 90 * pixelRatio; - break; - case WMSServerType.CARMENTA_SERVER: - case WMSServerType.QGIS: - params['DPI'] = 90 * pixelRatio; - break; - default: - assert(false, 52); // Unknown `serverType` configured - break; - } - } - - const axisOrientation = projection.getAxisOrientation(); - const bbox = tileExtent; - if (this.v13_ && axisOrientation.substr(0, 2) == 'ne') { - let tmp; - tmp = tileExtent[0]; - bbox[0] = tileExtent[1]; - bbox[1] = tmp; - tmp = tileExtent[2]; - bbox[2] = tileExtent[3]; - bbox[3] = tmp; - } - params['BBOX'] = bbox.join(','); - - let url; - if (urls.length == 1) { - url = urls[0]; - } else { - const index = modulo(tileCoordHash(tileCoord), urls.length); - url = urls[index]; - } - return appendParams(url, params); -}; - - -/** - * @inheritDoc - */ -TileWMS.prototype.getTilePixelRatio = function(pixelRatio) { - return (!this.hidpi_ || this.serverType_ === undefined) ? 1 : - /** @type {number} */ (pixelRatio); -}; - - -/** - * @private - * @return {string} The key for the current params. - */ -TileWMS.prototype.getKeyForParams_ = function() { - let i = 0; - const res = []; - for (const key in this.params_) { - res[i++] = key + '-' + this.params_[key]; - } - return res.join('/'); -}; - - -/** - * @inheritDoc - */ -TileWMS.prototype.fixedTileUrlFunction = function(tileCoord, pixelRatio, projection) { - - let tileGrid = this.getTileGrid(); - if (!tileGrid) { - tileGrid = this.getTileGridForProjection(projection); - } - - if (tileGrid.getResolutions().length <= tileCoord[0]) { - return undefined; - } - - if (pixelRatio != 1 && (!this.hidpi_ || this.serverType_ === undefined)) { - pixelRatio = 1; - } - - const tileResolution = tileGrid.getResolution(tileCoord[0]); - let tileExtent = tileGrid.getTileCoordExtent(tileCoord, this.tmpExtent_); - let tileSize = toSize( - tileGrid.getTileSize(tileCoord[0]), this.tmpSize); - - const gutter = this.gutter_; - if (gutter !== 0) { - tileSize = bufferSize(tileSize, gutter, this.tmpSize); - tileExtent = buffer(tileExtent, tileResolution * gutter, tileExtent); - } - - if (pixelRatio != 1) { - tileSize = scaleSize(tileSize, pixelRatio, this.tmpSize); - } - - const baseParams = { - 'SERVICE': 'WMS', - 'VERSION': DEFAULT_WMS_VERSION, - 'REQUEST': 'GetMap', - 'FORMAT': 'image/png', - 'TRANSPARENT': true - }; - assign(baseParams, this.params_); - - return this.getRequestUrl_(tileCoord, tileSize, tileExtent, - pixelRatio, projection, baseParams); -}; - -/** - * Update the user-provided params. - * @param {Object} params Params. - * @api - */ -TileWMS.prototype.updateParams = function(params) { - assign(this.params_, params); - this.updateV13_(); - this.setKey(this.getKeyForParams_()); -}; - - -/** - * @private - */ -TileWMS.prototype.updateV13_ = function() { - const version = this.params_['VERSION'] || DEFAULT_WMS_VERSION; - this.v13_ = compareVersions(version, '1.3') >= 0; -}; export default TileWMS; diff --git a/src/ol/source/UrlTile.js b/src/ol/source/UrlTile.js index d8412a29f4..542c376d61 100644 --- a/src/ol/source/UrlTile.js +++ b/src/ol/source/UrlTile.js @@ -37,56 +37,173 @@ import {getKeyZXY} from '../tilecoord.js'; * @extends {module:ol/source/Tile} * @param {module:ol/source/UrlTile~Options=} options Image tile options. */ -const UrlTile = function(options) { +class UrlTile { + constructor(options) { - TileSource.call(this, { - attributions: options.attributions, - cacheSize: options.cacheSize, - extent: options.extent, - opaque: options.opaque, - projection: options.projection, - state: options.state, - tileGrid: options.tileGrid, - tilePixelRatio: options.tilePixelRatio, - wrapX: options.wrapX, - transition: options.transition - }); + TileSource.call(this, { + attributions: options.attributions, + cacheSize: options.cacheSize, + extent: options.extent, + opaque: options.opaque, + projection: options.projection, + state: options.state, + tileGrid: options.tileGrid, + tilePixelRatio: options.tilePixelRatio, + wrapX: options.wrapX, + transition: options.transition + }); - /** - * @protected - * @type {module:ol/Tile~LoadFunction} - */ - this.tileLoadFunction = options.tileLoadFunction; + /** + * @protected + * @type {module:ol/Tile~LoadFunction} + */ + this.tileLoadFunction = options.tileLoadFunction; - /** - * @protected - * @type {module:ol/Tile~UrlFunction} - */ - this.tileUrlFunction = this.fixedTileUrlFunction ? - this.fixedTileUrlFunction.bind(this) : nullTileUrlFunction; + /** + * @protected + * @type {module:ol/Tile~UrlFunction} + */ + this.tileUrlFunction = this.fixedTileUrlFunction ? + this.fixedTileUrlFunction.bind(this) : nullTileUrlFunction; - /** - * @protected - * @type {!Array.|null} - */ - this.urls = null; + /** + * @protected + * @type {!Array.|null} + */ + this.urls = null; + + if (options.urls) { + this.setUrls(options.urls); + } else if (options.url) { + this.setUrl(options.url); + } + if (options.tileUrlFunction) { + this.setTileUrlFunction(options.tileUrlFunction); + } + + /** + * @private + * @type {!Object.} + */ + this.tileLoadingKeys_ = {}; - if (options.urls) { - this.setUrls(options.urls); - } else if (options.url) { - this.setUrl(options.url); - } - if (options.tileUrlFunction) { - this.setTileUrlFunction(options.tileUrlFunction); } /** - * @private - * @type {!Object.} + * Return the tile load function of the source. + * @return {module:ol/Tile~LoadFunction} TileLoadFunction + * @api */ - this.tileLoadingKeys_ = {}; + getTileLoadFunction() { + return this.tileLoadFunction; + } -}; + /** + * Return the tile URL function of the source. + * @return {module:ol/Tile~UrlFunction} TileUrlFunction + * @api + */ + getTileUrlFunction() { + return this.tileUrlFunction; + } + + /** + * Return the URLs used for this source. + * When a tileUrlFunction is used instead of url or urls, + * null will be returned. + * @return {!Array.|null} URLs. + * @api + */ + getUrls() { + return this.urls; + } + + /** + * Handle tile change events. + * @param {module:ol/events/Event} event Event. + * @protected + */ + handleTileChange(event) { + const tile = /** @type {module:ol/Tile} */ (event.target); + const uid = getUid(tile); + const tileState = tile.getState(); + let type; + if (tileState == TileState.LOADING) { + this.tileLoadingKeys_[uid] = true; + type = TileEventType.TILELOADSTART; + } else if (uid in this.tileLoadingKeys_) { + delete this.tileLoadingKeys_[uid]; + type = tileState == TileState.ERROR ? TileEventType.TILELOADERROR : + (tileState == TileState.LOADED || tileState == TileState.ABORT) ? + TileEventType.TILELOADEND : undefined; + } + if (type != undefined) { + this.dispatchEvent(new TileSourceEvent(type, tile)); + } + } + + /** + * Set the tile load function of the source. + * @param {module:ol/Tile~LoadFunction} tileLoadFunction Tile load function. + * @api + */ + setTileLoadFunction(tileLoadFunction) { + this.tileCache.clear(); + this.tileLoadFunction = tileLoadFunction; + this.changed(); + } + + /** + * Set the tile URL function of the source. + * @param {module:ol/Tile~UrlFunction} tileUrlFunction Tile URL function. + * @param {string=} opt_key Optional new tile key for the source. + * @api + */ + setTileUrlFunction(tileUrlFunction, opt_key) { + this.tileUrlFunction = tileUrlFunction; + this.tileCache.pruneExceptNewestZ(); + if (typeof opt_key !== 'undefined') { + this.setKey(opt_key); + } else { + this.changed(); + } + } + + /** + * Set the URL to use for requests. + * @param {string} url URL. + * @api + */ + setUrl(url) { + const urls = this.urls = expandUrl(url); + this.setTileUrlFunction(this.fixedTileUrlFunction ? + this.fixedTileUrlFunction.bind(this) : + createFromTemplates(urls, this.tileGrid), url); + } + + /** + * Set the URLs to use for requests. + * @param {Array.} urls URLs. + * @api + */ + setUrls(urls) { + this.urls = urls; + const key = urls.join('\n'); + this.setTileUrlFunction(this.fixedTileUrlFunction ? + this.fixedTileUrlFunction.bind(this) : + createFromTemplates(urls, this.tileGrid), key); + } + + /** + * @inheritDoc + */ + useTile(z, x, y) { + const tileCoordKey = getKeyZXY(z, x, y); + if (this.tileCache.containsKey(tileCoordKey)) { + this.tileCache.get(tileCoordKey); + } + } +} inherits(UrlTile, TileSource); @@ -97,126 +214,4 @@ inherits(UrlTile, TileSource); */ UrlTile.prototype.fixedTileUrlFunction; -/** - * Return the tile load function of the source. - * @return {module:ol/Tile~LoadFunction} TileLoadFunction - * @api - */ -UrlTile.prototype.getTileLoadFunction = function() { - return this.tileLoadFunction; -}; - - -/** - * Return the tile URL function of the source. - * @return {module:ol/Tile~UrlFunction} TileUrlFunction - * @api - */ -UrlTile.prototype.getTileUrlFunction = function() { - return this.tileUrlFunction; -}; - - -/** - * Return the URLs used for this source. - * When a tileUrlFunction is used instead of url or urls, - * null will be returned. - * @return {!Array.|null} URLs. - * @api - */ -UrlTile.prototype.getUrls = function() { - return this.urls; -}; - - -/** - * Handle tile change events. - * @param {module:ol/events/Event} event Event. - * @protected - */ -UrlTile.prototype.handleTileChange = function(event) { - const tile = /** @type {module:ol/Tile} */ (event.target); - const uid = getUid(tile); - const tileState = tile.getState(); - let type; - if (tileState == TileState.LOADING) { - this.tileLoadingKeys_[uid] = true; - type = TileEventType.TILELOADSTART; - } else if (uid in this.tileLoadingKeys_) { - delete this.tileLoadingKeys_[uid]; - type = tileState == TileState.ERROR ? TileEventType.TILELOADERROR : - (tileState == TileState.LOADED || tileState == TileState.ABORT) ? - TileEventType.TILELOADEND : undefined; - } - if (type != undefined) { - this.dispatchEvent(new TileSourceEvent(type, tile)); - } -}; - - -/** - * Set the tile load function of the source. - * @param {module:ol/Tile~LoadFunction} tileLoadFunction Tile load function. - * @api - */ -UrlTile.prototype.setTileLoadFunction = function(tileLoadFunction) { - this.tileCache.clear(); - this.tileLoadFunction = tileLoadFunction; - this.changed(); -}; - - -/** - * Set the tile URL function of the source. - * @param {module:ol/Tile~UrlFunction} tileUrlFunction Tile URL function. - * @param {string=} opt_key Optional new tile key for the source. - * @api - */ -UrlTile.prototype.setTileUrlFunction = function(tileUrlFunction, opt_key) { - this.tileUrlFunction = tileUrlFunction; - this.tileCache.pruneExceptNewestZ(); - if (typeof opt_key !== 'undefined') { - this.setKey(opt_key); - } else { - this.changed(); - } -}; - - -/** - * Set the URL to use for requests. - * @param {string} url URL. - * @api - */ -UrlTile.prototype.setUrl = function(url) { - const urls = this.urls = expandUrl(url); - this.setTileUrlFunction(this.fixedTileUrlFunction ? - this.fixedTileUrlFunction.bind(this) : - createFromTemplates(urls, this.tileGrid), url); -}; - - -/** - * Set the URLs to use for requests. - * @param {Array.} urls URLs. - * @api - */ -UrlTile.prototype.setUrls = function(urls) { - this.urls = urls; - const key = urls.join('\n'); - this.setTileUrlFunction(this.fixedTileUrlFunction ? - this.fixedTileUrlFunction.bind(this) : - createFromTemplates(urls, this.tileGrid), key); -}; - - -/** - * @inheritDoc - */ -UrlTile.prototype.useTile = function(z, x, y) { - const tileCoordKey = getKeyZXY(z, x, y); - if (this.tileCache.containsKey(tileCoordKey)) { - this.tileCache.get(tileCoordKey); - } -}; export default UrlTile; From f78d0d4cfa46d8e5e8373ab4cfdf6b9640cf896a Mon Sep 17 00:00:00 2001 From: Tim Schaub Date: Mon, 16 Jul 2018 17:09:50 -0600 Subject: [PATCH 002/107] Manual class transform --- src/ol/AssertionError.js | 39 +- src/ol/Collection.js | 21 +- src/ol/MapBrowserPointerEvent.js | 24 +- src/ol/MapEvent.js | 35 +- src/ol/Object.js | 37 +- src/ol/Tile.js | 375 ++-- src/ol/TileCache.js | 74 +- src/ol/TileQueue.js | 184 +- src/ol/TileRange.js | 215 +- src/ol/VectorImageTile.js | 519 ++--- src/ol/VectorTile.js | 414 ++-- src/ol/View.js | 1804 ++++++++--------- src/ol/WebGLMap.js | 40 +- src/ol/events/Event.js | 61 +- src/ol/format/filter/And.js | 12 +- src/ol/format/filter/Bbox.js | 34 +- src/ol/format/filter/Comparison.js | 18 +- src/ol/format/filter/ComparisonBinary.js | 27 +- src/ol/format/filter/Contains.js | 11 +- src/ol/format/filter/During.js | 27 +- src/ol/format/filter/EqualTo.js | 11 +- src/ol/format/filter/GreaterThan.js | 11 +- src/ol/format/filter/GreaterThanOrEqualTo.js | 11 +- src/ol/format/filter/Intersects.js | 10 +- src/ol/format/filter/IsBetween.js | 27 +- src/ol/format/filter/IsLike.js | 51 +- src/ol/format/filter/IsNull.js | 12 +- src/ol/format/filter/LessThan.js | 11 +- src/ol/format/filter/LessThanOrEqualTo.js | 11 +- src/ol/format/filter/LogicalNary.js | 21 +- src/ol/format/filter/Not.js | 19 +- src/ol/format/filter/NotEqualTo.js | 11 +- src/ol/format/filter/Or.js | 13 +- src/ol/format/filter/Spatial.js | 34 +- src/ol/format/filter/Within.js | 9 +- src/ol/interaction/DoubleClickZoom.js | 36 +- src/ol/interaction/DragAndDrop.js | 47 +- src/ol/interaction/DragBox.js | 82 +- src/ol/interaction/DragPan.js | 65 +- src/ol/interaction/DragRotate.js | 51 +- src/ol/interaction/DragRotateAndZoom.js | 70 +- src/ol/interaction/Draw.js | 22 +- src/ol/interaction/Extent.js | 22 +- src/ol/interaction/KeyboardPan.js | 68 +- src/ol/interaction/KeyboardZoom.js | 46 +- src/ol/interaction/Modify.js | 35 +- src/ol/interaction/PinchRotate.js | 80 +- src/ol/interaction/PinchZoom.js | 70 +- src/ol/interaction/Select.js | 47 +- src/ol/interaction/Translate.js | 37 +- src/ol/layer/Image.js | 24 +- src/ol/proj/Projection.js | 172 +- src/ol/proj/epsg3857.js | 28 +- src/ol/proj/epsg4326.js | 26 +- src/ol/render/Event.js | 64 +- .../circlereplay/defaultshader/Locations.js | 129 +- .../defaultshader/Locations.js | 139 +- .../polygonreplay/defaultshader/Locations.js | 69 +- .../texturereplay/defaultshader/Locations.js | 109 +- .../webgl/defaultmapshader/Locations.js | 71 +- .../webgl/tilelayershader/Locations.js | 51 +- src/ol/source/Image.js | 23 +- src/ol/source/OSM.js | 56 +- src/ol/source/Raster.js | 49 +- src/ol/source/Stamen.js | 47 +- src/ol/source/Tile.js | 23 +- src/ol/source/UTFGrid.js | 166 +- src/ol/source/Vector.js | 237 +-- src/ol/source/VectorTile.js | 100 +- src/ol/source/WMTS.js | 258 +-- src/ol/source/XYZ.js | 59 +- src/ol/source/Zoomify.js | 276 +-- src/ol/structs/LRUCache.js | 501 ++--- src/ol/structs/LinkedList.js | 465 ++--- src/ol/structs/PriorityQueue.js | 478 ++--- src/ol/structs/RBush.js | 375 ++-- src/ol/style/Atlas.js | 285 ++- src/ol/style/AtlasManager.js | 353 ++-- src/ol/style/Circle.js | 84 +- src/ol/style/Fill.js | 129 +- src/ol/style/Icon.js | 620 +++--- src/ol/style/IconImage.js | 402 ++-- src/ol/style/IconImageCache.js | 157 +- src/ol/style/Image.js | 380 ++-- src/ol/style/RegularShape.js | 1011 +++++---- src/ol/style/Stroke.js | 495 +++-- src/ol/style/Style.js | 476 +++-- src/ol/style/Text.js | 929 ++++----- src/ol/tilegrid/TileGrid.js | 975 +++++---- src/ol/tilegrid/WMTS.js | 71 +- src/ol/webgl/Buffer.js | 52 +- src/ol/webgl/Context.js | 532 +++-- src/ol/webgl/Fragment.js | 21 +- src/ol/webgl/Shader.js | 40 +- src/ol/webgl/Vertex.js | 21 +- src/ol/webgl/shaderlocations.mustache | 37 +- 96 files changed, 8112 insertions(+), 7964 deletions(-) diff --git a/src/ol/AssertionError.js b/src/ol/AssertionError.js index c74c6bb869..02e091c2e2 100644 --- a/src/ol/AssertionError.js +++ b/src/ol/AssertionError.js @@ -11,29 +11,32 @@ import {VERSION, inherits} from './util.js'; * @extends {Error} * @param {number} code Error code. */ -const AssertionError = function(code) { +class AssertionError { - const path = VERSION.split('-')[0]; + constructor(code) { + const path = VERSION.split('-')[0]; - /** - * @type {string} - */ - this.message = 'Assertion failed. See https://openlayers.org/en/' + path + - '/doc/errors/#' + code + ' for details.'; + /** + * @type {string} + */ + this.message = 'Assertion failed. See https://openlayers.org/en/' + path + + '/doc/errors/#' + code + ' for details.'; - /** - * Error code. The meaning of the code can be found on - * {@link https://openlayers.org/en/latest/doc/errors/} (replace `latest` with - * the version found in the OpenLayers script's header comment if a version - * other than the latest is used). - * @type {number} - * @api - */ - this.code = code; + /** + * Error code. The meaning of the code can be found on + * {@link https://openlayers.org/en/latest/doc/errors/} (replace `latest` with + * the version found in the OpenLayers script's header comment if a version + * other than the latest is used). + * @type {number} + * @api + */ + this.code = code; - this.name = 'AssertionError'; + this.name = 'AssertionError'; -}; + } + +} inherits(AssertionError, Error); diff --git a/src/ol/Collection.js b/src/ol/Collection.js index b50ed0d40c..03cbf2c979 100644 --- a/src/ol/Collection.js +++ b/src/ol/Collection.js @@ -27,18 +27,21 @@ const Property = { * @param {module:ol/CollectionEventType} type Type. * @param {*=} opt_element Element. */ -export const CollectionEvent = function(type, opt_element) { +export class CollectionEvent { - Event.call(this, type); + constructor(type, opt_element) { + Event.call(this, type); - /** - * The element that is added to or removed from the collection. - * @type {*} - * @api - */ - this.element = opt_element; + /** + * The element that is added to or removed from the collection. + * @type {*} + * @api + */ + this.element = opt_element; -}; + } + +} inherits(CollectionEvent, Event); diff --git a/src/ol/MapBrowserPointerEvent.js b/src/ol/MapBrowserPointerEvent.js index d956bd6c6a..94cd226353 100644 --- a/src/ol/MapBrowserPointerEvent.js +++ b/src/ol/MapBrowserPointerEvent.js @@ -14,19 +14,23 @@ import MapBrowserEvent from './MapBrowserEvent.js'; * @param {boolean=} opt_dragging Is the map currently being dragged? * @param {?module:ol/PluggableMap~FrameState=} opt_frameState Frame state. */ -const MapBrowserPointerEvent = function(type, map, pointerEvent, opt_dragging, - opt_frameState) { +class MapBrowserPointerEvent { - MapBrowserEvent.call(this, type, map, pointerEvent.originalEvent, opt_dragging, - opt_frameState); + constructor(type, map, pointerEvent, opt_dragging, opt_frameState) { - /** - * @const - * @type {module:ol/pointer/PointerEvent} - */ - this.pointerEvent = pointerEvent; + MapBrowserEvent.call(this, type, map, pointerEvent.originalEvent, opt_dragging, + opt_frameState); -}; + /** + * @const + * @type {module:ol/pointer/PointerEvent} + */ + this.pointerEvent = pointerEvent; + + } + +} inherits(MapBrowserPointerEvent, MapBrowserEvent); + export default MapBrowserPointerEvent; diff --git a/src/ol/MapEvent.js b/src/ol/MapEvent.js index ae02a3bc06..7e6c4d7188 100644 --- a/src/ol/MapEvent.js +++ b/src/ol/MapEvent.js @@ -15,25 +15,30 @@ import Event from './events/Event.js'; * @param {module:ol/PluggableMap} map Map. * @param {?module:ol/PluggableMap~FrameState=} opt_frameState Frame state. */ -const MapEvent = function(type, map, opt_frameState) { +class MapEvent { - Event.call(this, type); + constructor(type, map, opt_frameState) { - /** - * The map where the event occurred. - * @type {module:ol/PluggableMap} - * @api - */ - this.map = map; + Event.call(this, type); - /** - * The frame state at the time of the event. - * @type {?module:ol/PluggableMap~FrameState} - * @api - */ - this.frameState = opt_frameState !== undefined ? opt_frameState : null; + /** + * The map where the event occurred. + * @type {module:ol/PluggableMap} + * @api + */ + this.map = map; -}; + /** + * The frame state at the time of the event. + * @type {?module:ol/PluggableMap~FrameState} + * @api + */ + this.frameState = opt_frameState !== undefined ? opt_frameState : null; + + } + +} inherits(MapEvent, Event); + export default MapEvent; diff --git a/src/ol/Object.js b/src/ol/Object.js index 39747bbb3e..f7406f6903 100644 --- a/src/ol/Object.js +++ b/src/ol/Object.js @@ -19,25 +19,30 @@ import {assign} from './obj.js'; * @extends {module:ol/events/Event} * @constructor */ -const ObjectEvent = function(type, key, oldValue) { - Event.call(this, type); +class ObjectEvent { - /** - * The name of the property whose value is changing. - * @type {string} - * @api - */ - this.key = key; + constructor(type, key, oldValue) { + Event.call(this, type); - /** - * The old value. To get the new value use `e.target.get(e.key)` where - * `e` is the event object. - * @type {*} - * @api - */ - this.oldValue = oldValue; + /** + * The name of the property whose value is changing. + * @type {string} + * @api + */ + this.key = key; + + /** + * The old value. To get the new value use `e.target.get(e.key)` where + * `e` is the event object. + * @type {*} + * @api + */ + this.oldValue = oldValue; + + } + +} -}; inherits(ObjectEvent, Event); diff --git a/src/ol/Tile.js b/src/ol/Tile.js index b32f5d97cb..c24fd2ce87 100644 --- a/src/ol/Tile.js +++ b/src/ol/Tile.js @@ -51,214 +51,215 @@ import EventType from './events/EventType.js'; * @param {module:ol/TileState} state State. * @param {module:ol/Tile~Options=} opt_options Tile options. */ -const Tile = function(tileCoord, state, opt_options) { - EventTarget.call(this); +class Tile { + constructor(tileCoord, state, opt_options) { + EventTarget.call(this); - const options = opt_options ? opt_options : {}; + const options = opt_options ? opt_options : {}; - /** - * @type {module:ol/tilecoord~TileCoord} - */ - this.tileCoord = tileCoord; + /** + * @type {module:ol/tilecoord~TileCoord} + */ + this.tileCoord = tileCoord; + + /** + * @protected + * @type {module:ol/TileState} + */ + this.state = state; + + /** + * An "interim" tile for this tile. The interim tile may be used while this + * one is loading, for "smooth" transitions when changing params/dimensions + * on the source. + * @type {module:ol/Tile} + */ + this.interimTile = null; + + /** + * A key assigned to the tile. This is used by the tile source to determine + * if this tile can effectively be used, or if a new tile should be created + * and this one be used as an interim tile for this new tile. + * @type {string} + */ + this.key = ''; + + /** + * The duration for the opacity transition. + * @type {number} + */ + this.transition_ = options.transition === undefined ? 250 : options.transition; + + /** + * Lookup of start times for rendering transitions. If the start time is + * equal to -1, the transition is complete. + * @type {Object.} + */ + this.transitionStarts_ = {}; + + } /** * @protected - * @type {module:ol/TileState} */ - this.state = state; + changed() { + this.dispatchEvent(EventType.CHANGE); + } /** - * An "interim" tile for this tile. The interim tile may be used while this - * one is loading, for "smooth" transitions when changing params/dimensions - * on the source. - * @type {module:ol/Tile} + * @return {string} Key. */ - this.interimTile = null; + getKey() { + return this.key + '/' + this.tileCoord; + } /** - * A key assigned to the tile. This is used by the tile source to determine - * if this tile can effectively be used, or if a new tile should be created - * and this one be used as an interim tile for this new tile. - * @type {string} + * Get the interim tile most suitable for rendering using the chain of interim + * tiles. This corresponds to the most recent tile that has been loaded, if no + * such tile exists, the original tile is returned. + * @return {!module:ol/Tile} Best tile for rendering. */ - this.key = ''; + getInterimTile() { + if (!this.interimTile) { + //empty chain + return this; + } + let tile = this.interimTile; + + // find the first loaded tile and return it. Since the chain is sorted in + // decreasing order of creation time, there is no need to search the remainder + // of the list (all those tiles correspond to older requests and will be + // cleaned up by refreshInterimChain) + do { + if (tile.getState() == TileState.LOADED) { + return tile; + } + tile = tile.interimTile; + } while (tile); + + // we can not find a better tile + return this; + } /** - * The duration for the opacity transition. - * @type {number} + * Goes through the chain of interim tiles and discards sections of the chain + * that are no longer relevant. */ - this.transition_ = options.transition === undefined ? 250 : options.transition; + refreshInterimChain() { + if (!this.interimTile) { + return; + } + + let tile = this.interimTile; + let prev = this; + + do { + if (tile.getState() == TileState.LOADED) { + //we have a loaded tile, we can discard the rest of the list + //we would could abort any LOADING tile request + //older than this tile (i.e. any LOADING tile following this entry in the chain) + tile.interimTile = null; + break; + } else if (tile.getState() == TileState.LOADING) { + //keep this LOADING tile any loaded tiles later in the chain are + //older than this tile, so we're still interested in the request + prev = tile; + } else if (tile.getState() == TileState.IDLE) { + //the head of the list is the most current tile, we don't need + //to start any other requests for this chain + prev.interimTile = tile.interimTile; + } else { + prev = tile; + } + tile = prev.interimTile; + } while (tile); + } /** - * Lookup of start times for rendering transitions. If the start time is - * equal to -1, the transition is complete. - * @type {Object.} + * Get the tile coordinate for this tile. + * @return {module:ol/tilecoord~TileCoord} The tile coordinate. + * @api */ - this.transitionStarts_ = {}; + getTileCoord() { + return this.tileCoord; + } -}; + /** + * @return {module:ol/TileState} State. + */ + getState() { + return this.state; + } + + /** + * @param {module:ol/TileState} state State. + */ + setState(state) { + this.state = state; + this.changed(); + } + + /** + * Load the image or retry if loading previously failed. + * Loading is taken care of by the tile queue, and calling this method is + * only needed for preloading or for reloading in case of an error. + * @abstract + * @api + */ + load() {} + + /** + * Get the alpha value for rendering. + * @param {number} id An id for the renderer. + * @param {number} time The render frame time. + * @return {number} A number between 0 and 1. + */ + getAlpha(id, time) { + if (!this.transition_) { + return 1; + } + + let start = this.transitionStarts_[id]; + if (!start) { + start = time; + this.transitionStarts_[id] = start; + } else if (start === -1) { + return 1; + } + + const delta = time - start + (1000 / 60); // avoid rendering at 0 + if (delta >= this.transition_) { + return 1; + } + return easeIn(delta / this.transition_); + } + + /** + * 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. + * @return {boolean} The tile is in transition. + */ + inTransition(id) { + if (!this.transition_) { + return false; + } + return this.transitionStarts_[id] !== -1; + } + + /** + * Mark a transition as complete. + * @param {number} id An id for the renderer. + */ + endTransition(id) { + if (this.transition_) { + this.transitionStarts_[id] = -1; + } + } +} inherits(Tile, EventTarget); -/** - * @protected - */ -Tile.prototype.changed = function() { - this.dispatchEvent(EventType.CHANGE); -}; - - -/** - * @return {string} Key. - */ -Tile.prototype.getKey = function() { - return this.key + '/' + this.tileCoord; -}; - -/** - * Get the interim tile most suitable for rendering using the chain of interim - * tiles. This corresponds to the most recent tile that has been loaded, if no - * such tile exists, the original tile is returned. - * @return {!module:ol/Tile} Best tile for rendering. - */ -Tile.prototype.getInterimTile = function() { - if (!this.interimTile) { - //empty chain - return this; - } - let tile = this.interimTile; - - // find the first loaded tile and return it. Since the chain is sorted in - // decreasing order of creation time, there is no need to search the remainder - // of the list (all those tiles correspond to older requests and will be - // cleaned up by refreshInterimChain) - do { - if (tile.getState() == TileState.LOADED) { - return tile; - } - tile = tile.interimTile; - } while (tile); - - // we can not find a better tile - return this; -}; - -/** - * Goes through the chain of interim tiles and discards sections of the chain - * that are no longer relevant. - */ -Tile.prototype.refreshInterimChain = function() { - if (!this.interimTile) { - return; - } - - let tile = this.interimTile; - let prev = this; - - do { - if (tile.getState() == TileState.LOADED) { - //we have a loaded tile, we can discard the rest of the list - //we would could abort any LOADING tile request - //older than this tile (i.e. any LOADING tile following this entry in the chain) - tile.interimTile = null; - break; - } else if (tile.getState() == TileState.LOADING) { - //keep this LOADING tile any loaded tiles later in the chain are - //older than this tile, so we're still interested in the request - prev = tile; - } else if (tile.getState() == TileState.IDLE) { - //the head of the list is the most current tile, we don't need - //to start any other requests for this chain - prev.interimTile = tile.interimTile; - } else { - prev = tile; - } - tile = prev.interimTile; - } while (tile); -}; - -/** - * Get the tile coordinate for this tile. - * @return {module:ol/tilecoord~TileCoord} The tile coordinate. - * @api - */ -Tile.prototype.getTileCoord = function() { - return this.tileCoord; -}; - - -/** - * @return {module:ol/TileState} State. - */ -Tile.prototype.getState = function() { - return this.state; -}; - -/** - * @param {module:ol/TileState} state State. - */ -Tile.prototype.setState = function(state) { - this.state = state; - this.changed(); -}; - -/** - * Load the image or retry if loading previously failed. - * Loading is taken care of by the tile queue, and calling this method is - * only needed for preloading or for reloading in case of an error. - * @abstract - * @api - */ -Tile.prototype.load = function() {}; - -/** - * Get the alpha value for rendering. - * @param {number} id An id for the renderer. - * @param {number} time The render frame time. - * @return {number} A number between 0 and 1. - */ -Tile.prototype.getAlpha = function(id, time) { - if (!this.transition_) { - return 1; - } - - let start = this.transitionStarts_[id]; - if (!start) { - start = time; - this.transitionStarts_[id] = start; - } else if (start === -1) { - return 1; - } - - const delta = time - start + (1000 / 60); // avoid rendering at 0 - if (delta >= this.transition_) { - return 1; - } - return easeIn(delta / this.transition_); -}; - -/** - * 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. - * @return {boolean} The tile is in transition. - */ -Tile.prototype.inTransition = function(id) { - if (!this.transition_) { - return false; - } - return this.transitionStarts_[id] !== -1; -}; - -/** - * Mark a transition as complete. - * @param {number} id An id for the renderer. - */ -Tile.prototype.endTransition = function(id) { - if (this.transition_) { - this.transitionStarts_[id] = -1; - } -}; export default Tile; diff --git a/src/ol/TileCache.js b/src/ol/TileCache.js index 3b12bbcf91..3db53f81ec 100644 --- a/src/ol/TileCache.js +++ b/src/ol/TileCache.js @@ -11,46 +11,48 @@ import {fromKey, getKey} from './tilecoord.js'; * @param {number=} opt_highWaterMark High water mark. * @struct */ -const TileCache = function(opt_highWaterMark) { +class TileCache { + constructor(opt_highWaterMark) { - LRUCache.call(this, opt_highWaterMark); + LRUCache.call(this, opt_highWaterMark); -}; + } + + /** + * @param {!Object.} usedTiles Used tiles. + */ + expireCache(usedTiles) { + while (this.canExpireCache()) { + const tile = this.peekLast(); + const zKey = tile.tileCoord[0].toString(); + if (zKey in usedTiles && usedTiles[zKey].contains(tile.tileCoord)) { + break; + } else { + this.pop().dispose(); + } + } + } + + /** + * Prune all tiles from the cache that don't have the same z as the newest tile. + */ + pruneExceptNewestZ() { + if (this.getCount() === 0) { + return; + } + const key = this.peekFirstKey(); + const tileCoord = fromKey(key); + const z = tileCoord[0]; + this.forEach(function(tile) { + if (tile.tileCoord[0] !== z) { + this.remove(getKey(tile.tileCoord)); + tile.dispose(); + } + }, this); + } +} inherits(TileCache, LRUCache); -/** - * @param {!Object.} usedTiles Used tiles. - */ -TileCache.prototype.expireCache = function(usedTiles) { - while (this.canExpireCache()) { - const tile = this.peekLast(); - const zKey = tile.tileCoord[0].toString(); - if (zKey in usedTiles && usedTiles[zKey].contains(tile.tileCoord)) { - break; - } else { - this.pop().dispose(); - } - } -}; - - -/** - * Prune all tiles from the cache that don't have the same z as the newest tile. - */ -TileCache.prototype.pruneExceptNewestZ = function() { - if (this.getCount() === 0) { - return; - } - const key = this.peekFirstKey(); - const tileCoord = fromKey(key); - const z = tileCoord[0]; - this.forEach(function(tile) { - if (tile.tileCoord[0] !== z) { - this.remove(getKey(tile.tileCoord)); - tile.dispose(); - } - }, this); -}; export default TileCache; diff --git a/src/ol/TileQueue.js b/src/ol/TileQueue.js index d10dc223bf..1c849c8ff8 100644 --- a/src/ol/TileQueue.js +++ b/src/ol/TileQueue.js @@ -22,115 +22,115 @@ import PriorityQueue from './structs/PriorityQueue.js'; * Function called on each tile change event. * @struct */ -const TileQueue = function(tilePriorityFunction, tileChangeCallback) { +class TileQueue { + constructor(tilePriorityFunction, tileChangeCallback) { + + PriorityQueue.call( + this, + /** + * @param {Array} element Element. + * @return {number} Priority. + */ + function(element) { + return tilePriorityFunction.apply(null, element); + }, + /** + * @param {Array} element Element. + * @return {string} Key. + */ + function(element) { + return (/** @type {module:ol/Tile} */ (element[0]).getKey()); + }); - PriorityQueue.call( - this, /** - * @param {Array} element Element. - * @return {number} Priority. + * @private + * @type {function(): ?} */ - function(element) { - return tilePriorityFunction.apply(null, element); - }, + this.tileChangeCallback_ = tileChangeCallback; + /** - * @param {Array} element Element. - * @return {string} Key. + * @private + * @type {number} */ - function(element) { - return (/** @type {module:ol/Tile} */ (element[0]).getKey()); - }); + this.tilesLoading_ = 0; + + /** + * @private + * @type {!Object.} + */ + this.tilesLoadingKeys_ = {}; + + } /** - * @private - * @type {function(): ?} + * @inheritDoc */ - this.tileChangeCallback_ = tileChangeCallback; + enqueue(element) { + const added = PriorityQueue.prototype.enqueue.call(this, element); + if (added) { + const tile = element[0]; + listen(tile, EventType.CHANGE, this.handleTileChange, this); + } + return added; + } /** - * @private - * @type {number} + * @return {number} Number of tiles loading. */ - this.tilesLoading_ = 0; + getTilesLoading() { + return this.tilesLoading_; + } /** - * @private - * @type {!Object.} + * @param {module:ol/events/Event} event Event. + * @protected */ - this.tilesLoadingKeys_ = {}; + handleTileChange(event) { + const tile = /** @type {module:ol/Tile} */ (event.target); + const state = tile.getState(); + if (state === TileState.LOADED || state === TileState.ERROR || + state === TileState.EMPTY || state === TileState.ABORT) { + unlisten(tile, EventType.CHANGE, this.handleTileChange, this); + const tileKey = tile.getKey(); + if (tileKey in this.tilesLoadingKeys_) { + delete this.tilesLoadingKeys_[tileKey]; + --this.tilesLoading_; + } + this.tileChangeCallback_(); + } + } -}; + /** + * @param {number} maxTotalLoading Maximum number tiles to load simultaneously. + * @param {number} maxNewLoads Maximum number of new tiles to load. + */ + loadMoreTiles(maxTotalLoading, maxNewLoads) { + let newLoads = 0; + let abortedTiles = false; + let state, tile, tileKey; + while (this.tilesLoading_ < maxTotalLoading && newLoads < maxNewLoads && + this.getCount() > 0) { + tile = /** @type {module:ol/Tile} */ (this.dequeue()[0]); + tileKey = tile.getKey(); + state = tile.getState(); + if (state === TileState.ABORT) { + abortedTiles = true; + } else if (state === TileState.IDLE && !(tileKey in this.tilesLoadingKeys_)) { + this.tilesLoadingKeys_[tileKey] = true; + ++this.tilesLoading_; + ++newLoads; + tile.load(); + } + } + if (newLoads === 0 && abortedTiles) { + // Do not stop the render loop when all wanted tiles were aborted due to + // a small, saturated tile cache. + this.tileChangeCallback_(); + } + } +} inherits(TileQueue, PriorityQueue); -/** - * @inheritDoc - */ -TileQueue.prototype.enqueue = function(element) { - const added = PriorityQueue.prototype.enqueue.call(this, element); - if (added) { - const tile = element[0]; - listen(tile, EventType.CHANGE, this.handleTileChange, this); - } - return added; -}; - - -/** - * @return {number} Number of tiles loading. - */ -TileQueue.prototype.getTilesLoading = function() { - return this.tilesLoading_; -}; - - -/** - * @param {module:ol/events/Event} event Event. - * @protected - */ -TileQueue.prototype.handleTileChange = function(event) { - const tile = /** @type {module:ol/Tile} */ (event.target); - const state = tile.getState(); - if (state === TileState.LOADED || state === TileState.ERROR || - state === TileState.EMPTY || state === TileState.ABORT) { - unlisten(tile, EventType.CHANGE, this.handleTileChange, this); - const tileKey = tile.getKey(); - if (tileKey in this.tilesLoadingKeys_) { - delete this.tilesLoadingKeys_[tileKey]; - --this.tilesLoading_; - } - this.tileChangeCallback_(); - } -}; - - -/** - * @param {number} maxTotalLoading Maximum number tiles to load simultaneously. - * @param {number} maxNewLoads Maximum number of new tiles to load. - */ -TileQueue.prototype.loadMoreTiles = function(maxTotalLoading, maxNewLoads) { - let newLoads = 0; - let abortedTiles = false; - let state, tile, tileKey; - while (this.tilesLoading_ < maxTotalLoading && newLoads < maxNewLoads && - this.getCount() > 0) { - tile = /** @type {module:ol/Tile} */ (this.dequeue()[0]); - tileKey = tile.getKey(); - state = tile.getState(); - if (state === TileState.ABORT) { - abortedTiles = true; - } else if (state === TileState.IDLE && !(tileKey in this.tilesLoadingKeys_)) { - this.tilesLoadingKeys_[tileKey] = true; - ++this.tilesLoading_; - ++newLoads; - tile.load(); - } - } - if (newLoads === 0 && abortedTiles) { - // Do not stop the render loop when all wanted tiles were aborted due to - // a small, saturated tile cache. - this.tileChangeCallback_(); - } -}; export default TileQueue; diff --git a/src/ol/TileRange.js b/src/ol/TileRange.js index efa9129c42..8411a25e61 100644 --- a/src/ol/TileRange.js +++ b/src/ol/TileRange.js @@ -12,29 +12,116 @@ * @param {number} maxY Maximum Y. * @struct */ -const TileRange = function(minX, maxX, minY, maxY) { +class TileRange { + constructor(minX, maxX, minY, maxY) { - /** - * @type {number} - */ - this.minX = minX; + /** + * @type {number} + */ + this.minX = minX; - /** - * @type {number} - */ - this.maxX = maxX; + /** + * @type {number} + */ + this.maxX = maxX; - /** - * @type {number} - */ - this.minY = minY; + /** + * @type {number} + */ + this.minY = minY; - /** - * @type {number} - */ - this.maxY = maxY; + /** + * @type {number} + */ + this.maxY = maxY; -}; + } + + /** + * @param {module:ol/tilecoord~TileCoord} tileCoord Tile coordinate. + * @return {boolean} Contains tile coordinate. + */ + contains(tileCoord) { + return this.containsXY(tileCoord[1], tileCoord[2]); + } + + /** + * @param {module:ol/TileRange} tileRange Tile range. + * @return {boolean} Contains. + */ + containsTileRange(tileRange) { + return this.minX <= tileRange.minX && tileRange.maxX <= this.maxX && + this.minY <= tileRange.minY && tileRange.maxY <= this.maxY; + } + + /** + * @param {number} x Tile coordinate x. + * @param {number} y Tile coordinate y. + * @return {boolean} Contains coordinate. + */ + containsXY(x, y) { + return this.minX <= x && x <= this.maxX && this.minY <= y && y <= this.maxY; + } + + /** + * @param {module:ol/TileRange} tileRange Tile range. + * @return {boolean} Equals. + */ + equals(tileRange) { + return this.minX == tileRange.minX && this.minY == tileRange.minY && + this.maxX == tileRange.maxX && this.maxY == tileRange.maxY; + } + + /** + * @param {module:ol/TileRange} tileRange Tile range. + */ + extend(tileRange) { + if (tileRange.minX < this.minX) { + this.minX = tileRange.minX; + } + if (tileRange.maxX > this.maxX) { + this.maxX = tileRange.maxX; + } + if (tileRange.minY < this.minY) { + this.minY = tileRange.minY; + } + if (tileRange.maxY > this.maxY) { + this.maxY = tileRange.maxY; + } + } + + /** + * @return {number} Height. + */ + getHeight() { + return this.maxY - this.minY + 1; + } + + /** + * @return {module:ol/size~Size} Size. + */ + getSize() { + return [this.getWidth(), this.getHeight()]; + } + + /** + * @return {number} Width. + */ + getWidth() { + return this.maxX - this.minX + 1; + } + + /** + * @param {module:ol/TileRange} tileRange Tile range. + * @return {boolean} Intersects. + */ + intersects(tileRange) { + return this.minX <= tileRange.maxX && + this.maxX >= tileRange.minX && + this.minY <= tileRange.maxY && + this.maxY >= tileRange.minY; + } +} /** @@ -58,96 +145,4 @@ export function createOrUpdate(minX, maxX, minY, maxY, tileRange) { } -/** - * @param {module:ol/tilecoord~TileCoord} tileCoord Tile coordinate. - * @return {boolean} Contains tile coordinate. - */ -TileRange.prototype.contains = function(tileCoord) { - return this.containsXY(tileCoord[1], tileCoord[2]); -}; - - -/** - * @param {module:ol/TileRange} tileRange Tile range. - * @return {boolean} Contains. - */ -TileRange.prototype.containsTileRange = function(tileRange) { - return this.minX <= tileRange.minX && tileRange.maxX <= this.maxX && - this.minY <= tileRange.minY && tileRange.maxY <= this.maxY; -}; - - -/** - * @param {number} x Tile coordinate x. - * @param {number} y Tile coordinate y. - * @return {boolean} Contains coordinate. - */ -TileRange.prototype.containsXY = function(x, y) { - return this.minX <= x && x <= this.maxX && this.minY <= y && y <= this.maxY; -}; - - -/** - * @param {module:ol/TileRange} tileRange Tile range. - * @return {boolean} Equals. - */ -TileRange.prototype.equals = function(tileRange) { - return this.minX == tileRange.minX && this.minY == tileRange.minY && - this.maxX == tileRange.maxX && this.maxY == tileRange.maxY; -}; - - -/** - * @param {module:ol/TileRange} tileRange Tile range. - */ -TileRange.prototype.extend = function(tileRange) { - if (tileRange.minX < this.minX) { - this.minX = tileRange.minX; - } - if (tileRange.maxX > this.maxX) { - this.maxX = tileRange.maxX; - } - if (tileRange.minY < this.minY) { - this.minY = tileRange.minY; - } - if (tileRange.maxY > this.maxY) { - this.maxY = tileRange.maxY; - } -}; - - -/** - * @return {number} Height. - */ -TileRange.prototype.getHeight = function() { - return this.maxY - this.minY + 1; -}; - - -/** - * @return {module:ol/size~Size} Size. - */ -TileRange.prototype.getSize = function() { - return [this.getWidth(), this.getHeight()]; -}; - - -/** - * @return {number} Width. - */ -TileRange.prototype.getWidth = function() { - return this.maxX - this.minX + 1; -}; - - -/** - * @param {module:ol/TileRange} tileRange Tile range. - * @return {boolean} Intersects. - */ -TileRange.prototype.intersects = function(tileRange) { - return this.minX <= tileRange.maxX && - this.maxX >= tileRange.minX && - this.minY <= tileRange.maxY && - this.maxY >= tileRange.minY; -}; export default TileRange; diff --git a/src/ol/VectorImageTile.js b/src/ol/VectorImageTile.js index 2fb5edda7a..255775a2dd 100644 --- a/src/ol/VectorImageTile.js +++ b/src/ol/VectorImageTile.js @@ -43,286 +43,295 @@ import {UNDEFINED} from './functions.js'; * Function to call when a source tile's state changes. * @param {number} zoom Integer zoom to render the tile for. */ -const VectorImageTile = function(tileCoord, state, sourceRevision, format, - tileLoadFunction, urlTileCoord, tileUrlFunction, sourceTileGrid, tileGrid, - sourceTiles, pixelRatio, projection, tileClass, handleTileChange, zoom) { +class VectorImageTile { + constructor( + tileCoord, + state, + sourceRevision, + format, + tileLoadFunction, + urlTileCoord, + tileUrlFunction, + sourceTileGrid, + tileGrid, + sourceTiles, + pixelRatio, + projection, + tileClass, + handleTileChange, + zoom + ) { - Tile.call(this, tileCoord, state, {transition: 0}); + Tile.call(this, tileCoord, state, {transition: 0}); - /** - * @private - * @type {!Object.} - */ - this.context_ = {}; + /** + * @private + * @type {!Object.} + */ + this.context_ = {}; - /** - * @private - * @type {module:ol/featureloader~FeatureLoader} - */ - this.loader_; + /** + * @private + * @type {module:ol/featureloader~FeatureLoader} + */ + this.loader_; - /** - * @private - * @type {!Object.} - */ - this.replayState_ = {}; + /** + * @private + * @type {!Object.} + */ + this.replayState_ = {}; - /** - * @private - * @type {Object.} - */ - this.sourceTiles_ = sourceTiles; + /** + * @private + * @type {Object.} + */ + this.sourceTiles_ = sourceTiles; - /** - * Keys of source tiles used by this tile. Use with {@link #getTile}. - * @type {Array.} - */ - this.tileKeys = []; + /** + * Keys of source tiles used by this tile. Use with {@link #getTile}. + * @type {Array.} + */ + this.tileKeys = []; - /** - * @type {module:ol/extent~Extent} - */ - this.extent = null; + /** + * @type {module:ol/extent~Extent} + */ + this.extent = null; - /** - * @type {number} - */ - this.sourceRevision_ = sourceRevision; + /** + * @type {number} + */ + this.sourceRevision_ = sourceRevision; - /** - * @type {module:ol/tilecoord~TileCoord} - */ - this.wrappedTileCoord = urlTileCoord; + /** + * @type {module:ol/tilecoord~TileCoord} + */ + this.wrappedTileCoord = urlTileCoord; - /** - * @type {Array.} - */ - this.loadListenerKeys_ = []; + /** + * @type {Array.} + */ + this.loadListenerKeys_ = []; - /** - * @type {Array.} - */ - this.sourceTileListenerKeys_ = []; + /** + * @type {Array.} + */ + this.sourceTileListenerKeys_ = []; - if (urlTileCoord) { - const extent = this.extent = tileGrid.getTileCoordExtent(urlTileCoord); - const resolution = tileGrid.getResolution(zoom); - const sourceZ = sourceTileGrid.getZForResolution(resolution); - const useLoadedOnly = zoom != tileCoord[0]; - let loadCount = 0; - sourceTileGrid.forEachTileCoord(extent, sourceZ, function(sourceTileCoord) { - let sharedExtent = getIntersection(extent, - sourceTileGrid.getTileCoordExtent(sourceTileCoord)); - const sourceExtent = sourceTileGrid.getExtent(); - if (sourceExtent) { - sharedExtent = getIntersection(sharedExtent, sourceExtent, sharedExtent); - } - if (getWidth(sharedExtent) / resolution >= 0.5 && - getHeight(sharedExtent) / resolution >= 0.5) { - // only include source tile if overlap is at least 1 pixel - ++loadCount; - const sourceTileKey = sourceTileCoord.toString(); - let sourceTile = sourceTiles[sourceTileKey]; - if (!sourceTile && !useLoadedOnly) { - const tileUrl = tileUrlFunction(sourceTileCoord, pixelRatio, projection); - sourceTile = sourceTiles[sourceTileKey] = new tileClass(sourceTileCoord, - tileUrl == undefined ? TileState.EMPTY : TileState.IDLE, - tileUrl == undefined ? '' : tileUrl, - format, tileLoadFunction); - this.sourceTileListenerKeys_.push( - listen(sourceTile, EventType.CHANGE, handleTileChange)); + if (urlTileCoord) { + const extent = this.extent = tileGrid.getTileCoordExtent(urlTileCoord); + const resolution = tileGrid.getResolution(zoom); + const sourceZ = sourceTileGrid.getZForResolution(resolution); + const useLoadedOnly = zoom != tileCoord[0]; + let loadCount = 0; + sourceTileGrid.forEachTileCoord(extent, sourceZ, function(sourceTileCoord) { + let sharedExtent = getIntersection(extent, + sourceTileGrid.getTileCoordExtent(sourceTileCoord)); + const sourceExtent = sourceTileGrid.getExtent(); + if (sourceExtent) { + sharedExtent = getIntersection(sharedExtent, sourceExtent, sharedExtent); } - if (sourceTile && (!useLoadedOnly || sourceTile.getState() == TileState.LOADED)) { - sourceTile.consumers++; - this.tileKeys.push(sourceTileKey); + if (getWidth(sharedExtent) / resolution >= 0.5 && + getHeight(sharedExtent) / resolution >= 0.5) { + // only include source tile if overlap is at least 1 pixel + ++loadCount; + const sourceTileKey = sourceTileCoord.toString(); + let sourceTile = sourceTiles[sourceTileKey]; + if (!sourceTile && !useLoadedOnly) { + const tileUrl = tileUrlFunction(sourceTileCoord, pixelRatio, projection); + sourceTile = sourceTiles[sourceTileKey] = new tileClass(sourceTileCoord, + tileUrl == undefined ? TileState.EMPTY : TileState.IDLE, + tileUrl == undefined ? '' : tileUrl, + format, tileLoadFunction); + this.sourceTileListenerKeys_.push( + listen(sourceTile, EventType.CHANGE, handleTileChange)); + } + if (sourceTile && (!useLoadedOnly || sourceTile.getState() == TileState.LOADED)) { + sourceTile.consumers++; + this.tileKeys.push(sourceTileKey); + } + } + }.bind(this)); + + if (useLoadedOnly && loadCount == this.tileKeys.length) { + this.finishLoading_(); + } + + if (zoom <= tileCoord[0] && this.state != TileState.LOADED) { + while (zoom > tileGrid.getMinZoom()) { + const tile = new VectorImageTile(tileCoord, state, sourceRevision, + format, tileLoadFunction, urlTileCoord, tileUrlFunction, + sourceTileGrid, tileGrid, sourceTiles, pixelRatio, projection, + tileClass, UNDEFINED, --zoom); + if (tile.state == TileState.LOADED) { + this.interimTile = tile; + break; + } } } - }.bind(this)); - - if (useLoadedOnly && loadCount == this.tileKeys.length) { - this.finishLoading_(); } - if (zoom <= tileCoord[0] && this.state != TileState.LOADED) { - while (zoom > tileGrid.getMinZoom()) { - const tile = new VectorImageTile(tileCoord, state, sourceRevision, - format, tileLoadFunction, urlTileCoord, tileUrlFunction, - sourceTileGrid, tileGrid, sourceTiles, pixelRatio, projection, - tileClass, UNDEFINED, --zoom); - if (tile.state == TileState.LOADED) { - this.interimTile = tile; - break; - } + } + + /** + * @inheritDoc + */ + disposeInternal() { + this.state = TileState.ABORT; + this.changed(); + if (this.interimTile) { + this.interimTile.dispose(); + } + + for (let i = 0, ii = this.tileKeys.length; i < ii; ++i) { + const sourceTileKey = this.tileKeys[i]; + const sourceTile = this.getTile(sourceTileKey); + sourceTile.consumers--; + if (sourceTile.consumers == 0) { + delete this.sourceTiles_[sourceTileKey]; + sourceTile.dispose(); } } + this.tileKeys.length = 0; + this.sourceTiles_ = null; + this.loadListenerKeys_.forEach(unlistenByKey); + this.loadListenerKeys_.length = 0; + this.sourceTileListenerKeys_.forEach(unlistenByKey); + this.sourceTileListenerKeys_.length = 0; + Tile.prototype.disposeInternal.call(this); + } + + /** + * @param {module:ol/layer/Layer} layer Layer. + * @return {CanvasRenderingContext2D} The rendering context. + */ + getContext(layer) { + const key = getUid(layer).toString(); + if (!(key in this.context_)) { + this.context_[key] = createCanvasContext2D(); + } + return this.context_[key]; + } + + /** + * Get the Canvas for this tile. + * @param {module:ol/layer/Layer} layer Layer. + * @return {HTMLCanvasElement} Canvas. + */ + getImage(layer) { + return this.getReplayState(layer).renderedTileRevision == -1 ? + null : this.getContext(layer).canvas; + } + + /** + * @param {module:ol/layer/Layer} layer Layer. + * @return {module:ol/VectorImageTile~ReplayState} The replay state. + */ + getReplayState(layer) { + const key = getUid(layer).toString(); + if (!(key in this.replayState_)) { + this.replayState_[key] = { + dirty: false, + renderedRenderOrder: null, + renderedRevision: -1, + renderedTileRevision: -1 + }; + } + return this.replayState_[key]; + } + + /** + * @inheritDoc + */ + getKey() { + return this.tileKeys.join('/') + '-' + this.sourceRevision_; + } + + /** + * @param {string} tileKey Key (tileCoord) of the source tile. + * @return {module:ol/VectorTile} Source tile. + */ + getTile(tileKey) { + return this.sourceTiles_[tileKey]; + } + + /** + * @inheritDoc + */ + load() { + // Source tiles with LOADED state - we just count them because once they are + // loaded, we're no longer listening to state changes. + let leftToLoad = 0; + // Source tiles with ERROR state - we track them because they can still have + // an ERROR state after another load attempt. + const errorSourceTiles = {}; + + if (this.state == TileState.IDLE) { + this.setState(TileState.LOADING); + } + if (this.state == TileState.LOADING) { + this.tileKeys.forEach(function(sourceTileKey) { + const sourceTile = this.getTile(sourceTileKey); + if (sourceTile.state == TileState.IDLE) { + sourceTile.setLoader(this.loader_); + sourceTile.load(); + } + if (sourceTile.state == TileState.LOADING) { + const key = listen(sourceTile, EventType.CHANGE, function(e) { + const state = sourceTile.getState(); + if (state == TileState.LOADED || + state == TileState.ERROR) { + const uid = getUid(sourceTile); + if (state == TileState.ERROR) { + errorSourceTiles[uid] = true; + } else { + --leftToLoad; + delete errorSourceTiles[uid]; + } + if (leftToLoad - Object.keys(errorSourceTiles).length == 0) { + this.finishLoading_(); + } + } + }.bind(this)); + this.loadListenerKeys_.push(key); + ++leftToLoad; + } + }.bind(this)); + } + if (leftToLoad - Object.keys(errorSourceTiles).length == 0) { + setTimeout(this.finishLoading_.bind(this), 0); + } } -}; + /** + * @private + */ + finishLoading_() { + let loaded = this.tileKeys.length; + let empty = 0; + for (let i = loaded - 1; i >= 0; --i) { + const state = this.getTile(this.tileKeys[i]).getState(); + if (state != TileState.LOADED) { + --loaded; + } + if (state == TileState.EMPTY) { + ++empty; + } + } + if (loaded == this.tileKeys.length) { + this.loadListenerKeys_.forEach(unlistenByKey); + this.loadListenerKeys_.length = 0; + this.setState(TileState.LOADED); + } else { + this.setState(empty == this.tileKeys.length ? TileState.EMPTY : TileState.ERROR); + } + } +} inherits(VectorImageTile, Tile); -/** - * @inheritDoc - */ -VectorImageTile.prototype.disposeInternal = function() { - this.state = TileState.ABORT; - this.changed(); - if (this.interimTile) { - this.interimTile.dispose(); - } - - for (let i = 0, ii = this.tileKeys.length; i < ii; ++i) { - const sourceTileKey = this.tileKeys[i]; - const sourceTile = this.getTile(sourceTileKey); - sourceTile.consumers--; - if (sourceTile.consumers == 0) { - delete this.sourceTiles_[sourceTileKey]; - sourceTile.dispose(); - } - } - this.tileKeys.length = 0; - this.sourceTiles_ = null; - this.loadListenerKeys_.forEach(unlistenByKey); - this.loadListenerKeys_.length = 0; - this.sourceTileListenerKeys_.forEach(unlistenByKey); - this.sourceTileListenerKeys_.length = 0; - Tile.prototype.disposeInternal.call(this); -}; - - -/** - * @param {module:ol/layer/Layer} layer Layer. - * @return {CanvasRenderingContext2D} The rendering context. - */ -VectorImageTile.prototype.getContext = function(layer) { - const key = getUid(layer).toString(); - if (!(key in this.context_)) { - this.context_[key] = createCanvasContext2D(); - } - return this.context_[key]; -}; - - -/** - * Get the Canvas for this tile. - * @param {module:ol/layer/Layer} layer Layer. - * @return {HTMLCanvasElement} Canvas. - */ -VectorImageTile.prototype.getImage = function(layer) { - return this.getReplayState(layer).renderedTileRevision == -1 ? - null : this.getContext(layer).canvas; -}; - - -/** - * @param {module:ol/layer/Layer} layer Layer. - * @return {module:ol/VectorImageTile~ReplayState} The replay state. - */ -VectorImageTile.prototype.getReplayState = function(layer) { - const key = getUid(layer).toString(); - if (!(key in this.replayState_)) { - this.replayState_[key] = { - dirty: false, - renderedRenderOrder: null, - renderedRevision: -1, - renderedTileRevision: -1 - }; - } - return this.replayState_[key]; -}; - - -/** - * @inheritDoc - */ -VectorImageTile.prototype.getKey = function() { - return this.tileKeys.join('/') + '-' + this.sourceRevision_; -}; - - -/** - * @param {string} tileKey Key (tileCoord) of the source tile. - * @return {module:ol/VectorTile} Source tile. - */ -VectorImageTile.prototype.getTile = function(tileKey) { - return this.sourceTiles_[tileKey]; -}; - - -/** - * @inheritDoc - */ -VectorImageTile.prototype.load = function() { - // Source tiles with LOADED state - we just count them because once they are - // loaded, we're no longer listening to state changes. - let leftToLoad = 0; - // Source tiles with ERROR state - we track them because they can still have - // an ERROR state after another load attempt. - const errorSourceTiles = {}; - - if (this.state == TileState.IDLE) { - this.setState(TileState.LOADING); - } - if (this.state == TileState.LOADING) { - this.tileKeys.forEach(function(sourceTileKey) { - const sourceTile = this.getTile(sourceTileKey); - if (sourceTile.state == TileState.IDLE) { - sourceTile.setLoader(this.loader_); - sourceTile.load(); - } - if (sourceTile.state == TileState.LOADING) { - const key = listen(sourceTile, EventType.CHANGE, function(e) { - const state = sourceTile.getState(); - if (state == TileState.LOADED || - state == TileState.ERROR) { - const uid = getUid(sourceTile); - if (state == TileState.ERROR) { - errorSourceTiles[uid] = true; - } else { - --leftToLoad; - delete errorSourceTiles[uid]; - } - if (leftToLoad - Object.keys(errorSourceTiles).length == 0) { - this.finishLoading_(); - } - } - }.bind(this)); - this.loadListenerKeys_.push(key); - ++leftToLoad; - } - }.bind(this)); - } - if (leftToLoad - Object.keys(errorSourceTiles).length == 0) { - setTimeout(this.finishLoading_.bind(this), 0); - } -}; - - -/** - * @private - */ -VectorImageTile.prototype.finishLoading_ = function() { - let loaded = this.tileKeys.length; - let empty = 0; - for (let i = loaded - 1; i >= 0; --i) { - const state = this.getTile(this.tileKeys[i]).getState(); - if (state != TileState.LOADED) { - --loaded; - } - if (state == TileState.EMPTY) { - ++empty; - } - } - if (loaded == this.tileKeys.length) { - this.loadListenerKeys_.forEach(unlistenByKey); - this.loadListenerKeys_.length = 0; - this.setState(TileState.LOADED); - } else { - this.setState(empty == this.tileKeys.length ? TileState.EMPTY : TileState.ERROR); - } -}; - export default VectorImageTile; /** diff --git a/src/ol/VectorTile.js b/src/ol/VectorTile.js index 15a2c09a72..7dee5f3245 100644 --- a/src/ol/VectorTile.js +++ b/src/ol/VectorTile.js @@ -21,65 +21,218 @@ import TileState from './TileState.js'; * @param {module:ol/Tile~LoadFunction} tileLoadFunction Tile load function. * @param {module:ol/Tile~Options=} opt_options Tile options. */ -const VectorTile = function(tileCoord, state, src, format, tileLoadFunction, opt_options) { +class VectorTile { + constructor(tileCoord, state, src, format, tileLoadFunction, opt_options) { - Tile.call(this, tileCoord, state, opt_options); + Tile.call(this, tileCoord, state, opt_options); - /** - * @type {number} - */ - this.consumers = 0; + /** + * @type {number} + */ + this.consumers = 0; - /** - * @private - * @type {module:ol/extent~Extent} - */ - this.extent_ = null; + /** + * @private + * @type {module:ol/extent~Extent} + */ + this.extent_ = null; - /** - * @private - * @type {module:ol/format/Feature} - */ - this.format_ = format; + /** + * @private + * @type {module:ol/format/Feature} + */ + this.format_ = format; - /** - * @private - * @type {Array.} - */ - this.features_ = null; + /** + * @private + * @type {Array.} + */ + this.features_ = null; - /** - * @private - * @type {module:ol/featureloader~FeatureLoader} - */ - this.loader_; + /** + * @private + * @type {module:ol/featureloader~FeatureLoader} + */ + this.loader_; - /** - * Data projection - * @private - * @type {module:ol/proj/Projection} - */ - this.projection_ = null; + /** + * Data projection + * @private + * @type {module:ol/proj/Projection} + */ + this.projection_ = null; - /** - * @private - * @type {Object.} - */ - this.replayGroups_ = {}; + /** + * @private + * @type {Object.} + */ + this.replayGroups_ = {}; - /** - * @private - * @type {module:ol/Tile~LoadFunction} - */ - this.tileLoadFunction_ = tileLoadFunction; + /** + * @private + * @type {module:ol/Tile~LoadFunction} + */ + this.tileLoadFunction_ = tileLoadFunction; - /** - * @private - * @type {string} - */ - this.url_ = src; + /** + * @private + * @type {string} + */ + this.url_ = src; -}; + } + + /** + * @inheritDoc + */ + disposeInternal() { + this.features_ = null; + this.replayGroups_ = {}; + this.state = TileState.ABORT; + this.changed(); + Tile.prototype.disposeInternal.call(this); + } + + /** + * Gets the extent of the vector tile. + * @return {module:ol/extent~Extent} The extent. + * @api + */ + getExtent() { + return this.extent_ || DEFAULT_EXTENT; + } + + /** + * Get the feature format assigned for reading this tile's features. + * @return {module:ol/format/Feature} Feature format. + * @api + */ + getFormat() { + return this.format_; + } + + /** + * Get the features for this tile. Geometries will be in the projection returned + * by {@link module:ol/VectorTile~VectorTile#getProjection}. + * @return {Array.} Features. + * @api + */ + getFeatures() { + return this.features_; + } + + /** + * @inheritDoc + */ + getKey() { + return this.url_; + } + + /** + * Get the feature projection of features returned by + * {@link module:ol/VectorTile~VectorTile#getFeatures}. + * @return {module:ol/proj/Projection} Feature projection. + * @api + */ + getProjection() { + return this.projection_; + } + + /** + * @param {module:ol/layer/Layer} layer Layer. + * @param {string} key Key. + * @return {module:ol/render/ReplayGroup} Replay group. + */ + getReplayGroup(layer, key) { + return this.replayGroups_[getUid(layer) + ',' + key]; + } + + /** + * @inheritDoc + */ + load() { + if (this.state == TileState.IDLE) { + this.setState(TileState.LOADING); + this.tileLoadFunction_(this, this.url_); + this.loader_(null, NaN, null); + } + } + + /** + * Handler for successful tile load. + * @param {Array.} features The loaded features. + * @param {module:ol/proj/Projection} dataProjection Data projection. + * @param {module:ol/extent~Extent} extent Extent. + */ + onLoad(features, dataProjection, extent) { + this.setProjection(dataProjection); + this.setFeatures(features); + this.setExtent(extent); + } + + /** + * Handler for tile load errors. + */ + onError() { + this.setState(TileState.ERROR); + } + + /** + * Function for use in an {@link module:ol/source/VectorTile~VectorTile}'s + * `tileLoadFunction`. Sets the extent of the vector tile. This is only required + * for tiles in projections with `tile-pixels` as units. The extent should be + * set to `[0, 0, tilePixelSize, tilePixelSize]`, where `tilePixelSize` is + * calculated by multiplying the tile size with the tile pixel ratio. For + * sources using {@link module:ol/format/MVT~MVT} as feature format, the + * {@link module:ol/format/MVT~MVT#getLastExtent} method will return the correct + * extent. The default is `[0, 0, 4096, 4096]`. + * @param {module:ol/extent~Extent} extent The extent. + * @api + */ + setExtent(extent) { + this.extent_ = extent; + } + + /** + * Function for use in an {@link module:ol/source/VectorTile~VectorTile}'s `tileLoadFunction`. + * Sets the features for the tile. + * @param {Array.} features Features. + * @api + */ + setFeatures(features) { + this.features_ = features; + this.setState(TileState.LOADED); + } + + /** + * Function for use in an {@link module:ol/source/VectorTile~VectorTile}'s `tileLoadFunction`. + * Sets the projection of the features that were added with + * {@link module:ol/VectorTile~VectorTile#setFeatures}. + * @param {module:ol/proj/Projection} projection Feature projection. + * @api + */ + setProjection(projection) { + this.projection_ = projection; + } + + /** + * @param {module:ol/layer/Layer} layer Layer. + * @param {string} key Key. + * @param {module:ol/render/ReplayGroup} replayGroup Replay group. + */ + setReplayGroup(layer, key, replayGroup) { + this.replayGroups_[getUid(layer) + ',' + key] = replayGroup; + } + + /** + * Set the feature loader for reading this tile's features. + * @param {module:ol/featureloader~FeatureLoader} loader Feature loader. + * @api + */ + setLoader(loader) { + this.loader_ = loader; + } +} inherits(VectorTile, Tile); @@ -90,169 +243,4 @@ inherits(VectorTile, Tile); const DEFAULT_EXTENT = [0, 0, 4096, 4096]; -/** - * @inheritDoc - */ -VectorTile.prototype.disposeInternal = function() { - this.features_ = null; - this.replayGroups_ = {}; - this.state = TileState.ABORT; - this.changed(); - Tile.prototype.disposeInternal.call(this); -}; - - -/** - * Gets the extent of the vector tile. - * @return {module:ol/extent~Extent} The extent. - * @api - */ -VectorTile.prototype.getExtent = function() { - return this.extent_ || DEFAULT_EXTENT; -}; - - -/** - * Get the feature format assigned for reading this tile's features. - * @return {module:ol/format/Feature} Feature format. - * @api - */ -VectorTile.prototype.getFormat = function() { - return this.format_; -}; - - -/** - * Get the features for this tile. Geometries will be in the projection returned - * by {@link module:ol/VectorTile~VectorTile#getProjection}. - * @return {Array.} Features. - * @api - */ -VectorTile.prototype.getFeatures = function() { - return this.features_; -}; - - -/** - * @inheritDoc - */ -VectorTile.prototype.getKey = function() { - return this.url_; -}; - - -/** - * Get the feature projection of features returned by - * {@link module:ol/VectorTile~VectorTile#getFeatures}. - * @return {module:ol/proj/Projection} Feature projection. - * @api - */ -VectorTile.prototype.getProjection = function() { - return this.projection_; -}; - - -/** - * @param {module:ol/layer/Layer} layer Layer. - * @param {string} key Key. - * @return {module:ol/render/ReplayGroup} Replay group. - */ -VectorTile.prototype.getReplayGroup = function(layer, key) { - return this.replayGroups_[getUid(layer) + ',' + key]; -}; - - -/** - * @inheritDoc - */ -VectorTile.prototype.load = function() { - if (this.state == TileState.IDLE) { - this.setState(TileState.LOADING); - this.tileLoadFunction_(this, this.url_); - this.loader_(null, NaN, null); - } -}; - - -/** - * Handler for successful tile load. - * @param {Array.} features The loaded features. - * @param {module:ol/proj/Projection} dataProjection Data projection. - * @param {module:ol/extent~Extent} extent Extent. - */ -VectorTile.prototype.onLoad = function(features, dataProjection, extent) { - this.setProjection(dataProjection); - this.setFeatures(features); - this.setExtent(extent); -}; - - -/** - * Handler for tile load errors. - */ -VectorTile.prototype.onError = function() { - this.setState(TileState.ERROR); -}; - - -/** - * Function for use in an {@link module:ol/source/VectorTile~VectorTile}'s - * `tileLoadFunction`. Sets the extent of the vector tile. This is only required - * for tiles in projections with `tile-pixels` as units. The extent should be - * set to `[0, 0, tilePixelSize, tilePixelSize]`, where `tilePixelSize` is - * calculated by multiplying the tile size with the tile pixel ratio. For - * sources using {@link module:ol/format/MVT~MVT} as feature format, the - * {@link module:ol/format/MVT~MVT#getLastExtent} method will return the correct - * extent. The default is `[0, 0, 4096, 4096]`. - * @param {module:ol/extent~Extent} extent The extent. - * @api - */ -VectorTile.prototype.setExtent = function(extent) { - this.extent_ = extent; -}; - - -/** - * Function for use in an {@link module:ol/source/VectorTile~VectorTile}'s `tileLoadFunction`. - * Sets the features for the tile. - * @param {Array.} features Features. - * @api - */ -VectorTile.prototype.setFeatures = function(features) { - this.features_ = features; - this.setState(TileState.LOADED); -}; - - -/** - * Function for use in an {@link module:ol/source/VectorTile~VectorTile}'s `tileLoadFunction`. - * Sets the projection of the features that were added with - * {@link module:ol/VectorTile~VectorTile#setFeatures}. - * @param {module:ol/proj/Projection} projection Feature projection. - * @api - */ -VectorTile.prototype.setProjection = function(projection) { - this.projection_ = projection; -}; - - -/** - * @param {module:ol/layer/Layer} layer Layer. - * @param {string} key Key. - * @param {module:ol/render/ReplayGroup} replayGroup Replay group. - */ -VectorTile.prototype.setReplayGroup = function(layer, key, replayGroup) { - this.replayGroups_[getUid(layer) + ',' + key] = replayGroup; -}; - - -/** - * Set the feature loader for reading this tile's features. - * @param {module:ol/featureloader~FeatureLoader} loader Feature loader. - * @api - */ -VectorTile.prototype.setLoader = function(loader) { - this.loader_ = loader; -}; - export default VectorTile; diff --git a/src/ol/View.js b/src/ol/View.js index d5a0242b76..8983eb1a34 100644 --- a/src/ol/View.js +++ b/src/ol/View.js @@ -229,995 +229,957 @@ const DEFAULT_MIN_ZOOM = 0; * @param {module:ol/View~ViewOptions=} opt_options View options. * @api */ -const View = function(opt_options) { - BaseObject.call(this); +class View { + constructor(opt_options) { + BaseObject.call(this); - const options = assign({}, opt_options); + const options = assign({}, opt_options); - /** - * @private - * @type {Array.} - */ - this.hints_ = [0, 0]; + /** + * @private + * @type {Array.} + */ + this.hints_ = [0, 0]; - /** - * @private - * @type {Array.>} - */ - this.animations_ = []; + /** + * @private + * @type {Array.>} + */ + this.animations_ = []; - /** - * @private - * @type {number|undefined} - */ - this.updateAnimationKey_; + /** + * @private + * @type {number|undefined} + */ + this.updateAnimationKey_; - this.updateAnimations_ = this.updateAnimations_.bind(this); + this.updateAnimations_ = this.updateAnimations_.bind(this); - /** - * @private - * @const - * @type {module:ol/proj/Projection} - */ - this.projection_ = createProjection(options.projection, 'EPSG:3857'); + /** + * @private + * @const + * @type {module:ol/proj/Projection} + */ + this.projection_ = createProjection(options.projection, 'EPSG:3857'); - this.applyOptions_(options); -}; - -inherits(View, BaseObject); - - -/** - * Set up the view with the given options. - * @param {module:ol/View~ViewOptions} options View options. - */ -View.prototype.applyOptions_ = function(options) { - - /** - * @type {Object.} - */ - const properties = {}; - properties[ViewProperty.CENTER] = options.center !== undefined ? - options.center : null; - - const resolutionConstraintInfo = createResolutionConstraint(options); - - /** - * @private - * @type {number} - */ - this.maxResolution_ = resolutionConstraintInfo.maxResolution; - - /** - * @private - * @type {number} - */ - this.minResolution_ = resolutionConstraintInfo.minResolution; - - /** - * @private - * @type {number} - */ - this.zoomFactor_ = resolutionConstraintInfo.zoomFactor; - - /** - * @private - * @type {Array.|undefined} - */ - this.resolutions_ = options.resolutions; - - /** - * @private - * @type {number} - */ - this.minZoom_ = resolutionConstraintInfo.minZoom; - - const centerConstraint = createCenterConstraint(options); - const resolutionConstraint = resolutionConstraintInfo.constraint; - const rotationConstraint = createRotationConstraint(options); - - /** - * @private - * @type {module:ol/View~Constraints} - */ - this.constraints_ = { - center: centerConstraint, - resolution: resolutionConstraint, - rotation: rotationConstraint - }; - - if (options.resolution !== undefined) { - properties[ViewProperty.RESOLUTION] = options.resolution; - } else if (options.zoom !== undefined) { - properties[ViewProperty.RESOLUTION] = this.constrainResolution( - this.maxResolution_, options.zoom - this.minZoom_); - - if (this.resolutions_) { // in case map zoom is out of min/max zoom range - properties[ViewProperty.RESOLUTION] = clamp( - Number(this.getResolution() || properties[ViewProperty.RESOLUTION]), - this.minResolution_, this.maxResolution_); - } - } - properties[ViewProperty.ROTATION] = options.rotation !== undefined ? options.rotation : 0; - this.setProperties(properties); - - /** - * @private - * @type {module:ol/View~ViewOptions} - */ - this.options_ = options; - -}; - -/** - * Get an updated version of the view options used to construct the view. The - * current resolution (or zoom), center, and rotation are applied to any stored - * options. The provided options can be used to apply new min/max zoom or - * resolution limits. - * @param {module:ol/View~ViewOptions} newOptions New options to be applied. - * @return {module:ol/View~ViewOptions} New options updated with the current view state. - */ -View.prototype.getUpdatedOptions_ = function(newOptions) { - const options = assign({}, this.options_); - - // preserve resolution (or zoom) - if (options.resolution !== undefined) { - options.resolution = this.getResolution(); - } else { - options.zoom = this.getZoom(); + this.applyOptions_(options); } - // preserve center - options.center = this.getCenter(); + /** + * Set up the view with the given options. + * @param {module:ol/View~ViewOptions} options View options. + */ + applyOptions_(options) { - // preserve rotation - options.rotation = this.getRotation(); + /** + * @type {Object.} + */ + const properties = {}; + properties[ViewProperty.CENTER] = options.center !== undefined ? + options.center : null; - return assign({}, options, newOptions); -}; + const resolutionConstraintInfo = createResolutionConstraint(options); + /** + * @private + * @type {number} + */ + this.maxResolution_ = resolutionConstraintInfo.maxResolution; + + /** + * @private + * @type {number} + */ + this.minResolution_ = resolutionConstraintInfo.minResolution; + + /** + * @private + * @type {number} + */ + this.zoomFactor_ = resolutionConstraintInfo.zoomFactor; + + /** + * @private + * @type {Array.|undefined} + */ + this.resolutions_ = options.resolutions; + + /** + * @private + * @type {number} + */ + this.minZoom_ = resolutionConstraintInfo.minZoom; + + const centerConstraint = createCenterConstraint(options); + const resolutionConstraint = resolutionConstraintInfo.constraint; + const rotationConstraint = createRotationConstraint(options); + + /** + * @private + * @type {module:ol/View~Constraints} + */ + this.constraints_ = { + center: centerConstraint, + resolution: resolutionConstraint, + rotation: rotationConstraint + }; + + if (options.resolution !== undefined) { + properties[ViewProperty.RESOLUTION] = options.resolution; + } else if (options.zoom !== undefined) { + properties[ViewProperty.RESOLUTION] = this.constrainResolution( + this.maxResolution_, options.zoom - this.minZoom_); + + if (this.resolutions_) { // in case map zoom is out of min/max zoom range + properties[ViewProperty.RESOLUTION] = clamp( + Number(this.getResolution() || properties[ViewProperty.RESOLUTION]), + this.minResolution_, this.maxResolution_); + } + } + properties[ViewProperty.ROTATION] = options.rotation !== undefined ? options.rotation : 0; + this.setProperties(properties); + + /** + * @private + * @type {module:ol/View~ViewOptions} + */ + this.options_ = options; -/** - * Animate the view. The view's center, zoom (or resolution), and rotation - * can be animated for smooth transitions between view states. For example, - * to animate the view to a new zoom level: - * - * view.animate({zoom: view.getZoom() + 1}); - * - * By default, the animation lasts one second and uses in-and-out easing. You - * can customize this behavior by including `duration` (in milliseconds) and - * `easing` options (see {@link module:ol/easing}). - * - * To chain together multiple animations, call the method with multiple - * animation objects. For example, to first zoom and then pan: - * - * view.animate({zoom: 10}, {center: [0, 0]}); - * - * If you provide a function as the last argument to the animate method, it - * will get called at the end of an animation series. The callback will be - * called with `true` if the animation series completed on its own or `false` - * if it was cancelled. - * - * Animations are cancelled by user interactions (e.g. dragging the map) or by - * calling `view.setCenter()`, `view.setResolution()`, or `view.setRotation()` - * (or another method that calls one of these). - * - * @param {...(module:ol/View~AnimationOptions|function(boolean))} var_args Animation - * options. Multiple animations can be run in series by passing multiple - * options objects. To run multiple animations in parallel, call the method - * multiple times. An optional callback can be provided as a final - * argument. The callback will be called with a boolean indicating whether - * the animation completed without being cancelled. - * @api - */ -View.prototype.animate = function(var_args) { - let animationCount = arguments.length; - let callback; - if (animationCount > 1 && typeof arguments[animationCount - 1] === 'function') { - callback = arguments[animationCount - 1]; - --animationCount; } - if (!this.isDef()) { - // if view properties are not yet set, shortcut to the final state - const state = arguments[animationCount - 1]; - if (state.center) { - this.setCenter(state.center); - } - if (state.zoom !== undefined) { - this.setZoom(state.zoom); - } - if (state.rotation !== undefined) { - this.setRotation(state.rotation); - } - if (callback) { - setTimeout(function() { - callback(true); - }, 0); - } - return; - } - let start = Date.now(); - let center = this.getCenter().slice(); - let resolution = this.getResolution(); - let rotation = this.getRotation(); - const series = []; - for (let i = 0; i < animationCount; ++i) { - const options = /** @type {module:ol/View~AnimationOptions} */ (arguments[i]); - const animation = /** @type {module:ol/View~Animation} */ ({ - start: start, - complete: false, - anchor: options.anchor, - duration: options.duration !== undefined ? options.duration : 1000, - easing: options.easing || inAndOut - }); + /** + * Get an updated version of the view options used to construct the view. The + * current resolution (or zoom), center, and rotation are applied to any stored + * options. The provided options can be used to apply new min/max zoom or + * resolution limits. + * @param {module:ol/View~ViewOptions} newOptions New options to be applied. + * @return {module:ol/View~ViewOptions} New options updated with the current view state. + */ + getUpdatedOptions_(newOptions) { + const options = assign({}, this.options_); - if (options.center) { - animation.sourceCenter = center; - animation.targetCenter = options.center; - center = animation.targetCenter; - } - - if (options.zoom !== undefined) { - animation.sourceResolution = resolution; - animation.targetResolution = this.constrainResolution( - this.maxResolution_, options.zoom - this.minZoom_, 0); - resolution = animation.targetResolution; - } else if (options.resolution) { - animation.sourceResolution = resolution; - animation.targetResolution = options.resolution; - resolution = animation.targetResolution; - } - - if (options.rotation !== undefined) { - animation.sourceRotation = rotation; - const delta = modulo(options.rotation - rotation + Math.PI, 2 * Math.PI) - Math.PI; - animation.targetRotation = rotation + delta; - rotation = animation.targetRotation; - } - - animation.callback = callback; - - // check if animation is a no-op - if (isNoopAnimation(animation)) { - animation.complete = true; - // we still push it onto the series for callback handling + // preserve resolution (or zoom) + if (options.resolution !== undefined) { + options.resolution = this.getResolution(); } else { - start += animation.duration; + options.zoom = this.getZoom(); } - series.push(animation); + + // preserve center + options.center = this.getCenter(); + + // preserve rotation + options.rotation = this.getRotation(); + + return assign({}, options, newOptions); } - this.animations_.push(series); - this.setHint(ViewHint.ANIMATING, 1); - this.updateAnimations_(); -}; - -/** - * Determine if the view is being animated. - * @return {boolean} The view is being animated. - * @api - */ -View.prototype.getAnimating = function() { - return this.hints_[ViewHint.ANIMATING] > 0; -}; - - -/** - * Determine if the user is interacting with the view, such as panning or zooming. - * @return {boolean} The view is being interacted with. - * @api - */ -View.prototype.getInteracting = function() { - return this.hints_[ViewHint.INTERACTING] > 0; -}; - - -/** - * Cancel any ongoing animations. - * @api - */ -View.prototype.cancelAnimations = function() { - this.setHint(ViewHint.ANIMATING, -this.hints_[ViewHint.ANIMATING]); - for (let i = 0, ii = this.animations_.length; i < ii; ++i) { - const series = this.animations_[i]; - if (series[0].callback) { - series[0].callback(false); + /** + * Animate the view. The view's center, zoom (or resolution), and rotation + * can be animated for smooth transitions between view states. For example, + * to animate the view to a new zoom level: + * + * view.animate({zoom: view.getZoom() + 1}); + * + * By default, the animation lasts one second and uses in-and-out easing. You + * can customize this behavior by including `duration` (in milliseconds) and + * `easing` options (see {@link module:ol/easing}). + * + * To chain together multiple animations, call the method with multiple + * animation objects. For example, to first zoom and then pan: + * + * view.animate({zoom: 10}, {center: [0, 0]}); + * + * If you provide a function as the last argument to the animate method, it + * will get called at the end of an animation series. The callback will be + * called with `true` if the animation series completed on its own or `false` + * if it was cancelled. + * + * Animations are cancelled by user interactions (e.g. dragging the map) or by + * calling `view.setCenter()`, `view.setResolution()`, or `view.setRotation()` + * (or another method that calls one of these). + * + * @param {...(module:ol/View~AnimationOptions|function(boolean))} var_args Animation + * options. Multiple animations can be run in series by passing multiple + * options objects. To run multiple animations in parallel, call the method + * multiple times. An optional callback can be provided as a final + * argument. The callback will be called with a boolean indicating whether + * the animation completed without being cancelled. + * @api + */ + animate(var_args) { + let animationCount = arguments.length; + let callback; + if (animationCount > 1 && typeof arguments[animationCount - 1] === 'function') { + callback = arguments[animationCount - 1]; + --animationCount; } - } - this.animations_.length = 0; -}; - -/** - * Update all animations. - */ -View.prototype.updateAnimations_ = function() { - if (this.updateAnimationKey_ !== undefined) { - cancelAnimationFrame(this.updateAnimationKey_); - this.updateAnimationKey_ = undefined; - } - if (!this.getAnimating()) { - return; - } - const now = Date.now(); - let more = false; - for (let i = this.animations_.length - 1; i >= 0; --i) { - const series = this.animations_[i]; - let seriesComplete = true; - for (let j = 0, jj = series.length; j < jj; ++j) { - const animation = series[j]; - if (animation.complete) { - continue; + if (!this.isDef()) { + // if view properties are not yet set, shortcut to the final state + const state = arguments[animationCount - 1]; + if (state.center) { + this.setCenter(state.center); } - const elapsed = now - animation.start; - let fraction = animation.duration > 0 ? elapsed / animation.duration : 1; - if (fraction >= 1) { - animation.complete = true; - fraction = 1; - } else { - seriesComplete = false; + if (state.zoom !== undefined) { + this.setZoom(state.zoom); } - const progress = animation.easing(fraction); - if (animation.sourceCenter) { - const x0 = animation.sourceCenter[0]; - const y0 = animation.sourceCenter[1]; - const x1 = animation.targetCenter[0]; - const y1 = animation.targetCenter[1]; - const x = x0 + progress * (x1 - x0); - const y = y0 + progress * (y1 - y0); - this.set(ViewProperty.CENTER, [x, y]); + if (state.rotation !== undefined) { + this.setRotation(state.rotation); } - if (animation.sourceResolution && animation.targetResolution) { - const resolution = progress === 1 ? - animation.targetResolution : - animation.sourceResolution + progress * (animation.targetResolution - animation.sourceResolution); - if (animation.anchor) { - this.set(ViewProperty.CENTER, - this.calculateCenterZoom(resolution, animation.anchor)); - } - this.set(ViewProperty.RESOLUTION, resolution); - } - if (animation.sourceRotation !== undefined && animation.targetRotation !== undefined) { - const rotation = progress === 1 ? - modulo(animation.targetRotation + Math.PI, 2 * Math.PI) - Math.PI : - animation.sourceRotation + progress * (animation.targetRotation - animation.sourceRotation); - if (animation.anchor) { - this.set(ViewProperty.CENTER, - this.calculateCenterRotate(rotation, animation.anchor)); - } - this.set(ViewProperty.ROTATION, rotation); - } - more = true; - if (!animation.complete) { - break; - } - } - if (seriesComplete) { - this.animations_[i] = null; - this.setHint(ViewHint.ANIMATING, -1); - const callback = series[0].callback; if (callback) { setTimeout(function() { callback(true); }, 0); } + return; + } + let start = Date.now(); + let center = this.getCenter().slice(); + let resolution = this.getResolution(); + let rotation = this.getRotation(); + const series = []; + for (let i = 0; i < animationCount; ++i) { + const options = /** @type {module:ol/View~AnimationOptions} */ (arguments[i]); + + const animation = /** @type {module:ol/View~Animation} */ ({ + start: start, + complete: false, + anchor: options.anchor, + duration: options.duration !== undefined ? options.duration : 1000, + easing: options.easing || inAndOut + }); + + if (options.center) { + animation.sourceCenter = center; + animation.targetCenter = options.center; + center = animation.targetCenter; + } + + if (options.zoom !== undefined) { + animation.sourceResolution = resolution; + animation.targetResolution = this.constrainResolution( + this.maxResolution_, options.zoom - this.minZoom_, 0); + resolution = animation.targetResolution; + } else if (options.resolution) { + animation.sourceResolution = resolution; + animation.targetResolution = options.resolution; + resolution = animation.targetResolution; + } + + if (options.rotation !== undefined) { + animation.sourceRotation = rotation; + const delta = modulo(options.rotation - rotation + Math.PI, 2 * Math.PI) - Math.PI; + animation.targetRotation = rotation + delta; + rotation = animation.targetRotation; + } + + animation.callback = callback; + + // check if animation is a no-op + if (isNoopAnimation(animation)) { + animation.complete = true; + // we still push it onto the series for callback handling + } else { + start += animation.duration; + } + series.push(animation); + } + this.animations_.push(series); + this.setHint(ViewHint.ANIMATING, 1); + this.updateAnimations_(); + } + + /** + * Determine if the view is being animated. + * @return {boolean} The view is being animated. + * @api + */ + getAnimating() { + return this.hints_[ViewHint.ANIMATING] > 0; + } + + /** + * Determine if the user is interacting with the view, such as panning or zooming. + * @return {boolean} The view is being interacted with. + * @api + */ + getInteracting() { + return this.hints_[ViewHint.INTERACTING] > 0; + } + + /** + * Cancel any ongoing animations. + * @api + */ + cancelAnimations() { + this.setHint(ViewHint.ANIMATING, -this.hints_[ViewHint.ANIMATING]); + for (let i = 0, ii = this.animations_.length; i < ii; ++i) { + const series = this.animations_[i]; + if (series[0].callback) { + series[0].callback(false); + } + } + this.animations_.length = 0; + } + + /** + * Update all animations. + */ + updateAnimations_() { + if (this.updateAnimationKey_ !== undefined) { + cancelAnimationFrame(this.updateAnimationKey_); + this.updateAnimationKey_ = undefined; + } + if (!this.getAnimating()) { + return; + } + const now = Date.now(); + let more = false; + for (let i = this.animations_.length - 1; i >= 0; --i) { + const series = this.animations_[i]; + let seriesComplete = true; + for (let j = 0, jj = series.length; j < jj; ++j) { + const animation = series[j]; + if (animation.complete) { + continue; + } + const elapsed = now - animation.start; + let fraction = animation.duration > 0 ? elapsed / animation.duration : 1; + if (fraction >= 1) { + animation.complete = true; + fraction = 1; + } else { + seriesComplete = false; + } + const progress = animation.easing(fraction); + if (animation.sourceCenter) { + const x0 = animation.sourceCenter[0]; + const y0 = animation.sourceCenter[1]; + const x1 = animation.targetCenter[0]; + const y1 = animation.targetCenter[1]; + const x = x0 + progress * (x1 - x0); + const y = y0 + progress * (y1 - y0); + this.set(ViewProperty.CENTER, [x, y]); + } + if (animation.sourceResolution && animation.targetResolution) { + const resolution = progress === 1 ? + animation.targetResolution : + animation.sourceResolution + progress * (animation.targetResolution - animation.sourceResolution); + if (animation.anchor) { + this.set(ViewProperty.CENTER, + this.calculateCenterZoom(resolution, animation.anchor)); + } + this.set(ViewProperty.RESOLUTION, resolution); + } + if (animation.sourceRotation !== undefined && animation.targetRotation !== undefined) { + const rotation = progress === 1 ? + modulo(animation.targetRotation + Math.PI, 2 * Math.PI) - Math.PI : + animation.sourceRotation + progress * (animation.targetRotation - animation.sourceRotation); + if (animation.anchor) { + this.set(ViewProperty.CENTER, + this.calculateCenterRotate(rotation, animation.anchor)); + } + this.set(ViewProperty.ROTATION, rotation); + } + more = true; + if (!animation.complete) { + break; + } + } + if (seriesComplete) { + this.animations_[i] = null; + this.setHint(ViewHint.ANIMATING, -1); + const callback = series[0].callback; + if (callback) { + setTimeout(function() { + callback(true); + }, 0); + } + } + } + // prune completed series + this.animations_ = this.animations_.filter(Boolean); + if (more && this.updateAnimationKey_ === undefined) { + this.updateAnimationKey_ = requestAnimationFrame(this.updateAnimations_); } } - // prune completed series - this.animations_ = this.animations_.filter(Boolean); - if (more && this.updateAnimationKey_ === undefined) { - this.updateAnimationKey_ = requestAnimationFrame(this.updateAnimations_); + + /** + * @param {number} rotation Target rotation. + * @param {module:ol/coordinate~Coordinate} anchor Rotation anchor. + * @return {module:ol/coordinate~Coordinate|undefined} Center for rotation and anchor. + */ + calculateCenterRotate(rotation, anchor) { + let center; + const currentCenter = this.getCenter(); + if (currentCenter !== undefined) { + center = [currentCenter[0] - anchor[0], currentCenter[1] - anchor[1]]; + rotateCoordinate(center, rotation - this.getRotation()); + addCoordinate(center, anchor); + } + return center; } -}; -/** - * @param {number} rotation Target rotation. - * @param {module:ol/coordinate~Coordinate} anchor Rotation anchor. - * @return {module:ol/coordinate~Coordinate|undefined} Center for rotation and anchor. - */ -View.prototype.calculateCenterRotate = function(rotation, anchor) { - let center; - const currentCenter = this.getCenter(); - if (currentCenter !== undefined) { - center = [currentCenter[0] - anchor[0], currentCenter[1] - anchor[1]]; - rotateCoordinate(center, rotation - this.getRotation()); - addCoordinate(center, anchor); + /** + * @param {number} resolution Target resolution. + * @param {module:ol/coordinate~Coordinate} anchor Zoom anchor. + * @return {module:ol/coordinate~Coordinate|undefined} Center for resolution and anchor. + */ + calculateCenterZoom(resolution, anchor) { + let center; + const currentCenter = this.getCenter(); + const currentResolution = this.getResolution(); + if (currentCenter !== undefined && currentResolution !== undefined) { + const x = anchor[0] - resolution * (anchor[0] - currentCenter[0]) / currentResolution; + const y = anchor[1] - resolution * (anchor[1] - currentCenter[1]) / currentResolution; + center = [x, y]; + } + return center; } - return center; -}; - -/** - * @param {number} resolution Target resolution. - * @param {module:ol/coordinate~Coordinate} anchor Zoom anchor. - * @return {module:ol/coordinate~Coordinate|undefined} Center for resolution and anchor. - */ -View.prototype.calculateCenterZoom = function(resolution, anchor) { - let center; - const currentCenter = this.getCenter(); - const currentResolution = this.getResolution(); - if (currentCenter !== undefined && currentResolution !== undefined) { - const x = anchor[0] - resolution * (anchor[0] - currentCenter[0]) / currentResolution; - const y = anchor[1] - resolution * (anchor[1] - currentCenter[1]) / currentResolution; - center = [x, y]; + /** + * @private + * @return {module:ol/size~Size} Viewport size or `[100, 100]` when no viewport is found. + */ + getSizeFromViewport_() { + const size = [100, 100]; + const selector = '.ol-viewport[data-view="' + getUid(this) + '"]'; + const element = document.querySelector(selector); + if (element) { + const metrics = getComputedStyle(element); + size[0] = parseInt(metrics.width, 10); + size[1] = parseInt(metrics.height, 10); + } + return size; } - return center; -}; - -/** - * @private - * @return {module:ol/size~Size} Viewport size or `[100, 100]` when no viewport is found. - */ -View.prototype.getSizeFromViewport_ = function() { - const size = [100, 100]; - const selector = '.ol-viewport[data-view="' + getUid(this) + '"]'; - const element = document.querySelector(selector); - if (element) { - const metrics = getComputedStyle(element); - size[0] = parseInt(metrics.width, 10); - size[1] = parseInt(metrics.height, 10); + /** + * Get the constrained center of this view. + * @param {module:ol/coordinate~Coordinate|undefined} center Center. + * @return {module:ol/coordinate~Coordinate|undefined} Constrained center. + * @api + */ + constrainCenter(center) { + return this.constraints_.center(center); } - return size; -}; - -/** - * Get the constrained center of this view. - * @param {module:ol/coordinate~Coordinate|undefined} center Center. - * @return {module:ol/coordinate~Coordinate|undefined} Constrained center. - * @api - */ -View.prototype.constrainCenter = function(center) { - return this.constraints_.center(center); -}; - - -/** - * Get the constrained resolution of this view. - * @param {number|undefined} resolution Resolution. - * @param {number=} opt_delta Delta. Default is `0`. - * @param {number=} opt_direction Direction. Default is `0`. - * @return {number|undefined} Constrained resolution. - * @api - */ -View.prototype.constrainResolution = function(resolution, opt_delta, opt_direction) { - const delta = opt_delta || 0; - const direction = opt_direction || 0; - return this.constraints_.resolution(resolution, delta, direction); -}; - - -/** - * Get the constrained rotation of this view. - * @param {number|undefined} rotation Rotation. - * @param {number=} opt_delta Delta. Default is `0`. - * @return {number|undefined} Constrained rotation. - * @api - */ -View.prototype.constrainRotation = function(rotation, opt_delta) { - const delta = opt_delta || 0; - return this.constraints_.rotation(rotation, delta); -}; - - -/** - * Get the view center. - * @return {module:ol/coordinate~Coordinate|undefined} The center of the view. - * @observable - * @api - */ -View.prototype.getCenter = function() { - return ( - /** @type {module:ol/coordinate~Coordinate|undefined} */ (this.get(ViewProperty.CENTER)) - ); -}; - - -/** - * @return {module:ol/View~Constraints} Constraints. - */ -View.prototype.getConstraints = function() { - return this.constraints_; -}; - - -/** - * @param {Array.=} opt_hints Destination array. - * @return {Array.} Hint. - */ -View.prototype.getHints = function(opt_hints) { - if (opt_hints !== undefined) { - opt_hints[0] = this.hints_[0]; - opt_hints[1] = this.hints_[1]; - return opt_hints; - } else { - return this.hints_.slice(); + /** + * Get the constrained resolution of this view. + * @param {number|undefined} resolution Resolution. + * @param {number=} opt_delta Delta. Default is `0`. + * @param {number=} opt_direction Direction. Default is `0`. + * @return {number|undefined} Constrained resolution. + * @api + */ + constrainResolution(resolution, opt_delta, opt_direction) { + const delta = opt_delta || 0; + const direction = opt_direction || 0; + return this.constraints_.resolution(resolution, delta, direction); } -}; - -/** - * Calculate the extent for the current view state and the passed size. - * The size is the pixel dimensions of the box into which the calculated extent - * should fit. In most cases you want to get the extent of the entire map, - * that is `map.getSize()`. - * @param {module:ol/size~Size=} opt_size Box pixel size. If not provided, the size of the - * first map that uses this view will be used. - * @return {module:ol/extent~Extent} Extent. - * @api - */ -View.prototype.calculateExtent = function(opt_size) { - const size = opt_size || this.getSizeFromViewport_(); - const center = /** @type {!module:ol/coordinate~Coordinate} */ (this.getCenter()); - assert(center, 1); // The view center is not defined - const resolution = /** @type {!number} */ (this.getResolution()); - assert(resolution !== undefined, 2); // The view resolution is not defined - const rotation = /** @type {!number} */ (this.getRotation()); - assert(rotation !== undefined, 3); // The view rotation is not defined - - return getForViewAndSize(center, resolution, rotation, size); -}; - - -/** - * Get the maximum resolution of the view. - * @return {number} The maximum resolution of the view. - * @api - */ -View.prototype.getMaxResolution = function() { - return this.maxResolution_; -}; - - -/** - * Get the minimum resolution of the view. - * @return {number} The minimum resolution of the view. - * @api - */ -View.prototype.getMinResolution = function() { - return this.minResolution_; -}; - - -/** - * Get the maximum zoom level for the view. - * @return {number} The maximum zoom level. - * @api - */ -View.prototype.getMaxZoom = function() { - return /** @type {number} */ (this.getZoomForResolution(this.minResolution_)); -}; - - -/** - * Set a new maximum zoom level for the view. - * @param {number} zoom The maximum zoom level. - * @api - */ -View.prototype.setMaxZoom = function(zoom) { - this.applyOptions_(this.getUpdatedOptions_({maxZoom: zoom})); -}; - - -/** - * Get the minimum zoom level for the view. - * @return {number} The minimum zoom level. - * @api - */ -View.prototype.getMinZoom = function() { - return /** @type {number} */ (this.getZoomForResolution(this.maxResolution_)); -}; - - -/** - * Set a new minimum zoom level for the view. - * @param {number} zoom The minimum zoom level. - * @api - */ -View.prototype.setMinZoom = function(zoom) { - this.applyOptions_(this.getUpdatedOptions_({minZoom: zoom})); -}; - - -/** - * Get the view projection. - * @return {module:ol/proj/Projection} The projection of the view. - * @api - */ -View.prototype.getProjection = function() { - return this.projection_; -}; - - -/** - * Get the view resolution. - * @return {number|undefined} The resolution of the view. - * @observable - * @api - */ -View.prototype.getResolution = function() { - return /** @type {number|undefined} */ (this.get(ViewProperty.RESOLUTION)); -}; - - -/** - * Get the resolutions for the view. This returns the array of resolutions - * passed to the constructor of the View, or undefined if none were given. - * @return {Array.|undefined} The resolutions of the view. - * @api - */ -View.prototype.getResolutions = function() { - return this.resolutions_; -}; - - -/** - * Get the resolution for a provided extent (in map units) and size (in pixels). - * @param {module:ol/extent~Extent} extent Extent. - * @param {module:ol/size~Size=} opt_size Box pixel size. - * @return {number} The resolution at which the provided extent will render at - * the given size. - * @api - */ -View.prototype.getResolutionForExtent = function(extent, opt_size) { - const size = opt_size || this.getSizeFromViewport_(); - const xResolution = getWidth(extent) / size[0]; - const yResolution = getHeight(extent) / size[1]; - return Math.max(xResolution, yResolution); -}; - - -/** - * Return a function that returns a value between 0 and 1 for a - * resolution. Exponential scaling is assumed. - * @param {number=} opt_power Power. - * @return {function(number): number} Resolution for value function. - */ -View.prototype.getResolutionForValueFunction = function(opt_power) { - const power = opt_power || 2; - const maxResolution = this.maxResolution_; - const minResolution = this.minResolution_; - const max = Math.log(maxResolution / minResolution) / Math.log(power); - return ( - /** - * @param {number} value Value. - * @return {number} Resolution. - */ - function(value) { - const resolution = maxResolution / Math.pow(power, value * max); - return resolution; - }); -}; - - -/** - * Get the view rotation. - * @return {number} The rotation of the view in radians. - * @observable - * @api - */ -View.prototype.getRotation = function() { - return /** @type {number} */ (this.get(ViewProperty.ROTATION)); -}; - - -/** - * Return a function that returns a resolution for a value between - * 0 and 1. Exponential scaling is assumed. - * @param {number=} opt_power Power. - * @return {function(number): number} Value for resolution function. - */ -View.prototype.getValueForResolutionFunction = function(opt_power) { - const power = opt_power || 2; - const maxResolution = this.maxResolution_; - const minResolution = this.minResolution_; - const max = Math.log(maxResolution / minResolution) / Math.log(power); - return ( - /** - * @param {number} resolution Resolution. - * @return {number} Value. - */ - function(resolution) { - const value = (Math.log(maxResolution / resolution) / Math.log(power)) / max; - return value; - }); -}; - - -/** - * @return {module:ol/View~State} View state. - */ -View.prototype.getState = function() { - const center = /** @type {module:ol/coordinate~Coordinate} */ (this.getCenter()); - const projection = this.getProjection(); - const resolution = /** @type {number} */ (this.getResolution()); - const rotation = this.getRotation(); - return ( - /** @type {module:ol/View~State} */ ({ - center: center.slice(), - projection: projection !== undefined ? projection : null, - resolution: resolution, - rotation: rotation, - zoom: this.getZoom() - }) - ); -}; - - -/** - * Get the current zoom level. If you configured your view with a resolutions - * array (this is rare), this method may return non-integer zoom levels (so - * the zoom level is not safe to use as an index into a resolutions array). - * @return {number|undefined} Zoom. - * @api - */ -View.prototype.getZoom = function() { - let zoom; - const resolution = this.getResolution(); - if (resolution !== undefined) { - zoom = this.getZoomForResolution(resolution); + /** + * Get the constrained rotation of this view. + * @param {number|undefined} rotation Rotation. + * @param {number=} opt_delta Delta. Default is `0`. + * @return {number|undefined} Constrained rotation. + * @api + */ + constrainRotation(rotation, opt_delta) { + const delta = opt_delta || 0; + return this.constraints_.rotation(rotation, delta); } - return zoom; -}; + /** + * Get the view center. + * @return {module:ol/coordinate~Coordinate|undefined} The center of the view. + * @observable + * @api + */ + getCenter() { + return ( + /** @type {module:ol/coordinate~Coordinate|undefined} */ (this.get(ViewProperty.CENTER)) + ); + } -/** - * Get the zoom level for a resolution. - * @param {number} resolution The resolution. - * @return {number|undefined} The zoom level for the provided resolution. - * @api - */ -View.prototype.getZoomForResolution = function(resolution) { - let offset = this.minZoom_ || 0; - let max, zoomFactor; - if (this.resolutions_) { - const nearest = linearFindNearest(this.resolutions_, resolution, 1); - offset = nearest; - max = this.resolutions_[nearest]; - if (nearest == this.resolutions_.length - 1) { - zoomFactor = 2; + /** + * @return {module:ol/View~Constraints} Constraints. + */ + getConstraints() { + return this.constraints_; + } + + /** + * @param {Array.=} opt_hints Destination array. + * @return {Array.} Hint. + */ + getHints(opt_hints) { + if (opt_hints !== undefined) { + opt_hints[0] = this.hints_[0]; + opt_hints[1] = this.hints_[1]; + return opt_hints; } else { - zoomFactor = max / this.resolutions_[nearest + 1]; + return this.hints_.slice(); } - } else { - max = this.maxResolution_; - zoomFactor = this.zoomFactor_; - } - return offset + Math.log(max / resolution) / Math.log(zoomFactor); -}; - - -/** - * Get the resolution for a zoom level. - * @param {number} zoom Zoom level. - * @return {number} The view resolution for the provided zoom level. - * @api - */ -View.prototype.getResolutionForZoom = function(zoom) { - return /** @type {number} */ (this.constrainResolution( - this.maxResolution_, zoom - this.minZoom_, 0)); -}; - - -/** - * Fit the given geometry or extent based on the given map size and border. - * The size is pixel dimensions of the box to fit the extent into. - * In most cases you will want to use the map size, that is `map.getSize()`. - * Takes care of the map angle. - * @param {module:ol/geom/SimpleGeometry|module:ol/extent~Extent} geometryOrExtent The geometry or - * extent to fit the view to. - * @param {module:ol/View~FitOptions=} opt_options Options. - * @api - */ -View.prototype.fit = function(geometryOrExtent, opt_options) { - const options = opt_options || {}; - let size = options.size; - if (!size) { - size = this.getSizeFromViewport_(); - } - /** @type {module:ol/geom/SimpleGeometry} */ - let geometry; - if (!(geometryOrExtent instanceof SimpleGeometry)) { - assert(Array.isArray(geometryOrExtent), - 24); // Invalid extent or geometry provided as `geometry` - assert(!isEmpty(geometryOrExtent), - 25); // Cannot fit empty extent provided as `geometry` - geometry = polygonFromExtent(geometryOrExtent); - } else if (geometryOrExtent.getType() === GeometryType.CIRCLE) { - geometryOrExtent = geometryOrExtent.getExtent(); - geometry = polygonFromExtent(geometryOrExtent); - geometry.rotate(this.getRotation(), getCenter(geometryOrExtent)); - } else { - geometry = geometryOrExtent; } - const padding = options.padding !== undefined ? options.padding : [0, 0, 0, 0]; - const constrainResolution = options.constrainResolution !== undefined ? - options.constrainResolution : true; - const nearest = options.nearest !== undefined ? options.nearest : false; - let minResolution; - if (options.minResolution !== undefined) { - minResolution = options.minResolution; - } else if (options.maxZoom !== undefined) { - minResolution = this.constrainResolution( - this.maxResolution_, options.maxZoom - this.minZoom_, 0); - } else { - minResolution = 0; - } - const coords = geometry.getFlatCoordinates(); + /** + * Calculate the extent for the current view state and the passed size. + * The size is the pixel dimensions of the box into which the calculated extent + * should fit. In most cases you want to get the extent of the entire map, + * that is `map.getSize()`. + * @param {module:ol/size~Size=} opt_size Box pixel size. If not provided, the size of the + * first map that uses this view will be used. + * @return {module:ol/extent~Extent} Extent. + * @api + */ + calculateExtent(opt_size) { + const size = opt_size || this.getSizeFromViewport_(); + const center = /** @type {!module:ol/coordinate~Coordinate} */ (this.getCenter()); + assert(center, 1); // The view center is not defined + const resolution = /** @type {!number} */ (this.getResolution()); + assert(resolution !== undefined, 2); // The view resolution is not defined + const rotation = /** @type {!number} */ (this.getRotation()); + assert(rotation !== undefined, 3); // The view rotation is not defined - // calculate rotated extent - const rotation = this.getRotation(); - const cosAngle = Math.cos(-rotation); - let sinAngle = Math.sin(-rotation); - let minRotX = +Infinity; - let minRotY = +Infinity; - let maxRotX = -Infinity; - let maxRotY = -Infinity; - const stride = geometry.getStride(); - for (let i = 0, ii = coords.length; i < ii; i += stride) { - const rotX = coords[i] * cosAngle - coords[i + 1] * sinAngle; - const rotY = coords[i] * sinAngle + coords[i + 1] * cosAngle; - minRotX = Math.min(minRotX, rotX); - minRotY = Math.min(minRotY, rotY); - maxRotX = Math.max(maxRotX, rotX); - maxRotY = Math.max(maxRotY, rotY); + return getForViewAndSize(center, resolution, rotation, size); } - // calculate resolution - let resolution = this.getResolutionForExtent( - [minRotX, minRotY, maxRotX, maxRotY], - [size[0] - padding[1] - padding[3], size[1] - padding[0] - padding[2]]); - resolution = isNaN(resolution) ? minResolution : - Math.max(resolution, minResolution); - if (constrainResolution) { - let constrainedResolution = this.constrainResolution(resolution, 0, 0); - if (!nearest && constrainedResolution < resolution) { - constrainedResolution = this.constrainResolution( - constrainedResolution, -1, 0); + /** + * Get the maximum resolution of the view. + * @return {number} The maximum resolution of the view. + * @api + */ + getMaxResolution() { + return this.maxResolution_; + } + + /** + * Get the minimum resolution of the view. + * @return {number} The minimum resolution of the view. + * @api + */ + getMinResolution() { + return this.minResolution_; + } + + /** + * Get the maximum zoom level for the view. + * @return {number} The maximum zoom level. + * @api + */ + getMaxZoom() { + return /** @type {number} */ (this.getZoomForResolution(this.minResolution_)); + } + + /** + * Set a new maximum zoom level for the view. + * @param {number} zoom The maximum zoom level. + * @api + */ + setMaxZoom(zoom) { + this.applyOptions_(this.getUpdatedOptions_({maxZoom: zoom})); + } + + /** + * Get the minimum zoom level for the view. + * @return {number} The minimum zoom level. + * @api + */ + getMinZoom() { + return /** @type {number} */ (this.getZoomForResolution(this.maxResolution_)); + } + + /** + * Set a new minimum zoom level for the view. + * @param {number} zoom The minimum zoom level. + * @api + */ + setMinZoom(zoom) { + this.applyOptions_(this.getUpdatedOptions_({minZoom: zoom})); + } + + /** + * Get the view projection. + * @return {module:ol/proj/Projection} The projection of the view. + * @api + */ + getProjection() { + return this.projection_; + } + + /** + * Get the view resolution. + * @return {number|undefined} The resolution of the view. + * @observable + * @api + */ + getResolution() { + return /** @type {number|undefined} */ (this.get(ViewProperty.RESOLUTION)); + } + + /** + * Get the resolutions for the view. This returns the array of resolutions + * passed to the constructor of the View, or undefined if none were given. + * @return {Array.|undefined} The resolutions of the view. + * @api + */ + getResolutions() { + return this.resolutions_; + } + + /** + * Get the resolution for a provided extent (in map units) and size (in pixels). + * @param {module:ol/extent~Extent} extent Extent. + * @param {module:ol/size~Size=} opt_size Box pixel size. + * @return {number} The resolution at which the provided extent will render at + * the given size. + * @api + */ + getResolutionForExtent(extent, opt_size) { + const size = opt_size || this.getSizeFromViewport_(); + const xResolution = getWidth(extent) / size[0]; + const yResolution = getHeight(extent) / size[1]; + return Math.max(xResolution, yResolution); + } + + /** + * Return a function that returns a value between 0 and 1 for a + * resolution. Exponential scaling is assumed. + * @param {number=} opt_power Power. + * @return {function(number): number} Resolution for value function. + */ + getResolutionForValueFunction(opt_power) { + const power = opt_power || 2; + const maxResolution = this.maxResolution_; + const minResolution = this.minResolution_; + const max = Math.log(maxResolution / minResolution) / Math.log(power); + return ( + /** + * @param {number} value Value. + * @return {number} Resolution. + */ + function(value) { + const resolution = maxResolution / Math.pow(power, value * max); + return resolution; + }); + } + + /** + * Get the view rotation. + * @return {number} The rotation of the view in radians. + * @observable + * @api + */ + getRotation() { + return /** @type {number} */ (this.get(ViewProperty.ROTATION)); + } + + /** + * Return a function that returns a resolution for a value between + * 0 and 1. Exponential scaling is assumed. + * @param {number=} opt_power Power. + * @return {function(number): number} Value for resolution function. + */ + getValueForResolutionFunction(opt_power) { + const power = opt_power || 2; + const maxResolution = this.maxResolution_; + const minResolution = this.minResolution_; + const max = Math.log(maxResolution / minResolution) / Math.log(power); + return ( + /** + * @param {number} resolution Resolution. + * @return {number} Value. + */ + function(resolution) { + const value = (Math.log(maxResolution / resolution) / Math.log(power)) / max; + return value; + }); + } + + /** + * @return {module:ol/View~State} View state. + */ + getState() { + const center = /** @type {module:ol/coordinate~Coordinate} */ (this.getCenter()); + const projection = this.getProjection(); + const resolution = /** @type {number} */ (this.getResolution()); + const rotation = this.getRotation(); + return ( + /** @type {module:ol/View~State} */ ({ + center: center.slice(), + projection: projection !== undefined ? projection : null, + resolution: resolution, + rotation: rotation, + zoom: this.getZoom() + }) + ); + } + + /** + * Get the current zoom level. If you configured your view with a resolutions + * array (this is rare), this method may return non-integer zoom levels (so + * the zoom level is not safe to use as an index into a resolutions array). + * @return {number|undefined} Zoom. + * @api + */ + getZoom() { + let zoom; + const resolution = this.getResolution(); + if (resolution !== undefined) { + zoom = this.getZoomForResolution(resolution); } - resolution = constrainedResolution; + return zoom; } - // calculate center - sinAngle = -sinAngle; // go back to original rotation - let centerRotX = (minRotX + maxRotX) / 2; - let centerRotY = (minRotY + maxRotY) / 2; - centerRotX += (padding[1] - padding[3]) / 2 * resolution; - centerRotY += (padding[0] - padding[2]) / 2 * resolution; - const centerX = centerRotX * cosAngle - centerRotY * sinAngle; - const centerY = centerRotY * cosAngle + centerRotX * sinAngle; - const center = [centerX, centerY]; - const callback = options.callback ? options.callback : UNDEFINED; - - if (options.duration !== undefined) { - this.animate({ - resolution: resolution, - center: center, - duration: options.duration, - easing: options.easing - }, callback); - } else { - this.setResolution(resolution); - this.setCenter(center); - setTimeout(callback.bind(undefined, true), 0); + /** + * Get the zoom level for a resolution. + * @param {number} resolution The resolution. + * @return {number|undefined} The zoom level for the provided resolution. + * @api + */ + getZoomForResolution(resolution) { + let offset = this.minZoom_ || 0; + let max, zoomFactor; + if (this.resolutions_) { + const nearest = linearFindNearest(this.resolutions_, resolution, 1); + offset = nearest; + max = this.resolutions_[nearest]; + if (nearest == this.resolutions_.length - 1) { + zoomFactor = 2; + } else { + zoomFactor = max / this.resolutions_[nearest + 1]; + } + } else { + max = this.maxResolution_; + zoomFactor = this.zoomFactor_; + } + return offset + Math.log(max / resolution) / Math.log(zoomFactor); } -}; - -/** - * Center on coordinate and view position. - * @param {module:ol/coordinate~Coordinate} coordinate Coordinate. - * @param {module:ol/size~Size} size Box pixel size. - * @param {module:ol~Pixel} position Position on the view to center on. - * @api - */ -View.prototype.centerOn = function(coordinate, size, position) { - // calculate rotated position - const rotation = this.getRotation(); - const cosAngle = Math.cos(-rotation); - let sinAngle = Math.sin(-rotation); - let rotX = coordinate[0] * cosAngle - coordinate[1] * sinAngle; - let rotY = coordinate[1] * cosAngle + coordinate[0] * sinAngle; - const resolution = this.getResolution(); - rotX += (size[0] / 2 - position[0]) * resolution; - rotY += (position[1] - size[1] / 2) * resolution; - - // go back to original angle - sinAngle = -sinAngle; // go back to original rotation - const centerX = rotX * cosAngle - rotY * sinAngle; - const centerY = rotY * cosAngle + rotX * sinAngle; - - this.setCenter([centerX, centerY]); -}; - - -/** - * @return {boolean} Is defined. - */ -View.prototype.isDef = function() { - return !!this.getCenter() && this.getResolution() !== undefined; -}; - - -/** - * Rotate the view around a given coordinate. - * @param {number} rotation New rotation value for the view. - * @param {module:ol/coordinate~Coordinate=} opt_anchor The rotation center. - * @api - */ -View.prototype.rotate = function(rotation, opt_anchor) { - if (opt_anchor !== undefined) { - const center = this.calculateCenterRotate(rotation, opt_anchor); - this.setCenter(center); + /** + * Get the resolution for a zoom level. + * @param {number} zoom Zoom level. + * @return {number} The view resolution for the provided zoom level. + * @api + */ + getResolutionForZoom(zoom) { + return /** @type {number} */ (this.constrainResolution( + this.maxResolution_, zoom - this.minZoom_, 0)); } - this.setRotation(rotation); -}; + /** + * Fit the given geometry or extent based on the given map size and border. + * The size is pixel dimensions of the box to fit the extent into. + * In most cases you will want to use the map size, that is `map.getSize()`. + * Takes care of the map angle. + * @param {module:ol/geom/SimpleGeometry|module:ol/extent~Extent} geometryOrExtent The geometry or + * extent to fit the view to. + * @param {module:ol/View~FitOptions=} opt_options Options. + * @api + */ + fit(geometryOrExtent, opt_options) { + const options = opt_options || {}; + let size = options.size; + if (!size) { + size = this.getSizeFromViewport_(); + } + /** @type {module:ol/geom/SimpleGeometry} */ + let geometry; + if (!(geometryOrExtent instanceof SimpleGeometry)) { + assert(Array.isArray(geometryOrExtent), + 24); // Invalid extent or geometry provided as `geometry` + assert(!isEmpty(geometryOrExtent), + 25); // Cannot fit empty extent provided as `geometry` + geometry = polygonFromExtent(geometryOrExtent); + } else if (geometryOrExtent.getType() === GeometryType.CIRCLE) { + geometryOrExtent = geometryOrExtent.getExtent(); + geometry = polygonFromExtent(geometryOrExtent); + geometry.rotate(this.getRotation(), getCenter(geometryOrExtent)); + } else { + geometry = geometryOrExtent; + } -/** - * Set the center of the current view. - * @param {module:ol/coordinate~Coordinate|undefined} center The center of the view. - * @observable - * @api - */ -View.prototype.setCenter = function(center) { - this.set(ViewProperty.CENTER, center); - if (this.getAnimating()) { - this.cancelAnimations(); + const padding = options.padding !== undefined ? options.padding : [0, 0, 0, 0]; + const constrainResolution = options.constrainResolution !== undefined ? + options.constrainResolution : true; + const nearest = options.nearest !== undefined ? options.nearest : false; + let minResolution; + if (options.minResolution !== undefined) { + minResolution = options.minResolution; + } else if (options.maxZoom !== undefined) { + minResolution = this.constrainResolution( + this.maxResolution_, options.maxZoom - this.minZoom_, 0); + } else { + minResolution = 0; + } + const coords = geometry.getFlatCoordinates(); + + // calculate rotated extent + const rotation = this.getRotation(); + const cosAngle = Math.cos(-rotation); + let sinAngle = Math.sin(-rotation); + let minRotX = +Infinity; + let minRotY = +Infinity; + let maxRotX = -Infinity; + let maxRotY = -Infinity; + const stride = geometry.getStride(); + for (let i = 0, ii = coords.length; i < ii; i += stride) { + const rotX = coords[i] * cosAngle - coords[i + 1] * sinAngle; + const rotY = coords[i] * sinAngle + coords[i + 1] * cosAngle; + minRotX = Math.min(minRotX, rotX); + minRotY = Math.min(minRotY, rotY); + maxRotX = Math.max(maxRotX, rotX); + maxRotY = Math.max(maxRotY, rotY); + } + + // calculate resolution + let resolution = this.getResolutionForExtent( + [minRotX, minRotY, maxRotX, maxRotY], + [size[0] - padding[1] - padding[3], size[1] - padding[0] - padding[2]]); + resolution = isNaN(resolution) ? minResolution : + Math.max(resolution, minResolution); + if (constrainResolution) { + let constrainedResolution = this.constrainResolution(resolution, 0, 0); + if (!nearest && constrainedResolution < resolution) { + constrainedResolution = this.constrainResolution( + constrainedResolution, -1, 0); + } + resolution = constrainedResolution; + } + + // calculate center + sinAngle = -sinAngle; // go back to original rotation + let centerRotX = (minRotX + maxRotX) / 2; + let centerRotY = (minRotY + maxRotY) / 2; + centerRotX += (padding[1] - padding[3]) / 2 * resolution; + centerRotY += (padding[0] - padding[2]) / 2 * resolution; + const centerX = centerRotX * cosAngle - centerRotY * sinAngle; + const centerY = centerRotY * cosAngle + centerRotX * sinAngle; + const center = [centerX, centerY]; + const callback = options.callback ? options.callback : UNDEFINED; + + if (options.duration !== undefined) { + this.animate({ + resolution: resolution, + center: center, + duration: options.duration, + easing: options.easing + }, callback); + } else { + this.setResolution(resolution); + this.setCenter(center); + setTimeout(callback.bind(undefined, true), 0); + } } -}; + /** + * Center on coordinate and view position. + * @param {module:ol/coordinate~Coordinate} coordinate Coordinate. + * @param {module:ol/size~Size} size Box pixel size. + * @param {module:ol~Pixel} position Position on the view to center on. + * @api + */ + centerOn(coordinate, size, position) { + // calculate rotated position + const rotation = this.getRotation(); + const cosAngle = Math.cos(-rotation); + let sinAngle = Math.sin(-rotation); + let rotX = coordinate[0] * cosAngle - coordinate[1] * sinAngle; + let rotY = coordinate[1] * cosAngle + coordinate[0] * sinAngle; + const resolution = this.getResolution(); + rotX += (size[0] / 2 - position[0]) * resolution; + rotY += (position[1] - size[1] / 2) * resolution; -/** - * @param {module:ol/ViewHint} hint Hint. - * @param {number} delta Delta. - * @return {number} New value. - */ -View.prototype.setHint = function(hint, delta) { - this.hints_[hint] += delta; - this.changed(); - return this.hints_[hint]; -}; + // go back to original angle + sinAngle = -sinAngle; // go back to original rotation + const centerX = rotX * cosAngle - rotY * sinAngle; + const centerY = rotY * cosAngle + rotX * sinAngle; - -/** - * Set the resolution for this view. - * @param {number|undefined} resolution The resolution of the view. - * @observable - * @api - */ -View.prototype.setResolution = function(resolution) { - this.set(ViewProperty.RESOLUTION, resolution); - if (this.getAnimating()) { - this.cancelAnimations(); + this.setCenter([centerX, centerY]); } -}; - -/** - * Set the rotation for this view. - * @param {number} rotation The rotation of the view in radians. - * @observable - * @api - */ -View.prototype.setRotation = function(rotation) { - this.set(ViewProperty.ROTATION, rotation); - if (this.getAnimating()) { - this.cancelAnimations(); + /** + * @return {boolean} Is defined. + */ + isDef() { + return !!this.getCenter() && this.getResolution() !== undefined; } -}; + /** + * Rotate the view around a given coordinate. + * @param {number} rotation New rotation value for the view. + * @param {module:ol/coordinate~Coordinate=} opt_anchor The rotation center. + * @api + */ + rotate(rotation, opt_anchor) { + if (opt_anchor !== undefined) { + const center = this.calculateCenterRotate(rotation, opt_anchor); + this.setCenter(center); + } + this.setRotation(rotation); + } -/** - * Zoom to a specific zoom level. - * @param {number} zoom Zoom level. - * @api - */ -View.prototype.setZoom = function(zoom) { - this.setResolution(this.getResolutionForZoom(zoom)); -}; + /** + * Set the center of the current view. + * @param {module:ol/coordinate~Coordinate|undefined} center The center of the view. + * @observable + * @api + */ + setCenter(center) { + this.set(ViewProperty.CENTER, center); + if (this.getAnimating()) { + this.cancelAnimations(); + } + } + + /** + * @param {module:ol/ViewHint} hint Hint. + * @param {number} delta Delta. + * @return {number} New value. + */ + setHint(hint, delta) { + this.hints_[hint] += delta; + this.changed(); + return this.hints_[hint]; + } + + /** + * Set the resolution for this view. + * @param {number|undefined} resolution The resolution of the view. + * @observable + * @api + */ + setResolution(resolution) { + this.set(ViewProperty.RESOLUTION, resolution); + if (this.getAnimating()) { + this.cancelAnimations(); + } + } + + /** + * Set the rotation for this view. + * @param {number} rotation The rotation of the view in radians. + * @observable + * @api + */ + setRotation(rotation) { + this.set(ViewProperty.ROTATION, rotation); + if (this.getAnimating()) { + this.cancelAnimations(); + } + } + + /** + * Zoom to a specific zoom level. + * @param {number} zoom Zoom level. + * @api + */ + setZoom(zoom) { + this.setResolution(this.getResolutionForZoom(zoom)); + } +} + +inherits(View, BaseObject); /** diff --git a/src/ol/WebGLMap.js b/src/ol/WebGLMap.js index b9a73c2396..2717ec16c2 100644 --- a/src/ol/WebGLMap.js +++ b/src/ol/WebGLMap.js @@ -66,29 +66,31 @@ import WebGLVectorLayerRenderer from './renderer/webgl/VectorLayer.js'; * @fires module:ol/render/Event~RenderEvent#precompose * @api */ -const WebGLMap = function(options) { - options = assign({}, options); - if (!options.controls) { - options.controls = defaultControls(); - } - if (!options.interactions) { - options.interactions = defaultInteractions(); +class WebGLMap { + constructor(options) { + options = assign({}, options); + if (!options.controls) { + options.controls = defaultControls(); + } + if (!options.interactions) { + options.interactions = defaultInteractions(); + } + + PluggableMap.call(this, options); } - PluggableMap.call(this, options); -}; + createRenderer() { + const renderer = new WebGLMapRenderer(this); + renderer.registerLayerRenderers([ + WebGLImageLayerRenderer, + WebGLTileLayerRenderer, + WebGLVectorLayerRenderer + ]); + return renderer; + } +} inherits(WebGLMap, PluggableMap); -WebGLMap.prototype.createRenderer = function() { - const renderer = new WebGLMapRenderer(this); - renderer.registerLayerRenderers([ - WebGLImageLayerRenderer, - WebGLTileLayerRenderer, - WebGLVectorLayerRenderer - ]); - return renderer; -}; - export default WebGLMap; diff --git a/src/ol/events/Event.js b/src/ol/events/Event.js index 6e87787bc0..3916a987b6 100644 --- a/src/ol/events/Event.js +++ b/src/ol/events/Event.js @@ -1,6 +1,7 @@ /** * @module ol/events/Event */ + /** * @classdesc * Stripped down implementation of the W3C DOM Level 2 Event interface. @@ -14,45 +15,49 @@ * @constructor * @param {string} type Type. */ -const Event = function(type) { +class Event { - /** - * @type {boolean} - */ - this.propagationStopped; + constructor(type) { - /** - * The event type. - * @type {string} - * @api - */ - this.type = type; + /** + * @type {boolean} + */ + this.propagationStopped; - /** - * The event target. - * @type {Object} - * @api - */ - this.target = null; + /** + * The event type. + * @type {string} + * @api + */ + this.type = type; -}; - - -/** - * Stop event propagation. - * @function - * @api - */ -Event.prototype.preventDefault = + /** + * The event target. + * @type {Object} + * @api + */ + this.target = null; + } /** * Stop event propagation. * @function * @api */ - Event.prototype.stopPropagation = function() { + preventDefault() { this.propagationStopped = true; - }; + } + + /** + * Stop event propagation. + * @function + * @api + */ + stopPropagation() { + this.propagationStopped = true; + } + +} /** diff --git a/src/ol/format/filter/And.js b/src/ol/format/filter/And.js index c76a3ae80f..f1d9426094 100644 --- a/src/ol/format/filter/And.js +++ b/src/ol/format/filter/And.js @@ -13,10 +13,14 @@ import LogicalNary from '../filter/LogicalNary.js'; * @param {...module:ol/format/filter/Filter} conditions Conditions. * @extends {module:ol/format/filter/LogicalNary} */ -const And = function(conditions) { - const params = ['And'].concat(Array.prototype.slice.call(arguments)); - LogicalNary.apply(this, params); -}; +class And { + + constructor(conditions) { + const params = ['And'].concat(Array.prototype.slice.call(arguments)); + LogicalNary.apply(this, params); + } + +} inherits(And, LogicalNary); diff --git a/src/ol/format/filter/Bbox.js b/src/ol/format/filter/Bbox.js index b97ca862d2..1e747c5c28 100644 --- a/src/ol/format/filter/Bbox.js +++ b/src/ol/format/filter/Bbox.js @@ -17,25 +17,29 @@ import Filter from '../filter/Filter.js'; * @extends {module:ol/format/filter/Filter} * @api */ -const Bbox = function(geometryName, extent, opt_srsName) { +class Bbox { - Filter.call(this, 'BBOX'); + constructor(geometryName, extent, opt_srsName) { - /** - * @type {!string} - */ - this.geometryName = geometryName; + Filter.call(this, 'BBOX'); - /** - * @type {module:ol/extent~Extent} - */ - this.extent = extent; + /** + * @type {!string} + */ + this.geometryName = geometryName; - /** - * @type {string|undefined} - */ - this.srsName = opt_srsName; -}; + /** + * @type {module:ol/extent~Extent} + */ + this.extent = extent; + + /** + * @type {string|undefined} + */ + this.srsName = opt_srsName; + } + +} inherits(Bbox, Filter); diff --git a/src/ol/format/filter/Comparison.js b/src/ol/format/filter/Comparison.js index 783a86179a..1d781511a9 100644 --- a/src/ol/format/filter/Comparison.js +++ b/src/ol/format/filter/Comparison.js @@ -15,15 +15,19 @@ import Filter from '../filter/Filter.js'; * @param {!string} propertyName Name of the context property to compare. * @extends {module:ol/format/filter/Filter} */ -const Comparison = function(tagName, propertyName) { +class Comparison { - Filter.call(this, tagName); + constructor(tagName, propertyName) { - /** - * @type {!string} - */ - this.propertyName = propertyName; -}; + Filter.call(this, tagName); + + /** + * @type {!string} + */ + this.propertyName = propertyName; + } + +} inherits(Comparison, Filter); diff --git a/src/ol/format/filter/ComparisonBinary.js b/src/ol/format/filter/ComparisonBinary.js index 10d34beabe..8adeb7303c 100644 --- a/src/ol/format/filter/ComparisonBinary.js +++ b/src/ol/format/filter/ComparisonBinary.js @@ -17,20 +17,25 @@ import Comparison from '../filter/Comparison.js'; * @param {boolean=} opt_matchCase Case-sensitive? * @extends {module:ol/format/filter/Comparison} */ -const ComparisonBinary = function(tagName, propertyName, expression, opt_matchCase) { +class ComparisonBinary { - Comparison.call(this, tagName, propertyName); + constructor(tagName, propertyName, expression, opt_matchCase) { - /** - * @type {!(string|number)} - */ - this.expression = expression; + Comparison.call(this, tagName, propertyName); - /** - * @type {boolean|undefined} - */ - this.matchCase = opt_matchCase; -}; + /** + * @type {!(string|number)} + */ + this.expression = expression; + + /** + * @type {boolean|undefined} + */ + this.matchCase = opt_matchCase; + } + +} inherits(ComparisonBinary, Comparison); + export default ComparisonBinary; diff --git a/src/ol/format/filter/Contains.js b/src/ol/format/filter/Contains.js index 1af172a6bb..4de230a1ab 100644 --- a/src/ol/format/filter/Contains.js +++ b/src/ol/format/filter/Contains.js @@ -17,11 +17,16 @@ import Spatial from '../filter/Spatial.js'; * @extends {module:ol/format/filter/Spatial} * @api */ -const Contains = function(geometryName, geometry, opt_srsName) { +class Contains { - Spatial.call(this, 'Contains', geometryName, geometry, opt_srsName); + constructor(geometryName, geometry, opt_srsName) { -}; + Spatial.call(this, 'Contains', geometryName, geometry, opt_srsName); + + } + +} inherits(Contains, Spatial); + export default Contains; diff --git a/src/ol/format/filter/During.js b/src/ol/format/filter/During.js index 588625dfbb..ca7f848afc 100644 --- a/src/ol/format/filter/During.js +++ b/src/ol/format/filter/During.js @@ -15,19 +15,24 @@ import Comparison from '../filter/Comparison.js'; * @extends {module:ol/format/filter/Comparison} * @api */ -const During = function(propertyName, begin, end) { - Comparison.call(this, 'During', propertyName); +class During { - /** - * @type {!string} - */ - this.begin = begin; + constructor(propertyName, begin, end) { + Comparison.call(this, 'During', propertyName); - /** - * @type {!string} - */ - this.end = end; -}; + /** + * @type {!string} + */ + this.begin = begin; + + /** + * @type {!string} + */ + this.end = end; + } + +} inherits(During, Comparison); + export default During; diff --git a/src/ol/format/filter/EqualTo.js b/src/ol/format/filter/EqualTo.js index f56825f1f8..a9eedf6c16 100644 --- a/src/ol/format/filter/EqualTo.js +++ b/src/ol/format/filter/EqualTo.js @@ -15,9 +15,14 @@ import ComparisonBinary from '../filter/ComparisonBinary.js'; * @extends {module:ol/format/filter/ComparisonBinary} * @api */ -const EqualTo = function(propertyName, expression, opt_matchCase) { - ComparisonBinary.call(this, 'PropertyIsEqualTo', propertyName, expression, opt_matchCase); -}; +class EqualTo { + + constructor(propertyName, expression, opt_matchCase) { + ComparisonBinary.call(this, 'PropertyIsEqualTo', propertyName, expression, opt_matchCase); + } + +} inherits(EqualTo, ComparisonBinary); + export default EqualTo; diff --git a/src/ol/format/filter/GreaterThan.js b/src/ol/format/filter/GreaterThan.js index 5716001dee..a19804a00c 100644 --- a/src/ol/format/filter/GreaterThan.js +++ b/src/ol/format/filter/GreaterThan.js @@ -14,9 +14,14 @@ import ComparisonBinary from '../filter/ComparisonBinary.js'; * @extends {module:ol/format/filter/ComparisonBinary} * @api */ -const GreaterThan = function(propertyName, expression) { - ComparisonBinary.call(this, 'PropertyIsGreaterThan', propertyName, expression); -}; +class GreaterThan { + + constructor(propertyName, expression) { + ComparisonBinary.call(this, 'PropertyIsGreaterThan', propertyName, expression); + } + +} inherits(GreaterThan, ComparisonBinary); + export default GreaterThan; diff --git a/src/ol/format/filter/GreaterThanOrEqualTo.js b/src/ol/format/filter/GreaterThanOrEqualTo.js index 5e7e96f2ed..a55cc09d62 100644 --- a/src/ol/format/filter/GreaterThanOrEqualTo.js +++ b/src/ol/format/filter/GreaterThanOrEqualTo.js @@ -14,9 +14,14 @@ import ComparisonBinary from '../filter/ComparisonBinary.js'; * @extends {module:ol/format/filter/ComparisonBinary} * @api */ -const GreaterThanOrEqualTo = function(propertyName, expression) { - ComparisonBinary.call(this, 'PropertyIsGreaterThanOrEqualTo', propertyName, expression); -}; +class GreaterThanOrEqualTo { + + constructor(propertyName, expression) { + ComparisonBinary.call(this, 'PropertyIsGreaterThanOrEqualTo', propertyName, expression); + } + +} inherits(GreaterThanOrEqualTo, ComparisonBinary); + export default GreaterThanOrEqualTo; diff --git a/src/ol/format/filter/Intersects.js b/src/ol/format/filter/Intersects.js index bcdcbf68e2..b64696c96d 100644 --- a/src/ol/format/filter/Intersects.js +++ b/src/ol/format/filter/Intersects.js @@ -17,11 +17,15 @@ import Spatial from '../filter/Spatial.js'; * @extends {module:ol/format/filter/Spatial} * @api */ -const Intersects = function(geometryName, geometry, opt_srsName) { +class Intersects { - Spatial.call(this, 'Intersects', geometryName, geometry, opt_srsName); + constructor(geometryName, geometry, opt_srsName) { -}; + Spatial.call(this, 'Intersects', geometryName, geometry, opt_srsName); + } + +} inherits(Intersects, Spatial); + export default Intersects; diff --git a/src/ol/format/filter/IsBetween.js b/src/ol/format/filter/IsBetween.js index ac5e1a7f4b..3134f36059 100644 --- a/src/ol/format/filter/IsBetween.js +++ b/src/ol/format/filter/IsBetween.js @@ -15,19 +15,24 @@ import Comparison from '../filter/Comparison.js'; * @extends {module:ol/format/filter/Comparison} * @api */ -const IsBetween = function(propertyName, lowerBoundary, upperBoundary) { - Comparison.call(this, 'PropertyIsBetween', propertyName); +class IsBetween { - /** - * @type {!number} - */ - this.lowerBoundary = lowerBoundary; + constructor(propertyName, lowerBoundary, upperBoundary) { + Comparison.call(this, 'PropertyIsBetween', propertyName); - /** - * @type {!number} - */ - this.upperBoundary = upperBoundary; -}; + /** + * @type {!number} + */ + this.lowerBoundary = lowerBoundary; + + /** + * @type {!number} + */ + this.upperBoundary = upperBoundary; + + } +} inherits(IsBetween, Comparison); + export default IsBetween; diff --git a/src/ol/format/filter/IsLike.js b/src/ol/format/filter/IsLike.js index 37d1f2466d..fbf07e8b08 100644 --- a/src/ol/format/filter/IsLike.js +++ b/src/ol/format/filter/IsLike.js @@ -21,34 +21,39 @@ import Comparison from '../filter/Comparison.js'; * @extends {module:ol/format/filter/Comparison} * @api */ -const IsLike = function(propertyName, pattern, opt_wildCard, opt_singleChar, opt_escapeChar, opt_matchCase) { - Comparison.call(this, 'PropertyIsLike', propertyName); +class IsLike { - /** - * @type {!string} - */ - this.pattern = pattern; + constructor(propertyName, pattern, opt_wildCard, opt_singleChar, opt_escapeChar, opt_matchCase) { + Comparison.call(this, 'PropertyIsLike', propertyName); - /** - * @type {!string} - */ - this.wildCard = (opt_wildCard !== undefined) ? opt_wildCard : '*'; + /** + * @type {!string} + */ + this.pattern = pattern; - /** - * @type {!string} - */ - this.singleChar = (opt_singleChar !== undefined) ? opt_singleChar : '.'; + /** + * @type {!string} + */ + this.wildCard = (opt_wildCard !== undefined) ? opt_wildCard : '*'; - /** - * @type {!string} - */ - this.escapeChar = (opt_escapeChar !== undefined) ? opt_escapeChar : '!'; + /** + * @type {!string} + */ + this.singleChar = (opt_singleChar !== undefined) ? opt_singleChar : '.'; - /** - * @type {boolean|undefined} - */ - this.matchCase = opt_matchCase; -}; + /** + * @type {!string} + */ + this.escapeChar = (opt_escapeChar !== undefined) ? opt_escapeChar : '!'; + + /** + * @type {boolean|undefined} + */ + this.matchCase = opt_matchCase; + + } +} inherits(IsLike, Comparison); + export default IsLike; diff --git a/src/ol/format/filter/IsNull.js b/src/ol/format/filter/IsNull.js index fad2b07c85..4daf9a7bc9 100644 --- a/src/ol/format/filter/IsNull.js +++ b/src/ol/format/filter/IsNull.js @@ -13,9 +13,15 @@ import Comparison from '../filter/Comparison.js'; * @extends {module:ol/format/filter/Comparison} * @api */ -const IsNull = function(propertyName) { - Comparison.call(this, 'PropertyIsNull', propertyName); -}; +class IsNull { + + constructor(propertyName) { + Comparison.call(this, 'PropertyIsNull', propertyName); + } + +} + inherits(IsNull, Comparison); + export default IsNull; diff --git a/src/ol/format/filter/LessThan.js b/src/ol/format/filter/LessThan.js index e3f8ffc982..85d241bd9f 100644 --- a/src/ol/format/filter/LessThan.js +++ b/src/ol/format/filter/LessThan.js @@ -14,9 +14,14 @@ import ComparisonBinary from '../filter/ComparisonBinary.js'; * @extends {module:ol/format/filter/ComparisonBinary} * @api */ -const LessThan = function(propertyName, expression) { - ComparisonBinary.call(this, 'PropertyIsLessThan', propertyName, expression); -}; +class LessThan { + + constructor(propertyName, expression) { + ComparisonBinary.call(this, 'PropertyIsLessThan', propertyName, expression); + } + +} inherits(LessThan, ComparisonBinary); + export default LessThan; diff --git a/src/ol/format/filter/LessThanOrEqualTo.js b/src/ol/format/filter/LessThanOrEqualTo.js index 2575334635..ef89c408a5 100644 --- a/src/ol/format/filter/LessThanOrEqualTo.js +++ b/src/ol/format/filter/LessThanOrEqualTo.js @@ -14,9 +14,14 @@ import ComparisonBinary from '../filter/ComparisonBinary.js'; * @extends {module:ol/format/filter/ComparisonBinary} * @api */ -const LessThanOrEqualTo = function(propertyName, expression) { - ComparisonBinary.call(this, 'PropertyIsLessThanOrEqualTo', propertyName, expression); -}; +class LessThanOrEqualTo { + + constructor(propertyName, expression) { + ComparisonBinary.call(this, 'PropertyIsLessThanOrEqualTo', propertyName, expression); + } + +} inherits(LessThanOrEqualTo, ComparisonBinary); + export default LessThanOrEqualTo; diff --git a/src/ol/format/filter/LogicalNary.js b/src/ol/format/filter/LogicalNary.js index e86186756d..fcbcdb0b09 100644 --- a/src/ol/format/filter/LogicalNary.js +++ b/src/ol/format/filter/LogicalNary.js @@ -16,16 +16,21 @@ import Filter from '../filter/Filter.js'; * @param {...module:ol/format/filter/Filter} conditions Conditions. * @extends {module:ol/format/filter/Filter} */ -const LogicalNary = function(tagName, conditions) { +class LogicalNary { - Filter.call(this, tagName); + constructor(tagName, conditions) { - /** - * @type {Array.} - */ - this.conditions = Array.prototype.slice.call(arguments, 1); - assert(this.conditions.length >= 2, 57); // At least 2 conditions are required. -}; + Filter.call(this, tagName); + + /** + * @type {Array.} + */ + this.conditions = Array.prototype.slice.call(arguments, 1); + assert(this.conditions.length >= 2, 57); // At least 2 conditions are required. + } + +} inherits(LogicalNary, Filter); + export default LogicalNary; diff --git a/src/ol/format/filter/Not.js b/src/ol/format/filter/Not.js index f8a37c875b..6fd462d24e 100644 --- a/src/ol/format/filter/Not.js +++ b/src/ol/format/filter/Not.js @@ -13,15 +13,20 @@ import Filter from '../filter/Filter.js'; * @extends {module:ol/format/filter/Filter} * @api */ -const Not = function(condition) { +class Not { - Filter.call(this, 'Not'); + constructor(condition) { - /** - * @type {!module:ol/format/filter/Filter} - */ - this.condition = condition; -}; + Filter.call(this, 'Not'); + + /** + * @type {!module:ol/format/filter/Filter} + */ + this.condition = condition; + + } + +} inherits(Not, Filter); export default Not; diff --git a/src/ol/format/filter/NotEqualTo.js b/src/ol/format/filter/NotEqualTo.js index 533e570223..e0abaaa69a 100644 --- a/src/ol/format/filter/NotEqualTo.js +++ b/src/ol/format/filter/NotEqualTo.js @@ -15,9 +15,14 @@ import ComparisonBinary from '../filter/ComparisonBinary.js'; * @extends {module:ol/format/filter/ComparisonBinary} * @api */ -const NotEqualTo = function(propertyName, expression, opt_matchCase) { - ComparisonBinary.call(this, 'PropertyIsNotEqualTo', propertyName, expression, opt_matchCase); -}; +class NotEqualTo { + + constructor(propertyName, expression, opt_matchCase) { + ComparisonBinary.call(this, 'PropertyIsNotEqualTo', propertyName, expression, opt_matchCase); + } + +} inherits(NotEqualTo, ComparisonBinary); + export default NotEqualTo; diff --git a/src/ol/format/filter/Or.js b/src/ol/format/filter/Or.js index 5267f3f5ac..98952613cc 100644 --- a/src/ol/format/filter/Or.js +++ b/src/ol/format/filter/Or.js @@ -13,10 +13,15 @@ import LogicalNary from '../filter/LogicalNary.js'; * @extends {module:ol/format/filter/LogicalNary} * @api */ -const Or = function(conditions) { - const params = ['Or'].concat(Array.prototype.slice.call(arguments)); - LogicalNary.apply(this, params); -}; +class Or { + + constructor(conditions) { + const params = ['Or'].concat(Array.prototype.slice.call(arguments)); + LogicalNary.apply(this, params); + } + +} inherits(Or, LogicalNary); + export default Or; diff --git a/src/ol/format/filter/Spatial.js b/src/ol/format/filter/Spatial.js index 8e5260a819..2bf67267b8 100644 --- a/src/ol/format/filter/Spatial.js +++ b/src/ol/format/filter/Spatial.js @@ -19,25 +19,29 @@ import Filter from '../filter/Filter.js'; * set on geometries when this is not provided. * @extends {module:ol/format/filter/Filter} */ -const Spatial = function(tagName, geometryName, geometry, opt_srsName) { +class Spatial { - Filter.call(this, tagName); + constructor(tagName, geometryName, geometry, opt_srsName) { - /** - * @type {!string} - */ - this.geometryName = geometryName || 'the_geom'; + Filter.call(this, tagName); - /** - * @type {module:ol/geom/Geometry} - */ - this.geometry = geometry; + /** + * @type {!string} + */ + this.geometryName = geometryName || 'the_geom'; - /** - * @type {string|undefined} - */ - this.srsName = opt_srsName; -}; + /** + * @type {module:ol/geom/Geometry} + */ + this.geometry = geometry; + + /** + * @type {string|undefined} + */ + this.srsName = opt_srsName; + } + +} inherits(Spatial, Filter); diff --git a/src/ol/format/filter/Within.js b/src/ol/format/filter/Within.js index e3dec4f9fb..72bccbaba1 100644 --- a/src/ol/format/filter/Within.js +++ b/src/ol/format/filter/Within.js @@ -17,11 +17,14 @@ import Spatial from '../filter/Spatial.js'; * @extends {module:ol/format/filter/Spatial} * @api */ -const Within = function(geometryName, geometry, opt_srsName) { +class Within { - Spatial.call(this, 'Within', geometryName, geometry, opt_srsName); + constructor(geometryName, geometry, opt_srsName) { + Spatial.call(this, 'Within', geometryName, geometry, opt_srsName); + } -}; +} inherits(Within, Spatial); + export default Within; diff --git a/src/ol/interaction/DoubleClickZoom.js b/src/ol/interaction/DoubleClickZoom.js index ef663c97d3..49146148ea 100644 --- a/src/ol/interaction/DoubleClickZoom.js +++ b/src/ol/interaction/DoubleClickZoom.js @@ -22,27 +22,31 @@ import Interaction, {zoomByDelta} from '../interaction/Interaction.js'; * @param {module:ol/interaction/DoubleClickZoom~Options=} opt_options Options. * @api */ -const DoubleClickZoom = function(opt_options) { +class DoubleClickZoom { - const options = opt_options ? opt_options : {}; + constructor(opt_options) { - /** - * @private - * @type {number} - */ - this.delta_ = options.delta ? options.delta : 1; + const options = opt_options ? opt_options : {}; - Interaction.call(this, { - handleEvent: handleEvent - }); + /** + * @private + * @type {number} + */ + this.delta_ = options.delta ? options.delta : 1; - /** - * @private - * @type {number} - */ - this.duration_ = options.duration !== undefined ? options.duration : 250; + Interaction.call(this, { + handleEvent: handleEvent + }); -}; + /** + * @private + * @type {number} + */ + this.duration_ = options.duration !== undefined ? options.duration : 250; + + } + +} inherits(DoubleClickZoom, Interaction); diff --git a/src/ol/interaction/DragAndDrop.js b/src/ol/interaction/DragAndDrop.js index c34e1cf5e6..adde90c1b7 100644 --- a/src/ol/interaction/DragAndDrop.js +++ b/src/ol/interaction/DragAndDrop.js @@ -50,32 +50,37 @@ const DragAndDropEventType = { * @param {Array.=} opt_features Features. * @param {module:ol/proj/Projection=} opt_projection Projection. */ -const DragAndDropEvent = function(type, file, opt_features, opt_projection) { +class DragAndDropEvent { - Event.call(this, type); + constructor(type, file, opt_features, opt_projection) { - /** - * The features parsed from dropped data. - * @type {Array.|undefined} - * @api - */ - this.features = opt_features; + Event.call(this, type); - /** - * The dropped file. - * @type {File} - * @api - */ - this.file = file; + /** + * The features parsed from dropped data. + * @type {Array.|undefined} + * @api + */ + this.features = opt_features; - /** - * The feature projection. - * @type {module:ol/proj/Projection|undefined} - * @api - */ - this.projection = opt_projection; + /** + * The dropped file. + * @type {File} + * @api + */ + this.file = file; + + /** + * The feature projection. + * @type {module:ol/proj/Projection|undefined} + * @api + */ + this.projection = opt_projection; + + } + +} -}; inherits(DragAndDropEvent, Event); diff --git a/src/ol/interaction/DragBox.js b/src/ol/interaction/DragBox.js index 6befab8280..bfb1040ee3 100644 --- a/src/ol/interaction/DragBox.js +++ b/src/ol/interaction/DragBox.js @@ -70,25 +70,29 @@ const DragBoxEventType = { * @extends {module:ol/events/Event} * @constructor */ -const DragBoxEvent = function(type, coordinate, mapBrowserEvent) { - Event.call(this, type); +class DragBoxEvent { - /** - * The coordinate of the drag event. - * @const - * @type {module:ol/coordinate~Coordinate} - * @api - */ - this.coordinate = coordinate; + constructor(type, coordinate, mapBrowserEvent) { + Event.call(this, type); - /** - * @const - * @type {module:ol/MapBrowserEvent} - * @api - */ - this.mapBrowserEvent = mapBrowserEvent; + /** + * The coordinate of the drag event. + * @const + * @type {module:ol/coordinate~Coordinate} + * @api + */ + this.coordinate = coordinate; -}; + /** + * @const + * @type {module:ol/MapBrowserEvent} + * @api + */ + this.mapBrowserEvent = mapBrowserEvent; + + } + +} inherits(DragBoxEvent, Event); @@ -111,56 +115,56 @@ inherits(DragBoxEvent, Event); * @api */ class DragBox { - constructor(opt_options) { + constructor(opt_options) { - PointerInteraction.call(this, { - handleDownEvent: handleDownEvent, - handleDragEvent: handleDragEvent, - handleUpEvent: handleUpEvent - }); + PointerInteraction.call(this, { + handleDownEvent: handleDownEvent, + handleDragEvent: handleDragEvent, + handleUpEvent: handleUpEvent + }); - const options = opt_options ? opt_options : {}; + const options = opt_options ? opt_options : {}; - /** + /** * @type {module:ol/render/Box} * @private */ - this.box_ = new RenderBox(options.className || 'ol-dragbox'); + this.box_ = new RenderBox(options.className || 'ol-dragbox'); - /** + /** * @type {number} * @private */ - this.minArea_ = options.minArea !== undefined ? options.minArea : 64; + this.minArea_ = options.minArea !== undefined ? options.minArea : 64; - /** + /** * @type {module:ol~Pixel} * @private */ - this.startPixel_ = null; + this.startPixel_ = null; - /** + /** * @private * @type {module:ol/events/condition~Condition} */ - this.condition_ = options.condition ? options.condition : always; + this.condition_ = options.condition ? options.condition : always; - /** + /** * @private * @type {module:ol/interaction/DragBox~EndCondition} */ - this.boxEndCondition_ = options.boxEndCondition ? - options.boxEndCondition : defaultBoxEndCondition; - } + this.boxEndCondition_ = options.boxEndCondition ? + options.boxEndCondition : defaultBoxEndCondition; + } - /** + /** * Returns geometry of last drawn box. * @return {module:ol/geom/Polygon} Geometry. * @api */ - getGeometry() { - return this.box_.getGeometry(); - } + getGeometry() { + return this.box_.getGeometry(); + } } inherits(DragBox, PointerInteraction); diff --git a/src/ol/interaction/DragPan.js b/src/ol/interaction/DragPan.js index b39f8e7e35..b714199432 100644 --- a/src/ol/interaction/DragPan.js +++ b/src/ol/interaction/DragPan.js @@ -28,45 +28,48 @@ import PointerInteraction, {centroid as centroidFromPointers} from '../interacti * @param {module:ol/interaction/DragPan~Options=} opt_options Options. * @api */ -const DragPan = function(opt_options) { +class DragPan { + constructor(opt_options) { - PointerInteraction.call(this, { - handleDownEvent: handleDownEvent, - handleDragEvent: handleDragEvent, - handleUpEvent: handleUpEvent - }); + PointerInteraction.call(this, { + handleDownEvent: handleDownEvent, + handleDragEvent: handleDragEvent, + handleUpEvent: handleUpEvent + }); - const options = opt_options ? opt_options : {}; + const options = opt_options ? opt_options : {}; - /** - * @private - * @type {module:ol/Kinetic|undefined} - */ - this.kinetic_ = options.kinetic; + /** + * @private + * @type {module:ol/Kinetic|undefined} + */ + this.kinetic_ = options.kinetic; - /** - * @type {module:ol~Pixel} - */ - this.lastCentroid = null; + /** + * @type {module:ol~Pixel} + */ + this.lastCentroid = null; - /** - * @type {number} - */ - this.lastPointersCount_; + /** + * @type {number} + */ + this.lastPointersCount_; - /** - * @private - * @type {module:ol/events/condition~Condition} - */ - this.condition_ = options.condition ? options.condition : noModifierKeys; + /** + * @private + * @type {module:ol/events/condition~Condition} + */ + this.condition_ = options.condition ? options.condition : noModifierKeys; - /** - * @private - * @type {boolean} - */ - this.noKinetic_ = false; + /** + * @private + * @type {boolean} + */ + this.noKinetic_ = false; -}; + } + +} inherits(DragPan, PointerInteraction); diff --git a/src/ol/interaction/DragRotate.js b/src/ol/interaction/DragRotate.js index 71104ed096..059e18e44a 100644 --- a/src/ol/interaction/DragRotate.js +++ b/src/ol/interaction/DragRotate.js @@ -33,34 +33,39 @@ import PointerInteraction from '../interaction/Pointer.js'; * @param {module:ol/interaction/DragRotate~Options=} opt_options Options. * @api */ -const DragRotate = function(opt_options) { +class DragRotate { - const options = opt_options ? opt_options : {}; + constructor(opt_options) { - PointerInteraction.call(this, { - handleDownEvent: handleDownEvent, - handleDragEvent: handleDragEvent, - handleUpEvent: handleUpEvent - }); + const options = opt_options ? opt_options : {}; - /** - * @private - * @type {module:ol/events/condition~Condition} - */ - this.condition_ = options.condition ? options.condition : altShiftKeysOnly; + PointerInteraction.call(this, { + handleDownEvent: handleDownEvent, + handleDragEvent: handleDragEvent, + handleUpEvent: handleUpEvent + }); - /** - * @private - * @type {number|undefined} - */ - this.lastAngle_ = undefined; + /** + * @private + * @type {module:ol/events/condition~Condition} + */ + this.condition_ = options.condition ? options.condition : altShiftKeysOnly; - /** - * @private - * @type {number} - */ - this.duration_ = options.duration !== undefined ? options.duration : 250; -}; + /** + * @private + * @type {number|undefined} + */ + this.lastAngle_ = undefined; + + /** + * @private + * @type {number} + */ + this.duration_ = options.duration !== undefined ? options.duration : 250; + + } + +} inherits(DragRotate, PointerInteraction); diff --git a/src/ol/interaction/DragRotateAndZoom.js b/src/ol/interaction/DragRotateAndZoom.js index 79615db556..dc139da900 100644 --- a/src/ol/interaction/DragRotateAndZoom.js +++ b/src/ol/interaction/DragRotateAndZoom.js @@ -34,47 +34,51 @@ import PointerInteraction from '../interaction/Pointer.js'; * @param {module:ol/interaction/DragRotateAndZoom~Options=} opt_options Options. * @api */ -const DragRotateAndZoom = function(opt_options) { +class DragRotateAndZoom { - const options = opt_options ? opt_options : {}; + constructor(opt_options) { - PointerInteraction.call(this, { - handleDownEvent: handleDownEvent, - handleDragEvent: handleDragEvent, - handleUpEvent: handleUpEvent - }); + const options = opt_options ? opt_options : {}; - /** - * @private - * @type {module:ol/events/condition~Condition} - */ - this.condition_ = options.condition ? options.condition : shiftKeyOnly; + PointerInteraction.call(this, { + handleDownEvent: handleDownEvent, + handleDragEvent: handleDragEvent, + handleUpEvent: handleUpEvent + }); - /** - * @private - * @type {number|undefined} - */ - this.lastAngle_ = undefined; + /** + * @private + * @type {module:ol/events/condition~Condition} + */ + this.condition_ = options.condition ? options.condition : shiftKeyOnly; - /** - * @private - * @type {number|undefined} - */ - this.lastMagnitude_ = undefined; + /** + * @private + * @type {number|undefined} + */ + this.lastAngle_ = undefined; - /** - * @private - * @type {number} - */ - this.lastScaleDelta_ = 0; + /** + * @private + * @type {number|undefined} + */ + this.lastMagnitude_ = undefined; - /** - * @private - * @type {number} - */ - this.duration_ = options.duration !== undefined ? options.duration : 400; + /** + * @private + * @type {number} + */ + this.lastScaleDelta_ = 0; -}; + /** + * @private + * @type {number} + */ + this.duration_ = options.duration !== undefined ? options.duration : 400; + + } + +} inherits(DragRotateAndZoom, PointerInteraction); diff --git a/src/ol/interaction/Draw.js b/src/ol/interaction/Draw.js index e30dd6d24b..c5afd7c687 100644 --- a/src/ol/interaction/Draw.js +++ b/src/ol/interaction/Draw.js @@ -134,18 +134,22 @@ const DrawEventType = { * @param {module:ol/interaction/Draw~DrawEventType} type Type. * @param {module:ol/Feature} feature The feature drawn. */ -const DrawEvent = function(type, feature) { +class DrawEvent { - Event.call(this, type); + constructor(type, feature) { - /** - * The feature being drawn. - * @type {module:ol/Feature} - * @api - */ - this.feature = feature; + Event.call(this, type); -}; + /** + * The feature being drawn. + * @type {module:ol/Feature} + * @api + */ + this.feature = feature; + + } + +} inherits(DrawEvent, Event); diff --git a/src/ol/interaction/Extent.js b/src/ol/interaction/Extent.js index a3b7fcaf38..1140dd1b1a 100644 --- a/src/ol/interaction/Extent.js +++ b/src/ol/interaction/Extent.js @@ -56,17 +56,21 @@ const ExtentEventType = { * @param {module:ol/extent~Extent} extent the new extent * @extends {module:ol/events/Event} */ -const ExtentInteractionEvent = function(extent) { - Event.call(this, ExtentEventType.EXTENTCHANGED); +class ExtentInteractionEvent { - /** - * The current extent. - * @type {module:ol/extent~Extent} - * @api - */ - this.extent = extent; + constructor(extent) { + Event.call(this, ExtentEventType.EXTENTCHANGED); + + /** + * The current extent. + * @type {module:ol/extent~Extent} + * @api + */ + this.extent = extent; + } + +} -}; inherits(ExtentInteractionEvent, Event); diff --git a/src/ol/interaction/KeyboardPan.js b/src/ol/interaction/KeyboardPan.js index 87cab8384c..ca94db57e3 100644 --- a/src/ol/interaction/KeyboardPan.js +++ b/src/ol/interaction/KeyboardPan.js @@ -39,45 +39,49 @@ import Interaction, {pan} from '../interaction/Interaction.js'; * @param {module:ol/interaction/KeyboardPan~Options=} opt_options Options. * @api */ -const KeyboardPan = function(opt_options) { +class KeyboardPan { - Interaction.call(this, { - handleEvent: handleEvent - }); + constructor(opt_options) { - const options = opt_options || {}; + Interaction.call(this, { + handleEvent: handleEvent + }); - /** - * @private - * @param {module:ol/MapBrowserEvent} mapBrowserEvent Browser event. - * @return {boolean} Combined condition result. - */ - this.defaultCondition_ = function(mapBrowserEvent) { - return noModifierKeys(mapBrowserEvent) && - targetNotEditable(mapBrowserEvent); - }; + const options = opt_options || {}; - /** - * @private - * @type {module:ol/events/condition~Condition} - */ - this.condition_ = options.condition !== undefined ? - options.condition : this.defaultCondition_; + /** + * @private + * @param {module:ol/MapBrowserEvent} mapBrowserEvent Browser event. + * @return {boolean} Combined condition result. + */ + this.defaultCondition_ = function(mapBrowserEvent) { + return noModifierKeys(mapBrowserEvent) && + targetNotEditable(mapBrowserEvent); + }; - /** - * @private - * @type {number} - */ - this.duration_ = options.duration !== undefined ? options.duration : 100; + /** + * @private + * @type {module:ol/events/condition~Condition} + */ + this.condition_ = options.condition !== undefined ? + options.condition : this.defaultCondition_; - /** - * @private - * @type {number} - */ - this.pixelDelta_ = options.pixelDelta !== undefined ? - options.pixelDelta : 128; + /** + * @private + * @type {number} + */ + this.duration_ = options.duration !== undefined ? options.duration : 100; -}; + /** + * @private + * @type {number} + */ + this.pixelDelta_ = options.pixelDelta !== undefined ? + options.pixelDelta : 128; + + } + +} inherits(KeyboardPan, Interaction); diff --git a/src/ol/interaction/KeyboardZoom.js b/src/ol/interaction/KeyboardZoom.js index 4741ca5c37..e5b49053e8 100644 --- a/src/ol/interaction/KeyboardZoom.js +++ b/src/ol/interaction/KeyboardZoom.js @@ -35,33 +35,37 @@ import Interaction, {zoomByDelta} from '../interaction/Interaction.js'; * @extends {module:ol/interaction/Interaction} * @api */ -const KeyboardZoom = function(opt_options) { +class KeyboardZoom { - Interaction.call(this, { - handleEvent: handleEvent - }); + constructor(opt_options) { - const options = opt_options ? opt_options : {}; + Interaction.call(this, { + handleEvent: handleEvent + }); - /** - * @private - * @type {module:ol/events/condition~Condition} - */ - this.condition_ = options.condition ? options.condition : targetNotEditable; + const options = opt_options ? opt_options : {}; - /** - * @private - * @type {number} - */ - this.delta_ = options.delta ? options.delta : 1; + /** + * @private + * @type {module:ol/events/condition~Condition} + */ + this.condition_ = options.condition ? options.condition : targetNotEditable; - /** - * @private - * @type {number} - */ - this.duration_ = options.duration !== undefined ? options.duration : 100; + /** + * @private + * @type {number} + */ + this.delta_ = options.delta ? options.delta : 1; -}; + /** + * @private + * @type {number} + */ + this.duration_ = options.duration !== undefined ? options.duration : 100; + + } + +} inherits(KeyboardZoom, Interaction); diff --git a/src/ol/interaction/Modify.js b/src/ol/interaction/Modify.js index bff27bc18d..3708f37ddc 100644 --- a/src/ol/interaction/Modify.js +++ b/src/ol/interaction/Modify.js @@ -99,24 +99,29 @@ const ModifyEventType = { * @param {module:ol/MapBrowserPointerEvent} mapBrowserPointerEvent * Associated {@link module:ol/MapBrowserPointerEvent}. */ -export const ModifyEvent = function(type, features, mapBrowserPointerEvent) { +export class ModifyEvent { - Event.call(this, type); + constructor(type, features, mapBrowserPointerEvent) { - /** - * The features being modified. - * @type {module:ol/Collection.} - * @api - */ - this.features = features; + Event.call(this, type); - /** - * Associated {@link module:ol/MapBrowserEvent}. - * @type {module:ol/MapBrowserEvent} - * @api - */ - this.mapBrowserEvent = mapBrowserPointerEvent; -}; + /** + * The features being modified. + * @type {module:ol/Collection.} + * @api + */ + this.features = features; + + /** + * Associated {@link module:ol/MapBrowserEvent}. + * @type {module:ol/MapBrowserEvent} + * @api + */ + this.mapBrowserEvent = mapBrowserPointerEvent; + + } + +} inherits(ModifyEvent, Event); diff --git a/src/ol/interaction/PinchRotate.js b/src/ol/interaction/PinchRotate.js index d882f3383a..8c06a4c0c2 100644 --- a/src/ol/interaction/PinchRotate.js +++ b/src/ol/interaction/PinchRotate.js @@ -27,53 +27,57 @@ import {disable} from '../rotationconstraint.js'; * @param {module:ol/interaction/PinchRotate~Options=} opt_options Options. * @api */ -const PinchRotate = function(opt_options) { +class PinchRotate { - PointerInteraction.call(this, { - handleDownEvent: handleDownEvent, - handleDragEvent: handleDragEvent, - handleUpEvent: handleUpEvent - }); + constructor(opt_options) { - const options = opt_options || {}; + PointerInteraction.call(this, { + handleDownEvent: handleDownEvent, + handleDragEvent: handleDragEvent, + handleUpEvent: handleUpEvent + }); - /** - * @private - * @type {module:ol/coordinate~Coordinate} - */ - this.anchor_ = null; + const options = opt_options || {}; - /** - * @private - * @type {number|undefined} - */ - this.lastAngle_ = undefined; + /** + * @private + * @type {module:ol/coordinate~Coordinate} + */ + this.anchor_ = null; - /** - * @private - * @type {boolean} - */ - this.rotating_ = false; + /** + * @private + * @type {number|undefined} + */ + this.lastAngle_ = undefined; - /** - * @private - * @type {number} - */ - this.rotationDelta_ = 0.0; + /** + * @private + * @type {boolean} + */ + this.rotating_ = false; - /** - * @private - * @type {number} - */ - this.threshold_ = options.threshold !== undefined ? options.threshold : 0.3; + /** + * @private + * @type {number} + */ + this.rotationDelta_ = 0.0; - /** - * @private - * @type {number} - */ - this.duration_ = options.duration !== undefined ? options.duration : 250; + /** + * @private + * @type {number} + */ + this.threshold_ = options.threshold !== undefined ? options.threshold : 0.3; -}; + /** + * @private + * @type {number} + */ + this.duration_ = options.duration !== undefined ? options.duration : 250; + + } + +} inherits(PinchRotate, PointerInteraction); diff --git a/src/ol/interaction/PinchZoom.js b/src/ol/interaction/PinchZoom.js index 639bca1de9..542770b9f6 100644 --- a/src/ol/interaction/PinchZoom.js +++ b/src/ol/interaction/PinchZoom.js @@ -26,47 +26,51 @@ import PointerInteraction, {centroid as centroidFromPointers} from '../interacti * @param {module:ol/interaction/PinchZoom~Options=} opt_options Options. * @api */ -const PinchZoom = function(opt_options) { +class PinchZoom { - PointerInteraction.call(this, { - handleDownEvent: handleDownEvent, - handleDragEvent: handleDragEvent, - handleUpEvent: handleUpEvent - }); + constructor(opt_options) { - const options = opt_options ? opt_options : {}; + PointerInteraction.call(this, { + handleDownEvent: handleDownEvent, + handleDragEvent: handleDragEvent, + handleUpEvent: handleUpEvent + }); - /** - * @private - * @type {boolean} - */ - this.constrainResolution_ = options.constrainResolution || false; + const options = opt_options ? opt_options : {}; - /** - * @private - * @type {module:ol/coordinate~Coordinate} - */ - this.anchor_ = null; + /** + * @private + * @type {boolean} + */ + this.constrainResolution_ = options.constrainResolution || false; - /** - * @private - * @type {number} - */ - this.duration_ = options.duration !== undefined ? options.duration : 400; + /** + * @private + * @type {module:ol/coordinate~Coordinate} + */ + this.anchor_ = null; - /** - * @private - * @type {number|undefined} - */ - this.lastDistance_ = undefined; + /** + * @private + * @type {number} + */ + this.duration_ = options.duration !== undefined ? options.duration : 400; - /** - * @private - * @type {number} - */ - this.lastScaleDelta_ = 1; + /** + * @private + * @type {number|undefined} + */ + this.lastDistance_ = undefined; -}; + /** + * @private + * @type {number} + */ + this.lastScaleDelta_ = 1; + + } + +} inherits(PinchZoom, PointerInteraction); diff --git a/src/ol/interaction/Select.js b/src/ol/interaction/Select.js index 1fb87e9063..d209c69c64 100644 --- a/src/ol/interaction/Select.js +++ b/src/ol/interaction/Select.js @@ -110,30 +110,35 @@ const SelectEventType = { * @extends {module:ol/events/Event} * @constructor */ -const SelectEvent = function(type, selected, deselected, mapBrowserEvent) { - Event.call(this, type); +class SelectEvent { - /** - * Selected features array. - * @type {Array.} - * @api - */ - this.selected = selected; + constructor(type, selected, deselected, mapBrowserEvent) { + Event.call(this, type); - /** - * Deselected features array. - * @type {Array.} - * @api - */ - this.deselected = deselected; + /** + * Selected features array. + * @type {Array.} + * @api + */ + this.selected = selected; - /** - * Associated {@link module:ol/MapBrowserEvent}. - * @type {module:ol/MapBrowserEvent} - * @api - */ - this.mapBrowserEvent = mapBrowserEvent; -}; + /** + * Deselected features array. + * @type {Array.} + * @api + */ + this.deselected = deselected; + + /** + * Associated {@link module:ol/MapBrowserEvent}. + * @type {module:ol/MapBrowserEvent} + * @api + */ + this.mapBrowserEvent = mapBrowserEvent; + + } + +} inherits(SelectEvent, Event); diff --git a/src/ol/interaction/Translate.js b/src/ol/interaction/Translate.js index 31ab39650e..e7d40ac29f 100644 --- a/src/ol/interaction/Translate.js +++ b/src/ol/interaction/Translate.js @@ -63,25 +63,30 @@ const TranslateEventType = { * @param {module:ol/Collection.} features The features translated. * @param {module:ol/coordinate~Coordinate} coordinate The event coordinate. */ -export const TranslateEvent = function(type, features, coordinate) { +export class TranslateEvent { - Event.call(this, type); + constructor(type, features, coordinate) { - /** - * The features being translated. - * @type {module:ol/Collection.} - * @api - */ - this.features = features; + Event.call(this, type); - /** - * The coordinate of the drag event. - * @const - * @type {module:ol/coordinate~Coordinate} - * @api - */ - this.coordinate = coordinate; -}; + /** + * The features being translated. + * @type {module:ol/Collection.} + * @api + */ + this.features = features; + + /** + * The coordinate of the drag event. + * @const + * @type {module:ol/coordinate~Coordinate} + * @api + */ + this.coordinate = coordinate; + + } + +} inherits(TranslateEvent, Event); diff --git a/src/ol/layer/Image.js b/src/ol/layer/Image.js index 6f67096260..3b09138dfe 100644 --- a/src/ol/layer/Image.js +++ b/src/ol/layer/Image.js @@ -40,18 +40,22 @@ import Layer from '../layer/Layer.js'; * @param {module:ol/layer/Image~Options=} opt_options Layer options. * @api */ -const ImageLayer = function(opt_options) { - const options = opt_options ? opt_options : {}; - Layer.call(this, /** @type {module:ol/layer/Layer~Options} */ (options)); +class ImageLayer { - /** - * The layer type. - * @protected - * @type {module:ol/LayerType} - */ - this.type = LayerType.IMAGE; + constructor(opt_options) { + const options = opt_options ? opt_options : {}; + Layer.call(this, /** @type {module:ol/layer/Layer~Options} */ (options)); -}; + /** + * The layer type. + * @protected + * @type {module:ol/LayerType} + */ + this.type = LayerType.IMAGE; + + } + +} inherits(ImageLayer, Layer); diff --git a/src/ol/proj/Projection.js b/src/ol/proj/Projection.js index 91ccf3a2e9..6a59f4847d 100644 --- a/src/ol/proj/Projection.js +++ b/src/ol/proj/Projection.js @@ -55,134 +55,134 @@ import {METERS_PER_UNIT} from '../proj/Units.js'; * @api */ class Projection { - constructor(options) { - /** + constructor(options) { + /** * @private * @type {string} */ - this.code_ = options.code; + this.code_ = options.code; - /** + /** * Units of projected coordinates. When set to `TILE_PIXELS`, a * `this.extent_` and `this.worldExtent_` must be configured properly for each * tile. * @private * @type {module:ol/proj/Units} */ - this.units_ = /** @type {module:ol/proj/Units} */ (options.units); + this.units_ = /** @type {module:ol/proj/Units} */ (options.units); - /** + /** * Validity extent of the projection in projected coordinates. For projections * with `TILE_PIXELS` units, this is the extent of the tile in * tile pixel space. * @private * @type {module:ol/extent~Extent} */ - this.extent_ = options.extent !== undefined ? options.extent : null; + this.extent_ = options.extent !== undefined ? options.extent : null; - /** + /** * Extent of the world in EPSG:4326. For projections with * `TILE_PIXELS` units, this is the extent of the tile in * projected coordinate space. * @private * @type {module:ol/extent~Extent} */ - this.worldExtent_ = options.worldExtent !== undefined ? - options.worldExtent : null; + this.worldExtent_ = options.worldExtent !== undefined ? + options.worldExtent : null; - /** + /** * @private * @type {string} */ - this.axisOrientation_ = options.axisOrientation !== undefined ? - options.axisOrientation : 'enu'; + this.axisOrientation_ = options.axisOrientation !== undefined ? + options.axisOrientation : 'enu'; - /** + /** * @private * @type {boolean} */ - this.global_ = options.global !== undefined ? options.global : false; + this.global_ = options.global !== undefined ? options.global : false; - /** + /** * @private * @type {boolean} */ - this.canWrapX_ = !!(this.global_ && this.extent_); + this.canWrapX_ = !!(this.global_ && this.extent_); - /** + /** * @private * @type {function(number, module:ol/coordinate~Coordinate):number|undefined} */ - this.getPointResolutionFunc_ = options.getPointResolution; + this.getPointResolutionFunc_ = options.getPointResolution; - /** + /** * @private * @type {module:ol/tilegrid/TileGrid} */ - this.defaultTileGrid_ = null; + this.defaultTileGrid_ = null; - /** + /** * @private * @type {number|undefined} */ - this.metersPerUnit_ = options.metersPerUnit; - } + this.metersPerUnit_ = options.metersPerUnit; + } - /** + /** * @return {boolean} The projection is suitable for wrapping the x-axis */ - canWrapX() { - return this.canWrapX_; - } + canWrapX() { + return this.canWrapX_; + } - /** + /** * Get the code for this projection, e.g. 'EPSG:4326'. * @return {string} Code. * @api */ - getCode() { - return this.code_; - } + getCode() { + return this.code_; + } - /** + /** * Get the validity extent for this projection. * @return {module:ol/extent~Extent} Extent. * @api */ - getExtent() { - return this.extent_; - } + getExtent() { + return this.extent_; + } - /** + /** * Get the units of this projection. * @return {module:ol/proj/Units} Units. * @api */ - getUnits() { - return this.units_; - } + getUnits() { + return this.units_; + } - /** + /** * Get the amount of meters per unit of this projection. If the projection is * not configured with `metersPerUnit` or a units identifier, the return is * `undefined`. * @return {number|undefined} Meters. * @api */ - getMetersPerUnit() { - return this.metersPerUnit_ || METERS_PER_UNIT[this.units_]; - } + getMetersPerUnit() { + return this.metersPerUnit_ || METERS_PER_UNIT[this.units_]; + } - /** + /** * Get the world extent for this projection. * @return {module:ol/extent~Extent} Extent. * @api */ - getWorldExtent() { - return this.worldExtent_; - } + getWorldExtent() { + return this.worldExtent_; + } - /** + /** * Get the axis orientation of this projection. * Example values are: * enu - the default easting, northing, elevation. @@ -193,81 +193,81 @@ class Projection { * @return {string} Axis orientation. * @api */ - getAxisOrientation() { - return this.axisOrientation_; - } + getAxisOrientation() { + return this.axisOrientation_; + } - /** + /** * Is this projection a global projection which spans the whole world? * @return {boolean} Whether the projection is global. * @api */ - isGlobal() { - return this.global_; - } + isGlobal() { + return this.global_; + } - /** + /** * Set if the projection is a global projection which spans the whole world * @param {boolean} global Whether the projection is global. * @api */ - setGlobal(global) { - this.global_ = global; - this.canWrapX_ = !!(global && this.extent_); - } + setGlobal(global) { + this.global_ = global; + this.canWrapX_ = !!(global && this.extent_); + } - /** + /** * @return {module:ol/tilegrid/TileGrid} The default tile grid. */ - getDefaultTileGrid() { - return this.defaultTileGrid_; - } + getDefaultTileGrid() { + return this.defaultTileGrid_; + } - /** + /** * @param {module:ol/tilegrid/TileGrid} tileGrid The default tile grid. */ - setDefaultTileGrid(tileGrid) { - this.defaultTileGrid_ = tileGrid; - } + setDefaultTileGrid(tileGrid) { + this.defaultTileGrid_ = tileGrid; + } - /** + /** * Set the validity extent for this projection. * @param {module:ol/extent~Extent} extent Extent. * @api */ - setExtent(extent) { - this.extent_ = extent; - this.canWrapX_ = !!(this.global_ && extent); - } + setExtent(extent) { + this.extent_ = extent; + this.canWrapX_ = !!(this.global_ && extent); + } - /** + /** * Set the world extent for this projection. * @param {module:ol/extent~Extent} worldExtent World extent * [minlon, minlat, maxlon, maxlat]. * @api */ - setWorldExtent(worldExtent) { - this.worldExtent_ = worldExtent; - } + setWorldExtent(worldExtent) { + this.worldExtent_ = worldExtent; + } - /** + /** * Set the getPointResolution function (see {@link module:ol/proj~getPointResolution} * for this projection. * @param {function(number, module:ol/coordinate~Coordinate):number} func Function * @api */ - setGetPointResolution(func) { - this.getPointResolutionFunc_ = func; - } + setGetPointResolution(func) { + this.getPointResolutionFunc_ = func; + } - /** + /** * Get the custom point resolution function for this projection (if set). * @return {function(number, module:ol/coordinate~Coordinate):number|undefined} The custom point * resolution function (if set). */ - getPointResolutionFunc() { - return this.getPointResolutionFunc_; - } + getPointResolutionFunc() { + return this.getPointResolutionFunc_; + } } export default Projection; diff --git a/src/ol/proj/epsg3857.js b/src/ol/proj/epsg3857.js index f47d4e8ba7..c5e96cc7b0 100644 --- a/src/ol/proj/epsg3857.js +++ b/src/ol/proj/epsg3857.js @@ -48,18 +48,24 @@ export const WORLD_EXTENT = [-180, -85, 180, 85]; * @extends {module:ol/proj/Projection} * @param {string} code Code. */ -function EPSG3857Projection(code) { - Projection.call(this, { - code: code, - units: Units.METERS, - extent: EXTENT, - global: true, - worldExtent: WORLD_EXTENT, - getPointResolution: function(resolution, point) { - return resolution / cosh(point[1] / RADIUS); - } - }); +class EPSG3857Projection { + + constructor(code) { + Projection.call(this, { + code: code, + units: Units.METERS, + extent: EXTENT, + global: true, + worldExtent: WORLD_EXTENT, + getPointResolution: function(resolution, point) { + return resolution / cosh(point[1] / RADIUS); + } + }); + + } + } + inherits(EPSG3857Projection, Projection); diff --git a/src/ol/proj/epsg4326.js b/src/ol/proj/epsg4326.js index 2f7ea2a80c..39363d29dc 100644 --- a/src/ol/proj/epsg4326.js +++ b/src/ol/proj/epsg4326.js @@ -44,17 +44,23 @@ export const METERS_PER_UNIT = Math.PI * RADIUS / 180; * @param {string} code Code. * @param {string=} opt_axisOrientation Axis orientation. */ -function EPSG4326Projection(code, opt_axisOrientation) { - Projection.call(this, { - code: code, - units: Units.DEGREES, - extent: EXTENT, - axisOrientation: opt_axisOrientation, - global: true, - metersPerUnit: METERS_PER_UNIT, - worldExtent: EXTENT - }); +class EPSG4326Projection { + + constructor(code, opt_axisOrientation) { + Projection.call(this, { + code: code, + units: Units.DEGREES, + extent: EXTENT, + axisOrientation: opt_axisOrientation, + global: true, + metersPerUnit: METERS_PER_UNIT, + worldExtent: EXTENT + }); + + } + } + inherits(EPSG4326Projection, Projection); diff --git a/src/ol/render/Event.js b/src/ol/render/Event.js index bd64e18c58..ff1052dfd3 100644 --- a/src/ol/render/Event.js +++ b/src/ol/render/Event.js @@ -13,43 +13,45 @@ import Event from '../events/Event.js'; * @param {?CanvasRenderingContext2D=} opt_context Context. * @param {?module:ol/webgl/Context=} opt_glContext WebGL Context. */ -const RenderEvent = function( - type, opt_vectorContext, opt_frameState, opt_context, - opt_glContext) { +class RenderEvent { - Event.call(this, type); + constructor(type, opt_vectorContext, opt_frameState, opt_context, opt_glContext) { - /** - * For canvas, this is an instance of {@link module:ol/render/canvas/Immediate}. - * @type {module:ol/render/VectorContext|undefined} - * @api - */ - this.vectorContext = opt_vectorContext; + Event.call(this, type); - /** - * An object representing the current render frame state. - * @type {module:ol/PluggableMap~FrameState|undefined} - * @api - */ - this.frameState = opt_frameState; + /** + * For canvas, this is an instance of {@link module:ol/render/canvas/Immediate}. + * @type {module:ol/render/VectorContext|undefined} + * @api + */ + this.vectorContext = opt_vectorContext; - /** - * Canvas context. Only available when a Canvas renderer is used, null - * otherwise. - * @type {CanvasRenderingContext2D|null|undefined} - * @api - */ - this.context = opt_context; + /** + * An object representing the current render frame state. + * @type {module:ol/PluggableMap~FrameState|undefined} + * @api + */ + this.frameState = opt_frameState; - /** - * WebGL context. Only available when a WebGL renderer is used, null - * otherwise. - * @type {module:ol/webgl/Context|null|undefined} - * @api - */ - this.glContext = opt_glContext; + /** + * Canvas context. Only available when a Canvas renderer is used, null + * otherwise. + * @type {CanvasRenderingContext2D|null|undefined} + * @api + */ + this.context = opt_context; -}; + /** + * WebGL context. Only available when a WebGL renderer is used, null + * otherwise. + * @type {module:ol/webgl/Context|null|undefined} + * @api + */ + this.glContext = opt_glContext; + + } + +} inherits(RenderEvent, Event); export default RenderEvent; diff --git a/src/ol/render/webgl/circlereplay/defaultshader/Locations.js b/src/ol/render/webgl/circlereplay/defaultshader/Locations.js index dd87ac0ceb..1cd45a199c 100644 --- a/src/ol/render/webgl/circlereplay/defaultshader/Locations.js +++ b/src/ol/render/webgl/circlereplay/defaultshader/Locations.js @@ -12,79 +12,84 @@ import {DEBUG as DEBUG_WEBGL} from '../../../../webgl.js'; * @param {WebGLProgram} program Program. * @struct */ -const Locations = function(gl, program) { +class Locations { - /** - * @type {WebGLUniformLocation} - */ - this.u_projectionMatrix = gl.getUniformLocation( - program, DEBUG_WEBGL ? 'u_projectionMatrix' : 'h'); + constructor(gl, program) { - /** - * @type {WebGLUniformLocation} - */ - this.u_offsetScaleMatrix = gl.getUniformLocation( - program, DEBUG_WEBGL ? 'u_offsetScaleMatrix' : 'i'); + /** + * @type {WebGLUniformLocation} + */ + this.u_projectionMatrix = gl.getUniformLocation( + program, DEBUG_WEBGL ? 'u_projectionMatrix' : 'h'); - /** - * @type {WebGLUniformLocation} - */ - this.u_offsetRotateMatrix = gl.getUniformLocation( - program, DEBUG_WEBGL ? 'u_offsetRotateMatrix' : 'j'); + /** + * @type {WebGLUniformLocation} + */ + this.u_offsetScaleMatrix = gl.getUniformLocation( + program, DEBUG_WEBGL ? 'u_offsetScaleMatrix' : 'i'); - /** - * @type {WebGLUniformLocation} - */ - this.u_lineWidth = gl.getUniformLocation( - program, DEBUG_WEBGL ? 'u_lineWidth' : 'k'); + /** + * @type {WebGLUniformLocation} + */ + this.u_offsetRotateMatrix = gl.getUniformLocation( + program, DEBUG_WEBGL ? 'u_offsetRotateMatrix' : 'j'); - /** - * @type {WebGLUniformLocation} - */ - this.u_pixelRatio = gl.getUniformLocation( - program, DEBUG_WEBGL ? 'u_pixelRatio' : 'l'); + /** + * @type {WebGLUniformLocation} + */ + this.u_lineWidth = gl.getUniformLocation( + program, DEBUG_WEBGL ? 'u_lineWidth' : 'k'); - /** - * @type {WebGLUniformLocation} - */ - this.u_opacity = gl.getUniformLocation( - program, DEBUG_WEBGL ? 'u_opacity' : 'm'); + /** + * @type {WebGLUniformLocation} + */ + this.u_pixelRatio = gl.getUniformLocation( + program, DEBUG_WEBGL ? 'u_pixelRatio' : 'l'); - /** - * @type {WebGLUniformLocation} - */ - this.u_fillColor = gl.getUniformLocation( - program, DEBUG_WEBGL ? 'u_fillColor' : 'n'); + /** + * @type {WebGLUniformLocation} + */ + this.u_opacity = gl.getUniformLocation( + program, DEBUG_WEBGL ? 'u_opacity' : 'm'); - /** - * @type {WebGLUniformLocation} - */ - this.u_strokeColor = gl.getUniformLocation( - program, DEBUG_WEBGL ? 'u_strokeColor' : 'o'); + /** + * @type {WebGLUniformLocation} + */ + this.u_fillColor = gl.getUniformLocation( + program, DEBUG_WEBGL ? 'u_fillColor' : 'n'); - /** - * @type {WebGLUniformLocation} - */ - this.u_size = gl.getUniformLocation( - program, DEBUG_WEBGL ? 'u_size' : 'p'); + /** + * @type {WebGLUniformLocation} + */ + this.u_strokeColor = gl.getUniformLocation( + program, DEBUG_WEBGL ? 'u_strokeColor' : 'o'); - /** - * @type {number} - */ - this.a_position = gl.getAttribLocation( - program, DEBUG_WEBGL ? 'a_position' : 'e'); + /** + * @type {WebGLUniformLocation} + */ + this.u_size = gl.getUniformLocation( + program, DEBUG_WEBGL ? 'u_size' : 'p'); - /** - * @type {number} - */ - this.a_instruction = gl.getAttribLocation( - program, DEBUG_WEBGL ? 'a_instruction' : 'f'); + /** + * @type {number} + */ + this.a_position = gl.getAttribLocation( + program, DEBUG_WEBGL ? 'a_position' : 'e'); - /** - * @type {number} - */ - this.a_radius = gl.getAttribLocation( - program, DEBUG_WEBGL ? 'a_radius' : 'g'); -}; + /** + * @type {number} + */ + this.a_instruction = gl.getAttribLocation( + program, DEBUG_WEBGL ? 'a_instruction' : 'f'); + + /** + * @type {number} + */ + this.a_radius = gl.getAttribLocation( + program, DEBUG_WEBGL ? 'a_radius' : 'g'); + + } + +} export default Locations; diff --git a/src/ol/render/webgl/linestringreplay/defaultshader/Locations.js b/src/ol/render/webgl/linestringreplay/defaultshader/Locations.js index cbe7a09dff..b1fc309e29 100644 --- a/src/ol/render/webgl/linestringreplay/defaultshader/Locations.js +++ b/src/ol/render/webgl/linestringreplay/defaultshader/Locations.js @@ -12,85 +12,90 @@ import {DEBUG as DEBUG_WEBGL} from '../../../../webgl.js'; * @param {WebGLProgram} program Program. * @struct */ -const Locations = function(gl, program) { +class Locations { - /** - * @type {WebGLUniformLocation} - */ - this.u_projectionMatrix = gl.getUniformLocation( - program, DEBUG_WEBGL ? 'u_projectionMatrix' : 'h'); + constructor(gl, program) { - /** - * @type {WebGLUniformLocation} - */ - this.u_offsetScaleMatrix = gl.getUniformLocation( - program, DEBUG_WEBGL ? 'u_offsetScaleMatrix' : 'i'); + /** + * @type {WebGLUniformLocation} + */ + this.u_projectionMatrix = gl.getUniformLocation( + program, DEBUG_WEBGL ? 'u_projectionMatrix' : 'h'); - /** - * @type {WebGLUniformLocation} - */ - this.u_offsetRotateMatrix = gl.getUniformLocation( - program, DEBUG_WEBGL ? 'u_offsetRotateMatrix' : 'j'); + /** + * @type {WebGLUniformLocation} + */ + this.u_offsetScaleMatrix = gl.getUniformLocation( + program, DEBUG_WEBGL ? 'u_offsetScaleMatrix' : 'i'); - /** - * @type {WebGLUniformLocation} - */ - this.u_lineWidth = gl.getUniformLocation( - program, DEBUG_WEBGL ? 'u_lineWidth' : 'k'); + /** + * @type {WebGLUniformLocation} + */ + this.u_offsetRotateMatrix = gl.getUniformLocation( + program, DEBUG_WEBGL ? 'u_offsetRotateMatrix' : 'j'); - /** - * @type {WebGLUniformLocation} - */ - this.u_miterLimit = gl.getUniformLocation( - program, DEBUG_WEBGL ? 'u_miterLimit' : 'l'); + /** + * @type {WebGLUniformLocation} + */ + this.u_lineWidth = gl.getUniformLocation( + program, DEBUG_WEBGL ? 'u_lineWidth' : 'k'); - /** - * @type {WebGLUniformLocation} - */ - this.u_opacity = gl.getUniformLocation( - program, DEBUG_WEBGL ? 'u_opacity' : 'm'); + /** + * @type {WebGLUniformLocation} + */ + this.u_miterLimit = gl.getUniformLocation( + program, DEBUG_WEBGL ? 'u_miterLimit' : 'l'); - /** - * @type {WebGLUniformLocation} - */ - this.u_color = gl.getUniformLocation( - program, DEBUG_WEBGL ? 'u_color' : 'n'); + /** + * @type {WebGLUniformLocation} + */ + this.u_opacity = gl.getUniformLocation( + program, DEBUG_WEBGL ? 'u_opacity' : 'm'); - /** - * @type {WebGLUniformLocation} - */ - this.u_size = gl.getUniformLocation( - program, DEBUG_WEBGL ? 'u_size' : 'o'); + /** + * @type {WebGLUniformLocation} + */ + this.u_color = gl.getUniformLocation( + program, DEBUG_WEBGL ? 'u_color' : 'n'); - /** - * @type {WebGLUniformLocation} - */ - this.u_pixelRatio = gl.getUniformLocation( - program, DEBUG_WEBGL ? 'u_pixelRatio' : 'p'); + /** + * @type {WebGLUniformLocation} + */ + this.u_size = gl.getUniformLocation( + program, DEBUG_WEBGL ? 'u_size' : 'o'); - /** - * @type {number} - */ - this.a_lastPos = gl.getAttribLocation( - program, DEBUG_WEBGL ? 'a_lastPos' : 'd'); + /** + * @type {WebGLUniformLocation} + */ + this.u_pixelRatio = gl.getUniformLocation( + program, DEBUG_WEBGL ? 'u_pixelRatio' : 'p'); - /** - * @type {number} - */ - this.a_position = gl.getAttribLocation( - program, DEBUG_WEBGL ? 'a_position' : 'e'); + /** + * @type {number} + */ + this.a_lastPos = gl.getAttribLocation( + program, DEBUG_WEBGL ? 'a_lastPos' : 'd'); - /** - * @type {number} - */ - this.a_nextPos = gl.getAttribLocation( - program, DEBUG_WEBGL ? 'a_nextPos' : 'f'); + /** + * @type {number} + */ + this.a_position = gl.getAttribLocation( + program, DEBUG_WEBGL ? 'a_position' : 'e'); - /** - * @type {number} - */ - this.a_direction = gl.getAttribLocation( - program, DEBUG_WEBGL ? 'a_direction' : 'g'); -}; + /** + * @type {number} + */ + this.a_nextPos = gl.getAttribLocation( + program, DEBUG_WEBGL ? 'a_nextPos' : 'f'); + + /** + * @type {number} + */ + this.a_direction = gl.getAttribLocation( + program, DEBUG_WEBGL ? 'a_direction' : 'g'); + + } + +} export default Locations; diff --git a/src/ol/render/webgl/polygonreplay/defaultshader/Locations.js b/src/ol/render/webgl/polygonreplay/defaultshader/Locations.js index f2abb411f4..5cb9960cd5 100644 --- a/src/ol/render/webgl/polygonreplay/defaultshader/Locations.js +++ b/src/ol/render/webgl/polygonreplay/defaultshader/Locations.js @@ -12,43 +12,48 @@ import {DEBUG as DEBUG_WEBGL} from '../../../../webgl.js'; * @param {WebGLProgram} program Program. * @struct */ -const Locations = function(gl, program) { +class Locations { - /** - * @type {WebGLUniformLocation} - */ - this.u_projectionMatrix = gl.getUniformLocation( - program, DEBUG_WEBGL ? 'u_projectionMatrix' : 'b'); + constructor(gl, program) { - /** - * @type {WebGLUniformLocation} - */ - this.u_offsetScaleMatrix = gl.getUniformLocation( - program, DEBUG_WEBGL ? 'u_offsetScaleMatrix' : 'c'); + /** + * @type {WebGLUniformLocation} + */ + this.u_projectionMatrix = gl.getUniformLocation( + program, DEBUG_WEBGL ? 'u_projectionMatrix' : 'b'); - /** - * @type {WebGLUniformLocation} - */ - this.u_offsetRotateMatrix = gl.getUniformLocation( - program, DEBUG_WEBGL ? 'u_offsetRotateMatrix' : 'd'); + /** + * @type {WebGLUniformLocation} + */ + this.u_offsetScaleMatrix = gl.getUniformLocation( + program, DEBUG_WEBGL ? 'u_offsetScaleMatrix' : 'c'); - /** - * @type {WebGLUniformLocation} - */ - this.u_color = gl.getUniformLocation( - program, DEBUG_WEBGL ? 'u_color' : 'e'); + /** + * @type {WebGLUniformLocation} + */ + this.u_offsetRotateMatrix = gl.getUniformLocation( + program, DEBUG_WEBGL ? 'u_offsetRotateMatrix' : 'd'); - /** - * @type {WebGLUniformLocation} - */ - this.u_opacity = gl.getUniformLocation( - program, DEBUG_WEBGL ? 'u_opacity' : 'f'); + /** + * @type {WebGLUniformLocation} + */ + this.u_color = gl.getUniformLocation( + program, DEBUG_WEBGL ? 'u_color' : 'e'); - /** - * @type {number} - */ - this.a_position = gl.getAttribLocation( - program, DEBUG_WEBGL ? 'a_position' : 'a'); -}; + /** + * @type {WebGLUniformLocation} + */ + this.u_opacity = gl.getUniformLocation( + program, DEBUG_WEBGL ? 'u_opacity' : 'f'); + + /** + * @type {number} + */ + this.a_position = gl.getAttribLocation( + program, DEBUG_WEBGL ? 'a_position' : 'a'); + + } + +} export default Locations; diff --git a/src/ol/render/webgl/texturereplay/defaultshader/Locations.js b/src/ol/render/webgl/texturereplay/defaultshader/Locations.js index 1d5bdddac8..90c99fb757 100644 --- a/src/ol/render/webgl/texturereplay/defaultshader/Locations.js +++ b/src/ol/render/webgl/texturereplay/defaultshader/Locations.js @@ -12,67 +12,72 @@ import {DEBUG as DEBUG_WEBGL} from '../../../../webgl.js'; * @param {WebGLProgram} program Program. * @struct */ -const Locations = function(gl, program) { +class Locations { - /** - * @type {WebGLUniformLocation} - */ - this.u_projectionMatrix = gl.getUniformLocation( - program, DEBUG_WEBGL ? 'u_projectionMatrix' : 'h'); + constructor(gl, program) { - /** - * @type {WebGLUniformLocation} - */ - this.u_offsetScaleMatrix = gl.getUniformLocation( - program, DEBUG_WEBGL ? 'u_offsetScaleMatrix' : 'i'); + /** + * @type {WebGLUniformLocation} + */ + this.u_projectionMatrix = gl.getUniformLocation( + program, DEBUG_WEBGL ? 'u_projectionMatrix' : 'h'); - /** - * @type {WebGLUniformLocation} - */ - this.u_offsetRotateMatrix = gl.getUniformLocation( - program, DEBUG_WEBGL ? 'u_offsetRotateMatrix' : 'j'); + /** + * @type {WebGLUniformLocation} + */ + this.u_offsetScaleMatrix = gl.getUniformLocation( + program, DEBUG_WEBGL ? 'u_offsetScaleMatrix' : 'i'); - /** - * @type {WebGLUniformLocation} - */ - this.u_opacity = gl.getUniformLocation( - program, DEBUG_WEBGL ? 'u_opacity' : 'k'); + /** + * @type {WebGLUniformLocation} + */ + this.u_offsetRotateMatrix = gl.getUniformLocation( + program, DEBUG_WEBGL ? 'u_offsetRotateMatrix' : 'j'); - /** - * @type {WebGLUniformLocation} - */ - this.u_image = gl.getUniformLocation( - program, DEBUG_WEBGL ? 'u_image' : 'l'); + /** + * @type {WebGLUniformLocation} + */ + this.u_opacity = gl.getUniformLocation( + program, DEBUG_WEBGL ? 'u_opacity' : 'k'); - /** - * @type {number} - */ - this.a_position = gl.getAttribLocation( - program, DEBUG_WEBGL ? 'a_position' : 'c'); + /** + * @type {WebGLUniformLocation} + */ + this.u_image = gl.getUniformLocation( + program, DEBUG_WEBGL ? 'u_image' : 'l'); - /** - * @type {number} - */ - this.a_texCoord = gl.getAttribLocation( - program, DEBUG_WEBGL ? 'a_texCoord' : 'd'); + /** + * @type {number} + */ + this.a_position = gl.getAttribLocation( + program, DEBUG_WEBGL ? 'a_position' : 'c'); - /** - * @type {number} - */ - this.a_offsets = gl.getAttribLocation( - program, DEBUG_WEBGL ? 'a_offsets' : 'e'); + /** + * @type {number} + */ + this.a_texCoord = gl.getAttribLocation( + program, DEBUG_WEBGL ? 'a_texCoord' : 'd'); - /** - * @type {number} - */ - this.a_opacity = gl.getAttribLocation( - program, DEBUG_WEBGL ? 'a_opacity' : 'f'); + /** + * @type {number} + */ + this.a_offsets = gl.getAttribLocation( + program, DEBUG_WEBGL ? 'a_offsets' : 'e'); - /** - * @type {number} - */ - this.a_rotateWithView = gl.getAttribLocation( - program, DEBUG_WEBGL ? 'a_rotateWithView' : 'g'); -}; + /** + * @type {number} + */ + this.a_opacity = gl.getAttribLocation( + program, DEBUG_WEBGL ? 'a_opacity' : 'f'); + + /** + * @type {number} + */ + this.a_rotateWithView = gl.getAttribLocation( + program, DEBUG_WEBGL ? 'a_rotateWithView' : 'g'); + + } + +} export default Locations; diff --git a/src/ol/renderer/webgl/defaultmapshader/Locations.js b/src/ol/renderer/webgl/defaultmapshader/Locations.js index 573efe0aaa..0ac95de300 100644 --- a/src/ol/renderer/webgl/defaultmapshader/Locations.js +++ b/src/ol/renderer/webgl/defaultmapshader/Locations.js @@ -4,7 +4,7 @@ // This file is automatically generated, do not edit // Run `make shaders` to generate, and commit the result. -import {DEBUG as DEBUG_WEBGL} from '../../../webgl.js'; +import {DEBUG as DEBUG_WEBGL} from '../../../../webgl.js'; /** * @constructor @@ -12,43 +12,48 @@ import {DEBUG as DEBUG_WEBGL} from '../../../webgl.js'; * @param {WebGLProgram} program Program. * @struct */ -const Locations = function(gl, program) { +class Locations { - /** - * @type {WebGLUniformLocation} - */ - this.u_texCoordMatrix = gl.getUniformLocation( - program, DEBUG_WEBGL ? 'u_texCoordMatrix' : 'd'); + constructor(gl, program) { - /** - * @type {WebGLUniformLocation} - */ - this.u_projectionMatrix = gl.getUniformLocation( - program, DEBUG_WEBGL ? 'u_projectionMatrix' : 'e'); + /** + * @type {WebGLUniformLocation} + */ + this.u_texCoordMatrix = gl.getUniformLocation( + program, DEBUG_WEBGL ? 'u_texCoordMatrix' : 'd'); - /** - * @type {WebGLUniformLocation} - */ - this.u_opacity = gl.getUniformLocation( - program, DEBUG_WEBGL ? 'u_opacity' : 'f'); + /** + * @type {WebGLUniformLocation} + */ + this.u_projectionMatrix = gl.getUniformLocation( + program, DEBUG_WEBGL ? 'u_projectionMatrix' : 'e'); - /** - * @type {WebGLUniformLocation} - */ - this.u_texture = gl.getUniformLocation( - program, DEBUG_WEBGL ? 'u_texture' : 'g'); + /** + * @type {WebGLUniformLocation} + */ + this.u_opacity = gl.getUniformLocation( + program, DEBUG_WEBGL ? 'u_opacity' : 'f'); - /** - * @type {number} - */ - this.a_position = gl.getAttribLocation( - program, DEBUG_WEBGL ? 'a_position' : 'b'); + /** + * @type {WebGLUniformLocation} + */ + this.u_texture = gl.getUniformLocation( + program, DEBUG_WEBGL ? 'u_texture' : 'g'); - /** - * @type {number} - */ - this.a_texCoord = gl.getAttribLocation( - program, DEBUG_WEBGL ? 'a_texCoord' : 'c'); -}; + /** + * @type {number} + */ + this.a_position = gl.getAttribLocation( + program, DEBUG_WEBGL ? 'a_position' : 'b'); + + /** + * @type {number} + */ + this.a_texCoord = gl.getAttribLocation( + program, DEBUG_WEBGL ? 'a_texCoord' : 'c'); + + } + +} export default Locations; diff --git a/src/ol/renderer/webgl/tilelayershader/Locations.js b/src/ol/renderer/webgl/tilelayershader/Locations.js index 636f0d0fd2..8560c438e2 100644 --- a/src/ol/renderer/webgl/tilelayershader/Locations.js +++ b/src/ol/renderer/webgl/tilelayershader/Locations.js @@ -4,7 +4,7 @@ // This file is automatically generated, do not edit // Run `make shaders` to generate, and commit the result. -import {DEBUG as DEBUG_WEBGL} from '../../../webgl.js'; +import {DEBUG as DEBUG_WEBGL} from '../../../../webgl.js'; /** * @constructor @@ -12,31 +12,36 @@ import {DEBUG as DEBUG_WEBGL} from '../../../webgl.js'; * @param {WebGLProgram} program Program. * @struct */ -const Locations = function(gl, program) { +class Locations { - /** - * @type {WebGLUniformLocation} - */ - this.u_tileOffset = gl.getUniformLocation( - program, DEBUG_WEBGL ? 'u_tileOffset' : 'd'); + constructor(gl, program) { - /** - * @type {WebGLUniformLocation} - */ - this.u_texture = gl.getUniformLocation( - program, DEBUG_WEBGL ? 'u_texture' : 'e'); + /** + * @type {WebGLUniformLocation} + */ + this.u_tileOffset = gl.getUniformLocation( + program, DEBUG_WEBGL ? 'u_tileOffset' : 'd'); - /** - * @type {number} - */ - this.a_position = gl.getAttribLocation( - program, DEBUG_WEBGL ? 'a_position' : 'b'); + /** + * @type {WebGLUniformLocation} + */ + this.u_texture = gl.getUniformLocation( + program, DEBUG_WEBGL ? 'u_texture' : 'e'); - /** - * @type {number} - */ - this.a_texCoord = gl.getAttribLocation( - program, DEBUG_WEBGL ? 'a_texCoord' : 'c'); -}; + /** + * @type {number} + */ + this.a_position = gl.getAttribLocation( + program, DEBUG_WEBGL ? 'a_position' : 'b'); + + /** + * @type {number} + */ + this.a_texCoord = gl.getAttribLocation( + program, DEBUG_WEBGL ? 'a_texCoord' : 'c'); + + } + +} export default Locations; diff --git a/src/ol/source/Image.js b/src/ol/source/Image.js index d5bcea2eb7..2d778d5eb5 100644 --- a/src/ol/source/Image.js +++ b/src/ol/source/Image.js @@ -51,18 +51,23 @@ const ImageSourceEventType = { * @param {string} type Type. * @param {module:ol/Image} image The image. */ -const ImageSourceEvent = function(type, image) { +class ImageSourceEvent { - Event.call(this, type); + constructor(type, image) { - /** - * The image related to the event. - * @type {module:ol/Image} - * @api - */ - this.image = image; + Event.call(this, type); + + /** + * The image related to the event. + * @type {module:ol/Image} + * @api + */ + this.image = image; + + } + +} -}; inherits(ImageSourceEvent, Event); diff --git a/src/ol/source/OSM.js b/src/ol/source/OSM.js index d46b6d1f25..e7661b3306 100644 --- a/src/ol/source/OSM.js +++ b/src/ol/source/OSM.js @@ -50,36 +50,40 @@ export const ATTRIBUTION = '© ' + * @param {module:ol/source/OSM~Options=} [opt_options] Open Street Map options. * @api */ -const OSM = function(opt_options) { +class OSM { - const options = opt_options || {}; + constructor(opt_options) { + + const options = opt_options || {}; + + let attributions; + if (options.attributions !== undefined) { + attributions = options.attributions; + } else { + attributions = [ATTRIBUTION]; + } + + const crossOrigin = options.crossOrigin !== undefined ? + options.crossOrigin : 'anonymous'; + + const url = options.url !== undefined ? + options.url : 'https://{a-c}.tile.openstreetmap.org/{z}/{x}/{y}.png'; + + XYZ.call(this, { + attributions: attributions, + cacheSize: options.cacheSize, + crossOrigin: crossOrigin, + opaque: options.opaque !== undefined ? options.opaque : true, + maxZoom: options.maxZoom !== undefined ? options.maxZoom : 19, + reprojectionErrorThreshold: options.reprojectionErrorThreshold, + tileLoadFunction: options.tileLoadFunction, + url: url, + wrapX: options.wrapX + }); - let attributions; - if (options.attributions !== undefined) { - attributions = options.attributions; - } else { - attributions = [ATTRIBUTION]; } - const crossOrigin = options.crossOrigin !== undefined ? - options.crossOrigin : 'anonymous'; - - const url = options.url !== undefined ? - options.url : 'https://{a-c}.tile.openstreetmap.org/{z}/{x}/{y}.png'; - - XYZ.call(this, { - attributions: attributions, - cacheSize: options.cacheSize, - crossOrigin: crossOrigin, - opaque: options.opaque !== undefined ? options.opaque : true, - maxZoom: options.maxZoom !== undefined ? options.maxZoom : 19, - reprojectionErrorThreshold: options.reprojectionErrorThreshold, - tileLoadFunction: options.tileLoadFunction, - url: url, - wrapX: options.wrapX - }); - -}; +} inherits(OSM, XYZ); diff --git a/src/ol/source/Raster.js b/src/ol/source/Raster.js index 74a76700af..c7b6240c9c 100644 --- a/src/ol/source/Raster.js +++ b/src/ol/source/Raster.js @@ -83,32 +83,37 @@ const RasterOperationType = { * @param {module:ol/PluggableMap~FrameState} frameState The frame state. * @param {Object} data An object made available to operations. */ -const RasterSourceEvent = function(type, frameState, data) { - Event.call(this, type); +class RasterSourceEvent { - /** - * The raster extent. - * @type {module:ol/extent~Extent} - * @api - */ - this.extent = frameState.extent; + constructor(type, frameState, data) { + Event.call(this, type); - /** - * The pixel resolution (map units per pixel). - * @type {number} - * @api - */ - this.resolution = frameState.viewState.resolution / frameState.pixelRatio; + /** + * The raster extent. + * @type {module:ol/extent~Extent} + * @api + */ + this.extent = frameState.extent; - /** - * An object made available to all operations. This can be used by operations - * as a storage object (e.g. for calculating statistics). - * @type {Object} - * @api - */ - this.data = data; + /** + * The pixel resolution (map units per pixel). + * @type {number} + * @api + */ + this.resolution = frameState.viewState.resolution / frameState.pixelRatio; + + /** + * An object made available to all operations. This can be used by operations + * as a storage object (e.g. for calculating statistics). + * @type {Object} + * @api + */ + this.data = data; + + } + +} -}; inherits(RasterSourceEvent, Event); /** diff --git a/src/ol/source/Stamen.js b/src/ol/source/Stamen.js index ff907d8602..bfac063cd9 100644 --- a/src/ol/source/Stamen.js +++ b/src/ol/source/Stamen.js @@ -118,30 +118,35 @@ const ProviderConfig = { * @param {module:ol/source/Stamen~Options=} options Stamen options. * @api */ -const Stamen = function(options) { - const i = options.layer.indexOf('-'); - const provider = i == -1 ? options.layer : options.layer.slice(0, i); - const providerConfig = ProviderConfig[provider]; +class Stamen { - const layerConfig = LayerConfig[options.layer]; + constructor(options) { + const i = options.layer.indexOf('-'); + const provider = i == -1 ? options.layer : options.layer.slice(0, i); + const providerConfig = ProviderConfig[provider]; - const url = options.url !== undefined ? options.url : - 'https://stamen-tiles-{a-d}.a.ssl.fastly.net/' + options.layer + - '/{z}/{x}/{y}.' + layerConfig.extension; + const layerConfig = LayerConfig[options.layer]; - XYZ.call(this, { - attributions: ATTRIBUTIONS, - cacheSize: options.cacheSize, - crossOrigin: 'anonymous', - maxZoom: options.maxZoom != undefined ? options.maxZoom : providerConfig.maxZoom, - minZoom: options.minZoom != undefined ? options.minZoom : providerConfig.minZoom, - opaque: layerConfig.opaque, - reprojectionErrorThreshold: options.reprojectionErrorThreshold, - tileLoadFunction: options.tileLoadFunction, - url: url, - wrapX: options.wrapX - }); -}; + const url = options.url !== undefined ? options.url : + 'https://stamen-tiles-{a-d}.a.ssl.fastly.net/' + options.layer + + '/{z}/{x}/{y}.' + layerConfig.extension; + + XYZ.call(this, { + attributions: ATTRIBUTIONS, + cacheSize: options.cacheSize, + crossOrigin: 'anonymous', + maxZoom: options.maxZoom != undefined ? options.maxZoom : providerConfig.maxZoom, + minZoom: options.minZoom != undefined ? options.minZoom : providerConfig.minZoom, + opaque: layerConfig.opaque, + reprojectionErrorThreshold: options.reprojectionErrorThreshold, + tileLoadFunction: options.tileLoadFunction, + url: url, + wrapX: options.wrapX + }); + + } + +} inherits(Stamen, XYZ); diff --git a/src/ol/source/Tile.js b/src/ol/source/Tile.js index 7435278073..9c8854b89e 100644 --- a/src/ol/source/Tile.js +++ b/src/ol/source/Tile.js @@ -318,18 +318,23 @@ TileSource.prototype.useTile = UNDEFINED; * @param {string} type Type. * @param {module:ol/Tile} tile The tile. */ -export const TileSourceEvent = function(type, tile) { +export class TileSourceEvent { - Event.call(this, type); + constructor(type, tile) { - /** - * The tile related to the event. - * @type {module:ol/Tile} - * @api - */ - this.tile = tile; + Event.call(this, type); + + /** + * The tile related to the event. + * @type {module:ol/Tile} + * @api + */ + this.tile = tile; + + } + +} -}; inherits(TileSourceEvent, Event); export default TileSource; diff --git a/src/ol/source/UTFGrid.js b/src/ol/source/UTFGrid.js index 854acd8639..a90baa5b6b 100644 --- a/src/ol/source/UTFGrid.js +++ b/src/ol/source/UTFGrid.js @@ -27,54 +27,59 @@ import {createXYZ, extentFromProjection} from '../tilegrid.js'; * @param {boolean} preemptive Load the tile when visible (before it's needed). * @param {boolean} jsonp Load the tile as a script. */ -export const CustomTile = function(tileCoord, state, src, extent, preemptive, jsonp) { +export class CustomTile { - Tile.call(this, tileCoord, state); + constructor(tileCoord, state, src, extent, preemptive, jsonp) { - /** - * @private - * @type {string} - */ - this.src_ = src; + Tile.call(this, tileCoord, state); - /** - * @private - * @type {module:ol/extent~Extent} - */ - this.extent_ = extent; + /** + * @private + * @type {string} + */ + this.src_ = src; - /** - * @private - * @type {boolean} - */ - this.preemptive_ = preemptive; + /** + * @private + * @type {module:ol/extent~Extent} + */ + this.extent_ = extent; - /** - * @private - * @type {Array.} - */ - this.grid_ = null; + /** + * @private + * @type {boolean} + */ + this.preemptive_ = preemptive; - /** - * @private - * @type {Array.} - */ - this.keys_ = null; + /** + * @private + * @type {Array.} + */ + this.grid_ = null; - /** - * @private - * @type {Object.|undefined} - */ - this.data_ = null; + /** + * @private + * @type {Array.} + */ + this.keys_ = null; + + /** + * @private + * @type {Object.|undefined} + */ + this.data_ = null; - /** - * @private - * @type {boolean} - */ - this.jsonp_ = jsonp; + /** + * @private + * @type {boolean} + */ + this.jsonp_ = jsonp; + + } + +} -}; inherits(CustomTile, Tile); @@ -275,54 +280,59 @@ CustomTile.prototype.load = function() { * @param {module:ol/source/UTFGrid~Options=} options Source options. * @api */ -const UTFGrid = function(options) { - TileSource.call(this, { - projection: getProjection('EPSG:3857'), - state: SourceState.LOADING - }); +class UTFGrid { - /** - * @private - * @type {boolean} - */ - this.preemptive_ = options.preemptive !== undefined ? - options.preemptive : true; + constructor(options) { + TileSource.call(this, { + projection: getProjection('EPSG:3857'), + state: SourceState.LOADING + }); - /** - * @private - * @type {!module:ol/Tile~UrlFunction} - */ - this.tileUrlFunction_ = nullTileUrlFunction; + /** + * @private + * @type {boolean} + */ + this.preemptive_ = options.preemptive !== undefined ? + options.preemptive : true; - /** - * @private - * @type {string|undefined} - */ - this.template_ = undefined; + /** + * @private + * @type {!module:ol/Tile~UrlFunction} + */ + this.tileUrlFunction_ = nullTileUrlFunction; - /** - * @private - * @type {boolean} - */ - this.jsonp_ = options.jsonp || false; + /** + * @private + * @type {string|undefined} + */ + this.template_ = undefined; - if (options.url) { - if (this.jsonp_) { - requestJSONP(options.url, this.handleTileJSONResponse.bind(this), - this.handleTileJSONError.bind(this)); + /** + * @private + * @type {boolean} + */ + this.jsonp_ = options.jsonp || false; + + if (options.url) { + if (this.jsonp_) { + requestJSONP(options.url, this.handleTileJSONResponse.bind(this), + this.handleTileJSONError.bind(this)); + } else { + const client = new XMLHttpRequest(); + client.addEventListener('load', this.onXHRLoad_.bind(this)); + client.addEventListener('error', this.onXHRError_.bind(this)); + client.open('GET', options.url); + client.send(); + } + } else if (options.tileJSON) { + this.handleTileJSONResponse(options.tileJSON); } else { - const client = new XMLHttpRequest(); - client.addEventListener('load', this.onXHRLoad_.bind(this)); - client.addEventListener('error', this.onXHRError_.bind(this)); - client.open('GET', options.url); - client.send(); + assert(false, 51); // Either `url` or `tileJSON` options must be provided } - } else if (options.tileJSON) { - this.handleTileJSONResponse(options.tileJSON); - } else { - assert(false, 51); // Either `url` or `tileJSON` options must be provided + } -}; + +} inherits(UTFGrid, TileSource); diff --git a/src/ol/source/Vector.js b/src/ol/source/Vector.js index 10fdb1226a..7b8fe888b9 100644 --- a/src/ol/source/Vector.js +++ b/src/ol/source/Vector.js @@ -41,18 +41,23 @@ import RBush from '../structs/RBush.js'; * @param {string} type Type. * @param {module:ol/Feature=} opt_feature Feature. */ -export const VectorSourceEvent = function(type, opt_feature) { +export class VectorSourceEvent { - Event.call(this, type); + constructor(type, opt_feature) { - /** - * The feature being added or removed. - * @type {module:ol/Feature|undefined} - * @api - */ - this.feature = opt_feature; + Event.call(this, type); + + /** + * The feature being added or removed. + * @type {module:ol/Feature|undefined} + * @api + */ + this.feature = opt_feature; + + } + +} -}; inherits(VectorSourceEvent, Event); @@ -158,120 +163,124 @@ inherits(VectorSourceEvent, Event); * @param {module:ol/source/Vector~Options=} opt_options Vector source options. * @api */ -const VectorSource = function(opt_options) { +class VectorSource { - const options = opt_options || {}; + constructor(opt_options) { - Source.call(this, { - attributions: options.attributions, - projection: undefined, - state: SourceState.READY, - wrapX: options.wrapX !== undefined ? options.wrapX : true - }); + const options = opt_options || {}; - /** - * @private - * @type {module:ol/featureloader~FeatureLoader} - */ - this.loader_ = UNDEFINED; + Source.call(this, { + attributions: options.attributions, + projection: undefined, + state: SourceState.READY, + wrapX: options.wrapX !== undefined ? options.wrapX : true + }); - /** - * @private - * @type {module:ol/format/Feature|undefined} - */ - this.format_ = options.format; + /** + * @private + * @type {module:ol/featureloader~FeatureLoader} + */ + this.loader_ = UNDEFINED; - /** - * @private - * @type {boolean} - */ - this.overlaps_ = options.overlaps == undefined ? true : options.overlaps; + /** + * @private + * @type {module:ol/format/Feature|undefined} + */ + this.format_ = options.format; - /** - * @private - * @type {string|module:ol/featureloader~FeatureUrlFunction|undefined} - */ - this.url_ = options.url; + /** + * @private + * @type {boolean} + */ + this.overlaps_ = options.overlaps == undefined ? true : options.overlaps; + + /** + * @private + * @type {string|module:ol/featureloader~FeatureUrlFunction|undefined} + */ + this.url_ = options.url; + + if (options.loader !== undefined) { + this.loader_ = options.loader; + } else if (this.url_ !== undefined) { + assert(this.format_, 7); // `format` must be set when `url` is set + // create a XHR feature loader for "url" and "format" + this.loader_ = xhr(this.url_, /** @type {module:ol/format/Feature} */ (this.format_)); + } + + /** + * @private + * @type {module:ol/source/Vector~LoadingStrategy} + */ + this.strategy_ = options.strategy !== undefined ? options.strategy : allStrategy; + + const useSpatialIndex = + options.useSpatialIndex !== undefined ? options.useSpatialIndex : true; + + /** + * @private + * @type {module:ol/structs/RBush.} + */ + this.featuresRtree_ = useSpatialIndex ? new RBush() : null; + + /** + * @private + * @type {module:ol/structs/RBush.<{extent: module:ol/extent~Extent}>} + */ + this.loadedExtentsRtree_ = new RBush(); + + /** + * @private + * @type {!Object.} + */ + this.nullGeometryFeatures_ = {}; + + /** + * A lookup of features by id (the return from feature.getId()). + * @private + * @type {!Object.} + */ + this.idIndex_ = {}; + + /** + * A lookup of features without id (keyed by getUid(feature)). + * @private + * @type {!Object.} + */ + this.undefIdIndex_ = {}; + + /** + * @private + * @type {Object.>} + */ + this.featureChangeKeys_ = {}; + + /** + * @private + * @type {module:ol/Collection.} + */ + this.featuresCollection_ = null; + + let collection, features; + if (options.features instanceof Collection) { + collection = options.features; + features = collection.getArray(); + } else if (Array.isArray(options.features)) { + features = options.features; + } + if (!useSpatialIndex && collection === undefined) { + collection = new Collection(features); + } + if (features !== undefined) { + this.addFeaturesInternal(features); + } + if (collection !== undefined) { + this.bindFeaturesCollection_(collection); + } - if (options.loader !== undefined) { - this.loader_ = options.loader; - } else if (this.url_ !== undefined) { - assert(this.format_, 7); // `format` must be set when `url` is set - // create a XHR feature loader for "url" and "format" - this.loader_ = xhr(this.url_, /** @type {module:ol/format/Feature} */ (this.format_)); } - /** - * @private - * @type {module:ol/source/Vector~LoadingStrategy} - */ - this.strategy_ = options.strategy !== undefined ? options.strategy : allStrategy; - - const useSpatialIndex = - options.useSpatialIndex !== undefined ? options.useSpatialIndex : true; - - /** - * @private - * @type {module:ol/structs/RBush.} - */ - this.featuresRtree_ = useSpatialIndex ? new RBush() : null; - - /** - * @private - * @type {module:ol/structs/RBush.<{extent: module:ol/extent~Extent}>} - */ - this.loadedExtentsRtree_ = new RBush(); - - /** - * @private - * @type {!Object.} - */ - this.nullGeometryFeatures_ = {}; - - /** - * A lookup of features by id (the return from feature.getId()). - * @private - * @type {!Object.} - */ - this.idIndex_ = {}; - - /** - * A lookup of features without id (keyed by getUid(feature)). - * @private - * @type {!Object.} - */ - this.undefIdIndex_ = {}; - - /** - * @private - * @type {Object.>} - */ - this.featureChangeKeys_ = {}; - - /** - * @private - * @type {module:ol/Collection.} - */ - this.featuresCollection_ = null; - - let collection, features; - if (options.features instanceof Collection) { - collection = options.features; - features = collection.getArray(); - } else if (Array.isArray(options.features)) { - features = options.features; - } - if (!useSpatialIndex && collection === undefined) { - collection = new Collection(features); - } - if (features !== undefined) { - this.addFeaturesInternal(features); - } - if (collection !== undefined) { - this.bindFeaturesCollection_(collection); - } - -}; +} inherits(VectorSource, Source); diff --git a/src/ol/source/VectorTile.js b/src/ol/source/VectorTile.js index 2e088a31b8..a38ae74645 100644 --- a/src/ol/source/VectorTile.js +++ b/src/ol/source/VectorTile.js @@ -74,66 +74,70 @@ import {createXYZ, extentFromProjection, createForProjection} from '../tilegrid. * @param {module:ol/source/VectorTile~Options=} options Vector tile options. * @api */ -const VectorTile = function(options) { - const projection = options.projection || 'EPSG:3857'; +class VectorTile { - const extent = options.extent || extentFromProjection(projection); + constructor(options) { + const projection = options.projection || 'EPSG:3857'; - const tileGrid = options.tileGrid || createXYZ({ - extent: extent, - maxZoom: options.maxZoom || 22, - minZoom: options.minZoom, - tileSize: options.tileSize || 512 - }); + const extent = options.extent || extentFromProjection(projection); - UrlTile.call(this, { - attributions: options.attributions, - cacheSize: options.cacheSize !== undefined ? options.cacheSize : 128, - extent: extent, - opaque: false, - projection: projection, - state: options.state, - tileGrid: tileGrid, - tileLoadFunction: options.tileLoadFunction ? options.tileLoadFunction : defaultLoadFunction, - tileUrlFunction: options.tileUrlFunction, - url: options.url, - urls: options.urls, - wrapX: options.wrapX === undefined ? true : options.wrapX, - transition: options.transition - }); + const tileGrid = options.tileGrid || createXYZ({ + extent: extent, + maxZoom: options.maxZoom || 22, + minZoom: options.minZoom, + tileSize: options.tileSize || 512 + }); - /** - * @private - * @type {module:ol/format/Feature} - */ - this.format_ = options.format ? options.format : null; + UrlTile.call(this, { + attributions: options.attributions, + cacheSize: options.cacheSize !== undefined ? options.cacheSize : 128, + extent: extent, + opaque: false, + projection: projection, + state: options.state, + tileGrid: tileGrid, + tileLoadFunction: options.tileLoadFunction ? options.tileLoadFunction : defaultLoadFunction, + tileUrlFunction: options.tileUrlFunction, + url: options.url, + urls: options.urls, + wrapX: options.wrapX === undefined ? true : options.wrapX, + transition: options.transition + }); - /** + /** * @private - * @type {Object.} + * @type {module:ol/format/Feature} */ - this.sourceTiles_ = {}; + this.format_ = options.format ? options.format : null; - /** - * @private - * @type {boolean} - */ - this.overlaps_ = options.overlaps == undefined ? true : options.overlaps; + /** + * @private + * @type {Object.} + */ + this.sourceTiles_ = {}; - /** - * @protected - * @type {function(new: module:ol/VectorTile, module:ol/tilecoord~TileCoord, module:ol/TileState, string, - * module:ol/format/Feature, module:ol/Tile~LoadFunction)} + /** + * @private + * @type {boolean} */ - this.tileClass = options.tileClass ? options.tileClass : Tile; + this.overlaps_ = options.overlaps == undefined ? true : options.overlaps; - /** - * @private - * @type {Object.} - */ - this.tileGrids_ = {}; + /** + * @protected + * @type {function(new: module:ol/VectorTile, module:ol/tilecoord~TileCoord, module:ol/TileState, string, + * module:ol/format/Feature, module:ol/Tile~LoadFunction)} + */ + this.tileClass = options.tileClass ? options.tileClass : Tile; -}; + /** + * @private + * @type {Object.} + */ + this.tileGrids_ = {}; + + } + +} inherits(VectorTile, UrlTile); diff --git a/src/ol/source/WMTS.js b/src/ol/source/WMTS.js index 61579c0ed7..7a4a0c99a5 100644 --- a/src/ol/source/WMTS.js +++ b/src/ol/source/WMTS.js @@ -65,157 +65,161 @@ import {appendParams} from '../uri.js'; * @param {module:ol/source/WMTS~Options=} options WMTS options. * @api */ -const WMTS = function(options) { +class WMTS { - // TODO: add support for TileMatrixLimits + constructor(options) { - /** - * @private - * @type {string} - */ - this.version_ = options.version !== undefined ? options.version : '1.0.0'; + // TODO: add support for TileMatrixLimits - /** - * @private - * @type {string} - */ - this.format_ = options.format !== undefined ? options.format : 'image/jpeg'; + /** + * @private + * @type {string} + */ + this.version_ = options.version !== undefined ? options.version : '1.0.0'; - /** - * @private - * @type {!Object} - */ - this.dimensions_ = options.dimensions !== undefined ? options.dimensions : {}; + /** + * @private + * @type {string} + */ + this.format_ = options.format !== undefined ? options.format : 'image/jpeg'; - /** - * @private - * @type {string} - */ - this.layer_ = options.layer; + /** + * @private + * @type {!Object} + */ + this.dimensions_ = options.dimensions !== undefined ? options.dimensions : {}; - /** - * @private - * @type {string} - */ - this.matrixSet_ = options.matrixSet; + /** + * @private + * @type {string} + */ + this.layer_ = options.layer; - /** - * @private - * @type {string} - */ - this.style_ = options.style; + /** + * @private + * @type {string} + */ + this.matrixSet_ = options.matrixSet; - let urls = options.urls; - if (urls === undefined && options.url !== undefined) { - urls = expandUrl(options.url); - } + /** + * @private + * @type {string} + */ + this.style_ = options.style; - // FIXME: should we guess this requestEncoding from options.url(s) - // structure? that would mean KVP only if a template is not provided. + let urls = options.urls; + if (urls === undefined && options.url !== undefined) { + urls = expandUrl(options.url); + } - /** - * @private - * @type {module:ol/source/WMTSRequestEncoding} - */ - this.requestEncoding_ = options.requestEncoding !== undefined ? - /** @type {module:ol/source/WMTSRequestEncoding} */ (options.requestEncoding) : - WMTSRequestEncoding.KVP; + // FIXME: should we guess this requestEncoding from options.url(s) + // structure? that would mean KVP only if a template is not provided. - const requestEncoding = this.requestEncoding_; + /** + * @private + * @type {module:ol/source/WMTSRequestEncoding} + */ + this.requestEncoding_ = options.requestEncoding !== undefined ? + /** @type {module:ol/source/WMTSRequestEncoding} */ (options.requestEncoding) : + WMTSRequestEncoding.KVP; - // FIXME: should we create a default tileGrid? - // we could issue a getCapabilities xhr to retrieve missing configuration - const tileGrid = options.tileGrid; + const requestEncoding = this.requestEncoding_; - // context property names are lower case to allow for a case insensitive - // replacement as some services use different naming conventions - const context = { - 'layer': this.layer_, - 'style': this.style_, - 'tilematrixset': this.matrixSet_ - }; + // FIXME: should we create a default tileGrid? + // we could issue a getCapabilities xhr to retrieve missing configuration + const tileGrid = options.tileGrid; - if (requestEncoding == WMTSRequestEncoding.KVP) { - assign(context, { - 'Service': 'WMTS', - 'Request': 'GetTile', - 'Version': this.version_, - 'Format': this.format_ - }); - } + // context property names are lower case to allow for a case insensitive + // replacement as some services use different naming conventions + const context = { + 'layer': this.layer_, + 'style': this.style_, + 'tilematrixset': this.matrixSet_ + }; - const dimensions = this.dimensions_; - - /** - * @param {string} template Template. - * @return {module:ol/Tile~UrlFunction} Tile URL function. - * @private - */ - this.createFromWMTSTemplate_ = function(template) { - - // TODO: we may want to create our own appendParams function so that params - // order conforms to wmts spec guidance, and so that we can avoid to escape - // special template params - - template = (requestEncoding == WMTSRequestEncoding.KVP) ? - appendParams(template, context) : - template.replace(/\{(\w+?)\}/g, function(m, p) { - return (p.toLowerCase() in context) ? context[p.toLowerCase()] : m; + if (requestEncoding == WMTSRequestEncoding.KVP) { + assign(context, { + 'Service': 'WMTS', + 'Request': 'GetTile', + 'Version': this.version_, + 'Format': this.format_ }); + } - return ( - /** - * @param {module:ol/tilecoord~TileCoord} tileCoord Tile coordinate. - * @param {number} pixelRatio Pixel ratio. - * @param {module:ol/proj/Projection} projection Projection. - * @return {string|undefined} Tile URL. - */ - function(tileCoord, pixelRatio, projection) { - if (!tileCoord) { - return undefined; - } else { - const localContext = { - 'TileMatrix': tileGrid.getMatrixId(tileCoord[0]), - 'TileCol': tileCoord[1], - 'TileRow': -tileCoord[2] - 1 - }; - assign(localContext, dimensions); - let url = template; - if (requestEncoding == WMTSRequestEncoding.KVP) { - url = appendParams(url, localContext); + const dimensions = this.dimensions_; + + /** + * @param {string} template Template. + * @return {module:ol/Tile~UrlFunction} Tile URL function. + * @private + */ + this.createFromWMTSTemplate_ = function(template) { + + // TODO: we may want to create our own appendParams function so that params + // order conforms to wmts spec guidance, and so that we can avoid to escape + // special template params + + template = (requestEncoding == WMTSRequestEncoding.KVP) ? + appendParams(template, context) : + template.replace(/\{(\w+?)\}/g, function(m, p) { + return (p.toLowerCase() in context) ? context[p.toLowerCase()] : m; + }); + + return ( + /** + * @param {module:ol/tilecoord~TileCoord} tileCoord Tile coordinate. + * @param {number} pixelRatio Pixel ratio. + * @param {module:ol/proj/Projection} projection Projection. + * @return {string|undefined} Tile URL. + */ + function(tileCoord, pixelRatio, projection) { + if (!tileCoord) { + return undefined; } else { - url = url.replace(/\{(\w+?)\}/g, function(m, p) { - return localContext[p]; - }); + const localContext = { + 'TileMatrix': tileGrid.getMatrixId(tileCoord[0]), + 'TileCol': tileCoord[1], + 'TileRow': -tileCoord[2] - 1 + }; + assign(localContext, dimensions); + let url = template; + if (requestEncoding == WMTSRequestEncoding.KVP) { + url = appendParams(url, localContext); + } else { + url = url.replace(/\{(\w+?)\}/g, function(m, p) { + return localContext[p]; + }); + } + return url; } - return url; } - } - ); - }; + ); + }; - const tileUrlFunction = (urls && urls.length > 0) ? - createFromTileUrlFunctions(urls.map(this.createFromWMTSTemplate_)) : nullTileUrlFunction; + const tileUrlFunction = (urls && urls.length > 0) ? + createFromTileUrlFunctions(urls.map(this.createFromWMTSTemplate_)) : nullTileUrlFunction; - TileImage.call(this, { - attributions: options.attributions, - cacheSize: options.cacheSize, - crossOrigin: options.crossOrigin, - projection: options.projection, - reprojectionErrorThreshold: options.reprojectionErrorThreshold, - tileClass: options.tileClass, - tileGrid: tileGrid, - tileLoadFunction: options.tileLoadFunction, - tilePixelRatio: options.tilePixelRatio, - tileUrlFunction: tileUrlFunction, - urls: urls, - wrapX: options.wrapX !== undefined ? options.wrapX : false, - transition: options.transition - }); + TileImage.call(this, { + attributions: options.attributions, + cacheSize: options.cacheSize, + crossOrigin: options.crossOrigin, + projection: options.projection, + reprojectionErrorThreshold: options.reprojectionErrorThreshold, + tileClass: options.tileClass, + tileGrid: tileGrid, + tileLoadFunction: options.tileLoadFunction, + tilePixelRatio: options.tilePixelRatio, + tileUrlFunction: tileUrlFunction, + urls: urls, + wrapX: options.wrapX !== undefined ? options.wrapX : false, + transition: options.transition + }); - this.setKey(this.getKeyForDimensions_()); + this.setKey(this.getKeyForDimensions_()); -}; + } + +} inherits(WMTS, TileImage); diff --git a/src/ol/source/XYZ.js b/src/ol/source/XYZ.js index a0212b9ec6..6f2b90996c 100644 --- a/src/ol/source/XYZ.js +++ b/src/ol/source/XYZ.js @@ -66,37 +66,42 @@ import {createXYZ, extentFromProjection} from '../tilegrid.js'; * @param {module:ol/source/XYZ~Options=} opt_options XYZ options. * @api */ -const XYZ = function(opt_options) { - const options = opt_options || {}; - const projection = options.projection !== undefined ? - options.projection : 'EPSG:3857'; +class XYZ { - const tileGrid = options.tileGrid !== undefined ? options.tileGrid : - createXYZ({ - extent: extentFromProjection(projection), - maxZoom: options.maxZoom, - minZoom: options.minZoom, - tileSize: options.tileSize + constructor(opt_options) { + const options = opt_options || {}; + const projection = options.projection !== undefined ? + options.projection : 'EPSG:3857'; + + const tileGrid = options.tileGrid !== undefined ? options.tileGrid : + createXYZ({ + extent: extentFromProjection(projection), + maxZoom: options.maxZoom, + minZoom: options.minZoom, + tileSize: options.tileSize + }); + + TileImage.call(this, { + attributions: options.attributions, + cacheSize: options.cacheSize, + crossOrigin: options.crossOrigin, + opaque: options.opaque, + projection: projection, + reprojectionErrorThreshold: options.reprojectionErrorThreshold, + tileGrid: tileGrid, + tileLoadFunction: options.tileLoadFunction, + tilePixelRatio: options.tilePixelRatio, + tileUrlFunction: options.tileUrlFunction, + url: options.url, + urls: options.urls, + wrapX: options.wrapX !== undefined ? options.wrapX : true, + transition: options.transition }); - TileImage.call(this, { - attributions: options.attributions, - cacheSize: options.cacheSize, - crossOrigin: options.crossOrigin, - opaque: options.opaque, - projection: projection, - reprojectionErrorThreshold: options.reprojectionErrorThreshold, - tileGrid: tileGrid, - tileLoadFunction: options.tileLoadFunction, - tilePixelRatio: options.tilePixelRatio, - tileUrlFunction: options.tileUrlFunction, - url: options.url, - urls: options.urls, - wrapX: options.wrapX !== undefined ? options.wrapX : true, - transition: options.transition - }); + } -}; +} inherits(XYZ, TileImage); + export default XYZ; diff --git a/src/ol/source/Zoomify.js b/src/ol/source/Zoomify.js index d7ef97180b..6917422764 100644 --- a/src/ol/source/Zoomify.js +++ b/src/ol/source/Zoomify.js @@ -34,23 +34,28 @@ const TierSizeCalculation = { * @param {module:ol/Tile~LoadFunction} tileLoadFunction Tile load function. * @param {module:ol/Tile~Options=} opt_options Tile options. */ -export const CustomTile = function( - tileGrid, tileCoord, state, src, crossOrigin, tileLoadFunction, opt_options) { +export class CustomTile { - ImageTile.call(this, tileCoord, state, src, crossOrigin, tileLoadFunction, opt_options); + constructor(tileGrid, tileCoord, state, src, crossOrigin, tileLoadFunction, opt_options) { - /** - * @private - * @type {HTMLCanvasElement|HTMLImageElement|HTMLVideoElement} - */ - this.zoomifyImage_ = null; + ImageTile.call(this, tileCoord, state, src, crossOrigin, tileLoadFunction, opt_options); + + /** + * @private + * @type {HTMLCanvasElement|HTMLImageElement|HTMLVideoElement} + */ + this.zoomifyImage_ = null; + + /** + * @private + * @type {module:ol/size~Size} + */ + this.tileSize_ = toSize(tileGrid.getTileSize(tileCoord[0])); + + } + +} - /** - * @private - * @type {module:ol/size~Size} - */ - this.tileSize_ = toSize(tileGrid.getTileSize(tileCoord[0])); -}; inherits(CustomTile, ImageTile); @@ -124,135 +129,138 @@ CustomTile.prototype.getImage = function() { * @param {module:ol/source/Zoomify~Options=} opt_options Options. * @api */ -const Zoomify = function(opt_options) { +class Zoomify { - const options = opt_options || {}; + constructor(opt_options) { - const size = options.size; - const tierSizeCalculation = options.tierSizeCalculation !== undefined ? - options.tierSizeCalculation : - TierSizeCalculation.DEFAULT; + const options = opt_options || {}; - const imageWidth = size[0]; - const imageHeight = size[1]; - const extent = options.extent || [0, -size[1], size[0], 0]; - const tierSizeInTiles = []; - const tileSize = options.tileSize || DEFAULT_TILE_SIZE; - let tileSizeForTierSizeCalculation = tileSize; + const size = options.size; + const tierSizeCalculation = options.tierSizeCalculation !== undefined ? + options.tierSizeCalculation : + TierSizeCalculation.DEFAULT; - switch (tierSizeCalculation) { - case TierSizeCalculation.DEFAULT: - while (imageWidth > tileSizeForTierSizeCalculation || imageHeight > tileSizeForTierSizeCalculation) { - tierSizeInTiles.push([ - Math.ceil(imageWidth / tileSizeForTierSizeCalculation), - Math.ceil(imageHeight / tileSizeForTierSizeCalculation) - ]); - tileSizeForTierSizeCalculation += tileSizeForTierSizeCalculation; - } - break; - case TierSizeCalculation.TRUNCATED: - let width = imageWidth; - let height = imageHeight; - while (width > tileSizeForTierSizeCalculation || height > tileSizeForTierSizeCalculation) { - tierSizeInTiles.push([ - Math.ceil(width / tileSizeForTierSizeCalculation), - Math.ceil(height / tileSizeForTierSizeCalculation) - ]); - width >>= 1; - height >>= 1; - } - break; - default: - assert(false, 53); // Unknown `tierSizeCalculation` configured - break; - } + const imageWidth = size[0]; + const imageHeight = size[1]; + const extent = options.extent || [0, -size[1], size[0], 0]; + const tierSizeInTiles = []; + const tileSize = options.tileSize || DEFAULT_TILE_SIZE; + let tileSizeForTierSizeCalculation = tileSize; - tierSizeInTiles.push([1, 1]); - tierSizeInTiles.reverse(); - - const resolutions = [1]; - const tileCountUpToTier = [0]; - for (let i = 1, ii = tierSizeInTiles.length; i < ii; i++) { - resolutions.push(1 << i); - tileCountUpToTier.push( - tierSizeInTiles[i - 1][0] * tierSizeInTiles[i - 1][1] + - tileCountUpToTier[i - 1] - ); - } - resolutions.reverse(); - - const tileGrid = new TileGrid({ - tileSize: tileSize, - extent: extent, - origin: getTopLeft(extent), - resolutions: resolutions - }); - - let url = options.url; - if (url && url.indexOf('{TileGroup}') == -1 && url.indexOf('{tileIndex}') == -1) { - url += '{TileGroup}/{z}-{x}-{y}.jpg'; - } - const urls = expandUrl(url); - - /** - * @param {string} template Template. - * @return {module:ol/Tile~UrlFunction} Tile URL function. - */ - function createFromTemplate(template) { - - return ( - /** - * @param {module:ol/tilecoord~TileCoord} tileCoord Tile Coordinate. - * @param {number} pixelRatio Pixel ratio. - * @param {module:ol/proj/Projection} projection Projection. - * @return {string|undefined} Tile URL. - */ - function(tileCoord, pixelRatio, projection) { - if (!tileCoord) { - return undefined; - } else { - const tileCoordZ = tileCoord[0]; - const tileCoordX = tileCoord[1]; - const tileCoordY = -tileCoord[2] - 1; - const tileIndex = - tileCoordX + - tileCoordY * tierSizeInTiles[tileCoordZ][0]; - const tileSize = tileGrid.getTileSize(tileCoordZ); - const tileGroup = ((tileIndex + tileCountUpToTier[tileCoordZ]) / tileSize) | 0; - const localContext = { - 'z': tileCoordZ, - 'x': tileCoordX, - 'y': tileCoordY, - 'tileIndex': tileIndex, - 'TileGroup': 'TileGroup' + tileGroup - }; - return template.replace(/\{(\w+?)\}/g, function(m, p) { - return localContext[p]; - }); + switch (tierSizeCalculation) { + case TierSizeCalculation.DEFAULT: + while (imageWidth > tileSizeForTierSizeCalculation || imageHeight > tileSizeForTierSizeCalculation) { + tierSizeInTiles.push([ + Math.ceil(imageWidth / tileSizeForTierSizeCalculation), + Math.ceil(imageHeight / tileSizeForTierSizeCalculation) + ]); + tileSizeForTierSizeCalculation += tileSizeForTierSizeCalculation; } - } - ); + break; + case TierSizeCalculation.TRUNCATED: + let width = imageWidth; + let height = imageHeight; + while (width > tileSizeForTierSizeCalculation || height > tileSizeForTierSizeCalculation) { + tierSizeInTiles.push([ + Math.ceil(width / tileSizeForTierSizeCalculation), + Math.ceil(height / tileSizeForTierSizeCalculation) + ]); + width >>= 1; + height >>= 1; + } + break; + default: + assert(false, 53); // Unknown `tierSizeCalculation` configured + break; + } + + tierSizeInTiles.push([1, 1]); + tierSizeInTiles.reverse(); + + const resolutions = [1]; + const tileCountUpToTier = [0]; + for (let i = 1, ii = tierSizeInTiles.length; i < ii; i++) { + resolutions.push(1 << i); + tileCountUpToTier.push( + tierSizeInTiles[i - 1][0] * tierSizeInTiles[i - 1][1] + + tileCountUpToTier[i - 1] + ); + } + resolutions.reverse(); + + const tileGrid = new TileGrid({ + tileSize: tileSize, + extent: extent, + origin: getTopLeft(extent), + resolutions: resolutions + }); + + let url = options.url; + if (url && url.indexOf('{TileGroup}') == -1 && url.indexOf('{tileIndex}') == -1) { + url += '{TileGroup}/{z}-{x}-{y}.jpg'; + } + const urls = expandUrl(url); + + /** + * @param {string} template Template. + * @return {module:ol/Tile~UrlFunction} Tile URL function. + */ + function createFromTemplate(template) { + + return ( + /** + * @param {module:ol/tilecoord~TileCoord} tileCoord Tile Coordinate. + * @param {number} pixelRatio Pixel ratio. + * @param {module:ol/proj/Projection} projection Projection. + * @return {string|undefined} Tile URL. + */ + function(tileCoord, pixelRatio, projection) { + if (!tileCoord) { + return undefined; + } else { + const tileCoordZ = tileCoord[0]; + const tileCoordX = tileCoord[1]; + const tileCoordY = -tileCoord[2] - 1; + const tileIndex = + tileCoordX + + tileCoordY * tierSizeInTiles[tileCoordZ][0]; + const tileSize = tileGrid.getTileSize(tileCoordZ); + const tileGroup = ((tileIndex + tileCountUpToTier[tileCoordZ]) / tileSize) | 0; + const localContext = { + 'z': tileCoordZ, + 'x': tileCoordX, + 'y': tileCoordY, + 'tileIndex': tileIndex, + 'TileGroup': 'TileGroup' + tileGroup + }; + return template.replace(/\{(\w+?)\}/g, function(m, p) { + return localContext[p]; + }); + } + } + ); + } + + const tileUrlFunction = createFromTileUrlFunctions(urls.map(createFromTemplate)); + + const ZoomifyTileClass = CustomTile.bind(null, tileGrid); + + TileImage.call(this, { + attributions: options.attributions, + cacheSize: options.cacheSize, + crossOrigin: options.crossOrigin, + projection: options.projection, + reprojectionErrorThreshold: options.reprojectionErrorThreshold, + tileClass: ZoomifyTileClass, + tileGrid: tileGrid, + tileUrlFunction: tileUrlFunction, + transition: options.transition + }); + } - const tileUrlFunction = createFromTileUrlFunctions(urls.map(createFromTemplate)); - - const ZoomifyTileClass = CustomTile.bind(null, tileGrid); - - TileImage.call(this, { - attributions: options.attributions, - cacheSize: options.cacheSize, - crossOrigin: options.crossOrigin, - projection: options.projection, - reprojectionErrorThreshold: options.reprojectionErrorThreshold, - tileClass: ZoomifyTileClass, - tileGrid: tileGrid, - tileUrlFunction: tileUrlFunction, - transition: options.transition - }); - -}; +} inherits(Zoomify, TileImage); - export default Zoomify; diff --git a/src/ol/structs/LRUCache.js b/src/ol/structs/LRUCache.js index 9a01971a51..bfa20ad27b 100644 --- a/src/ol/structs/LRUCache.js +++ b/src/ol/structs/LRUCache.js @@ -27,272 +27,277 @@ import EventType from '../events/EventType.js'; * @template T * @param {number=} opt_highWaterMark High water mark. */ -const LRUCache = function(opt_highWaterMark) { +class LRUCache { + + constructor(opt_highWaterMark) { + + EventTarget.call(this); + + /** + * @type {number} + */ + this.highWaterMark = opt_highWaterMark !== undefined ? opt_highWaterMark : 2048; + + /** + * @private + * @type {number} + */ + this.count_ = 0; + + /** + * @private + * @type {!Object.} + */ + this.entries_ = {}; + + /** + * @private + * @type {?module:ol/structs/LRUCache~Entry} + */ + this.oldest_ = null; + + /** + * @private + * @type {?module:ol/structs/LRUCache~Entry} + */ + this.newest_ = null; + + } - EventTarget.call(this); /** - * @type {number} + * @return {boolean} Can expire cache. */ - this.highWaterMark = opt_highWaterMark !== undefined ? opt_highWaterMark : 2048; + canExpireCache() { + return this.getCount() > this.highWaterMark; + } + /** - * @private - * @type {number} + * FIXME empty description for jsdoc */ - this.count_ = 0; + clear() { + this.count_ = 0; + this.entries_ = {}; + this.oldest_ = null; + this.newest_ = null; + this.dispatchEvent(EventType.CLEAR); + } + /** - * @private - * @type {!Object.} + * @param {string} key Key. + * @return {boolean} Contains key. */ - this.entries_ = {}; + containsKey(key) { + return this.entries_.hasOwnProperty(key); + } + /** - * @private - * @type {?module:ol/structs/LRUCache~Entry} + * @param {function(this: S, T, string, module:ol/structs/LRUCache): ?} f The function + * to call for every entry from the oldest to the newer. This function takes + * 3 arguments (the entry value, the entry key and the LRUCache object). + * The return value is ignored. + * @param {S=} opt_this The object to use as `this` in `f`. + * @template S */ - this.oldest_ = null; + forEach(f, opt_this) { + let entry = this.oldest_; + while (entry) { + f.call(opt_this, entry.value_, entry.key_, this); + entry = entry.newer; + } + } + /** - * @private - * @type {?module:ol/structs/LRUCache~Entry} + * @param {string} key Key. + * @return {T} Value. */ - this.newest_ = null; + get(key) { + const entry = this.entries_[key]; + assert(entry !== undefined, + 15); // Tried to get a value for a key that does not exist in the cache + if (entry === this.newest_) { + return entry.value_; + } else if (entry === this.oldest_) { + this.oldest_ = /** @type {module:ol/structs/LRUCache~Entry} */ (this.oldest_.newer); + this.oldest_.older = null; + } else { + entry.newer.older = entry.older; + entry.older.newer = entry.newer; + } + entry.newer = null; + entry.older = this.newest_; + this.newest_.newer = entry; + this.newest_ = entry; + return entry.value_; + } -}; + + /** + * Remove an entry from the cache. + * @param {string} key The entry key. + * @return {T} The removed entry. + */ + remove(key) { + const entry = this.entries_[key]; + assert(entry !== undefined, 15); // Tried to get a value for a key that does not exist in the cache + if (entry === this.newest_) { + this.newest_ = /** @type {module:ol/structs/LRUCache~Entry} */ (entry.older); + if (this.newest_) { + this.newest_.newer = null; + } + } else if (entry === this.oldest_) { + this.oldest_ = /** @type {module:ol/structs/LRUCache~Entry} */ (entry.newer); + if (this.oldest_) { + this.oldest_.older = null; + } + } else { + entry.newer.older = entry.older; + entry.older.newer = entry.newer; + } + delete this.entries_[key]; + --this.count_; + return entry.value_; + } + + + /** + * @return {number} Count. + */ + getCount() { + return this.count_; + } + + + /** + * @return {Array.} Keys. + */ + getKeys() { + const keys = new Array(this.count_); + let i = 0; + let entry; + for (entry = this.newest_; entry; entry = entry.older) { + keys[i++] = entry.key_; + } + return keys; + } + + + /** + * @return {Array.} Values. + */ + getValues() { + const values = new Array(this.count_); + let i = 0; + let entry; + for (entry = this.newest_; entry; entry = entry.older) { + values[i++] = entry.value_; + } + return values; + } + + + /** + * @return {T} Last value. + */ + peekLast() { + return this.oldest_.value_; + } + + + /** + * @return {string} Last key. + */ + peekLastKey() { + return this.oldest_.key_; + } + + + /** + * Get the key of the newest item in the cache. Throws if the cache is empty. + * @return {string} The newest key. + */ + peekFirstKey() { + return this.newest_.key_; + } + + + /** + * @return {T} value Value. + */ + pop() { + const entry = this.oldest_; + delete this.entries_[entry.key_]; + if (entry.newer) { + entry.newer.older = null; + } + this.oldest_ = /** @type {module:ol/structs/LRUCache~Entry} */ (entry.newer); + if (!this.oldest_) { + this.newest_ = null; + } + --this.count_; + return entry.value_; + } + + + /** + * @param {string} key Key. + * @param {T} value Value. + */ + replace(key, value) { + this.get(key); // update `newest_` + this.entries_[key].value_ = value; + } + + + /** + * @param {string} key Key. + * @param {T} value Value. + */ + set(key, value) { + assert(!(key in this.entries_), + 16); // Tried to set a value for a key that is used already + const entry = /** @type {module:ol/structs/LRUCache~Entry} */ ({ + key_: key, + newer: null, + older: this.newest_, + value_: value + }); + if (!this.newest_) { + this.oldest_ = entry; + } else { + this.newest_.newer = entry; + } + this.newest_ = entry; + this.entries_[key] = entry; + ++this.count_; + } + + + /** + * Set a maximum number of entries for the cache. + * @param {number} size Cache size. + * @api + */ + setSize(size) { + this.highWaterMark = size; + } + + + /** + * Prune the cache. + */ + prune() { + while (this.canExpireCache()) { + this.pop(); + } + } + +} inherits(LRUCache, EventTarget); - -/** - * @return {boolean} Can expire cache. - */ -LRUCache.prototype.canExpireCache = function() { - return this.getCount() > this.highWaterMark; -}; - - -/** - * FIXME empty description for jsdoc - */ -LRUCache.prototype.clear = function() { - this.count_ = 0; - this.entries_ = {}; - this.oldest_ = null; - this.newest_ = null; - this.dispatchEvent(EventType.CLEAR); -}; - - -/** - * @param {string} key Key. - * @return {boolean} Contains key. - */ -LRUCache.prototype.containsKey = function(key) { - return this.entries_.hasOwnProperty(key); -}; - - -/** - * @param {function(this: S, T, string, module:ol/structs/LRUCache): ?} f The function - * to call for every entry from the oldest to the newer. This function takes - * 3 arguments (the entry value, the entry key and the LRUCache object). - * The return value is ignored. - * @param {S=} opt_this The object to use as `this` in `f`. - * @template S - */ -LRUCache.prototype.forEach = function(f, opt_this) { - let entry = this.oldest_; - while (entry) { - f.call(opt_this, entry.value_, entry.key_, this); - entry = entry.newer; - } -}; - - -/** - * @param {string} key Key. - * @return {T} Value. - */ -LRUCache.prototype.get = function(key) { - const entry = this.entries_[key]; - assert(entry !== undefined, - 15); // Tried to get a value for a key that does not exist in the cache - if (entry === this.newest_) { - return entry.value_; - } else if (entry === this.oldest_) { - this.oldest_ = /** @type {module:ol/structs/LRUCache~Entry} */ (this.oldest_.newer); - this.oldest_.older = null; - } else { - entry.newer.older = entry.older; - entry.older.newer = entry.newer; - } - entry.newer = null; - entry.older = this.newest_; - this.newest_.newer = entry; - this.newest_ = entry; - return entry.value_; -}; - - -/** - * Remove an entry from the cache. - * @param {string} key The entry key. - * @return {T} The removed entry. - */ -LRUCache.prototype.remove = function(key) { - const entry = this.entries_[key]; - assert(entry !== undefined, 15); // Tried to get a value for a key that does not exist in the cache - if (entry === this.newest_) { - this.newest_ = /** @type {module:ol/structs/LRUCache~Entry} */ (entry.older); - if (this.newest_) { - this.newest_.newer = null; - } - } else if (entry === this.oldest_) { - this.oldest_ = /** @type {module:ol/structs/LRUCache~Entry} */ (entry.newer); - if (this.oldest_) { - this.oldest_.older = null; - } - } else { - entry.newer.older = entry.older; - entry.older.newer = entry.newer; - } - delete this.entries_[key]; - --this.count_; - return entry.value_; -}; - - -/** - * @return {number} Count. - */ -LRUCache.prototype.getCount = function() { - return this.count_; -}; - - -/** - * @return {Array.} Keys. - */ -LRUCache.prototype.getKeys = function() { - const keys = new Array(this.count_); - let i = 0; - let entry; - for (entry = this.newest_; entry; entry = entry.older) { - keys[i++] = entry.key_; - } - return keys; -}; - - -/** - * @return {Array.} Values. - */ -LRUCache.prototype.getValues = function() { - const values = new Array(this.count_); - let i = 0; - let entry; - for (entry = this.newest_; entry; entry = entry.older) { - values[i++] = entry.value_; - } - return values; -}; - - -/** - * @return {T} Last value. - */ -LRUCache.prototype.peekLast = function() { - return this.oldest_.value_; -}; - - -/** - * @return {string} Last key. - */ -LRUCache.prototype.peekLastKey = function() { - return this.oldest_.key_; -}; - - -/** - * Get the key of the newest item in the cache. Throws if the cache is empty. - * @return {string} The newest key. - */ -LRUCache.prototype.peekFirstKey = function() { - return this.newest_.key_; -}; - - -/** - * @return {T} value Value. - */ -LRUCache.prototype.pop = function() { - const entry = this.oldest_; - delete this.entries_[entry.key_]; - if (entry.newer) { - entry.newer.older = null; - } - this.oldest_ = /** @type {module:ol/structs/LRUCache~Entry} */ (entry.newer); - if (!this.oldest_) { - this.newest_ = null; - } - --this.count_; - return entry.value_; -}; - - -/** - * @param {string} key Key. - * @param {T} value Value. - */ -LRUCache.prototype.replace = function(key, value) { - this.get(key); // update `newest_` - this.entries_[key].value_ = value; -}; - - -/** - * @param {string} key Key. - * @param {T} value Value. - */ -LRUCache.prototype.set = function(key, value) { - assert(!(key in this.entries_), - 16); // Tried to set a value for a key that is used already - const entry = /** @type {module:ol/structs/LRUCache~Entry} */ ({ - key_: key, - newer: null, - older: this.newest_, - value_: value - }); - if (!this.newest_) { - this.oldest_ = entry; - } else { - this.newest_.newer = entry; - } - this.newest_ = entry; - this.entries_[key] = entry; - ++this.count_; -}; - - -/** - * Set a maximum number of entries for the cache. - * @param {number} size Cache size. - * @api - */ -LRUCache.prototype.setSize = function(size) { - this.highWaterMark = size; -}; - - -/** - * Prune the cache. - */ -LRUCache.prototype.prune = function() { - while (this.canExpireCache()) { - this.pop(); - } -}; export default LRUCache; diff --git a/src/ol/structs/LinkedList.js b/src/ol/structs/LinkedList.js index d10b582c69..933d9c42c4 100644 --- a/src/ol/structs/LinkedList.js +++ b/src/ol/structs/LinkedList.js @@ -19,242 +19,249 @@ * @param {boolean=} opt_circular The last item is connected to the first one, * and the first item to the last one. Default is true. */ -const LinkedList = function(opt_circular) { +class LinkedList { + + constructor(opt_circular) { + + /** + * @private + * @type {module:ol/structs/LinkedList~Item|undefined} + */ + this.first_; + + /** + * @private + * @type {module:ol/structs/LinkedList~Item|undefined} + */ + this.last_; + + /** + * @private + * @type {module:ol/structs/LinkedList~Item|undefined} + */ + this.head_; + + /** + * @private + * @type {boolean} + */ + this.circular_ = opt_circular === undefined ? true : opt_circular; + + /** + * @private + * @type {number} + */ + this.length_ = 0; + + } /** - * @private - * @type {module:ol/structs/LinkedList~Item|undefined} + * Inserts an item into the linked list right after the current one. + * + * @param {?} data Item data. */ - this.first_; + insertItem(data) { - /** - * @private - * @type {module:ol/structs/LinkedList~Item|undefined} - */ - this.last_; + /** @type {module:ol/structs/LinkedList~Item} */ + const item = { + prev: undefined, + next: undefined, + data: data + }; - /** - * @private - * @type {module:ol/structs/LinkedList~Item|undefined} - */ - this.head_; + const head = this.head_; - /** - * @private - * @type {boolean} - */ - this.circular_ = opt_circular === undefined ? true : opt_circular; - - /** - * @private - * @type {number} - */ - this.length_ = 0; -}; - -/** - * Inserts an item into the linked list right after the current one. - * - * @param {?} data Item data. - */ -LinkedList.prototype.insertItem = function(data) { - - /** @type {module:ol/structs/LinkedList~Item} */ - const item = { - prev: undefined, - next: undefined, - data: data - }; - - const head = this.head_; - - //Initialize the list. - if (!head) { - this.first_ = item; - this.last_ = item; - if (this.circular_) { - item.next = item; - item.prev = item; - } - } else { - //Link the new item to the adjacent ones. - const next = head.next; - item.prev = head; - item.next = next; - head.next = item; - if (next) { - next.prev = item; - } - - if (head === this.last_) { + //Initialize the list. + if (!head) { + this.first_ = item; this.last_ = item; - } - } - this.head_ = item; - this.length_++; -}; - -/** - * Removes the current item from the list. Sets the cursor to the next item, - * if possible. - */ -LinkedList.prototype.removeItem = function() { - const head = this.head_; - if (head) { - const next = head.next; - const prev = head.prev; - if (next) { - next.prev = prev; - } - if (prev) { - prev.next = next; - } - this.head_ = next || prev; - - if (this.first_ === this.last_) { - this.head_ = undefined; - this.first_ = undefined; - this.last_ = undefined; - } else if (this.first_ === head) { - this.first_ = this.head_; - } else if (this.last_ === head) { - this.last_ = prev ? this.head_.prev : this.head_; - } - this.length_--; - } -}; - -/** - * Sets the cursor to the first item, and returns the associated data. - * - * @return {?} Item data. - */ -LinkedList.prototype.firstItem = function() { - this.head_ = this.first_; - if (this.head_) { - return this.head_.data; - } - return undefined; -}; - -/** -* Sets the cursor to the last item, and returns the associated data. -* -* @return {?} Item data. -*/ -LinkedList.prototype.lastItem = function() { - this.head_ = this.last_; - if (this.head_) { - return this.head_.data; - } - return undefined; -}; - -/** - * Sets the cursor to the next item, and returns the associated data. - * - * @return {?} Item data. - */ -LinkedList.prototype.nextItem = function() { - if (this.head_ && this.head_.next) { - this.head_ = this.head_.next; - return this.head_.data; - } - return undefined; -}; - -/** - * Returns the next item's data without moving the cursor. - * - * @return {?} Item data. - */ -LinkedList.prototype.getNextItem = function() { - if (this.head_ && this.head_.next) { - return this.head_.next.data; - } - return undefined; -}; - -/** - * Sets the cursor to the previous item, and returns the associated data. - * - * @return {?} Item data. - */ -LinkedList.prototype.prevItem = function() { - if (this.head_ && this.head_.prev) { - this.head_ = this.head_.prev; - return this.head_.data; - } - return undefined; -}; - -/** - * Returns the previous item's data without moving the cursor. - * - * @return {?} Item data. - */ -LinkedList.prototype.getPrevItem = function() { - if (this.head_ && this.head_.prev) { - return this.head_.prev.data; - } - return undefined; -}; - -/** - * Returns the current item's data. - * - * @return {?} Item data. - */ -LinkedList.prototype.getCurrItem = function() { - if (this.head_) { - return this.head_.data; - } - return undefined; -}; - -/** - * Sets the first item of the list. This only works for circular lists, and sets - * the last item accordingly. - */ -LinkedList.prototype.setFirstItem = function() { - if (this.circular_ && this.head_) { - this.first_ = this.head_; - this.last_ = this.head_.prev; - } -}; - -/** - * Concatenates two lists. - * @param {module:ol/structs/LinkedList} list List to merge into the current list. - */ -LinkedList.prototype.concat = function(list) { - if (list.head_) { - if (this.head_) { - const end = this.head_.next; - this.head_.next = list.first_; - list.first_.prev = this.head_; - end.prev = list.last_; - list.last_.next = end; - this.length_ += list.length_; + if (this.circular_) { + item.next = item; + item.prev = item; + } } else { - this.head_ = list.head_; - this.first_ = list.first_; - this.last_ = list.last_; - this.length_ = list.length_; - } - list.head_ = undefined; - list.first_ = undefined; - list.last_ = undefined; - list.length_ = 0; - } -}; + //Link the new item to the adjacent ones. + const next = head.next; + item.prev = head; + item.next = next; + head.next = item; + if (next) { + next.prev = item; + } + + if (head === this.last_) { + this.last_ = item; + } + } + this.head_ = item; + this.length_++; + } + + /** + * Removes the current item from the list. Sets the cursor to the next item, + * if possible. + */ + removeItem() { + const head = this.head_; + if (head) { + const next = head.next; + const prev = head.prev; + if (next) { + next.prev = prev; + } + if (prev) { + prev.next = next; + } + this.head_ = next || prev; + + if (this.first_ === this.last_) { + this.head_ = undefined; + this.first_ = undefined; + this.last_ = undefined; + } else if (this.first_ === head) { + this.first_ = this.head_; + } else if (this.last_ === head) { + this.last_ = prev ? this.head_.prev : this.head_; + } + this.length_--; + } + } + + /** + * Sets the cursor to the first item, and returns the associated data. + * + * @return {?} Item data. + */ + firstItem() { + this.head_ = this.first_; + if (this.head_) { + return this.head_.data; + } + return undefined; + } + + /** + * Sets the cursor to the last item, and returns the associated data. + * + * @return {?} Item data. + */ + lastItem() { + this.head_ = this.last_; + if (this.head_) { + return this.head_.data; + } + return undefined; + } + + /** + * Sets the cursor to the next item, and returns the associated data. + * + * @return {?} Item data. + */ + nextItem() { + if (this.head_ && this.head_.next) { + this.head_ = this.head_.next; + return this.head_.data; + } + return undefined; + } + + /** + * Returns the next item's data without moving the cursor. + * + * @return {?} Item data. + */ + getNextItem() { + if (this.head_ && this.head_.next) { + return this.head_.next.data; + } + return undefined; + } + + /** + * Sets the cursor to the previous item, and returns the associated data. + * + * @return {?} Item data. + */ + prevItem() { + if (this.head_ && this.head_.prev) { + this.head_ = this.head_.prev; + return this.head_.data; + } + return undefined; + } + + /** + * Returns the previous item's data without moving the cursor. + * + * @return {?} Item data. + */ + getPrevItem() { + if (this.head_ && this.head_.prev) { + return this.head_.prev.data; + } + return undefined; + } + + /** + * Returns the current item's data. + * + * @return {?} Item data. + */ + getCurrItem() { + if (this.head_) { + return this.head_.data; + } + return undefined; + } + + /** + * Sets the first item of the list. This only works for circular lists, and sets + * the last item accordingly. + */ + setFirstItem() { + if (this.circular_ && this.head_) { + this.first_ = this.head_; + this.last_ = this.head_.prev; + } + } + + /** + * Concatenates two lists. + * @param {module:ol/structs/LinkedList} list List to merge into the current list. + */ + concat(list) { + if (list.head_) { + if (this.head_) { + const end = this.head_.next; + this.head_.next = list.first_; + list.first_.prev = this.head_; + end.prev = list.last_; + list.last_.next = end; + this.length_ += list.length_; + } else { + this.head_ = list.head_; + this.first_ = list.first_; + this.last_ = list.last_; + this.length_ = list.length_; + } + list.head_ = undefined; + list.first_ = undefined; + list.last_ = undefined; + list.length_ = 0; + } + } + + /** + * Returns the current length of the list. + * + * @return {number} Length. + */ + getLength() { + return this.length_; + } + +} + -/** - * Returns the current length of the list. - * - * @return {number} Length. - */ -LinkedList.prototype.getLength = function() { - return this.length_; -}; export default LinkedList; diff --git a/src/ol/structs/PriorityQueue.js b/src/ol/structs/PriorityQueue.js index ef2513a99d..d8b75a9a17 100644 --- a/src/ol/structs/PriorityQueue.js +++ b/src/ol/structs/PriorityQueue.js @@ -4,6 +4,13 @@ import {assert} from '../asserts.js'; import {clear} from '../obj.js'; + +/** + * @type {number} + */ +export const DROP = Infinity; + + /** * Priority queue. * @@ -19,257 +26,256 @@ import {clear} from '../obj.js'; * @struct * @template T */ -const PriorityQueue = function(priorityFunction, keyFunction) { +class PriorityQueue { - /** - * @type {function(T): number} - * @private - */ - this.priorityFunction_ = priorityFunction; + constructor(priorityFunction, keyFunction) { - /** - * @type {function(T): string} - * @private - */ - this.keyFunction_ = keyFunction; + /** + * @type {function(T): number} + * @private + */ + this.priorityFunction_ = priorityFunction; - /** - * @type {Array.} - * @private - */ - this.elements_ = []; + /** + * @type {function(T): string} + * @private + */ + this.keyFunction_ = keyFunction; - /** - * @type {Array.} - * @private - */ - this.priorities_ = []; + /** + * @type {Array.} + * @private + */ + this.elements_ = []; - /** - * @type {!Object.} - * @private - */ - this.queuedElements_ = {}; + /** + * @type {Array.} + * @private + */ + this.priorities_ = []; -}; + /** + * @type {!Object.} + * @private + */ + this.queuedElements_ = {}; - -/** - * @type {number} - */ -export const DROP = Infinity; - - -/** - * FIXME empty description for jsdoc - */ -PriorityQueue.prototype.clear = function() { - this.elements_.length = 0; - this.priorities_.length = 0; - clear(this.queuedElements_); -}; - - -/** - * Remove and return the highest-priority element. O(log N). - * @return {T} Element. - */ -PriorityQueue.prototype.dequeue = function() { - const elements = this.elements_; - const priorities = this.priorities_; - const element = elements[0]; - if (elements.length == 1) { - elements.length = 0; - priorities.length = 0; - } else { - elements[0] = elements.pop(); - priorities[0] = priorities.pop(); - this.siftUp_(0); - } - const elementKey = this.keyFunction_(element); - delete this.queuedElements_[elementKey]; - return element; -}; - - -/** - * Enqueue an element. O(log N). - * @param {T} element Element. - * @return {boolean} The element was added to the queue. - */ -PriorityQueue.prototype.enqueue = function(element) { - assert(!(this.keyFunction_(element) in this.queuedElements_), - 31); // Tried to enqueue an `element` that was already added to the queue - const priority = this.priorityFunction_(element); - if (priority != DROP) { - this.elements_.push(element); - this.priorities_.push(priority); - this.queuedElements_[this.keyFunction_(element)] = true; - this.siftDown_(0, this.elements_.length - 1); - return true; - } - return false; -}; - - -/** - * @return {number} Count. - */ -PriorityQueue.prototype.getCount = function() { - return this.elements_.length; -}; - - -/** - * Gets the index of the left child of the node at the given index. - * @param {number} index The index of the node to get the left child for. - * @return {number} The index of the left child. - * @private - */ -PriorityQueue.prototype.getLeftChildIndex_ = function(index) { - return index * 2 + 1; -}; - - -/** - * Gets the index of the right child of the node at the given index. - * @param {number} index The index of the node to get the right child for. - * @return {number} The index of the right child. - * @private - */ -PriorityQueue.prototype.getRightChildIndex_ = function(index) { - return index * 2 + 2; -}; - - -/** - * Gets the index of the parent of the node at the given index. - * @param {number} index The index of the node to get the parent for. - * @return {number} The index of the parent. - * @private - */ -PriorityQueue.prototype.getParentIndex_ = function(index) { - return (index - 1) >> 1; -}; - - -/** - * Make this a heap. O(N). - * @private - */ -PriorityQueue.prototype.heapify_ = function() { - let i; - for (i = (this.elements_.length >> 1) - 1; i >= 0; i--) { - this.siftUp_(i); - } -}; - - -/** - * @return {boolean} Is empty. - */ -PriorityQueue.prototype.isEmpty = function() { - return this.elements_.length === 0; -}; - - -/** - * @param {string} key Key. - * @return {boolean} Is key queued. - */ -PriorityQueue.prototype.isKeyQueued = function(key) { - return key in this.queuedElements_; -}; - - -/** - * @param {T} element Element. - * @return {boolean} Is queued. - */ -PriorityQueue.prototype.isQueued = function(element) { - return this.isKeyQueued(this.keyFunction_(element)); -}; - - -/** - * @param {number} index The index of the node to move down. - * @private - */ -PriorityQueue.prototype.siftUp_ = function(index) { - const elements = this.elements_; - const priorities = this.priorities_; - const count = elements.length; - const element = elements[index]; - const priority = priorities[index]; - const startIndex = index; - - while (index < (count >> 1)) { - const lIndex = this.getLeftChildIndex_(index); - const rIndex = this.getRightChildIndex_(index); - - const smallerChildIndex = rIndex < count && - priorities[rIndex] < priorities[lIndex] ? - rIndex : lIndex; - - elements[index] = elements[smallerChildIndex]; - priorities[index] = priorities[smallerChildIndex]; - index = smallerChildIndex; } - elements[index] = element; - priorities[index] = priority; - this.siftDown_(startIndex, index); -}; + /** + * FIXME empty description for jsdoc + */ + clear() { + this.elements_.length = 0; + this.priorities_.length = 0; + clear(this.queuedElements_); + } -/** - * @param {number} startIndex The index of the root. - * @param {number} index The index of the node to move up. - * @private - */ -PriorityQueue.prototype.siftDown_ = function(startIndex, index) { - const elements = this.elements_; - const priorities = this.priorities_; - const element = elements[index]; - const priority = priorities[index]; - - while (index > startIndex) { - const parentIndex = this.getParentIndex_(index); - if (priorities[parentIndex] > priority) { - elements[index] = elements[parentIndex]; - priorities[index] = priorities[parentIndex]; - index = parentIndex; + /** + * Remove and return the highest-priority element. O(log N). + * @return {T} Element. + */ + dequeue() { + const elements = this.elements_; + const priorities = this.priorities_; + const element = elements[0]; + if (elements.length == 1) { + elements.length = 0; + priorities.length = 0; } else { - break; + elements[0] = elements.pop(); + priorities[0] = priorities.pop(); + this.siftUp_(0); + } + const elementKey = this.keyFunction_(element); + delete this.queuedElements_[elementKey]; + return element; + } + + + /** + * Enqueue an element. O(log N). + * @param {T} element Element. + * @return {boolean} The element was added to the queue. + */ + enqueue(element) { + assert(!(this.keyFunction_(element) in this.queuedElements_), + 31); // Tried to enqueue an `element` that was already added to the queue + const priority = this.priorityFunction_(element); + if (priority != DROP) { + this.elements_.push(element); + this.priorities_.push(priority); + this.queuedElements_[this.keyFunction_(element)] = true; + this.siftDown_(0, this.elements_.length - 1); + return true; + } + return false; + } + + + /** + * @return {number} Count. + */ + getCount() { + return this.elements_.length; + } + + + /** + * Gets the index of the left child of the node at the given index. + * @param {number} index The index of the node to get the left child for. + * @return {number} The index of the left child. + * @private + */ + getLeftChildIndex_(index) { + return index * 2 + 1; + } + + + /** + * Gets the index of the right child of the node at the given index. + * @param {number} index The index of the node to get the right child for. + * @return {number} The index of the right child. + * @private + */ + getRightChildIndex_(index) { + return index * 2 + 2; + } + + + /** + * Gets the index of the parent of the node at the given index. + * @param {number} index The index of the node to get the parent for. + * @return {number} The index of the parent. + * @private + */ + getParentIndex_(index) { + return (index - 1) >> 1; + } + + + /** + * Make this a heap. O(N). + * @private + */ + heapify_() { + let i; + for (i = (this.elements_.length >> 1) - 1; i >= 0; i--) { + this.siftUp_(i); } } - elements[index] = element; - priorities[index] = priority; -}; -/** - * FIXME empty description for jsdoc - */ -PriorityQueue.prototype.reprioritize = function() { - const priorityFunction = this.priorityFunction_; - const elements = this.elements_; - const priorities = this.priorities_; - let index = 0; - const n = elements.length; - let element, i, priority; - for (i = 0; i < n; ++i) { - element = elements[i]; - priority = priorityFunction(element); - if (priority == DROP) { - delete this.queuedElements_[this.keyFunction_(element)]; - } else { - priorities[index] = priority; - elements[index++] = element; - } + /** + * @return {boolean} Is empty. + */ + isEmpty() { + return this.elements_.length === 0; } - elements.length = index; - priorities.length = index; - this.heapify_(); -}; + + + /** + * @param {string} key Key. + * @return {boolean} Is key queued. + */ + isKeyQueued(key) { + return key in this.queuedElements_; + } + + + /** + * @param {T} element Element. + * @return {boolean} Is queued. + */ + isQueued(element) { + return this.isKeyQueued(this.keyFunction_(element)); + } + + + /** + * @param {number} index The index of the node to move down. + * @private + */ + siftUp_(index) { + const elements = this.elements_; + const priorities = this.priorities_; + const count = elements.length; + const element = elements[index]; + const priority = priorities[index]; + const startIndex = index; + + while (index < (count >> 1)) { + const lIndex = this.getLeftChildIndex_(index); + const rIndex = this.getRightChildIndex_(index); + + const smallerChildIndex = rIndex < count && + priorities[rIndex] < priorities[lIndex] ? + rIndex : lIndex; + + elements[index] = elements[smallerChildIndex]; + priorities[index] = priorities[smallerChildIndex]; + index = smallerChildIndex; + } + + elements[index] = element; + priorities[index] = priority; + this.siftDown_(startIndex, index); + } + + + /** + * @param {number} startIndex The index of the root. + * @param {number} index The index of the node to move up. + * @private + */ + siftDown_(startIndex, index) { + const elements = this.elements_; + const priorities = this.priorities_; + const element = elements[index]; + const priority = priorities[index]; + + while (index > startIndex) { + const parentIndex = this.getParentIndex_(index); + if (priorities[parentIndex] > priority) { + elements[index] = elements[parentIndex]; + priorities[index] = priorities[parentIndex]; + index = parentIndex; + } else { + break; + } + } + elements[index] = element; + priorities[index] = priority; + } + + + /** + * FIXME empty description for jsdoc + */ + reprioritize() { + const priorityFunction = this.priorityFunction_; + const elements = this.elements_; + const priorities = this.priorities_; + let index = 0; + const n = elements.length; + let element, i, priority; + for (i = 0; i < n; ++i) { + element = elements[i]; + priority = priorityFunction(element); + if (priority == DROP) { + delete this.queuedElements_[this.keyFunction_(element)]; + } else { + priorities[index] = priority; + elements[index++] = element; + } + } + elements.length = index; + priorities.length = index; + this.heapify_(); + } + +} + + export default PriorityQueue; diff --git a/src/ol/structs/RBush.js b/src/ol/structs/RBush.js index abdd79ef34..025d81aee6 100644 --- a/src/ol/structs/RBush.js +++ b/src/ol/structs/RBush.js @@ -24,55 +24,31 @@ import {isEmpty} from '../obj.js'; * @struct * @template T */ -const RBush = function(opt_maxEntries) { +class RBush { + + constructor(opt_maxEntries) { + + /** + * @private + */ + this.rbush_ = rbush(opt_maxEntries, undefined); + + /** + * A mapping between the objects added to this rbush wrapper + * and the objects that are actually added to the internal rbush. + * @private + * @type {Object.} + */ + this.items_ = {}; + + } /** - * @private + * Insert a value into the RBush. + * @param {module:ol/extent~Extent} extent Extent. + * @param {T} value Value. */ - this.rbush_ = rbush(opt_maxEntries, undefined); - - /** - * A mapping between the objects added to this rbush wrapper - * and the objects that are actually added to the internal rbush. - * @private - * @type {Object.} - */ - this.items_ = {}; - -}; - - -/** - * Insert a value into the RBush. - * @param {module:ol/extent~Extent} extent Extent. - * @param {T} value Value. - */ -RBush.prototype.insert = function(extent, value) { - /** @type {module:ol/structs/RBush~Entry} */ - const item = { - minX: extent[0], - minY: extent[1], - maxX: extent[2], - maxY: extent[3], - value: value - }; - - this.rbush_.insert(item); - this.items_[getUid(value)] = item; -}; - - -/** - * Bulk-insert values into the RBush. - * @param {Array.} extents Extents. - * @param {Array.} values Values. - */ -RBush.prototype.load = function(extents, values) { - const items = new Array(values.length); - for (let i = 0, l = values.length; i < l; i++) { - const extent = extents[i]; - const value = values[i]; - + insert(extent, value) { /** @type {module:ol/structs/RBush~Entry} */ const item = { minX: extent[0], @@ -81,158 +57,187 @@ RBush.prototype.load = function(extents, values) { maxY: extent[3], value: value }; - items[i] = item; + + this.rbush_.insert(item); this.items_[getUid(value)] = item; } - this.rbush_.load(items); -}; -/** - * Remove a value from the RBush. - * @param {T} value Value. - * @return {boolean} Removed. - */ -RBush.prototype.remove = function(value) { - const uid = getUid(value); + /** + * Bulk-insert values into the RBush. + * @param {Array.} extents Extents. + * @param {Array.} values Values. + */ + load(extents, values) { + const items = new Array(values.length); + for (let i = 0, l = values.length; i < l; i++) { + const extent = extents[i]; + const value = values[i]; - // get the object in which the value was wrapped when adding to the - // internal rbush. then use that object to do the removal. - const item = this.items_[uid]; - delete this.items_[uid]; - return this.rbush_.remove(item) !== null; -}; - - -/** - * Update the extent of a value in the RBush. - * @param {module:ol/extent~Extent} extent Extent. - * @param {T} value Value. - */ -RBush.prototype.update = function(extent, value) { - const item = this.items_[getUid(value)]; - const bbox = [item.minX, item.minY, item.maxX, item.maxY]; - if (!equals(bbox, extent)) { - this.remove(value); - this.insert(extent, value); + /** @type {module:ol/structs/RBush~Entry} */ + const item = { + minX: extent[0], + minY: extent[1], + maxX: extent[2], + maxY: extent[3], + value: value + }; + items[i] = item; + this.items_[getUid(value)] = item; + } + this.rbush_.load(items); } -}; -/** - * Return all values in the RBush. - * @return {Array.} All. - */ -RBush.prototype.getAll = function() { - const items = this.rbush_.all(); - return items.map(function(item) { - return item.value; - }); -}; + /** + * Remove a value from the RBush. + * @param {T} value Value. + * @return {boolean} Removed. + */ + remove(value) { + const uid = getUid(value); + + // get the object in which the value was wrapped when adding to the + // internal rbush. then use that object to do the removal. + const item = this.items_[uid]; + delete this.items_[uid]; + return this.rbush_.remove(item) !== null; + } -/** - * Return all values in the given extent. - * @param {module:ol/extent~Extent} extent Extent. - * @return {Array.} All in extent. - */ -RBush.prototype.getInExtent = function(extent) { - /** @type {module:ol/structs/RBush~Entry} */ - const bbox = { - minX: extent[0], - minY: extent[1], - maxX: extent[2], - maxY: extent[3] - }; - const items = this.rbush_.search(bbox); - return items.map(function(item) { - return item.value; - }); -}; - - -/** - * Calls a callback function with each value in the tree. - * If the callback returns a truthy value, this value is returned without - * checking the rest of the tree. - * @param {function(this: S, T): *} callback Callback. - * @param {S=} opt_this The object to use as `this` in `callback`. - * @return {*} Callback return value. - * @template S - */ -RBush.prototype.forEach = function(callback, opt_this) { - return this.forEach_(this.getAll(), callback, opt_this); -}; - - -/** - * Calls a callback function with each value in the provided extent. - * @param {module:ol/extent~Extent} extent Extent. - * @param {function(this: S, T): *} callback Callback. - * @param {S=} opt_this The object to use as `this` in `callback`. - * @return {*} Callback return value. - * @template S - */ -RBush.prototype.forEachInExtent = function(extent, callback, opt_this) { - return this.forEach_(this.getInExtent(extent), callback, opt_this); -}; - - -/** - * @param {Array.} values Values. - * @param {function(this: S, T): *} callback Callback. - * @param {S=} opt_this The object to use as `this` in `callback`. - * @private - * @return {*} Callback return value. - * @template S - */ -RBush.prototype.forEach_ = function(values, callback, opt_this) { - let result; - for (let i = 0, l = values.length; i < l; i++) { - result = callback.call(opt_this, values[i]); - if (result) { - return result; + /** + * Update the extent of a value in the RBush. + * @param {module:ol/extent~Extent} extent Extent. + * @param {T} value Value. + */ + update(extent, value) { + const item = this.items_[getUid(value)]; + const bbox = [item.minX, item.minY, item.maxX, item.maxY]; + if (!equals(bbox, extent)) { + this.remove(value); + this.insert(extent, value); } } - return result; -}; -/** - * @return {boolean} Is empty. - */ -RBush.prototype.isEmpty = function() { - return isEmpty(this.items_); -}; - - -/** - * Remove all values from the RBush. - */ -RBush.prototype.clear = function() { - this.rbush_.clear(); - this.items_ = {}; -}; - - -/** - * @param {module:ol/extent~Extent=} opt_extent Extent. - * @return {module:ol/extent~Extent} Extent. - */ -RBush.prototype.getExtent = function(opt_extent) { - // FIXME add getExtent() to rbush - const data = this.rbush_.data; - return createOrUpdate(data.minX, data.minY, data.maxX, data.maxY, opt_extent); -}; - - -/** - * @param {module:ol/structs/RBush} rbush R-Tree. - */ -RBush.prototype.concat = function(rbush) { - this.rbush_.load(rbush.rbush_.all()); - for (const i in rbush.items_) { - this.items_[i | 0] = rbush.items_[i | 0]; + /** + * Return all values in the RBush. + * @return {Array.} All. + */ + getAll() { + const items = this.rbush_.all(); + return items.map(function(item) { + return item.value; + }); } -}; + + + /** + * Return all values in the given extent. + * @param {module:ol/extent~Extent} extent Extent. + * @return {Array.} All in extent. + */ + getInExtent(extent) { + /** @type {module:ol/structs/RBush~Entry} */ + const bbox = { + minX: extent[0], + minY: extent[1], + maxX: extent[2], + maxY: extent[3] + }; + const items = this.rbush_.search(bbox); + return items.map(function(item) { + return item.value; + }); + } + + + /** + * Calls a callback function with each value in the tree. + * If the callback returns a truthy value, this value is returned without + * checking the rest of the tree. + * @param {function(this: S, T): *} callback Callback. + * @param {S=} opt_this The object to use as `this` in `callback`. + * @return {*} Callback return value. + * @template S + */ + forEach(callback, opt_this) { + return this.forEach_(this.getAll(), callback, opt_this); + } + + + /** + * Calls a callback function with each value in the provided extent. + * @param {module:ol/extent~Extent} extent Extent. + * @param {function(this: S, T): *} callback Callback. + * @param {S=} opt_this The object to use as `this` in `callback`. + * @return {*} Callback return value. + * @template S + */ + forEachInExtent(extent, callback, opt_this) { + return this.forEach_(this.getInExtent(extent), callback, opt_this); + } + + + /** + * @param {Array.} values Values. + * @param {function(this: S, T): *} callback Callback. + * @param {S=} opt_this The object to use as `this` in `callback`. + * @private + * @return {*} Callback return value. + * @template S + */ + forEach_(values, callback, opt_this) { + let result; + for (let i = 0, l = values.length; i < l; i++) { + result = callback.call(opt_this, values[i]); + if (result) { + return result; + } + } + return result; + } + + + /** + * @return {boolean} Is empty. + */ + isEmpty() { + return isEmpty(this.items_); + } + + + /** + * Remove all values from the RBush. + */ + clear() { + this.rbush_.clear(); + this.items_ = {}; + } + + + /** + * @param {module:ol/extent~Extent=} opt_extent Extent. + * @return {module:ol/extent~Extent} Extent. + */ + getExtent(opt_extent) { + // FIXME add getExtent() to rbush + const data = this.rbush_.data; + return createOrUpdate(data.minX, data.minY, data.maxX, data.maxY, opt_extent); + } + + + /** + * @param {module:ol/structs/RBush} rbush R-Tree. + */ + concat(rbush) { + this.rbush_.load(rbush.rbush_.all()); + for (const i in rbush.items_) { + this.items_[i | 0] = rbush.items_[i | 0]; + } + } + +} + + export default RBush; diff --git a/src/ol/style/Atlas.js b/src/ol/style/Atlas.js index 83077c6273..27dba344e4 100644 --- a/src/ol/style/Atlas.js +++ b/src/ol/style/Atlas.js @@ -38,161 +38,160 @@ import {createCanvasContext2D} from '../dom.js'; * edges overlap when being rendered). To avoid this we add a * padding around each image. */ -const Atlas = function(size, space) { +class Atlas { + constructor(size, space) { + + /** + * @private + * @type {number} + */ + this.space_ = space; + + /** + * @private + * @type {Array.} + */ + this.emptyBlocks_ = [{x: 0, y: 0, width: size, height: size}]; + + /** + * @private + * @type {Object.} + */ + this.entries_ = {}; + + /** + * @private + * @type {CanvasRenderingContext2D} + */ + this.context_ = createCanvasContext2D(size, size); + + /** + * @private + * @type {HTMLCanvasElement} + */ + this.canvas_ = this.context_.canvas; + } + + /** + * @param {string} id The identifier of the entry to check. + * @return {?module:ol/style/Atlas~AtlasInfo} The atlas info. + */ + get(id) { + return this.entries_[id] || null; + } + + /** + * @param {string} id The identifier of the entry to add. + * @param {number} width The width. + * @param {number} height The height. + * @param {function(CanvasRenderingContext2D, number, number)} renderCallback + * Called to render the new image onto an atlas image. + * @param {Object=} opt_this Value to use as `this` when executing + * `renderCallback`. + * @return {?module:ol/style/Atlas~AtlasInfo} The position and atlas image for the entry. + */ + add(id, width, height, renderCallback, opt_this) { + for (let i = 0, ii = this.emptyBlocks_.length; i < ii; ++i) { + const block = this.emptyBlocks_[i]; + if (block.width >= width + this.space_ && + block.height >= height + this.space_) { + // we found a block that is big enough for our entry + const entry = { + offsetX: block.x + this.space_, + offsetY: block.y + this.space_, + image: this.canvas_ + }; + this.entries_[id] = entry; + + // render the image on the atlas image + renderCallback.call(opt_this, this.context_, + block.x + this.space_, block.y + this.space_); + + // split the block after the insertion, either horizontally or vertically + this.split_(i, block, width + this.space_, height + this.space_); + + return entry; + } + } + + // there is no space for the new entry in this atlas + return null; + } /** * @private - * @type {number} + * @param {number} index The index of the block. + * @param {module:ol/style/Atlas~AtlasBlock} block The block to split. + * @param {number} width The width of the entry to insert. + * @param {number} height The height of the entry to insert. */ - this.space_ = space; + split_(index, block, width, height) { + const deltaWidth = block.width - width; + const deltaHeight = block.height - height; - /** - * @private - * @type {Array.} - */ - this.emptyBlocks_ = [{x: 0, y: 0, width: size, height: size}]; + /** @type {module:ol/style/Atlas~AtlasBlock} */ + let newBlock1; + /** @type {module:ol/style/Atlas~AtlasBlock} */ + let newBlock2; - /** - * @private - * @type {Object.} - */ - this.entries_ = {}; - - /** - * @private - * @type {CanvasRenderingContext2D} - */ - this.context_ = createCanvasContext2D(size, size); - - /** - * @private - * @type {HTMLCanvasElement} - */ - this.canvas_ = this.context_.canvas; -}; - - -/** - * @param {string} id The identifier of the entry to check. - * @return {?module:ol/style/Atlas~AtlasInfo} The atlas info. - */ -Atlas.prototype.get = function(id) { - return this.entries_[id] || null; -}; - - -/** - * @param {string} id The identifier of the entry to add. - * @param {number} width The width. - * @param {number} height The height. - * @param {function(CanvasRenderingContext2D, number, number)} renderCallback - * Called to render the new image onto an atlas image. - * @param {Object=} opt_this Value to use as `this` when executing - * `renderCallback`. - * @return {?module:ol/style/Atlas~AtlasInfo} The position and atlas image for the entry. - */ -Atlas.prototype.add = function(id, width, height, renderCallback, opt_this) { - for (let i = 0, ii = this.emptyBlocks_.length; i < ii; ++i) { - const block = this.emptyBlocks_[i]; - if (block.width >= width + this.space_ && - block.height >= height + this.space_) { - // we found a block that is big enough for our entry - const entry = { - offsetX: block.x + this.space_, - offsetY: block.y + this.space_, - image: this.canvas_ + if (deltaWidth > deltaHeight) { + // split vertically + // block right of the inserted entry + newBlock1 = { + x: block.x + width, + y: block.y, + width: block.width - width, + height: block.height }; - this.entries_[id] = entry; - // render the image on the atlas image - renderCallback.call(opt_this, this.context_, - block.x + this.space_, block.y + this.space_); + // block below the inserted entry + newBlock2 = { + x: block.x, + y: block.y + height, + width: width, + height: block.height - height + }; + this.updateBlocks_(index, newBlock1, newBlock2); + } else { + // split horizontally + // block right of the inserted entry + newBlock1 = { + x: block.x + width, + y: block.y, + width: block.width - width, + height: height + }; - // split the block after the insertion, either horizontally or vertically - this.split_(i, block, width + this.space_, height + this.space_); - - return entry; + // block below the inserted entry + newBlock2 = { + x: block.x, + y: block.y + height, + width: block.width, + height: block.height - height + }; + this.updateBlocks_(index, newBlock1, newBlock2); } } - // there is no space for the new entry in this atlas - return null; -}; - - -/** - * @private - * @param {number} index The index of the block. - * @param {module:ol/style/Atlas~AtlasBlock} block The block to split. - * @param {number} width The width of the entry to insert. - * @param {number} height The height of the entry to insert. - */ -Atlas.prototype.split_ = function(index, block, width, height) { - const deltaWidth = block.width - width; - const deltaHeight = block.height - height; - - /** @type {module:ol/style/Atlas~AtlasBlock} */ - let newBlock1; - /** @type {module:ol/style/Atlas~AtlasBlock} */ - let newBlock2; - - if (deltaWidth > deltaHeight) { - // split vertically - // block right of the inserted entry - newBlock1 = { - x: block.x + width, - y: block.y, - width: block.width - width, - height: block.height - }; - - // block below the inserted entry - newBlock2 = { - x: block.x, - y: block.y + height, - width: width, - height: block.height - height - }; - this.updateBlocks_(index, newBlock1, newBlock2); - } else { - // split horizontally - // block right of the inserted entry - newBlock1 = { - x: block.x + width, - y: block.y, - width: block.width - width, - height: height - }; - - // block below the inserted entry - newBlock2 = { - x: block.x, - y: block.y + height, - width: block.width, - height: block.height - height - }; - this.updateBlocks_(index, newBlock1, newBlock2); + /** + * Remove the old block and insert new blocks at the same array position. + * The new blocks are inserted at the same position, so that splitted + * blocks (that are potentially smaller) are filled first. + * @private + * @param {number} index The index of the block to remove. + * @param {module:ol/style/Atlas~AtlasBlock} newBlock1 The 1st block to add. + * @param {module:ol/style/Atlas~AtlasBlock} newBlock2 The 2nd block to add. + */ + updateBlocks_(index, newBlock1, newBlock2) { + const args = [index, 1]; + if (newBlock1.width > 0 && newBlock1.height > 0) { + args.push(newBlock1); + } + if (newBlock2.width > 0 && newBlock2.height > 0) { + args.push(newBlock2); + } + this.emptyBlocks_.splice.apply(this.emptyBlocks_, args); } -}; +} - -/** - * Remove the old block and insert new blocks at the same array position. - * The new blocks are inserted at the same position, so that splitted - * blocks (that are potentially smaller) are filled first. - * @private - * @param {number} index The index of the block to remove. - * @param {module:ol/style/Atlas~AtlasBlock} newBlock1 The 1st block to add. - * @param {module:ol/style/Atlas~AtlasBlock} newBlock2 The 2nd block to add. - */ -Atlas.prototype.updateBlocks_ = function(index, newBlock1, newBlock2) { - const args = [index, 1]; - if (newBlock1.width > 0 && newBlock1.height > 0) { - args.push(newBlock1); - } - if (newBlock2.width > 0 && newBlock2.height > 0) { - args.push(newBlock2); - } - this.emptyBlocks_.splice.apply(this.emptyBlocks_, args); -}; export default Atlas; diff --git a/src/ol/style/AtlasManager.js b/src/ol/style/AtlasManager.js index cf0eb8f022..bcfb7e2592 100644 --- a/src/ol/style/AtlasManager.js +++ b/src/ol/style/AtlasManager.js @@ -58,199 +58,196 @@ const MAX_ATLAS_SIZE = -1; * @api * @param {module:ol/style/AtlasManager~Options=} opt_options Options. */ -const AtlasManager = function(opt_options) { +class AtlasManager { + constructor(opt_options) { - const options = opt_options || {}; + const options = opt_options || {}; - /** - * The size in pixels of the latest atlas image. - * @private - * @type {number} - */ - this.currentSize_ = options.initialSize !== undefined ? - options.initialSize : INITIAL_ATLAS_SIZE; + /** + * The size in pixels of the latest atlas image. + * @private + * @type {number} + */ + this.currentSize_ = options.initialSize !== undefined ? + options.initialSize : INITIAL_ATLAS_SIZE; - /** - * The maximum size in pixels of atlas images. - * @private - * @type {number} - */ - this.maxSize_ = options.maxSize !== undefined ? - options.maxSize : MAX_ATLAS_SIZE != -1 ? - MAX_ATLAS_SIZE : WEBGL_MAX_TEXTURE_SIZE !== undefined ? - WEBGL_MAX_TEXTURE_SIZE : 2048; + /** + * The maximum size in pixels of atlas images. + * @private + * @type {number} + */ + this.maxSize_ = options.maxSize !== undefined ? + options.maxSize : MAX_ATLAS_SIZE != -1 ? + MAX_ATLAS_SIZE : WEBGL_MAX_TEXTURE_SIZE !== undefined ? + WEBGL_MAX_TEXTURE_SIZE : 2048; - /** - * The size in pixels between images. - * @private - * @type {number} - */ - this.space_ = options.space !== undefined ? options.space : 1; + /** + * The size in pixels between images. + * @private + * @type {number} + */ + this.space_ = options.space !== undefined ? options.space : 1; - /** - * @private - * @type {Array.} - */ - this.atlases_ = [new Atlas(this.currentSize_, this.space_)]; + /** + * @private + * @type {Array.} + */ + this.atlases_ = [new Atlas(this.currentSize_, this.space_)]; - /** - * The size in pixels of the latest atlas image for hit-detection images. - * @private - * @type {number} - */ - this.currentHitSize_ = this.currentSize_; + /** + * The size in pixels of the latest atlas image for hit-detection images. + * @private + * @type {number} + */ + this.currentHitSize_ = this.currentSize_; - /** - * @private - * @type {Array.} - */ - this.hitAtlases_ = [new Atlas(this.currentHitSize_, this.space_)]; -}; - - -/** - * @param {string} id The identifier of the entry to check. - * @return {?module:ol/style/AtlasManager~AtlasManagerInfo} The position and atlas image for the - * entry, or `null` if the entry is not part of the atlas manager. - */ -AtlasManager.prototype.getInfo = function(id) { - /** @type {?module:ol/style/Atlas~AtlasInfo} */ - const info = this.getInfo_(this.atlases_, id); - - if (!info) { - return null; + /** + * @private + * @type {Array.} + */ + this.hitAtlases_ = [new Atlas(this.currentHitSize_, this.space_)]; } - const hitInfo = /** @type {module:ol/style/Atlas~AtlasInfo} */ (this.getInfo_(this.hitAtlases_, id)); - return this.mergeInfos_(info, hitInfo); -}; + /** + * @param {string} id The identifier of the entry to check. + * @return {?module:ol/style/AtlasManager~AtlasManagerInfo} The position and atlas image for the + * entry, or `null` if the entry is not part of the atlas manager. + */ + getInfo(id) { + /** @type {?module:ol/style/Atlas~AtlasInfo} */ + const info = this.getInfo_(this.atlases_, id); - -/** - * @private - * @param {Array.} atlases The atlases to search. - * @param {string} id The identifier of the entry to check. - * @return {?module:ol/style/Atlas~AtlasInfo} The position and atlas image for the entry, - * or `null` if the entry is not part of the atlases. - */ -AtlasManager.prototype.getInfo_ = function(atlases, id) { - for (let i = 0, ii = atlases.length; i < ii; ++i) { - const atlas = atlases[i]; - const info = atlas.get(id); - if (info) { - return info; + if (!info) { + return null; } - } - return null; -}; + const hitInfo = /** @type {module:ol/style/Atlas~AtlasInfo} */ (this.getInfo_(this.hitAtlases_, id)); - -/** - * @private - * @param {module:ol/style/Atlas~AtlasInfo} info The info for the real image. - * @param {module:ol/style/Atlas~AtlasInfo} hitInfo The info for the hit-detection - * image. - * @return {?module:ol/style/AtlasManager~AtlasManagerInfo} The position and atlas image for the - * entry, or `null` if the entry is not part of the atlases. - */ -AtlasManager.prototype.mergeInfos_ = function(info, hitInfo) { - return ( - /** @type {module:ol/style/AtlasManager~AtlasManagerInfo} */ ({ - offsetX: info.offsetX, - offsetY: info.offsetY, - image: info.image, - hitImage: hitInfo.image - }) - ); -}; - - -/** - * Add an image to the atlas manager. - * - * If an entry for the given id already exists, the entry will - * be overridden (but the space on the atlas graphic will not be freed). - * - * If `renderHitCallback` is provided, the image (or the hit-detection version - * of the image) will be rendered into a separate hit-detection atlas image. - * - * @param {string} id The identifier of the entry to add. - * @param {number} width The width. - * @param {number} height The height. - * @param {function(CanvasRenderingContext2D, number, number)} renderCallback - * Called to render the new image onto an atlas image. - * @param {function(CanvasRenderingContext2D, number, number)=} - * opt_renderHitCallback Called to render a hit-detection image onto a hit - * detection atlas image. - * @param {Object=} opt_this Value to use as `this` when executing - * `renderCallback` and `renderHitCallback`. - * @return {?module:ol/style/AtlasManager~AtlasManagerInfo} The position and atlas image for the - * entry, or `null` if the image is too big. - */ -AtlasManager.prototype.add = function(id, width, height, - renderCallback, opt_renderHitCallback, opt_this) { - if (width + this.space_ > this.maxSize_ || - height + this.space_ > this.maxSize_) { - return null; + return this.mergeInfos_(info, hitInfo); } - /** @type {?module:ol/style/Atlas~AtlasInfo} */ - const info = this.add_(false, id, width, height, renderCallback, opt_this); - if (!info) { - return null; - } - - // even if no hit-detection entry is requested, we insert a fake entry into - // the hit-detection atlas, to make sure that the offset is the same for - // the original image and the hit-detection image. - const renderHitCallback = opt_renderHitCallback !== undefined ? - opt_renderHitCallback : UNDEFINED; - - const hitInfo = /** @type {module:ol/style/Atlas~AtlasInfo} */ (this.add_(true, - id, width, height, renderHitCallback, opt_this)); - - return this.mergeInfos_(info, hitInfo); -}; - - -/** - * @private - * @param {boolean} isHitAtlas If the hit-detection atlases are used. - * @param {string} id The identifier of the entry to add. - * @param {number} width The width. - * @param {number} height The height. - * @param {function(CanvasRenderingContext2D, number, number)} renderCallback - * Called to render the new image onto an atlas image. - * @param {Object=} opt_this Value to use as `this` when executing - * `renderCallback` and `renderHitCallback`. - * @return {?module:ol/style/Atlas~AtlasInfo} The position and atlas image for the entry, - * or `null` if the image is too big. - */ -AtlasManager.prototype.add_ = function(isHitAtlas, id, width, height, renderCallback, opt_this) { - const atlases = (isHitAtlas) ? this.hitAtlases_ : this.atlases_; - let atlas, info, i, ii; - for (i = 0, ii = atlases.length; i < ii; ++i) { - atlas = atlases[i]; - info = atlas.add(id, width, height, renderCallback, opt_this); - if (info) { - return info; - } else if (!info && i === ii - 1) { - // the entry could not be added to one of the existing atlases, - // create a new atlas that is twice as big and try to add to this one. - let size; - if (isHitAtlas) { - size = Math.min(this.currentHitSize_ * 2, this.maxSize_); - this.currentHitSize_ = size; - } else { - size = Math.min(this.currentSize_ * 2, this.maxSize_); - this.currentSize_ = size; + /** + * @private + * @param {Array.} atlases The atlases to search. + * @param {string} id The identifier of the entry to check. + * @return {?module:ol/style/Atlas~AtlasInfo} The position and atlas image for the entry, + * or `null` if the entry is not part of the atlases. + */ + getInfo_(atlases, id) { + for (let i = 0, ii = atlases.length; i < ii; ++i) { + const atlas = atlases[i]; + const info = atlas.get(id); + if (info) { + return info; } - atlas = new Atlas(size, this.space_); - atlases.push(atlas); - // run the loop another time - ++ii; } + return null; } - return null; -}; + + /** + * @private + * @param {module:ol/style/Atlas~AtlasInfo} info The info for the real image. + * @param {module:ol/style/Atlas~AtlasInfo} hitInfo The info for the hit-detection + * image. + * @return {?module:ol/style/AtlasManager~AtlasManagerInfo} The position and atlas image for the + * entry, or `null` if the entry is not part of the atlases. + */ + mergeInfos_(info, hitInfo) { + return ( + /** @type {module:ol/style/AtlasManager~AtlasManagerInfo} */ ({ + offsetX: info.offsetX, + offsetY: info.offsetY, + image: info.image, + hitImage: hitInfo.image + }) + ); + } + + /** + * Add an image to the atlas manager. + * + * If an entry for the given id already exists, the entry will + * be overridden (but the space on the atlas graphic will not be freed). + * + * If `renderHitCallback` is provided, the image (or the hit-detection version + * of the image) will be rendered into a separate hit-detection atlas image. + * + * @param {string} id The identifier of the entry to add. + * @param {number} width The width. + * @param {number} height The height. + * @param {function(CanvasRenderingContext2D, number, number)} renderCallback + * Called to render the new image onto an atlas image. + * @param {function(CanvasRenderingContext2D, number, number)=} + * opt_renderHitCallback Called to render a hit-detection image onto a hit + * detection atlas image. + * @param {Object=} opt_this Value to use as `this` when executing + * `renderCallback` and `renderHitCallback`. + * @return {?module:ol/style/AtlasManager~AtlasManagerInfo} The position and atlas image for the + * entry, or `null` if the image is too big. + */ + add(id, width, height, renderCallback, opt_renderHitCallback, opt_this) { + if (width + this.space_ > this.maxSize_ || + height + this.space_ > this.maxSize_) { + return null; + } + + /** @type {?module:ol/style/Atlas~AtlasInfo} */ + const info = this.add_(false, id, width, height, renderCallback, opt_this); + if (!info) { + return null; + } + + // even if no hit-detection entry is requested, we insert a fake entry into + // the hit-detection atlas, to make sure that the offset is the same for + // the original image and the hit-detection image. + const renderHitCallback = opt_renderHitCallback !== undefined ? + opt_renderHitCallback : UNDEFINED; + + const hitInfo = /** @type {module:ol/style/Atlas~AtlasInfo} */ (this.add_(true, + id, width, height, renderHitCallback, opt_this)); + + return this.mergeInfos_(info, hitInfo); + } + + /** + * @private + * @param {boolean} isHitAtlas If the hit-detection atlases are used. + * @param {string} id The identifier of the entry to add. + * @param {number} width The width. + * @param {number} height The height. + * @param {function(CanvasRenderingContext2D, number, number)} renderCallback + * Called to render the new image onto an atlas image. + * @param {Object=} opt_this Value to use as `this` when executing + * `renderCallback` and `renderHitCallback`. + * @return {?module:ol/style/Atlas~AtlasInfo} The position and atlas image for the entry, + * or `null` if the image is too big. + */ + add_(isHitAtlas, id, width, height, renderCallback, opt_this) { + const atlases = (isHitAtlas) ? this.hitAtlases_ : this.atlases_; + let atlas, info, i, ii; + for (i = 0, ii = atlases.length; i < ii; ++i) { + atlas = atlases[i]; + info = atlas.add(id, width, height, renderCallback, opt_this); + if (info) { + return info; + } else if (!info && i === ii - 1) { + // the entry could not be added to one of the existing atlases, + // create a new atlas that is twice as big and try to add to this one. + let size; + if (isHitAtlas) { + size = Math.min(this.currentHitSize_ * 2, this.maxSize_); + this.currentHitSize_ = size; + } else { + size = Math.min(this.currentSize_ * 2, this.maxSize_); + this.currentSize_ = size; + } + atlas = new Atlas(size, this.space_); + atlases.push(atlas); + // run the loop another time + ++ii; + } + } + return null; + } +} + export default AtlasManager; diff --git a/src/ol/style/Circle.js b/src/ol/style/Circle.js index 912f07b79d..0340d9c1fa 100644 --- a/src/ol/style/Circle.js +++ b/src/ol/style/Circle.js @@ -29,52 +29,54 @@ import RegularShape from '../style/RegularShape.js'; * @extends {module:ol/style/RegularShape} * @api */ -const CircleStyle = function(opt_options) { +class CircleStyle { + constructor(opt_options) { - const options = opt_options || {}; + const options = opt_options || {}; - RegularShape.call(this, { - points: Infinity, - fill: options.fill, - radius: options.radius, - snapToPixel: options.snapToPixel, - stroke: options.stroke, - atlasManager: options.atlasManager - }); + RegularShape.call(this, { + points: Infinity, + fill: options.fill, + radius: options.radius, + snapToPixel: options.snapToPixel, + stroke: options.stroke, + atlasManager: options.atlasManager + }); -}; + } + + /** + * Clones the style. If an atlasmanager was provided to the original style it will be used in the cloned style, too. + * @return {module:ol/style/Circle} The cloned style. + * @override + * @api + */ + clone() { + const style = new CircleStyle({ + fill: this.getFill() ? this.getFill().clone() : undefined, + stroke: this.getStroke() ? this.getStroke().clone() : undefined, + radius: this.getRadius(), + snapToPixel: this.getSnapToPixel(), + atlasManager: this.atlasManager_ + }); + style.setOpacity(this.getOpacity()); + style.setScale(this.getScale()); + return style; + } + + /** + * Set the circle radius. + * + * @param {number} radius Circle radius. + * @api + */ + setRadius(radius) { + this.radius_ = radius; + this.render_(this.atlasManager_); + } +} inherits(CircleStyle, RegularShape); -/** - * Clones the style. If an atlasmanager was provided to the original style it will be used in the cloned style, too. - * @return {module:ol/style/Circle} The cloned style. - * @override - * @api - */ -CircleStyle.prototype.clone = function() { - const style = new CircleStyle({ - fill: this.getFill() ? this.getFill().clone() : undefined, - stroke: this.getStroke() ? this.getStroke().clone() : undefined, - radius: this.getRadius(), - snapToPixel: this.getSnapToPixel(), - atlasManager: this.atlasManager_ - }); - style.setOpacity(this.getOpacity()); - style.setScale(this.getScale()); - return style; -}; - - -/** - * Set the circle radius. - * - * @param {number} radius Circle radius. - * @api - */ -CircleStyle.prototype.setRadius = function(radius) { - this.radius_ = radius; - this.render_(this.atlasManager_); -}; export default CircleStyle; diff --git a/src/ol/style/Fill.js b/src/ol/style/Fill.js index ef3a49af48..9a4ba82cee 100644 --- a/src/ol/style/Fill.js +++ b/src/ol/style/Fill.js @@ -21,74 +21,73 @@ import {asString} from '../color.js'; * @param {module:ol/style/Fill~Options=} opt_options Options. * @api */ -const Fill = function(opt_options) { +class Fill { + constructor(opt_options) { - const options = opt_options || {}; + const options = opt_options || {}; - /** - * @private - * @type {module:ol/color~Color|module:ol/colorlike~ColorLike} - */ - this.color_ = options.color !== undefined ? options.color : null; + /** + * @private + * @type {module:ol/color~Color|module:ol/colorlike~ColorLike} + */ + this.color_ = options.color !== undefined ? options.color : null; - /** - * @private - * @type {string|undefined} - */ - this.checksum_ = undefined; -}; - - -/** - * Clones the style. The color is not cloned if it is an {@link module:ol/colorlike~ColorLike}. - * @return {module:ol/style/Fill} The cloned style. - * @api - */ -Fill.prototype.clone = function() { - const color = this.getColor(); - return new Fill({ - color: (color && color.slice) ? color.slice() : color || undefined - }); -}; - - -/** - * Get the fill color. - * @return {module:ol/color~Color|module:ol/colorlike~ColorLike} Color. - * @api - */ -Fill.prototype.getColor = function() { - return this.color_; -}; - - -/** - * Set the color. - * - * @param {module:ol/color~Color|module:ol/colorlike~ColorLike} color Color. - * @api - */ -Fill.prototype.setColor = function(color) { - this.color_ = color; - this.checksum_ = undefined; -}; - - -/** - * @return {string} The checksum. - */ -Fill.prototype.getChecksum = function() { - if (this.checksum_ === undefined) { - if ( - this.color_ instanceof CanvasPattern || - this.color_ instanceof CanvasGradient - ) { - this.checksum_ = getUid(this.color_).toString(); - } else { - this.checksum_ = 'f' + (this.color_ ? asString(this.color_) : '-'); - } + /** + * @private + * @type {string|undefined} + */ + this.checksum_ = undefined; } - return this.checksum_; -}; + /** + * Clones the style. The color is not cloned if it is an {@link module:ol/colorlike~ColorLike}. + * @return {module:ol/style/Fill} The cloned style. + * @api + */ + clone() { + const color = this.getColor(); + return new Fill({ + color: (color && color.slice) ? color.slice() : color || undefined + }); + } + + /** + * Get the fill color. + * @return {module:ol/color~Color|module:ol/colorlike~ColorLike} Color. + * @api + */ + getColor() { + return this.color_; + } + + /** + * Set the color. + * + * @param {module:ol/color~Color|module:ol/colorlike~ColorLike} color Color. + * @api + */ + setColor(color) { + this.color_ = color; + this.checksum_ = undefined; + } + + /** + * @return {string} The checksum. + */ + getChecksum() { + if (this.checksum_ === undefined) { + if ( + this.color_ instanceof CanvasPattern || + this.color_ instanceof CanvasGradient + ) { + this.checksum_ = getUid(this.color_).toString(); + } else { + this.checksum_ = 'f' + (this.color_ ? asString(this.color_) : '-'); + } + } + + return this.checksum_; + } +} + export default Fill; diff --git a/src/ol/style/Icon.js b/src/ol/style/Icon.js index 3f48ca95a0..50f60e3cc7 100644 --- a/src/ol/style/Icon.js +++ b/src/ol/style/Icon.js @@ -61,374 +61,364 @@ import ImageStyle from '../style/Image.js'; * @extends {module:ol/style/Image} * @api */ -const Icon = function(opt_options) { +class Icon { + constructor(opt_options) { - const options = opt_options || {}; + const options = opt_options || {}; - /** - * @private - * @type {Array.} - */ - this.anchor_ = options.anchor !== undefined ? options.anchor : [0.5, 0.5]; + /** + * @private + * @type {Array.} + */ + this.anchor_ = options.anchor !== undefined ? options.anchor : [0.5, 0.5]; - /** - * @private - * @type {Array.} - */ - this.normalizedAnchor_ = null; + /** + * @private + * @type {Array.} + */ + this.normalizedAnchor_ = null; - /** - * @private - * @type {module:ol/style/IconOrigin} - */ - this.anchorOrigin_ = options.anchorOrigin !== undefined ? - options.anchorOrigin : IconOrigin.TOP_LEFT; + /** + * @private + * @type {module:ol/style/IconOrigin} + */ + this.anchorOrigin_ = options.anchorOrigin !== undefined ? + options.anchorOrigin : IconOrigin.TOP_LEFT; - /** - * @private - * @type {module:ol/style/IconAnchorUnits} - */ - this.anchorXUnits_ = options.anchorXUnits !== undefined ? - options.anchorXUnits : IconAnchorUnits.FRACTION; + /** + * @private + * @type {module:ol/style/IconAnchorUnits} + */ + this.anchorXUnits_ = options.anchorXUnits !== undefined ? + options.anchorXUnits : IconAnchorUnits.FRACTION; - /** - * @private - * @type {module:ol/style/IconAnchorUnits} - */ - this.anchorYUnits_ = options.anchorYUnits !== undefined ? - options.anchorYUnits : IconAnchorUnits.FRACTION; + /** + * @private + * @type {module:ol/style/IconAnchorUnits} + */ + this.anchorYUnits_ = options.anchorYUnits !== undefined ? + options.anchorYUnits : IconAnchorUnits.FRACTION; - /** - * @private - * @type {?string} - */ - this.crossOrigin_ = - options.crossOrigin !== undefined ? options.crossOrigin : null; + /** + * @private + * @type {?string} + */ + this.crossOrigin_ = + options.crossOrigin !== undefined ? options.crossOrigin : null; - /** - * @type {HTMLImageElement|HTMLCanvasElement} - */ - const image = options.img !== undefined ? options.img : null; + /** + * @type {HTMLImageElement|HTMLCanvasElement} + */ + const image = options.img !== undefined ? options.img : null; - /** - * @type {module:ol/size~Size} - */ - const imgSize = options.imgSize !== undefined ? options.imgSize : null; + /** + * @type {module:ol/size~Size} + */ + const imgSize = options.imgSize !== undefined ? options.imgSize : null; - /** - * @type {string|undefined} - */ - let src = options.src; + /** + * @type {string|undefined} + */ + let src = options.src; - assert(!(src !== undefined && image), - 4); // `image` and `src` cannot be provided at the same time - assert(!image || (image && imgSize), - 5); // `imgSize` must be set when `image` is provided + assert(!(src !== undefined && image), + 4); // `image` and `src` cannot be provided at the same time + assert(!image || (image && imgSize), + 5); // `imgSize` must be set when `image` is provided + + if ((src === undefined || src.length === 0) && image) { + src = image.src || getUid(image).toString(); + } + assert(src !== undefined && src.length > 0, + 6); // A defined and non-empty `src` or `image` must be provided + + /** + * @type {module:ol/ImageState} + */ + const imageState = options.src !== undefined ? + ImageState.IDLE : ImageState.LOADED; + + /** + * @private + * @type {module:ol/color~Color} + */ + this.color_ = options.color !== undefined ? asArray(options.color) : null; + + /** + * @private + * @type {module:ol/style/IconImage} + */ + this.iconImage_ = getIconImage( + image, /** @type {string} */ (src), imgSize, this.crossOrigin_, imageState, this.color_); + + /** + * @private + * @type {Array.} + */ + this.offset_ = options.offset !== undefined ? options.offset : [0, 0]; + + /** + * @private + * @type {module:ol/style/IconOrigin} + */ + this.offsetOrigin_ = options.offsetOrigin !== undefined ? + options.offsetOrigin : IconOrigin.TOP_LEFT; + + /** + * @private + * @type {Array.} + */ + this.origin_ = null; + + /** + * @private + * @type {module:ol/size~Size} + */ + this.size_ = options.size !== undefined ? options.size : null; + + /** + * @type {number} + */ + const opacity = options.opacity !== undefined ? options.opacity : 1; + + /** + * @type {boolean} + */ + const rotateWithView = options.rotateWithView !== undefined ? + options.rotateWithView : false; + + /** + * @type {number} + */ + const rotation = options.rotation !== undefined ? options.rotation : 0; + + /** + * @type {number} + */ + const scale = options.scale !== undefined ? options.scale : 1; + + /** + * @type {boolean} + */ + const snapToPixel = options.snapToPixel !== undefined ? + options.snapToPixel : true; + + ImageStyle.call(this, { + opacity: opacity, + rotation: rotation, + scale: scale, + snapToPixel: snapToPixel, + rotateWithView: rotateWithView + }); - if ((src === undefined || src.length === 0) && image) { - src = image.src || getUid(image).toString(); } - assert(src !== undefined && src.length > 0, - 6); // A defined and non-empty `src` or `image` must be provided /** - * @type {module:ol/ImageState} + * Clones the style. The underlying Image/HTMLCanvasElement is not cloned. + * @return {module:ol/style/Icon} The cloned style. + * @api */ - const imageState = options.src !== undefined ? - ImageState.IDLE : ImageState.LOADED; + clone() { + return new Icon({ + anchor: this.anchor_.slice(), + anchorOrigin: this.anchorOrigin_, + anchorXUnits: this.anchorXUnits_, + anchorYUnits: this.anchorYUnits_, + crossOrigin: this.crossOrigin_, + color: (this.color_ && this.color_.slice) ? this.color_.slice() : this.color_ || undefined, + src: this.getSrc(), + offset: this.offset_.slice(), + offsetOrigin: this.offsetOrigin_, + size: this.size_ !== null ? this.size_.slice() : undefined, + opacity: this.getOpacity(), + scale: this.getScale(), + snapToPixel: this.getSnapToPixel(), + rotation: this.getRotation(), + rotateWithView: this.getRotateWithView() + }); + } /** - * @private - * @type {module:ol/color~Color} + * @inheritDoc + * @api */ - this.color_ = options.color !== undefined ? asArray(options.color) : null; + getAnchor() { + if (this.normalizedAnchor_) { + return this.normalizedAnchor_; + } + let anchor = this.anchor_; + const size = this.getSize(); + if (this.anchorXUnits_ == IconAnchorUnits.FRACTION || + this.anchorYUnits_ == IconAnchorUnits.FRACTION) { + if (!size) { + return null; + } + anchor = this.anchor_.slice(); + if (this.anchorXUnits_ == IconAnchorUnits.FRACTION) { + anchor[0] *= size[0]; + } + if (this.anchorYUnits_ == IconAnchorUnits.FRACTION) { + anchor[1] *= size[1]; + } + } + + if (this.anchorOrigin_ != IconOrigin.TOP_LEFT) { + if (!size) { + return null; + } + if (anchor === this.anchor_) { + anchor = this.anchor_.slice(); + } + if (this.anchorOrigin_ == IconOrigin.TOP_RIGHT || + this.anchorOrigin_ == IconOrigin.BOTTOM_RIGHT) { + anchor[0] = -anchor[0] + size[0]; + } + if (this.anchorOrigin_ == IconOrigin.BOTTOM_LEFT || + this.anchorOrigin_ == IconOrigin.BOTTOM_RIGHT) { + anchor[1] = -anchor[1] + size[1]; + } + } + this.normalizedAnchor_ = anchor; + return this.normalizedAnchor_; + } /** - * @private - * @type {module:ol/style/IconImage} + * Set the anchor point. The anchor determines the center point for the + * symbolizer. + * + * @param {Array.} anchor Anchor. + * @api */ - this.iconImage_ = getIconImage( - image, /** @type {string} */ (src), imgSize, this.crossOrigin_, imageState, this.color_); + setAnchor(anchor) { + this.anchor_ = anchor; + this.normalizedAnchor_ = null; + } /** - * @private - * @type {Array.} + * Get the icon color. + * @return {module:ol/color~Color} Color. + * @api */ - this.offset_ = options.offset !== undefined ? options.offset : [0, 0]; + getColor() { + return this.color_; + } /** - * @private - * @type {module:ol/style/IconOrigin} + * Get the image icon. + * @param {number} pixelRatio Pixel ratio. + * @return {HTMLImageElement|HTMLCanvasElement} Image or Canvas element. + * @override + * @api */ - this.offsetOrigin_ = options.offsetOrigin !== undefined ? - options.offsetOrigin : IconOrigin.TOP_LEFT; + getImage(pixelRatio) { + return this.iconImage_.getImage(pixelRatio); + } /** - * @private - * @type {Array.} + * @override */ - this.origin_ = null; + getImageSize() { + return this.iconImage_.getSize(); + } /** - * @private - * @type {module:ol/size~Size} + * @override */ - this.size_ = options.size !== undefined ? options.size : null; + getHitDetectionImageSize() { + return this.getImageSize(); + } /** - * @type {number} + * @override */ - const opacity = options.opacity !== undefined ? options.opacity : 1; + getImageState() { + return this.iconImage_.getImageState(); + } /** - * @type {boolean} + * @override */ - const rotateWithView = options.rotateWithView !== undefined ? - options.rotateWithView : false; + getHitDetectionImage(pixelRatio) { + return this.iconImage_.getHitDetectionImage(pixelRatio); + } /** - * @type {number} + * @inheritDoc + * @api */ - const rotation = options.rotation !== undefined ? options.rotation : 0; + getOrigin() { + if (this.origin_) { + return this.origin_; + } + let offset = this.offset_; + + if (this.offsetOrigin_ != IconOrigin.TOP_LEFT) { + const size = this.getSize(); + const iconImageSize = this.iconImage_.getSize(); + if (!size || !iconImageSize) { + return null; + } + offset = offset.slice(); + if (this.offsetOrigin_ == IconOrigin.TOP_RIGHT || + this.offsetOrigin_ == IconOrigin.BOTTOM_RIGHT) { + offset[0] = iconImageSize[0] - size[0] - offset[0]; + } + if (this.offsetOrigin_ == IconOrigin.BOTTOM_LEFT || + this.offsetOrigin_ == IconOrigin.BOTTOM_RIGHT) { + offset[1] = iconImageSize[1] - size[1] - offset[1]; + } + } + this.origin_ = offset; + return this.origin_; + } /** - * @type {number} + * Get the image URL. + * @return {string|undefined} Image src. + * @api */ - const scale = options.scale !== undefined ? options.scale : 1; + getSrc() { + return this.iconImage_.getSrc(); + } /** - * @type {boolean} + * @inheritDoc + * @api */ - const snapToPixel = options.snapToPixel !== undefined ? - options.snapToPixel : true; + getSize() { + return !this.size_ ? this.iconImage_.getSize() : this.size_; + } - ImageStyle.call(this, { - opacity: opacity, - rotation: rotation, - scale: scale, - snapToPixel: snapToPixel, - rotateWithView: rotateWithView - }); + /** + * @override + */ + listenImageChange(listener, thisArg) { + return listen(this.iconImage_, EventType.CHANGE, + listener, thisArg); + } -}; + /** + * Load not yet loaded URI. + * When rendering a feature with an icon style, the vector renderer will + * automatically call this method. However, you might want to call this + * method yourself for preloading or other purposes. + * @override + * @api + */ + load() { + this.iconImage_.load(); + } + + /** + * @override + */ + unlistenImageChange(listener, thisArg) { + unlisten(this.iconImage_, EventType.CHANGE, + listener, thisArg); + } +} inherits(Icon, ImageStyle); -/** - * Clones the style. The underlying Image/HTMLCanvasElement is not cloned. - * @return {module:ol/style/Icon} The cloned style. - * @api - */ -Icon.prototype.clone = function() { - return new Icon({ - anchor: this.anchor_.slice(), - anchorOrigin: this.anchorOrigin_, - anchorXUnits: this.anchorXUnits_, - anchorYUnits: this.anchorYUnits_, - crossOrigin: this.crossOrigin_, - color: (this.color_ && this.color_.slice) ? this.color_.slice() : this.color_ || undefined, - src: this.getSrc(), - offset: this.offset_.slice(), - offsetOrigin: this.offsetOrigin_, - size: this.size_ !== null ? this.size_.slice() : undefined, - opacity: this.getOpacity(), - scale: this.getScale(), - snapToPixel: this.getSnapToPixel(), - rotation: this.getRotation(), - rotateWithView: this.getRotateWithView() - }); -}; - - -/** - * @inheritDoc - * @api - */ -Icon.prototype.getAnchor = function() { - if (this.normalizedAnchor_) { - return this.normalizedAnchor_; - } - let anchor = this.anchor_; - const size = this.getSize(); - if (this.anchorXUnits_ == IconAnchorUnits.FRACTION || - this.anchorYUnits_ == IconAnchorUnits.FRACTION) { - if (!size) { - return null; - } - anchor = this.anchor_.slice(); - if (this.anchorXUnits_ == IconAnchorUnits.FRACTION) { - anchor[0] *= size[0]; - } - if (this.anchorYUnits_ == IconAnchorUnits.FRACTION) { - anchor[1] *= size[1]; - } - } - - if (this.anchorOrigin_ != IconOrigin.TOP_LEFT) { - if (!size) { - return null; - } - if (anchor === this.anchor_) { - anchor = this.anchor_.slice(); - } - if (this.anchorOrigin_ == IconOrigin.TOP_RIGHT || - this.anchorOrigin_ == IconOrigin.BOTTOM_RIGHT) { - anchor[0] = -anchor[0] + size[0]; - } - if (this.anchorOrigin_ == IconOrigin.BOTTOM_LEFT || - this.anchorOrigin_ == IconOrigin.BOTTOM_RIGHT) { - anchor[1] = -anchor[1] + size[1]; - } - } - this.normalizedAnchor_ = anchor; - return this.normalizedAnchor_; -}; - -/** - * Set the anchor point. The anchor determines the center point for the - * symbolizer. - * - * @param {Array.} anchor Anchor. - * @api - */ -Icon.prototype.setAnchor = function(anchor) { - this.anchor_ = anchor; - this.normalizedAnchor_ = null; -}; - - -/** - * Get the icon color. - * @return {module:ol/color~Color} Color. - * @api - */ -Icon.prototype.getColor = function() { - return this.color_; -}; - - -/** - * Get the image icon. - * @param {number} pixelRatio Pixel ratio. - * @return {HTMLImageElement|HTMLCanvasElement} Image or Canvas element. - * @override - * @api - */ -Icon.prototype.getImage = function(pixelRatio) { - return this.iconImage_.getImage(pixelRatio); -}; - - -/** - * @override - */ -Icon.prototype.getImageSize = function() { - return this.iconImage_.getSize(); -}; - - -/** - * @override - */ -Icon.prototype.getHitDetectionImageSize = function() { - return this.getImageSize(); -}; - - -/** - * @override - */ -Icon.prototype.getImageState = function() { - return this.iconImage_.getImageState(); -}; - - -/** - * @override - */ -Icon.prototype.getHitDetectionImage = function(pixelRatio) { - return this.iconImage_.getHitDetectionImage(pixelRatio); -}; - - -/** - * @inheritDoc - * @api - */ -Icon.prototype.getOrigin = function() { - if (this.origin_) { - return this.origin_; - } - let offset = this.offset_; - - if (this.offsetOrigin_ != IconOrigin.TOP_LEFT) { - const size = this.getSize(); - const iconImageSize = this.iconImage_.getSize(); - if (!size || !iconImageSize) { - return null; - } - offset = offset.slice(); - if (this.offsetOrigin_ == IconOrigin.TOP_RIGHT || - this.offsetOrigin_ == IconOrigin.BOTTOM_RIGHT) { - offset[0] = iconImageSize[0] - size[0] - offset[0]; - } - if (this.offsetOrigin_ == IconOrigin.BOTTOM_LEFT || - this.offsetOrigin_ == IconOrigin.BOTTOM_RIGHT) { - offset[1] = iconImageSize[1] - size[1] - offset[1]; - } - } - this.origin_ = offset; - return this.origin_; -}; - - -/** - * Get the image URL. - * @return {string|undefined} Image src. - * @api - */ -Icon.prototype.getSrc = function() { - return this.iconImage_.getSrc(); -}; - - -/** - * @inheritDoc - * @api - */ -Icon.prototype.getSize = function() { - return !this.size_ ? this.iconImage_.getSize() : this.size_; -}; - - -/** - * @override - */ -Icon.prototype.listenImageChange = function(listener, thisArg) { - return listen(this.iconImage_, EventType.CHANGE, - listener, thisArg); -}; - - -/** - * Load not yet loaded URI. - * When rendering a feature with an icon style, the vector renderer will - * automatically call this method. However, you might want to call this - * method yourself for preloading or other purposes. - * @override - * @api - */ -Icon.prototype.load = function() { - this.iconImage_.load(); -}; - - -/** - * @override - */ -Icon.prototype.unlistenImageChange = function(listener, thisArg) { - unlisten(this.iconImage_, EventType.CHANGE, - listener, thisArg); -}; export default Icon; diff --git a/src/ol/style/IconImage.js b/src/ol/style/IconImage.js index 1f73fc88ea..4f677a6b51 100644 --- a/src/ol/style/IconImage.js +++ b/src/ol/style/IconImage.js @@ -19,74 +19,227 @@ import {shared as iconImageCache} from '../style/IconImageCache.js'; * @param {module:ol/color~Color} color Color. * @extends {module:ol/events/EventTarget} */ -const IconImage = function(image, src, size, crossOrigin, imageState, color) { +class IconImage { + constructor(image, src, size, crossOrigin, imageState, color) { - EventTarget.call(this); + EventTarget.call(this); - /** - * @private - * @type {HTMLImageElement|HTMLCanvasElement} - */ - this.hitDetectionImage_ = null; + /** + * @private + * @type {HTMLImageElement|HTMLCanvasElement} + */ + this.hitDetectionImage_ = null; - /** - * @private - * @type {HTMLImageElement|HTMLCanvasElement} - */ - this.image_ = !image ? new Image() : image; + /** + * @private + * @type {HTMLImageElement|HTMLCanvasElement} + */ + this.image_ = !image ? new Image() : image; + + if (crossOrigin !== null) { + this.image_.crossOrigin = crossOrigin; + } + + /** + * @private + * @type {HTMLCanvasElement} + */ + this.canvas_ = color ? + /** @type {HTMLCanvasElement} */ (document.createElement('CANVAS')) : + null; + + /** + * @private + * @type {module:ol/color~Color} + */ + this.color_ = color; + + /** + * @private + * @type {Array.} + */ + this.imageListenerKeys_ = null; + + /** + * @private + * @type {module:ol/ImageState} + */ + this.imageState_ = imageState; + + /** + * @private + * @type {module:ol/size~Size} + */ + this.size_ = size; + + /** + * @private + * @type {string|undefined} + */ + this.src_ = src; + + /** + * @private + * @type {boolean} + */ + this.tainting_ = false; + if (this.imageState_ == ImageState.LOADED) { + this.determineTainting_(); + } - if (crossOrigin !== null) { - this.image_.crossOrigin = crossOrigin; } /** * @private - * @type {HTMLCanvasElement} */ - this.canvas_ = color ? - /** @type {HTMLCanvasElement} */ (document.createElement('CANVAS')) : - null; + determineTainting_() { + const context = createCanvasContext2D(1, 1); + try { + context.drawImage(this.image_, 0, 0); + context.getImageData(0, 0, 1, 1); + } catch (e) { + this.tainting_ = true; + } + } /** * @private - * @type {module:ol/color~Color} */ - this.color_ = color; + dispatchChangeEvent_() { + this.dispatchEvent(EventType.CHANGE); + } /** * @private - * @type {Array.} */ - this.imageListenerKeys_ = null; + handleImageError_() { + this.imageState_ = ImageState.ERROR; + this.unlistenImage_(); + this.dispatchChangeEvent_(); + } /** * @private - * @type {module:ol/ImageState} */ - this.imageState_ = imageState; - - /** - * @private - * @type {module:ol/size~Size} - */ - this.size_ = size; - - /** - * @private - * @type {string|undefined} - */ - this.src_ = src; - - /** - * @private - * @type {boolean} - */ - this.tainting_ = false; - if (this.imageState_ == ImageState.LOADED) { + handleImageLoad_() { + this.imageState_ = ImageState.LOADED; + if (this.size_) { + this.image_.width = this.size_[0]; + this.image_.height = this.size_[1]; + } + this.size_ = [this.image_.width, this.image_.height]; + this.unlistenImage_(); this.determineTainting_(); + this.replaceColor_(); + this.dispatchChangeEvent_(); } -}; + /** + * @param {number} pixelRatio Pixel ratio. + * @return {HTMLImageElement|HTMLCanvasElement} Image or Canvas element. + */ + getImage(pixelRatio) { + return this.canvas_ ? this.canvas_ : this.image_; + } + + /** + * @return {module:ol/ImageState} Image state. + */ + getImageState() { + return this.imageState_; + } + + /** + * @param {number} pixelRatio Pixel ratio. + * @return {HTMLImageElement|HTMLCanvasElement} Image element. + */ + getHitDetectionImage(pixelRatio) { + if (!this.hitDetectionImage_) { + if (this.tainting_) { + const width = this.size_[0]; + const height = this.size_[1]; + const context = createCanvasContext2D(width, height); + context.fillRect(0, 0, width, height); + this.hitDetectionImage_ = context.canvas; + } else { + this.hitDetectionImage_ = this.image_; + } + } + return this.hitDetectionImage_; + } + + /** + * @return {module:ol/size~Size} Image size. + */ + getSize() { + return this.size_; + } + + /** + * @return {string|undefined} Image src. + */ + getSrc() { + return this.src_; + } + + /** + * Load not yet loaded URI. + */ + load() { + if (this.imageState_ == ImageState.IDLE) { + this.imageState_ = ImageState.LOADING; + this.imageListenerKeys_ = [ + listenOnce(this.image_, EventType.ERROR, + this.handleImageError_, this), + listenOnce(this.image_, EventType.LOAD, + this.handleImageLoad_, this) + ]; + try { + this.image_.src = this.src_; + } catch (e) { + this.handleImageError_(); + } + } + } + + /** + * @private + */ + replaceColor_() { + if (this.tainting_ || this.color_ === null) { + return; + } + + this.canvas_.width = this.image_.width; + this.canvas_.height = this.image_.height; + + const ctx = this.canvas_.getContext('2d'); + ctx.drawImage(this.image_, 0, 0); + + const imgData = ctx.getImageData(0, 0, this.image_.width, this.image_.height); + const data = imgData.data; + const r = this.color_[0] / 255.0; + const g = this.color_[1] / 255.0; + const b = this.color_[2] / 255.0; + + for (let i = 0, ii = data.length; i < ii; i += 4) { + data[i] *= r; + data[i + 1] *= g; + data[i + 2] *= b; + } + ctx.putImageData(imgData, 0, 0); + } + + /** + * Discards event handlers which listen for load completion or errors. + * + * @private + */ + unlistenImage_() { + this.imageListenerKeys_.forEach(unlistenByKey); + this.imageListenerKeys_ = null; + } +} inherits(IconImage, EventTarget); @@ -110,165 +263,4 @@ export function get(image, src, size, crossOrigin, imageState, color) { } -/** - * @private - */ -IconImage.prototype.determineTainting_ = function() { - const context = createCanvasContext2D(1, 1); - try { - context.drawImage(this.image_, 0, 0); - context.getImageData(0, 0, 1, 1); - } catch (e) { - this.tainting_ = true; - } -}; - - -/** - * @private - */ -IconImage.prototype.dispatchChangeEvent_ = function() { - this.dispatchEvent(EventType.CHANGE); -}; - - -/** - * @private - */ -IconImage.prototype.handleImageError_ = function() { - this.imageState_ = ImageState.ERROR; - this.unlistenImage_(); - this.dispatchChangeEvent_(); -}; - - -/** - * @private - */ -IconImage.prototype.handleImageLoad_ = function() { - this.imageState_ = ImageState.LOADED; - if (this.size_) { - this.image_.width = this.size_[0]; - this.image_.height = this.size_[1]; - } - this.size_ = [this.image_.width, this.image_.height]; - this.unlistenImage_(); - this.determineTainting_(); - this.replaceColor_(); - this.dispatchChangeEvent_(); -}; - - -/** - * @param {number} pixelRatio Pixel ratio. - * @return {HTMLImageElement|HTMLCanvasElement} Image or Canvas element. - */ -IconImage.prototype.getImage = function(pixelRatio) { - return this.canvas_ ? this.canvas_ : this.image_; -}; - - -/** - * @return {module:ol/ImageState} Image state. - */ -IconImage.prototype.getImageState = function() { - return this.imageState_; -}; - - -/** - * @param {number} pixelRatio Pixel ratio. - * @return {HTMLImageElement|HTMLCanvasElement} Image element. - */ -IconImage.prototype.getHitDetectionImage = function(pixelRatio) { - if (!this.hitDetectionImage_) { - if (this.tainting_) { - const width = this.size_[0]; - const height = this.size_[1]; - const context = createCanvasContext2D(width, height); - context.fillRect(0, 0, width, height); - this.hitDetectionImage_ = context.canvas; - } else { - this.hitDetectionImage_ = this.image_; - } - } - return this.hitDetectionImage_; -}; - - -/** - * @return {module:ol/size~Size} Image size. - */ -IconImage.prototype.getSize = function() { - return this.size_; -}; - - -/** - * @return {string|undefined} Image src. - */ -IconImage.prototype.getSrc = function() { - return this.src_; -}; - - -/** - * Load not yet loaded URI. - */ -IconImage.prototype.load = function() { - if (this.imageState_ == ImageState.IDLE) { - this.imageState_ = ImageState.LOADING; - this.imageListenerKeys_ = [ - listenOnce(this.image_, EventType.ERROR, - this.handleImageError_, this), - listenOnce(this.image_, EventType.LOAD, - this.handleImageLoad_, this) - ]; - try { - this.image_.src = this.src_; - } catch (e) { - this.handleImageError_(); - } - } -}; - - -/** - * @private - */ -IconImage.prototype.replaceColor_ = function() { - if (this.tainting_ || this.color_ === null) { - return; - } - - this.canvas_.width = this.image_.width; - this.canvas_.height = this.image_.height; - - const ctx = this.canvas_.getContext('2d'); - ctx.drawImage(this.image_, 0, 0); - - const imgData = ctx.getImageData(0, 0, this.image_.width, this.image_.height); - const data = imgData.data; - const r = this.color_[0] / 255.0; - const g = this.color_[1] / 255.0; - const b = this.color_[2] / 255.0; - - for (let i = 0, ii = data.length; i < ii; i += 4) { - data[i] *= r; - data[i + 1] *= g; - data[i + 2] *= b; - } - ctx.putImageData(imgData, 0, 0); -}; - - -/** - * Discards event handlers which listen for load completion or errors. - * - * @private - */ -IconImage.prototype.unlistenImage_ = function() { - this.imageListenerKeys_.forEach(unlistenByKey); - this.imageListenerKeys_ = null; -}; export default IconImage; diff --git a/src/ol/style/IconImageCache.js b/src/ol/style/IconImageCache.js index 25b5c421bb..882c100272 100644 --- a/src/ol/style/IconImageCache.js +++ b/src/ol/style/IconImageCache.js @@ -7,26 +7,87 @@ import {asString} from '../color.js'; * Singleton class. Available through {@link module:ol/style/IconImageCache~shared}. * @constructor */ -const IconImageCache = function() { +class IconImageCache { + constructor() { - /** - * @type {!Object.} - * @private - */ - this.cache_ = {}; + /** + * @type {!Object.} + * @private + */ + this.cache_ = {}; - /** - * @type {number} - * @private - */ - this.cacheSize_ = 0; + /** + * @type {number} + * @private + */ + this.cacheSize_ = 0; - /** - * @type {number} - * @private - */ - this.maxCacheSize_ = 32; -}; + /** + * @type {number} + * @private + */ + this.maxCacheSize_ = 32; + } + + /** + * FIXME empty description for jsdoc + */ + clear() { + this.cache_ = {}; + this.cacheSize_ = 0; + } + + /** + * FIXME empty description for jsdoc + */ + expire() { + if (this.cacheSize_ > this.maxCacheSize_) { + let i = 0; + for (const key in this.cache_) { + const iconImage = this.cache_[key]; + if ((i++ & 3) === 0 && !iconImage.hasListener()) { + delete this.cache_[key]; + --this.cacheSize_; + } + } + } + } + + /** + * @param {string} src Src. + * @param {?string} crossOrigin Cross origin. + * @param {module:ol/color~Color} color Color. + * @return {module:ol/style/IconImage} Icon image. + */ + get(src, crossOrigin, color) { + const key = getKey(src, crossOrigin, color); + return key in this.cache_ ? this.cache_[key] : null; + } + + /** + * @param {string} src Src. + * @param {?string} crossOrigin Cross origin. + * @param {module:ol/color~Color} color Color. + * @param {module:ol/style/IconImage} iconImage Icon image. + */ + set(src, crossOrigin, color, iconImage) { + const key = getKey(src, crossOrigin, color); + this.cache_[key] = iconImage; + ++this.cacheSize_; + } + + /** + * Set the cache size of the icon cache. Default is `32`. Change this value when + * your map uses more than 32 different icon images and you are not caching icon + * styles on the application level. + * @param {number} maxCacheSize Cache max size. + * @api + */ + setSize(maxCacheSize) { + this.maxCacheSize_ = maxCacheSize; + this.expire(); + } +} /** @@ -41,68 +102,6 @@ function getKey(src, crossOrigin, color) { } -/** - * FIXME empty description for jsdoc - */ -IconImageCache.prototype.clear = function() { - this.cache_ = {}; - this.cacheSize_ = 0; -}; - - -/** - * FIXME empty description for jsdoc - */ -IconImageCache.prototype.expire = function() { - if (this.cacheSize_ > this.maxCacheSize_) { - let i = 0; - for (const key in this.cache_) { - const iconImage = this.cache_[key]; - if ((i++ & 3) === 0 && !iconImage.hasListener()) { - delete this.cache_[key]; - --this.cacheSize_; - } - } - } -}; - - -/** - * @param {string} src Src. - * @param {?string} crossOrigin Cross origin. - * @param {module:ol/color~Color} color Color. - * @return {module:ol/style/IconImage} Icon image. - */ -IconImageCache.prototype.get = function(src, crossOrigin, color) { - const key = getKey(src, crossOrigin, color); - return key in this.cache_ ? this.cache_[key] : null; -}; - - -/** - * @param {string} src Src. - * @param {?string} crossOrigin Cross origin. - * @param {module:ol/color~Color} color Color. - * @param {module:ol/style/IconImage} iconImage Icon image. - */ -IconImageCache.prototype.set = function(src, crossOrigin, color, iconImage) { - const key = getKey(src, crossOrigin, color); - this.cache_[key] = iconImage; - ++this.cacheSize_; -}; - - -/** - * Set the cache size of the icon cache. Default is `32`. Change this value when - * your map uses more than 32 different icon images and you are not caching icon - * styles on the application level. - * @param {number} maxCacheSize Cache max size. - * @api - */ -IconImageCache.prototype.setSize = function(maxCacheSize) { - this.maxCacheSize_ = maxCacheSize; - this.expire(); -}; export default IconImageCache; diff --git a/src/ol/style/Image.js b/src/ol/style/Image.js index 95665a7b67..6b1e9fe18c 100644 --- a/src/ol/style/Image.js +++ b/src/ol/style/Image.js @@ -24,231 +24,213 @@ * @param {module:ol/style/Image~Options} options Options. * @api */ -const ImageStyle = function(options) { +class ImageStyle { + constructor(options) { - /** - * @private - * @type {number} - */ - this.opacity_ = options.opacity; + /** + * @private + * @type {number} + */ + this.opacity_ = options.opacity; - /** - * @private - * @type {boolean} - */ - this.rotateWithView_ = options.rotateWithView; + /** + * @private + * @type {boolean} + */ + this.rotateWithView_ = options.rotateWithView; - /** - * @private - * @type {number} - */ - this.rotation_ = options.rotation; + /** + * @private + * @type {number} + */ + this.rotation_ = options.rotation; - /** - * @private - * @type {number} - */ - this.scale_ = options.scale; + /** + * @private + * @type {number} + */ + this.scale_ = options.scale; - /** - * @private - * @type {boolean} - */ - this.snapToPixel_ = options.snapToPixel; + /** + * @private + * @type {boolean} + */ + this.snapToPixel_ = options.snapToPixel; -}; + } + /** + * Get the symbolizer opacity. + * @return {number} Opacity. + * @api + */ + getOpacity() { + return this.opacity_; + } -/** - * Get the symbolizer opacity. - * @return {number} Opacity. - * @api - */ -ImageStyle.prototype.getOpacity = function() { - return this.opacity_; -}; + /** + * Determine whether the symbolizer rotates with the map. + * @return {boolean} Rotate with map. + * @api + */ + getRotateWithView() { + return this.rotateWithView_; + } + /** + * Get the symoblizer rotation. + * @return {number} Rotation. + * @api + */ + getRotation() { + return this.rotation_; + } -/** - * Determine whether the symbolizer rotates with the map. - * @return {boolean} Rotate with map. - * @api - */ -ImageStyle.prototype.getRotateWithView = function() { - return this.rotateWithView_; -}; + /** + * Get the symbolizer scale. + * @return {number} Scale. + * @api + */ + getScale() { + return this.scale_; + } + /** + * Determine whether the symbolizer should be snapped to a pixel. + * @return {boolean} The symbolizer should snap to a pixel. + * @api + */ + getSnapToPixel() { + return this.snapToPixel_; + } -/** - * Get the symoblizer rotation. - * @return {number} Rotation. - * @api - */ -ImageStyle.prototype.getRotation = function() { - return this.rotation_; -}; + /** + * Get the anchor point in pixels. The anchor determines the center point for the + * symbolizer. + * @abstract + * @return {Array.} Anchor. + */ + getAnchor() {} + /** + * Get the image element for the symbolizer. + * @abstract + * @param {number} pixelRatio Pixel ratio. + * @return {HTMLCanvasElement|HTMLVideoElement|HTMLImageElement} Image element. + */ + getImage(pixelRatio) {} -/** - * Get the symbolizer scale. - * @return {number} Scale. - * @api - */ -ImageStyle.prototype.getScale = function() { - return this.scale_; -}; + /** + * @abstract + * @param {number} pixelRatio Pixel ratio. + * @return {HTMLCanvasElement|HTMLVideoElement|HTMLImageElement} Image element. + */ + getHitDetectionImage(pixelRatio) {} + /** + * @abstract + * @return {module:ol/ImageState} Image state. + */ + getImageState() {} -/** - * Determine whether the symbolizer should be snapped to a pixel. - * @return {boolean} The symbolizer should snap to a pixel. - * @api - */ -ImageStyle.prototype.getSnapToPixel = function() { - return this.snapToPixel_; -}; + /** + * @abstract + * @return {module:ol/size~Size} Image size. + */ + getImageSize() {} + /** + * @abstract + * @return {module:ol/size~Size} Size of the hit-detection image. + */ + getHitDetectionImageSize() {} -/** - * Get the anchor point in pixels. The anchor determines the center point for the - * symbolizer. - * @abstract - * @return {Array.} Anchor. - */ -ImageStyle.prototype.getAnchor = function() {}; + /** + * Get the origin of the symbolizer. + * @abstract + * @return {Array.} Origin. + */ + getOrigin() {} + /** + * Get the size of the symbolizer (in pixels). + * @abstract + * @return {module:ol/size~Size} Size. + */ + getSize() {} -/** - * Get the image element for the symbolizer. - * @abstract - * @param {number} pixelRatio Pixel ratio. - * @return {HTMLCanvasElement|HTMLVideoElement|HTMLImageElement} Image element. - */ -ImageStyle.prototype.getImage = function(pixelRatio) {}; + /** + * Set the opacity. + * + * @param {number} opacity Opacity. + * @api + */ + setOpacity(opacity) { + this.opacity_ = opacity; + } + /** + * Set whether to rotate the style with the view. + * + * @param {boolean} rotateWithView Rotate with map. + * @api + */ + setRotateWithView(rotateWithView) { + this.rotateWithView_ = rotateWithView; + } -/** - * @abstract - * @param {number} pixelRatio Pixel ratio. - * @return {HTMLCanvasElement|HTMLVideoElement|HTMLImageElement} Image element. - */ -ImageStyle.prototype.getHitDetectionImage = function(pixelRatio) {}; + /** + * Set the rotation. + * + * @param {number} rotation Rotation. + * @api + */ + setRotation(rotation) { + this.rotation_ = rotation; + } + /** + * Set the scale. + * + * @param {number} scale Scale. + * @api + */ + setScale(scale) { + this.scale_ = scale; + } -/** - * @abstract - * @return {module:ol/ImageState} Image state. - */ -ImageStyle.prototype.getImageState = function() {}; + /** + * Set whether to snap the image to the closest pixel. + * + * @param {boolean} snapToPixel Snap to pixel? + * @api + */ + setSnapToPixel(snapToPixel) { + this.snapToPixel_ = snapToPixel; + } + /** + * @abstract + * @param {function(this: T, module:ol/events/Event)} listener Listener function. + * @param {T} thisArg Value to use as `this` when executing `listener`. + * @return {module:ol/events~EventsKey|undefined} Listener key. + * @template T + */ + listenImageChange(listener, thisArg) {} -/** - * @abstract - * @return {module:ol/size~Size} Image size. - */ -ImageStyle.prototype.getImageSize = function() {}; + /** + * Load not yet loaded URI. + * @abstract + */ + load() {} + /** + * @abstract + * @param {function(this: T, module:ol/events/Event)} listener Listener function. + * @param {T} thisArg Value to use as `this` when executing `listener`. + * @template T + */ + unlistenImageChange(listener, thisArg) {} +} -/** - * @abstract - * @return {module:ol/size~Size} Size of the hit-detection image. - */ -ImageStyle.prototype.getHitDetectionImageSize = function() {}; - - -/** - * Get the origin of the symbolizer. - * @abstract - * @return {Array.} Origin. - */ -ImageStyle.prototype.getOrigin = function() {}; - - -/** - * Get the size of the symbolizer (in pixels). - * @abstract - * @return {module:ol/size~Size} Size. - */ -ImageStyle.prototype.getSize = function() {}; - - -/** - * Set the opacity. - * - * @param {number} opacity Opacity. - * @api - */ -ImageStyle.prototype.setOpacity = function(opacity) { - this.opacity_ = opacity; -}; - - -/** - * Set whether to rotate the style with the view. - * - * @param {boolean} rotateWithView Rotate with map. - * @api - */ -ImageStyle.prototype.setRotateWithView = function(rotateWithView) { - this.rotateWithView_ = rotateWithView; -}; - - -/** - * Set the rotation. - * - * @param {number} rotation Rotation. - * @api - */ -ImageStyle.prototype.setRotation = function(rotation) { - this.rotation_ = rotation; -}; - - -/** - * Set the scale. - * - * @param {number} scale Scale. - * @api - */ -ImageStyle.prototype.setScale = function(scale) { - this.scale_ = scale; -}; - - -/** - * Set whether to snap the image to the closest pixel. - * - * @param {boolean} snapToPixel Snap to pixel? - * @api - */ -ImageStyle.prototype.setSnapToPixel = function(snapToPixel) { - this.snapToPixel_ = snapToPixel; -}; - - -/** - * @abstract - * @param {function(this: T, module:ol/events/Event)} listener Listener function. - * @param {T} thisArg Value to use as `this` when executing `listener`. - * @return {module:ol/events~EventsKey|undefined} Listener key. - * @template T - */ -ImageStyle.prototype.listenImageChange = function(listener, thisArg) {}; - - -/** - * Load not yet loaded URI. - * @abstract - */ -ImageStyle.prototype.load = function() {}; - - -/** - * @abstract - * @param {function(this: T, module:ol/events/Event)} listener Listener function. - * @param {T} thisArg Value to use as `this` when executing `listener`. - * @template T - */ -ImageStyle.prototype.unlistenImageChange = function(listener, thisArg) {}; export default ImageStyle; diff --git a/src/ol/style/RegularShape.js b/src/ol/style/RegularShape.js index a26e89ef95..4b786d9902 100644 --- a/src/ol/style/RegularShape.js +++ b/src/ol/style/RegularShape.js @@ -57,560 +57,541 @@ import ImageStyle from '../style/Image.js'; * @extends {module:ol/style/Image} * @api */ -const RegularShape = function(options) { - /** - * @private - * @type {Array.} - */ - this.checksums_ = null; +class RegularShape { + constructor(options) { + /** + * @private + * @type {Array.} + */ + this.checksums_ = null; + + /** + * @private + * @type {HTMLCanvasElement} + */ + this.canvas_ = null; + + /** + * @private + * @type {HTMLCanvasElement} + */ + this.hitDetectionCanvas_ = null; + + /** + * @private + * @type {module:ol/style/Fill} + */ + this.fill_ = options.fill !== undefined ? options.fill : null; + + /** + * @private + * @type {Array.} + */ + this.origin_ = [0, 0]; + + /** + * @private + * @type {number} + */ + this.points_ = options.points; + + /** + * @protected + * @type {number} + */ + this.radius_ = /** @type {number} */ (options.radius !== undefined ? + options.radius : options.radius1); + + /** + * @private + * @type {number|undefined} + */ + this.radius2_ = options.radius2; + + /** + * @private + * @type {number} + */ + this.angle_ = options.angle !== undefined ? options.angle : 0; + + /** + * @private + * @type {module:ol/style/Stroke} + */ + this.stroke_ = options.stroke !== undefined ? options.stroke : null; + + /** + * @private + * @type {Array.} + */ + this.anchor_ = null; + + /** + * @private + * @type {module:ol/size~Size} + */ + this.size_ = null; + + /** + * @private + * @type {module:ol/size~Size} + */ + this.imageSize_ = null; + + /** + * @private + * @type {module:ol/size~Size} + */ + this.hitDetectionImageSize_ = null; + + /** + * @protected + * @type {module:ol/style/AtlasManager|undefined} + */ + this.atlasManager_ = options.atlasManager; + + this.render_(this.atlasManager_); + + /** + * @type {boolean} + */ + const snapToPixel = options.snapToPixel !== undefined ? + options.snapToPixel : true; + + /** + * @type {boolean} + */ + const rotateWithView = options.rotateWithView !== undefined ? + options.rotateWithView : false; + + ImageStyle.call(this, { + opacity: 1, + rotateWithView: rotateWithView, + rotation: options.rotation !== undefined ? options.rotation : 0, + scale: 1, + snapToPixel: snapToPixel + }); + } /** - * @private - * @type {HTMLCanvasElement} + * Clones the style. If an atlasmanager was provided to the original style it will be used in the cloned style, too. + * @return {module:ol/style/RegularShape} The cloned style. + * @api */ - this.canvas_ = null; + clone() { + const style = new RegularShape({ + fill: this.getFill() ? this.getFill().clone() : undefined, + points: this.getPoints(), + radius: this.getRadius(), + radius2: this.getRadius2(), + angle: this.getAngle(), + snapToPixel: this.getSnapToPixel(), + stroke: this.getStroke() ? this.getStroke().clone() : undefined, + rotation: this.getRotation(), + rotateWithView: this.getRotateWithView(), + atlasManager: this.atlasManager_ + }); + style.setOpacity(this.getOpacity()); + style.setScale(this.getScale()); + return style; + } /** - * @private - * @type {HTMLCanvasElement} + * @inheritDoc + * @api */ - this.hitDetectionCanvas_ = null; + getAnchor() { + return this.anchor_; + } /** - * @private - * @type {module:ol/style/Fill} + * Get the angle used in generating the shape. + * @return {number} Shape's rotation in radians. + * @api */ - this.fill_ = options.fill !== undefined ? options.fill : null; + getAngle() { + return this.angle_; + } /** - * @private - * @type {Array.} + * Get the fill style for the shape. + * @return {module:ol/style/Fill} Fill style. + * @api */ - this.origin_ = [0, 0]; + getFill() { + return this.fill_; + } /** - * @private - * @type {number} + * @inheritDoc */ - this.points_ = options.points; + getHitDetectionImage(pixelRatio) { + return this.hitDetectionCanvas_; + } + + /** + * @inheritDoc + * @api + */ + getImage(pixelRatio) { + return this.canvas_; + } + + /** + * @inheritDoc + */ + getImageSize() { + return this.imageSize_; + } + + /** + * @inheritDoc + */ + getHitDetectionImageSize() { + return this.hitDetectionImageSize_; + } + + /** + * @inheritDoc + */ + getImageState() { + return ImageState.LOADED; + } + + /** + * @inheritDoc + * @api + */ + getOrigin() { + return this.origin_; + } + + /** + * Get the number of points for generating the shape. + * @return {number} Number of points for stars and regular polygons. + * @api + */ + getPoints() { + return this.points_; + } + + /** + * Get the (primary) radius for the shape. + * @return {number} Radius. + * @api + */ + getRadius() { + return this.radius_; + } + + /** + * Get the secondary radius for the shape. + * @return {number|undefined} Radius2. + * @api + */ + getRadius2() { + return this.radius2_; + } + + /** + * @inheritDoc + * @api + */ + getSize() { + return this.size_; + } + + /** + * Get the stroke style for the shape. + * @return {module:ol/style/Stroke} Stroke style. + * @api + */ + getStroke() { + return this.stroke_; + } + + /** + * @inheritDoc + */ + listenImageChange(listener, thisArg) {} + + /** + * @inheritDoc + */ + load() {} + + /** + * @inheritDoc + */ + unlistenImageChange(listener, thisArg) {} /** * @protected - * @type {number} + * @param {module:ol/style/AtlasManager|undefined} atlasManager An atlas manager. */ - this.radius_ = /** @type {number} */ (options.radius !== undefined ? - options.radius : options.radius1); + render_(atlasManager) { + let imageSize; + let lineCap = ''; + let lineJoin = ''; + let miterLimit = 0; + let lineDash = null; + let lineDashOffset = 0; + let strokeStyle; + let strokeWidth = 0; + + if (this.stroke_) { + strokeStyle = this.stroke_.getColor(); + if (strokeStyle === null) { + strokeStyle = defaultStrokeStyle; + } + strokeStyle = asColorLike(strokeStyle); + strokeWidth = this.stroke_.getWidth(); + if (strokeWidth === undefined) { + strokeWidth = defaultLineWidth; + } + lineDash = this.stroke_.getLineDash(); + lineDashOffset = this.stroke_.getLineDashOffset(); + if (!CANVAS_LINE_DASH) { + lineDash = null; + lineDashOffset = 0; + } + lineJoin = this.stroke_.getLineJoin(); + if (lineJoin === undefined) { + lineJoin = defaultLineJoin; + } + lineCap = this.stroke_.getLineCap(); + if (lineCap === undefined) { + lineCap = defaultLineCap; + } + miterLimit = this.stroke_.getMiterLimit(); + if (miterLimit === undefined) { + miterLimit = defaultMiterLimit; + } + } + + let size = 2 * (this.radius_ + strokeWidth) + 1; + + /** @type {module:ol/style/RegularShape~RenderOptions} */ + const renderOptions = { + strokeStyle: strokeStyle, + strokeWidth: strokeWidth, + size: size, + lineCap: lineCap, + lineDash: lineDash, + lineDashOffset: lineDashOffset, + lineJoin: lineJoin, + miterLimit: miterLimit + }; + + if (atlasManager === undefined) { + // no atlas manager is used, create a new canvas + const context = createCanvasContext2D(size, size); + this.canvas_ = context.canvas; + + // canvas.width and height are rounded to the closest integer + size = this.canvas_.width; + imageSize = size; + + this.draw_(renderOptions, context, 0, 0); + + this.createHitDetectionCanvas_(renderOptions); + } else { + // an atlas manager is used, add the symbol to an atlas + size = Math.round(size); + + const hasCustomHitDetectionImage = !this.fill_; + let renderHitDetectionCallback; + if (hasCustomHitDetectionImage) { + // render the hit-detection image into a separate atlas image + renderHitDetectionCallback = + this.drawHitDetectionCanvas_.bind(this, renderOptions); + } + + const id = this.getChecksum(); + const info = atlasManager.add( + id, size, size, this.draw_.bind(this, renderOptions), + renderHitDetectionCallback); + + this.canvas_ = info.image; + this.origin_ = [info.offsetX, info.offsetY]; + imageSize = info.image.width; + + if (hasCustomHitDetectionImage) { + this.hitDetectionCanvas_ = info.hitImage; + this.hitDetectionImageSize_ = + [info.hitImage.width, info.hitImage.height]; + } else { + this.hitDetectionCanvas_ = this.canvas_; + this.hitDetectionImageSize_ = [imageSize, imageSize]; + } + } + + this.anchor_ = [size / 2, size / 2]; + this.size_ = [size, size]; + this.imageSize_ = [imageSize, imageSize]; + } /** * @private - * @type {number|undefined} + * @param {module:ol/style/RegularShape~RenderOptions} renderOptions Render options. + * @param {CanvasRenderingContext2D} context The rendering context. + * @param {number} x The origin for the symbol (x). + * @param {number} y The origin for the symbol (y). */ - this.radius2_ = options.radius2; + draw_(renderOptions, context, x, y) { + let i, angle0, radiusC; + // reset transform + context.setTransform(1, 0, 0, 1, 0, 0); + + // then move to (x, y) + context.translate(x, y); + + context.beginPath(); + + let points = this.points_; + if (points === Infinity) { + context.arc( + renderOptions.size / 2, renderOptions.size / 2, + this.radius_, 0, 2 * Math.PI, true); + } else { + const radius2 = (this.radius2_ !== undefined) ? this.radius2_ + : this.radius_; + if (radius2 !== this.radius_) { + points = 2 * points; + } + for (i = 0; i <= points; i++) { + angle0 = i * 2 * Math.PI / points - Math.PI / 2 + this.angle_; + radiusC = i % 2 === 0 ? this.radius_ : radius2; + context.lineTo(renderOptions.size / 2 + radiusC * Math.cos(angle0), + renderOptions.size / 2 + radiusC * Math.sin(angle0)); + } + } + + + if (this.fill_) { + let color = this.fill_.getColor(); + if (color === null) { + color = defaultFillStyle; + } + context.fillStyle = asColorLike(color); + context.fill(); + } + if (this.stroke_) { + context.strokeStyle = renderOptions.strokeStyle; + context.lineWidth = renderOptions.strokeWidth; + if (renderOptions.lineDash) { + context.setLineDash(renderOptions.lineDash); + context.lineDashOffset = renderOptions.lineDashOffset; + } + context.lineCap = renderOptions.lineCap; + context.lineJoin = renderOptions.lineJoin; + context.miterLimit = renderOptions.miterLimit; + context.stroke(); + } + context.closePath(); + } /** * @private - * @type {number} + * @param {module:ol/style/RegularShape~RenderOptions} renderOptions Render options. */ - this.angle_ = options.angle !== undefined ? options.angle : 0; + createHitDetectionCanvas_(renderOptions) { + this.hitDetectionImageSize_ = [renderOptions.size, renderOptions.size]; + if (this.fill_) { + this.hitDetectionCanvas_ = this.canvas_; + return; + } + + // if no fill style is set, create an extra hit-detection image with a + // default fill style + const context = createCanvasContext2D(renderOptions.size, renderOptions.size); + this.hitDetectionCanvas_ = context.canvas; + + this.drawHitDetectionCanvas_(renderOptions, context, 0, 0); + } /** * @private - * @type {module:ol/style/Stroke} + * @param {module:ol/style/RegularShape~RenderOptions} renderOptions Render options. + * @param {CanvasRenderingContext2D} context The context. + * @param {number} x The origin for the symbol (x). + * @param {number} y The origin for the symbol (y). */ - this.stroke_ = options.stroke !== undefined ? options.stroke : null; + drawHitDetectionCanvas_(renderOptions, context, x, y) { + // reset transform + context.setTransform(1, 0, 0, 1, 0, 0); + + // then move to (x, y) + context.translate(x, y); + + context.beginPath(); + + let points = this.points_; + if (points === Infinity) { + context.arc( + renderOptions.size / 2, renderOptions.size / 2, + this.radius_, 0, 2 * Math.PI, true); + } else { + const radius2 = (this.radius2_ !== undefined) ? this.radius2_ + : this.radius_; + if (radius2 !== this.radius_) { + points = 2 * points; + } + let i, radiusC, angle0; + for (i = 0; i <= points; i++) { + angle0 = i * 2 * Math.PI / points - Math.PI / 2 + this.angle_; + radiusC = i % 2 === 0 ? this.radius_ : radius2; + context.lineTo(renderOptions.size / 2 + radiusC * Math.cos(angle0), + renderOptions.size / 2 + radiusC * Math.sin(angle0)); + } + } + + context.fillStyle = defaultFillStyle; + context.fill(); + if (this.stroke_) { + context.strokeStyle = renderOptions.strokeStyle; + context.lineWidth = renderOptions.strokeWidth; + if (renderOptions.lineDash) { + context.setLineDash(renderOptions.lineDash); + context.lineDashOffset = renderOptions.lineDashOffset; + } + context.stroke(); + } + context.closePath(); + } /** - * @private - * @type {Array.} + * @return {string} The checksum. */ - this.anchor_ = null; + getChecksum() { + const strokeChecksum = this.stroke_ ? + this.stroke_.getChecksum() : '-'; + const fillChecksum = this.fill_ ? + this.fill_.getChecksum() : '-'; - /** - * @private - * @type {module:ol/size~Size} - */ - this.size_ = null; + const recalculate = !this.checksums_ || + (strokeChecksum != this.checksums_[1] || + fillChecksum != this.checksums_[2] || + this.radius_ != this.checksums_[3] || + this.radius2_ != this.checksums_[4] || + this.angle_ != this.checksums_[5] || + this.points_ != this.checksums_[6]); - /** - * @private - * @type {module:ol/size~Size} - */ - this.imageSize_ = null; + if (recalculate) { + const checksum = 'r' + strokeChecksum + fillChecksum + + (this.radius_ !== undefined ? this.radius_.toString() : '-') + + (this.radius2_ !== undefined ? this.radius2_.toString() : '-') + + (this.angle_ !== undefined ? this.angle_.toString() : '-') + + (this.points_ !== undefined ? this.points_.toString() : '-'); + this.checksums_ = [checksum, strokeChecksum, fillChecksum, + this.radius_, this.radius2_, this.angle_, this.points_]; + } - /** - * @private - * @type {module:ol/size~Size} - */ - this.hitDetectionImageSize_ = null; - - /** - * @protected - * @type {module:ol/style/AtlasManager|undefined} - */ - this.atlasManager_ = options.atlasManager; - - this.render_(this.atlasManager_); - - /** - * @type {boolean} - */ - const snapToPixel = options.snapToPixel !== undefined ? - options.snapToPixel : true; - - /** - * @type {boolean} - */ - const rotateWithView = options.rotateWithView !== undefined ? - options.rotateWithView : false; - - ImageStyle.call(this, { - opacity: 1, - rotateWithView: rotateWithView, - rotation: options.rotation !== undefined ? options.rotation : 0, - scale: 1, - snapToPixel: snapToPixel - }); -}; + return this.checksums_[0]; + } +} inherits(RegularShape, ImageStyle); -/** - * Clones the style. If an atlasmanager was provided to the original style it will be used in the cloned style, too. - * @return {module:ol/style/RegularShape} The cloned style. - * @api - */ -RegularShape.prototype.clone = function() { - const style = new RegularShape({ - fill: this.getFill() ? this.getFill().clone() : undefined, - points: this.getPoints(), - radius: this.getRadius(), - radius2: this.getRadius2(), - angle: this.getAngle(), - snapToPixel: this.getSnapToPixel(), - stroke: this.getStroke() ? this.getStroke().clone() : undefined, - rotation: this.getRotation(), - rotateWithView: this.getRotateWithView(), - atlasManager: this.atlasManager_ - }); - style.setOpacity(this.getOpacity()); - style.setScale(this.getScale()); - return style; -}; - - -/** - * @inheritDoc - * @api - */ -RegularShape.prototype.getAnchor = function() { - return this.anchor_; -}; - - -/** - * Get the angle used in generating the shape. - * @return {number} Shape's rotation in radians. - * @api - */ -RegularShape.prototype.getAngle = function() { - return this.angle_; -}; - - -/** - * Get the fill style for the shape. - * @return {module:ol/style/Fill} Fill style. - * @api - */ -RegularShape.prototype.getFill = function() { - return this.fill_; -}; - - -/** - * @inheritDoc - */ -RegularShape.prototype.getHitDetectionImage = function(pixelRatio) { - return this.hitDetectionCanvas_; -}; - - -/** - * @inheritDoc - * @api - */ -RegularShape.prototype.getImage = function(pixelRatio) { - return this.canvas_; -}; - - -/** - * @inheritDoc - */ -RegularShape.prototype.getImageSize = function() { - return this.imageSize_; -}; - - -/** - * @inheritDoc - */ -RegularShape.prototype.getHitDetectionImageSize = function() { - return this.hitDetectionImageSize_; -}; - - -/** - * @inheritDoc - */ -RegularShape.prototype.getImageState = function() { - return ImageState.LOADED; -}; - - -/** - * @inheritDoc - * @api - */ -RegularShape.prototype.getOrigin = function() { - return this.origin_; -}; - - -/** - * Get the number of points for generating the shape. - * @return {number} Number of points for stars and regular polygons. - * @api - */ -RegularShape.prototype.getPoints = function() { - return this.points_; -}; - - -/** - * Get the (primary) radius for the shape. - * @return {number} Radius. - * @api - */ -RegularShape.prototype.getRadius = function() { - return this.radius_; -}; - - -/** - * Get the secondary radius for the shape. - * @return {number|undefined} Radius2. - * @api - */ -RegularShape.prototype.getRadius2 = function() { - return this.radius2_; -}; - - -/** - * @inheritDoc - * @api - */ -RegularShape.prototype.getSize = function() { - return this.size_; -}; - - -/** - * Get the stroke style for the shape. - * @return {module:ol/style/Stroke} Stroke style. - * @api - */ -RegularShape.prototype.getStroke = function() { - return this.stroke_; -}; - - -/** - * @inheritDoc - */ -RegularShape.prototype.listenImageChange = function(listener, thisArg) {}; - - -/** - * @inheritDoc - */ -RegularShape.prototype.load = function() {}; - - -/** - * @inheritDoc - */ -RegularShape.prototype.unlistenImageChange = function(listener, thisArg) {}; - - -/** - * @protected - * @param {module:ol/style/AtlasManager|undefined} atlasManager An atlas manager. - */ -RegularShape.prototype.render_ = function(atlasManager) { - let imageSize; - let lineCap = ''; - let lineJoin = ''; - let miterLimit = 0; - let lineDash = null; - let lineDashOffset = 0; - let strokeStyle; - let strokeWidth = 0; - - if (this.stroke_) { - strokeStyle = this.stroke_.getColor(); - if (strokeStyle === null) { - strokeStyle = defaultStrokeStyle; - } - strokeStyle = asColorLike(strokeStyle); - strokeWidth = this.stroke_.getWidth(); - if (strokeWidth === undefined) { - strokeWidth = defaultLineWidth; - } - lineDash = this.stroke_.getLineDash(); - lineDashOffset = this.stroke_.getLineDashOffset(); - if (!CANVAS_LINE_DASH) { - lineDash = null; - lineDashOffset = 0; - } - lineJoin = this.stroke_.getLineJoin(); - if (lineJoin === undefined) { - lineJoin = defaultLineJoin; - } - lineCap = this.stroke_.getLineCap(); - if (lineCap === undefined) { - lineCap = defaultLineCap; - } - miterLimit = this.stroke_.getMiterLimit(); - if (miterLimit === undefined) { - miterLimit = defaultMiterLimit; - } - } - - let size = 2 * (this.radius_ + strokeWidth) + 1; - - /** @type {module:ol/style/RegularShape~RenderOptions} */ - const renderOptions = { - strokeStyle: strokeStyle, - strokeWidth: strokeWidth, - size: size, - lineCap: lineCap, - lineDash: lineDash, - lineDashOffset: lineDashOffset, - lineJoin: lineJoin, - miterLimit: miterLimit - }; - - if (atlasManager === undefined) { - // no atlas manager is used, create a new canvas - const context = createCanvasContext2D(size, size); - this.canvas_ = context.canvas; - - // canvas.width and height are rounded to the closest integer - size = this.canvas_.width; - imageSize = size; - - this.draw_(renderOptions, context, 0, 0); - - this.createHitDetectionCanvas_(renderOptions); - } else { - // an atlas manager is used, add the symbol to an atlas - size = Math.round(size); - - const hasCustomHitDetectionImage = !this.fill_; - let renderHitDetectionCallback; - if (hasCustomHitDetectionImage) { - // render the hit-detection image into a separate atlas image - renderHitDetectionCallback = - this.drawHitDetectionCanvas_.bind(this, renderOptions); - } - - const id = this.getChecksum(); - const info = atlasManager.add( - id, size, size, this.draw_.bind(this, renderOptions), - renderHitDetectionCallback); - - this.canvas_ = info.image; - this.origin_ = [info.offsetX, info.offsetY]; - imageSize = info.image.width; - - if (hasCustomHitDetectionImage) { - this.hitDetectionCanvas_ = info.hitImage; - this.hitDetectionImageSize_ = - [info.hitImage.width, info.hitImage.height]; - } else { - this.hitDetectionCanvas_ = this.canvas_; - this.hitDetectionImageSize_ = [imageSize, imageSize]; - } - } - - this.anchor_ = [size / 2, size / 2]; - this.size_ = [size, size]; - this.imageSize_ = [imageSize, imageSize]; -}; - - -/** - * @private - * @param {module:ol/style/RegularShape~RenderOptions} renderOptions Render options. - * @param {CanvasRenderingContext2D} context The rendering context. - * @param {number} x The origin for the symbol (x). - * @param {number} y The origin for the symbol (y). - */ -RegularShape.prototype.draw_ = function(renderOptions, context, x, y) { - let i, angle0, radiusC; - // reset transform - context.setTransform(1, 0, 0, 1, 0, 0); - - // then move to (x, y) - context.translate(x, y); - - context.beginPath(); - - let points = this.points_; - if (points === Infinity) { - context.arc( - renderOptions.size / 2, renderOptions.size / 2, - this.radius_, 0, 2 * Math.PI, true); - } else { - const radius2 = (this.radius2_ !== undefined) ? this.radius2_ - : this.radius_; - if (radius2 !== this.radius_) { - points = 2 * points; - } - for (i = 0; i <= points; i++) { - angle0 = i * 2 * Math.PI / points - Math.PI / 2 + this.angle_; - radiusC = i % 2 === 0 ? this.radius_ : radius2; - context.lineTo(renderOptions.size / 2 + radiusC * Math.cos(angle0), - renderOptions.size / 2 + radiusC * Math.sin(angle0)); - } - } - - - if (this.fill_) { - let color = this.fill_.getColor(); - if (color === null) { - color = defaultFillStyle; - } - context.fillStyle = asColorLike(color); - context.fill(); - } - if (this.stroke_) { - context.strokeStyle = renderOptions.strokeStyle; - context.lineWidth = renderOptions.strokeWidth; - if (renderOptions.lineDash) { - context.setLineDash(renderOptions.lineDash); - context.lineDashOffset = renderOptions.lineDashOffset; - } - context.lineCap = renderOptions.lineCap; - context.lineJoin = renderOptions.lineJoin; - context.miterLimit = renderOptions.miterLimit; - context.stroke(); - } - context.closePath(); -}; - - -/** - * @private - * @param {module:ol/style/RegularShape~RenderOptions} renderOptions Render options. - */ -RegularShape.prototype.createHitDetectionCanvas_ = function(renderOptions) { - this.hitDetectionImageSize_ = [renderOptions.size, renderOptions.size]; - if (this.fill_) { - this.hitDetectionCanvas_ = this.canvas_; - return; - } - - // if no fill style is set, create an extra hit-detection image with a - // default fill style - const context = createCanvasContext2D(renderOptions.size, renderOptions.size); - this.hitDetectionCanvas_ = context.canvas; - - this.drawHitDetectionCanvas_(renderOptions, context, 0, 0); -}; - - -/** - * @private - * @param {module:ol/style/RegularShape~RenderOptions} renderOptions Render options. - * @param {CanvasRenderingContext2D} context The context. - * @param {number} x The origin for the symbol (x). - * @param {number} y The origin for the symbol (y). - */ -RegularShape.prototype.drawHitDetectionCanvas_ = function(renderOptions, context, x, y) { - // reset transform - context.setTransform(1, 0, 0, 1, 0, 0); - - // then move to (x, y) - context.translate(x, y); - - context.beginPath(); - - let points = this.points_; - if (points === Infinity) { - context.arc( - renderOptions.size / 2, renderOptions.size / 2, - this.radius_, 0, 2 * Math.PI, true); - } else { - const radius2 = (this.radius2_ !== undefined) ? this.radius2_ - : this.radius_; - if (radius2 !== this.radius_) { - points = 2 * points; - } - let i, radiusC, angle0; - for (i = 0; i <= points; i++) { - angle0 = i * 2 * Math.PI / points - Math.PI / 2 + this.angle_; - radiusC = i % 2 === 0 ? this.radius_ : radius2; - context.lineTo(renderOptions.size / 2 + radiusC * Math.cos(angle0), - renderOptions.size / 2 + radiusC * Math.sin(angle0)); - } - } - - context.fillStyle = defaultFillStyle; - context.fill(); - if (this.stroke_) { - context.strokeStyle = renderOptions.strokeStyle; - context.lineWidth = renderOptions.strokeWidth; - if (renderOptions.lineDash) { - context.setLineDash(renderOptions.lineDash); - context.lineDashOffset = renderOptions.lineDashOffset; - } - context.stroke(); - } - context.closePath(); -}; - - -/** - * @return {string} The checksum. - */ -RegularShape.prototype.getChecksum = function() { - const strokeChecksum = this.stroke_ ? - this.stroke_.getChecksum() : '-'; - const fillChecksum = this.fill_ ? - this.fill_.getChecksum() : '-'; - - const recalculate = !this.checksums_ || - (strokeChecksum != this.checksums_[1] || - fillChecksum != this.checksums_[2] || - this.radius_ != this.checksums_[3] || - this.radius2_ != this.checksums_[4] || - this.angle_ != this.checksums_[5] || - this.points_ != this.checksums_[6]); - - if (recalculate) { - const checksum = 'r' + strokeChecksum + fillChecksum + - (this.radius_ !== undefined ? this.radius_.toString() : '-') + - (this.radius2_ !== undefined ? this.radius2_.toString() : '-') + - (this.angle_ !== undefined ? this.angle_.toString() : '-') + - (this.points_ !== undefined ? this.points_.toString() : '-'); - this.checksums_ = [checksum, strokeChecksum, fillChecksum, - this.radius_, this.radius2_, this.angle_, this.points_]; - } - - return this.checksums_[0]; -}; export default RegularShape; diff --git a/src/ol/style/Stroke.js b/src/ol/style/Stroke.js index 5cd465c04b..d5e6571197 100644 --- a/src/ol/style/Stroke.js +++ b/src/ol/style/Stroke.js @@ -31,269 +31,256 @@ import {getUid} from '../util.js'; * @param {module:ol/style/Stroke~Options=} opt_options Options. * @api */ -const Stroke = function(opt_options) { +class Stroke { + constructor(opt_options) { - const options = opt_options || {}; + const options = opt_options || {}; - /** - * @private - * @type {module:ol/color~Color|module:ol/colorlike~ColorLike} - */ - this.color_ = options.color !== undefined ? options.color : null; + /** + * @private + * @type {module:ol/color~Color|module:ol/colorlike~ColorLike} + */ + this.color_ = options.color !== undefined ? options.color : null; - /** - * @private - * @type {string|undefined} - */ - this.lineCap_ = options.lineCap; + /** + * @private + * @type {string|undefined} + */ + this.lineCap_ = options.lineCap; - /** - * @private - * @type {Array.} - */ - this.lineDash_ = options.lineDash !== undefined ? options.lineDash : null; + /** + * @private + * @type {Array.} + */ + this.lineDash_ = options.lineDash !== undefined ? options.lineDash : null; - /** - * @private - * @type {number|undefined} - */ - this.lineDashOffset_ = options.lineDashOffset; + /** + * @private + * @type {number|undefined} + */ + this.lineDashOffset_ = options.lineDashOffset; - /** - * @private - * @type {string|undefined} - */ - this.lineJoin_ = options.lineJoin; + /** + * @private + * @type {string|undefined} + */ + this.lineJoin_ = options.lineJoin; - /** - * @private - * @type {number|undefined} - */ - this.miterLimit_ = options.miterLimit; + /** + * @private + * @type {number|undefined} + */ + this.miterLimit_ = options.miterLimit; - /** - * @private - * @type {number|undefined} - */ - this.width_ = options.width; + /** + * @private + * @type {number|undefined} + */ + this.width_ = options.width; - /** - * @private - * @type {string|undefined} - */ - this.checksum_ = undefined; -}; - - -/** - * Clones the style. - * @return {module:ol/style/Stroke} The cloned style. - * @api - */ -Stroke.prototype.clone = function() { - const color = this.getColor(); - return new Stroke({ - color: (color && color.slice) ? color.slice() : color || undefined, - lineCap: this.getLineCap(), - lineDash: this.getLineDash() ? this.getLineDash().slice() : undefined, - lineDashOffset: this.getLineDashOffset(), - lineJoin: this.getLineJoin(), - miterLimit: this.getMiterLimit(), - width: this.getWidth() - }); -}; - - -/** - * Get the stroke color. - * @return {module:ol/color~Color|module:ol/colorlike~ColorLike} Color. - * @api - */ -Stroke.prototype.getColor = function() { - return this.color_; -}; - - -/** - * Get the line cap type for the stroke. - * @return {string|undefined} Line cap. - * @api - */ -Stroke.prototype.getLineCap = function() { - return this.lineCap_; -}; - - -/** - * Get the line dash style for the stroke. - * @return {Array.} Line dash. - * @api - */ -Stroke.prototype.getLineDash = function() { - return this.lineDash_; -}; - - -/** - * Get the line dash offset for the stroke. - * @return {number|undefined} Line dash offset. - * @api - */ -Stroke.prototype.getLineDashOffset = function() { - return this.lineDashOffset_; -}; - - -/** - * Get the line join type for the stroke. - * @return {string|undefined} Line join. - * @api - */ -Stroke.prototype.getLineJoin = function() { - return this.lineJoin_; -}; - - -/** - * Get the miter limit for the stroke. - * @return {number|undefined} Miter limit. - * @api - */ -Stroke.prototype.getMiterLimit = function() { - return this.miterLimit_; -}; - - -/** - * Get the stroke width. - * @return {number|undefined} Width. - * @api - */ -Stroke.prototype.getWidth = function() { - return this.width_; -}; - - -/** - * Set the color. - * - * @param {module:ol/color~Color|module:ol/colorlike~ColorLike} color Color. - * @api - */ -Stroke.prototype.setColor = function(color) { - this.color_ = color; - this.checksum_ = undefined; -}; - - -/** - * Set the line cap. - * - * @param {string|undefined} lineCap Line cap. - * @api - */ -Stroke.prototype.setLineCap = function(lineCap) { - this.lineCap_ = lineCap; - this.checksum_ = undefined; -}; - - -/** - * Set the line dash. - * - * Please note that Internet Explorer 10 and lower [do not support][mdn] the - * `setLineDash` method on the `CanvasRenderingContext2D` and therefore this - * property will have no visual effect in these browsers. - * - * [mdn]: https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/setLineDash#Browser_compatibility - * - * @param {Array.} lineDash Line dash. - * @api - */ -Stroke.prototype.setLineDash = function(lineDash) { - this.lineDash_ = lineDash; - this.checksum_ = undefined; -}; - - -/** - * Set the line dash offset. - * - * @param {number|undefined} lineDashOffset Line dash offset. - * @api - */ -Stroke.prototype.setLineDashOffset = function(lineDashOffset) { - this.lineDashOffset_ = lineDashOffset; - this.checksum_ = undefined; -}; - - -/** - * Set the line join. - * - * @param {string|undefined} lineJoin Line join. - * @api - */ -Stroke.prototype.setLineJoin = function(lineJoin) { - this.lineJoin_ = lineJoin; - this.checksum_ = undefined; -}; - - -/** - * Set the miter limit. - * - * @param {number|undefined} miterLimit Miter limit. - * @api - */ -Stroke.prototype.setMiterLimit = function(miterLimit) { - this.miterLimit_ = miterLimit; - this.checksum_ = undefined; -}; - - -/** - * Set the width. - * - * @param {number|undefined} width Width. - * @api - */ -Stroke.prototype.setWidth = function(width) { - this.width_ = width; - this.checksum_ = undefined; -}; - - -/** - * @return {string} The checksum. - */ -Stroke.prototype.getChecksum = function() { - if (this.checksum_ === undefined) { - this.checksum_ = 's'; - if (this.color_) { - if (typeof this.color_ === 'string') { - this.checksum_ += this.color_; - } else { - this.checksum_ += getUid(this.color_).toString(); - } - } else { - this.checksum_ += '-'; - } - this.checksum_ += ',' + - (this.lineCap_ !== undefined ? - this.lineCap_.toString() : '-') + ',' + - (this.lineDash_ ? - this.lineDash_.toString() : '-') + ',' + - (this.lineDashOffset_ !== undefined ? - this.lineDashOffset_ : '-') + ',' + - (this.lineJoin_ !== undefined ? - this.lineJoin_ : '-') + ',' + - (this.miterLimit_ !== undefined ? - this.miterLimit_.toString() : '-') + ',' + - (this.width_ !== undefined ? - this.width_.toString() : '-'); + /** + * @private + * @type {string|undefined} + */ + this.checksum_ = undefined; } - return this.checksum_; -}; + /** + * Clones the style. + * @return {module:ol/style/Stroke} The cloned style. + * @api + */ + clone() { + const color = this.getColor(); + return new Stroke({ + color: (color && color.slice) ? color.slice() : color || undefined, + lineCap: this.getLineCap(), + lineDash: this.getLineDash() ? this.getLineDash().slice() : undefined, + lineDashOffset: this.getLineDashOffset(), + lineJoin: this.getLineJoin(), + miterLimit: this.getMiterLimit(), + width: this.getWidth() + }); + } + + /** + * Get the stroke color. + * @return {module:ol/color~Color|module:ol/colorlike~ColorLike} Color. + * @api + */ + getColor() { + return this.color_; + } + + /** + * Get the line cap type for the stroke. + * @return {string|undefined} Line cap. + * @api + */ + getLineCap() { + return this.lineCap_; + } + + /** + * Get the line dash style for the stroke. + * @return {Array.} Line dash. + * @api + */ + getLineDash() { + return this.lineDash_; + } + + /** + * Get the line dash offset for the stroke. + * @return {number|undefined} Line dash offset. + * @api + */ + getLineDashOffset() { + return this.lineDashOffset_; + } + + /** + * Get the line join type for the stroke. + * @return {string|undefined} Line join. + * @api + */ + getLineJoin() { + return this.lineJoin_; + } + + /** + * Get the miter limit for the stroke. + * @return {number|undefined} Miter limit. + * @api + */ + getMiterLimit() { + return this.miterLimit_; + } + + /** + * Get the stroke width. + * @return {number|undefined} Width. + * @api + */ + getWidth() { + return this.width_; + } + + /** + * Set the color. + * + * @param {module:ol/color~Color|module:ol/colorlike~ColorLike} color Color. + * @api + */ + setColor(color) { + this.color_ = color; + this.checksum_ = undefined; + } + + /** + * Set the line cap. + * + * @param {string|undefined} lineCap Line cap. + * @api + */ + setLineCap(lineCap) { + this.lineCap_ = lineCap; + this.checksum_ = undefined; + } + + /** + * Set the line dash. + * + * Please note that Internet Explorer 10 and lower [do not support][mdn] the + * `setLineDash` method on the `CanvasRenderingContext2D` and therefore this + * property will have no visual effect in these browsers. + * + * [mdn]: https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/setLineDash#Browser_compatibility + * + * @param {Array.} lineDash Line dash. + * @api + */ + setLineDash(lineDash) { + this.lineDash_ = lineDash; + this.checksum_ = undefined; + } + + /** + * Set the line dash offset. + * + * @param {number|undefined} lineDashOffset Line dash offset. + * @api + */ + setLineDashOffset(lineDashOffset) { + this.lineDashOffset_ = lineDashOffset; + this.checksum_ = undefined; + } + + /** + * Set the line join. + * + * @param {string|undefined} lineJoin Line join. + * @api + */ + setLineJoin(lineJoin) { + this.lineJoin_ = lineJoin; + this.checksum_ = undefined; + } + + /** + * Set the miter limit. + * + * @param {number|undefined} miterLimit Miter limit. + * @api + */ + setMiterLimit(miterLimit) { + this.miterLimit_ = miterLimit; + this.checksum_ = undefined; + } + + /** + * Set the width. + * + * @param {number|undefined} width Width. + * @api + */ + setWidth(width) { + this.width_ = width; + this.checksum_ = undefined; + } + + /** + * @return {string} The checksum. + */ + getChecksum() { + if (this.checksum_ === undefined) { + this.checksum_ = 's'; + if (this.color_) { + if (typeof this.color_ === 'string') { + this.checksum_ += this.color_; + } else { + this.checksum_ += getUid(this.color_).toString(); + } + } else { + this.checksum_ += '-'; + } + this.checksum_ += ',' + + (this.lineCap_ !== undefined ? + this.lineCap_.toString() : '-') + ',' + + (this.lineDash_ ? + this.lineDash_.toString() : '-') + ',' + + (this.lineDashOffset_ !== undefined ? + this.lineDashOffset_ : '-') + ',' + + (this.lineJoin_ !== undefined ? + this.lineJoin_ : '-') + ',' + + (this.miterLimit_ !== undefined ? + this.miterLimit_.toString() : '-') + ',' + + (this.width_ !== undefined ? + this.width_.toString() : '-'); + } + + return this.checksum_; + } +} + export default Stroke; diff --git a/src/ol/style/Style.js b/src/ol/style/Style.js index 27534aefbd..69c8cadf3a 100644 --- a/src/ol/style/Style.js +++ b/src/ol/style/Style.js @@ -149,260 +149,246 @@ import Stroke from '../style/Stroke.js'; * @param {module:ol/style/Style~Options=} opt_options Style options. * @api */ -const Style = function(opt_options) { +class Style { + constructor(opt_options) { - const options = opt_options || {}; + const options = opt_options || {}; - /** - * @private - * @type {string|module:ol/geom/Geometry|module:ol/style/Style~GeometryFunction} - */ - this.geometry_ = null; - - /** - * @private - * @type {!module:ol/style/Style~GeometryFunction} - */ - this.geometryFunction_ = defaultGeometryFunction; - - if (options.geometry !== undefined) { - this.setGeometry(options.geometry); - } - - /** - * @private - * @type {module:ol/style/Fill} - */ - this.fill_ = options.fill !== undefined ? options.fill : null; - - /** + /** * @private - * @type {module:ol/style/Image} + * @type {string|module:ol/geom/Geometry|module:ol/style/Style~GeometryFunction} */ - this.image_ = options.image !== undefined ? options.image : null; + this.geometry_ = null; - /** - * @private - * @type {module:ol/style/Style~RenderFunction|null} - */ - this.renderer_ = options.renderer !== undefined ? options.renderer : null; - - /** - * @private - * @type {module:ol/style/Stroke} - */ - this.stroke_ = options.stroke !== undefined ? options.stroke : null; - - /** - * @private - * @type {module:ol/style/Text} - */ - this.text_ = options.text !== undefined ? options.text : null; - - /** - * @private - * @type {number|undefined} - */ - this.zIndex_ = options.zIndex; - -}; - - -/** - * Clones the style. - * @return {module:ol/style/Style} The cloned style. - * @api - */ -Style.prototype.clone = function() { - let geometry = this.getGeometry(); - if (geometry && geometry.clone) { - geometry = geometry.clone(); - } - return new Style({ - geometry: geometry, - fill: this.getFill() ? this.getFill().clone() : undefined, - image: this.getImage() ? this.getImage().clone() : undefined, - stroke: this.getStroke() ? this.getStroke().clone() : undefined, - text: this.getText() ? this.getText().clone() : undefined, - zIndex: this.getZIndex() - }); -}; - - -/** - * Get the custom renderer function that was configured with - * {@link #setRenderer} or the `renderer` constructor option. - * @return {module:ol/style/Style~RenderFunction|null} Custom renderer function. - * @api - */ -Style.prototype.getRenderer = function() { - return this.renderer_; -}; - - -/** - * Sets a custom renderer function for this style. When set, `fill`, `stroke` - * and `image` options of the style will be ignored. - * @param {module:ol/style/Style~RenderFunction|null} renderer Custom renderer function. - * @api - */ -Style.prototype.setRenderer = function(renderer) { - this.renderer_ = renderer; -}; - - -/** - * Get the geometry to be rendered. - * @return {string|module:ol/geom/Geometry|module:ol/style/Style~GeometryFunction} - * Feature property or geometry or function that returns the geometry that will - * be rendered with this style. - * @api - */ -Style.prototype.getGeometry = function() { - return this.geometry_; -}; - - -/** - * Get the function used to generate a geometry for rendering. - * @return {!module:ol/style/Style~GeometryFunction} Function that is called with a feature - * and returns the geometry to render instead of the feature's geometry. - * @api - */ -Style.prototype.getGeometryFunction = function() { - return this.geometryFunction_; -}; - - -/** - * Get the fill style. - * @return {module:ol/style/Fill} Fill style. - * @api - */ -Style.prototype.getFill = function() { - return this.fill_; -}; - - -/** - * Set the fill style. - * @param {module:ol/style/Fill} fill Fill style. - * @api - */ -Style.prototype.setFill = function(fill) { - this.fill_ = fill; -}; - - -/** - * Get the image style. - * @return {module:ol/style/Image} Image style. - * @api - */ -Style.prototype.getImage = function() { - return this.image_; -}; - - -/** - * Set the image style. - * @param {module:ol/style/Image} image Image style. - * @api - */ -Style.prototype.setImage = function(image) { - this.image_ = image; -}; - - -/** - * Get the stroke style. - * @return {module:ol/style/Stroke} Stroke style. - * @api - */ -Style.prototype.getStroke = function() { - return this.stroke_; -}; - - -/** - * Set the stroke style. - * @param {module:ol/style/Stroke} stroke Stroke style. - * @api - */ -Style.prototype.setStroke = function(stroke) { - this.stroke_ = stroke; -}; - - -/** - * Get the text style. - * @return {module:ol/style/Text} Text style. - * @api - */ -Style.prototype.getText = function() { - return this.text_; -}; - - -/** - * Set the text style. - * @param {module:ol/style/Text} text Text style. - * @api - */ -Style.prototype.setText = function(text) { - this.text_ = text; -}; - - -/** - * Get the z-index for the style. - * @return {number|undefined} ZIndex. - * @api - */ -Style.prototype.getZIndex = function() { - return this.zIndex_; -}; - - -/** - * Set a geometry that is rendered instead of the feature's geometry. - * - * @param {string|module:ol/geom/Geometry|module:ol/style/Style~GeometryFunction} geometry - * Feature property or geometry or function returning a geometry to render - * for this style. - * @api - */ -Style.prototype.setGeometry = function(geometry) { - if (typeof geometry === 'function') { - this.geometryFunction_ = geometry; - } else if (typeof geometry === 'string') { - this.geometryFunction_ = function(feature) { - return ( - /** @type {module:ol/geom/Geometry} */ (feature.get(geometry)) - ); - }; - } else if (!geometry) { + /** + * @private + * @type {!module:ol/style/Style~GeometryFunction} + */ this.geometryFunction_ = defaultGeometryFunction; - } else if (geometry !== undefined) { - this.geometryFunction_ = function() { - return ( - /** @type {module:ol/geom/Geometry} */ (geometry) - ); - }; + + if (options.geometry !== undefined) { + this.setGeometry(options.geometry); + } + + /** + * @private + * @type {module:ol/style/Fill} + */ + this.fill_ = options.fill !== undefined ? options.fill : null; + + /** + * @private + * @type {module:ol/style/Image} + */ + this.image_ = options.image !== undefined ? options.image : null; + + /** + * @private + * @type {module:ol/style/Style~RenderFunction|null} + */ + this.renderer_ = options.renderer !== undefined ? options.renderer : null; + + /** + * @private + * @type {module:ol/style/Stroke} + */ + this.stroke_ = options.stroke !== undefined ? options.stroke : null; + + /** + * @private + * @type {module:ol/style/Text} + */ + this.text_ = options.text !== undefined ? options.text : null; + + /** + * @private + * @type {number|undefined} + */ + this.zIndex_ = options.zIndex; + } - this.geometry_ = geometry; -}; + /** + * Clones the style. + * @return {module:ol/style/Style} The cloned style. + * @api + */ + clone() { + let geometry = this.getGeometry(); + if (geometry && geometry.clone) { + geometry = geometry.clone(); + } + return new Style({ + geometry: geometry, + fill: this.getFill() ? this.getFill().clone() : undefined, + image: this.getImage() ? this.getImage().clone() : undefined, + stroke: this.getStroke() ? this.getStroke().clone() : undefined, + text: this.getText() ? this.getText().clone() : undefined, + zIndex: this.getZIndex() + }); + } -/** - * Set the z-index. - * - * @param {number|undefined} zIndex ZIndex. - * @api - */ -Style.prototype.setZIndex = function(zIndex) { - this.zIndex_ = zIndex; -}; + /** + * Get the custom renderer function that was configured with + * {@link #setRenderer} or the `renderer` constructor option. + * @return {module:ol/style/Style~RenderFunction|null} Custom renderer function. + * @api + */ + getRenderer() { + return this.renderer_; + } + + /** + * Sets a custom renderer function for this style. When set, `fill`, `stroke` + * and `image` options of the style will be ignored. + * @param {module:ol/style/Style~RenderFunction|null} renderer Custom renderer function. + * @api + */ + setRenderer(renderer) { + this.renderer_ = renderer; + } + + /** + * Get the geometry to be rendered. + * @return {string|module:ol/geom/Geometry|module:ol/style/Style~GeometryFunction} + * Feature property or geometry or function that returns the geometry that will + * be rendered with this style. + * @api + */ + getGeometry() { + return this.geometry_; + } + + /** + * Get the function used to generate a geometry for rendering. + * @return {!module:ol/style/Style~GeometryFunction} Function that is called with a feature + * and returns the geometry to render instead of the feature's geometry. + * @api + */ + getGeometryFunction() { + return this.geometryFunction_; + } + + /** + * Get the fill style. + * @return {module:ol/style/Fill} Fill style. + * @api + */ + getFill() { + return this.fill_; + } + + /** + * Set the fill style. + * @param {module:ol/style/Fill} fill Fill style. + * @api + */ + setFill(fill) { + this.fill_ = fill; + } + + /** + * Get the image style. + * @return {module:ol/style/Image} Image style. + * @api + */ + getImage() { + return this.image_; + } + + /** + * Set the image style. + * @param {module:ol/style/Image} image Image style. + * @api + */ + setImage(image) { + this.image_ = image; + } + + /** + * Get the stroke style. + * @return {module:ol/style/Stroke} Stroke style. + * @api + */ + getStroke() { + return this.stroke_; + } + + /** + * Set the stroke style. + * @param {module:ol/style/Stroke} stroke Stroke style. + * @api + */ + setStroke(stroke) { + this.stroke_ = stroke; + } + + /** + * Get the text style. + * @return {module:ol/style/Text} Text style. + * @api + */ + getText() { + return this.text_; + } + + /** + * Set the text style. + * @param {module:ol/style/Text} text Text style. + * @api + */ + setText(text) { + this.text_ = text; + } + + /** + * Get the z-index for the style. + * @return {number|undefined} ZIndex. + * @api + */ + getZIndex() { + return this.zIndex_; + } + + /** + * Set a geometry that is rendered instead of the feature's geometry. + * + * @param {string|module:ol/geom/Geometry|module:ol/style/Style~GeometryFunction} geometry + * Feature property or geometry or function returning a geometry to render + * for this style. + * @api + */ + setGeometry(geometry) { + if (typeof geometry === 'function') { + this.geometryFunction_ = geometry; + } else if (typeof geometry === 'string') { + this.geometryFunction_ = function(feature) { + return ( + /** @type {module:ol/geom/Geometry} */ (feature.get(geometry)) + ); + }; + } else if (!geometry) { + this.geometryFunction_ = defaultGeometryFunction; + } else if (geometry !== undefined) { + this.geometryFunction_ = function() { + return ( + /** @type {module:ol/geom/Geometry} */ (geometry) + ); + }; + } + this.geometry_ = geometry; + } + + /** + * Set the z-index. + * + * @param {number|undefined} zIndex ZIndex. + * @api + */ + setZIndex(zIndex) { + this.zIndex_ = zIndex; + } +} /** diff --git a/src/ol/style/Text.js b/src/ol/style/Text.js index 790b531b43..96a3da5edb 100644 --- a/src/ol/style/Text.js +++ b/src/ol/style/Text.js @@ -53,484 +53,453 @@ const DEFAULT_FILL_COLOR = '#333'; * @param {module:ol/style/Text~Options=} opt_options Options. * @api */ -const Text = function(opt_options) { - - const options = opt_options || {}; - - /** - * @private - * @type {string|undefined} - */ - this.font_ = options.font; - - /** - * @private - * @type {number|undefined} - */ - this.rotation_ = options.rotation; - - /** - * @private - * @type {boolean|undefined} - */ - this.rotateWithView_ = options.rotateWithView; - - /** - * @private - * @type {number|undefined} - */ - this.scale_ = options.scale; - - /** - * @private - * @type {string|undefined} - */ - this.text_ = options.text; - - /** - * @private - * @type {string|undefined} - */ - this.textAlign_ = options.textAlign; - - /** - * @private - * @type {string|undefined} - */ - this.textBaseline_ = options.textBaseline; - - /** - * @private - * @type {module:ol/style/Fill} - */ - this.fill_ = options.fill !== undefined ? options.fill : - new Fill({color: DEFAULT_FILL_COLOR}); - - /** - * @private - * @type {number} - */ - this.maxAngle_ = options.maxAngle !== undefined ? options.maxAngle : Math.PI / 4; - - /** - * @private - * @type {module:ol/style/TextPlacement|string} - */ - this.placement_ = options.placement !== undefined ? options.placement : TextPlacement.POINT; - - /** - * @private - * @type {boolean} - */ - this.overflow_ = !!options.overflow; - - /** - * @private - * @type {module:ol/style/Stroke} - */ - this.stroke_ = options.stroke !== undefined ? options.stroke : null; - - /** - * @private - * @type {number} - */ - this.offsetX_ = options.offsetX !== undefined ? options.offsetX : 0; - - /** - * @private - * @type {number} - */ - this.offsetY_ = options.offsetY !== undefined ? options.offsetY : 0; - - /** - * @private - * @type {module:ol/style/Fill} - */ - this.backgroundFill_ = options.backgroundFill ? options.backgroundFill : null; - - /** - * @private - * @type {module:ol/style/Stroke} - */ - this.backgroundStroke_ = options.backgroundStroke ? options.backgroundStroke : null; - - /** - * @private - * @type {Array.} - */ - this.padding_ = options.padding === undefined ? null : options.padding; -}; - - -/** - * Clones the style. - * @return {module:ol/style/Text} The cloned style. - * @api - */ -Text.prototype.clone = function() { - return new Text({ - font: this.getFont(), - placement: this.getPlacement(), - maxAngle: this.getMaxAngle(), - overflow: this.getOverflow(), - rotation: this.getRotation(), - rotateWithView: this.getRotateWithView(), - scale: this.getScale(), - text: this.getText(), - textAlign: this.getTextAlign(), - textBaseline: this.getTextBaseline(), - fill: this.getFill() ? this.getFill().clone() : undefined, - stroke: this.getStroke() ? this.getStroke().clone() : undefined, - offsetX: this.getOffsetX(), - offsetY: this.getOffsetY(), - backgroundFill: this.getBackgroundFill() ? this.getBackgroundFill().clone() : undefined, - backgroundStroke: this.getBackgroundStroke() ? this.getBackgroundStroke().clone() : undefined - }); -}; - - -/** - * Get the `overflow` configuration. - * @return {boolean} Let text overflow the length of the path they follow. - * @api - */ -Text.prototype.getOverflow = function() { - return this.overflow_; -}; - - -/** - * Get the font name. - * @return {string|undefined} Font. - * @api - */ -Text.prototype.getFont = function() { - return this.font_; -}; - - -/** - * Get the maximum angle between adjacent characters. - * @return {number} Angle in radians. - * @api - */ -Text.prototype.getMaxAngle = function() { - return this.maxAngle_; -}; - - -/** - * Get the label placement. - * @return {module:ol/style/TextPlacement|string} Text placement. - * @api - */ -Text.prototype.getPlacement = function() { - return this.placement_; -}; - - -/** - * Get the x-offset for the text. - * @return {number} Horizontal text offset. - * @api - */ -Text.prototype.getOffsetX = function() { - return this.offsetX_; -}; - - -/** - * Get the y-offset for the text. - * @return {number} Vertical text offset. - * @api - */ -Text.prototype.getOffsetY = function() { - return this.offsetY_; -}; - - -/** - * Get the fill style for the text. - * @return {module:ol/style/Fill} Fill style. - * @api - */ -Text.prototype.getFill = function() { - return this.fill_; -}; - - -/** - * Determine whether the text rotates with the map. - * @return {boolean|undefined} Rotate with map. - * @api - */ -Text.prototype.getRotateWithView = function() { - return this.rotateWithView_; -}; - - -/** - * Get the text rotation. - * @return {number|undefined} Rotation. - * @api - */ -Text.prototype.getRotation = function() { - return this.rotation_; -}; - - -/** - * Get the text scale. - * @return {number|undefined} Scale. - * @api - */ -Text.prototype.getScale = function() { - return this.scale_; -}; - - -/** - * Get the stroke style for the text. - * @return {module:ol/style/Stroke} Stroke style. - * @api - */ -Text.prototype.getStroke = function() { - return this.stroke_; -}; - - -/** - * Get the text to be rendered. - * @return {string|undefined} Text. - * @api - */ -Text.prototype.getText = function() { - return this.text_; -}; - - -/** - * Get the text alignment. - * @return {string|undefined} Text align. - * @api - */ -Text.prototype.getTextAlign = function() { - return this.textAlign_; -}; - - -/** - * Get the text baseline. - * @return {string|undefined} Text baseline. - * @api - */ -Text.prototype.getTextBaseline = function() { - return this.textBaseline_; -}; - - -/** - * Get the background fill style for the text. - * @return {module:ol/style/Fill} Fill style. - * @api - */ -Text.prototype.getBackgroundFill = function() { - return this.backgroundFill_; -}; - - -/** - * Get the background stroke style for the text. - * @return {module:ol/style/Stroke} Stroke style. - * @api - */ -Text.prototype.getBackgroundStroke = function() { - return this.backgroundStroke_; -}; - - -/** - * Get the padding for the text. - * @return {Array.} Padding. - * @api - */ -Text.prototype.getPadding = function() { - return this.padding_; -}; - - -/** - * Set the `overflow` property. - * - * @param {boolean} overflow Let text overflow the path that it follows. - * @api - */ -Text.prototype.setOverflow = function(overflow) { - this.overflow_ = overflow; -}; - - -/** - * Set the font. - * - * @param {string|undefined} font Font. - * @api - */ -Text.prototype.setFont = function(font) { - this.font_ = font; -}; - - -/** - * Set the maximum angle between adjacent characters. - * - * @param {number} maxAngle Angle in radians. - * @api - */ -Text.prototype.setMaxAngle = function(maxAngle) { - this.maxAngle_ = maxAngle; -}; - - -/** - * Set the x offset. - * - * @param {number} offsetX Horizontal text offset. - * @api - */ -Text.prototype.setOffsetX = function(offsetX) { - this.offsetX_ = offsetX; -}; - - -/** - * Set the y offset. - * - * @param {number} offsetY Vertical text offset. - * @api - */ -Text.prototype.setOffsetY = function(offsetY) { - this.offsetY_ = offsetY; -}; - - -/** - * Set the text placement. - * - * @param {module:ol/style/TextPlacement|string} placement Placement. - * @api - */ -Text.prototype.setPlacement = function(placement) { - this.placement_ = placement; -}; - - -/** - * Set the fill. - * - * @param {module:ol/style/Fill} fill Fill style. - * @api - */ -Text.prototype.setFill = function(fill) { - this.fill_ = fill; -}; - - -/** - * Set the rotation. - * - * @param {number|undefined} rotation Rotation. - * @api - */ -Text.prototype.setRotation = function(rotation) { - this.rotation_ = rotation; -}; - - -/** - * Set the scale. - * - * @param {number|undefined} scale Scale. - * @api - */ -Text.prototype.setScale = function(scale) { - this.scale_ = scale; -}; - - -/** - * Set the stroke. - * - * @param {module:ol/style/Stroke} stroke Stroke style. - * @api - */ -Text.prototype.setStroke = function(stroke) { - this.stroke_ = stroke; -}; - - -/** - * Set the text. - * - * @param {string|undefined} text Text. - * @api - */ -Text.prototype.setText = function(text) { - this.text_ = text; -}; - - -/** - * Set the text alignment. - * - * @param {string|undefined} textAlign Text align. - * @api - */ -Text.prototype.setTextAlign = function(textAlign) { - this.textAlign_ = textAlign; -}; - - -/** - * Set the text baseline. - * - * @param {string|undefined} textBaseline Text baseline. - * @api - */ -Text.prototype.setTextBaseline = function(textBaseline) { - this.textBaseline_ = textBaseline; -}; - - -/** - * Set the background fill. - * - * @param {module:ol/style/Fill} fill Fill style. - * @api - */ -Text.prototype.setBackgroundFill = function(fill) { - this.backgroundFill_ = fill; -}; - - -/** - * Set the background stroke. - * - * @param {module:ol/style/Stroke} stroke Stroke style. - * @api - */ -Text.prototype.setBackgroundStroke = function(stroke) { - this.backgroundStroke_ = stroke; -}; - - -/** - * Set the padding (`[top, right, bottom, left]`). - * - * @param {!Array.} padding Padding. - * @api - */ -Text.prototype.setPadding = function(padding) { - this.padding_ = padding; -}; +class Text { + constructor(opt_options) { + + const options = opt_options || {}; + + /** + * @private + * @type {string|undefined} + */ + this.font_ = options.font; + + /** + * @private + * @type {number|undefined} + */ + this.rotation_ = options.rotation; + + /** + * @private + * @type {boolean|undefined} + */ + this.rotateWithView_ = options.rotateWithView; + + /** + * @private + * @type {number|undefined} + */ + this.scale_ = options.scale; + + /** + * @private + * @type {string|undefined} + */ + this.text_ = options.text; + + /** + * @private + * @type {string|undefined} + */ + this.textAlign_ = options.textAlign; + + /** + * @private + * @type {string|undefined} + */ + this.textBaseline_ = options.textBaseline; + + /** + * @private + * @type {module:ol/style/Fill} + */ + this.fill_ = options.fill !== undefined ? options.fill : + new Fill({color: DEFAULT_FILL_COLOR}); + + /** + * @private + * @type {number} + */ + this.maxAngle_ = options.maxAngle !== undefined ? options.maxAngle : Math.PI / 4; + + /** + * @private + * @type {module:ol/style/TextPlacement|string} + */ + this.placement_ = options.placement !== undefined ? options.placement : TextPlacement.POINT; + + /** + * @private + * @type {boolean} + */ + this.overflow_ = !!options.overflow; + + /** + * @private + * @type {module:ol/style/Stroke} + */ + this.stroke_ = options.stroke !== undefined ? options.stroke : null; + + /** + * @private + * @type {number} + */ + this.offsetX_ = options.offsetX !== undefined ? options.offsetX : 0; + + /** + * @private + * @type {number} + */ + this.offsetY_ = options.offsetY !== undefined ? options.offsetY : 0; + + /** + * @private + * @type {module:ol/style/Fill} + */ + this.backgroundFill_ = options.backgroundFill ? options.backgroundFill : null; + + /** + * @private + * @type {module:ol/style/Stroke} + */ + this.backgroundStroke_ = options.backgroundStroke ? options.backgroundStroke : null; + + /** + * @private + * @type {Array.} + */ + this.padding_ = options.padding === undefined ? null : options.padding; + } + + /** + * Clones the style. + * @return {module:ol/style/Text} The cloned style. + * @api + */ + clone() { + return new Text({ + font: this.getFont(), + placement: this.getPlacement(), + maxAngle: this.getMaxAngle(), + overflow: this.getOverflow(), + rotation: this.getRotation(), + rotateWithView: this.getRotateWithView(), + scale: this.getScale(), + text: this.getText(), + textAlign: this.getTextAlign(), + textBaseline: this.getTextBaseline(), + fill: this.getFill() ? this.getFill().clone() : undefined, + stroke: this.getStroke() ? this.getStroke().clone() : undefined, + offsetX: this.getOffsetX(), + offsetY: this.getOffsetY(), + backgroundFill: this.getBackgroundFill() ? this.getBackgroundFill().clone() : undefined, + backgroundStroke: this.getBackgroundStroke() ? this.getBackgroundStroke().clone() : undefined + }); + } + + /** + * Get the `overflow` configuration. + * @return {boolean} Let text overflow the length of the path they follow. + * @api + */ + getOverflow() { + return this.overflow_; + } + + /** + * Get the font name. + * @return {string|undefined} Font. + * @api + */ + getFont() { + return this.font_; + } + + /** + * Get the maximum angle between adjacent characters. + * @return {number} Angle in radians. + * @api + */ + getMaxAngle() { + return this.maxAngle_; + } + + /** + * Get the label placement. + * @return {module:ol/style/TextPlacement|string} Text placement. + * @api + */ + getPlacement() { + return this.placement_; + } + + /** + * Get the x-offset for the text. + * @return {number} Horizontal text offset. + * @api + */ + getOffsetX() { + return this.offsetX_; + } + + /** + * Get the y-offset for the text. + * @return {number} Vertical text offset. + * @api + */ + getOffsetY() { + return this.offsetY_; + } + + /** + * Get the fill style for the text. + * @return {module:ol/style/Fill} Fill style. + * @api + */ + getFill() { + return this.fill_; + } + + /** + * Determine whether the text rotates with the map. + * @return {boolean|undefined} Rotate with map. + * @api + */ + getRotateWithView() { + return this.rotateWithView_; + } + + /** + * Get the text rotation. + * @return {number|undefined} Rotation. + * @api + */ + getRotation() { + return this.rotation_; + } + + /** + * Get the text scale. + * @return {number|undefined} Scale. + * @api + */ + getScale() { + return this.scale_; + } + + /** + * Get the stroke style for the text. + * @return {module:ol/style/Stroke} Stroke style. + * @api + */ + getStroke() { + return this.stroke_; + } + + /** + * Get the text to be rendered. + * @return {string|undefined} Text. + * @api + */ + getText() { + return this.text_; + } + + /** + * Get the text alignment. + * @return {string|undefined} Text align. + * @api + */ + getTextAlign() { + return this.textAlign_; + } + + /** + * Get the text baseline. + * @return {string|undefined} Text baseline. + * @api + */ + getTextBaseline() { + return this.textBaseline_; + } + + /** + * Get the background fill style for the text. + * @return {module:ol/style/Fill} Fill style. + * @api + */ + getBackgroundFill() { + return this.backgroundFill_; + } + + /** + * Get the background stroke style for the text. + * @return {module:ol/style/Stroke} Stroke style. + * @api + */ + getBackgroundStroke() { + return this.backgroundStroke_; + } + + /** + * Get the padding for the text. + * @return {Array.} Padding. + * @api + */ + getPadding() { + return this.padding_; + } + + /** + * Set the `overflow` property. + * + * @param {boolean} overflow Let text overflow the path that it follows. + * @api + */ + setOverflow(overflow) { + this.overflow_ = overflow; + } + + /** + * Set the font. + * + * @param {string|undefined} font Font. + * @api + */ + setFont(font) { + this.font_ = font; + } + + /** + * Set the maximum angle between adjacent characters. + * + * @param {number} maxAngle Angle in radians. + * @api + */ + setMaxAngle(maxAngle) { + this.maxAngle_ = maxAngle; + } + + /** + * Set the x offset. + * + * @param {number} offsetX Horizontal text offset. + * @api + */ + setOffsetX(offsetX) { + this.offsetX_ = offsetX; + } + + /** + * Set the y offset. + * + * @param {number} offsetY Vertical text offset. + * @api + */ + setOffsetY(offsetY) { + this.offsetY_ = offsetY; + } + + /** + * Set the text placement. + * + * @param {module:ol/style/TextPlacement|string} placement Placement. + * @api + */ + setPlacement(placement) { + this.placement_ = placement; + } + + /** + * Set the fill. + * + * @param {module:ol/style/Fill} fill Fill style. + * @api + */ + setFill(fill) { + this.fill_ = fill; + } + + /** + * Set the rotation. + * + * @param {number|undefined} rotation Rotation. + * @api + */ + setRotation(rotation) { + this.rotation_ = rotation; + } + + /** + * Set the scale. + * + * @param {number|undefined} scale Scale. + * @api + */ + setScale(scale) { + this.scale_ = scale; + } + + /** + * Set the stroke. + * + * @param {module:ol/style/Stroke} stroke Stroke style. + * @api + */ + setStroke(stroke) { + this.stroke_ = stroke; + } + + /** + * Set the text. + * + * @param {string|undefined} text Text. + * @api + */ + setText(text) { + this.text_ = text; + } + + /** + * Set the text alignment. + * + * @param {string|undefined} textAlign Text align. + * @api + */ + setTextAlign(textAlign) { + this.textAlign_ = textAlign; + } + + /** + * Set the text baseline. + * + * @param {string|undefined} textBaseline Text baseline. + * @api + */ + setTextBaseline(textBaseline) { + this.textBaseline_ = textBaseline; + } + + /** + * Set the background fill. + * + * @param {module:ol/style/Fill} fill Fill style. + * @api + */ + setBackgroundFill(fill) { + this.backgroundFill_ = fill; + } + + /** + * Set the background stroke. + * + * @param {module:ol/style/Stroke} stroke Stroke style. + * @api + */ + setBackgroundStroke(stroke) { + this.backgroundStroke_ = stroke; + } + + /** + * Set the padding (`[top, right, bottom, left]`). + * + * @param {!Array.} padding Padding. + * @api + */ + setPadding(padding) { + this.padding_ = padding; + } +} + export default Text; diff --git a/src/ol/tilegrid/TileGrid.js b/src/ol/tilegrid/TileGrid.js index 2dd3d90079..f7edb6a322 100644 --- a/src/ol/tilegrid/TileGrid.js +++ b/src/ol/tilegrid/TileGrid.js @@ -47,135 +47,501 @@ import {createOrUpdate as createOrUpdateTileCoord} from '../tilecoord.js'; * @struct * @api */ -const TileGrid = function(options) { +class TileGrid { + constructor(options) { - /** - * @protected - * @type {number} - */ - this.minZoom = options.minZoom !== undefined ? options.minZoom : 0; + /** + * @protected + * @type {number} + */ + this.minZoom = options.minZoom !== undefined ? options.minZoom : 0; - /** - * @private - * @type {!Array.} - */ - this.resolutions_ = options.resolutions; - assert(isSorted(this.resolutions_, function(a, b) { - return b - a; - }, true), 17); // `resolutions` must be sorted in descending order + /** + * @private + * @type {!Array.} + */ + this.resolutions_ = options.resolutions; + assert(isSorted(this.resolutions_, function(a, b) { + return b - a; + }, true), 17); // `resolutions` must be sorted in descending order - // check if we've got a consistent zoom factor and origin - let zoomFactor; - if (!options.origins) { - for (let i = 0, ii = this.resolutions_.length - 1; i < ii; ++i) { - if (!zoomFactor) { - zoomFactor = this.resolutions_[i] / this.resolutions_[i + 1]; - } else { - if (this.resolutions_[i] / this.resolutions_[i + 1] !== zoomFactor) { - zoomFactor = undefined; - break; + // check if we've got a consistent zoom factor and origin + let zoomFactor; + if (!options.origins) { + for (let i = 0, ii = this.resolutions_.length - 1; i < ii; ++i) { + if (!zoomFactor) { + zoomFactor = this.resolutions_[i] / this.resolutions_[i + 1]; + } else { + if (this.resolutions_[i] / this.resolutions_[i + 1] !== zoomFactor) { + zoomFactor = undefined; + break; + } } } } + + + /** + * @private + * @type {number|undefined} + */ + this.zoomFactor_ = zoomFactor; + + + /** + * @protected + * @type {number} + */ + this.maxZoom = this.resolutions_.length - 1; + + /** + * @private + * @type {module:ol/coordinate~Coordinate} + */ + this.origin_ = options.origin !== undefined ? options.origin : null; + + /** + * @private + * @type {Array.} + */ + this.origins_ = null; + if (options.origins !== undefined) { + this.origins_ = options.origins; + assert(this.origins_.length == this.resolutions_.length, + 20); // Number of `origins` and `resolutions` must be equal + } + + const extent = options.extent; + + if (extent !== undefined && + !this.origin_ && !this.origins_) { + this.origin_ = getTopLeft(extent); + } + + assert( + (!this.origin_ && this.origins_) || (this.origin_ && !this.origins_), + 18); // Either `origin` or `origins` must be configured, never both + + /** + * @private + * @type {Array.} + */ + this.tileSizes_ = null; + if (options.tileSizes !== undefined) { + this.tileSizes_ = options.tileSizes; + assert(this.tileSizes_.length == this.resolutions_.length, + 19); // Number of `tileSizes` and `resolutions` must be equal + } + + /** + * @private + * @type {number|module:ol/size~Size} + */ + this.tileSize_ = options.tileSize !== undefined ? + options.tileSize : + !this.tileSizes_ ? DEFAULT_TILE_SIZE : null; + assert( + (!this.tileSize_ && this.tileSizes_) || + (this.tileSize_ && !this.tileSizes_), + 22); // Either `tileSize` or `tileSizes` must be configured, never both + + /** + * @private + * @type {module:ol/extent~Extent} + */ + this.extent_ = extent !== undefined ? extent : null; + + + /** + * @private + * @type {Array.} + */ + this.fullTileRanges_ = null; + + /** + * @private + * @type {module:ol/size~Size} + */ + this.tmpSize_ = [0, 0]; + + if (options.sizes !== undefined) { + this.fullTileRanges_ = options.sizes.map(function(size, z) { + const tileRange = new TileRange( + Math.min(0, size[0]), Math.max(size[0] - 1, -1), + Math.min(0, size[1]), Math.max(size[1] - 1, -1)); + return tileRange; + }, this); + } else if (extent) { + this.calculateTileRanges_(extent); + } + + } + + /** + * Call a function with each tile coordinate for a given extent and zoom level. + * + * @param {module:ol/extent~Extent} extent Extent. + * @param {number} zoom Integer zoom level. + * @param {function(module:ol/tilecoord~TileCoord)} callback Function called with each tile coordinate. + * @api + */ + forEachTileCoord(extent, zoom, callback) { + const tileRange = this.getTileRangeForExtentAndZ(extent, zoom); + for (let i = tileRange.minX, ii = tileRange.maxX; i <= ii; ++i) { + for (let j = tileRange.minY, jj = tileRange.maxY; j <= jj; ++j) { + callback([zoom, i, j]); + } + } } - /** - * @private - * @type {number|undefined} + * @param {module:ol/tilecoord~TileCoord} tileCoord Tile coordinate. + * @param {function(this: T, number, module:ol/TileRange): boolean} callback Callback. + * @param {T=} opt_this The object to use as `this` in `callback`. + * @param {module:ol/TileRange=} opt_tileRange Temporary module:ol/TileRange object. + * @param {module:ol/extent~Extent=} opt_extent Temporary module:ol/extent~Extent object. + * @return {boolean} Callback succeeded. + * @template T */ - this.zoomFactor_ = zoomFactor; - - - /** - * @protected - * @type {number} - */ - this.maxZoom = this.resolutions_.length - 1; - - /** - * @private - * @type {module:ol/coordinate~Coordinate} - */ - this.origin_ = options.origin !== undefined ? options.origin : null; - - /** - * @private - * @type {Array.} - */ - this.origins_ = null; - if (options.origins !== undefined) { - this.origins_ = options.origins; - assert(this.origins_.length == this.resolutions_.length, - 20); // Number of `origins` and `resolutions` must be equal - } - - const extent = options.extent; - - if (extent !== undefined && - !this.origin_ && !this.origins_) { - this.origin_ = getTopLeft(extent); - } - - assert( - (!this.origin_ && this.origins_) || (this.origin_ && !this.origins_), - 18); // Either `origin` or `origins` must be configured, never both - - /** - * @private - * @type {Array.} - */ - this.tileSizes_ = null; - if (options.tileSizes !== undefined) { - this.tileSizes_ = options.tileSizes; - assert(this.tileSizes_.length == this.resolutions_.length, - 19); // Number of `tileSizes` and `resolutions` must be equal + forEachTileCoordParentTileRange(tileCoord, callback, opt_this, opt_tileRange, opt_extent) { + let tileRange, x, y; + let tileCoordExtent = null; + let z = tileCoord[0] - 1; + if (this.zoomFactor_ === 2) { + x = tileCoord[1]; + y = tileCoord[2]; + } else { + tileCoordExtent = this.getTileCoordExtent(tileCoord, opt_extent); + } + while (z >= this.minZoom) { + if (this.zoomFactor_ === 2) { + x = Math.floor(x / 2); + y = Math.floor(y / 2); + tileRange = createOrUpdateTileRange(x, x, y, y, opt_tileRange); + } else { + tileRange = this.getTileRangeForExtentAndZ(tileCoordExtent, z, opt_tileRange); + } + if (callback.call(opt_this, z, tileRange)) { + return true; + } + --z; + } + return false; } /** - * @private - * @type {number|module:ol/size~Size} + * Get the extent for this tile grid, if it was configured. + * @return {module:ol/extent~Extent} Extent. */ - this.tileSize_ = options.tileSize !== undefined ? - options.tileSize : - !this.tileSizes_ ? DEFAULT_TILE_SIZE : null; - assert( - (!this.tileSize_ && this.tileSizes_) || - (this.tileSize_ && !this.tileSizes_), - 22); // Either `tileSize` or `tileSizes` must be configured, never both - - /** - * @private - * @type {module:ol/extent~Extent} - */ - this.extent_ = extent !== undefined ? extent : null; - - - /** - * @private - * @type {Array.} - */ - this.fullTileRanges_ = null; - - /** - * @private - * @type {module:ol/size~Size} - */ - this.tmpSize_ = [0, 0]; - - if (options.sizes !== undefined) { - this.fullTileRanges_ = options.sizes.map(function(size, z) { - const tileRange = new TileRange( - Math.min(0, size[0]), Math.max(size[0] - 1, -1), - Math.min(0, size[1]), Math.max(size[1] - 1, -1)); - return tileRange; - }, this); - } else if (extent) { - this.calculateTileRanges_(extent); + getExtent() { + return this.extent_; } -}; + /** + * Get the maximum zoom level for the grid. + * @return {number} Max zoom. + * @api + */ + getMaxZoom() { + return this.maxZoom; + } + + /** + * Get the minimum zoom level for the grid. + * @return {number} Min zoom. + * @api + */ + getMinZoom() { + return this.minZoom; + } + + /** + * Get the origin for the grid at the given zoom level. + * @param {number} z Integer zoom level. + * @return {module:ol/coordinate~Coordinate} Origin. + * @api + */ + getOrigin(z) { + if (this.origin_) { + return this.origin_; + } else { + return this.origins_[z]; + } + } + + /** + * Get the resolution for the given zoom level. + * @param {number} z Integer zoom level. + * @return {number} Resolution. + * @api + */ + getResolution(z) { + return this.resolutions_[z]; + } + + /** + * Get the list of resolutions for the tile grid. + * @return {Array.} Resolutions. + * @api + */ + getResolutions() { + return this.resolutions_; + } + + /** + * @param {module:ol/tilecoord~TileCoord} tileCoord Tile coordinate. + * @param {module:ol/TileRange=} opt_tileRange Temporary module:ol/TileRange object. + * @param {module:ol/extent~Extent=} opt_extent Temporary module:ol/extent~Extent object. + * @return {module:ol/TileRange} Tile range. + */ + getTileCoordChildTileRange(tileCoord, opt_tileRange, opt_extent) { + if (tileCoord[0] < this.maxZoom) { + if (this.zoomFactor_ === 2) { + const minX = tileCoord[1] * 2; + const minY = tileCoord[2] * 2; + return createOrUpdateTileRange(minX, minX + 1, minY, minY + 1, opt_tileRange); + } + const tileCoordExtent = this.getTileCoordExtent(tileCoord, opt_extent); + return this.getTileRangeForExtentAndZ( + tileCoordExtent, tileCoord[0] + 1, opt_tileRange); + } + return null; + } + + /** + * Get the extent for a tile range. + * @param {number} z Integer zoom level. + * @param {module:ol/TileRange} tileRange Tile range. + * @param {module:ol/extent~Extent=} opt_extent Temporary module:ol/extent~Extent object. + * @return {module:ol/extent~Extent} Extent. + */ + getTileRangeExtent(z, tileRange, opt_extent) { + const origin = this.getOrigin(z); + const resolution = this.getResolution(z); + const tileSize = toSize(this.getTileSize(z), this.tmpSize_); + const minX = origin[0] + tileRange.minX * tileSize[0] * resolution; + const maxX = origin[0] + (tileRange.maxX + 1) * tileSize[0] * resolution; + const minY = origin[1] + tileRange.minY * tileSize[1] * resolution; + const maxY = origin[1] + (tileRange.maxY + 1) * tileSize[1] * resolution; + return createOrUpdate(minX, minY, maxX, maxY, opt_extent); + } + + /** + * Get a tile range for the given extent and integer zoom level. + * @param {module:ol/extent~Extent} extent Extent. + * @param {number} z Integer zoom level. + * @param {module:ol/TileRange=} opt_tileRange Temporary tile range object. + * @return {module:ol/TileRange} Tile range. + */ + getTileRangeForExtentAndZ(extent, z, opt_tileRange) { + const tileCoord = tmpTileCoord; + this.getTileCoordForXYAndZ_(extent[0], extent[1], z, false, tileCoord); + const minX = tileCoord[1]; + const minY = tileCoord[2]; + this.getTileCoordForXYAndZ_(extent[2], extent[3], z, true, tileCoord); + return createOrUpdateTileRange(minX, tileCoord[1], minY, tileCoord[2], opt_tileRange); + } + + /** + * @param {module:ol/tilecoord~TileCoord} tileCoord Tile coordinate. + * @return {module:ol/coordinate~Coordinate} Tile center. + */ + getTileCoordCenter(tileCoord) { + const origin = this.getOrigin(tileCoord[0]); + const resolution = this.getResolution(tileCoord[0]); + const tileSize = toSize(this.getTileSize(tileCoord[0]), this.tmpSize_); + return [ + origin[0] + (tileCoord[1] + 0.5) * tileSize[0] * resolution, + origin[1] + (tileCoord[2] + 0.5) * tileSize[1] * resolution + ]; + } + + /** + * Get the extent of a tile coordinate. + * + * @param {module:ol/tilecoord~TileCoord} tileCoord Tile coordinate. + * @param {module:ol/extent~Extent=} opt_extent Temporary extent object. + * @return {module:ol/extent~Extent} Extent. + * @api + */ + getTileCoordExtent(tileCoord, opt_extent) { + const origin = this.getOrigin(tileCoord[0]); + const resolution = this.getResolution(tileCoord[0]); + const tileSize = toSize(this.getTileSize(tileCoord[0]), this.tmpSize_); + const minX = origin[0] + tileCoord[1] * tileSize[0] * resolution; + const minY = origin[1] + tileCoord[2] * tileSize[1] * resolution; + const maxX = minX + tileSize[0] * resolution; + const maxY = minY + tileSize[1] * resolution; + return createOrUpdate(minX, minY, maxX, maxY, opt_extent); + } + + /** + * Get the tile coordinate for the given map coordinate and resolution. This + * method considers that coordinates that intersect tile boundaries should be + * assigned the higher tile coordinate. + * + * @param {module:ol/coordinate~Coordinate} coordinate Coordinate. + * @param {number} resolution Resolution. + * @param {module:ol/tilecoord~TileCoord=} opt_tileCoord Destination module:ol/tilecoord~TileCoord object. + * @return {module:ol/tilecoord~TileCoord} Tile coordinate. + * @api + */ + getTileCoordForCoordAndResolution(coordinate, resolution, opt_tileCoord) { + return this.getTileCoordForXYAndResolution_( + coordinate[0], coordinate[1], resolution, false, opt_tileCoord); + } + + /** + * Note that this method should not be called for resolutions that correspond + * to an integer zoom level. Instead call the `getTileCoordForXYAndZ_` method. + * @param {number} x X. + * @param {number} y Y. + * @param {number} resolution Resolution (for a non-integer zoom level). + * @param {boolean} reverseIntersectionPolicy Instead of letting edge + * intersections go to the higher tile coordinate, let edge intersections + * go to the lower tile coordinate. + * @param {module:ol/tilecoord~TileCoord=} opt_tileCoord Temporary module:ol/tilecoord~TileCoord object. + * @return {module:ol/tilecoord~TileCoord} Tile coordinate. + * @private + */ + getTileCoordForXYAndResolution_(x, y, resolution, reverseIntersectionPolicy, opt_tileCoord) { + const z = this.getZForResolution(resolution); + const scale = resolution / this.getResolution(z); + const origin = this.getOrigin(z); + const tileSize = toSize(this.getTileSize(z), this.tmpSize_); + + const adjustX = reverseIntersectionPolicy ? 0.5 : 0; + const adjustY = reverseIntersectionPolicy ? 0 : 0.5; + const xFromOrigin = Math.floor((x - origin[0]) / resolution + adjustX); + const yFromOrigin = Math.floor((y - origin[1]) / resolution + adjustY); + let tileCoordX = scale * xFromOrigin / tileSize[0]; + let tileCoordY = scale * yFromOrigin / tileSize[1]; + + if (reverseIntersectionPolicy) { + tileCoordX = Math.ceil(tileCoordX) - 1; + tileCoordY = Math.ceil(tileCoordY) - 1; + } else { + tileCoordX = Math.floor(tileCoordX); + tileCoordY = Math.floor(tileCoordY); + } + + return createOrUpdateTileCoord(z, tileCoordX, tileCoordY, opt_tileCoord); + } + + /** + * Although there is repetition between this method and `getTileCoordForXYAndResolution_`, + * they should have separate implementations. This method is for integer zoom + * levels. The other method should only be called for resolutions corresponding + * to non-integer zoom levels. + * @param {number} x Map x coordinate. + * @param {number} y Map y coordinate. + * @param {number} z Integer zoom level. + * @param {boolean} reverseIntersectionPolicy Instead of letting edge + * intersections go to the higher tile coordinate, let edge intersections + * go to the lower tile coordinate. + * @param {module:ol/tilecoord~TileCoord=} opt_tileCoord Temporary module:ol/tilecoord~TileCoord object. + * @return {module:ol/tilecoord~TileCoord} Tile coordinate. + * @private + */ + getTileCoordForXYAndZ_(x, y, z, reverseIntersectionPolicy, opt_tileCoord) { + const origin = this.getOrigin(z); + const resolution = this.getResolution(z); + const tileSize = toSize(this.getTileSize(z), this.tmpSize_); + + const adjustX = reverseIntersectionPolicy ? 0.5 : 0; + const adjustY = reverseIntersectionPolicy ? 0 : 0.5; + const xFromOrigin = Math.floor((x - origin[0]) / resolution + adjustX); + const yFromOrigin = Math.floor((y - origin[1]) / resolution + adjustY); + let tileCoordX = xFromOrigin / tileSize[0]; + let tileCoordY = yFromOrigin / tileSize[1]; + + if (reverseIntersectionPolicy) { + tileCoordX = Math.ceil(tileCoordX) - 1; + tileCoordY = Math.ceil(tileCoordY) - 1; + } else { + tileCoordX = Math.floor(tileCoordX); + tileCoordY = Math.floor(tileCoordY); + } + + return createOrUpdateTileCoord(z, tileCoordX, tileCoordY, opt_tileCoord); + } + + /** + * Get a tile coordinate given a map coordinate and zoom level. + * @param {module:ol/coordinate~Coordinate} coordinate Coordinate. + * @param {number} z Zoom level. + * @param {module:ol/tilecoord~TileCoord=} opt_tileCoord Destination module:ol/tilecoord~TileCoord object. + * @return {module:ol/tilecoord~TileCoord} Tile coordinate. + * @api + */ + getTileCoordForCoordAndZ(coordinate, z, opt_tileCoord) { + return this.getTileCoordForXYAndZ_( + coordinate[0], coordinate[1], z, false, opt_tileCoord); + } + + /** + * @param {module:ol/tilecoord~TileCoord} tileCoord Tile coordinate. + * @return {number} Tile resolution. + */ + getTileCoordResolution(tileCoord) { + return this.resolutions_[tileCoord[0]]; + } + + /** + * Get the tile size for a zoom level. The type of the return value matches the + * `tileSize` or `tileSizes` that the tile grid was configured with. To always + * get an `module:ol/size~Size`, run the result through `module:ol/size~Size.toSize()`. + * @param {number} z Z. + * @return {number|module:ol/size~Size} Tile size. + * @api + */ + getTileSize(z) { + if (this.tileSize_) { + return this.tileSize_; + } else { + return this.tileSizes_[z]; + } + } + + /** + * @param {number} z Zoom level. + * @return {module:ol/TileRange} Extent tile range for the specified zoom level. + */ + getFullTileRange(z) { + if (!this.fullTileRanges_) { + return null; + } else { + return this.fullTileRanges_[z]; + } + } + + /** + * @param {number} resolution Resolution. + * @param {number=} opt_direction If 0, the nearest resolution will be used. + * If 1, the nearest lower resolution will be used. If -1, the nearest + * higher resolution will be used. Default is 0. + * @return {number} Z. + * @api + */ + getZForResolution(resolution, opt_direction) { + const z = linearFindNearest(this.resolutions_, resolution, opt_direction || 0); + return clamp(z, this.minZoom, this.maxZoom); + } + + /** + * @param {!module:ol/extent~Extent} extent Extent for this tile grid. + * @private + */ + calculateTileRanges_(extent) { + const length = this.resolutions_.length; + const fullTileRanges = new Array(length); + for (let z = this.minZoom; z < length; ++z) { + fullTileRanges[z] = this.getTileRangeForExtentAndZ(extent, z); + } + this.fullTileRanges_ = fullTileRanges; + } +} /** @@ -185,389 +551,4 @@ const TileGrid = function(options) { const tmpTileCoord = [0, 0, 0]; -/** - * Call a function with each tile coordinate for a given extent and zoom level. - * - * @param {module:ol/extent~Extent} extent Extent. - * @param {number} zoom Integer zoom level. - * @param {function(module:ol/tilecoord~TileCoord)} callback Function called with each tile coordinate. - * @api - */ -TileGrid.prototype.forEachTileCoord = function(extent, zoom, callback) { - const tileRange = this.getTileRangeForExtentAndZ(extent, zoom); - for (let i = tileRange.minX, ii = tileRange.maxX; i <= ii; ++i) { - for (let j = tileRange.minY, jj = tileRange.maxY; j <= jj; ++j) { - callback([zoom, i, j]); - } - } -}; - - -/** - * @param {module:ol/tilecoord~TileCoord} tileCoord Tile coordinate. - * @param {function(this: T, number, module:ol/TileRange): boolean} callback Callback. - * @param {T=} opt_this The object to use as `this` in `callback`. - * @param {module:ol/TileRange=} opt_tileRange Temporary module:ol/TileRange object. - * @param {module:ol/extent~Extent=} opt_extent Temporary module:ol/extent~Extent object. - * @return {boolean} Callback succeeded. - * @template T - */ -TileGrid.prototype.forEachTileCoordParentTileRange = function(tileCoord, callback, opt_this, opt_tileRange, opt_extent) { - let tileRange, x, y; - let tileCoordExtent = null; - let z = tileCoord[0] - 1; - if (this.zoomFactor_ === 2) { - x = tileCoord[1]; - y = tileCoord[2]; - } else { - tileCoordExtent = this.getTileCoordExtent(tileCoord, opt_extent); - } - while (z >= this.minZoom) { - if (this.zoomFactor_ === 2) { - x = Math.floor(x / 2); - y = Math.floor(y / 2); - tileRange = createOrUpdateTileRange(x, x, y, y, opt_tileRange); - } else { - tileRange = this.getTileRangeForExtentAndZ(tileCoordExtent, z, opt_tileRange); - } - if (callback.call(opt_this, z, tileRange)) { - return true; - } - --z; - } - return false; -}; - - -/** - * Get the extent for this tile grid, if it was configured. - * @return {module:ol/extent~Extent} Extent. - */ -TileGrid.prototype.getExtent = function() { - return this.extent_; -}; - - -/** - * Get the maximum zoom level for the grid. - * @return {number} Max zoom. - * @api - */ -TileGrid.prototype.getMaxZoom = function() { - return this.maxZoom; -}; - - -/** - * Get the minimum zoom level for the grid. - * @return {number} Min zoom. - * @api - */ -TileGrid.prototype.getMinZoom = function() { - return this.minZoom; -}; - - -/** - * Get the origin for the grid at the given zoom level. - * @param {number} z Integer zoom level. - * @return {module:ol/coordinate~Coordinate} Origin. - * @api - */ -TileGrid.prototype.getOrigin = function(z) { - if (this.origin_) { - return this.origin_; - } else { - return this.origins_[z]; - } -}; - - -/** - * Get the resolution for the given zoom level. - * @param {number} z Integer zoom level. - * @return {number} Resolution. - * @api - */ -TileGrid.prototype.getResolution = function(z) { - return this.resolutions_[z]; -}; - - -/** - * Get the list of resolutions for the tile grid. - * @return {Array.} Resolutions. - * @api - */ -TileGrid.prototype.getResolutions = function() { - return this.resolutions_; -}; - - -/** - * @param {module:ol/tilecoord~TileCoord} tileCoord Tile coordinate. - * @param {module:ol/TileRange=} opt_tileRange Temporary module:ol/TileRange object. - * @param {module:ol/extent~Extent=} opt_extent Temporary module:ol/extent~Extent object. - * @return {module:ol/TileRange} Tile range. - */ -TileGrid.prototype.getTileCoordChildTileRange = function(tileCoord, opt_tileRange, opt_extent) { - if (tileCoord[0] < this.maxZoom) { - if (this.zoomFactor_ === 2) { - const minX = tileCoord[1] * 2; - const minY = tileCoord[2] * 2; - return createOrUpdateTileRange(minX, minX + 1, minY, minY + 1, opt_tileRange); - } - const tileCoordExtent = this.getTileCoordExtent(tileCoord, opt_extent); - return this.getTileRangeForExtentAndZ( - tileCoordExtent, tileCoord[0] + 1, opt_tileRange); - } - return null; -}; - - -/** - * Get the extent for a tile range. - * @param {number} z Integer zoom level. - * @param {module:ol/TileRange} tileRange Tile range. - * @param {module:ol/extent~Extent=} opt_extent Temporary module:ol/extent~Extent object. - * @return {module:ol/extent~Extent} Extent. - */ -TileGrid.prototype.getTileRangeExtent = function(z, tileRange, opt_extent) { - const origin = this.getOrigin(z); - const resolution = this.getResolution(z); - const tileSize = toSize(this.getTileSize(z), this.tmpSize_); - const minX = origin[0] + tileRange.minX * tileSize[0] * resolution; - const maxX = origin[0] + (tileRange.maxX + 1) * tileSize[0] * resolution; - const minY = origin[1] + tileRange.minY * tileSize[1] * resolution; - const maxY = origin[1] + (tileRange.maxY + 1) * tileSize[1] * resolution; - return createOrUpdate(minX, minY, maxX, maxY, opt_extent); -}; - - -/** - * Get a tile range for the given extent and integer zoom level. - * @param {module:ol/extent~Extent} extent Extent. - * @param {number} z Integer zoom level. - * @param {module:ol/TileRange=} opt_tileRange Temporary tile range object. - * @return {module:ol/TileRange} Tile range. - */ -TileGrid.prototype.getTileRangeForExtentAndZ = function(extent, z, opt_tileRange) { - const tileCoord = tmpTileCoord; - this.getTileCoordForXYAndZ_(extent[0], extent[1], z, false, tileCoord); - const minX = tileCoord[1]; - const minY = tileCoord[2]; - this.getTileCoordForXYAndZ_(extent[2], extent[3], z, true, tileCoord); - return createOrUpdateTileRange(minX, tileCoord[1], minY, tileCoord[2], opt_tileRange); -}; - - -/** - * @param {module:ol/tilecoord~TileCoord} tileCoord Tile coordinate. - * @return {module:ol/coordinate~Coordinate} Tile center. - */ -TileGrid.prototype.getTileCoordCenter = function(tileCoord) { - const origin = this.getOrigin(tileCoord[0]); - const resolution = this.getResolution(tileCoord[0]); - const tileSize = toSize(this.getTileSize(tileCoord[0]), this.tmpSize_); - return [ - origin[0] + (tileCoord[1] + 0.5) * tileSize[0] * resolution, - origin[1] + (tileCoord[2] + 0.5) * tileSize[1] * resolution - ]; -}; - - -/** - * Get the extent of a tile coordinate. - * - * @param {module:ol/tilecoord~TileCoord} tileCoord Tile coordinate. - * @param {module:ol/extent~Extent=} opt_extent Temporary extent object. - * @return {module:ol/extent~Extent} Extent. - * @api - */ -TileGrid.prototype.getTileCoordExtent = function(tileCoord, opt_extent) { - const origin = this.getOrigin(tileCoord[0]); - const resolution = this.getResolution(tileCoord[0]); - const tileSize = toSize(this.getTileSize(tileCoord[0]), this.tmpSize_); - const minX = origin[0] + tileCoord[1] * tileSize[0] * resolution; - const minY = origin[1] + tileCoord[2] * tileSize[1] * resolution; - const maxX = minX + tileSize[0] * resolution; - const maxY = minY + tileSize[1] * resolution; - return createOrUpdate(minX, minY, maxX, maxY, opt_extent); -}; - - -/** - * Get the tile coordinate for the given map coordinate and resolution. This - * method considers that coordinates that intersect tile boundaries should be - * assigned the higher tile coordinate. - * - * @param {module:ol/coordinate~Coordinate} coordinate Coordinate. - * @param {number} resolution Resolution. - * @param {module:ol/tilecoord~TileCoord=} opt_tileCoord Destination module:ol/tilecoord~TileCoord object. - * @return {module:ol/tilecoord~TileCoord} Tile coordinate. - * @api - */ -TileGrid.prototype.getTileCoordForCoordAndResolution = function(coordinate, resolution, opt_tileCoord) { - return this.getTileCoordForXYAndResolution_( - coordinate[0], coordinate[1], resolution, false, opt_tileCoord); -}; - - -/** - * Note that this method should not be called for resolutions that correspond - * to an integer zoom level. Instead call the `getTileCoordForXYAndZ_` method. - * @param {number} x X. - * @param {number} y Y. - * @param {number} resolution Resolution (for a non-integer zoom level). - * @param {boolean} reverseIntersectionPolicy Instead of letting edge - * intersections go to the higher tile coordinate, let edge intersections - * go to the lower tile coordinate. - * @param {module:ol/tilecoord~TileCoord=} opt_tileCoord Temporary module:ol/tilecoord~TileCoord object. - * @return {module:ol/tilecoord~TileCoord} Tile coordinate. - * @private - */ -TileGrid.prototype.getTileCoordForXYAndResolution_ = function( - x, y, resolution, reverseIntersectionPolicy, opt_tileCoord) { - const z = this.getZForResolution(resolution); - const scale = resolution / this.getResolution(z); - const origin = this.getOrigin(z); - const tileSize = toSize(this.getTileSize(z), this.tmpSize_); - - const adjustX = reverseIntersectionPolicy ? 0.5 : 0; - const adjustY = reverseIntersectionPolicy ? 0 : 0.5; - const xFromOrigin = Math.floor((x - origin[0]) / resolution + adjustX); - const yFromOrigin = Math.floor((y - origin[1]) / resolution + adjustY); - let tileCoordX = scale * xFromOrigin / tileSize[0]; - let tileCoordY = scale * yFromOrigin / tileSize[1]; - - if (reverseIntersectionPolicy) { - tileCoordX = Math.ceil(tileCoordX) - 1; - tileCoordY = Math.ceil(tileCoordY) - 1; - } else { - tileCoordX = Math.floor(tileCoordX); - tileCoordY = Math.floor(tileCoordY); - } - - return createOrUpdateTileCoord(z, tileCoordX, tileCoordY, opt_tileCoord); -}; - - -/** - * Although there is repetition between this method and `getTileCoordForXYAndResolution_`, - * they should have separate implementations. This method is for integer zoom - * levels. The other method should only be called for resolutions corresponding - * to non-integer zoom levels. - * @param {number} x Map x coordinate. - * @param {number} y Map y coordinate. - * @param {number} z Integer zoom level. - * @param {boolean} reverseIntersectionPolicy Instead of letting edge - * intersections go to the higher tile coordinate, let edge intersections - * go to the lower tile coordinate. - * @param {module:ol/tilecoord~TileCoord=} opt_tileCoord Temporary module:ol/tilecoord~TileCoord object. - * @return {module:ol/tilecoord~TileCoord} Tile coordinate. - * @private - */ -TileGrid.prototype.getTileCoordForXYAndZ_ = function(x, y, z, reverseIntersectionPolicy, opt_tileCoord) { - const origin = this.getOrigin(z); - const resolution = this.getResolution(z); - const tileSize = toSize(this.getTileSize(z), this.tmpSize_); - - const adjustX = reverseIntersectionPolicy ? 0.5 : 0; - const adjustY = reverseIntersectionPolicy ? 0 : 0.5; - const xFromOrigin = Math.floor((x - origin[0]) / resolution + adjustX); - const yFromOrigin = Math.floor((y - origin[1]) / resolution + adjustY); - let tileCoordX = xFromOrigin / tileSize[0]; - let tileCoordY = yFromOrigin / tileSize[1]; - - if (reverseIntersectionPolicy) { - tileCoordX = Math.ceil(tileCoordX) - 1; - tileCoordY = Math.ceil(tileCoordY) - 1; - } else { - tileCoordX = Math.floor(tileCoordX); - tileCoordY = Math.floor(tileCoordY); - } - - return createOrUpdateTileCoord(z, tileCoordX, tileCoordY, opt_tileCoord); -}; - - -/** - * Get a tile coordinate given a map coordinate and zoom level. - * @param {module:ol/coordinate~Coordinate} coordinate Coordinate. - * @param {number} z Zoom level. - * @param {module:ol/tilecoord~TileCoord=} opt_tileCoord Destination module:ol/tilecoord~TileCoord object. - * @return {module:ol/tilecoord~TileCoord} Tile coordinate. - * @api - */ -TileGrid.prototype.getTileCoordForCoordAndZ = function(coordinate, z, opt_tileCoord) { - return this.getTileCoordForXYAndZ_( - coordinate[0], coordinate[1], z, false, opt_tileCoord); -}; - - -/** - * @param {module:ol/tilecoord~TileCoord} tileCoord Tile coordinate. - * @return {number} Tile resolution. - */ -TileGrid.prototype.getTileCoordResolution = function(tileCoord) { - return this.resolutions_[tileCoord[0]]; -}; - - -/** - * Get the tile size for a zoom level. The type of the return value matches the - * `tileSize` or `tileSizes` that the tile grid was configured with. To always - * get an `module:ol/size~Size`, run the result through `module:ol/size~Size.toSize()`. - * @param {number} z Z. - * @return {number|module:ol/size~Size} Tile size. - * @api - */ -TileGrid.prototype.getTileSize = function(z) { - if (this.tileSize_) { - return this.tileSize_; - } else { - return this.tileSizes_[z]; - } -}; - - -/** - * @param {number} z Zoom level. - * @return {module:ol/TileRange} Extent tile range for the specified zoom level. - */ -TileGrid.prototype.getFullTileRange = function(z) { - if (!this.fullTileRanges_) { - return null; - } else { - return this.fullTileRanges_[z]; - } -}; - - -/** - * @param {number} resolution Resolution. - * @param {number=} opt_direction If 0, the nearest resolution will be used. - * If 1, the nearest lower resolution will be used. If -1, the nearest - * higher resolution will be used. Default is 0. - * @return {number} Z. - * @api - */ -TileGrid.prototype.getZForResolution = function(resolution, opt_direction) { - const z = linearFindNearest(this.resolutions_, resolution, opt_direction || 0); - return clamp(z, this.minZoom, this.maxZoom); -}; - - -/** - * @param {!module:ol/extent~Extent} extent Extent for this tile grid. - * @private - */ -TileGrid.prototype.calculateTileRanges_ = function(extent) { - const length = this.resolutions_.length; - const fullTileRanges = new Array(length); - for (let z = this.minZoom; z < length; ++z) { - fullTileRanges[z] = this.getTileRangeForExtentAndZ(extent, z); - } - this.fullTileRanges_ = fullTileRanges; -}; export default TileGrid; diff --git a/src/ol/tilegrid/WMTS.js b/src/ol/tilegrid/WMTS.js index 5b6899184f..6fc2dd9178 100644 --- a/src/ol/tilegrid/WMTS.js +++ b/src/ol/tilegrid/WMTS.js @@ -55,46 +55,47 @@ import TileGrid from '../tilegrid/TileGrid.js'; * @struct * @api */ -const WMTSTileGrid = function(options) { - /** - * @private - * @type {!Array.} - */ - this.matrixIds_ = options.matrixIds; - // FIXME: should the matrixIds become optional? +class WMTSTileGrid { + constructor(options) { + /** + * @private + * @type {!Array.} + */ + this.matrixIds_ = options.matrixIds; + // FIXME: should the matrixIds become optional? - TileGrid.call(this, { - extent: options.extent, - origin: options.origin, - origins: options.origins, - resolutions: options.resolutions, - tileSize: options.tileSize, - tileSizes: options.tileSizes, - sizes: options.sizes - }); -}; + TileGrid.call(this, { + extent: options.extent, + origin: options.origin, + origins: options.origins, + resolutions: options.resolutions, + tileSize: options.tileSize, + tileSizes: options.tileSizes, + sizes: options.sizes + }); + } + + /** + * @param {number} z Z. + * @return {string} MatrixId.. + */ + getMatrixId(z) { + return this.matrixIds_[z]; + } + + /** + * Get the list of matrix identifiers. + * @return {Array.} MatrixIds. + * @api + */ + getMatrixIds() { + return this.matrixIds_; + } +} inherits(WMTSTileGrid, TileGrid); -/** - * @param {number} z Z. - * @return {string} MatrixId.. - */ -WMTSTileGrid.prototype.getMatrixId = function(z) { - return this.matrixIds_[z]; -}; - - -/** - * Get the list of matrix identifiers. - * @return {Array.} MatrixIds. - * @api - */ -WMTSTileGrid.prototype.getMatrixIds = function() { - return this.matrixIds_; -}; - export default WMTSTileGrid; /** diff --git a/src/ol/webgl/Buffer.js b/src/ol/webgl/Buffer.js index 58423b1831..0bbd88b7c3 100644 --- a/src/ol/webgl/Buffer.js +++ b/src/ol/webgl/Buffer.js @@ -18,36 +18,36 @@ const BufferUsage = { * @param {number=} opt_usage Usage. * @struct */ -const WebGLBuffer = function(opt_arr, opt_usage) { +class WebGLBuffer { + constructor(opt_arr, opt_usage) { - /** - * @private - * @type {Array.} - */ - this.arr_ = opt_arr !== undefined ? opt_arr : []; + /** + * @private + * @type {Array.} + */ + this.arr_ = opt_arr !== undefined ? opt_arr : []; - /** - * @private - * @type {number} - */ - this.usage_ = opt_usage !== undefined ? opt_usage : BufferUsage.STATIC_DRAW; + /** + * @private + * @type {number} + */ + this.usage_ = opt_usage !== undefined ? opt_usage : BufferUsage.STATIC_DRAW; -}; + } + /** + * @return {Array.} Array. + */ + getArray() { + return this.arr_; + } -/** - * @return {Array.} Array. - */ -WebGLBuffer.prototype.getArray = function() { - return this.arr_; -}; - - -/** - * @return {number} Usage. - */ -WebGLBuffer.prototype.getUsage = function() { - return this.usage_; -}; + /** + * @return {number} Usage. + */ + getUsage() { + return this.usage_; + } +} export default WebGLBuffer; diff --git a/src/ol/webgl/Context.js b/src/ol/webgl/Context.js index 2d17f8feda..4ec4130ab4 100644 --- a/src/ol/webgl/Context.js +++ b/src/ol/webgl/Context.js @@ -27,295 +27,285 @@ import ContextEventType from '../webgl/ContextEventType.js'; * @param {HTMLCanvasElement} canvas Canvas. * @param {WebGLRenderingContext} gl GL. */ -const WebGLContext = function(canvas, gl) { +class WebGLContext { + constructor(canvas, gl) { - /** - * @private - * @type {HTMLCanvasElement} - */ - this.canvas_ = canvas; + /** + * @private + * @type {HTMLCanvasElement} + */ + this.canvas_ = canvas; - /** - * @private - * @type {WebGLRenderingContext} - */ - this.gl_ = gl; + /** + * @private + * @type {WebGLRenderingContext} + */ + this.gl_ = gl; - /** - * @private - * @type {!Object.} - */ - this.bufferCache_ = {}; + /** + * @private + * @type {!Object.} + */ + this.bufferCache_ = {}; - /** - * @private - * @type {!Object.} - */ - this.shaderCache_ = {}; + /** + * @private + * @type {!Object.} + */ + this.shaderCache_ = {}; - /** - * @private - * @type {!Object.} - */ - this.programCache_ = {}; + /** + * @private + * @type {!Object.} + */ + this.programCache_ = {}; - /** - * @private - * @type {WebGLProgram} - */ - this.currentProgram_ = null; + /** + * @private + * @type {WebGLProgram} + */ + this.currentProgram_ = null; - /** - * @private - * @type {WebGLFramebuffer} - */ - this.hitDetectionFramebuffer_ = null; + /** + * @private + * @type {WebGLFramebuffer} + */ + this.hitDetectionFramebuffer_ = null; - /** - * @private - * @type {WebGLTexture} - */ - this.hitDetectionTexture_ = null; + /** + * @private + * @type {WebGLTexture} + */ + this.hitDetectionTexture_ = null; - /** - * @private - * @type {WebGLRenderbuffer} - */ - this.hitDetectionRenderbuffer_ = null; + /** + * @private + * @type {WebGLRenderbuffer} + */ + this.hitDetectionRenderbuffer_ = null; - /** - * @type {boolean} - */ - this.hasOESElementIndexUint = includes(WEBGL_EXTENSIONS, 'OES_element_index_uint'); + /** + * @type {boolean} + */ + this.hasOESElementIndexUint = includes(WEBGL_EXTENSIONS, 'OES_element_index_uint'); + + // use the OES_element_index_uint extension if available + if (this.hasOESElementIndexUint) { + gl.getExtension('OES_element_index_uint'); + } + + listen(this.canvas_, ContextEventType.LOST, + this.handleWebGLContextLost, this); + listen(this.canvas_, ContextEventType.RESTORED, + this.handleWebGLContextRestored, this); - // use the OES_element_index_uint extension if available - if (this.hasOESElementIndexUint) { - gl.getExtension('OES_element_index_uint'); } - listen(this.canvas_, ContextEventType.LOST, - this.handleWebGLContextLost, this); - listen(this.canvas_, ContextEventType.RESTORED, - this.handleWebGLContextRestored, this); + /** + * Just bind the buffer if it's in the cache. Otherwise create + * the WebGL buffer, bind it, populate it, and add an entry to + * the cache. + * @param {number} target Target. + * @param {module:ol/webgl/Buffer} buf Buffer. + */ + bindBuffer(target, buf) { + const gl = this.getGL(); + const arr = buf.getArray(); + const bufferKey = String(getUid(buf)); + if (bufferKey in this.bufferCache_) { + const bufferCacheEntry = this.bufferCache_[bufferKey]; + gl.bindBuffer(target, bufferCacheEntry.buffer); + } else { + const buffer = gl.createBuffer(); + gl.bindBuffer(target, buffer); + let /** @type {ArrayBufferView} */ arrayBuffer; + if (target == ARRAY_BUFFER) { + arrayBuffer = new Float32Array(arr); + } else if (target == ELEMENT_ARRAY_BUFFER) { + arrayBuffer = this.hasOESElementIndexUint ? + new Uint32Array(arr) : new Uint16Array(arr); + } + gl.bufferData(target, arrayBuffer, buf.getUsage()); + this.bufferCache_[bufferKey] = { + buf: buf, + buffer: buffer + }; + } + } -}; + /** + * @param {module:ol/webgl/Buffer} buf Buffer. + */ + deleteBuffer(buf) { + const gl = this.getGL(); + const bufferKey = String(getUid(buf)); + const bufferCacheEntry = this.bufferCache_[bufferKey]; + if (!gl.isContextLost()) { + gl.deleteBuffer(bufferCacheEntry.buffer); + } + delete this.bufferCache_[bufferKey]; + } + + /** + * @inheritDoc + */ + disposeInternal() { + unlistenAll(this.canvas_); + const gl = this.getGL(); + if (!gl.isContextLost()) { + for (const key in this.bufferCache_) { + gl.deleteBuffer(this.bufferCache_[key].buffer); + } + for (const key in this.programCache_) { + gl.deleteProgram(this.programCache_[key]); + } + for (const key in this.shaderCache_) { + gl.deleteShader(this.shaderCache_[key]); + } + // delete objects for hit-detection + gl.deleteFramebuffer(this.hitDetectionFramebuffer_); + gl.deleteRenderbuffer(this.hitDetectionRenderbuffer_); + gl.deleteTexture(this.hitDetectionTexture_); + } + } + + /** + * @return {HTMLCanvasElement} Canvas. + */ + getCanvas() { + return this.canvas_; + } + + /** + * Get the WebGL rendering context + * @return {WebGLRenderingContext} The rendering context. + * @api + */ + getGL() { + return this.gl_; + } + + /** + * Get the frame buffer for hit detection. + * @return {WebGLFramebuffer} The hit detection frame buffer. + */ + getHitDetectionFramebuffer() { + if (!this.hitDetectionFramebuffer_) { + this.initHitDetectionFramebuffer_(); + } + return this.hitDetectionFramebuffer_; + } + + /** + * Get shader from the cache if it's in the cache. Otherwise, create + * the WebGL shader, compile it, and add entry to cache. + * @param {module:ol/webgl/Shader} shaderObject Shader object. + * @return {WebGLShader} Shader. + */ + getShader(shaderObject) { + const shaderKey = String(getUid(shaderObject)); + if (shaderKey in this.shaderCache_) { + return this.shaderCache_[shaderKey]; + } else { + const gl = this.getGL(); + const shader = gl.createShader(shaderObject.getType()); + gl.shaderSource(shader, shaderObject.getSource()); + gl.compileShader(shader); + this.shaderCache_[shaderKey] = shader; + return shader; + } + } + + /** + * Get the program from the cache if it's in the cache. Otherwise create + * the WebGL program, attach the shaders to it, and add an entry to the + * cache. + * @param {module:ol/webgl/Fragment} fragmentShaderObject Fragment shader. + * @param {module:ol/webgl/Vertex} vertexShaderObject Vertex shader. + * @return {WebGLProgram} Program. + */ + getProgram(fragmentShaderObject, vertexShaderObject) { + const programKey = getUid(fragmentShaderObject) + '/' + getUid(vertexShaderObject); + if (programKey in this.programCache_) { + return this.programCache_[programKey]; + } else { + const gl = this.getGL(); + const program = gl.createProgram(); + gl.attachShader(program, this.getShader(fragmentShaderObject)); + gl.attachShader(program, this.getShader(vertexShaderObject)); + gl.linkProgram(program); + this.programCache_[programKey] = program; + return program; + } + } + + /** + * FIXME empty description for jsdoc + */ + handleWebGLContextLost() { + clear(this.bufferCache_); + clear(this.shaderCache_); + clear(this.programCache_); + this.currentProgram_ = null; + this.hitDetectionFramebuffer_ = null; + this.hitDetectionTexture_ = null; + this.hitDetectionRenderbuffer_ = null; + } + + /** + * FIXME empty description for jsdoc + */ + handleWebGLContextRestored() { + } + + /** + * Creates a 1x1 pixel framebuffer for the hit-detection. + * @private + */ + initHitDetectionFramebuffer_() { + const gl = this.gl_; + const framebuffer = gl.createFramebuffer(); + gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer); + + const texture = createEmptyTexture(gl, 1, 1); + const renderbuffer = gl.createRenderbuffer(); + gl.bindRenderbuffer(gl.RENDERBUFFER, renderbuffer); + gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_COMPONENT16, 1, 1); + gl.framebufferTexture2D( + gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, texture, 0); + gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, + gl.RENDERBUFFER, renderbuffer); + + gl.bindTexture(gl.TEXTURE_2D, null); + gl.bindRenderbuffer(gl.RENDERBUFFER, null); + gl.bindFramebuffer(gl.FRAMEBUFFER, null); + + this.hitDetectionFramebuffer_ = framebuffer; + this.hitDetectionTexture_ = texture; + this.hitDetectionRenderbuffer_ = renderbuffer; + } + + /** + * Use a program. If the program is already in use, this will return `false`. + * @param {WebGLProgram} program Program. + * @return {boolean} Changed. + * @api + */ + useProgram(program) { + if (program == this.currentProgram_) { + return false; + } else { + const gl = this.getGL(); + gl.useProgram(program); + this.currentProgram_ = program; + return true; + } + } +} inherits(WebGLContext, Disposable); -/** - * Just bind the buffer if it's in the cache. Otherwise create - * the WebGL buffer, bind it, populate it, and add an entry to - * the cache. - * @param {number} target Target. - * @param {module:ol/webgl/Buffer} buf Buffer. - */ -WebGLContext.prototype.bindBuffer = function(target, buf) { - const gl = this.getGL(); - const arr = buf.getArray(); - const bufferKey = String(getUid(buf)); - if (bufferKey in this.bufferCache_) { - const bufferCacheEntry = this.bufferCache_[bufferKey]; - gl.bindBuffer(target, bufferCacheEntry.buffer); - } else { - const buffer = gl.createBuffer(); - gl.bindBuffer(target, buffer); - let /** @type {ArrayBufferView} */ arrayBuffer; - if (target == ARRAY_BUFFER) { - arrayBuffer = new Float32Array(arr); - } else if (target == ELEMENT_ARRAY_BUFFER) { - arrayBuffer = this.hasOESElementIndexUint ? - new Uint32Array(arr) : new Uint16Array(arr); - } - gl.bufferData(target, arrayBuffer, buf.getUsage()); - this.bufferCache_[bufferKey] = { - buf: buf, - buffer: buffer - }; - } -}; - - -/** - * @param {module:ol/webgl/Buffer} buf Buffer. - */ -WebGLContext.prototype.deleteBuffer = function(buf) { - const gl = this.getGL(); - const bufferKey = String(getUid(buf)); - const bufferCacheEntry = this.bufferCache_[bufferKey]; - if (!gl.isContextLost()) { - gl.deleteBuffer(bufferCacheEntry.buffer); - } - delete this.bufferCache_[bufferKey]; -}; - - -/** - * @inheritDoc - */ -WebGLContext.prototype.disposeInternal = function() { - unlistenAll(this.canvas_); - const gl = this.getGL(); - if (!gl.isContextLost()) { - for (const key in this.bufferCache_) { - gl.deleteBuffer(this.bufferCache_[key].buffer); - } - for (const key in this.programCache_) { - gl.deleteProgram(this.programCache_[key]); - } - for (const key in this.shaderCache_) { - gl.deleteShader(this.shaderCache_[key]); - } - // delete objects for hit-detection - gl.deleteFramebuffer(this.hitDetectionFramebuffer_); - gl.deleteRenderbuffer(this.hitDetectionRenderbuffer_); - gl.deleteTexture(this.hitDetectionTexture_); - } -}; - - -/** - * @return {HTMLCanvasElement} Canvas. - */ -WebGLContext.prototype.getCanvas = function() { - return this.canvas_; -}; - - -/** - * Get the WebGL rendering context - * @return {WebGLRenderingContext} The rendering context. - * @api - */ -WebGLContext.prototype.getGL = function() { - return this.gl_; -}; - - -/** - * Get the frame buffer for hit detection. - * @return {WebGLFramebuffer} The hit detection frame buffer. - */ -WebGLContext.prototype.getHitDetectionFramebuffer = function() { - if (!this.hitDetectionFramebuffer_) { - this.initHitDetectionFramebuffer_(); - } - return this.hitDetectionFramebuffer_; -}; - - -/** - * Get shader from the cache if it's in the cache. Otherwise, create - * the WebGL shader, compile it, and add entry to cache. - * @param {module:ol/webgl/Shader} shaderObject Shader object. - * @return {WebGLShader} Shader. - */ -WebGLContext.prototype.getShader = function(shaderObject) { - const shaderKey = String(getUid(shaderObject)); - if (shaderKey in this.shaderCache_) { - return this.shaderCache_[shaderKey]; - } else { - const gl = this.getGL(); - const shader = gl.createShader(shaderObject.getType()); - gl.shaderSource(shader, shaderObject.getSource()); - gl.compileShader(shader); - this.shaderCache_[shaderKey] = shader; - return shader; - } -}; - - -/** - * Get the program from the cache if it's in the cache. Otherwise create - * the WebGL program, attach the shaders to it, and add an entry to the - * cache. - * @param {module:ol/webgl/Fragment} fragmentShaderObject Fragment shader. - * @param {module:ol/webgl/Vertex} vertexShaderObject Vertex shader. - * @return {WebGLProgram} Program. - */ -WebGLContext.prototype.getProgram = function(fragmentShaderObject, vertexShaderObject) { - const programKey = getUid(fragmentShaderObject) + '/' + getUid(vertexShaderObject); - if (programKey in this.programCache_) { - return this.programCache_[programKey]; - } else { - const gl = this.getGL(); - const program = gl.createProgram(); - gl.attachShader(program, this.getShader(fragmentShaderObject)); - gl.attachShader(program, this.getShader(vertexShaderObject)); - gl.linkProgram(program); - this.programCache_[programKey] = program; - return program; - } -}; - - -/** - * FIXME empty description for jsdoc - */ -WebGLContext.prototype.handleWebGLContextLost = function() { - clear(this.bufferCache_); - clear(this.shaderCache_); - clear(this.programCache_); - this.currentProgram_ = null; - this.hitDetectionFramebuffer_ = null; - this.hitDetectionTexture_ = null; - this.hitDetectionRenderbuffer_ = null; -}; - - -/** - * FIXME empty description for jsdoc - */ -WebGLContext.prototype.handleWebGLContextRestored = function() { -}; - - -/** - * Creates a 1x1 pixel framebuffer for the hit-detection. - * @private - */ -WebGLContext.prototype.initHitDetectionFramebuffer_ = function() { - const gl = this.gl_; - const framebuffer = gl.createFramebuffer(); - gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer); - - const texture = createEmptyTexture(gl, 1, 1); - const renderbuffer = gl.createRenderbuffer(); - gl.bindRenderbuffer(gl.RENDERBUFFER, renderbuffer); - gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_COMPONENT16, 1, 1); - gl.framebufferTexture2D( - gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, texture, 0); - gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, - gl.RENDERBUFFER, renderbuffer); - - gl.bindTexture(gl.TEXTURE_2D, null); - gl.bindRenderbuffer(gl.RENDERBUFFER, null); - gl.bindFramebuffer(gl.FRAMEBUFFER, null); - - this.hitDetectionFramebuffer_ = framebuffer; - this.hitDetectionTexture_ = texture; - this.hitDetectionRenderbuffer_ = renderbuffer; -}; - - -/** - * Use a program. If the program is already in use, this will return `false`. - * @param {WebGLProgram} program Program. - * @return {boolean} Changed. - * @api - */ -WebGLContext.prototype.useProgram = function(program) { - if (program == this.currentProgram_) { - return false; - } else { - const gl = this.getGL(); - gl.useProgram(program); - this.currentProgram_ = program; - return true; - } -}; - - /** * @param {WebGLRenderingContext} gl WebGL rendering context. * @param {number=} opt_wrapS wrapS. diff --git a/src/ol/webgl/Fragment.js b/src/ol/webgl/Fragment.js index 460e43baee..459651b6b1 100644 --- a/src/ol/webgl/Fragment.js +++ b/src/ol/webgl/Fragment.js @@ -11,17 +11,20 @@ import WebGLShader from '../webgl/Shader.js'; * @param {string} source Source. * @struct */ -const WebGLFragment = function(source) { - WebGLShader.call(this, source); -}; +class WebGLFragment { + constructor(source) { + WebGLShader.call(this, source); + } + + /** + * @inheritDoc + */ + getType() { + return FRAGMENT_SHADER; + } +} inherits(WebGLFragment, WebGLShader); -/** - * @inheritDoc - */ -WebGLFragment.prototype.getType = function() { - return FRAGMENT_SHADER; -}; export default WebGLFragment; diff --git a/src/ol/webgl/Shader.js b/src/ol/webgl/Shader.js index 6e7f6fb491..ed7927e80b 100644 --- a/src/ol/webgl/Shader.js +++ b/src/ol/webgl/Shader.js @@ -9,30 +9,30 @@ import {FALSE} from '../functions.js'; * @param {string} source Source. * @struct */ -const WebGLShader = function(source) { +class WebGLShader { + constructor(source) { - /** - * @private - * @type {string} - */ - this.source_ = source; + /** + * @private + * @type {string} + */ + this.source_ = source; -}; + } + /** + * @abstract + * @return {number} Type. + */ + getType() {} -/** - * @abstract - * @return {number} Type. - */ -WebGLShader.prototype.getType = function() {}; - - -/** - * @return {string} Source. - */ -WebGLShader.prototype.getSource = function() { - return this.source_; -}; + /** + * @return {string} Source. + */ + getSource() { + return this.source_; + } +} /** diff --git a/src/ol/webgl/Vertex.js b/src/ol/webgl/Vertex.js index 138ac78751..3f798a7860 100644 --- a/src/ol/webgl/Vertex.js +++ b/src/ol/webgl/Vertex.js @@ -11,17 +11,20 @@ import WebGLShader from '../webgl/Shader.js'; * @param {string} source Source. * @struct */ -const WebGLVertex = function(source) { - WebGLShader.call(this, source); -}; +class WebGLVertex { + constructor(source) { + WebGLShader.call(this, source); + } + + /** + * @inheritDoc + */ + getType() { + return VERTEX_SHADER; + } +} inherits(WebGLVertex, WebGLShader); -/** - * @inheritDoc - */ -WebGLVertex.prototype.getType = function() { - return VERTEX_SHADER; -}; export default WebGLVertex; diff --git a/src/ol/webgl/shaderlocations.mustache b/src/ol/webgl/shaderlocations.mustache index 1b0b87d9b5..aec7cddff5 100644 --- a/src/ol/webgl/shaderlocations.mustache +++ b/src/ol/webgl/shaderlocations.mustache @@ -12,23 +12,28 @@ import {DEBUG as DEBUG_WEBGL} from '../../../../webgl.js'; * @param {WebGLProgram} program Program. * @struct */ -const Locations = function(gl, program) { -{{#uniforms}} +class Locations { - /** - * @type {WebGLUniformLocation} - */ - this.{{originalName}} = gl.getUniformLocation( - program, DEBUG_WEBGL ? '{{originalName}}' : '{{shortName}}'); -{{/uniforms}} -{{#attributes}} + constructor(gl, program) { + {{#uniforms}} - /** - * @type {number} - */ - this.{{originalName}} = gl.getAttribLocation( - program, DEBUG_WEBGL ? '{{originalName}}' : '{{shortName}}'); -{{/attributes}} -}; + /** + * @type {WebGLUniformLocation} + */ + this.{{originalName}} = gl.getUniformLocation( + program, DEBUG_WEBGL ? '{{originalName}}' : '{{shortName}}'); + {{/uniforms}} + {{#attributes}} + + /** + * @type {number} + */ + this.{{originalName}} = gl.getAttribLocation( + program, DEBUG_WEBGL ? '{{originalName}}' : '{{shortName}}'); + {{/attributes}} + + } + +} export default Locations; From d0ab8dce38a7f58d0cbae194847fabede963ccca Mon Sep 17 00:00:00 2001 From: Tim Schaub Date: Mon, 16 Jul 2018 17:57:57 -0600 Subject: [PATCH 003/107] Lint removal --- src/ol/Disposable.js | 14 +- src/ol/Image.js | 116 ++++---- src/ol/ImageBase.js | 70 ++--- src/ol/ImageCanvas.js | 72 ++--- src/ol/Kinetic.js | 128 ++++----- src/ol/MapBrowserEvent.js | 42 +-- src/ol/TileRange.js | 114 ++++---- src/ol/VectorTile.js | 198 ++++++------- src/ol/control/Control.js | 104 +++---- src/ol/control/ZoomToExtent.js | 70 ++--- src/ol/format/Feature.js | 90 +++--- src/ol/format/GML2.js | 24 +- src/ol/format/GML3.js | 24 +- src/ol/format/GMLBase.js | 28 +- src/ol/format/GPX.js | 130 ++++----- src/ol/format/IGC.js | 61 ++-- src/ol/format/JSONFeature.js | 104 +++---- src/ol/format/KML.js | 274 +++++++++--------- src/ol/format/OSMXML.js | 59 ++-- src/ol/format/OWS.js | 39 +-- src/ol/format/TextFeature.js | 102 +++---- src/ol/format/WFS.js | 126 ++++----- src/ol/format/WKT.js | 76 +++-- src/ol/format/WMSCapabilities.js | 66 ++--- src/ol/format/WMSGetFeatureInfo.js | 28 +- src/ol/format/WMTSCapabilities.js | 61 ++-- src/ol/format/XML.js | 34 +-- src/ol/format/filter/Filter.js | 16 +- src/ol/geom/Geometry.js | 162 +++++------ src/ol/interaction/Modify.js | 30 +- src/ol/layer/Base.js | 178 ++++++------ src/ol/layer/Heatmap.js | 5 +- src/ol/layer/Tile.js | 56 ++-- src/ol/layer/Vector.js | 158 +++++------ src/ol/layer/VectorTile.js | 70 ++--- src/ol/pointer/EventSource.js | 28 +- src/ol/pointer/MouseSource.js | 41 +-- src/ol/pointer/MsSource.js | 275 +++++++++--------- src/ol/pointer/NativeSource.js | 90 +++--- src/ol/pointer/PointerEvent.js | 238 ++++++++-------- src/ol/pointer/PointerEventHandler.js | 81 +++--- src/ol/pointer/TouchSource.js | 23 +- src/ol/render/Box.js | 142 +++++----- src/ol/render/Feature.js | 239 ++++++++-------- src/ol/render/ReplayGroup.js | 8 +- src/ol/render/VectorContext.js | 64 ++--- src/ol/render/canvas/ImageReplay.js | 258 ++++++++--------- src/ol/render/canvas/Replay.js | 25 +- src/ol/render/webgl/Replay.js | 316 ++++++++++----------- src/ol/source/BingMaps.js | 24 +- src/ol/source/ImageCanvas.js | 76 ++--- src/ol/source/ImageWMS.js | 15 +- src/ol/source/Source.js | 122 ++++---- src/ol/source/TileDebug.js | 106 +++---- src/ol/style/Circle.js | 58 ++-- src/ol/style/IconImageCache.js | 84 +++--- src/ol/style/Image.js | 148 +++++----- src/ol/style/Text.js | 382 +++++++++++++------------- src/ol/tilegrid/TileGrid.js | 14 +- src/ol/webgl/Buffer.js | 28 +- src/ol/webgl/Fragment.js | 14 +- src/ol/webgl/Shader.js | 20 +- src/ol/webgl/Vertex.js | 14 +- 63 files changed, 2945 insertions(+), 2917 deletions(-) diff --git a/src/ol/Disposable.js b/src/ol/Disposable.js index 4b377d5c5c..eec64ae19d 100644 --- a/src/ol/Disposable.js +++ b/src/ol/Disposable.js @@ -8,15 +8,15 @@ import {UNDEFINED} from './functions.js'; * @constructor */ class Disposable { - /** + /** * Clean up. */ - dispose() { - if (!this.disposed_) { - this.disposed_ = true; - this.disposeInternal(); - } - } + dispose() { + if (!this.disposed_) { + this.disposed_ = true; + this.disposeInternal(); + } + } } /** diff --git a/src/ol/Image.js b/src/ol/Image.js index 30c48ca8bc..f04cc6c76d 100644 --- a/src/ol/Image.js +++ b/src/ol/Image.js @@ -39,115 +39,115 @@ import {getHeight} from './extent.js'; * @param {module:ol/Image~LoadFunction} imageLoadFunction Image load function. */ class ImageWrapper { - constructor(extent, resolution, pixelRatio, src, crossOrigin, imageLoadFunction) { + constructor(extent, resolution, pixelRatio, src, crossOrigin, imageLoadFunction) { - ImageBase.call(this, extent, resolution, pixelRatio, ImageState.IDLE); + ImageBase.call(this, extent, resolution, pixelRatio, ImageState.IDLE); - /** + /** * @private * @type {string} */ - this.src_ = src; + this.src_ = src; - /** + /** * @private * @type {HTMLCanvasElement|HTMLImageElement|HTMLVideoElement} */ - this.image_ = new Image(); - if (crossOrigin !== null) { - this.image_.crossOrigin = crossOrigin; - } + this.image_ = new Image(); + if (crossOrigin !== null) { + this.image_.crossOrigin = crossOrigin; + } - /** + /** * @private * @type {Array.} */ - this.imageListenerKeys_ = null; + this.imageListenerKeys_ = null; - /** + /** * @protected * @type {module:ol/ImageState} */ - this.state = ImageState.IDLE; + this.state = ImageState.IDLE; - /** + /** * @private * @type {module:ol/Image~LoadFunction} */ - this.imageLoadFunction_ = imageLoadFunction; + this.imageLoadFunction_ = imageLoadFunction; - } + } - /** + /** * @inheritDoc * @api */ - getImage() { - return this.image_; - } + getImage() { + return this.image_; + } - /** + /** * Tracks loading or read errors. * * @private */ - handleImageError_() { - this.state = ImageState.ERROR; - this.unlistenImage_(); - this.changed(); - } + handleImageError_() { + this.state = ImageState.ERROR; + this.unlistenImage_(); + this.changed(); + } - /** + /** * Tracks successful image load. * * @private */ - handleImageLoad_() { - if (this.resolution === undefined) { - this.resolution = getHeight(this.extent) / this.image_.height; - } - this.state = ImageState.LOADED; - this.unlistenImage_(); - this.changed(); - } + handleImageLoad_() { + if (this.resolution === undefined) { + this.resolution = getHeight(this.extent) / this.image_.height; + } + this.state = ImageState.LOADED; + this.unlistenImage_(); + this.changed(); + } - /** + /** * Load the image or retry if loading previously failed. * Loading is taken care of by the tile queue, and calling this method is * only needed for preloading or for reloading in case of an error. * @override * @api */ - load() { - if (this.state == ImageState.IDLE || this.state == ImageState.ERROR) { - this.state = ImageState.LOADING; - this.changed(); - this.imageListenerKeys_ = [ - listenOnce(this.image_, EventType.ERROR, - this.handleImageError_, this), - listenOnce(this.image_, EventType.LOAD, - this.handleImageLoad_, this) - ]; - this.imageLoadFunction_(this, this.src_); - } - } + load() { + if (this.state == ImageState.IDLE || this.state == ImageState.ERROR) { + this.state = ImageState.LOADING; + this.changed(); + this.imageListenerKeys_ = [ + listenOnce(this.image_, EventType.ERROR, + this.handleImageError_, this), + listenOnce(this.image_, EventType.LOAD, + this.handleImageLoad_, this) + ]; + this.imageLoadFunction_(this, this.src_); + } + } - /** + /** * @param {HTMLCanvasElement|HTMLImageElement|HTMLVideoElement} image Image. */ - setImage(image) { - this.image_ = image; - } + setImage(image) { + this.image_ = image; + } - /** + /** * Discards event handlers which listen for load completion or errors. * * @private */ - unlistenImage_() { - this.imageListenerKeys_.forEach(unlistenByKey); - this.imageListenerKeys_ = null; - } + unlistenImage_() { + this.imageListenerKeys_.forEach(unlistenByKey); + this.imageListenerKeys_ = null; + } } inherits(ImageWrapper, ImageBase); diff --git a/src/ol/ImageBase.js b/src/ol/ImageBase.js index bcc4a7adcc..a0e4ec866a 100644 --- a/src/ol/ImageBase.js +++ b/src/ol/ImageBase.js @@ -15,82 +15,82 @@ import EventType from './events/EventType.js'; * @param {module:ol/ImageState} state State. */ class ImageBase { - constructor(extent, resolution, pixelRatio, state) { + constructor(extent, resolution, pixelRatio, state) { - EventTarget.call(this); + EventTarget.call(this); - /** + /** * @protected * @type {module:ol/extent~Extent} */ - this.extent = extent; + this.extent = extent; - /** + /** * @private * @type {number} */ - this.pixelRatio_ = pixelRatio; + this.pixelRatio_ = pixelRatio; - /** + /** * @protected * @type {number|undefined} */ - this.resolution = resolution; + this.resolution = resolution; - /** + /** * @protected * @type {module:ol/ImageState} */ - this.state = state; + this.state = state; - } + } - /** + /** * @protected */ - changed() { - this.dispatchEvent(EventType.CHANGE); - } + changed() { + this.dispatchEvent(EventType.CHANGE); + } - /** + /** * @return {module:ol/extent~Extent} Extent. */ - getExtent() { - return this.extent; - } + getExtent() { + return this.extent; + } - /** + /** * @abstract * @return {HTMLCanvasElement|HTMLImageElement|HTMLVideoElement} Image. */ - getImage() {} + getImage() {} - /** + /** * @return {number} PixelRatio. */ - getPixelRatio() { - return this.pixelRatio_; - } + getPixelRatio() { + return this.pixelRatio_; + } - /** + /** * @return {number} Resolution. */ - getResolution() { - return /** @type {number} */ (this.resolution); - } + getResolution() { + return /** @type {number} */ (this.resolution); + } - /** + /** * @return {module:ol/ImageState} State. */ - getState() { - return this.state; - } + getState() { + return this.state; + } - /** + /** * Load not yet loaded URI. * @abstract */ - load() {} + load() {} } inherits(ImageBase, EventTarget); diff --git a/src/ol/ImageCanvas.js b/src/ol/ImageCanvas.js index 989503d928..ea3f2d362e 100644 --- a/src/ol/ImageCanvas.js +++ b/src/ol/ImageCanvas.js @@ -27,73 +27,73 @@ import ImageState from './ImageState.js'; * support asynchronous canvas drawing. */ class ImageCanvas { - constructor(extent, resolution, pixelRatio, canvas, opt_loader) { + constructor(extent, resolution, pixelRatio, canvas, opt_loader) { - /** + /** * Optional canvas loader function. * @type {?module:ol/ImageCanvas~Loader} * @private */ - this.loader_ = opt_loader !== undefined ? opt_loader : null; + this.loader_ = opt_loader !== undefined ? opt_loader : null; - const state = opt_loader !== undefined ? ImageState.IDLE : ImageState.LOADED; + const state = opt_loader !== undefined ? ImageState.IDLE : ImageState.LOADED; - ImageBase.call(this, extent, resolution, pixelRatio, state); + ImageBase.call(this, extent, resolution, pixelRatio, state); - /** + /** * @private * @type {HTMLCanvasElement} */ - this.canvas_ = canvas; + this.canvas_ = canvas; - /** + /** * @private * @type {Error} */ - this.error_ = null; + this.error_ = null; - } + } - /** + /** * Get any error associated with asynchronous rendering. * @return {Error} Any error that occurred during rendering. */ - getError() { - return this.error_; - } + getError() { + return this.error_; + } - /** + /** * Handle async drawing complete. * @param {Error} err Any error during drawing. * @private */ - handleLoad_(err) { - if (err) { - this.error_ = err; - this.state = ImageState.ERROR; - } else { - this.state = ImageState.LOADED; - } - this.changed(); - } + handleLoad_(err) { + if (err) { + this.error_ = err; + this.state = ImageState.ERROR; + } else { + this.state = ImageState.LOADED; + } + this.changed(); + } - /** + /** * @inheritDoc */ - load() { - if (this.state == ImageState.IDLE) { - this.state = ImageState.LOADING; - this.changed(); - this.loader_(this.handleLoad_.bind(this)); - } - } + load() { + if (this.state == ImageState.IDLE) { + this.state = ImageState.LOADING; + this.changed(); + this.loader_(this.handleLoad_.bind(this)); + } + } - /** + /** * @return {HTMLCanvasElement} Canvas element. */ - getImage() { - return this.canvas_; - } + getImage() { + return this.canvas_; + } } inherits(ImageCanvas, ImageBase); diff --git a/src/ol/Kinetic.js b/src/ol/Kinetic.js index 4b6a5e470c..73090f8b9d 100644 --- a/src/ol/Kinetic.js +++ b/src/ol/Kinetic.js @@ -15,113 +15,113 @@ * @api */ class Kinetic { - constructor(decay, minVelocity, delay) { + constructor(decay, minVelocity, delay) { - /** + /** * @private * @type {number} */ - this.decay_ = decay; + this.decay_ = decay; - /** + /** * @private * @type {number} */ - this.minVelocity_ = minVelocity; + this.minVelocity_ = minVelocity; - /** + /** * @private * @type {number} */ - this.delay_ = delay; + this.delay_ = delay; - /** + /** * @private * @type {Array.} */ - this.points_ = []; + this.points_ = []; - /** + /** * @private * @type {number} */ - this.angle_ = 0; + this.angle_ = 0; - /** + /** * @private * @type {number} */ - this.initialVelocity_ = 0; - } + this.initialVelocity_ = 0; + } - /** + /** * FIXME empty description for jsdoc */ - begin() { - this.points_.length = 0; - this.angle_ = 0; - this.initialVelocity_ = 0; - } + begin() { + this.points_.length = 0; + this.angle_ = 0; + this.initialVelocity_ = 0; + } - /** + /** * @param {number} x X. * @param {number} y Y. */ - update(x, y) { - this.points_.push(x, y, Date.now()); - } + update(x, y) { + this.points_.push(x, y, Date.now()); + } - /** + /** * @return {boolean} Whether we should do kinetic animation. */ - end() { - if (this.points_.length < 6) { - // at least 2 points are required (i.e. there must be at least 6 elements - // in the array) - return false; - } - const delay = Date.now() - this.delay_; - const lastIndex = this.points_.length - 3; - if (this.points_[lastIndex + 2] < delay) { - // the last tracked point is too old, which means that the user stopped - // panning before releasing the map - return false; - } + end() { + if (this.points_.length < 6) { + // at least 2 points are required (i.e. there must be at least 6 elements + // in the array) + return false; + } + const delay = Date.now() - this.delay_; + const lastIndex = this.points_.length - 3; + if (this.points_[lastIndex + 2] < delay) { + // the last tracked point is too old, which means that the user stopped + // panning before releasing the map + return false; + } - // get the first point which still falls into the delay time - let firstIndex = lastIndex - 3; - while (firstIndex > 0 && this.points_[firstIndex + 2] > delay) { - firstIndex -= 3; - } + // get the first point which still falls into the delay time + let firstIndex = lastIndex - 3; + while (firstIndex > 0 && this.points_[firstIndex + 2] > delay) { + firstIndex -= 3; + } - const duration = this.points_[lastIndex + 2] - this.points_[firstIndex + 2]; - // we don't want a duration of 0 (divide by zero) - // we also make sure the user panned for a duration of at least one frame - // (1/60s) to compute sane displacement values - if (duration < 1000 / 60) { - return false; - } + const duration = this.points_[lastIndex + 2] - this.points_[firstIndex + 2]; + // we don't want a duration of 0 (divide by zero) + // we also make sure the user panned for a duration of at least one frame + // (1/60s) to compute sane displacement values + if (duration < 1000 / 60) { + return false; + } - const dx = this.points_[lastIndex] - this.points_[firstIndex]; - const dy = this.points_[lastIndex + 1] - this.points_[firstIndex + 1]; - this.angle_ = Math.atan2(dy, dx); - this.initialVelocity_ = Math.sqrt(dx * dx + dy * dy) / duration; - return this.initialVelocity_ > this.minVelocity_; - } + const dx = this.points_[lastIndex] - this.points_[firstIndex]; + const dy = this.points_[lastIndex + 1] - this.points_[firstIndex + 1]; + this.angle_ = Math.atan2(dy, dx); + this.initialVelocity_ = Math.sqrt(dx * dx + dy * dy) / duration; + return this.initialVelocity_ > this.minVelocity_; + } - /** + /** * @return {number} Total distance travelled (pixels). */ - getDistance() { - return (this.minVelocity_ - this.initialVelocity_) / this.decay_; - } + getDistance() { + return (this.minVelocity_ - this.initialVelocity_) / this.decay_; + } - /** + /** * @return {number} Angle of the kinetic panning animation (radians). */ - getAngle() { - return this.angle_; - } + getAngle() { + return this.angle_; + } } export default Kinetic; diff --git a/src/ol/MapBrowserEvent.js b/src/ol/MapBrowserEvent.js index 3043c4e7d5..ab11a55549 100644 --- a/src/ol/MapBrowserEvent.js +++ b/src/ol/MapBrowserEvent.js @@ -18,64 +18,64 @@ import MapEvent from './MapEvent.js'; * @param {?module:ol/PluggableMap~FrameState=} opt_frameState Frame state. */ class MapBrowserEvent { - constructor(type, map, browserEvent, opt_dragging, opt_frameState) { + constructor(type, map, browserEvent, opt_dragging, opt_frameState) { - MapEvent.call(this, type, map, opt_frameState); + MapEvent.call(this, type, map, opt_frameState); - /** + /** * The original browser event. * @const * @type {Event} * @api */ - this.originalEvent = browserEvent; + this.originalEvent = browserEvent; - /** + /** * The map pixel relative to the viewport corresponding to the original browser event. * @type {module:ol~Pixel} * @api */ - this.pixel = map.getEventPixel(browserEvent); + this.pixel = map.getEventPixel(browserEvent); - /** + /** * The coordinate in view projection corresponding to the original browser event. * @type {module:ol/coordinate~Coordinate} * @api */ - this.coordinate = map.getCoordinateFromPixel(this.pixel); + this.coordinate = map.getCoordinateFromPixel(this.pixel); - /** + /** * Indicates if the map is currently being dragged. Only set for * `POINTERDRAG` and `POINTERMOVE` events. Default is `false`. * * @type {boolean} * @api */ - this.dragging = opt_dragging !== undefined ? opt_dragging : false; + this.dragging = opt_dragging !== undefined ? opt_dragging : false; - } + } - /** + /** * Prevents the default browser action. * @see https://developer.mozilla.org/en-US/docs/Web/API/event.preventDefault * @override * @api */ - preventDefault() { - MapEvent.prototype.preventDefault.call(this); - this.originalEvent.preventDefault(); - } + preventDefault() { + MapEvent.prototype.preventDefault.call(this); + this.originalEvent.preventDefault(); + } - /** + /** * Prevents further propagation of the current event. * @see https://developer.mozilla.org/en-US/docs/Web/API/event.stopPropagation * @override * @api */ - stopPropagation() { - MapEvent.prototype.stopPropagation.call(this); - this.originalEvent.stopPropagation(); - } + stopPropagation() { + MapEvent.prototype.stopPropagation.call(this); + this.originalEvent.stopPropagation(); + } } inherits(MapBrowserEvent, MapEvent); diff --git a/src/ol/TileRange.js b/src/ol/TileRange.js index 8411a25e61..d3be21b15d 100644 --- a/src/ol/TileRange.js +++ b/src/ol/TileRange.js @@ -13,114 +13,114 @@ * @struct */ class TileRange { - constructor(minX, maxX, minY, maxY) { + constructor(minX, maxX, minY, maxY) { - /** + /** * @type {number} */ - this.minX = minX; + this.minX = minX; - /** + /** * @type {number} */ - this.maxX = maxX; + this.maxX = maxX; - /** + /** * @type {number} */ - this.minY = minY; + this.minY = minY; - /** + /** * @type {number} */ - this.maxY = maxY; + this.maxY = maxY; - } + } - /** + /** * @param {module:ol/tilecoord~TileCoord} tileCoord Tile coordinate. * @return {boolean} Contains tile coordinate. */ - contains(tileCoord) { - return this.containsXY(tileCoord[1], tileCoord[2]); - } + contains(tileCoord) { + return this.containsXY(tileCoord[1], tileCoord[2]); + } - /** + /** * @param {module:ol/TileRange} tileRange Tile range. * @return {boolean} Contains. */ - containsTileRange(tileRange) { - return this.minX <= tileRange.minX && tileRange.maxX <= this.maxX && + containsTileRange(tileRange) { + return this.minX <= tileRange.minX && tileRange.maxX <= this.maxX && this.minY <= tileRange.minY && tileRange.maxY <= this.maxY; - } + } - /** + /** * @param {number} x Tile coordinate x. * @param {number} y Tile coordinate y. * @return {boolean} Contains coordinate. */ - containsXY(x, y) { - return this.minX <= x && x <= this.maxX && this.minY <= y && y <= this.maxY; - } + containsXY(x, y) { + return this.minX <= x && x <= this.maxX && this.minY <= y && y <= this.maxY; + } - /** + /** * @param {module:ol/TileRange} tileRange Tile range. * @return {boolean} Equals. */ - equals(tileRange) { - return this.minX == tileRange.minX && this.minY == tileRange.minY && + equals(tileRange) { + return this.minX == tileRange.minX && this.minY == tileRange.minY && this.maxX == tileRange.maxX && this.maxY == tileRange.maxY; - } + } - /** + /** * @param {module:ol/TileRange} tileRange Tile range. */ - extend(tileRange) { - if (tileRange.minX < this.minX) { - this.minX = tileRange.minX; - } - if (tileRange.maxX > this.maxX) { - this.maxX = tileRange.maxX; - } - if (tileRange.minY < this.minY) { - this.minY = tileRange.minY; - } - if (tileRange.maxY > this.maxY) { - this.maxY = tileRange.maxY; - } - } + extend(tileRange) { + if (tileRange.minX < this.minX) { + this.minX = tileRange.minX; + } + if (tileRange.maxX > this.maxX) { + this.maxX = tileRange.maxX; + } + if (tileRange.minY < this.minY) { + this.minY = tileRange.minY; + } + if (tileRange.maxY > this.maxY) { + this.maxY = tileRange.maxY; + } + } - /** + /** * @return {number} Height. */ - getHeight() { - return this.maxY - this.minY + 1; - } + getHeight() { + return this.maxY - this.minY + 1; + } - /** + /** * @return {module:ol/size~Size} Size. */ - getSize() { - return [this.getWidth(), this.getHeight()]; - } + getSize() { + return [this.getWidth(), this.getHeight()]; + } - /** + /** * @return {number} Width. */ - getWidth() { - return this.maxX - this.minX + 1; - } + getWidth() { + return this.maxX - this.minX + 1; + } - /** + /** * @param {module:ol/TileRange} tileRange Tile range. * @return {boolean} Intersects. */ - intersects(tileRange) { - return this.minX <= tileRange.maxX && + intersects(tileRange) { + return this.minX <= tileRange.maxX && this.maxX >= tileRange.minX && this.minY <= tileRange.maxY && this.maxY >= tileRange.minY; - } + } } diff --git a/src/ol/VectorTile.js b/src/ol/VectorTile.js index 7dee5f3245..ff2591a255 100644 --- a/src/ol/VectorTile.js +++ b/src/ol/VectorTile.js @@ -5,6 +5,13 @@ import {getUid, inherits} from './util.js'; import Tile from './Tile.js'; import TileState from './TileState.js'; +/** + * @const + * @type {module:ol/extent~Extent} + */ +const DEFAULT_EXTENT = [0, 0, 4096, 4096]; + + /** * @typedef {function(new: module:ol/VectorTile, module:ol/tilecoord~TileCoord, * module:ol/TileState, string, ?string, module:ol/Tile~LoadFunction)} TileClass @@ -22,162 +29,162 @@ import TileState from './TileState.js'; * @param {module:ol/Tile~Options=} opt_options Tile options. */ class VectorTile { - constructor(tileCoord, state, src, format, tileLoadFunction, opt_options) { + constructor(tileCoord, state, src, format, tileLoadFunction, opt_options) { - Tile.call(this, tileCoord, state, opt_options); + Tile.call(this, tileCoord, state, opt_options); - /** + /** * @type {number} */ - this.consumers = 0; + this.consumers = 0; - /** + /** * @private * @type {module:ol/extent~Extent} */ - this.extent_ = null; + this.extent_ = null; - /** + /** * @private * @type {module:ol/format/Feature} */ - this.format_ = format; + this.format_ = format; - /** + /** * @private * @type {Array.} */ - this.features_ = null; + this.features_ = null; - /** + /** * @private * @type {module:ol/featureloader~FeatureLoader} */ - this.loader_; + this.loader_; - /** + /** * Data projection * @private * @type {module:ol/proj/Projection} */ - this.projection_ = null; + this.projection_ = null; - /** + /** * @private * @type {Object.} */ - this.replayGroups_ = {}; + this.replayGroups_ = {}; - /** + /** * @private * @type {module:ol/Tile~LoadFunction} */ - this.tileLoadFunction_ = tileLoadFunction; + this.tileLoadFunction_ = tileLoadFunction; - /** + /** * @private * @type {string} */ - this.url_ = src; + this.url_ = src; - } + } - /** + /** * @inheritDoc */ - disposeInternal() { - this.features_ = null; - this.replayGroups_ = {}; - this.state = TileState.ABORT; - this.changed(); - Tile.prototype.disposeInternal.call(this); - } + disposeInternal() { + this.features_ = null; + this.replayGroups_ = {}; + this.state = TileState.ABORT; + this.changed(); + Tile.prototype.disposeInternal.call(this); + } - /** + /** * Gets the extent of the vector tile. * @return {module:ol/extent~Extent} The extent. * @api */ - getExtent() { - return this.extent_ || DEFAULT_EXTENT; - } + getExtent() { + return this.extent_ || DEFAULT_EXTENT; + } - /** + /** * Get the feature format assigned for reading this tile's features. * @return {module:ol/format/Feature} Feature format. * @api */ - getFormat() { - return this.format_; - } + getFormat() { + return this.format_; + } - /** + /** * Get the features for this tile. Geometries will be in the projection returned * by {@link module:ol/VectorTile~VectorTile#getProjection}. * @return {Array.} Features. * @api */ - getFeatures() { - return this.features_; - } + getFeatures() { + return this.features_; + } - /** + /** * @inheritDoc */ - getKey() { - return this.url_; - } + getKey() { + return this.url_; + } - /** + /** * Get the feature projection of features returned by * {@link module:ol/VectorTile~VectorTile#getFeatures}. * @return {module:ol/proj/Projection} Feature projection. * @api */ - getProjection() { - return this.projection_; - } + getProjection() { + return this.projection_; + } - /** + /** * @param {module:ol/layer/Layer} layer Layer. * @param {string} key Key. * @return {module:ol/render/ReplayGroup} Replay group. */ - getReplayGroup(layer, key) { - return this.replayGroups_[getUid(layer) + ',' + key]; - } + getReplayGroup(layer, key) { + return this.replayGroups_[getUid(layer) + ',' + key]; + } - /** + /** * @inheritDoc */ - load() { - if (this.state == TileState.IDLE) { - this.setState(TileState.LOADING); - this.tileLoadFunction_(this, this.url_); - this.loader_(null, NaN, null); - } - } + load() { + if (this.state == TileState.IDLE) { + this.setState(TileState.LOADING); + this.tileLoadFunction_(this, this.url_); + this.loader_(null, NaN, null); + } + } - /** + /** * Handler for successful tile load. * @param {Array.} features The loaded features. * @param {module:ol/proj/Projection} dataProjection Data projection. * @param {module:ol/extent~Extent} extent Extent. */ - onLoad(features, dataProjection, extent) { - this.setProjection(dataProjection); - this.setFeatures(features); - this.setExtent(extent); - } + onLoad(features, dataProjection, extent) { + this.setProjection(dataProjection); + this.setFeatures(features); + this.setExtent(extent); + } - /** + /** * Handler for tile load errors. */ - onError() { - this.setState(TileState.ERROR); - } + onError() { + this.setState(TileState.ERROR); + } - /** + /** * Function for use in an {@link module:ol/source/VectorTile~VectorTile}'s * `tileLoadFunction`. Sets the extent of the vector tile. This is only required * for tiles in projections with `tile-pixels` as units. The extent should be @@ -189,58 +196,51 @@ class VectorTile { * @param {module:ol/extent~Extent} extent The extent. * @api */ - setExtent(extent) { - this.extent_ = extent; - } + setExtent(extent) { + this.extent_ = extent; + } - /** + /** * Function for use in an {@link module:ol/source/VectorTile~VectorTile}'s `tileLoadFunction`. * Sets the features for the tile. * @param {Array.} features Features. * @api */ - setFeatures(features) { - this.features_ = features; - this.setState(TileState.LOADED); - } + setFeatures(features) { + this.features_ = features; + this.setState(TileState.LOADED); + } - /** + /** * Function for use in an {@link module:ol/source/VectorTile~VectorTile}'s `tileLoadFunction`. * Sets the projection of the features that were added with * {@link module:ol/VectorTile~VectorTile#setFeatures}. * @param {module:ol/proj/Projection} projection Feature projection. * @api */ - setProjection(projection) { - this.projection_ = projection; - } + setProjection(projection) { + this.projection_ = projection; + } - /** + /** * @param {module:ol/layer/Layer} layer Layer. * @param {string} key Key. * @param {module:ol/render/ReplayGroup} replayGroup Replay group. */ - setReplayGroup(layer, key, replayGroup) { - this.replayGroups_[getUid(layer) + ',' + key] = replayGroup; - } + setReplayGroup(layer, key, replayGroup) { + this.replayGroups_[getUid(layer) + ',' + key] = replayGroup; + } - /** + /** * Set the feature loader for reading this tile's features. * @param {module:ol/featureloader~FeatureLoader} loader Feature loader. * @api */ - setLoader(loader) { - this.loader_ = loader; - } + setLoader(loader) { + this.loader_ = loader; + } } inherits(VectorTile, Tile); -/** - * @const - * @type {module:ol/extent~Extent} - */ -const DEFAULT_EXTENT = [0, 0, 4096, 4096]; - - export default VectorTile; diff --git a/src/ol/control/Control.js b/src/ol/control/Control.js index 4ce8985d46..af040e02c6 100644 --- a/src/ol/control/Control.js +++ b/src/ol/control/Control.js @@ -50,91 +50,91 @@ import {listen, unlistenByKey} from '../events.js'; * @api */ class Control { - constructor(options) { + constructor(options) { - BaseObject.call(this); + BaseObject.call(this); - /** + /** * @protected * @type {Element} */ - this.element = options.element ? options.element : null; + this.element = options.element ? options.element : null; - /** + /** * @private * @type {Element} */ - this.target_ = null; + this.target_ = null; - /** + /** * @private * @type {module:ol/PluggableMap} */ - this.map_ = null; + this.map_ = null; - /** + /** * @protected * @type {!Array.} */ - this.listenerKeys = []; + this.listenerKeys = []; - /** + /** * @type {function(module:ol/MapEvent)} */ - this.render = options.render ? options.render : UNDEFINED; + this.render = options.render ? options.render : UNDEFINED; - if (options.target) { - this.setTarget(options.target); - } + if (options.target) { + this.setTarget(options.target); + } - } + } - /** + /** * @inheritDoc */ - disposeInternal() { - removeNode(this.element); - BaseObject.prototype.disposeInternal.call(this); - } + disposeInternal() { + removeNode(this.element); + BaseObject.prototype.disposeInternal.call(this); + } - /** + /** * Get the map associated with this control. * @return {module:ol/PluggableMap} Map. * @api */ - getMap() { - return this.map_; - } + getMap() { + return this.map_; + } - /** + /** * Remove the control from its current map and attach it to the new map. * Subclasses may set up event handlers to get notified about changes to * the map here. * @param {module:ol/PluggableMap} map Map. * @api */ - setMap(map) { - if (this.map_) { - removeNode(this.element); - } - for (let i = 0, ii = this.listenerKeys.length; i < ii; ++i) { - unlistenByKey(this.listenerKeys[i]); - } - this.listenerKeys.length = 0; - this.map_ = map; - if (this.map_) { - const target = this.target_ ? - this.target_ : map.getOverlayContainerStopEvent(); - target.appendChild(this.element); - if (this.render !== UNDEFINED) { - this.listenerKeys.push(listen(map, - MapEventType.POSTRENDER, this.render, this)); - } - map.render(); - } - } + setMap(map) { + if (this.map_) { + removeNode(this.element); + } + for (let i = 0, ii = this.listenerKeys.length; i < ii; ++i) { + unlistenByKey(this.listenerKeys[i]); + } + this.listenerKeys.length = 0; + this.map_ = map; + if (this.map_) { + const target = this.target_ ? + this.target_ : map.getOverlayContainerStopEvent(); + target.appendChild(this.element); + if (this.render !== UNDEFINED) { + this.listenerKeys.push(listen(map, + MapEventType.POSTRENDER, this.render, this)); + } + map.render(); + } + } - /** + /** * This function is used to set a target element for the control. It has no * effect if it is called after the control has been added to the map (i.e. * after `setMap` is called on the control). If no `target` is set in the @@ -143,11 +143,11 @@ class Control { * @param {Element|string} target Target. * @api */ - setTarget(target) { - this.target_ = typeof target === 'string' ? - document.getElementById(target) : - target; - } + setTarget(target) { + this.target_ = typeof target === 'string' ? + document.getElementById(target) : + target; + } } inherits(Control, BaseObject); diff --git a/src/ol/control/ZoomToExtent.js b/src/ol/control/ZoomToExtent.js index cafa6d7190..01b230ee04 100644 --- a/src/ol/control/ZoomToExtent.js +++ b/src/ol/control/ZoomToExtent.js @@ -32,57 +32,57 @@ import {CLASS_CONTROL, CLASS_UNSELECTABLE} from '../css.js'; * @api */ class ZoomToExtent { - constructor(opt_options) { - const options = opt_options ? opt_options : {}; + constructor(opt_options) { + const options = opt_options ? opt_options : {}; - /** + /** * @type {module:ol/extent~Extent} * @protected */ - this.extent = options.extent ? options.extent : null; + this.extent = options.extent ? options.extent : null; - const className = options.className !== undefined ? options.className : 'ol-zoom-extent'; + const className = options.className !== undefined ? options.className : 'ol-zoom-extent'; - const label = options.label !== undefined ? options.label : 'E'; - const tipLabel = options.tipLabel !== undefined ? options.tipLabel : 'Fit to extent'; - const button = document.createElement('button'); - button.setAttribute('type', 'button'); - button.title = tipLabel; - button.appendChild( - typeof label === 'string' ? document.createTextNode(label) : label - ); + const label = options.label !== undefined ? options.label : 'E'; + const tipLabel = options.tipLabel !== undefined ? options.tipLabel : 'Fit to extent'; + const button = document.createElement('button'); + button.setAttribute('type', 'button'); + button.title = tipLabel; + button.appendChild( + typeof label === 'string' ? document.createTextNode(label) : label + ); - listen(button, EventType.CLICK, this.handleClick_, this); + listen(button, EventType.CLICK, this.handleClick_, this); - const cssClasses = className + ' ' + CLASS_UNSELECTABLE + ' ' + CLASS_CONTROL; - const element = document.createElement('div'); - element.className = cssClasses; - element.appendChild(button); + const cssClasses = className + ' ' + CLASS_UNSELECTABLE + ' ' + CLASS_CONTROL; + const element = document.createElement('div'); + element.className = cssClasses; + element.appendChild(button); - Control.call(this, { - element: element, - target: options.target - }); - } + Control.call(this, { + element: element, + target: options.target + }); + } - /** + /** * @param {MouseEvent} event The event to handle * @private */ - handleClick_(event) { - event.preventDefault(); - this.handleZoomToExtent(); - } + handleClick_(event) { + event.preventDefault(); + this.handleZoomToExtent(); + } - /** + /** * @protected */ - handleZoomToExtent() { - const map = this.getMap(); - const view = map.getView(); - const extent = !this.extent ? view.getProjection().getExtent() : this.extent; - view.fit(extent); - } + handleZoomToExtent() { + const map = this.getMap(); + const view = map.getView(); + const extent = !this.extent ? view.getProjection().getExtent() : this.extent; + view.fit(extent); + } } inherits(ZoomToExtent, Control); diff --git a/src/ol/format/Feature.js b/src/ol/format/Feature.js index 089cbc518b..ce9c8a7e29 100644 --- a/src/ol/format/Feature.js +++ b/src/ol/format/Feature.js @@ -61,42 +61,42 @@ import {get as getProjection, equivalent as equivalentProjection, transformExten * @api */ class FeatureFormat { - constructor() { + constructor() { - /** + /** * @protected * @type {module:ol/proj/Projection} */ - this.dataProjection = null; + this.dataProjection = null; - /** + /** * @protected * @type {module:ol/proj/Projection} */ - this.defaultFeatureProjection = null; + this.defaultFeatureProjection = null; - } + } - /** + /** * Adds the data projection to the read options. * @param {Document|Node|Object|string} source Source. * @param {module:ol/format/Feature~ReadOptions=} opt_options Options. * @return {module:ol/format/Feature~ReadOptions|undefined} Options. * @protected */ - getReadOptions(source, opt_options) { - let options; - if (opt_options) { - options = { - dataProjection: opt_options.dataProjection ? - opt_options.dataProjection : this.readProjection(source), - featureProjection: opt_options.featureProjection - }; - } - return this.adaptOptions(options); - } + getReadOptions(source, opt_options) { + let options; + if (opt_options) { + options = { + dataProjection: opt_options.dataProjection ? + opt_options.dataProjection : this.readProjection(source), + featureProjection: opt_options.featureProjection + }; + } + return this.adaptOptions(options); + } - /** + /** * Sets the `dataProjection` on the options, if no `dataProjection` * is set. * @param {module:ol/format/Feature~WriteOptions|module:ol/format/Feature~ReadOptions|undefined} options @@ -105,28 +105,28 @@ class FeatureFormat { * @return {module:ol/format/Feature~WriteOptions|module:ol/format/Feature~ReadOptions|undefined} * Updated options. */ - adaptOptions(options) { - return assign({ - dataProjection: this.dataProjection, - featureProjection: this.defaultFeatureProjection - }, options); - } + adaptOptions(options) { + return assign({ + dataProjection: this.dataProjection, + featureProjection: this.defaultFeatureProjection + }, options); + } - /** + /** * Get the extent from the source of the last {@link readFeatures} call. * @return {module:ol/extent~Extent} Tile extent. */ - getLastExtent() { - return null; - } + getLastExtent() { + return null; + } - /** + /** * @abstract * @return {module:ol/format/FormatType} Format. */ - getType() {} + getType() {} - /** + /** * Read a single feature from a source. * * @abstract @@ -134,9 +134,9 @@ class FeatureFormat { * @param {module:ol/format/Feature~ReadOptions=} opt_options Read options. * @return {module:ol/Feature} Feature. */ - readFeature(source, opt_options) {} + readFeature(source, opt_options) {} - /** + /** * Read all features from a source. * * @abstract @@ -144,9 +144,9 @@ class FeatureFormat { * @param {module:ol/format/Feature~ReadOptions=} opt_options Read options. * @return {Array.} Features. */ - readFeatures(source, opt_options) {} + readFeatures(source, opt_options) {} - /** + /** * Read a single geometry from a source. * * @abstract @@ -154,18 +154,18 @@ class FeatureFormat { * @param {module:ol/format/Feature~ReadOptions=} opt_options Read options. * @return {module:ol/geom/Geometry} Geometry. */ - readGeometry(source, opt_options) {} + readGeometry(source, opt_options) {} - /** + /** * Read the projection from a source. * * @abstract * @param {Document|Node|Object|string} source Source. * @return {module:ol/proj/Projection} Projection. */ - readProjection(source) {} + readProjection(source) {} - /** + /** * Encode a feature in this format. * * @abstract @@ -173,9 +173,9 @@ class FeatureFormat { * @param {module:ol/format/Feature~WriteOptions=} opt_options Write options. * @return {string} Result. */ - writeFeature(feature, opt_options) {} + writeFeature(feature, opt_options) {} - /** + /** * Encode an array of features in this format. * * @abstract @@ -183,9 +183,9 @@ class FeatureFormat { * @param {module:ol/format/Feature~WriteOptions=} opt_options Write options. * @return {string} Result. */ - writeFeatures(features, opt_options) {} + writeFeatures(features, opt_options) {} - /** + /** * Write a single geometry in this format. * * @abstract @@ -193,7 +193,7 @@ class FeatureFormat { * @param {module:ol/format/Feature~WriteOptions=} opt_options Write options. * @return {string} Result. */ - writeGeometry(geometry, opt_options) {} + writeGeometry(geometry, opt_options) {} } export default FeatureFormat; diff --git a/src/ol/format/GML2.js b/src/ol/format/GML2.js index b16365f1e0..7fc311bd06 100644 --- a/src/ol/format/GML2.js +++ b/src/ol/format/GML2.js @@ -20,6 +20,18 @@ import {createElementNS, getAllTextContent, makeArrayPusher, makeChildAppender, const schemaLocation = GMLNS + ' http://schemas.opengis.net/gml/2.1.2/feature.xsd'; +/** + * @const + * @type {Object.} + */ +const MULTIGEOMETRY_TO_MEMBER_NODENAME = { + 'MultiLineString': 'lineStringMember', + 'MultiCurve': 'curveMember', + 'MultiPolygon': 'polygonMember', + 'MultiSurface': 'surfaceMember' +}; + + /** * @classdesc * Feature format for reading and writing data in the GML format, @@ -576,18 +588,6 @@ class GML2 { inherits(GML2, GMLBase); -/** - * @const - * @type {Object.} - */ -const MULTIGEOMETRY_TO_MEMBER_NODENAME = { - 'MultiLineString': 'lineStringMember', - 'MultiCurve': 'curveMember', - 'MultiPolygon': 'polygonMember', - 'MultiSurface': 'surfaceMember' -}; - - /** * @const * @type {Object.>} diff --git a/src/ol/format/GML3.js b/src/ol/format/GML3.js index 34de9e9d86..25dd096c18 100644 --- a/src/ol/format/GML3.js +++ b/src/ol/format/GML3.js @@ -30,6 +30,18 @@ const schemaLocation = GMLNS + '1.0.0/gmlsf.xsd'; +/** + * @const + * @type {Object.} + */ +const MULTIGEOMETRY_TO_MEMBER_NODENAME = { + 'MultiLineString': 'lineStringMember', + 'MultiCurve': 'curveMember', + 'MultiPolygon': 'polygonMember', + 'MultiSurface': 'surfaceMember' +}; + + /** * @classdesc * Feature format for reading and writing data in the GML format @@ -1088,18 +1100,6 @@ GML3.prototype.SEGMENTS_PARSERS_ = { }; -/** - * @const - * @type {Object.} - */ -const MULTIGEOMETRY_TO_MEMBER_NODENAME = { - 'MultiLineString': 'lineStringMember', - 'MultiCurve': 'curveMember', - 'MultiPolygon': 'polygonMember', - 'MultiSurface': 'surfaceMember' -}; - - /** * Encode an array of features in GML 3.1.1 Simple Features. * diff --git a/src/ol/format/GMLBase.js b/src/ol/format/GMLBase.js index 6761aac1a6..bb5c73c372 100644 --- a/src/ol/format/GMLBase.js +++ b/src/ol/format/GMLBase.js @@ -29,6 +29,20 @@ import {getAllTextContent, getAttributeNS, makeArrayPusher, makeReplacer, parseN export const GMLNS = 'http://www.opengis.net/gml'; +/** + * A regular expression that matches if a string only contains whitespace + * characters. It will e.g. match `''`, `' '`, `'\n'` etc. The non-breaking + * space (0xa0) is explicitly included as IE doesn't include it in its + * definition of `\s`. + * + * Information from `goog.string.isEmptyOrWhitespace`: https://github.com/google/closure-library/blob/e877b1e/closure/goog/string/string.js#L156-L160 + * + * @const + * @type {RegExp} + */ +const ONLY_WHITESPACE_RE = /^[\s\xa0]*$/; + + /** * @typedef {Object} Options * @property {Object.|string} [featureNS] Feature @@ -458,20 +472,6 @@ class GMLBase { inherits(GMLBase, XMLFeature); -/** - * A regular expression that matches if a string only contains whitespace - * characters. It will e.g. match `''`, `' '`, `'\n'` etc. The non-breaking - * space (0xa0) is explicitly included as IE doesn't include it in its - * definition of `\s`. - * - * Information from `goog.string.isEmptyOrWhitespace`: https://github.com/google/closure-library/blob/e877b1e/closure/goog/string/string.js#L156-L160 - * - * @const - * @type {RegExp} - */ -const ONLY_WHITESPACE_RE = /^[\s\xa0]*$/; - - /** * @const * @type {Object.>} diff --git a/src/ol/format/GPX.js b/src/ol/format/GPX.js index dbcc8fd9e2..f68a4b381f 100644 --- a/src/ol/format/GPX.js +++ b/src/ol/format/GPX.js @@ -18,6 +18,71 @@ import {createElementNS, makeArrayPusher, makeArraySerializer, makeChildAppender XML_SCHEMA_INSTANCE_URI} from '../xml.js'; +/** + * @const + * @type {Array.} + */ +const NAMESPACE_URIS = [ + null, + 'http://www.topografix.com/GPX/1/0', + 'http://www.topografix.com/GPX/1/1' +]; + + +/** + * @const + * @type {string} + */ +const SCHEMA_LOCATION = 'http://www.topografix.com/GPX/1/1 ' + + 'http://www.topografix.com/GPX/1/1/gpx.xsd'; + + +/** + * @const + * @type {Object.): (module:ol/Feature|undefined)>} + */ +const FEATURE_READER = { + 'rte': readRte, + 'trk': readTrk, + 'wpt': readWpt +}; + + +/** + * @const + * @type {Object.>} + */ +const GPX_PARSERS = makeStructureNS( + NAMESPACE_URIS, { + 'rte': makeArrayPusher(readRte), + 'trk': makeArrayPusher(readTrk), + 'wpt': makeArrayPusher(readWpt) + }); + + +/** + * @const + * @type {Object.>} + */ +const LINK_PARSERS = makeStructureNS( + NAMESPACE_URIS, { + 'text': makeObjectPropertySetter(readString, 'linkText'), + 'type': makeObjectPropertySetter(readString, 'linkType') + }); + + +/** + * @const + * @type {Object.>} + */ +const GPX_SERIALIZERS = makeStructureNS( + NAMESPACE_URIS, { + 'rte': makeChildAppender(writeRte), + 'trk': makeChildAppender(writeTrk), + 'wpt': makeChildAppender(writeWpt) + }); + + /** * @typedef {Object} Options * @property {function(module:ol/Feature, Node)} [readExtensions] Callback function @@ -150,59 +215,6 @@ class GPX { inherits(GPX, XMLFeature); -/** - * @const - * @type {Array.} - */ -const NAMESPACE_URIS = [ - null, - 'http://www.topografix.com/GPX/1/0', - 'http://www.topografix.com/GPX/1/1' -]; - - -/** - * @const - * @type {string} - */ -const SCHEMA_LOCATION = 'http://www.topografix.com/GPX/1/1 ' + - 'http://www.topografix.com/GPX/1/1/gpx.xsd'; - - -/** - * @const - * @type {Object.): (module:ol/Feature|undefined)>} - */ -const FEATURE_READER = { - 'rte': readRte, - 'trk': readTrk, - 'wpt': readWpt -}; - - -/** - * @const - * @type {Object.>} - */ -const GPX_PARSERS = makeStructureNS( - NAMESPACE_URIS, { - 'rte': makeArrayPusher(readRte), - 'trk': makeArrayPusher(readTrk), - 'wpt': makeArrayPusher(readWpt) - }); - - -/** - * @const - * @type {Object.>} - */ -const LINK_PARSERS = makeStructureNS( - NAMESPACE_URIS, { - 'text': makeObjectPropertySetter(readString, 'linkText'), - 'type': makeObjectPropertySetter(readString, 'linkType') - }); - - /** * @const * @type {Object.>} @@ -466,18 +478,6 @@ function GPX_NODE_FACTORY(value, objectStack, opt_nodeName) { } -/** - * @const - * @type {Object.>} - */ -const GPX_SERIALIZERS = makeStructureNS( - NAMESPACE_URIS, { - 'rte': makeChildAppender(writeRte), - 'trk': makeChildAppender(writeTrk), - 'wpt': makeChildAppender(writeWpt) - }); - - /** * @param {Array.} flatCoordinates Flat coordinates. * @param {module:ol/format/GPX~LayoutOptions} layoutOptions Layout options. diff --git a/src/ol/format/IGC.js b/src/ol/format/IGC.js index e67a08dc9e..5a23b38b14 100644 --- a/src/ol/format/IGC.js +++ b/src/ol/format/IGC.js @@ -19,6 +19,36 @@ const IGCZ = { NONE: 'none' }; +/** + * @const + * @type {RegExp} + */ +const B_RECORD_RE = + /^B(\d{2})(\d{2})(\d{2})(\d{2})(\d{5})([NS])(\d{3})(\d{5})([EW])([AV])(\d{5})(\d{5})/; + + +/** + * @const + * @type {RegExp} + */ +const H_RECORD_RE = /^H.([A-Z]{3}).*?:(.*)/; + + +/** + * @const + * @type {RegExp} + */ +const HFDTE_RECORD_RE = /^HFDTE(\d{2})(\d{2})(\d{2})/; + + +/** + * A regular expression matching the newline characters `\r\n`, `\r` and `\n`. + * + * @const + * @type {RegExp} + */ +const NEWLINE_RE = /\r\n|\r|\n/; + /** * @typedef {Object} Options @@ -170,37 +200,6 @@ class IGC { inherits(IGC, TextFeature); -/** - * @const - * @type {RegExp} - */ -const B_RECORD_RE = - /^B(\d{2})(\d{2})(\d{2})(\d{2})(\d{5})([NS])(\d{3})(\d{5})([EW])([AV])(\d{5})(\d{5})/; - - -/** - * @const - * @type {RegExp} - */ -const H_RECORD_RE = /^H.([A-Z]{3}).*?:(.*)/; - - -/** - * @const - * @type {RegExp} - */ -const HFDTE_RECORD_RE = /^HFDTE(\d{2})(\d{2})(\d{2})/; - - -/** - * A regular expression matching the newline characters `\r\n`, `\r` and `\n`. - * - * @const - * @type {RegExp} - */ -const NEWLINE_RE = /\r\n|\r|\n/; - - /** * Read the feature from the IGC source. * diff --git a/src/ol/format/JSONFeature.js b/src/ol/format/JSONFeature.js index 5a55a3f44c..45c3039a31 100644 --- a/src/ol/format/JSONFeature.js +++ b/src/ol/format/JSONFeature.js @@ -16,127 +16,127 @@ import FormatType from '../format/FormatType.js'; * @extends {module:ol/format/Feature} */ class JSONFeature { - constructor() { - FeatureFormat.call(this); - } + constructor() { + FeatureFormat.call(this); + } - /** + /** * @inheritDoc */ - getType() { - return FormatType.JSON; - } + getType() { + return FormatType.JSON; + } - /** + /** * @inheritDoc */ - readFeature(source, opt_options) { - return this.readFeatureFromObject( - getObject(source), this.getReadOptions(source, opt_options)); - } + readFeature(source, opt_options) { + return this.readFeatureFromObject( + getObject(source), this.getReadOptions(source, opt_options)); + } - /** + /** * @inheritDoc */ - readFeatures(source, opt_options) { - return this.readFeaturesFromObject( - getObject(source), this.getReadOptions(source, opt_options)); - } + readFeatures(source, opt_options) { + return this.readFeaturesFromObject( + getObject(source), this.getReadOptions(source, opt_options)); + } - /** + /** * @abstract * @param {Object} object Object. * @param {module:ol/format/Feature~ReadOptions=} opt_options Read options. * @protected * @return {module:ol/Feature} Feature. */ - readFeatureFromObject(object, opt_options) {} + readFeatureFromObject(object, opt_options) {} - /** + /** * @abstract * @param {Object} object Object. * @param {module:ol/format/Feature~ReadOptions=} opt_options Read options. * @protected * @return {Array.} Features. */ - readFeaturesFromObject(object, opt_options) {} + readFeaturesFromObject(object, opt_options) {} - /** + /** * @inheritDoc */ - readGeometry(source, opt_options) { - return this.readGeometryFromObject( - getObject(source), this.getReadOptions(source, opt_options)); - } + readGeometry(source, opt_options) { + return this.readGeometryFromObject( + getObject(source), this.getReadOptions(source, opt_options)); + } - /** + /** * @abstract * @param {Object} object Object. * @param {module:ol/format/Feature~ReadOptions=} opt_options Read options. * @protected * @return {module:ol/geom/Geometry} Geometry. */ - readGeometryFromObject(object, opt_options) {} + readGeometryFromObject(object, opt_options) {} - /** + /** * @inheritDoc */ - readProjection(source) { - return this.readProjectionFromObject(getObject(source)); - } + readProjection(source) { + return this.readProjectionFromObject(getObject(source)); + } - /** + /** * @abstract * @param {Object} object Object. * @protected * @return {module:ol/proj/Projection} Projection. */ - readProjectionFromObject(object) {} + readProjectionFromObject(object) {} - /** + /** * @inheritDoc */ - writeFeature(feature, opt_options) { - return JSON.stringify(this.writeFeatureObject(feature, opt_options)); - } + writeFeature(feature, opt_options) { + return JSON.stringify(this.writeFeatureObject(feature, opt_options)); + } - /** + /** * @abstract * @param {module:ol/Feature} feature Feature. * @param {module:ol/format/Feature~WriteOptions=} opt_options Write options. * @return {Object} Object. */ - writeFeatureObject(feature, opt_options) {} + writeFeatureObject(feature, opt_options) {} - /** + /** * @inheritDoc */ - writeFeatures(features, opt_options) { - return JSON.stringify(this.writeFeaturesObject(features, opt_options)); - } + writeFeatures(features, opt_options) { + return JSON.stringify(this.writeFeaturesObject(features, opt_options)); + } - /** + /** * @abstract * @param {Array.} features Features. * @param {module:ol/format/Feature~WriteOptions=} opt_options Write options. * @return {Object} Object. */ - writeFeaturesObject(features, opt_options) {} + writeFeaturesObject(features, opt_options) {} - /** + /** * @inheritDoc */ - writeGeometry(geometry, opt_options) { - return JSON.stringify(this.writeGeometryObject(geometry, opt_options)); - } + writeGeometry(geometry, opt_options) { + return JSON.stringify(this.writeGeometryObject(geometry, opt_options)); + } - /** + /** * @abstract * @param {module:ol/geom/Geometry} geometry Geometry. * @param {module:ol/format/Feature~WriteOptions=} opt_options Write options. * @return {Object} Object. */ - writeGeometryObject(geometry, opt_options) {} + writeGeometryObject(geometry, opt_options) {} } inherits(JSONFeature, FeatureFormat); diff --git a/src/ol/format/KML.js b/src/ol/format/KML.js index 55385b8419..ab2ef3203b 100644 --- a/src/ol/format/KML.js +++ b/src/ol/format/KML.js @@ -48,6 +48,143 @@ import {createElementNS, getAllTextContent, isDocument, isNode, makeArrayExtende * @property {Array.} whens */ + +/** + * @const + * @type {Array.} + */ +const GX_NAMESPACE_URIS = [ + 'http://www.google.com/kml/ext/2.2' +]; + + +/** + * @const + * @type {Array.} + */ +const NAMESPACE_URIS = [ + null, + 'http://earth.google.com/kml/2.0', + 'http://earth.google.com/kml/2.1', + 'http://earth.google.com/kml/2.2', + 'http://www.opengis.net/kml/2.2' +]; + + +/** + * @const + * @type {string} + */ +const SCHEMA_LOCATION = 'http://www.opengis.net/kml/2.2 ' + + 'https://developers.google.com/kml/schema/kml22gx.xsd'; + + +/** + * @type {Object.} + */ +const ICON_ANCHOR_UNITS_MAP = { + 'fraction': IconAnchorUnits.FRACTION, + 'pixels': IconAnchorUnits.PIXELS, + 'insetPixels': IconAnchorUnits.PIXELS +}; + +/** + * @const + * @type {Object.>} + */ +const PLACEMARK_PARSERS = makeStructureNS( + NAMESPACE_URIS, { + 'ExtendedData': extendedDataParser, + 'Region': regionParser, + 'MultiGeometry': makeObjectPropertySetter( + readMultiGeometry, 'geometry'), + 'LineString': makeObjectPropertySetter( + readLineString, 'geometry'), + 'LinearRing': makeObjectPropertySetter( + readLinearRing, 'geometry'), + 'Point': makeObjectPropertySetter( + readPoint, 'geometry'), + 'Polygon': makeObjectPropertySetter( + readPolygon, 'geometry'), + 'Style': makeObjectPropertySetter(readStyle), + 'StyleMap': placemarkStyleMapParser, + 'address': makeObjectPropertySetter(readString), + 'description': makeObjectPropertySetter(readString), + 'name': makeObjectPropertySetter(readString), + 'open': makeObjectPropertySetter(readBoolean), + 'phoneNumber': makeObjectPropertySetter(readString), + 'styleUrl': makeObjectPropertySetter(readURI), + 'visibility': makeObjectPropertySetter(readBoolean) + }, makeStructureNS( + GX_NAMESPACE_URIS, { + 'MultiTrack': makeObjectPropertySetter( + readGxMultiTrack, 'geometry'), + 'Track': makeObjectPropertySetter( + readGxTrack, 'geometry') + } + )); + + +/** + * @const + * @type {Object.>} + */ +const NETWORK_LINK_PARSERS = makeStructureNS( + NAMESPACE_URIS, { + 'ExtendedData': extendedDataParser, + 'Region': regionParser, + 'Link': linkParser, + 'address': makeObjectPropertySetter(readString), + 'description': makeObjectPropertySetter(readString), + 'name': makeObjectPropertySetter(readString), + 'open': makeObjectPropertySetter(readBoolean), + 'phoneNumber': makeObjectPropertySetter(readString), + 'visibility': makeObjectPropertySetter(readBoolean) + }); + + +/** + * @const + * @type {Object.>} + */ +const LINK_PARSERS = makeStructureNS( + NAMESPACE_URIS, { + 'href': makeObjectPropertySetter(readURI) + }); + + +/** + * @const + * @type {Object.>} + */ +const REGION_PARSERS = makeStructureNS( + NAMESPACE_URIS, { + 'LatLonAltBox': latLonAltBoxParser, + 'Lod': lodParser + }); + + +/** + * @const + * @type {Object.>} + */ +const KML_SEQUENCE = makeStructureNS( + NAMESPACE_URIS, [ + 'Document', 'Placemark' + ]); + + +/** + * @const + * @type {Object.>} + */ +const KML_SERIALIZERS = makeStructureNS( + NAMESPACE_URIS, { + 'Document': makeChildAppender(writeDocument), + 'Placemark': makeChildAppender(writePlacemark) + }); + + /** * @type {module:ol/color~Color} */ @@ -713,46 +850,6 @@ class KML { inherits(KML, XMLFeature); -/** - * @const - * @type {Array.} - */ -const GX_NAMESPACE_URIS = [ - 'http://www.google.com/kml/ext/2.2' -]; - - -/** - * @const - * @type {Array.} - */ -const NAMESPACE_URIS = [ - null, - 'http://earth.google.com/kml/2.0', - 'http://earth.google.com/kml/2.1', - 'http://earth.google.com/kml/2.2', - 'http://www.opengis.net/kml/2.2' -]; - - -/** - * @const - * @type {string} - */ -const SCHEMA_LOCATION = 'http://www.opengis.net/kml/2.2 ' + - 'https://developers.google.com/kml/schema/kml22gx.xsd'; - - -/** - * @type {Object.} - */ -const ICON_ANCHOR_UNITS_MAP = { - 'fraction': IconAnchorUnits.FRACTION, - 'pixels': IconAnchorUnits.PIXELS, - 'insetPixels': IconAnchorUnits.PIXELS -}; - - /** * @param {module:ol/style/Style|undefined} foundStyle Style. * @param {string} name Name. @@ -1723,17 +1820,6 @@ function extendedDataParser(node, objectStack) { parseNode(EXTENDED_DATA_PARSERS, node, objectStack); } -/** - * @const - * @type {Object.>} - */ -const REGION_PARSERS = makeStructureNS( - NAMESPACE_URIS, { - 'LatLonAltBox': latLonAltBoxParser, - 'Lod': lodParser - }); - - /** * @param {Node} node Node. * @param {Array.<*>} objectStack Object stack. @@ -1955,34 +2041,6 @@ function outerBoundaryIsParser(node, objectStack) { } -/** - * @const - * @type {Object.>} - */ -const NETWORK_LINK_PARSERS = makeStructureNS( - NAMESPACE_URIS, { - 'ExtendedData': extendedDataParser, - 'Region': regionParser, - 'Link': linkParser, - 'address': makeObjectPropertySetter(readString), - 'description': makeObjectPropertySetter(readString), - 'name': makeObjectPropertySetter(readString), - 'open': makeObjectPropertySetter(readBoolean), - 'phoneNumber': makeObjectPropertySetter(readString), - 'visibility': makeObjectPropertySetter(readBoolean) - }); - - -/** - * @const - * @type {Object.>} - */ -const LINK_PARSERS = makeStructureNS( - NAMESPACE_URIS, { - 'href': makeObjectPropertySetter(readURI) - }); - - /** * @param {Node} node Node. * @param {Array.<*>} objectStack Object stack. @@ -2006,43 +2064,6 @@ function whenParser(node, objectStack) { } -/** - * @const - * @type {Object.>} - */ -const PLACEMARK_PARSERS = makeStructureNS( - NAMESPACE_URIS, { - 'ExtendedData': extendedDataParser, - 'Region': regionParser, - 'MultiGeometry': makeObjectPropertySetter( - readMultiGeometry, 'geometry'), - 'LineString': makeObjectPropertySetter( - readLineString, 'geometry'), - 'LinearRing': makeObjectPropertySetter( - readLinearRing, 'geometry'), - 'Point': makeObjectPropertySetter( - readPoint, 'geometry'), - 'Polygon': makeObjectPropertySetter( - readPolygon, 'geometry'), - 'Style': makeObjectPropertySetter(readStyle), - 'StyleMap': placemarkStyleMapParser, - 'address': makeObjectPropertySetter(readString), - 'description': makeObjectPropertySetter(readString), - 'name': makeObjectPropertySetter(readString), - 'open': makeObjectPropertySetter(readBoolean), - 'phoneNumber': makeObjectPropertySetter(readString), - 'styleUrl': makeObjectPropertySetter(readURI), - 'visibility': makeObjectPropertySetter(readBoolean) - }, makeStructureNS( - GX_NAMESPACE_URIS, { - 'MultiTrack': makeObjectPropertySetter( - readGxMultiTrack, 'geometry'), - 'Track': makeObjectPropertySetter( - readGxTrack, 'geometry') - } - )); - - /** * Read the first feature from a KML source. MultiGeometries are converted into * GeometryCollections if they are a mix of geometry types, and into MultiPoint/ @@ -2941,27 +2962,6 @@ function writeVec2(node, vec2) { } -/** - * @const - * @type {Object.>} - */ -const KML_SEQUENCE = makeStructureNS( - NAMESPACE_URIS, [ - 'Document', 'Placemark' - ]); - - -/** - * @const - * @type {Object.>} - */ -const KML_SERIALIZERS = makeStructureNS( - NAMESPACE_URIS, { - 'Document': makeChildAppender(writeDocument), - 'Placemark': makeChildAppender(writePlacemark) - }); - - /** * Encode an array of features in the KML format. GeometryCollections, MultiPoints, * MultiLineStrings, and MultiPolygons are output as MultiGeometries. diff --git a/src/ol/format/OSMXML.js b/src/ol/format/OSMXML.js index 57cb4d88cb..f18bbf8f83 100644 --- a/src/ol/format/OSMXML.js +++ b/src/ol/format/OSMXML.js @@ -15,6 +15,36 @@ import {isEmpty} from '../obj.js'; import {get as getProjection} from '../proj.js'; import {pushParseAndPop, makeStructureNS} from '../xml.js'; + +/** + * @const + * @type {Array.} + */ +const NAMESPACE_URIS = [null]; + + +/** + * @const + * @type {Object.>} + */ +const WAY_PARSERS = makeStructureNS( + NAMESPACE_URIS, { + 'nd': readNd, + 'tag': readTag + }); + + +/** + * @const + * @type {Object.>} + */ +const PARSERS = makeStructureNS( + NAMESPACE_URIS, { + 'node': readNode, + 'way': readWay + }); + + /** * @classdesc * Feature format for reading data in the @@ -96,35 +126,6 @@ class OSMXML { inherits(OSMXML, XMLFeature); -/** - * @const - * @type {Array.} - */ -const NAMESPACE_URIS = [null]; - - -/** - * @const - * @type {Object.>} - */ -const WAY_PARSERS = makeStructureNS( - NAMESPACE_URIS, { - 'nd': readNd, - 'tag': readTag - }); - - -/** - * @const - * @type {Object.>} - */ -const PARSERS = makeStructureNS( - NAMESPACE_URIS, { - 'node': readNode, - 'way': readWay - }); - - /** * @const * @type {Object.>} diff --git a/src/ol/format/OWS.js b/src/ol/format/OWS.js index f92b7b6559..fecdf3ac90 100644 --- a/src/ol/format/OWS.js +++ b/src/ol/format/OWS.js @@ -7,6 +7,26 @@ import XML from '../format/XML.js'; import {readString} from '../format/xsd.js'; import {makeObjectPropertyPusher, makeObjectPropertySetter, makeStructureNS, pushParseAndPop} from '../xml.js'; + +/** + * @const + * @type {Array.} + */ +const NAMESPACE_URIS = [null, 'http://www.opengis.net/ows/1.1']; + + +/** + * @const + * @type {Object.>} + */ +const PARSERS = makeStructureNS( + NAMESPACE_URIS, { + 'ServiceIdentification': makeObjectPropertySetter(readServiceIdentification), + 'ServiceProvider': makeObjectPropertySetter(readServiceProvider), + 'OperationsMetadata': makeObjectPropertySetter(readOperationsMetadata) + }); + + /** * @constructor * @extends {module:ol/format/XML} @@ -41,25 +61,6 @@ class OWS { inherits(OWS, XML); -/** - * @const - * @type {Array.} - */ -const NAMESPACE_URIS = [null, 'http://www.opengis.net/ows/1.1']; - - -/** - * @const - * @type {Object.>} - */ -const PARSERS = makeStructureNS( - NAMESPACE_URIS, { - 'ServiceIdentification': makeObjectPropertySetter(readServiceIdentification), - 'ServiceProvider': makeObjectPropertySetter(readServiceProvider), - 'OperationsMetadata': makeObjectPropertySetter(readOperationsMetadata) - }); - - /** * @const * @type {Object.>} diff --git a/src/ol/format/TextFeature.js b/src/ol/format/TextFeature.js index 801becd8b4..34b4d6bfd0 100644 --- a/src/ol/format/TextFeature.js +++ b/src/ol/format/TextFeature.js @@ -16,128 +16,128 @@ import FormatType from '../format/FormatType.js'; * @extends {module:ol/format/Feature} */ class TextFeature { - constructor() { - FeatureFormat.call(this); - } + constructor() { + FeatureFormat.call(this); + } - /** + /** * @inheritDoc */ - getType() { - return FormatType.TEXT; - } + getType() { + return FormatType.TEXT; + } - /** + /** * @inheritDoc */ - readFeature(source, opt_options) { - return this.readFeatureFromText(getText(source), this.adaptOptions(opt_options)); - } + readFeature(source, opt_options) { + return this.readFeatureFromText(getText(source), this.adaptOptions(opt_options)); + } - /** + /** * @abstract * @param {string} text Text. * @param {module:ol/format/Feature~ReadOptions=} opt_options Read options. * @protected * @return {module:ol/Feature} Feature. */ - readFeatureFromText(text, opt_options) {} + readFeatureFromText(text, opt_options) {} - /** + /** * @inheritDoc */ - readFeatures(source, opt_options) { - return this.readFeaturesFromText(getText(source), this.adaptOptions(opt_options)); - } + readFeatures(source, opt_options) { + return this.readFeaturesFromText(getText(source), this.adaptOptions(opt_options)); + } - /** + /** * @abstract * @param {string} text Text. * @param {module:ol/format/Feature~ReadOptions=} opt_options Read options. * @protected * @return {Array.} Features. */ - readFeaturesFromText(text, opt_options) {} + readFeaturesFromText(text, opt_options) {} - /** + /** * @inheritDoc */ - readGeometry(source, opt_options) { - return this.readGeometryFromText(getText(source), this.adaptOptions(opt_options)); - } + readGeometry(source, opt_options) { + return this.readGeometryFromText(getText(source), this.adaptOptions(opt_options)); + } - /** + /** * @abstract * @param {string} text Text. * @param {module:ol/format/Feature~ReadOptions=} opt_options Read options. * @protected * @return {module:ol/geom/Geometry} Geometry. */ - readGeometryFromText(text, opt_options) {} + readGeometryFromText(text, opt_options) {} - /** + /** * @inheritDoc */ - readProjection(source) { - return this.readProjectionFromText(getText(source)); - } + readProjection(source) { + return this.readProjectionFromText(getText(source)); + } - /** + /** * @param {string} text Text. * @protected * @return {module:ol/proj/Projection} Projection. */ - readProjectionFromText(text) { - return this.dataProjection; - } + readProjectionFromText(text) { + return this.dataProjection; + } - /** + /** * @inheritDoc */ - writeFeature(feature, opt_options) { - return this.writeFeatureText(feature, this.adaptOptions(opt_options)); - } + writeFeature(feature, opt_options) { + return this.writeFeatureText(feature, this.adaptOptions(opt_options)); + } - /** + /** * @abstract * @param {module:ol/Feature} feature Features. * @param {module:ol/format/Feature~WriteOptions=} opt_options Write options. * @protected * @return {string} Text. */ - writeFeatureText(feature, opt_options) {} + writeFeatureText(feature, opt_options) {} - /** + /** * @inheritDoc */ - writeFeatures(features, opt_options) { - return this.writeFeaturesText(features, this.adaptOptions(opt_options)); - } + writeFeatures(features, opt_options) { + return this.writeFeaturesText(features, this.adaptOptions(opt_options)); + } - /** + /** * @abstract * @param {Array.} features Features. * @param {module:ol/format/Feature~WriteOptions=} opt_options Write options. * @protected * @return {string} Text. */ - writeFeaturesText(features, opt_options) {} + writeFeaturesText(features, opt_options) {} - /** + /** * @inheritDoc */ - writeGeometry(geometry, opt_options) { - return this.writeGeometryText(geometry, this.adaptOptions(opt_options)); - } + writeGeometry(geometry, opt_options) { + return this.writeGeometryText(geometry, this.adaptOptions(opt_options)); + } - /** + /** * @abstract * @param {module:ol/geom/Geometry} geometry Geometry. * @param {module:ol/format/Feature~WriteOptions=} opt_options Write options. * @protected * @return {string} Text. */ - writeGeometryText(geometry, opt_options) {} + writeGeometryText(geometry, opt_options) {} } inherits(TextFeature, FeatureFormat); diff --git a/src/ol/format/WFS.js b/src/ol/format/WFS.js index 045a483317..bd86b1f7a4 100644 --- a/src/ol/format/WFS.js +++ b/src/ol/format/WFS.js @@ -17,6 +17,69 @@ import {createElementNS, isDocument, isNode, makeArrayPusher, makeChildAppender, pushParseAndPop, pushSerializeAndPop, XML_SCHEMA_INSTANCE_URI} from '../xml.js'; +/** + * @const + * @type {Object.>} + */ +const FEATURE_COLLECTION_PARSERS = { + 'http://www.opengis.net/gml': { + 'boundedBy': makeObjectPropertySetter( + GMLBase.prototype.readGeometryElement, 'bounds') + } +}; + + +/** + * @const + * @type {Object.>} + */ +const TRANSACTION_SUMMARY_PARSERS = { + 'http://www.opengis.net/wfs': { + 'totalInserted': makeObjectPropertySetter(readNonNegativeInteger), + 'totalUpdated': makeObjectPropertySetter(readNonNegativeInteger), + 'totalDeleted': makeObjectPropertySetter(readNonNegativeInteger) + } +}; + + +/** + * @const + * @type {Object.>} + */ +const TRANSACTION_RESPONSE_PARSERS = { + 'http://www.opengis.net/wfs': { + 'TransactionSummary': makeObjectPropertySetter( + readTransactionSummary, 'transactionSummary'), + 'InsertResults': makeObjectPropertySetter( + readInsertResults, 'insertIds') + } +}; + + +/** + * @type {Object.>} + */ +const QUERY_SERIALIZERS = { + 'http://www.opengis.net/wfs': { + 'PropertyName': makeChildAppender(writeStringTextNode) + } +}; + + +/** + * @type {Object.>} + */ +const TRANSACTION_SERIALIZERS = { + 'http://www.opengis.net/wfs': { + 'Insert': makeChildAppender(writeFeature), + 'Update': makeChildAppender(writeUpdate), + 'Delete': makeChildAppender(writeDelete), + 'Property': makeChildAppender(writeProperty), + 'Native': makeChildAppender(writeNative) + } +}; + + /** * @typedef {Object} Options * @property {Object.|string} [featureNS] The namespace URI used for features. @@ -487,31 +550,6 @@ inherits(WFS, XMLFeature); WFS.prototype.readFeatures; -/** - * @const - * @type {Object.>} - */ -const FEATURE_COLLECTION_PARSERS = { - 'http://www.opengis.net/gml': { - 'boundedBy': makeObjectPropertySetter( - GMLBase.prototype.readGeometryElement, 'bounds') - } -}; - - -/** - * @const - * @type {Object.>} - */ -const TRANSACTION_SUMMARY_PARSERS = { - 'http://www.opengis.net/wfs': { - 'totalInserted': makeObjectPropertySetter(readNonNegativeInteger), - 'totalUpdated': makeObjectPropertySetter(readNonNegativeInteger), - 'totalDeleted': makeObjectPropertySetter(readNonNegativeInteger) - } -}; - - /** * @param {Node} node Node. * @param {Array.<*>} objectStack Object stack. @@ -567,30 +605,6 @@ function readInsertResults(node, objectStack) { } -/** - * @const - * @type {Object.>} - */ -const TRANSACTION_RESPONSE_PARSERS = { - 'http://www.opengis.net/wfs': { - 'TransactionSummary': makeObjectPropertySetter( - readTransactionSummary, 'transactionSummary'), - 'InsertResults': makeObjectPropertySetter( - readInsertResults, 'insertIds') - } -}; - - -/** - * @type {Object.>} - */ -const QUERY_SERIALIZERS = { - 'http://www.opengis.net/wfs': { - 'PropertyName': makeChildAppender(writeStringTextNode) - } -}; - - /** * @param {Node} node Node. * @param {module:ol/Feature} feature Feature. @@ -663,20 +677,6 @@ function writeDelete(node, feature, objectStack) { } -/** - * @type {Object.>} - */ -const TRANSACTION_SERIALIZERS = { - 'http://www.opengis.net/wfs': { - 'Insert': makeChildAppender(writeFeature), - 'Update': makeChildAppender(writeUpdate), - 'Delete': makeChildAppender(writeDelete), - 'Property': makeChildAppender(writeProperty), - 'Native': makeChildAppender(writeNative) - } -}; - - /** * @param {Node} node Node. * @param {module:ol/Feature} feature Feature. diff --git a/src/ol/format/WKT.js b/src/ol/format/WKT.js index 5e149705ab..a6fc544caa 100644 --- a/src/ol/format/WKT.js +++ b/src/ol/format/WKT.js @@ -17,6 +17,19 @@ import Polygon from '../geom/Polygon.js'; import SimpleGeometry from '../geom/SimpleGeometry.js'; +/** + * @enum {function (new:module:ol/geom/Geometry, Array, module:ol/geom/GeometryLayout)} + */ +const GeometryConstructor = { + 'POINT': Point, + 'LINESTRING': LineString, + 'POLYGON': Polygon, + 'MULTIPOINT': MultiPoint, + 'MULTILINESTRING': MultiLineString, + 'MULTIPOLYGON': MultiPolygon +}; + + /** * @typedef {Object} Options * @property {boolean} [splitCollection=false] Whether to split GeometryCollections into @@ -521,12 +534,42 @@ class Parser { const geometries = this.parseGeometryCollectionText_(); return new GeometryCollection(geometries); } else { - const parser = GeometryParser[geomType]; const ctor = GeometryConstructor[geomType]; - if (!parser || !ctor) { + if (!ctor) { throw new Error('Invalid geometry type: ' + geomType); } - let coordinates = parser.call(this); + + let coordinates; + switch (geomType) { + case GeometryType.POINT: { + coordinates = this.parsePointText_(); + break; + } + case GeometryType.LINESTRING: { + coordinates = this.parseLineStringText_(); + break; + } + case GeometryType.POLYGON: { + coordinates = Parser.prototype.parsePolygonText_(); + break; + } + case GeometryType.MULTIPOINT: { + coordinates = Parser.prototype.parseMultiPointText_(); + break; + } + case GeometryType.MULTILINESTRING: { + coordinates = Parser.prototype.parseMultiLineStringText_(); + break; + } + case GeometryType.MULTIPOLYGON: { + coordinates = Parser.prototype.parseMultiPolygonText_(); + break; + } + default: { + throw new Error('Invalid geometry type: ' + geomType); + } + } + if (!coordinates) { if (ctor === GeometryConstructor[GeometryType.POINT]) { coordinates = [NaN, NaN]; @@ -541,6 +584,7 @@ class Parser { } } + /** * @classdesc * Geometry format for reading and writing data in the `WellKnownText` (WKT) @@ -855,32 +899,6 @@ WKT.prototype.readFeatures; WKT.prototype.readGeometry; -/** - * @enum {function (new:module:ol/geom/Geometry, Array, module:ol/geom/GeometryLayout)} - */ -const GeometryConstructor = { - 'POINT': Point, - 'LINESTRING': LineString, - 'POLYGON': Polygon, - 'MULTIPOINT': MultiPoint, - 'MULTILINESTRING': MultiLineString, - 'MULTIPOLYGON': MultiPolygon -}; - - -/** - * @enum {(function(): Array)} - */ -const GeometryParser = { - 'POINT': Parser.prototype.parsePointText_, - 'LINESTRING': Parser.prototype.parseLineStringText_, - 'POLYGON': Parser.prototype.parsePolygonText_, - 'MULTIPOINT': Parser.prototype.parseMultiPointText_, - 'MULTILINESTRING': Parser.prototype.parseMultiLineStringText_, - 'MULTIPOLYGON': Parser.prototype.parseMultiPolygonText_ -}; - - /** * Encode a feature as a WKT string. * diff --git a/src/ol/format/WMSCapabilities.js b/src/ol/format/WMSCapabilities.js index b87177196a..ec24bce93c 100644 --- a/src/ol/format/WMSCapabilities.js +++ b/src/ol/format/WMSCapabilities.js @@ -9,6 +9,39 @@ import {makeArrayPusher, makeObjectPropertyPusher, makeObjectPropertySetter, makeStructureNS, pushParseAndPop} from '../xml.js'; +/** + * @const + * @type {Array.} + */ +const NAMESPACE_URIS = [ + null, + 'http://www.opengis.net/wms' +]; + + +/** + * @const + * @type {Object.>} + */ +const PARSERS = makeStructureNS( + NAMESPACE_URIS, { + 'Service': makeObjectPropertySetter(readService), + 'Capability': makeObjectPropertySetter(readCapability) + }); + + +/** + * @const + * @type {Object.>} + */ +const CAPABILITY_PARSERS = makeStructureNS( + NAMESPACE_URIS, { + 'Request': makeObjectPropertySetter(readRequest), + 'Exception': makeObjectPropertySetter(readException), + 'Layer': makeObjectPropertySetter(readCapabilityLayer) + }); + + /** * @classdesc * Format for reading WMS capabilities data @@ -55,39 +88,6 @@ class WMSCapabilities { inherits(WMSCapabilities, XML); -/** - * @const - * @type {Array.} - */ -const NAMESPACE_URIS = [ - null, - 'http://www.opengis.net/wms' -]; - - -/** - * @const - * @type {Object.>} - */ -const PARSERS = makeStructureNS( - NAMESPACE_URIS, { - 'Service': makeObjectPropertySetter(readService), - 'Capability': makeObjectPropertySetter(readCapability) - }); - - -/** - * @const - * @type {Object.>} - */ -const CAPABILITY_PARSERS = makeStructureNS( - NAMESPACE_URIS, { - 'Request': makeObjectPropertySetter(readRequest), - 'Exception': makeObjectPropertySetter(readException), - 'Layer': makeObjectPropertySetter(readCapabilityLayer) - }); - - /** * @const * @type {Object.>} diff --git a/src/ol/format/WMSGetFeatureInfo.js b/src/ol/format/WMSGetFeatureInfo.js index 7f84c1f99f..125cf7d2f7 100644 --- a/src/ol/format/WMSGetFeatureInfo.js +++ b/src/ol/format/WMSGetFeatureInfo.js @@ -15,6 +15,20 @@ import {makeArrayPusher, makeStructureNS, pushParseAndPop} from '../xml.js'; */ +/** + * @const + * @type {string} + */ +const featureIdentifier = '_feature'; + + +/** + * @const + * @type {string} + */ +const layerIdentifier = '_layer'; + + /** * @classdesc * Format for reading WMSGetFeatureInfo format. It uses @@ -159,20 +173,6 @@ class WMSGetFeatureInfo { inherits(WMSGetFeatureInfo, XMLFeature); -/** - * @const - * @type {string} - */ -const featureIdentifier = '_feature'; - - -/** - * @const - * @type {string} - */ -const layerIdentifier = '_layer'; - - /** * Read all features from a WMSGetFeatureInfo response. * diff --git a/src/ol/format/WMTSCapabilities.js b/src/ol/format/WMTSCapabilities.js index fa46d1f218..4acffd40a9 100644 --- a/src/ol/format/WMTSCapabilities.js +++ b/src/ol/format/WMTSCapabilities.js @@ -10,6 +10,37 @@ import {readString, readNonNegativeInteger, readDecimal} from '../format/xsd.js' import {pushParseAndPop, makeStructureNS, makeObjectPropertySetter, makeObjectPropertyPusher, makeArrayPusher} from '../xml.js'; + +/** + * @const + * @type {Array.} + */ +const NAMESPACE_URIS = [ + null, + 'http://www.opengis.net/wmts/1.0' +]; + + +/** + * @const + * @type {Array.} + */ +const OWS_NAMESPACE_URIS = [ + null, + 'http://www.opengis.net/ows/1.1' +]; + + +/** + * @const + * @type {Object.>} + */ +const PARSERS = makeStructureNS( + NAMESPACE_URIS, { + 'Contents': makeObjectPropertySetter(readContents) + }); + + /** * @classdesc * Format for reading WMTS capabilities data. @@ -59,36 +90,6 @@ class WMTSCapabilities { inherits(WMTSCapabilities, XML); -/** - * @const - * @type {Array.} - */ -const NAMESPACE_URIS = [ - null, - 'http://www.opengis.net/wmts/1.0' -]; - - -/** - * @const - * @type {Array.} - */ -const OWS_NAMESPACE_URIS = [ - null, - 'http://www.opengis.net/ows/1.1' -]; - - -/** - * @const - * @type {Object.>} - */ -const PARSERS = makeStructureNS( - NAMESPACE_URIS, { - 'Contents': makeObjectPropertySetter(readContents) - }); - - /** * @const * @type {Object.>} diff --git a/src/ol/format/XML.js b/src/ol/format/XML.js index 1dd06cfd9f..817aaeb4f1 100644 --- a/src/ol/format/XML.js +++ b/src/ol/format/XML.js @@ -12,36 +12,36 @@ import {isDocument, isNode, parse} from '../xml.js'; * @struct */ class XML { - /** + /** * @param {Document|Node|string} source Source. * @return {Object} The parsed result. */ - read(source) { - if (isDocument(source)) { - return this.readFromDocument(/** @type {Document} */ (source)); - } else if (isNode(source)) { - return this.readFromNode(/** @type {Node} */ (source)); - } else if (typeof source === 'string') { - const doc = parse(source); - return this.readFromDocument(doc); - } else { - return null; - } - } + read(source) { + if (isDocument(source)) { + return this.readFromDocument(/** @type {Document} */ (source)); + } else if (isNode(source)) { + return this.readFromNode(/** @type {Node} */ (source)); + } else if (typeof source === 'string') { + const doc = parse(source); + return this.readFromDocument(doc); + } else { + return null; + } + } - /** + /** * @abstract * @param {Document} doc Document. * @return {Object} Object */ - readFromDocument(doc) {} + readFromDocument(doc) {} - /** + /** * @abstract * @param {Node} node Node. * @return {Object} Object */ - readFromNode(node) {} + readFromNode(node) {} } export default XML; diff --git a/src/ol/format/filter/Filter.js b/src/ol/format/filter/Filter.js index 0f6f538124..5be0df9c27 100644 --- a/src/ol/format/filter/Filter.js +++ b/src/ol/format/filter/Filter.js @@ -14,22 +14,22 @@ * @struct */ class Filter { - constructor(tagName) { + constructor(tagName) { - /** + /** * @private * @type {!string} */ - this.tagName_ = tagName; - } + this.tagName_ = tagName; + } - /** + /** * The XML tag name for a filter. * @returns {!string} Name. */ - getTagName() { - return this.tagName_; - } + getTagName() { + return this.tagName_; + } } export default Filter; diff --git a/src/ol/geom/Geometry.js b/src/ol/geom/Geometry.js index a71d3ca775..7df226d66a 100644 --- a/src/ol/geom/Geometry.js +++ b/src/ol/geom/Geometry.js @@ -11,6 +11,12 @@ import Units from '../proj/Units.js'; import {create as createTransform, compose as composeTransform} from '../transform.js'; +/** + * @type {module:ol/transform~Transform} + */ +const tmpTransform = createTransform(); + + /** * @classdesc * Abstract base class; normally only used for creating subclasses and not @@ -26,50 +32,50 @@ import {create as createTransform, compose as composeTransform} from '../transfo * @api */ class Geometry { - constructor() { + constructor() { - BaseObject.call(this); + BaseObject.call(this); - /** + /** * @private * @type {module:ol/extent~Extent} */ - this.extent_ = createEmpty(); + this.extent_ = createEmpty(); - /** + /** * @private * @type {number} */ - this.extentRevision_ = -1; + this.extentRevision_ = -1; - /** + /** * @protected * @type {Object.} */ - this.simplifiedGeometryCache = {}; + this.simplifiedGeometryCache = {}; - /** + /** * @protected * @type {number} */ - this.simplifiedGeometryMaxMinSquaredTolerance = 0; + this.simplifiedGeometryMaxMinSquaredTolerance = 0; - /** + /** * @protected * @type {number} */ - this.simplifiedGeometryRevision = 0; + this.simplifiedGeometryRevision = 0; - } + } - /** + /** * Make a complete copy of the geometry. * @abstract * @return {!module:ol/geom/Geometry} Clone. */ - clone() {} + clone() {} - /** + /** * @abstract * @param {number} x X. * @param {number} y Y. @@ -77,9 +83,9 @@ class Geometry { * @param {number} minSquaredDistance Minimum squared distance. * @return {number} Minimum squared distance. */ - closestPointXY(x, y, closestPoint, minSquaredDistance) {} + closestPointXY(x, y, closestPoint, minSquaredDistance) {} - /** + /** * Return the closest point of the geometry to the passed point as * {@link module:ol/coordinate~Coordinate coordinate}. * @param {module:ol/coordinate~Coordinate} point Point. @@ -87,46 +93,46 @@ class Geometry { * @return {module:ol/coordinate~Coordinate} Closest point. * @api */ - getClosestPoint(point, opt_closestPoint) { - const closestPoint = opt_closestPoint ? opt_closestPoint : [NaN, NaN]; - this.closestPointXY(point[0], point[1], closestPoint, Infinity); - return closestPoint; - } + getClosestPoint(point, opt_closestPoint) { + const closestPoint = opt_closestPoint ? opt_closestPoint : [NaN, NaN]; + this.closestPointXY(point[0], point[1], closestPoint, Infinity); + return closestPoint; + } - /** + /** * Returns true if this geometry includes the specified coordinate. If the * coordinate is on the boundary of the geometry, returns false. * @param {module:ol/coordinate~Coordinate} coordinate Coordinate. * @return {boolean} Contains coordinate. * @api */ - intersectsCoordinate(coordinate) { - return this.containsXY(coordinate[0], coordinate[1]); - } + intersectsCoordinate(coordinate) { + return this.containsXY(coordinate[0], coordinate[1]); + } - /** + /** * @abstract * @param {module:ol/extent~Extent} extent Extent. * @protected * @return {module:ol/extent~Extent} extent Extent. */ - computeExtent(extent) {} + computeExtent(extent) {} - /** + /** * Get the extent of the geometry. * @param {module:ol/extent~Extent=} opt_extent Extent. * @return {module:ol/extent~Extent} extent Extent. * @api */ - getExtent(opt_extent) { - if (this.extentRevision_ != this.getRevision()) { - this.extent_ = this.computeExtent(this.extent_); - this.extentRevision_ = this.getRevision(); - } - return returnOrUpdate(this.extent_, opt_extent); - } + getExtent(opt_extent) { + if (this.extentRevision_ != this.getRevision()) { + this.extent_ = this.computeExtent(this.extent_); + this.extentRevision_ = this.getRevision(); + } + return returnOrUpdate(this.extent_, opt_extent); + } - /** + /** * Rotate the geometry around a given coordinate. This modifies the geometry * coordinates in place. * @abstract @@ -134,9 +140,9 @@ class Geometry { * @param {module:ol/coordinate~Coordinate} anchor The rotation center. * @api */ - rotate(angle, anchor) {} + rotate(angle, anchor) {} - /** + /** * Scale the geometry (with an optional origin). This modifies the geometry * coordinates in place. * @abstract @@ -147,9 +153,9 @@ class Geometry { * of the geometry extent). * @api */ - scale(sx, opt_sy, opt_anchor) {} + scale(sx, opt_sy, opt_anchor) {} - /** + /** * Create a simplified version of this geometry. For linestrings, this uses * the the {@link * https://en.wikipedia.org/wiki/Ramer-Douglas-Peucker_algorithm @@ -161,11 +167,11 @@ class Geometry { * geometry. * @api */ - simplify(tolerance) { - return this.getSimplifiedGeometry(tolerance * tolerance); - } + simplify(tolerance) { + return this.getSimplifiedGeometry(tolerance * tolerance); + } - /** + /** * Create a simplified version of this geometry using the Douglas Peucker * algorithm. * @see https://en.wikipedia.org/wiki/Ramer-Douglas-Peucker_algorithm @@ -173,16 +179,16 @@ class Geometry { * @param {number} squaredTolerance Squared tolerance. * @return {module:ol/geom/Geometry} Simplified geometry. */ - getSimplifiedGeometry(squaredTolerance) {} + getSimplifiedGeometry(squaredTolerance) {} - /** + /** * Get the type of this geometry. * @abstract * @return {module:ol/geom/GeometryType} Geometry type. */ - getType() {} + getType() {} - /** + /** * Apply a transform function to each coordinate of the geometry. * The geometry is modified in place. * If you do not want the geometry modified in place, first `clone()` it and @@ -190,26 +196,26 @@ class Geometry { * @abstract * @param {module:ol/proj~TransformFunction} transformFn Transform. */ - applyTransform(transformFn) {} + applyTransform(transformFn) {} - /** + /** * Test if the geometry and the passed extent intersect. * @abstract * @param {module:ol/extent~Extent} extent Extent. * @return {boolean} `true` if the geometry and the extent intersect. */ - intersectsExtent(extent) {} + intersectsExtent(extent) {} - /** + /** * Translate the geometry. This modifies the geometry coordinates in place. If * instead you want a new geometry, first `clone()` this geometry. * @abstract * @param {number} deltaX Delta X. * @param {number} deltaY Delta Y. */ - translate(deltaX, deltaY) {} + translate(deltaX, deltaY) {} - /** + /** * Transform each coordinate of the geometry from one coordinate reference * system to another. The geometry is modified in place. * For example, a line will be transformed to a line and a circle to a circle. @@ -224,36 +230,30 @@ class Geometry { * modified in place. * @api */ - transform(source, destination) { - source = getProjection(source); - const transformFn = source.getUnits() == Units.TILE_PIXELS ? - function(inCoordinates, outCoordinates, stride) { - const pixelExtent = source.getExtent(); - const projectedExtent = source.getWorldExtent(); - const scale = getHeight(projectedExtent) / getHeight(pixelExtent); - composeTransform(tmpTransform, - projectedExtent[0], projectedExtent[3], - scale, -scale, 0, - 0, 0); - transform2D(inCoordinates, 0, inCoordinates.length, stride, - tmpTransform, outCoordinates); - return getTransform(source, destination)(inCoordinates, outCoordinates, stride); - } : - getTransform(source, destination); - this.applyTransform(transformFn); - return this; - } + transform(source, destination) { + source = getProjection(source); + const transformFn = source.getUnits() == Units.TILE_PIXELS ? + function(inCoordinates, outCoordinates, stride) { + const pixelExtent = source.getExtent(); + const projectedExtent = source.getWorldExtent(); + const scale = getHeight(projectedExtent) / getHeight(pixelExtent); + composeTransform(tmpTransform, + projectedExtent[0], projectedExtent[3], + scale, -scale, 0, + 0, 0); + transform2D(inCoordinates, 0, inCoordinates.length, stride, + tmpTransform, outCoordinates); + return getTransform(source, destination)(inCoordinates, outCoordinates, stride); + } : + getTransform(source, destination); + this.applyTransform(transformFn); + return this; + } } inherits(Geometry, BaseObject); -/** - * @type {module:ol/transform~Transform} - */ -const tmpTransform = createTransform(); - - /** * @param {number} x X. * @param {number} y Y. diff --git a/src/ol/interaction/Modify.js b/src/ol/interaction/Modify.js index 3708f37ddc..2cf7ba9f5d 100644 --- a/src/ol/interaction/Modify.js +++ b/src/ol/interaction/Modify.js @@ -24,6 +24,21 @@ import RBush from '../structs/RBush.js'; import {createEditingStyle} from '../style/Style.js'; +/** + * The segment index assigned to a circle's center when + * breaking up a circle into ModifySegmentDataType segments. + * @type {number} + */ +const CIRCLE_CENTER_INDEX = 0; + +/** + * The segment index assigned to a circle's circumference when + * breaking up a circle into ModifySegmentDataType segments. + * @type {number} + */ +const CIRCLE_CIRCUMFERENCE_INDEX = 1; + + /** * @enum {string} */ @@ -958,21 +973,6 @@ class Modify { inherits(Modify, PointerInteraction); -/** - * The segment index assigned to a circle's center when - * breaking up a circle into ModifySegmentDataType segments. - * @type {number} - */ -const CIRCLE_CENTER_INDEX = 0; - -/** - * The segment index assigned to a circle's circumference when - * breaking up a circle into ModifySegmentDataType segments. - * @type {number} - */ -const CIRCLE_CIRCUMFERENCE_INDEX = 1; - - /** * @param {module:ol/interaction/Modify~SegmentData} a The first segment data. * @param {module:ol/interaction/Modify~SegmentData} b The second segment data. diff --git a/src/ol/layer/Base.js b/src/ol/layer/Base.js index 0218288a4f..1f2bb3f7aa 100644 --- a/src/ol/layer/Base.js +++ b/src/ol/layer/Base.js @@ -38,215 +38,215 @@ import {assign} from '../obj.js'; * @api */ class BaseLayer { - constructor(options) { + constructor(options) { - BaseObject.call(this); + BaseObject.call(this); - /** + /** * @type {Object.} */ - const properties = assign({}, options); - properties[LayerProperty.OPACITY] = + const properties = assign({}, options); + properties[LayerProperty.OPACITY] = options.opacity !== undefined ? options.opacity : 1; - properties[LayerProperty.VISIBLE] = + properties[LayerProperty.VISIBLE] = options.visible !== undefined ? options.visible : true; - properties[LayerProperty.Z_INDEX] = + properties[LayerProperty.Z_INDEX] = options.zIndex !== undefined ? options.zIndex : 0; - properties[LayerProperty.MAX_RESOLUTION] = + properties[LayerProperty.MAX_RESOLUTION] = options.maxResolution !== undefined ? options.maxResolution : Infinity; - properties[LayerProperty.MIN_RESOLUTION] = + properties[LayerProperty.MIN_RESOLUTION] = options.minResolution !== undefined ? options.minResolution : 0; - this.setProperties(properties); + this.setProperties(properties); - /** + /** * @type {module:ol/layer/Layer~State} * @private */ - this.state_ = /** @type {module:ol/layer/Layer~State} */ ({ - layer: /** @type {module:ol/layer/Layer} */ (this), - managed: true - }); + this.state_ = /** @type {module:ol/layer/Layer~State} */ ({ + layer: /** @type {module:ol/layer/Layer} */ (this), + managed: true + }); - /** + /** * The layer type. * @type {module:ol/LayerType} * @protected; */ - this.type; + this.type; - } + } - /** + /** * Get the layer type (used when creating a layer renderer). * @return {module:ol/LayerType} The layer type. */ - getType() { - return this.type; - } + getType() { + return this.type; + } - /** + /** * @return {module:ol/layer/Layer~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(); - this.state_.maxResolution = this.getMaxResolution(); - this.state_.minResolution = Math.max(this.getMinResolution(), 0); + 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(); + this.state_.maxResolution = this.getMaxResolution(); + this.state_.minResolution = Math.max(this.getMinResolution(), 0); - return this.state_; - } + return this.state_; + } - /** + /** * @abstract * @param {Array.=} opt_array Array of layers (to be * modified in place). * @return {Array.} Array of layers. */ - getLayersArray(opt_array) {} + getLayersArray(opt_array) {} - /** + /** * @abstract * @param {Array.=} opt_states Optional list of layer * states (to be modified in place). * @return {Array.} List of layer states. */ - getLayerStatesArray(opt_states) {} + getLayerStatesArray(opt_states) {} - /** + /** * Return the {@link module:ol/extent~Extent extent} of the layer or `undefined` if it * will be visible regardless of extent. * @return {module:ol/extent~Extent|undefined} The layer extent. * @observable * @api */ - getExtent() { - return ( - /** @type {module:ol/extent~Extent|undefined} */ (this.get(LayerProperty.EXTENT)) - ); - } + getExtent() { + return ( + /** @type {module:ol/extent~Extent|undefined} */ (this.get(LayerProperty.EXTENT)) + ); + } - /** + /** * 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)); - } + 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 */ - getMinResolution() { - return /** @type {number} */ (this.get(LayerProperty.MIN_RESOLUTION)); - } + 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 */ - getOpacity() { - return /** @type {number} */ (this.get(LayerProperty.OPACITY)); - } + getOpacity() { + return /** @type {number} */ (this.get(LayerProperty.OPACITY)); + } - /** + /** * @abstract * @return {module:ol/source/State} Source state. */ - getSourceState() {} + getSourceState() {} - /** + /** * 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)); - } + 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 */ - getZIndex() { - return /** @type {number} */ (this.get(LayerProperty.Z_INDEX)); - } + 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 {module:ol/extent~Extent|undefined} extent The extent of the layer. * @observable * @api */ - setExtent(extent) { - this.set(LayerProperty.EXTENT, extent); - } + 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 */ - setMaxResolution(maxResolution) { - this.set(LayerProperty.MAX_RESOLUTION, maxResolution); - } + 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 */ - setMinResolution(minResolution) { - this.set(LayerProperty.MIN_RESOLUTION, minResolution); - } + 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 */ - setOpacity(opacity) { - this.set(LayerProperty.OPACITY, opacity); - } + 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 */ - setVisible(visible) { - this.set(LayerProperty.VISIBLE, visible); - } + 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 */ - setZIndex(zindex) { - this.set(LayerProperty.Z_INDEX, zindex); - } + setZIndex(zindex) { + this.set(LayerProperty.Z_INDEX, zindex); + } } inherits(BaseLayer, BaseObject); diff --git a/src/ol/layer/Heatmap.js b/src/ol/layer/Heatmap.js index 5eafc51eee..e8fe57bd70 100644 --- a/src/ol/layer/Heatmap.js +++ b/src/ol/layer/Heatmap.js @@ -289,9 +289,8 @@ inherits(Heatmap, VectorLayer); /** * @param {Array.} colors A list of colored. * @return {Uint8ClampedArray} An array. - * @private */ -const createGradient = function(colors) { +function createGradient(colors) { const width = 1; const height = 256; const context = createCanvasContext2D(width, height); @@ -306,7 +305,7 @@ const createGradient = function(colors) { context.fillRect(0, 0, width, height); return context.getImageData(0, 0, width, height).data; -}; +} export default Heatmap; diff --git a/src/ol/layer/Tile.js b/src/ol/layer/Tile.js index 5cfa7a9935..f1b504e4e6 100644 --- a/src/ol/layer/Tile.js +++ b/src/ol/layer/Tile.js @@ -46,67 +46,67 @@ import {assign} from '../obj.js'; * @api */ class TileLayer { - constructor(opt_options) { - const options = opt_options ? opt_options : {}; + constructor(opt_options) { + const options = opt_options ? opt_options : {}; - const baseOptions = assign({}, options); + const baseOptions = assign({}, options); - delete baseOptions.preload; - delete baseOptions.useInterimTilesOnError; - Layer.call(this, /** @type {module:ol/layer/Layer~Options} */ (baseOptions)); + delete baseOptions.preload; + delete baseOptions.useInterimTilesOnError; + Layer.call(this, /** @type {module:ol/layer/Layer~Options} */ (baseOptions)); - this.setPreload(options.preload !== undefined ? options.preload : 0); - this.setUseInterimTilesOnError(options.useInterimTilesOnError !== undefined ? - options.useInterimTilesOnError : true); + this.setPreload(options.preload !== undefined ? options.preload : 0); + this.setUseInterimTilesOnError(options.useInterimTilesOnError !== undefined ? + options.useInterimTilesOnError : true); - /** + /** * The layer type. * @protected * @type {module:ol/LayerType} */ - this.type = LayerType.TILE; + this.type = LayerType.TILE; - } + } - /** + /** * Return the level as number to which we will preload tiles up to. * @return {number} The level to preload tiles up to. * @observable * @api */ - getPreload() { - return /** @type {number} */ (this.get(TileProperty.PRELOAD)); - } + getPreload() { + return /** @type {number} */ (this.get(TileProperty.PRELOAD)); + } - /** + /** * Set the level as number to which we will preload tiles up to. * @param {number} preload The level to preload tiles up to. * @observable * @api */ - setPreload(preload) { - this.set(TileProperty.PRELOAD, preload); - } + setPreload(preload) { + this.set(TileProperty.PRELOAD, preload); + } - /** + /** * Whether we use interim tiles on error. * @return {boolean} Use interim tiles on error. * @observable * @api */ - getUseInterimTilesOnError() { - return /** @type {boolean} */ (this.get(TileProperty.USE_INTERIM_TILES_ON_ERROR)); - } + getUseInterimTilesOnError() { + return /** @type {boolean} */ (this.get(TileProperty.USE_INTERIM_TILES_ON_ERROR)); + } - /** + /** * Set whether we use interim tiles on error. * @param {boolean} useInterimTilesOnError Use interim tiles on error. * @observable * @api */ - setUseInterimTilesOnError(useInterimTilesOnError) { - this.set(TileProperty.USE_INTERIM_TILES_ON_ERROR, useInterimTilesOnError); - } + setUseInterimTilesOnError(useInterimTilesOnError) { + this.set(TileProperty.USE_INTERIM_TILES_ON_ERROR, useInterimTilesOnError); + } } inherits(TileLayer, Layer); diff --git a/src/ol/layer/Vector.js b/src/ol/layer/Vector.js index 2cc7f33089..7cd627e21b 100644 --- a/src/ol/layer/Vector.js +++ b/src/ol/layer/Vector.js @@ -92,152 +92,152 @@ const Property = { * @api */ class VectorLayer { - constructor(opt_options) { - const options = opt_options ? - opt_options : /** @type {module:ol/layer/Vector~Options} */ ({}); + constructor(opt_options) { + const options = opt_options ? + opt_options : /** @type {module:ol/layer/Vector~Options} */ ({}); - const baseOptions = assign({}, options); + const baseOptions = assign({}, options); - delete baseOptions.style; - delete baseOptions.renderBuffer; - delete baseOptions.updateWhileAnimating; - delete baseOptions.updateWhileInteracting; - Layer.call(this, /** @type {module:ol/layer/Layer~Options} */ (baseOptions)); + delete baseOptions.style; + delete baseOptions.renderBuffer; + delete baseOptions.updateWhileAnimating; + delete baseOptions.updateWhileInteracting; + Layer.call(this, /** @type {module:ol/layer/Layer~Options} */ (baseOptions)); - /** + /** * @private * @type {boolean} */ - this.declutter_ = options.declutter !== undefined ? options.declutter : false; + this.declutter_ = options.declutter !== undefined ? options.declutter : false; - /** + /** * @type {number} * @private */ - this.renderBuffer_ = options.renderBuffer !== undefined ? - options.renderBuffer : 100; + this.renderBuffer_ = options.renderBuffer !== undefined ? + options.renderBuffer : 100; - /** + /** * User provided style. * @type {module:ol/style/Style|Array.|module:ol/style/Style~StyleFunction} * @private */ - this.style_ = null; + this.style_ = null; - /** + /** * Style function for use within the library. * @type {module:ol/style/Style~StyleFunction|undefined} * @private */ - this.styleFunction_ = undefined; + this.styleFunction_ = undefined; - this.setStyle(options.style); + this.setStyle(options.style); - /** + /** * @type {boolean} * @private */ - this.updateWhileAnimating_ = options.updateWhileAnimating !== undefined ? - options.updateWhileAnimating : false; + this.updateWhileAnimating_ = options.updateWhileAnimating !== undefined ? + options.updateWhileAnimating : false; - /** + /** * @type {boolean} * @private */ - this.updateWhileInteracting_ = options.updateWhileInteracting !== undefined ? - options.updateWhileInteracting : false; + this.updateWhileInteracting_ = options.updateWhileInteracting !== undefined ? + options.updateWhileInteracting : false; - /** + /** * @private * @type {module:ol/layer/VectorTileRenderType|string} */ - this.renderMode_ = options.renderMode || VectorRenderType.VECTOR; + this.renderMode_ = options.renderMode || VectorRenderType.VECTOR; - /** + /** * The layer type. * @protected * @type {module:ol/LayerType} */ - this.type = LayerType.VECTOR; + this.type = LayerType.VECTOR; - } + } - /** + /** * @return {boolean} Declutter. */ - getDeclutter() { - return this.declutter_; - } + getDeclutter() { + return this.declutter_; + } - /** + /** * @param {boolean} declutter Declutter. */ - setDeclutter(declutter) { - this.declutter_ = declutter; - } + setDeclutter(declutter) { + this.declutter_ = declutter; + } - /** + /** * @return {number|undefined} Render buffer. */ - getRenderBuffer() { - return this.renderBuffer_; - } + getRenderBuffer() { + return this.renderBuffer_; + } - /** + /** * @return {function(module:ol/Feature, module:ol/Feature): number|null|undefined} Render * order. */ - getRenderOrder() { - return ( - /** @type {module:ol/render~OrderFunction|null|undefined} */ (this.get(Property.RENDER_ORDER)) - ); - } + getRenderOrder() { + return ( + /** @type {module:ol/render~OrderFunction|null|undefined} */ (this.get(Property.RENDER_ORDER)) + ); + } - /** + /** * Get the style for features. This returns whatever was passed to the `style` * option at construction or to the `setStyle` method. * @return {module:ol/style/Style|Array.|module:ol/style/Style~StyleFunction} * Layer style. * @api */ - getStyle() { - return this.style_; - } + getStyle() { + return this.style_; + } - /** + /** * Get the style function. * @return {module:ol/style/Style~StyleFunction|undefined} Layer style function. * @api */ - getStyleFunction() { - return this.styleFunction_; - } + getStyleFunction() { + return this.styleFunction_; + } - /** + /** * @return {boolean} Whether the rendered layer should be updated while * animating. */ - getUpdateWhileAnimating() { - return this.updateWhileAnimating_; - } + getUpdateWhileAnimating() { + return this.updateWhileAnimating_; + } - /** + /** * @return {boolean} Whether the rendered layer should be updated while * interacting. */ - getUpdateWhileInteracting() { - return this.updateWhileInteracting_; - } + getUpdateWhileInteracting() { + return this.updateWhileInteracting_; + } - /** + /** * @param {module:ol/render~OrderFunction|null|undefined} renderOrder * Render order. */ - setRenderOrder(renderOrder) { - this.set(Property.RENDER_ORDER, renderOrder); - } + setRenderOrder(renderOrder) { + this.set(Property.RENDER_ORDER, renderOrder); + } - /** + /** * Set the style for features. This can be a single style object, an array * of styles, or a function that takes a feature and resolution and returns * an array of styles. If it is `undefined` the default style is used. If @@ -248,19 +248,19 @@ class VectorLayer { * style Layer style. * @api */ - setStyle(style) { - this.style_ = style !== undefined ? style : createDefaultStyle; - this.styleFunction_ = style === null ? - undefined : toStyleFunction(this.style_); - this.changed(); - } + setStyle(style) { + this.style_ = style !== undefined ? style : createDefaultStyle; + this.styleFunction_ = style === null ? + undefined : toStyleFunction(this.style_); + this.changed(); + } - /** + /** * @return {module:ol/layer/VectorRenderType|string} The render mode. */ - getRenderMode() { - return this.renderMode_; - } + getRenderMode() { + return this.renderMode_; + } } inherits(VectorLayer, Layer); diff --git a/src/ol/layer/VectorTile.js b/src/ol/layer/VectorTile.js index 2f0ac36950..20d7453d88 100644 --- a/src/ol/layer/VectorTile.js +++ b/src/ol/layer/VectorTile.js @@ -100,78 +100,78 @@ export const RenderType = { * @api */ class VectorTileLayer { - constructor(opt_options) { - const options = opt_options ? opt_options : {}; + constructor(opt_options) { + const options = opt_options ? opt_options : {}; - let renderMode = options.renderMode || VectorTileRenderType.HYBRID; - assert(renderMode == undefined || + let renderMode = options.renderMode || VectorTileRenderType.HYBRID; + assert(renderMode == undefined || renderMode == VectorTileRenderType.IMAGE || renderMode == VectorTileRenderType.HYBRID || renderMode == VectorTileRenderType.VECTOR, - 28); // `renderMode` must be `'image'`, `'hybrid'` or `'vector'` - if (options.declutter && renderMode == VectorTileRenderType.IMAGE) { - renderMode = VectorTileRenderType.HYBRID; - } - options.renderMode = renderMode; + 28); // `renderMode` must be `'image'`, `'hybrid'` or `'vector'` + if (options.declutter && renderMode == VectorTileRenderType.IMAGE) { + renderMode = VectorTileRenderType.HYBRID; + } + options.renderMode = renderMode; - const baseOptions = assign({}, options); + const baseOptions = assign({}, options); - delete baseOptions.preload; - delete baseOptions.useInterimTilesOnError; - VectorLayer.call(this, /** @type {module:ol/layer/Vector~Options} */ (baseOptions)); + delete baseOptions.preload; + delete baseOptions.useInterimTilesOnError; + VectorLayer.call(this, /** @type {module:ol/layer/Vector~Options} */ (baseOptions)); - this.setPreload(options.preload ? options.preload : 0); - this.setUseInterimTilesOnError(options.useInterimTilesOnError !== undefined ? - options.useInterimTilesOnError : true); + this.setPreload(options.preload ? options.preload : 0); + this.setUseInterimTilesOnError(options.useInterimTilesOnError !== undefined ? + options.useInterimTilesOnError : true); - /** + /** * The layer type. * @protected * @type {module:ol/LayerType} */ - this.type = LayerType.VECTOR_TILE; + this.type = LayerType.VECTOR_TILE; - } + } - /** + /** * Return the level as number to which we will preload tiles up to. * @return {number} The level to preload tiles up to. * @observable * @api */ - getPreload() { - return /** @type {number} */ (this.get(TileProperty.PRELOAD)); - } + getPreload() { + return /** @type {number} */ (this.get(TileProperty.PRELOAD)); + } - /** + /** * Whether we use interim tiles on error. * @return {boolean} Use interim tiles on error. * @observable * @api */ - getUseInterimTilesOnError() { - return /** @type {boolean} */ (this.get(TileProperty.USE_INTERIM_TILES_ON_ERROR)); - } + getUseInterimTilesOnError() { + return /** @type {boolean} */ (this.get(TileProperty.USE_INTERIM_TILES_ON_ERROR)); + } - /** + /** * Set the level as number to which we will preload tiles up to. * @param {number} preload The level to preload tiles up to. * @observable * @api */ - setPreload(preload) { - this.set(TileProperty.PRELOAD, preload); - } + setPreload(preload) { + this.set(TileProperty.PRELOAD, preload); + } - /** + /** * Set whether we use interim tiles on error. * @param {boolean} useInterimTilesOnError Use interim tiles on error. * @observable * @api */ - setUseInterimTilesOnError(useInterimTilesOnError) { - this.set(TileProperty.USE_INTERIM_TILES_ON_ERROR, useInterimTilesOnError); - } + setUseInterimTilesOnError(useInterimTilesOnError) { + this.set(TileProperty.USE_INTERIM_TILES_ON_ERROR, useInterimTilesOnError); + } } inherits(VectorTileLayer, VectorLayer); diff --git a/src/ol/pointer/EventSource.js b/src/ol/pointer/EventSource.js index e7ea944ce2..46547520d3 100644 --- a/src/ol/pointer/EventSource.js +++ b/src/ol/pointer/EventSource.js @@ -7,36 +7,36 @@ * @constructor */ class EventSource { - constructor(dispatcher, mapping) { - /** + constructor(dispatcher, mapping) { + /** * @type {module:ol/pointer/PointerEventHandler} */ - this.dispatcher = dispatcher; + this.dispatcher = dispatcher; - /** + /** * @private * @const * @type {!Object.} */ - this.mapping_ = mapping; - } + this.mapping_ = mapping; + } - /** + /** * List of events supported by this source. * @return {Array.} Event names */ - getEvents() { - return Object.keys(this.mapping_); - } + getEvents() { + return Object.keys(this.mapping_); + } - /** + /** * Returns the handler that should handle a given event type. * @param {string} eventType The event type. * @return {function(Event)} Handler */ - getHandlerForEvent(eventType) { - return this.mapping_[eventType]; - } + getHandlerForEvent(eventType) { + return this.mapping_[eventType]; + } } export default EventSource; diff --git a/src/ol/pointer/MouseSource.js b/src/ol/pointer/MouseSource.js index c8f12bde3f..1c8c69e345 100644 --- a/src/ol/pointer/MouseSource.js +++ b/src/ol/pointer/MouseSource.js @@ -34,6 +34,27 @@ import {inherits} from '../util.js'; import EventSource from '../pointer/EventSource.js'; + +/** + * @type {number} + */ +export const POINTER_ID = 1; + + +/** + * @type {string} + */ +export const POINTER_TYPE = 'mouse'; + + +/** + * Radius around touchend that swallows mouse events. + * + * @type {number} + */ +const DEDUP_DIST = 25; + + /** * @param {module:ol/pointer/PointerEventHandler} dispatcher Event handler. * @constructor @@ -195,26 +216,6 @@ class MouseSource { inherits(MouseSource, EventSource); -/** - * @type {number} - */ -export const POINTER_ID = 1; - - -/** - * @type {string} - */ -export const POINTER_TYPE = 'mouse'; - - -/** - * Radius around touchend that swallows mouse events. - * - * @type {number} - */ -const DEDUP_DIST = 25; - - /** * Creates a copy of the original event that will be used * for the fake pointer event. diff --git a/src/ol/pointer/MsSource.js b/src/ol/pointer/MsSource.js index 402ff54b7f..230b0aba9d 100644 --- a/src/ol/pointer/MsSource.js +++ b/src/ol/pointer/MsSource.js @@ -34,143 +34,6 @@ import {inherits} from '../util.js'; import EventSource from '../pointer/EventSource.js'; -/** - * @param {module:ol/pointer/PointerEventHandler} dispatcher Event handler. - * @constructor - * @extends {module:ol/pointer/EventSource} - */ -class MsSource { - constructor(dispatcher) { - const mapping = { - 'MSPointerDown': this.msPointerDown, - 'MSPointerMove': this.msPointerMove, - 'MSPointerUp': this.msPointerUp, - 'MSPointerOut': this.msPointerOut, - 'MSPointerOver': this.msPointerOver, - 'MSPointerCancel': this.msPointerCancel, - 'MSGotPointerCapture': this.msGotPointerCapture, - 'MSLostPointerCapture': this.msLostPointerCapture - }; - EventSource.call(this, dispatcher, mapping); - - /** - * @const - * @type {!Object.} - */ - this.pointerMap = dispatcher.pointerMap; - } - - /** - * Creates a copy of the original event that will be used - * for the fake pointer event. - * - * @private - * @param {MSPointerEvent} inEvent The in event. - * @return {Object} The copied event. - */ - prepareEvent_(inEvent) { - let e = inEvent; - if (typeof inEvent.pointerType === 'number') { - e = this.dispatcher.cloneEvent(inEvent, inEvent); - e.pointerType = POINTER_TYPES[inEvent.pointerType]; - } - - return e; - } - - /** - * Remove this pointer from the list of active pointers. - * @param {number} pointerId Pointer identifier. - */ - cleanup(pointerId) { - delete this.pointerMap[pointerId.toString()]; - } - - /** - * Handler for `msPointerDown`. - * - * @param {MSPointerEvent} inEvent The in event. - */ - msPointerDown(inEvent) { - this.pointerMap[inEvent.pointerId.toString()] = inEvent; - const e = this.prepareEvent_(inEvent); - this.dispatcher.down(e, inEvent); - } - - /** - * Handler for `msPointerMove`. - * - * @param {MSPointerEvent} inEvent The in event. - */ - msPointerMove(inEvent) { - const e = this.prepareEvent_(inEvent); - this.dispatcher.move(e, inEvent); - } - - /** - * Handler for `msPointerUp`. - * - * @param {MSPointerEvent} inEvent The in event. - */ - msPointerUp(inEvent) { - const e = this.prepareEvent_(inEvent); - this.dispatcher.up(e, inEvent); - this.cleanup(inEvent.pointerId); - } - - /** - * Handler for `msPointerOut`. - * - * @param {MSPointerEvent} inEvent The in event. - */ - msPointerOut(inEvent) { - const e = this.prepareEvent_(inEvent); - this.dispatcher.leaveOut(e, inEvent); - } - - /** - * Handler for `msPointerOver`. - * - * @param {MSPointerEvent} inEvent The in event. - */ - msPointerOver(inEvent) { - const e = this.prepareEvent_(inEvent); - this.dispatcher.enterOver(e, inEvent); - } - - /** - * Handler for `msPointerCancel`. - * - * @param {MSPointerEvent} inEvent The in event. - */ - msPointerCancel(inEvent) { - const e = this.prepareEvent_(inEvent); - this.dispatcher.cancel(e, inEvent); - this.cleanup(inEvent.pointerId); - } - - /** - * Handler for `msLostPointerCapture`. - * - * @param {MSPointerEvent} inEvent The in event. - */ - msLostPointerCapture(inEvent) { - const e = this.dispatcher.makeEvent('lostpointercapture', inEvent, inEvent); - this.dispatcher.dispatchEvent(e); - } - - /** - * Handler for `msGotPointerCapture`. - * - * @param {MSPointerEvent} inEvent The in event. - */ - msGotPointerCapture(inEvent) { - const e = this.dispatcher.makeEvent('gotpointercapture', inEvent, inEvent); - this.dispatcher.dispatchEvent(e); - } -} - -inherits(MsSource, EventSource); /** * @const @@ -185,4 +48,142 @@ const POINTER_TYPES = [ ]; +/** + * @param {module:ol/pointer/PointerEventHandler} dispatcher Event handler. + * @constructor + * @extends {module:ol/pointer/EventSource} + */ +class MsSource { + constructor(dispatcher) { + const mapping = { + 'MSPointerDown': this.msPointerDown, + 'MSPointerMove': this.msPointerMove, + 'MSPointerUp': this.msPointerUp, + 'MSPointerOut': this.msPointerOut, + 'MSPointerOver': this.msPointerOver, + 'MSPointerCancel': this.msPointerCancel, + 'MSGotPointerCapture': this.msGotPointerCapture, + 'MSLostPointerCapture': this.msLostPointerCapture + }; + EventSource.call(this, dispatcher, mapping); + + /** + * @const + * @type {!Object.} + */ + this.pointerMap = dispatcher.pointerMap; + } + + /** + * Creates a copy of the original event that will be used + * for the fake pointer event. + * + * @private + * @param {MSPointerEvent} inEvent The in event. + * @return {Object} The copied event. + */ + prepareEvent_(inEvent) { + let e = inEvent; + if (typeof inEvent.pointerType === 'number') { + e = this.dispatcher.cloneEvent(inEvent, inEvent); + e.pointerType = POINTER_TYPES[inEvent.pointerType]; + } + + return e; + } + + /** + * Remove this pointer from the list of active pointers. + * @param {number} pointerId Pointer identifier. + */ + cleanup(pointerId) { + delete this.pointerMap[pointerId.toString()]; + } + + /** + * Handler for `msPointerDown`. + * + * @param {MSPointerEvent} inEvent The in event. + */ + msPointerDown(inEvent) { + this.pointerMap[inEvent.pointerId.toString()] = inEvent; + const e = this.prepareEvent_(inEvent); + this.dispatcher.down(e, inEvent); + } + + /** + * Handler for `msPointerMove`. + * + * @param {MSPointerEvent} inEvent The in event. + */ + msPointerMove(inEvent) { + const e = this.prepareEvent_(inEvent); + this.dispatcher.move(e, inEvent); + } + + /** + * Handler for `msPointerUp`. + * + * @param {MSPointerEvent} inEvent The in event. + */ + msPointerUp(inEvent) { + const e = this.prepareEvent_(inEvent); + this.dispatcher.up(e, inEvent); + this.cleanup(inEvent.pointerId); + } + + /** + * Handler for `msPointerOut`. + * + * @param {MSPointerEvent} inEvent The in event. + */ + msPointerOut(inEvent) { + const e = this.prepareEvent_(inEvent); + this.dispatcher.leaveOut(e, inEvent); + } + + /** + * Handler for `msPointerOver`. + * + * @param {MSPointerEvent} inEvent The in event. + */ + msPointerOver(inEvent) { + const e = this.prepareEvent_(inEvent); + this.dispatcher.enterOver(e, inEvent); + } + + /** + * Handler for `msPointerCancel`. + * + * @param {MSPointerEvent} inEvent The in event. + */ + msPointerCancel(inEvent) { + const e = this.prepareEvent_(inEvent); + this.dispatcher.cancel(e, inEvent); + this.cleanup(inEvent.pointerId); + } + + /** + * Handler for `msLostPointerCapture`. + * + * @param {MSPointerEvent} inEvent The in event. + */ + msLostPointerCapture(inEvent) { + const e = this.dispatcher.makeEvent('lostpointercapture', inEvent, inEvent); + this.dispatcher.dispatchEvent(e); + } + + /** + * Handler for `msGotPointerCapture`. + * + * @param {MSPointerEvent} inEvent The in event. + */ + msGotPointerCapture(inEvent) { + const e = this.dispatcher.makeEvent('gotpointercapture', inEvent, inEvent); + this.dispatcher.dispatchEvent(e); + } +} + +inherits(MsSource, EventSource); + export default MsSource; diff --git a/src/ol/pointer/NativeSource.js b/src/ol/pointer/NativeSource.js index a28740ee7b..0ac60c42bd 100644 --- a/src/ol/pointer/NativeSource.js +++ b/src/ol/pointer/NativeSource.js @@ -40,91 +40,91 @@ import EventSource from '../pointer/EventSource.js'; * @extends {module:ol/pointer/EventSource} */ class NativeSource { - constructor(dispatcher) { - const mapping = { - 'pointerdown': this.pointerDown, - 'pointermove': this.pointerMove, - 'pointerup': this.pointerUp, - 'pointerout': this.pointerOut, - 'pointerover': this.pointerOver, - 'pointercancel': this.pointerCancel, - 'gotpointercapture': this.gotPointerCapture, - 'lostpointercapture': this.lostPointerCapture - }; - EventSource.call(this, dispatcher, mapping); - } + constructor(dispatcher) { + const mapping = { + 'pointerdown': this.pointerDown, + 'pointermove': this.pointerMove, + 'pointerup': this.pointerUp, + 'pointerout': this.pointerOut, + 'pointerover': this.pointerOver, + 'pointercancel': this.pointerCancel, + 'gotpointercapture': this.gotPointerCapture, + 'lostpointercapture': this.lostPointerCapture + }; + EventSource.call(this, dispatcher, mapping); + } - /** + /** * Handler for `pointerdown`. * * @param {Event} inEvent The in event. */ - pointerDown(inEvent) { - this.dispatcher.fireNativeEvent(inEvent); - } + pointerDown(inEvent) { + this.dispatcher.fireNativeEvent(inEvent); + } - /** + /** * Handler for `pointermove`. * * @param {Event} inEvent The in event. */ - pointerMove(inEvent) { - this.dispatcher.fireNativeEvent(inEvent); - } + pointerMove(inEvent) { + this.dispatcher.fireNativeEvent(inEvent); + } - /** + /** * Handler for `pointerup`. * * @param {Event} inEvent The in event. */ - pointerUp(inEvent) { - this.dispatcher.fireNativeEvent(inEvent); - } + pointerUp(inEvent) { + this.dispatcher.fireNativeEvent(inEvent); + } - /** + /** * Handler for `pointerout`. * * @param {Event} inEvent The in event. */ - pointerOut(inEvent) { - this.dispatcher.fireNativeEvent(inEvent); - } + pointerOut(inEvent) { + this.dispatcher.fireNativeEvent(inEvent); + } - /** + /** * Handler for `pointerover`. * * @param {Event} inEvent The in event. */ - pointerOver(inEvent) { - this.dispatcher.fireNativeEvent(inEvent); - } + pointerOver(inEvent) { + this.dispatcher.fireNativeEvent(inEvent); + } - /** + /** * Handler for `pointercancel`. * * @param {Event} inEvent The in event. */ - pointerCancel(inEvent) { - this.dispatcher.fireNativeEvent(inEvent); - } + pointerCancel(inEvent) { + this.dispatcher.fireNativeEvent(inEvent); + } - /** + /** * Handler for `lostpointercapture`. * * @param {Event} inEvent The in event. */ - lostPointerCapture(inEvent) { - this.dispatcher.fireNativeEvent(inEvent); - } + lostPointerCapture(inEvent) { + this.dispatcher.fireNativeEvent(inEvent); + } - /** + /** * Handler for `gotpointercapture`. * * @param {Event} inEvent The in event. */ - gotPointerCapture(inEvent) { - this.dispatcher.fireNativeEvent(inEvent); - } + gotPointerCapture(inEvent) { + this.dispatcher.fireNativeEvent(inEvent); + } } inherits(NativeSource, EventSource); diff --git a/src/ol/pointer/PointerEvent.js b/src/ol/pointer/PointerEvent.js index 56957d9943..f21ad0aa11 100644 --- a/src/ol/pointer/PointerEvent.js +++ b/src/ol/pointer/PointerEvent.js @@ -34,6 +34,14 @@ import {inherits} from '../util.js'; import Event from '../events/Event.js'; + +/** + * Is the `buttons` property supported? + * @type {boolean} + */ +let HAS_BUTTONS = false; + + /** * A class for pointer events. * @@ -48,221 +56,214 @@ import Event from '../events/Event.js'; * initial event properties. */ class PointerEvent { - constructor(type, originalEvent, opt_eventDict) { - Event.call(this, type); + constructor(type, originalEvent, opt_eventDict) { + Event.call(this, type); - /** + /** * @const * @type {Event} */ - this.originalEvent = originalEvent; + this.originalEvent = originalEvent; - const eventDict = opt_eventDict ? opt_eventDict : {}; + const eventDict = opt_eventDict ? opt_eventDict : {}; - /** + /** * @type {number} */ - this.buttons = this.getButtons_(eventDict); + this.buttons = this.getButtons_(eventDict); - /** + /** * @type {number} */ - this.pressure = this.getPressure_(eventDict, this.buttons); + this.pressure = this.getPressure_(eventDict, this.buttons); - // MouseEvent related properties + // MouseEvent related properties - /** + /** * @type {boolean} */ - this.bubbles = 'bubbles' in eventDict ? eventDict['bubbles'] : false; + this.bubbles = 'bubbles' in eventDict ? eventDict['bubbles'] : false; - /** + /** * @type {boolean} */ - this.cancelable = 'cancelable' in eventDict ? eventDict['cancelable'] : false; + this.cancelable = 'cancelable' in eventDict ? eventDict['cancelable'] : false; - /** + /** * @type {Object} */ - this.view = 'view' in eventDict ? eventDict['view'] : null; + this.view = 'view' in eventDict ? eventDict['view'] : null; - /** + /** * @type {number} */ - this.detail = 'detail' in eventDict ? eventDict['detail'] : null; + this.detail = 'detail' in eventDict ? eventDict['detail'] : null; - /** + /** * @type {number} */ - this.screenX = 'screenX' in eventDict ? eventDict['screenX'] : 0; + this.screenX = 'screenX' in eventDict ? eventDict['screenX'] : 0; - /** + /** * @type {number} */ - this.screenY = 'screenY' in eventDict ? eventDict['screenY'] : 0; + this.screenY = 'screenY' in eventDict ? eventDict['screenY'] : 0; - /** + /** * @type {number} */ - this.clientX = 'clientX' in eventDict ? eventDict['clientX'] : 0; + this.clientX = 'clientX' in eventDict ? eventDict['clientX'] : 0; - /** + /** * @type {number} */ - this.clientY = 'clientY' in eventDict ? eventDict['clientY'] : 0; + this.clientY = 'clientY' in eventDict ? eventDict['clientY'] : 0; - /** + /** * @type {boolean} */ - this.ctrlKey = 'ctrlKey' in eventDict ? eventDict['ctrlKey'] : false; + this.ctrlKey = 'ctrlKey' in eventDict ? eventDict['ctrlKey'] : false; - /** + /** * @type {boolean} */ - this.altKey = 'altKey' in eventDict ? eventDict['altKey'] : false; + this.altKey = 'altKey' in eventDict ? eventDict['altKey'] : false; - /** + /** * @type {boolean} */ - this.shiftKey = 'shiftKey' in eventDict ? eventDict['shiftKey'] : false; + this.shiftKey = 'shiftKey' in eventDict ? eventDict['shiftKey'] : false; - /** + /** * @type {boolean} */ - this.metaKey = 'metaKey' in eventDict ? eventDict['metaKey'] : false; + this.metaKey = 'metaKey' in eventDict ? eventDict['metaKey'] : false; - /** + /** * @type {number} */ - this.button = 'button' in eventDict ? eventDict['button'] : 0; + this.button = 'button' in eventDict ? eventDict['button'] : 0; - /** + /** * @type {Node} */ - this.relatedTarget = 'relatedTarget' in eventDict ? - eventDict['relatedTarget'] : null; + this.relatedTarget = 'relatedTarget' in eventDict ? + eventDict['relatedTarget'] : null; - // PointerEvent related properties + // PointerEvent related properties - /** + /** * @const * @type {number} */ - this.pointerId = 'pointerId' in eventDict ? eventDict['pointerId'] : 0; + this.pointerId = 'pointerId' in eventDict ? eventDict['pointerId'] : 0; - /** + /** * @type {number} */ - this.width = 'width' in eventDict ? eventDict['width'] : 0; + this.width = 'width' in eventDict ? eventDict['width'] : 0; - /** + /** * @type {number} */ - this.height = 'height' in eventDict ? eventDict['height'] : 0; + this.height = 'height' in eventDict ? eventDict['height'] : 0; - /** + /** * @type {number} */ - this.tiltX = 'tiltX' in eventDict ? eventDict['tiltX'] : 0; + this.tiltX = 'tiltX' in eventDict ? eventDict['tiltX'] : 0; - /** + /** * @type {number} */ - this.tiltY = 'tiltY' in eventDict ? eventDict['tiltY'] : 0; + this.tiltY = 'tiltY' in eventDict ? eventDict['tiltY'] : 0; - /** + /** * @type {string} */ - this.pointerType = 'pointerType' in eventDict ? eventDict['pointerType'] : ''; + this.pointerType = 'pointerType' in eventDict ? eventDict['pointerType'] : ''; - /** + /** * @type {number} */ - this.hwTimestamp = 'hwTimestamp' in eventDict ? eventDict['hwTimestamp'] : 0; + this.hwTimestamp = 'hwTimestamp' in eventDict ? eventDict['hwTimestamp'] : 0; - /** + /** * @type {boolean} */ - this.isPrimary = 'isPrimary' in eventDict ? eventDict['isPrimary'] : false; + this.isPrimary = 'isPrimary' in eventDict ? eventDict['isPrimary'] : false; - // keep the semantics of preventDefault - if (originalEvent.preventDefault) { - this.preventDefault = function() { - originalEvent.preventDefault(); - }; - } - } + // keep the semantics of preventDefault + if (originalEvent.preventDefault) { + this.preventDefault = function() { + originalEvent.preventDefault(); + }; + } + } - /** + /** * @private * @param {Object.} 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; - } + 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.} 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; - } - return 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; + } + return pressure; + } } inherits(PointerEvent, Event); -/** - * Is the `buttons` property supported? - * @type {boolean} - */ -let HAS_BUTTONS = false; - - /** * Checks if the `buttons` property is supported. */ @@ -274,4 +275,5 @@ let HAS_BUTTONS = false; // pass } })(); + export default PointerEvent; diff --git a/src/ol/pointer/PointerEventHandler.js b/src/ol/pointer/PointerEventHandler.js index 2be6d9ca34..aa9becd009 100644 --- a/src/ol/pointer/PointerEventHandler.js +++ b/src/ol/pointer/PointerEventHandler.js @@ -42,6 +42,47 @@ import NativeSource from '../pointer/NativeSource.js'; import PointerEvent from '../pointer/PointerEvent.js'; import TouchSource from '../pointer/TouchSource.js'; + +/** + * Properties to copy when cloning an event, with default values. + * @type {Array.} + */ +const CLONE_PROPS = [ + // MouseEvent + ['bubbles', false], + ['cancelable', false], + ['view', null], + ['detail', null], + ['screenX', 0], + ['screenY', 0], + ['clientX', 0], + ['clientY', 0], + ['ctrlKey', false], + ['altKey', false], + ['shiftKey', false], + ['metaKey', false], + ['button', 0], + ['relatedTarget', null], + // DOM Level 3 + ['buttons', 0], + // PointerEvent + ['pointerId', 0], + ['width', 0], + ['height', 0], + ['pressure', 0], + ['tiltX', 0], + ['tiltY', 0], + ['pointerType', ''], + ['hwTimestamp', 0], + ['isPrimary', false], + // event instance + ['type', ''], + ['target', null], + ['currentTarget', null], + ['which', 0] +]; + + /** * @constructor * @extends {module:ol/events/EventTarget} @@ -377,44 +418,4 @@ class PointerEventHandler { inherits(PointerEventHandler, EventTarget); -/** - * Properties to copy when cloning an event, with default values. - * @type {Array.} - */ -const CLONE_PROPS = [ - // MouseEvent - ['bubbles', false], - ['cancelable', false], - ['view', null], - ['detail', null], - ['screenX', 0], - ['screenY', 0], - ['clientX', 0], - ['clientY', 0], - ['ctrlKey', false], - ['altKey', false], - ['shiftKey', false], - ['metaKey', false], - ['button', 0], - ['relatedTarget', null], - // DOM Level 3 - ['buttons', 0], - // PointerEvent - ['pointerId', 0], - ['width', 0], - ['height', 0], - ['pressure', 0], - ['tiltX', 0], - ['tiltY', 0], - ['pointerType', ''], - ['hwTimestamp', 0], - ['isPrimary', false], - // event instance - ['type', ''], - ['target', null], - ['currentTarget', null], - ['which', 0] -]; - - export default PointerEventHandler; diff --git a/src/ol/pointer/TouchSource.js b/src/ol/pointer/TouchSource.js index a9c2b05e62..92d34fa54d 100644 --- a/src/ol/pointer/TouchSource.js +++ b/src/ol/pointer/TouchSource.js @@ -37,6 +37,18 @@ import EventSource from '../pointer/EventSource.js'; import {POINTER_ID} from '../pointer/MouseSource.js'; +/** + * @type {number} + */ +const CLICK_COUNT_TIMEOUT = 200; + + +/** + * @type {string} + */ +const POINTER_TYPE = 'touch'; + + /** * @constructor * @param {module:ol/pointer/PointerEventHandler} dispatcher The event handler. @@ -411,15 +423,4 @@ class TouchSource { inherits(TouchSource, EventSource); -/** - * @type {number} - */ -const CLICK_COUNT_TIMEOUT = 200; - - -/** - * @type {string} - */ -const POINTER_TYPE = 'touch'; - export default TouchSource; diff --git a/src/ol/render/Box.js b/src/ol/render/Box.js index 59892c8cd5..63dce4bc66 100644 --- a/src/ol/render/Box.js +++ b/src/ol/render/Box.js @@ -13,117 +13,117 @@ import Polygon from '../geom/Polygon.js'; * @param {string} className CSS class name. */ class RenderBox { - constructor(className) { + constructor(className) { - /** + /** * @type {module:ol/geom/Polygon} * @private */ - this.geometry_ = null; + this.geometry_ = null; - /** + /** * @type {HTMLDivElement} * @private */ - this.element_ = /** @type {HTMLDivElement} */ (document.createElement('div')); - this.element_.style.position = 'absolute'; - this.element_.className = 'ol-box ' + className; + this.element_ = /** @type {HTMLDivElement} */ (document.createElement('div')); + this.element_.style.position = 'absolute'; + this.element_.className = 'ol-box ' + className; - /** + /** * @private * @type {module:ol/PluggableMap} */ - this.map_ = null; + this.map_ = null; - /** + /** * @private * @type {module:ol~Pixel} */ - this.startPixel_ = null; + this.startPixel_ = null; - /** + /** * @private * @type {module:ol~Pixel} */ - this.endPixel_ = null; + this.endPixel_ = null; - } + } - /** + /** * @inheritDoc */ - disposeInternal() { - this.setMap(null); - } + disposeInternal() { + this.setMap(null); + } - /** + /** * @private */ - render_() { - const startPixel = this.startPixel_; - const endPixel = this.endPixel_; - const px = 'px'; - const style = this.element_.style; - style.left = Math.min(startPixel[0], endPixel[0]) + px; - style.top = Math.min(startPixel[1], endPixel[1]) + px; - style.width = Math.abs(endPixel[0] - startPixel[0]) + px; - style.height = Math.abs(endPixel[1] - startPixel[1]) + px; - } + render_() { + const startPixel = this.startPixel_; + const endPixel = this.endPixel_; + const px = 'px'; + const style = this.element_.style; + style.left = Math.min(startPixel[0], endPixel[0]) + px; + style.top = Math.min(startPixel[1], endPixel[1]) + px; + style.width = Math.abs(endPixel[0] - startPixel[0]) + px; + style.height = Math.abs(endPixel[1] - startPixel[1]) + px; + } - /** + /** * @param {module:ol/PluggableMap} map Map. */ - setMap(map) { - if (this.map_) { - this.map_.getOverlayContainer().removeChild(this.element_); - const style = this.element_.style; - style.left = style.top = style.width = style.height = 'inherit'; - } - this.map_ = map; - if (this.map_) { - this.map_.getOverlayContainer().appendChild(this.element_); - } - } + setMap(map) { + if (this.map_) { + this.map_.getOverlayContainer().removeChild(this.element_); + const style = this.element_.style; + style.left = style.top = style.width = style.height = 'inherit'; + } + this.map_ = map; + if (this.map_) { + this.map_.getOverlayContainer().appendChild(this.element_); + } + } - /** + /** * @param {module:ol~Pixel} startPixel Start pixel. * @param {module:ol~Pixel} endPixel End pixel. */ - setPixels(startPixel, endPixel) { - this.startPixel_ = startPixel; - this.endPixel_ = endPixel; - this.createOrUpdateGeometry(); - this.render_(); - } + setPixels(startPixel, endPixel) { + this.startPixel_ = startPixel; + this.endPixel_ = endPixel; + this.createOrUpdateGeometry(); + this.render_(); + } - /** + /** * Creates or updates the cached geometry. */ - createOrUpdateGeometry() { - const startPixel = this.startPixel_; - const endPixel = this.endPixel_; - const pixels = [ - startPixel, - [startPixel[0], endPixel[1]], - endPixel, - [endPixel[0], startPixel[1]] - ]; - const coordinates = pixels.map(this.map_.getCoordinateFromPixel, this.map_); - // close the polygon - coordinates[4] = coordinates[0].slice(); - if (!this.geometry_) { - this.geometry_ = new Polygon([coordinates]); - } else { - this.geometry_.setCoordinates([coordinates]); - } - } + createOrUpdateGeometry() { + const startPixel = this.startPixel_; + const endPixel = this.endPixel_; + const pixels = [ + startPixel, + [startPixel[0], endPixel[1]], + endPixel, + [endPixel[0], startPixel[1]] + ]; + const coordinates = pixels.map(this.map_.getCoordinateFromPixel, this.map_); + // close the polygon + coordinates[4] = coordinates[0].slice(); + if (!this.geometry_) { + this.geometry_ = new Polygon([coordinates]); + } else { + this.geometry_.setCoordinates([coordinates]); + } + } - /** + /** * @return {module:ol/geom/Polygon} Geometry. */ - getGeometry() { - return this.geometry_; - } + getGeometry() { + return this.geometry_; + } } inherits(RenderBox, Disposable); diff --git a/src/ol/render/Feature.js b/src/ol/render/Feature.js index c305f03eee..5c3ccf2f03 100644 --- a/src/ol/render/Feature.js +++ b/src/ol/render/Feature.js @@ -12,6 +12,13 @@ import {get as getProjection} from '../proj.js'; import {transform2D} from '../geom/flat/transform.js'; import {create as createTransform, compose as composeTransform} from '../transform.js'; + +/** + * @type {module:ol/transform~Transform} + */ +const tmpTransform = createTransform(); + + /** * Lightweight, read-only, {@link module:ol/Feature~Feature} and {@link module:ol/geom/Geometry~Geometry} like * structure, optimized for vector tile rendering and styling. Geometry access @@ -26,219 +33,213 @@ import {create as createTransform, compose as composeTransform} from '../transfo * @param {number|string|undefined} id Feature id. */ class RenderFeature { - constructor(type, flatCoordinates, ends, properties, id) { - /** + constructor(type, flatCoordinates, ends, properties, id) { + /** * @private * @type {module:ol/extent~Extent|undefined} */ - this.extent_; + this.extent_; - /** + /** * @private * @type {number|string|undefined} */ - this.id_ = id; + this.id_ = id; - /** + /** * @private * @type {module:ol/geom/GeometryType} */ - this.type_ = type; + this.type_ = type; - /** + /** * @private * @type {Array.} */ - this.flatCoordinates_ = flatCoordinates; + this.flatCoordinates_ = flatCoordinates; - /** + /** * @private * @type {Array.} */ - this.flatInteriorPoints_ = null; + this.flatInteriorPoints_ = null; - /** + /** * @private * @type {Array.} */ - this.flatMidpoints_ = null; + this.flatMidpoints_ = null; - /** + /** * @private * @type {Array.|Array.>} */ - this.ends_ = ends; + this.ends_ = ends; - /** + /** * @private * @type {Object.} */ - this.properties_ = properties; + this.properties_ = properties; - } + } - /** + /** * Get a feature property by its key. * @param {string} key Key * @return {*} Value for the requested key. * @api */ - get(key) { - return this.properties_[key]; - } + get(key) { + return this.properties_[key]; + } - /** + /** * Get the extent of this feature's geometry. * @return {module:ol/extent~Extent} Extent. * @api */ - getExtent() { - if (!this.extent_) { - this.extent_ = this.type_ === GeometryType.POINT ? - createOrUpdateFromCoordinate(this.flatCoordinates_) : - createOrUpdateFromFlatCoordinates( - this.flatCoordinates_, 0, this.flatCoordinates_.length, 2); + getExtent() { + if (!this.extent_) { + this.extent_ = this.type_ === GeometryType.POINT ? + createOrUpdateFromCoordinate(this.flatCoordinates_) : + createOrUpdateFromFlatCoordinates( + this.flatCoordinates_, 0, this.flatCoordinates_.length, 2); - } - return this.extent_; - } + } + return this.extent_; + } - /** + /** * @return {Array.} Flat interior points. */ - getFlatInteriorPoint() { - if (!this.flatInteriorPoints_) { - const flatCenter = getCenter(this.getExtent()); - this.flatInteriorPoints_ = getInteriorPointOfArray( - this.flatCoordinates_, 0, this.ends_, 2, flatCenter, 0); - } - return this.flatInteriorPoints_; - } + getFlatInteriorPoint() { + if (!this.flatInteriorPoints_) { + const flatCenter = getCenter(this.getExtent()); + this.flatInteriorPoints_ = getInteriorPointOfArray( + this.flatCoordinates_, 0, this.ends_, 2, flatCenter, 0); + } + return this.flatInteriorPoints_; + } - /** + /** * @return {Array.} Flat interior points. */ - getFlatInteriorPoints() { - if (!this.flatInteriorPoints_) { - const flatCenters = linearRingssCenter( - this.flatCoordinates_, 0, this.ends_, 2); - this.flatInteriorPoints_ = getInteriorPointsOfMultiArray( - this.flatCoordinates_, 0, this.ends_, 2, flatCenters); - } - return this.flatInteriorPoints_; - } + getFlatInteriorPoints() { + if (!this.flatInteriorPoints_) { + const flatCenters = linearRingssCenter( + this.flatCoordinates_, 0, this.ends_, 2); + this.flatInteriorPoints_ = getInteriorPointsOfMultiArray( + this.flatCoordinates_, 0, this.ends_, 2, flatCenters); + } + return this.flatInteriorPoints_; + } - /** + /** * @return {Array.} Flat midpoint. */ - getFlatMidpoint() { - if (!this.flatMidpoints_) { - this.flatMidpoints_ = interpolatePoint( - this.flatCoordinates_, 0, this.flatCoordinates_.length, 2, 0.5); - } - return this.flatMidpoints_; - } + getFlatMidpoint() { + if (!this.flatMidpoints_) { + this.flatMidpoints_ = interpolatePoint( + this.flatCoordinates_, 0, this.flatCoordinates_.length, 2, 0.5); + } + return this.flatMidpoints_; + } - /** + /** * @return {Array.} Flat midpoints. */ - getFlatMidpoints() { - if (!this.flatMidpoints_) { - this.flatMidpoints_ = []; - const flatCoordinates = this.flatCoordinates_; - let offset = 0; - const ends = this.ends_; - for (let i = 0, ii = ends.length; i < ii; ++i) { - const end = ends[i]; - const midpoint = interpolatePoint( - flatCoordinates, offset, end, 2, 0.5); - extend(this.flatMidpoints_, midpoint); - offset = end; - } - } - return this.flatMidpoints_; - } + getFlatMidpoints() { + if (!this.flatMidpoints_) { + this.flatMidpoints_ = []; + const flatCoordinates = this.flatCoordinates_; + let offset = 0; + const ends = this.ends_; + for (let i = 0, ii = ends.length; i < ii; ++i) { + const end = ends[i]; + const midpoint = interpolatePoint( + flatCoordinates, offset, end, 2, 0.5); + extend(this.flatMidpoints_, midpoint); + offset = end; + } + } + return this.flatMidpoints_; + } - /** + /** * Get the feature identifier. This is a stable identifier for the feature and * is set when reading data from a remote source. * @return {number|string|undefined} Id. * @api */ - getId() { - return this.id_; - } + getId() { + return this.id_; + } - /** + /** * @return {Array.} Flat coordinates. */ - getOrientedFlatCoordinates() { - return this.flatCoordinates_; - } + getOrientedFlatCoordinates() { + return this.flatCoordinates_; + } - /** + /** * For API compatibility with {@link module:ol/Feature~Feature}, this method is useful when * determining the geometry type in style function (see {@link #getType}). * @return {module:ol/render/Feature} Feature. * @api */ - getGeometry() { - return this; - } + getGeometry() { + return this; + } - /** + /** * Get the feature properties. * @return {Object.} Feature properties. * @api */ - getProperties() { - return this.properties_; - } + getProperties() { + return this.properties_; + } - /** + /** * @return {number} Stride. */ - getStride() { - return 2; - } + getStride() { + return 2; + } - /** + /** * Get the type of this feature's geometry. * @return {module:ol/geom/GeometryType} Geometry type. * @api */ - getType() { - return this.type_; - } + getType() { + return this.type_; + } - /** + /** * Transform geometry coordinates from tile pixel space to projected. * The SRS of the source and destination are expected to be the same. * * @param {module:ol/proj~ProjectionLike} source The current projection * @param {module:ol/proj~ProjectionLike} destination The desired projection. */ - transform(source, destination) { - source = getProjection(source); - const pixelExtent = source.getExtent(); - const projectedExtent = source.getWorldExtent(); - const scale = getHeight(projectedExtent) / getHeight(pixelExtent); - composeTransform(tmpTransform, - projectedExtent[0], projectedExtent[3], - scale, -scale, 0, - 0, 0); - transform2D(this.flatCoordinates_, 0, this.flatCoordinates_.length, 2, - tmpTransform, this.flatCoordinates_); - } + transform(source, destination) { + source = getProjection(source); + const pixelExtent = source.getExtent(); + const projectedExtent = source.getWorldExtent(); + const scale = getHeight(projectedExtent) / getHeight(pixelExtent); + composeTransform(tmpTransform, + projectedExtent[0], projectedExtent[3], + scale, -scale, 0, + 0, 0); + transform2D(this.flatCoordinates_, 0, this.flatCoordinates_.length, 2, + tmpTransform, this.flatCoordinates_); + } } -/** - * @type {module:ol/transform~Transform} - */ -const tmpTransform = createTransform(); - - /** * @return {Array.|Array.>} Ends or endss. */ diff --git a/src/ol/render/ReplayGroup.js b/src/ol/render/ReplayGroup.js index c0b3c37fcb..3457654326 100644 --- a/src/ol/render/ReplayGroup.js +++ b/src/ol/render/ReplayGroup.js @@ -7,19 +7,19 @@ * @abstract */ class ReplayGroup { - /** + /** * @abstract * @param {number|undefined} zIndex Z index. * @param {module:ol/render/ReplayType} replayType Replay type. * @return {module:ol/render/VectorContext} Replay. */ - getReplay(zIndex, replayType) {} + getReplay(zIndex, replayType) {} - /** + /** * @abstract * @return {boolean} Is empty. */ - isEmpty() {} + isEmpty() {} } export default ReplayGroup; diff --git a/src/ol/render/VectorContext.js b/src/ol/render/VectorContext.js index 0d160c6547..1d376d3a12 100644 --- a/src/ol/render/VectorContext.js +++ b/src/ol/render/VectorContext.js @@ -10,107 +10,107 @@ * @api */ class VectorContext { - /** + /** * Render a geometry with a custom renderer. * * @param {module:ol/geom/SimpleGeometry} geometry Geometry. * @param {module:ol/Feature|module:ol/render/Feature} feature Feature. * @param {Function} renderer Renderer. */ - drawCustom(geometry, feature, renderer) {} + drawCustom(geometry, feature, renderer) {} - /** + /** * Render a geometry. * * @param {module:ol/geom/Geometry} geometry The geometry to render. */ - drawGeometry(geometry) {} + drawGeometry(geometry) {} - /** + /** * Set the rendering style. * * @param {module:ol/style/Style} style The rendering style. */ - setStyle(style) {} + setStyle(style) {} - /** + /** * @param {module:ol/geom/Circle} circleGeometry Circle geometry. * @param {module:ol/Feature} feature Feature. */ - drawCircle(circleGeometry, feature) {} + drawCircle(circleGeometry, feature) {} - /** + /** * @param {module:ol/Feature} feature Feature. * @param {module:ol/style/Style} style Style. */ - drawFeature(feature, style) {} + drawFeature(feature, style) {} - /** + /** * @param {module:ol/geom/GeometryCollection} geometryCollectionGeometry Geometry * collection. * @param {module:ol/Feature} feature Feature. */ - drawGeometryCollection(geometryCollectionGeometry, feature) {} + drawGeometryCollection(geometryCollectionGeometry, feature) {} - /** + /** * @param {module:ol/geom/LineString|module:ol/render/Feature} lineStringGeometry Line string geometry. * @param {module:ol/Feature|module:ol/render/Feature} feature Feature. */ - drawLineString(lineStringGeometry, feature) {} + drawLineString(lineStringGeometry, feature) {} - /** + /** * @param {module:ol/geom/MultiLineString|module:ol/render/Feature} multiLineStringGeometry MultiLineString geometry. * @param {module:ol/Feature|module:ol/render/Feature} feature Feature. */ - drawMultiLineString(multiLineStringGeometry, feature) {} + drawMultiLineString(multiLineStringGeometry, feature) {} - /** + /** * @param {module:ol/geom/MultiPoint|module:ol/render/Feature} multiPointGeometry MultiPoint geometry. * @param {module:ol/Feature|module:ol/render/Feature} feature Feature. */ - drawMultiPoint(multiPointGeometry, feature) {} + drawMultiPoint(multiPointGeometry, feature) {} - /** + /** * @param {module:ol/geom/MultiPolygon} multiPolygonGeometry MultiPolygon geometry. * @param {module:ol/Feature|module:ol/render/Feature} feature Feature. */ - drawMultiPolygon(multiPolygonGeometry, feature) {} + drawMultiPolygon(multiPolygonGeometry, feature) {} - /** + /** * @param {module:ol/geom/Point|module:ol/render/Feature} pointGeometry Point geometry. * @param {module:ol/Feature|module:ol/render/Feature} feature Feature. */ - drawPoint(pointGeometry, feature) {} + drawPoint(pointGeometry, feature) {} - /** + /** * @param {module:ol/geom/Polygon|module:ol/render/Feature} polygonGeometry Polygon geometry. * @param {module:ol/Feature|module:ol/render/Feature} feature Feature. */ - drawPolygon(polygonGeometry, feature) {} + drawPolygon(polygonGeometry, feature) {} - /** + /** * @param {module:ol/geom/Geometry|module:ol/render/Feature} geometry Geometry. * @param {module:ol/Feature|module:ol/render/Feature} feature Feature. */ - drawText(geometry, feature) {} + drawText(geometry, feature) {} - /** + /** * @param {module:ol/style/Fill} fillStyle Fill style. * @param {module:ol/style/Stroke} strokeStyle Stroke style. */ - setFillStrokeStyle(fillStyle, strokeStyle) {} + setFillStrokeStyle(fillStyle, strokeStyle) {} - /** + /** * @param {module:ol/style/Image} imageStyle Image style. * @param {module:ol/render/canvas~DeclutterGroup=} opt_declutterGroup Declutter. */ - setImageStyle(imageStyle, opt_declutterGroup) {} + setImageStyle(imageStyle, opt_declutterGroup) {} - /** + /** * @param {module:ol/style/Text} textStyle Text style. * @param {module:ol/render/canvas~DeclutterGroup=} opt_declutterGroup Declutter. */ - setTextStyle(textStyle, opt_declutterGroup) {} + setTextStyle(textStyle, opt_declutterGroup) {} } export default VectorContext; diff --git a/src/ol/render/canvas/ImageReplay.js b/src/ol/render/canvas/ImageReplay.js index e39e7009c4..8d6f325637 100644 --- a/src/ol/render/canvas/ImageReplay.js +++ b/src/ol/render/canvas/ImageReplay.js @@ -17,97 +17,97 @@ import CanvasReplay from '../canvas/Replay.js'; * @struct */ class CanvasImageReplay { - constructor(tolerance, maxExtent, resolution, pixelRatio, overlaps, declutterTree) { - CanvasReplay.call(this, - tolerance, maxExtent, resolution, pixelRatio, overlaps, declutterTree); + constructor(tolerance, maxExtent, resolution, pixelRatio, overlaps, declutterTree) { + CanvasReplay.call(this, + tolerance, maxExtent, resolution, pixelRatio, overlaps, declutterTree); - /** + /** * @private * @type {module:ol/render/canvas~DeclutterGroup} */ - this.declutterGroup_ = null; + this.declutterGroup_ = null; - /** + /** * @private * @type {HTMLCanvasElement|HTMLVideoElement|HTMLImageElement} */ - this.hitDetectionImage_ = null; + this.hitDetectionImage_ = null; - /** + /** * @private * @type {HTMLCanvasElement|HTMLVideoElement|HTMLImageElement} */ - this.image_ = null; + this.image_ = null; - /** + /** * @private * @type {number|undefined} */ - this.anchorX_ = undefined; + this.anchorX_ = undefined; - /** + /** * @private * @type {number|undefined} */ - this.anchorY_ = undefined; + this.anchorY_ = undefined; - /** + /** * @private * @type {number|undefined} */ - this.height_ = undefined; + this.height_ = undefined; - /** + /** * @private * @type {number|undefined} */ - this.opacity_ = undefined; + this.opacity_ = undefined; - /** + /** * @private * @type {number|undefined} */ - this.originX_ = undefined; + this.originX_ = undefined; - /** + /** * @private * @type {number|undefined} */ - this.originY_ = undefined; + this.originY_ = undefined; - /** + /** * @private * @type {boolean|undefined} */ - this.rotateWithView_ = undefined; + this.rotateWithView_ = undefined; - /** + /** * @private * @type {number|undefined} */ - this.rotation_ = undefined; + this.rotation_ = undefined; - /** + /** * @private * @type {number|undefined} */ - this.scale_ = undefined; + this.scale_ = undefined; - /** + /** * @private * @type {boolean|undefined} */ - this.snapToPixel_ = undefined; + this.snapToPixel_ = undefined; - /** + /** * @private * @type {number|undefined} */ - this.width_ = undefined; + this.width_ = undefined; - } + } - /** + /** * @param {Array.} flatCoordinates Flat coordinates. * @param {number} offset Offset. * @param {number} end End. @@ -115,114 +115,114 @@ class CanvasImageReplay { * @private * @return {number} My end. */ - drawCoordinates_(flatCoordinates, offset, end, stride) { - return this.appendFlatCoordinates(flatCoordinates, offset, end, stride, false, false); - } + drawCoordinates_(flatCoordinates, offset, end, stride) { + return this.appendFlatCoordinates(flatCoordinates, offset, end, stride, false, false); + } - /** + /** * @inheritDoc */ - drawPoint(pointGeometry, feature) { - if (!this.image_) { - return; - } - this.beginGeometry(pointGeometry, feature); - const flatCoordinates = pointGeometry.getFlatCoordinates(); - const stride = pointGeometry.getStride(); - const myBegin = this.coordinates.length; - const myEnd = this.drawCoordinates_(flatCoordinates, 0, flatCoordinates.length, stride); - this.instructions.push([ - CanvasInstruction.DRAW_IMAGE, myBegin, myEnd, this.image_, - // Remaining arguments to DRAW_IMAGE are in alphabetical order - this.anchorX_, this.anchorY_, this.declutterGroup_, this.height_, this.opacity_, - this.originX_, this.originY_, this.rotateWithView_, this.rotation_, - this.scale_ * this.pixelRatio, this.snapToPixel_, this.width_ - ]); - this.hitDetectionInstructions.push([ - CanvasInstruction.DRAW_IMAGE, myBegin, myEnd, this.hitDetectionImage_, - // Remaining arguments to DRAW_IMAGE are in alphabetical order - this.anchorX_, this.anchorY_, this.declutterGroup_, this.height_, this.opacity_, - this.originX_, this.originY_, this.rotateWithView_, this.rotation_, - this.scale_, this.snapToPixel_, this.width_ - ]); - this.endGeometry(pointGeometry, feature); - } + drawPoint(pointGeometry, feature) { + if (!this.image_) { + return; + } + this.beginGeometry(pointGeometry, feature); + const flatCoordinates = pointGeometry.getFlatCoordinates(); + const stride = pointGeometry.getStride(); + const myBegin = this.coordinates.length; + const myEnd = this.drawCoordinates_(flatCoordinates, 0, flatCoordinates.length, stride); + this.instructions.push([ + CanvasInstruction.DRAW_IMAGE, myBegin, myEnd, this.image_, + // Remaining arguments to DRAW_IMAGE are in alphabetical order + this.anchorX_, this.anchorY_, this.declutterGroup_, this.height_, this.opacity_, + this.originX_, this.originY_, this.rotateWithView_, this.rotation_, + this.scale_ * this.pixelRatio, this.snapToPixel_, this.width_ + ]); + this.hitDetectionInstructions.push([ + CanvasInstruction.DRAW_IMAGE, myBegin, myEnd, this.hitDetectionImage_, + // Remaining arguments to DRAW_IMAGE are in alphabetical order + this.anchorX_, this.anchorY_, this.declutterGroup_, this.height_, this.opacity_, + this.originX_, this.originY_, this.rotateWithView_, this.rotation_, + this.scale_, this.snapToPixel_, this.width_ + ]); + this.endGeometry(pointGeometry, feature); + } - /** + /** * @inheritDoc */ - drawMultiPoint(multiPointGeometry, feature) { - if (!this.image_) { - return; - } - this.beginGeometry(multiPointGeometry, feature); - const flatCoordinates = multiPointGeometry.getFlatCoordinates(); - const stride = multiPointGeometry.getStride(); - const myBegin = this.coordinates.length; - const myEnd = this.drawCoordinates_( - flatCoordinates, 0, flatCoordinates.length, stride); - this.instructions.push([ - CanvasInstruction.DRAW_IMAGE, myBegin, myEnd, this.image_, - // Remaining arguments to DRAW_IMAGE are in alphabetical order - this.anchorX_, this.anchorY_, this.declutterGroup_, this.height_, this.opacity_, - this.originX_, this.originY_, this.rotateWithView_, this.rotation_, - this.scale_ * this.pixelRatio, this.snapToPixel_, this.width_ - ]); - this.hitDetectionInstructions.push([ - CanvasInstruction.DRAW_IMAGE, myBegin, myEnd, this.hitDetectionImage_, - // Remaining arguments to DRAW_IMAGE are in alphabetical order - this.anchorX_, this.anchorY_, this.declutterGroup_, this.height_, this.opacity_, - this.originX_, this.originY_, this.rotateWithView_, this.rotation_, - this.scale_, this.snapToPixel_, this.width_ - ]); - this.endGeometry(multiPointGeometry, feature); - } + drawMultiPoint(multiPointGeometry, feature) { + if (!this.image_) { + return; + } + this.beginGeometry(multiPointGeometry, feature); + const flatCoordinates = multiPointGeometry.getFlatCoordinates(); + const stride = multiPointGeometry.getStride(); + const myBegin = this.coordinates.length; + const myEnd = this.drawCoordinates_( + flatCoordinates, 0, flatCoordinates.length, stride); + this.instructions.push([ + CanvasInstruction.DRAW_IMAGE, myBegin, myEnd, this.image_, + // Remaining arguments to DRAW_IMAGE are in alphabetical order + this.anchorX_, this.anchorY_, this.declutterGroup_, this.height_, this.opacity_, + this.originX_, this.originY_, this.rotateWithView_, this.rotation_, + this.scale_ * this.pixelRatio, this.snapToPixel_, this.width_ + ]); + this.hitDetectionInstructions.push([ + CanvasInstruction.DRAW_IMAGE, myBegin, myEnd, this.hitDetectionImage_, + // Remaining arguments to DRAW_IMAGE are in alphabetical order + this.anchorX_, this.anchorY_, this.declutterGroup_, this.height_, this.opacity_, + this.originX_, this.originY_, this.rotateWithView_, this.rotation_, + this.scale_, this.snapToPixel_, this.width_ + ]); + this.endGeometry(multiPointGeometry, feature); + } - /** + /** * @inheritDoc */ - finish() { - this.reverseHitDetectionInstructions(); - // FIXME this doesn't really protect us against further calls to draw*Geometry - this.anchorX_ = undefined; - this.anchorY_ = undefined; - this.hitDetectionImage_ = null; - this.image_ = null; - this.height_ = undefined; - this.scale_ = undefined; - this.opacity_ = undefined; - this.originX_ = undefined; - this.originY_ = undefined; - this.rotateWithView_ = undefined; - this.rotation_ = undefined; - this.snapToPixel_ = undefined; - this.width_ = undefined; - } + finish() { + this.reverseHitDetectionInstructions(); + // FIXME this doesn't really protect us against further calls to draw*Geometry + this.anchorX_ = undefined; + this.anchorY_ = undefined; + this.hitDetectionImage_ = null; + this.image_ = null; + this.height_ = undefined; + this.scale_ = undefined; + this.opacity_ = undefined; + this.originX_ = undefined; + this.originY_ = undefined; + this.rotateWithView_ = undefined; + this.rotation_ = undefined; + this.snapToPixel_ = undefined; + this.width_ = undefined; + } - /** + /** * @inheritDoc */ - setImageStyle(imageStyle, declutterGroup) { - const anchor = imageStyle.getAnchor(); - const size = imageStyle.getSize(); - const hitDetectionImage = imageStyle.getHitDetectionImage(1); - const image = imageStyle.getImage(1); - const origin = imageStyle.getOrigin(); - this.anchorX_ = anchor[0]; - this.anchorY_ = anchor[1]; - this.declutterGroup_ = /** @type {module:ol/render/canvas~DeclutterGroup} */ (declutterGroup); - this.hitDetectionImage_ = hitDetectionImage; - this.image_ = image; - this.height_ = size[1]; - this.opacity_ = imageStyle.getOpacity(); - this.originX_ = origin[0]; - this.originY_ = origin[1]; - this.rotateWithView_ = imageStyle.getRotateWithView(); - this.rotation_ = imageStyle.getRotation(); - this.scale_ = imageStyle.getScale(); - this.snapToPixel_ = imageStyle.getSnapToPixel(); - this.width_ = size[0]; - } + setImageStyle(imageStyle, declutterGroup) { + const anchor = imageStyle.getAnchor(); + const size = imageStyle.getSize(); + const hitDetectionImage = imageStyle.getHitDetectionImage(1); + const image = imageStyle.getImage(1); + const origin = imageStyle.getOrigin(); + this.anchorX_ = anchor[0]; + this.anchorY_ = anchor[1]; + this.declutterGroup_ = /** @type {module:ol/render/canvas~DeclutterGroup} */ (declutterGroup); + this.hitDetectionImage_ = hitDetectionImage; + this.image_ = image; + this.height_ = size[1]; + this.opacity_ = imageStyle.getOpacity(); + this.originX_ = origin[0]; + this.originY_ = origin[1]; + this.rotateWithView_ = imageStyle.getRotateWithView(); + this.rotation_ = imageStyle.getRotation(); + this.scale_ = imageStyle.getScale(); + this.snapToPixel_ = imageStyle.getSnapToPixel(); + this.width_ = size[0]; + } } inherits(CanvasImageReplay, CanvasReplay); diff --git a/src/ol/render/canvas/Replay.js b/src/ol/render/canvas/Replay.js index 398b34bbda..65462a7118 100644 --- a/src/ol/render/canvas/Replay.js +++ b/src/ol/render/canvas/Replay.js @@ -28,6 +28,19 @@ import { setFromArray as transformSetFromArray } from '../../transform.js'; + +/** + * @type {module:ol/extent~Extent} + */ +const tmpExtent = createEmpty(); + + +/** + * @type {!module:ol/transform~Transform} + */ +const tmpTransform = createTransform(); + + /** * @constructor * @extends {module:ol/render/VectorContext} @@ -1072,18 +1085,6 @@ class CanvasReplay { inherits(CanvasReplay, VectorContext); -/** - * @type {module:ol/extent~Extent} - */ -const tmpExtent = createEmpty(); - - -/** - * @type {!module:ol/transform~Transform} - */ -const tmpTransform = createTransform(); - - /** * FIXME empty description for jsdoc */ diff --git a/src/ol/render/webgl/Replay.js b/src/ol/render/webgl/Replay.js index 9f82854f69..f3c6b83f7c 100644 --- a/src/ol/render/webgl/Replay.js +++ b/src/ol/render/webgl/Replay.js @@ -24,23 +24,23 @@ import {ARRAY_BUFFER, ELEMENT_ARRAY_BUFFER, TRIANGLES, * @struct */ class WebGLReplay { - constructor(tolerance, maxExtent) { - VectorContext.call(this); + constructor(tolerance, maxExtent) { + VectorContext.call(this); - /** + /** * @protected * @type {number} */ - this.tolerance = tolerance; + this.tolerance = tolerance; - /** + /** * @protected * @const * @type {module:ol/extent~Extent} */ - this.maxExtent = maxExtent; + this.maxExtent = maxExtent; - /** + /** * The origin of the coordinate system for the point coordinates sent to * the GPU. To eliminate jitter caused by precision problems in the GPU * we use the "Rendering Relative to Eye" technique described in the "3D @@ -48,93 +48,93 @@ class WebGLReplay { * @protected * @type {module:ol/coordinate~Coordinate} */ - this.origin = getCenter(maxExtent); + this.origin = getCenter(maxExtent); - /** + /** * @private * @type {module:ol/transform~Transform} */ - this.projectionMatrix_ = createTransform(); + this.projectionMatrix_ = createTransform(); - /** + /** * @private * @type {module:ol/transform~Transform} */ - this.offsetRotateMatrix_ = createTransform(); + this.offsetRotateMatrix_ = createTransform(); - /** + /** * @private * @type {module:ol/transform~Transform} */ - this.offsetScaleMatrix_ = createTransform(); + this.offsetScaleMatrix_ = createTransform(); - /** + /** * @private * @type {Array.} */ - this.tmpMat4_ = create(); + this.tmpMat4_ = create(); - /** + /** * @protected * @type {Array.} */ - this.indices = []; + this.indices = []; - /** + /** * @protected * @type {?module:ol/webgl/Buffer} */ - this.indicesBuffer = null; + this.indicesBuffer = null; - /** + /** * Start index per feature (the index). * @protected * @type {Array.} */ - this.startIndices = []; + this.startIndices = []; - /** + /** * Start index per feature (the feature). * @protected * @type {Array.} */ - this.startIndicesFeature = []; + this.startIndicesFeature = []; - /** + /** * @protected * @type {Array.} */ - this.vertices = []; + this.vertices = []; - /** + /** * @protected * @type {?module:ol/webgl/Buffer} */ - this.verticesBuffer = null; + this.verticesBuffer = null; - /** + /** * Optional parameter for PolygonReplay instances. * @protected * @type {module:ol/render/webgl/LineStringReplay|undefined} */ - this.lineStringReplay = undefined; + this.lineStringReplay = undefined; - } + } - /** + /** * @abstract * @param {module:ol/webgl/Context} context WebGL context. * @return {function()} Delete resources function. */ - getDeleteResourcesFunction(context) {} + getDeleteResourcesFunction(context) {} - /** + /** * @abstract * @param {module:ol/webgl/Context} context Context. */ - finish(context) {} + finish(context) {} - /** + /** * @abstract * @protected * @param {WebGLRenderingContext} gl gl. @@ -146,9 +146,9 @@ class WebGLReplay { module:ol/render/webgl/polygonreplay/defaultshader/Locations| module:ol/render/webgl/texturereplay/defaultshader/Locations} Locations. */ - setUpProgram(gl, context, size, pixelRatio) {} + setUpProgram(gl, context, size, pixelRatio) {} - /** + /** * @abstract * @protected * @param {WebGLRenderingContext} gl gl. @@ -157,9 +157,9 @@ class WebGLReplay { module:ol/render/webgl/polygonreplay/defaultshader/Locations| module:ol/render/webgl/texturereplay/defaultshader/Locations} locations Locations. */ - shutDownProgram(gl, locations) {} + shutDownProgram(gl, locations) {} - /** + /** * @abstract * @protected * @param {WebGLRenderingContext} gl gl. @@ -167,9 +167,9 @@ class WebGLReplay { * @param {Object.} skippedFeaturesHash Ids of features to skip. * @param {boolean} hitDetection Hit detection mode. */ - drawReplay(gl, context, skippedFeaturesHash, hitDetection) {} + drawReplay(gl, context, skippedFeaturesHash, hitDetection) {} - /** + /** * @abstract * @protected * @param {WebGLRenderingContext} gl gl. @@ -180,9 +180,9 @@ class WebGLReplay { * @return {T|undefined} Callback result. * @template T */ - drawHitDetectionReplayOneByOne(gl, context, skippedFeaturesHash, featureCallback, opt_hitExtent) {} + drawHitDetectionReplayOneByOne(gl, context, skippedFeaturesHash, featureCallback, opt_hitExtent) {} - /** + /** * @protected * @param {WebGLRenderingContext} gl gl. * @param {module:ol/webgl/Context} context Context. @@ -194,19 +194,19 @@ class WebGLReplay { * @return {T|undefined} Callback result. * @template T */ - drawHitDetectionReplay(gl, context, skippedFeaturesHash, featureCallback, oneByOne, opt_hitExtent) { - if (!oneByOne) { - // draw all hit-detection features in "once" (by texture group) - return this.drawHitDetectionReplayAll(gl, context, - skippedFeaturesHash, featureCallback); - } else { - // draw hit-detection features one by one - return this.drawHitDetectionReplayOneByOne(gl, context, - skippedFeaturesHash, featureCallback, opt_hitExtent); - } - } + drawHitDetectionReplay(gl, context, skippedFeaturesHash, featureCallback, oneByOne, opt_hitExtent) { + if (!oneByOne) { + // draw all hit-detection features in "once" (by texture group) + return this.drawHitDetectionReplayAll(gl, context, + skippedFeaturesHash, featureCallback); + } else { + // draw hit-detection features one by one + return this.drawHitDetectionReplayOneByOne(gl, context, + skippedFeaturesHash, featureCallback, opt_hitExtent); + } + } - /** + /** * @protected * @param {WebGLRenderingContext} gl gl. * @param {module:ol/webgl/Context} context Context. @@ -215,19 +215,19 @@ class WebGLReplay { * @return {T|undefined} Callback result. * @template T */ - drawHitDetectionReplayAll(gl, context, skippedFeaturesHash, featureCallback) { - gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); - this.drawReplay(gl, context, skippedFeaturesHash, true); + drawHitDetectionReplayAll(gl, context, skippedFeaturesHash, featureCallback) { + gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); + this.drawReplay(gl, context, skippedFeaturesHash, true); - const result = featureCallback(null); - if (result) { - return result; - } else { - return undefined; - } - } + const result = featureCallback(null); + if (result) { + return result; + } else { + return undefined; + } + } - /** + /** * @param {module:ol/webgl/Context} context Context. * @param {module:ol/coordinate~Coordinate} center Center. * @param {number} resolution Resolution. @@ -243,120 +243,120 @@ class WebGLReplay { * @return {T|undefined} Callback result. * @template T */ - replay( - context, - center, - resolution, - rotation, - size, - pixelRatio, - opacity, - skippedFeaturesHash, - featureCallback, - oneByOne, - opt_hitExtent - ) { - const gl = context.getGL(); - let tmpStencil, tmpStencilFunc, tmpStencilMaskVal, tmpStencilRef, tmpStencilMask, - tmpStencilOpFail, tmpStencilOpPass, tmpStencilOpZFail; + replay( + context, + center, + resolution, + rotation, + size, + pixelRatio, + opacity, + skippedFeaturesHash, + featureCallback, + oneByOne, + opt_hitExtent + ) { + const gl = context.getGL(); + let tmpStencil, tmpStencilFunc, tmpStencilMaskVal, tmpStencilRef, tmpStencilMask, + tmpStencilOpFail, tmpStencilOpPass, tmpStencilOpZFail; - if (this.lineStringReplay) { - tmpStencil = gl.isEnabled(gl.STENCIL_TEST); - tmpStencilFunc = gl.getParameter(gl.STENCIL_FUNC); - tmpStencilMaskVal = gl.getParameter(gl.STENCIL_VALUE_MASK); - tmpStencilRef = gl.getParameter(gl.STENCIL_REF); - tmpStencilMask = gl.getParameter(gl.STENCIL_WRITEMASK); - tmpStencilOpFail = gl.getParameter(gl.STENCIL_FAIL); - tmpStencilOpPass = gl.getParameter(gl.STENCIL_PASS_DEPTH_PASS); - tmpStencilOpZFail = gl.getParameter(gl.STENCIL_PASS_DEPTH_FAIL); + if (this.lineStringReplay) { + tmpStencil = gl.isEnabled(gl.STENCIL_TEST); + tmpStencilFunc = gl.getParameter(gl.STENCIL_FUNC); + tmpStencilMaskVal = gl.getParameter(gl.STENCIL_VALUE_MASK); + tmpStencilRef = gl.getParameter(gl.STENCIL_REF); + tmpStencilMask = gl.getParameter(gl.STENCIL_WRITEMASK); + tmpStencilOpFail = gl.getParameter(gl.STENCIL_FAIL); + tmpStencilOpPass = gl.getParameter(gl.STENCIL_PASS_DEPTH_PASS); + tmpStencilOpZFail = gl.getParameter(gl.STENCIL_PASS_DEPTH_FAIL); - gl.enable(gl.STENCIL_TEST); - gl.clear(gl.STENCIL_BUFFER_BIT); - gl.stencilMask(255); - gl.stencilFunc(gl.ALWAYS, 1, 255); - gl.stencilOp(gl.KEEP, gl.KEEP, gl.REPLACE); + gl.enable(gl.STENCIL_TEST); + gl.clear(gl.STENCIL_BUFFER_BIT); + gl.stencilMask(255); + gl.stencilFunc(gl.ALWAYS, 1, 255); + gl.stencilOp(gl.KEEP, gl.KEEP, gl.REPLACE); - this.lineStringReplay.replay(context, - center, resolution, rotation, size, pixelRatio, - opacity, skippedFeaturesHash, - featureCallback, oneByOne, opt_hitExtent); + this.lineStringReplay.replay(context, + center, resolution, rotation, size, pixelRatio, + opacity, skippedFeaturesHash, + featureCallback, oneByOne, opt_hitExtent); - gl.stencilMask(0); - gl.stencilFunc(gl.NOTEQUAL, 1, 255); - } + gl.stencilMask(0); + gl.stencilFunc(gl.NOTEQUAL, 1, 255); + } - context.bindBuffer(ARRAY_BUFFER, this.verticesBuffer); + context.bindBuffer(ARRAY_BUFFER, this.verticesBuffer); - context.bindBuffer(ELEMENT_ARRAY_BUFFER, this.indicesBuffer); + context.bindBuffer(ELEMENT_ARRAY_BUFFER, this.indicesBuffer); - const locations = this.setUpProgram(gl, context, size, pixelRatio); + const locations = this.setUpProgram(gl, context, size, pixelRatio); - // set the "uniform" values - const projectionMatrix = resetTransform(this.projectionMatrix_); - scaleTransform(projectionMatrix, 2 / (resolution * size[0]), 2 / (resolution * size[1])); - rotateTransform(projectionMatrix, -rotation); - translateTransform(projectionMatrix, -(center[0] - this.origin[0]), -(center[1] - this.origin[1])); + // set the "uniform" values + const projectionMatrix = resetTransform(this.projectionMatrix_); + scaleTransform(projectionMatrix, 2 / (resolution * size[0]), 2 / (resolution * size[1])); + rotateTransform(projectionMatrix, -rotation); + translateTransform(projectionMatrix, -(center[0] - this.origin[0]), -(center[1] - this.origin[1])); - const offsetScaleMatrix = resetTransform(this.offsetScaleMatrix_); - scaleTransform(offsetScaleMatrix, 2 / size[0], 2 / size[1]); + const offsetScaleMatrix = resetTransform(this.offsetScaleMatrix_); + scaleTransform(offsetScaleMatrix, 2 / size[0], 2 / size[1]); - const offsetRotateMatrix = resetTransform(this.offsetRotateMatrix_); - if (rotation !== 0) { - rotateTransform(offsetRotateMatrix, -rotation); - } + const offsetRotateMatrix = resetTransform(this.offsetRotateMatrix_); + if (rotation !== 0) { + rotateTransform(offsetRotateMatrix, -rotation); + } - gl.uniformMatrix4fv(locations.u_projectionMatrix, false, - fromTransform(this.tmpMat4_, projectionMatrix)); - gl.uniformMatrix4fv(locations.u_offsetScaleMatrix, false, - fromTransform(this.tmpMat4_, offsetScaleMatrix)); - gl.uniformMatrix4fv(locations.u_offsetRotateMatrix, false, - fromTransform(this.tmpMat4_, offsetRotateMatrix)); - gl.uniform1f(locations.u_opacity, opacity); + gl.uniformMatrix4fv(locations.u_projectionMatrix, false, + fromTransform(this.tmpMat4_, projectionMatrix)); + gl.uniformMatrix4fv(locations.u_offsetScaleMatrix, false, + fromTransform(this.tmpMat4_, offsetScaleMatrix)); + gl.uniformMatrix4fv(locations.u_offsetRotateMatrix, false, + fromTransform(this.tmpMat4_, offsetRotateMatrix)); + gl.uniform1f(locations.u_opacity, opacity); - // draw! - let result; - if (featureCallback === undefined) { - this.drawReplay(gl, context, skippedFeaturesHash, false); - } else { - // draw feature by feature for the hit-detection - result = this.drawHitDetectionReplay(gl, context, skippedFeaturesHash, - featureCallback, oneByOne, opt_hitExtent); - } + // draw! + let result; + if (featureCallback === undefined) { + this.drawReplay(gl, context, skippedFeaturesHash, false); + } else { + // draw feature by feature for the hit-detection + result = this.drawHitDetectionReplay(gl, context, skippedFeaturesHash, + featureCallback, oneByOne, opt_hitExtent); + } - // disable the vertex attrib arrays - this.shutDownProgram(gl, locations); + // disable the vertex attrib arrays + this.shutDownProgram(gl, locations); - if (this.lineStringReplay) { - if (!tmpStencil) { - gl.disable(gl.STENCIL_TEST); - } - gl.clear(gl.STENCIL_BUFFER_BIT); - gl.stencilFunc(/** @type {number} */ (tmpStencilFunc), - /** @type {number} */ (tmpStencilRef), /** @type {number} */ (tmpStencilMaskVal)); - gl.stencilMask(/** @type {number} */ (tmpStencilMask)); - gl.stencilOp(/** @type {number} */ (tmpStencilOpFail), - /** @type {number} */ (tmpStencilOpZFail), /** @type {number} */ (tmpStencilOpPass)); - } + if (this.lineStringReplay) { + if (!tmpStencil) { + gl.disable(gl.STENCIL_TEST); + } + gl.clear(gl.STENCIL_BUFFER_BIT); + gl.stencilFunc(/** @type {number} */ (tmpStencilFunc), + /** @type {number} */ (tmpStencilRef), /** @type {number} */ (tmpStencilMaskVal)); + gl.stencilMask(/** @type {number} */ (tmpStencilMask)); + gl.stencilOp(/** @type {number} */ (tmpStencilOpFail), + /** @type {number} */ (tmpStencilOpZFail), /** @type {number} */ (tmpStencilOpPass)); + } - return result; - } + return result; + } - /** + /** * @protected * @param {WebGLRenderingContext} gl gl. * @param {module:ol/webgl/Context} context Context. * @param {number} start Start index. * @param {number} end End index. */ - drawElements(gl, context, start, end) { - const elementType = context.hasOESElementIndexUint ? - UNSIGNED_INT : UNSIGNED_SHORT; - const elementSize = context.hasOESElementIndexUint ? 4 : 2; + drawElements(gl, context, start, end) { + const elementType = context.hasOESElementIndexUint ? + UNSIGNED_INT : UNSIGNED_SHORT; + const elementSize = context.hasOESElementIndexUint ? 4 : 2; - const numItems = end - start; - const offsetInBytes = start * elementSize; - gl.drawElements(TRIANGLES, numItems, elementType, offsetInBytes); - } + const numItems = end - start; + const offsetInBytes = start * elementSize; + gl.drawElements(TRIANGLES, numItems, elementType, offsetInBytes); + } } inherits(WebGLReplay, VectorContext); diff --git a/src/ol/source/BingMaps.js b/src/ol/source/BingMaps.js index 928befb475..aa86003fd3 100644 --- a/src/ol/source/BingMaps.js +++ b/src/ol/source/BingMaps.js @@ -11,6 +11,18 @@ import TileImage from '../source/TileImage.js'; import {createOrUpdate, quadKey} from '../tilecoord.js'; import {createXYZ, extentFromProjection} from '../tilegrid.js'; + +/** + * The attribution containing a link to the Microsoft® Bingâ„¢ Maps Platform APIs’ + * Terms Of Use. + * @const + * @type {string} + */ +const TOS_ATTRIBUTION = '' + + 'Terms of Use'; + + /** * @typedef {Object} Options * @property {number} [cacheSize=2048] Cache size. @@ -213,16 +225,4 @@ class BingMaps { inherits(BingMaps, TileImage); - -/** - * The attribution containing a link to the Microsoft® Bingâ„¢ Maps Platform APIs’ - * Terms Of Use. - * @const - * @type {string} - */ -const TOS_ATTRIBUTION = '' + - 'Terms of Use'; - - export default BingMaps; diff --git a/src/ol/source/ImageCanvas.js b/src/ol/source/ImageCanvas.js index 4080b6fe06..ab61528c3f 100644 --- a/src/ol/source/ImageCanvas.js +++ b/src/ol/source/ImageCanvas.js @@ -52,73 +52,73 @@ import ImageSource from '../source/Image.js'; * @api */ class ImageCanvasSource { - constructor(options) { + constructor(options) { - ImageSource.call(this, { - attributions: options.attributions, - projection: options.projection, - resolutions: options.resolutions, - state: options.state - }); + ImageSource.call(this, { + attributions: options.attributions, + projection: options.projection, + resolutions: options.resolutions, + state: options.state + }); - /** + /** * @private * @type {module:ol/source/ImageCanvas~FunctionType} */ - this.canvasFunction_ = options.canvasFunction; + this.canvasFunction_ = options.canvasFunction; - /** + /** * @private * @type {module:ol/ImageCanvas} */ - this.canvas_ = null; + this.canvas_ = null; - /** + /** * @private * @type {number} */ - this.renderedRevision_ = 0; + this.renderedRevision_ = 0; - /** + /** * @private * @type {number} */ - this.ratio_ = options.ratio !== undefined ? - options.ratio : 1.5; + this.ratio_ = options.ratio !== undefined ? + options.ratio : 1.5; - } + } - /** + /** * @inheritDoc */ - getImageInternal(extent, resolution, pixelRatio, projection) { - resolution = this.findNearestResolution(resolution); + getImageInternal(extent, resolution, pixelRatio, projection) { + resolution = this.findNearestResolution(resolution); - let canvas = this.canvas_; - if (canvas && + let canvas = this.canvas_; + if (canvas && this.renderedRevision_ == this.getRevision() && canvas.getResolution() == resolution && canvas.getPixelRatio() == pixelRatio && containsExtent(canvas.getExtent(), extent)) { - return canvas; - } + return canvas; + } - extent = extent.slice(); - scaleFromCenter(extent, this.ratio_); - const width = getWidth(extent) / resolution; - const height = getHeight(extent) / resolution; - const size = [width * pixelRatio, height * pixelRatio]; + extent = extent.slice(); + scaleFromCenter(extent, this.ratio_); + const width = getWidth(extent) / resolution; + const height = getHeight(extent) / resolution; + const size = [width * pixelRatio, height * pixelRatio]; - const canvasElement = this.canvasFunction_( - extent, resolution, pixelRatio, size, projection); - if (canvasElement) { - canvas = new ImageCanvas(extent, resolution, pixelRatio, canvasElement); - } - this.canvas_ = canvas; - this.renderedRevision_ = this.getRevision(); + const canvasElement = this.canvasFunction_( + extent, resolution, pixelRatio, size, projection); + if (canvasElement) { + canvas = new ImageCanvas(extent, resolution, pixelRatio, canvasElement); + } + this.canvas_ = canvas; + this.renderedRevision_ = this.getRevision(); - return canvas; - } + return canvas; + } } inherits(ImageCanvasSource, ImageSource); diff --git a/src/ol/source/ImageWMS.js b/src/ol/source/ImageWMS.js index 963f66fda8..133ea2f8d7 100644 --- a/src/ol/source/ImageWMS.js +++ b/src/ol/source/ImageWMS.js @@ -17,6 +17,14 @@ import WMSServerType from '../source/WMSServerType.js'; import {compareVersions} from '../string.js'; import {appendParams} from '../uri.js'; + +/** + * @const + * @type {module:ol/size~Size} + */ +const GETFEATUREINFO_IMAGE_SIZE = [101, 101]; + + /** * @typedef {Object} Options * @property {module:ol/source/Source~AttributionLike} [attributions] Attributions. @@ -380,11 +388,4 @@ class ImageWMS { inherits(ImageWMS, ImageSource); -/** - * @const - * @type {module:ol/size~Size} - */ -const GETFEATUREINFO_IMAGE_SIZE = [101, 101]; - - export default ImageWMS; diff --git a/src/ol/source/Source.js b/src/ol/source/Source.js index c16e41a232..7356008f40 100644 --- a/src/ol/source/Source.js +++ b/src/ol/source/Source.js @@ -52,129 +52,129 @@ import SourceState from '../source/State.js'; * @api */ class Source { - constructor(options) { + constructor(options) { - BaseObject.call(this); + BaseObject.call(this); - /** + /** * @private * @type {module:ol/proj/Projection} */ - this.projection_ = getProjection(options.projection); + this.projection_ = getProjection(options.projection); - /** + /** * @private * @type {?module:ol/source/Source~Attribution} */ - this.attributions_ = this.adaptAttributions_(options.attributions); + this.attributions_ = this.adaptAttributions_(options.attributions); - /** + /** * @private * @type {module:ol/source/State} */ - this.state_ = options.state !== undefined ? - options.state : SourceState.READY; + this.state_ = options.state !== undefined ? + options.state : SourceState.READY; - /** + /** * @private * @type {boolean} */ - this.wrapX_ = options.wrapX !== undefined ? options.wrapX : false; + this.wrapX_ = options.wrapX !== undefined ? options.wrapX : false; - } + } - /** + /** * Turns the attributions option into an attributions function. * @param {module:ol/source/Source~AttributionLike|undefined} attributionLike The attribution option. * @return {?module:ol/source/Source~Attribution} An attribution function (or null). */ - adaptAttributions_(attributionLike) { - if (!attributionLike) { - return null; - } - if (Array.isArray(attributionLike)) { - return function(frameState) { - return attributionLike; - }; - } + adaptAttributions_(attributionLike) { + if (!attributionLike) { + return null; + } + if (Array.isArray(attributionLike)) { + return function(frameState) { + return attributionLike; + }; + } - if (typeof attributionLike === 'function') { - return attributionLike; - } + if (typeof attributionLike === 'function') { + return attributionLike; + } - return function(frameState) { - return [attributionLike]; - }; - } + return function(frameState) { + return [attributionLike]; + }; + } - /** + /** * Get the attribution function for the source. * @return {?module:ol/source/Source~Attribution} Attribution function. */ - getAttributions() { - return this.attributions_; - } + getAttributions() { + return this.attributions_; + } - /** + /** * Get the projection of the source. * @return {module:ol/proj/Projection} Projection. * @api */ - getProjection() { - return this.projection_; - } + getProjection() { + return this.projection_; + } - /** + /** * @abstract * @return {Array.|undefined} Resolutions. */ - getResolutions() {} + getResolutions() {} - /** + /** * Get the state of the source, see {@link module:ol/source/State~State} for possible states. * @return {module:ol/source/State} State. * @api */ - getState() { - return this.state_; - } + getState() { + return this.state_; + } - /** + /** * @return {boolean|undefined} Wrap X. */ - getWrapX() { - return this.wrapX_; - } + getWrapX() { + return this.wrapX_; + } - /** + /** * Refreshes the source and finally dispatches a 'change' event. * @api */ - refresh() { - this.changed(); - } + refresh() { + this.changed(); + } - /** + /** * Set the attributions of the source. * @param {module:ol/source/Source~AttributionLike|undefined} attributions Attributions. * Can be passed as `string`, `Array`, `{@link module:ol/source/Source~Attribution}`, * or `undefined`. * @api */ - setAttributions(attributions) { - this.attributions_ = this.adaptAttributions_(attributions); - this.changed(); - } + setAttributions(attributions) { + this.attributions_ = this.adaptAttributions_(attributions); + this.changed(); + } - /** + /** * Set the state of the source. * @param {module:ol/source/State} state State. * @protected */ - setState(state) { - this.state_ = state; - this.changed(); - } + setState(state) { + this.state_ = state; + this.changed(); + } } inherits(Source, BaseObject); diff --git a/src/ol/source/TileDebug.js b/src/ol/source/TileDebug.js index 20eefc9d78..93e30bd1d4 100644 --- a/src/ol/source/TileDebug.js +++ b/src/ol/source/TileDebug.js @@ -18,59 +18,59 @@ import {getKeyZXY} from '../tilecoord.js'; * @param {string} text Text. */ class LabeledTile { - constructor(tileCoord, tileSize, text) { + constructor(tileCoord, tileSize, text) { - Tile.call(this, tileCoord, TileState.LOADED); + Tile.call(this, tileCoord, TileState.LOADED); - /** + /** * @private * @type {module:ol/size~Size} */ - this.tileSize_ = tileSize; + this.tileSize_ = tileSize; - /** + /** * @private * @type {string} */ - this.text_ = text; + this.text_ = text; - /** + /** * @private * @type {HTMLCanvasElement} */ - this.canvas_ = null; + this.canvas_ = null; - } + } - /** + /** * Get the image element for this tile. * @return {HTMLCanvasElement} Image. */ - getImage() { - if (this.canvas_) { - return this.canvas_; - } else { - const tileSize = this.tileSize_; - const context = createCanvasContext2D(tileSize[0], tileSize[1]); + getImage() { + if (this.canvas_) { + return this.canvas_; + } else { + const tileSize = this.tileSize_; + const context = createCanvasContext2D(tileSize[0], tileSize[1]); - context.strokeStyle = 'black'; - context.strokeRect(0.5, 0.5, tileSize[0] + 0.5, tileSize[1] + 0.5); + context.strokeStyle = 'black'; + context.strokeRect(0.5, 0.5, tileSize[0] + 0.5, tileSize[1] + 0.5); - context.fillStyle = 'black'; - context.textAlign = 'center'; - context.textBaseline = 'middle'; - context.font = '24px sans-serif'; - context.fillText(this.text_, tileSize[0] / 2, tileSize[1] / 2); + context.fillStyle = 'black'; + context.textAlign = 'center'; + context.textBaseline = 'middle'; + context.font = '24px sans-serif'; + context.fillText(this.text_, tileSize[0] / 2, tileSize[1] / 2); - this.canvas_ = context.canvas; - return context.canvas; - } - } + this.canvas_ = context.canvas; + return context.canvas; + } + } - /** + /** * @override */ - load() {} + load() {} } inherits(LabeledTile, Tile); @@ -98,35 +98,35 @@ inherits(LabeledTile, Tile); * @api */ class TileDebug { - constructor(options) { + constructor(options) { - TileSource.call(this, { - opaque: false, - projection: options.projection, - tileGrid: options.tileGrid, - wrapX: options.wrapX !== undefined ? options.wrapX : true - }); + TileSource.call(this, { + opaque: false, + projection: options.projection, + tileGrid: options.tileGrid, + wrapX: options.wrapX !== undefined ? options.wrapX : true + }); - } + } - /** + /** * @inheritDoc */ - getTile(z, x, y) { - const tileCoordKey = getKeyZXY(z, x, y); - if (this.tileCache.containsKey(tileCoordKey)) { - return /** @type {!module:ol/source/TileDebug~LabeledTile} */ (this.tileCache.get(tileCoordKey)); - } else { - const tileSize = toSize(this.tileGrid.getTileSize(z)); - const tileCoord = [z, x, y]; - const textTileCoord = this.getTileCoordForTileUrlFunction(tileCoord); - const text = !textTileCoord ? '' : - this.getTileCoordForTileUrlFunction(textTileCoord).toString(); - const tile = new LabeledTile(tileCoord, tileSize, text); - this.tileCache.set(tileCoordKey, tile); - return tile; - } - } + getTile(z, x, y) { + const tileCoordKey = getKeyZXY(z, x, y); + if (this.tileCache.containsKey(tileCoordKey)) { + return /** @type {!module:ol/source/TileDebug~LabeledTile} */ (this.tileCache.get(tileCoordKey)); + } else { + const tileSize = toSize(this.tileGrid.getTileSize(z)); + const tileCoord = [z, x, y]; + const textTileCoord = this.getTileCoordForTileUrlFunction(tileCoord); + const text = !textTileCoord ? '' : + this.getTileCoordForTileUrlFunction(textTileCoord).toString(); + const tile = new LabeledTile(tileCoord, tileSize, text); + this.tileCache.set(tileCoordKey, tile); + return tile; + } + } } inherits(TileDebug, TileSource); diff --git a/src/ol/style/Circle.js b/src/ol/style/Circle.js index 0340d9c1fa..5851760cb7 100644 --- a/src/ol/style/Circle.js +++ b/src/ol/style/Circle.js @@ -30,50 +30,50 @@ import RegularShape from '../style/RegularShape.js'; * @api */ class CircleStyle { - constructor(opt_options) { + constructor(opt_options) { - const options = opt_options || {}; + const options = opt_options || {}; - RegularShape.call(this, { - points: Infinity, - fill: options.fill, - radius: options.radius, - snapToPixel: options.snapToPixel, - stroke: options.stroke, - atlasManager: options.atlasManager - }); + RegularShape.call(this, { + points: Infinity, + fill: options.fill, + radius: options.radius, + snapToPixel: options.snapToPixel, + stroke: options.stroke, + atlasManager: options.atlasManager + }); - } + } - /** + /** * Clones the style. If an atlasmanager was provided to the original style it will be used in the cloned style, too. * @return {module:ol/style/Circle} The cloned style. * @override * @api */ - clone() { - const style = new CircleStyle({ - fill: this.getFill() ? this.getFill().clone() : undefined, - stroke: this.getStroke() ? this.getStroke().clone() : undefined, - radius: this.getRadius(), - snapToPixel: this.getSnapToPixel(), - atlasManager: this.atlasManager_ - }); - style.setOpacity(this.getOpacity()); - style.setScale(this.getScale()); - return style; - } + clone() { + const style = new CircleStyle({ + fill: this.getFill() ? this.getFill().clone() : undefined, + stroke: this.getStroke() ? this.getStroke().clone() : undefined, + radius: this.getRadius(), + snapToPixel: this.getSnapToPixel(), + atlasManager: this.atlasManager_ + }); + style.setOpacity(this.getOpacity()); + style.setScale(this.getScale()); + return style; + } - /** + /** * Set the circle radius. * * @param {number} radius Circle radius. * @api */ - setRadius(radius) { - this.radius_ = radius; - this.render_(this.atlasManager_); - } + setRadius(radius) { + this.radius_ = radius; + this.render_(this.atlasManager_); + } } inherits(CircleStyle, RegularShape); diff --git a/src/ol/style/IconImageCache.js b/src/ol/style/IconImageCache.js index 882c100272..311798d8d8 100644 --- a/src/ol/style/IconImageCache.js +++ b/src/ol/style/IconImageCache.js @@ -8,85 +8,85 @@ import {asString} from '../color.js'; * @constructor */ class IconImageCache { - constructor() { + constructor() { - /** + /** * @type {!Object.} * @private */ - this.cache_ = {}; + this.cache_ = {}; - /** + /** * @type {number} * @private */ - this.cacheSize_ = 0; + this.cacheSize_ = 0; - /** + /** * @type {number} * @private */ - this.maxCacheSize_ = 32; - } + this.maxCacheSize_ = 32; + } - /** + /** * FIXME empty description for jsdoc */ - clear() { - this.cache_ = {}; - this.cacheSize_ = 0; - } + clear() { + this.cache_ = {}; + this.cacheSize_ = 0; + } - /** + /** * FIXME empty description for jsdoc */ - expire() { - if (this.cacheSize_ > this.maxCacheSize_) { - let i = 0; - for (const key in this.cache_) { - const iconImage = this.cache_[key]; - if ((i++ & 3) === 0 && !iconImage.hasListener()) { - delete this.cache_[key]; - --this.cacheSize_; - } - } - } - } + expire() { + if (this.cacheSize_ > this.maxCacheSize_) { + let i = 0; + for (const key in this.cache_) { + const iconImage = this.cache_[key]; + if ((i++ & 3) === 0 && !iconImage.hasListener()) { + delete this.cache_[key]; + --this.cacheSize_; + } + } + } + } - /** + /** * @param {string} src Src. * @param {?string} crossOrigin Cross origin. * @param {module:ol/color~Color} color Color. * @return {module:ol/style/IconImage} Icon image. */ - get(src, crossOrigin, color) { - const key = getKey(src, crossOrigin, color); - return key in this.cache_ ? this.cache_[key] : null; - } + get(src, crossOrigin, color) { + const key = getKey(src, crossOrigin, color); + return key in this.cache_ ? this.cache_[key] : null; + } - /** + /** * @param {string} src Src. * @param {?string} crossOrigin Cross origin. * @param {module:ol/color~Color} color Color. * @param {module:ol/style/IconImage} iconImage Icon image. */ - set(src, crossOrigin, color, iconImage) { - const key = getKey(src, crossOrigin, color); - this.cache_[key] = iconImage; - ++this.cacheSize_; - } + set(src, crossOrigin, color, iconImage) { + const key = getKey(src, crossOrigin, color); + this.cache_[key] = iconImage; + ++this.cacheSize_; + } - /** + /** * Set the cache size of the icon cache. Default is `32`. Change this value when * your map uses more than 32 different icon images and you are not caching icon * styles on the application level. * @param {number} maxCacheSize Cache max size. * @api */ - setSize(maxCacheSize) { - this.maxCacheSize_ = maxCacheSize; - this.expire(); - } + setSize(maxCacheSize) { + this.maxCacheSize_ = maxCacheSize; + this.expire(); + } } diff --git a/src/ol/style/Image.js b/src/ol/style/Image.js index 6b1e9fe18c..8b5d105366 100644 --- a/src/ol/style/Image.js +++ b/src/ol/style/Image.js @@ -25,212 +25,212 @@ * @api */ class ImageStyle { - constructor(options) { + constructor(options) { - /** + /** * @private * @type {number} */ - this.opacity_ = options.opacity; + this.opacity_ = options.opacity; - /** + /** * @private * @type {boolean} */ - this.rotateWithView_ = options.rotateWithView; + this.rotateWithView_ = options.rotateWithView; - /** + /** * @private * @type {number} */ - this.rotation_ = options.rotation; + this.rotation_ = options.rotation; - /** + /** * @private * @type {number} */ - this.scale_ = options.scale; + this.scale_ = options.scale; - /** + /** * @private * @type {boolean} */ - this.snapToPixel_ = options.snapToPixel; + this.snapToPixel_ = options.snapToPixel; - } + } - /** + /** * Get the symbolizer opacity. * @return {number} Opacity. * @api */ - getOpacity() { - return this.opacity_; - } + getOpacity() { + return this.opacity_; + } - /** + /** * Determine whether the symbolizer rotates with the map. * @return {boolean} Rotate with map. * @api */ - getRotateWithView() { - return this.rotateWithView_; - } + getRotateWithView() { + return this.rotateWithView_; + } - /** + /** * Get the symoblizer rotation. * @return {number} Rotation. * @api */ - getRotation() { - return this.rotation_; - } + getRotation() { + return this.rotation_; + } - /** + /** * Get the symbolizer scale. * @return {number} Scale. * @api */ - getScale() { - return this.scale_; - } + getScale() { + return this.scale_; + } - /** + /** * Determine whether the symbolizer should be snapped to a pixel. * @return {boolean} The symbolizer should snap to a pixel. * @api */ - getSnapToPixel() { - return this.snapToPixel_; - } + getSnapToPixel() { + return this.snapToPixel_; + } - /** + /** * Get the anchor point in pixels. The anchor determines the center point for the * symbolizer. * @abstract * @return {Array.} Anchor. */ - getAnchor() {} + getAnchor() {} - /** + /** * Get the image element for the symbolizer. * @abstract * @param {number} pixelRatio Pixel ratio. * @return {HTMLCanvasElement|HTMLVideoElement|HTMLImageElement} Image element. */ - getImage(pixelRatio) {} + getImage(pixelRatio) {} - /** + /** * @abstract * @param {number} pixelRatio Pixel ratio. * @return {HTMLCanvasElement|HTMLVideoElement|HTMLImageElement} Image element. */ - getHitDetectionImage(pixelRatio) {} + getHitDetectionImage(pixelRatio) {} - /** + /** * @abstract * @return {module:ol/ImageState} Image state. */ - getImageState() {} + getImageState() {} - /** + /** * @abstract * @return {module:ol/size~Size} Image size. */ - getImageSize() {} + getImageSize() {} - /** + /** * @abstract * @return {module:ol/size~Size} Size of the hit-detection image. */ - getHitDetectionImageSize() {} + getHitDetectionImageSize() {} - /** + /** * Get the origin of the symbolizer. * @abstract * @return {Array.} Origin. */ - getOrigin() {} + getOrigin() {} - /** + /** * Get the size of the symbolizer (in pixels). * @abstract * @return {module:ol/size~Size} Size. */ - getSize() {} + getSize() {} - /** + /** * Set the opacity. * * @param {number} opacity Opacity. * @api */ - setOpacity(opacity) { - this.opacity_ = opacity; - } + setOpacity(opacity) { + this.opacity_ = opacity; + } - /** + /** * Set whether to rotate the style with the view. * * @param {boolean} rotateWithView Rotate with map. * @api */ - setRotateWithView(rotateWithView) { - this.rotateWithView_ = rotateWithView; - } + setRotateWithView(rotateWithView) { + this.rotateWithView_ = rotateWithView; + } - /** + /** * Set the rotation. * * @param {number} rotation Rotation. * @api */ - setRotation(rotation) { - this.rotation_ = rotation; - } + setRotation(rotation) { + this.rotation_ = rotation; + } - /** + /** * Set the scale. * * @param {number} scale Scale. * @api */ - setScale(scale) { - this.scale_ = scale; - } + setScale(scale) { + this.scale_ = scale; + } - /** + /** * Set whether to snap the image to the closest pixel. * * @param {boolean} snapToPixel Snap to pixel? * @api */ - setSnapToPixel(snapToPixel) { - this.snapToPixel_ = snapToPixel; - } + setSnapToPixel(snapToPixel) { + this.snapToPixel_ = snapToPixel; + } - /** + /** * @abstract * @param {function(this: T, module:ol/events/Event)} listener Listener function. * @param {T} thisArg Value to use as `this` when executing `listener`. * @return {module:ol/events~EventsKey|undefined} Listener key. * @template T */ - listenImageChange(listener, thisArg) {} + listenImageChange(listener, thisArg) {} - /** + /** * Load not yet loaded URI. * @abstract */ - load() {} + load() {} - /** + /** * @abstract * @param {function(this: T, module:ol/events/Event)} listener Listener function. * @param {T} thisArg Value to use as `this` when executing `listener`. * @template T */ - unlistenImageChange(listener, thisArg) {} + unlistenImageChange(listener, thisArg) {} } export default ImageStyle; diff --git a/src/ol/style/Text.js b/src/ol/style/Text.js index 96a3da5edb..cd88073c82 100644 --- a/src/ol/style/Text.js +++ b/src/ol/style/Text.js @@ -54,452 +54,452 @@ const DEFAULT_FILL_COLOR = '#333'; * @api */ class Text { - constructor(opt_options) { + constructor(opt_options) { - const options = opt_options || {}; + const options = opt_options || {}; - /** + /** * @private * @type {string|undefined} */ - this.font_ = options.font; + this.font_ = options.font; - /** + /** * @private * @type {number|undefined} */ - this.rotation_ = options.rotation; + this.rotation_ = options.rotation; - /** + /** * @private * @type {boolean|undefined} */ - this.rotateWithView_ = options.rotateWithView; + this.rotateWithView_ = options.rotateWithView; - /** + /** * @private * @type {number|undefined} */ - this.scale_ = options.scale; + this.scale_ = options.scale; - /** + /** * @private * @type {string|undefined} */ - this.text_ = options.text; + this.text_ = options.text; - /** + /** * @private * @type {string|undefined} */ - this.textAlign_ = options.textAlign; + this.textAlign_ = options.textAlign; - /** + /** * @private * @type {string|undefined} */ - this.textBaseline_ = options.textBaseline; + this.textBaseline_ = options.textBaseline; - /** + /** * @private * @type {module:ol/style/Fill} */ - this.fill_ = options.fill !== undefined ? options.fill : - new Fill({color: DEFAULT_FILL_COLOR}); + this.fill_ = options.fill !== undefined ? options.fill : + new Fill({color: DEFAULT_FILL_COLOR}); - /** + /** * @private * @type {number} */ - this.maxAngle_ = options.maxAngle !== undefined ? options.maxAngle : Math.PI / 4; + this.maxAngle_ = options.maxAngle !== undefined ? options.maxAngle : Math.PI / 4; - /** + /** * @private * @type {module:ol/style/TextPlacement|string} */ - this.placement_ = options.placement !== undefined ? options.placement : TextPlacement.POINT; + this.placement_ = options.placement !== undefined ? options.placement : TextPlacement.POINT; - /** + /** * @private * @type {boolean} */ - this.overflow_ = !!options.overflow; + this.overflow_ = !!options.overflow; - /** + /** * @private * @type {module:ol/style/Stroke} */ - this.stroke_ = options.stroke !== undefined ? options.stroke : null; + this.stroke_ = options.stroke !== undefined ? options.stroke : null; - /** + /** * @private * @type {number} */ - this.offsetX_ = options.offsetX !== undefined ? options.offsetX : 0; + this.offsetX_ = options.offsetX !== undefined ? options.offsetX : 0; - /** + /** * @private * @type {number} */ - this.offsetY_ = options.offsetY !== undefined ? options.offsetY : 0; + this.offsetY_ = options.offsetY !== undefined ? options.offsetY : 0; - /** + /** * @private * @type {module:ol/style/Fill} */ - this.backgroundFill_ = options.backgroundFill ? options.backgroundFill : null; + this.backgroundFill_ = options.backgroundFill ? options.backgroundFill : null; - /** + /** * @private * @type {module:ol/style/Stroke} */ - this.backgroundStroke_ = options.backgroundStroke ? options.backgroundStroke : null; + this.backgroundStroke_ = options.backgroundStroke ? options.backgroundStroke : null; - /** + /** * @private * @type {Array.} */ - this.padding_ = options.padding === undefined ? null : options.padding; - } + this.padding_ = options.padding === undefined ? null : options.padding; + } - /** + /** * Clones the style. * @return {module:ol/style/Text} The cloned style. * @api */ - clone() { - return new Text({ - font: this.getFont(), - placement: this.getPlacement(), - maxAngle: this.getMaxAngle(), - overflow: this.getOverflow(), - rotation: this.getRotation(), - rotateWithView: this.getRotateWithView(), - scale: this.getScale(), - text: this.getText(), - textAlign: this.getTextAlign(), - textBaseline: this.getTextBaseline(), - fill: this.getFill() ? this.getFill().clone() : undefined, - stroke: this.getStroke() ? this.getStroke().clone() : undefined, - offsetX: this.getOffsetX(), - offsetY: this.getOffsetY(), - backgroundFill: this.getBackgroundFill() ? this.getBackgroundFill().clone() : undefined, - backgroundStroke: this.getBackgroundStroke() ? this.getBackgroundStroke().clone() : undefined - }); - } + clone() { + return new Text({ + font: this.getFont(), + placement: this.getPlacement(), + maxAngle: this.getMaxAngle(), + overflow: this.getOverflow(), + rotation: this.getRotation(), + rotateWithView: this.getRotateWithView(), + scale: this.getScale(), + text: this.getText(), + textAlign: this.getTextAlign(), + textBaseline: this.getTextBaseline(), + fill: this.getFill() ? this.getFill().clone() : undefined, + stroke: this.getStroke() ? this.getStroke().clone() : undefined, + offsetX: this.getOffsetX(), + offsetY: this.getOffsetY(), + backgroundFill: this.getBackgroundFill() ? this.getBackgroundFill().clone() : undefined, + backgroundStroke: this.getBackgroundStroke() ? this.getBackgroundStroke().clone() : undefined + }); + } - /** + /** * Get the `overflow` configuration. * @return {boolean} Let text overflow the length of the path they follow. * @api */ - getOverflow() { - return this.overflow_; - } + getOverflow() { + return this.overflow_; + } - /** + /** * Get the font name. * @return {string|undefined} Font. * @api */ - getFont() { - return this.font_; - } + getFont() { + return this.font_; + } - /** + /** * Get the maximum angle between adjacent characters. * @return {number} Angle in radians. * @api */ - getMaxAngle() { - return this.maxAngle_; - } + getMaxAngle() { + return this.maxAngle_; + } - /** + /** * Get the label placement. * @return {module:ol/style/TextPlacement|string} Text placement. * @api */ - getPlacement() { - return this.placement_; - } + getPlacement() { + return this.placement_; + } - /** + /** * Get the x-offset for the text. * @return {number} Horizontal text offset. * @api */ - getOffsetX() { - return this.offsetX_; - } + getOffsetX() { + return this.offsetX_; + } - /** + /** * Get the y-offset for the text. * @return {number} Vertical text offset. * @api */ - getOffsetY() { - return this.offsetY_; - } + getOffsetY() { + return this.offsetY_; + } - /** + /** * Get the fill style for the text. * @return {module:ol/style/Fill} Fill style. * @api */ - getFill() { - return this.fill_; - } + getFill() { + return this.fill_; + } - /** + /** * Determine whether the text rotates with the map. * @return {boolean|undefined} Rotate with map. * @api */ - getRotateWithView() { - return this.rotateWithView_; - } + getRotateWithView() { + return this.rotateWithView_; + } - /** + /** * Get the text rotation. * @return {number|undefined} Rotation. * @api */ - getRotation() { - return this.rotation_; - } + getRotation() { + return this.rotation_; + } - /** + /** * Get the text scale. * @return {number|undefined} Scale. * @api */ - getScale() { - return this.scale_; - } + getScale() { + return this.scale_; + } - /** + /** * Get the stroke style for the text. * @return {module:ol/style/Stroke} Stroke style. * @api */ - getStroke() { - return this.stroke_; - } + getStroke() { + return this.stroke_; + } - /** + /** * Get the text to be rendered. * @return {string|undefined} Text. * @api */ - getText() { - return this.text_; - } + getText() { + return this.text_; + } - /** + /** * Get the text alignment. * @return {string|undefined} Text align. * @api */ - getTextAlign() { - return this.textAlign_; - } + getTextAlign() { + return this.textAlign_; + } - /** + /** * Get the text baseline. * @return {string|undefined} Text baseline. * @api */ - getTextBaseline() { - return this.textBaseline_; - } + getTextBaseline() { + return this.textBaseline_; + } - /** + /** * Get the background fill style for the text. * @return {module:ol/style/Fill} Fill style. * @api */ - getBackgroundFill() { - return this.backgroundFill_; - } + getBackgroundFill() { + return this.backgroundFill_; + } - /** + /** * Get the background stroke style for the text. * @return {module:ol/style/Stroke} Stroke style. * @api */ - getBackgroundStroke() { - return this.backgroundStroke_; - } + getBackgroundStroke() { + return this.backgroundStroke_; + } - /** + /** * Get the padding for the text. * @return {Array.} Padding. * @api */ - getPadding() { - return this.padding_; - } + getPadding() { + return this.padding_; + } - /** + /** * Set the `overflow` property. * * @param {boolean} overflow Let text overflow the path that it follows. * @api */ - setOverflow(overflow) { - this.overflow_ = overflow; - } + setOverflow(overflow) { + this.overflow_ = overflow; + } - /** + /** * Set the font. * * @param {string|undefined} font Font. * @api */ - setFont(font) { - this.font_ = font; - } + setFont(font) { + this.font_ = font; + } - /** + /** * Set the maximum angle between adjacent characters. * * @param {number} maxAngle Angle in radians. * @api */ - setMaxAngle(maxAngle) { - this.maxAngle_ = maxAngle; - } + setMaxAngle(maxAngle) { + this.maxAngle_ = maxAngle; + } - /** + /** * Set the x offset. * * @param {number} offsetX Horizontal text offset. * @api */ - setOffsetX(offsetX) { - this.offsetX_ = offsetX; - } + setOffsetX(offsetX) { + this.offsetX_ = offsetX; + } - /** + /** * Set the y offset. * * @param {number} offsetY Vertical text offset. * @api */ - setOffsetY(offsetY) { - this.offsetY_ = offsetY; - } + setOffsetY(offsetY) { + this.offsetY_ = offsetY; + } - /** + /** * Set the text placement. * * @param {module:ol/style/TextPlacement|string} placement Placement. * @api */ - setPlacement(placement) { - this.placement_ = placement; - } + setPlacement(placement) { + this.placement_ = placement; + } - /** + /** * Set the fill. * * @param {module:ol/style/Fill} fill Fill style. * @api */ - setFill(fill) { - this.fill_ = fill; - } + setFill(fill) { + this.fill_ = fill; + } - /** + /** * Set the rotation. * * @param {number|undefined} rotation Rotation. * @api */ - setRotation(rotation) { - this.rotation_ = rotation; - } + setRotation(rotation) { + this.rotation_ = rotation; + } - /** + /** * Set the scale. * * @param {number|undefined} scale Scale. * @api */ - setScale(scale) { - this.scale_ = scale; - } + setScale(scale) { + this.scale_ = scale; + } - /** + /** * Set the stroke. * * @param {module:ol/style/Stroke} stroke Stroke style. * @api */ - setStroke(stroke) { - this.stroke_ = stroke; - } + setStroke(stroke) { + this.stroke_ = stroke; + } - /** + /** * Set the text. * * @param {string|undefined} text Text. * @api */ - setText(text) { - this.text_ = text; - } + setText(text) { + this.text_ = text; + } - /** + /** * Set the text alignment. * * @param {string|undefined} textAlign Text align. * @api */ - setTextAlign(textAlign) { - this.textAlign_ = textAlign; - } + setTextAlign(textAlign) { + this.textAlign_ = textAlign; + } - /** + /** * Set the text baseline. * * @param {string|undefined} textBaseline Text baseline. * @api */ - setTextBaseline(textBaseline) { - this.textBaseline_ = textBaseline; - } + setTextBaseline(textBaseline) { + this.textBaseline_ = textBaseline; + } - /** + /** * Set the background fill. * * @param {module:ol/style/Fill} fill Fill style. * @api */ - setBackgroundFill(fill) { - this.backgroundFill_ = fill; - } + setBackgroundFill(fill) { + this.backgroundFill_ = fill; + } - /** + /** * Set the background stroke. * * @param {module:ol/style/Stroke} stroke Stroke style. * @api */ - setBackgroundStroke(stroke) { - this.backgroundStroke_ = stroke; - } + setBackgroundStroke(stroke) { + this.backgroundStroke_ = stroke; + } - /** + /** * Set the padding (`[top, right, bottom, left]`). * * @param {!Array.} padding Padding. * @api */ - setPadding(padding) { - this.padding_ = padding; - } + setPadding(padding) { + this.padding_ = padding; + } } export default Text; diff --git a/src/ol/tilegrid/TileGrid.js b/src/ol/tilegrid/TileGrid.js index f7edb6a322..b1c064c9b2 100644 --- a/src/ol/tilegrid/TileGrid.js +++ b/src/ol/tilegrid/TileGrid.js @@ -11,6 +11,13 @@ import {toSize} from '../size.js'; import {createOrUpdate as createOrUpdateTileCoord} from '../tilecoord.js'; +/** + * @private + * @type {module:ol/tilecoord~TileCoord} + */ +const tmpTileCoord = [0, 0, 0]; + + /** * @typedef {Object} Options * @property {module:ol/extent~Extent} [extent] Extent for the tile grid. No tiles outside this @@ -544,11 +551,4 @@ class TileGrid { } -/** - * @private - * @type {module:ol/tilecoord~TileCoord} - */ -const tmpTileCoord = [0, 0, 0]; - - export default TileGrid; diff --git a/src/ol/webgl/Buffer.js b/src/ol/webgl/Buffer.js index 0bbd88b7c3..2ad0dbba75 100644 --- a/src/ol/webgl/Buffer.js +++ b/src/ol/webgl/Buffer.js @@ -19,35 +19,35 @@ const BufferUsage = { * @struct */ class WebGLBuffer { - constructor(opt_arr, opt_usage) { + constructor(opt_arr, opt_usage) { - /** + /** * @private * @type {Array.} */ - this.arr_ = opt_arr !== undefined ? opt_arr : []; + this.arr_ = opt_arr !== undefined ? opt_arr : []; - /** + /** * @private * @type {number} */ - this.usage_ = opt_usage !== undefined ? opt_usage : BufferUsage.STATIC_DRAW; + this.usage_ = opt_usage !== undefined ? opt_usage : BufferUsage.STATIC_DRAW; - } + } - /** + /** * @return {Array.} Array. */ - getArray() { - return this.arr_; - } + getArray() { + return this.arr_; + } - /** + /** * @return {number} Usage. */ - getUsage() { - return this.usage_; - } + getUsage() { + return this.usage_; + } } export default WebGLBuffer; diff --git a/src/ol/webgl/Fragment.js b/src/ol/webgl/Fragment.js index 459651b6b1..1775950fe2 100644 --- a/src/ol/webgl/Fragment.js +++ b/src/ol/webgl/Fragment.js @@ -12,16 +12,16 @@ import WebGLShader from '../webgl/Shader.js'; * @struct */ class WebGLFragment { - constructor(source) { - WebGLShader.call(this, source); - } + constructor(source) { + WebGLShader.call(this, source); + } - /** + /** * @inheritDoc */ - getType() { - return FRAGMENT_SHADER; - } + getType() { + return FRAGMENT_SHADER; + } } inherits(WebGLFragment, WebGLShader); diff --git a/src/ol/webgl/Shader.js b/src/ol/webgl/Shader.js index ed7927e80b..0ef59b4626 100644 --- a/src/ol/webgl/Shader.js +++ b/src/ol/webgl/Shader.js @@ -10,28 +10,28 @@ import {FALSE} from '../functions.js'; * @struct */ class WebGLShader { - constructor(source) { + constructor(source) { - /** + /** * @private * @type {string} */ - this.source_ = source; + this.source_ = source; - } + } - /** + /** * @abstract * @return {number} Type. */ - getType() {} + getType() {} - /** + /** * @return {string} Source. */ - getSource() { - return this.source_; - } + getSource() { + return this.source_; + } } diff --git a/src/ol/webgl/Vertex.js b/src/ol/webgl/Vertex.js index 3f798a7860..bdf794b86e 100644 --- a/src/ol/webgl/Vertex.js +++ b/src/ol/webgl/Vertex.js @@ -12,16 +12,16 @@ import WebGLShader from '../webgl/Shader.js'; * @struct */ class WebGLVertex { - constructor(source) { - WebGLShader.call(this, source); - } + constructor(source) { + WebGLShader.call(this, source); + } - /** + /** * @inheritDoc */ - getType() { - return VERTEX_SHADER; - } + getType() { + return VERTEX_SHADER; + } } inherits(WebGLVertex, WebGLShader); From 7e3e0e54ca21c52be82d20cb55130b9843c5b3ed Mon Sep 17 00:00:00 2001 From: Frederic Junod Date: Tue, 17 Jul 2018 09:12:50 +0200 Subject: [PATCH 004/107] Fix comments indentation --- src/ol/Disposable.js | 4 +- src/ol/Image.js | 80 ++++---- src/ol/ImageBase.js | 56 +++--- src/ol/ImageCanvas.js | 42 ++-- src/ol/Kinetic.js | 58 +++--- src/ol/MapBrowserEvent.js | 58 +++--- src/ol/TileRange.js | 46 ++--- src/ol/VectorTile.js | 184 +++++++++--------- src/ol/control/Control.js | 68 +++---- src/ol/control/ZoomToExtent.js | 16 +- .../circlereplay/defaultshader/Locations.js | 48 ++--- .../defaultshader/Locations.js | 52 ++--- .../polygonreplay/defaultshader/Locations.js | 24 +-- .../texturereplay/defaultshader/Locations.js | 40 ++-- .../webgl/defaultmapshader/Locations.js | 24 +-- .../webgl/tilelayershader/Locations.js | 16 +- src/ol/webgl/shaderlocations.mustache | 8 +- 17 files changed, 412 insertions(+), 412 deletions(-) diff --git a/src/ol/Disposable.js b/src/ol/Disposable.js index eec64ae19d..a7091d8e8f 100644 --- a/src/ol/Disposable.js +++ b/src/ol/Disposable.js @@ -9,8 +9,8 @@ import {UNDEFINED} from './functions.js'; */ class Disposable { /** - * Clean up. - */ + * Clean up. + */ dispose() { if (!this.disposed_) { this.disposed_ = true; diff --git a/src/ol/Image.js b/src/ol/Image.js index f04cc6c76d..4c2f62a5fa 100644 --- a/src/ol/Image.js +++ b/src/ol/Image.js @@ -44,53 +44,53 @@ class ImageWrapper { ImageBase.call(this, extent, resolution, pixelRatio, ImageState.IDLE); /** - * @private - * @type {string} - */ + * @private + * @type {string} + */ this.src_ = src; /** - * @private - * @type {HTMLCanvasElement|HTMLImageElement|HTMLVideoElement} - */ + * @private + * @type {HTMLCanvasElement|HTMLImageElement|HTMLVideoElement} + */ this.image_ = new Image(); if (crossOrigin !== null) { this.image_.crossOrigin = crossOrigin; } /** - * @private - * @type {Array.} - */ + * @private + * @type {Array.} + */ this.imageListenerKeys_ = null; /** - * @protected - * @type {module:ol/ImageState} - */ + * @protected + * @type {module:ol/ImageState} + */ this.state = ImageState.IDLE; /** - * @private - * @type {module:ol/Image~LoadFunction} - */ + * @private + * @type {module:ol/Image~LoadFunction} + */ this.imageLoadFunction_ = imageLoadFunction; } /** - * @inheritDoc - * @api - */ + * @inheritDoc + * @api + */ getImage() { return this.image_; } /** - * Tracks loading or read errors. - * - * @private - */ + * Tracks loading or read errors. + * + * @private + */ handleImageError_() { this.state = ImageState.ERROR; this.unlistenImage_(); @@ -98,10 +98,10 @@ class ImageWrapper { } /** - * Tracks successful image load. - * - * @private - */ + * Tracks successful image load. + * + * @private + */ handleImageLoad_() { if (this.resolution === undefined) { this.resolution = getHeight(this.extent) / this.image_.height; @@ -112,38 +112,38 @@ class ImageWrapper { } /** - * Load the image or retry if loading previously failed. - * Loading is taken care of by the tile queue, and calling this method is - * only needed for preloading or for reloading in case of an error. - * @override - * @api - */ + * Load the image or retry if loading previously failed. + * Loading is taken care of by the tile queue, and calling this method is + * only needed for preloading or for reloading in case of an error. + * @override + * @api + */ load() { if (this.state == ImageState.IDLE || this.state == ImageState.ERROR) { this.state = ImageState.LOADING; this.changed(); this.imageListenerKeys_ = [ listenOnce(this.image_, EventType.ERROR, - this.handleImageError_, this), + this.handleImageError_, this), listenOnce(this.image_, EventType.LOAD, - this.handleImageLoad_, this) + this.handleImageLoad_, this) ]; this.imageLoadFunction_(this, this.src_); } } /** - * @param {HTMLCanvasElement|HTMLImageElement|HTMLVideoElement} image Image. - */ + * @param {HTMLCanvasElement|HTMLImageElement|HTMLVideoElement} image Image. + */ setImage(image) { this.image_ = image; } /** - * Discards event handlers which listen for load completion or errors. - * - * @private - */ + * Discards event handlers which listen for load completion or errors. + * + * @private + */ unlistenImage_() { this.imageListenerKeys_.forEach(unlistenByKey); this.imageListenerKeys_ = null; diff --git a/src/ol/ImageBase.js b/src/ol/ImageBase.js index a0e4ec866a..f6b16e6785 100644 --- a/src/ol/ImageBase.js +++ b/src/ol/ImageBase.js @@ -20,76 +20,76 @@ class ImageBase { EventTarget.call(this); /** - * @protected - * @type {module:ol/extent~Extent} - */ + * @protected + * @type {module:ol/extent~Extent} + */ this.extent = extent; /** - * @private - * @type {number} - */ + * @private + * @type {number} + */ this.pixelRatio_ = pixelRatio; /** - * @protected - * @type {number|undefined} - */ + * @protected + * @type {number|undefined} + */ this.resolution = resolution; /** - * @protected - * @type {module:ol/ImageState} - */ + * @protected + * @type {module:ol/ImageState} + */ this.state = state; } /** - * @protected - */ + * @protected + */ changed() { this.dispatchEvent(EventType.CHANGE); } /** - * @return {module:ol/extent~Extent} Extent. - */ + * @return {module:ol/extent~Extent} Extent. + */ getExtent() { return this.extent; } /** - * @abstract - * @return {HTMLCanvasElement|HTMLImageElement|HTMLVideoElement} Image. - */ + * @abstract + * @return {HTMLCanvasElement|HTMLImageElement|HTMLVideoElement} Image. + */ getImage() {} /** - * @return {number} PixelRatio. - */ + * @return {number} PixelRatio. + */ getPixelRatio() { return this.pixelRatio_; } /** - * @return {number} Resolution. - */ + * @return {number} Resolution. + */ getResolution() { return /** @type {number} */ (this.resolution); } /** - * @return {module:ol/ImageState} State. - */ + * @return {module:ol/ImageState} State. + */ getState() { return this.state; } /** - * Load not yet loaded URI. - * @abstract - */ + * Load not yet loaded URI. + * @abstract + */ load() {} } diff --git a/src/ol/ImageCanvas.js b/src/ol/ImageCanvas.js index ea3f2d362e..e95bb8852a 100644 --- a/src/ol/ImageCanvas.js +++ b/src/ol/ImageCanvas.js @@ -30,10 +30,10 @@ class ImageCanvas { constructor(extent, resolution, pixelRatio, canvas, opt_loader) { /** - * Optional canvas loader function. - * @type {?module:ol/ImageCanvas~Loader} - * @private - */ + * Optional canvas loader function. + * @type {?module:ol/ImageCanvas~Loader} + * @private + */ this.loader_ = opt_loader !== undefined ? opt_loader : null; const state = opt_loader !== undefined ? ImageState.IDLE : ImageState.LOADED; @@ -41,32 +41,32 @@ class ImageCanvas { ImageBase.call(this, extent, resolution, pixelRatio, state); /** - * @private - * @type {HTMLCanvasElement} - */ + * @private + * @type {HTMLCanvasElement} + */ this.canvas_ = canvas; /** - * @private - * @type {Error} - */ + * @private + * @type {Error} + */ this.error_ = null; } /** - * Get any error associated with asynchronous rendering. - * @return {Error} Any error that occurred during rendering. - */ + * Get any error associated with asynchronous rendering. + * @return {Error} Any error that occurred during rendering. + */ getError() { return this.error_; } /** - * Handle async drawing complete. - * @param {Error} err Any error during drawing. - * @private - */ + * Handle async drawing complete. + * @param {Error} err Any error during drawing. + * @private + */ handleLoad_(err) { if (err) { this.error_ = err; @@ -78,8 +78,8 @@ class ImageCanvas { } /** - * @inheritDoc - */ + * @inheritDoc + */ load() { if (this.state == ImageState.IDLE) { this.state = ImageState.LOADING; @@ -89,8 +89,8 @@ class ImageCanvas { } /** - * @return {HTMLCanvasElement} Canvas element. - */ + * @return {HTMLCanvasElement} Canvas element. + */ getImage() { return this.canvas_; } diff --git a/src/ol/Kinetic.js b/src/ol/Kinetic.js index 73090f8b9d..ef5be333e9 100644 --- a/src/ol/Kinetic.js +++ b/src/ol/Kinetic.js @@ -18,45 +18,45 @@ class Kinetic { constructor(decay, minVelocity, delay) { /** - * @private - * @type {number} - */ + * @private + * @type {number} + */ this.decay_ = decay; /** - * @private - * @type {number} - */ + * @private + * @type {number} + */ this.minVelocity_ = minVelocity; /** - * @private - * @type {number} - */ + * @private + * @type {number} + */ this.delay_ = delay; /** - * @private - * @type {Array.} - */ + * @private + * @type {Array.} + */ this.points_ = []; /** - * @private - * @type {number} - */ + * @private + * @type {number} + */ this.angle_ = 0; /** - * @private - * @type {number} - */ + * @private + * @type {number} + */ this.initialVelocity_ = 0; } /** - * FIXME empty description for jsdoc - */ + * FIXME empty description for jsdoc + */ begin() { this.points_.length = 0; this.angle_ = 0; @@ -64,16 +64,16 @@ class Kinetic { } /** - * @param {number} x X. - * @param {number} y Y. - */ + * @param {number} x X. + * @param {number} y Y. + */ update(x, y) { this.points_.push(x, y, Date.now()); } /** - * @return {boolean} Whether we should do kinetic animation. - */ + * @return {boolean} Whether we should do kinetic animation. + */ end() { if (this.points_.length < 6) { // at least 2 points are required (i.e. there must be at least 6 elements @@ -110,15 +110,15 @@ class Kinetic { } /** - * @return {number} Total distance travelled (pixels). - */ + * @return {number} Total distance travelled (pixels). + */ getDistance() { return (this.minVelocity_ - this.initialVelocity_) / this.decay_; } /** - * @return {number} Angle of the kinetic panning animation (radians). - */ + * @return {number} Angle of the kinetic panning animation (radians). + */ getAngle() { return this.angle_; } diff --git a/src/ol/MapBrowserEvent.js b/src/ol/MapBrowserEvent.js index ab11a55549..e8a7a7e201 100644 --- a/src/ol/MapBrowserEvent.js +++ b/src/ol/MapBrowserEvent.js @@ -23,55 +23,55 @@ class MapBrowserEvent { MapEvent.call(this, type, map, opt_frameState); /** - * The original browser event. - * @const - * @type {Event} - * @api - */ + * The original browser event. + * @const + * @type {Event} + * @api + */ this.originalEvent = browserEvent; /** - * The map pixel relative to the viewport corresponding to the original browser event. - * @type {module:ol~Pixel} - * @api - */ + * The map pixel relative to the viewport corresponding to the original browser event. + * @type {module:ol~Pixel} + * @api + */ this.pixel = map.getEventPixel(browserEvent); /** - * The coordinate in view projection corresponding to the original browser event. - * @type {module:ol/coordinate~Coordinate} - * @api - */ + * The coordinate in view projection corresponding to the original browser event. + * @type {module:ol/coordinate~Coordinate} + * @api + */ this.coordinate = map.getCoordinateFromPixel(this.pixel); /** - * Indicates if the map is currently being dragged. Only set for - * `POINTERDRAG` and `POINTERMOVE` events. Default is `false`. - * - * @type {boolean} - * @api - */ + * Indicates if the map is currently being dragged. Only set for + * `POINTERDRAG` and `POINTERMOVE` events. Default is `false`. + * + * @type {boolean} + * @api + */ this.dragging = opt_dragging !== undefined ? opt_dragging : false; } /** - * Prevents the default browser action. - * @see https://developer.mozilla.org/en-US/docs/Web/API/event.preventDefault - * @override - * @api - */ + * Prevents the default browser action. + * @see https://developer.mozilla.org/en-US/docs/Web/API/event.preventDefault + * @override + * @api + */ preventDefault() { MapEvent.prototype.preventDefault.call(this); this.originalEvent.preventDefault(); } /** - * Prevents further propagation of the current event. - * @see https://developer.mozilla.org/en-US/docs/Web/API/event.stopPropagation - * @override - * @api - */ + * Prevents further propagation of the current event. + * @see https://developer.mozilla.org/en-US/docs/Web/API/event.stopPropagation + * @override + * @api + */ stopPropagation() { MapEvent.prototype.stopPropagation.call(this); this.originalEvent.stopPropagation(); diff --git a/src/ol/TileRange.js b/src/ol/TileRange.js index d3be21b15d..127b6f7b95 100644 --- a/src/ol/TileRange.js +++ b/src/ol/TileRange.js @@ -16,65 +16,65 @@ class TileRange { constructor(minX, maxX, minY, maxY) { /** - * @type {number} - */ + * @type {number} + */ this.minX = minX; /** - * @type {number} - */ + * @type {number} + */ this.maxX = maxX; /** - * @type {number} - */ + * @type {number} + */ this.minY = minY; /** - * @type {number} - */ + * @type {number} + */ this.maxY = maxY; } /** - * @param {module:ol/tilecoord~TileCoord} tileCoord Tile coordinate. - * @return {boolean} Contains tile coordinate. - */ + * @param {module:ol/tilecoord~TileCoord} tileCoord Tile coordinate. + * @return {boolean} Contains tile coordinate. + */ contains(tileCoord) { return this.containsXY(tileCoord[1], tileCoord[2]); } /** - * @param {module:ol/TileRange} tileRange Tile range. - * @return {boolean} Contains. - */ + * @param {module:ol/TileRange} tileRange Tile range. + * @return {boolean} Contains. + */ containsTileRange(tileRange) { return this.minX <= tileRange.minX && tileRange.maxX <= this.maxX && this.minY <= tileRange.minY && tileRange.maxY <= this.maxY; } /** - * @param {number} x Tile coordinate x. - * @param {number} y Tile coordinate y. - * @return {boolean} Contains coordinate. - */ + * @param {number} x Tile coordinate x. + * @param {number} y Tile coordinate y. + * @return {boolean} Contains coordinate. + */ containsXY(x, y) { return this.minX <= x && x <= this.maxX && this.minY <= y && y <= this.maxY; } /** - * @param {module:ol/TileRange} tileRange Tile range. - * @return {boolean} Equals. - */ + * @param {module:ol/TileRange} tileRange Tile range. + * @return {boolean} Equals. + */ equals(tileRange) { return this.minX == tileRange.minX && this.minY == tileRange.minY && this.maxX == tileRange.maxX && this.maxY == tileRange.maxY; } /** - * @param {module:ol/TileRange} tileRange Tile range. - */ + * @param {module:ol/TileRange} tileRange Tile range. + */ extend(tileRange) { if (tileRange.minX < this.minX) { this.minX = tileRange.minX; diff --git a/src/ol/VectorTile.js b/src/ol/VectorTile.js index ff2591a255..cf7f56c937 100644 --- a/src/ol/VectorTile.js +++ b/src/ol/VectorTile.js @@ -34,64 +34,64 @@ class VectorTile { Tile.call(this, tileCoord, state, opt_options); /** - * @type {number} - */ + * @type {number} + */ this.consumers = 0; /** - * @private - * @type {module:ol/extent~Extent} - */ + * @private + * @type {module:ol/extent~Extent} + */ this.extent_ = null; /** - * @private - * @type {module:ol/format/Feature} - */ + * @private + * @type {module:ol/format/Feature} + */ this.format_ = format; /** - * @private - * @type {Array.} - */ + * @private + * @type {Array.} + */ this.features_ = null; /** - * @private - * @type {module:ol/featureloader~FeatureLoader} - */ + * @private + * @type {module:ol/featureloader~FeatureLoader} + */ this.loader_; /** - * Data projection - * @private - * @type {module:ol/proj/Projection} - */ + * Data projection + * @private + * @type {module:ol/proj/Projection} + */ this.projection_ = null; /** - * @private - * @type {Object.} - */ + * @private + * @type {Object.} + */ this.replayGroups_ = {}; /** - * @private - * @type {module:ol/Tile~LoadFunction} - */ + * @private + * @type {module:ol/Tile~LoadFunction} + */ this.tileLoadFunction_ = tileLoadFunction; /** - * @private - * @type {string} - */ + * @private + * @type {string} + */ this.url_ = src; } /** - * @inheritDoc - */ + * @inheritDoc + */ disposeInternal() { this.features_ = null; this.replayGroups_ = {}; @@ -101,62 +101,62 @@ class VectorTile { } /** - * Gets the extent of the vector tile. - * @return {module:ol/extent~Extent} The extent. - * @api - */ + * Gets the extent of the vector tile. + * @return {module:ol/extent~Extent} The extent. + * @api + */ getExtent() { return this.extent_ || DEFAULT_EXTENT; } /** - * Get the feature format assigned for reading this tile's features. - * @return {module:ol/format/Feature} Feature format. - * @api - */ + * Get the feature format assigned for reading this tile's features. + * @return {module:ol/format/Feature} Feature format. + * @api + */ getFormat() { return this.format_; } /** - * Get the features for this tile. Geometries will be in the projection returned - * by {@link module:ol/VectorTile~VectorTile#getProjection}. - * @return {Array.} Features. - * @api - */ + * Get the features for this tile. Geometries will be in the projection returned + * by {@link module:ol/VectorTile~VectorTile#getProjection}. + * @return {Array.} Features. + * @api + */ getFeatures() { return this.features_; } /** - * @inheritDoc - */ + * @inheritDoc + */ getKey() { return this.url_; } /** - * Get the feature projection of features returned by - * {@link module:ol/VectorTile~VectorTile#getFeatures}. - * @return {module:ol/proj/Projection} Feature projection. - * @api - */ + * Get the feature projection of features returned by + * {@link module:ol/VectorTile~VectorTile#getFeatures}. + * @return {module:ol/proj/Projection} Feature projection. + * @api + */ getProjection() { return this.projection_; } /** - * @param {module:ol/layer/Layer} layer Layer. - * @param {string} key Key. - * @return {module:ol/render/ReplayGroup} Replay group. - */ + * @param {module:ol/layer/Layer} layer Layer. + * @param {string} key Key. + * @return {module:ol/render/ReplayGroup} Replay group. + */ getReplayGroup(layer, key) { return this.replayGroups_[getUid(layer) + ',' + key]; } /** - * @inheritDoc - */ + * @inheritDoc + */ load() { if (this.state == TileState.IDLE) { this.setState(TileState.LOADING); @@ -166,11 +166,11 @@ class VectorTile { } /** - * Handler for successful tile load. - * @param {Array.} features The loaded features. - * @param {module:ol/proj/Projection} dataProjection Data projection. - * @param {module:ol/extent~Extent} extent Extent. - */ + * Handler for successful tile load. + * @param {Array.} features The loaded features. + * @param {module:ol/proj/Projection} dataProjection Data projection. + * @param {module:ol/extent~Extent} extent Extent. + */ onLoad(features, dataProjection, extent) { this.setProjection(dataProjection); this.setFeatures(features); @@ -178,64 +178,64 @@ class VectorTile { } /** - * Handler for tile load errors. - */ + * Handler for tile load errors. + */ onError() { this.setState(TileState.ERROR); } /** - * Function for use in an {@link module:ol/source/VectorTile~VectorTile}'s - * `tileLoadFunction`. Sets the extent of the vector tile. This is only required - * for tiles in projections with `tile-pixels` as units. The extent should be - * set to `[0, 0, tilePixelSize, tilePixelSize]`, where `tilePixelSize` is - * calculated by multiplying the tile size with the tile pixel ratio. For - * sources using {@link module:ol/format/MVT~MVT} as feature format, the - * {@link module:ol/format/MVT~MVT#getLastExtent} method will return the correct - * extent. The default is `[0, 0, 4096, 4096]`. - * @param {module:ol/extent~Extent} extent The extent. - * @api - */ + * Function for use in an {@link module:ol/source/VectorTile~VectorTile}'s + * `tileLoadFunction`. Sets the extent of the vector tile. This is only required + * for tiles in projections with `tile-pixels` as units. The extent should be + * set to `[0, 0, tilePixelSize, tilePixelSize]`, where `tilePixelSize` is + * calculated by multiplying the tile size with the tile pixel ratio. For + * sources using {@link module:ol/format/MVT~MVT} as feature format, the + * {@link module:ol/format/MVT~MVT#getLastExtent} method will return the correct + * extent. The default is `[0, 0, 4096, 4096]`. + * @param {module:ol/extent~Extent} extent The extent. + * @api + */ setExtent(extent) { this.extent_ = extent; } /** - * Function for use in an {@link module:ol/source/VectorTile~VectorTile}'s `tileLoadFunction`. - * Sets the features for the tile. - * @param {Array.} features Features. - * @api - */ + * Function for use in an {@link module:ol/source/VectorTile~VectorTile}'s `tileLoadFunction`. + * Sets the features for the tile. + * @param {Array.} features Features. + * @api + */ setFeatures(features) { this.features_ = features; this.setState(TileState.LOADED); } /** - * Function for use in an {@link module:ol/source/VectorTile~VectorTile}'s `tileLoadFunction`. - * Sets the projection of the features that were added with - * {@link module:ol/VectorTile~VectorTile#setFeatures}. - * @param {module:ol/proj/Projection} projection Feature projection. - * @api - */ + * Function for use in an {@link module:ol/source/VectorTile~VectorTile}'s `tileLoadFunction`. + * Sets the projection of the features that were added with + * {@link module:ol/VectorTile~VectorTile#setFeatures}. + * @param {module:ol/proj/Projection} projection Feature projection. + * @api + */ setProjection(projection) { this.projection_ = projection; } /** - * @param {module:ol/layer/Layer} layer Layer. - * @param {string} key Key. - * @param {module:ol/render/ReplayGroup} replayGroup Replay group. - */ + * @param {module:ol/layer/Layer} layer Layer. + * @param {string} key Key. + * @param {module:ol/render/ReplayGroup} replayGroup Replay group. + */ setReplayGroup(layer, key, replayGroup) { this.replayGroups_[getUid(layer) + ',' + key] = replayGroup; } /** - * Set the feature loader for reading this tile's features. - * @param {module:ol/featureloader~FeatureLoader} loader Feature loader. - * @api - */ + * Set the feature loader for reading this tile's features. + * @param {module:ol/featureloader~FeatureLoader} loader Feature loader. + * @api + */ setLoader(loader) { this.loader_ = loader; } diff --git a/src/ol/control/Control.js b/src/ol/control/Control.js index af040e02c6..d02c61f9fa 100644 --- a/src/ol/control/Control.js +++ b/src/ol/control/Control.js @@ -55,32 +55,32 @@ class Control { BaseObject.call(this); /** - * @protected - * @type {Element} - */ + * @protected + * @type {Element} + */ this.element = options.element ? options.element : null; /** - * @private - * @type {Element} - */ + * @private + * @type {Element} + */ this.target_ = null; /** - * @private - * @type {module:ol/PluggableMap} - */ + * @private + * @type {module:ol/PluggableMap} + */ this.map_ = null; /** - * @protected - * @type {!Array.} - */ + * @protected + * @type {!Array.} + */ this.listenerKeys = []; /** - * @type {function(module:ol/MapEvent)} - */ + * @type {function(module:ol/MapEvent)} + */ this.render = options.render ? options.render : UNDEFINED; if (options.target) { @@ -90,29 +90,29 @@ class Control { } /** - * @inheritDoc - */ + * @inheritDoc + */ disposeInternal() { removeNode(this.element); BaseObject.prototype.disposeInternal.call(this); } /** - * Get the map associated with this control. - * @return {module:ol/PluggableMap} Map. - * @api - */ + * Get the map associated with this control. + * @return {module:ol/PluggableMap} Map. + * @api + */ getMap() { return this.map_; } /** - * Remove the control from its current map and attach it to the new map. - * Subclasses may set up event handlers to get notified about changes to - * the map here. - * @param {module:ol/PluggableMap} map Map. - * @api - */ + * Remove the control from its current map and attach it to the new map. + * Subclasses may set up event handlers to get notified about changes to + * the map here. + * @param {module:ol/PluggableMap} map Map. + * @api + */ setMap(map) { if (this.map_) { removeNode(this.element); @@ -135,14 +135,14 @@ class Control { } /** - * This function is used to set a target element for the control. It has no - * effect if it is called after the control has been added to the map (i.e. - * after `setMap` is called on the control). If no `target` is set in the - * options passed to the control constructor and if `setTarget` is not called - * then the control is added to the map's overlay container. - * @param {Element|string} target Target. - * @api - */ + * This function is used to set a target element for the control. It has no + * effect if it is called after the control has been added to the map (i.e. + * after `setMap` is called on the control). If no `target` is set in the + * options passed to the control constructor and if `setTarget` is not called + * then the control is added to the map's overlay container. + * @param {Element|string} target Target. + * @api + */ setTarget(target) { this.target_ = typeof target === 'string' ? document.getElementById(target) : diff --git a/src/ol/control/ZoomToExtent.js b/src/ol/control/ZoomToExtent.js index 01b230ee04..a73951a026 100644 --- a/src/ol/control/ZoomToExtent.js +++ b/src/ol/control/ZoomToExtent.js @@ -36,9 +36,9 @@ class ZoomToExtent { const options = opt_options ? opt_options : {}; /** - * @type {module:ol/extent~Extent} - * @protected - */ + * @type {module:ol/extent~Extent} + * @protected + */ this.extent = options.extent ? options.extent : null; const className = options.className !== undefined ? options.className : 'ol-zoom-extent'; @@ -66,17 +66,17 @@ class ZoomToExtent { } /** - * @param {MouseEvent} event The event to handle - * @private - */ + * @param {MouseEvent} event The event to handle + * @private + */ handleClick_(event) { event.preventDefault(); this.handleZoomToExtent(); } /** - * @protected - */ + * @protected + */ handleZoomToExtent() { const map = this.getMap(); const view = map.getView(); diff --git a/src/ol/render/webgl/circlereplay/defaultshader/Locations.js b/src/ol/render/webgl/circlereplay/defaultshader/Locations.js index 1cd45a199c..a0c370e082 100644 --- a/src/ol/render/webgl/circlereplay/defaultshader/Locations.js +++ b/src/ol/render/webgl/circlereplay/defaultshader/Locations.js @@ -17,74 +17,74 @@ class Locations { constructor(gl, program) { /** - * @type {WebGLUniformLocation} - */ + * @type {WebGLUniformLocation} + */ this.u_projectionMatrix = gl.getUniformLocation( program, DEBUG_WEBGL ? 'u_projectionMatrix' : 'h'); /** - * @type {WebGLUniformLocation} - */ + * @type {WebGLUniformLocation} + */ this.u_offsetScaleMatrix = gl.getUniformLocation( program, DEBUG_WEBGL ? 'u_offsetScaleMatrix' : 'i'); /** - * @type {WebGLUniformLocation} - */ + * @type {WebGLUniformLocation} + */ this.u_offsetRotateMatrix = gl.getUniformLocation( program, DEBUG_WEBGL ? 'u_offsetRotateMatrix' : 'j'); /** - * @type {WebGLUniformLocation} - */ + * @type {WebGLUniformLocation} + */ this.u_lineWidth = gl.getUniformLocation( program, DEBUG_WEBGL ? 'u_lineWidth' : 'k'); /** - * @type {WebGLUniformLocation} - */ + * @type {WebGLUniformLocation} + */ this.u_pixelRatio = gl.getUniformLocation( program, DEBUG_WEBGL ? 'u_pixelRatio' : 'l'); /** - * @type {WebGLUniformLocation} - */ + * @type {WebGLUniformLocation} + */ this.u_opacity = gl.getUniformLocation( program, DEBUG_WEBGL ? 'u_opacity' : 'm'); /** - * @type {WebGLUniformLocation} - */ + * @type {WebGLUniformLocation} + */ this.u_fillColor = gl.getUniformLocation( program, DEBUG_WEBGL ? 'u_fillColor' : 'n'); /** - * @type {WebGLUniformLocation} - */ + * @type {WebGLUniformLocation} + */ this.u_strokeColor = gl.getUniformLocation( program, DEBUG_WEBGL ? 'u_strokeColor' : 'o'); /** - * @type {WebGLUniformLocation} - */ + * @type {WebGLUniformLocation} + */ this.u_size = gl.getUniformLocation( program, DEBUG_WEBGL ? 'u_size' : 'p'); /** - * @type {number} - */ + * @type {number} + */ this.a_position = gl.getAttribLocation( program, DEBUG_WEBGL ? 'a_position' : 'e'); /** - * @type {number} - */ + * @type {number} + */ this.a_instruction = gl.getAttribLocation( program, DEBUG_WEBGL ? 'a_instruction' : 'f'); /** - * @type {number} - */ + * @type {number} + */ this.a_radius = gl.getAttribLocation( program, DEBUG_WEBGL ? 'a_radius' : 'g'); diff --git a/src/ol/render/webgl/linestringreplay/defaultshader/Locations.js b/src/ol/render/webgl/linestringreplay/defaultshader/Locations.js index b1fc309e29..1b9a9f6c55 100644 --- a/src/ol/render/webgl/linestringreplay/defaultshader/Locations.js +++ b/src/ol/render/webgl/linestringreplay/defaultshader/Locations.js @@ -17,80 +17,80 @@ class Locations { constructor(gl, program) { /** - * @type {WebGLUniformLocation} - */ + * @type {WebGLUniformLocation} + */ this.u_projectionMatrix = gl.getUniformLocation( program, DEBUG_WEBGL ? 'u_projectionMatrix' : 'h'); /** - * @type {WebGLUniformLocation} - */ + * @type {WebGLUniformLocation} + */ this.u_offsetScaleMatrix = gl.getUniformLocation( program, DEBUG_WEBGL ? 'u_offsetScaleMatrix' : 'i'); /** - * @type {WebGLUniformLocation} - */ + * @type {WebGLUniformLocation} + */ this.u_offsetRotateMatrix = gl.getUniformLocation( program, DEBUG_WEBGL ? 'u_offsetRotateMatrix' : 'j'); /** - * @type {WebGLUniformLocation} - */ + * @type {WebGLUniformLocation} + */ this.u_lineWidth = gl.getUniformLocation( program, DEBUG_WEBGL ? 'u_lineWidth' : 'k'); /** - * @type {WebGLUniformLocation} - */ + * @type {WebGLUniformLocation} + */ this.u_miterLimit = gl.getUniformLocation( program, DEBUG_WEBGL ? 'u_miterLimit' : 'l'); /** - * @type {WebGLUniformLocation} - */ + * @type {WebGLUniformLocation} + */ this.u_opacity = gl.getUniformLocation( program, DEBUG_WEBGL ? 'u_opacity' : 'm'); /** - * @type {WebGLUniformLocation} - */ + * @type {WebGLUniformLocation} + */ this.u_color = gl.getUniformLocation( program, DEBUG_WEBGL ? 'u_color' : 'n'); /** - * @type {WebGLUniformLocation} - */ + * @type {WebGLUniformLocation} + */ this.u_size = gl.getUniformLocation( program, DEBUG_WEBGL ? 'u_size' : 'o'); /** - * @type {WebGLUniformLocation} - */ + * @type {WebGLUniformLocation} + */ this.u_pixelRatio = gl.getUniformLocation( program, DEBUG_WEBGL ? 'u_pixelRatio' : 'p'); /** - * @type {number} - */ + * @type {number} + */ this.a_lastPos = gl.getAttribLocation( program, DEBUG_WEBGL ? 'a_lastPos' : 'd'); /** - * @type {number} - */ + * @type {number} + */ this.a_position = gl.getAttribLocation( program, DEBUG_WEBGL ? 'a_position' : 'e'); /** - * @type {number} - */ + * @type {number} + */ this.a_nextPos = gl.getAttribLocation( program, DEBUG_WEBGL ? 'a_nextPos' : 'f'); /** - * @type {number} - */ + * @type {number} + */ this.a_direction = gl.getAttribLocation( program, DEBUG_WEBGL ? 'a_direction' : 'g'); diff --git a/src/ol/render/webgl/polygonreplay/defaultshader/Locations.js b/src/ol/render/webgl/polygonreplay/defaultshader/Locations.js index 5cb9960cd5..19482c6188 100644 --- a/src/ol/render/webgl/polygonreplay/defaultshader/Locations.js +++ b/src/ol/render/webgl/polygonreplay/defaultshader/Locations.js @@ -17,38 +17,38 @@ class Locations { constructor(gl, program) { /** - * @type {WebGLUniformLocation} - */ + * @type {WebGLUniformLocation} + */ this.u_projectionMatrix = gl.getUniformLocation( program, DEBUG_WEBGL ? 'u_projectionMatrix' : 'b'); /** - * @type {WebGLUniformLocation} - */ + * @type {WebGLUniformLocation} + */ this.u_offsetScaleMatrix = gl.getUniformLocation( program, DEBUG_WEBGL ? 'u_offsetScaleMatrix' : 'c'); /** - * @type {WebGLUniformLocation} - */ + * @type {WebGLUniformLocation} + */ this.u_offsetRotateMatrix = gl.getUniformLocation( program, DEBUG_WEBGL ? 'u_offsetRotateMatrix' : 'd'); /** - * @type {WebGLUniformLocation} - */ + * @type {WebGLUniformLocation} + */ this.u_color = gl.getUniformLocation( program, DEBUG_WEBGL ? 'u_color' : 'e'); /** - * @type {WebGLUniformLocation} - */ + * @type {WebGLUniformLocation} + */ this.u_opacity = gl.getUniformLocation( program, DEBUG_WEBGL ? 'u_opacity' : 'f'); /** - * @type {number} - */ + * @type {number} + */ this.a_position = gl.getAttribLocation( program, DEBUG_WEBGL ? 'a_position' : 'a'); diff --git a/src/ol/render/webgl/texturereplay/defaultshader/Locations.js b/src/ol/render/webgl/texturereplay/defaultshader/Locations.js index 90c99fb757..d8890f7c85 100644 --- a/src/ol/render/webgl/texturereplay/defaultshader/Locations.js +++ b/src/ol/render/webgl/texturereplay/defaultshader/Locations.js @@ -17,62 +17,62 @@ class Locations { constructor(gl, program) { /** - * @type {WebGLUniformLocation} - */ + * @type {WebGLUniformLocation} + */ this.u_projectionMatrix = gl.getUniformLocation( program, DEBUG_WEBGL ? 'u_projectionMatrix' : 'h'); /** - * @type {WebGLUniformLocation} - */ + * @type {WebGLUniformLocation} + */ this.u_offsetScaleMatrix = gl.getUniformLocation( program, DEBUG_WEBGL ? 'u_offsetScaleMatrix' : 'i'); /** - * @type {WebGLUniformLocation} - */ + * @type {WebGLUniformLocation} + */ this.u_offsetRotateMatrix = gl.getUniformLocation( program, DEBUG_WEBGL ? 'u_offsetRotateMatrix' : 'j'); /** - * @type {WebGLUniformLocation} - */ + * @type {WebGLUniformLocation} + */ this.u_opacity = gl.getUniformLocation( program, DEBUG_WEBGL ? 'u_opacity' : 'k'); /** - * @type {WebGLUniformLocation} - */ + * @type {WebGLUniformLocation} + */ this.u_image = gl.getUniformLocation( program, DEBUG_WEBGL ? 'u_image' : 'l'); /** - * @type {number} - */ + * @type {number} + */ this.a_position = gl.getAttribLocation( program, DEBUG_WEBGL ? 'a_position' : 'c'); /** - * @type {number} - */ + * @type {number} + */ this.a_texCoord = gl.getAttribLocation( program, DEBUG_WEBGL ? 'a_texCoord' : 'd'); /** - * @type {number} - */ + * @type {number} + */ this.a_offsets = gl.getAttribLocation( program, DEBUG_WEBGL ? 'a_offsets' : 'e'); /** - * @type {number} - */ + * @type {number} + */ this.a_opacity = gl.getAttribLocation( program, DEBUG_WEBGL ? 'a_opacity' : 'f'); /** - * @type {number} - */ + * @type {number} + */ this.a_rotateWithView = gl.getAttribLocation( program, DEBUG_WEBGL ? 'a_rotateWithView' : 'g'); diff --git a/src/ol/renderer/webgl/defaultmapshader/Locations.js b/src/ol/renderer/webgl/defaultmapshader/Locations.js index 0ac95de300..229f3ba4a4 100644 --- a/src/ol/renderer/webgl/defaultmapshader/Locations.js +++ b/src/ol/renderer/webgl/defaultmapshader/Locations.js @@ -17,38 +17,38 @@ class Locations { constructor(gl, program) { /** - * @type {WebGLUniformLocation} - */ + * @type {WebGLUniformLocation} + */ this.u_texCoordMatrix = gl.getUniformLocation( program, DEBUG_WEBGL ? 'u_texCoordMatrix' : 'd'); /** - * @type {WebGLUniformLocation} - */ + * @type {WebGLUniformLocation} + */ this.u_projectionMatrix = gl.getUniformLocation( program, DEBUG_WEBGL ? 'u_projectionMatrix' : 'e'); /** - * @type {WebGLUniformLocation} - */ + * @type {WebGLUniformLocation} + */ this.u_opacity = gl.getUniformLocation( program, DEBUG_WEBGL ? 'u_opacity' : 'f'); /** - * @type {WebGLUniformLocation} - */ + * @type {WebGLUniformLocation} + */ this.u_texture = gl.getUniformLocation( program, DEBUG_WEBGL ? 'u_texture' : 'g'); /** - * @type {number} - */ + * @type {number} + */ this.a_position = gl.getAttribLocation( program, DEBUG_WEBGL ? 'a_position' : 'b'); /** - * @type {number} - */ + * @type {number} + */ this.a_texCoord = gl.getAttribLocation( program, DEBUG_WEBGL ? 'a_texCoord' : 'c'); diff --git a/src/ol/renderer/webgl/tilelayershader/Locations.js b/src/ol/renderer/webgl/tilelayershader/Locations.js index 8560c438e2..0b87535cfc 100644 --- a/src/ol/renderer/webgl/tilelayershader/Locations.js +++ b/src/ol/renderer/webgl/tilelayershader/Locations.js @@ -17,26 +17,26 @@ class Locations { constructor(gl, program) { /** - * @type {WebGLUniformLocation} - */ + * @type {WebGLUniformLocation} + */ this.u_tileOffset = gl.getUniformLocation( program, DEBUG_WEBGL ? 'u_tileOffset' : 'd'); /** - * @type {WebGLUniformLocation} - */ + * @type {WebGLUniformLocation} + */ this.u_texture = gl.getUniformLocation( program, DEBUG_WEBGL ? 'u_texture' : 'e'); /** - * @type {number} - */ + * @type {number} + */ this.a_position = gl.getAttribLocation( program, DEBUG_WEBGL ? 'a_position' : 'b'); /** - * @type {number} - */ + * @type {number} + */ this.a_texCoord = gl.getAttribLocation( program, DEBUG_WEBGL ? 'a_texCoord' : 'c'); diff --git a/src/ol/webgl/shaderlocations.mustache b/src/ol/webgl/shaderlocations.mustache index aec7cddff5..988cac4126 100644 --- a/src/ol/webgl/shaderlocations.mustache +++ b/src/ol/webgl/shaderlocations.mustache @@ -18,16 +18,16 @@ class Locations { {{#uniforms}} /** - * @type {WebGLUniformLocation} - */ + * @type {WebGLUniformLocation} + */ this.{{originalName}} = gl.getUniformLocation( program, DEBUG_WEBGL ? '{{originalName}}' : '{{shortName}}'); {{/uniforms}} {{#attributes}} /** - * @type {number} - */ + * @type {number} + */ this.{{originalName}} = gl.getAttribLocation( program, DEBUG_WEBGL ? '{{originalName}}' : '{{shortName}}'); {{/attributes}} From 384920734f9ae549f242131a5fc9dc69a1239702 Mon Sep 17 00:00:00 2001 From: Frederic Junod Date: Tue, 17 Jul 2018 09:19:04 +0200 Subject: [PATCH 005/107] Fix webgl.js path import in webgl shaders --- src/ol/renderer/webgl/defaultmapshader/Locations.js | 2 +- src/ol/renderer/webgl/tilelayershader/Locations.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ol/renderer/webgl/defaultmapshader/Locations.js b/src/ol/renderer/webgl/defaultmapshader/Locations.js index 229f3ba4a4..4e365512a1 100644 --- a/src/ol/renderer/webgl/defaultmapshader/Locations.js +++ b/src/ol/renderer/webgl/defaultmapshader/Locations.js @@ -4,7 +4,7 @@ // This file is automatically generated, do not edit // Run `make shaders` to generate, and commit the result. -import {DEBUG as DEBUG_WEBGL} from '../../../../webgl.js'; +import {DEBUG as DEBUG_WEBGL} from '../../../webgl.js'; /** * @constructor diff --git a/src/ol/renderer/webgl/tilelayershader/Locations.js b/src/ol/renderer/webgl/tilelayershader/Locations.js index 0b87535cfc..8e9f20357e 100644 --- a/src/ol/renderer/webgl/tilelayershader/Locations.js +++ b/src/ol/renderer/webgl/tilelayershader/Locations.js @@ -4,7 +4,7 @@ // This file is automatically generated, do not edit // Run `make shaders` to generate, and commit the result. -import {DEBUG as DEBUG_WEBGL} from '../../../../webgl.js'; +import {DEBUG as DEBUG_WEBGL} from '../../../webgl.js'; /** * @constructor From f2d0b11d24dfea1085cf3d46c603d7a4ce1c7147 Mon Sep 17 00:00:00 2001 From: Frederic Junod Date: Tue, 17 Jul 2018 09:59:00 +0200 Subject: [PATCH 006/107] Move jsdoc constructor comments --- src/ol/AssertionError.js | 8 +- src/ol/Collection.js | 18 +- src/ol/Feature.js | 12 +- src/ol/Geolocation.js | 6 +- src/ol/Graticule.js | 6 +- src/ol/Image.js | 20 +- src/ol/ImageBase.js | 12 +- src/ol/ImageCanvas.js | 16 +- src/ol/ImageTile.js | 16 +- src/ol/Kinetic.js | 13 +- src/ol/Map.js | 6 +- src/ol/MapBrowserEvent.js | 14 +- src/ol/MapBrowserEventHandler.js | 10 +- src/ol/MapBrowserPointerEvent.js | 14 +- src/ol/MapEvent.js | 9 +- src/ol/Object.js | 19 +- src/ol/Observable.js | 2 - src/ol/Overlay.js | 6 +- src/ol/PluggableMap.js | 6 +- src/ol/Tile.js | 10 +- src/ol/TileCache.js | 7 +- src/ol/TileQueue.js | 11 +- src/ol/TileRange.js | 15 +- src/ol/VectorImageTile.js | 40 ++-- src/ol/VectorTile.js | 16 +- src/ol/View.js | 6 +- src/ol/WebGLMap.js | 6 +- src/ol/proj/Projection.js | 213 +++++++++--------- src/ol/proj/epsg3857.js | 5 +- src/ol/proj/epsg4326.js | 6 +- .../circlereplay/defaultshader/Locations.js | 10 +- .../defaultshader/Locations.js | 10 +- .../polygonreplay/defaultshader/Locations.js | 10 +- .../texturereplay/defaultshader/Locations.js | 10 +- .../webgl/defaultmapshader/Locations.js | 10 +- .../webgl/tilelayershader/Locations.js | 10 +- src/ol/webgl/Buffer.js | 32 +-- src/ol/webgl/Context.js | 8 +- src/ol/webgl/Fragment.js | 11 +- src/ol/webgl/Shader.js | 23 +- src/ol/webgl/Vertex.js | 11 +- src/ol/webgl/shaderlocations.mustache | 10 +- 42 files changed, 369 insertions(+), 334 deletions(-) diff --git a/src/ol/AssertionError.js b/src/ol/AssertionError.js index 02e091c2e2..439a4336cd 100644 --- a/src/ol/AssertionError.js +++ b/src/ol/AssertionError.js @@ -7,12 +7,13 @@ import {VERSION, inherits} from './util.js'; * Error object thrown when an assertion failed. This is an ECMA-262 Error, * extended with a `code` property. * @see {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error} - * @constructor * @extends {Error} - * @param {number} code Error code. */ class AssertionError { + /** + * @param {number} code Error code. + */ constructor(code) { const path = VERSION.split('-')[0]; @@ -32,6 +33,9 @@ class AssertionError { */ this.code = code; + /** + * @type {string} + */ this.name = 'AssertionError'; } diff --git a/src/ol/Collection.js b/src/ol/Collection.js index 03cbf2c979..1ee55f95a1 100644 --- a/src/ol/Collection.js +++ b/src/ol/Collection.js @@ -22,13 +22,14 @@ const Property = { * Events emitted by {@link module:ol/Collection~Collection} instances are instances of this * type. * - * @constructor * @extends {module:ol/events/Event} - * @param {module:ol/CollectionEventType} type Type. - * @param {*=} opt_element Element. */ export class CollectionEvent { + /** + * @param {module:ol/CollectionEventType} type Type. + * @param {*=} opt_element Element. + */ constructor(type, opt_element) { Event.call(this, type); @@ -60,15 +61,16 @@ inherits(CollectionEvent, Event); * Collection; they trigger events on the appropriate object, not on the * Collection as a whole. * - * @constructor * @extends {module:ol/Object} - * @fires module:ol/Collection~CollectionEvent - * @param {Array.=} opt_array Array. - * @param {module:ol/Collection~Options=} opt_options Collection options. - * @template T * @api */ class Collection { + + /** + * @param {Array.=} opt_array Array. + * @param {module:ol/Collection~Options=} opt_options Collection options. + * @template T + */ constructor(opt_array, opt_options) { BaseObject.call(this); diff --git a/src/ol/Feature.js b/src/ol/Feature.js index fcd14ebb19..ddc9c3049d 100644 --- a/src/ol/Feature.js +++ b/src/ol/Feature.js @@ -51,15 +51,17 @@ import Style from './style/Style.js'; * var point = feature.getGeometry(); * ``` * - * @constructor * @extends {module:ol/Object} - * @param {module:ol/geom/Geometry|Object.=} opt_geometryOrProperties - * You may pass a Geometry object directly, or an object literal containing - * properties. If you pass an object literal, you may include a Geometry - * associated with a `geometry` key. * @api */ class Feature { + + /** + * @param {module:ol/geom/Geometry|Object.=} opt_geometryOrProperties + * You may pass a Geometry object directly, or an object literal containing + * properties. If you pass an object literal, you may include a Geometry + * associated with a `geometry` key. + */ constructor(opt_geometryOrProperties) { BaseObject.call(this); diff --git a/src/ol/Geolocation.js b/src/ol/Geolocation.js index 80a3902162..8ef22b3882 100644 --- a/src/ol/Geolocation.js +++ b/src/ol/Geolocation.js @@ -44,12 +44,14 @@ import {get as getProjection, getTransformFromProjections, identityTransform} fr * }); * * @fires error - * @constructor * @extends {module:ol/Object} - * @param {module:ol/Geolocation~Options=} opt_options Options. * @api */ class Geolocation { + + /** + * @param {module:ol/Geolocation~Options=} opt_options Options. + */ constructor(opt_options) { BaseObject.call(this); diff --git a/src/ol/Graticule.js b/src/ol/Graticule.js index 375d2df7a0..e0d987a2ef 100644 --- a/src/ol/Graticule.js +++ b/src/ol/Graticule.js @@ -112,11 +112,13 @@ const INTERVALS = [ /** * Render a grid for a coordinate system on a map. - * @constructor - * @param {module:ol/Graticule~Options=} opt_options Options. * @api */ class Graticule { + + /** + * @param {module:ol/Graticule~Options=} opt_options Options. + */ constructor(opt_options) { const options = opt_options || {}; diff --git a/src/ol/Image.js b/src/ol/Image.js index 4c2f62a5fa..984b3227d3 100644 --- a/src/ol/Image.js +++ b/src/ol/Image.js @@ -29,16 +29,18 @@ import {getHeight} from './extent.js'; /** - * @constructor * @extends {module:ol/ImageBase} - * @param {module:ol/extent~Extent} extent Extent. - * @param {number|undefined} resolution Resolution. - * @param {number} pixelRatio Pixel ratio. - * @param {string} src Image source URI. - * @param {?string} crossOrigin Cross origin. - * @param {module:ol/Image~LoadFunction} imageLoadFunction Image load function. */ class ImageWrapper { + + /** + * @param {module:ol/extent~Extent} extent Extent. + * @param {number|undefined} resolution Resolution. + * @param {number} pixelRatio Pixel ratio. + * @param {string} src Image source URI. + * @param {?string} crossOrigin Cross origin. + * @param {module:ol/Image~LoadFunction} imageLoadFunction Image load function. + */ constructor(extent, resolution, pixelRatio, src, crossOrigin, imageLoadFunction) { ImageBase.call(this, extent, resolution, pixelRatio, ImageState.IDLE); @@ -124,9 +126,9 @@ class ImageWrapper { this.changed(); this.imageListenerKeys_ = [ listenOnce(this.image_, EventType.ERROR, - this.handleImageError_, this), + this.handleImageError_, this), listenOnce(this.image_, EventType.LOAD, - this.handleImageLoad_, this) + this.handleImageLoad_, this) ]; this.imageLoadFunction_(this, this.src_); } diff --git a/src/ol/ImageBase.js b/src/ol/ImageBase.js index f6b16e6785..ca8dc2ad76 100644 --- a/src/ol/ImageBase.js +++ b/src/ol/ImageBase.js @@ -6,15 +6,17 @@ import EventTarget from './events/EventTarget.js'; import EventType from './events/EventType.js'; /** - * @constructor * @abstract * @extends {module:ol/events/EventTarget} - * @param {module:ol/extent~Extent} extent Extent. - * @param {number|undefined} resolution Resolution. - * @param {number} pixelRatio Pixel ratio. - * @param {module:ol/ImageState} state State. */ class ImageBase { + + /** + * @param {module:ol/extent~Extent} extent Extent. + * @param {number|undefined} resolution Resolution. + * @param {number} pixelRatio Pixel ratio. + * @param {module:ol/ImageState} state State. + */ constructor(extent, resolution, pixelRatio, state) { EventTarget.call(this); diff --git a/src/ol/ImageCanvas.js b/src/ol/ImageCanvas.js index e95bb8852a..5db87722e9 100644 --- a/src/ol/ImageCanvas.js +++ b/src/ol/ImageCanvas.js @@ -17,16 +17,18 @@ import ImageState from './ImageState.js'; /** - * @constructor * @extends {module:ol/ImageBase} - * @param {module:ol/extent~Extent} extent Extent. - * @param {number} resolution Resolution. - * @param {number} pixelRatio Pixel ratio. - * @param {HTMLCanvasElement} canvas Canvas. - * @param {module:ol/ImageCanvas~Loader=} opt_loader Optional loader function to - * support asynchronous canvas drawing. */ class ImageCanvas { + + /** + * @param {module:ol/extent~Extent} extent Extent. + * @param {number} resolution Resolution. + * @param {number} pixelRatio Pixel ratio. + * @param {HTMLCanvasElement} canvas Canvas. + * @param {module:ol/ImageCanvas~Loader=} opt_loader Optional loader function to + * support asynchronous canvas drawing. + */ constructor(extent, resolution, pixelRatio, canvas, opt_loader) { /** diff --git a/src/ol/ImageTile.js b/src/ol/ImageTile.js index 7ac535885a..c6613bd5a1 100644 --- a/src/ol/ImageTile.js +++ b/src/ol/ImageTile.js @@ -15,16 +15,18 @@ import EventType from './events/EventType.js'; */ /** - * @constructor * @extends {module:ol/Tile} - * @param {module:ol/tilecoord~TileCoord} tileCoord Tile coordinate. - * @param {module:ol/TileState} state State. - * @param {string} src Image source URI. - * @param {?string} crossOrigin Cross origin. - * @param {module:ol/Tile~LoadFunction} tileLoadFunction Tile load function. - * @param {module:ol/Tile~Options=} opt_options Tile options. */ class ImageTile { + + /** + * @param {module:ol/tilecoord~TileCoord} tileCoord Tile coordinate. + * @param {module:ol/TileState} state State. + * @param {string} src Image source URI. + * @param {?string} crossOrigin Cross origin. + * @param {module:ol/Tile~LoadFunction} tileLoadFunction Tile load function. + * @param {module:ol/Tile~Options=} opt_options Tile options. + */ constructor(tileCoord, state, src, crossOrigin, tileLoadFunction, opt_options) { Tile.call(this, tileCoord, state, opt_options); diff --git a/src/ol/Kinetic.js b/src/ol/Kinetic.js index ef5be333e9..bff24954a2 100644 --- a/src/ol/Kinetic.js +++ b/src/ol/Kinetic.js @@ -6,15 +6,16 @@ * @classdesc * Implementation of inertial deceleration for map movement. * - * @constructor - * @param {number} decay Rate of decay (must be negative). - * @param {number} minVelocity Minimum velocity (pixels/millisecond). - * @param {number} delay Delay to consider to calculate the kinetic - * initial values (milliseconds). - * @struct * @api */ class Kinetic { + + /** + * @param {number} decay Rate of decay (must be negative). + * @param {number} minVelocity Minimum velocity (pixels/millisecond). + * @param {number} delay Delay to consider to calculate the kinetic + * initial values (milliseconds). + */ constructor(decay, minVelocity, delay) { /** diff --git a/src/ol/Map.js b/src/ol/Map.js index 23bb531540..0b148878db 100644 --- a/src/ol/Map.js +++ b/src/ol/Map.js @@ -57,9 +57,7 @@ import CanvasVectorTileLayerRenderer from './renderer/canvas/VectorTileLayer.js' * options or added with `addLayer` can be groups, which can contain further * groups, and so on. * - * @constructor * @extends {module:ol/PluggableMap} - * @param {module:ol/PluggableMap~MapOptions} options Map options. * @fires module:ol/MapBrowserEvent~MapBrowserEvent * @fires module:ol/MapEvent~MapEvent * @fires module:ol/render/Event~RenderEvent#postcompose @@ -67,6 +65,10 @@ import CanvasVectorTileLayerRenderer from './renderer/canvas/VectorTileLayer.js' * @api */ class Map { + + /** + * @param {module:ol/PluggableMap~MapOptions} options Map options. + */ constructor(options) { options = assign({}, options); if (!options.controls) { diff --git a/src/ol/MapBrowserEvent.js b/src/ol/MapBrowserEvent.js index e8a7a7e201..18ab508d38 100644 --- a/src/ol/MapBrowserEvent.js +++ b/src/ol/MapBrowserEvent.js @@ -9,15 +9,17 @@ import MapEvent from './MapEvent.js'; * Events emitted as map browser events are instances of this type. * See {@link module:ol/Map~Map} for which events trigger a map browser event. * - * @constructor * @extends {module:ol/MapEvent} - * @param {string} type Event type. - * @param {module:ol/PluggableMap} map Map. - * @param {Event} browserEvent Browser event. - * @param {boolean=} opt_dragging Is the map currently being dragged? - * @param {?module:ol/PluggableMap~FrameState=} opt_frameState Frame state. */ class MapBrowserEvent { + + /** + * @param {string} type Event type. + * @param {module:ol/PluggableMap} map Map. + * @param {Event} browserEvent Browser event. + * @param {boolean=} opt_dragging Is the map currently being dragged? + * @param {?module:ol/PluggableMap~FrameState=} opt_frameState Frame state. + */ constructor(type, map, browserEvent, opt_dragging, opt_frameState) { MapEvent.call(this, type, map, opt_frameState); diff --git a/src/ol/MapBrowserEventHandler.js b/src/ol/MapBrowserEventHandler.js index 48dc9109a2..7778985950 100644 --- a/src/ol/MapBrowserEventHandler.js +++ b/src/ol/MapBrowserEventHandler.js @@ -11,14 +11,14 @@ import PointerEventType from './pointer/EventType.js'; import PointerEventHandler from './pointer/PointerEventHandler.js'; /** - * @param {module:ol/PluggableMap} map The map with the viewport to - * listen to events on. - * @param {number=} moveTolerance The minimal distance the pointer must travel - * to trigger a move. - * @constructor * @extends {module:ol/events/EventTarget} */ class MapBrowserEventHandler { + + /** + * @param {module:ol/PluggableMap} map The map with the viewport to listen to events on. + * @param {number=} moveTolerance The minimal distance the pointer must travel to trigger a move. + */ constructor(map, moveTolerance) { EventTarget.call(this); diff --git a/src/ol/MapBrowserPointerEvent.js b/src/ol/MapBrowserPointerEvent.js index 94cd226353..406ba18cea 100644 --- a/src/ol/MapBrowserPointerEvent.js +++ b/src/ol/MapBrowserPointerEvent.js @@ -5,17 +5,17 @@ import {inherits} from './util.js'; import MapBrowserEvent from './MapBrowserEvent.js'; /** - * @constructor * @extends {module:ol/MapBrowserEvent} - * @param {string} type Event type. - * @param {module:ol/PluggableMap} map Map. - * @param {module:ol/pointer/PointerEvent} pointerEvent Pointer - * event. - * @param {boolean=} opt_dragging Is the map currently being dragged? - * @param {?module:ol/PluggableMap~FrameState=} opt_frameState Frame state. */ class MapBrowserPointerEvent { + /** + * @param {string} type Event type. + * @param {module:ol/PluggableMap} map Map. + * @param {module:ol/pointer/PointerEvent} pointerEvent Pointer event. + * @param {boolean=} opt_dragging Is the map currently being dragged? + * @param {?module:ol/PluggableMap~FrameState=} opt_frameState Frame state. + */ constructor(type, map, pointerEvent, opt_dragging, opt_frameState) { MapBrowserEvent.call(this, type, map, pointerEvent.originalEvent, opt_dragging, diff --git a/src/ol/MapEvent.js b/src/ol/MapEvent.js index 7e6c4d7188..0d9c78a861 100644 --- a/src/ol/MapEvent.js +++ b/src/ol/MapEvent.js @@ -9,14 +9,15 @@ import Event from './events/Event.js'; * Events emitted as map events are instances of this type. * See {@link module:ol/Map~Map} for which events trigger a map event. * - * @constructor * @extends {module:ol/events/Event} - * @param {string} type Event type. - * @param {module:ol/PluggableMap} map Map. - * @param {?module:ol/PluggableMap~FrameState=} opt_frameState Frame state. */ class MapEvent { + /** + * @param {string} type Event type. + * @param {module:ol/PluggableMap} map Map. + * @param {?module:ol/PluggableMap~FrameState=} opt_frameState Frame state. + */ constructor(type, map, opt_frameState) { Event.call(this, type); diff --git a/src/ol/Object.js b/src/ol/Object.js index f7406f6903..47a8864e96 100644 --- a/src/ol/Object.js +++ b/src/ol/Object.js @@ -10,17 +10,16 @@ import {assign} from './obj.js'; /** * @classdesc - * Events emitted by {@link module:ol/Object~BaseObject} instances are instances of - * this type. - * - * @param {string} type The event type. - * @param {string} key The property name. - * @param {*} oldValue The old value for `key`. + * Events emitted by {@link module:ol/Object~BaseObject} instances are instances of this type. * @extends {module:ol/events/Event} - * @constructor */ class ObjectEvent { + /** + * @param {string} type The event type. + * @param {string} key The property name. + * @param {*} oldValue The old value for `key`. + */ constructor(type, key, oldValue) { Event.call(this, type); @@ -86,13 +85,15 @@ inherits(ObjectEvent, Event); * Properties can be deleted by using the unset method. E.g. * object.unset('foo'). * - * @constructor * @extends {module:ol/Observable} - * @param {Object.=} opt_values An object with key-value pairs. * @fires module:ol/Object~ObjectEvent * @api */ class BaseObject { + + /** + * @param {Object.=} opt_values An object with key-value pairs. + */ constructor(opt_values) { Observable.call(this); diff --git a/src/ol/Observable.js b/src/ol/Observable.js index 30edbbbc09..35ae9c39b5 100644 --- a/src/ol/Observable.js +++ b/src/ol/Observable.js @@ -14,10 +14,8 @@ import EventType from './events/EventType.js'; * and unregistration. A generic `change` event is always available through * {@link module:ol/Observable~Observable#changed}. * - * @constructor * @extends {module:ol/events/EventTarget} * @fires module:ol/events/Event~Event - * @struct * @api */ class Observable { diff --git a/src/ol/Overlay.js b/src/ol/Overlay.js index b5fc6d3d0b..cf4551304d 100644 --- a/src/ol/Overlay.js +++ b/src/ol/Overlay.js @@ -93,12 +93,14 @@ const Property = { * popup.setPosition(coordinate); * map.addOverlay(popup); * - * @constructor * @extends {module:ol/Object} - * @param {module:ol/Overlay~Options} options Overlay options. * @api */ class Overlay { + + /** + * @param {module:ol/Overlay~Options} options Overlay options. + */ constructor(options) { BaseObject.call(this); diff --git a/src/ol/PluggableMap.js b/src/ol/PluggableMap.js index 9e8bb2fbc7..d6cbcef038 100644 --- a/src/ol/PluggableMap.js +++ b/src/ol/PluggableMap.js @@ -131,9 +131,7 @@ import {create as createTransform, apply as applyTransform} from './transform.js /** - * @constructor * @extends {module:ol/Object} - * @param {module:ol/PluggableMap~MapOptions} options Map options. * @fires module:ol/MapBrowserEvent~MapBrowserEvent * @fires module:ol/MapEvent~MapEvent * @fires module:ol/render/Event~RenderEvent#postcompose @@ -141,6 +139,10 @@ import {create as createTransform, apply as applyTransform} from './transform.js * @api */ class PluggableMap { + + /** + * @param {module:ol/PluggableMap~MapOptions} options Map options. + */ constructor(options) { BaseObject.call(this); diff --git a/src/ol/Tile.js b/src/ol/Tile.js index c24fd2ce87..6956541b8b 100644 --- a/src/ol/Tile.js +++ b/src/ol/Tile.js @@ -44,14 +44,16 @@ import EventType from './events/EventType.js'; * @classdesc * Base class for tiles. * - * @constructor * @abstract * @extends {module:ol/events/EventTarget} - * @param {module:ol/tilecoord~TileCoord} tileCoord Tile coordinate. - * @param {module:ol/TileState} state State. - * @param {module:ol/Tile~Options=} opt_options Tile options. */ class Tile { + + /** + * @param {module:ol/tilecoord~TileCoord} tileCoord Tile coordinate. + * @param {module:ol/TileState} state State. + * @param {module:ol/Tile~Options=} opt_options Tile options. + */ constructor(tileCoord, state, opt_options) { EventTarget.call(this); diff --git a/src/ol/TileCache.js b/src/ol/TileCache.js index 3db53f81ec..2181f7ee7d 100644 --- a/src/ol/TileCache.js +++ b/src/ol/TileCache.js @@ -6,12 +6,13 @@ import LRUCache from './structs/LRUCache.js'; import {fromKey, getKey} from './tilecoord.js'; /** - * @constructor * @extends {module:ol/structs/LRUCache.} - * @param {number=} opt_highWaterMark High water mark. - * @struct */ class TileCache { + + /** + * @param {number=} opt_highWaterMark High water mark. + */ constructor(opt_highWaterMark) { LRUCache.call(this, opt_highWaterMark); diff --git a/src/ol/TileQueue.js b/src/ol/TileQueue.js index 1c849c8ff8..d566d0260d 100644 --- a/src/ol/TileQueue.js +++ b/src/ol/TileQueue.js @@ -14,15 +14,14 @@ import PriorityQueue from './structs/PriorityQueue.js'; /** - * @constructor * @extends {module:ol/structs/PriorityQueue.} - * @param {module:ol/TileQueue~PriorityFunction} tilePriorityFunction - * Tile priority function. - * @param {function(): ?} tileChangeCallback - * Function called on each tile change event. - * @struct */ class TileQueue { + + /** + * @param {module:ol/TileQueue~PriorityFunction} tilePriorityFunction Tile priority function. + * @param {function(): ?} tileChangeCallback Function called on each tile change event. + */ constructor(tilePriorityFunction, tileChangeCallback) { PriorityQueue.call( diff --git a/src/ol/TileRange.js b/src/ol/TileRange.js index 127b6f7b95..006b38cad3 100644 --- a/src/ol/TileRange.js +++ b/src/ol/TileRange.js @@ -1,18 +1,19 @@ /** * @module ol/TileRange */ + /** * A representation of a contiguous block of tiles. A tile range is specified * by its min/max tile coordinates and is inclusive of coordinates. - * - * @constructor - * @param {number} minX Minimum X. - * @param {number} maxX Maximum X. - * @param {number} minY Minimum Y. - * @param {number} maxY Maximum Y. - * @struct */ class TileRange { + + /** + * @param {number} minX Minimum X. + * @param {number} maxX Maximum X. + * @param {number} minY Minimum Y. + * @param {number} maxY Maximum Y. + */ constructor(minX, maxX, minY, maxY) { /** diff --git a/src/ol/VectorImageTile.js b/src/ol/VectorImageTile.js index 255775a2dd..7d733dc670 100644 --- a/src/ol/VectorImageTile.js +++ b/src/ol/VectorImageTile.js @@ -22,28 +22,30 @@ import {UNDEFINED} from './functions.js'; /** - * @constructor * @extends {module:ol/Tile} - * @param {module:ol/tilecoord~TileCoord} tileCoord Tile coordinate. - * @param {module:ol/TileState} state State. - * @param {number} sourceRevision Source revision. - * @param {module:ol/format/Feature} format Feature format. - * @param {module:ol/Tile~LoadFunction} tileLoadFunction Tile load function. - * @param {module:ol/tilecoord~TileCoord} urlTileCoord Wrapped tile coordinate for source urls. - * @param {module:ol/Tile~UrlFunction} tileUrlFunction Tile url function. - * @param {module:ol/tilegrid/TileGrid} sourceTileGrid Tile grid of the source. - * @param {module:ol/tilegrid/TileGrid} tileGrid Tile grid of the renderer. - * @param {Object.} sourceTiles Source tiles. - * @param {number} pixelRatio Pixel ratio. - * @param {module:ol/proj/Projection} projection Projection. - * @param {function(new: module:ol/VectorTile, module:ol/tilecoord~TileCoord, module:ol/TileState, string, - * module:ol/format/Feature, module:ol/Tile~LoadFunction)} tileClass Class to - * instantiate for source tiles. - * @param {function(this: module:ol/source/VectorTile, module:ol/events/Event)} handleTileChange - * Function to call when a source tile's state changes. - * @param {number} zoom Integer zoom to render the tile for. */ class VectorImageTile { + + /** + * @param {module:ol/tilecoord~TileCoord} tileCoord Tile coordinate. + * @param {module:ol/TileState} state State. + * @param {number} sourceRevision Source revision. + * @param {module:ol/format/Feature} format Feature format. + * @param {module:ol/Tile~LoadFunction} tileLoadFunction Tile load function. + * @param {module:ol/tilecoord~TileCoord} urlTileCoord Wrapped tile coordinate for source urls. + * @param {module:ol/Tile~UrlFunction} tileUrlFunction Tile url function. + * @param {module:ol/tilegrid/TileGrid} sourceTileGrid Tile grid of the source. + * @param {module:ol/tilegrid/TileGrid} tileGrid Tile grid of the renderer. + * @param {Object.} sourceTiles Source tiles. + * @param {number} pixelRatio Pixel ratio. + * @param {module:ol/proj/Projection} projection Projection. + * @param {function(new: module:ol/VectorTile, module:ol/tilecoord~TileCoord, module:ol/TileState, string, + * module:ol/format/Feature, module:ol/Tile~LoadFunction)} tileClass Class to + * instantiate for source tiles. + * @param {function(this: module:ol/source/VectorTile, module:ol/events/Event)} handleTileChange + * Function to call when a source tile's state changes. + * @param {number} zoom Integer zoom to render the tile for. + */ constructor( tileCoord, state, diff --git a/src/ol/VectorTile.js b/src/ol/VectorTile.js index cf7f56c937..5341573e84 100644 --- a/src/ol/VectorTile.js +++ b/src/ol/VectorTile.js @@ -19,16 +19,18 @@ const DEFAULT_EXTENT = [0, 0, 4096, 4096]; */ /** - * @constructor * @extends {module:ol/Tile} - * @param {module:ol/tilecoord~TileCoord} tileCoord Tile coordinate. - * @param {module:ol/TileState} state State. - * @param {string} src Data source url. - * @param {module:ol/format/Feature} format Feature format. - * @param {module:ol/Tile~LoadFunction} tileLoadFunction Tile load function. - * @param {module:ol/Tile~Options=} opt_options Tile options. */ class VectorTile { + + /** + * @param {module:ol/tilecoord~TileCoord} tileCoord Tile coordinate. + * @param {module:ol/TileState} state State. + * @param {string} src Data source url. + * @param {module:ol/format/Feature} format Feature format. + * @param {module:ol/Tile~LoadFunction} tileLoadFunction Tile load function. + * @param {module:ol/Tile~Options=} opt_options Tile options. + */ constructor(tileCoord, state, src, format, tileLoadFunction, opt_options) { Tile.call(this, tileCoord, state, opt_options); diff --git a/src/ol/View.js b/src/ol/View.js index 8983eb1a34..1e34fe7d4c 100644 --- a/src/ol/View.js +++ b/src/ol/View.js @@ -224,12 +224,14 @@ const DEFAULT_MIN_ZOOM = 0; * The *center constraint* is determined by the `extent` option. By * default the center is not constrained at all. * - * @constructor * @extends {module:ol/Object} - * @param {module:ol/View~ViewOptions=} opt_options View options. * @api */ class View { + + /** + * @param {module:ol/View~ViewOptions=} opt_options View options. + */ constructor(opt_options) { BaseObject.call(this); diff --git a/src/ol/WebGLMap.js b/src/ol/WebGLMap.js index 2717ec16c2..97d29032af 100644 --- a/src/ol/WebGLMap.js +++ b/src/ol/WebGLMap.js @@ -57,9 +57,7 @@ import WebGLVectorLayerRenderer from './renderer/webgl/VectorLayer.js'; * {@link module:ol/layer/Base}, so layers entered in the options or added * with `addLayer` can be groups, which can contain further groups, and so on. * - * @constructor * @extends {module:ol/PluggableMap} - * @param {module:ol/PluggableMap~MapOptions} options Map options. * @fires module:ol/MapBrowserEvent~MapBrowserEvent * @fires module:ol/MapEvent~MapEvent * @fires module:ol/render/Event~RenderEvent#postcompose @@ -67,6 +65,10 @@ import WebGLVectorLayerRenderer from './renderer/webgl/VectorLayer.js'; * @api */ class WebGLMap { + + /** + * @param {module:ol/PluggableMap~MapOptions} options Map options. + */ constructor(options) { options = assign({}, options); if (!options.controls) { diff --git a/src/ol/proj/Projection.js b/src/ol/proj/Projection.js index 6a59f4847d..33614141eb 100644 --- a/src/ol/proj/Projection.js +++ b/src/ol/proj/Projection.js @@ -49,222 +49,223 @@ import {METERS_PER_UNIT} from '../proj/Units.js'; * be added using `proj4.defs()`. After all required projection definitions are * added, call the {@link module:ol/proj/proj4~register} function. * - * @constructor - * @param {module:ol/proj/Projection~Options} options Projection options. - * @struct * @api */ class Projection { + + /** + * @param {module:ol/proj/Projection~Options} options Projection options. + */ constructor(options) { /** - * @private - * @type {string} - */ + * @private + * @type {string} + */ this.code_ = options.code; /** - * Units of projected coordinates. When set to `TILE_PIXELS`, a - * `this.extent_` and `this.worldExtent_` must be configured properly for each - * tile. - * @private - * @type {module:ol/proj/Units} - */ + * Units of projected coordinates. When set to `TILE_PIXELS`, a + * `this.extent_` and `this.worldExtent_` must be configured properly for each + * tile. + * @private + * @type {module:ol/proj/Units} + */ this.units_ = /** @type {module:ol/proj/Units} */ (options.units); /** - * Validity extent of the projection in projected coordinates. For projections - * with `TILE_PIXELS` units, this is the extent of the tile in - * tile pixel space. - * @private - * @type {module:ol/extent~Extent} - */ + * Validity extent of the projection in projected coordinates. For projections + * with `TILE_PIXELS` units, this is the extent of the tile in + * tile pixel space. + * @private + * @type {module:ol/extent~Extent} + */ this.extent_ = options.extent !== undefined ? options.extent : null; /** - * Extent of the world in EPSG:4326. For projections with - * `TILE_PIXELS` units, this is the extent of the tile in - * projected coordinate space. - * @private - * @type {module:ol/extent~Extent} - */ + * Extent of the world in EPSG:4326. For projections with + * `TILE_PIXELS` units, this is the extent of the tile in + * projected coordinate space. + * @private + * @type {module:ol/extent~Extent} + */ this.worldExtent_ = options.worldExtent !== undefined ? options.worldExtent : null; /** - * @private - * @type {string} - */ + * @private + * @type {string} + */ this.axisOrientation_ = options.axisOrientation !== undefined ? options.axisOrientation : 'enu'; /** - * @private - * @type {boolean} - */ + * @private + * @type {boolean} + */ this.global_ = options.global !== undefined ? options.global : false; /** - * @private - * @type {boolean} - */ + * @private + * @type {boolean} + */ this.canWrapX_ = !!(this.global_ && this.extent_); /** - * @private - * @type {function(number, module:ol/coordinate~Coordinate):number|undefined} - */ + * @private + * @type {function(number, module:ol/coordinate~Coordinate):number|undefined} + */ this.getPointResolutionFunc_ = options.getPointResolution; /** - * @private - * @type {module:ol/tilegrid/TileGrid} - */ + * @private + * @type {module:ol/tilegrid/TileGrid} + */ this.defaultTileGrid_ = null; /** - * @private - * @type {number|undefined} - */ + * @private + * @type {number|undefined} + */ this.metersPerUnit_ = options.metersPerUnit; } /** - * @return {boolean} The projection is suitable for wrapping the x-axis - */ + * @return {boolean} The projection is suitable for wrapping the x-axis + */ canWrapX() { return this.canWrapX_; } /** - * Get the code for this projection, e.g. 'EPSG:4326'. - * @return {string} Code. - * @api - */ + * Get the code for this projection, e.g. 'EPSG:4326'. + * @return {string} Code. + * @api + */ getCode() { return this.code_; } /** - * Get the validity extent for this projection. - * @return {module:ol/extent~Extent} Extent. - * @api - */ + * Get the validity extent for this projection. + * @return {module:ol/extent~Extent} Extent. + * @api + */ getExtent() { return this.extent_; } /** - * Get the units of this projection. - * @return {module:ol/proj/Units} Units. - * @api - */ + * Get the units of this projection. + * @return {module:ol/proj/Units} Units. + * @api + */ getUnits() { return this.units_; } /** - * Get the amount of meters per unit of this projection. If the projection is - * not configured with `metersPerUnit` or a units identifier, the return is - * `undefined`. - * @return {number|undefined} Meters. - * @api - */ + * Get the amount of meters per unit of this projection. If the projection is + * not configured with `metersPerUnit` or a units identifier, the return is + * `undefined`. + * @return {number|undefined} Meters. + * @api + */ getMetersPerUnit() { return this.metersPerUnit_ || METERS_PER_UNIT[this.units_]; } /** - * Get the world extent for this projection. - * @return {module:ol/extent~Extent} Extent. - * @api - */ + * Get the world extent for this projection. + * @return {module:ol/extent~Extent} Extent. + * @api + */ getWorldExtent() { return this.worldExtent_; } /** - * Get the axis orientation of this projection. - * Example values are: - * enu - the default easting, northing, elevation. - * neu - northing, easting, up - useful for "lat/long" geographic coordinates, - * or south orientated transverse mercator. - * wnu - westing, northing, up - some planetary coordinate systems have - * "west positive" coordinate systems - * @return {string} Axis orientation. - * @api - */ + * Get the axis orientation of this projection. + * Example values are: + * enu - the default easting, northing, elevation. + * neu - northing, easting, up - useful for "lat/long" geographic coordinates, + * or south orientated transverse mercator. + * wnu - westing, northing, up - some planetary coordinate systems have + * "west positive" coordinate systems + * @return {string} Axis orientation. + * @api + */ getAxisOrientation() { return this.axisOrientation_; } /** - * Is this projection a global projection which spans the whole world? - * @return {boolean} Whether the projection is global. - * @api - */ + * Is this projection a global projection which spans the whole world? + * @return {boolean} Whether the projection is global. + * @api + */ isGlobal() { return this.global_; } /** - * Set if the projection is a global projection which spans the whole world - * @param {boolean} global Whether the projection is global. - * @api - */ + * Set if the projection is a global projection which spans the whole world + * @param {boolean} global Whether the projection is global. + * @api + */ setGlobal(global) { this.global_ = global; this.canWrapX_ = !!(global && this.extent_); } /** - * @return {module:ol/tilegrid/TileGrid} The default tile grid. - */ + * @return {module:ol/tilegrid/TileGrid} The default tile grid. + */ getDefaultTileGrid() { return this.defaultTileGrid_; } /** - * @param {module:ol/tilegrid/TileGrid} tileGrid The default tile grid. - */ + * @param {module:ol/tilegrid/TileGrid} tileGrid The default tile grid. + */ setDefaultTileGrid(tileGrid) { this.defaultTileGrid_ = tileGrid; } /** - * Set the validity extent for this projection. - * @param {module:ol/extent~Extent} extent Extent. - * @api - */ + * Set the validity extent for this projection. + * @param {module:ol/extent~Extent} extent Extent. + * @api + */ setExtent(extent) { this.extent_ = extent; this.canWrapX_ = !!(this.global_ && extent); } /** - * Set the world extent for this projection. - * @param {module:ol/extent~Extent} worldExtent World extent - * [minlon, minlat, maxlon, maxlat]. - * @api - */ + * Set the world extent for this projection. + * @param {module:ol/extent~Extent} worldExtent World extent + * [minlon, minlat, maxlon, maxlat]. + * @api + */ setWorldExtent(worldExtent) { this.worldExtent_ = worldExtent; } /** - * Set the getPointResolution function (see {@link module:ol/proj~getPointResolution} - * for this projection. - * @param {function(number, module:ol/coordinate~Coordinate):number} func Function - * @api - */ + * Set the getPointResolution function (see {@link module:ol/proj~getPointResolution} + * for this projection. + * @param {function(number, module:ol/coordinate~Coordinate):number} func Function + * @api + */ setGetPointResolution(func) { this.getPointResolutionFunc_ = func; } /** - * Get the custom point resolution function for this projection (if set). - * @return {function(number, module:ol/coordinate~Coordinate):number|undefined} The custom point - * resolution function (if set). - */ + * Get the custom point resolution function for this projection (if set). + * @return {function(number, module:ol/coordinate~Coordinate):number|undefined} The custom point + * resolution function (if set). + */ getPointResolutionFunc() { return this.getPointResolutionFunc_; } diff --git a/src/ol/proj/epsg3857.js b/src/ol/proj/epsg3857.js index c5e96cc7b0..0235f69223 100644 --- a/src/ol/proj/epsg3857.js +++ b/src/ol/proj/epsg3857.js @@ -44,12 +44,13 @@ export const WORLD_EXTENT = [-180, -85, 180, 85]; * @classdesc * Projection object for web/spherical Mercator (EPSG:3857). * - * @constructor * @extends {module:ol/proj/Projection} - * @param {string} code Code. */ class EPSG3857Projection { + /** + * @param {string} code Code. + */ constructor(code) { Projection.call(this, { code: code, diff --git a/src/ol/proj/epsg4326.js b/src/ol/proj/epsg4326.js index 39363d29dc..d626467d91 100644 --- a/src/ol/proj/epsg4326.js +++ b/src/ol/proj/epsg4326.js @@ -41,11 +41,13 @@ export const METERS_PER_UNIT = Math.PI * RADIUS / 180; * * @constructor * @extends {module:ol/proj/Projection} - * @param {string} code Code. - * @param {string=} opt_axisOrientation Axis orientation. */ class EPSG4326Projection { + /** + * @param {string} code Code. + * @param {string=} opt_axisOrientation Axis orientation. + */ constructor(code, opt_axisOrientation) { Projection.call(this, { code: code, diff --git a/src/ol/render/webgl/circlereplay/defaultshader/Locations.js b/src/ol/render/webgl/circlereplay/defaultshader/Locations.js index a0c370e082..33a3a749f0 100644 --- a/src/ol/render/webgl/circlereplay/defaultshader/Locations.js +++ b/src/ol/render/webgl/circlereplay/defaultshader/Locations.js @@ -6,14 +6,12 @@ import {DEBUG as DEBUG_WEBGL} from '../../../../webgl.js'; -/** - * @constructor - * @param {WebGLRenderingContext} gl GL. - * @param {WebGLProgram} program Program. - * @struct - */ class Locations { + /** + * @param {WebGLRenderingContext} gl GL. + * @param {WebGLProgram} program Program. + */ constructor(gl, program) { /** diff --git a/src/ol/render/webgl/linestringreplay/defaultshader/Locations.js b/src/ol/render/webgl/linestringreplay/defaultshader/Locations.js index 1b9a9f6c55..e2af474caa 100644 --- a/src/ol/render/webgl/linestringreplay/defaultshader/Locations.js +++ b/src/ol/render/webgl/linestringreplay/defaultshader/Locations.js @@ -6,14 +6,12 @@ import {DEBUG as DEBUG_WEBGL} from '../../../../webgl.js'; -/** - * @constructor - * @param {WebGLRenderingContext} gl GL. - * @param {WebGLProgram} program Program. - * @struct - */ class Locations { + /** + * @param {WebGLRenderingContext} gl GL. + * @param {WebGLProgram} program Program. + */ constructor(gl, program) { /** diff --git a/src/ol/render/webgl/polygonreplay/defaultshader/Locations.js b/src/ol/render/webgl/polygonreplay/defaultshader/Locations.js index 19482c6188..cbaeb1b07a 100644 --- a/src/ol/render/webgl/polygonreplay/defaultshader/Locations.js +++ b/src/ol/render/webgl/polygonreplay/defaultshader/Locations.js @@ -6,14 +6,12 @@ import {DEBUG as DEBUG_WEBGL} from '../../../../webgl.js'; -/** - * @constructor - * @param {WebGLRenderingContext} gl GL. - * @param {WebGLProgram} program Program. - * @struct - */ class Locations { + /** + * @param {WebGLRenderingContext} gl GL. + * @param {WebGLProgram} program Program. + */ constructor(gl, program) { /** diff --git a/src/ol/render/webgl/texturereplay/defaultshader/Locations.js b/src/ol/render/webgl/texturereplay/defaultshader/Locations.js index d8890f7c85..7628dc83aa 100644 --- a/src/ol/render/webgl/texturereplay/defaultshader/Locations.js +++ b/src/ol/render/webgl/texturereplay/defaultshader/Locations.js @@ -6,14 +6,12 @@ import {DEBUG as DEBUG_WEBGL} from '../../../../webgl.js'; -/** - * @constructor - * @param {WebGLRenderingContext} gl GL. - * @param {WebGLProgram} program Program. - * @struct - */ class Locations { + /** + * @param {WebGLRenderingContext} gl GL. + * @param {WebGLProgram} program Program. + */ constructor(gl, program) { /** diff --git a/src/ol/renderer/webgl/defaultmapshader/Locations.js b/src/ol/renderer/webgl/defaultmapshader/Locations.js index 4e365512a1..e24fa2cfc2 100644 --- a/src/ol/renderer/webgl/defaultmapshader/Locations.js +++ b/src/ol/renderer/webgl/defaultmapshader/Locations.js @@ -6,14 +6,12 @@ import {DEBUG as DEBUG_WEBGL} from '../../../webgl.js'; -/** - * @constructor - * @param {WebGLRenderingContext} gl GL. - * @param {WebGLProgram} program Program. - * @struct - */ class Locations { + /** + * @param {WebGLRenderingContext} gl GL. + * @param {WebGLProgram} program Program. + */ constructor(gl, program) { /** diff --git a/src/ol/renderer/webgl/tilelayershader/Locations.js b/src/ol/renderer/webgl/tilelayershader/Locations.js index 8e9f20357e..3ef15401fb 100644 --- a/src/ol/renderer/webgl/tilelayershader/Locations.js +++ b/src/ol/renderer/webgl/tilelayershader/Locations.js @@ -6,14 +6,12 @@ import {DEBUG as DEBUG_WEBGL} from '../../../webgl.js'; -/** - * @constructor - * @param {WebGLRenderingContext} gl GL. - * @param {WebGLProgram} program Program. - * @struct - */ class Locations { + /** + * @param {WebGLRenderingContext} gl GL. + * @param {WebGLProgram} program Program. + */ constructor(gl, program) { /** diff --git a/src/ol/webgl/Buffer.js b/src/ol/webgl/Buffer.js index 2ad0dbba75..f9d6786a21 100644 --- a/src/ol/webgl/Buffer.js +++ b/src/ol/webgl/Buffer.js @@ -12,39 +12,39 @@ const BufferUsage = { DYNAMIC_DRAW: DYNAMIC_DRAW }; -/** - * @constructor - * @param {Array.=} opt_arr Array. - * @param {number=} opt_usage Usage. - * @struct - */ + class WebGLBuffer { + + /** + * @param {Array.=} opt_arr Array. + * @param {number=} opt_usage Usage. + */ constructor(opt_arr, opt_usage) { /** - * @private - * @type {Array.} - */ + * @private + * @type {Array.} + */ this.arr_ = opt_arr !== undefined ? opt_arr : []; /** - * @private - * @type {number} - */ + * @private + * @type {number} + */ this.usage_ = opt_usage !== undefined ? opt_usage : BufferUsage.STATIC_DRAW; } /** - * @return {Array.} Array. - */ + * @return {Array.} Array. + */ getArray() { return this.arr_; } /** - * @return {number} Usage. - */ + * @return {number} Usage. + */ getUsage() { return this.usage_; } diff --git a/src/ol/webgl/Context.js b/src/ol/webgl/Context.js index 4ec4130ab4..ed04d45e6a 100644 --- a/src/ol/webgl/Context.js +++ b/src/ol/webgl/Context.js @@ -22,12 +22,14 @@ import ContextEventType from '../webgl/ContextEventType.js'; * @classdesc * A WebGL context for accessing low-level WebGL capabilities. * - * @constructor * @extends {module:ol/Disposable} - * @param {HTMLCanvasElement} canvas Canvas. - * @param {WebGLRenderingContext} gl GL. */ class WebGLContext { + + /** + * @param {HTMLCanvasElement} canvas Canvas. + * @param {WebGLRenderingContext} gl GL. + */ constructor(canvas, gl) { /** diff --git a/src/ol/webgl/Fragment.js b/src/ol/webgl/Fragment.js index 1775950fe2..809f5726d0 100644 --- a/src/ol/webgl/Fragment.js +++ b/src/ol/webgl/Fragment.js @@ -6,19 +6,20 @@ import {FRAGMENT_SHADER} from '../webgl.js'; import WebGLShader from '../webgl/Shader.js'; /** - * @constructor * @extends {module:ol/webgl/Shader} - * @param {string} source Source. - * @struct */ class WebGLFragment { + + /** + * @param {string} source Source. + */ constructor(source) { WebGLShader.call(this, source); } /** - * @inheritDoc - */ + * @inheritDoc + */ getType() { return FRAGMENT_SHADER; } diff --git a/src/ol/webgl/Shader.js b/src/ol/webgl/Shader.js index 0ef59b4626..aacd329b2a 100644 --- a/src/ol/webgl/Shader.js +++ b/src/ol/webgl/Shader.js @@ -4,31 +4,32 @@ import {FALSE} from '../functions.js'; /** - * @constructor * @abstract - * @param {string} source Source. - * @struct */ class WebGLShader { + + /** + * @param {string} source Source. + */ constructor(source) { /** - * @private - * @type {string} - */ + * @private + * @type {string} + */ this.source_ = source; } /** - * @abstract - * @return {number} Type. - */ + * @abstract + * @return {number} Type. + */ getType() {} /** - * @return {string} Source. - */ + * @return {string} Source. + */ getSource() { return this.source_; } diff --git a/src/ol/webgl/Vertex.js b/src/ol/webgl/Vertex.js index bdf794b86e..eadd8dd316 100644 --- a/src/ol/webgl/Vertex.js +++ b/src/ol/webgl/Vertex.js @@ -6,19 +6,20 @@ import {VERTEX_SHADER} from '../webgl.js'; import WebGLShader from '../webgl/Shader.js'; /** - * @constructor * @extends {module:ol/webgl/Shader} - * @param {string} source Source. - * @struct */ class WebGLVertex { + + /** + * @param {string} source Source. + */ constructor(source) { WebGLShader.call(this, source); } /** - * @inheritDoc - */ + * @inheritDoc + */ getType() { return VERTEX_SHADER; } diff --git a/src/ol/webgl/shaderlocations.mustache b/src/ol/webgl/shaderlocations.mustache index 988cac4126..0b979bd3a1 100644 --- a/src/ol/webgl/shaderlocations.mustache +++ b/src/ol/webgl/shaderlocations.mustache @@ -6,14 +6,12 @@ import {DEBUG as DEBUG_WEBGL} from '../../../../webgl.js'; -/** - * @constructor - * @param {WebGLRenderingContext} gl GL. - * @param {WebGLProgram} program Program. - * @struct - */ class Locations { + /** + * @param {WebGLRenderingContext} gl GL. + * @param {WebGLProgram} program Program. + */ constructor(gl, program) { {{#uniforms}} From 540b1793e79231650b0740961603440e316fb0bd Mon Sep 17 00:00:00 2001 From: Frederic Junod Date: Tue, 17 Jul 2018 10:20:26 +0200 Subject: [PATCH 007/107] Move jsdoc constructor comments --- src/ol/format/EsriJSON.js | 6 +- src/ol/format/Feature.js | 149 ++++++++++++++--------------- src/ol/format/GML2.js | 6 +- src/ol/format/GML3.js | 7 +- src/ol/format/GMLBase.js | 7 +- src/ol/format/GPX.js | 6 +- src/ol/format/GeoJSON.js | 6 +- src/ol/format/IGC.js | 6 +- src/ol/format/JSONFeature.js | 109 +++++++++++---------- src/ol/format/KML.js | 6 +- src/ol/format/MVT.js | 5 +- src/ol/format/OSMXML.js | 1 - src/ol/format/OWS.js | 1 - src/ol/format/Polyline.js | 6 +- src/ol/format/TextFeature.js | 113 +++++++++++----------- src/ol/format/TopoJSON.js | 6 +- src/ol/format/WFS.js | 6 +- src/ol/format/WKT.js | 18 ++-- src/ol/format/WMSCapabilities.js | 1 - src/ol/format/WMSGetFeatureInfo.js | 6 +- src/ol/format/WMTSCapabilities.js | 1 - src/ol/format/XML.js | 24 +++-- src/ol/format/XMLFeature.js | 1 - 23 files changed, 259 insertions(+), 238 deletions(-) diff --git a/src/ol/format/EsriJSON.js b/src/ol/format/EsriJSON.js index 4b0ad55729..0f388a10b2 100644 --- a/src/ol/format/EsriJSON.js +++ b/src/ol/format/EsriJSON.js @@ -58,12 +58,14 @@ GEOMETRY_WRITERS[GeometryType.MULTI_POLYGON] = writeMultiPolygonGeometry; * @classdesc * Feature format for reading and writing data in the EsriJSON format. * - * @constructor * @extends {module:ol/format/JSONFeature} - * @param {module:ol/format/EsriJSON~Options=} opt_options Options. * @api */ class EsriJSON { + + /** + * @param {module:ol/format/EsriJSON~Options=} opt_options Options. + */ constructor(opt_options) { const options = opt_options ? opt_options : {}; diff --git a/src/ol/format/Feature.js b/src/ol/format/Feature.js index ce9c8a7e29..f5b49cdc9f 100644 --- a/src/ol/format/Feature.js +++ b/src/ol/format/Feature.js @@ -56,7 +56,6 @@ import {get as getProjection, equivalent as equivalentProjection, transformExten * {@link module:ol/Feature~Feature} objects from a variety of commonly used geospatial * file formats. See the documentation for each format for more details. * - * @constructor * @abstract * @api */ @@ -64,26 +63,26 @@ class FeatureFormat { constructor() { /** - * @protected - * @type {module:ol/proj/Projection} - */ + * @protected + * @type {module:ol/proj/Projection} + */ this.dataProjection = null; /** - * @protected - * @type {module:ol/proj/Projection} - */ + * @protected + * @type {module:ol/proj/Projection} + */ this.defaultFeatureProjection = null; } /** - * Adds the data projection to the read options. - * @param {Document|Node|Object|string} source Source. - * @param {module:ol/format/Feature~ReadOptions=} opt_options Options. - * @return {module:ol/format/Feature~ReadOptions|undefined} Options. - * @protected - */ + * Adds the data projection to the read options. + * @param {Document|Node|Object|string} source Source. + * @param {module:ol/format/Feature~ReadOptions=} opt_options Options. + * @return {module:ol/format/Feature~ReadOptions|undefined} Options. + * @protected + */ getReadOptions(source, opt_options) { let options; if (opt_options) { @@ -97,14 +96,14 @@ class FeatureFormat { } /** - * Sets the `dataProjection` on the options, if no `dataProjection` - * is set. - * @param {module:ol/format/Feature~WriteOptions|module:ol/format/Feature~ReadOptions|undefined} options - * Options. - * @protected - * @return {module:ol/format/Feature~WriteOptions|module:ol/format/Feature~ReadOptions|undefined} - * Updated options. - */ + * Sets the `dataProjection` on the options, if no `dataProjection` + * is set. + * @param {module:ol/format/Feature~WriteOptions|module:ol/format/Feature~ReadOptions|undefined} options + * Options. + * @protected + * @return {module:ol/format/Feature~WriteOptions|module:ol/format/Feature~ReadOptions|undefined} + * Updated options. + */ adaptOptions(options) { return assign({ dataProjection: this.dataProjection, @@ -113,86 +112,86 @@ class FeatureFormat { } /** - * Get the extent from the source of the last {@link readFeatures} call. - * @return {module:ol/extent~Extent} Tile extent. - */ + * Get the extent from the source of the last {@link readFeatures} call. + * @return {module:ol/extent~Extent} Tile extent. + */ getLastExtent() { return null; } /** - * @abstract - * @return {module:ol/format/FormatType} Format. - */ + * @abstract + * @return {module:ol/format/FormatType} Format. + */ getType() {} /** - * Read a single feature from a source. - * - * @abstract - * @param {Document|Node|Object|string} source Source. - * @param {module:ol/format/Feature~ReadOptions=} opt_options Read options. - * @return {module:ol/Feature} Feature. - */ + * Read a single feature from a source. + * + * @abstract + * @param {Document|Node|Object|string} source Source. + * @param {module:ol/format/Feature~ReadOptions=} opt_options Read options. + * @return {module:ol/Feature} Feature. + */ readFeature(source, opt_options) {} /** - * Read all features from a source. - * - * @abstract - * @param {Document|Node|ArrayBuffer|Object|string} source Source. - * @param {module:ol/format/Feature~ReadOptions=} opt_options Read options. - * @return {Array.} Features. - */ + * Read all features from a source. + * + * @abstract + * @param {Document|Node|ArrayBuffer|Object|string} source Source. + * @param {module:ol/format/Feature~ReadOptions=} opt_options Read options. + * @return {Array.} Features. + */ readFeatures(source, opt_options) {} /** - * Read a single geometry from a source. - * - * @abstract - * @param {Document|Node|Object|string} source Source. - * @param {module:ol/format/Feature~ReadOptions=} opt_options Read options. - * @return {module:ol/geom/Geometry} Geometry. - */ + * Read a single geometry from a source. + * + * @abstract + * @param {Document|Node|Object|string} source Source. + * @param {module:ol/format/Feature~ReadOptions=} opt_options Read options. + * @return {module:ol/geom/Geometry} Geometry. + */ readGeometry(source, opt_options) {} /** - * Read the projection from a source. - * - * @abstract - * @param {Document|Node|Object|string} source Source. - * @return {module:ol/proj/Projection} Projection. - */ + * Read the projection from a source. + * + * @abstract + * @param {Document|Node|Object|string} source Source. + * @return {module:ol/proj/Projection} Projection. + */ readProjection(source) {} /** - * Encode a feature in this format. - * - * @abstract - * @param {module:ol/Feature} feature Feature. - * @param {module:ol/format/Feature~WriteOptions=} opt_options Write options. - * @return {string} Result. - */ + * Encode a feature in this format. + * + * @abstract + * @param {module:ol/Feature} feature Feature. + * @param {module:ol/format/Feature~WriteOptions=} opt_options Write options. + * @return {string} Result. + */ writeFeature(feature, opt_options) {} /** - * Encode an array of features in this format. - * - * @abstract - * @param {Array.} features Features. - * @param {module:ol/format/Feature~WriteOptions=} opt_options Write options. - * @return {string} Result. - */ + * Encode an array of features in this format. + * + * @abstract + * @param {Array.} features Features. + * @param {module:ol/format/Feature~WriteOptions=} opt_options Write options. + * @return {string} Result. + */ writeFeatures(features, opt_options) {} /** - * Write a single geometry in this format. - * - * @abstract - * @param {module:ol/geom/Geometry} geometry Geometry. - * @param {module:ol/format/Feature~WriteOptions=} opt_options Write options. - * @return {string} Result. - */ + * Write a single geometry in this format. + * + * @abstract + * @param {module:ol/geom/Geometry} geometry Geometry. + * @param {module:ol/format/Feature~WriteOptions=} opt_options Write options. + * @return {string} Result. + */ writeGeometry(geometry, opt_options) {} } diff --git a/src/ol/format/GML2.js b/src/ol/format/GML2.js index 7fc311bd06..31d878c000 100644 --- a/src/ol/format/GML2.js +++ b/src/ol/format/GML2.js @@ -37,12 +37,14 @@ const MULTIGEOMETRY_TO_MEMBER_NODENAME = { * Feature format for reading and writing data in the GML format, * version 2.1.2. * - * @constructor - * @param {module:ol/format/GMLBase~Options=} opt_options Optional configuration object. * @extends {module:ol/format/GMLBase} * @api */ class GML2 { + + /** + * @param {module:ol/format/GMLBase~Options=} opt_options Optional configuration object. + */ constructor(opt_options) { const options = /** @type {module:ol/format/GMLBase~Options} */ (opt_options ? opt_options : {}); diff --git a/src/ol/format/GML3.js b/src/ol/format/GML3.js index 25dd096c18..cd7090a5d0 100644 --- a/src/ol/format/GML3.js +++ b/src/ol/format/GML3.js @@ -48,13 +48,14 @@ const MULTIGEOMETRY_TO_MEMBER_NODENAME = { * version 3.1.1. * Currently only supports GML 3.1.1 Simple Features profile. * - * @constructor - * @param {module:ol/format/GMLBase~Options=} opt_options - * Optional configuration object. * @extends {module:ol/format/GMLBase} * @api */ class GML3 { + + /** + * @param {module:ol/format/GMLBase~Options=} opt_options Optional configuration object. + */ constructor(opt_options) { const options = /** @type {module:ol/format/GMLBase~Options} */ (opt_options ? opt_options : {}); diff --git a/src/ol/format/GMLBase.js b/src/ol/format/GMLBase.js index bb5c73c372..38267f92ea 100644 --- a/src/ol/format/GMLBase.js +++ b/src/ol/format/GMLBase.js @@ -83,13 +83,14 @@ const ONLY_WHITESPACE_RE = /^[\s\xa0]*$/; * This class cannot be instantiated, it contains only base content that * is shared with versioned format classes GML2 and GML3. * - * @constructor * @abstract - * @param {module:ol/format/GMLBase~Options=} opt_options - * Optional configuration object. * @extends {module:ol/format/XMLFeature} */ class GMLBase { + + /** + * @param {module:ol/format/GMLBase~Options=} opt_options Optional configuration object. + */ constructor(opt_options) { const options = /** @type {module:ol/format/GMLBase~Options} */ (opt_options ? opt_options : {}); diff --git a/src/ol/format/GPX.js b/src/ol/format/GPX.js index f68a4b381f..5981678cba 100644 --- a/src/ol/format/GPX.js +++ b/src/ol/format/GPX.js @@ -103,12 +103,14 @@ const GPX_SERIALIZERS = makeStructureNS( * @classdesc * Feature format for reading and writing data in the GPX format. * - * @constructor * @extends {module:ol/format/XMLFeature} - * @param {module:ol/format/GPX~Options=} opt_options Options. * @api */ class GPX { + + /** + * @param {module:ol/format/GPX~Options=} opt_options Options. + */ constructor(opt_options) { const options = opt_options ? opt_options : {}; diff --git a/src/ol/format/GeoJSON.js b/src/ol/format/GeoJSON.js index f3392fdfdf..67a3de5d38 100644 --- a/src/ol/format/GeoJSON.js +++ b/src/ol/format/GeoJSON.js @@ -37,12 +37,14 @@ import {get as getProjection} from '../proj.js'; * @classdesc * Feature format for reading and writing data in the GeoJSON format. * - * @constructor * @extends {module:ol/format/JSONFeature} - * @param {module:ol/format/GeoJSON~Options=} opt_options Options. * @api */ class GeoJSON { + + /** + * @param {module:ol/format/GeoJSON~Options=} opt_options Options. + */ constructor(opt_options) { const options = opt_options ? opt_options : {}; diff --git a/src/ol/format/IGC.js b/src/ol/format/IGC.js index 5a23b38b14..8c21735640 100644 --- a/src/ol/format/IGC.js +++ b/src/ol/format/IGC.js @@ -61,12 +61,14 @@ const NEWLINE_RE = /\r\n|\r|\n/; * @classdesc * Feature format for `*.igc` flight recording files. * - * @constructor * @extends {module:ol/format/TextFeature} - * @param {module:ol/format/IGC~Options=} opt_options Options. * @api */ class IGC { + + /** + * @param {module:ol/format/IGC~Options=} opt_options Options. + */ constructor(opt_options) { const options = opt_options ? opt_options : {}; diff --git a/src/ol/format/JSONFeature.js b/src/ol/format/JSONFeature.js index 45c3039a31..682bdc87e5 100644 --- a/src/ol/format/JSONFeature.js +++ b/src/ol/format/JSONFeature.js @@ -11,7 +11,6 @@ import FormatType from '../format/FormatType.js'; * instantiated in apps. * Base class for JSON feature formats. * - * @constructor * @abstract * @extends {module:ol/format/Feature} */ @@ -21,121 +20,121 @@ class JSONFeature { } /** - * @inheritDoc - */ + * @inheritDoc + */ getType() { return FormatType.JSON; } /** - * @inheritDoc - */ + * @inheritDoc + */ readFeature(source, opt_options) { return this.readFeatureFromObject( getObject(source), this.getReadOptions(source, opt_options)); } /** - * @inheritDoc - */ + * @inheritDoc + */ readFeatures(source, opt_options) { return this.readFeaturesFromObject( getObject(source), this.getReadOptions(source, opt_options)); } /** - * @abstract - * @param {Object} object Object. - * @param {module:ol/format/Feature~ReadOptions=} opt_options Read options. - * @protected - * @return {module:ol/Feature} Feature. - */ + * @abstract + * @param {Object} object Object. + * @param {module:ol/format/Feature~ReadOptions=} opt_options Read options. + * @protected + * @return {module:ol/Feature} Feature. + */ readFeatureFromObject(object, opt_options) {} /** - * @abstract - * @param {Object} object Object. - * @param {module:ol/format/Feature~ReadOptions=} opt_options Read options. - * @protected - * @return {Array.} Features. - */ + * @abstract + * @param {Object} object Object. + * @param {module:ol/format/Feature~ReadOptions=} opt_options Read options. + * @protected + * @return {Array.} Features. + */ readFeaturesFromObject(object, opt_options) {} /** - * @inheritDoc - */ + * @inheritDoc + */ readGeometry(source, opt_options) { return this.readGeometryFromObject( getObject(source), this.getReadOptions(source, opt_options)); } /** - * @abstract - * @param {Object} object Object. - * @param {module:ol/format/Feature~ReadOptions=} opt_options Read options. - * @protected - * @return {module:ol/geom/Geometry} Geometry. - */ + * @abstract + * @param {Object} object Object. + * @param {module:ol/format/Feature~ReadOptions=} opt_options Read options. + * @protected + * @return {module:ol/geom/Geometry} Geometry. + */ readGeometryFromObject(object, opt_options) {} /** - * @inheritDoc - */ + * @inheritDoc + */ readProjection(source) { return this.readProjectionFromObject(getObject(source)); } /** - * @abstract - * @param {Object} object Object. - * @protected - * @return {module:ol/proj/Projection} Projection. - */ + * @abstract + * @param {Object} object Object. + * @protected + * @return {module:ol/proj/Projection} Projection. + */ readProjectionFromObject(object) {} /** - * @inheritDoc - */ + * @inheritDoc + */ writeFeature(feature, opt_options) { return JSON.stringify(this.writeFeatureObject(feature, opt_options)); } /** - * @abstract - * @param {module:ol/Feature} feature Feature. - * @param {module:ol/format/Feature~WriteOptions=} opt_options Write options. - * @return {Object} Object. - */ + * @abstract + * @param {module:ol/Feature} feature Feature. + * @param {module:ol/format/Feature~WriteOptions=} opt_options Write options. + * @return {Object} Object. + */ writeFeatureObject(feature, opt_options) {} /** - * @inheritDoc - */ + * @inheritDoc + */ writeFeatures(features, opt_options) { return JSON.stringify(this.writeFeaturesObject(features, opt_options)); } /** - * @abstract - * @param {Array.} features Features. - * @param {module:ol/format/Feature~WriteOptions=} opt_options Write options. - * @return {Object} Object. - */ + * @abstract + * @param {Array.} features Features. + * @param {module:ol/format/Feature~WriteOptions=} opt_options Write options. + * @return {Object} Object. + */ writeFeaturesObject(features, opt_options) {} /** - * @inheritDoc - */ + * @inheritDoc + */ writeGeometry(geometry, opt_options) { return JSON.stringify(this.writeGeometryObject(geometry, opt_options)); } /** - * @abstract - * @param {module:ol/geom/Geometry} geometry Geometry. - * @param {module:ol/format/Feature~WriteOptions=} opt_options Write options. - * @return {Object} Object. - */ + * @abstract + * @param {module:ol/geom/Geometry} geometry Geometry. + * @param {module:ol/format/Feature~WriteOptions=} opt_options Write options. + * @return {Object} Object. + */ writeGeometryObject(geometry, opt_options) {} } diff --git a/src/ol/format/KML.js b/src/ol/format/KML.js index ab2ef3203b..be5b75b5dd 100644 --- a/src/ol/format/KML.js +++ b/src/ol/format/KML.js @@ -391,12 +391,14 @@ function createStyleDefaults() { * Note that the KML format uses the URL() constructor. Older browsers such as IE * which do not support this will need a URL polyfill to be loaded before use. * - * @constructor * @extends {module:ol/format/XMLFeature} - * @param {module:ol/format/KML~Options=} opt_options Options. * @api */ class KML { + + /** + * @param {module:ol/format/KML~Options=} opt_options Options. + */ constructor(opt_options) { const options = opt_options ? opt_options : {}; diff --git a/src/ol/format/MVT.js b/src/ol/format/MVT.js index 2e09508cbc..9c325c0ef3 100644 --- a/src/ol/format/MVT.js +++ b/src/ol/format/MVT.js @@ -42,12 +42,15 @@ import RenderFeature from '../render/Feature.js'; * @classdesc * Feature format for reading data in the Mapbox MVT format. * - * @constructor * @extends {module:ol/format/Feature} * @param {module:ol/format/MVT~Options=} opt_options Options. * @api */ class MVT { + + /** + * @param {module:ol/format/MVT~Options=} opt_options Options. + */ constructor(opt_options) { FeatureFormat.call(this); diff --git a/src/ol/format/OSMXML.js b/src/ol/format/OSMXML.js index f18bbf8f83..3d76a8e7c1 100644 --- a/src/ol/format/OSMXML.js +++ b/src/ol/format/OSMXML.js @@ -50,7 +50,6 @@ const PARSERS = makeStructureNS( * Feature format for reading data in the * [OSMXML format](http://wiki.openstreetmap.org/wiki/OSM_XML). * - * @constructor * @extends {module:ol/format/XMLFeature} * @api */ diff --git a/src/ol/format/OWS.js b/src/ol/format/OWS.js index fecdf3ac90..dfcc9e2d34 100644 --- a/src/ol/format/OWS.js +++ b/src/ol/format/OWS.js @@ -28,7 +28,6 @@ const PARSERS = makeStructureNS( /** - * @constructor * @extends {module:ol/format/XML} */ class OWS { diff --git a/src/ol/format/Polyline.js b/src/ol/format/Polyline.js index d7f57cee60..5cdfbee7a9 100644 --- a/src/ol/format/Polyline.js +++ b/src/ol/format/Polyline.js @@ -27,12 +27,14 @@ import {get as getProjection} from '../proj.js'; * Feature format for reading and writing data in the Encoded * Polyline Algorithm Format. * - * @constructor * @extends {module:ol/format/TextFeature} - * @param {module:ol/format/Polyline~Options=} opt_options Optional configuration object. * @api */ class Polyline { + + /** + * @param {module:ol/format/Polyline~Options=} opt_options Optional configuration object. + */ constructor(opt_options) { const options = opt_options ? opt_options : {}; diff --git a/src/ol/format/TextFeature.js b/src/ol/format/TextFeature.js index 34b4d6bfd0..bf0561da42 100644 --- a/src/ol/format/TextFeature.js +++ b/src/ol/format/TextFeature.js @@ -11,7 +11,6 @@ import FormatType from '../format/FormatType.js'; * instantiated in apps. * Base class for text feature formats. * - * @constructor * @abstract * @extends {module:ol/format/Feature} */ @@ -21,122 +20,122 @@ class TextFeature { } /** - * @inheritDoc - */ + * @inheritDoc + */ getType() { return FormatType.TEXT; } /** - * @inheritDoc - */ + * @inheritDoc + */ readFeature(source, opt_options) { return this.readFeatureFromText(getText(source), this.adaptOptions(opt_options)); } /** - * @abstract - * @param {string} text Text. - * @param {module:ol/format/Feature~ReadOptions=} opt_options Read options. - * @protected - * @return {module:ol/Feature} Feature. - */ + * @abstract + * @param {string} text Text. + * @param {module:ol/format/Feature~ReadOptions=} opt_options Read options. + * @protected + * @return {module:ol/Feature} Feature. + */ readFeatureFromText(text, opt_options) {} /** - * @inheritDoc - */ + * @inheritDoc + */ readFeatures(source, opt_options) { return this.readFeaturesFromText(getText(source), this.adaptOptions(opt_options)); } /** - * @abstract - * @param {string} text Text. - * @param {module:ol/format/Feature~ReadOptions=} opt_options Read options. - * @protected - * @return {Array.} Features. - */ + * @abstract + * @param {string} text Text. + * @param {module:ol/format/Feature~ReadOptions=} opt_options Read options. + * @protected + * @return {Array.} Features. + */ readFeaturesFromText(text, opt_options) {} /** - * @inheritDoc - */ + * @inheritDoc + */ readGeometry(source, opt_options) { return this.readGeometryFromText(getText(source), this.adaptOptions(opt_options)); } /** - * @abstract - * @param {string} text Text. - * @param {module:ol/format/Feature~ReadOptions=} opt_options Read options. - * @protected - * @return {module:ol/geom/Geometry} Geometry. - */ + * @abstract + * @param {string} text Text. + * @param {module:ol/format/Feature~ReadOptions=} opt_options Read options. + * @protected + * @return {module:ol/geom/Geometry} Geometry. + */ readGeometryFromText(text, opt_options) {} /** - * @inheritDoc - */ + * @inheritDoc + */ readProjection(source) { return this.readProjectionFromText(getText(source)); } /** - * @param {string} text Text. - * @protected - * @return {module:ol/proj/Projection} Projection. - */ + * @param {string} text Text. + * @protected + * @return {module:ol/proj/Projection} Projection. + */ readProjectionFromText(text) { return this.dataProjection; } /** - * @inheritDoc - */ + * @inheritDoc + */ writeFeature(feature, opt_options) { return this.writeFeatureText(feature, this.adaptOptions(opt_options)); } /** - * @abstract - * @param {module:ol/Feature} feature Features. - * @param {module:ol/format/Feature~WriteOptions=} opt_options Write options. - * @protected - * @return {string} Text. - */ + * @abstract + * @param {module:ol/Feature} feature Features. + * @param {module:ol/format/Feature~WriteOptions=} opt_options Write options. + * @protected + * @return {string} Text. + */ writeFeatureText(feature, opt_options) {} /** - * @inheritDoc - */ + * @inheritDoc + */ writeFeatures(features, opt_options) { return this.writeFeaturesText(features, this.adaptOptions(opt_options)); } /** - * @abstract - * @param {Array.} features Features. - * @param {module:ol/format/Feature~WriteOptions=} opt_options Write options. - * @protected - * @return {string} Text. - */ + * @abstract + * @param {Array.} features Features. + * @param {module:ol/format/Feature~WriteOptions=} opt_options Write options. + * @protected + * @return {string} Text. + */ writeFeaturesText(features, opt_options) {} /** - * @inheritDoc - */ + * @inheritDoc + */ writeGeometry(geometry, opt_options) { return this.writeGeometryText(geometry, this.adaptOptions(opt_options)); } /** - * @abstract - * @param {module:ol/geom/Geometry} geometry Geometry. - * @param {module:ol/format/Feature~WriteOptions=} opt_options Write options. - * @protected - * @return {string} Text. - */ + * @abstract + * @param {module:ol/geom/Geometry} geometry Geometry. + * @param {module:ol/format/Feature~WriteOptions=} opt_options Write options. + * @protected + * @return {string} Text. + */ writeGeometryText(geometry, opt_options) {} } diff --git a/src/ol/format/TopoJSON.js b/src/ol/format/TopoJSON.js index 5ed2121b70..5c586c36f4 100644 --- a/src/ol/format/TopoJSON.js +++ b/src/ol/format/TopoJSON.js @@ -43,12 +43,14 @@ import {get as getProjection} from '../proj.js'; * @classdesc * Feature format for reading data in the TopoJSON format. * - * @constructor * @extends {module:ol/format/JSONFeature} - * @param {module:ol/format/TopoJSON~Options=} opt_options Options. * @api */ class TopoJSON { + + /** + * @param {module:ol/format/TopoJSON~Options=} opt_options Options. + */ constructor(opt_options) { const options = opt_options ? opt_options : {}; diff --git a/src/ol/format/WFS.js b/src/ol/format/WFS.js index bd86b1f7a4..2fbcbc7911 100644 --- a/src/ol/format/WFS.js +++ b/src/ol/format/WFS.js @@ -201,12 +201,14 @@ const DEFAULT_VERSION = '1.1.0'; * as option if you want to read a WFS that contains GML2 (WFS 1.0.0). * Also see {@link module:ol/format/GMLBase~GMLBase} which is used by this format. * - * @constructor - * @param {module:ol/format/WFS~Options=} opt_options Optional configuration object. * @extends {module:ol/format/XMLFeature} * @api */ class WFS { + + /** + * @param {module:ol/format/WFS~Options=} opt_options Optional configuration object. + */ constructor(opt_options) { const options = opt_options ? opt_options : {}; diff --git a/src/ol/format/WKT.js b/src/ol/format/WKT.js index a6fc544caa..4a73ae6da7 100644 --- a/src/ol/format/WKT.js +++ b/src/ol/format/WKT.js @@ -87,10 +87,12 @@ const TokenType = { /** * Class to tokenize a WKT string. - * @param {string} wkt WKT string. - * @constructor */ class Lexer { + + /** + * @param {string} wkt WKT string. + */ constructor(wkt) { /** @@ -218,10 +220,12 @@ class Lexer { /** * Class to parse the tokens from the WKT string. - * @param {module:ol/format/WKT~Lexer} lexer The lexer. - * @constructor */ class Parser { + + /** + * @param {module:ol/format/WKT~Lexer} lexer The lexer. + */ constructor(lexer) { /** @@ -590,12 +594,14 @@ class Parser { * Geometry format for reading and writing data in the `WellKnownText` (WKT) * format. * - * @constructor * @extends {module:ol/format/TextFeature} - * @param {module:ol/format/WKT~Options=} opt_options Options. * @api */ class WKT { + + /** + * @param {module:ol/format/WKT~Options=} opt_options Options. + */ constructor(opt_options) { const options = opt_options ? opt_options : {}; diff --git a/src/ol/format/WMSCapabilities.js b/src/ol/format/WMSCapabilities.js index ec24bce93c..5e49906fae 100644 --- a/src/ol/format/WMSCapabilities.js +++ b/src/ol/format/WMSCapabilities.js @@ -46,7 +46,6 @@ const CAPABILITY_PARSERS = makeStructureNS( * @classdesc * Format for reading WMS capabilities data * - * @constructor * @extends {module:ol/format/XML} * @api */ diff --git a/src/ol/format/WMSGetFeatureInfo.js b/src/ol/format/WMSGetFeatureInfo.js index 125cf7d2f7..e4aca1f103 100644 --- a/src/ol/format/WMSGetFeatureInfo.js +++ b/src/ol/format/WMSGetFeatureInfo.js @@ -34,12 +34,14 @@ const layerIdentifier = '_layer'; * Format for reading WMSGetFeatureInfo format. It uses * {@link module:ol/format/GML2~GML2} to read features. * - * @constructor * @extends {module:ol/format/XMLFeature} - * @param {module:ol/format/WMSGetFeatureInfo~Options=} opt_options Options. * @api */ class WMSGetFeatureInfo { + + /** + * @param {module:ol/format/WMSGetFeatureInfo~Options=} opt_options Options. + */ constructor(opt_options) { const options = opt_options ? opt_options : {}; diff --git a/src/ol/format/WMTSCapabilities.js b/src/ol/format/WMTSCapabilities.js index 4acffd40a9..a9b03e95f4 100644 --- a/src/ol/format/WMTSCapabilities.js +++ b/src/ol/format/WMTSCapabilities.js @@ -45,7 +45,6 @@ const PARSERS = makeStructureNS( * @classdesc * Format for reading WMTS capabilities data. * - * @constructor * @extends {module:ol/format/XML} * @api */ diff --git a/src/ol/format/XML.js b/src/ol/format/XML.js index 817aaeb4f1..d210640b32 100644 --- a/src/ol/format/XML.js +++ b/src/ol/format/XML.js @@ -7,15 +7,13 @@ import {isDocument, isNode, parse} from '../xml.js'; * @classdesc * Generic format for reading non-feature XML data * - * @constructor * @abstract - * @struct */ class XML { /** - * @param {Document|Node|string} source Source. - * @return {Object} The parsed result. - */ + * @param {Document|Node|string} source Source. + * @return {Object} The parsed result. + */ read(source) { if (isDocument(source)) { return this.readFromDocument(/** @type {Document} */ (source)); @@ -30,17 +28,17 @@ class XML { } /** - * @abstract - * @param {Document} doc Document. - * @return {Object} Object - */ + * @abstract + * @param {Document} doc Document. + * @return {Object} Object + */ readFromDocument(doc) {} /** - * @abstract - * @param {Node} node Node. - * @return {Object} Object - */ + * @abstract + * @param {Node} node Node. + * @return {Object} Object + */ readFromNode(node) {} } diff --git a/src/ol/format/XMLFeature.js b/src/ol/format/XMLFeature.js index 5365257d39..8874ee7c20 100644 --- a/src/ol/format/XMLFeature.js +++ b/src/ol/format/XMLFeature.js @@ -13,7 +13,6 @@ import {isDocument, isNode, parse} from '../xml.js'; * instantiated in apps. * Base class for XML feature formats. * - * @constructor * @abstract * @extends {module:ol/format/Feature} */ From 0224e2b3c56962a79ebd76dc7de34c10c75b9ee8 Mon Sep 17 00:00:00 2001 From: ahocevar Date: Tue, 17 Jul 2018 11:45:50 +0200 Subject: [PATCH 008/107] Use extends and super for AssertionError --- src/ol/AssertionError.js | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/ol/AssertionError.js b/src/ol/AssertionError.js index 439a4336cd..b1ab3ac881 100644 --- a/src/ol/AssertionError.js +++ b/src/ol/AssertionError.js @@ -1,7 +1,7 @@ /** * @module ol/AssertionError */ -import {VERSION, inherits} from './util.js'; +import {VERSION} from './util.js'; /** * Error object thrown when an assertion failed. This is an ECMA-262 Error, @@ -9,7 +9,7 @@ import {VERSION, inherits} from './util.js'; * @see {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error} * @extends {Error} */ -class AssertionError { +class AssertionError extends Error { /** * @param {number} code Error code. @@ -42,6 +42,4 @@ class AssertionError { } -inherits(AssertionError, Error); - export default AssertionError; From aa7b6350ecc0a668465a6babe12f4f9c67e48f37 Mon Sep 17 00:00:00 2001 From: ahocevar Date: Tue, 17 Jul 2018 11:46:19 +0200 Subject: [PATCH 009/107] Use extends and super for Collection --- src/ol/Collection.js | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/src/ol/Collection.js b/src/ol/Collection.js index 1ee55f95a1..6c15953019 100644 --- a/src/ol/Collection.js +++ b/src/ol/Collection.js @@ -1,7 +1,6 @@ /** * @module ol/Collection */ -import {inherits} from './util.js'; import AssertionError from './AssertionError.js'; import CollectionEventType from './CollectionEventType.js'; import BaseObject from './Object.js'; @@ -21,17 +20,15 @@ const Property = { * @classdesc * Events emitted by {@link module:ol/Collection~Collection} instances are instances of this * type. - * - * @extends {module:ol/events/Event} */ -export class CollectionEvent { +export class CollectionEvent extends Event { /** * @param {module:ol/CollectionEventType} type Type. * @param {*=} opt_element Element. */ constructor(type, opt_element) { - Event.call(this, type); + super(type); /** * The element that is added to or removed from the collection. @@ -44,8 +41,6 @@ export class CollectionEvent { } -inherits(CollectionEvent, Event); - /** * @typedef {Object} Options @@ -61,10 +56,9 @@ inherits(CollectionEvent, Event); * Collection; they trigger events on the appropriate object, not on the * Collection as a whole. * - * @extends {module:ol/Object} * @api */ -class Collection { +class Collection extends BaseObject { /** * @param {Array.=} opt_array Array. @@ -73,7 +67,7 @@ class Collection { */ constructor(opt_array, opt_options) { - BaseObject.call(this); + super(); const options = opt_options || {}; @@ -288,7 +282,5 @@ class Collection { } } -inherits(Collection, BaseObject); - export default Collection; From 6f4d2e34a94b02a3aa49bb0de37e11acdbaa7c05 Mon Sep 17 00:00:00 2001 From: ahocevar Date: Tue, 17 Jul 2018 11:49:37 +0200 Subject: [PATCH 010/107] Use extends and super for Feature --- src/ol/Feature.js | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/ol/Feature.js b/src/ol/Feature.js index ddc9c3049d..838e8dcaa8 100644 --- a/src/ol/Feature.js +++ b/src/ol/Feature.js @@ -4,7 +4,6 @@ import {assert} from './asserts.js'; import {listen, unlisten, unlistenByKey} from './events.js'; import EventType from './events/EventType.js'; -import {inherits} from './util.js'; import BaseObject, {getChangeEventType} from './Object.js'; import Geometry from './geom/Geometry.js'; import Style from './style/Style.js'; @@ -51,10 +50,9 @@ import Style from './style/Style.js'; * var point = feature.getGeometry(); * ``` * - * @extends {module:ol/Object} * @api */ -class Feature { +class Feature extends BaseObject { /** * @param {module:ol/geom/Geometry|Object.=} opt_geometryOrProperties @@ -64,7 +62,7 @@ class Feature { */ constructor(opt_geometryOrProperties) { - BaseObject.call(this); + super(); /** * @private @@ -271,8 +269,6 @@ class Feature { } } -inherits(Feature, BaseObject); - /** * Convert the provided object into a feature style function. Functions passed From ec495bfcec4eac2f9013b4b842df5bec2dcc4200 Mon Sep 17 00:00:00 2001 From: ahocevar Date: Tue, 17 Jul 2018 11:56:21 +0200 Subject: [PATCH 011/107] Use extends and super for Geolocation --- src/ol/Geolocation.js | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/ol/Geolocation.js b/src/ol/Geolocation.js index 8ef22b3882..771479504a 100644 --- a/src/ol/Geolocation.js +++ b/src/ol/Geolocation.js @@ -1,7 +1,6 @@ /** * @module ol/Geolocation */ -import {inherits} from './util.js'; import GeolocationProperty from './GeolocationProperty.js'; import BaseObject, {getChangeEventType} from './Object.js'; import {listen} from './events.js'; @@ -44,10 +43,9 @@ import {get as getProjection, getTransformFromProjections, identityTransform} fr * }); * * @fires error - * @extends {module:ol/Object} * @api */ -class Geolocation { +class Geolocation extends BaseObject { /** * @param {module:ol/Geolocation~Options=} opt_options Options. @@ -100,7 +98,7 @@ class Geolocation { */ disposeInternal() { this.setTracking(false); - BaseObject.prototype.disposeInternal.call(this); + super.disposeInternal(); } /** @@ -333,7 +331,5 @@ class Geolocation { } } -inherits(Geolocation, BaseObject); - export default Geolocation; From fb59d7a65b203085f6cee255652af93529b3a90e Mon Sep 17 00:00:00 2001 From: ahocevar Date: Tue, 17 Jul 2018 13:27:37 +0200 Subject: [PATCH 012/107] Use extends and super for Image --- src/ol/Image.js | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/src/ol/Image.js b/src/ol/Image.js index 984b3227d3..c9b0578b68 100644 --- a/src/ol/Image.js +++ b/src/ol/Image.js @@ -1,7 +1,6 @@ /** * @module ol/Image */ -import {inherits} from './util.js'; import ImageBase from './ImageBase.js'; import ImageState from './ImageState.js'; import {listenOnce, unlistenByKey} from './events.js'; @@ -28,10 +27,7 @@ import {getHeight} from './extent.js'; */ -/** - * @extends {module:ol/ImageBase} - */ -class ImageWrapper { +class ImageWrapper extends ImageBase { /** * @param {module:ol/extent~Extent} extent Extent. @@ -43,7 +39,7 @@ class ImageWrapper { */ constructor(extent, resolution, pixelRatio, src, crossOrigin, imageLoadFunction) { - ImageBase.call(this, extent, resolution, pixelRatio, ImageState.IDLE); + super(extent, resolution, pixelRatio, ImageState.IDLE); /** * @private @@ -152,7 +148,5 @@ class ImageWrapper { } } -inherits(ImageWrapper, ImageBase); - export default ImageWrapper; From 61c48ce09fc8fce5b3a4437db3b3ddbfc1c81ecc Mon Sep 17 00:00:00 2001 From: ahocevar Date: Tue, 17 Jul 2018 13:40:49 +0200 Subject: [PATCH 013/107] Use extends and super for ImageBase --- src/ol/ImageBase.js | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/ol/ImageBase.js b/src/ol/ImageBase.js index ca8dc2ad76..472148be1c 100644 --- a/src/ol/ImageBase.js +++ b/src/ol/ImageBase.js @@ -1,15 +1,13 @@ /** * @module ol/ImageBase */ -import {inherits} from './util.js'; import EventTarget from './events/EventTarget.js'; import EventType from './events/EventType.js'; /** * @abstract - * @extends {module:ol/events/EventTarget} */ -class ImageBase { +class ImageBase extends EventTarget { /** * @param {module:ol/extent~Extent} extent Extent. @@ -19,7 +17,7 @@ class ImageBase { */ constructor(extent, resolution, pixelRatio, state) { - EventTarget.call(this); + super(); /** * @protected @@ -95,7 +93,5 @@ class ImageBase { load() {} } -inherits(ImageBase, EventTarget); - export default ImageBase; From 6ca865375e52f24bb7825f34e7e67f5a864b677b Mon Sep 17 00:00:00 2001 From: ahocevar Date: Tue, 17 Jul 2018 13:45:54 +0200 Subject: [PATCH 014/107] Use extends and super for ImageCanvas --- src/ol/ImageCanvas.js | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/src/ol/ImageCanvas.js b/src/ol/ImageCanvas.js index 5db87722e9..df586aebfe 100644 --- a/src/ol/ImageCanvas.js +++ b/src/ol/ImageCanvas.js @@ -1,7 +1,6 @@ /** * @module ol/ImageCanvas */ -import {inherits} from './util.js'; import ImageBase from './ImageBase.js'; import ImageState from './ImageState.js'; @@ -16,10 +15,7 @@ import ImageState from './ImageState.js'; */ -/** - * @extends {module:ol/ImageBase} - */ -class ImageCanvas { +class ImageCanvas extends ImageBase { /** * @param {module:ol/extent~Extent} extent Extent. @@ -31,6 +27,10 @@ class ImageCanvas { */ constructor(extent, resolution, pixelRatio, canvas, opt_loader) { + const state = opt_loader !== undefined ? ImageState.IDLE : ImageState.LOADED; + + super(extent, resolution, pixelRatio, state); + /** * Optional canvas loader function. * @type {?module:ol/ImageCanvas~Loader} @@ -38,10 +38,6 @@ class ImageCanvas { */ this.loader_ = opt_loader !== undefined ? opt_loader : null; - const state = opt_loader !== undefined ? ImageState.IDLE : ImageState.LOADED; - - ImageBase.call(this, extent, resolution, pixelRatio, state); - /** * @private * @type {HTMLCanvasElement} @@ -98,7 +94,5 @@ class ImageCanvas { } } -inherits(ImageCanvas, ImageBase); - export default ImageCanvas; From 5f40b5b66b6e7c3875e9e383a00fbee11b007301 Mon Sep 17 00:00:00 2001 From: ahocevar Date: Tue, 17 Jul 2018 13:47:13 +0200 Subject: [PATCH 015/107] Use extends and super for ImageTile --- src/ol/ImageTile.js | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/src/ol/ImageTile.js b/src/ol/ImageTile.js index c6613bd5a1..5de7a12eae 100644 --- a/src/ol/ImageTile.js +++ b/src/ol/ImageTile.js @@ -1,7 +1,6 @@ /** * @module ol/ImageTile */ -import {inherits} from './util.js'; import Tile from './Tile.js'; import TileState from './TileState.js'; import {createCanvasContext2D} from './dom.js'; @@ -14,10 +13,7 @@ import EventType from './events/EventType.js'; * @api */ -/** - * @extends {module:ol/Tile} - */ -class ImageTile { +class ImageTile extends Tile { /** * @param {module:ol/tilecoord~TileCoord} tileCoord Tile coordinate. @@ -29,7 +25,7 @@ class ImageTile { */ constructor(tileCoord, state, src, crossOrigin, tileLoadFunction, opt_options) { - Tile.call(this, tileCoord, state, opt_options); + super(tileCoord, state, opt_options); /** * @private @@ -81,7 +77,7 @@ class ImageTile { } this.state = TileState.ABORT; this.changed(); - Tile.prototype.disposeInternal.call(this); + super.disposeInternal(); } /** @@ -163,8 +159,6 @@ class ImageTile { } } -inherits(ImageTile, Tile); - /** * Get a 1-pixel blank image. From de104d6e22fb9e77a80c5da2cc5fdff9acc56146 Mon Sep 17 00:00:00 2001 From: ahocevar Date: Tue, 17 Jul 2018 13:49:22 +0200 Subject: [PATCH 016/107] Use extends and super for Map --- src/ol/Map.js | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/ol/Map.js b/src/ol/Map.js index 0b148878db..32363f8d9b 100644 --- a/src/ol/Map.js +++ b/src/ol/Map.js @@ -1,7 +1,6 @@ /** * @module ol/Map */ -import {inherits} from './util.js'; import PluggableMap from './PluggableMap.js'; import {defaults as defaultControls} from './control/util.js'; import {defaults as defaultInteractions} from './interaction.js'; @@ -57,14 +56,13 @@ import CanvasVectorTileLayerRenderer from './renderer/canvas/VectorTileLayer.js' * options or added with `addLayer` can be groups, which can contain further * groups, and so on. * - * @extends {module:ol/PluggableMap} * @fires module:ol/MapBrowserEvent~MapBrowserEvent * @fires module:ol/MapEvent~MapEvent * @fires module:ol/render/Event~RenderEvent#postcompose * @fires module:ol/render/Event~RenderEvent#precompose * @api */ -class Map { +class Map extends PluggableMap { /** * @param {module:ol/PluggableMap~MapOptions} options Map options. @@ -78,7 +76,7 @@ class Map { options.interactions = defaultInteractions(); } - PluggableMap.call(this, options); + super(options); } createRenderer() { @@ -93,6 +91,5 @@ class Map { } } -inherits(Map, PluggableMap); export default Map; From 16849a36111fadfba194541e9c2e027c09f6521d Mon Sep 17 00:00:00 2001 From: ahocevar Date: Tue, 17 Jul 2018 13:51:32 +0200 Subject: [PATCH 017/107] Use extends and super for MapBrowserEvent --- src/ol/MapBrowserEvent.js | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/src/ol/MapBrowserEvent.js b/src/ol/MapBrowserEvent.js index 18ab508d38..a807f7c4c7 100644 --- a/src/ol/MapBrowserEvent.js +++ b/src/ol/MapBrowserEvent.js @@ -1,17 +1,14 @@ /** * @module ol/MapBrowserEvent */ -import {inherits} from './util.js'; import MapEvent from './MapEvent.js'; /** * @classdesc * Events emitted as map browser events are instances of this type. * See {@link module:ol/Map~Map} for which events trigger a map browser event. - * - * @extends {module:ol/MapEvent} */ -class MapBrowserEvent { +class MapBrowserEvent extends MapEvent { /** * @param {string} type Event type. @@ -22,7 +19,7 @@ class MapBrowserEvent { */ constructor(type, map, browserEvent, opt_dragging, opt_frameState) { - MapEvent.call(this, type, map, opt_frameState); + super(type, map, opt_frameState); /** * The original browser event. @@ -64,7 +61,7 @@ class MapBrowserEvent { * @api */ preventDefault() { - MapEvent.prototype.preventDefault.call(this); + super.preventDefault(); this.originalEvent.preventDefault(); } @@ -75,12 +72,10 @@ class MapBrowserEvent { * @api */ stopPropagation() { - MapEvent.prototype.stopPropagation.call(this); + super.stopPropagation(); this.originalEvent.stopPropagation(); } } -inherits(MapBrowserEvent, MapEvent); - export default MapBrowserEvent; From edd23dbf6af295e02ee677a976035733acd572a1 Mon Sep 17 00:00:00 2001 From: ahocevar Date: Tue, 17 Jul 2018 13:53:05 +0200 Subject: [PATCH 018/107] Use extends and super for MapBrowserEventHandler --- src/ol/MapBrowserEventHandler.js | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/src/ol/MapBrowserEventHandler.js b/src/ol/MapBrowserEventHandler.js index 7778985950..cf29d58cff 100644 --- a/src/ol/MapBrowserEventHandler.js +++ b/src/ol/MapBrowserEventHandler.js @@ -1,7 +1,6 @@ /** * @module ol/MapBrowserEventHandler */ -import {inherits} from './util.js'; import {DEVICE_PIXEL_RATIO} from './has.js'; import MapBrowserEventType from './MapBrowserEventType.js'; import MapBrowserPointerEvent from './MapBrowserPointerEvent.js'; @@ -10,10 +9,7 @@ import EventTarget from './events/EventTarget.js'; import PointerEventType from './pointer/EventType.js'; import PointerEventHandler from './pointer/PointerEventHandler.js'; -/** - * @extends {module:ol/events/EventTarget} - */ -class MapBrowserEventHandler { +class MapBrowserEventHandler extends EventTarget { /** * @param {module:ol/PluggableMap} map The map with the viewport to listen to events on. @@ -21,7 +17,7 @@ class MapBrowserEventHandler { */ constructor(map, moveTolerance) { - EventTarget.call(this); + super(); /** * This is the element that we will listen to the real events on. @@ -321,11 +317,9 @@ class MapBrowserEventHandler { this.pointerEventHandler_.dispose(); this.pointerEventHandler_ = null; } - EventTarget.prototype.disposeInternal.call(this); + super.disposeInternal(); } } -inherits(MapBrowserEventHandler, EventTarget); - export default MapBrowserEventHandler; From 5a90203aa06aa5a70c6de0f222e245a037d6537d Mon Sep 17 00:00:00 2001 From: ahocevar Date: Tue, 17 Jul 2018 13:54:30 +0200 Subject: [PATCH 019/107] Use extends and super for MapBrowserPointerEvent --- src/ol/MapBrowserPointerEvent.js | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/src/ol/MapBrowserPointerEvent.js b/src/ol/MapBrowserPointerEvent.js index 406ba18cea..a12823d616 100644 --- a/src/ol/MapBrowserPointerEvent.js +++ b/src/ol/MapBrowserPointerEvent.js @@ -1,13 +1,9 @@ /** * @module ol/MapBrowserPointerEvent */ -import {inherits} from './util.js'; import MapBrowserEvent from './MapBrowserEvent.js'; -/** - * @extends {module:ol/MapBrowserEvent} - */ -class MapBrowserPointerEvent { +class MapBrowserPointerEvent extends MapBrowserEvent { /** * @param {string} type Event type. @@ -18,8 +14,7 @@ class MapBrowserPointerEvent { */ constructor(type, map, pointerEvent, opt_dragging, opt_frameState) { - MapBrowserEvent.call(this, type, map, pointerEvent.originalEvent, opt_dragging, - opt_frameState); + super(type, map, pointerEvent.originalEvent, opt_dragging, opt_frameState); /** * @const @@ -31,6 +26,4 @@ class MapBrowserPointerEvent { } -inherits(MapBrowserPointerEvent, MapBrowserEvent); - export default MapBrowserPointerEvent; From e1fedca22ee76c4f86aa0f2372a0104cb068a22b Mon Sep 17 00:00:00 2001 From: ahocevar Date: Tue, 17 Jul 2018 13:55:37 +0200 Subject: [PATCH 020/107] Use extends and super for MapEvent --- src/ol/MapEvent.js | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/src/ol/MapEvent.js b/src/ol/MapEvent.js index 0d9c78a861..ac22f422b7 100644 --- a/src/ol/MapEvent.js +++ b/src/ol/MapEvent.js @@ -1,17 +1,14 @@ /** * @module ol/MapEvent */ -import {inherits} from './util.js'; import Event from './events/Event.js'; /** * @classdesc * Events emitted as map events are instances of this type. * See {@link module:ol/Map~Map} for which events trigger a map event. - * - * @extends {module:ol/events/Event} */ -class MapEvent { +class MapEvent extends Event { /** * @param {string} type Event type. @@ -20,7 +17,7 @@ class MapEvent { */ constructor(type, map, opt_frameState) { - Event.call(this, type); + super(type); /** * The map where the event occurred. @@ -40,6 +37,4 @@ class MapEvent { } -inherits(MapEvent, Event); - export default MapEvent; From 2792ba701ad9bd02b09d8e2478eaccadd21c6cd4 Mon Sep 17 00:00:00 2001 From: ahocevar Date: Tue, 17 Jul 2018 14:01:21 +0200 Subject: [PATCH 021/107] Use extends and super for Object --- src/ol/Object.js | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/src/ol/Object.js b/src/ol/Object.js index 47a8864e96..10d4ec5853 100644 --- a/src/ol/Object.js +++ b/src/ol/Object.js @@ -1,7 +1,7 @@ /** * @module ol/Object */ -import {getUid, inherits} from './util.js'; +import {getUid} from './util.js'; import ObjectEventType from './ObjectEventType.js'; import Observable from './Observable.js'; import Event from './events/Event.js'; @@ -11,9 +11,8 @@ import {assign} from './obj.js'; /** * @classdesc * Events emitted by {@link module:ol/Object~BaseObject} instances are instances of this type. - * @extends {module:ol/events/Event} - */ -class ObjectEvent { + */ +class ObjectEvent extends Event { /** * @param {string} type The event type. @@ -21,7 +20,7 @@ class ObjectEvent { * @param {*} oldValue The old value for `key`. */ constructor(type, key, oldValue) { - Event.call(this, type); + super(type); /** * The name of the property whose value is changing. @@ -42,8 +41,6 @@ class ObjectEvent { } -inherits(ObjectEvent, Event); - /** * @classdesc @@ -85,17 +82,16 @@ inherits(ObjectEvent, Event); * Properties can be deleted by using the unset method. E.g. * object.unset('foo'). * - * @extends {module:ol/Observable} * @fires module:ol/Object~ObjectEvent * @api */ -class BaseObject { +class BaseObject extends Observable { /** * @param {Object.=} opt_values An object with key-value pairs. */ constructor(opt_values) { - Observable.call(this); + super(); // Call {@link module:ol~getUid} to ensure that the order of objects' ids is // the same as the order in which they were created. This also helps to @@ -207,8 +203,6 @@ class BaseObject { } } -inherits(BaseObject, Observable); - /** * @type {Object.} From 2fd022d21518fcf3e12464a73472091639ce1c1f Mon Sep 17 00:00:00 2001 From: ahocevar Date: Tue, 17 Jul 2018 14:05:52 +0200 Subject: [PATCH 022/107] Use extends and super for Observable --- src/ol/Observable.js | 22 ++-------------------- src/ol/events/EventTarget.js | 10 ++++++++-- 2 files changed, 10 insertions(+), 22 deletions(-) diff --git a/src/ol/Observable.js b/src/ol/Observable.js index 35ae9c39b5..7c68f91b0c 100644 --- a/src/ol/Observable.js +++ b/src/ol/Observable.js @@ -1,7 +1,6 @@ /** * @module ol/Observable */ -import {inherits} from './util.js'; import {listen, unlistenByKey, unlisten, listenOnce} from './events.js'; import EventTarget from './events/EventTarget.js'; import EventType from './events/EventType.js'; @@ -14,14 +13,13 @@ import EventType from './events/EventType.js'; * and unregistration. A generic `change` event is always available through * {@link module:ol/Observable~Observable#changed}. * - * @extends {module:ol/events/EventTarget} * @fires module:ol/events/Event~Event * @api */ -class Observable { +class Observable extends EventTarget { constructor() { - EventTarget.call(this); + super(); /** * @private @@ -112,8 +110,6 @@ class Observable { } } -inherits(Observable, EventTarget); - /** * Removes an event listener using the key returned by `on()` or `once()`. @@ -132,18 +128,4 @@ export function unByKey(key) { } -/** - * Dispatches an event and calls all listeners listening for events - * of this type. The event parameter can either be a string or an - * Object with a `type` property. - * - * @param {{type: string, - * target: (EventTarget|module:ol/events/EventTarget|undefined)}| - * module:ol/events/Event|string} event Event object. - * @function - * @api - */ -Observable.prototype.dispatchEvent; - - export default Observable; diff --git a/src/ol/events/EventTarget.js b/src/ol/events/EventTarget.js index e7cbaa9db4..bd3179aa7b 100644 --- a/src/ol/events/EventTarget.js +++ b/src/ol/events/EventTarget.js @@ -71,11 +71,17 @@ class EventTarget { } /** + * Dispatches an event and calls all listeners listening for events + * of this type. The event parameter can either be a string or an + * Object with a `type` property. + * * @param {{type: string, - * target: (EventTarget|module:ol/events/EventTarget|undefined)}|module:ol/events/Event| - * string} event Event or event type. + * target: (EventTarget|module:ol/events/EventTarget|undefined)}| + * module:ol/events/Event|string} event Event object. * @return {boolean|undefined} `false` if anyone called preventDefault on the * event object or if any of the listeners returned false. + * @function + * @api */ dispatchEvent(event) { const evt = typeof event === 'string' ? new Event(event) : event; From 0f6ee28c19c7acc1a3654a875f12589debdbdd91 Mon Sep 17 00:00:00 2001 From: ahocevar Date: Tue, 17 Jul 2018 14:07:53 +0200 Subject: [PATCH 023/107] Use extends and super for Overlay --- src/ol/Overlay.js | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/ol/Overlay.js b/src/ol/Overlay.js index cf4551304d..2dfefdd43c 100644 --- a/src/ol/Overlay.js +++ b/src/ol/Overlay.js @@ -1,7 +1,6 @@ /** * @module ol/Overlay */ -import {inherits} from './util.js'; import MapEventType from './MapEventType.js'; import BaseObject, {getChangeEventType} from './Object.js'; import OverlayPositioning from './OverlayPositioning.js'; @@ -93,17 +92,16 @@ const Property = { * popup.setPosition(coordinate); * map.addOverlay(popup); * - * @extends {module:ol/Object} * @api */ -class Overlay { +class Overlay extends BaseObject { /** * @param {module:ol/Overlay~Options} options Overlay options. */ constructor(options) { - BaseObject.call(this); + super(); /** * @protected @@ -583,7 +581,5 @@ class Overlay { } } -inherits(Overlay, BaseObject); - export default Overlay; From 505f88031b28a6fad810ebc1318046f1ed19fe02 Mon Sep 17 00:00:00 2001 From: ahocevar Date: Tue, 17 Jul 2018 14:09:23 +0200 Subject: [PATCH 024/107] Use extends and super for PluggableMap --- src/ol/PluggableMap.js | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/src/ol/PluggableMap.js b/src/ol/PluggableMap.js index d6cbcef038..09f7a33e25 100644 --- a/src/ol/PluggableMap.js +++ b/src/ol/PluggableMap.js @@ -1,7 +1,7 @@ /** * @module ol/PluggableMap */ -import {getUid, inherits} from './util.js'; +import {getUid} from './util.js'; import Collection from './Collection.js'; import CollectionEventType from './CollectionEventType.js'; import MapBrowserEvent from './MapBrowserEvent.js'; @@ -131,21 +131,20 @@ import {create as createTransform, apply as applyTransform} from './transform.js /** - * @extends {module:ol/Object} * @fires module:ol/MapBrowserEvent~MapBrowserEvent * @fires module:ol/MapEvent~MapEvent * @fires module:ol/render/Event~RenderEvent#postcompose * @fires module:ol/render/Event~RenderEvent#precompose * @api */ -class PluggableMap { +class PluggableMap extends BaseObject { /** * @param {module:ol/PluggableMap~MapOptions} options Map options. */ constructor(options) { - BaseObject.call(this); + super(); const optionsInternal = createOptionsInternal(options); @@ -537,7 +536,7 @@ class PluggableMap { this.animationDelayKey_ = undefined; } this.setTarget(null); - BaseObject.prototype.disposeInternal.call(this); + super.disposeInternal(); } /** @@ -1339,8 +1338,6 @@ class PluggableMap { } } -inherits(PluggableMap, BaseObject); - /** * @param {MapOptions} options Map options. From 9e259c471db5d1dd0ac4795d9bc996af2a50a31e Mon Sep 17 00:00:00 2001 From: ahocevar Date: Tue, 17 Jul 2018 14:10:32 +0200 Subject: [PATCH 025/107] Use extends and super for Tile --- src/ol/Tile.js | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/src/ol/Tile.js b/src/ol/Tile.js index 6956541b8b..2883a8bcfe 100644 --- a/src/ol/Tile.js +++ b/src/ol/Tile.js @@ -1,7 +1,6 @@ /** * @module ol/Tile */ -import {inherits} from './util.js'; import TileState from './TileState.js'; import {easeIn} from './easing.js'; import EventTarget from './events/EventTarget.js'; @@ -45,9 +44,8 @@ import EventType from './events/EventType.js'; * Base class for tiles. * * @abstract - * @extends {module:ol/events/EventTarget} - */ -class Tile { + */ +class Tile extends EventTarget { /** * @param {module:ol/tilecoord~TileCoord} tileCoord Tile coordinate. @@ -55,7 +53,7 @@ class Tile { * @param {module:ol/Tile~Options=} opt_options Tile options. */ constructor(tileCoord, state, opt_options) { - EventTarget.call(this); + super(); const options = opt_options ? opt_options : {}; @@ -261,7 +259,5 @@ class Tile { } } -inherits(Tile, EventTarget); - export default Tile; From 64190b71092a37ab760eb7f00ef3cc20a39a8bdd Mon Sep 17 00:00:00 2001 From: ahocevar Date: Tue, 17 Jul 2018 14:11:47 +0200 Subject: [PATCH 026/107] Use extends and super for TileCache --- src/ol/TileCache.js | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/src/ol/TileCache.js b/src/ol/TileCache.js index 2181f7ee7d..47f7a8bead 100644 --- a/src/ol/TileCache.js +++ b/src/ol/TileCache.js @@ -1,21 +1,17 @@ /** * @module ol/TileCache */ -import {inherits} from './util.js'; import LRUCache from './structs/LRUCache.js'; import {fromKey, getKey} from './tilecoord.js'; -/** - * @extends {module:ol/structs/LRUCache.} - */ -class TileCache { +class TileCache extends LRUCache { /** * @param {number=} opt_highWaterMark High water mark. */ constructor(opt_highWaterMark) { - LRUCache.call(this, opt_highWaterMark); + super(opt_highWaterMark); } @@ -53,7 +49,5 @@ class TileCache { } } -inherits(TileCache, LRUCache); - export default TileCache; From 2e5054d11c76bb43f6e6d576e78996ac2567416c Mon Sep 17 00:00:00 2001 From: ahocevar Date: Tue, 17 Jul 2018 14:13:46 +0200 Subject: [PATCH 027/107] Use extends and super for TileQueue --- src/ol/TileQueue.js | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/src/ol/TileQueue.js b/src/ol/TileQueue.js index d566d0260d..1c53aa3bb7 100644 --- a/src/ol/TileQueue.js +++ b/src/ol/TileQueue.js @@ -1,7 +1,6 @@ /** * @module ol/TileQueue */ -import {inherits} from './util.js'; import TileState from './TileState.js'; import {listen, unlisten} from './events.js'; import EventType from './events/EventType.js'; @@ -13,10 +12,7 @@ import PriorityQueue from './structs/PriorityQueue.js'; */ -/** - * @extends {module:ol/structs/PriorityQueue.} - */ -class TileQueue { +class TileQueue extends PriorityQueue { /** * @param {module:ol/TileQueue~PriorityFunction} tilePriorityFunction Tile priority function. @@ -24,8 +20,7 @@ class TileQueue { */ constructor(tilePriorityFunction, tileChangeCallback) { - PriorityQueue.call( - this, + super( /** * @param {Array} element Element. * @return {number} Priority. @@ -65,7 +60,7 @@ class TileQueue { * @inheritDoc */ enqueue(element) { - const added = PriorityQueue.prototype.enqueue.call(this, element); + const added = super.enqueue(element); if (added) { const tile = element[0]; listen(tile, EventType.CHANGE, this.handleTileChange, this); @@ -129,7 +124,5 @@ class TileQueue { } } -inherits(TileQueue, PriorityQueue); - export default TileQueue; From 603f4897087d49dd651b3236a85d0eeb212b35c5 Mon Sep 17 00:00:00 2001 From: ahocevar Date: Tue, 17 Jul 2018 14:18:40 +0200 Subject: [PATCH 028/107] Use extends and super for VectorImageTile --- src/ol/VectorImageTile.js | 33 +++++++-------------------------- 1 file changed, 7 insertions(+), 26 deletions(-) diff --git a/src/ol/VectorImageTile.js b/src/ol/VectorImageTile.js index 7d733dc670..f732460b32 100644 --- a/src/ol/VectorImageTile.js +++ b/src/ol/VectorImageTile.js @@ -1,7 +1,7 @@ /** * @module ol/VectorImageTile */ -import {getUid, inherits} from './util.js'; +import {getUid} from './util.js'; import Tile from './Tile.js'; import TileState from './TileState.js'; import {createCanvasContext2D} from './dom.js'; @@ -21,10 +21,7 @@ import {UNDEFINED} from './functions.js'; */ -/** - * @extends {module:ol/Tile} - */ -class VectorImageTile { +class VectorImageTile extends Tile { /** * @param {module:ol/tilecoord~TileCoord} tileCoord Tile coordinate. @@ -46,25 +43,11 @@ class VectorImageTile { * Function to call when a source tile's state changes. * @param {number} zoom Integer zoom to render the tile for. */ - constructor( - tileCoord, - state, - sourceRevision, - format, - tileLoadFunction, - urlTileCoord, - tileUrlFunction, - sourceTileGrid, - tileGrid, - sourceTiles, - pixelRatio, - projection, - tileClass, - handleTileChange, - zoom - ) { + constructor(tileCoord, state, sourceRevision, format, tileLoadFunction, + urlTileCoord, tileUrlFunction, sourceTileGrid, tileGrid, sourceTiles, + pixelRatio, projection, tileClass, handleTileChange, zoom) { - Tile.call(this, tileCoord, state, {transition: 0}); + super(tileCoord, state, {transition: 0}); /** * @private @@ -201,7 +184,7 @@ class VectorImageTile { this.loadListenerKeys_.length = 0; this.sourceTileListenerKeys_.forEach(unlistenByKey); this.sourceTileListenerKeys_.length = 0; - Tile.prototype.disposeInternal.call(this); + super.disposeInternal(); } /** @@ -331,8 +314,6 @@ class VectorImageTile { } } -inherits(VectorImageTile, Tile); - export default VectorImageTile; From 9c994138ad46fe77d862644c1695d86709434276 Mon Sep 17 00:00:00 2001 From: ahocevar Date: Tue, 17 Jul 2018 14:19:43 +0200 Subject: [PATCH 029/107] Use extends and super for VectorTile --- src/ol/VectorTile.js | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/src/ol/VectorTile.js b/src/ol/VectorTile.js index 5341573e84..545228da8e 100644 --- a/src/ol/VectorTile.js +++ b/src/ol/VectorTile.js @@ -1,7 +1,7 @@ /** * @module ol/VectorTile */ -import {getUid, inherits} from './util.js'; +import {getUid} from './util.js'; import Tile from './Tile.js'; import TileState from './TileState.js'; @@ -18,10 +18,7 @@ const DEFAULT_EXTENT = [0, 0, 4096, 4096]; * @api */ -/** - * @extends {module:ol/Tile} - */ -class VectorTile { +class VectorTile extends Tile { /** * @param {module:ol/tilecoord~TileCoord} tileCoord Tile coordinate. @@ -33,7 +30,7 @@ class VectorTile { */ constructor(tileCoord, state, src, format, tileLoadFunction, opt_options) { - Tile.call(this, tileCoord, state, opt_options); + super(tileCoord, state, opt_options); /** * @type {number} @@ -99,7 +96,7 @@ class VectorTile { this.replayGroups_ = {}; this.state = TileState.ABORT; this.changed(); - Tile.prototype.disposeInternal.call(this); + super.disposeInternal(); } /** @@ -243,6 +240,4 @@ class VectorTile { } } -inherits(VectorTile, Tile); - export default VectorTile; From 4f26f145ae1b90f1b9da11598d618288b639cad1 Mon Sep 17 00:00:00 2001 From: ahocevar Date: Tue, 17 Jul 2018 14:20:48 +0200 Subject: [PATCH 030/107] Use extends and super for View --- src/ol/View.js | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/src/ol/View.js b/src/ol/View.js index 1e34fe7d4c..dfdf355f97 100644 --- a/src/ol/View.js +++ b/src/ol/View.js @@ -2,7 +2,7 @@ * @module ol/View */ import {DEFAULT_TILE_SIZE} from './tilegrid/common.js'; -import {getUid, inherits} from './util.js'; +import {getUid} from './util.js'; import {UNDEFINED} from './functions.js'; import {createExtent, none as centerNone} from './centerconstraint.js'; import BaseObject from './Object.js'; @@ -224,16 +224,15 @@ const DEFAULT_MIN_ZOOM = 0; * The *center constraint* is determined by the `extent` option. By * default the center is not constrained at all. * - * @extends {module:ol/Object} - * @api + * @api */ -class View { +class View extends BaseObject { /** * @param {module:ol/View~ViewOptions=} opt_options View options. */ constructor(opt_options) { - BaseObject.call(this); + super(); const options = assign({}, opt_options); @@ -1181,8 +1180,6 @@ class View { } } -inherits(View, BaseObject); - /** * @param {module:ol/View~ViewOptions} options View options. From 778e867c7978564440eac3e8f36b442aa1ab58a4 Mon Sep 17 00:00:00 2001 From: ahocevar Date: Tue, 17 Jul 2018 14:21:50 +0200 Subject: [PATCH 031/107] Use extends and super for WebGLMap --- src/ol/WebGLMap.js | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/ol/WebGLMap.js b/src/ol/WebGLMap.js index 97d29032af..cea3990b0c 100644 --- a/src/ol/WebGLMap.js +++ b/src/ol/WebGLMap.js @@ -1,7 +1,6 @@ /** * @module ol/WebGLMap */ -import {inherits} from './util.js'; import PluggableMap from './PluggableMap.js'; import {defaults as defaultControls} from './control.js'; import {defaults as defaultInteractions} from './interaction.js'; @@ -57,14 +56,13 @@ import WebGLVectorLayerRenderer from './renderer/webgl/VectorLayer.js'; * {@link module:ol/layer/Base}, so layers entered in the options or added * with `addLayer` can be groups, which can contain further groups, and so on. * - * @extends {module:ol/PluggableMap} * @fires module:ol/MapBrowserEvent~MapBrowserEvent * @fires module:ol/MapEvent~MapEvent * @fires module:ol/render/Event~RenderEvent#postcompose * @fires module:ol/render/Event~RenderEvent#precompose * @api */ -class WebGLMap { +class WebGLMap extends PluggableMap { /** * @param {module:ol/PluggableMap~MapOptions} options Map options. @@ -78,7 +76,7 @@ class WebGLMap { options.interactions = defaultInteractions(); } - PluggableMap.call(this, options); + super(options); } createRenderer() { @@ -92,7 +90,5 @@ class WebGLMap { } } -inherits(WebGLMap, PluggableMap); - export default WebGLMap; From 49c4a75777162918dba4ef78728b7020f6563b7d Mon Sep 17 00:00:00 2001 From: ahocevar Date: Tue, 17 Jul 2018 14:24:17 +0200 Subject: [PATCH 032/107] Use extends and super for control/Attribution --- src/ol/control/Attribution.js | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/src/ol/control/Attribution.js b/src/ol/control/Attribution.js index 2699c0d489..e3531e90e6 100644 --- a/src/ol/control/Attribution.js +++ b/src/ol/control/Attribution.js @@ -1,7 +1,6 @@ /** * @module ol/control/Attribution */ -import {inherits} from '../util.js'; import {equals} from '../array.js'; import Control from '../control/Control.js'; import {CLASS_CONTROL, CLASS_UNSELECTABLE, CLASS_COLLAPSED} from '../css.js'; @@ -43,15 +42,20 @@ import {visibleAtResolution} from '../layer/Layer.js'; * be changed by using a css selector for `.ol-attribution`. * * @constructor - * @extends {module:ol/control/Control} * @param {module:ol/control/Attribution~Options=} opt_options Attribution options. * @api */ -class Attribution { +class Attribution extends Control { constructor(opt_options) { const options = opt_options ? opt_options : {}; + super({ + element: document.createElement('div'), + render: options.render || render, + target: options.target + }); + /** * @private * @type {Element} @@ -118,17 +122,11 @@ class Attribution { const cssClasses = className + ' ' + CLASS_UNSELECTABLE + ' ' + CLASS_CONTROL + (this.collapsed_ && this.collapsible_ ? ' ' + CLASS_COLLAPSED : '') + (this.collapsible_ ? '' : ' ol-uncollapsible'); - const element = document.createElement('div'); + const element = this.element; element.className = cssClasses; element.appendChild(this.ulElement_); element.appendChild(button); - Control.call(this, { - element: element, - render: options.render || render, - target: options.target - }); - /** * A list of currently rendered resolutions. * @type {Array.} @@ -312,8 +310,6 @@ class Attribution { } } -inherits(Attribution, Control); - /** * Update the attribution element. From 322a51d8220347a3127cf1df5aae6c54ffe6414b Mon Sep 17 00:00:00 2001 From: ahocevar Date: Tue, 17 Jul 2018 14:25:26 +0200 Subject: [PATCH 033/107] Use extends and super for control/Control --- src/ol/control/Control.js | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/src/ol/control/Control.js b/src/ol/control/Control.js index d02c61f9fa..de7215e614 100644 --- a/src/ol/control/Control.js +++ b/src/ol/control/Control.js @@ -1,7 +1,6 @@ /** * @module ol/control/Control */ -import {inherits} from '../util.js'; import {UNDEFINED} from '../functions.js'; import MapEventType from '../MapEventType.js'; import BaseObject from '../Object.js'; @@ -45,14 +44,13 @@ import {listen, unlistenByKey} from '../events.js'; * examples/custom-controls for an example of how to do this. * * @constructor - * @extends {module:ol/Object} * @param {module:ol/control/Control~Options} options Control options. * @api */ -class Control { +class Control extends BaseObject { constructor(options) { - BaseObject.call(this); + super(); /** * @protected @@ -94,7 +92,7 @@ class Control { */ disposeInternal() { removeNode(this.element); - BaseObject.prototype.disposeInternal.call(this); + super.disposeInternal(); } /** @@ -150,7 +148,5 @@ class Control { } } -inherits(Control, BaseObject); - export default Control; From b9f08c6a2a4f84637dfffb32b593500ae18f9c77 Mon Sep 17 00:00:00 2001 From: ahocevar Date: Tue, 17 Jul 2018 14:26:38 +0200 Subject: [PATCH 034/107] Use extends and super for control/FullScreen --- src/ol/control/FullScreen.js | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/src/ol/control/FullScreen.js b/src/ol/control/FullScreen.js index 2abd9ac2d9..5bc3487537 100644 --- a/src/ol/control/FullScreen.js +++ b/src/ol/control/FullScreen.js @@ -1,7 +1,6 @@ /** * @module ol/control/FullScreen */ -import {inherits} from '../util.js'; import Control from '../control/Control.js'; import {CLASS_CONTROL, CLASS_UNSELECTABLE, CLASS_UNSUPPORTED} from '../css.js'; import {replaceNode} from '../dom.js'; @@ -63,15 +62,19 @@ const getChangeType = (function() { * * * @constructor - * @extends {module:ol/control/Control} * @param {module:ol/control/FullScreen~Options=} opt_options Options. * @api */ -class FullScreen { +class FullScreen extends Control { constructor(opt_options) { const options = opt_options ? opt_options : {}; + super({ + element: document.createElement('div'), + target: options.target + }); + /** * @private * @type {string} @@ -110,15 +113,10 @@ class FullScreen { const cssClasses = this.cssClassName_ + ' ' + CLASS_UNSELECTABLE + ' ' + CLASS_CONTROL + ' ' + (!isFullScreenSupported() ? CLASS_UNSUPPORTED : ''); - const element = document.createElement('div'); + const element = this.element; element.className = cssClasses; element.appendChild(button); - Control.call(this, { - element: element, - target: options.target - }); - /** * @private * @type {boolean} @@ -196,7 +194,7 @@ class FullScreen { * @api */ setMap(map) { - Control.prototype.setMap.call(this, map); + super.setMap(map); if (map) { this.listenerKeys.push(listen(document, getChangeType(), @@ -206,8 +204,6 @@ class FullScreen { } } -inherits(FullScreen, Control); - /** * @return {boolean} Fullscreen is supported by the current platform. From 353a00af896ca72bfbd0136d5a87d2f3e0fd043c Mon Sep 17 00:00:00 2001 From: ahocevar Date: Tue, 17 Jul 2018 14:28:10 +0200 Subject: [PATCH 035/107] Use extends and super for control/MousePosition --- src/ol/control/MousePosition.js | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/src/ol/control/MousePosition.js b/src/ol/control/MousePosition.js index b4c17da69c..666d30c558 100644 --- a/src/ol/control/MousePosition.js +++ b/src/ol/control/MousePosition.js @@ -1,8 +1,6 @@ /** * @module ol/control/MousePosition */ - -import {inherits} from '../util.js'; import {listen} from '../events.js'; import EventType from '../events/EventType.js'; import {getChangeEventType} from '../Object.js'; @@ -47,12 +45,11 @@ const COORDINATE_FORMAT = 'coordinateFormat'; * can be changed by using the css selector `.ol-mouse-position`. * * @constructor - * @extends {module:ol/control/Control} * @param {module:ol/control/MousePosition~Options=} opt_options Mouse position * options. * @api */ -class MousePosition { +class MousePosition extends Control { constructor(opt_options) { const options = opt_options ? opt_options : {}; @@ -60,7 +57,7 @@ class MousePosition { const element = document.createElement('DIV'); element.className = options.className !== undefined ? options.className : 'ol-mouse-position'; - Control.call(this, { + super({ element: element, render: options.render || render, target: options.target @@ -173,7 +170,7 @@ class MousePosition { * @api */ setMap(map) { - Control.prototype.setMap.call(this, map); + super.setMap(map); if (map) { const viewport = map.getViewport(); this.listenerKeys.push( @@ -244,8 +241,6 @@ class MousePosition { } } -inherits(MousePosition, Control); - /** * Update the mouseposition element. From 6ffda39b140da5ecb278228711faf2b81cbc18b8 Mon Sep 17 00:00:00 2001 From: ahocevar Date: Tue, 17 Jul 2018 14:29:27 +0200 Subject: [PATCH 036/107] Use extends and super for control/OverviewMap --- src/ol/control/OverviewMap.js | 22 +++++++++------------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/src/ol/control/OverviewMap.js b/src/ol/control/OverviewMap.js index 9849dcb538..5095040040 100644 --- a/src/ol/control/OverviewMap.js +++ b/src/ol/control/OverviewMap.js @@ -1,7 +1,6 @@ /** * @module ol/control/OverviewMap */ -import {inherits} from '../util.js'; import Collection from '../Collection.js'; import Map from '../Map.js'; import MapEventType from '../MapEventType.js'; @@ -62,15 +61,20 @@ const MIN_RATIO = 0.1; * Create a new control with a map acting as an overview map for an other * defined map. * @constructor - * @extends {module:ol/control/Control} * @param {module:ol/control/OverviewMap~Options=} opt_options OverviewMap options. * @api */ -class OverviewMap { +class OverviewMap extends Control { constructor(opt_options) { const options = opt_options ? opt_options : {}; + super({ + element: document.createElement('div'), + render: options.render || render, + target: options.target + }); + /** * @type {boolean} * @private @@ -175,17 +179,11 @@ class OverviewMap { const cssClasses = className + ' ' + CLASS_UNSELECTABLE + ' ' + CLASS_CONTROL + (this.collapsed_ && this.collapsible_ ? ' ' + CLASS_COLLAPSED : '') + (this.collapsible_ ? '' : ' ol-uncollapsible'); - const element = document.createElement('div'); + const element = this.element; element.className = cssClasses; element.appendChild(this.ovmapDiv_); element.appendChild(button); - Control.call(this, { - element: element, - render: options.render || render, - target: options.target - }); - /* Interactive map */ const scope = this; @@ -241,7 +239,7 @@ class OverviewMap { } this.ovmap_.setTarget(null); } - Control.prototype.setMap.call(this, map); + super.setMap(map); if (map) { this.ovmap_.setTarget(this.ovmapDiv_); @@ -565,8 +563,6 @@ class OverviewMap { } } -inherits(OverviewMap, Control); - /** * Update the overview map element. From 7d5efd03485b3dc6e00dc2f9b6d7ecd7637270df Mon Sep 17 00:00:00 2001 From: ahocevar Date: Tue, 17 Jul 2018 14:31:31 +0200 Subject: [PATCH 037/107] Use extends and super for control/Rotate --- src/ol/control/Rotate.js | 24 +++++++++--------------- 1 file changed, 9 insertions(+), 15 deletions(-) diff --git a/src/ol/control/Rotate.js b/src/ol/control/Rotate.js index 6d8336827c..a12205dce0 100644 --- a/src/ol/control/Rotate.js +++ b/src/ol/control/Rotate.js @@ -1,13 +1,11 @@ /** * @module ol/control/Rotate */ - import Control from '../control/Control.js'; import {CLASS_CONTROL, CLASS_HIDDEN, CLASS_UNSELECTABLE} from '../css.js'; import {easeOut} from '../easing.js'; import {listen} from '../events.js'; import EventType from '../events/EventType.js'; -import {inherits} from '../util.js'; /** @@ -34,15 +32,20 @@ import {inherits} from '../util.js'; * selector is added to the button when the rotation is 0. * * @constructor - * @extends {module:ol/control/Control} * @param {module:ol/control/Rotate~Options=} opt_options Rotate options. * @api */ -class Rotate { +class Rotate extends Control { constructor(opt_options) { const options = opt_options ? opt_options : {}; + super({ + element: document.createElement('div'), + render: options.render || render, + target: options.target + }); + const className = options.className !== undefined ? options.className : 'ol-rotate'; const label = options.label !== undefined ? options.label : '\u21E7'; @@ -70,22 +73,15 @@ class Rotate { button.title = tipLabel; button.appendChild(this.label_); - listen(button, EventType.CLICK, - Rotate.prototype.handleClick_, this); + listen(button, EventType.CLICK, this.handleClick_, this); const cssClasses = className + ' ' + CLASS_UNSELECTABLE + ' ' + CLASS_CONTROL; - const element = document.createElement('div'); + const element = this.element; element.className = cssClasses; element.appendChild(button); this.callResetNorth_ = options.resetNorth ? options.resetNorth : undefined; - Control.call(this, { - element: element, - render: options.render || render, - target: options.target - }); - /** * @type {number} * @private @@ -148,8 +144,6 @@ class Rotate { } } -inherits(Rotate, Control); - /** * Update the rotate control element. From 1da14a6d9775e659ccd0e6cc89746edce624bdda Mon Sep 17 00:00:00 2001 From: ahocevar Date: Tue, 17 Jul 2018 14:35:47 +0200 Subject: [PATCH 038/107] Use extends and super for control/ScaleLine --- src/ol/control/ScaleLine.js | 33 ++++++++++++--------------------- 1 file changed, 12 insertions(+), 21 deletions(-) diff --git a/src/ol/control/ScaleLine.js b/src/ol/control/ScaleLine.js index 1b6000e377..6fbf7b15cb 100644 --- a/src/ol/control/ScaleLine.js +++ b/src/ol/control/ScaleLine.js @@ -1,7 +1,6 @@ /** * @module ol/control/ScaleLine */ -import {inherits} from '../util.js'; import {getChangeEventType} from '../Object.js'; import {assert} from '../asserts.js'; import Control from '../control/Control.js'; @@ -60,17 +59,22 @@ const LEADING_DIGITS = [1, 2, 5]; * but this can be changed by using the css selector `.ol-scale-line`. * * @constructor - * @extends {module:ol/control/Control} * @param {module:ol/control/ScaleLine~Options=} opt_options Scale line options. * @api */ -class ScaleLine { +class ScaleLine extends Control { constructor(opt_options) { const options = opt_options ? opt_options : {}; const className = options.className !== undefined ? options.className : 'ol-scale-line'; + super({ + element: document.createElement('DIV'), + render: options.render || render, + target: options.target + }); + /** * @private * @type {HTMLElement} @@ -78,13 +82,8 @@ class ScaleLine { this.innerElement_ = document.createElement('DIV'); this.innerElement_.className = className + '-inner'; - /** - * @private - * @type {HTMLElement} - */ - this.element_ = document.createElement('DIV'); - this.element_.className = className + ' ' + CLASS_UNSELECTABLE; - this.element_.appendChild(this.innerElement_); + this.element.className = className + ' ' + CLASS_UNSELECTABLE; + this.element.appendChild(this.innerElement_); /** * @private @@ -116,12 +115,6 @@ class ScaleLine { */ this.renderedHTML_ = ''; - Control.call(this, { - element: this.element_, - render: options.render || render, - target: options.target - }); - listen( this, getChangeEventType(UNITS_PROP), this.handleUnitsChanged_, this); @@ -169,7 +162,7 @@ class ScaleLine { if (!viewState) { if (this.renderedVisible_) { - this.element_.style.display = 'none'; + this.element.style.display = 'none'; this.renderedVisible_ = false; } return; @@ -256,7 +249,7 @@ class ScaleLine { Math.pow(10, Math.floor(i / 3)); width = Math.round(count / pointResolution); if (isNaN(width)) { - this.element_.style.display = 'none'; + this.element.style.display = 'none'; this.renderedVisible_ = false; return; } else if (width >= this.minWidth_) { @@ -277,15 +270,13 @@ class ScaleLine { } if (!this.renderedVisible_) { - this.element_.style.display = ''; + this.element.style.display = ''; this.renderedVisible_ = true; } } } -inherits(ScaleLine, Control); - /** * Update the scale line element. From 600205f4d9529998d27d65c54c07e88c6bee8e17 Mon Sep 17 00:00:00 2001 From: ahocevar Date: Tue, 17 Jul 2018 14:44:27 +0200 Subject: [PATCH 039/107] Use extends and super for control/Zoom --- src/ol/control/Zoom.js | 24 +++++++++--------------- 1 file changed, 9 insertions(+), 15 deletions(-) diff --git a/src/ol/control/Zoom.js b/src/ol/control/Zoom.js index e8ecbe4f65..20832e274c 100644 --- a/src/ol/control/Zoom.js +++ b/src/ol/control/Zoom.js @@ -1,7 +1,6 @@ /** * @module ol/control/Zoom */ -import {inherits} from '../util.js'; import {listen} from '../events.js'; import EventType from '../events/EventType.js'; import Control from '../control/Control.js'; @@ -32,15 +31,19 @@ import {easeOut} from '../easing.js'; * use css selectors `.ol-zoom-in` and `.ol-zoom-out`. * * @constructor - * @extends {module:ol/control/Control} * @param {module:ol/control/Zoom~Options=} opt_options Zoom options. * @api */ -class Zoom { +class Zoom extends Control { constructor(opt_options) { const options = opt_options ? opt_options : {}; + super({ + element: document.createElement('div'), + target: options.target + }); + const className = options.className !== undefined ? options.className : 'ol-zoom'; const delta = options.delta !== undefined ? options.delta : 1; @@ -61,8 +64,7 @@ class Zoom { typeof zoomInLabel === 'string' ? document.createTextNode(zoomInLabel) : zoomInLabel ); - listen(inElement, EventType.CLICK, - Zoom.prototype.handleClick_.bind(this, delta)); + listen(inElement, EventType.CLICK, this.handleClick_.bind(this, delta)); const outElement = document.createElement('button'); outElement.className = className + '-out'; @@ -72,20 +74,14 @@ class Zoom { typeof zoomOutLabel === 'string' ? document.createTextNode(zoomOutLabel) : zoomOutLabel ); - listen(outElement, EventType.CLICK, - Zoom.prototype.handleClick_.bind(this, -delta)); + listen(outElement, EventType.CLICK, this.handleClick_.bind(this, -delta)); const cssClasses = className + ' ' + CLASS_UNSELECTABLE + ' ' + CLASS_CONTROL; - const element = document.createElement('div'); + const element = this.element; element.className = cssClasses; element.appendChild(inElement); element.appendChild(outElement); - Control.call(this, { - element: element, - target: options.target - }); - /** * @type {number} * @private @@ -135,7 +131,5 @@ class Zoom { } } -inherits(Zoom, Control); - export default Zoom; From 11f1f5b25c0cf6dba1cf3de6e3d01f525c8d40fd Mon Sep 17 00:00:00 2001 From: ahocevar Date: Tue, 17 Jul 2018 14:58:12 +0200 Subject: [PATCH 040/107] Use new eslint config to enforce no-this-before-super --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 0cfc0339d3..0f6d53058b 100644 --- a/package.json +++ b/package.json @@ -49,7 +49,7 @@ "copy-webpack-plugin": "^4.4.1", "coveralls": "3.0.1", "eslint": "5.0.1", - "eslint-config-openlayers": "^9.2.0", + "eslint-config-openlayers": "^10.0.0", "expect.js": "0.3.1", "front-matter": "^2.1.2", "fs-extra": "^6.0.0", From 65301199182dfb713a8302547465253f2c17c0f3 Mon Sep 17 00:00:00 2001 From: ahocevar Date: Tue, 17 Jul 2018 14:58:37 +0200 Subject: [PATCH 041/107] Use extends and super for control/ZoomSlider --- src/ol/control/ZoomSlider.js | 22 +++++++++------------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/src/ol/control/ZoomSlider.js b/src/ol/control/ZoomSlider.js index 9cea621fcb..6dbc53ab42 100644 --- a/src/ol/control/ZoomSlider.js +++ b/src/ol/control/ZoomSlider.js @@ -1,7 +1,6 @@ /** * @module ol/control/ZoomSlider */ -import {inherits} from '../util.js'; import ViewHint from '../ViewHint.js'; import Control from '../control/Control.js'; import {CLASS_CONTROL, CLASS_UNSELECTABLE} from '../css.js'; @@ -43,15 +42,19 @@ const Direction = { * map.addControl(new ZoomSlider()); * * @constructor - * @extends {module:ol/control/Control} * @param {module:ol/control/ZoomSlider~Options=} opt_options Zoom slider options. * @api */ -class ZoomSlider { +class ZoomSlider extends Control { constructor(opt_options) { const options = opt_options ? opt_options : {}; + super({ + element: document.createElement('div'), + render: options.render || render + }); + /** * Will hold the current resolution of the view. * @@ -124,7 +127,7 @@ class ZoomSlider { const thumbElement = document.createElement('button'); thumbElement.setAttribute('type', 'button'); thumbElement.className = className + '-thumb ' + CLASS_UNSELECTABLE; - const containerElement = document.createElement('div'); + const containerElement = this.element; containerElement.className = className + ' ' + CLASS_UNSELECTABLE + ' ' + CLASS_CONTROL; containerElement.appendChild(thumbElement); /** @@ -142,11 +145,6 @@ class ZoomSlider { listen(containerElement, EventType.CLICK, this.handleContainerClick_, this); listen(thumbElement, EventType.CLICK, stopPropagation); - - Control.call(this, { - element: containerElement, - render: options.render || render - }); } /** @@ -154,14 +152,14 @@ class ZoomSlider { */ disposeInternal() { this.dragger_.dispose(); - Control.prototype.disposeInternal.call(this); + super.disposeInternal(); } /** * @inheritDoc */ setMap(map) { - Control.prototype.setMap.call(this, map); + super.setMap(map); if (map) { map.render(); } @@ -341,8 +339,6 @@ class ZoomSlider { } } -inherits(ZoomSlider, Control); - /** * Update the zoomslider element. From f5272bb391782f3ca3abeab9ccdb930d4364a252 Mon Sep 17 00:00:00 2001 From: ahocevar Date: Tue, 17 Jul 2018 15:02:38 +0200 Subject: [PATCH 042/107] Use extends and super for control/ZoomToExtent --- src/ol/control/ZoomToExtent.js | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/src/ol/control/ZoomToExtent.js b/src/ol/control/ZoomToExtent.js index a73951a026..35997066ae 100644 --- a/src/ol/control/ZoomToExtent.js +++ b/src/ol/control/ZoomToExtent.js @@ -1,7 +1,6 @@ /** * @module ol/control/ZoomToExtent */ -import {inherits} from '../util.js'; import {listen} from '../events.js'; import EventType from '../events/EventType.js'; import Control from '../control/Control.js'; @@ -27,14 +26,18 @@ import {CLASS_CONTROL, CLASS_UNSELECTABLE} from '../css.js'; * extent. To style this control use the css selector `.ol-zoom-extent`. * * @constructor - * @extends {module:ol/control/Control} * @param {module:ol/control/ZoomToExtent~Options=} opt_options Options. * @api */ -class ZoomToExtent { +class ZoomToExtent extends Control { constructor(opt_options) { const options = opt_options ? opt_options : {}; + super({ + element: document.createElement('div'), + target: options.target + }); + /** * @type {module:ol/extent~Extent} * @protected @@ -55,14 +58,9 @@ class ZoomToExtent { listen(button, EventType.CLICK, this.handleClick_, this); const cssClasses = className + ' ' + CLASS_UNSELECTABLE + ' ' + CLASS_CONTROL; - const element = document.createElement('div'); + const element = this.element; element.className = cssClasses; element.appendChild(button); - - Control.call(this, { - element: element, - target: options.target - }); } /** From a235dc906f05d5dcafbe55ba882289ac388cd91d Mon Sep 17 00:00:00 2001 From: ahocevar Date: Tue, 17 Jul 2018 16:01:04 +0200 Subject: [PATCH 043/107] Use extends and super for events/EventTarget --- src/ol/events/EventTarget.js | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/ol/events/EventTarget.js b/src/ol/events/EventTarget.js index bd3179aa7b..71e5d3e42b 100644 --- a/src/ol/events/EventTarget.js +++ b/src/ol/events/EventTarget.js @@ -1,7 +1,6 @@ /** * @module ol/events/EventTarget */ -import {inherits} from '../util.js'; import Disposable from '../Disposable.js'; import {unlistenAll} from '../events.js'; import {UNDEFINED} from '../functions.js'; @@ -29,12 +28,11 @@ import Event from '../events/Event.js'; * returns false. * * @constructor - * @extends {module:ol/Disposable} */ -class EventTarget { +class EventTarget extends Disposable { constructor() { - Disposable.call(this); + super(); /** * @private @@ -165,7 +163,5 @@ class EventTarget { } } -inherits(EventTarget, Disposable); - export default EventTarget; From 44bea898b8884442cbf5cde85581ff6e3128d8ab Mon Sep 17 00:00:00 2001 From: ahocevar Date: Tue, 17 Jul 2018 16:10:11 +0200 Subject: [PATCH 044/107] Use extends and super for format/JSONFeature --- src/ol/format/EsriJSON.js | 93 +----------------------------------- src/ol/format/JSONFeature.js | 50 ++++++++++++++++--- 2 files changed, 45 insertions(+), 98 deletions(-) diff --git a/src/ol/format/EsriJSON.js b/src/ol/format/EsriJSON.js index 0f388a10b2..7c5e4fc080 100644 --- a/src/ol/format/EsriJSON.js +++ b/src/ol/format/EsriJSON.js @@ -1,7 +1,6 @@ /** * @module ol/format/EsriJSON */ -import {inherits} from '../util.js'; import Feature from '../Feature.js'; import {assert} from '../asserts.js'; import {containsExtent} from '../extent.js'; @@ -58,10 +57,9 @@ GEOMETRY_WRITERS[GeometryType.MULTI_POLYGON] = writeMultiPolygonGeometry; * @classdesc * Feature format for reading and writing data in the EsriJSON format. * - * @extends {module:ol/format/JSONFeature} * @api */ -class EsriJSON { +class EsriJSON extends JSONFeature { /** * @param {module:ol/format/EsriJSON~Options=} opt_options Options. @@ -70,7 +68,7 @@ class EsriJSON { const options = opt_options ? opt_options : {}; - JSONFeature.call(this); + super(); /** * Name of the geometry attribute for features. @@ -208,8 +206,6 @@ class EsriJSON { } } -inherits(EsriJSON, JSONFeature); - /** * @param {EsriJSONGeometry} object Object. @@ -543,55 +539,6 @@ function writeMultiPolygonGeometry(geometry, opt_options) { } -/** - * Read a feature from a EsriJSON Feature source. Only works for Feature, - * use `readFeatures` to read FeatureCollection source. - * - * @function - * @param {ArrayBuffer|Document|Node|Object|string} source Source. - * @param {module:ol/format/Feature~ReadOptions=} opt_options Read options. - * @return {module:ol/Feature} Feature. - * @api - */ -EsriJSON.prototype.readFeature; - - -/** - * Read all features from a EsriJSON source. Works with both Feature and - * FeatureCollection sources. - * - * @function - * @param {ArrayBuffer|Document|Node|Object|string} source Source. - * @param {module:ol/format/Feature~ReadOptions=} opt_options Read options. - * @return {Array.} Features. - * @api - */ -EsriJSON.prototype.readFeatures; - - -/** - * Read a geometry from a EsriJSON source. - * - * @function - * @param {ArrayBuffer|Document|Node|Object|string} source Source. - * @param {module:ol/format/Feature~ReadOptions=} opt_options Read options. - * @return {module:ol/geom/Geometry} Geometry. - * @api - */ -EsriJSON.prototype.readGeometry; - - -/** - * Read the projection from a EsriJSON source. - * - * @function - * @param {ArrayBuffer|Document|Node|Object|string} source Source. - * @return {module:ol/proj/Projection} Projection. - * @api - */ -EsriJSON.prototype.readProjection; - - /** * @param {module:ol/geom/Geometry} geometry Geometry. * @param {module:ol/format/Feature~WriteOptions=} opt_options Write options. @@ -604,40 +551,4 @@ function writeGeometry(geometry, opt_options) { } -/** - * Encode a geometry as a EsriJSON string. - * - * @function - * @param {module:ol/geom/Geometry} geometry Geometry. - * @param {module:ol/format/Feature~WriteOptions=} opt_options Write options. - * @return {string} EsriJSON. - * @api - */ -EsriJSON.prototype.writeGeometry; - - -/** - * Encode a feature as a EsriJSON Feature string. - * - * @function - * @param {module:ol/Feature} feature Feature. - * @param {module:ol/format/Feature~WriteOptions=} opt_options Write options. - * @return {string} EsriJSON. - * @api - */ -EsriJSON.prototype.writeFeature; - - -/** - * Encode an array of features as EsriJSON. - * - * @function - * @param {Array.} features Features. - * @param {module:ol/format/Feature~WriteOptions=} opt_options Write options. - * @return {string} EsriJSON. - * @api - */ -EsriJSON.prototype.writeFeatures; - - export default EsriJSON; diff --git a/src/ol/format/JSONFeature.js b/src/ol/format/JSONFeature.js index 682bdc87e5..dfdc63c041 100644 --- a/src/ol/format/JSONFeature.js +++ b/src/ol/format/JSONFeature.js @@ -27,7 +27,13 @@ class JSONFeature { } /** - * @inheritDoc + * Read a feature. Only works for a single feature. Use `readFeatures` to + * read a feature collection. + * + * @param {ArrayBuffer|Document|Node|Object|string} source Source. + * @param {module:ol/format/Feature~ReadOptions=} opt_options Read options. + * @return {module:ol/Feature} Feature. + * @api */ readFeature(source, opt_options) { return this.readFeatureFromObject( @@ -35,7 +41,13 @@ class JSONFeature { } /** - * @inheritDoc + * Read all features. Works with both a single feature and a feature + * collection. + * + * @param {ArrayBuffer|Document|Node|Object|string} source Source. + * @param {module:ol/format/Feature~ReadOptions=} opt_options Read options. + * @return {Array.} Features. + * @api */ readFeatures(source, opt_options) { return this.readFeaturesFromObject( @@ -61,7 +73,12 @@ class JSONFeature { readFeaturesFromObject(object, opt_options) {} /** - * @inheritDoc + * Read a geometry. + * + * @param {ArrayBuffer|Document|Node|Object|string} source Source. + * @param {module:ol/format/Feature~ReadOptions=} opt_options Read options. + * @return {module:ol/geom/Geometry} Geometry. + * @api */ readGeometry(source, opt_options) { return this.readGeometryFromObject( @@ -78,7 +95,11 @@ class JSONFeature { readGeometryFromObject(object, opt_options) {} /** - * @inheritDoc + * Read the projection. + * + * @param {ArrayBuffer|Document|Node|Object|string} source Source. + * @return {module:ol/proj/Projection} Projection. + * @api */ readProjection(source) { return this.readProjectionFromObject(getObject(source)); @@ -93,7 +114,12 @@ class JSONFeature { readProjectionFromObject(object) {} /** - * @inheritDoc + * Encode a feature as string. + * + * @param {module:ol/Feature} feature Feature. + * @param {module:ol/format/Feature~WriteOptions=} opt_options Write options. + * @return {string} Encoded feature. + * @api */ writeFeature(feature, opt_options) { return JSON.stringify(this.writeFeatureObject(feature, opt_options)); @@ -108,7 +134,12 @@ class JSONFeature { writeFeatureObject(feature, opt_options) {} /** - * @inheritDoc + * Encode an array of features as string. + * + * @param {Array.} features Features. + * @param {module:ol/format/Feature~WriteOptions=} opt_options Write options. + * @return {string} Encoded features. + * @api */ writeFeatures(features, opt_options) { return JSON.stringify(this.writeFeaturesObject(features, opt_options)); @@ -123,7 +154,12 @@ class JSONFeature { writeFeaturesObject(features, opt_options) {} /** - * @inheritDoc + * Encode a geometry as string. + * + * @param {module:ol/geom/Geometry} geometry Geometry. + * @param {module:ol/format/Feature~WriteOptions=} opt_options Write options. + * @return {string} Encoded geometry. + * @api */ writeGeometry(geometry, opt_options) { return JSON.stringify(this.writeGeometryObject(geometry, opt_options)); From c707b4c74688e571f5bb5d86817d473ceedbc755 Mon Sep 17 00:00:00 2001 From: Frederic Junod Date: Tue, 17 Jul 2018 16:18:30 +0200 Subject: [PATCH 045/107] Use extends and super for geom --- src/ol/geom/Circle.js | 22 ++- src/ol/geom/Geometry.js | 249 +++++++++++++++--------------- src/ol/geom/GeometryCollection.js | 14 +- src/ol/geom/LineString.js | 19 +-- src/ol/geom/LinearRing.js | 19 +-- src/ol/geom/MultiLineString.js | 22 ++- src/ol/geom/MultiPoint.js | 19 +-- src/ol/geom/MultiPolygon.js | 22 ++- src/ol/geom/Point.js | 16 +- src/ol/geom/Polygon.js | 31 ++-- src/ol/geom/SimpleGeometry.js | 9 +- 11 files changed, 204 insertions(+), 238 deletions(-) diff --git a/src/ol/geom/Circle.js b/src/ol/geom/Circle.js index a3528fd277..e5c9e59074 100644 --- a/src/ol/geom/Circle.js +++ b/src/ol/geom/Circle.js @@ -1,7 +1,6 @@ /** * @module ol/geom/Circle */ -import {inherits} from '../util.js'; import {createOrUpdate, forEachCorner, intersects} from '../extent.js'; import GeometryType from '../geom/GeometryType.js'; import SimpleGeometry from '../geom/SimpleGeometry.js'; @@ -11,18 +10,19 @@ import {deflateCoordinate} from '../geom/flat/deflate.js'; * @classdesc * Circle geometry. * - * @constructor - * @extends {!module:ol/geom/SimpleGeometry} - * @param {!module:ol/coordinate~Coordinate} center Center. (For internal use, - * flat coordinates in combination with `opt_layout` and no `opt_radius` are - * also accepted.) - * @param {number=} opt_radius Radius. - * @param {module:ol/geom/GeometryLayout=} opt_layout Layout. * @api */ -class Circle { +class Circle extends SimpleGeometry { + + /** + * @param {!module:ol/coordinate~Coordinate} center Center. + * For internal use, flat coordinates in combination with `opt_layout` and no + * `opt_radius` are also accepted. + * @param {number=} opt_radius Radius. + * @param {module:ol/geom/GeometryLayout=} opt_layout Layout. + */ constructor(center, opt_radius, opt_layout) { - SimpleGeometry.call(this); + super(); if (opt_layout !== undefined && opt_radius === undefined) { this.setFlatCoordinates(opt_layout, center); } else { @@ -212,8 +212,6 @@ class Circle { } } -inherits(Circle, SimpleGeometry); - /** * Transform each coordinate of the circle from one coordinate reference system diff --git a/src/ol/geom/Geometry.js b/src/ol/geom/Geometry.js index 7df226d66a..80fbc90641 100644 --- a/src/ol/geom/Geometry.js +++ b/src/ol/geom/Geometry.js @@ -1,7 +1,6 @@ /** * @module ol/geom/Geometry */ -import {inherits} from '../util.js'; import BaseObject from '../Object.js'; import {createEmpty, getHeight, returnOrUpdate} from '../extent.js'; import {FALSE} from '../functions.js'; @@ -26,73 +25,71 @@ const tmpTransform = createTransform(); * To get notified of changes to the geometry, register a listener for the * generic `change` event on your geometry instance. * - * @constructor * @abstract - * @extends {module:ol/Object} * @api */ -class Geometry { +class Geometry extends BaseObject { constructor() { - BaseObject.call(this); + super(); /** - * @private - * @type {module:ol/extent~Extent} - */ + * @private + * @type {module:ol/extent~Extent} + */ this.extent_ = createEmpty(); /** - * @private - * @type {number} - */ + * @private + * @type {number} + */ this.extentRevision_ = -1; /** - * @protected - * @type {Object.} - */ + * @protected + * @type {Object.} + */ this.simplifiedGeometryCache = {}; /** - * @protected - * @type {number} - */ + * @protected + * @type {number} + */ this.simplifiedGeometryMaxMinSquaredTolerance = 0; /** - * @protected - * @type {number} - */ + * @protected + * @type {number} + */ this.simplifiedGeometryRevision = 0; } /** - * Make a complete copy of the geometry. - * @abstract - * @return {!module:ol/geom/Geometry} Clone. - */ + * Make a complete copy of the geometry. + * @abstract + * @return {!module:ol/geom/Geometry} Clone. + */ clone() {} /** - * @abstract - * @param {number} x X. - * @param {number} y Y. - * @param {module:ol/coordinate~Coordinate} closestPoint Closest point. - * @param {number} minSquaredDistance Minimum squared distance. - * @return {number} Minimum squared distance. - */ + * @abstract + * @param {number} x X. + * @param {number} y Y. + * @param {module:ol/coordinate~Coordinate} closestPoint Closest point. + * @param {number} minSquaredDistance Minimum squared distance. + * @return {number} Minimum squared distance. + */ closestPointXY(x, y, closestPoint, minSquaredDistance) {} /** - * Return the closest point of the geometry to the passed point as - * {@link module:ol/coordinate~Coordinate coordinate}. - * @param {module:ol/coordinate~Coordinate} point Point. - * @param {module:ol/coordinate~Coordinate=} opt_closestPoint Closest point. - * @return {module:ol/coordinate~Coordinate} Closest point. - * @api - */ + * Return the closest point of the geometry to the passed point as + * {@link module:ol/coordinate~Coordinate coordinate}. + * @param {module:ol/coordinate~Coordinate} point Point. + * @param {module:ol/coordinate~Coordinate=} opt_closestPoint Closest point. + * @return {module:ol/coordinate~Coordinate} Closest point. + * @api + */ getClosestPoint(point, opt_closestPoint) { const closestPoint = opt_closestPoint ? opt_closestPoint : [NaN, NaN]; this.closestPointXY(point[0], point[1], closestPoint, Infinity); @@ -100,30 +97,30 @@ class Geometry { } /** - * Returns true if this geometry includes the specified coordinate. If the - * coordinate is on the boundary of the geometry, returns false. - * @param {module:ol/coordinate~Coordinate} coordinate Coordinate. - * @return {boolean} Contains coordinate. - * @api - */ + * Returns true if this geometry includes the specified coordinate. If the + * coordinate is on the boundary of the geometry, returns false. + * @param {module:ol/coordinate~Coordinate} coordinate Coordinate. + * @return {boolean} Contains coordinate. + * @api + */ intersectsCoordinate(coordinate) { return this.containsXY(coordinate[0], coordinate[1]); } /** - * @abstract - * @param {module:ol/extent~Extent} extent Extent. - * @protected - * @return {module:ol/extent~Extent} extent Extent. - */ + * @abstract + * @param {module:ol/extent~Extent} extent Extent. + * @protected + * @return {module:ol/extent~Extent} extent Extent. + */ computeExtent(extent) {} /** - * Get the extent of the geometry. - * @param {module:ol/extent~Extent=} opt_extent Extent. - * @return {module:ol/extent~Extent} extent Extent. - * @api - */ + * Get the extent of the geometry. + * @param {module:ol/extent~Extent=} opt_extent Extent. + * @return {module:ol/extent~Extent} extent Extent. + * @api + */ getExtent(opt_extent) { if (this.extentRevision_ != this.getRevision()) { this.extent_ = this.computeExtent(this.extent_); @@ -133,103 +130,103 @@ class Geometry { } /** - * Rotate the geometry around a given coordinate. This modifies the geometry - * coordinates in place. - * @abstract - * @param {number} angle Rotation angle in radians. - * @param {module:ol/coordinate~Coordinate} anchor The rotation center. - * @api - */ + * Rotate the geometry around a given coordinate. This modifies the geometry + * coordinates in place. + * @abstract + * @param {number} angle Rotation angle in radians. + * @param {module:ol/coordinate~Coordinate} anchor The rotation center. + * @api + */ rotate(angle, anchor) {} /** - * Scale the geometry (with an optional origin). This modifies the geometry - * coordinates in place. - * @abstract - * @param {number} sx The scaling factor in the x-direction. - * @param {number=} opt_sy The scaling factor in the y-direction (defaults to - * sx). - * @param {module:ol/coordinate~Coordinate=} opt_anchor The scale origin (defaults to the center - * of the geometry extent). - * @api - */ + * Scale the geometry (with an optional origin). This modifies the geometry + * coordinates in place. + * @abstract + * @param {number} sx The scaling factor in the x-direction. + * @param {number=} opt_sy The scaling factor in the y-direction (defaults to + * sx). + * @param {module:ol/coordinate~Coordinate=} opt_anchor The scale origin (defaults to the center + * of the geometry extent). + * @api + */ scale(sx, opt_sy, opt_anchor) {} /** - * Create a simplified version of this geometry. For linestrings, this uses - * the the {@link - * https://en.wikipedia.org/wiki/Ramer-Douglas-Peucker_algorithm - * Douglas Peucker} algorithm. For polygons, a quantization-based - * simplification is used to preserve topology. - * @function - * @param {number} tolerance The tolerance distance for simplification. - * @return {module:ol/geom/Geometry} A new, simplified version of the original - * geometry. - * @api - */ + * Create a simplified version of this geometry. For linestrings, this uses + * the the {@link + * https://en.wikipedia.org/wiki/Ramer-Douglas-Peucker_algorithm + * Douglas Peucker} algorithm. For polygons, a quantization-based + * simplification is used to preserve topology. + * @function + * @param {number} tolerance The tolerance distance for simplification. + * @return {module:ol/geom/Geometry} A new, simplified version of the original + * geometry. + * @api + */ simplify(tolerance) { return this.getSimplifiedGeometry(tolerance * tolerance); } /** - * Create a simplified version of this geometry using the Douglas Peucker - * algorithm. - * @see https://en.wikipedia.org/wiki/Ramer-Douglas-Peucker_algorithm - * @abstract - * @param {number} squaredTolerance Squared tolerance. - * @return {module:ol/geom/Geometry} Simplified geometry. - */ + * Create a simplified version of this geometry using the Douglas Peucker + * algorithm. + * @see https://en.wikipedia.org/wiki/Ramer-Douglas-Peucker_algorithm + * @abstract + * @param {number} squaredTolerance Squared tolerance. + * @return {module:ol/geom/Geometry} Simplified geometry. + */ getSimplifiedGeometry(squaredTolerance) {} /** - * Get the type of this geometry. - * @abstract - * @return {module:ol/geom/GeometryType} Geometry type. - */ + * Get the type of this geometry. + * @abstract + * @return {module:ol/geom/GeometryType} Geometry type. + */ getType() {} /** - * Apply a transform function to each coordinate of the geometry. - * The geometry is modified in place. - * If you do not want the geometry modified in place, first `clone()` it and - * then use this function on the clone. - * @abstract - * @param {module:ol/proj~TransformFunction} transformFn Transform. - */ + * Apply a transform function to each coordinate of the geometry. + * The geometry is modified in place. + * If you do not want the geometry modified in place, first `clone()` it and + * then use this function on the clone. + * @abstract + * @param {module:ol/proj~TransformFunction} transformFn Transform. + */ applyTransform(transformFn) {} /** - * Test if the geometry and the passed extent intersect. - * @abstract - * @param {module:ol/extent~Extent} extent Extent. - * @return {boolean} `true` if the geometry and the extent intersect. - */ + * Test if the geometry and the passed extent intersect. + * @abstract + * @param {module:ol/extent~Extent} extent Extent. + * @return {boolean} `true` if the geometry and the extent intersect. + */ intersectsExtent(extent) {} /** - * Translate the geometry. This modifies the geometry coordinates in place. If - * instead you want a new geometry, first `clone()` this geometry. - * @abstract - * @param {number} deltaX Delta X. - * @param {number} deltaY Delta Y. - */ + * Translate the geometry. This modifies the geometry coordinates in place. If + * instead you want a new geometry, first `clone()` this geometry. + * @abstract + * @param {number} deltaX Delta X. + * @param {number} deltaY Delta Y. + */ translate(deltaX, deltaY) {} /** - * Transform each coordinate of the geometry from one coordinate reference - * system to another. The geometry is modified in place. - * For example, a line will be transformed to a line and a circle to a circle. - * If you do not want the geometry modified in place, first `clone()` it and - * then use this function on the clone. - * - * @param {module:ol/proj~ProjectionLike} source The current projection. Can be a - * string identifier or a {@link module:ol/proj/Projection~Projection} object. - * @param {module:ol/proj~ProjectionLike} destination The desired projection. Can be a - * string identifier or a {@link module:ol/proj/Projection~Projection} object. - * @return {module:ol/geom/Geometry} This geometry. Note that original geometry is - * modified in place. - * @api - */ + * Transform each coordinate of the geometry from one coordinate reference + * system to another. The geometry is modified in place. + * For example, a line will be transformed to a line and a circle to a circle. + * If you do not want the geometry modified in place, first `clone()` it and + * then use this function on the clone. + * + * @param {module:ol/proj~ProjectionLike} source The current projection. Can be a + * string identifier or a {@link module:ol/proj/Projection~Projection} object. + * @param {module:ol/proj~ProjectionLike} destination The desired projection. Can be a + * string identifier or a {@link module:ol/proj/Projection~Projection} object. + * @return {module:ol/geom/Geometry} This geometry. Note that original geometry is + * modified in place. + * @api + */ transform(source, destination) { source = getProjection(source); const transformFn = source.getUnits() == Units.TILE_PIXELS ? @@ -251,8 +248,6 @@ class Geometry { } } -inherits(Geometry, BaseObject); - /** * @param {number} x X. diff --git a/src/ol/geom/GeometryCollection.js b/src/ol/geom/GeometryCollection.js index d7a85c4a6e..b06c0bc3ec 100644 --- a/src/ol/geom/GeometryCollection.js +++ b/src/ol/geom/GeometryCollection.js @@ -1,7 +1,6 @@ /** * @module ol/geom/GeometryCollection */ -import {inherits} from '../util.js'; import {listen, unlisten} from '../events.js'; import EventType from '../events/EventType.js'; import {createOrUpdateEmpty, closestSquaredDistanceXY, extend, getCenter} from '../extent.js'; @@ -13,15 +12,16 @@ import {clear} from '../obj.js'; * @classdesc * An array of {@link module:ol/geom/Geometry} objects. * - * @constructor - * @extends {module:ol/geom/Geometry} - * @param {Array.=} opt_geometries Geometries. * @api */ -class GeometryCollection { +class GeometryCollection extends Geometry { + + /** + * @param {Array.=} opt_geometries Geometries. + */ constructor(opt_geometries) { - Geometry.call(this); + super(); /** * @private @@ -281,8 +281,6 @@ class GeometryCollection { } } -inherits(GeometryCollection, Geometry); - /** * @param {Array.} geometries Geometries. diff --git a/src/ol/geom/LineString.js b/src/ol/geom/LineString.js index 69faab0d62..9a61e850dc 100644 --- a/src/ol/geom/LineString.js +++ b/src/ol/geom/LineString.js @@ -1,7 +1,6 @@ /** * @module ol/geom/LineString */ -import {inherits} from '../util.js'; import {extend} from '../array.js'; import {closestSquaredDistanceXY} from '../extent.js'; import GeometryLayout from '../geom/GeometryLayout.js'; @@ -20,18 +19,18 @@ import {douglasPeucker} from '../geom/flat/simplify.js'; * @classdesc * Linestring geometry. * - * @constructor - * @extends {module:ol/geom/SimpleGeometry} - * @param {Array.|Array.} coordinates - * Coordinates. (For internal use, flat coordinates in combination with - * `opt_layout` are also accepted). - * @param {module:ol/geom/GeometryLayout=} opt_layout Layout. * @api */ -class LineString { +class LineString extends SimpleGeometry { + + /** + * @param {Array.|Array.} coordinates Coordinates. + * For internal use, flat coordinates in combination with `opt_layout` are also accepted. + * @param {module:ol/geom/GeometryLayout=} opt_layout Layout. + */ constructor(coordinates, opt_layout) { - SimpleGeometry.call(this); + super(); /** * @private @@ -240,7 +239,5 @@ class LineString { } } -inherits(LineString, SimpleGeometry); - export default LineString; diff --git a/src/ol/geom/LinearRing.js b/src/ol/geom/LinearRing.js index 9cb2c5149f..6f21242d1f 100644 --- a/src/ol/geom/LinearRing.js +++ b/src/ol/geom/LinearRing.js @@ -1,7 +1,6 @@ /** * @module ol/geom/LinearRing */ -import {inherits} from '../util.js'; import {closestSquaredDistanceXY} from '../extent.js'; import GeometryLayout from '../geom/GeometryLayout.js'; import GeometryType from '../geom/GeometryType.js'; @@ -17,18 +16,18 @@ import {douglasPeucker} from '../geom/flat/simplify.js'; * Linear ring geometry. Only used as part of polygon; cannot be rendered * on its own. * - * @constructor - * @extends {module:ol/geom/SimpleGeometry} - * @param {Array.|Array.} coordinates - * Coordinates. (For internal use, flat coordinates in combination with - * `opt_layout` are also accepted.) - * @param {module:ol/geom/GeometryLayout=} opt_layout Layout. * @api */ -class LinearRing { +class LinearRing extends SimpleGeometry { + + /** + * @param {Array.|Array.} coordinates Coordinates. + * For internal use, flat coordinates in combination with `opt_layout` are also accepted. + * @param {module:ol/geom/GeometryLayout=} opt_layout Layout. + */ constructor(coordinates, opt_layout) { - SimpleGeometry.call(this); + super(); /** * @private @@ -139,7 +138,5 @@ class LinearRing { } } -inherits(LinearRing, SimpleGeometry); - export default LinearRing; diff --git a/src/ol/geom/MultiLineString.js b/src/ol/geom/MultiLineString.js index ab9e92b952..669ea2b3f0 100644 --- a/src/ol/geom/MultiLineString.js +++ b/src/ol/geom/MultiLineString.js @@ -1,7 +1,6 @@ /** * @module ol/geom/MultiLineString */ -import {inherits} from '../util.js'; import {extend} from '../array.js'; import {closestSquaredDistanceXY} from '../extent.js'; import GeometryLayout from '../geom/GeometryLayout.js'; @@ -19,19 +18,20 @@ import {douglasPeuckerArray} from '../geom/flat/simplify.js'; * @classdesc * Multi-linestring geometry. * - * @constructor - * @extends {module:ol/geom/SimpleGeometry} - * @param {Array.|module:ol/geom~MultiLineString>|Array.} coordinates - * Coordinates or LineString geometries. (For internal use, flat coordinates in - * combination with `opt_layout` and `opt_ends` are also accepted.) - * @param {module:ol/geom/GeometryLayout=} opt_layout Layout. - * @param {Array.} opt_ends Flat coordinate ends for internal use. * @api */ -class MultiLineString { +class MultiLineString extends SimpleGeometry { + + /** + * @param {Array.|module:ol/geom~MultiLineString>|Array.} coordinates + * Coordinates or LineString geometries. (For internal use, flat coordinates in + * combination with `opt_layout` and `opt_ends` are also accepted.) + * @param {module:ol/geom/GeometryLayout=} opt_layout Layout. + * @param {Array.} opt_ends Flat coordinate ends for internal use. + */ constructor(coordinates, opt_layout, opt_ends) { - SimpleGeometry.call(this); + super(); /** * @type {Array.} @@ -270,7 +270,5 @@ class MultiLineString { } } -inherits(MultiLineString, SimpleGeometry); - export default MultiLineString; diff --git a/src/ol/geom/MultiPoint.js b/src/ol/geom/MultiPoint.js index b366817415..834ea037ad 100644 --- a/src/ol/geom/MultiPoint.js +++ b/src/ol/geom/MultiPoint.js @@ -1,7 +1,6 @@ /** * @module ol/geom/MultiPoint */ -import {inherits} from '../util.js'; import {extend} from '../array.js'; import {closestSquaredDistanceXY, containsXY} from '../extent.js'; import GeometryType from '../geom/GeometryType.js'; @@ -15,17 +14,17 @@ import {squaredDistance as squaredDx} from '../math.js'; * @classdesc * Multi-point geometry. * - * @constructor - * @extends {module:ol/geom/SimpleGeometry} - * @param {Array.|Array.} coordinates - * Coordinates. (For internal use, flat coordinates in combination with - * `opt_layout` are also accepted) - * @param {module:ol/geom/GeometryLayout=} opt_layout Layout. * @api */ -class MultiPoint { +class MultiPoint extends SimpleGeometry { + + /** + * @param {Array.|Array.} coordinates Coordinates. + * For internal use, flat coordinates in combination with `opt_layout` are also accepted. + * @param {module:ol/geom/GeometryLayout=} opt_layout Layout. + */ constructor(coordinates, opt_layout) { - SimpleGeometry.call(this); + super(); if (opt_layout && !Array.isArray(coordinates[0])) { this.setFlatCoordinates(opt_layout, coordinates); } else { @@ -168,7 +167,5 @@ class MultiPoint { } } -inherits(MultiPoint, SimpleGeometry); - export default MultiPoint; diff --git a/src/ol/geom/MultiPolygon.js b/src/ol/geom/MultiPolygon.js index a5bb32c476..0235a6e175 100644 --- a/src/ol/geom/MultiPolygon.js +++ b/src/ol/geom/MultiPolygon.js @@ -1,7 +1,6 @@ /** * @module ol/geom/MultiPolygon */ -import {inherits} from '../util.js'; import {extend} from '../array.js'; import {closestSquaredDistanceXY} from '../extent.js'; import GeometryLayout from '../geom/GeometryLayout.js'; @@ -24,20 +23,19 @@ import {quantizeMultiArray} from '../geom/flat/simplify.js'; * @classdesc * Multi-polygon geometry. * - * @constructor - * @extends {module:ol/geom/SimpleGeometry} - * @param {Array.>>|Array.} coordinates - * Coordinates. (For internal use, flat coordinats in combination with - * `opt_layout` and `opt_endss` are also accepted). - * @param {module:ol/geom/GeometryLayout=} opt_layout Layout. - * @param {Array.} opt_endss Array of ends for internal use with flat - * coordinates. * @api */ -class MultiPolygon { +class MultiPolygon extends SimpleGeometry { + + /** + * @param {Array.>>|Array.} coordinates Coordinates. + * For internal use, flat coordinats in combination with `opt_layout` and `opt_endss` are also accepted. + * @param {module:ol/geom/GeometryLayout=} opt_layout Layout. + * @param {Array.} opt_endss Array of ends for internal use with flat coordinates. + */ constructor(coordinates, opt_layout, opt_endss) { - SimpleGeometry.call(this); + super(); /** * @type {Array.>} @@ -373,7 +371,5 @@ class MultiPolygon { } } -inherits(MultiPolygon, SimpleGeometry); - export default MultiPolygon; diff --git a/src/ol/geom/Point.js b/src/ol/geom/Point.js index e93b7a5514..af6277bca0 100644 --- a/src/ol/geom/Point.js +++ b/src/ol/geom/Point.js @@ -1,7 +1,6 @@ /** * @module ol/geom/Point */ -import {inherits} from '../util.js'; import {createOrUpdateFromCoordinate, containsXY} from '../extent.js'; import GeometryType from '../geom/GeometryType.js'; import SimpleGeometry from '../geom/SimpleGeometry.js'; @@ -12,15 +11,16 @@ import {squaredDistance as squaredDx} from '../math.js'; * @classdesc * Point geometry. * - * @constructor - * @extends {module:ol/geom/SimpleGeometry} - * @param {module:ol/coordinate~Coordinate} coordinates Coordinates. - * @param {module:ol/geom/GeometryLayout=} opt_layout Layout. * @api */ -class Point { +class Point extends SimpleGeometry { + + /** + * @param {module:ol/coordinate~Coordinate} coordinates Coordinates. + * @param {module:ol/geom/GeometryLayout=} opt_layout Layout. + */ constructor(coordinates, opt_layout) { - SimpleGeometry.call(this); + super(); this.setCoordinates(coordinates, opt_layout); } @@ -101,7 +101,5 @@ class Point { } } -inherits(Point, SimpleGeometry); - export default Point; diff --git a/src/ol/geom/Polygon.js b/src/ol/geom/Polygon.js index 87fa4215c0..14a6634eef 100644 --- a/src/ol/geom/Polygon.js +++ b/src/ol/geom/Polygon.js @@ -1,7 +1,6 @@ /** * @module ol/geom/Polygon */ -import {inherits} from '../util.js'; import {extend} from '../array.js'; import {closestSquaredDistanceXY, getCenter} from '../extent.js'; import GeometryLayout from '../geom/GeometryLayout.js'; @@ -25,24 +24,24 @@ import {modulo} from '../math.js'; * @classdesc * Polygon geometry. * - * @constructor - * @extends {module:ol/geom/SimpleGeometry} - * @param {!Array.>|!Array.} coordinates - * Array of linear rings that define the polygon. The first linear ring of the - * array defines the outer-boundary or surface of the polygon. Each subsequent - * linear ring defines a hole in the surface of the polygon. A linear ring is - * an array of vertices' coordinates where the first coordinate and the last are - * equivalent. (For internal use, flat coordinates in combination with - * `opt_layout` and `opt_ends` are also accepted.) - * @param {module:ol/geom/GeometryLayout=} opt_layout Layout. - * @param {Array.=} opt_ends Ends (for internal use with flat - * coordinates). * @api */ -class Polygon { +class Polygon extends SimpleGeometry { + + /** + * @param {!Array.>|!Array.} coordinates + * Array of linear rings that define the polygon. The first linear ring of the + * array defines the outer-boundary or surface of the polygon. Each subsequent + * linear ring defines a hole in the surface of the polygon. A linear ring is + * an array of vertices' coordinates where the first coordinate and the last are + * equivalent. (For internal use, flat coordinates in combination with + * `opt_layout` and `opt_ends` are also accepted.) + * @param {module:ol/geom/GeometryLayout=} opt_layout Layout. + * @param {Array.=} opt_ends Ends (for internal use with flat coordinates). + */ constructor(coordinates, opt_layout, opt_ends) { - SimpleGeometry.call(this); + super(); /** * @type {Array.} @@ -330,8 +329,6 @@ class Polygon { } } -inherits(Polygon, SimpleGeometry); - export default Polygon; diff --git a/src/ol/geom/SimpleGeometry.js b/src/ol/geom/SimpleGeometry.js index e456a5e2ef..0ac4a68662 100644 --- a/src/ol/geom/SimpleGeometry.js +++ b/src/ol/geom/SimpleGeometry.js @@ -1,7 +1,6 @@ /** * @module ol/geom/SimpleGeometry */ -import {inherits} from '../util.js'; import {FALSE} from '../functions.js'; import {createOrUpdateFromFlatCoordinates, getCenter} from '../extent.js'; import Geometry from '../geom/Geometry.js'; @@ -14,15 +13,13 @@ import {clear} from '../obj.js'; * Abstract base class; only used for creating subclasses; do not instantiate * in apps, as cannot be rendered. * - * @constructor * @abstract - * @extends {module:ol/geom/Geometry} * @api */ -class SimpleGeometry { +class SimpleGeometry extends Geometry { constructor() { - Geometry.call(this); + super(); /** * @protected @@ -257,8 +254,6 @@ class SimpleGeometry { } } -inherits(SimpleGeometry, Geometry); - /** * @param {number} stride Stride. From 672a462b1e18d20ec96de0a422cf5088c75e72f5 Mon Sep 17 00:00:00 2001 From: ahocevar Date: Tue, 17 Jul 2018 16:13:36 +0200 Subject: [PATCH 046/107] Use extends and super for format/GeoJSON --- src/ol/format/GeoJSON.js | 99 ++-------------------------------------- 1 file changed, 3 insertions(+), 96 deletions(-) diff --git a/src/ol/format/GeoJSON.js b/src/ol/format/GeoJSON.js index 67a3de5d38..85ca67281a 100644 --- a/src/ol/format/GeoJSON.js +++ b/src/ol/format/GeoJSON.js @@ -4,7 +4,6 @@ // TODO: serialize dataProjection as crs member when writing // see https://github.com/openlayers/openlayers/issues/2078 -import {inherits} from '../util.js'; import {assert} from '../asserts.js'; import Feature from '../Feature.js'; import {transformWithOptions} from '../format/Feature.js'; @@ -37,10 +36,9 @@ import {get as getProjection} from '../proj.js'; * @classdesc * Feature format for reading and writing data in the GeoJSON format. * - * @extends {module:ol/format/JSONFeature} - * @api + * @api */ -class GeoJSON { +class GeoJSON extends JSONFeature { /** * @param {module:ol/format/GeoJSON~Options=} opt_options Options. @@ -49,7 +47,7 @@ class GeoJSON { const options = opt_options ? opt_options : {}; - JSONFeature.call(this); + super(); /** * @inheritDoc @@ -230,8 +228,6 @@ class GeoJSON { } } -inherits(GeoJSON, JSONFeature); - /** * @const @@ -480,93 +476,4 @@ function writePolygonGeometry(geometry, opt_options) { } -/** - * Read a feature from a GeoJSON Feature source. Only works for Feature or - * geometry types. Use {@link module:ol/format/GeoJSON#readFeatures} to read - * FeatureCollection source. If feature at source has an id, it will be used - * as Feature id by calling {@link module:ol/Feature#setId} internally. - * - * @function - * @param {Document|Node|Object|string} source Source. - * @param {module:ol/format/Feature~ReadOptions=} opt_options Read options. - * @return {module:ol/Feature} Feature. - * @api - */ -GeoJSON.prototype.readFeature; - - -/** - * Read all features from a GeoJSON source. Works for all GeoJSON types. - * If the source includes only geometries, features will be created with those - * geometries. - * - * @function - * @param {Document|Node|Object|string} source Source. - * @param {module:ol/format/Feature~ReadOptions=} opt_options Read options. - * @return {Array.} Features. - * @api - */ -GeoJSON.prototype.readFeatures; - - -/** - * Read a geometry from a GeoJSON source. - * - * @function - * @param {Document|Node|Object|string} source Source. - * @param {module:ol/format/Feature~ReadOptions=} opt_options Read options. - * @return {module:ol/geom/Geometry} Geometry. - * @api - */ -GeoJSON.prototype.readGeometry; - - -/** - * Read the projection from a GeoJSON source. - * - * @function - * @param {Document|Node|Object|string} source Source. - * @return {module:ol/proj/Projection} Projection. - * @api - */ -GeoJSON.prototype.readProjection; - - -/** - * Encode a feature as a GeoJSON Feature string. - * - * @function - * @param {module:ol/Feature} feature Feature. - * @param {module:ol/format/Feature~WriteOptions=} opt_options Write options. - * @return {string} GeoJSON. - * @override - * @api - */ -GeoJSON.prototype.writeFeature; - - -/** - * Encode an array of features as GeoJSON. - * - * @function - * @param {Array.} features Features. - * @param {module:ol/format/Feature~WriteOptions=} opt_options Write options. - * @return {string} GeoJSON. - * @api - */ -GeoJSON.prototype.writeFeatures; - - -/** - * Encode a geometry as a GeoJSON string. - * - * @function - * @param {module:ol/geom/Geometry} geometry Geometry. - * @param {module:ol/format/Feature~WriteOptions=} opt_options Write options. - * @return {string} GeoJSON. - * @api - */ -GeoJSON.prototype.writeGeometry; - - export default GeoJSON; From 84e9d7c72efd7acbca630b74bc02387008ce64c2 Mon Sep 17 00:00:00 2001 From: ahocevar Date: Tue, 17 Jul 2018 16:27:18 +0200 Subject: [PATCH 047/107] Use extends and super for format/GML2 --- src/ol/format/GML2.js | 321 ++++++++++++++++++++---------------------- 1 file changed, 153 insertions(+), 168 deletions(-) diff --git a/src/ol/format/GML2.js b/src/ol/format/GML2.js index 31d878c000..a35088f0fe 100644 --- a/src/ol/format/GML2.js +++ b/src/ol/format/GML2.js @@ -1,7 +1,6 @@ /** * @module ol/format/GML2 */ -import {inherits} from '../util.js'; import {createOrUpdate} from '../extent.js'; import {transformWithOptions} from '../format/Feature.js'; import GMLBase, {GMLNS} from '../format/GMLBase.js'; @@ -37,10 +36,9 @@ const MULTIGEOMETRY_TO_MEMBER_NODENAME = { * Feature format for reading and writing data in the GML format, * version 2.1.2. * - * @extends {module:ol/format/GMLBase} * @api */ -class GML2 { +class GML2 extends GMLBase { /** * @param {module:ol/format/GMLBase~Options=} opt_options Optional configuration object. @@ -49,11 +47,11 @@ class GML2 { const options = /** @type {module:ol/format/GMLBase~Options} */ (opt_options ? opt_options : {}); - GMLBase.call(this, options); + super(options); this.FEATURE_COLLECTION_PARSERS[GMLNS][ 'featureMember'] = - makeArrayPusher(GMLBase.prototype.readFeaturesInternal); + makeArrayPusher(this.readFeaturesInternal); /** * @inheritDoc @@ -61,6 +59,156 @@ class GML2 { this.schemaLocation = options.schemaLocation ? options.schemaLocation : schemaLocation; + /** + * @const + * @type {Object.>} + * @private + */ + this.GEOMETRY_FLAT_COORDINATES_PARSERS_ = { + 'http://www.opengis.net/gml': { + 'coordinates': makeReplacer(this.readFlatCoordinates_) + } + }; + + /** + * @const + * @type {Object.>} + * @private + */ + this.FLAT_LINEAR_RINGS_PARSERS_ = { + 'http://www.opengis.net/gml': { + 'innerBoundaryIs': this.innerBoundaryIsParser_, + 'outerBoundaryIs': this.outerBoundaryIsParser_ + } + }; + + /** + * @const + * @type {Object.>} + * @private + */ + this.BOX_PARSERS_ = { + 'http://www.opengis.net/gml': { + 'coordinates': makeArrayPusher( + this.readFlatCoordinates_) + } + }; + + /** + * @const + * @type {Object.>} + * @private + */ + this.GEOMETRY_PARSERS_ = { + 'http://www.opengis.net/gml': { + 'Point': makeReplacer(this.readPoint), + 'MultiPoint': makeReplacer( + this.readMultiPoint), + 'LineString': makeReplacer( + this.readLineString), + 'MultiLineString': makeReplacer( + this.readMultiLineString), + 'LinearRing': makeReplacer( + this.readLinearRing), + 'Polygon': makeReplacer(this.readPolygon), + 'MultiPolygon': makeReplacer( + this.readMultiPolygon), + 'Box': makeReplacer(this.readBox_) + } + }; + + /** + * @const + * @type {Object.>} + * @private + */ + this.GEOMETRY_SERIALIZERS_ = { + 'http://www.opengis.net/gml': { + 'Curve': makeChildAppender( + this.writeCurveOrLineString_), + 'MultiCurve': makeChildAppender( + this.writeMultiCurveOrLineString_), + 'Point': makeChildAppender(this.writePoint_), + 'MultiPoint': makeChildAppender( + this.writeMultiPoint_), + 'LineString': makeChildAppender( + this.writeCurveOrLineString_), + 'MultiLineString': makeChildAppender( + this.writeMultiCurveOrLineString_), + 'LinearRing': makeChildAppender( + this.writeLinearRing_), + 'Polygon': makeChildAppender( + this.writeSurfaceOrPolygon_), + 'MultiPolygon': makeChildAppender( + this.writeMultiSurfaceOrPolygon_), + 'Surface': makeChildAppender( + this.writeSurfaceOrPolygon_), + 'MultiSurface': makeChildAppender( + this.writeMultiSurfaceOrPolygon_), + 'Envelope': makeChildAppender( + this.writeEnvelope) + } + }; + + /** + * @type {Object.>} + * @private + */ + this.LINESTRINGORCURVEMEMBER_SERIALIZERS_ = { + 'http://www.opengis.net/gml': { + 'lineStringMember': makeChildAppender( + this.writeLineStringOrCurveMember_), + 'curveMember': makeChildAppender( + this.writeLineStringOrCurveMember_) + } + }; + + /** + * @type {Object.>} + * @private + */ + this.RING_SERIALIZERS_ = { + 'http://www.opengis.net/gml': { + 'outerBoundaryIs': makeChildAppender(this.writeRing_), + 'innerBoundaryIs': makeChildAppender(this.writeRing_) + } + }; + + /** + * @type {Object.>} + * @private + */ + this.POINTMEMBER_SERIALIZERS_ = { + 'http://www.opengis.net/gml': { + 'pointMember': makeChildAppender( + this.writePointMember_) + } + }; + + /** + * @const + * @type {Object.>} + * @private + */ + this.SURFACEORPOLYGONMEMBER_SERIALIZERS_ = { + 'http://www.opengis.net/gml': { + 'surfaceMember': makeChildAppender( + this.writeSurfaceOrPolygonMember_), + 'polygonMember': makeChildAppender( + this.writeSurfaceOrPolygonMember_) + } + }; + + /** + * @type {Object.>} + * @private + */ + this.ENVELOPE_SERIALIZERS_ = { + 'http://www.opengis.net/gml': { + 'lowerCorner': makeChildAppender(writeStringTextNode), + 'upperCorner': makeChildAppender(writeStringTextNode) + } + }; } /** @@ -587,167 +735,4 @@ class GML2 { } } -inherits(GML2, GMLBase); - - -/** - * @const - * @type {Object.>} - * @private - */ -GML2.prototype.GEOMETRY_FLAT_COORDINATES_PARSERS_ = { - 'http://www.opengis.net/gml': { - 'coordinates': makeReplacer(GML2.prototype.readFlatCoordinates_) - } -}; - - -/** - * @const - * @type {Object.>} - * @private - */ -GML2.prototype.FLAT_LINEAR_RINGS_PARSERS_ = { - 'http://www.opengis.net/gml': { - 'innerBoundaryIs': GML2.prototype.innerBoundaryIsParser_, - 'outerBoundaryIs': GML2.prototype.outerBoundaryIsParser_ - } -}; - - -/** - * @const - * @type {Object.>} - * @private - */ -GML2.prototype.BOX_PARSERS_ = { - 'http://www.opengis.net/gml': { - 'coordinates': makeArrayPusher( - GML2.prototype.readFlatCoordinates_) - } -}; - - -/** - * @const - * @type {Object.>} - * @private - */ -GML2.prototype.GEOMETRY_PARSERS_ = { - 'http://www.opengis.net/gml': { - '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), - 'Box': makeReplacer(GML2.prototype.readBox_) - } -}; - - -/** - * @const - * @type {Object.>} - * @private - */ -GML2.prototype.GEOMETRY_SERIALIZERS_ = { - 'http://www.opengis.net/gml': { - 'Curve': makeChildAppender( - GML2.prototype.writeCurveOrLineString_), - 'MultiCurve': makeChildAppender( - GML2.prototype.writeMultiCurveOrLineString_), - 'Point': makeChildAppender(GML2.prototype.writePoint_), - 'MultiPoint': makeChildAppender( - GML2.prototype.writeMultiPoint_), - 'LineString': makeChildAppender( - GML2.prototype.writeCurveOrLineString_), - 'MultiLineString': makeChildAppender( - GML2.prototype.writeMultiCurveOrLineString_), - 'LinearRing': makeChildAppender( - GML2.prototype.writeLinearRing_), - 'Polygon': makeChildAppender( - GML2.prototype.writeSurfaceOrPolygon_), - 'MultiPolygon': makeChildAppender( - GML2.prototype.writeMultiSurfaceOrPolygon_), - 'Surface': makeChildAppender( - GML2.prototype.writeSurfaceOrPolygon_), - 'MultiSurface': makeChildAppender( - GML2.prototype.writeMultiSurfaceOrPolygon_), - 'Envelope': makeChildAppender( - GML2.prototype.writeEnvelope) - } -}; - - -/** - * @type {Object.>} - * @private - */ -GML2.prototype.LINESTRINGORCURVEMEMBER_SERIALIZERS_ = { - 'http://www.opengis.net/gml': { - 'lineStringMember': makeChildAppender( - GML2.prototype.writeLineStringOrCurveMember_), - 'curveMember': makeChildAppender( - GML2.prototype.writeLineStringOrCurveMember_) - } -}; - - -/** - * @type {Object.>} - * @private - */ -GML2.prototype.RING_SERIALIZERS_ = { - 'http://www.opengis.net/gml': { - 'outerBoundaryIs': makeChildAppender(GML2.prototype.writeRing_), - 'innerBoundaryIs': makeChildAppender(GML2.prototype.writeRing_) - } -}; - - -/** - * @type {Object.>} - * @private - */ -GML2.prototype.POINTMEMBER_SERIALIZERS_ = { - 'http://www.opengis.net/gml': { - 'pointMember': makeChildAppender( - GML2.prototype.writePointMember_) - } -}; - - -/** - * @const - * @type {Object.>} - * @private - */ -GML2.prototype.SURFACEORPOLYGONMEMBER_SERIALIZERS_ = { - 'http://www.opengis.net/gml': { - 'surfaceMember': makeChildAppender( - GML2.prototype.writeSurfaceOrPolygonMember_), - 'polygonMember': makeChildAppender( - GML2.prototype.writeSurfaceOrPolygonMember_) - } -}; - - -/** - * @type {Object.>} - * @private - */ -GML2.prototype.ENVELOPE_SERIALIZERS_ = { - 'http://www.opengis.net/gml': { - 'lowerCorner': makeChildAppender(writeStringTextNode), - 'upperCorner': makeChildAppender(writeStringTextNode) - } -}; - export default GML2; From 7f05bf1d7e3b85c36d52f25f9c52ee890d84b56d Mon Sep 17 00:00:00 2001 From: ahocevar Date: Tue, 17 Jul 2018 16:29:55 +0200 Subject: [PATCH 048/107] Use extends and super for format/GML3 --- src/ol/format/GML3.js | 582 +++++++++++++++++++++--------------------- 1 file changed, 288 insertions(+), 294 deletions(-) diff --git a/src/ol/format/GML3.js b/src/ol/format/GML3.js index cd7090a5d0..a1f7600233 100644 --- a/src/ol/format/GML3.js +++ b/src/ol/format/GML3.js @@ -1,7 +1,6 @@ /** * @module ol/format/GML3 */ -import {inherits} from '../util.js'; import {extend} from '../array.js'; import {createOrUpdate} from '../extent.js'; import {transformWithOptions} from '../format/Feature.js'; @@ -48,10 +47,9 @@ const MULTIGEOMETRY_TO_MEMBER_NODENAME = { * version 3.1.1. * Currently only supports GML 3.1.1 Simple Features profile. * - * @extends {module:ol/format/GMLBase} * @api */ -class GML3 { +class GML3 extends GMLBase { /** * @param {module:ol/format/GMLBase~Options=} opt_options Optional configuration object. @@ -60,7 +58,7 @@ class GML3 { const options = /** @type {module:ol/format/GMLBase~Options} */ (opt_options ? opt_options : {}); - GMLBase.call(this, options); + super(options); /** * @private @@ -101,6 +99,292 @@ class GML3 { this.hasZ = options.hasZ !== undefined ? options.hasZ : false; + /** + * @const + * @type {Object.>} + * @private + */ + this.GEOMETRY_FLAT_COORDINATES_PARSERS_ = { + 'http://www.opengis.net/gml': { + 'pos': makeReplacer(this.readFlatPos_), + 'posList': makeReplacer(this.readFlatPosList_) + } + }; + + + /** + * @const + * @type {Object.>} + * @private + */ + this.FLAT_LINEAR_RINGS_PARSERS_ = { + 'http://www.opengis.net/gml': { + 'interior': this.interiorParser_, + 'exterior': this.exteriorParser_ + } + }; + + + /** + * @const + * @type {Object.>} + * @private + */ + this.GEOMETRY_PARSERS_ = { + 'http://www.opengis.net/gml': { + 'Point': makeReplacer(this.readPoint), + 'MultiPoint': makeReplacer( + this.readMultiPoint), + 'LineString': makeReplacer( + this.readLineString), + 'MultiLineString': makeReplacer( + this.readMultiLineString), + 'LinearRing': makeReplacer( + this.readLinearRing), + 'Polygon': makeReplacer(this.readPolygon), + 'MultiPolygon': makeReplacer( + this.readMultiPolygon), + 'Surface': makeReplacer(this.readSurface_), + 'MultiSurface': makeReplacer( + this.readMultiSurface_), + 'Curve': makeReplacer(this.readCurve_), + 'MultiCurve': makeReplacer( + this.readMultiCurve_), + 'Envelope': makeReplacer(this.readEnvelope_) + } + }; + + + /** + * @const + * @type {Object.>} + * @private + */ + this.MULTICURVE_PARSERS_ = { + 'http://www.opengis.net/gml': { + 'curveMember': makeArrayPusher( + this.curveMemberParser_), + 'curveMembers': makeArrayPusher( + this.curveMemberParser_) + } + }; + + + /** + * @const + * @type {Object.>} + * @private + */ + this.MULTISURFACE_PARSERS_ = { + 'http://www.opengis.net/gml': { + 'surfaceMember': makeArrayPusher( + this.surfaceMemberParser_), + 'surfaceMembers': makeArrayPusher( + this.surfaceMemberParser_) + } + }; + + + /** + * @const + * @type {Object.>} + * @private + */ + this.CURVEMEMBER_PARSERS_ = { + 'http://www.opengis.net/gml': { + 'LineString': makeArrayPusher( + this.readLineString), + 'Curve': makeArrayPusher(this.readCurve_) + } + }; + + + /** + * @const + * @type {Object.>} + * @private + */ + this.SURFACEMEMBER_PARSERS_ = { + 'http://www.opengis.net/gml': { + 'Polygon': makeArrayPusher(this.readPolygon), + 'Surface': makeArrayPusher(this.readSurface_) + } + }; + + + /** + * @const + * @type {Object.>} + * @private + */ + this.SURFACE_PARSERS_ = { + 'http://www.opengis.net/gml': { + 'patches': makeReplacer(this.readPatch_) + } + }; + + + /** + * @const + * @type {Object.>} + * @private + */ + this.CURVE_PARSERS_ = { + 'http://www.opengis.net/gml': { + 'segments': makeReplacer(this.readSegment_) + } + }; + + + /** + * @const + * @type {Object.>} + * @private + */ + this.ENVELOPE_PARSERS_ = { + 'http://www.opengis.net/gml': { + 'lowerCorner': makeArrayPusher( + this.readFlatPosList_), + 'upperCorner': makeArrayPusher( + this.readFlatPosList_) + } + }; + + + /** + * @const + * @type {Object.>} + * @private + */ + this.PATCHES_PARSERS_ = { + 'http://www.opengis.net/gml': { + 'PolygonPatch': makeReplacer( + this.readPolygonPatch_) + } + }; + + + /** + * @const + * @type {Object.>} + * @private + */ + this.SEGMENTS_PARSERS_ = { + 'http://www.opengis.net/gml': { + 'LineStringSegment': makeReplacer( + this.readLineStringSegment_) + } + }; + + + /** + * Encode an array of features in GML 3.1.1 Simple Features. + * + * @function + * @param {Array.} features Features. + * @param {module:ol/format/Feature~WriteOptions=} opt_options Options. + * @return {string} Result. + * @api + */ + this.writeFeatures; + + + /** + * @type {Object.>} + * @private + */ + this.RING_SERIALIZERS_ = { + 'http://www.opengis.net/gml': { + 'exterior': makeChildAppender(this.writeRing_), + 'interior': makeChildAppender(this.writeRing_) + } + }; + + + /** + * @type {Object.>} + * @private + */ + this.ENVELOPE_SERIALIZERS_ = { + 'http://www.opengis.net/gml': { + 'lowerCorner': makeChildAppender(writeStringTextNode), + 'upperCorner': makeChildAppender(writeStringTextNode) + } + }; + + + /** + * @type {Object.>} + * @private + */ + this.SURFACEORPOLYGONMEMBER_SERIALIZERS_ = { + 'http://www.opengis.net/gml': { + 'surfaceMember': makeChildAppender( + this.writeSurfaceOrPolygonMember_), + 'polygonMember': makeChildAppender( + this.writeSurfaceOrPolygonMember_) + } + }; + + + /** + * @type {Object.>} + * @private + */ + this.POINTMEMBER_SERIALIZERS_ = { + 'http://www.opengis.net/gml': { + 'pointMember': makeChildAppender( + this.writePointMember_) + } + }; + + + /** + * @type {Object.>} + * @private + */ + this.LINESTRINGORCURVEMEMBER_SERIALIZERS_ = { + 'http://www.opengis.net/gml': { + 'lineStringMember': makeChildAppender( + this.writeLineStringOrCurveMember_), + 'curveMember': makeChildAppender( + this.writeLineStringOrCurveMember_) + } + }; + + + /** + * @type {Object.>} + * @private + */ + this.GEOMETRY_SERIALIZERS_ = { + 'http://www.opengis.net/gml': { + 'Curve': makeChildAppender( + this.writeCurveOrLineString_), + 'MultiCurve': makeChildAppender( + this.writeMultiCurveOrLineString_), + 'Point': makeChildAppender(this.writePoint_), + 'MultiPoint': makeChildAppender( + this.writeMultiPoint_), + 'LineString': makeChildAppender( + this.writeCurveOrLineString_), + 'MultiLineString': makeChildAppender( + this.writeMultiCurveOrLineString_), + 'LinearRing': makeChildAppender( + this.writeLinearRing_), + 'Polygon': makeChildAppender( + this.writeSurfaceOrPolygon_), + 'MultiPolygon': makeChildAppender( + this.writeMultiSurfaceOrPolygon_), + 'Surface': makeChildAppender( + this.writeSurfaceOrPolygon_), + 'MultiSurface': makeChildAppender( + this.writeMultiSurfaceOrPolygon_), + 'Envelope': makeChildAppender( + this.writeEnvelope) + } + }; + } /** @@ -920,294 +1204,4 @@ class GML3 { } } -inherits(GML3, GMLBase); - - -/** - * @const - * @type {Object.>} - * @private - */ -GML3.prototype.GEOMETRY_FLAT_COORDINATES_PARSERS_ = { - 'http://www.opengis.net/gml': { - 'pos': makeReplacer(GML3.prototype.readFlatPos_), - 'posList': makeReplacer(GML3.prototype.readFlatPosList_) - } -}; - - -/** - * @const - * @type {Object.>} - * @private - */ -GML3.prototype.FLAT_LINEAR_RINGS_PARSERS_ = { - 'http://www.opengis.net/gml': { - 'interior': GML3.prototype.interiorParser_, - 'exterior': GML3.prototype.exteriorParser_ - } -}; - - -/** - * @const - * @type {Object.>} - * @private - */ -GML3.prototype.GEOMETRY_PARSERS_ = { - 'http://www.opengis.net/gml': { - '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(GML3.prototype.readSurface_), - 'MultiSurface': makeReplacer( - GML3.prototype.readMultiSurface_), - 'Curve': makeReplacer(GML3.prototype.readCurve_), - 'MultiCurve': makeReplacer( - GML3.prototype.readMultiCurve_), - 'Envelope': makeReplacer(GML3.prototype.readEnvelope_) - } -}; - - -/** - * @const - * @type {Object.>} - * @private - */ -GML3.prototype.MULTICURVE_PARSERS_ = { - 'http://www.opengis.net/gml': { - 'curveMember': makeArrayPusher( - GML3.prototype.curveMemberParser_), - 'curveMembers': makeArrayPusher( - GML3.prototype.curveMemberParser_) - } -}; - - -/** - * @const - * @type {Object.>} - * @private - */ -GML3.prototype.MULTISURFACE_PARSERS_ = { - 'http://www.opengis.net/gml': { - 'surfaceMember': makeArrayPusher( - GML3.prototype.surfaceMemberParser_), - 'surfaceMembers': makeArrayPusher( - GML3.prototype.surfaceMemberParser_) - } -}; - - -/** - * @const - * @type {Object.>} - * @private - */ -GML3.prototype.CURVEMEMBER_PARSERS_ = { - 'http://www.opengis.net/gml': { - 'LineString': makeArrayPusher( - GMLBase.prototype.readLineString), - 'Curve': makeArrayPusher(GML3.prototype.readCurve_) - } -}; - - -/** - * @const - * @type {Object.>} - * @private - */ -GML3.prototype.SURFACEMEMBER_PARSERS_ = { - 'http://www.opengis.net/gml': { - 'Polygon': makeArrayPusher(GMLBase.prototype.readPolygon), - 'Surface': makeArrayPusher(GML3.prototype.readSurface_) - } -}; - - -/** - * @const - * @type {Object.>} - * @private - */ -GML3.prototype.SURFACE_PARSERS_ = { - 'http://www.opengis.net/gml': { - 'patches': makeReplacer(GML3.prototype.readPatch_) - } -}; - - -/** - * @const - * @type {Object.>} - * @private - */ -GML3.prototype.CURVE_PARSERS_ = { - 'http://www.opengis.net/gml': { - 'segments': makeReplacer(GML3.prototype.readSegment_) - } -}; - - -/** - * @const - * @type {Object.>} - * @private - */ -GML3.prototype.ENVELOPE_PARSERS_ = { - 'http://www.opengis.net/gml': { - 'lowerCorner': makeArrayPusher( - GML3.prototype.readFlatPosList_), - 'upperCorner': makeArrayPusher( - GML3.prototype.readFlatPosList_) - } -}; - - -/** - * @const - * @type {Object.>} - * @private - */ -GML3.prototype.PATCHES_PARSERS_ = { - 'http://www.opengis.net/gml': { - 'PolygonPatch': makeReplacer( - GML3.prototype.readPolygonPatch_) - } -}; - - -/** - * @const - * @type {Object.>} - * @private - */ -GML3.prototype.SEGMENTS_PARSERS_ = { - 'http://www.opengis.net/gml': { - 'LineStringSegment': makeReplacer( - GML3.prototype.readLineStringSegment_) - } -}; - - -/** - * Encode an array of features in GML 3.1.1 Simple Features. - * - * @function - * @param {Array.} features Features. - * @param {module:ol/format/Feature~WriteOptions=} opt_options Options. - * @return {string} Result. - * @api - */ -GML3.prototype.writeFeatures; - - -/** - * @type {Object.>} - * @private - */ -GML3.prototype.RING_SERIALIZERS_ = { - 'http://www.opengis.net/gml': { - 'exterior': makeChildAppender(GML3.prototype.writeRing_), - 'interior': makeChildAppender(GML3.prototype.writeRing_) - } -}; - - -/** - * @type {Object.>} - * @private - */ -GML3.prototype.ENVELOPE_SERIALIZERS_ = { - 'http://www.opengis.net/gml': { - 'lowerCorner': makeChildAppender(writeStringTextNode), - 'upperCorner': makeChildAppender(writeStringTextNode) - } -}; - - -/** - * @type {Object.>} - * @private - */ -GML3.prototype.SURFACEORPOLYGONMEMBER_SERIALIZERS_ = { - 'http://www.opengis.net/gml': { - 'surfaceMember': makeChildAppender( - GML3.prototype.writeSurfaceOrPolygonMember_), - 'polygonMember': makeChildAppender( - GML3.prototype.writeSurfaceOrPolygonMember_) - } -}; - - -/** - * @type {Object.>} - * @private - */ -GML3.prototype.POINTMEMBER_SERIALIZERS_ = { - 'http://www.opengis.net/gml': { - 'pointMember': makeChildAppender( - GML3.prototype.writePointMember_) - } -}; - - -/** - * @type {Object.>} - * @private - */ -GML3.prototype.LINESTRINGORCURVEMEMBER_SERIALIZERS_ = { - 'http://www.opengis.net/gml': { - 'lineStringMember': makeChildAppender( - GML3.prototype.writeLineStringOrCurveMember_), - 'curveMember': makeChildAppender( - GML3.prototype.writeLineStringOrCurveMember_) - } -}; - - -/** - * @type {Object.>} - * @private - */ -GML3.prototype.GEOMETRY_SERIALIZERS_ = { - 'http://www.opengis.net/gml': { - 'Curve': makeChildAppender( - GML3.prototype.writeCurveOrLineString_), - 'MultiCurve': makeChildAppender( - GML3.prototype.writeMultiCurveOrLineString_), - 'Point': makeChildAppender(GML3.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 GML3; From 9a28af76cacb059919c5b0341208c317223b871c Mon Sep 17 00:00:00 2001 From: ahocevar Date: Tue, 17 Jul 2018 16:33:23 +0200 Subject: [PATCH 049/107] Use extends and super for GMLBase --- src/ol/format/GMLBase.js | 200 +++++++++++++++++------------------- src/ol/format/XMLFeature.js | 8 +- 2 files changed, 99 insertions(+), 109 deletions(-) diff --git a/src/ol/format/GMLBase.js b/src/ol/format/GMLBase.js index 38267f92ea..44f62826e9 100644 --- a/src/ol/format/GMLBase.js +++ b/src/ol/format/GMLBase.js @@ -4,7 +4,6 @@ // FIXME Envelopes should not be treated as geometries! readEnvelope_ is part // of GEOMETRY_PARSERS_ and methods using GEOMETRY_PARSERS_ do not expect // envelopes/extents, only geometries! -import {inherits} from '../util.js'; import {extend} from '../array.js'; import Feature from '../Feature.js'; import {transformWithOptions} from '../format/Feature.js'; @@ -84,14 +83,15 @@ const ONLY_WHITESPACE_RE = /^[\s\xa0]*$/; * is shared with versioned format classes GML2 and GML3. * * @abstract - * @extends {module:ol/format/XMLFeature} */ -class GMLBase { +class GMLBase extends XMLFeature { /** * @param {module:ol/format/GMLBase~Options=} opt_options Optional configuration object. */ constructor(opt_options) { + super(); + const options = /** @type {module:ol/format/GMLBase~Options} */ (opt_options ? opt_options : {}); /** @@ -123,11 +123,97 @@ class GMLBase { */ this.FEATURE_COLLECTION_PARSERS = {}; this.FEATURE_COLLECTION_PARSERS[GMLNS] = { - 'featureMember': makeReplacer(GMLBase.prototype.readFeaturesInternal), - 'featureMembers': makeReplacer(GMLBase.prototype.readFeaturesInternal) + 'featureMember': makeReplacer(this.readFeaturesInternal), + 'featureMembers': makeReplacer(this.readFeaturesInternal) }; - XMLFeature.call(this); + /** + * @const + * @type {Object.>} + * @private + */ + this.MULTIPOINT_PARSERS_ = { + 'http://www.opengis.net/gml': { + 'pointMember': makeArrayPusher(this.pointMemberParser_), + 'pointMembers': makeArrayPusher(this.pointMemberParser_) + } + }; + + + /** + * @const + * @type {Object.>} + * @private + */ + this.MULTILINESTRING_PARSERS_ = { + 'http://www.opengis.net/gml': { + 'lineStringMember': makeArrayPusher(this.lineStringMemberParser_), + 'lineStringMembers': makeArrayPusher(this.lineStringMemberParser_) + } + }; + + + /** + * @const + * @type {Object.>} + * @private + */ + this.MULTIPOLYGON_PARSERS_ = { + 'http://www.opengis.net/gml': { + 'polygonMember': makeArrayPusher(this.polygonMemberParser_), + 'polygonMembers': makeArrayPusher(this.polygonMemberParser_) + } + }; + + + /** + * @const + * @type {Object.>} + * @private + */ + this.POINTMEMBER_PARSERS_ = { + 'http://www.opengis.net/gml': { + 'Point': makeArrayPusher(this.readFlatCoordinatesFromNode_) + } + }; + + + /** + * @const + * @type {Object.>} + * @private + */ + this.LINESTRINGMEMBER_PARSERS_ = { + 'http://www.opengis.net/gml': { + 'LineString': makeArrayPusher(this.readLineString) + } + }; + + + /** + * @const + * @type {Object.>} + * @private + */ + this.POLYGONMEMBER_PARSERS_ = { + 'http://www.opengis.net/gml': { + 'Polygon': makeArrayPusher(this.readPolygon) + } + }; + + + /** + * @const + * @type {Object.>} + * @protected + */ + this.RING_PARSERS = { + 'http://www.opengis.net/gml': { + 'LinearRing': makeReplacer(this.readFlatLinearRing_) + } + }; + + } /** @@ -470,106 +556,4 @@ class GMLBase { } } -inherits(GMLBase, XMLFeature); - - -/** - * @const - * @type {Object.>} - * @private - */ -GMLBase.prototype.MULTIPOINT_PARSERS_ = { - 'http://www.opengis.net/gml': { - 'pointMember': makeArrayPusher(GMLBase.prototype.pointMemberParser_), - 'pointMembers': makeArrayPusher(GMLBase.prototype.pointMemberParser_) - } -}; - - -/** - * @const - * @type {Object.>} - * @private - */ -GMLBase.prototype.MULTILINESTRING_PARSERS_ = { - 'http://www.opengis.net/gml': { - 'lineStringMember': makeArrayPusher(GMLBase.prototype.lineStringMemberParser_), - 'lineStringMembers': makeArrayPusher(GMLBase.prototype.lineStringMemberParser_) - } -}; - - -/** - * @const - * @type {Object.>} - * @private - */ -GMLBase.prototype.MULTIPOLYGON_PARSERS_ = { - 'http://www.opengis.net/gml': { - 'polygonMember': makeArrayPusher(GMLBase.prototype.polygonMemberParser_), - 'polygonMembers': makeArrayPusher(GMLBase.prototype.polygonMemberParser_) - } -}; - - -/** - * @const - * @type {Object.>} - * @private - */ -GMLBase.prototype.POINTMEMBER_PARSERS_ = { - 'http://www.opengis.net/gml': { - 'Point': makeArrayPusher(GMLBase.prototype.readFlatCoordinatesFromNode_) - } -}; - - -/** - * @const - * @type {Object.>} - * @private - */ -GMLBase.prototype.LINESTRINGMEMBER_PARSERS_ = { - 'http://www.opengis.net/gml': { - 'LineString': makeArrayPusher(GMLBase.prototype.readLineString) - } -}; - - -/** - * @const - * @type {Object.>} - * @private - */ -GMLBase.prototype.POLYGONMEMBER_PARSERS_ = { - 'http://www.opengis.net/gml': { - 'Polygon': makeArrayPusher(GMLBase.prototype.readPolygon) - } -}; - - -/** - * @const - * @type {Object.>} - * @protected - */ -GMLBase.prototype.RING_PARSERS = { - 'http://www.opengis.net/gml': { - 'LinearRing': makeReplacer(GMLBase.prototype.readFlatLinearRing_) - } -}; - - -/** - * Read all features from a GML FeatureCollection. - * - * @function - * @param {Document|Node|Object|string} source Source. - * @param {module:ol/format/Feature~ReadOptions=} opt_options Options. - * @return {Array.} Features. - * @api - */ -GMLBase.prototype.readFeatures; - - export default GMLBase; diff --git a/src/ol/format/XMLFeature.js b/src/ol/format/XMLFeature.js index 8874ee7c20..ae873faec6 100644 --- a/src/ol/format/XMLFeature.js +++ b/src/ol/format/XMLFeature.js @@ -75,7 +75,13 @@ class XMLFeature { } /** - * @inheritDoc + * Read all features from a feature collection. + * + * @function + * @param {Document|Node|Object|string} source Source. + * @param {module:ol/format/Feature~ReadOptions=} opt_options Options. + * @return {Array.} Features. + * @api */ readFeatures(source, opt_options) { if (isDocument(source)) { From e1e5e54d7482463d06df5b1cab06dfc18d792cf6 Mon Sep 17 00:00:00 2001 From: ahocevar Date: Tue, 17 Jul 2018 16:43:28 +0200 Subject: [PATCH 050/107] Use extends and super for format/GPX --- src/ol/format/GPX.js | 71 +++++++------------------------------ src/ol/format/XMLFeature.js | 20 +++++++++-- 2 files changed, 29 insertions(+), 62 deletions(-) diff --git a/src/ol/format/GPX.js b/src/ol/format/GPX.js index 5981678cba..4189888192 100644 --- a/src/ol/format/GPX.js +++ b/src/ol/format/GPX.js @@ -1,7 +1,6 @@ /** * @module ol/format/GPX */ -import {inherits} from '../util.js'; import Feature from '../Feature.js'; import {includes} from '../array.js'; import {transformWithOptions} from '../format/Feature.js'; @@ -103,19 +102,28 @@ const GPX_SERIALIZERS = makeStructureNS( * @classdesc * Feature format for reading and writing data in the GPX format. * - * @extends {module:ol/format/XMLFeature} + * Note that {@link module:ol/format/GPX~GPX#readFeature} only reads the first + * feature of the source. + * + * When reading, routes (``) are converted into LineString geometries, and + * tracks (``) into MultiLineString. Any properties on route and track + * waypoints are ignored. + * + * When writing, LineString geometries are output as routes (``), and + * MultiLineString as tracks (``). + * * @api */ -class GPX { +class GPX extends XMLFeature { /** * @param {module:ol/format/GPX~Options=} opt_options Options. */ constructor(opt_options) { + super(); const options = opt_options ? opt_options : {}; - XMLFeature.call(this); /** * @inheritDoc @@ -214,8 +222,6 @@ class GPX { } } -inherits(GPX, XMLFeature); - /** * @const @@ -702,45 +708,6 @@ function readWpt(node, objectStack) { } -/** - * Read the first feature from a GPX source. - * Routes (``) are converted into LineString geometries, and tracks (``) - * into MultiLineString. Any properties on route and track waypoints are ignored. - * - * @function - * @param {Document|Node|Object|string} source Source. - * @param {module:ol/format/Feature~ReadOptions=} opt_options Read options. - * @return {module:ol/Feature} Feature. - * @api - */ -GPX.prototype.readFeature; - - -/** - * Read all features from a GPX source. - * Routes (``) are converted into LineString geometries, and tracks (``) - * into MultiLineString. Any properties on route and track waypoints are ignored. - * - * @function - * @param {Document|Node|Object|string} source Source. - * @param {module:ol/format/Feature~ReadOptions=} opt_options Read options. - * @return {Array.} Features. - * @api - */ -GPX.prototype.readFeatures; - - -/** - * Read the projection from a GPX source. - * - * @function - * @param {Document|Node|Object|string} source Source. - * @return {module:ol/proj/Projection} Projection. - * @api - */ -GPX.prototype.readProjection; - - /** * @param {Node} node Node. * @param {string} value Value for the link's `href` attribute. @@ -887,18 +854,4 @@ function writeWpt(node, feature, objectStack) { } -/** - * Encode an array of features in the GPX format. - * LineString geometries are output as routes (``), and MultiLineString - * as tracks (``). - * - * @function - * @param {Array.} features Features. - * @param {module:ol/format/Feature~WriteOptions=} opt_options Write options. - * @return {string} Result. - * @api - */ -GPX.prototype.writeFeatures; - - export default GPX; diff --git a/src/ol/format/XMLFeature.js b/src/ol/format/XMLFeature.js index ae873faec6..db23e96d0f 100644 --- a/src/ol/format/XMLFeature.js +++ b/src/ol/format/XMLFeature.js @@ -36,7 +36,12 @@ class XMLFeature { } /** - * @inheritDoc + * Read a single feature. + * + * @param {Document|Node|Object|string} source Source. + * @param {module:ol/format/Feature~ReadOptions=} opt_options Read options. + * @return {module:ol/Feature} Feature. + * @api */ readFeature(source, opt_options) { if (isDocument(source)) { @@ -161,7 +166,11 @@ class XMLFeature { } /** - * @inheritDoc + * Read the projection from the source. + * + * @param {Document|Node|Object|string} source Source. + * @return {module:ol/proj/Projection} Projection. + * @api */ readProjection(source) { if (isDocument(source)) { @@ -213,7 +222,12 @@ class XMLFeature { } /** - * @inheritDoc + * Encode an array of features as string. + * + * @param {Array.} features Features. + * @param {module:ol/format/Feature~WriteOptions=} opt_options Write options. + * @return {string} Result. + * @api */ writeFeatures(features, opt_options) { const node = this.writeFeaturesNode(features, opt_options); From f140ff368d49d3a8a19868b6520ad488f16055f6 Mon Sep 17 00:00:00 2001 From: Frederic Junod Date: Tue, 17 Jul 2018 16:44:33 +0200 Subject: [PATCH 051/107] Move jsdoc constructor comments for controls --- src/ol/control/Attribution.js | 6 ++++-- src/ol/control/Control.js | 6 ++++-- src/ol/control/FullScreen.js | 7 ++++--- src/ol/control/MousePosition.js | 7 ++++--- src/ol/control/OverviewMap.js | 7 +++++-- src/ol/control/Rotate.js | 6 ++++-- src/ol/control/ScaleLine.js | 6 ++++-- src/ol/control/Zoom.js | 6 ++++-- src/ol/control/ZoomSlider.js | 6 ++++-- src/ol/control/ZoomToExtent.js | 6 ++++-- 10 files changed, 41 insertions(+), 22 deletions(-) diff --git a/src/ol/control/Attribution.js b/src/ol/control/Attribution.js index e3531e90e6..57d4d8922e 100644 --- a/src/ol/control/Attribution.js +++ b/src/ol/control/Attribution.js @@ -41,11 +41,13 @@ import {visibleAtResolution} from '../layer/Layer.js'; * By default it will show in the bottom right portion of the map, but this can * be changed by using a css selector for `.ol-attribution`. * - * @constructor - * @param {module:ol/control/Attribution~Options=} opt_options Attribution options. * @api */ class Attribution extends Control { + + /** + * @param {module:ol/control/Attribution~Options=} opt_options Attribution options. + */ constructor(opt_options) { const options = opt_options ? opt_options : {}; diff --git a/src/ol/control/Control.js b/src/ol/control/Control.js index de7215e614..ebeea4f3e0 100644 --- a/src/ol/control/Control.js +++ b/src/ol/control/Control.js @@ -43,11 +43,13 @@ import {listen, unlistenByKey} from '../events.js'; * You can also extend this base for your own control class. See * examples/custom-controls for an example of how to do this. * - * @constructor - * @param {module:ol/control/Control~Options} options Control options. * @api */ class Control extends BaseObject { + + /** + * @param {module:ol/control/Control~Options} options Control options. + */ constructor(options) { super(); diff --git a/src/ol/control/FullScreen.js b/src/ol/control/FullScreen.js index 5bc3487537..15ce93e7bc 100644 --- a/src/ol/control/FullScreen.js +++ b/src/ol/control/FullScreen.js @@ -60,12 +60,13 @@ const getChangeType = (function() { * The [Fullscreen API](http://www.w3.org/TR/fullscreen/) is used to * toggle the map in full screen mode. * - * - * @constructor - * @param {module:ol/control/FullScreen~Options=} opt_options Options. * @api */ class FullScreen extends Control { + + /** + * @param {module:ol/control/FullScreen~Options=} opt_options Options. + */ constructor(opt_options) { const options = opt_options ? opt_options : {}; diff --git a/src/ol/control/MousePosition.js b/src/ol/control/MousePosition.js index 666d30c558..f3f68f3996 100644 --- a/src/ol/control/MousePosition.js +++ b/src/ol/control/MousePosition.js @@ -44,12 +44,13 @@ const COORDINATE_FORMAT = 'coordinateFormat'; * By default the control is shown in the top right corner of the map, but this * can be changed by using the css selector `.ol-mouse-position`. * - * @constructor - * @param {module:ol/control/MousePosition~Options=} opt_options Mouse position - * options. * @api */ class MousePosition extends Control { + + /** + * @param {module:ol/control/MousePosition~Options=} opt_options Mouse position options. + */ constructor(opt_options) { const options = opt_options ? opt_options : {}; diff --git a/src/ol/control/OverviewMap.js b/src/ol/control/OverviewMap.js index 5095040040..435fd4d4cc 100644 --- a/src/ol/control/OverviewMap.js +++ b/src/ol/control/OverviewMap.js @@ -60,11 +60,14 @@ const MIN_RATIO = 0.1; /** * Create a new control with a map acting as an overview map for an other * defined map. - * @constructor - * @param {module:ol/control/OverviewMap~Options=} opt_options OverviewMap options. + * * @api */ class OverviewMap extends Control { + + /** + * @param {module:ol/control/OverviewMap~Options=} opt_options OverviewMap options. + */ constructor(opt_options) { const options = opt_options ? opt_options : {}; diff --git a/src/ol/control/Rotate.js b/src/ol/control/Rotate.js index a12205dce0..6b364daa66 100644 --- a/src/ol/control/Rotate.js +++ b/src/ol/control/Rotate.js @@ -31,11 +31,13 @@ import EventType from '../events/EventType.js'; * To style this control use css selector `.ol-rotate`. A `.ol-hidden` css * selector is added to the button when the rotation is 0. * - * @constructor - * @param {module:ol/control/Rotate~Options=} opt_options Rotate options. * @api */ class Rotate extends Control { + + /** + * @param {module:ol/control/Rotate~Options=} opt_options Rotate options. + */ constructor(opt_options) { const options = opt_options ? opt_options : {}; diff --git a/src/ol/control/ScaleLine.js b/src/ol/control/ScaleLine.js index 6fbf7b15cb..c854920cfd 100644 --- a/src/ol/control/ScaleLine.js +++ b/src/ol/control/ScaleLine.js @@ -58,11 +58,13 @@ const LEADING_DIGITS = [1, 2, 5]; * By default the scale line will show in the bottom left portion of the map, * but this can be changed by using the css selector `.ol-scale-line`. * - * @constructor - * @param {module:ol/control/ScaleLine~Options=} opt_options Scale line options. * @api */ class ScaleLine extends Control { + + /** + * @param {module:ol/control/ScaleLine~Options=} opt_options Scale line options. + */ constructor(opt_options) { const options = opt_options ? opt_options : {}; diff --git a/src/ol/control/Zoom.js b/src/ol/control/Zoom.js index 20832e274c..b6a0cc1fb1 100644 --- a/src/ol/control/Zoom.js +++ b/src/ol/control/Zoom.js @@ -30,11 +30,13 @@ import {easeOut} from '../easing.js'; * This control is one of the default controls of a map. To style this control * use css selectors `.ol-zoom-in` and `.ol-zoom-out`. * - * @constructor - * @param {module:ol/control/Zoom~Options=} opt_options Zoom options. * @api */ class Zoom extends Control { + + /** + * @param {module:ol/control/Zoom~Options=} opt_options Zoom options. + */ constructor(opt_options) { const options = opt_options ? opt_options : {}; diff --git a/src/ol/control/ZoomSlider.js b/src/ol/control/ZoomSlider.js index 6dbc53ab42..e2369689c2 100644 --- a/src/ol/control/ZoomSlider.js +++ b/src/ol/control/ZoomSlider.js @@ -41,11 +41,13 @@ const Direction = { * * map.addControl(new ZoomSlider()); * - * @constructor - * @param {module:ol/control/ZoomSlider~Options=} opt_options Zoom slider options. * @api */ class ZoomSlider extends Control { + + /** + * @param {module:ol/control/ZoomSlider~Options=} opt_options Zoom slider options. + */ constructor(opt_options) { const options = opt_options ? opt_options : {}; diff --git a/src/ol/control/ZoomToExtent.js b/src/ol/control/ZoomToExtent.js index 35997066ae..b55f82aa6f 100644 --- a/src/ol/control/ZoomToExtent.js +++ b/src/ol/control/ZoomToExtent.js @@ -25,11 +25,13 @@ import {CLASS_CONTROL, CLASS_UNSELECTABLE} from '../css.js'; * A button control which, when pressed, changes the map view to a specific * extent. To style this control use the css selector `.ol-zoom-extent`. * - * @constructor - * @param {module:ol/control/ZoomToExtent~Options=} opt_options Options. * @api */ class ZoomToExtent extends Control { + + /** + * @param {module:ol/control/ZoomToExtent~Options=} opt_options Options. + */ constructor(opt_options) { const options = opt_options ? opt_options : {}; From 6a39b9f412d4aa9e7d008b928dbbd85f6c7fd9fb Mon Sep 17 00:00:00 2001 From: ahocevar Date: Tue, 17 Jul 2018 16:49:23 +0200 Subject: [PATCH 052/107] Use extends and super for format/IGC --- src/ol/format/IGC.js | 50 +++++------------------------------- src/ol/format/TextFeature.js | 21 ++++++++++++--- 2 files changed, 24 insertions(+), 47 deletions(-) diff --git a/src/ol/format/IGC.js b/src/ol/format/IGC.js index 8c21735640..4072e779a6 100644 --- a/src/ol/format/IGC.js +++ b/src/ol/format/IGC.js @@ -1,7 +1,6 @@ /** * @module ol/format/IGC */ -import {inherits} from '../util.js'; import Feature from '../Feature.js'; import {transformWithOptions} from '../format/Feature.js'; import TextFeature from '../format/TextFeature.js'; @@ -61,20 +60,22 @@ const NEWLINE_RE = /\r\n|\r|\n/; * @classdesc * Feature format for `*.igc` flight recording files. * - * @extends {module:ol/format/TextFeature} + * As IGC sources contain a single feature, + * {@link module:ol/format/IGC~IGC#readFeatures} will return the feature in an + * array + * * @api */ -class IGC { +class IGC extends TextFeature { /** * @param {module:ol/format/IGC~Options=} opt_options Options. */ constructor(opt_options) { + super(); const options = opt_options ? opt_options : {}; - TextFeature.call(this); - /** * @inheritDoc */ @@ -199,43 +200,4 @@ class IGC { readGeometryFromText(text, opt_options) {} } -inherits(IGC, TextFeature); - - -/** - * Read the feature from the IGC source. - * - * @function - * @param {Document|Node|Object|string} source Source. - * @param {module:ol/format/Feature~ReadOptions=} opt_options Read options. - * @return {module:ol/Feature} Feature. - * @api - */ -IGC.prototype.readFeature; - - -/** - * Read the feature from the source. As IGC sources contain a single - * feature, this will return the feature in an array. - * - * @function - * @param {Document|Node|Object|string} source Source. - * @param {module:ol/format/Feature~ReadOptions=} opt_options Read options. - * @return {Array.} Features. - * @api - */ -IGC.prototype.readFeatures; - - -/** - * Read the projection from the IGC source. - * - * @function - * @param {Document|Node|Object|string} source Source. - * @return {module:ol/proj/Projection} Projection. - * @api - */ -IGC.prototype.readProjection; - - export default IGC; diff --git a/src/ol/format/TextFeature.js b/src/ol/format/TextFeature.js index bf0561da42..3769ea3845 100644 --- a/src/ol/format/TextFeature.js +++ b/src/ol/format/TextFeature.js @@ -27,7 +27,12 @@ class TextFeature { } /** - * @inheritDoc + * Read the feature from the source. + * + * @param {Document|Node|Object|string} source Source. + * @param {module:ol/format/Feature~ReadOptions=} opt_options Read options. + * @return {module:ol/Feature} Feature. + * @api */ readFeature(source, opt_options) { return this.readFeatureFromText(getText(source), this.adaptOptions(opt_options)); @@ -43,7 +48,12 @@ class TextFeature { readFeatureFromText(text, opt_options) {} /** - * @inheritDoc + * Read the features from the source. + * + * @param {Document|Node|Object|string} source Source. + * @param {module:ol/format/Feature~ReadOptions=} opt_options Read options. + * @return {Array.} Features. + * @api */ readFeatures(source, opt_options) { return this.readFeaturesFromText(getText(source), this.adaptOptions(opt_options)); @@ -75,7 +85,12 @@ class TextFeature { readGeometryFromText(text, opt_options) {} /** - * @inheritDoc + * Read the projection from the source. + * + * @function + * @param {Document|Node|Object|string} source Source. + * @return {module:ol/proj/Projection} Projection. + * @api */ readProjection(source) { return this.readProjectionFromText(getText(source)); From ad4e192ee9b35691fbf71e1f5183656d482c579a Mon Sep 17 00:00:00 2001 From: ahocevar Date: Tue, 17 Jul 2018 16:54:17 +0200 Subject: [PATCH 053/107] Use extends and super for format/JSONFeature --- src/ol/format/JSONFeature.js | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/ol/format/JSONFeature.js b/src/ol/format/JSONFeature.js index dfdc63c041..34042a5871 100644 --- a/src/ol/format/JSONFeature.js +++ b/src/ol/format/JSONFeature.js @@ -1,7 +1,6 @@ /** * @module ol/format/JSONFeature */ -import {inherits} from '../util.js'; import FeatureFormat from '../format/Feature.js'; import FormatType from '../format/FormatType.js'; @@ -12,11 +11,10 @@ import FormatType from '../format/FormatType.js'; * Base class for JSON feature formats. * * @abstract - * @extends {module:ol/format/Feature} */ -class JSONFeature { +class JSONFeature extends FeatureFormat { constructor() { - FeatureFormat.call(this); + super(); } /** @@ -174,8 +172,6 @@ class JSONFeature { writeGeometryObject(geometry, opt_options) {} } -inherits(JSONFeature, FeatureFormat); - /** * @param {Document|Node|Object|string} source Source. From a8bd0c5dd7a24547ac5fd8c1e41ea680f98181e2 Mon Sep 17 00:00:00 2001 From: ahocevar Date: Tue, 17 Jul 2018 16:59:13 +0200 Subject: [PATCH 054/107] Use extends and super for format/KML --- src/ol/format/KML.js | 68 ++++++-------------------------------------- 1 file changed, 9 insertions(+), 59 deletions(-) diff --git a/src/ol/format/KML.js b/src/ol/format/KML.js index be5b75b5dd..e45e3b6afa 100644 --- a/src/ol/format/KML.js +++ b/src/ol/format/KML.js @@ -1,7 +1,6 @@ /** * @module ol/format/KML */ -import {inherits} from '../util.js'; import Feature from '../Feature.js'; import {extend, includes} from '../array.js'; import {assert} from '../asserts.js'; @@ -388,23 +387,28 @@ function createStyleDefaults() { * @classdesc * Feature format for reading and writing data in the KML format. * + * {@link module:ol/format/KML~KML#readFeature} will read the first feature from + * a KML source. + * + * MultiGeometries are converted into GeometryCollections if they are a mix of + * geometry types, and into MultiPoint/MultiLineString/MultiPolygon if they are + * all of the same type. + * * Note that the KML format uses the URL() constructor. Older browsers such as IE * which do not support this will need a URL polyfill to be loaded before use. * - * @extends {module:ol/format/XMLFeature} * @api */ -class KML { +class KML extends XMLFeature { /** * @param {module:ol/format/KML~Options=} opt_options Options. */ constructor(opt_options) { + super(); const options = opt_options ? opt_options : {}; - XMLFeature.call(this); - if (!DEFAULT_STYLE_ARRAY) { createStyleDefaults(); } @@ -849,8 +853,6 @@ class KML { } } -inherits(KML, XMLFeature); - /** * @param {module:ol/style/Style|undefined} foundStyle Style. @@ -2066,45 +2068,6 @@ function whenParser(node, objectStack) { } -/** - * Read the first feature from a KML source. MultiGeometries are converted into - * GeometryCollections if they are a mix of geometry types, and into MultiPoint/ - * MultiLineString/MultiPolygon if they are all of the same type. - * - * @function - * @param {Document|Node|Object|string} source Source. - * @param {module:ol/format/Feature~ReadOptions=} opt_options Read options. - * @return {module:ol/Feature} Feature. - * @api - */ -KML.prototype.readFeature; - - -/** - * Read all features from a KML source. MultiGeometries are converted into - * GeometryCollections if they are a mix of geometry types, and into MultiPoint/ - * MultiLineString/MultiPolygon if they are all of the same type. - * - * @function - * @param {Document|Node|Object|string} source Source. - * @param {module:ol/format/Feature~ReadOptions=} opt_options Read options. - * @return {Array.} Features. - * @api - */ -KML.prototype.readFeatures; - - -/** - * Read the projection from a KML source. - * - * @function - * @param {Document|Node|Object|string} source Source. - * @return {module:ol/proj/Projection} Projection. - * @api - */ -KML.prototype.readProjection; - - /** * @param {Node} node Node to append a TextNode with the color to. * @param {module:ol/color~Color|string} color Color. @@ -2964,17 +2927,4 @@ function writeVec2(node, vec2) { } -/** - * Encode an array of features in the KML format. GeometryCollections, MultiPoints, - * MultiLineStrings, and MultiPolygons are output as MultiGeometries. - * - * @function - * @param {Array.} features Features. - * @param {module:ol/format/Feature~WriteOptions=} opt_options Options. - * @return {string} Result. - * @api - */ -KML.prototype.writeFeatures; - - export default KML; From cf15af43e384306db91d23a2ef9ae4ce9b0ad504 Mon Sep 17 00:00:00 2001 From: ahocevar Date: Tue, 17 Jul 2018 17:00:43 +0200 Subject: [PATCH 055/107] Use extends and super for format/MVT --- src/ol/format/MVT.js | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/src/ol/format/MVT.js b/src/ol/format/MVT.js index 9c325c0ef3..400e93e75e 100644 --- a/src/ol/format/MVT.js +++ b/src/ol/format/MVT.js @@ -3,7 +3,6 @@ */ //FIXME Implement projection handling -import {inherits} from '../util.js'; import {assert} from '../asserts.js'; import PBF from 'pbf'; import FeatureFormat, {transformWithOptions} from '../format/Feature.js'; @@ -42,18 +41,16 @@ import RenderFeature from '../render/Feature.js'; * @classdesc * Feature format for reading data in the Mapbox MVT format. * - * @extends {module:ol/format/Feature} * @param {module:ol/format/MVT~Options=} opt_options Options. * @api */ -class MVT { +class MVT extends FeatureFormat { /** * @param {module:ol/format/MVT~Options=} opt_options Options. */ constructor(opt_options) { - - FeatureFormat.call(this); + super(); const options = opt_options ? opt_options : {}; @@ -321,8 +318,6 @@ class MVT { writeFeatures() {} } -inherits(MVT, FeatureFormat); - /** * Reader callback for parsing layers. From 5e71e6ce805eaf9d94d12b9bf3ed8892a519c131 Mon Sep 17 00:00:00 2001 From: ahocevar Date: Tue, 17 Jul 2018 17:02:55 +0200 Subject: [PATCH 056/107] Use extends and super for format/OSMXML --- src/ol/format/OSMXML.js | 31 ++----------------------------- 1 file changed, 2 insertions(+), 29 deletions(-) diff --git a/src/ol/format/OSMXML.js b/src/ol/format/OSMXML.js index 3d76a8e7c1..acd6d3bbc1 100644 --- a/src/ol/format/OSMXML.js +++ b/src/ol/format/OSMXML.js @@ -2,7 +2,6 @@ * @module ol/format/OSMXML */ // FIXME add typedef for stack state objects -import {inherits} from '../util.js'; import {extend} from '../array.js'; import Feature from '../Feature.js'; import {transformWithOptions} from '../format/Feature.js'; @@ -50,12 +49,11 @@ const PARSERS = makeStructureNS( * Feature format for reading data in the * [OSMXML format](http://wiki.openstreetmap.org/wiki/OSM_XML). * - * @extends {module:ol/format/XMLFeature} * @api */ -class OSMXML { +class OSMXML extends XMLFeature { constructor() { - XMLFeature.call(this); + super(); /** * @inheritDoc @@ -122,8 +120,6 @@ class OSMXML { writeGeometryNode(geometry, opt_options) {} } -inherits(OSMXML, XMLFeature); - /** * @const @@ -200,27 +196,4 @@ function readTag(node, objectStack) { } -/** - * Read all features from an OSM source. - * - * @function - * @param {Document|Node|Object|string} source Source. - * @param {module:ol/format/Feature~ReadOptions=} opt_options Read options. - * @return {Array.} Features. - * @api - */ -OSMXML.prototype.readFeatures; - - -/** - * Read the projection from an OSM source. - * - * @function - * @param {Document|Node|Object|string} source Source. - * @return {module:ol/proj/Projection} Projection. - * @api - */ -OSMXML.prototype.readProjection; - - export default OSMXML; From 092441017dc29a19056bd95bf26bc6577fbcb2d8 Mon Sep 17 00:00:00 2001 From: ahocevar Date: Tue, 17 Jul 2018 17:04:02 +0200 Subject: [PATCH 057/107] Use extends and super for format/OWS --- src/ol/format/OWS.js | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/src/ol/format/OWS.js b/src/ol/format/OWS.js index dfcc9e2d34..0bbb70d164 100644 --- a/src/ol/format/OWS.js +++ b/src/ol/format/OWS.js @@ -1,7 +1,6 @@ /** * @module ol/format/OWS */ -import {inherits} from '../util.js'; import {readHref} from '../format/XLink.js'; import XML from '../format/XML.js'; import {readString} from '../format/xsd.js'; @@ -27,12 +26,9 @@ const PARSERS = makeStructureNS( }); -/** - * @extends {module:ol/format/XML} - */ -class OWS { +class OWS extends XML { constructor() { - XML.call(this); + super(); } /** @@ -57,8 +53,6 @@ class OWS { } } -inherits(OWS, XML); - /** * @const From 9b2b921f7ba94944533397b7b2511a750a7f23a2 Mon Sep 17 00:00:00 2001 From: ahocevar Date: Tue, 17 Jul 2018 17:13:13 +0200 Subject: [PATCH 058/107] Use extends and super for format/Polyline --- src/ol/format/Polyline.js | 76 +++++------------------------------- src/ol/format/TextFeature.js | 15 ++++++- 2 files changed, 22 insertions(+), 69 deletions(-) diff --git a/src/ol/format/Polyline.js b/src/ol/format/Polyline.js index 5cdfbee7a9..206d46a03f 100644 --- a/src/ol/format/Polyline.js +++ b/src/ol/format/Polyline.js @@ -1,7 +1,6 @@ /** * @module ol/format/Polyline */ -import {inherits} from '../util.js'; import {assert} from '../asserts.js'; import Feature from '../Feature.js'; import {transformWithOptions} from '../format/Feature.js'; @@ -27,19 +26,25 @@ import {get as getProjection} from '../proj.js'; * Feature format for reading and writing data in the Encoded * Polyline Algorithm Format. * - * @extends {module:ol/format/TextFeature} + * When reading features, the coordinates are assumed to be in two dimensions + * and in [latitude, longitude] order. + * + * As Polyline sources contain a single feature, + * {@link module:ol/format/Polyline~Polyline#readFeatures} will return the + * feature in an array. + * * @api */ -class Polyline { +class Polyline extends TextFeature { /** * @param {module:ol/format/Polyline~Options=} opt_options Optional configuration object. */ constructor(opt_options) { + super(); const options = opt_options ? opt_options : {}; - TextFeature.call(this); /** * @inheritDoc @@ -127,8 +132,6 @@ class Polyline { } } -inherits(Polyline, TextFeature); - /** * Encode a list of n-dimensional points and return an encoded string @@ -334,65 +337,4 @@ export function encodeUnsignedInteger(num) { } -/** - * Read the feature from the Polyline source. The coordinates are assumed to be - * in two dimensions and in latitude, longitude order. - * - * @function - * @param {Document|Node|Object|string} source Source. - * @param {module:ol/format/Feature~ReadOptions=} opt_options Read options. - * @return {module:ol/Feature} Feature. - * @api - */ -Polyline.prototype.readFeature; - - -/** - * Read the feature from the source. As Polyline sources contain a single - * feature, this will return the feature in an array. - * - * @function - * @param {Document|Node|Object|string} source Source. - * @param {module:ol/format/Feature~ReadOptions=} opt_options Read options. - * @return {Array.} Features. - * @api - */ -Polyline.prototype.readFeatures; - - -/** - * Read the geometry from the source. - * - * @function - * @param {Document|Node|Object|string} source Source. - * @param {module:ol/format/Feature~ReadOptions=} opt_options Read options. - * @return {module:ol/geom/Geometry} Geometry. - * @api - */ -Polyline.prototype.readGeometry; - - -/** - * Read the projection from a Polyline source. - * - * @function - * @param {Document|Node|Object|string} source Source. - * @return {module:ol/proj/Projection} Projection. - * @api - */ -Polyline.prototype.readProjection; - - -/** - * Write a single geometry in Polyline format. - * - * @function - * @param {module:ol/geom/Geometry} geometry Geometry. - * @param {module:ol/format/Feature~WriteOptions=} opt_options Write options. - * @return {string} Geometry. - * @api - */ -Polyline.prototype.writeGeometry; - - export default Polyline; diff --git a/src/ol/format/TextFeature.js b/src/ol/format/TextFeature.js index 3769ea3845..953763ecf1 100644 --- a/src/ol/format/TextFeature.js +++ b/src/ol/format/TextFeature.js @@ -69,7 +69,12 @@ class TextFeature { readFeaturesFromText(text, opt_options) {} /** - * @inheritDoc + * Read the geometry from the source. + * + * @param {Document|Node|Object|string} source Source. + * @param {module:ol/format/Feature~ReadOptions=} opt_options Read options. + * @return {module:ol/geom/Geometry} Geometry. + * @api */ readGeometry(source, opt_options) { return this.readGeometryFromText(getText(source), this.adaptOptions(opt_options)); @@ -138,7 +143,13 @@ class TextFeature { writeFeaturesText(features, opt_options) {} /** - * @inheritDoc + * Write a single geometry in Polyline format. + * + * @function + * @param {module:ol/geom/Geometry} geometry Geometry. + * @param {module:ol/format/Feature~WriteOptions=} opt_options Write options. + * @return {string} Geometry. + * @api */ writeGeometry(geometry, opt_options) { return this.writeGeometryText(geometry, this.adaptOptions(opt_options)); From c969afcddc530e45d1f783365c237673006e199e Mon Sep 17 00:00:00 2001 From: ahocevar Date: Tue, 17 Jul 2018 17:14:14 +0200 Subject: [PATCH 059/107] Use extends and super for format/TextFeature --- src/ol/format/TextFeature.js | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/ol/format/TextFeature.js b/src/ol/format/TextFeature.js index 953763ecf1..a1bb7c965c 100644 --- a/src/ol/format/TextFeature.js +++ b/src/ol/format/TextFeature.js @@ -1,7 +1,6 @@ /** * @module ol/format/TextFeature */ -import {inherits} from '../util.js'; import FeatureFormat from '../format/Feature.js'; import FormatType from '../format/FormatType.js'; @@ -12,11 +11,10 @@ import FormatType from '../format/FormatType.js'; * Base class for text feature formats. * * @abstract - * @extends {module:ol/format/Feature} */ -class TextFeature { +class TextFeature extends FeatureFormat { constructor() { - FeatureFormat.call(this); + super(); } /** @@ -165,8 +163,6 @@ class TextFeature { writeGeometryText(geometry, opt_options) {} } -inherits(TextFeature, FeatureFormat); - /** * @param {Document|Node|Object|string} source Source. From 788f932550626440b0fb145e0b19811e33977e39 Mon Sep 17 00:00:00 2001 From: ahocevar Date: Tue, 17 Jul 2018 17:15:41 +0200 Subject: [PATCH 060/107] Use extends and super for format/TopoJSON --- src/ol/format/TopoJSON.js | 31 ++----------------------------- 1 file changed, 2 insertions(+), 29 deletions(-) diff --git a/src/ol/format/TopoJSON.js b/src/ol/format/TopoJSON.js index 5c586c36f4..d7dc6aba0e 100644 --- a/src/ol/format/TopoJSON.js +++ b/src/ol/format/TopoJSON.js @@ -1,7 +1,6 @@ /** * @module ol/format/TopoJSON */ -import {inherits} from '../util.js'; import Feature from '../Feature.js'; import {transformWithOptions} from '../format/Feature.js'; import JSONFeature from '../format/JSONFeature.js'; @@ -43,20 +42,18 @@ import {get as getProjection} from '../proj.js'; * @classdesc * Feature format for reading data in the TopoJSON format. * - * @extends {module:ol/format/JSONFeature} * @api */ -class TopoJSON { +class TopoJSON extends JSONFeature { /** * @param {module:ol/format/TopoJSON~Options=} opt_options Options. */ constructor(opt_options) { + super(); const options = opt_options ? opt_options : {}; - JSONFeature.call(this); - /** * @private * @type {string|undefined} @@ -157,8 +154,6 @@ class TopoJSON { readFeatureFromObject() {} } -inherits(TopoJSON, JSONFeature); - /** * @const @@ -380,17 +375,6 @@ function readFeatureFromGeometry(object, arcs, scale, translate, property, name, } -/** - * Read all features from a TopoJSON source. - * - * @function - * @param {Document|Node|Object|string} source Source. - * @return {Array.} Features. - * @api - */ -TopoJSON.prototype.readFeatures; - - /** * Apply a linear transform to array of arcs. The provided array of arcs is * modified in place. @@ -441,15 +425,4 @@ function transformVertex(vertex, scale, translate) { } -/** - * Read the projection from a TopoJSON source. - * - * @param {Document|Node|Object|string} object Source. - * @return {module:ol/proj/Projection} Projection. - * @override - * @api - */ -TopoJSON.prototype.readProjection; - - export default TopoJSON; From 942a219c3023a4d2961e49f6924be7713a7e3d30 Mon Sep 17 00:00:00 2001 From: ahocevar Date: Tue, 17 Jul 2018 17:42:25 +0200 Subject: [PATCH 061/107] Use extends and super for format/WFS --- src/ol/format/GML2.js | 301 ++++++++++---------- src/ol/format/GML3.js | 572 +++++++++++++++++++-------------------- src/ol/format/GMLBase.js | 173 ++++++------ src/ol/format/WFS.js | 33 +-- 4 files changed, 526 insertions(+), 553 deletions(-) diff --git a/src/ol/format/GML2.js b/src/ol/format/GML2.js index a35088f0fe..a6baf61c32 100644 --- a/src/ol/format/GML2.js +++ b/src/ol/format/GML2.js @@ -59,156 +59,6 @@ class GML2 extends GMLBase { this.schemaLocation = options.schemaLocation ? options.schemaLocation : schemaLocation; - /** - * @const - * @type {Object.>} - * @private - */ - this.GEOMETRY_FLAT_COORDINATES_PARSERS_ = { - 'http://www.opengis.net/gml': { - 'coordinates': makeReplacer(this.readFlatCoordinates_) - } - }; - - /** - * @const - * @type {Object.>} - * @private - */ - this.FLAT_LINEAR_RINGS_PARSERS_ = { - 'http://www.opengis.net/gml': { - 'innerBoundaryIs': this.innerBoundaryIsParser_, - 'outerBoundaryIs': this.outerBoundaryIsParser_ - } - }; - - /** - * @const - * @type {Object.>} - * @private - */ - this.BOX_PARSERS_ = { - 'http://www.opengis.net/gml': { - 'coordinates': makeArrayPusher( - this.readFlatCoordinates_) - } - }; - - /** - * @const - * @type {Object.>} - * @private - */ - this.GEOMETRY_PARSERS_ = { - 'http://www.opengis.net/gml': { - 'Point': makeReplacer(this.readPoint), - 'MultiPoint': makeReplacer( - this.readMultiPoint), - 'LineString': makeReplacer( - this.readLineString), - 'MultiLineString': makeReplacer( - this.readMultiLineString), - 'LinearRing': makeReplacer( - this.readLinearRing), - 'Polygon': makeReplacer(this.readPolygon), - 'MultiPolygon': makeReplacer( - this.readMultiPolygon), - 'Box': makeReplacer(this.readBox_) - } - }; - - /** - * @const - * @type {Object.>} - * @private - */ - this.GEOMETRY_SERIALIZERS_ = { - 'http://www.opengis.net/gml': { - 'Curve': makeChildAppender( - this.writeCurveOrLineString_), - 'MultiCurve': makeChildAppender( - this.writeMultiCurveOrLineString_), - 'Point': makeChildAppender(this.writePoint_), - 'MultiPoint': makeChildAppender( - this.writeMultiPoint_), - 'LineString': makeChildAppender( - this.writeCurveOrLineString_), - 'MultiLineString': makeChildAppender( - this.writeMultiCurveOrLineString_), - 'LinearRing': makeChildAppender( - this.writeLinearRing_), - 'Polygon': makeChildAppender( - this.writeSurfaceOrPolygon_), - 'MultiPolygon': makeChildAppender( - this.writeMultiSurfaceOrPolygon_), - 'Surface': makeChildAppender( - this.writeSurfaceOrPolygon_), - 'MultiSurface': makeChildAppender( - this.writeMultiSurfaceOrPolygon_), - 'Envelope': makeChildAppender( - this.writeEnvelope) - } - }; - - /** - * @type {Object.>} - * @private - */ - this.LINESTRINGORCURVEMEMBER_SERIALIZERS_ = { - 'http://www.opengis.net/gml': { - 'lineStringMember': makeChildAppender( - this.writeLineStringOrCurveMember_), - 'curveMember': makeChildAppender( - this.writeLineStringOrCurveMember_) - } - }; - - /** - * @type {Object.>} - * @private - */ - this.RING_SERIALIZERS_ = { - 'http://www.opengis.net/gml': { - 'outerBoundaryIs': makeChildAppender(this.writeRing_), - 'innerBoundaryIs': makeChildAppender(this.writeRing_) - } - }; - - /** - * @type {Object.>} - * @private - */ - this.POINTMEMBER_SERIALIZERS_ = { - 'http://www.opengis.net/gml': { - 'pointMember': makeChildAppender( - this.writePointMember_) - } - }; - - /** - * @const - * @type {Object.>} - * @private - */ - this.SURFACEORPOLYGONMEMBER_SERIALIZERS_ = { - 'http://www.opengis.net/gml': { - 'surfaceMember': makeChildAppender( - this.writeSurfaceOrPolygonMember_), - 'polygonMember': makeChildAppender( - this.writeSurfaceOrPolygonMember_) - } - }; - - /** - * @type {Object.>} - * @private - */ - this.ENVELOPE_SERIALIZERS_ = { - 'http://www.opengis.net/gml': { - 'lowerCorner': makeChildAppender(writeStringTextNode), - 'upperCorner': makeChildAppender(writeStringTextNode) - } - }; } /** @@ -735,4 +585,155 @@ class GML2 extends GMLBase { } } +/** + * @const + * @type {Object.>} + * @private + */ +GML2.prototype.GEOMETRY_FLAT_COORDINATES_PARSERS_ = { + 'http://www.opengis.net/gml': { + 'coordinates': makeReplacer(GML2.prototype.readFlatCoordinates_) + } +}; + +/** + * @const + * @type {Object.>} + * @private + */ +GML2.prototype.FLAT_LINEAR_RINGS_PARSERS_ = { + 'http://www.opengis.net/gml': { + 'innerBoundaryIs': GML2.prototype.innerBoundaryIsParser_, + 'outerBoundaryIs': GML2.prototype.outerBoundaryIsParser_ + } +}; + +/** + * @const + * @type {Object.>} + * @private + */ +GML2.prototype.BOX_PARSERS_ = { + 'http://www.opengis.net/gml': { + 'coordinates': makeArrayPusher( + GML2.prototype.readFlatCoordinates_) + } +}; + +/** + * @const + * @type {Object.>} + * @private + */ +GML2.prototype.GEOMETRY_PARSERS_ = { + 'http://www.opengis.net/gml': { + '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), + 'Box': makeReplacer(GML2.prototype.readBox_) + } +}; + +/** + * @const + * @type {Object.>} + * @private + */ +GML2.prototype.GEOMETRY_SERIALIZERS_ = { + 'http://www.opengis.net/gml': { + 'Curve': makeChildAppender( + GML2.prototype.writeCurveOrLineString_), + 'MultiCurve': makeChildAppender( + GML2.prototype.writeMultiCurveOrLineString_), + 'Point': makeChildAppender(GML2.prototype.writePoint_), + 'MultiPoint': makeChildAppender( + GML2.prototype.writeMultiPoint_), + 'LineString': makeChildAppender( + GML2.prototype.writeCurveOrLineString_), + 'MultiLineString': makeChildAppender( + GML2.prototype.writeMultiCurveOrLineString_), + 'LinearRing': makeChildAppender( + GML2.prototype.writeLinearRing_), + 'Polygon': makeChildAppender( + GML2.prototype.writeSurfaceOrPolygon_), + 'MultiPolygon': makeChildAppender( + GML2.prototype.writeMultiSurfaceOrPolygon_), + 'Surface': makeChildAppender( + GML2.prototype.writeSurfaceOrPolygon_), + 'MultiSurface': makeChildAppender( + GML2.prototype.writeMultiSurfaceOrPolygon_), + 'Envelope': makeChildAppender( + GML2.prototype.writeEnvelope) + } +}; + +/** + * @type {Object.>} + * @private + */ +GML2.prototype.LINESTRINGORCURVEMEMBER_SERIALIZERS_ = { + 'http://www.opengis.net/gml': { + 'lineStringMember': makeChildAppender( + GML2.prototype.writeLineStringOrCurveMember_), + 'curveMember': makeChildAppender( + GML2.prototype.writeLineStringOrCurveMember_) + } +}; + +/** + * @type {Object.>} + * @private + */ +GML2.prototype.RING_SERIALIZERS_ = { + 'http://www.opengis.net/gml': { + 'outerBoundaryIs': makeChildAppender(GML2.prototype.writeRing_), + 'innerBoundaryIs': makeChildAppender(GML2.prototype.writeRing_) + } +}; + +/** + * @type {Object.>} + * @private + */ +GML2.prototype.POINTMEMBER_SERIALIZERS_ = { + 'http://www.opengis.net/gml': { + 'pointMember': makeChildAppender( + GML2.prototype.writePointMember_) + } +}; + +/** + * @const + * @type {Object.>} + * @private + */ +GML2.prototype.SURFACEORPOLYGONMEMBER_SERIALIZERS_ = { + 'http://www.opengis.net/gml': { + 'surfaceMember': makeChildAppender( + GML2.prototype.writeSurfaceOrPolygonMember_), + 'polygonMember': makeChildAppender( + GML2.prototype.writeSurfaceOrPolygonMember_) + } +}; + +/** + * @type {Object.>} + * @private + */ +GML2.prototype.ENVELOPE_SERIALIZERS_ = { + 'http://www.opengis.net/gml': { + 'lowerCorner': makeChildAppender(writeStringTextNode), + 'upperCorner': makeChildAppender(writeStringTextNode) + } +}; + export default GML2; diff --git a/src/ol/format/GML3.js b/src/ol/format/GML3.js index a1f7600233..5fd31cfae0 100644 --- a/src/ol/format/GML3.js +++ b/src/ol/format/GML3.js @@ -99,292 +99,6 @@ class GML3 extends GMLBase { this.hasZ = options.hasZ !== undefined ? options.hasZ : false; - /** - * @const - * @type {Object.>} - * @private - */ - this.GEOMETRY_FLAT_COORDINATES_PARSERS_ = { - 'http://www.opengis.net/gml': { - 'pos': makeReplacer(this.readFlatPos_), - 'posList': makeReplacer(this.readFlatPosList_) - } - }; - - - /** - * @const - * @type {Object.>} - * @private - */ - this.FLAT_LINEAR_RINGS_PARSERS_ = { - 'http://www.opengis.net/gml': { - 'interior': this.interiorParser_, - 'exterior': this.exteriorParser_ - } - }; - - - /** - * @const - * @type {Object.>} - * @private - */ - this.GEOMETRY_PARSERS_ = { - 'http://www.opengis.net/gml': { - 'Point': makeReplacer(this.readPoint), - 'MultiPoint': makeReplacer( - this.readMultiPoint), - 'LineString': makeReplacer( - this.readLineString), - 'MultiLineString': makeReplacer( - this.readMultiLineString), - 'LinearRing': makeReplacer( - this.readLinearRing), - 'Polygon': makeReplacer(this.readPolygon), - 'MultiPolygon': makeReplacer( - this.readMultiPolygon), - 'Surface': makeReplacer(this.readSurface_), - 'MultiSurface': makeReplacer( - this.readMultiSurface_), - 'Curve': makeReplacer(this.readCurve_), - 'MultiCurve': makeReplacer( - this.readMultiCurve_), - 'Envelope': makeReplacer(this.readEnvelope_) - } - }; - - - /** - * @const - * @type {Object.>} - * @private - */ - this.MULTICURVE_PARSERS_ = { - 'http://www.opengis.net/gml': { - 'curveMember': makeArrayPusher( - this.curveMemberParser_), - 'curveMembers': makeArrayPusher( - this.curveMemberParser_) - } - }; - - - /** - * @const - * @type {Object.>} - * @private - */ - this.MULTISURFACE_PARSERS_ = { - 'http://www.opengis.net/gml': { - 'surfaceMember': makeArrayPusher( - this.surfaceMemberParser_), - 'surfaceMembers': makeArrayPusher( - this.surfaceMemberParser_) - } - }; - - - /** - * @const - * @type {Object.>} - * @private - */ - this.CURVEMEMBER_PARSERS_ = { - 'http://www.opengis.net/gml': { - 'LineString': makeArrayPusher( - this.readLineString), - 'Curve': makeArrayPusher(this.readCurve_) - } - }; - - - /** - * @const - * @type {Object.>} - * @private - */ - this.SURFACEMEMBER_PARSERS_ = { - 'http://www.opengis.net/gml': { - 'Polygon': makeArrayPusher(this.readPolygon), - 'Surface': makeArrayPusher(this.readSurface_) - } - }; - - - /** - * @const - * @type {Object.>} - * @private - */ - this.SURFACE_PARSERS_ = { - 'http://www.opengis.net/gml': { - 'patches': makeReplacer(this.readPatch_) - } - }; - - - /** - * @const - * @type {Object.>} - * @private - */ - this.CURVE_PARSERS_ = { - 'http://www.opengis.net/gml': { - 'segments': makeReplacer(this.readSegment_) - } - }; - - - /** - * @const - * @type {Object.>} - * @private - */ - this.ENVELOPE_PARSERS_ = { - 'http://www.opengis.net/gml': { - 'lowerCorner': makeArrayPusher( - this.readFlatPosList_), - 'upperCorner': makeArrayPusher( - this.readFlatPosList_) - } - }; - - - /** - * @const - * @type {Object.>} - * @private - */ - this.PATCHES_PARSERS_ = { - 'http://www.opengis.net/gml': { - 'PolygonPatch': makeReplacer( - this.readPolygonPatch_) - } - }; - - - /** - * @const - * @type {Object.>} - * @private - */ - this.SEGMENTS_PARSERS_ = { - 'http://www.opengis.net/gml': { - 'LineStringSegment': makeReplacer( - this.readLineStringSegment_) - } - }; - - - /** - * Encode an array of features in GML 3.1.1 Simple Features. - * - * @function - * @param {Array.} features Features. - * @param {module:ol/format/Feature~WriteOptions=} opt_options Options. - * @return {string} Result. - * @api - */ - this.writeFeatures; - - - /** - * @type {Object.>} - * @private - */ - this.RING_SERIALIZERS_ = { - 'http://www.opengis.net/gml': { - 'exterior': makeChildAppender(this.writeRing_), - 'interior': makeChildAppender(this.writeRing_) - } - }; - - - /** - * @type {Object.>} - * @private - */ - this.ENVELOPE_SERIALIZERS_ = { - 'http://www.opengis.net/gml': { - 'lowerCorner': makeChildAppender(writeStringTextNode), - 'upperCorner': makeChildAppender(writeStringTextNode) - } - }; - - - /** - * @type {Object.>} - * @private - */ - this.SURFACEORPOLYGONMEMBER_SERIALIZERS_ = { - 'http://www.opengis.net/gml': { - 'surfaceMember': makeChildAppender( - this.writeSurfaceOrPolygonMember_), - 'polygonMember': makeChildAppender( - this.writeSurfaceOrPolygonMember_) - } - }; - - - /** - * @type {Object.>} - * @private - */ - this.POINTMEMBER_SERIALIZERS_ = { - 'http://www.opengis.net/gml': { - 'pointMember': makeChildAppender( - this.writePointMember_) - } - }; - - - /** - * @type {Object.>} - * @private - */ - this.LINESTRINGORCURVEMEMBER_SERIALIZERS_ = { - 'http://www.opengis.net/gml': { - 'lineStringMember': makeChildAppender( - this.writeLineStringOrCurveMember_), - 'curveMember': makeChildAppender( - this.writeLineStringOrCurveMember_) - } - }; - - - /** - * @type {Object.>} - * @private - */ - this.GEOMETRY_SERIALIZERS_ = { - 'http://www.opengis.net/gml': { - 'Curve': makeChildAppender( - this.writeCurveOrLineString_), - 'MultiCurve': makeChildAppender( - this.writeMultiCurveOrLineString_), - 'Point': makeChildAppender(this.writePoint_), - 'MultiPoint': makeChildAppender( - this.writeMultiPoint_), - 'LineString': makeChildAppender( - this.writeCurveOrLineString_), - 'MultiLineString': makeChildAppender( - this.writeMultiCurveOrLineString_), - 'LinearRing': makeChildAppender( - this.writeLinearRing_), - 'Polygon': makeChildAppender( - this.writeSurfaceOrPolygon_), - 'MultiPolygon': makeChildAppender( - this.writeMultiSurfaceOrPolygon_), - 'Surface': makeChildAppender( - this.writeSurfaceOrPolygon_), - 'MultiSurface': makeChildAppender( - this.writeMultiSurfaceOrPolygon_), - 'Envelope': makeChildAppender( - this.writeEnvelope) - } - }; - } /** @@ -1204,4 +918,290 @@ class GML3 extends GMLBase { } } +/** + * @const + * @type {Object.>} + * @private + */ +GML3.prototype.GEOMETRY_FLAT_COORDINATES_PARSERS_ = { + 'http://www.opengis.net/gml': { + 'pos': makeReplacer(GML3.prototype.readFlatPos_), + 'posList': makeReplacer(GML3.prototype.readFlatPosList_) + } +}; + + +/** + * @const + * @type {Object.>} + * @private + */ +GML3.prototype.FLAT_LINEAR_RINGS_PARSERS_ = { + 'http://www.opengis.net/gml': { + 'interior': GML3.prototype.interiorParser_, + 'exterior': GML3.prototype.exteriorParser_ + } +}; + + +/** + * @const + * @type {Object.>} + * @private + */ +GML3.prototype.GEOMETRY_PARSERS_ = { + 'http://www.opengis.net/gml': { + '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(GML3.prototype.readSurface_), + 'MultiSurface': makeReplacer( + GML3.prototype.readMultiSurface_), + 'Curve': makeReplacer(GML3.prototype.readCurve_), + 'MultiCurve': makeReplacer( + GML3.prototype.readMultiCurve_), + 'Envelope': makeReplacer(GML3.prototype.readEnvelope_) + } +}; + + +/** + * @const + * @type {Object.>} + * @private + */ +GML3.prototype.MULTICURVE_PARSERS_ = { + 'http://www.opengis.net/gml': { + 'curveMember': makeArrayPusher( + GML3.prototype.curveMemberParser_), + 'curveMembers': makeArrayPusher( + GML3.prototype.curveMemberParser_) + } +}; + + +/** + * @const + * @type {Object.>} + * @private + */ +GML3.prototype.MULTISURFACE_PARSERS_ = { + 'http://www.opengis.net/gml': { + 'surfaceMember': makeArrayPusher( + GML3.prototype.surfaceMemberParser_), + 'surfaceMembers': makeArrayPusher( + GML3.prototype.surfaceMemberParser_) + } +}; + + +/** + * @const + * @type {Object.>} + * @private + */ +GML3.prototype.CURVEMEMBER_PARSERS_ = { + 'http://www.opengis.net/gml': { + 'LineString': makeArrayPusher( + GMLBase.prototype.readLineString), + 'Curve': makeArrayPusher(GML3.prototype.readCurve_) + } +}; + + +/** + * @const + * @type {Object.>} + * @private + */ +GML3.prototype.SURFACEMEMBER_PARSERS_ = { + 'http://www.opengis.net/gml': { + 'Polygon': makeArrayPusher(GMLBase.prototype.readPolygon), + 'Surface': makeArrayPusher(GML3.prototype.readSurface_) + } +}; + + +/** + * @const + * @type {Object.>} + * @private + */ +GML3.prototype.SURFACE_PARSERS_ = { + 'http://www.opengis.net/gml': { + 'patches': makeReplacer(GML3.prototype.readPatch_) + } +}; + + +/** + * @const + * @type {Object.>} + * @private + */ +GML3.prototype.CURVE_PARSERS_ = { + 'http://www.opengis.net/gml': { + 'segments': makeReplacer(GML3.prototype.readSegment_) + } +}; + + +/** + * @const + * @type {Object.>} + * @private + */ +GML3.prototype.ENVELOPE_PARSERS_ = { + 'http://www.opengis.net/gml': { + 'lowerCorner': makeArrayPusher( + GML3.prototype.readFlatPosList_), + 'upperCorner': makeArrayPusher( + GML3.prototype.readFlatPosList_) + } +}; + + +/** + * @const + * @type {Object.>} + * @private + */ +GML3.prototype.PATCHES_PARSERS_ = { + 'http://www.opengis.net/gml': { + 'PolygonPatch': makeReplacer( + GML3.prototype.readPolygonPatch_) + } +}; + + +/** + * @const + * @type {Object.>} + * @private + */ +GML3.prototype.SEGMENTS_PARSERS_ = { + 'http://www.opengis.net/gml': { + 'LineStringSegment': makeReplacer( + GML3.prototype.readLineStringSegment_) + } +}; + + +/** + * Encode an array of features in GML 3.1.1 Simple Features. + * + * @function + * @param {Array.} features Features. + * @param {module:ol/format/Feature~WriteOptions=} opt_options Options. + * @return {string} Result. + * @api + */ +GML3.prototype.writeFeatures; + + +/** + * @type {Object.>} + * @private + */ +GML3.prototype.RING_SERIALIZERS_ = { + 'http://www.opengis.net/gml': { + 'exterior': makeChildAppender(GML3.prototype.writeRing_), + 'interior': makeChildAppender(GML3.prototype.writeRing_) + } +}; + + +/** + * @type {Object.>} + * @private + */ +GML3.prototype.ENVELOPE_SERIALIZERS_ = { + 'http://www.opengis.net/gml': { + 'lowerCorner': makeChildAppender(writeStringTextNode), + 'upperCorner': makeChildAppender(writeStringTextNode) + } +}; + + +/** + * @type {Object.>} + * @private + */ +GML3.prototype.SURFACEORPOLYGONMEMBER_SERIALIZERS_ = { + 'http://www.opengis.net/gml': { + 'surfaceMember': makeChildAppender( + GML3.prototype.writeSurfaceOrPolygonMember_), + 'polygonMember': makeChildAppender( + GML3.prototype.writeSurfaceOrPolygonMember_) + } +}; + + +/** + * @type {Object.>} + * @private + */ +GML3.prototype.POINTMEMBER_SERIALIZERS_ = { + 'http://www.opengis.net/gml': { + 'pointMember': makeChildAppender( + GML3.prototype.writePointMember_) + } +}; + + +/** + * @type {Object.>} + * @private + */ +GML3.prototype.LINESTRINGORCURVEMEMBER_SERIALIZERS_ = { + 'http://www.opengis.net/gml': { + 'lineStringMember': makeChildAppender( + GML3.prototype.writeLineStringOrCurveMember_), + 'curveMember': makeChildAppender( + GML3.prototype.writeLineStringOrCurveMember_) + } +}; + + +/** + * @type {Object.>} + * @private + */ +GML3.prototype.GEOMETRY_SERIALIZERS_ = { + 'http://www.opengis.net/gml': { + 'Curve': makeChildAppender( + GML3.prototype.writeCurveOrLineString_), + 'MultiCurve': makeChildAppender( + GML3.prototype.writeMultiCurveOrLineString_), + 'Point': makeChildAppender(GML3.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 GML3; diff --git a/src/ol/format/GMLBase.js b/src/ol/format/GMLBase.js index 44f62826e9..2892f23f52 100644 --- a/src/ol/format/GMLBase.js +++ b/src/ol/format/GMLBase.js @@ -127,93 +127,6 @@ class GMLBase extends XMLFeature { 'featureMembers': makeReplacer(this.readFeaturesInternal) }; - /** - * @const - * @type {Object.>} - * @private - */ - this.MULTIPOINT_PARSERS_ = { - 'http://www.opengis.net/gml': { - 'pointMember': makeArrayPusher(this.pointMemberParser_), - 'pointMembers': makeArrayPusher(this.pointMemberParser_) - } - }; - - - /** - * @const - * @type {Object.>} - * @private - */ - this.MULTILINESTRING_PARSERS_ = { - 'http://www.opengis.net/gml': { - 'lineStringMember': makeArrayPusher(this.lineStringMemberParser_), - 'lineStringMembers': makeArrayPusher(this.lineStringMemberParser_) - } - }; - - - /** - * @const - * @type {Object.>} - * @private - */ - this.MULTIPOLYGON_PARSERS_ = { - 'http://www.opengis.net/gml': { - 'polygonMember': makeArrayPusher(this.polygonMemberParser_), - 'polygonMembers': makeArrayPusher(this.polygonMemberParser_) - } - }; - - - /** - * @const - * @type {Object.>} - * @private - */ - this.POINTMEMBER_PARSERS_ = { - 'http://www.opengis.net/gml': { - 'Point': makeArrayPusher(this.readFlatCoordinatesFromNode_) - } - }; - - - /** - * @const - * @type {Object.>} - * @private - */ - this.LINESTRINGMEMBER_PARSERS_ = { - 'http://www.opengis.net/gml': { - 'LineString': makeArrayPusher(this.readLineString) - } - }; - - - /** - * @const - * @type {Object.>} - * @private - */ - this.POLYGONMEMBER_PARSERS_ = { - 'http://www.opengis.net/gml': { - 'Polygon': makeArrayPusher(this.readPolygon) - } - }; - - - /** - * @const - * @type {Object.>} - * @protected - */ - this.RING_PARSERS = { - 'http://www.opengis.net/gml': { - 'LinearRing': makeReplacer(this.readFlatLinearRing_) - } - }; - - } /** @@ -556,4 +469,90 @@ class GMLBase extends XMLFeature { } } +/** + * @const + * @type {Object.>} + * @private + */ +GMLBase.prototype.MULTIPOINT_PARSERS_ = { + 'http://www.opengis.net/gml': { + 'pointMember': makeArrayPusher(GMLBase.prototype.pointMemberParser_), + 'pointMembers': makeArrayPusher(GMLBase.prototype.pointMemberParser_) + } +}; + + +/** + * @const + * @type {Object.>} + * @private + */ +GMLBase.prototype.MULTILINESTRING_PARSERS_ = { + 'http://www.opengis.net/gml': { + 'lineStringMember': makeArrayPusher(GMLBase.prototype.lineStringMemberParser_), + 'lineStringMembers': makeArrayPusher(GMLBase.prototype.lineStringMemberParser_) + } +}; + + +/** + * @const + * @type {Object.>} + * @private + */ +GMLBase.prototype.MULTIPOLYGON_PARSERS_ = { + 'http://www.opengis.net/gml': { + 'polygonMember': makeArrayPusher(GMLBase.prototype.polygonMemberParser_), + 'polygonMembers': makeArrayPusher(GMLBase.prototype.polygonMemberParser_) + } +}; + + +/** + * @const + * @type {Object.>} + * @private + */ +GMLBase.prototype.POINTMEMBER_PARSERS_ = { + 'http://www.opengis.net/gml': { + 'Point': makeArrayPusher(GMLBase.prototype.readFlatCoordinatesFromNode_) + } +}; + + +/** + * @const + * @type {Object.>} + * @private + */ +GMLBase.prototype.LINESTRINGMEMBER_PARSERS_ = { + 'http://www.opengis.net/gml': { + 'LineString': makeArrayPusher(GMLBase.prototype.readLineString) + } +}; + + +/** + * @const + * @type {Object.>} + * @private + */ +GMLBase.prototype.POLYGONMEMBER_PARSERS_ = { + 'http://www.opengis.net/gml': { + 'Polygon': makeArrayPusher(GMLBase.prototype.readPolygon) + } +}; + + +/** + * @const + * @type {Object.>} + * @protected + */ +GMLBase.prototype.RING_PARSERS = { + 'http://www.opengis.net/gml': { + 'LinearRing': makeReplacer(GMLBase.prototype.readFlatLinearRing_) + } +}; + export default GMLBase; diff --git a/src/ol/format/WFS.js b/src/ol/format/WFS.js index 2fbcbc7911..7354da25d6 100644 --- a/src/ol/format/WFS.js +++ b/src/ol/format/WFS.js @@ -1,7 +1,6 @@ /** * @module ol/format/WFS */ -import {inherits} from '../util.js'; import {assert} from '../asserts.js'; import GML2 from '../format/GML2.js'; import GML3 from '../format/GML3.js'; @@ -201,15 +200,16 @@ const DEFAULT_VERSION = '1.1.0'; * as option if you want to read a WFS that contains GML2 (WFS 1.0.0). * Also see {@link module:ol/format/GMLBase~GMLBase} which is used by this format. * - * @extends {module:ol/format/XMLFeature} * @api */ -class WFS { +class WFS extends XMLFeature { /** * @param {module:ol/format/WFS~Options=} opt_options Optional configuration object. */ constructor(opt_options) { + super(); + const options = opt_options ? opt_options : {}; /** @@ -237,8 +237,6 @@ class WFS { */ this.schemaLocation_ = options.schemaLocation ? options.schemaLocation : SCHEMA_LOCATIONS[DEFAULT_VERSION]; - - XMLFeature.call(this); } /** @@ -537,20 +535,6 @@ class WFS { } } -inherits(WFS, XMLFeature); - - -/** - * Read all features from a WFS FeatureCollection. - * - * @function - * @param {Document|Node|Object|string} source Source. - * @param {module:ol/format/Feature~ReadOptions=} opt_options Read options. - * @return {Array.} Features. - * @api - */ -WFS.prototype.readFeatures; - /** * @param {Node} node Node. @@ -1100,15 +1084,4 @@ function writeGetFeature(node, featureTypes, objectStack) { } -/** - * Read the projection from a WFS source. - * - * @function - * @param {Document|Node|Object|string} source Source. - * @return {?module:ol/proj/Projection} Projection. - * @api - */ -WFS.prototype.readProjection; - - export default WFS; From df89b0718aafd78093356a289aa9159666d3a478 Mon Sep 17 00:00:00 2001 From: ahocevar Date: Tue, 17 Jul 2018 17:56:30 +0200 Subject: [PATCH 062/107] Use extends and super for format/WKT --- src/ol/format/TextFeature.js | 17 ++++++-- src/ol/format/WKT.js | 80 +----------------------------------- 2 files changed, 16 insertions(+), 81 deletions(-) diff --git a/src/ol/format/TextFeature.js b/src/ol/format/TextFeature.js index a1bb7c965c..0f866bdfd6 100644 --- a/src/ol/format/TextFeature.js +++ b/src/ol/format/TextFeature.js @@ -109,7 +109,13 @@ class TextFeature extends FeatureFormat { } /** - * @inheritDoc + * Encode a feature as a string. + * + * @function + * @param {module:ol/Feature} feature Feature. + * @param {module:ol/format/Feature~WriteOptions=} opt_options Write options. + * @return {string} Encoded feature. + * @api */ writeFeature(feature, opt_options) { return this.writeFeatureText(feature, this.adaptOptions(opt_options)); @@ -125,7 +131,12 @@ class TextFeature extends FeatureFormat { writeFeatureText(feature, opt_options) {} /** - * @inheritDoc + * Encode an array of features as string. + * + * @param {Array.} features Features. + * @param {module:ol/format/Feature~WriteOptions=} opt_options Write options. + * @return {string} Encoded features. + * @api */ writeFeatures(features, opt_options) { return this.writeFeaturesText(features, this.adaptOptions(opt_options)); @@ -141,7 +152,7 @@ class TextFeature extends FeatureFormat { writeFeaturesText(features, opt_options) {} /** - * Write a single geometry in Polyline format. + * Write a single geometry. * * @function * @param {module:ol/geom/Geometry} geometry Geometry. diff --git a/src/ol/format/WKT.js b/src/ol/format/WKT.js index 4a73ae6da7..463ec6ee2d 100644 --- a/src/ol/format/WKT.js +++ b/src/ol/format/WKT.js @@ -1,7 +1,6 @@ /** * @module ol/format/WKT */ -import {inherits} from '../util.js'; import Feature from '../Feature.js'; import {transformWithOptions} from '../format/Feature.js'; import TextFeature from '../format/TextFeature.js'; @@ -594,19 +593,18 @@ class Parser { * Geometry format for reading and writing data in the `WellKnownText` (WKT) * format. * - * @extends {module:ol/format/TextFeature} * @api */ -class WKT { +class WKT extends TextFeature { /** * @param {module:ol/format/WKT~Options=} opt_options Options. */ constructor(opt_options) { + super(); const options = opt_options ? opt_options : {}; - TextFeature.call(this); /** * Split GeometryCollection into multiple features. @@ -715,8 +713,6 @@ class WKT { } } -inherits(WKT, TextFeature); - /** * @param {module:ol/geom/Point} geom Point geometry. @@ -869,76 +865,4 @@ function encode(geom) { } -/** - * Read a feature from a WKT source. - * - * @function - * @param {Document|Node|Object|string} source Source. - * @param {module:ol/format/Feature~ReadOptions=} opt_options Read options. - * @return {module:ol/Feature} Feature. - * @api - */ -WKT.prototype.readFeature; - - -/** - * Read all features from a WKT source. - * - * @function - * @param {Document|Node|Object|string} source Source. - * @param {module:ol/format/Feature~ReadOptions=} opt_options Read options. - * @return {Array.} Features. - * @api - */ -WKT.prototype.readFeatures; - - -/** - * Read a single geometry from a WKT source. - * - * @function - * @param {Document|Node|Object|string} source Source. - * @param {module:ol/format/Feature~ReadOptions=} opt_options Read options. - * @return {module:ol/geom/Geometry} Geometry. - * @api - */ -WKT.prototype.readGeometry; - - -/** - * Encode a feature as a WKT string. - * - * @function - * @param {module:ol/Feature} feature Feature. - * @param {module:ol/format/Feature~WriteOptions=} opt_options Write options. - * @return {string} WKT string. - * @api - */ -WKT.prototype.writeFeature; - - -/** - * Encode an array of features as a WKT string. - * - * @function - * @param {Array.} features Features. - * @param {module:ol/format/Feature~WriteOptions=} opt_options Write options. - * @return {string} WKT string. - * @api - */ -WKT.prototype.writeFeatures; - - -/** - * Write a single geometry as a WKT string. - * - * @function - * @param {module:ol/geom/Geometry} geometry Geometry. - * @param {module:ol/format/Feature~WriteOptions=} opt_options Write options. - * @return {string} WKT string. - * @api - */ -WKT.prototype.writeGeometry; - - export default WKT; From a4abd990df6b744079261ca99cfc8e7312760307 Mon Sep 17 00:00:00 2001 From: ahocevar Date: Tue, 17 Jul 2018 17:59:53 +0200 Subject: [PATCH 063/107] Use extends and super for format/WMSCapabilities --- src/ol/format/WMSCapabilities.js | 20 ++------------------ src/ol/format/XML.js | 7 +++++-- 2 files changed, 7 insertions(+), 20 deletions(-) diff --git a/src/ol/format/WMSCapabilities.js b/src/ol/format/WMSCapabilities.js index 5e49906fae..1cb7a19c3b 100644 --- a/src/ol/format/WMSCapabilities.js +++ b/src/ol/format/WMSCapabilities.js @@ -1,7 +1,6 @@ /** * @module ol/format/WMSCapabilities */ -import {inherits} from '../util.js'; import {readHref} from '../format/XLink.js'; import XML from '../format/XML.js'; import {readDecimalString, readString, readNonNegativeInteger, readDecimal, readBooleanString, readNonNegativeIntegerString} from '../format/xsd.js'; @@ -46,13 +45,11 @@ const CAPABILITY_PARSERS = makeStructureNS( * @classdesc * Format for reading WMS capabilities data * - * @extends {module:ol/format/XML} * @api */ -class WMSCapabilities { +class WMSCapabilities extends XML { constructor() { - - XML.call(this); + super(); /** * @type {string|undefined} @@ -84,8 +81,6 @@ class WMSCapabilities { } } -inherits(WMSCapabilities, XML); - /** * @const @@ -290,17 +285,6 @@ const KEYWORDLIST_PARSERS = makeStructureNS( }); -/** - * Read a WMS capabilities document. - * - * @function - * @param {Document|Node|string} source The XML source. - * @return {Object} An object representing the WMS capabilities. - * @api - */ -WMSCapabilities.prototype.read; - - /** * @param {Node} node Node. * @param {Array.<*>} objectStack Object stack. diff --git a/src/ol/format/XML.js b/src/ol/format/XML.js index d210640b32..a61f580e11 100644 --- a/src/ol/format/XML.js +++ b/src/ol/format/XML.js @@ -11,8 +11,11 @@ import {isDocument, isNode, parse} from '../xml.js'; */ class XML { /** - * @param {Document|Node|string} source Source. - * @return {Object} The parsed result. + * Read the source document. + * + * @param {Document|Node|string} source The XML source. + * @return {Object} An object representing the source. + * @api */ read(source) { if (isDocument(source)) { From ba431421a1802ce5e46031fffe4b1cae6674cce7 Mon Sep 17 00:00:00 2001 From: ahocevar Date: Tue, 17 Jul 2018 18:01:07 +0200 Subject: [PATCH 064/107] Use extends and super for format/WMSGetFeatureInfo --- src/ol/format/WMSGetFeatureInfo.js | 21 ++------------------- 1 file changed, 2 insertions(+), 19 deletions(-) diff --git a/src/ol/format/WMSGetFeatureInfo.js b/src/ol/format/WMSGetFeatureInfo.js index e4aca1f103..9fb91b66f2 100644 --- a/src/ol/format/WMSGetFeatureInfo.js +++ b/src/ol/format/WMSGetFeatureInfo.js @@ -1,7 +1,6 @@ /** * @module ol/format/WMSGetFeatureInfo */ -import {inherits} from '../util.js'; import {extend, includes} from '../array.js'; import GML2 from '../format/GML2.js'; import XMLFeature from '../format/XMLFeature.js'; @@ -34,15 +33,15 @@ const layerIdentifier = '_layer'; * Format for reading WMSGetFeatureInfo format. It uses * {@link module:ol/format/GML2~GML2} to read features. * - * @extends {module:ol/format/XMLFeature} * @api */ -class WMSGetFeatureInfo { +class WMSGetFeatureInfo extends XMLFeature { /** * @param {module:ol/format/WMSGetFeatureInfo~Options=} opt_options Options. */ constructor(opt_options) { + super(); const options = opt_options ? opt_options : {}; @@ -65,8 +64,6 @@ class WMSGetFeatureInfo { * @type {Array.} */ this.layers_ = options.layers ? options.layers : null; - - XMLFeature.call(this); } /** @@ -172,19 +169,5 @@ class WMSGetFeatureInfo { writeGeometryNode(geometry, opt_options) {} } -inherits(WMSGetFeatureInfo, XMLFeature); - - -/** - * Read all features from a WMSGetFeatureInfo response. - * - * @function - * @param {Document|Node|Object|string} source Source. - * @param {module:ol/format/Feature~ReadOptions=} opt_options Options. - * @return {Array.} Features. - * @api - */ -WMSGetFeatureInfo.prototype.readFeatures; - export default WMSGetFeatureInfo; From a02c1713a4fb2cb086fa1e8244c80395e7205816 Mon Sep 17 00:00:00 2001 From: ahocevar Date: Tue, 17 Jul 2018 18:02:01 +0200 Subject: [PATCH 065/107] Use extends and super for format/WMTSCapabilities --- src/ol/format/WMTSCapabilities.js | 21 +++------------------ 1 file changed, 3 insertions(+), 18 deletions(-) diff --git a/src/ol/format/WMTSCapabilities.js b/src/ol/format/WMTSCapabilities.js index a9b03e95f4..f42a7587dc 100644 --- a/src/ol/format/WMTSCapabilities.js +++ b/src/ol/format/WMTSCapabilities.js @@ -1,7 +1,6 @@ /** * @module ol/format/WMTSCapabilities */ -import {inherits} from '../util.js'; import {boundingExtent} from '../extent.js'; import OWS from '../format/OWS.js'; import {readHref} from '../format/XLink.js'; @@ -45,12 +44,11 @@ const PARSERS = makeStructureNS( * @classdesc * Format for reading WMTS capabilities data. * - * @extends {module:ol/format/XML} - * @api + * @api */ -class WMTSCapabilities { +class WMTSCapabilities extends XML { constructor() { - XML.call(this); + super(); /** * @type {module:ol/format/OWS} @@ -86,8 +84,6 @@ class WMTSCapabilities { } } -inherits(WMTSCapabilities, XML); - /** * @const @@ -221,17 +217,6 @@ const TM_PARSERS = makeStructureNS( })); -/** - * Read a WMTS capabilities document. - * - * @function - * @param {Document|Node|string} source The XML source. - * @return {Object} An object representing the WMTS capabilities. - * @api - */ -WMTSCapabilities.prototype.read; - - /** * @param {Node} node Node. * @param {Array.<*>} objectStack Object stack. From 414d1556a7d4b9f627ff5cddb7f2191d7244195b Mon Sep 17 00:00:00 2001 From: ahocevar Date: Tue, 17 Jul 2018 18:03:21 +0200 Subject: [PATCH 066/107] Use extends and super for format/XMLFeature --- src/ol/format/XMLFeature.js | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/src/ol/format/XMLFeature.js b/src/ol/format/XMLFeature.js index db23e96d0f..38f543698b 100644 --- a/src/ol/format/XMLFeature.js +++ b/src/ol/format/XMLFeature.js @@ -1,7 +1,6 @@ /** * @module ol/format/XMLFeature */ -import {inherits} from '../util.js'; import {extend} from '../array.js'; import FeatureFormat from '../format/Feature.js'; import FormatType from '../format/FormatType.js'; @@ -14,18 +13,16 @@ import {isDocument, isNode, parse} from '../xml.js'; * Base class for XML feature formats. * * @abstract - * @extends {module:ol/format/Feature} */ -class XMLFeature { +class XMLFeature extends FeatureFormat { constructor() { + super(); /** * @type {XMLSerializer} * @private */ this.xmlSerializer_ = new XMLSerializer(); - - FeatureFormat.call(this); } /** @@ -261,7 +258,5 @@ class XMLFeature { } } -inherits(XMLFeature, FeatureFormat); - export default XMLFeature; From fd962caa1c40f015690ecefeb029c7f250adf654 Mon Sep 17 00:00:00 2001 From: ahocevar Date: Tue, 17 Jul 2018 18:36:26 +0200 Subject: [PATCH 067/107] Use extends, super and proper constructor comments for format/filter/ --- src/ol/format/filter/And.js | 13 +++----- src/ol/format/filter/Bbox.js | 19 +++++------ src/ol/format/filter/Comparison.js | 15 ++++----- src/ol/format/filter/ComparisonBinary.js | 19 +++++------ src/ol/format/filter/Contains.js | 22 ++++++------- src/ol/format/filter/During.js | 20 +++++------- src/ol/format/filter/EqualTo.js | 20 +++++------- src/ol/format/filter/Filter.js | 5 +-- src/ol/format/filter/GreaterThan.js | 18 +++++------ src/ol/format/filter/GreaterThanOrEqualTo.js | 18 +++++------ src/ol/format/filter/Intersects.js | 22 +++++-------- src/ol/format/filter/IsBetween.js | 20 +++++------- src/ol/format/filter/IsLike.js | 33 +++++++++----------- src/ol/format/filter/IsNull.js | 17 ++++------ src/ol/format/filter/LessThan.js | 18 +++++------ src/ol/format/filter/LessThanOrEqualTo.js | 18 +++++------ src/ol/format/filter/LogicalNary.js | 15 ++++----- src/ol/format/filter/Not.js | 15 ++++----- src/ol/format/filter/NotEqualTo.js | 20 +++++------- src/ol/format/filter/Or.js | 16 ++++------ src/ol/format/filter/Spatial.js | 21 ++++++------- 21 files changed, 155 insertions(+), 229 deletions(-) diff --git a/src/ol/format/filter/And.js b/src/ol/format/filter/And.js index f1d9426094..c1a662a003 100644 --- a/src/ol/format/filter/And.js +++ b/src/ol/format/filter/And.js @@ -1,27 +1,24 @@ /** * @module ol/format/filter/And */ -import {inherits} from '../../util.js'; import LogicalNary from '../filter/LogicalNary.js'; /** * @classdesc * Represents a logical `` operator between two or more filter conditions. * - * @constructor * @abstract - * @param {...module:ol/format/filter/Filter} conditions Conditions. - * @extends {module:ol/format/filter/LogicalNary} */ -class And { +class And extends LogicalNary { + /** + * @param {...module:ol/format/filter/Filter} conditions Conditions. + */ constructor(conditions) { const params = ['And'].concat(Array.prototype.slice.call(arguments)); - LogicalNary.apply(this, params); + super(...params); } } -inherits(And, LogicalNary); - export default And; diff --git a/src/ol/format/filter/Bbox.js b/src/ol/format/filter/Bbox.js index 1e747c5c28..9d849ad25b 100644 --- a/src/ol/format/filter/Bbox.js +++ b/src/ol/format/filter/Bbox.js @@ -1,7 +1,6 @@ /** * @module ol/format/filter/Bbox */ -import {inherits} from '../../util.js'; import Filter from '../filter/Filter.js'; /** @@ -9,19 +8,19 @@ import Filter from '../filter/Filter.js'; * Represents a `` operator to test whether a geometry-valued property * intersects a fixed bounding box * - * @constructor - * @param {!string} geometryName Geometry name to use. - * @param {!module:ol/extent~Extent} extent Extent. - * @param {string=} opt_srsName SRS name. No srsName attribute will be - * set on geometries when this is not provided. - * @extends {module:ol/format/filter/Filter} * @api */ -class Bbox { +class Bbox extends Filter { + /** + * @param {!string} geometryName Geometry name to use. + * @param {!module:ol/extent~Extent} extent Extent. + * @param {string=} opt_srsName SRS name. No srsName attribute will be set + * on geometries when this is not provided. + */ constructor(geometryName, extent, opt_srsName) { - Filter.call(this, 'BBOX'); + super('BBOX'); /** * @type {!string} @@ -41,6 +40,4 @@ class Bbox { } -inherits(Bbox, Filter); - export default Bbox; diff --git a/src/ol/format/filter/Comparison.js b/src/ol/format/filter/Comparison.js index 1d781511a9..138f490459 100644 --- a/src/ol/format/filter/Comparison.js +++ b/src/ol/format/filter/Comparison.js @@ -1,7 +1,6 @@ /** * @module ol/format/filter/Comparison */ -import {inherits} from '../../util.js'; import Filter from '../filter/Filter.js'; /** @@ -9,17 +8,17 @@ import Filter from '../filter/Filter.js'; * Abstract class; normally only used for creating subclasses and not instantiated in apps. * Base class for WFS GetFeature property comparison filters. * - * @constructor * @abstract - * @param {!string} tagName The XML tag name for this filter. - * @param {!string} propertyName Name of the context property to compare. - * @extends {module:ol/format/filter/Filter} */ -class Comparison { +class Comparison extends Filter { + /** + * @param {!string} tagName The XML tag name for this filter. + * @param {!string} propertyName Name of the context property to compare. + */ constructor(tagName, propertyName) { - Filter.call(this, tagName); + super(tagName); /** * @type {!string} @@ -29,6 +28,4 @@ class Comparison { } -inherits(Comparison, Filter); - export default Comparison; diff --git a/src/ol/format/filter/ComparisonBinary.js b/src/ol/format/filter/ComparisonBinary.js index 8adeb7303c..d4d5d4f1b2 100644 --- a/src/ol/format/filter/ComparisonBinary.js +++ b/src/ol/format/filter/ComparisonBinary.js @@ -1,7 +1,6 @@ /** * @module ol/format/filter/ComparisonBinary */ -import {inherits} from '../../util.js'; import Comparison from '../filter/Comparison.js'; /** @@ -9,19 +8,19 @@ import Comparison from '../filter/Comparison.js'; * Abstract class; normally only used for creating subclasses and not instantiated in apps. * Base class for WFS GetFeature property binary comparison filters. * - * @constructor * @abstract - * @param {!string} tagName The XML tag name for this filter. - * @param {!string} propertyName Name of the context property to compare. - * @param {!(string|number)} expression The value to compare. - * @param {boolean=} opt_matchCase Case-sensitive? - * @extends {module:ol/format/filter/Comparison} */ -class ComparisonBinary { +class ComparisonBinary extends Comparison { + /** + * @param {!string} tagName The XML tag name for this filter. + * @param {!string} propertyName Name of the context property to compare. + * @param {!(string|number)} expression The value to compare. + * @param {boolean=} opt_matchCase Case-sensitive? + */ constructor(tagName, propertyName, expression, opt_matchCase) { - Comparison.call(this, tagName, propertyName); + super(tagName, propertyName); /** * @type {!(string|number)} @@ -36,6 +35,4 @@ class ComparisonBinary { } -inherits(ComparisonBinary, Comparison); - export default ComparisonBinary; diff --git a/src/ol/format/filter/Contains.js b/src/ol/format/filter/Contains.js index 4de230a1ab..4bea87e530 100644 --- a/src/ol/format/filter/Contains.js +++ b/src/ol/format/filter/Contains.js @@ -1,32 +1,28 @@ /** * @module ol/format/filter/Contains */ -import {inherits} from '../../util.js'; import Spatial from '../filter/Spatial.js'; /** * @classdesc * Represents a `` operator to test whether a geometry-valued property * contains a given geometry. - * - * @constructor - * @param {!string} geometryName Geometry name to use. - * @param {!module:ol/geom/Geometry} geometry Geometry. - * @param {string=} opt_srsName SRS name. No srsName attribute will be - * set on geometries when this is not provided. - * @extends {module:ol/format/filter/Spatial} - * @api */ -class Contains { +class Contains extends Spatial { + /** + * @param {!string} geometryName Geometry name to use. + * @param {!module:ol/geom/Geometry} geometry Geometry. + * @param {string=} opt_srsName SRS name. No srsName attribute will be + * set on geometries when this is not provided. + * @api + */ constructor(geometryName, geometry, opt_srsName) { - Spatial.call(this, 'Contains', geometryName, geometry, opt_srsName); + super('Contains', geometryName, geometry, opt_srsName); } } -inherits(Contains, Spatial); - export default Contains; diff --git a/src/ol/format/filter/During.js b/src/ol/format/filter/During.js index ca7f848afc..45f4ade49d 100644 --- a/src/ol/format/filter/During.js +++ b/src/ol/format/filter/During.js @@ -1,24 +1,22 @@ /** * @module ol/format/filter/During */ -import {inherits} from '../../util.js'; import Comparison from '../filter/Comparison.js'; /** * @classdesc * Represents a `` comparison operator. - * - * @constructor - * @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. - * @extends {module:ol/format/filter/Comparison} - * @api */ -class During { +class During extends Comparison { + /** + * @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. + * @api + */ constructor(propertyName, begin, end) { - Comparison.call(this, 'During', propertyName); + super('During', propertyName); /** * @type {!string} @@ -33,6 +31,4 @@ class During { } -inherits(During, Comparison); - export default During; diff --git a/src/ol/format/filter/EqualTo.js b/src/ol/format/filter/EqualTo.js index a9eedf6c16..253a5add6c 100644 --- a/src/ol/format/filter/EqualTo.js +++ b/src/ol/format/filter/EqualTo.js @@ -1,28 +1,24 @@ /** * @module ol/format/filter/EqualTo */ -import {inherits} from '../../util.js'; import ComparisonBinary from '../filter/ComparisonBinary.js'; /** * @classdesc * Represents a `` comparison operator. - * - * @constructor - * @param {!string} propertyName Name of the context property to compare. - * @param {!(string|number)} expression The value to compare. - * @param {boolean=} opt_matchCase Case-sensitive? - * @extends {module:ol/format/filter/ComparisonBinary} - * @api */ -class EqualTo { +class EqualTo extends ComparisonBinary { + /** + * @param {!string} propertyName Name of the context property to compare. + * @param {!(string|number)} expression The value to compare. + * @param {boolean=} opt_matchCase Case-sensitive? + * @api + */ constructor(propertyName, expression, opt_matchCase) { - ComparisonBinary.call(this, 'PropertyIsEqualTo', propertyName, expression, opt_matchCase); + super('PropertyIsEqualTo', propertyName, expression, opt_matchCase); } } -inherits(EqualTo, ComparisonBinary); - export default EqualTo; diff --git a/src/ol/format/filter/Filter.js b/src/ol/format/filter/Filter.js index 5be0df9c27..8f5a7265fa 100644 --- a/src/ol/format/filter/Filter.js +++ b/src/ol/format/filter/Filter.js @@ -8,12 +8,13 @@ * Abstract class; normally only used for creating subclasses and not instantiated in apps. * Base class for WFS GetFeature filters. * - * @constructor * @abstract - * @param {!string} tagName The XML tag name for this filter. * @struct */ class Filter { + /** + * @param {!string} tagName The XML tag name for this filter. + */ constructor(tagName) { /** diff --git a/src/ol/format/filter/GreaterThan.js b/src/ol/format/filter/GreaterThan.js index a19804a00c..e2ab526557 100644 --- a/src/ol/format/filter/GreaterThan.js +++ b/src/ol/format/filter/GreaterThan.js @@ -1,27 +1,23 @@ /** * @module ol/format/filter/GreaterThan */ -import {inherits} from '../../util.js'; import ComparisonBinary from '../filter/ComparisonBinary.js'; /** * @classdesc * Represents a `` comparison operator. - * - * @constructor - * @param {!string} propertyName Name of the context property to compare. - * @param {!number} expression The value to compare. - * @extends {module:ol/format/filter/ComparisonBinary} - * @api */ -class GreaterThan { +class GreaterThan extends ComparisonBinary { + /** + * @param {!string} propertyName Name of the context property to compare. + * @param {!number} expression The value to compare. + * @api + */ constructor(propertyName, expression) { - ComparisonBinary.call(this, 'PropertyIsGreaterThan', propertyName, expression); + super('PropertyIsGreaterThan', propertyName, expression); } } -inherits(GreaterThan, ComparisonBinary); - export default GreaterThan; diff --git a/src/ol/format/filter/GreaterThanOrEqualTo.js b/src/ol/format/filter/GreaterThanOrEqualTo.js index a55cc09d62..dac7068184 100644 --- a/src/ol/format/filter/GreaterThanOrEqualTo.js +++ b/src/ol/format/filter/GreaterThanOrEqualTo.js @@ -1,27 +1,23 @@ /** * @module ol/format/filter/GreaterThanOrEqualTo */ -import {inherits} from '../../util.js'; import ComparisonBinary from '../filter/ComparisonBinary.js'; /** * @classdesc * Represents a `` comparison operator. - * - * @constructor - * @param {!string} propertyName Name of the context property to compare. - * @param {!number} expression The value to compare. - * @extends {module:ol/format/filter/ComparisonBinary} - * @api */ -class GreaterThanOrEqualTo { +class GreaterThanOrEqualTo extends ComparisonBinary { + /** + * @param {!string} propertyName Name of the context property to compare. + * @param {!number} expression The value to compare. + * @api + */ constructor(propertyName, expression) { - ComparisonBinary.call(this, 'PropertyIsGreaterThanOrEqualTo', propertyName, expression); + super('PropertyIsGreaterThanOrEqualTo', propertyName, expression); } } -inherits(GreaterThanOrEqualTo, ComparisonBinary); - export default GreaterThanOrEqualTo; diff --git a/src/ol/format/filter/Intersects.js b/src/ol/format/filter/Intersects.js index b64696c96d..94f0117bb5 100644 --- a/src/ol/format/filter/Intersects.js +++ b/src/ol/format/filter/Intersects.js @@ -1,31 +1,25 @@ /** * @module ol/format/filter/Intersects */ -import {inherits} from '../../util.js'; import Spatial from '../filter/Spatial.js'; /** * @classdesc * Represents a `` operator to test whether a geometry-valued property * intersects a given geometry. - * - * @constructor - * @param {!string} geometryName Geometry name to use. - * @param {!module:ol/geom/Geometry} geometry Geometry. - * @param {string=} opt_srsName SRS name. No srsName attribute will be - * set on geometries when this is not provided. - * @extends {module:ol/format/filter/Spatial} - * @api */ -class Intersects { +class Intersects extends Spatial { + /** + * @param {!string} geometryName Geometry name to use. + * @param {!module:ol/geom/Geometry} geometry Geometry. + * @param {string=} opt_srsName SRS name. No srsName attribute will be + * set on geometries when this is not provided. + */ constructor(geometryName, geometry, opt_srsName) { - - Spatial.call(this, 'Intersects', geometryName, geometry, opt_srsName); + super('Intersects', geometryName, geometry, opt_srsName); } } -inherits(Intersects, Spatial); - export default Intersects; diff --git a/src/ol/format/filter/IsBetween.js b/src/ol/format/filter/IsBetween.js index 3134f36059..899836fc62 100644 --- a/src/ol/format/filter/IsBetween.js +++ b/src/ol/format/filter/IsBetween.js @@ -1,24 +1,22 @@ /** * @module ol/format/filter/IsBetween */ -import {inherits} from '../../util.js'; import Comparison from '../filter/Comparison.js'; /** * @classdesc * Represents a `` comparison operator. - * - * @constructor - * @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. - * @extends {module:ol/format/filter/Comparison} - * @api */ -class IsBetween { +class IsBetween extends Comparison { + /** + * @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. + * @api + */ constructor(propertyName, lowerBoundary, upperBoundary) { - Comparison.call(this, 'PropertyIsBetween', propertyName); + super('PropertyIsBetween', propertyName); /** * @type {!number} @@ -33,6 +31,4 @@ class IsBetween { } } -inherits(IsBetween, Comparison); - export default IsBetween; diff --git a/src/ol/format/filter/IsLike.js b/src/ol/format/filter/IsLike.js index fbf07e8b08..c663446016 100644 --- a/src/ol/format/filter/IsLike.js +++ b/src/ol/format/filter/IsLike.js @@ -1,30 +1,29 @@ /** * @module ol/format/filter/IsLike */ -import {inherits} from '../../util.js'; import Comparison from '../filter/Comparison.js'; /** * @classdesc * Represents a `` comparison operator. - * - * @constructor - * @param {!string} propertyName Name of the context property to compare. - * @param {!string} pattern Text pattern. - * @param {string=} opt_wildCard Pattern character which matches any sequence of - * zero or more string characters. Default is '*'. - * @param {string=} opt_singleChar pattern character which matches any single - * string character. Default is '.'. - * @param {string=} opt_escapeChar Escape character which can be used to escape - * the pattern characters. Default is '!'. - * @param {boolean=} opt_matchCase Case-sensitive? - * @extends {module:ol/format/filter/Comparison} - * @api */ -class IsLike { +class IsLike extends Comparison { + /** + * [constructor description] + * @param {!string} propertyName Name of the context property to compare. + * @param {!string} pattern Text pattern. + * @param {string=} opt_wildCard Pattern character which matches any sequence of + * zero or more string characters. Default is '*'. + * @param {string=} opt_singleChar pattern character which matches any single + * string character. Default is '.'. + * @param {string=} opt_escapeChar Escape character which can be used to escape + * the pattern characters. Default is '!'. + * @param {boolean=} opt_matchCase Case-sensitive? + * @api + */ constructor(propertyName, pattern, opt_wildCard, opt_singleChar, opt_escapeChar, opt_matchCase) { - Comparison.call(this, 'PropertyIsLike', propertyName); + super('PropertyIsLike', propertyName); /** * @type {!string} @@ -54,6 +53,4 @@ class IsLike { } } -inherits(IsLike, Comparison); - export default IsLike; diff --git a/src/ol/format/filter/IsNull.js b/src/ol/format/filter/IsNull.js index 4daf9a7bc9..17fa0fa901 100644 --- a/src/ol/format/filter/IsNull.js +++ b/src/ol/format/filter/IsNull.js @@ -1,27 +1,22 @@ /** * @module ol/format/filter/IsNull */ -import {inherits} from '../../util.js'; import Comparison from '../filter/Comparison.js'; /** * @classdesc * Represents a `` comparison operator. - * - * @constructor - * @param {!string} propertyName Name of the context property to compare. - * @extends {module:ol/format/filter/Comparison} - * @api */ -class IsNull { +class IsNull extends Comparison { + /** + * @param {!string} propertyName Name of the context property to compare. + * @api + */ constructor(propertyName) { - Comparison.call(this, 'PropertyIsNull', propertyName); + super('PropertyIsNull', propertyName); } } - -inherits(IsNull, Comparison); - export default IsNull; diff --git a/src/ol/format/filter/LessThan.js b/src/ol/format/filter/LessThan.js index 85d241bd9f..82917eb2d0 100644 --- a/src/ol/format/filter/LessThan.js +++ b/src/ol/format/filter/LessThan.js @@ -1,27 +1,23 @@ /** * @module ol/format/filter/LessThan */ -import {inherits} from '../../util.js'; import ComparisonBinary from '../filter/ComparisonBinary.js'; /** * @classdesc * Represents a `` comparison operator. - * - * @constructor - * @param {!string} propertyName Name of the context property to compare. - * @param {!number} expression The value to compare. - * @extends {module:ol/format/filter/ComparisonBinary} - * @api */ -class LessThan { +class LessThan extends ComparisonBinary { + /** + * @param {!string} propertyName Name of the context property to compare. + * @param {!number} expression The value to compare. + * @api + */ constructor(propertyName, expression) { - ComparisonBinary.call(this, 'PropertyIsLessThan', propertyName, expression); + super('PropertyIsLessThan', propertyName, expression); } } -inherits(LessThan, ComparisonBinary); - export default LessThan; diff --git a/src/ol/format/filter/LessThanOrEqualTo.js b/src/ol/format/filter/LessThanOrEqualTo.js index ef89c408a5..46c4f09a11 100644 --- a/src/ol/format/filter/LessThanOrEqualTo.js +++ b/src/ol/format/filter/LessThanOrEqualTo.js @@ -1,27 +1,23 @@ /** * @module ol/format/filter/LessThanOrEqualTo */ -import {inherits} from '../../util.js'; import ComparisonBinary from '../filter/ComparisonBinary.js'; /** * @classdesc * Represents a `` comparison operator. - * - * @constructor - * @param {!string} propertyName Name of the context property to compare. - * @param {!number} expression The value to compare. - * @extends {module:ol/format/filter/ComparisonBinary} - * @api */ -class LessThanOrEqualTo { +class LessThanOrEqualTo extends ComparisonBinary { + /** + * @param {!string} propertyName Name of the context property to compare. + * @param {!number} expression The value to compare. + * @api + */ constructor(propertyName, expression) { - ComparisonBinary.call(this, 'PropertyIsLessThanOrEqualTo', propertyName, expression); + super('PropertyIsLessThanOrEqualTo', propertyName, expression); } } -inherits(LessThanOrEqualTo, ComparisonBinary); - export default LessThanOrEqualTo; diff --git a/src/ol/format/filter/LogicalNary.js b/src/ol/format/filter/LogicalNary.js index fcbcdb0b09..425e3e9ab9 100644 --- a/src/ol/format/filter/LogicalNary.js +++ b/src/ol/format/filter/LogicalNary.js @@ -1,7 +1,6 @@ /** * @module ol/format/filter/LogicalNary */ -import {inherits} from '../../util.js'; import {assert} from '../../asserts.js'; import Filter from '../filter/Filter.js'; @@ -10,17 +9,17 @@ import Filter from '../filter/Filter.js'; * Abstract class; normally only used for creating subclasses and not instantiated in apps. * Base class for WFS GetFeature n-ary logical filters. * - * @constructor * @abstract - * @param {!string} tagName The XML tag name for this filter. - * @param {...module:ol/format/filter/Filter} conditions Conditions. - * @extends {module:ol/format/filter/Filter} */ -class LogicalNary { +class LogicalNary extends Filter { + /** + * @param {!string} tagName The XML tag name for this filter. + * @param {...module:ol/format/filter/Filter} conditions Conditions. + */ constructor(tagName, conditions) { - Filter.call(this, tagName); + super(tagName); /** * @type {Array.} @@ -31,6 +30,4 @@ class LogicalNary { } -inherits(LogicalNary, Filter); - export default LogicalNary; diff --git a/src/ol/format/filter/Not.js b/src/ol/format/filter/Not.js index 6fd462d24e..c5cb77782c 100644 --- a/src/ol/format/filter/Not.js +++ b/src/ol/format/filter/Not.js @@ -1,23 +1,21 @@ /** * @module ol/format/filter/Not */ -import {inherits} from '../../util.js'; import Filter from '../filter/Filter.js'; /** * @classdesc * Represents a logical `` operator for a filter condition. - * - * @constructor - * @param {!module:ol/format/filter/Filter} condition Filter condition. - * @extends {module:ol/format/filter/Filter} - * @api */ -class Not { +class Not extends Filter { + /** + * @param {!module:ol/format/filter/Filter} condition Filter condition. + * @api + */ constructor(condition) { - Filter.call(this, 'Not'); + super('Not'); /** * @type {!module:ol/format/filter/Filter} @@ -28,5 +26,4 @@ class Not { } -inherits(Not, Filter); export default Not; diff --git a/src/ol/format/filter/NotEqualTo.js b/src/ol/format/filter/NotEqualTo.js index e0abaaa69a..d66ca80ad3 100644 --- a/src/ol/format/filter/NotEqualTo.js +++ b/src/ol/format/filter/NotEqualTo.js @@ -1,28 +1,24 @@ /** * @module ol/format/filter/NotEqualTo */ -import {inherits} from '../../util.js'; import ComparisonBinary from '../filter/ComparisonBinary.js'; /** * @classdesc * Represents a `` comparison operator. - * - * @constructor - * @param {!string} propertyName Name of the context property to compare. - * @param {!(string|number)} expression The value to compare. - * @param {boolean=} opt_matchCase Case-sensitive? - * @extends {module:ol/format/filter/ComparisonBinary} - * @api */ -class NotEqualTo { +class NotEqualTo extends ComparisonBinary { + /** + * @param {!string} propertyName Name of the context property to compare. + * @param {!(string|number)} expression The value to compare. + * @param {boolean=} opt_matchCase Case-sensitive? + * @api + */ constructor(propertyName, expression, opt_matchCase) { - ComparisonBinary.call(this, 'PropertyIsNotEqualTo', propertyName, expression, opt_matchCase); + super('PropertyIsNotEqualTo', propertyName, expression, opt_matchCase); } } -inherits(NotEqualTo, ComparisonBinary); - export default NotEqualTo; diff --git a/src/ol/format/filter/Or.js b/src/ol/format/filter/Or.js index 98952613cc..6f2543d2aa 100644 --- a/src/ol/format/filter/Or.js +++ b/src/ol/format/filter/Or.js @@ -1,27 +1,23 @@ /** * @module ol/format/filter/Or */ -import {inherits} from '../../util.js'; import LogicalNary from '../filter/LogicalNary.js'; /** * @classdesc * Represents a logical `` operator between two ore more filter conditions. - * - * @constructor - * @param {...module:ol/format/filter/Filter} conditions Conditions. - * @extends {module:ol/format/filter/LogicalNary} - * @api */ -class Or { +class Or extends LogicalNary { + /** + * @param {...module:ol/format/filter/Filter} conditions Conditions. + * @api + */ constructor(conditions) { const params = ['Or'].concat(Array.prototype.slice.call(arguments)); - LogicalNary.apply(this, params); + super(...params); } } -inherits(Or, LogicalNary); - export default Or; diff --git a/src/ol/format/filter/Spatial.js b/src/ol/format/filter/Spatial.js index 2bf67267b8..79f28b6110 100644 --- a/src/ol/format/filter/Spatial.js +++ b/src/ol/format/filter/Spatial.js @@ -1,7 +1,6 @@ /** * @module ol/format/filter/Spatial */ -import {inherits} from '../../util.js'; import Filter from '../filter/Filter.js'; /** @@ -10,20 +9,20 @@ import Filter from '../filter/Filter.js'; * Represents a spatial operator to test whether a geometry-valued property * relates to a given geometry. * - * @constructor * @abstract - * @param {!string} tagName The XML tag name for this filter. - * @param {!string} geometryName Geometry name to use. - * @param {!module:ol/geom/Geometry} geometry Geometry. - * @param {string=} opt_srsName SRS name. No srsName attribute will be - * set on geometries when this is not provided. - * @extends {module:ol/format/filter/Filter} */ -class Spatial { +class Spatial extends Filter { + /** + * @param {!string} tagName The XML tag name for this filter. + * @param {!string} geometryName Geometry name to use. + * @param {!module:ol/geom/Geometry} geometry Geometry. + * @param {string=} opt_srsName SRS name. No srsName attribute will be + * set on geometries when this is not provided. + */ constructor(tagName, geometryName, geometry, opt_srsName) { - Filter.call(this, tagName); + super(tagName); /** * @type {!string} @@ -43,6 +42,4 @@ class Spatial { } -inherits(Spatial, Filter); - export default Spatial; From e79add2e77cb708cbc2c63f4be3e32d90e1265da Mon Sep 17 00:00:00 2001 From: ahocevar Date: Tue, 17 Jul 2018 21:09:06 +0200 Subject: [PATCH 068/107] Use extends, super and proper constructor jsdoc for ol/interaction --- src/ol/interaction/DoubleClickZoom.js | 21 ++---- src/ol/interaction/DragAndDrop.js | 38 ++++------ src/ol/interaction/DragBox.js | 55 ++++++-------- src/ol/interaction/DragPan.js | 23 ++---- src/ol/interaction/DragRotate.js | 24 ++---- src/ol/interaction/DragRotateAndZoom.js | 16 ++-- src/ol/interaction/DragZoom.js | 97 ++++++++++++------------- src/ol/interaction/Draw.js | 42 ++++------- src/ol/interaction/Extent.js | 42 +++++------ src/ol/interaction/Interaction.js | 17 ++--- src/ol/interaction/KeyboardPan.js | 16 ++-- src/ol/interaction/KeyboardZoom.js | 17 ++--- src/ol/interaction/Modify.js | 44 +++++------ src/ol/interaction/MouseWheelZoom.js | 16 ++-- src/ol/interaction/PinchRotate.js | 26 ++----- src/ol/interaction/PinchZoom.js | 25 ++----- src/ol/interaction/Pointer.js | 53 +++++++------- src/ol/interaction/Select.js | 40 +++++----- src/ol/interaction/Snap.js | 28 +++---- src/ol/interaction/Translate.js | 35 ++++----- 20 files changed, 273 insertions(+), 402 deletions(-) diff --git a/src/ol/interaction/DoubleClickZoom.js b/src/ol/interaction/DoubleClickZoom.js index 49146148ea..04f1fd5a16 100644 --- a/src/ol/interaction/DoubleClickZoom.js +++ b/src/ol/interaction/DoubleClickZoom.js @@ -1,7 +1,6 @@ /** * @module ol/interaction/DoubleClickZoom */ -import {inherits} from '../util.js'; import MapBrowserEventType from '../MapBrowserEventType.js'; import Interaction, {zoomByDelta} from '../interaction/Interaction.js'; @@ -16,15 +15,17 @@ import Interaction, {zoomByDelta} from '../interaction/Interaction.js'; /** * @classdesc * Allows the user to zoom by double-clicking on the map. - * - * @constructor - * @extends {module:ol/interaction/Interaction} - * @param {module:ol/interaction/DoubleClickZoom~Options=} opt_options Options. - * @api */ -class DoubleClickZoom { +class DoubleClickZoom extends Interaction { + /** + * @param {module:ol/interaction/DoubleClickZoom~Options=} opt_options Options. + * @api + */ constructor(opt_options) { + super({ + handleEvent: handleEvent + }); const options = opt_options ? opt_options : {}; @@ -34,10 +35,6 @@ class DoubleClickZoom { */ this.delta_ = options.delta ? options.delta : 1; - Interaction.call(this, { - handleEvent: handleEvent - }); - /** * @private * @type {number} @@ -48,8 +45,6 @@ class DoubleClickZoom { } -inherits(DoubleClickZoom, Interaction); - /** * Handles the {@link module:ol/MapBrowserEvent map browser event} (if it was a diff --git a/src/ol/interaction/DragAndDrop.js b/src/ol/interaction/DragAndDrop.js index adde90c1b7..186d1798c9 100644 --- a/src/ol/interaction/DragAndDrop.js +++ b/src/ol/interaction/DragAndDrop.js @@ -3,7 +3,6 @@ */ // FIXME should handle all geo-referenced data, not just vector data -import {inherits} from '../util.js'; import {TRUE} from '../functions.js'; import {listen, unlistenByKey} from '../events.js'; import Event from '../events/Event.js'; @@ -42,19 +41,18 @@ const DragAndDropEventType = { * @classdesc * Events emitted by {@link module:ol/interaction/DragAndDrop~DragAndDrop} instances are instances * of this type. - * - * @constructor - * @extends {module:ol/events/Event} - * @param {module:ol/interaction/DragAndDrop~DragAndDropEventType} type Type. - * @param {File} file File. - * @param {Array.=} opt_features Features. - * @param {module:ol/proj/Projection=} opt_projection Projection. */ -class DragAndDropEvent { +class DragAndDropEvent extends Event { + /** + * @param {module:ol/interaction/DragAndDrop~DragAndDropEventType} type Type. + * @param {File} file File. + * @param {Array.=} opt_features Features. + * @param {module:ol/proj/Projection=} opt_projection Projection. + */ constructor(type, file, opt_features, opt_projection) { - Event.call(this, type); + super(type); /** * The features parsed from dropped data. @@ -81,25 +79,23 @@ class DragAndDropEvent { } -inherits(DragAndDropEvent, Event); - /** * @classdesc * Handles input of vector data by drag and drop. * - * @constructor - * @extends {module:ol/interaction/Interaction} * @fires module:ol/interaction/DragAndDrop~DragAndDropEvent - * @param {module:ol/interaction/DragAndDrop~Options=} opt_options Options. - * @api */ -class DragAndDrop { +class DragAndDrop extends Interaction { + /** + * @param {module:ol/interaction/DragAndDrop~Options=} opt_options Options. + * @api + */ constructor(opt_options) { const options = opt_options ? opt_options : {}; - Interaction.call(this, { + super({ handleEvent: TRUE }); @@ -200,7 +196,7 @@ class DragAndDrop { * @inheritDoc */ setActive(active) { - Interaction.prototype.setActive.call(this, active); + super.setActive(active); if (active) { this.registerListeners_(); } else { @@ -213,7 +209,7 @@ class DragAndDrop { */ setMap(map) { this.unregisterListeners_(); - Interaction.prototype.setMap.call(this, map); + super.setMap(map); if (this.getActive()) { this.registerListeners_(); } @@ -245,8 +241,6 @@ class DragAndDrop { } } -inherits(DragAndDrop, Interaction); - /** * @param {DragEvent} event Event. diff --git a/src/ol/interaction/DragBox.js b/src/ol/interaction/DragBox.js index bfb1040ee3..60bf0b4063 100644 --- a/src/ol/interaction/DragBox.js +++ b/src/ol/interaction/DragBox.js @@ -3,7 +3,6 @@ */ // FIXME draw drag box import Event from '../events/Event.js'; -import {inherits} from '../util.js'; import {always, mouseOnly, mouseActionButton} from '../events/condition.js'; import {UNDEFINED} from '../functions.js'; import PointerInteraction from '../interaction/Pointer.js'; @@ -29,6 +28,8 @@ import RenderBox from '../render/Box.js'; * @property {module:ol/interaction/DragBox~EndCondition} [boxEndCondition] A function that takes a {@link module:ol/MapBrowserEvent~MapBrowserEvent} and two * {@link module:ol~Pixel}s to indicate whether a `boxend` event should be fired. * Default is `true` if the area of the box is bigger than the `minArea` option. + * @property {function(this:module:ol/interaction/DragBox, module:ol/MapBrowserEvent)} onBoxEnd Code to execute just + * before `boxend` is fired. */ @@ -63,17 +64,16 @@ const DragBoxEventType = { * @classdesc * Events emitted by {@link module:ol/interaction/DragBox~DragBox} instances are instances of * this type. - * - * @param {string} type The event type. - * @param {module:ol/coordinate~Coordinate} coordinate The event coordinate. - * @param {module:ol/MapBrowserEvent} mapBrowserEvent Originating event. - * @extends {module:ol/events/Event} - * @constructor */ -class DragBoxEvent { +class DragBoxEvent extends Event { + /** + * @param {string} type The event type. + * @param {module:ol/coordinate~Coordinate} coordinate The event coordinate. + * @param {module:ol/MapBrowserEvent} mapBrowserEvent Originating event. + */ constructor(type, coordinate, mapBrowserEvent) { - Event.call(this, type); + super(type); /** * The coordinate of the drag event. @@ -94,8 +94,6 @@ class DragBoxEvent { } -inherits(DragBoxEvent, Event); - /** * @classdesc @@ -108,16 +106,16 @@ inherits(DragBoxEvent, Event); * * This interaction is only supported for mouse devices. * - * @constructor - * @extends {module:ol/interaction/Pointer} * @fires module:ol/interaction/DragBox~DragBoxEvent - * @param {module:ol/interaction/DragBox~Options=} opt_options Options. - * @api */ -class DragBox { +class DragBox extends PointerInteraction { + /** + * @param {module:ol/interaction/DragBox~Options=} opt_options Options. + * @api + */ constructor(opt_options) { - PointerInteraction.call(this, { + super({ handleDownEvent: handleDownEvent, handleDragEvent: handleDragEvent, handleUpEvent: handleUpEvent @@ -137,6 +135,13 @@ class DragBox { */ this.minArea_ = options.minArea !== undefined ? options.minArea : 64; + /** + * Function to execute just before `onboxend` is fired + * @type {{function(this:module:ol/interaction/DragBox, module:ol/MapBrowserEvent)}} + * @private + */ + this.onBoxEnd_ = options.onBoxEnd ? options.onBoxEnd : UNDEFINED; + /** * @type {module:ol~Pixel} * @private @@ -167,8 +172,6 @@ class DragBox { } } -inherits(DragBox, PointerInteraction); - /** * The default condition for determining whether the boxend event @@ -203,15 +206,6 @@ function handleDragEvent(mapBrowserEvent) { } -/** - * To be overridden by child classes. - * FIXME: use constructor option instead of relying on overriding. - * @param {module:ol/MapBrowserEvent} mapBrowserEvent Map browser event. - * @protected - */ -DragBox.prototype.onBoxEnd = UNDEFINED; - - /** * @param {module:ol/MapBrowserPointerEvent} mapBrowserEvent Event. * @return {boolean} Stop drag sequence? @@ -224,9 +218,8 @@ function handleUpEvent(mapBrowserEvent) { this.box_.setMap(null); - if (this.boxEndCondition_(mapBrowserEvent, - this.startPixel_, mapBrowserEvent.pixel)) { - this.onBoxEnd(mapBrowserEvent); + if (this.boxEndCondition_(mapBrowserEvent, this.startPixel_, mapBrowserEvent.pixel)) { + this.onBoxEnd_(mapBrowserEvent); this.dispatchEvent(new DragBoxEvent(DragBoxEventType.BOXEND, mapBrowserEvent.coordinate, mapBrowserEvent)); } diff --git a/src/ol/interaction/DragPan.js b/src/ol/interaction/DragPan.js index b714199432..386c15544d 100644 --- a/src/ol/interaction/DragPan.js +++ b/src/ol/interaction/DragPan.js @@ -1,7 +1,6 @@ /** * @module ol/interaction/DragPan */ -import {inherits} from '../util.js'; import ViewHint from '../ViewHint.js'; import {scale as scaleCoordinate, rotate as rotateCoordinate, add as addCoordinate} from '../coordinate.js'; import {easeOut} from '../easing.js'; @@ -22,19 +21,19 @@ import PointerInteraction, {centroid as centroidFromPointers} from '../interacti /** * @classdesc * Allows the user to pan the map by dragging the map. - * - * @constructor - * @extends {module:ol/interaction/Pointer} - * @param {module:ol/interaction/DragPan~Options=} opt_options Options. - * @api */ -class DragPan { +class DragPan extends PointerInteraction { + /** + * @param {module:ol/interaction/DragPan~Options=} opt_options Options. + * @api + */ constructor(opt_options) { - PointerInteraction.call(this, { + super({ handleDownEvent: handleDownEvent, handleDragEvent: handleDragEvent, - handleUpEvent: handleUpEvent + handleUpEvent: handleUpEvent, + stopDown: FALSE }); const options = opt_options ? opt_options : {}; @@ -71,8 +70,6 @@ class DragPan { } -inherits(DragPan, PointerInteraction); - /** * @param {module:ol/MapBrowserPointerEvent} mapBrowserEvent Event. @@ -175,8 +172,4 @@ function handleDownEvent(mapBrowserEvent) { } -/** - * @inheritDoc - */ -DragPan.prototype.shouldStopEvent = FALSE; export default DragPan; diff --git a/src/ol/interaction/DragRotate.js b/src/ol/interaction/DragRotate.js index 059e18e44a..bfa82a3189 100644 --- a/src/ol/interaction/DragRotate.js +++ b/src/ol/interaction/DragRotate.js @@ -1,7 +1,6 @@ /** * @module ol/interaction/DragRotate */ -import {inherits} from '../util.js'; import {disable} from '../rotationconstraint.js'; import ViewHint from '../ViewHint.js'; import {altShiftKeysOnly, mouseOnly, mouseActionButton} from '../events/condition.js'; @@ -27,22 +26,22 @@ import PointerInteraction from '../interaction/Pointer.js'; * it to when the alt and shift keys are held down. * * This interaction is only supported for mouse devices. - * - * @constructor - * @extends {module:ol/interaction/Pointer} - * @param {module:ol/interaction/DragRotate~Options=} opt_options Options. - * @api */ -class DragRotate { +class DragRotate extends PointerInteraction { + /** + * @param {module:ol/interaction/DragRotate~Options=} opt_options Options. + * @api + */ constructor(opt_options) { const options = opt_options ? opt_options : {}; - PointerInteraction.call(this, { + super({ handleDownEvent: handleDownEvent, handleDragEvent: handleDragEvent, - handleUpEvent: handleUpEvent + handleUpEvent: handleUpEvent, + stopDown: FALSE }); /** @@ -67,8 +66,6 @@ class DragRotate { } -inherits(DragRotate, PointerInteraction); - /** * @param {module:ol/MapBrowserPointerEvent} mapBrowserEvent Event. @@ -136,9 +133,4 @@ function handleDownEvent(mapBrowserEvent) { } } - -/** - * @inheritDoc - */ -DragRotate.prototype.shouldStopEvent = FALSE; export default DragRotate; diff --git a/src/ol/interaction/DragRotateAndZoom.js b/src/ol/interaction/DragRotateAndZoom.js index dc139da900..b751a871cf 100644 --- a/src/ol/interaction/DragRotateAndZoom.js +++ b/src/ol/interaction/DragRotateAndZoom.js @@ -1,7 +1,6 @@ /** * @module ol/interaction/DragRotateAndZoom */ -import {inherits} from '../util.js'; import {disable} from '../rotationconstraint.js'; import ViewHint from '../ViewHint.js'; import {shiftKeyOnly, mouseOnly} from '../events/condition.js'; @@ -28,19 +27,18 @@ import PointerInteraction from '../interaction/Pointer.js'; * This interaction is only supported for mouse devices. * * And this interaction is not included in the default interactions. - * - * @constructor - * @extends {module:ol/interaction/Pointer} - * @param {module:ol/interaction/DragRotateAndZoom~Options=} opt_options Options. - * @api */ -class DragRotateAndZoom { +class DragRotateAndZoom extends PointerInteraction { + /** + * @param {module:ol/interaction/DragRotateAndZoom~Options=} opt_options Options. + * @api + */ constructor(opt_options) { const options = opt_options ? opt_options : {}; - PointerInteraction.call(this, { + super({ handleDownEvent: handleDownEvent, handleDragEvent: handleDragEvent, handleUpEvent: handleUpEvent @@ -80,8 +78,6 @@ class DragRotateAndZoom { } -inherits(DragRotateAndZoom, PointerInteraction); - /** * @param {module:ol/MapBrowserPointerEvent} mapBrowserEvent Event. diff --git a/src/ol/interaction/DragZoom.js b/src/ol/interaction/DragZoom.js index ff0b2ade1e..a8dc5c28fa 100644 --- a/src/ol/interaction/DragZoom.js +++ b/src/ol/interaction/DragZoom.js @@ -1,7 +1,6 @@ /** * @module ol/interaction/DragZoom */ -import {inherits} from '../util.js'; import {easeOut} from '../easing.js'; import {shiftKeyOnly} from '../events/condition.js'; import {createOrUpdateFromCoordinates, getBottomLeft, getCenter, getTopRight, scaleFromCenter} from '../extent.js'; @@ -29,18 +28,23 @@ import DragBox from '../interaction/DragBox.js'; * * To change the style of the box, use CSS and the `.ol-dragzoom` selector, or * your custom one configured with `className`. - * - * @constructor - * @extends {module:ol/interaction/DragBox} - * @param {module:ol/interaction/DragZoom~Options=} opt_options Options. - * @api */ -class DragZoom { +class DragZoom extends DragBox { + /** + * @param {module:ol/interaction/DragZoom~Options=} opt_options Options. + * @api + */ constructor(opt_options) { const options = opt_options ? opt_options : {}; const condition = options.condition ? options.condition : shiftKeyOnly; + super({ + condition: condition, + className: options.className || 'ol-dragzoom', + onBoxEnd: onBoxEnd + }); + /** * @private * @type {number} @@ -52,54 +56,43 @@ class DragZoom { * @type {boolean} */ this.out_ = options.out !== undefined ? options.out : false; - - DragBox.call(this, { - condition: condition, - className: options.className || 'ol-dragzoom' - }); - - } - - /** - * @inheritDoc - */ - onBoxEnd() { - const map = this.getMap(); - - const view = /** @type {!module:ol/View} */ (map.getView()); - - const size = /** @type {!module:ol/size~Size} */ (map.getSize()); - - let extent = this.getGeometry().getExtent(); - - if (this.out_) { - const mapExtent = view.calculateExtent(size); - const boxPixelExtent = createOrUpdateFromCoordinates([ - map.getPixelFromCoordinate(getBottomLeft(extent)), - map.getPixelFromCoordinate(getTopRight(extent))]); - const factor = view.getResolutionForExtent(boxPixelExtent, size); - - scaleFromCenter(mapExtent, 1 / factor); - extent = mapExtent; - } - - const resolution = view.constrainResolution( - view.getResolutionForExtent(extent, size)); - - let center = getCenter(extent); - center = view.constrainCenter(center); - - view.animate({ - resolution: resolution, - center: center, - duration: this.duration_, - easing: easeOut - }); - } } -inherits(DragZoom, DragBox); + +/** + * @this {module:ol/interaction/DragZoom} + */ +function onBoxEnd() { + const map = this.getMap(); + const view = /** @type {!module:ol/View} */ (map.getView()); + const size = /** @type {!module:ol/size~Size} */ (map.getSize()); + let extent = this.getGeometry().getExtent(); + + if (this.out_) { + const mapExtent = view.calculateExtent(size); + const boxPixelExtent = createOrUpdateFromCoordinates([ + map.getPixelFromCoordinate(getBottomLeft(extent)), + map.getPixelFromCoordinate(getTopRight(extent))]); + const factor = view.getResolutionForExtent(boxPixelExtent, size); + + scaleFromCenter(mapExtent, 1 / factor); + extent = mapExtent; + } + + const resolution = view.constrainResolution( + view.getResolutionForExtent(extent, size)); + + let center = getCenter(extent); + center = view.constrainCenter(center); + + view.animate({ + resolution: resolution, + center: center, + duration: this.duration_, + easing: easeOut + }); +} export default DragZoom; diff --git a/src/ol/interaction/Draw.js b/src/ol/interaction/Draw.js index c5afd7c687..f6a8c592f3 100644 --- a/src/ol/interaction/Draw.js +++ b/src/ol/interaction/Draw.js @@ -1,7 +1,6 @@ /** * @module ol/interaction/Draw */ -import {inherits} from '../util.js'; import EventType from '../events/EventType.js'; import Feature from '../Feature.js'; import MapBrowserEventType from '../MapBrowserEventType.js'; @@ -128,17 +127,15 @@ const DrawEventType = { * @classdesc * Events emitted by {@link module:ol/interaction/Draw~Draw} instances are * instances of this type. - * - * @constructor - * @extends {module:ol/events/Event} - * @param {module:ol/interaction/Draw~DrawEventType} type Type. - * @param {module:ol/Feature} feature The feature drawn. */ -class DrawEvent { - +class DrawEvent extends Event { + /** + * @param {module:ol/interaction/Draw~DrawEventType} type Type. + * @param {module:ol/Feature} feature The feature drawn. + */ constructor(type, feature) { - Event.call(this, type); + super(type); /** * The feature being drawn. @@ -151,26 +148,25 @@ class DrawEvent { } -inherits(DrawEvent, Event); - /** * @classdesc * Interaction for drawing feature geometries. * - * @constructor - * @extends {module:ol/interaction/Pointer} * @fires module:ol/interaction/Draw~DrawEvent - * @param {module:ol/interaction/Draw~Options} options Options. - * @api */ -class Draw { +class Draw extends PointerInteraction { + /** + * @param {module:ol/interaction/Draw~Options} options Options. + * @api + */ constructor(options) { - PointerInteraction.call(this, { + super({ handleDownEvent: handleDownEvent, handleEvent: handleEvent, - handleUpEvent: handleUpEvent + handleUpEvent: handleUpEvent, + stopDown: FALSE }); /** @@ -441,7 +437,7 @@ class Draw { * @inheritDoc */ setMap(map) { - PointerInteraction.prototype.setMap.call(this, map); + super.setMap(map); this.updateState_(); } @@ -801,8 +797,6 @@ class Draw { } } -inherits(Draw, PointerInteraction); - /** * @return {module:ol/style/Style~StyleFunction} Styles. @@ -941,12 +935,6 @@ function handleUpEvent(event) { } -/** - * @inheritDoc - */ -Draw.prototype.shouldStopEvent = FALSE; - - /** * 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 diff --git a/src/ol/interaction/Extent.js b/src/ol/interaction/Extent.js index 1140dd1b1a..340ee6da42 100644 --- a/src/ol/interaction/Extent.js +++ b/src/ol/interaction/Extent.js @@ -1,7 +1,6 @@ /** * @module ol/interaction/Extent */ -import {inherits} from '../util.js'; import Feature from '../Feature.js'; import MapBrowserEventType from '../MapBrowserEventType.js'; import MapBrowserPointerEvent from '../MapBrowserPointerEvent.js'; @@ -51,15 +50,14 @@ const ExtentEventType = { * @classdesc * Events emitted by {@link module:ol/interaction/Extent~Extent} instances are * instances of this type. - * - * @constructor - * @param {module:ol/extent~Extent} extent the new extent - * @extends {module:ol/events/Event} */ -class ExtentInteractionEvent { +class ExtentInteractionEvent extends Event { + /** + * @param {module:ol/extent~Extent} extent the new extent + */ constructor(extent) { - Event.call(this, ExtentEventType.EXTENTCHANGED); + super(ExtentEventType.EXTENTCHANGED); /** * The current extent. @@ -71,8 +69,6 @@ class ExtentInteractionEvent { } -inherits(ExtentInteractionEvent, Event); - /** * @classdesc @@ -80,15 +76,22 @@ inherits(ExtentInteractionEvent, Event); * Once drawn, the vector box can be modified by dragging its vertices or edges. * This interaction is only supported for mouse devices. * - * @constructor - * @extends {module:ol/interaction/Pointer} * @fires module:ol/interaction/Extent~Event - * @param {module:ol/interaction/Extent~Options=} opt_options Options. - * @api */ -class ExtentInteraction { +class ExtentInteraction extends PointerInteraction { + /** + * @param {module:ol/interaction/Extent~Options=} opt_options Options. + * @api + */ constructor(opt_options) { + super({ + handleDownEvent: handleDownEvent, + handleDragEvent: handleDragEvent, + handleEvent: handleEvent, + handleUpEvent: handleUpEvent + }); + const options = opt_options || {}; /** @@ -138,13 +141,6 @@ class ExtentInteraction { opt_options = {}; } - PointerInteraction.call(this, { - handleDownEvent: handleDownEvent, - handleDragEvent: handleDragEvent, - handleEvent: handleEvent, - handleUpEvent: handleUpEvent - }); - /** * Layer for the extentFeature * @type {module:ol/layer/Vector} @@ -287,7 +283,7 @@ class ExtentInteraction { setMap(map) { this.extentOverlay_.setMap(map); this.vertexOverlay_.setMap(map); - PointerInteraction.prototype.setMap.call(this, map); + super.setMap(map); } /** @@ -314,8 +310,6 @@ class ExtentInteraction { } } -inherits(ExtentInteraction, PointerInteraction); - /** * @param {module:ol/MapBrowserEvent} mapBrowserEvent Event. * @return {boolean} Propagate event? diff --git a/src/ol/interaction/Interaction.js b/src/ol/interaction/Interaction.js index 7bd2e432f8..0a97db11de 100644 --- a/src/ol/interaction/Interaction.js +++ b/src/ol/interaction/Interaction.js @@ -1,7 +1,6 @@ /** * @module ol/interaction/Interaction */ -import {inherits} from '../util.js'; import BaseObject from '../Object.js'; import {easeOut, linear} from '../easing.js'; import InteractionProperty from '../interaction/Property.js'; @@ -30,16 +29,14 @@ import {clamp} from '../math.js'; * by a keyboard event not a button element event. * Although interactions do not have a DOM element, some of them do render * vectors and so are visible on the screen. - * - * @constructor - * @param {module:ol/interaction/Interaction~InteractionOptions} options Options. - * @extends {module:ol/Object} - * @api */ -class Interaction { +class Interaction extends BaseObject { + /** + * @param {module:ol/interaction/Interaction~InteractionOptions} options Options. + * @api + */ constructor(options) { - - BaseObject.call(this); + super(); /** * @private @@ -96,8 +93,6 @@ class Interaction { } } -inherits(Interaction, BaseObject); - /** * @param {module:ol/View} view View. diff --git a/src/ol/interaction/KeyboardPan.js b/src/ol/interaction/KeyboardPan.js index ca94db57e3..c6ee6e84a8 100644 --- a/src/ol/interaction/KeyboardPan.js +++ b/src/ol/interaction/KeyboardPan.js @@ -1,7 +1,6 @@ /** * @module ol/interaction/KeyboardPan */ -import {inherits} from '../util.js'; import {rotate as rotateCoordinate} from '../coordinate.js'; import EventType from '../events/EventType.js'; import KeyCode from '../events/KeyCode.js'; @@ -33,17 +32,15 @@ import Interaction, {pan} from '../interaction/Interaction.js'; * element, focus will have to be on, and returned to, this element if the keys * are to function. * See also {@link module:ol/interaction/KeyboardZoom~KeyboardZoom}. - * - * @constructor - * @extends {module:ol/interaction/Interaction} - * @param {module:ol/interaction/KeyboardPan~Options=} opt_options Options. - * @api */ -class KeyboardPan { - +class KeyboardPan extends Interaction { + /** + * @param {module:ol/interaction/KeyboardPan~Options=} opt_options Options. + * @api + */ constructor(opt_options) { - Interaction.call(this, { + super({ handleEvent: handleEvent }); @@ -83,7 +80,6 @@ class KeyboardPan { } -inherits(KeyboardPan, Interaction); /** * Handles the {@link module:ol/MapBrowserEvent map browser event} if it was a diff --git a/src/ol/interaction/KeyboardZoom.js b/src/ol/interaction/KeyboardZoom.js index e5b49053e8..496b85859d 100644 --- a/src/ol/interaction/KeyboardZoom.js +++ b/src/ol/interaction/KeyboardZoom.js @@ -1,7 +1,6 @@ /** * @module ol/interaction/KeyboardZoom */ -import {inherits} from '../util.js'; import EventType from '../events/EventType.js'; import {targetNotEditable} from '../events/condition.js'; import Interaction, {zoomByDelta} from '../interaction/Interaction.js'; @@ -29,17 +28,15 @@ import Interaction, {zoomByDelta} from '../interaction/Interaction.js'; * 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}. - * - * @constructor - * @param {module:ol/interaction/KeyboardZoom~Options=} opt_options Options. - * @extends {module:ol/interaction/Interaction} - * @api */ -class KeyboardZoom { - +class KeyboardZoom extends Interaction { + /** + * @param {module:ol/interaction/KeyboardZoom~Options=} opt_options Options. + * @api + */ constructor(opt_options) { - Interaction.call(this, { + super({ handleEvent: handleEvent }); @@ -67,8 +64,6 @@ class KeyboardZoom { } -inherits(KeyboardZoom, Interaction); - /** * Handles the {@link module:ol/MapBrowserEvent map browser event} if it was a diff --git a/src/ol/interaction/Modify.js b/src/ol/interaction/Modify.js index 2cf7ba9f5d..cbcf947c3d 100644 --- a/src/ol/interaction/Modify.js +++ b/src/ol/interaction/Modify.js @@ -1,7 +1,7 @@ /** * @module ol/interaction/Modify */ -import {getUid, inherits} from '../util.js'; +import {getUid} from '../util.js'; import Collection from '../Collection.js'; import CollectionEventType from '../CollectionEventType.js'; import Feature from '../Feature.js'; @@ -105,20 +105,17 @@ const ModifyEventType = { * @classdesc * Events emitted by {@link module:ol/interaction/Modify~Modify} instances are * instances of this type. - * - * @constructor - * @extends {module:ol/events/Event} - * @param {ModifyEventType} type Type. - * @param {module:ol/Collection.} features - * The features modified. - * @param {module:ol/MapBrowserPointerEvent} mapBrowserPointerEvent - * Associated {@link module:ol/MapBrowserPointerEvent}. */ -export class ModifyEvent { - +export class ModifyEvent extends Event { + /** + * @param {ModifyEventType} type Type. + * @param {module:ol/Collection.} features + * The features modified. + * @param {module:ol/MapBrowserPointerEvent} mapBrowserPointerEvent + * Associated {@link module:ol/MapBrowserPointerEvent}. + */ constructor(type, features, mapBrowserPointerEvent) { - - Event.call(this, type); + super(type); /** * The features being modified. @@ -138,8 +135,6 @@ export class ModifyEvent { } -inherits(ModifyEvent, Event); - /** * @classdesc @@ -153,17 +148,16 @@ inherits(ModifyEvent, Event); * By default, the interaction will allow deletion of vertices when the `alt` * key is pressed. To configure the interaction with a different condition * for deletion, use the `deleteCondition` option. - * - * @constructor - * @extends {module:ol/interaction/Pointer} - * @param {module:ol/interaction/Modify~Options} options Options. * @fires module:ol/interaction/Modify~ModifyEvent - * @api */ -class Modify { +class Modify extends PointerInteraction { + /** + * @param {module:ol/interaction/Modify~Options} options Options. + * @api + */ constructor(options) { - PointerInteraction.call(this, { + super({ handleDownEvent: handleDownEvent, handleDragEvent: handleDragEvent, handleEvent: handleEvent, @@ -417,7 +411,7 @@ class Modify { this.overlay_.getSource().removeFeature(this.vertexFeature_); this.vertexFeature_ = null; } - PointerInteraction.prototype.setActive.call(this, active); + super.setActive(active); } /** @@ -425,7 +419,7 @@ class Modify { */ setMap(map) { this.overlay_.setMap(map); - PointerInteraction.prototype.setMap.call(this, map); + super.setMap(this, map); } /** @@ -970,8 +964,6 @@ class Modify { } } -inherits(Modify, PointerInteraction); - /** * @param {module:ol/interaction/Modify~SegmentData} a The first segment data. diff --git a/src/ol/interaction/MouseWheelZoom.js b/src/ol/interaction/MouseWheelZoom.js index e5ab1e3dce..038eabb829 100644 --- a/src/ol/interaction/MouseWheelZoom.js +++ b/src/ol/interaction/MouseWheelZoom.js @@ -1,7 +1,6 @@ /** * @module ol/interaction/MouseWheelZoom */ -import {inherits} from '../util.js'; import ViewHint from '../ViewHint.js'; import {always} from '../events/condition.js'; import {easeOut} from '../easing.js'; @@ -47,16 +46,15 @@ export const Mode = { /** * @classdesc * Allows the user to zoom the map by scrolling the mouse wheel. - * - * @constructor - * @extends {module:ol/interaction/Interaction} - * @param {module:ol/interaction/MouseWheelZoom~Options=} opt_options Options. - * @api */ -class MouseWheelZoom { +class MouseWheelZoom extends Interaction { + /** + * @param {module:ol/interaction/MouseWheelZoom~Options=} opt_options Options. + * @api + */ constructor(opt_options) { - Interaction.call(this, { + super({ handleEvent: handleEvent }); @@ -192,8 +190,6 @@ class MouseWheelZoom { } } -inherits(MouseWheelZoom, Interaction); - /** * Handles the {@link module:ol/MapBrowserEvent map browser event} (if it was a diff --git a/src/ol/interaction/PinchRotate.js b/src/ol/interaction/PinchRotate.js index 8c06a4c0c2..d87290e197 100644 --- a/src/ol/interaction/PinchRotate.js +++ b/src/ol/interaction/PinchRotate.js @@ -1,7 +1,6 @@ /** * @module ol/interaction/PinchRotate */ -import {inherits} from '../util.js'; import ViewHint from '../ViewHint.js'; import {FALSE} from '../functions.js'; import {rotate, rotateWithoutConstraints} from '../interaction/Interaction.js'; @@ -21,20 +20,19 @@ import {disable} from '../rotationconstraint.js'; * @classdesc * Allows the user to rotate the map by twisting with two fingers * on a touch screen. - * - * @constructor - * @extends {module:ol/interaction/Pointer} - * @param {module:ol/interaction/PinchRotate~Options=} opt_options Options. - * @api */ -class PinchRotate { - +class PinchRotate extends PointerInteraction { + /** + * @param {module:ol/interaction/PinchRotate~Options=} opt_options Options. + * @api + */ constructor(opt_options) { - PointerInteraction.call(this, { + super({ handleDownEvent: handleDownEvent, handleDragEvent: handleDragEvent, - handleUpEvent: handleUpEvent + handleUpEvent: handleUpEvent, + stopDown: FALSE }); const options = opt_options || {}; @@ -79,8 +77,6 @@ class PinchRotate { } -inherits(PinchRotate, PointerInteraction); - /** * @param {module:ol/MapBrowserPointerEvent} mapBrowserEvent Event. @@ -174,10 +170,4 @@ function handleDownEvent(mapBrowserEvent) { } } - -/** - * @inheritDoc - */ -PinchRotate.prototype.shouldStopEvent = FALSE; - export default PinchRotate; diff --git a/src/ol/interaction/PinchZoom.js b/src/ol/interaction/PinchZoom.js index 542770b9f6..257ea320fa 100644 --- a/src/ol/interaction/PinchZoom.js +++ b/src/ol/interaction/PinchZoom.js @@ -1,7 +1,6 @@ /** * @module ol/interaction/PinchZoom */ -import {inherits} from '../util.js'; import ViewHint from '../ViewHint.js'; import {FALSE} from '../functions.js'; import {zoom, zoomWithoutConstraints} from '../interaction/Interaction.js'; @@ -20,20 +19,19 @@ import PointerInteraction, {centroid as centroidFromPointers} from '../interacti * @classdesc * Allows the user to zoom the map by pinching with two fingers * on a touch screen. - * - * @constructor - * @extends {module:ol/interaction/Pointer} - * @param {module:ol/interaction/PinchZoom~Options=} opt_options Options. - * @api */ -class PinchZoom { - +class PinchZoom extends PointerInteraction { + /** + * @param {module:ol/interaction/PinchZoom~Options=} opt_options Options. + * @api + */ constructor(opt_options) { - PointerInteraction.call(this, { + super({ handleDownEvent: handleDownEvent, handleDragEvent: handleDragEvent, - handleUpEvent: handleUpEvent + handleUpEvent: handleUpEvent, + stopDown: FALSE }); const options = opt_options ? opt_options : {}; @@ -72,8 +70,6 @@ class PinchZoom { } -inherits(PinchZoom, PointerInteraction); - /** * @param {module:ol/MapBrowserPointerEvent} mapBrowserEvent Event. @@ -174,9 +170,4 @@ function handleDownEvent(mapBrowserEvent) { } } - -/** - * @inheritDoc - */ -PinchZoom.prototype.shouldStopEvent = FALSE; export default PinchZoom; diff --git a/src/ol/interaction/Pointer.js b/src/ol/interaction/Pointer.js index 928a026508..a1cc939eb8 100644 --- a/src/ol/interaction/Pointer.js +++ b/src/ol/interaction/Pointer.js @@ -1,7 +1,6 @@ /** * @module ol/interaction/Pointer */ -import {inherits} from '../util.js'; import {FALSE, UNDEFINED} from '../functions.js'; import MapBrowserEventType from '../MapBrowserEventType.js'; import MapBrowserPointerEvent from '../MapBrowserPointerEvent.js'; @@ -59,6 +58,9 @@ const handleMoveEvent = UNDEFINED; * @property {(function(module:ol/MapBrowserPointerEvent):boolean)} [handleUpEvent] * Function handling "up" events. If the function returns `false` then the * current drag sequence is stopped. + * @property {function(boolean):boolean} stopDown + * Should the down event be propagated to other interactions, or should be + * stopped? */ @@ -71,18 +73,17 @@ const handleMoveEvent = UNDEFINED; * started. During a drag sequence the `handleDragEvent` user function is * called on `move` events. The drag sequence ends when the `handleUpEvent` * user function is called and returns `false`. - * - * @constructor - * @param {module:ol/interaction/Pointer~Options=} opt_options Options. - * @extends {module:ol/interaction/Interaction} - * @api */ -class PointerInteraction { +class PointerInteraction extends Interaction { + /** + * @param {module:ol/interaction/Pointer~Options=} opt_options Options. + * @api + */ constructor(opt_options) { const options = opt_options ? opt_options : {}; - Interaction.call(this, { + super({ handleEvent: options.handleEvent || handleEvent }); @@ -120,6 +121,14 @@ class PointerInteraction { */ 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.} * @private @@ -156,26 +165,8 @@ class PointerInteraction { } } - /** - * This method is used to determine if "down" events should be propagated to - * other interactions or should be stopped. - * - * The method receives the return code of the "handleDownEvent" function. - * - * By default this function is the "identity" function. It's overridden in - * child classes. - * - * @param {boolean} handled Was the event handled by the interaction? - * @return {boolean} Should the event be stopped? - * @protected - */ - shouldStopEvent(handled) { - return handled; - } } -inherits(PointerInteraction, Interaction); - /** * @param {Array.} pointerEvents List of events. @@ -233,7 +224,7 @@ export function handleEvent(mapBrowserEvent) { if (mapBrowserEvent.type == MapBrowserEventType.POINTERDOWN) { const handled = this.handleDownEvent_(mapBrowserEvent); this.handlingDownUpSequence = handled; - stopEvent = this.shouldStopEvent(handled); + stopEvent = this.stopDown(handled); } else if (mapBrowserEvent.type == MapBrowserEventType.POINTERMOVE) { this.handleMoveEvent_(mapBrowserEvent); } @@ -243,3 +234,11 @@ export function handleEvent(mapBrowserEvent) { 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; +} diff --git a/src/ol/interaction/Select.js b/src/ol/interaction/Select.js index d209c69c64..3b2c1d5551 100644 --- a/src/ol/interaction/Select.js +++ b/src/ol/interaction/Select.js @@ -1,7 +1,7 @@ /** * @module ol/interaction/Select */ -import {getUid, inherits} from '../util.js'; +import {getUid} from '../util.js'; import CollectionEventType from '../CollectionEventType.js'; import {extend, includes} from '../array.js'; import {listen} from '../events.js'; @@ -101,19 +101,17 @@ const SelectEventType = { * @classdesc * Events emitted by {@link module:ol/interaction/Select~Select} instances are instances of * this type. - * - * @param {SelectEventType} type The event type. - * @param {Array.} selected Selected features. - * @param {Array.} deselected Deselected features. - * @param {module:ol/MapBrowserEvent} mapBrowserEvent Associated - * {@link module:ol/MapBrowserEvent}. - * @extends {module:ol/events/Event} - * @constructor */ -class SelectEvent { - +class SelectEvent extends Event { + /** + * @param {SelectEventType} type The event type. + * @param {Array.} selected Selected features. + * @param {Array.} deselected Deselected features. + * @param {module:ol/MapBrowserEvent} mapBrowserEvent Associated + * {@link module:ol/MapBrowserEvent}. + */ constructor(type, selected, deselected, mapBrowserEvent) { - Event.call(this, type); + super(type); /** * Selected features array. @@ -140,8 +138,6 @@ class SelectEvent { } -inherits(SelectEvent, Event); - /** * @classdesc @@ -155,16 +151,16 @@ inherits(SelectEvent, Event); * * Selected features are added to an internal unmanaged layer. * - * @constructor - * @extends {module:ol/interaction/Interaction} - * @param {module:ol/interaction/Select~Options=} opt_options Options. * @fires SelectEvent - * @api */ -class Select { +class Select extends Interaction { + /** + * @param {module:ol/interaction/Select~Options=} opt_options Options. + * @api + */ constructor(opt_options) { - Interaction.call(this, { + super({ handleEvent: handleEvent }); @@ -336,7 +332,7 @@ class Select { if (currentMap) { selectedFeatures.forEach(currentMap.unskipFeature.bind(currentMap)); } - Interaction.prototype.setMap.call(this, map); + super.setMap(map); this.featureOverlay_.setMap(map); if (map) { selectedFeatures.forEach(map.skipFeature.bind(map)); @@ -375,8 +371,6 @@ class Select { } } -inherits(Select, Interaction); - /** * Handles the {@link module:ol/MapBrowserEvent map browser event} and may change the diff --git a/src/ol/interaction/Snap.js b/src/ol/interaction/Snap.js index 85c9bff6fc..4d41460b5f 100644 --- a/src/ol/interaction/Snap.js +++ b/src/ol/interaction/Snap.js @@ -1,7 +1,7 @@ /** * @module ol/interaction/Snap */ -import {getUid, inherits} from '../util.js'; +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'; @@ -62,19 +62,19 @@ import RBush from '../structs/RBush.js'; * var snap = new Snap({ * source: source * }); - * - * @constructor - * @extends {module:ol/interaction/Pointer} - * @param {module:ol/interaction/Snap~Options=} opt_options Options. - * @api */ -class Snap { +class Snap extends PointerInteraction { + /** + * @param {module:ol/interaction/Snap~Options=} opt_options Options. + * @api + */ constructor(opt_options) { - PointerInteraction.call(this, { + super({ handleEvent: handleEvent, handleDownEvent: TRUE, - handleUpEvent: handleUpEvent + handleUpEvent: handleUpEvent, + stopDown: FALSE }); const options = opt_options ? opt_options : {}; @@ -325,7 +325,7 @@ class Snap { keys.length = 0; features.forEach(this.forEachFeatureRemove_.bind(this)); } - PointerInteraction.prototype.setMap.call(this, map); + super.setMap(map); if (map) { if (this.features_) { @@ -585,14 +585,6 @@ class Snap { } } -inherits(Snap, PointerInteraction); - - -/** - * @inheritDoc - */ -Snap.prototype.shouldStopEvent = FALSE; - /** * Handle all pointer events events. diff --git a/src/ol/interaction/Translate.js b/src/ol/interaction/Translate.js index e7d40ac29f..3d082ccf9e 100644 --- a/src/ol/interaction/Translate.js +++ b/src/ol/interaction/Translate.js @@ -1,7 +1,6 @@ /** * @module ol/interaction/Translate */ -import {inherits} from '../util.js'; import Collection from '../Collection.js'; import {getChangeEventType} from '../Object.js'; import {listen} from '../events.js'; @@ -56,18 +55,16 @@ const TranslateEventType = { * @classdesc * Events emitted by {@link module:ol/interaction/Translate~Translate} instances * are instances of this type. - * - * @constructor - * @extends {module:ol/events/Event} - * @param {module:ol/interaction/Translate~TranslateEventType} type Type. - * @param {module:ol/Collection.} features The features translated. - * @param {module:ol/coordinate~Coordinate} coordinate The event coordinate. */ -export class TranslateEvent { - +export class TranslateEvent extends Event { + /** + * @param {module:ol/interaction/Translate~TranslateEventType} type Type. + * @param {module:ol/Collection.} features The features translated. + * @param {module:ol/coordinate~Coordinate} coordinate The event coordinate. + */ constructor(type, features, coordinate) { - Event.call(this, type); + super(type); /** * The features being translated. @@ -88,22 +85,20 @@ export class TranslateEvent { } -inherits(TranslateEvent, Event); - /** * @classdesc * Interaction for translating (moving) features. * - * @constructor - * @extends {module:ol/interaction/Pointer} * @fires module:ol/interaction/Translate~TranslateEvent - * @param {module:ol/interaction/Translate~Options=} opt_options Options. - * @api */ -class Translate { +class Translate extends PointerInteraction { + /** + * @param {module:ol/interaction/Translate~Options=} opt_options Options. + * @api + */ constructor(opt_options) { - PointerInteraction.call(this, { + super({ handleDownEvent: handleDownEvent, handleDragEvent: handleDragEvent, handleMoveEvent: handleMoveEvent, @@ -211,7 +206,7 @@ class Translate { */ setMap(map) { const oldMap = this.getMap(); - PointerInteraction.prototype.setMap.call(this, map); + super.setMap(map); this.updateState_(oldMap); } @@ -239,8 +234,6 @@ class Translate { } } -inherits(Translate, PointerInteraction); - /** * @param {module:ol/MapBrowserPointerEvent} event Event. From 524b4c99d50caa8964478109d7e1e4cfa4fe1e60 Mon Sep 17 00:00:00 2001 From: Tim Schaub Date: Tue, 17 Jul 2018 14:41:08 -0600 Subject: [PATCH 069/107] Bad @const annotation --- src/ol/webgl.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/ol/webgl.js b/src/ol/webgl.js index 05d5efb56e..85bf67ce2e 100644 --- a/src/ol/webgl.js +++ b/src/ol/webgl.js @@ -315,7 +315,6 @@ let EXTENSIONS; // value is set below /** * True if both OpenLayers and browser support WebGL. - * @const ol/has.WEBGL * @type {boolean} * @api */ From 7c6755d3ecca774a332390c1e271bac5c0f363a9 Mon Sep 17 00:00:00 2001 From: Tim Schaub Date: Tue, 17 Jul 2018 14:48:06 -0600 Subject: [PATCH 070/107] Scripts for generating the package and legacy build --- .gitignore | 2 -- config/rollup.js | 2 +- package.json | 23 ++++++++--------------- tasks/generate-index.js | 16 +++++----------- tasks/prepare-package.js | 28 ++++++++++------------------ tasks/transform-types.js | 31 ------------------------------- tasks/typecheck.js | 28 ---------------------------- 7 files changed, 24 insertions(+), 106 deletions(-) delete mode 100644 tasks/transform-types.js delete mode 100644 tasks/typecheck.js diff --git a/.gitignore b/.gitignore index d914ce0fc6..6df267a88f 100644 --- a/.gitignore +++ b/.gitignore @@ -2,5 +2,3 @@ /coverage/ /dist/ node_modules/ -src/index.js -src/ol/package.json diff --git a/config/rollup.js b/config/rollup.js index 18297d1ea0..4d19d05ffd 100644 --- a/config/rollup.js +++ b/config/rollup.js @@ -7,7 +7,7 @@ import buble from 'rollup-plugin-buble'; import sourcemaps from 'rollup-plugin-sourcemaps'; export default { - input: 'src/index.js', + input: 'build/index.js', output: [ {file: 'build/ol.js', format: 'iife', sourcemap: true} ], diff --git a/package.json b/package.json index 0f6d53058b..c014623699 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,5 @@ { - "name": "openlayers", + "name": "ol", "version": "5.0.3", "description": "OpenLayers mapping library", "keywords": [ @@ -7,6 +7,7 @@ "mapping", "ol" ], + "private": true, "homepage": "https://openlayers.org/", "scripts": { "lint": "eslint tasks test src/ol examples config", @@ -15,18 +16,13 @@ "karma": "karma start test/karma.config.js", "serve-examples": "webpack-dev-server --config examples/webpack/config.js --mode development --watch", "build-examples": "webpack --config examples/webpack/config.js --mode production", - "build-index": "node tasks/generate-index", - "prepare-package": "node tasks/prepare-package", - "prebuild": "npm run prepare-package && npm run build-index", - "prepare": "npm run prepare-package", - "build": "rollup --config config/rollup.js && cleancss --source-map src/ol/ol.css -o build/ol.css", - "presrc-closure": "npm run prebuild", - "src-closure": "node tasks/transform-types", - "pretypecheck": "npm run src-closure", - "typecheck": "node tasks/typecheck", + "build-package": "npm run transpile && node tasks/prepare-package", + "build-index": "npm run build-package && node tasks/generate-index", + "build-legacy": "rm -rf build && npm run build-index && rollup --config config/rollup.js && cleancss --source-map src/ol/ol.css -o build/ol.css", + "transpile": "rm -rf build/ol && mkdir -p build && buble --input src/ol --output build/ol --no modules --sourcemap", "apidoc": "jsdoc config/jsdoc/api/index.md -c config/jsdoc/api/conf.json -P package.json -d build/apidoc" }, - "main": "src/ol/index.js", + "main": "index.js", "repository": { "type": "git", "url": "git://github.com/openlayers/openlayers.git" @@ -41,8 +37,7 @@ "rbush": "2.0.2" }, "devDependencies": { - "babel-core": "^6.26.3", - "babel-plugin-jsdoc-closure": "1.5.1", + "buble": "^0.19.3", "buble-loader": "^0.5.1", "chaikin-smooth": "^1.0.4", "clean-css-cli": "4.1.11", @@ -54,7 +49,6 @@ "front-matter": "^2.1.2", "fs-extra": "^6.0.0", "glob": "^7.1.2", - "google-closure-compiler": "20180610.0.2", "handlebars": "4.0.11", "istanbul": "0.4.5", "jquery": "3.3.1", @@ -72,7 +66,6 @@ "mustache": "^2.3.0", "pixelmatch": "^4.0.2", "proj4": "2.4.4", - "recast": "0.15.2", "rollup": "0.62.0", "rollup-plugin-buble": "0.19.2", "rollup-plugin-commonjs": "9.1.3", diff --git a/tasks/generate-index.js b/tasks/generate-index.js index 5bd07243dd..c94638428e 100644 --- a/tasks/generate-index.js +++ b/tasks/generate-index.js @@ -12,12 +12,6 @@ async function getSymbols() { return info.symbols.filter(symbol => symbol.kind != 'member'); } -const srcPath = path.posix.resolve(__dirname, '../src').replace(/\\/g, '/'); -function getPath(name) { - const fullPath = require.resolve(path.resolve('src', name)); - return './' + path.posix.relative(srcPath, fullPath.replace(/\\/g, '/')); -} - /** * Generate a list of imports. * @param {Array.} symbols List of symbols. @@ -31,12 +25,12 @@ function getImports(symbols) { if (defaultExport.length > 1) { const from = defaultExport[0].replace(/^module\:/, './'); const importName = from.replace(/[.\/]+/g, '$'); - const defaultImport = `import ${importName} from '${getPath(from)}';`; + const defaultImport = `import ${importName} from '${from}';`; imports[defaultImport] = true; } else if (namedExport.length > 1) { const from = namedExport[0].replace(/^module\:/, './'); const importName = from.replace(/[.\/]+/g, '_'); - const namedImport = `import * as ${importName} from '${getPath(from)}';`; + const namedImport = `import * as ${importName} from '${from}';`; imports[namedImport] = true; } }); @@ -86,14 +80,14 @@ function generateExports(symbols, namespaces, imports) { } } }); - const nsdefs = ['const ol = window[\'ol\'] = {};']; + const nsdefs = []; const ns = Object.keys(namespaces).sort(); for (let i = 0, ii = ns.length; i < ii; ++i) { if (namespaces[ns[i]]) { nsdefs.push(`${ns[i]} = {};`); } } - blocks = imports.concat(nsdefs.sort()).concat(blocks.sort()); + blocks = imports.concat('\nvar ol = window[\'ol\'] = {};\n', nsdefs.sort()).concat(blocks.sort()); blocks.push(''); return blocks.join('\n'); } @@ -116,7 +110,7 @@ async function main() { */ if (require.main === module) { main().then(async code => { - const filepath = path.join(__dirname, '..', 'src', 'index.js'); + const filepath = path.join(__dirname, '..', 'build', 'index.js'); await fse.outputFile(filepath, code); }).catch(err => { process.stderr.write(`${err.message}\n`, () => process.exit(1)); diff --git a/tasks/prepare-package.js b/tasks/prepare-package.js index 5397b5dc5a..0234f5e830 100644 --- a/tasks/prepare-package.js +++ b/tasks/prepare-package.js @@ -2,26 +2,18 @@ const fs = require('fs'); const path = require('path'); const pkg = require('../package.json'); -const util = require.resolve('../src/ol/util'); -const lines = fs.readFileSync(util, 'utf-8').split('\n'); -const versionRegEx = /const VERSION = '(.*)';$/; -for (let i = 0, ii = lines.length; i < ii; ++i) { - const line = lines[i]; - if (versionRegEx.test(line)) { - lines[i] = line.replace(versionRegEx, `const VERSION = '${pkg.version}';`); - break; - } -} -fs.writeFileSync(util, lines.join('\n'), 'utf-8'); +const buildDir = path.resolve(__dirname, '../build/ol'); -const src = path.join('src', 'ol'); -const packageJson = path.resolve(__dirname, path.join('..', src, 'package.json')); +// update the version number in util.js +const utilPath = path.join(buildDir, 'util.js'); +const versionRegEx = /const VERSION = '(.*)';/g; +const utilSrc = fs.readFileSync(utilPath, 'utf-8').replace(versionRegEx, `const VERSION = '${pkg.version}';`); +fs.writeFileSync(utilPath, utilSrc, 'utf-8'); + +// write out simplified package.json delete pkg.scripts; delete pkg.devDependencies; delete pkg.style; delete pkg.eslintConfig; -const main = path.posix.relative(src, require.resolve(path.join('..', pkg.main))); -pkg.main = pkg.module = main; -pkg.name = 'ol'; - -fs.writeFileSync(packageJson, JSON.stringify(pkg, null, 2), 'utf-8'); +delete pkg.private; +fs.writeFileSync(path.join(buildDir, 'package.json'), JSON.stringify(pkg, null, 2), 'utf-8'); diff --git a/tasks/transform-types.js b/tasks/transform-types.js deleted file mode 100644 index 90ffb7a6e8..0000000000 --- a/tasks/transform-types.js +++ /dev/null @@ -1,31 +0,0 @@ -/** - * @filedesc - * Transforms type comments in all source files to types that Closure Compiler - * understands. - */ - -const glob = require('glob'); -const mkdirp = require('mkdirp').sync; -const fs = require('fs'); -const path = require('path'); -const transform = require('babel-core').transformFileSync; - -const options = { - plugins: 'jsdoc-closure', - parserOpts: { - parser: 'recast' - }, - generatorOpts: { - generator: 'recast' - } -}; - -const outDir = path.join('build', 'src-closure'); - -glob('src/**/*.js', (err, matches) => { - matches.forEach(match => { - const out = path.join(outDir, path.relative('src', match)); - mkdirp(path.dirname(out)); - fs.writeFileSync(out, transform(match, options).code, 'utf-8'); - }); -}); diff --git a/tasks/typecheck.js b/tasks/typecheck.js deleted file mode 100644 index 0a7f667f37..0000000000 --- a/tasks/typecheck.js +++ /dev/null @@ -1,28 +0,0 @@ -const Compiler = require('google-closure-compiler').compiler; - -const compiler = new Compiler({ - js: [ - './build/src-closure/**.js', - // Resolve dependencies - './node_modules/pbf/package.json', './node_modules/pbf/**.js', './node_modules/ieee754/**.js', - './node_modules/pixelworks/package.json', './node_modules/pixelworks/**.js', - './node_modules/rbush/package.json', './node_modules/rbush/**.js', 'node_modules/quickselect/**.js' - ], - entry_point: './build/src-closure/index.js', - module_resolution: 'NODE', - dependency_mode: 'STRICT', - checks_only: true, - jscomp_error: ['newCheckTypes'], - // Options to make dependencies work - process_common_js_modules: true, - hide_warnings_for: 'node_modules' -}); - -compiler.run((exit, out, err) => { - if (exit) { - process.stderr.write(err, () => process.exit(exit)); - } else { - process.stderr.write(err); - process.stdout.write(out); - } -}); From bd7328675a75d0a89574c1da4deb7e13a1fdf63a Mon Sep 17 00:00:00 2001 From: Tim Schaub Date: Tue, 17 Jul 2018 14:55:10 -0600 Subject: [PATCH 071/107] Construct error with message --- src/ol/AssertionError.js | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/ol/AssertionError.js b/src/ol/AssertionError.js index b1ab3ac881..293c6996ae 100644 --- a/src/ol/AssertionError.js +++ b/src/ol/AssertionError.js @@ -16,12 +16,10 @@ class AssertionError extends Error { */ constructor(code) { const path = VERSION.split('-')[0]; + const message = 'Assertion failed. See https://openlayers.org/en/' + path + + '/doc/errors/#' + code + ' for details.'; - /** - * @type {string} - */ - this.message = 'Assertion failed. See https://openlayers.org/en/' + path + - '/doc/errors/#' + code + ' for details.'; + super(message); /** * Error code. The meaning of the code can be found on From 2f4aa5aa053847490e47fdd50cb40a51b04ab51c Mon Sep 17 00:00:00 2001 From: Tim Schaub Date: Tue, 17 Jul 2018 14:56:19 -0600 Subject: [PATCH 072/107] No inherits in ZoomToExtent --- src/ol/control/ZoomToExtent.js | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/ol/control/ZoomToExtent.js b/src/ol/control/ZoomToExtent.js index b55f82aa6f..22a4e659dd 100644 --- a/src/ol/control/ZoomToExtent.js +++ b/src/ol/control/ZoomToExtent.js @@ -85,7 +85,4 @@ class ZoomToExtent extends Control { } } -inherits(ZoomToExtent, Control); - - export default ZoomToExtent; From d855f5ba0aa548b09c434f594b9e8414713f3a72 Mon Sep 17 00:00:00 2001 From: Tim Schaub Date: Tue, 17 Jul 2018 14:57:57 -0600 Subject: [PATCH 073/107] Call super instead of BaseObject --- src/ol/Geolocation.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ol/Geolocation.js b/src/ol/Geolocation.js index 771479504a..5f72759677 100644 --- a/src/ol/Geolocation.js +++ b/src/ol/Geolocation.js @@ -52,7 +52,7 @@ class Geolocation extends BaseObject { */ constructor(opt_options) { - BaseObject.call(this); + super(); const options = opt_options || {}; From 069187859dcc902055d4f75d3f0d7e3f21fafdab Mon Sep 17 00:00:00 2001 From: Tim Schaub Date: Tue, 17 Jul 2018 17:48:27 -0600 Subject: [PATCH 074/107] Cleaned up ol/pointer classes --- src/ol/pointer/EventSource.js | 39 +++-- src/ol/pointer/MouseSource.js | 170 +++++++++---------- src/ol/pointer/MsSource.js | 226 +++++++++++++------------- src/ol/pointer/NativeSource.js | 187 ++++++++++----------- src/ol/pointer/PointerEvent.js | 155 +++++++++--------- src/ol/pointer/PointerEventHandler.js | 17 +- src/ol/pointer/TouchSource.js | 119 +++++++------- 7 files changed, 460 insertions(+), 453 deletions(-) diff --git a/src/ol/pointer/EventSource.js b/src/ol/pointer/EventSource.js index 46547520d3..6f3971506b 100644 --- a/src/ol/pointer/EventSource.js +++ b/src/ol/pointer/EventSource.js @@ -1,42 +1,45 @@ /** * @module ol/pointer/EventSource */ -/** - * @param {module:ol/pointer/PointerEventHandler} dispatcher Event handler. - * @param {!Object.} mapping Event mapping. - * @constructor - */ + class EventSource { + + /** + * @param {module:ol/pointer/PointerEventHandler} dispatcher Event handler. + * @param {!Object.} mapping Event mapping. + */ constructor(dispatcher, mapping) { + /** - * @type {module:ol/pointer/PointerEventHandler} - */ + * @type {module:ol/pointer/PointerEventHandler} + */ this.dispatcher = dispatcher; /** - * @private - * @const - * @type {!Object.} - */ + * @private + * @const + * @type {!Object.} + */ this.mapping_ = mapping; } /** - * List of events supported by this source. - * @return {Array.} Event names - */ + * List of events supported by this source. + * @return {Array.} Event names + */ getEvents() { return Object.keys(this.mapping_); } /** - * Returns the handler that should handle a given event type. - * @param {string} eventType The event type. - * @return {function(Event)} Handler - */ + * Returns the handler that should handle a given event type. + * @param {string} eventType The event type. + * @return {function(Event)} Handler + */ getHandlerForEvent(eventType) { return this.mapping_[eventType]; } + } export default EventSource; diff --git a/src/ol/pointer/MouseSource.js b/src/ol/pointer/MouseSource.js index 1c8c69e345..90b2f27ef7 100644 --- a/src/ol/pointer/MouseSource.js +++ b/src/ol/pointer/MouseSource.js @@ -1,6 +1,7 @@ /** * @module ol/pointer/MouseSource */ + // Based on https://github.com/Polymer/PointerEvents // Copyright (c) 2013 The Polymer Authors. All rights reserved. @@ -31,7 +32,6 @@ // (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 {inherits} from '../util.js'; import EventSource from '../pointer/EventSource.js'; @@ -54,22 +54,97 @@ export const POINTER_TYPE = 'mouse'; */ const DEDUP_DIST = 25; +/** + * Handler for `mousedown`. + * + * @this {module:ol/pointer/MouseSource} + * @param {MouseEvent} inEvent The in event. + */ +function mousedown(inEvent) { + if (!this.isEventSimulatedFromTouch_(inEvent)) { + // TODO(dfreedman) workaround for some elements not sending mouseup + // http://crbug/149091 + if (POINTER_ID.toString() in this.pointerMap) { + this.cancel(inEvent); + } + const e = prepareEvent(inEvent, this.dispatcher); + this.pointerMap[POINTER_ID.toString()] = inEvent; + this.dispatcher.down(e, inEvent); + } +} /** - * @param {module:ol/pointer/PointerEventHandler} dispatcher Event handler. - * @constructor - * @extends {module:ol/pointer/EventSource} + * Handler for `mousemove`. + * + * @this {module:ol/pointer/MouseSource} + * @param {MouseEvent} inEvent The in event. */ -class MouseSource { +function mousemove(inEvent) { + if (!this.isEventSimulatedFromTouch_(inEvent)) { + const e = prepareEvent(inEvent, this.dispatcher); + this.dispatcher.move(e, inEvent); + } +} + +/** + * Handler for `mouseup`. + * + * @this {module:ol/pointer/MouseSource} + * @param {MouseEvent} inEvent The in event. + */ +function mouseup(inEvent) { + if (!this.isEventSimulatedFromTouch_(inEvent)) { + const p = this.pointerMap[POINTER_ID.toString()]; + + if (p && p.button === inEvent.button) { + const e = prepareEvent(inEvent, this.dispatcher); + this.dispatcher.up(e, inEvent); + this.cleanupMouse(); + } + } +} + +/** + * Handler for `mouseover`. + * + * @this {module:ol/pointer/MouseSource} + * @param {MouseEvent} inEvent The in event. + */ +function mouseover(inEvent) { + if (!this.isEventSimulatedFromTouch_(inEvent)) { + const e = prepareEvent(inEvent, this.dispatcher); + this.dispatcher.enterOver(e, inEvent); + } +} + +/** + * Handler for `mouseout`. + * + * @this {module:ol/pointer/MouseSource} + * @param {MouseEvent} inEvent The in event. + */ +function mouseout(inEvent) { + if (!this.isEventSimulatedFromTouch_(inEvent)) { + const e = prepareEvent(inEvent, this.dispatcher); + this.dispatcher.leaveOut(e, inEvent); + } +} + + +class MouseSource extends EventSource { + + /** + * @param {module:ol/pointer/PointerEventHandler} dispatcher Event handler. + */ constructor(dispatcher) { const mapping = { - 'mousedown': this.mousedown, - 'mousemove': this.mousemove, - 'mouseup': this.mouseup, - 'mouseover': this.mouseover, - 'mouseout': this.mouseout + 'mousedown': mousedown, + 'mousemove': mousemove, + 'mouseup': mouseup, + 'mouseover': mouseover, + 'mouseout': mouseout }; - EventSource.call(this, dispatcher, mapping); + super(dispatcher, mapping); /** * @const @@ -123,77 +198,6 @@ class MouseSource { return false; } - /** - * Handler for `mousedown`. - * - * @param {MouseEvent} inEvent The in event. - */ - mousedown(inEvent) { - if (!this.isEventSimulatedFromTouch_(inEvent)) { - // TODO(dfreedman) workaround for some elements not sending mouseup - // http://crbug/149091 - if (POINTER_ID.toString() in this.pointerMap) { - this.cancel(inEvent); - } - const e = prepareEvent(inEvent, this.dispatcher); - this.pointerMap[POINTER_ID.toString()] = inEvent; - this.dispatcher.down(e, inEvent); - } - } - - /** - * Handler for `mousemove`. - * - * @param {MouseEvent} inEvent The in event. - */ - mousemove(inEvent) { - if (!this.isEventSimulatedFromTouch_(inEvent)) { - const e = prepareEvent(inEvent, this.dispatcher); - this.dispatcher.move(e, inEvent); - } - } - - /** - * Handler for `mouseup`. - * - * @param {MouseEvent} inEvent The in event. - */ - mouseup(inEvent) { - if (!this.isEventSimulatedFromTouch_(inEvent)) { - const p = this.pointerMap[POINTER_ID.toString()]; - - if (p && p.button === inEvent.button) { - const e = prepareEvent(inEvent, this.dispatcher); - this.dispatcher.up(e, inEvent); - this.cleanupMouse(); - } - } - } - - /** - * Handler for `mouseover`. - * - * @param {MouseEvent} inEvent The in event. - */ - mouseover(inEvent) { - if (!this.isEventSimulatedFromTouch_(inEvent)) { - const e = prepareEvent(inEvent, this.dispatcher); - this.dispatcher.enterOver(e, inEvent); - } - } - - /** - * Handler for `mouseout`. - * - * @param {MouseEvent} inEvent The in event. - */ - mouseout(inEvent) { - if (!this.isEventSimulatedFromTouch_(inEvent)) { - const e = prepareEvent(inEvent, this.dispatcher); - this.dispatcher.leaveOut(e, inEvent); - } - } - /** * Dispatches a `pointercancel` event. * @@ -213,8 +217,6 @@ class MouseSource { } } -inherits(MouseSource, EventSource); - /** * Creates a copy of the original event that will be used diff --git a/src/ol/pointer/MsSource.js b/src/ol/pointer/MsSource.js index 230b0aba9d..17e174c0c6 100644 --- a/src/ol/pointer/MsSource.js +++ b/src/ol/pointer/MsSource.js @@ -31,7 +31,6 @@ // (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 {inherits} from '../util.js'; import EventSource from '../pointer/EventSource.js'; @@ -47,41 +46,130 @@ const POINTER_TYPES = [ 'mouse' ]; +/** + * Handler for `msPointerDown`. + * + * @this {module:ol/pointer/MsSource} + * @param {MSPointerEvent} inEvent The in event. + */ +function msPointerDown(inEvent) { + this.pointerMap[inEvent.pointerId.toString()] = inEvent; + const e = this.prepareEvent_(inEvent); + this.dispatcher.down(e, inEvent); +} /** - * @param {module:ol/pointer/PointerEventHandler} dispatcher Event handler. - * @constructor - * @extends {module:ol/pointer/EventSource} + * Handler for `msPointerMove`. + * + * @this {module:ol/pointer/MsSource} + * @param {MSPointerEvent} inEvent The in event. */ -class MsSource { +function msPointerMove(inEvent) { + const e = this.prepareEvent_(inEvent); + this.dispatcher.move(e, inEvent); +} + +/** + * Handler for `msPointerUp`. + * + * @this {module:ol/pointer/MsSource} + * @param {MSPointerEvent} inEvent The in event. + */ +function msPointerUp(inEvent) { + const e = this.prepareEvent_(inEvent); + this.dispatcher.up(e, inEvent); + this.cleanup(inEvent.pointerId); +} + +/** + * Handler for `msPointerOut`. + * + * @this {module:ol/pointer/MsSource} + * @param {MSPointerEvent} inEvent The in event. + */ +function msPointerOut(inEvent) { + const e = this.prepareEvent_(inEvent); + this.dispatcher.leaveOut(e, inEvent); +} + +/** + * Handler for `msPointerOver`. + * + * @this {module:ol/pointer/MsSource} + * @param {MSPointerEvent} inEvent The in event. + */ +function msPointerOver(inEvent) { + const e = this.prepareEvent_(inEvent); + this.dispatcher.enterOver(e, inEvent); +} + +/** + * Handler for `msPointerCancel`. + * + * @this {module:ol/pointer/MsSource} + * @param {MSPointerEvent} inEvent The in event. + */ +function msPointerCancel(inEvent) { + const e = this.prepareEvent_(inEvent); + this.dispatcher.cancel(e, inEvent); + this.cleanup(inEvent.pointerId); +} + +/** + * Handler for `msLostPointerCapture`. + * + * @this {module:ol/pointer/MsSource} + * @param {MSPointerEvent} inEvent The in event. + */ +function msLostPointerCapture(inEvent) { + const e = this.dispatcher.makeEvent('lostpointercapture', inEvent, inEvent); + this.dispatcher.dispatchEvent(e); +} + +/** + * Handler for `msGotPointerCapture`. + * + * @this {module:ol/pointer/MsSource} + * @param {MSPointerEvent} inEvent The in event. + */ +function msGotPointerCapture(inEvent) { + const e = this.dispatcher.makeEvent('gotpointercapture', inEvent, inEvent); + this.dispatcher.dispatchEvent(e); +} + +class MsSource extends EventSource { + + /** + * @param {module:ol/pointer/PointerEventHandler} dispatcher Event handler. + */ constructor(dispatcher) { const mapping = { - 'MSPointerDown': this.msPointerDown, - 'MSPointerMove': this.msPointerMove, - 'MSPointerUp': this.msPointerUp, - 'MSPointerOut': this.msPointerOut, - 'MSPointerOver': this.msPointerOver, - 'MSPointerCancel': this.msPointerCancel, - 'MSGotPointerCapture': this.msGotPointerCapture, - 'MSLostPointerCapture': this.msLostPointerCapture + 'MSPointerDown': msPointerDown, + 'MSPointerMove': msPointerMove, + 'MSPointerUp': msPointerUp, + 'MSPointerOut': msPointerOut, + 'MSPointerOver': msPointerOver, + 'MSPointerCancel': msPointerCancel, + 'MSGotPointerCapture': msGotPointerCapture, + 'MSLostPointerCapture': msLostPointerCapture }; - EventSource.call(this, dispatcher, mapping); + super(dispatcher, mapping); /** - * @const - * @type {!Object.} - */ + * @const + * @type {!Object.} + */ this.pointerMap = dispatcher.pointerMap; } /** - * Creates a copy of the original event that will be used - * for the fake pointer event. - * - * @private - * @param {MSPointerEvent} inEvent The in event. - * @return {Object} The copied event. - */ + * Creates a copy of the original event that will be used + * for the fake pointer event. + * + * @private + * @param {MSPointerEvent} inEvent The in event. + * @return {Object} The copied event. + */ prepareEvent_(inEvent) { let e = inEvent; if (typeof inEvent.pointerType === 'number') { @@ -93,97 +181,13 @@ class MsSource { } /** - * Remove this pointer from the list of active pointers. - * @param {number} pointerId Pointer identifier. - */ + * Remove this pointer from the list of active pointers. + * @param {number} pointerId Pointer identifier. + */ cleanup(pointerId) { delete this.pointerMap[pointerId.toString()]; } - /** - * Handler for `msPointerDown`. - * - * @param {MSPointerEvent} inEvent The in event. - */ - msPointerDown(inEvent) { - this.pointerMap[inEvent.pointerId.toString()] = inEvent; - const e = this.prepareEvent_(inEvent); - this.dispatcher.down(e, inEvent); - } - - /** - * Handler for `msPointerMove`. - * - * @param {MSPointerEvent} inEvent The in event. - */ - msPointerMove(inEvent) { - const e = this.prepareEvent_(inEvent); - this.dispatcher.move(e, inEvent); - } - - /** - * Handler for `msPointerUp`. - * - * @param {MSPointerEvent} inEvent The in event. - */ - msPointerUp(inEvent) { - const e = this.prepareEvent_(inEvent); - this.dispatcher.up(e, inEvent); - this.cleanup(inEvent.pointerId); - } - - /** - * Handler for `msPointerOut`. - * - * @param {MSPointerEvent} inEvent The in event. - */ - msPointerOut(inEvent) { - const e = this.prepareEvent_(inEvent); - this.dispatcher.leaveOut(e, inEvent); - } - - /** - * Handler for `msPointerOver`. - * - * @param {MSPointerEvent} inEvent The in event. - */ - msPointerOver(inEvent) { - const e = this.prepareEvent_(inEvent); - this.dispatcher.enterOver(e, inEvent); - } - - /** - * Handler for `msPointerCancel`. - * - * @param {MSPointerEvent} inEvent The in event. - */ - msPointerCancel(inEvent) { - const e = this.prepareEvent_(inEvent); - this.dispatcher.cancel(e, inEvent); - this.cleanup(inEvent.pointerId); - } - - /** - * Handler for `msLostPointerCapture`. - * - * @param {MSPointerEvent} inEvent The in event. - */ - msLostPointerCapture(inEvent) { - const e = this.dispatcher.makeEvent('lostpointercapture', inEvent, inEvent); - this.dispatcher.dispatchEvent(e); - } - - /** - * Handler for `msGotPointerCapture`. - * - * @param {MSPointerEvent} inEvent The in event. - */ - msGotPointerCapture(inEvent) { - const e = this.dispatcher.makeEvent('gotpointercapture', inEvent, inEvent); - this.dispatcher.dispatchEvent(e); - } } -inherits(MsSource, EventSource); - export default MsSource; diff --git a/src/ol/pointer/NativeSource.js b/src/ol/pointer/NativeSource.js index 0ac60c42bd..34986ecbec 100644 --- a/src/ol/pointer/NativeSource.js +++ b/src/ol/pointer/NativeSource.js @@ -1,6 +1,7 @@ /** * @module ol/pointer/NativeSource */ + // Based on https://github.com/Polymer/PointerEvents // Copyright (c) 2013 The Polymer Authors. All rights reserved. @@ -31,103 +32,107 @@ // (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 {inherits} from '../util.js'; import EventSource from '../pointer/EventSource.js'; /** - * @param {module:ol/pointer/PointerEventHandler} dispatcher Event handler. - * @constructor - * @extends {module:ol/pointer/EventSource} + * Handler for `pointerdown`. + * + * @this {module:ol/pointer/NativeSource} + * @param {Event} inEvent The in event. */ -class NativeSource { - constructor(dispatcher) { - const mapping = { - 'pointerdown': this.pointerDown, - 'pointermove': this.pointerMove, - 'pointerup': this.pointerUp, - 'pointerout': this.pointerOut, - 'pointerover': this.pointerOver, - 'pointercancel': this.pointerCancel, - 'gotpointercapture': this.gotPointerCapture, - 'lostpointercapture': this.lostPointerCapture - }; - EventSource.call(this, dispatcher, mapping); - } - - /** - * Handler for `pointerdown`. - * - * @param {Event} inEvent The in event. - */ - pointerDown(inEvent) { - this.dispatcher.fireNativeEvent(inEvent); - } - - /** - * Handler for `pointermove`. - * - * @param {Event} inEvent The in event. - */ - pointerMove(inEvent) { - this.dispatcher.fireNativeEvent(inEvent); - } - - /** - * Handler for `pointerup`. - * - * @param {Event} inEvent The in event. - */ - pointerUp(inEvent) { - this.dispatcher.fireNativeEvent(inEvent); - } - - /** - * Handler for `pointerout`. - * - * @param {Event} inEvent The in event. - */ - pointerOut(inEvent) { - this.dispatcher.fireNativeEvent(inEvent); - } - - /** - * Handler for `pointerover`. - * - * @param {Event} inEvent The in event. - */ - pointerOver(inEvent) { - this.dispatcher.fireNativeEvent(inEvent); - } - - /** - * Handler for `pointercancel`. - * - * @param {Event} inEvent The in event. - */ - pointerCancel(inEvent) { - this.dispatcher.fireNativeEvent(inEvent); - } - - /** - * Handler for `lostpointercapture`. - * - * @param {Event} inEvent The in event. - */ - lostPointerCapture(inEvent) { - this.dispatcher.fireNativeEvent(inEvent); - } - - /** - * Handler for `gotpointercapture`. - * - * @param {Event} inEvent The in event. - */ - gotPointerCapture(inEvent) { - this.dispatcher.fireNativeEvent(inEvent); - } +function pointerDown(inEvent) { + this.dispatcher.fireNativeEvent(inEvent); } -inherits(NativeSource, EventSource); +/** + * Handler for `pointermove`. + * + * @this {module:ol/pointer/NativeSource} + * @param {Event} inEvent The in event. + */ +function pointerMove(inEvent) { + this.dispatcher.fireNativeEvent(inEvent); +} +/** + * Handler for `pointerup`. + * + * @this {module:ol/pointer/NativeSource} + * @param {Event} inEvent The in event. + */ +function pointerUp(inEvent) { + this.dispatcher.fireNativeEvent(inEvent); +} + +/** + * Handler for `pointerout`. + * + * @this {module:ol/pointer/NativeSource} + * @param {Event} inEvent The in event. + */ +function pointerOut(inEvent) { + this.dispatcher.fireNativeEvent(inEvent); +} + +/** + * Handler for `pointerover`. + * + * @this {module:ol/pointer/NativeSource} + * @param {Event} inEvent The in event. + */ +function pointerOver(inEvent) { + this.dispatcher.fireNativeEvent(inEvent); +} + +/** + * Handler for `pointercancel`. + * + * @this {module:ol/pointer/NativeSource} + * @param {Event} inEvent The in event. + */ +function pointerCancel(inEvent) { + this.dispatcher.fireNativeEvent(inEvent); +} + +/** + * Handler for `lostpointercapture`. + * + * @this {module:ol/pointer/NativeSource} + * @param {Event} inEvent The in event. + */ +function lostPointerCapture(inEvent) { + this.dispatcher.fireNativeEvent(inEvent); +} + +/** + * Handler for `gotpointercapture`. + * + * @this {module:ol/pointer/NativeSource} + * @param {Event} inEvent The in event. + */ +function gotPointerCapture(inEvent) { + this.dispatcher.fireNativeEvent(inEvent); +} + +class NativeSource extends EventSource { + + /** + * @param {module:ol/pointer/PointerEventHandler} dispatcher Event handler. + */ + constructor(dispatcher) { + const mapping = { + 'pointerdown': pointerDown, + 'pointermove': pointerMove, + 'pointerup': pointerUp, + 'pointerout': pointerOut, + 'pointerover': pointerOver, + 'pointercancel': pointerCancel, + 'gotpointercapture': gotPointerCapture, + 'lostpointercapture': lostPointerCapture + }; + super(dispatcher, mapping); + } + +} export default NativeSource; diff --git a/src/ol/pointer/PointerEvent.js b/src/ol/pointer/PointerEvent.js index f21ad0aa11..acaffe16c7 100644 --- a/src/ol/pointer/PointerEvent.js +++ b/src/ol/pointer/PointerEvent.js @@ -1,6 +1,7 @@ /** * @module ol/pointer/PointerEvent */ + // Based on https://github.com/Polymer/PointerEvents // Copyright (c) 2013 The Polymer Authors. All rights reserved. @@ -31,7 +32,6 @@ // (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 {inherits} from '../util.js'; import Event from '../events/Event.js'; @@ -42,155 +42,154 @@ import Event from '../events/Event.js'; let HAS_BUTTONS = false; -/** - * A class for pointer events. - * - * This class is used as an abstraction for mouse events, - * touch events and even native pointer events. - * - * @constructor - * @extends {module:ol/events/Event} - * @param {string} type The type of the event to create. - * @param {Event} originalEvent The event. - * @param {Object.=} opt_eventDict An optional dictionary of - * initial event properties. - */ -class PointerEvent { +class PointerEvent extends Event { + + /** + * A class for pointer events. + * + * This class is used as an abstraction for mouse events, + * touch events and even native pointer events. + * + * @param {string} type The type of the event to create. + * @param {Event} originalEvent The event. + * @param {Object.=} opt_eventDict An optional dictionary of + * initial event properties. + */ constructor(type, originalEvent, opt_eventDict) { - Event.call(this, type); + super(type); /** - * @const - * @type {Event} - */ + * @const + * @type {Event} + */ this.originalEvent = originalEvent; const eventDict = opt_eventDict ? opt_eventDict : {}; /** - * @type {number} - */ + * @type {number} + */ this.buttons = this.getButtons_(eventDict); /** - * @type {number} - */ + * @type {number} + */ this.pressure = this.getPressure_(eventDict, this.buttons); // MouseEvent related properties /** - * @type {boolean} - */ + * @type {boolean} + */ this.bubbles = 'bubbles' in eventDict ? eventDict['bubbles'] : false; /** - * @type {boolean} - */ + * @type {boolean} + */ this.cancelable = 'cancelable' in eventDict ? eventDict['cancelable'] : false; /** - * @type {Object} - */ + * @type {Object} + */ this.view = 'view' in eventDict ? eventDict['view'] : null; /** - * @type {number} - */ + * @type {number} + */ this.detail = 'detail' in eventDict ? eventDict['detail'] : null; /** - * @type {number} - */ + * @type {number} + */ this.screenX = 'screenX' in eventDict ? eventDict['screenX'] : 0; /** - * @type {number} - */ + * @type {number} + */ this.screenY = 'screenY' in eventDict ? eventDict['screenY'] : 0; /** - * @type {number} - */ + * @type {number} + */ this.clientX = 'clientX' in eventDict ? eventDict['clientX'] : 0; /** - * @type {number} - */ + * @type {number} + */ this.clientY = 'clientY' in eventDict ? eventDict['clientY'] : 0; /** - * @type {boolean} - */ + * @type {boolean} + */ this.ctrlKey = 'ctrlKey' in eventDict ? eventDict['ctrlKey'] : false; /** - * @type {boolean} - */ + * @type {boolean} + */ this.altKey = 'altKey' in eventDict ? eventDict['altKey'] : false; /** - * @type {boolean} - */ + * @type {boolean} + */ this.shiftKey = 'shiftKey' in eventDict ? eventDict['shiftKey'] : false; /** - * @type {boolean} - */ + * @type {boolean} + */ this.metaKey = 'metaKey' in eventDict ? eventDict['metaKey'] : false; /** - * @type {number} - */ + * @type {number} + */ this.button = 'button' in eventDict ? eventDict['button'] : 0; /** - * @type {Node} - */ + * @type {Node} + */ this.relatedTarget = 'relatedTarget' in eventDict ? eventDict['relatedTarget'] : null; // PointerEvent related properties /** - * @const - * @type {number} - */ + * @const + * @type {number} + */ this.pointerId = 'pointerId' in eventDict ? eventDict['pointerId'] : 0; /** - * @type {number} - */ + * @type {number} + */ this.width = 'width' in eventDict ? eventDict['width'] : 0; /** - * @type {number} - */ + * @type {number} + */ this.height = 'height' in eventDict ? eventDict['height'] : 0; /** - * @type {number} - */ + * @type {number} + */ this.tiltX = 'tiltX' in eventDict ? eventDict['tiltX'] : 0; /** - * @type {number} - */ + * @type {number} + */ this.tiltY = 'tiltY' in eventDict ? eventDict['tiltY'] : 0; /** - * @type {string} - */ + * @type {string} + */ this.pointerType = 'pointerType' in eventDict ? eventDict['pointerType'] : ''; /** - * @type {number} - */ + * @type {number} + */ this.hwTimestamp = 'hwTimestamp' in eventDict ? eventDict['hwTimestamp'] : 0; /** - * @type {boolean} - */ + * @type {boolean} + */ this.isPrimary = 'isPrimary' in eventDict ? eventDict['isPrimary'] : false; // keep the semantics of preventDefault @@ -202,10 +201,10 @@ class PointerEvent { } /** - * @private - * @param {Object.} eventDict The event dictionary. - * @return {number} Button indicator. - */ + * @private + * @param {Object.} 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 @@ -243,11 +242,11 @@ class PointerEvent { } /** - * @private - * @param {Object.} eventDict The event dictionary. - * @param {number} buttons Button indicator. - * @return {number} The pressure. - */ + * @private + * @param {Object.} 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. @@ -261,8 +260,6 @@ class PointerEvent { } } -inherits(PointerEvent, Event); - /** * Checks if the `buttons` property is supported. diff --git a/src/ol/pointer/PointerEventHandler.js b/src/ol/pointer/PointerEventHandler.js index aa9becd009..a7a350129d 100644 --- a/src/ol/pointer/PointerEventHandler.js +++ b/src/ol/pointer/PointerEventHandler.js @@ -1,6 +1,7 @@ /** * @module ol/pointer/PointerEventHandler */ + // Based on https://github.com/Polymer/PointerEvents // Copyright (c) 2013 The Polymer Authors. All rights reserved. @@ -31,7 +32,6 @@ // (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 {inherits} from '../util.js'; import {listen, unlisten} from '../events.js'; import EventTarget from '../events/EventTarget.js'; import {POINTER, MSPOINTER, TOUCH} from '../has.js'; @@ -83,14 +83,13 @@ const CLONE_PROPS = [ ]; -/** - * @constructor - * @extends {module:ol/events/EventTarget} - * @param {Element|HTMLDocument} element Viewport element. - */ -class PointerEventHandler { +class PointerEventHandler extends EventTarget { + + /** + * @param {Element|HTMLDocument} element Viewport element. + */ constructor(element) { - EventTarget.call(this); + super(); /** * @const @@ -416,6 +415,4 @@ class PointerEventHandler { } } -inherits(PointerEventHandler, EventTarget); - export default PointerEventHandler; diff --git a/src/ol/pointer/TouchSource.js b/src/ol/pointer/TouchSource.js index 92d34fa54d..b5331144a4 100644 --- a/src/ol/pointer/TouchSource.js +++ b/src/ol/pointer/TouchSource.js @@ -1,6 +1,7 @@ /** * @module ol/pointer/TouchSource */ + // Based on https://github.com/Polymer/PointerEvents // Copyright (c) 2013 The Polymer Authors. All rights reserved. @@ -31,7 +32,6 @@ // (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 {inherits} from '../util.js'; import {remove} from '../array.js'; import EventSource from '../pointer/EventSource.js'; import {POINTER_ID} from '../pointer/MouseSource.js'; @@ -42,28 +42,75 @@ import {POINTER_ID} from '../pointer/MouseSource.js'; */ const CLICK_COUNT_TIMEOUT = 200; - /** * @type {string} */ const POINTER_TYPE = 'touch'; +/** + * Handler for `touchstart`, triggers `pointerover`, + * `pointerenter` and `pointerdown` events. + * + * @this {module:ol/pointer/TouchSource} + * @param {TouchEvent} inEvent The in event. + */ +function touchstart(inEvent) { + this.vacuumTouches_(inEvent); + this.setPrimaryTouch_(inEvent.changedTouches[0]); + this.dedupSynthMouse_(inEvent); + this.clickCount_++; + this.processTouches_(inEvent, this.overDown_); +} /** - * @constructor - * @param {module:ol/pointer/PointerEventHandler} dispatcher The event handler. - * @param {module:ol/pointer/MouseSource} mouseSource Mouse source. - * @extends {module:ol/pointer/EventSource} + * Handler for `touchmove`. + * + * @this {module:ol/pointer/TouchSource} + * @param {TouchEvent} inEvent The in event. */ -class TouchSource { +function touchmove(inEvent) { + inEvent.preventDefault(); + this.processTouches_(inEvent, this.moveOverOut_); +} + +/** + * Handler for `touchend`, triggers `pointerup`, + * `pointerout` and `pointerleave` events. + * + * @this {module:ol/pointer/TouchSource} + * @param {TouchEvent} inEvent The event. + */ +function touchend(inEvent) { + this.dedupSynthMouse_(inEvent); + this.processTouches_(inEvent, this.upOut_); +} + +/** + * Handler for `touchcancel`, triggers `pointercancel`, + * `pointerout` and `pointerleave` events. + * + * @this {module:ol/pointer/TouchSource} + * @param {TouchEvent} inEvent The in event. + */ +function touchcancel(inEvent) { + this.processTouches_(inEvent, this.cancelOut_); +} + + +class TouchSource extends EventSource { + + /** + * @param {module:ol/pointer/PointerEventHandler} dispatcher The event handler. + * @param {module:ol/pointer/MouseSource} mouseSource Mouse source. + */ constructor(dispatcher, mouseSource) { const mapping = { - 'touchstart': this.touchstart, - 'touchmove': this.touchmove, - 'touchend': this.touchend, - 'touchcancel': this.touchcancel + 'touchstart': touchstart, + 'touchmove': touchmove, + 'touchend': touchend, + 'touchcancel': touchcancel }; - EventSource.call(this, dispatcher, mapping); + super(dispatcher, mapping); /** * @const @@ -269,20 +316,6 @@ class TouchSource { } } - /** - * Handler for `touchstart`, triggers `pointerover`, - * `pointerenter` and `pointerdown` events. - * - * @param {TouchEvent} inEvent The in event. - */ - touchstart(inEvent) { - this.vacuumTouches_(inEvent); - this.setPrimaryTouch_(inEvent.changedTouches[0]); - this.dedupSynthMouse_(inEvent); - this.clickCount_++; - this.processTouches_(inEvent, this.overDown_); - } - /** * @private * @param {TouchEvent} browserEvent The event. @@ -299,16 +332,6 @@ class TouchSource { this.dispatcher.down(inPointer, browserEvent); } - /** - * Handler for `touchmove`. - * - * @param {TouchEvent} inEvent The in event. - */ - touchmove(inEvent) { - inEvent.preventDefault(); - this.processTouches_(inEvent, this.moveOverOut_); - } - /** * @private * @param {TouchEvent} browserEvent The event. @@ -343,17 +366,6 @@ class TouchSource { pointer.outTarget = event.target; } - /** - * Handler for `touchend`, triggers `pointerup`, - * `pointerout` and `pointerleave` events. - * - * @param {TouchEvent} inEvent The event. - */ - touchend(inEvent) { - this.dedupSynthMouse_(inEvent); - this.processTouches_(inEvent, this.upOut_); - } - /** * @private * @param {TouchEvent} browserEvent An event. @@ -366,16 +378,6 @@ class TouchSource { this.cleanUpPointer_(inPointer); } - /** - * Handler for `touchcancel`, triggers `pointercancel`, - * `pointerout` and `pointerleave` events. - * - * @param {TouchEvent} inEvent The in event. - */ - touchcancel(inEvent) { - this.processTouches_(inEvent, this.cancelOut_); - } - /** * @private * @param {TouchEvent} browserEvent The event. @@ -420,7 +422,4 @@ class TouchSource { } } -inherits(TouchSource, EventSource); - - export default TouchSource; From 45e0926896d501d4a5a33564650302fd6fe11cc7 Mon Sep 17 00:00:00 2001 From: Tim Schaub Date: Tue, 17 Jul 2018 17:54:24 -0600 Subject: [PATCH 075/107] Cleaned up ol/proj --- src/ol/proj/epsg3857.js | 9 ++------- src/ol/proj/epsg4326.js | 10 ++-------- 2 files changed, 4 insertions(+), 15 deletions(-) diff --git a/src/ol/proj/epsg3857.js b/src/ol/proj/epsg3857.js index 0235f69223..9cd1c1684a 100644 --- a/src/ol/proj/epsg3857.js +++ b/src/ol/proj/epsg3857.js @@ -1,7 +1,6 @@ /** * @module ol/proj/epsg3857 */ -import {inherits} from '../util.js'; import {cosh} from '../math.js'; import Projection from '../proj/Projection.js'; import Units from '../proj/Units.js'; @@ -43,16 +42,14 @@ export const WORLD_EXTENT = [-180, -85, 180, 85]; /** * @classdesc * Projection object for web/spherical Mercator (EPSG:3857). - * - * @extends {module:ol/proj/Projection} */ -class EPSG3857Projection { +class EPSG3857Projection extends Projection { /** * @param {string} code Code. */ constructor(code) { - Projection.call(this, { + super({ code: code, units: Units.METERS, extent: EXTENT, @@ -67,8 +64,6 @@ class EPSG3857Projection { } -inherits(EPSG3857Projection, Projection); - /** * Projections equal to EPSG:3857. diff --git a/src/ol/proj/epsg4326.js b/src/ol/proj/epsg4326.js index d626467d91..7001b8eab5 100644 --- a/src/ol/proj/epsg4326.js +++ b/src/ol/proj/epsg4326.js @@ -1,7 +1,6 @@ /** * @module ol/proj/epsg4326 */ -import {inherits} from '../util.js'; import Projection from '../proj/Projection.js'; import Units from '../proj/Units.js'; @@ -38,18 +37,15 @@ export const METERS_PER_UNIT = Math.PI * RADIUS / 180; * Note that OpenLayers does not strictly comply with the EPSG definition. * The EPSG registry defines 4326 as a CRS for Latitude,Longitude (y,x). * OpenLayers treats EPSG:4326 as a pseudo-projection, with x,y coordinates. - * - * @constructor - * @extends {module:ol/proj/Projection} */ -class EPSG4326Projection { +class EPSG4326Projection extends Projection { /** * @param {string} code Code. * @param {string=} opt_axisOrientation Axis orientation. */ constructor(code, opt_axisOrientation) { - Projection.call(this, { + super({ code: code, units: Units.DEGREES, extent: EXTENT, @@ -63,8 +59,6 @@ class EPSG4326Projection { } -inherits(EPSG4326Projection, Projection); - /** * Projections equal to EPSG:4326. From 792e7e54f1fbcbb9f074357dfd2cb27327c4dc75 Mon Sep 17 00:00:00 2001 From: Tim Schaub Date: Tue, 17 Jul 2018 18:15:47 -0600 Subject: [PATCH 076/107] Clean up ol/renderer/canvas classes --- src/ol/renderer/canvas/ImageLayer.js | 22 ++++++++--------- src/ol/renderer/canvas/IntermediateCanvas.js | 17 +++++-------- src/ol/renderer/canvas/Layer.js | 18 +++++--------- src/ol/renderer/canvas/Map.js | 18 ++++++-------- src/ol/renderer/canvas/TileLayer.js | 24 +++++++++--------- src/ol/renderer/canvas/VectorLayer.js | 19 ++++++-------- src/ol/renderer/canvas/VectorTileLayer.js | 26 ++++++++------------ 7 files changed, 58 insertions(+), 86 deletions(-) diff --git a/src/ol/renderer/canvas/ImageLayer.js b/src/ol/renderer/canvas/ImageLayer.js index f0006e4621..ea726ba167 100644 --- a/src/ol/renderer/canvas/ImageLayer.js +++ b/src/ol/renderer/canvas/ImageLayer.js @@ -2,7 +2,6 @@ * @module ol/renderer/canvas/ImageLayer */ import {ENABLE_RASTER_REPROJECTION} from '../../reproj/common.js'; -import {inherits} from '../../util.js'; import ImageCanvas from '../../ImageCanvas.js'; import LayerType from '../../LayerType.js'; import ViewHint from '../../ViewHint.js'; @@ -11,19 +10,20 @@ import {getHeight, getIntersection, getWidth, isEmpty} from '../../extent.js'; import VectorRenderType from '../../layer/VectorRenderType.js'; import {assign} from '../../obj.js'; import {layerRendererConstructors} from './Map.js'; -import IntermediateCanvasRenderer from '../canvas/IntermediateCanvas.js'; +import IntermediateCanvasRenderer from './IntermediateCanvas.js'; import {create as createTransform, compose as composeTransform} from '../../transform.js'; -/** - * @constructor - * @extends {module:ol/renderer/canvas/IntermediateCanvas} - * @param {module:ol/layer/Image|module:ol/layer/Vector} imageLayer Image or vector layer. - * @api - */ -class CanvasImageLayerRenderer { +class CanvasImageLayerRenderer extends IntermediateCanvasRenderer { + + /** + * @constructor + * @extends {module:ol/renderer/canvas/IntermediateCanvas} + * @param {module:ol/layer/Image|module:ol/layer/Vector} imageLayer Image or vector layer. + * @api + */ constructor(imageLayer) { - IntermediateCanvasRenderer.call(this, imageLayer); + super(imageLayer); /** * @private @@ -187,8 +187,6 @@ class CanvasImageLayerRenderer { } } -inherits(CanvasImageLayerRenderer, IntermediateCanvasRenderer); - /** * Determine if this renderer handles the provided layer. diff --git a/src/ol/renderer/canvas/IntermediateCanvas.js b/src/ol/renderer/canvas/IntermediateCanvas.js index f2147f829d..8d8a185ebe 100644 --- a/src/ol/renderer/canvas/IntermediateCanvas.js +++ b/src/ol/renderer/canvas/IntermediateCanvas.js @@ -1,7 +1,6 @@ /** * @module ol/renderer/canvas/IntermediateCanvas */ -import {inherits} from '../../util.js'; import {scale as scaleCoordinate} from '../../coordinate.js'; import {createCanvasContext2D} from '../../dom.js'; import {containsExtent, intersects} from '../../extent.js'; @@ -9,16 +8,14 @@ import {UNDEFINED} from '../../functions.js'; import CanvasLayerRenderer from '../canvas/Layer.js'; import {create as createTransform, apply as applyTransform} from '../../transform.js'; -/** - * @constructor - * @abstract - * @extends {module:ol/renderer/canvas/Layer} - * @param {module:ol/layer/Layer} layer Layer. - */ -class IntermediateCanvasRenderer { +class IntermediateCanvasRenderer extends CanvasLayerRenderer { + + /** + * @param {module:ol/layer/Layer} layer Layer. + */ constructor(layer) { - CanvasLayerRenderer.call(this, layer); + super(layer); /** * @protected @@ -143,7 +140,5 @@ class IntermediateCanvasRenderer { } } -inherits(IntermediateCanvasRenderer, CanvasLayerRenderer); - export default IntermediateCanvasRenderer; diff --git a/src/ol/renderer/canvas/Layer.js b/src/ol/renderer/canvas/Layer.js index 30d1d166b8..eac68ada1d 100644 --- a/src/ol/renderer/canvas/Layer.js +++ b/src/ol/renderer/canvas/Layer.js @@ -1,7 +1,6 @@ /** * @module ol/renderer/canvas/Layer */ -import {inherits} from '../../util.js'; import {getBottomLeft, getBottomRight, getTopLeft, getTopRight} from '../../extent.js'; import {TRUE} from '../../functions.js'; import RenderEvent from '../../render/Event.js'; @@ -11,16 +10,14 @@ import CanvasImmediateRenderer from '../../render/canvas/Immediate.js'; import LayerRenderer from '../Layer.js'; import {create as createTransform, apply as applyTransform, compose as composeTransform} from '../../transform.js'; -/** - * @constructor - * @abstract - * @extends {module:ol/renderer/Layer} - * @param {module:ol/layer/Layer} layer Layer. - */ -class CanvasLayerRenderer { +class CanvasLayerRenderer extends LayerRenderer { + + /** + * @param {module:ol/layer/Layer} layer Layer. + */ constructor(layer) { - LayerRenderer.call(this, layer); + super(layer); /** * @protected @@ -181,7 +178,4 @@ class CanvasLayerRenderer { prepareFrame(frameState, layerState) {} } -inherits(CanvasLayerRenderer, LayerRenderer); - - export default CanvasLayerRenderer; diff --git a/src/ol/renderer/canvas/Map.js b/src/ol/renderer/canvas/Map.js index 7e5ea1ad2a..a41ab50e73 100644 --- a/src/ol/renderer/canvas/Map.js +++ b/src/ol/renderer/canvas/Map.js @@ -2,7 +2,6 @@ * @module ol/renderer/canvas/Map */ import {create as createTransform, apply as applyTransform, compose as composeTransform} from '../../transform.js'; -import {inherits} from '../../util.js'; import {includes, stableSort} from '../../array.js'; import {CLASS_UNSELECTABLE} from '../../css.js'; import {createCanvasContext2D} from '../../dom.js'; @@ -21,15 +20,14 @@ import SourceState from '../../source/State.js'; export const layerRendererConstructors = []; -/** - * @constructor - * @extends {module:ol/renderer/Map} - * @param {module:ol/PluggableMap} map Map. - * @api - */ -class CanvasMapRenderer { +class CanvasMapRenderer extends MapRenderer { + + /** + * @param {module:ol/PluggableMap} map Map. + * @api + */ constructor(map) { - MapRenderer.call(this, map); + super(map); const container = map.getViewport(); @@ -218,7 +216,5 @@ class CanvasMapRenderer { } } -inherits(CanvasMapRenderer, MapRenderer); - export default CanvasMapRenderer; diff --git a/src/ol/renderer/canvas/TileLayer.js b/src/ol/renderer/canvas/TileLayer.js index b0069fa973..7f406795d1 100644 --- a/src/ol/renderer/canvas/TileLayer.js +++ b/src/ol/renderer/canvas/TileLayer.js @@ -1,7 +1,7 @@ /** * @module ol/renderer/canvas/TileLayer */ -import {getUid, inherits} from '../../util.js'; +import {getUid} from '../../util.js'; import LayerType from '../../LayerType.js'; import TileRange from '../../TileRange.js'; import TileState from '../../TileState.js'; @@ -11,22 +11,22 @@ import {containsExtent, createEmpty, equals, getIntersection, isEmpty} from '../ import IntermediateCanvasRenderer from '../canvas/IntermediateCanvas.js'; import {create as createTransform, compose as composeTransform} from '../../transform.js'; -/** - * @constructor - * @extends {module:ol/renderer/canvas/IntermediateCanvas} - * @param {module:ol/layer/Tile|module:ol/layer/VectorTile} tileLayer Tile layer. - * @api - */ -class CanvasTileLayerRenderer { - constructor(tileLayer) { +class CanvasTileLayerRenderer extends IntermediateCanvasRenderer { - IntermediateCanvasRenderer.call(this, tileLayer); + /** + * @param {module:ol/layer/Tile|module:ol/layer/VectorTile} tileLayer Tile layer. + * @param {boolean=} opt_noContext Skip the context creation. + * @api + */ + constructor(tileLayer, opt_noContext) { + + super(tileLayer); /** * @protected * @type {CanvasRenderingContext2D} */ - this.context = this.context === null ? null : createCanvasContext2D(); + this.context = opt_noContext ? null : createCanvasContext2D(); /** * @private @@ -351,8 +351,6 @@ class CanvasTileLayerRenderer { } } -inherits(CanvasTileLayerRenderer, IntermediateCanvasRenderer); - /** * Determine if this renderer handles the provided layer. diff --git a/src/ol/renderer/canvas/VectorLayer.js b/src/ol/renderer/canvas/VectorLayer.js index 206c5f5c49..b59881720f 100644 --- a/src/ol/renderer/canvas/VectorLayer.js +++ b/src/ol/renderer/canvas/VectorLayer.js @@ -1,7 +1,7 @@ /** * @module ol/renderer/canvas/VectorLayer */ -import {getUid, inherits} from '../../util.js'; +import {getUid} from '../../util.js'; import LayerType from '../../LayerType.js'; import ViewHint from '../../ViewHint.js'; import {createCanvasContext2D} from '../../dom.js'; @@ -15,16 +15,15 @@ import CanvasReplayGroup from '../../render/canvas/ReplayGroup.js'; import CanvasLayerRenderer from '../canvas/Layer.js'; import {defaultOrder as defaultRenderOrder, getTolerance as getRenderTolerance, getSquaredTolerance as getSquaredRenderTolerance, renderFeature} from '../vector.js'; -/** - * @constructor - * @extends {module:ol/renderer/canvas/Layer} - * @param {module:ol/layer/Vector} vectorLayer Vector layer. - * @api - */ -class CanvasVectorLayerRenderer { +class CanvasVectorLayerRenderer extends CanvasLayerRenderer { + + /** + * @param {module:ol/layer/Vector} vectorLayer Vector layer. + * @api + */ constructor(vectorLayer) { - CanvasLayerRenderer.call(this, vectorLayer); + super(vectorLayer); /** * Declutter tree. @@ -407,8 +406,6 @@ class CanvasVectorLayerRenderer { } } -inherits(CanvasVectorLayerRenderer, CanvasLayerRenderer); - /** * Determine if this renderer handles the provided layer. diff --git a/src/ol/renderer/canvas/VectorTileLayer.js b/src/ol/renderer/canvas/VectorTileLayer.js index deb400bafd..49256a4fca 100644 --- a/src/ol/renderer/canvas/VectorTileLayer.js +++ b/src/ol/renderer/canvas/VectorTileLayer.js @@ -1,7 +1,7 @@ /** * @module ol/renderer/canvas/VectorTileLayer */ -import {getUid, inherits} from '../../util.js'; +import {getUid} from '../../util.js'; import LayerType from '../../LayerType.js'; import TileState from '../../TileState.js'; import {createCanvasContext2D} from '../../dom.js'; @@ -47,21 +47,17 @@ const VECTOR_REPLAYS = { }; -/** - * @constructor - * @extends {module:ol/renderer/canvas/TileLayer} - * @param {module:ol/layer/VectorTile} layer VectorTile layer. - * @api - */ -class CanvasVectorTileLayerRenderer { +class CanvasVectorTileLayerRenderer extends CanvasTileLayerRenderer { + + /** + * @constructor + * @extends {module:ol/renderer/canvas/TileLayer} + * @param {module:ol/layer/VectorTile} layer VectorTile layer. + * @api + */ constructor(layer) { - /** - * @type {CanvasRenderingContext2D} - */ - this.context = null; - - CanvasTileLayerRenderer.call(this, layer); + super(layer, true); /** * Declutter tree. @@ -474,8 +470,6 @@ class CanvasVectorTileLayerRenderer { } } -inherits(CanvasVectorTileLayerRenderer, CanvasTileLayerRenderer); - /** * Determine if this renderer handles the provided layer. From 9ee5edf19c3d3e27362a6a9df6306b965144d203 Mon Sep 17 00:00:00 2001 From: Tim Schaub Date: Tue, 17 Jul 2018 18:23:52 -0600 Subject: [PATCH 077/107] Clean up ol/renderer/webgl classes --- src/ol/renderer/webgl/ImageLayer.js | 20 ++++++++------------ src/ol/renderer/webgl/Layer.js | 19 +++++++------------ src/ol/renderer/webgl/Map.js | 18 +++++++----------- src/ol/renderer/webgl/TileLayer.js | 20 ++++++++------------ src/ol/renderer/webgl/VectorLayer.js | 21 +++++++++------------ 5 files changed, 39 insertions(+), 59 deletions(-) diff --git a/src/ol/renderer/webgl/ImageLayer.js b/src/ol/renderer/webgl/ImageLayer.js index 644d9c0c76..6cf9a4e28b 100644 --- a/src/ol/renderer/webgl/ImageLayer.js +++ b/src/ol/renderer/webgl/ImageLayer.js @@ -2,7 +2,6 @@ * @module ol/renderer/webgl/ImageLayer */ import {ENABLE_RASTER_REPROJECTION} from '../../reproj/common.js'; -import {inherits} from '../../util.js'; import {TRUE, UNDEFINED} from '../../functions.js'; import LayerType from '../../LayerType.js'; import ViewHint from '../../ViewHint.js'; @@ -22,17 +21,16 @@ import { import {CLAMP_TO_EDGE} from '../../webgl.js'; import {createTexture} from '../../webgl/Context.js'; -/** - * @constructor - * @extends {module:ol/renderer/webgl/Layer} - * @param {module:ol/renderer/webgl/Map} mapRenderer Map renderer. - * @param {module:ol/layer/Image} imageLayer Tile layer. - * @api - */ -class WebGLImageLayerRenderer { +class WebGLImageLayerRenderer extends WebGLLayerRenderer { + + /** + * @param {module:ol/renderer/webgl/Map} mapRenderer Map renderer. + * @param {module:ol/layer/Image} imageLayer Tile layer. + * @api + */ constructor(mapRenderer, imageLayer) { - WebGLLayerRenderer.call(this, mapRenderer, imageLayer); + super(mapRenderer, imageLayer); /** * The last rendered image. @@ -310,8 +308,6 @@ class WebGLImageLayerRenderer { } } -inherits(WebGLImageLayerRenderer, WebGLLayerRenderer); - /** * Determine if this renderer handles the provided layer. diff --git a/src/ol/renderer/webgl/Layer.js b/src/ol/renderer/webgl/Layer.js index 0fffb2fe21..06fb3eedf3 100644 --- a/src/ol/renderer/webgl/Layer.js +++ b/src/ol/renderer/webgl/Layer.js @@ -1,7 +1,6 @@ /** * @module ol/renderer/webgl/Layer */ -import {inherits} from '../../util.js'; import RenderEvent from '../../render/Event.js'; import RenderEventType from '../../render/EventType.js'; import WebGLImmediateRenderer from '../../render/webgl/Immediate.js'; @@ -15,17 +14,15 @@ import {ARRAY_BUFFER, FRAMEBUFFER, FLOAT, TEXTURE_2D, import WebGLBuffer from '../../webgl/Buffer.js'; import {createEmptyTexture} from '../../webgl/Context.js'; -/** - * @constructor - * @abstract - * @extends {module:ol/renderer/Layer} - * @param {module:ol/renderer/webgl/Map} mapRenderer Map renderer. - * @param {module:ol/layer/Layer} layer Layer. - */ -class WebGLLayerRenderer { +class WebGLLayerRenderer extends LayerRenderer { + + /** + * @param {module:ol/renderer/webgl/Map} mapRenderer Map renderer. + * @param {module:ol/layer/Layer} layer Layer. + */ constructor(mapRenderer, layer) { - LayerRenderer.call(this, layer); + super(layer); /** * @protected @@ -254,7 +251,5 @@ class WebGLLayerRenderer { forEachLayerAtPixel(pixel, frameState, callback, thisArg) {} } -inherits(WebGLLayerRenderer, LayerRenderer); - export default WebGLLayerRenderer; diff --git a/src/ol/renderer/webgl/Map.js b/src/ol/renderer/webgl/Map.js index 8d4c254aa8..d951d3fa5e 100644 --- a/src/ol/renderer/webgl/Map.js +++ b/src/ol/renderer/webgl/Map.js @@ -2,7 +2,6 @@ * @module ol/renderer/webgl/Map */ -import {inherits} from '../../util.js'; import {stableSort} from '../../array.js'; import {CLASS_UNSELECTABLE} from '../../css.js'; import {createCanvasContext2D} from '../../dom.js'; @@ -38,15 +37,14 @@ import ContextEventType from '../../webgl/ContextEventType.js'; const WEBGL_TEXTURE_CACHE_HIGH_WATER_MARK = 1024; -/** - * @constructor - * @extends {module:ol/renderer/Map} - * @param {module:ol/PluggableMap} map Map. - * @api - */ -class WebGLMapRenderer { +class WebGLMapRenderer extends MapRenderer { + + /** + * @param {module:ol/PluggableMap} map Map. + * @api + */ constructor(map) { - MapRenderer.call(this, map); + super(map); const container = map.getViewport(); @@ -576,7 +574,5 @@ class WebGLMapRenderer { } } -inherits(WebGLMapRenderer, MapRenderer); - export default WebGLMapRenderer; diff --git a/src/ol/renderer/webgl/TileLayer.js b/src/ol/renderer/webgl/TileLayer.js index dfc9161f8d..5fb00b1aae 100644 --- a/src/ol/renderer/webgl/TileLayer.js +++ b/src/ol/renderer/webgl/TileLayer.js @@ -4,7 +4,6 @@ // FIXME large resolutions lead to too large framebuffers :-( // FIXME animated shaders! check in redraw -import {inherits} from '../../util.js'; import LayerType from '../../LayerType.js'; import TileRange from '../../TileRange.js'; import TileState from '../../TileState.js'; @@ -25,17 +24,16 @@ import { import {COLOR_BUFFER_BIT, BLEND, ARRAY_BUFFER, FLOAT, LINEAR, TRIANGLE_STRIP} from '../../webgl.js'; import WebGLBuffer from '../../webgl/Buffer.js'; -/** - * @constructor - * @extends {module:ol/renderer/webgl/Layer} - * @param {module:ol/renderer/webgl/Map} mapRenderer Map renderer. - * @param {module:ol/layer/Tile} tileLayer Tile layer. - * @api - */ -class WebGLTileLayerRenderer { +class WebGLTileLayerRenderer extends WebGLLayerRenderer { + + /** + * @param {module:ol/renderer/webgl/Map} mapRenderer Map renderer. + * @param {module:ol/layer/Tile} tileLayer Tile layer. + * @api + */ constructor(mapRenderer, tileLayer) { - WebGLLayerRenderer.call(this, mapRenderer, tileLayer); + super(mapRenderer, tileLayer); /** * @private @@ -382,8 +380,6 @@ class WebGLTileLayerRenderer { } } -inherits(WebGLTileLayerRenderer, WebGLLayerRenderer); - /** * Determine if this renderer handles the provided layer. diff --git a/src/ol/renderer/webgl/VectorLayer.js b/src/ol/renderer/webgl/VectorLayer.js index 02016204b3..7efee54e5c 100644 --- a/src/ol/renderer/webgl/VectorLayer.js +++ b/src/ol/renderer/webgl/VectorLayer.js @@ -1,7 +1,7 @@ /** * @module ol/renderer/webgl/VectorLayer */ -import {getUid, inherits} from '../../util.js'; +import {getUid} from '../../util.js'; import LayerType from '../../LayerType.js'; import ViewHint from '../../ViewHint.js'; import {buffer, containsExtent, createEmpty} from '../../extent.js'; @@ -10,17 +10,16 @@ import {defaultOrder as defaultRenderOrder, getTolerance as getRenderTolerance, import WebGLLayerRenderer from '../webgl/Layer.js'; import {apply as applyTransform} from '../../transform.js'; -/** - * @constructor - * @extends {module:ol/renderer/webgl/Layer} - * @param {module:ol/renderer/webgl/Map} mapRenderer Map renderer. - * @param {module:ol/layer/Vector} vectorLayer Vector layer. - * @api - */ -class WebGLVectorLayerRenderer { +class WebGLVectorLayerRenderer extends WebGLLayerRenderer { + + /** + * @param {module:ol/renderer/webgl/Map} mapRenderer Map renderer. + * @param {module:ol/layer/Vector} vectorLayer Vector layer. + * @api + */ constructor(mapRenderer, vectorLayer) { - WebGLLayerRenderer.call(this, mapRenderer, vectorLayer); + super(mapRenderer, vectorLayer); /** * @private @@ -299,8 +298,6 @@ class WebGLVectorLayerRenderer { } } -inherits(WebGLVectorLayerRenderer, WebGLLayerRenderer); - /** * Determine if this renderer handles the provided layer. From c4e5a7467e3593931c49ed89e3a449212b28cb2d Mon Sep 17 00:00:00 2001 From: Tim Schaub Date: Tue, 17 Jul 2018 18:26:51 -0600 Subject: [PATCH 078/107] Clean up ol/renderer classes --- src/ol/renderer/Layer.js | 20 ++++++++------------ src/ol/renderer/Map.js | 20 ++++++++------------ 2 files changed, 16 insertions(+), 24 deletions(-) diff --git a/src/ol/renderer/Layer.js b/src/ol/renderer/Layer.js index f6014665f3..99b5c34e59 100644 --- a/src/ol/renderer/Layer.js +++ b/src/ol/renderer/Layer.js @@ -1,7 +1,7 @@ /** * @module ol/renderer/Layer */ -import {getUid, inherits} from '../util.js'; +import {getUid} from '../util.js'; import ImageState from '../ImageState.js'; import Observable from '../Observable.js'; import TileState from '../TileState.js'; @@ -10,16 +10,15 @@ import EventType from '../events/EventType.js'; import {FALSE, UNDEFINED} from '../functions.js'; import SourceState from '../source/State.js'; -/** - * @constructor - * @extends {module:ol/Observable} - * @param {module:ol/layer/Layer} layer Layer. - * @struct - */ -class LayerRenderer { +class LayerRenderer extends Observable { + + /** + * @param {module:ol/layer/Layer} layer Layer. + * @struct + */ constructor(layer) { - Observable.call(this); + super(); /** * @private @@ -27,7 +26,6 @@ class LayerRenderer { */ this.layer_ = layer; - } /** @@ -221,8 +219,6 @@ class LayerRenderer { } } -inherits(LayerRenderer, Observable); - /** * @param {module:ol/coordinate~Coordinate} coordinate Coordinate. diff --git a/src/ol/renderer/Map.js b/src/ol/renderer/Map.js index f914b0b9ba..2127802d60 100644 --- a/src/ol/renderer/Map.js +++ b/src/ol/renderer/Map.js @@ -1,7 +1,7 @@ /** * @module ol/renderer/Map */ -import {getUid, inherits} from '../util.js'; +import {getUid} from '../util.js'; import Disposable from '../Disposable.js'; import {listen, unlistenByKey} from '../events.js'; import EventType from '../events/EventType.js'; @@ -12,16 +12,14 @@ import {shared as iconImageCache} from '../style/IconImageCache.js'; import {compose as composeTransform, invert as invertTransform, setFromArray as transformSetFromArray} from '../transform.js'; -/** - * @constructor - * @abstract - * @extends {module:ol/Disposable} - * @param {module:ol/PluggableMap} map Map. - * @struct - */ -class MapRenderer { +class MapRenderer extends Disposable { + + /** + * @param {module:ol/PluggableMap} map Map. + * @struct + */ constructor(map) { - Disposable.call(this); + super(); /** * @private @@ -318,8 +316,6 @@ class MapRenderer { } } -inherits(MapRenderer, Disposable); - /** * @param {module:ol/PluggableMap} map Map. From f6046c023c1f48a962993061647b451270e25d82 Mon Sep 17 00:00:00 2001 From: Tim Schaub Date: Tue, 17 Jul 2018 18:30:15 -0600 Subject: [PATCH 079/107] Clean up remaining ol/format/filter class --- src/ol/format/filter/Within.js | 22 +++++++++------------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/src/ol/format/filter/Within.js b/src/ol/format/filter/Within.js index 72bccbaba1..e158555309 100644 --- a/src/ol/format/filter/Within.js +++ b/src/ol/format/filter/Within.js @@ -1,30 +1,26 @@ /** * @module ol/format/filter/Within */ -import {inherits} from '../../util.js'; import Spatial from '../filter/Spatial.js'; /** * @classdesc * Represents a `` operator to test whether a geometry-valued property * is within a given geometry. - * - * @constructor - * @param {!string} geometryName Geometry name to use. - * @param {!module:ol/geom/Geometry} geometry Geometry. - * @param {string=} opt_srsName SRS name. No srsName attribute will be - * set on geometries when this is not provided. - * @extends {module:ol/format/filter/Spatial} - * @api */ -class Within { +class Within extends Spatial { + /** + * @param {!string} geometryName Geometry name to use. + * @param {!module:ol/geom/Geometry} geometry Geometry. + * @param {string=} opt_srsName SRS name. No srsName attribute will be + * set on geometries when this is not provided. + * @api + */ constructor(geometryName, geometry, opt_srsName) { - Spatial.call(this, 'Within', geometryName, geometry, opt_srsName); + super('Within', geometryName, geometry, opt_srsName); } } -inherits(Within, Spatial); - export default Within; From 1a5cf52b610bb87bec54341d1d15220f22e835d2 Mon Sep 17 00:00:00 2001 From: Tim Schaub Date: Tue, 17 Jul 2018 23:43:10 -0600 Subject: [PATCH 080/107] Remove remaining use of inherits in src --- src/ol/layer/Base.js | 34 +++++++------- src/ol/layer/Group.js | 28 ++++++------ src/ol/layer/Heatmap.js | 32 ++++++------- src/ol/layer/Image.js | 34 +++++++------- src/ol/layer/Layer.js | 50 ++++++++++---------- src/ol/layer/Tile.js | 34 +++++++------- src/ol/layer/Vector.js | 32 ++++++------- src/ol/layer/VectorTile.js | 30 ++++++------ src/ol/render/Box.js | 14 +++--- src/ol/render/Event.js | 23 +++++----- src/ol/render/canvas/ImageReplay.js | 29 ++++++------ src/ol/render/canvas/Immediate.js | 42 ++++++++--------- src/ol/render/canvas/LineStringReplay.js | 29 ++++++------ src/ol/render/canvas/PolygonReplay.js | 29 ++++++------ src/ol/render/canvas/Replay.js | 28 ++++++------ src/ol/render/canvas/ReplayGroup.js | 32 ++++++------- src/ol/render/canvas/TextReplay.js | 29 ++++++------ src/ol/render/webgl/CircleReplay.js | 20 ++++---- src/ol/render/webgl/ImageReplay.js | 20 ++++---- src/ol/render/webgl/Immediate.js | 30 ++++++------ src/ol/render/webgl/LineStringReplay.js | 20 ++++---- src/ol/render/webgl/PolygonReplay.js | 20 ++++---- src/ol/render/webgl/Replay.js | 22 ++++----- src/ol/render/webgl/ReplayGroup.js | 22 ++++----- src/ol/render/webgl/TextReplay.js | 20 ++++---- src/ol/render/webgl/TextureReplay.js | 22 ++++----- src/ol/reproj/Image.js | 36 +++++++-------- src/ol/reproj/Tile.js | 46 +++++++++---------- src/ol/source/BingMaps.js | 24 +++++----- src/ol/source/CartoDB.js | 24 +++++----- src/ol/source/Cluster.js | 28 ++++++------ src/ol/source/Image.js | 56 +++++++++++------------ src/ol/source/ImageArcGISRest.js | 36 +++++++-------- src/ol/source/ImageCanvas.js | 24 +++++----- src/ol/source/ImageMapGuide.js | 26 +++++------ src/ol/source/ImageStatic.js | 24 +++++----- src/ol/source/ImageWMS.js | 26 +++++------ src/ol/source/OSM.js | 24 +++++----- src/ol/source/Raster.js | 58 +++++++++++------------- src/ol/source/Source.js | 34 +++++++------- src/ol/source/Stamen.js | 24 +++++----- src/ol/source/Tile.js | 36 +++++++-------- src/ol/source/TileArcGISRest.js | 32 ++++++------- src/ol/source/TileDebug.js | 52 ++++++++++----------- src/ol/source/TileImage.js | 26 +++++------ src/ol/source/TileJSON.js | 24 +++++----- src/ol/source/TileWMS.js | 24 +++++----- src/ol/source/UTFGrid.js | 30 ++++++------ src/ol/source/UrlTile.js | 26 +++++------ src/ol/source/Vector.js | 36 +++++++-------- src/ol/source/VectorTile.js | 38 ++++++++-------- src/ol/source/WMTS.js | 24 +++++----- src/ol/source/XYZ.js | 50 ++++++++++---------- src/ol/source/Zoomify.js | 32 ++++++------- src/ol/structs/LRUCache.js | 28 ++++++------ src/ol/style/Circle.js | 24 +++++----- src/ol/style/Icon.js | 24 +++++----- src/ol/style/IconImage.js | 26 +++++------ src/ol/style/RegularShape.js | 28 ++++++------ src/ol/tilegrid/WMTS.js | 28 +++++------- src/ol/webgl/Context.js | 4 +- src/ol/webgl/Fragment.js | 6 +-- src/ol/webgl/Vertex.js | 6 +-- 63 files changed, 837 insertions(+), 982 deletions(-) diff --git a/src/ol/layer/Base.js b/src/ol/layer/Base.js index 1f2bb3f7aa..48a2a8e705 100644 --- a/src/ol/layer/Base.js +++ b/src/ol/layer/Base.js @@ -23,24 +23,24 @@ import {assign} from '../obj.js'; */ -/** - * @classdesc - * Abstract base class; normally only used for creating subclasses and not - * instantiated in apps. - * Note that with {@link module:ol/layer/Base} and all its subclasses, any property set in - * the options is set as a {@link module:ol/Object} property on the layer object, so - * is observable, and has get/set accessors. - * - * @constructor - * @abstract - * @extends {module:ol/Object} - * @param {module:ol/layer/Base~Options} options Layer options. - * @api - */ -class BaseLayer { +class BaseLayer extends BaseObject { + /** + * @classdesc + * Abstract base class; normally only used for creating subclasses and not + * instantiated in apps. + * Note that with {@link module:ol/layer/Base} and all its subclasses, any property set in + * the options is set as a {@link module:ol/Object} property on the layer object, so + * is observable, and has get/set accessors. + * + * @constructor + * @abstract + * @extends {module:ol/Object} + * @param {module:ol/layer/Base~Options} options Layer options. + * @api + */ constructor(options) { - BaseObject.call(this); + super(); /** * @type {Object.} @@ -249,7 +249,5 @@ class BaseLayer { } } -inherits(BaseLayer, BaseObject); - export default BaseLayer; diff --git a/src/ol/layer/Group.js b/src/ol/layer/Group.js index 411c078d61..4b68fb48b3 100644 --- a/src/ol/layer/Group.js +++ b/src/ol/layer/Group.js @@ -40,18 +40,18 @@ const Property = { }; -/** - * @classdesc - * A {@link module:ol/Collection~Collection} of layers that are handled together. - * - * A generic `change` event is triggered when the group/Collection changes. - * - * @constructor - * @extends {module:ol/layer/Base} - * @param {module:ol/layer/Group~Options=} opt_options Layer options. - * @api - */ -class LayerGroup { +class LayerGroup extends BaseLayer { + /** + * @classdesc + * A {@link module:ol/Collection~Collection} of layers that are handled together. + * + * A generic `change` event is triggered when the group/Collection changes. + * + * @constructor + * @extends {module:ol/layer/Base} + * @param {module:ol/layer/Group~Options=} opt_options Layer options. + * @api + */ constructor(opt_options) { const options = opt_options || {}; @@ -60,7 +60,7 @@ class LayerGroup { let layers = options.layers; - BaseLayer.call(this, baseOptions); + super(baseOptions); /** * @private @@ -236,7 +236,5 @@ class LayerGroup { } } -inherits(LayerGroup, BaseLayer); - export default LayerGroup; diff --git a/src/ol/layer/Heatmap.js b/src/ol/layer/Heatmap.js index e8fe57bd70..7df2be2043 100644 --- a/src/ol/layer/Heatmap.js +++ b/src/ol/layer/Heatmap.js @@ -60,20 +60,20 @@ const Property = { const DEFAULT_GRADIENT = ['#00f', '#0ff', '#0f0', '#ff0', '#f00']; -/** - * @classdesc - * Layer for rendering vector data as a heatmap. - * Note that any property set in the options is set as a {@link module:ol/Object~BaseObject} - * property on the layer object; for example, setting `title: 'My Title'` in the - * options means that `title` is observable, and has get/set accessors. - * - * @constructor - * @extends {module:ol/layer/Vector} - * @fires module:ol/render/Event~RenderEvent - * @param {module:ol/layer/Heatmap~Options=} opt_options Options. - * @api - */ -class Heatmap { +class Heatmap extends VectorLayer { + /** + * @classdesc + * Layer for rendering vector data as a heatmap. + * Note that any property set in the options is set as a {@link module:ol/Object~BaseObject} + * property on the layer object; for example, setting `title: 'My Title'` in the + * options means that `title` is observable, and has get/set accessors. + * + * @constructor + * @extends {module:ol/layer/Vector} + * @fires module:ol/render/Event~RenderEvent + * @param {module:ol/layer/Heatmap~Options=} opt_options Options. + * @api + */ constructor(opt_options) { const options = opt_options ? opt_options : {}; @@ -84,7 +84,7 @@ class Heatmap { delete baseOptions.blur; delete baseOptions.shadow; delete baseOptions.weight; - VectorLayer.call(this, /** @type {module:ol/layer/Vector~Options} */ (baseOptions)); + super(baseOptions); /** * @private @@ -283,8 +283,6 @@ class Heatmap { } } -inherits(Heatmap, VectorLayer); - /** * @param {Array.} colors A list of colored. diff --git a/src/ol/layer/Image.js b/src/ol/layer/Image.js index 3b09138dfe..9828ab8043 100644 --- a/src/ol/layer/Image.js +++ b/src/ol/layer/Image.js @@ -26,25 +26,25 @@ import Layer from '../layer/Layer.js'; */ -/** - * @classdesc - * Server-rendered images that are available for arbitrary extents and - * resolutions. - * Note that any property set in the options is set as a {@link module:ol/Object~BaseObject} - * property on the layer object; for example, setting `title: 'My Title'` in the - * options means that `title` is observable, and has get/set accessors. - * - * @constructor - * @extends {module:ol/layer/Layer} - * @fires module:ol/render/Event~RenderEvent - * @param {module:ol/layer/Image~Options=} opt_options Layer options. - * @api - */ -class ImageLayer { +class ImageLayer extends Layer { + /** + * @classdesc + * Server-rendered images that are available for arbitrary extents and + * resolutions. + * Note that any property set in the options is set as a {@link module:ol/Object~BaseObject} + * property on the layer object; for example, setting `title: 'My Title'` in the + * options means that `title` is observable, and has get/set accessors. + * + * @constructor + * @extends {module:ol/layer/Layer} + * @fires module:ol/render/Event~RenderEvent + * @param {module:ol/layer/Image~Options=} opt_options Layer options. + * @api + */ constructor(opt_options) { const options = opt_options ? opt_options : {}; - Layer.call(this, /** @type {module:ol/layer/Layer~Options} */ (options)); + super(options); /** * The layer type. @@ -57,8 +57,6 @@ class ImageLayer { } -inherits(ImageLayer, Layer); - /** * Return the associated {@link module:ol/source/Image source} of the image layer. diff --git a/src/ol/layer/Layer.js b/src/ol/layer/Layer.js index d3a2a80aef..aff9ea4495 100644 --- a/src/ol/layer/Layer.js +++ b/src/ol/layer/Layer.js @@ -44,35 +44,35 @@ import SourceState from '../source/State.js'; */ -/** - * @classdesc - * Abstract base class; normally only used for creating subclasses and not - * instantiated in apps. - * A visual representation of raster or vector map data. - * Layers group together those properties that pertain to how the data is to be - * displayed, irrespective of the source of that data. - * - * Layers are usually added to a map with {@link module:ol/Map#addLayer}. Components - * like {@link module:ol/interaction/Select~Select} use unmanaged layers - * internally. These unmanaged layers are associated with the map using - * {@link module:ol/layer/Layer~Layer#setMap} instead. - * - * A generic `change` event is fired when the state of the source changes. - * - * @constructor - * @abstract - * @extends {module:ol/layer/Base} - * @fires module:ol/render/Event~RenderEvent - * @param {module:ol/layer/Layer~Options} options Layer options. - * @api - */ -class Layer { +class Layer extends BaseLayer { + /** + * @classdesc + * Abstract base class; normally only used for creating subclasses and not + * instantiated in apps. + * A visual representation of raster or vector map data. + * Layers group together those properties that pertain to how the data is to be + * displayed, irrespective of the source of that data. + * + * Layers are usually added to a map with {@link module:ol/Map#addLayer}. Components + * like {@link module:ol/interaction/Select~Select} use unmanaged layers + * internally. These unmanaged layers are associated with the map using + * {@link module:ol/layer/Layer~Layer#setMap} instead. + * + * A generic `change` event is fired when the state of the source changes. + * + * @constructor + * @abstract + * @extends {module:ol/layer/Base} + * @fires module:ol/render/Event~RenderEvent + * @param {module:ol/layer/Layer~Options} options Layer options. + * @api + */ constructor(options) { const baseOptions = assign({}, options); delete baseOptions.source; - BaseLayer.call(this, /** @type {module:ol/layer/Base~Options} */ (baseOptions)); + super(baseOptions); /** * @private @@ -214,8 +214,6 @@ class Layer { } } -inherits(Layer, BaseLayer); - /** * Return `true` if the layer is visible, and if the passed resolution is diff --git a/src/ol/layer/Tile.js b/src/ol/layer/Tile.js index f1b504e4e6..ba74bb3311 100644 --- a/src/ol/layer/Tile.js +++ b/src/ol/layer/Tile.js @@ -31,21 +31,21 @@ import {assign} from '../obj.js'; */ -/** - * @classdesc - * For layer sources that provide pre-rendered, tiled images in grids that are - * organized by zoom levels for specific resolutions. - * Note that any property set in the options is set as a {@link module:ol/Object~BaseObject} - * property on the layer object; for example, setting `title: 'My Title'` in the - * options means that `title` is observable, and has get/set accessors. - * - * @constructor - * @extends {module:ol/layer/Layer} - * @fires module:ol/render/Event~RenderEvent - * @param {module:ol/layer/Tile~Options=} opt_options Tile layer options. - * @api - */ -class TileLayer { +class TileLayer extends Layer { + /** + * @classdesc + * For layer sources that provide pre-rendered, tiled images in grids that are + * organized by zoom levels for specific resolutions. + * Note that any property set in the options is set as a {@link module:ol/Object~BaseObject} + * property on the layer object; for example, setting `title: 'My Title'` in the + * options means that `title` is observable, and has get/set accessors. + * + * @constructor + * @extends {module:ol/layer/Layer} + * @fires module:ol/render/Event~RenderEvent + * @param {module:ol/layer/Tile~Options=} opt_options Tile layer options. + * @api + */ constructor(opt_options) { const options = opt_options ? opt_options : {}; @@ -53,7 +53,7 @@ class TileLayer { delete baseOptions.preload; delete baseOptions.useInterimTilesOnError; - Layer.call(this, /** @type {module:ol/layer/Layer~Options} */ (baseOptions)); + super(baseOptions); this.setPreload(options.preload !== undefined ? options.preload : 0); this.setUseInterimTilesOnError(options.useInterimTilesOnError !== undefined ? @@ -109,8 +109,6 @@ class TileLayer { } } -inherits(TileLayer, Layer); - /** * Return the associated {@link module:ol/source/Tile tilesource} of the layer. diff --git a/src/ol/layer/Vector.js b/src/ol/layer/Vector.js index 7cd627e21b..5ddcdc5b19 100644 --- a/src/ol/layer/Vector.js +++ b/src/ol/layer/Vector.js @@ -78,20 +78,20 @@ const Property = { }; -/** - * @classdesc - * Vector data that is rendered client-side. - * Note that any property set in the options is set as a {@link module:ol/Object~BaseObject} - * property on the layer object; for example, setting `title: 'My Title'` in the - * options means that `title` is observable, and has get/set accessors. - * - * @constructor - * @extends {module:ol/layer/Layer} - * @fires module:ol/render/Event~RenderEvent - * @param {module:ol/layer/Vector~Options=} opt_options Options. - * @api - */ -class VectorLayer { +class VectorLayer extends Layer { + /** + * @classdesc + * Vector data that is rendered client-side. + * Note that any property set in the options is set as a {@link module:ol/Object~BaseObject} + * property on the layer object; for example, setting `title: 'My Title'` in the + * options means that `title` is observable, and has get/set accessors. + * + * @constructor + * @extends {module:ol/layer/Layer} + * @fires module:ol/render/Event~RenderEvent + * @param {module:ol/layer/Vector~Options=} opt_options Options. + * @api + */ constructor(opt_options) { const options = opt_options ? opt_options : /** @type {module:ol/layer/Vector~Options} */ ({}); @@ -102,7 +102,7 @@ class VectorLayer { delete baseOptions.renderBuffer; delete baseOptions.updateWhileAnimating; delete baseOptions.updateWhileInteracting; - Layer.call(this, /** @type {module:ol/layer/Layer~Options} */ (baseOptions)); + super(baseOptions); /** * @private @@ -263,8 +263,6 @@ class VectorLayer { } } -inherits(VectorLayer, Layer); - /** * Return the associated {@link module:ol/source/Vector vectorsource} of the layer. diff --git a/src/ol/layer/VectorTile.js b/src/ol/layer/VectorTile.js index 20d7453d88..0afe3ed466 100644 --- a/src/ol/layer/VectorTile.js +++ b/src/ol/layer/VectorTile.js @@ -87,19 +87,19 @@ export const RenderType = { */ -/** - * @classdesc - * Layer for vector tile data that is rendered client-side. - * Note that any property set in the options is set as a {@link module:ol/Object~BaseObject} - * property on the layer object; for example, setting `title: 'My Title'` in the - * options means that `title` is observable, and has get/set accessors. - * - * @constructor - * @extends {module:ol/layer/Vector} - * @param {module:ol/layer/VectorTile~Options=} opt_options Options. - * @api - */ -class VectorTileLayer { +class VectorTileLayer extends VectorLayer { + /** + * @classdesc + * Layer for vector tile data that is rendered client-side. + * Note that any property set in the options is set as a {@link module:ol/Object~BaseObject} + * property on the layer object; for example, setting `title: 'My Title'` in the + * options means that `title` is observable, and has get/set accessors. + * + * @constructor + * @extends {module:ol/layer/Vector} + * @param {module:ol/layer/VectorTile~Options=} opt_options Options. + * @api + */ constructor(opt_options) { const options = opt_options ? opt_options : {}; @@ -118,7 +118,7 @@ class VectorTileLayer { delete baseOptions.preload; delete baseOptions.useInterimTilesOnError; - VectorLayer.call(this, /** @type {module:ol/layer/Vector~Options} */ (baseOptions)); + super(baseOptions); this.setPreload(options.preload ? options.preload : 0); this.setUseInterimTilesOnError(options.useInterimTilesOnError !== undefined ? @@ -174,8 +174,6 @@ class VectorTileLayer { } } -inherits(VectorTileLayer, VectorLayer); - /** * Return the associated {@link module:ol/source/VectorTile vectortilesource} of the layer. diff --git a/src/ol/render/Box.js b/src/ol/render/Box.js index 63dce4bc66..f51afd682b 100644 --- a/src/ol/render/Box.js +++ b/src/ol/render/Box.js @@ -7,12 +7,12 @@ import {inherits} from '../util.js'; import Disposable from '../Disposable.js'; import Polygon from '../geom/Polygon.js'; -/** - * @constructor - * @extends {module:ol/Disposable} - * @param {string} className CSS class name. - */ -class RenderBox { +class RenderBox extends Disposable { + /** + * @constructor + * @extends {module:ol/Disposable} + * @param {string} className CSS class name. + */ constructor(className) { /** @@ -126,7 +126,5 @@ class RenderBox { } } -inherits(RenderBox, Disposable); - export default RenderBox; diff --git a/src/ol/render/Event.js b/src/ol/render/Event.js index ff1052dfd3..4bab695a7d 100644 --- a/src/ol/render/Event.js +++ b/src/ol/render/Event.js @@ -4,20 +4,20 @@ import {inherits} from '../util.js'; import Event from '../events/Event.js'; -/** - * @constructor - * @extends {module:ol/events/Event} - * @param {module:ol/render/EventType} type Type. - * @param {module:ol/render/VectorContext=} opt_vectorContext Vector context. - * @param {module:ol/PluggableMap~FrameState=} opt_frameState Frame state. - * @param {?CanvasRenderingContext2D=} opt_context Context. - * @param {?module:ol/webgl/Context=} opt_glContext WebGL Context. - */ -class RenderEvent { +class RenderEvent extends Event { + /** + * @constructor + * @extends {module:ol/events/Event} + * @param {module:ol/render/EventType} type Type. + * @param {module:ol/render/VectorContext=} opt_vectorContext Vector context. + * @param {module:ol/PluggableMap~FrameState=} opt_frameState Frame state. + * @param {?CanvasRenderingContext2D=} opt_context Context. + * @param {?module:ol/webgl/Context=} opt_glContext WebGL Context. + */ constructor(type, opt_vectorContext, opt_frameState, opt_context, opt_glContext) { - Event.call(this, type); + super(type); /** * For canvas, this is an instance of {@link module:ol/render/canvas/Immediate}. @@ -53,5 +53,4 @@ class RenderEvent { } -inherits(RenderEvent, Event); export default RenderEvent; diff --git a/src/ol/render/canvas/ImageReplay.js b/src/ol/render/canvas/ImageReplay.js index 8d6f325637..c348f22e7d 100644 --- a/src/ol/render/canvas/ImageReplay.js +++ b/src/ol/render/canvas/ImageReplay.js @@ -5,21 +5,20 @@ import {inherits} from '../../util.js'; import CanvasInstruction from '../canvas/Instruction.js'; import CanvasReplay from '../canvas/Replay.js'; -/** - * @constructor - * @extends {module:ol/render/canvas/Replay} - * @param {number} tolerance Tolerance. - * @param {module:ol/extent~Extent} maxExtent Maximum extent. - * @param {number} resolution Resolution. - * @param {number} pixelRatio Pixel ratio. - * @param {boolean} overlaps The replay can have overlapping geometries. - * @param {?} declutterTree Declutter tree. - * @struct - */ -class CanvasImageReplay { +class CanvasImageReplay extends CanvasReplay { + /** + * @constructor + * @extends {module:ol/render/canvas/Replay} + * @param {number} tolerance Tolerance. + * @param {module:ol/extent~Extent} maxExtent Maximum extent. + * @param {number} resolution Resolution. + * @param {number} pixelRatio Pixel ratio. + * @param {boolean} overlaps The replay can have overlapping geometries. + * @param {?} declutterTree Declutter tree. + * @struct + */ constructor(tolerance, maxExtent, resolution, pixelRatio, overlaps, declutterTree) { - CanvasReplay.call(this, - tolerance, maxExtent, resolution, pixelRatio, overlaps, declutterTree); + super(tolerance, maxExtent, resolution, pixelRatio, overlaps, declutterTree); /** * @private @@ -225,7 +224,5 @@ class CanvasImageReplay { } } -inherits(CanvasImageReplay, CanvasReplay); - export default CanvasImageReplay; diff --git a/src/ol/render/canvas/Immediate.js b/src/ol/render/canvas/Immediate.js index 8843869e91..dcc26ceace 100644 --- a/src/ol/render/canvas/Immediate.js +++ b/src/ol/render/canvas/Immediate.js @@ -17,27 +17,27 @@ import VectorContext from '../VectorContext.js'; import {defaultTextAlign, defaultFillStyle, defaultLineCap, defaultLineDash, defaultLineDashOffset, defaultLineJoin, defaultLineWidth, defaultMiterLimit, defaultStrokeStyle, defaultTextBaseline, defaultFont} from '../canvas.js'; import {create as createTransform, compose as composeTransform} from '../../transform.js'; -/** - * @classdesc - * A concrete subclass of {@link module:ol/render/VectorContext} that implements - * direct rendering of features and geometries to an HTML5 Canvas context. - * Instances of this class are created internally by the library and - * provided to application code as vectorContext member of the - * {@link module:ol/render/Event~RenderEvent} object associated with postcompose, precompose and - * render events emitted by layers and maps. - * - * @constructor - * @extends {module:ol/render/VectorContext} - * @param {CanvasRenderingContext2D} context Context. - * @param {number} pixelRatio Pixel ratio. - * @param {module:ol/extent~Extent} extent Extent. - * @param {module:ol/transform~Transform} transform Transform. - * @param {number} viewRotation View rotation. - * @struct - */ -class CanvasImmediateRenderer { +class CanvasImmediateRenderer extends VectorContext { + /** + * @classdesc + * A concrete subclass of {@link module:ol/render/VectorContext} that implements + * direct rendering of features and geometries to an HTML5 Canvas context. + * Instances of this class are created internally by the library and + * provided to application code as vectorContext member of the + * {@link module:ol/render/Event~RenderEvent} object associated with postcompose, precompose and + * render events emitted by layers and maps. + * + * @constructor + * @extends {module:ol/render/VectorContext} + * @param {CanvasRenderingContext2D} context Context. + * @param {number} pixelRatio Pixel ratio. + * @param {module:ol/extent~Extent} extent Extent. + * @param {module:ol/transform~Transform} transform Transform. + * @param {number} viewRotation View rotation. + * @struct + */ constructor(context, pixelRatio, extent, transform, viewRotation) { - VectorContext.call(this); + super(); /** * @private @@ -943,7 +943,5 @@ class CanvasImmediateRenderer { } } -inherits(CanvasImmediateRenderer, VectorContext); - export default CanvasImmediateRenderer; diff --git a/src/ol/render/canvas/LineStringReplay.js b/src/ol/render/canvas/LineStringReplay.js index 1e99544fcc..c16abb9f57 100644 --- a/src/ol/render/canvas/LineStringReplay.js +++ b/src/ol/render/canvas/LineStringReplay.js @@ -5,21 +5,20 @@ import {inherits} from '../../util.js'; import CanvasInstruction, {strokeInstruction, beginPathInstruction} from '../canvas/Instruction.js'; import CanvasReplay from '../canvas/Replay.js'; -/** - * @constructor - * @extends {module:ol/render/canvas/Replay} - * @param {number} tolerance Tolerance. - * @param {module:ol/extent~Extent} maxExtent Maximum extent. - * @param {number} resolution Resolution. - * @param {number} pixelRatio Pixel ratio. - * @param {boolean} overlaps The replay can have overlapping geometries. - * @param {?} declutterTree Declutter tree. - * @struct - */ -class CanvasLineStringReplay { +class CanvasLineStringReplay extends CanvasReplay { + /** + * @constructor + * @extends {module:ol/render/canvas/Replay} + * @param {number} tolerance Tolerance. + * @param {module:ol/extent~Extent} maxExtent Maximum extent. + * @param {number} resolution Resolution. + * @param {number} pixelRatio Pixel ratio. + * @param {boolean} overlaps The replay can have overlapping geometries. + * @param {?} declutterTree Declutter tree. + * @struct + */ constructor(tolerance, maxExtent, resolution, pixelRatio, overlaps, declutterTree) { - CanvasReplay.call(this, - tolerance, maxExtent, resolution, pixelRatio, overlaps, declutterTree); + super(tolerance, maxExtent, resolution, pixelRatio, overlaps, declutterTree); } /** @@ -118,7 +117,5 @@ class CanvasLineStringReplay { } } -inherits(CanvasLineStringReplay, CanvasReplay); - export default CanvasLineStringReplay; diff --git a/src/ol/render/canvas/PolygonReplay.js b/src/ol/render/canvas/PolygonReplay.js index efb597f3dd..50c90e0aed 100644 --- a/src/ol/render/canvas/PolygonReplay.js +++ b/src/ol/render/canvas/PolygonReplay.js @@ -11,21 +11,20 @@ import CanvasInstruction, { import CanvasReplay from '../canvas/Replay.js'; -/** - * @constructor - * @extends {module:ol/render/canvas/Replay} - * @param {number} tolerance Tolerance. - * @param {module:ol/extent~Extent} maxExtent Maximum extent. - * @param {number} resolution Resolution. - * @param {number} pixelRatio Pixel ratio. - * @param {boolean} overlaps The replay can have overlapping geometries. - * @param {?} declutterTree Declutter tree. - * @struct - */ -class CanvasPolygonReplay { +class CanvasPolygonReplay extends CanvasReplay { + /** + * @constructor + * @extends {module:ol/render/canvas/Replay} + * @param {number} tolerance Tolerance. + * @param {module:ol/extent~Extent} maxExtent Maximum extent. + * @param {number} resolution Resolution. + * @param {number} pixelRatio Pixel ratio. + * @param {boolean} overlaps The replay can have overlapping geometries. + * @param {?} declutterTree Declutter tree. + * @struct + */ constructor(tolerance, maxExtent, resolution, pixelRatio, overlaps, declutterTree) { - CanvasReplay.call(this, - tolerance, maxExtent, resolution, pixelRatio, overlaps, declutterTree); + super(tolerance, maxExtent, resolution, pixelRatio, overlaps, declutterTree); } /** @@ -215,7 +214,5 @@ class CanvasPolygonReplay { } } -inherits(CanvasPolygonReplay, CanvasReplay); - export default CanvasPolygonReplay; diff --git a/src/ol/render/canvas/Replay.js b/src/ol/render/canvas/Replay.js index 65462a7118..8057993bf0 100644 --- a/src/ol/render/canvas/Replay.js +++ b/src/ol/render/canvas/Replay.js @@ -41,20 +41,20 @@ const tmpExtent = createEmpty(); const tmpTransform = createTransform(); -/** - * @constructor - * @extends {module:ol/render/VectorContext} - * @param {number} tolerance Tolerance. - * @param {module:ol/extent~Extent} maxExtent Maximum extent. - * @param {number} resolution Resolution. - * @param {number} pixelRatio Pixel ratio. - * @param {boolean} overlaps The replay can have overlapping geometries. - * @param {?} declutterTree Declutter tree. - * @struct - */ -class CanvasReplay { +class CanvasReplay extends VectorContext { + /** + * @constructor + * @extends {module:ol/render/VectorContext} + * @param {number} tolerance Tolerance. + * @param {module:ol/extent~Extent} maxExtent Maximum extent. + * @param {number} resolution Resolution. + * @param {number} pixelRatio Pixel ratio. + * @param {boolean} overlaps The replay can have overlapping geometries. + * @param {?} declutterTree Declutter tree. + * @struct + */ constructor(tolerance, maxExtent, resolution, pixelRatio, overlaps, declutterTree) { - VectorContext.call(this); + super(); /** * @type {?} @@ -1082,8 +1082,6 @@ class CanvasReplay { } } -inherits(CanvasReplay, VectorContext); - /** * FIXME empty description for jsdoc diff --git a/src/ol/render/canvas/ReplayGroup.js b/src/ol/render/canvas/ReplayGroup.js index 2aa3f5cfeb..53077b3cfc 100644 --- a/src/ol/render/canvas/ReplayGroup.js +++ b/src/ol/render/canvas/ReplayGroup.js @@ -33,20 +33,20 @@ const BATCH_CONSTRUCTORS = { }; -/** - * @constructor - * @extends {module:ol/render/ReplayGroup} - * @param {number} tolerance Tolerance. - * @param {module:ol/extent~Extent} maxExtent Max extent. - * @param {number} resolution Resolution. - * @param {number} pixelRatio Pixel ratio. - * @param {boolean} overlaps The replay group can have overlapping geometries. - * @param {?} declutterTree Declutter tree - * for declutter processing in postrender. - * @param {number=} opt_renderBuffer Optional rendering buffer. - * @struct - */ -class CanvasReplayGroup { +class CanvasReplayGroup extends ReplayGroup { + /** + * @constructor + * @extends {module:ol/render/ReplayGroup} + * @param {number} tolerance Tolerance. + * @param {module:ol/extent~Extent} maxExtent Max extent. + * @param {number} resolution Resolution. + * @param {number} pixelRatio Pixel ratio. + * @param {boolean} overlaps The replay group can have overlapping geometries. + * @param {?} declutterTree Declutter tree + * for declutter processing in postrender. + * @param {number=} opt_renderBuffer Optional rendering buffer. + * @struct + */ constructor( tolerance, maxExtent, @@ -56,7 +56,7 @@ class CanvasReplayGroup { declutterTree, opt_renderBuffer ) { - ReplayGroup.call(this); + super(); /** * Declutter tree. @@ -407,8 +407,6 @@ class CanvasReplayGroup { } } -inherits(CanvasReplayGroup, ReplayGroup); - /** * This cache is used for storing calculated pixel circles for increasing performance. diff --git a/src/ol/render/canvas/TextReplay.js b/src/ol/render/canvas/TextReplay.js index 945243bad1..5c9db8771d 100644 --- a/src/ol/render/canvas/TextReplay.js +++ b/src/ol/render/canvas/TextReplay.js @@ -14,21 +14,20 @@ import CanvasReplay from '../canvas/Replay.js'; import {TEXT_ALIGN} from '../replay.js'; import TextPlacement from '../../style/TextPlacement.js'; -/** - * @constructor - * @extends {module:ol/render/canvas/Replay} - * @param {number} tolerance Tolerance. - * @param {module:ol/extent~Extent} maxExtent Maximum extent. - * @param {number} resolution Resolution. - * @param {number} pixelRatio Pixel ratio. - * @param {boolean} overlaps The replay can have overlapping geometries. - * @param {?} declutterTree Declutter tree. - * @struct - */ -class CanvasTextReplay { +class CanvasTextReplay extends CanvasReplay { + /** + * @constructor + * @extends {module:ol/render/canvas/Replay} + * @param {number} tolerance Tolerance. + * @param {module:ol/extent~Extent} maxExtent Maximum extent. + * @param {number} resolution Resolution. + * @param {number} pixelRatio Pixel ratio. + * @param {boolean} overlaps The replay can have overlapping geometries. + * @param {?} declutterTree Declutter tree. + * @struct + */ constructor(tolerance, maxExtent, resolution, pixelRatio, overlaps, declutterTree) { - CanvasReplay.call(this, - tolerance, maxExtent, resolution, pixelRatio, overlaps, declutterTree); + super(tolerance, maxExtent, resolution, pixelRatio, overlaps, declutterTree); /** * @private @@ -521,8 +520,6 @@ class CanvasTextReplay { } } -inherits(CanvasTextReplay, CanvasReplay); - /** * @param {string} font Font to use for measuring. diff --git a/src/ol/render/webgl/CircleReplay.js b/src/ol/render/webgl/CircleReplay.js index 6d98db4d2c..1a7f02f8c7 100644 --- a/src/ol/render/webgl/CircleReplay.js +++ b/src/ol/render/webgl/CircleReplay.js @@ -15,16 +15,16 @@ import {DEFAULT_LINEDASH, DEFAULT_LINEDASHOFFSET, DEFAULT_STROKESTYLE, import {FLOAT} from '../../webgl.js'; import WebGLBuffer from '../../webgl/Buffer.js'; -/** - * @constructor - * @extends {module:ol/render/webgl/Replay} - * @param {number} tolerance Tolerance. - * @param {module:ol/extent~Extent} maxExtent Max extent. - * @struct - */ -class WebGLCircleReplay { +class WebGLCircleReplay extends WebGLReplay { + /** + * @constructor + * @extends {module:ol/render/webgl/Replay} + * @param {number} tolerance Tolerance. + * @param {module:ol/extent~Extent} maxExtent Max extent. + * @struct + */ constructor(tolerance, maxExtent) { - WebGLReplay.call(this, tolerance, maxExtent); + super(tolerance, maxExtent); /** * @private @@ -404,7 +404,5 @@ class WebGLCircleReplay { } } -inherits(WebGLCircleReplay, WebGLReplay); - export default WebGLCircleReplay; diff --git a/src/ol/render/webgl/ImageReplay.js b/src/ol/render/webgl/ImageReplay.js index 03f6350fdf..4570ab2da0 100644 --- a/src/ol/render/webgl/ImageReplay.js +++ b/src/ol/render/webgl/ImageReplay.js @@ -5,16 +5,16 @@ import {getUid, inherits} from '../../util.js'; import WebGLTextureReplay from '../webgl/TextureReplay.js'; import WebGLBuffer from '../../webgl/Buffer.js'; -/** - * @constructor - * @extends {module:ol/render/webgl/TextureReplay} - * @param {number} tolerance Tolerance. - * @param {module:ol/extent~Extent} maxExtent Max extent. - * @struct - */ -class WebGLImageReplay { +class WebGLImageReplay extends WebGLTextureReplay { + /** + * @constructor + * @extends {module:ol/render/webgl/TextureReplay} + * @param {number} tolerance Tolerance. + * @param {module:ol/extent~Extent} maxExtent Max extent. + * @struct + */ constructor(tolerance, maxExtent) { - WebGLTextureReplay.call(this, tolerance, maxExtent); + super(tolerance, maxExtent); /** * @type {Array.} @@ -163,7 +163,5 @@ class WebGLImageReplay { } } -inherits(WebGLImageReplay, WebGLTextureReplay); - export default WebGLImageReplay; diff --git a/src/ol/render/webgl/Immediate.js b/src/ol/render/webgl/Immediate.js index ef281f2510..415e7779bd 100644 --- a/src/ol/render/webgl/Immediate.js +++ b/src/ol/render/webgl/Immediate.js @@ -8,21 +8,21 @@ import ReplayType from '../ReplayType.js'; import VectorContext from '../VectorContext.js'; import WebGLReplayGroup from '../webgl/ReplayGroup.js'; -/** - * @constructor - * @extends {module:ol/render/VectorContext} - * @param {module:ol/webgl/Context} context Context. - * @param {module:ol/coordinate~Coordinate} center Center. - * @param {number} resolution Resolution. - * @param {number} rotation Rotation. - * @param {module:ol/size~Size} size Size. - * @param {module:ol/extent~Extent} extent Extent. - * @param {number} pixelRatio Pixel ratio. - * @struct - */ -class WebGLImmediateRenderer { +class WebGLImmediateRenderer extends VectorContext { + /** + * @constructor + * @extends {module:ol/render/VectorContext} + * @param {module:ol/webgl/Context} context Context. + * @param {module:ol/coordinate~Coordinate} center Center. + * @param {number} resolution Resolution. + * @param {number} rotation Rotation. + * @param {module:ol/size~Size} size Size. + * @param {module:ol/extent~Extent} extent Extent. + * @param {number} pixelRatio Pixel ratio. + * @struct + */ constructor(context, center, resolution, rotation, size, extent, pixelRatio) { - VectorContext.call(this); + super(); /** * @private @@ -385,7 +385,5 @@ class WebGLImmediateRenderer { } } -inherits(WebGLImmediateRenderer, VectorContext); - export default WebGLImmediateRenderer; diff --git a/src/ol/render/webgl/LineStringReplay.js b/src/ol/render/webgl/LineStringReplay.js index db18c94472..bd089f0a4e 100644 --- a/src/ol/render/webgl/LineStringReplay.js +++ b/src/ol/render/webgl/LineStringReplay.js @@ -35,16 +35,16 @@ const Instruction = { }; -/** - * @constructor - * @extends {module:ol/render/webgl/Replay} - * @param {number} tolerance Tolerance. - * @param {module:ol/extent~Extent} maxExtent Max extent. - * @struct - */ -class WebGLLineStringReplay { +class WebGLLineStringReplay extends WebGLReplay { + /** + * @constructor + * @extends {module:ol/render/webgl/Replay} + * @param {number} tolerance Tolerance. + * @param {module:ol/extent~Extent} maxExtent Max extent. + * @struct + */ constructor(tolerance, maxExtent) { - WebGLReplay.call(this, tolerance, maxExtent); + super(tolerance, maxExtent); /** * @private @@ -666,7 +666,5 @@ class WebGLLineStringReplay { } } -inherits(WebGLLineStringReplay, WebGLReplay); - export default WebGLLineStringReplay; diff --git a/src/ol/render/webgl/PolygonReplay.js b/src/ol/render/webgl/PolygonReplay.js index 14bb136170..424b97ee60 100644 --- a/src/ol/render/webgl/PolygonReplay.js +++ b/src/ol/render/webgl/PolygonReplay.js @@ -36,16 +36,16 @@ import WebGLBuffer from '../../webgl/Buffer.js'; */ -/** - * @constructor - * @extends {module:ol/render/webgl/Replay} - * @param {number} tolerance Tolerance. - * @param {module:ol/extent~Extent} maxExtent Max extent. - * @struct - */ -class WebGLPolygonReplay { +class WebGLPolygonReplay extends WebGLReplay { + /** + * @constructor + * @extends {module:ol/render/webgl/Replay} + * @param {number} tolerance Tolerance. + * @param {module:ol/extent~Extent} maxExtent Max extent. + * @struct + */ constructor(tolerance, maxExtent) { - WebGLReplay.call(this, tolerance, maxExtent); + super(tolerance, maxExtent); this.lineStringReplay = new WebGLLineStringReplay( tolerance, maxExtent); @@ -1045,7 +1045,5 @@ class WebGLPolygonReplay { } } -inherits(WebGLPolygonReplay, WebGLReplay); - export default WebGLPolygonReplay; diff --git a/src/ol/render/webgl/Replay.js b/src/ol/render/webgl/Replay.js index f3c6b83f7c..743fcce134 100644 --- a/src/ol/render/webgl/Replay.js +++ b/src/ol/render/webgl/Replay.js @@ -15,17 +15,17 @@ import {create, fromTransform} from '../../vec/mat4.js'; import {ARRAY_BUFFER, ELEMENT_ARRAY_BUFFER, TRIANGLES, UNSIGNED_INT, UNSIGNED_SHORT} from '../../webgl.js'; -/** - * @constructor - * @abstract - * @extends {module:ol/render/VectorContext} - * @param {number} tolerance Tolerance. - * @param {module:ol/extent~Extent} maxExtent Max extent. - * @struct - */ -class WebGLReplay { +class WebGLReplay extends VectorContext { + /** + * @constructor + * @abstract + * @extends {module:ol/render/VectorContext} + * @param {number} tolerance Tolerance. + * @param {module:ol/extent~Extent} maxExtent Max extent. + * @struct + */ constructor(tolerance, maxExtent) { - VectorContext.call(this); + super(); /** * @protected @@ -359,7 +359,5 @@ class WebGLReplay { } } -inherits(WebGLReplay, VectorContext); - export default WebGLReplay; diff --git a/src/ol/render/webgl/ReplayGroup.js b/src/ol/render/webgl/ReplayGroup.js index 3bf6cc030a..0a18839434 100644 --- a/src/ol/render/webgl/ReplayGroup.js +++ b/src/ol/render/webgl/ReplayGroup.js @@ -32,17 +32,17 @@ const BATCH_CONSTRUCTORS = { }; -/** - * @constructor - * @extends {module:ol/render/ReplayGroup} - * @param {number} tolerance Tolerance. - * @param {module:ol/extent~Extent} maxExtent Max extent. - * @param {number=} opt_renderBuffer Render buffer. - * @struct - */ -class WebGLReplayGroup { +class WebGLReplayGroup extends ReplayGroup { + /** + * @constructor + * @extends {module:ol/render/ReplayGroup} + * @param {number} tolerance Tolerance. + * @param {module:ol/extent~Extent} maxExtent Max extent. + * @param {number=} opt_renderBuffer Render buffer. + * @struct + */ constructor(tolerance, maxExtent, opt_renderBuffer) { - ReplayGroup.call(this); + super(); /** * @type {module:ol/extent~Extent} @@ -341,7 +341,5 @@ class WebGLReplayGroup { } } -inherits(WebGLReplayGroup, ReplayGroup); - export default WebGLReplayGroup; diff --git a/src/ol/render/webgl/TextReplay.js b/src/ol/render/webgl/TextReplay.js index 7f4e9a37db..2d9214fab1 100644 --- a/src/ol/render/webgl/TextReplay.js +++ b/src/ol/render/webgl/TextReplay.js @@ -22,16 +22,16 @@ import WebGLBuffer from '../../webgl/Buffer.js'; */ -/** - * @constructor - * @extends {module:ol/render/webgl/TextureReplay} - * @param {number} tolerance Tolerance. - * @param {module:ol/extent~Extent} maxExtent Max extent. - * @struct - */ -class WebGLTextReplay { +class WebGLTextReplay extends WebGLTextureReplay { + /** + * @constructor + * @extends {module:ol/render/webgl/TextureReplay} + * @param {number} tolerance Tolerance. + * @param {module:ol/extent~Extent} maxExtent Max extent. + * @struct + */ constructor(tolerance, maxExtent) { - WebGLTextureReplay.call(this, tolerance, maxExtent); + super(tolerance, maxExtent); /** * @private @@ -457,7 +457,5 @@ class WebGLTextReplay { } } -inherits(WebGLTextReplay, WebGLTextureReplay); - export default WebGLTextReplay; diff --git a/src/ol/render/webgl/TextureReplay.js b/src/ol/render/webgl/TextureReplay.js index 3ce621d0b3..f2d95a2eed 100644 --- a/src/ol/render/webgl/TextureReplay.js +++ b/src/ol/render/webgl/TextureReplay.js @@ -10,17 +10,17 @@ import WebGLReplay from '../webgl/Replay.js'; import {CLAMP_TO_EDGE, FLOAT, TEXTURE_2D} from '../../webgl.js'; import {createTexture} from '../../webgl/Context.js'; -/** - * @constructor - * @abstract - * @extends {module:ol/render/webgl/Replay} - * @param {number} tolerance Tolerance. - * @param {module:ol/extent~Extent} maxExtent Max extent. - * @struct - */ -class WebGLTextureReplay { +class WebGLTextureReplay extends WebGLReplay { + /** + * @constructor + * @abstract + * @extends {module:ol/render/webgl/Replay} + * @param {number} tolerance Tolerance. + * @param {module:ol/extent~Extent} maxExtent Max extent. + * @struct + */ constructor(tolerance, maxExtent) { - WebGLReplay.call(this, tolerance, maxExtent); + super(tolerance, maxExtent); /** * @type {number|undefined} @@ -478,7 +478,5 @@ class WebGLTextureReplay { getHitDetectionTextures() {} } -inherits(WebGLTextureReplay, WebGLReplay); - export default WebGLTextureReplay; diff --git a/src/ol/reproj/Image.js b/src/ol/reproj/Image.js index 4fc11ccec8..d084b46064 100644 --- a/src/ol/reproj/Image.js +++ b/src/ol/reproj/Image.js @@ -17,22 +17,22 @@ import Triangulation from '../reproj/Triangulation.js'; */ -/** - * @classdesc - * Class encapsulating single reprojected image. - * See {@link module:ol/source/Image~ImageSource}. - * - * @constructor - * @extends {module:ol/ImageBase} - * @param {module:ol/proj/Projection} sourceProj Source projection (of the data). - * @param {module:ol/proj/Projection} targetProj Target projection. - * @param {module:ol/extent~Extent} targetExtent Target extent. - * @param {number} targetResolution Target resolution. - * @param {number} pixelRatio Pixel ratio. - * @param {module:ol/reproj/Image~FunctionType} getImageFunction - * Function returning source images (extent, resolution, pixelRatio). - */ -class ReprojImage { +class ReprojImage extends ImageBase { + /** + * @classdesc + * Class encapsulating single reprojected image. + * See {@link module:ol/source/Image~ImageSource}. + * + * @constructor + * @extends {module:ol/ImageBase} + * @param {module:ol/proj/Projection} sourceProj Source projection (of the data). + * @param {module:ol/proj/Projection} targetProj Target projection. + * @param {module:ol/extent~Extent} targetExtent Target extent. + * @param {number} targetResolution Target resolution. + * @param {number} pixelRatio Pixel ratio. + * @param {module:ol/reproj/Image~FunctionType} getImageFunction + * Function returning source images (extent, resolution, pixelRatio). + */ constructor( sourceProj, targetProj, @@ -119,7 +119,7 @@ class ReprojImage { state = ImageState.IDLE; } - ImageBase.call(this, targetExtent, targetResolution, this.sourcePixelRatio_, state); + super(targetExtent, targetResolution, this.sourcePixelRatio_, state); } /** @@ -200,7 +200,5 @@ class ReprojImage { } } -inherits(ReprojImage, ImageBase); - export default ReprojImage; diff --git a/src/ol/reproj/Tile.js b/src/ol/reproj/Tile.js index c399cabacf..a038169edf 100644 --- a/src/ol/reproj/Tile.js +++ b/src/ol/reproj/Tile.js @@ -18,27 +18,27 @@ import Triangulation from '../reproj/Triangulation.js'; */ -/** - * @classdesc - * Class encapsulating single reprojected tile. - * See {@link module:ol/source/TileImage~TileImage}. - * - * @constructor - * @extends {module:ol/Tile} - * @param {module:ol/proj/Projection} sourceProj Source projection. - * @param {module:ol/tilegrid/TileGrid} sourceTileGrid Source tile grid. - * @param {module:ol/proj/Projection} targetProj Target projection. - * @param {module:ol/tilegrid/TileGrid} targetTileGrid Target tile grid. - * @param {module:ol/tilecoord~TileCoord} tileCoord Coordinate of the tile. - * @param {module:ol/tilecoord~TileCoord} wrappedTileCoord Coordinate of the tile wrapped in X. - * @param {number} pixelRatio Pixel ratio. - * @param {number} gutter Gutter of the source tiles. - * @param {module:ol/reproj/Tile~FunctionType} getTileFunction - * Function returning source tiles (z, x, y, pixelRatio). - * @param {number=} opt_errorThreshold Acceptable reprojection error (in px). - * @param {boolean=} opt_renderEdges Render reprojection edges. - */ -class ReprojTile { +class ReprojTile extends Tile { + /** + * @classdesc + * Class encapsulating single reprojected tile. + * See {@link module:ol/source/TileImage~TileImage}. + * + * @constructor + * @extends {module:ol/Tile} + * @param {module:ol/proj/Projection} sourceProj Source projection. + * @param {module:ol/tilegrid/TileGrid} sourceTileGrid Source tile grid. + * @param {module:ol/proj/Projection} targetProj Target projection. + * @param {module:ol/tilegrid/TileGrid} targetTileGrid Target tile grid. + * @param {module:ol/tilecoord~TileCoord} tileCoord Coordinate of the tile. + * @param {module:ol/tilecoord~TileCoord} wrappedTileCoord Coordinate of the tile wrapped in X. + * @param {number} pixelRatio Pixel ratio. + * @param {number} gutter Gutter of the source tiles. + * @param {module:ol/reproj/Tile~FunctionType} getTileFunction + * Function returning source tiles (z, x, y, pixelRatio). + * @param {number=} opt_errorThreshold Acceptable reprojection error (in px). + * @param {boolean=} opt_renderEdges Render reprojection edges. + */ constructor( sourceProj, sourceTileGrid, @@ -52,7 +52,7 @@ class ReprojTile { opt_errorThreshold, opt_renderEdges ) { - Tile.call(this, tileCoord, TileState.IDLE); + super(tileCoord, TileState.IDLE); /** * @private @@ -314,7 +314,5 @@ class ReprojTile { } } -inherits(ReprojTile, Tile); - export default ReprojTile; diff --git a/src/ol/source/BingMaps.js b/src/ol/source/BingMaps.js index aa86003fd3..de5ef5f186 100644 --- a/src/ol/source/BingMaps.js +++ b/src/ol/source/BingMaps.js @@ -44,16 +44,16 @@ const TOS_ATTRIBUTION = '} sources Input @@ -137,19 +135,19 @@ inherits(RasterSourceEvent, Event); */ -/** - * @classdesc - * A source that transforms data from any number of input sources using an - * {@link module:ol/source/Raster~Operation} function to transform input pixel values into - * output pixel values. - * - * @constructor - * @extends {module:ol/source/Image} - * @fires ol/source/Raster~RasterSourceEvent - * @param {module:ol/source/Raster~Options=} options Options. - * @api - */ -class RasterSource { +class RasterSource extends ImageSource { + /** + * @classdesc + * A source that transforms data from any number of input sources using an + * {@link module:ol/source/Raster~Operation} function to transform input pixel values into + * output pixel values. + * + * @constructor + * @extends {module:ol/source/Image} + * @fires ol/source/Raster~RasterSourceEvent + * @param {module:ol/source/Raster~Options=} options Options. + * @api + */ constructor(options) { /** @@ -245,7 +243,7 @@ class RasterSource { wantedTiles: {} }; - ImageSource.call(this, {}); + super({}); if (options.operation !== undefined) { this.setOperation(options.operation, options.lib); @@ -421,8 +419,6 @@ class RasterSource { } } -inherits(RasterSource, ImageSource); - /** * A reusable canvas context. diff --git a/src/ol/source/Source.js b/src/ol/source/Source.js index 7356008f40..84930b9afb 100644 --- a/src/ol/source/Source.js +++ b/src/ol/source/Source.js @@ -37,24 +37,24 @@ import SourceState from '../source/State.js'; */ -/** - * @classdesc - * Abstract base class; normally only used for creating subclasses and not - * instantiated in apps. - * Base class for {@link module:ol/layer/Layer~Layer} sources. - * - * A generic `change` event is triggered when the state of the source changes. - * - * @constructor - * @abstract - * @extends {module:ol/Object} - * @param {module:ol/source/Source~Options} options Source options. - * @api - */ -class Source { +class Source extends BaseObject { + /** + * @classdesc + * Abstract base class; normally only used for creating subclasses and not + * instantiated in apps. + * Base class for {@link module:ol/layer/Layer~Layer} sources. + * + * A generic `change` event is triggered when the state of the source changes. + * + * @constructor + * @abstract + * @extends {module:ol/Object} + * @param {module:ol/source/Source~Options} options Source options. + * @api + */ constructor(options) { - BaseObject.call(this); + super(); /** * @private @@ -177,8 +177,6 @@ class Source { } } -inherits(Source, BaseObject); - /** * @param {module:ol/coordinate~Coordinate} coordinate Coordinate. * @param {number} resolution Resolution. diff --git a/src/ol/source/Stamen.js b/src/ol/source/Stamen.js index bfac063cd9..c75353f459 100644 --- a/src/ol/source/Stamen.js +++ b/src/ol/source/Stamen.js @@ -109,17 +109,17 @@ const ProviderConfig = { */ -/** - * @classdesc - * Layer source for the Stamen tile server. - * - * @constructor - * @extends {module:ol/source/XYZ} - * @param {module:ol/source/Stamen~Options=} options Stamen options. - * @api - */ -class Stamen { +class Stamen extends XYZ { + /** + * @classdesc + * Layer source for the Stamen tile server. + * + * @constructor + * @extends {module:ol/source/XYZ} + * @param {module:ol/source/Stamen~Options=} options Stamen options. + * @api + */ constructor(options) { const i = options.layer.indexOf('-'); const provider = i == -1 ? options.layer : options.layer.slice(0, i); @@ -131,7 +131,7 @@ class Stamen { 'https://stamen-tiles-{a-d}.a.ssl.fastly.net/' + options.layer + '/{z}/{x}/{y}.' + layerConfig.extension; - XYZ.call(this, { + super({ attributions: ATTRIBUTIONS, cacheSize: options.cacheSize, crossOrigin: 'anonymous', @@ -148,6 +148,4 @@ class Stamen { } -inherits(Stamen, XYZ); - export default Stamen; diff --git a/src/ol/source/Tile.js b/src/ol/source/Tile.js index 9c8854b89e..fd8623d7c9 100644 --- a/src/ol/source/Tile.js +++ b/src/ol/source/Tile.js @@ -27,22 +27,22 @@ import {wrapX, getForProjection as getTileGridForProjection} from '../tilegrid.j */ -/** - * @classdesc - * Abstract base class; normally only used for creating subclasses and not - * instantiated in apps. - * Base class for sources providing images divided into a tile grid. - * - * @constructor - * @abstract - * @extends {module:ol/source/Source} - * @param {module:ol/source/Tile~Options=} options SourceTile source options. - * @api - */ -class TileSource { +class TileSource extends Source { + /** + * @classdesc + * Abstract base class; normally only used for creating subclasses and not + * instantiated in apps. + * Base class for sources providing images divided into a tile grid. + * + * @constructor + * @abstract + * @extends {module:ol/source/Source} + * @param {module:ol/source/Tile~Options=} options SourceTile source options. + * @api + */ constructor(options) { - Source.call(this, { + super({ attributions: options.attributions, extent: options.extent, projection: options.projection, @@ -295,8 +295,6 @@ class TileSource { } } -inherits(TileSource, Source); - /** * Marks a tile coord as being used, without triggering a load. @@ -318,11 +316,11 @@ TileSource.prototype.useTile = UNDEFINED; * @param {string} type Type. * @param {module:ol/Tile} tile The tile. */ -export class TileSourceEvent { +export class TileSourceEvent extends Event { constructor(type, tile) { - Event.call(this, type); + super(type); /** * The tile related to the event. @@ -335,6 +333,4 @@ export class TileSourceEvent { } -inherits(TileSourceEvent, Event); - export default TileSource; diff --git a/src/ol/source/TileArcGISRest.js b/src/ol/source/TileArcGISRest.js index b06e72493c..a276b98326 100644 --- a/src/ol/source/TileArcGISRest.js +++ b/src/ol/source/TileArcGISRest.js @@ -51,25 +51,25 @@ import {appendParams} from '../uri.js'; */ -/** - * @classdesc - * Layer source for tile data from ArcGIS Rest services. Map and Image - * Services are supported. - * - * For cached ArcGIS services, better performance is available using the - * {@link module:ol/source/XYZ~XYZ} data source. - * - * @constructor - * @extends {module:ol/source/TileImage} - * @param {module:ol/source/TileArcGISRest~Options=} opt_options Tile ArcGIS Rest options. - * @api - */ -class TileArcGISRest { +class TileArcGISRest extends TileImage { + /** + * @classdesc + * Layer source for tile data from ArcGIS Rest services. Map and Image + * Services are supported. + * + * For cached ArcGIS services, better performance is available using the + * {@link module:ol/source/XYZ~XYZ} data source. + * + * @constructor + * @extends {module:ol/source/TileImage} + * @param {module:ol/source/TileArcGISRest~Options=} opt_options Tile ArcGIS Rest options. + * @api + */ constructor(opt_options) { const options = opt_options || {}; - TileImage.call(this, { + super({ attributions: options.attributions, cacheSize: options.cacheSize, crossOrigin: options.crossOrigin, @@ -216,7 +216,5 @@ class TileArcGISRest { } } -inherits(TileArcGISRest, TileImage); - export default TileArcGISRest; diff --git a/src/ol/source/TileDebug.js b/src/ol/source/TileDebug.js index 93e30bd1d4..07632b58a9 100644 --- a/src/ol/source/TileDebug.js +++ b/src/ol/source/TileDebug.js @@ -10,17 +10,17 @@ import TileSource from '../source/Tile.js'; import {getKeyZXY} from '../tilecoord.js'; -/** - * @constructor - * @extends {module:ol/Tile} - * @param {module:ol/tilecoord~TileCoord} tileCoord Tile coordinate. - * @param {module:ol/size~Size} tileSize Tile size. - * @param {string} text Text. - */ -class LabeledTile { +class LabeledTile extends Tile { + /** + * @constructor + * @extends {module:ol/Tile} + * @param {module:ol/tilecoord~TileCoord} tileCoord Tile coordinate. + * @param {module:ol/size~Size} tileSize Tile size. + * @param {string} text Text. + */ constructor(tileCoord, tileSize, text) { - Tile.call(this, tileCoord, TileState.LOADED); + super(tileCoord, TileState.LOADED); /** * @private @@ -73,8 +73,6 @@ class LabeledTile { load() {} } -inherits(LabeledTile, Tile); - /** * @typedef {Object} Options @@ -84,23 +82,23 @@ inherits(LabeledTile, Tile); */ -/** - * @classdesc - * A pseudo tile source, which does not fetch tiles from a server, but renders - * a grid outline for the tile grid/projection along with the coordinates for - * each tile. See examples/canvas-tiles for an example. - * - * Uses Canvas context2d, so requires Canvas support. - * - * @constructor - * @extends {module:ol/source/Tile} - * @param {module:ol/source/TileDebug~Options=} options Debug tile options. - * @api - */ -class TileDebug { +class TileDebug extends TileSource { + /** + * @classdesc + * A pseudo tile source, which does not fetch tiles from a server, but renders + * a grid outline for the tile grid/projection along with the coordinates for + * each tile. See examples/canvas-tiles for an example. + * + * Uses Canvas context2d, so requires Canvas support. + * + * @constructor + * @extends {module:ol/source/Tile} + * @param {module:ol/source/TileDebug~Options=} options Debug tile options. + * @api + */ constructor(options) { - TileSource.call(this, { + super({ opaque: false, projection: options.projection, tileGrid: options.tileGrid, @@ -129,7 +127,5 @@ class TileDebug { } } -inherits(TileDebug, TileSource); - export default TileDebug; diff --git a/src/ol/source/TileImage.js b/src/ol/source/TileImage.js index f59a06ec41..7e79507743 100644 --- a/src/ol/source/TileImage.js +++ b/src/ol/source/TileImage.js @@ -54,20 +54,20 @@ import {getForProjection as getTileGridForProjection} from '../tilegrid.js'; */ -/** - * @classdesc - * Base class for sources providing images divided into a tile grid. - * - * @constructor - * @fires module:ol/source/Tile~TileSourceEvent - * @extends {module:ol/source/UrlTile} - * @param {module:ol/source/TileImage~Options=} options Image tile options. - * @api - */ -class TileImage { +class TileImage extends UrlTile { + /** + * @classdesc + * Base class for sources providing images divided into a tile grid. + * + * @constructor + * @fires module:ol/source/Tile~TileSourceEvent + * @extends {module:ol/source/UrlTile} + * @param {module:ol/source/TileImage~Options=} options Image tile options. + * @api + */ constructor(options) { - UrlTile.call(this, { + super({ attributions: options.attributions, cacheSize: options.cacheSize, extent: options.extent, @@ -389,8 +389,6 @@ class TileImage { } } -inherits(TileImage, UrlTile); - /** * @param {module:ol/ImageTile} imageTile Image tile. diff --git a/src/ol/source/TileJSON.js b/src/ol/source/TileJSON.js index 7a6ee11678..354ecaadcd 100644 --- a/src/ol/source/TileJSON.js +++ b/src/ol/source/TileJSON.js @@ -44,16 +44,16 @@ import {createXYZ, extentFromProjection} from '../tilegrid.js'; */ -/** - * @classdesc - * Layer source for tile data in TileJSON format. - * - * @constructor - * @extends {module:ol/source/TileImage} - * @param {module:ol/source/TileJSON~Options=} options TileJSON options. - * @api - */ -class TileJSON { +class TileJSON extends TileImage { + /** + * @classdesc + * Layer source for tile data in TileJSON format. + * + * @constructor + * @extends {module:ol/source/TileImage} + * @param {module:ol/source/TileJSON~Options=} options TileJSON options. + * @api + */ constructor(options) { /** @@ -62,7 +62,7 @@ class TileJSON { */ this.tileJSON_ = null; - TileImage.call(this, { + super({ attributions: options.attributions, cacheSize: options.cacheSize, crossOrigin: options.crossOrigin, @@ -182,7 +182,5 @@ class TileJSON { } } -inherits(TileJSON, TileImage); - export default TileJSON; diff --git a/src/ol/source/TileWMS.js b/src/ol/source/TileWMS.js index 1e053d19c0..849f4e1da1 100644 --- a/src/ol/source/TileWMS.js +++ b/src/ol/source/TileWMS.js @@ -71,16 +71,16 @@ import {appendParams} from '../uri.js'; */ -/** - * @classdesc - * Layer source for tile data from WMS servers. - * - * @constructor - * @extends {module:ol/source/TileImage} - * @param {module:ol/source/TileWMS~Options=} [opt_options] Tile WMS options. - * @api - */ -class TileWMS { +class TileWMS extends TileImage { + /** + * @classdesc + * Layer source for tile data from WMS servers. + * + * @constructor + * @extends {module:ol/source/TileImage} + * @param {module:ol/source/TileWMS~Options=} [opt_options] Tile WMS options. + * @api + */ constructor(opt_options) { const options = opt_options || {}; @@ -89,7 +89,7 @@ class TileWMS { const transparent = 'TRANSPARENT' in params ? params['TRANSPARENT'] : true; - TileImage.call(this, { + super({ attributions: options.attributions, cacheSize: options.cacheSize, crossOrigin: options.crossOrigin, @@ -388,7 +388,5 @@ class TileWMS { } } -inherits(TileWMS, TileImage); - export default TileWMS; diff --git a/src/ol/source/UTFGrid.js b/src/ol/source/UTFGrid.js index a90baa5b6b..f763ec3e19 100644 --- a/src/ol/source/UTFGrid.js +++ b/src/ol/source/UTFGrid.js @@ -27,11 +27,11 @@ import {createXYZ, extentFromProjection} from '../tilegrid.js'; * @param {boolean} preemptive Load the tile when visible (before it's needed). * @param {boolean} jsonp Load the tile as a script. */ -export class CustomTile { +export class CustomTile extends Tile { constructor(tileCoord, state, src, extent, preemptive, jsonp) { - Tile.call(this, tileCoord, state); + super(tileCoord, state); /** * @private @@ -80,8 +80,6 @@ export class CustomTile { } -inherits(CustomTile, Tile); - /** * Get the image element for this tile. @@ -271,19 +269,19 @@ CustomTile.prototype.load = function() { */ -/** - * @classdesc - * Layer source for UTFGrid interaction data loaded from TileJSON format. - * - * @constructor - * @extends {module:ol/source/Tile} - * @param {module:ol/source/UTFGrid~Options=} options Source options. - * @api - */ -class UTFGrid { +class UTFGrid extends TileSource { + /** + * @classdesc + * Layer source for UTFGrid interaction data loaded from TileJSON format. + * + * @constructor + * @extends {module:ol/source/Tile} + * @param {module:ol/source/UTFGrid~Options=} options Source options. + * @api + */ constructor(options) { - TileSource.call(this, { + super({ projection: getProjection('EPSG:3857'), state: SourceState.LOADING }); @@ -334,8 +332,6 @@ class UTFGrid { } -inherits(UTFGrid, TileSource); - /** * @private diff --git a/src/ol/source/UrlTile.js b/src/ol/source/UrlTile.js index 542c376d61..b19f558f77 100644 --- a/src/ol/source/UrlTile.js +++ b/src/ol/source/UrlTile.js @@ -27,20 +27,20 @@ import {getKeyZXY} from '../tilecoord.js'; */ -/** - * @classdesc - * Base class for sources providing tiles divided into a tile grid over http. - * - * @constructor - * @abstract - * @fires module:ol/source/TileEvent - * @extends {module:ol/source/Tile} - * @param {module:ol/source/UrlTile~Options=} options Image tile options. - */ -class UrlTile { +class UrlTile extends TileSource { + /** + * @classdesc + * Base class for sources providing tiles divided into a tile grid over http. + * + * @constructor + * @abstract + * @fires module:ol/source/TileEvent + * @extends {module:ol/source/Tile} + * @param {module:ol/source/UrlTile~Options=} options Image tile options. + */ constructor(options) { - TileSource.call(this, { + super({ attributions: options.attributions, cacheSize: options.cacheSize, extent: options.extent, @@ -205,8 +205,6 @@ class UrlTile { } } -inherits(UrlTile, TileSource); - /** * @type {module:ol/Tile~UrlFunction|undefined} diff --git a/src/ol/source/Vector.js b/src/ol/source/Vector.js index 7b8fe888b9..3ef13cb11d 100644 --- a/src/ol/source/Vector.js +++ b/src/ol/source/Vector.js @@ -41,11 +41,11 @@ import RBush from '../structs/RBush.js'; * @param {string} type Type. * @param {module:ol/Feature=} opt_feature Feature. */ -export class VectorSourceEvent { +export class VectorSourceEvent extends Event { constructor(type, opt_feature) { - Event.call(this, type); + super(type); /** * The feature being added or removed. @@ -58,8 +58,6 @@ export class VectorSourceEvent { } -inherits(VectorSourceEvent, Event); - /** * @typedef {Object} Options @@ -151,25 +149,25 @@ inherits(VectorSourceEvent, Event); */ -/** - * @classdesc - * Provides a source of features for vector layers. Vector features provided - * by this source are suitable for editing. See {@link module:ol/source/VectorTile~VectorTile} for - * vector data that is optimized for rendering. - * - * @constructor - * @extends {module:ol/source/Source} - * @fires ol/source/Vector~VectorSourceEvent - * @param {module:ol/source/Vector~Options=} opt_options Vector source options. - * @api - */ -class VectorSource { +class VectorSource extends Source { + /** + * @classdesc + * Provides a source of features for vector layers. Vector features provided + * by this source are suitable for editing. See {@link module:ol/source/VectorTile~VectorTile} for + * vector data that is optimized for rendering. + * + * @constructor + * @extends {module:ol/source/Source} + * @fires ol/source/Vector~VectorSourceEvent + * @param {module:ol/source/Vector~Options=} opt_options Vector source options. + * @api + */ constructor(opt_options) { const options = opt_options || {}; - Source.call(this, { + super({ attributions: options.attributions, projection: undefined, state: SourceState.READY, @@ -282,8 +280,6 @@ class VectorSource { } -inherits(VectorSource, Source); - /** * Add a single feature to the source. If you want to add a batch of features diff --git a/src/ol/source/VectorTile.js b/src/ol/source/VectorTile.js index a38ae74645..e1c1158596 100644 --- a/src/ol/source/VectorTile.js +++ b/src/ol/source/VectorTile.js @@ -58,24 +58,24 @@ import {createXYZ, extentFromProjection, createForProjection} from '../tilegrid. */ -/** - * @classdesc - * Class for layer sources providing vector data divided into a tile grid, to be - * used with {@link module:ol/layer/VectorTile~VectorTile}. Although this source receives tiles - * with vector features from the server, it is not meant for feature editing. - * Features are optimized for rendering, their geometries are clipped at or near - * tile boundaries and simplified for a view resolution. See - * {@link module:ol/source/Vector} for vector sources that are suitable for feature - * editing. - * - * @constructor - * @fires module:ol/source/Tile~TileSourceEvent - * @extends {module:ol/source/UrlTile} - * @param {module:ol/source/VectorTile~Options=} options Vector tile options. - * @api - */ -class VectorTile { +class VectorTile extends UrlTile { + /** + * @classdesc + * Class for layer sources providing vector data divided into a tile grid, to be + * used with {@link module:ol/layer/VectorTile~VectorTile}. Although this source receives tiles + * with vector features from the server, it is not meant for feature editing. + * Features are optimized for rendering, their geometries are clipped at or near + * tile boundaries and simplified for a view resolution. See + * {@link module:ol/source/Vector} for vector sources that are suitable for feature + * editing. + * + * @constructor + * @fires module:ol/source/Tile~TileSourceEvent + * @extends {module:ol/source/UrlTile} + * @param {module:ol/source/VectorTile~Options=} options Vector tile options. + * @api + */ constructor(options) { const projection = options.projection || 'EPSG:3857'; @@ -88,7 +88,7 @@ class VectorTile { tileSize: options.tileSize || 512 }); - UrlTile.call(this, { + super({ attributions: options.attributions, cacheSize: options.cacheSize !== undefined ? options.cacheSize : 128, extent: extent, @@ -139,8 +139,6 @@ class VectorTile { } -inherits(VectorTile, UrlTile); - /** * @return {boolean} The source can have overlapping geometries. diff --git a/src/ol/source/WMTS.js b/src/ol/source/WMTS.js index 7a4a0c99a5..f309b09d53 100644 --- a/src/ol/source/WMTS.js +++ b/src/ol/source/WMTS.js @@ -56,17 +56,17 @@ import {appendParams} from '../uri.js'; */ -/** - * @classdesc - * Layer source for tile data from WMTS servers. - * - * @constructor - * @extends {module:ol/source/TileImage} - * @param {module:ol/source/WMTS~Options=} options WMTS options. - * @api - */ -class WMTS { +class WMTS extends TileImage { + /** + * @classdesc + * Layer source for tile data from WMTS servers. + * + * @constructor + * @extends {module:ol/source/TileImage} + * @param {module:ol/source/WMTS~Options=} options WMTS options. + * @api + */ constructor(options) { // TODO: add support for TileMatrixLimits @@ -199,7 +199,7 @@ class WMTS { const tileUrlFunction = (urls && urls.length > 0) ? createFromTileUrlFunctions(urls.map(this.createFromWMTSTemplate_)) : nullTileUrlFunction; - TileImage.call(this, { + super({ attributions: options.attributions, cacheSize: options.cacheSize, crossOrigin: options.crossOrigin, @@ -221,8 +221,6 @@ class WMTS { } -inherits(WMTS, TileImage); - /** * Set the URLs to use for requests. * URLs may contain OCG conform URL Template Variables: {TileMatrix}, {TileRow}, {TileCol}. diff --git a/src/ol/source/XYZ.js b/src/ol/source/XYZ.js index 6f2b90996c..b5a1590485 100644 --- a/src/ol/source/XYZ.js +++ b/src/ol/source/XYZ.js @@ -44,30 +44,30 @@ import {createXYZ, extentFromProjection} from '../tilegrid.js'; */ -/** - * @classdesc - * Layer source for tile data with URLs in a set XYZ format that are - * defined in a URL template. By default, this follows the widely-used - * Google grid where `x` 0 and `y` 0 are in the top left. Grids like - * TMS where `x` 0 and `y` 0 are in the bottom left can be used by - * using the `{-y}` placeholder in the URL template, so long as the - * source does not have a custom tile grid. In this case, - * {@link module:ol/source/TileImage} can be used with a `tileUrlFunction` - * such as: - * - * tileUrlFunction: function(coordinate) { - * return 'http://mapserver.com/' + coordinate[0] + '/' + - * coordinate[1] + '/' + coordinate[2] + '.png'; - * } - * - * - * @constructor - * @extends {module:ol/source/TileImage} - * @param {module:ol/source/XYZ~Options=} opt_options XYZ options. - * @api - */ -class XYZ { +class XYZ extends TileImage { + /** + * @classdesc + * Layer source for tile data with URLs in a set XYZ format that are + * defined in a URL template. By default, this follows the widely-used + * Google grid where `x` 0 and `y` 0 are in the top left. Grids like + * TMS where `x` 0 and `y` 0 are in the bottom left can be used by + * using the `{-y}` placeholder in the URL template, so long as the + * source does not have a custom tile grid. In this case, + * {@link module:ol/source/TileImage} can be used with a `tileUrlFunction` + * such as: + * + * tileUrlFunction: function(coordinate) { + * return 'http://mapserver.com/' + coordinate[0] + '/' + + * coordinate[1] + '/' + coordinate[2] + '.png'; + * } + * + * + * @constructor + * @extends {module:ol/source/TileImage} + * @param {module:ol/source/XYZ~Options=} opt_options XYZ options. + * @api + */ constructor(opt_options) { const options = opt_options || {}; const projection = options.projection !== undefined ? @@ -81,7 +81,7 @@ class XYZ { tileSize: options.tileSize }); - TileImage.call(this, { + super({ attributions: options.attributions, cacheSize: options.cacheSize, crossOrigin: options.crossOrigin, @@ -102,6 +102,4 @@ class XYZ { } -inherits(XYZ, TileImage); - export default XYZ; diff --git a/src/ol/source/Zoomify.js b/src/ol/source/Zoomify.js index 6917422764..b8a0818d51 100644 --- a/src/ol/source/Zoomify.js +++ b/src/ol/source/Zoomify.js @@ -34,11 +34,11 @@ const TierSizeCalculation = { * @param {module:ol/Tile~LoadFunction} tileLoadFunction Tile load function. * @param {module:ol/Tile~Options=} opt_options Tile options. */ -export class CustomTile { +export class CustomTile extends ImageTile { constructor(tileGrid, tileCoord, state, src, crossOrigin, tileLoadFunction, opt_options) { - ImageTile.call(this, tileCoord, state, src, crossOrigin, tileLoadFunction, opt_options); + super(tileCoord, state, src, crossOrigin, tileLoadFunction, opt_options); /** * @private @@ -56,8 +56,6 @@ export class CustomTile { } -inherits(CustomTile, ImageTile); - /** * @inheritDoc @@ -119,18 +117,18 @@ CustomTile.prototype.getImage = function() { */ -/** - * @classdesc - * Layer source for tile data in Zoomify format (both Zoomify and Internet - * Imaging Protocol are supported). - * - * @constructor - * @extends {module:ol/source/TileImage} - * @param {module:ol/source/Zoomify~Options=} opt_options Options. - * @api - */ -class Zoomify { +class Zoomify extends TileImage { + /** + * @classdesc + * Layer source for tile data in Zoomify format (both Zoomify and Internet + * Imaging Protocol are supported). + * + * @constructor + * @extends {module:ol/source/TileImage} + * @param {module:ol/source/Zoomify~Options=} opt_options Options. + * @api + */ constructor(opt_options) { const options = opt_options || {}; @@ -245,7 +243,7 @@ class Zoomify { const ZoomifyTileClass = CustomTile.bind(null, tileGrid); - TileImage.call(this, { + super({ attributions: options.attributions, cacheSize: options.cacheSize, crossOrigin: options.crossOrigin, @@ -261,6 +259,4 @@ class Zoomify { } -inherits(Zoomify, TileImage); - export default Zoomify; diff --git a/src/ol/structs/LRUCache.js b/src/ol/structs/LRUCache.js index bfa20ad27b..e3a90ec94e 100644 --- a/src/ol/structs/LRUCache.js +++ b/src/ol/structs/LRUCache.js @@ -16,22 +16,22 @@ import EventType from '../events/EventType.js'; */ -/** - * Implements a Least-Recently-Used cache where the keys do not conflict with - * Object's properties (e.g. 'hasOwnProperty' is not allowed as a key). Expiring - * items from the cache is the responsibility of the user. - * @constructor - * @extends {module:ol/events/EventTarget} - * @fires module:ol/events/Event~Event - * @struct - * @template T - * @param {number=} opt_highWaterMark High water mark. - */ -class LRUCache { +class LRUCache extends EventTarget { + /** + * Implements a Least-Recently-Used cache where the keys do not conflict with + * Object's properties (e.g. 'hasOwnProperty' is not allowed as a key). Expiring + * items from the cache is the responsibility of the user. + * @constructor + * @extends {module:ol/events/EventTarget} + * @fires module:ol/events/Event~Event + * @struct + * @template T + * @param {number=} opt_highWaterMark High water mark. + */ constructor(opt_highWaterMark) { - EventTarget.call(this); + super(); /** * @type {number} @@ -298,6 +298,4 @@ class LRUCache { } -inherits(LRUCache, EventTarget); - export default LRUCache; diff --git a/src/ol/style/Circle.js b/src/ol/style/Circle.js index 5851760cb7..a79fbc7c9c 100644 --- a/src/ol/style/Circle.js +++ b/src/ol/style/Circle.js @@ -20,21 +20,21 @@ import RegularShape from '../style/RegularShape.js'; */ -/** - * @classdesc - * Set circle style for vector features. - * - * @constructor - * @param {module:ol/style/Circle~Options=} opt_options Options. - * @extends {module:ol/style/RegularShape} - * @api - */ -class CircleStyle { +class CircleStyle extends RegularShape { + /** + * @classdesc + * Set circle style for vector features. + * + * @constructor + * @param {module:ol/style/Circle~Options=} opt_options Options. + * @extends {module:ol/style/RegularShape} + * @api + */ constructor(opt_options) { const options = opt_options || {}; - RegularShape.call(this, { + super({ points: Infinity, fill: options.fill, radius: options.radius, @@ -76,7 +76,5 @@ class CircleStyle { } } -inherits(CircleStyle, RegularShape); - export default CircleStyle; diff --git a/src/ol/style/Icon.js b/src/ol/style/Icon.js index 50f60e3cc7..715125fa27 100644 --- a/src/ol/style/Icon.js +++ b/src/ol/style/Icon.js @@ -52,16 +52,16 @@ import ImageStyle from '../style/Image.js'; */ -/** - * @classdesc - * Set icon style for vector features. - * - * @constructor - * @param {module:ol/style/Icon~Options=} opt_options Options. - * @extends {module:ol/style/Image} - * @api - */ -class Icon { +class Icon extends ImageStyle { + /** + * @classdesc + * Set icon style for vector features. + * + * @constructor + * @param {module:ol/style/Icon~Options=} opt_options Options. + * @extends {module:ol/style/Image} + * @api + */ constructor(opt_options) { const options = opt_options || {}; @@ -203,7 +203,7 @@ class Icon { const snapToPixel = options.snapToPixel !== undefined ? options.snapToPixel : true; - ImageStyle.call(this, { + super({ opacity: opacity, rotation: rotation, scale: scale, @@ -418,7 +418,5 @@ class Icon { } } -inherits(Icon, ImageStyle); - export default Icon; diff --git a/src/ol/style/IconImage.js b/src/ol/style/IconImage.js index 4f677a6b51..194eb443c1 100644 --- a/src/ol/style/IconImage.js +++ b/src/ol/style/IconImage.js @@ -9,20 +9,20 @@ import EventType from '../events/EventType.js'; import ImageState from '../ImageState.js'; import {shared as iconImageCache} from '../style/IconImageCache.js'; -/** - * @constructor - * @param {HTMLImageElement|HTMLCanvasElement} image Image. - * @param {string|undefined} src Src. - * @param {module:ol/size~Size} size Size. - * @param {?string} crossOrigin Cross origin. - * @param {module:ol/ImageState} imageState Image state. - * @param {module:ol/color~Color} color Color. - * @extends {module:ol/events/EventTarget} - */ -class IconImage { +class IconImage extends EventTarget { + /** + * @constructor + * @param {HTMLImageElement|HTMLCanvasElement} image Image. + * @param {string|undefined} src Src. + * @param {module:ol/size~Size} size Size. + * @param {?string} crossOrigin Cross origin. + * @param {module:ol/ImageState} imageState Image state. + * @param {module:ol/color~Color} color Color. + * @extends {module:ol/events/EventTarget} + */ constructor(image, src, size, crossOrigin, imageState, color) { - EventTarget.call(this); + super(); /** * @private @@ -241,8 +241,6 @@ class IconImage { } } -inherits(IconImage, EventTarget); - /** * @param {HTMLImageElement|HTMLCanvasElement} image Image. diff --git a/src/ol/style/RegularShape.js b/src/ol/style/RegularShape.js index 4b786d9902..c3efe8bca2 100644 --- a/src/ol/style/RegularShape.js +++ b/src/ol/style/RegularShape.js @@ -46,18 +46,18 @@ import ImageStyle from '../style/Image.js'; */ -/** - * @classdesc - * Set regular shape style for vector features. The resulting shape will be - * a regular polygon when `radius` is provided, or a star when `radius1` and - * `radius2` are provided. - * - * @constructor - * @param {module:ol/style/RegularShape~Options} options Options. - * @extends {module:ol/style/Image} - * @api - */ -class RegularShape { +class RegularShape extends ImageStyle { + /** + * @classdesc + * Set regular shape style for vector features. The resulting shape will be + * a regular polygon when `radius` is provided, or a star when `radius1` and + * `radius2` are provided. + * + * @constructor + * @param {module:ol/style/RegularShape~Options} options Options. + * @extends {module:ol/style/Image} + * @api + */ constructor(options) { /** * @private @@ -164,7 +164,7 @@ class RegularShape { const rotateWithView = options.rotateWithView !== undefined ? options.rotateWithView : false; - ImageStyle.call(this, { + super({ opacity: 1, rotateWithView: rotateWithView, rotation: options.rotation !== undefined ? options.rotation : 0, @@ -591,7 +591,5 @@ class RegularShape { } } -inherits(RegularShape, ImageStyle); - export default RegularShape; diff --git a/src/ol/tilegrid/WMTS.js b/src/ol/tilegrid/WMTS.js index 6fc2dd9178..972b357017 100644 --- a/src/ol/tilegrid/WMTS.js +++ b/src/ol/tilegrid/WMTS.js @@ -45,26 +45,24 @@ import TileGrid from '../tilegrid/TileGrid.js'; */ -/** - * @classdesc - * Set the grid pattern for sources accessing WMTS tiled-image servers. - * - * @constructor - * @extends {module:ol/tilegrid/TileGrid} - * @param {module:ol/tilegrid/WMTS~Options} options WMTS options. - * @struct - * @api - */ -class WMTSTileGrid { +class WMTSTileGrid extends TileGrid { + /** + * @classdesc + * Set the grid pattern for sources accessing WMTS tiled-image servers. + * + * @constructor + * @extends {module:ol/tilegrid/TileGrid} + * @param {module:ol/tilegrid/WMTS~Options} options WMTS options. + * @struct + * @api + */ constructor(options) { /** * @private * @type {!Array.} */ this.matrixIds_ = options.matrixIds; - // FIXME: should the matrixIds become optional? - - TileGrid.call(this, { + super({ extent: options.extent, origin: options.origin, origins: options.origins, @@ -93,8 +91,6 @@ class WMTSTileGrid { } } -inherits(WMTSTileGrid, TileGrid); - export default WMTSTileGrid; diff --git a/src/ol/webgl/Context.js b/src/ol/webgl/Context.js index ed04d45e6a..bb90bf3ee9 100644 --- a/src/ol/webgl/Context.js +++ b/src/ol/webgl/Context.js @@ -24,7 +24,7 @@ import ContextEventType from '../webgl/ContextEventType.js'; * * @extends {module:ol/Disposable} */ -class WebGLContext { +class WebGLContext extends Disposable { /** * @param {HTMLCanvasElement} canvas Canvas. @@ -305,8 +305,6 @@ class WebGLContext { } } -inherits(WebGLContext, Disposable); - /** * @param {WebGLRenderingContext} gl WebGL rendering context. diff --git a/src/ol/webgl/Fragment.js b/src/ol/webgl/Fragment.js index 809f5726d0..3b27053d40 100644 --- a/src/ol/webgl/Fragment.js +++ b/src/ol/webgl/Fragment.js @@ -8,13 +8,13 @@ import WebGLShader from '../webgl/Shader.js'; /** * @extends {module:ol/webgl/Shader} */ -class WebGLFragment { +class WebGLFragment extends WebGLShader { /** * @param {string} source Source. */ constructor(source) { - WebGLShader.call(this, source); + super(source); } /** @@ -25,7 +25,5 @@ class WebGLFragment { } } -inherits(WebGLFragment, WebGLShader); - export default WebGLFragment; diff --git a/src/ol/webgl/Vertex.js b/src/ol/webgl/Vertex.js index eadd8dd316..494b972387 100644 --- a/src/ol/webgl/Vertex.js +++ b/src/ol/webgl/Vertex.js @@ -8,13 +8,13 @@ import WebGLShader from '../webgl/Shader.js'; /** * @extends {module:ol/webgl/Shader} */ -class WebGLVertex { +class WebGLVertex extends WebGLShader { /** * @param {string} source Source. */ constructor(source) { - WebGLShader.call(this, source); + super(source); } /** @@ -25,7 +25,5 @@ class WebGLVertex { } } -inherits(WebGLVertex, WebGLShader); - export default WebGLVertex; From b7b15eb7f0e888faa695fe65b5cc7613a9059e11 Mon Sep 17 00:00:00 2001 From: Tim Schaub Date: Wed, 18 Jul 2018 00:13:43 -0600 Subject: [PATCH 081/107] Remove unused inherits --- src/ol/layer/Base.js | 1 - src/ol/layer/Group.js | 4 ++-- src/ol/layer/Heatmap.js | 1 - src/ol/layer/Image.js | 1 - src/ol/layer/Layer.js | 2 +- src/ol/layer/Tile.js | 1 - src/ol/layer/Vector.js | 1 - src/ol/layer/VectorTile.js | 1 - src/ol/render/Box.js | 2 -- src/ol/render/Event.js | 2 +- src/ol/render/canvas/ImageReplay.js | 1 - src/ol/render/canvas/Immediate.js | 1 - src/ol/render/canvas/LineStringReplay.js | 1 - src/ol/render/canvas/PolygonReplay.js | 1 - src/ol/render/canvas/Replay.js | 2 +- src/ol/render/canvas/ReplayGroup.js | 2 +- src/ol/render/canvas/TextReplay.js | 2 +- src/ol/render/webgl/CircleReplay.js | 2 +- src/ol/render/webgl/ImageReplay.js | 2 +- src/ol/render/webgl/Immediate.js | 2 +- src/ol/render/webgl/LineStringReplay.js | 2 +- src/ol/render/webgl/PolygonReplay.js | 2 +- src/ol/render/webgl/Replay.js | 2 +- src/ol/render/webgl/ReplayGroup.js | 2 +- src/ol/render/webgl/TextReplay.js | 2 +- src/ol/render/webgl/TextureReplay.js | 2 +- src/ol/reproj/Image.js | 2 +- src/ol/reproj/Tile.js | 2 +- src/ol/source/BingMaps.js | 2 +- src/ol/source/CartoDB.js | 2 +- src/ol/source/Cluster.js | 2 +- src/ol/source/Image.js | 2 +- src/ol/source/ImageArcGISRest.js | 2 +- src/ol/source/ImageCanvas.js | 2 +- src/ol/source/ImageMapGuide.js | 2 +- src/ol/source/ImageStatic.js | 2 +- src/ol/source/ImageWMS.js | 2 +- src/ol/source/OSM.js | 2 +- src/ol/source/Raster.js | 2 +- src/ol/source/Source.js | 2 +- src/ol/source/Stamen.js | 2 +- src/ol/source/Tile.js | 2 +- src/ol/source/TileArcGISRest.js | 2 +- src/ol/source/TileDebug.js | 2 +- src/ol/source/TileImage.js | 2 +- src/ol/source/TileJSON.js | 2 +- src/ol/source/TileWMS.js | 2 +- src/ol/source/UTFGrid.js | 2 +- src/ol/source/UrlTile.js | 2 +- src/ol/source/Vector.js | 2 +- src/ol/source/VectorTile.js | 2 +- src/ol/source/WMTS.js | 2 +- src/ol/source/XYZ.js | 2 +- src/ol/source/Zoomify.js | 2 +- src/ol/structs/LRUCache.js | 2 +- src/ol/style/Circle.js | 2 +- src/ol/style/Icon.js | 2 +- src/ol/style/IconImage.js | 2 +- src/ol/style/RegularShape.js | 2 +- src/ol/tilegrid/WMTS.js | 2 +- src/ol/webgl/Context.js | 2 +- src/ol/webgl/Fragment.js | 2 +- src/ol/webgl/Vertex.js | 2 +- 63 files changed, 53 insertions(+), 65 deletions(-) diff --git a/src/ol/layer/Base.js b/src/ol/layer/Base.js index 48a2a8e705..8fd2740bad 100644 --- a/src/ol/layer/Base.js +++ b/src/ol/layer/Base.js @@ -1,7 +1,6 @@ /** * @module ol/layer/Base */ -import {inherits} from '../util.js'; import BaseObject from '../Object.js'; import LayerProperty from '../layer/Property.js'; import {clamp} from '../math.js'; diff --git a/src/ol/layer/Group.js b/src/ol/layer/Group.js index 4b68fb48b3..8c6ce1fae3 100644 --- a/src/ol/layer/Group.js +++ b/src/ol/layer/Group.js @@ -1,7 +1,7 @@ /** * @module ol/layer/Group */ -import {getUid, inherits} from '../util.js'; +import {getUid} from '../util.js'; import Collection from '../Collection.js'; import CollectionEventType from '../CollectionEventType.js'; import {getChangeEventType} from '../Object.js'; @@ -105,7 +105,7 @@ class LayerGroup extends BaseLayer { * @param {module:ol/events/Event} event Event. * @private */ - handleLayersChanged_(event) { + handleLayersChanged_() { this.layersListenerKeys_.forEach(unlistenByKey); this.layersListenerKeys_.length = 0; diff --git a/src/ol/layer/Heatmap.js b/src/ol/layer/Heatmap.js index 7df2be2043..17ee8a1914 100644 --- a/src/ol/layer/Heatmap.js +++ b/src/ol/layer/Heatmap.js @@ -2,7 +2,6 @@ * @module ol/layer/Heatmap */ import {listen} from '../events.js'; -import {inherits} from '../util.js'; import {getChangeEventType} from '../Object.js'; import {createCanvasContext2D} from '../dom.js'; import VectorLayer from '../layer/Vector.js'; diff --git a/src/ol/layer/Image.js b/src/ol/layer/Image.js index 9828ab8043..e3aac7a201 100644 --- a/src/ol/layer/Image.js +++ b/src/ol/layer/Image.js @@ -1,7 +1,6 @@ /** * @module ol/layer/Image */ -import {inherits} from '../util.js'; import LayerType from '../LayerType.js'; import Layer from '../layer/Layer.js'; diff --git a/src/ol/layer/Layer.js b/src/ol/layer/Layer.js index aff9ea4495..f93399eacc 100644 --- a/src/ol/layer/Layer.js +++ b/src/ol/layer/Layer.js @@ -3,7 +3,7 @@ */ import {listen, unlistenByKey} from '../events.js'; import EventType from '../events/EventType.js'; -import {getUid, inherits} from '../util.js'; +import {getUid} from '../util.js'; import {getChangeEventType} from '../Object.js'; import BaseLayer from '../layer/Base.js'; import LayerProperty from '../layer/Property.js'; diff --git a/src/ol/layer/Tile.js b/src/ol/layer/Tile.js index ba74bb3311..ee5b078275 100644 --- a/src/ol/layer/Tile.js +++ b/src/ol/layer/Tile.js @@ -1,7 +1,6 @@ /** * @module ol/layer/Tile */ -import {inherits} from '../util.js'; import LayerType from '../LayerType.js'; import Layer from '../layer/Layer.js'; import TileProperty from '../layer/TileProperty.js'; diff --git a/src/ol/layer/Vector.js b/src/ol/layer/Vector.js index 5ddcdc5b19..e1ebfcc451 100644 --- a/src/ol/layer/Vector.js +++ b/src/ol/layer/Vector.js @@ -1,7 +1,6 @@ /** * @module ol/layer/Vector */ -import {inherits} from '../util.js'; import LayerType from '../LayerType.js'; import Layer from '../layer/Layer.js'; import VectorRenderType from '../layer/VectorRenderType.js'; diff --git a/src/ol/layer/VectorTile.js b/src/ol/layer/VectorTile.js index 0afe3ed466..33b29d5f2b 100644 --- a/src/ol/layer/VectorTile.js +++ b/src/ol/layer/VectorTile.js @@ -1,7 +1,6 @@ /** * @module ol/layer/VectorTile */ -import {inherits} from '../util.js'; import LayerType from '../LayerType.js'; import {assert} from '../asserts.js'; import TileProperty from '../layer/TileProperty.js'; diff --git a/src/ol/render/Box.js b/src/ol/render/Box.js index f51afd682b..2831998822 100644 --- a/src/ol/render/Box.js +++ b/src/ol/render/Box.js @@ -1,9 +1,7 @@ /** * @module ol/render/Box */ -// FIXME add rotation -import {inherits} from '../util.js'; import Disposable from '../Disposable.js'; import Polygon from '../geom/Polygon.js'; diff --git a/src/ol/render/Event.js b/src/ol/render/Event.js index 4bab695a7d..565e1c10e7 100644 --- a/src/ol/render/Event.js +++ b/src/ol/render/Event.js @@ -1,7 +1,7 @@ /** * @module ol/render/Event */ -import {inherits} from '../util.js'; + import Event from '../events/Event.js'; class RenderEvent extends Event { diff --git a/src/ol/render/canvas/ImageReplay.js b/src/ol/render/canvas/ImageReplay.js index c348f22e7d..9d48125380 100644 --- a/src/ol/render/canvas/ImageReplay.js +++ b/src/ol/render/canvas/ImageReplay.js @@ -1,7 +1,6 @@ /** * @module ol/render/canvas/ImageReplay */ -import {inherits} from '../../util.js'; import CanvasInstruction from '../canvas/Instruction.js'; import CanvasReplay from '../canvas/Replay.js'; diff --git a/src/ol/render/canvas/Immediate.js b/src/ol/render/canvas/Immediate.js index dcc26ceace..4fc556293f 100644 --- a/src/ol/render/canvas/Immediate.js +++ b/src/ol/render/canvas/Immediate.js @@ -5,7 +5,6 @@ // FIXME need to handle large thick features (where pixel size matters) // FIXME add offset and end to ol/geom/flat/transform~transform2D? -import {inherits} from '../../util.js'; import {equals} from '../../array.js'; import {asColorLike} from '../../colorlike.js'; import {intersects} from '../../extent.js'; diff --git a/src/ol/render/canvas/LineStringReplay.js b/src/ol/render/canvas/LineStringReplay.js index c16abb9f57..d63b2ea101 100644 --- a/src/ol/render/canvas/LineStringReplay.js +++ b/src/ol/render/canvas/LineStringReplay.js @@ -1,7 +1,6 @@ /** * @module ol/render/canvas/LineStringReplay */ -import {inherits} from '../../util.js'; import CanvasInstruction, {strokeInstruction, beginPathInstruction} from '../canvas/Instruction.js'; import CanvasReplay from '../canvas/Replay.js'; diff --git a/src/ol/render/canvas/PolygonReplay.js b/src/ol/render/canvas/PolygonReplay.js index 50c90e0aed..1b7f99d470 100644 --- a/src/ol/render/canvas/PolygonReplay.js +++ b/src/ol/render/canvas/PolygonReplay.js @@ -1,7 +1,6 @@ /** * @module ol/render/canvas/PolygonReplay */ -import {inherits} from '../../util.js'; import {asString} from '../../color.js'; import {snap} from '../../geom/flat/simplify.js'; import {defaultFillStyle} from '../canvas.js'; diff --git a/src/ol/render/canvas/Replay.js b/src/ol/render/canvas/Replay.js index 8057993bf0..f3c3a1d6d4 100644 --- a/src/ol/render/canvas/Replay.js +++ b/src/ol/render/canvas/Replay.js @@ -1,7 +1,7 @@ /** * @module ol/render/canvas/Replay */ -import {getUid, inherits} from '../../util.js'; +import {getUid} from '../../util.js'; import {UNDEFINED} from '../../functions.js'; import {equals, reverseSubArray} from '../../array.js'; import {asColorLike} from '../../colorlike.js'; diff --git a/src/ol/render/canvas/ReplayGroup.js b/src/ol/render/canvas/ReplayGroup.js index 53077b3cfc..8c6523ece2 100644 --- a/src/ol/render/canvas/ReplayGroup.js +++ b/src/ol/render/canvas/ReplayGroup.js @@ -1,7 +1,7 @@ /** * @module ol/render/canvas/ReplayGroup */ -import {inherits} from '../../util.js'; + import {numberSafeCompareFunction} from '../../array.js'; import {createCanvasContext2D} from '../../dom.js'; import {buffer, createEmpty, extendCoordinate} from '../../extent.js'; diff --git a/src/ol/render/canvas/TextReplay.js b/src/ol/render/canvas/TextReplay.js index 5c9db8771d..f6f81b6740 100644 --- a/src/ol/render/canvas/TextReplay.js +++ b/src/ol/render/canvas/TextReplay.js @@ -1,7 +1,7 @@ /** * @module ol/render/canvas/TextReplay */ -import {getUid, inherits} from '../../util.js'; +import {getUid} from '../../util.js'; import {asColorLike} from '../../colorlike.js'; import {createCanvasContext2D} from '../../dom.js'; import {intersects} from '../../extent.js'; diff --git a/src/ol/render/webgl/CircleReplay.js b/src/ol/render/webgl/CircleReplay.js index 1a7f02f8c7..f358f3e112 100644 --- a/src/ol/render/webgl/CircleReplay.js +++ b/src/ol/render/webgl/CircleReplay.js @@ -1,7 +1,7 @@ /** * @module ol/render/webgl/CircleReplay */ -import {getUid, inherits} from '../../util.js'; +import {getUid} from '../../util.js'; import {equals} from '../../array.js'; import {asArray} from '../../color.js'; import {intersects} from '../../extent.js'; diff --git a/src/ol/render/webgl/ImageReplay.js b/src/ol/render/webgl/ImageReplay.js index 4570ab2da0..092448f319 100644 --- a/src/ol/render/webgl/ImageReplay.js +++ b/src/ol/render/webgl/ImageReplay.js @@ -1,7 +1,7 @@ /** * @module ol/render/webgl/ImageReplay */ -import {getUid, inherits} from '../../util.js'; +import {getUid} from '../../util.js'; import WebGLTextureReplay from '../webgl/TextureReplay.js'; import WebGLBuffer from '../../webgl/Buffer.js'; diff --git a/src/ol/render/webgl/Immediate.js b/src/ol/render/webgl/Immediate.js index 415e7779bd..adb49bbbb3 100644 --- a/src/ol/render/webgl/Immediate.js +++ b/src/ol/render/webgl/Immediate.js @@ -1,7 +1,7 @@ /** * @module ol/render/webgl/Immediate */ -import {inherits} from '../../util.js'; + import {intersects} from '../../extent.js'; import GeometryType from '../../geom/GeometryType.js'; import ReplayType from '../ReplayType.js'; diff --git a/src/ol/render/webgl/LineStringReplay.js b/src/ol/render/webgl/LineStringReplay.js index bd089f0a4e..678a2ae79e 100644 --- a/src/ol/render/webgl/LineStringReplay.js +++ b/src/ol/render/webgl/LineStringReplay.js @@ -1,7 +1,7 @@ /** * @module ol/render/webgl/LineStringReplay */ -import {getUid, inherits} from '../../util.js'; +import {getUid} from '../../util.js'; import {equals} from '../../array.js'; import {asArray} from '../../color.js'; import {intersects} from '../../extent.js'; diff --git a/src/ol/render/webgl/PolygonReplay.js b/src/ol/render/webgl/PolygonReplay.js index 424b97ee60..8d02dd5af0 100644 --- a/src/ol/render/webgl/PolygonReplay.js +++ b/src/ol/render/webgl/PolygonReplay.js @@ -1,7 +1,7 @@ /** * @module ol/render/webgl/PolygonReplay */ -import {getUid, inherits} from '../../util.js'; +import {getUid} from '../../util.js'; import {equals} from '../../array.js'; import {asArray} from '../../color.js'; import {intersects} from '../../extent.js'; diff --git a/src/ol/render/webgl/Replay.js b/src/ol/render/webgl/Replay.js index 743fcce134..6697a968dc 100644 --- a/src/ol/render/webgl/Replay.js +++ b/src/ol/render/webgl/Replay.js @@ -1,7 +1,7 @@ /** * @module ol/render/webgl/Replay */ -import {inherits} from '../../util.js'; + import {getCenter} from '../../extent.js'; import VectorContext from '../VectorContext.js'; import { diff --git a/src/ol/render/webgl/ReplayGroup.js b/src/ol/render/webgl/ReplayGroup.js index 0a18839434..660fa2975c 100644 --- a/src/ol/render/webgl/ReplayGroup.js +++ b/src/ol/render/webgl/ReplayGroup.js @@ -1,7 +1,7 @@ /** * @module ol/render/webgl/ReplayGroup */ -import {inherits} from '../../util.js'; + import {numberSafeCompareFunction} from '../../array.js'; import {buffer, createOrUpdateFromCoordinate} from '../../extent.js'; import {isEmpty} from '../../obj.js'; diff --git a/src/ol/render/webgl/TextReplay.js b/src/ol/render/webgl/TextReplay.js index 2d9214fab1..b36a276ef4 100644 --- a/src/ol/render/webgl/TextReplay.js +++ b/src/ol/render/webgl/TextReplay.js @@ -1,7 +1,7 @@ /** * @module ol/render/webgl/TextReplay */ -import {getUid, inherits} from '../../util.js'; +import {getUid} from '../../util.js'; import {asColorLike} from '../../colorlike.js'; import {createCanvasContext2D} from '../../dom.js'; import GeometryType from '../../geom/GeometryType.js'; diff --git a/src/ol/render/webgl/TextureReplay.js b/src/ol/render/webgl/TextureReplay.js index f2d95a2eed..b7c58753a7 100644 --- a/src/ol/render/webgl/TextureReplay.js +++ b/src/ol/render/webgl/TextureReplay.js @@ -1,7 +1,7 @@ /** * @module ol/render/webgl/TextureReplay */ -import {getUid, inherits} from '../../util.js'; +import {getUid} from '../../util.js'; import {intersects} from '../../extent.js'; import {isEmpty} from '../../obj.js'; import {fragment, vertex} from '../webgl/texturereplay/defaultshader.js'; diff --git a/src/ol/reproj/Image.js b/src/ol/reproj/Image.js index d084b46064..7c68e098dd 100644 --- a/src/ol/reproj/Image.js +++ b/src/ol/reproj/Image.js @@ -2,7 +2,7 @@ * @module ol/reproj/Image */ import {ERROR_THRESHOLD} from './common.js'; -import {inherits} from '../util.js'; + import ImageBase from '../ImageBase.js'; import ImageState from '../ImageState.js'; import {listen, unlistenByKey} from '../events.js'; diff --git a/src/ol/reproj/Tile.js b/src/ol/reproj/Tile.js index a038169edf..a02d4b7606 100644 --- a/src/ol/reproj/Tile.js +++ b/src/ol/reproj/Tile.js @@ -2,7 +2,7 @@ * @module ol/reproj/Tile */ import {ERROR_THRESHOLD} from './common.js'; -import {inherits} from '../util.js'; + import Tile from '../Tile.js'; import TileState from '../TileState.js'; import {listen, unlistenByKey} from '../events.js'; diff --git a/src/ol/source/BingMaps.js b/src/ol/source/BingMaps.js index de5ef5f186..d8813f815c 100644 --- a/src/ol/source/BingMaps.js +++ b/src/ol/source/BingMaps.js @@ -1,7 +1,7 @@ /** * @module ol/source/BingMaps */ -import {inherits} from '../util.js'; + import {createFromTileUrlFunctions} from '../tileurlfunction.js'; import {applyTransform, intersects} from '../extent.js'; import {jsonp as requestJSONP} from '../net.js'; diff --git a/src/ol/source/CartoDB.js b/src/ol/source/CartoDB.js index 50eb652b8a..301c21d339 100644 --- a/src/ol/source/CartoDB.js +++ b/src/ol/source/CartoDB.js @@ -1,7 +1,7 @@ /** * @module ol/source/CartoDB */ -import {inherits} from '../util.js'; + import {assign} from '../obj.js'; import SourceState from '../source/State.js'; import XYZ from '../source/XYZ.js'; diff --git a/src/ol/source/Cluster.js b/src/ol/source/Cluster.js index 8aad86687e..48eaaf5a9d 100644 --- a/src/ol/source/Cluster.js +++ b/src/ol/source/Cluster.js @@ -2,7 +2,7 @@ * @module ol/source/Cluster */ -import {getUid, inherits} from '../util.js'; +import {getUid} from '../util.js'; import {assert} from '../asserts.js'; import Feature from '../Feature.js'; import {scale as scaleCoordinate, add as addCoordinate} from '../coordinate.js'; diff --git a/src/ol/source/Image.js b/src/ol/source/Image.js index 36e5bcc7e4..1a0a27172f 100644 --- a/src/ol/source/Image.js +++ b/src/ol/source/Image.js @@ -2,7 +2,7 @@ * @module ol/source/Image */ import {ENABLE_RASTER_REPROJECTION} from '../reproj/common.js'; -import {inherits} from '../util.js'; + import ImageState from '../ImageState.js'; import {linearFindNearest} from '../array.js'; import Event from '../events/Event.js'; diff --git a/src/ol/source/ImageArcGISRest.js b/src/ol/source/ImageArcGISRest.js index 3a0653109d..9bd9b4a011 100644 --- a/src/ol/source/ImageArcGISRest.js +++ b/src/ol/source/ImageArcGISRest.js @@ -1,7 +1,7 @@ /** * @module ol/source/ImageArcGISRest */ -import {inherits} from '../util.js'; + import ImageWrapper from '../Image.js'; import {assert} from '../asserts.js'; import {listen} from '../events.js'; diff --git a/src/ol/source/ImageCanvas.js b/src/ol/source/ImageCanvas.js index 94de3a60f1..eee6f6a6dd 100644 --- a/src/ol/source/ImageCanvas.js +++ b/src/ol/source/ImageCanvas.js @@ -1,7 +1,7 @@ /** * @module ol/source/ImageCanvas */ -import {inherits} from '../util.js'; + import ImageCanvas from '../ImageCanvas.js'; import {containsExtent, getHeight, getWidth, scaleFromCenter} from '../extent.js'; import ImageSource from '../source/Image.js'; diff --git a/src/ol/source/ImageMapGuide.js b/src/ol/source/ImageMapGuide.js index 40939982e2..115e764576 100644 --- a/src/ol/source/ImageMapGuide.js +++ b/src/ol/source/ImageMapGuide.js @@ -1,7 +1,7 @@ /** * @module ol/source/ImageMapGuide */ -import {inherits} from '../util.js'; + import ImageWrapper from '../Image.js'; import {listen} from '../events.js'; import EventType from '../events/EventType.js'; diff --git a/src/ol/source/ImageStatic.js b/src/ol/source/ImageStatic.js index 5d33692d2b..11ea42ea44 100644 --- a/src/ol/source/ImageStatic.js +++ b/src/ol/source/ImageStatic.js @@ -1,7 +1,7 @@ /** * @module ol/source/ImageStatic */ -import {inherits} from '../util.js'; + import ImageWrapper from '../Image.js'; import ImageState from '../ImageState.js'; import {createCanvasContext2D} from '../dom.js'; diff --git a/src/ol/source/ImageWMS.js b/src/ol/source/ImageWMS.js index 3594392b28..fd8f80cc5e 100644 --- a/src/ol/source/ImageWMS.js +++ b/src/ol/source/ImageWMS.js @@ -3,7 +3,7 @@ */ import {DEFAULT_WMS_VERSION} from './common.js'; -import {inherits} from '../util.js'; + import ImageWrapper from '../Image.js'; import {assert} from '../asserts.js'; import {listen} from '../events.js'; diff --git a/src/ol/source/OSM.js b/src/ol/source/OSM.js index 85183bf441..d551190299 100644 --- a/src/ol/source/OSM.js +++ b/src/ol/source/OSM.js @@ -1,7 +1,7 @@ /** * @module ol/source/OSM */ -import {inherits} from '../util.js'; + import XYZ from '../source/XYZ.js'; diff --git a/src/ol/source/Raster.js b/src/ol/source/Raster.js index 852cf90763..9526dd7d61 100644 --- a/src/ol/source/Raster.js +++ b/src/ol/source/Raster.js @@ -1,7 +1,7 @@ /** * @module ol/source/Raster */ -import {getUid, inherits} from '../util.js'; +import {getUid} from '../util.js'; import ImageCanvas from '../ImageCanvas.js'; import TileQueue from '../TileQueue.js'; import {createCanvasContext2D} from '../dom.js'; diff --git a/src/ol/source/Source.js b/src/ol/source/Source.js index 84930b9afb..468d7c71b8 100644 --- a/src/ol/source/Source.js +++ b/src/ol/source/Source.js @@ -1,7 +1,7 @@ /** * @module ol/source/Source */ -import {inherits} from '../util.js'; + import {UNDEFINED} from '../functions.js'; import BaseObject from '../Object.js'; import {get as getProjection} from '../proj.js'; diff --git a/src/ol/source/Stamen.js b/src/ol/source/Stamen.js index c75353f459..6a23d12c27 100644 --- a/src/ol/source/Stamen.js +++ b/src/ol/source/Stamen.js @@ -1,7 +1,7 @@ /** * @module ol/source/Stamen */ -import {inherits} from '../util.js'; + import {ATTRIBUTION as OSM_ATTRIBUTION} from '../source/OSM.js'; import XYZ from '../source/XYZ.js'; diff --git a/src/ol/source/Tile.js b/src/ol/source/Tile.js index fd8623d7c9..427f3e547b 100644 --- a/src/ol/source/Tile.js +++ b/src/ol/source/Tile.js @@ -1,7 +1,7 @@ /** * @module ol/source/Tile */ -import {inherits} from '../util.js'; + import {UNDEFINED} from '../functions.js'; import TileCache from '../TileCache.js'; import TileState from '../TileState.js'; diff --git a/src/ol/source/TileArcGISRest.js b/src/ol/source/TileArcGISRest.js index a276b98326..d4e83a4a64 100644 --- a/src/ol/source/TileArcGISRest.js +++ b/src/ol/source/TileArcGISRest.js @@ -1,7 +1,7 @@ /** * @module ol/source/TileArcGISRest */ -import {inherits} from '../util.js'; + import {createEmpty} from '../extent.js'; import {modulo} from '../math.js'; import {assign} from '../obj.js'; diff --git a/src/ol/source/TileDebug.js b/src/ol/source/TileDebug.js index 07632b58a9..44033c6c69 100644 --- a/src/ol/source/TileDebug.js +++ b/src/ol/source/TileDebug.js @@ -1,7 +1,7 @@ /** * @module ol/source/TileDebug */ -import {inherits} from '../util.js'; + import Tile from '../Tile.js'; import TileState from '../TileState.js'; import {createCanvasContext2D} from '../dom.js'; diff --git a/src/ol/source/TileImage.js b/src/ol/source/TileImage.js index 7e79507743..d822e37fe6 100644 --- a/src/ol/source/TileImage.js +++ b/src/ol/source/TileImage.js @@ -2,7 +2,7 @@ * @module ol/source/TileImage */ import {ENABLE_RASTER_REPROJECTION} from '../reproj/common.js'; -import {getUid, inherits} from '../util.js'; +import {getUid} from '../util.js'; import ImageTile from '../ImageTile.js'; import TileCache from '../TileCache.js'; import TileState from '../TileState.js'; diff --git a/src/ol/source/TileJSON.js b/src/ol/source/TileJSON.js index 354ecaadcd..5bb800fbd9 100644 --- a/src/ol/source/TileJSON.js +++ b/src/ol/source/TileJSON.js @@ -7,7 +7,7 @@ * @see http://mapbox.com/developers/api/ */ -import {inherits} from '../util.js'; + import {createFromTemplates} from '../tileurlfunction.js'; import {assert} from '../asserts.js'; import {applyTransform, intersects} from '../extent.js'; diff --git a/src/ol/source/TileWMS.js b/src/ol/source/TileWMS.js index 849f4e1da1..890b1a157b 100644 --- a/src/ol/source/TileWMS.js +++ b/src/ol/source/TileWMS.js @@ -3,7 +3,7 @@ */ import {DEFAULT_WMS_VERSION} from './common.js'; -import {inherits} from '../util.js'; + import {assert} from '../asserts.js'; import {buffer, createEmpty} from '../extent.js'; import {assign} from '../obj.js'; diff --git a/src/ol/source/UTFGrid.js b/src/ol/source/UTFGrid.js index f763ec3e19..37619a55b8 100644 --- a/src/ol/source/UTFGrid.js +++ b/src/ol/source/UTFGrid.js @@ -1,7 +1,7 @@ /** * @module ol/source/UTFGrid */ -import {inherits} from '../util.js'; + import Tile from '../Tile.js'; import TileState from '../TileState.js'; import {createFromTemplates, nullTileUrlFunction} from '../tileurlfunction.js'; diff --git a/src/ol/source/UrlTile.js b/src/ol/source/UrlTile.js index b19f558f77..073698bbd4 100644 --- a/src/ol/source/UrlTile.js +++ b/src/ol/source/UrlTile.js @@ -1,7 +1,7 @@ /** * @module ol/source/UrlTile */ -import {getUid, inherits} from '../util.js'; +import {getUid} from '../util.js'; import TileState from '../TileState.js'; import {expandUrl, createFromTemplates, nullTileUrlFunction} from '../tileurlfunction.js'; import TileSource, {TileSourceEvent} from '../source/Tile.js'; diff --git a/src/ol/source/Vector.js b/src/ol/source/Vector.js index 3ef13cb11d..90bf0fdd1d 100644 --- a/src/ol/source/Vector.js +++ b/src/ol/source/Vector.js @@ -2,7 +2,7 @@ * @module ol/source/Vector */ -import {getUid, inherits} from '../util.js'; +import {getUid} from '../util.js'; import Collection from '../Collection.js'; import CollectionEventType from '../CollectionEventType.js'; import ObjectEventType from '../ObjectEventType.js'; diff --git a/src/ol/source/VectorTile.js b/src/ol/source/VectorTile.js index e1c1158596..31b4ef8a1b 100644 --- a/src/ol/source/VectorTile.js +++ b/src/ol/source/VectorTile.js @@ -1,7 +1,7 @@ /** * @module ol/source/VectorTile */ -import {inherits} from '../util.js'; + import TileState from '../TileState.js'; import VectorImageTile, {defaultLoadFunction} from '../VectorImageTile.js'; import Tile from '../VectorTile.js'; diff --git a/src/ol/source/WMTS.js b/src/ol/source/WMTS.js index f309b09d53..d418f96386 100644 --- a/src/ol/source/WMTS.js +++ b/src/ol/source/WMTS.js @@ -1,7 +1,7 @@ /** * @module ol/source/WMTS */ -import {inherits} from '../util.js'; + import {expandUrl, createFromTileUrlFunctions, nullTileUrlFunction} from '../tileurlfunction.js'; import {find, findIndex, includes} from '../array.js'; import {containsExtent} from '../extent.js'; diff --git a/src/ol/source/XYZ.js b/src/ol/source/XYZ.js index b5a1590485..fbc9cc756d 100644 --- a/src/ol/source/XYZ.js +++ b/src/ol/source/XYZ.js @@ -1,7 +1,7 @@ /** * @module ol/source/XYZ */ -import {inherits} from '../util.js'; + import TileImage from '../source/TileImage.js'; import {createXYZ, extentFromProjection} from '../tilegrid.js'; diff --git a/src/ol/source/Zoomify.js b/src/ol/source/Zoomify.js index b8a0818d51..f1e7ed9878 100644 --- a/src/ol/source/Zoomify.js +++ b/src/ol/source/Zoomify.js @@ -2,7 +2,7 @@ * @module ol/source/Zoomify */ import {DEFAULT_TILE_SIZE} from '../tilegrid/common.js'; -import {inherits} from '../util.js'; + import ImageTile from '../ImageTile.js'; import TileState from '../TileState.js'; import {expandUrl, createFromTileUrlFunctions} from '../tileurlfunction.js'; diff --git a/src/ol/structs/LRUCache.js b/src/ol/structs/LRUCache.js index e3a90ec94e..2c4dff5798 100644 --- a/src/ol/structs/LRUCache.js +++ b/src/ol/structs/LRUCache.js @@ -1,7 +1,7 @@ /** * @module ol/structs/LRUCache */ -import {inherits} from '../util.js'; + import {assert} from '../asserts.js'; import EventTarget from '../events/EventTarget.js'; import EventType from '../events/EventType.js'; diff --git a/src/ol/style/Circle.js b/src/ol/style/Circle.js index a79fbc7c9c..9e6d91c9ef 100644 --- a/src/ol/style/Circle.js +++ b/src/ol/style/Circle.js @@ -1,7 +1,7 @@ /** * @module ol/style/Circle */ -import {inherits} from '../util.js'; + import RegularShape from '../style/RegularShape.js'; diff --git a/src/ol/style/Icon.js b/src/ol/style/Icon.js index 715125fa27..8eb05223d2 100644 --- a/src/ol/style/Icon.js +++ b/src/ol/style/Icon.js @@ -1,7 +1,7 @@ /** * @module ol/style/Icon */ -import {getUid, inherits} from '../util.js'; +import {getUid} from '../util.js'; import ImageState from '../ImageState.js'; import {assert} from '../asserts.js'; import {asArray} from '../color.js'; diff --git a/src/ol/style/IconImage.js b/src/ol/style/IconImage.js index 194eb443c1..34c148f199 100644 --- a/src/ol/style/IconImage.js +++ b/src/ol/style/IconImage.js @@ -1,7 +1,7 @@ /** * @module ol/style/IconImage */ -import {inherits} from '../util.js'; + import {createCanvasContext2D} from '../dom.js'; import {listenOnce, unlistenByKey} from '../events.js'; import EventTarget from '../events/EventTarget.js'; diff --git a/src/ol/style/RegularShape.js b/src/ol/style/RegularShape.js index c3efe8bca2..ca70d7508b 100644 --- a/src/ol/style/RegularShape.js +++ b/src/ol/style/RegularShape.js @@ -1,7 +1,7 @@ /** * @module ol/style/RegularShape */ -import {inherits} from '../util.js'; + import {asColorLike} from '../colorlike.js'; import {createCanvasContext2D} from '../dom.js'; import {CANVAS_LINE_DASH} from '../has.js'; diff --git a/src/ol/tilegrid/WMTS.js b/src/ol/tilegrid/WMTS.js index 972b357017..f9245390db 100644 --- a/src/ol/tilegrid/WMTS.js +++ b/src/ol/tilegrid/WMTS.js @@ -1,7 +1,7 @@ /** * @module ol/tilegrid/WMTS */ -import {inherits} from '../util.js'; + import {find} from '../array.js'; import {get as getProjection} from '../proj.js'; import TileGrid from '../tilegrid/TileGrid.js'; diff --git a/src/ol/webgl/Context.js b/src/ol/webgl/Context.js index bb90bf3ee9..2c313263a3 100644 --- a/src/ol/webgl/Context.js +++ b/src/ol/webgl/Context.js @@ -1,7 +1,7 @@ /** * @module ol/webgl/Context */ -import {getUid, inherits} from '../util.js'; +import {getUid} from '../util.js'; import {EXTENSIONS as WEBGL_EXTENSIONS} from '../webgl.js'; import Disposable from '../Disposable.js'; import {includes} from '../array.js'; diff --git a/src/ol/webgl/Fragment.js b/src/ol/webgl/Fragment.js index 3b27053d40..e96a853502 100644 --- a/src/ol/webgl/Fragment.js +++ b/src/ol/webgl/Fragment.js @@ -1,7 +1,7 @@ /** * @module ol/webgl/Fragment */ -import {inherits} from '../util.js'; + import {FRAGMENT_SHADER} from '../webgl.js'; import WebGLShader from '../webgl/Shader.js'; diff --git a/src/ol/webgl/Vertex.js b/src/ol/webgl/Vertex.js index 494b972387..746d871319 100644 --- a/src/ol/webgl/Vertex.js +++ b/src/ol/webgl/Vertex.js @@ -1,7 +1,7 @@ /** * @module ol/webgl/Vertex */ -import {inherits} from '../util.js'; + import {VERTEX_SHADER} from '../webgl.js'; import WebGLShader from '../webgl/Shader.js'; From 9ce36da34946c07d6c203aaa60b11f5d5f0fd712 Mon Sep 17 00:00:00 2001 From: Tim Schaub Date: Wed, 18 Jul 2018 00:36:29 -0600 Subject: [PATCH 082/107] Remove @extends and @constructor annotations --- src/ol/AssertionError.js | 1 - src/ol/Disposable.js | 1 - src/ol/events/Event.js | 6 ++-- src/ol/events/EventTarget.js | 2 -- src/ol/format/GML.js | 2 -- src/ol/layer/Base.js | 3 -- src/ol/layer/Group.js | 2 -- src/ol/layer/Heatmap.js | 2 -- src/ol/layer/Image.js | 2 -- src/ol/layer/Layer.js | 3 -- src/ol/layer/Tile.js | 3 -- src/ol/layer/Vector.js | 3 -- src/ol/layer/VectorTile.js | 2 -- src/ol/render/Box.js | 2 -- src/ol/render/Event.js | 2 -- src/ol/render/Feature.js | 1 - src/ol/render/ReplayGroup.js | 18 ++++++------ src/ol/render/VectorContext.js | 3 +- src/ol/render/canvas/ImageReplay.js | 2 -- src/ol/render/canvas/Immediate.js | 2 -- src/ol/render/canvas/LineStringReplay.js | 2 -- src/ol/render/canvas/PolygonReplay.js | 2 -- src/ol/render/canvas/Replay.js | 2 -- src/ol/render/canvas/ReplayGroup.js | 2 -- src/ol/render/canvas/TextReplay.js | 2 -- src/ol/render/webgl/CircleReplay.js | 2 -- src/ol/render/webgl/ImageReplay.js | 2 -- src/ol/render/webgl/Immediate.js | 2 -- src/ol/render/webgl/LineStringReplay.js | 2 -- src/ol/render/webgl/PolygonReplay.js | 2 -- src/ol/render/webgl/Replay.js | 3 -- src/ol/render/webgl/ReplayGroup.js | 2 -- src/ol/render/webgl/TextReplay.js | 2 -- src/ol/render/webgl/TextureReplay.js | 3 -- src/ol/renderer/canvas/ImageLayer.js | 2 -- src/ol/renderer/canvas/VectorTileLayer.js | 2 -- src/ol/reproj/Image.js | 2 -- src/ol/reproj/Tile.js | 2 -- src/ol/reproj/Triangulation.js | 15 +++++----- src/ol/source/BingMaps.js | 2 -- src/ol/source/CartoDB.js | 2 -- src/ol/source/Cluster.js | 2 -- src/ol/source/Image.js | 5 ---- src/ol/source/ImageArcGISRest.js | 2 -- src/ol/source/ImageCanvas.js | 2 -- src/ol/source/ImageMapGuide.js | 2 -- src/ol/source/ImageStatic.js | 2 -- src/ol/source/ImageWMS.js | 2 -- src/ol/source/OSM.js | 2 -- src/ol/source/Raster.js | 4 --- src/ol/source/Source.js | 3 -- src/ol/source/Stamen.js | 2 -- src/ol/source/Tile.js | 5 ---- src/ol/source/TileArcGISRest.js | 2 -- src/ol/source/TileDebug.js | 4 --- src/ol/source/TileImage.js | 2 -- src/ol/source/TileJSON.js | 2 -- src/ol/source/TileWMS.js | 2 -- src/ol/source/UTFGrid.js | 4 --- src/ol/source/UrlTile.js | 3 -- src/ol/source/Vector.js | 4 --- src/ol/source/VectorTile.js | 2 -- src/ol/source/WMTS.js | 2 -- src/ol/source/XYZ.js | 2 -- src/ol/source/Zoomify.js | 4 --- src/ol/structs/LRUCache.js | 3 +- src/ol/structs/LinkedList.js | 14 ++++------ src/ol/structs/PriorityQueue.js | 30 ++++++++++---------- src/ol/structs/RBush.js | 17 ++++++------ src/ol/style/Atlas.js | 34 +++++++++++------------ src/ol/style/AtlasManager.js | 33 +++++++++++----------- src/ol/style/Circle.js | 2 -- src/ol/style/Fill.js | 8 +++--- src/ol/style/Icon.js | 2 -- src/ol/style/IconImage.js | 2 -- src/ol/style/IconImageCache.js | 1 - src/ol/style/Image.js | 9 +++--- src/ol/style/RegularShape.js | 2 -- src/ol/style/Stroke.js | 9 +++--- src/ol/style/Style.js | 10 +++---- src/ol/style/Text.js | 9 +++--- src/ol/tilegrid/TileGrid.js | 10 +++---- src/ol/tilegrid/WMTS.js | 2 -- src/ol/webgl/Context.js | 2 -- src/ol/webgl/Fragment.js | 3 -- src/ol/webgl/Vertex.js | 3 -- test/spec/ol/source/tile.test.js | 2 -- 87 files changed, 110 insertions(+), 282 deletions(-) diff --git a/src/ol/AssertionError.js b/src/ol/AssertionError.js index 293c6996ae..e39cc915a6 100644 --- a/src/ol/AssertionError.js +++ b/src/ol/AssertionError.js @@ -7,7 +7,6 @@ import {VERSION} from './util.js'; * Error object thrown when an assertion failed. This is an ECMA-262 Error, * extended with a `code` property. * @see {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error} - * @extends {Error} */ class AssertionError extends Error { diff --git a/src/ol/Disposable.js b/src/ol/Disposable.js index a7091d8e8f..3cdf80c89c 100644 --- a/src/ol/Disposable.js +++ b/src/ol/Disposable.js @@ -5,7 +5,6 @@ import {UNDEFINED} from './functions.js'; /** * Objects that need to clean up after themselves. - * @constructor */ class Disposable { /** diff --git a/src/ol/events/Event.js b/src/ol/events/Event.js index 3916a987b6..e4b9e49cc3 100644 --- a/src/ol/events/Event.js +++ b/src/ol/events/Event.js @@ -11,12 +11,12 @@ * `stopPropagation` and `preventDefault` methods. It is meant as base class * for higher level events defined in the library, and works with * {@link module:ol/events/EventTarget~EventTarget}. - * - * @constructor - * @param {string} type Type. */ class Event { + /** + * @param {string} type Type. + */ constructor(type) { /** diff --git a/src/ol/events/EventTarget.js b/src/ol/events/EventTarget.js index 71e5d3e42b..f319b05fc9 100644 --- a/src/ol/events/EventTarget.js +++ b/src/ol/events/EventTarget.js @@ -26,8 +26,6 @@ import Event from '../events/Event.js'; * `stopPropagation` or `preventDefault` on an event object, it means that no * more listeners after this one will be called. Same as when the listener * returns false. - * - * @constructor */ class EventTarget extends Disposable { constructor() { diff --git a/src/ol/format/GML.js b/src/ol/format/GML.js index eeb59fd36c..f7ef19052b 100644 --- a/src/ol/format/GML.js +++ b/src/ol/format/GML.js @@ -9,10 +9,8 @@ import GML3 from '../format/GML3.js'; * version 3.1.1. * Currently only supports GML 3.1.1 Simple Features profile. * - * @constructor * @param {module:ol/format/GMLBase~Options=} opt_options * Optional configuration object. - * @extends {module:ol/format/GMLBase} * @api */ const GML = GML3; diff --git a/src/ol/layer/Base.js b/src/ol/layer/Base.js index 8fd2740bad..e75b3eec1a 100644 --- a/src/ol/layer/Base.js +++ b/src/ol/layer/Base.js @@ -31,9 +31,6 @@ class BaseLayer extends BaseObject { * the options is set as a {@link module:ol/Object} property on the layer object, so * is observable, and has get/set accessors. * - * @constructor - * @abstract - * @extends {module:ol/Object} * @param {module:ol/layer/Base~Options} options Layer options. * @api */ diff --git a/src/ol/layer/Group.js b/src/ol/layer/Group.js index 8c6ce1fae3..4d6332a5d0 100644 --- a/src/ol/layer/Group.js +++ b/src/ol/layer/Group.js @@ -47,8 +47,6 @@ class LayerGroup extends BaseLayer { * * A generic `change` event is triggered when the group/Collection changes. * - * @constructor - * @extends {module:ol/layer/Base} * @param {module:ol/layer/Group~Options=} opt_options Layer options. * @api */ diff --git a/src/ol/layer/Heatmap.js b/src/ol/layer/Heatmap.js index 17ee8a1914..d7c28495a1 100644 --- a/src/ol/layer/Heatmap.js +++ b/src/ol/layer/Heatmap.js @@ -67,8 +67,6 @@ class Heatmap extends VectorLayer { * property on the layer object; for example, setting `title: 'My Title'` in the * options means that `title` is observable, and has get/set accessors. * - * @constructor - * @extends {module:ol/layer/Vector} * @fires module:ol/render/Event~RenderEvent * @param {module:ol/layer/Heatmap~Options=} opt_options Options. * @api diff --git a/src/ol/layer/Image.js b/src/ol/layer/Image.js index e3aac7a201..417864db8e 100644 --- a/src/ol/layer/Image.js +++ b/src/ol/layer/Image.js @@ -35,8 +35,6 @@ class ImageLayer extends Layer { * property on the layer object; for example, setting `title: 'My Title'` in the * options means that `title` is observable, and has get/set accessors. * - * @constructor - * @extends {module:ol/layer/Layer} * @fires module:ol/render/Event~RenderEvent * @param {module:ol/layer/Image~Options=} opt_options Layer options. * @api diff --git a/src/ol/layer/Layer.js b/src/ol/layer/Layer.js index f93399eacc..0711d28b6f 100644 --- a/src/ol/layer/Layer.js +++ b/src/ol/layer/Layer.js @@ -60,9 +60,6 @@ class Layer extends BaseLayer { * * A generic `change` event is fired when the state of the source changes. * - * @constructor - * @abstract - * @extends {module:ol/layer/Base} * @fires module:ol/render/Event~RenderEvent * @param {module:ol/layer/Layer~Options} options Layer options. * @api diff --git a/src/ol/layer/Tile.js b/src/ol/layer/Tile.js index ee5b078275..ca1cef189e 100644 --- a/src/ol/layer/Tile.js +++ b/src/ol/layer/Tile.js @@ -39,9 +39,6 @@ class TileLayer extends Layer { * property on the layer object; for example, setting `title: 'My Title'` in the * options means that `title` is observable, and has get/set accessors. * - * @constructor - * @extends {module:ol/layer/Layer} - * @fires module:ol/render/Event~RenderEvent * @param {module:ol/layer/Tile~Options=} opt_options Tile layer options. * @api */ diff --git a/src/ol/layer/Vector.js b/src/ol/layer/Vector.js index e1ebfcc451..55933e088f 100644 --- a/src/ol/layer/Vector.js +++ b/src/ol/layer/Vector.js @@ -85,9 +85,6 @@ class VectorLayer extends Layer { * property on the layer object; for example, setting `title: 'My Title'` in the * options means that `title` is observable, and has get/set accessors. * - * @constructor - * @extends {module:ol/layer/Layer} - * @fires module:ol/render/Event~RenderEvent * @param {module:ol/layer/Vector~Options=} opt_options Options. * @api */ diff --git a/src/ol/layer/VectorTile.js b/src/ol/layer/VectorTile.js index 33b29d5f2b..a9f5c7d199 100644 --- a/src/ol/layer/VectorTile.js +++ b/src/ol/layer/VectorTile.js @@ -94,8 +94,6 @@ class VectorTileLayer extends VectorLayer { * property on the layer object; for example, setting `title: 'My Title'` in the * options means that `title` is observable, and has get/set accessors. * - * @constructor - * @extends {module:ol/layer/Vector} * @param {module:ol/layer/VectorTile~Options=} opt_options Options. * @api */ diff --git a/src/ol/render/Box.js b/src/ol/render/Box.js index 2831998822..15b84bb6cc 100644 --- a/src/ol/render/Box.js +++ b/src/ol/render/Box.js @@ -7,8 +7,6 @@ import Polygon from '../geom/Polygon.js'; class RenderBox extends Disposable { /** - * @constructor - * @extends {module:ol/Disposable} * @param {string} className CSS class name. */ constructor(className) { diff --git a/src/ol/render/Event.js b/src/ol/render/Event.js index 565e1c10e7..e4c677355b 100644 --- a/src/ol/render/Event.js +++ b/src/ol/render/Event.js @@ -7,8 +7,6 @@ import Event from '../events/Event.js'; class RenderEvent extends Event { /** - * @constructor - * @extends {module:ol/events/Event} * @param {module:ol/render/EventType} type Type. * @param {module:ol/render/VectorContext=} opt_vectorContext Vector context. * @param {module:ol/PluggableMap~FrameState=} opt_frameState Frame state. diff --git a/src/ol/render/Feature.js b/src/ol/render/Feature.js index 5c3ccf2f03..447d684658 100644 --- a/src/ol/render/Feature.js +++ b/src/ol/render/Feature.js @@ -24,7 +24,6 @@ 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. * - * @constructor * @param {module:ol/geom/GeometryType} type Geometry type. * @param {Array.} flatCoordinates Flat coordinates. These always need * to be right-handed for polygons. diff --git a/src/ol/render/ReplayGroup.js b/src/ol/render/ReplayGroup.js index 3457654326..f28edc9b86 100644 --- a/src/ol/render/ReplayGroup.js +++ b/src/ol/render/ReplayGroup.js @@ -3,22 +3,20 @@ */ /** * Base class for replay groups. - * @constructor - * @abstract */ class ReplayGroup { /** - * @abstract - * @param {number|undefined} zIndex Z index. - * @param {module:ol/render/ReplayType} replayType Replay type. - * @return {module:ol/render/VectorContext} Replay. - */ + * @abstract + * @param {number|undefined} zIndex Z index. + * @param {module:ol/render/ReplayType} replayType Replay type. + * @return {module:ol/render/VectorContext} Replay. + */ getReplay(zIndex, replayType) {} /** - * @abstract - * @return {boolean} Is empty. - */ + * @abstract + * @return {boolean} Is empty. + */ isEmpty() {} } diff --git a/src/ol/render/VectorContext.js b/src/ol/render/VectorContext.js index 1d376d3a12..1ed6f81497 100644 --- a/src/ol/render/VectorContext.js +++ b/src/ol/render/VectorContext.js @@ -1,11 +1,10 @@ /** * @module ol/render/VectorContext */ + /** * Context for drawing geometries. A vector context is available on render * events and does not need to be constructed directly. - * @constructor - * @abstract * @struct * @api */ diff --git a/src/ol/render/canvas/ImageReplay.js b/src/ol/render/canvas/ImageReplay.js index 9d48125380..7b6a704423 100644 --- a/src/ol/render/canvas/ImageReplay.js +++ b/src/ol/render/canvas/ImageReplay.js @@ -6,8 +6,6 @@ import CanvasReplay from '../canvas/Replay.js'; class CanvasImageReplay extends CanvasReplay { /** - * @constructor - * @extends {module:ol/render/canvas/Replay} * @param {number} tolerance Tolerance. * @param {module:ol/extent~Extent} maxExtent Maximum extent. * @param {number} resolution Resolution. diff --git a/src/ol/render/canvas/Immediate.js b/src/ol/render/canvas/Immediate.js index 4fc556293f..4d63df1bb3 100644 --- a/src/ol/render/canvas/Immediate.js +++ b/src/ol/render/canvas/Immediate.js @@ -26,8 +26,6 @@ class CanvasImmediateRenderer extends VectorContext { * {@link module:ol/render/Event~RenderEvent} object associated with postcompose, precompose and * render events emitted by layers and maps. * - * @constructor - * @extends {module:ol/render/VectorContext} * @param {CanvasRenderingContext2D} context Context. * @param {number} pixelRatio Pixel ratio. * @param {module:ol/extent~Extent} extent Extent. diff --git a/src/ol/render/canvas/LineStringReplay.js b/src/ol/render/canvas/LineStringReplay.js index d63b2ea101..9f0e970ad8 100644 --- a/src/ol/render/canvas/LineStringReplay.js +++ b/src/ol/render/canvas/LineStringReplay.js @@ -6,8 +6,6 @@ import CanvasReplay from '../canvas/Replay.js'; class CanvasLineStringReplay extends CanvasReplay { /** - * @constructor - * @extends {module:ol/render/canvas/Replay} * @param {number} tolerance Tolerance. * @param {module:ol/extent~Extent} maxExtent Maximum extent. * @param {number} resolution Resolution. diff --git a/src/ol/render/canvas/PolygonReplay.js b/src/ol/render/canvas/PolygonReplay.js index 1b7f99d470..64144c9519 100644 --- a/src/ol/render/canvas/PolygonReplay.js +++ b/src/ol/render/canvas/PolygonReplay.js @@ -12,8 +12,6 @@ import CanvasReplay from '../canvas/Replay.js'; class CanvasPolygonReplay extends CanvasReplay { /** - * @constructor - * @extends {module:ol/render/canvas/Replay} * @param {number} tolerance Tolerance. * @param {module:ol/extent~Extent} maxExtent Maximum extent. * @param {number} resolution Resolution. diff --git a/src/ol/render/canvas/Replay.js b/src/ol/render/canvas/Replay.js index f3c3a1d6d4..20a2ec2971 100644 --- a/src/ol/render/canvas/Replay.js +++ b/src/ol/render/canvas/Replay.js @@ -43,8 +43,6 @@ const tmpTransform = createTransform(); class CanvasReplay extends VectorContext { /** - * @constructor - * @extends {module:ol/render/VectorContext} * @param {number} tolerance Tolerance. * @param {module:ol/extent~Extent} maxExtent Maximum extent. * @param {number} resolution Resolution. diff --git a/src/ol/render/canvas/ReplayGroup.js b/src/ol/render/canvas/ReplayGroup.js index 8c6523ece2..3ebaaffa9f 100644 --- a/src/ol/render/canvas/ReplayGroup.js +++ b/src/ol/render/canvas/ReplayGroup.js @@ -35,8 +35,6 @@ const BATCH_CONSTRUCTORS = { class CanvasReplayGroup extends ReplayGroup { /** - * @constructor - * @extends {module:ol/render/ReplayGroup} * @param {number} tolerance Tolerance. * @param {module:ol/extent~Extent} maxExtent Max extent. * @param {number} resolution Resolution. diff --git a/src/ol/render/canvas/TextReplay.js b/src/ol/render/canvas/TextReplay.js index f6f81b6740..87cbaf8922 100644 --- a/src/ol/render/canvas/TextReplay.js +++ b/src/ol/render/canvas/TextReplay.js @@ -16,8 +16,6 @@ import TextPlacement from '../../style/TextPlacement.js'; class CanvasTextReplay extends CanvasReplay { /** - * @constructor - * @extends {module:ol/render/canvas/Replay} * @param {number} tolerance Tolerance. * @param {module:ol/extent~Extent} maxExtent Maximum extent. * @param {number} resolution Resolution. diff --git a/src/ol/render/webgl/CircleReplay.js b/src/ol/render/webgl/CircleReplay.js index f358f3e112..352af6b2c6 100644 --- a/src/ol/render/webgl/CircleReplay.js +++ b/src/ol/render/webgl/CircleReplay.js @@ -17,8 +17,6 @@ import WebGLBuffer from '../../webgl/Buffer.js'; class WebGLCircleReplay extends WebGLReplay { /** - * @constructor - * @extends {module:ol/render/webgl/Replay} * @param {number} tolerance Tolerance. * @param {module:ol/extent~Extent} maxExtent Max extent. * @struct diff --git a/src/ol/render/webgl/ImageReplay.js b/src/ol/render/webgl/ImageReplay.js index 092448f319..9ce185dcbe 100644 --- a/src/ol/render/webgl/ImageReplay.js +++ b/src/ol/render/webgl/ImageReplay.js @@ -7,8 +7,6 @@ import WebGLBuffer from '../../webgl/Buffer.js'; class WebGLImageReplay extends WebGLTextureReplay { /** - * @constructor - * @extends {module:ol/render/webgl/TextureReplay} * @param {number} tolerance Tolerance. * @param {module:ol/extent~Extent} maxExtent Max extent. * @struct diff --git a/src/ol/render/webgl/Immediate.js b/src/ol/render/webgl/Immediate.js index adb49bbbb3..6e5c258b4f 100644 --- a/src/ol/render/webgl/Immediate.js +++ b/src/ol/render/webgl/Immediate.js @@ -10,8 +10,6 @@ import WebGLReplayGroup from '../webgl/ReplayGroup.js'; class WebGLImmediateRenderer extends VectorContext { /** - * @constructor - * @extends {module:ol/render/VectorContext} * @param {module:ol/webgl/Context} context Context. * @param {module:ol/coordinate~Coordinate} center Center. * @param {number} resolution Resolution. diff --git a/src/ol/render/webgl/LineStringReplay.js b/src/ol/render/webgl/LineStringReplay.js index 678a2ae79e..adce9c1c20 100644 --- a/src/ol/render/webgl/LineStringReplay.js +++ b/src/ol/render/webgl/LineStringReplay.js @@ -37,8 +37,6 @@ const Instruction = { class WebGLLineStringReplay extends WebGLReplay { /** - * @constructor - * @extends {module:ol/render/webgl/Replay} * @param {number} tolerance Tolerance. * @param {module:ol/extent~Extent} maxExtent Max extent. * @struct diff --git a/src/ol/render/webgl/PolygonReplay.js b/src/ol/render/webgl/PolygonReplay.js index 8d02dd5af0..6de0fc4dfd 100644 --- a/src/ol/render/webgl/PolygonReplay.js +++ b/src/ol/render/webgl/PolygonReplay.js @@ -38,8 +38,6 @@ import WebGLBuffer from '../../webgl/Buffer.js'; class WebGLPolygonReplay extends WebGLReplay { /** - * @constructor - * @extends {module:ol/render/webgl/Replay} * @param {number} tolerance Tolerance. * @param {module:ol/extent~Extent} maxExtent Max extent. * @struct diff --git a/src/ol/render/webgl/Replay.js b/src/ol/render/webgl/Replay.js index 6697a968dc..3bd4af2ba6 100644 --- a/src/ol/render/webgl/Replay.js +++ b/src/ol/render/webgl/Replay.js @@ -17,9 +17,6 @@ import {ARRAY_BUFFER, ELEMENT_ARRAY_BUFFER, TRIANGLES, class WebGLReplay extends VectorContext { /** - * @constructor - * @abstract - * @extends {module:ol/render/VectorContext} * @param {number} tolerance Tolerance. * @param {module:ol/extent~Extent} maxExtent Max extent. * @struct diff --git a/src/ol/render/webgl/ReplayGroup.js b/src/ol/render/webgl/ReplayGroup.js index 660fa2975c..62b15c5932 100644 --- a/src/ol/render/webgl/ReplayGroup.js +++ b/src/ol/render/webgl/ReplayGroup.js @@ -34,8 +34,6 @@ const BATCH_CONSTRUCTORS = { class WebGLReplayGroup extends ReplayGroup { /** - * @constructor - * @extends {module:ol/render/ReplayGroup} * @param {number} tolerance Tolerance. * @param {module:ol/extent~Extent} maxExtent Max extent. * @param {number=} opt_renderBuffer Render buffer. diff --git a/src/ol/render/webgl/TextReplay.js b/src/ol/render/webgl/TextReplay.js index b36a276ef4..7ecc5c2357 100644 --- a/src/ol/render/webgl/TextReplay.js +++ b/src/ol/render/webgl/TextReplay.js @@ -24,8 +24,6 @@ import WebGLBuffer from '../../webgl/Buffer.js'; class WebGLTextReplay extends WebGLTextureReplay { /** - * @constructor - * @extends {module:ol/render/webgl/TextureReplay} * @param {number} tolerance Tolerance. * @param {module:ol/extent~Extent} maxExtent Max extent. * @struct diff --git a/src/ol/render/webgl/TextureReplay.js b/src/ol/render/webgl/TextureReplay.js index b7c58753a7..ab1b3c22b8 100644 --- a/src/ol/render/webgl/TextureReplay.js +++ b/src/ol/render/webgl/TextureReplay.js @@ -12,9 +12,6 @@ import {createTexture} from '../../webgl/Context.js'; class WebGLTextureReplay extends WebGLReplay { /** - * @constructor - * @abstract - * @extends {module:ol/render/webgl/Replay} * @param {number} tolerance Tolerance. * @param {module:ol/extent~Extent} maxExtent Max extent. * @struct diff --git a/src/ol/renderer/canvas/ImageLayer.js b/src/ol/renderer/canvas/ImageLayer.js index ea726ba167..5a240bb580 100644 --- a/src/ol/renderer/canvas/ImageLayer.js +++ b/src/ol/renderer/canvas/ImageLayer.js @@ -16,8 +16,6 @@ import {create as createTransform, compose as composeTransform} from '../../tran class CanvasImageLayerRenderer extends IntermediateCanvasRenderer { /** - * @constructor - * @extends {module:ol/renderer/canvas/IntermediateCanvas} * @param {module:ol/layer/Image|module:ol/layer/Vector} imageLayer Image or vector layer. * @api */ diff --git a/src/ol/renderer/canvas/VectorTileLayer.js b/src/ol/renderer/canvas/VectorTileLayer.js index 49256a4fca..03267f69fc 100644 --- a/src/ol/renderer/canvas/VectorTileLayer.js +++ b/src/ol/renderer/canvas/VectorTileLayer.js @@ -50,8 +50,6 @@ const VECTOR_REPLAYS = { class CanvasVectorTileLayerRenderer extends CanvasTileLayerRenderer { /** - * @constructor - * @extends {module:ol/renderer/canvas/TileLayer} * @param {module:ol/layer/VectorTile} layer VectorTile layer. * @api */ diff --git a/src/ol/reproj/Image.js b/src/ol/reproj/Image.js index 7c68e098dd..4c75e87565 100644 --- a/src/ol/reproj/Image.js +++ b/src/ol/reproj/Image.js @@ -23,8 +23,6 @@ class ReprojImage extends ImageBase { * Class encapsulating single reprojected image. * See {@link module:ol/source/Image~ImageSource}. * - * @constructor - * @extends {module:ol/ImageBase} * @param {module:ol/proj/Projection} sourceProj Source projection (of the data). * @param {module:ol/proj/Projection} targetProj Target projection. * @param {module:ol/extent~Extent} targetExtent Target extent. diff --git a/src/ol/reproj/Tile.js b/src/ol/reproj/Tile.js index a02d4b7606..2ef188b86e 100644 --- a/src/ol/reproj/Tile.js +++ b/src/ol/reproj/Tile.js @@ -24,8 +24,6 @@ class ReprojTile extends Tile { * Class encapsulating single reprojected tile. * See {@link module:ol/source/TileImage~TileImage}. * - * @constructor - * @extends {module:ol/Tile} * @param {module:ol/proj/Projection} sourceProj Source projection. * @param {module:ol/tilegrid/TileGrid} sourceTileGrid Source tile grid. * @param {module:ol/proj/Projection} targetProj Target projection. diff --git a/src/ol/reproj/Triangulation.js b/src/ol/reproj/Triangulation.js index 838c653ca7..4a480524c3 100644 --- a/src/ol/reproj/Triangulation.js +++ b/src/ol/reproj/Triangulation.js @@ -40,15 +40,16 @@ const MAX_TRIANGLE_WIDTH = 0.25; * @classdesc * Class containing triangulation of the given target extent. * Used for determining source data and the reprojection itself. - * - * @param {module:ol/proj/Projection} sourceProj Source projection. - * @param {module:ol/proj/Projection} targetProj Target projection. - * @param {module:ol/extent~Extent} targetExtent Target extent to triangulate. - * @param {module:ol/extent~Extent} maxSourceExtent Maximal source extent that can be used. - * @param {number} errorThreshold Acceptable error (in source units). - * @constructor */ class Triangulation { + + /** + * @param {module:ol/proj/Projection} sourceProj Source projection. + * @param {module:ol/proj/Projection} targetProj Target projection. + * @param {module:ol/extent~Extent} targetExtent Target extent to triangulate. + * @param {module:ol/extent~Extent} maxSourceExtent Maximal source extent that can be used. + * @param {number} errorThreshold Acceptable error (in source units). + */ constructor(sourceProj, targetProj, targetExtent, maxSourceExtent, errorThreshold) { /** diff --git a/src/ol/source/BingMaps.js b/src/ol/source/BingMaps.js index d8813f815c..c92d5926a1 100644 --- a/src/ol/source/BingMaps.js +++ b/src/ol/source/BingMaps.js @@ -49,8 +49,6 @@ class BingMaps extends TileImage { * @classdesc * Layer source for Bing Maps tile data. * - * @constructor - * @extends {module:ol/source/TileImage} * @param {module:ol/source/BingMaps~Options=} options Bing Maps options. * @api */ diff --git a/src/ol/source/CartoDB.js b/src/ol/source/CartoDB.js index 301c21d339..cea2152946 100644 --- a/src/ol/source/CartoDB.js +++ b/src/ol/source/CartoDB.js @@ -36,8 +36,6 @@ class CartoDB extends XYZ { * @classdesc * Layer source for the CartoDB Maps API. * - * @constructor - * @extends {module:ol/source/XYZ} * @param {module:ol/source/CartoDB~Options=} options CartoDB options. * @api */ diff --git a/src/ol/source/Cluster.js b/src/ol/source/Cluster.js index 48eaaf5a9d..4e10689c68 100644 --- a/src/ol/source/Cluster.js +++ b/src/ol/source/Cluster.js @@ -43,9 +43,7 @@ class Cluster extends VectorSource { * geometries. For other geometry types, or if not all geometries should be * considered for clustering, a custom `geometryFunction` can be defined. * - * @constructor * @param {module:ol/source/Cluster~Options=} options Cluster options. - * @extends {module:ol/source/Vector} * @api */ constructor(options) { diff --git a/src/ol/source/Image.js b/src/ol/source/Image.js index 1a0a27172f..9c5a34b499 100644 --- a/src/ol/source/Image.js +++ b/src/ol/source/Image.js @@ -48,8 +48,6 @@ class ImageSourceEvent extends Event { * Events emitted by {@link module:ol/source/Image~ImageSource} instances are instances of this * type. * - * @constructor - * @extends {module:ol/events/Event} * @param {string} type Type. * @param {module:ol/Image} image The image. */ @@ -86,9 +84,6 @@ class ImageSource extends Source { * instantiated in apps. * Base class for sources providing a single image. * - * @constructor - * @abstract - * @extends {module:ol/source/Source} * @param {module:ol/source/Image~Options} options Single image source options. * @api */ diff --git a/src/ol/source/ImageArcGISRest.js b/src/ol/source/ImageArcGISRest.js index 9bd9b4a011..1141f5c3a0 100644 --- a/src/ol/source/ImageArcGISRest.js +++ b/src/ol/source/ImageArcGISRest.js @@ -49,9 +49,7 @@ class ImageArcGISRest extends ImageSource { * take advantage of ol image caching and use * {@link module:ol/source/TileArcGISRest} data source. * - * @constructor * @fires ol/source/Image~ImageSourceEvent - * @extends {module:ol/source/Image} * @param {module:ol/source/ImageArcGISRest~Options=} opt_options Image ArcGIS Rest Options. * @api */ diff --git a/src/ol/source/ImageCanvas.js b/src/ol/source/ImageCanvas.js index eee6f6a6dd..81838533a1 100644 --- a/src/ol/source/ImageCanvas.js +++ b/src/ol/source/ImageCanvas.js @@ -47,8 +47,6 @@ class ImageCanvasSource extends ImageSource { * @classdesc * Base class for image sources where a canvas element is the image. * - * @constructor - * @extends {module:ol/source/Image} * @param {module:ol/source/ImageCanvas~Options=} options ImageCanvas options. * @api */ diff --git a/src/ol/source/ImageMapGuide.js b/src/ol/source/ImageMapGuide.js index 115e764576..d65be4ce7b 100644 --- a/src/ol/source/ImageMapGuide.js +++ b/src/ol/source/ImageMapGuide.js @@ -37,9 +37,7 @@ class ImageMapGuide extends ImageSource { * @classdesc * Source for images from Mapguide servers * - * @constructor * @fires ol/source/Image~ImageSourceEvent - * @extends {module:ol/source/Image} * @param {module:ol/source/ImageMapGuide~Options=} options ImageMapGuide options. * @api */ diff --git a/src/ol/source/ImageStatic.js b/src/ol/source/ImageStatic.js index 11ea42ea44..c278854785 100644 --- a/src/ol/source/ImageStatic.js +++ b/src/ol/source/ImageStatic.js @@ -33,8 +33,6 @@ class Static extends ImageSource { * @classdesc * A layer source for displaying a single, static image. * - * @constructor - * @extends {module:ol/source/Image} * @param {module:ol/source/ImageStatic~Options=} options ImageStatic options. * @api */ diff --git a/src/ol/source/ImageWMS.js b/src/ol/source/ImageWMS.js index fd8f80cc5e..a02e8b346c 100644 --- a/src/ol/source/ImageWMS.js +++ b/src/ol/source/ImageWMS.js @@ -56,9 +56,7 @@ class ImageWMS extends ImageSource { * @classdesc * Source for WMS servers providing single, untiled images. * - * @constructor * @fires ol/source/Image~ImageSourceEvent - * @extends {module:ol/source/Image} * @param {module:ol/source/ImageWMS~Options=} [opt_options] ImageWMS options. * @api */ diff --git a/src/ol/source/OSM.js b/src/ol/source/OSM.js index d551190299..c5192e8911 100644 --- a/src/ol/source/OSM.js +++ b/src/ol/source/OSM.js @@ -47,8 +47,6 @@ class OSM extends XYZ { * @classdesc * Layer source for the OpenStreetMap tile server. * - * @constructor - * @extends {module:ol/source/XYZ} * @param {module:ol/source/OSM~Options=} [opt_options] Open Street Map options. * @api */ diff --git a/src/ol/source/Raster.js b/src/ol/source/Raster.js index 9526dd7d61..b48e1ee9bf 100644 --- a/src/ol/source/Raster.js +++ b/src/ol/source/Raster.js @@ -79,8 +79,6 @@ class RasterSourceEvent extends Event { * Events emitted by {@link module:ol/source/Raster} instances are instances of this * type. * - * @constructor - * @extends {module:ol/events/Event} * @param {string} type Type. * @param {module:ol/PluggableMap~FrameState} frameState The frame state. * @param {Object} data An object made available to operations. @@ -142,8 +140,6 @@ class RasterSource extends ImageSource { * {@link module:ol/source/Raster~Operation} function to transform input pixel values into * output pixel values. * - * @constructor - * @extends {module:ol/source/Image} * @fires ol/source/Raster~RasterSourceEvent * @param {module:ol/source/Raster~Options=} options Options. * @api diff --git a/src/ol/source/Source.js b/src/ol/source/Source.js index 468d7c71b8..6310d7587a 100644 --- a/src/ol/source/Source.js +++ b/src/ol/source/Source.js @@ -46,9 +46,6 @@ class Source extends BaseObject { * * A generic `change` event is triggered when the state of the source changes. * - * @constructor - * @abstract - * @extends {module:ol/Object} * @param {module:ol/source/Source~Options} options Source options. * @api */ diff --git a/src/ol/source/Stamen.js b/src/ol/source/Stamen.js index 6a23d12c27..20447bffda 100644 --- a/src/ol/source/Stamen.js +++ b/src/ol/source/Stamen.js @@ -115,8 +115,6 @@ class Stamen extends XYZ { * @classdesc * Layer source for the Stamen tile server. * - * @constructor - * @extends {module:ol/source/XYZ} * @param {module:ol/source/Stamen~Options=} options Stamen options. * @api */ diff --git a/src/ol/source/Tile.js b/src/ol/source/Tile.js index 427f3e547b..8bbea4407f 100644 --- a/src/ol/source/Tile.js +++ b/src/ol/source/Tile.js @@ -34,9 +34,6 @@ class TileSource extends Source { * instantiated in apps. * Base class for sources providing images divided into a tile grid. * - * @constructor - * @abstract - * @extends {module:ol/source/Source} * @param {module:ol/source/Tile~Options=} options SourceTile source options. * @api */ @@ -311,8 +308,6 @@ TileSource.prototype.useTile = UNDEFINED; * Events emitted by {@link module:ol/source/Tile~TileSource} instances are instances of this * type. * - * @constructor - * @extends {module:ol/events/Event} * @param {string} type Type. * @param {module:ol/Tile} tile The tile. */ diff --git a/src/ol/source/TileArcGISRest.js b/src/ol/source/TileArcGISRest.js index d4e83a4a64..c93e2b7f78 100644 --- a/src/ol/source/TileArcGISRest.js +++ b/src/ol/source/TileArcGISRest.js @@ -60,8 +60,6 @@ class TileArcGISRest extends TileImage { * For cached ArcGIS services, better performance is available using the * {@link module:ol/source/XYZ~XYZ} data source. * - * @constructor - * @extends {module:ol/source/TileImage} * @param {module:ol/source/TileArcGISRest~Options=} opt_options Tile ArcGIS Rest options. * @api */ diff --git a/src/ol/source/TileDebug.js b/src/ol/source/TileDebug.js index 44033c6c69..e0286cb9d2 100644 --- a/src/ol/source/TileDebug.js +++ b/src/ol/source/TileDebug.js @@ -12,8 +12,6 @@ import {getKeyZXY} from '../tilecoord.js'; class LabeledTile extends Tile { /** - * @constructor - * @extends {module:ol/Tile} * @param {module:ol/tilecoord~TileCoord} tileCoord Tile coordinate. * @param {module:ol/size~Size} tileSize Tile size. * @param {string} text Text. @@ -91,8 +89,6 @@ class TileDebug extends TileSource { * * Uses Canvas context2d, so requires Canvas support. * - * @constructor - * @extends {module:ol/source/Tile} * @param {module:ol/source/TileDebug~Options=} options Debug tile options. * @api */ diff --git a/src/ol/source/TileImage.js b/src/ol/source/TileImage.js index d822e37fe6..8ac058026a 100644 --- a/src/ol/source/TileImage.js +++ b/src/ol/source/TileImage.js @@ -59,9 +59,7 @@ class TileImage extends UrlTile { * @classdesc * Base class for sources providing images divided into a tile grid. * - * @constructor * @fires module:ol/source/Tile~TileSourceEvent - * @extends {module:ol/source/UrlTile} * @param {module:ol/source/TileImage~Options=} options Image tile options. * @api */ diff --git a/src/ol/source/TileJSON.js b/src/ol/source/TileJSON.js index 5bb800fbd9..69b01151b0 100644 --- a/src/ol/source/TileJSON.js +++ b/src/ol/source/TileJSON.js @@ -49,8 +49,6 @@ class TileJSON extends TileImage { * @classdesc * Layer source for tile data in TileJSON format. * - * @constructor - * @extends {module:ol/source/TileImage} * @param {module:ol/source/TileJSON~Options=} options TileJSON options. * @api */ diff --git a/src/ol/source/TileWMS.js b/src/ol/source/TileWMS.js index 890b1a157b..13add52b03 100644 --- a/src/ol/source/TileWMS.js +++ b/src/ol/source/TileWMS.js @@ -76,8 +76,6 @@ class TileWMS extends TileImage { * @classdesc * Layer source for tile data from WMS servers. * - * @constructor - * @extends {module:ol/source/TileImage} * @param {module:ol/source/TileWMS~Options=} [opt_options] Tile WMS options. * @api */ diff --git a/src/ol/source/UTFGrid.js b/src/ol/source/UTFGrid.js index 37619a55b8..c867101eb2 100644 --- a/src/ol/source/UTFGrid.js +++ b/src/ol/source/UTFGrid.js @@ -18,8 +18,6 @@ import {createXYZ, extentFromProjection} from '../tilegrid.js'; /** - * @constructor - * @extends {module:ol/Tile} * @param {module:ol/tilecoord~TileCoord} tileCoord Tile coordinate. * @param {module:ol/TileState} state State. * @param {string} src Image source URI. @@ -275,8 +273,6 @@ class UTFGrid extends TileSource { * @classdesc * Layer source for UTFGrid interaction data loaded from TileJSON format. * - * @constructor - * @extends {module:ol/source/Tile} * @param {module:ol/source/UTFGrid~Options=} options Source options. * @api */ diff --git a/src/ol/source/UrlTile.js b/src/ol/source/UrlTile.js index 073698bbd4..39f07412aa 100644 --- a/src/ol/source/UrlTile.js +++ b/src/ol/source/UrlTile.js @@ -32,10 +32,7 @@ class UrlTile extends TileSource { * @classdesc * Base class for sources providing tiles divided into a tile grid over http. * - * @constructor - * @abstract * @fires module:ol/source/TileEvent - * @extends {module:ol/source/Tile} * @param {module:ol/source/UrlTile~Options=} options Image tile options. */ constructor(options) { diff --git a/src/ol/source/Vector.js b/src/ol/source/Vector.js index 90bf0fdd1d..1c9c4f00e5 100644 --- a/src/ol/source/Vector.js +++ b/src/ol/source/Vector.js @@ -36,8 +36,6 @@ import RBush from '../structs/RBush.js'; * Events emitted by {@link module:ol/source/Vector} instances are instances of this * type. * - * @constructor - * @extends {module:ol/events/Event} * @param {string} type Type. * @param {module:ol/Feature=} opt_feature Feature. */ @@ -157,8 +155,6 @@ class VectorSource extends Source { * by this source are suitable for editing. See {@link module:ol/source/VectorTile~VectorTile} for * vector data that is optimized for rendering. * - * @constructor - * @extends {module:ol/source/Source} * @fires ol/source/Vector~VectorSourceEvent * @param {module:ol/source/Vector~Options=} opt_options Vector source options. * @api diff --git a/src/ol/source/VectorTile.js b/src/ol/source/VectorTile.js index 31b4ef8a1b..a128617354 100644 --- a/src/ol/source/VectorTile.js +++ b/src/ol/source/VectorTile.js @@ -70,9 +70,7 @@ class VectorTile extends UrlTile { * {@link module:ol/source/Vector} for vector sources that are suitable for feature * editing. * - * @constructor * @fires module:ol/source/Tile~TileSourceEvent - * @extends {module:ol/source/UrlTile} * @param {module:ol/source/VectorTile~Options=} options Vector tile options. * @api */ diff --git a/src/ol/source/WMTS.js b/src/ol/source/WMTS.js index d418f96386..fe48d62a22 100644 --- a/src/ol/source/WMTS.js +++ b/src/ol/source/WMTS.js @@ -62,8 +62,6 @@ class WMTS extends TileImage { * @classdesc * Layer source for tile data from WMTS servers. * - * @constructor - * @extends {module:ol/source/TileImage} * @param {module:ol/source/WMTS~Options=} options WMTS options. * @api */ diff --git a/src/ol/source/XYZ.js b/src/ol/source/XYZ.js index fbc9cc756d..ddabfaf5cf 100644 --- a/src/ol/source/XYZ.js +++ b/src/ol/source/XYZ.js @@ -63,8 +63,6 @@ class XYZ extends TileImage { * } * * - * @constructor - * @extends {module:ol/source/TileImage} * @param {module:ol/source/XYZ~Options=} opt_options XYZ options. * @api */ diff --git a/src/ol/source/Zoomify.js b/src/ol/source/Zoomify.js index f1e7ed9878..b6f58be100 100644 --- a/src/ol/source/Zoomify.js +++ b/src/ol/source/Zoomify.js @@ -24,8 +24,6 @@ const TierSizeCalculation = { /** - * @constructor - * @extends {module:ol/ImageTile} * @param {module:ol/tilegrid/TileGrid} tileGrid TileGrid that the tile belongs to. * @param {module:ol/tilecoord~TileCoord} tileCoord Tile coordinate. * @param {module:ol/TileState} state State. @@ -124,8 +122,6 @@ class Zoomify extends TileImage { * Layer source for tile data in Zoomify format (both Zoomify and Internet * Imaging Protocol are supported). * - * @constructor - * @extends {module:ol/source/TileImage} * @param {module:ol/source/Zoomify~Options=} opt_options Options. * @api */ diff --git a/src/ol/structs/LRUCache.js b/src/ol/structs/LRUCache.js index 2c4dff5798..ee431d8253 100644 --- a/src/ol/structs/LRUCache.js +++ b/src/ol/structs/LRUCache.js @@ -22,8 +22,7 @@ class LRUCache extends EventTarget { * Implements a Least-Recently-Used cache where the keys do not conflict with * Object's properties (e.g. 'hasOwnProperty' is not allowed as a key). Expiring * items from the cache is the responsibility of the user. - * @constructor - * @extends {module:ol/events/EventTarget} + * * @fires module:ol/events/Event~Event * @struct * @template T diff --git a/src/ol/structs/LinkedList.js b/src/ol/structs/LinkedList.js index 933d9c42c4..bbaf16afb4 100644 --- a/src/ol/structs/LinkedList.js +++ b/src/ol/structs/LinkedList.js @@ -11,16 +11,14 @@ */ -/** - * Creates an empty linked list structure. - * - * @constructor - * @struct - * @param {boolean=} opt_circular The last item is connected to the first one, - * and the first item to the last one. Default is true. - */ class LinkedList { + /** + * Creates an empty linked list structure. + * + * @param {boolean=} opt_circular The last item is connected to the first one, + * and the first item to the last one. Default is true. + */ constructor(opt_circular) { /** diff --git a/src/ol/structs/PriorityQueue.js b/src/ol/structs/PriorityQueue.js index d8b75a9a17..2bbd260cdc 100644 --- a/src/ol/structs/PriorityQueue.js +++ b/src/ol/structs/PriorityQueue.js @@ -11,23 +11,21 @@ import {clear} from '../obj.js'; export const DROP = Infinity; -/** - * Priority queue. - * - * The implementation is inspired from the Closure Library's Heap class and - * Python's heapq module. - * - * @see http://closure-library.googlecode.com/svn/docs/closure_goog_structs_heap.js.source.html - * @see http://hg.python.org/cpython/file/2.7/Lib/heapq.py - * - * @constructor - * @param {function(T): number} priorityFunction Priority function. - * @param {function(T): string} keyFunction Key function. - * @struct - * @template T - */ class PriorityQueue { - + /** + * Priority queue. + * + * The implementation is inspired from the Closure Library's Heap class and + * Python's heapq module. + * + * @see http://closure-library.googlecode.com/svn/docs/closure_goog_structs_heap.js.source.html + * @see http://hg.python.org/cpython/file/2.7/Lib/heapq.py + * + * @param {function(T): number} priorityFunction Priority function. + * @param {function(T): string} keyFunction Key function. + * @struct + * @template T + */ constructor(priorityFunction, keyFunction) { /** diff --git a/src/ol/structs/RBush.js b/src/ol/structs/RBush.js index 025d81aee6..9cf1350187 100644 --- a/src/ol/structs/RBush.js +++ b/src/ol/structs/RBush.js @@ -15,17 +15,16 @@ import {isEmpty} from '../obj.js'; * @property {Object} [value] */ -/** - * Wrapper around the RBush by Vladimir Agafonkin. - * - * @constructor - * @param {number=} opt_maxEntries Max entries. - * @see https://github.com/mourner/rbush - * @struct - * @template T - */ class RBush { + /** + * Wrapper around the RBush by Vladimir Agafonkin. + * + * @param {number=} opt_maxEntries Max entries. + * @see https://github.com/mourner/rbush + * @struct + * @template T + */ constructor(opt_maxEntries) { /** diff --git a/src/ol/style/Atlas.js b/src/ol/style/Atlas.js index 27dba344e4..d60ab4f387 100644 --- a/src/ol/style/Atlas.js +++ b/src/ol/style/Atlas.js @@ -21,24 +21,24 @@ import {createCanvasContext2D} from '../dom.js'; * @property {HTMLCanvasElement} image */ -/** - * This class facilitates the creation of image atlases. - * - * Images added to an atlas will be rendered onto a single - * atlas canvas. The distribution of images on the canvas is - * managed with the bin packing algorithm described in: - * http://www.blackpawn.com/texts/lightmaps/ - * - * @constructor - * @struct - * @param {number} size The size in pixels of the sprite image. - * @param {number} space The space in pixels between images. - * Because texture coordinates are float values, the edges of - * images might not be completely correct (in a way that the - * edges overlap when being rendered). To avoid this we add a - * padding around each image. - */ class Atlas { + + /** + * This class facilitates the creation of image atlases. + * + * Images added to an atlas will be rendered onto a single + * atlas canvas. The distribution of images on the canvas is + * managed with the bin packing algorithm described in: + * http://www.blackpawn.com/texts/lightmaps/ + * + * @struct + * @param {number} size The size in pixels of the sprite image. + * @param {number} space The space in pixels between images. + * Because texture coordinates are float values, the edges of + * images might not be completely correct (in a way that the + * edges overlap when being rendered). To avoid this we add a + * padding around each image. + */ constructor(size, space) { /** diff --git a/src/ol/style/AtlasManager.js b/src/ol/style/AtlasManager.js index bcfb7e2592..d2883ff301 100644 --- a/src/ol/style/AtlasManager.js +++ b/src/ol/style/AtlasManager.js @@ -41,24 +41,23 @@ const INITIAL_ATLAS_SIZE = 256; const MAX_ATLAS_SIZE = -1; -/** - * Manages the creation of image atlases. - * - * Images added to this manager will be inserted into an atlas, which - * will be used for rendering. - * The `size` given in the constructor is the size for the first - * atlas. After that, when new atlases are created, they will have - * twice the size as the latest atlas (until `maxSize` is reached). - * - * If an application uses many images or very large images, it is recommended - * to set a higher `size` value to avoid the creation of too many atlases. - * - * @constructor - * @struct - * @api - * @param {module:ol/style/AtlasManager~Options=} opt_options Options. - */ class AtlasManager { + /** + * Manages the creation of image atlases. + * + * Images added to this manager will be inserted into an atlas, which + * will be used for rendering. + * The `size` given in the constructor is the size for the first + * atlas. After that, when new atlases are created, they will have + * twice the size as the latest atlas (until `maxSize` is reached). + * + * If an application uses many images or very large images, it is recommended + * to set a higher `size` value to avoid the creation of too many atlases. + * + * @struct + * @api + * @param {module:ol/style/AtlasManager~Options=} opt_options Options. + */ constructor(opt_options) { const options = opt_options || {}; diff --git a/src/ol/style/Circle.js b/src/ol/style/Circle.js index 9e6d91c9ef..a322e90907 100644 --- a/src/ol/style/Circle.js +++ b/src/ol/style/Circle.js @@ -25,9 +25,7 @@ class CircleStyle extends RegularShape { * @classdesc * Set circle style for vector features. * - * @constructor * @param {module:ol/style/Circle~Options=} opt_options Options. - * @extends {module:ol/style/RegularShape} * @api */ constructor(opt_options) { diff --git a/src/ol/style/Fill.js b/src/ol/style/Fill.js index 9a4ba82cee..595dec40c2 100644 --- a/src/ol/style/Fill.js +++ b/src/ol/style/Fill.js @@ -16,12 +16,12 @@ import {asString} from '../color.js'; /** * @classdesc * Set fill style for vector features. - * - * @constructor - * @param {module:ol/style/Fill~Options=} opt_options Options. - * @api */ class Fill { + /** + * @param {module:ol/style/Fill~Options=} opt_options Options. + * @api + */ constructor(opt_options) { const options = opt_options || {}; diff --git a/src/ol/style/Icon.js b/src/ol/style/Icon.js index 8eb05223d2..5972bfe5e9 100644 --- a/src/ol/style/Icon.js +++ b/src/ol/style/Icon.js @@ -57,9 +57,7 @@ class Icon extends ImageStyle { * @classdesc * Set icon style for vector features. * - * @constructor * @param {module:ol/style/Icon~Options=} opt_options Options. - * @extends {module:ol/style/Image} * @api */ constructor(opt_options) { diff --git a/src/ol/style/IconImage.js b/src/ol/style/IconImage.js index 34c148f199..3b01ee9a94 100644 --- a/src/ol/style/IconImage.js +++ b/src/ol/style/IconImage.js @@ -11,14 +11,12 @@ import {shared as iconImageCache} from '../style/IconImageCache.js'; class IconImage extends EventTarget { /** - * @constructor * @param {HTMLImageElement|HTMLCanvasElement} image Image. * @param {string|undefined} src Src. * @param {module:ol/size~Size} size Size. * @param {?string} crossOrigin Cross origin. * @param {module:ol/ImageState} imageState Image state. * @param {module:ol/color~Color} color Color. - * @extends {module:ol/events/EventTarget} */ constructor(image, src, size, crossOrigin, imageState, color) { diff --git a/src/ol/style/IconImageCache.js b/src/ol/style/IconImageCache.js index 311798d8d8..b89d55f2d1 100644 --- a/src/ol/style/IconImageCache.js +++ b/src/ol/style/IconImageCache.js @@ -5,7 +5,6 @@ import {asString} from '../color.js'; /** * Singleton class. Available through {@link module:ol/style/IconImageCache~shared}. - * @constructor */ class IconImageCache { constructor() { diff --git a/src/ol/style/Image.js b/src/ol/style/Image.js index 8b5d105366..5b659ff3de 100644 --- a/src/ol/style/Image.js +++ b/src/ol/style/Image.js @@ -18,13 +18,12 @@ * A base class used for creating subclasses and not instantiated in * apps. Base class for {@link module:ol/style/Icon~Icon}, {@link module:ol/style/Circle~CircleStyle} and * {@link module:ol/style/RegularShape~RegularShape}. - * - * @constructor - * @abstract - * @param {module:ol/style/Image~Options} options Options. - * @api */ class ImageStyle { + /** + * @param {module:ol/style/Image~Options} options Options. + * @api + */ constructor(options) { /** diff --git a/src/ol/style/RegularShape.js b/src/ol/style/RegularShape.js index ca70d7508b..b8c00b3c20 100644 --- a/src/ol/style/RegularShape.js +++ b/src/ol/style/RegularShape.js @@ -53,9 +53,7 @@ class RegularShape extends ImageStyle { * a regular polygon when `radius` is provided, or a star when `radius1` and * `radius2` are provided. * - * @constructor * @param {module:ol/style/RegularShape~Options} options Options. - * @extends {module:ol/style/Image} * @api */ constructor(options) { diff --git a/src/ol/style/Stroke.js b/src/ol/style/Stroke.js index d5e6571197..4e42ae23a8 100644 --- a/src/ol/style/Stroke.js +++ b/src/ol/style/Stroke.js @@ -26,12 +26,13 @@ import {getUid} from '../util.js'; * Note that the defaults given are the Canvas defaults, which will be used if * option is not defined. The `get` functions return whatever was entered in * the options; they will not return the default. - * - * @constructor - * @param {module:ol/style/Stroke~Options=} opt_options Options. - * @api */ class Stroke { + + /** + * @param {module:ol/style/Stroke~Options=} opt_options Options. + * @api + */ constructor(opt_options) { const options = opt_options || {}; diff --git a/src/ol/style/Style.js b/src/ol/style/Style.js index 69c8cadf3a..a1fa6d404d 100644 --- a/src/ol/style/Style.js +++ b/src/ol/style/Style.js @@ -143,13 +143,13 @@ import Stroke from '../style/Stroke.js'; * Container for vector feature rendering styles. Any changes made to the style * or its children through `set*()` methods will not take effect until the * feature or layer that uses the style is re-rendered. - * - * @constructor - * @struct - * @param {module:ol/style/Style~Options=} opt_options Style options. - * @api */ class Style { + + /** + * @param {module:ol/style/Style~Options=} opt_options Style options. + * @api + */ constructor(opt_options) { const options = opt_options || {}; diff --git a/src/ol/style/Text.js b/src/ol/style/Text.js index cd88073c82..c1eccc69fd 100644 --- a/src/ol/style/Text.js +++ b/src/ol/style/Text.js @@ -48,12 +48,13 @@ const DEFAULT_FILL_COLOR = '#333'; /** * @classdesc * Set text style for vector features. - * - * @constructor - * @param {module:ol/style/Text~Options=} opt_options Options. - * @api */ class Text { + + /** + * @param {module:ol/style/Text~Options=} opt_options Options. + * @api + */ constructor(opt_options) { const options = opt_options || {}; diff --git a/src/ol/tilegrid/TileGrid.js b/src/ol/tilegrid/TileGrid.js index b1c064c9b2..9d3d4bee6c 100644 --- a/src/ol/tilegrid/TileGrid.js +++ b/src/ol/tilegrid/TileGrid.js @@ -48,13 +48,13 @@ const tmpTileCoord = [0, 0, 0]; * @classdesc * Base class for setting the grid pattern for sources accessing tiled-image * servers. - * - * @constructor - * @param {module:ol/tilegrid/TileGrid~Options} options Tile grid options. - * @struct - * @api */ class TileGrid { + + /** + * @param {module:ol/tilegrid/TileGrid~Options} options Tile grid options. + * @api + */ constructor(options) { /** diff --git a/src/ol/tilegrid/WMTS.js b/src/ol/tilegrid/WMTS.js index f9245390db..7582fd7077 100644 --- a/src/ol/tilegrid/WMTS.js +++ b/src/ol/tilegrid/WMTS.js @@ -50,8 +50,6 @@ class WMTSTileGrid extends TileGrid { * @classdesc * Set the grid pattern for sources accessing WMTS tiled-image servers. * - * @constructor - * @extends {module:ol/tilegrid/TileGrid} * @param {module:ol/tilegrid/WMTS~Options} options WMTS options. * @struct * @api diff --git a/src/ol/webgl/Context.js b/src/ol/webgl/Context.js index 2c313263a3..d3cad9c99e 100644 --- a/src/ol/webgl/Context.js +++ b/src/ol/webgl/Context.js @@ -21,8 +21,6 @@ import ContextEventType from '../webgl/ContextEventType.js'; /** * @classdesc * A WebGL context for accessing low-level WebGL capabilities. - * - * @extends {module:ol/Disposable} */ class WebGLContext extends Disposable { diff --git a/src/ol/webgl/Fragment.js b/src/ol/webgl/Fragment.js index e96a853502..aeb90368f2 100644 --- a/src/ol/webgl/Fragment.js +++ b/src/ol/webgl/Fragment.js @@ -5,9 +5,6 @@ import {FRAGMENT_SHADER} from '../webgl.js'; import WebGLShader from '../webgl/Shader.js'; -/** - * @extends {module:ol/webgl/Shader} - */ class WebGLFragment extends WebGLShader { /** diff --git a/src/ol/webgl/Vertex.js b/src/ol/webgl/Vertex.js index 746d871319..579aaa2bae 100644 --- a/src/ol/webgl/Vertex.js +++ b/src/ol/webgl/Vertex.js @@ -5,9 +5,6 @@ import {VERTEX_SHADER} from '../webgl.js'; import WebGLShader from '../webgl/Shader.js'; -/** - * @extends {module:ol/webgl/Shader} - */ class WebGLVertex extends WebGLShader { /** diff --git a/test/spec/ol/source/tile.test.js b/test/spec/ol/source/tile.test.js index 4073321b81..0a46e6dc85 100644 --- a/test/spec/ol/source/tile.test.js +++ b/test/spec/ol/source/tile.test.js @@ -13,8 +13,6 @@ import TileGrid from '../../../../src/ol/tilegrid/TileGrid.js'; * Tile source for tests that uses a EPSG:4326 based grid with 4 resolutions and * 256x256 tiles. * - * @constructor - * @extends {ol.source.Tile} * @param {Object.} tileStates Lookup of tile key to * tile state. */ From 5f058e877f38bbdf293ffbeb3585a50be2c10b7c Mon Sep 17 00:00:00 2001 From: Tim Schaub Date: Wed, 18 Jul 2018 00:39:51 -0600 Subject: [PATCH 083/107] Call super and fix up comments in render box --- src/ol/render/Box.js | 57 ++++++++++++++++++++++---------------------- 1 file changed, 29 insertions(+), 28 deletions(-) diff --git a/src/ol/render/Box.js b/src/ol/render/Box.js index 15b84bb6cc..c0e9a149bd 100644 --- a/src/ol/render/Box.js +++ b/src/ol/render/Box.js @@ -10,51 +10,52 @@ class RenderBox extends Disposable { * @param {string} className CSS class name. */ constructor(className) { + super(); /** - * @type {module:ol/geom/Polygon} - * @private - */ + * @type {module:ol/geom/Polygon} + * @private + */ this.geometry_ = null; /** - * @type {HTMLDivElement} - * @private - */ + * @type {HTMLDivElement} + * @private + */ this.element_ = /** @type {HTMLDivElement} */ (document.createElement('div')); this.element_.style.position = 'absolute'; this.element_.className = 'ol-box ' + className; /** - * @private - * @type {module:ol/PluggableMap} - */ + * @private + * @type {module:ol/PluggableMap} + */ this.map_ = null; /** - * @private - * @type {module:ol~Pixel} - */ + * @private + * @type {module:ol~Pixel} + */ this.startPixel_ = null; /** - * @private - * @type {module:ol~Pixel} - */ + * @private + * @type {module:ol~Pixel} + */ this.endPixel_ = null; } /** - * @inheritDoc - */ + * @inheritDoc + */ disposeInternal() { this.setMap(null); } /** - * @private - */ + * @private + */ render_() { const startPixel = this.startPixel_; const endPixel = this.endPixel_; @@ -67,8 +68,8 @@ class RenderBox extends Disposable { } /** - * @param {module:ol/PluggableMap} map Map. - */ + * @param {module:ol/PluggableMap} map Map. + */ setMap(map) { if (this.map_) { this.map_.getOverlayContainer().removeChild(this.element_); @@ -82,9 +83,9 @@ class RenderBox extends Disposable { } /** - * @param {module:ol~Pixel} startPixel Start pixel. - * @param {module:ol~Pixel} endPixel End pixel. - */ + * @param {module:ol~Pixel} startPixel Start pixel. + * @param {module:ol~Pixel} endPixel End pixel. + */ setPixels(startPixel, endPixel) { this.startPixel_ = startPixel; this.endPixel_ = endPixel; @@ -93,8 +94,8 @@ class RenderBox extends Disposable { } /** - * Creates or updates the cached geometry. - */ + * Creates or updates the cached geometry. + */ createOrUpdateGeometry() { const startPixel = this.startPixel_; const endPixel = this.endPixel_; @@ -115,8 +116,8 @@ class RenderBox extends Disposable { } /** - * @return {module:ol/geom/Polygon} Geometry. - */ + * @return {module:ol/geom/Polygon} Geometry. + */ getGeometry() { return this.geometry_; } From 1e4ab17513eea0a33b4a172065e99d35b6dff34b Mon Sep 17 00:00:00 2001 From: Tim Schaub Date: Wed, 18 Jul 2018 00:43:29 -0600 Subject: [PATCH 084/107] Assign to this after super in WMTS tile grid --- src/ol/tilegrid/WMTS.js | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/ol/tilegrid/WMTS.js b/src/ol/tilegrid/WMTS.js index 7582fd7077..04fe5e3838 100644 --- a/src/ol/tilegrid/WMTS.js +++ b/src/ol/tilegrid/WMTS.js @@ -55,11 +55,6 @@ class WMTSTileGrid extends TileGrid { * @api */ constructor(options) { - /** - * @private - * @type {!Array.} - */ - this.matrixIds_ = options.matrixIds; super({ extent: options.extent, origin: options.origin, @@ -69,6 +64,12 @@ class WMTSTileGrid extends TileGrid { tileSizes: options.tileSizes, sizes: options.sizes }); + + /** + * @private + * @type {!Array.} + */ + this.matrixIds_ = options.matrixIds; } /** From 3f4ba0af5f46febc28525fcaffb82b159bdd4553 Mon Sep 17 00:00:00 2001 From: Tim Schaub Date: Wed, 18 Jul 2018 00:44:24 -0600 Subject: [PATCH 085/107] Call super in regular shape --- src/ol/style/RegularShape.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/ol/style/RegularShape.js b/src/ol/style/RegularShape.js index b8c00b3c20..34f7ee4853 100644 --- a/src/ol/style/RegularShape.js +++ b/src/ol/style/RegularShape.js @@ -57,6 +57,8 @@ class RegularShape extends ImageStyle { * @api */ constructor(options) { + super(); + /** * @private * @type {Array.} From fe8fcfbc7e45e122365db03b2446ea54d1e25d54 Mon Sep 17 00:00:00 2001 From: Tim Schaub Date: Wed, 18 Jul 2018 00:45:19 -0600 Subject: [PATCH 086/107] Call super in WebGL context --- src/ol/webgl/Context.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/ol/webgl/Context.js b/src/ol/webgl/Context.js index d3cad9c99e..2dc1fe7a57 100644 --- a/src/ol/webgl/Context.js +++ b/src/ol/webgl/Context.js @@ -29,6 +29,7 @@ class WebGLContext extends Disposable { * @param {WebGLRenderingContext} gl GL. */ constructor(canvas, gl) { + super(); /** * @private From 7713015b268d9d40c65201d23e4c1debd5c5fc27 Mon Sep 17 00:00:00 2001 From: Tim Schaub Date: Wed, 18 Jul 2018 00:49:00 -0600 Subject: [PATCH 087/107] Call super before accessing this in icon --- src/ol/style/Icon.js | 71 ++++++++++++++++++++++---------------------- 1 file changed, 35 insertions(+), 36 deletions(-) diff --git a/src/ol/style/Icon.js b/src/ol/style/Icon.js index 5972bfe5e9..ebacee9523 100644 --- a/src/ol/style/Icon.js +++ b/src/ol/style/Icon.js @@ -61,9 +61,43 @@ class Icon extends ImageStyle { * @api */ constructor(opt_options) { - const options = opt_options || {}; + /** + * @type {number} + */ + const opacity = options.opacity !== undefined ? options.opacity : 1; + + /** + * @type {number} + */ + const rotation = options.rotation !== undefined ? options.rotation : 0; + + /** + * @type {number} + */ + const scale = options.scale !== undefined ? options.scale : 1; + + /** + * @type {boolean} + */ + const rotateWithView = options.rotateWithView !== undefined ? + options.rotateWithView : false; + + /** + * @type {boolean} + */ + const snapToPixel = options.snapToPixel !== undefined ? + options.snapToPixel : true; + + super({ + opacity: opacity, + rotation: rotation, + scale: scale, + snapToPixel: snapToPixel, + rotateWithView: rotateWithView + }); + /** * @private * @type {Array.} @@ -174,41 +208,6 @@ class Icon extends ImageStyle { */ this.size_ = options.size !== undefined ? options.size : null; - /** - * @type {number} - */ - const opacity = options.opacity !== undefined ? options.opacity : 1; - - /** - * @type {boolean} - */ - const rotateWithView = options.rotateWithView !== undefined ? - options.rotateWithView : false; - - /** - * @type {number} - */ - const rotation = options.rotation !== undefined ? options.rotation : 0; - - /** - * @type {number} - */ - const scale = options.scale !== undefined ? options.scale : 1; - - /** - * @type {boolean} - */ - const snapToPixel = options.snapToPixel !== undefined ? - options.snapToPixel : true; - - super({ - opacity: opacity, - rotation: rotation, - scale: scale, - snapToPixel: snapToPixel, - rotateWithView: rotateWithView - }); - } /** From d885771d8d704f517710e9de45668e0d5793c82a Mon Sep 17 00:00:00 2001 From: Tim Schaub Date: Wed, 18 Jul 2018 00:52:23 -0600 Subject: [PATCH 088/107] Call super before accessing this in BingMaps source --- src/ol/source/BingMaps.js | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/src/ol/source/BingMaps.js b/src/ol/source/BingMaps.js index c92d5926a1..f3dc8835e1 100644 --- a/src/ol/source/BingMaps.js +++ b/src/ol/source/BingMaps.js @@ -54,11 +54,7 @@ class BingMaps extends TileImage { */ constructor(options) { - /** - * @private - * @type {boolean} - */ - this.hidpi_ = options.hidpi !== undefined ? options.hidpi : false; + const hidpi = options.hidpi !== undefined ? options.hidpi : false; super({ cacheSize: options.cacheSize, @@ -68,11 +64,18 @@ class BingMaps extends TileImage { reprojectionErrorThreshold: options.reprojectionErrorThreshold, state: SourceState.LOADING, tileLoadFunction: options.tileLoadFunction, - tilePixelRatio: this.hidpi_ ? 2 : 1, + tilePixelRatio: hidpi ? 2 : 1, wrapX: options.wrapX !== undefined ? options.wrapX : true, transition: options.transition }); + /** + * @private + * @type {boolean} + */ + this.hidpi_ = hidpi; + + /** * @private * @type {string} From 023bb638b28ba27dea0057c5bf4b370df8fd3cca Mon Sep 17 00:00:00 2001 From: Tim Schaub Date: Wed, 18 Jul 2018 00:53:17 -0600 Subject: [PATCH 089/107] Call super before accessing this in raster source --- src/ol/source/Raster.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/ol/source/Raster.js b/src/ol/source/Raster.js index b48e1ee9bf..d3436ff251 100644 --- a/src/ol/source/Raster.js +++ b/src/ol/source/Raster.js @@ -145,6 +145,7 @@ class RasterSource extends ImageSource { * @api */ constructor(options) { + super({}); /** * @private @@ -239,8 +240,6 @@ class RasterSource extends ImageSource { wantedTiles: {} }; - super({}); - if (options.operation !== undefined) { this.setOperation(options.operation, options.lib); } From 1309cf9b69e53406d1280b05f0963f9b6798eedd Mon Sep 17 00:00:00 2001 From: Tim Schaub Date: Wed, 18 Jul 2018 00:53:57 -0600 Subject: [PATCH 090/107] Call super before accessing this in Carto source --- src/ol/source/CartoDB.js | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/ol/source/CartoDB.js b/src/ol/source/CartoDB.js index cea2152946..e6a8f4fcea 100644 --- a/src/ol/source/CartoDB.js +++ b/src/ol/source/CartoDB.js @@ -40,6 +40,16 @@ class CartoDB extends XYZ { * @api */ constructor(options) { + super({ + attributions: options.attributions, + cacheSize: options.cacheSize, + crossOrigin: options.crossOrigin, + maxZoom: options.maxZoom !== undefined ? options.maxZoom : 18, + minZoom: options.minZoom, + projection: options.projection, + state: SourceState.LOADING, + wrapX: options.wrapX + }); /** * @type {string} @@ -65,16 +75,6 @@ class CartoDB extends XYZ { */ this.templateCache_ = {}; - super({ - attributions: options.attributions, - cacheSize: options.cacheSize, - crossOrigin: options.crossOrigin, - maxZoom: options.maxZoom !== undefined ? options.maxZoom : 18, - minZoom: options.minZoom, - projection: options.projection, - state: SourceState.LOADING, - wrapX: options.wrapX - }); this.initializeMap_(); } From b4e0d4c188d5168986be670d4edc02b5bf8b2092 Mon Sep 17 00:00:00 2001 From: Tim Schaub Date: Wed, 18 Jul 2018 01:00:19 -0600 Subject: [PATCH 091/107] Super before this in TileJSON source --- src/ol/source/TileJSON.js | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/ol/source/TileJSON.js b/src/ol/source/TileJSON.js index 69b01151b0..2013e81079 100644 --- a/src/ol/source/TileJSON.js +++ b/src/ol/source/TileJSON.js @@ -53,13 +53,6 @@ class TileJSON extends TileImage { * @api */ constructor(options) { - - /** - * @type {TileJSON} - * @private - */ - this.tileJSON_ = null; - super({ attributions: options.attributions, cacheSize: options.cacheSize, @@ -72,6 +65,13 @@ class TileJSON extends TileImage { transition: options.transition }); + /** + * @type {TileJSON} + * @private + */ + this.tileJSON_ = null; + + if (options.url) { if (options.jsonp) { requestJSONP(options.url, this.handleTileJSONResponse.bind(this), From 65b306891bd6dba95f990f4b743fe05e67811490 Mon Sep 17 00:00:00 2001 From: Frederic Junod Date: Wed, 18 Jul 2018 09:07:19 +0200 Subject: [PATCH 092/107] Remove 'struct' jsdoc tags, code indentation --- src/ol/format/filter/Filter.js | 13 +- src/ol/render/VectorContext.js | 108 +++++---- src/ol/render/canvas/ImageReplay.js | 115 +++++----- src/ol/render/canvas/Immediate.js | 1 - src/ol/render/canvas/LineStringReplay.js | 1 - src/ol/render/canvas/PolygonReplay.js | 1 - src/ol/render/canvas/Replay.js | 1 - src/ol/render/canvas/ReplayGroup.js | 4 +- src/ol/render/canvas/TextReplay.js | 1 - src/ol/render/webgl/CircleReplay.js | 1 - src/ol/render/webgl/ImageReplay.js | 1 - src/ol/render/webgl/Immediate.js | 1 - src/ol/render/webgl/LineStringReplay.js | 1 - src/ol/render/webgl/PolygonReplay.js | 1 - src/ol/render/webgl/Replay.js | 265 +++++++++++------------ src/ol/render/webgl/ReplayGroup.js | 1 - src/ol/render/webgl/TextReplay.js | 1 - src/ol/render/webgl/TextureReplay.js | 1 - src/ol/renderer/Layer.js | 1 - src/ol/renderer/Map.js | 1 - src/ol/structs/LRUCache.js | 1 - src/ol/structs/PriorityQueue.js | 1 - src/ol/structs/RBush.js | 1 - src/ol/style/Atlas.js | 1 - src/ol/style/AtlasManager.js | 1 - src/ol/tilegrid/WMTS.js | 1 - 26 files changed, 248 insertions(+), 278 deletions(-) diff --git a/src/ol/format/filter/Filter.js b/src/ol/format/filter/Filter.js index 8f5a7265fa..6ffb6ac25c 100644 --- a/src/ol/format/filter/Filter.js +++ b/src/ol/format/filter/Filter.js @@ -9,7 +9,6 @@ * Base class for WFS GetFeature filters. * * @abstract - * @struct */ class Filter { /** @@ -18,16 +17,16 @@ class Filter { constructor(tagName) { /** - * @private - * @type {!string} - */ + * @private + * @type {!string} + */ this.tagName_ = tagName; } /** - * The XML tag name for a filter. - * @returns {!string} Name. - */ + * The XML tag name for a filter. + * @returns {!string} Name. + */ getTagName() { return this.tagName_; } diff --git a/src/ol/render/VectorContext.js b/src/ol/render/VectorContext.js index 1ed6f81497..c189528ecb 100644 --- a/src/ol/render/VectorContext.js +++ b/src/ol/render/VectorContext.js @@ -5,110 +5,108 @@ /** * Context for drawing geometries. A vector context is available on render * events and does not need to be constructed directly. - * @struct * @api */ class VectorContext { /** - * Render a geometry with a custom renderer. - * - * @param {module:ol/geom/SimpleGeometry} geometry Geometry. - * @param {module:ol/Feature|module:ol/render/Feature} feature Feature. - * @param {Function} renderer Renderer. - */ + * Render a geometry with a custom renderer. + * + * @param {module:ol/geom/SimpleGeometry} geometry Geometry. + * @param {module:ol/Feature|module:ol/render/Feature} feature Feature. + * @param {Function} renderer Renderer. + */ drawCustom(geometry, feature, renderer) {} /** - * Render a geometry. - * - * @param {module:ol/geom/Geometry} geometry The geometry to render. - */ + * Render a geometry. + * + * @param {module:ol/geom/Geometry} geometry The geometry to render. + */ drawGeometry(geometry) {} /** - * Set the rendering style. - * - * @param {module:ol/style/Style} style The rendering style. - */ + * Set the rendering style. + * + * @param {module:ol/style/Style} style The rendering style. + */ setStyle(style) {} /** - * @param {module:ol/geom/Circle} circleGeometry Circle geometry. - * @param {module:ol/Feature} feature Feature. - */ + * @param {module:ol/geom/Circle} circleGeometry Circle geometry. + * @param {module:ol/Feature} feature Feature. + */ drawCircle(circleGeometry, feature) {} /** - * @param {module:ol/Feature} feature Feature. - * @param {module:ol/style/Style} style Style. - */ + * @param {module:ol/Feature} feature Feature. + * @param {module:ol/style/Style} style Style. + */ drawFeature(feature, style) {} /** - * @param {module:ol/geom/GeometryCollection} geometryCollectionGeometry Geometry - * collection. - * @param {module:ol/Feature} feature Feature. - */ + * @param {module:ol/geom/GeometryCollection} geometryCollectionGeometry Geometry collection. + * @param {module:ol/Feature} feature Feature. + */ drawGeometryCollection(geometryCollectionGeometry, feature) {} /** - * @param {module:ol/geom/LineString|module:ol/render/Feature} lineStringGeometry Line string geometry. - * @param {module:ol/Feature|module:ol/render/Feature} feature Feature. - */ + * @param {module:ol/geom/LineString|module:ol/render/Feature} lineStringGeometry Line string geometry. + * @param {module:ol/Feature|module:ol/render/Feature} feature Feature. + */ drawLineString(lineStringGeometry, feature) {} /** - * @param {module:ol/geom/MultiLineString|module:ol/render/Feature} multiLineStringGeometry MultiLineString geometry. - * @param {module:ol/Feature|module:ol/render/Feature} feature Feature. - */ + * @param {module:ol/geom/MultiLineString|module:ol/render/Feature} multiLineStringGeometry MultiLineString geometry. + * @param {module:ol/Feature|module:ol/render/Feature} feature Feature. + */ drawMultiLineString(multiLineStringGeometry, feature) {} /** - * @param {module:ol/geom/MultiPoint|module:ol/render/Feature} multiPointGeometry MultiPoint geometry. - * @param {module:ol/Feature|module:ol/render/Feature} feature Feature. - */ + * @param {module:ol/geom/MultiPoint|module:ol/render/Feature} multiPointGeometry MultiPoint geometry. + * @param {module:ol/Feature|module:ol/render/Feature} feature Feature. + */ drawMultiPoint(multiPointGeometry, feature) {} /** - * @param {module:ol/geom/MultiPolygon} multiPolygonGeometry MultiPolygon geometry. - * @param {module:ol/Feature|module:ol/render/Feature} feature Feature. - */ + * @param {module:ol/geom/MultiPolygon} multiPolygonGeometry MultiPolygon geometry. + * @param {module:ol/Feature|module:ol/render/Feature} feature Feature. + */ drawMultiPolygon(multiPolygonGeometry, feature) {} /** - * @param {module:ol/geom/Point|module:ol/render/Feature} pointGeometry Point geometry. - * @param {module:ol/Feature|module:ol/render/Feature} feature Feature. - */ + * @param {module:ol/geom/Point|module:ol/render/Feature} pointGeometry Point geometry. + * @param {module:ol/Feature|module:ol/render/Feature} feature Feature. + */ drawPoint(pointGeometry, feature) {} /** - * @param {module:ol/geom/Polygon|module:ol/render/Feature} polygonGeometry Polygon geometry. - * @param {module:ol/Feature|module:ol/render/Feature} feature Feature. - */ + * @param {module:ol/geom/Polygon|module:ol/render/Feature} polygonGeometry Polygon geometry. + * @param {module:ol/Feature|module:ol/render/Feature} feature Feature. + */ drawPolygon(polygonGeometry, feature) {} /** - * @param {module:ol/geom/Geometry|module:ol/render/Feature} geometry Geometry. - * @param {module:ol/Feature|module:ol/render/Feature} feature Feature. - */ + * @param {module:ol/geom/Geometry|module:ol/render/Feature} geometry Geometry. + * @param {module:ol/Feature|module:ol/render/Feature} feature Feature. + */ drawText(geometry, feature) {} /** - * @param {module:ol/style/Fill} fillStyle Fill style. - * @param {module:ol/style/Stroke} strokeStyle Stroke style. - */ + * @param {module:ol/style/Fill} fillStyle Fill style. + * @param {module:ol/style/Stroke} strokeStyle Stroke style. + */ setFillStrokeStyle(fillStyle, strokeStyle) {} /** - * @param {module:ol/style/Image} imageStyle Image style. - * @param {module:ol/render/canvas~DeclutterGroup=} opt_declutterGroup Declutter. - */ + * @param {module:ol/style/Image} imageStyle Image style. + * @param {module:ol/render/canvas~DeclutterGroup=} opt_declutterGroup Declutter. + */ setImageStyle(imageStyle, opt_declutterGroup) {} /** - * @param {module:ol/style/Text} textStyle Text style. - * @param {module:ol/render/canvas~DeclutterGroup=} opt_declutterGroup Declutter. - */ + * @param {module:ol/style/Text} textStyle Text style. + * @param {module:ol/render/canvas~DeclutterGroup=} opt_declutterGroup Declutter. + */ setTextStyle(textStyle, opt_declutterGroup) {} } diff --git a/src/ol/render/canvas/ImageReplay.js b/src/ol/render/canvas/ImageReplay.js index 7b6a704423..7d013d3b41 100644 --- a/src/ol/render/canvas/ImageReplay.js +++ b/src/ol/render/canvas/ImageReplay.js @@ -12,112 +12,111 @@ class CanvasImageReplay extends CanvasReplay { * @param {number} pixelRatio Pixel ratio. * @param {boolean} overlaps The replay can have overlapping geometries. * @param {?} declutterTree Declutter tree. - * @struct */ constructor(tolerance, maxExtent, resolution, pixelRatio, overlaps, declutterTree) { super(tolerance, maxExtent, resolution, pixelRatio, overlaps, declutterTree); /** - * @private - * @type {module:ol/render/canvas~DeclutterGroup} - */ + * @private + * @type {module:ol/render/canvas~DeclutterGroup} + */ this.declutterGroup_ = null; /** - * @private - * @type {HTMLCanvasElement|HTMLVideoElement|HTMLImageElement} - */ + * @private + * @type {HTMLCanvasElement|HTMLVideoElement|HTMLImageElement} + */ this.hitDetectionImage_ = null; /** - * @private - * @type {HTMLCanvasElement|HTMLVideoElement|HTMLImageElement} - */ + * @private + * @type {HTMLCanvasElement|HTMLVideoElement|HTMLImageElement} + */ this.image_ = null; /** - * @private - * @type {number|undefined} - */ + * @private + * @type {number|undefined} + */ this.anchorX_ = undefined; /** - * @private - * @type {number|undefined} - */ + * @private + * @type {number|undefined} + */ this.anchorY_ = undefined; /** - * @private - * @type {number|undefined} - */ + * @private + * @type {number|undefined} + */ this.height_ = undefined; /** - * @private - * @type {number|undefined} - */ + * @private + * @type {number|undefined} + */ this.opacity_ = undefined; /** - * @private - * @type {number|undefined} - */ + * @private + * @type {number|undefined} + */ this.originX_ = undefined; /** - * @private - * @type {number|undefined} - */ + * @private + * @type {number|undefined} + */ this.originY_ = undefined; /** - * @private - * @type {boolean|undefined} - */ + * @private + * @type {boolean|undefined} + */ this.rotateWithView_ = undefined; /** - * @private - * @type {number|undefined} - */ + * @private + * @type {number|undefined} + */ this.rotation_ = undefined; /** - * @private - * @type {number|undefined} - */ + * @private + * @type {number|undefined} + */ this.scale_ = undefined; /** - * @private - * @type {boolean|undefined} - */ + * @private + * @type {boolean|undefined} + */ this.snapToPixel_ = undefined; /** - * @private - * @type {number|undefined} - */ + * @private + * @type {number|undefined} + */ this.width_ = undefined; } /** - * @param {Array.} flatCoordinates Flat coordinates. - * @param {number} offset Offset. - * @param {number} end End. - * @param {number} stride Stride. - * @private - * @return {number} My end. - */ + * @param {Array.} flatCoordinates Flat coordinates. + * @param {number} offset Offset. + * @param {number} end End. + * @param {number} stride Stride. + * @private + * @return {number} My end. + */ drawCoordinates_(flatCoordinates, offset, end, stride) { return this.appendFlatCoordinates(flatCoordinates, offset, end, stride, false, false); } /** - * @inheritDoc - */ + * @inheritDoc + */ drawPoint(pointGeometry, feature) { if (!this.image_) { return; @@ -145,8 +144,8 @@ class CanvasImageReplay extends CanvasReplay { } /** - * @inheritDoc - */ + * @inheritDoc + */ drawMultiPoint(multiPointGeometry, feature) { if (!this.image_) { return; @@ -175,8 +174,8 @@ class CanvasImageReplay extends CanvasReplay { } /** - * @inheritDoc - */ + * @inheritDoc + */ finish() { this.reverseHitDetectionInstructions(); // FIXME this doesn't really protect us against further calls to draw*Geometry @@ -196,8 +195,8 @@ class CanvasImageReplay extends CanvasReplay { } /** - * @inheritDoc - */ + * @inheritDoc + */ setImageStyle(imageStyle, declutterGroup) { const anchor = imageStyle.getAnchor(); const size = imageStyle.getSize(); diff --git a/src/ol/render/canvas/Immediate.js b/src/ol/render/canvas/Immediate.js index 4d63df1bb3..dd74c5943a 100644 --- a/src/ol/render/canvas/Immediate.js +++ b/src/ol/render/canvas/Immediate.js @@ -31,7 +31,6 @@ class CanvasImmediateRenderer extends VectorContext { * @param {module:ol/extent~Extent} extent Extent. * @param {module:ol/transform~Transform} transform Transform. * @param {number} viewRotation View rotation. - * @struct */ constructor(context, pixelRatio, extent, transform, viewRotation) { super(); diff --git a/src/ol/render/canvas/LineStringReplay.js b/src/ol/render/canvas/LineStringReplay.js index 9f0e970ad8..66c8406f3e 100644 --- a/src/ol/render/canvas/LineStringReplay.js +++ b/src/ol/render/canvas/LineStringReplay.js @@ -12,7 +12,6 @@ class CanvasLineStringReplay extends CanvasReplay { * @param {number} pixelRatio Pixel ratio. * @param {boolean} overlaps The replay can have overlapping geometries. * @param {?} declutterTree Declutter tree. - * @struct */ constructor(tolerance, maxExtent, resolution, pixelRatio, overlaps, declutterTree) { super(tolerance, maxExtent, resolution, pixelRatio, overlaps, declutterTree); diff --git a/src/ol/render/canvas/PolygonReplay.js b/src/ol/render/canvas/PolygonReplay.js index 64144c9519..6c0d898055 100644 --- a/src/ol/render/canvas/PolygonReplay.js +++ b/src/ol/render/canvas/PolygonReplay.js @@ -18,7 +18,6 @@ class CanvasPolygonReplay extends CanvasReplay { * @param {number} pixelRatio Pixel ratio. * @param {boolean} overlaps The replay can have overlapping geometries. * @param {?} declutterTree Declutter tree. - * @struct */ constructor(tolerance, maxExtent, resolution, pixelRatio, overlaps, declutterTree) { super(tolerance, maxExtent, resolution, pixelRatio, overlaps, declutterTree); diff --git a/src/ol/render/canvas/Replay.js b/src/ol/render/canvas/Replay.js index 20a2ec2971..e50d99e883 100644 --- a/src/ol/render/canvas/Replay.js +++ b/src/ol/render/canvas/Replay.js @@ -49,7 +49,6 @@ class CanvasReplay extends VectorContext { * @param {number} pixelRatio Pixel ratio. * @param {boolean} overlaps The replay can have overlapping geometries. * @param {?} declutterTree Declutter tree. - * @struct */ constructor(tolerance, maxExtent, resolution, pixelRatio, overlaps, declutterTree) { super(); diff --git a/src/ol/render/canvas/ReplayGroup.js b/src/ol/render/canvas/ReplayGroup.js index 3ebaaffa9f..731f114b95 100644 --- a/src/ol/render/canvas/ReplayGroup.js +++ b/src/ol/render/canvas/ReplayGroup.js @@ -40,10 +40,8 @@ class CanvasReplayGroup extends ReplayGroup { * @param {number} resolution Resolution. * @param {number} pixelRatio Pixel ratio. * @param {boolean} overlaps The replay group can have overlapping geometries. - * @param {?} declutterTree Declutter tree - * for declutter processing in postrender. + * @param {?} declutterTree Declutter tree for declutter processing in postrender. * @param {number=} opt_renderBuffer Optional rendering buffer. - * @struct */ constructor( tolerance, diff --git a/src/ol/render/canvas/TextReplay.js b/src/ol/render/canvas/TextReplay.js index 87cbaf8922..bed111ab37 100644 --- a/src/ol/render/canvas/TextReplay.js +++ b/src/ol/render/canvas/TextReplay.js @@ -22,7 +22,6 @@ class CanvasTextReplay extends CanvasReplay { * @param {number} pixelRatio Pixel ratio. * @param {boolean} overlaps The replay can have overlapping geometries. * @param {?} declutterTree Declutter tree. - * @struct */ constructor(tolerance, maxExtent, resolution, pixelRatio, overlaps, declutterTree) { super(tolerance, maxExtent, resolution, pixelRatio, overlaps, declutterTree); diff --git a/src/ol/render/webgl/CircleReplay.js b/src/ol/render/webgl/CircleReplay.js index 352af6b2c6..7b95dd085a 100644 --- a/src/ol/render/webgl/CircleReplay.js +++ b/src/ol/render/webgl/CircleReplay.js @@ -19,7 +19,6 @@ class WebGLCircleReplay extends WebGLReplay { /** * @param {number} tolerance Tolerance. * @param {module:ol/extent~Extent} maxExtent Max extent. - * @struct */ constructor(tolerance, maxExtent) { super(tolerance, maxExtent); diff --git a/src/ol/render/webgl/ImageReplay.js b/src/ol/render/webgl/ImageReplay.js index 9ce185dcbe..b1b22edfba 100644 --- a/src/ol/render/webgl/ImageReplay.js +++ b/src/ol/render/webgl/ImageReplay.js @@ -9,7 +9,6 @@ class WebGLImageReplay extends WebGLTextureReplay { /** * @param {number} tolerance Tolerance. * @param {module:ol/extent~Extent} maxExtent Max extent. - * @struct */ constructor(tolerance, maxExtent) { super(tolerance, maxExtent); diff --git a/src/ol/render/webgl/Immediate.js b/src/ol/render/webgl/Immediate.js index 6e5c258b4f..13a2845e43 100644 --- a/src/ol/render/webgl/Immediate.js +++ b/src/ol/render/webgl/Immediate.js @@ -17,7 +17,6 @@ class WebGLImmediateRenderer extends VectorContext { * @param {module:ol/size~Size} size Size. * @param {module:ol/extent~Extent} extent Extent. * @param {number} pixelRatio Pixel ratio. - * @struct */ constructor(context, center, resolution, rotation, size, extent, pixelRatio) { super(); diff --git a/src/ol/render/webgl/LineStringReplay.js b/src/ol/render/webgl/LineStringReplay.js index adce9c1c20..4851b5a7db 100644 --- a/src/ol/render/webgl/LineStringReplay.js +++ b/src/ol/render/webgl/LineStringReplay.js @@ -39,7 +39,6 @@ class WebGLLineStringReplay extends WebGLReplay { /** * @param {number} tolerance Tolerance. * @param {module:ol/extent~Extent} maxExtent Max extent. - * @struct */ constructor(tolerance, maxExtent) { super(tolerance, maxExtent); diff --git a/src/ol/render/webgl/PolygonReplay.js b/src/ol/render/webgl/PolygonReplay.js index 6de0fc4dfd..0b69e32a8e 100644 --- a/src/ol/render/webgl/PolygonReplay.js +++ b/src/ol/render/webgl/PolygonReplay.js @@ -40,7 +40,6 @@ class WebGLPolygonReplay extends WebGLReplay { /** * @param {number} tolerance Tolerance. * @param {module:ol/extent~Extent} maxExtent Max extent. - * @struct */ constructor(tolerance, maxExtent) { super(tolerance, maxExtent); diff --git a/src/ol/render/webgl/Replay.js b/src/ol/render/webgl/Replay.js index 3bd4af2ba6..5a74456316 100644 --- a/src/ol/render/webgl/Replay.js +++ b/src/ol/render/webgl/Replay.js @@ -19,178 +19,176 @@ class WebGLReplay extends VectorContext { /** * @param {number} tolerance Tolerance. * @param {module:ol/extent~Extent} maxExtent Max extent. - * @struct */ constructor(tolerance, maxExtent) { super(); /** - * @protected - * @type {number} - */ + * @protected + * @type {number} + */ this.tolerance = tolerance; /** - * @protected - * @const - * @type {module:ol/extent~Extent} - */ + * @protected + * @const + * @type {module:ol/extent~Extent} + */ this.maxExtent = maxExtent; /** - * The origin of the coordinate system for the point coordinates sent to - * the GPU. To eliminate jitter caused by precision problems in the GPU - * we use the "Rendering Relative to Eye" technique described in the "3D - * Engine Design for Virtual Globes" book. - * @protected - * @type {module:ol/coordinate~Coordinate} - */ + * The origin of the coordinate system for the point coordinates sent to + * the GPU. To eliminate jitter caused by precision problems in the GPU + * we use the "Rendering Relative to Eye" technique described in the "3D + * Engine Design for Virtual Globes" book. + * @protected + * @type {module:ol/coordinate~Coordinate} + */ this.origin = getCenter(maxExtent); /** - * @private - * @type {module:ol/transform~Transform} - */ + * @private + * @type {module:ol/transform~Transform} + */ this.projectionMatrix_ = createTransform(); /** - * @private - * @type {module:ol/transform~Transform} - */ + * @private + * @type {module:ol/transform~Transform} + */ this.offsetRotateMatrix_ = createTransform(); /** - * @private - * @type {module:ol/transform~Transform} - */ + * @private + * @type {module:ol/transform~Transform} + */ this.offsetScaleMatrix_ = createTransform(); /** - * @private - * @type {Array.} - */ + * @private + * @type {Array.} + */ this.tmpMat4_ = create(); /** - * @protected - * @type {Array.} - */ + * @protected + * @type {Array.} + */ this.indices = []; /** - * @protected - * @type {?module:ol/webgl/Buffer} - */ + * @protected + * @type {?module:ol/webgl/Buffer} + */ this.indicesBuffer = null; /** - * Start index per feature (the index). - * @protected - * @type {Array.} - */ + * Start index per feature (the index). + * @protected + * @type {Array.} + */ this.startIndices = []; /** - * Start index per feature (the feature). - * @protected - * @type {Array.} - */ + * Start index per feature (the feature). + * @protected + * @type {Array.} + */ this.startIndicesFeature = []; /** - * @protected - * @type {Array.} - */ + * @protected + * @type {Array.} + */ this.vertices = []; /** - * @protected - * @type {?module:ol/webgl/Buffer} - */ + * @protected + * @type {?module:ol/webgl/Buffer} + */ this.verticesBuffer = null; /** - * Optional parameter for PolygonReplay instances. - * @protected - * @type {module:ol/render/webgl/LineStringReplay|undefined} - */ + * Optional parameter for PolygonReplay instances. + * @protected + * @type {module:ol/render/webgl/LineStringReplay|undefined} + */ this.lineStringReplay = undefined; } /** - * @abstract - * @param {module:ol/webgl/Context} context WebGL context. - * @return {function()} Delete resources function. - */ + * @abstract + * @param {module:ol/webgl/Context} context WebGL context. + * @return {function()} Delete resources function. + */ getDeleteResourcesFunction(context) {} /** - * @abstract - * @param {module:ol/webgl/Context} context Context. - */ + * @abstract + * @param {module:ol/webgl/Context} context Context. + */ finish(context) {} /** - * @abstract - * @protected - * @param {WebGLRenderingContext} gl gl. - * @param {module:ol/webgl/Context} context Context. - * @param {module:ol/size~Size} size Size. - * @param {number} pixelRatio Pixel ratio. - * @return {module:ol/render/webgl/circlereplay/defaultshader/Locations| - module:ol/render/webgl/linestringreplay/defaultshader/Locations| - module:ol/render/webgl/polygonreplay/defaultshader/Locations| - module:ol/render/webgl/texturereplay/defaultshader/Locations} Locations. - */ + * @abstract + * @protected + * @param {WebGLRenderingContext} gl gl. + * @param {module:ol/webgl/Context} context Context. + * @param {module:ol/size~Size} size Size. + * @param {number} pixelRatio Pixel ratio. + * @return {module:ol/render/webgl/circlereplay/defaultshader/Locations| + module:ol/render/webgl/linestringreplay/defaultshader/Locations| + module:ol/render/webgl/polygonreplay/defaultshader/Locations| + module:ol/render/webgl/texturereplay/defaultshader/Locations} Locations. + */ setUpProgram(gl, context, size, pixelRatio) {} /** - * @abstract - * @protected - * @param {WebGLRenderingContext} gl gl. - * @param {module:ol/render/webgl/circlereplay/defaultshader/Locations| - module:ol/render/webgl/linestringreplay/defaultshader/Locations| - module:ol/render/webgl/polygonreplay/defaultshader/Locations| - module:ol/render/webgl/texturereplay/defaultshader/Locations} locations Locations. - */ + * @abstract + * @protected + * @param {WebGLRenderingContext} gl gl. + * @param {module:ol/render/webgl/circlereplay/defaultshader/Locations| + module:ol/render/webgl/linestringreplay/defaultshader/Locations| + module:ol/render/webgl/polygonreplay/defaultshader/Locations| + module:ol/render/webgl/texturereplay/defaultshader/Locations} locations Locations. + */ shutDownProgram(gl, locations) {} /** - * @abstract - * @protected - * @param {WebGLRenderingContext} gl gl. - * @param {module:ol/webgl/Context} context Context. - * @param {Object.} skippedFeaturesHash Ids of features to skip. - * @param {boolean} hitDetection Hit detection mode. - */ + * @abstract + * @protected + * @param {WebGLRenderingContext} gl gl. + * @param {module:ol/webgl/Context} context Context. + * @param {Object.} skippedFeaturesHash Ids of features to skip. + * @param {boolean} hitDetection Hit detection mode. + */ drawReplay(gl, context, skippedFeaturesHash, hitDetection) {} /** - * @abstract - * @protected - * @param {WebGLRenderingContext} gl gl. - * @param {module:ol/webgl/Context} context Context. - * @param {Object.} skippedFeaturesHash Ids of features to skip. - * @param {function((module:ol/Feature|module:ol/render/Feature)): T|undefined} featureCallback Feature callback. - * @param {module:ol/extent~Extent=} opt_hitExtent Hit extent: Only features intersecting this extent are checked. - * @return {T|undefined} Callback result. - * @template T - */ + * @abstract + * @protected + * @param {WebGLRenderingContext} gl gl. + * @param {module:ol/webgl/Context} context Context. + * @param {Object.} skippedFeaturesHash Ids of features to skip. + * @param {function((module:ol/Feature|module:ol/render/Feature)): T|undefined} featureCallback Feature callback. + * @param {module:ol/extent~Extent=} opt_hitExtent Hit extent: Only features intersecting this extent are checked. + * @return {T|undefined} Callback result. + * @template T + */ drawHitDetectionReplayOneByOne(gl, context, skippedFeaturesHash, featureCallback, opt_hitExtent) {} /** - * @protected - * @param {WebGLRenderingContext} gl gl. - * @param {module:ol/webgl/Context} context Context. - * @param {Object.} skippedFeaturesHash Ids of features to skip. - * @param {function((module:ol/Feature|module:ol/render/Feature)): T|undefined} featureCallback Feature callback. - * @param {boolean} oneByOne Draw features one-by-one for the hit-detecion. - * @param {module:ol/extent~Extent=} opt_hitExtent Hit extent: Only features intersecting - * this extent are checked. - * @return {T|undefined} Callback result. - * @template T - */ + * @protected + * @param {WebGLRenderingContext} gl gl. + * @param {module:ol/webgl/Context} context Context. + * @param {Object.} skippedFeaturesHash Ids of features to skip. + * @param {function((module:ol/Feature|module:ol/render/Feature)): T|undefined} featureCallback Feature callback. + * @param {boolean} oneByOne Draw features one-by-one for the hit-detecion. + * @param {module:ol/extent~Extent=} opt_hitExtent Hit extent: Only features intersecting this extent are checked. + * @return {T|undefined} Callback result. + * @template T + */ drawHitDetectionReplay(gl, context, skippedFeaturesHash, featureCallback, oneByOne, opt_hitExtent) { if (!oneByOne) { // draw all hit-detection features in "once" (by texture group) @@ -204,14 +202,14 @@ class WebGLReplay extends VectorContext { } /** - * @protected - * @param {WebGLRenderingContext} gl gl. - * @param {module:ol/webgl/Context} context Context. - * @param {Object.} skippedFeaturesHash Ids of features to skip. - * @param {function((module:ol/Feature|module:ol/render/Feature)): T|undefined} featureCallback Feature callback. - * @return {T|undefined} Callback result. - * @template T - */ + * @protected + * @param {WebGLRenderingContext} gl gl. + * @param {module:ol/webgl/Context} context Context. + * @param {Object.} skippedFeaturesHash Ids of features to skip. + * @param {function((module:ol/Feature|module:ol/render/Feature)): T|undefined} featureCallback Feature callback. + * @return {T|undefined} Callback result. + * @template T + */ drawHitDetectionReplayAll(gl, context, skippedFeaturesHash, featureCallback) { gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); this.drawReplay(gl, context, skippedFeaturesHash, true); @@ -225,21 +223,20 @@ class WebGLReplay extends VectorContext { } /** - * @param {module:ol/webgl/Context} context Context. - * @param {module:ol/coordinate~Coordinate} center Center. - * @param {number} resolution Resolution. - * @param {number} rotation Rotation. - * @param {module:ol/size~Size} size Size. - * @param {number} pixelRatio Pixel ratio. - * @param {number} opacity Global opacity. - * @param {Object.} skippedFeaturesHash Ids of features to skip. - * @param {function((module:ol/Feature|module:ol/render/Feature)): T|undefined} featureCallback Feature callback. - * @param {boolean} oneByOne Draw features one-by-one for the hit-detecion. - * @param {module:ol/extent~Extent=} opt_hitExtent Hit extent: Only features intersecting - * this extent are checked. - * @return {T|undefined} Callback result. - * @template T - */ + * @param {module:ol/webgl/Context} context Context. + * @param {module:ol/coordinate~Coordinate} center Center. + * @param {number} resolution Resolution. + * @param {number} rotation Rotation. + * @param {module:ol/size~Size} size Size. + * @param {number} pixelRatio Pixel ratio. + * @param {number} opacity Global opacity. + * @param {Object.} skippedFeaturesHash Ids of features to skip. + * @param {function((module:ol/Feature|module:ol/render/Feature)): T|undefined} featureCallback Feature callback. + * @param {boolean} oneByOne Draw features one-by-one for the hit-detecion. + * @param {module:ol/extent~Extent=} opt_hitExtent Hit extent: Only features intersecting this extent are checked. + * @return {T|undefined} Callback result. + * @template T + */ replay( context, center, @@ -339,12 +336,12 @@ class WebGLReplay extends VectorContext { } /** - * @protected - * @param {WebGLRenderingContext} gl gl. - * @param {module:ol/webgl/Context} context Context. - * @param {number} start Start index. - * @param {number} end End index. - */ + * @protected + * @param {WebGLRenderingContext} gl gl. + * @param {module:ol/webgl/Context} context Context. + * @param {number} start Start index. + * @param {number} end End index. + */ drawElements(gl, context, start, end) { const elementType = context.hasOESElementIndexUint ? UNSIGNED_INT : UNSIGNED_SHORT; diff --git a/src/ol/render/webgl/ReplayGroup.js b/src/ol/render/webgl/ReplayGroup.js index 62b15c5932..0891ec3f9f 100644 --- a/src/ol/render/webgl/ReplayGroup.js +++ b/src/ol/render/webgl/ReplayGroup.js @@ -37,7 +37,6 @@ class WebGLReplayGroup extends ReplayGroup { * @param {number} tolerance Tolerance. * @param {module:ol/extent~Extent} maxExtent Max extent. * @param {number=} opt_renderBuffer Render buffer. - * @struct */ constructor(tolerance, maxExtent, opt_renderBuffer) { super(); diff --git a/src/ol/render/webgl/TextReplay.js b/src/ol/render/webgl/TextReplay.js index 7ecc5c2357..93407d902d 100644 --- a/src/ol/render/webgl/TextReplay.js +++ b/src/ol/render/webgl/TextReplay.js @@ -26,7 +26,6 @@ class WebGLTextReplay extends WebGLTextureReplay { /** * @param {number} tolerance Tolerance. * @param {module:ol/extent~Extent} maxExtent Max extent. - * @struct */ constructor(tolerance, maxExtent) { super(tolerance, maxExtent); diff --git a/src/ol/render/webgl/TextureReplay.js b/src/ol/render/webgl/TextureReplay.js index ab1b3c22b8..60302eb5dc 100644 --- a/src/ol/render/webgl/TextureReplay.js +++ b/src/ol/render/webgl/TextureReplay.js @@ -14,7 +14,6 @@ class WebGLTextureReplay extends WebGLReplay { /** * @param {number} tolerance Tolerance. * @param {module:ol/extent~Extent} maxExtent Max extent. - * @struct */ constructor(tolerance, maxExtent) { super(tolerance, maxExtent); diff --git a/src/ol/renderer/Layer.js b/src/ol/renderer/Layer.js index 99b5c34e59..ebcd30fcb3 100644 --- a/src/ol/renderer/Layer.js +++ b/src/ol/renderer/Layer.js @@ -14,7 +14,6 @@ class LayerRenderer extends Observable { /** * @param {module:ol/layer/Layer} layer Layer. - * @struct */ constructor(layer) { diff --git a/src/ol/renderer/Map.js b/src/ol/renderer/Map.js index 2127802d60..e189d39074 100644 --- a/src/ol/renderer/Map.js +++ b/src/ol/renderer/Map.js @@ -16,7 +16,6 @@ class MapRenderer extends Disposable { /** * @param {module:ol/PluggableMap} map Map. - * @struct */ constructor(map) { super(); diff --git a/src/ol/structs/LRUCache.js b/src/ol/structs/LRUCache.js index ee431d8253..c631c08e77 100644 --- a/src/ol/structs/LRUCache.js +++ b/src/ol/structs/LRUCache.js @@ -24,7 +24,6 @@ class LRUCache extends EventTarget { * items from the cache is the responsibility of the user. * * @fires module:ol/events/Event~Event - * @struct * @template T * @param {number=} opt_highWaterMark High water mark. */ diff --git a/src/ol/structs/PriorityQueue.js b/src/ol/structs/PriorityQueue.js index 2bbd260cdc..c78cb3b3dc 100644 --- a/src/ol/structs/PriorityQueue.js +++ b/src/ol/structs/PriorityQueue.js @@ -23,7 +23,6 @@ class PriorityQueue { * * @param {function(T): number} priorityFunction Priority function. * @param {function(T): string} keyFunction Key function. - * @struct * @template T */ constructor(priorityFunction, keyFunction) { diff --git a/src/ol/structs/RBush.js b/src/ol/structs/RBush.js index 9cf1350187..1039171e97 100644 --- a/src/ol/structs/RBush.js +++ b/src/ol/structs/RBush.js @@ -22,7 +22,6 @@ class RBush { * * @param {number=} opt_maxEntries Max entries. * @see https://github.com/mourner/rbush - * @struct * @template T */ constructor(opt_maxEntries) { diff --git a/src/ol/style/Atlas.js b/src/ol/style/Atlas.js index d60ab4f387..b263266a96 100644 --- a/src/ol/style/Atlas.js +++ b/src/ol/style/Atlas.js @@ -31,7 +31,6 @@ class Atlas { * managed with the bin packing algorithm described in: * http://www.blackpawn.com/texts/lightmaps/ * - * @struct * @param {number} size The size in pixels of the sprite image. * @param {number} space The space in pixels between images. * Because texture coordinates are float values, the edges of diff --git a/src/ol/style/AtlasManager.js b/src/ol/style/AtlasManager.js index d2883ff301..29c21afdc0 100644 --- a/src/ol/style/AtlasManager.js +++ b/src/ol/style/AtlasManager.js @@ -54,7 +54,6 @@ class AtlasManager { * If an application uses many images or very large images, it is recommended * to set a higher `size` value to avoid the creation of too many atlases. * - * @struct * @api * @param {module:ol/style/AtlasManager~Options=} opt_options Options. */ diff --git a/src/ol/tilegrid/WMTS.js b/src/ol/tilegrid/WMTS.js index 04fe5e3838..4ea2359d18 100644 --- a/src/ol/tilegrid/WMTS.js +++ b/src/ol/tilegrid/WMTS.js @@ -51,7 +51,6 @@ class WMTSTileGrid extends TileGrid { * Set the grid pattern for sources accessing WMTS tiled-image servers. * * @param {module:ol/tilegrid/WMTS~Options} options WMTS options. - * @struct * @api */ constructor(options) { From 8991dc9718cfceb51dc531d6d8eda5038715a145 Mon Sep 17 00:00:00 2001 From: ahocevar Date: Wed, 18 Jul 2018 12:09:04 +0200 Subject: [PATCH 093/107] Do not use this before super() --- src/ol/reproj/Image.js | 69 +++++++------- src/ol/source/WMTS.js | 200 ++++++++++++++++++++--------------------- 2 files changed, 131 insertions(+), 138 deletions(-) diff --git a/src/ol/reproj/Image.js b/src/ol/reproj/Image.js index 4c75e87565..0fd04bd71f 100644 --- a/src/ol/reproj/Image.js +++ b/src/ol/reproj/Image.js @@ -31,26 +31,8 @@ class ReprojImage extends ImageBase { * @param {module:ol/reproj/Image~FunctionType} getImageFunction * Function returning source images (extent, resolution, pixelRatio). */ - constructor( - sourceProj, - targetProj, - targetExtent, - targetResolution, - pixelRatio, - getImageFunction - ) { - - /** - * @private - * @type {module:ol/proj/Projection} - */ - this.targetProj_ = targetProj; - - /** - * @private - * @type {module:ol/extent~Extent} - */ - this.maxSourceExtent_ = sourceProj.getExtent(); + constructor(sourceProj, targetProj, targetExtent, targetResolution, pixelRatio, getImageFunction) { + const maxSourceExtent = sourceProj.getExtent(); const maxTargetExtent = targetProj.getExtent(); const limitedTargetExtent = maxTargetExtent ? @@ -62,13 +44,37 @@ class ReprojImage extends ImageBase { const errorThresholdInPixels = ERROR_THRESHOLD; + const triangulation = new Triangulation( + sourceProj, targetProj, limitedTargetExtent, maxSourceExtent, + sourceResolution * errorThresholdInPixels); + + const sourceExtent = triangulation.calculateSourceExtent(); + const sourceImage = getImageFunction(sourceExtent, sourceResolution, pixelRatio); + let state = ImageState.LOADED; + if (sourceImage) { + state = ImageState.IDLE; + } + const sourcePixelRatio = sourceImage ? sourceImage.getPixelRatio() : 1; + + super(targetExtent, targetResolution, sourcePixelRatio, state); + + /** + * @private + * @type {module:ol/proj/Projection} + */ + this.targetProj_ = targetProj; + + /** + * @private + * @type {module:ol/extent~Extent} + */ + this.maxSourceExtent_ = maxSourceExtent; + /** * @private * @type {!module:ol/reproj/Triangulation} */ - this.triangulation_ = new Triangulation( - sourceProj, targetProj, limitedTargetExtent, this.maxSourceExtent_, - sourceResolution * errorThresholdInPixels); + this.triangulation_ = triangulation; /** * @private @@ -82,21 +88,17 @@ class ReprojImage extends ImageBase { */ this.targetExtent_ = targetExtent; - const sourceExtent = this.triangulation_.calculateSourceExtent(); - /** * @private * @type {module:ol/ImageBase} */ - this.sourceImage_ = - getImageFunction(sourceExtent, sourceResolution, pixelRatio); + this.sourceImage_ = sourceImage; /** * @private * @type {number} */ - this.sourcePixelRatio_ = - this.sourceImage_ ? this.sourceImage_.getPixelRatio() : 1; + this.sourcePixelRatio_ = sourcePixelRatio; /** * @private @@ -109,15 +111,6 @@ class ReprojImage extends ImageBase { * @type {?module:ol/events~EventsKey} */ this.sourceListenerKey_ = null; - - - let state = ImageState.LOADED; - - if (this.sourceImage_) { - state = ImageState.IDLE; - } - - super(targetExtent, targetResolution, this.sourcePixelRatio_, state); } /** diff --git a/src/ol/source/WMTS.js b/src/ol/source/WMTS.js index fe48d62a22..1a580b511f 100644 --- a/src/ol/source/WMTS.js +++ b/src/ol/source/WMTS.js @@ -69,6 +69,38 @@ class WMTS extends TileImage { // TODO: add support for TileMatrixLimits + const requestEncoding = options.requestEncoding !== undefined ? + /** @type {module:ol/source/WMTSRequestEncoding} */ (options.requestEncoding) : + WMTSRequestEncoding.KVP; + + // FIXME: should we create a default tileGrid? + // we could issue a getCapabilities xhr to retrieve missing configuration + const tileGrid = options.tileGrid; + + let urls = options.urls; + if (urls === undefined && options.url !== undefined) { + urls = expandUrl(options.url); + } + + const tileUrlFunction = (urls && urls.length > 0) ? + createFromTileUrlFunctions(urls.map(createFromWMTSTemplate)) : nullTileUrlFunction; + + super({ + attributions: options.attributions, + cacheSize: options.cacheSize, + crossOrigin: options.crossOrigin, + projection: options.projection, + reprojectionErrorThreshold: options.reprojectionErrorThreshold, + tileClass: options.tileClass, + tileGrid: tileGrid, + tileLoadFunction: options.tileLoadFunction, + tilePixelRatio: options.tilePixelRatio, + tileUrlFunction: tileUrlFunction, + urls: urls, + wrapX: options.wrapX !== undefined ? options.wrapX : false, + transition: options.transition + }); + /** * @private * @type {string} @@ -105,11 +137,6 @@ class WMTS extends TileImage { */ this.style_ = options.style; - let urls = options.urls; - if (urls === undefined && options.url !== undefined) { - urls = expandUrl(options.url); - } - // FIXME: should we guess this requestEncoding from options.url(s) // structure? that would mean KVP only if a template is not provided. @@ -117,101 +144,7 @@ class WMTS extends TileImage { * @private * @type {module:ol/source/WMTSRequestEncoding} */ - this.requestEncoding_ = options.requestEncoding !== undefined ? - /** @type {module:ol/source/WMTSRequestEncoding} */ (options.requestEncoding) : - WMTSRequestEncoding.KVP; - - const requestEncoding = this.requestEncoding_; - - // FIXME: should we create a default tileGrid? - // we could issue a getCapabilities xhr to retrieve missing configuration - const tileGrid = options.tileGrid; - - // context property names are lower case to allow for a case insensitive - // replacement as some services use different naming conventions - const context = { - 'layer': this.layer_, - 'style': this.style_, - 'tilematrixset': this.matrixSet_ - }; - - if (requestEncoding == WMTSRequestEncoding.KVP) { - assign(context, { - 'Service': 'WMTS', - 'Request': 'GetTile', - 'Version': this.version_, - 'Format': this.format_ - }); - } - - const dimensions = this.dimensions_; - - /** - * @param {string} template Template. - * @return {module:ol/Tile~UrlFunction} Tile URL function. - * @private - */ - this.createFromWMTSTemplate_ = function(template) { - - // TODO: we may want to create our own appendParams function so that params - // order conforms to wmts spec guidance, and so that we can avoid to escape - // special template params - - template = (requestEncoding == WMTSRequestEncoding.KVP) ? - appendParams(template, context) : - template.replace(/\{(\w+?)\}/g, function(m, p) { - return (p.toLowerCase() in context) ? context[p.toLowerCase()] : m; - }); - - return ( - /** - * @param {module:ol/tilecoord~TileCoord} tileCoord Tile coordinate. - * @param {number} pixelRatio Pixel ratio. - * @param {module:ol/proj/Projection} projection Projection. - * @return {string|undefined} Tile URL. - */ - function(tileCoord, pixelRatio, projection) { - if (!tileCoord) { - return undefined; - } else { - const localContext = { - 'TileMatrix': tileGrid.getMatrixId(tileCoord[0]), - 'TileCol': tileCoord[1], - 'TileRow': -tileCoord[2] - 1 - }; - assign(localContext, dimensions); - let url = template; - if (requestEncoding == WMTSRequestEncoding.KVP) { - url = appendParams(url, localContext); - } else { - url = url.replace(/\{(\w+?)\}/g, function(m, p) { - return localContext[p]; - }); - } - return url; - } - } - ); - }; - - const tileUrlFunction = (urls && urls.length > 0) ? - createFromTileUrlFunctions(urls.map(this.createFromWMTSTemplate_)) : nullTileUrlFunction; - - super({ - attributions: options.attributions, - cacheSize: options.cacheSize, - crossOrigin: options.crossOrigin, - projection: options.projection, - reprojectionErrorThreshold: options.reprojectionErrorThreshold, - tileClass: options.tileClass, - tileGrid: tileGrid, - tileLoadFunction: options.tileLoadFunction, - tilePixelRatio: options.tilePixelRatio, - tileUrlFunction: tileUrlFunction, - urls: urls, - wrapX: options.wrapX !== undefined ? options.wrapX : false, - transition: options.transition - }); + this.requestEncoding_ = requestEncoding; this.setKey(this.getKeyForDimensions_()); @@ -522,4 +455,71 @@ export function optionsFromCapabilities(wmtsCap, config) { }; } +/** + * @param {string} template Template. + * @return {module:ol/Tile~UrlFunction} Tile URL function. + * @this {module:ol/source/WMTS} + */ +function createFromWMTSTemplate(template) { + const requestEncoding = this.requestEncoding_; + + // context property names are lower case to allow for a case insensitive + // replacement as some services use different naming conventions + const context = { + 'layer': this.layer_, + 'style': this.style_, + 'tilematrixset': this.matrixSet_ + }; + + if (requestEncoding == WMTSRequestEncoding.KVP) { + assign(context, { + 'Service': 'WMTS', + 'Request': 'GetTile', + 'Version': this.version_, + 'Format': this.format_ + }); + } + + // TODO: we may want to create our own appendParams function so that params + // order conforms to wmts spec guidance, and so that we can avoid to escape + // special template params + + template = (requestEncoding == WMTSRequestEncoding.KVP) ? + appendParams(template, context) : + template.replace(/\{(\w+?)\}/g, function(m, p) { + return (p.toLowerCase() in context) ? context[p.toLowerCase()] : m; + }); + + return ( + /** + * @param {module:ol/tilecoord~TileCoord} tileCoord Tile coordinate. + * @param {number} pixelRatio Pixel ratio. + * @param {module:ol/proj/Projection} projection Projection. + * @return {string|undefined} Tile URL. + */ + function(tileCoord, pixelRatio, projection) { + if (!tileCoord) { + return undefined; + } else { + const localContext = { + 'TileMatrix': this.tileGrid.getMatrixId(tileCoord[0]), + 'TileCol': tileCoord[1], + 'TileRow': -tileCoord[2] - 1 + }; + assign(localContext, this.dimensions_); + let url = template; + if (requestEncoding == WMTSRequestEncoding.KVP) { + url = appendParams(url, localContext); + } else { + url = url.replace(/\{(\w+?)\}/g, function(m, p) { + return localContext[p]; + }); + } + return url; + } + } + ); +} + + export default WMTS; From d6190a0cd781c25a7d1eb34a16b6a7b1d0403cd8 Mon Sep 17 00:00:00 2001 From: ahocevar Date: Wed, 18 Jul 2018 12:15:02 +0200 Subject: [PATCH 094/107] Remove accidently added .only() --- test/spec/ol/renderer/canvas/vectorlayer.test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/spec/ol/renderer/canvas/vectorlayer.test.js b/test/spec/ol/renderer/canvas/vectorlayer.test.js index b6d9ecfa0b..2b3df9d4b2 100644 --- a/test/spec/ol/renderer/canvas/vectorlayer.test.js +++ b/test/spec/ol/renderer/canvas/vectorlayer.test.js @@ -296,7 +296,7 @@ describe('ol.renderer.canvas.VectorLayer', function() { expect(renderer.replayGroupChanged).to.be(false); }); - it.only('dispatches a render event when rendering to own context', function(done) { + it('dispatches a render event when rendering to own context', function(done) { const layer = renderer.getLayer(); layer.getSource().addFeature(new Feature(new Point([0, 0]))); layer.once('render', function() { From 87a3510c830014ee103411b14552bde2ce12d76a Mon Sep 17 00:00:00 2001 From: ahocevar Date: Wed, 18 Jul 2018 12:19:15 +0200 Subject: [PATCH 095/107] Clean up super calls --- src/ol/style/RegularShape.js | 39 ++++++++++++++++++------------------ 1 file changed, 19 insertions(+), 20 deletions(-) diff --git a/src/ol/style/RegularShape.js b/src/ol/style/RegularShape.js index 34f7ee4853..eddf889e1e 100644 --- a/src/ol/style/RegularShape.js +++ b/src/ol/style/RegularShape.js @@ -57,7 +57,25 @@ class RegularShape extends ImageStyle { * @api */ constructor(options) { - super(); + /** + * @type {boolean} + */ + const snapToPixel = options.snapToPixel !== undefined ? + options.snapToPixel : true; + + /** + * @type {boolean} + */ + const rotateWithView = options.rotateWithView !== undefined ? + options.rotateWithView : false; + + super({ + opacity: 1, + rotateWithView: rotateWithView, + rotation: options.rotation !== undefined ? options.rotation : 0, + scale: 1, + snapToPixel: snapToPixel + }); /** * @private @@ -152,25 +170,6 @@ class RegularShape extends ImageStyle { this.render_(this.atlasManager_); - /** - * @type {boolean} - */ - const snapToPixel = options.snapToPixel !== undefined ? - options.snapToPixel : true; - - /** - * @type {boolean} - */ - const rotateWithView = options.rotateWithView !== undefined ? - options.rotateWithView : false; - - super({ - opacity: 1, - rotateWithView: rotateWithView, - rotation: options.rotation !== undefined ? options.rotation : 0, - scale: 1, - snapToPixel: snapToPixel - }); } /** From b813937991706ea6e340827570c58bde1a291866 Mon Sep 17 00:00:00 2001 From: ahocevar Date: Wed, 18 Jul 2018 12:19:33 +0200 Subject: [PATCH 096/107] Access the correct element --- test/spec/ol/control/scaleline.test.js | 62 +++++++++++++------------- 1 file changed, 31 insertions(+), 31 deletions(-) diff --git a/test/spec/ol/control/scaleline.test.js b/test/spec/ol/control/scaleline.test.js index 5921fb380a..ea3e2f75b3 100644 --- a/test/spec/ol/control/scaleline.test.js +++ b/test/spec/ol/control/scaleline.test.js @@ -201,7 +201,7 @@ describe('ol.control.ScaleLine', function() { zoom: 0 })); map.once('postrender', function() { - metricHtml = ctrl.element_.innerHTML; + metricHtml = ctrl.element.innerHTML; done(); }); }); @@ -215,25 +215,25 @@ describe('ol.control.ScaleLine', function() { }); it('renders a different scaleline for "nautical"', function() { ctrl.setUnits('nautical'); - nauticalHtml = ctrl.element_.innerHTML; + nauticalHtml = ctrl.element.innerHTML; expect(nauticalHtml).to.not.be(metricHtml); }); it('renders a different scaleline for "degrees"', function() { ctrl.setUnits('degrees'); - degreesHtml = ctrl.element_.innerHTML; + degreesHtml = ctrl.element.innerHTML; expect(degreesHtml).to.not.be(metricHtml); expect(degreesHtml).to.not.be(nauticalHtml); }); it('renders a different scaleline for "imperial"', function() { ctrl.setUnits('imperial'); - imperialHtml = ctrl.element_.innerHTML; + imperialHtml = ctrl.element.innerHTML; expect(imperialHtml).to.not.be(metricHtml); expect(imperialHtml).to.not.be(nauticalHtml); expect(imperialHtml).to.not.be(degreesHtml); }); it('renders a different scaleline for "us"', function() { ctrl.setUnits('us'); - usHtml = ctrl.element_.innerHTML; + usHtml = ctrl.element.innerHTML; expect(usHtml).to.not.be(metricHtml); expect(usHtml).to.not.be(nauticalHtml); expect(usHtml).to.not.be(degreesHtml); @@ -252,14 +252,14 @@ describe('ol.control.ScaleLine', function() { projection: 'EPSG:3857' })); map.renderSync(); - const innerHtml3857 = ctrl.element_.innerHTML; + const innerHtml3857 = ctrl.element.innerHTML; map.setView(new View({ center: [7, 52], zoom: 2, projection: 'EPSG:4326' })); map.renderSync(); - const innerHtml4326 = ctrl.element_.innerHTML; + const innerHtml4326 = ctrl.element.innerHTML; expect(innerHtml4326).to.not.be(innerHtml3857); }); @@ -281,16 +281,16 @@ describe('ol.control.ScaleLine', function() { map.renderSync(); ctrl.setUnits('metric'); - expect(ctrl.element_.innerText).to.be('100 m'); + expect(ctrl.element.innerText).to.be('100 m'); ctrl.setUnits('imperial'); - expect(ctrl.element_.innerText).to.be('500 ft'); + expect(ctrl.element.innerText).to.be('500 ft'); ctrl.setUnits('nautical'); - expect(ctrl.element_.innerText).to.be('0.05 nm'); + expect(ctrl.element.innerText).to.be('0.05 nm'); ctrl.setUnits('us'); - expect(ctrl.element_.innerText).to.be('500 ft'); + expect(ctrl.element.innerText).to.be('500 ft'); map.setView(new View({ @@ -309,16 +309,16 @@ describe('ol.control.ScaleLine', function() { map.renderSync(); ctrl.setUnits('metric'); - expect(ctrl.element_.innerText).to.be('100 mm'); + expect(ctrl.element.innerText).to.be('100 mm'); ctrl.setUnits('imperial'); - expect(ctrl.element_.innerText).to.be('5 in'); + expect(ctrl.element.innerText).to.be('5 in'); ctrl.setUnits('nautical'); - expect(ctrl.element_.innerText).to.be('0.00005 nm'); + expect(ctrl.element.innerText).to.be('0.00005 nm'); ctrl.setUnits('us'); - expect(ctrl.element_.innerText).to.be('5 in'); + expect(ctrl.element.innerText).to.be('5 in'); }); it('Metric display works with Geographic (EPSG:4326) projection', function() { @@ -330,10 +330,10 @@ describe('ol.control.ScaleLine', function() { projection: 'EPSG:4326' })); map.renderSync(); - expect(ctrl.element_.innerText).to.be('10000 km'); + expect(ctrl.element.innerText).to.be('10000 km'); map.getView().setZoom(28); /* max zoom */ map.renderSync(); - expect(ctrl.element_.innerText).to.be('50 mm'); + expect(ctrl.element.innerText).to.be('50 mm'); }); }); @@ -348,10 +348,10 @@ describe('ol.control.ScaleLine', function() { projection: 'EPSG:4326' })); map.renderSync(); - const innerHtml0 = ctrl.element_.innerHTML; + const innerHtml0 = ctrl.element.innerHTML; map.getView().setCenter([7, 52]); map.renderSync(); - const innerHtml52 = ctrl.element_.innerHTML; + const innerHtml52 = ctrl.element.innerHTML; expect(innerHtml0).to.not.be(innerHtml52); }); @@ -366,10 +366,10 @@ describe('ol.control.ScaleLine', function() { projection: 'EPSG:4326' })); map.renderSync(); - const innerHtml0 = ctrl.element_.innerHTML; + const innerHtml0 = ctrl.element.innerHTML; map.getView().setCenter([7, 52]); map.renderSync(); - const innerHtml52 = ctrl.element_.innerHTML; + const innerHtml52 = ctrl.element.innerHTML; expect(innerHtml0).to.be(innerHtml52); }); @@ -427,11 +427,11 @@ describe('ol.control.ScaleLine', function() { it('metric: is rendered differently for different zoomlevels', function() { ctrl.setUnits('metric'); map.renderSync(); - renderedHtmls[ctrl.element_.innerHTML] = true; + renderedHtmls[ctrl.element.innerHTML] = true; while (--currentZoom >= 0) { mapView.setZoom(currentZoom); map.renderSync(); - const currentHtml = ctrl.element_.innerHTML; + const currentHtml = ctrl.element.innerHTML; expect(currentHtml in renderedHtmls).to.be(false); renderedHtmls[currentHtml] = true; @@ -442,11 +442,11 @@ describe('ol.control.ScaleLine', function() { it('degrees: is rendered differently for different zoomlevels', function() { ctrl.setUnits('degrees'); map.renderSync(); - renderedHtmls[ctrl.element_.innerHTML] = true; + renderedHtmls[ctrl.element.innerHTML] = true; while (--currentZoom >= 0) { mapView.setZoom(currentZoom); map.renderSync(); - const currentHtml = ctrl.element_.innerHTML; + const currentHtml = ctrl.element.innerHTML; expect(currentHtml in renderedHtmls).to.be(false); renderedHtmls[currentHtml] = true; } @@ -454,11 +454,11 @@ describe('ol.control.ScaleLine', function() { it('imperial: is rendered differently for different zoomlevels', function() { ctrl.setUnits('imperial'); map.renderSync(); - renderedHtmls[ctrl.element_.innerHTML] = true; + renderedHtmls[ctrl.element.innerHTML] = true; while (--currentZoom >= 0) { mapView.setZoom(currentZoom); map.renderSync(); - const currentHtml = ctrl.element_.innerHTML; + const currentHtml = ctrl.element.innerHTML; expect(currentHtml in renderedHtmls).to.be(false); renderedHtmls[currentHtml] = true; @@ -469,11 +469,11 @@ describe('ol.control.ScaleLine', function() { it('nautical: is rendered differently for different zoomlevels', function() { ctrl.setUnits('nautical'); map.renderSync(); - renderedHtmls[ctrl.element_.innerHTML] = true; + renderedHtmls[ctrl.element.innerHTML] = true; while (--currentZoom >= 0) { mapView.setZoom(currentZoom); map.renderSync(); - const currentHtml = ctrl.element_.innerHTML; + const currentHtml = ctrl.element.innerHTML; expect(currentHtml in renderedHtmls).to.be(false); renderedHtmls[currentHtml] = true; } @@ -481,11 +481,11 @@ describe('ol.control.ScaleLine', function() { it('us: is rendered differently for different zoomlevels', function() { ctrl.setUnits('us'); map.renderSync(); - renderedHtmls[ctrl.element_.innerHTML] = true; + renderedHtmls[ctrl.element.innerHTML] = true; while (--currentZoom >= 0) { mapView.setZoom(currentZoom); map.renderSync(); - const currentHtml = ctrl.element_.innerHTML; + const currentHtml = ctrl.element.innerHTML; expect(currentHtml in renderedHtmls).to.be(false); renderedHtmls[currentHtml] = true; } From 4adab517686c13e2cab34b20da8cd1cd94424e90 Mon Sep 17 00:00:00 2001 From: ahocevar Date: Wed, 18 Jul 2018 12:21:42 +0200 Subject: [PATCH 097/107] stopPropagation and preventDefault are now separate functions --- test/spec/ol/events/event.test.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/spec/ol/events/event.test.js b/test/spec/ol/events/event.test.js index c308dedb88..a433fde86f 100644 --- a/test/spec/ol/events/event.test.js +++ b/test/spec/ol/events/event.test.js @@ -19,9 +19,9 @@ describe('ol.events.Event', function() { event.preventDefault(); expect(event.propagationStopped).to.be(true); }); - it('is the same as #stopPropagation', function() { + it('does the same as #stopPropagation', function() { const event = new Event('foo'); - expect(event.stopPropagation).to.equal(event.preventDefault); + expect(event.stopPropagation()).to.equal(event.preventDefault()); }); }); From 95533bef66024180db4c1988a0607740a350036a Mon Sep 17 00:00:00 2001 From: ahocevar Date: Wed, 18 Jul 2018 15:40:39 +0200 Subject: [PATCH 098/107] Fix WKT geometry type lookup and tests --- src/ol/format/WKT.js | 33 +++++++++++++++++++++------------ test/spec/ol/format/wkt.test.js | 8 +++++++- 2 files changed, 28 insertions(+), 13 deletions(-) diff --git a/src/ol/format/WKT.js b/src/ol/format/WKT.js index 463ec6ee2d..236c718f5e 100644 --- a/src/ol/format/WKT.js +++ b/src/ol/format/WKT.js @@ -83,6 +83,15 @@ const TokenType = { EOF: 6 }; +/** + * @const + * @type {Object.} + */ +const WKTGeometryType = {}; +for (const type in GeometryType) { + WKTGeometryType[type] = GeometryType[type].toUpperCase(); +} + /** * Class to tokenize a WKT string. @@ -533,7 +542,7 @@ class Parser { if (this.match(TokenType.TEXT)) { const geomType = token.value; this.layout_ = this.parseGeometryLayout_(); - if (geomType == GeometryType.GEOMETRY_COLLECTION.toUpperCase()) { + if (geomType == 'GEOMETRYCOLLECTION') { const geometries = this.parseGeometryCollectionText_(); return new GeometryCollection(geometries); } else { @@ -544,28 +553,28 @@ class Parser { let coordinates; switch (geomType) { - case GeometryType.POINT: { + case 'POINT': { coordinates = this.parsePointText_(); break; } - case GeometryType.LINESTRING: { + case 'LINESTRING': { coordinates = this.parseLineStringText_(); break; } - case GeometryType.POLYGON: { - coordinates = Parser.prototype.parsePolygonText_(); + case 'POLYGON': { + coordinates = this.parsePolygonText_(); break; } - case GeometryType.MULTIPOINT: { - coordinates = Parser.prototype.parseMultiPointText_(); + case 'MULTIPOINT': { + coordinates = this.parseMultiPointText_(); break; } - case GeometryType.MULTILINESTRING: { - coordinates = Parser.prototype.parseMultiLineStringText_(); + case 'MULTILINESTRING': { + coordinates = this.parseMultiLineStringText_(); break; } - case GeometryType.MULTIPOLYGON: { - coordinates = Parser.prototype.parseMultiPolygonText_(); + case 'MULTIPOLYGON': { + coordinates = this.parseMultiPolygonText_(); break; } default: { @@ -574,7 +583,7 @@ class Parser { } if (!coordinates) { - if (ctor === GeometryConstructor[GeometryType.POINT]) { + if (ctor === GeometryConstructor['POINT']) { coordinates = [NaN, NaN]; } else { coordinates = []; diff --git a/test/spec/ol/format/wkt.test.js b/test/spec/ol/format/wkt.test.js index da8e8dca8e..0f1f376059 100644 --- a/test/spec/ol/format/wkt.test.js +++ b/test/spec/ol/format/wkt.test.js @@ -710,8 +710,14 @@ describe('ol.format.WKT', function() { }); it('Empty geometries read / written correctly', function() { + const wkt = 'POINT EMPTY'; + const geom = format.readGeometry(wkt); + const coordinates = geom.getCoordinates(); + expect(coordinates.length).to.be(2); + expect(isNaN(coordinates[0])).to.be(true); + expect(isNaN(coordinates[1])).to.be(true); const wkts = [ - 'POINT', 'LINESTRING', 'POLYGON', 'MULTIPOINT', 'MULTILINESTRING', 'MULTIPOLYGON' + 'LINESTRING', 'POLYGON', 'MULTIPOINT', 'MULTILINESTRING', 'MULTIPOLYGON' ]; for (let i = 0, ii = wkts.length; i < ii; ++i) { const wkt = wkts[i] + ' EMPTY'; From 6f32d1a15b22b13fe184eeab35515bea5aa19841 Mon Sep 17 00:00:00 2001 From: ahocevar Date: Wed, 18 Jul 2018 15:40:53 +0200 Subject: [PATCH 099/107] Deprecate ol/inherits --- src/ol/util.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/ol/util.js b/src/ol/util.js index 07395da368..f5966f1c93 100644 --- a/src/ol/util.js +++ b/src/ol/util.js @@ -22,6 +22,7 @@ * @param {!Function} childCtor Child constructor. * @param {!Function} parentCtor Parent constructor. * @function module:ol.inherits + * @deprecated * @api */ export function inherits(childCtor, parentCtor) { From e52cabcb3115ab10f5db874ec6d010a22306bb8c Mon Sep 17 00:00:00 2001 From: ahocevar Date: Wed, 18 Jul 2018 15:41:13 +0200 Subject: [PATCH 100/107] Fix DragZoom tests --- test/spec/ol/interaction/dragzoom.test.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/spec/ol/interaction/dragzoom.test.js b/test/spec/ol/interaction/dragzoom.test.js index 8a5c2447f5..de9f3ec6af 100644 --- a/test/spec/ol/interaction/dragzoom.test.js +++ b/test/spec/ol/interaction/dragzoom.test.js @@ -75,7 +75,7 @@ describe('ol.interaction.DragZoom', function() { box.geometry_ = polygonFromExtent(extent); interaction.box_ = box; - interaction.onBoxEnd(); + interaction.onBoxEnd_(); setTimeout(function() { const view = map.getView(); const center = view.getCenter(); @@ -99,7 +99,7 @@ describe('ol.interaction.DragZoom', function() { map.getView().setResolution(0.25); setTimeout(function() { - interaction.onBoxEnd(); + interaction.onBoxEnd_(); setTimeout(function() { const view = map.getView(); const resolution = view.getResolution(); From 4589345facda0ee4d6592cc677657030a9817d0c Mon Sep 17 00:00:00 2001 From: ahocevar Date: Wed, 18 Jul 2018 15:41:35 +0200 Subject: [PATCH 101/107] Remove leftover this in super call --- src/ol/interaction/Modify.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ol/interaction/Modify.js b/src/ol/interaction/Modify.js index cbcf947c3d..63bb54b79c 100644 --- a/src/ol/interaction/Modify.js +++ b/src/ol/interaction/Modify.js @@ -419,7 +419,7 @@ class Modify extends PointerInteraction { */ setMap(map) { this.overlay_.setMap(map); - super.setMap(this, map); + super.setMap(map); } /** From 9ed3540edf99e6ffe52e62220e99527616fd13b9 Mon Sep 17 00:00:00 2001 From: ahocevar Date: Wed, 18 Jul 2018 16:29:53 +0200 Subject: [PATCH 102/107] Fix this access in WMTS source --- src/ol/source/WMTS.js | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/src/ol/source/WMTS.js b/src/ol/source/WMTS.js index 1a580b511f..0ec7a21a15 100644 --- a/src/ol/source/WMTS.js +++ b/src/ol/source/WMTS.js @@ -82,9 +82,6 @@ class WMTS extends TileImage { urls = expandUrl(options.url); } - const tileUrlFunction = (urls && urls.length > 0) ? - createFromTileUrlFunctions(urls.map(createFromWMTSTemplate)) : nullTileUrlFunction; - super({ attributions: options.attributions, cacheSize: options.cacheSize, @@ -95,7 +92,7 @@ class WMTS extends TileImage { tileGrid: tileGrid, tileLoadFunction: options.tileLoadFunction, tilePixelRatio: options.tilePixelRatio, - tileUrlFunction: tileUrlFunction, + tileUrlFunction: nullTileUrlFunction, urls: urls, wrapX: options.wrapX !== undefined ? options.wrapX : false, transition: options.transition @@ -148,6 +145,10 @@ class WMTS extends TileImage { this.setKey(this.getKeyForDimensions_()); + if (urls && urls.length > 0) { + this.tileUrlFunction = createFromTileUrlFunctions(urls.map(createFromWMTSTemplate.bind(this))); + } + } } @@ -162,7 +163,7 @@ WMTS.prototype.setUrls = function(urls) { const key = urls.join('\n'); this.setTileUrlFunction(this.fixedTileUrlFunction ? this.fixedTileUrlFunction.bind(this) : - createFromTileUrlFunctions(urls.map(this.createFromWMTSTemplate_.bind(this))), key); + createFromTileUrlFunctions(urls.map(createFromWMTSTemplate.bind(this))), key); }; /** @@ -490,6 +491,8 @@ function createFromWMTSTemplate(template) { return (p.toLowerCase() in context) ? context[p.toLowerCase()] : m; }); + const tileGrid = this.tileGrid; + return ( /** * @param {module:ol/tilecoord~TileCoord} tileCoord Tile coordinate. @@ -502,7 +505,7 @@ function createFromWMTSTemplate(template) { return undefined; } else { const localContext = { - 'TileMatrix': this.tileGrid.getMatrixId(tileCoord[0]), + 'TileMatrix': tileGrid.getMatrixId(tileCoord[0]), 'TileCol': tileCoord[1], 'TileRow': -tileCoord[2] - 1 }; From 0a126b620cdca2f1dcec113e2fe8f12ff7631bf1 Mon Sep 17 00:00:00 2001 From: ahocevar Date: Wed, 18 Jul 2018 16:30:09 +0200 Subject: [PATCH 103/107] Use class keyword in tests --- test/spec/ol/interaction/draganddrop.test.js | 15 +++--- .../renderer/canvas/vectortilelayer.test.js | 52 ++++++++++--------- test/spec/ol/source/tile.test.js | 31 ++++++----- 3 files changed, 50 insertions(+), 48 deletions(-) diff --git a/test/spec/ol/interaction/draganddrop.test.js b/test/spec/ol/interaction/draganddrop.test.js index de6dd8cf49..0cd429eeb6 100644 --- a/test/spec/ol/interaction/draganddrop.test.js +++ b/test/spec/ol/interaction/draganddrop.test.js @@ -1,4 +1,3 @@ -import {inherits} from '../../../../src/ol/index.js'; import View from '../../../../src/ol/View.js'; import Event from '../../../../src/ol/events/Event.js'; import EventTarget from '../../../../src/ol/events/EventTarget.js'; @@ -94,14 +93,16 @@ where('FileReader').describe('ol.interaction.DragAndDrop', function() { beforeEach(function() { OrigFileReader = FileReader; - FileReader = function() { - EventTarget.apply(this, arguments); - this.readAsText = function(file) { + class MockFileReader extends EventTarget { + constructor() { + super(...arguments); + } + readAsText(file) { this.result = file; this.dispatchEvent('load'); - }; - }; - inherits(FileReader, EventTarget); + } + } + FileReader = MockFileReader; }); afterEach(function() { diff --git a/test/spec/ol/renderer/canvas/vectortilelayer.test.js b/test/spec/ol/renderer/canvas/vectortilelayer.test.js index 1ddfe46f6f..909ea2989a 100644 --- a/test/spec/ol/renderer/canvas/vectortilelayer.test.js +++ b/test/spec/ol/renderer/canvas/vectortilelayer.test.js @@ -59,14 +59,15 @@ describe('ol.renderer.canvas.VectorTileLayer', function() { feature2 = new Feature(new Point([0, 0])); feature3 = new RenderFeature('Point', [1, -1], []); feature2.setStyle(featureStyle); - const TileClass = function() { - VectorTile.apply(this, arguments); - this.setState(TileState.LOADED); - this.setFeatures([feature1, feature2, feature3]); - this.setProjection(getProjection('EPSG:4326')); - tileCallback(this); - }; - inherits(TileClass, VectorTile); + class TileClass extends VectorTile { + constructor() { + super(...arguments); + this.setState(TileState.LOADED); + this.setFeatures([feature1, feature2, feature3]); + this.setProjection(getProjection('EPSG:4326')); + tileCallback(this); + } + } source = new VectorTileSource({ format: new MVT(), tileClass: TileClass, @@ -291,23 +292,24 @@ describe('ol.renderer.canvas.VectorTileLayer', function() { describe('#forEachFeatureAtCoordinate', function() { let layer, renderer, replayGroup; - const TileClass = function() { - VectorImageTile.apply(this, arguments); - this.extent = [-Infinity, -Infinity, Infinity, Infinity]; - this.setState(TileState.LOADED); - const sourceTile = new VectorTile([0, 0, 0]); - sourceTile.setState(TileState.LOADED); - sourceTile.setProjection(getProjection('EPSG:3857')); - sourceTile.getReplayGroup = function() { - return replayGroup; - }; - const key = sourceTile.tileCoord.toString(); - this.tileKeys = [key]; - this.sourceTiles_ = {}; - this.sourceTiles_[key] = sourceTile; - this.wrappedTileCoord = arguments[0]; - }; - inherits(TileClass, VectorImageTile); + class TileClass extends VectorImageTile { + constructor() { + super(...arguments); + this.extent = [-Infinity, -Infinity, Infinity, Infinity]; + this.setState(TileState.LOADED); + const sourceTile = new VectorTile([0, 0, 0]); + sourceTile.setState(TileState.LOADED); + sourceTile.setProjection(getProjection('EPSG:3857')); + sourceTile.getReplayGroup = function() { + return replayGroup; + }; + const key = sourceTile.tileCoord.toString(); + this.tileKeys = [key]; + this.sourceTiles_ = {}; + this.sourceTiles_[key] = sourceTile; + this.wrappedTileCoord = arguments[0]; + } + } beforeEach(function() { replayGroup = {}; diff --git a/test/spec/ol/source/tile.test.js b/test/spec/ol/source/tile.test.js index 0a46e6dc85..b8b14bbb51 100644 --- a/test/spec/ol/source/tile.test.js +++ b/test/spec/ol/source/tile.test.js @@ -1,4 +1,3 @@ -import {inherits} from '../../../../src/ol/index.js'; import Tile from '../../../../src/ol/Tile.js'; import TileRange from '../../../../src/ol/TileRange.js'; import {get as getProjection} from '../../../../src/ol/proj.js'; @@ -16,24 +15,24 @@ import TileGrid from '../../../../src/ol/tilegrid/TileGrid.js'; * @param {Object.} tileStates Lookup of tile key to * tile state. */ -const MockTile = function(tileStates) { - const tileGrid = new TileGrid({ - resolutions: [360 / 256, 180 / 256, 90 / 256, 45 / 256], - origin: [-180, -180], - tileSize: 256 - }); +class MockTile extends TileSource { + constructor(tileStates) { + const tileGrid = new TileGrid({ + resolutions: [360 / 256, 180 / 256, 90 / 256, 45 / 256], + origin: [-180, -180], + tileSize: 256 + }); - TileSource.call(this, { - projection: getProjection('EPSG:4326'), - tileGrid: tileGrid - }); + super({ + projection: getProjection('EPSG:4326'), + tileGrid: tileGrid + }); - for (const key in tileStates) { - this.tileCache.set(key, new Tile(key.split('/'), tileStates[key])); + for (const key in tileStates) { + this.tileCache.set(key, new Tile(key.split('/'), tileStates[key])); + } } - -}; -inherits(MockTile, TileSource); +} /** From aecb7e1789924de3e6a1778fc01c0f7bbcfa9c4a Mon Sep 17 00:00:00 2001 From: ahocevar Date: Wed, 18 Jul 2018 16:33:35 +0200 Subject: [PATCH 104/107] Remove unused inherits --- test/spec/ol/renderer/canvas/vectortilelayer.test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/spec/ol/renderer/canvas/vectortilelayer.test.js b/test/spec/ol/renderer/canvas/vectortilelayer.test.js index 909ea2989a..8f34848207 100644 --- a/test/spec/ol/renderer/canvas/vectortilelayer.test.js +++ b/test/spec/ol/renderer/canvas/vectortilelayer.test.js @@ -1,4 +1,4 @@ -import {getUid, inherits} from '../../../../../src/ol/index.js'; +import {getUid} from '../../../../../src/ol/index.js'; import {clear} from '../../../../../src/ol/obj.js'; import Feature from '../../../../../src/ol/Feature.js'; import Map from '../../../../../src/ol/Map.js'; From 5124c98fb55be00c80bd26469abf486238e1a218 Mon Sep 17 00:00:00 2001 From: ahocevar Date: Wed, 18 Jul 2018 17:41:22 +0200 Subject: [PATCH 105/107] Default VERSION to 'latest' --- src/ol/util.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ol/util.js b/src/ol/util.js index f5966f1c93..beecbdc9fa 100644 --- a/src/ol/util.js +++ b/src/ol/util.js @@ -53,4 +53,4 @@ export function getUid(obj) { * OpenLayers version. * @type {string} */ -export const VERSION = '5.0.3'; +export const VERSION = 'latest'; From 47c58d8a34dee73342a943aea1c05f90427e609a Mon Sep 17 00:00:00 2001 From: ahocevar Date: Wed, 18 Jul 2018 17:41:42 +0200 Subject: [PATCH 106/107] Transpile sources for testing --- src/ol/AssertionError.js | 2 ++ test/karma.config.js | 12 +++++++++++- test/spec/ol/assertionerror.test.js | 7 +++++++ 3 files changed, 20 insertions(+), 1 deletion(-) diff --git a/src/ol/AssertionError.js b/src/ol/AssertionError.js index e39cc915a6..6e7a2f5b94 100644 --- a/src/ol/AssertionError.js +++ b/src/ol/AssertionError.js @@ -35,6 +35,8 @@ class AssertionError extends Error { */ this.name = 'AssertionError'; + // Re-assign message, see https://github.com/Rich-Harris/buble/issues/40 + this.message = message; } } diff --git a/test/karma.config.js b/test/karma.config.js index 5416708aa8..030c4c0057 100644 --- a/test/karma.config.js +++ b/test/karma.config.js @@ -60,7 +60,17 @@ module.exports = function(karma) { reporters: ['progress'], webpack: { devtool: 'inline-source-map', - mode: 'development' + mode: 'development', + module: { + rules: [ + { + test: /\.js$/, + use: { + loader: 'buble-loader' + } + } + ] + } }, webpackMiddleware: { noInfo: true diff --git a/test/spec/ol/assertionerror.test.js b/test/spec/ol/assertionerror.test.js index 6a6a4febce..528287d3e4 100644 --- a/test/spec/ol/assertionerror.test.js +++ b/test/spec/ol/assertionerror.test.js @@ -22,4 +22,11 @@ describe('ol.AssertionError', function() { const error = new AssertionError(42); expect(error.name).to.be('AssertionError'); }); + + it('is instanceof Error and AssertionError', function() { + const error = new AssertionError(42); + expect(error instanceof Error).to.be(true); + expect(error instanceof AssertionError).to.be(true); + }); + }); From 4cdab30201d079b0b7112c14ec56615e94155692 Mon Sep 17 00:00:00 2001 From: ahocevar Date: Wed, 18 Jul 2018 18:05:02 +0200 Subject: [PATCH 107/107] Replace VERSION correctly in transpiled code --- tasks/prepare-package.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tasks/prepare-package.js b/tasks/prepare-package.js index 0234f5e830..6ffabc748f 100644 --- a/tasks/prepare-package.js +++ b/tasks/prepare-package.js @@ -6,8 +6,8 @@ const buildDir = path.resolve(__dirname, '../build/ol'); // update the version number in util.js const utilPath = path.join(buildDir, 'util.js'); -const versionRegEx = /const VERSION = '(.*)';/g; -const utilSrc = fs.readFileSync(utilPath, 'utf-8').replace(versionRegEx, `const VERSION = '${pkg.version}';`); +const versionRegEx = /var VERSION = '(.*)';/g; +const utilSrc = fs.readFileSync(utilPath, 'utf-8').replace(versionRegEx, `var VERSION = '${pkg.version}';`); fs.writeFileSync(utilPath, utilSrc, 'utf-8'); // write out simplified package.json