diff --git a/CHANGELOG.md b/CHANGELOG.md index 1e36472..a078df5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +## 9.1.0.0 +* Update to [Openlayers 9.1.0](https://github.com/openlayers/openlayers/releases/tag/v9.1.0) +* Removed legacy alias ol.PluggableMap. Use ol.Map instead + ## 7.5.2.0 * Update to [Openlayers 7.5.2](https://github.com/openlayers/openlayers/releases/tag/v7.5.2) * Expose all formats from ol.formats diff --git a/dist/ol-debug.js b/dist/ol-debug.js index 91bc877..c427164 100644 --- a/dist/ol-debug.js +++ b/dist/ol-debug.js @@ -1,108 +1,6 @@ var ol = (function () { 'use strict'; - /** - * @module ol/AssertionError - */ - - /** @type {Object} */ - const messages$1 = { - 1: 'The view center is not defined', - 2: 'The view resolution is not defined', - 3: 'The view rotation is not defined', - 4: '`image` and `src` cannot be provided at the same time', - 5: '`imgSize` must be set when `image` is provided', - 7: '`format` must be set when `url` is set', - 8: 'Unknown `serverType` configured', - 9: '`url` must be configured or set using `#setUrl()`', - 10: 'The default `geometryFunction` can only handle `Point` geometries', - 11: '`options.featureTypes` must be an Array', - 12: '`options.geometryName` must also be provided when `options.bbox` is set', - 13: 'Invalid corner', - 14: 'Invalid color', - 15: 'Tried to get a value for a key that does not exist in the cache', - 16: 'Tried to set a value for a key that is used already', - 17: '`resolutions` must be sorted in descending order', - 18: 'Either `origin` or `origins` must be configured, never both', - 19: 'Number of `tileSizes` and `resolutions` must be equal', - 20: 'Number of `origins` and `resolutions` must be equal', - 22: 'Either `tileSize` or `tileSizes` must be configured, never both', - 24: 'Invalid extent or geometry provided as `geometry`', - 25: 'Cannot fit empty extent provided as `geometry`', - 26: 'Features must have an id set', - 27: 'Features must have an id set', - 28: '`renderMode` must be `"hybrid"` or `"vector"`', - 30: 'The passed `feature` was already added to the source', - 31: 'Tried to enqueue an `element` that was already added to the queue', - 32: 'Transformation matrix cannot be inverted', - 33: 'Invalid units', - 34: 'Invalid geometry layout', - 36: 'Unknown SRS type', - 37: 'Unknown geometry type found', - 38: '`styleMapValue` has an unknown type', - 39: 'Unknown geometry type', - 40: 'Expected `feature` to have a geometry', - 41: 'Expected an `ol/style/Style` or an array of `ol/style/Style.js`', - 42: 'Question unknown, the answer is 42', - 43: 'Expected `layers` to be an array or a `Collection`', - 47: 'Expected `controls` to be an array or an `ol/Collection`', - 48: 'Expected `interactions` to be an array or an `ol/Collection`', - 49: 'Expected `overlays` to be an array or an `ol/Collection`', - 50: '`options.featureTypes` should be an Array', - 51: 'Either `url` or `tileJSON` options must be provided', - 52: 'Unknown `serverType` configured', - 53: 'Unknown `tierSizeCalculation` configured', - 55: 'The {-y} placeholder requires a tile grid with extent', - 56: 'mapBrowserEvent must originate from a pointer event', - 57: 'At least 2 conditions are required', - 59: 'Invalid command found in the PBF', - 60: 'Missing or invalid `size`', - 61: 'Cannot determine IIIF Image API version from provided image information JSON', - 62: 'A `WebGLArrayBuffer` must either be of type `ELEMENT_ARRAY_BUFFER` or `ARRAY_BUFFER`', - 64: 'Layer opacity must be a number', - 66: '`forEachFeatureAtCoordinate` cannot be used on a WebGL layer if the hit detection logic has not been enabled. This is done by providing adequate shaders using the `hitVertexShader` and `hitFragmentShader` properties of `WebGLPointsLayerRenderer`', - 67: 'A layer can only be added to the map once. Use either `layer.setMap()` or `map.addLayer()`, not both', - 68: 'A VectorTile source can only be rendered if it has a projection compatible with the view projection', - 69: '`width` or `height` cannot be provided together with `scale`', - }; - - /** - * Error object thrown when an assertion failed. This is an ECMA-262 Error, - * extended with a `code` property. - * See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error. - */ - class AssertionError extends Error { - /** - * @param {number} code Error code. - */ - constructor(code) { - const message = messages$1[code]; - - super(message); - - /** - * Error code. The meaning of the code can be found on - * 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} - * @deprecated ol/AssertionError and error codes will be removed in v8.0 - * @api - */ - this.code = code; - - /** - * @type {string} - */ - this.name = 'AssertionError'; - - // Re-assign message, see https://github.com/Rich-Harris/buble/issues/40 - this.message = message; - } - } - - var AssertionError$1 = AssertionError; - /** * @module ol/events/Event */ @@ -181,9 +79,9 @@ var ol = (function () { var nsEvents_Event = { __proto__: null, - stopPropagation: stopPropagation, + default: BaseEvent, preventDefault: preventDefault, - 'default': BaseEvent + stopPropagation: stopPropagation }; /** @@ -241,8 +139,6 @@ var ol = (function () { disposeInternal() {} } - var Disposable$1 = Disposable; - /** * @module ol/array */ @@ -256,7 +152,7 @@ var ol = (function () { * @param {Function} [comparator] Comparator function. * @return {number} The index of the item if found, -1 if not. */ - function binarySearch$1(haystack, needle, comparator) { + function binarySearch(haystack, needle, comparator) { let mid, cmp; comparator = comparator || ascending; let low = 0; @@ -415,8 +311,8 @@ var ol = (function () { } /** - * @param {Array|Uint8ClampedArray} arr1 The first array to compare. - * @param {Array|Uint8ClampedArray} arr2 The second array to compare. + * @param {Array|Uint8ClampedArray} arr1 The first array to compare. + * @param {Array|Uint8ClampedArray} arr2 The second array to compare. * @return {boolean} Whether the two arrays are equal. */ function equals$3(arr1, arr2) { @@ -445,7 +341,7 @@ var ol = (function () { return true; } const res = compare(arr[index - 1], currentVal); - return !(res > 0 || (strict && res === 0)); + return !(res > 0 || (res === 0)); }); } @@ -453,6 +349,7 @@ var ol = (function () { * @module ol/functions */ + /** * Always returns true. * @return {boolean} true. @@ -535,7 +432,7 @@ var ol = (function () { /** * Removes all properties from an object. - * @param {Object} object The object to clear. + * @param {Object} object The object to clear. */ function clear$2(object) { for (const property in object) { @@ -579,7 +476,7 @@ var ol = (function () { * more listeners after this one will be called. Same as when the listener * returns false. */ - class Target extends Disposable$1 { + class Target extends Disposable { /** * @param {*} [target] Default event target for dispatched events. */ @@ -594,19 +491,19 @@ var ol = (function () { /** * @private - * @type {Object} + * @type {Object|null} */ this.pendingRemovals_ = null; /** * @private - * @type {Object} + * @type {Object|null} */ this.dispatching_ = null; /** * @private - * @type {Object>} + * @type {Object>|null} */ this.listeners_ = null; } @@ -720,27 +617,29 @@ var ol = (function () { * @param {import("../events.js").Listener} listener Listener. */ removeEventListener(type, listener) { - const listeners = this.listeners_ && this.listeners_[type]; - if (listeners) { - const index = listeners.indexOf(listener); - if (index !== -1) { - if (this.pendingRemovals_ && type in this.pendingRemovals_) { - // make listener a no-op, and remove later in #dispatchEvent() - listeners[index] = VOID; - ++this.pendingRemovals_[type]; - } else { - listeners.splice(index, 1); - if (listeners.length === 0) { - delete this.listeners_[type]; - } + if (!this.listeners_) { + return; + } + const listeners = this.listeners_[type]; + if (!listeners) { + return; + } + const index = listeners.indexOf(listener); + if (index !== -1) { + if (this.pendingRemovals_ && type in this.pendingRemovals_) { + // make listener a no-op, and remove later in #dispatchEvent() + listeners[index] = VOID; + ++this.pendingRemovals_[type]; + } else { + listeners.splice(index, 1); + if (listeners.length === 0) { + delete this.listeners_[type]; } } } } } - var EventTarget = Target; - /** * @module ol/events/EventType */ @@ -930,7 +829,7 @@ var ol = (function () { * @fires import("./events/Event.js").default * @api */ - class Observable extends EventTarget { + class Observable extends Target { constructor() { super(); @@ -1080,8 +979,6 @@ var ol = (function () { } } - var Observable$1 = Observable; - /** * @module ol/util */ @@ -1117,7 +1014,7 @@ var ol = (function () { * OpenLayers version. * @type {string} */ - const VERSION = '7.5.2'; + const VERSION = '9.1.0'; /** * @module ol/Object @@ -1203,7 +1100,7 @@ var ol = (function () { * @fires ObjectEvent * @api */ - class BaseObject extends Observable$1 { + class BaseObject extends Observable { /** * @param {Object} [values] An object with key-value pairs. */ @@ -1233,7 +1130,7 @@ var ol = (function () { /** * @private - * @type {Object} + * @type {Object|null} */ this.values_ = null; @@ -1274,6 +1171,14 @@ var ol = (function () { return (this.values_ && Object.assign({}, this.values_)) || {}; } + /** + * Get an object of all property names and values. + * @return {Object?} Object. + */ + getPropertiesInternal() { + return this.values_; + } + /** * @return {boolean} The object has properties. */ @@ -1378,8 +1283,6 @@ var ol = (function () { } } - var olObject = BaseObject; - /** * @module ol/CollectionEventType */ @@ -1474,7 +1377,7 @@ var ol = (function () { * @template T * @api */ - class Collection extends olObject { + class Collection extends BaseObject { /** * @param {Array} [array] Array. * @param {Options} [options] Collection options. @@ -1606,7 +1509,7 @@ var ol = (function () { this.array_.splice(index, 0, elem); this.updateLength_(); this.dispatchEvent( - new CollectionEvent(CollectionEventType.ADD, elem, index) + new CollectionEvent(CollectionEventType.ADD, elem, index), ); } @@ -1668,7 +1571,7 @@ var ol = (function () { this.dispatchEvent( /** @type {CollectionEvent} */ ( new CollectionEvent(CollectionEventType.REMOVE, prev, index) - ) + ), ); return prev; } @@ -1696,12 +1599,12 @@ var ol = (function () { this.dispatchEvent( /** @type {CollectionEvent} */ ( new CollectionEvent(CollectionEventType.REMOVE, prev, index) - ) + ), ); this.dispatchEvent( /** @type {CollectionEvent} */ ( new CollectionEvent(CollectionEventType.ADD, elem, index) - ) + ), ); } @@ -1720,25 +1623,23 @@ var ol = (function () { assertUnique_(elem, except) { for (let i = 0, ii = this.array_.length; i < ii; ++i) { if (this.array_[i] === elem && i !== except) { - throw new AssertionError$1(58); + throw new Error('Duplicate item added to a unique collection'); } } } } - var Collection$1 = Collection; - /** * @module ol/asserts */ /** * @param {*} assertion Assertion we expected to be truthy. - * @param {number} errorCode Error code. + * @param {string} errorMessage Error message. */ - function assert(assertion, errorCode) { + function assert(assertion, errorMessage) { if (!assertion) { - throw new AssertionError$1(errorCode); + throw new Error(errorMessage); } } @@ -1763,7 +1664,7 @@ var ol = (function () { */ /*** - * @template Geometry + * @template {import("./geom/Geometry.js").default} [Geometry=import("./geom/Geometry.js").default] * @typedef {Object & { geometry?: Geometry }} ObjectWithGeometry */ @@ -1812,7 +1713,7 @@ var ol = (function () { * @api * @template {import("./geom/Geometry.js").default} [Geometry=import("./geom/Geometry.js").default] */ - class Feature extends olObject { + class Feature extends BaseObject { /** * @param {Geometry|ObjectWithGeometry} [geometryOrProperties] * You may pass a Geometry object directly, or an object literal containing @@ -1983,7 +1884,7 @@ var ol = (function () { geometry, EventType.CHANGE, this.handleGeometryChange_, - this + this, ); } this.changed(); @@ -2063,7 +1964,10 @@ var ol = (function () { if (Array.isArray(obj)) { styles = obj; } else { - assert(typeof (/** @type {?} */ (obj).getZIndex) === 'function', 41); // Expected an `import("./style/Style.js").Style` or an array of `import("./style/Style.js").Style` + assert( + typeof (/** @type {?} */ (obj).getZIndex) === 'function', + 'Expected an `ol/style/Style` or an array of `ol/style/Style.js`', + ); const style = /** @type {import("./style/Style.js").default} */ (obj); styles = [style]; } @@ -2071,96 +1975,6 @@ var ol = (function () { return styles; }; } - var Feature$1 = Feature; - - /** - * @module ol/has - */ - - const ua = - typeof navigator !== 'undefined' && typeof navigator.userAgent !== 'undefined' - ? navigator.userAgent.toLowerCase() - : ''; - - /** - * User agent string says we are dealing with Firefox as browser. - * @type {boolean} - */ - const FIREFOX = ua.includes('firefox'); - - /** - * User agent string says we are dealing with Safari as browser. - * @type {boolean} - */ - const SAFARI = ua.includes('safari') && !ua.includes('chrom'); - - /** - * https://bugs.webkit.org/show_bug.cgi?id=237906 - * @type {boolean} - */ - const SAFARI_BUG_237906 = - SAFARI && - (ua.includes('version/15.4') || - /cpu (os|iphone os) 15_4 like mac os x/.test(ua)); - - /** - * User agent string says we are dealing with a WebKit engine. - * @type {boolean} - */ - const WEBKIT = ua.includes('webkit') && !ua.includes('edge'); - - /** - * User agent string says we are dealing with a Mac as platform. - * @type {boolean} - */ - const MAC = ua.includes('macintosh'); - - /** - * The ratio between physical pixels and device-independent pixels - * (dips) on the device (`window.devicePixelRatio`). - * @const - * @type {number} - * @api - */ - const DEVICE_PIXEL_RATIO = - typeof devicePixelRatio !== 'undefined' ? devicePixelRatio : 1; - - /** - * The execution context is a worker with OffscreenCanvas available. - * @const - * @type {boolean} - */ - const WORKER_OFFSCREEN_CANVAS = - typeof WorkerGlobalScope !== 'undefined' && - typeof OffscreenCanvas !== 'undefined' && - self instanceof WorkerGlobalScope; //eslint-disable-line - - /** - * Image.prototype.decode() is supported. - * @type {boolean} - */ - const IMAGE_DECODE = - typeof Image !== 'undefined' && Image.prototype.decode; - - /** - * @type {boolean} - */ - const PASSIVE_EVENT_LISTENERS = (function () { - let passive = false; - try { - const options = Object.defineProperty({}, 'passive', { - get: function () { - passive = true; - }, - }); - - window.addEventListener('_', null, options); - window.removeEventListener('_', null, options); - } catch (error) { - // passive not supported - } - return passive; - })(); /** * @module ol/transform @@ -2316,17 +2130,6 @@ var ol = (function () { return multiply(transform, set(tmp_, x, 0, 0, y, 0, 0)); } - /** - * Creates a scale transform. - * @param {!Transform} target Transform to overwrite. - * @param {number} x Scale factor x. - * @param {number} y Scale factor y. - * @return {!Transform} The scale transform. - */ - function makeScale(target, x, y) { - return set(target, x, 0, 0, y, 0, 0); - } - /** * Applies translation to the given transform. * @param {!Transform} transform Transform. @@ -2372,7 +2175,7 @@ var ol = (function () { */ function makeInverse(target, source) { const det = determinant(source); - assert(det !== 0, 32); // Transformation matrix cannot be inverted + assert(det !== 0, 'Transformation matrix cannot be inverted'); const a = source[0]; const b = source[1]; @@ -2401,10 +2204,9 @@ var ol = (function () { } /** - * @type {HTMLElement} - * @private + * @type {Array} */ - let transformStringDiv; + const matrixPrecision = [1e6, 1e6, 1e6, 1e6, 2, 2]; /** * A rounded string version of the transform. This can be used @@ -2412,15 +2214,17 @@ var ol = (function () { * @param {!Transform} mat Matrix. * @return {string} The transform as a string. */ - function toString$4(mat) { - const transformString = 'matrix(' + mat.join(', ') + ')'; - if (WORKER_OFFSCREEN_CANVAS) { - return transformString; - } - const node = - transformStringDiv || (transformStringDiv = document.createElement('div')); - node.style.transform = transformString; - return node.style.transform; + function toString$3(mat) { + const transformString = + 'matrix(' + + mat + .map( + (value, i) => + Math.round(value * matrixPrecision[i]) / matrixPrecision[i], + ) + .join(', ') + + ')'; + return transformString; } /** @@ -2493,7 +2297,7 @@ var ol = (function () { * @return {Extent} Extent. * @api */ - function buffer$2(extent, value, dest) { + function buffer$1(extent, value, dest) { if (dest) { dest[0] = extent[0] - value; dest[1] = extent[1] - value; @@ -2701,7 +2505,7 @@ var ol = (function () { offset, end, stride, - dest + dest, ) { const extent = createOrUpdateEmpty(dest); return extendFlatCoordinates(extent, flatCoordinates, offset, end, stride); @@ -2816,7 +2620,7 @@ var ol = (function () { flatCoordinates, offset, end, - stride + stride, ) { for (; offset < end; offset += stride) { extendXY(extent, flatCoordinates[offset], flatCoordinates[offset + 1]); @@ -2939,7 +2743,7 @@ var ol = (function () { } else if (corner === 'top-right') { coordinate = getTopRight(extent); } else { - assert(false, 13); // Invalid corner + throw new Error('Invalid corner'); } return coordinate; } @@ -2970,14 +2774,14 @@ var ol = (function () { center, resolution, rotation, - size + size, ); return createOrUpdate$2( Math.min(x0, x1, x2, x3), Math.min(y0, y1, y2, y3), Math.max(x0, x1, x2, x3), Math.max(y0, y1, y2, y3), - dest + dest, ); } @@ -3265,7 +3069,7 @@ var ol = (function () { extent[2] - (width * i) / stops, extent[3], extent[0], - extent[3] - (height * i) / stops + extent[3] - (height * i) / stops, ); } } else { @@ -3307,7 +3111,7 @@ var ol = (function () { ) { const worldWidth = getWidth(projectionExtent); const worldsAway = Math.floor( - (center[0] - projectionExtent[0]) / worldWidth + (center[0] - projectionExtent[0]) / worldWidth, ); const offset = worldsAway * worldWidth; extent[0] -= offset; @@ -3364,8 +3168,10 @@ var ol = (function () { var nsExtent = { __proto__: null, + applyTransform: applyTransform, + approximatelyEquals: approximatelyEquals, boundingExtent: boundingExtent, - buffer: buffer$2, + buffer: buffer$1, clone: clone, closestSquaredDistanceXY: closestSquaredDistanceXY, containsCoordinate: containsCoordinate, @@ -3380,7 +3186,6 @@ var ol = (function () { createOrUpdateFromFlatCoordinates: createOrUpdateFromFlatCoordinates, createOrUpdateFromRings: createOrUpdateFromRings, equals: equals$2, - approximatelyEquals: approximatelyEquals, extend: extend, extendCoordinate: extendCoordinate, extendCoordinates: extendCoordinates, @@ -3395,23 +3200,22 @@ var ol = (function () { getCorner: getCorner, getEnlargedArea: getEnlargedArea, getForViewAndSize: getForViewAndSize, - getRotatedViewport: getRotatedViewport, getHeight: getHeight, - getIntersectionArea: getIntersectionArea, getIntersection: getIntersection, + getIntersectionArea: getIntersectionArea, getMargin: getMargin, + getRotatedViewport: getRotatedViewport, getSize: getSize, getTopLeft: getTopLeft, getTopRight: getTopRight, getWidth: getWidth, intersects: intersects$2, + intersectsSegment: intersectsSegment, isEmpty: isEmpty, returnOrUpdate: returnOrUpdate, scaleFromCenter: scaleFromCenter, - intersectsSegment: intersectsSegment, - applyTransform: applyTransform, - wrapX: wrapX$2, - wrapAndSliceX: wrapAndSliceX + wrapAndSliceX: wrapAndSliceX, + wrapX: wrapX$2 }; /** @@ -3760,7 +3564,7 @@ var ol = (function () { * @const * @type {import("../extent.js").Extent} */ - const EXTENT$2 = [-HALF_SIZE, -HALF_SIZE, HALF_SIZE, HALF_SIZE]; + const EXTENT$1 = [-HALF_SIZE, -HALF_SIZE, HALF_SIZE, HALF_SIZE]; /** * @const @@ -3787,7 +3591,7 @@ var ol = (function () { super({ code: code, units: 'm', - extent: EXTENT$2, + extent: EXTENT$1, global: true, worldExtent: WORLD_EXTENT, getPointResolution: function (resolution, point) { @@ -3889,7 +3693,7 @@ var ol = (function () { * @const * @type {import("../extent.js").Extent} */ - const EXTENT$1 = [-180, -90, 180, 90]; + const EXTENT = [-180, -90, 180, 90]; /** * @const @@ -3914,11 +3718,11 @@ var ol = (function () { super({ code: code, units: 'degrees', - extent: EXTENT$1, + extent: EXTENT, axisOrientation: axisOrientation, global: true, metersPerUnit: METERS_PER_UNIT, - worldExtent: EXTENT$1, + worldExtent: EXTENT, }); } } @@ -3946,13 +3750,13 @@ var ol = (function () { /** * @type {Object} */ - let cache = {}; + let cache$1 = {}; /** * Clear the projections cache. */ function clear$1() { - cache = {}; + cache$1 = {}; } /** @@ -3960,10 +3764,10 @@ var ol = (function () { * @param {string} code The code for the projection. * @return {import("./Projection.js").default} The projection (if cached). */ - function get$4(code) { + function get$3(code) { return ( - cache[code] || - cache[code.replace(/urn:(x-)?ogc:def:crs:EPSG:(.*:)?(\w+)$/, 'EPSG:$3')] || + cache$1[code] || + cache$1[code.replace(/urn:(x-)?ogc:def:crs:EPSG:(.*:)?(\w+)$/, 'EPSG:$3')] || null ); } @@ -3974,7 +3778,7 @@ var ol = (function () { * @param {import("./Projection.js").default} projection The projection to cache. */ function add$2(code, projection) { - cache[code] = projection; + cache$1[code] = projection; } /** @@ -4017,7 +3821,7 @@ var ol = (function () { * @param {string} destinationCode The code for the destination projection. * @return {import("../proj.js").TransformFunction|undefined} The transform function (if found). */ - function get$3(sourceCode, destinationCode) { + function get$2(sourceCode, destinationCode) { let transform; if (sourceCode in transforms && destinationCode in transforms[sourceCode]) { transform = transforms[sourceCode][destinationCode]; @@ -4087,7 +3891,7 @@ var ol = (function () { * * @param {Array>} mat Augmented matrix (n x n + 1 column) * in row-major order. - * @return {Array} The resulting vector. + * @return {Array|null} The resulting vector. */ function solveLinearSystem(mat) { const n = mat.length; @@ -4701,7 +4505,7 @@ var ol = (function () { ) { sourceExtentWidth = sourceExtentWidth || getWidth(projectionExtent); worldsAway = Math.floor( - (coordinate[0] - projectionExtent[0]) / sourceExtentWidth + (coordinate[0] - projectionExtent[0]) / sourceExtentWidth, ); } return worldsAway; @@ -4714,17 +4518,17 @@ var ol = (function () { closestOnSegment: closestOnSegment, createStringXY: createStringXY, degreesToStringHDMS: degreesToStringHDMS, - format: format, + distance: distance, equals: equals$1, + format: format, + getWorldsAway: getWorldsAway, rotate: rotate$1, scale: scale$2, squaredDistance: squaredDistance, - distance: distance, squaredDistanceToSegment: squaredDistanceToSegment, toStringHDMS: toStringHDMS, toStringXY: toStringXY, - wrapX: wrapX$1, - getWorldsAway: getWorldsAway + wrapX: wrapX$1 }; /** @@ -4979,13 +4783,13 @@ var ol = (function () { const dByR = distance / radius; const lat = Math.asin( Math.sin(lat1) * Math.cos(dByR) + - Math.cos(lat1) * Math.sin(dByR) * Math.cos(bearing) + Math.cos(lat1) * Math.sin(dByR) * Math.cos(bearing), ); const lon = lon1 + Math.atan2( Math.sin(bearing) * Math.sin(dByR) * Math.cos(lat1), - Math.cos(dByR) - Math.sin(lat1) * Math.sin(lat) + Math.cos(dByR) - Math.sin(lat1) * Math.sin(lat), ); return [toDegrees(lon), toDegrees(lat)]; } @@ -4993,9 +4797,9 @@ var ol = (function () { var nsSphere = { __proto__: null, DEFAULT_RADIUS: DEFAULT_RADIUS, + getArea: getArea, getDistance: getDistance, getLength: getLength, - getArea: getArea, offset: offset }; @@ -5022,14 +4826,17 @@ var ol = (function () { */ let level = levels.info; + /** + * @param {...any} args Arguments to log + */ function warn(...args) { - if (level > levels.warn) { - return; - } console.warn(...args); // eslint-disable-line no-console } - function error$1(...args) { + /** + * @param {...any} args Arguments to log + */ + function error(...args) { if (level > levels.error) { return; } @@ -5040,6 +4847,7 @@ var ol = (function () { * @module ol/proj */ + let showCoordinateWarning = true; /** @@ -5111,9 +4919,9 @@ var ol = (function () { * @return {Projection|null} Projection object, or null if not in list. * @api */ - function get$2(projectionLike) { + function get$1(projectionLike) { return typeof projectionLike === 'string' - ? get$4(/** @type {string} */ (projectionLike)) + ? get$3(/** @type {string} */ (projectionLike)) : /** @type {Projection} */ (projectionLike) || null; } @@ -5138,7 +4946,7 @@ var ol = (function () { * @api */ function getPointResolution(projection, resolution, point, units) { - projection = get$2(projection); + projection = get$1(projection); let pointResolution; const getter = projection.getPointResolutionFunc(); if (getter) { @@ -5160,7 +4968,7 @@ var ol = (function () { // average of the width and height. const toEPSG4326 = getTransformFromProjections( projection, - get$2('EPSG:4326') + get$1('EPSG:4326'), ); if (toEPSG4326 === identityTransform && projUnits !== 'degrees') { // no transform is available @@ -5227,7 +5035,7 @@ var ol = (function () { projections1, projections2, forwardTransform, - inverseTransform + inverseTransform, ) { projections1.forEach(function (projection1) { projections2.forEach(function (projection2) { @@ -5252,10 +5060,10 @@ var ol = (function () { */ function createProjection(projection, defaultCode) { if (!projection) { - return get$2(defaultCode); + return get$1(defaultCode); } if (typeof projection === 'string') { - return get$2(projection); + return get$1(projection); } return /** @type {Projection} */ (projection); } @@ -5314,17 +5122,17 @@ var ol = (function () { * @api */ function addCoordinateTransforms(source, destination, forward, inverse) { - const sourceProj = get$2(source); - const destProj = get$2(destination); + const sourceProj = get$1(source); + const destProj = get$1(destination); add$1( sourceProj, destProj, - createTransformFromCoordinateTransform(forward) + createTransformFromCoordinateTransform(forward), ); add$1( destProj, sourceProj, - createTransformFromCoordinateTransform(inverse) + createTransformFromCoordinateTransform(inverse), ); } @@ -5342,7 +5150,7 @@ var ol = (function () { return transform( coordinate, 'EPSG:4326', - projection !== undefined ? projection : 'EPSG:3857' + projection !== undefined ? projection : 'EPSG:3857', ); } @@ -5359,7 +5167,7 @@ var ol = (function () { const lonLat = transform( coordinate, projection !== undefined ? projection : 'EPSG:3857', - 'EPSG:4326' + 'EPSG:4326', ); const lon = lonLat[0]; if (lon < -180 || lon > 180) { @@ -5401,11 +5209,11 @@ var ol = (function () { */ function getTransformFromProjections( sourceProjection, - destinationProjection + destinationProjection, ) { const sourceCode = sourceProjection.getCode(); const destinationCode = destinationProjection.getCode(); - let transformFunc = get$3(sourceCode, destinationCode); + let transformFunc = get$2(sourceCode, destinationCode); if (!transformFunc) { transformFunc = identityTransform; } @@ -5423,8 +5231,8 @@ var ol = (function () { * @api */ function getTransform(source, destination) { - const sourceProjection = get$2(source); - const destinationProjection = get$2(destination); + const sourceProjection = get$1(source); + const destinationProjection = get$1(destination); return getTransformFromProjections(sourceProjection, destinationProjection); } @@ -5475,11 +5283,11 @@ var ol = (function () { function transformWithProjections( point, sourceProjection, - destinationProjection + destinationProjection, ) { const transformFunc = getTransformFromProjections( sourceProjection, - destinationProjection + destinationProjection, ); return transformFunc(point); } @@ -5497,7 +5305,7 @@ var ol = (function () { * @api */ function setUserProjection(projection) { - userProjection = get$2(projection); + userProjection = get$1(projection); } /** @@ -5560,7 +5368,7 @@ var ol = (function () { ) { showCoordinateWarning = false; warn( - 'Call useGeographic() from ol/proj once to work with [longitude, latitude] coordinates.' + 'Call useGeographic() from ol/proj once to work with [longitude, latitude] coordinates.', ); } return coordinate; @@ -5608,10 +5416,10 @@ var ol = (function () { if (!userProjection) { return resolution; } - const sourceUnits = get$2(sourceProjection).getUnits(); - const userUnits = userProjection.getUnits(); - return sourceUnits && userUnits - ? (resolution * METERS_PER_UNIT$1[sourceUnits]) / METERS_PER_UNIT$1[userUnits] + const sourceMetersPerUnit = get$1(sourceProjection).getMetersPerUnit(); + const userMetersPerUnit = userProjection.getMetersPerUnit(); + return sourceMetersPerUnit && userMetersPerUnit + ? (resolution * sourceMetersPerUnit) / userMetersPerUnit : resolution; } @@ -5627,10 +5435,10 @@ var ol = (function () { if (!userProjection) { return resolution; } - const sourceUnits = get$2(destProjection).getUnits(); - const userUnits = userProjection.getUnits(); - return sourceUnits && userUnits - ? (resolution * METERS_PER_UNIT$1[userUnits]) / METERS_PER_UNIT$1[sourceUnits] + const destMetersPerUnit = get$1(destProjection).getMetersPerUnit(); + const userMetersPerUnit = userProjection.getMetersPerUnit(); + return destMetersPerUnit && userMetersPerUnit + ? (resolution * userMetersPerUnit) / destMetersPerUnit : resolution; } @@ -5686,7 +5494,7 @@ var ol = (function () { PROJECTIONS, PROJECTIONS$1, fromEPSG4326, - toEPSG4326 + toEPSG4326, ); } @@ -5696,39 +5504,39 @@ var ol = (function () { __proto__: null, METERS_PER_UNIT: METERS_PER_UNIT$1, Projection: Projection$1, - disableCoordinateWarning: disableCoordinateWarning, - cloneTransform: cloneTransform, - identityTransform: identityTransform, - addProjection: addProjection, - addProjections: addProjections, - get: get$2, - getPointResolution: getPointResolution, + addCommon: addCommon, + addCoordinateTransforms: addCoordinateTransforms, addEquivalentProjections: addEquivalentProjections, addEquivalentTransforms: addEquivalentTransforms, + addProjection: addProjection, + addProjections: addProjections, clearAllProjections: clearAllProjections, + clearUserProjection: clearUserProjection, + cloneTransform: cloneTransform, createProjection: createProjection, + createSafeCoordinateTransform: createSafeCoordinateTransform, createTransformFromCoordinateTransform: createTransformFromCoordinateTransform, - addCoordinateTransforms: addCoordinateTransforms, - fromLonLat: fromLonLat, - toLonLat: toLonLat, + disableCoordinateWarning: disableCoordinateWarning, equivalent: equivalent, - getTransformFromProjections: getTransformFromProjections, + fromLonLat: fromLonLat, + fromUserCoordinate: fromUserCoordinate, + fromUserExtent: fromUserExtent, + fromUserResolution: fromUserResolution, + get: get$1, + getPointResolution: getPointResolution, getTransform: getTransform, - transform: transform, - transformExtent: transformExtent, - transformWithProjections: transformWithProjections, - setUserProjection: setUserProjection, - clearUserProjection: clearUserProjection, + getTransformFromProjections: getTransformFromProjections, getUserProjection: getUserProjection, - useGeographic: useGeographic, + identityTransform: identityTransform, + setUserProjection: setUserProjection, + toLonLat: toLonLat, toUserCoordinate: toUserCoordinate, - fromUserCoordinate: fromUserCoordinate, toUserExtent: toUserExtent, - fromUserExtent: fromUserExtent, toUserResolution: toUserResolution, - fromUserResolution: fromUserResolution, - createSafeCoordinateTransform: createSafeCoordinateTransform, - addCommon: addCommon + transform: transform, + transformExtent: transformExtent, + transformWithProjections: transformWithProjections, + useGeographic: useGeographic }; /** @@ -5750,7 +5558,7 @@ var ol = (function () { end, stride, transform, - dest + dest, ) { dest = dest ? dest : []; let i = 0; @@ -5783,7 +5591,7 @@ var ol = (function () { stride, angle, anchor, - dest + dest, ) { dest = dest ? dest : []; const cos = Math.cos(angle); @@ -5826,7 +5634,7 @@ var ol = (function () { sx, sy, anchor, - dest + dest, ) { dest = dest ? dest : []; const anchorX = anchor[0]; @@ -5864,7 +5672,7 @@ var ol = (function () { stride, deltaX, deltaY, - dest + dest, ) { dest = dest ? dest : []; let i = 0; @@ -5915,7 +5723,7 @@ var ol = (function () { * @abstract * @api */ - class Geometry extends olObject { + class Geometry extends BaseObject { constructor() { super(); @@ -5951,18 +5759,16 @@ var ol = (function () { * @param {import("../proj.js").TransformFunction} [transform] Optional transform function. * @return {Geometry} Simplified geometry. */ - this.simplifyTransformedInternal = memoizeOne(function ( - revision, - squaredTolerance, - transform - ) { - if (!transform) { - return this.getSimplifiedGeometry(squaredTolerance); - } - const clone = this.clone(); - clone.applyTransform(transform); - return clone.getSimplifiedGeometry(squaredTolerance); - }); + this.simplifyTransformedInternal = memoizeOne( + (revision, squaredTolerance, transform) => { + if (!transform) { + return this.getSimplifiedGeometry(squaredTolerance); + } + const clone = this.clone(); + clone.applyTransform(transform); + return clone.getSimplifiedGeometry(squaredTolerance); + }, + ); } /** @@ -5976,7 +5782,7 @@ var ol = (function () { return this.simplifyTransformedInternal( this.getRevision(), squaredTolerance, - transform + transform, ); } @@ -6169,13 +5975,13 @@ var ol = (function () { * string identifier or a {@link module:ol/proj/Projection~Projection} object. * @param {import("../proj.js").ProjectionLike} destination The desired projection. Can be a * string identifier or a {@link module:ol/proj/Projection~Projection} object. - * @return {Geometry} This geometry. Note that original geometry is + * @return {this} This geometry. Note that original geometry is * modified in place. * @api */ transform(source, destination) { /** @type {import("../proj/Projection.js").default} */ - const sourceProj = get$2(source); + const sourceProj = get$1(source); const transformFn = sourceProj.getUnits() == 'tile-pixels' ? function (inCoordinates, outCoordinates, stride) { @@ -6190,7 +5996,7 @@ var ol = (function () { -scale, 0, 0, - 0 + 0, ); transform2D( inCoordinates, @@ -6198,12 +6004,12 @@ var ol = (function () { inCoordinates.length, stride, tmpTransform$1, - outCoordinates + outCoordinates, ); return getTransform(sourceProj, destination)( inCoordinates, outCoordinates, - stride + stride, ); } : getTransform(sourceProj, destination); @@ -6246,7 +6052,7 @@ var ol = (function () { * @protected * @type {Array} */ - this.flatCoordinates = null; + this.flatCoordinates; } /** @@ -6260,7 +6066,7 @@ var ol = (function () { 0, this.flatCoordinates.length, this.stride, - extent + extent, ); } @@ -6295,7 +6101,7 @@ var ol = (function () { */ getLastCoordinate() { return this.flatCoordinates.slice( - this.flatCoordinates.length - this.stride + this.flatCoordinates.length - this.stride, ); } @@ -6386,7 +6192,6 @@ var ol = (function () { * @protected */ setLayout(layout, coordinates, nesting) { - /** @type {number} */ let stride; if (layout) { stride = getStrideForLayout(layout); @@ -6397,7 +6202,7 @@ var ol = (function () { this.stride = 2; return; } - coordinates = /** @type {Array} */ (coordinates[0]); + coordinates = /** @type {Array} */ (coordinates[0]); } stride = coordinates.length; layout = getLayoutForStride(stride); @@ -6440,7 +6245,7 @@ var ol = (function () { stride, angle, anchor, - flatCoordinates + flatCoordinates, ); this.changed(); } @@ -6473,7 +6278,7 @@ var ol = (function () { sx, sy, anchor, - flatCoordinates + flatCoordinates, ); this.changed(); } @@ -6497,7 +6302,7 @@ var ol = (function () { stride, deltaX, deltaY, - flatCoordinates + flatCoordinates, ); this.changed(); } @@ -6554,7 +6359,7 @@ var ol = (function () { flatCoordinates.length, stride, transform, - dest + dest, ); } @@ -6583,7 +6388,7 @@ var ol = (function () { stride, x, y, - closestPoint + closestPoint, ) { const x1 = flatCoordinates[offset1]; const y1 = flatCoordinates[offset1 + 1]; @@ -6601,7 +6406,7 @@ var ol = (function () { closestPoint[i] = lerp$1( flatCoordinates[offset1 + i], flatCoordinates[offset2 + i], - t + t, ); } closestPoint.length = stride; @@ -6655,7 +6460,7 @@ var ol = (function () { offset, ends, stride, - max + max, ) { for (let i = 0, ii = ends.length; i < ii; ++i) { const end = ends[i]; @@ -6678,7 +6483,7 @@ var ol = (function () { offset, endss, stride, - max + max, ) { for (let i = 0, ii = endss.length; i < ii; ++i) { const ends = endss[i]; @@ -6713,7 +6518,7 @@ var ol = (function () { y, closestPoint, minSquaredDistance, - tmpPoint + tmpPoint, ) { if (offset == end) { return minSquaredDistance; @@ -6725,7 +6530,7 @@ var ol = (function () { x, y, flatCoordinates[offset], - flatCoordinates[offset + 1] + flatCoordinates[offset + 1], ); if (squaredDistance < minSquaredDistance) { for (i = 0; i < stride; ++i) { @@ -6746,7 +6551,7 @@ var ol = (function () { stride, x, y, - tmpPoint + tmpPoint, ); squaredDistance = squaredDistance$1(x, y, tmpPoint[0], tmpPoint[1]); if (squaredDistance < minSquaredDistance) { @@ -6773,7 +6578,7 @@ var ol = (function () { ((Math.sqrt(squaredDistance) - Math.sqrt(minSquaredDistance)) / maxDelta) | 0, - 1 + 1, ); } } @@ -6786,7 +6591,7 @@ var ol = (function () { stride, x, y, - tmpPoint + tmpPoint, ); squaredDistance = squaredDistance$1(x, y, tmpPoint[0], tmpPoint[1]); if (squaredDistance < minSquaredDistance) { @@ -6825,7 +6630,7 @@ var ol = (function () { y, closestPoint, minSquaredDistance, - tmpPoint + tmpPoint, ) { tmpPoint = tmpPoint ? tmpPoint : [NaN, NaN]; for (let i = 0, ii = ends.length; i < ii; ++i) { @@ -6841,7 +6646,7 @@ var ol = (function () { y, closestPoint, minSquaredDistance, - tmpPoint + tmpPoint, ); offset = end; } @@ -6873,7 +6678,7 @@ var ol = (function () { y, closestPoint, minSquaredDistance, - tmpPoint + tmpPoint, ) { tmpPoint = tmpPoint ? tmpPoint : [NaN, NaN]; for (let i = 0, ii = endss.length; i < ii; ++i) { @@ -6889,7 +6694,7 @@ var ol = (function () { y, closestPoint, minSquaredDistance, - tmpPoint + tmpPoint, ); offset = ends[ends.length - 1]; } @@ -6925,7 +6730,7 @@ var ol = (function () { flatCoordinates, offset, coordinates, - stride + stride, ) { for (let i = 0, ii = coordinates.length; i < ii; ++i) { const coordinate = coordinates[i]; @@ -6949,7 +6754,7 @@ var ol = (function () { offset, coordinatess, stride, - ends + ends, ) { ends = ends ? ends : []; let i = 0; @@ -6958,7 +6763,7 @@ var ol = (function () { flatCoordinates, offset, coordinatess[j], - stride + stride, ); ends[i++] = end; offset = end; @@ -6980,7 +6785,7 @@ var ol = (function () { offset, coordinatesss, stride, - endss + endss, ) { endss = endss ? endss : []; let i = 0; @@ -6990,7 +6795,7 @@ var ol = (function () { offset, coordinatesss[j], stride, - endss[i] + endss[i], ); if (ends.length === 0) { ends[0] = offset; @@ -7005,6 +6810,32 @@ var ol = (function () { /** * @module ol/geom/flat/simplify */ + // Based on simplify-js https://github.com/mourner/simplify-js + // Copyright (c) 2012, Vladimir Agafonkin + // All rights reserved. + // + // Redistribution and use in source and binary forms, with or without + // modification, are permitted provided that the following conditions are met: + // + // 1. Redistributions of source code must retain the above copyright notice, + // this list of conditions and the following disclaimer. + // + // 2. Redistributions in binary form must reproduce the above copyright + // notice, this list of conditions and the following disclaimer in the + // documentation and/or other materials provided with the distribution. + // + // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + // POSSIBILITY OF SUCH DAMAGE. + /** * @param {Array} flatCoordinates Flat coordinates. @@ -7024,7 +6855,7 @@ var ol = (function () { stride, squaredTolerance, simplifiedFlatCoordinates, - simplifiedOffset + simplifiedOffset, ) { const n = (end - offset) / stride; if (n < 3) { @@ -7100,7 +6931,7 @@ var ol = (function () { squaredTolerance, simplifiedFlatCoordinates, simplifiedOffset, - simplifiedEnds + simplifiedEnds, ) { for (let i = 0, ii = ends.length; i < ii; ++i) { const end = ends[i]; @@ -7111,7 +6942,7 @@ var ol = (function () { stride, squaredTolerance, simplifiedFlatCoordinates, - simplifiedOffset + simplifiedOffset, ); simplifiedEnds.push(simplifiedOffset); offset = end; @@ -7154,7 +6985,7 @@ var ol = (function () { stride, tolerance, simplifiedFlatCoordinates, - simplifiedOffset + simplifiedOffset, ) { // do nothing if the line is empty if (offset == end) { @@ -7248,7 +7079,7 @@ var ol = (function () { tolerance, simplifiedFlatCoordinates, simplifiedOffset, - simplifiedEnds + simplifiedEnds, ) { for (let i = 0, ii = ends.length; i < ii; ++i) { const end = ends[i]; @@ -7259,7 +7090,7 @@ var ol = (function () { stride, tolerance, simplifiedFlatCoordinates, - simplifiedOffset + simplifiedOffset, ); simplifiedEnds.push(simplifiedOffset); offset = end; @@ -7287,10 +7118,11 @@ var ol = (function () { tolerance, simplifiedFlatCoordinates, simplifiedOffset, - simplifiedEndss + simplifiedEndss, ) { for (let i = 0, ii = endss.length; i < ii; ++i) { const ends = endss[i]; + /** @type {Array} */ const simplifiedEnds = []; simplifiedOffset = quantizeArray( flatCoordinates, @@ -7300,7 +7132,7 @@ var ol = (function () { tolerance, simplifiedFlatCoordinates, simplifiedOffset, - simplifiedEnds + simplifiedEnds, ); simplifiedEndss.push(simplifiedEnds); offset = ends[ends.length - 1]; @@ -7325,7 +7157,7 @@ var ol = (function () { offset, end, stride, - coordinates + coordinates, ) { coordinates = coordinates !== undefined ? coordinates : []; let i = 0; @@ -7349,7 +7181,7 @@ var ol = (function () { offset, ends, stride, - coordinatess + coordinatess, ) { coordinatess = coordinatess !== undefined ? coordinatess : []; let i = 0; @@ -7360,7 +7192,7 @@ var ol = (function () { offset, end, stride, - coordinatess[i] + coordinatess[i], ); offset = end; } @@ -7382,7 +7214,7 @@ var ol = (function () { offset, endss, stride, - coordinatesss + coordinatesss, ) { coordinatesss = coordinatesss !== undefined ? coordinatesss : []; let i = 0; @@ -7396,7 +7228,7 @@ var ol = (function () { offset, ends, stride, - coordinatesss[i] + coordinatesss[i], ); offset = ends[ends.length - 1]; } @@ -7498,14 +7330,14 @@ var ol = (function () { if (layout !== undefined && !Array.isArray(coordinates[0])) { this.setFlatCoordinates( layout, - /** @type {Array} */ (coordinates) + /** @type {Array} */ (coordinates), ); } else { this.setCoordinates( /** @type {Array} */ ( coordinates ), - layout + layout, ); } } @@ -7537,8 +7369,8 @@ var ol = (function () { 0, this.flatCoordinates.length, this.stride, - 0 - ) + 0, + ), ); this.maxDeltaRevision_ = this.getRevision(); } @@ -7552,7 +7384,7 @@ var ol = (function () { x, y, closestPoint, - minSquaredDistance + minSquaredDistance, ); } @@ -7566,7 +7398,7 @@ var ol = (function () { this.flatCoordinates, 0, this.flatCoordinates.length, - this.stride + this.stride, ); } @@ -7580,7 +7412,7 @@ var ol = (function () { this.flatCoordinates, 0, this.flatCoordinates.length, - this.stride + this.stride, ); } @@ -7590,6 +7422,7 @@ var ol = (function () { * @protected */ getSimplifiedGeometryInternal(squaredTolerance) { + /** @type {Array} */ const simplifiedFlatCoordinates = []; simplifiedFlatCoordinates.length = douglasPeucker( this.flatCoordinates, @@ -7598,7 +7431,7 @@ var ol = (function () { this.stride, squaredTolerance, simplifiedFlatCoordinates, - 0 + 0, ); return new LinearRing(simplifiedFlatCoordinates, 'XY'); } @@ -7637,7 +7470,7 @@ var ol = (function () { this.flatCoordinates, 0, coordinates, - this.stride + this.stride, ); this.changed(); } @@ -7689,7 +7522,7 @@ var ol = (function () { x, y, flatCoordinates[0], - flatCoordinates[1] + flatCoordinates[1], ); if (squaredDistance < minSquaredDistance) { const stride = this.stride; @@ -7708,7 +7541,7 @@ var ol = (function () { * @api */ getCoordinates() { - return !this.flatCoordinates ? [] : this.flatCoordinates.slice(); + return this.flatCoordinates.slice(); } /** @@ -7753,7 +7586,7 @@ var ol = (function () { this.flatCoordinates, 0, coordinates, - this.stride + this.stride, ); this.changed(); } @@ -7778,7 +7611,7 @@ var ol = (function () { offset, end, stride, - extent + extent, ) { const outside = forEachCorner( extent, @@ -7793,9 +7626,9 @@ var ol = (function () { end, stride, coordinate[0], - coordinate[1] + coordinate[1], ); - } + }, ); return !outside; } @@ -7815,7 +7648,7 @@ var ol = (function () { end, stride, x, - y + y, ) { // https://geomalgorithms.com/a03-_inclusion.html // Copyright 2000 softSurfer, 2012 Dan Sunday @@ -7858,7 +7691,7 @@ var ol = (function () { ends, stride, x, - y + y, ) { if (ends.length === 0) { return false; @@ -7891,7 +7724,7 @@ var ol = (function () { endss, stride, x, - y + y, ) { if (endss.length === 0) { return false; @@ -7930,7 +7763,7 @@ var ol = (function () { stride, flatCenters, flatCentersOffset, - dest + dest, ) { let i, ii, x, x1, x2, y1, y2; const y = flatCenters[flatCentersOffset + 1]; @@ -7996,8 +7829,9 @@ var ol = (function () { offset, endss, stride, - flatCenters + flatCenters, ) { + /** @type {Array} */ let interiorPoints = []; for (let i = 0, ii = endss.length; i < ii; ++i) { const ends = endss[i]; @@ -8008,7 +7842,7 @@ var ol = (function () { stride, flatCenters, 2 * i, - interiorPoints + interiorPoints, ); offset = ends[ends.length - 1]; } @@ -8038,7 +7872,7 @@ var ol = (function () { for (; offset < end; offset += stride) { ret = callback( flatCoordinates.slice(offset - stride, offset), - flatCoordinates.slice(offset, offset + stride) + flatCoordinates.slice(offset, offset + stride), ); if (ret) { return ret; @@ -8064,14 +7898,14 @@ var ol = (function () { offset, end, stride, - extent + extent, ) { const coordinatesExtent = extendFlatCoordinates( createEmpty(), flatCoordinates, offset, end, - stride + stride, ); if (!intersects$2(extent, coordinatesExtent)) { return false; @@ -8098,7 +7932,7 @@ var ol = (function () { */ function (point1, point2) { return intersectsSegment(extent, point1, point2); - } + }, ); } @@ -8115,7 +7949,7 @@ var ol = (function () { offset, ends, stride, - extent + extent, ) { for (let i = 0, ii = ends.length; i < ii; ++i) { if ( @@ -8141,7 +7975,7 @@ var ol = (function () { offset, end, stride, - extent + extent, ) { if (intersectsLineString(flatCoordinates, offset, end, stride, extent)) { return true; @@ -8153,7 +7987,7 @@ var ol = (function () { end, stride, extent[0], - extent[1] + extent[1], ) ) { return true; @@ -8165,7 +7999,7 @@ var ol = (function () { end, stride, extent[0], - extent[3] + extent[3], ) ) { return true; @@ -8177,7 +8011,7 @@ var ol = (function () { end, stride, extent[2], - extent[1] + extent[1], ) ) { return true; @@ -8189,7 +8023,7 @@ var ol = (function () { end, stride, extent[2], - extent[3] + extent[3], ) ) { return true; @@ -8210,7 +8044,7 @@ var ol = (function () { offset, ends, stride, - extent + extent, ) { if (!intersectsLinearRing(flatCoordinates, offset, ends[0], stride, extent)) { return false; @@ -8225,7 +8059,7 @@ var ol = (function () { ends[i - 1], ends[i], stride, - extent + extent, ) ) { if ( @@ -8234,7 +8068,7 @@ var ol = (function () { ends[i - 1], ends[i], stride, - extent + extent, ) ) { return false; @@ -8257,7 +8091,7 @@ var ol = (function () { offset, endss, stride, - extent + extent, ) { for (let i = 0, ii = endss.length; i < ii; ++i) { const ends = endss[i]; @@ -8305,7 +8139,7 @@ var ol = (function () { * @param {number} offset Offset. * @param {number} end End. * @param {number} stride Stride. - * @return {boolean} Is clockwise. + * @return {boolean|undefined} Is clockwise. */ function linearRingIsClockwise(flatCoordinates, offset, end, stride) { // https://stackoverflow.com/q/1165647/clockwise-method#1165943 @@ -8341,7 +8175,7 @@ var ol = (function () { offset, ends, stride, - right + right, ) { right = right !== undefined ? right : false; for (let i = 0, ii = ends.length; i < ii; ++i) { @@ -8350,7 +8184,7 @@ var ol = (function () { flatCoordinates, offset, end, - stride + stride, ); if (i === 0) { if ((right && isClockwise) || (!right && !isClockwise)) { @@ -8384,7 +8218,7 @@ var ol = (function () { offset, endss, stride, - right + right, ) { for (let i = 0, ii = endss.length; i < ii; ++i) { const ends = endss[i]; @@ -8416,7 +8250,7 @@ var ol = (function () { offset, ends, stride, - right + right, ) { right = right !== undefined ? right : false; for (let i = 0, ii = ends.length; i < ii; ++i) { @@ -8425,7 +8259,7 @@ var ol = (function () { flatCoordinates, offset, end, - stride + stride, ); const reverse = i === 0 @@ -8457,7 +8291,7 @@ var ol = (function () { offset, endss, stride, - right + right, ) { for (let i = 0, ii = endss.length; i < ii; ++i) { offset = orientLinearRings( @@ -8465,7 +8299,7 @@ var ol = (function () { offset, endss[i], stride, - right + right, ); } return offset; @@ -8482,10 +8316,15 @@ var ol = (function () { const endss = []; let offset = 0; let prevEndIndex = 0; + let startOrientation; for (let i = 0, ii = ends.length; i < ii; ++i) { const end = ends[i]; // classifies an array of rings into polygons with outer rings and holes - if (!linearRingIsClockwise(flatCoordinates, offset, end, 2)) { + const orientation = linearRingIsClockwise(flatCoordinates, offset, end, 2); + if (startOrientation === undefined) { + startOrientation = orientation; + } + if (orientation === startOrientation) { endss.push(ends.slice(prevEndIndex, i + 1)); } else { if (endss.length === 0) { @@ -8538,7 +8377,7 @@ var ol = (function () { /** * @private - * @type {import("../coordinate.js").Coordinate} + * @type {import("../coordinate.js").Coordinate|null} */ this.flatInteriorPoint_ = null; @@ -8562,14 +8401,14 @@ var ol = (function () { /** * @private - * @type {Array} + * @type {Array|null} */ this.orientedFlatCoordinates_ = null; if (layout !== undefined && ends) { this.setFlatCoordinates( layout, - /** @type {Array} */ (coordinates) + /** @type {Array} */ (coordinates), ); this.ends_ = ends; } else { @@ -8577,7 +8416,7 @@ var ol = (function () { /** @type {Array>} */ ( coordinates ), - layout + layout, ); } } @@ -8606,7 +8445,7 @@ var ol = (function () { const polygon = new Polygon( this.flatCoordinates.slice(), this.layout, - this.ends_.slice() + this.ends_.slice(), ); polygon.applyProperties(this); return polygon; @@ -8630,8 +8469,8 @@ var ol = (function () { 0, this.ends_, this.stride, - 0 - ) + 0, + ), ); this.maxDeltaRevision_ = this.getRevision(); } @@ -8645,7 +8484,7 @@ var ol = (function () { x, y, closestPoint, - minSquaredDistance + minSquaredDistance, ); } @@ -8661,7 +8500,7 @@ var ol = (function () { this.ends_, this.stride, x, - y + y, ); } @@ -8675,7 +8514,7 @@ var ol = (function () { this.getOrientedFlatCoordinates(), 0, this.ends_, - this.stride + this.stride, ); } @@ -8723,11 +8562,13 @@ var ol = (function () { this.ends_, this.stride, flatCenter, - 0 + 0, ); this.flatInteriorPointRevision_ = this.getRevision(); } - return this.flatInteriorPoint_; + return /** @type {import("../coordinate.js").Coordinate} */ ( + this.flatInteriorPoint_ + ); } /** @@ -8768,9 +8609,9 @@ var ol = (function () { return new LinearRing$1( this.flatCoordinates.slice( index === 0 ? 0 : this.ends_[index - 1], - this.ends_[index] + this.ends_[index], ), - this.layout + this.layout, ); } @@ -8789,7 +8630,7 @@ var ol = (function () { const end = ends[i]; const linearRing = new LinearRing$1( flatCoordinates.slice(offset, end), - layout + layout, ); linearRings.push(linearRing); offset = end; @@ -8811,12 +8652,12 @@ var ol = (function () { this.orientedFlatCoordinates_, 0, this.ends_, - this.stride + this.stride, ); } this.orientedRevision_ = this.getRevision(); } - return this.orientedFlatCoordinates_; + return /** @type {Array} */ (this.orientedFlatCoordinates_); } /** @@ -8825,7 +8666,9 @@ var ol = (function () { * @protected */ getSimplifiedGeometryInternal(squaredTolerance) { + /** @type {Array} */ const simplifiedFlatCoordinates = []; + /** @type {Array} */ const simplifiedEnds = []; simplifiedFlatCoordinates.length = quantizeArray( this.flatCoordinates, @@ -8835,7 +8678,7 @@ var ol = (function () { Math.sqrt(squaredTolerance), simplifiedFlatCoordinates, 0, - simplifiedEnds + simplifiedEnds, ); return new Polygon(simplifiedFlatCoordinates, 'XY', simplifiedEnds); } @@ -8861,7 +8704,7 @@ var ol = (function () { 0, this.ends_, this.stride, - extent + extent, ); } @@ -8881,7 +8724,7 @@ var ol = (function () { 0, coordinates, this.stride, - this.ends_ + this.ends_, ); this.flatCoordinates.length = ends.length === 0 ? 0 : ends[ends.length - 1]; this.changed(); @@ -8909,7 +8752,7 @@ var ol = (function () { for (let i = 0; i < n; ++i) { extend$1( flatCoordinates, - offset(center, radius, (2 * Math.PI * i) / n, sphereRadius) + offset(center, radius, (2 * Math.PI * i) / n, sphereRadius), ); } flatCoordinates.push(flatCoordinates[0], flatCoordinates[1]); @@ -8998,10 +8841,10 @@ var ol = (function () { var geom_Polygon = { __proto__: null, - 'default': Polygon$1, circular: circular, - fromExtent: fromExtent, + default: Polygon$1, fromCircle: fromCircle, + fromExtent: fromExtent, makeRegular: makeRegular }; @@ -9115,7 +8958,7 @@ var ol = (function () { * @fires GeolocationError * @api */ - class Geolocation extends olObject { + class Geolocation extends BaseObject { /** * @param {Options} [options] Options. */ @@ -9186,8 +9029,8 @@ var ol = (function () { const projection = this.getProjection(); if (projection) { this.transform_ = getTransformFromProjections( - get$2('EPSG:4326'), - projection + get$1('EPSG:4326'), + projection, ); if (this.position_) { this.set(Property$4.POSITION, this.transform_(this.position_)); @@ -9205,7 +9048,7 @@ var ol = (function () { this.watchId_ = navigator.geolocation.watchPosition( this.positionChange_.bind(this), this.positionError_.bind(this), - this.getTrackingOptions() + this.getTrackingOptions(), ); } else if (!tracking && this.watchId_ !== undefined) { navigator.geolocation.clearWatch(this.watchId_); @@ -9223,15 +9066,15 @@ var ol = (function () { this.set(Property$4.ACCURACY, coords.accuracy); this.set( Property$4.ALTITUDE, - coords.altitude === null ? undefined : coords.altitude + coords.altitude === null ? undefined : coords.altitude, ); this.set( Property$4.ALTITUDE_ACCURACY, - coords.altitudeAccuracy === null ? undefined : coords.altitudeAccuracy + coords.altitudeAccuracy === null ? undefined : coords.altitudeAccuracy, ); this.set( Property$4.HEADING, - coords.heading === null ? undefined : toRadians(coords.heading) + coords.heading === null ? undefined : toRadians(coords.heading), ); if (!this.position_) { this.position_ = [coords.longitude, coords.latitude]; @@ -9385,7 +9228,7 @@ var ol = (function () { * @api */ setProjection(projection) { - this.set(Property$4.PROJECTION, get$2(projection)); + this.set(Property$4.PROJECTION, get$1(projection)); } /** @@ -9412,8 +9255,6 @@ var ol = (function () { } } - var Geolocation$1 = Geolocation; - /** * @module ol/render/EventType */ @@ -9473,2455 +9314,3074 @@ var ol = (function () { */ /** - * @module ol/style/Fill + * @module ol/ImageState */ /** - * @typedef {Object} Options - * @property {import("../color.js").Color|import("../colorlike.js").ColorLike|null} [color=null] A color, gradient or pattern. - * See {@link module:ol/color~Color} and {@link module:ol/colorlike~ColorLike} for possible formats. - * Default null; if null, the Canvas/renderer default black will be used. + * @enum {number} */ + var ImageState = { + IDLE: 0, + LOADING: 1, + LOADED: 2, + ERROR: 3, + EMPTY: 4, + }; /** - * @classdesc - * Set fill style for vector features. - * @api + * RGB space. + * + * @module color-space/rgb */ - class Fill { - /** - * @param {Options} [options] Options. - */ - constructor(options) { - options = options || {}; - /** - * @private - * @type {import("../color.js").Color|import("../colorlike.js").ColorLike|null} - */ - this.color_ = options.color !== undefined ? options.color : null; - } + var rgb = { + name: 'rgb', + min: [0,0,0], + max: [255,255,255], + channel: ['red', 'green', 'blue'], + alias: ['RGB'] + }; - /** - * Clones the style. The color is not cloned if it is an {@link module:ol/colorlike~ColorLike}. - * @return {Fill} The cloned style. - * @api - */ - clone() { - const color = this.getColor(); - return new Fill({ - color: Array.isArray(color) ? color.slice() : color || undefined, - }); - } + /** + * CIE XYZ + * + * @module color-space/xyz + */ - /** - * Get the fill color. - * @return {import("../color.js").Color|import("../colorlike.js").ColorLike|null} Color. - * @api - */ - getColor() { - return this.color_; - } + var xyz = { + name: 'xyz', + min: [0,0,0], + channel: ['X','Y','Z'], + alias: ['XYZ', 'ciexyz', 'cie1931'] + }; - /** - * Set the color. - * - * @param {import("../color.js").Color|import("../colorlike.js").ColorLike|null} color Color. - * @api - */ - setColor(color) { - this.color_ = color; - } - } - var Fill$1 = Fill; + /** + * Whitepoint reference values with observer/illuminant + * + * http://en.wikipedia.org/wiki/Standard_illuminant + */ + xyz.whitepoint = { + //1931 2° + 2: { + //incadescent + A:[109.85, 100, 35.585], + // B:[], + C: [98.074, 100, 118.232], + D50: [96.422, 100, 82.521], + D55: [95.682, 100, 92.149], + //daylight + D65: [95.045592705167, 100, 108.9057750759878], + D75: [94.972, 100, 122.638], + //flourescent + // F1: [], + F2: [99.187, 100, 67.395], + // F3: [], + // F4: [], + // F5: [], + // F6:[], + F7: [95.044, 100, 108.755], + // F8: [], + // F9: [], + // F10: [], + F11: [100.966, 100, 64.370], + // F12: [], + E: [100,100,100] + }, + + //1964 10° + 10: { + //incadescent + A:[111.144, 100, 35.200], + C: [97.285, 100, 116.145], + D50: [96.720, 100, 81.427], + D55: [95.799, 100, 90.926], + //daylight + D65: [94.811, 100, 107.304], + D75: [94.416, 100, 120.641], + //flourescent + F2: [103.280, 100, 69.026], + F7: [95.792, 100, 107.687], + F11: [103.866, 100, 65.627], + E: [100,100,100] + } + }; + /** - * @module ol/geom/flat/interpolate + * Top values are the whitepoint’s top values, default are D65 */ + xyz.max = xyz.whitepoint[2].D65; + /** - * @param {Array} flatCoordinates Flat coordinates. - * @param {number} offset Offset. - * @param {number} end End. - * @param {number} stride Stride. - * @param {number} fraction Fraction. - * @param {Array} [dest] Destination. - * @param {number} [dimension] Destination dimension (default is `2`) - * @return {Array} Destination. + * Transform xyz to rgb + * + * @param {Array} xyz Array of xyz values + * + * @return {Array} RGB values */ - function interpolatePoint( - flatCoordinates, - offset, - end, - stride, - fraction, - dest, - dimension - ) { - let o, t; - const n = (end - offset) / stride; - if (n === 1) { - o = offset; - } else if (n === 2) { - o = offset; - t = fraction; - } else if (n !== 0) { - let x1 = flatCoordinates[offset]; - let y1 = flatCoordinates[offset + 1]; - let length = 0; - const cumulativeLengths = [0]; - for (let i = offset + stride; i < end; i += stride) { - const x2 = flatCoordinates[i]; - const y2 = flatCoordinates[i + 1]; - length += Math.sqrt((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1)); - cumulativeLengths.push(length); - x1 = x2; - y1 = y2; - } - const target = fraction * length; - const index = binarySearch$1(cumulativeLengths, target); - if (index < 0) { - t = - (target - cumulativeLengths[-index - 2]) / - (cumulativeLengths[-index - 1] - cumulativeLengths[-index - 2]); - o = offset + (-index - 2) * stride; - } else { - o = offset + index * stride; - } - } - dimension = dimension > 1 ? dimension : 2; - dest = dest ? dest : new Array(dimension); - for (let i = 0; i < dimension; ++i) { - dest[i] = - o === undefined - ? NaN - : t === undefined - ? flatCoordinates[o + i] - : lerp$1(flatCoordinates[o + i], flatCoordinates[o + stride + i], t); - } - return dest; - } + xyz.rgb = function (_xyz, white) { + //FIXME: make sure we have to divide like this. Probably we have to replace matrix as well then + white = white || xyz.whitepoint[2].E; + + var x = _xyz[0] / white[0], + y = _xyz[1] / white[1], + z = _xyz[2] / white[2], + r, g, b; + + // assume sRGB + // http://www.brucelindbloom.com/index.html?Eqn_RGB_XYZ_Matrix.html + r = (x * 3.240969941904521) + (y * -1.537383177570093) + (z * -0.498610760293); + g = (x * -0.96924363628087) + (y * 1.87596750150772) + (z * 0.041555057407175); + b = (x * 0.055630079696993) + (y * -0.20397695888897) + (z * 1.056971514242878); + + r = r > 0.0031308 ? ((1.055 * Math.pow(r, 1.0 / 2.4)) - 0.055) + : r = (r * 12.92); + + g = g > 0.0031308 ? ((1.055 * Math.pow(g, 1.0 / 2.4)) - 0.055) + : g = (g * 12.92); + + b = b > 0.0031308 ? ((1.055 * Math.pow(b, 1.0 / 2.4)) - 0.055) + : b = (b * 12.92); + + r = Math.min(Math.max(0, r), 1); + g = Math.min(Math.max(0, g), 1); + b = Math.min(Math.max(0, b), 1); + + return [r * 255, g * 255, b * 255]; + }; + + /** - * @param {Array} flatCoordinates Flat coordinates. - * @param {number} offset Offset. - * @param {number} end End. - * @param {number} stride Stride. - * @param {number} m M. - * @param {boolean} extrapolate Extrapolate. - * @return {import("../../coordinate.js").Coordinate|null} Coordinate. + * RGB to XYZ + * + * @param {Array} rgb RGB channels + * + * @return {Array} XYZ channels */ - function lineStringCoordinateAtM( - flatCoordinates, - offset, - end, - stride, - m, - extrapolate - ) { - if (end == offset) { - return null; - } - let coordinate; - if (m < flatCoordinates[offset + stride - 1]) { - if (extrapolate) { - coordinate = flatCoordinates.slice(offset, offset + stride); - coordinate[stride - 1] = m; - return coordinate; - } - return null; - } - if (flatCoordinates[end - 1] < m) { - if (extrapolate) { - coordinate = flatCoordinates.slice(end - stride, end); - coordinate[stride - 1] = m; - return coordinate; - } - return null; - } - // FIXME use O(1) search - if (m == flatCoordinates[offset + stride - 1]) { - return flatCoordinates.slice(offset, offset + stride); - } - let lo = offset / stride; - let hi = end / stride; - while (lo < hi) { - const mid = (lo + hi) >> 1; - if (m < flatCoordinates[(mid + 1) * stride - 1]) { - hi = mid; - } else { - lo = mid + 1; - } - } - const m0 = flatCoordinates[lo * stride - 1]; - if (m == m0) { - return flatCoordinates.slice((lo - 1) * stride, (lo - 1) * stride + stride); - } - const m1 = flatCoordinates[(lo + 1) * stride - 1]; - const t = (m - m0) / (m1 - m0); - coordinate = []; - for (let i = 0; i < stride - 1; ++i) { - coordinate.push( - lerp$1( - flatCoordinates[(lo - 1) * stride + i], - flatCoordinates[lo * stride + i], - t - ) - ); - } - coordinate.push(m); - return coordinate; + rgb.xyz = function(rgb, white) { + var r = rgb[0] / 255, + g = rgb[1] / 255, + b = rgb[2] / 255; + + // assume sRGB + r = r > 0.04045 ? Math.pow(((r + 0.055) / 1.055), 2.4) : (r / 12.92); + g = g > 0.04045 ? Math.pow(((g + 0.055) / 1.055), 2.4) : (g / 12.92); + b = b > 0.04045 ? Math.pow(((b + 0.055) / 1.055), 2.4) : (b / 12.92); + + var x = (r * 0.41239079926595) + (g * 0.35758433938387) + (b * 0.18048078840183); + var y = (r * 0.21263900587151) + (g * 0.71516867876775) + (b * 0.072192315360733); + var z = (r * 0.019330818715591) + (g * 0.11919477979462) + (b * 0.95053215224966); + + white = white || xyz.whitepoint[2].E; + + return [x * white[0], y * white[1], z * white[2]]; + }; + + /** + * CIE LUV (C'est la vie) + * + * @module color-space/luv + */ + + var luv = { + name: 'luv', + //NOTE: luv has no rigidly defined limits + //easyrgb fails to get proper coords + //boronine states no rigid limits + //colorMine refers this ones: + min: [0,-134,-140], + max: [100,224,122], + channel: ['lightness', 'u', 'v'], + alias: ['LUV', 'cieluv', 'cie1976'], + + xyz: function(arg, i, o){ + var _u, _v, l, u, v, x, y, z, xn, yn, zn, un, vn; + l = arg[0], u = arg[1], v = arg[2]; + + if (l === 0) return [0,0,0]; + + //get constants + //var e = 0.008856451679035631; //(6/29)^3 + var k = 0.0011070564598794539; //(3/29)^3 + + //get illuminant/observer + i = i || 'D65'; + o = o || 2; + + xn = xyz.whitepoint[o][i][0]; + yn = xyz.whitepoint[o][i][1]; + zn = xyz.whitepoint[o][i][2]; + + un = (4 * xn) / (xn + (15 * yn) + (3 * zn)); + vn = (9 * yn) / (xn + (15 * yn) + (3 * zn)); + // un = 0.19783000664283; + // vn = 0.46831999493879; + + + _u = u / (13 * l) + un || 0; + _v = v / (13 * l) + vn || 0; + + y = l > 8 ? yn * Math.pow( (l + 16) / 116 , 3) : yn * l * k; + + //wikipedia method + x = y * 9 * _u / (4 * _v) || 0; + z = y * (12 - 3 * _u - 20 * _v) / (4 * _v) || 0; + + //boronine method + //https://github.com/boronine/husl/blob/master/husl.coffee#L201 + // x = 0 - (9 * y * _u) / ((_u - 4) * _v - _u * _v); + // z = (9 * y - (15 * _v * y) - (_v * x)) / (3 * _v); + + return [x, y, z]; + } + }; + + // http://www.brucelindbloom.com/index.html?Equations.html + // https://github.com/boronine/husl/blob/master/husl.coffee + //i - illuminant + //o - observer + xyz.luv = function(arg, i, o) { + var _u, _v, l, u, v, x, y, z, xn, yn, zn, un, vn; + + //get constants + var e = 0.008856451679035631; //(6/29)^3 + var k = 903.2962962962961; //(29/3)^3 + + //get illuminant/observer coords + i = i || 'D65'; + o = o || 2; + + xn = xyz.whitepoint[o][i][0]; + yn = xyz.whitepoint[o][i][1]; + zn = xyz.whitepoint[o][i][2]; + + un = (4 * xn) / (xn + (15 * yn) + (3 * zn)); + vn = (9 * yn) / (xn + (15 * yn) + (3 * zn)); + + + x = arg[0], y = arg[1], z = arg[2]; + + + _u = (4 * x) / (x + (15 * y) + (3 * z)) || 0; + _v = (9 * y) / (x + (15 * y) + (3 * z)) || 0; + + var yr = y/yn; + + l = yr <= e ? k * yr : 116 * Math.pow(yr, 1/3) - 16; + + u = 13 * l * (_u - un); + v = 13 * l * (_v - vn); + + return [l, u, v]; + }; + + /** + * Cylindrical CIE LUV + * + * @module color-space/lchuv + */ + + //cylindrical luv + var lchuv = { + name: 'lchuv', + channel: ['lightness', 'chroma', 'hue'], + alias: ['LCHuv', 'cielchuv'], + min: [0,0,0], + max: [100,100,360], + + luv: function(luv){ + var l = luv[0], + c = luv[1], + h = luv[2], + u, v, hr; + + hr = h / 360 * 2 * Math.PI; + u = c * Math.cos(hr); + v = c * Math.sin(hr); + return [l, u, v]; + }, + + xyz: function(arg) { + return luv.xyz(lchuv.luv(arg)); + } + }; + + luv.lchuv = function(luv){ + var l = luv[0], u = luv[1], v = luv[2]; + + var c = Math.sqrt(u*u + v*v); + var hr = Math.atan2(v,u); + var h = hr * 360 / 2 / Math.PI; + if (h < 0) { + h += 360; + } + + return [l,c,h] + }; + + xyz.lchuv = function(arg){ + return luv.lchuv(xyz.luv(arg)); + }; + + var names = { + aliceblue: [240, 248, 255], + antiquewhite: [250, 235, 215], + aqua: [0, 255, 255], + aquamarine: [127, 255, 212], + azure: [240, 255, 255], + beige: [245, 245, 220], + bisque: [255, 228, 196], + black: [0, 0, 0], + blanchedalmond: [255, 235, 205], + blue: [0, 0, 255], + blueviolet: [138, 43, 226], + brown: [165, 42, 42], + burlywood: [222, 184, 135], + cadetblue: [95, 158, 160], + chartreuse: [127, 255, 0], + chocolate: [210, 105, 30], + coral: [255, 127, 80], + cornflowerblue: [100, 149, 237], + cornsilk: [255, 248, 220], + crimson: [220, 20, 60], + cyan: [0, 255, 255], + darkblue: [0, 0, 139], + darkcyan: [0, 139, 139], + darkgoldenrod: [184, 134, 11], + darkgray: [169, 169, 169], + darkgreen: [0, 100, 0], + darkgrey: [169, 169, 169], + darkkhaki: [189, 183, 107], + darkmagenta: [139, 0, 139], + darkolivegreen: [85, 107, 47], + darkorange: [255, 140, 0], + darkorchid: [153, 50, 204], + darkred: [139, 0, 0], + darksalmon: [233, 150, 122], + darkseagreen: [143, 188, 143], + darkslateblue: [72, 61, 139], + darkslategray: [47, 79, 79], + darkslategrey: [47, 79, 79], + darkturquoise: [0, 206, 209], + darkviolet: [148, 0, 211], + deeppink: [255, 20, 147], + deepskyblue: [0, 191, 255], + dimgray: [105, 105, 105], + dimgrey: [105, 105, 105], + dodgerblue: [30, 144, 255], + firebrick: [178, 34, 34], + floralwhite: [255, 250, 240], + forestgreen: [34, 139, 34], + fuchsia: [255, 0, 255], + gainsboro: [220, 220, 220], + ghostwhite: [248, 248, 255], + gold: [255, 215, 0], + goldenrod: [218, 165, 32], + gray: [128, 128, 128], + green: [0, 128, 0], + greenyellow: [173, 255, 47], + grey: [128, 128, 128], + honeydew: [240, 255, 240], + hotpink: [255, 105, 180], + indianred: [205, 92, 92], + indigo: [75, 0, 130], + ivory: [255, 255, 240], + khaki: [240, 230, 140], + lavender: [230, 230, 250], + lavenderblush: [255, 240, 245], + lawngreen: [124, 252, 0], + lemonchiffon: [255, 250, 205], + lightblue: [173, 216, 230], + lightcoral: [240, 128, 128], + lightcyan: [224, 255, 255], + lightgoldenrodyellow: [250, 250, 210], + lightgray: [211, 211, 211], + lightgreen: [144, 238, 144], + lightgrey: [211, 211, 211], + lightpink: [255, 182, 193], + lightsalmon: [255, 160, 122], + lightseagreen: [32, 178, 170], + lightskyblue: [135, 206, 250], + lightslategray: [119, 136, 153], + lightslategrey: [119, 136, 153], + lightsteelblue: [176, 196, 222], + lightyellow: [255, 255, 224], + lime: [0, 255, 0], + limegreen: [50, 205, 50], + linen: [250, 240, 230], + magenta: [255, 0, 255], + maroon: [128, 0, 0], + mediumaquamarine: [102, 205, 170], + mediumblue: [0, 0, 205], + mediumorchid: [186, 85, 211], + mediumpurple: [147, 112, 219], + mediumseagreen: [60, 179, 113], + mediumslateblue: [123, 104, 238], + mediumspringgreen: [0, 250, 154], + mediumturquoise: [72, 209, 204], + mediumvioletred: [199, 21, 133], + midnightblue: [25, 25, 112], + mintcream: [245, 255, 250], + mistyrose: [255, 228, 225], + moccasin: [255, 228, 181], + navajowhite: [255, 222, 173], + navy: [0, 0, 128], + oldlace: [253, 245, 230], + olive: [128, 128, 0], + olivedrab: [107, 142, 35], + orange: [255, 165, 0], + orangered: [255, 69, 0], + orchid: [218, 112, 214], + palegoldenrod: [238, 232, 170], + palegreen: [152, 251, 152], + paleturquoise: [175, 238, 238], + palevioletred: [219, 112, 147], + papayawhip: [255, 239, 213], + peachpuff: [255, 218, 185], + peru: [205, 133, 63], + pink: [255, 192, 203], + plum: [221, 160, 221], + powderblue: [176, 224, 230], + purple: [128, 0, 128], + rebeccapurple: [102, 51, 153], + red: [255, 0, 0], + rosybrown: [188, 143, 143], + royalblue: [65, 105, 225], + saddlebrown: [139, 69, 19], + salmon: [250, 128, 114], + sandybrown: [244, 164, 96], + seagreen: [46, 139, 87], + seashell: [255, 245, 238], + sienna: [160, 82, 45], + silver: [192, 192, 192], + skyblue: [135, 206, 235], + slateblue: [106, 90, 205], + slategray: [112, 128, 144], + slategrey: [112, 128, 144], + snow: [255, 250, 250], + springgreen: [0, 255, 127], + steelblue: [70, 130, 180], + tan: [210, 180, 140], + teal: [0, 128, 128], + thistle: [216, 191, 216], + tomato: [255, 99, 71], + turquoise: [64, 224, 208], + violet: [238, 130, 238], + wheat: [245, 222, 179], + white: [255, 255, 255], + whitesmoke: [245, 245, 245], + yellow: [255, 255, 0], + yellowgreen: [154, 205, 50] + }; + + /** + * @module color-parse + */ + + /** + * Base hues + * http://dev.w3.org/csswg/css-color/#typedef-named-hue + */ + //FIXME: use external hue detector + var baseHues = { + red: 0, + orange: 60, + yellow: 120, + green: 180, + blue: 240, + purple: 300 + }; + + /** + * Parse color from the string passed + * + * @return {Object} A space indicator `space`, an array `values` and `alpha` + */ + function parse$2(cstr) { + var m, parts = [], alpha = 1, space; + + //numeric case + if (typeof cstr === 'number') { + return { space: 'rgb', values: [cstr >>> 16, (cstr & 0x00ff00) >>> 8, cstr & 0x0000ff], alpha: 1 } + } + if (typeof cstr === 'number') return { space: 'rgb', values: [cstr >>> 16, (cstr & 0x00ff00) >>> 8, cstr & 0x0000ff], alpha: 1 } + + cstr = String(cstr).toLowerCase(); + + //keyword + if (names[cstr]) { + parts = names[cstr].slice(); + space = 'rgb'; + } + + //reserved words + else if (cstr === 'transparent') { + alpha = 0; + space = 'rgb'; + parts = [0, 0, 0]; + } + + //hex + else if (cstr[0] === '#') { + var base = cstr.slice(1); + var size = base.length; + var isShort = size <= 4; + alpha = 1; + + if (isShort) { + parts = [ + parseInt(base[0] + base[0], 16), + parseInt(base[1] + base[1], 16), + parseInt(base[2] + base[2], 16) + ]; + if (size === 4) { + alpha = parseInt(base[3] + base[3], 16) / 255; + } + } + else { + parts = [ + parseInt(base[0] + base[1], 16), + parseInt(base[2] + base[3], 16), + parseInt(base[4] + base[5], 16) + ]; + if (size === 8) { + alpha = parseInt(base[6] + base[7], 16) / 255; + } + } + + if (!parts[0]) parts[0] = 0; + if (!parts[1]) parts[1] = 0; + if (!parts[2]) parts[2] = 0; + + space = 'rgb'; + } + + // color space + else if (m = /^((?:rgba?|hs[lvb]a?|hwba?|cmyk?|xy[zy]|gray|lab|lchu?v?|[ly]uv|lms|oklch|oklab|color))\s*\(([^\)]*)\)/.exec(cstr)) { + var name = m[1]; + space = name.replace(/a$/, ''); + var dims = space === 'cmyk' ? 4 : space === 'gray' ? 1 : 3; + parts = m[2].trim().split(/\s*[,\/]\s*|\s+/); + + // color(srgb-linear x x x) -> srgb-linear(x x x) + if (space === 'color') space = parts.shift(); + + parts = parts.map(function (x, i) { + // + if (x[x.length - 1] === '%') { + x = parseFloat(x) / 100; + // alpha -> 0..1 + if (i === 3) return x + // rgb -> 0..255 + if (space === 'rgb') return x * 255 + // hsl, hwb H -> 0..100 + if (space[0] === 'h') return x * 100 + // lch, lab L -> 0..100 + if (space[0] === 'l' && !i) return x * 100 + // lab A B -> -125..125 + if (space === 'lab') return x * 125 + // lch C -> 0..150, H -> 0..360 + if (space === 'lch') return i < 2 ? x * 150 : x * 360 + // oklch/oklab L -> 0..1 + if (space[0] === 'o' && !i) return x + // oklab A B -> -0.4..0.4 + if (space === 'oklab') return x * 0.4 + // oklch C -> 0..0.4, H -> 0..360 + if (space === 'oklch') return i < 2 ? x * 0.4 : x * 360 + // color(xxx) -> 0..1 + return x + } + + //hue + if (space[i] === 'h' || (i === 2 && space[space.length - 1] === 'h')) { + // + if (baseHues[x] !== undefined) return baseHues[x] + // + if (x.endsWith('deg')) return parseFloat(x) + // + if (x.endsWith('turn')) return parseFloat(x) * 360 + if (x.endsWith('grad')) return parseFloat(x) * 360 / 400 + if (x.endsWith('rad')) return parseFloat(x) * 180 / Math.PI + } + if (x === 'none') return 0 + return parseFloat(x) + }); + + alpha = parts.length > dims ? parts.pop() : 1; + } + + //named channels case + else if (/[0-9](?:\s|\/|,)/.test(cstr)) { + parts = cstr.match(/([0-9]+)/g).map(function (value) { + return parseFloat(value) + }); + + space = cstr.match(/([a-z])/ig)?.join('')?.toLowerCase() || 'rgb'; + } + + return { + space, + values: parts, + alpha + } } /** - * @param {Array} flatCoordinates Flat coordinates. - * @param {number} offset Offset. - * @param {Array} ends Ends. - * @param {number} stride Stride. - * @param {number} m M. - * @param {boolean} extrapolate Extrapolate. - * @param {boolean} interpolate Interpolate. - * @return {import("../../coordinate.js").Coordinate|null} Coordinate. + * @module color-space/hsl */ - function lineStringsCoordinateAtM( - flatCoordinates, - offset, - ends, - stride, - m, - extrapolate, - interpolate - ) { - if (interpolate) { - return lineStringCoordinateAtM( - flatCoordinates, - offset, - ends[ends.length - 1], - stride, - m, - extrapolate - ); - } - let coordinate; - if (m < flatCoordinates[stride - 1]) { - if (extrapolate) { - coordinate = flatCoordinates.slice(0, stride); - coordinate[stride - 1] = m; - return coordinate; - } - return null; - } - if (flatCoordinates[flatCoordinates.length - 1] < m) { - if (extrapolate) { - coordinate = flatCoordinates.slice(flatCoordinates.length - stride); - coordinate[stride - 1] = m; - return coordinate; - } - return null; - } - for (let i = 0, ii = ends.length; i < ii; ++i) { - const end = ends[i]; - if (offset == end) { - continue; - } - if (m < flatCoordinates[offset + stride - 1]) { - return null; - } - if (m <= flatCoordinates[end - 1]) { - return lineStringCoordinateAtM( - flatCoordinates, - offset, - end, - stride, - m, - false - ); - } - offset = end; - } - return null; + + var hsl = { + name: 'hsl', + min: [0,0,0], + max: [360,100,100], + channel: ['hue', 'saturation', 'lightness'], + alias: ['HSL'], + + rgb: function(hsl) { + var h = hsl[0]/360, s = hsl[1]/100, l = hsl[2]/100, t1, t2, t3, rgb, val, i=0; + + if (s === 0) return val = l * 255, [val, val, val]; + + t2 = l < 0.5 ? l * (1 + s) : l + s - l * s; + t1 = 2 * l - t2; + + rgb = [0, 0, 0]; + for (;i<3;) { + t3 = h + 1 / 3 * - (i - 1); + t3 < 0 ? t3++ : t3 > 1 && t3--; + val = 6 * t3 < 1 ? t1 + (t2 - t1) * 6 * t3 : + 2 * t3 < 1 ? t2 : + 3 * t3 < 2 ? t1 + (t2 - t1) * (2 / 3 - t3) * 6 : + t1; + rgb[i++] = val * 255; + } + + return rgb; + } + }; + + + //extend rgb + rgb.hsl = function(rgb) { + var r = rgb[0]/255, + g = rgb[1]/255, + b = rgb[2]/255, + min = Math.min(r, g, b), + max = Math.max(r, g, b), + delta = max - min, + h, s, l; + + if (max === min) { + h = 0; + } + else if (r === max) { + h = (g - b) / delta; + } + else if (g === max) { + h = 2 + (b - r) / delta; + } + else if (b === max) { + h = 4 + (r - g)/ delta; + } + + h = Math.min(h * 60, 360); + + if (h < 0) { + h += 360; + } + + l = (min + max) / 2; + + if (max === min) { + s = 0; + } + else if (l <= 0.5) { + s = delta / (max + min); + } + else { + s = delta / (2 - max - min); + } + + return [h, s * 100, l * 100]; + }; + + /** @module color-rgba */ + + function rgba(color) { + // template literals + if (Array.isArray(color) && color.raw) color = String.raw(...arguments); + if (color instanceof Number) color = +color; + + var values; + + //attempt to parse non-array arguments + var parsed = parse$2(color); + + if (!parsed.space) return [] + + const min = parsed.space[0] === 'h' ? hsl.min : rgb.min; + const max = parsed.space[0] === 'h' ? hsl.max : rgb.max; + + values = Array(3); + values[0] = Math.min(Math.max(parsed.values[0], min[0]), max[0]); + values[1] = Math.min(Math.max(parsed.values[1], min[1]), max[1]); + values[2] = Math.min(Math.max(parsed.values[2], min[2]), max[2]); + + if (parsed.space[0] === 'h') { + values = hsl.rgb(values); + } + + values.push(Math.min(Math.max(parsed.alpha, 0), 1)); + + return values } /** - * @module ol/geom/flat/length + * @module ol/color */ /** - * @param {Array} flatCoordinates Flat coordinates. - * @param {number} offset Offset. - * @param {number} end End. - * @param {number} stride Stride. - * @return {number} Length. + * A color represented as a short array [red, green, blue, alpha]. + * red, green, and blue should be integers in the range 0..255 inclusive. + * alpha should be a float in the range 0..1 inclusive. If no alpha value is + * given then `1` will be used. + * @typedef {Array} Color + * @api */ - function lineStringLength(flatCoordinates, offset, end, stride) { - let x1 = flatCoordinates[offset]; - let y1 = flatCoordinates[offset + 1]; - let length = 0; - for (let i = offset + stride; i < end; i += stride) { - const x2 = flatCoordinates[i]; - const y2 = flatCoordinates[i + 1]; - length += Math.sqrt((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1)); - x1 = x2; - y1 = y2; + + /** + * Return the color as an rgba string. + * @param {Color|string} color Color. + * @return {string} Rgba string. + * @api + */ + function asString(color) { + if (typeof color === 'string') { + return color; } - return length; + return toString$2(color); } /** - * @module ol/geom/LineString + * @type {number} */ + const MAX_CACHE_SIZE = 1024; /** - * @classdesc - * Linestring geometry. + * We maintain a small cache of parsed strings. Whenever the cache grows too large, + * we delete an arbitrary set of the entries. * - * @api + * @type {Object} */ - class LineString extends SimpleGeometry$1 { - /** - * @param {Array|Array} coordinates Coordinates. - * For internal use, flat coordinates in combination with `layout` are also accepted. - * @param {import("./Geometry.js").GeometryLayout} [layout] Layout. - */ - constructor(coordinates, layout) { - super(); - - /** - * @private - * @type {import("../coordinate.js").Coordinate} - */ - this.flatMidpoint_ = null; + const cache = {}; - /** - * @private - * @type {number} - */ - this.flatMidpointRevision_ = -1; + /** + * @type {number} + */ + let cacheSize = 0; - /** - * @private - * @type {number} - */ - this.maxDelta_ = -1; - - /** - * @private - * @type {number} - */ - this.maxDeltaRevision_ = -1; - - if (layout !== undefined && !Array.isArray(coordinates[0])) { - this.setFlatCoordinates( - layout, - /** @type {Array} */ (coordinates) - ); - } else { - this.setCoordinates( - /** @type {Array} */ ( - coordinates - ), - layout - ); - } + /** + * @param {Color} color A color that may or may not have an alpha channel. + * @return {Color} The input color with an alpha channel. If the input color has + * an alpha channel, the input color will be returned unchanged. Otherwise, a new + * array will be returned with the input color and an alpha channel of 1. + */ + function withAlpha(color) { + if (color.length === 4) { + return color; } + const output = color.slice(); + output[3] = 1; + return output; + } - /** - * Append the passed coordinate to the coordinates of the linestring. - * @param {import("../coordinate.js").Coordinate} coordinate Coordinate. - * @api - */ - appendCoordinate(coordinate) { - if (!this.flatCoordinates) { - this.flatCoordinates = coordinate.slice(); - } else { - extend$1(this.flatCoordinates, coordinate); - } - this.changed(); - } + /** + * @param {Color} color RGBA color. + * @return {Color} LCHuv color with alpha. + */ + function rgbaToLcha(color) { + const output = xyz.lchuv(rgb.xyz(color)); + output[3] = color[3]; + return output; + } - /** - * Make a complete copy of the geometry. - * @return {!LineString} Clone. - * @api - */ - clone() { - const lineString = new LineString( - this.flatCoordinates.slice(), - this.layout - ); - lineString.applyProperties(this); - return lineString; - } + /** + * @param {Color} color LCHuv color with alpha. + * @return {Color} RGBA color. + */ + function lchaToRgba(color) { + const output = xyz.rgb(lchuv.xyz(color)); + output[3] = color[3]; + return output; + } - /** - * @param {number} x X. - * @param {number} y Y. - * @param {import("../coordinate.js").Coordinate} closestPoint Closest point. - * @param {number} minSquaredDistance Minimum squared distance. - * @return {number} Minimum squared distance. - */ - 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(); + /** + * @param {string} s String. + * @return {Color} Color. + */ + function fromString(s) { + if (cache.hasOwnProperty(s)) { + return cache[s]; + } + if (cacheSize >= MAX_CACHE_SIZE) { + let i = 0; + for (const key in cache) { + if ((i++ & 3) === 0) { + delete cache[key]; + --cacheSize; + } } - 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, import("../coordinate.js").Coordinate, import("../coordinate.js").Coordinate): T} callback Function - * called for each segment. The function will receive two arguments, the start and end coordinates of the segment. - * @return {T|boolean} Value. - * @template T,S - * @api - */ - forEachSegment(callback) { - return forEach( - this.flatCoordinates, - 0, - this.flatCoordinates.length, - this.stride, - callback - ); + const color = rgba(s); + if (color.length !== 4) { + throw new Error('Failed to parse "' + s + '" as color'); } - - /** - * Returns the coordinate at `m` using linear interpolation, or `null` if no - * such coordinate exists. - * - * `extrapolate` controls extrapolation beyond the range of Ms in the - * MultiLineString. If `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} [extrapolate] Extrapolate. Default is `false`. - * @return {import("../coordinate.js").Coordinate|null} Coordinate. - * @api - */ - getCoordinateAtM(m, extrapolate) { - if (this.layout != 'XYM' && this.layout != 'XYZM') { - return null; + for (const c of color) { + if (isNaN(c)) { + throw new Error('Failed to parse "' + s + '" as color'); } - extrapolate = extrapolate !== undefined ? extrapolate : false; - return lineStringCoordinateAtM( - this.flatCoordinates, - 0, - this.flatCoordinates.length, - this.stride, - m, - extrapolate - ); } + normalize(color); + cache[s] = color; + ++cacheSize; + return color; + } - /** - * Return the coordinates of the linestring. - * @return {Array} Coordinates. - * @api - */ - getCoordinates() { - return inflateCoordinates( - this.flatCoordinates, - 0, - this.flatCoordinates.length, - this.stride - ); + /** + * Return the color as an array. This function maintains a cache of calculated + * arrays which means the result should not be modified. + * @param {Color|string} color Color. + * @return {Color} Color. + * @api + */ + function asArray(color) { + if (Array.isArray(color)) { + return color; } + return fromString(color); + } - /** - * 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 {import("../coordinate.js").Coordinate} [dest] Optional coordinate whose values will - * be modified. If not provided, a new coordinate will be returned. - * @return {import("../coordinate.js").Coordinate} Coordinate of the interpolated point. - * @api - */ - getCoordinateAt(fraction, dest) { - return interpolatePoint( - this.flatCoordinates, - 0, - this.flatCoordinates.length, - this.stride, - fraction, - dest, - this.stride - ); - } + /** + * Exported for the tests. + * @param {Color} color Color. + * @return {Color} Clamped color. + */ + function normalize(color) { + color[0] = clamp((color[0] + 0.5) | 0, 0, 255); + color[1] = clamp((color[1] + 0.5) | 0, 0, 255); + color[2] = clamp((color[2] + 0.5) | 0, 0, 255); + color[3] = clamp(color[3], 0, 1); + return color; + } - /** - * 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 - ); + /** + * @param {Color} color Color. + * @return {string} String. + */ + function toString$2(color) { + let r = color[0]; + if (r != (r | 0)) { + r = (r + 0.5) | 0; } - - /** - * @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_; + let g = color[1]; + if (g != (g | 0)) { + g = (g + 0.5) | 0; } - - /** - * @param {number} squaredTolerance Squared tolerance. - * @return {LineString} Simplified LineString. - * @protected - */ - getSimplifiedGeometryInternal(squaredTolerance) { - const simplifiedFlatCoordinates = []; - simplifiedFlatCoordinates.length = douglasPeucker( - this.flatCoordinates, - 0, - this.flatCoordinates.length, - this.stride, - squaredTolerance, - simplifiedFlatCoordinates, - 0 - ); - return new LineString(simplifiedFlatCoordinates, 'XY'); + let b = color[2]; + if (b != (b | 0)) { + b = (b + 0.5) | 0; } + const a = color[3] === undefined ? 1 : Math.round(color[3] * 1000) / 1000; + return 'rgba(' + r + ',' + g + ',' + b + ',' + a + ')'; + } - /** - * Get the type of this geometry. - * @return {import("./Geometry.js").Type} Geometry type. - * @api - */ - getType() { - return 'LineString'; + /** + * @param {string} s String. + * @return {boolean} Whether the string is actually a valid color + */ + function isStringColor(s) { + try { + fromString(s); + return true; + } catch (_) { + return false; } + } - /** - * Test if the geometry and the passed extent intersect. - * @param {import("../extent.js").Extent} extent Extent. - * @return {boolean} `true` if the geometry and the extent intersect. - * @api - */ - intersectsExtent(extent) { - return intersectsLineString( - this.flatCoordinates, - 0, - this.flatCoordinates.length, - this.stride, - extent - ); - } + /** + * @module ol/has + */ - /** - * Set the coordinates of the linestring. - * @param {!Array} coordinates Coordinates. - * @param {import("./Geometry.js").GeometryLayout} [layout] Layout. - * @api - */ - setCoordinates(coordinates, layout) { - this.setLayout(layout, coordinates, 1); - if (!this.flatCoordinates) { - this.flatCoordinates = []; - } - this.flatCoordinates.length = deflateCoordinates( - this.flatCoordinates, - 0, - coordinates, - this.stride - ); - this.changed(); - } - } + const ua = + typeof navigator !== 'undefined' && typeof navigator.userAgent !== 'undefined' + ? navigator.userAgent.toLowerCase() + : ''; - var LineString$1 = LineString; + /** + * User agent string says we are dealing with Firefox as browser. + * @type {boolean} + */ + const FIREFOX = ua.includes('firefox'); /** - * @module ol/style/Stroke + * User agent string says we are dealing with Safari as browser. + * @type {boolean} */ + const SAFARI = ua.includes('safari') && !ua.includes('chrom'); /** - * @typedef {Object} Options - * @property {import("../color.js").Color|import("../colorlike.js").ColorLike} [color] A color, gradient or pattern. - * See {@link module:ol/color~Color} and {@link module:ol/colorlike~ColorLike} for possible formats. - * Default null; if null, the Canvas/renderer default black will be used. - * @property {CanvasLineCap} [lineCap='round'] Line cap style: `butt`, `round`, or `square`. - * @property {CanvasLineJoin} [lineJoin='round'] Line join style: `bevel`, `round`, or `miter`. - * @property {Array} [lineDash] Line dash pattern. Default is `null` (no dash). - * @property {number} [lineDashOffset=0] Line dash offset. - * @property {number} [miterLimit=10] Miter limit. - * @property {number} [width] Width. + * https://bugs.webkit.org/show_bug.cgi?id=237906 + * @type {boolean} */ + const SAFARI_BUG_237906 = + SAFARI && + (ua.includes('version/15.4') || + /cpu (os|iphone os) 15_4 like mac os x/.test(ua)); /** - * @classdesc - * Set stroke style for vector features. - * 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. - * @api + * User agent string says we are dealing with a WebKit engine. + * @type {boolean} */ - class Stroke { - /** - * @param {Options} [options] Options. - */ - constructor(options) { - options = options || {}; + const WEBKIT = ua.includes('webkit') && !ua.includes('edge'); - /** - * @private - * @type {import("../color.js").Color|import("../colorlike.js").ColorLike} - */ - this.color_ = options.color !== undefined ? options.color : null; + /** + * User agent string says we are dealing with a Mac as platform. + * @type {boolean} + */ + const MAC = ua.includes('macintosh'); - /** - * @private - * @type {CanvasLineCap|undefined} - */ - this.lineCap_ = options.lineCap; + /** + * The ratio between physical pixels and device-independent pixels + * (dips) on the device (`window.devicePixelRatio`). + * @const + * @type {number} + * @api + */ + const DEVICE_PIXEL_RATIO = + typeof devicePixelRatio !== 'undefined' ? devicePixelRatio : 1; - /** - * @private - * @type {Array|null} - */ - this.lineDash_ = options.lineDash !== undefined ? options.lineDash : null; + /** + * The execution context is a worker with OffscreenCanvas available. + * @const + * @type {boolean} + */ + const WORKER_OFFSCREEN_CANVAS = + typeof WorkerGlobalScope !== 'undefined' && + typeof OffscreenCanvas !== 'undefined' && + self instanceof WorkerGlobalScope; //eslint-disable-line - /** - * @private - * @type {number|undefined} - */ - this.lineDashOffset_ = options.lineDashOffset; + /** + * Image.prototype.decode() is supported. + * @type {boolean} + */ + const IMAGE_DECODE = + typeof Image !== 'undefined' && Image.prototype.decode; - /** - * @private - * @type {CanvasLineJoin|undefined} - */ - this.lineJoin_ = options.lineJoin; + /** + * createImageBitmap() is supported. + * @type {boolean} + */ + const CREATE_IMAGE_BITMAP = typeof createImageBitmap === 'function'; - /** - * @private - * @type {number|undefined} - */ - this.miterLimit_ = options.miterLimit; + /** + * @type {boolean} + */ + const PASSIVE_EVENT_LISTENERS = (function () { + let passive = false; + try { + const options = Object.defineProperty({}, 'passive', { + get: function () { + passive = true; + }, + }); - /** - * @private - * @type {number|undefined} - */ - this.width_ = options.width; + // @ts-ignore Ignore invalid event type '_' + window.addEventListener('_', null, options); + // @ts-ignore Ignore invalid event type '_' + window.removeEventListener('_', null, options); + } catch (error) { + // passive not supported } + return passive; + })(); - /** - * Clones the style. - * @return {Stroke} The cloned style. - * @api - */ - clone() { - const color = this.getColor(); - return new Stroke({ - color: Array.isArray(color) ? 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(), - }); - } + /** + * @module ol/dom + */ - /** - * Get the stroke color. - * @return {import("../color.js").Color|import("../colorlike.js").ColorLike} Color. - * @api - */ - getColor() { - return this.color_; + //FIXME Move this function to the canvas module + /** + * Create an html canvas element and returns its 2d context. + * @param {number} [width] Canvas width. + * @param {number} [height] Canvas height. + * @param {Array} [canvasPool] Canvas pool to take existing canvas from. + * @param {CanvasRenderingContext2DSettings} [settings] CanvasRenderingContext2DSettings + * @return {CanvasRenderingContext2D} The context. + */ + function createCanvasContext2D(width, height, canvasPool, settings) { + /** @type {HTMLCanvasElement|OffscreenCanvas} */ + let canvas; + if (canvasPool && canvasPool.length) { + canvas = /** @type {HTMLCanvasElement} */ (canvasPool.shift()); + } else if (WORKER_OFFSCREEN_CANVAS) { + canvas = new OffscreenCanvas(width || 300, height || 300); + } else { + canvas = document.createElement('canvas'); } - - /** - * Get the line cap type for the stroke. - * @return {CanvasLineCap|undefined} Line cap. - * @api - */ - getLineCap() { - return this.lineCap_; + if (width) { + canvas.width = width; } - - /** - * Get the line dash style for the stroke. - * @return {Array|null} Line dash. - * @api - */ - getLineDash() { - return this.lineDash_; + if (height) { + canvas.height = height; } + //FIXME Allow OffscreenCanvasRenderingContext2D as return type + return /** @type {CanvasRenderingContext2D} */ ( + canvas.getContext('2d', settings) + ); + } - /** - * Get the line dash offset for the stroke. - * @return {number|undefined} Line dash offset. - * @api - */ - getLineDashOffset() { - return this.lineDashOffset_; + /** @type {CanvasRenderingContext2D} */ + let sharedCanvasContext; + + /** + * @return {CanvasRenderingContext2D} Shared canvas context. + */ + function getSharedCanvasContext2D() { + if (!sharedCanvasContext) { + sharedCanvasContext = createCanvasContext2D(1, 1); } + return sharedCanvasContext; + } - /** - * Get the line join type for the stroke. - * @return {CanvasLineJoin|undefined} Line join. - * @api + /** + * Releases canvas memory to avoid exceeding memory limits in Safari. + * See https://pqina.nl/blog/total-canvas-memory-use-exceeds-the-maximum-limit/ + * @param {CanvasRenderingContext2D} context Context. + */ + function releaseCanvas$1(context) { + const canvas = context.canvas; + canvas.width = 1; + canvas.height = 1; + context.clearRect(0, 0, 1, 1); + } + + /** + * Get the current computed width for the given element including margin, + * padding and border. + * Equivalent to jQuery's `$(el).outerWidth(true)`. + * @param {!HTMLElement} element Element. + * @return {number} The width. + */ + function outerWidth(element) { + let width = element.offsetWidth; + const style = getComputedStyle(element); + width += parseInt(style.marginLeft, 10) + parseInt(style.marginRight, 10); + + return width; + } + + /** + * Get the current computed height for the given element including margin, + * padding and border. + * Equivalent to jQuery's `$(el).outerHeight(true)`. + * @param {!HTMLElement} element Element. + * @return {number} The height. + */ + function outerHeight(element) { + let height = element.offsetHeight; + const style = getComputedStyle(element); + height += parseInt(style.marginTop, 10) + parseInt(style.marginBottom, 10); + + return height; + } + + /** + * @param {Node} newNode Node to replace old node + * @param {Node} oldNode The node to be replaced + */ + function replaceNode(newNode, oldNode) { + const parent = oldNode.parentNode; + if (parent) { + parent.replaceChild(newNode, oldNode); + } + } + + /** + * @param {Node} node The node to remove. + * @return {Node|null} The node that was removed or null. + */ + function removeNode$1(node) { + return node && node.parentNode ? node.parentNode.removeChild(node) : null; + } + + /** + * @param {Node} node The node to remove the children from. + */ + function removeChildren(node) { + while (node.lastChild) { + node.removeChild(node.lastChild); + } + } + + /** + * Transform the children of a parent node so they match the + * provided list of children. This function aims to efficiently + * remove, add, and reorder child nodes while maintaining a simple + * implementation (it is not guaranteed to minimize DOM operations). + * @param {Node} node The parent node whose children need reworking. + * @param {Array} children The desired children. + */ + function replaceChildren(node, children) { + const oldChildren = node.childNodes; + + for (let i = 0; true; ++i) { + const oldChild = oldChildren[i]; + const newChild = children[i]; + + // check if our work is done + if (!oldChild && !newChild) { + break; + } + + // check if children match + if (oldChild === newChild) { + continue; + } + + // check if a new child needs to be added + if (!oldChild) { + node.appendChild(newChild); + continue; + } + + // check if an old child needs to be removed + if (!newChild) { + node.removeChild(oldChild); + --i; + continue; + } + + // reorder + node.insertBefore(newChild, oldChild); + } + } + + /** + * @module ol/Image + */ + + /** + * A function that takes an {@link module:ol/Image~ImageWrapper} for the image and a + * `{string}` for the src as arguments. It is supposed to make it so the + * underlying image {@link module:ol/Image~ImageWrapper#getImage} is assigned the + * content specified by the src. If not specified, the default is + * + * function(image, src) { + * image.getImage().src = src; + * } + * + * Providing a custom `imageLoadFunction` can be useful to load images with + * post requests or - in general - through XHR requests, where the src of the + * image element would be set to a data URI when the content is loaded. + * + * @typedef {function(import("./Image.js").default, string): void} LoadFunction + * @api + */ + + /** + * @typedef {Object} ImageObject + * @property {import("./extent.js").Extent} [extent] Extent, if different from the requested one. + * @property {import("./resolution.js").ResolutionLike} [resolution] Resolution, if different from the requested one. + * When x and y resolution are different, use the array type (`[xResolution, yResolution]`). + * @property {number} [pixelRatio] Pixel ratio, if different from the requested one. + * @property {import('./DataTile.js').ImageLike} image Image. + */ + + /** + * Loader function used for image sources. Receives extent, resolution and pixel ratio as arguments. + * For images that cover any extent and resolution (static images), the loader function should not accept + * any arguments. The function returns an {@link import("./DataTile.js").ImageLike image}, an + * {@link import("./Image.js").ImageObject image object}, or a promise for the same. + * For loaders that generate images, the promise should not resolve until the image is loaded. + * If the returned image does not match the extent, resolution or pixel ratio passed to the loader, + * it has to return an {@link import("./Image.js").ImageObject image object} with the `image` and the + * correct `extent`, `resolution` and `pixelRatio`. + * + * @typedef {function(import("./extent.js").Extent, number, number, (function(HTMLImageElement, string): void)=): import("./DataTile.js").ImageLike|ImageObject|Promise} Loader + * @api + */ + + /** + * Loader function used for image sources. Receives extent, resolution and pixel ratio as arguments. + * The function returns a promise for an {@link import("./Image.js").ImageObject image object}. + * + * @typedef {function(import("./extent.js").Extent, number, number, (function(HTMLImageElement, string): void)=): import("./DataTile.js").ImageLike|ImageObject|Promise} ImageObjectPromiseLoader + */ + + class ImageWrapper extends Target { + /** + * @param {import("./extent.js").Extent} extent Extent. + * @param {number|Array|undefined} resolution Resolution. If provided as array, x and y + * resolution will be assumed. + * @param {number} pixelRatio Pixel ratio. + * @param {import("./ImageState.js").default|import("./Image.js").Loader} stateOrLoader State. */ - getLineJoin() { - return this.lineJoin_; + constructor(extent, resolution, pixelRatio, stateOrLoader) { + super(); + + /** + * @protected + * @type {import("./extent.js").Extent} + */ + this.extent = extent; + + /** + * @private + * @type {number} + */ + this.pixelRatio_ = pixelRatio; + + /** + * @protected + * @type {number|Array|undefined} + */ + this.resolution = resolution; + + /** + * @protected + * @type {import("./ImageState.js").default} + */ + this.state = + typeof stateOrLoader === 'function' ? ImageState.IDLE : stateOrLoader; + + /** + * @private + * @type {import('./DataTile.js').ImageLike|null} + */ + this.image_ = null; + + /** + * @protected + * @type {import("./Image.js").Loader} + */ + this.loader = typeof stateOrLoader === 'function' ? stateOrLoader : null; } /** - * Get the miter limit for the stroke. - * @return {number|undefined} Miter limit. - * @api + * @protected */ - getMiterLimit() { - return this.miterLimit_; + changed() { + this.dispatchEvent(EventType.CHANGE); } /** - * Get the stroke width. - * @return {number|undefined} Width. - * @api + * @return {import("./extent.js").Extent} Extent. */ - getWidth() { - return this.width_; + getExtent() { + return this.extent; } /** - * Set the color. - * - * @param {import("../color.js").Color|import("../colorlike.js").ColorLike} color Color. - * @api + * @return {import('./DataTile.js').ImageLike} Image. */ - setColor(color) { - this.color_ = color; + getImage() { + return this.image_; } /** - * Set the line cap. - * - * @param {CanvasLineCap|undefined} lineCap Line cap. - * @api + * @return {number} PixelRatio. */ - setLineCap(lineCap) { - this.lineCap_ = lineCap; + getPixelRatio() { + return this.pixelRatio_; } /** - * Set the line dash. - * - * @param {Array|null} lineDash Line dash. - * @api + * @return {number|Array} Resolution. */ - setLineDash(lineDash) { - this.lineDash_ = lineDash; + getResolution() { + return /** @type {number} */ (this.resolution); } /** - * Set the line dash offset. - * - * @param {number|undefined} lineDashOffset Line dash offset. - * @api + * @return {import("./ImageState.js").default} State. */ - setLineDashOffset(lineDashOffset) { - this.lineDashOffset_ = lineDashOffset; + getState() { + return this.state; } /** - * Set the line join. - * - * @param {CanvasLineJoin|undefined} lineJoin Line join. - * @api + * Load not yet loaded URI. */ - setLineJoin(lineJoin) { - this.lineJoin_ = lineJoin; + load() { + if (this.state == ImageState.IDLE) { + if (this.loader) { + this.state = ImageState.LOADING; + this.changed(); + const resolution = this.getResolution(); + const requestResolution = Array.isArray(resolution) + ? resolution[0] + : resolution; + toPromise(() => + this.loader( + this.getExtent(), + requestResolution, + this.getPixelRatio(), + ), + ) + .then((image) => { + if ('image' in image) { + this.image_ = image.image; + } + if ('extent' in image) { + this.extent = image.extent; + } + if ('resolution' in image) { + this.resolution = image.resolution; + } + if ('pixelRatio' in image) { + this.pixelRatio_ = image.pixelRatio; + } + if ( + image instanceof HTMLImageElement || + image instanceof ImageBitmap || + image instanceof HTMLCanvasElement || + image instanceof HTMLVideoElement + ) { + this.image_ = image; + } + this.state = ImageState.LOADED; + }) + .catch((error) => { + this.state = ImageState.ERROR; + console.error(error); // eslint-disable-line no-console + }) + .finally(() => this.changed()); + } + } } /** - * Set the miter limit. - * - * @param {number|undefined} miterLimit Miter limit. - * @api + * @param {import('./DataTile.js').ImageLike} image The image. */ - setMiterLimit(miterLimit) { - this.miterLimit_ = miterLimit; + setImage(image) { + this.image_ = image; } /** - * Set the width. - * - * @param {number|undefined} width Width. - * @api + * @param {number|Array} resolution Resolution. */ - setWidth(width) { - this.width_ = width; + setResolution(resolution) { + this.resolution = resolution; } } - var Stroke$1 = Stroke; - - /** - * @module ol/ImageState - */ - - /** - * @enum {number} - */ - var ImageState = { - IDLE: 0, - LOADING: 1, - LOADED: 2, - ERROR: 3, - EMPTY: 4, - }; - /** - * @module ol/size + * @param {import('./DataTile.js').ImageLike} image Image element. + * @param {function():any} loadHandler Load callback function. + * @param {function():any} errorHandler Error callback function. + * @return {function():void} Callback to stop listening. */ + function listenImage(image, loadHandler, errorHandler) { + const img = /** @type {HTMLImageElement} */ (image); + let listening = true; + let decoding = false; + let loaded = false; - /** - * An array of numbers representing a size: `[width, height]`. - * @typedef {Array} Size - * @api - */ + const listenerKeys = [ + listenOnce(img, EventType.LOAD, function () { + loaded = true; + if (!decoding) { + loadHandler(); + } + }), + ]; - /** - * Returns a buffered size. - * @param {Size} size Size. - * @param {number} num The amount by which to buffer. - * @param {Size} [dest] Optional reusable size array. - * @return {Size} The buffered size. - */ - function buffer$1(size, num, dest) { - if (dest === undefined) { - dest = [0, 0]; + if (img.src && IMAGE_DECODE) { + decoding = true; + img + .decode() + .then(function () { + if (listening) { + loadHandler(); + } + }) + .catch(function (error) { + if (listening) { + if (loaded) { + loadHandler(); + } else { + errorHandler(); + } + } + }); + } else { + listenerKeys.push(listenOnce(img, EventType.ERROR, errorHandler)); } - dest[0] = size[0] + 2 * num; - dest[1] = size[1] + 2 * num; - return dest; + + return function unlisten() { + listening = false; + listenerKeys.forEach(unlistenByKey); + }; } /** - * Determines if a size has a positive area. - * @param {Size} size The size to test. - * @return {boolean} The size has a positive area. + * Loads an image. + * @param {HTMLImageElement} image Image, not yet loaded. + * @param {string} [src] `src` attribute of the image. Optional, not required if already present. + * @return {Promise} Promise resolving to an `HTMLImageElement`. + * @api */ - function hasArea(size) { - return size[0] > 0 && size[1] > 0; + function load(image, src) { + return new Promise((resolve, reject) => { + function handleLoad() { + unlisten(); + resolve(image); + } + function handleError() { + unlisten(); + reject(new Error('Image load error')); + } + function unlisten() { + image.removeEventListener('load', handleLoad); + image.removeEventListener('error', handleError); + } + image.addEventListener('load', handleLoad); + image.addEventListener('error', handleError); + }); } /** - * Returns a size scaled by a ratio. The result will be an array of integers. - * @param {Size} size Size. - * @param {number} ratio Ratio. - * @param {Size} [dest] Optional reusable size array. - * @return {Size} The scaled size. + * @param {HTMLImageElement} image Image, not yet loaded. + * @param {string} [src] `src` attribute of the image. Optional, not required if already present. + * @return {Promise} Promise resolving to an `HTMLImageElement`. */ - function scale(size, ratio, dest) { - if (dest === undefined) { - dest = [0, 0]; + function decodeFallback(image, src) { + if (src) { + image.src = src; } - dest[0] = (size[0] * ratio + 0.5) | 0; - dest[1] = (size[1] * ratio + 0.5) | 0; - return dest; + return image.src && IMAGE_DECODE + ? new Promise((resolve, reject) => + image + .decode() + .then(() => resolve(image)) + .catch((e) => + image.complete && image.width ? resolve(image) : reject(e), + ), + ) + : load(image); } /** - * Returns an `Size` array for the passed in number (meaning: square) or - * `Size` array. - * (meaning: non-square), - * @param {number|Size} size Width and height. - * @param {Size} [dest] Optional reusable size array. - * @return {Size} Size. + * Loads an image and decodes it to an `ImageBitmap` if `createImageBitmap()` is supported. Returns + * the loaded image otherwise. + * @param {HTMLImageElement} image Image, not yet loaded. + * @param {string} [src] `src` attribute of the image. Optional, not required if already present. + * @return {Promise} Promise resolving to an `ImageBitmap` or an + * `HTMLImageElement` if `createImageBitmap()` is not supported. * @api */ - function toSize(size, dest) { - if (Array.isArray(size)) { - return size; - } - if (dest === undefined) { - dest = [size, size]; - } else { - dest[0] = size; - dest[1] = size; - } - return dest; + function decode(image, src) { + if (src) { + image.src = src; + } + return image.src && IMAGE_DECODE && CREATE_IMAGE_BITMAP + ? image + .decode() + .then(() => createImageBitmap(image)) + .catch((e) => { + if (image.complete && image.width) { + return image; + } + throw e; + }) + : decodeFallback(image); } /** - * @module ol/style/Image + * @module ol/style/IconImageCache */ - /** - * @typedef {Object} Options - * @property {number} opacity Opacity. - * @property {boolean} rotateWithView If the image should get rotated with the view. - * @property {number} rotation Rotation. - * @property {number|import("../size.js").Size} scale Scale. - * @property {Array} displacement Displacement. - * @property {"declutter"|"obstacle"|"none"|undefined} declutterMode Declutter mode: `declutter`, `obstacle`, 'none */ - /** * @classdesc - * 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}. - * @abstract - * @api + * Singleton class. Available through {@link module:ol/style/IconImageCache.shared}. */ - class ImageStyle { - /** - * @param {Options} options Options. - */ - constructor(options) { + class IconImageCache { + constructor() { /** + * @type {!Object} * @private - * @type {number} */ - this.opacity_ = options.opacity; + this.cache_ = {}; /** + * @type {!Object} * @private - * @type {boolean} */ - this.rotateWithView_ = options.rotateWithView; + this.patternCache_ = {}; /** - * @private * @type {number} - */ - this.rotation_ = options.rotation; - - /** - * @private - * @type {number|import("../size.js").Size} - */ - this.scale_ = options.scale; - - /** - * @private - * @type {import("../size.js").Size} - */ - this.scaleArray_ = toSize(options.scale); - - /** * @private - * @type {Array} */ - this.displacement_ = options.displacement; + this.cacheSize_ = 0; /** + * @type {number} * @private - * @type {"declutter"|"obstacle"|"none"|undefined} */ - this.declutterMode_ = options.declutterMode; + this.maxCacheSize_ = 32; } /** - * Clones the style. - * @return {ImageStyle} The cloned style. - * @api + * FIXME empty description for jsdoc */ - clone() { - const scale = this.getScale(); - return new ImageStyle({ - opacity: this.getOpacity(), - scale: Array.isArray(scale) ? scale.slice() : scale, - rotation: this.getRotation(), - rotateWithView: this.getRotateWithView(), - displacement: this.getDisplacement().slice(), - declutterMode: this.getDeclutterMode(), - }); + clear() { + this.cache_ = {}; + this.patternCache_ = {}; + this.cacheSize_ = 0; } /** - * Get the symbolizer opacity. - * @return {number} Opacity. - * @api + * @return {boolean} Can expire cache. */ - getOpacity() { - return this.opacity_; + canExpireCache() { + return this.cacheSize_ > this.maxCacheSize_; } /** - * Determine whether the symbolizer rotates with the map. - * @return {boolean} Rotate with map. - * @api + * FIXME empty description for jsdoc */ - getRotateWithView() { - return this.rotateWithView_; + expire() { + if (this.canExpireCache()) { + let i = 0; + for (const key in this.cache_) { + const iconImage = this.cache_[key]; + if ((i++ & 3) === 0 && !iconImage.hasListener()) { + delete this.cache_[key]; + delete this.patternCache_[key]; + --this.cacheSize_; + } + } + } } /** - * Get the symoblizer rotation. - * @return {number} Rotation. - * @api + * @param {string} src Src. + * @param {?string} crossOrigin Cross origin. + * @param {import("../color.js").Color|string|null} color Color. + * @return {import("./IconImage.js").default} Icon image. */ - getRotation() { - return this.rotation_; + get(src, crossOrigin, color) { + const key = getCacheKey$1(src, crossOrigin, color); + return key in this.cache_ ? this.cache_[key] : null; } /** - * Get the symbolizer scale. - * @return {number|import("../size.js").Size} Scale. - * @api + * @param {string} src Src. + * @param {?string} crossOrigin Cross origin. + * @param {import("../color.js").Color|string|null} color Color. + * @return {CanvasPattern} Icon image. */ - getScale() { - return this.scale_; + getPattern(src, crossOrigin, color) { + const key = getCacheKey$1(src, crossOrigin, color); + return key in this.patternCache_ ? this.patternCache_[key] : null; } /** - * Get the symbolizer scale array. - * @return {import("../size.js").Size} Scale array. + * @param {string} src Src. + * @param {?string} crossOrigin Cross origin. + * @param {import("../color.js").Color|string|null} color Color. + * @param {import("./IconImage.js").default|null} iconImage Icon image. + * @param {boolean} [pattern] Also cache a `'repeat'` pattern with this `iconImage`. */ - getScaleArray() { - return this.scaleArray_; + set(src, crossOrigin, color, iconImage, pattern) { + const key = getCacheKey$1(src, crossOrigin, color); + const update = key in this.cache_; + this.cache_[key] = iconImage; + if (pattern) { + if (iconImage.getImageState() === ImageState.IDLE) { + iconImage.load(); + } + if (iconImage.getImageState() === ImageState.LOADING) { + iconImage.ready().then(() => { + this.patternCache_[key] = getSharedCanvasContext2D().createPattern( + iconImage.getImage(1), + 'repeat', + ); + }); + } else { + this.patternCache_[key] = getSharedCanvasContext2D().createPattern( + iconImage.getImage(1), + 'repeat', + ); + } + } + if (!update) { + ++this.cacheSize_; + } } /** - * Get the displacement of the shape - * @return {Array} Shape's center displacement + * 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 */ - getDisplacement() { - return this.displacement_; + setSize(maxCacheSize) { + this.maxCacheSize_ = maxCacheSize; + this.expire(); } + } - /** - * Get the declutter mode of the shape - * @return {"declutter"|"obstacle"|"none"|undefined} Shape's declutter mode - * @api - */ - getDeclutterMode() { - return this.declutterMode_; - } + /** + * @param {string} src Src. + * @param {?string} crossOrigin Cross origin. + * @param {import("../color.js").Color|string|null} color Color. + * @return {string} Cache key. + */ + function getCacheKey$1(src, crossOrigin, color) { + const colorString = color ? asArray(color) : 'null'; + return crossOrigin + ':' + src + ':' + colorString; + } - /** - * Get the anchor point in pixels. The anchor determines the center point for the - * symbolizer. - * @abstract - * @return {Array} Anchor. - */ - getAnchor() { - return abstract(); - } + /** + * The {@link module:ol/style/IconImageCache~IconImageCache} for + * {@link module:ol/style/Icon~Icon} images. + * @api + */ + const shared = new IconImageCache(); + + /** + * @module ol/style/IconImage + */ + + + /** + * @type {CanvasRenderingContext2D} + */ + let taintedTestContext = null; + class IconImage extends Target { /** - * Get the image element for the symbolizer. - * @abstract - * @param {number} pixelRatio Pixel ratio. - * @return {HTMLCanvasElement|HTMLVideoElement|HTMLImageElement} Image element. + * @param {HTMLImageElement|HTMLCanvasElement|ImageBitmap|null} image Image. + * @param {string|undefined} src Src. + * @param {?string} crossOrigin Cross origin. + * @param {import("../ImageState.js").default|undefined} imageState Image state. + * @param {import("../color.js").Color|string|null} color Color. */ - getImage(pixelRatio) { - return abstract(); + constructor(image, src, crossOrigin, imageState, color) { + super(); + + /** + * @private + * @type {HTMLImageElement|HTMLCanvasElement|ImageBitmap} + */ + this.hitDetectionImage_ = null; + + /** + * @private + * @type {HTMLImageElement|HTMLCanvasElement|ImageBitmap|null} + */ + this.image_ = image; + + /** + * @private + * @type {string|null} + */ + this.crossOrigin_ = crossOrigin; + + /** + * @private + * @type {Object} + */ + this.canvas_ = {}; + + /** + * @private + * @type {import("../color.js").Color|string|null} + */ + this.color_ = color; + + /** + * @private + * @type {import("../ImageState.js").default} + */ + this.imageState_ = imageState === undefined ? ImageState.IDLE : imageState; + + /** + * @private + * @type {import("../size.js").Size|null} + */ + this.size_ = + image && image.width && image.height ? [image.width, image.height] : null; + + /** + * @private + * @type {string|undefined} + */ + this.src_ = src; + + /** + * @private + */ + this.tainted_; + + /** + * @private + * @type {Promise|null} + */ + this.ready_ = null; } /** - * @abstract - * @return {HTMLCanvasElement|HTMLVideoElement|HTMLImageElement} Image element. + * @private */ - getHitDetectionImage() { - return abstract(); + initializeImage_() { + this.image_ = new Image(); + if (this.crossOrigin_ !== null) { + this.image_.crossOrigin = this.crossOrigin_; + } } /** - * Get the image pixel ratio. - * @param {number} pixelRatio Pixel ratio. - * @return {number} Pixel ratio. + * @private + * @return {boolean} The image canvas is tainted. */ - getPixelRatio(pixelRatio) { - return 1; + isTainted_() { + if (this.tainted_ === undefined && this.imageState_ === ImageState.LOADED) { + if (!taintedTestContext) { + taintedTestContext = createCanvasContext2D(1, 1, undefined, { + willReadFrequently: true, + }); + } + taintedTestContext.drawImage(this.image_, 0, 0); + try { + taintedTestContext.getImageData(0, 0, 1, 1); + this.tainted_ = false; + } catch (e) { + taintedTestContext = null; + this.tainted_ = true; + } + } + return this.tainted_ === true; } /** - * @abstract - * @return {import("../ImageState.js").default} Image state. + * @private */ - getImageState() { - return abstract(); + dispatchChangeEvent_() { + this.dispatchEvent(EventType.CHANGE); } /** - * @abstract - * @return {import("../size.js").Size} Image size. + * @private */ - getImageSize() { - return abstract(); + handleImageError_() { + this.imageState_ = ImageState.ERROR; + this.dispatchChangeEvent_(); } /** - * Get the origin of the symbolizer. - * @abstract - * @return {Array} Origin. + * @private */ - getOrigin() { - return abstract(); + handleImageLoad_() { + this.imageState_ = ImageState.LOADED; + this.size_ = [this.image_.width, this.image_.height]; + this.dispatchChangeEvent_(); } /** - * Get the size of the symbolizer (in pixels). - * @abstract - * @return {import("../size.js").Size} Size. + * @param {number} pixelRatio Pixel ratio. + * @return {HTMLImageElement|HTMLCanvasElement|ImageBitmap} Image or Canvas element or image bitmap. */ - getSize() { - return abstract(); + getImage(pixelRatio) { + if (!this.image_) { + this.initializeImage_(); + } + this.replaceColor_(pixelRatio); + return this.canvas_[pixelRatio] ? this.canvas_[pixelRatio] : this.image_; } /** - * Set the displacement. - * - * @param {Array} displacement Displacement. - * @api + * @param {number} pixelRatio Pixel ratio. + * @return {number} Image or Canvas element. */ - setDisplacement(displacement) { - this.displacement_ = displacement; + getPixelRatio(pixelRatio) { + this.replaceColor_(pixelRatio); + return this.canvas_[pixelRatio] ? pixelRatio : 1; } /** - * Set the opacity. - * - * @param {number} opacity Opacity. - * @api + * @return {import("../ImageState.js").default} Image state. */ - setOpacity(opacity) { - this.opacity_ = opacity; + getImageState() { + return this.imageState_; } /** - * Set whether to rotate the style with the view. - * - * @param {boolean} rotateWithView Rotate with map. - * @api + * @return {HTMLImageElement|HTMLCanvasElement|ImageBitmap} Image element. */ - setRotateWithView(rotateWithView) { - this.rotateWithView_ = rotateWithView; + getHitDetectionImage() { + if (!this.image_) { + this.initializeImage_(); + } + if (!this.hitDetectionImage_) { + if (this.isTainted_()) { + 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_; } /** - * Set the rotation. - * - * @param {number} rotation Rotation. - * @api + * Get the size of the icon (in pixels). + * @return {import("../size.js").Size} Image size. */ - setRotation(rotation) { - this.rotation_ = rotation; + getSize() { + return this.size_; } /** - * Set the scale. - * - * @param {number|import("../size.js").Size} scale Scale. - * @api + * @return {string|undefined} Image src. */ - setScale(scale) { - this.scale_ = scale; - this.scaleArray_ = toSize(scale); + getSrc() { + return this.src_; } /** - * @abstract - * @param {function(import("../events/Event.js").default): void} listener Listener function. + * Load not yet loaded URI. */ - listenImageChange(listener) { - abstract(); + load() { + if (this.imageState_ !== ImageState.IDLE) { + return; + } + if (!this.image_) { + this.initializeImage_(); + } + + this.imageState_ = ImageState.LOADING; + try { + if (this.src_ !== undefined) { + /** @type {HTMLImageElement} */ (this.image_).src = this.src_; + } + } catch (e) { + this.handleImageError_(); + } + if (this.image_ instanceof HTMLImageElement) { + decodeFallback(this.image_, this.src_) + .then((image) => { + this.image_ = image; + this.handleImageLoad_(); + }) + .catch(this.handleImageError_.bind(this)); + } } /** - * Load not yet loaded URI. - * @abstract + * @param {number} pixelRatio Pixel ratio. + * @private */ - load() { - abstract(); + replaceColor_(pixelRatio) { + if ( + !this.color_ || + this.canvas_[pixelRatio] || + this.imageState_ !== ImageState.LOADED + ) { + return; + } + + const image = this.image_; + const canvas = document.createElement('canvas'); + canvas.width = Math.ceil(image.width * pixelRatio); + canvas.height = Math.ceil(image.height * pixelRatio); + + const ctx = canvas.getContext('2d'); + ctx.scale(pixelRatio, pixelRatio); + ctx.drawImage(image, 0, 0); + + ctx.globalCompositeOperation = 'multiply'; + ctx.fillStyle = asString(this.color_); + ctx.fillRect(0, 0, canvas.width / pixelRatio, canvas.height / pixelRatio); + + ctx.globalCompositeOperation = 'destination-in'; + ctx.drawImage(image, 0, 0); + + this.canvas_[pixelRatio] = canvas; } /** - * @abstract - * @param {function(import("../events/Event.js").default): void} listener Listener function. + * @return {Promise} Promise that resolves when the image is loaded. */ - unlistenImageChange(listener) { - abstract(); + ready() { + if (!this.ready_) { + this.ready_ = new Promise((resolve) => { + if ( + this.imageState_ === ImageState.LOADED || + this.imageState_ === ImageState.ERROR + ) { + resolve(); + } else { + this.addEventListener(EventType.CHANGE, function onChange() { + if ( + this.imageState_ === ImageState.LOADED || + this.imageState_ === ImageState.ERROR + ) { + this.removeEventListener(EventType.CHANGE, onChange); + resolve(); + } + }); + } + }); + } + return this.ready_; } } - var ImageStyle$1 = ImageStyle; - /** - * @module ol/color + * @param {HTMLImageElement|HTMLCanvasElement|ImageBitmap|null} image Image. + * @param {string|undefined} cacheKey Src. + * @param {?string} crossOrigin Cross origin. + * @param {import("../ImageState.js").default|undefined} imageState Image state. + * @param {import("../color.js").Color|string|null} color Color. + * @param {boolean} [pattern] Also cache a `repeat` pattern with the icon image. + * @return {IconImage} Icon image. */ + function get(image, cacheKey, crossOrigin, imageState, color, pattern) { + let iconImage = + cacheKey === undefined + ? undefined + : shared.get(cacheKey, crossOrigin, color); + if (!iconImage) { + iconImage = new IconImage( + image, + image instanceof HTMLImageElement ? image.src || undefined : cacheKey, + crossOrigin, + imageState, + color, + ); + shared.set(cacheKey, crossOrigin, color, iconImage, pattern); + } + if ( + pattern && + iconImage && + !shared.getPattern(cacheKey, crossOrigin, color) + ) { + shared.set(cacheKey, crossOrigin, color, iconImage, pattern); + } + return iconImage; + } - /** - * A color represented as a short array [red, green, blue, alpha]. - * red, green, and blue should be integers in the range 0..255 inclusive. - * alpha should be a float in the range 0..1 inclusive. If no alpha value is - * given then `1` will be used. - * @typedef {Array} Color - * @api - */ + var IconImage$1 = IconImage; /** - * This RegExp matches # followed by 3, 4, 6, or 8 hex digits. - * @const - * @type {RegExp} - * @private + * @module ol/style/Fill */ - const HEX_COLOR_RE_ = /^#([a-f0-9]{3}|[a-f0-9]{4}(?:[a-f0-9]{2}){0,2})$/i; + /** - * Regular expression for matching potential named color style strings. - * @const - * @type {RegExp} - * @private + * @typedef {Object} Options + * @property {import("../color.js").Color|import("../colorlike.js").ColorLike|import('../colorlike.js').PatternDescriptor|null} [color=null] A color, + * gradient or pattern. + * See {@link module:ol/color~Color} and {@link module:ol/colorlike~ColorLike} for possible formats. For polygon fills (not for {@link import("./RegularShape.js").default} fills), + * a pattern can also be provided as {@link module:ol/colorlike~PatternDescriptor}. + * Default null; if null, the Canvas/renderer default black will be used. */ - const NAMED_COLOR_RE_ = /^([a-z]*)$|^hsla?\(.*\)$/i; /** - * Return the color as an rgba string. - * @param {Color|string} color Color. - * @return {string} Rgba string. + * @classdesc + * Set fill style for vector features. * @api */ - function asString(color) { - if (typeof color === 'string') { - return color; - } - return toString$3(color); - } + class Fill { + /** + * @param {Options} [options] Options. + */ + constructor(options) { + options = options || {}; - /** - * Return named color as an rgba string. - * @param {string} color Named color. - * @return {string} Rgb string. - */ - function fromNamed(color) { - const el = document.createElement('div'); - el.style.color = color; - if (el.style.color !== '') { - document.body.appendChild(el); - const rgb = getComputedStyle(el).color; - document.body.removeChild(el); - return rgb; - } - return ''; - } + /** + * @private + * @type {import("./IconImage.js").default|null} + */ + this.patternImage_ = null; - /** - * @param {string} s String. - * @return {Color} Color. - */ - const fromString = (function () { - // We maintain a small cache of parsed strings. To provide cheap LRU-like - // semantics, whenever the cache grows too large we simply delete an - // arbitrary 25% of the entries. + /** + * @private + * @type {import("../color.js").Color|import("../colorlike.js").ColorLike|import('../colorlike.js').PatternDescriptor|null} + */ + this.color_ = null; + if (options.color !== undefined) { + this.setColor(options.color); + } + } /** - * @const - * @type {number} + * Clones the style. The color is not cloned if it is an {@link module:ol/colorlike~ColorLike}. + * @return {Fill} The cloned style. + * @api */ - const MAX_CACHE_SIZE = 1024; + clone() { + const color = this.getColor(); + return new Fill({ + color: Array.isArray(color) ? color.slice() : color || undefined, + }); + } /** - * @type {Object} + * Get the fill color. + * @return {import("../color.js").Color|import("../colorlike.js").ColorLike|import('../colorlike.js').PatternDescriptor|null} Color. + * @api */ - const cache = {}; + getColor() { + return this.color_; + } /** - * @type {number} + * Set the color. + * + * @param {import("../color.js").Color|import("../colorlike.js").ColorLike|import('../colorlike.js').PatternDescriptor|null} color Color. + * @api */ - let cacheSize = 0; - - return ( - /** - * @param {string} s String. - * @return {Color} Color. - */ - function (s) { - let color; - if (cache.hasOwnProperty(s)) { - color = cache[s]; - } else { - if (cacheSize >= MAX_CACHE_SIZE) { - let i = 0; - for (const key in cache) { - if ((i++ & 3) === 0) { - delete cache[key]; - --cacheSize; - } - } - } - color = fromStringInternal_(s); - cache[s] = color; - ++cacheSize; + setColor(color) { + if (color !== null && typeof color === 'object' && 'src' in color) { + const patternImage = get( + null, + color.src, + 'anonymous', + undefined, + color.offset ? null : color.color ? color.color : null, + !(color.offset && color.size), + ); + patternImage.ready().then(() => { + this.patternImage_ = null; + }); + if (patternImage.getImageState() === ImageState.IDLE) { + patternImage.load(); + } + if (patternImage.getImageState() === ImageState.LOADING) { + this.patternImage_ = patternImage; } - return color; } - ); - })(); - - /** - * Return the color as an array. This function maintains a cache of calculated - * arrays which means the result should not be modified. - * @param {Color|string} color Color. - * @return {Color} Color. - * @api - */ - function asArray(color) { - if (Array.isArray(color)) { - return color; + this.color_ = color; } - return fromString(color); - } - - /** - * @param {string} s String. - * @private - * @return {Color} Color. - */ - function fromStringInternal_(s) { - let r, g, b, a, color; - if (NAMED_COLOR_RE_.exec(s)) { - s = fromNamed(s); + /** + * @return {boolean} The fill style is loading an image pattern. + */ + loading() { + return !!this.patternImage_; } - if (HEX_COLOR_RE_.exec(s)) { - // hex - const n = s.length - 1; // number of hex digits - let d; // number of digits per channel - if (n <= 4) { - d = 1; - } else { - d = 2; - } - const hasAlpha = n === 4 || n === 8; - r = parseInt(s.substr(1 + 0 * d, d), 16); - g = parseInt(s.substr(1 + 1 * d, d), 16); - b = parseInt(s.substr(1 + 2 * d, d), 16); - if (hasAlpha) { - a = parseInt(s.substr(1 + 3 * d, d), 16); - } else { - a = 255; - } - if (d == 1) { - r = (r << 4) + r; - g = (g << 4) + g; - b = (b << 4) + b; - if (hasAlpha) { - a = (a << 4) + a; - } - } - color = [r, g, b, a / 255]; - } else if (s.startsWith('rgba(')) { - // rgba() - color = s.slice(5, -1).split(',').map(Number); - normalize(color); - } else if (s.startsWith('rgb(')) { - // rgb() - color = s.slice(4, -1).split(',').map(Number); - color.push(1); - normalize(color); - } else { - assert(false, 14); // Invalid color + /** + * @return {Promise} `false` or a promise that resolves when the style is ready to use. + */ + ready() { + return this.patternImage_ ? this.patternImage_.ready() : Promise.resolve(); } - return color; } + var Fill$1 = Fill; + /** - * TODO this function is only used in the test, we probably shouldn't export it - * @param {Color} color Color. - * @return {Color} Clamped color. + * @module ol/geom/flat/interpolate */ - function normalize(color) { - color[0] = clamp((color[0] + 0.5) | 0, 0, 255); - color[1] = clamp((color[1] + 0.5) | 0, 0, 255); - color[2] = clamp((color[2] + 0.5) | 0, 0, 255); - color[3] = clamp(color[3], 0, 1); - return color; - } /** - * @param {Color} color Color. - * @return {string} String. + * @param {Array} flatCoordinates Flat coordinates. + * @param {number} offset Offset. + * @param {number} end End. + * @param {number} stride Stride. + * @param {number} fraction Fraction. + * @param {Array} [dest] Destination. + * @param {number} [dimension] Destination dimension (default is `2`) + * @return {Array} Destination. */ - function toString$3(color) { - let r = color[0]; - if (r != (r | 0)) { - r = (r + 0.5) | 0; + function interpolatePoint( + flatCoordinates, + offset, + end, + stride, + fraction, + dest, + dimension, + ) { + let o, t; + const n = (end - offset) / stride; + if (n === 1) { + o = offset; + } else if (n === 2) { + o = offset; + t = fraction; + } else if (n !== 0) { + let x1 = flatCoordinates[offset]; + let y1 = flatCoordinates[offset + 1]; + let length = 0; + const cumulativeLengths = [0]; + for (let i = offset + stride; i < end; i += stride) { + const x2 = flatCoordinates[i]; + const y2 = flatCoordinates[i + 1]; + length += Math.sqrt((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1)); + cumulativeLengths.push(length); + x1 = x2; + y1 = y2; + } + const target = fraction * length; + const index = binarySearch(cumulativeLengths, target); + if (index < 0) { + t = + (target - cumulativeLengths[-index - 2]) / + (cumulativeLengths[-index - 1] - cumulativeLengths[-index - 2]); + o = offset + (-index - 2) * stride; + } else { + o = offset + index * stride; + } } - let g = color[1]; - if (g != (g | 0)) { - g = (g + 0.5) | 0; + dimension = dimension > 1 ? dimension : 2; + dest = dest ? dest : new Array(dimension); + for (let i = 0; i < dimension; ++i) { + dest[i] = + o === undefined + ? NaN + : t === undefined + ? flatCoordinates[o + i] + : lerp$1(flatCoordinates[o + i], flatCoordinates[o + stride + i], t); } - let b = color[2]; - if (b != (b | 0)) { - b = (b + 0.5) | 0; - } - const a = color[3] === undefined ? 1 : Math.round(color[3] * 100) / 100; - return 'rgba(' + r + ',' + g + ',' + b + ',' + a + ')'; + return dest; } /** - * @param {string} s String. - * @return {boolean} Whether the string is actually a valid color + * @param {Array} flatCoordinates Flat coordinates. + * @param {number} offset Offset. + * @param {number} end End. + * @param {number} stride Stride. + * @param {number} m M. + * @param {boolean} extrapolate Extrapolate. + * @return {import("../../coordinate.js").Coordinate|null} Coordinate. */ - function isStringColor(s) { - if (NAMED_COLOR_RE_.test(s)) { - s = fromNamed(s); + function lineStringCoordinateAtM( + flatCoordinates, + offset, + end, + stride, + m, + extrapolate, + ) { + if (end == offset) { + return null; } - return HEX_COLOR_RE_.test(s) || s.startsWith('rgba(') || s.startsWith('rgb('); - } - - /** - * @module ol/colorlike - */ - - /** - * A type accepted by CanvasRenderingContext2D.fillStyle - * or CanvasRenderingContext2D.strokeStyle. - * Represents a color, pattern, or gradient. The origin for patterns and - * gradients as fill style is an increment of 512 css pixels from map coordinate - * `[0, 0]`. For seamless repeat patterns, width and height of the pattern image - * must be a factor of two (2, 4, 8, ..., 512). - * - * @typedef {string|CanvasPattern|CanvasGradient} ColorLike - * @api - */ - - /** - * @param {import("./color.js").Color|ColorLike} color Color. - * @return {ColorLike} The color as an {@link ol/colorlike~ColorLike}. - * @api - */ - function asColorLike(color) { - if (Array.isArray(color)) { - return toString$3(color); + let coordinate; + if (m < flatCoordinates[offset + stride - 1]) { + if (extrapolate) { + coordinate = flatCoordinates.slice(offset, offset + stride); + coordinate[stride - 1] = m; + return coordinate; + } + return null; } - return color; - } - - /** - * @module ol/dom - */ - - //FIXME Move this function to the canvas module - /** - * Create an html canvas element and returns its 2d context. - * @param {number} [width] Canvas width. - * @param {number} [height] Canvas height. - * @param {Array} [canvasPool] Canvas pool to take existing canvas from. - * @param {CanvasRenderingContext2DSettings} [settings] CanvasRenderingContext2DSettings - * @return {CanvasRenderingContext2D} The context. - */ - function createCanvasContext2D(width, height, canvasPool, settings) { - /** @type {HTMLCanvasElement|OffscreenCanvas} */ - let canvas; - if (canvasPool && canvasPool.length) { - canvas = canvasPool.shift(); - } else if (WORKER_OFFSCREEN_CANVAS) { - canvas = new OffscreenCanvas(width || 300, height || 300); - } else { - canvas = document.createElement('canvas'); + if (flatCoordinates[end - 1] < m) { + if (extrapolate) { + coordinate = flatCoordinates.slice(end - stride, end); + coordinate[stride - 1] = m; + return coordinate; + } + return null; } - if (width) { - canvas.width = width; + // FIXME use O(1) search + if (m == flatCoordinates[offset + stride - 1]) { + return flatCoordinates.slice(offset, offset + stride); } - if (height) { - canvas.height = height; + let lo = offset / stride; + let hi = end / stride; + while (lo < hi) { + const mid = (lo + hi) >> 1; + if (m < flatCoordinates[(mid + 1) * stride - 1]) { + hi = mid; + } else { + lo = mid + 1; + } } - //FIXME Allow OffscreenCanvasRenderingContext2D as return type - return /** @type {CanvasRenderingContext2D} */ ( - canvas.getContext('2d', settings) - ); - } - - /** - * Releases canvas memory to avoid exceeding memory limits in Safari. - * See https://pqina.nl/blog/total-canvas-memory-use-exceeds-the-maximum-limit/ - * @param {CanvasRenderingContext2D} context Context. - */ - function releaseCanvas$1(context) { - const canvas = context.canvas; - canvas.width = 1; - canvas.height = 1; - context.clearRect(0, 0, 1, 1); - } - - /** - * Get the current computed width for the given element including margin, - * padding and border. - * Equivalent to jQuery's `$(el).outerWidth(true)`. - * @param {!HTMLElement} element Element. - * @return {number} The width. - */ - function outerWidth(element) { - let width = element.offsetWidth; - const style = getComputedStyle(element); - width += parseInt(style.marginLeft, 10) + parseInt(style.marginRight, 10); - - return width; - } - - /** - * Get the current computed height for the given element including margin, - * padding and border. - * Equivalent to jQuery's `$(el).outerHeight(true)`. - * @param {!HTMLElement} element Element. - * @return {number} The height. - */ - function outerHeight(element) { - let height = element.offsetHeight; - const style = getComputedStyle(element); - height += parseInt(style.marginTop, 10) + parseInt(style.marginBottom, 10); - - return height; - } - - /** - * @param {Node} newNode Node to replace old node - * @param {Node} oldNode The node to be replaced - */ - function replaceNode(newNode, oldNode) { - const parent = oldNode.parentNode; - if (parent) { - parent.replaceChild(newNode, oldNode); + const m0 = flatCoordinates[lo * stride - 1]; + if (m == m0) { + return flatCoordinates.slice((lo - 1) * stride, (lo - 1) * stride + stride); } - } - - /** - * @param {Node} node The node to remove. - * @return {Node|null} The node that was removed or null. - */ - function removeNode$1(node) { - return node && node.parentNode ? node.parentNode.removeChild(node) : null; - } - - /** - * @param {Node} node The node to remove the children from. - */ - function removeChildren(node) { - while (node.lastChild) { - node.removeChild(node.lastChild); + const m1 = flatCoordinates[(lo + 1) * stride - 1]; + const t = (m - m0) / (m1 - m0); + coordinate = []; + for (let i = 0; i < stride - 1; ++i) { + coordinate.push( + lerp$1( + flatCoordinates[(lo - 1) * stride + i], + flatCoordinates[lo * stride + i], + t, + ), + ); } + coordinate.push(m); + return coordinate; } /** - * Transform the children of a parent node so they match the - * provided list of children. This function aims to efficiently - * remove, add, and reorder child nodes while maintaining a simple - * implementation (it is not guaranteed to minimize DOM operations). - * @param {Node} node The parent node whose children need reworking. - * @param {Array} children The desired children. + * @param {Array} flatCoordinates Flat coordinates. + * @param {number} offset Offset. + * @param {Array} ends Ends. + * @param {number} stride Stride. + * @param {number} m M. + * @param {boolean} extrapolate Extrapolate. + * @param {boolean} interpolate Interpolate. + * @return {import("../../coordinate.js").Coordinate|null} Coordinate. */ - function replaceChildren(node, children) { - const oldChildren = node.childNodes; - - for (let i = 0; true; ++i) { - const oldChild = oldChildren[i]; - const newChild = children[i]; - - // check if our work is done - if (!oldChild && !newChild) { - break; + function lineStringsCoordinateAtM( + flatCoordinates, + offset, + ends, + stride, + m, + extrapolate, + interpolate, + ) { + if (interpolate) { + return lineStringCoordinateAtM( + flatCoordinates, + offset, + ends[ends.length - 1], + stride, + m, + extrapolate, + ); + } + let coordinate; + if (m < flatCoordinates[stride - 1]) { + if (extrapolate) { + coordinate = flatCoordinates.slice(0, stride); + coordinate[stride - 1] = m; + return coordinate; } - - // check if children match - if (oldChild === newChild) { - continue; + return null; + } + if (flatCoordinates[flatCoordinates.length - 1] < m) { + if (extrapolate) { + coordinate = flatCoordinates.slice(flatCoordinates.length - stride); + coordinate[stride - 1] = m; + return coordinate; } - - // check if a new child needs to be added - if (!oldChild) { - node.appendChild(newChild); + return null; + } + for (let i = 0, ii = ends.length; i < ii; ++i) { + const end = ends[i]; + if (offset == end) { continue; } - - // check if an old child needs to be removed - if (!newChild) { - node.removeChild(oldChild); - --i; - continue; + if (m < flatCoordinates[offset + stride - 1]) { + return null; } - - // reorder - node.insertBefore(newChild, oldChild); + if (m <= flatCoordinates[end - 1]) { + return lineStringCoordinateAtM( + flatCoordinates, + offset, + end, + stride, + m, + false, + ); + } + offset = end; } + return null; } /** - * @module ol/css - */ - - /** - * @typedef {Object} FontParameters - * @property {string} style Style. - * @property {string} variant Variant. - * @property {string} weight Weight. - * @property {string} size Size. - * @property {string} lineHeight LineHeight. - * @property {string} family Family. - * @property {Array} families Families. + * @module ol/geom/flat/length */ /** - * The CSS class for hidden feature. - * - * @const - * @type {string} + * @param {Array} flatCoordinates Flat coordinates. + * @param {number} offset Offset. + * @param {number} end End. + * @param {number} stride Stride. + * @return {number} Length. */ - const CLASS_HIDDEN = 'ol-hidden'; + function lineStringLength(flatCoordinates, offset, end, stride) { + let x1 = flatCoordinates[offset]; + let y1 = flatCoordinates[offset + 1]; + let length = 0; + for (let i = offset + stride; i < end; i += stride) { + const x2 = flatCoordinates[i]; + const y2 = flatCoordinates[i + 1]; + length += Math.sqrt((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1)); + x1 = x2; + y1 = y2; + } + return length; + } /** - * The CSS class that we'll give the DOM elements to have them selectable. - * - * @const - * @type {string} + * @module ol/geom/LineString */ - const CLASS_SELECTABLE = 'ol-selectable'; /** - * The CSS class that we'll give the DOM elements to have them unselectable. + * @classdesc + * Linestring geometry. * - * @const - * @type {string} + * @api */ - const CLASS_UNSELECTABLE = 'ol-unselectable'; + class LineString extends SimpleGeometry$1 { + /** + * @param {Array|Array} coordinates Coordinates. + * For internal use, flat coordinates in combination with `layout` are also accepted. + * @param {import("./Geometry.js").GeometryLayout} [layout] Layout. + */ + constructor(coordinates, layout) { + super(); - /** - * The CSS class for unsupported feature. - * - * @const - * @type {string} - */ - const CLASS_UNSUPPORTED = 'ol-unsupported'; + /** + * @private + * @type {import("../coordinate.js").Coordinate|null} + */ + this.flatMidpoint_ = null; - /** - * The CSS class for controls. - * - * @const - * @type {string} - */ - const CLASS_CONTROL = 'ol-control'; + /** + * @private + * @type {number} + */ + this.flatMidpointRevision_ = -1; - /** - * The CSS class that we'll give the DOM elements that are collapsed, i.e. - * to those elements which usually can be expanded. - * - * @const - * @type {string} - */ - const CLASS_COLLAPSED = 'ol-collapsed'; + /** + * @private + * @type {number} + */ + this.maxDelta_ = -1; - /** - * From https://stackoverflow.com/questions/10135697/regex-to-parse-any-css-font - * @type {RegExp} - */ - const fontRegEx = new RegExp( - [ - '^\\s*(?=(?:(?:[-a-z]+\\s*){0,2}(italic|oblique))?)', - '(?=(?:(?:[-a-z]+\\s*){0,2}(small-caps))?)', - '(?=(?:(?:[-a-z]+\\s*){0,2}(bold(?:er)?|lighter|[1-9]00 ))?)', - '(?:(?:normal|\\1|\\2|\\3)\\s*){0,3}((?:xx?-)?', - '(?:small|large)|medium|smaller|larger|[\\.\\d]+(?:\\%|in|[cem]m|ex|p[ctx]))', - '(?:\\s*\\/\\s*(normal|[\\.\\d]+(?:\\%|in|[cem]m|ex|p[ctx])?))', - '?\\s*([-,\\"\\\'\\sa-z]+?)\\s*$', - ].join(''), - 'i' - ); - const fontRegExMatchIndex = [ - 'style', - 'variant', - 'weight', - 'size', - 'lineHeight', - 'family', - ]; + /** + * @private + * @type {number} + */ + this.maxDeltaRevision_ = -1; - /** - * Get the list of font families from a font spec. Note that this doesn't work - * for font families that have commas in them. - * @param {string} fontSpec The CSS font property. - * @return {FontParameters|null} The font parameters (or null if the input spec is invalid). - */ - const getFontParameters = function (fontSpec) { - const match = fontSpec.match(fontRegEx); - if (!match) { - return null; - } - const style = /** @type {FontParameters} */ ({ - lineHeight: 'normal', - size: '1.2em', - style: 'normal', - weight: 'normal', - variant: 'normal', - }); - for (let i = 0, ii = fontRegExMatchIndex.length; i < ii; ++i) { - const value = match[i + 1]; - if (value !== undefined) { - style[fontRegExMatchIndex[i]] = value; + if (layout !== undefined && !Array.isArray(coordinates[0])) { + this.setFlatCoordinates( + layout, + /** @type {Array} */ (coordinates), + ); + } else { + this.setCoordinates( + /** @type {Array} */ ( + coordinates + ), + layout, + ); } } - style.families = style.family.split(/,\s?/); - return style; - }; - - /** - * @module ol/render/canvas - */ - /** - * @typedef {'Circle' | 'Image' | 'LineString' | 'Polygon' | 'Text' | 'Default'} BuilderType - */ + /** + * Append the passed coordinate to the coordinates of the linestring. + * @param {import("../coordinate.js").Coordinate} coordinate Coordinate. + * @api + */ + appendCoordinate(coordinate) { + extend$1(this.flatCoordinates, coordinate); + this.changed(); + } - /** - * @typedef {Object} FillState - * @property {import("../colorlike.js").ColorLike} fillStyle FillStyle. - */ + /** + * Make a complete copy of the geometry. + * @return {!LineString} Clone. + * @api + */ + clone() { + const lineString = new LineString( + this.flatCoordinates.slice(), + this.layout, + ); + lineString.applyProperties(this); + return lineString; + } - /** - * @typedef Label - * @property {number} width Width. - * @property {number} height Height. - * @property {Array} contextInstructions ContextInstructions. - */ + /** + * @param {number} x X. + * @param {number} y Y. + * @param {import("../coordinate.js").Coordinate} closestPoint Closest point. + * @param {number} minSquaredDistance Minimum squared distance. + * @return {number} Minimum squared distance. + */ + 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, + ); + } - /** - * @typedef {Object} FillStrokeState - * @property {import("../colorlike.js").ColorLike} [currentFillStyle] Current FillStyle. - * @property {import("../colorlike.js").ColorLike} [currentStrokeStyle] Current StrokeStyle. - * @property {CanvasLineCap} [currentLineCap] Current LineCap. - * @property {Array} currentLineDash Current LineDash. - * @property {number} [currentLineDashOffset] Current LineDashOffset. - * @property {CanvasLineJoin} [currentLineJoin] Current LineJoin. - * @property {number} [currentLineWidth] Current LineWidth. - * @property {number} [currentMiterLimit] Current MiterLimit. - * @property {number} [lastStroke] Last stroke. - * @property {import("../colorlike.js").ColorLike} [fillStyle] FillStyle. - * @property {import("../colorlike.js").ColorLike} [strokeStyle] StrokeStyle. - * @property {CanvasLineCap} [lineCap] LineCap. - * @property {Array} lineDash LineDash. - * @property {number} [lineDashOffset] LineDashOffset. - * @property {CanvasLineJoin} [lineJoin] LineJoin. - * @property {number} [lineWidth] LineWidth. - * @property {number} [miterLimit] MiterLimit. - */ - - /** - * @typedef {Object} StrokeState - * @property {CanvasLineCap} lineCap LineCap. - * @property {Array} lineDash LineDash. - * @property {number} lineDashOffset LineDashOffset. - * @property {CanvasLineJoin} lineJoin LineJoin. - * @property {number} lineWidth LineWidth. - * @property {number} miterLimit MiterLimit. - * @property {import("../colorlike.js").ColorLike} strokeStyle StrokeStyle. - */ + /** + * 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, import("../coordinate.js").Coordinate, import("../coordinate.js").Coordinate): T} callback Function + * called for each segment. The function will receive two arguments, the start and end coordinates of the segment. + * @return {T|boolean} Value. + * @template T,S + * @api + */ + forEachSegment(callback) { + return forEach( + this.flatCoordinates, + 0, + this.flatCoordinates.length, + this.stride, + callback, + ); + } - /** - * @typedef {Object} TextState - * @property {string} font Font. - * @property {CanvasTextAlign} [textAlign] TextAlign. - * @property {number} [repeat] Repeat. - * @property {import("../style/Text.js").TextJustify} [justify] Justify. - * @property {CanvasTextBaseline} textBaseline TextBaseline. - * @property {import("../style/Text.js").TextPlacement} [placement] Placement. - * @property {number} [maxAngle] MaxAngle. - * @property {boolean} [overflow] Overflow. - * @property {import("../style/Fill.js").default} [backgroundFill] BackgroundFill. - * @property {import("../style/Stroke.js").default} [backgroundStroke] BackgroundStroke. - * @property {import("../size.js").Size} [scale] Scale. - * @property {Array} [padding] Padding. - */ + /** + * Returns the coordinate at `m` using linear interpolation, or `null` if no + * such coordinate exists. + * + * `extrapolate` controls extrapolation beyond the range of Ms in the + * MultiLineString. If `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} [extrapolate] Extrapolate. Default is `false`. + * @return {import("../coordinate.js").Coordinate|null} Coordinate. + * @api + */ + getCoordinateAtM(m, extrapolate) { + if (this.layout != 'XYM' && this.layout != 'XYZM') { + return null; + } + extrapolate = extrapolate !== undefined ? extrapolate : false; + return lineStringCoordinateAtM( + this.flatCoordinates, + 0, + this.flatCoordinates.length, + this.stride, + m, + extrapolate, + ); + } - /** - * @typedef {Object} SerializableInstructions - * @property {Array<*>} instructions The rendering instructions. - * @property {Array<*>} hitDetectionInstructions The rendering hit detection instructions. - * @property {Array} coordinates The array of all coordinates. - * @property {!Object} [textStates] The text states (decluttering). - * @property {!Object} [fillStates] The fill states (decluttering). - * @property {!Object} [strokeStates] The stroke states (decluttering). - */ + /** + * Return the coordinates of the linestring. + * @return {Array} Coordinates. + * @api + */ + getCoordinates() { + return inflateCoordinates( + this.flatCoordinates, + 0, + this.flatCoordinates.length, + this.stride, + ); + } - /** - * @typedef {Object} DeclutterImageWithText - */ + /** + * 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 {import("../coordinate.js").Coordinate} [dest] Optional coordinate whose values will + * be modified. If not provided, a new coordinate will be returned. + * @return {import("../coordinate.js").Coordinate} Coordinate of the interpolated point. + * @api + */ + getCoordinateAt(fraction, dest) { + return interpolatePoint( + this.flatCoordinates, + 0, + this.flatCoordinates.length, + this.stride, + fraction, + dest, + this.stride, + ); + } - /** - * @const - * @type {string} - */ - const defaultFont = '10px sans-serif'; + /** + * 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, + ); + } - /** - * @const - * @type {string} - */ - const defaultFillStyle = '#000'; + /** + * @return {Array} Flat midpoint. + */ + getFlatMidpoint() { + if (this.flatMidpointRevision_ != this.getRevision()) { + this.flatMidpoint_ = this.getCoordinateAt( + 0.5, + this.flatMidpoint_ ?? undefined, + ); + this.flatMidpointRevision_ = this.getRevision(); + } + return /** @type {Array} */ (this.flatMidpoint_); + } - /** - * @const - * @type {CanvasLineCap} - */ - const defaultLineCap = 'round'; + /** + * @param {number} squaredTolerance Squared tolerance. + * @return {LineString} Simplified LineString. + * @protected + */ + getSimplifiedGeometryInternal(squaredTolerance) { + /** @type {Array} */ + const simplifiedFlatCoordinates = []; + simplifiedFlatCoordinates.length = douglasPeucker( + this.flatCoordinates, + 0, + this.flatCoordinates.length, + this.stride, + squaredTolerance, + simplifiedFlatCoordinates, + 0, + ); + return new LineString(simplifiedFlatCoordinates, 'XY'); + } - /** - * @const - * @type {Array} - */ - const defaultLineDash = []; + /** + * Get the type of this geometry. + * @return {import("./Geometry.js").Type} Geometry type. + * @api + */ + getType() { + return 'LineString'; + } - /** - * @const - * @type {number} - */ - const defaultLineDashOffset = 0; + /** + * Test if the geometry and the passed extent intersect. + * @param {import("../extent.js").Extent} extent Extent. + * @return {boolean} `true` if the geometry and the extent intersect. + * @api + */ + intersectsExtent(extent) { + return intersectsLineString( + this.flatCoordinates, + 0, + this.flatCoordinates.length, + this.stride, + extent, + ); + } - /** - * @const - * @type {CanvasLineJoin} - */ - const defaultLineJoin = 'round'; + /** + * Set the coordinates of the linestring. + * @param {!Array} coordinates Coordinates. + * @param {import("./Geometry.js").GeometryLayout} [layout] Layout. + * @api + */ + setCoordinates(coordinates, layout) { + this.setLayout(layout, coordinates, 1); + if (!this.flatCoordinates) { + this.flatCoordinates = []; + } + this.flatCoordinates.length = deflateCoordinates( + this.flatCoordinates, + 0, + coordinates, + this.stride, + ); + this.changed(); + } + } - /** - * @const - * @type {number} - */ - const defaultMiterLimit = 10; + var LineString$1 = LineString; /** - * @const - * @type {import("../colorlike.js").ColorLike} + * @module ol/style/Stroke */ - const defaultStrokeStyle = '#000'; /** - * @const - * @type {CanvasTextAlign} + * @typedef {Object} Options + * @property {import("../color.js").Color|import("../colorlike.js").ColorLike} [color] A color, gradient or pattern. + * See {@link module:ol/color~Color} and {@link module:ol/colorlike~ColorLike} for possible formats. + * Default null; if null, the Canvas/renderer default black will be used. + * @property {CanvasLineCap} [lineCap='round'] Line cap style: `butt`, `round`, or `square`. + * @property {CanvasLineJoin} [lineJoin='round'] Line join style: `bevel`, `round`, or `miter`. + * @property {Array} [lineDash] Line dash pattern. Default is `null` (no dash). + * @property {number} [lineDashOffset=0] Line dash offset. + * @property {number} [miterLimit=10] Miter limit. + * @property {number} [width] Width. */ - const defaultTextAlign = 'center'; /** - * @const - * @type {CanvasTextBaseline} + * @classdesc + * Set stroke style for vector features. + * 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. + * @api */ - const defaultTextBaseline = 'middle'; + class Stroke { + /** + * @param {Options} [options] Options. + */ + constructor(options) { + options = options || {}; - /** - * @const - * @type {Array} - */ - const defaultPadding = [0, 0, 0, 0]; + /** + * @private + * @type {import("../color.js").Color|import("../colorlike.js").ColorLike} + */ + this.color_ = options.color !== undefined ? options.color : null; - /** - * @const - * @type {number} - */ - const defaultLineWidth = 1; + /** + * @private + * @type {CanvasLineCap|undefined} + */ + this.lineCap_ = options.lineCap; - /** - * @type {BaseObject} - */ - const checkedFonts = new olObject(); + /** + * @private + * @type {Array|null} + */ + this.lineDash_ = options.lineDash !== undefined ? options.lineDash : null; - /** - * @type {CanvasRenderingContext2D} - */ - let measureContext$1 = null; + /** + * @private + * @type {number|undefined} + */ + this.lineDashOffset_ = options.lineDashOffset; - /** - * @type {string} - */ - let measureFont; + /** + * @private + * @type {CanvasLineJoin|undefined} + */ + this.lineJoin_ = options.lineJoin; - /** - * @type {!Object} - */ - const textHeights = {}; + /** + * @private + * @type {number|undefined} + */ + this.miterLimit_ = options.miterLimit; - /** - * Clears the label cache when a font becomes available. - * @param {string} fontSpec CSS font spec. - */ - const registerFont = (function () { - const retries = 100; - const size = '32px '; - const referenceFonts = ['monospace', 'serif']; - const len = referenceFonts.length; - const text = 'wmytzilWMYTZIL@#/&?$%10\uF013'; - let interval, referenceWidth; + /** + * @private + * @type {number|undefined} + */ + this.width_ = options.width; + } /** - * @param {string} fontStyle Css font-style - * @param {string} fontWeight Css font-weight - * @param {*} fontFamily Css font-family - * @return {boolean} Font with style and weight is available + * Clones the style. + * @return {Stroke} The cloned style. + * @api */ - function isAvailable(fontStyle, fontWeight, fontFamily) { - let available = true; - for (let i = 0; i < len; ++i) { - const referenceFont = referenceFonts[i]; - referenceWidth = measureTextWidth( - fontStyle + ' ' + fontWeight + ' ' + size + referenceFont, - text - ); - if (fontFamily != referenceFont) { - const width = measureTextWidth( - fontStyle + - ' ' + - fontWeight + - ' ' + - size + - fontFamily + - ',' + - referenceFont, - text - ); - // If width and referenceWidth are the same, then the fallback was used - // instead of the font we wanted, so the font is not available. - available = available && width != referenceWidth; - } - } - if (available) { - return true; - } - return false; + clone() { + const color = this.getColor(); + return new Stroke({ + color: Array.isArray(color) ? 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(), + }); } - function check() { - let done = true; - const fonts = checkedFonts.getKeys(); - for (let i = 0, ii = fonts.length; i < ii; ++i) { - const font = fonts[i]; - if (checkedFonts.get(font) < retries) { - if (isAvailable.apply(this, font.split('\n'))) { - clear$2(textHeights); - // Make sure that loaded fonts are picked up by Safari - measureContext$1 = null; - measureFont = undefined; - checkedFonts.set(font, retries); - } else { - checkedFonts.set(font, checkedFonts.get(font) + 1, true); - done = false; - } - } - } - if (done) { - clearInterval(interval); - interval = undefined; - } + /** + * Get the stroke color. + * @return {import("../color.js").Color|import("../colorlike.js").ColorLike} Color. + * @api + */ + getColor() { + return this.color_; } - return function (fontSpec) { - const font = getFontParameters(fontSpec); - if (!font) { - return; - } - const families = font.families; - for (let i = 0, ii = families.length; i < ii; ++i) { - const family = families[i]; - const key = font.style + '\n' + font.weight + '\n' + family; - if (checkedFonts.get(key) === undefined) { - checkedFonts.set(key, retries, true); - if (!isAvailable(font.style, font.weight, family)) { - checkedFonts.set(key, 0, true); - if (interval === undefined) { - interval = setInterval(check, 32); - } - } - } - } - }; - })(); + /** + * Get the line cap type for the stroke. + * @return {CanvasLineCap|undefined} Line cap. + * @api + */ + getLineCap() { + return this.lineCap_; + } - /** - * @param {string} font Font to use for measuring. - * @return {import("../size.js").Size} Measurement. - */ - const measureTextHeight = (function () { /** - * @type {HTMLDivElement} + * Get the line dash style for the stroke. + * @return {Array|null} Line dash. + * @api */ - let measureElement; - return function (fontSpec) { - let height = textHeights[fontSpec]; - if (height == undefined) { - if (WORKER_OFFSCREEN_CANVAS) { - const font = getFontParameters(fontSpec); - const metrics = measureText$1(fontSpec, 'Žg'); - const lineHeight = isNaN(Number(font.lineHeight)) - ? 1.2 - : Number(font.lineHeight); - height = - lineHeight * - (metrics.actualBoundingBoxAscent + metrics.actualBoundingBoxDescent); - } else { - if (!measureElement) { - measureElement = document.createElement('div'); - measureElement.innerHTML = 'M'; - measureElement.style.minHeight = '0'; - measureElement.style.maxHeight = 'none'; - measureElement.style.height = 'auto'; - measureElement.style.padding = '0'; - measureElement.style.border = 'none'; - measureElement.style.position = 'absolute'; - measureElement.style.display = 'block'; - measureElement.style.left = '-99999px'; - } - measureElement.style.font = fontSpec; - document.body.appendChild(measureElement); - height = measureElement.offsetHeight; - document.body.removeChild(measureElement); - } - textHeights[fontSpec] = height; - } - return height; - }; - })(); + getLineDash() { + return this.lineDash_; + } - /** - * @param {string} font Font. - * @param {string} text Text. - * @return {TextMetrics} Text metrics. - */ - function measureText$1(font, text) { - if (!measureContext$1) { - measureContext$1 = createCanvasContext2D(1, 1); + /** + * Get the line dash offset for the stroke. + * @return {number|undefined} Line dash offset. + * @api + */ + getLineDashOffset() { + return this.lineDashOffset_; } - if (font != measureFont) { - measureContext$1.font = font; - measureFont = measureContext$1.font; + + /** + * Get the line join type for the stroke. + * @return {CanvasLineJoin|undefined} Line join. + * @api + */ + getLineJoin() { + return this.lineJoin_; } - return measureContext$1.measureText(text); - } - /** - * @param {string} font Font. - * @param {string} text Text. - * @return {number} Width. - */ - function measureTextWidth(font, text) { - return measureText$1(font, text).width; - } + /** + * Get the miter limit for the stroke. + * @return {number|undefined} Miter limit. + * @api + */ + getMiterLimit() { + return this.miterLimit_; + } - /** - * Measure text width using a cache. - * @param {string} font The font. - * @param {string} text The text to measure. - * @param {Object} cache A lookup of cached widths by text. - * @return {number} The text width. - */ - function measureAndCacheTextWidth(font, text, cache) { - if (text in cache) { - return cache[text]; + /** + * Get the stroke width. + * @return {number|undefined} Width. + * @api + */ + getWidth() { + return this.width_; } - const width = text - .split('\n') - .reduce((prev, curr) => Math.max(prev, measureTextWidth(font, curr)), 0); - cache[text] = width; - return width; - } - /** - * @param {TextState} baseStyle Base style. - * @param {Array} chunks Text chunks to measure. - * @return {{width: number, height: number, widths: Array, heights: Array, lineWidths: Array}}} Text metrics. - */ - function getTextDimensions(baseStyle, chunks) { - const widths = []; - const heights = []; - const lineWidths = []; - let width = 0; - let lineWidth = 0; - let height = 0; - let lineHeight = 0; - for (let i = 0, ii = chunks.length; i <= ii; i += 2) { - const text = chunks[i]; - if (text === '\n' || i === ii) { - width = Math.max(width, lineWidth); - lineWidths.push(lineWidth); - lineWidth = 0; - height += lineHeight; - continue; - } - const font = chunks[i + 1] || baseStyle.font; - const currentWidth = measureTextWidth(font, text); - widths.push(currentWidth); - lineWidth += currentWidth; - const currentHeight = measureTextHeight(font); - heights.push(currentHeight); - lineHeight = Math.max(lineHeight, currentHeight); + /** + * Set the color. + * + * @param {import("../color.js").Color|import("../colorlike.js").ColorLike} color Color. + * @api + */ + setColor(color) { + this.color_ = color; } - return {width, height, widths, heights, lineWidths}; - } - /** - * @param {CanvasRenderingContext2D} context Context. - * @param {import("../transform.js").Transform|null} transform Transform. - * @param {number} opacity Opacity. - * @param {Label|HTMLCanvasElement|HTMLImageElement|HTMLVideoElement} labelOrImage Label. - * @param {number} originX Origin X. - * @param {number} originY Origin Y. - * @param {number} w Width. - * @param {number} h Height. - * @param {number} x X. - * @param {number} y Y. - * @param {import("../size.js").Size} scale Scale. - */ - function drawImageOrLabel( - context, - transform, - opacity, - labelOrImage, - originX, - originY, - w, - h, - x, - y, - scale - ) { - context.save(); + /** + * Set the line cap. + * + * @param {CanvasLineCap|undefined} lineCap Line cap. + * @api + */ + setLineCap(lineCap) { + this.lineCap_ = lineCap; + } - if (opacity !== 1) { - context.globalAlpha *= opacity; + /** + * Set the line dash. + * + * @param {Array|null} lineDash Line dash. + * @api + */ + setLineDash(lineDash) { + this.lineDash_ = lineDash; } - if (transform) { - context.setTransform.apply(context, transform); + + /** + * Set the line dash offset. + * + * @param {number|undefined} lineDashOffset Line dash offset. + * @api + */ + setLineDashOffset(lineDashOffset) { + this.lineDashOffset_ = lineDashOffset; } - if (/** @type {*} */ (labelOrImage).contextInstructions) { - // label - context.translate(x, y); - context.scale(scale[0], scale[1]); - executeLabelInstructions(/** @type {Label} */ (labelOrImage), context); - } else if (scale[0] < 0 || scale[1] < 0) { - // flipped image - context.translate(x, y); - context.scale(scale[0], scale[1]); - context.drawImage( - /** @type {HTMLCanvasElement|HTMLImageElement|HTMLVideoElement} */ ( - labelOrImage - ), - originX, - originY, - w, - h, - 0, - 0, - w, - h - ); - } else { - // if image not flipped translate and scale can be avoided - context.drawImage( - /** @type {HTMLCanvasElement|HTMLImageElement|HTMLVideoElement} */ ( - labelOrImage - ), - originX, - originY, - w, - h, - x, - y, - w * scale[0], - h * scale[1] - ); + /** + * Set the line join. + * + * @param {CanvasLineJoin|undefined} lineJoin Line join. + * @api + */ + setLineJoin(lineJoin) { + this.lineJoin_ = lineJoin; } - context.restore(); + /** + * Set the miter limit. + * + * @param {number|undefined} miterLimit Miter limit. + * @api + */ + setMiterLimit(miterLimit) { + this.miterLimit_ = miterLimit; + } + + /** + * Set the width. + * + * @param {number|undefined} width Width. + * @api + */ + setWidth(width) { + this.width_ = width; + } } + var Stroke$1 = Stroke; + /** - * @param {Label} label Label. - * @param {CanvasRenderingContext2D} context Context. + * @module ol/size */ - function executeLabelInstructions(label, context) { - const contextInstructions = label.contextInstructions; - for (let i = 0, ii = contextInstructions.length; i < ii; i += 2) { - if (Array.isArray(contextInstructions[i + 1])) { - context[contextInstructions[i]].apply( - context, - contextInstructions[i + 1] - ); - } else { - context[contextInstructions[i]] = contextInstructions[i + 1]; - } + + + /** + * Determines if a size has a positive area. + * @param {Size} size The size to test. + * @return {boolean} The size has a positive area. + */ + function hasArea(size) { + return size[0] > 0 && size[1] > 0; + } + + /** + * Returns a size scaled by a ratio. The result will be an array of integers. + * @param {Size} size Size. + * @param {number} ratio Ratio. + * @param {Size} [dest] Optional reusable size array. + * @return {Size} The scaled size. + */ + function scale(size, ratio, dest) { + if (dest === undefined) { + dest = [0, 0]; } + dest[0] = (size[0] * ratio + 0.5) | 0; + dest[1] = (size[1] * ratio + 0.5) | 0; + return dest; } /** - * @module ol/style/RegularShape + * Returns an `Size` array for the passed in number (meaning: square) or + * `Size` array. + * (meaning: non-square), + * @param {number|Size} size Width and height. + * @param {Size} [dest] Optional reusable size array. + * @return {Size} Size. + * @api */ + function toSize(size, dest) { + if (Array.isArray(size)) { + return size; + } + if (dest === undefined) { + dest = [size, size]; + } else { + dest[0] = size; + dest[1] = size; + } + return dest; + } /** - * Specify radius for regular polygons, or radius1 and radius2 for stars. - * @typedef {Object} Options - * @property {import("./Fill.js").default} [fill] Fill style. - * @property {number} points Number of points for stars and regular polygons. In case of a polygon, the number of points - * is the number of sides. - * @property {number} [radius] Radius of a regular polygon. - * @property {number} [radius1] First radius of a star. Ignored if radius is set. - * @property {number} [radius2] Second radius of a star. - * @property {number} [angle=0] Shape's angle in radians. A value of 0 will have one of the shape's points facing up. - * @property {Array} [displacement=[0, 0]] Displacement of the shape in pixels. - * Positive values will shift the shape right and up. - * @property {import("./Stroke.js").default} [stroke] Stroke style. - * @property {number} [rotation=0] Rotation in radians (positive rotation clockwise). - * @property {boolean} [rotateWithView=false] Whether to rotate the shape with the view. - * @property {number|import("../size.js").Size} [scale=1] Scale. Unless two dimensional scaling is required a better - * result may be obtained with appropriate settings for `radius`, `radius1` and `radius2`. - * @property {"declutter"|"obstacle"|"none"|undefined} [declutterMode] Declutter mode. + * @module ol/style/Image */ /** - * @typedef {Object} RenderOptions - * @property {import("../colorlike.js").ColorLike} [strokeStyle] StrokeStyle. - * @property {number} strokeWidth StrokeWidth. - * @property {number} size Size. - * @property {Array|null} lineDash LineDash. - * @property {number} lineDashOffset LineDashOffset. - * @property {CanvasLineJoin} lineJoin LineJoin. - * @property {number} miterLimit MiterLimit. + * @typedef {Object} Options + * @property {number} opacity Opacity. + * @property {boolean} rotateWithView If the image should get rotated with the view. + * @property {number} rotation Rotation. + * @property {number|import("../size.js").Size} scale Scale. + * @property {Array} displacement Displacement. + * @property {import('../style/Style.js').DeclutterMode} declutterMode Declutter mode: `declutter`, `obstacle`, `none`. */ /** * @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. + * 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}. + * @abstract * @api */ - class RegularShape extends ImageStyle$1 { + class ImageStyle { /** * @param {Options} options Options. */ constructor(options) { - /** - * @type {boolean} - */ - const rotateWithView = - options.rotateWithView !== undefined ? options.rotateWithView : false; - - super({ - opacity: 1, - rotateWithView: rotateWithView, - rotation: options.rotation !== undefined ? options.rotation : 0, - scale: options.scale !== undefined ? options.scale : 1, - displacement: - options.displacement !== undefined ? options.displacement : [0, 0], - declutterMode: options.declutterMode, - }); - - /** - * @private - * @type {Object} - */ - this.canvas_ = undefined; - - /** - * @private - * @type {HTMLCanvasElement} - */ - this.hitDetectionCanvas_ = null; - /** * @private - * @type {import("./Fill.js").default} + * @type {number} */ - this.fill_ = options.fill !== undefined ? options.fill : null; + this.opacity_ = options.opacity; /** * @private - * @type {Array} + * @type {boolean} */ - this.origin_ = [0, 0]; + this.rotateWithView_ = options.rotateWithView; /** * @private * @type {number} */ - this.points_ = options.points; - - /** - * @protected - * @type {number} - */ - this.radius_ = - options.radius !== undefined ? options.radius : options.radius1; - - /** - * @private - * @type {number|undefined} - */ - this.radius2_ = options.radius2; + this.rotation_ = options.rotation; /** * @private - * @type {number} + * @type {number|import("../size.js").Size} */ - this.angle_ = options.angle !== undefined ? options.angle : 0; + this.scale_ = options.scale; /** * @private - * @type {import("./Stroke.js").default} + * @type {import("../size.js").Size} */ - this.stroke_ = options.stroke !== undefined ? options.stroke : null; + this.scaleArray_ = toSize(options.scale); /** * @private - * @type {import("../size.js").Size} + * @type {Array} */ - this.size_ = null; + this.displacement_ = options.displacement; /** * @private - * @type {RenderOptions} + * @type {import('../style/Style.js').DeclutterMode} */ - this.renderOptions_ = null; - - this.render(); + this.declutterMode_ = options.declutterMode; } /** * Clones the style. - * @return {RegularShape} The cloned style. + * @return {ImageStyle} The cloned style. * @api */ clone() { const scale = this.getScale(); - const style = new RegularShape({ - fill: this.getFill() ? this.getFill().clone() : undefined, - points: this.getPoints(), - radius: this.getRadius(), - radius2: this.getRadius2(), - angle: this.getAngle(), - stroke: this.getStroke() ? this.getStroke().clone() : undefined, + return new ImageStyle({ + opacity: this.getOpacity(), + scale: Array.isArray(scale) ? scale.slice() : scale, rotation: this.getRotation(), rotateWithView: this.getRotateWithView(), - scale: Array.isArray(scale) ? scale.slice() : scale, displacement: this.getDisplacement().slice(), declutterMode: this.getDeclutterMode(), }); - style.setOpacity(this.getOpacity()); - return style; } /** - * Get the anchor point in pixels. The anchor determines the center point for the - * symbolizer. - * @return {Array} Anchor. + * Get the symbolizer opacity. + * @return {number} Opacity. * @api */ - getAnchor() { - const size = this.size_; - if (!size) { - return null; - } - const displacement = this.getDisplacement(); - const scale = this.getScaleArray(); - // anchor is scaled by renderer but displacement should not be scaled - // so divide by scale here - return [ - size[0] / 2 - displacement[0] / scale[0], - size[1] / 2 + displacement[1] / scale[1], - ]; + getOpacity() { + return this.opacity_; } /** - * Get the angle used in generating the shape. - * @return {number} Shape's rotation in radians. + * Determine whether the symbolizer rotates with the map. + * @return {boolean} Rotate with map. * @api */ - getAngle() { - return this.angle_; + getRotateWithView() { + return this.rotateWithView_; } /** - * Get the fill style for the shape. - * @return {import("./Fill.js").default} Fill style. + * Get the symoblizer rotation. + * @return {number} Rotation. * @api */ - getFill() { - return this.fill_; + getRotation() { + return this.rotation_; } /** - * Set the fill style. - * @param {import("./Fill.js").default} fill Fill style. + * Get the symbolizer scale. + * @return {number|import("../size.js").Size} Scale. * @api */ - setFill(fill) { - this.fill_ = fill; - this.render(); + getScale() { + return this.scale_; } /** - * @return {HTMLCanvasElement} Image element. + * Get the symbolizer scale array. + * @return {import("../size.js").Size} Scale array. */ - getHitDetectionImage() { - if (!this.hitDetectionCanvas_) { - this.createHitDetectionCanvas_(this.renderOptions_); - } - return this.hitDetectionCanvas_; + getScaleArray() { + return this.scaleArray_; } /** - * Get the image icon. - * @param {number} pixelRatio Pixel ratio. - * @return {HTMLCanvasElement} Image or Canvas element. + * Get the displacement of the shape + * @return {Array} Shape's center displacement + * @api + */ + getDisplacement() { + return this.displacement_; + } + + /** + * Get the declutter mode of the shape + * @return {import("./Style.js").DeclutterMode} Shape's declutter mode * @api */ + getDeclutterMode() { + return this.declutterMode_; + } + + /** + * Get the anchor point in pixels. The anchor determines the center point for the + * symbolizer. + * @abstract + * @return {Array} Anchor. + */ + getAnchor() { + return abstract(); + } + + /** + * Get the image element for the symbolizer. + * @abstract + * @param {number} pixelRatio Pixel ratio. + * @return {import('../DataTile.js').ImageLike} Image element. + */ getImage(pixelRatio) { - let image = this.canvas_[pixelRatio]; - if (!image) { - const renderOptions = this.renderOptions_; - const context = createCanvasContext2D( - renderOptions.size * pixelRatio, - renderOptions.size * pixelRatio - ); - this.draw_(renderOptions, context, pixelRatio); + return abstract(); + } - image = context.canvas; - this.canvas_[pixelRatio] = image; - } - return image; + /** + * @abstract + * @return {import('../DataTile.js').ImageLike} Image element. + */ + getHitDetectionImage() { + return abstract(); } /** @@ -11930,11424 +12390,12612 @@ var ol = (function () { * @return {number} Pixel ratio. */ getPixelRatio(pixelRatio) { - return pixelRatio; + return 1; } /** - * @return {import("../size.js").Size} Image size. + * @abstract + * @return {import("../ImageState.js").default} Image state. */ - getImageSize() { - return this.size_; + getImageState() { + return abstract(); } /** - * @return {import("../ImageState.js").default} Image state. + * @abstract + * @return {import("../size.js").Size} Image size. */ - getImageState() { - return ImageState.LOADED; + getImageSize() { + return abstract(); } /** * Get the origin of the symbolizer. + * @abstract * @return {Array} Origin. - * @api */ getOrigin() { - return this.origin_; + return abstract(); } /** - * Get the number of points for generating the shape. - * @return {number} Number of points for stars and regular polygons. - * @api + * Get the size of the symbolizer (in pixels). + * @abstract + * @return {import("../size.js").Size} Size. */ - getPoints() { - return this.points_; + getSize() { + return abstract(); } /** - * Get the (primary) radius for the shape. - * @return {number} Radius. + * Set the displacement. + * + * @param {Array} displacement Displacement. * @api */ - getRadius() { - return this.radius_; + setDisplacement(displacement) { + this.displacement_ = displacement; } /** - * Get the secondary radius for the shape. - * @return {number|undefined} Radius2. + * Set the opacity. + * + * @param {number} opacity Opacity. * @api */ - getRadius2() { - return this.radius2_; + setOpacity(opacity) { + this.opacity_ = opacity; } /** - * Get the size of the symbolizer (in pixels). - * @return {import("../size.js").Size} Size. + * Set whether to rotate the style with the view. + * + * @param {boolean} rotateWithView Rotate with map. * @api */ - getSize() { - return this.size_; + setRotateWithView(rotateWithView) { + this.rotateWithView_ = rotateWithView; } /** - * Get the stroke style for the shape. - * @return {import("./Stroke.js").default} Stroke style. + * Set the rotation. + * + * @param {number} rotation Rotation. * @api */ - getStroke() { - return this.stroke_; + setRotation(rotation) { + this.rotation_ = rotation; } /** - * Set the stroke style. - * @param {import("./Stroke.js").default} stroke Stroke style. + * Set the scale. + * + * @param {number|import("../size.js").Size} scale Scale. * @api */ - setStroke(stroke) { - this.stroke_ = stroke; - this.render(); + setScale(scale) { + this.scale_ = scale; + this.scaleArray_ = toSize(scale); } /** + * @abstract * @param {function(import("../events/Event.js").default): void} listener Listener function. */ - listenImageChange(listener) {} + listenImageChange(listener) { + abstract(); + } /** * Load not yet loaded URI. + * @abstract */ - load() {} + load() { + abstract(); + } /** + * @abstract * @param {function(import("../events/Event.js").default): void} listener Listener function. */ - unlistenImageChange(listener) {} + unlistenImageChange(listener) { + abstract(); + } /** - * Calculate additional canvas size needed for the miter. - * @param {string} lineJoin Line join - * @param {number} strokeWidth Stroke width - * @param {number} miterLimit Miter limit - * @return {number} Additional canvas size needed - * @private + * @return {Promise} `false` or Promise that resolves when the style is ready to use. */ - calculateLineJoinSize_(lineJoin, strokeWidth, miterLimit) { - if ( - strokeWidth === 0 || - this.points_ === Infinity || - (lineJoin !== 'bevel' && lineJoin !== 'miter') - ) { - return strokeWidth; - } - // m | ^ - // i | |\ . - // t >| #\ - // e | |\ \ . - // r \s\ - // | \t\ . . - // \r\ . . - // | \o\ . . . . . - // e \k\ . . . . - // | \e\ . . . . . - // d \ \ . . . . - // | _ _a_ _\# . . . - // r1 / ` . . - // | . . - // b / . . - // | . . - // / r2 . . - // | . . - // / . . - // |α . . - // / . . - // ° center - let r1 = this.radius_; - let r2 = this.radius2_ === undefined ? r1 : this.radius2_; - if (r1 < r2) { - const tmp = r1; - r1 = r2; - r2 = tmp; - } - const points = - this.radius2_ === undefined ? this.points_ : this.points_ * 2; - const alpha = (2 * Math.PI) / points; - const a = r2 * Math.sin(alpha); - const b = Math.sqrt(r2 * r2 - a * a); - const d = r1 - b; - const e = Math.sqrt(a * a + d * d); - const miterRatio = e / a; - if (lineJoin === 'miter' && miterRatio <= miterLimit) { - return miterRatio * strokeWidth; - } - // Calculate the distance from center to the stroke corner where - // it was cut short because of the miter limit. - // l - // ----+---- <= distance from center to here is maxr - // /####|k ##\ - // /#####^#####\ - // /#### /+\# s #\ - // /### h/+++\# t #\ - // /### t/+++++\# r #\ - // /### a/+++++++\# o #\ - // /### p/++ fill +\# k #\ - ///#### /+++++^+++++\# e #\ - //#####/+++++/+\+++++\#####\ - const k = strokeWidth / 2 / miterRatio; - const l = (strokeWidth / 2) * (d / e); - const maxr = Math.sqrt((r1 + k) * (r1 + k) + l * l); - const bevelAdd = maxr - r1; - if (this.radius2_ === undefined || lineJoin === 'bevel') { - return bevelAdd * 2; - } - // If outer miter is over the miter limit the inner miter may reach through the - // center and be longer than the bevel, same calculation as above but swap r1 / r2. - const aa = r1 * Math.sin(alpha); - const bb = Math.sqrt(r1 * r1 - aa * aa); - const dd = r2 - bb; - const ee = Math.sqrt(aa * aa + dd * dd); - const innerMiterRatio = ee / aa; - if (innerMiterRatio <= miterLimit) { - const innerLength = (innerMiterRatio * strokeWidth) / 2 - r2 - r1; - return 2 * Math.max(bevelAdd, innerLength); - } - return bevelAdd * 2; + ready() { + return Promise.resolve(); } + } - /** - * @return {RenderOptions} The render options - * @protected - */ - createRenderOptions() { - let lineJoin = defaultLineJoin; - let miterLimit = 0; - let lineDash = null; - let lineDashOffset = 0; - let strokeStyle; - let strokeWidth = 0; + var ImageStyle$1 = ImageStyle; - 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(); - lineJoin = this.stroke_.getLineJoin(); - if (lineJoin === undefined) { - lineJoin = defaultLineJoin; - } - miterLimit = this.stroke_.getMiterLimit(); - if (miterLimit === undefined) { - miterLimit = defaultMiterLimit; - } - } + /** + * @module ol/colorlike + */ - const add = this.calculateLineJoinSize_(lineJoin, strokeWidth, miterLimit); - const maxRadius = Math.max(this.radius_, this.radius2_ || 0); - const size = Math.ceil(2 * maxRadius + add); + /** + * @typedef {Object} PatternDescriptor + * @property {string} src Pattern image URL + * @property {import("./color.js").Color|string} [color] Color to tint the pattern with. + * @property {import("./size.js").Size} [size] Size of the desired slice from the pattern image. + * Use this together with `offset` when the pattern image is a sprite sheet. + * @property {import("./size.js").Size} [offset] Offset of the desired slice from the pattern image. + * Use this together with `size` when the pattern image is a sprite sheet. + */ - return { - strokeStyle: strokeStyle, - strokeWidth: strokeWidth, - size: size, - lineDash: lineDash, - lineDashOffset: lineDashOffset, - lineJoin: lineJoin, - miterLimit: miterLimit, - }; - } + /** + * A type accepted by CanvasRenderingContext2D.fillStyle + * or CanvasRenderingContext2D.strokeStyle. + * Represents a color, [CanvasPattern](https://developer.mozilla.org/en-US/docs/Web/API/CanvasPattern), + * or [CanvasGradient](https://developer.mozilla.org/en-US/docs/Web/API/CanvasGradient). The origin for + * patterns and gradients as fill style is an increment of 512 css pixels from map coordinate + * `[0, 0]`. For seamless repeat patterns, width and height of the pattern image + * must be a factor of two (2, 4, 8, ..., 512). + * + * @typedef {string|CanvasPattern|CanvasGradient} ColorLike + * @api + */ - /** - * @protected - */ - render() { - this.renderOptions_ = this.createRenderOptions(); - const size = this.renderOptions_.size; - this.canvas_ = {}; - this.size_ = [size, size]; + /** + * @param {import("./color.js").Color|ColorLike|PatternDescriptor|null} color Color. + * @return {ColorLike|null} The color as an {@link ol/colorlike~ColorLike}. + * @api + */ + function asColorLike(color) { + if (!color) { + return null; } - - /** - * @private - * @param {RenderOptions} renderOptions Render options. - * @param {CanvasRenderingContext2D} context The rendering context. - * @param {number} pixelRatio The pixel ratio. - */ - draw_(renderOptions, context, pixelRatio) { - context.scale(pixelRatio, pixelRatio); - // set origin to canvas center - context.translate(renderOptions.size / 2, renderOptions.size / 2); - - this.createPath_(context); - - 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.lineJoin = renderOptions.lineJoin; - context.miterLimit = renderOptions.miterLimit; - context.stroke(); - } + if (Array.isArray(color)) { + return toString$2(color); } - - /** - * @private - * @param {RenderOptions} renderOptions Render options. - */ - createHitDetectionCanvas_(renderOptions) { - if (this.fill_) { - let color = this.fill_.getColor(); - - // determine if fill is transparent (or pattern or gradient) - let opacity = 0; - if (typeof color === 'string') { - color = asArray(color); - } - if (color === null) { - opacity = 1; - } else if (Array.isArray(color)) { - opacity = color.length === 4 ? color[3] : 1; - } - if (opacity === 0) { - // if a transparent 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); - } - } - if (!this.hitDetectionCanvas_) { - this.hitDetectionCanvas_ = this.getImage(1); - } + if (typeof color === 'object' && 'src' in color) { + return asCanvasPattern(color); } + return color; + } - /** - * @private - * @param {CanvasRenderingContext2D} context The context to draw in. - */ - createPath_(context) { - let points = this.points_; - const radius = this.radius_; - if (points === Infinity) { - context.arc(0, 0, radius, 0, 2 * Math.PI); - } else { - const radius2 = this.radius2_ === undefined ? radius : this.radius2_; - if (this.radius2_ !== undefined) { - points *= 2; - } - const startAngle = this.angle_ - Math.PI / 2; - const step = (2 * Math.PI) / points; - for (let i = 0; i < points; i++) { - const angle0 = startAngle + i * step; - const radiusC = i % 2 === 0 ? radius : radius2; - context.lineTo(radiusC * Math.cos(angle0), radiusC * Math.sin(angle0)); - } - context.closePath(); - } + /** + * @param {PatternDescriptor} pattern Pattern descriptor. + * @return {CanvasPattern|null} Canvas pattern or null if the pattern referenced in the + * PatternDescriptor was not found in the icon image cache. + */ + function asCanvasPattern(pattern) { + if (!pattern.offset || !pattern.size) { + return shared.getPattern(pattern.src, 'anonymous', pattern.color); } - /** - * @private - * @param {RenderOptions} renderOptions Render options. - * @param {CanvasRenderingContext2D} context The context. - */ - drawHitDetectionCanvas_(renderOptions, context) { - // set origin to canvas center - context.translate(renderOptions.size / 2, renderOptions.size / 2); + const cacheKey = pattern.src + ':' + pattern.offset; - this.createPath_(context); + const canvasPattern = shared.getPattern( + cacheKey, + undefined, + pattern.color, + ); + if (canvasPattern) { + return canvasPattern; + } - 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.lineJoin = renderOptions.lineJoin; - context.miterLimit = renderOptions.miterLimit; - context.stroke(); - } + const iconImage = shared.get(pattern.src, 'anonymous', null); + if (iconImage.getImageState() !== ImageState.LOADED) { + return null; } + const patternCanvasContext = createCanvasContext2D( + pattern.size[0], + pattern.size[1], + ); + patternCanvasContext.drawImage( + iconImage.getImage(1), + pattern.offset[0], + pattern.offset[1], + pattern.size[0], + pattern.size[1], + 0, + 0, + pattern.size[0], + pattern.size[1], + ); + get( + patternCanvasContext.canvas, + cacheKey, + undefined, + ImageState.LOADED, + pattern.color, + true, + ); + return shared.getPattern(cacheKey, undefined, pattern.color); } - var RegularShape$1 = RegularShape; - /** - * @module ol/style/Circle + * @module ol/css */ /** - * @typedef {Object} Options - * @property {import("./Fill.js").default} [fill] Fill style. - * @property {number} radius Circle radius. - * @property {import("./Stroke.js").default} [stroke] Stroke style. - * @property {Array} [displacement=[0,0]] displacement - * @property {number|import("../size.js").Size} [scale=1] Scale. A two dimensional scale will produce an ellipse. - * Unless two dimensional scaling is required a better result may be obtained with an appropriate setting for `radius`. - * @property {number} [rotation=0] Rotation in radians - * (positive rotation clockwise, meaningful only when used in conjunction with a two dimensional scale). - * @property {boolean} [rotateWithView=false] Whether to rotate the shape with the view - * (meaningful only when used in conjunction with a two dimensional scale). - * @property {"declutter"|"obstacle"|"none"|undefined} [declutterMode] Declutter mode + * @typedef {Object} FontParameters + * @property {string} style Style. + * @property {string} variant Variant. + * @property {string} weight Weight. + * @property {string} size Size. + * @property {string} lineHeight LineHeight. + * @property {string} family Family. + * @property {Array} families Families. */ /** - * @classdesc - * Set circle style for vector features. - * @api + * The CSS class for hidden feature. + * + * @const + * @type {string} */ - class CircleStyle extends RegularShape$1 { - /** - * @param {Options} [options] Options. - */ - constructor(options) { - options = options ? options : {radius: 5}; - - super({ - points: Infinity, - fill: options.fill, - radius: options.radius, - stroke: options.stroke, - scale: options.scale !== undefined ? options.scale : 1, - rotation: options.rotation !== undefined ? options.rotation : 0, - rotateWithView: - options.rotateWithView !== undefined ? options.rotateWithView : false, - displacement: - options.displacement !== undefined ? options.displacement : [0, 0], - declutterMode: options.declutterMode, - }); - } - - /** - * Clones the style. - * @return {CircleStyle} The cloned style. - * @api - */ - clone() { - const scale = this.getScale(); - const style = new CircleStyle({ - fill: this.getFill() ? this.getFill().clone() : undefined, - stroke: this.getStroke() ? this.getStroke().clone() : undefined, - radius: this.getRadius(), - scale: Array.isArray(scale) ? scale.slice() : scale, - rotation: this.getRotation(), - rotateWithView: this.getRotateWithView(), - displacement: this.getDisplacement().slice(), - declutterMode: this.getDeclutterMode(), - }); - style.setOpacity(this.getOpacity()); - return style; - } - - /** - * Set the circle radius. - * - * @param {number} radius Circle radius. - * @api - */ - setRadius(radius) { - this.radius_ = radius; - this.render(); - } - } - - var Circle$2 = CircleStyle; + const CLASS_HIDDEN = 'ol-hidden'; /** - * @module ol/style/Style + * The CSS class that we'll give the DOM elements to have them selectable. + * + * @const + * @type {string} */ + const CLASS_SELECTABLE = 'ol-selectable'; /** - * A function that takes an {@link module:ol/Feature~Feature} and a `{number}` - * representing the view's resolution. The function should return a - * {@link module:ol/style/Style~Style} or an array of them. This way e.g. a - * vector layer can be styled. If the function returns `undefined`, the - * feature will not be rendered. + * The CSS class that we'll give the DOM elements to have them unselectable. * - * @typedef {function(import("../Feature.js").FeatureLike, number):(Style|Array