Add decluttering for images and text

This commit is contained in:
Andreas Hocevar
2017-10-09 18:27:31 +02:00
parent 5ebc969599
commit 08af207724
18 changed files with 450 additions and 130 deletions
+18 -3
View File
@@ -4,6 +4,7 @@ goog.require('ol');
goog.require('ol.LayerType');
goog.require('ol.ViewHint');
goog.require('ol.dom');
goog.require('ol.ext.rbush');
goog.require('ol.extent');
goog.require('ol.render.EventType');
goog.require('ol.render.canvas');
@@ -23,6 +24,13 @@ ol.renderer.canvas.VectorLayer = function(vectorLayer) {
ol.renderer.canvas.Layer.call(this, vectorLayer);
/**
* Declutter tree.
* @private
*/
this.declutterTree_ = vectorLayer.getDeclutter() ?
ol.ext.rbush(9, ['[0]', '[1]', '[2]', '[3]']) : null;
/**
* @private
* @type {boolean}
@@ -209,6 +217,9 @@ ol.renderer.canvas.VectorLayer.prototype.composeFrame = function(frameState, lay
if (clipped) {
context.restore();
}
if (this.declutterTree_) {
this.declutterTree_.clear();
}
this.postCompose(context, frameState, layerState, transform);
};
@@ -226,7 +237,7 @@ ol.renderer.canvas.VectorLayer.prototype.forEachFeatureAtCoordinate = function(c
var layer = this.getLayer();
/** @type {Object.<string, boolean>} */
var features = {};
return this.replayGroup_.forEachFeatureAtCoordinate(coordinate, resolution,
var result = this.replayGroup_.forEachFeatureAtCoordinate(coordinate, resolution,
rotation, hitTolerance, {},
/**
* @param {ol.Feature|ol.render.Feature} feature Feature.
@@ -238,7 +249,11 @@ ol.renderer.canvas.VectorLayer.prototype.forEachFeatureAtCoordinate = function(c
features[key] = true;
return callback.call(thisArg, feature, layer);
}
});
}, null);
if (this.declutterTree_) {
this.declutterTree_.clear();
}
return result;
}
};
@@ -317,7 +332,7 @@ ol.renderer.canvas.VectorLayer.prototype.prepareFrame = function(frameState, lay
var replayGroup = new ol.render.canvas.ReplayGroup(
ol.renderer.vector.getTolerance(resolution, pixelRatio), extent, resolution,
pixelRatio, vectorSource.getOverlaps(), vectorLayer.getRenderBuffer());
pixelRatio, vectorSource.getOverlaps(), this.declutterTree_, vectorLayer.getRenderBuffer());
vectorSource.loadFeatures(extent, resolution, projection);
/**
* @param {ol.Feature} feature Feature.
+32 -10
View File
@@ -4,6 +4,7 @@ goog.require('ol');
goog.require('ol.LayerType');
goog.require('ol.TileState');
goog.require('ol.dom');
goog.require('ol.ext.rbush');
goog.require('ol.extent');
goog.require('ol.layer.VectorTileRenderType');
goog.require('ol.proj');
@@ -33,6 +34,12 @@ ol.renderer.canvas.VectorTileLayer = function(layer) {
ol.renderer.canvas.TileLayer.call(this, layer);
/**
* Declutter tree.
* @private
*/
this.declutterTree_ = layer.getDeclutter() ? ol.ext.rbush(9, ['[0]', '[1]', '[2]', '[3]']) : null;
/**
* @private
* @type {boolean}
@@ -54,7 +61,6 @@ ol.renderer.canvas.VectorTileLayer = function(layer) {
// Use lower resolution for pure vector rendering. Closest resolution otherwise.
this.zDirection =
layer.getRenderMode() == ol.layer.VectorTileRenderType.VECTOR ? 1 : 0;
};
ol.inherits(ol.renderer.canvas.VectorTileLayer, ol.renderer.canvas.TileLayer);
@@ -150,6 +156,7 @@ ol.renderer.canvas.VectorTileLayer.prototype.createReplayGroup_ = function(
var resolution = tileGrid.getResolution(tile.tileCoord[0]);
var tileExtent = tileGrid.getTileCoordExtent(tile.wrappedTileCoord);
var zIndexKeys = {};
for (var t = 0, tt = tile.tileKeys.length; t < tt; ++t) {
var sourceTile = tile.getTile(tile.tileKeys[t]);
if (sourceTile.getState() == ol.TileState.ERROR) {
@@ -168,8 +175,8 @@ ol.renderer.canvas.VectorTileLayer.prototype.createReplayGroup_ = function(
sourceTile.setProjection(projection);
}
replayState.dirty = false;
var replayGroup = new ol.render.canvas.ReplayGroup(0, sharedExtent,
resolution, pixelRatio, source.getOverlaps(), layer.getRenderBuffer());
var replayGroup = new ol.render.canvas.ReplayGroup(0, sharedExtent, resolution,
pixelRatio, source.getOverlaps(), this.declutterTree_, layer.getRenderBuffer());
var squaredTolerance = ol.renderer.vector.getSquaredTolerance(
resolution, pixelRatio);
@@ -217,6 +224,9 @@ ol.renderer.canvas.VectorTileLayer.prototype.createReplayGroup_ = function(
}
}
replayGroup.finish();
for (var r in replayGroup.getReplays()) {
zIndexKeys[r] = true;
}
sourceTile.setReplayGroup(layer, tile.tileCoord.toString(), replayGroup);
}
replayState.renderedRevision = revision;
@@ -246,6 +256,7 @@ ol.renderer.canvas.VectorTileLayer.prototype.forEachFeatureAtCoordinate = functi
var rotation = frameState.viewState.rotation;
hitTolerance = hitTolerance == undefined ? 0 : hitTolerance;
var layer = this.getLayer();
var declutterReplays = layer.getDeclutter() ? {} : null;
/** @type {Object.<string, boolean>} */
var features = {};
@@ -283,9 +294,12 @@ ol.renderer.canvas.VectorTileLayer.prototype.forEachFeatureAtCoordinate = functi
features[key] = true;
return callback.call(thisArg, feature, layer);
}
});
}, declutterReplays);
}
}
if (this.declutterTree_) {
this.declutterTree_.clear();
}
return found;
};
@@ -335,14 +349,16 @@ ol.renderer.canvas.VectorTileLayer.prototype.handleStyleImageChange_ = function(
*/
ol.renderer.canvas.VectorTileLayer.prototype.postCompose = function(context, frameState, layerState) {
var layer = this.getLayer();
var declutterReplays = layer.getDeclutter() ? {} : null;
var source = /** @type {ol.source.VectorTile} */ (layer.getSource());
var renderMode = layer.getRenderMode();
var replays = ol.renderer.canvas.VectorTileLayer.VECTOR_REPLAYS[renderMode];
var replayTypes = ol.renderer.canvas.VectorTileLayer.VECTOR_REPLAYS[renderMode];
var pixelRatio = frameState.pixelRatio;
var rotation = frameState.viewState.rotation;
var size = frameState.size;
var offsetX = Math.round(pixelRatio * size[0] / 2);
var offsetY = Math.round(pixelRatio * size[1] / 2);
ol.render.canvas.rotateAtOffset(context, -rotation, offsetX, offsetY);
var tiles = this.renderedTiles;
var tileGrid = source.getTileGridForProjection(frameState.viewState.projection);
var clips = [];
@@ -355,21 +371,23 @@ ol.renderer.canvas.VectorTileLayer.prototype.postCompose = function(context, fra
var tileCoord = tile.tileCoord;
var worldOffset = tileGrid.getTileCoordExtent(tileCoord)[0] -
tileGrid.getTileCoordExtent(tile.wrappedTileCoord)[0];
var transform = undefined;
for (var t = 0, tt = tile.tileKeys.length; t < tt; ++t) {
var sourceTile = tile.getTile(tile.tileKeys[t]);
if (sourceTile.getState() == ol.TileState.ERROR) {
continue;
}
var replayGroup = sourceTile.getReplayGroup(layer, tileCoord.toString());
if (renderMode != ol.layer.VectorTileRenderType.VECTOR && !replayGroup.hasReplays(replays)) {
if (renderMode != ol.layer.VectorTileRenderType.VECTOR && !replayGroup.hasReplays(replayTypes)) {
continue;
}
if (!transform) {
transform = this.getTransform(frameState, worldOffset);
}
var currentZ = sourceTile.tileCoord[0];
var transform = this.getTransform(frameState, worldOffset);
var currentClip = replayGroup.getClipCoords(transform);
context.save();
context.globalAlpha = layerState.opacity;
ol.render.canvas.rotateAtOffset(context, -rotation, offsetX, offsetY);
// Create a clip mask for regions in this low resolution tile that are
// already filled by a higher resolution tile
for (var j = 0, jj = clips.length; j < jj; ++j) {
@@ -389,12 +407,16 @@ ol.renderer.canvas.VectorTileLayer.prototype.postCompose = function(context, fra
context.clip();
}
}
replayGroup.replay(context, transform, rotation, {}, replays);
replayGroup.replay(context, transform, rotation, {}, replayTypes, declutterReplays);
context.restore();
clips.push(currentClip);
zs.push(currentZ);
}
}
if (declutterReplays) {
ol.render.canvas.ReplayGroup.replayDeclutter(declutterReplays, context, rotation);
this.declutterTree_.clear();
}
ol.renderer.canvas.TileLayer.prototype.postCompose.apply(this, arguments);
};
@@ -462,7 +484,7 @@ ol.renderer.canvas.VectorTileLayer.prototype.renderTileImage_ = function(
ol.transform.scale(transform, pixelScale, -pixelScale);
ol.transform.translate(transform, -tileExtent[0], -tileExtent[3]);
var replayGroup = sourceTile.getReplayGroup(layer, tile.tileCoord.toString());
replayGroup.replay(context, transform, 0, {}, replays, true);
replayGroup.replay(context, transform, 0, {}, replays);
}
}
};
+9 -9
View File
@@ -57,7 +57,7 @@ ol.renderer.vector.renderCircleGeometry_ = function(replayGroup, geometry, style
if (textStyle) {
var textReplay = replayGroup.getReplay(
style.getZIndex(), ol.render.ReplayType.TEXT);
textReplay.setTextStyle(textStyle);
textReplay.setTextStyle(textStyle, replayGroup.addDeclutter(false));
textReplay.drawText(geometry, feature);
}
};
@@ -180,7 +180,7 @@ ol.renderer.vector.renderLineStringGeometry_ = function(replayGroup, geometry, s
if (textStyle) {
var textReplay = replayGroup.getReplay(
style.getZIndex(), ol.render.ReplayType.TEXT);
textReplay.setTextStyle(textStyle);
textReplay.setTextStyle(textStyle, replayGroup.addDeclutter(false));
textReplay.drawText(geometry, feature);
}
};
@@ -205,7 +205,7 @@ ol.renderer.vector.renderMultiLineStringGeometry_ = function(replayGroup, geomet
if (textStyle) {
var textReplay = replayGroup.getReplay(
style.getZIndex(), ol.render.ReplayType.TEXT);
textReplay.setTextStyle(textStyle);
textReplay.setTextStyle(textStyle, replayGroup.addDeclutter(false));
textReplay.drawText(geometry, feature);
}
};
@@ -231,7 +231,7 @@ ol.renderer.vector.renderMultiPolygonGeometry_ = function(replayGroup, geometry,
if (textStyle) {
var textReplay = replayGroup.getReplay(
style.getZIndex(), ol.render.ReplayType.TEXT);
textReplay.setTextStyle(textStyle);
textReplay.setTextStyle(textStyle, replayGroup.addDeclutter(false));
textReplay.drawText(geometry, feature);
}
};
@@ -252,14 +252,14 @@ ol.renderer.vector.renderPointGeometry_ = function(replayGroup, geometry, style,
}
var imageReplay = replayGroup.getReplay(
style.getZIndex(), ol.render.ReplayType.IMAGE);
imageReplay.setImageStyle(imageStyle);
imageReplay.setImageStyle(imageStyle, replayGroup.addDeclutter(false));
imageReplay.drawPoint(geometry, feature);
}
var textStyle = style.getText();
if (textStyle) {
var textReplay = replayGroup.getReplay(
style.getZIndex(), ol.render.ReplayType.TEXT);
textReplay.setTextStyle(textStyle);
textReplay.setTextStyle(textStyle, replayGroup.addDeclutter(!!imageStyle));
textReplay.drawText(geometry, feature);
}
};
@@ -280,14 +280,14 @@ ol.renderer.vector.renderMultiPointGeometry_ = function(replayGroup, geometry, s
}
var imageReplay = replayGroup.getReplay(
style.getZIndex(), ol.render.ReplayType.IMAGE);
imageReplay.setImageStyle(imageStyle);
imageReplay.setImageStyle(imageStyle, replayGroup.addDeclutter(false));
imageReplay.drawMultiPoint(geometry, feature);
}
var textStyle = style.getText();
if (textStyle) {
var textReplay = replayGroup.getReplay(
style.getZIndex(), ol.render.ReplayType.TEXT);
textReplay.setTextStyle(textStyle);
textReplay.setTextStyle(textStyle, replayGroup.addDeclutter(!!imageStyle));
textReplay.drawText(geometry, feature);
}
};
@@ -313,7 +313,7 @@ ol.renderer.vector.renderPolygonGeometry_ = function(replayGroup, geometry, styl
if (textStyle) {
var textReplay = replayGroup.getReplay(
style.getZIndex(), ol.render.ReplayType.TEXT);
textReplay.setTextStyle(textStyle);
textReplay.setTextStyle(textStyle, replayGroup.addDeclutter(false));
textReplay.drawText(geometry, feature);
}
};