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.
This commit is contained in:
@@ -808,17 +808,21 @@ class Executor {
|
|||||||
instruction[12]
|
instruction[12]
|
||||||
);
|
);
|
||||||
let width = /** @type {number} */ (instruction[13]);
|
let width = /** @type {number} */ (instruction[13]);
|
||||||
const declutterImageWithText =
|
const declutterMode =
|
||||||
/** @type {import("../canvas.js").DeclutterImageWithText} */ (
|
/** @type {"declutter"|"obstacle"|"none"|undefined} */ (
|
||||||
instruction[14]
|
instruction[14]
|
||||||
);
|
);
|
||||||
|
const declutterImageWithText =
|
||||||
|
/** @type {import("../canvas.js").DeclutterImageWithText} */ (
|
||||||
|
instruction[15]
|
||||||
|
);
|
||||||
|
|
||||||
if (!image && instruction.length >= 19) {
|
if (!image && instruction.length >= 20) {
|
||||||
// create label images
|
// create label images
|
||||||
text = /** @type {string} */ (instruction[18]);
|
text = /** @type {string} */ (instruction[19]);
|
||||||
textKey = /** @type {string} */ (instruction[19]);
|
textKey = /** @type {string} */ (instruction[20]);
|
||||||
strokeKey = /** @type {string} */ (instruction[20]);
|
strokeKey = /** @type {string} */ (instruction[21]);
|
||||||
fillKey = /** @type {string} */ (instruction[21]);
|
fillKey = /** @type {string} */ (instruction[22]);
|
||||||
const labelWithAnchor = this.drawLabelWithPointPlacement_(
|
const labelWithAnchor = this.drawLabelWithPointPlacement_(
|
||||||
text,
|
text,
|
||||||
textKey,
|
textKey,
|
||||||
@@ -827,10 +831,10 @@ class Executor {
|
|||||||
);
|
);
|
||||||
image = labelWithAnchor.label;
|
image = labelWithAnchor.label;
|
||||||
instruction[3] = image;
|
instruction[3] = image;
|
||||||
const textOffsetX = /** @type {number} */ (instruction[22]);
|
const textOffsetX = /** @type {number} */ (instruction[23]);
|
||||||
anchorX = (labelWithAnchor.anchorX - textOffsetX) * this.pixelRatio;
|
anchorX = (labelWithAnchor.anchorX - textOffsetX) * this.pixelRatio;
|
||||||
instruction[4] = anchorX;
|
instruction[4] = anchorX;
|
||||||
const textOffsetY = /** @type {number} */ (instruction[23]);
|
const textOffsetY = /** @type {number} */ (instruction[24]);
|
||||||
anchorY = (labelWithAnchor.anchorY - textOffsetY) * this.pixelRatio;
|
anchorY = (labelWithAnchor.anchorY - textOffsetY) * this.pixelRatio;
|
||||||
instruction[5] = anchorY;
|
instruction[5] = anchorY;
|
||||||
height = image.height;
|
height = image.height;
|
||||||
@@ -840,15 +844,15 @@ class Executor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let geometryWidths;
|
let geometryWidths;
|
||||||
if (instruction.length > 24) {
|
if (instruction.length > 25) {
|
||||||
geometryWidths = /** @type {number} */ (instruction[24]);
|
geometryWidths = /** @type {number} */ (instruction[25]);
|
||||||
}
|
}
|
||||||
|
|
||||||
let padding, backgroundFill, backgroundStroke;
|
let padding, backgroundFill, backgroundStroke;
|
||||||
if (instruction.length > 16) {
|
if (instruction.length > 17) {
|
||||||
padding = /** @type {Array<number>} */ (instruction[15]);
|
padding = /** @type {Array<number>} */ (instruction[16]);
|
||||||
backgroundFill = /** @type {boolean} */ (instruction[16]);
|
backgroundFill = /** @type {boolean} */ (instruction[17]);
|
||||||
backgroundStroke = /** @type {boolean} */ (instruction[17]);
|
backgroundStroke = /** @type {boolean} */ (instruction[18]);
|
||||||
} else {
|
} else {
|
||||||
padding = defaultPadding;
|
padding = defaultPadding;
|
||||||
backgroundFill = false;
|
backgroundFill = false;
|
||||||
@@ -902,9 +906,18 @@ class Executor {
|
|||||||
? /** @type {Array<*>} */ (lastStrokeInstruction)
|
? /** @type {Array<*>} */ (lastStrokeInstruction)
|
||||||
: null,
|
: null,
|
||||||
];
|
];
|
||||||
|
if (opt_declutterTree) {
|
||||||
|
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 imageArgs;
|
||||||
let imageDeclutterBox;
|
let imageDeclutterBox;
|
||||||
if (opt_declutterTree && declutterImageWithText) {
|
if (declutterImageWithText) {
|
||||||
const index = dd - d;
|
const index = dd - d;
|
||||||
if (!declutterImageWithText[index]) {
|
if (!declutterImageWithText[index]) {
|
||||||
// We now have the image for an image+text combination.
|
// We now have the image for an image+text combination.
|
||||||
@@ -919,23 +932,18 @@ class Executor {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (
|
if (opt_declutterTree.collides(dimensions.declutterBox)) {
|
||||||
opt_declutterTree &&
|
|
||||||
opt_declutterTree.collides(dimensions.declutterBox)
|
|
||||||
) {
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (imageArgs) {
|
if (imageArgs) {
|
||||||
// We now have image and text for an image+text combination.
|
// We now have image and text for an image+text combination.
|
||||||
if (opt_declutterTree) {
|
|
||||||
opt_declutterTree.insert(imageDeclutterBox);
|
opt_declutterTree.insert(imageDeclutterBox);
|
||||||
}
|
|
||||||
// Render the image before we render the text.
|
// Render the image before we render the text.
|
||||||
this.replayImageOrLabel_.apply(this, imageArgs);
|
this.replayImageOrLabel_.apply(this, imageArgs);
|
||||||
}
|
}
|
||||||
if (opt_declutterTree) {
|
|
||||||
opt_declutterTree.insert(dimensions.declutterBox);
|
opt_declutterTree.insert(dimensions.declutterBox);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
this.replayImageOrLabel_.apply(this, args);
|
this.replayImageOrLabel_.apply(this, args);
|
||||||
}
|
}
|
||||||
++i;
|
++i;
|
||||||
|
|||||||
@@ -92,6 +92,12 @@ class CanvasImageBuilder extends CanvasBuilder {
|
|||||||
*/
|
*/
|
||||||
this.width_ = undefined;
|
this.width_ = undefined;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @private
|
||||||
|
* @type {"declutter"|"obstacle"|"none"|undefined}
|
||||||
|
*/
|
||||||
|
this.declutterMode_ = undefined;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Data shared with a text builder for combined decluttering.
|
* Data shared with a text builder for combined decluttering.
|
||||||
* @private
|
* @private
|
||||||
@@ -132,6 +138,7 @@ class CanvasImageBuilder extends CanvasBuilder {
|
|||||||
(this.scale_[1] * this.pixelRatio) / this.imagePixelRatio_,
|
(this.scale_[1] * this.pixelRatio) / this.imagePixelRatio_,
|
||||||
],
|
],
|
||||||
Math.ceil(this.width_ * this.imagePixelRatio_),
|
Math.ceil(this.width_ * this.imagePixelRatio_),
|
||||||
|
this.declutterMode_,
|
||||||
this.declutterImageWithText_,
|
this.declutterImageWithText_,
|
||||||
]);
|
]);
|
||||||
this.hitDetectionInstructions.push([
|
this.hitDetectionInstructions.push([
|
||||||
@@ -150,6 +157,7 @@ class CanvasImageBuilder extends CanvasBuilder {
|
|||||||
this.rotation_,
|
this.rotation_,
|
||||||
this.scale_,
|
this.scale_,
|
||||||
this.width_,
|
this.width_,
|
||||||
|
this.declutterMode_,
|
||||||
this.declutterImageWithText_,
|
this.declutterImageWithText_,
|
||||||
]);
|
]);
|
||||||
this.endGeometry(feature);
|
this.endGeometry(feature);
|
||||||
@@ -187,6 +195,7 @@ class CanvasImageBuilder extends CanvasBuilder {
|
|||||||
(this.scale_[1] * this.pixelRatio) / this.imagePixelRatio_,
|
(this.scale_[1] * this.pixelRatio) / this.imagePixelRatio_,
|
||||||
],
|
],
|
||||||
Math.ceil(this.width_ * this.imagePixelRatio_),
|
Math.ceil(this.width_ * this.imagePixelRatio_),
|
||||||
|
this.declutterMode_,
|
||||||
this.declutterImageWithText_,
|
this.declutterImageWithText_,
|
||||||
]);
|
]);
|
||||||
this.hitDetectionInstructions.push([
|
this.hitDetectionInstructions.push([
|
||||||
@@ -205,6 +214,7 @@ class CanvasImageBuilder extends CanvasBuilder {
|
|||||||
this.rotation_,
|
this.rotation_,
|
||||||
this.scale_,
|
this.scale_,
|
||||||
this.width_,
|
this.width_,
|
||||||
|
this.declutterMode_,
|
||||||
this.declutterImageWithText_,
|
this.declutterImageWithText_,
|
||||||
]);
|
]);
|
||||||
this.endGeometry(feature);
|
this.endGeometry(feature);
|
||||||
@@ -255,6 +265,7 @@ class CanvasImageBuilder extends CanvasBuilder {
|
|||||||
this.rotation_ = imageStyle.getRotation();
|
this.rotation_ = imageStyle.getRotation();
|
||||||
this.scale_ = imageStyle.getScaleArray();
|
this.scale_ = imageStyle.getScaleArray();
|
||||||
this.width_ = size[0];
|
this.width_ = size[0];
|
||||||
|
this.declutterMode_ = imageStyle.getDeclutterMode();
|
||||||
this.declutterImageWithText_ = opt_sharedData;
|
this.declutterImageWithText_ = opt_sharedData;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -374,6 +374,7 @@ class CanvasTextBuilder extends CanvasBuilder {
|
|||||||
this.textRotation_,
|
this.textRotation_,
|
||||||
[1, 1],
|
[1, 1],
|
||||||
NaN,
|
NaN,
|
||||||
|
undefined,
|
||||||
this.declutterImageWithText_,
|
this.declutterImageWithText_,
|
||||||
padding == defaultPadding
|
padding == defaultPadding
|
||||||
? defaultPadding
|
? defaultPadding
|
||||||
@@ -406,6 +407,7 @@ class CanvasTextBuilder extends CanvasBuilder {
|
|||||||
this.textRotation_,
|
this.textRotation_,
|
||||||
[scale, scale],
|
[scale, scale],
|
||||||
NaN,
|
NaN,
|
||||||
|
undefined,
|
||||||
this.declutterImageWithText_,
|
this.declutterImageWithText_,
|
||||||
padding,
|
padding,
|
||||||
!!textState.backgroundFill,
|
!!textState.backgroundFill,
|
||||||
|
|||||||
@@ -362,24 +362,41 @@ function renderPointGeometry(
|
|||||||
const textStyle = style.getText();
|
const textStyle = style.getText();
|
||||||
/** @type {import("../render/canvas.js").DeclutterImageWithText} */
|
/** @type {import("../render/canvas.js").DeclutterImageWithText} */
|
||||||
let declutterImageWithText;
|
let declutterImageWithText;
|
||||||
if (opt_declutterBuilderGroup) {
|
|
||||||
builderGroup = opt_declutterBuilderGroup;
|
|
||||||
declutterImageWithText =
|
|
||||||
imageStyle && textStyle && textStyle.getText() ? {} : undefined;
|
|
||||||
}
|
|
||||||
if (imageStyle) {
|
if (imageStyle) {
|
||||||
if (imageStyle.getImageState() != ImageState.LOADED) {
|
if (imageStyle.getImageState() != ImageState.LOADED) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
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(
|
const imageReplay = builderGroup.getBuilder(
|
||||||
style.getZIndex(),
|
style.getZIndex(),
|
||||||
BuilderType.IMAGE
|
BuilderType.IMAGE
|
||||||
);
|
);
|
||||||
imageReplay.setImageStyle(imageStyle, declutterImageWithText);
|
imageReplay.setImageStyle(imageStyle, declutterImageWithText);
|
||||||
imageReplay.drawPoint(geometry, feature);
|
imageReplay.drawPoint(geometry, feature);
|
||||||
|
} else if (textStyle && textStyle.getText()) {
|
||||||
|
declutterImageWithText = {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const imageReplay = imageBuilderGroup.getBuilder(
|
||||||
|
style.getZIndex(),
|
||||||
|
BuilderType.IMAGE
|
||||||
|
);
|
||||||
|
imageReplay.setImageStyle(imageStyle, declutterImageWithText);
|
||||||
|
imageReplay.drawPoint(geometry, feature);
|
||||||
}
|
}
|
||||||
if (textStyle && textStyle.getText()) {
|
if (textStyle && textStyle.getText()) {
|
||||||
const textReplay = builderGroup.getBuilder(
|
let textBuilderGroup = builderGroup;
|
||||||
|
if (opt_declutterBuilderGroup) {
|
||||||
|
textBuilderGroup = opt_declutterBuilderGroup;
|
||||||
|
}
|
||||||
|
const textReplay = textBuilderGroup.getBuilder(
|
||||||
style.getZIndex(),
|
style.getZIndex(),
|
||||||
BuilderType.TEXT
|
BuilderType.TEXT
|
||||||
);
|
);
|
||||||
@@ -406,24 +423,41 @@ function renderMultiPointGeometry(
|
|||||||
const textStyle = style.getText();
|
const textStyle = style.getText();
|
||||||
/** @type {import("../render/canvas.js").DeclutterImageWithText} */
|
/** @type {import("../render/canvas.js").DeclutterImageWithText} */
|
||||||
let declutterImageWithText;
|
let declutterImageWithText;
|
||||||
if (opt_declutterBuilderGroup) {
|
|
||||||
builderGroup = opt_declutterBuilderGroup;
|
|
||||||
declutterImageWithText =
|
|
||||||
imageStyle && textStyle && textStyle.getText() ? {} : undefined;
|
|
||||||
}
|
|
||||||
if (imageStyle) {
|
if (imageStyle) {
|
||||||
if (imageStyle.getImageState() != ImageState.LOADED) {
|
if (imageStyle.getImageState() != ImageState.LOADED) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
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(
|
const imageReplay = builderGroup.getBuilder(
|
||||||
style.getZIndex(),
|
style.getZIndex(),
|
||||||
BuilderType.IMAGE
|
BuilderType.IMAGE
|
||||||
);
|
);
|
||||||
imageReplay.setImageStyle(imageStyle, declutterImageWithText);
|
imageReplay.setImageStyle(imageStyle, declutterImageWithText);
|
||||||
imageReplay.drawMultiPoint(geometry, feature);
|
imageReplay.drawMultiPoint(geometry, feature);
|
||||||
|
} else if (textStyle && textStyle.getText()) {
|
||||||
|
declutterImageWithText = {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const imageReplay = imageBuilderGroup.getBuilder(
|
||||||
|
style.getZIndex(),
|
||||||
|
BuilderType.IMAGE
|
||||||
|
);
|
||||||
|
imageReplay.setImageStyle(imageStyle, declutterImageWithText);
|
||||||
|
imageReplay.drawMultiPoint(geometry, feature);
|
||||||
}
|
}
|
||||||
if (textStyle && textStyle.getText()) {
|
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(),
|
style.getZIndex(),
|
||||||
BuilderType.TEXT
|
BuilderType.TEXT
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ import RegularShape from './RegularShape.js';
|
|||||||
* (positive rotation clockwise, meaningful only when used in conjunction with a two dimensional scale).
|
* (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
|
* @property {boolean} [rotateWithView=false] Whether to rotate the shape with the view
|
||||||
* (meaningful only when used in conjunction with a two dimensional scale).
|
* (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,
|
options.rotateWithView !== undefined ? options.rotateWithView : false,
|
||||||
displacement:
|
displacement:
|
||||||
options.displacement !== undefined ? options.displacement : [0, 0],
|
options.displacement !== undefined ? options.displacement : [0, 0],
|
||||||
|
declutterMode: options.declutterMode,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -59,6 +61,7 @@ class CircleStyle extends RegularShape {
|
|||||||
rotation: this.getRotation(),
|
rotation: this.getRotation(),
|
||||||
rotateWithView: this.getRotateWithView(),
|
rotateWithView: this.getRotateWithView(),
|
||||||
displacement: this.getDisplacement().slice(),
|
displacement: this.getDisplacement().slice(),
|
||||||
|
declutterMode: this.getDeclutterMode(),
|
||||||
});
|
});
|
||||||
style.setOpacity(this.getOpacity());
|
style.setOpacity(this.getOpacity());
|
||||||
return style;
|
return style;
|
||||||
|
|||||||
@@ -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
|
* @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.
|
* 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 {string} [src] Image source URI.
|
||||||
|
* @property {"declutter"|"obstacle"|"none"|undefined} [declutterMode] Declutter mode
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -86,6 +87,7 @@ class Icon extends ImageStyle {
|
|||||||
displacement:
|
displacement:
|
||||||
options.displacement !== undefined ? options.displacement : [0, 0],
|
options.displacement !== undefined ? options.displacement : [0, 0],
|
||||||
rotateWithView: rotateWithView,
|
rotateWithView: rotateWithView,
|
||||||
|
declutterMode: options.declutterMode,
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import {toSize} from '../size.js';
|
|||||||
* @property {number} rotation Rotation.
|
* @property {number} rotation Rotation.
|
||||||
* @property {number|import("../size.js").Size} scale Scale.
|
* @property {number|import("../size.js").Size} scale Scale.
|
||||||
* @property {Array<number>} displacement Displacement.
|
* @property {Array<number>} displacement Displacement.
|
||||||
*/
|
* @property {"declutter"|"obstacle"|"none"|undefined} declutterMode Declutter mode: `declutter`, `obstacle`, 'none */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @classdesc
|
* @classdesc
|
||||||
@@ -61,6 +61,12 @@ class ImageStyle {
|
|||||||
* @type {Array<number>}
|
* @type {Array<number>}
|
||||||
*/
|
*/
|
||||||
this.displacement_ = options.displacement;
|
this.displacement_ = options.displacement;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @private
|
||||||
|
* @type {"declutter"|"obstacle"|"none"|undefined}
|
||||||
|
*/
|
||||||
|
this.declutterMode_ = options.declutterMode;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -76,6 +82,7 @@ class ImageStyle {
|
|||||||
rotation: this.getRotation(),
|
rotation: this.getRotation(),
|
||||||
rotateWithView: this.getRotateWithView(),
|
rotateWithView: this.getRotateWithView(),
|
||||||
displacement: this.getDisplacement().slice(),
|
displacement: this.getDisplacement().slice(),
|
||||||
|
declutterMode: this.getDeclutterMode(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -132,6 +139,15 @@ class ImageStyle {
|
|||||||
return this.displacement_;
|
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
|
* Get the anchor point in pixels. The anchor determines the center point for the
|
||||||
* symbolizer.
|
* symbolizer.
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ import {
|
|||||||
* @property {boolean} [rotateWithView=false] Whether to rotate the shape with the view.
|
* @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
|
* @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`.
|
* 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,
|
scale: options.scale !== undefined ? options.scale : 1,
|
||||||
displacement:
|
displacement:
|
||||||
options.displacement !== undefined ? options.displacement : [0, 0],
|
options.displacement !== undefined ? options.displacement : [0, 0],
|
||||||
|
declutterMode: options.declutterMode,
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -159,6 +161,7 @@ class RegularShape extends ImageStyle {
|
|||||||
rotateWithView: this.getRotateWithView(),
|
rotateWithView: this.getRotateWithView(),
|
||||||
scale: Array.isArray(scale) ? scale.slice() : scale,
|
scale: Array.isArray(scale) ? scale.slice() : scale,
|
||||||
displacement: this.getDisplacement().slice(),
|
displacement: this.getDisplacement().slice(),
|
||||||
|
declutterMode: this.getDeclutterMode(),
|
||||||
});
|
});
|
||||||
style.setOpacity(this.getOpacity());
|
style.setOpacity(this.getOpacity());
|
||||||
return style;
|
return style;
|
||||||
|
|||||||
@@ -320,7 +320,7 @@ describe('ol.render.canvas.TextBuilder', function () {
|
|||||||
builder.drawText(feature.getGeometry(), feature);
|
builder.drawText(feature.getGeometry(), feature);
|
||||||
expect(builder.coordinates).to.have.length(2);
|
expect(builder.coordinates).to.have.length(2);
|
||||||
expect(builder.instructions).to.have.length(3);
|
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).to.have.length(1);
|
||||||
expect(geometryWidths[0]).to.be(120);
|
expect(geometryWidths[0]).to.be(120);
|
||||||
});
|
});
|
||||||
|
|||||||
Binary file not shown.
|
After Width: | Height: | Size: 8.2 KiB |
188
test/rendering/cases/layer-vector-decluttering-extended/main.js
Normal file
188
test/rendering/cases/layer-vector-decluttering-extended/main.js
Normal file
@@ -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});
|
||||||
Reference in New Issue
Block a user