Merge branch 'master' into patch-4
This commit is contained in:
@@ -26,7 +26,7 @@ export class CollectionEvent extends Event {
|
||||
/**
|
||||
* @param {CollectionEventType} type Type.
|
||||
* @param {*=} opt_element Element.
|
||||
* @param {number} opt_index The index of the added or removed element.
|
||||
* @param {number=} opt_index The index of the added or removed element.
|
||||
*/
|
||||
constructor(type, opt_element, opt_index) {
|
||||
super(type);
|
||||
|
||||
@@ -58,20 +58,6 @@ class ImageTile extends Tile {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
disposeInternal() {
|
||||
if (this.state == TileState.LOADING) {
|
||||
this.unlistenImage_();
|
||||
this.image_ = getBlankImage();
|
||||
}
|
||||
if (this.interimTile) {
|
||||
this.interimTile.dispose();
|
||||
}
|
||||
super.disposeInternal();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the HTML image element for this tile (may be a Canvas, Image, or Video).
|
||||
* @return {HTMLCanvasElement|HTMLImageElement|HTMLVideoElement} Image.
|
||||
|
||||
+31
-6
@@ -1,7 +1,6 @@
|
||||
/**
|
||||
* @module ol/PluggableMap
|
||||
*/
|
||||
import {getUid} from './util.js';
|
||||
import Collection from './Collection.js';
|
||||
import CollectionEventType from './CollectionEventType.js';
|
||||
import MapBrowserEvent from './MapBrowserEvent.js';
|
||||
@@ -933,12 +932,16 @@ class PluggableMap extends BaseObject {
|
||||
// coordinates so interactions cannot be used.
|
||||
return;
|
||||
}
|
||||
let target = /** @type {Node} */ (mapBrowserEvent.originalEvent.target);
|
||||
while (target) {
|
||||
if (target.parentElement === this.overlayContainerStopEvent_) {
|
||||
const target = /** @type {Node} */ (mapBrowserEvent.originalEvent.target);
|
||||
if (!mapBrowserEvent.dragging) {
|
||||
if (this.overlayContainerStopEvent_.contains(target) || !(document.body.contains(target) || this.viewport_.getRootNode && this.viewport_.getRootNode().contains(target))) {
|
||||
// Abort if the event target is a child of the container that doesn't allow
|
||||
// event propagation or is no longer in the page. It's possible for the target to no longer
|
||||
// be in the page if it has been removed in an event listener, this might happen in a Control
|
||||
// that recreates it's content based on user interaction either manually or via a render
|
||||
// in something like https://reactjs.org/
|
||||
return;
|
||||
}
|
||||
target = target.parentElement;
|
||||
}
|
||||
mapBrowserEvent.frameState = this.frameState_;
|
||||
const interactionsArray = this.getInteractions().getArray();
|
||||
@@ -1102,7 +1105,8 @@ class PluggableMap extends BaseObject {
|
||||
}
|
||||
const view = this.getView();
|
||||
if (view) {
|
||||
this.viewport_.setAttribute('data-view', getUid(view));
|
||||
this.updateViewportSize_();
|
||||
|
||||
this.viewPropertyListenerKey_ = listen(
|
||||
view, ObjectEventType.PROPERTYCHANGE,
|
||||
this.handleViewPropertyChanged_, this);
|
||||
@@ -1360,6 +1364,27 @@ class PluggableMap extends BaseObject {
|
||||
parseFloat(computedStyle['borderBottomWidth'])
|
||||
]);
|
||||
}
|
||||
|
||||
this.updateViewportSize_();
|
||||
}
|
||||
|
||||
/**
|
||||
* Recomputes the viewport size and save it on the view object (if any)
|
||||
* @private
|
||||
*/
|
||||
updateViewportSize_() {
|
||||
const view = this.getView();
|
||||
if (view) {
|
||||
let size = undefined;
|
||||
const computedStyle = getComputedStyle(this.viewport_);
|
||||
if (computedStyle.width && computedStyle.height) {
|
||||
size = [
|
||||
parseInt(computedStyle.width, 10),
|
||||
parseInt(computedStyle.height, 10)
|
||||
];
|
||||
}
|
||||
view.setViewportSize(size);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+2
-3
@@ -145,10 +145,9 @@ class Tile extends EventTarget {
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
* Called by the tile cache when the tile is removed from the cache due to expiry
|
||||
*/
|
||||
disposeInternal() {
|
||||
this.setState(TileState.ABORT);
|
||||
release() {
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+3
-12
@@ -7,16 +7,7 @@ import {fromKey, getKey} from './tilecoord.js';
|
||||
class TileCache extends LRUCache {
|
||||
|
||||
/**
|
||||
* @param {number=} opt_highWaterMark High water mark.
|
||||
*/
|
||||
constructor(opt_highWaterMark) {
|
||||
|
||||
super(opt_highWaterMark);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {!Object<string, import("./TileRange.js").default>} usedTiles Used tiles.
|
||||
* @param {!Object<string, boolean>} usedTiles Used tiles.
|
||||
*/
|
||||
expireCache(usedTiles) {
|
||||
while (this.canExpireCache()) {
|
||||
@@ -24,7 +15,7 @@ class TileCache extends LRUCache {
|
||||
if (tile.getKey() in usedTiles) {
|
||||
break;
|
||||
} else {
|
||||
this.pop().dispose();
|
||||
this.pop().release();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -42,7 +33,7 @@ class TileCache extends LRUCache {
|
||||
this.forEach(function(tile) {
|
||||
if (tile.tileCoord[0] !== z) {
|
||||
this.remove(getKey(tile.tileCoord));
|
||||
tile.dispose();
|
||||
tile.release();
|
||||
}
|
||||
}.bind(this));
|
||||
}
|
||||
|
||||
+2
-11
@@ -84,8 +84,7 @@ class TileQueue extends PriorityQueue {
|
||||
handleTileChange(event) {
|
||||
const tile = /** @type {import("./Tile.js").default} */ (event.target);
|
||||
const state = tile.getState();
|
||||
if (tile.hifi && state === TileState.LOADED || state === TileState.ERROR ||
|
||||
state === TileState.EMPTY || state === TileState.ABORT) {
|
||||
if (tile.hifi && state === TileState.LOADED || state === TileState.ERROR || state === TileState.EMPTY) {
|
||||
tile.removeEventListener(EventType.CHANGE, this.boundHandleTileChange_);
|
||||
const tileKey = tile.getKey();
|
||||
if (tileKey in this.tilesLoadingKeys_) {
|
||||
@@ -102,27 +101,19 @@ class TileQueue extends PriorityQueue {
|
||||
*/
|
||||
loadMoreTiles(maxTotalLoading, maxNewLoads) {
|
||||
let newLoads = 0;
|
||||
let abortedTiles = false;
|
||||
let state, tile, tileKey;
|
||||
while (this.tilesLoading_ < maxTotalLoading && newLoads < maxNewLoads &&
|
||||
this.getCount() > 0) {
|
||||
tile = /** @type {import("./Tile.js").default} */ (this.dequeue()[0]);
|
||||
tileKey = tile.getKey();
|
||||
state = tile.getState();
|
||||
if (state === TileState.ABORT) {
|
||||
abortedTiles = true;
|
||||
} else if (state === TileState.IDLE && !(tileKey in this.tilesLoadingKeys_)) {
|
||||
if (state === TileState.IDLE && !(tileKey in this.tilesLoadingKeys_)) {
|
||||
this.tilesLoadingKeys_[tileKey] = true;
|
||||
++this.tilesLoading_;
|
||||
++newLoads;
|
||||
tile.load();
|
||||
}
|
||||
}
|
||||
if (newLoads === 0 && abortedTiles) {
|
||||
// Do not stop the render loop when all wanted tiles were aborted due to
|
||||
// a small, saturated tile cache.
|
||||
this.tileChangeCallback_();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+1
-2
@@ -14,6 +14,5 @@ export default {
|
||||
* @type {number}
|
||||
*/
|
||||
ERROR: 3,
|
||||
EMPTY: 4,
|
||||
ABORT: 5
|
||||
EMPTY: 4
|
||||
};
|
||||
|
||||
+23
-45
@@ -4,7 +4,6 @@
|
||||
import {getUid} from './util.js';
|
||||
import Tile from './Tile.js';
|
||||
import {createCanvasContext2D} from './dom.js';
|
||||
import {unlistenByKey} from './events.js';
|
||||
|
||||
|
||||
/**
|
||||
@@ -19,6 +18,10 @@ import {unlistenByKey} from './events.js';
|
||||
* @property {number} renderedTileZ
|
||||
*/
|
||||
|
||||
/**
|
||||
* @type {Array<HTMLCanvasElement>}
|
||||
*/
|
||||
const canvasPool = [];
|
||||
|
||||
class VectorRenderTile extends Tile {
|
||||
|
||||
@@ -26,13 +29,10 @@ class VectorRenderTile extends Tile {
|
||||
* @param {import("./tilecoord.js").TileCoord} tileCoord Tile coordinate.
|
||||
* @param {import("./TileState.js").default} state State.
|
||||
* @param {import("./tilecoord.js").TileCoord} urlTileCoord Wrapped tile coordinate for source urls.
|
||||
* @param {import("./tilegrid/TileGrid.js").default} sourceTileGrid Tile grid of the source.
|
||||
* @param {function(VectorRenderTile):Array<import("./VectorTile").default>} getSourceTiles Function
|
||||
* to get an source tiles for this tile.
|
||||
* @param {function(VectorRenderTile):void} removeSourceTiles Function to remove this tile from its
|
||||
* source tiles's consumer count.
|
||||
* to get source tiles for this tile.
|
||||
*/
|
||||
constructor(tileCoord, state, urlTileCoord, sourceTileGrid, getSourceTiles, removeSourceTiles) {
|
||||
constructor(tileCoord, state, urlTileCoord, getSourceTiles) {
|
||||
|
||||
super(tileCoord, state, {transition: 0});
|
||||
|
||||
@@ -71,6 +71,11 @@ class VectorRenderTile extends Tile {
|
||||
*/
|
||||
this.replayState_ = {};
|
||||
|
||||
/**
|
||||
* @type {Array<import("./VectorTile.js").default>}
|
||||
*/
|
||||
this.sourceTiles = null;
|
||||
|
||||
/**
|
||||
* @type {number}
|
||||
*/
|
||||
@@ -79,23 +84,7 @@ class VectorRenderTile extends Tile {
|
||||
/**
|
||||
* @type {!function():Array<import("./VectorTile.js").default>}
|
||||
*/
|
||||
this.getSourceTiles = getSourceTiles.bind(this, this);
|
||||
|
||||
/**
|
||||
* @type {!function(import("./VectorRenderTile.js").default):void}
|
||||
*/
|
||||
this.removeSourceTiles_ = removeSourceTiles;
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @type {import("./tilegrid/TileGrid.js").default}
|
||||
*/
|
||||
this.sourceTileGrid_ = sourceTileGrid;
|
||||
|
||||
/**
|
||||
* @type {Array<import("./events.js").EventsKey>}
|
||||
*/
|
||||
this.sourceTileListenerKeys = [];
|
||||
this.getSourceTiles = getSourceTiles.bind(undefined, this);
|
||||
|
||||
/**
|
||||
* z of the source tiles of the last getSourceTiles call.
|
||||
@@ -115,27 +104,6 @@ class VectorRenderTile extends Tile {
|
||||
this.wrappedTileCoord = urlTileCoord;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
disposeInternal() {
|
||||
this.sourceTileListenerKeys.forEach(unlistenByKey);
|
||||
this.sourceTileListenerKeys.length = 0;
|
||||
this.removeSourceTiles_(this);
|
||||
for (const key in this.context_) {
|
||||
const canvas = this.context_[key].canvas;
|
||||
canvas.width = 0;
|
||||
canvas.height = 0;
|
||||
}
|
||||
for (const key in this.executorGroups) {
|
||||
const executorGroups = this.executorGroups[key];
|
||||
for (let i = 0, ii = executorGroups.length; i < ii; ++i) {
|
||||
executorGroups[i].disposeInternal();
|
||||
}
|
||||
}
|
||||
super.disposeInternal();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {import("./layer/Layer.js").default} layer Layer.
|
||||
* @return {CanvasRenderingContext2D} The rendering context.
|
||||
@@ -143,7 +111,7 @@ class VectorRenderTile extends Tile {
|
||||
getContext(layer) {
|
||||
const key = getUid(layer);
|
||||
if (!(key in this.context_)) {
|
||||
this.context_[key] = createCanvasContext2D();
|
||||
this.context_[key] = createCanvasContext2D(1, 1, canvasPool);
|
||||
}
|
||||
return this.context_[key];
|
||||
}
|
||||
@@ -192,6 +160,16 @@ class VectorRenderTile extends Tile {
|
||||
load() {
|
||||
this.getSourceTiles();
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
release() {
|
||||
for (const key in this.context_) {
|
||||
canvasPool.push(this.context_[key].canvas);
|
||||
}
|
||||
super.release();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -18,11 +18,6 @@ class VectorTile extends Tile {
|
||||
|
||||
super(tileCoord, state, opt_options);
|
||||
|
||||
/**
|
||||
* @type {number}
|
||||
*/
|
||||
this.consumers = 0;
|
||||
|
||||
/**
|
||||
* Extent of this tile; set by the source.
|
||||
* @type {import("./extent.js").Extent}
|
||||
|
||||
+65
-35
@@ -2,7 +2,6 @@
|
||||
* @module ol/View
|
||||
*/
|
||||
import {DEFAULT_TILE_SIZE} from './tilegrid/common.js';
|
||||
import {getUid} from './util.js';
|
||||
import {VOID} from './functions.js';
|
||||
import {createExtent, none as centerNone} from './centerconstraint.js';
|
||||
import BaseObject from './Object.js';
|
||||
@@ -130,6 +129,14 @@ import {createMinMaxResolution} from './resolutionconstraint.js';
|
||||
* @property {boolean} [smoothResolutionConstraint=true] If true, the resolution
|
||||
* min/max values will be applied smoothly, i. e. allow the view to exceed slightly
|
||||
* the given resolution or zoom bounds.
|
||||
* @property {boolean} [showFullExtent=false] Allow the view to be zoomed out to
|
||||
* show the full configured extent. By default, when a view is configured with an
|
||||
* extent, users will not be able to zoom out so the viewport exceeds the extent in
|
||||
* either dimension. This means the full extent may not be visible if the viewport
|
||||
* is taller or wider than the aspect ratio of the configured extent. If
|
||||
* showFullExtent is true, the user will be able to zoom out so that the viewport
|
||||
* exceeds the height or width of the configured extent, but not both, allowing the
|
||||
* full extent to be shown.
|
||||
* @property {import("./proj.js").ProjectionLike} [projection='EPSG:3857'] The
|
||||
* projection. The default is Spherical Mercator.
|
||||
* @property {number} [resolution] The initial resolution for the view. The
|
||||
@@ -294,6 +301,12 @@ class View extends BaseObject {
|
||||
*/
|
||||
this.projection_ = createProjection(options.projection, 'EPSG:3857');
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @type {import("./size.js").Size}
|
||||
*/
|
||||
this.viewportSize_ = [100, 100];
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @type {import("./coordinate.js").Coordinate|undefined}
|
||||
@@ -649,7 +662,7 @@ class View extends BaseObject {
|
||||
animation.targetResolution :
|
||||
animation.sourceResolution + progress * (animation.targetResolution - animation.sourceResolution);
|
||||
if (animation.anchor) {
|
||||
const size = this.getSizeFromViewport_(this.getRotation());
|
||||
const size = this.getViewportSize_(this.getRotation());
|
||||
const constrainedResolution = this.constraints_.resolution(resolution, 0, size, true);
|
||||
this.targetCenter_ = this.calculateCenterZoom(constrainedResolution, animation.anchor);
|
||||
}
|
||||
@@ -722,26 +735,33 @@ class View extends BaseObject {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current viewport size.
|
||||
* @private
|
||||
* @param {number=} opt_rotation Take into account the rotation of the viewport when giving the size
|
||||
* @return {import("./size.js").Size} Viewport size or `[100, 100]` when no viewport is found.
|
||||
*/
|
||||
getSizeFromViewport_(opt_rotation) {
|
||||
const size = [100, 100];
|
||||
const selector = '.ol-viewport[data-view="' + getUid(this) + '"]';
|
||||
const element = document.querySelector(selector);
|
||||
if (element) {
|
||||
const metrics = getComputedStyle(element);
|
||||
size[0] = parseInt(metrics.width, 10);
|
||||
size[1] = parseInt(metrics.height, 10);
|
||||
}
|
||||
getViewportSize_(opt_rotation) {
|
||||
const size = this.viewportSize_;
|
||||
if (opt_rotation) {
|
||||
const w = size[0];
|
||||
const h = size[1];
|
||||
size[0] = Math.abs(w * Math.cos(opt_rotation)) + Math.abs(h * Math.sin(opt_rotation));
|
||||
size[1] = Math.abs(w * Math.sin(opt_rotation)) + Math.abs(h * Math.cos(opt_rotation));
|
||||
return [
|
||||
Math.abs(w * Math.cos(opt_rotation)) + Math.abs(h * Math.sin(opt_rotation)),
|
||||
Math.abs(w * Math.sin(opt_rotation)) + Math.abs(h * Math.cos(opt_rotation))
|
||||
];
|
||||
} else {
|
||||
return size;
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
/**
|
||||
* Stores the viewport size on the view. The viewport size is not read every time from the DOM
|
||||
* to avoid performance hit and layout reflow.
|
||||
* This should be done on map size change.
|
||||
* @param {import("./size.js").Size=} opt_size Viewport size; if undefined, [100, 100] is assumed
|
||||
*/
|
||||
setViewportSize(opt_size) {
|
||||
this.viewportSize_ = Array.isArray(opt_size) ? opt_size.slice() : [100, 100];
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -773,6 +793,13 @@ class View extends BaseObject {
|
||||
return this.constraints_;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {boolean} Resolution constraint is set
|
||||
*/
|
||||
getConstrainResolution() {
|
||||
return this.options_.constrainResolution;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Array<number>=} opt_hints Destination array.
|
||||
* @return {Array<number>} Hint.
|
||||
@@ -792,8 +819,8 @@ class View extends BaseObject {
|
||||
* The size is the pixel dimensions of the box into which the calculated extent
|
||||
* should fit. In most cases you want to get the extent of the entire map,
|
||||
* that is `map.getSize()`.
|
||||
* @param {import("./size.js").Size=} opt_size Box pixel size. If not provided, the size of the
|
||||
* first map that uses this view will be used.
|
||||
* @param {import("./size.js").Size=} opt_size Box pixel size. If not provided, the size
|
||||
* of the map that uses this view will be used.
|
||||
* @return {import("./extent.js").Extent} Extent.
|
||||
* @api
|
||||
*/
|
||||
@@ -808,7 +835,7 @@ class View extends BaseObject {
|
||||
* @return {import("./extent.js").Extent} Extent.
|
||||
*/
|
||||
calculateExtentInternal(opt_size) {
|
||||
const size = opt_size || this.getSizeFromViewport_();
|
||||
const size = opt_size || this.getViewportSize_();
|
||||
const center = /** @type {!import("./coordinate.js").Coordinate} */ (this.getCenterInternal());
|
||||
assert(center, 1); // The view center is not defined
|
||||
const resolution = /** @type {!number} */ (this.getResolution());
|
||||
@@ -931,7 +958,7 @@ class View extends BaseObject {
|
||||
* the given size.
|
||||
*/
|
||||
getResolutionForExtentInternal(extent, opt_size) {
|
||||
const size = opt_size || this.getSizeFromViewport_();
|
||||
const size = opt_size || this.getViewportSize_();
|
||||
const xResolution = getWidth(extent) / size[0];
|
||||
const yResolution = getHeight(extent) / size[1];
|
||||
return Math.max(xResolution, yResolution);
|
||||
@@ -945,7 +972,7 @@ class View extends BaseObject {
|
||||
*/
|
||||
getResolutionForValueFunction(opt_power) {
|
||||
const power = opt_power || 2;
|
||||
const maxResolution = this.maxResolution_;
|
||||
const maxResolution = this.getConstrainedResolution(this.maxResolution_);
|
||||
const minResolution = this.minResolution_;
|
||||
const max = Math.log(maxResolution / minResolution) / Math.log(power);
|
||||
return (
|
||||
@@ -976,17 +1003,17 @@ class View extends BaseObject {
|
||||
* @return {function(number): number} Value for resolution function.
|
||||
*/
|
||||
getValueForResolutionFunction(opt_power) {
|
||||
const power = opt_power || 2;
|
||||
const maxResolution = this.maxResolution_;
|
||||
const logPower = Math.log(opt_power || 2);
|
||||
const maxResolution = this.getConstrainedResolution(this.maxResolution_);
|
||||
const minResolution = this.minResolution_;
|
||||
const max = Math.log(maxResolution / minResolution) / Math.log(power);
|
||||
const max = Math.log(maxResolution / minResolution) / logPower;
|
||||
return (
|
||||
/**
|
||||
* @param {number} resolution Resolution.
|
||||
* @return {number} Value.
|
||||
*/
|
||||
function(resolution) {
|
||||
const value = (Math.log(maxResolution / resolution) / Math.log(power)) / max;
|
||||
const value = (Math.log(maxResolution / resolution) / logPower) / max;
|
||||
return value;
|
||||
});
|
||||
}
|
||||
@@ -1079,7 +1106,7 @@ class View extends BaseObject {
|
||||
* @api
|
||||
*/
|
||||
fit(geometryOrExtent, opt_options) {
|
||||
const options = assign({size: this.getSizeFromViewport_()}, opt_options || {});
|
||||
const options = assign({size: this.getViewportSize_()}, opt_options || {});
|
||||
|
||||
/** @type {import("./geom/SimpleGeometry.js").default} */
|
||||
let geometry;
|
||||
@@ -1097,7 +1124,7 @@ class View extends BaseObject {
|
||||
} else {
|
||||
const userProjection = getUserProjection();
|
||||
if (userProjection) {
|
||||
geometry = /** @type {import("./geom/SimpleGeometry.js").default} */ (geometry.clone().transform(userProjection, this.getProjection()));
|
||||
geometry = /** @type {import("./geom/SimpleGeometry.js").default} */ (geometryOrExtent.clone().transform(userProjection, this.getProjection()));
|
||||
} else {
|
||||
geometry = geometryOrExtent;
|
||||
}
|
||||
@@ -1114,7 +1141,7 @@ class View extends BaseObject {
|
||||
const options = opt_options || {};
|
||||
let size = options.size;
|
||||
if (!size) {
|
||||
size = this.getSizeFromViewport_();
|
||||
size = this.getViewportSize_();
|
||||
}
|
||||
const padding = options.padding !== undefined ? options.padding : [0, 0, 0, 0];
|
||||
const nearest = options.nearest !== undefined ? options.nearest : false;
|
||||
@@ -1261,7 +1288,7 @@ class View extends BaseObject {
|
||||
*/
|
||||
adjustResolutionInternal(ratio, opt_anchor) {
|
||||
const isMoving = this.getAnimating() || this.getInteracting();
|
||||
const size = this.getSizeFromViewport_(this.getRotation());
|
||||
const size = this.getViewportSize_(this.getRotation());
|
||||
const newResolution = this.constraints_.resolution(this.targetResolution_ * ratio, 0, size, isMoving);
|
||||
|
||||
if (opt_anchor) {
|
||||
@@ -1385,7 +1412,7 @@ class View extends BaseObject {
|
||||
|
||||
// compute rotation
|
||||
const newRotation = this.constraints_.rotation(this.targetRotation_, isMoving);
|
||||
const size = this.getSizeFromViewport_(newRotation);
|
||||
const size = this.getViewportSize_(newRotation);
|
||||
const newResolution = this.constraints_.resolution(this.targetResolution_, 0, size, isMoving);
|
||||
const newCenter = this.constraints_.center(this.targetCenter_, newResolution, size, isMoving);
|
||||
|
||||
@@ -1419,7 +1446,7 @@ class View extends BaseObject {
|
||||
const direction = opt_resolutionDirection || 0;
|
||||
|
||||
const newRotation = this.constraints_.rotation(this.targetRotation_);
|
||||
const size = this.getSizeFromViewport_(newRotation);
|
||||
const size = this.getViewportSize_(newRotation);
|
||||
const newResolution = this.constraints_.resolution(this.targetResolution_, direction, size);
|
||||
const newCenter = this.constraints_.center(this.targetCenter_, newResolution, size);
|
||||
|
||||
@@ -1500,7 +1527,7 @@ class View extends BaseObject {
|
||||
* @return {import("./coordinate.js").Coordinate|undefined} Valid center position.
|
||||
*/
|
||||
getConstrainedCenter(targetCenter, opt_targetResolution) {
|
||||
const size = this.getSizeFromViewport_(this.getRotation());
|
||||
const size = this.getViewportSize_(this.getRotation());
|
||||
return this.constraints_.center(targetCenter, opt_targetResolution || this.getResolution(), size);
|
||||
}
|
||||
|
||||
@@ -1529,7 +1556,7 @@ class View extends BaseObject {
|
||||
*/
|
||||
getConstrainedResolution(targetResolution, opt_direction) {
|
||||
const direction = opt_direction || 0;
|
||||
const size = this.getSizeFromViewport_(this.getRotation());
|
||||
const size = this.getViewportSize_(this.getRotation());
|
||||
|
||||
return this.constraints_.resolution(targetResolution, direction, size);
|
||||
}
|
||||
@@ -1599,6 +1626,9 @@ export function createResolutionConstraint(options) {
|
||||
const smooth =
|
||||
options.smoothResolutionConstraint !== undefined ? options.smoothResolutionConstraint : true;
|
||||
|
||||
const showFullExtent =
|
||||
options.showFullExtent !== undefined ? options.showFullExtent : false;
|
||||
|
||||
const projection = createProjection(options.projection, 'EPSG:3857');
|
||||
const projExtent = projection.getExtent();
|
||||
let constrainOnlyCenter = options.constrainOnlyCenter;
|
||||
@@ -1616,10 +1646,10 @@ export function createResolutionConstraint(options) {
|
||||
|
||||
if (options.constrainResolution) {
|
||||
resolutionConstraint = createSnapToResolutions(resolutions, smooth,
|
||||
!constrainOnlyCenter && extent);
|
||||
!constrainOnlyCenter && extent, showFullExtent);
|
||||
} else {
|
||||
resolutionConstraint = createMinMaxResolution(maxResolution, minResolution, smooth,
|
||||
!constrainOnlyCenter && extent);
|
||||
!constrainOnlyCenter && extent, showFullExtent);
|
||||
}
|
||||
} else {
|
||||
// calculate the default min and max resolution
|
||||
@@ -1665,10 +1695,10 @@ export function createResolutionConstraint(options) {
|
||||
if (options.constrainResolution) {
|
||||
resolutionConstraint = createSnapToPower(
|
||||
zoomFactor, maxResolution, minResolution, smooth,
|
||||
!constrainOnlyCenter && extent);
|
||||
!constrainOnlyCenter && extent, showFullExtent);
|
||||
} else {
|
||||
resolutionConstraint = createMinMaxResolution(maxResolution, minResolution, smooth,
|
||||
!constrainOnlyCenter && extent);
|
||||
!constrainOnlyCenter && extent, showFullExtent);
|
||||
}
|
||||
}
|
||||
return {constraint: resolutionConstraint, maxResolution: maxResolution,
|
||||
|
||||
@@ -325,7 +325,6 @@ class Attribution extends Control {
|
||||
* Update the attribution element.
|
||||
* @param {import("../MapEvent.js").default} mapEvent Map event.
|
||||
* @this {Attribution}
|
||||
* @api
|
||||
*/
|
||||
export function render(mapEvent) {
|
||||
this.updateElement_(mapEvent.frameState);
|
||||
|
||||
@@ -79,9 +79,10 @@ class Control extends BaseObject {
|
||||
this.listenerKeys = [];
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @type {function(import("../MapEvent.js").default): void}
|
||||
*/
|
||||
this.render = options.render ? options.render : VOID;
|
||||
this.render_ = options.render ? options.render : VOID;
|
||||
|
||||
if (options.target) {
|
||||
this.setTarget(options.target);
|
||||
@@ -134,6 +135,16 @@ class Control extends BaseObject {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the projection. Rendering of the coordinates is done in
|
||||
* `handleMouseMove` and `handleMouseUp`.
|
||||
* @param {import("../MapEvent.js").default} mapEvent Map event.
|
||||
* @api
|
||||
*/
|
||||
render(mapEvent) {
|
||||
this.render_.call(this, mapEvent);
|
||||
}
|
||||
|
||||
/**
|
||||
* This function is used to set a target element for the control. It has no
|
||||
* effect if it is called after the control has been added to the map (i.e.
|
||||
|
||||
@@ -9,6 +9,29 @@ import EventType from '../events/EventType.js';
|
||||
|
||||
const events = ['fullscreenchange', 'webkitfullscreenchange', 'MSFullscreenChange'];
|
||||
|
||||
|
||||
/**
|
||||
* @enum {string}
|
||||
*/
|
||||
const FullScreenEventType = {
|
||||
|
||||
/**
|
||||
* Triggered after the map entered fullscreen.
|
||||
* @event FullScreenEventType#enterfullscreen
|
||||
* @api
|
||||
*/
|
||||
ENTERFULLSCREEN: 'enterfullscreen',
|
||||
|
||||
/**
|
||||
* Triggered after the map leave fullscreen.
|
||||
* @event FullScreenEventType#leavefullscreen
|
||||
* @api
|
||||
*/
|
||||
LEAVEFULLSCREEN: 'leavefullscreen'
|
||||
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @typedef {Object} Options
|
||||
* @property {string} [className='ol-full-screen'] CSS class name.
|
||||
@@ -38,6 +61,8 @@ const events = ['fullscreenchange', 'webkitfullscreenchange', 'MSFullscreenChang
|
||||
* The [Fullscreen API](http://www.w3.org/TR/fullscreen/) is used to
|
||||
* toggle the map in full screen mode.
|
||||
*
|
||||
* @fires FullScreenEventType#enterfullscreen
|
||||
* @fires FullScreenEventType#leavefullscreen
|
||||
* @api
|
||||
*/
|
||||
class FullScreen extends Control {
|
||||
@@ -162,9 +187,11 @@ class FullScreen extends Control {
|
||||
if (isFullScreen()) {
|
||||
this.setClassName_(this.button_, true);
|
||||
replaceNode(this.labelActiveNode_, this.labelNode_);
|
||||
this.dispatchEvent(FullScreenEventType.ENTERFULLSCREEN);
|
||||
} else {
|
||||
this.setClassName_(this.button_, false);
|
||||
replaceNode(this.labelNode_, this.labelActiveNode_);
|
||||
this.dispatchEvent(FullScreenEventType.LEAVEFULLSCREEN);
|
||||
}
|
||||
if (map) {
|
||||
map.updateSize();
|
||||
|
||||
@@ -248,7 +248,6 @@ class MousePosition extends Control {
|
||||
* `handleMouseMove` and `handleMouseUp`.
|
||||
* @param {import("../MapEvent.js").default} mapEvent Map event.
|
||||
* @this {MousePosition}
|
||||
* @api
|
||||
*/
|
||||
export function render(mapEvent) {
|
||||
const frameState = mapEvent.frameState;
|
||||
|
||||
@@ -17,6 +17,7 @@ import {replaceNode} from '../dom.js';
|
||||
import {listen, listenOnce} from '../events.js';
|
||||
import EventType from '../events/EventType.js';
|
||||
import {containsExtent, equals as equalsExtent, getBottomRight, getTopLeft, scaleFromCenter} from '../extent.js';
|
||||
import View from '../View.js';
|
||||
|
||||
|
||||
/**
|
||||
@@ -59,8 +60,8 @@ class ControlledMap extends PluggableMap {
|
||||
* @property {HTMLElement|string} [target] Specify a target if you want the control
|
||||
* to be rendered outside of the map's viewport.
|
||||
* @property {string} [tipLabel='Overview map'] Text label to use for the button tip.
|
||||
* @property {import("../View.js").default} [view] Custom view for the overview map. If not provided,
|
||||
* a default view with an EPSG:3857 projection will be used.
|
||||
* @property {View} [view] Custom view for the overview map (should use same projection as main map). If not provided,
|
||||
* a default view with the same projection as the main map will be used.
|
||||
*/
|
||||
|
||||
|
||||
@@ -167,6 +168,13 @@ class OverviewMap extends Control {
|
||||
this.ovmapDiv_ = document.createElement('div');
|
||||
this.ovmapDiv_.className = 'ol-overviewmap-map';
|
||||
|
||||
/**
|
||||
* Explicitly given view to be used instead of a view derived from the main map.
|
||||
* @type {View}
|
||||
* @private
|
||||
*/
|
||||
this.view_ = options.view;
|
||||
|
||||
/**
|
||||
* @type {ControlledMap}
|
||||
* @private
|
||||
@@ -303,6 +311,14 @@ class OverviewMap extends Control {
|
||||
* @private
|
||||
*/
|
||||
bindView_(view) {
|
||||
if (!this.view_) {
|
||||
// Unless an explicit view definition was given, derive default from whatever main map uses.
|
||||
const newView = new View({
|
||||
projection: view.getProjection()
|
||||
});
|
||||
this.ovmap_.setView(newView);
|
||||
}
|
||||
|
||||
view.addEventListener(getChangeEventType(ViewProperty.ROTATION), this.boundHandleRotationChanged_);
|
||||
// Sync once with the new view
|
||||
this.handleRotationChanged_();
|
||||
@@ -603,7 +619,6 @@ class OverviewMap extends Control {
|
||||
* Update the overview map element.
|
||||
* @param {import("../MapEvent.js").default} mapEvent Map event.
|
||||
* @this {OverviewMap}
|
||||
* @api
|
||||
*/
|
||||
export function render(mapEvent) {
|
||||
this.validateExtent_();
|
||||
|
||||
@@ -131,8 +131,9 @@ class Rotate extends Control {
|
||||
// upon it
|
||||
return;
|
||||
}
|
||||
if (view.getRotation() !== undefined) {
|
||||
if (this.duration_ > 0) {
|
||||
const rotation = view.getRotation();
|
||||
if (rotation !== undefined) {
|
||||
if (this.duration_ > 0 && rotation % (2 * Math.PI) !== 0) {
|
||||
view.animate({
|
||||
rotation: 0,
|
||||
duration: this.duration_,
|
||||
@@ -150,7 +151,6 @@ class Rotate extends Control {
|
||||
* Update the rotate control element.
|
||||
* @param {import("../MapEvent.js").default} mapEvent Map event.
|
||||
* @this {Rotate}
|
||||
* @api
|
||||
*/
|
||||
export function render(mapEvent) {
|
||||
const frameState = mapEvent.frameState;
|
||||
|
||||
@@ -418,7 +418,6 @@ class ScaleLine extends Control {
|
||||
* Update the scale line element.
|
||||
* @param {import("../MapEvent.js").default} mapEvent Map event.
|
||||
* @this {ScaleLine}
|
||||
* @api
|
||||
*/
|
||||
export function render(mapEvent) {
|
||||
const frameState = mapEvent.frameState;
|
||||
|
||||
@@ -339,7 +339,6 @@ class ZoomSlider extends Control {
|
||||
* Update the zoomslider element.
|
||||
* @param {import("../MapEvent.js").default} mapEvent Map event.
|
||||
* @this {ZoomSlider}
|
||||
* @api
|
||||
*/
|
||||
export function render(mapEvent) {
|
||||
if (!mapEvent.frameState) {
|
||||
|
||||
+4
-2
@@ -7,10 +7,12 @@
|
||||
* Create an html canvas element and returns its 2d context.
|
||||
* @param {number=} opt_width Canvas width.
|
||||
* @param {number=} opt_height Canvas height.
|
||||
* @param {Array<HTMLCanvasElement>=} opt_canvasPool Canvas pool to take existing canvas from.
|
||||
* @return {CanvasRenderingContext2D} The context.
|
||||
*/
|
||||
export function createCanvasContext2D(opt_width, opt_height) {
|
||||
const canvas = document.createElement('canvas');
|
||||
export function createCanvasContext2D(opt_width, opt_height, opt_canvasPool) {
|
||||
const canvas = opt_canvasPool && opt_canvasPool.length ?
|
||||
opt_canvasPool.shift() : document.createElement('canvas');
|
||||
if (opt_width) {
|
||||
canvas.width = opt_width;
|
||||
}
|
||||
|
||||
@@ -162,6 +162,8 @@ class GeoJSON extends JSONFeature {
|
||||
if (crs) {
|
||||
if (crs['type'] == 'name') {
|
||||
projection = getProjection(crs['properties']['name']);
|
||||
} else if (crs['type'] === 'EPSG') {
|
||||
projection = getProjection('EPSG:' + crs['properties']['code']);
|
||||
} else {
|
||||
assert(false, 36); // Unknown SRS type
|
||||
}
|
||||
|
||||
@@ -349,7 +349,7 @@ class IIIFInfo {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {PreferredOptions} opt_preferredOptions Optional options for preferred format and quality.
|
||||
* @param {PreferredOptions=} opt_preferredOptions Optional options for preferred format and quality.
|
||||
* @returns {import("../source/IIIF.js").Options} IIIF tile source ready constructor options.
|
||||
* @api
|
||||
*/
|
||||
|
||||
+112
-37
@@ -378,6 +378,11 @@ function createStyleDefaults() {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @type {HTMLTextAreaElement}
|
||||
*/
|
||||
let TEXTAREA;
|
||||
|
||||
|
||||
/**
|
||||
* @typedef {Object} Options
|
||||
@@ -877,32 +882,32 @@ class KML extends XMLFeature {
|
||||
* @return {Style} style Style.
|
||||
*/
|
||||
function createNameStyleFunction(foundStyle, name) {
|
||||
let textStyle = null;
|
||||
const textOffset = [0, 0];
|
||||
let textAlign = 'start';
|
||||
if (foundStyle.getImage()) {
|
||||
let imageSize = foundStyle.getImage().getImageSize();
|
||||
const imageStyle = foundStyle.getImage();
|
||||
if (imageStyle) {
|
||||
let imageSize = imageStyle.getImageSize();
|
||||
if (imageSize === null) {
|
||||
imageSize = DEFAULT_IMAGE_STYLE_SIZE;
|
||||
}
|
||||
if (imageSize.length == 2) {
|
||||
const imageScale = foundStyle.getImage().getScale();
|
||||
// Offset the label to be centered to the right of the icon, if there is
|
||||
// one.
|
||||
const imageScale = imageStyle.getScale();
|
||||
// Offset the label to be centered to the right of the icon,
|
||||
// if there is one.
|
||||
textOffset[0] = imageScale * imageSize[0] / 2;
|
||||
textOffset[1] = -imageScale * imageSize[1] / 2;
|
||||
textAlign = 'left';
|
||||
}
|
||||
}
|
||||
if (foundStyle.getText() !== null) {
|
||||
let textStyle = foundStyle.getText();
|
||||
if (textStyle) {
|
||||
// clone the text style, customizing it with name, alignments and offset.
|
||||
// Note that kml does not support many text options that OpenLayers does (rotation, textBaseline).
|
||||
const foundText = foundStyle.getText();
|
||||
textStyle = foundText.clone();
|
||||
textStyle.setFont(foundText.getFont() || DEFAULT_TEXT_STYLE.getFont());
|
||||
textStyle.setScale(foundText.getScale() || DEFAULT_TEXT_STYLE.getScale());
|
||||
textStyle.setFill(foundText.getFill() || DEFAULT_TEXT_STYLE.getFill());
|
||||
textStyle.setStroke(foundText.getStroke() || DEFAULT_TEXT_STROKE_STYLE);
|
||||
textStyle = textStyle.clone();
|
||||
textStyle.setFont(textStyle.getFont() || DEFAULT_TEXT_STYLE.getFont());
|
||||
textStyle.setScale(textStyle.getScale() || DEFAULT_TEXT_STYLE.getScale());
|
||||
textStyle.setFill(textStyle.getFill() || DEFAULT_TEXT_STYLE.getFill());
|
||||
textStyle.setStroke(textStyle.getStroke() || DEFAULT_TEXT_STROKE_STYLE);
|
||||
} else {
|
||||
textStyle = DEFAULT_TEXT_STYLE.clone();
|
||||
}
|
||||
@@ -912,7 +917,12 @@ function createNameStyleFunction(foundStyle, name) {
|
||||
textStyle.setTextAlign(textAlign);
|
||||
|
||||
const nameStyle = new Style({
|
||||
text: textStyle
|
||||
image: imageStyle,
|
||||
text: textStyle,
|
||||
// although nameStyle will be used only for Point geometries
|
||||
// fill and stroke are included to assist writing of MultiGeometry styles
|
||||
fill: foundStyle.getFill(),
|
||||
stroke: foundStyle.getStroke()
|
||||
});
|
||||
return nameStyle;
|
||||
}
|
||||
@@ -932,45 +942,66 @@ function createFeatureStyleFunction(style, styleUrl, defaultStyle, sharedStyles,
|
||||
/**
|
||||
* @param {Feature} feature feature.
|
||||
* @param {number} resolution Resolution.
|
||||
* @return {Array<Style>} Style.
|
||||
* @return {Array<Style>|Style} Style.
|
||||
*/
|
||||
function(feature, resolution) {
|
||||
let drawName = showPointNames;
|
||||
/** @type {Style|undefined} */
|
||||
let nameStyle;
|
||||
let name = '';
|
||||
let multiGeometryPoints = [];
|
||||
if (drawName) {
|
||||
const geometry = feature.getGeometry();
|
||||
if (geometry) {
|
||||
drawName = geometry.getType() === GeometryType.POINT;
|
||||
const type = geometry.getType();
|
||||
if (type === GeometryType.GEOMETRY_COLLECTION) {
|
||||
multiGeometryPoints = geometry.getGeometriesArray().filter(function(geometry) {
|
||||
const type = geometry.getType();
|
||||
return type === GeometryType.POINT || type === GeometryType.MULTI_POINT;
|
||||
});
|
||||
drawName = multiGeometryPoints.length > 0;
|
||||
} else {
|
||||
drawName = type === GeometryType.POINT || type === GeometryType.MULTI_POINT;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (drawName) {
|
||||
name = /** @type {string} */ (feature.get('name'));
|
||||
drawName = drawName && !!name;
|
||||
// convert any html character codes
|
||||
if (drawName && name.search(/&[^&]+;/) > -1) {
|
||||
if (!TEXTAREA) {
|
||||
TEXTAREA = document.createElement('textarea');
|
||||
}
|
||||
TEXTAREA.innerHTML = name;
|
||||
name = TEXTAREA.value;
|
||||
}
|
||||
}
|
||||
|
||||
let featureStyle = defaultStyle;
|
||||
if (style) {
|
||||
if (drawName) {
|
||||
nameStyle = createNameStyleFunction(style[0], name);
|
||||
return style.concat(nameStyle);
|
||||
}
|
||||
return style;
|
||||
}
|
||||
if (styleUrl) {
|
||||
const foundStyle = findStyle(styleUrl, defaultStyle, sharedStyles);
|
||||
if (drawName) {
|
||||
nameStyle = createNameStyleFunction(foundStyle[0], name);
|
||||
return foundStyle.concat(nameStyle);
|
||||
}
|
||||
return foundStyle;
|
||||
featureStyle = style;
|
||||
} else if (styleUrl) {
|
||||
featureStyle = findStyle(styleUrl, defaultStyle, sharedStyles);
|
||||
}
|
||||
if (drawName) {
|
||||
nameStyle = createNameStyleFunction(defaultStyle[0], name);
|
||||
return defaultStyle.concat(nameStyle);
|
||||
const nameStyle = createNameStyleFunction(featureStyle[0], name);
|
||||
if (multiGeometryPoints.length > 0) {
|
||||
// in multigeometries restrict the name style to points and create a
|
||||
// style without image or text for geometries requiring fill or stroke
|
||||
// including any polygon specific style if there is one
|
||||
nameStyle.setGeometry(new GeometryCollection(multiGeometryPoints));
|
||||
const baseStyle = new Style({
|
||||
geometry: featureStyle[0].getGeometry(),
|
||||
image: null,
|
||||
fill: featureStyle[0].getFill(),
|
||||
stroke: featureStyle[0].getStroke(),
|
||||
text: null
|
||||
});
|
||||
return [nameStyle, baseStyle].concat(featureStyle.slice(1));
|
||||
}
|
||||
return nameStyle;
|
||||
}
|
||||
return defaultStyle;
|
||||
return featureStyle;
|
||||
}
|
||||
);
|
||||
}
|
||||
@@ -1759,13 +1790,57 @@ function readStyle(node, objectStack) {
|
||||
const textStyle = /** @type {Text} */
|
||||
('textStyle' in styleObject ?
|
||||
styleObject['textStyle'] : DEFAULT_TEXT_STYLE);
|
||||
let strokeStyle = /** @type {Stroke} */
|
||||
const strokeStyle = /** @type {Stroke} */
|
||||
('strokeStyle' in styleObject ?
|
||||
styleObject['strokeStyle'] : DEFAULT_STROKE_STYLE);
|
||||
const outline = /** @type {boolean|undefined} */
|
||||
(styleObject['outline']);
|
||||
if (outline !== undefined && !outline) {
|
||||
strokeStyle = null;
|
||||
// if the polystyle specifies no outline two styles are needed,
|
||||
// one for non-polygon geometries where linestrings require a stroke
|
||||
// and one for polygons where there should be no stroke
|
||||
return [
|
||||
new Style({
|
||||
geometry: function(feature) {
|
||||
const geometry = feature.getGeometry();
|
||||
const type = geometry.getType();
|
||||
if (type === GeometryType.GEOMETRY_COLLECTION) {
|
||||
return new GeometryCollection(
|
||||
geometry.getGeometriesArray().filter(function(geometry) {
|
||||
const type = geometry.getType();
|
||||
return type !== GeometryType.POLYGON && type !== GeometryType.MULTI_POLYGON;
|
||||
})
|
||||
);
|
||||
} else if (type !== GeometryType.POLYGON && type !== GeometryType.MULTI_POLYGON) {
|
||||
return geometry;
|
||||
}
|
||||
},
|
||||
fill: fillStyle,
|
||||
image: imageStyle,
|
||||
stroke: strokeStyle,
|
||||
text: textStyle,
|
||||
zIndex: undefined // FIXME
|
||||
}),
|
||||
new Style({
|
||||
geometry: function(feature) {
|
||||
const geometry = feature.getGeometry();
|
||||
const type = geometry.getType();
|
||||
if (type === GeometryType.GEOMETRY_COLLECTION) {
|
||||
return new GeometryCollection(
|
||||
geometry.getGeometriesArray().filter(function(geometry) {
|
||||
const type = geometry.getType();
|
||||
return type === GeometryType.POLYGON || type === GeometryType.MULTI_POLYGON;
|
||||
})
|
||||
);
|
||||
} else if (type === GeometryType.POLYGON || type === GeometryType.MULTI_POLYGON) {
|
||||
return geometry;
|
||||
}
|
||||
},
|
||||
fill: fillStyle,
|
||||
stroke: null,
|
||||
zIndex: undefined // FIXME
|
||||
})
|
||||
];
|
||||
}
|
||||
return [new Style({
|
||||
fill: fillStyle,
|
||||
@@ -2527,7 +2602,7 @@ function writeLineStyle(node, style, objectStack) {
|
||||
const /** @type {import("../xml.js").NodeStackItem} */ context = {node: node};
|
||||
const properties = {
|
||||
'color': style.getColor(),
|
||||
'width': style.getWidth()
|
||||
'width': Number(style.getWidth()) || 1
|
||||
};
|
||||
const parentNode = objectStack[objectStack.length - 1].node;
|
||||
const orderedKeys = LINE_STYLE_SEQUENCE[parentNode.namespaceURI];
|
||||
|
||||
@@ -74,7 +74,10 @@ class WMTSCapabilities extends XML {
|
||||
* @inheritDoc
|
||||
*/
|
||||
readFromNode(node) {
|
||||
const version = node.getAttribute('version').trim();
|
||||
let version = node.getAttribute('version');
|
||||
if (version) {
|
||||
version = version.trim();
|
||||
}
|
||||
let WMTSCapabilityObject = this.owsParser_.readFromNode(node);
|
||||
if (!WMTSCapabilityObject) {
|
||||
return null;
|
||||
|
||||
@@ -5,7 +5,7 @@ import {abstract} from '../util.js';
|
||||
import {extend} from '../array.js';
|
||||
import FeatureFormat from '../format/Feature.js';
|
||||
import FormatType from '../format/FormatType.js';
|
||||
import {isDocument, parse} from '../xml.js';
|
||||
import {isDocument, parse, getXMLSerializer} from '../xml.js';
|
||||
|
||||
/**
|
||||
* @classdesc
|
||||
@@ -23,7 +23,7 @@ class XMLFeature extends FeatureFormat {
|
||||
* @type {XMLSerializer}
|
||||
* @private
|
||||
*/
|
||||
this.xmlSerializer_ = new XMLSerializer();
|
||||
this.xmlSerializer_ = getXMLSerializer();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/**
|
||||
* @module ol/format/xsd
|
||||
*/
|
||||
import {getAllTextContent, DOCUMENT} from '../xml.js';
|
||||
import {getAllTextContent, getDocument} from '../xml.js';
|
||||
import {padNumber} from '../string.js';
|
||||
|
||||
|
||||
@@ -112,7 +112,7 @@ export function writeBooleanTextNode(node, bool) {
|
||||
* @param {string} string String.
|
||||
*/
|
||||
export function writeCDATASection(node, string) {
|
||||
node.appendChild(DOCUMENT.createCDATASection(string));
|
||||
node.appendChild(getDocument().createCDATASection(string));
|
||||
}
|
||||
|
||||
|
||||
@@ -128,7 +128,7 @@ export function writeDateTimeTextNode(node, dateTime) {
|
||||
padNumber(date.getUTCHours(), 2) + ':' +
|
||||
padNumber(date.getUTCMinutes(), 2) + ':' +
|
||||
padNumber(date.getUTCSeconds(), 2) + 'Z';
|
||||
node.appendChild(DOCUMENT.createTextNode(string));
|
||||
node.appendChild(getDocument().createTextNode(string));
|
||||
}
|
||||
|
||||
|
||||
@@ -138,7 +138,7 @@ export function writeDateTimeTextNode(node, dateTime) {
|
||||
*/
|
||||
export function writeDecimalTextNode(node, decimal) {
|
||||
const string = decimal.toPrecision();
|
||||
node.appendChild(DOCUMENT.createTextNode(string));
|
||||
node.appendChild(getDocument().createTextNode(string));
|
||||
}
|
||||
|
||||
|
||||
@@ -148,7 +148,7 @@ export function writeDecimalTextNode(node, decimal) {
|
||||
*/
|
||||
export function writeNonNegativeIntegerTextNode(node, nonNegativeInteger) {
|
||||
const string = nonNegativeInteger.toString();
|
||||
node.appendChild(DOCUMENT.createTextNode(string));
|
||||
node.appendChild(getDocument().createTextNode(string));
|
||||
}
|
||||
|
||||
|
||||
@@ -157,5 +157,5 @@ export function writeNonNegativeIntegerTextNode(node, nonNegativeInteger) {
|
||||
* @param {string} string String.
|
||||
*/
|
||||
export function writeStringTextNode(node, string) {
|
||||
node.appendChild(DOCUMENT.createTextNode(string));
|
||||
node.appendChild(getDocument().createTextNode(string));
|
||||
}
|
||||
|
||||
@@ -13,6 +13,8 @@ import PointerInteraction, {centroid as centroidFromPointers} from './Pointer.js
|
||||
* @property {import("../events/condition.js").Condition} [condition] A function that takes an {@link module:ol/MapBrowserEvent~MapBrowserEvent} and returns a boolean
|
||||
* to indicate whether that event should be handled.
|
||||
* Default is {@link module:ol/events/condition~noModifierKeys} and {@link module:ol/events/condition~primaryAction}.
|
||||
* In addition, if there is a `tabindex` attribute on the map element,
|
||||
* {@link module:ol/events/condition~focus} will also be applied.
|
||||
* @property {import("../Kinetic.js").default} [kinetic] Kinetic inertia to apply to the pan.
|
||||
*/
|
||||
|
||||
|
||||
+65
-14
@@ -143,7 +143,13 @@ const DrawEventType = {
|
||||
* @event DrawEvent#drawend
|
||||
* @api
|
||||
*/
|
||||
DRAWEND: 'drawend'
|
||||
DRAWEND: 'drawend',
|
||||
/**
|
||||
* Triggered upon feature draw abortion
|
||||
* @event DrawEvent#drawabort
|
||||
* @api
|
||||
*/
|
||||
DRAWABORT: 'drawabort'
|
||||
};
|
||||
|
||||
|
||||
@@ -505,7 +511,7 @@ class Draw extends PointerInteraction {
|
||||
if (this.freehand_ &&
|
||||
event.type === MapBrowserEventType.POINTERDRAG &&
|
||||
this.sketchFeature_ !== null) {
|
||||
this.addToDrawing_(event);
|
||||
this.addToDrawing_(event.coordinate);
|
||||
pass = false;
|
||||
} else if (this.freehand_ &&
|
||||
event.type === MapBrowserEventType.POINTERDOWN) {
|
||||
@@ -580,12 +586,11 @@ class Draw extends PointerInteraction {
|
||||
this.finishDrawing();
|
||||
}
|
||||
} else {
|
||||
this.addToDrawing_(event);
|
||||
this.addToDrawing_(event.coordinate);
|
||||
}
|
||||
pass = false;
|
||||
} else if (this.freehand_) {
|
||||
this.finishCoordinate_ = null;
|
||||
this.abortDrawing_();
|
||||
this.abortDrawing();
|
||||
}
|
||||
if (!pass && this.stopClick_) {
|
||||
event.stopPropagation();
|
||||
@@ -764,13 +769,12 @@ class Draw extends PointerInteraction {
|
||||
|
||||
/**
|
||||
* Add a new coordinate to the drawing.
|
||||
* @param {import("../MapBrowserEvent.js").default} event Event.
|
||||
* @param {!PointCoordType} coordinate Coordinate
|
||||
* @private
|
||||
*/
|
||||
addToDrawing_(event) {
|
||||
const coordinate = event.coordinate;
|
||||
addToDrawing_(coordinate) {
|
||||
const geometry = this.sketchFeature_.getGeometry();
|
||||
const projection = event.map.getView().getProjection();
|
||||
const projection = this.getMap().getView().getProjection();
|
||||
let done;
|
||||
let coordinates;
|
||||
if (this.mode_ === Mode.LINE_STRING) {
|
||||
@@ -835,7 +839,7 @@ class Draw extends PointerInteraction {
|
||||
}
|
||||
|
||||
if (coordinates.length === 0) {
|
||||
this.finishCoordinate_ = null;
|
||||
this.abortDrawing();
|
||||
}
|
||||
|
||||
this.updateSketchFeatures_();
|
||||
@@ -903,9 +907,56 @@ class Draw extends PointerInteraction {
|
||||
}
|
||||
|
||||
/**
|
||||
* Extend an existing geometry by adding additional points. This only works
|
||||
* on features with `LineString` geometries, where the interaction will
|
||||
* extend lines by adding points to the end of the coordinates array.
|
||||
* Stop drawing without adding the sketch feature to the target layer.
|
||||
* @api
|
||||
*/
|
||||
abortDrawing() {
|
||||
const sketchFeature = this.abortDrawing_();
|
||||
if (sketchFeature) {
|
||||
this.dispatchEvent(new DrawEvent(DrawEventType.DRAWABORT, sketchFeature));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Append coordinates to the end of the geometry that is currently being drawn.
|
||||
* This can be used when drawing LineStrings or Polygons. Coordinates will
|
||||
* either be appended to the current LineString or the outer ring of the current
|
||||
* Polygon.
|
||||
* @param {!LineCoordType} coordinates Linear coordinates to be appended into
|
||||
* the coordinate array.
|
||||
* @api
|
||||
*/
|
||||
appendCoordinates(coordinates) {
|
||||
const mode = this.mode_;
|
||||
let sketchCoords = [];
|
||||
if (mode === Mode.LINE_STRING) {
|
||||
sketchCoords = /** @type {LineCoordType} */ this.sketchCoords_;
|
||||
} else if (mode === Mode.POLYGON) {
|
||||
sketchCoords = this.sketchCoords_ && this.sketchCoords_.length ? /** @type {PolyCoordType} */ (this.sketchCoords_)[0] : [];
|
||||
}
|
||||
|
||||
// Remove last coordinate from sketch drawing (this coordinate follows cursor position)
|
||||
const ending = sketchCoords.pop();
|
||||
|
||||
// Append coordinate list
|
||||
for (let i = 0; i < coordinates.length; i++) {
|
||||
this.addToDrawing_(coordinates[i]);
|
||||
}
|
||||
|
||||
// Duplicate last coordinate for sketch drawing
|
||||
this.addToDrawing_(ending);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initiate draw mode by starting from an existing geometry which will
|
||||
* receive new additional points. This only works on features with
|
||||
* `LineString` geometries, where the interaction will extend lines by adding
|
||||
* points to the end of the coordinates array.
|
||||
* This will change the original feature, instead of drawing a copy.
|
||||
*
|
||||
* The function will dispatch a `drawstart` event.
|
||||
*
|
||||
* @param {!Feature<LineString>} feature Feature to be extended.
|
||||
* @api
|
||||
*/
|
||||
@@ -948,7 +999,7 @@ class Draw extends PointerInteraction {
|
||||
const map = this.getMap();
|
||||
const active = this.getActive();
|
||||
if (!map || !active) {
|
||||
this.abortDrawing_();
|
||||
this.abortDrawing();
|
||||
}
|
||||
this.overlay_.setMap(active ? map : null);
|
||||
}
|
||||
|
||||
@@ -23,6 +23,8 @@ export const Mode = {
|
||||
* takes an {@link module:ol/MapBrowserEvent~MapBrowserEvent} and returns a
|
||||
* boolean to indicate whether that event should be handled. Default is
|
||||
* {@link module:ol/events/condition~always}.
|
||||
* In addition, if there is a `tabindex` attribute on the map element,
|
||||
* {@link module:ol/events/condition~focus} will also be applied.
|
||||
* @property {number} [maxDelta=1] Maximum mouse wheel delta.
|
||||
* @property {number} [duration=250] Animation duration in milliseconds.
|
||||
* @property {number} [timeout=80] Mouse wheel timeout duration in milliseconds.
|
||||
@@ -130,7 +132,7 @@ class MouseWheelZoom extends Interaction {
|
||||
* @private
|
||||
* @type {number}
|
||||
*/
|
||||
this.trackpadDeltaPerZoom_ = 300;
|
||||
this.deltaPerZoom_ = 300;
|
||||
|
||||
}
|
||||
|
||||
@@ -212,15 +214,18 @@ class MouseWheelZoom extends Interaction {
|
||||
Mode.WHEEL;
|
||||
}
|
||||
|
||||
if (this.mode_ === Mode.TRACKPAD) {
|
||||
const view = map.getView();
|
||||
const view = map.getView();
|
||||
if (this.mode_ === Mode.TRACKPAD && !view.getConstrainResolution()) {
|
||||
if (this.trackpadTimeoutId_) {
|
||||
clearTimeout(this.trackpadTimeoutId_);
|
||||
} else {
|
||||
if (view.getAnimating()) {
|
||||
view.cancelAnimations();
|
||||
}
|
||||
view.beginInteraction();
|
||||
}
|
||||
this.trackpadTimeoutId_ = setTimeout(this.endInteraction_.bind(this), this.trackpadEventGap_);
|
||||
view.adjustZoom(-delta / this.trackpadDeltaPerZoom_, this.lastAnchor_);
|
||||
this.trackpadTimeoutId_ = setTimeout(this.endInteraction_.bind(this), this.timeout_);
|
||||
view.adjustZoom(-delta / this.deltaPerZoom_, this.lastAnchor_);
|
||||
this.startTime_ = now;
|
||||
return false;
|
||||
}
|
||||
@@ -244,8 +249,13 @@ class MouseWheelZoom extends Interaction {
|
||||
if (view.getAnimating()) {
|
||||
view.cancelAnimations();
|
||||
}
|
||||
const delta = clamp(this.totalDelta_, -this.maxDelta_, this.maxDelta_);
|
||||
zoomByDelta(view, -delta, this.lastAnchor_, this.duration_);
|
||||
let delta = -clamp(this.totalDelta_, -this.maxDelta_ * this.deltaPerZoom_, this.maxDelta_ * this.deltaPerZoom_) / this.deltaPerZoom_;
|
||||
if (view.getConstrainResolution()) {
|
||||
// view has a zoom constraint, zoom by 1
|
||||
delta = delta ? delta > 0 ? 1 : -1 : 0;
|
||||
}
|
||||
zoomByDelta(view, delta, this.lastAnchor_, this.duration_);
|
||||
|
||||
this.mode_ = undefined;
|
||||
this.totalDelta_ = 0;
|
||||
this.lastAnchor_ = null;
|
||||
|
||||
@@ -61,7 +61,7 @@ const SelectEventType = {
|
||||
* @property {import("../style/Style.js").StyleLike} [style]
|
||||
* Style for the selected features. By default the default edit style is used
|
||||
* (see {@link module:ol/style}).
|
||||
* If set to `false` the selected feature's style will not change.
|
||||
* If set to a falsey value, the selected feature's style will not change.
|
||||
* @property {import("../events/condition.js").Condition} [removeCondition] A function
|
||||
* that takes an {@link module:ol/MapBrowserEvent~MapBrowserEvent} and returns a
|
||||
* boolean to indicate whether that event should be handled.
|
||||
@@ -133,6 +133,12 @@ class SelectEvent extends Event {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Original feature styles to reset to when features are no longer selected.
|
||||
* @type {Object.<number, import("../style/Style.js").default|Array.<import("../style/Style.js").default>|import("../style/Style.js").StyleFunction>}
|
||||
*/
|
||||
const originalFeatureStyles = {};
|
||||
|
||||
|
||||
/**
|
||||
* @classdesc
|
||||
@@ -209,14 +215,6 @@ class Select extends Interaction {
|
||||
*/
|
||||
this.style_ = options.style !== undefined ? options.style : getDefaultStyleFunction();
|
||||
|
||||
/**
|
||||
* An association between selected feature (key)
|
||||
* and original style (value)
|
||||
* @private
|
||||
* @type {Object.<number, import("../style/Style.js").default|Array.<import("../style/Style.js").default>|import("../style/Style.js").StyleFunction>}
|
||||
*/
|
||||
this.featureStyleAssociation_ = {};
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @type {import("../Collection.js").default}
|
||||
@@ -319,11 +317,11 @@ class Select extends Interaction {
|
||||
setMap(map) {
|
||||
const currentMap = this.getMap();
|
||||
if (currentMap && this.style_) {
|
||||
this.features_.forEach(this.removeSelectedStyle_.bind(this));
|
||||
this.features_.forEach(this.restorePreviousStyle_.bind(this));
|
||||
}
|
||||
super.setMap(map);
|
||||
if (map && this.style_) {
|
||||
this.features_.forEach(this.giveSelectedStyle_.bind(this));
|
||||
this.features_.forEach(this.applySelectedStyle_.bind(this));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -334,7 +332,7 @@ class Select extends Interaction {
|
||||
addFeature_(evt) {
|
||||
const feature = evt.element;
|
||||
if (this.style_) {
|
||||
this.giveSelectedStyle_(feature);
|
||||
this.applySelectedStyle_(feature);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -345,17 +343,26 @@ class Select extends Interaction {
|
||||
removeFeature_(evt) {
|
||||
const feature = evt.element;
|
||||
if (this.style_) {
|
||||
this.removeSelectedStyle_(feature);
|
||||
this.restorePreviousStyle_(feature);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {import("../style/Style.js").default|Array.<import("../style/Style.js").default>|import("../style/Style.js").StyleFunction|null} Select style.
|
||||
*/
|
||||
getStyle() {
|
||||
return this.style_;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {import("../Feature.js").default} feature Feature
|
||||
* @private
|
||||
*/
|
||||
giveSelectedStyle_(feature) {
|
||||
applySelectedStyle_(feature) {
|
||||
const key = getUid(feature);
|
||||
this.featureStyleAssociation_[key] = feature.getStyle();
|
||||
if (!(key in originalFeatureStyles)) {
|
||||
originalFeatureStyles[key] = feature.getStyle();
|
||||
}
|
||||
feature.setStyle(this.style_);
|
||||
}
|
||||
|
||||
@@ -363,10 +370,17 @@ class Select extends Interaction {
|
||||
* @param {import("../Feature.js").default} feature Feature
|
||||
* @private
|
||||
*/
|
||||
removeSelectedStyle_(feature) {
|
||||
restorePreviousStyle_(feature) {
|
||||
const key = getUid(feature);
|
||||
feature.setStyle(this.featureStyleAssociation_[key]);
|
||||
delete this.featureStyleAssociation_[key];
|
||||
const selectInteractions = /** @type {Array<Select>} */ (this.getMap().getInteractions().getArray().filter(function(interaction) {
|
||||
return interaction instanceof Select && interaction.getStyle() && interaction.getFeatures().getArray().indexOf(feature) !== -1;
|
||||
}));
|
||||
if (selectInteractions.length > 0) {
|
||||
feature.setStyle(selectInteractions[selectInteractions.length - 1].getStyle());
|
||||
} else {
|
||||
feature.setStyle(originalFeatureStyles[key]);
|
||||
delete originalFeatureStyles[key];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -19,6 +19,10 @@ import Layer from './Layer.js';
|
||||
* visible.
|
||||
* @property {number} [maxResolution] The maximum resolution (exclusive) below which this layer will
|
||||
* be visible.
|
||||
* @property {number} [minZoom] The minimum view zoom level (exclusive) above which this layer will be
|
||||
* visible.
|
||||
* @property {number} [maxZoom] The maximum view zoom level (inclusive) at which this layer will
|
||||
* be visible.
|
||||
* @property {import("../PluggableMap.js").default} [map] Sets the layer as overlay on a map. The map will not manage
|
||||
* this layer in its layers collection, and the layer will be rendered on top. This is useful for
|
||||
* temporary layers. The standard way to add a layer to a map and have it managed by the map is to
|
||||
|
||||
@@ -21,6 +21,10 @@ import {assign} from '../obj.js';
|
||||
* visible.
|
||||
* @property {number} [maxResolution] The maximum resolution (exclusive) below which this layer will
|
||||
* be visible.
|
||||
* @property {number} [minZoom] The minimum view zoom level (exclusive) above which this layer will be
|
||||
* visible.
|
||||
* @property {number} [maxZoom] The maximum view zoom level (inclusive) at which this layer will
|
||||
* be visible.
|
||||
* @property {number} [preload=0] Preload. Load low-resolution tiles up to `preload` levels. `0`
|
||||
* means no preloading.
|
||||
* @property {import("../source/Tile.js").default} [source] Source for this layer.
|
||||
|
||||
@@ -21,6 +21,10 @@ import {createDefaultStyle, toFunction as toStyleFunction} from '../style/Style.
|
||||
* visible.
|
||||
* @property {number} [maxResolution] The maximum resolution (exclusive) below which this layer will
|
||||
* be visible.
|
||||
* @property {number} [minZoom] The minimum view zoom level (exclusive) above which this layer will be
|
||||
* visible.
|
||||
* @property {number} [maxZoom] The maximum view zoom level (inclusive) at which this layer will
|
||||
* be visible.
|
||||
* @property {import("../render.js").OrderFunction} [renderOrder] Render order. Function to be used when sorting
|
||||
* features before rendering. By default features are drawn in the order that they are created. Use
|
||||
* `null` to avoid the sort, but get an undefined draw order.
|
||||
|
||||
+143
-63
@@ -15,15 +15,16 @@ import {
|
||||
getTransform,
|
||||
transformExtent
|
||||
} from '../proj.js';
|
||||
import {getCenter, intersects, equals, getIntersection, isEmpty} from '../extent.js';
|
||||
import {getCenter, getHeight, getWidth, intersects, equals, getIntersection, isEmpty} from '../extent.js';
|
||||
import {clamp} from '../math.js';
|
||||
import Style from '../style/Style.js';
|
||||
import Feature from '../Feature.js';
|
||||
import {bbox} from '../loadingstrategy.js';
|
||||
import {meridian, parallel} from '../geom/flat/geodesic.js';
|
||||
import GeometryLayout from '../geom/GeometryLayout.js';
|
||||
import Point from '../geom/Point.js';
|
||||
import Collection from '../Collection.js';
|
||||
import {getVectorContext} from '../render.js';
|
||||
import EventType from '../render/EventType.js';
|
||||
|
||||
|
||||
/**
|
||||
@@ -65,6 +66,10 @@ const INTERVALS = [
|
||||
* visible.
|
||||
* @property {number} [maxResolution] The maximum resolution (exclusive) below which this layer will
|
||||
* be visible.
|
||||
* @property {number} [minZoom] The minimum view zoom level (exclusive) above which this layer will be
|
||||
* visible.
|
||||
* @property {number} [maxZoom] The maximum view zoom level (inclusive) at which this layer will
|
||||
* be visible.
|
||||
* @property {number} [maxLines=100] The maximum number of meridians and
|
||||
* parallels from the center of the map. The default value of 100 means that at
|
||||
* most 200 meridians and 200 parallels will be displayed. The default value is
|
||||
@@ -379,6 +384,8 @@ class Graticule extends VectorLayer {
|
||||
|
||||
this.meridiansLabels_ = [];
|
||||
this.parallelsLabels_ = [];
|
||||
|
||||
this.addEventListener(EventType.POSTRENDER, this.drawLabels_.bind(this));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -391,7 +398,7 @@ class Graticule extends VectorLayer {
|
||||
this.setSource(
|
||||
new VectorSource({
|
||||
loader: this.loaderFunction.bind(this),
|
||||
strategy: bbox,
|
||||
strategy: this.strategyFunction.bind(this),
|
||||
features: new Collection(),
|
||||
overlaps: false,
|
||||
useSpatialIndex: false,
|
||||
@@ -414,6 +421,12 @@ class Graticule extends VectorLayer {
|
||||
stroke: this.strokeStyle_
|
||||
});
|
||||
|
||||
/**
|
||||
* @type {?import("../extent.js").Extent}
|
||||
* @private
|
||||
*/
|
||||
this.loadedExtent_ = null;
|
||||
|
||||
/**
|
||||
* @type {?import("../extent.js").Extent}
|
||||
*/
|
||||
@@ -421,7 +434,21 @@ class Graticule extends VectorLayer {
|
||||
|
||||
this.setRenderOrder(null);
|
||||
|
||||
this.tmpExtent_ = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Strategy function for loading features based on the view's extent and
|
||||
* resolution.
|
||||
* @param {import("../extent.js").Extent} extent Extent.
|
||||
* @param {number} resolution Resolution.
|
||||
* @return {Array<import("../extent.js").Extent>} Extents.
|
||||
*/
|
||||
strategyFunction(extent, resolution) {
|
||||
if (this.loadedExtent_ && !equals(this.loadedExtent_, extent)) {
|
||||
// we should not keep track of loaded extents
|
||||
this.getSource().removeLoadedExtent(this.loadedExtent_);
|
||||
}
|
||||
return [extent];
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -431,16 +458,12 @@ class Graticule extends VectorLayer {
|
||||
* @param {import("../proj/Projection.js").default} projection Projection
|
||||
*/
|
||||
loaderFunction(extent, resolution, projection) {
|
||||
this.loadedExtent_ = extent;
|
||||
const source = this.getSource();
|
||||
|
||||
// only consider the intersection between our own extent & the requested one
|
||||
const layerExtent = this.getExtent() || [-Infinity, -Infinity, Infinity, Infinity];
|
||||
const renderExtent = getIntersection(layerExtent, extent, this.tmpExtent_);
|
||||
|
||||
// we should not keep track of loaded extents
|
||||
setTimeout(function() {
|
||||
source.removeLoadedExtent(extent);
|
||||
}, 0);
|
||||
const renderExtent = getIntersection(layerExtent, extent);
|
||||
|
||||
if (this.renderedExtent_ && equals(this.renderedExtent_, renderExtent)) {
|
||||
return;
|
||||
@@ -468,10 +491,10 @@ class Graticule extends VectorLayer {
|
||||
// first make sure we have enough features in the pool
|
||||
let featureCount = this.meridians_.length + this.parallels_.length;
|
||||
if (this.meridiansLabels_) {
|
||||
featureCount += this.meridiansLabels_.length;
|
||||
featureCount += this.meridians_.length;
|
||||
}
|
||||
if (this.parallelsLabels_) {
|
||||
featureCount += this.parallelsLabels_.length;
|
||||
featureCount += this.parallels_.length;
|
||||
}
|
||||
|
||||
let feature;
|
||||
@@ -498,27 +521,6 @@ class Graticule extends VectorLayer {
|
||||
feature.setStyle(this.lineStyle_);
|
||||
featuresColl.push(feature);
|
||||
}
|
||||
let labelData;
|
||||
if (this.meridiansLabels_) {
|
||||
for (i = 0, l = this.meridiansLabels_.length; i < l; ++i) {
|
||||
labelData = this.meridiansLabels_[i];
|
||||
feature = this.featurePool_[poolIndex++];
|
||||
feature.setGeometry(labelData.geom);
|
||||
feature.setStyle(this.lonLabelStyle_);
|
||||
feature.set('graticule_label', labelData.text);
|
||||
featuresColl.push(feature);
|
||||
}
|
||||
}
|
||||
if (this.parallelsLabels_) {
|
||||
for (i = 0, l = this.parallelsLabels_.length; i < l; ++i) {
|
||||
labelData = this.parallelsLabels_[i];
|
||||
feature = this.featurePool_[poolIndex++];
|
||||
feature.setGeometry(labelData.geom);
|
||||
feature.setStyle(this.latLabelStyle_);
|
||||
feature.set('graticule_label', labelData.text);
|
||||
featuresColl.push(feature);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -535,11 +537,15 @@ class Graticule extends VectorLayer {
|
||||
const lineString = this.getMeridian_(lon, minLat, maxLat, squaredTolerance, index);
|
||||
if (intersects(lineString.getExtent(), extent)) {
|
||||
if (this.meridiansLabels_) {
|
||||
const textPoint = this.getMeridianPoint_(lineString, extent, index);
|
||||
this.meridiansLabels_[index] = {
|
||||
geom: textPoint,
|
||||
text: this.lonLabelFormatter_(lon)
|
||||
};
|
||||
const text = this.lonLabelFormatter_(lon);
|
||||
if (index in this.meridiansLabels_) {
|
||||
this.meridiansLabels_[index].text = text;
|
||||
} else {
|
||||
this.meridiansLabels_[index] = {
|
||||
geom: new Point([]),
|
||||
text: text
|
||||
};
|
||||
}
|
||||
}
|
||||
this.meridians_[index++] = lineString;
|
||||
}
|
||||
@@ -560,17 +566,83 @@ class Graticule extends VectorLayer {
|
||||
const lineString = this.getParallel_(lat, minLon, maxLon, squaredTolerance, index);
|
||||
if (intersects(lineString.getExtent(), extent)) {
|
||||
if (this.parallelsLabels_) {
|
||||
const textPoint = this.getParallelPoint_(lineString, extent, index);
|
||||
this.parallelsLabels_[index] = {
|
||||
geom: textPoint,
|
||||
text: this.latLabelFormatter_(lat)
|
||||
};
|
||||
const text = this.latLabelFormatter_(lat);
|
||||
if (index in this.parallelsLabels_) {
|
||||
this.parallelsLabels_[index].text = text;
|
||||
} else {
|
||||
this.parallelsLabels_[index] = {
|
||||
geom: new Point([]),
|
||||
text: text
|
||||
};
|
||||
}
|
||||
}
|
||||
this.parallels_[index++] = lineString;
|
||||
}
|
||||
return index;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {import("../render/Event.js").default} event Render event.
|
||||
* @private
|
||||
*/
|
||||
drawLabels_(event) {
|
||||
const rotation = event.frameState.viewState.rotation;
|
||||
const extent = event.frameState.extent;
|
||||
let rotationCenter, rotationExtent;
|
||||
if (rotation) {
|
||||
rotationCenter = getCenter(extent);
|
||||
const width = getWidth(extent);
|
||||
const height = getHeight(extent);
|
||||
const cr = Math.abs(Math.cos(rotation));
|
||||
const sr = Math.abs(Math.sin(rotation));
|
||||
const unrotatedWidth = (sr * height - cr * width) / (sr * sr - cr * cr);
|
||||
const unrotatedHeight = (sr * width - cr * height) / (sr * sr - cr * cr);
|
||||
rotationExtent = [
|
||||
rotationCenter[0] - unrotatedWidth / 2, rotationCenter[1] - unrotatedHeight / 2,
|
||||
rotationCenter[0] + unrotatedWidth / 2, rotationCenter[1] + unrotatedHeight / 2
|
||||
];
|
||||
}
|
||||
|
||||
const vectorContext = getVectorContext(event);
|
||||
let poolIndex = this.meridians_.length + this.parallels_.length;
|
||||
let feature, index, l, textPoint;
|
||||
|
||||
if (this.meridiansLabels_) {
|
||||
for (index = 0, l = this.meridiansLabels_.length; index < l; ++index) {
|
||||
const lineString = this.meridians_[index];
|
||||
if (!rotation) {
|
||||
textPoint = this.getMeridianPoint_(lineString, extent, index);
|
||||
} else {
|
||||
const clone = lineString.clone();
|
||||
clone.rotate(-rotation, rotationCenter);
|
||||
textPoint = this.getMeridianPoint_(clone, rotationExtent, index);
|
||||
textPoint.rotate(rotation, rotationCenter);
|
||||
}
|
||||
feature = this.featurePool_[poolIndex++];
|
||||
feature.setGeometry(textPoint);
|
||||
feature.set('graticule_label', this.meridiansLabels_[index].text);
|
||||
vectorContext.drawFeature(feature, this.lonLabelStyle_(feature));
|
||||
}
|
||||
}
|
||||
if (this.parallelsLabels_) {
|
||||
for (index = 0, l = this.parallels_.length; index < l; ++index) {
|
||||
const lineString = this.parallels_[index];
|
||||
if (!rotation) {
|
||||
textPoint = this.getParallelPoint_(lineString, extent, index);
|
||||
} else {
|
||||
const clone = lineString.clone();
|
||||
clone.rotate(-rotation, rotationCenter);
|
||||
textPoint = this.getParallelPoint_(clone, rotationExtent, index);
|
||||
textPoint.rotate(rotation, rotationCenter);
|
||||
}
|
||||
feature = this.featurePool_[poolIndex++];
|
||||
feature.setGeometry(textPoint);
|
||||
feature.set('graticule_label', this.parallelsLabels_[index].text);
|
||||
vectorContext.drawFeature(feature, this.latLabelStyle_(feature));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {import("../extent.js").Extent} extent Extent.
|
||||
* @param {import("../coordinate.js").Coordinate} center Center.
|
||||
@@ -727,19 +799,23 @@ class Graticule extends VectorLayer {
|
||||
*/
|
||||
getMeridianPoint_(lineString, extent, index) {
|
||||
const flatCoordinates = lineString.getFlatCoordinates();
|
||||
const clampedBottom = Math.max(extent[1], flatCoordinates[1]);
|
||||
const clampedTop = Math.min(extent[3], flatCoordinates[flatCoordinates.length - 1]);
|
||||
let bottom = 1;
|
||||
let top = flatCoordinates.length - 1;
|
||||
if (flatCoordinates[bottom] > flatCoordinates[top]) {
|
||||
bottom = top;
|
||||
top = 1;
|
||||
}
|
||||
const clampedBottom = Math.max(extent[1], flatCoordinates[bottom]);
|
||||
const clampedTop = Math.min(extent[3], flatCoordinates[top]);
|
||||
const lat = clamp(
|
||||
extent[1] + Math.abs(extent[1] - extent[3]) * this.lonLabelPosition_,
|
||||
clampedBottom, clampedTop);
|
||||
const coordinate = [flatCoordinates[0], lat];
|
||||
let point;
|
||||
if (index in this.meridiansLabels_) {
|
||||
point = this.meridiansLabels_[index].geom;
|
||||
point.setCoordinates(coordinate);
|
||||
} else {
|
||||
point = new Point(coordinate);
|
||||
}
|
||||
const coordinate0 = flatCoordinates[bottom - 1] +
|
||||
(flatCoordinates[top - 1] - flatCoordinates[bottom - 1]) * (lat - flatCoordinates[bottom]) /
|
||||
(flatCoordinates[top] - flatCoordinates[bottom]);
|
||||
const coordinate = [coordinate0, lat];
|
||||
const point = this.meridiansLabels_[index].geom;
|
||||
point.setCoordinates(coordinate);
|
||||
return point;
|
||||
}
|
||||
|
||||
@@ -783,19 +859,23 @@ class Graticule extends VectorLayer {
|
||||
*/
|
||||
getParallelPoint_(lineString, extent, index) {
|
||||
const flatCoordinates = lineString.getFlatCoordinates();
|
||||
const clampedLeft = Math.max(extent[0], flatCoordinates[0]);
|
||||
const clampedRight = Math.min(extent[2], flatCoordinates[flatCoordinates.length - 2]);
|
||||
let left = 0;
|
||||
let right = flatCoordinates.length - 2;
|
||||
if (flatCoordinates[left] > flatCoordinates[right]) {
|
||||
left = right;
|
||||
right = 0;
|
||||
}
|
||||
const clampedLeft = Math.max(extent[0], flatCoordinates[left]);
|
||||
const clampedRight = Math.min(extent[2], flatCoordinates[right]);
|
||||
const lon = clamp(
|
||||
extent[0] + Math.abs(extent[0] - extent[2]) * this.latLabelPosition_,
|
||||
clampedLeft, clampedRight);
|
||||
const coordinate = [lon, flatCoordinates[1]];
|
||||
let point;
|
||||
if (index in this.parallelsLabels_) {
|
||||
point = this.parallelsLabels_[index].geom;
|
||||
point.setCoordinates(coordinate);
|
||||
} else {
|
||||
point = new Point(coordinate);
|
||||
}
|
||||
const coordinate1 = flatCoordinates[left + 1] +
|
||||
(flatCoordinates[right + 1] - flatCoordinates[left + 1]) * (lon - flatCoordinates[left]) /
|
||||
(flatCoordinates[right] - flatCoordinates[left]);
|
||||
const coordinate = [lon, coordinate1];
|
||||
const point = this.parallelsLabels_[index].geom;
|
||||
point.setCoordinates(coordinate);
|
||||
return point;
|
||||
}
|
||||
|
||||
|
||||
@@ -33,6 +33,10 @@ import SourceState from '../source/State.js';
|
||||
* visible.
|
||||
* @property {number} [maxZoom] The maximum view zoom level (inclusive) at which this layer will
|
||||
* be visible.
|
||||
* @property {number} [minZoom] The minimum view zoom level (exclusive) above which this layer will be
|
||||
* visible.
|
||||
* @property {number} [maxZoom] The maximum view zoom level (inclusive) at which this layer will
|
||||
* be visible.
|
||||
* @property {Array<import("./Base.js").default>|import("../Collection.js").default<import("./Base.js").default>} [layers] Child layers.
|
||||
*/
|
||||
|
||||
|
||||
@@ -24,6 +24,10 @@ import WebGLPointsLayerRenderer from '../renderer/webgl/PointsLayer.js';
|
||||
* visible.
|
||||
* @property {number} [maxResolution] The maximum resolution (exclusive) below which this layer will
|
||||
* be visible.
|
||||
* @property {number} [minZoom] The minimum view zoom level (exclusive) above which this layer will be
|
||||
* visible.
|
||||
* @property {number} [maxZoom] The maximum view zoom level (inclusive) at which this layer will
|
||||
* be visible.
|
||||
* @property {Array<string>} [gradient=['#00f', '#0ff', '#0f0', '#ff0', '#f00']] The color gradient
|
||||
* of the heatmap, specified as an array of CSS color strings.
|
||||
* @property {number} [radius=8] Radius size in pixels.
|
||||
@@ -294,7 +298,9 @@ class Heatmap extends VectorLayer {
|
||||
gl_FragColor.rgb *= gl_FragColor.a;
|
||||
}`,
|
||||
uniforms: {
|
||||
u_gradientTexture: this.gradient_
|
||||
u_gradientTexture: function() {
|
||||
return this.gradient_;
|
||||
}.bind(this)
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
+12
-1
@@ -9,6 +9,7 @@ import LayerProperty from './Property.js';
|
||||
import {assign} from '../obj.js';
|
||||
import RenderEventType from '../render/EventType.js';
|
||||
import SourceState from '../source/State.js';
|
||||
import {assert} from '../asserts.js';
|
||||
|
||||
/**
|
||||
* @typedef {function(import("../PluggableMap.js").FrameState):HTMLElement} RenderFunction
|
||||
@@ -30,6 +31,10 @@ import SourceState from '../source/State.js';
|
||||
* visible.
|
||||
* @property {number} [maxResolution] The maximum resolution (exclusive) below which this layer will
|
||||
* be visible.
|
||||
* @property {number} [minZoom] The minimum view zoom level (exclusive) above which this layer will be
|
||||
* visible.
|
||||
* @property {number} [maxZoom] The maximum view zoom level (inclusive) at which this layer will
|
||||
* be visible.
|
||||
* @property {import("../source/Source.js").default} [source] Source for this layer. If not provided to the constructor,
|
||||
* the source can be set by calling {@link module:ol/layer/Layer#setSource layer.setSource(source)} after
|
||||
* construction.
|
||||
@@ -243,7 +248,13 @@ class Layer extends BaseLayer {
|
||||
if (map) {
|
||||
this.mapPrecomposeKey_ = listen(map, RenderEventType.PRECOMPOSE, function(evt) {
|
||||
const renderEvent = /** @type {import("../render/Event.js").default} */ (evt);
|
||||
renderEvent.frameState.layerStatesArray.push(this.getLayerState(false));
|
||||
const layerStatesArray = renderEvent.frameState.layerStatesArray;
|
||||
const layerState = this.getLayerState(false);
|
||||
// A layer can only be added to the map once. Use either `layer.setMap()` or `map.addLayer()`, not both.
|
||||
assert(!layerStatesArray.some(function(arrayLayerState) {
|
||||
return arrayLayerState.layer === layerState.layer;
|
||||
}), 67);
|
||||
layerStatesArray.push(layerState);
|
||||
}, this);
|
||||
this.mapRenderKey_ = listen(this, EventType.CHANGE, map.render, map);
|
||||
this.changed();
|
||||
|
||||
@@ -20,6 +20,10 @@ import CanvasVectorImageLayerRenderer from '../renderer/canvas/VectorImageLayer.
|
||||
* visible.
|
||||
* @property {number} [maxResolution] The maximum resolution (exclusive) below which this layer will
|
||||
* be visible.
|
||||
* @property {number} [minZoom] The minimum view zoom level (exclusive) above which this layer will be
|
||||
* visible.
|
||||
* @property {number} [maxZoom] The maximum view zoom level (inclusive) at which this layer will
|
||||
* be visible.
|
||||
* @property {import("../render.js").OrderFunction} [renderOrder] Render order. Function to be used when sorting
|
||||
* features before rendering. By default features are drawn in the order that they are created. Use
|
||||
* `null` to avoid the sort, but get an undefined draw order.
|
||||
|
||||
@@ -24,6 +24,10 @@ import {assign} from '../obj.js';
|
||||
* visible.
|
||||
* @property {number} [maxResolution] The maximum resolution (exclusive) below which this layer will
|
||||
* be visible.
|
||||
* @property {number} [minZoom] The minimum view zoom level (exclusive) above which this layer will be
|
||||
* visible.
|
||||
* @property {number} [maxZoom] The maximum view zoom level (inclusive) at which this layer will
|
||||
* be visible.
|
||||
* @property {import("../render.js").OrderFunction} [renderOrder] Render order. Function to be used when sorting
|
||||
* features before rendering. By default features are drawn in the order that they are created. Use
|
||||
* `null` to avoid the sort, but get an undefined draw order.
|
||||
|
||||
@@ -23,6 +23,10 @@ import Layer from './Layer.js';
|
||||
* visible.
|
||||
* @property {number} [maxResolution] The maximum resolution (exclusive) below which this layer will
|
||||
* be visible.
|
||||
* @property {number} [minZoom] The minimum view zoom level (exclusive) above which this layer will be
|
||||
* visible.
|
||||
* @property {number} [maxZoom] The maximum view zoom level (inclusive) at which this layer will
|
||||
* be visible.
|
||||
* @property {import("../source/Vector.js").default} [source] Source.
|
||||
* @property {boolean} [disableHitDetection=false] Setting this to true will provide a slight performance boost, but will
|
||||
* prevent all hit detection on the layer.
|
||||
|
||||
+60
-39
@@ -4,8 +4,8 @@
|
||||
import {getFontParameters} from '../css.js';
|
||||
import {createCanvasContext2D} from '../dom.js';
|
||||
import {clear} from '../obj.js';
|
||||
import {create as createTransform} from '../transform.js';
|
||||
import LabelCache from './canvas/LabelCache.js';
|
||||
import BaseObject from '../Object.js';
|
||||
import EventTarget from '../events/Target.js';
|
||||
|
||||
|
||||
/**
|
||||
@@ -13,6 +13,12 @@ import LabelCache from './canvas/LabelCache.js';
|
||||
* @property {import("../colorlike.js").ColorLike} fillStyle
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef Label
|
||||
* @property {number} width
|
||||
* @property {number} height
|
||||
* @property {Array<string|number>} contextInstructions
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {Object} FillStrokeState
|
||||
@@ -164,21 +170,23 @@ export const defaultPadding = [0, 0, 0, 0];
|
||||
*/
|
||||
export const defaultLineWidth = 1;
|
||||
|
||||
/**
|
||||
* @type {BaseObject}
|
||||
*/
|
||||
export const checkedFonts = new BaseObject();
|
||||
|
||||
/**
|
||||
* The label cache for text rendering. To change the default cache size of 2048
|
||||
* entries, use {@link module:ol/structs/LRUCache#setSize}.
|
||||
* @type {LabelCache}
|
||||
* Deprecated - there is no label cache any more.
|
||||
* @type {?}
|
||||
* @api
|
||||
* @deprecated
|
||||
*/
|
||||
export const labelCache = new LabelCache();
|
||||
|
||||
|
||||
/**
|
||||
* @type {!Object<string, number>}
|
||||
*/
|
||||
export const checkedFonts = {};
|
||||
|
||||
export const labelCache = new EventTarget();
|
||||
labelCache.setSize = function() {
|
||||
console.warn('labelCache is deprecated.'); //eslint-disable-line
|
||||
};
|
||||
|
||||
/**
|
||||
* @type {CanvasRenderingContext2D}
|
||||
@@ -200,9 +208,8 @@ export const textHeights = {};
|
||||
* Clears the label cache when a font becomes available.
|
||||
* @param {string} fontSpec CSS font spec.
|
||||
*/
|
||||
export const checkFont = (function() {
|
||||
export const registerFont = (function() {
|
||||
const retries = 100;
|
||||
const checked = checkedFonts;
|
||||
const size = '32px ';
|
||||
const referenceFonts = ['monospace', 'serif'];
|
||||
const len = referenceFonts.length;
|
||||
@@ -235,19 +242,18 @@ export const checkFont = (function() {
|
||||
|
||||
function check() {
|
||||
let done = true;
|
||||
for (const font in checked) {
|
||||
if (checked[font] < retries) {
|
||||
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'))) {
|
||||
checked[font] = retries;
|
||||
clear(textHeights);
|
||||
// Make sure that loaded fonts are picked up by Safari
|
||||
measureContext = null;
|
||||
measureFont = undefined;
|
||||
if (labelCache.getCount()) {
|
||||
labelCache.clear();
|
||||
}
|
||||
checkedFonts.set(font, retries);
|
||||
} else {
|
||||
++checked[font];
|
||||
checkedFonts.set(font, checkedFonts.get(font) + 1, true);
|
||||
done = false;
|
||||
}
|
||||
}
|
||||
@@ -267,10 +273,10 @@ export const checkFont = (function() {
|
||||
for (let i = 0, ii = families.length; i < ii; ++i) {
|
||||
const family = families[i];
|
||||
const key = font.style + '\n' + font.weight + '\n' + family;
|
||||
if (!(key in checked)) {
|
||||
checked[key] = retries;
|
||||
if (checkedFonts.get(key) === undefined) {
|
||||
checkedFonts.set(key, retries, true);
|
||||
if (!isAvailable(font.style, font.weight, family)) {
|
||||
checked[key] = 0;
|
||||
checkedFonts.set(key, 0, true);
|
||||
if (interval === undefined) {
|
||||
interval = setInterval(check, 32);
|
||||
}
|
||||
@@ -381,14 +387,11 @@ export function rotateAtOffset(context, rotation, offsetX, offsetY) {
|
||||
}
|
||||
|
||||
|
||||
export const resetTransform = createTransform();
|
||||
|
||||
|
||||
/**
|
||||
* @param {CanvasRenderingContext2D} context Context.
|
||||
* @param {import("../transform.js").Transform|null} transform Transform.
|
||||
* @param {number} opacity Opacity.
|
||||
* @param {HTMLImageElement|HTMLCanvasElement|HTMLVideoElement} image Image.
|
||||
* @param {Label|HTMLCanvasElement|HTMLImageElement|HTMLVideoElement} labelOrImage Label.
|
||||
* @param {number} originX Origin X.
|
||||
* @param {number} originY Origin Y.
|
||||
* @param {number} w Width.
|
||||
@@ -397,23 +400,41 @@ export const resetTransform = createTransform();
|
||||
* @param {number} y Y.
|
||||
* @param {number} scale Scale.
|
||||
*/
|
||||
export function drawImage(context,
|
||||
transform, opacity, image, originX, originY, w, h, x, y, scale) {
|
||||
let alpha;
|
||||
if (opacity != 1) {
|
||||
alpha = context.globalAlpha;
|
||||
context.globalAlpha = alpha * opacity;
|
||||
export function drawImageOrLabel(context,
|
||||
transform, opacity, labelOrImage, originX, originY, w, h, x, y, scale) {
|
||||
context.save();
|
||||
|
||||
if (opacity !== 1) {
|
||||
context.globalAlpha *= opacity;
|
||||
}
|
||||
if (transform) {
|
||||
context.setTransform.apply(context, transform);
|
||||
}
|
||||
|
||||
context.drawImage(image, originX, originY, w, h, x, y, w * scale, h * scale);
|
||||
|
||||
if (opacity != 1) {
|
||||
context.globalAlpha = alpha;
|
||||
if ((/** @type {*} */ (labelOrImage).contextInstructions)) {
|
||||
// label
|
||||
context.translate(x, y);
|
||||
context.scale(scale, scale);
|
||||
executeLabelInstructions(/** @type {Label} */ (labelOrImage), context);
|
||||
} else {
|
||||
// image
|
||||
context.drawImage(/** @type {HTMLCanvasElement|HTMLImageElement|HTMLVideoElement} */ (labelOrImage), originX, originY, w, h, x, y, w * scale, h * scale);
|
||||
}
|
||||
if (transform) {
|
||||
context.setTransform.apply(context, resetTransform);
|
||||
|
||||
context.restore();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Label} label Label.
|
||||
* @param {CanvasRenderingContext2D} context Context.
|
||||
*/
|
||||
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])) {
|
||||
CanvasRenderingContext2D.prototype[contextInstructions[i]].apply(context, contextInstructions[i + 1]);
|
||||
} else {
|
||||
context[contextInstructions[i]] = contextInstructions[i + 1];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ import {createEmpty, createOrUpdate,
|
||||
import {lineStringLength} from '../../geom/flat/length.js';
|
||||
import {drawTextOnPath} from '../../geom/flat/textpath.js';
|
||||
import {transform2D} from '../../geom/flat/transform.js';
|
||||
import {drawImage, defaultPadding, defaultTextBaseline} from '../canvas.js';
|
||||
import {drawImageOrLabel, defaultPadding, defaultTextBaseline} from '../canvas.js';
|
||||
import CanvasInstruction from './Instruction.js';
|
||||
import {TEXT_ALIGN} from './TextBuilder.js';
|
||||
import {
|
||||
@@ -16,9 +16,7 @@ import {
|
||||
apply as applyTransform,
|
||||
setFromArray as transformSetFromArray
|
||||
} from '../../transform.js';
|
||||
import {createCanvasContext2D} from '../../dom.js';
|
||||
import {labelCache, defaultTextAlign, measureTextHeight, measureAndCacheTextWidth, measureTextWidths} from '../canvas.js';
|
||||
import Disposable from '../../Disposable.js';
|
||||
import {defaultTextAlign, measureTextHeight, measureAndCacheTextWidth, measureTextWidths} from '../canvas.js';
|
||||
import RBush from 'rbush/rbush.js';
|
||||
|
||||
|
||||
@@ -52,7 +50,7 @@ const p3 = [];
|
||||
const p4 = [];
|
||||
|
||||
|
||||
class Executor extends Disposable {
|
||||
class Executor {
|
||||
/**
|
||||
* @param {number} resolution Resolution.
|
||||
* @param {number} pixelRatio Pixel ratio.
|
||||
@@ -60,7 +58,6 @@ class Executor extends Disposable {
|
||||
* @param {SerializableInstructions} instructions The serializable instructions
|
||||
*/
|
||||
constructor(resolution, pixelRatio, overlaps, instructions) {
|
||||
super();
|
||||
|
||||
/**
|
||||
* @protected
|
||||
@@ -154,85 +151,84 @@ class Executor extends Disposable {
|
||||
* @type {Object<string, Object<string, number>>}
|
||||
*/
|
||||
this.widths_ = {};
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
disposeInternal() {
|
||||
labelCache.release(this);
|
||||
super.disposeInternal();
|
||||
/**
|
||||
* @private
|
||||
* @type {Object<string, import("../canvas.js").Label>}
|
||||
*/
|
||||
this.labels_ = {};
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param {string} text Text.
|
||||
* @param {string} textKey Text style key.
|
||||
* @param {string} fillKey Fill style key.
|
||||
* @param {string} strokeKey Stroke style key.
|
||||
* @return {HTMLCanvasElement} Image.
|
||||
* @return {import("../canvas.js").Label} Label.
|
||||
*/
|
||||
getTextImage(text, textKey, fillKey, strokeKey) {
|
||||
let label;
|
||||
const key = strokeKey + textKey + text + fillKey + this.pixelRatio;
|
||||
createLabel(text, textKey, fillKey, strokeKey) {
|
||||
const key = text + textKey + fillKey + strokeKey;
|
||||
if (this.labels_[key]) {
|
||||
return this.labels_[key];
|
||||
}
|
||||
const strokeState = strokeKey ? this.strokeStates[strokeKey] : null;
|
||||
const fillState = fillKey ? this.fillStates[fillKey] : null;
|
||||
const textState = this.textStates[textKey];
|
||||
const pixelRatio = this.pixelRatio;
|
||||
const scale = textState.scale * pixelRatio;
|
||||
const align = TEXT_ALIGN[textState.textAlign || defaultTextAlign];
|
||||
const strokeWidth = strokeKey && strokeState.lineWidth ? strokeState.lineWidth : 0;
|
||||
|
||||
if (!labelCache.containsKey(key)) {
|
||||
const strokeState = strokeKey ? this.strokeStates[strokeKey] : null;
|
||||
const fillState = fillKey ? this.fillStates[fillKey] : null;
|
||||
const textState = this.textStates[textKey];
|
||||
const pixelRatio = this.pixelRatio;
|
||||
const scale = textState.scale * pixelRatio;
|
||||
const align = TEXT_ALIGN[textState.textAlign || defaultTextAlign];
|
||||
const strokeWidth = strokeKey && strokeState.lineWidth ? strokeState.lineWidth : 0;
|
||||
|
||||
const lines = text.split('\n');
|
||||
const numLines = lines.length;
|
||||
const widths = [];
|
||||
const width = measureTextWidths(textState.font, lines, widths);
|
||||
const lineHeight = measureTextHeight(textState.font);
|
||||
const height = lineHeight * numLines;
|
||||
const renderWidth = width + strokeWidth;
|
||||
const context = createCanvasContext2D(
|
||||
// make canvas 2 pixels wider to account for italic text width measurement errors
|
||||
Math.ceil((renderWidth + 2) * scale),
|
||||
Math.ceil((height + strokeWidth) * scale));
|
||||
label = context.canvas;
|
||||
labelCache.set(key, label);
|
||||
if (scale != 1) {
|
||||
context.scale(scale, scale);
|
||||
}
|
||||
context.font = textState.font;
|
||||
if (strokeKey) {
|
||||
context.strokeStyle = strokeState.strokeStyle;
|
||||
context.lineWidth = strokeWidth;
|
||||
context.lineCap = strokeState.lineCap;
|
||||
context.lineJoin = strokeState.lineJoin;
|
||||
context.miterLimit = strokeState.miterLimit;
|
||||
if (context.setLineDash && strokeState.lineDash.length) {
|
||||
context.setLineDash(strokeState.lineDash);
|
||||
context.lineDashOffset = strokeState.lineDashOffset;
|
||||
}
|
||||
}
|
||||
if (fillKey) {
|
||||
context.fillStyle = fillState.fillStyle;
|
||||
}
|
||||
context.textBaseline = 'middle';
|
||||
context.textAlign = 'center';
|
||||
const leftRight = (0.5 - align);
|
||||
const x = align * renderWidth + leftRight * strokeWidth;
|
||||
let i;
|
||||
if (strokeKey) {
|
||||
for (i = 0; i < numLines; ++i) {
|
||||
context.strokeText(lines[i], x + leftRight * widths[i], 0.5 * (strokeWidth + lineHeight) + i * lineHeight);
|
||||
}
|
||||
}
|
||||
if (fillKey) {
|
||||
for (i = 0; i < numLines; ++i) {
|
||||
context.fillText(lines[i], x + leftRight * widths[i], 0.5 * (strokeWidth + lineHeight) + i * lineHeight);
|
||||
}
|
||||
const lines = text.split('\n');
|
||||
const numLines = lines.length;
|
||||
const widths = [];
|
||||
const width = measureTextWidths(textState.font, lines, widths);
|
||||
const lineHeight = measureTextHeight(textState.font);
|
||||
const height = lineHeight * numLines;
|
||||
const renderWidth = width + strokeWidth;
|
||||
const contextInstructions = [];
|
||||
/** @type {import("../canvas.js").Label} */
|
||||
const label = {
|
||||
// make canvas 2 pixels wider to account for italic text width measurement errors
|
||||
width: Math.ceil((renderWidth + 2) * scale),
|
||||
height: Math.ceil((height + strokeWidth) * scale),
|
||||
contextInstructions: contextInstructions
|
||||
};
|
||||
if (scale != 1) {
|
||||
contextInstructions.push('scale', [scale, scale]);
|
||||
}
|
||||
contextInstructions.push('font', textState.font);
|
||||
if (strokeKey) {
|
||||
contextInstructions.push('strokeStyle', strokeState.strokeStyle);
|
||||
contextInstructions.push('lineWidth', strokeWidth);
|
||||
contextInstructions.push('lineCap', strokeState.lineCap);
|
||||
contextInstructions.push('lineJoin', strokeState.lineJoin);
|
||||
contextInstructions.push('miterLimit', strokeState.miterLimit);
|
||||
if (CanvasRenderingContext2D.prototype.setLineDash) {
|
||||
contextInstructions.push('setLineDash', [strokeState.lineDash]);
|
||||
contextInstructions.push('lineDashOffset', strokeState.lineDashOffset);
|
||||
}
|
||||
}
|
||||
return labelCache.get(key, this);
|
||||
if (fillKey) {
|
||||
contextInstructions.push('fillStyle', fillState.fillStyle);
|
||||
}
|
||||
contextInstructions.push('textBaseline', 'middle');
|
||||
contextInstructions.push('textAlign', 'center');
|
||||
const leftRight = (0.5 - align);
|
||||
const x = align * renderWidth + leftRight * strokeWidth;
|
||||
let i;
|
||||
if (strokeKey) {
|
||||
for (i = 0; i < numLines; ++i) {
|
||||
contextInstructions.push('strokeText', [lines[i], x + leftRight * widths[i], 0.5 * (strokeWidth + lineHeight) + i * lineHeight]);
|
||||
}
|
||||
}
|
||||
if (fillKey) {
|
||||
for (i = 0; i < numLines; ++i) {
|
||||
contextInstructions.push('fillText', [lines[i], x + leftRight * widths[i], 0.5 * (strokeWidth + lineHeight) + i * lineHeight]);
|
||||
}
|
||||
}
|
||||
this.labels_[key] = label;
|
||||
return label;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -265,7 +261,7 @@ class Executor extends Disposable {
|
||||
* @param {CanvasRenderingContext2D} context Context.
|
||||
* @param {number} x X.
|
||||
* @param {number} y Y.
|
||||
* @param {HTMLImageElement|HTMLCanvasElement|HTMLVideoElement} image Image.
|
||||
* @param {import("../canvas.js").Label|HTMLImageElement|HTMLCanvasElement|HTMLVideoElement} imageOrLabel Image.
|
||||
* @param {number} anchorX Anchor X.
|
||||
* @param {number} anchorY Anchor Y.
|
||||
* @param {import("../canvas.js").DeclutterGroup} declutterGroup Declutter group.
|
||||
@@ -281,11 +277,11 @@ class Executor extends Disposable {
|
||||
* @param {Array<*>} fillInstruction Fill instruction.
|
||||
* @param {Array<*>} strokeInstruction Stroke instruction.
|
||||
*/
|
||||
replayImage_(
|
||||
replayImageOrLabel_(
|
||||
context,
|
||||
x,
|
||||
y,
|
||||
image,
|
||||
imageOrLabel,
|
||||
anchorX,
|
||||
anchorY,
|
||||
declutterGroup,
|
||||
@@ -307,8 +303,8 @@ class Executor extends Disposable {
|
||||
x -= anchorX;
|
||||
y -= anchorY;
|
||||
|
||||
const w = (width + originX > image.width) ? image.width - originX : width;
|
||||
const h = (height + originY > image.height) ? image.height - originY : height;
|
||||
const w = (width + originX > imageOrLabel.width) ? imageOrLabel.width - originX : width;
|
||||
const h = (height + originY > imageOrLabel.height) ? imageOrLabel.height - originY : height;
|
||||
const boxW = padding[3] + w * scale + padding[1];
|
||||
const boxH = padding[0] + h * scale + padding[2];
|
||||
const boxX = x - padding[3];
|
||||
@@ -362,11 +358,11 @@ class Executor extends Disposable {
|
||||
}
|
||||
extend(declutterGroup, tmpExtent);
|
||||
const declutterArgs = intersects ?
|
||||
[context, transform ? transform.slice(0) : null, opacity, image, originX, originY, w, h, x, y, scale] :
|
||||
[context, transform ? transform.slice(0) : null, opacity, imageOrLabel, originX, originY, w, h, x, y, scale] :
|
||||
null;
|
||||
if (declutterArgs) {
|
||||
if (fillStroke) {
|
||||
declutterArgs.push(fillInstruction, strokeInstruction, p1, p2, p3, p4);
|
||||
declutterArgs.push(fillInstruction, strokeInstruction, p1.slice(0), p2.slice(0), p3.slice(0), p4.slice(0));
|
||||
}
|
||||
declutterGroup.push(declutterArgs);
|
||||
}
|
||||
@@ -376,7 +372,7 @@ class Executor extends Disposable {
|
||||
/** @type {Array<*>} */ (fillInstruction),
|
||||
/** @type {Array<*>} */ (strokeInstruction));
|
||||
}
|
||||
drawImage(context, transform, opacity, image, originX, originY, w, h, x, y, scale);
|
||||
drawImageOrLabel(context, transform, opacity, imageOrLabel, originX, originY, w, h, x, y, scale);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -451,7 +447,7 @@ class Executor extends Disposable {
|
||||
declutterData[13], declutterData[14], declutterData[15], declutterData[16],
|
||||
declutterData[11], declutterData[12]);
|
||||
}
|
||||
drawImage.apply(undefined, declutterData);
|
||||
drawImageOrLabel.apply(undefined, declutterData);
|
||||
if (currentAlpha !== opacity) {
|
||||
context.globalAlpha = currentAlpha;
|
||||
}
|
||||
@@ -470,12 +466,12 @@ class Executor extends Disposable {
|
||||
* @param {string} textKey The key of the text state.
|
||||
* @param {string} strokeKey The key for the stroke state.
|
||||
* @param {string} fillKey The key for the fill state.
|
||||
* @return {{label: HTMLCanvasElement, anchorX: number, anchorY: number}} The text image and its anchor.
|
||||
* @return {{label: import("../canvas.js").Label, anchorX: number, anchorY: number}} The text image and its anchor.
|
||||
*/
|
||||
drawTextImageWithPointPlacement_(text, textKey, strokeKey, fillKey) {
|
||||
drawLabelWithPointPlacement_(text, textKey, strokeKey, fillKey) {
|
||||
const textState = this.textStates[textKey];
|
||||
|
||||
const label = this.getTextImage(text, textKey, fillKey, strokeKey);
|
||||
const label = this.createLabel(text, textKey, fillKey, strokeKey);
|
||||
|
||||
const strokeState = this.strokeStates[strokeKey];
|
||||
const pixelRatio = this.pixelRatio;
|
||||
@@ -483,7 +479,7 @@ class Executor extends Disposable {
|
||||
const baseline = TEXT_ALIGN[textState.textBaseline || defaultTextBaseline];
|
||||
const strokeWidth = strokeState && strokeState.lineWidth ? strokeState.lineWidth : 0;
|
||||
|
||||
// Remove the 2 pixels we added in getTextImage() for the anchor
|
||||
// Remove the 2 pixels we added in createLabel() for the anchor
|
||||
const width = label.width / pixelRatio - 2 * textState.scale;
|
||||
const anchorX = align * width + 2 * (0.5 - align) * strokeWidth;
|
||||
const anchorY = baseline * label.height / pixelRatio + 2 * (0.5 - baseline) * strokeWidth;
|
||||
@@ -648,7 +644,7 @@ class Executor extends Disposable {
|
||||
textKey = /** @type {string} */ (instruction[19]);
|
||||
strokeKey = /** @type {string} */ (instruction[20]);
|
||||
fillKey = /** @type {string} */ (instruction[21]);
|
||||
const labelWithAnchor = this.drawTextImageWithPointPlacement_(text, textKey, strokeKey, fillKey);
|
||||
const labelWithAnchor = this.drawLabelWithPointPlacement_(text, textKey, strokeKey, fillKey);
|
||||
image = labelWithAnchor.label;
|
||||
instruction[3] = image;
|
||||
const textOffsetX = /** @type {number} */ (instruction[22]);
|
||||
@@ -701,7 +697,7 @@ class Executor extends Disposable {
|
||||
}
|
||||
declutterGroup = declutterGroups[index];
|
||||
}
|
||||
this.replayImage_(context,
|
||||
this.replayImageOrLabel_(context,
|
||||
pixelCoordinates[d], pixelCoordinates[d + 1], image, anchorX, anchorY,
|
||||
declutterGroup, height, opacity, originX, originY, rotation, scale,
|
||||
snapToPixel, width, padding,
|
||||
@@ -758,10 +754,10 @@ class Executor extends Disposable {
|
||||
for (c = 0, cc = parts.length; c < cc; ++c) {
|
||||
part = parts[c]; // x, y, anchorX, rotation, chunk
|
||||
chars = /** @type {string} */ (part[4]);
|
||||
label = this.getTextImage(chars, textKey, '', strokeKey);
|
||||
label = this.createLabel(chars, textKey, '', strokeKey);
|
||||
anchorX = /** @type {number} */ (part[2]) + strokeWidth;
|
||||
anchorY = baseline * label.height + (0.5 - baseline) * 2 * strokeWidth - offsetY;
|
||||
this.replayImage_(context,
|
||||
this.replayImageOrLabel_(context,
|
||||
/** @type {number} */ (part[0]), /** @type {number} */ (part[1]), label,
|
||||
anchorX, anchorY, declutterGroup, label.height, 1, 0, 0,
|
||||
/** @type {number} */ (part[3]), pixelRatioScale, false, label.width,
|
||||
@@ -772,10 +768,10 @@ class Executor extends Disposable {
|
||||
for (c = 0, cc = parts.length; c < cc; ++c) {
|
||||
part = parts[c]; // x, y, anchorX, rotation, chunk
|
||||
chars = /** @type {string} */ (part[4]);
|
||||
label = this.getTextImage(chars, textKey, fillKey, '');
|
||||
label = this.createLabel(chars, textKey, fillKey, '');
|
||||
anchorX = /** @type {number} */ (part[2]);
|
||||
anchorY = baseline * label.height - offsetY;
|
||||
this.replayImage_(context,
|
||||
this.replayImageOrLabel_(context,
|
||||
/** @type {number} */ (part[0]), /** @type {number} */ (part[1]), label,
|
||||
anchorX, anchorY, declutterGroup, label.height, 1, 0, 0,
|
||||
/** @type {number} */ (part[3]), pixelRatioScale, false, label.width,
|
||||
|
||||
@@ -10,7 +10,6 @@ import {isEmpty} from '../../obj.js';
|
||||
import BuilderType from './BuilderType.js';
|
||||
import {create as createTransform, compose as composeTransform} from '../../transform.js';
|
||||
import Executor from './Executor.js';
|
||||
import Disposable from '../../Disposable.js';
|
||||
|
||||
/**
|
||||
* @const
|
||||
@@ -26,7 +25,7 @@ const ORDER = [
|
||||
];
|
||||
|
||||
|
||||
class ExecutorGroup extends Disposable {
|
||||
class ExecutorGroup {
|
||||
/**
|
||||
* @param {import("../../extent.js").Extent} maxExtent Max extent for clipping. When a
|
||||
* `maxExtent` was set on the Buillder for this executor group, the same `maxExtent`
|
||||
@@ -40,7 +39,6 @@ class ExecutorGroup extends Disposable {
|
||||
* @param {number=} opt_renderBuffer Optional rendering buffer.
|
||||
*/
|
||||
constructor(maxExtent, resolution, pixelRatio, overlaps, allInstructions, opt_renderBuffer) {
|
||||
super();
|
||||
|
||||
/**
|
||||
* @private
|
||||
@@ -128,24 +126,6 @@ class ExecutorGroup extends Disposable {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
disposeInternal() {
|
||||
for (const z in this.executorsByZIndex_) {
|
||||
const executors = this.executorsByZIndex_[z];
|
||||
for (const key in executors) {
|
||||
executors[key].disposeInternal();
|
||||
}
|
||||
}
|
||||
if (this.hitDetectionContext_) {
|
||||
const canvas = this.hitDetectionContext_.canvas;
|
||||
canvas.width = 0;
|
||||
canvas.height = 0;
|
||||
}
|
||||
|
||||
super.disposeInternal();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Array<BuilderType>} executors Executors.
|
||||
|
||||
@@ -1,69 +0,0 @@
|
||||
import {getUid} from '../../util.js';
|
||||
import LRUCache from '../../structs/LRUCache.js';
|
||||
|
||||
/**
|
||||
* @module ol/render/canvas/LabelCache
|
||||
*/
|
||||
|
||||
/**
|
||||
* @classdesc
|
||||
* Cache of pre-rendered labels.
|
||||
*/
|
||||
class LabelCache extends LRUCache {
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
constructor(opt_highWaterMark) {
|
||||
super(opt_highWaterMark);
|
||||
this.consumers = {};
|
||||
}
|
||||
|
||||
clear() {
|
||||
this.consumers = {};
|
||||
super.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* @override
|
||||
* @param {string} key Label key.
|
||||
* @param {import("./Executor.js").default} consumer Label consumer.
|
||||
* @return {HTMLCanvasElement} Label.
|
||||
*/
|
||||
get(key, consumer) {
|
||||
const canvas = super.get(key);
|
||||
const consumerId = getUid(consumer);
|
||||
if (!(consumerId in this.consumers)) {
|
||||
this.consumers[consumerId] = {};
|
||||
}
|
||||
this.consumers[consumerId][key] = true;
|
||||
return canvas;
|
||||
}
|
||||
|
||||
prune() {
|
||||
outer:
|
||||
while (this.canExpireCache()) {
|
||||
const key = this.peekLastKey();
|
||||
for (const consumerId in this.consumers) {
|
||||
if (key in this.consumers[consumerId]) {
|
||||
break outer;
|
||||
}
|
||||
}
|
||||
const canvas = this.pop();
|
||||
canvas.width = 0;
|
||||
canvas.height = 0;
|
||||
for (const consumerId in this.consumers) {
|
||||
delete this.consumers[consumerId][key];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {import("./Executor.js").default} consumer Label consumer.
|
||||
*/
|
||||
release(consumer) {
|
||||
delete this.consumers[getUid(consumer)];
|
||||
}
|
||||
}
|
||||
|
||||
export default LabelCache;
|
||||
@@ -6,7 +6,7 @@ import {asColorLike} from '../../colorlike.js';
|
||||
import {intersects} from '../../extent.js';
|
||||
import {matchingChunk} from '../../geom/flat/straightchunk.js';
|
||||
import GeometryType from '../../geom/GeometryType.js';
|
||||
import {labelCache, defaultTextAlign, defaultPadding, defaultLineCap, defaultLineDashOffset, defaultLineDash, defaultLineJoin, defaultFillStyle, checkFont, defaultFont, defaultLineWidth, defaultMiterLimit, defaultStrokeStyle, defaultTextBaseline} from '../canvas.js';
|
||||
import {defaultTextAlign, defaultPadding, defaultLineCap, defaultLineDashOffset, defaultLineDash, defaultLineJoin, defaultFillStyle, registerFont, defaultFont, defaultLineWidth, defaultMiterLimit, defaultStrokeStyle, defaultTextBaseline} from '../canvas.js';
|
||||
import CanvasInstruction from './Instruction.js';
|
||||
import CanvasBuilder from './Builder.js';
|
||||
import TextPlacement from '../../style/TextPlacement.js';
|
||||
@@ -131,8 +131,6 @@ class CanvasTextBuilder extends CanvasBuilder {
|
||||
* @type {string}
|
||||
*/
|
||||
this.strokeKey_ = '';
|
||||
|
||||
labelCache.prune();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -433,7 +431,7 @@ class CanvasTextBuilder extends CanvasBuilder {
|
||||
|
||||
textState = this.textState_;
|
||||
const font = textStyle.getFont() || defaultFont;
|
||||
checkFont(font);
|
||||
registerFont(font);
|
||||
const textScale = textStyle.getScale();
|
||||
textState.overflow = textStyle.getOverflow();
|
||||
textState.font = font;
|
||||
@@ -461,7 +459,7 @@ class CanvasTextBuilder extends CanvasBuilder {
|
||||
strokeState.lineCap + strokeState.lineDashOffset + '|' + strokeState.lineWidth +
|
||||
strokeState.lineJoin + strokeState.miterLimit + '[' + strokeState.lineDash.join() + ']' :
|
||||
'';
|
||||
this.textKey_ = textState.font + textState.scale + (textState.textAlign || '?');
|
||||
this.textKey_ = textState.font + textState.scale + (textState.textAlign || '?') + (textState.textBaseline || '?');
|
||||
this.fillKey_ = fillState ?
|
||||
(typeof fillState.fillStyle == 'string' ? fillState.fillStyle : ('|' + getUid(fillState.fillStyle))) :
|
||||
'';
|
||||
|
||||
@@ -33,7 +33,7 @@ export function createHitDetectionImageData(size, transforms, features, styleFun
|
||||
const renderer = new CanvasImmediateRenderer(context, 0.5, extent, null, rotation);
|
||||
const featureCount = features.length;
|
||||
// Stretch hit detection index to use the whole available color range
|
||||
const indexFactor = Math.ceil((256 * 256 * 256 - 1) / featureCount);
|
||||
const indexFactor = Math.floor((256 * 256 * 256 - 1) / featureCount);
|
||||
const featuresByZIndex = {};
|
||||
for (let i = 1; i <= featureCount; ++i) {
|
||||
const feature = features[i - 1];
|
||||
@@ -121,6 +121,7 @@ export function createHitDetectionImageData(size, transforms, features, styleFun
|
||||
}
|
||||
}
|
||||
}
|
||||
document.body.appendChild(context.canvas);
|
||||
return context.getImageData(0, 0, canvas.width, canvas.height);
|
||||
}
|
||||
|
||||
@@ -141,7 +142,7 @@ export function hitDetect(pixel, features, imageData) {
|
||||
const g = imageData.data[index + 1];
|
||||
const b = imageData.data[index + 2];
|
||||
const i = b + (256 * (g + (256 * r)));
|
||||
const indexFactor = Math.ceil((256 * 256 * 256 - 1) / features.length);
|
||||
const indexFactor = Math.floor((256 * 256 * 256 - 1) / features.length);
|
||||
if (i && i % indexFactor === 0) {
|
||||
resultFeatures.push(features[i / indexFactor - 1]);
|
||||
}
|
||||
|
||||
@@ -8,9 +8,9 @@ import RenderEventType from '../render/EventType.js';
|
||||
import MapRenderer from './Map.js';
|
||||
import SourceState from '../source/State.js';
|
||||
import {replaceChildren} from '../dom.js';
|
||||
import {labelCache} from '../render/canvas.js';
|
||||
import EventType from '../events/EventType.js';
|
||||
import {listen, unlistenByKey} from '../events.js';
|
||||
import {checkedFonts} from '../render/canvas.js';
|
||||
import ObjectEventType from '../ObjectEventType.js';
|
||||
|
||||
|
||||
/**
|
||||
@@ -29,7 +29,7 @@ class CompositeMapRenderer extends MapRenderer {
|
||||
/**
|
||||
* @type {import("../events.js").EventsKey}
|
||||
*/
|
||||
this.labelCacheKey_ = listen(labelCache, EventType.CLEAR, map.redrawText.bind(map));
|
||||
this.fontChangeListenerKey_ = listen(checkedFonts, ObjectEventType.PROPERTYCHANGE, map.redrawText.bind(map));
|
||||
|
||||
/**
|
||||
* @private
|
||||
@@ -73,7 +73,7 @@ class CompositeMapRenderer extends MapRenderer {
|
||||
}
|
||||
|
||||
disposeInternal() {
|
||||
unlistenByKey(this.labelCacheKey_);
|
||||
unlistenByKey(this.fontChangeListenerKey_);
|
||||
this.element_.parentNode.removeChild(this.element_);
|
||||
super.disposeInternal();
|
||||
}
|
||||
|
||||
@@ -362,7 +362,6 @@ class CanvasTileLayerRenderer extends CanvasLayerRenderer {
|
||||
|
||||
this.manageTilePyramid(frameState, tileSource, tileGrid, pixelRatio,
|
||||
projection, extent, z, tileLayer.getPreload());
|
||||
this.updateCacheSize_(frameState, tileSource);
|
||||
this.scheduleExpireCache(frameState, tileSource);
|
||||
|
||||
this.postRender(context, frameState);
|
||||
@@ -474,27 +473,6 @@ class CanvasTileLayerRenderer extends CanvasLayerRenderer {
|
||||
usedTiles[tileSourceKey][tile.getKey()] = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the cache is big enough, and increase its size if necessary.
|
||||
* @param {import("../../PluggableMap.js").FrameState} frameState Frame state.
|
||||
* @param {import("../../source/Tile.js").default} tileSource Tile source.
|
||||
* @private
|
||||
*/
|
||||
updateCacheSize_(frameState, tileSource) {
|
||||
const tileSourceKey = getUid(tileSource);
|
||||
let size = 0;
|
||||
if (tileSourceKey in frameState.usedTiles) {
|
||||
size += Object.keys(frameState.usedTiles[tileSourceKey]).length;
|
||||
}
|
||||
if (tileSourceKey in frameState.wantedTiles) {
|
||||
size += Object.keys(frameState.wantedTiles[tileSourceKey]).length;
|
||||
}
|
||||
const tileCache = tileSource.tileCache;
|
||||
if (tileCache.highWaterMark < size) {
|
||||
tileCache.highWaterMark = size;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Manage tile pyramid.
|
||||
* This function performs a number of functions related to the tiles at the
|
||||
|
||||
@@ -105,6 +105,7 @@ class CanvasVectorImageLayerRenderer extends CanvasImageLayerRenderer {
|
||||
const context = vectorRenderer.context;
|
||||
const imageFrameState = /** @type {import("../../PluggableMap.js").FrameState} */ (assign({}, frameState, {
|
||||
declutterItems: [],
|
||||
extent: renderedExtent,
|
||||
size: [width, height],
|
||||
viewState: /** @type {import("../../View.js").State} */ (assign({}, frameState.viewState, {
|
||||
rotation: 0
|
||||
|
||||
@@ -329,6 +329,9 @@ class CanvasVectorLayerRenderer extends CanvasLayerRenderer {
|
||||
prepareFrame(frameState) {
|
||||
const vectorLayer = this.getLayer();
|
||||
const vectorSource = vectorLayer.getSource();
|
||||
if (!vectorSource) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const animating = frameState.viewHints[ViewHint.ANIMATING];
|
||||
const interacting = frameState.viewHints[ViewHint.INTERACTING];
|
||||
@@ -384,9 +387,6 @@ class CanvasVectorLayerRenderer extends CanvasLayerRenderer {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (this.replayGroup_) {
|
||||
this.replayGroup_.dispose();
|
||||
}
|
||||
this.replayGroup_ = null;
|
||||
|
||||
this.dirty_ = false;
|
||||
@@ -457,7 +457,7 @@ class CanvasVectorLayerRenderer extends CanvasLayerRenderer {
|
||||
* @param {number} squaredTolerance Squared render tolerance.
|
||||
* @param {import("../../style/Style.js").default|Array<import("../../style/Style.js").default>} styles The style or array of styles.
|
||||
* @param {import("../../render/canvas/BuilderGroup.js").default} builderGroup Builder group.
|
||||
* @param {import("../../proj.js").TransformFunction} opt_transform Transform from user to view projection.
|
||||
* @param {import("../../proj.js").TransformFunction=} opt_transform Transform from user to view projection.
|
||||
* @return {boolean} `true` if an image is loading.
|
||||
*/
|
||||
renderFeature(feature, squaredTolerance, styles, builderGroup, opt_transform) {
|
||||
|
||||
@@ -117,8 +117,7 @@ class CanvasVectorTileLayerRenderer extends CanvasTileLayerRenderer {
|
||||
let render;
|
||||
const tileUid = getUid(tile);
|
||||
const state = tile.getState();
|
||||
if (((state === TileState.LOADED && tile.hifi) ||
|
||||
state === TileState.ERROR || state === TileState.ABORT) &&
|
||||
if (((state === TileState.LOADED && tile.hifi) || state === TileState.ERROR) &&
|
||||
tileUid in this.tileListenerKeys_) {
|
||||
unlistenByKey(this.tileListenerKeys_[tileUid]);
|
||||
delete this.tileListenerKeys_[tileUid];
|
||||
@@ -219,12 +218,6 @@ class CanvasVectorTileLayerRenderer extends CanvasTileLayerRenderer {
|
||||
|
||||
const sourceTiles = source.getSourceTiles(pixelRatio, projection, tile);
|
||||
const layerUid = getUid(layer);
|
||||
const executorGroups = tile.executorGroups[layerUid];
|
||||
if (executorGroups) {
|
||||
for (let i = 0, ii = executorGroups.length; i < ii; ++i) {
|
||||
executorGroups[i].dispose();
|
||||
}
|
||||
}
|
||||
delete tile.hitDetectionImageData[layerUid];
|
||||
tile.executorGroups[layerUid] = [];
|
||||
for (let t = 0, tt = sourceTiles.length; t < tt; ++t) {
|
||||
@@ -468,9 +461,6 @@ class CanvasVectorTileLayerRenderer extends CanvasTileLayerRenderer {
|
||||
const clipZs = [];
|
||||
for (let i = tiles.length - 1; i >= 0; --i) {
|
||||
const tile = /** @type {import("../../VectorRenderTile.js").default} */ (tiles[i]);
|
||||
if (tile.getState() == TileState.ABORT) {
|
||||
continue;
|
||||
}
|
||||
const tileCoord = tile.tileCoord;
|
||||
const tileExtent = tileGrid.getTileCoordExtent(tile.wrappedTileCoord);
|
||||
const worldOffset = tileGrid.getTileCoordExtent(tileCoord, this.tmpExtent)[0] - tileExtent[0];
|
||||
|
||||
@@ -211,16 +211,6 @@ class ReprojTile extends Tile {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
disposeInternal() {
|
||||
if (this.state == TileState.LOADING) {
|
||||
this.unlistenSources_();
|
||||
}
|
||||
super.disposeInternal();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the HTML Canvas element for this tile.
|
||||
* @return {HTMLCanvasElement} Canvas.
|
||||
|
||||
@@ -11,16 +11,21 @@ import {clamp} from './math.js';
|
||||
*/
|
||||
|
||||
/**
|
||||
* Returns a modified resolution taking into acocunt the viewport size and maximum
|
||||
* Returns a modified resolution taking into account the viewport size and maximum
|
||||
* allowed extent.
|
||||
* @param {number} resolution Resolution
|
||||
* @param {import("./extent.js").Extent=} maxExtent Maximum allowed extent.
|
||||
* @param {import("./size.js").Size} viewportSize Viewport size.
|
||||
* @param {boolean} showFullExtent Whether to show the full extent.
|
||||
* @return {number} Capped resolution.
|
||||
*/
|
||||
function getViewportClampedResolution(resolution, maxExtent, viewportSize) {
|
||||
function getViewportClampedResolution(resolution, maxExtent, viewportSize, showFullExtent) {
|
||||
const xResolution = getWidth(maxExtent) / viewportSize[0];
|
||||
const yResolution = getHeight(maxExtent) / viewportSize[1];
|
||||
|
||||
if (showFullExtent) {
|
||||
return Math.min(resolution, Math.max(xResolution, yResolution));
|
||||
}
|
||||
return Math.min(resolution, Math.min(xResolution, yResolution));
|
||||
}
|
||||
|
||||
@@ -52,9 +57,10 @@ function getSmoothClampedResolution(resolution, maxResolution, minResolution) {
|
||||
* @param {Array<number>} resolutions Resolutions.
|
||||
* @param {boolean=} opt_smooth If true, the view will be able to slightly exceed resolution limits. Default: true.
|
||||
* @param {import("./extent.js").Extent=} opt_maxExtent Maximum allowed extent.
|
||||
* @param {boolean=} opt_showFullExtent If true, allows us to show the full extent. Default: false.
|
||||
* @return {Type} Zoom function.
|
||||
*/
|
||||
export function createSnapToResolutions(resolutions, opt_smooth, opt_maxExtent) {
|
||||
export function createSnapToResolutions(resolutions, opt_smooth, opt_maxExtent, opt_showFullExtent) {
|
||||
return (
|
||||
/**
|
||||
* @param {number|undefined} resolution Resolution.
|
||||
@@ -68,7 +74,7 @@ export function createSnapToResolutions(resolutions, opt_smooth, opt_maxExtent)
|
||||
const maxResolution = resolutions[0];
|
||||
const minResolution = resolutions[resolutions.length - 1];
|
||||
const cappedMaxRes = opt_maxExtent ?
|
||||
getViewportClampedResolution(maxResolution, opt_maxExtent, size) :
|
||||
getViewportClampedResolution(maxResolution, opt_maxExtent, size, opt_showFullExtent) :
|
||||
maxResolution;
|
||||
|
||||
// during interacting or animating, allow intermediary values
|
||||
@@ -100,9 +106,10 @@ export function createSnapToResolutions(resolutions, opt_smooth, opt_maxExtent)
|
||||
* @param {number=} opt_minResolution Minimum resolution.
|
||||
* @param {boolean=} opt_smooth If true, the view will be able to slightly exceed resolution limits. Default: true.
|
||||
* @param {import("./extent.js").Extent=} opt_maxExtent Maximum allowed extent.
|
||||
* @param {boolean=} opt_showFullExtent If true, allows us to show the full extent. Default: false.
|
||||
* @return {Type} Zoom function.
|
||||
*/
|
||||
export function createSnapToPower(power, maxResolution, opt_minResolution, opt_smooth, opt_maxExtent) {
|
||||
export function createSnapToPower(power, maxResolution, opt_minResolution, opt_smooth, opt_maxExtent, opt_showFullExtent) {
|
||||
return (
|
||||
/**
|
||||
* @param {number|undefined} resolution Resolution.
|
||||
@@ -114,7 +121,7 @@ export function createSnapToPower(power, maxResolution, opt_minResolution, opt_s
|
||||
function(resolution, direction, size, opt_isMoving) {
|
||||
if (resolution !== undefined) {
|
||||
const cappedMaxRes = opt_maxExtent ?
|
||||
getViewportClampedResolution(maxResolution, opt_maxExtent, size) :
|
||||
getViewportClampedResolution(maxResolution, opt_maxExtent, size, opt_showFullExtent) :
|
||||
maxResolution;
|
||||
const minResolution = opt_minResolution !== undefined ? opt_minResolution : 0;
|
||||
|
||||
@@ -148,9 +155,10 @@ export function createSnapToPower(power, maxResolution, opt_minResolution, opt_s
|
||||
* @param {number} minResolution Min resolution.
|
||||
* @param {boolean=} opt_smooth If true, the view will be able to slightly exceed resolution limits. Default: true.
|
||||
* @param {import("./extent.js").Extent=} opt_maxExtent Maximum allowed extent.
|
||||
* @param {boolean=} opt_showFullExtent If true, allows us to show the full extent. Default: false.
|
||||
* @return {Type} Zoom function.
|
||||
*/
|
||||
export function createMinMaxResolution(maxResolution, minResolution, opt_smooth, opt_maxExtent) {
|
||||
export function createMinMaxResolution(maxResolution, minResolution, opt_smooth, opt_maxExtent, opt_showFullExtent) {
|
||||
return (
|
||||
/**
|
||||
* @param {number|undefined} resolution Resolution.
|
||||
@@ -162,7 +170,7 @@ export function createMinMaxResolution(maxResolution, minResolution, opt_smooth,
|
||||
function(resolution, direction, size, opt_isMoving) {
|
||||
if (resolution !== undefined) {
|
||||
const cappedMaxRes = opt_maxExtent ?
|
||||
getViewportClampedResolution(maxResolution, opt_maxExtent, size) :
|
||||
getViewportClampedResolution(maxResolution, opt_maxExtent, size, opt_showFullExtent) :
|
||||
maxResolution;
|
||||
const smooth = opt_smooth !== undefined ? opt_smooth : true;
|
||||
|
||||
|
||||
@@ -71,7 +71,7 @@ export function createSnapToZero(opt_tolerance) {
|
||||
return (
|
||||
/**
|
||||
* @param {number|undefined} rotation Rotation.
|
||||
* @param {boolean} opt_isMoving True if an interaction or animation is in progress.
|
||||
* @param {boolean=} opt_isMoving True if an interaction or animation is in progress.
|
||||
* @return {number|undefined} Rotation.
|
||||
*/
|
||||
function(rotation, opt_isMoving) {
|
||||
|
||||
@@ -50,7 +50,7 @@ const TOS_ATTRIBUTION = '<a class="ol-attribution-bing-tos" ' +
|
||||
|
||||
/**
|
||||
* @typedef {Object} Options
|
||||
* @property {number} [cacheSize] Tile cache size. The default depends on the screen size. Will increase if too small.
|
||||
* @property {number} [cacheSize] Tile cache size. The default depends on the screen size. Will be ignored if too small.
|
||||
* @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/.
|
||||
|
||||
@@ -9,7 +9,7 @@ import XYZ from './XYZ.js';
|
||||
/**
|
||||
* @typedef {Object} Options
|
||||
* @property {import("./Source.js").AttributionLike} [attributions] Attributions.
|
||||
* @property {number} [cacheSize] Tile cache size. The default depends on the screen size. Will increase if too small.
|
||||
* @property {number} [cacheSize] Tile cache size. The default depends on the screen size. Will be ignored if too small.
|
||||
* @property {null|string} [crossOrigin] The `crossOrigin` attribute for loaded images. Note that
|
||||
* you must provide a `crossOrigin` value if you want to access pixel data with the Canvas renderer.
|
||||
* See https://developer.mozilla.org/en-US/docs/Web/HTML/CORS_enabled_image for more detail.
|
||||
|
||||
+33
-10
@@ -29,7 +29,7 @@ import VectorSource from './Vector.js';
|
||||
* ```
|
||||
* See {@link module:ol/geom/Polygon~Polygon#getInteriorPoint} for a way to get a cluster
|
||||
* calculation point for polygons.
|
||||
* @property {VectorSource} source Source.
|
||||
* @property {VectorSource} [source] Source.
|
||||
* @property {boolean} [wrapX=true] Whether to wrap the world horizontally.
|
||||
*/
|
||||
|
||||
@@ -39,6 +39,10 @@ import VectorSource from './Vector.js';
|
||||
* Layer source to cluster vector data. Works out of the box with point
|
||||
* geometries. For other geometry types, or if not all geometries should be
|
||||
* considered for clustering, a custom `geometryFunction` can be defined.
|
||||
*
|
||||
* If the instance is disposed without also disposing the underlying
|
||||
* source `setSource(null)` has to be called to remove the listener reference
|
||||
* from the wrapped source.
|
||||
* @api
|
||||
*/
|
||||
class Cluster extends VectorSource {
|
||||
@@ -81,13 +85,17 @@ class Cluster extends VectorSource {
|
||||
return geometry;
|
||||
};
|
||||
|
||||
/**
|
||||
* @type {VectorSource}
|
||||
* @protected
|
||||
*/
|
||||
this.source = options.source;
|
||||
this.boundRefresh_ = this.refresh.bind(this);
|
||||
|
||||
this.source.addEventListener(EventType.CHANGE, this.refresh.bind(this));
|
||||
this.setSource(options.source || null);
|
||||
}
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
clear(opt_fast) {
|
||||
this.features.length = 0;
|
||||
super.clear(opt_fast);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -132,7 +140,23 @@ class Cluster extends VectorSource {
|
||||
}
|
||||
|
||||
/**
|
||||
* handle the source changing
|
||||
* Replace the wrapped source.
|
||||
* @param {VectorSource} source The new source for this instance.
|
||||
* @api
|
||||
*/
|
||||
setSource(source) {
|
||||
if (this.source) {
|
||||
this.source.removeEventListener(EventType.CHANGE, this.boundRefresh_);
|
||||
}
|
||||
this.source = source;
|
||||
if (source) {
|
||||
source.addEventListener(EventType.CHANGE, this.boundRefresh_);
|
||||
}
|
||||
this.refresh();
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the source changing.
|
||||
* @override
|
||||
*/
|
||||
refresh() {
|
||||
@@ -145,10 +169,9 @@ class Cluster extends VectorSource {
|
||||
* @protected
|
||||
*/
|
||||
cluster() {
|
||||
if (this.resolution === undefined) {
|
||||
if (this.resolution === undefined || !this.source) {
|
||||
return;
|
||||
}
|
||||
this.features.length = 0;
|
||||
const extent = createEmpty();
|
||||
const mapDistance = this.distance * this.resolution;
|
||||
const features = this.source.getFeatures();
|
||||
|
||||
@@ -9,6 +9,7 @@ import {Versions} from '../format/IIIFInfo.js';
|
||||
import {assert} from '../asserts.js';
|
||||
import TileGrid from '../tilegrid/TileGrid.js';
|
||||
import TileImage from './TileImage.js';
|
||||
import {toSize} from '../size.js';
|
||||
|
||||
/**
|
||||
* @typedef {Object} Options
|
||||
@@ -60,7 +61,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
|
||||
*/
|
||||
@@ -89,7 +90,7 @@ class IIIF extends TileImage {
|
||||
const extent = options.extent || [0, -height, width, 0];
|
||||
|
||||
const supportsListedSizes = sizes != undefined && Array.isArray(sizes) && sizes.length > 0;
|
||||
const supportsListedTiles = tileSize != undefined && (typeof tileSize === 'number' && Number.isInteger(tileSize) && tileSize > 0 || Array.isArray(tileSize) && tileSize.length > 0);
|
||||
const supportsListedTiles = tileSize !== undefined && (typeof tileSize === 'number' && Number.isInteger(tileSize) && tileSize > 0 || Array.isArray(tileSize) && tileSize.length > 0);
|
||||
const supportsArbitraryTiling = supports != undefined && Array.isArray(supports) &&
|
||||
(supports.includes('regionByPx') || supports.includes('regionByPct')) &&
|
||||
(supports.includes('sizeByWh') || supports.includes('sizeByH') ||
|
||||
@@ -267,7 +268,9 @@ class IIIF extends TileImage {
|
||||
return baseUrl + regionParam + '/' + sizeParam + '/0/' + quality + '.' + format;
|
||||
};
|
||||
|
||||
const IiifTileClass = CustomTile.bind(null, tilePixelRatio, tileGrid);
|
||||
const IiifTileClass = CustomTile.bind(null, toSize(tileSize || 256).map(function(size) {
|
||||
return size * tilePixelRatio;
|
||||
}));
|
||||
|
||||
super({
|
||||
attributions: options.attributions,
|
||||
|
||||
@@ -20,7 +20,7 @@ export const ATTRIBUTION = '© ' +
|
||||
/**
|
||||
* @typedef {Object} Options
|
||||
* @property {import("./Source.js").AttributionLike} [attributions] Attributions.
|
||||
* @property {number} [cacheSize] Tile cache size. The default depends on the screen size. Will increase if too small.
|
||||
* @property {number} [cacheSize] Tile cache size. The default depends on the screen size. Will be ignored if too small.
|
||||
* @property {null|string} [crossOrigin='anonymous'] The `crossOrigin` attribute for loaded images. Note that
|
||||
* you must provide a `crossOrigin` value if you want to access pixel data with the Canvas renderer.
|
||||
* See https://developer.mozilla.org/en-US/docs/Web/HTML/CORS_enabled_image for more detail.
|
||||
|
||||
@@ -90,7 +90,7 @@ const ProviderConfig = {
|
||||
|
||||
/**
|
||||
* @typedef {Object} Options
|
||||
* @property {number} [cacheSize] Tile cache size. The default depends on the screen size. Will increase if too small.
|
||||
* @property {number} [cacheSize] Tile cache size. The default depends on the screen size. Will be ignored if too small.
|
||||
* @property {string} layer Layer name.
|
||||
* @property {number} [minZoom] Minimum zoom.
|
||||
* @property {number} [maxZoom] Maximum zoom.
|
||||
|
||||
+10
-16
@@ -69,24 +69,21 @@ class TileSource extends Source {
|
||||
*/
|
||||
this.tileGrid = options.tileGrid !== undefined ? options.tileGrid : null;
|
||||
|
||||
let cacheSize = options.cacheSize;
|
||||
if (cacheSize === undefined) {
|
||||
const tileSize = [256, 256];
|
||||
const tileGrid = options.tileGrid;
|
||||
if (tileGrid) {
|
||||
toSize(tileGrid.getTileSize(tileGrid.getMinZoom()), tileSize);
|
||||
}
|
||||
const canUseScreen = typeof screen !== 'undefined';
|
||||
const width = canUseScreen ? (screen.availWidth || screen.width) : 1920;
|
||||
const height = canUseScreen ? (screen.availHeight || screen.height) : 1080;
|
||||
cacheSize = 4 * Math.ceil(width / tileSize[0]) * Math.ceil(height / tileSize[1]);
|
||||
const tileSize = [256, 256];
|
||||
const tileGrid = options.tileGrid;
|
||||
if (tileGrid) {
|
||||
toSize(tileGrid.getTileSize(tileGrid.getMinZoom()), tileSize);
|
||||
}
|
||||
const canUseScreen = typeof screen !== 'undefined';
|
||||
const width = canUseScreen ? (screen.availWidth || screen.width) : 1920;
|
||||
const height = canUseScreen ? (screen.availHeight || screen.height) : 1080;
|
||||
const minCacheSize = 4 * Math.ceil(width / tileSize[0]) * Math.ceil(height / tileSize[1]);
|
||||
|
||||
/**
|
||||
* @protected
|
||||
* @type {import("../TileCache.js").default}
|
||||
*/
|
||||
this.tileCache = new TileCache(cacheSize);
|
||||
this.tileCache = new TileCache(Math.max(minCacheSize, options.cacheSize || 0));
|
||||
|
||||
/**
|
||||
* @protected
|
||||
@@ -125,7 +122,7 @@ class TileSource extends Source {
|
||||
|
||||
/**
|
||||
* @param {import("../proj/Projection.js").default} projection Projection.
|
||||
* @param {!Object<string, import("../TileRange.js").default>} usedTiles Used tiles.
|
||||
* @param {!Object<string, boolean>} usedTiles Used tiles.
|
||||
*/
|
||||
expireCache(projection, usedTiles) {
|
||||
const tileCache = this.getTileCacheForProjection(projection);
|
||||
@@ -317,9 +314,6 @@ class TileSource extends Source {
|
||||
this.tileCache.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
refresh() {
|
||||
this.clear();
|
||||
super.refresh();
|
||||
|
||||
@@ -13,7 +13,7 @@ import {appendParams} from '../uri.js';
|
||||
/**
|
||||
* @typedef {Object} Options
|
||||
* @property {import("./Source.js").AttributionLike} [attributions] Attributions.
|
||||
* @property {number} [cacheSize] Tile cache size. The default depends on the screen size. Will increase if too small.
|
||||
* @property {number} [cacheSize] Tile cache size. The default depends on the screen size. Will be ignored if too small.
|
||||
* @property {null|string} [crossOrigin] The `crossOrigin` attribute for loaded images. Note that
|
||||
* you must provide a `crossOrigin` value if you want to access pixel data with the Canvas renderer.
|
||||
* See https://developer.mozilla.org/en-US/docs/Web/HTML/CORS_enabled_image for more detail.
|
||||
|
||||
@@ -17,7 +17,7 @@ import {getForProjection as getTileGridForProjection} from '../tilegrid.js';
|
||||
* @typedef {Object} Options
|
||||
* @property {import("./Source.js").AttributionLike} [attributions] Attributions.
|
||||
* @property {boolean} [attributionsCollapsible=true] Attributions are collapsible.
|
||||
* @property {number} [cacheSize] Tile cache size. The default depends on the screen size. Will increase if too small.
|
||||
* @property {number} [cacheSize] Tile cache size. The default depends on the screen size. Will be ignored if too small.
|
||||
* @property {null|string} [crossOrigin] The `crossOrigin` attribute for loaded images. Note that
|
||||
* you must provide a `crossOrigin` value if you want to access pixel data with the Canvas renderer.
|
||||
* See https://developer.mozilla.org/en-US/docs/Web/HTML/CORS_enabled_image for more detail.
|
||||
|
||||
@@ -39,7 +39,7 @@ import {createXYZ, extentFromProjection} from '../tilegrid.js';
|
||||
/**
|
||||
* @typedef {Object} Options
|
||||
* @property {import("./Source.js").AttributionLike} [attributions] Attributions.
|
||||
* @property {number} [cacheSize] Tile cache size. The default depends on the screen size. Will increase if too small.
|
||||
* @property {number} [cacheSize] Tile cache size. The default depends on the screen size. Will be ignored if too small.
|
||||
* @property {null|string} [crossOrigin] The `crossOrigin` attribute for loaded images. Note that
|
||||
* you must provide a `crossOrigin` value if you want to access pixel data with the Canvas renderer.
|
||||
* See https://developer.mozilla.org/en-US/docs/Web/HTML/CORS_enabled_image for more detail.
|
||||
|
||||
@@ -20,7 +20,7 @@ import {appendParams} from '../uri.js';
|
||||
/**
|
||||
* @typedef {Object} Options
|
||||
* @property {import("./Source.js").AttributionLike} [attributions] Attributions.
|
||||
* @property {number} [cacheSize] Tile cache size. The default depends on the screen size. Will increase if too small.
|
||||
* @property {number} [cacheSize] Tile cache size. The default depends on the screen size. Will be ignored if too small.
|
||||
* @property {null|string} [crossOrigin] The `crossOrigin` attribute for loaded images. Note that
|
||||
* you must provide a `crossOrigin` value if you want to access pixel data with the Canvas renderer.
|
||||
* See https://developer.mozilla.org/en-US/docs/Web/HTML/CORS_enabled_image for more detail.
|
||||
|
||||
@@ -139,7 +139,7 @@ class UrlTile extends TileSource {
|
||||
} else if (uid in this.tileLoadingKeys_) {
|
||||
delete this.tileLoadingKeys_[uid];
|
||||
type = tileState == TileState.ERROR ? TileEventType.TILELOADERROR :
|
||||
(tileState == TileState.LOADED || tileState == TileState.ABORT) ?
|
||||
tileState == TileState.LOADED ?
|
||||
TileEventType.TILELOADEND : undefined;
|
||||
}
|
||||
if (type != undefined) {
|
||||
|
||||
@@ -914,9 +914,6 @@ class VectorSource extends Source {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
refresh() {
|
||||
this.clear(true);
|
||||
this.loadedExtentsRtree_.clear();
|
||||
|
||||
+27
-58
@@ -12,8 +12,8 @@ import {createXYZ, extentFromProjection, createForProjection} from '../tilegrid.
|
||||
import {buffer as bufferExtent, getIntersection, intersects} from '../extent.js';
|
||||
import EventType from '../events/EventType.js';
|
||||
import {loadFeaturesXhr} from '../featureloader.js';
|
||||
import {equals, remove} from '../array.js';
|
||||
import {listen, unlistenByKey} from '../events.js';
|
||||
import {equals} from '../array.js';
|
||||
import TileCache from '../TileCache.js';
|
||||
|
||||
/**
|
||||
* @typedef {Object} Options
|
||||
@@ -30,9 +30,10 @@ import {listen, unlistenByKey} from '../events.js';
|
||||
* @property {import("./State.js").default} [state] Source state.
|
||||
* @property {typeof import("../VectorTile.js").default} [tileClass] Class used to instantiate image tiles.
|
||||
* Default is {@link module:ol/VectorTile}.
|
||||
* @property {number} [maxZoom=22] Optional max zoom level.
|
||||
* @property {number} [minZoom] Optional min zoom level.
|
||||
* @property {number|import("../size.js").Size} [tileSize=512] Optional tile size.
|
||||
* @property {number} [maxZoom=22] Optional max zoom level. Not used if `tileGrid` is provided.
|
||||
* @property {number} [minZoom] Optional min zoom level. Not used if `tileGrid` is provided.
|
||||
* @property {number|import("../size.js").Size} [tileSize=512] Optional tile size. Not used if `tileGrid` is provided.
|
||||
* @property {number} [maxResolution] Optional tile grid resolution at level zero. Not used if `tileGrid` is provided.
|
||||
* @property {import("../tilegrid/TileGrid.js").default} [tileGrid] Tile grid.
|
||||
* @property {import("../Tile.js").LoadFunction} [tileLoadFunction]
|
||||
* Optional function to load a tile given a URL. Could look like this for pbf tiles:
|
||||
@@ -105,6 +106,7 @@ class VectorTile extends UrlTile {
|
||||
|
||||
const tileGrid = options.tileGrid || createXYZ({
|
||||
extent: extent,
|
||||
maxResolution: options.maxResolution,
|
||||
maxZoom: options.maxZoom !== undefined ? options.maxZoom : 22,
|
||||
minZoom: options.minZoom,
|
||||
tileSize: options.tileSize || 512
|
||||
@@ -140,15 +142,9 @@ class VectorTile extends UrlTile {
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @type {Object<string, import("../VectorTile.js").default>}
|
||||
* @type {TileCache}
|
||||
*/
|
||||
this.sourceTileByKey_ = {};
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @type {Object<string, Array<import("../VectorTile.js").default>>}
|
||||
*/
|
||||
this.sourceTilesByTileKey_ = {};
|
||||
this.sourceTileCache = new TileCache(this.tileCache.highWaterMark);
|
||||
|
||||
/**
|
||||
* @private
|
||||
@@ -228,8 +224,16 @@ class VectorTile extends UrlTile {
|
||||
*/
|
||||
clear() {
|
||||
this.tileCache.clear();
|
||||
this.sourceTileByKey_ = {};
|
||||
this.sourceTilesByTileKey_ = {};
|
||||
this.sourceTileCache.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {import("../proj/Projection.js").default} projection Projection.
|
||||
* @param {!Object<string, boolean>} usedTiles Used tiles.
|
||||
*/
|
||||
expireCache(projection, usedTiles) {
|
||||
super.expireCache(projection, usedTiles);
|
||||
this.sourceTileCache.expireCache({});
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -254,7 +258,7 @@ class VectorTile extends UrlTile {
|
||||
const sourceZ = sourceTileGrid.getZForResolution(resolution, 1);
|
||||
const minZoom = sourceTileGrid.getMinZoom();
|
||||
|
||||
const previousSourceTiles = this.sourceTilesByTileKey_[tile.getKey()];
|
||||
const previousSourceTiles = tile.sourceTiles;
|
||||
let sourceTiles, covered, loadedZ;
|
||||
if (previousSourceTiles && previousSourceTiles.length > 0 && previousSourceTiles[0].tileCoord[0] === sourceZ) {
|
||||
sourceTiles = previousSourceTiles;
|
||||
@@ -270,8 +274,8 @@ class VectorTile extends UrlTile {
|
||||
const tileUrl = this.tileUrlFunction(sourceTileCoord, pixelRatio, projection);
|
||||
let sourceTile;
|
||||
if (tileUrl !== undefined) {
|
||||
if (tileUrl in this.sourceTileByKey_) {
|
||||
sourceTile = this.sourceTileByKey_[tileUrl];
|
||||
if (this.sourceTileCache.containsKey(tileUrl)) {
|
||||
sourceTile = this.sourceTileCache.get(tileUrl);
|
||||
const state = sourceTile.getState();
|
||||
if (state === TileState.LOADED || state === TileState.ERROR || state === TileState.EMPTY) {
|
||||
sourceTiles.push(sourceTile);
|
||||
@@ -283,7 +287,7 @@ class VectorTile extends UrlTile {
|
||||
sourceTile.extent = sourceTileGrid.getTileCoordExtent(sourceTileCoord);
|
||||
sourceTile.projection = projection;
|
||||
sourceTile.resolution = sourceTileGrid.getResolution(sourceTileCoord[0]);
|
||||
this.sourceTileByKey_[tileUrl] = sourceTile;
|
||||
this.sourceTileCache.set(tileUrl, sourceTile);
|
||||
sourceTile.addEventListener(EventType.CHANGE, this.handleTileChange.bind(this));
|
||||
sourceTile.load();
|
||||
}
|
||||
@@ -294,13 +298,12 @@ class VectorTile extends UrlTile {
|
||||
}
|
||||
if (sourceTile.getState() !== TileState.EMPTY && tile.getState() === TileState.IDLE) {
|
||||
tile.loadingSourceTiles++;
|
||||
const key = listen(sourceTile, EventType.CHANGE, function() {
|
||||
sourceTile.addEventListener(EventType.CHANGE, function listenChange() {
|
||||
const state = sourceTile.getState();
|
||||
const sourceTileKey = sourceTile.getKey();
|
||||
if (state === TileState.LOADED || state === TileState.ERROR) {
|
||||
if (state === TileState.LOADED) {
|
||||
remove(tile.sourceTileListenerKeys, key);
|
||||
unlistenByKey(key);
|
||||
sourceTile.removeEventListener(EventType.CHANGE, listenChange);
|
||||
tile.loadingSourceTiles--;
|
||||
delete tile.errorSourceTileKeys[sourceTileKey];
|
||||
} else if (state === TileState.ERROR) {
|
||||
@@ -314,7 +317,6 @@ class VectorTile extends UrlTile {
|
||||
}
|
||||
}
|
||||
});
|
||||
tile.sourceTileListenerKeys.push(key);
|
||||
}
|
||||
}.bind(this));
|
||||
if (!covered) {
|
||||
@@ -332,43 +334,12 @@ class VectorTile extends UrlTile {
|
||||
if (tile.getState() < TileState.LOADED) {
|
||||
tile.setState(TileState.LOADED);
|
||||
} else if (!previousSourceTiles || !equals(sourceTiles, previousSourceTiles)) {
|
||||
this.removeSourceTiles(tile);
|
||||
this.addSourceTiles(tile, sourceTiles);
|
||||
tile.sourceTiles = sourceTiles;
|
||||
}
|
||||
}
|
||||
return sourceTiles;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {VectorRenderTile} tile Tile.
|
||||
* @param {Array<import("../VectorTile").default>} sourceTiles Source tiles.
|
||||
*/
|
||||
addSourceTiles(tile, sourceTiles) {
|
||||
this.sourceTilesByTileKey_[tile.getKey()] = sourceTiles;
|
||||
for (let i = 0, ii = sourceTiles.length; i < ii; ++i) {
|
||||
sourceTiles[i].consumers++;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {VectorRenderTile} tile Tile.
|
||||
*/
|
||||
removeSourceTiles(tile) {
|
||||
const tileKey = tile.getKey();
|
||||
if (tileKey in this.sourceTilesByTileKey_) {
|
||||
const sourceTiles = this.sourceTilesByTileKey_[tileKey];
|
||||
for (let i = 0, ii = sourceTiles.length; i < ii; ++i) {
|
||||
const sourceTile = sourceTiles[i];
|
||||
sourceTile.consumers--;
|
||||
if (sourceTile.consumers === 0) {
|
||||
sourceTile.dispose();
|
||||
delete this.sourceTileByKey_[sourceTile.getKey()];
|
||||
}
|
||||
}
|
||||
}
|
||||
delete this.sourceTilesByTileKey_[tileKey];
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
@@ -410,9 +381,7 @@ class VectorTile extends UrlTile {
|
||||
tileCoord,
|
||||
empty ? TileState.EMPTY : TileState.IDLE,
|
||||
urlTileCoord,
|
||||
this.tileGrid,
|
||||
this.getSourceTiles.bind(this, pixelRatio, projection),
|
||||
this.removeSourceTiles.bind(this));
|
||||
this.getSourceTiles.bind(this, pixelRatio, projection));
|
||||
|
||||
newTile.key = key;
|
||||
if (tile) {
|
||||
|
||||
@@ -15,7 +15,7 @@ import {appendParams} from '../uri.js';
|
||||
/**
|
||||
* @typedef {Object} Options
|
||||
* @property {import("./Source.js").AttributionLike} [attributions] Attributions.
|
||||
* @property {number} [cacheSize] Tile cache size. The default depends on the screen size. Will increase if too small.
|
||||
* @property {number} [cacheSize] Tile cache size. The default depends on the screen size. Will be ignored if too small.
|
||||
* @property {null|string} [crossOrigin] The `crossOrigin` attribute for loaded images. Note that
|
||||
* you must provide a `crossOrigin` value if you want to access pixel data with the Canvas renderer.
|
||||
* See https://developer.mozilla.org/en-US/docs/Web/HTML/CORS_enabled_image for more detail.
|
||||
|
||||
@@ -9,7 +9,7 @@ import {createXYZ, extentFromProjection} from '../tilegrid.js';
|
||||
* @typedef {Object} Options
|
||||
* @property {import("./Source.js").AttributionLike} [attributions] Attributions.
|
||||
* @property {boolean} [attributionsCollapsible=true] Attributions are collapsible.
|
||||
* @property {number} [cacheSize] Tile cache size. The default depends on the screen size. Will increase if too small.
|
||||
* @property {number} [cacheSize] Tile cache size. The default depends on the screen size. Will be ignored if too small.
|
||||
* @property {null|string} [crossOrigin] The `crossOrigin` attribute for loaded images. Note that
|
||||
* you must provide a `crossOrigin` value if you want to access pixel data with the Canvas renderer.
|
||||
* See https://developer.mozilla.org/en-US/docs/Web/HTML/CORS_enabled_image for more detail.
|
||||
@@ -19,8 +19,9 @@ import {createXYZ, extentFromProjection} from '../tilegrid.js';
|
||||
* Higher values can increase reprojection performance, but decrease precision.
|
||||
* @property {object} [reprojectionContextOptions] Optional properties to set on the canvas context used
|
||||
* for reprojection. For example specify `{imageSmoothingEnabled: false}` to disable image smoothing.
|
||||
* @property {number} [maxZoom=18] Optional max zoom level.
|
||||
* @property {number} [minZoom=0] Optional min zoom level.
|
||||
* @property {number} [maxZoom=42] Optional max zoom level. Not used if `tileGrid` is provided.
|
||||
* @property {number} [minZoom=0] Optional min zoom level. Not used if `tileGrid` is provided.
|
||||
* @property {number} [maxResolution] Optional tile grid resolution at level zero. Not used if `tileGrid` is provided.
|
||||
* @property {import("../tilegrid/TileGrid.js").default} [tileGrid] Tile grid.
|
||||
* @property {import("../Tile.js").LoadFunction} [tileLoadFunction] Optional function to load a tile given a URL. The default is
|
||||
* ```js
|
||||
@@ -33,9 +34,10 @@ import {createXYZ, extentFromProjection} from '../tilegrid.js';
|
||||
* by 512px images (for retina/hidpi devices) then `tilePixelRatio`
|
||||
* should be set to `2`.
|
||||
* @property {number|import("../size.js").Size} [tileSize=[256, 256]] The tile size used by the tile service.
|
||||
* Not used if `tileGrid` is provided.
|
||||
* @property {import("../Tile.js").UrlFunction} [tileUrlFunction] Optional function to get
|
||||
* tile URL given a tile coordinate and the projection.
|
||||
* Required if url or urls are not provided.
|
||||
* Required if `url` or `urls` are not provided.
|
||||
* @property {string} [url] URL template. Must include `{x}`, `{y}` or `{-y}`,
|
||||
* and `{z}` placeholders. A `{?-?}` template pattern, for example `subdomain{a-f}.domain.com`,
|
||||
* may be used instead of defining each one separately in the `urls` option.
|
||||
@@ -80,6 +82,7 @@ class XYZ extends TileImage {
|
||||
const tileGrid = options.tileGrid !== undefined ? options.tileGrid :
|
||||
createXYZ({
|
||||
extent: extentFromProjection(projection),
|
||||
maxResolution: options.maxResolution,
|
||||
maxZoom: options.maxZoom,
|
||||
minZoom: options.minZoom,
|
||||
tileSize: options.tileSize
|
||||
|
||||
+16
-23
@@ -8,7 +8,6 @@ import TileState from '../TileState.js';
|
||||
import {expandUrl, createFromTileUrlFunctions} from '../tileurlfunction.js';
|
||||
import {assert} from '../asserts.js';
|
||||
import {createCanvasContext2D} from '../dom.js';
|
||||
import {getTopLeft} from '../extent.js';
|
||||
import {toSize} from '../size.js';
|
||||
import TileImage from './TileImage.js';
|
||||
import TileGrid from '../tilegrid/TileGrid.js';
|
||||
@@ -26,8 +25,7 @@ const TierSizeCalculation = {
|
||||
export class CustomTile extends ImageTile {
|
||||
|
||||
/**
|
||||
* @param {number} tilePixelRatio Tile pixel ratio to display the tile
|
||||
* @param {import("../tilegrid/TileGrid.js").default} tileGrid TileGrid that the tile belongs to.
|
||||
* @param {import("../size.js").Size} tileSize Full tile size.
|
||||
* @param {import("../tilecoord.js").TileCoord} tileCoord Tile coordinate.
|
||||
* @param {TileState} state State.
|
||||
* @param {string} src Image source URI.
|
||||
@@ -35,7 +33,8 @@ export class CustomTile extends ImageTile {
|
||||
* @param {import("../Tile.js").LoadFunction} tileLoadFunction Tile load function.
|
||||
* @param {import("../Tile.js").Options=} opt_options Tile options.
|
||||
*/
|
||||
constructor(tilePixelRatio, tileGrid, tileCoord, state, src, crossOrigin, tileLoadFunction, opt_options) {
|
||||
constructor(tileSize, tileCoord, state, src, crossOrigin, tileLoadFunction, opt_options) {
|
||||
|
||||
super(tileCoord, state, src, crossOrigin, tileLoadFunction, opt_options);
|
||||
|
||||
/**
|
||||
@@ -45,14 +44,10 @@ export class CustomTile extends ImageTile {
|
||||
this.zoomifyImage_ = null;
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @type {import("../size.js").Size}
|
||||
*/
|
||||
this.tileSize_ = toSize(tileGrid.getTileSize(tileCoord[0])).map(
|
||||
function(x) {
|
||||
return x * tilePixelRatio;
|
||||
}
|
||||
);
|
||||
this.tileSize_ = tileSize;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -85,7 +80,7 @@ export class CustomTile extends ImageTile {
|
||||
/**
|
||||
* @typedef {Object} Options
|
||||
* @property {import("./Source.js").AttributionLike} [attributions] Attributions.
|
||||
* @property {number} [cacheSize] Tile cache size. The default depends on the screen size. Will increase if too small.
|
||||
* @property {number} [cacheSize] Tile cache size. The default depends on the screen size. Will be ignored if too small.
|
||||
* @property {null|string} [crossOrigin] The `crossOrigin` attribute for loaded images. Note that
|
||||
* you must provide a `crossOrigin` value you want to access pixel data with the Canvas renderer.
|
||||
* See https://developer.mozilla.org/en-US/docs/Web/HTML/CORS_enabled_image for more detail.
|
||||
@@ -116,8 +111,8 @@ export class CustomTile extends ImageTile {
|
||||
* @property {number} [transition] Duration of the opacity transition for rendering.
|
||||
* To disable the opacity transition, pass `transition: 0`.
|
||||
* @property {number} [tileSize=256] Tile size. Same tile size is used for all zoom levels.
|
||||
* @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.
|
||||
* @property {number} [zDirection] Indicate which resolution should be used
|
||||
* by a renderer if the views resolution does not match any resolution of the tile source.
|
||||
* If 0, the nearest resolution will be used. If 1, the nearest lower resolution
|
||||
* will be used. If -1, the nearest higher resolution will be used.
|
||||
*/
|
||||
@@ -143,13 +138,12 @@ class Zoomify extends TileImage {
|
||||
options.tierSizeCalculation :
|
||||
TierSizeCalculation.DEFAULT;
|
||||
|
||||
const tilePixelRatio = options.tilePixelRatio || 1;
|
||||
const imageWidth = size[0];
|
||||
const imageHeight = size[1];
|
||||
const extent = options.extent || [0, -size[1], size[0], 0];
|
||||
const tierSizeInTiles = [];
|
||||
const tileSize = options.tileSize || DEFAULT_TILE_SIZE;
|
||||
const tilePixelRatio = options.tilePixelRatio || 1;
|
||||
let tileSizeForTierSizeCalculation = tileSize;
|
||||
let tileSizeForTierSizeCalculation = tileSize * tilePixelRatio;
|
||||
|
||||
switch (tierSizeCalculation) {
|
||||
case TierSizeCalculation.DEFAULT:
|
||||
@@ -181,10 +175,10 @@ class Zoomify extends TileImage {
|
||||
tierSizeInTiles.push([1, 1]);
|
||||
tierSizeInTiles.reverse();
|
||||
|
||||
const resolutions = [1];
|
||||
const resolutions = [tilePixelRatio];
|
||||
const tileCountUpToTier = [0];
|
||||
for (let i = 1, ii = tierSizeInTiles.length; i < ii; i++) {
|
||||
resolutions.push(1 << i);
|
||||
resolutions.push(tilePixelRatio << i);
|
||||
tileCountUpToTier.push(
|
||||
tierSizeInTiles[i - 1][0] * tierSizeInTiles[i - 1][1] +
|
||||
tileCountUpToTier[i - 1]
|
||||
@@ -194,8 +188,7 @@ class Zoomify extends TileImage {
|
||||
|
||||
const tileGrid = new TileGrid({
|
||||
tileSize: tileSize,
|
||||
extent: extent,
|
||||
origin: getTopLeft(extent),
|
||||
extent: options.extent || [0, -imageHeight, imageWidth, 0],
|
||||
resolutions: resolutions
|
||||
});
|
||||
|
||||
@@ -205,6 +198,8 @@ class Zoomify extends TileImage {
|
||||
}
|
||||
const urls = expandUrl(url);
|
||||
|
||||
const tileWidth = tileSize * tilePixelRatio;
|
||||
|
||||
/**
|
||||
* @param {string} template Template.
|
||||
* @return {import("../Tile.js").UrlFunction} Tile URL function.
|
||||
@@ -228,8 +223,6 @@ class Zoomify extends TileImage {
|
||||
const tileIndex =
|
||||
tileCoordX +
|
||||
tileCoordY * tierSizeInTiles[tileCoordZ][0];
|
||||
const tileSize = tileGrid.getTileSize(tileCoordZ);
|
||||
const tileWidth = Array.isArray(tileSize) ? tileSize[0] : tileSize;
|
||||
const tileGroup = ((tileIndex + tileCountUpToTier[tileCoordZ]) / tileWidth) | 0;
|
||||
const localContext = {
|
||||
'z': tileCoordZ,
|
||||
@@ -248,7 +241,7 @@ class Zoomify extends TileImage {
|
||||
|
||||
const tileUrlFunction = createFromTileUrlFunctions(urls.map(createFromTemplate));
|
||||
|
||||
const ZoomifyTileClass = CustomTile.bind(null, tilePixelRatio, tileGrid);
|
||||
const ZoomifyTileClass = CustomTile.bind(null, toSize(tileSize * tilePixelRatio));
|
||||
|
||||
super({
|
||||
attributions: options.attributions,
|
||||
|
||||
@@ -1,10 +1,3 @@
|
||||
/**
|
||||
* @license
|
||||
* Latitude/longitude spherical geodesy formulae taken from
|
||||
* http://www.movable-type.co.uk/scripts/latlong.html
|
||||
* Licensed under CC-BY-3.0.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @module ol/sphere
|
||||
*/
|
||||
|
||||
@@ -3,8 +3,6 @@
|
||||
*/
|
||||
|
||||
import {assert} from '../asserts.js';
|
||||
import EventTarget from '../events/Target.js';
|
||||
import EventType from '../events/EventType.js';
|
||||
|
||||
|
||||
/**
|
||||
@@ -25,15 +23,13 @@ import EventType from '../events/EventType.js';
|
||||
* @fires import("../events/Event.js").default
|
||||
* @template T
|
||||
*/
|
||||
class LRUCache extends EventTarget {
|
||||
class LRUCache {
|
||||
|
||||
/**
|
||||
* @param {number=} opt_highWaterMark High water mark.
|
||||
*/
|
||||
constructor(opt_highWaterMark) {
|
||||
|
||||
super();
|
||||
|
||||
/**
|
||||
* @type {number}
|
||||
*/
|
||||
@@ -82,7 +78,6 @@ class LRUCache extends EventTarget {
|
||||
this.entries_ = {};
|
||||
this.oldest_ = null;
|
||||
this.newest_ = null;
|
||||
this.dispatchEvent(EventType.CLEAR);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@ import RegularShape from './RegularShape.js';
|
||||
* @property {import("./Fill.js").default} [fill] Fill style.
|
||||
* @property {number} radius Circle radius.
|
||||
* @property {import("./Stroke.js").default} [stroke] Stroke style.
|
||||
* @property {Array<number>} [displacement=[0,0]] displacement
|
||||
*/
|
||||
|
||||
|
||||
@@ -30,7 +31,8 @@ class CircleStyle extends RegularShape {
|
||||
points: Infinity,
|
||||
fill: options.fill,
|
||||
radius: options.radius,
|
||||
stroke: options.stroke
|
||||
stroke: options.stroke,
|
||||
displacement: options.displacement !== undefined ? options.displacement : [0, 0]
|
||||
});
|
||||
|
||||
}
|
||||
@@ -45,7 +47,8 @@ class CircleStyle extends RegularShape {
|
||||
const style = new CircleStyle({
|
||||
fill: this.getFill() ? this.getFill().clone() : undefined,
|
||||
stroke: this.getStroke() ? this.getStroke().clone() : undefined,
|
||||
radius: this.getRadius()
|
||||
radius: this.getRadius(),
|
||||
displacement: this.getDisplacement().slice()
|
||||
});
|
||||
style.setOpacity(this.getOpacity());
|
||||
style.setScale(this.getScale());
|
||||
|
||||
@@ -33,6 +33,7 @@ import ImageStyle from './Image.js';
|
||||
* to provide the size of the image, with the `imgSize` option.
|
||||
* @property {Array<number>} [offset=[0, 0]] Offset, which, together with the size and the offset origin, define the
|
||||
* sub-rectangle to use from the original icon image.
|
||||
* @property {Array<number>} [displacement=[0,0]] Displacement the icon
|
||||
* @property {import("./IconOrigin.js").default} [offsetOrigin='top-left'] Origin of the offset: `bottom-left`, `bottom-right`,
|
||||
* `top-left` or `top-right`.
|
||||
* @property {number} [opacity=1] Opacity of the icon.
|
||||
@@ -84,6 +85,7 @@ class Icon extends ImageStyle {
|
||||
opacity: opacity,
|
||||
rotation: rotation,
|
||||
scale: scale,
|
||||
displacement: options.displacement !== undefined ? options.displacement : [0, 0],
|
||||
rotateWithView: rotateWithView
|
||||
});
|
||||
|
||||
@@ -177,7 +179,6 @@ class Icon extends ImageStyle {
|
||||
* @type {Array<number>}
|
||||
*/
|
||||
this.offset_ = options.offset !== undefined ? options.offset : [0, 0];
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @type {import("./IconOrigin.js").default}
|
||||
@@ -336,6 +337,7 @@ class Icon extends ImageStyle {
|
||||
return this.origin_;
|
||||
}
|
||||
let offset = this.offset_;
|
||||
const displacement = this.getDisplacement();
|
||||
|
||||
if (this.offsetOrigin_ != IconOrigin.TOP_LEFT) {
|
||||
const size = this.getSize();
|
||||
@@ -353,6 +355,8 @@ class Icon extends ImageStyle {
|
||||
offset[1] = iconImageSize[1] - size[1] - offset[1];
|
||||
}
|
||||
}
|
||||
offset[0] += displacement[0];
|
||||
offset[1] += displacement[1];
|
||||
this.origin_ = offset;
|
||||
return this.origin_;
|
||||
}
|
||||
|
||||
+18
-1
@@ -10,6 +10,7 @@ import {abstract} from '../util.js';
|
||||
* @property {boolean} rotateWithView
|
||||
* @property {number} rotation
|
||||
* @property {number} scale
|
||||
* @property {Array<number>} displacement
|
||||
*/
|
||||
|
||||
|
||||
@@ -51,6 +52,12 @@ class ImageStyle {
|
||||
*/
|
||||
this.scale_ = options.scale;
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @type {Array<number>}
|
||||
*/
|
||||
this.displacement_ = options.displacement;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -63,7 +70,8 @@ class ImageStyle {
|
||||
opacity: this.getOpacity(),
|
||||
scale: this.getScale(),
|
||||
rotation: this.getRotation(),
|
||||
rotateWithView: this.getRotateWithView()
|
||||
rotateWithView: this.getRotateWithView(),
|
||||
displacement: this.getDisplacement().slice()
|
||||
});
|
||||
}
|
||||
|
||||
@@ -103,6 +111,15 @@ class ImageStyle {
|
||||
return this.scale_;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the displacement of the shape
|
||||
* @return {Array<number>} Shape's center displacement
|
||||
* @api
|
||||
*/
|
||||
getDisplacement() {
|
||||
return this.displacement_;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the anchor point in pixels. The anchor determines the center point for the
|
||||
* symbolizer.
|
||||
|
||||
@@ -35,6 +35,7 @@ export const SymbolType = {
|
||||
* @property {string} [src] Path to the image to be used for the symbol. Only required with `symbolType: 'image'`.
|
||||
* @property {import("../color.js").Color|Array<ExpressionValue>|string} [color='#FFFFFF'] Color used for the representation (either fill, line or symbol).
|
||||
* @property {ExpressionValue} [opacity=1] Opacity.
|
||||
* @property {ExpressionValue} [rotation=0] Symbol rotation in radians.
|
||||
* @property {Array<ExpressionValue, ExpressionValue>} [offset] Offset on X and Y axis for symbols. If not specified, the symbol will be centered.
|
||||
* @property {Array<ExpressionValue, ExpressionValue, ExpressionValue, ExpressionValue>} [textureCoord] Texture coordinates. If not specified, the whole texture will be used (range for 0 to 1 on both axes).
|
||||
* @property {boolean} [rotateWithView=false] Specify whether the symbol must rotate with the view or stay upwards.
|
||||
|
||||
@@ -20,6 +20,7 @@ import ImageStyle from './Image.js';
|
||||
* @property {number} [radius1] Outer radius of a star.
|
||||
* @property {number} [radius2] Inner radius of a star.
|
||||
* @property {number} [angle=0] Shape's angle in radians. A value of 0 will have one of the shape's point facing up.
|
||||
* @property {Array<number>} [displacement=[0,0]] Displacement of the shape
|
||||
* @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.
|
||||
@@ -61,7 +62,8 @@ class RegularShape extends ImageStyle {
|
||||
opacity: 1,
|
||||
rotateWithView: rotateWithView,
|
||||
rotation: options.rotation !== undefined ? options.rotation : 0,
|
||||
scale: 1
|
||||
scale: 1,
|
||||
displacement: options.displacement !== undefined ? options.displacement : [0, 0]
|
||||
});
|
||||
|
||||
/**
|
||||
@@ -160,7 +162,8 @@ class RegularShape extends ImageStyle {
|
||||
angle: this.getAngle(),
|
||||
stroke: this.getStroke() ? this.getStroke().clone() : undefined,
|
||||
rotation: this.getRotation(),
|
||||
rotateWithView: this.getRotateWithView()
|
||||
rotateWithView: this.getRotateWithView(),
|
||||
displacement: this.getDisplacement().slice()
|
||||
});
|
||||
style.setOpacity(this.getOpacity());
|
||||
style.setScale(this.getScale());
|
||||
@@ -353,12 +356,13 @@ class RegularShape extends ImageStyle {
|
||||
// canvas.width and height are rounded to the closest integer
|
||||
size = this.canvas_.width;
|
||||
const imageSize = size;
|
||||
const displacement = this.getDisplacement();
|
||||
|
||||
this.draw_(renderOptions, context, 0, 0);
|
||||
|
||||
this.createHitDetectionCanvas_(renderOptions);
|
||||
|
||||
this.anchor_ = [size / 2, size / 2];
|
||||
this.anchor_ = [size / 2 - displacement[0], size / 2 + displacement[1]];
|
||||
this.size_ = [size, size];
|
||||
this.imageSize_ = [imageSize, imageSize];
|
||||
}
|
||||
|
||||
@@ -13,9 +13,10 @@ import Stroke from './Stroke.js';
|
||||
* A function that takes an {@link module:ol/Feature} and a `{number}`
|
||||
* representing the view's resolution. The function should return a
|
||||
* {@link module:ol/style/Style} or an array of them. This way e.g. a
|
||||
* vector layer can be styled.
|
||||
* vector layer can be styled. If the function returns `undefined`, the
|
||||
* feature will not be rendered.
|
||||
*
|
||||
* @typedef {function(import("../Feature.js").FeatureLike, number):(Style|Array<Style>)} StyleFunction
|
||||
* @typedef {function(import("../Feature.js").FeatureLike, number):(Style|Array<Style>|void)} StyleFunction
|
||||
*/
|
||||
|
||||
/**
|
||||
|
||||
@@ -16,6 +16,8 @@ import {asArray, isStringColor} from '../color.js';
|
||||
* Note: those will be taken from the attributes provided to the renderer
|
||||
* * `['var', 'varName']` fetches a value from the style variables, or 0 if undefined
|
||||
* * `['time']` returns the time in seconds since the creation of the layer
|
||||
* * `['zoom']` returns the current zoom level
|
||||
* * `['resolution']` returns the current resolution
|
||||
*
|
||||
* * Math operators:
|
||||
* * `['*', value1, value2]` multiplies `value1` by `value2`
|
||||
|
||||
+9
-6
@@ -72,8 +72,9 @@ export function createForExtent(extent, opt_maxZoom, opt_tileSize, opt_corner) {
|
||||
/**
|
||||
* @typedef {Object} XYZOptions
|
||||
* @property {import("./extent.js").Extent} [extent] Extent for the tile grid. The origin for an XYZ tile grid is the
|
||||
* top-left corner of the extent. The zero level of the grid is defined by the resolution at which one tile fits in the
|
||||
* provided extent. If not provided, the extent of the EPSG:3857 projection is used.
|
||||
* top-left corner of the extent. If `maxResolution` is not provided the zero level of the grid is defined by the resolution
|
||||
* at which one tile fits in the provided extent. If not provided, the extent of the EPSG:3857 projection is used.
|
||||
* @property {number} [maxResolution] Resolution at level zero.
|
||||
* @property {number} [maxZoom] Maximum zoom. The default is `42`. This determines the number of levels
|
||||
* in the grid set. For example, a `maxZoom` of 21 means there are 22 levels in the grid set.
|
||||
* @property {number} [minZoom=0] Minimum zoom.
|
||||
@@ -99,7 +100,8 @@ export function createXYZ(opt_options) {
|
||||
resolutions: resolutionsFromExtent(
|
||||
extent,
|
||||
xyzOptions.maxZoom,
|
||||
xyzOptions.tileSize
|
||||
xyzOptions.tileSize,
|
||||
xyzOptions.maxResolution
|
||||
)
|
||||
};
|
||||
return new TileGrid(gridOptions);
|
||||
@@ -113,9 +115,10 @@ export function createXYZ(opt_options) {
|
||||
* DEFAULT_MAX_ZOOM).
|
||||
* @param {number|import("./size.js").Size=} opt_tileSize Tile size (default uses
|
||||
* DEFAULT_TILE_SIZE).
|
||||
* @param {number=} opt_maxResolution Resolution at level zero.
|
||||
* @return {!Array<number>} Resolutions array.
|
||||
*/
|
||||
function resolutionsFromExtent(extent, opt_maxZoom, opt_tileSize) {
|
||||
function resolutionsFromExtent(extent, opt_maxZoom, opt_tileSize, opt_maxResolution) {
|
||||
const maxZoom = opt_maxZoom !== undefined ?
|
||||
opt_maxZoom : DEFAULT_MAX_ZOOM;
|
||||
|
||||
@@ -124,8 +127,8 @@ function resolutionsFromExtent(extent, opt_maxZoom, opt_tileSize) {
|
||||
|
||||
const tileSize = toSize(opt_tileSize !== undefined ?
|
||||
opt_tileSize : DEFAULT_TILE_SIZE);
|
||||
const maxResolution = Math.max(
|
||||
width / tileSize[0], height / tileSize[1]);
|
||||
const maxResolution = opt_maxResolution > 0 ? opt_maxResolution :
|
||||
Math.max(width / tileSize[0], height / tileSize[1]);
|
||||
|
||||
const length = maxZoom + 1;
|
||||
const resolutions = new Array(length);
|
||||
|
||||
@@ -558,6 +558,7 @@ class WebGLHelper extends Disposable {
|
||||
if (value instanceof HTMLCanvasElement || value instanceof HTMLImageElement || value instanceof ImageData) {
|
||||
// create a texture & put data
|
||||
if (!uniform.texture) {
|
||||
uniform.prevValue = undefined;
|
||||
uniform.texture = gl.createTexture();
|
||||
}
|
||||
gl.activeTexture(gl[`TEXTURE${textureSlot}`]);
|
||||
@@ -567,7 +568,8 @@ class WebGLHelper extends Disposable {
|
||||
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
|
||||
|
||||
const imageReady = !(value instanceof HTMLImageElement) || /** @type {HTMLImageElement} */(value).complete;
|
||||
if (imageReady) {
|
||||
if (imageReady && uniform.prevValue !== value) {
|
||||
uniform.prevValue = value;
|
||||
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, value);
|
||||
}
|
||||
|
||||
|
||||
@@ -56,6 +56,12 @@ export class ShaderBuilder {
|
||||
*/
|
||||
this.sizeExpression = 'vec2(1.0)';
|
||||
|
||||
/**
|
||||
* @type {string}
|
||||
* @private
|
||||
*/
|
||||
this.rotationExpression = '0.0';
|
||||
|
||||
/**
|
||||
* @type {string}
|
||||
* @private
|
||||
@@ -138,6 +144,18 @@ export class ShaderBuilder {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets an expression to compute the rotation of the shape.
|
||||
* This expression can use all the uniforms and attributes available
|
||||
* in the vertex shader, and should evaluate to a `float` value in radians.
|
||||
* @param {string} expression Size expression
|
||||
* @return {ShaderBuilder} the builder object
|
||||
*/
|
||||
setRotationExpression(expression) {
|
||||
this.rotationExpression = expression;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets an expression to compute the offset of the symbol from the point center.
|
||||
* This expression can use all the uniforms and attributes available
|
||||
@@ -291,10 +309,24 @@ ${varyings.map(function(varying) {
|
||||
}).join('\n')}
|
||||
void main(void) {
|
||||
mat4 offsetMatrix = ${offsetMatrix};
|
||||
vec2 size = ${this.sizeExpression};
|
||||
vec2 halfSize = ${this.sizeExpression} * 0.5;
|
||||
vec2 offset = ${this.offsetExpression};
|
||||
float offsetX = a_index == 0.0 || a_index == 3.0 ? offset.x - size.x / 2.0 : offset.x + size.x / 2.0;
|
||||
float offsetY = a_index == 0.0 || a_index == 1.0 ? offset.y - size.y / 2.0 : offset.y + size.y / 2.0;
|
||||
float angle = ${this.rotationExpression};
|
||||
float offsetX;
|
||||
float offsetY;
|
||||
if (a_index == 0.0) {
|
||||
offsetX = (offset.x - halfSize.x) * cos(angle) + (offset.y - halfSize.y) * sin(angle);
|
||||
offsetY = (offset.y - halfSize.y) * cos(angle) - (offset.x - halfSize.x) * sin(angle);
|
||||
} else if (a_index == 1.0) {
|
||||
offsetX = (offset.x + halfSize.x) * cos(angle) + (offset.y - halfSize.y) * sin(angle);
|
||||
offsetY = (offset.y - halfSize.y) * cos(angle) - (offset.x + halfSize.x) * sin(angle);
|
||||
} else if (a_index == 2.0) {
|
||||
offsetX = (offset.x + halfSize.x) * cos(angle) + (offset.y + halfSize.y) * sin(angle);
|
||||
offsetY = (offset.y + halfSize.y) * cos(angle) - (offset.x + halfSize.x) * sin(angle);
|
||||
} else {
|
||||
offsetX = (offset.x - halfSize.x) * cos(angle) + (offset.y + halfSize.y) * sin(angle);
|
||||
offsetY = (offset.y + halfSize.y) * cos(angle) - (offset.x - halfSize.x) * sin(angle);
|
||||
}
|
||||
vec4 offsets = offsetMatrix * vec4(offsetX, offsetY, 0.0, 0.0);
|
||||
gl_Position = u_projectionMatrix * vec4(a_position, 0.0, 1.0) + offsets;
|
||||
vec4 texCoord = ${this.texCoordExpression};
|
||||
@@ -381,6 +413,7 @@ export function parseLiteralStyle(style) {
|
||||
const texCoord = symbStyle.textureCoord || [0, 0, 1, 1];
|
||||
const offset = symbStyle.offset || [0, 0];
|
||||
const opacity = symbStyle.opacity !== undefined ? symbStyle.opacity : 1;
|
||||
const rotation = symbStyle.rotation !== undefined ? symbStyle.rotation : 0;
|
||||
|
||||
/**
|
||||
* @type {import("../style/expressions.js").ParsingContext}
|
||||
@@ -406,6 +439,7 @@ export function parseLiteralStyle(style) {
|
||||
};
|
||||
const parsedColor = expressionToGlsl(fragContext, color, ValueTypes.COLOR);
|
||||
const parsedOpacity = expressionToGlsl(fragContext, opacity, ValueTypes.NUMBER);
|
||||
const parsedRotation = expressionToGlsl(fragContext, rotation, ValueTypes.NUMBER);
|
||||
|
||||
let opacityFilter = '1.0';
|
||||
const visibleSize = `vec2(${expressionToGlsl(fragContext, size, ValueTypes.NUMBER_ARRAY | ValueTypes.NUMBER)}).x`;
|
||||
@@ -427,6 +461,7 @@ export function parseLiteralStyle(style) {
|
||||
|
||||
const builder = new ShaderBuilder()
|
||||
.setSizeExpression(`vec2(${parsedSize})`)
|
||||
.setRotationExpression(parsedRotation)
|
||||
.setSymbolOffsetExpression(parsedOffset)
|
||||
.setTextureCoordinateExpression(parsedTexCoord)
|
||||
.setSymbolRotateWithView(!!symbStyle.rotateWithView)
|
||||
|
||||
+49
-10
@@ -23,15 +23,6 @@ import {extend} from './array.js';
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* This document should be used when creating nodes for XML serializations. This
|
||||
* document is also used by {@link module:ol/xml~createElementNS}
|
||||
* @const
|
||||
* @type {Document}
|
||||
*/
|
||||
export const DOCUMENT = document.implementation.createDocument('', '', null);
|
||||
|
||||
|
||||
/**
|
||||
* @type {string}
|
||||
*/
|
||||
@@ -44,7 +35,7 @@ export const XML_SCHEMA_INSTANCE_URI = 'http://www.w3.org/2001/XMLSchema-instanc
|
||||
* @return {Element} Node.
|
||||
*/
|
||||
export function createElementNS(namespaceURI, qualifiedName) {
|
||||
return DOCUMENT.createElementNS(namespaceURI, qualifiedName);
|
||||
return getDocument().createElementNS(namespaceURI, qualifiedName);
|
||||
}
|
||||
|
||||
|
||||
@@ -494,3 +485,51 @@ export function pushSerializeAndPop(object, serializersNS, nodeFactory, values,
|
||||
serialize(serializersNS, nodeFactory, values, objectStack, opt_keys, opt_this);
|
||||
return /** @type {O|undefined} */ (objectStack.pop());
|
||||
}
|
||||
|
||||
let xmlSerializer_ = undefined;
|
||||
|
||||
/**
|
||||
* Register a XMLSerializer. Can be used to inject a XMLSerializer
|
||||
* where there is no globally available implementation.
|
||||
*
|
||||
* @param {XMLSerializer} xmlSerializer A XMLSerializer.
|
||||
* @api
|
||||
*/
|
||||
export function registerXMLSerializer(xmlSerializer) {
|
||||
xmlSerializer_ = xmlSerializer;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {XMLSerializer} The XMLSerializer.
|
||||
*/
|
||||
export function getXMLSerializer() {
|
||||
if (xmlSerializer_ === undefined && typeof XMLSerializer !== 'undefined') {
|
||||
xmlSerializer_ = new XMLSerializer();
|
||||
}
|
||||
return xmlSerializer_;
|
||||
}
|
||||
|
||||
|
||||
let document_ = undefined;
|
||||
|
||||
/**
|
||||
* Register a Document to use when creating nodes for XML serializations. Can be used
|
||||
* to inject a Document where there is no globally available implementation.
|
||||
*
|
||||
* @param {Document} document A Document.
|
||||
* @api
|
||||
*/
|
||||
export function registerDocument(document) {
|
||||
document_ = document;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a document that should be used when creating nodes for XML serializations.
|
||||
* @return {Document} The document.
|
||||
*/
|
||||
export function getDocument() {
|
||||
if (document_ === undefined && typeof document !== 'undefined') {
|
||||
document_ = document.implementation.createDocument('', '', null);
|
||||
}
|
||||
return document_;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user