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