map-2d-base 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -0
- package/dist/assets/map-2d-base.css +1 -0
- package/dist/chunks/asserts-B5CSgOpZ.js +713 -0
- package/dist/chunks/index-CwJi8_r0.js +828 -0
- package/dist/chunks/index-qMK-h7Yi.js +3260 -0
- package/dist/chunks/index-snoDp5kN.js +1216 -0
- package/dist/chunks/math-CtjJ8U9C.js +105 -0
- package/dist/geom.esm.js +4 -0
- package/dist/index.esm.js +22796 -0
- package/dist/style.esm.js +9 -0
- package/dist/utils.esm.js +5 -0
- package/package.json +80 -0
|
@@ -0,0 +1,3260 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
3
|
+
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
4
|
+
var __async = (__this, __arguments, generator) => {
|
|
5
|
+
return new Promise((resolve, reject) => {
|
|
6
|
+
var fulfilled = (value) => {
|
|
7
|
+
try {
|
|
8
|
+
step(generator.next(value));
|
|
9
|
+
} catch (e) {
|
|
10
|
+
reject(e);
|
|
11
|
+
}
|
|
12
|
+
};
|
|
13
|
+
var rejected = (value) => {
|
|
14
|
+
try {
|
|
15
|
+
step(generator.throw(value));
|
|
16
|
+
} catch (e) {
|
|
17
|
+
reject(e);
|
|
18
|
+
}
|
|
19
|
+
};
|
|
20
|
+
var step = (x) => x.done ? resolve(x.value) : Promise.resolve(x.value).then(fulfilled, rejected);
|
|
21
|
+
step((generator = generator.apply(__this, __arguments)).next());
|
|
22
|
+
});
|
|
23
|
+
};
|
|
24
|
+
import { s as listenOnce, E as EventType, u as unlistenByKey, T as Target, B as BaseObject, c as clear, i as abstract, j as getUid, b as assert } from "./asserts-B5CSgOpZ.js";
|
|
25
|
+
import { c as clamp, b as toFixed } from "./math-CtjJ8U9C.js";
|
|
26
|
+
const ua = typeof navigator !== "undefined" && typeof navigator.userAgent !== "undefined" ? navigator.userAgent.toLowerCase() : "";
|
|
27
|
+
const SAFARI = ua.includes("safari") && !ua.includes("chrom");
|
|
28
|
+
SAFARI && (ua.includes("version/15.4") || /cpu (os|iphone os) 15_4 like mac os x/.test(ua));
|
|
29
|
+
const WEBKIT = ua.includes("webkit") && !ua.includes("edge");
|
|
30
|
+
const MAC = ua.includes("macintosh");
|
|
31
|
+
const DEVICE_PIXEL_RATIO = typeof devicePixelRatio !== "undefined" ? devicePixelRatio : 1;
|
|
32
|
+
const WORKER_OFFSCREEN_CANVAS = typeof WorkerGlobalScope !== "undefined" && typeof OffscreenCanvas !== "undefined" && self instanceof WorkerGlobalScope;
|
|
33
|
+
const IMAGE_DECODE = typeof Image !== "undefined" && Image.prototype.decode;
|
|
34
|
+
const PASSIVE_EVENT_LISTENERS = (function() {
|
|
35
|
+
let passive = false;
|
|
36
|
+
try {
|
|
37
|
+
const options = Object.defineProperty({}, "passive", {
|
|
38
|
+
get: function() {
|
|
39
|
+
passive = true;
|
|
40
|
+
}
|
|
41
|
+
});
|
|
42
|
+
window.addEventListener("_", null, options);
|
|
43
|
+
window.removeEventListener("_", null, options);
|
|
44
|
+
} catch (e) {
|
|
45
|
+
}
|
|
46
|
+
return passive;
|
|
47
|
+
})();
|
|
48
|
+
const CLASS_HIDDEN = "ol-hidden";
|
|
49
|
+
const CLASS_SELECTABLE = "ol-selectable";
|
|
50
|
+
const CLASS_UNSELECTABLE = "ol-unselectable";
|
|
51
|
+
const CLASS_CONTROL = "ol-control";
|
|
52
|
+
const CLASS_COLLAPSED = "ol-collapsed";
|
|
53
|
+
const fontRegEx = new RegExp(
|
|
54
|
+
[
|
|
55
|
+
"^\\s*(?=(?:(?:[-a-z]+\\s*){0,2}(italic|oblique))?)",
|
|
56
|
+
"(?=(?:(?:[-a-z]+\\s*){0,2}(small-caps))?)",
|
|
57
|
+
"(?=(?:(?:[-a-z]+\\s*){0,2}(bold(?:er)?|lighter|[1-9]00 ))?)",
|
|
58
|
+
"(?:(?:normal|\\1|\\2|\\3)\\s*){0,3}((?:xx?-)?",
|
|
59
|
+
"(?:small|large)|medium|smaller|larger|[\\.\\d]+(?:\\%|in|[cem]m|ex|p[ctx]))",
|
|
60
|
+
"(?:\\s*\\/\\s*(normal|[\\.\\d]+(?:\\%|in|[cem]m|ex|p[ctx])?))",
|
|
61
|
+
`?\\s*([-,\\"\\'\\sa-z0-9]+?)\\s*$`
|
|
62
|
+
].join(""),
|
|
63
|
+
"i"
|
|
64
|
+
);
|
|
65
|
+
const fontRegExMatchIndex = [
|
|
66
|
+
"style",
|
|
67
|
+
"variant",
|
|
68
|
+
"weight",
|
|
69
|
+
"size",
|
|
70
|
+
"lineHeight",
|
|
71
|
+
"family"
|
|
72
|
+
];
|
|
73
|
+
const fontWeights = {
|
|
74
|
+
normal: 400,
|
|
75
|
+
bold: 700
|
|
76
|
+
};
|
|
77
|
+
const getFontParameters = function(fontSpec) {
|
|
78
|
+
const match = fontSpec.match(fontRegEx);
|
|
79
|
+
if (!match) {
|
|
80
|
+
return null;
|
|
81
|
+
}
|
|
82
|
+
const style = (
|
|
83
|
+
/** @type {FontParameters} */
|
|
84
|
+
{
|
|
85
|
+
lineHeight: "normal",
|
|
86
|
+
size: "1.2em",
|
|
87
|
+
style: "normal",
|
|
88
|
+
weight: "400",
|
|
89
|
+
variant: "normal"
|
|
90
|
+
}
|
|
91
|
+
);
|
|
92
|
+
for (let i = 0, ii = fontRegExMatchIndex.length; i < ii; ++i) {
|
|
93
|
+
const value = match[i + 1];
|
|
94
|
+
if (value !== void 0) {
|
|
95
|
+
style[fontRegExMatchIndex[i]] = typeof value === "string" ? value.trim() : value;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
if (isNaN(Number(style.weight)) && style.weight in fontWeights) {
|
|
99
|
+
style.weight = fontWeights[style.weight];
|
|
100
|
+
}
|
|
101
|
+
style.families = style.family.split(/,\s?/).map((f) => f.trim().replace(/^['"]|['"]$/g, ""));
|
|
102
|
+
return style;
|
|
103
|
+
};
|
|
104
|
+
function createCanvasContext2D(width, height, canvasPool, settings) {
|
|
105
|
+
let canvas;
|
|
106
|
+
if (canvasPool && canvasPool.length) {
|
|
107
|
+
canvas = /** @type {HTMLCanvasElement} */
|
|
108
|
+
canvasPool.shift();
|
|
109
|
+
} else if (WORKER_OFFSCREEN_CANVAS) {
|
|
110
|
+
canvas = new class extends OffscreenCanvas {
|
|
111
|
+
constructor() {
|
|
112
|
+
super(...arguments);
|
|
113
|
+
__publicField(this, "style", {});
|
|
114
|
+
}
|
|
115
|
+
}(width != null ? width : 300, height != null ? height : 150);
|
|
116
|
+
} else {
|
|
117
|
+
canvas = document.createElement("canvas");
|
|
118
|
+
}
|
|
119
|
+
if (width) {
|
|
120
|
+
canvas.width = width;
|
|
121
|
+
}
|
|
122
|
+
if (height) {
|
|
123
|
+
canvas.height = height;
|
|
124
|
+
}
|
|
125
|
+
return (
|
|
126
|
+
/** @type {CanvasRenderingContext2D|OffscreenCanvasRenderingContext2D} */
|
|
127
|
+
canvas.getContext("2d", settings)
|
|
128
|
+
);
|
|
129
|
+
}
|
|
130
|
+
let sharedCanvasContext;
|
|
131
|
+
function getSharedCanvasContext2D() {
|
|
132
|
+
if (!sharedCanvasContext) {
|
|
133
|
+
sharedCanvasContext = createCanvasContext2D(1, 1);
|
|
134
|
+
}
|
|
135
|
+
return sharedCanvasContext;
|
|
136
|
+
}
|
|
137
|
+
function releaseCanvas(context) {
|
|
138
|
+
const canvas = context.canvas;
|
|
139
|
+
canvas.width = 1;
|
|
140
|
+
canvas.height = 1;
|
|
141
|
+
context.clearRect(0, 0, 1, 1);
|
|
142
|
+
}
|
|
143
|
+
function outerWidth(element) {
|
|
144
|
+
let width = element.offsetWidth;
|
|
145
|
+
const style = getComputedStyle(element);
|
|
146
|
+
width += parseInt(style.marginLeft, 10) + parseInt(style.marginRight, 10);
|
|
147
|
+
return width;
|
|
148
|
+
}
|
|
149
|
+
function outerHeight(element) {
|
|
150
|
+
let height = element.offsetHeight;
|
|
151
|
+
const style = getComputedStyle(element);
|
|
152
|
+
height += parseInt(style.marginTop, 10) + parseInt(style.marginBottom, 10);
|
|
153
|
+
return height;
|
|
154
|
+
}
|
|
155
|
+
function replaceNode(newNode, oldNode) {
|
|
156
|
+
const parent = oldNode.parentNode;
|
|
157
|
+
if (parent) {
|
|
158
|
+
parent.replaceChild(newNode, oldNode);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
function removeChildren(node) {
|
|
162
|
+
while (node.lastChild) {
|
|
163
|
+
node.lastChild.remove();
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
function replaceChildren(node, children) {
|
|
167
|
+
const oldChildren = node.childNodes;
|
|
168
|
+
for (let i = 0; true; ++i) {
|
|
169
|
+
const oldChild = oldChildren[i];
|
|
170
|
+
const newChild = children[i];
|
|
171
|
+
if (!oldChild && !newChild) {
|
|
172
|
+
break;
|
|
173
|
+
}
|
|
174
|
+
if (oldChild === newChild) {
|
|
175
|
+
continue;
|
|
176
|
+
}
|
|
177
|
+
if (!oldChild) {
|
|
178
|
+
node.appendChild(newChild);
|
|
179
|
+
continue;
|
|
180
|
+
}
|
|
181
|
+
if (!newChild) {
|
|
182
|
+
node.removeChild(oldChild);
|
|
183
|
+
--i;
|
|
184
|
+
continue;
|
|
185
|
+
}
|
|
186
|
+
node.insertBefore(newChild, oldChild);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
function createMockDiv() {
|
|
190
|
+
const mockedDiv = new Proxy(
|
|
191
|
+
{
|
|
192
|
+
/**
|
|
193
|
+
* @type {Array<HTMLElement>}
|
|
194
|
+
*/
|
|
195
|
+
childNodes: [],
|
|
196
|
+
/**
|
|
197
|
+
* @param {HTMLElement} node html node.
|
|
198
|
+
* @return {HTMLElement} html node.
|
|
199
|
+
*/
|
|
200
|
+
appendChild: function(node) {
|
|
201
|
+
this.childNodes.push(node);
|
|
202
|
+
return node;
|
|
203
|
+
},
|
|
204
|
+
/**
|
|
205
|
+
* dummy function, as this structure is not supposed to have a parent.
|
|
206
|
+
*/
|
|
207
|
+
remove: function() {
|
|
208
|
+
},
|
|
209
|
+
/**
|
|
210
|
+
* @param {HTMLElement} node html node.
|
|
211
|
+
* @return {HTMLElement} html node.
|
|
212
|
+
*/
|
|
213
|
+
removeChild: function(node) {
|
|
214
|
+
const index = this.childNodes.indexOf(node);
|
|
215
|
+
if (index === -1) {
|
|
216
|
+
throw new Error("Node to remove was not found");
|
|
217
|
+
}
|
|
218
|
+
this.childNodes.splice(index, 1);
|
|
219
|
+
return node;
|
|
220
|
+
},
|
|
221
|
+
/**
|
|
222
|
+
* @param {HTMLElement} newNode new html node.
|
|
223
|
+
* @param {HTMLElement} referenceNode reference html node.
|
|
224
|
+
* @return {HTMLElement} new html node.
|
|
225
|
+
*/
|
|
226
|
+
insertBefore: function(newNode, referenceNode) {
|
|
227
|
+
const index = this.childNodes.indexOf(referenceNode);
|
|
228
|
+
if (index === -1) {
|
|
229
|
+
throw new Error("Reference node not found");
|
|
230
|
+
}
|
|
231
|
+
this.childNodes.splice(index, 0, newNode);
|
|
232
|
+
return newNode;
|
|
233
|
+
},
|
|
234
|
+
style: {}
|
|
235
|
+
},
|
|
236
|
+
{
|
|
237
|
+
get(target, prop, receiver) {
|
|
238
|
+
if (prop === "firstElementChild") {
|
|
239
|
+
return target.childNodes.length > 0 ? target.childNodes[0] : null;
|
|
240
|
+
}
|
|
241
|
+
return Reflect.get(target, prop, receiver);
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
);
|
|
245
|
+
return (
|
|
246
|
+
/** @type {HTMLDivElement} */
|
|
247
|
+
/** @type {*} */
|
|
248
|
+
mockedDiv
|
|
249
|
+
);
|
|
250
|
+
}
|
|
251
|
+
function isCanvas(obj) {
|
|
252
|
+
return typeof HTMLCanvasElement !== "undefined" && obj instanceof HTMLCanvasElement || typeof OffscreenCanvas !== "undefined" && obj instanceof OffscreenCanvas;
|
|
253
|
+
}
|
|
254
|
+
const NO_COLOR = [NaN, NaN, NaN, 0];
|
|
255
|
+
let colorParseContext;
|
|
256
|
+
function getColorParseContext() {
|
|
257
|
+
if (!colorParseContext) {
|
|
258
|
+
colorParseContext = createCanvasContext2D(1, 1, void 0, {
|
|
259
|
+
willReadFrequently: true,
|
|
260
|
+
desynchronized: true
|
|
261
|
+
});
|
|
262
|
+
}
|
|
263
|
+
return colorParseContext;
|
|
264
|
+
}
|
|
265
|
+
const rgbModernRegEx = /^rgba?\(\s*(\d+%?)\s+(\d+%?)\s+(\d+%?)(?:\s*\/\s*(\d+%|\d*\.\d+|[01]))?\s*\)$/i;
|
|
266
|
+
const rgbLegacyAbsoluteRegEx = /^rgba?\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)(?:\s*,\s*(\d+%|\d*\.\d+|[01]))?\s*\)$/i;
|
|
267
|
+
const rgbLegacyPercentageRegEx = /^rgba?\(\s*(\d+%)\s*,\s*(\d+%)\s*,\s*(\d+%)(?:\s*,\s*(\d+%|\d*\.\d+|[01]))?\s*\)$/i;
|
|
268
|
+
const hexRegEx = /^#([\da-f]{3,4}|[\da-f]{6}|[\da-f]{8})$/i;
|
|
269
|
+
function toColorComponent(s, divider) {
|
|
270
|
+
return s.endsWith("%") ? Number(s.substring(0, s.length - 1)) / divider : Number(s);
|
|
271
|
+
}
|
|
272
|
+
function throwInvalidColor(color) {
|
|
273
|
+
throw new Error('failed to parse "' + color + '" as color');
|
|
274
|
+
}
|
|
275
|
+
function parseRgba(color) {
|
|
276
|
+
if (color.toLowerCase().startsWith("rgb")) {
|
|
277
|
+
const rgb = color.match(rgbLegacyAbsoluteRegEx) || color.match(rgbModernRegEx) || color.match(rgbLegacyPercentageRegEx);
|
|
278
|
+
if (rgb) {
|
|
279
|
+
const alpha = rgb[4];
|
|
280
|
+
const rgbDivider = 100 / 255;
|
|
281
|
+
return [
|
|
282
|
+
clamp(toColorComponent(rgb[1], rgbDivider) + 0.5 | 0, 0, 255),
|
|
283
|
+
clamp(toColorComponent(rgb[2], rgbDivider) + 0.5 | 0, 0, 255),
|
|
284
|
+
clamp(toColorComponent(rgb[3], rgbDivider) + 0.5 | 0, 0, 255),
|
|
285
|
+
alpha !== void 0 ? clamp(toColorComponent(alpha, 100), 0, 1) : 1
|
|
286
|
+
];
|
|
287
|
+
}
|
|
288
|
+
throwInvalidColor(color);
|
|
289
|
+
}
|
|
290
|
+
if (color.startsWith("#")) {
|
|
291
|
+
if (hexRegEx.test(color)) {
|
|
292
|
+
const hex = color.substring(1);
|
|
293
|
+
const step = hex.length <= 4 ? 1 : 2;
|
|
294
|
+
const colorFromHex = [0, 0, 0, 255];
|
|
295
|
+
for (let i = 0, ii = hex.length; i < ii; i += step) {
|
|
296
|
+
let colorComponent = parseInt(hex.substring(i, i + step), 16);
|
|
297
|
+
if (step === 1) {
|
|
298
|
+
colorComponent += colorComponent << 4;
|
|
299
|
+
}
|
|
300
|
+
colorFromHex[i / step] = colorComponent;
|
|
301
|
+
}
|
|
302
|
+
colorFromHex[3] = colorFromHex[3] / 255;
|
|
303
|
+
return colorFromHex;
|
|
304
|
+
}
|
|
305
|
+
throwInvalidColor(color);
|
|
306
|
+
}
|
|
307
|
+
const context = getColorParseContext();
|
|
308
|
+
context.fillStyle = "#abcdef";
|
|
309
|
+
let invalidCheckFillStyle = context.fillStyle;
|
|
310
|
+
context.fillStyle = color;
|
|
311
|
+
if (context.fillStyle === invalidCheckFillStyle) {
|
|
312
|
+
context.fillStyle = "#fedcba";
|
|
313
|
+
invalidCheckFillStyle = context.fillStyle;
|
|
314
|
+
context.fillStyle = color;
|
|
315
|
+
if (context.fillStyle === invalidCheckFillStyle) {
|
|
316
|
+
throwInvalidColor(color);
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
const colorString = context.fillStyle;
|
|
320
|
+
if (colorString.startsWith("#") || colorString.startsWith("rgba")) {
|
|
321
|
+
return parseRgba(colorString);
|
|
322
|
+
}
|
|
323
|
+
context.clearRect(0, 0, 1, 1);
|
|
324
|
+
context.fillRect(0, 0, 1, 1);
|
|
325
|
+
const colorFromImage = Array.from(context.getImageData(0, 0, 1, 1).data);
|
|
326
|
+
colorFromImage[3] = toFixed(colorFromImage[3] / 255, 3);
|
|
327
|
+
return colorFromImage;
|
|
328
|
+
}
|
|
329
|
+
function asString(color) {
|
|
330
|
+
if (typeof color === "string") {
|
|
331
|
+
return color;
|
|
332
|
+
}
|
|
333
|
+
return toString(color);
|
|
334
|
+
}
|
|
335
|
+
const MAX_CACHE_SIZE = 1024;
|
|
336
|
+
const cache = {};
|
|
337
|
+
let cacheSize = 0;
|
|
338
|
+
function withAlpha(color) {
|
|
339
|
+
if (color.length === 4) {
|
|
340
|
+
return color;
|
|
341
|
+
}
|
|
342
|
+
const output = color.slice();
|
|
343
|
+
output[3] = 1;
|
|
344
|
+
return output;
|
|
345
|
+
}
|
|
346
|
+
function b1(v) {
|
|
347
|
+
return v > 31308e-7 ? Math.pow(v, 1 / 2.4) * 269.025 - 14.025 : v * 3294.6;
|
|
348
|
+
}
|
|
349
|
+
function b2(v) {
|
|
350
|
+
return v > 0.2068965 ? Math.pow(v, 3) : (v - 4 / 29) * (108 / 841);
|
|
351
|
+
}
|
|
352
|
+
function a1(v) {
|
|
353
|
+
return v > 10.314724 ? Math.pow((v + 14.025) / 269.025, 2.4) : v / 3294.6;
|
|
354
|
+
}
|
|
355
|
+
function a2(v) {
|
|
356
|
+
return v > 88564e-7 ? Math.pow(v, 1 / 3) : v / (108 / 841) + 4 / 29;
|
|
357
|
+
}
|
|
358
|
+
function rgbaToLcha(color) {
|
|
359
|
+
const r = a1(color[0]);
|
|
360
|
+
const g = a1(color[1]);
|
|
361
|
+
const b = a1(color[2]);
|
|
362
|
+
const y = a2(r * 0.222488403 + g * 0.716873169 + b * 0.06060791);
|
|
363
|
+
const l = 500 * (a2(r * 0.452247074 + g * 0.399439023 + b * 0.148375274) - y);
|
|
364
|
+
const q = 200 * (y - a2(r * 0.016863605 + g * 0.117638439 + b * 0.865350722));
|
|
365
|
+
const h = Math.atan2(q, l) * (180 / Math.PI);
|
|
366
|
+
return [
|
|
367
|
+
116 * y - 16,
|
|
368
|
+
Math.sqrt(l * l + q * q),
|
|
369
|
+
h < 0 ? h + 360 : h,
|
|
370
|
+
color[3]
|
|
371
|
+
];
|
|
372
|
+
}
|
|
373
|
+
function lchaToRgba(color) {
|
|
374
|
+
const l = (color[0] + 16) / 116;
|
|
375
|
+
const c = color[1];
|
|
376
|
+
const h = color[2] * Math.PI / 180;
|
|
377
|
+
const y = b2(l);
|
|
378
|
+
const x = b2(l + c / 500 * Math.cos(h));
|
|
379
|
+
const z = b2(l - c / 200 * Math.sin(h));
|
|
380
|
+
const r = b1(x * 3.021973625 - y * 1.617392459 - z * 0.404875592);
|
|
381
|
+
const g = b1(x * -0.943766287 + y * 1.916279586 + z * 0.027607165);
|
|
382
|
+
const b = b1(x * 0.069407491 - y * 0.22898585 + z * 1.159737864);
|
|
383
|
+
return [
|
|
384
|
+
clamp(r + 0.5 | 0, 0, 255),
|
|
385
|
+
clamp(g + 0.5 | 0, 0, 255),
|
|
386
|
+
clamp(b + 0.5 | 0, 0, 255),
|
|
387
|
+
color[3]
|
|
388
|
+
];
|
|
389
|
+
}
|
|
390
|
+
function fromString(s) {
|
|
391
|
+
if (s === "none") {
|
|
392
|
+
return NO_COLOR;
|
|
393
|
+
}
|
|
394
|
+
if (cache.hasOwnProperty(s)) {
|
|
395
|
+
return cache[s];
|
|
396
|
+
}
|
|
397
|
+
if (cacheSize >= MAX_CACHE_SIZE) {
|
|
398
|
+
let i = 0;
|
|
399
|
+
for (const key in cache) {
|
|
400
|
+
if ((i++ & 3) === 0) {
|
|
401
|
+
delete cache[key];
|
|
402
|
+
--cacheSize;
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
const color = parseRgba(s);
|
|
407
|
+
if (color.length !== 4) {
|
|
408
|
+
throwInvalidColor(s);
|
|
409
|
+
}
|
|
410
|
+
for (const c of color) {
|
|
411
|
+
if (isNaN(c)) {
|
|
412
|
+
throwInvalidColor(s);
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
cache[s] = color;
|
|
416
|
+
++cacheSize;
|
|
417
|
+
return color;
|
|
418
|
+
}
|
|
419
|
+
function asArray(color) {
|
|
420
|
+
if (Array.isArray(color)) {
|
|
421
|
+
return color;
|
|
422
|
+
}
|
|
423
|
+
return fromString(color);
|
|
424
|
+
}
|
|
425
|
+
function toString(color) {
|
|
426
|
+
let r = color[0];
|
|
427
|
+
if (r != (r | 0)) {
|
|
428
|
+
r = r + 0.5 | 0;
|
|
429
|
+
}
|
|
430
|
+
let g = color[1];
|
|
431
|
+
if (g != (g | 0)) {
|
|
432
|
+
g = g + 0.5 | 0;
|
|
433
|
+
}
|
|
434
|
+
let b = color[2];
|
|
435
|
+
if (b != (b | 0)) {
|
|
436
|
+
b = b + 0.5 | 0;
|
|
437
|
+
}
|
|
438
|
+
const a = color[3] === void 0 ? 1 : Math.round(color[3] * 1e3) / 1e3;
|
|
439
|
+
return "rgba(" + r + "," + g + "," + b + "," + a + ")";
|
|
440
|
+
}
|
|
441
|
+
function hasArea(size) {
|
|
442
|
+
return size[0] > 0 && size[1] > 0;
|
|
443
|
+
}
|
|
444
|
+
function scale(size, ratio, dest) {
|
|
445
|
+
if (dest === void 0) {
|
|
446
|
+
dest = [0, 0];
|
|
447
|
+
}
|
|
448
|
+
dest[0] = size[0] * ratio + 0.5 | 0;
|
|
449
|
+
dest[1] = size[1] * ratio + 0.5 | 0;
|
|
450
|
+
return dest;
|
|
451
|
+
}
|
|
452
|
+
function toSize(size, dest) {
|
|
453
|
+
if (Array.isArray(size)) {
|
|
454
|
+
return size;
|
|
455
|
+
}
|
|
456
|
+
if (dest === void 0) {
|
|
457
|
+
dest = [size, size];
|
|
458
|
+
} else {
|
|
459
|
+
dest[0] = size;
|
|
460
|
+
dest[1] = size;
|
|
461
|
+
}
|
|
462
|
+
return dest;
|
|
463
|
+
}
|
|
464
|
+
const ImageState = {
|
|
465
|
+
IDLE: 0,
|
|
466
|
+
LOADING: 1,
|
|
467
|
+
LOADED: 2,
|
|
468
|
+
ERROR: 3
|
|
469
|
+
};
|
|
470
|
+
function listenImage(image, loadHandler, errorHandler) {
|
|
471
|
+
const img = (
|
|
472
|
+
/** @type {HTMLImageElement} */
|
|
473
|
+
image
|
|
474
|
+
);
|
|
475
|
+
let listening = true;
|
|
476
|
+
let decoding = false;
|
|
477
|
+
let loaded = false;
|
|
478
|
+
const listenerKeys = [
|
|
479
|
+
listenOnce(img, EventType.LOAD, function() {
|
|
480
|
+
loaded = true;
|
|
481
|
+
if (!decoding) {
|
|
482
|
+
loadHandler();
|
|
483
|
+
}
|
|
484
|
+
})
|
|
485
|
+
];
|
|
486
|
+
if (img.src && IMAGE_DECODE) {
|
|
487
|
+
decoding = true;
|
|
488
|
+
img.decode().then(function() {
|
|
489
|
+
if (listening) {
|
|
490
|
+
loadHandler();
|
|
491
|
+
}
|
|
492
|
+
}).catch(function(error) {
|
|
493
|
+
if (listening) {
|
|
494
|
+
if (loaded) {
|
|
495
|
+
loadHandler();
|
|
496
|
+
} else {
|
|
497
|
+
errorHandler();
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
});
|
|
501
|
+
} else {
|
|
502
|
+
listenerKeys.push(listenOnce(img, EventType.ERROR, errorHandler));
|
|
503
|
+
}
|
|
504
|
+
return function unlisten() {
|
|
505
|
+
listening = false;
|
|
506
|
+
listenerKeys.forEach(unlistenByKey);
|
|
507
|
+
};
|
|
508
|
+
}
|
|
509
|
+
function load(image, src) {
|
|
510
|
+
return new Promise((resolve, reject) => {
|
|
511
|
+
function handleLoad() {
|
|
512
|
+
unlisten();
|
|
513
|
+
resolve(image);
|
|
514
|
+
}
|
|
515
|
+
function handleError() {
|
|
516
|
+
unlisten();
|
|
517
|
+
reject(new Error("Image load error"));
|
|
518
|
+
}
|
|
519
|
+
function unlisten() {
|
|
520
|
+
image.removeEventListener("load", handleLoad);
|
|
521
|
+
image.removeEventListener("error", handleError);
|
|
522
|
+
}
|
|
523
|
+
image.addEventListener("load", handleLoad);
|
|
524
|
+
image.addEventListener("error", handleError);
|
|
525
|
+
});
|
|
526
|
+
}
|
|
527
|
+
function decodeFallback(image, src) {
|
|
528
|
+
if (src) {
|
|
529
|
+
image.src = src;
|
|
530
|
+
}
|
|
531
|
+
return image.src && IMAGE_DECODE ? new Promise(
|
|
532
|
+
(resolve, reject) => image.decode().then(() => resolve(image)).catch(
|
|
533
|
+
(e) => image.complete && image.width ? resolve(image) : reject(e)
|
|
534
|
+
)
|
|
535
|
+
) : load(image);
|
|
536
|
+
}
|
|
537
|
+
class IconImageCache {
|
|
538
|
+
constructor() {
|
|
539
|
+
this.cache_ = {};
|
|
540
|
+
this.patternCache_ = {};
|
|
541
|
+
this.cacheSize_ = 0;
|
|
542
|
+
this.maxCacheSize_ = 1024;
|
|
543
|
+
}
|
|
544
|
+
/**
|
|
545
|
+
* FIXME empty description for jsdoc
|
|
546
|
+
*/
|
|
547
|
+
clear() {
|
|
548
|
+
this.cache_ = {};
|
|
549
|
+
this.patternCache_ = {};
|
|
550
|
+
this.cacheSize_ = 0;
|
|
551
|
+
}
|
|
552
|
+
/**
|
|
553
|
+
* @return {boolean} Can expire cache.
|
|
554
|
+
*/
|
|
555
|
+
canExpireCache() {
|
|
556
|
+
return this.cacheSize_ > this.maxCacheSize_;
|
|
557
|
+
}
|
|
558
|
+
/**
|
|
559
|
+
* FIXME empty description for jsdoc
|
|
560
|
+
*/
|
|
561
|
+
expire() {
|
|
562
|
+
if (this.canExpireCache()) {
|
|
563
|
+
let i = 0;
|
|
564
|
+
for (const key in this.cache_) {
|
|
565
|
+
const iconImage = this.cache_[key];
|
|
566
|
+
if ((i++ & 3) === 0 && !iconImage.hasListener()) {
|
|
567
|
+
delete this.cache_[key];
|
|
568
|
+
delete this.patternCache_[key];
|
|
569
|
+
--this.cacheSize_;
|
|
570
|
+
}
|
|
571
|
+
}
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
/**
|
|
575
|
+
* @param {string} src Src.
|
|
576
|
+
* @param {?string} crossOrigin Cross origin.
|
|
577
|
+
* @param {import("../color.js").Color|string|null} color Color.
|
|
578
|
+
* @return {import("./IconImage.js").default} Icon image.
|
|
579
|
+
*/
|
|
580
|
+
get(src, crossOrigin, color) {
|
|
581
|
+
const key = getCacheKey(src, crossOrigin, color);
|
|
582
|
+
return key in this.cache_ ? this.cache_[key] : null;
|
|
583
|
+
}
|
|
584
|
+
/**
|
|
585
|
+
* @param {string} src Src.
|
|
586
|
+
* @param {?string} crossOrigin Cross origin.
|
|
587
|
+
* @param {import("../color.js").Color|string|null} color Color.
|
|
588
|
+
* @return {CanvasPattern} Icon image.
|
|
589
|
+
*/
|
|
590
|
+
getPattern(src, crossOrigin, color) {
|
|
591
|
+
const key = getCacheKey(src, crossOrigin, color);
|
|
592
|
+
return key in this.patternCache_ ? this.patternCache_[key] : null;
|
|
593
|
+
}
|
|
594
|
+
/**
|
|
595
|
+
* @param {string} src Src.
|
|
596
|
+
* @param {?string} crossOrigin Cross origin.
|
|
597
|
+
* @param {import("../color.js").Color|string|null} color Color.
|
|
598
|
+
* @param {import("./IconImage.js").default|null} iconImage Icon image.
|
|
599
|
+
* @param {boolean} [pattern] Also cache a `'repeat'` pattern with this `iconImage`.
|
|
600
|
+
*/
|
|
601
|
+
set(src, crossOrigin, color, iconImage, pattern) {
|
|
602
|
+
const key = getCacheKey(src, crossOrigin, color);
|
|
603
|
+
const update = key in this.cache_;
|
|
604
|
+
this.cache_[key] = iconImage;
|
|
605
|
+
if (pattern) {
|
|
606
|
+
if (iconImage.getImageState() === ImageState.IDLE) {
|
|
607
|
+
iconImage.load();
|
|
608
|
+
}
|
|
609
|
+
if (iconImage.getImageState() === ImageState.LOADING) {
|
|
610
|
+
iconImage.ready().then(() => {
|
|
611
|
+
this.patternCache_[key] = getSharedCanvasContext2D().createPattern(
|
|
612
|
+
iconImage.getImage(1),
|
|
613
|
+
"repeat"
|
|
614
|
+
);
|
|
615
|
+
});
|
|
616
|
+
} else {
|
|
617
|
+
this.patternCache_[key] = getSharedCanvasContext2D().createPattern(
|
|
618
|
+
iconImage.getImage(1),
|
|
619
|
+
"repeat"
|
|
620
|
+
);
|
|
621
|
+
}
|
|
622
|
+
}
|
|
623
|
+
if (!update) {
|
|
624
|
+
++this.cacheSize_;
|
|
625
|
+
}
|
|
626
|
+
}
|
|
627
|
+
/**
|
|
628
|
+
* Set the cache size of the icon cache. Default is `1024`. Change this value when
|
|
629
|
+
* your map uses more than 1024 different icon images and you are not caching icon
|
|
630
|
+
* styles on the application level.
|
|
631
|
+
* @param {number} maxCacheSize Cache max size.
|
|
632
|
+
* @api
|
|
633
|
+
*/
|
|
634
|
+
setSize(maxCacheSize) {
|
|
635
|
+
this.maxCacheSize_ = maxCacheSize;
|
|
636
|
+
this.expire();
|
|
637
|
+
}
|
|
638
|
+
}
|
|
639
|
+
function getCacheKey(src, crossOrigin, color) {
|
|
640
|
+
const colorString = color ? asArray(color) : "null";
|
|
641
|
+
return crossOrigin + ":" + src + ":" + colorString;
|
|
642
|
+
}
|
|
643
|
+
const shared = new IconImageCache();
|
|
644
|
+
let taintedTestContext = null;
|
|
645
|
+
class IconImage extends Target {
|
|
646
|
+
/**
|
|
647
|
+
* @param {HTMLImageElement|HTMLCanvasElement|OffscreenCanvas|ImageBitmap|null} image Image.
|
|
648
|
+
* @param {string|undefined} src Src.
|
|
649
|
+
* @param {?string} crossOrigin Cross origin.
|
|
650
|
+
* @param {import("../ImageState.js").default|undefined} imageState Image state.
|
|
651
|
+
* @param {import("../color.js").Color|string|null} color Color.
|
|
652
|
+
*/
|
|
653
|
+
constructor(image, src, crossOrigin, imageState, color) {
|
|
654
|
+
super();
|
|
655
|
+
this.hitDetectionImage_ = null;
|
|
656
|
+
this.image_ = image;
|
|
657
|
+
this.crossOrigin_ = crossOrigin;
|
|
658
|
+
this.canvas_ = {};
|
|
659
|
+
this.color_ = color;
|
|
660
|
+
this.imageState_ = imageState === void 0 ? ImageState.IDLE : imageState;
|
|
661
|
+
this.size_ = image && image.width && image.height ? [image.width, image.height] : null;
|
|
662
|
+
this.src_ = src;
|
|
663
|
+
this.tainted_;
|
|
664
|
+
this.ready_ = null;
|
|
665
|
+
}
|
|
666
|
+
/**
|
|
667
|
+
* @private
|
|
668
|
+
*/
|
|
669
|
+
initializeImage_() {
|
|
670
|
+
this.image_ = new Image();
|
|
671
|
+
if (this.crossOrigin_ !== null) {
|
|
672
|
+
this.image_.crossOrigin = this.crossOrigin_;
|
|
673
|
+
}
|
|
674
|
+
}
|
|
675
|
+
/**
|
|
676
|
+
* @private
|
|
677
|
+
* @return {boolean} The image canvas is tainted.
|
|
678
|
+
*/
|
|
679
|
+
isTainted_() {
|
|
680
|
+
if (this.tainted_ === void 0 && this.imageState_ === ImageState.LOADED) {
|
|
681
|
+
if (!taintedTestContext) {
|
|
682
|
+
taintedTestContext = createCanvasContext2D(1, 1, void 0, {
|
|
683
|
+
willReadFrequently: true
|
|
684
|
+
});
|
|
685
|
+
}
|
|
686
|
+
taintedTestContext.drawImage(this.image_, 0, 0);
|
|
687
|
+
try {
|
|
688
|
+
taintedTestContext.getImageData(0, 0, 1, 1);
|
|
689
|
+
this.tainted_ = false;
|
|
690
|
+
} catch (e) {
|
|
691
|
+
taintedTestContext = null;
|
|
692
|
+
this.tainted_ = true;
|
|
693
|
+
}
|
|
694
|
+
}
|
|
695
|
+
return this.tainted_ === true;
|
|
696
|
+
}
|
|
697
|
+
/**
|
|
698
|
+
* @private
|
|
699
|
+
*/
|
|
700
|
+
dispatchChangeEvent_() {
|
|
701
|
+
this.dispatchEvent(EventType.CHANGE);
|
|
702
|
+
}
|
|
703
|
+
/**
|
|
704
|
+
* @private
|
|
705
|
+
*/
|
|
706
|
+
handleImageError_() {
|
|
707
|
+
this.imageState_ = ImageState.ERROR;
|
|
708
|
+
this.dispatchChangeEvent_();
|
|
709
|
+
}
|
|
710
|
+
/**
|
|
711
|
+
* @private
|
|
712
|
+
*/
|
|
713
|
+
handleImageLoad_() {
|
|
714
|
+
this.imageState_ = ImageState.LOADED;
|
|
715
|
+
this.size_ = [this.image_.width, this.image_.height];
|
|
716
|
+
this.dispatchChangeEvent_();
|
|
717
|
+
}
|
|
718
|
+
/**
|
|
719
|
+
* @param {number} pixelRatio Pixel ratio.
|
|
720
|
+
* @return {HTMLImageElement|HTMLCanvasElement|OffscreenCanvas|ImageBitmap} Image or Canvas element or image bitmap.
|
|
721
|
+
*/
|
|
722
|
+
getImage(pixelRatio) {
|
|
723
|
+
if (!this.image_) {
|
|
724
|
+
this.initializeImage_();
|
|
725
|
+
}
|
|
726
|
+
this.replaceColor_(pixelRatio);
|
|
727
|
+
return this.canvas_[pixelRatio] ? this.canvas_[pixelRatio] : this.image_;
|
|
728
|
+
}
|
|
729
|
+
/**
|
|
730
|
+
* @param {number} pixelRatio Pixel ratio.
|
|
731
|
+
* @return {number} Image or Canvas element.
|
|
732
|
+
*/
|
|
733
|
+
getPixelRatio(pixelRatio) {
|
|
734
|
+
this.replaceColor_(pixelRatio);
|
|
735
|
+
return this.canvas_[pixelRatio] ? pixelRatio : 1;
|
|
736
|
+
}
|
|
737
|
+
/**
|
|
738
|
+
* @return {import("../ImageState.js").default} Image state.
|
|
739
|
+
*/
|
|
740
|
+
getImageState() {
|
|
741
|
+
return this.imageState_;
|
|
742
|
+
}
|
|
743
|
+
/**
|
|
744
|
+
* @return {HTMLImageElement|HTMLCanvasElement|OffscreenCanvas|ImageBitmap} Image element.
|
|
745
|
+
*/
|
|
746
|
+
getHitDetectionImage() {
|
|
747
|
+
if (!this.image_) {
|
|
748
|
+
this.initializeImage_();
|
|
749
|
+
}
|
|
750
|
+
if (!this.hitDetectionImage_) {
|
|
751
|
+
if (this.isTainted_()) {
|
|
752
|
+
const width = this.size_[0];
|
|
753
|
+
const height = this.size_[1];
|
|
754
|
+
const context = createCanvasContext2D(width, height);
|
|
755
|
+
context.fillRect(0, 0, width, height);
|
|
756
|
+
this.hitDetectionImage_ = context.canvas;
|
|
757
|
+
} else {
|
|
758
|
+
this.hitDetectionImage_ = this.image_;
|
|
759
|
+
}
|
|
760
|
+
}
|
|
761
|
+
return this.hitDetectionImage_;
|
|
762
|
+
}
|
|
763
|
+
/**
|
|
764
|
+
* Get the size of the icon (in pixels).
|
|
765
|
+
* @return {import("../size.js").Size} Image size.
|
|
766
|
+
*/
|
|
767
|
+
getSize() {
|
|
768
|
+
return this.size_;
|
|
769
|
+
}
|
|
770
|
+
/**
|
|
771
|
+
* @return {string|undefined} Image src.
|
|
772
|
+
*/
|
|
773
|
+
getSrc() {
|
|
774
|
+
return this.src_;
|
|
775
|
+
}
|
|
776
|
+
/**
|
|
777
|
+
* Load not yet loaded URI.
|
|
778
|
+
*/
|
|
779
|
+
load() {
|
|
780
|
+
if (this.imageState_ !== ImageState.IDLE) {
|
|
781
|
+
return;
|
|
782
|
+
}
|
|
783
|
+
if (!this.image_) {
|
|
784
|
+
this.initializeImage_();
|
|
785
|
+
}
|
|
786
|
+
this.imageState_ = ImageState.LOADING;
|
|
787
|
+
try {
|
|
788
|
+
if (this.src_ !== void 0) {
|
|
789
|
+
this.image_.src = this.src_;
|
|
790
|
+
}
|
|
791
|
+
} catch (e) {
|
|
792
|
+
this.handleImageError_();
|
|
793
|
+
}
|
|
794
|
+
if (this.image_ instanceof HTMLImageElement) {
|
|
795
|
+
decodeFallback(this.image_, this.src_).then((image) => {
|
|
796
|
+
this.image_ = image;
|
|
797
|
+
this.handleImageLoad_();
|
|
798
|
+
}).catch(this.handleImageError_.bind(this));
|
|
799
|
+
}
|
|
800
|
+
}
|
|
801
|
+
/**
|
|
802
|
+
* @param {number} pixelRatio Pixel ratio.
|
|
803
|
+
* @private
|
|
804
|
+
*/
|
|
805
|
+
replaceColor_(pixelRatio) {
|
|
806
|
+
if (!this.color_ || this.canvas_[pixelRatio] || this.imageState_ !== ImageState.LOADED) {
|
|
807
|
+
return;
|
|
808
|
+
}
|
|
809
|
+
const image = this.image_;
|
|
810
|
+
const ctx = createCanvasContext2D(
|
|
811
|
+
Math.ceil(image.width * pixelRatio),
|
|
812
|
+
Math.ceil(image.height * pixelRatio)
|
|
813
|
+
);
|
|
814
|
+
const canvas = ctx.canvas;
|
|
815
|
+
ctx.scale(pixelRatio, pixelRatio);
|
|
816
|
+
ctx.drawImage(image, 0, 0);
|
|
817
|
+
ctx.globalCompositeOperation = "multiply";
|
|
818
|
+
ctx.fillStyle = asString(this.color_);
|
|
819
|
+
ctx.fillRect(0, 0, canvas.width / pixelRatio, canvas.height / pixelRatio);
|
|
820
|
+
ctx.globalCompositeOperation = "destination-in";
|
|
821
|
+
ctx.drawImage(image, 0, 0);
|
|
822
|
+
this.canvas_[pixelRatio] = canvas;
|
|
823
|
+
}
|
|
824
|
+
/**
|
|
825
|
+
* @return {Promise<void>} Promise that resolves when the image is loaded.
|
|
826
|
+
*/
|
|
827
|
+
ready() {
|
|
828
|
+
if (!this.ready_) {
|
|
829
|
+
this.ready_ = new Promise((resolve) => {
|
|
830
|
+
if (this.imageState_ === ImageState.LOADED || this.imageState_ === ImageState.ERROR) {
|
|
831
|
+
resolve();
|
|
832
|
+
} else {
|
|
833
|
+
const onChange = () => {
|
|
834
|
+
if (this.imageState_ === ImageState.LOADED || this.imageState_ === ImageState.ERROR) {
|
|
835
|
+
this.removeEventListener(EventType.CHANGE, onChange);
|
|
836
|
+
resolve();
|
|
837
|
+
}
|
|
838
|
+
};
|
|
839
|
+
this.addEventListener(EventType.CHANGE, onChange);
|
|
840
|
+
}
|
|
841
|
+
});
|
|
842
|
+
}
|
|
843
|
+
return this.ready_;
|
|
844
|
+
}
|
|
845
|
+
}
|
|
846
|
+
function get(image, cacheKey, crossOrigin, imageState, color, pattern) {
|
|
847
|
+
let iconImage = cacheKey === void 0 ? void 0 : shared.get(cacheKey, crossOrigin, color);
|
|
848
|
+
if (!iconImage) {
|
|
849
|
+
iconImage = new IconImage(
|
|
850
|
+
image,
|
|
851
|
+
image && "src" in image ? image.src || void 0 : cacheKey,
|
|
852
|
+
crossOrigin,
|
|
853
|
+
imageState,
|
|
854
|
+
color
|
|
855
|
+
);
|
|
856
|
+
shared.set(cacheKey, crossOrigin, color, iconImage, pattern);
|
|
857
|
+
}
|
|
858
|
+
if (pattern && iconImage && !shared.getPattern(cacheKey, crossOrigin, color)) {
|
|
859
|
+
shared.set(cacheKey, crossOrigin, color, iconImage, pattern);
|
|
860
|
+
}
|
|
861
|
+
return iconImage;
|
|
862
|
+
}
|
|
863
|
+
function asColorLike(color) {
|
|
864
|
+
if (!color) {
|
|
865
|
+
return null;
|
|
866
|
+
}
|
|
867
|
+
if (Array.isArray(color)) {
|
|
868
|
+
return toString(color);
|
|
869
|
+
}
|
|
870
|
+
if (typeof color === "object" && "src" in color) {
|
|
871
|
+
return asCanvasPattern(color);
|
|
872
|
+
}
|
|
873
|
+
return color;
|
|
874
|
+
}
|
|
875
|
+
function asCanvasPattern(pattern) {
|
|
876
|
+
if (!pattern.offset || !pattern.size) {
|
|
877
|
+
return shared.getPattern(pattern.src, "anonymous", pattern.color);
|
|
878
|
+
}
|
|
879
|
+
const cacheKey = pattern.src + ":" + pattern.offset;
|
|
880
|
+
const canvasPattern = shared.getPattern(
|
|
881
|
+
cacheKey,
|
|
882
|
+
void 0,
|
|
883
|
+
pattern.color
|
|
884
|
+
);
|
|
885
|
+
if (canvasPattern) {
|
|
886
|
+
return canvasPattern;
|
|
887
|
+
}
|
|
888
|
+
const iconImage = shared.get(pattern.src, "anonymous", null);
|
|
889
|
+
if (iconImage.getImageState() !== ImageState.LOADED) {
|
|
890
|
+
return null;
|
|
891
|
+
}
|
|
892
|
+
const patternCanvasContext = createCanvasContext2D(
|
|
893
|
+
pattern.size[0],
|
|
894
|
+
pattern.size[1]
|
|
895
|
+
);
|
|
896
|
+
patternCanvasContext.drawImage(
|
|
897
|
+
iconImage.getImage(1),
|
|
898
|
+
pattern.offset[0],
|
|
899
|
+
pattern.offset[1],
|
|
900
|
+
pattern.size[0],
|
|
901
|
+
pattern.size[1],
|
|
902
|
+
0,
|
|
903
|
+
0,
|
|
904
|
+
pattern.size[0],
|
|
905
|
+
pattern.size[1]
|
|
906
|
+
);
|
|
907
|
+
get(
|
|
908
|
+
patternCanvasContext.canvas,
|
|
909
|
+
cacheKey,
|
|
910
|
+
void 0,
|
|
911
|
+
ImageState.LOADED,
|
|
912
|
+
pattern.color,
|
|
913
|
+
true
|
|
914
|
+
);
|
|
915
|
+
return shared.getPattern(cacheKey, void 0, pattern.color);
|
|
916
|
+
}
|
|
917
|
+
const defaultFont = "10px sans-serif";
|
|
918
|
+
const defaultFillStyle = "#000";
|
|
919
|
+
const defaultLineCap = "round";
|
|
920
|
+
const defaultLineDash = [];
|
|
921
|
+
const defaultLineDashOffset = 0;
|
|
922
|
+
const defaultLineJoin = "round";
|
|
923
|
+
const defaultMiterLimit = 10;
|
|
924
|
+
const defaultStrokeStyle = "#000";
|
|
925
|
+
const defaultTextAlign = "center";
|
|
926
|
+
const defaultTextBaseline = "middle";
|
|
927
|
+
const defaultPadding = [0, 0, 0, 0];
|
|
928
|
+
const defaultLineWidth = 1;
|
|
929
|
+
const checkedFonts = new BaseObject();
|
|
930
|
+
let measureContext = null;
|
|
931
|
+
let measureFont;
|
|
932
|
+
const textHeights = {};
|
|
933
|
+
const genericFontFamilies = /* @__PURE__ */ new Set([
|
|
934
|
+
"serif",
|
|
935
|
+
"sans-serif",
|
|
936
|
+
"monospace",
|
|
937
|
+
"cursive",
|
|
938
|
+
"fantasy",
|
|
939
|
+
"system-ui",
|
|
940
|
+
"ui-serif",
|
|
941
|
+
"ui-sans-serif",
|
|
942
|
+
"ui-monospace",
|
|
943
|
+
"ui-rounded",
|
|
944
|
+
"emoji",
|
|
945
|
+
"math",
|
|
946
|
+
"fangsong"
|
|
947
|
+
]);
|
|
948
|
+
function getFontKey(style, weight, family) {
|
|
949
|
+
return `${style} ${weight} 16px "${family}"`;
|
|
950
|
+
}
|
|
951
|
+
const registerFont = /* @__PURE__ */ (function() {
|
|
952
|
+
const retries = 100;
|
|
953
|
+
let timeout, fontFaceSet;
|
|
954
|
+
function isAvailable(fontSpec) {
|
|
955
|
+
return __async(this, null, function* () {
|
|
956
|
+
yield fontFaceSet.ready;
|
|
957
|
+
const fontFaces = yield fontFaceSet.load(fontSpec);
|
|
958
|
+
if (fontFaces.length === 0) {
|
|
959
|
+
return false;
|
|
960
|
+
}
|
|
961
|
+
const font = getFontParameters(fontSpec);
|
|
962
|
+
const checkFamily = font.families[0].toLowerCase();
|
|
963
|
+
const checkWeight = font.weight;
|
|
964
|
+
return fontFaces.some(
|
|
965
|
+
/**
|
|
966
|
+
* @param {import('../css.js').FontParameters} f Font.
|
|
967
|
+
* @return {boolean} Font matches.
|
|
968
|
+
*/
|
|
969
|
+
(f) => {
|
|
970
|
+
const family = f.family.replace(/^['"]|['"]$/g, "").toLowerCase();
|
|
971
|
+
const weight = fontWeights[f.weight] || f.weight;
|
|
972
|
+
return family === checkFamily && f.style === font.style && weight == checkWeight;
|
|
973
|
+
}
|
|
974
|
+
);
|
|
975
|
+
});
|
|
976
|
+
}
|
|
977
|
+
function check() {
|
|
978
|
+
return __async(this, null, function* () {
|
|
979
|
+
yield fontFaceSet.ready;
|
|
980
|
+
let done = true;
|
|
981
|
+
const checkedFontsProperties = checkedFonts.getProperties();
|
|
982
|
+
const fonts = Object.keys(checkedFontsProperties).filter(
|
|
983
|
+
(key) => checkedFontsProperties[key] < retries
|
|
984
|
+
);
|
|
985
|
+
for (let i = fonts.length - 1; i >= 0; --i) {
|
|
986
|
+
const font = fonts[i];
|
|
987
|
+
let currentRetries = checkedFontsProperties[font];
|
|
988
|
+
if (currentRetries < retries) {
|
|
989
|
+
if (yield isAvailable(font)) {
|
|
990
|
+
clear(textHeights);
|
|
991
|
+
checkedFonts.set(font, retries);
|
|
992
|
+
} else {
|
|
993
|
+
currentRetries += 10;
|
|
994
|
+
checkedFonts.set(font, currentRetries, true);
|
|
995
|
+
if (currentRetries < retries) {
|
|
996
|
+
done = false;
|
|
997
|
+
}
|
|
998
|
+
}
|
|
999
|
+
}
|
|
1000
|
+
}
|
|
1001
|
+
timeout = void 0;
|
|
1002
|
+
if (!done) {
|
|
1003
|
+
timeout = setTimeout(check, 100);
|
|
1004
|
+
}
|
|
1005
|
+
});
|
|
1006
|
+
}
|
|
1007
|
+
return function(fontSpec) {
|
|
1008
|
+
return __async(this, null, function* () {
|
|
1009
|
+
if (!fontFaceSet) {
|
|
1010
|
+
fontFaceSet = WORKER_OFFSCREEN_CANVAS ? self.fonts : document.fonts;
|
|
1011
|
+
}
|
|
1012
|
+
const font = getFontParameters(fontSpec);
|
|
1013
|
+
if (!font) {
|
|
1014
|
+
return;
|
|
1015
|
+
}
|
|
1016
|
+
const families = font.families;
|
|
1017
|
+
let needCheck = false;
|
|
1018
|
+
for (const family of families) {
|
|
1019
|
+
if (genericFontFamilies.has(family)) {
|
|
1020
|
+
continue;
|
|
1021
|
+
}
|
|
1022
|
+
const key = getFontKey(font.style, font.weight, family);
|
|
1023
|
+
if (checkedFonts.get(key) !== void 0) {
|
|
1024
|
+
continue;
|
|
1025
|
+
}
|
|
1026
|
+
checkedFonts.set(key, 0, true);
|
|
1027
|
+
needCheck = true;
|
|
1028
|
+
}
|
|
1029
|
+
if (needCheck) {
|
|
1030
|
+
clearTimeout(timeout);
|
|
1031
|
+
timeout = setTimeout(check, 100);
|
|
1032
|
+
}
|
|
1033
|
+
});
|
|
1034
|
+
};
|
|
1035
|
+
})();
|
|
1036
|
+
const measureTextHeight = /* @__PURE__ */ (function() {
|
|
1037
|
+
let measureElement;
|
|
1038
|
+
return function(fontSpec) {
|
|
1039
|
+
let height = textHeights[fontSpec];
|
|
1040
|
+
if (height == void 0) {
|
|
1041
|
+
if (WORKER_OFFSCREEN_CANVAS) {
|
|
1042
|
+
const font = getFontParameters(fontSpec);
|
|
1043
|
+
const metrics = measureText(fontSpec, "Žg");
|
|
1044
|
+
const lineHeight = isNaN(Number(font.lineHeight)) ? 1.2 : Number(font.lineHeight);
|
|
1045
|
+
height = lineHeight * (metrics.actualBoundingBoxAscent + metrics.actualBoundingBoxDescent);
|
|
1046
|
+
} else {
|
|
1047
|
+
if (!measureElement) {
|
|
1048
|
+
measureElement = document.createElement("div");
|
|
1049
|
+
measureElement.innerHTML = "M";
|
|
1050
|
+
measureElement.style.minHeight = "0";
|
|
1051
|
+
measureElement.style.maxHeight = "none";
|
|
1052
|
+
measureElement.style.height = "auto";
|
|
1053
|
+
measureElement.style.padding = "0";
|
|
1054
|
+
measureElement.style.border = "none";
|
|
1055
|
+
measureElement.style.position = "absolute";
|
|
1056
|
+
measureElement.style.display = "block";
|
|
1057
|
+
measureElement.style.left = "-99999px";
|
|
1058
|
+
}
|
|
1059
|
+
measureElement.style.font = fontSpec;
|
|
1060
|
+
document.body.appendChild(measureElement);
|
|
1061
|
+
height = measureElement.offsetHeight;
|
|
1062
|
+
document.body.removeChild(measureElement);
|
|
1063
|
+
}
|
|
1064
|
+
textHeights[fontSpec] = height;
|
|
1065
|
+
}
|
|
1066
|
+
return height;
|
|
1067
|
+
};
|
|
1068
|
+
})();
|
|
1069
|
+
function measureText(font, text) {
|
|
1070
|
+
if (!measureContext) {
|
|
1071
|
+
measureContext = createCanvasContext2D(1, 1);
|
|
1072
|
+
}
|
|
1073
|
+
if (font != measureFont) {
|
|
1074
|
+
measureContext.font = font;
|
|
1075
|
+
measureFont = measureContext.font;
|
|
1076
|
+
}
|
|
1077
|
+
return measureContext.measureText(text);
|
|
1078
|
+
}
|
|
1079
|
+
function measureTextWidth(font, text) {
|
|
1080
|
+
return measureText(font, text).width;
|
|
1081
|
+
}
|
|
1082
|
+
function measureAndCacheTextWidth(font, text, cache2) {
|
|
1083
|
+
if (text in cache2) {
|
|
1084
|
+
return cache2[text];
|
|
1085
|
+
}
|
|
1086
|
+
const width = text.split("\n").reduce((prev, curr) => Math.max(prev, measureTextWidth(font, curr)), 0);
|
|
1087
|
+
cache2[text] = width;
|
|
1088
|
+
return width;
|
|
1089
|
+
}
|
|
1090
|
+
function getTextDimensions(baseStyle, chunks) {
|
|
1091
|
+
const widths = [];
|
|
1092
|
+
const heights = [];
|
|
1093
|
+
const lineWidths = [];
|
|
1094
|
+
let width = 0;
|
|
1095
|
+
let lineWidth = 0;
|
|
1096
|
+
let height = 0;
|
|
1097
|
+
let lineHeight = 0;
|
|
1098
|
+
for (let i = 0, ii = chunks.length; i <= ii; i += 2) {
|
|
1099
|
+
const text = chunks[i];
|
|
1100
|
+
if (text === "\n" || i === ii) {
|
|
1101
|
+
width = Math.max(width, lineWidth);
|
|
1102
|
+
lineWidths.push(lineWidth);
|
|
1103
|
+
lineWidth = 0;
|
|
1104
|
+
height += lineHeight;
|
|
1105
|
+
lineHeight = 0;
|
|
1106
|
+
continue;
|
|
1107
|
+
}
|
|
1108
|
+
const font = chunks[i + 1] || baseStyle.font;
|
|
1109
|
+
const currentWidth = measureTextWidth(font, text);
|
|
1110
|
+
widths.push(currentWidth);
|
|
1111
|
+
lineWidth += currentWidth;
|
|
1112
|
+
const currentHeight = measureTextHeight(font);
|
|
1113
|
+
heights.push(currentHeight);
|
|
1114
|
+
lineHeight = Math.max(lineHeight, currentHeight);
|
|
1115
|
+
}
|
|
1116
|
+
return { width, height, widths, heights, lineWidths };
|
|
1117
|
+
}
|
|
1118
|
+
function drawImageOrLabel(context, transform, opacity, labelOrImage, originX, originY, w, h, x, y, scale2) {
|
|
1119
|
+
context.save();
|
|
1120
|
+
if (opacity !== 1) {
|
|
1121
|
+
if (context.globalAlpha === void 0) {
|
|
1122
|
+
context.globalAlpha = (context2) => context2.globalAlpha *= opacity;
|
|
1123
|
+
} else {
|
|
1124
|
+
context.globalAlpha *= opacity;
|
|
1125
|
+
}
|
|
1126
|
+
}
|
|
1127
|
+
if (transform) {
|
|
1128
|
+
context.transform.apply(context, transform);
|
|
1129
|
+
}
|
|
1130
|
+
if (
|
|
1131
|
+
/** @type {*} */
|
|
1132
|
+
labelOrImage.contextInstructions
|
|
1133
|
+
) {
|
|
1134
|
+
context.translate(x, y);
|
|
1135
|
+
context.scale(scale2[0], scale2[1]);
|
|
1136
|
+
executeLabelInstructions(
|
|
1137
|
+
/** @type {Label} */
|
|
1138
|
+
labelOrImage,
|
|
1139
|
+
context
|
|
1140
|
+
);
|
|
1141
|
+
} else if (scale2[0] < 0 || scale2[1] < 0) {
|
|
1142
|
+
context.translate(x, y);
|
|
1143
|
+
context.scale(scale2[0], scale2[1]);
|
|
1144
|
+
context.drawImage(
|
|
1145
|
+
/** @type {HTMLCanvasElement|HTMLImageElement|HTMLVideoElement} */
|
|
1146
|
+
labelOrImage,
|
|
1147
|
+
originX,
|
|
1148
|
+
originY,
|
|
1149
|
+
w,
|
|
1150
|
+
h,
|
|
1151
|
+
0,
|
|
1152
|
+
0,
|
|
1153
|
+
w,
|
|
1154
|
+
h
|
|
1155
|
+
);
|
|
1156
|
+
} else {
|
|
1157
|
+
context.drawImage(
|
|
1158
|
+
/** @type {HTMLCanvasElement|HTMLImageElement|HTMLVideoElement} */
|
|
1159
|
+
labelOrImage,
|
|
1160
|
+
originX,
|
|
1161
|
+
originY,
|
|
1162
|
+
w,
|
|
1163
|
+
h,
|
|
1164
|
+
x,
|
|
1165
|
+
y,
|
|
1166
|
+
w * scale2[0],
|
|
1167
|
+
h * scale2[1]
|
|
1168
|
+
);
|
|
1169
|
+
}
|
|
1170
|
+
context.restore();
|
|
1171
|
+
}
|
|
1172
|
+
function executeLabelInstructions(label, context) {
|
|
1173
|
+
const contextInstructions = label.contextInstructions;
|
|
1174
|
+
for (let i = 0, ii = contextInstructions.length; i < ii; i += 2) {
|
|
1175
|
+
if (Array.isArray(contextInstructions[i + 1])) {
|
|
1176
|
+
context[contextInstructions[i]].apply(
|
|
1177
|
+
context,
|
|
1178
|
+
contextInstructions[i + 1]
|
|
1179
|
+
);
|
|
1180
|
+
} else {
|
|
1181
|
+
context[contextInstructions[i]] = contextInstructions[i + 1];
|
|
1182
|
+
}
|
|
1183
|
+
}
|
|
1184
|
+
}
|
|
1185
|
+
class ImageStyle {
|
|
1186
|
+
/**
|
|
1187
|
+
* @param {Options} options Options.
|
|
1188
|
+
*/
|
|
1189
|
+
constructor(options) {
|
|
1190
|
+
this.opacity_ = options.opacity;
|
|
1191
|
+
this.rotateWithView_ = options.rotateWithView;
|
|
1192
|
+
this.rotation_ = options.rotation;
|
|
1193
|
+
this.scale_ = options.scale;
|
|
1194
|
+
this.scaleArray_ = toSize(options.scale);
|
|
1195
|
+
this.displacement_ = options.displacement;
|
|
1196
|
+
this.declutterMode_ = options.declutterMode;
|
|
1197
|
+
}
|
|
1198
|
+
/**
|
|
1199
|
+
* Clones the style.
|
|
1200
|
+
* @return {ImageStyle} The cloned style.
|
|
1201
|
+
* @api
|
|
1202
|
+
*/
|
|
1203
|
+
clone() {
|
|
1204
|
+
const scale2 = this.getScale();
|
|
1205
|
+
return new ImageStyle({
|
|
1206
|
+
opacity: this.getOpacity(),
|
|
1207
|
+
scale: Array.isArray(scale2) ? scale2.slice() : scale2,
|
|
1208
|
+
rotation: this.getRotation(),
|
|
1209
|
+
rotateWithView: this.getRotateWithView(),
|
|
1210
|
+
displacement: this.getDisplacement().slice(),
|
|
1211
|
+
declutterMode: this.getDeclutterMode()
|
|
1212
|
+
});
|
|
1213
|
+
}
|
|
1214
|
+
/**
|
|
1215
|
+
* Get the symbolizer opacity.
|
|
1216
|
+
* @return {number} Opacity.
|
|
1217
|
+
* @api
|
|
1218
|
+
*/
|
|
1219
|
+
getOpacity() {
|
|
1220
|
+
return this.opacity_;
|
|
1221
|
+
}
|
|
1222
|
+
/**
|
|
1223
|
+
* Determine whether the symbolizer rotates with the map.
|
|
1224
|
+
* @return {boolean} Rotate with map.
|
|
1225
|
+
* @api
|
|
1226
|
+
*/
|
|
1227
|
+
getRotateWithView() {
|
|
1228
|
+
return this.rotateWithView_;
|
|
1229
|
+
}
|
|
1230
|
+
/**
|
|
1231
|
+
* Get the symoblizer rotation.
|
|
1232
|
+
* @return {number} Rotation.
|
|
1233
|
+
* @api
|
|
1234
|
+
*/
|
|
1235
|
+
getRotation() {
|
|
1236
|
+
return this.rotation_;
|
|
1237
|
+
}
|
|
1238
|
+
/**
|
|
1239
|
+
* Get the symbolizer scale.
|
|
1240
|
+
* @return {number|import("../size.js").Size} Scale.
|
|
1241
|
+
* @api
|
|
1242
|
+
*/
|
|
1243
|
+
getScale() {
|
|
1244
|
+
return this.scale_;
|
|
1245
|
+
}
|
|
1246
|
+
/**
|
|
1247
|
+
* Get the symbolizer scale array.
|
|
1248
|
+
* @return {import("../size.js").Size} Scale array.
|
|
1249
|
+
*/
|
|
1250
|
+
getScaleArray() {
|
|
1251
|
+
return this.scaleArray_;
|
|
1252
|
+
}
|
|
1253
|
+
/**
|
|
1254
|
+
* Get the displacement of the shape
|
|
1255
|
+
* @return {Array<number>} Shape's center displacement
|
|
1256
|
+
* @api
|
|
1257
|
+
*/
|
|
1258
|
+
getDisplacement() {
|
|
1259
|
+
return this.displacement_;
|
|
1260
|
+
}
|
|
1261
|
+
/**
|
|
1262
|
+
* Get the declutter mode of the shape
|
|
1263
|
+
* @return {import("./Style.js").DeclutterMode} Shape's declutter mode
|
|
1264
|
+
* @api
|
|
1265
|
+
*/
|
|
1266
|
+
getDeclutterMode() {
|
|
1267
|
+
return this.declutterMode_;
|
|
1268
|
+
}
|
|
1269
|
+
/**
|
|
1270
|
+
* Get the anchor point in pixels. The anchor determines the center point for the
|
|
1271
|
+
* symbolizer.
|
|
1272
|
+
* @abstract
|
|
1273
|
+
* @return {Array<number>} Anchor.
|
|
1274
|
+
*/
|
|
1275
|
+
getAnchor() {
|
|
1276
|
+
return abstract();
|
|
1277
|
+
}
|
|
1278
|
+
/**
|
|
1279
|
+
* Get the image element for the symbolizer.
|
|
1280
|
+
* @abstract
|
|
1281
|
+
* @param {number} pixelRatio Pixel ratio.
|
|
1282
|
+
* @return {import('../DataTile.js').ImageLike} Image element.
|
|
1283
|
+
*/
|
|
1284
|
+
getImage(pixelRatio) {
|
|
1285
|
+
return abstract();
|
|
1286
|
+
}
|
|
1287
|
+
/**
|
|
1288
|
+
* @abstract
|
|
1289
|
+
* @return {import('../DataTile.js').ImageLike} Image element.
|
|
1290
|
+
*/
|
|
1291
|
+
getHitDetectionImage() {
|
|
1292
|
+
return abstract();
|
|
1293
|
+
}
|
|
1294
|
+
/**
|
|
1295
|
+
* Get the image pixel ratio.
|
|
1296
|
+
* @param {number} pixelRatio Pixel ratio.
|
|
1297
|
+
* @return {number} Pixel ratio.
|
|
1298
|
+
*/
|
|
1299
|
+
getPixelRatio(pixelRatio) {
|
|
1300
|
+
return 1;
|
|
1301
|
+
}
|
|
1302
|
+
/**
|
|
1303
|
+
* @abstract
|
|
1304
|
+
* @return {import("../ImageState.js").default} Image state.
|
|
1305
|
+
*/
|
|
1306
|
+
getImageState() {
|
|
1307
|
+
return abstract();
|
|
1308
|
+
}
|
|
1309
|
+
/**
|
|
1310
|
+
* @abstract
|
|
1311
|
+
* @return {import("../size.js").Size} Image size.
|
|
1312
|
+
*/
|
|
1313
|
+
getImageSize() {
|
|
1314
|
+
return abstract();
|
|
1315
|
+
}
|
|
1316
|
+
/**
|
|
1317
|
+
* Get the origin of the symbolizer.
|
|
1318
|
+
* @abstract
|
|
1319
|
+
* @return {Array<number>} Origin.
|
|
1320
|
+
*/
|
|
1321
|
+
getOrigin() {
|
|
1322
|
+
return abstract();
|
|
1323
|
+
}
|
|
1324
|
+
/**
|
|
1325
|
+
* Get the size of the symbolizer (in pixels).
|
|
1326
|
+
* @abstract
|
|
1327
|
+
* @return {import("../size.js").Size} Size.
|
|
1328
|
+
*/
|
|
1329
|
+
getSize() {
|
|
1330
|
+
return abstract();
|
|
1331
|
+
}
|
|
1332
|
+
/**
|
|
1333
|
+
* Set the displacement.
|
|
1334
|
+
*
|
|
1335
|
+
* @param {Array<number>} displacement Displacement.
|
|
1336
|
+
* @api
|
|
1337
|
+
*/
|
|
1338
|
+
setDisplacement(displacement) {
|
|
1339
|
+
this.displacement_ = displacement;
|
|
1340
|
+
}
|
|
1341
|
+
/**
|
|
1342
|
+
* Set the opacity.
|
|
1343
|
+
*
|
|
1344
|
+
* @param {number} opacity Opacity.
|
|
1345
|
+
* @api
|
|
1346
|
+
*/
|
|
1347
|
+
setOpacity(opacity) {
|
|
1348
|
+
this.opacity_ = opacity;
|
|
1349
|
+
}
|
|
1350
|
+
/**
|
|
1351
|
+
* Set whether to rotate the style with the view.
|
|
1352
|
+
*
|
|
1353
|
+
* @param {boolean} rotateWithView Rotate with map.
|
|
1354
|
+
* @api
|
|
1355
|
+
*/
|
|
1356
|
+
setRotateWithView(rotateWithView) {
|
|
1357
|
+
this.rotateWithView_ = rotateWithView;
|
|
1358
|
+
}
|
|
1359
|
+
/**
|
|
1360
|
+
* Set the rotation.
|
|
1361
|
+
*
|
|
1362
|
+
* @param {number} rotation Rotation.
|
|
1363
|
+
* @api
|
|
1364
|
+
*/
|
|
1365
|
+
setRotation(rotation) {
|
|
1366
|
+
this.rotation_ = rotation;
|
|
1367
|
+
}
|
|
1368
|
+
/**
|
|
1369
|
+
* Set the scale.
|
|
1370
|
+
*
|
|
1371
|
+
* @param {number|import("../size.js").Size} scale Scale.
|
|
1372
|
+
* @api
|
|
1373
|
+
*/
|
|
1374
|
+
setScale(scale2) {
|
|
1375
|
+
this.scale_ = scale2;
|
|
1376
|
+
this.scaleArray_ = toSize(scale2);
|
|
1377
|
+
}
|
|
1378
|
+
/**
|
|
1379
|
+
* @abstract
|
|
1380
|
+
* @param {function(import("../events/Event.js").default): void} listener Listener function.
|
|
1381
|
+
*/
|
|
1382
|
+
listenImageChange(listener) {
|
|
1383
|
+
abstract();
|
|
1384
|
+
}
|
|
1385
|
+
/**
|
|
1386
|
+
* Load not yet loaded URI.
|
|
1387
|
+
* @abstract
|
|
1388
|
+
*/
|
|
1389
|
+
load() {
|
|
1390
|
+
abstract();
|
|
1391
|
+
}
|
|
1392
|
+
/**
|
|
1393
|
+
* @abstract
|
|
1394
|
+
* @param {function(import("../events/Event.js").default): void} listener Listener function.
|
|
1395
|
+
*/
|
|
1396
|
+
unlistenImageChange(listener) {
|
|
1397
|
+
abstract();
|
|
1398
|
+
}
|
|
1399
|
+
/**
|
|
1400
|
+
* @return {Promise<void>} `false` or Promise that resolves when the style is ready to use.
|
|
1401
|
+
*/
|
|
1402
|
+
ready() {
|
|
1403
|
+
return Promise.resolve();
|
|
1404
|
+
}
|
|
1405
|
+
}
|
|
1406
|
+
class RegularShape extends ImageStyle {
|
|
1407
|
+
/**
|
|
1408
|
+
* @param {Options} options Options.
|
|
1409
|
+
*/
|
|
1410
|
+
constructor(options) {
|
|
1411
|
+
super({
|
|
1412
|
+
opacity: 1,
|
|
1413
|
+
rotateWithView: options.rotateWithView !== void 0 ? options.rotateWithView : false,
|
|
1414
|
+
rotation: options.rotation !== void 0 ? options.rotation : 0,
|
|
1415
|
+
scale: options.scale !== void 0 ? options.scale : 1,
|
|
1416
|
+
displacement: options.displacement !== void 0 ? options.displacement : [0, 0],
|
|
1417
|
+
declutterMode: options.declutterMode
|
|
1418
|
+
});
|
|
1419
|
+
this.hitDetectionCanvas_ = null;
|
|
1420
|
+
this.fill_ = options.fill !== void 0 ? options.fill : null;
|
|
1421
|
+
this.origin_ = [0, 0];
|
|
1422
|
+
this.points_ = options.points;
|
|
1423
|
+
this.radius = options.radius;
|
|
1424
|
+
this.radius2_ = options.radius2;
|
|
1425
|
+
this.angle_ = options.angle !== void 0 ? options.angle : 0;
|
|
1426
|
+
this.stroke_ = options.stroke !== void 0 ? options.stroke : null;
|
|
1427
|
+
this.size_;
|
|
1428
|
+
this.renderOptions_;
|
|
1429
|
+
this.imageState_ = this.fill_ && this.fill_.loading() ? ImageState.LOADING : ImageState.LOADED;
|
|
1430
|
+
if (this.imageState_ === ImageState.LOADING) {
|
|
1431
|
+
this.ready().then(() => this.imageState_ = ImageState.LOADED);
|
|
1432
|
+
}
|
|
1433
|
+
this.render();
|
|
1434
|
+
}
|
|
1435
|
+
/**
|
|
1436
|
+
* Clones the style.
|
|
1437
|
+
* @return {RegularShape} The cloned style.
|
|
1438
|
+
* @api
|
|
1439
|
+
* @override
|
|
1440
|
+
*/
|
|
1441
|
+
clone() {
|
|
1442
|
+
const scale2 = this.getScale();
|
|
1443
|
+
const style = new RegularShape({
|
|
1444
|
+
fill: this.getFill() ? this.getFill().clone() : void 0,
|
|
1445
|
+
points: this.getPoints(),
|
|
1446
|
+
radius: this.getRadius(),
|
|
1447
|
+
radius2: this.getRadius2(),
|
|
1448
|
+
angle: this.getAngle(),
|
|
1449
|
+
stroke: this.getStroke() ? this.getStroke().clone() : void 0,
|
|
1450
|
+
rotation: this.getRotation(),
|
|
1451
|
+
rotateWithView: this.getRotateWithView(),
|
|
1452
|
+
scale: Array.isArray(scale2) ? scale2.slice() : scale2,
|
|
1453
|
+
displacement: this.getDisplacement().slice(),
|
|
1454
|
+
declutterMode: this.getDeclutterMode()
|
|
1455
|
+
});
|
|
1456
|
+
style.setOpacity(this.getOpacity());
|
|
1457
|
+
return style;
|
|
1458
|
+
}
|
|
1459
|
+
/**
|
|
1460
|
+
* Get the anchor point in pixels. The anchor determines the center point for the
|
|
1461
|
+
* symbolizer.
|
|
1462
|
+
* @return {Array<number>} Anchor.
|
|
1463
|
+
* @api
|
|
1464
|
+
* @override
|
|
1465
|
+
*/
|
|
1466
|
+
getAnchor() {
|
|
1467
|
+
const size = this.size_;
|
|
1468
|
+
const displacement = this.getDisplacement();
|
|
1469
|
+
const scale2 = this.getScaleArray();
|
|
1470
|
+
return [
|
|
1471
|
+
size[0] / 2 - displacement[0] / scale2[0],
|
|
1472
|
+
size[1] / 2 + displacement[1] / scale2[1]
|
|
1473
|
+
];
|
|
1474
|
+
}
|
|
1475
|
+
/**
|
|
1476
|
+
* Get the angle used in generating the shape.
|
|
1477
|
+
* @return {number} Shape's rotation in radians.
|
|
1478
|
+
* @api
|
|
1479
|
+
*/
|
|
1480
|
+
getAngle() {
|
|
1481
|
+
return this.angle_;
|
|
1482
|
+
}
|
|
1483
|
+
/**
|
|
1484
|
+
* Get the fill style for the shape.
|
|
1485
|
+
* @return {import("./Fill.js").default|null} Fill style.
|
|
1486
|
+
* @api
|
|
1487
|
+
*/
|
|
1488
|
+
getFill() {
|
|
1489
|
+
return this.fill_;
|
|
1490
|
+
}
|
|
1491
|
+
/**
|
|
1492
|
+
* Set the fill style.
|
|
1493
|
+
* @param {import("./Fill.js").default|null} fill Fill style.
|
|
1494
|
+
* @api
|
|
1495
|
+
*/
|
|
1496
|
+
setFill(fill) {
|
|
1497
|
+
this.fill_ = fill;
|
|
1498
|
+
this.render();
|
|
1499
|
+
}
|
|
1500
|
+
/**
|
|
1501
|
+
* @return {HTMLCanvasElement|OffscreenCanvas} Image element.
|
|
1502
|
+
* @override
|
|
1503
|
+
*/
|
|
1504
|
+
getHitDetectionImage() {
|
|
1505
|
+
if (!this.hitDetectionCanvas_) {
|
|
1506
|
+
this.hitDetectionCanvas_ = this.createHitDetectionCanvas_(
|
|
1507
|
+
this.renderOptions_
|
|
1508
|
+
);
|
|
1509
|
+
}
|
|
1510
|
+
return this.hitDetectionCanvas_;
|
|
1511
|
+
}
|
|
1512
|
+
/**
|
|
1513
|
+
* Get the image icon.
|
|
1514
|
+
* @param {number} pixelRatio Pixel ratio.
|
|
1515
|
+
* @return {HTMLCanvasElement|OffscreenCanvas} Image or Canvas element.
|
|
1516
|
+
* @api
|
|
1517
|
+
* @override
|
|
1518
|
+
*/
|
|
1519
|
+
getImage(pixelRatio) {
|
|
1520
|
+
var _a, _b;
|
|
1521
|
+
const fillKey = (_a = this.fill_) == null ? void 0 : _a.getKey();
|
|
1522
|
+
const cacheKey = `${pixelRatio},${this.angle_},${this.radius},${this.radius2_},${this.points_},${fillKey}` + Object.values(this.renderOptions_).join(",");
|
|
1523
|
+
let image = (
|
|
1524
|
+
/** @type {HTMLCanvasElement|OffscreenCanvas} */
|
|
1525
|
+
(_b = shared.get(cacheKey, null, null)) == null ? void 0 : _b.getImage(1)
|
|
1526
|
+
);
|
|
1527
|
+
if (!image) {
|
|
1528
|
+
const renderOptions = this.renderOptions_;
|
|
1529
|
+
const size = Math.ceil(renderOptions.size * pixelRatio);
|
|
1530
|
+
const context = createCanvasContext2D(size, size);
|
|
1531
|
+
this.draw_(renderOptions, context, pixelRatio);
|
|
1532
|
+
image = context.canvas;
|
|
1533
|
+
shared.set(
|
|
1534
|
+
cacheKey,
|
|
1535
|
+
null,
|
|
1536
|
+
null,
|
|
1537
|
+
new IconImage(image, void 0, null, ImageState.LOADED, null)
|
|
1538
|
+
);
|
|
1539
|
+
}
|
|
1540
|
+
return image;
|
|
1541
|
+
}
|
|
1542
|
+
/**
|
|
1543
|
+
* Get the image pixel ratio.
|
|
1544
|
+
* @param {number} pixelRatio Pixel ratio.
|
|
1545
|
+
* @return {number} Pixel ratio.
|
|
1546
|
+
* @override
|
|
1547
|
+
*/
|
|
1548
|
+
getPixelRatio(pixelRatio) {
|
|
1549
|
+
return pixelRatio;
|
|
1550
|
+
}
|
|
1551
|
+
/**
|
|
1552
|
+
* @return {import("../size.js").Size} Image size.
|
|
1553
|
+
* @override
|
|
1554
|
+
*/
|
|
1555
|
+
getImageSize() {
|
|
1556
|
+
return this.size_;
|
|
1557
|
+
}
|
|
1558
|
+
/**
|
|
1559
|
+
* @return {import("../ImageState.js").default} Image state.
|
|
1560
|
+
* @override
|
|
1561
|
+
*/
|
|
1562
|
+
getImageState() {
|
|
1563
|
+
return this.imageState_;
|
|
1564
|
+
}
|
|
1565
|
+
/**
|
|
1566
|
+
* Get the origin of the symbolizer.
|
|
1567
|
+
* @return {Array<number>} Origin.
|
|
1568
|
+
* @api
|
|
1569
|
+
* @override
|
|
1570
|
+
*/
|
|
1571
|
+
getOrigin() {
|
|
1572
|
+
return this.origin_;
|
|
1573
|
+
}
|
|
1574
|
+
/**
|
|
1575
|
+
* Get the number of points for generating the shape.
|
|
1576
|
+
* @return {number} Number of points for stars and regular polygons.
|
|
1577
|
+
* @api
|
|
1578
|
+
*/
|
|
1579
|
+
getPoints() {
|
|
1580
|
+
return this.points_;
|
|
1581
|
+
}
|
|
1582
|
+
/**
|
|
1583
|
+
* Get the (primary) radius for the shape.
|
|
1584
|
+
* @return {number} Radius.
|
|
1585
|
+
* @api
|
|
1586
|
+
*/
|
|
1587
|
+
getRadius() {
|
|
1588
|
+
return this.radius;
|
|
1589
|
+
}
|
|
1590
|
+
/**
|
|
1591
|
+
* Get the secondary radius for the shape.
|
|
1592
|
+
* @return {number|undefined} Radius2.
|
|
1593
|
+
* @api
|
|
1594
|
+
*/
|
|
1595
|
+
getRadius2() {
|
|
1596
|
+
return this.radius2_;
|
|
1597
|
+
}
|
|
1598
|
+
/**
|
|
1599
|
+
* Get the size of the symbolizer (in pixels).
|
|
1600
|
+
* @return {import("../size.js").Size} Size.
|
|
1601
|
+
* @api
|
|
1602
|
+
* @override
|
|
1603
|
+
*/
|
|
1604
|
+
getSize() {
|
|
1605
|
+
return this.size_;
|
|
1606
|
+
}
|
|
1607
|
+
/**
|
|
1608
|
+
* Get the stroke style for the shape.
|
|
1609
|
+
* @return {import("./Stroke.js").default|null} Stroke style.
|
|
1610
|
+
* @api
|
|
1611
|
+
*/
|
|
1612
|
+
getStroke() {
|
|
1613
|
+
return this.stroke_;
|
|
1614
|
+
}
|
|
1615
|
+
/**
|
|
1616
|
+
* Set the stroke style.
|
|
1617
|
+
* @param {import("./Stroke.js").default|null} stroke Stroke style.
|
|
1618
|
+
* @api
|
|
1619
|
+
*/
|
|
1620
|
+
setStroke(stroke) {
|
|
1621
|
+
this.stroke_ = stroke;
|
|
1622
|
+
this.render();
|
|
1623
|
+
}
|
|
1624
|
+
/**
|
|
1625
|
+
* @param {function(import("../events/Event.js").default): void} listener Listener function.
|
|
1626
|
+
* @override
|
|
1627
|
+
*/
|
|
1628
|
+
listenImageChange(listener) {
|
|
1629
|
+
}
|
|
1630
|
+
/**
|
|
1631
|
+
* Load not yet loaded URI.
|
|
1632
|
+
* @override
|
|
1633
|
+
*/
|
|
1634
|
+
load() {
|
|
1635
|
+
}
|
|
1636
|
+
/**
|
|
1637
|
+
* @param {function(import("../events/Event.js").default): void} listener Listener function.
|
|
1638
|
+
* @override
|
|
1639
|
+
*/
|
|
1640
|
+
unlistenImageChange(listener) {
|
|
1641
|
+
}
|
|
1642
|
+
/**
|
|
1643
|
+
* Calculate additional canvas size needed for the miter.
|
|
1644
|
+
* @param {string} lineJoin Line join
|
|
1645
|
+
* @param {number} strokeWidth Stroke width
|
|
1646
|
+
* @param {number} miterLimit Miter limit
|
|
1647
|
+
* @return {number} Additional canvas size needed
|
|
1648
|
+
* @private
|
|
1649
|
+
*/
|
|
1650
|
+
calculateLineJoinSize_(lineJoin, strokeWidth, miterLimit) {
|
|
1651
|
+
if (strokeWidth === 0 || this.points_ === Infinity || lineJoin !== "bevel" && lineJoin !== "miter") {
|
|
1652
|
+
return strokeWidth;
|
|
1653
|
+
}
|
|
1654
|
+
let r1 = this.radius;
|
|
1655
|
+
let r2 = this.radius2_ === void 0 ? r1 : this.radius2_;
|
|
1656
|
+
if (r1 < r2) {
|
|
1657
|
+
const tmp = r1;
|
|
1658
|
+
r1 = r2;
|
|
1659
|
+
r2 = tmp;
|
|
1660
|
+
}
|
|
1661
|
+
const points = this.radius2_ === void 0 ? this.points_ : this.points_ * 2;
|
|
1662
|
+
const alpha = 2 * Math.PI / points;
|
|
1663
|
+
const a = r2 * Math.sin(alpha);
|
|
1664
|
+
const b = Math.sqrt(r2 * r2 - a * a);
|
|
1665
|
+
const d = r1 - b;
|
|
1666
|
+
const e = Math.sqrt(a * a + d * d);
|
|
1667
|
+
const miterRatio = e / a;
|
|
1668
|
+
if (lineJoin === "miter" && miterRatio <= miterLimit) {
|
|
1669
|
+
return miterRatio * strokeWidth;
|
|
1670
|
+
}
|
|
1671
|
+
const k = strokeWidth / 2 / miterRatio;
|
|
1672
|
+
const l = strokeWidth / 2 * (d / e);
|
|
1673
|
+
const maxr = Math.sqrt((r1 + k) * (r1 + k) + l * l);
|
|
1674
|
+
const bevelAdd = maxr - r1;
|
|
1675
|
+
if (this.radius2_ === void 0 || lineJoin === "bevel") {
|
|
1676
|
+
return bevelAdd * 2;
|
|
1677
|
+
}
|
|
1678
|
+
const aa = r1 * Math.sin(alpha);
|
|
1679
|
+
const bb = Math.sqrt(r1 * r1 - aa * aa);
|
|
1680
|
+
const dd = r2 - bb;
|
|
1681
|
+
const ee = Math.sqrt(aa * aa + dd * dd);
|
|
1682
|
+
const innerMiterRatio = ee / aa;
|
|
1683
|
+
if (innerMiterRatio <= miterLimit) {
|
|
1684
|
+
const innerLength = innerMiterRatio * strokeWidth / 2 - r2 - r1;
|
|
1685
|
+
return 2 * Math.max(bevelAdd, innerLength);
|
|
1686
|
+
}
|
|
1687
|
+
return bevelAdd * 2;
|
|
1688
|
+
}
|
|
1689
|
+
/**
|
|
1690
|
+
* @return {RenderOptions} The render options
|
|
1691
|
+
* @protected
|
|
1692
|
+
*/
|
|
1693
|
+
createRenderOptions() {
|
|
1694
|
+
var _a, _b, _c, _d, _e, _f;
|
|
1695
|
+
let lineCap = defaultLineCap;
|
|
1696
|
+
let lineJoin = defaultLineJoin;
|
|
1697
|
+
let miterLimit = 0;
|
|
1698
|
+
let lineDash = null;
|
|
1699
|
+
let lineDashOffset = 0;
|
|
1700
|
+
let strokeStyle;
|
|
1701
|
+
let strokeWidth = 0;
|
|
1702
|
+
if (this.stroke_) {
|
|
1703
|
+
strokeStyle = asColorLike((_a = this.stroke_.getColor()) != null ? _a : defaultStrokeStyle);
|
|
1704
|
+
strokeWidth = (_b = this.stroke_.getWidth()) != null ? _b : defaultLineWidth;
|
|
1705
|
+
lineDash = this.stroke_.getLineDash();
|
|
1706
|
+
lineDashOffset = (_c = this.stroke_.getLineDashOffset()) != null ? _c : 0;
|
|
1707
|
+
lineJoin = (_d = this.stroke_.getLineJoin()) != null ? _d : defaultLineJoin;
|
|
1708
|
+
lineCap = (_e = this.stroke_.getLineCap()) != null ? _e : defaultLineCap;
|
|
1709
|
+
miterLimit = (_f = this.stroke_.getMiterLimit()) != null ? _f : defaultMiterLimit;
|
|
1710
|
+
}
|
|
1711
|
+
const add = this.calculateLineJoinSize_(lineJoin, strokeWidth, miterLimit);
|
|
1712
|
+
const maxRadius = Math.max(this.radius, this.radius2_ || 0);
|
|
1713
|
+
const size = Math.ceil(2 * maxRadius + add);
|
|
1714
|
+
return {
|
|
1715
|
+
strokeStyle,
|
|
1716
|
+
strokeWidth,
|
|
1717
|
+
size,
|
|
1718
|
+
lineCap,
|
|
1719
|
+
lineDash,
|
|
1720
|
+
lineDashOffset,
|
|
1721
|
+
lineJoin,
|
|
1722
|
+
miterLimit
|
|
1723
|
+
};
|
|
1724
|
+
}
|
|
1725
|
+
/**
|
|
1726
|
+
* @protected
|
|
1727
|
+
*/
|
|
1728
|
+
render() {
|
|
1729
|
+
this.renderOptions_ = this.createRenderOptions();
|
|
1730
|
+
const size = this.renderOptions_.size;
|
|
1731
|
+
this.hitDetectionCanvas_ = null;
|
|
1732
|
+
this.size_ = [size, size];
|
|
1733
|
+
}
|
|
1734
|
+
/**
|
|
1735
|
+
* @private
|
|
1736
|
+
* @param {RenderOptions} renderOptions Render options.
|
|
1737
|
+
* @param {CanvasRenderingContext2D|OffscreenCanvasRenderingContext2D} context The rendering context.
|
|
1738
|
+
* @param {number} pixelRatio The pixel ratio.
|
|
1739
|
+
*/
|
|
1740
|
+
draw_(renderOptions, context, pixelRatio) {
|
|
1741
|
+
context.scale(pixelRatio, pixelRatio);
|
|
1742
|
+
context.translate(renderOptions.size / 2, renderOptions.size / 2);
|
|
1743
|
+
this.createPath_(context);
|
|
1744
|
+
if (this.fill_) {
|
|
1745
|
+
let color = this.fill_.getColor();
|
|
1746
|
+
if (color === null) {
|
|
1747
|
+
color = defaultFillStyle;
|
|
1748
|
+
}
|
|
1749
|
+
context.fillStyle = asColorLike(color);
|
|
1750
|
+
context.fill();
|
|
1751
|
+
}
|
|
1752
|
+
if (renderOptions.strokeStyle) {
|
|
1753
|
+
context.strokeStyle = renderOptions.strokeStyle;
|
|
1754
|
+
context.lineWidth = renderOptions.strokeWidth;
|
|
1755
|
+
if (renderOptions.lineDash) {
|
|
1756
|
+
context.setLineDash(renderOptions.lineDash);
|
|
1757
|
+
context.lineDashOffset = renderOptions.lineDashOffset;
|
|
1758
|
+
}
|
|
1759
|
+
context.lineCap = renderOptions.lineCap;
|
|
1760
|
+
context.lineJoin = renderOptions.lineJoin;
|
|
1761
|
+
context.miterLimit = renderOptions.miterLimit;
|
|
1762
|
+
context.stroke();
|
|
1763
|
+
}
|
|
1764
|
+
}
|
|
1765
|
+
/**
|
|
1766
|
+
* @private
|
|
1767
|
+
* @param {RenderOptions} renderOptions Render options.
|
|
1768
|
+
* @return {HTMLCanvasElement|OffscreenCanvas} Canvas containing the icon
|
|
1769
|
+
*/
|
|
1770
|
+
createHitDetectionCanvas_(renderOptions) {
|
|
1771
|
+
let context;
|
|
1772
|
+
if (this.fill_) {
|
|
1773
|
+
let color = this.fill_.getColor();
|
|
1774
|
+
let opacity = 0;
|
|
1775
|
+
if (typeof color === "string") {
|
|
1776
|
+
color = asArray(color);
|
|
1777
|
+
}
|
|
1778
|
+
if (color === null) {
|
|
1779
|
+
opacity = 1;
|
|
1780
|
+
} else if (Array.isArray(color)) {
|
|
1781
|
+
opacity = color.length === 4 ? color[3] : 1;
|
|
1782
|
+
}
|
|
1783
|
+
if (opacity === 0) {
|
|
1784
|
+
context = createCanvasContext2D(renderOptions.size, renderOptions.size);
|
|
1785
|
+
this.drawHitDetectionCanvas_(renderOptions, context);
|
|
1786
|
+
}
|
|
1787
|
+
}
|
|
1788
|
+
return context ? context.canvas : this.getImage(1);
|
|
1789
|
+
}
|
|
1790
|
+
/**
|
|
1791
|
+
* @private
|
|
1792
|
+
* @param {CanvasRenderingContext2D|OffscreenCanvasRenderingContext2D} context The context to draw in.
|
|
1793
|
+
*/
|
|
1794
|
+
createPath_(context) {
|
|
1795
|
+
let points = this.points_;
|
|
1796
|
+
const radius = this.radius;
|
|
1797
|
+
if (points === Infinity) {
|
|
1798
|
+
context.arc(0, 0, radius, 0, 2 * Math.PI);
|
|
1799
|
+
} else {
|
|
1800
|
+
const radius2 = this.radius2_ === void 0 ? radius : this.radius2_;
|
|
1801
|
+
if (this.radius2_ !== void 0) {
|
|
1802
|
+
points *= 2;
|
|
1803
|
+
}
|
|
1804
|
+
const startAngle = this.angle_ - Math.PI / 2;
|
|
1805
|
+
const step = 2 * Math.PI / points;
|
|
1806
|
+
for (let i = 0; i < points; i++) {
|
|
1807
|
+
const angle0 = startAngle + i * step;
|
|
1808
|
+
const radiusC = i % 2 === 0 ? radius : radius2;
|
|
1809
|
+
context.lineTo(radiusC * Math.cos(angle0), radiusC * Math.sin(angle0));
|
|
1810
|
+
}
|
|
1811
|
+
context.closePath();
|
|
1812
|
+
}
|
|
1813
|
+
}
|
|
1814
|
+
/**
|
|
1815
|
+
* @private
|
|
1816
|
+
* @param {RenderOptions} renderOptions Render options.
|
|
1817
|
+
* @param {CanvasRenderingContext2D|OffscreenCanvasRenderingContext2D} context The context.
|
|
1818
|
+
*/
|
|
1819
|
+
drawHitDetectionCanvas_(renderOptions, context) {
|
|
1820
|
+
context.translate(renderOptions.size / 2, renderOptions.size / 2);
|
|
1821
|
+
this.createPath_(context);
|
|
1822
|
+
context.fillStyle = defaultFillStyle;
|
|
1823
|
+
context.fill();
|
|
1824
|
+
if (renderOptions.strokeStyle) {
|
|
1825
|
+
context.strokeStyle = renderOptions.strokeStyle;
|
|
1826
|
+
context.lineWidth = renderOptions.strokeWidth;
|
|
1827
|
+
if (renderOptions.lineDash) {
|
|
1828
|
+
context.setLineDash(renderOptions.lineDash);
|
|
1829
|
+
context.lineDashOffset = renderOptions.lineDashOffset;
|
|
1830
|
+
}
|
|
1831
|
+
context.lineJoin = renderOptions.lineJoin;
|
|
1832
|
+
context.miterLimit = renderOptions.miterLimit;
|
|
1833
|
+
context.stroke();
|
|
1834
|
+
}
|
|
1835
|
+
}
|
|
1836
|
+
/**
|
|
1837
|
+
* @override
|
|
1838
|
+
*/
|
|
1839
|
+
ready() {
|
|
1840
|
+
return this.fill_ ? this.fill_.ready() : Promise.resolve();
|
|
1841
|
+
}
|
|
1842
|
+
}
|
|
1843
|
+
class CircleStyle extends RegularShape {
|
|
1844
|
+
/**
|
|
1845
|
+
* @param {Options} [options] Options.
|
|
1846
|
+
*/
|
|
1847
|
+
constructor(options) {
|
|
1848
|
+
options = options ? options : { radius: 5 };
|
|
1849
|
+
super({
|
|
1850
|
+
points: Infinity,
|
|
1851
|
+
fill: options.fill,
|
|
1852
|
+
radius: options.radius,
|
|
1853
|
+
stroke: options.stroke,
|
|
1854
|
+
scale: options.scale !== void 0 ? options.scale : 1,
|
|
1855
|
+
rotation: options.rotation !== void 0 ? options.rotation : 0,
|
|
1856
|
+
rotateWithView: options.rotateWithView !== void 0 ? options.rotateWithView : false,
|
|
1857
|
+
displacement: options.displacement !== void 0 ? options.displacement : [0, 0],
|
|
1858
|
+
declutterMode: options.declutterMode
|
|
1859
|
+
});
|
|
1860
|
+
}
|
|
1861
|
+
/**
|
|
1862
|
+
* Clones the style.
|
|
1863
|
+
* @return {CircleStyle} The cloned style.
|
|
1864
|
+
* @api
|
|
1865
|
+
* @override
|
|
1866
|
+
*/
|
|
1867
|
+
clone() {
|
|
1868
|
+
const scale2 = this.getScale();
|
|
1869
|
+
const style = new CircleStyle({
|
|
1870
|
+
fill: this.getFill() ? this.getFill().clone() : void 0,
|
|
1871
|
+
stroke: this.getStroke() ? this.getStroke().clone() : void 0,
|
|
1872
|
+
radius: this.getRadius(),
|
|
1873
|
+
scale: Array.isArray(scale2) ? scale2.slice() : scale2,
|
|
1874
|
+
rotation: this.getRotation(),
|
|
1875
|
+
rotateWithView: this.getRotateWithView(),
|
|
1876
|
+
displacement: this.getDisplacement().slice(),
|
|
1877
|
+
declutterMode: this.getDeclutterMode()
|
|
1878
|
+
});
|
|
1879
|
+
style.setOpacity(this.getOpacity());
|
|
1880
|
+
return style;
|
|
1881
|
+
}
|
|
1882
|
+
/**
|
|
1883
|
+
* Set the circle radius.
|
|
1884
|
+
*
|
|
1885
|
+
* @param {number} radius Circle radius.
|
|
1886
|
+
* @api
|
|
1887
|
+
*/
|
|
1888
|
+
setRadius(radius) {
|
|
1889
|
+
this.radius = radius;
|
|
1890
|
+
this.render();
|
|
1891
|
+
}
|
|
1892
|
+
}
|
|
1893
|
+
class Fill {
|
|
1894
|
+
/**
|
|
1895
|
+
* @param {Options} [options] Options.
|
|
1896
|
+
*/
|
|
1897
|
+
constructor(options) {
|
|
1898
|
+
options = options || {};
|
|
1899
|
+
this.patternImage_ = null;
|
|
1900
|
+
this.color_ = null;
|
|
1901
|
+
if (options.color !== void 0) {
|
|
1902
|
+
this.setColor(options.color);
|
|
1903
|
+
}
|
|
1904
|
+
}
|
|
1905
|
+
/**
|
|
1906
|
+
* Clones the style. The color is not cloned if it is a {@link module:ol/colorlike~ColorLike}.
|
|
1907
|
+
* @return {Fill} The cloned style.
|
|
1908
|
+
* @api
|
|
1909
|
+
*/
|
|
1910
|
+
clone() {
|
|
1911
|
+
const color = this.getColor();
|
|
1912
|
+
return new Fill({
|
|
1913
|
+
color: Array.isArray(color) ? color.slice() : color || void 0
|
|
1914
|
+
});
|
|
1915
|
+
}
|
|
1916
|
+
/**
|
|
1917
|
+
* Get the fill color.
|
|
1918
|
+
* @return {import("../color.js").Color|import("../colorlike.js").ColorLike|import('../colorlike.js').PatternDescriptor|null} Color.
|
|
1919
|
+
* @api
|
|
1920
|
+
*/
|
|
1921
|
+
getColor() {
|
|
1922
|
+
return this.color_;
|
|
1923
|
+
}
|
|
1924
|
+
/**
|
|
1925
|
+
* Set the color.
|
|
1926
|
+
*
|
|
1927
|
+
* @param {import("../color.js").Color|import("../colorlike.js").ColorLike|import('../colorlike.js').PatternDescriptor|null} color Color.
|
|
1928
|
+
* @api
|
|
1929
|
+
*/
|
|
1930
|
+
setColor(color) {
|
|
1931
|
+
if (color !== null && typeof color === "object" && "src" in color) {
|
|
1932
|
+
const patternImage = get(
|
|
1933
|
+
null,
|
|
1934
|
+
color.src,
|
|
1935
|
+
"anonymous",
|
|
1936
|
+
void 0,
|
|
1937
|
+
color.offset ? null : color.color ? color.color : null,
|
|
1938
|
+
!(color.offset && color.size)
|
|
1939
|
+
);
|
|
1940
|
+
patternImage.ready().then(() => {
|
|
1941
|
+
this.patternImage_ = null;
|
|
1942
|
+
});
|
|
1943
|
+
if (patternImage.getImageState() === ImageState.IDLE) {
|
|
1944
|
+
patternImage.load();
|
|
1945
|
+
}
|
|
1946
|
+
if (patternImage.getImageState() === ImageState.LOADING) {
|
|
1947
|
+
this.patternImage_ = patternImage;
|
|
1948
|
+
}
|
|
1949
|
+
}
|
|
1950
|
+
this.color_ = color;
|
|
1951
|
+
}
|
|
1952
|
+
/**
|
|
1953
|
+
* @return {string} Key of the fill for cache lookup.
|
|
1954
|
+
*/
|
|
1955
|
+
getKey() {
|
|
1956
|
+
const fill = this.getColor();
|
|
1957
|
+
if (!fill) {
|
|
1958
|
+
return "";
|
|
1959
|
+
}
|
|
1960
|
+
return fill instanceof CanvasPattern || fill instanceof CanvasGradient ? getUid(fill) : typeof fill === "object" && "src" in fill ? fill.src + ":" + fill.offset : asArray(fill).toString();
|
|
1961
|
+
}
|
|
1962
|
+
/**
|
|
1963
|
+
* @return {boolean} The fill style is loading an image pattern.
|
|
1964
|
+
*/
|
|
1965
|
+
loading() {
|
|
1966
|
+
return !!this.patternImage_;
|
|
1967
|
+
}
|
|
1968
|
+
/**
|
|
1969
|
+
* @return {Promise<void>} `false` or a promise that resolves when the style is ready to use.
|
|
1970
|
+
*/
|
|
1971
|
+
ready() {
|
|
1972
|
+
return this.patternImage_ ? this.patternImage_.ready() : Promise.resolve();
|
|
1973
|
+
}
|
|
1974
|
+
}
|
|
1975
|
+
function calculateScale(width, height, wantedWidth, wantedHeight) {
|
|
1976
|
+
if (wantedWidth !== void 0 && wantedHeight !== void 0) {
|
|
1977
|
+
return [wantedWidth / width, wantedHeight / height];
|
|
1978
|
+
}
|
|
1979
|
+
if (wantedWidth !== void 0) {
|
|
1980
|
+
return wantedWidth / width;
|
|
1981
|
+
}
|
|
1982
|
+
if (wantedHeight !== void 0) {
|
|
1983
|
+
return wantedHeight / height;
|
|
1984
|
+
}
|
|
1985
|
+
return 1;
|
|
1986
|
+
}
|
|
1987
|
+
class Icon extends ImageStyle {
|
|
1988
|
+
/**
|
|
1989
|
+
* @param {Options} [options] Options.
|
|
1990
|
+
*/
|
|
1991
|
+
constructor(options) {
|
|
1992
|
+
options = options || {};
|
|
1993
|
+
const opacity = options.opacity !== void 0 ? options.opacity : 1;
|
|
1994
|
+
const rotation = options.rotation !== void 0 ? options.rotation : 0;
|
|
1995
|
+
const scale2 = options.scale !== void 0 ? options.scale : 1;
|
|
1996
|
+
const rotateWithView = options.rotateWithView !== void 0 ? options.rotateWithView : false;
|
|
1997
|
+
super({
|
|
1998
|
+
opacity,
|
|
1999
|
+
rotation,
|
|
2000
|
+
scale: scale2,
|
|
2001
|
+
displacement: options.displacement !== void 0 ? options.displacement : [0, 0],
|
|
2002
|
+
rotateWithView,
|
|
2003
|
+
declutterMode: options.declutterMode
|
|
2004
|
+
});
|
|
2005
|
+
this.anchor_ = options.anchor !== void 0 ? options.anchor : [0.5, 0.5];
|
|
2006
|
+
this.normalizedAnchor_ = null;
|
|
2007
|
+
this.anchorOrigin_ = options.anchorOrigin !== void 0 ? options.anchorOrigin : "top-left";
|
|
2008
|
+
this.anchorXUnits_ = options.anchorXUnits !== void 0 ? options.anchorXUnits : "fraction";
|
|
2009
|
+
this.anchorYUnits_ = options.anchorYUnits !== void 0 ? options.anchorYUnits : "fraction";
|
|
2010
|
+
this.crossOrigin_ = options.crossOrigin !== void 0 ? options.crossOrigin : null;
|
|
2011
|
+
const image = options.img !== void 0 ? options.img : null;
|
|
2012
|
+
let cacheKey = options.src;
|
|
2013
|
+
assert(
|
|
2014
|
+
!(cacheKey !== void 0 && image),
|
|
2015
|
+
"`image` and `src` cannot be provided at the same time"
|
|
2016
|
+
);
|
|
2017
|
+
if ((cacheKey === void 0 || cacheKey.length === 0) && image) {
|
|
2018
|
+
cacheKey = /** @type {HTMLImageElement} */
|
|
2019
|
+
image.src || getUid(image);
|
|
2020
|
+
}
|
|
2021
|
+
assert(
|
|
2022
|
+
cacheKey !== void 0 && cacheKey.length > 0,
|
|
2023
|
+
"A defined and non-empty `src` or `image` must be provided"
|
|
2024
|
+
);
|
|
2025
|
+
assert(
|
|
2026
|
+
!((options.width !== void 0 || options.height !== void 0) && options.scale !== void 0),
|
|
2027
|
+
"`width` or `height` cannot be provided together with `scale`"
|
|
2028
|
+
);
|
|
2029
|
+
let imageState;
|
|
2030
|
+
if (options.src !== void 0) {
|
|
2031
|
+
imageState = ImageState.IDLE;
|
|
2032
|
+
} else if (image !== void 0) {
|
|
2033
|
+
if ("complete" in image) {
|
|
2034
|
+
if (image.complete) {
|
|
2035
|
+
imageState = image.src ? ImageState.LOADED : ImageState.IDLE;
|
|
2036
|
+
} else {
|
|
2037
|
+
imageState = ImageState.LOADING;
|
|
2038
|
+
}
|
|
2039
|
+
} else {
|
|
2040
|
+
imageState = ImageState.LOADED;
|
|
2041
|
+
}
|
|
2042
|
+
}
|
|
2043
|
+
this.color_ = options.color !== void 0 ? asArray(options.color) : null;
|
|
2044
|
+
this.iconImage_ = get(
|
|
2045
|
+
image,
|
|
2046
|
+
/** @type {string} */
|
|
2047
|
+
cacheKey,
|
|
2048
|
+
this.crossOrigin_,
|
|
2049
|
+
imageState,
|
|
2050
|
+
this.color_
|
|
2051
|
+
);
|
|
2052
|
+
this.offset_ = options.offset !== void 0 ? options.offset : [0, 0];
|
|
2053
|
+
this.offsetOrigin_ = options.offsetOrigin !== void 0 ? options.offsetOrigin : "top-left";
|
|
2054
|
+
this.origin_ = null;
|
|
2055
|
+
this.size_ = options.size !== void 0 ? options.size : null;
|
|
2056
|
+
this.initialOptions_;
|
|
2057
|
+
if (options.width !== void 0 || options.height !== void 0) {
|
|
2058
|
+
let width, height;
|
|
2059
|
+
if (options.size) {
|
|
2060
|
+
[width, height] = options.size;
|
|
2061
|
+
} else {
|
|
2062
|
+
const image2 = this.getImage(1);
|
|
2063
|
+
if (image2.width && image2.height) {
|
|
2064
|
+
width = image2.width;
|
|
2065
|
+
height = image2.height;
|
|
2066
|
+
} else if (image2 instanceof HTMLImageElement) {
|
|
2067
|
+
this.initialOptions_ = options;
|
|
2068
|
+
const onload = () => {
|
|
2069
|
+
this.unlistenImageChange(onload);
|
|
2070
|
+
if (!this.initialOptions_) {
|
|
2071
|
+
return;
|
|
2072
|
+
}
|
|
2073
|
+
const imageSize = this.iconImage_.getSize();
|
|
2074
|
+
this.setScale(
|
|
2075
|
+
calculateScale(
|
|
2076
|
+
imageSize[0],
|
|
2077
|
+
imageSize[1],
|
|
2078
|
+
options.width,
|
|
2079
|
+
options.height
|
|
2080
|
+
)
|
|
2081
|
+
);
|
|
2082
|
+
};
|
|
2083
|
+
this.listenImageChange(onload);
|
|
2084
|
+
return;
|
|
2085
|
+
}
|
|
2086
|
+
}
|
|
2087
|
+
if (width !== void 0) {
|
|
2088
|
+
this.setScale(
|
|
2089
|
+
calculateScale(width, height, options.width, options.height)
|
|
2090
|
+
);
|
|
2091
|
+
}
|
|
2092
|
+
}
|
|
2093
|
+
}
|
|
2094
|
+
/**
|
|
2095
|
+
* Clones the style. The underlying Image/HTMLCanvasElement is not cloned.
|
|
2096
|
+
* @return {Icon} The cloned style.
|
|
2097
|
+
* @api
|
|
2098
|
+
* @override
|
|
2099
|
+
*/
|
|
2100
|
+
clone() {
|
|
2101
|
+
let scale2, width, height;
|
|
2102
|
+
if (this.initialOptions_) {
|
|
2103
|
+
width = this.initialOptions_.width;
|
|
2104
|
+
height = this.initialOptions_.height;
|
|
2105
|
+
} else {
|
|
2106
|
+
scale2 = this.getScale();
|
|
2107
|
+
scale2 = Array.isArray(scale2) ? scale2.slice() : scale2;
|
|
2108
|
+
}
|
|
2109
|
+
return new Icon({
|
|
2110
|
+
anchor: this.anchor_.slice(),
|
|
2111
|
+
anchorOrigin: this.anchorOrigin_,
|
|
2112
|
+
anchorXUnits: this.anchorXUnits_,
|
|
2113
|
+
anchorYUnits: this.anchorYUnits_,
|
|
2114
|
+
color: this.color_ && this.color_.slice ? this.color_.slice() : this.color_ || void 0,
|
|
2115
|
+
crossOrigin: this.crossOrigin_,
|
|
2116
|
+
offset: this.offset_.slice(),
|
|
2117
|
+
offsetOrigin: this.offsetOrigin_,
|
|
2118
|
+
opacity: this.getOpacity(),
|
|
2119
|
+
rotateWithView: this.getRotateWithView(),
|
|
2120
|
+
rotation: this.getRotation(),
|
|
2121
|
+
scale: scale2,
|
|
2122
|
+
width,
|
|
2123
|
+
height,
|
|
2124
|
+
size: this.size_ !== null ? this.size_.slice() : void 0,
|
|
2125
|
+
src: this.getSrc(),
|
|
2126
|
+
displacement: this.getDisplacement().slice(),
|
|
2127
|
+
declutterMode: this.getDeclutterMode()
|
|
2128
|
+
});
|
|
2129
|
+
}
|
|
2130
|
+
/**
|
|
2131
|
+
* Get the anchor point in pixels. The anchor determines the center point for the
|
|
2132
|
+
* symbolizer.
|
|
2133
|
+
* @return {Array<number>} Anchor.
|
|
2134
|
+
* @api
|
|
2135
|
+
* @override
|
|
2136
|
+
*/
|
|
2137
|
+
getAnchor() {
|
|
2138
|
+
let anchor = this.normalizedAnchor_;
|
|
2139
|
+
if (!anchor) {
|
|
2140
|
+
anchor = this.anchor_;
|
|
2141
|
+
const size = this.getSize();
|
|
2142
|
+
if (this.anchorXUnits_ == "fraction" || this.anchorYUnits_ == "fraction") {
|
|
2143
|
+
if (!size) {
|
|
2144
|
+
return null;
|
|
2145
|
+
}
|
|
2146
|
+
anchor = this.anchor_.slice();
|
|
2147
|
+
if (this.anchorXUnits_ == "fraction") {
|
|
2148
|
+
anchor[0] *= size[0];
|
|
2149
|
+
}
|
|
2150
|
+
if (this.anchorYUnits_ == "fraction") {
|
|
2151
|
+
anchor[1] *= size[1];
|
|
2152
|
+
}
|
|
2153
|
+
}
|
|
2154
|
+
if (this.anchorOrigin_ != "top-left") {
|
|
2155
|
+
if (!size) {
|
|
2156
|
+
return null;
|
|
2157
|
+
}
|
|
2158
|
+
if (anchor === this.anchor_) {
|
|
2159
|
+
anchor = this.anchor_.slice();
|
|
2160
|
+
}
|
|
2161
|
+
if (this.anchorOrigin_ == "top-right" || this.anchorOrigin_ == "bottom-right") {
|
|
2162
|
+
anchor[0] = -anchor[0] + size[0];
|
|
2163
|
+
}
|
|
2164
|
+
if (this.anchorOrigin_ == "bottom-left" || this.anchorOrigin_ == "bottom-right") {
|
|
2165
|
+
anchor[1] = -anchor[1] + size[1];
|
|
2166
|
+
}
|
|
2167
|
+
}
|
|
2168
|
+
this.normalizedAnchor_ = anchor;
|
|
2169
|
+
}
|
|
2170
|
+
const displacement = this.getDisplacement();
|
|
2171
|
+
const scale2 = this.getScaleArray();
|
|
2172
|
+
return [
|
|
2173
|
+
anchor[0] - displacement[0] / scale2[0],
|
|
2174
|
+
anchor[1] + displacement[1] / scale2[1]
|
|
2175
|
+
];
|
|
2176
|
+
}
|
|
2177
|
+
/**
|
|
2178
|
+
* Set the anchor point. The anchor determines the center point for the
|
|
2179
|
+
* symbolizer.
|
|
2180
|
+
*
|
|
2181
|
+
* @param {Array<number>} anchor Anchor.
|
|
2182
|
+
* @api
|
|
2183
|
+
*/
|
|
2184
|
+
setAnchor(anchor) {
|
|
2185
|
+
this.anchor_ = anchor;
|
|
2186
|
+
this.normalizedAnchor_ = null;
|
|
2187
|
+
}
|
|
2188
|
+
/**
|
|
2189
|
+
* Get the icon color.
|
|
2190
|
+
* @return {import("../color.js").Color} Color.
|
|
2191
|
+
* @api
|
|
2192
|
+
*/
|
|
2193
|
+
getColor() {
|
|
2194
|
+
return this.color_;
|
|
2195
|
+
}
|
|
2196
|
+
/**
|
|
2197
|
+
* Get the image icon.
|
|
2198
|
+
* @param {number} pixelRatio Pixel ratio.
|
|
2199
|
+
* @return {HTMLImageElement|HTMLCanvasElement|OffscreenCanvas|ImageBitmap} Image or Canvas element. If the Icon
|
|
2200
|
+
* style was configured with `src` or with a not let loaded `img`, an `ImageBitmap` will be returned.
|
|
2201
|
+
* @api
|
|
2202
|
+
* @override
|
|
2203
|
+
*/
|
|
2204
|
+
getImage(pixelRatio) {
|
|
2205
|
+
return this.iconImage_.getImage(pixelRatio);
|
|
2206
|
+
}
|
|
2207
|
+
/**
|
|
2208
|
+
* Get the pixel ratio.
|
|
2209
|
+
* @param {number} pixelRatio Pixel ratio.
|
|
2210
|
+
* @return {number} The pixel ratio of the image.
|
|
2211
|
+
* @api
|
|
2212
|
+
* @override
|
|
2213
|
+
*/
|
|
2214
|
+
getPixelRatio(pixelRatio) {
|
|
2215
|
+
return this.iconImage_.getPixelRatio(pixelRatio);
|
|
2216
|
+
}
|
|
2217
|
+
/**
|
|
2218
|
+
* @return {import("../size.js").Size} Image size.
|
|
2219
|
+
* @override
|
|
2220
|
+
*/
|
|
2221
|
+
getImageSize() {
|
|
2222
|
+
return this.iconImage_.getSize();
|
|
2223
|
+
}
|
|
2224
|
+
/**
|
|
2225
|
+
* @return {import("../ImageState.js").default} Image state.
|
|
2226
|
+
* @override
|
|
2227
|
+
*/
|
|
2228
|
+
getImageState() {
|
|
2229
|
+
return this.iconImage_.getImageState();
|
|
2230
|
+
}
|
|
2231
|
+
/**
|
|
2232
|
+
* @return {HTMLImageElement|HTMLCanvasElement|OffscreenCanvas|ImageBitmap} Image element.
|
|
2233
|
+
* @override
|
|
2234
|
+
*/
|
|
2235
|
+
getHitDetectionImage() {
|
|
2236
|
+
return this.iconImage_.getHitDetectionImage();
|
|
2237
|
+
}
|
|
2238
|
+
/**
|
|
2239
|
+
* Get the origin of the symbolizer.
|
|
2240
|
+
* @return {Array<number>} Origin.
|
|
2241
|
+
* @api
|
|
2242
|
+
* @override
|
|
2243
|
+
*/
|
|
2244
|
+
getOrigin() {
|
|
2245
|
+
if (this.origin_) {
|
|
2246
|
+
return this.origin_;
|
|
2247
|
+
}
|
|
2248
|
+
let offset = this.offset_;
|
|
2249
|
+
if (this.offsetOrigin_ != "top-left") {
|
|
2250
|
+
const size = this.getSize();
|
|
2251
|
+
const iconImageSize = this.iconImage_.getSize();
|
|
2252
|
+
if (!size || !iconImageSize) {
|
|
2253
|
+
return null;
|
|
2254
|
+
}
|
|
2255
|
+
offset = offset.slice();
|
|
2256
|
+
if (this.offsetOrigin_ == "top-right" || this.offsetOrigin_ == "bottom-right") {
|
|
2257
|
+
offset[0] = iconImageSize[0] - size[0] - offset[0];
|
|
2258
|
+
}
|
|
2259
|
+
if (this.offsetOrigin_ == "bottom-left" || this.offsetOrigin_ == "bottom-right") {
|
|
2260
|
+
offset[1] = iconImageSize[1] - size[1] - offset[1];
|
|
2261
|
+
}
|
|
2262
|
+
}
|
|
2263
|
+
this.origin_ = offset;
|
|
2264
|
+
return this.origin_;
|
|
2265
|
+
}
|
|
2266
|
+
/**
|
|
2267
|
+
* Get the image URL.
|
|
2268
|
+
* @return {string|undefined} Image src.
|
|
2269
|
+
* @api
|
|
2270
|
+
*/
|
|
2271
|
+
getSrc() {
|
|
2272
|
+
return this.iconImage_.getSrc();
|
|
2273
|
+
}
|
|
2274
|
+
/**
|
|
2275
|
+
* Set the image URI
|
|
2276
|
+
* @param {string} src Image source URI
|
|
2277
|
+
* @api
|
|
2278
|
+
*/
|
|
2279
|
+
setSrc(src) {
|
|
2280
|
+
this.iconImage_ = get(
|
|
2281
|
+
null,
|
|
2282
|
+
src,
|
|
2283
|
+
this.crossOrigin_,
|
|
2284
|
+
ImageState.IDLE,
|
|
2285
|
+
this.color_
|
|
2286
|
+
);
|
|
2287
|
+
}
|
|
2288
|
+
/**
|
|
2289
|
+
* Get the size of the icon (in pixels).
|
|
2290
|
+
* @return {import("../size.js").Size} Image size.
|
|
2291
|
+
* @api
|
|
2292
|
+
* @override
|
|
2293
|
+
*/
|
|
2294
|
+
getSize() {
|
|
2295
|
+
return !this.size_ ? this.iconImage_.getSize() : this.size_;
|
|
2296
|
+
}
|
|
2297
|
+
/**
|
|
2298
|
+
* Get the width of the icon (in pixels). Will return undefined when the icon image is not yet loaded.
|
|
2299
|
+
* @return {number} Icon width (in pixels).
|
|
2300
|
+
* @api
|
|
2301
|
+
*/
|
|
2302
|
+
getWidth() {
|
|
2303
|
+
const scale2 = this.getScaleArray();
|
|
2304
|
+
if (this.size_) {
|
|
2305
|
+
return this.size_[0] * scale2[0];
|
|
2306
|
+
}
|
|
2307
|
+
if (this.iconImage_.getImageState() == ImageState.LOADED) {
|
|
2308
|
+
return this.iconImage_.getSize()[0] * scale2[0];
|
|
2309
|
+
}
|
|
2310
|
+
return void 0;
|
|
2311
|
+
}
|
|
2312
|
+
/**
|
|
2313
|
+
* Get the height of the icon (in pixels). Will return undefined when the icon image is not yet loaded.
|
|
2314
|
+
* @return {number} Icon height (in pixels).
|
|
2315
|
+
* @api
|
|
2316
|
+
*/
|
|
2317
|
+
getHeight() {
|
|
2318
|
+
const scale2 = this.getScaleArray();
|
|
2319
|
+
if (this.size_) {
|
|
2320
|
+
return this.size_[1] * scale2[1];
|
|
2321
|
+
}
|
|
2322
|
+
if (this.iconImage_.getImageState() == ImageState.LOADED) {
|
|
2323
|
+
return this.iconImage_.getSize()[1] * scale2[1];
|
|
2324
|
+
}
|
|
2325
|
+
return void 0;
|
|
2326
|
+
}
|
|
2327
|
+
/**
|
|
2328
|
+
* Set the scale.
|
|
2329
|
+
*
|
|
2330
|
+
* @param {number|import("../size.js").Size} scale Scale.
|
|
2331
|
+
* @api
|
|
2332
|
+
* @override
|
|
2333
|
+
*/
|
|
2334
|
+
setScale(scale2) {
|
|
2335
|
+
delete this.initialOptions_;
|
|
2336
|
+
super.setScale(scale2);
|
|
2337
|
+
}
|
|
2338
|
+
/**
|
|
2339
|
+
* @param {function(import("../events/Event.js").default): void} listener Listener function.
|
|
2340
|
+
* @override
|
|
2341
|
+
*/
|
|
2342
|
+
listenImageChange(listener) {
|
|
2343
|
+
this.iconImage_.addEventListener(EventType.CHANGE, listener);
|
|
2344
|
+
}
|
|
2345
|
+
/**
|
|
2346
|
+
* Load not yet loaded URI.
|
|
2347
|
+
* When rendering a feature with an icon style, the vector renderer will
|
|
2348
|
+
* automatically call this method. However, you might want to call this
|
|
2349
|
+
* method yourself for preloading or other purposes.
|
|
2350
|
+
* @api
|
|
2351
|
+
* @override
|
|
2352
|
+
*/
|
|
2353
|
+
load() {
|
|
2354
|
+
this.iconImage_.load();
|
|
2355
|
+
}
|
|
2356
|
+
/**
|
|
2357
|
+
* @param {function(import("../events/Event.js").default): void} listener Listener function.
|
|
2358
|
+
* @override
|
|
2359
|
+
*/
|
|
2360
|
+
unlistenImageChange(listener) {
|
|
2361
|
+
this.iconImage_.removeEventListener(EventType.CHANGE, listener);
|
|
2362
|
+
}
|
|
2363
|
+
/**
|
|
2364
|
+
* @override
|
|
2365
|
+
*/
|
|
2366
|
+
ready() {
|
|
2367
|
+
return this.iconImage_.ready();
|
|
2368
|
+
}
|
|
2369
|
+
}
|
|
2370
|
+
class Stroke {
|
|
2371
|
+
/**
|
|
2372
|
+
* @param {Options} [options] Options.
|
|
2373
|
+
*/
|
|
2374
|
+
constructor(options) {
|
|
2375
|
+
options = options || {};
|
|
2376
|
+
this.color_ = options.color !== void 0 ? options.color : null;
|
|
2377
|
+
this.lineCap_ = options.lineCap;
|
|
2378
|
+
this.lineDash_ = options.lineDash !== void 0 ? options.lineDash : null;
|
|
2379
|
+
this.lineDashOffset_ = options.lineDashOffset;
|
|
2380
|
+
this.lineJoin_ = options.lineJoin;
|
|
2381
|
+
this.miterLimit_ = options.miterLimit;
|
|
2382
|
+
this.width_ = options.width;
|
|
2383
|
+
}
|
|
2384
|
+
/**
|
|
2385
|
+
* Clones the style.
|
|
2386
|
+
* @return {Stroke} The cloned style.
|
|
2387
|
+
* @api
|
|
2388
|
+
*/
|
|
2389
|
+
clone() {
|
|
2390
|
+
const color = this.getColor();
|
|
2391
|
+
return new Stroke({
|
|
2392
|
+
color: Array.isArray(color) ? color.slice() : color || void 0,
|
|
2393
|
+
lineCap: this.getLineCap(),
|
|
2394
|
+
lineDash: this.getLineDash() ? this.getLineDash().slice() : void 0,
|
|
2395
|
+
lineDashOffset: this.getLineDashOffset(),
|
|
2396
|
+
lineJoin: this.getLineJoin(),
|
|
2397
|
+
miterLimit: this.getMiterLimit(),
|
|
2398
|
+
width: this.getWidth()
|
|
2399
|
+
});
|
|
2400
|
+
}
|
|
2401
|
+
/**
|
|
2402
|
+
* Get the stroke color.
|
|
2403
|
+
* @return {import("../color.js").Color|import("../colorlike.js").ColorLike} Color.
|
|
2404
|
+
* @api
|
|
2405
|
+
*/
|
|
2406
|
+
getColor() {
|
|
2407
|
+
return this.color_;
|
|
2408
|
+
}
|
|
2409
|
+
/**
|
|
2410
|
+
* Get the line cap type for the stroke.
|
|
2411
|
+
* @return {CanvasLineCap|undefined} Line cap.
|
|
2412
|
+
* @api
|
|
2413
|
+
*/
|
|
2414
|
+
getLineCap() {
|
|
2415
|
+
return this.lineCap_;
|
|
2416
|
+
}
|
|
2417
|
+
/**
|
|
2418
|
+
* Get the line dash style for the stroke.
|
|
2419
|
+
* @return {Array<number>|null} Line dash.
|
|
2420
|
+
* @api
|
|
2421
|
+
*/
|
|
2422
|
+
getLineDash() {
|
|
2423
|
+
return this.lineDash_;
|
|
2424
|
+
}
|
|
2425
|
+
/**
|
|
2426
|
+
* Get the line dash offset for the stroke.
|
|
2427
|
+
* @return {number|undefined} Line dash offset.
|
|
2428
|
+
* @api
|
|
2429
|
+
*/
|
|
2430
|
+
getLineDashOffset() {
|
|
2431
|
+
return this.lineDashOffset_;
|
|
2432
|
+
}
|
|
2433
|
+
/**
|
|
2434
|
+
* Get the line join type for the stroke.
|
|
2435
|
+
* @return {CanvasLineJoin|undefined} Line join.
|
|
2436
|
+
* @api
|
|
2437
|
+
*/
|
|
2438
|
+
getLineJoin() {
|
|
2439
|
+
return this.lineJoin_;
|
|
2440
|
+
}
|
|
2441
|
+
/**
|
|
2442
|
+
* Get the miter limit for the stroke.
|
|
2443
|
+
* @return {number|undefined} Miter limit.
|
|
2444
|
+
* @api
|
|
2445
|
+
*/
|
|
2446
|
+
getMiterLimit() {
|
|
2447
|
+
return this.miterLimit_;
|
|
2448
|
+
}
|
|
2449
|
+
/**
|
|
2450
|
+
* Get the stroke width.
|
|
2451
|
+
* @return {number|undefined} Width.
|
|
2452
|
+
* @api
|
|
2453
|
+
*/
|
|
2454
|
+
getWidth() {
|
|
2455
|
+
return this.width_;
|
|
2456
|
+
}
|
|
2457
|
+
/**
|
|
2458
|
+
* Set the color.
|
|
2459
|
+
*
|
|
2460
|
+
* @param {import("../color.js").Color|import("../colorlike.js").ColorLike} color Color.
|
|
2461
|
+
* @api
|
|
2462
|
+
*/
|
|
2463
|
+
setColor(color) {
|
|
2464
|
+
this.color_ = color;
|
|
2465
|
+
}
|
|
2466
|
+
/**
|
|
2467
|
+
* Set the line cap.
|
|
2468
|
+
*
|
|
2469
|
+
* @param {CanvasLineCap|undefined} lineCap Line cap.
|
|
2470
|
+
* @api
|
|
2471
|
+
*/
|
|
2472
|
+
setLineCap(lineCap) {
|
|
2473
|
+
this.lineCap_ = lineCap;
|
|
2474
|
+
}
|
|
2475
|
+
/**
|
|
2476
|
+
* Set the line dash.
|
|
2477
|
+
*
|
|
2478
|
+
* @param {Array<number>|null} lineDash Line dash.
|
|
2479
|
+
* @api
|
|
2480
|
+
*/
|
|
2481
|
+
setLineDash(lineDash) {
|
|
2482
|
+
this.lineDash_ = lineDash;
|
|
2483
|
+
}
|
|
2484
|
+
/**
|
|
2485
|
+
* Set the line dash offset.
|
|
2486
|
+
*
|
|
2487
|
+
* @param {number|undefined} lineDashOffset Line dash offset.
|
|
2488
|
+
* @api
|
|
2489
|
+
*/
|
|
2490
|
+
setLineDashOffset(lineDashOffset) {
|
|
2491
|
+
this.lineDashOffset_ = lineDashOffset;
|
|
2492
|
+
}
|
|
2493
|
+
/**
|
|
2494
|
+
* Set the line join.
|
|
2495
|
+
*
|
|
2496
|
+
* @param {CanvasLineJoin|undefined} lineJoin Line join.
|
|
2497
|
+
* @api
|
|
2498
|
+
*/
|
|
2499
|
+
setLineJoin(lineJoin) {
|
|
2500
|
+
this.lineJoin_ = lineJoin;
|
|
2501
|
+
}
|
|
2502
|
+
/**
|
|
2503
|
+
* Set the miter limit.
|
|
2504
|
+
*
|
|
2505
|
+
* @param {number|undefined} miterLimit Miter limit.
|
|
2506
|
+
* @api
|
|
2507
|
+
*/
|
|
2508
|
+
setMiterLimit(miterLimit) {
|
|
2509
|
+
this.miterLimit_ = miterLimit;
|
|
2510
|
+
}
|
|
2511
|
+
/**
|
|
2512
|
+
* Set the width.
|
|
2513
|
+
*
|
|
2514
|
+
* @param {number|undefined} width Width.
|
|
2515
|
+
* @api
|
|
2516
|
+
*/
|
|
2517
|
+
setWidth(width) {
|
|
2518
|
+
this.width_ = width;
|
|
2519
|
+
}
|
|
2520
|
+
}
|
|
2521
|
+
class Style {
|
|
2522
|
+
/**
|
|
2523
|
+
* @param {Options} [options] Style options.
|
|
2524
|
+
*/
|
|
2525
|
+
constructor(options) {
|
|
2526
|
+
options = options || {};
|
|
2527
|
+
this.geometry_ = null;
|
|
2528
|
+
this.geometryFunction_ = defaultGeometryFunction;
|
|
2529
|
+
if (options.geometry !== void 0) {
|
|
2530
|
+
this.setGeometry(options.geometry);
|
|
2531
|
+
}
|
|
2532
|
+
this.fill_ = options.fill !== void 0 ? options.fill : null;
|
|
2533
|
+
this.image_ = options.image !== void 0 ? options.image : null;
|
|
2534
|
+
this.renderer_ = options.renderer !== void 0 ? options.renderer : null;
|
|
2535
|
+
this.hitDetectionRenderer_ = options.hitDetectionRenderer !== void 0 ? options.hitDetectionRenderer : null;
|
|
2536
|
+
this.stroke_ = options.stroke !== void 0 ? options.stroke : null;
|
|
2537
|
+
this.text_ = options.text !== void 0 ? options.text : null;
|
|
2538
|
+
this.zIndex_ = options.zIndex;
|
|
2539
|
+
}
|
|
2540
|
+
/**
|
|
2541
|
+
* Clones the style.
|
|
2542
|
+
* @return {Style} The cloned style.
|
|
2543
|
+
* @api
|
|
2544
|
+
*/
|
|
2545
|
+
clone() {
|
|
2546
|
+
var _a;
|
|
2547
|
+
let geometry = this.getGeometry();
|
|
2548
|
+
if (geometry && typeof geometry === "object") {
|
|
2549
|
+
geometry = /** @type {import("../geom/Geometry.js").default} */
|
|
2550
|
+
geometry.clone();
|
|
2551
|
+
}
|
|
2552
|
+
return new Style({
|
|
2553
|
+
geometry: geometry != null ? geometry : void 0,
|
|
2554
|
+
fill: this.getFill() ? this.getFill().clone() : void 0,
|
|
2555
|
+
image: this.getImage() ? this.getImage().clone() : void 0,
|
|
2556
|
+
renderer: (_a = this.getRenderer()) != null ? _a : void 0,
|
|
2557
|
+
stroke: this.getStroke() ? this.getStroke().clone() : void 0,
|
|
2558
|
+
text: this.getText() ? this.getText().clone() : void 0,
|
|
2559
|
+
zIndex: this.getZIndex()
|
|
2560
|
+
});
|
|
2561
|
+
}
|
|
2562
|
+
/**
|
|
2563
|
+
* Get the custom renderer function that was configured with
|
|
2564
|
+
* {@link #setRenderer} or the `renderer` constructor option.
|
|
2565
|
+
* @return {RenderFunction|null} Custom renderer function.
|
|
2566
|
+
* @api
|
|
2567
|
+
*/
|
|
2568
|
+
getRenderer() {
|
|
2569
|
+
return this.renderer_;
|
|
2570
|
+
}
|
|
2571
|
+
/**
|
|
2572
|
+
* Sets a custom renderer function for this style. When set, `fill`, `stroke`
|
|
2573
|
+
* and `image` options of the style will be ignored.
|
|
2574
|
+
* @param {RenderFunction|null} renderer Custom renderer function.
|
|
2575
|
+
* @api
|
|
2576
|
+
*/
|
|
2577
|
+
setRenderer(renderer) {
|
|
2578
|
+
this.renderer_ = renderer;
|
|
2579
|
+
}
|
|
2580
|
+
/**
|
|
2581
|
+
* Sets a custom renderer function for this style used
|
|
2582
|
+
* in hit detection.
|
|
2583
|
+
* @param {RenderFunction|null} renderer Custom renderer function.
|
|
2584
|
+
* @api
|
|
2585
|
+
*/
|
|
2586
|
+
setHitDetectionRenderer(renderer) {
|
|
2587
|
+
this.hitDetectionRenderer_ = renderer;
|
|
2588
|
+
}
|
|
2589
|
+
/**
|
|
2590
|
+
* Get the custom renderer function that was configured with
|
|
2591
|
+
* {@link #setHitDetectionRenderer} or the `hitDetectionRenderer` constructor option.
|
|
2592
|
+
* @return {RenderFunction|null} Custom renderer function.
|
|
2593
|
+
* @api
|
|
2594
|
+
*/
|
|
2595
|
+
getHitDetectionRenderer() {
|
|
2596
|
+
return this.hitDetectionRenderer_;
|
|
2597
|
+
}
|
|
2598
|
+
/**
|
|
2599
|
+
* Get the geometry to be rendered.
|
|
2600
|
+
* @return {string|import("../geom/Geometry.js").default|GeometryFunction|null}
|
|
2601
|
+
* Feature property or geometry or function that returns the geometry that will
|
|
2602
|
+
* be rendered with this style.
|
|
2603
|
+
* @api
|
|
2604
|
+
*/
|
|
2605
|
+
getGeometry() {
|
|
2606
|
+
return this.geometry_;
|
|
2607
|
+
}
|
|
2608
|
+
/**
|
|
2609
|
+
* Get the function used to generate a geometry for rendering.
|
|
2610
|
+
* @return {!GeometryFunction} Function that is called with a feature
|
|
2611
|
+
* and returns the geometry to render instead of the feature's geometry.
|
|
2612
|
+
* @api
|
|
2613
|
+
*/
|
|
2614
|
+
getGeometryFunction() {
|
|
2615
|
+
return this.geometryFunction_;
|
|
2616
|
+
}
|
|
2617
|
+
/**
|
|
2618
|
+
* Get the fill style.
|
|
2619
|
+
* @return {import("./Fill.js").default|null} Fill style.
|
|
2620
|
+
* @api
|
|
2621
|
+
*/
|
|
2622
|
+
getFill() {
|
|
2623
|
+
return this.fill_;
|
|
2624
|
+
}
|
|
2625
|
+
/**
|
|
2626
|
+
* Set the fill style.
|
|
2627
|
+
* @param {import("./Fill.js").default|null} fill Fill style.
|
|
2628
|
+
* @api
|
|
2629
|
+
*/
|
|
2630
|
+
setFill(fill) {
|
|
2631
|
+
this.fill_ = fill;
|
|
2632
|
+
}
|
|
2633
|
+
/**
|
|
2634
|
+
* Get the image style.
|
|
2635
|
+
* @return {import("./Image.js").default|null} Image style.
|
|
2636
|
+
* @api
|
|
2637
|
+
*/
|
|
2638
|
+
getImage() {
|
|
2639
|
+
return this.image_;
|
|
2640
|
+
}
|
|
2641
|
+
/**
|
|
2642
|
+
* Set the image style.
|
|
2643
|
+
* @param {import("./Image.js").default} image Image style.
|
|
2644
|
+
* @api
|
|
2645
|
+
*/
|
|
2646
|
+
setImage(image) {
|
|
2647
|
+
this.image_ = image;
|
|
2648
|
+
}
|
|
2649
|
+
/**
|
|
2650
|
+
* Get the stroke style.
|
|
2651
|
+
* @return {import("./Stroke.js").default|null} Stroke style.
|
|
2652
|
+
* @api
|
|
2653
|
+
*/
|
|
2654
|
+
getStroke() {
|
|
2655
|
+
return this.stroke_;
|
|
2656
|
+
}
|
|
2657
|
+
/**
|
|
2658
|
+
* Set the stroke style.
|
|
2659
|
+
* @param {import("./Stroke.js").default|null} stroke Stroke style.
|
|
2660
|
+
* @api
|
|
2661
|
+
*/
|
|
2662
|
+
setStroke(stroke) {
|
|
2663
|
+
this.stroke_ = stroke;
|
|
2664
|
+
}
|
|
2665
|
+
/**
|
|
2666
|
+
* Get the text style.
|
|
2667
|
+
* @return {import("./Text.js").default|null} Text style.
|
|
2668
|
+
* @api
|
|
2669
|
+
*/
|
|
2670
|
+
getText() {
|
|
2671
|
+
return this.text_;
|
|
2672
|
+
}
|
|
2673
|
+
/**
|
|
2674
|
+
* Set the text style.
|
|
2675
|
+
* @param {import("./Text.js").default} text Text style.
|
|
2676
|
+
* @api
|
|
2677
|
+
*/
|
|
2678
|
+
setText(text) {
|
|
2679
|
+
this.text_ = text;
|
|
2680
|
+
}
|
|
2681
|
+
/**
|
|
2682
|
+
* Get the z-index for the style.
|
|
2683
|
+
* @return {number|undefined} ZIndex.
|
|
2684
|
+
* @api
|
|
2685
|
+
*/
|
|
2686
|
+
getZIndex() {
|
|
2687
|
+
return this.zIndex_;
|
|
2688
|
+
}
|
|
2689
|
+
/**
|
|
2690
|
+
* Set a geometry that is rendered instead of the feature's geometry.
|
|
2691
|
+
*
|
|
2692
|
+
* @param {string|import("../geom/Geometry.js").default|GeometryFunction|null} geometry
|
|
2693
|
+
* Feature property or geometry or function returning a geometry to render
|
|
2694
|
+
* for this style.
|
|
2695
|
+
* @api
|
|
2696
|
+
*/
|
|
2697
|
+
setGeometry(geometry) {
|
|
2698
|
+
if (typeof geometry === "function") {
|
|
2699
|
+
this.geometryFunction_ = geometry;
|
|
2700
|
+
} else if (typeof geometry === "string") {
|
|
2701
|
+
this.geometryFunction_ = function(feature) {
|
|
2702
|
+
return (
|
|
2703
|
+
/** @type {import("../geom/Geometry.js").default} */
|
|
2704
|
+
feature.get(geometry)
|
|
2705
|
+
);
|
|
2706
|
+
};
|
|
2707
|
+
} else if (!geometry) {
|
|
2708
|
+
this.geometryFunction_ = defaultGeometryFunction;
|
|
2709
|
+
} else if (geometry !== void 0) {
|
|
2710
|
+
this.geometryFunction_ = function() {
|
|
2711
|
+
return (
|
|
2712
|
+
/** @type {import("../geom/Geometry.js").default} */
|
|
2713
|
+
geometry
|
|
2714
|
+
);
|
|
2715
|
+
};
|
|
2716
|
+
}
|
|
2717
|
+
this.geometry_ = geometry;
|
|
2718
|
+
}
|
|
2719
|
+
/**
|
|
2720
|
+
* Set the z-index.
|
|
2721
|
+
*
|
|
2722
|
+
* @param {number|undefined} zIndex ZIndex.
|
|
2723
|
+
* @api
|
|
2724
|
+
*/
|
|
2725
|
+
setZIndex(zIndex) {
|
|
2726
|
+
this.zIndex_ = zIndex;
|
|
2727
|
+
}
|
|
2728
|
+
}
|
|
2729
|
+
function toFunction(obj) {
|
|
2730
|
+
let styleFunction;
|
|
2731
|
+
if (typeof obj === "function") {
|
|
2732
|
+
styleFunction = obj;
|
|
2733
|
+
} else {
|
|
2734
|
+
let styles;
|
|
2735
|
+
if (Array.isArray(obj)) {
|
|
2736
|
+
styles = obj;
|
|
2737
|
+
} else {
|
|
2738
|
+
assert(
|
|
2739
|
+
typeof /** @type {?} */
|
|
2740
|
+
obj.getZIndex === "function",
|
|
2741
|
+
"Expected an `Style` or an array of `Style`"
|
|
2742
|
+
);
|
|
2743
|
+
const style = (
|
|
2744
|
+
/** @type {Style} */
|
|
2745
|
+
obj
|
|
2746
|
+
);
|
|
2747
|
+
styles = [style];
|
|
2748
|
+
}
|
|
2749
|
+
styleFunction = function() {
|
|
2750
|
+
return styles;
|
|
2751
|
+
};
|
|
2752
|
+
}
|
|
2753
|
+
return styleFunction;
|
|
2754
|
+
}
|
|
2755
|
+
let defaultStyles = null;
|
|
2756
|
+
function createDefaultStyle(feature, resolution) {
|
|
2757
|
+
if (!defaultStyles) {
|
|
2758
|
+
const fill = new Fill({
|
|
2759
|
+
color: "rgba(255,255,255,0.4)"
|
|
2760
|
+
});
|
|
2761
|
+
const stroke = new Stroke({
|
|
2762
|
+
color: "#3399CC",
|
|
2763
|
+
width: 1.25
|
|
2764
|
+
});
|
|
2765
|
+
defaultStyles = [
|
|
2766
|
+
new Style({
|
|
2767
|
+
image: new CircleStyle({
|
|
2768
|
+
fill,
|
|
2769
|
+
stroke,
|
|
2770
|
+
radius: 5
|
|
2771
|
+
}),
|
|
2772
|
+
fill,
|
|
2773
|
+
stroke
|
|
2774
|
+
})
|
|
2775
|
+
];
|
|
2776
|
+
}
|
|
2777
|
+
return defaultStyles;
|
|
2778
|
+
}
|
|
2779
|
+
function defaultGeometryFunction(feature) {
|
|
2780
|
+
return feature.getGeometry();
|
|
2781
|
+
}
|
|
2782
|
+
const DEFAULT_FILL_COLOR = "#333";
|
|
2783
|
+
class Text {
|
|
2784
|
+
/**
|
|
2785
|
+
* @param {Options} [options] Options.
|
|
2786
|
+
*/
|
|
2787
|
+
constructor(options) {
|
|
2788
|
+
options = options || {};
|
|
2789
|
+
this.font_ = options.font;
|
|
2790
|
+
this.rotation_ = options.rotation;
|
|
2791
|
+
this.rotateWithView_ = options.rotateWithView;
|
|
2792
|
+
this.keepUpright_ = options.keepUpright;
|
|
2793
|
+
this.scale_ = options.scale;
|
|
2794
|
+
this.scaleArray_ = toSize(options.scale !== void 0 ? options.scale : 1);
|
|
2795
|
+
this.text_ = options.text;
|
|
2796
|
+
this.textAlign_ = options.textAlign;
|
|
2797
|
+
this.justify_ = options.justify;
|
|
2798
|
+
this.repeat_ = options.repeat;
|
|
2799
|
+
this.textBaseline_ = options.textBaseline;
|
|
2800
|
+
this.fill_ = options.fill !== void 0 ? options.fill : new Fill({ color: DEFAULT_FILL_COLOR });
|
|
2801
|
+
this.maxAngle_ = options.maxAngle !== void 0 ? options.maxAngle : Math.PI / 4;
|
|
2802
|
+
this.placement_ = options.placement !== void 0 ? options.placement : "point";
|
|
2803
|
+
this.overflow_ = !!options.overflow;
|
|
2804
|
+
this.stroke_ = options.stroke !== void 0 ? options.stroke : null;
|
|
2805
|
+
this.offsetX_ = options.offsetX !== void 0 ? options.offsetX : 0;
|
|
2806
|
+
this.offsetY_ = options.offsetY !== void 0 ? options.offsetY : 0;
|
|
2807
|
+
this.backgroundFill_ = options.backgroundFill ? options.backgroundFill : null;
|
|
2808
|
+
this.backgroundStroke_ = options.backgroundStroke ? options.backgroundStroke : null;
|
|
2809
|
+
this.padding_ = options.padding === void 0 ? null : options.padding;
|
|
2810
|
+
this.declutterMode_ = options.declutterMode;
|
|
2811
|
+
}
|
|
2812
|
+
/**
|
|
2813
|
+
* Clones the style.
|
|
2814
|
+
* @return {Text} The cloned style.
|
|
2815
|
+
* @api
|
|
2816
|
+
*/
|
|
2817
|
+
clone() {
|
|
2818
|
+
const scale2 = this.getScale();
|
|
2819
|
+
return new Text({
|
|
2820
|
+
font: this.getFont(),
|
|
2821
|
+
placement: this.getPlacement(),
|
|
2822
|
+
repeat: this.getRepeat(),
|
|
2823
|
+
maxAngle: this.getMaxAngle(),
|
|
2824
|
+
overflow: this.getOverflow(),
|
|
2825
|
+
rotation: this.getRotation(),
|
|
2826
|
+
rotateWithView: this.getRotateWithView(),
|
|
2827
|
+
keepUpright: this.getKeepUpright(),
|
|
2828
|
+
scale: Array.isArray(scale2) ? scale2.slice() : scale2,
|
|
2829
|
+
text: this.getText(),
|
|
2830
|
+
textAlign: this.getTextAlign(),
|
|
2831
|
+
justify: this.getJustify(),
|
|
2832
|
+
textBaseline: this.getTextBaseline(),
|
|
2833
|
+
fill: this.getFill() instanceof Fill ? this.getFill().clone() : this.getFill(),
|
|
2834
|
+
stroke: this.getStroke() ? this.getStroke().clone() : void 0,
|
|
2835
|
+
offsetX: this.getOffsetX(),
|
|
2836
|
+
offsetY: this.getOffsetY(),
|
|
2837
|
+
backgroundFill: this.getBackgroundFill() ? this.getBackgroundFill().clone() : void 0,
|
|
2838
|
+
backgroundStroke: this.getBackgroundStroke() ? this.getBackgroundStroke().clone() : void 0,
|
|
2839
|
+
padding: this.getPadding() || void 0,
|
|
2840
|
+
declutterMode: this.getDeclutterMode()
|
|
2841
|
+
});
|
|
2842
|
+
}
|
|
2843
|
+
/**
|
|
2844
|
+
* Get the `overflow` configuration.
|
|
2845
|
+
* @return {boolean} Let text overflow the length of the path they follow.
|
|
2846
|
+
* @api
|
|
2847
|
+
*/
|
|
2848
|
+
getOverflow() {
|
|
2849
|
+
return this.overflow_;
|
|
2850
|
+
}
|
|
2851
|
+
/**
|
|
2852
|
+
* Get the font name.
|
|
2853
|
+
* @return {string|undefined} Font.
|
|
2854
|
+
* @api
|
|
2855
|
+
*/
|
|
2856
|
+
getFont() {
|
|
2857
|
+
return this.font_;
|
|
2858
|
+
}
|
|
2859
|
+
/**
|
|
2860
|
+
* Get the maximum angle between adjacent characters.
|
|
2861
|
+
* @return {number} Angle in radians.
|
|
2862
|
+
* @api
|
|
2863
|
+
*/
|
|
2864
|
+
getMaxAngle() {
|
|
2865
|
+
return this.maxAngle_;
|
|
2866
|
+
}
|
|
2867
|
+
/**
|
|
2868
|
+
* Get the label placement.
|
|
2869
|
+
* @return {TextPlacement} Text placement.
|
|
2870
|
+
* @api
|
|
2871
|
+
*/
|
|
2872
|
+
getPlacement() {
|
|
2873
|
+
return this.placement_;
|
|
2874
|
+
}
|
|
2875
|
+
/**
|
|
2876
|
+
* Get the repeat interval of the text.
|
|
2877
|
+
* @return {number|undefined} Repeat interval in pixels.
|
|
2878
|
+
* @api
|
|
2879
|
+
*/
|
|
2880
|
+
getRepeat() {
|
|
2881
|
+
return this.repeat_;
|
|
2882
|
+
}
|
|
2883
|
+
/**
|
|
2884
|
+
* Get the x-offset for the text.
|
|
2885
|
+
* @return {number} Horizontal text offset.
|
|
2886
|
+
* @api
|
|
2887
|
+
*/
|
|
2888
|
+
getOffsetX() {
|
|
2889
|
+
return this.offsetX_;
|
|
2890
|
+
}
|
|
2891
|
+
/**
|
|
2892
|
+
* Get the y-offset for the text.
|
|
2893
|
+
* @return {number} Vertical text offset.
|
|
2894
|
+
* @api
|
|
2895
|
+
*/
|
|
2896
|
+
getOffsetY() {
|
|
2897
|
+
return this.offsetY_;
|
|
2898
|
+
}
|
|
2899
|
+
/**
|
|
2900
|
+
* Get the fill style for the text.
|
|
2901
|
+
* @return {import("./Fill.js").default|null} Fill style.
|
|
2902
|
+
* @api
|
|
2903
|
+
*/
|
|
2904
|
+
getFill() {
|
|
2905
|
+
return this.fill_;
|
|
2906
|
+
}
|
|
2907
|
+
/**
|
|
2908
|
+
* Determine whether the text rotates with the map.
|
|
2909
|
+
* @return {boolean|undefined} Rotate with map.
|
|
2910
|
+
* @api
|
|
2911
|
+
*/
|
|
2912
|
+
getRotateWithView() {
|
|
2913
|
+
return this.rotateWithView_;
|
|
2914
|
+
}
|
|
2915
|
+
/**
|
|
2916
|
+
* Determine whether the text can be rendered upside down.
|
|
2917
|
+
* @return {boolean|undefined} Keep text upright.
|
|
2918
|
+
* @api
|
|
2919
|
+
*/
|
|
2920
|
+
getKeepUpright() {
|
|
2921
|
+
return this.keepUpright_;
|
|
2922
|
+
}
|
|
2923
|
+
/**
|
|
2924
|
+
* Get the text rotation.
|
|
2925
|
+
* @return {number|undefined} Rotation.
|
|
2926
|
+
* @api
|
|
2927
|
+
*/
|
|
2928
|
+
getRotation() {
|
|
2929
|
+
return this.rotation_;
|
|
2930
|
+
}
|
|
2931
|
+
/**
|
|
2932
|
+
* Get the text scale.
|
|
2933
|
+
* @return {number|import("../size.js").Size|undefined} Scale.
|
|
2934
|
+
* @api
|
|
2935
|
+
*/
|
|
2936
|
+
getScale() {
|
|
2937
|
+
return this.scale_;
|
|
2938
|
+
}
|
|
2939
|
+
/**
|
|
2940
|
+
* Get the symbolizer scale array.
|
|
2941
|
+
* @return {import("../size.js").Size} Scale array.
|
|
2942
|
+
*/
|
|
2943
|
+
getScaleArray() {
|
|
2944
|
+
return this.scaleArray_;
|
|
2945
|
+
}
|
|
2946
|
+
/**
|
|
2947
|
+
* Get the stroke style for the text.
|
|
2948
|
+
* @return {import("./Stroke.js").default|null} Stroke style.
|
|
2949
|
+
* @api
|
|
2950
|
+
*/
|
|
2951
|
+
getStroke() {
|
|
2952
|
+
return this.stroke_;
|
|
2953
|
+
}
|
|
2954
|
+
/**
|
|
2955
|
+
* Get the text to be rendered.
|
|
2956
|
+
* @return {string|Array<string>|undefined} Text.
|
|
2957
|
+
* @api
|
|
2958
|
+
*/
|
|
2959
|
+
getText() {
|
|
2960
|
+
return this.text_;
|
|
2961
|
+
}
|
|
2962
|
+
/**
|
|
2963
|
+
* Get the text alignment.
|
|
2964
|
+
* @return {CanvasTextAlign|undefined} Text align.
|
|
2965
|
+
* @api
|
|
2966
|
+
*/
|
|
2967
|
+
getTextAlign() {
|
|
2968
|
+
return this.textAlign_;
|
|
2969
|
+
}
|
|
2970
|
+
/**
|
|
2971
|
+
* Get the justification.
|
|
2972
|
+
* @return {TextJustify|undefined} Justification.
|
|
2973
|
+
* @api
|
|
2974
|
+
*/
|
|
2975
|
+
getJustify() {
|
|
2976
|
+
return this.justify_;
|
|
2977
|
+
}
|
|
2978
|
+
/**
|
|
2979
|
+
* Get the text baseline.
|
|
2980
|
+
* @return {CanvasTextBaseline|undefined} Text baseline.
|
|
2981
|
+
* @api
|
|
2982
|
+
*/
|
|
2983
|
+
getTextBaseline() {
|
|
2984
|
+
return this.textBaseline_;
|
|
2985
|
+
}
|
|
2986
|
+
/**
|
|
2987
|
+
* Get the background fill style for the text.
|
|
2988
|
+
* @return {import("./Fill.js").default|null} Fill style.
|
|
2989
|
+
* @api
|
|
2990
|
+
*/
|
|
2991
|
+
getBackgroundFill() {
|
|
2992
|
+
return this.backgroundFill_;
|
|
2993
|
+
}
|
|
2994
|
+
/**
|
|
2995
|
+
* Get the background stroke style for the text.
|
|
2996
|
+
* @return {import("./Stroke.js").default|null} Stroke style.
|
|
2997
|
+
* @api
|
|
2998
|
+
*/
|
|
2999
|
+
getBackgroundStroke() {
|
|
3000
|
+
return this.backgroundStroke_;
|
|
3001
|
+
}
|
|
3002
|
+
/**
|
|
3003
|
+
* Get the padding for the text.
|
|
3004
|
+
* @return {Array<number>|null} Padding.
|
|
3005
|
+
* @api
|
|
3006
|
+
*/
|
|
3007
|
+
getPadding() {
|
|
3008
|
+
return this.padding_;
|
|
3009
|
+
}
|
|
3010
|
+
/**
|
|
3011
|
+
* Get the declutter mode of the shape
|
|
3012
|
+
* @return {import("./Style.js").DeclutterMode} Shape's declutter mode
|
|
3013
|
+
* @api
|
|
3014
|
+
*/
|
|
3015
|
+
getDeclutterMode() {
|
|
3016
|
+
return this.declutterMode_;
|
|
3017
|
+
}
|
|
3018
|
+
/**
|
|
3019
|
+
* Set the `overflow` property.
|
|
3020
|
+
*
|
|
3021
|
+
* @param {boolean} overflow Let text overflow the path that it follows.
|
|
3022
|
+
* @api
|
|
3023
|
+
*/
|
|
3024
|
+
setOverflow(overflow) {
|
|
3025
|
+
this.overflow_ = overflow;
|
|
3026
|
+
}
|
|
3027
|
+
/**
|
|
3028
|
+
* Set the font.
|
|
3029
|
+
*
|
|
3030
|
+
* @param {string|undefined} font Font.
|
|
3031
|
+
* @api
|
|
3032
|
+
*/
|
|
3033
|
+
setFont(font) {
|
|
3034
|
+
this.font_ = font;
|
|
3035
|
+
}
|
|
3036
|
+
/**
|
|
3037
|
+
* Set the maximum angle between adjacent characters.
|
|
3038
|
+
*
|
|
3039
|
+
* @param {number} maxAngle Angle in radians.
|
|
3040
|
+
* @api
|
|
3041
|
+
*/
|
|
3042
|
+
setMaxAngle(maxAngle) {
|
|
3043
|
+
this.maxAngle_ = maxAngle;
|
|
3044
|
+
}
|
|
3045
|
+
/**
|
|
3046
|
+
* Set the x offset.
|
|
3047
|
+
*
|
|
3048
|
+
* @param {number} offsetX Horizontal text offset.
|
|
3049
|
+
* @api
|
|
3050
|
+
*/
|
|
3051
|
+
setOffsetX(offsetX) {
|
|
3052
|
+
this.offsetX_ = offsetX;
|
|
3053
|
+
}
|
|
3054
|
+
/**
|
|
3055
|
+
* Set the y offset.
|
|
3056
|
+
*
|
|
3057
|
+
* @param {number} offsetY Vertical text offset.
|
|
3058
|
+
* @api
|
|
3059
|
+
*/
|
|
3060
|
+
setOffsetY(offsetY) {
|
|
3061
|
+
this.offsetY_ = offsetY;
|
|
3062
|
+
}
|
|
3063
|
+
/**
|
|
3064
|
+
* Set the text placement.
|
|
3065
|
+
*
|
|
3066
|
+
* @param {TextPlacement} placement Placement.
|
|
3067
|
+
* @api
|
|
3068
|
+
*/
|
|
3069
|
+
setPlacement(placement) {
|
|
3070
|
+
this.placement_ = placement;
|
|
3071
|
+
}
|
|
3072
|
+
/**
|
|
3073
|
+
* Set the repeat interval of the text.
|
|
3074
|
+
* @param {number|undefined} [repeat] Repeat interval in pixels.
|
|
3075
|
+
* @api
|
|
3076
|
+
*/
|
|
3077
|
+
setRepeat(repeat) {
|
|
3078
|
+
this.repeat_ = repeat;
|
|
3079
|
+
}
|
|
3080
|
+
/**
|
|
3081
|
+
* Set whether to rotate the text with the view.
|
|
3082
|
+
*
|
|
3083
|
+
* @param {boolean} rotateWithView Rotate with map.
|
|
3084
|
+
* @api
|
|
3085
|
+
*/
|
|
3086
|
+
setRotateWithView(rotateWithView) {
|
|
3087
|
+
this.rotateWithView_ = rotateWithView;
|
|
3088
|
+
}
|
|
3089
|
+
/**
|
|
3090
|
+
* Set whether the text can be rendered upside down.
|
|
3091
|
+
*
|
|
3092
|
+
* @param {boolean} keepUpright Keep text upright.
|
|
3093
|
+
* @api
|
|
3094
|
+
*/
|
|
3095
|
+
setKeepUpright(keepUpright) {
|
|
3096
|
+
this.keepUpright_ = keepUpright;
|
|
3097
|
+
}
|
|
3098
|
+
/**
|
|
3099
|
+
* Set the fill.
|
|
3100
|
+
*
|
|
3101
|
+
* @param {import("./Fill.js").default|null} fill Fill style.
|
|
3102
|
+
* @api
|
|
3103
|
+
*/
|
|
3104
|
+
setFill(fill) {
|
|
3105
|
+
this.fill_ = fill;
|
|
3106
|
+
}
|
|
3107
|
+
/**
|
|
3108
|
+
* Set the rotation.
|
|
3109
|
+
*
|
|
3110
|
+
* @param {number|undefined} rotation Rotation.
|
|
3111
|
+
* @api
|
|
3112
|
+
*/
|
|
3113
|
+
setRotation(rotation) {
|
|
3114
|
+
this.rotation_ = rotation;
|
|
3115
|
+
}
|
|
3116
|
+
/**
|
|
3117
|
+
* Set the scale.
|
|
3118
|
+
*
|
|
3119
|
+
* @param {number|import("../size.js").Size|undefined} scale Scale.
|
|
3120
|
+
* @api
|
|
3121
|
+
*/
|
|
3122
|
+
setScale(scale2) {
|
|
3123
|
+
this.scale_ = scale2;
|
|
3124
|
+
this.scaleArray_ = toSize(scale2 !== void 0 ? scale2 : 1);
|
|
3125
|
+
}
|
|
3126
|
+
/**
|
|
3127
|
+
* Set the stroke.
|
|
3128
|
+
*
|
|
3129
|
+
* @param {import("./Stroke.js").default|null} stroke Stroke style.
|
|
3130
|
+
* @api
|
|
3131
|
+
*/
|
|
3132
|
+
setStroke(stroke) {
|
|
3133
|
+
this.stroke_ = stroke;
|
|
3134
|
+
}
|
|
3135
|
+
/**
|
|
3136
|
+
* Set the text.
|
|
3137
|
+
*
|
|
3138
|
+
* @param {string|Array<string>|undefined} text Text.
|
|
3139
|
+
* @api
|
|
3140
|
+
*/
|
|
3141
|
+
setText(text) {
|
|
3142
|
+
this.text_ = text;
|
|
3143
|
+
}
|
|
3144
|
+
/**
|
|
3145
|
+
* Set the text alignment.
|
|
3146
|
+
*
|
|
3147
|
+
* @param {CanvasTextAlign|undefined} textAlign Text align.
|
|
3148
|
+
* @api
|
|
3149
|
+
*/
|
|
3150
|
+
setTextAlign(textAlign) {
|
|
3151
|
+
this.textAlign_ = textAlign;
|
|
3152
|
+
}
|
|
3153
|
+
/**
|
|
3154
|
+
* Set the justification.
|
|
3155
|
+
*
|
|
3156
|
+
* @param {TextJustify|undefined} justify Justification.
|
|
3157
|
+
* @api
|
|
3158
|
+
*/
|
|
3159
|
+
setJustify(justify) {
|
|
3160
|
+
this.justify_ = justify;
|
|
3161
|
+
}
|
|
3162
|
+
/**
|
|
3163
|
+
* Set the text baseline.
|
|
3164
|
+
*
|
|
3165
|
+
* @param {CanvasTextBaseline|undefined} textBaseline Text baseline.
|
|
3166
|
+
* @api
|
|
3167
|
+
*/
|
|
3168
|
+
setTextBaseline(textBaseline) {
|
|
3169
|
+
this.textBaseline_ = textBaseline;
|
|
3170
|
+
}
|
|
3171
|
+
/**
|
|
3172
|
+
* Set the background fill.
|
|
3173
|
+
*
|
|
3174
|
+
* @param {import("./Fill.js").default|null} fill Fill style.
|
|
3175
|
+
* @api
|
|
3176
|
+
*/
|
|
3177
|
+
setBackgroundFill(fill) {
|
|
3178
|
+
this.backgroundFill_ = fill;
|
|
3179
|
+
}
|
|
3180
|
+
/**
|
|
3181
|
+
* Set the background stroke.
|
|
3182
|
+
*
|
|
3183
|
+
* @param {import("./Stroke.js").default|null} stroke Stroke style.
|
|
3184
|
+
* @api
|
|
3185
|
+
*/
|
|
3186
|
+
setBackgroundStroke(stroke) {
|
|
3187
|
+
this.backgroundStroke_ = stroke;
|
|
3188
|
+
}
|
|
3189
|
+
/**
|
|
3190
|
+
* Set the padding (`[top, right, bottom, left]`).
|
|
3191
|
+
*
|
|
3192
|
+
* @param {Array<number>|null} padding Padding.
|
|
3193
|
+
* @api
|
|
3194
|
+
*/
|
|
3195
|
+
setPadding(padding) {
|
|
3196
|
+
this.padding_ = padding;
|
|
3197
|
+
}
|
|
3198
|
+
}
|
|
3199
|
+
export {
|
|
3200
|
+
asArray as $,
|
|
3201
|
+
defaultLineDash as A,
|
|
3202
|
+
defaultLineDashOffset as B,
|
|
3203
|
+
CLASS_UNSELECTABLE as C,
|
|
3204
|
+
DEVICE_PIXEL_RATIO as D,
|
|
3205
|
+
defaultLineJoin as E,
|
|
3206
|
+
Fill as F,
|
|
3207
|
+
defaultLineWidth as G,
|
|
3208
|
+
defaultMiterLimit as H,
|
|
3209
|
+
Icon as I,
|
|
3210
|
+
defaultPadding as J,
|
|
3211
|
+
defaultTextBaseline as K,
|
|
3212
|
+
defaultTextAlign as L,
|
|
3213
|
+
MAC as M,
|
|
3214
|
+
NO_COLOR as N,
|
|
3215
|
+
defaultFont as O,
|
|
3216
|
+
PASSIVE_EVENT_LISTENERS as P,
|
|
3217
|
+
registerFont as Q,
|
|
3218
|
+
RegularShape as R,
|
|
3219
|
+
Style as S,
|
|
3220
|
+
Text as T,
|
|
3221
|
+
getSharedCanvasContext2D as U,
|
|
3222
|
+
getTextDimensions as V,
|
|
3223
|
+
WEBKIT as W,
|
|
3224
|
+
drawImageOrLabel as X,
|
|
3225
|
+
measureAndCacheTextWidth as Y,
|
|
3226
|
+
createCanvasContext2D as Z,
|
|
3227
|
+
ImageState as _,
|
|
3228
|
+
CLASS_CONTROL as a,
|
|
3229
|
+
releaseCanvas as a0,
|
|
3230
|
+
listenImage as a1,
|
|
3231
|
+
scale as a2,
|
|
3232
|
+
CLASS_SELECTABLE as a3,
|
|
3233
|
+
outerWidth as a4,
|
|
3234
|
+
outerHeight as a5,
|
|
3235
|
+
CLASS_COLLAPSED as b,
|
|
3236
|
+
replaceNode as c,
|
|
3237
|
+
CLASS_HIDDEN as d,
|
|
3238
|
+
toString as e,
|
|
3239
|
+
fromString as f,
|
|
3240
|
+
rgbaToLcha as g,
|
|
3241
|
+
Stroke as h,
|
|
3242
|
+
CircleStyle as i,
|
|
3243
|
+
createDefaultStyle as j,
|
|
3244
|
+
toFunction as k,
|
|
3245
|
+
lchaToRgba as l,
|
|
3246
|
+
checkedFonts as m,
|
|
3247
|
+
createMockDiv as n,
|
|
3248
|
+
WORKER_OFFSCREEN_CANVAS as o,
|
|
3249
|
+
replaceChildren as p,
|
|
3250
|
+
isCanvas as q,
|
|
3251
|
+
removeChildren as r,
|
|
3252
|
+
shared as s,
|
|
3253
|
+
toSize as t,
|
|
3254
|
+
hasArea as u,
|
|
3255
|
+
asColorLike as v,
|
|
3256
|
+
withAlpha as w,
|
|
3257
|
+
defaultFillStyle as x,
|
|
3258
|
+
defaultStrokeStyle as y,
|
|
3259
|
+
defaultLineCap as z
|
|
3260
|
+
};
|