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