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

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;