New decluttering implementation

This commit is contained in:
Andreas Hocevar
2020-09-20 23:36:27 +02:00
parent 7a5e0db59f
commit 8e862766fc
22 changed files with 784 additions and 330 deletions
+10 -1
View File
@@ -102,6 +102,10 @@ class CompositeMapRenderer extends MapRenderer {
const viewState = frameState.viewState;
this.children_.length = 0;
/**
* @type {Array<import("../layer/BaseVector.js").default>}
*/
const declutterLayers = [];
let previousElement = null;
for (let i = 0, ii = layerStatesArray.length; i < ii; ++i) {
const layerState = layerStatesArray[i];
@@ -123,8 +127,13 @@ class CompositeMapRenderer extends MapRenderer {
this.children_.push(element);
previousElement = element;
}
if ('getDeclutter' in layer) {
declutterLayers.push(layer);
}
}
for (let i = declutterLayers.length - 1; i >= 0; --i) {
declutterLayers[i].renderDeclutter(frameState);
}
super.renderFrame(frameState);
replaceChildren(this.element_, this.children_);
+5
View File
@@ -25,6 +25,11 @@ class LayerRenderer extends Observable {
* @type {LayerType}
*/
this.layer_ = layer;
/**
* @type {import("../render/canvas/ExecutorGroup").default}
*/
this.declutterExecutorGroup = null;
}
/**
+4 -1
View File
@@ -211,9 +211,12 @@ class MapRenderer extends Disposable {
/**
* Render.
* @abstract
* @param {?import("../PluggableMap.js").FrameState} frameState Frame state.
*/
renderFrame(frameState) {}
renderFrame(frameState) {
abstract();
}
/**
* @param {import("../PluggableMap.js").FrameState} frameState Frame state.
@@ -6,6 +6,7 @@ import CanvasVectorLayerRenderer from './VectorLayer.js';
import EventType from '../../events/EventType.js';
import ImageCanvas from '../../ImageCanvas.js';
import ImageState from '../../ImageState.js';
import RBush from 'rbush';
import ViewHint from '../../ViewHint.js';
import {apply, compose, create} from '../../transform.js';
import {assign} from '../../obj.js';
@@ -114,6 +115,7 @@ class CanvasVectorImageLayerRenderer extends CanvasImageLayerRenderer {
{},
frameState,
{
declutterTree: new RBush(9),
extent: renderedExtent,
size: [width, height],
viewState: /** @type {import("../../View.js").State} */ (assign(
@@ -137,6 +139,7 @@ class CanvasVectorImageLayerRenderer extends CanvasImageLayerRenderer {
) {
vectorRenderer.clipping = false;
vectorRenderer.renderFrame(imageFrameState, null);
vectorRenderer.renderDeclutter(imageFrameState);
callback();
}
}
@@ -183,6 +186,10 @@ class CanvasVectorImageLayerRenderer extends CanvasImageLayerRenderer {
*/
postRender() {}
/**
*/
renderDeclutter() {}
/**
* @param {import("../../coordinate.js").Coordinate} coordinate Coordinate.
* @param {import("../../PluggableMap.js").FrameState} frameState Frame state.
+139 -59
View File
@@ -128,6 +128,11 @@ class CanvasVectorLayerRenderer extends CanvasLayerRenderer {
*/
this.replayGroupChanged = true;
/**
* @type {import("../../render/canvas/ExecutorGroup").default}
*/
this.declutterExecutorGroup = null;
/**
* Clipping to be performed by `renderFrame()`
* @type {boolean}
@@ -148,6 +153,73 @@ class CanvasVectorLayerRenderer extends CanvasLayerRenderer {
super.useContainer(target, transform, opacity);
}
/**
* @param {ExecutorGroup} executorGroup Executor group.
* @param {import("../../PluggableMap.js").FrameState} frameState Frame state.
* @param {import("rbush").default=} opt_declutterTree Declutter tree.
*/
renderWorlds(executorGroup, frameState, opt_declutterTree) {
const extent = frameState.extent;
const viewState = frameState.viewState;
const center = viewState.center;
const resolution = viewState.resolution;
const projection = viewState.projection;
const rotation = viewState.rotation;
const projectionExtent = projection.getExtent();
const vectorSource = this.getLayer().getSource();
const pixelRatio = frameState.pixelRatio;
const viewHints = frameState.viewHints;
const snapToPixel = !(
viewHints[ViewHint.ANIMATING] || viewHints[ViewHint.INTERACTING]
);
const context = this.context;
const width = Math.round(frameState.size[0] * pixelRatio);
const height = Math.round(frameState.size[1] * pixelRatio);
const multiWorld = vectorSource.getWrapX() && projection.canWrapX();
const worldWidth = multiWorld ? getWidth(projectionExtent) : null;
const endWorld = multiWorld
? Math.ceil((extent[2] - projectionExtent[2]) / worldWidth) + 1
: 1;
let world = multiWorld
? Math.floor((extent[0] - projectionExtent[0]) / worldWidth)
: 0;
do {
const transform = this.getRenderTransform(
center,
resolution,
rotation,
pixelRatio,
width,
height,
world * worldWidth
);
executorGroup.execute(
context,
1,
transform,
rotation,
snapToPixel,
undefined,
opt_declutterTree
);
} while (++world < endWorld);
}
/**
* Render declutter items for this layer
* @param {import("../../PluggableMap.js").FrameState} frameState Frame state.
*/
renderDeclutter(frameState) {
if (this.declutterExecutorGroup) {
this.renderWorlds(
this.declutterExecutorGroup,
frameState,
frameState.declutterTree
);
}
}
/**
* Render the layer.
* @param {import("../../PluggableMap.js").FrameState} frameState Frame state.
@@ -169,7 +241,11 @@ class CanvasVectorLayerRenderer extends CanvasLayerRenderer {
const canvas = context.canvas;
const replayGroup = this.replayGroup_;
if (!replayGroup || replayGroup.isEmpty()) {
const declutterExecutorGroup = this.declutterExecutorGroup;
if (
(!replayGroup || replayGroup.isEmpty()) &&
(!declutterExecutorGroup || declutterExecutorGroup.isEmpty())
) {
if (!this.containerReused && canvas.width > 0) {
canvas.width = 0;
}
@@ -191,14 +267,8 @@ class CanvasVectorLayerRenderer extends CanvasLayerRenderer {
this.preRender(context, frameState);
const extent = frameState.extent;
const viewState = frameState.viewState;
const center = viewState.center;
const resolution = viewState.resolution;
const projection = viewState.projection;
const rotation = viewState.rotation;
const projectionExtent = projection.getExtent();
const vectorSource = this.getLayer().getSource();
// clipped rendering if layer extent is set
let clipped = false;
@@ -212,38 +282,7 @@ class CanvasVectorLayerRenderer extends CanvasLayerRenderer {
}
}
const viewHints = frameState.viewHints;
const snapToPixel = !(
viewHints[ViewHint.ANIMATING] || viewHints[ViewHint.INTERACTING]
);
const multiWorld = vectorSource.getWrapX() && projection.canWrapX();
const worldWidth = multiWorld ? getWidth(projectionExtent) : null;
const endWorld = multiWorld
? Math.ceil((extent[2] - projectionExtent[2]) / worldWidth) + 1
: 1;
let world = multiWorld
? Math.floor((extent[0] - projectionExtent[0]) / worldWidth)
: 0;
do {
const transform = this.getRenderTransform(
center,
resolution,
rotation,
pixelRatio,
width,
height,
world * worldWidth
);
replayGroup.execute(
context,
1,
transform,
rotation,
snapToPixel,
undefined
);
} while (++world < endWorld);
this.renderWorlds(replayGroup, frameState);
if (clipped) {
context.restore();
@@ -378,26 +417,41 @@ class CanvasVectorLayerRenderer extends CanvasLayerRenderer {
const resolution = frameState.viewState.resolution;
const rotation = frameState.viewState.rotation;
const layer = this.getLayer();
/** @type {!Object<string, boolean>} */
const features = {};
const result = this.replayGroup_.forEachFeatureAtCoordinate(
coordinate,
resolution,
rotation,
hitTolerance,
/**
* @param {import("../../Feature.js").FeatureLike} feature Feature.
* @return {?} Callback result.
*/
function (feature) {
const key = getUid(feature);
if (!(key in features)) {
features[key] = true;
return callback(feature, layer);
}
/**
* @param {import("../../Feature.js").FeatureLike} feature Feature.
* @return {?} Callback result.
*/
const featureCallback = function (feature) {
const key = getUid(feature);
if (!(key in features)) {
features[key] = true;
return callback(feature, layer);
}
);
};
let result;
const executorGroups = [this.replayGroup_];
if (this.declutterExecutorGroup) {
executorGroups.push(this.declutterExecutorGroup);
}
executorGroups.forEach((executorGroup) => {
result =
result ||
executorGroup.forEachFeatureAtCoordinate(
coordinate,
resolution,
rotation,
hitTolerance,
featureCallback,
executorGroup === this.declutterExecutorGroup
? frameState.declutterTree.all().map((item) => item.value)
: null
);
});
return result;
}
@@ -531,6 +585,16 @@ class CanvasVectorLayerRenderer extends CanvasLayerRenderer {
pixelRatio
);
let declutterBuilderGroup;
if (this.getLayer().getDeclutter()) {
declutterBuilderGroup = new CanvasBuilderGroup(
getRenderTolerance(resolution, pixelRatio),
extent,
resolution,
pixelRatio
);
}
const userProjection = getUserProjection();
let userTransform;
if (userProjection) {
@@ -568,7 +632,8 @@ class CanvasVectorLayerRenderer extends CanvasLayerRenderer {
squaredTolerance,
styles,
replayGroup,
userTransform
userTransform,
declutterBuilderGroup
);
this.dirty_ = this.dirty_ || dirty;
}
@@ -595,6 +660,17 @@ class CanvasVectorLayerRenderer extends CanvasLayerRenderer {
vectorLayer.getRenderBuffer()
);
if (declutterBuilderGroup) {
this.declutterExecutorGroup = new ExecutorGroup(
extent,
resolution,
pixelRatio,
vectorSource.getOverlaps(),
declutterBuilderGroup.finish(),
vectorLayer.getRenderBuffer()
);
}
this.renderedResolution_ = resolution;
this.renderedRevision_ = vectorLayerRevision;
this.renderedRenderOrder_ = vectorLayerRenderOrder;
@@ -614,6 +690,7 @@ class CanvasVectorLayerRenderer extends CanvasLayerRenderer {
* @param {import("../../style/Style.js").default|Array<import("../../style/Style.js").default>} styles The style or array of styles.
* @param {import("../../render/canvas/BuilderGroup.js").default} builderGroup Builder group.
* @param {import("../../proj.js").TransformFunction=} opt_transform Transform from user to view projection.
* @param {import("../../render/canvas/BuilderGroup.js").default=} opt_declutterBuilderGroup Builder for decluttering.
* @return {boolean} `true` if an image is loading.
*/
renderFeature(
@@ -621,7 +698,8 @@ class CanvasVectorLayerRenderer extends CanvasLayerRenderer {
squaredTolerance,
styles,
builderGroup,
opt_transform
opt_transform,
opt_declutterBuilderGroup
) {
if (!styles) {
return false;
@@ -636,7 +714,8 @@ class CanvasVectorLayerRenderer extends CanvasLayerRenderer {
styles[i],
squaredTolerance,
this.boundHandleStyleImageChange_,
opt_transform
opt_transform,
opt_declutterBuilderGroup
) || loading;
}
} else {
@@ -646,7 +725,8 @@ class CanvasVectorLayerRenderer extends CanvasLayerRenderer {
styles,
squaredTolerance,
this.boundHandleStyleImageChange_,
opt_transform
opt_transform,
opt_declutterBuilderGroup
);
}
return loading;
+140 -66
View File
@@ -260,6 +260,7 @@ class CanvasVectorTileLayerRenderer extends CanvasTileLayerRenderer {
}
const source = layer.getSource();
const declutter = layer.getDeclutter();
const sourceTileGrid = source.getTileGrid();
const tileGrid = source.getTileGridForProjection(projection);
const tileExtent = tileGrid.getTileCoordExtent(tile.wrappedTileCoord);
@@ -268,6 +269,9 @@ class CanvasVectorTileLayerRenderer extends CanvasTileLayerRenderer {
const layerUid = getUid(layer);
delete tile.hitDetectionImageData[layerUid];
tile.executorGroups[layerUid] = [];
if (declutter) {
tile.declutterExecutorGroups[layerUid] = [];
}
for (let t = 0, tt = sourceTiles.length; t < tt; ++t) {
const sourceTile = sourceTiles[t];
if (sourceTile.getState() != TileState.LOADED) {
@@ -292,6 +296,9 @@ class CanvasVectorTileLayerRenderer extends CanvasTileLayerRenderer {
resolution,
pixelRatio
);
const declutterBuilderGroup = declutter
? new CanvasBuilderGroup(0, sharedExtent, resolution, pixelRatio)
: undefined;
const squaredTolerance = getSquaredRenderTolerance(
resolution,
pixelRatio
@@ -313,7 +320,8 @@ class CanvasVectorTileLayerRenderer extends CanvasTileLayerRenderer {
feature,
squaredTolerance,
styles,
builderGroup
builderGroup,
declutterBuilderGroup
);
this.dirty_ = this.dirty_ || dirty;
builderState.dirty = builderState.dirty || dirty;
@@ -337,7 +345,7 @@ class CanvasVectorTileLayerRenderer extends CanvasTileLayerRenderer {
// no need to clip when the render tile is covered by a single source tile
const replayExtent =
layer.getRenderMode() !== VectorTileRenderType.VECTOR &&
layer.getDeclutter() &&
declutter &&
sourceTiles.length === 1
? null
: sharedExtent;
@@ -350,6 +358,17 @@ class CanvasVectorTileLayerRenderer extends CanvasTileLayerRenderer {
layer.getRenderBuffer()
);
tile.executorGroups[layerUid].push(renderingReplayGroup);
if (declutterBuilderGroup) {
const declutterExecutorGroup = new CanvasExecutorGroup(
replayExtent,
resolution,
pixelRatio,
source.getOverlaps(),
declutterBuilderGroup.finish(),
layer.getRenderBuffer()
);
tile.declutterExecutorGroups[layerUid].push(declutterExecutorGroup);
}
}
builderState.renderedRevision = revision;
builderState.renderedZ = tile.sourceZ;
@@ -395,34 +414,44 @@ class CanvasVectorTileLayerRenderer extends CanvasTileLayerRenderer {
continue;
}
}
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) {
let key = feature.getId();
if (key === undefined) {
key = getUid(feature);
}
if (!(key in features)) {
features[key] = true;
return callback(feature, layer);
}
}
}
);
const layerUid = getUid(layer);
const executorGroups = [tile.executorGroups[layerUid]];
const declutterExecutorGroups = tile.declutterExecutorGroups[layerUid];
if (declutterExecutorGroups) {
executorGroups.push(declutterExecutorGroups);
}
executorGroups.forEach((executorGroups) => {
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) {
let key = feature.getId();
if (key === undefined) {
key = getUid(feature);
}
if (!(key in features)) {
features[key] = true;
return callback(feature, layer);
}
}
},
executorGroups === declutterExecutorGroups
? frameState.declutterTree.all().map((item) => item.value)
: null
);
}
});
}
return found;
}
@@ -539,6 +568,70 @@ class CanvasVectorTileLayerRenderer extends CanvasTileLayerRenderer {
this.renderIfReadyAndVisible();
}
/**
* Render declutter items for this layer
* @param {import("../../PluggableMap.js").FrameState} frameState Frame state.
*/
renderDeclutter(frameState) {
const viewHints = frameState.viewHints;
const hifi = !(
viewHints[ViewHint.ANIMATING] || viewHints[ViewHint.INTERACTING]
);
const tiles = /** @type {Array<import("../../VectorRenderTile.js").default>} */ (this
.renderedTiles);
for (let i = 0, ii = tiles.length; i < ii; ++i) {
const tile = tiles[i];
const declutterExecutorGroups =
tile.declutterExecutorGroups[getUid(this.getLayer())];
if (declutterExecutorGroups) {
for (let j = declutterExecutorGroups.length - 1; j >= 0; --j) {
declutterExecutorGroups[j].execute(
this.context,
1,
this.getTileRenderTransform(tile, frameState),
frameState.viewState.rotation,
hifi,
undefined,
frameState.declutterTree
);
}
}
}
}
getTileRenderTransform(tile, frameState) {
const pixelRatio = frameState.pixelRatio;
const viewState = frameState.viewState;
const center = viewState.center;
const resolution = viewState.resolution;
const rotation = viewState.rotation;
const size = frameState.size;
const width = Math.round(size[0] * pixelRatio);
const height = Math.round(size[1] * pixelRatio);
const source = this.getLayer().getSource();
const tileGrid = source.getTileGridForProjection(
frameState.viewState.projection
);
const tileCoord = tile.tileCoord;
const tileExtent = tileGrid.getTileCoordExtent(tile.wrappedTileCoord);
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
)
);
return transform;
}
/**
* Render the layer.
* @param {import("../../PluggableMap.js").FrameState} frameState Frame state.
@@ -573,47 +666,17 @@ class CanvasVectorTileLayerRenderer extends CanvasTileLayerRenderer {
const context = this.context;
const replayTypes = VECTOR_REPLAYS[renderMode];
const pixelRatio = frameState.pixelRatio;
const viewState = frameState.viewState;
const center = viewState.center;
const resolution = viewState.resolution;
const rotation = viewState.rotation;
const size = frameState.size;
const width = Math.round(size[0] * pixelRatio);
const height = Math.round(size[1] * pixelRatio);
const tiles = this.renderedTiles;
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 tileCoord = tile.tileCoord;
const tileExtent = tileGrid.getTileCoordExtent(tile.wrappedTileCoord);
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
)
);
const transform = this.getTileRenderTransform(tile, frameState);
const executorGroups = tile.executorGroups[getUid(layer)];
let clipped = false;
for (let t = 0, tt = executorGroups.length; t < tt; ++t) {
@@ -692,10 +755,17 @@ class CanvasVectorTileLayerRenderer extends CanvasTileLayerRenderer {
* @param {import("../../Feature.js").FeatureLike} feature Feature.
* @param {number} squaredTolerance Squared tolerance.
* @param {import("../../style/Style.js").default|Array<import("../../style/Style.js").default>} styles The style or array of styles.
* @param {import("../../render/canvas/BuilderGroup.js").default} executorGroup Replay group.
* @param {import("../../render/canvas/BuilderGroup.js").default} builderGroup Replay group.
* @param {import("../../render/canvas/BuilderGroup.js").default=} opt_declutterBuilderGroup Builder group for decluttering.
* @return {boolean} `true` if an image is loading.
*/
renderFeature(feature, squaredTolerance, styles, executorGroup) {
renderFeature(
feature,
squaredTolerance,
styles,
builderGroup,
opt_declutterBuilderGroup
) {
if (!styles) {
return false;
}
@@ -704,20 +774,24 @@ class CanvasVectorTileLayerRenderer extends CanvasTileLayerRenderer {
for (let i = 0, ii = styles.length; i < ii; ++i) {
loading =
renderFeature(
executorGroup,
builderGroup,
feature,
styles[i],
squaredTolerance,
this.boundHandleStyleImageChange_
this.boundHandleStyleImageChange_,
undefined,
opt_declutterBuilderGroup
) || loading;
}
} else {
loading = renderFeature(
executorGroup,
builderGroup,
feature,
styles,
squaredTolerance,
this.boundHandleStyleImageChange_
this.boundHandleStyleImageChange_,
undefined,
opt_declutterBuilderGroup
);
}
return loading;
+103 -26
View File
@@ -62,8 +62,15 @@ export function getTolerance(resolution, pixelRatio) {
* @param {import("../geom/Circle.js").default} geometry Geometry.
* @param {import("../style/Style.js").default} style Style.
* @param {import("../Feature.js").default} feature Feature.
* @param {import("../render/canvas/BuilderGroup.js").default=} opt_declutterBuilderGroup Builder for decluttering.
*/
function renderCircleGeometry(builderGroup, geometry, style, feature) {
function renderCircleGeometry(
builderGroup,
geometry,
style,
feature,
opt_declutterBuilderGroup
) {
const fillStyle = style.getFill();
const strokeStyle = style.getStroke();
if (fillStyle || strokeStyle) {
@@ -76,7 +83,7 @@ function renderCircleGeometry(builderGroup, geometry, style, feature) {
}
const textStyle = style.getText();
if (textStyle) {
const textReplay = builderGroup.getBuilder(
const textReplay = (opt_declutterBuilderGroup || builderGroup).getBuilder(
style.getZIndex(),
BuilderType.TEXT
);
@@ -92,8 +99,8 @@ function renderCircleGeometry(builderGroup, geometry, style, feature) {
* @param {number} squaredTolerance Squared tolerance.
* @param {function(import("../events/Event.js").default): void} listener Listener function.
* @param {import("../proj.js").TransformFunction} [opt_transform] Transform from user to view projection.
* @param {import("../render/canvas/BuilderGroup.js").default=} opt_declutterBuilderGroup Builder for decluttering.
* @return {boolean} `true` if style is loading.
* @template T
*/
export function renderFeature(
replayGroup,
@@ -101,7 +108,8 @@ export function renderFeature(
style,
squaredTolerance,
listener,
opt_transform
opt_transform,
opt_declutterBuilderGroup
) {
let loading = false;
const imageStyle = style.getImage();
@@ -123,7 +131,8 @@ export function renderFeature(
feature,
style,
squaredTolerance,
opt_transform
opt_transform,
opt_declutterBuilderGroup
);
return loading;
@@ -135,13 +144,15 @@ export function renderFeature(
* @param {import("../style/Style.js").default} style Style.
* @param {number} squaredTolerance Squared tolerance.
* @param {import("../proj.js").TransformFunction} [opt_transform] Optional transform function.
* @param {import("../render/canvas/BuilderGroup.js").default=} opt_declutterBuilderGroup Builder for decluttering.
*/
function renderFeatureInternal(
replayGroup,
feature,
style,
squaredTolerance,
opt_transform
opt_transform,
opt_declutterBuilderGroup
) {
const geometry = style.getGeometryFunction()(feature);
if (!geometry) {
@@ -156,7 +167,13 @@ function renderFeatureInternal(
renderGeometry(replayGroup, simplifiedGeometry, style, feature);
} else {
const geometryRenderer = GEOMETRY_RENDERERS[simplifiedGeometry.getType()];
geometryRenderer(replayGroup, simplifiedGeometry, style, feature);
geometryRenderer(
replayGroup,
simplifiedGeometry,
style,
feature,
opt_declutterBuilderGroup
);
}
}
@@ -187,18 +204,26 @@ function renderGeometry(replayGroup, geometry, style, feature) {
* @param {import("../geom/GeometryCollection.js").default} geometry Geometry.
* @param {import("../style/Style.js").default} style Style.
* @param {import("../Feature.js").default} feature Feature.
* @param {import("../render/canvas/BuilderGroup.js").default=} opt_declutterBuilderGroup Builder for decluttering.
*/
function renderGeometryCollectionGeometry(
replayGroup,
geometry,
style,
feature
feature,
opt_declutterBuilderGroup
) {
const geometries = geometry.getGeometriesArray();
let i, ii;
for (i = 0, ii = geometries.length; i < ii; ++i) {
const geometryRenderer = GEOMETRY_RENDERERS[geometries[i].getType()];
geometryRenderer(replayGroup, geometries[i], style, feature);
geometryRenderer(
replayGroup,
geometries[i],
style,
feature,
opt_declutterBuilderGroup
);
}
}
@@ -207,8 +232,15 @@ function renderGeometryCollectionGeometry(
* @param {import("../geom/LineString.js").default|import("../render/Feature.js").default} geometry Geometry.
* @param {import("../style/Style.js").default} style Style.
* @param {import("../Feature.js").FeatureLike} feature Feature.
* @param {import("../render/canvas/BuilderGroup.js").default=} opt_declutterBuilderGroup Builder for decluttering.
*/
function renderLineStringGeometry(builderGroup, geometry, style, feature) {
function renderLineStringGeometry(
builderGroup,
geometry,
style,
feature,
opt_declutterBuilderGroup
) {
const strokeStyle = style.getStroke();
if (strokeStyle) {
const lineStringReplay = builderGroup.getBuilder(
@@ -220,7 +252,7 @@ function renderLineStringGeometry(builderGroup, geometry, style, feature) {
}
const textStyle = style.getText();
if (textStyle) {
const textReplay = builderGroup.getBuilder(
const textReplay = (opt_declutterBuilderGroup || builderGroup).getBuilder(
style.getZIndex(),
BuilderType.TEXT
);
@@ -234,8 +266,15 @@ function renderLineStringGeometry(builderGroup, geometry, style, feature) {
* @param {import("../geom/MultiLineString.js").default|import("../render/Feature.js").default} geometry Geometry.
* @param {import("../style/Style.js").default} style Style.
* @param {import("../Feature.js").FeatureLike} feature Feature.
* @param {import("../render/canvas/BuilderGroup.js").default=} opt_declutterBuilderGroup Builder for decluttering.
*/
function renderMultiLineStringGeometry(builderGroup, geometry, style, feature) {
function renderMultiLineStringGeometry(
builderGroup,
geometry,
style,
feature,
opt_declutterBuilderGroup
) {
const strokeStyle = style.getStroke();
if (strokeStyle) {
const lineStringReplay = builderGroup.getBuilder(
@@ -247,7 +286,7 @@ function renderMultiLineStringGeometry(builderGroup, geometry, style, feature) {
}
const textStyle = style.getText();
if (textStyle) {
const textReplay = builderGroup.getBuilder(
const textReplay = (opt_declutterBuilderGroup || builderGroup).getBuilder(
style.getZIndex(),
BuilderType.TEXT
);
@@ -261,8 +300,15 @@ function renderMultiLineStringGeometry(builderGroup, geometry, style, feature) {
* @param {import("../geom/MultiPolygon.js").default} geometry Geometry.
* @param {import("../style/Style.js").default} style Style.
* @param {import("../Feature.js").default} feature Feature.
* @param {import("../render/canvas/BuilderGroup.js").default=} opt_declutterBuilderGroup Builder for decluttering.
*/
function renderMultiPolygonGeometry(builderGroup, geometry, style, feature) {
function renderMultiPolygonGeometry(
builderGroup,
geometry,
style,
feature,
opt_declutterBuilderGroup
) {
const fillStyle = style.getFill();
const strokeStyle = style.getStroke();
if (strokeStyle || fillStyle) {
@@ -275,7 +321,7 @@ function renderMultiPolygonGeometry(builderGroup, geometry, style, feature) {
}
const textStyle = style.getText();
if (textStyle) {
const textReplay = builderGroup.getBuilder(
const textReplay = (opt_declutterBuilderGroup || builderGroup).getBuilder(
style.getZIndex(),
BuilderType.TEXT
);
@@ -289,9 +335,22 @@ function renderMultiPolygonGeometry(builderGroup, geometry, style, feature) {
* @param {import("../geom/Point.js").default|import("../render/Feature.js").default} geometry Geometry.
* @param {import("../style/Style.js").default} style Style.
* @param {import("../Feature.js").FeatureLike} feature Feature.
* @param {import("../render/canvas/BuilderGroup.js").default=} opt_declutterBuilderGroup Builder for decluttering.
*/
function renderPointGeometry(builderGroup, geometry, style, feature) {
function renderPointGeometry(
builderGroup,
geometry,
style,
feature,
opt_declutterBuilderGroup
) {
const imageStyle = style.getImage();
const textStyle = style.getText();
let sharedData;
if (opt_declutterBuilderGroup) {
builderGroup = opt_declutterBuilderGroup;
sharedData = imageStyle && textStyle ? {} : undefined;
}
if (imageStyle) {
if (imageStyle.getImageState() != ImageState.LOADED) {
return;
@@ -300,16 +359,15 @@ function renderPointGeometry(builderGroup, geometry, style, feature) {
style.getZIndex(),
BuilderType.IMAGE
);
imageReplay.setImageStyle(imageStyle);
imageReplay.setImageStyle(imageStyle, sharedData);
imageReplay.drawPoint(geometry, feature);
}
const textStyle = style.getText();
if (textStyle) {
const textReplay = builderGroup.getBuilder(
style.getZIndex(),
BuilderType.TEXT
);
textReplay.setTextStyle(textStyle);
textReplay.setTextStyle(textStyle, sharedData);
textReplay.drawText(geometry, feature);
}
}
@@ -319,9 +377,22 @@ function renderPointGeometry(builderGroup, geometry, style, feature) {
* @param {import("../geom/MultiPoint.js").default|import("../render/Feature.js").default} geometry Geometry.
* @param {import("../style/Style.js").default} style Style.
* @param {import("../Feature.js").FeatureLike} feature Feature.
* @param {import("../render/canvas/BuilderGroup.js").default=} opt_declutterBuilderGroup Builder for decluttering.
*/
function renderMultiPointGeometry(builderGroup, geometry, style, feature) {
function renderMultiPointGeometry(
builderGroup,
geometry,
style,
feature,
opt_declutterBuilderGroup
) {
const imageStyle = style.getImage();
const textStyle = style.getText();
let sharedData;
if (opt_declutterBuilderGroup) {
builderGroup = opt_declutterBuilderGroup;
sharedData = imageStyle && textStyle ? {} : undefined;
}
if (imageStyle) {
if (imageStyle.getImageState() != ImageState.LOADED) {
return;
@@ -330,16 +401,15 @@ function renderMultiPointGeometry(builderGroup, geometry, style, feature) {
style.getZIndex(),
BuilderType.IMAGE
);
imageReplay.setImageStyle(imageStyle);
imageReplay.setImageStyle(imageStyle, sharedData);
imageReplay.drawMultiPoint(geometry, feature);
}
const textStyle = style.getText();
if (textStyle) {
const textReplay = builderGroup.getBuilder(
const textReplay = (opt_declutterBuilderGroup || builderGroup).getBuilder(
style.getZIndex(),
BuilderType.TEXT
);
textReplay.setTextStyle(textStyle);
textReplay.setTextStyle(textStyle, sharedData);
textReplay.drawText(geometry, feature);
}
}
@@ -349,8 +419,15 @@ function renderMultiPointGeometry(builderGroup, geometry, style, feature) {
* @param {import("../geom/Polygon.js").default|import("../render/Feature.js").default} geometry Geometry.
* @param {import("../style/Style.js").default} style Style.
* @param {import("../Feature.js").FeatureLike} feature Feature.
* @param {import("../render/canvas/BuilderGroup.js").default=} opt_declutterBuilderGroup Builder for decluttering.
*/
function renderPolygonGeometry(builderGroup, geometry, style, feature) {
function renderPolygonGeometry(
builderGroup,
geometry,
style,
feature,
opt_declutterBuilderGroup
) {
const fillStyle = style.getFill();
const strokeStyle = style.getStroke();
if (fillStyle || strokeStyle) {
@@ -363,7 +440,7 @@ function renderPolygonGeometry(builderGroup, geometry, style, feature) {
}
const textStyle = style.getText();
if (textStyle) {
const textReplay = builderGroup.getBuilder(
const textReplay = (opt_declutterBuilderGroup || builderGroup).getBuilder(
style.getZIndex(),
BuilderType.TEXT
);