This commit is contained in:
Amir Ashkan Baghdoust
2021-03-07 18:31:04 +01:00
481 changed files with 22380 additions and 6525 deletions

View File

@@ -51,7 +51,7 @@ const TOS_ATTRIBUTION =
* @property {number} [cacheSize] Initial tile cache size. Will auto-grow to hold at least the number of tiles in the viewport.
* @property {boolean} [hidpi=false] If `true` hidpi tiles will be requested.
* @property {string} [culture='en-us'] Culture code.
* @property {string} key Bing Maps API key. Get yours at http://www.bingmapsportal.com/.
* @property {string} key Bing Maps API key. Get yours at https://www.bingmapsportal.com/.
* @property {string} imagerySet Type of imagery.
* @property {boolean} [imageSmoothing=true] Enable image smoothing.
* @property {number} [maxZoom=21] Max zoom. Default is what's advertized by the BingMaps service.
@@ -78,7 +78,7 @@ const TOS_ATTRIBUTION =
/**
* @typedef {Object} ResourceSet
* @property {Array<Resource>} resources
* @property {Array<Resource>} resources Resources.
*/
/**

View File

@@ -18,15 +18,17 @@ import {assign} from '../obj.js';
* @property {number} [minZoom] Minimum zoom.
* @property {boolean} [wrapX=true] Whether to wrap the world horizontally.
* @property {Object} [config] If using anonymous maps, the CartoDB config to use. See
* http://docs.cartodb.com/cartodb-platform/maps-api/anonymous-maps/
* https://carto.com/developers/maps-api/guides/anonymous-maps/
* for more detail.
* If using named maps, a key-value lookup with the template parameters.
* See http://docs.cartodb.com/cartodb-platform/maps-api/named-maps/
* See https://carto.com/developers/maps-api/guides/named-maps/
* for more detail.
* @property {string} [map] If using named maps, this will be the name of the template to load.
* See http://docs.cartodb.com/cartodb-platform/maps-api/named-maps/
* See https://carto.com/developers/maps-api/guides/named-maps/
* for more detail.
* @property {string} account If using named maps, this will be the name of the template to load.
* @property {string} [account] Username as used to access public Carto dashboard at https://{username}.carto.com/.
* @property {number} [transition=250] Duration of the opacity transition for rendering.
* To disable the opacity transition, pass `transition: 0`.
*/
/**
@@ -52,6 +54,7 @@ class CartoDB extends XYZ {
maxZoom: options.maxZoom !== undefined ? options.maxZoom : 18,
minZoom: options.minZoom,
projection: options.projection,
transition: options.transition,
wrapX: options.wrapX,
});

View File

@@ -9,18 +9,29 @@ import Point from '../geom/Point.js';
import VectorSource from './Vector.js';
import {add as addCoordinate, scale as scaleCoordinate} from '../coordinate.js';
import {assert} from '../asserts.js';
import {buffer, createEmpty, createOrUpdateFromCoordinate} from '../extent.js';
import {
buffer,
createEmpty,
createOrUpdateFromCoordinate,
getCenter,
} from '../extent.js';
import {getUid} from '../util.js';
/**
* @typedef {Object} Options
* @property {import("./Source.js").AttributionLike} [attributions] Attributions.
* @property {number} [distance=20] Minimum distance in pixels between clusters.
* @property {number} [distance=20] Distance in pixels within which features will
* be clustered together.
* @property {number} [minDistance=0] Minimum distance in pixels between clusters.
* Will be capped at the configured distance.
* By default no minimum distance is guaranteed. This config can be used to avoid
* overlapping icons. As a tradoff, the cluster feature's position will no longer be
* the center of all its features.
* @property {function(Feature):Point} [geometryFunction]
* Function that takes an {@link module:ol/Feature} as argument and returns an
* {@link module:ol/geom/Point} as cluster calculation point for the feature. When a
* feature should not be considered for clustering, the function should return
* `null`. The default, which works when the underyling source contains point
* `null`. The default, which works when the underlying source contains point
* features only, is
* ```js
* function(feature) {
@@ -66,6 +77,18 @@ class Cluster extends VectorSource {
*/
this.distance = options.distance !== undefined ? options.distance : 20;
/**
* @type {number}
* @protected
*/
this.minDistance = options.minDistance || 0;
/**
* @type {number}
* @protected
*/
this.interpolationRatio = 0;
/**
* @type {Array<Feature>}
* @protected
@@ -85,14 +108,21 @@ class Cluster extends VectorSource {
return geometry;
};
/**
* @type {VectorSource}
* @protected
*/
this.source = null;
this.boundRefresh_ = this.refresh.bind(this);
this.updateDistance(this.distance, this.minDistance);
this.setSource(options.source || null);
}
/**
* Remove all features from the source.
* @param {boolean=} opt_fast Skip dispatching of {@link module:ol/source/Vector.VectorSourceEvent#removefeature} events.
* @param {boolean} [opt_fast] Skip dispatching of {@link module:ol/source/Vector.VectorSourceEvent#removefeature} events.
* @api
*/
clear(opt_fast) {
@@ -126,21 +156,37 @@ class Cluster extends VectorSource {
loadFeatures(extent, resolution, projection) {
this.source.loadFeatures(extent, resolution, projection);
if (resolution !== this.resolution) {
this.clear();
this.resolution = resolution;
this.cluster();
this.addFeatures(this.features);
this.refresh();
}
}
/**
* Set the distance in pixels between clusters.
* Set the distance within which features will be clusterd together.
* @param {number} distance The distance in pixels.
* @api
*/
setDistance(distance) {
this.distance = distance;
this.refresh();
this.updateDistance(distance, this.minDistance);
}
/**
* Set the minimum distance between clusters. Will be capped at the
* configured distance.
* @param {number} minDistance The minimum distance in pixels.
* @api
*/
setMinDistance(minDistance) {
this.updateDistance(this.distance, minDistance);
}
/**
* The configured minimum distance between clusters.
* @return {number} The minimum distance in pixels.
* @api
*/
getMinDistance() {
return this.minDistance;
}
/**
@@ -168,6 +214,24 @@ class Cluster extends VectorSource {
this.addFeatures(this.features);
}
/**
* Update the distances and refresh the source if necessary.
* @param {number} distance The new distance.
* @param {number} minDistance The new minimum distance.
*/
updateDistance(distance, minDistance) {
const ratio =
distance === 0 ? 0 : Math.min(minDistance, distance) / distance;
const changed =
distance !== this.distance || this.interpolationRatio !== ratio;
this.distance = distance;
this.minDistance = minDistance;
this.interpolationRatio = ratio;
if (changed) {
this.refresh();
}
}
/**
* @protected
*/
@@ -179,9 +243,7 @@ class Cluster extends VectorSource {
const mapDistance = this.distance * this.resolution;
const features = this.source.getFeatures();
/**
* @type {!Object<string, boolean>}
*/
/** @type {Object<string, true>} */
const clustered = {};
for (let i = 0, ii = features.length; i < ii; i++) {
@@ -193,17 +255,17 @@ class Cluster extends VectorSource {
createOrUpdateFromCoordinate(coordinates, extent);
buffer(extent, mapDistance, extent);
let neighbors = this.source.getFeaturesInExtent(extent);
neighbors = neighbors.filter(function (neighbor) {
const uid = getUid(neighbor);
if (!(uid in clustered)) {
const neighbors = this.source
.getFeaturesInExtent(extent)
.filter(function (neighbor) {
const uid = getUid(neighbor);
if (uid in clustered) {
return false;
}
clustered[uid] = true;
return true;
} else {
return false;
}
});
this.features.push(this.createCluster(neighbors));
});
this.features.push(this.createCluster(neighbors, extent));
}
}
}
@@ -211,10 +273,11 @@ class Cluster extends VectorSource {
/**
* @param {Array<Feature>} features Features
* @param {import("../extent.js").Extent} extent The searched extent for these features.
* @return {Feature} The cluster feature.
* @protected
*/
createCluster(features) {
createCluster(features, extent) {
const centroid = [0, 0];
for (let i = features.length - 1; i >= 0; --i) {
const geometry = this.geometryFunction(features[i]);
@@ -225,9 +288,14 @@ class Cluster extends VectorSource {
}
}
scaleCoordinate(centroid, 1 / features.length);
const cluster = new Feature(new Point(centroid));
cluster.set('features', features);
const searchCenter = getCenter(extent);
const ratio = this.interpolationRatio;
const geometry = new Point([
centroid[0] * (1 - ratio) + searchCenter[0] * ratio,
centroid[1] * (1 - ratio) + searchCenter[1] * ratio,
]);
const cluster = new Feature(geometry);
cluster.set('features', features, true);
return cluster;
}
}

View File

@@ -9,18 +9,19 @@ import {DEFAULT_TILE_SIZE} from '../tilegrid/common.js';
import {Versions} from '../format/IIIFInfo.js';
import {assert} from '../asserts.js';
import {getTopLeft} from '../extent.js';
import {includes} from '../array.js';
import {toSize} from '../size.js';
/**
* @typedef {Object} Options
* @property {import("./Source.js").AttributionLike} [attributions] Attributions.
* @property {boolean} [attributionsCollapsible=true] Attributions are collapsible.
* @property {number} [cacheSize]
* @property {null|string} [crossOrigin]
* @property {import("../extent.js").Extent} [extent=[0, -height, width, 0]]
* @property {number} [cacheSize] Size of the cache.
* @property {null|string} [crossOrigin] The value for the crossOrigin option of the request.
* @property {import("../extent.js").Extent} [extent=[0, -height, width, 0]] The extent.
* @property {string} [format='jpg'] Requested image format.
* @property {boolean} [imageSmoothing=true] Enable image smoothing.
* @property {import("../proj.js").ProjectionLike} [projection]
* @property {import("../proj.js").ProjectionLike} [projection] Projection.
* @property {string} [quality] Requested IIIF image quality. Default is 'native'
* for version 1, 'default' for versions 2 and 3.
* @property {number} [reprojectionErrorThreshold=0.5] Maximum allowed reprojection error (in pixels).
@@ -32,13 +33,13 @@ import {toSize} from '../size.js';
* @property {import("./State.js").default} [state] Source state.
* @property {Array<string>} [supports=[]] Supported IIIF region and size calculation
* features.
* @property {number} [tilePixelRatio]
* @property {number} [tilePixelRatio] Tile pixel ratio.
* @property {number|import("../size.js").Size} [tileSize] Tile size.
* Same tile size is used for all zoom levels. If tile size is a number,
* a square tile is assumed. If the IIIF image service supports arbitrary
* tiling (sizeByH, sizeByW, sizeByWh or sizeByPct as well as regionByPx or regionByPct
* are supported), the default tilesize is 256.
* @property {number} [transition]
* @property {number} [transition] Transition.
* @property {string} [url] Base URL of the IIIF Image service.
* This should be the same as the IIIF Image ID.
* @property {import("../format/IIIFInfo.js").Versions} [version=Versions.VERSION2] Service's IIIF Image API version.
@@ -59,7 +60,7 @@ function formatPercentage(percentage) {
*/
class IIIF extends TileImage {
/**
* @param {Options=} opt_options Tile source options. Use {@link import("../format/IIIFInfo.js").IIIFInfo}
* @param {Options} [opt_options] Tile source options. Use {@link import("../format/IIIFInfo.js").IIIFInfo}
* to parse Image API service information responses into constructor options.
* @api
*/
@@ -111,11 +112,11 @@ class IIIF extends TileImage {
const supportsArbitraryTiling =
supports != undefined &&
Array.isArray(supports) &&
(supports.includes('regionByPx') || supports.includes('regionByPct')) &&
(supports.includes('sizeByWh') ||
supports.includes('sizeByH') ||
supports.includes('sizeByW') ||
supports.includes('sizeByPct'));
(includes(supports, 'regionByPx') || includes(supports, 'regionByPct')) &&
(includes(supports, 'sizeByWh') ||
includes(supports, 'sizeByH') ||
includes(supports, 'sizeByW') ||
includes(supports, 'sizeByPct'));
let tileWidth, tileHeight, maxZoom;
@@ -270,10 +271,10 @@ class IIIF extends TileImage {
regionParam = 'full';
} else if (
!supportsArbitraryTiling ||
supports.includes('regionByPx')
includes(supports, 'regionByPx')
) {
regionParam = regionX + ',' + regionY + ',' + regionW + ',' + regionH;
} else if (supports.includes('regionByPct')) {
} else if (includes(supports, 'regionByPct')) {
const pctX = formatPercentage((regionX / width) * 100),
pctY = formatPercentage((regionY / height) * 100),
pctW = formatPercentage((regionW / width) * 100),
@@ -282,16 +283,16 @@ class IIIF extends TileImage {
}
if (
version == Versions.VERSION3 &&
(!supportsArbitraryTiling || supports.includes('sizeByWh'))
(!supportsArbitraryTiling || includes(supports, 'sizeByWh'))
) {
sizeParam = sizeW + ',' + sizeH;
} else if (!supportsArbitraryTiling || supports.includes('sizeByW')) {
} else if (!supportsArbitraryTiling || includes(supports, 'sizeByW')) {
sizeParam = sizeW + ',';
} else if (supports.includes('sizeByH')) {
} else if (includes(supports, 'sizeByH')) {
sizeParam = ',' + sizeH;
} else if (supports.includes('sizeByWh')) {
} else if (includes(supports, 'sizeByWh')) {
sizeParam = sizeW + ',' + sizeH;
} else if (supports.includes('sizeByPct')) {
} else if (includes(supports, 'sizeByPct')) {
sizeParam = 'pct:' + formatPercentage(100 / scale);
}
} else {

View File

@@ -62,11 +62,11 @@ export class ImageSourceEvent extends Event {
/**
* @typedef {Object} Options
* @property {import("./Source.js").AttributionLike} [attributions]
* @property {import("./Source.js").AttributionLike} [attributions] Attributions.
* @property {boolean} [imageSmoothing=true] Enable image smoothing.
* @property {import("../proj.js").ProjectionLike} [projection]
* @property {Array<number>} [resolutions]
* @property {import("./State.js").default} [state]
* @property {import("../proj.js").ProjectionLike} [projection] Projection.
* @property {Array<number>} [resolutions] Resolutions.
* @property {import("./State.js").default} [state] State.
*/
/**

View File

@@ -25,7 +25,7 @@ import {containsExtent, getHeight, getWidth} from '../extent.js';
* defaults will be used for any fields not specified. `FORMAT` is `PNG32` by default. `F` is
* `IMAGE` by default. `TRANSPARENT` is `true` by default. `BBOX`, `SIZE`, `BBOXSR`, and `IMAGESR`
* will be set dynamically. Set `LAYERS` to override the default service layer visibility. See
* {@link http://resources.arcgis.com/en/help/arcgis-rest-api/index.html#/Export_Map/02r3000000v7000000/}
* https://developers.arcgis.com/rest/services-reference/export-map.htm
* for further reference.
* @property {import("../proj.js").ProjectionLike} [projection] Projection. Default is the view projection.
* @property {number} [ratio=1.5] Ratio. `1` means image requests are the size of the map viewport,
@@ -50,7 +50,7 @@ import {containsExtent, getHeight, getWidth} from '../extent.js';
*/
class ImageArcGISRest extends ImageSource {
/**
* @param {Options=} opt_options Image ArcGIS Rest Options.
* @param {Options} [opt_options] Image ArcGIS Rest Options.
*/
constructor(opt_options) {
const options = opt_options ? opt_options : {};

View File

@@ -15,8 +15,8 @@ import {
* A function returning the canvas element (`{HTMLCanvasElement}`)
* used by the source as an image. The arguments passed to the function are:
* {@link module:ol/extent~Extent} the image extent, `{number}` the image resolution,
* `{number}` the device pixel ratio, {@link module:ol/size~Size} the image size, and
* {@link module:ol/proj/Projection} the image projection. The canvas returned by
* `{number}` the pixel ratio of the map, {@link module:ol/size~Size} the image size,
* and {@link module:ol/proj/Projection} the image projection. The canvas returned by
* this function is cached by the source. The this keyword inside the function
* references the {@link module:ol/source/ImageCanvas}.
*
@@ -30,8 +30,8 @@ import {
* @property {FunctionType} [canvasFunction] Canvas function.
* The function returning the canvas element used by the source
* as an image. The arguments passed to the function are: `{import("../extent.js").Extent}` the
* image extent, `{number}` the image resolution, `{number}` the device pixel
* ratio, `{import("../size.js").Size}` the image size, and `{import("../proj/Projection.js").Projection}` the image
* image extent, `{number}` the image resolution, `{number}` the pixel ratio of the map,
* `{import("../size.js").Size}` the image size, and `{import("../proj/Projection.js").Projection}` the image
* projection. The canvas returned by this function is cached by the source. If
* the value returned by the function is later changed then
* `changed` should be called on the source for the source to
@@ -52,7 +52,7 @@ import {
*/
class ImageCanvasSource extends ImageSource {
/**
* @param {Options=} opt_options ImageCanvas options.
* @param {Options} [opt_options] ImageCanvas options.
*/
constructor(opt_options) {
const options = opt_options ? opt_options : {};

View File

@@ -62,7 +62,7 @@ const GETFEATUREINFO_IMAGE_SIZE = [101, 101];
*/
class ImageWMS extends ImageSource {
/**
* @param {Options=} [opt_options] ImageWMS options.
* @param {Options} [opt_options] ImageWMS options.
*/
constructor(opt_options) {
const options = opt_options ? opt_options : {};

View File

@@ -34,6 +34,8 @@ export const ATTRIBUTION =
* imageTile.getImage().src = src;
* };
* ```
* @property {number} [transition=250] Duration of the opacity transition for rendering.
* To disable the opacity transition, pass `transition: 0`.
* @property {string} [url='https://{a-c}.tile.openstreetmap.org/{z}/{x}/{y}.png'] URL template.
* Must include `{x}`, `{y}` or `{-y}`, and `{z}` placeholders.
* @property {boolean} [wrapX=true] Whether to wrap the world horizontally.
@@ -46,7 +48,7 @@ export const ATTRIBUTION =
*/
class OSM extends XYZ {
/**
* @param {Options=} [opt_options] Open Street Map options.
* @param {Options} [opt_options] Open Street Map options.
*/
constructor(opt_options) {
const options = opt_options || {};
@@ -68,16 +70,17 @@ class OSM extends XYZ {
super({
attributions: attributions,
attributionsCollapsible: false,
cacheSize: options.cacheSize,
crossOrigin: crossOrigin,
imageSmoothing: options.imageSmoothing,
opaque: options.opaque !== undefined ? options.opaque : true,
maxZoom: options.maxZoom !== undefined ? options.maxZoom : 19,
opaque: options.opaque !== undefined ? options.opaque : true,
reprojectionErrorThreshold: options.reprojectionErrorThreshold,
tileLoadFunction: options.tileLoadFunction,
transition: options.transition,
url: url,
wrapX: options.wrapX,
attributionsCollapsible: false,
});
}
}

View File

@@ -223,7 +223,7 @@ export class Processor extends Disposable {
/**
* Run operation on input data.
* @param {Array.<Array|ImageData>} inputs Array of pixels or image data
* @param {Array<Array|ImageData>} inputs Array of pixels or image data
* (depending on the operation type).
* @param {Object} meta A user data object. This is passed to all operations
* and must be serializable.
@@ -283,7 +283,7 @@ export class Processor extends Disposable {
const offset = i * segmentLength;
const slices = [];
for (let j = 0, jj = buffers.length; j < jj; ++j) {
slices.push(buffers[i].slice(offset, offset + segmentLength));
slices.push(buffers[j].slice(offset, offset + segmentLength));
}
this._workers[i].postMessage(
{
@@ -549,6 +549,7 @@ class RasterSource extends ImageSource {
this.frameState_ = {
animate: false,
coordinateToPixelTransform: createTransform(),
declutterTree: null,
extent: null,
index: 0,
layerIndex: 0,
@@ -565,7 +566,6 @@ class RasterSource extends ImageSource {
}),
viewHints: [],
wantedTiles: {},
declutterItems: [],
};
this.setAttributions(function (frameState) {
@@ -597,7 +597,7 @@ class RasterSource extends ImageSource {
/**
* Set the operation.
* @param {Operation} operation New operation.
* @param {Object=} opt_lib Functions that will be available to operations run
* @param {Object} [opt_lib] Functions that will be available to operations run
* in a worker.
* @api
*/
@@ -832,6 +832,9 @@ function getImageData(layer, frameState) {
}
const width = frameState.size[0];
const height = frameState.size[1];
if (width === 0 || height === 0) {
return null;
}
const container = renderer.renderFrame(frameState, null);
let element;
if (container) {

View File

@@ -26,11 +26,11 @@ import {get as getProjection} from '../proj.js';
/**
* @typedef {Object} Options
* @property {AttributionLike} [attributions]
* @property {AttributionLike} [attributions] Attributions.
* @property {boolean} [attributionsCollapsible=true] Attributions are collapsible.
* @property {import("../proj.js").ProjectionLike} [projection] Projection. Default is the view projection.
* @property {import("./State.js").default} [state='ready']
* @property {boolean} [wrapX=false]
* @property {import("./State.js").default} [state='ready'] State.
* @property {boolean} [wrapX=false] WrapX.
*/
/**
@@ -51,10 +51,10 @@ class Source extends BaseObject {
super();
/**
* @private
* @protected
* @type {import("../proj/Projection.js").default}
*/
this.projection_ = getProjection(options.projection);
this.projection = getProjection(options.projection);
/**
* @private
@@ -113,7 +113,7 @@ class Source extends BaseObject {
* @api
*/
getProjection() {
return this.projection_;
return this.projection;
}
/**

View File

@@ -100,7 +100,7 @@ const ProviderConfig = {
* imageTile.getImage().src = src;
* };
* ```
* @property {number} [transition] Duration of the opacity transition for rendering.
* @property {number} [transition=250] Duration of the opacity transition for rendering.
* To disable the opacity transition, pass `transition: 0`.
* @property {string} [url] URL template. Must include `{x}`, `{y}` or `{-y}`, and `{z}` placeholders.
* @property {boolean} [wrapX=true] Whether to wrap the world horizontally.

View File

@@ -6,6 +6,7 @@ import Source from './Source.js';
import TileCache from '../TileCache.js';
import TileState from '../TileState.js';
import {abstract} from '../util.js';
import {assert} from '../asserts.js';
import {equivalent} from '../proj.js';
import {getKeyZXY, withinExtentAndZ} from '../tilecoord.js';
import {
@@ -16,18 +17,18 @@ import {scale as scaleSize, toSize} from '../size.js';
/**
* @typedef {Object} Options
* @property {import("./Source.js").AttributionLike} [attributions]
* @property {import("./Source.js").AttributionLike} [attributions] Attributions.
* @property {boolean} [attributionsCollapsible=true] Attributions are collapsible.
* @property {number} [cacheSize]
* @property {number} [cacheSize] CacheSize.
* @property {boolean} [opaque=false] Whether the layer is opaque.
* @property {number} [tilePixelRatio]
* @property {import("../proj.js").ProjectionLike} [projection]
* @property {import("./State.js").default} [state]
* @property {import("../tilegrid/TileGrid.js").default} [tileGrid]
* @property {boolean} [wrapX=true]
* @property {number} [transition]
* @property {string} [key]
* @property {number} [zDirection=0]
* @property {number} [tilePixelRatio] TilePixelRatio.
* @property {import("../proj.js").ProjectionLike} [projection] Projection.
* @property {import("./State.js").default} [state] State.
* @property {import("../tilegrid/TileGrid.js").default} [tileGrid] TileGrid.
* @property {boolean} [wrapX=true] WrapX.
* @property {number} [transition] Transition.
* @property {string} [key] Key.
* @property {number} [zDirection=0] ZDirection.
*/
/**
@@ -250,12 +251,11 @@ class TileSource extends Source {
* @protected
*/
getTileCacheForProjection(projection) {
const thisProj = this.getProjection();
if (thisProj && !equivalent(thisProj, projection)) {
return null;
} else {
return this.tileCache;
}
assert(
equivalent(this.getProjection(), projection),
68 // A VectorTile source can only be rendered if it has a projection compatible with the view projection.
);
return this.tileCache;
}
/**
@@ -291,7 +291,7 @@ class TileSource extends Source {
* is outside the resolution and extent range of the tile grid, `null` will be
* returned.
* @param {import("../tilecoord.js").TileCoord} tileCoord Tile coordinate.
* @param {import("../proj/Projection.js").default=} opt_projection Projection.
* @param {import("../proj/Projection.js").default} [opt_projection] Projection.
* @return {import("../tilecoord.js").TileCoord} Tile coordinate to be passed to the tileUrlFunction or
* null if no tile URL should be created for the passed `tileCoord`.
*/

View File

@@ -23,7 +23,7 @@ import {hash as tileCoordHash} from '../tilecoord.js';
* default. `TRANSPARENT` is `true` by default. `BBOX`, `SIZE`, `BBOXSR`,
* and `IMAGESR` will be set dynamically. Set `LAYERS` to
* override the default service layer visibility. See
* http://resources.arcgis.com/en/help/arcgis-rest-api/index.html#/Export_Map/02r3000000v7000000/
* https://developers.arcgis.com/rest/services-reference/export-map.htm
* for further reference.
* @property {boolean} [hidpi=true] Use the `ol/Map#pixelRatio` value when requesting
* the image from the remote server.
@@ -62,7 +62,7 @@ import {hash as tileCoordHash} from '../tilecoord.js';
*/
class TileArcGISRest extends TileImage {
/**
* @param {Options=} opt_options Tile ArcGIS Rest options.
* @param {Options} [opt_options] Tile ArcGIS Rest options.
*/
constructor(opt_options) {
const options = opt_options ? opt_options : {};

View File

@@ -101,7 +101,7 @@ class LabeledTile extends Tile {
*/
class TileDebug extends XYZ {
/**
* @param {Options=} opt_options Debug tile options.
* @param {Options} [opt_options] Debug tile options.
*/
constructor(opt_options) {
/**

View File

@@ -171,10 +171,11 @@ class TileJSON extends TileImage {
extent = applyTransform(tileJSON['bounds'], transform);
}
const gridExtent = extentFromProjection(sourceProjection);
const minZoom = tileJSON['minzoom'] || 0;
const maxZoom = tileJSON['maxzoom'] || 22;
const tileGrid = createXYZ({
extent: extentFromProjection(sourceProjection),
extent: gridExtent,
maxZoom: maxZoom,
minZoom: minZoom,
tileSize: this.tileSize_,
@@ -184,9 +185,7 @@ class TileJSON extends TileImage {
this.tileUrlFunction = createFromTemplates(tileJSON['tiles'], tileGrid);
if (tileJSON['attribution'] !== undefined && !this.getAttributions()) {
const attributionExtent =
extent !== undefined ? extent : epsg4326Projection.getExtent();
const attributionExtent = extent !== undefined ? extent : gridExtent;
this.setAttributions(function (frameState) {
if (intersects(attributionExtent, frameState.extent)) {
return [tileJSON['attribution']];

View File

@@ -37,7 +37,7 @@ import {hash as tileCoordHash} from '../tilecoord.js';
* ignored. If you control the WMS service it is recommended to address
* "artifacts at tile edges" issues by properly configuring the WMS service. For
* example, MapServer has a `tile_map_edge_buffer` configuration parameter for
* this. See http://mapserver.org/output/tile_mode.html.
* this. See https://mapserver.org/output/tile_mode.html.
* @property {boolean} [hidpi=true] Use the `ol/Map#pixelRatio` value when requesting
* the image from the remote server.
* @property {import("../proj.js").ProjectionLike} [projection] Projection. Default is the view projection.
@@ -77,7 +77,7 @@ import {hash as tileCoordHash} from '../tilecoord.js';
*/
class TileWMS extends TileImage {
/**
* @param {Options=} [opt_options] Tile WMS options.
* @param {Options} [opt_options] Tile WMS options.
*/
constructor(opt_options) {
const options = opt_options ? opt_options : /** @type {Options} */ ({});

View File

@@ -132,7 +132,7 @@ export class CustomTile extends Tile {
* for given coordinate (or `null` if not yet loaded).
* @param {import("../coordinate.js").Coordinate} coordinate Coordinate.
* @param {function(*): void} callback Callback.
* @param {boolean=} opt_request If `true` the callback is always async.
* @param {boolean} [opt_request] If `true` the callback is always async.
* The tile data is requested if not yet loaded.
*/
forDataAtCoordinate(coordinate, callback, opt_request) {
@@ -378,7 +378,7 @@ class UTFGrid extends TileSource {
* @param {import("../coordinate.js").Coordinate} coordinate Coordinate.
* @param {number} resolution Resolution.
* @param {function(*): void} callback Callback.
* @param {boolean=} opt_request If `true` the callback is always async.
* @param {boolean} [opt_request] If `true` the callback is always async.
* The tile data is requested if not yet loaded.
* @api
*/
@@ -435,10 +435,11 @@ class UTFGrid extends TileSource {
extent = applyTransform(tileJSON['bounds'], transform);
}
const gridExtent = extentFromProjection(sourceProjection);
const minZoom = tileJSON['minzoom'] || 0;
const maxZoom = tileJSON['maxzoom'] || 22;
const tileGrid = createXYZ({
extent: extentFromProjection(sourceProjection),
extent: gridExtent,
maxZoom: maxZoom,
minZoom: minZoom,
});
@@ -455,9 +456,7 @@ class UTFGrid extends TileSource {
this.tileUrlFunction_ = createFromTemplates(grids, tileGrid);
if (tileJSON['attribution'] !== undefined) {
const attributionExtent =
extent !== undefined ? extent : epsg4326Projection.getExtent();
const attributionExtent = extent !== undefined ? extent : gridExtent;
this.setAttributions(function (frameState) {
if (intersects(attributionExtent, frameState.extent)) {
return [tileJSON['attribution']];

View File

@@ -10,22 +10,22 @@ import {getUid} from '../util.js';
/**
* @typedef {Object} Options
* @property {import("./Source.js").AttributionLike} [attributions]
* @property {import("./Source.js").AttributionLike} [attributions] Attributions.
* @property {boolean} [attributionsCollapsible=true] Attributions are collapsible.
* @property {number} [cacheSize]
* @property {number} [cacheSize] Cache size.
* @property {boolean} [opaque=false] Whether the layer is opaque.
* @property {import("../proj.js").ProjectionLike} [projection]
* @property {import("./State.js").default} [state]
* @property {import("../tilegrid/TileGrid.js").default} [tileGrid]
* @property {import("../Tile.js").LoadFunction} tileLoadFunction
* @property {number} [tilePixelRatio]
* @property {import("../Tile.js").UrlFunction} [tileUrlFunction]
* @property {string} [url]
* @property {Array<string>} [urls]
* @property {boolean} [wrapX=true]
* @property {number} [transition]
* @property {string} [key]
* @property {number} [zDirection=0]
* @property {import("../proj.js").ProjectionLike} [projection] Projection.
* @property {import("./State.js").default} [state] State.
* @property {import("../tilegrid/TileGrid.js").default} [tileGrid] TileGrid.
* @property {import("../Tile.js").LoadFunction} tileLoadFunction TileLoadFunction.
* @property {number} [tilePixelRatio] TilePixelRatio.
* @property {import("../Tile.js").UrlFunction} [tileUrlFunction] TileUrlFunction.
* @property {string} [url] Url.
* @property {Array<string>} [urls] Urls.
* @property {boolean} [wrapX=true] WrapX.
* @property {number} [transition] Transition.
* @property {string} [key] Key.
* @property {number} [zDirection=0] ZDirection.
*/
/**
@@ -68,7 +68,7 @@ class UrlTile extends TileSource {
this.tileLoadFunction = options.tileLoadFunction;
if (options.tileUrlFunction) {
this.tileUrlFunction = options.tileUrlFunction.bind(this);
this.tileUrlFunction = options.tileUrlFunction;
}
/**
@@ -105,7 +105,9 @@ class UrlTile extends TileSource {
* @api
*/
getTileUrlFunction() {
return this.tileUrlFunction;
return Object.getPrototypeOf(this).tileUrlFunction === this.tileUrlFunction
? this.tileUrlFunction.bind(this)
: this.tileUrlFunction;
}
/**
@@ -160,7 +162,7 @@ class UrlTile extends TileSource {
/**
* Set the tile URL function of the source.
* @param {import("../Tile.js").UrlFunction} tileUrlFunction Tile URL function.
* @param {string=} key Optional new tile key for the source.
* @param {string} [key] Optional new tile key for the source.
* @api
*/
setTileUrlFunction(tileUrlFunction, key) {

View File

@@ -39,17 +39,25 @@ import {xhr} from '../featureloader.js';
export class VectorSourceEvent extends Event {
/**
* @param {string} type Type.
* @param {import("../Feature.js").default<Geometry>=} opt_feature Feature.
* @param {import("../Feature.js").default<Geometry>} [opt_feature] Feature.
* @param {Array<import("../Feature.js").default<Geometry>>} [opt_features] Features.
*/
constructor(type, opt_feature) {
constructor(type, opt_feature, opt_features) {
super(type);
/**
* The feature being added or removed.
* The added or removed feature for the `ADDFEATURE` and `REMOVEFEATURE` events, `undefined` otherwise.
* @type {import("../Feature.js").default<Geometry>|undefined}
* @api
*/
this.feature = opt_feature;
/**
* The loaded features for the `FEATURESLOADED` event, `undefined` otherwise.
* @type {Array<import("../Feature.js").default<Geometry>>|undefined}
* @api
*/
this.features = opt_features;
}
}
@@ -64,7 +72,8 @@ export class VectorSourceEvent extends Event {
* @property {import("../featureloader.js").FeatureLoader} [loader]
* The loader function used to load features, from a remote source for example.
* If this is not set and `url` is set, the source will create and use an XHR
* feature loader.
* feature loader. The `'featuresloadend'` and `'featuresloaderror'` events
* will only fire if the `success` and `failure` callbacks are used.
*
* Example:
*
@@ -75,7 +84,7 @@ export class VectorSourceEvent extends Event {
*
* var vectorSource = new Vector({
* format: new GeoJSON(),
* loader: function(extent, resolution, projection) {
* loader: function(extent, resolution, projection, success, failure) {
* var proj = projection.getCode();
* var url = 'https://ahocevar.com/geoserver/wfs?service=WFS&' +
* 'version=1.1.0&request=GetFeature&typename=osm:water_areas&' +
@@ -85,12 +94,14 @@ export class VectorSourceEvent extends Event {
* xhr.open('GET', url);
* var onError = function() {
* vectorSource.removeLoadedExtent(extent);
* failure();
* }
* xhr.onerror = onError;
* xhr.onload = function() {
* if (xhr.status == 200) {
* vectorSource.addFeatures(
* vectorSource.getFormat().readFeatures(xhr.responseText));
* var features = vectorSource.getFormat().readFeatures(xhr.responseText);
* vectorSource.addFeatures(features);
* success(features);
* } else {
* onError();
* }
@@ -154,7 +165,7 @@ export class VectorSourceEvent extends Event {
*/
class VectorSource extends Source {
/**
* @param {Options=} opt_options Vector source options.
* @param {Options} [opt_options] Vector source options.
*/
constructor(opt_options) {
const options = opt_options || {};
@@ -223,6 +234,12 @@ class VectorSource extends Source {
*/
this.loadedExtentsRtree_ = new RBush();
/**
* @type {number}
* @private
*/
this.loadingExtentsCount_ = 0;
/**
* @private
* @type {!Object<string, import("../Feature.js").default<Geometry>>}
@@ -483,7 +500,7 @@ class VectorSource extends Source {
/**
* Remove all features from the source.
* @param {boolean=} opt_fast Skip dispatching of {@link module:ol/source/Vector.VectorSourceEvent#removefeature} events.
* @param {boolean} [opt_fast] Skip dispatching of {@link module:ol/source/Vector.VectorSourceEvent#removefeature} events.
* @api
*/
clear(opt_fast) {
@@ -695,7 +712,7 @@ class VectorSource extends Source {
* This method is not available when the source is configured with
* `useSpatialIndex` set to `false`.
* @param {import("../coordinate.js").Coordinate} coordinate Coordinate.
* @param {function(import("../Feature.js").default<Geometry>):boolean=} opt_filter Feature filter function.
* @param {function(import("../Feature.js").default<Geometry>):boolean} [opt_filter] Feature filter function.
* The filter function will receive one argument, the {@link module:ol/Feature feature}
* and it should return a boolean value. By default, no filtering is made.
* @return {import("../Feature.js").default<Geometry>} Closest feature.
@@ -754,7 +771,7 @@ class VectorSource extends Source {
*
* This method is not available when the source is configured with
* `useSpatialIndex` set to `false`.
* @param {import("../extent.js").Extent=} opt_extent Destination extent. If provided, no new extent
* @param {import("../extent.js").Extent} [opt_extent] Destination extent. If provided, no new extent
* will be created. Instead, that extent's coordinates will be overwritten.
* @return {import("../extent.js").Extent} Extent.
* @api
@@ -890,7 +907,6 @@ class VectorSource extends Source {
loadFeatures(extent, resolution, projection) {
const loadedExtentsRtree = this.loadedExtentsRtree_;
const extentsToLoad = this.strategy_(extent, resolution);
this.loading = false;
for (let i = 0, ii = extentsToLoad.length; i < ii; ++i) {
const extentToLoad = extentsToLoad[i];
const alreadyLoaded = loadedExtentsRtree.forEachInExtent(
@@ -904,11 +920,37 @@ class VectorSource extends Source {
}
);
if (!alreadyLoaded) {
this.loader_.call(this, extentToLoad, resolution, projection);
++this.loadingExtentsCount_;
this.dispatchEvent(
new VectorSourceEvent(VectorEventType.FEATURESLOADSTART)
);
this.loader_.call(
this,
extentToLoad,
resolution,
projection,
function (features) {
--this.loadingExtentsCount_;
this.dispatchEvent(
new VectorSourceEvent(
VectorEventType.FEATURESLOADEND,
undefined,
features
)
);
}.bind(this),
function () {
--this.loadingExtentsCount_;
this.dispatchEvent(
new VectorSourceEvent(VectorEventType.FEATURESLOADERROR)
);
}.bind(this)
);
loadedExtentsRtree.insert(extentToLoad, {extent: extentToLoad.slice()});
this.loading = this.loader_ !== VOID;
}
}
this.loading =
this.loader_ === VOID ? false : this.loadingExtentsCount_ > 0;
}
refresh() {

View File

@@ -34,4 +34,25 @@ export default {
* @api
*/
REMOVEFEATURE: 'removefeature',
/**
* Triggered when features starts loading.
* @event module:ol/source/Vector.VectorSourceEvent#featuresloadstart
* @api
*/
FEATURESLOADSTART: 'featuresloadstart',
/**
* Triggered when features finishes loading.
* @event module:ol/source/Vector.VectorSourceEvent#featuresloadend
* @api
*/
FEATURESLOADEND: 'featuresloadend',
/**
* Triggered if feature loading results in an error.
* @event module:ol/source/Vector.VectorSourceEvent#featuresloaderror
* @api
*/
FEATURESLOADERROR: 'featuresloaderror',
};

View File

@@ -28,7 +28,7 @@ import {toSize} from '../size.js';
* @property {import("./Source.js").AttributionLike} [attributions] Attributions.
* @property {boolean} [attributionsCollapsible=true] Attributions are collapsible.
* @property {number} [cacheSize] Initial tile cache size. Will auto-grow to hold at least twice the number of tiles in the viewport.
* @property {import("../extent.js").Extent} [extent]
* @property {import("../extent.js").Extent} [extent] Extent.
* @property {import("../format/Feature.js").default} [format] Feature format for tiles. Used and required by the default.
* @property {boolean} [overlaps=true] This source may have overlapping geometries. Setting this
* to `false` (e.g. for sources with polygons that represent administrative
@@ -528,11 +528,22 @@ export default VectorTile;
* @param {string} url URL.
*/
export function defaultLoadFunction(tile, url) {
const loader = loadFeaturesXhr(
url,
tile.getFormat(),
tile.onLoad.bind(tile),
tile.onError.bind(tile)
tile.setLoader(
/**
* @param {import("../extent.js").Extent} extent Extent.
* @param {number} resolution Resolution.
* @param {import("../proj/Projection.js").default} projection Projection.
*/
function (extent, resolution, projection) {
loadFeaturesXhr(
url,
tile.getFormat(),
extent,
resolution,
projection,
tile.onLoad.bind(tile),
tile.onError.bind(tile)
);
}
);
tile.setLoader(loader);
}

View File

@@ -370,10 +370,7 @@ export function optionsFromCapabilities(wmtsCap, config) {
return el['Identifier'] == elt['TileMatrixSet'];
});
const supportedCRS = tileMatrixSet['SupportedCRS'];
const proj1 =
getProjection(
supportedCRS.replace(/urn:ogc:def:crs:(\w+):(.*:)?(\w+)$/, '$1:$3')
) || getProjection(supportedCRS);
const proj1 = getProjection(supportedCRS);
const proj2 = getProjection(config['projection']);
if (proj1 && proj2) {
return equivalent(proj1, proj2);
@@ -435,10 +432,7 @@ export function optionsFromCapabilities(wmtsCap, config) {
let projection;
const code = matrixSetObj['SupportedCRS'];
if (code) {
projection =
getProjection(
code.replace(/urn:ogc:def:crs:(\w+):(.*:)?(\w+)$/, '$1:$3')
) || getProjection(code);
projection = getProjection(code);
}
if ('projection' in config) {
const projConfig = getProjection(config['projection']);
@@ -458,7 +452,7 @@ export function optionsFromCapabilities(wmtsCap, config) {
let selectedMatrixLimit = {
MinTileCol: 0,
MinTileRow: 0,
// substract one to end up at tile top left
// subtract one to end up at tile top left
MaxTileCol: matrix.MatrixWidth - 1,
MaxTileRow: matrix.MatrixHeight - 1,
};
@@ -466,10 +460,16 @@ export function optionsFromCapabilities(wmtsCap, config) {
//in case of matrix limits, use matrix limits to calculate extent
if (matrixLimits) {
selectedMatrixLimit = matrixLimits[matrixLimits.length - 1];
matrix = find(
const m = find(
matrixSetObj.TileMatrix,
(value) => value.Identifier === selectedMatrixLimit.TileMatrix
(tileMatrixValue) =>
tileMatrixValue.Identifier === selectedMatrixLimit.TileMatrix ||
matrixSetObj.Identifier + ':' + tileMatrixValue.Identifier ===
selectedMatrixLimit.TileMatrix
);
if (m) {
matrix = m;
}
}
const resolution =

View File

@@ -42,7 +42,7 @@ import {createXYZ, extentFromProjection} from '../tilegrid.js';
* may be used instead of defining each one separately in the `urls` option.
* @property {Array<string>} [urls] An array of URL templates.
* @property {boolean} [wrapX=true] Whether to wrap the world horizontally.
* @property {number} [transition] Duration of the opacity transition for rendering.
* @property {number} [transition=250] Duration of the opacity transition for rendering.
* To disable the opacity transition, pass `transition: 0`.
* @property {number} [zDirection=0] Indicate which resolution should be used
* by a renderer if the view resolution does not match any resolution of the tile source.
@@ -57,20 +57,19 @@ import {createXYZ, extentFromProjection} from '../tilegrid.js';
* Google grid where `x` 0 and `y` 0 are in the top left. Grids like
* TMS where `x` 0 and `y` 0 are in the bottom left can be used by
* using the `{-y}` placeholder in the URL template, so long as the
* source does not have a custom tile grid. In this case,
* {@link module:ol/source/TileImage} can be used with a `tileUrlFunction`
* such as:
*
* source does not have a custom tile grid. In this case
* a `tileUrlFunction` can be used, such as:
* ```js
* tileUrlFunction: function(coordinate) {
* return 'http://mapserver.com/' + coordinate[0] + '/' +
* coordinate[1] + '/' + coordinate[2] + '.png';
* }
*
* coordinate[1] + '/' + (-coordinate[2] - 1) + '.png';
* }
* ```
* @api
*/
class XYZ extends TileImage {
/**
* @param {Options=} opt_options XYZ options.
* @param {Options} [opt_options] XYZ options.
*/
constructor(opt_options) {
const options = opt_options || {};

View File

@@ -29,7 +29,7 @@ export class CustomTile extends ImageTile {
* @param {string} src Image source URI.
* @param {?string} crossOrigin Cross origin.
* @param {import("../Tile.js").LoadFunction} tileLoadFunction Tile load function.
* @param {import("../Tile.js").Options=} opt_options Tile options.
* @param {import("../Tile.js").Options} [opt_options] Tile options.
*/
constructor(
tileSize,
@@ -104,7 +104,7 @@ export class CustomTile extends ImageTile {
* A `{?-?}` template pattern, for example `subdomain{a-f}.domain.com`, may be
* used instead of defining each one separately in the `urls` option.
* @property {string} [tierSizeCalculation] Tier size calculation method: `default` or `truncated`.
* @property {import("../size.js").Size} size
* @property {import("../size.js").Size} size Size.
* @property {import("../extent.js").Extent} [extent] Extent for the TileGrid that is created.
* Default sets the TileGrid in the
* fourth quadrant, meaning extent is `[0, -height, width, 0]`. To change the