@guardian/interactive-component-library 0.2.0-rc-06ceabc.0 → 0.2.0-rc1
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.
|
@@ -6184,175 +6184,6 @@ function ZoomControl({ resetEnabled, onZoomIn, onZoomOut, onReset }) {
|
|
|
6184
6184
|
)
|
|
6185
6185
|
] });
|
|
6186
6186
|
}
|
|
6187
|
-
class FeatureRenderer {
|
|
6188
|
-
constructor() {
|
|
6189
|
-
this.drawingFunction = geoPath();
|
|
6190
|
-
}
|
|
6191
|
-
setStyle(style2) {
|
|
6192
|
-
this.style = style2;
|
|
6193
|
-
}
|
|
6194
|
-
render(frameState, feature, context) {
|
|
6195
|
-
if (!this.style) {
|
|
6196
|
-
return;
|
|
6197
|
-
}
|
|
6198
|
-
const { projection, transform, pixelRatio } = frameState.viewState;
|
|
6199
|
-
const { stroke, fill } = this.style;
|
|
6200
|
-
this.drawingFunction.context(context);
|
|
6201
|
-
context.beginPath();
|
|
6202
|
-
const geometries = feature.getProjectedGeometries(projection);
|
|
6203
|
-
if (frameState.debug) {
|
|
6204
|
-
try {
|
|
6205
|
-
validateGeometries(geometries);
|
|
6206
|
-
} catch {
|
|
6207
|
-
console.error(
|
|
6208
|
-
`Invalid geometry. Feature skipped during rendering. Click here to inspect geometry: ${generateDebugUrl(feature)}
|
|
6209
|
-
`,
|
|
6210
|
-
feature
|
|
6211
|
-
);
|
|
6212
|
-
}
|
|
6213
|
-
}
|
|
6214
|
-
for (const geometry of geometries) {
|
|
6215
|
-
this.drawingFunction(geometry);
|
|
6216
|
-
}
|
|
6217
|
-
if (fill) {
|
|
6218
|
-
context.fillStyle = fill.getRgba();
|
|
6219
|
-
context.fill();
|
|
6220
|
-
}
|
|
6221
|
-
if (stroke) {
|
|
6222
|
-
context.lineWidth = stroke.width * pixelRatio / transform.k;
|
|
6223
|
-
context.strokeStyle = stroke.getRgba();
|
|
6224
|
-
context.stroke();
|
|
6225
|
-
}
|
|
6226
|
-
}
|
|
6227
|
-
}
|
|
6228
|
-
const textPadding = {
|
|
6229
|
-
top: 20,
|
|
6230
|
-
right: 20,
|
|
6231
|
-
bottom: 20,
|
|
6232
|
-
left: 20
|
|
6233
|
-
};
|
|
6234
|
-
class TextLayerRenderer {
|
|
6235
|
-
constructor(layer) {
|
|
6236
|
-
this.layer = layer;
|
|
6237
|
-
this.featureRenderer = new FeatureRenderer();
|
|
6238
|
-
this._element = document.createElement("div");
|
|
6239
|
-
this._element.className = "gv-text-layer";
|
|
6240
|
-
const style2 = this._element.style;
|
|
6241
|
-
style2.position = "absolute";
|
|
6242
|
-
style2.width = "100%";
|
|
6243
|
-
style2.height = "100%";
|
|
6244
|
-
style2.pointerEvents = "none";
|
|
6245
|
-
style2.overflow = "hidden";
|
|
6246
|
-
}
|
|
6247
|
-
renderFrame(frameState, targetElement) {
|
|
6248
|
-
if (this.layer.opacity === 0) return targetElement;
|
|
6249
|
-
const { declutterTree } = frameState;
|
|
6250
|
-
const { projection, viewPortSize, sizeInPixels, visibleExtent, transform } = frameState.viewState;
|
|
6251
|
-
this._element.style.opacity = this.layer.opacity;
|
|
6252
|
-
const source = this.layer.source;
|
|
6253
|
-
const features = source.getFeaturesInExtent(visibleExtent);
|
|
6254
|
-
const textElements = [];
|
|
6255
|
-
for (const feature of features) {
|
|
6256
|
-
const geometries = feature.getProjectedGeometries(projection);
|
|
6257
|
-
const point = geometries.find((d2) => d2.type === "Point");
|
|
6258
|
-
if (!point) {
|
|
6259
|
-
throw new Error(
|
|
6260
|
-
`Expected Point geometry for feature in TextLayer: ${feature}`
|
|
6261
|
-
);
|
|
6262
|
-
}
|
|
6263
|
-
const styleFunction2 = feature.getStyleFunction() || this.layer.getStyleFunction();
|
|
6264
|
-
const featureStyle = styleFunction2(feature);
|
|
6265
|
-
const textElement = this.getTextElementWithID(feature.uid);
|
|
6266
|
-
textElement.innerText = featureStyle.text.content;
|
|
6267
|
-
const [relativeX, relativeY] = transform.apply(point.coordinates).map((d2, i) => d2 / sizeInPixels[i]);
|
|
6268
|
-
const position = {
|
|
6269
|
-
left: `${relativeX * 100}%`,
|
|
6270
|
-
top: `${relativeY * 100}%`
|
|
6271
|
-
};
|
|
6272
|
-
this.styleTextElement(textElement, featureStyle.text, position);
|
|
6273
|
-
const bbox = this.getElementBBox(textElement, {
|
|
6274
|
-
x: relativeX * viewPortSize[0],
|
|
6275
|
-
y: relativeY * viewPortSize[1]
|
|
6276
|
-
});
|
|
6277
|
-
if (declutterTree.collides(bbox)) {
|
|
6278
|
-
continue;
|
|
6279
|
-
}
|
|
6280
|
-
declutterTree.insert(bbox);
|
|
6281
|
-
if (this.layer.drawCollisionBoxes) {
|
|
6282
|
-
const collisionBoxDebugElement = this.getCollisionBoxElement(bbox);
|
|
6283
|
-
textElements.push(collisionBoxDebugElement);
|
|
6284
|
-
}
|
|
6285
|
-
textElements.push(textElement);
|
|
6286
|
-
}
|
|
6287
|
-
replaceChildren(this._element, textElements);
|
|
6288
|
-
return this._element;
|
|
6289
|
-
}
|
|
6290
|
-
getTextElementWithID(id2) {
|
|
6291
|
-
const elementId = `text-feature-${id2}`;
|
|
6292
|
-
let textElement = this._element.querySelector(`#${elementId}`);
|
|
6293
|
-
if (!textElement) {
|
|
6294
|
-
textElement = document.createElement("div");
|
|
6295
|
-
textElement.id = elementId;
|
|
6296
|
-
}
|
|
6297
|
-
return textElement;
|
|
6298
|
-
}
|
|
6299
|
-
styleTextElement(element, textStyle, position) {
|
|
6300
|
-
const style2 = element.style;
|
|
6301
|
-
style2.position = "absolute";
|
|
6302
|
-
style2.transform = `translate(-50%, -50%)`;
|
|
6303
|
-
style2.left = position.left;
|
|
6304
|
-
style2.top = position.top;
|
|
6305
|
-
style2.textAlign = "center";
|
|
6306
|
-
style2.whiteSpace = "nowrap";
|
|
6307
|
-
style2.fontFamily = textStyle.fontFamily;
|
|
6308
|
-
style2.fontSize = textStyle.fontSize;
|
|
6309
|
-
style2.fontWeight = textStyle.fontWeight;
|
|
6310
|
-
style2.lineHeight = textStyle.lineHeight;
|
|
6311
|
-
style2.color = textStyle.color;
|
|
6312
|
-
style2.textShadow = textStyle.textShadow;
|
|
6313
|
-
style2.padding = `${textPadding.top}px ${textPadding.right}px ${textPadding.bottom}px ${textPadding.left}px`;
|
|
6314
|
-
}
|
|
6315
|
-
getElementBBox(element, position) {
|
|
6316
|
-
if (!element.parentElement) {
|
|
6317
|
-
document.body.appendChild(element);
|
|
6318
|
-
}
|
|
6319
|
-
const { width, height } = element.getBoundingClientRect();
|
|
6320
|
-
if (element.parentElement !== this._element) {
|
|
6321
|
-
element.remove();
|
|
6322
|
-
}
|
|
6323
|
-
return {
|
|
6324
|
-
minX: Math.floor(position.x) - width / 2,
|
|
6325
|
-
minY: Math.floor(position.y) - height / 2,
|
|
6326
|
-
maxX: Math.ceil(position.x + width / 2),
|
|
6327
|
-
maxY: Math.ceil(position.y + height / 2)
|
|
6328
|
-
};
|
|
6329
|
-
}
|
|
6330
|
-
getCollisionBoxElement(bbox) {
|
|
6331
|
-
const element = document.createElement("div");
|
|
6332
|
-
const style2 = element.style;
|
|
6333
|
-
style2.position = "absolute";
|
|
6334
|
-
style2.left = `${bbox.minX}px`;
|
|
6335
|
-
style2.top = `${bbox.minY}px`;
|
|
6336
|
-
style2.width = `${bbox.maxX - bbox.minX}px`;
|
|
6337
|
-
style2.height = `${bbox.maxY - bbox.minY}px`;
|
|
6338
|
-
style2.border = "2px solid black";
|
|
6339
|
-
return element;
|
|
6340
|
-
}
|
|
6341
|
-
}
|
|
6342
|
-
class Style {
|
|
6343
|
-
constructor(properties) {
|
|
6344
|
-
this.stroke = properties == null ? void 0 : properties.stroke;
|
|
6345
|
-
this.fill = properties == null ? void 0 : properties.fill;
|
|
6346
|
-
this.text = properties == null ? void 0 : properties.text;
|
|
6347
|
-
}
|
|
6348
|
-
clone() {
|
|
6349
|
-
return new Style({
|
|
6350
|
-
stroke: this.stroke,
|
|
6351
|
-
fill: this.fill,
|
|
6352
|
-
text: this.text
|
|
6353
|
-
});
|
|
6354
|
-
}
|
|
6355
|
-
}
|
|
6356
6187
|
function memoise(fn) {
|
|
6357
6188
|
let called = false;
|
|
6358
6189
|
let lastResult;
|
|
@@ -6369,928 +6200,1302 @@ function memoise(fn) {
|
|
|
6369
6200
|
return lastResult;
|
|
6370
6201
|
};
|
|
6371
6202
|
}
|
|
6372
|
-
|
|
6373
|
-
|
|
6374
|
-
|
|
6375
|
-
|
|
6376
|
-
|
|
6377
|
-
|
|
6378
|
-
|
|
6379
|
-
|
|
6380
|
-
|
|
6381
|
-
|
|
6382
|
-
|
|
6383
|
-
|
|
6384
|
-
|
|
6385
|
-
|
|
6386
|
-
|
|
6387
|
-
|
|
6388
|
-
|
|
6389
|
-
|
|
6390
|
-
|
|
6391
|
-
|
|
6392
|
-
|
|
6393
|
-
}
|
|
6394
|
-
class Stroke {
|
|
6395
|
-
constructor(options) {
|
|
6396
|
-
this.color = (options == null ? void 0 : options.color) || "#121212";
|
|
6397
|
-
this.width = (options == null ? void 0 : options.width) || 0.5;
|
|
6398
|
-
this.opacity = (options == null ? void 0 : options.opacity) || 1;
|
|
6399
|
-
this._getRgba = memoise(toRgba);
|
|
6203
|
+
class Feature {
|
|
6204
|
+
/**
|
|
6205
|
+
* Represents a feature on the map
|
|
6206
|
+
* @constructor
|
|
6207
|
+
* @param {Object} props - The properties for the feature.
|
|
6208
|
+
* @property {string} id - The unique identifier of the feature
|
|
6209
|
+
* @property {Array} geometries - The geometries of the feature
|
|
6210
|
+
* @property {Object} properties - The properties of the feature
|
|
6211
|
+
* @property {import("./styles").Style | import("./styles").StyleFunction} style - The style of the feature
|
|
6212
|
+
*/
|
|
6213
|
+
constructor({ id: id2, geometries, properties, style: style2 }) {
|
|
6214
|
+
this.id = id2;
|
|
6215
|
+
this.geometries = geometries;
|
|
6216
|
+
this.properties = properties;
|
|
6217
|
+
this.style = style2;
|
|
6218
|
+
this.uid = createUid();
|
|
6219
|
+
this._getProjectedGeometries = memoise((projection) => {
|
|
6220
|
+
return this.geometries.map(
|
|
6221
|
+
(d2) => d2.getProjected(projection, projection.revision)
|
|
6222
|
+
);
|
|
6223
|
+
}).bind(this);
|
|
6400
6224
|
}
|
|
6401
|
-
|
|
6402
|
-
|
|
6225
|
+
setGeometries(geometries) {
|
|
6226
|
+
this.geometries = geometries;
|
|
6227
|
+
this._extent = void 0;
|
|
6403
6228
|
}
|
|
6404
|
-
|
|
6405
|
-
|
|
6406
|
-
|
|
6407
|
-
|
|
6408
|
-
|
|
6409
|
-
|
|
6229
|
+
getExtent() {
|
|
6230
|
+
if (this._extent) return this._extent;
|
|
6231
|
+
const extent = this.geometries.reduce((combinedExtent, geometry) => {
|
|
6232
|
+
if (!combinedExtent) return geometry.extent;
|
|
6233
|
+
return combineExtents(geometry.extent, combinedExtent);
|
|
6234
|
+
}, null);
|
|
6235
|
+
this._extent = extent;
|
|
6236
|
+
return extent;
|
|
6410
6237
|
}
|
|
6411
|
-
|
|
6412
|
-
return this.
|
|
6238
|
+
getProjectedGeometries(projection) {
|
|
6239
|
+
return this._getProjectedGeometries(projection, projection.revision);
|
|
6413
6240
|
}
|
|
6414
|
-
|
|
6415
|
-
|
|
6416
|
-
|
|
6417
|
-
|
|
6418
|
-
|
|
6419
|
-
|
|
6420
|
-
|
|
6421
|
-
this.lineHeight = (options == null ? void 0 : options.lineHeight) || 1.3;
|
|
6422
|
-
this.color = (options == null ? void 0 : options.color) || "#121212";
|
|
6423
|
-
this.textShadow = (options == null ? void 0 : options.textShadow) || "1px 1px 0px #f6f6f6, -1px -1px 0px #f6f6f6, -1px 1px 0px #f6f6f6, 1px -1px #f6f6f6";
|
|
6241
|
+
getStyleFunction() {
|
|
6242
|
+
const style2 = this.style;
|
|
6243
|
+
if (!style2) return null;
|
|
6244
|
+
if (typeof style2 === "function") return style2;
|
|
6245
|
+
return () => {
|
|
6246
|
+
return style2;
|
|
6247
|
+
};
|
|
6424
6248
|
}
|
|
6425
|
-
|
|
6426
|
-
|
|
6427
|
-
|
|
6428
|
-
this.data = data;
|
|
6429
|
-
this.length = this.data.length;
|
|
6430
|
-
this.compare = compare;
|
|
6431
|
-
if (this.length > 0) {
|
|
6432
|
-
for (let i = (this.length >> 1) - 1; i >= 0; i--) this._down(i);
|
|
6249
|
+
containsCoordinate(coordinate) {
|
|
6250
|
+
if (!containsCoordinate(this.getExtent(), coordinate)) {
|
|
6251
|
+
return false;
|
|
6433
6252
|
}
|
|
6253
|
+
for (const geometries of this.geometries) {
|
|
6254
|
+
if (geoContains(geometries.getGeoJSON(), coordinate)) {
|
|
6255
|
+
return true;
|
|
6256
|
+
}
|
|
6257
|
+
}
|
|
6258
|
+
return false;
|
|
6434
6259
|
}
|
|
6435
|
-
|
|
6436
|
-
|
|
6437
|
-
|
|
6438
|
-
|
|
6439
|
-
|
|
6440
|
-
|
|
6441
|
-
|
|
6442
|
-
const top = this.data[0];
|
|
6443
|
-
const bottom = this.data.pop();
|
|
6444
|
-
this.length--;
|
|
6445
|
-
if (this.length > 0) {
|
|
6446
|
-
this.data[0] = bottom;
|
|
6447
|
-
this._down(0);
|
|
6448
|
-
}
|
|
6449
|
-
return top;
|
|
6260
|
+
clone() {
|
|
6261
|
+
return new Feature({
|
|
6262
|
+
id: this.id,
|
|
6263
|
+
geometries: this.geometries.map((d2) => d2.clone()),
|
|
6264
|
+
properties: this.properties,
|
|
6265
|
+
style: this.style
|
|
6266
|
+
});
|
|
6450
6267
|
}
|
|
6451
|
-
|
|
6452
|
-
|
|
6268
|
+
/**
|
|
6269
|
+
* Returns the geometries as a GeoJSON object
|
|
6270
|
+
* @returns {Object} The GeoJSON representation of the geometries
|
|
6271
|
+
*/
|
|
6272
|
+
getGeoJSON() {
|
|
6273
|
+
const geometries = this.geometries.map((d2) => d2.getGeoJSON());
|
|
6274
|
+
if (geometries.length === 1) return geometries[0];
|
|
6275
|
+
return {
|
|
6276
|
+
type: "Feature",
|
|
6277
|
+
geometry: this._getGeometryGeoJSON(),
|
|
6278
|
+
properties: this.properties
|
|
6279
|
+
};
|
|
6453
6280
|
}
|
|
6454
|
-
|
|
6455
|
-
const
|
|
6456
|
-
|
|
6457
|
-
|
|
6458
|
-
|
|
6459
|
-
|
|
6460
|
-
|
|
6461
|
-
|
|
6462
|
-
|
|
6281
|
+
_getGeometryGeoJSON() {
|
|
6282
|
+
const geometries = this.geometries.map((d2) => d2.getGeoJSON());
|
|
6283
|
+
if (geometries.length === 0) throw new Error("Feature has no geometries");
|
|
6284
|
+
if (geometries.length === 1) return geometries[0];
|
|
6285
|
+
if (geometries[0].type === "Polygon") {
|
|
6286
|
+
return {
|
|
6287
|
+
type: "MultiPolygon",
|
|
6288
|
+
coordinates: geometries.map((d2) => d2.coordinates)
|
|
6289
|
+
};
|
|
6290
|
+
} else if (geometries[0].type === "LineString") {
|
|
6291
|
+
return {
|
|
6292
|
+
type: "MultiLineString",
|
|
6293
|
+
coordinates: geometries.map((d2) => d2.coordinates)
|
|
6294
|
+
};
|
|
6295
|
+
} else if (geometries[0].type === "Point") {
|
|
6296
|
+
return {
|
|
6297
|
+
type: "MultiPoint",
|
|
6298
|
+
coordinates: geometries.map((d2) => d2.coordinates)
|
|
6299
|
+
};
|
|
6463
6300
|
}
|
|
6464
|
-
|
|
6301
|
+
throw new Error("Could not determine geometry type");
|
|
6465
6302
|
}
|
|
6466
|
-
|
|
6467
|
-
|
|
6468
|
-
|
|
6469
|
-
|
|
6470
|
-
|
|
6471
|
-
|
|
6472
|
-
|
|
6473
|
-
|
|
6474
|
-
|
|
6475
|
-
|
|
6476
|
-
|
|
6477
|
-
|
|
6478
|
-
|
|
6479
|
-
|
|
6480
|
-
|
|
6481
|
-
|
|
6482
|
-
|
|
6303
|
+
}
|
|
6304
|
+
class Geometry {
|
|
6305
|
+
/**
|
|
6306
|
+
* Represents vector geometry
|
|
6307
|
+
* @constructor
|
|
6308
|
+
* @param {Object} options
|
|
6309
|
+
* @param {string} options.type - The type of geometry (e.g., 'Point', 'LineString', 'Polygon')
|
|
6310
|
+
* @param {Array} options.extent - The extent of the geometry (e.g., [xmin, ymin, xmax, ymax])
|
|
6311
|
+
* @param {Array} options.coordinates - The coordinates of the geometry (e.g., [[x1, y1], [x2, y2], ...])
|
|
6312
|
+
*/
|
|
6313
|
+
constructor({ type, extent, coordinates }) {
|
|
6314
|
+
this.type = type;
|
|
6315
|
+
this.extent = extent;
|
|
6316
|
+
this.coordinates = coordinates;
|
|
6317
|
+
this.getProjected = memoise(this._getProjected).bind(this);
|
|
6318
|
+
}
|
|
6319
|
+
/**
|
|
6320
|
+
* Returns the geometry as a GeoJSON object
|
|
6321
|
+
* @function
|
|
6322
|
+
* @param {import("../projection").Projection} projection - The projection to use for the geometry
|
|
6323
|
+
* @returns {Object} A GeoJSON representation of the projected geometry
|
|
6324
|
+
* @private
|
|
6325
|
+
*/
|
|
6326
|
+
// eslint-disable-next-line no-unused-vars
|
|
6327
|
+
_getProjected(projection) {
|
|
6328
|
+
throw new Error("Not implemented");
|
|
6329
|
+
}
|
|
6330
|
+
/**
|
|
6331
|
+
* Returns the geometry as a GeoJSON object
|
|
6332
|
+
* @returns {Object} The GeoJSON representation of the geometry
|
|
6333
|
+
*/
|
|
6334
|
+
getGeoJSON() {
|
|
6335
|
+
return {
|
|
6336
|
+
type: this.type,
|
|
6337
|
+
coordinates: this.coordinates
|
|
6338
|
+
};
|
|
6483
6339
|
}
|
|
6484
6340
|
}
|
|
6485
|
-
|
|
6486
|
-
|
|
6341
|
+
class LineString extends Geometry {
|
|
6342
|
+
constructor({ type = "LineString", extent, coordinates }) {
|
|
6343
|
+
super({ type, extent, coordinates });
|
|
6344
|
+
}
|
|
6345
|
+
_getProjected(projection) {
|
|
6346
|
+
const projected = [];
|
|
6347
|
+
for (const point of this.coordinates) {
|
|
6348
|
+
projected.push(projection(point));
|
|
6349
|
+
}
|
|
6350
|
+
return {
|
|
6351
|
+
type: this.type,
|
|
6352
|
+
coordinates: projected
|
|
6353
|
+
};
|
|
6354
|
+
}
|
|
6487
6355
|
}
|
|
6488
|
-
|
|
6489
|
-
|
|
6490
|
-
|
|
6491
|
-
|
|
6492
|
-
|
|
6493
|
-
|
|
6494
|
-
|
|
6495
|
-
|
|
6496
|
-
const
|
|
6497
|
-
{
|
|
6498
|
-
|
|
6499
|
-
|
|
6500
|
-
|
|
6501
|
-
|
|
6502
|
-
|
|
6356
|
+
class Polygon extends Geometry {
|
|
6357
|
+
constructor({ type = "Polygon", extent, coordinates }) {
|
|
6358
|
+
super({ type, extent, coordinates });
|
|
6359
|
+
}
|
|
6360
|
+
_getProjected(projection) {
|
|
6361
|
+
const projected = [];
|
|
6362
|
+
const rings = this.coordinates;
|
|
6363
|
+
for (const ring of rings) {
|
|
6364
|
+
const projectedRing = [];
|
|
6365
|
+
for (const point of ring) {
|
|
6366
|
+
const projectedPoint = projection(point);
|
|
6367
|
+
if (projectedPoint) {
|
|
6368
|
+
projectedRing.push(projectedPoint);
|
|
6369
|
+
} else {
|
|
6370
|
+
break;
|
|
6371
|
+
}
|
|
6372
|
+
}
|
|
6373
|
+
if (projectedRing.length > 0) {
|
|
6374
|
+
projected.push(projectedRing);
|
|
6503
6375
|
}
|
|
6504
6376
|
}
|
|
6505
|
-
|
|
6506
|
-
|
|
6507
|
-
|
|
6508
|
-
|
|
6509
|
-
if (result.length === n2) return result;
|
|
6510
|
-
}
|
|
6511
|
-
node = queue.pop();
|
|
6512
|
-
if (node) node = node.node;
|
|
6377
|
+
return {
|
|
6378
|
+
type: this.type,
|
|
6379
|
+
coordinates: projected
|
|
6380
|
+
};
|
|
6513
6381
|
}
|
|
6514
|
-
|
|
6515
|
-
|
|
6516
|
-
function compareDist(a, b) {
|
|
6517
|
-
return a.dist - b.dist;
|
|
6518
|
-
}
|
|
6519
|
-
function boxDist(x, y, box) {
|
|
6520
|
-
const dx = axisDist(x, box.minX, box.maxX), dy = axisDist(y, box.minY, box.maxY);
|
|
6521
|
-
return dx * dx + dy * dy;
|
|
6522
|
-
}
|
|
6523
|
-
function axisDist(k, min, max) {
|
|
6524
|
-
return k < min ? min - k : k <= max ? 0 : k - max;
|
|
6525
|
-
}
|
|
6526
|
-
class VectorSource {
|
|
6527
|
-
constructor({ features }) {
|
|
6528
|
-
this.dispatcher = new Dispatcher(this);
|
|
6529
|
-
this._featuresRtree = new RBush();
|
|
6530
|
-
this.setFeatures(features);
|
|
6382
|
+
getOuterRing() {
|
|
6383
|
+
return this.coordinates[0];
|
|
6531
6384
|
}
|
|
6532
|
-
|
|
6533
|
-
this.
|
|
6385
|
+
setOuterRing(coordinates) {
|
|
6386
|
+
this.coordinates[0] = coordinates;
|
|
6534
6387
|
}
|
|
6535
|
-
|
|
6536
|
-
|
|
6388
|
+
setCoordinates(coordinates) {
|
|
6389
|
+
this.coordinates = coordinates;
|
|
6537
6390
|
}
|
|
6538
|
-
|
|
6539
|
-
|
|
6540
|
-
|
|
6541
|
-
this.
|
|
6542
|
-
lon,
|
|
6543
|
-
lat,
|
|
6544
|
-
10,
|
|
6545
|
-
(d2) => d2.feature.containsCoordinate(coordinate)
|
|
6546
|
-
).map((d2) => {
|
|
6547
|
-
const midX = d2.minX + (d2.minX + d2.maxX) / 2;
|
|
6548
|
-
const midY = d2.minY + (d2.minY + d2.maxY) / 2;
|
|
6549
|
-
d2.distance = Math.hypot(midX - lon, midY - lat);
|
|
6550
|
-
return d2;
|
|
6391
|
+
clone() {
|
|
6392
|
+
return new Polygon({
|
|
6393
|
+
extent: this.extent,
|
|
6394
|
+
coordinates: JSON.parse(JSON.stringify(this.coordinates))
|
|
6551
6395
|
});
|
|
6552
|
-
features.sort((a, b) => a.distance - b.distance);
|
|
6553
|
-
return features.map((d2) => d2.feature);
|
|
6554
6396
|
}
|
|
6555
|
-
|
|
6556
|
-
|
|
6557
|
-
|
|
6397
|
+
}
|
|
6398
|
+
class Point extends Geometry {
|
|
6399
|
+
constructor({ type = "Point", coordinates }) {
|
|
6400
|
+
super({ type, extent: null, coordinates });
|
|
6401
|
+
this.extent = [...coordinates, ...coordinates];
|
|
6558
6402
|
}
|
|
6559
|
-
|
|
6560
|
-
|
|
6561
|
-
|
|
6562
|
-
|
|
6563
|
-
|
|
6564
|
-
minX: Math.floor(minX),
|
|
6565
|
-
minY: Math.floor(minY),
|
|
6566
|
-
maxX: Math.ceil(maxX),
|
|
6567
|
-
maxY: Math.ceil(maxY),
|
|
6568
|
-
feature
|
|
6569
|
-
});
|
|
6570
|
-
}
|
|
6571
|
-
this._features = features;
|
|
6572
|
-
this.dispatcher.dispatch(MapEvent.CHANGE);
|
|
6403
|
+
_getProjected(projection) {
|
|
6404
|
+
return {
|
|
6405
|
+
type: this.type,
|
|
6406
|
+
coordinates: projection(this.coordinates)
|
|
6407
|
+
};
|
|
6573
6408
|
}
|
|
6574
6409
|
}
|
|
6575
|
-
class
|
|
6576
|
-
|
|
6577
|
-
|
|
6578
|
-
features
|
|
6579
|
-
|
|
6580
|
-
|
|
6581
|
-
|
|
6582
|
-
|
|
6583
|
-
|
|
6584
|
-
|
|
6585
|
-
|
|
6586
|
-
|
|
6587
|
-
|
|
6588
|
-
|
|
6589
|
-
|
|
6590
|
-
|
|
6591
|
-
|
|
6592
|
-
|
|
6593
|
-
|
|
6594
|
-
|
|
6595
|
-
|
|
6596
|
-
|
|
6597
|
-
|
|
6598
|
-
|
|
6599
|
-
|
|
6600
|
-
|
|
6410
|
+
class GeoJSON {
|
|
6411
|
+
readFeaturesFromObject(object) {
|
|
6412
|
+
const geoJSONObject = object;
|
|
6413
|
+
let features = null;
|
|
6414
|
+
if (geoJSONObject["type"] === "FeatureCollection") {
|
|
6415
|
+
const geoJSONFeatureCollection = object;
|
|
6416
|
+
features = [];
|
|
6417
|
+
const geoJSONFeatures = geoJSONFeatureCollection["features"];
|
|
6418
|
+
for (let i = 0, ii = geoJSONFeatures.length; i < ii; ++i) {
|
|
6419
|
+
const featureObject = this.readFeatureFromObject(geoJSONFeatures[i]);
|
|
6420
|
+
if (!featureObject) {
|
|
6421
|
+
continue;
|
|
6422
|
+
}
|
|
6423
|
+
features.push(featureObject);
|
|
6424
|
+
}
|
|
6425
|
+
} else if (geoJSONObject["type"] === "Feature") {
|
|
6426
|
+
features = [this.readFeatureFromObject(geoJSONObject)];
|
|
6427
|
+
} else if (Array.isArray(geoJSONObject)) {
|
|
6428
|
+
features = [];
|
|
6429
|
+
for (let i = 0, ii = geoJSONObject.length; i < ii; ++i) {
|
|
6430
|
+
const featureObject = this.readFeatureFromObject(geoJSONObject[i]);
|
|
6431
|
+
if (!featureObject) {
|
|
6432
|
+
continue;
|
|
6433
|
+
}
|
|
6434
|
+
features.push(featureObject);
|
|
6435
|
+
}
|
|
6436
|
+
} else {
|
|
6437
|
+
try {
|
|
6438
|
+
const geometries = this.readGeometriesFromObject(geoJSONObject);
|
|
6439
|
+
const feature = new Feature({ geometries });
|
|
6440
|
+
features = [feature];
|
|
6441
|
+
} catch {
|
|
6442
|
+
console.warn("Unable to interpret GeoJSON:", geoJSONObject);
|
|
6443
|
+
return;
|
|
6444
|
+
}
|
|
6445
|
+
}
|
|
6446
|
+
return features.flat();
|
|
6447
|
+
}
|
|
6448
|
+
readFeatureFromObject(geoJSONObject) {
|
|
6449
|
+
const geometries = this.readGeometriesFromObject(geoJSONObject["geometry"]);
|
|
6450
|
+
if (geometries.length > 0) {
|
|
6451
|
+
return new Feature({
|
|
6452
|
+
id: geoJSONObject["id"],
|
|
6453
|
+
geometries,
|
|
6454
|
+
properties: geoJSONObject["properties"]
|
|
6455
|
+
});
|
|
6456
|
+
}
|
|
6601
6457
|
return null;
|
|
6602
6458
|
}
|
|
6459
|
+
readGeometriesFromObject(geometry) {
|
|
6460
|
+
const geometries = [];
|
|
6461
|
+
if (geometry.type === "Polygon") {
|
|
6462
|
+
const polygon = this.readPolygonForCoordinates(geometry.coordinates);
|
|
6463
|
+
geometries.push(polygon);
|
|
6464
|
+
} else if (geometry.type === "MultiPolygon") {
|
|
6465
|
+
for (const polygonCoordinates of geometry.coordinates) {
|
|
6466
|
+
const polygon = this.readPolygonForCoordinates(polygonCoordinates);
|
|
6467
|
+
geometries.push(polygon);
|
|
6468
|
+
}
|
|
6469
|
+
} else if (geometry.type === "LineString") {
|
|
6470
|
+
const lineString = this.readLineStringForCoordinates(geometry.coordinates);
|
|
6471
|
+
geometries.push(lineString);
|
|
6472
|
+
} else if (geometry.type === "MultiLineString") {
|
|
6473
|
+
for (const lineStringCoordinates of geometry.coordinates) {
|
|
6474
|
+
const lineString = this.readLineStringForCoordinates(
|
|
6475
|
+
lineStringCoordinates
|
|
6476
|
+
);
|
|
6477
|
+
geometries.push(lineString);
|
|
6478
|
+
}
|
|
6479
|
+
} else if (geometry.type === "Point") {
|
|
6480
|
+
const point = this.readPointForCoordinates(geometry.coordinates);
|
|
6481
|
+
geometries.push(point);
|
|
6482
|
+
}
|
|
6483
|
+
return geometries;
|
|
6484
|
+
}
|
|
6485
|
+
readPolygonForCoordinates(coordinates) {
|
|
6486
|
+
const outerRing = coordinates[0];
|
|
6487
|
+
const extent = extentForCoordinates(outerRing);
|
|
6488
|
+
return new Polygon({ extent, coordinates });
|
|
6489
|
+
}
|
|
6490
|
+
readLineStringForCoordinates(coordinates) {
|
|
6491
|
+
const extent = extentForCoordinates(coordinates);
|
|
6492
|
+
return new LineString({ extent, coordinates });
|
|
6493
|
+
}
|
|
6494
|
+
readPointForCoordinates(coordinates) {
|
|
6495
|
+
return new Point({ coordinates });
|
|
6496
|
+
}
|
|
6497
|
+
}
|
|
6498
|
+
class FeatureCollection {
|
|
6603
6499
|
/**
|
|
6604
|
-
*
|
|
6605
|
-
* @param {
|
|
6500
|
+
* Create a feature collection from GeoJSON features.
|
|
6501
|
+
* @param {Object[]} geoJSON - The GeoJSON object
|
|
6502
|
+
* @returns {FeatureCollection} The feature collection
|
|
6606
6503
|
*/
|
|
6607
|
-
static
|
|
6608
|
-
const
|
|
6609
|
-
return new
|
|
6504
|
+
static fromGeoJSON(geoJSON) {
|
|
6505
|
+
const features = new GeoJSON().readFeaturesFromObject(geoJSON);
|
|
6506
|
+
return new FeatureCollection(features);
|
|
6610
6507
|
}
|
|
6611
6508
|
/**
|
|
6612
|
-
*
|
|
6613
|
-
* @
|
|
6614
|
-
* @param {
|
|
6615
|
-
* @param {number} [params.minZoom=0]
|
|
6616
|
-
* @param {number} [params.opacity=1]
|
|
6617
|
-
* @param {boolean} [params.declutter=true]
|
|
6618
|
-
* @param {boolean} [params.drawCollisionBoxes=false]
|
|
6509
|
+
* Create a feature collection.
|
|
6510
|
+
* @constructor
|
|
6511
|
+
* @param {import("./Feature").Feature[]} features - The features to put in the collection
|
|
6619
6512
|
*/
|
|
6620
|
-
constructor({
|
|
6621
|
-
|
|
6622
|
-
style: style2,
|
|
6623
|
-
minZoom = 0,
|
|
6624
|
-
opacity = 1,
|
|
6625
|
-
declutter = true,
|
|
6626
|
-
drawCollisionBoxes = false
|
|
6627
|
-
}) {
|
|
6628
|
-
this.source = source;
|
|
6629
|
-
this._style = style2;
|
|
6630
|
-
this.minZoom = minZoom;
|
|
6631
|
-
this.opacity = opacity;
|
|
6632
|
-
this.declutter = declutter;
|
|
6633
|
-
this.drawCollisionBoxes = drawCollisionBoxes;
|
|
6634
|
-
this.renderer = new TextLayerRenderer(this);
|
|
6635
|
-
this.dispatcher = new Dispatcher(this);
|
|
6513
|
+
constructor(features) {
|
|
6514
|
+
this.features = features;
|
|
6636
6515
|
}
|
|
6637
|
-
|
|
6638
|
-
|
|
6516
|
+
}
|
|
6517
|
+
class Style {
|
|
6518
|
+
constructor(properties) {
|
|
6519
|
+
this.stroke = properties == null ? void 0 : properties.stroke;
|
|
6520
|
+
this.fill = properties == null ? void 0 : properties.fill;
|
|
6521
|
+
this.text = properties == null ? void 0 : properties.text;
|
|
6639
6522
|
}
|
|
6640
|
-
|
|
6641
|
-
|
|
6642
|
-
|
|
6643
|
-
|
|
6523
|
+
clone() {
|
|
6524
|
+
return new Style({
|
|
6525
|
+
stroke: this.stroke,
|
|
6526
|
+
fill: this.fill,
|
|
6527
|
+
text: this.text
|
|
6644
6528
|
});
|
|
6645
|
-
return defaultStyle;
|
|
6646
6529
|
}
|
|
6647
|
-
|
|
6648
|
-
|
|
6649
|
-
|
|
6530
|
+
}
|
|
6531
|
+
function toRgba(color2, opacity = 1) {
|
|
6532
|
+
color2 = color2.replace(/\s+/g, "").toLowerCase();
|
|
6533
|
+
if (color2.startsWith("#")) {
|
|
6534
|
+
color2 = color2.replace(/^#/, "");
|
|
6535
|
+
if (color2.length === 3) {
|
|
6536
|
+
color2 = color2.split("").map((char) => char + char).join("");
|
|
6537
|
+
}
|
|
6538
|
+
let r = parseInt(color2.substring(0, 2), 16);
|
|
6539
|
+
let g = parseInt(color2.substring(2, 4), 16);
|
|
6540
|
+
let b = parseInt(color2.substring(4, 6), 16);
|
|
6541
|
+
return `rgba(${r}, ${g}, ${b}, ${opacity})`;
|
|
6650
6542
|
}
|
|
6651
|
-
|
|
6652
|
-
|
|
6653
|
-
|
|
6654
|
-
|
|
6655
|
-
|
|
6656
|
-
|
|
6657
|
-
|
|
6658
|
-
}, null);
|
|
6659
|
-
this._extent = extent;
|
|
6660
|
-
return extent;
|
|
6543
|
+
let rgbaMatch = color2.match(/^rgba?\((\d+),(\d+),(\d+)(?:,(\d+(\.\d+)?))?\)$/);
|
|
6544
|
+
if (rgbaMatch) {
|
|
6545
|
+
let r = parseInt(rgbaMatch[1], 10);
|
|
6546
|
+
let g = parseInt(rgbaMatch[2], 10);
|
|
6547
|
+
let b = parseInt(rgbaMatch[3], 10);
|
|
6548
|
+
let a = rgbaMatch[4] !== void 0 ? parseFloat(rgbaMatch[4]) : opacity;
|
|
6549
|
+
return `rgba(${r}, ${g}, ${b}, ${a})`;
|
|
6661
6550
|
}
|
|
6662
|
-
|
|
6663
|
-
|
|
6664
|
-
|
|
6665
|
-
|
|
6666
|
-
|
|
6667
|
-
|
|
6551
|
+
throw new Error("Unsupported color format");
|
|
6552
|
+
}
|
|
6553
|
+
const StrokePosition = {
|
|
6554
|
+
CENTER: "center",
|
|
6555
|
+
INSIDE: "inside"
|
|
6556
|
+
};
|
|
6557
|
+
class Stroke {
|
|
6558
|
+
constructor(options) {
|
|
6559
|
+
this.color = (options == null ? void 0 : options.color) || "#121212";
|
|
6560
|
+
this.width = (options == null ? void 0 : options.width) || 0.5;
|
|
6561
|
+
this.opacity = (options == null ? void 0 : options.opacity) || 1;
|
|
6562
|
+
this.position = (options == null ? void 0 : options.position) || StrokePosition.CENTER;
|
|
6563
|
+
this._getRgba = memoise(toRgba);
|
|
6668
6564
|
}
|
|
6669
|
-
|
|
6670
|
-
return this.
|
|
6565
|
+
getRgba() {
|
|
6566
|
+
return this._getRgba(this.color, this.opacity);
|
|
6671
6567
|
}
|
|
6672
6568
|
}
|
|
6673
|
-
class
|
|
6674
|
-
constructor(
|
|
6675
|
-
this.
|
|
6676
|
-
this.
|
|
6569
|
+
class Fill {
|
|
6570
|
+
constructor(options) {
|
|
6571
|
+
this.color = (options == null ? void 0 : options.color) || "#CCC";
|
|
6572
|
+
this.opacity = (options == null ? void 0 : options.opacity) || 1;
|
|
6573
|
+
this._getRgba = memoise(toRgba);
|
|
6677
6574
|
}
|
|
6678
|
-
|
|
6679
|
-
|
|
6680
|
-
|
|
6681
|
-
|
|
6682
|
-
|
|
6683
|
-
|
|
6684
|
-
|
|
6685
|
-
|
|
6686
|
-
|
|
6687
|
-
|
|
6688
|
-
|
|
6689
|
-
|
|
6690
|
-
|
|
6691
|
-
|
|
6692
|
-
|
|
6693
|
-
|
|
6694
|
-
|
|
6695
|
-
|
|
6696
|
-
|
|
6697
|
-
|
|
6698
|
-
|
|
6699
|
-
|
|
6700
|
-
|
|
6701
|
-
|
|
6702
|
-
|
|
6703
|
-
|
|
6704
|
-
|
|
6705
|
-
|
|
6706
|
-
|
|
6575
|
+
getRgba() {
|
|
6576
|
+
return this._getRgba(this.color, this.opacity);
|
|
6577
|
+
}
|
|
6578
|
+
}
|
|
6579
|
+
const TextAnchor = {
|
|
6580
|
+
TOP: "top",
|
|
6581
|
+
BOTTOM: "bottom",
|
|
6582
|
+
LEFT: "left",
|
|
6583
|
+
RIGHT: "right",
|
|
6584
|
+
CENTER: "center",
|
|
6585
|
+
TOP_LEFT: "top-left",
|
|
6586
|
+
TOP_RIGHT: "top-right",
|
|
6587
|
+
BOTTOM_LEFT: "bottom-left",
|
|
6588
|
+
BOTTOM_RIGHT: "bottom-right"
|
|
6589
|
+
};
|
|
6590
|
+
class Text {
|
|
6591
|
+
/**
|
|
6592
|
+
* Create a text element style
|
|
6593
|
+
* @constructor
|
|
6594
|
+
* @param {TextStyle} options - Style options
|
|
6595
|
+
*/
|
|
6596
|
+
constructor(options) {
|
|
6597
|
+
this.content = options == null ? void 0 : options.content;
|
|
6598
|
+
this.anchor = (options == null ? void 0 : options.anchor) || TextAnchor.CENTER;
|
|
6599
|
+
this.fontFamily = (options == null ? void 0 : options.fontFamily) || "var(--text-sans)";
|
|
6600
|
+
this.fontSize = (options == null ? void 0 : options.fontSize) || "17px";
|
|
6601
|
+
this.fontWeight = (options == null ? void 0 : options.fontWeight) || "400";
|
|
6602
|
+
this.lineHeight = (options == null ? void 0 : options.lineHeight) || 1.3;
|
|
6603
|
+
this.color = (options == null ? void 0 : options.color) || "#121212";
|
|
6604
|
+
this.textShadow = (options == null ? void 0 : options.textShadow) || "1px 1px 0px #f6f6f6, -1px -1px 0px #f6f6f6, -1px 1px 0px #f6f6f6, 1px -1px #f6f6f6";
|
|
6605
|
+
this.radialOffset = (options == null ? void 0 : options.radialOffset) || 0;
|
|
6606
|
+
}
|
|
6607
|
+
/**
|
|
6608
|
+
* Get the relative translation for the text element based on its anchor. The translation does not take `radialOffset` into account
|
|
6609
|
+
* @private
|
|
6610
|
+
* @return {{x: number, y: number}} - The x and y translation in percentage points
|
|
6611
|
+
*/
|
|
6612
|
+
_getRelativeTranslation() {
|
|
6613
|
+
switch (this.anchor) {
|
|
6614
|
+
case TextAnchor.TOP:
|
|
6615
|
+
return { x: -50, y: 0 };
|
|
6616
|
+
case TextAnchor.BOTTOM:
|
|
6617
|
+
return { x: -50, y: -100 };
|
|
6618
|
+
case TextAnchor.LEFT:
|
|
6619
|
+
return { x: 0, y: -50 };
|
|
6620
|
+
case TextAnchor.RIGHT:
|
|
6621
|
+
return { x: -100, y: -50 };
|
|
6622
|
+
case TextAnchor.CENTER:
|
|
6623
|
+
return { x: -50, y: -50 };
|
|
6624
|
+
case TextAnchor.TOP_LEFT:
|
|
6625
|
+
return { x: 0, y: 0 };
|
|
6626
|
+
case TextAnchor.TOP_RIGHT:
|
|
6627
|
+
return { x: 100, y: 0 };
|
|
6628
|
+
case TextAnchor.BOTTOM_LEFT:
|
|
6629
|
+
return { x: 0, y: 100 };
|
|
6630
|
+
case TextAnchor.BOTTOM_RIGHT:
|
|
6631
|
+
return { x: 100, y: 100 };
|
|
6632
|
+
default:
|
|
6633
|
+
return { x: 0, y: 0 };
|
|
6707
6634
|
}
|
|
6708
|
-
context.restore();
|
|
6709
|
-
return container2;
|
|
6710
|
-
}
|
|
6711
|
-
getOrCreateContainer(targetElement, sizeInPixels) {
|
|
6712
|
-
let container2 = null;
|
|
6713
|
-
let containerReused = false;
|
|
6714
|
-
let canvas = targetElement && targetElement.firstElementChild;
|
|
6715
|
-
if (canvas instanceof HTMLCanvasElement) {
|
|
6716
|
-
container2 = targetElement;
|
|
6717
|
-
containerReused = true;
|
|
6718
|
-
} else if (this._container) {
|
|
6719
|
-
container2 = this._container;
|
|
6720
|
-
} else {
|
|
6721
|
-
container2 = this.createContainer();
|
|
6722
|
-
}
|
|
6723
|
-
if (!containerReused) {
|
|
6724
|
-
const canvas2 = container2.firstElementChild;
|
|
6725
|
-
canvas2.width = sizeInPixels[0];
|
|
6726
|
-
canvas2.height = sizeInPixels[1];
|
|
6727
|
-
}
|
|
6728
|
-
this._container = container2;
|
|
6729
|
-
return container2;
|
|
6730
|
-
}
|
|
6731
|
-
createContainer() {
|
|
6732
|
-
const container2 = document.createElement("div");
|
|
6733
|
-
container2.className = "gv-map-layer";
|
|
6734
|
-
let style2 = container2.style;
|
|
6735
|
-
style2.position = "absolute";
|
|
6736
|
-
style2.width = "100%";
|
|
6737
|
-
style2.height = "100%";
|
|
6738
|
-
const canvas = document.createElement("canvas");
|
|
6739
|
-
style2 = canvas.style;
|
|
6740
|
-
style2.position = "absolute";
|
|
6741
|
-
style2.width = "100%";
|
|
6742
|
-
style2.height = "100%";
|
|
6743
|
-
container2.appendChild(canvas);
|
|
6744
|
-
return container2;
|
|
6745
|
-
}
|
|
6746
|
-
}
|
|
6747
|
-
class VectorLayer {
|
|
6748
|
-
/** @param {VectorLayerComponentProps} props */
|
|
6749
|
-
static Component({ features, style: style2, minZoom, opacity, hitDetectionEnabled }) {
|
|
6750
|
-
const { registerLayer } = useContext(MapContext);
|
|
6751
|
-
const layer = useMemo(
|
|
6752
|
-
() => VectorLayer.with(features, {
|
|
6753
|
-
style: style2,
|
|
6754
|
-
minZoom,
|
|
6755
|
-
opacity,
|
|
6756
|
-
hitDetectionEnabled
|
|
6757
|
-
}),
|
|
6758
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
6759
|
-
[features, minZoom, opacity, hitDetectionEnabled]
|
|
6760
|
-
);
|
|
6761
|
-
registerLayer(layer);
|
|
6762
|
-
useEffect(() => {
|
|
6763
|
-
layer.style = style2;
|
|
6764
|
-
}, [style2]);
|
|
6765
|
-
return null;
|
|
6766
6635
|
}
|
|
6767
6636
|
/**
|
|
6768
|
-
*
|
|
6769
|
-
* @param {
|
|
6637
|
+
* Get the translation for the text element in pixels
|
|
6638
|
+
* @param {number} elementWidth - The width of the element
|
|
6639
|
+
* @param {number} elementHeight - The height of the element
|
|
6640
|
+
* @return {{x: number, y: number}} - The x and y translation in pixels
|
|
6770
6641
|
*/
|
|
6771
|
-
|
|
6772
|
-
const
|
|
6773
|
-
|
|
6642
|
+
getTranslation(elementWidth, elementHeight) {
|
|
6643
|
+
const translate = this._getRelativeTranslation();
|
|
6644
|
+
let x = translate.x / 100 * elementWidth;
|
|
6645
|
+
let y = translate.y / 100 * elementHeight;
|
|
6646
|
+
const radialOffsetInPixels = this.radialOffset * this.fontSize.replace("px", "");
|
|
6647
|
+
switch (this.anchor) {
|
|
6648
|
+
case TextAnchor.TOP:
|
|
6649
|
+
y += radialOffsetInPixels;
|
|
6650
|
+
break;
|
|
6651
|
+
case TextAnchor.BOTTOM:
|
|
6652
|
+
y -= radialOffsetInPixels;
|
|
6653
|
+
break;
|
|
6654
|
+
case TextAnchor.LEFT:
|
|
6655
|
+
x += radialOffsetInPixels;
|
|
6656
|
+
break;
|
|
6657
|
+
case TextAnchor.RIGHT:
|
|
6658
|
+
x -= radialOffsetInPixels;
|
|
6659
|
+
break;
|
|
6660
|
+
case TextAnchor.CENTER:
|
|
6661
|
+
break;
|
|
6662
|
+
case TextAnchor.TOP_LEFT:
|
|
6663
|
+
x += radialOffsetInPixels;
|
|
6664
|
+
y += radialOffsetInPixels;
|
|
6665
|
+
break;
|
|
6666
|
+
case TextAnchor.TOP_RIGHT:
|
|
6667
|
+
x -= radialOffsetInPixels;
|
|
6668
|
+
y += radialOffsetInPixels;
|
|
6669
|
+
break;
|
|
6670
|
+
case TextAnchor.BOTTOM_LEFT:
|
|
6671
|
+
x += radialOffsetInPixels;
|
|
6672
|
+
y -= radialOffsetInPixels;
|
|
6673
|
+
break;
|
|
6674
|
+
case TextAnchor.BOTTOM_RIGHT:
|
|
6675
|
+
x -= radialOffsetInPixels;
|
|
6676
|
+
y -= radialOffsetInPixels;
|
|
6677
|
+
break;
|
|
6678
|
+
}
|
|
6679
|
+
return { x, y };
|
|
6774
6680
|
}
|
|
6775
6681
|
/**
|
|
6776
|
-
*
|
|
6777
|
-
* @param {
|
|
6778
|
-
* @param {
|
|
6779
|
-
* @
|
|
6780
|
-
* @param {number} [params.opacity=1]
|
|
6781
|
-
* @param {boolean} [params.hitDetectionEnabled=false]
|
|
6682
|
+
* Get the transform for the text element
|
|
6683
|
+
* @param {number} elementWidth - The width of the element
|
|
6684
|
+
* @param {number} elementHeight - The height of the element
|
|
6685
|
+
* @return {string} - The transform for the text element
|
|
6782
6686
|
*/
|
|
6783
|
-
|
|
6784
|
-
|
|
6785
|
-
|
|
6786
|
-
minZoom = 0,
|
|
6787
|
-
opacity = 1,
|
|
6788
|
-
hitDetectionEnabled = true
|
|
6789
|
-
}) {
|
|
6790
|
-
this.dispatcher = new Dispatcher(this);
|
|
6791
|
-
this.renderer = new VectorLayerRenderer(this);
|
|
6792
|
-
this.source = source;
|
|
6793
|
-
this._style = style2;
|
|
6794
|
-
this.minZoom = minZoom;
|
|
6795
|
-
this.opacity = opacity;
|
|
6796
|
-
this.hitDetectionEnabled = hitDetectionEnabled;
|
|
6797
|
-
}
|
|
6798
|
-
get source() {
|
|
6799
|
-
return this._source;
|
|
6687
|
+
getTransform(elementWidth, elementHeight) {
|
|
6688
|
+
const { x, y } = this.getTranslation(elementWidth, elementHeight);
|
|
6689
|
+
return `translate(${x}px, ${y}px)`;
|
|
6800
6690
|
}
|
|
6801
|
-
|
|
6802
|
-
|
|
6803
|
-
|
|
6804
|
-
|
|
6805
|
-
this._source = source;
|
|
6806
|
-
source.on(MapEvent.CHANGE, () => {
|
|
6807
|
-
this._extent = null;
|
|
6808
|
-
this.dispatcher.dispatch(MapEvent.CHANGE);
|
|
6809
|
-
});
|
|
6691
|
+
}
|
|
6692
|
+
class FeatureRenderer {
|
|
6693
|
+
constructor() {
|
|
6694
|
+
this.drawingFunction = geoPath();
|
|
6810
6695
|
}
|
|
6811
|
-
|
|
6812
|
-
this.
|
|
6696
|
+
setStyle(style2) {
|
|
6697
|
+
this.style = style2;
|
|
6813
6698
|
}
|
|
6814
|
-
|
|
6815
|
-
this.
|
|
6699
|
+
setFeature(feature) {
|
|
6700
|
+
this.feature = feature;
|
|
6816
6701
|
}
|
|
6817
|
-
|
|
6818
|
-
if (this.
|
|
6819
|
-
|
|
6820
|
-
|
|
6821
|
-
|
|
6822
|
-
|
|
6702
|
+
render(frameState, context) {
|
|
6703
|
+
if (!this.style) {
|
|
6704
|
+
return;
|
|
6705
|
+
}
|
|
6706
|
+
const feature = this.feature;
|
|
6707
|
+
const { projection, transform, pixelRatio } = frameState.viewState;
|
|
6708
|
+
const { stroke, fill } = this.style;
|
|
6709
|
+
const geometries = feature.getProjectedGeometries(projection);
|
|
6710
|
+
if (frameState.debug) {
|
|
6711
|
+
try {
|
|
6712
|
+
validateGeometries(geometries);
|
|
6713
|
+
} catch {
|
|
6714
|
+
console.error(
|
|
6715
|
+
`Invalid geometry. Feature skipped during rendering. Click here to inspect geometry: ${generateDebugUrl(feature)}
|
|
6716
|
+
`,
|
|
6717
|
+
feature
|
|
6718
|
+
);
|
|
6719
|
+
}
|
|
6720
|
+
}
|
|
6721
|
+
this.drawPath(geometries, context);
|
|
6722
|
+
if (fill) {
|
|
6723
|
+
context.fillStyle = fill.getRgba();
|
|
6724
|
+
context.fill();
|
|
6725
|
+
}
|
|
6726
|
+
if (stroke) {
|
|
6727
|
+
context.save();
|
|
6728
|
+
this.drawStroke(frameState, context, {
|
|
6729
|
+
style: stroke.getRgba(),
|
|
6730
|
+
width: stroke.width / transform.k * pixelRatio,
|
|
6731
|
+
position: stroke.position
|
|
6732
|
+
});
|
|
6733
|
+
context.restore();
|
|
6734
|
+
}
|
|
6823
6735
|
}
|
|
6824
|
-
|
|
6825
|
-
this.
|
|
6826
|
-
|
|
6736
|
+
drawPath(geometries, context, clipPath = false) {
|
|
6737
|
+
this.drawingFunction.context(context);
|
|
6738
|
+
context.beginPath();
|
|
6739
|
+
for (const geometry of geometries) {
|
|
6740
|
+
this.drawingFunction(geometry);
|
|
6741
|
+
}
|
|
6742
|
+
if (clipPath) {
|
|
6743
|
+
context.clip();
|
|
6744
|
+
} else {
|
|
6745
|
+
context.closePath();
|
|
6746
|
+
}
|
|
6827
6747
|
}
|
|
6828
|
-
|
|
6829
|
-
const
|
|
6830
|
-
|
|
6831
|
-
|
|
6832
|
-
|
|
6833
|
-
|
|
6748
|
+
drawStroke(frameState, context, { style: style2, width: strokeWidth, position }) {
|
|
6749
|
+
const { projection } = frameState.viewState;
|
|
6750
|
+
context.lineWidth = strokeWidth;
|
|
6751
|
+
context.strokeStyle = style2;
|
|
6752
|
+
if (position === StrokePosition.INSIDE) {
|
|
6753
|
+
context.lineWidth = strokeWidth * 2;
|
|
6754
|
+
const geometries = this.feature.getProjectedGeometries(projection);
|
|
6755
|
+
this.drawPath(geometries, context, true);
|
|
6756
|
+
}
|
|
6757
|
+
context.stroke();
|
|
6834
6758
|
}
|
|
6835
|
-
|
|
6836
|
-
|
|
6837
|
-
|
|
6838
|
-
|
|
6839
|
-
|
|
6840
|
-
|
|
6841
|
-
|
|
6759
|
+
createCanvas(width, height) {
|
|
6760
|
+
const canvas = document.createElement("canvas");
|
|
6761
|
+
canvas.width = width;
|
|
6762
|
+
canvas.height = height;
|
|
6763
|
+
return canvas;
|
|
6764
|
+
}
|
|
6765
|
+
getProjectedExtent(projection) {
|
|
6766
|
+
const geometries = this.feature.getProjectedGeometries(projection);
|
|
6767
|
+
const extent = geometries.reduce((combinedExtent, geometry) => {
|
|
6768
|
+
const bounds = this.drawingFunction.bounds(geometry);
|
|
6769
|
+
if (!combinedExtent) return bounds;
|
|
6770
|
+
return combineExtents(bounds, combinedExtent);
|
|
6842
6771
|
}, null);
|
|
6843
|
-
this._extent = extent;
|
|
6844
6772
|
return extent;
|
|
6845
6773
|
}
|
|
6846
|
-
findFeatures(coordinate) {
|
|
6847
|
-
if (!this.hitDetectionEnabled) return;
|
|
6848
|
-
return this.source.getFeaturesAtCoordinate(coordinate);
|
|
6849
|
-
}
|
|
6850
|
-
renderFrame(frameState, targetElement) {
|
|
6851
|
-
return this.renderer.renderFrame(frameState, targetElement);
|
|
6852
|
-
}
|
|
6853
6774
|
}
|
|
6854
|
-
|
|
6855
|
-
|
|
6856
|
-
|
|
6857
|
-
|
|
6858
|
-
);
|
|
6775
|
+
class TextLayerRenderer {
|
|
6776
|
+
constructor(layer) {
|
|
6777
|
+
this.layer = layer;
|
|
6778
|
+
this.featureRenderer = new FeatureRenderer();
|
|
6779
|
+
this._element = document.createElement("div");
|
|
6780
|
+
this._element.className = "gv-text-layer";
|
|
6781
|
+
const style2 = this._element.style;
|
|
6782
|
+
style2.position = "absolute";
|
|
6783
|
+
style2.width = "100%";
|
|
6784
|
+
style2.height = "100%";
|
|
6785
|
+
style2.pointerEvents = "none";
|
|
6786
|
+
style2.overflow = "hidden";
|
|
6859
6787
|
}
|
|
6860
|
-
|
|
6861
|
-
|
|
6862
|
-
const
|
|
6863
|
-
const
|
|
6864
|
-
|
|
6865
|
-
|
|
6866
|
-
|
|
6867
|
-
|
|
6868
|
-
|
|
6869
|
-
|
|
6870
|
-
|
|
6871
|
-
|
|
6872
|
-
|
|
6873
|
-
|
|
6874
|
-
newGeometries[e].getOuterRing(),
|
|
6875
|
-
{ string: false }
|
|
6788
|
+
renderFrame(frameState, targetElement) {
|
|
6789
|
+
if (this.layer.opacity === 0) return targetElement;
|
|
6790
|
+
const { declutterTree } = frameState;
|
|
6791
|
+
const { projection, viewPortSize, sizeInPixels, visibleExtent, transform } = frameState.viewState;
|
|
6792
|
+
this._element.style.opacity = this.layer.opacity;
|
|
6793
|
+
const source = this.layer.source;
|
|
6794
|
+
const features = source.getFeaturesInExtent(visibleExtent);
|
|
6795
|
+
const textElements = [];
|
|
6796
|
+
for (const feature of features) {
|
|
6797
|
+
const geometries = feature.getProjectedGeometries(projection);
|
|
6798
|
+
const point = geometries.find((d2) => d2.type === "Point");
|
|
6799
|
+
if (!point) {
|
|
6800
|
+
throw new Error(
|
|
6801
|
+
`Expected Point geometry for feature in TextLayer: ${feature}`
|
|
6876
6802
|
);
|
|
6877
|
-
geometryInterpolators.push({
|
|
6878
|
-
type: "default",
|
|
6879
|
-
interpolator: shapeInterpolator
|
|
6880
|
-
});
|
|
6881
6803
|
}
|
|
6882
|
-
|
|
6883
|
-
const
|
|
6884
|
-
|
|
6885
|
-
|
|
6886
|
-
|
|
6887
|
-
|
|
6888
|
-
|
|
6889
|
-
|
|
6890
|
-
|
|
6891
|
-
|
|
6892
|
-
|
|
6893
|
-
|
|
6894
|
-
|
|
6895
|
-
newGeometries[0].getOuterRing(),
|
|
6896
|
-
{ string: false, single: true }
|
|
6897
|
-
);
|
|
6898
|
-
geometryInterpolators.push({
|
|
6899
|
-
type: "combine",
|
|
6900
|
-
interpolator: combinationInterpolator
|
|
6804
|
+
const styleFunction2 = feature.getStyleFunction() || this.layer.getStyleFunction();
|
|
6805
|
+
const featureStyle = styleFunction2(feature);
|
|
6806
|
+
const textElement = this.getTextElementWithID(feature.uid);
|
|
6807
|
+
textElement.innerText = featureStyle.text.content;
|
|
6808
|
+
const [relativeX, relativeY] = transform.apply(point.coordinates).map((d2, i) => d2 / sizeInPixels[i]);
|
|
6809
|
+
const position = {
|
|
6810
|
+
left: `${relativeX * 100}%`,
|
|
6811
|
+
top: `${relativeY * 100}%`
|
|
6812
|
+
};
|
|
6813
|
+
this.styleTextElement(textElement, featureStyle.text, position);
|
|
6814
|
+
const bbox = this.getElementBBox(textElement, featureStyle.text, {
|
|
6815
|
+
x: relativeX * viewPortSize[0],
|
|
6816
|
+
y: relativeY * viewPortSize[1]
|
|
6901
6817
|
});
|
|
6902
|
-
|
|
6903
|
-
|
|
6904
|
-
|
|
6905
|
-
);
|
|
6818
|
+
if (declutterTree.collides(bbox)) {
|
|
6819
|
+
continue;
|
|
6820
|
+
}
|
|
6821
|
+
declutterTree.insert(bbox);
|
|
6822
|
+
if (this.layer.drawCollisionBoxes) {
|
|
6823
|
+
const collisionBoxDebugElement = this.getCollisionBoxElement(bbox);
|
|
6824
|
+
textElements.push(collisionBoxDebugElement);
|
|
6825
|
+
}
|
|
6826
|
+
textElements.push(textElement);
|
|
6906
6827
|
}
|
|
6907
|
-
|
|
6828
|
+
replaceChildren(this._element, textElements);
|
|
6829
|
+
return this._element;
|
|
6908
6830
|
}
|
|
6909
|
-
|
|
6910
|
-
|
|
6911
|
-
|
|
6912
|
-
|
|
6913
|
-
|
|
6914
|
-
|
|
6915
|
-
|
|
6916
|
-
|
|
6917
|
-
|
|
6918
|
-
|
|
6919
|
-
|
|
6920
|
-
|
|
6921
|
-
|
|
6922
|
-
|
|
6923
|
-
|
|
6924
|
-
|
|
6925
|
-
|
|
6926
|
-
|
|
6927
|
-
|
|
6928
|
-
|
|
6929
|
-
|
|
6930
|
-
|
|
6931
|
-
|
|
6932
|
-
|
|
6933
|
-
|
|
6934
|
-
|
|
6935
|
-
|
|
6936
|
-
|
|
6937
|
-
|
|
6938
|
-
|
|
6831
|
+
getTextElementWithID(id2) {
|
|
6832
|
+
const elementId = `text-feature-${id2}`;
|
|
6833
|
+
let textElement = this._element.querySelector(`#${elementId}`);
|
|
6834
|
+
if (!textElement) {
|
|
6835
|
+
textElement = document.createElement("div");
|
|
6836
|
+
textElement.id = elementId;
|
|
6837
|
+
}
|
|
6838
|
+
return textElement;
|
|
6839
|
+
}
|
|
6840
|
+
styleTextElement(element, textStyle, position) {
|
|
6841
|
+
const style2 = element.style;
|
|
6842
|
+
style2.position = "absolute";
|
|
6843
|
+
style2.left = position.left;
|
|
6844
|
+
style2.top = position.top;
|
|
6845
|
+
style2.textAlign = "center";
|
|
6846
|
+
style2.whiteSpace = "nowrap";
|
|
6847
|
+
style2.fontFamily = textStyle.fontFamily;
|
|
6848
|
+
style2.fontSize = textStyle.fontSize;
|
|
6849
|
+
style2.fontWeight = textStyle.fontWeight;
|
|
6850
|
+
style2.lineHeight = textStyle.lineHeight;
|
|
6851
|
+
style2.color = textStyle.color;
|
|
6852
|
+
style2.textShadow = textStyle.textShadow;
|
|
6853
|
+
const { width, height } = this.getElementSize(element);
|
|
6854
|
+
style2.transform = textStyle.getTransform(width, height);
|
|
6855
|
+
}
|
|
6856
|
+
getElementSize(element) {
|
|
6857
|
+
if (!element.parentElement) {
|
|
6858
|
+
document.body.appendChild(element);
|
|
6859
|
+
}
|
|
6860
|
+
const { width, height } = element.getBoundingClientRect();
|
|
6861
|
+
if (element.parentElement !== this._element) {
|
|
6862
|
+
element.remove();
|
|
6863
|
+
}
|
|
6864
|
+
return { width, height };
|
|
6865
|
+
}
|
|
6866
|
+
getElementBBox(element, textStyle, position) {
|
|
6867
|
+
const collissionPadding = {
|
|
6868
|
+
top: 2,
|
|
6869
|
+
right: 2,
|
|
6870
|
+
bottom: 2,
|
|
6871
|
+
left: 2
|
|
6872
|
+
};
|
|
6873
|
+
const { width, height } = this.getElementSize(element);
|
|
6874
|
+
const { x: translateX, y: translateY } = textStyle.getTranslation(
|
|
6875
|
+
width,
|
|
6876
|
+
height
|
|
6877
|
+
);
|
|
6878
|
+
const minX = Math.floor(position.x + translateX - collissionPadding.left);
|
|
6879
|
+
const minY = Math.floor(position.y + translateY - collissionPadding.top);
|
|
6880
|
+
return {
|
|
6881
|
+
minX,
|
|
6882
|
+
minY,
|
|
6883
|
+
maxX: Math.ceil(
|
|
6884
|
+
minX + width + collissionPadding.left + collissionPadding.right
|
|
6885
|
+
),
|
|
6886
|
+
maxY: Math.ceil(
|
|
6887
|
+
minY + height + collissionPadding.top + collissionPadding.bottom
|
|
6888
|
+
)
|
|
6889
|
+
};
|
|
6890
|
+
}
|
|
6891
|
+
getCollisionBoxElement(bbox) {
|
|
6892
|
+
const element = document.createElement("div");
|
|
6893
|
+
const style2 = element.style;
|
|
6894
|
+
style2.position = "absolute";
|
|
6895
|
+
style2.left = `${bbox.minX}px`;
|
|
6896
|
+
style2.top = `${bbox.minY}px`;
|
|
6897
|
+
style2.width = `${bbox.maxX - bbox.minX}px`;
|
|
6898
|
+
style2.height = `${bbox.maxY - bbox.minY}px`;
|
|
6899
|
+
style2.border = "1px solid red";
|
|
6900
|
+
return element;
|
|
6901
|
+
}
|
|
6902
|
+
}
|
|
6903
|
+
class TinyQueue {
|
|
6904
|
+
constructor(data = [], compare = defaultCompare) {
|
|
6905
|
+
this.data = data;
|
|
6906
|
+
this.length = this.data.length;
|
|
6907
|
+
this.compare = compare;
|
|
6908
|
+
if (this.length > 0) {
|
|
6909
|
+
for (let i = (this.length >> 1) - 1; i >= 0; i--) this._down(i);
|
|
6910
|
+
}
|
|
6911
|
+
}
|
|
6912
|
+
push(item) {
|
|
6913
|
+
this.data.push(item);
|
|
6914
|
+
this.length++;
|
|
6915
|
+
this._up(this.length - 1);
|
|
6916
|
+
}
|
|
6917
|
+
pop() {
|
|
6918
|
+
if (this.length === 0) return void 0;
|
|
6919
|
+
const top = this.data[0];
|
|
6920
|
+
const bottom = this.data.pop();
|
|
6921
|
+
this.length--;
|
|
6922
|
+
if (this.length > 0) {
|
|
6923
|
+
this.data[0] = bottom;
|
|
6924
|
+
this._down(0);
|
|
6925
|
+
}
|
|
6926
|
+
return top;
|
|
6927
|
+
}
|
|
6928
|
+
peek() {
|
|
6929
|
+
return this.data[0];
|
|
6930
|
+
}
|
|
6931
|
+
_up(pos) {
|
|
6932
|
+
const { data, compare } = this;
|
|
6933
|
+
const item = data[pos];
|
|
6934
|
+
while (pos > 0) {
|
|
6935
|
+
const parent = pos - 1 >> 1;
|
|
6936
|
+
const current = data[parent];
|
|
6937
|
+
if (compare(item, current) >= 0) break;
|
|
6938
|
+
data[pos] = current;
|
|
6939
|
+
pos = parent;
|
|
6940
|
+
}
|
|
6941
|
+
data[pos] = item;
|
|
6942
|
+
}
|
|
6943
|
+
_down(pos) {
|
|
6944
|
+
const { data, compare } = this;
|
|
6945
|
+
const halfLength = this.length >> 1;
|
|
6946
|
+
const item = data[pos];
|
|
6947
|
+
while (pos < halfLength) {
|
|
6948
|
+
let left = (pos << 1) + 1;
|
|
6949
|
+
let best = data[left];
|
|
6950
|
+
const right = left + 1;
|
|
6951
|
+
if (right < this.length && compare(data[right], best) < 0) {
|
|
6952
|
+
left = right;
|
|
6953
|
+
best = data[right];
|
|
6939
6954
|
}
|
|
6940
|
-
|
|
6941
|
-
|
|
6955
|
+
if (compare(best, item) >= 0) break;
|
|
6956
|
+
data[pos] = best;
|
|
6957
|
+
pos = left;
|
|
6942
6958
|
}
|
|
6943
|
-
|
|
6944
|
-
}
|
|
6959
|
+
data[pos] = item;
|
|
6960
|
+
}
|
|
6945
6961
|
}
|
|
6946
|
-
function
|
|
6947
|
-
|
|
6948
|
-
firstStyle.fill,
|
|
6949
|
-
secondStyle.fill,
|
|
6950
|
-
interpolateColors,
|
|
6951
|
-
interpolateNumbers
|
|
6952
|
-
);
|
|
6953
|
-
const strokeInterpolator = interpolateStroke(
|
|
6954
|
-
firstStyle.stroke,
|
|
6955
|
-
secondStyle.stroke,
|
|
6956
|
-
interpolateColors,
|
|
6957
|
-
interpolateNumbers
|
|
6958
|
-
);
|
|
6959
|
-
return (t) => {
|
|
6960
|
-
return new Style({
|
|
6961
|
-
fill: fillInterpolator(t),
|
|
6962
|
-
stroke: strokeInterpolator(t)
|
|
6963
|
-
});
|
|
6964
|
-
};
|
|
6962
|
+
function defaultCompare(a, b) {
|
|
6963
|
+
return a < b ? -1 : a > b ? 1 : 0;
|
|
6965
6964
|
}
|
|
6966
|
-
function
|
|
6967
|
-
|
|
6968
|
-
|
|
6969
|
-
|
|
6970
|
-
);
|
|
6971
|
-
|
|
6972
|
-
(
|
|
6973
|
-
|
|
6974
|
-
|
|
6975
|
-
|
|
6976
|
-
|
|
6977
|
-
|
|
6978
|
-
|
|
6979
|
-
|
|
6980
|
-
|
|
6965
|
+
function knn(tree, x, y, n2, predicate, maxDistance) {
|
|
6966
|
+
let node = tree.data;
|
|
6967
|
+
const result = [];
|
|
6968
|
+
const toBBox = tree.toBBox;
|
|
6969
|
+
const queue = new TinyQueue(void 0, compareDist);
|
|
6970
|
+
while (node) {
|
|
6971
|
+
for (let i = 0; i < node.children.length; i++) {
|
|
6972
|
+
const child = node.children[i];
|
|
6973
|
+
const dist = boxDist(x, y, node.leaf ? toBBox(child) : child);
|
|
6974
|
+
{
|
|
6975
|
+
queue.push({
|
|
6976
|
+
node: child,
|
|
6977
|
+
isItem: node.leaf,
|
|
6978
|
+
dist
|
|
6979
|
+
});
|
|
6980
|
+
}
|
|
6981
|
+
}
|
|
6982
|
+
while (queue.length && queue.peek().isItem) {
|
|
6983
|
+
const candidate = queue.pop().node;
|
|
6984
|
+
if (!predicate || predicate(candidate))
|
|
6985
|
+
result.push(candidate);
|
|
6986
|
+
if (result.length === n2) return result;
|
|
6987
|
+
}
|
|
6988
|
+
node = queue.pop();
|
|
6989
|
+
if (node) node = node.node;
|
|
6990
|
+
}
|
|
6991
|
+
return result;
|
|
6981
6992
|
}
|
|
6982
|
-
function
|
|
6983
|
-
|
|
6984
|
-
|
|
6985
|
-
|
|
6986
|
-
);
|
|
6987
|
-
|
|
6988
|
-
|
|
6989
|
-
|
|
6990
|
-
|
|
6991
|
-
|
|
6992
|
-
|
|
6993
|
-
|
|
6994
|
-
|
|
6995
|
-
|
|
6996
|
-
|
|
6997
|
-
|
|
6998
|
-
|
|
6999
|
-
|
|
6993
|
+
function compareDist(a, b) {
|
|
6994
|
+
return a.dist - b.dist;
|
|
6995
|
+
}
|
|
6996
|
+
function boxDist(x, y, box) {
|
|
6997
|
+
const dx = axisDist(x, box.minX, box.maxX), dy = axisDist(y, box.minY, box.maxY);
|
|
6998
|
+
return dx * dx + dy * dy;
|
|
6999
|
+
}
|
|
7000
|
+
function axisDist(k, min, max) {
|
|
7001
|
+
return k < min ? min - k : k <= max ? 0 : k - max;
|
|
7002
|
+
}
|
|
7003
|
+
class VectorSource {
|
|
7004
|
+
constructor({ features }) {
|
|
7005
|
+
this.dispatcher = new Dispatcher(this);
|
|
7006
|
+
this._featuresRtree = new RBush();
|
|
7007
|
+
this.setFeatures(features);
|
|
7008
|
+
}
|
|
7009
|
+
tearDown() {
|
|
7010
|
+
this.dispatcher = null;
|
|
7011
|
+
}
|
|
7012
|
+
getFeatures() {
|
|
7013
|
+
return this._features;
|
|
7014
|
+
}
|
|
7015
|
+
getFeaturesAtCoordinate(coordinate) {
|
|
7016
|
+
const [lon, lat] = coordinate;
|
|
7017
|
+
const features = knn(
|
|
7018
|
+
this._featuresRtree,
|
|
7019
|
+
lon,
|
|
7020
|
+
lat,
|
|
7021
|
+
10,
|
|
7022
|
+
(d2) => d2.feature.containsCoordinate(coordinate)
|
|
7023
|
+
).map((d2) => {
|
|
7024
|
+
const midX = d2.minX + (d2.minX + d2.maxX) / 2;
|
|
7025
|
+
const midY = d2.minY + (d2.minY + d2.maxY) / 2;
|
|
7026
|
+
d2.distance = Math.hypot(midX - lon, midY - lat);
|
|
7027
|
+
return d2;
|
|
7000
7028
|
});
|
|
7001
|
-
|
|
7029
|
+
features.sort((a, b) => a.distance - b.distance);
|
|
7030
|
+
return features.map((d2) => d2.feature);
|
|
7031
|
+
}
|
|
7032
|
+
getFeaturesInExtent(extent) {
|
|
7033
|
+
const [minX, minY, maxX, maxY] = extent;
|
|
7034
|
+
return this._featuresRtree.search({ minX, minY, maxX, maxY }).map((d2) => d2.feature);
|
|
7035
|
+
}
|
|
7036
|
+
setFeatures(features) {
|
|
7037
|
+
this._featuresRtree.clear();
|
|
7038
|
+
for (const feature of features) {
|
|
7039
|
+
const [minX, minY, maxX, maxY] = feature.getExtent();
|
|
7040
|
+
this._featuresRtree.insert({
|
|
7041
|
+
minX: Math.floor(minX),
|
|
7042
|
+
minY: Math.floor(minY),
|
|
7043
|
+
maxX: Math.ceil(maxX),
|
|
7044
|
+
maxY: Math.ceil(maxY),
|
|
7045
|
+
feature
|
|
7046
|
+
});
|
|
7047
|
+
}
|
|
7048
|
+
this._features = features;
|
|
7049
|
+
this.dispatcher.dispatch(MapEvent.CHANGE);
|
|
7050
|
+
}
|
|
7002
7051
|
}
|
|
7003
|
-
class
|
|
7052
|
+
class TextLayer {
|
|
7053
|
+
/** @param {TextLayerComponentProps} props */
|
|
7054
|
+
static Component({
|
|
7055
|
+
features: featureCollection,
|
|
7056
|
+
style: style2,
|
|
7057
|
+
minZoom,
|
|
7058
|
+
opacity,
|
|
7059
|
+
declutter,
|
|
7060
|
+
drawCollisionBoxes
|
|
7061
|
+
}) {
|
|
7062
|
+
const { registerLayer } = useContext(MapContext);
|
|
7063
|
+
const layer = useMemo(
|
|
7064
|
+
() => {
|
|
7065
|
+
const features = featureCollection instanceof FeatureCollection ? featureCollection.features : (
|
|
7066
|
+
/** @type {import("../Feature").Feature[]} */
|
|
7067
|
+
featureCollection
|
|
7068
|
+
);
|
|
7069
|
+
return TextLayer.with(features, {
|
|
7070
|
+
style: style2,
|
|
7071
|
+
minZoom,
|
|
7072
|
+
opacity,
|
|
7073
|
+
declutter,
|
|
7074
|
+
drawCollisionBoxes
|
|
7075
|
+
});
|
|
7076
|
+
},
|
|
7077
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
7078
|
+
[featureCollection, minZoom, opacity, declutter, drawCollisionBoxes]
|
|
7079
|
+
);
|
|
7080
|
+
registerLayer(layer);
|
|
7081
|
+
useEffect(() => {
|
|
7082
|
+
layer.style = style2;
|
|
7083
|
+
}, [style2]);
|
|
7084
|
+
return null;
|
|
7085
|
+
}
|
|
7086
|
+
/**
|
|
7087
|
+
* @param {import("../Feature").Feature[]} features
|
|
7088
|
+
* @param {TextLayerOptions} options
|
|
7089
|
+
*/
|
|
7090
|
+
static with(features, options) {
|
|
7091
|
+
const source = new VectorSource({ features });
|
|
7092
|
+
return new TextLayer({ source, ...options });
|
|
7093
|
+
}
|
|
7004
7094
|
/**
|
|
7005
|
-
* Represents a feature on the map
|
|
7006
7095
|
* @constructor
|
|
7007
|
-
* @param {Object}
|
|
7008
|
-
* @
|
|
7009
|
-
* @
|
|
7010
|
-
* @
|
|
7011
|
-
* @
|
|
7096
|
+
* @param {Object} params
|
|
7097
|
+
* @param {VectorSource} params.source
|
|
7098
|
+
* @param {Style | (() => Style)} [params.style=undefined]
|
|
7099
|
+
* @param {number} [params.minZoom=0]
|
|
7100
|
+
* @param {number} [params.opacity=1]
|
|
7101
|
+
* @param {boolean} [params.declutter=true]
|
|
7102
|
+
* @param {boolean} [params.drawCollisionBoxes=false]
|
|
7012
7103
|
*/
|
|
7013
|
-
constructor({
|
|
7014
|
-
|
|
7015
|
-
|
|
7016
|
-
|
|
7017
|
-
|
|
7018
|
-
|
|
7104
|
+
constructor({
|
|
7105
|
+
source,
|
|
7106
|
+
style: style2,
|
|
7107
|
+
minZoom = 0,
|
|
7108
|
+
opacity = 1,
|
|
7109
|
+
declutter = true,
|
|
7110
|
+
drawCollisionBoxes = false
|
|
7111
|
+
}) {
|
|
7112
|
+
this.source = source;
|
|
7113
|
+
this._style = style2;
|
|
7114
|
+
this.minZoom = minZoom;
|
|
7115
|
+
this.opacity = opacity;
|
|
7116
|
+
this.declutter = declutter;
|
|
7117
|
+
this.drawCollisionBoxes = drawCollisionBoxes;
|
|
7118
|
+
this.renderer = new TextLayerRenderer(this);
|
|
7119
|
+
this.dispatcher = new Dispatcher(this);
|
|
7120
|
+
}
|
|
7121
|
+
tearDown() {
|
|
7122
|
+
this.dispatcher = null;
|
|
7123
|
+
}
|
|
7124
|
+
get style() {
|
|
7125
|
+
if (this._style) return this._style;
|
|
7126
|
+
const defaultStyle = new Style({
|
|
7127
|
+
text: new Text()
|
|
7128
|
+
});
|
|
7129
|
+
return defaultStyle;
|
|
7130
|
+
}
|
|
7131
|
+
set style(style2) {
|
|
7132
|
+
this._style = style2;
|
|
7133
|
+
this.dispatcher.dispatch(MapEvent.CHANGE);
|
|
7019
7134
|
}
|
|
7020
7135
|
getExtent() {
|
|
7021
7136
|
if (this._extent) return this._extent;
|
|
7022
|
-
const
|
|
7023
|
-
|
|
7024
|
-
|
|
7137
|
+
const features = this.source.getFeatures();
|
|
7138
|
+
const extent = features.reduce((combinedExtent, feature) => {
|
|
7139
|
+
const featureExtent = feature.getExtent();
|
|
7140
|
+
if (!combinedExtent) return featureExtent;
|
|
7141
|
+
return combineExtents(featureExtent, combinedExtent);
|
|
7025
7142
|
}, null);
|
|
7026
7143
|
this._extent = extent;
|
|
7027
7144
|
return extent;
|
|
7028
7145
|
}
|
|
7029
|
-
setgeometries(geometries) {
|
|
7030
|
-
this.geometries = geometries;
|
|
7031
|
-
this._extent = void 0;
|
|
7032
|
-
}
|
|
7033
|
-
getProjectedGeometries(projection) {
|
|
7034
|
-
return this.geometries.map(
|
|
7035
|
-
(d2) => d2.getProjected(projection, projection.revision)
|
|
7036
|
-
);
|
|
7037
|
-
}
|
|
7038
7146
|
getStyleFunction() {
|
|
7039
7147
|
const style2 = this.style;
|
|
7040
|
-
if (!style2) return null;
|
|
7041
7148
|
if (typeof style2 === "function") return style2;
|
|
7042
7149
|
return () => {
|
|
7043
7150
|
return style2;
|
|
7044
7151
|
};
|
|
7045
7152
|
}
|
|
7046
|
-
|
|
7047
|
-
|
|
7048
|
-
|
|
7049
|
-
|
|
7050
|
-
|
|
7051
|
-
|
|
7052
|
-
|
|
7153
|
+
renderFrame(frameState, targetElement) {
|
|
7154
|
+
return this.renderer.renderFrame(frameState, targetElement);
|
|
7155
|
+
}
|
|
7156
|
+
}
|
|
7157
|
+
class VectorLayerRenderer {
|
|
7158
|
+
constructor(layer) {
|
|
7159
|
+
this.layer = layer;
|
|
7160
|
+
this.featureRenderer = new FeatureRenderer();
|
|
7161
|
+
}
|
|
7162
|
+
renderFrame(frameState, targetElement) {
|
|
7163
|
+
if (this.layer.opacity === 0) return targetElement;
|
|
7164
|
+
const { projection, sizeInPixels, visibleExtent, transform } = frameState.viewState;
|
|
7165
|
+
const container2 = this.getOrCreateContainer(targetElement, sizeInPixels);
|
|
7166
|
+
const context = container2.firstElementChild.getContext("2d");
|
|
7167
|
+
context.save();
|
|
7168
|
+
context.translate(transform.x, transform.y);
|
|
7169
|
+
context.scale(transform.k, transform.k);
|
|
7170
|
+
context.lineJoin = "round";
|
|
7171
|
+
context.lineCap = "round";
|
|
7172
|
+
context.globalAlpha = this.layer.opacity;
|
|
7173
|
+
const source = this.layer.source;
|
|
7174
|
+
const features = source.getFeaturesInExtent(visibleExtent);
|
|
7175
|
+
for (const feature of features) {
|
|
7176
|
+
const styleFunction2 = feature.getStyleFunction() || this.layer.getStyleFunction();
|
|
7177
|
+
const featureStyle = styleFunction2(feature);
|
|
7178
|
+
if ((featureStyle == null ? void 0 : featureStyle.stroke) || (featureStyle == null ? void 0 : featureStyle.fill)) {
|
|
7179
|
+
context.save();
|
|
7180
|
+
this.featureRenderer.setStyle(featureStyle);
|
|
7181
|
+
this.featureRenderer.setFeature(feature);
|
|
7182
|
+
this.featureRenderer.render(frameState, context);
|
|
7183
|
+
context.restore();
|
|
7053
7184
|
}
|
|
7054
7185
|
}
|
|
7055
|
-
|
|
7056
|
-
|
|
7057
|
-
|
|
7058
|
-
|
|
7059
|
-
|
|
7060
|
-
|
|
7061
|
-
|
|
7062
|
-
|
|
7063
|
-
|
|
7064
|
-
}
|
|
7065
|
-
/**
|
|
7066
|
-
* Returns the geometries as a GeoJSON object
|
|
7067
|
-
* @returns {Object} The GeoJSON representation of the geometries
|
|
7068
|
-
*/
|
|
7069
|
-
getGeoJSON() {
|
|
7070
|
-
const geometries = this.geometries.map((d2) => d2.getGeoJSON());
|
|
7071
|
-
if (geometries.length === 1) return geometries[0];
|
|
7072
|
-
return {
|
|
7073
|
-
type: "Feature",
|
|
7074
|
-
geometry: this._getGeometryGeoJSON(),
|
|
7075
|
-
properties: this.properties
|
|
7076
|
-
};
|
|
7186
|
+
if (Object.prototype.hasOwnProperty.call(projection, "getCompositionBorders")) {
|
|
7187
|
+
context.beginPath();
|
|
7188
|
+
context.lineWidth = 1 / transform.k;
|
|
7189
|
+
context.strokeStyle = "#999";
|
|
7190
|
+
projection.drawCompositionBorders(context);
|
|
7191
|
+
context.stroke();
|
|
7192
|
+
}
|
|
7193
|
+
context.restore();
|
|
7194
|
+
return container2;
|
|
7077
7195
|
}
|
|
7078
|
-
|
|
7079
|
-
|
|
7080
|
-
|
|
7081
|
-
|
|
7082
|
-
if (
|
|
7083
|
-
|
|
7084
|
-
|
|
7085
|
-
|
|
7086
|
-
|
|
7087
|
-
} else
|
|
7088
|
-
|
|
7089
|
-
type: "MultiLineString",
|
|
7090
|
-
coordinates: geometries.map((d2) => d2.coordinates)
|
|
7091
|
-
};
|
|
7092
|
-
} else if (geometries[0].type === "Point") {
|
|
7093
|
-
return {
|
|
7094
|
-
type: "MultiPoint",
|
|
7095
|
-
coordinates: geometries.map((d2) => d2.coordinates)
|
|
7096
|
-
};
|
|
7196
|
+
getOrCreateContainer(targetElement, sizeInPixels) {
|
|
7197
|
+
let container2 = null;
|
|
7198
|
+
let containerReused = false;
|
|
7199
|
+
let canvas = targetElement && targetElement.firstElementChild;
|
|
7200
|
+
if (canvas instanceof HTMLCanvasElement) {
|
|
7201
|
+
container2 = targetElement;
|
|
7202
|
+
containerReused = true;
|
|
7203
|
+
} else if (this._container) {
|
|
7204
|
+
container2 = this._container;
|
|
7205
|
+
} else {
|
|
7206
|
+
container2 = this.createContainer();
|
|
7097
7207
|
}
|
|
7098
|
-
|
|
7208
|
+
if (!containerReused) {
|
|
7209
|
+
const canvas2 = container2.firstElementChild;
|
|
7210
|
+
canvas2.width = sizeInPixels[0];
|
|
7211
|
+
canvas2.height = sizeInPixels[1];
|
|
7212
|
+
}
|
|
7213
|
+
this._container = container2;
|
|
7214
|
+
return container2;
|
|
7215
|
+
}
|
|
7216
|
+
createContainer() {
|
|
7217
|
+
const container2 = document.createElement("div");
|
|
7218
|
+
container2.className = "gv-map-layer";
|
|
7219
|
+
let style2 = container2.style;
|
|
7220
|
+
style2.position = "absolute";
|
|
7221
|
+
style2.width = "100%";
|
|
7222
|
+
style2.height = "100%";
|
|
7223
|
+
const canvas = document.createElement("canvas");
|
|
7224
|
+
style2 = canvas.style;
|
|
7225
|
+
style2.position = "absolute";
|
|
7226
|
+
style2.width = "100%";
|
|
7227
|
+
style2.height = "100%";
|
|
7228
|
+
container2.appendChild(canvas);
|
|
7229
|
+
return container2;
|
|
7099
7230
|
}
|
|
7100
7231
|
}
|
|
7101
|
-
class
|
|
7102
|
-
/**
|
|
7103
|
-
|
|
7104
|
-
|
|
7105
|
-
|
|
7106
|
-
|
|
7107
|
-
|
|
7108
|
-
|
|
7109
|
-
|
|
7110
|
-
|
|
7111
|
-
|
|
7112
|
-
|
|
7113
|
-
|
|
7114
|
-
|
|
7232
|
+
class VectorLayer {
|
|
7233
|
+
/** @param {VectorLayerComponentProps} props */
|
|
7234
|
+
static Component({
|
|
7235
|
+
features: featureCollection,
|
|
7236
|
+
style: style2,
|
|
7237
|
+
minZoom,
|
|
7238
|
+
opacity,
|
|
7239
|
+
hitDetectionEnabled = true
|
|
7240
|
+
}) {
|
|
7241
|
+
const { registerLayer } = useContext(MapContext);
|
|
7242
|
+
const layer = useMemo(
|
|
7243
|
+
() => {
|
|
7244
|
+
const features = featureCollection instanceof FeatureCollection ? featureCollection.features : (
|
|
7245
|
+
/** @type {import("../Feature").Feature[]} */
|
|
7246
|
+
featureCollection
|
|
7247
|
+
);
|
|
7248
|
+
return VectorLayer.with(features, {
|
|
7249
|
+
style: style2,
|
|
7250
|
+
minZoom,
|
|
7251
|
+
opacity,
|
|
7252
|
+
hitDetectionEnabled
|
|
7253
|
+
});
|
|
7254
|
+
},
|
|
7255
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
7256
|
+
[featureCollection, minZoom, opacity, hitDetectionEnabled]
|
|
7257
|
+
);
|
|
7258
|
+
registerLayer(layer);
|
|
7259
|
+
useEffect(() => {
|
|
7260
|
+
layer.style = style2;
|
|
7261
|
+
}, [style2]);
|
|
7262
|
+
return null;
|
|
7115
7263
|
}
|
|
7116
7264
|
/**
|
|
7117
|
-
*
|
|
7118
|
-
* @
|
|
7119
|
-
* @param {import("../projection").Projection} projection - The projection to use for the geometry
|
|
7120
|
-
* @returns {Object} A GeoJSON representation of the projected geometry
|
|
7121
|
-
* @private
|
|
7265
|
+
* @param {import("../Feature").Feature[]} features
|
|
7266
|
+
* @param {VectorLayerOptions} options
|
|
7122
7267
|
*/
|
|
7123
|
-
|
|
7124
|
-
|
|
7125
|
-
|
|
7268
|
+
static with(features, options) {
|
|
7269
|
+
const source = new VectorSource({ features });
|
|
7270
|
+
return new VectorLayer({ source, ...options });
|
|
7126
7271
|
}
|
|
7127
7272
|
/**
|
|
7128
|
-
*
|
|
7129
|
-
* @
|
|
7273
|
+
* @param {Object} params
|
|
7274
|
+
* @param {VectorSource} params.source
|
|
7275
|
+
* @param {Style | (() => Style)} [params.style=undefined]
|
|
7276
|
+
* @param {number} [params.minZoom=0]
|
|
7277
|
+
* @param {number} [params.opacity=1]
|
|
7278
|
+
* @param {boolean} [params.hitDetectionEnabled=true]
|
|
7130
7279
|
*/
|
|
7131
|
-
|
|
7132
|
-
|
|
7133
|
-
|
|
7134
|
-
|
|
7135
|
-
|
|
7280
|
+
constructor({
|
|
7281
|
+
source,
|
|
7282
|
+
style: style2,
|
|
7283
|
+
minZoom = 0,
|
|
7284
|
+
opacity = 1,
|
|
7285
|
+
hitDetectionEnabled = true
|
|
7286
|
+
}) {
|
|
7287
|
+
this.dispatcher = new Dispatcher(this);
|
|
7288
|
+
this.renderer = new VectorLayerRenderer(this);
|
|
7289
|
+
this.source = source;
|
|
7290
|
+
this._style = style2;
|
|
7291
|
+
this.minZoom = minZoom;
|
|
7292
|
+
this.opacity = opacity;
|
|
7293
|
+
this.hitDetectionEnabled = hitDetectionEnabled;
|
|
7136
7294
|
}
|
|
7137
|
-
|
|
7138
|
-
|
|
7139
|
-
constructor({ type = "LineString", extent, coordinates }) {
|
|
7140
|
-
super({ type, extent, coordinates });
|
|
7295
|
+
get source() {
|
|
7296
|
+
return this._source;
|
|
7141
7297
|
}
|
|
7142
|
-
|
|
7143
|
-
|
|
7144
|
-
|
|
7145
|
-
projected.push(projection(point));
|
|
7298
|
+
set source(source) {
|
|
7299
|
+
if (this._source && source !== this._source) {
|
|
7300
|
+
this._source.tearDown();
|
|
7146
7301
|
}
|
|
7147
|
-
|
|
7148
|
-
|
|
7149
|
-
|
|
7150
|
-
|
|
7302
|
+
this._source = source;
|
|
7303
|
+
source.on(MapEvent.CHANGE, () => {
|
|
7304
|
+
this._extent = null;
|
|
7305
|
+
this.dispatcher.dispatch(MapEvent.CHANGE);
|
|
7306
|
+
});
|
|
7151
7307
|
}
|
|
7152
|
-
|
|
7153
|
-
|
|
7154
|
-
constructor({ type = "Polygon", extent, coordinates }) {
|
|
7155
|
-
super({ type, extent, coordinates });
|
|
7308
|
+
setRawProjection(projection) {
|
|
7309
|
+
this.projection = projection;
|
|
7156
7310
|
}
|
|
7157
|
-
|
|
7158
|
-
|
|
7159
|
-
|
|
7160
|
-
|
|
7161
|
-
|
|
7162
|
-
|
|
7163
|
-
|
|
7164
|
-
|
|
7165
|
-
|
|
7166
|
-
|
|
7167
|
-
|
|
7168
|
-
|
|
7169
|
-
|
|
7170
|
-
|
|
7171
|
-
|
|
7172
|
-
|
|
7173
|
-
|
|
7174
|
-
return {
|
|
7175
|
-
|
|
7176
|
-
coordinates: projected
|
|
7311
|
+
tearDown() {
|
|
7312
|
+
this.dispatcher = null;
|
|
7313
|
+
}
|
|
7314
|
+
get style() {
|
|
7315
|
+
if (this._style) return this._style;
|
|
7316
|
+
const defaultStyle = new Style({
|
|
7317
|
+
stroke: new Stroke()
|
|
7318
|
+
});
|
|
7319
|
+
return defaultStyle;
|
|
7320
|
+
}
|
|
7321
|
+
set style(style2) {
|
|
7322
|
+
this._style = style2;
|
|
7323
|
+
this.dispatcher.dispatch(MapEvent.CHANGE);
|
|
7324
|
+
}
|
|
7325
|
+
getStyleFunction() {
|
|
7326
|
+
const style2 = this.style;
|
|
7327
|
+
if (typeof style2 === "function") return style2;
|
|
7328
|
+
return () => {
|
|
7329
|
+
return style2;
|
|
7177
7330
|
};
|
|
7178
7331
|
}
|
|
7179
|
-
|
|
7180
|
-
return this.
|
|
7181
|
-
|
|
7182
|
-
|
|
7183
|
-
|
|
7332
|
+
getExtent() {
|
|
7333
|
+
if (this._extent) return this._extent;
|
|
7334
|
+
const features = this.source.getFeatures();
|
|
7335
|
+
const extent = features.reduce((combinedExtent, feature) => {
|
|
7336
|
+
const featureExtent = feature.getExtent();
|
|
7337
|
+
if (!combinedExtent) return featureExtent;
|
|
7338
|
+
return combineExtents(featureExtent, combinedExtent);
|
|
7339
|
+
}, null);
|
|
7340
|
+
this._extent = extent;
|
|
7341
|
+
return extent;
|
|
7184
7342
|
}
|
|
7185
|
-
|
|
7186
|
-
this.
|
|
7343
|
+
findFeatures(coordinate) {
|
|
7344
|
+
if (!this.hitDetectionEnabled) return;
|
|
7345
|
+
return this.source.getFeaturesAtCoordinate(coordinate);
|
|
7187
7346
|
}
|
|
7188
|
-
|
|
7189
|
-
return
|
|
7190
|
-
extent: this.extent,
|
|
7191
|
-
coordinates: JSON.parse(JSON.stringify(this.coordinates))
|
|
7192
|
-
});
|
|
7347
|
+
renderFrame(frameState, targetElement) {
|
|
7348
|
+
return this.renderer.renderFrame(frameState, targetElement);
|
|
7193
7349
|
}
|
|
7194
7350
|
}
|
|
7195
|
-
|
|
7196
|
-
|
|
7197
|
-
|
|
7198
|
-
|
|
7199
|
-
|
|
7200
|
-
_getProjected(projection) {
|
|
7201
|
-
return {
|
|
7202
|
-
type: this.type,
|
|
7203
|
-
coordinates: projection(this.coordinates)
|
|
7204
|
-
};
|
|
7351
|
+
function interpolateFeatures(currentFeatures, newFeatures, { interpolate: interpolate2, separate, combine }) {
|
|
7352
|
+
if (currentFeatures.length !== newFeatures.length) {
|
|
7353
|
+
throw new Error(
|
|
7354
|
+
"interpolateFeatures expects an equal number of features for start and end"
|
|
7355
|
+
);
|
|
7205
7356
|
}
|
|
7206
|
-
|
|
7207
|
-
|
|
7208
|
-
|
|
7209
|
-
const
|
|
7210
|
-
|
|
7211
|
-
if (
|
|
7212
|
-
|
|
7213
|
-
|
|
7214
|
-
|
|
7215
|
-
|
|
7216
|
-
|
|
7217
|
-
if (!featureObject) {
|
|
7218
|
-
continue;
|
|
7219
|
-
}
|
|
7220
|
-
features.push(featureObject);
|
|
7221
|
-
}
|
|
7222
|
-
} else if (geoJSONObject["type"] === "Feature") {
|
|
7223
|
-
features = [this.readFeatureFromObject(geoJSONObject)];
|
|
7224
|
-
} else if (Array.isArray(geoJSONObject)) {
|
|
7225
|
-
features = [];
|
|
7226
|
-
for (let i = 0, ii = geoJSONObject.length; i < ii; ++i) {
|
|
7227
|
-
const featureObject = this.readFeatureFromObject(geoJSONObject[i]);
|
|
7228
|
-
if (!featureObject) {
|
|
7229
|
-
continue;
|
|
7357
|
+
const featureInterpolators = [];
|
|
7358
|
+
for (let i = 0; i < currentFeatures.length; i++) {
|
|
7359
|
+
const geometryInterpolators = [];
|
|
7360
|
+
const currentGeometries = currentFeatures[i].geometries;
|
|
7361
|
+
const newGeometries = newFeatures[i].geometries;
|
|
7362
|
+
if (newGeometries.length === currentGeometries.length) {
|
|
7363
|
+
for (let e = 0; e < currentGeometries.length; e++) {
|
|
7364
|
+
const currentGeometry = currentGeometries[e];
|
|
7365
|
+
const newGeometry = newGeometries[e];
|
|
7366
|
+
if (currentGeometry.type !== "Polygon" || newGeometry.type !== "Polygon") {
|
|
7367
|
+
throw new Error("interpolateFeatures expects only Polygon geometry");
|
|
7230
7368
|
}
|
|
7231
|
-
|
|
7369
|
+
const shapeInterpolator = interpolate2(
|
|
7370
|
+
currentGeometries[e].getOuterRing(),
|
|
7371
|
+
newGeometries[e].getOuterRing(),
|
|
7372
|
+
{ string: false }
|
|
7373
|
+
);
|
|
7374
|
+
geometryInterpolators.push({
|
|
7375
|
+
type: "default",
|
|
7376
|
+
interpolator: shapeInterpolator
|
|
7377
|
+
});
|
|
7232
7378
|
}
|
|
7379
|
+
} else if (currentGeometries.length === 1 && newGeometries.length > 1) {
|
|
7380
|
+
const separationInterpolator = separate(
|
|
7381
|
+
currentGeometries[0].getOuterRing(),
|
|
7382
|
+
newGeometries.map((geometry) => geometry.getOuterRing()),
|
|
7383
|
+
{ string: false, single: true }
|
|
7384
|
+
);
|
|
7385
|
+
geometryInterpolators.push({
|
|
7386
|
+
type: "separate",
|
|
7387
|
+
interpolator: separationInterpolator
|
|
7388
|
+
});
|
|
7389
|
+
} else if (currentGeometries.length > 1 && newGeometries.length === 1) {
|
|
7390
|
+
const combinationInterpolator = combine(
|
|
7391
|
+
currentGeometries.map((geometry) => geometry.getOuterRing()),
|
|
7392
|
+
newGeometries[0].getOuterRing(),
|
|
7393
|
+
{ string: false, single: true }
|
|
7394
|
+
);
|
|
7395
|
+
geometryInterpolators.push({
|
|
7396
|
+
type: "combine",
|
|
7397
|
+
interpolator: combinationInterpolator
|
|
7398
|
+
});
|
|
7233
7399
|
} else {
|
|
7234
|
-
|
|
7235
|
-
|
|
7236
|
-
|
|
7237
|
-
features = [feature];
|
|
7238
|
-
} catch {
|
|
7239
|
-
console.warn("Unable to interpret GeoJSON:", geoJSONObject);
|
|
7240
|
-
return;
|
|
7241
|
-
}
|
|
7400
|
+
throw new Error(
|
|
7401
|
+
`Encountered an unexpected number of geometries: ${currentGeometries.length} and ${newGeometries.length}`
|
|
7402
|
+
);
|
|
7242
7403
|
}
|
|
7243
|
-
|
|
7404
|
+
featureInterpolators.push(geometryInterpolators);
|
|
7244
7405
|
}
|
|
7245
|
-
|
|
7246
|
-
|
|
7247
|
-
|
|
7248
|
-
return new Feature({
|
|
7249
|
-
id: geoJSONObject["id"],
|
|
7250
|
-
geometries,
|
|
7251
|
-
properties: geoJSONObject["properties"]
|
|
7252
|
-
});
|
|
7406
|
+
return (t) => {
|
|
7407
|
+
if (t >= 1) {
|
|
7408
|
+
return newFeatures;
|
|
7253
7409
|
}
|
|
7254
|
-
|
|
7255
|
-
|
|
7256
|
-
|
|
7257
|
-
|
|
7258
|
-
|
|
7259
|
-
const
|
|
7260
|
-
|
|
7261
|
-
|
|
7262
|
-
|
|
7263
|
-
|
|
7264
|
-
|
|
7265
|
-
|
|
7266
|
-
|
|
7267
|
-
|
|
7268
|
-
|
|
7269
|
-
|
|
7270
|
-
|
|
7271
|
-
|
|
7272
|
-
|
|
7273
|
-
|
|
7274
|
-
|
|
7410
|
+
const features = [];
|
|
7411
|
+
for (let i = 0; i < featureInterpolators.length; i++) {
|
|
7412
|
+
const feature = newFeatures[i].clone();
|
|
7413
|
+
const geometries = [];
|
|
7414
|
+
const geometryInterpolators = featureInterpolators[i];
|
|
7415
|
+
for (const [
|
|
7416
|
+
index,
|
|
7417
|
+
{ type, interpolator }
|
|
7418
|
+
] of geometryInterpolators.entries()) {
|
|
7419
|
+
let geometry = feature.geometries[index].clone();
|
|
7420
|
+
let interpolated;
|
|
7421
|
+
switch (type) {
|
|
7422
|
+
case "separate":
|
|
7423
|
+
case "combine":
|
|
7424
|
+
interpolated = interpolator(t);
|
|
7425
|
+
interpolated.forEach((d2) => {
|
|
7426
|
+
const polygon = geometry.clone();
|
|
7427
|
+
polygon.setCoordinates([d2]);
|
|
7428
|
+
geometries.push(polygon);
|
|
7429
|
+
});
|
|
7430
|
+
break;
|
|
7431
|
+
default:
|
|
7432
|
+
geometry.setOuterRing(interpolator(t));
|
|
7433
|
+
geometries.push(geometry);
|
|
7434
|
+
break;
|
|
7435
|
+
}
|
|
7275
7436
|
}
|
|
7276
|
-
|
|
7277
|
-
|
|
7278
|
-
geometries.push(point);
|
|
7437
|
+
feature.setGeometry(geometries);
|
|
7438
|
+
features.push(feature);
|
|
7279
7439
|
}
|
|
7280
|
-
return
|
|
7281
|
-
}
|
|
7282
|
-
|
|
7283
|
-
|
|
7284
|
-
|
|
7285
|
-
|
|
7286
|
-
|
|
7287
|
-
|
|
7288
|
-
|
|
7289
|
-
|
|
7290
|
-
|
|
7291
|
-
|
|
7292
|
-
|
|
7293
|
-
|
|
7440
|
+
return features;
|
|
7441
|
+
};
|
|
7442
|
+
}
|
|
7443
|
+
function interpolateStyles(firstStyle, secondStyle, interpolateColors, interpolateNumbers) {
|
|
7444
|
+
const fillInterpolator = interpolateFill(
|
|
7445
|
+
firstStyle.fill,
|
|
7446
|
+
secondStyle.fill,
|
|
7447
|
+
interpolateColors,
|
|
7448
|
+
interpolateNumbers
|
|
7449
|
+
);
|
|
7450
|
+
const strokeInterpolator = interpolateStroke(
|
|
7451
|
+
firstStyle.stroke,
|
|
7452
|
+
secondStyle.stroke,
|
|
7453
|
+
interpolateColors,
|
|
7454
|
+
interpolateNumbers
|
|
7455
|
+
);
|
|
7456
|
+
return (t) => {
|
|
7457
|
+
return new Style({
|
|
7458
|
+
fill: fillInterpolator(t),
|
|
7459
|
+
stroke: strokeInterpolator(t)
|
|
7460
|
+
});
|
|
7461
|
+
};
|
|
7462
|
+
}
|
|
7463
|
+
function interpolateFill(fillA, fillB, interpolateColors, interpolateNumbers) {
|
|
7464
|
+
const colorInterpolator = interpolateColors(
|
|
7465
|
+
(fillA == null ? void 0 : fillA.color) ?? "transparent",
|
|
7466
|
+
(fillB == null ? void 0 : fillB.color) ?? "transparent"
|
|
7467
|
+
);
|
|
7468
|
+
const opacityInterpolator = interpolateNumbers(
|
|
7469
|
+
(fillA == null ? void 0 : fillA.opacity) ?? 1,
|
|
7470
|
+
(fillB == null ? void 0 : fillB.opacity) ?? 1
|
|
7471
|
+
);
|
|
7472
|
+
return (t) => {
|
|
7473
|
+
return new Fill({
|
|
7474
|
+
color: colorInterpolator(t),
|
|
7475
|
+
opacity: opacityInterpolator(t)
|
|
7476
|
+
});
|
|
7477
|
+
};
|
|
7478
|
+
}
|
|
7479
|
+
function interpolateStroke(strokeA, strokeB, interpolateColors, interpolateNumbers) {
|
|
7480
|
+
const colorInterpolator = interpolateColors(
|
|
7481
|
+
(strokeA == null ? void 0 : strokeA.color) ?? "transparent",
|
|
7482
|
+
(strokeB == null ? void 0 : strokeB.color) ?? "transparent"
|
|
7483
|
+
);
|
|
7484
|
+
const opacityInterpolator = interpolateNumbers(
|
|
7485
|
+
(strokeA == null ? void 0 : strokeA.opacity) ?? 1,
|
|
7486
|
+
(strokeB == null ? void 0 : strokeB.opacity) ?? 1
|
|
7487
|
+
);
|
|
7488
|
+
const widthInterpolator = interpolateNumbers(
|
|
7489
|
+
(strokeA == null ? void 0 : strokeA.width) ?? 1,
|
|
7490
|
+
(strokeB == null ? void 0 : strokeB.width) ?? 1
|
|
7491
|
+
);
|
|
7492
|
+
return (t) => {
|
|
7493
|
+
return new Stroke({
|
|
7494
|
+
color: colorInterpolator(t),
|
|
7495
|
+
opacity: opacityInterpolator(t),
|
|
7496
|
+
width: widthInterpolator(t)
|
|
7497
|
+
});
|
|
7498
|
+
};
|
|
7294
7499
|
}
|
|
7295
7500
|
function useWindowSize() {
|
|
7296
7501
|
const [windowSize, setWindowSize] = useState(() => {
|
|
@@ -7730,6 +7935,8 @@ export {
|
|
|
7730
7935
|
ControlChange,
|
|
7731
7936
|
Dispatcher,
|
|
7732
7937
|
Dropdown,
|
|
7938
|
+
Feature,
|
|
7939
|
+
FeatureCollection,
|
|
7733
7940
|
Fill,
|
|
7734
7941
|
FirstPastThePostWaffle,
|
|
7735
7942
|
GeoJSON,
|
|
@@ -7760,6 +7967,7 @@ export {
|
|
|
7760
7967
|
StackedBar,
|
|
7761
7968
|
StackedGrid,
|
|
7762
7969
|
Stroke,
|
|
7970
|
+
StrokePosition,
|
|
7763
7971
|
Style,
|
|
7764
7972
|
Table,
|
|
7765
7973
|
Text,
|