Make code prettier

This updates ESLint and our shared eslint-config-openlayers to use Prettier.  Most formatting changes were automatically applied with this:

    npm run lint -- --fix

A few manual changes were required:

 * In `examples/offscreen-canvas.js`, the `//eslint-disable-line` comment needed to be moved to the appropriate line to disable the error about the `'worker-loader!./offscreen-canvas.worker.js'` import.
 * In `examples/webpack/exapmle-builder.js`, spaces could not be added after a couple `function`s for some reason.  While editing this, I reworked `ExampleBuilder` to be a class.
 * In `src/ol/format/WMSGetFeatureInfo.js`, the `// @ts-ignore` comment needed to be moved down one line so it applied to the `parsersNS` argument.
This commit is contained in:
Tim Schaub
2020-04-06 12:25:12 -06:00
parent 53b48baf62
commit 054af09032
790 changed files with 46833 additions and 33765 deletions
+57 -26
View File
@@ -1,14 +1,14 @@
/**
* @module ol/renderer/canvas/ImageLayer
*/
import {ENABLE_RASTER_REPROJECTION} from '../../reproj/common.js';
import CanvasLayerRenderer from './Layer.js';
import ViewHint from '../../ViewHint.js';
import {ENABLE_RASTER_REPROJECTION} from '../../reproj/common.js';
import {compose as composeTransform, makeInverse} from '../../transform.js';
import {containsExtent, intersects} from '../../extent.js';
import {createTransformString} from '../../render/canvas.js';
import {fromUserExtent} from '../../proj.js';
import {getIntersection, isEmpty} from '../../extent.js';
import CanvasLayerRenderer from './Layer.js';
import {compose as composeTransform, makeInverse} from '../../transform.js';
import {createTransformString} from '../../render/canvas.js';
/**
* @classdesc
@@ -16,7 +16,6 @@ import {createTransformString} from '../../render/canvas.js';
* @api
*/
class CanvasImageLayerRenderer extends CanvasLayerRenderer {
/**
* @param {import("../../layer/Image.js").default} imageLayer Image layer.
*/
@@ -54,10 +53,17 @@ class CanvasImageLayerRenderer extends CanvasLayerRenderer {
let renderedExtent = frameState.extent;
if (layerState.extent !== undefined) {
renderedExtent = getIntersection(renderedExtent, fromUserExtent(layerState.extent, viewState.projection));
renderedExtent = getIntersection(
renderedExtent,
fromUserExtent(layerState.extent, viewState.projection)
);
}
if (!hints[ViewHint.ANIMATING] && !hints[ViewHint.INTERACTING] && !isEmpty(renderedExtent)) {
if (
!hints[ViewHint.ANIMATING] &&
!hints[ViewHint.INTERACTING] &&
!isEmpty(renderedExtent)
) {
if (imageSource) {
let projection = viewState.projection;
if (!ENABLE_RASTER_REPROJECTION) {
@@ -66,7 +72,12 @@ class CanvasImageLayerRenderer extends CanvasLayerRenderer {
projection = sourceProjection;
}
}
const image = imageSource.getImage(renderedExtent, viewResolution, pixelRatio, projection);
const image = imageSource.getImage(
renderedExtent,
viewResolution,
pixelRatio,
projection
);
if (image && this.loadImage(image)) {
this.image_ = image;
}
@@ -95,7 +106,8 @@ class CanvasImageLayerRenderer extends CanvasLayerRenderer {
const viewCenter = viewState.center;
const viewResolution = viewState.resolution;
const size = frameState.size;
const scale = pixelRatio * imageResolution / (viewResolution * imagePixelRatio);
const scale =
(pixelRatio * imageResolution) / (viewResolution * imagePixelRatio);
let width = Math.round(size[0] * pixelRatio);
let height = Math.round(size[1] * pixelRatio);
@@ -107,11 +119,15 @@ class CanvasImageLayerRenderer extends CanvasLayerRenderer {
}
// set forward and inverse pixel transforms
composeTransform(this.pixelTransform,
frameState.size[0] / 2, frameState.size[1] / 2,
1 / pixelRatio, 1 / pixelRatio,
composeTransform(
this.pixelTransform,
frameState.size[0] / 2,
frameState.size[1] / 2,
1 / pixelRatio,
1 / pixelRatio,
rotation,
-width / 2, -height / 2
-width / 2,
-height / 2
);
makeInverse(this.inversePixelTransform, this.pixelTransform);
@@ -132,8 +148,13 @@ class CanvasImageLayerRenderer extends CanvasLayerRenderer {
// clipped rendering if layer extent is set
let clipped = false;
if (layerState.extent) {
const layerExtent = fromUserExtent(layerState.extent, viewState.projection);
clipped = !containsExtent(layerExtent, frameState.extent) && intersects(layerExtent, frameState.extent);
const layerExtent = fromUserExtent(
layerState.extent,
viewState.projection
);
clipped =
!containsExtent(layerExtent, frameState.extent) &&
intersects(layerExtent, frameState.extent);
if (clipped) {
this.clipUnrotated(context, frameState, layerExtent);
}
@@ -141,14 +162,18 @@ class CanvasImageLayerRenderer extends CanvasLayerRenderer {
const img = image.getImage();
const transform = composeTransform(this.tempTransform_,
width / 2, height / 2,
scale, scale,
const transform = composeTransform(
this.tempTransform_,
width / 2,
height / 2,
scale,
scale,
0,
imagePixelRatio * (imageExtent[0] - viewCenter[0]) / imageResolution,
imagePixelRatio * (viewCenter[1] - imageExtent[3]) / imageResolution);
(imagePixelRatio * (imageExtent[0] - viewCenter[0])) / imageResolution,
(imagePixelRatio * (viewCenter[1] - imageExtent[3])) / imageResolution
);
this.renderedResolution = imageResolution * pixelRatio / imagePixelRatio;
this.renderedResolution = (imageResolution * pixelRatio) / imagePixelRatio;
const dx = transform[4];
const dy = transform[5];
@@ -163,8 +188,17 @@ class CanvasImageLayerRenderer extends CanvasLayerRenderer {
previousAlpha = this.context.globalAlpha;
this.context.globalAlpha = opacity;
}
this.context.drawImage(img, 0, 0, +img.width, +img.height,
Math.round(dx), Math.round(dy), Math.round(dw), Math.round(dh));
this.context.drawImage(
img,
0,
0,
+img.width,
+img.height,
Math.round(dx),
Math.round(dy),
Math.round(dw),
Math.round(dh)
);
if (opacity !== 1) {
this.context.globalAlpha = previousAlpha;
}
@@ -180,10 +214,7 @@ class CanvasImageLayerRenderer extends CanvasLayerRenderer {
}
return this.container;
}
}
export default CanvasImageLayerRenderer;
+53 -15
View File
@@ -1,25 +1,32 @@
/**
* @module ol/renderer/canvas/Layer
*/
import {getBottomLeft, getBottomRight, getTopLeft, getTopRight} from '../../extent.js';
import {createCanvasContext2D} from '../../dom.js';
import LayerRenderer from '../Layer.js';
import RenderEvent from '../../render/Event.js';
import RenderEventType from '../../render/EventType.js';
import {
apply as applyTransform,
compose as composeTransform,
create as createTransform,
} from '../../transform.js';
import {createCanvasContext2D} from '../../dom.js';
import {
getBottomLeft,
getBottomRight,
getTopLeft,
getTopRight,
} from '../../extent.js';
import {rotateAtOffset} from '../../render/canvas.js';
import LayerRenderer from '../Layer.js';
import {create as createTransform, apply as applyTransform, compose as composeTransform} from '../../transform.js';
/**
* @abstract
* @template {import("../../layer/Layer.js").default} LayerType
*/
class CanvasLayerRenderer extends LayerRenderer {
/**
* @param {LayerType} layer Layer.
*/
constructor(layer) {
super(layer);
/**
@@ -68,7 +75,6 @@ class CanvasLayerRenderer extends LayerRenderer {
* @type {boolean}
*/
this.containerReused = false;
}
/**
@@ -80,7 +86,11 @@ class CanvasLayerRenderer extends LayerRenderer {
useContainer(target, transform, opacity) {
const layerClassName = this.getLayer().getClassName();
let container, context;
if (target && target.style.opacity === '' && target.className === layerClassName) {
if (
target &&
target.style.opacity === '' &&
target.className === layerClassName
) {
const canvas = target.firstElementChild;
if (canvas instanceof HTMLCanvasElement) {
context = canvas.getContext('2d');
@@ -189,7 +199,12 @@ class CanvasLayerRenderer extends LayerRenderer {
dispatchRenderEvent_(type, context, frameState) {
const layer = this.getLayer();
if (layer.hasListener(type)) {
const event = new RenderEvent(type, this.inversePixelTransform, frameState, context);
const event = new RenderEvent(
type,
this.inversePixelTransform,
frameState,
context
);
layer.dispatchEvent(event);
}
}
@@ -224,14 +239,31 @@ class CanvasLayerRenderer extends LayerRenderer {
* @protected
* @return {!import("../../transform.js").Transform} Transform.
*/
getRenderTransform(center, resolution, rotation, pixelRatio, width, height, offsetX) {
getRenderTransform(
center,
resolution,
rotation,
pixelRatio,
width,
height,
offsetX
) {
const dx1 = width / 2;
const dy1 = height / 2;
const sx = pixelRatio / resolution;
const sy = -sx;
const dx2 = -center[0] + offsetX;
const dy2 = -center[1];
return composeTransform(this.tempTransform_, dx1, dy1, sx, sy, -rotation, dx2, dy2);
return composeTransform(
this.tempTransform_,
dx1,
dy1,
sx,
sy,
-rotation,
dx2,
dy2
);
}
/**
@@ -243,12 +275,20 @@ class CanvasLayerRenderer extends LayerRenderer {
* returned, and empty array will be returned.
*/
getDataAtPixel(pixel, frameState, hitTolerance) {
const renderPixel = applyTransform(this.inversePixelTransform, pixel.slice());
const renderPixel = applyTransform(
this.inversePixelTransform,
pixel.slice()
);
const context = this.context;
let data;
try {
data = context.getImageData(Math.round(renderPixel[0]), Math.round(renderPixel[1]), 1, 1).data;
data = context.getImageData(
Math.round(renderPixel[0]),
Math.round(renderPixel[1]),
1,
1
).data;
} catch (err) {
if (err.name === 'SecurityError') {
// tainted canvas, we assume there is data at the given pixel (although there might not be)
@@ -262,8 +302,6 @@ class CanvasLayerRenderer extends LayerRenderer {
}
return data;
}
}
export default CanvasLayerRenderer;
+138 -55
View File
@@ -1,16 +1,25 @@
/**
* @module ol/renderer/canvas/TileLayer
*/
import {getUid} from '../../util.js';
import {fromUserExtent} from '../../proj.js';
import CanvasLayerRenderer from './Layer.js';
import TileRange from '../../TileRange.js';
import TileState from '../../TileState.js';
import {createEmpty, equals, getIntersection, getTopLeft} from '../../extent.js';
import CanvasLayerRenderer from './Layer.js';
import {apply as applyTransform, compose as composeTransform, makeInverse} from '../../transform.js';
import {numberSafeCompareFunction} from '../../array.js';
import {createTransformString} from '../../render/canvas.js';
import {
apply as applyTransform,
compose as composeTransform,
makeInverse,
} from '../../transform.js';
import {assign} from '../../obj.js';
import {
createEmpty,
equals,
getIntersection,
getTopLeft,
} from '../../extent.js';
import {createTransformString} from '../../render/canvas.js';
import {fromUserExtent} from '../../proj.js';
import {getUid} from '../../util.js';
import {numberSafeCompareFunction} from '../../array.js';
/**
* @classdesc
@@ -18,7 +27,6 @@ import {assign} from '../../obj.js';
* @api
*/
class CanvasTileLayerRenderer extends CanvasLayerRenderer {
/**
* @param {import("../../layer/Tile.js").default|import("../../layer/VectorTile.js").default} tileLayer Tile layer.
*/
@@ -89,9 +97,11 @@ class CanvasTileLayerRenderer extends CanvasLayerRenderer {
const tileLayer = this.getLayer();
const tileState = tile.getState();
const useInterimTilesOnError = tileLayer.getUseInterimTilesOnError();
return tileState == TileState.LOADED ||
tileState == TileState.EMPTY ||
tileState == TileState.ERROR && !useInterimTilesOnError;
return (
tileState == TileState.LOADED ||
tileState == TileState.EMPTY ||
(tileState == TileState.ERROR && !useInterimTilesOnError)
);
}
/**
@@ -167,9 +177,13 @@ class CanvasTileLayerRenderer extends CanvasLayerRenderer {
const tileResolution = tileGrid.getResolution(z);
let extent = frameState.extent;
const layerExtent = layerState.extent && fromUserExtent(layerState.extent, projection);
const layerExtent =
layerState.extent && fromUserExtent(layerState.extent, projection);
if (layerExtent) {
extent = getIntersection(extent, fromUserExtent(layerState.extent, projection));
extent = getIntersection(
extent,
fromUserExtent(layerState.extent, projection)
);
}
const tilePixelRatio = tileSource.getTilePixelRatio(pixelRatio);
@@ -184,13 +198,13 @@ class CanvasTileLayerRenderer extends CanvasLayerRenderer {
height = size;
}
const dx = tileResolution * width / 2 / tilePixelRatio;
const dy = tileResolution * height / 2 / tilePixelRatio;
const dx = (tileResolution * width) / 2 / tilePixelRatio;
const dy = (tileResolution * height) / 2 / tilePixelRatio;
const canvasExtent = [
viewCenter[0] - dx,
viewCenter[1] - dy,
viewCenter[0] + dx,
viewCenter[1] + dy
viewCenter[1] + dy,
];
const tileRange = tileGrid.getTileRangeForExtentAndZ(extent, z);
@@ -201,7 +215,11 @@ class CanvasTileLayerRenderer extends CanvasLayerRenderer {
const tilesToDrawByZ = {};
tilesToDrawByZ[z] = {};
const findLoadedTiles = this.createLoadedTileFinder(tileSource, projection, tilesToDrawByZ);
const findLoadedTiles = this.createLoadedTileFinder(
tileSource,
projection,
tilesToDrawByZ
);
const tmpExtent = this.tmpExtent;
const tmpTileRange = this.tmpTileRange_;
@@ -214,7 +232,10 @@ class CanvasTileLayerRenderer extends CanvasLayerRenderer {
if (tile.getState() == TileState.LOADED) {
tilesToDrawByZ[z][tile.tileCoord.toString()] = tile;
const inTransition = tile.inTransition(uid);
if (!this.newTiles_ && (inTransition || this.renderedTiles.indexOf(tile) === -1)) {
if (
!this.newTiles_ &&
(inTransition || this.renderedTiles.indexOf(tile) === -1)
) {
this.newTiles_ = true;
}
}
@@ -224,28 +245,39 @@ class CanvasTileLayerRenderer extends CanvasLayerRenderer {
}
}
const childTileRange = tileGrid.getTileCoordChildTileRange(tile.tileCoord, tmpTileRange, tmpExtent);
const childTileRange = tileGrid.getTileCoordChildTileRange(
tile.tileCoord,
tmpTileRange,
tmpExtent
);
let covered = false;
if (childTileRange) {
covered = findLoadedTiles(z + 1, childTileRange);
}
if (!covered) {
tileGrid.forEachTileCoordParentTileRange(tile.tileCoord, findLoadedTiles, tmpTileRange, tmpExtent);
tileGrid.forEachTileCoordParentTileRange(
tile.tileCoord,
findLoadedTiles,
tmpTileRange,
tmpExtent
);
}
}
}
const canvasScale = tileResolution / viewResolution;
// set forward and inverse pixel transforms
composeTransform(this.pixelTransform,
frameState.size[0] / 2, frameState.size[1] / 2,
1 / tilePixelRatio, 1 / tilePixelRatio,
composeTransform(
this.pixelTransform,
frameState.size[0] / 2,
frameState.size[1] / 2,
1 / tilePixelRatio,
1 / tilePixelRatio,
rotation,
-width / 2, -height / 2
-width / 2,
-height / 2
);
const canvasTransform = createTransformString(this.pixelTransform);
@@ -257,11 +289,15 @@ class CanvasTileLayerRenderer extends CanvasLayerRenderer {
makeInverse(this.inversePixelTransform, this.pixelTransform);
// set scale transform for calculating tile positions on the canvas
composeTransform(this.tempTransform_,
width / 2, height / 2,
canvasScale, canvasScale,
composeTransform(
this.tempTransform_,
width / 2,
height / 2,
canvasScale,
canvasScale,
0,
-width / 2, -height / 2
-width / 2,
-height / 2
);
if (canvas.width != width || canvas.height != height) {
@@ -284,7 +320,11 @@ class CanvasTileLayerRenderer extends CanvasLayerRenderer {
zs.sort(numberSafeCompareFunction);
let clips, clipZs, currentClip;
if (layerState.opacity === 1 && (!this.containerReused || tileSource.getOpaque(frameState.viewState.projection))) {
if (
layerState.opacity === 1 &&
(!this.containerReused ||
tileSource.getOpaque(frameState.viewState.projection))
) {
zs = zs.reverse();
} else {
clips = [];
@@ -292,27 +332,39 @@ class CanvasTileLayerRenderer extends CanvasLayerRenderer {
}
for (let i = zs.length - 1; i >= 0; --i) {
const currentZ = zs[i];
const currentTilePixelSize = tileSource.getTilePixelSize(currentZ, pixelRatio, projection);
const currentTilePixelSize = tileSource.getTilePixelSize(
currentZ,
pixelRatio,
projection
);
const currentResolution = tileGrid.getResolution(currentZ);
const currentScale = currentResolution / tileResolution;
const dx = currentTilePixelSize[0] * currentScale * canvasScale;
const dy = currentTilePixelSize[1] * currentScale * canvasScale;
const originTileCoord = tileGrid.getTileCoordForCoordAndZ(getTopLeft(canvasExtent), currentZ);
const originTileCoord = tileGrid.getTileCoordForCoordAndZ(
getTopLeft(canvasExtent),
currentZ
);
const originTileExtent = tileGrid.getTileCoordExtent(originTileCoord);
const origin = applyTransform(this.tempTransform_, [
tilePixelRatio * (originTileExtent[0] - canvasExtent[0]) / tileResolution,
tilePixelRatio * (canvasExtent[3] - originTileExtent[3]) / tileResolution
(tilePixelRatio * (originTileExtent[0] - canvasExtent[0])) /
tileResolution,
(tilePixelRatio * (canvasExtent[3] - originTileExtent[3])) /
tileResolution,
]);
const tileGutter = tilePixelRatio * tileSource.getGutterForProjection(projection);
const tileGutter =
tilePixelRatio * tileSource.getGutterForProjection(projection);
const tilesToDraw = tilesToDrawByZ[currentZ];
for (const tileCoordKey in tilesToDraw) {
const tile = /** @type {import("../../ImageTile.js").default} */ (tilesToDraw[tileCoordKey]);
const tile = /** @type {import("../../ImageTile.js").default} */ (tilesToDraw[
tileCoordKey
]);
const tileCoord = tile.tileCoord;
// Calculate integer positions and sizes so that tiles align
const floatX = (origin[0] - (originTileCoord[1] - tileCoord[1]) * dx);
const floatX = origin[0] - (originTileCoord[1] - tileCoord[1]) * dx;
const nextX = Math.round(floatX + dx);
const floatY = (origin[1] - (originTileCoord[2] - tileCoord[2]) * dy);
const floatY = origin[1] - (originTileCoord[2] - tileCoord[2]) * dy;
const nextY = Math.round(floatY + dy);
const x = Math.round(floatX);
const y = Math.round(floatY);
@@ -320,7 +372,8 @@ class CanvasTileLayerRenderer extends CanvasLayerRenderer {
const h = nextY - y;
const transition = z === currentZ;
const inTransition = transition && tile.getAlpha(getUid(this), frameState.time) !== 1;
const inTransition =
transition && tile.getAlpha(getUid(this), frameState.time) !== 1;
if (!inTransition) {
if (clips) {
// Clip mask for regions in this tile that already filled by a higher z tile
@@ -349,7 +402,17 @@ class CanvasTileLayerRenderer extends CanvasLayerRenderer {
context.clearRect(x, y, w, h);
}
}
this.drawTileImage(tile, frameState, x, y, w, h, tileGutter, transition, layerState.opacity);
this.drawTileImage(
tile,
frameState,
x,
y,
w,
h,
tileGutter,
transition,
layerState.opacity
);
if (clips && !inTransition) {
context.restore();
}
@@ -358,16 +421,24 @@ class CanvasTileLayerRenderer extends CanvasLayerRenderer {
}
}
this.renderedRevision = sourceRevision;
this.renderedResolution = tileResolution;
this.extentChanged = !this.renderedExtent_ || !equals(this.renderedExtent_, canvasExtent);
this.extentChanged =
!this.renderedExtent_ || !equals(this.renderedExtent_, canvasExtent);
this.renderedExtent_ = canvasExtent;
this.renderedPixelRatio = pixelRatio;
this.renderedProjection = projection;
this.manageTilePyramid(frameState, tileSource, tileGrid, pixelRatio,
projection, extent, z, tileLayer.getPreload());
this.manageTilePyramid(
frameState,
tileSource,
tileGrid,
pixelRatio,
projection,
extent,
z,
tileLayer.getPreload()
);
this.scheduleExpireCache(frameState, tileSource);
this.postRender(context, frameState);
@@ -407,8 +478,17 @@ class CanvasTileLayerRenderer extends CanvasLayerRenderer {
this.context.save();
this.context.globalAlpha = alpha;
}
this.context.drawImage(image, gutter, gutter,
image.width - 2 * gutter, image.height - 2 * gutter, x, y, w, h);
this.context.drawImage(
image,
gutter,
gutter,
image.width - 2 * gutter,
image.height - 2 * gutter,
x,
y,
w,
h
);
if (alphaChanged) {
this.context.restore();
@@ -450,11 +530,13 @@ class CanvasTileLayerRenderer extends CanvasLayerRenderer {
* @param {import("../../PluggableMap.js").default} map Map.
* @param {import("../../PluggableMap.js").FrameState} frameState Frame state.
*/
const postRenderFunction = function(tileSource, map, frameState) {
const postRenderFunction = function (tileSource, map, frameState) {
const tileSourceKey = getUid(tileSource);
if (tileSourceKey in frameState.usedTiles) {
tileSource.expireCache(frameState.viewState.projection,
frameState.usedTiles[tileSourceKey]);
tileSource.expireCache(
frameState.viewState.projection,
frameState.usedTiles[tileSourceKey]
);
}
}.bind(null, tileSource);
@@ -526,8 +608,12 @@ class CanvasTileLayerRenderer extends CanvasLayerRenderer {
if (tile.getState() == TileState.IDLE) {
wantedTiles[tile.getKey()] = true;
if (!tileQueue.isKeyQueued(tile.getKey())) {
tileQueue.enqueue([tile, tileSourceKey,
tileGrid.getTileCoordCenter(tile.tileCoord), tileResolution]);
tileQueue.enqueue([
tile,
tileSourceKey,
tileGrid.getTileCoordCenter(tile.tileCoord),
tileResolution,
]);
}
}
if (opt_tileCallback !== undefined) {
@@ -540,15 +626,12 @@ class CanvasTileLayerRenderer extends CanvasLayerRenderer {
}
}
}
}
/**
* @function
* @return {import("../../layer/Tile.js").default|import("../../layer/VectorTile.js").default}
*/
CanvasTileLayerRenderer.prototype.getLayer;
export default CanvasTileLayerRenderer;
+86 -39
View File
@@ -1,16 +1,16 @@
/**
* @module ol/renderer/canvas/VectorImageLayer
*/
import ImageCanvas from '../../ImageCanvas.js';
import ViewHint from '../../ViewHint.js';
import {getHeight, getWidth, isEmpty, scaleFromCenter} from '../../extent.js';
import {assign} from '../../obj.js';
import CanvasImageLayerRenderer from './ImageLayer.js';
import CanvasVectorLayerRenderer from './VectorLayer.js';
import EventType from '../../events/EventType.js';
import ImageCanvas from '../../ImageCanvas.js';
import ImageState from '../../ImageState.js';
import {renderDeclutterItems} from '../../render.js';
import ViewHint from '../../ViewHint.js';
import {apply, compose, create} from '../../transform.js';
import {assign} from '../../obj.js';
import {getHeight, getWidth, isEmpty, scaleFromCenter} from '../../extent.js';
import {renderDeclutterItems} from '../../render.js';
/**
* @classdesc
@@ -18,7 +18,6 @@ import {apply, compose, create} from '../../transform.js';
* @api
*/
class CanvasVectorImageLayerRenderer extends CanvasImageLayerRenderer {
/**
* @param {import("../../layer/VectorImage.js").default} layer Vector image layer.
*/
@@ -48,7 +47,6 @@ class CanvasVectorImageLayerRenderer extends CanvasImageLayerRenderer {
* @type {import("../../transform.js").Transform}
*/
this.renderedPixelToCoordinateTransform_ = null;
}
/**
@@ -66,11 +64,13 @@ class CanvasVectorImageLayerRenderer extends CanvasImageLayerRenderer {
*/
getFeatures(pixel) {
if (this.vectorRenderer_) {
const vectorPixel = apply(this.coordinateToVectorPixelTransform_,
apply(this.renderedPixelToCoordinateTransform_, pixel.slice()));
const vectorPixel = apply(
this.coordinateToVectorPixelTransform_,
apply(this.renderedPixelToCoordinateTransform_, pixel.slice())
);
return this.vectorRenderer_.getFeatures(vectorPixel);
} else {
const promise = new Promise(function(resolve, reject) {
const promise = new Promise(function (resolve, reject) {
resolve([]);
});
return promise;
@@ -104,30 +104,54 @@ class CanvasVectorImageLayerRenderer extends CanvasImageLayerRenderer {
const width = getWidth(renderedExtent) / viewResolution;
const height = getHeight(renderedExtent) / viewResolution;
if (!hints[ViewHint.ANIMATING] && !hints[ViewHint.INTERACTING] && !isEmpty(renderedExtent)) {
if (
!hints[ViewHint.ANIMATING] &&
!hints[ViewHint.INTERACTING] &&
!isEmpty(renderedExtent)
) {
vectorRenderer.useContainer(null, null, 1);
const context = vectorRenderer.context;
const imageFrameState = /** @type {import("../../PluggableMap.js").FrameState} */ (assign({}, frameState, {
declutterItems: [],
extent: renderedExtent,
size: [width, height],
viewState: /** @type {import("../../View.js").State} */ (assign({}, frameState.viewState, {
rotation: 0
}))
}));
const image = new ImageCanvas(renderedExtent, viewResolution, pixelRatio, context.canvas, function(callback) {
if (vectorRenderer.prepareFrame(imageFrameState) && vectorRenderer.replayGroupChanged) {
vectorRenderer.renderFrame(imageFrameState, null);
renderDeclutterItems(imageFrameState, null);
callback();
const imageFrameState = /** @type {import("../../PluggableMap.js").FrameState} */ (assign(
{},
frameState,
{
declutterItems: [],
extent: renderedExtent,
size: [width, height],
viewState: /** @type {import("../../View.js").State} */ (assign(
{},
frameState.viewState,
{
rotation: 0,
}
)),
}
});
));
const image = new ImageCanvas(
renderedExtent,
viewResolution,
pixelRatio,
context.canvas,
function (callback) {
if (
vectorRenderer.prepareFrame(imageFrameState) &&
vectorRenderer.replayGroupChanged
) {
vectorRenderer.renderFrame(imageFrameState, null);
renderDeclutterItems(imageFrameState, null);
callback();
}
}
);
image.addEventListener(EventType.CHANGE, function() {
if (image.getState() === ImageState.LOADED) {
this.image_ = image;
}
}.bind(this));
image.addEventListener(
EventType.CHANGE,
function () {
if (image.getState() === ImageState.LOADED) {
this.image_ = image;
}
}.bind(this)
);
image.load();
}
@@ -135,14 +159,20 @@ class CanvasVectorImageLayerRenderer extends CanvasImageLayerRenderer {
const image = this.image_;
const imageResolution = image.getResolution();
const imagePixelRatio = image.getPixelRatio();
const renderedResolution = imageResolution * pixelRatio / imagePixelRatio;
const renderedResolution =
(imageResolution * pixelRatio) / imagePixelRatio;
this.renderedResolution = renderedResolution;
this.renderedPixelToCoordinateTransform_ = frameState.pixelToCoordinateTransform.slice();
this.coordinateToVectorPixelTransform_ = compose(this.coordinateToVectorPixelTransform_,
width / 2, height / 2,
1 / renderedResolution, -1 / renderedResolution,
this.coordinateToVectorPixelTransform_ = compose(
this.coordinateToVectorPixelTransform_,
width / 2,
height / 2,
1 / renderedResolution,
-1 / renderedResolution,
0,
-viewState.center[0], -viewState.center[1]);
-viewState.center[0],
-viewState.center[1]
);
}
return !!this.image_;
@@ -165,14 +195,31 @@ class CanvasVectorImageLayerRenderer extends CanvasImageLayerRenderer {
* @return {T|void} Callback result.
* @template T
*/
forEachFeatureAtCoordinate(coordinate, frameState, hitTolerance, callback, declutteredFeatures) {
forEachFeatureAtCoordinate(
coordinate,
frameState,
hitTolerance,
callback,
declutteredFeatures
) {
if (this.vectorRenderer_) {
return this.vectorRenderer_.forEachFeatureAtCoordinate(coordinate, frameState, hitTolerance, callback, declutteredFeatures);
return this.vectorRenderer_.forEachFeatureAtCoordinate(
coordinate,
frameState,
hitTolerance,
callback,
declutteredFeatures
);
} else {
return super.forEachFeatureAtCoordinate(coordinate, frameState, hitTolerance, callback, declutteredFeatures);
return super.forEachFeatureAtCoordinate(
coordinate,
frameState,
hitTolerance,
callback,
declutteredFeatures
);
}
}
}
export default CanvasVectorImageLayerRenderer;
+302 -99
View File
@@ -1,17 +1,44 @@
/**
* @module ol/renderer/canvas/VectorLayer
*/
import {getUid} from '../../util.js';
import ViewHint from '../../ViewHint.js';
import {buffer, createEmpty, containsExtent, getWidth, intersects as intersectsExtent, wrapX as wrapExtentX} from '../../extent.js';
import {wrapX as wrapCoordinateX} from '../../coordinate.js';
import {fromUserExtent, toUserExtent, getUserProjection, getTransformFromProjections} from '../../proj.js';
import CanvasBuilderGroup from '../../render/canvas/BuilderGroup.js';
import ExecutorGroup, {replayDeclutter} from '../../render/canvas/ExecutorGroup.js';
import CanvasLayerRenderer from './Layer.js';
import {defaultOrder as defaultRenderOrder, getTolerance as getRenderTolerance, getSquaredTolerance as getSquaredRenderTolerance, renderFeature} from '../vector.js';
import {toString as transformToString, makeScale, makeInverse, apply} from '../../transform.js';
import {createHitDetectionImageData, hitDetect} from '../../render/canvas/hitdetect.js';
import ExecutorGroup, {
replayDeclutter,
} from '../../render/canvas/ExecutorGroup.js';
import ViewHint from '../../ViewHint.js';
import {
apply,
makeInverse,
makeScale,
toString as transformToString,
} from '../../transform.js';
import {
buffer,
containsExtent,
createEmpty,
getWidth,
intersects as intersectsExtent,
wrapX as wrapExtentX,
} from '../../extent.js';
import {
createHitDetectionImageData,
hitDetect,
} from '../../render/canvas/hitdetect.js';
import {
defaultOrder as defaultRenderOrder,
getTolerance as getRenderTolerance,
getSquaredTolerance as getSquaredRenderTolerance,
renderFeature,
} from '../vector.js';
import {
fromUserExtent,
getTransformFromProjections,
getUserProjection,
toUserExtent,
} from '../../proj.js';
import {getUid} from '../../util.js';
import {wrapX as wrapCoordinateX} from '../../coordinate.js';
/**
* @classdesc
@@ -19,12 +46,10 @@ import {createHitDetectionImageData, hitDetect} from '../../render/canvas/hitdet
* @api
*/
class CanvasVectorLayerRenderer extends CanvasLayerRenderer {
/**
* @param {import("../../layer/Vector.js").default} vectorLayer Vector layer.
*/
constructor(vectorLayer) {
super(vectorLayer);
/** @private */
@@ -126,7 +151,6 @@ class CanvasVectorLayerRenderer extends CanvasLayerRenderer {
* @return {HTMLElement} The rendered element.
*/
renderFrame(frameState, target) {
const pixelRatio = frameState.pixelRatio;
const layerState = frameState.layerStatesArray[frameState.layerIndex];
@@ -176,21 +200,43 @@ class CanvasVectorLayerRenderer extends CanvasLayerRenderer {
let clipped = false;
if (layerState.extent) {
const layerExtent = fromUserExtent(layerState.extent, projection);
clipped = !containsExtent(layerExtent, frameState.extent) && intersectsExtent(layerExtent, frameState.extent);
clipped =
!containsExtent(layerExtent, frameState.extent) &&
intersectsExtent(layerExtent, frameState.extent);
if (clipped) {
this.clip(context, frameState, layerExtent);
}
}
const viewHints = frameState.viewHints;
const snapToPixel = !(viewHints[ViewHint.ANIMATING] || viewHints[ViewHint.INTERACTING]);
const snapToPixel = !(
viewHints[ViewHint.ANIMATING] || viewHints[ViewHint.INTERACTING]
);
const transform = this.getRenderTransform(center, resolution, rotation, pixelRatio, width, height, 0);
const transform = this.getRenderTransform(
center,
resolution,
rotation,
pixelRatio,
width,
height,
0
);
const declutterReplays = this.getLayer().getDeclutter() ? {} : null;
replayGroup.execute(context, transform, rotation, snapToPixel, undefined, declutterReplays);
replayGroup.execute(
context,
transform,
rotation,
snapToPixel,
undefined,
declutterReplays
);
if (vectorSource.getWrapX() && projection.canWrapX() && !containsExtent(projectionExtent, extent)) {
if (
vectorSource.getWrapX() &&
projection.canWrapX() &&
!containsExtent(projectionExtent, extent)
) {
let startX = extent[0];
const worldWidth = getWidth(projectionExtent);
let world = 0;
@@ -198,8 +244,23 @@ class CanvasVectorLayerRenderer extends CanvasLayerRenderer {
while (startX < projectionExtent[0]) {
--world;
offsetX = worldWidth * world;
const transform = this.getRenderTransform(center, resolution, rotation, pixelRatio, width, height, offsetX);
replayGroup.execute(context, transform, rotation, snapToPixel, undefined, declutterReplays);
const transform = this.getRenderTransform(
center,
resolution,
rotation,
pixelRatio,
width,
height,
offsetX
);
replayGroup.execute(
context,
transform,
rotation,
snapToPixel,
undefined,
declutterReplays
);
startX += worldWidth;
}
world = 0;
@@ -207,15 +268,39 @@ class CanvasVectorLayerRenderer extends CanvasLayerRenderer {
while (startX > projectionExtent[2]) {
++world;
offsetX = worldWidth * world;
const transform = this.getRenderTransform(center, resolution, rotation, pixelRatio, width, height, offsetX);
replayGroup.execute(context, transform, rotation, snapToPixel, undefined, declutterReplays);
const transform = this.getRenderTransform(
center,
resolution,
rotation,
pixelRatio,
width,
height,
offsetX
);
replayGroup.execute(
context,
transform,
rotation,
snapToPixel,
undefined,
declutterReplays
);
startX -= worldWidth;
}
}
if (declutterReplays) {
const viewHints = frameState.viewHints;
const hifi = !(viewHints[ViewHint.ANIMATING] || viewHints[ViewHint.INTERACTING]);
replayDeclutter(declutterReplays, context, rotation, 1, hifi, frameState.declutterItems);
const hifi = !(
viewHints[ViewHint.ANIMATING] || viewHints[ViewHint.INTERACTING]
);
replayDeclutter(
declutterReplays,
context,
rotation,
1,
hifi,
frameState.declutterItems
);
}
if (clipped) {
@@ -239,48 +324,93 @@ class CanvasVectorLayerRenderer extends CanvasLayerRenderer {
* @return {Promise<Array<import("../../Feature").default>>} Promise that resolves with an array of features.
*/
getFeatures(pixel) {
return new Promise(function(resolve, reject) {
if (!this.hitDetectionImageData_ && !this.animatingOrInteracting_) {
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;
return new Promise(
function (resolve, reject) {
if (!this.hitDetectionImageData_ && !this.animatingOrInteracting_) {
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;
}
}
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));
this.hitDetectionImageData_ = createHitDetectionImageData(
size,
transforms,
this.renderedFeatures_,
layer.getStyleFunction(),
extent,
resolution,
rotation
);
}
resolve(
hitDetect(pixel, this.renderedFeatures_, this.hitDetectionImageData_)
);
}.bind(this)
);
}
/**
@@ -292,7 +422,13 @@ class CanvasVectorLayerRenderer extends CanvasLayerRenderer {
* @return {T|void} Callback result.
* @template T
*/
forEachFeatureAtCoordinate(coordinate, frameState, hitTolerance, callback, declutteredFeatures) {
forEachFeatureAtCoordinate(
coordinate,
frameState,
hitTolerance,
callback,
declutteredFeatures
) {
if (!this.replayGroup_) {
return undefined;
} else {
@@ -302,18 +438,24 @@ class CanvasVectorLayerRenderer extends CanvasLayerRenderer {
/** @type {!Object<string, boolean>} */
const features = {};
const result = this.replayGroup_.forEachFeatureAtCoordinate(coordinate, resolution, rotation, hitTolerance,
const result = this.replayGroup_.forEachFeatureAtCoordinate(
coordinate,
resolution,
rotation,
hitTolerance,
/**
* @param {import("../../Feature.js").FeatureLike} feature Feature.
* @return {?} Callback result.
*/
function(feature) {
function (feature) {
const key = getUid(feature);
if (!(key in features)) {
features[key] = true;
return callback(feature, layer);
}
}, layer.getDeclutter() ? declutteredFeatures : null);
},
layer.getDeclutter() ? declutteredFeatures : null
);
return result;
}
@@ -355,8 +497,10 @@ class CanvasVectorLayerRenderer extends CanvasLayerRenderer {
const updateWhileAnimating = vectorLayer.getUpdateWhileAnimating();
const updateWhileInteracting = vectorLayer.getUpdateWhileInteracting();
if (!this.dirty_ && (!updateWhileAnimating && animating) ||
(!updateWhileInteracting && interacting)) {
if (
(!this.dirty_ && !updateWhileAnimating && animating) ||
(!updateWhileInteracting && interacting)
) {
this.animatingOrInteracting_ = true;
return true;
}
@@ -376,13 +520,18 @@ class CanvasVectorLayerRenderer extends CanvasLayerRenderer {
}
const center = viewState.center.slice();
const extent = buffer(frameStateExtent,
vectorLayerRenderBuffer * resolution);
const extent = buffer(
frameStateExtent,
vectorLayerRenderBuffer * resolution
);
const loadExtents = [extent.slice()];
const projectionExtent = projection.getExtent();
if (vectorSource.getWrapX() && projection.canWrapX() &&
!containsExtent(projectionExtent, frameState.extent)) {
if (
vectorSource.getWrapX() &&
projection.canWrapX() &&
!containsExtent(projectionExtent, frameState.extent)
) {
// For the replay group, we need an extent that intersects the real world
// (-180° to +180°). To support geometries in a coordinate range from -540°
// to +540°, we add at least 1 world width on each side of the projection
@@ -395,18 +544,36 @@ class CanvasVectorLayerRenderer extends CanvasLayerRenderer {
wrapCoordinateX(center, projection);
const loadExtent = wrapExtentX(loadExtents[0], projection);
// If the extent crosses the date line, we load data for both edges of the worlds
if (loadExtent[0] < projectionExtent[0] && loadExtent[2] < projectionExtent[2]) {
loadExtents.push([loadExtent[0] + worldWidth, loadExtent[1], loadExtent[2] + worldWidth, loadExtent[3]]);
} else if (loadExtent[0] > projectionExtent[0] && loadExtent[2] > projectionExtent[2]) {
loadExtents.push([loadExtent[0] - worldWidth, loadExtent[1], loadExtent[2] - worldWidth, loadExtent[3]]);
if (
loadExtent[0] < projectionExtent[0] &&
loadExtent[2] < projectionExtent[2]
) {
loadExtents.push([
loadExtent[0] + worldWidth,
loadExtent[1],
loadExtent[2] + worldWidth,
loadExtent[3],
]);
} else if (
loadExtent[0] > projectionExtent[0] &&
loadExtent[2] > projectionExtent[2]
) {
loadExtents.push([
loadExtent[0] - worldWidth,
loadExtent[1],
loadExtent[2] - worldWidth,
loadExtent[3],
]);
}
}
if (!this.dirty_ &&
this.renderedResolution_ == resolution &&
this.renderedRevision_ == vectorLayerRevision &&
this.renderedRenderOrder_ == vectorLayerRenderOrder &&
containsExtent(this.renderedExtent_, extent)) {
if (
!this.dirty_ &&
this.renderedResolution_ == resolution &&
this.renderedRevision_ == vectorLayerRevision &&
this.renderedRenderOrder_ == vectorLayerRenderOrder &&
containsExtent(this.renderedExtent_, extent)
) {
this.replayGroupChanged = false;
return true;
}
@@ -416,14 +583,22 @@ class CanvasVectorLayerRenderer extends CanvasLayerRenderer {
this.dirty_ = false;
const replayGroup = new CanvasBuilderGroup(
getRenderTolerance(resolution, pixelRatio), extent, resolution,
pixelRatio, vectorLayer.getDeclutter());
getRenderTolerance(resolution, pixelRatio),
extent,
resolution,
pixelRatio,
vectorLayer.getDeclutter()
);
const userProjection = getUserProjection();
let userTransform;
if (userProjection) {
for (let i = 0, ii = loadExtents.length; i < ii; ++i) {
vectorSource.loadFeatures(toUserExtent(loadExtents[i], projection), resolution, userProjection);
vectorSource.loadFeatures(
toUserExtent(loadExtents[i], projection),
resolution,
userProjection
);
}
userTransform = getTransformFromProjections(userProjection, projection);
} else {
@@ -438,14 +613,21 @@ class CanvasVectorLayerRenderer extends CanvasLayerRenderer {
* @param {import("../../Feature.js").default} feature Feature.
* @this {CanvasVectorLayerRenderer}
*/
const render = function(feature) {
const render = function (feature) {
let styles;
const styleFunction = feature.getStyleFunction() || vectorLayer.getStyleFunction();
const styleFunction =
feature.getStyleFunction() || vectorLayer.getStyleFunction();
if (styleFunction) {
styles = styleFunction(feature, resolution);
}
if (styles) {
const dirty = this.renderFeature(feature, squaredTolerance, styles, replayGroup, userTransform);
const dirty = this.renderFeature(
feature,
squaredTolerance,
styles,
replayGroup,
userTransform
);
this.dirty_ = this.dirty_ || dirty;
}
}.bind(this);
@@ -462,9 +644,14 @@ class CanvasVectorLayerRenderer extends CanvasLayerRenderer {
this.renderedFeatures_ = features;
const replayGroupInstructions = replayGroup.finish();
const executorGroup = new ExecutorGroup(extent, resolution,
pixelRatio, vectorSource.getOverlaps(),
replayGroupInstructions, vectorLayer.getRenderBuffer());
const executorGroup = new ExecutorGroup(
extent,
resolution,
pixelRatio,
vectorSource.getOverlaps(),
replayGroupInstructions,
vectorLayer.getRenderBuffer()
);
this.renderedResolution_ = resolution;
this.renderedRevision_ = vectorLayerRevision;
@@ -488,25 +675,41 @@ class CanvasVectorLayerRenderer extends CanvasLayerRenderer {
* @param {import("../../proj.js").TransformFunction=} opt_transform Transform from user to view projection.
* @return {boolean} `true` if an image is loading.
*/
renderFeature(feature, squaredTolerance, styles, builderGroup, opt_transform) {
renderFeature(
feature,
squaredTolerance,
styles,
builderGroup,
opt_transform
) {
if (!styles) {
return false;
}
let loading = false;
if (Array.isArray(styles)) {
for (let i = 0, ii = styles.length; i < ii; ++i) {
loading = renderFeature(
builderGroup, feature, styles[i], squaredTolerance,
this.boundHandleStyleImageChange_, opt_transform) || loading;
loading =
renderFeature(
builderGroup,
feature,
styles[i],
squaredTolerance,
this.boundHandleStyleImageChange_,
opt_transform
) || loading;
}
} else {
loading = renderFeature(
builderGroup, feature, styles, squaredTolerance,
this.boundHandleStyleImageChange_, opt_transform);
builderGroup,
feature,
styles,
squaredTolerance,
this.boundHandleStyleImageChange_,
opt_transform
);
}
return loading;
}
}
export default CanvasVectorLayerRenderer;
+309 -133
View File
@@ -1,61 +1,85 @@
/**
* @module ol/renderer/canvas/VectorTileLayer
*/
import {getUid} from '../../util.js';
import TileState from '../../TileState.js';
import ViewHint from '../../ViewHint.js';
import {listen, unlistenByKey} from '../../events.js';
import EventType from '../../events/EventType.js';
import {buffer, containsCoordinate, equals, getIntersection, intersects, containsExtent, getTopLeft} from '../../extent.js';
import VectorTileRenderType from '../../layer/VectorTileRenderType.js';
import ReplayType from '../../render/canvas/BuilderType.js';
import CanvasBuilderGroup from '../../render/canvas/BuilderGroup.js';
import CanvasExecutorGroup, {
replayDeclutter,
} from '../../render/canvas/ExecutorGroup.js';
import CanvasTileLayerRenderer from './TileLayer.js';
import {toSize} from '../../size.js';
import {getSquaredTolerance as getSquaredRenderTolerance, renderFeature} from '../vector.js';
import EventType from '../../events/EventType.js';
import ReplayType from '../../render/canvas/BuilderType.js';
import TileState from '../../TileState.js';
import VectorTileRenderType from '../../layer/VectorTileRenderType.js';
import ViewHint from '../../ViewHint.js';
import {
apply as applyTransform,
create as createTransform,
multiply,
reset as resetTransform,
scale,
scale as scaleTransform,
translate as translateTransform,
multiply,
scale
} from '../../transform.js';
import CanvasExecutorGroup, {replayDeclutter} from '../../render/canvas/ExecutorGroup.js';
import {
buffer,
containsCoordinate,
containsExtent,
equals,
getIntersection,
getTopLeft,
intersects,
} from '../../extent.js';
import {clear} from '../../obj.js';
import {createHitDetectionImageData, hitDetect} from '../../render/canvas/hitdetect.js';
import {
createHitDetectionImageData,
hitDetect,
} from '../../render/canvas/hitdetect.js';
import {
getSquaredTolerance as getSquaredRenderTolerance,
renderFeature,
} from '../vector.js';
import {getUid} from '../../util.js';
import {listen, unlistenByKey} from '../../events.js';
import {toSize} from '../../size.js';
import {wrapX} from '../../coordinate.js';
/**
* @type {!Object<string, Array<import("../../render/canvas/BuilderType.js").default>>}
*/
const IMAGE_REPLAYS = {
'image': [ReplayType.POLYGON, ReplayType.CIRCLE,
ReplayType.LINE_STRING, ReplayType.IMAGE, ReplayType.TEXT],
'image': [
ReplayType.POLYGON,
ReplayType.CIRCLE,
ReplayType.LINE_STRING,
ReplayType.IMAGE,
ReplayType.TEXT,
],
'hybrid': [ReplayType.POLYGON, ReplayType.LINE_STRING],
'vector': []
'vector': [],
};
/**
* @type {!Object<string, Array<import("../../render/canvas/BuilderType.js").default>>}
*/
const VECTOR_REPLAYS = {
'image': [ReplayType.DEFAULT],
'hybrid': [ReplayType.IMAGE, ReplayType.TEXT, ReplayType.DEFAULT],
'vector': [ReplayType.POLYGON, ReplayType.CIRCLE, ReplayType.LINE_STRING, ReplayType.IMAGE, ReplayType.TEXT, ReplayType.DEFAULT]
'vector': [
ReplayType.POLYGON,
ReplayType.CIRCLE,
ReplayType.LINE_STRING,
ReplayType.IMAGE,
ReplayType.TEXT,
ReplayType.DEFAULT,
],
};
/**
* @classdesc
* Canvas renderer for vector tile layers.
* @api
*/
class CanvasVectorTileLayerRenderer extends CanvasTileLayerRenderer {
/**
* @param {import("../../layer/VectorTile.js").default} layer VectorTile layer.
*/
@@ -118,8 +142,11 @@ class CanvasVectorTileLayerRenderer extends CanvasTileLayerRenderer {
let render;
const tileUid = getUid(tile);
const state = tile.getState();
if (((state === TileState.LOADED && tile.hifi) || state === TileState.ERROR) &&
tileUid in this.tileListenerKeys_) {
if (
((state === TileState.LOADED && tile.hifi) ||
state === TileState.ERROR) &&
tileUid in this.tileListenerKeys_
) {
unlistenByKey(this.tileListenerKeys_[tileUid]);
delete this.tileListenerKeys_[tileUid];
}
@@ -153,12 +180,18 @@ class CanvasVectorTileLayerRenderer extends CanvasTileLayerRenderer {
tile.wantedResolution = resolution;
const tileUid = getUid(tile);
if (!(tileUid in this.tileListenerKeys_)) {
const listenerKey = listen(tile, EventType.CHANGE, this.prepareTile.bind(this, tile, pixelRatio, projection, true));
const listenerKey = listen(
tile,
EventType.CHANGE,
this.prepareTile.bind(this, tile, pixelRatio, projection, true)
);
this.tileListenerKeys_[tileUid] = listenerKey;
}
} else {
const viewHints = frameState.viewHints;
const hifi = !(viewHints[ViewHint.ANIMATING] || viewHints[ViewHint.INTERACTING]);
const hifi = !(
viewHints[ViewHint.ANIMATING] || viewHints[ViewHint.INTERACTING]
);
if (hifi || !tile.wantedResolution) {
tile.wantedResolution = resolution;
}
@@ -176,7 +209,11 @@ class CanvasVectorTileLayerRenderer extends CanvasTileLayerRenderer {
*/
isDrawableTile(tile) {
const layer = this.getLayer();
return super.isDrawableTile(tile) && layer.getRenderMode() === VectorTileRenderType.VECTOR || tile.hasContext(layer);
return (
(super.isDrawableTile(tile) &&
layer.getRenderMode() === VectorTileRenderType.VECTOR) ||
tile.hasContext(layer)
);
}
/**
@@ -213,9 +250,13 @@ class CanvasVectorTileLayerRenderer extends CanvasTileLayerRenderer {
const resolution = tile.wantedResolution;
const builderState = tile.getReplayState(layer);
if (!builderState.dirty && builderState.renderedResolution === resolution &&
builderState.renderedRevision == revision &&
builderState.renderedRenderOrder == renderOrder && builderState.renderedZ === tile.sourceZ) {
if (
!builderState.dirty &&
builderState.renderedResolution === resolution &&
builderState.renderedRevision == revision &&
builderState.renderedRenderOrder == renderOrder &&
builderState.renderedZ === tile.sourceZ
) {
return;
}
@@ -234,27 +275,48 @@ class CanvasVectorTileLayerRenderer extends CanvasTileLayerRenderer {
continue;
}
const sourceTileCoord = sourceTile.tileCoord;
const sourceTileExtent = sourceTileGrid.getTileCoordExtent(sourceTileCoord);
const sourceTileExtent = sourceTileGrid.getTileCoordExtent(
sourceTileCoord
);
const sharedExtent = getIntersection(tileExtent, sourceTileExtent);
const bufferedExtent = equals(sourceTileExtent, sharedExtent) ? null :
buffer(sharedExtent, layer.getRenderBuffer() * resolution, this.tmpExtent);
const bufferedExtent = equals(sourceTileExtent, sharedExtent)
? null
: buffer(
sharedExtent,
layer.getRenderBuffer() * resolution,
this.tmpExtent
);
builderState.dirty = false;
const builderGroup = new CanvasBuilderGroup(0, sharedExtent, resolution,
pixelRatio, layer.getDeclutter());
const squaredTolerance = getSquaredRenderTolerance(resolution, pixelRatio);
const builderGroup = new CanvasBuilderGroup(
0,
sharedExtent,
resolution,
pixelRatio,
layer.getDeclutter()
);
const squaredTolerance = getSquaredRenderTolerance(
resolution,
pixelRatio
);
/**
* @param {import("../../Feature.js").FeatureLike} feature Feature.
* @this {CanvasVectorTileLayerRenderer}
*/
const render = function(feature) {
const render = function (feature) {
let styles;
const styleFunction = feature.getStyleFunction() || layer.getStyleFunction();
const styleFunction =
feature.getStyleFunction() || layer.getStyleFunction();
if (styleFunction) {
styles = styleFunction(feature, resolution);
}
if (styles) {
const dirty = this.renderFeature(feature, squaredTolerance, styles, builderGroup);
const dirty = this.renderFeature(
feature,
squaredTolerance,
styles,
builderGroup
);
this.dirty_ = this.dirty_ || dirty;
builderState.dirty = builderState.dirty || dirty;
}
@@ -266,17 +328,29 @@ class CanvasVectorTileLayerRenderer extends CanvasTileLayerRenderer {
}
for (let i = 0, ii = features.length; i < ii; ++i) {
const feature = features[i];
if (!bufferedExtent || intersects(bufferedExtent, feature.getGeometry().getExtent())) {
if (
!bufferedExtent ||
intersects(bufferedExtent, feature.getGeometry().getExtent())
) {
render.call(this, feature);
}
}
const executorGroupInstructions = builderGroup.finish();
// no need to clip when the render tile is covered by a single source tile
const replayExtent = layer.getRenderMode() !== VectorTileRenderType.VECTOR && layer.getDeclutter() && sourceTiles.length === 1 ?
null :
sharedExtent;
const renderingReplayGroup = new CanvasExecutorGroup(replayExtent, resolution,
pixelRatio, source.getOverlaps(), executorGroupInstructions, layer.getRenderBuffer());
const replayExtent =
layer.getRenderMode() !== VectorTileRenderType.VECTOR &&
layer.getDeclutter() &&
sourceTiles.length === 1
? null
: sharedExtent;
const renderingReplayGroup = new CanvasExecutorGroup(
replayExtent,
resolution,
pixelRatio,
source.getOverlaps(),
executorGroupInstructions,
layer.getRenderBuffer()
);
tile.executorGroups[layerUid].push(renderingReplayGroup);
}
builderState.renderedRevision = revision;
@@ -294,18 +368,27 @@ class CanvasVectorTileLayerRenderer extends CanvasTileLayerRenderer {
* @return {T|void} Callback result.
* @template T
*/
forEachFeatureAtCoordinate(coordinate, frameState, hitTolerance, callback, declutteredFeatures) {
forEachFeatureAtCoordinate(
coordinate,
frameState,
hitTolerance,
callback,
declutteredFeatures
) {
const resolution = frameState.viewState.resolution;
const rotation = frameState.viewState.rotation;
hitTolerance = hitTolerance == undefined ? 0 : hitTolerance;
const layer = this.getLayer();
const declutter = layer.getDeclutter();
const source = layer.getSource();
const tileGrid = source.getTileGridForProjection(frameState.viewState.projection);
const tileGrid = source.getTileGridForProjection(
frameState.viewState.projection
);
/** @type {!Object<string, boolean>} */
const features = {};
const renderedTiles = /** @type {Array<import("../../VectorRenderTile.js").default>} */ (this.renderedTiles);
const renderedTiles = /** @type {Array<import("../../VectorRenderTile.js").default>} */ (this
.renderedTiles);
let found;
let i, ii;
@@ -324,23 +407,35 @@ class CanvasVectorTileLayerRenderer extends CanvasTileLayerRenderer {
const executorGroups = tile.executorGroups[getUid(layer)];
for (let t = 0, tt = executorGroups.length; t < tt; ++t) {
const executorGroup = executorGroups[t];
found = found || executorGroup.forEachFeatureAtCoordinate(coordinate, resolution, rotation, hitTolerance,
/**
* @param {import("../../Feature.js").FeatureLike} feature Feature.
* @return {?} Callback result.
*/
function(feature) {
if (tileContainsCoordinate || (declutteredFeatures && declutteredFeatures.indexOf(feature) !== -1)) {
let key = feature.getId();
if (key === undefined) {
key = getUid(feature);
found =
found ||
executorGroup.forEachFeatureAtCoordinate(
coordinate,
resolution,
rotation,
hitTolerance,
/**
* @param {import("../../Feature.js").FeatureLike} feature Feature.
* @return {?} Callback result.
*/
function (feature) {
if (
tileContainsCoordinate ||
(declutteredFeatures &&
declutteredFeatures.indexOf(feature) !== -1)
) {
let key = feature.getId();
if (key === undefined) {
key = getUid(feature);
}
if (!(key in features)) {
features[key] = true;
return callback(feature, layer);
}
}
if (!(key in features)) {
features[key] = true;
return callback(feature, layer);
}
}
}, layer.getDeclutter() ? declutteredFeatures : null);
},
layer.getDeclutter() ? declutteredFeatures : null
);
}
}
return found;
@@ -352,60 +447,90 @@ class CanvasVectorTileLayerRenderer extends CanvasTileLayerRenderer {
* @return {Promise<Array<import("../../Feature").default>>} Promise that resolves with an array of features.
*/
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();
const resolution = this.renderedResolution;
const tileGrid = source.getTileGridForProjection(projection);
const coordinate = applyTransform(this.renderedPixelToCoordinateTransform_, pixel.slice());
const tileCoord = tileGrid.getTileCoordForCoordAndResolution(coordinate, resolution);
let tile;
for (let i = 0, ii = this.renderedTiles.length; i < ii; ++i) {
if (tileCoord.toString() === this.renderedTiles[i].tileCoord.toString()) {
tile = this.renderedTiles[i];
if (tile.getState() === TileState.LOADED && tile.hifi) {
const extent = tileGrid.getTileCoordExtent(tile.tileCoord);
if (source.getWrapX() && projection.canWrapX() && !containsExtent(projectionExtent, extent)) {
wrapX(coordinate, projection);
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();
const resolution = this.renderedResolution;
const tileGrid = source.getTileGridForProjection(projection);
const coordinate = applyTransform(
this.renderedPixelToCoordinateTransform_,
pixel.slice()
);
const tileCoord = tileGrid.getTileCoordForCoordAndResolution(
coordinate,
resolution
);
let tile;
for (let i = 0, ii = this.renderedTiles.length; i < ii; ++i) {
if (
tileCoord.toString() === this.renderedTiles[i].tileCoord.toString()
) {
tile = this.renderedTiles[i];
if (tile.getState() === TileState.LOADED && tile.hifi) {
const extent = tileGrid.getTileCoordExtent(tile.tileCoord);
if (
source.getWrapX() &&
projection.canWrapX() &&
!containsExtent(projectionExtent, extent)
) {
wrapX(coordinate, projection);
}
break;
}
break;
tile = undefined;
}
tile = undefined;
}
}
if (!tile || tile.loadingSourceTiles > 0) {
resolve([]);
return;
}
const extent = tileGrid.getTileCoordExtent(tile.wrappedTileCoord);
const corner = getTopLeft(extent);
const tilePixel = [
(coordinate[0] - corner[0]) / resolution,
(corner[1] - coordinate[1]) / resolution
];
const features = tile.getSourceTiles().reduce(function(accumulator, sourceTile) {
return accumulator.concat(sourceTile.getFeatures());
}, []);
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_;
const transforms = [
this.getRenderTransform(tileGrid.getTileCoordCenter(tile.wrappedTileCoord),
resolution, 0, 0.5, size[0], size[1], 0)
if (!tile || tile.loadingSourceTiles > 0) {
resolve([]);
return;
}
const extent = tileGrid.getTileCoordExtent(tile.wrappedTileCoord);
const corner = getTopLeft(extent);
const tilePixel = [
(coordinate[0] - corner[0]) / resolution,
(corner[1] - coordinate[1]) / resolution,
];
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));
const features = tile
.getSourceTiles()
.reduce(function (accumulator, sourceTile) {
return accumulator.concat(sourceTile.getFeatures());
}, []);
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_;
const transforms = [
this.getRenderTransform(
tileGrid.getTileCoordCenter(tile.wrappedTileCoord),
resolution,
0,
0.5,
size[0],
size[1],
0
),
];
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)
);
}
/**
@@ -436,14 +561,15 @@ class CanvasVectorTileLayerRenderer extends CanvasTileLayerRenderer {
*/
renderFrame(frameState, target) {
const viewHints = frameState.viewHints;
const hifi = !(viewHints[ViewHint.ANIMATING] || viewHints[ViewHint.INTERACTING]);
const hifi = !(
viewHints[ViewHint.ANIMATING] || viewHints[ViewHint.INTERACTING]
);
this.renderQueuedTileImages_(hifi, frameState);
super.renderFrame(frameState, target);
this.renderedPixelToCoordinateTransform_ = frameState.pixelToCoordinateTransform.slice();
this.renderedRotation_ = frameState.viewState.rotation;
const layer = /** @type {import("../../layer/VectorTile.js").default} */ (this.getLayer());
const renderMode = layer.getRenderMode();
if (renderMode === VectorTileRenderType.IMAGE) {
@@ -473,17 +599,35 @@ class CanvasVectorTileLayerRenderer extends CanvasTileLayerRenderer {
const height = Math.round(size[1] * pixelRatio);
const tiles = this.renderedTiles;
const tileGrid = source.getTileGridForProjection(frameState.viewState.projection);
const tileGrid = source.getTileGridForProjection(
frameState.viewState.projection
);
const clips = [];
const clipZs = [];
for (let i = tiles.length - 1; i >= 0; --i) {
const tile = /** @type {import("../../VectorRenderTile.js").default} */ (tiles[i]);
const tile = /** @type {import("../../VectorRenderTile.js").default} */ (tiles[
i
]);
const tileCoord = tile.tileCoord;
const tileExtent = tileGrid.getTileCoordExtent(tile.wrappedTileCoord);
const worldOffset = tileGrid.getTileCoordExtent(tileCoord, this.tmpExtent)[0] - tileExtent[0];
const worldOffset =
tileGrid.getTileCoordExtent(tileCoord, this.tmpExtent)[0] -
tileExtent[0];
const transform = multiply(
scale(this.inversePixelTransform.slice(), 1 / pixelRatio, 1 / pixelRatio),
this.getRenderTransform(center, resolution, rotation, pixelRatio, width, height, worldOffset)
scale(
this.inversePixelTransform.slice(),
1 / pixelRatio,
1 / pixelRatio
),
this.getRenderTransform(
center,
resolution,
rotation,
pixelRatio,
width,
height,
worldOffset
)
);
const executorGroups = tile.executorGroups[getUid(layer)];
let clipped = false;
@@ -519,7 +663,14 @@ class CanvasVectorTileLayerRenderer extends CanvasTileLayerRenderer {
}
}
}
executorGroup.execute(context, transform, rotation, hifi, replayTypes, declutterReplays);
executorGroup.execute(
context,
transform,
rotation,
hifi,
replayTypes,
declutterReplays
);
if (!declutterReplays && !clipped) {
context.restore();
clips.push(currentClip);
@@ -530,7 +681,14 @@ class CanvasVectorTileLayerRenderer extends CanvasTileLayerRenderer {
}
if (declutterReplays) {
const layerState = frameState.layerStatesArray[frameState.layerIndex];
replayDeclutter(declutterReplays, context, rotation, layerState.opacity, hifi, frameState.declutterItems);
replayDeclutter(
declutterReplays,
context,
rotation,
layerState.opacity,
hifi,
frameState.declutterItems
);
}
return this.container;
@@ -568,14 +726,23 @@ class CanvasVectorTileLayerRenderer extends CanvasTileLayerRenderer {
let loading = false;
if (Array.isArray(styles)) {
for (let i = 0, ii = styles.length; i < ii; ++i) {
loading = renderFeature(
executorGroup, feature, styles[i], squaredTolerance,
this.boundHandleStyleImageChange_) || loading;
loading =
renderFeature(
executorGroup,
feature,
styles[i],
squaredTolerance,
this.boundHandleStyleImageChange_
) || loading;
}
} else {
loading = renderFeature(
executorGroup, feature, styles, squaredTolerance,
this.boundHandleStyleImageChange_);
executorGroup,
feature,
styles,
squaredTolerance,
this.boundHandleStyleImageChange_
);
}
return loading;
}
@@ -593,7 +760,11 @@ class CanvasVectorTileLayerRenderer extends CanvasTileLayerRenderer {
const revision = layer.getRevision();
const sourceZ = tile.sourceZ;
const resolution = tile.wantedResolution;
return replayState.renderedTileResolution !== resolution || replayState.renderedTileRevision !== revision || replayState.renderedTileZ !== sourceZ;
return (
replayState.renderedTileResolution !== resolution ||
replayState.renderedTileRevision !== revision ||
replayState.renderedTileZ !== sourceZ
);
}
/**
@@ -617,7 +788,8 @@ class CanvasVectorTileLayerRenderer extends CanvasTileLayerRenderer {
const projection = viewState.projection;
const tileGrid = source.getTileGridForProjection(projection);
const tileResolution = tileGrid.getResolution(tile.tileCoord[0]);
const renderPixelRatio = frameState.pixelRatio / tile.wantedResolution * tileResolution;
const renderPixelRatio =
(frameState.pixelRatio / tile.wantedResolution) * tileResolution;
const resolution = tileGrid.getResolution(z);
const context = tile.getContext(layer);
@@ -639,12 +811,16 @@ class CanvasVectorTileLayerRenderer extends CanvasTileLayerRenderer {
translateTransform(transform, -tileExtent[0], -tileExtent[3]);
for (let i = 0, ii = executorGroups.length; i < ii; ++i) {
const executorGroup = executorGroups[i];
executorGroup.execute(context, transform, 0, true, IMAGE_REPLAYS[layer.getRenderMode()]);
executorGroup.execute(
context,
transform,
0,
true,
IMAGE_REPLAYS[layer.getRenderMode()]
);
}
replayState.renderedTileResolution = tile.wantedResolution;
}
}
export default CanvasVectorTileLayerRenderer;