diff --git a/changelog/upgrade-notes.md b/changelog/upgrade-notes.md index 7740f1e13d..25ed045ef9 100644 --- a/changelog/upgrade-notes.md +++ b/changelog/upgrade-notes.md @@ -100,7 +100,7 @@ If you were previously using `Vector` layers with `renderMode: 'image'`, you hav ##### New declutter behavior -If a map has more than one layer with `declutter` set to true, decluttering now considers all layers instead of decluttering each layer separately. The higher the z-index of the layer, the higher the priority of decluttered items. +If a map has more than one layer with `declutter` set to true, decluttering now considers all `Vector` and `VectorTile` instead of decluttering each layer separately. Only `VectorImage` layers continue to be decluttered separately. The higher the z-index of the layer, the higher the priority of decluttered items. Within a layer, the declutter order has changed. Previously, styles with a lower `zIndex` were prioritized over those with a higher `zIndex`. Now the opposite order is used. diff --git a/rendering/cases/layer-vectorimage-decluttering/expected.png b/rendering/cases/layer-vectorimage-decluttering/expected.png index 7b907f8ba8..5ccebf610e 100644 Binary files a/rendering/cases/layer-vectorimage-decluttering/expected.png and b/rendering/cases/layer-vectorimage-decluttering/expected.png differ diff --git a/rendering/cases/layer-vectorimage-decluttering/main.js b/rendering/cases/layer-vectorimage-decluttering/main.js index 43d45ff972..1a6d6c16ca 100644 --- a/rendering/cases/layer-vectorimage-decluttering/main.js +++ b/rendering/cases/layer-vectorimage-decluttering/main.js @@ -93,7 +93,7 @@ layer2.setStyle(function(feature) { zIndex: feature.get('zIndex'), text: new Text({ text: feature.get('text'), - font: 'italic bold 16px Ubuntu' + font: 'italic bold 18px Ubuntu' }) }); }); @@ -123,7 +123,7 @@ layer3.setStyle(function(feature) { }), text: new Text({ text: feature.get('text'), - font: 'italic bold 16px Ubuntu', + font: 'italic bold 18px Ubuntu', textBaseline: 'bottom', offsetY: -12 }) @@ -156,7 +156,7 @@ line.setStyle(new Style({ text: new Text({ placement: 'line', text: 'east-west', - font: 'italic bold 16px Ubuntu', + font: 'italic bold 18px Ubuntu', overflow: true }) })); diff --git a/src/ol/layer/BaseVector.js b/src/ol/layer/BaseVector.js index 6974eeddf1..b612b5f762 100644 --- a/src/ol/layer/BaseVector.js +++ b/src/ol/layer/BaseVector.js @@ -32,9 +32,10 @@ import {createDefaultStyle, toFunction as toStyleFunction} from '../style/Style. * temporary layers. The standard way to add a layer to a map and have it managed by the map is to * use {@link module:ol/Map#addLayer}. * @property {boolean} [declutter=false] Declutter images and text. Decluttering is applied to all - * image and text styles of all layers that have set this to `true`. The priority is defined by the z-index - * of the layer, the `zIndex` of the style and the render order of features. Higher z-index means higher - * priority. Within the same z-index, a feature rendered before another has higher priority. + * image and text styles of all Vector and VectorTile layers that have set this to `true`. The priority + * is defined by the z-index of the layer, the `zIndex` of the style and the render order of features. + * Higher z-index means higher priority. Within the same z-index, a feature rendered before another has + * higher priority. * @property {import("../style/Style.js").StyleLike} [style] Layer style. See * {@link module:ol/style} for default style which will be used if this is not defined. * @property {boolean} [updateWhileAnimating=false] When set to `true`, feature batches will diff --git a/src/ol/layer/VectorImage.js b/src/ol/layer/VectorImage.js index 9775ae251e..1b948ce832 100644 --- a/src/ol/layer/VectorImage.js +++ b/src/ol/layer/VectorImage.js @@ -30,9 +30,9 @@ import CanvasVectorImageLayerRenderer from '../renderer/canvas/VectorImageLayer. * this layer in its layers collection, and the layer will be rendered on top. This is useful for * temporary layers. The standard way to add a layer to a map and have it managed by the map is to * use {@link module:ol/Map#addLayer}. - * @property {boolean} [declutter=false] Declutter images and text. Decluttering is applied to all - * image and text styles, and the priority is defined by the z-index of the style. Lower z-index - * means higher priority. + * @property {boolean} [declutter=false] Declutter images and text on this layer. The priority is defined + * by the `zIndex` of the style and the render order of features. Higher z-index means higher priority. + * Within the same z-index, a feature rendered before another has higher priority. * @property {import("../style/Style.js").StyleLike} [style] Layer style. See * {@link module:ol/style} for default style which will be used if this is not defined. * @property {boolean} [updateWhileAnimating=false] When set to `true`, feature batches will diff --git a/src/ol/layer/VectorTile.js b/src/ol/layer/VectorTile.js index e53dd9741a..067ba0d8e1 100644 --- a/src/ol/layer/VectorTile.js +++ b/src/ol/layer/VectorTile.js @@ -46,9 +46,10 @@ import {assign} from '../obj.js'; * temporary layers. The standard way to add a layer to a map and have it managed by the map is to * use {@link module:ol/Map#addLayer}. * @property {boolean} [declutter=false] Declutter images and text. Decluttering is applied to all - * image and text styles of all layers that have set this to `true`. The priority is defined by the z-index - * of the layer, the `zIndex` of the style and the render order of features. Higher z-index means higher - * priority. Within the same z-index, a feature rendered before another has higher priority. + * image and text styles of all Vector and VectorTile layers that have set this to `true`. The priority + * is defined by the z-index of the layer, the `zIndex` of the style and the render order of features. + * Higher z-index means higher priority. Within the same z-index, a feature rendered before another has + * higher priority. * @property {import("../style/Style.js").StyleLike} [style] Layer style. See * {@link module:ol/style} for default style which will be used if this is not defined. * @property {boolean} [updateWhileAnimating=false] When set to `true`, feature batches will be diff --git a/src/ol/render.js b/src/ol/render.js index 330e5aafa6..0b0beb371a 100644 --- a/src/ol/render.js +++ b/src/ol/render.js @@ -110,3 +110,18 @@ export function getRenderPixel(event, pixel) { applyTransform(event.inversePixelTransform.slice(), result); return result; } + +export function renderDeclutterItems(frameState, declutterTree) { + if (declutterTree) { + declutterTree.clear(); + } + const items = frameState.declutterItems; + for (let z = items.length - 1; z >= 0; --z) { + const zIndexItems = items[z]; + for (let i = 0, ii = zIndexItems.length; i < ii; i += 3) { + declutterTree = zIndexItems[i].renderDeclutter(zIndexItems[i + 1], zIndexItems[i + 2], declutterTree); + } + } + items.length = 0; + return declutterTree; +} diff --git a/src/ol/renderer/Map.js b/src/ol/renderer/Map.js index 488698d081..9955be7a51 100644 --- a/src/ol/renderer/Map.js +++ b/src/ol/renderer/Map.js @@ -10,6 +10,7 @@ import {TRUE} from '../functions.js'; import {visibleAtResolution} from '../layer/Layer.js'; import {shared as iconImageCache} from '../style/IconImageCache.js'; import {compose as composeTransform, makeInverse} from '../transform.js'; +import {renderDeclutterItems} from '../render.js'; /** * @abstract @@ -266,17 +267,7 @@ class MapRenderer extends Disposable { * @param {?import("../PluggableMap.js").FrameState} frameState Frame state. */ renderFrame(frameState) { - if (this.declutterTree_) { - this.declutterTree_.clear(); - } - const items = frameState.declutterItems; - for (let z = items.length - 1; z >= 0; --z) { - const zIndexItems = items[z]; - for (let i = 0, ii = zIndexItems.length; i < ii; i += 3) { - this.declutterTree_ = zIndexItems[i].renderDeclutter(zIndexItems[i + 1], zIndexItems[i + 2], this.declutterTree_); - } - } - items.length = 0; + this.declutterTree_ = renderDeclutterItems(frameState, this.declutterTree_); } /** diff --git a/src/ol/renderer/canvas/VectorImageLayer.js b/src/ol/renderer/canvas/VectorImageLayer.js index c92c36885e..8d7e7b20b3 100644 --- a/src/ol/renderer/canvas/VectorImageLayer.js +++ b/src/ol/renderer/canvas/VectorImageLayer.js @@ -11,6 +11,7 @@ import CanvasVectorLayerRenderer from './VectorLayer.js'; import {listen} from '../../events.js'; import EventType from '../../events/EventType.js'; import ImageState from '../../ImageState.js'; +import {renderDeclutterItems} from '../../render.js'; /** * @classdesc @@ -72,6 +73,7 @@ class CanvasVectorImageLayerRenderer extends CanvasImageLayerRenderer { let skippedFeatures = this.skippedFeatures_; const context = vectorRenderer.context; const imageFrameState = /** @type {import("../../PluggableMap.js").FrameState} */ (assign({}, frameState, { + declutterItems: [], size: [ getWidth(renderedExtent) / viewResolution, getHeight(renderedExtent) / viewResolution @@ -86,6 +88,7 @@ class CanvasVectorImageLayerRenderer extends CanvasImageLayerRenderer { (vectorRenderer.replayGroupChanged || !equals(skippedFeatures, newSkippedFeatures))) { vectorRenderer.renderFrame(imageFrameState, layerState); + renderDeclutterItems(imageFrameState, null); skippedFeatures = newSkippedFeatures; callback(); }