From e1593ce59d396e5c14d68fd946299ef0c2fe38d1 Mon Sep 17 00:00:00 2001 From: VLCEK Martin Date: Mon, 11 Apr 2022 16:36:43 +0200 Subject: [PATCH 1/5] Support declutter mode for image styles Allows to specify for each image style, whether the image should be decluttered, always drawn but still serving as obstacle, or drawn without being an obstacle for other images/texts. The layer must still have declutter = true set for this property to have any effect. --- src/ol/render/canvas/Executor.js | 102 +++++----- src/ol/render/canvas/ImageBuilder.js | 11 + src/ol/render/canvas/TextBuilder.js | 2 + src/ol/renderer/vector.js | 62 ++++-- src/ol/style/Circle.js | 3 + src/ol/style/Icon.js | 2 + src/ol/style/Image.js | 18 +- src/ol/style/RegularShape.js | 3 + .../spec/ol/render/canvas/textbuilder.test.js | 2 +- .../expected.png | Bin 0 -> 8397 bytes .../main.js | 188 ++++++++++++++++++ 11 files changed, 330 insertions(+), 63 deletions(-) create mode 100644 test/rendering/cases/layer-vector-decluttering-extended/expected.png create mode 100644 test/rendering/cases/layer-vector-decluttering-extended/main.js diff --git a/src/ol/render/canvas/Executor.js b/src/ol/render/canvas/Executor.js index b424dfb7bd..c406ebb905 100644 --- a/src/ol/render/canvas/Executor.js +++ b/src/ol/render/canvas/Executor.js @@ -808,17 +808,21 @@ class Executor { instruction[12] ); let width = /** @type {number} */ (instruction[13]); - const declutterImageWithText = - /** @type {import("../canvas.js").DeclutterImageWithText} */ ( + const declutterMode = + /** @type {"declutter"|"obstacle"|"none"|undefined} */ ( instruction[14] ); + const declutterImageWithText = + /** @type {import("../canvas.js").DeclutterImageWithText} */ ( + instruction[15] + ); - if (!image && instruction.length >= 19) { + if (!image && instruction.length >= 20) { // create label images - text = /** @type {string} */ (instruction[18]); - textKey = /** @type {string} */ (instruction[19]); - strokeKey = /** @type {string} */ (instruction[20]); - fillKey = /** @type {string} */ (instruction[21]); + text = /** @type {string} */ (instruction[19]); + textKey = /** @type {string} */ (instruction[20]); + strokeKey = /** @type {string} */ (instruction[21]); + fillKey = /** @type {string} */ (instruction[22]); const labelWithAnchor = this.drawLabelWithPointPlacement_( text, textKey, @@ -827,10 +831,10 @@ class Executor { ); image = labelWithAnchor.label; instruction[3] = image; - const textOffsetX = /** @type {number} */ (instruction[22]); + const textOffsetX = /** @type {number} */ (instruction[23]); anchorX = (labelWithAnchor.anchorX - textOffsetX) * this.pixelRatio; instruction[4] = anchorX; - const textOffsetY = /** @type {number} */ (instruction[23]); + const textOffsetY = /** @type {number} */ (instruction[24]); anchorY = (labelWithAnchor.anchorY - textOffsetY) * this.pixelRatio; instruction[5] = anchorY; height = image.height; @@ -840,15 +844,15 @@ class Executor { } let geometryWidths; - if (instruction.length > 24) { - geometryWidths = /** @type {number} */ (instruction[24]); + if (instruction.length > 25) { + geometryWidths = /** @type {number} */ (instruction[25]); } let padding, backgroundFill, backgroundStroke; - if (instruction.length > 16) { - padding = /** @type {Array} */ (instruction[15]); - backgroundFill = /** @type {boolean} */ (instruction[16]); - backgroundStroke = /** @type {boolean} */ (instruction[17]); + if (instruction.length > 17) { + padding = /** @type {Array} */ (instruction[16]); + backgroundFill = /** @type {boolean} */ (instruction[17]); + backgroundStroke = /** @type {boolean} */ (instruction[18]); } else { padding = defaultPadding; backgroundFill = false; @@ -902,39 +906,43 @@ class Executor { ? /** @type {Array<*>} */ (lastStrokeInstruction) : null, ]; - let imageArgs; - let imageDeclutterBox; - if (opt_declutterTree && declutterImageWithText) { - const index = dd - d; - if (!declutterImageWithText[index]) { - // We now have the image for an image+text combination. - declutterImageWithText[index] = args; - // Don't render anything for now, wait for the text. - continue; - } - imageArgs = declutterImageWithText[index]; - delete declutterImageWithText[index]; - imageDeclutterBox = getDeclutterBox(imageArgs); - if (opt_declutterTree.collides(imageDeclutterBox)) { - continue; - } - } - if ( - opt_declutterTree && - opt_declutterTree.collides(dimensions.declutterBox) - ) { - continue; - } - if (imageArgs) { - // We now have image and text for an image+text combination. - if (opt_declutterTree) { - opt_declutterTree.insert(imageDeclutterBox); - } - // Render the image before we render the text. - this.replayImageOrLabel_.apply(this, imageArgs); - } if (opt_declutterTree) { - opt_declutterTree.insert(dimensions.declutterBox); + if (declutterMode === 'none') { + // not rendered in declutter group + continue; + } else if (declutterMode === 'obstacle') { + // will always be drawn, thus no collision detection, but insert as obstacle + opt_declutterTree.insert(dimensions.declutterBox); + continue; + } else { + let imageArgs; + let imageDeclutterBox; + if (declutterImageWithText) { + const index = dd - d; + if (!declutterImageWithText[index]) { + // We now have the image for an image+text combination. + declutterImageWithText[index] = args; + // Don't render anything for now, wait for the text. + continue; + } + imageArgs = declutterImageWithText[index]; + delete declutterImageWithText[index]; + imageDeclutterBox = getDeclutterBox(imageArgs); + if (opt_declutterTree.collides(imageDeclutterBox)) { + continue; + } + } + if (opt_declutterTree.collides(dimensions.declutterBox)) { + continue; + } + if (imageArgs) { + // We now have image and text for an image+text combination. + opt_declutterTree.insert(imageDeclutterBox); + // Render the image before we render the text. + this.replayImageOrLabel_.apply(this, imageArgs); + } + opt_declutterTree.insert(dimensions.declutterBox); + } } this.replayImageOrLabel_.apply(this, args); } diff --git a/src/ol/render/canvas/ImageBuilder.js b/src/ol/render/canvas/ImageBuilder.js index 8407fc560b..0768333f0d 100644 --- a/src/ol/render/canvas/ImageBuilder.js +++ b/src/ol/render/canvas/ImageBuilder.js @@ -92,6 +92,12 @@ class CanvasImageBuilder extends CanvasBuilder { */ this.width_ = undefined; + /** + * @private + * @type {"declutter"|"obstacle"|"none"|undefined} + */ + this.declutterMode_ = undefined; + /** * Data shared with a text builder for combined decluttering. * @private @@ -132,6 +138,7 @@ class CanvasImageBuilder extends CanvasBuilder { (this.scale_[1] * this.pixelRatio) / this.imagePixelRatio_, ], Math.ceil(this.width_ * this.imagePixelRatio_), + this.declutterMode_, this.declutterImageWithText_, ]); this.hitDetectionInstructions.push([ @@ -150,6 +157,7 @@ class CanvasImageBuilder extends CanvasBuilder { this.rotation_, this.scale_, this.width_, + this.declutterMode_, this.declutterImageWithText_, ]); this.endGeometry(feature); @@ -187,6 +195,7 @@ class CanvasImageBuilder extends CanvasBuilder { (this.scale_[1] * this.pixelRatio) / this.imagePixelRatio_, ], Math.ceil(this.width_ * this.imagePixelRatio_), + this.declutterMode_, this.declutterImageWithText_, ]); this.hitDetectionInstructions.push([ @@ -205,6 +214,7 @@ class CanvasImageBuilder extends CanvasBuilder { this.rotation_, this.scale_, this.width_, + this.declutterMode_, this.declutterImageWithText_, ]); this.endGeometry(feature); @@ -255,6 +265,7 @@ class CanvasImageBuilder extends CanvasBuilder { this.rotation_ = imageStyle.getRotation(); this.scale_ = imageStyle.getScaleArray(); this.width_ = size[0]; + this.declutterMode_ = imageStyle.getDeclutterMode(); this.declutterImageWithText_ = opt_sharedData; } } diff --git a/src/ol/render/canvas/TextBuilder.js b/src/ol/render/canvas/TextBuilder.js index 3e1bb4a45e..b45ccc805d 100644 --- a/src/ol/render/canvas/TextBuilder.js +++ b/src/ol/render/canvas/TextBuilder.js @@ -374,6 +374,7 @@ class CanvasTextBuilder extends CanvasBuilder { this.textRotation_, [1, 1], NaN, + undefined, this.declutterImageWithText_, padding == defaultPadding ? defaultPadding @@ -406,6 +407,7 @@ class CanvasTextBuilder extends CanvasBuilder { this.textRotation_, [scale, scale], NaN, + undefined, this.declutterImageWithText_, padding, !!textState.backgroundFill, diff --git a/src/ol/renderer/vector.js b/src/ol/renderer/vector.js index c59b36296f..86fcc2266c 100644 --- a/src/ol/renderer/vector.js +++ b/src/ol/renderer/vector.js @@ -362,16 +362,29 @@ function renderPointGeometry( const textStyle = style.getText(); /** @type {import("../render/canvas.js").DeclutterImageWithText} */ let declutterImageWithText; - if (opt_declutterBuilderGroup) { - builderGroup = opt_declutterBuilderGroup; - declutterImageWithText = - imageStyle && textStyle && textStyle.getText() ? {} : undefined; - } if (imageStyle) { if (imageStyle.getImageState() != ImageState.LOADED) { return; } - const imageReplay = builderGroup.getBuilder( + let imageBuilderGroup = builderGroup; + if (opt_declutterBuilderGroup) { + const declutterMode = imageStyle.getDeclutterMode(); + if (declutterMode !== 'none') { + imageBuilderGroup = opt_declutterBuilderGroup; + if (declutterMode === 'obstacle') { + // draw in non-declutter group: + const imageReplay = builderGroup.getBuilder( + style.getZIndex(), + BuilderType.IMAGE + ); + imageReplay.setImageStyle(imageStyle, declutterImageWithText); + imageReplay.drawPoint(geometry, feature); + } else if (textStyle && textStyle.getText()) { + declutterImageWithText = {}; + } + } + } + const imageReplay = imageBuilderGroup.getBuilder( style.getZIndex(), BuilderType.IMAGE ); @@ -379,7 +392,11 @@ function renderPointGeometry( imageReplay.drawPoint(geometry, feature); } if (textStyle && textStyle.getText()) { - const textReplay = builderGroup.getBuilder( + let textBuilderGroup = builderGroup; + if (opt_declutterBuilderGroup) { + textBuilderGroup = opt_declutterBuilderGroup; + } + const textReplay = textBuilderGroup.getBuilder( style.getZIndex(), BuilderType.TEXT ); @@ -406,16 +423,29 @@ function renderMultiPointGeometry( const textStyle = style.getText(); /** @type {import("../render/canvas.js").DeclutterImageWithText} */ let declutterImageWithText; - if (opt_declutterBuilderGroup) { - builderGroup = opt_declutterBuilderGroup; - declutterImageWithText = - imageStyle && textStyle && textStyle.getText() ? {} : undefined; - } if (imageStyle) { if (imageStyle.getImageState() != ImageState.LOADED) { return; } - const imageReplay = builderGroup.getBuilder( + let imageBuilderGroup = builderGroup; + if (opt_declutterBuilderGroup) { + const declutterMode = imageStyle.getDeclutterMode(); + if (declutterMode !== 'none') { + imageBuilderGroup = opt_declutterBuilderGroup; + if (declutterMode === 'obstacle') { + // draw in non-declutter group: + const imageReplay = builderGroup.getBuilder( + style.getZIndex(), + BuilderType.IMAGE + ); + imageReplay.setImageStyle(imageStyle, declutterImageWithText); + imageReplay.drawMultiPoint(geometry, feature); + } else if (textStyle && textStyle.getText()) { + declutterImageWithText = {}; + } + } + } + const imageReplay = imageBuilderGroup.getBuilder( style.getZIndex(), BuilderType.IMAGE ); @@ -423,7 +453,11 @@ function renderMultiPointGeometry( imageReplay.drawMultiPoint(geometry, feature); } if (textStyle && textStyle.getText()) { - const textReplay = (opt_declutterBuilderGroup || builderGroup).getBuilder( + let textBuilderGroup = builderGroup; + if (opt_declutterBuilderGroup) { + textBuilderGroup = opt_declutterBuilderGroup; + } + const textReplay = textBuilderGroup.getBuilder( style.getZIndex(), BuilderType.TEXT ); diff --git a/src/ol/style/Circle.js b/src/ol/style/Circle.js index ef890c3a58..81503eb725 100644 --- a/src/ol/style/Circle.js +++ b/src/ol/style/Circle.js @@ -16,6 +16,7 @@ import RegularShape from './RegularShape.js'; * (positive rotation clockwise, meaningful only when used in conjunction with a two dimensional scale). * @property {boolean} [rotateWithView=false] Whether to rotate the shape with the view * (meaningful only when used in conjunction with a two dimensional scale). + * @property {"declutter"|"obstacle"|"none"|undefined} [declutterMode] Declutter mode */ /** @@ -41,6 +42,7 @@ class CircleStyle extends RegularShape { options.rotateWithView !== undefined ? options.rotateWithView : false, displacement: options.displacement !== undefined ? options.displacement : [0, 0], + declutterMode: options.declutterMode, }); } @@ -59,6 +61,7 @@ class CircleStyle extends RegularShape { rotation: this.getRotation(), rotateWithView: this.getRotateWithView(), displacement: this.getDisplacement().slice(), + declutterMode: this.getDeclutterMode(), }); style.setOpacity(this.getOpacity()); return style; diff --git a/src/ol/style/Icon.js b/src/ol/style/Icon.js index 58c52a60f7..03440a9d5f 100644 --- a/src/ol/style/Icon.js +++ b/src/ol/style/Icon.js @@ -44,6 +44,7 @@ import {getUid} from '../util.js'; * @property {import("../size.js").Size} [imgSize] Image size in pixels. Only required if `img` is set and `src` is not, and * for SVG images in Internet Explorer 11. The provided `imgSize` needs to match the actual size of the image. * @property {string} [src] Image source URI. + * @property {"declutter"|"obstacle"|"none"|undefined} [declutterMode] Declutter mode */ /** @@ -86,6 +87,7 @@ class Icon extends ImageStyle { displacement: options.displacement !== undefined ? options.displacement : [0, 0], rotateWithView: rotateWithView, + declutterMode: options.declutterMode, }); /** diff --git a/src/ol/style/Image.js b/src/ol/style/Image.js index 73a948341d..f42ed7a8ea 100644 --- a/src/ol/style/Image.js +++ b/src/ol/style/Image.js @@ -11,7 +11,7 @@ import {toSize} from '../size.js'; * @property {number} rotation Rotation. * @property {number|import("../size.js").Size} scale Scale. * @property {Array} displacement Displacement. - */ + * @property {"declutter"|"obstacle"|"none"|undefined} declutterMode Declutter mode: `declutter`, `obstacle`, 'none */ /** * @classdesc @@ -61,6 +61,12 @@ class ImageStyle { * @type {Array} */ this.displacement_ = options.displacement; + + /** + * @private + * @type {"declutter"|"obstacle"|"none"|undefined} + */ + this.declutterMode_ = options.declutterMode; } /** @@ -76,6 +82,7 @@ class ImageStyle { rotation: this.getRotation(), rotateWithView: this.getRotateWithView(), displacement: this.getDisplacement().slice(), + declutterMode: this.getDeclutterMode(), }); } @@ -132,6 +139,15 @@ class ImageStyle { return this.displacement_; } + /** + * Get the declutter mode of the shape + * @return {"declutter"|"obstacle"|"none"|undefined} Shape's declutter mode + * @api + */ + getDeclutterMode() { + return this.declutterMode_; + } + /** * Get the anchor point in pixels. The anchor determines the center point for the * symbolizer. diff --git a/src/ol/style/RegularShape.js b/src/ol/style/RegularShape.js index 920e40565e..e04f0bc579 100644 --- a/src/ol/style/RegularShape.js +++ b/src/ol/style/RegularShape.js @@ -31,6 +31,7 @@ import { * @property {boolean} [rotateWithView=false] Whether to rotate the shape with the view. * @property {number|import("../size.js").Size} [scale=1] Scale. Unless two dimensional scaling is required a better * result may be obtained with appropriate settings for `radius`, `radius1` and `radius2`. + * @property {"declutter"|"obstacle"|"none"|undefined} [declutterMode] Declutter mode */ /** @@ -69,6 +70,7 @@ class RegularShape extends ImageStyle { scale: options.scale !== undefined ? options.scale : 1, displacement: options.displacement !== undefined ? options.displacement : [0, 0], + declutterMode: options.declutterMode, }); /** @@ -159,6 +161,7 @@ class RegularShape extends ImageStyle { rotateWithView: this.getRotateWithView(), scale: Array.isArray(scale) ? scale.slice() : scale, displacement: this.getDisplacement().slice(), + declutterMode: this.getDeclutterMode(), }); style.setOpacity(this.getOpacity()); return style; diff --git a/test/browser/spec/ol/render/canvas/textbuilder.test.js b/test/browser/spec/ol/render/canvas/textbuilder.test.js index 9aae74124e..f2582d8e65 100644 --- a/test/browser/spec/ol/render/canvas/textbuilder.test.js +++ b/test/browser/spec/ol/render/canvas/textbuilder.test.js @@ -320,7 +320,7 @@ describe('ol.render.canvas.TextBuilder', function () { builder.drawText(feature.getGeometry(), feature); expect(builder.coordinates).to.have.length(2); expect(builder.instructions).to.have.length(3); - const geometryWidths = builder.instructions[1][24]; + const geometryWidths = builder.instructions[1][25]; expect(geometryWidths).to.have.length(1); expect(geometryWidths[0]).to.be(120); }); diff --git a/test/rendering/cases/layer-vector-decluttering-extended/expected.png b/test/rendering/cases/layer-vector-decluttering-extended/expected.png new file mode 100644 index 0000000000000000000000000000000000000000..f54ee278b009320d972f6598c8c2601eb106d663 GIT binary patch literal 8397 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 literal 0 HcmV?d00001 diff --git a/test/rendering/cases/layer-vector-decluttering-extended/main.js b/test/rendering/cases/layer-vector-decluttering-extended/main.js new file mode 100644 index 0000000000..fad395b447 --- /dev/null +++ b/test/rendering/cases/layer-vector-decluttering-extended/main.js @@ -0,0 +1,188 @@ +import CircleStyle from '../../../../src/ol/style/Circle.js'; +import Feature from '../../../../src/ol/Feature.js'; +import Fill from '../../../../src/ol/style/Fill.js'; +import Map from '../../../../src/ol/Map.js'; +import Point from '../../../../src/ol/geom/Point.js'; +import Stroke from '../../../../src/ol/style/Stroke.js'; +import Style from '../../../../src/ol/style/Style.js'; +import Text from '../../../../src/ol/style/Text.js'; +import VectorLayer from '../../../../src/ol/layer/Vector.js'; +import VectorSource from '../../../../src/ol/source/Vector.js'; +import View from '../../../../src/ol/View.js'; + +const center = [1825927.7316762917, 6143091.089223046]; +const map = new Map({ + pixelRatio: 1, + target: 'map', + view: new View({ + center: center, + zoom: 13.1, + }), +}); + +const sourceBlue = new VectorSource(); +sourceBlue.addFeatures([ + new Feature({ + geometry: new Point([center[0] + 1000 + 540, center[1] + 900 - 600]), + text: 'top-blue', + }), +]); +// circles are always drawn, but serve as obstacles, +// texts are decluttered against each other and the circles +map.addLayer( + new VectorLayer({ + zIndex: 4, + declutter: true, + source: sourceBlue, + style: function (feature) { + return new Style({ + image: new CircleStyle({ + radius: 10, + stroke: new Stroke({ + color: 'blue', + width: 8, + }), + declutterMode: 'declutter', + }), + text: new Text({ + text: feature.get('text'), + font: 'italic bold 18px Ubuntu', + textBaseline: 'bottom', + offsetY: -15, + }), + }); + }, + }) +); + +const sourceRed = new VectorSource(); +sourceRed.addFeatures([ + new Feature({ + geometry: new Point([center[0] + 1000, center[1] + 1000 - 200]), + text: 'c-red', + }), + new Feature({ + geometry: new Point([center[0] + 1000 - 540, center[1] + 1000]), + text: 'w-red', + }), + new Feature({ + geometry: new Point([center[0] + 1000 + 540, center[1] + 1000 - 400]), + text: 'e-red', + }), +]); +// circles are always drawn, but serve as obstacles, +// texts are decluttered against each other and the circles +map.addLayer( + new VectorLayer({ + zIndex: 3, + declutter: true, + source: sourceRed, + style: function (feature) { + return new Style({ + image: new CircleStyle({ + radius: 10, + stroke: new Stroke({ + color: 'red', + width: 8, + }), + declutterMode: 'obstacle', + }), + text: new Text({ + text: feature.get('text'), + font: 'italic bold 18px Ubuntu', + textBaseline: 'bottom', + offsetY: -15, + }), + }); + }, + }) +); + +const sourceOrange = new VectorSource(); +sourceOrange.addFeatures([ + new Feature({ + geometry: new Point([center[0], center[1]]), + text: 'c-orange', + }), + new Feature({ + geometry: new Point([center[0] - 540, center[1]]), + text: 'w-orange', + }), + new Feature({ + geometry: new Point([center[0] + 540, center[1]]), + text: 'e-orange', + }), +]); +// circles are always drawn, +// texts are decluttered against each other and the layer 1 circles/texts +map.addLayer( + new VectorLayer({ + zIndex: 2, + declutter: true, + source: sourceOrange, + style: function (feature) { + return new Style({ + image: new CircleStyle({ + radius: 15, + fill: new Fill({ + color: 'orange', + }), + declutterMode: 'none', + }), + text: new Text({ + text: feature.get('text'), + font: 'italic bold 18px Ubuntu', + textBaseline: 'bottom', + offsetY: -17, + }), + }); + }, + }) +); + +const sourceCyan = new VectorSource(); +sourceCyan.addFeatures([ + new Feature({ + geometry: new Point([center[0] + 1000 - 700, center[1] - 100]), + text: 'w-cyan', + }), + new Feature({ + geometry: new Point([center[0] + 1000, center[1] - 400]), + text: 'c-cyan', + }), + new Feature({ + geometry: new Point([center[0] + 1000 + 700, center[1] - 700]), + text: 'e-cyan', + }), +]); +// circles are always drawn +// texts are decluttered against each others (and layers 1/2) +// the circles of layer 2 and this layer are no obstactles for texts +// the texts are decluttered and thus above the circles of layer 2 +map.addLayer( + new VectorLayer({ + zIndex: 1, + declutter: true, + source: sourceCyan, + style: function (feature) { + return new Style({ + image: new CircleStyle({ + radius: 15, + fill: new Fill({ + color: 'cyan', + }), + declutterMode: 'none', + }), + text: new Text({ + text: feature.get('text'), + font: 'italic bold 18px Ubuntu', + textBaseline: 'middle', + textAlign: 'right', + offsetX: -19, + }), + }); + }, + }) +); + +render({tolerance: 0.007}); From 933828666190fd72281c2bf40a6b6be733917bd1 Mon Sep 17 00:00:00 2001 From: VLCEK Martin Date: Thu, 5 May 2022 13:26:12 +0200 Subject: [PATCH 2/5] improve test with overlapping circles. --- .../expected.png | Bin 8397 -> 7727 bytes .../main.js | 3 ++- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/test/rendering/cases/layer-vector-decluttering-extended/expected.png b/test/rendering/cases/layer-vector-decluttering-extended/expected.png index f54ee278b009320d972f6598c8c2601eb106d663..6ae024d2d3106d5f174ff315566c655bb97c4f15 100644 GIT binary patch literal 7727 zcmeHsS6mbAx9-q8C`Eb^q=O(`S^z4xh zA&D3uBE5IA^Zl>(-skRIp5M8enf04_*E_2|>v>Zx%=8&(xo80ZU@$b${RaR@h_@sF zH6`)l6!OXQ->1-j^mTyR37$0o;9WD+)wYT(+C&HXv3fnl?*^ujnexc_|0316NdkYU zq;CRrC@;{M+`3I|ugc>QE0>q5k-7a^HGO}9{P3 zxv43`YXkRE8J`ggVE6+&?eo^4B~oBggpgF43y7KE}<7tP%l{4AGw9w%r+PNCa7I_`%5WqpN&9iJVp11GVXZ{NP9CMP8) zB{4EK_6Z1xkO;qbkL-z7LQ)bXpsK3+=kH%Z9!5B1+ePQSg$ZXqB^hcWbbKMnBM*H>IxT9`Ri1q7Eo5-o|<(buQaEXll=%=o~`$>bKZp?$4;vICIx zcWrHH@FlmIUUh9P<*(Tq$FPV9RS1DsE#lW8{1vB>bACm*;$*B(b1^g0rr2b5*HWO zdA`+=sPjI*D?T{*jLzpQuieP33{!)M&vamp%pn$e#(JM=kr!!cFmMVXN_7i=@a=4kXp)uDJhFSv z!1?sz;s%cwc1cZD)vhB#8rlu=^y}MtWDp?^(BryLZ}Rfs0cce`K0?;Y)>af@DViNn z+%<%IUt2rD=@!%xeOS5QZES1|yB?211$LW8MMd>BWNG{-{ml;aQil62=(PI5w7~Ue z;(#uFIdJVxJ}FV~mim?uJAGe++xHp80}^7)|A9Xw%XwqA5pLuVO)pk(dgORA9{BDv z%|!9|hT0^E$=Z77Y<*`~s12dX$wraz0dgdDfyVaYOES04C2e}tw(Xt>WWONQN`fp? z`=Q^$8X5VMq#YYA-?%>~)%r&*idjOU(b3bGuG+N(E$8u*706m#8Qm%4Un$=(&I5nT zTFj9aDjiD5`m^Oc-z*JT2}F6q?uitUYBBSwQ&*VdYcHlNYC?w-Edhg4?3EH6=Mppi zmMLiS?cv>su`{Iim>I-PT2I}nOa&{6EJFO~`mL;8CJZ6w*Fv(8*=Z1v^ zxtK-BM*d+swx)9A%*x7+EkdOvJ^x_7Wow}7f)>bbhKFLag7GSy$K;xqp^lgb7# zOAfTh-ytOtlQh0OuasEl=mss>qWPHA-bme4h*NDkKi>b*hIw(+YlVhAD#03U=5Hu$&2T_To$bF>Uf;1 zX#0>=4juLr@i*Asp3|icVudeEXCW`&j&hL9J9%jbqJS zI-wjNI0o@0su7mES3s)u=p$W1$;GYx#@`t)O$rBV{-C?d>R&aD=HX-{%gS5xhrjqt z+)*4DI2j!&GQY%SisrDyrxWtJKdf#OG*_5$yNlyz#Jsn*wjR;egjh&9HjkZlAxa*Z zn!XIhEK#x*78bhQ&Nc&G_=*o_+^JY?S&X78iB;yA4(AaG3#F}dAzdR$)pL=()LeGDtL2ZB__h-CrU z66m^d*xbB_iP`>$n05LGFsL3#OD0@y$CpIe2M@KM6BzPtU-u3a{f?P9s_PPTvgpC+ zrBR!fm>I@{DrB4lzGKV|#|wd?w;F|8-p|`L-6(;sCnRsLcBn`CV$M)M(_IJpkL?PEu#9hJMa39*a7`ahsjeM9_aUd z+?eq) zH~;%hG`WkVMQ!KeCHLAvG!3r`V5j{E?#NDb&+I4BGb`!z5Ig!9pUg*H%}5IikCmE=iaV(A{5b zQS;%{h<63M%^O2pNBg`}!eZ1n?7s|)pFgjy%{PLXRl?FY1D?g8_P^=Ykr|gh zqgr~Y-00xm`Yr)xuA!&@MJBMSD=K@_HBN7bpYVEoC{y$@AUs?V)eYu5yiD?Q)G1k(S7Uj*R)wd4B;*EsQDxh{lHo55KdD*L# z_muSYx4CaSmwu^}Ii72Zs8Jl1RfXP}S$3uqK$=J%S682%MdRnEI{P>Yf5eb}(#NM8 z=6ZS*Xf%4_&Mv$wi`UKkS}JG);eQl_Hwlm1ji);9HXJOD$2fcq>~xpy?&0(xEHS~A zU#h^wyCSqP6k3gK^GER6$fJ1Km6a7K=k~Dqkj;@lm~6gkq(gXkxS6o=3{w+7RX7A< z`QX88*>$>VPwc;u8?r$oxO=pTVrQ`%A0MxFL8z`_r_hUECV2@EZ$Cd1E(srhq+BEZ z%I)>4`W=>#Ee!ADaxV##FT2V0D0bl6JR5pk1No@cR^w)~+`{FAmcXxrH3vsW7}8@) z{KL%53=w*fK1pC;*El2i z4rcH^W74c&y)9)2j;pM!F5y*sb9H{SKw)V?w9bV|4OlimXoyTDYVX#2ixgtza*14e zo@4q4EXy(dbiM;pCofmH!S>*o^@nwE+_JgmY5ytA+3W)D*YT9emeEccMl8muETTXa z8=Qz>1Wt1-&liP-g*O(O=!m#x|2PEnma9S7xEi`s2UTqB?PZg!-Y!z7i9c^y%f!|{ z^j^wz&l;04ki5nL{gH);&{oCGFmriDsWH8)s7U5k4p3qH(BA%baPWQrx?Ru9t0MFm z+u)Wge<=qgM;14PE2z4SYdn+8wW@p7F4^8Oy3{q@A^ET|r9q)_E)}ntBY<&m&N0Uz+UM9<|o=5|JF$^-zUwu$_=|2e@@_8X;Uzv3DDI z@!~~Mhj(bGd`(Ra5e4r5PLm1w`&0bv>@4ya4S|){J6}&rOY`A;_+rR>(gwB zt}-NRT&4vn|0BzJE{KbZOKkjzFRsewazP*)En&&zSN=hPO;Tz%=e3{8n`>Q1PvaLl{Di!Ox1Eg_^Z>$sKcBC_RxLqpN zHW`Xcr^*cMJnQ|)tjVKb*d1!D2=w6#N-fMwlK$?7rvHXPMczm|CQahg>^b_N7$+B( ziT3dIgyp(~_PVOZMrMx3N#~bW`9{9V=kjs6KXJPjmX zG|L6O8}j2&85rEGvF~;4h4R{=4BTbWT#;Jcc$l9@0>*AG=1%j{6H`JcdaAUw zHJ>;K2rH~{b8&sDstT2`Z!d@V_nBz)9V1v0rH}#^2SjI$8eO#7|XdbjC`2EVEFd&wY9aq zhwF1pZ*p@P_*BEZpVmqkJ2+5M-SjQFG3y0y)#l*wdwBVszp_%~%vDNsFMz&>l~{x} zkZ{o@VdmiRO(wTG04e6(nwyk5#62n(+?X(W9{h4-sE8-~9;f~u0;+_*l z1z!puI6IG@Z+C<4Qk~v^T)j`Hd6v>K2IlUbKne%o<`z8$hw=Q&Gzj`z*+XKh>>I9O z`O0+x<7*z5kCz@sxPIHbD?-G~dkZ5J@xS-$uI?@1TySD+Uv}K8^D>~5nLh8ImVMYZ zs)@WXFX}G7;yE=S5s0d)k%q?^Wi&jI$m;g`uFJ-4K+x6a-~oT<1q0 z)6qU$A*sq*aV#iPh*6?f{?<_V=ti(`Sz~Q|%AIxc8Z1*C0G66f>(;Xe5u>q@H)A1+m%&0on@i8tU$4m7 zpUB%CE`91QuB8jw;IO)sg||_dD%&uq&3RQukSI{Ayw*g$xg24#ue;DXk_6esAFsKb zR85U9J9b8%0Ny@NDw*UQJKn9}*CrL&KGVrQxXu4$6xaH9%RT(G{xI+kZGoSg%9z}< zj8=bo{qWk@kGFetQx+3fD+Xutij8P#mp;nezX%iH81oyb3qx_7O`DUwL!cdI{3;*N zfd#|1Y*!#1bAgwI>8}uDU{z>$lgQdsSai;DJ4vzS07u|ufvKtD4~{GUM#Rg9#%b28Rux1kS+@6c%s0<;f)a9JaOa0`l@trd zIqSQwuHwPL!TgsBi)sWLTU&VCd4xcK>lr;{Avo}_PgPBg2QrV4%Pack+L@`g8#h-(rlt zAXi+k{Vm=K+6XtNB%4l~r!AtY1tMbJQQMJMlg|RVJwZ%#L+vl$8>TZPxHlrkPu|~4 zW{wc0r78iofS{(i_8HwRQ(S-d1O*uFiYbqbNLuIXAMsxI62yjc@pw7Gcs6+){LJVl$h zR}lzb8=A!c5t1Ze>s{ppHa>h;5k=1)KQwK7>Pi0oxcEhJsrnjeQ3*n>3@6y5{Y&E) z(7(MTBm}+?05|#%KFL9)cO37)k&dw*jlOkWy=h~GidKd~SW(O+nhmjsj1PUgy`>g6 z^%bjz?Y{C_$M1VG{1R_9U&bf|9NIglQ-E64+E)qW>GW|I^KY;7VwwCXLb@A!etP)P zfz?|$Gk0}`>F^g2pq3CtDS8ziFop)@T0zd-Yo2~ro^A{J`W7Jx61h2k^Dqf_AHlTjJQq@##DuscEC?wGj!FYhl|PE*Oz-n3CGLJ8}`vbukqJI zKA8q3x=m`fz4hiHlKF$V_a839&TF@8e+KxYGf5k2o?^E7LMS;mE%G-qq>z0OH#>y` zp`Xkg3~posd8l+UKTPM{l^f{zhE-FeSV$2l4-{7zZq6jF(h>#*txSQo)tasm2x4V_ zFTIM4;w~rUQ({5V<)4Xz%lvzb-|JqxMep}bY%VFDZ@9L{jIHoc-fwA4XiH~IaPpJK z{QSA>M^(JH2#U<2NHYeivbG#TrOQjU#9Jq873aiu84z+#R|o!>t2K7u-~gn%4SSkz z(;I4jB)mH+<->=uybU{DRr?_Zh^n#6YvXcT=I<0Nq$2&%n9_|w~Yx4X*--qVf;mC0xfmSs}{Mrzg%r`Gu;9FbrH$r)c zgns>?@m?;e*5E+E;8DsQOZkLdCz3l@teR|up{%8_W3&1+g3<5ukvcF>WLx@P3?FsT znv>;mas5dv_O+26&MZBbZf(mXiR|nuJ`JX8#@v>{KQ?7J#@RfU5ITMqzpF`k60f8zY6VR|*3_I}Vq{jm92?l3_+3vsQDvk(*LuOt9w6wEO?Fj8a27Mk58(-k~Ayr6v}s;D`>mQ!ruOyHu&%f8SDLf zfBv~gw)q6eD;=#Qi^Vw%#}8WjGCaM?V!mCZIesed)ynG}>|+z7Mltnq7MgVE1Qm>M z8N;Lb*Z|5DUBNq}#i-FDWDH+IlerEAqRhs|HsO%8bx4oy5c#;U--GzEM?{)#6A$aE z7m-Lq*Dpm?L^9nH+qHh2mlGt~{vldhWvn`4{fUdKJ?C*JCVwC8T%T~x$K=TQwm|v1 zn#}5cKqeQNY(k98Gh*LNWU#2Lsv`2|AKTiHG~|x|VE~oOG?*48rSJVlV{yCvIs3{o zomvb*qRbD^Y8-nkK2nPP*<5+g=ll7fV~Q=WLQilkC290mXH+L{*90j_kJBar%N`GY z0%Q;eKT>XJzx0y%v@kWL?d2tkpMV+9+xGek?aIAC#&3U!xsUO%fI!?lXd7% zVq-h}I05;N_M)w?U7{t+O|5ci`T56;#GvW{w$$J;bGX+j*nI({CE2mj=ujpLNzK9_ zhBOWWV5cyF9~F5?!qKyS_>YYZrJXYX;qUJ1(kzpcL+^vF7tM-x&n2o|yD^-R)s>Zf z+s8xd)+gq!P{V5{@vo_6bPLZ4M@Oim5uGflJHNb2Na|xxL-5gyawuon4w2tQ$WlPI zpE&iyybc3e0?>lYhJ@dNHw+R1&Zi6L=r&l@3xyR}plHnx% z91C1huSvDi%f_{n#r~!D@BD9`|NkKV|G(gW9)$c~*n_}tL233{<1EdQcmfO<>Y3@* I>VTjA7xsn7;{X5v literal 8397 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 diff --git a/test/rendering/cases/layer-vector-decluttering-extended/main.js b/test/rendering/cases/layer-vector-decluttering-extended/main.js index fad395b447..821174ec20 100644 --- a/test/rendering/cases/layer-vector-decluttering-extended/main.js +++ b/test/rendering/cases/layer-vector-decluttering-extended/main.js @@ -16,7 +16,7 @@ const map = new Map({ target: 'map', view: new View({ center: center, - zoom: 13.1, + zoom: 12.6, }), }); @@ -133,6 +133,7 @@ map.addLayer( text: feature.get('text'), font: 'italic bold 18px Ubuntu', textBaseline: 'bottom', + offsetX: -25, offsetY: -17, }), }); From 9adfe7aca6c3310e73d053c833499e59cbfc585a Mon Sep 17 00:00:00 2001 From: VLCEK Martin Date: Thu, 5 May 2022 13:47:46 +0200 Subject: [PATCH 3/5] improve test with overlapping circles. --- .../main.js | 22 ++++++++++--------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/test/rendering/cases/layer-vector-decluttering-extended/main.js b/test/rendering/cases/layer-vector-decluttering-extended/main.js index 821174ec20..423fdbc8e7 100644 --- a/test/rendering/cases/layer-vector-decluttering-extended/main.js +++ b/test/rendering/cases/layer-vector-decluttering-extended/main.js @@ -27,8 +27,8 @@ sourceBlue.addFeatures([ text: 'top-blue', }), ]); -// circles are always drawn, but serve as obstacles, -// texts are decluttered against each other and the circles +// on-top blue circle. +// shows that objects (red layer) will not serve as obstacles for layers on-top. map.addLayer( new VectorLayer({ zIndex: 4, @@ -70,8 +70,10 @@ sourceRed.addFeatures([ text: 'e-red', }), ]); -// circles are always drawn, but serve as obstacles, -// texts are decluttered against each other and the circles +// red circles are always drawn, but serve as obstacles. +// however, they cannot serve as obstacles for layers on-top (blue layer). +// texts are decluttered against each other and the circles. +// circles are drawn on non-declutter executor, i.e. behind decluttered labels and objects. map.addLayer( new VectorLayer({ zIndex: 3, @@ -113,8 +115,8 @@ sourceOrange.addFeatures([ text: 'e-orange', }), ]); -// circles are always drawn, -// texts are decluttered against each other and the layer 1 circles/texts +// orange circles are always drawn. +// texts are decluttered against each other and the blue/red layer circles/texts. map.addLayer( new VectorLayer({ zIndex: 2, @@ -156,10 +158,10 @@ sourceCyan.addFeatures([ text: 'e-cyan', }), ]); -// circles are always drawn -// texts are decluttered against each others (and layers 1/2) -// the circles of layer 2 and this layer are no obstactles for texts -// the texts are decluttered and thus above the circles of layer 2 +// cyan circles are always drawn. +// texts are decluttered against each others (and blue/red/orange layers). +// the circles of the orange layer and this layer are no obstactles for texts. +// the texts are decluttered and thus above the circles of the orange layer. map.addLayer( new VectorLayer({ zIndex: 1, From bce37b56508304724951129869000d09b11e0af7 Mon Sep 17 00:00:00 2001 From: VLCEK Martin Date: Fri, 6 May 2022 06:56:13 +0200 Subject: [PATCH 4/5] Zoom out a bit for rendering test --- .../expected.png | Bin 7727 -> 10667 bytes .../main.js | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/test/rendering/cases/layer-vector-decluttering-extended/expected.png b/test/rendering/cases/layer-vector-decluttering-extended/expected.png index 6ae024d2d3106d5f174ff315566c655bb97c4f15..1c59aacd2b868cfc4e7839a797666f0a3e64a0d4 100644 GIT binary patch literal 10667 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 literal 7727 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 I>VTjA7xsn7;{X5v diff --git a/test/rendering/cases/layer-vector-decluttering-extended/main.js b/test/rendering/cases/layer-vector-decluttering-extended/main.js index 423fdbc8e7..9a6ee7c59f 100644 --- a/test/rendering/cases/layer-vector-decluttering-extended/main.js +++ b/test/rendering/cases/layer-vector-decluttering-extended/main.js @@ -16,7 +16,7 @@ const map = new Map({ target: 'map', view: new View({ center: center, - zoom: 12.6, + zoom: 12.7, }), }); From 6ab4d452a4d87b80422647a19a3f83d61ad28159 Mon Sep 17 00:00:00 2001 From: VLCEK Martin Date: Fri, 6 May 2022 16:52:56 +0200 Subject: [PATCH 5/5] reference image from CI --- .../expected.png | Bin 10667 -> 9943 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/test/rendering/cases/layer-vector-decluttering-extended/expected.png b/test/rendering/cases/layer-vector-decluttering-extended/expected.png index 1c59aacd2b868cfc4e7839a797666f0a3e64a0d4..58a7bf9cb45251b0be5050a4d8407a71ba9f8d7d 100644 GIT binary patch literal 9943 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 C^~ux# literal 10667 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