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:
@@ -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
@@ -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
@@ -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.
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user