Merge pull request #11592 from ahocevar/new-declutter
New decluttering implementation
This commit is contained in:
@@ -6,7 +6,6 @@ import stringify from 'json-stringify-safe';
|
|||||||
import styleFunction from 'ol-mapbox-style/dist/stylefunction.js';
|
import styleFunction from 'ol-mapbox-style/dist/stylefunction.js';
|
||||||
import {Projection} from '../src/ol/proj.js';
|
import {Projection} from '../src/ol/proj.js';
|
||||||
import {inView} from '../src/ol/layer/Layer.js';
|
import {inView} from '../src/ol/layer/Layer.js';
|
||||||
import {renderDeclutterItems} from '../src/ol/render.js';
|
|
||||||
import {getTilePriority as tilePriorityFunction} from '../src/ol/TileQueue.js';
|
import {getTilePriority as tilePriorityFunction} from '../src/ol/TileQueue.js';
|
||||||
|
|
||||||
/** @type {any} */
|
/** @type {any} */
|
||||||
@@ -145,7 +144,7 @@ worker.addEventListener('message', (event) => {
|
|||||||
renderer.renderFrame(frameState, canvas);
|
renderer.renderFrame(frameState, canvas);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
renderDeclutterItems(frameState, null);
|
layers.forEach((layer) => layer.renderDeclutter(frameState));
|
||||||
if (tileQueue.getTilesLoading() < maxTotalLoading) {
|
if (tileQueue.getTilesLoading() < maxTotalLoading) {
|
||||||
tileQueue.reprioritize();
|
tileQueue.reprioritize();
|
||||||
tileQueue.loadMoreTiles(maxTotalLoading, maxNewLoads);
|
tileQueue.loadMoreTiles(maxTotalLoading, maxNewLoads);
|
||||||
|
|||||||
Binary file not shown.
|
After Width: | Height: | Size: 12 KiB |
@@ -0,0 +1,53 @@
|
|||||||
|
import Feature from '../../../src/ol/Feature.js';
|
||||||
|
import Map from '../../../src/ol/Map.js';
|
||||||
|
import MultiPoint from '../../../src/ol/geom/MultiPoint.js';
|
||||||
|
import VectorSource from '../../../src/ol/source/Vector.js';
|
||||||
|
import View from '../../../src/ol/View.js';
|
||||||
|
import {Icon, Stroke, Style, Text} from '../../../src/ol/style.js';
|
||||||
|
import {Vector as VectorLayer} from '../../../src/ol/layer.js';
|
||||||
|
|
||||||
|
const vectorLayer = new VectorLayer({
|
||||||
|
declutter: true,
|
||||||
|
renderBuffer: 0,
|
||||||
|
source: new VectorSource({
|
||||||
|
features: [
|
||||||
|
new Feature(
|
||||||
|
new MultiPoint([
|
||||||
|
[0, 0],
|
||||||
|
[0, 1],
|
||||||
|
[0.5, 0.5],
|
||||||
|
[0.9, 0.85],
|
||||||
|
[1, 0],
|
||||||
|
[0.3, 0.5],
|
||||||
|
])
|
||||||
|
),
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
style: new Style({
|
||||||
|
image: new Icon({
|
||||||
|
anchor: [0.5, 46],
|
||||||
|
anchorXUnits: 'fraction',
|
||||||
|
anchorYUnits: 'pixels',
|
||||||
|
src: '/data/icon.png',
|
||||||
|
}),
|
||||||
|
text: new Text({
|
||||||
|
text: 'Test',
|
||||||
|
font: 'italic 700 20px Ubuntu',
|
||||||
|
stroke: new Stroke({
|
||||||
|
color: 'red',
|
||||||
|
width: 20,
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
new Map({
|
||||||
|
layers: [vectorLayer],
|
||||||
|
target: document.getElementById('map'),
|
||||||
|
view: new View({
|
||||||
|
center: [0.5, 0.5],
|
||||||
|
resolution: 0.006679631467570084,
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
render({tolerance: 0.007});
|
||||||
+2
-10
@@ -50,8 +50,8 @@ import {removeNode} from './dom.js';
|
|||||||
* @property {import("./View.js").State} viewState The state of the current view.
|
* @property {import("./View.js").State} viewState The state of the current view.
|
||||||
* @property {boolean} animate
|
* @property {boolean} animate
|
||||||
* @property {import("./transform.js").Transform} coordinateToPixelTransform
|
* @property {import("./transform.js").Transform} coordinateToPixelTransform
|
||||||
|
* @property {import("rbush").default} declutterTree
|
||||||
* @property {null|import("./extent.js").Extent} extent
|
* @property {null|import("./extent.js").Extent} extent
|
||||||
* @property {Array<DeclutterItems>} declutterItems
|
|
||||||
* @property {number} index
|
* @property {number} index
|
||||||
* @property {Array<import("./layer/Layer.js").State>} layerStatesArray
|
* @property {Array<import("./layer/Layer.js").State>} layerStatesArray
|
||||||
* @property {number} layerIndex
|
* @property {number} layerIndex
|
||||||
@@ -64,12 +64,6 @@ import {removeNode} from './dom.js';
|
|||||||
* @property {!Object<string, Object<string, boolean>>} wantedTiles
|
* @property {!Object<string, Object<string, boolean>>} wantedTiles
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
|
||||||
* @typedef {Object} DeclutterItems
|
|
||||||
* @property {Array<*>} items Declutter items of an executor.
|
|
||||||
* @property {number} opacity Layer opacity.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @typedef {function(PluggableMap, ?FrameState): any} PostRenderFunction
|
* @typedef {function(PluggableMap, ?FrameState): any} PostRenderFunction
|
||||||
*/
|
*/
|
||||||
@@ -1387,9 +1381,7 @@ class PluggableMap extends BaseObject {
|
|||||||
frameState = {
|
frameState = {
|
||||||
animate: false,
|
animate: false,
|
||||||
coordinateToPixelTransform: this.coordinateToPixelTransform_,
|
coordinateToPixelTransform: this.coordinateToPixelTransform_,
|
||||||
declutterItems: previousFrameState
|
declutterTree: null,
|
||||||
? previousFrameState.declutterItems
|
|
||||||
: [],
|
|
||||||
extent: getForViewAndSize(
|
extent: getForViewAndSize(
|
||||||
viewState.center,
|
viewState.center,
|
||||||
viewState.resolution,
|
viewState.resolution,
|
||||||
|
|||||||
@@ -45,6 +45,12 @@ class VectorRenderTile extends Tile {
|
|||||||
*/
|
*/
|
||||||
this.executorGroups = {};
|
this.executorGroups = {};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Executor groups for decluttering, by layer uid. Entries are read/written by the renderer.
|
||||||
|
* @type {Object<string, Array<import("./render/canvas/ExecutorGroup.js").default>>}
|
||||||
|
*/
|
||||||
|
this.declutterExecutorGroups = {};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Number of loading source tiles. Read/written by the source.
|
* Number of loading source tiles. Read/written by the source.
|
||||||
* @type {number}
|
* @type {number}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
* @module ol/layer/BaseVector
|
* @module ol/layer/BaseVector
|
||||||
*/
|
*/
|
||||||
import Layer from './Layer.js';
|
import Layer from './Layer.js';
|
||||||
|
import RBush from 'rbush';
|
||||||
import {assign} from '../obj.js';
|
import {assign} from '../obj.js';
|
||||||
import {
|
import {
|
||||||
createDefaultStyle,
|
createDefaultStyle,
|
||||||
@@ -214,6 +215,17 @@ class BaseVectorLayer extends Layer {
|
|||||||
return this.updateWhileInteracting_;
|
return this.updateWhileInteracting_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Render declutter items for this layer
|
||||||
|
* @param {import("../PluggableMap.js").FrameState} frameState Frame state.
|
||||||
|
*/
|
||||||
|
renderDeclutter(frameState) {
|
||||||
|
if (!frameState.declutterTree) {
|
||||||
|
frameState.declutterTree = new RBush(9);
|
||||||
|
}
|
||||||
|
/** @type {*} */ (this.getRenderer()).renderDeclutter(frameState);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {import("../render.js").OrderFunction|null|undefined} renderOrder
|
* @param {import("../render.js").OrderFunction|null|undefined} renderOrder
|
||||||
* Render order.
|
* Render order.
|
||||||
|
|||||||
@@ -309,6 +309,8 @@ class Heatmap extends VectorLayer {
|
|||||||
],
|
],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
renderDeclutter() {}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -129,29 +129,3 @@ export function getRenderPixel(event, pixel) {
|
|||||||
applyTransform(event.inversePixelTransform.slice(), result);
|
applyTransform(event.inversePixelTransform.slice(), result);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {import("./PluggableMap.js").FrameState} frameState Frame state.
|
|
||||||
* @param {?} declutterTree Declutter tree.
|
|
||||||
* @returns {?} Declutter tree.
|
|
||||||
*/
|
|
||||||
export function renderDeclutterItems(frameState, declutterTree) {
|
|
||||||
if (declutterTree) {
|
|
||||||
declutterTree.clear();
|
|
||||||
}
|
|
||||||
const items = frameState.declutterItems;
|
|
||||||
for (let z = items.length - 1; z >= 0; --z) {
|
|
||||||
const item = items[z];
|
|
||||||
const zIndexItems = item.items;
|
|
||||||
for (let i = 0, ii = zIndexItems.length; i < ii; i += 3) {
|
|
||||||
declutterTree = zIndexItems[i].renderDeclutter(
|
|
||||||
zIndexItems[i + 1],
|
|
||||||
zIndexItems[i + 2],
|
|
||||||
item.opacity,
|
|
||||||
declutterTree
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
items.length = 0;
|
|
||||||
return declutterTree;
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -100,15 +100,15 @@ class VectorContext {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {import("../style/Image.js").default} imageStyle Image style.
|
* @param {import("../style/Image.js").default} imageStyle Image style.
|
||||||
* @param {import("./canvas.js").DeclutterGroup=} opt_declutterGroup Declutter.
|
* @param {import("../render/canvas.js").DeclutterImageWithText=} opt_declutterImageWithText Shared data for combined decluttering with a text style.
|
||||||
*/
|
*/
|
||||||
setImageStyle(imageStyle, opt_declutterGroup) {}
|
setImageStyle(imageStyle, opt_declutterImageWithText) {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {import("../style/Text.js").default} textStyle Text style.
|
* @param {import("../style/Text.js").default} textStyle Text style.
|
||||||
* @param {import("./canvas.js").DeclutterGroups=} opt_declutterGroups Declutter.
|
* @param {import("../render/canvas.js").DeclutterImageWithText=} opt_declutterImageWithText Shared data for combined decluttering with an image style.
|
||||||
*/
|
*/
|
||||||
setTextStyle(textStyle, opt_declutterGroups) {}
|
setTextStyle(textStyle, opt_declutterImageWithText) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default VectorContext;
|
export default VectorContext;
|
||||||
|
|||||||
+11
-15
@@ -68,20 +68,17 @@ import {toString} from '../transform.js';
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Container for decluttered replay instructions that need to be rendered or
|
* @typedef {Object} SerializableInstructions
|
||||||
* omitted together, i.e. when styles render both an image and text, or for the
|
* @property {Array<*>} instructions The rendering instructions.
|
||||||
* characters that form text along lines. The basic elements of this array are
|
* @property {Array<*>} hitDetectionInstructions The rendering hit detection instructions.
|
||||||
* `[minX, minY, maxX, maxY, count]`, where the first four entries are the
|
* @property {Array<number>} coordinates The array of all coordinates.
|
||||||
* rendered extent of the group in pixel space. `count` is the number of styles
|
* @property {!Object<string, TextState>} [textStates] The text states (decluttering).
|
||||||
* in the group, i.e. 2 when an image and a text are grouped, or 1 otherwise.
|
* @property {!Object<string, FillState>} [fillStates] The fill states (decluttering).
|
||||||
* In addition to these four elements, declutter instruction arrays (i.e. the
|
* @property {!Object<string, StrokeState>} [strokeStates] The stroke states (decluttering).
|
||||||
* arguments to {@link module:ol/render/canvas~drawImage} are appended to the array.
|
|
||||||
* @typedef {Array<*>} DeclutterGroup
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Declutter groups for support of multi geometries.
|
* @typedef {Object<number, import("./canvas/Executor.js").ReplayImageOrLabelArgs>} DeclutterImageWithText
|
||||||
* @typedef {Array<DeclutterGroup>} DeclutterGroups
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -293,9 +290,8 @@ export const measureTextHeight = (function () {
|
|||||||
* @type {HTMLDivElement}
|
* @type {HTMLDivElement}
|
||||||
*/
|
*/
|
||||||
let div;
|
let div;
|
||||||
const heights = textHeights;
|
|
||||||
return function (fontSpec) {
|
return function (fontSpec) {
|
||||||
let height = heights[fontSpec];
|
let height = textHeights[fontSpec];
|
||||||
if (height == undefined) {
|
if (height == undefined) {
|
||||||
if (WORKER_OFFSCREEN_CANVAS) {
|
if (WORKER_OFFSCREEN_CANVAS) {
|
||||||
const font = getFontParameters(fontSpec);
|
const font = getFontParameters(fontSpec);
|
||||||
@@ -303,7 +299,7 @@ export const measureTextHeight = (function () {
|
|||||||
const lineHeight = isNaN(Number(font.lineHeight))
|
const lineHeight = isNaN(Number(font.lineHeight))
|
||||||
? 1.2
|
? 1.2
|
||||||
: Number(font.lineHeight);
|
: Number(font.lineHeight);
|
||||||
textHeights[fontSpec] =
|
height =
|
||||||
lineHeight *
|
lineHeight *
|
||||||
(metrics.actualBoundingBoxAscent + metrics.actualBoundingBoxDescent);
|
(metrics.actualBoundingBoxAscent + metrics.actualBoundingBoxDescent);
|
||||||
} else {
|
} else {
|
||||||
@@ -318,9 +314,9 @@ export const measureTextHeight = (function () {
|
|||||||
div.style.font = fontSpec;
|
div.style.font = fontSpec;
|
||||||
document.body.appendChild(div);
|
document.body.appendChild(div);
|
||||||
height = div.offsetHeight;
|
height = div.offsetHeight;
|
||||||
heights[fontSpec] = height;
|
|
||||||
document.body.removeChild(div);
|
document.body.removeChild(div);
|
||||||
}
|
}
|
||||||
|
textHeights[fontSpec] = height;
|
||||||
}
|
}
|
||||||
return height;
|
return height;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -29,16 +29,6 @@ import {
|
|||||||
inflateMultiCoordinatesArray,
|
inflateMultiCoordinatesArray,
|
||||||
} from '../../geom/flat/inflate.js';
|
} from '../../geom/flat/inflate.js';
|
||||||
|
|
||||||
/**
|
|
||||||
* @typedef {Object} SerializableInstructions
|
|
||||||
* @property {Array<*>} instructions The rendering instructions.
|
|
||||||
* @property {Array<*>} hitDetectionInstructions The rendering hit detection instructions.
|
|
||||||
* @property {Array<number>} coordinates The array of all coordinates.
|
|
||||||
* @property {!Object<string, import("../canvas.js").TextState>} [textStates] The text states (decluttering).
|
|
||||||
* @property {!Object<string, import("../canvas.js").FillState>} [fillStates] The fill states (decluttering).
|
|
||||||
* @property {!Object<string, import("../canvas.js").StrokeState>} [strokeStates] The stroke states (decluttering).
|
|
||||||
*/
|
|
||||||
|
|
||||||
class CanvasBuilder extends VectorContext {
|
class CanvasBuilder extends VectorContext {
|
||||||
/**
|
/**
|
||||||
* @param {number} tolerance Tolerance.
|
* @param {number} tolerance Tolerance.
|
||||||
@@ -383,7 +373,7 @@ class CanvasBuilder extends VectorContext {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return {SerializableInstructions} the serializable instructions.
|
* @return {import("../canvas.js").SerializableInstructions} the serializable instructions.
|
||||||
*/
|
*/
|
||||||
finish() {
|
finish() {
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -26,21 +26,8 @@ class BuilderGroup {
|
|||||||
* @param {import("../../extent.js").Extent} maxExtent Max extent.
|
* @param {import("../../extent.js").Extent} maxExtent Max extent.
|
||||||
* @param {number} resolution Resolution.
|
* @param {number} resolution Resolution.
|
||||||
* @param {number} pixelRatio Pixel ratio.
|
* @param {number} pixelRatio Pixel ratio.
|
||||||
* @param {boolean} declutter Decluttering enabled.
|
|
||||||
*/
|
*/
|
||||||
constructor(tolerance, maxExtent, resolution, pixelRatio, declutter) {
|
constructor(tolerance, maxExtent, resolution, pixelRatio) {
|
||||||
/**
|
|
||||||
* @type {boolean}
|
|
||||||
* @private
|
|
||||||
*/
|
|
||||||
this.declutter_ = declutter;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @type {import("../canvas.js").DeclutterGroups}
|
|
||||||
* @private
|
|
||||||
*/
|
|
||||||
this.declutterGroups_ = null;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @private
|
* @private
|
||||||
* @type {number}
|
* @type {number}
|
||||||
@@ -72,25 +59,6 @@ class BuilderGroup {
|
|||||||
this.buildersByZIndex_ = {};
|
this.buildersByZIndex_ = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {boolean} group Group with previous builder.
|
|
||||||
* @return {import("../canvas").DeclutterGroups} The resulting instruction groups.
|
|
||||||
*/
|
|
||||||
addDeclutter(group) {
|
|
||||||
/** @type {Array<*>} */
|
|
||||||
let declutter = null;
|
|
||||||
if (this.declutter_) {
|
|
||||||
if (group) {
|
|
||||||
declutter = this.declutterGroups_;
|
|
||||||
/** @type {number} */ (declutter[0][0])++;
|
|
||||||
} else {
|
|
||||||
declutter = [[1]];
|
|
||||||
this.declutterGroups_ = declutter;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return declutter;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return {!Object<string, !Object<import("./BuilderType").default, import("./Builder.js").SerializableInstructions>>} The serializable instructions
|
* @return {!Object<string, !Object<import("./BuilderType").default, import("./Builder.js").SerializableInstructions>>} The serializable instructions
|
||||||
*/
|
*/
|
||||||
|
|||||||
+290
-311
@@ -2,7 +2,6 @@
|
|||||||
* @module ol/render/canvas/Executor
|
* @module ol/render/canvas/Executor
|
||||||
*/
|
*/
|
||||||
import CanvasInstruction from './Instruction.js';
|
import CanvasInstruction from './Instruction.js';
|
||||||
import RBush from 'rbush';
|
|
||||||
import {TEXT_ALIGN} from './TextBuilder.js';
|
import {TEXT_ALIGN} from './TextBuilder.js';
|
||||||
import {WORKER_OFFSCREEN_CANVAS} from '../../has.js';
|
import {WORKER_OFFSCREEN_CANVAS} from '../../has.js';
|
||||||
import {
|
import {
|
||||||
@@ -11,13 +10,7 @@ import {
|
|||||||
create as createTransform,
|
create as createTransform,
|
||||||
setFromArray as transformSetFromArray,
|
setFromArray as transformSetFromArray,
|
||||||
} from '../../transform.js';
|
} from '../../transform.js';
|
||||||
import {
|
import {createEmpty, createOrUpdate, intersects} from '../../extent.js';
|
||||||
createEmpty,
|
|
||||||
createOrUpdate,
|
|
||||||
getHeight,
|
|
||||||
getWidth,
|
|
||||||
intersects,
|
|
||||||
} from '../../extent.js';
|
|
||||||
import {
|
import {
|
||||||
defaultPadding,
|
defaultPadding,
|
||||||
defaultTextBaseline,
|
defaultTextBaseline,
|
||||||
@@ -35,13 +28,29 @@ import {lineStringLength} from '../../geom/flat/length.js';
|
|||||||
import {transform2D} from '../../geom/flat/transform.js';
|
import {transform2D} from '../../geom/flat/transform.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @typedef {Object} SerializableInstructions
|
* @typedef {Object} BBox
|
||||||
* @property {Array<*>} instructions The rendering instructions.
|
* @property {number} minX
|
||||||
* @property {Array<*>} hitDetectionInstructions The rendering hit detection instructions.
|
* @property {number} minY
|
||||||
* @property {Array<number>} coordinates The array of all coordinates.
|
* @property {number} maxX
|
||||||
* @property {!Object<string, import("../canvas.js").TextState>} textStates The text states (decluttering).
|
* @property {number} maxY
|
||||||
* @property {!Object<string, import("../canvas.js").FillState>} fillStates The fill states (decluttering).
|
* @property {*} value
|
||||||
* @property {!Object<string, import("../canvas.js").StrokeState>} strokeStates The stroke states (decluttering).
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef {Object} ImageOrLabelDimensions
|
||||||
|
* @property {number} drawImageX
|
||||||
|
* @property {number} drawImageY
|
||||||
|
* @property {number} drawImageW
|
||||||
|
* @property {number} drawImageH
|
||||||
|
* @property {number} originX
|
||||||
|
* @property {number} originY
|
||||||
|
* @property {Array<number>} scale
|
||||||
|
* @property {BBox} declutterBox
|
||||||
|
* @property {import("../../transform.js").Transform} canvasTransform
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef {{0: CanvasRenderingContext2D, 1: number, 2: import("../canvas.js").Label|HTMLImageElement|HTMLCanvasElement|HTMLVideoElement, 3: ImageOrLabelDimensions, 4: number, 5: Array<*>, 6: Array<*>}} ReplayImageOrLabelArgs
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -49,11 +58,6 @@ import {transform2D} from '../../geom/flat/transform.js';
|
|||||||
*/
|
*/
|
||||||
const tmpExtent = createEmpty();
|
const tmpExtent = createEmpty();
|
||||||
|
|
||||||
/**
|
|
||||||
* @type {!import("../../transform.js").Transform}
|
|
||||||
*/
|
|
||||||
const tmpTransform = createTransform();
|
|
||||||
|
|
||||||
/** @type {import("../../coordinate.js").Coordinate} */
|
/** @type {import("../../coordinate.js").Coordinate} */
|
||||||
const p1 = [];
|
const p1 = [];
|
||||||
/** @type {import("../../coordinate.js").Coordinate} */
|
/** @type {import("../../coordinate.js").Coordinate} */
|
||||||
@@ -63,12 +67,20 @@ const p3 = [];
|
|||||||
/** @type {import("../../coordinate.js").Coordinate} */
|
/** @type {import("../../coordinate.js").Coordinate} */
|
||||||
const p4 = [];
|
const p4 = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {ReplayImageOrLabelArgs} replayImageOrLabelArgs Arguments to replayImageOrLabel
|
||||||
|
* @return {BBox} Declutter bbox.
|
||||||
|
*/
|
||||||
|
function getDeclutterBox(replayImageOrLabelArgs) {
|
||||||
|
return replayImageOrLabelArgs[3].declutterBox;
|
||||||
|
}
|
||||||
|
|
||||||
class Executor {
|
class Executor {
|
||||||
/**
|
/**
|
||||||
* @param {number} resolution Resolution.
|
* @param {number} resolution Resolution.
|
||||||
* @param {number} pixelRatio Pixel ratio.
|
* @param {number} pixelRatio Pixel ratio.
|
||||||
* @param {boolean} overlaps The replay can have overlapping geometries.
|
* @param {boolean} overlaps The replay can have overlapping geometries.
|
||||||
* @param {SerializableInstructions} instructions The serializable instructions
|
* @param {import("../canvas.js").SerializableInstructions} instructions The serializable instructions
|
||||||
* @param {import("../../size.js").Size} renderBuffer Render buffer (width/height) in pixels.
|
* @param {import("../../size.js").Size} renderBuffer Render buffer (width/height) in pixels.
|
||||||
*/
|
*/
|
||||||
constructor(resolution, pixelRatio, overlaps, instructions, renderBuffer) {
|
constructor(resolution, pixelRatio, overlaps, instructions, renderBuffer) {
|
||||||
@@ -97,11 +109,6 @@ class Executor {
|
|||||||
*/
|
*/
|
||||||
this.alignFill_;
|
this.alignFill_;
|
||||||
|
|
||||||
/**
|
|
||||||
* @type {Array<*>}
|
|
||||||
*/
|
|
||||||
this.declutterItems = [];
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @protected
|
* @protected
|
||||||
* @type {Array<*>}
|
* @type {Array<*>}
|
||||||
@@ -274,7 +281,6 @@ class Executor {
|
|||||||
* @param {import("../../coordinate.js").Coordinate} p4 4th point of the background box.
|
* @param {import("../../coordinate.js").Coordinate} p4 4th point of the background box.
|
||||||
* @param {Array<*>} fillInstruction Fill instruction.
|
* @param {Array<*>} fillInstruction Fill instruction.
|
||||||
* @param {Array<*>} strokeInstruction Stroke instruction.
|
* @param {Array<*>} strokeInstruction Stroke instruction.
|
||||||
* @param {boolean} declutter Declutter.
|
|
||||||
*/
|
*/
|
||||||
replayTextBackground_(
|
replayTextBackground_(
|
||||||
context,
|
context,
|
||||||
@@ -283,8 +289,7 @@ class Executor {
|
|||||||
p3,
|
p3,
|
||||||
p4,
|
p4,
|
||||||
fillInstruction,
|
fillInstruction,
|
||||||
strokeInstruction,
|
strokeInstruction
|
||||||
declutter
|
|
||||||
) {
|
) {
|
||||||
context.beginPath();
|
context.beginPath();
|
||||||
context.moveTo.apply(context, p1);
|
context.moveTo.apply(context, p1);
|
||||||
@@ -294,9 +299,6 @@ class Executor {
|
|||||||
context.lineTo.apply(context, p1);
|
context.lineTo.apply(context, p1);
|
||||||
if (fillInstruction) {
|
if (fillInstruction) {
|
||||||
this.alignFill_ = /** @type {boolean} */ (fillInstruction[2]);
|
this.alignFill_ = /** @type {boolean} */ (fillInstruction[2]);
|
||||||
if (declutter) {
|
|
||||||
context.fillStyle = /** @type {import("../../colorlike.js").ColorLike} */ (fillInstruction[1]);
|
|
||||||
}
|
|
||||||
this.fill_(context);
|
this.fill_(context);
|
||||||
}
|
}
|
||||||
if (strokeInstruction) {
|
if (strokeInstruction) {
|
||||||
@@ -310,62 +312,49 @@ class Executor {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @private
|
* @private
|
||||||
* @param {CanvasRenderingContext2D} context Context.
|
* @param {number} sheetWidth Width of the sprite sheet.
|
||||||
* @param {number} contextScale Scale of the context.
|
* @param {number} sheetHeight Height of the sprite sheet.
|
||||||
* @param {number} x X.
|
* @param {number} centerX X.
|
||||||
* @param {number} y Y.
|
* @param {number} centerY Y.
|
||||||
* @param {import("../canvas.js").Label|HTMLImageElement|HTMLCanvasElement|HTMLVideoElement} imageOrLabel Image.
|
* @param {number} width Width.
|
||||||
|
* @param {number} height Height.
|
||||||
* @param {number} anchorX Anchor X.
|
* @param {number} anchorX Anchor X.
|
||||||
* @param {number} anchorY Anchor Y.
|
* @param {number} anchorY Anchor Y.
|
||||||
* @param {import("../canvas.js").DeclutterGroup} declutterGroup Declutter group.
|
|
||||||
* @param {number} height Height.
|
|
||||||
* @param {number} opacity Opacity.
|
|
||||||
* @param {number} originX Origin X.
|
* @param {number} originX Origin X.
|
||||||
* @param {number} originY Origin Y.
|
* @param {number} originY Origin Y.
|
||||||
* @param {number} rotation Rotation.
|
* @param {number} rotation Rotation.
|
||||||
* @param {import("../../size.js").Size} scale Scale.
|
* @param {import("../../size.js").Size} scale Scale.
|
||||||
* @param {boolean} snapToPixel Snap to pixel.
|
* @param {boolean} snapToPixel Snap to pixel.
|
||||||
* @param {number} width Width.
|
|
||||||
* @param {Array<number>} padding Padding.
|
* @param {Array<number>} padding Padding.
|
||||||
* @param {Array<*>} fillInstruction Fill instruction.
|
* @param {boolean} fillStroke Background fill or stroke.
|
||||||
* @param {Array<*>} strokeInstruction Stroke instruction.
|
* @param {import("../../Feature.js").FeatureLike} feature Feature.
|
||||||
* @return {boolean} The image or label was rendered.
|
* @return {ImageOrLabelDimensions} Dimensions for positioning and decluttering the image or label.
|
||||||
*/
|
*/
|
||||||
replayImageOrLabel_(
|
calculateImageOrLabelDimensions_(
|
||||||
context,
|
sheetWidth,
|
||||||
contextScale,
|
sheetHeight,
|
||||||
x,
|
centerX,
|
||||||
y,
|
centerY,
|
||||||
imageOrLabel,
|
width,
|
||||||
|
height,
|
||||||
anchorX,
|
anchorX,
|
||||||
anchorY,
|
anchorY,
|
||||||
declutterGroup,
|
|
||||||
height,
|
|
||||||
opacity,
|
|
||||||
originX,
|
originX,
|
||||||
originY,
|
originY,
|
||||||
rotation,
|
rotation,
|
||||||
scale,
|
scale,
|
||||||
snapToPixel,
|
snapToPixel,
|
||||||
width,
|
|
||||||
padding,
|
padding,
|
||||||
fillInstruction,
|
fillStroke,
|
||||||
strokeInstruction
|
feature
|
||||||
) {
|
) {
|
||||||
const fillStroke = fillInstruction || strokeInstruction;
|
|
||||||
anchorX *= scale[0];
|
anchorX *= scale[0];
|
||||||
anchorY *= scale[1];
|
anchorY *= scale[1];
|
||||||
x -= anchorX;
|
let x = centerX - anchorX;
|
||||||
y -= anchorY;
|
let y = centerY - anchorY;
|
||||||
|
|
||||||
const w =
|
const w = width + originX > sheetWidth ? sheetWidth - originX : width;
|
||||||
width + originX > imageOrLabel.width
|
const h = height + originY > sheetHeight ? sheetHeight - originY : height;
|
||||||
? imageOrLabel.width - originX
|
|
||||||
: width;
|
|
||||||
const h =
|
|
||||||
height + originY > imageOrLabel.height
|
|
||||||
? imageOrLabel.height - originY
|
|
||||||
: height;
|
|
||||||
const boxW = padding[3] + w * scale[0] + padding[1];
|
const boxW = padding[3] + w * scale[0] + padding[1];
|
||||||
const boxH = padding[0] + h * scale[1] + padding[2];
|
const boxH = padding[0] + h * scale[1] + padding[2];
|
||||||
const boxX = x - padding[3];
|
const boxX = x - padding[3];
|
||||||
@@ -382,12 +371,10 @@ class Executor {
|
|||||||
p4[1] = p3[1];
|
p4[1] = p3[1];
|
||||||
}
|
}
|
||||||
|
|
||||||
let transform = null;
|
let transform;
|
||||||
if (rotation !== 0) {
|
if (rotation !== 0) {
|
||||||
const centerX = x + anchorX;
|
|
||||||
const centerY = y + anchorY;
|
|
||||||
transform = composeTransform(
|
transform = composeTransform(
|
||||||
tmpTransform,
|
createTransform(),
|
||||||
centerX,
|
centerX,
|
||||||
centerY,
|
centerY,
|
||||||
1,
|
1,
|
||||||
@@ -397,10 +384,10 @@ class Executor {
|
|||||||
-centerY
|
-centerY
|
||||||
);
|
);
|
||||||
|
|
||||||
applyTransform(tmpTransform, p1);
|
applyTransform(transform, p1);
|
||||||
applyTransform(tmpTransform, p2);
|
applyTransform(transform, p2);
|
||||||
applyTransform(tmpTransform, p3);
|
applyTransform(transform, p3);
|
||||||
applyTransform(tmpTransform, p4);
|
applyTransform(transform, p4);
|
||||||
createOrUpdate(
|
createOrUpdate(
|
||||||
Math.min(p1[0], p2[0], p3[0], p4[0]),
|
Math.min(p1[0], p2[0], p3[0], p4[0]),
|
||||||
Math.min(p1[1], p2[1], p3[1], p4[1]),
|
Math.min(p1[1], p2[1], p3[1], p4[1]),
|
||||||
@@ -417,66 +404,63 @@ class Executor {
|
|||||||
tmpExtent
|
tmpExtent
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
let renderBufferX = 0;
|
|
||||||
let renderBufferY = 0;
|
|
||||||
if (declutterGroup) {
|
|
||||||
const renderBuffer = this.renderBuffer_;
|
|
||||||
renderBuffer[0] = Math.max(renderBuffer[0], getWidth(tmpExtent));
|
|
||||||
renderBufferX = renderBuffer[0];
|
|
||||||
renderBuffer[1] = Math.max(renderBuffer[1], getHeight(tmpExtent));
|
|
||||||
renderBufferY = renderBuffer[1];
|
|
||||||
}
|
|
||||||
const canvas = context.canvas;
|
|
||||||
const strokePadding = strokeInstruction
|
|
||||||
? (strokeInstruction[2] * scale[0]) / 2
|
|
||||||
: 0;
|
|
||||||
const intersects =
|
|
||||||
tmpExtent[0] - strokePadding <=
|
|
||||||
(canvas.width + renderBufferX) / contextScale &&
|
|
||||||
tmpExtent[2] + strokePadding >= -renderBufferX / contextScale &&
|
|
||||||
tmpExtent[1] - strokePadding <=
|
|
||||||
(canvas.height + renderBufferY) / contextScale &&
|
|
||||||
tmpExtent[3] + strokePadding >= -renderBufferY / contextScale;
|
|
||||||
|
|
||||||
if (snapToPixel) {
|
if (snapToPixel) {
|
||||||
x = Math.round(x);
|
x = Math.round(x);
|
||||||
y = Math.round(y);
|
y = Math.round(y);
|
||||||
}
|
}
|
||||||
|
return {
|
||||||
|
drawImageX: x,
|
||||||
|
drawImageY: y,
|
||||||
|
drawImageW: w,
|
||||||
|
drawImageH: h,
|
||||||
|
originX: originX,
|
||||||
|
originY: originY,
|
||||||
|
declutterBox: {
|
||||||
|
minX: tmpExtent[0],
|
||||||
|
minY: tmpExtent[1],
|
||||||
|
maxX: tmpExtent[2],
|
||||||
|
maxY: tmpExtent[3],
|
||||||
|
value: feature,
|
||||||
|
},
|
||||||
|
canvasTransform: transform,
|
||||||
|
scale: scale,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
if (declutterGroup) {
|
/**
|
||||||
if (!intersects && declutterGroup[0] == 1) {
|
* @private
|
||||||
return false;
|
* @param {CanvasRenderingContext2D} context Context.
|
||||||
}
|
* @param {number} contextScale Scale of the context.
|
||||||
const declutterArgs = intersects
|
* @param {import("../canvas.js").Label|HTMLImageElement|HTMLCanvasElement|HTMLVideoElement} imageOrLabel Image.
|
||||||
? [
|
* @param {ImageOrLabelDimensions} dimensions Dimensions.
|
||||||
context,
|
* @param {number} opacity Opacity.
|
||||||
transform ? transform.slice(0) : null,
|
* @param {Array<*>} fillInstruction Fill instruction.
|
||||||
opacity,
|
* @param {Array<*>} strokeInstruction Stroke instruction.
|
||||||
imageOrLabel,
|
* @return {boolean} The image or label was rendered.
|
||||||
originX,
|
*/
|
||||||
originY,
|
replayImageOrLabel_(
|
||||||
w,
|
context,
|
||||||
h,
|
contextScale,
|
||||||
x,
|
imageOrLabel,
|
||||||
y,
|
dimensions,
|
||||||
scale,
|
opacity,
|
||||||
tmpExtent.slice(),
|
fillInstruction,
|
||||||
]
|
strokeInstruction
|
||||||
: null;
|
) {
|
||||||
if (declutterArgs) {
|
const fillStroke = !!(fillInstruction || strokeInstruction);
|
||||||
if (fillStroke) {
|
|
||||||
declutterArgs.push(
|
const box = dimensions.declutterBox;
|
||||||
fillInstruction,
|
const canvas = context.canvas;
|
||||||
strokeInstruction,
|
const strokePadding = strokeInstruction
|
||||||
p1.slice(0),
|
? (strokeInstruction[2] * dimensions.scale[0]) / 2
|
||||||
p2.slice(0),
|
: 0;
|
||||||
p3.slice(0),
|
const intersects =
|
||||||
p4.slice(0)
|
box.minX - strokePadding <= canvas.width / contextScale &&
|
||||||
);
|
box.maxX + strokePadding >= 0 &&
|
||||||
}
|
box.minY - strokePadding <= canvas.height / contextScale &&
|
||||||
declutterGroup.push(declutterArgs);
|
box.maxY + strokePadding >= 0;
|
||||||
}
|
|
||||||
} else if (intersects) {
|
if (intersects) {
|
||||||
if (fillStroke) {
|
if (fillStroke) {
|
||||||
this.replayTextBackground_(
|
this.replayTextBackground_(
|
||||||
context,
|
context,
|
||||||
@@ -485,22 +469,21 @@ class Executor {
|
|||||||
p3,
|
p3,
|
||||||
p4,
|
p4,
|
||||||
/** @type {Array<*>} */ (fillInstruction),
|
/** @type {Array<*>} */ (fillInstruction),
|
||||||
/** @type {Array<*>} */ (strokeInstruction),
|
/** @type {Array<*>} */ (strokeInstruction)
|
||||||
false
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
drawImageOrLabel(
|
drawImageOrLabel(
|
||||||
context,
|
context,
|
||||||
transform,
|
dimensions.canvasTransform,
|
||||||
opacity,
|
opacity,
|
||||||
imageOrLabel,
|
imageOrLabel,
|
||||||
originX,
|
dimensions.originX,
|
||||||
originY,
|
dimensions.originY,
|
||||||
w,
|
dimensions.drawImageW,
|
||||||
h,
|
dimensions.drawImageH,
|
||||||
x,
|
dimensions.drawImageX,
|
||||||
y,
|
dimensions.drawImageY,
|
||||||
scale
|
dimensions.scale
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
@@ -530,7 +513,9 @@ class Executor {
|
|||||||
* @param {Array<*>} instruction Instruction.
|
* @param {Array<*>} instruction Instruction.
|
||||||
*/
|
*/
|
||||||
setStrokeStyle_(context, instruction) {
|
setStrokeStyle_(context, instruction) {
|
||||||
context.strokeStyle = /** @type {import("../../colorlike.js").ColorLike} */ (instruction[1]);
|
context[
|
||||||
|
'strokeStyle'
|
||||||
|
] = /** @type {import("../../colorlike.js").ColorLike} */ (instruction[1]);
|
||||||
context.lineWidth = /** @type {number} */ (instruction[2]);
|
context.lineWidth = /** @type {number} */ (instruction[2]);
|
||||||
context.lineCap = /** @type {CanvasLineCap} */ (instruction[3]);
|
context.lineCap = /** @type {CanvasLineCap} */ (instruction[3]);
|
||||||
context.lineJoin = /** @type {CanvasLineJoin} */ (instruction[4]);
|
context.lineJoin = /** @type {CanvasLineJoin} */ (instruction[4]);
|
||||||
@@ -541,68 +526,6 @@ class Executor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {import("../canvas.js").DeclutterGroup} declutterGroup Declutter group.
|
|
||||||
* @param {import("../../Feature.js").FeatureLike} feature Feature.
|
|
||||||
* @param {number} opacity Layer opacity.
|
|
||||||
* @param {?} declutterTree Declutter tree.
|
|
||||||
* @return {?} Declutter tree.
|
|
||||||
*/
|
|
||||||
renderDeclutter(declutterGroup, feature, opacity, declutterTree) {
|
|
||||||
/** @type {Array<import("../../structs/RBush.js").Entry>} */
|
|
||||||
const boxes = [];
|
|
||||||
for (let i = 1, ii = declutterGroup.length; i < ii; ++i) {
|
|
||||||
const declutterData = declutterGroup[i];
|
|
||||||
const box = declutterData[11];
|
|
||||||
boxes.push({
|
|
||||||
minX: box[0],
|
|
||||||
minY: box[1],
|
|
||||||
maxX: box[2],
|
|
||||||
maxY: box[3],
|
|
||||||
value: feature,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if (!declutterTree) {
|
|
||||||
declutterTree = new RBush(9);
|
|
||||||
}
|
|
||||||
let collides = false;
|
|
||||||
for (let i = 0, ii = boxes.length; i < ii; ++i) {
|
|
||||||
if (declutterTree.collides(boxes[i])) {
|
|
||||||
collides = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!collides) {
|
|
||||||
declutterTree.load(boxes);
|
|
||||||
for (let j = 1, jj = declutterGroup.length; j < jj; ++j) {
|
|
||||||
const declutterData = /** @type {Array} */ (declutterGroup[j]);
|
|
||||||
const context = declutterData[0];
|
|
||||||
const currentAlpha = context.globalAlpha;
|
|
||||||
if (currentAlpha !== opacity) {
|
|
||||||
context.globalAlpha = opacity;
|
|
||||||
}
|
|
||||||
if (declutterData.length > 12) {
|
|
||||||
this.replayTextBackground_(
|
|
||||||
declutterData[0],
|
|
||||||
declutterData[14],
|
|
||||||
declutterData[15],
|
|
||||||
declutterData[16],
|
|
||||||
declutterData[17],
|
|
||||||
declutterData[12],
|
|
||||||
declutterData[13],
|
|
||||||
true
|
|
||||||
);
|
|
||||||
}
|
|
||||||
drawImageOrLabel.apply(undefined, declutterData);
|
|
||||||
if (currentAlpha !== opacity) {
|
|
||||||
context.globalAlpha = currentAlpha;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
declutterGroup.length = 1;
|
|
||||||
return declutterTree;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @private
|
* @private
|
||||||
* @param {string} text The text to draw.
|
* @param {string} text The text to draw.
|
||||||
@@ -647,6 +570,7 @@ class Executor {
|
|||||||
* @param {function(import("../../Feature.js").FeatureLike): T|undefined} featureCallback Feature callback.
|
* @param {function(import("../../Feature.js").FeatureLike): T|undefined} featureCallback Feature callback.
|
||||||
* @param {import("../../extent.js").Extent=} opt_hitExtent Only check features that intersect this
|
* @param {import("../../extent.js").Extent=} opt_hitExtent Only check features that intersect this
|
||||||
* extent.
|
* extent.
|
||||||
|
* @param {import("rbush").default=} opt_declutterTree Declutter tree.
|
||||||
* @return {T|undefined} Callback result.
|
* @return {T|undefined} Callback result.
|
||||||
* @template T
|
* @template T
|
||||||
*/
|
*/
|
||||||
@@ -657,9 +581,9 @@ class Executor {
|
|||||||
instructions,
|
instructions,
|
||||||
snapToPixel,
|
snapToPixel,
|
||||||
featureCallback,
|
featureCallback,
|
||||||
opt_hitExtent
|
opt_hitExtent,
|
||||||
|
opt_declutterTree
|
||||||
) {
|
) {
|
||||||
this.declutterItems.length = 0;
|
|
||||||
/** @type {Array<number>} */
|
/** @type {Array<number>} */
|
||||||
let pixelCoordinates;
|
let pixelCoordinates;
|
||||||
if (this.pixelCoordinates_ && equals(transform, this.renderedTransform_)) {
|
if (this.pixelCoordinates_ && equals(transform, this.renderedTransform_)) {
|
||||||
@@ -688,12 +612,11 @@ class Executor {
|
|||||||
prevY,
|
prevY,
|
||||||
roundX,
|
roundX,
|
||||||
roundY,
|
roundY,
|
||||||
declutterGroup,
|
|
||||||
declutterGroups,
|
|
||||||
image,
|
image,
|
||||||
text,
|
text,
|
||||||
textKey;
|
textKey,
|
||||||
let strokeKey, fillKey;
|
strokeKey,
|
||||||
|
fillKey;
|
||||||
let pendingFill = 0;
|
let pendingFill = 0;
|
||||||
let pendingStroke = 0;
|
let pendingStroke = 0;
|
||||||
let lastFillInstruction = null;
|
let lastFillInstruction = null;
|
||||||
@@ -796,15 +719,15 @@ class Executor {
|
|||||||
// Remaining arguments in DRAW_IMAGE are in alphabetical order
|
// Remaining arguments in DRAW_IMAGE are in alphabetical order
|
||||||
anchorX = /** @type {number} */ (instruction[4]);
|
anchorX = /** @type {number} */ (instruction[4]);
|
||||||
anchorY = /** @type {number} */ (instruction[5]);
|
anchorY = /** @type {number} */ (instruction[5]);
|
||||||
declutterGroups = featureCallback ? null : instruction[6];
|
let height = /** @type {number} */ (instruction[6]);
|
||||||
let height = /** @type {number} */ (instruction[7]);
|
const opacity = /** @type {number} */ (instruction[7]);
|
||||||
const opacity = /** @type {number} */ (instruction[8]);
|
const originX = /** @type {number} */ (instruction[8]);
|
||||||
const originX = /** @type {number} */ (instruction[9]);
|
const originY = /** @type {number} */ (instruction[9]);
|
||||||
const originY = /** @type {number} */ (instruction[10]);
|
const rotateWithView = /** @type {boolean} */ (instruction[10]);
|
||||||
const rotateWithView = /** @type {boolean} */ (instruction[11]);
|
let rotation = /** @type {number} */ (instruction[11]);
|
||||||
let rotation = /** @type {number} */ (instruction[12]);
|
const scale = /** @type {import("../../size.js").Size} */ (instruction[12]);
|
||||||
const scale = /** @type {import("../../size.js").Size} */ (instruction[13]);
|
let width = /** @type {number} */ (instruction[13]);
|
||||||
let width = /** @type {number} */ (instruction[14]);
|
const declutterImageWithText = /** @type {import("../canvas.js").DeclutterImageWithText} */ (instruction[14]);
|
||||||
|
|
||||||
if (!image && instruction.length >= 19) {
|
if (!image && instruction.length >= 19) {
|
||||||
// create label images
|
// create label images
|
||||||
@@ -827,9 +750,9 @@ class Executor {
|
|||||||
anchorY = (labelWithAnchor.anchorY - textOffsetY) * this.pixelRatio;
|
anchorY = (labelWithAnchor.anchorY - textOffsetY) * this.pixelRatio;
|
||||||
instruction[5] = anchorY;
|
instruction[5] = anchorY;
|
||||||
height = image.height;
|
height = image.height;
|
||||||
instruction[7] = height;
|
instruction[6] = height;
|
||||||
width = image.width;
|
width = image.width;
|
||||||
instruction[14] = width;
|
instruction[13] = width;
|
||||||
}
|
}
|
||||||
|
|
||||||
let geometryWidths;
|
let geometryWidths;
|
||||||
@@ -856,7 +779,6 @@ class Executor {
|
|||||||
rotation -= viewRotation;
|
rotation -= viewRotation;
|
||||||
}
|
}
|
||||||
let widthIndex = 0;
|
let widthIndex = 0;
|
||||||
let declutterGroupIndex = 0;
|
|
||||||
for (; d < dd; d += 2) {
|
for (; d < dd; d += 2) {
|
||||||
if (
|
if (
|
||||||
geometryWidths &&
|
geometryWidths &&
|
||||||
@@ -864,51 +786,72 @@ class Executor {
|
|||||||
) {
|
) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (declutterGroups) {
|
const dimensions = this.calculateImageOrLabelDimensions_(
|
||||||
const index = Math.floor(declutterGroupIndex);
|
image.width,
|
||||||
declutterGroup =
|
image.height,
|
||||||
declutterGroups.length < index + 1
|
|
||||||
? [declutterGroups[0][0]]
|
|
||||||
: declutterGroups[index];
|
|
||||||
}
|
|
||||||
const rendered = this.replayImageOrLabel_(
|
|
||||||
context,
|
|
||||||
contextScale,
|
|
||||||
pixelCoordinates[d],
|
pixelCoordinates[d],
|
||||||
pixelCoordinates[d + 1],
|
pixelCoordinates[d + 1],
|
||||||
image,
|
width,
|
||||||
|
height,
|
||||||
anchorX,
|
anchorX,
|
||||||
anchorY,
|
anchorY,
|
||||||
declutterGroup,
|
|
||||||
height,
|
|
||||||
opacity,
|
|
||||||
originX,
|
originX,
|
||||||
originY,
|
originY,
|
||||||
rotation,
|
rotation,
|
||||||
scale,
|
scale,
|
||||||
snapToPixel,
|
snapToPixel,
|
||||||
width,
|
|
||||||
padding,
|
padding,
|
||||||
|
backgroundFill || backgroundStroke,
|
||||||
|
feature
|
||||||
|
);
|
||||||
|
/** @type {ReplayImageOrLabelArgs} */
|
||||||
|
const args = [
|
||||||
|
context,
|
||||||
|
contextScale,
|
||||||
|
image,
|
||||||
|
dimensions,
|
||||||
|
opacity,
|
||||||
backgroundFill
|
backgroundFill
|
||||||
? /** @type {Array<*>} */ (lastFillInstruction)
|
? /** @type {Array<*>} */ (lastFillInstruction)
|
||||||
: null,
|
: null,
|
||||||
backgroundStroke
|
backgroundStroke
|
||||||
? /** @type {Array<*>} */ (lastStrokeInstruction)
|
? /** @type {Array<*>} */ (lastStrokeInstruction)
|
||||||
: null
|
: null,
|
||||||
);
|
];
|
||||||
if (
|
let imageArgs;
|
||||||
rendered &&
|
let imageDeclutterBox;
|
||||||
declutterGroup &&
|
if (opt_declutterTree && declutterImageWithText) {
|
||||||
declutterGroups[declutterGroups.length - 1] !== declutterGroup
|
if (!declutterImageWithText[d]) {
|
||||||
) {
|
// We now have the image for an image+text combination.
|
||||||
declutterGroups.push(declutterGroup);
|
declutterImageWithText[d] = args;
|
||||||
}
|
// Don't render anything for now, wait for the text.
|
||||||
if (declutterGroup) {
|
continue;
|
||||||
if (declutterGroup.length - 1 === declutterGroup[0]) {
|
}
|
||||||
this.declutterItems.push(this, declutterGroup, feature);
|
imageArgs = declutterImageWithText[d];
|
||||||
|
delete declutterImageWithText[d];
|
||||||
|
imageDeclutterBox = getDeclutterBox(imageArgs);
|
||||||
|
if (opt_declutterTree.collides(imageDeclutterBox)) {
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
declutterGroupIndex += 1 / declutterGroup[0];
|
|
||||||
}
|
}
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
this.replayImageOrLabel_.apply(this, args);
|
||||||
}
|
}
|
||||||
++i;
|
++i;
|
||||||
break;
|
break;
|
||||||
@@ -916,19 +859,18 @@ class Executor {
|
|||||||
const begin = /** @type {number} */ (instruction[1]);
|
const begin = /** @type {number} */ (instruction[1]);
|
||||||
const end = /** @type {number} */ (instruction[2]);
|
const end = /** @type {number} */ (instruction[2]);
|
||||||
const baseline = /** @type {number} */ (instruction[3]);
|
const baseline = /** @type {number} */ (instruction[3]);
|
||||||
declutterGroup = featureCallback ? null : instruction[4];
|
const overflow = /** @type {number} */ (instruction[4]);
|
||||||
const overflow = /** @type {number} */ (instruction[5]);
|
fillKey = /** @type {string} */ (instruction[5]);
|
||||||
fillKey = /** @type {string} */ (instruction[6]);
|
const maxAngle = /** @type {number} */ (instruction[6]);
|
||||||
const maxAngle = /** @type {number} */ (instruction[7]);
|
const measurePixelRatio = /** @type {number} */ (instruction[7]);
|
||||||
const measurePixelRatio = /** @type {number} */ (instruction[8]);
|
const offsetY = /** @type {number} */ (instruction[8]);
|
||||||
const offsetY = /** @type {number} */ (instruction[9]);
|
strokeKey = /** @type {string} */ (instruction[9]);
|
||||||
strokeKey = /** @type {string} */ (instruction[10]);
|
const strokeWidth = /** @type {number} */ (instruction[10]);
|
||||||
const strokeWidth = /** @type {number} */ (instruction[11]);
|
text = /** @type {string} */ (instruction[11]);
|
||||||
text = /** @type {string} */ (instruction[12]);
|
textKey = /** @type {string} */ (instruction[12]);
|
||||||
textKey = /** @type {string} */ (instruction[13]);
|
|
||||||
const pixelRatioScale = [
|
const pixelRatioScale = [
|
||||||
/** @type {number} */ (instruction[14]),
|
/** @type {number} */ (instruction[13]),
|
||||||
/** @type {number} */ (instruction[14]),
|
/** @type {number} */ (instruction[13]),
|
||||||
];
|
];
|
||||||
|
|
||||||
const textState = this.textStates[textKey];
|
const textState = this.textStates[textKey];
|
||||||
@@ -967,8 +909,9 @@ class Executor {
|
|||||||
cachedWidths,
|
cachedWidths,
|
||||||
viewRotationFromTransform ? 0 : this.viewRotation_
|
viewRotationFromTransform ? 0 : this.viewRotation_
|
||||||
);
|
);
|
||||||
if (parts) {
|
drawChars: if (parts) {
|
||||||
let rendered = false;
|
/** @type {Array<ReplayImageOrLabelArgs>} */
|
||||||
|
const replayImageOrLabelArgs = [];
|
||||||
let c, cc, chars, label, part;
|
let c, cc, chars, label, part;
|
||||||
if (strokeKey) {
|
if (strokeKey) {
|
||||||
for (c = 0, cc = parts.length; c < cc; ++c) {
|
for (c = 0, cc = parts.length; c < cc; ++c) {
|
||||||
@@ -981,28 +924,39 @@ class Executor {
|
|||||||
((0.5 - baseline) * 2 * strokeWidth * textScale[1]) /
|
((0.5 - baseline) * 2 * strokeWidth * textScale[1]) /
|
||||||
textScale[0] -
|
textScale[0] -
|
||||||
offsetY;
|
offsetY;
|
||||||
rendered =
|
const dimensions = this.calculateImageOrLabelDimensions_(
|
||||||
this.replayImageOrLabel_(
|
label.width,
|
||||||
context,
|
label.height,
|
||||||
contextScale,
|
part[0],
|
||||||
/** @type {number} */ (part[0]),
|
part[1],
|
||||||
/** @type {number} */ (part[1]),
|
label.width,
|
||||||
label,
|
label.height,
|
||||||
anchorX,
|
anchorX,
|
||||||
anchorY,
|
anchorY,
|
||||||
declutterGroup,
|
0,
|
||||||
label.height,
|
0,
|
||||||
1,
|
part[3],
|
||||||
0,
|
pixelRatioScale,
|
||||||
0,
|
false,
|
||||||
/** @type {number} */ (part[3]),
|
defaultPadding,
|
||||||
pixelRatioScale,
|
false,
|
||||||
false,
|
feature
|
||||||
label.width,
|
);
|
||||||
defaultPadding,
|
if (
|
||||||
null,
|
opt_declutterTree &&
|
||||||
null
|
opt_declutterTree.collides(dimensions.declutterBox)
|
||||||
) || rendered;
|
) {
|
||||||
|
break drawChars;
|
||||||
|
}
|
||||||
|
replayImageOrLabelArgs.push([
|
||||||
|
context,
|
||||||
|
contextScale,
|
||||||
|
label,
|
||||||
|
dimensions,
|
||||||
|
1,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (fillKey) {
|
if (fillKey) {
|
||||||
@@ -1012,32 +966,48 @@ class Executor {
|
|||||||
label = this.createLabel(chars, textKey, fillKey, '');
|
label = this.createLabel(chars, textKey, fillKey, '');
|
||||||
anchorX = /** @type {number} */ (part[2]);
|
anchorX = /** @type {number} */ (part[2]);
|
||||||
anchorY = baseline * label.height - offsetY;
|
anchorY = baseline * label.height - offsetY;
|
||||||
rendered =
|
const dimensions = this.calculateImageOrLabelDimensions_(
|
||||||
this.replayImageOrLabel_(
|
label.width,
|
||||||
context,
|
label.height,
|
||||||
contextScale,
|
part[0],
|
||||||
/** @type {number} */ (part[0]),
|
part[1],
|
||||||
/** @type {number} */ (part[1]),
|
label.width,
|
||||||
label,
|
label.height,
|
||||||
anchorX,
|
anchorX,
|
||||||
anchorY,
|
anchorY,
|
||||||
declutterGroup,
|
0,
|
||||||
label.height,
|
0,
|
||||||
1,
|
part[3],
|
||||||
0,
|
pixelRatioScale,
|
||||||
0,
|
false,
|
||||||
/** @type {number} */ (part[3]),
|
defaultPadding,
|
||||||
pixelRatioScale,
|
false,
|
||||||
false,
|
feature
|
||||||
label.width,
|
);
|
||||||
defaultPadding,
|
if (
|
||||||
null,
|
opt_declutterTree &&
|
||||||
null
|
opt_declutterTree.collides(dimensions.declutterBox)
|
||||||
) || rendered;
|
) {
|
||||||
|
break drawChars;
|
||||||
|
}
|
||||||
|
replayImageOrLabelArgs.push([
|
||||||
|
context,
|
||||||
|
contextScale,
|
||||||
|
label,
|
||||||
|
dimensions,
|
||||||
|
1,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (rendered) {
|
if (opt_declutterTree) {
|
||||||
this.declutterItems.push(this, declutterGroup, feature);
|
opt_declutterTree.load(
|
||||||
|
replayImageOrLabelArgs.map(getDeclutterBox)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
for (let i = 0, ii = replayImageOrLabelArgs.length; i < ii; ++i) {
|
||||||
|
this.replayImageOrLabel_.apply(this, replayImageOrLabelArgs[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1139,8 +1109,16 @@ class Executor {
|
|||||||
* @param {import("../../transform.js").Transform} transform Transform.
|
* @param {import("../../transform.js").Transform} transform Transform.
|
||||||
* @param {number} viewRotation View rotation.
|
* @param {number} viewRotation View rotation.
|
||||||
* @param {boolean} snapToPixel Snap point symbols and text to integer pixels.
|
* @param {boolean} snapToPixel Snap point symbols and text to integer pixels.
|
||||||
|
* @param {import("rbush").default=} opt_declutterTree Declutter tree.
|
||||||
*/
|
*/
|
||||||
execute(context, contextScale, transform, viewRotation, snapToPixel) {
|
execute(
|
||||||
|
context,
|
||||||
|
contextScale,
|
||||||
|
transform,
|
||||||
|
viewRotation,
|
||||||
|
snapToPixel,
|
||||||
|
opt_declutterTree
|
||||||
|
) {
|
||||||
this.viewRotation_ = viewRotation;
|
this.viewRotation_ = viewRotation;
|
||||||
this.execute_(
|
this.execute_(
|
||||||
context,
|
context,
|
||||||
@@ -1149,7 +1127,8 @@ class Executor {
|
|||||||
this.instructions,
|
this.instructions,
|
||||||
snapToPixel,
|
snapToPixel,
|
||||||
undefined,
|
undefined,
|
||||||
undefined
|
undefined,
|
||||||
|
opt_declutterTree
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ class ExecutorGroup {
|
|||||||
* @param {number} resolution Resolution.
|
* @param {number} resolution Resolution.
|
||||||
* @param {number} pixelRatio Pixel ratio.
|
* @param {number} pixelRatio Pixel ratio.
|
||||||
* @param {boolean} overlaps The executor group can have overlapping geometries.
|
* @param {boolean} overlaps The executor group can have overlapping geometries.
|
||||||
* @param {!Object<string, !Object<import("./BuilderType.js").default, import("./Builder.js").SerializableInstructions>>} allInstructions
|
* @param {!Object<string, !Object<import("./BuilderType.js").default, import("../canvas.js").SerializableInstructions>>} allInstructions
|
||||||
* The serializable instructions.
|
* The serializable instructions.
|
||||||
* @param {number=} opt_renderBuffer Optional rendering buffer.
|
* @param {number=} opt_renderBuffer Optional rendering buffer.
|
||||||
*/
|
*/
|
||||||
@@ -116,7 +116,7 @@ class ExecutorGroup {
|
|||||||
/**
|
/**
|
||||||
* Create executors and populate them using the provided instructions.
|
* Create executors and populate them using the provided instructions.
|
||||||
* @private
|
* @private
|
||||||
* @param {!Object<string, !Object<import("./BuilderType.js").default, import("./Builder.js").SerializableInstructions>>} allInstructions The serializable instructions
|
* @param {!Object<string, !Object<import("./BuilderType.js").default, import("../canvas.js").SerializableInstructions>>} allInstructions The serializable instructions
|
||||||
*/
|
*/
|
||||||
createExecutors_(allInstructions) {
|
createExecutors_(allInstructions) {
|
||||||
for (const zIndex in allInstructions) {
|
for (const zIndex in allInstructions) {
|
||||||
@@ -318,7 +318,7 @@ class ExecutorGroup {
|
|||||||
* @param {boolean} snapToPixel Snap point symbols and test to integer pixel.
|
* @param {boolean} snapToPixel Snap point symbols and test to integer pixel.
|
||||||
* @param {Array<import("./BuilderType.js").default>=} opt_builderTypes Ordered replay types to replay.
|
* @param {Array<import("./BuilderType.js").default>=} opt_builderTypes Ordered replay types to replay.
|
||||||
* Default is {@link module:ol/render/replay~ORDER}
|
* Default is {@link module:ol/render/replay~ORDER}
|
||||||
* @param {Object<string, import("../canvas.js").DeclutterGroup>=} opt_declutterReplays Declutter replays.
|
* @param {import("rbush").default=} opt_declutterTree Declutter tree.
|
||||||
*/
|
*/
|
||||||
execute(
|
execute(
|
||||||
context,
|
context,
|
||||||
@@ -327,7 +327,7 @@ class ExecutorGroup {
|
|||||||
viewRotation,
|
viewRotation,
|
||||||
snapToPixel,
|
snapToPixel,
|
||||||
opt_builderTypes,
|
opt_builderTypes,
|
||||||
opt_declutterReplays
|
opt_declutterTree
|
||||||
) {
|
) {
|
||||||
/** @type {Array<number>} */
|
/** @type {Array<number>} */
|
||||||
const zs = Object.keys(this.executorsByZIndex_).map(Number);
|
const zs = Object.keys(this.executorsByZIndex_).map(Number);
|
||||||
@@ -342,6 +342,9 @@ class ExecutorGroup {
|
|||||||
|
|
||||||
const builderTypes = opt_builderTypes ? opt_builderTypes : ORDER;
|
const builderTypes = opt_builderTypes ? opt_builderTypes : ORDER;
|
||||||
let i, ii, j, jj, replays, replay;
|
let i, ii, j, jj, replays, replay;
|
||||||
|
if (opt_declutterTree) {
|
||||||
|
zs.reverse();
|
||||||
|
}
|
||||||
for (i = 0, ii = zs.length; i < ii; ++i) {
|
for (i = 0, ii = zs.length; i < ii; ++i) {
|
||||||
const zIndexKey = zs[i].toString();
|
const zIndexKey = zs[i].toString();
|
||||||
replays = this.executorsByZIndex_[zIndexKey];
|
replays = this.executorsByZIndex_[zIndexKey];
|
||||||
@@ -349,26 +352,14 @@ class ExecutorGroup {
|
|||||||
const builderType = builderTypes[j];
|
const builderType = builderTypes[j];
|
||||||
replay = replays[builderType];
|
replay = replays[builderType];
|
||||||
if (replay !== undefined) {
|
if (replay !== undefined) {
|
||||||
if (
|
replay.execute(
|
||||||
opt_declutterReplays &&
|
context,
|
||||||
(builderType == BuilderType.IMAGE ||
|
contextScale,
|
||||||
builderType == BuilderType.TEXT)
|
transform,
|
||||||
) {
|
viewRotation,
|
||||||
const declutter = opt_declutterReplays[zIndexKey];
|
snapToPixel,
|
||||||
if (!declutter) {
|
opt_declutterTree
|
||||||
opt_declutterReplays[zIndexKey] = [replay, transform.slice(0)];
|
);
|
||||||
} else {
|
|
||||||
declutter.push(replay, transform.slice(0));
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
replay.execute(
|
|
||||||
context,
|
|
||||||
contextScale,
|
|
||||||
transform,
|
|
||||||
viewRotation,
|
|
||||||
snapToPixel
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -454,41 +445,4 @@ export function getCircleArray(radius) {
|
|||||||
return arr;
|
return arr;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {!Object<string, Array<*>>} declutterReplays Declutter replays.
|
|
||||||
* @param {CanvasRenderingContext2D} context Context.
|
|
||||||
* @param {number} rotation Rotation.
|
|
||||||
* @param {number} opacity Opacity.
|
|
||||||
* @param {boolean} snapToPixel Snap point symbols and text to integer pixels.
|
|
||||||
* @param {Array<import("../../PluggableMap.js").DeclutterItems>} declutterItems Declutter items.
|
|
||||||
*/
|
|
||||||
export function replayDeclutter(
|
|
||||||
declutterReplays,
|
|
||||||
context,
|
|
||||||
rotation,
|
|
||||||
opacity,
|
|
||||||
snapToPixel,
|
|
||||||
declutterItems
|
|
||||||
) {
|
|
||||||
const zs = Object.keys(declutterReplays)
|
|
||||||
.map(Number)
|
|
||||||
.sort(numberSafeCompareFunction);
|
|
||||||
for (let z = 0, zz = zs.length; z < zz; ++z) {
|
|
||||||
const executorData = declutterReplays[zs[z].toString()];
|
|
||||||
let currentExecutor;
|
|
||||||
for (let i = 0, ii = executorData.length; i < ii; ) {
|
|
||||||
const executor = executorData[i++];
|
|
||||||
const transform = executorData[i++];
|
|
||||||
executor.execute(context, 1, transform, rotation, snapToPixel);
|
|
||||||
if (executor !== currentExecutor && executor.declutterItems.length > 0) {
|
|
||||||
currentExecutor = executor;
|
|
||||||
declutterItems.push({
|
|
||||||
items: executor.declutterItems,
|
|
||||||
opacity: opacity,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default ExecutorGroup;
|
export default ExecutorGroup;
|
||||||
|
|||||||
@@ -14,12 +14,6 @@ class CanvasImageBuilder extends CanvasBuilder {
|
|||||||
constructor(tolerance, maxExtent, resolution, pixelRatio) {
|
constructor(tolerance, maxExtent, resolution, pixelRatio) {
|
||||||
super(tolerance, maxExtent, resolution, pixelRatio);
|
super(tolerance, maxExtent, resolution, pixelRatio);
|
||||||
|
|
||||||
/**
|
|
||||||
* @private
|
|
||||||
* @type {import("../canvas.js").DeclutterGroups}
|
|
||||||
*/
|
|
||||||
this.declutterGroups_ = null;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @private
|
* @private
|
||||||
* @type {HTMLCanvasElement|HTMLVideoElement|HTMLImageElement}
|
* @type {HTMLCanvasElement|HTMLVideoElement|HTMLImageElement}
|
||||||
@@ -97,6 +91,13 @@ class CanvasImageBuilder extends CanvasBuilder {
|
|||||||
* @type {number|undefined}
|
* @type {number|undefined}
|
||||||
*/
|
*/
|
||||||
this.width_ = undefined;
|
this.width_ = undefined;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Data shared with a text builder for combined decluttering.
|
||||||
|
* @private
|
||||||
|
* @type {import("../canvas.js").DeclutterImageWithText}
|
||||||
|
*/
|
||||||
|
this.declutterImageWithText_ = undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -120,7 +121,6 @@ class CanvasImageBuilder extends CanvasBuilder {
|
|||||||
// Remaining arguments to DRAW_IMAGE are in alphabetical order
|
// Remaining arguments to DRAW_IMAGE are in alphabetical order
|
||||||
this.anchorX_ * this.imagePixelRatio_,
|
this.anchorX_ * this.imagePixelRatio_,
|
||||||
this.anchorY_ * this.imagePixelRatio_,
|
this.anchorY_ * this.imagePixelRatio_,
|
||||||
this.declutterGroups_,
|
|
||||||
Math.ceil(this.height_ * this.imagePixelRatio_),
|
Math.ceil(this.height_ * this.imagePixelRatio_),
|
||||||
this.opacity_,
|
this.opacity_,
|
||||||
this.originX_,
|
this.originX_,
|
||||||
@@ -132,6 +132,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.declutterImageWithText_,
|
||||||
]);
|
]);
|
||||||
this.hitDetectionInstructions.push([
|
this.hitDetectionInstructions.push([
|
||||||
CanvasInstruction.DRAW_IMAGE,
|
CanvasInstruction.DRAW_IMAGE,
|
||||||
@@ -141,7 +142,6 @@ class CanvasImageBuilder extends CanvasBuilder {
|
|||||||
// Remaining arguments to DRAW_IMAGE are in alphabetical order
|
// Remaining arguments to DRAW_IMAGE are in alphabetical order
|
||||||
this.anchorX_,
|
this.anchorX_,
|
||||||
this.anchorY_,
|
this.anchorY_,
|
||||||
this.declutterGroups_,
|
|
||||||
this.height_,
|
this.height_,
|
||||||
this.opacity_,
|
this.opacity_,
|
||||||
this.originX_,
|
this.originX_,
|
||||||
@@ -150,6 +150,7 @@ class CanvasImageBuilder extends CanvasBuilder {
|
|||||||
this.rotation_,
|
this.rotation_,
|
||||||
this.scale_,
|
this.scale_,
|
||||||
this.width_,
|
this.width_,
|
||||||
|
this.declutterImageWithText_,
|
||||||
]);
|
]);
|
||||||
this.endGeometry(feature);
|
this.endGeometry(feature);
|
||||||
}
|
}
|
||||||
@@ -175,7 +176,6 @@ class CanvasImageBuilder extends CanvasBuilder {
|
|||||||
// Remaining arguments to DRAW_IMAGE are in alphabetical order
|
// Remaining arguments to DRAW_IMAGE are in alphabetical order
|
||||||
this.anchorX_ * this.imagePixelRatio_,
|
this.anchorX_ * this.imagePixelRatio_,
|
||||||
this.anchorY_ * this.imagePixelRatio_,
|
this.anchorY_ * this.imagePixelRatio_,
|
||||||
this.declutterGroups_,
|
|
||||||
Math.ceil(this.height_ * this.imagePixelRatio_),
|
Math.ceil(this.height_ * this.imagePixelRatio_),
|
||||||
this.opacity_,
|
this.opacity_,
|
||||||
this.originX_,
|
this.originX_,
|
||||||
@@ -187,6 +187,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.declutterImageWithText_,
|
||||||
]);
|
]);
|
||||||
this.hitDetectionInstructions.push([
|
this.hitDetectionInstructions.push([
|
||||||
CanvasInstruction.DRAW_IMAGE,
|
CanvasInstruction.DRAW_IMAGE,
|
||||||
@@ -196,7 +197,6 @@ class CanvasImageBuilder extends CanvasBuilder {
|
|||||||
// Remaining arguments to DRAW_IMAGE are in alphabetical order
|
// Remaining arguments to DRAW_IMAGE are in alphabetical order
|
||||||
this.anchorX_,
|
this.anchorX_,
|
||||||
this.anchorY_,
|
this.anchorY_,
|
||||||
this.declutterGroups_,
|
|
||||||
this.height_,
|
this.height_,
|
||||||
this.opacity_,
|
this.opacity_,
|
||||||
this.originX_,
|
this.originX_,
|
||||||
@@ -205,12 +205,13 @@ class CanvasImageBuilder extends CanvasBuilder {
|
|||||||
this.rotation_,
|
this.rotation_,
|
||||||
this.scale_,
|
this.scale_,
|
||||||
this.width_,
|
this.width_,
|
||||||
|
this.declutterImageWithText_,
|
||||||
]);
|
]);
|
||||||
this.endGeometry(feature);
|
this.endGeometry(feature);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return {import("./Builder.js").SerializableInstructions} the serializable instructions.
|
* @return {import("../canvas.js").SerializableInstructions} the serializable instructions.
|
||||||
*/
|
*/
|
||||||
finish() {
|
finish() {
|
||||||
this.reverseHitDetectionInstructions();
|
this.reverseHitDetectionInstructions();
|
||||||
@@ -233,9 +234,9 @@ class CanvasImageBuilder extends CanvasBuilder {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {import("../../style/Image.js").default} imageStyle Image style.
|
* @param {import("../../style/Image.js").default} imageStyle Image style.
|
||||||
* @param {import("../canvas.js").DeclutterGroup} declutterGroups Declutter.
|
* @param {Object=} opt_sharedData Shared data.
|
||||||
*/
|
*/
|
||||||
setImageStyle(imageStyle, declutterGroups) {
|
setImageStyle(imageStyle, opt_sharedData) {
|
||||||
const anchor = imageStyle.getAnchor();
|
const anchor = imageStyle.getAnchor();
|
||||||
const size = imageStyle.getSize();
|
const size = imageStyle.getSize();
|
||||||
const hitDetectionImage = imageStyle.getHitDetectionImage();
|
const hitDetectionImage = imageStyle.getHitDetectionImage();
|
||||||
@@ -244,7 +245,6 @@ class CanvasImageBuilder extends CanvasBuilder {
|
|||||||
this.imagePixelRatio_ = imageStyle.getPixelRatio(this.pixelRatio);
|
this.imagePixelRatio_ = imageStyle.getPixelRatio(this.pixelRatio);
|
||||||
this.anchorX_ = anchor[0];
|
this.anchorX_ = anchor[0];
|
||||||
this.anchorY_ = anchor[1];
|
this.anchorY_ = anchor[1];
|
||||||
this.declutterGroups_ = declutterGroups;
|
|
||||||
this.hitDetectionImage_ = hitDetectionImage;
|
this.hitDetectionImage_ = hitDetectionImage;
|
||||||
this.image_ = image;
|
this.image_ = image;
|
||||||
this.height_ = size[1];
|
this.height_ = size[1];
|
||||||
@@ -255,6 +255,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.declutterImageWithText_ = opt_sharedData;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -127,7 +127,7 @@ class CanvasLineStringBuilder extends CanvasBuilder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return {import("./Builder.js").SerializableInstructions} the serializable instructions.
|
* @return {import("../canvas.js").SerializableInstructions} the serializable instructions.
|
||||||
*/
|
*/
|
||||||
finish() {
|
finish() {
|
||||||
const state = this.state;
|
const state = this.state;
|
||||||
|
|||||||
@@ -220,7 +220,7 @@ class CanvasPolygonBuilder extends CanvasBuilder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return {import("./Builder.js").SerializableInstructions} the serializable instructions.
|
* @return {import("../canvas.js").SerializableInstructions} the serializable instructions.
|
||||||
*/
|
*/
|
||||||
finish() {
|
finish() {
|
||||||
this.reverseHitDetectionInstructions();
|
this.reverseHitDetectionInstructions();
|
||||||
|
|||||||
@@ -52,12 +52,6 @@ class CanvasTextBuilder extends CanvasBuilder {
|
|||||||
constructor(tolerance, maxExtent, resolution, pixelRatio) {
|
constructor(tolerance, maxExtent, resolution, pixelRatio) {
|
||||||
super(tolerance, maxExtent, resolution, pixelRatio);
|
super(tolerance, maxExtent, resolution, pixelRatio);
|
||||||
|
|
||||||
/**
|
|
||||||
* @private
|
|
||||||
* @type {import("../canvas.js").DeclutterGroups}
|
|
||||||
*/
|
|
||||||
this.declutterGroups_;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @private
|
* @private
|
||||||
* @type {Array<HTMLCanvasElement>}
|
* @type {Array<HTMLCanvasElement>}
|
||||||
@@ -144,10 +138,17 @@ class CanvasTextBuilder extends CanvasBuilder {
|
|||||||
* @type {string}
|
* @type {string}
|
||||||
*/
|
*/
|
||||||
this.strokeKey_ = '';
|
this.strokeKey_ = '';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Data shared with an image builder for combined decluttering.
|
||||||
|
* @private
|
||||||
|
* @type {import("../canvas.js").DeclutterImageWithText}
|
||||||
|
*/
|
||||||
|
this.declutterImageWithText_ = undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return {import("./Builder.js").SerializableInstructions} the serializable instructions.
|
* @return {import("../canvas.js").SerializableInstructions} the serializable instructions.
|
||||||
*/
|
*/
|
||||||
finish() {
|
finish() {
|
||||||
const instructions = super.finish();
|
const instructions = super.finish();
|
||||||
@@ -226,12 +227,7 @@ class CanvasTextBuilder extends CanvasBuilder {
|
|||||||
}
|
}
|
||||||
const end = coordinates.length;
|
const end = coordinates.length;
|
||||||
flatOffset = ends[o];
|
flatOffset = ends[o];
|
||||||
const declutterGroup = this.declutterGroups_
|
this.drawChars_(begin, end);
|
||||||
? o === 0
|
|
||||||
? this.declutterGroups_[0]
|
|
||||||
: [].concat(this.declutterGroups_[0])
|
|
||||||
: null;
|
|
||||||
this.drawChars_(begin, end, declutterGroup);
|
|
||||||
begin = end;
|
begin = end;
|
||||||
}
|
}
|
||||||
this.endGeometry(feature);
|
this.endGeometry(feature);
|
||||||
@@ -331,7 +327,6 @@ class CanvasTextBuilder extends CanvasBuilder {
|
|||||||
null,
|
null,
|
||||||
NaN,
|
NaN,
|
||||||
NaN,
|
NaN,
|
||||||
this.declutterGroups_,
|
|
||||||
NaN,
|
NaN,
|
||||||
1,
|
1,
|
||||||
0,
|
0,
|
||||||
@@ -340,6 +335,7 @@ class CanvasTextBuilder extends CanvasBuilder {
|
|||||||
this.textRotation_,
|
this.textRotation_,
|
||||||
[1, 1],
|
[1, 1],
|
||||||
NaN,
|
NaN,
|
||||||
|
this.declutterImageWithText_,
|
||||||
padding == defaultPadding
|
padding == defaultPadding
|
||||||
? defaultPadding
|
? defaultPadding
|
||||||
: padding.map(function (p) {
|
: padding.map(function (p) {
|
||||||
@@ -363,7 +359,6 @@ class CanvasTextBuilder extends CanvasBuilder {
|
|||||||
null,
|
null,
|
||||||
NaN,
|
NaN,
|
||||||
NaN,
|
NaN,
|
||||||
this.declutterGroups_,
|
|
||||||
NaN,
|
NaN,
|
||||||
1,
|
1,
|
||||||
0,
|
0,
|
||||||
@@ -372,6 +367,7 @@ class CanvasTextBuilder extends CanvasBuilder {
|
|||||||
this.textRotation_,
|
this.textRotation_,
|
||||||
[scale, scale],
|
[scale, scale],
|
||||||
NaN,
|
NaN,
|
||||||
|
this.declutterImageWithText_,
|
||||||
padding,
|
padding,
|
||||||
!!textState.backgroundFill,
|
!!textState.backgroundFill,
|
||||||
!!textState.backgroundStroke,
|
!!textState.backgroundStroke,
|
||||||
@@ -433,9 +429,8 @@ class CanvasTextBuilder extends CanvasBuilder {
|
|||||||
* @private
|
* @private
|
||||||
* @param {number} begin Begin.
|
* @param {number} begin Begin.
|
||||||
* @param {number} end End.
|
* @param {number} end End.
|
||||||
* @param {import("../canvas.js").DeclutterGroup} declutterGroup Declutter group.
|
|
||||||
*/
|
*/
|
||||||
drawChars_(begin, end, declutterGroup) {
|
drawChars_(begin, end) {
|
||||||
const strokeState = this.textStrokeState_;
|
const strokeState = this.textStrokeState_;
|
||||||
const textState = this.textState_;
|
const textState = this.textState_;
|
||||||
|
|
||||||
@@ -458,7 +453,6 @@ class CanvasTextBuilder extends CanvasBuilder {
|
|||||||
begin,
|
begin,
|
||||||
end,
|
end,
|
||||||
baseline,
|
baseline,
|
||||||
declutterGroup,
|
|
||||||
textState.overflow,
|
textState.overflow,
|
||||||
fillKey,
|
fillKey,
|
||||||
textState.maxAngle,
|
textState.maxAngle,
|
||||||
@@ -475,7 +469,6 @@ class CanvasTextBuilder extends CanvasBuilder {
|
|||||||
begin,
|
begin,
|
||||||
end,
|
end,
|
||||||
baseline,
|
baseline,
|
||||||
declutterGroup,
|
|
||||||
textState.overflow,
|
textState.overflow,
|
||||||
fillKey,
|
fillKey,
|
||||||
textState.maxAngle,
|
textState.maxAngle,
|
||||||
@@ -491,15 +484,13 @@ class CanvasTextBuilder extends CanvasBuilder {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {import("../../style/Text.js").default} textStyle Text style.
|
* @param {import("../../style/Text.js").default} textStyle Text style.
|
||||||
* @param {import("../canvas.js").DeclutterGroups} declutterGroups Declutter.
|
* @param {Object=} opt_sharedData Shared data.
|
||||||
*/
|
*/
|
||||||
setTextStyle(textStyle, declutterGroups) {
|
setTextStyle(textStyle, opt_sharedData) {
|
||||||
let textState, fillState, strokeState;
|
let textState, fillState, strokeState;
|
||||||
if (!textStyle) {
|
if (!textStyle) {
|
||||||
this.text_ = '';
|
this.text_ = '';
|
||||||
} else {
|
} else {
|
||||||
this.declutterGroups_ = declutterGroups;
|
|
||||||
|
|
||||||
const textFillStyle = textStyle.getFill();
|
const textFillStyle = textStyle.getFill();
|
||||||
if (!textFillStyle) {
|
if (!textFillStyle) {
|
||||||
fillState = null;
|
fillState = null;
|
||||||
@@ -595,6 +586,7 @@ class CanvasTextBuilder extends CanvasBuilder {
|
|||||||
: '|' + getUid(fillState.fillStyle)
|
: '|' + getUid(fillState.fillStyle)
|
||||||
: '';
|
: '';
|
||||||
}
|
}
|
||||||
|
this.declutterImageWithText_ = opt_sharedData;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -102,6 +102,10 @@ class CompositeMapRenderer extends MapRenderer {
|
|||||||
const viewState = frameState.viewState;
|
const viewState = frameState.viewState;
|
||||||
|
|
||||||
this.children_.length = 0;
|
this.children_.length = 0;
|
||||||
|
/**
|
||||||
|
* @type {Array<import("../layer/BaseVector.js").default>}
|
||||||
|
*/
|
||||||
|
const declutterLayers = [];
|
||||||
let previousElement = null;
|
let previousElement = null;
|
||||||
for (let i = 0, ii = layerStatesArray.length; i < ii; ++i) {
|
for (let i = 0, ii = layerStatesArray.length; i < ii; ++i) {
|
||||||
const layerState = layerStatesArray[i];
|
const layerState = layerStatesArray[i];
|
||||||
@@ -123,8 +127,13 @@ class CompositeMapRenderer extends MapRenderer {
|
|||||||
this.children_.push(element);
|
this.children_.push(element);
|
||||||
previousElement = element;
|
previousElement = element;
|
||||||
}
|
}
|
||||||
|
if ('getDeclutter' in layer) {
|
||||||
|
declutterLayers.push(layer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (let i = declutterLayers.length - 1; i >= 0; --i) {
|
||||||
|
declutterLayers[i].renderDeclutter(frameState);
|
||||||
}
|
}
|
||||||
super.renderFrame(frameState);
|
|
||||||
|
|
||||||
replaceChildren(this.element_, this.children_);
|
replaceChildren(this.element_, this.children_);
|
||||||
|
|
||||||
|
|||||||
@@ -25,6 +25,11 @@ class LayerRenderer extends Observable {
|
|||||||
* @type {LayerType}
|
* @type {LayerType}
|
||||||
*/
|
*/
|
||||||
this.layer_ = layer;
|
this.layer_ = layer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @type {import("../render/canvas/ExecutorGroup").default}
|
||||||
|
*/
|
||||||
|
this.declutterExecutorGroup = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -101,17 +106,10 @@ class LayerRenderer extends Observable {
|
|||||||
* @param {import("../PluggableMap.js").FrameState} frameState Frame state.
|
* @param {import("../PluggableMap.js").FrameState} frameState Frame state.
|
||||||
* @param {number} hitTolerance Hit tolerance in pixels.
|
* @param {number} hitTolerance Hit tolerance in pixels.
|
||||||
* @param {function(import("../Feature.js").FeatureLike, import("../layer/Layer.js").default): T} callback Feature callback.
|
* @param {function(import("../Feature.js").FeatureLike, import("../layer/Layer.js").default): T} callback Feature callback.
|
||||||
* @param {Array<import("../Feature.js").FeatureLike>} declutteredFeatures Decluttered features.
|
|
||||||
* @return {T|void} Callback result.
|
* @return {T|void} Callback result.
|
||||||
* @template T
|
* @template T
|
||||||
*/
|
*/
|
||||||
forEachFeatureAtCoordinate(
|
forEachFeatureAtCoordinate(coordinate, frameState, hitTolerance, callback) {}
|
||||||
coordinate,
|
|
||||||
frameState,
|
|
||||||
hitTolerance,
|
|
||||||
callback,
|
|
||||||
declutteredFeatures
|
|
||||||
) {}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @abstract
|
* @abstract
|
||||||
|
|||||||
+3
-15
@@ -8,7 +8,6 @@ import {compose as composeTransform, makeInverse} from '../transform.js';
|
|||||||
import {getWidth} from '../extent.js';
|
import {getWidth} from '../extent.js';
|
||||||
import {shared as iconImageCache} from '../style/IconImageCache.js';
|
import {shared as iconImageCache} from '../style/IconImageCache.js';
|
||||||
import {inView} from '../layer/Layer.js';
|
import {inView} from '../layer/Layer.js';
|
||||||
import {renderDeclutterItems} from '../render.js';
|
|
||||||
import {wrapX} from '../coordinate.js';
|
import {wrapX} from '../coordinate.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -26,11 +25,6 @@ class MapRenderer extends Disposable {
|
|||||||
* @type {import("../PluggableMap.js").default}
|
* @type {import("../PluggableMap.js").default}
|
||||||
*/
|
*/
|
||||||
this.map_ = map;
|
this.map_ = map;
|
||||||
|
|
||||||
/**
|
|
||||||
* @private
|
|
||||||
*/
|
|
||||||
this.declutterTree_ = null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -116,12 +110,6 @@ class MapRenderer extends Disposable {
|
|||||||
|
|
||||||
const layerStates = frameState.layerStatesArray;
|
const layerStates = frameState.layerStatesArray;
|
||||||
const numLayers = layerStates.length;
|
const numLayers = layerStates.length;
|
||||||
let declutteredFeatures;
|
|
||||||
if (this.declutterTree_) {
|
|
||||||
declutteredFeatures = this.declutterTree_.all().map(function (entry) {
|
|
||||||
return entry.value;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const tmpCoord = [];
|
const tmpCoord = [];
|
||||||
for (let i = 0; i < offsets.length; i++) {
|
for (let i = 0; i < offsets.length; i++) {
|
||||||
@@ -149,8 +137,7 @@ class MapRenderer extends Disposable {
|
|||||||
tmpCoord,
|
tmpCoord,
|
||||||
frameState,
|
frameState,
|
||||||
hitTolerance,
|
hitTolerance,
|
||||||
callback,
|
callback
|
||||||
declutteredFeatures
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (result) {
|
if (result) {
|
||||||
@@ -224,10 +211,11 @@ class MapRenderer extends Disposable {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Render.
|
* Render.
|
||||||
|
* @abstract
|
||||||
* @param {?import("../PluggableMap.js").FrameState} frameState Frame state.
|
* @param {?import("../PluggableMap.js").FrameState} frameState Frame state.
|
||||||
*/
|
*/
|
||||||
renderFrame(frameState) {
|
renderFrame(frameState) {
|
||||||
this.declutterTree_ = renderDeclutterItems(frameState, this.declutterTree_);
|
abstract();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -6,11 +6,11 @@ import CanvasVectorLayerRenderer from './VectorLayer.js';
|
|||||||
import EventType from '../../events/EventType.js';
|
import EventType from '../../events/EventType.js';
|
||||||
import ImageCanvas from '../../ImageCanvas.js';
|
import ImageCanvas from '../../ImageCanvas.js';
|
||||||
import ImageState from '../../ImageState.js';
|
import ImageState from '../../ImageState.js';
|
||||||
|
import RBush from 'rbush';
|
||||||
import ViewHint from '../../ViewHint.js';
|
import ViewHint from '../../ViewHint.js';
|
||||||
import {apply, compose, create} from '../../transform.js';
|
import {apply, compose, create} from '../../transform.js';
|
||||||
import {assign} from '../../obj.js';
|
import {assign} from '../../obj.js';
|
||||||
import {getHeight, getWidth, isEmpty, scaleFromCenter} from '../../extent.js';
|
import {getHeight, getWidth, isEmpty, scaleFromCenter} from '../../extent.js';
|
||||||
import {renderDeclutterItems} from '../../render.js';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @classdesc
|
* @classdesc
|
||||||
@@ -115,7 +115,7 @@ class CanvasVectorImageLayerRenderer extends CanvasImageLayerRenderer {
|
|||||||
{},
|
{},
|
||||||
frameState,
|
frameState,
|
||||||
{
|
{
|
||||||
declutterItems: [],
|
declutterTree: new RBush(9),
|
||||||
extent: renderedExtent,
|
extent: renderedExtent,
|
||||||
size: [width, height],
|
size: [width, height],
|
||||||
viewState: /** @type {import("../../View.js").State} */ (assign(
|
viewState: /** @type {import("../../View.js").State} */ (assign(
|
||||||
@@ -139,7 +139,7 @@ class CanvasVectorImageLayerRenderer extends CanvasImageLayerRenderer {
|
|||||||
) {
|
) {
|
||||||
vectorRenderer.clipping = false;
|
vectorRenderer.clipping = false;
|
||||||
vectorRenderer.renderFrame(imageFrameState, null);
|
vectorRenderer.renderFrame(imageFrameState, null);
|
||||||
renderDeclutterItems(imageFrameState, null);
|
vectorRenderer.renderDeclutter(imageFrameState);
|
||||||
callback();
|
callback();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -186,37 +186,32 @@ class CanvasVectorImageLayerRenderer extends CanvasImageLayerRenderer {
|
|||||||
*/
|
*/
|
||||||
postRender() {}
|
postRender() {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
renderDeclutter() {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {import("../../coordinate.js").Coordinate} coordinate Coordinate.
|
* @param {import("../../coordinate.js").Coordinate} coordinate Coordinate.
|
||||||
* @param {import("../../PluggableMap.js").FrameState} frameState Frame state.
|
* @param {import("../../PluggableMap.js").FrameState} frameState Frame state.
|
||||||
* @param {number} hitTolerance Hit tolerance in pixels.
|
* @param {number} hitTolerance Hit tolerance in pixels.
|
||||||
* @param {function(import("../../Feature.js").FeatureLike, import("../../layer/Layer.js").default): T} callback Feature callback.
|
* @param {function(import("../../Feature.js").FeatureLike, import("../../layer/Layer.js").default): T} callback Feature callback.
|
||||||
* @param {Array<import("../../Feature.js").FeatureLike>} declutteredFeatures Decluttered features.
|
|
||||||
* @return {T|void} Callback result.
|
* @return {T|void} Callback result.
|
||||||
* @template T
|
* @template T
|
||||||
*/
|
*/
|
||||||
forEachFeatureAtCoordinate(
|
forEachFeatureAtCoordinate(coordinate, frameState, hitTolerance, callback) {
|
||||||
coordinate,
|
|
||||||
frameState,
|
|
||||||
hitTolerance,
|
|
||||||
callback,
|
|
||||||
declutteredFeatures
|
|
||||||
) {
|
|
||||||
if (this.vectorRenderer_) {
|
if (this.vectorRenderer_) {
|
||||||
return this.vectorRenderer_.forEachFeatureAtCoordinate(
|
return this.vectorRenderer_.forEachFeatureAtCoordinate(
|
||||||
coordinate,
|
coordinate,
|
||||||
frameState,
|
frameState,
|
||||||
hitTolerance,
|
hitTolerance,
|
||||||
callback,
|
callback
|
||||||
declutteredFeatures
|
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
return super.forEachFeatureAtCoordinate(
|
return super.forEachFeatureAtCoordinate(
|
||||||
coordinate,
|
coordinate,
|
||||||
frameState,
|
frameState,
|
||||||
hitTolerance,
|
hitTolerance,
|
||||||
callback,
|
callback
|
||||||
declutteredFeatures
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,9 +3,7 @@
|
|||||||
*/
|
*/
|
||||||
import CanvasBuilderGroup from '../../render/canvas/BuilderGroup.js';
|
import CanvasBuilderGroup from '../../render/canvas/BuilderGroup.js';
|
||||||
import CanvasLayerRenderer from './Layer.js';
|
import CanvasLayerRenderer from './Layer.js';
|
||||||
import ExecutorGroup, {
|
import ExecutorGroup from '../../render/canvas/ExecutorGroup.js';
|
||||||
replayDeclutter,
|
|
||||||
} from '../../render/canvas/ExecutorGroup.js';
|
|
||||||
import ViewHint from '../../ViewHint.js';
|
import ViewHint from '../../ViewHint.js';
|
||||||
import {
|
import {
|
||||||
apply,
|
apply,
|
||||||
@@ -130,6 +128,11 @@ class CanvasVectorLayerRenderer extends CanvasLayerRenderer {
|
|||||||
*/
|
*/
|
||||||
this.replayGroupChanged = true;
|
this.replayGroupChanged = true;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @type {import("../../render/canvas/ExecutorGroup").default}
|
||||||
|
*/
|
||||||
|
this.declutterExecutorGroup = null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Clipping to be performed by `renderFrame()`
|
* Clipping to be performed by `renderFrame()`
|
||||||
* @type {boolean}
|
* @type {boolean}
|
||||||
@@ -150,6 +153,73 @@ class CanvasVectorLayerRenderer extends CanvasLayerRenderer {
|
|||||||
super.useContainer(target, transform, opacity);
|
super.useContainer(target, transform, opacity);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {ExecutorGroup} executorGroup Executor group.
|
||||||
|
* @param {import("../../PluggableMap.js").FrameState} frameState Frame state.
|
||||||
|
* @param {import("rbush").default=} opt_declutterTree Declutter tree.
|
||||||
|
*/
|
||||||
|
renderWorlds(executorGroup, frameState, opt_declutterTree) {
|
||||||
|
const extent = frameState.extent;
|
||||||
|
const viewState = frameState.viewState;
|
||||||
|
const center = viewState.center;
|
||||||
|
const resolution = viewState.resolution;
|
||||||
|
const projection = viewState.projection;
|
||||||
|
const rotation = viewState.rotation;
|
||||||
|
const projectionExtent = projection.getExtent();
|
||||||
|
const vectorSource = this.getLayer().getSource();
|
||||||
|
const pixelRatio = frameState.pixelRatio;
|
||||||
|
const viewHints = frameState.viewHints;
|
||||||
|
const snapToPixel = !(
|
||||||
|
viewHints[ViewHint.ANIMATING] || viewHints[ViewHint.INTERACTING]
|
||||||
|
);
|
||||||
|
const context = this.context;
|
||||||
|
const width = Math.round(frameState.size[0] * pixelRatio);
|
||||||
|
const height = Math.round(frameState.size[1] * pixelRatio);
|
||||||
|
|
||||||
|
const multiWorld = vectorSource.getWrapX() && projection.canWrapX();
|
||||||
|
const worldWidth = multiWorld ? getWidth(projectionExtent) : null;
|
||||||
|
const endWorld = multiWorld
|
||||||
|
? Math.ceil((extent[2] - projectionExtent[2]) / worldWidth) + 1
|
||||||
|
: 1;
|
||||||
|
let world = multiWorld
|
||||||
|
? Math.floor((extent[0] - projectionExtent[0]) / worldWidth)
|
||||||
|
: 0;
|
||||||
|
do {
|
||||||
|
const transform = this.getRenderTransform(
|
||||||
|
center,
|
||||||
|
resolution,
|
||||||
|
rotation,
|
||||||
|
pixelRatio,
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
world * worldWidth
|
||||||
|
);
|
||||||
|
executorGroup.execute(
|
||||||
|
context,
|
||||||
|
1,
|
||||||
|
transform,
|
||||||
|
rotation,
|
||||||
|
snapToPixel,
|
||||||
|
undefined,
|
||||||
|
opt_declutterTree
|
||||||
|
);
|
||||||
|
} while (++world < endWorld);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Render declutter items for this layer
|
||||||
|
* @param {import("../../PluggableMap.js").FrameState} frameState Frame state.
|
||||||
|
*/
|
||||||
|
renderDeclutter(frameState) {
|
||||||
|
if (this.declutterExecutorGroup) {
|
||||||
|
this.renderWorlds(
|
||||||
|
this.declutterExecutorGroup,
|
||||||
|
frameState,
|
||||||
|
frameState.declutterTree
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Render the layer.
|
* Render the layer.
|
||||||
* @param {import("../../PluggableMap.js").FrameState} frameState Frame state.
|
* @param {import("../../PluggableMap.js").FrameState} frameState Frame state.
|
||||||
@@ -171,7 +241,11 @@ class CanvasVectorLayerRenderer extends CanvasLayerRenderer {
|
|||||||
const canvas = context.canvas;
|
const canvas = context.canvas;
|
||||||
|
|
||||||
const replayGroup = this.replayGroup_;
|
const replayGroup = this.replayGroup_;
|
||||||
if (!replayGroup || replayGroup.isEmpty()) {
|
const declutterExecutorGroup = this.declutterExecutorGroup;
|
||||||
|
if (
|
||||||
|
(!replayGroup || replayGroup.isEmpty()) &&
|
||||||
|
(!declutterExecutorGroup || declutterExecutorGroup.isEmpty())
|
||||||
|
) {
|
||||||
if (!this.containerReused && canvas.width > 0) {
|
if (!this.containerReused && canvas.width > 0) {
|
||||||
canvas.width = 0;
|
canvas.width = 0;
|
||||||
}
|
}
|
||||||
@@ -193,14 +267,8 @@ class CanvasVectorLayerRenderer extends CanvasLayerRenderer {
|
|||||||
|
|
||||||
this.preRender(context, frameState);
|
this.preRender(context, frameState);
|
||||||
|
|
||||||
const extent = frameState.extent;
|
|
||||||
const viewState = frameState.viewState;
|
const viewState = frameState.viewState;
|
||||||
const center = viewState.center;
|
|
||||||
const resolution = viewState.resolution;
|
|
||||||
const projection = viewState.projection;
|
const projection = viewState.projection;
|
||||||
const rotation = viewState.rotation;
|
|
||||||
const projectionExtent = projection.getExtent();
|
|
||||||
const vectorSource = this.getLayer().getSource();
|
|
||||||
|
|
||||||
// clipped rendering if layer extent is set
|
// clipped rendering if layer extent is set
|
||||||
let clipped = false;
|
let clipped = false;
|
||||||
@@ -214,56 +282,7 @@ class CanvasVectorLayerRenderer extends CanvasLayerRenderer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const viewHints = frameState.viewHints;
|
this.renderWorlds(replayGroup, frameState);
|
||||||
const snapToPixel = !(
|
|
||||||
viewHints[ViewHint.ANIMATING] || viewHints[ViewHint.INTERACTING]
|
|
||||||
);
|
|
||||||
|
|
||||||
const declutterReplays = this.getLayer().getDeclutter() ? {} : null;
|
|
||||||
|
|
||||||
const multiWorld = vectorSource.getWrapX() && projection.canWrapX();
|
|
||||||
const worldWidth = multiWorld ? getWidth(projectionExtent) : null;
|
|
||||||
const endWorld = multiWorld
|
|
||||||
? Math.ceil((extent[2] - projectionExtent[2]) / worldWidth) + 1
|
|
||||||
: 1;
|
|
||||||
let world = multiWorld
|
|
||||||
? Math.floor((extent[0] - projectionExtent[0]) / worldWidth)
|
|
||||||
: 0;
|
|
||||||
do {
|
|
||||||
const transform = this.getRenderTransform(
|
|
||||||
center,
|
|
||||||
resolution,
|
|
||||||
rotation,
|
|
||||||
pixelRatio,
|
|
||||||
width,
|
|
||||||
height,
|
|
||||||
world * worldWidth
|
|
||||||
);
|
|
||||||
replayGroup.execute(
|
|
||||||
context,
|
|
||||||
1,
|
|
||||||
transform,
|
|
||||||
rotation,
|
|
||||||
snapToPixel,
|
|
||||||
undefined,
|
|
||||||
declutterReplays
|
|
||||||
);
|
|
||||||
} while (++world < endWorld);
|
|
||||||
|
|
||||||
if (declutterReplays) {
|
|
||||||
const viewHints = frameState.viewHints;
|
|
||||||
const hifi = !(
|
|
||||||
viewHints[ViewHint.ANIMATING] || viewHints[ViewHint.INTERACTING]
|
|
||||||
);
|
|
||||||
replayDeclutter(
|
|
||||||
declutterReplays,
|
|
||||||
context,
|
|
||||||
rotation,
|
|
||||||
1,
|
|
||||||
hifi,
|
|
||||||
frameState.declutterItems
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (clipped) {
|
if (clipped) {
|
||||||
context.restore();
|
context.restore();
|
||||||
@@ -388,44 +407,51 @@ class CanvasVectorLayerRenderer extends CanvasLayerRenderer {
|
|||||||
* @param {import("../../PluggableMap.js").FrameState} frameState Frame state.
|
* @param {import("../../PluggableMap.js").FrameState} frameState Frame state.
|
||||||
* @param {number} hitTolerance Hit tolerance in pixels.
|
* @param {number} hitTolerance Hit tolerance in pixels.
|
||||||
* @param {function(import("../../Feature.js").FeatureLike, import("../../layer/Layer.js").default): T} callback Feature callback.
|
* @param {function(import("../../Feature.js").FeatureLike, import("../../layer/Layer.js").default): T} callback Feature callback.
|
||||||
* @param {Array<import("../../Feature.js").FeatureLike>} declutteredFeatures Decluttered features.
|
|
||||||
* @return {T|void} Callback result.
|
* @return {T|void} Callback result.
|
||||||
* @template T
|
* @template T
|
||||||
*/
|
*/
|
||||||
forEachFeatureAtCoordinate(
|
forEachFeatureAtCoordinate(coordinate, frameState, hitTolerance, callback) {
|
||||||
coordinate,
|
|
||||||
frameState,
|
|
||||||
hitTolerance,
|
|
||||||
callback,
|
|
||||||
declutteredFeatures
|
|
||||||
) {
|
|
||||||
if (!this.replayGroup_) {
|
if (!this.replayGroup_) {
|
||||||
return undefined;
|
return undefined;
|
||||||
} else {
|
} else {
|
||||||
const resolution = frameState.viewState.resolution;
|
const resolution = frameState.viewState.resolution;
|
||||||
const rotation = frameState.viewState.rotation;
|
const rotation = frameState.viewState.rotation;
|
||||||
const layer = this.getLayer();
|
const layer = this.getLayer();
|
||||||
|
|
||||||
/** @type {!Object<string, boolean>} */
|
/** @type {!Object<string, boolean>} */
|
||||||
const features = {};
|
const features = {};
|
||||||
|
|
||||||
const result = this.replayGroup_.forEachFeatureAtCoordinate(
|
/**
|
||||||
coordinate,
|
* @param {import("../../Feature.js").FeatureLike} feature Feature.
|
||||||
resolution,
|
* @return {?} Callback result.
|
||||||
rotation,
|
*/
|
||||||
hitTolerance,
|
const featureCallback = function (feature) {
|
||||||
/**
|
const key = getUid(feature);
|
||||||
* @param {import("../../Feature.js").FeatureLike} feature Feature.
|
if (!(key in features)) {
|
||||||
* @return {?} Callback result.
|
features[key] = true;
|
||||||
*/
|
return callback(feature, layer);
|
||||||
function (feature) {
|
}
|
||||||
const key = getUid(feature);
|
};
|
||||||
if (!(key in features)) {
|
|
||||||
features[key] = true;
|
let result;
|
||||||
return callback(feature, layer);
|
const executorGroups = [this.replayGroup_];
|
||||||
}
|
if (this.declutterExecutorGroup) {
|
||||||
},
|
executorGroups.push(this.declutterExecutorGroup);
|
||||||
layer.getDeclutter() ? declutteredFeatures : null
|
}
|
||||||
);
|
executorGroups.forEach((executorGroup) => {
|
||||||
|
result =
|
||||||
|
result ||
|
||||||
|
executorGroup.forEachFeatureAtCoordinate(
|
||||||
|
coordinate,
|
||||||
|
resolution,
|
||||||
|
rotation,
|
||||||
|
hitTolerance,
|
||||||
|
featureCallback,
|
||||||
|
executorGroup === this.declutterExecutorGroup
|
||||||
|
? frameState.declutterTree.all().map((item) => item.value)
|
||||||
|
: null
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@@ -556,10 +582,19 @@ class CanvasVectorLayerRenderer extends CanvasLayerRenderer {
|
|||||||
getRenderTolerance(resolution, pixelRatio),
|
getRenderTolerance(resolution, pixelRatio),
|
||||||
extent,
|
extent,
|
||||||
resolution,
|
resolution,
|
||||||
pixelRatio,
|
pixelRatio
|
||||||
vectorLayer.getDeclutter()
|
|
||||||
);
|
);
|
||||||
|
|
||||||
|
let declutterBuilderGroup;
|
||||||
|
if (this.getLayer().getDeclutter()) {
|
||||||
|
declutterBuilderGroup = new CanvasBuilderGroup(
|
||||||
|
getRenderTolerance(resolution, pixelRatio),
|
||||||
|
extent,
|
||||||
|
resolution,
|
||||||
|
pixelRatio
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
const userProjection = getUserProjection();
|
const userProjection = getUserProjection();
|
||||||
let userTransform;
|
let userTransform;
|
||||||
if (userProjection) {
|
if (userProjection) {
|
||||||
@@ -597,7 +632,8 @@ class CanvasVectorLayerRenderer extends CanvasLayerRenderer {
|
|||||||
squaredTolerance,
|
squaredTolerance,
|
||||||
styles,
|
styles,
|
||||||
replayGroup,
|
replayGroup,
|
||||||
userTransform
|
userTransform,
|
||||||
|
declutterBuilderGroup
|
||||||
);
|
);
|
||||||
this.dirty_ = this.dirty_ || dirty;
|
this.dirty_ = this.dirty_ || dirty;
|
||||||
}
|
}
|
||||||
@@ -624,6 +660,17 @@ class CanvasVectorLayerRenderer extends CanvasLayerRenderer {
|
|||||||
vectorLayer.getRenderBuffer()
|
vectorLayer.getRenderBuffer()
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (declutterBuilderGroup) {
|
||||||
|
this.declutterExecutorGroup = new ExecutorGroup(
|
||||||
|
extent,
|
||||||
|
resolution,
|
||||||
|
pixelRatio,
|
||||||
|
vectorSource.getOverlaps(),
|
||||||
|
declutterBuilderGroup.finish(),
|
||||||
|
vectorLayer.getRenderBuffer()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
this.renderedResolution_ = resolution;
|
this.renderedResolution_ = resolution;
|
||||||
this.renderedRevision_ = vectorLayerRevision;
|
this.renderedRevision_ = vectorLayerRevision;
|
||||||
this.renderedRenderOrder_ = vectorLayerRenderOrder;
|
this.renderedRenderOrder_ = vectorLayerRenderOrder;
|
||||||
@@ -643,6 +690,7 @@ class CanvasVectorLayerRenderer extends CanvasLayerRenderer {
|
|||||||
* @param {import("../../style/Style.js").default|Array<import("../../style/Style.js").default>} styles The style or array of styles.
|
* @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} builderGroup Builder group.
|
* @param {import("../../render/canvas/BuilderGroup.js").default} builderGroup Builder group.
|
||||||
* @param {import("../../proj.js").TransformFunction=} opt_transform Transform from user to view projection.
|
* @param {import("../../proj.js").TransformFunction=} opt_transform Transform from user to view projection.
|
||||||
|
* @param {import("../../render/canvas/BuilderGroup.js").default=} opt_declutterBuilderGroup Builder for decluttering.
|
||||||
* @return {boolean} `true` if an image is loading.
|
* @return {boolean} `true` if an image is loading.
|
||||||
*/
|
*/
|
||||||
renderFeature(
|
renderFeature(
|
||||||
@@ -650,7 +698,8 @@ class CanvasVectorLayerRenderer extends CanvasLayerRenderer {
|
|||||||
squaredTolerance,
|
squaredTolerance,
|
||||||
styles,
|
styles,
|
||||||
builderGroup,
|
builderGroup,
|
||||||
opt_transform
|
opt_transform,
|
||||||
|
opt_declutterBuilderGroup
|
||||||
) {
|
) {
|
||||||
if (!styles) {
|
if (!styles) {
|
||||||
return false;
|
return false;
|
||||||
@@ -665,7 +714,8 @@ class CanvasVectorLayerRenderer extends CanvasLayerRenderer {
|
|||||||
styles[i],
|
styles[i],
|
||||||
squaredTolerance,
|
squaredTolerance,
|
||||||
this.boundHandleStyleImageChange_,
|
this.boundHandleStyleImageChange_,
|
||||||
opt_transform
|
opt_transform,
|
||||||
|
opt_declutterBuilderGroup
|
||||||
) || loading;
|
) || loading;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -675,7 +725,8 @@ class CanvasVectorLayerRenderer extends CanvasLayerRenderer {
|
|||||||
styles,
|
styles,
|
||||||
squaredTolerance,
|
squaredTolerance,
|
||||||
this.boundHandleStyleImageChange_,
|
this.boundHandleStyleImageChange_,
|
||||||
opt_transform
|
opt_transform,
|
||||||
|
opt_declutterBuilderGroup
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return loading;
|
return loading;
|
||||||
|
|||||||
@@ -2,9 +2,7 @@
|
|||||||
* @module ol/renderer/canvas/VectorTileLayer
|
* @module ol/renderer/canvas/VectorTileLayer
|
||||||
*/
|
*/
|
||||||
import CanvasBuilderGroup from '../../render/canvas/BuilderGroup.js';
|
import CanvasBuilderGroup from '../../render/canvas/BuilderGroup.js';
|
||||||
import CanvasExecutorGroup, {
|
import CanvasExecutorGroup from '../../render/canvas/ExecutorGroup.js';
|
||||||
replayDeclutter,
|
|
||||||
} from '../../render/canvas/ExecutorGroup.js';
|
|
||||||
import CanvasTileLayerRenderer from './TileLayer.js';
|
import CanvasTileLayerRenderer from './TileLayer.js';
|
||||||
import EventType from '../../events/EventType.js';
|
import EventType from '../../events/EventType.js';
|
||||||
import ReplayType from '../../render/canvas/BuilderType.js';
|
import ReplayType from '../../render/canvas/BuilderType.js';
|
||||||
@@ -262,6 +260,7 @@ class CanvasVectorTileLayerRenderer extends CanvasTileLayerRenderer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const source = layer.getSource();
|
const source = layer.getSource();
|
||||||
|
const declutter = layer.getDeclutter();
|
||||||
const sourceTileGrid = source.getTileGrid();
|
const sourceTileGrid = source.getTileGrid();
|
||||||
const tileGrid = source.getTileGridForProjection(projection);
|
const tileGrid = source.getTileGridForProjection(projection);
|
||||||
const tileExtent = tileGrid.getTileCoordExtent(tile.wrappedTileCoord);
|
const tileExtent = tileGrid.getTileCoordExtent(tile.wrappedTileCoord);
|
||||||
@@ -270,6 +269,9 @@ class CanvasVectorTileLayerRenderer extends CanvasTileLayerRenderer {
|
|||||||
const layerUid = getUid(layer);
|
const layerUid = getUid(layer);
|
||||||
delete tile.hitDetectionImageData[layerUid];
|
delete tile.hitDetectionImageData[layerUid];
|
||||||
tile.executorGroups[layerUid] = [];
|
tile.executorGroups[layerUid] = [];
|
||||||
|
if (declutter) {
|
||||||
|
tile.declutterExecutorGroups[layerUid] = [];
|
||||||
|
}
|
||||||
for (let t = 0, tt = sourceTiles.length; t < tt; ++t) {
|
for (let t = 0, tt = sourceTiles.length; t < tt; ++t) {
|
||||||
const sourceTile = sourceTiles[t];
|
const sourceTile = sourceTiles[t];
|
||||||
if (sourceTile.getState() != TileState.LOADED) {
|
if (sourceTile.getState() != TileState.LOADED) {
|
||||||
@@ -292,9 +294,11 @@ class CanvasVectorTileLayerRenderer extends CanvasTileLayerRenderer {
|
|||||||
0,
|
0,
|
||||||
sharedExtent,
|
sharedExtent,
|
||||||
resolution,
|
resolution,
|
||||||
pixelRatio,
|
pixelRatio
|
||||||
layer.getDeclutter()
|
|
||||||
);
|
);
|
||||||
|
const declutterBuilderGroup = declutter
|
||||||
|
? new CanvasBuilderGroup(0, sharedExtent, resolution, pixelRatio)
|
||||||
|
: undefined;
|
||||||
const squaredTolerance = getSquaredRenderTolerance(
|
const squaredTolerance = getSquaredRenderTolerance(
|
||||||
resolution,
|
resolution,
|
||||||
pixelRatio
|
pixelRatio
|
||||||
@@ -316,7 +320,8 @@ class CanvasVectorTileLayerRenderer extends CanvasTileLayerRenderer {
|
|||||||
feature,
|
feature,
|
||||||
squaredTolerance,
|
squaredTolerance,
|
||||||
styles,
|
styles,
|
||||||
builderGroup
|
builderGroup,
|
||||||
|
declutterBuilderGroup
|
||||||
);
|
);
|
||||||
this.dirty_ = this.dirty_ || dirty;
|
this.dirty_ = this.dirty_ || dirty;
|
||||||
builderState.dirty = builderState.dirty || dirty;
|
builderState.dirty = builderState.dirty || dirty;
|
||||||
@@ -340,7 +345,7 @@ class CanvasVectorTileLayerRenderer extends CanvasTileLayerRenderer {
|
|||||||
// no need to clip when the render tile is covered by a single source tile
|
// no need to clip when the render tile is covered by a single source tile
|
||||||
const replayExtent =
|
const replayExtent =
|
||||||
layer.getRenderMode() !== VectorTileRenderType.VECTOR &&
|
layer.getRenderMode() !== VectorTileRenderType.VECTOR &&
|
||||||
layer.getDeclutter() &&
|
declutter &&
|
||||||
sourceTiles.length === 1
|
sourceTiles.length === 1
|
||||||
? null
|
? null
|
||||||
: sharedExtent;
|
: sharedExtent;
|
||||||
@@ -353,6 +358,17 @@ class CanvasVectorTileLayerRenderer extends CanvasTileLayerRenderer {
|
|||||||
layer.getRenderBuffer()
|
layer.getRenderBuffer()
|
||||||
);
|
);
|
||||||
tile.executorGroups[layerUid].push(renderingReplayGroup);
|
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.renderedRevision = revision;
|
||||||
builderState.renderedZ = tile.sourceZ;
|
builderState.renderedZ = tile.sourceZ;
|
||||||
@@ -365,17 +381,10 @@ class CanvasVectorTileLayerRenderer extends CanvasTileLayerRenderer {
|
|||||||
* @param {import("../../PluggableMap.js").FrameState} frameState Frame state.
|
* @param {import("../../PluggableMap.js").FrameState} frameState Frame state.
|
||||||
* @param {number} hitTolerance Hit tolerance in pixels.
|
* @param {number} hitTolerance Hit tolerance in pixels.
|
||||||
* @param {function(import("../../Feature.js").FeatureLike, import("../../layer/Layer.js").default): T} callback Feature callback.
|
* @param {function(import("../../Feature.js").FeatureLike, import("../../layer/Layer.js").default): T} callback Feature callback.
|
||||||
* @param {Array<import("../../Feature.js").FeatureLike>} declutteredFeatures Decluttered features.
|
|
||||||
* @return {T|void} Callback result.
|
* @return {T|void} Callback result.
|
||||||
* @template T
|
* @template T
|
||||||
*/
|
*/
|
||||||
forEachFeatureAtCoordinate(
|
forEachFeatureAtCoordinate(coordinate, frameState, hitTolerance, callback) {
|
||||||
coordinate,
|
|
||||||
frameState,
|
|
||||||
hitTolerance,
|
|
||||||
callback,
|
|
||||||
declutteredFeatures
|
|
||||||
) {
|
|
||||||
const resolution = frameState.viewState.resolution;
|
const resolution = frameState.viewState.resolution;
|
||||||
const rotation = frameState.viewState.rotation;
|
const rotation = frameState.viewState.rotation;
|
||||||
hitTolerance = hitTolerance == undefined ? 0 : hitTolerance;
|
hitTolerance = hitTolerance == undefined ? 0 : hitTolerance;
|
||||||
@@ -405,39 +414,44 @@ class CanvasVectorTileLayerRenderer extends CanvasTileLayerRenderer {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const executorGroups = tile.executorGroups[getUid(layer)];
|
const layerUid = getUid(layer);
|
||||||
for (let t = 0, tt = executorGroups.length; t < tt; ++t) {
|
const executorGroups = [tile.executorGroups[layerUid]];
|
||||||
const executorGroup = executorGroups[t];
|
const declutterExecutorGroups = tile.declutterExecutorGroups[layerUid];
|
||||||
found =
|
if (declutterExecutorGroups) {
|
||||||
found ||
|
executorGroups.push(declutterExecutorGroups);
|
||||||
executorGroup.forEachFeatureAtCoordinate(
|
|
||||||
coordinate,
|
|
||||||
resolution,
|
|
||||||
rotation,
|
|
||||||
hitTolerance,
|
|
||||||
/**
|
|
||||||
* @param {import("../../Feature.js").FeatureLike} feature Feature.
|
|
||||||
* @return {?} Callback result.
|
|
||||||
*/
|
|
||||||
function (feature) {
|
|
||||||
if (
|
|
||||||
tileContainsCoordinate ||
|
|
||||||
(declutteredFeatures &&
|
|
||||||
declutteredFeatures.indexOf(feature) !== -1)
|
|
||||||
) {
|
|
||||||
let key = feature.getId();
|
|
||||||
if (key === undefined) {
|
|
||||||
key = getUid(feature);
|
|
||||||
}
|
|
||||||
if (!(key in features)) {
|
|
||||||
features[key] = true;
|
|
||||||
return callback(feature, layer);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
layer.getDeclutter() ? declutteredFeatures : null
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
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;
|
return found;
|
||||||
}
|
}
|
||||||
@@ -554,6 +568,70 @@ class CanvasVectorTileLayerRenderer extends CanvasTileLayerRenderer {
|
|||||||
this.renderIfReadyAndVisible();
|
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.
|
* Render the layer.
|
||||||
* @param {import("../../PluggableMap.js").FrameState} frameState Frame state.
|
* @param {import("../../PluggableMap.js").FrameState} frameState Frame state.
|
||||||
@@ -587,49 +665,18 @@ class CanvasVectorTileLayerRenderer extends CanvasTileLayerRenderer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const context = this.context;
|
const context = this.context;
|
||||||
const declutterReplays = layer.getDeclutter() ? {} : null;
|
|
||||||
const replayTypes = VECTOR_REPLAYS[renderMode];
|
const replayTypes = VECTOR_REPLAYS[renderMode];
|
||||||
const pixelRatio = frameState.pixelRatio;
|
|
||||||
const viewState = frameState.viewState;
|
const viewState = frameState.viewState;
|
||||||
const center = viewState.center;
|
|
||||||
const resolution = viewState.resolution;
|
|
||||||
const rotation = viewState.rotation;
|
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 tiles = this.renderedTiles;
|
||||||
const tileGrid = source.getTileGridForProjection(
|
|
||||||
frameState.viewState.projection
|
|
||||||
);
|
|
||||||
const clips = [];
|
const clips = [];
|
||||||
const clipZs = [];
|
const clipZs = [];
|
||||||
for (let i = tiles.length - 1; i >= 0; --i) {
|
for (let i = tiles.length - 1; i >= 0; --i) {
|
||||||
const tile = /** @type {import("../../VectorRenderTile.js").default} */ (tiles[
|
const tile = /** @type {import("../../VectorRenderTile.js").default} */ (tiles[
|
||||||
i
|
i
|
||||||
]);
|
]);
|
||||||
const tileCoord = tile.tileCoord;
|
const transform = this.getTileRenderTransform(tile, frameState);
|
||||||
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 executorGroups = tile.executorGroups[getUid(layer)];
|
const executorGroups = tile.executorGroups[getUid(layer)];
|
||||||
let clipped = false;
|
let clipped = false;
|
||||||
for (let t = 0, tt = executorGroups.length; t < tt; ++t) {
|
for (let t = 0, tt = executorGroups.length; t < tt; ++t) {
|
||||||
@@ -640,27 +687,29 @@ class CanvasVectorTileLayerRenderer extends CanvasTileLayerRenderer {
|
|||||||
}
|
}
|
||||||
const currentZ = tile.tileCoord[0];
|
const currentZ = tile.tileCoord[0];
|
||||||
let currentClip;
|
let currentClip;
|
||||||
if (!declutterReplays && !clipped) {
|
if (!clipped) {
|
||||||
currentClip = executorGroup.getClipCoords(transform);
|
currentClip = executorGroup.getClipCoords(transform);
|
||||||
context.save();
|
if (currentClip) {
|
||||||
|
context.save();
|
||||||
|
|
||||||
// Create a clip mask for regions in this low resolution tile that are
|
// Create a clip mask for regions in this low resolution tile that are
|
||||||
// already filled by a higher resolution tile
|
// already filled by a higher resolution tile
|
||||||
for (let j = 0, jj = clips.length; j < jj; ++j) {
|
for (let j = 0, jj = clips.length; j < jj; ++j) {
|
||||||
const clip = clips[j];
|
const clip = clips[j];
|
||||||
if (currentZ < clipZs[j]) {
|
if (currentZ < clipZs[j]) {
|
||||||
context.beginPath();
|
context.beginPath();
|
||||||
// counter-clockwise (outer ring) for current tile
|
// counter-clockwise (outer ring) for current tile
|
||||||
context.moveTo(currentClip[0], currentClip[1]);
|
context.moveTo(currentClip[0], currentClip[1]);
|
||||||
context.lineTo(currentClip[2], currentClip[3]);
|
context.lineTo(currentClip[2], currentClip[3]);
|
||||||
context.lineTo(currentClip[4], currentClip[5]);
|
context.lineTo(currentClip[4], currentClip[5]);
|
||||||
context.lineTo(currentClip[6], currentClip[7]);
|
context.lineTo(currentClip[6], currentClip[7]);
|
||||||
// clockwise (inner ring) for higher resolution tile
|
// clockwise (inner ring) for higher resolution tile
|
||||||
context.moveTo(clip[6], clip[7]);
|
context.moveTo(clip[6], clip[7]);
|
||||||
context.lineTo(clip[4], clip[5]);
|
context.lineTo(clip[4], clip[5]);
|
||||||
context.lineTo(clip[2], clip[3]);
|
context.lineTo(clip[2], clip[3]);
|
||||||
context.lineTo(clip[0], clip[1]);
|
context.lineTo(clip[0], clip[1]);
|
||||||
context.clip();
|
context.clip();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -670,10 +719,9 @@ class CanvasVectorTileLayerRenderer extends CanvasTileLayerRenderer {
|
|||||||
transform,
|
transform,
|
||||||
rotation,
|
rotation,
|
||||||
hifi,
|
hifi,
|
||||||
replayTypes,
|
replayTypes
|
||||||
declutterReplays
|
|
||||||
);
|
);
|
||||||
if (!declutterReplays && !clipped) {
|
if (!clipped && currentClip) {
|
||||||
context.restore();
|
context.restore();
|
||||||
clips.push(currentClip);
|
clips.push(currentClip);
|
||||||
clipZs.push(currentZ);
|
clipZs.push(currentZ);
|
||||||
@@ -681,17 +729,6 @@ class CanvasVectorTileLayerRenderer extends CanvasTileLayerRenderer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (declutterReplays) {
|
|
||||||
const layerState = frameState.layerStatesArray[frameState.layerIndex];
|
|
||||||
replayDeclutter(
|
|
||||||
declutterReplays,
|
|
||||||
context,
|
|
||||||
rotation,
|
|
||||||
layerState.opacity,
|
|
||||||
hifi,
|
|
||||||
frameState.declutterItems
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.container;
|
return this.container;
|
||||||
}
|
}
|
||||||
@@ -718,10 +755,17 @@ class CanvasVectorTileLayerRenderer extends CanvasTileLayerRenderer {
|
|||||||
* @param {import("../../Feature.js").FeatureLike} feature Feature.
|
* @param {import("../../Feature.js").FeatureLike} feature Feature.
|
||||||
* @param {number} squaredTolerance Squared tolerance.
|
* @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("../../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.
|
* @return {boolean} `true` if an image is loading.
|
||||||
*/
|
*/
|
||||||
renderFeature(feature, squaredTolerance, styles, executorGroup) {
|
renderFeature(
|
||||||
|
feature,
|
||||||
|
squaredTolerance,
|
||||||
|
styles,
|
||||||
|
builderGroup,
|
||||||
|
opt_declutterBuilderGroup
|
||||||
|
) {
|
||||||
if (!styles) {
|
if (!styles) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -730,20 +774,24 @@ class CanvasVectorTileLayerRenderer extends CanvasTileLayerRenderer {
|
|||||||
for (let i = 0, ii = styles.length; i < ii; ++i) {
|
for (let i = 0, ii = styles.length; i < ii; ++i) {
|
||||||
loading =
|
loading =
|
||||||
renderFeature(
|
renderFeature(
|
||||||
executorGroup,
|
builderGroup,
|
||||||
feature,
|
feature,
|
||||||
styles[i],
|
styles[i],
|
||||||
squaredTolerance,
|
squaredTolerance,
|
||||||
this.boundHandleStyleImageChange_
|
this.boundHandleStyleImageChange_,
|
||||||
|
undefined,
|
||||||
|
opt_declutterBuilderGroup
|
||||||
) || loading;
|
) || loading;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
loading = renderFeature(
|
loading = renderFeature(
|
||||||
executorGroup,
|
builderGroup,
|
||||||
feature,
|
feature,
|
||||||
styles,
|
styles,
|
||||||
squaredTolerance,
|
squaredTolerance,
|
||||||
this.boundHandleStyleImageChange_
|
this.boundHandleStyleImageChange_,
|
||||||
|
undefined,
|
||||||
|
opt_declutterBuilderGroup
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return loading;
|
return loading;
|
||||||
|
|||||||
+119
-38
@@ -62,8 +62,15 @@ export function getTolerance(resolution, pixelRatio) {
|
|||||||
* @param {import("../geom/Circle.js").default} geometry Geometry.
|
* @param {import("../geom/Circle.js").default} geometry Geometry.
|
||||||
* @param {import("../style/Style.js").default} style Style.
|
* @param {import("../style/Style.js").default} style Style.
|
||||||
* @param {import("../Feature.js").default} feature Feature.
|
* @param {import("../Feature.js").default} feature Feature.
|
||||||
|
* @param {import("../render/canvas/BuilderGroup.js").default=} opt_declutterBuilderGroup Builder for decluttering.
|
||||||
*/
|
*/
|
||||||
function renderCircleGeometry(builderGroup, geometry, style, feature) {
|
function renderCircleGeometry(
|
||||||
|
builderGroup,
|
||||||
|
geometry,
|
||||||
|
style,
|
||||||
|
feature,
|
||||||
|
opt_declutterBuilderGroup
|
||||||
|
) {
|
||||||
const fillStyle = style.getFill();
|
const fillStyle = style.getFill();
|
||||||
const strokeStyle = style.getStroke();
|
const strokeStyle = style.getStroke();
|
||||||
if (fillStyle || strokeStyle) {
|
if (fillStyle || strokeStyle) {
|
||||||
@@ -75,12 +82,12 @@ function renderCircleGeometry(builderGroup, geometry, style, feature) {
|
|||||||
circleReplay.drawCircle(geometry, feature);
|
circleReplay.drawCircle(geometry, feature);
|
||||||
}
|
}
|
||||||
const textStyle = style.getText();
|
const textStyle = style.getText();
|
||||||
if (textStyle) {
|
if (textStyle && textStyle.getText()) {
|
||||||
const textReplay = builderGroup.getBuilder(
|
const textReplay = (opt_declutterBuilderGroup || builderGroup).getBuilder(
|
||||||
style.getZIndex(),
|
style.getZIndex(),
|
||||||
BuilderType.TEXT
|
BuilderType.TEXT
|
||||||
);
|
);
|
||||||
textReplay.setTextStyle(textStyle, builderGroup.addDeclutter(false));
|
textReplay.setTextStyle(textStyle);
|
||||||
textReplay.drawText(geometry, feature);
|
textReplay.drawText(geometry, feature);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -92,8 +99,8 @@ function renderCircleGeometry(builderGroup, geometry, style, feature) {
|
|||||||
* @param {number} squaredTolerance Squared tolerance.
|
* @param {number} squaredTolerance Squared tolerance.
|
||||||
* @param {function(import("../events/Event.js").default): void} listener Listener function.
|
* @param {function(import("../events/Event.js").default): void} listener Listener function.
|
||||||
* @param {import("../proj.js").TransformFunction} [opt_transform] Transform from user to view projection.
|
* @param {import("../proj.js").TransformFunction} [opt_transform] Transform from user to view projection.
|
||||||
|
* @param {import("../render/canvas/BuilderGroup.js").default=} opt_declutterBuilderGroup Builder for decluttering.
|
||||||
* @return {boolean} `true` if style is loading.
|
* @return {boolean} `true` if style is loading.
|
||||||
* @template T
|
|
||||||
*/
|
*/
|
||||||
export function renderFeature(
|
export function renderFeature(
|
||||||
replayGroup,
|
replayGroup,
|
||||||
@@ -101,7 +108,8 @@ export function renderFeature(
|
|||||||
style,
|
style,
|
||||||
squaredTolerance,
|
squaredTolerance,
|
||||||
listener,
|
listener,
|
||||||
opt_transform
|
opt_transform,
|
||||||
|
opt_declutterBuilderGroup
|
||||||
) {
|
) {
|
||||||
let loading = false;
|
let loading = false;
|
||||||
const imageStyle = style.getImage();
|
const imageStyle = style.getImage();
|
||||||
@@ -123,7 +131,8 @@ export function renderFeature(
|
|||||||
feature,
|
feature,
|
||||||
style,
|
style,
|
||||||
squaredTolerance,
|
squaredTolerance,
|
||||||
opt_transform
|
opt_transform,
|
||||||
|
opt_declutterBuilderGroup
|
||||||
);
|
);
|
||||||
|
|
||||||
return loading;
|
return loading;
|
||||||
@@ -135,13 +144,15 @@ export function renderFeature(
|
|||||||
* @param {import("../style/Style.js").default} style Style.
|
* @param {import("../style/Style.js").default} style Style.
|
||||||
* @param {number} squaredTolerance Squared tolerance.
|
* @param {number} squaredTolerance Squared tolerance.
|
||||||
* @param {import("../proj.js").TransformFunction} [opt_transform] Optional transform function.
|
* @param {import("../proj.js").TransformFunction} [opt_transform] Optional transform function.
|
||||||
|
* @param {import("../render/canvas/BuilderGroup.js").default=} opt_declutterBuilderGroup Builder for decluttering.
|
||||||
*/
|
*/
|
||||||
function renderFeatureInternal(
|
function renderFeatureInternal(
|
||||||
replayGroup,
|
replayGroup,
|
||||||
feature,
|
feature,
|
||||||
style,
|
style,
|
||||||
squaredTolerance,
|
squaredTolerance,
|
||||||
opt_transform
|
opt_transform,
|
||||||
|
opt_declutterBuilderGroup
|
||||||
) {
|
) {
|
||||||
const geometry = style.getGeometryFunction()(feature);
|
const geometry = style.getGeometryFunction()(feature);
|
||||||
if (!geometry) {
|
if (!geometry) {
|
||||||
@@ -156,7 +167,13 @@ function renderFeatureInternal(
|
|||||||
renderGeometry(replayGroup, simplifiedGeometry, style, feature);
|
renderGeometry(replayGroup, simplifiedGeometry, style, feature);
|
||||||
} else {
|
} else {
|
||||||
const geometryRenderer = GEOMETRY_RENDERERS[simplifiedGeometry.getType()];
|
const geometryRenderer = GEOMETRY_RENDERERS[simplifiedGeometry.getType()];
|
||||||
geometryRenderer(replayGroup, simplifiedGeometry, style, feature);
|
geometryRenderer(
|
||||||
|
replayGroup,
|
||||||
|
simplifiedGeometry,
|
||||||
|
style,
|
||||||
|
feature,
|
||||||
|
opt_declutterBuilderGroup
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -187,18 +204,26 @@ function renderGeometry(replayGroup, geometry, style, feature) {
|
|||||||
* @param {import("../geom/GeometryCollection.js").default} geometry Geometry.
|
* @param {import("../geom/GeometryCollection.js").default} geometry Geometry.
|
||||||
* @param {import("../style/Style.js").default} style Style.
|
* @param {import("../style/Style.js").default} style Style.
|
||||||
* @param {import("../Feature.js").default} feature Feature.
|
* @param {import("../Feature.js").default} feature Feature.
|
||||||
|
* @param {import("../render/canvas/BuilderGroup.js").default=} opt_declutterBuilderGroup Builder for decluttering.
|
||||||
*/
|
*/
|
||||||
function renderGeometryCollectionGeometry(
|
function renderGeometryCollectionGeometry(
|
||||||
replayGroup,
|
replayGroup,
|
||||||
geometry,
|
geometry,
|
||||||
style,
|
style,
|
||||||
feature
|
feature,
|
||||||
|
opt_declutterBuilderGroup
|
||||||
) {
|
) {
|
||||||
const geometries = geometry.getGeometriesArray();
|
const geometries = geometry.getGeometriesArray();
|
||||||
let i, ii;
|
let i, ii;
|
||||||
for (i = 0, ii = geometries.length; i < ii; ++i) {
|
for (i = 0, ii = geometries.length; i < ii; ++i) {
|
||||||
const geometryRenderer = GEOMETRY_RENDERERS[geometries[i].getType()];
|
const geometryRenderer = GEOMETRY_RENDERERS[geometries[i].getType()];
|
||||||
geometryRenderer(replayGroup, geometries[i], style, feature);
|
geometryRenderer(
|
||||||
|
replayGroup,
|
||||||
|
geometries[i],
|
||||||
|
style,
|
||||||
|
feature,
|
||||||
|
opt_declutterBuilderGroup
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -207,8 +232,15 @@ function renderGeometryCollectionGeometry(
|
|||||||
* @param {import("../geom/LineString.js").default|import("../render/Feature.js").default} geometry Geometry.
|
* @param {import("../geom/LineString.js").default|import("../render/Feature.js").default} geometry Geometry.
|
||||||
* @param {import("../style/Style.js").default} style Style.
|
* @param {import("../style/Style.js").default} style Style.
|
||||||
* @param {import("../Feature.js").FeatureLike} feature Feature.
|
* @param {import("../Feature.js").FeatureLike} feature Feature.
|
||||||
|
* @param {import("../render/canvas/BuilderGroup.js").default=} opt_declutterBuilderGroup Builder for decluttering.
|
||||||
*/
|
*/
|
||||||
function renderLineStringGeometry(builderGroup, geometry, style, feature) {
|
function renderLineStringGeometry(
|
||||||
|
builderGroup,
|
||||||
|
geometry,
|
||||||
|
style,
|
||||||
|
feature,
|
||||||
|
opt_declutterBuilderGroup
|
||||||
|
) {
|
||||||
const strokeStyle = style.getStroke();
|
const strokeStyle = style.getStroke();
|
||||||
if (strokeStyle) {
|
if (strokeStyle) {
|
||||||
const lineStringReplay = builderGroup.getBuilder(
|
const lineStringReplay = builderGroup.getBuilder(
|
||||||
@@ -219,12 +251,12 @@ function renderLineStringGeometry(builderGroup, geometry, style, feature) {
|
|||||||
lineStringReplay.drawLineString(geometry, feature);
|
lineStringReplay.drawLineString(geometry, feature);
|
||||||
}
|
}
|
||||||
const textStyle = style.getText();
|
const textStyle = style.getText();
|
||||||
if (textStyle) {
|
if (textStyle && textStyle.getText()) {
|
||||||
const textReplay = builderGroup.getBuilder(
|
const textReplay = (opt_declutterBuilderGroup || builderGroup).getBuilder(
|
||||||
style.getZIndex(),
|
style.getZIndex(),
|
||||||
BuilderType.TEXT
|
BuilderType.TEXT
|
||||||
);
|
);
|
||||||
textReplay.setTextStyle(textStyle, builderGroup.addDeclutter(false));
|
textReplay.setTextStyle(textStyle);
|
||||||
textReplay.drawText(geometry, feature);
|
textReplay.drawText(geometry, feature);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -234,8 +266,15 @@ function renderLineStringGeometry(builderGroup, geometry, style, feature) {
|
|||||||
* @param {import("../geom/MultiLineString.js").default|import("../render/Feature.js").default} geometry Geometry.
|
* @param {import("../geom/MultiLineString.js").default|import("../render/Feature.js").default} geometry Geometry.
|
||||||
* @param {import("../style/Style.js").default} style Style.
|
* @param {import("../style/Style.js").default} style Style.
|
||||||
* @param {import("../Feature.js").FeatureLike} feature Feature.
|
* @param {import("../Feature.js").FeatureLike} feature Feature.
|
||||||
|
* @param {import("../render/canvas/BuilderGroup.js").default=} opt_declutterBuilderGroup Builder for decluttering.
|
||||||
*/
|
*/
|
||||||
function renderMultiLineStringGeometry(builderGroup, geometry, style, feature) {
|
function renderMultiLineStringGeometry(
|
||||||
|
builderGroup,
|
||||||
|
geometry,
|
||||||
|
style,
|
||||||
|
feature,
|
||||||
|
opt_declutterBuilderGroup
|
||||||
|
) {
|
||||||
const strokeStyle = style.getStroke();
|
const strokeStyle = style.getStroke();
|
||||||
if (strokeStyle) {
|
if (strokeStyle) {
|
||||||
const lineStringReplay = builderGroup.getBuilder(
|
const lineStringReplay = builderGroup.getBuilder(
|
||||||
@@ -246,12 +285,12 @@ function renderMultiLineStringGeometry(builderGroup, geometry, style, feature) {
|
|||||||
lineStringReplay.drawMultiLineString(geometry, feature);
|
lineStringReplay.drawMultiLineString(geometry, feature);
|
||||||
}
|
}
|
||||||
const textStyle = style.getText();
|
const textStyle = style.getText();
|
||||||
if (textStyle) {
|
if (textStyle && textStyle.getText()) {
|
||||||
const textReplay = builderGroup.getBuilder(
|
const textReplay = (opt_declutterBuilderGroup || builderGroup).getBuilder(
|
||||||
style.getZIndex(),
|
style.getZIndex(),
|
||||||
BuilderType.TEXT
|
BuilderType.TEXT
|
||||||
);
|
);
|
||||||
textReplay.setTextStyle(textStyle, builderGroup.addDeclutter(false));
|
textReplay.setTextStyle(textStyle);
|
||||||
textReplay.drawText(geometry, feature);
|
textReplay.drawText(geometry, feature);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -261,8 +300,15 @@ function renderMultiLineStringGeometry(builderGroup, geometry, style, feature) {
|
|||||||
* @param {import("../geom/MultiPolygon.js").default} geometry Geometry.
|
* @param {import("../geom/MultiPolygon.js").default} geometry Geometry.
|
||||||
* @param {import("../style/Style.js").default} style Style.
|
* @param {import("../style/Style.js").default} style Style.
|
||||||
* @param {import("../Feature.js").default} feature Feature.
|
* @param {import("../Feature.js").default} feature Feature.
|
||||||
|
* @param {import("../render/canvas/BuilderGroup.js").default=} opt_declutterBuilderGroup Builder for decluttering.
|
||||||
*/
|
*/
|
||||||
function renderMultiPolygonGeometry(builderGroup, geometry, style, feature) {
|
function renderMultiPolygonGeometry(
|
||||||
|
builderGroup,
|
||||||
|
geometry,
|
||||||
|
style,
|
||||||
|
feature,
|
||||||
|
opt_declutterBuilderGroup
|
||||||
|
) {
|
||||||
const fillStyle = style.getFill();
|
const fillStyle = style.getFill();
|
||||||
const strokeStyle = style.getStroke();
|
const strokeStyle = style.getStroke();
|
||||||
if (strokeStyle || fillStyle) {
|
if (strokeStyle || fillStyle) {
|
||||||
@@ -274,12 +320,12 @@ function renderMultiPolygonGeometry(builderGroup, geometry, style, feature) {
|
|||||||
polygonReplay.drawMultiPolygon(geometry, feature);
|
polygonReplay.drawMultiPolygon(geometry, feature);
|
||||||
}
|
}
|
||||||
const textStyle = style.getText();
|
const textStyle = style.getText();
|
||||||
if (textStyle) {
|
if (textStyle && textStyle.getText()) {
|
||||||
const textReplay = builderGroup.getBuilder(
|
const textReplay = (opt_declutterBuilderGroup || builderGroup).getBuilder(
|
||||||
style.getZIndex(),
|
style.getZIndex(),
|
||||||
BuilderType.TEXT
|
BuilderType.TEXT
|
||||||
);
|
);
|
||||||
textReplay.setTextStyle(textStyle, builderGroup.addDeclutter(false));
|
textReplay.setTextStyle(textStyle);
|
||||||
textReplay.drawText(geometry, feature);
|
textReplay.drawText(geometry, feature);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -289,9 +335,24 @@ function renderMultiPolygonGeometry(builderGroup, geometry, style, feature) {
|
|||||||
* @param {import("../geom/Point.js").default|import("../render/Feature.js").default} geometry Geometry.
|
* @param {import("../geom/Point.js").default|import("../render/Feature.js").default} geometry Geometry.
|
||||||
* @param {import("../style/Style.js").default} style Style.
|
* @param {import("../style/Style.js").default} style Style.
|
||||||
* @param {import("../Feature.js").FeatureLike} feature Feature.
|
* @param {import("../Feature.js").FeatureLike} feature Feature.
|
||||||
|
* @param {import("../render/canvas/BuilderGroup.js").default=} opt_declutterBuilderGroup Builder for decluttering.
|
||||||
*/
|
*/
|
||||||
function renderPointGeometry(builderGroup, geometry, style, feature) {
|
function renderPointGeometry(
|
||||||
|
builderGroup,
|
||||||
|
geometry,
|
||||||
|
style,
|
||||||
|
feature,
|
||||||
|
opt_declutterBuilderGroup
|
||||||
|
) {
|
||||||
const imageStyle = style.getImage();
|
const imageStyle = style.getImage();
|
||||||
|
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) {
|
||||||
if (imageStyle.getImageState() != ImageState.LOADED) {
|
if (imageStyle.getImageState() != ImageState.LOADED) {
|
||||||
return;
|
return;
|
||||||
@@ -300,16 +361,15 @@ function renderPointGeometry(builderGroup, geometry, style, feature) {
|
|||||||
style.getZIndex(),
|
style.getZIndex(),
|
||||||
BuilderType.IMAGE
|
BuilderType.IMAGE
|
||||||
);
|
);
|
||||||
imageReplay.setImageStyle(imageStyle, builderGroup.addDeclutter(false));
|
imageReplay.setImageStyle(imageStyle, declutterImageWithText);
|
||||||
imageReplay.drawPoint(geometry, feature);
|
imageReplay.drawPoint(geometry, feature);
|
||||||
}
|
}
|
||||||
const textStyle = style.getText();
|
if (textStyle && textStyle.getText()) {
|
||||||
if (textStyle) {
|
|
||||||
const textReplay = builderGroup.getBuilder(
|
const textReplay = builderGroup.getBuilder(
|
||||||
style.getZIndex(),
|
style.getZIndex(),
|
||||||
BuilderType.TEXT
|
BuilderType.TEXT
|
||||||
);
|
);
|
||||||
textReplay.setTextStyle(textStyle, builderGroup.addDeclutter(!!imageStyle));
|
textReplay.setTextStyle(textStyle, declutterImageWithText);
|
||||||
textReplay.drawText(geometry, feature);
|
textReplay.drawText(geometry, feature);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -319,9 +379,24 @@ function renderPointGeometry(builderGroup, geometry, style, feature) {
|
|||||||
* @param {import("../geom/MultiPoint.js").default|import("../render/Feature.js").default} geometry Geometry.
|
* @param {import("../geom/MultiPoint.js").default|import("../render/Feature.js").default} geometry Geometry.
|
||||||
* @param {import("../style/Style.js").default} style Style.
|
* @param {import("../style/Style.js").default} style Style.
|
||||||
* @param {import("../Feature.js").FeatureLike} feature Feature.
|
* @param {import("../Feature.js").FeatureLike} feature Feature.
|
||||||
|
* @param {import("../render/canvas/BuilderGroup.js").default=} opt_declutterBuilderGroup Builder for decluttering.
|
||||||
*/
|
*/
|
||||||
function renderMultiPointGeometry(builderGroup, geometry, style, feature) {
|
function renderMultiPointGeometry(
|
||||||
|
builderGroup,
|
||||||
|
geometry,
|
||||||
|
style,
|
||||||
|
feature,
|
||||||
|
opt_declutterBuilderGroup
|
||||||
|
) {
|
||||||
const imageStyle = style.getImage();
|
const imageStyle = style.getImage();
|
||||||
|
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) {
|
||||||
if (imageStyle.getImageState() != ImageState.LOADED) {
|
if (imageStyle.getImageState() != ImageState.LOADED) {
|
||||||
return;
|
return;
|
||||||
@@ -330,16 +405,15 @@ function renderMultiPointGeometry(builderGroup, geometry, style, feature) {
|
|||||||
style.getZIndex(),
|
style.getZIndex(),
|
||||||
BuilderType.IMAGE
|
BuilderType.IMAGE
|
||||||
);
|
);
|
||||||
imageReplay.setImageStyle(imageStyle, builderGroup.addDeclutter(false));
|
imageReplay.setImageStyle(imageStyle, declutterImageWithText);
|
||||||
imageReplay.drawMultiPoint(geometry, feature);
|
imageReplay.drawMultiPoint(geometry, feature);
|
||||||
}
|
}
|
||||||
const textStyle = style.getText();
|
if (textStyle && textStyle.getText()) {
|
||||||
if (textStyle) {
|
const textReplay = (opt_declutterBuilderGroup || builderGroup).getBuilder(
|
||||||
const textReplay = builderGroup.getBuilder(
|
|
||||||
style.getZIndex(),
|
style.getZIndex(),
|
||||||
BuilderType.TEXT
|
BuilderType.TEXT
|
||||||
);
|
);
|
||||||
textReplay.setTextStyle(textStyle, builderGroup.addDeclutter(!!imageStyle));
|
textReplay.setTextStyle(textStyle, declutterImageWithText);
|
||||||
textReplay.drawText(geometry, feature);
|
textReplay.drawText(geometry, feature);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -349,8 +423,15 @@ function renderMultiPointGeometry(builderGroup, geometry, style, feature) {
|
|||||||
* @param {import("../geom/Polygon.js").default|import("../render/Feature.js").default} geometry Geometry.
|
* @param {import("../geom/Polygon.js").default|import("../render/Feature.js").default} geometry Geometry.
|
||||||
* @param {import("../style/Style.js").default} style Style.
|
* @param {import("../style/Style.js").default} style Style.
|
||||||
* @param {import("../Feature.js").FeatureLike} feature Feature.
|
* @param {import("../Feature.js").FeatureLike} feature Feature.
|
||||||
|
* @param {import("../render/canvas/BuilderGroup.js").default=} opt_declutterBuilderGroup Builder for decluttering.
|
||||||
*/
|
*/
|
||||||
function renderPolygonGeometry(builderGroup, geometry, style, feature) {
|
function renderPolygonGeometry(
|
||||||
|
builderGroup,
|
||||||
|
geometry,
|
||||||
|
style,
|
||||||
|
feature,
|
||||||
|
opt_declutterBuilderGroup
|
||||||
|
) {
|
||||||
const fillStyle = style.getFill();
|
const fillStyle = style.getFill();
|
||||||
const strokeStyle = style.getStroke();
|
const strokeStyle = style.getStroke();
|
||||||
if (fillStyle || strokeStyle) {
|
if (fillStyle || strokeStyle) {
|
||||||
@@ -362,12 +443,12 @@ function renderPolygonGeometry(builderGroup, geometry, style, feature) {
|
|||||||
polygonReplay.drawPolygon(geometry, feature);
|
polygonReplay.drawPolygon(geometry, feature);
|
||||||
}
|
}
|
||||||
const textStyle = style.getText();
|
const textStyle = style.getText();
|
||||||
if (textStyle) {
|
if (textStyle && textStyle.getText()) {
|
||||||
const textReplay = builderGroup.getBuilder(
|
const textReplay = (opt_declutterBuilderGroup || builderGroup).getBuilder(
|
||||||
style.getZIndex(),
|
style.getZIndex(),
|
||||||
BuilderType.TEXT
|
BuilderType.TEXT
|
||||||
);
|
);
|
||||||
textReplay.setTextStyle(textStyle, builderGroup.addDeclutter(false));
|
textReplay.setTextStyle(textStyle);
|
||||||
textReplay.drawText(geometry, feature);
|
textReplay.drawText(geometry, feature);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -598,17 +598,10 @@ class WebGLPointsLayerRenderer extends WebGLLayerRenderer {
|
|||||||
* @param {import("../../PluggableMap.js").FrameState} frameState Frame state.
|
* @param {import("../../PluggableMap.js").FrameState} frameState Frame state.
|
||||||
* @param {number} hitTolerance Hit tolerance in pixels.
|
* @param {number} hitTolerance Hit tolerance in pixels.
|
||||||
* @param {function(import("../../Feature.js").FeatureLike, import("../../layer/Layer.js").default): T} callback Feature callback.
|
* @param {function(import("../../Feature.js").FeatureLike, import("../../layer/Layer.js").default): T} callback Feature callback.
|
||||||
* @param {Array<import("../../Feature.js").FeatureLike>} declutteredFeatures Decluttered features.
|
|
||||||
* @return {T|void} Callback result.
|
* @return {T|void} Callback result.
|
||||||
* @template T
|
* @template T
|
||||||
*/
|
*/
|
||||||
forEachFeatureAtCoordinate(
|
forEachFeatureAtCoordinate(coordinate, frameState, hitTolerance, callback) {
|
||||||
coordinate,
|
|
||||||
frameState,
|
|
||||||
hitTolerance,
|
|
||||||
callback,
|
|
||||||
declutteredFeatures
|
|
||||||
) {
|
|
||||||
assert(this.hitDetectionEnabled_, 66);
|
assert(this.hitDetectionEnabled_, 66);
|
||||||
if (!this.hitRenderInstructions_) {
|
if (!this.hitRenderInstructions_) {
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -549,6 +549,7 @@ class RasterSource extends ImageSource {
|
|||||||
this.frameState_ = {
|
this.frameState_ = {
|
||||||
animate: false,
|
animate: false,
|
||||||
coordinateToPixelTransform: createTransform(),
|
coordinateToPixelTransform: createTransform(),
|
||||||
|
declutterTree: null,
|
||||||
extent: null,
|
extent: null,
|
||||||
index: 0,
|
index: 0,
|
||||||
layerIndex: 0,
|
layerIndex: 0,
|
||||||
@@ -565,7 +566,6 @@ class RasterSource extends ImageSource {
|
|||||||
}),
|
}),
|
||||||
viewHints: [],
|
viewHints: [],
|
||||||
wantedTiles: {},
|
wantedTiles: {},
|
||||||
declutterItems: [],
|
|
||||||
};
|
};
|
||||||
|
|
||||||
this.setAttributions(function (frameState) {
|
this.setAttributions(function (frameState) {
|
||||||
|
|||||||
Reference in New Issue
Block a user