Merge remote-tracking branch 'origin/master' into fix_triangulation
Conflicts: src/ol/TileCache.js
This commit is contained in:
@@ -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.
|
||||
|
||||
@@ -2,13 +2,14 @@
|
||||
* @module ol/MapBrowserEventHandler
|
||||
*/
|
||||
|
||||
import '@openlayers/pepjs';
|
||||
import {DEVICE_PIXEL_RATIO} from './has.js';
|
||||
import 'elm-pep';
|
||||
import {DEVICE_PIXEL_RATIO, PASSIVE_EVENT_LISTENERS} from './has.js';
|
||||
import MapBrowserEventType from './MapBrowserEventType.js';
|
||||
import MapBrowserPointerEvent from './MapBrowserPointerEvent.js';
|
||||
import {listen, unlistenByKey} from './events.js';
|
||||
import EventTarget from './events/Target.js';
|
||||
import PointerEventType from './pointer/EventType.js';
|
||||
import EventType from './events/EventType.js';
|
||||
|
||||
class MapBrowserEventHandler extends EventTarget {
|
||||
|
||||
@@ -84,6 +85,12 @@ class MapBrowserEventHandler extends EventTarget {
|
||||
PointerEventType.POINTERDOWN,
|
||||
this.handlePointerDown_, this);
|
||||
|
||||
/**
|
||||
* @type {PointerEvent}
|
||||
* @private
|
||||
*/
|
||||
this.originalPointerMoveEvent_;
|
||||
|
||||
/**
|
||||
* @type {?import("./events.js").EventsKey}
|
||||
* @private
|
||||
@@ -92,6 +99,13 @@ class MapBrowserEventHandler extends EventTarget {
|
||||
PointerEventType.POINTERMOVE,
|
||||
this.relayEvent_, this);
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
this.boundHandleTouchMove_ = this.handleTouchMove_.bind(this);
|
||||
|
||||
this.element_.addEventListener(EventType.TOUCHMOVE, this.boundHandleTouchMove_,
|
||||
PASSIVE_EVENT_LISTENERS ? {passive: false} : false);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -246,11 +260,26 @@ class MapBrowserEventHandler extends EventTarget {
|
||||
* @private
|
||||
*/
|
||||
relayEvent_(pointerEvent) {
|
||||
this.originalPointerMoveEvent_ = pointerEvent;
|
||||
const dragging = !!(this.down_ && this.isMoving_(pointerEvent));
|
||||
this.dispatchEvent(new MapBrowserPointerEvent(
|
||||
pointerEvent.type, this.map_, pointerEvent, dragging));
|
||||
}
|
||||
|
||||
/**
|
||||
* Flexible handling of a `touch-action: none` css equivalent: because calling
|
||||
* `preventDefault()` on a `pointermove` event does not stop native page scrolling
|
||||
* and zooming, we also listen for `touchmove` and call `preventDefault()` on it
|
||||
* when an interaction (currently `DragPan` handles the event.
|
||||
* @param {TouchEvent} event Event.
|
||||
* @private
|
||||
*/
|
||||
handleTouchMove_(event) {
|
||||
if (this.originalPointerMoveEvent_.defaultPrevented) {
|
||||
event.preventDefault();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {PointerEvent} pointerEvent Pointer
|
||||
* event.
|
||||
@@ -271,6 +300,8 @@ class MapBrowserEventHandler extends EventTarget {
|
||||
unlistenByKey(this.relayedListenerKey_);
|
||||
this.relayedListenerKey_ = null;
|
||||
}
|
||||
this.element_.removeEventListener(EventType.TOUCHMOVE, this.boundHandleTouchMove_);
|
||||
|
||||
if (this.pointerdownListenerKey_) {
|
||||
unlistenByKey(this.pointerdownListenerKey_);
|
||||
this.pointerdownListenerKey_ = null;
|
||||
|
||||
+33
-41
@@ -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';
|
||||
@@ -22,7 +21,7 @@ import {listen, unlistenByKey} from './events.js';
|
||||
import EventType from './events/EventType.js';
|
||||
import {clone, createOrUpdateEmpty, equals, getForViewAndSize, isEmpty} from './extent.js';
|
||||
import {TRUE} from './functions.js';
|
||||
import {DEVICE_PIXEL_RATIO, IMAGE_DECODE} from './has.js';
|
||||
import {DEVICE_PIXEL_RATIO, IMAGE_DECODE, PASSIVE_EVENT_LISTENERS} from './has.js';
|
||||
import LayerGroup from './layer/Group.js';
|
||||
import {hasArea} from './size.js';
|
||||
import {DROP} from './structs/PriorityQueue.js';
|
||||
@@ -131,17 +130,6 @@ import {toUserCoordinate, fromUserCoordinate} from './proj.js';
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* @param {HTMLElement} element Element.
|
||||
* @param {string} touchAction Value for `touch-action'.
|
||||
*/
|
||||
function setTouchAction(element, touchAction) {
|
||||
element.style.msTouchAction = touchAction;
|
||||
element.style.touchAction = touchAction;
|
||||
element.setAttribute('touch-action', touchAction);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @fires import("./MapBrowserEvent.js").MapBrowserEvent
|
||||
* @fires import("./MapEvent.js").MapEvent
|
||||
@@ -305,15 +293,10 @@ class PluggableMap extends BaseObject {
|
||||
*/
|
||||
this.keyHandlerKeys_ = null;
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @type {?Array<import("./events.js").EventsKey>}
|
||||
*/
|
||||
this.focusHandlerKeys_ = null;
|
||||
|
||||
const handleBrowserEvent = this.handleBrowserEvent.bind(this);
|
||||
this.viewport_.addEventListener(EventType.CONTEXTMENU, handleBrowserEvent, false);
|
||||
this.viewport_.addEventListener(EventType.WHEEL, handleBrowserEvent, false);
|
||||
this.viewport_.addEventListener(EventType.WHEEL, handleBrowserEvent,
|
||||
PASSIVE_EVENT_LISTENERS ? {passive: false} : false);
|
||||
|
||||
/**
|
||||
* @type {Collection<import("./control/Control.js").default>}
|
||||
@@ -949,12 +932,14 @@ class PluggableMap extends BaseObject {
|
||||
// coordinates so interactions cannot be used.
|
||||
return;
|
||||
}
|
||||
let target = mapBrowserEvent.originalEvent.target;
|
||||
while (target instanceof HTMLElement) {
|
||||
if (target.parentElement === this.overlayContainerStopEvent_) {
|
||||
return;
|
||||
let target = /** @type {Node} */ (mapBrowserEvent.originalEvent.target);
|
||||
if (!mapBrowserEvent.dragging) {
|
||||
while (target && target !== this.viewport_) {
|
||||
if (target.parentElement === this.overlayContainerStopEvent_) {
|
||||
return;
|
||||
}
|
||||
target = target.parentElement;
|
||||
}
|
||||
target = target.parentElement;
|
||||
}
|
||||
mapBrowserEvent.frameState = this.frameState_;
|
||||
const interactionsArray = this.getInteractions().getArray();
|
||||
@@ -1043,12 +1028,6 @@ class PluggableMap extends BaseObject {
|
||||
targetElement = this.getTargetElement();
|
||||
}
|
||||
|
||||
if (this.focusHandlerKeys_) {
|
||||
for (let i = 0, ii = this.focusHandlerKeys_.length; i < ii; ++i) {
|
||||
unlistenByKey(this.focusHandlerKeys_[i]);
|
||||
}
|
||||
this.focusHandlerKeys_ = null;
|
||||
}
|
||||
if (this.keyHandlerKeys_) {
|
||||
for (let i = 0, ii = this.keyHandlerKeys_.length; i < ii; ++i) {
|
||||
unlistenByKey(this.keyHandlerKeys_[i]);
|
||||
@@ -1077,15 +1056,6 @@ class PluggableMap extends BaseObject {
|
||||
if (!this.renderer_) {
|
||||
this.renderer_ = this.createRenderer();
|
||||
}
|
||||
let hasFocus = true;
|
||||
if (targetElement.hasAttribute('tabindex')) {
|
||||
hasFocus = document.activeElement === targetElement;
|
||||
this.focusHandlerKeys_ = [
|
||||
listen(targetElement, EventType.FOCUS, setTouchAction.bind(this, this.viewport_, 'none')),
|
||||
listen(targetElement, EventType.BLUR, setTouchAction.bind(this, this.viewport_, 'auto'))
|
||||
];
|
||||
}
|
||||
setTouchAction(this.viewport_, hasFocus ? 'none' : 'auto');
|
||||
|
||||
const keyboardEventTarget = !this.keyboardEventTarget_ ?
|
||||
targetElement : this.keyboardEventTarget_;
|
||||
@@ -1133,7 +1103,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);
|
||||
@@ -1391,6 +1362,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() {
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+2
-12
@@ -6,15 +6,6 @@ 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.
|
||||
*/
|
||||
@@ -24,8 +15,7 @@ class TileCache extends LRUCache {
|
||||
if (tile.getKey() in usedTiles) {
|
||||
break;
|
||||
} else {
|
||||
// This lets the GC clean up the object
|
||||
this.pop();
|
||||
this.pop().release();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -43,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
|
||||
};
|
||||
|
||||
+25
-47
@@ -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});
|
||||
|
||||
@@ -61,9 +61,9 @@ class VectorRenderTile extends Tile {
|
||||
this.errorSourceTileKeys = {};
|
||||
|
||||
/**
|
||||
* @type {ImageData}
|
||||
* @type {Object<number, ImageData>}
|
||||
*/
|
||||
this.hitDetectionImageData = null;
|
||||
this.hitDetectionImageData = {};
|
||||
|
||||
/**
|
||||
* @private
|
||||
@@ -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}
|
||||
@@ -105,7 +100,9 @@ class VectorTile extends Tile {
|
||||
if (this.state == TileState.IDLE) {
|
||||
this.setState(TileState.LOADING);
|
||||
this.tileLoadFunction_(this, this.url_);
|
||||
this.loader_(this.extent, this.resolution, this.projection);
|
||||
if (this.loader_) {
|
||||
this.loader_(this.extent, this.resolution, this.projection);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+63
-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';
|
||||
@@ -294,6 +293,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}
|
||||
@@ -312,6 +317,12 @@ class View extends BaseObject {
|
||||
*/
|
||||
this.targetRotation_;
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @type {import("./coordinate.js").Coordinate|undefined}
|
||||
*/
|
||||
this.cancelAnchor_ = undefined;
|
||||
|
||||
if (options.center) {
|
||||
options.center = fromUserCoordinate(options.center, this.projection_);
|
||||
}
|
||||
@@ -584,13 +595,19 @@ class View extends BaseObject {
|
||||
*/
|
||||
cancelAnimations() {
|
||||
this.setHint(ViewHint.ANIMATING, -this.hints_[ViewHint.ANIMATING]);
|
||||
let anchor;
|
||||
for (let i = 0, ii = this.animations_.length; i < ii; ++i) {
|
||||
const series = this.animations_[i];
|
||||
if (series[0].callback) {
|
||||
animationCallback(series[0].callback, false);
|
||||
}
|
||||
anchor = anchor ||
|
||||
series.filter(function(animation) {
|
||||
return !animation.complete;
|
||||
})[0].anchor;
|
||||
}
|
||||
this.animations_.length = 0;
|
||||
this.cancelAnchor_ = anchor;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -637,7 +654,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);
|
||||
}
|
||||
@@ -710,26 +727,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];
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -780,8 +804,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
|
||||
*/
|
||||
@@ -796,7 +820,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());
|
||||
@@ -919,7 +943,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);
|
||||
@@ -933,7 +957,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 (
|
||||
@@ -964,17 +988,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;
|
||||
});
|
||||
}
|
||||
@@ -1067,7 +1091,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;
|
||||
@@ -1085,7 +1109,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;
|
||||
}
|
||||
@@ -1102,7 +1126,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;
|
||||
@@ -1249,10 +1273,10 @@ 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 !== undefined) {
|
||||
if (opt_anchor) {
|
||||
this.targetCenter_ = this.calculateCenterZoom(newResolution, opt_anchor);
|
||||
}
|
||||
|
||||
@@ -1292,7 +1316,7 @@ class View extends BaseObject {
|
||||
adjustRotationInternal(delta, opt_anchor) {
|
||||
const isMoving = this.getAnimating() || this.getInteracting();
|
||||
const newRotation = this.constraints_.rotation(this.targetRotation_ + delta, isMoving);
|
||||
if (opt_anchor !== undefined) {
|
||||
if (opt_anchor) {
|
||||
this.targetCenter_ = this.calculateCenterRotate(newRotation, opt_anchor);
|
||||
}
|
||||
this.targetRotation_ += delta;
|
||||
@@ -1373,7 +1397,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);
|
||||
|
||||
@@ -1390,6 +1414,7 @@ class View extends BaseObject {
|
||||
if (this.getAnimating() && !opt_doNotCancelAnims) {
|
||||
this.cancelAnimations();
|
||||
}
|
||||
this.cancelAnchor_ = undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1406,11 +1431,11 @@ 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);
|
||||
|
||||
if (duration === 0) {
|
||||
if (duration === 0 && !this.cancelAnchor_) {
|
||||
this.targetResolution_ = newResolution;
|
||||
this.targetRotation_ = newRotation;
|
||||
this.targetCenter_ = newCenter;
|
||||
@@ -1418,6 +1443,9 @@ class View extends BaseObject {
|
||||
return;
|
||||
}
|
||||
|
||||
const anchor = opt_anchor || (duration === 0 ? this.cancelAnchor_ : undefined);
|
||||
this.cancelAnchor_ = undefined;
|
||||
|
||||
if (this.getResolution() !== newResolution ||
|
||||
this.getRotation() !== newRotation ||
|
||||
!this.getCenterInternal() ||
|
||||
@@ -1433,7 +1461,7 @@ class View extends BaseObject {
|
||||
resolution: newResolution,
|
||||
duration: duration,
|
||||
easing: easeOut,
|
||||
anchor: opt_anchor
|
||||
anchor: anchor
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1484,7 +1512,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);
|
||||
}
|
||||
|
||||
@@ -1513,7 +1541,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);
|
||||
}
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
/**
|
||||
* @module ol/control/MousePosition
|
||||
*/
|
||||
|
||||
import 'elm-pep';
|
||||
import {listen} from '../events.js';
|
||||
import EventType from '../pointer/EventType.js';
|
||||
import {getChangeEventType} from '../Object.js';
|
||||
import Control from './Control.js';
|
||||
import {getTransformFromProjections, identityTransform, get as getProjection, getUserProjection} from '../proj.js';
|
||||
import '@openlayers/pepjs';
|
||||
|
||||
|
||||
/**
|
||||
|
||||
@@ -304,6 +304,8 @@ class OverviewMap extends Control {
|
||||
*/
|
||||
bindView_(view) {
|
||||
view.addEventListener(getChangeEventType(ViewProperty.ROTATION), this.boundHandleRotationChanged_);
|
||||
// Sync once with the new view
|
||||
this.handleRotationChanged_();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
/**
|
||||
* @module ol/control/ZoomSlider
|
||||
*/
|
||||
|
||||
import 'elm-pep';
|
||||
import Control from './Control.js';
|
||||
import {CLASS_CONTROL, CLASS_UNSELECTABLE} from '../css.js';
|
||||
import {easeOut} from '../easing.js';
|
||||
@@ -9,7 +11,6 @@ import {stopPropagation} from '../events/Event.js';
|
||||
import EventType from '../events/EventType.js';
|
||||
import {clamp} from '../math.js';
|
||||
import PointerEventType from '../pointer/EventType.js';
|
||||
import '@openlayers/pepjs';
|
||||
|
||||
|
||||
/**
|
||||
@@ -135,7 +136,6 @@ class ZoomSlider extends Control {
|
||||
thumbElement.setAttribute('type', 'button');
|
||||
thumbElement.className = className + '-thumb ' + CLASS_UNSELECTABLE;
|
||||
const containerElement = this.element;
|
||||
containerElement.setAttribute('touch-action', 'none');
|
||||
containerElement.className = className + ' ' + CLASS_UNSELECTABLE + ' ' + CLASS_CONTROL;
|
||||
containerElement.appendChild(thumbElement);
|
||||
|
||||
|
||||
+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;
|
||||
}
|
||||
|
||||
@@ -34,5 +34,6 @@ export default {
|
||||
KEYPRESS: 'keypress',
|
||||
LOAD: 'load',
|
||||
RESIZE: 'resize',
|
||||
TOUCHMOVE: 'touchmove',
|
||||
WHEEL: 'wheel'
|
||||
};
|
||||
|
||||
@@ -207,12 +207,9 @@ export const shiftKeyOnly = function(mapBrowserEvent) {
|
||||
* @api
|
||||
*/
|
||||
export const targetNotEditable = function(mapBrowserEvent) {
|
||||
const target = mapBrowserEvent.target;
|
||||
const tagName = /** @type {Element} */ (target).tagName;
|
||||
return (
|
||||
tagName !== 'INPUT' &&
|
||||
tagName !== 'SELECT' &&
|
||||
tagName !== 'TEXTAREA');
|
||||
const originalEvent = /** @type {KeyboardEvent|MouseEvent|TouchEvent} */ (mapBrowserEvent.originalEvent);
|
||||
const tagName = /** @type {Element} */ (originalEvent.target).tagName;
|
||||
return tagName !== 'INPUT' && tagName !== 'SELECT' && tagName !== 'TEXTAREA';
|
||||
};
|
||||
|
||||
|
||||
|
||||
+21
-8
@@ -386,6 +386,8 @@ function createStyleDefaults() {
|
||||
* @property {Array<Style>} [defaultStyle] Default style. The
|
||||
* default default style is the same as Google Earth.
|
||||
* @property {boolean} [writeStyles=true] Write styles into KML.
|
||||
* @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.
|
||||
*/
|
||||
|
||||
|
||||
@@ -458,6 +460,13 @@ class KML extends XMLFeature {
|
||||
this.showPointNames_ = options.showPointNames !== undefined ?
|
||||
options.showPointNames : true;
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @type {null|string}
|
||||
*/
|
||||
this.crossOrigin_ = options.crossOrigin !== undefined ?
|
||||
options.crossOrigin : 'anonymous';
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -494,7 +503,7 @@ class KML extends XMLFeature {
|
||||
*/
|
||||
readPlacemark_(node, objectStack) {
|
||||
const object = pushParseAndPop({'geometry': null},
|
||||
PLACEMARK_PARSERS, node, objectStack);
|
||||
PLACEMARK_PARSERS, node, objectStack, this);
|
||||
if (!object) {
|
||||
return undefined;
|
||||
}
|
||||
@@ -537,7 +546,7 @@ class KML extends XMLFeature {
|
||||
readSharedStyle_(node, objectStack) {
|
||||
const id = node.getAttribute('id');
|
||||
if (id !== null) {
|
||||
const style = readStyle(node, objectStack);
|
||||
const style = readStyle.call(this, node, objectStack);
|
||||
if (style) {
|
||||
let styleUri;
|
||||
let baseURI = node.baseURI;
|
||||
@@ -565,7 +574,7 @@ class KML extends XMLFeature {
|
||||
if (id === null) {
|
||||
return;
|
||||
}
|
||||
const styleMapValue = readStyleMapValue(node, objectStack);
|
||||
const styleMapValue = readStyleMapValue.call(this, node, objectStack);
|
||||
if (!styleMapValue) {
|
||||
return;
|
||||
}
|
||||
@@ -1112,13 +1121,14 @@ const STYLE_MAP_PARSERS = makeStructureNS(
|
||||
|
||||
|
||||
/**
|
||||
* @this {KML}
|
||||
* @param {Element} node Node.
|
||||
* @param {Array<*>} objectStack Object stack.
|
||||
* @return {Array<Style>|string|undefined} StyleMap.
|
||||
*/
|
||||
function readStyleMapValue(node, objectStack) {
|
||||
return pushParseAndPop(undefined,
|
||||
STYLE_MAP_PARSERS, node, objectStack);
|
||||
STYLE_MAP_PARSERS, node, objectStack, this);
|
||||
}
|
||||
|
||||
|
||||
@@ -1137,6 +1147,7 @@ const ICON_STYLE_PARSERS = makeStructureNS(
|
||||
|
||||
|
||||
/**
|
||||
* @this {KML}
|
||||
* @param {Element} node Node.
|
||||
* @param {Array<*>} objectStack Object stack.
|
||||
*/
|
||||
@@ -1223,7 +1234,7 @@ function iconStyleParser(node, objectStack) {
|
||||
anchorOrigin: anchorOrigin,
|
||||
anchorXUnits: anchorXUnits,
|
||||
anchorYUnits: anchorYUnits,
|
||||
crossOrigin: 'anonymous', // FIXME should this be configurable?
|
||||
crossOrigin: this.crossOrigin_,
|
||||
offset: offset,
|
||||
offsetOrigin: IconOrigin.BOTTOM_LEFT,
|
||||
rotation: rotation,
|
||||
@@ -1719,13 +1730,14 @@ const STYLE_PARSERS = makeStructureNS(
|
||||
|
||||
|
||||
/**
|
||||
* @this {KML}
|
||||
* @param {Element} node Node.
|
||||
* @param {Array<*>} objectStack Object stack.
|
||||
* @return {Array<Style>} Style.
|
||||
*/
|
||||
function readStyle(node, objectStack) {
|
||||
const styleObject = pushParseAndPop(
|
||||
{}, STYLE_PARSERS, node, objectStack);
|
||||
{}, STYLE_PARSERS, node, objectStack, this);
|
||||
if (!styleObject) {
|
||||
return null;
|
||||
}
|
||||
@@ -1885,7 +1897,7 @@ const PAIR_PARSERS = makeStructureNS(
|
||||
*/
|
||||
function pairDataParser(node, objectStack) {
|
||||
const pairObject = pushParseAndPop(
|
||||
{}, PAIR_PARSERS, node, objectStack);
|
||||
{}, PAIR_PARSERS, node, objectStack, this);
|
||||
if (!pairObject) {
|
||||
return;
|
||||
}
|
||||
@@ -1907,11 +1919,12 @@ function pairDataParser(node, objectStack) {
|
||||
|
||||
|
||||
/**
|
||||
* @this {KML}
|
||||
* @param {Element} node Node.
|
||||
* @param {Array<*>} objectStack Object stack.
|
||||
*/
|
||||
function placemarkStyleMapParser(node, objectStack) {
|
||||
const styleMapValue = readStyleMapValue(node, objectStack);
|
||||
const styleMapValue = readStyleMapValue.call(this, node, objectStack);
|
||||
if (!styleMapValue) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
@@ -44,3 +44,23 @@ export const DEVICE_PIXEL_RATIO = window.devicePixelRatio || 1;
|
||||
* @type {boolean}
|
||||
*/
|
||||
export const IMAGE_DECODE = typeof Image !== 'undefined' && Image.prototype.decode;
|
||||
|
||||
/**
|
||||
* @type {boolean}
|
||||
*/
|
||||
export const PASSIVE_EVENT_LISTENERS = (function() {
|
||||
let passive = false;
|
||||
try {
|
||||
const options = Object.defineProperty({}, 'passive', {
|
||||
get: function() {
|
||||
passive = true;
|
||||
}
|
||||
});
|
||||
|
||||
window.addEventListener('_', null, options);
|
||||
window.removeEventListener('_', null, options);
|
||||
} catch (error) {
|
||||
// passive not supported
|
||||
}
|
||||
return passive;
|
||||
})();
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
*/
|
||||
// FIXME draw drag box
|
||||
import Event from '../events/Event.js';
|
||||
import {always, mouseOnly, mouseActionButton} from '../events/condition.js';
|
||||
import {mouseActionButton} from '../events/condition.js';
|
||||
import {VOID} from '../functions.js';
|
||||
import PointerInteraction from './Pointer.js';
|
||||
import RenderBox from '../render/Box.js';
|
||||
@@ -22,7 +22,7 @@ import RenderBox from '../render/Box.js';
|
||||
* @property {string} [className='ol-dragbox'] CSS class name for styling the box.
|
||||
* @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 ol/events/condition~always}.
|
||||
* Default is {@link ol/events/condition~mouseActionButton}.
|
||||
* @property {number} [minArea=64] The minimum area of the box in pixel, this value is used by the default
|
||||
* `boxEndCondition` function.
|
||||
* @property {EndCondition} [boxEndCondition] A function that takes a {@link module:ol/MapBrowserEvent~MapBrowserEvent} and two
|
||||
@@ -104,8 +104,6 @@ class DragBoxEvent extends Event {
|
||||
* (see {@link module:ol/interaction/DragZoom~DragZoom} and
|
||||
* {@link module:ol/interaction/DragRotateAndZoom}).
|
||||
*
|
||||
* This interaction is only supported for mouse devices.
|
||||
*
|
||||
* @fires DragBoxEvent
|
||||
* @api
|
||||
*/
|
||||
@@ -148,7 +146,7 @@ class DragBox extends PointerInteraction {
|
||||
* @private
|
||||
* @type {import("../events/condition.js").Condition}
|
||||
*/
|
||||
this.condition_ = options.condition ? options.condition : always;
|
||||
this.condition_ = options.condition ? options.condition : mouseActionButton;
|
||||
|
||||
/**
|
||||
* @private
|
||||
@@ -186,10 +184,6 @@ class DragBox extends PointerInteraction {
|
||||
* @inheritDoc
|
||||
*/
|
||||
handleDragEvent(mapBrowserEvent) {
|
||||
if (!mouseOnly(mapBrowserEvent)) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.box_.setPixels(this.startPixel_, mapBrowserEvent.pixel);
|
||||
|
||||
this.dispatchEvent(new DragBoxEvent(DragBoxEventType.BOXDRAG,
|
||||
@@ -200,10 +194,6 @@ class DragBox extends PointerInteraction {
|
||||
* @inheritDoc
|
||||
*/
|
||||
handleUpEvent(mapBrowserEvent) {
|
||||
if (!mouseOnly(mapBrowserEvent)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
this.box_.setMap(null);
|
||||
|
||||
if (this.boxEndCondition_(mapBrowserEvent, this.startPixel_, mapBrowserEvent.pixel)) {
|
||||
@@ -218,12 +208,7 @@ class DragBox extends PointerInteraction {
|
||||
* @inheritDoc
|
||||
*/
|
||||
handleDownEvent(mapBrowserEvent) {
|
||||
if (!mouseOnly(mapBrowserEvent)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (mouseActionButton(mapBrowserEvent) &&
|
||||
this.condition_(mapBrowserEvent)) {
|
||||
if (this.condition_(mapBrowserEvent)) {
|
||||
this.startPixel_ = mapBrowserEvent.pixel;
|
||||
this.box_.setMap(mapBrowserEvent.map);
|
||||
this.box_.setPixels(this.startPixel_, this.startPixel_);
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
*/
|
||||
import {scale as scaleCoordinate, rotate as rotateCoordinate} from '../coordinate.js';
|
||||
import {easeOut} from '../easing.js';
|
||||
import {noModifierKeys, primaryAction} from '../events/condition.js';
|
||||
import {noModifierKeys, primaryAction, focus} from '../events/condition.js';
|
||||
import {FALSE} from '../functions.js';
|
||||
import PointerInteraction, {centroid as centroidFromPointers} from './Pointer.js';
|
||||
|
||||
@@ -69,6 +69,20 @@ class DragPan extends PointerInteraction {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @param {import("../MapBrowserEvent").default} mapBrowserEvent Event.
|
||||
* @return {boolean} Condition passes.
|
||||
*/
|
||||
conditionInternal_(mapBrowserEvent) {
|
||||
let pass = true;
|
||||
if (mapBrowserEvent.map.getTargetElement().hasAttribute('tabindex')) {
|
||||
pass = focus(mapBrowserEvent);
|
||||
}
|
||||
return pass && this.condition_(mapBrowserEvent);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
@@ -101,6 +115,7 @@ class DragPan extends PointerInteraction {
|
||||
}
|
||||
this.lastCentroid = centroid;
|
||||
this.lastPointersCount_ = targetPointers.length;
|
||||
mapBrowserEvent.originalEvent.preventDefault();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -145,7 +160,7 @@ class DragPan extends PointerInteraction {
|
||||
* @inheritDoc
|
||||
*/
|
||||
handleDownEvent(mapBrowserEvent) {
|
||||
if (this.targetPointers.length > 0 && this.condition_(mapBrowserEvent)) {
|
||||
if (this.targetPointers.length > 0 && this.conditionInternal_(mapBrowserEvent)) {
|
||||
const map = mapBrowserEvent.map;
|
||||
const view = map.getView();
|
||||
this.lastCentroid = null;
|
||||
|
||||
+50
-28
@@ -24,6 +24,7 @@ import InteractionProperty from './Property.js';
|
||||
import VectorLayer from '../layer/Vector.js';
|
||||
import VectorSource from '../source/Vector.js';
|
||||
import {createEditingStyle} from '../style/Style.js';
|
||||
import {fromUserCoordinate, getUserProjection} from '../proj.js';
|
||||
|
||||
|
||||
/**
|
||||
@@ -104,11 +105,12 @@ import {createEditingStyle} from '../style/Style.js';
|
||||
|
||||
|
||||
/**
|
||||
* Function that takes an array of coordinates and an optional existing geometry as
|
||||
* arguments, and returns a geometry. The optional existing geometry is the
|
||||
* geometry that is returned when the function is called without a second
|
||||
* argument.
|
||||
* @typedef {function(!SketchCoordType, import("../geom/SimpleGeometry.js").default=):
|
||||
* Function that takes an array of coordinates and an optional existing geometry
|
||||
* and a projection as arguments, and returns a geometry. The optional existing
|
||||
* geometry is the geometry that is returned when the function is called without
|
||||
* a second argument.
|
||||
* @typedef {function(!SketchCoordType, import("../geom/SimpleGeometry.js").default=,
|
||||
* import("../proj/Projection.js").default):
|
||||
* import("../geom/SimpleGeometry.js").default} GeometryFunction
|
||||
*/
|
||||
|
||||
@@ -296,14 +298,20 @@ class Draw extends PointerInteraction {
|
||||
/**
|
||||
* @param {!LineCoordType} coordinates The coordinates.
|
||||
* @param {import("../geom/SimpleGeometry.js").default=} opt_geometry Optional geometry.
|
||||
* @param {import("../proj/Projection.js").default} projection The view projection.
|
||||
* @return {import("../geom/SimpleGeometry.js").default} A geometry.
|
||||
*/
|
||||
geometryFunction = function(coordinates, opt_geometry) {
|
||||
geometryFunction = function(coordinates, opt_geometry, projection) {
|
||||
const circle = opt_geometry ? /** @type {Circle} */ (opt_geometry) :
|
||||
new Circle([NaN, NaN]);
|
||||
const center = fromUserCoordinate(coordinates[0], projection);
|
||||
const squaredLength = squaredCoordinateDistance(
|
||||
coordinates[0], coordinates[1]);
|
||||
circle.setCenterAndRadius(coordinates[0], Math.sqrt(squaredLength));
|
||||
center, fromUserCoordinate(coordinates[1], projection));
|
||||
circle.setCenterAndRadius(center, Math.sqrt(squaredLength));
|
||||
const userProjection = getUserProjection();
|
||||
if (userProjection) {
|
||||
circle.transform(projection, userProjection);
|
||||
}
|
||||
return circle;
|
||||
};
|
||||
} else {
|
||||
@@ -319,9 +327,10 @@ class Draw extends PointerInteraction {
|
||||
/**
|
||||
* @param {!LineCoordType} coordinates The coordinates.
|
||||
* @param {import("../geom/SimpleGeometry.js").default=} opt_geometry Optional geometry.
|
||||
* @param {import("../proj/Projection.js").default} projection The view projection.
|
||||
* @return {import("../geom/SimpleGeometry.js").default} A geometry.
|
||||
*/
|
||||
geometryFunction = function(coordinates, opt_geometry) {
|
||||
geometryFunction = function(coordinates, opt_geometry, projection) {
|
||||
let geometry = opt_geometry;
|
||||
if (geometry) {
|
||||
if (mode === Mode.POLYGON) {
|
||||
@@ -675,6 +684,7 @@ class Draw extends PointerInteraction {
|
||||
*/
|
||||
startDrawing_(event) {
|
||||
const start = event.coordinate;
|
||||
const projection = event.map.getView().getProjection();
|
||||
this.finishCoordinate_ = start;
|
||||
if (this.mode_ === Mode.POINT) {
|
||||
this.sketchCoords_ = start.slice();
|
||||
@@ -688,7 +698,7 @@ class Draw extends PointerInteraction {
|
||||
this.sketchLine_ = new Feature(
|
||||
new LineString(this.sketchLineCoords_));
|
||||
}
|
||||
const geometry = this.geometryFunction_(this.sketchCoords_);
|
||||
const geometry = this.geometryFunction_(this.sketchCoords_, undefined, projection);
|
||||
this.sketchFeature_ = new Feature();
|
||||
if (this.geometryName_) {
|
||||
this.sketchFeature_.setGeometryName(this.geometryName_);
|
||||
@@ -706,6 +716,7 @@ class Draw extends PointerInteraction {
|
||||
modifyDrawing_(event) {
|
||||
let coordinate = event.coordinate;
|
||||
const geometry = this.sketchFeature_.getGeometry();
|
||||
const projection = event.map.getView().getProjection();
|
||||
let coordinates, last;
|
||||
if (this.mode_ === Mode.POINT) {
|
||||
last = this.sketchCoords_;
|
||||
@@ -722,7 +733,7 @@ class Draw extends PointerInteraction {
|
||||
}
|
||||
last[0] = coordinate[0];
|
||||
last[1] = coordinate[1];
|
||||
this.geometryFunction_(/** @type {!LineCoordType} */ (this.sketchCoords_), geometry);
|
||||
this.geometryFunction_(/** @type {!LineCoordType} */ (this.sketchCoords_), geometry, projection);
|
||||
if (this.sketchPoint_) {
|
||||
const sketchPointGeom = this.sketchPoint_.getGeometry();
|
||||
sketchPointGeom.setCoordinates(coordinate);
|
||||
@@ -759,6 +770,7 @@ class Draw extends PointerInteraction {
|
||||
addToDrawing_(event) {
|
||||
const coordinate = event.coordinate;
|
||||
const geometry = this.sketchFeature_.getGeometry();
|
||||
const projection = event.map.getView().getProjection();
|
||||
let done;
|
||||
let coordinates;
|
||||
if (this.mode_ === Mode.LINE_STRING) {
|
||||
@@ -772,7 +784,7 @@ class Draw extends PointerInteraction {
|
||||
}
|
||||
}
|
||||
coordinates.push(coordinate.slice());
|
||||
this.geometryFunction_(coordinates, geometry);
|
||||
this.geometryFunction_(coordinates, geometry, projection);
|
||||
} else if (this.mode_ === Mode.POLYGON) {
|
||||
coordinates = /** @type {PolyCoordType} */ (this.sketchCoords_)[0];
|
||||
if (coordinates.length >= this.maxPoints_) {
|
||||
@@ -786,7 +798,7 @@ class Draw extends PointerInteraction {
|
||||
if (done) {
|
||||
this.finishCoordinate_ = coordinates[0];
|
||||
}
|
||||
this.geometryFunction_(this.sketchCoords_, geometry);
|
||||
this.geometryFunction_(this.sketchCoords_, geometry, projection);
|
||||
}
|
||||
this.updateSketchFeatures_();
|
||||
if (done) {
|
||||
@@ -803,13 +815,14 @@ class Draw extends PointerInteraction {
|
||||
return;
|
||||
}
|
||||
const geometry = this.sketchFeature_.getGeometry();
|
||||
const projection = this.getMap().getView().getProjection();
|
||||
let coordinates;
|
||||
/** @type {LineString} */
|
||||
let sketchLineGeom;
|
||||
if (this.mode_ === Mode.LINE_STRING) {
|
||||
coordinates = /** @type {LineCoordType} */ (this.sketchCoords_);
|
||||
coordinates.splice(-2, 1);
|
||||
this.geometryFunction_(coordinates, geometry);
|
||||
this.geometryFunction_(coordinates, geometry, projection);
|
||||
if (coordinates.length >= 2) {
|
||||
this.finishCoordinate_ = coordinates[coordinates.length - 2].slice();
|
||||
}
|
||||
@@ -818,7 +831,7 @@ class Draw extends PointerInteraction {
|
||||
coordinates.splice(-2, 1);
|
||||
sketchLineGeom = this.sketchLine_.getGeometry();
|
||||
sketchLineGeom.setCoordinates(coordinates);
|
||||
this.geometryFunction_(this.sketchCoords_, geometry);
|
||||
this.geometryFunction_(this.sketchCoords_, geometry, projection);
|
||||
}
|
||||
|
||||
if (coordinates.length === 0) {
|
||||
@@ -841,14 +854,15 @@ class Draw extends PointerInteraction {
|
||||
}
|
||||
let coordinates = this.sketchCoords_;
|
||||
const geometry = sketchFeature.getGeometry();
|
||||
const projection = this.getMap().getView().getProjection();
|
||||
if (this.mode_ === Mode.LINE_STRING) {
|
||||
// remove the redundant last point
|
||||
coordinates.pop();
|
||||
this.geometryFunction_(coordinates, geometry);
|
||||
this.geometryFunction_(coordinates, geometry, projection);
|
||||
} else if (this.mode_ === Mode.POLYGON) {
|
||||
// remove the redundant last point in ring
|
||||
/** @type {PolyCoordType} */ (coordinates)[0].pop();
|
||||
this.geometryFunction_(coordinates, geometry);
|
||||
this.geometryFunction_(coordinates, geometry, projection);
|
||||
coordinates = geometry.getCoordinates();
|
||||
}
|
||||
|
||||
@@ -881,12 +895,10 @@ class Draw extends PointerInteraction {
|
||||
abortDrawing_() {
|
||||
this.finishCoordinate_ = null;
|
||||
const sketchFeature = this.sketchFeature_;
|
||||
if (sketchFeature) {
|
||||
this.sketchFeature_ = null;
|
||||
this.sketchPoint_ = null;
|
||||
this.sketchLine_ = null;
|
||||
this.overlay_.getSource().clear(true);
|
||||
}
|
||||
this.sketchFeature_ = null;
|
||||
this.sketchPoint_ = null;
|
||||
this.sketchLine_ = null;
|
||||
this.overlay_.getSource().clear(true);
|
||||
return sketchFeature;
|
||||
}
|
||||
|
||||
@@ -968,9 +980,9 @@ function getDefaultStyleFunction() {
|
||||
* @api
|
||||
*/
|
||||
export function createRegularPolygon(opt_sides, opt_angle) {
|
||||
return function(coordinates, opt_geometry) {
|
||||
const center = /** @type {LineCoordType} */ (coordinates)[0];
|
||||
const end = /** @type {LineCoordType} */ (coordinates)[1];
|
||||
return function(coordinates, opt_geometry, projection) {
|
||||
const center = fromUserCoordinate(/** @type {LineCoordType} */ (coordinates)[0], projection);
|
||||
const end = fromUserCoordinate(/** @type {LineCoordType} */ (coordinates)[1], projection);
|
||||
const radius = Math.sqrt(
|
||||
squaredCoordinateDistance(center, end));
|
||||
const geometry = opt_geometry ? /** @type {Polygon} */ (opt_geometry) :
|
||||
@@ -982,6 +994,10 @@ export function createRegularPolygon(opt_sides, opt_angle) {
|
||||
angle = Math.atan(y / x) - (x < 0 ? Math.PI : 0);
|
||||
}
|
||||
makeRegular(geometry, center, radius, angle);
|
||||
const userProjection = getUserProjection();
|
||||
if (userProjection) {
|
||||
geometry.transform(projection, userProjection);
|
||||
}
|
||||
return geometry;
|
||||
};
|
||||
}
|
||||
@@ -996,8 +1012,10 @@ export function createRegularPolygon(opt_sides, opt_angle) {
|
||||
*/
|
||||
export function createBox() {
|
||||
return (
|
||||
function(coordinates, opt_geometry) {
|
||||
const extent = boundingExtent(/** @type {LineCoordType} */ (coordinates));
|
||||
function(coordinates, opt_geometry, projection) {
|
||||
const extent = boundingExtent(/** @type {LineCoordType} */ (coordinates).map(function(coordinate) {
|
||||
return fromUserCoordinate(coordinate, projection);
|
||||
}));
|
||||
const boxCoordinates = [[
|
||||
getBottomLeft(extent),
|
||||
getBottomRight(extent),
|
||||
@@ -1011,6 +1029,10 @@ export function createBox() {
|
||||
} else {
|
||||
geometry = new Polygon(boxCoordinates);
|
||||
}
|
||||
const userProjection = getUserProjection();
|
||||
if (userProjection) {
|
||||
geometry.transform(projection, userProjection);
|
||||
}
|
||||
return geometry;
|
||||
}
|
||||
);
|
||||
|
||||
@@ -807,8 +807,8 @@ class Modify extends PointerInteraction {
|
||||
if (!this.condition_(evt)) {
|
||||
return false;
|
||||
}
|
||||
this.handlePointerAtPixel_(evt.pixel, evt.map);
|
||||
const pixelCoordinate = evt.coordinate;
|
||||
this.handlePointerAtPixel_(evt.pixel, evt.map, pixelCoordinate);
|
||||
this.dragSegments_.length = 0;
|
||||
this.modified_ = false;
|
||||
const vertexFeature = this.vertexFeature_;
|
||||
@@ -916,16 +916,17 @@ class Modify extends PointerInteraction {
|
||||
*/
|
||||
handlePointerMove_(evt) {
|
||||
this.lastPixel_ = evt.pixel;
|
||||
this.handlePointerAtPixel_(evt.pixel, evt.map);
|
||||
this.handlePointerAtPixel_(evt.pixel, evt.map, evt.coordinate);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {import("../pixel.js").Pixel} pixel Pixel
|
||||
* @param {import("../PluggableMap.js").default} map Map.
|
||||
* @param {import("../coordinate.js").Coordinate=} opt_coordinate The pixel Coordinate.
|
||||
* @private
|
||||
*/
|
||||
handlePointerAtPixel_(pixel, map) {
|
||||
const pixelCoordinate = map.getCoordinateFromPixel(pixel);
|
||||
handlePointerAtPixel_(pixel, map, opt_coordinate) {
|
||||
const pixelCoordinate = opt_coordinate || map.getCoordinateFromPixel(pixel);
|
||||
const projection = map.getView().getProjection();
|
||||
const sortByDistance = function(a, b) {
|
||||
return projectedDistanceToSegmentDataSquared(pixelCoordinate, a, projection) -
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/**
|
||||
* @module ol/interaction/MouseWheelZoom
|
||||
*/
|
||||
import {always} from '../events/condition.js';
|
||||
import {always, focus} from '../events/condition.js';
|
||||
import EventType from '../events/EventType.js';
|
||||
import {DEVICE_PIXEL_RATIO, FIREFOX} from '../has.js';
|
||||
import Interaction, {zoomByDelta} from './Interaction.js';
|
||||
@@ -134,13 +134,27 @@ class MouseWheelZoom extends Interaction {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @param {import("../MapBrowserEvent").default} mapBrowserEvent Event.
|
||||
* @return {boolean} Condition passes.
|
||||
*/
|
||||
conditionInternal_(mapBrowserEvent) {
|
||||
let pass = true;
|
||||
if (mapBrowserEvent.map.getTargetElement().hasAttribute('tabindex')) {
|
||||
pass = focus(mapBrowserEvent);
|
||||
}
|
||||
return pass && this.condition_(mapBrowserEvent);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
endInteraction_() {
|
||||
this.trackpadTimeoutId_ = undefined;
|
||||
const view = this.getMap().getView();
|
||||
view.endInteraction(undefined, Math.sign(this.lastDelta_), this.lastAnchor_);
|
||||
view.endInteraction(undefined, this.lastDelta_ ? (this.lastDelta_ > 0 ? 1 : -1) : 0, this.lastAnchor_);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -149,7 +163,7 @@ class MouseWheelZoom extends Interaction {
|
||||
* @override
|
||||
*/
|
||||
handleEvent(mapBrowserEvent) {
|
||||
if (!this.condition_(mapBrowserEvent)) {
|
||||
if (!this.conditionInternal_(mapBrowserEvent)) {
|
||||
return true;
|
||||
}
|
||||
const type = mapBrowserEvent.type;
|
||||
|
||||
@@ -95,6 +95,16 @@ class PointerInteraction extends Interaction {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current number of pointers involved in the interaction,
|
||||
* e.g. `2` when two fingers are used.
|
||||
* @return {number} The number of pointers.
|
||||
* @api
|
||||
*/
|
||||
getPointerCount() {
|
||||
return this.targetPointers.length;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle pointer down events.
|
||||
* @param {import("../MapBrowserPointerEvent.js").default} mapBrowserEvent Event.
|
||||
@@ -136,9 +146,6 @@ class PointerInteraction extends Interaction {
|
||||
} else {
|
||||
if (mapBrowserEvent.type == MapBrowserEventType.POINTERDOWN) {
|
||||
const handled = this.handleDownEvent(mapBrowserEvent);
|
||||
if (handled) {
|
||||
mapBrowserEvent.preventDefault();
|
||||
}
|
||||
this.handlingDownUpSequence = handled;
|
||||
stopEvent = this.stopDown(handled);
|
||||
} else if (mapBrowserEvent.type == MapBrowserEventType.POINTERMOVE) {
|
||||
|
||||
@@ -294,7 +294,9 @@ class Heatmap extends VectorLayer {
|
||||
gl_FragColor.rgb *= gl_FragColor.a;
|
||||
}`,
|
||||
uniforms: {
|
||||
u_gradientTexture: this.gradient_
|
||||
u_gradientTexture: function() {
|
||||
return this.gradient_;
|
||||
}.bind(this)
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
@@ -40,7 +40,9 @@ import {assign} from '../obj.js';
|
||||
* * `'hybrid'`: Polygon and line elements are rendered as images, so pixels are scaled during zoom
|
||||
* animations. Point symbols and texts are accurately rendered as vectors and can stay upright on
|
||||
* rotated views.
|
||||
*
|
||||
* * `'vector'`: Everything is rendered as vectors. Use this mode for improved performance on vector
|
||||
* tile layers with only a few rendered features (e.g. for highlighting a subset of features of
|
||||
* another layer with the same source).
|
||||
* @property {import("../source/VectorTile.js").default} [source] Source.
|
||||
* @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
|
||||
@@ -92,8 +94,9 @@ class VectorTileLayer extends BaseVectorLayer {
|
||||
const renderMode = options.renderMode || VectorTileRenderType.HYBRID;
|
||||
assert(renderMode == undefined ||
|
||||
renderMode == VectorTileRenderType.IMAGE ||
|
||||
renderMode == VectorTileRenderType.HYBRID,
|
||||
28); // `renderMode` must be `'image'` or `'hybrid'`
|
||||
renderMode == VectorTileRenderType.HYBRID ||
|
||||
renderMode == VectorTileRenderType.VECTOR,
|
||||
28); // `renderMode` must be `'image'`, `'hybrid'` or `'vector'`.
|
||||
|
||||
/**
|
||||
* @private
|
||||
|
||||
@@ -11,9 +11,14 @@
|
||||
* * `'hybrid'`: Polygon and line elements are rendered as images, so pixels
|
||||
* are scaled during zoom animations. Point symbols and texts are accurately
|
||||
* rendered as vectors and can stay upright on rotated views.
|
||||
* * `'vector'`: Everything is rendered as vectors. Use this mode for improved
|
||||
* performance on vector tile layers with only a few rendered features (e.g.
|
||||
* for highlighting a subset of features of another layer with the same
|
||||
* source).
|
||||
* @api
|
||||
*/
|
||||
export default {
|
||||
IMAGE: 'image',
|
||||
HYBRID: 'hybrid'
|
||||
HYBRID: 'hybrid',
|
||||
VECTOR: 'vector'
|
||||
};
|
||||
|
||||
@@ -24,7 +24,7 @@ import Layer from './Layer.js';
|
||||
* @property {number} [maxResolution] The maximum resolution (exclusive) below which this layer will
|
||||
* be visible.
|
||||
* @property {import("../source/Vector.js").default} [source] Source.
|
||||
* @property {boolean} [disableHitDetection] Setting this to true will provide a slight performance boost, but will
|
||||
* @property {boolean} [disableHitDetection=false] Setting this to true will provide a slight performance boost, but will
|
||||
* prevent all hit detection on the layer.
|
||||
*/
|
||||
|
||||
|
||||
@@ -74,6 +74,12 @@
|
||||
user-select: none;
|
||||
-webkit-tap-highlight-color: rgba(0,0,0,0);
|
||||
}
|
||||
.ol-overlaycontainer, .ol-overlaycontainer-stopevent {
|
||||
pointer-events: none;
|
||||
}
|
||||
.ol-overlaycontainer > *, .ol-overlaycontainer-stopevent > * {
|
||||
pointer-events: auto;
|
||||
}
|
||||
.ol-selectable {
|
||||
-webkit-touch-callout: default;
|
||||
-webkit-user-select: text;
|
||||
@@ -144,6 +150,9 @@
|
||||
border: none;
|
||||
padding: 0;
|
||||
}
|
||||
.ol-control button span {
|
||||
pointer-events: none;
|
||||
}
|
||||
.ol-zoom-extent button {
|
||||
line-height: 1.4em;
|
||||
}
|
||||
|
||||
+5
-17
@@ -216,15 +216,12 @@ export const checkFont = (function() {
|
||||
* @return {boolean} Font with style and weight is available
|
||||
*/
|
||||
function isAvailable(fontStyle, fontWeight, fontFamily) {
|
||||
const context = getMeasureContext();
|
||||
let available = true;
|
||||
for (let i = 0; i < len; ++i) {
|
||||
const referenceFont = referenceFonts[i];
|
||||
context.font = fontStyle + ' ' + fontWeight + ' ' + size + referenceFont;
|
||||
referenceWidth = context.measureText(text).width;
|
||||
referenceWidth = measureTextWidth(fontStyle + ' ' + fontWeight + ' ' + size + referenceFont, text);
|
||||
if (fontFamily != referenceFont) {
|
||||
context.font = fontStyle + ' ' + fontWeight + ' ' + size + fontFamily + ',' + referenceFont;
|
||||
const width = context.measureText(text).width;
|
||||
const width = measureTextWidth(fontStyle + ' ' + fontWeight + ' ' + size + fontFamily + ',' + referenceFont, text);
|
||||
// If width and referenceWidth are the same, then the fallback was used
|
||||
// instead of the font we wanted, so the font is not available.
|
||||
available = available && width != referenceWidth;
|
||||
@@ -284,17 +281,6 @@ export const checkFont = (function() {
|
||||
})();
|
||||
|
||||
|
||||
/**
|
||||
* @return {CanvasRenderingContext2D} Measure context.
|
||||
*/
|
||||
function getMeasureContext() {
|
||||
if (!measureContext) {
|
||||
measureContext = createCanvasContext2D(1, 1);
|
||||
}
|
||||
return measureContext;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param {string} font Font to use for measuring.
|
||||
* @return {import("../size.js").Size} Measurement.
|
||||
@@ -333,7 +319,9 @@ export const measureTextHeight = (function() {
|
||||
* @return {number} Width.
|
||||
*/
|
||||
export function measureTextWidth(font, text) {
|
||||
const measureContext = getMeasureContext();
|
||||
if (!measureContext) {
|
||||
measureContext = createCanvasContext2D(1, 1);
|
||||
}
|
||||
if (font != measureFont) {
|
||||
measureContext.font = font;
|
||||
measureFont = measureContext.font;
|
||||
|
||||
@@ -18,8 +18,7 @@ import {
|
||||
} from '../../transform.js';
|
||||
import {createCanvasContext2D} from '../../dom.js';
|
||||
import {labelCache, defaultTextAlign, measureTextHeight, measureAndCacheTextWidth, measureTextWidths} from '../canvas.js';
|
||||
import Disposable from '../../Disposable.js';
|
||||
import RBush from 'rbush';
|
||||
import RBush from 'rbush/rbush.js';
|
||||
|
||||
|
||||
/**
|
||||
@@ -52,7 +51,7 @@ const p3 = [];
|
||||
const p4 = [];
|
||||
|
||||
|
||||
class Executor extends Disposable {
|
||||
class Executor {
|
||||
/**
|
||||
* @param {number} resolution Resolution.
|
||||
* @param {number} pixelRatio Pixel ratio.
|
||||
@@ -60,7 +59,6 @@ class Executor extends Disposable {
|
||||
* @param {SerializableInstructions} instructions The serializable instructions
|
||||
*/
|
||||
constructor(resolution, pixelRatio, overlaps, instructions) {
|
||||
super();
|
||||
|
||||
/**
|
||||
* @protected
|
||||
@@ -156,15 +154,6 @@ class Executor extends Disposable {
|
||||
this.widths_ = {};
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
disposeInternal() {
|
||||
labelCache.release(this);
|
||||
super.disposeInternal();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param {string} text Text.
|
||||
* @param {string} textKey Text style key.
|
||||
|
||||
@@ -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,4 +1,3 @@
|
||||
import {getUid} from '../../util.js';
|
||||
import LRUCache from '../../structs/LRUCache.js';
|
||||
|
||||
/**
|
||||
@@ -11,59 +10,11 @@ import LRUCache from '../../structs/LRUCache.js';
|
||||
*/
|
||||
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:
|
||||
expireCache() {
|
||||
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];
|
||||
}
|
||||
this.pop();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {import("./Executor.js").default} consumer Label consumer.
|
||||
*/
|
||||
release(consumer) {
|
||||
delete this.consumers[getUid(consumer)];
|
||||
}
|
||||
}
|
||||
|
||||
export default LabelCache;
|
||||
|
||||
@@ -131,8 +131,6 @@ class CanvasTextBuilder extends CanvasBuilder {
|
||||
* @type {string}
|
||||
*/
|
||||
this.strokeKey_ = '';
|
||||
|
||||
labelCache.prune();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -140,6 +138,7 @@ class CanvasTextBuilder extends CanvasBuilder {
|
||||
*/
|
||||
finish() {
|
||||
const instructions = super.finish();
|
||||
labelCache.expireCache();
|
||||
instructions.textStates = this.textStates;
|
||||
instructions.fillStates = this.fillStates;
|
||||
instructions.strokeStates = this.strokeStates;
|
||||
@@ -461,7 +460,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,15 +33,18 @@ 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) / featureCount);
|
||||
const indexFactor = Math.ceil((256 * 256 * 256 - 1) / featureCount);
|
||||
const featuresByZIndex = {};
|
||||
for (let i = 0; i < featureCount; ++i) {
|
||||
const feature = features[i];
|
||||
for (let i = 1; i <= featureCount; ++i) {
|
||||
const feature = features[i - 1];
|
||||
const featureStyleFunction = feature.getStyleFunction() || styleFunction;
|
||||
if (!styleFunction) {
|
||||
continue;
|
||||
}
|
||||
let styles = featureStyleFunction(feature, resolution);
|
||||
if (!styles) {
|
||||
continue;
|
||||
}
|
||||
if (!Array.isArray(styles)) {
|
||||
styles = [styles];
|
||||
}
|
||||
@@ -138,9 +141,9 @@ 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) / features.length);
|
||||
if (i % indexFactor === 0) {
|
||||
resultFeatures.push(features[i / indexFactor]);
|
||||
const indexFactor = Math.ceil((256 * 256 * 256 - 1) / features.length);
|
||||
if (i && i % indexFactor === 0) {
|
||||
resultFeatures.push(features[i / indexFactor - 1]);
|
||||
}
|
||||
}
|
||||
return resultFeatures;
|
||||
|
||||
@@ -74,6 +74,7 @@ class CompositeMapRenderer extends MapRenderer {
|
||||
|
||||
disposeInternal() {
|
||||
unlistenByKey(this.labelCacheKey_);
|
||||
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
|
||||
|
||||
@@ -232,49 +232,45 @@ class CanvasVectorLayerRenderer extends CanvasLayerRenderer {
|
||||
getFeatures(pixel) {
|
||||
return new Promise(function(resolve, reject) {
|
||||
if (!this.hitDetectionImageData_ && !this.animatingOrInteracting_) {
|
||||
requestAnimationFrame(function() {
|
||||
const size = [this.context.canvas.width, this.context.canvas.height];
|
||||
apply(this.pixelTransform, size);
|
||||
const center = this.renderedCenter_;
|
||||
const resolution = this.renderedResolution_;
|
||||
const rotation = this.renderedRotation_;
|
||||
const projection = this.renderedProjection_;
|
||||
const extent = this.renderedExtent_;
|
||||
const layer = this.getLayer();
|
||||
const transforms = [];
|
||||
const width = size[0] / 2;
|
||||
const height = size[1] / 2;
|
||||
transforms.push(this.getRenderTransform(center, resolution, rotation, 0.5, width, height, 0).slice());
|
||||
const source = layer.getSource();
|
||||
const projectionExtent = projection.getExtent();
|
||||
if (source.getWrapX() && projection.canWrapX() && !containsExtent(projectionExtent, extent)) {
|
||||
let startX = extent[0];
|
||||
const worldWidth = getWidth(projectionExtent);
|
||||
let world = 0;
|
||||
let offsetX;
|
||||
while (startX < projectionExtent[0]) {
|
||||
--world;
|
||||
offsetX = worldWidth * world;
|
||||
transforms.push(this.getRenderTransform(center, resolution, rotation, 0.5, width, height, offsetX).slice());
|
||||
startX += worldWidth;
|
||||
}
|
||||
world = 0;
|
||||
startX = extent[2];
|
||||
while (startX > projectionExtent[2]) {
|
||||
++world;
|
||||
offsetX = worldWidth * world;
|
||||
transforms.push(this.getRenderTransform(center, resolution, rotation, 0.5, width, height, offsetX).slice());
|
||||
startX -= worldWidth;
|
||||
}
|
||||
const size = [this.context.canvas.width, this.context.canvas.height];
|
||||
apply(this.pixelTransform, size);
|
||||
const center = this.renderedCenter_;
|
||||
const resolution = this.renderedResolution_;
|
||||
const rotation = this.renderedRotation_;
|
||||
const projection = this.renderedProjection_;
|
||||
const extent = this.renderedExtent_;
|
||||
const layer = this.getLayer();
|
||||
const transforms = [];
|
||||
const width = size[0] / 2;
|
||||
const height = size[1] / 2;
|
||||
transforms.push(this.getRenderTransform(center, resolution, rotation, 0.5, width, height, 0).slice());
|
||||
const source = layer.getSource();
|
||||
const projectionExtent = projection.getExtent();
|
||||
if (source.getWrapX() && projection.canWrapX() && !containsExtent(projectionExtent, extent)) {
|
||||
let startX = extent[0];
|
||||
const worldWidth = getWidth(projectionExtent);
|
||||
let world = 0;
|
||||
let offsetX;
|
||||
while (startX < projectionExtent[0]) {
|
||||
--world;
|
||||
offsetX = worldWidth * world;
|
||||
transforms.push(this.getRenderTransform(center, resolution, rotation, 0.5, width, height, offsetX).slice());
|
||||
startX += worldWidth;
|
||||
}
|
||||
world = 0;
|
||||
startX = extent[2];
|
||||
while (startX > projectionExtent[2]) {
|
||||
++world;
|
||||
offsetX = worldWidth * world;
|
||||
transforms.push(this.getRenderTransform(center, resolution, rotation, 0.5, width, height, offsetX).slice());
|
||||
startX -= worldWidth;
|
||||
}
|
||||
}
|
||||
|
||||
this.hitDetectionImageData_ = createHitDetectionImageData(size, transforms,
|
||||
this.renderedFeatures_, layer.getStyleFunction(), extent, resolution, rotation);
|
||||
resolve(hitDetect(pixel, this.renderedFeatures_, this.hitDetectionImageData_));
|
||||
}.bind(this));
|
||||
} else {
|
||||
resolve(hitDetect(pixel, this.renderedFeatures_, this.hitDetectionImageData_));
|
||||
this.hitDetectionImageData_ = createHitDetectionImageData(size, transforms,
|
||||
this.renderedFeatures_, layer.getStyleFunction(), extent, resolution, rotation);
|
||||
}
|
||||
resolve(hitDetect(pixel, this.renderedFeatures_, this.hitDetectionImageData_));
|
||||
}.bind(this));
|
||||
}
|
||||
|
||||
@@ -333,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];
|
||||
@@ -388,9 +387,6 @@ class CanvasVectorLayerRenderer extends CanvasLayerRenderer {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (this.replayGroup_) {
|
||||
this.replayGroup_.dispose();
|
||||
}
|
||||
this.replayGroup_ = null;
|
||||
|
||||
this.dirty_ = false;
|
||||
|
||||
@@ -33,7 +33,8 @@ import {createHitDetectionImageData, hitDetect} from '../../render/canvas/hitdet
|
||||
const IMAGE_REPLAYS = {
|
||||
'image': [ReplayType.POLYGON, ReplayType.CIRCLE,
|
||||
ReplayType.LINE_STRING, ReplayType.IMAGE, ReplayType.TEXT],
|
||||
'hybrid': [ReplayType.POLYGON, ReplayType.LINE_STRING]
|
||||
'hybrid': [ReplayType.POLYGON, ReplayType.LINE_STRING],
|
||||
'vector': []
|
||||
};
|
||||
|
||||
|
||||
@@ -42,7 +43,8 @@ const IMAGE_REPLAYS = {
|
||||
*/
|
||||
const VECTOR_REPLAYS = {
|
||||
'image': [ReplayType.DEFAULT],
|
||||
'hybrid': [ReplayType.IMAGE, ReplayType.TEXT, ReplayType.DEFAULT]
|
||||
'hybrid': [ReplayType.IMAGE, ReplayType.TEXT, ReplayType.DEFAULT],
|
||||
'vector': [ReplayType.POLYGON, ReplayType.CIRCLE, ReplayType.LINE_STRING, ReplayType.IMAGE, ReplayType.TEXT, ReplayType.DEFAULT]
|
||||
};
|
||||
|
||||
|
||||
@@ -115,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];
|
||||
@@ -137,11 +138,12 @@ class CanvasVectorTileLayerRenderer extends CanvasTileLayerRenderer {
|
||||
* @inheritDoc
|
||||
*/
|
||||
getTile(z, x, y, frameState) {
|
||||
const tile = /** @type {import("../../VectorRenderTile.js").default} */ (super.getTile(z, x, y, frameState));
|
||||
const pixelRatio = frameState.pixelRatio;
|
||||
const viewState = frameState.viewState;
|
||||
const resolution = viewState.resolution;
|
||||
const projection = viewState.projection;
|
||||
const layer = this.getLayer();
|
||||
const tile = layer.getSource().getTile(z, x, y, pixelRatio, projection);
|
||||
if (tile.getState() < TileState.LOADED) {
|
||||
tile.wantedResolution = resolution;
|
||||
const tileUid = getUid(tile);
|
||||
@@ -156,18 +158,19 @@ class CanvasVectorTileLayerRenderer extends CanvasTileLayerRenderer {
|
||||
tile.wantedResolution = resolution;
|
||||
}
|
||||
const render = this.prepareTile(tile, pixelRatio, projection, false);
|
||||
if (render) {
|
||||
if (render && layer.getRenderMode() !== VectorTileRenderType.VECTOR) {
|
||||
this.renderTileImage_(tile, frameState);
|
||||
}
|
||||
}
|
||||
return tile;
|
||||
return super.getTile(z, x, y, frameState);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
isDrawableTile(tile) {
|
||||
return super.isDrawableTile(tile) && tile.hasContext(this.getLayer());
|
||||
const layer = this.getLayer();
|
||||
return super.isDrawableTile(tile) && layer.getRenderMode() === VectorTileRenderType.VECTOR || tile.hasContext(layer);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -215,13 +218,7 @@ 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();
|
||||
}
|
||||
}
|
||||
tile.hitDetectionImageData = null;
|
||||
delete tile.hitDetectionImageData[layerUid];
|
||||
tile.executorGroups[layerUid] = [];
|
||||
for (let t = 0, tt = sourceTiles.length; t < tt; ++t) {
|
||||
const sourceTile = sourceTiles[t];
|
||||
@@ -267,7 +264,7 @@ class CanvasVectorTileLayerRenderer extends CanvasTileLayerRenderer {
|
||||
}
|
||||
const executorGroupInstructions = builderGroup.finish();
|
||||
// no need to clip when the render tile is covered by a single source tile
|
||||
const replayExtent = layer.getDeclutter() && sourceTiles.length === 1 ?
|
||||
const replayExtent = layer.getRenderMode() !== VectorTileRenderType.VECTOR && layer.getDeclutter() && sourceTiles.length === 1 ?
|
||||
null :
|
||||
sharedExtent;
|
||||
const renderingReplayGroup = new CanvasExecutorGroup(replayExtent, resolution,
|
||||
@@ -341,6 +338,7 @@ class CanvasVectorTileLayerRenderer extends CanvasTileLayerRenderer {
|
||||
getFeatures(pixel) {
|
||||
return new Promise(function(resolve, reject) {
|
||||
const layer = /** @type {import("../../layer/VectorTile.js").default} */ (this.getLayer());
|
||||
const layerUid = getUid(layer);
|
||||
const source = layer.getSource();
|
||||
const projection = this.renderedProjection;
|
||||
const projectionExtent = projection.getExtent();
|
||||
@@ -364,7 +362,7 @@ class CanvasVectorTileLayerRenderer extends CanvasTileLayerRenderer {
|
||||
tile = undefined;
|
||||
}
|
||||
}
|
||||
if (!tile) {
|
||||
if (!tile || tile.loadingSourceTiles > 0) {
|
||||
resolve([]);
|
||||
return;
|
||||
}
|
||||
@@ -377,7 +375,8 @@ class CanvasVectorTileLayerRenderer extends CanvasTileLayerRenderer {
|
||||
const features = tile.getSourceTiles().reduce(function(accumulator, sourceTile) {
|
||||
return accumulator.concat(sourceTile.getFeatures());
|
||||
}, []);
|
||||
if (!tile.hitDetectionImageData) {
|
||||
let hitDetectionImageData = tile.hitDetectionImageData[layerUid];
|
||||
if (!hitDetectionImageData && !this.animatingOrInteracting_) {
|
||||
const tileSize = toSize(tileGrid.getTileSize(tileGrid.getZForResolution(resolution)));
|
||||
const size = [tileSize[0] / 2, tileSize[1] / 2];
|
||||
const rotation = this.renderedRotation_;
|
||||
@@ -385,16 +384,13 @@ class CanvasVectorTileLayerRenderer extends CanvasTileLayerRenderer {
|
||||
this.getRenderTransform(tileGrid.getTileCoordCenter(tile.wrappedTileCoord),
|
||||
resolution, 0, 0.5, size[0], size[1], 0)
|
||||
];
|
||||
requestAnimationFrame(function() {
|
||||
tile.hitDetectionImageData = createHitDetectionImageData(tileSize, transforms,
|
||||
features, layer.getStyleFunction(),
|
||||
tileGrid.getTileCoordExtent(tile.wrappedTileCoord),
|
||||
tile.getReplayState(layer).renderedResolution, rotation);
|
||||
resolve(hitDetect(tilePixel, features, tile.hitDetectionImageData));
|
||||
});
|
||||
} else {
|
||||
resolve(hitDetect(tilePixel, features, tile.hitDetectionImageData));
|
||||
hitDetectionImageData = createHitDetectionImageData(tileSize, transforms,
|
||||
features, layer.getStyleFunction(),
|
||||
tileGrid.getTileCoordExtent(tile.wrappedTileCoord),
|
||||
tile.getReplayState(layer).renderedResolution, rotation);
|
||||
tile.hitDetectionImageData[layerUid] = hitDetectionImageData;
|
||||
}
|
||||
resolve(hitDetect(tilePixel, features, hitDetectionImageData));
|
||||
}.bind(this));
|
||||
}
|
||||
|
||||
@@ -465,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];
|
||||
|
||||
@@ -143,10 +143,6 @@ class WebGLPointsLayerRenderer extends WebGLLayerRenderer {
|
||||
options.vertexShader
|
||||
);
|
||||
|
||||
if (this.getShaderCompileErrors()) {
|
||||
throw new Error(this.getShaderCompileErrors());
|
||||
}
|
||||
|
||||
/**
|
||||
* @type {boolean}
|
||||
* @private
|
||||
@@ -158,10 +154,6 @@ class WebGLPointsLayerRenderer extends WebGLLayerRenderer {
|
||||
options.hitVertexShader
|
||||
);
|
||||
|
||||
if (this.getShaderCompileErrors()) {
|
||||
throw new Error(this.getShaderCompileErrors());
|
||||
}
|
||||
|
||||
const customAttributes = options.attributes ?
|
||||
options.attributes.map(function(attribute) {
|
||||
return {
|
||||
|
||||
@@ -202,16 +202,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.
|
||||
|
||||
@@ -143,7 +143,8 @@ export class CustomTile extends Tile {
|
||||
* The tile data is requested if not yet loaded.
|
||||
*/
|
||||
forDataAtCoordinate(coordinate, callback, opt_request) {
|
||||
if (this.state == TileState.IDLE && opt_request === true) {
|
||||
if (this.state == TileState.EMPTY && opt_request === true) {
|
||||
this.state = TileState.IDLE;
|
||||
listenOnce(this, EventType.CHANGE, function(e) {
|
||||
callback(this.getData(coordinate));
|
||||
}, this);
|
||||
@@ -186,7 +187,7 @@ export class CustomTile extends Tile {
|
||||
this.keys_ = json['keys'];
|
||||
this.data_ = json['data'];
|
||||
|
||||
this.state = TileState.EMPTY;
|
||||
this.state = TileState.LOADED;
|
||||
this.changed();
|
||||
}
|
||||
|
||||
@@ -248,6 +249,8 @@ export class CustomTile extends Tile {
|
||||
load() {
|
||||
if (this.preemptive_) {
|
||||
this.loadInternal_();
|
||||
} else {
|
||||
this.setState(TileState.EMPTY);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -258,9 +261,9 @@ export class CustomTile extends Tile {
|
||||
* @property {boolean} [preemptive=true]
|
||||
* If `true` the UTFGrid source loads the tiles based on their "visibility".
|
||||
* This improves the speed of response, but increases traffic.
|
||||
* Note that if set to `false`, you need to pass `true` as `opt_request`
|
||||
* to the `forDataAtCoordinateAndResolution` method otherwise no data
|
||||
* will ever be loaded.
|
||||
* Note that if set to `false` (lazy loading), you need to pass `true` as
|
||||
* `opt_request` to the `forDataAtCoordinateAndResolution` method otherwise no
|
||||
* data will ever be loaded.
|
||||
* @property {boolean} [jsonp=false] Use JSONP with callback to load the TileJSON.
|
||||
* Useful when the server does not support CORS..
|
||||
* @property {import("./TileJSON.js").Config} [tileJSON] TileJSON configuration for this source.
|
||||
|
||||
@@ -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) {
|
||||
|
||||
+80
-66
@@ -7,13 +7,13 @@ import VectorRenderTile from '../VectorRenderTile.js';
|
||||
import Tile from '../VectorTile.js';
|
||||
import {toSize} from '../size.js';
|
||||
import UrlTile from './UrlTile.js';
|
||||
import {getKeyZXY, getKey} from '../tilecoord.js';
|
||||
import {getKeyZXY, fromKey} from '../tilecoord.js';
|
||||
import {createXYZ, extentFromProjection, createForProjection} from '../tilegrid.js';
|
||||
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
|
||||
@@ -52,6 +52,17 @@ import {listen, unlistenByKey} from '../events.js';
|
||||
* });
|
||||
* }
|
||||
* ```
|
||||
* If you do not need extent, resolution and projection to get the features for a tile (e.g.
|
||||
* for GeoJSON tiles), your `tileLoadFunction` does not need a `setLoader()` call. Only make sure
|
||||
* to call `setFeatures()` on the tile:
|
||||
* ```js
|
||||
* const format = new GeoJSON({featureProjection: map.getView().getProjection()});
|
||||
* async function tileLoadFunction(tile, url) {
|
||||
* const response = await fetch(url);
|
||||
* const data = await response.json();
|
||||
* tile.setFeatures(format.readFeatures(data));
|
||||
* }
|
||||
* ```
|
||||
* @property {import("../Tile.js").UrlFunction} [tileUrlFunction] Optional function to get tile URL given a tile coordinate and the projection.
|
||||
* @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
|
||||
@@ -129,15 +140,9 @@ class VectorTile extends UrlTile {
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @type {Object<string, import("../VectorTile.js").default>}
|
||||
* @type {TileCache}
|
||||
*/
|
||||
this.sourceTileByCoordKey_ = {};
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @type {Object<string, Array<import("../VectorTile.js").default>>}
|
||||
*/
|
||||
this.sourceTilesByTileKey_ = {};
|
||||
this.sourceTileCache = new TileCache(this.tileCache.highWaterMark);
|
||||
|
||||
/**
|
||||
* @private
|
||||
@@ -159,6 +164,51 @@ class VectorTile extends UrlTile {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Get features whose bounding box intersects the provided extent. Only features for cached
|
||||
* tiles for the last rendered zoom level are available in the source. So this method is only
|
||||
* suitable for requesting tiles for extents that are currently rendered.
|
||||
*
|
||||
* Features are returned in random tile order and as they are included in the tiles. This means
|
||||
* they can be clipped, duplicated across tiles, and simplified to the render resolution.
|
||||
*
|
||||
* @param {import("../extent.js").Extent} extent Extent.
|
||||
* @return {Array<import("../Feature.js").FeatureLike>} Features.
|
||||
* @api
|
||||
*/
|
||||
getFeaturesInExtent(extent) {
|
||||
const features = [];
|
||||
const tileCache = this.tileCache;
|
||||
if (tileCache.getCount() === 0) {
|
||||
return features;
|
||||
}
|
||||
const z = fromKey(tileCache.peekFirstKey())[0];
|
||||
const tileGrid = this.tileGrid;
|
||||
tileCache.forEach(function(tile) {
|
||||
if (tile.tileCoord[0] !== z || tile.getState() !== TileState.LOADED) {
|
||||
return;
|
||||
}
|
||||
const sourceTiles = tile.getSourceTiles();
|
||||
for (let i = 0, ii = sourceTiles.length; i < ii; ++i) {
|
||||
const sourceTile = sourceTiles[i];
|
||||
const tileCoord = sourceTile.tileCoord;
|
||||
if (intersects(extent, tileGrid.getTileCoordExtent(tileCoord))) {
|
||||
const tileFeatures = sourceTile.getFeatures();
|
||||
if (tileFeatures) {
|
||||
for (let j = 0, jj = tileFeatures.length; j < jj; ++j) {
|
||||
const candidate = tileFeatures[j];
|
||||
const geometry = candidate.getGeometry();
|
||||
if (intersects(extent, geometry.getExtent())) {
|
||||
features.push(candidate);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
return features;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {boolean} The source can have overlapping geometries.
|
||||
*/
|
||||
@@ -172,8 +222,7 @@ class VectorTile extends UrlTile {
|
||||
*/
|
||||
clear() {
|
||||
this.tileCache.clear();
|
||||
this.sourceTileByCoordKey_ = {};
|
||||
this.sourceTilesByTileKey_ = {};
|
||||
this.sourceTileCache.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -198,7 +247,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;
|
||||
@@ -211,41 +260,39 @@ class VectorTile extends UrlTile {
|
||||
--loadedZ;
|
||||
covered = true;
|
||||
sourceTileGrid.forEachTileCoord(extent, loadedZ, function(sourceTileCoord) {
|
||||
const coordKey = getKey(sourceTileCoord);
|
||||
const tileUrl = this.tileUrlFunction(sourceTileCoord, pixelRatio, projection);
|
||||
let sourceTile;
|
||||
if (coordKey in this.sourceTileByCoordKey_) {
|
||||
sourceTile = this.sourceTileByCoordKey_[coordKey];
|
||||
const state = sourceTile.getState();
|
||||
if (state === TileState.LOADED || state === TileState.ERROR || state === TileState.EMPTY) {
|
||||
sourceTiles.push(sourceTile);
|
||||
return;
|
||||
}
|
||||
} else if (loadedZ === sourceZ) {
|
||||
const tileUrl = this.tileUrlFunction(sourceTileCoord, pixelRatio, projection);
|
||||
if (tileUrl !== undefined) {
|
||||
if (tileUrl !== undefined) {
|
||||
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);
|
||||
return;
|
||||
}
|
||||
} else if (loadedZ === sourceZ) {
|
||||
sourceTile = new this.tileClass(sourceTileCoord, TileState.IDLE, tileUrl,
|
||||
this.format_, this.tileLoadFunction);
|
||||
sourceTile.extent = sourceTileGrid.getTileCoordExtent(sourceTileCoord);
|
||||
sourceTile.projection = projection;
|
||||
sourceTile.resolution = sourceTileGrid.getResolution(sourceTileCoord[0]);
|
||||
this.sourceTileByCoordKey_[coordKey] = sourceTile;
|
||||
this.sourceTileCache.set(tileUrl, sourceTile);
|
||||
sourceTile.addEventListener(EventType.CHANGE, this.handleTileChange.bind(this));
|
||||
sourceTile.load();
|
||||
}
|
||||
}
|
||||
covered = false;
|
||||
covered = covered && sourceTile && sourceTile.getState() === TileState.LOADED;
|
||||
if (!sourceTile) {
|
||||
return;
|
||||
}
|
||||
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) {
|
||||
@@ -259,7 +306,6 @@ class VectorTile extends UrlTile {
|
||||
}
|
||||
}
|
||||
});
|
||||
tile.sourceTileListenerKeys.push(key);
|
||||
}
|
||||
}.bind(this));
|
||||
if (!covered) {
|
||||
@@ -277,43 +323,13 @@ 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;
|
||||
}
|
||||
}
|
||||
this.sourceTileCache.expireCache({});
|
||||
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.sourceTileByCoordKey_[getKey(sourceTile.tileCoord)];
|
||||
}
|
||||
}
|
||||
}
|
||||
delete this.sourceTilesByTileKey_[tileKey];
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
@@ -355,9 +371,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) {
|
||||
|
||||
@@ -152,7 +152,7 @@ class WMTS extends TileImage {
|
||||
|
||||
/**
|
||||
* Set the URLs to use for requests.
|
||||
* URLs may contain OCG conform URL Template Variables: {TileMatrix}, {TileRow}, {TileCol}.
|
||||
* URLs may contain OGC conform URL Template Variables: {TileMatrix}, {TileRow}, {TileCol}.
|
||||
* @override
|
||||
*/
|
||||
setUrls(urls) {
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* @module ol/structs/RBush
|
||||
*/
|
||||
import {getUid} from '../util.js';
|
||||
import RBush_ from 'rbush';
|
||||
import RBush_ from 'rbush/rbush.js';
|
||||
import {createOrUpdate, equals} from '../extent.js';
|
||||
import {isEmpty} from '../obj.js';
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
* Default null; if null, the Canvas/renderer default black will be used.
|
||||
* @property {CanvasLineCap} [lineCap='round'] Line cap style: `butt`, `round`, or `square`.
|
||||
* @property {CanvasLineJoin} [lineJoin='round'] Line join style: `bevel`, `round`, or `miter`.
|
||||
* @property {Array<number>} [lineDash] Line dash pattern. Default is `undefined` (no dash).
|
||||
* @property {Array<number>} [lineDash] Line dash pattern. Default is `null` (no dash).
|
||||
* Please note that Internet Explorer 10 and lower do not support the `setLineDash` method on
|
||||
* the `CanvasRenderingContext2D` and therefore this option will have no visual effect in these browsers.
|
||||
* @property {number} [lineDashOffset=0] Line dash offset.
|
||||
|
||||
@@ -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`
|
||||
|
||||
@@ -25,17 +25,23 @@ const tmpTileCoord = [0, 0, 0];
|
||||
* `origins` are configured, the `origin` will be set to the top-left corner of the extent.
|
||||
* @property {number} [minZoom=0] Minimum zoom.
|
||||
* @property {import("../coordinate.js").Coordinate} [origin] The tile grid origin, i.e. where the `x`
|
||||
* and `y` axes meet (`[z, 0, 0]`). Tile coordinates increase left to right and upwards. If not
|
||||
* and `y` axes meet (`[z, 0, 0]`). Tile coordinates increase left to right and downwards. If not
|
||||
* specified, `extent` or `origins` must be provided.
|
||||
* @property {Array<import("../coordinate.js").Coordinate>} [origins] Tile grid origins, i.e. where
|
||||
* the `x` and `y` axes meet (`[z, 0, 0]`), for each zoom level. If given, the array length
|
||||
* should match the length of the `resolutions` array, i.e. each resolution can have a different
|
||||
* origin. Tile coordinates increase left to right and upwards. If not specified, `extent` or
|
||||
* origin. Tile coordinates increase left to right and downwards. If not specified, `extent` or
|
||||
* `origin` must be provided.
|
||||
* @property {!Array<number>} resolutions Resolutions. The array index of each resolution needs
|
||||
* to match the zoom level. This means that even if a `minZoom` is configured, the resolutions
|
||||
* array will have a length of `maxZoom + 1`.
|
||||
* @property {Array<import("../size.js").Size>} [sizes] Sizes.
|
||||
* @property {Array<import("../size.js").Size>} [sizes] Number of tile rows and columns
|
||||
* of the grid for each zoom level. If specified the values
|
||||
* define each zoom level's extent together with the `origin` or `origins`.
|
||||
* A grid `extent` can be configured in addition, and will further limit the extent
|
||||
* for which tile requests are made by sources. If the bottom-left corner of
|
||||
* an extent is used as `origin` or `origins`, then the `y` value must be
|
||||
* negative because OpenLayers tile coordinates use the top left as the origin.
|
||||
* @property {number|import("../size.js").Size} [tileSize] Tile size.
|
||||
* Default is `[256, 256]`.
|
||||
* @property {Array<import("../size.js").Size>} [tileSizes] Tile sizes. If given, the array length
|
||||
|
||||
+5
-10
@@ -15,12 +15,12 @@ import TileGrid from './TileGrid.js';
|
||||
* top-left corner of the extent.
|
||||
* @property {import("../coordinate.js").Coordinate} [origin] The tile grid origin, i.e.
|
||||
* where the `x` and `y` axes meet (`[z, 0, 0]`). Tile coordinates increase left
|
||||
* to right and upwards. If not specified, `extent` or `origins` must be provided.
|
||||
* to right and downwards. If not specified, `extent` or `origins` must be provided.
|
||||
* @property {Array<import("../coordinate.js").Coordinate>} [origins] Tile grid origins,
|
||||
* i.e. where the `x` and `y` axes meet (`[z, 0, 0]`), for each zoom level. If
|
||||
* given, the array length should match the length of the `resolutions` array, i.e.
|
||||
* each resolution can have a different origin. Tile coordinates increase left to
|
||||
* right and upwards. If not specified, `extent` or `origin` must be provided.
|
||||
* right and downwards. If not specified, `extent` or `origin` must be provided.
|
||||
* @property {!Array<number>} resolutions Resolutions. The array index of each
|
||||
* resolution needs to match the zoom level. This means that even if a `minZoom`
|
||||
* is configured, the resolutions array will have a length of `maxZoom + 1`
|
||||
@@ -29,19 +29,14 @@ import TileGrid from './TileGrid.js';
|
||||
* @property {Array<import("../size.js").Size>} [sizes] Number of tile rows and columns
|
||||
* of the grid for each zoom level. The values here are the `TileMatrixWidth` and
|
||||
* `TileMatrixHeight` advertised in the GetCapabilities response of the WMTS, and
|
||||
* define the grid's extent together with the `origin`.
|
||||
* An `extent` can be configured in addition, and will further limit the extent for
|
||||
* define each zoom level's extent together with the `origin` or `origins`.
|
||||
* A grid `extent` can be configured in addition, and will further limit the extent for
|
||||
* which tile requests are made by sources. If the bottom-left corner of
|
||||
* the `extent` is used as `origin` or `origins`, then the `y` value must be
|
||||
* an extent is used as `origin` or `origins`, then the `y` value must be
|
||||
* negative because OpenLayers tile coordinates use the top left as the origin.
|
||||
* @property {number|import("../size.js").Size} [tileSize] Tile size.
|
||||
* @property {Array<import("../size.js").Size>} [tileSizes] Tile sizes. The length of
|
||||
* this array needs to match the length of the `resolutions` array.
|
||||
* @property {Array<number>} [widths] Number of tile columns that cover the grid's
|
||||
* extent for each zoom level. Only required when used with a source that has `wrapX`
|
||||
* set to `true`, and only when the grid's origin differs from the one of the
|
||||
* projection's extent. The array length has to match the length of the `resolutions`
|
||||
* array, i.e. each resolution will have a matching entry here.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ import {assert} from './asserts.js';
|
||||
* An array representing an affine 2d transformation for use with
|
||||
* {@link module:ol/transform} functions. The array has 6 elements.
|
||||
* @typedef {!Array<number>} Transform
|
||||
* @api
|
||||
*/
|
||||
|
||||
|
||||
@@ -210,6 +211,24 @@ export function compose(transform, dx1, dy1, sx, sy, angle, dx2, dy2) {
|
||||
return transform;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a composite transform given an initial translation, scale, rotation, and
|
||||
* final translation (in that order only, not commutative). The resulting transform
|
||||
* string can be applied as `transform` porperty of an HTMLElement's style.
|
||||
* @param {number} dx1 Initial translation x.
|
||||
* @param {number} dy1 Initial translation y.
|
||||
* @param {number} sx Scale factor x.
|
||||
* @param {number} sy Scale factor y.
|
||||
* @param {number} angle Rotation (in counter-clockwise radians).
|
||||
* @param {number} dx2 Final translation x.
|
||||
* @param {number} dy2 Final translation y.
|
||||
* @return {string} The composite css transform.
|
||||
* @api
|
||||
*/
|
||||
export function composeCssTransform(dx1, dy1, sx, sy, angle, dx2, dy2) {
|
||||
return toString(compose(create(), dx1, dy1, sx, sy, angle, dx2, dy2));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Invert the given transform.
|
||||
|
||||
@@ -24,7 +24,6 @@ const DEFAULT_FRAGMENT_SHADER = `
|
||||
uniform sampler2D u_image;
|
||||
|
||||
varying vec2 v_texCoord;
|
||||
varying vec2 v_screenCoord;
|
||||
|
||||
void main() {
|
||||
gl_FragColor = texture2D(u_image, v_texCoord);
|
||||
@@ -89,7 +88,6 @@ const DEFAULT_FRAGMENT_SHADER = `
|
||||
* uniform sampler2D u_image;
|
||||
*
|
||||
* varying vec2 v_texCoord;
|
||||
* varying vec2 v_screenCoord;
|
||||
*
|
||||
* void main() {
|
||||
* gl_FragColor = texture2D(u_image, v_texCoord);
|
||||
@@ -176,17 +174,19 @@ class WebGLPostProcessingPass {
|
||||
*/
|
||||
init(frameState) {
|
||||
const gl = this.getGL();
|
||||
const canvas = gl.canvas;
|
||||
const size = frameState.size;
|
||||
const textureSize = [
|
||||
gl.drawingBufferWidth * this.scaleRatio_,
|
||||
gl.drawingBufferHeight * this.scaleRatio_
|
||||
];
|
||||
|
||||
// rendering goes to my buffer
|
||||
gl.bindFramebuffer(gl.FRAMEBUFFER, this.getFrameBuffer());
|
||||
gl.viewport(0, 0, canvas.width * this.scaleRatio_, canvas.height * this.scaleRatio_);
|
||||
gl.viewport(0, 0, textureSize[0], textureSize[1]);
|
||||
|
||||
// if size has changed: adjust canvas & render target texture
|
||||
if (!this.renderTargetTextureSize_ ||
|
||||
this.renderTargetTextureSize_[0] !== size[0] || this.renderTargetTextureSize_[1] !== size[1]) {
|
||||
this.renderTargetTextureSize_ = size;
|
||||
this.renderTargetTextureSize_[0] !== textureSize[0] || this.renderTargetTextureSize_[1] !== textureSize[1]) {
|
||||
this.renderTargetTextureSize_ = textureSize;
|
||||
|
||||
// create a new texture
|
||||
const level = 0;
|
||||
@@ -197,8 +197,8 @@ class WebGLPostProcessingPass {
|
||||
const data = null;
|
||||
gl.bindTexture(gl.TEXTURE_2D, this.renderTargetTexture_);
|
||||
gl.texImage2D(gl.TEXTURE_2D, level, internalFormat,
|
||||
canvas.width * this.scaleRatio_, canvas.height * this.scaleRatio_, border,
|
||||
format, type, data);
|
||||
textureSize[0], textureSize[1],
|
||||
border, format, type, data);
|
||||
|
||||
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
|
||||
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
|
||||
@@ -217,7 +217,7 @@ class WebGLPostProcessingPass {
|
||||
*/
|
||||
apply(frameState, nextPass) {
|
||||
const gl = this.getGL();
|
||||
const canvas = gl.canvas;
|
||||
const size = frameState.size;
|
||||
|
||||
gl.bindFramebuffer(gl.FRAMEBUFFER, nextPass ? nextPass.getFrameBuffer() : null);
|
||||
gl.activeTexture(gl.TEXTURE0);
|
||||
@@ -228,14 +228,14 @@ class WebGLPostProcessingPass {
|
||||
gl.clear(gl.COLOR_BUFFER_BIT);
|
||||
gl.enable(gl.BLEND);
|
||||
gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA);
|
||||
gl.viewport(0, 0, canvas.width, canvas.height);
|
||||
gl.viewport(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight);
|
||||
|
||||
gl.bindBuffer(gl.ARRAY_BUFFER, this.renderTargetVerticesBuffer_);
|
||||
|
||||
gl.useProgram(this.renderTargetProgram_);
|
||||
gl.enableVertexAttribArray(this.renderTargetAttribLocation_);
|
||||
gl.vertexAttribPointer(this.renderTargetAttribLocation_, 2, gl.FLOAT, false, 0, 0);
|
||||
gl.uniform2f(this.renderTargetUniformLocation_, canvas.width, canvas.height);
|
||||
gl.uniform2f(this.renderTargetUniformLocation_, size[0], size[1]);
|
||||
gl.uniform1i(this.renderTargetTextureLocation_, 0);
|
||||
|
||||
this.applyUniforms(frameState);
|
||||
|
||||
@@ -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