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
+27 -16
View File
@@ -1,17 +1,16 @@
/**
* @module ol/renderer/Composite
*/
import {CLASS_UNSELECTABLE} from '../css.js';
import {inView} from '../layer/Layer.js';
import MapRenderer from './Map.js';
import ObjectEventType from '../ObjectEventType.js';
import RenderEvent from '../render/Event.js';
import RenderEventType from '../render/EventType.js';
import MapRenderer from './Map.js';
import SourceState from '../source/State.js';
import {replaceChildren} from '../dom.js';
import {listen, unlistenByKey} from '../events.js';
import {CLASS_UNSELECTABLE} from '../css.js';
import {checkedFonts} from '../render/canvas.js';
import ObjectEventType from '../ObjectEventType.js';
import {inView} from '../layer/Layer.js';
import {listen, unlistenByKey} from '../events.js';
import {replaceChildren} from '../dom.js';
/**
* @classdesc
@@ -19,7 +18,6 @@ import ObjectEventType from '../ObjectEventType.js';
* @api
*/
class CompositeMapRenderer extends MapRenderer {
/**
* @param {import("../PluggableMap.js").default} map Map.
*/
@@ -29,7 +27,11 @@ class CompositeMapRenderer extends MapRenderer {
/**
* @type {import("../events.js").EventsKey}
*/
this.fontChangeListenerKey_ = listen(checkedFonts, ObjectEventType.PROPERTYCHANGE, map.redrawText.bind(map));
this.fontChangeListenerKey_ = listen(
checkedFonts,
ObjectEventType.PROPERTYCHANGE,
map.redrawText.bind(map)
);
/**
* @private
@@ -94,7 +96,7 @@ class CompositeMapRenderer extends MapRenderer {
this.calculateMatrices2D(frameState);
this.dispatchRenderEvent(RenderEventType.PRECOMPOSE, frameState);
const layerStatesArray = frameState.layerStatesArray.sort(function(a, b) {
const layerStatesArray = frameState.layerStatesArray.sort(function (a, b) {
return a.zIndex - b.zIndex;
});
const viewState = frameState.viewState;
@@ -104,8 +106,11 @@ class CompositeMapRenderer extends MapRenderer {
for (let i = 0, ii = layerStatesArray.length; i < ii; ++i) {
const layerState = layerStatesArray[i];
frameState.layerIndex = i;
if (!inView(layerState, viewState) ||
(layerState.sourceState != SourceState.READY && layerState.sourceState != SourceState.UNDEFINED)) {
if (
!inView(layerState, viewState) ||
(layerState.sourceState != SourceState.READY &&
layerState.sourceState != SourceState.UNDEFINED)
) {
continue;
}
@@ -155,9 +160,17 @@ class CompositeMapRenderer extends MapRenderer {
for (let i = numLayers - 1; i >= 0; --i) {
const layerState = layerStates[i];
const layer = layerState.layer;
if (layer.hasRenderer() && inView(layerState, viewState) && layerFilter(layer)) {
if (
layer.hasRenderer() &&
inView(layerState, viewState) &&
layerFilter(layer)
) {
const layerRenderer = layer.getRenderer();
const data = layerRenderer.getDataAtPixel(pixel, frameState, hitTolerance);
const data = layerRenderer.getDataAtPixel(
pixel,
frameState,
hitTolerance
);
if (data) {
const result = callback(layer, data);
if (result) {
@@ -168,8 +181,6 @@ class CompositeMapRenderer extends MapRenderer {
}
return undefined;
}
}
export default CompositeMapRenderer;
+12 -10
View File
@@ -1,22 +1,20 @@
/**
* @module ol/renderer/Layer
*/
import {abstract} from '../util.js';
import EventType from '../events/EventType.js';
import ImageState from '../ImageState.js';
import Observable from '../Observable.js';
import EventType from '../events/EventType.js';
import SourceState from '../source/State.js';
import {abstract} from '../util.js';
/**
* @template {import("../layer/Layer.js").default} LayerType
*/
class LayerRenderer extends Observable {
/**
* @param {LayerType} layer Layer.
*/
constructor(layer) {
super();
/** @private */
@@ -27,7 +25,6 @@ class LayerRenderer extends Observable {
* @type {LayerType}
*/
this.layer_ = layer;
}
/**
@@ -92,11 +89,11 @@ class LayerRenderer extends Observable {
* @return {boolean} The tile range is fully loaded.
* @this {LayerRenderer}
*/
function(zoom, tileRange) {
function (zoom, tileRange) {
const callback = this.loadedTileCallback.bind(this, tiles, zoom);
return source.forEachLoadedTile(projection, zoom, tileRange, callback);
}
).bind(this);
}.bind(this)
);
}
/**
* @abstract
@@ -108,7 +105,13 @@ class LayerRenderer extends Observable {
* @return {T|void} Callback result.
* @template T
*/
forEachFeatureAtCoordinate(coordinate, frameState, hitTolerance, callback, declutteredFeatures) {}
forEachFeatureAtCoordinate(
coordinate,
frameState,
hitTolerance,
callback,
declutteredFeatures
) {}
/**
* @abstract
@@ -176,7 +179,6 @@ class LayerRenderer extends Observable {
layer.changed();
}
}
}
export default LayerRenderer;
+48 -18
View File
@@ -1,13 +1,13 @@
/**
* @module ol/renderer/Map
*/
import {abstract} from '../util.js';
import Disposable from '../Disposable.js';
import {getWidth} from '../extent.js';
import {TRUE} from '../functions.js';
import {inView} from '../layer/Layer.js';
import {shared as iconImageCache} from '../style/IconImageCache.js';
import {abstract} from '../util.js';
import {compose as composeTransform, makeInverse} from '../transform.js';
import {getWidth} from '../extent.js';
import {shared as iconImageCache} from '../style/IconImageCache.js';
import {inView} from '../layer/Layer.js';
import {renderDeclutterItems} from '../render.js';
import {wrapX} from '../coordinate.js';
@@ -15,7 +15,6 @@ import {wrapX} from '../coordinate.js';
* @abstract
*/
class MapRenderer extends Disposable {
/**
* @param {import("../PluggableMap.js").default} map Map.
*/
@@ -32,7 +31,6 @@ class MapRenderer extends Disposable {
* @private
*/
this.declutterTree_ = null;
}
/**
@@ -53,11 +51,16 @@ class MapRenderer extends Disposable {
const coordinateToPixelTransform = frameState.coordinateToPixelTransform;
const pixelToCoordinateTransform = frameState.pixelToCoordinateTransform;
composeTransform(coordinateToPixelTransform,
frameState.size[0] / 2, frameState.size[1] / 2,
1 / viewState.resolution, -1 / viewState.resolution,
composeTransform(
coordinateToPixelTransform,
frameState.size[0] / 2,
frameState.size[1] / 2,
1 / viewState.resolution,
-1 / viewState.resolution,
-viewState.rotation,
-viewState.center[0], -viewState.center[1]);
-viewState.center[0],
-viewState.center[1]
);
makeInverse(pixelToCoordinateTransform, coordinateToPixelTransform);
}
@@ -115,7 +118,7 @@ class MapRenderer extends Disposable {
const numLayers = layerStates.length;
let declutteredFeatures;
if (this.declutterTree_) {
declutteredFeatures = this.declutterTree_.all().map(function(entry) {
declutteredFeatures = this.declutterTree_.all().map(function (entry) {
return entry.value;
});
}
@@ -125,17 +128,30 @@ class MapRenderer extends Disposable {
for (let j = numLayers - 1; j >= 0; --j) {
const layerState = layerStates[j];
const layer = /** @type {import("../layer/Layer.js").default} */ (layerState.layer);
if (layer.hasRenderer() && inView(layerState, viewState) && layerFilter.call(thisArg2, layer)) {
if (
layer.hasRenderer() &&
inView(layerState, viewState) &&
layerFilter.call(thisArg2, layer)
) {
const layerRenderer = layer.getRenderer();
const source = layer.getSource();
if (layerRenderer && source) {
const coordinates = source.getWrapX() ? translatedCoordinate : coordinate;
const callback = forEachFeatureAtCoordinate.bind(null, layerState.managed);
const coordinates = source.getWrapX()
? translatedCoordinate
: coordinate;
const callback = forEachFeatureAtCoordinate.bind(
null,
layerState.managed
);
tmpCoord[0] = coordinates[0] + offsets[i][0];
tmpCoord[1] = coordinates[1] + offsets[i][1];
result = layerRenderer.forEachFeatureAtCoordinate(
tmpCoord,
frameState, hitTolerance, callback, declutteredFeatures);
frameState,
hitTolerance,
callback,
declutteredFeatures
);
}
if (result) {
return result;
@@ -177,9 +193,24 @@ class MapRenderer extends Disposable {
* @return {boolean} Is there a feature at the given coordinate?
* @template U
*/
hasFeatureAtCoordinate(coordinate, frameState, hitTolerance, checkWrapped, layerFilter, thisArg) {
hasFeatureAtCoordinate(
coordinate,
frameState,
hitTolerance,
checkWrapped,
layerFilter,
thisArg
) {
const hasFeature = this.forEachFeatureAtCoordinate(
coordinate, frameState, hitTolerance, checkWrapped, TRUE, this, layerFilter, thisArg);
coordinate,
frameState,
hitTolerance,
checkWrapped,
TRUE,
this,
layerFilter,
thisArg
);
return hasFeature !== undefined;
}
@@ -210,7 +241,6 @@ class MapRenderer extends Disposable {
}
}
/**
* @param {import("../PluggableMap.js").default} map Map.
* @param {import("../PluggableMap.js").FrameState} frameState Frame state.
+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;
+99 -43
View File
@@ -1,11 +1,10 @@
/**
* @module ol/renderer/vector
*/
import {getUid} from '../util.js';
import ImageState from '../ImageState.js';
import GeometryType from '../geom/GeometryType.js';
import BuilderType from '../render/canvas/BuilderType.js';
import GeometryType from '../geom/GeometryType.js';
import ImageState from '../ImageState.js';
import {getUid} from '../util.js';
/**
* Tolerance for geometry simplification in device pixels.
@@ -13,7 +12,6 @@ import BuilderType from '../render/canvas/BuilderType.js';
*/
const SIMPLIFY_TOLERANCE = 0.5;
/**
* @const
* @type {Object<import("../geom/GeometryType.js").default,
@@ -28,10 +26,9 @@ const GEOMETRY_RENDERERS = {
'MultiLineString': renderMultiLineStringGeometry,
'MultiPolygon': renderMultiPolygonGeometry,
'GeometryCollection': renderGeometryCollectionGeometry,
'Circle': renderCircleGeometry
'Circle': renderCircleGeometry,
};
/**
* @param {import("../Feature.js").FeatureLike} feature1 Feature 1.
* @param {import("../Feature.js").FeatureLike} feature2 Feature 2.
@@ -41,7 +38,6 @@ export function defaultOrder(feature1, feature2) {
return parseInt(getUid(feature1), 10) - parseInt(getUid(feature2), 10);
}
/**
* @param {number} resolution Resolution.
* @param {number} pixelRatio Pixel ratio.
@@ -52,17 +48,15 @@ export function getSquaredTolerance(resolution, pixelRatio) {
return tolerance * tolerance;
}
/**
* @param {number} resolution Resolution.
* @param {number} pixelRatio Pixel ratio.
* @return {number} Pixel tolerance.
*/
export function getTolerance(resolution, pixelRatio) {
return SIMPLIFY_TOLERANCE * resolution / pixelRatio;
return (SIMPLIFY_TOLERANCE * resolution) / pixelRatio;
}
/**
* @param {import("../render/canvas/BuilderGroup.js").default} builderGroup Builder group.
* @param {import("../geom/Circle.js").default} geometry Geometry.
@@ -73,19 +67,24 @@ function renderCircleGeometry(builderGroup, geometry, style, feature) {
const fillStyle = style.getFill();
const strokeStyle = style.getStroke();
if (fillStyle || strokeStyle) {
const circleReplay = builderGroup.getBuilder(style.getZIndex(), BuilderType.CIRCLE);
const circleReplay = builderGroup.getBuilder(
style.getZIndex(),
BuilderType.CIRCLE
);
circleReplay.setFillStrokeStyle(fillStyle, strokeStyle);
circleReplay.drawCircle(geometry, feature);
}
const textStyle = style.getText();
if (textStyle) {
const textReplay = builderGroup.getBuilder(style.getZIndex(), BuilderType.TEXT);
const textReplay = builderGroup.getBuilder(
style.getZIndex(),
BuilderType.TEXT
);
textReplay.setTextStyle(textStyle, builderGroup.addDeclutter(false));
textReplay.drawText(geometry, feature);
}
}
/**
* @param {import("../render/canvas/BuilderGroup.js").default} replayGroup Replay group.
* @param {import("../Feature.js").FeatureLike} feature Feature.
@@ -96,7 +95,14 @@ function renderCircleGeometry(builderGroup, geometry, style, feature) {
* @return {boolean} `true` if style is loading.
* @template T
*/
export function renderFeature(replayGroup, feature, style, squaredTolerance, listener, opt_transform) {
export function renderFeature(
replayGroup,
feature,
style,
squaredTolerance,
listener,
opt_transform
) {
let loading = false;
const imageStyle = style.getImage();
if (imageStyle) {
@@ -112,12 +118,17 @@ export function renderFeature(replayGroup, feature, style, squaredTolerance, lis
loading = true;
}
}
renderFeatureInternal(replayGroup, feature, style, squaredTolerance, opt_transform);
renderFeatureInternal(
replayGroup,
feature,
style,
squaredTolerance,
opt_transform
);
return loading;
}
/**
* @param {import("../render/canvas/BuilderGroup.js").default} replayGroup Replay group.
* @param {import("../Feature.js").FeatureLike} feature Feature.
@@ -125,12 +136,21 @@ export function renderFeature(replayGroup, feature, style, squaredTolerance, lis
* @param {number} squaredTolerance Squared tolerance.
* @param {import("../proj.js").TransformFunction} [opt_transform] Optional transform function.
*/
function renderFeatureInternal(replayGroup, feature, style, squaredTolerance, opt_transform) {
function renderFeatureInternal(
replayGroup,
feature,
style,
squaredTolerance,
opt_transform
) {
const geometry = style.getGeometryFunction()(feature);
if (!geometry) {
return;
}
const simplifiedGeometry = geometry.simplifyTransformed(squaredTolerance, opt_transform);
const simplifiedGeometry = geometry.simplifyTransformed(
squaredTolerance,
opt_transform
);
const renderer = style.getRenderer();
if (renderer) {
renderGeometry(replayGroup, simplifiedGeometry, style, feature);
@@ -140,7 +160,6 @@ function renderFeatureInternal(replayGroup, feature, style, squaredTolerance, op
}
}
/**
* @param {import("../render/canvas/BuilderGroup.js").default} replayGroup Replay group.
* @param {import("../geom/Geometry.js").default|import("../render/Feature.js").default} geometry Geometry.
@@ -156,27 +175,33 @@ function renderGeometry(replayGroup, geometry, style, feature) {
return;
}
const replay = replayGroup.getBuilder(style.getZIndex(), BuilderType.DEFAULT);
replay.drawCustom(/** @type {import("../geom/SimpleGeometry.js").default} */ (geometry), feature, style.getRenderer());
replay.drawCustom(
/** @type {import("../geom/SimpleGeometry.js").default} */ (geometry),
feature,
style.getRenderer()
);
}
/**
* @param {import("../render/canvas/BuilderGroup.js").default} replayGroup Replay group.
* @param {import("../geom/GeometryCollection.js").default} geometry Geometry.
* @param {import("../style/Style.js").default} style Style.
* @param {import("../Feature.js").default} feature Feature.
*/
function renderGeometryCollectionGeometry(replayGroup, geometry, style, feature) {
function renderGeometryCollectionGeometry(
replayGroup,
geometry,
style,
feature
) {
const geometries = geometry.getGeometriesArray();
let i, ii;
for (i = 0, ii = geometries.length; i < ii; ++i) {
const geometryRenderer =
GEOMETRY_RENDERERS[geometries[i].getType()];
const geometryRenderer = GEOMETRY_RENDERERS[geometries[i].getType()];
geometryRenderer(replayGroup, geometries[i], style, feature);
}
}
/**
* @param {import("../render/canvas/BuilderGroup.js").default} builderGroup Replay group.
* @param {import("../geom/LineString.js").default|import("../render/Feature.js").default} geometry Geometry.
@@ -186,19 +211,24 @@ function renderGeometryCollectionGeometry(replayGroup, geometry, style, feature)
function renderLineStringGeometry(builderGroup, geometry, style, feature) {
const strokeStyle = style.getStroke();
if (strokeStyle) {
const lineStringReplay = builderGroup.getBuilder(style.getZIndex(), BuilderType.LINE_STRING);
const lineStringReplay = builderGroup.getBuilder(
style.getZIndex(),
BuilderType.LINE_STRING
);
lineStringReplay.setFillStrokeStyle(null, strokeStyle);
lineStringReplay.drawLineString(geometry, feature);
}
const textStyle = style.getText();
if (textStyle) {
const textReplay = builderGroup.getBuilder(style.getZIndex(), BuilderType.TEXT);
const textReplay = builderGroup.getBuilder(
style.getZIndex(),
BuilderType.TEXT
);
textReplay.setTextStyle(textStyle, builderGroup.addDeclutter(false));
textReplay.drawText(geometry, feature);
}
}
/**
* @param {import("../render/canvas/BuilderGroup.js").default} builderGroup Replay group.
* @param {import("../geom/MultiLineString.js").default|import("../render/Feature.js").default} geometry Geometry.
@@ -208,19 +238,24 @@ function renderLineStringGeometry(builderGroup, geometry, style, feature) {
function renderMultiLineStringGeometry(builderGroup, geometry, style, feature) {
const strokeStyle = style.getStroke();
if (strokeStyle) {
const lineStringReplay = builderGroup.getBuilder(style.getZIndex(), BuilderType.LINE_STRING);
const lineStringReplay = builderGroup.getBuilder(
style.getZIndex(),
BuilderType.LINE_STRING
);
lineStringReplay.setFillStrokeStyle(null, strokeStyle);
lineStringReplay.drawMultiLineString(geometry, feature);
}
const textStyle = style.getText();
if (textStyle) {
const textReplay = builderGroup.getBuilder(style.getZIndex(), BuilderType.TEXT);
const textReplay = builderGroup.getBuilder(
style.getZIndex(),
BuilderType.TEXT
);
textReplay.setTextStyle(textStyle, builderGroup.addDeclutter(false));
textReplay.drawText(geometry, feature);
}
}
/**
* @param {import("../render/canvas/BuilderGroup.js").default} builderGroup Replay group.
* @param {import("../geom/MultiPolygon.js").default} geometry Geometry.
@@ -231,19 +266,24 @@ function renderMultiPolygonGeometry(builderGroup, geometry, style, feature) {
const fillStyle = style.getFill();
const strokeStyle = style.getStroke();
if (strokeStyle || fillStyle) {
const polygonReplay = builderGroup.getBuilder(style.getZIndex(), BuilderType.POLYGON);
const polygonReplay = builderGroup.getBuilder(
style.getZIndex(),
BuilderType.POLYGON
);
polygonReplay.setFillStrokeStyle(fillStyle, strokeStyle);
polygonReplay.drawMultiPolygon(geometry, feature);
}
const textStyle = style.getText();
if (textStyle) {
const textReplay = builderGroup.getBuilder(style.getZIndex(), BuilderType.TEXT);
const textReplay = builderGroup.getBuilder(
style.getZIndex(),
BuilderType.TEXT
);
textReplay.setTextStyle(textStyle, builderGroup.addDeclutter(false));
textReplay.drawText(geometry, feature);
}
}
/**
* @param {import("../render/canvas/BuilderGroup.js").default} builderGroup Replay group.
* @param {import("../geom/Point.js").default|import("../render/Feature.js").default} geometry Geometry.
@@ -256,19 +296,24 @@ function renderPointGeometry(builderGroup, geometry, style, feature) {
if (imageStyle.getImageState() != ImageState.LOADED) {
return;
}
const imageReplay = builderGroup.getBuilder(style.getZIndex(), BuilderType.IMAGE);
const imageReplay = builderGroup.getBuilder(
style.getZIndex(),
BuilderType.IMAGE
);
imageReplay.setImageStyle(imageStyle, builderGroup.addDeclutter(false));
imageReplay.drawPoint(geometry, feature);
}
const textStyle = style.getText();
if (textStyle) {
const textReplay = builderGroup.getBuilder(style.getZIndex(), BuilderType.TEXT);
const textReplay = builderGroup.getBuilder(
style.getZIndex(),
BuilderType.TEXT
);
textReplay.setTextStyle(textStyle, builderGroup.addDeclutter(!!imageStyle));
textReplay.drawText(geometry, feature);
}
}
/**
* @param {import("../render/canvas/BuilderGroup.js").default} builderGroup Replay group.
* @param {import("../geom/MultiPoint.js").default|import("../render/Feature.js").default} geometry Geometry.
@@ -281,19 +326,24 @@ function renderMultiPointGeometry(builderGroup, geometry, style, feature) {
if (imageStyle.getImageState() != ImageState.LOADED) {
return;
}
const imageReplay = builderGroup.getBuilder(style.getZIndex(), BuilderType.IMAGE);
const imageReplay = builderGroup.getBuilder(
style.getZIndex(),
BuilderType.IMAGE
);
imageReplay.setImageStyle(imageStyle, builderGroup.addDeclutter(false));
imageReplay.drawMultiPoint(geometry, feature);
}
const textStyle = style.getText();
if (textStyle) {
const textReplay = builderGroup.getBuilder(style.getZIndex(), BuilderType.TEXT);
const textReplay = builderGroup.getBuilder(
style.getZIndex(),
BuilderType.TEXT
);
textReplay.setTextStyle(textStyle, builderGroup.addDeclutter(!!imageStyle));
textReplay.drawText(geometry, feature);
}
}
/**
* @param {import("../render/canvas/BuilderGroup.js").default} builderGroup Replay group.
* @param {import("../geom/Polygon.js").default|import("../render/Feature.js").default} geometry Geometry.
@@ -304,13 +354,19 @@ function renderPolygonGeometry(builderGroup, geometry, style, feature) {
const fillStyle = style.getFill();
const strokeStyle = style.getStroke();
if (fillStyle || strokeStyle) {
const polygonReplay = builderGroup.getBuilder(style.getZIndex(), BuilderType.POLYGON);
const polygonReplay = builderGroup.getBuilder(
style.getZIndex(),
BuilderType.POLYGON
);
polygonReplay.setFillStrokeStyle(fillStyle, strokeStyle);
polygonReplay.drawPolygon(geometry, feature);
}
const textStyle = style.getText();
if (textStyle) {
const textReplay = builderGroup.getBuilder(style.getZIndex(), BuilderType.TEXT);
const textReplay = builderGroup.getBuilder(
style.getZIndex(),
BuilderType.TEXT
);
textReplay.setTextStyle(textStyle, builderGroup.addDeclutter(false));
textReplay.drawText(geometry, feature);
}
+24 -13
View File
@@ -4,12 +4,11 @@
import LayerRenderer from '../Layer.js';
import WebGLHelper from '../../webgl/Helper.js';
/**
* @enum {string}
*/
export const WebGLWorkerMessageType = {
GENERATE_BUFFERS: 'GENERATE_BUFFERS'
GENERATE_BUFFERS: 'GENERATE_BUFFERS',
};
/**
@@ -47,7 +46,6 @@ export const WebGLWorkerMessageType = {
* @template {import("../../layer/Layer.js").default} LayerType
*/
class WebGLLayerRenderer extends LayerRenderer {
/**
* @param {LayerType} layer Layer.
* @param {Options=} [opt_options] Options.
@@ -63,7 +61,7 @@ class WebGLLayerRenderer extends LayerRenderer {
*/
this.helper = new WebGLHelper({
postProcesses: options.postProcesses,
uniforms: options.uniforms
uniforms: options.uniforms,
});
}
@@ -83,7 +81,6 @@ class WebGLLayerRenderer extends LayerRenderer {
getShaderCompileErrors() {
return this.helper.getShaderCompileErrors();
}
}
const tmpArray_ = [];
@@ -115,7 +112,14 @@ function writePointVertex(buffer, pos, x, y, index) {
* @property {number} indexPosition New position in the index buffer where future writes should start.
* @private
*/
export function writePointFeatureToBuffers(instructions, elementIndex, vertexBuffer, indexBuffer, customAttributesCount, bufferPositions) {
export function writePointFeatureToBuffers(
instructions,
elementIndex,
vertexBuffer,
indexBuffer,
customAttributesCount,
bufferPositions
) {
// This is for x, y and index
const baseVertexAttrsCount = 3;
const baseInstructionsCount = 2;
@@ -137,23 +141,31 @@ export function writePointFeatureToBuffers(instructions, elementIndex, vertexBuf
// push vertices for each of the four quad corners (first standard then custom attributes)
writePointVertex(vertexBuffer, vPos, x, y, 0);
customAttrs.length && vertexBuffer.set(customAttrs, vPos + baseVertexAttrsCount);
customAttrs.length &&
vertexBuffer.set(customAttrs, vPos + baseVertexAttrsCount);
vPos += stride;
writePointVertex(vertexBuffer, vPos, x, y, 1);
customAttrs.length && vertexBuffer.set(customAttrs, vPos + baseVertexAttrsCount);
customAttrs.length &&
vertexBuffer.set(customAttrs, vPos + baseVertexAttrsCount);
vPos += stride;
writePointVertex(vertexBuffer, vPos, x, y, 2);
customAttrs.length && vertexBuffer.set(customAttrs, vPos + baseVertexAttrsCount);
customAttrs.length &&
vertexBuffer.set(customAttrs, vPos + baseVertexAttrsCount);
vPos += stride;
writePointVertex(vertexBuffer, vPos, x, y, 3);
customAttrs.length && vertexBuffer.set(customAttrs, vPos + baseVertexAttrsCount);
customAttrs.length &&
vertexBuffer.set(customAttrs, vPos + baseVertexAttrsCount);
vPos += stride;
indexBuffer[iPos++] = baseIndex; indexBuffer[iPos++] = baseIndex + 1; indexBuffer[iPos++] = baseIndex + 3;
indexBuffer[iPos++] = baseIndex + 1; indexBuffer[iPos++] = baseIndex + 2; indexBuffer[iPos++] = baseIndex + 3;
indexBuffer[iPos++] = baseIndex;
indexBuffer[iPos++] = baseIndex + 1;
indexBuffer[iPos++] = baseIndex + 3;
indexBuffer[iPos++] = baseIndex + 1;
indexBuffer[iPos++] = baseIndex + 2;
indexBuffer[iPos++] = baseIndex + 3;
bufferPositions_.vertexPosition = vPos;
bufferPositions_.indexPosition = iPos;
@@ -194,7 +206,6 @@ export function colorEncodeId(id, opt_array) {
return array;
}
/**
* Reads an id from a color-encoded array
* Note: the expected range for each component is 0 to 1 with 256 steps.
+189 -112
View File
@@ -1,26 +1,30 @@
/**
* @module ol/renderer/webgl/PointsLayer
*/
import BaseVector from '../../layer/BaseVector.js';
import GeometryType from '../../geom/GeometryType.js';
import VectorEventType from '../../source/VectorEventType.js';
import ViewHint from '../../ViewHint.js';
import WebGLArrayBuffer from '../../webgl/Buffer.js';
import WebGLLayerRenderer, {
WebGLWorkerMessageType,
colorDecodeId,
colorEncodeId,
} from './Layer.js';
import WebGLRenderTarget from '../../webgl/RenderTarget.js';
import {ARRAY_BUFFER, DYNAMIC_DRAW, ELEMENT_ARRAY_BUFFER} from '../../webgl.js';
import {AttributeType, DefaultUniform} from '../../webgl/Helper.js';
import GeometryType from '../../geom/GeometryType.js';
import WebGLLayerRenderer, {colorDecodeId, colorEncodeId, WebGLWorkerMessageType} from './Layer.js';
import ViewHint from '../../ViewHint.js';
import {buffer, createEmpty, equals} from '../../extent.js';
import {
apply as applyTransform,
create as createTransform,
makeInverse as makeInverseTransform,
multiply as multiplyTransform
multiply as multiplyTransform,
} from '../../transform.js';
import {assert} from '../../asserts.js';
import {buffer, createEmpty, equals} from '../../extent.js';
import {create as createWebGLWorker} from '../../worker/webgl.js';
import {getUid} from '../../util.js';
import WebGLRenderTarget from '../../webgl/RenderTarget.js';
import {assert} from '../../asserts.js';
import BaseVector from '../../layer/BaseVector.js';
import {listen, unlistenByKey} from '../../events.js';
import VectorEventType from '../../source/VectorEventType.js';
/**
* @typedef {Object} CustomAttribute A description of a custom attribute to be passed on to the GPU, with a value different
@@ -117,7 +121,6 @@ import VectorEventType from '../../source/VectorEventType.js';
* @api
*/
class WebGLPointsLayerRenderer extends WebGLLayerRenderer {
/**
* @param {import("../../layer/Layer.js").default} layer Layer.
* @param {Options} options Options.
@@ -129,14 +132,17 @@ class WebGLPointsLayerRenderer extends WebGLLayerRenderer {
super(layer, {
uniforms: uniforms,
postProcesses: options.postProcesses
postProcesses: options.postProcesses,
});
this.sourceRevision_ = -1;
this.verticesBuffer_ = new WebGLArrayBuffer(ARRAY_BUFFER, DYNAMIC_DRAW);
this.hitVerticesBuffer_ = new WebGLArrayBuffer(ARRAY_BUFFER, DYNAMIC_DRAW);
this.indicesBuffer_ = new WebGLArrayBuffer(ELEMENT_ARRAY_BUFFER, DYNAMIC_DRAW);
this.indicesBuffer_ = new WebGLArrayBuffer(
ELEMENT_ARRAY_BUFFER,
DYNAMIC_DRAW
);
this.program_ = this.helper.getProgram(
options.fragmentShader,
@@ -147,58 +153,70 @@ class WebGLPointsLayerRenderer extends WebGLLayerRenderer {
* @type {boolean}
* @private
*/
this.hitDetectionEnabled_ = options.hitFragmentShader && options.hitVertexShader ? true : false;
this.hitDetectionEnabled_ =
options.hitFragmentShader && options.hitVertexShader ? true : false;
this.hitProgram_ = this.hitDetectionEnabled_ && this.helper.getProgram(
options.hitFragmentShader,
options.hitVertexShader
);
this.hitProgram_ =
this.hitDetectionEnabled_ &&
this.helper.getProgram(
options.hitFragmentShader,
options.hitVertexShader
);
const customAttributes = options.attributes ?
options.attributes.map(function(attribute) {
return {
name: 'a_' + attribute.name,
size: 1,
type: AttributeType.FLOAT
};
}) : [];
const customAttributes = options.attributes
? options.attributes.map(function (attribute) {
return {
name: 'a_' + attribute.name,
size: 1,
type: AttributeType.FLOAT,
};
})
: [];
/**
* A list of attributes used by the renderer. By default only the position and
* index of the vertex (0 to 3) are required.
* @type {Array<import('../../webgl/Helper.js').AttributeDescription>}
*/
this.attributes = [{
name: 'a_position',
size: 2,
type: AttributeType.FLOAT
}, {
name: 'a_index',
size: 1,
type: AttributeType.FLOAT
}].concat(customAttributes);
this.attributes = [
{
name: 'a_position',
size: 2,
type: AttributeType.FLOAT,
},
{
name: 'a_index',
size: 1,
type: AttributeType.FLOAT,
},
].concat(customAttributes);
/**
* A list of attributes used for hit detection.
* @type {Array<import('../../webgl/Helper.js').AttributeDescription>}
*/
this.hitDetectionAttributes = [{
name: 'a_position',
size: 2,
type: AttributeType.FLOAT
}, {
name: 'a_index',
size: 1,
type: AttributeType.FLOAT
}, {
name: 'a_hitColor',
size: 4,
type: AttributeType.FLOAT
}, {
name: 'a_featureUid',
size: 1,
type: AttributeType.FLOAT
}].concat(customAttributes);
this.hitDetectionAttributes = [
{
name: 'a_position',
size: 2,
type: AttributeType.FLOAT,
},
{
name: 'a_index',
size: 1,
type: AttributeType.FLOAT,
},
{
name: 'a_hitColor',
size: 4,
type: AttributeType.FLOAT,
},
{
name: 'a_featureUid',
size: 1,
type: AttributeType.FLOAT,
},
].concat(customAttributes);
this.customAttributes = options.attributes ? options.attributes : [];
@@ -243,34 +261,45 @@ class WebGLPointsLayerRenderer extends WebGLLayerRenderer {
* @type {WebGLRenderTarget}
* @private
*/
this.hitRenderTarget_ = this.hitDetectionEnabled_ && new WebGLRenderTarget(this.helper);
this.hitRenderTarget_ =
this.hitDetectionEnabled_ && new WebGLRenderTarget(this.helper);
this.worker_ = createWebGLWorker();
this.worker_.addEventListener('message', function(event) {
const received = event.data;
if (received.type === WebGLWorkerMessageType.GENERATE_BUFFERS) {
const projectionTransform = received.projectionTransform;
if (received.hitDetection) {
this.hitVerticesBuffer_.fromArrayBuffer(received.vertexBuffer);
this.helper.flushBufferData(this.hitVerticesBuffer_);
} else {
this.verticesBuffer_.fromArrayBuffer(received.vertexBuffer);
this.helper.flushBufferData(this.verticesBuffer_);
}
this.indicesBuffer_.fromArrayBuffer(received.indexBuffer);
this.helper.flushBufferData(this.indicesBuffer_);
this.worker_.addEventListener(
'message',
function (event) {
const received = event.data;
if (received.type === WebGLWorkerMessageType.GENERATE_BUFFERS) {
const projectionTransform = received.projectionTransform;
if (received.hitDetection) {
this.hitVerticesBuffer_.fromArrayBuffer(received.vertexBuffer);
this.helper.flushBufferData(this.hitVerticesBuffer_);
} else {
this.verticesBuffer_.fromArrayBuffer(received.vertexBuffer);
this.helper.flushBufferData(this.verticesBuffer_);
}
this.indicesBuffer_.fromArrayBuffer(received.indexBuffer);
this.helper.flushBufferData(this.indicesBuffer_);
this.renderTransform_ = projectionTransform;
makeInverseTransform(this.invertRenderTransform_, this.renderTransform_);
if (received.hitDetection) {
this.hitRenderInstructions_ = new Float32Array(event.data.renderInstructions);
} else {
this.renderInstructions_ = new Float32Array(event.data.renderInstructions);
}
this.renderTransform_ = projectionTransform;
makeInverseTransform(
this.invertRenderTransform_,
this.renderTransform_
);
if (received.hitDetection) {
this.hitRenderInstructions_ = new Float32Array(
event.data.renderInstructions
);
} else {
this.renderInstructions_ = new Float32Array(
event.data.renderInstructions
);
}
this.getLayer().changed();
}
}.bind(this));
this.getLayer().changed();
}
}.bind(this)
);
/**
* This object will be updated when the source changes. Key is uid.
@@ -288,19 +317,41 @@ class WebGLPointsLayerRenderer extends WebGLLayerRenderer {
const source = this.getLayer().getSource();
this.sourceListenKeys_ = [
listen(source, VectorEventType.ADDFEATURE, this.handleSourceFeatureAdded_, this),
listen(source, VectorEventType.CHANGEFEATURE, this.handleSourceFeatureChanged_, this),
listen(source, VectorEventType.REMOVEFEATURE, this.handleSourceFeatureDelete_, this),
listen(source, VectorEventType.CLEAR, this.handleSourceFeatureClear_, this)
listen(
source,
VectorEventType.ADDFEATURE,
this.handleSourceFeatureAdded_,
this
),
listen(
source,
VectorEventType.CHANGEFEATURE,
this.handleSourceFeatureChanged_,
this
),
listen(
source,
VectorEventType.REMOVEFEATURE,
this.handleSourceFeatureDelete_,
this
),
listen(
source,
VectorEventType.CLEAR,
this.handleSourceFeatureClear_,
this
),
];
source.forEachFeature(function(feature) {
this.featureCache_[getUid(feature)] = {
feature: feature,
properties: feature.getProperties(),
geometry: feature.getGeometry()
};
this.featureCount_++;
}.bind(this));
source.forEachFeature(
function (feature) {
this.featureCache_[getUid(feature)] = {
feature: feature,
properties: feature.getProperties(),
geometry: feature.getGeometry(),
};
this.featureCount_++;
}.bind(this)
);
}
/**
@@ -312,7 +363,7 @@ class WebGLPointsLayerRenderer extends WebGLLayerRenderer {
this.featureCache_[getUid(feature)] = {
feature: feature,
properties: feature.getProperties(),
geometry: feature.getGeometry()
geometry: feature.getGeometry(),
};
this.featureCount_++;
}
@@ -326,7 +377,7 @@ class WebGLPointsLayerRenderer extends WebGLLayerRenderer {
this.featureCache_[getUid(feature)] = {
feature: feature,
properties: feature.getProperties(),
geometry: feature.getGeometry()
geometry: feature.getGeometry(),
};
}
@@ -382,7 +433,9 @@ class WebGLPointsLayerRenderer extends WebGLLayerRenderer {
const layer = this.getLayer();
const vectorSource = layer.getSource();
const viewState = frameState.viewState;
const viewNotMoving = !frameState.viewHints[ViewHint.ANIMATING] && !frameState.viewHints[ViewHint.INTERACTING];
const viewNotMoving =
!frameState.viewHints[ViewHint.ANIMATING] &&
!frameState.viewHints[ViewHint.INTERACTING];
const extentChanged = !equals(this.previousExtent_, frameState.extent);
const sourceChanged = this.sourceRevision_ < vectorSource.getRevision();
@@ -394,7 +447,8 @@ class WebGLPointsLayerRenderer extends WebGLLayerRenderer {
const projection = viewState.projection;
const resolution = viewState.resolution;
const renderBuffer = layer instanceof BaseVector ? layer.getRenderBuffer() : 0;
const renderBuffer =
layer instanceof BaseVector ? layer.getRenderBuffer() : 0;
const extent = buffer(frameState.extent, renderBuffer * resolution);
vectorSource.loadFeatures(extent, resolution, projection);
@@ -432,14 +486,24 @@ class WebGLPointsLayerRenderer extends WebGLLayerRenderer {
// this can be done since we know that for normal render we only have x, y as base instructions,
// and x, y, r, g, b, a and featureUid for hit render instructions
// and we also know the amount of custom attributes to append to these
const totalInstructionsCount = (2 + this.customAttributes.length) * this.featureCount_;
if (!this.renderInstructions_ || this.renderInstructions_.length !== totalInstructionsCount) {
const totalInstructionsCount =
(2 + this.customAttributes.length) * this.featureCount_;
if (
!this.renderInstructions_ ||
this.renderInstructions_.length !== totalInstructionsCount
) {
this.renderInstructions_ = new Float32Array(totalInstructionsCount);
}
if (this.hitDetectionEnabled_) {
const totalHitInstructionsCount = (7 + this.customAttributes.length) * this.featureCount_;
if (!this.hitRenderInstructions_ || this.hitRenderInstructions_.length !== totalHitInstructionsCount) {
this.hitRenderInstructions_ = new Float32Array(totalHitInstructionsCount);
const totalHitInstructionsCount =
(7 + this.customAttributes.length) * this.featureCount_;
if (
!this.hitRenderInstructions_ ||
this.hitRenderInstructions_.length !== totalHitInstructionsCount
) {
this.hitRenderInstructions_ = new Float32Array(
totalHitInstructionsCount
);
}
}
@@ -452,7 +516,7 @@ class WebGLPointsLayerRenderer extends WebGLLayerRenderer {
let hitColor;
for (const featureUid in this.featureCache_) {
featureCache = this.featureCache_[featureUid];
geometry = /** @type {import("../../geom").Point} */(featureCache.geometry);
geometry = /** @type {import("../../geom").Point} */ (featureCache.geometry);
if (!geometry || geometry.getType() !== GeometryType.POINT) {
continue;
}
@@ -481,7 +545,10 @@ class WebGLPointsLayerRenderer extends WebGLLayerRenderer {
// pushing custom attributes
let value;
for (let j = 0; j < this.customAttributes.length; j++) {
value = this.customAttributes[j].callback(featureCache.feature, featureCache.properties);
value = this.customAttributes[j].callback(
featureCache.feature,
featureCache.properties
);
this.renderInstructions_[renderIndex++] = value;
if (this.hitDetectionEnabled_) {
this.hitRenderInstructions_[hitIndex++] = value;
@@ -493,7 +560,7 @@ class WebGLPointsLayerRenderer extends WebGLLayerRenderer {
const message = {
type: WebGLWorkerMessageType.GENERATE_BUFFERS,
renderInstructions: this.renderInstructions_.buffer,
customAttributesCount: this.customAttributes.length
customAttributesCount: this.customAttributes.length,
};
// additional properties will be sent back as-is by the worker
message['projectionTransform'] = projectionTransform;
@@ -505,11 +572,13 @@ class WebGLPointsLayerRenderer extends WebGLLayerRenderer {
const hitMessage = {
type: WebGLWorkerMessageType.GENERATE_BUFFERS,
renderInstructions: this.hitRenderInstructions_.buffer,
customAttributesCount: 5 + this.customAttributes.length
customAttributesCount: 5 + this.customAttributes.length,
};
hitMessage['projectionTransform'] = projectionTransform;
hitMessage['hitDetection'] = true;
this.worker_.postMessage(hitMessage, [this.hitRenderInstructions_.buffer]);
this.worker_.postMessage(hitMessage, [
this.hitRenderInstructions_.buffer,
]);
this.hitRenderInstructions_ = null;
}
}
@@ -523,21 +592,25 @@ class WebGLPointsLayerRenderer extends WebGLLayerRenderer {
* @return {T|void} Callback result.
* @template T
*/
forEachFeatureAtCoordinate(coordinate, frameState, hitTolerance, callback, declutteredFeatures) {
forEachFeatureAtCoordinate(
coordinate,
frameState,
hitTolerance,
callback,
declutteredFeatures
) {
assert(this.hitDetectionEnabled_, 66);
if (!this.hitRenderInstructions_) {
return;
}
const pixel = applyTransform(frameState.coordinateToPixelTransform, coordinate.slice());
const pixel = applyTransform(
frameState.coordinateToPixelTransform,
coordinate.slice()
);
const data = this.hitRenderTarget_.readPixel(pixel[0] / 2, pixel[1] / 2);
const color = [
data[0] / 255,
data[1] / 255,
data[2] / 255,
data[3] / 255
];
const color = [data[0] / 255, data[1] / 255, data[2] / 255, data[3] / 255];
const index = colorDecodeId(color);
const opacity = this.hitRenderInstructions_[index];
const uid = Math.floor(opacity).toString();
@@ -561,11 +634,15 @@ class WebGLPointsLayerRenderer extends WebGLLayerRenderer {
this.hitRenderTarget_.setSize([
Math.floor(frameState.size[0] / 2),
Math.floor(frameState.size[1] / 2)
Math.floor(frameState.size[1] / 2),
]);
this.helper.useProgram(this.hitProgram_);
this.helper.prepareDrawToRenderTarget(frameState, this.hitRenderTarget_, true);
this.helper.prepareDrawToRenderTarget(
frameState,
this.hitRenderTarget_,
true
);
this.helper.bindBuffer(this.hitVerticesBuffer_);
this.helper.bindBuffer(this.indicesBuffer_);
@@ -582,7 +659,7 @@ class WebGLPointsLayerRenderer extends WebGLLayerRenderer {
disposeInternal() {
this.worker_.terminate();
this.layer_ = null;
this.sourceListenKeys_.forEach(function(key) {
this.sourceListenKeys_.forEach(function (key) {
unlistenByKey(key);
});
this.sourceListenKeys_ = null;