@versatiles/svg-renderer 0.3.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +37 -29
- package/dist/index.cjs +312 -110
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +312 -110
- package/dist/index.js.map +1 -1
- package/dist/maplibre.cjs +312 -110
- package/dist/maplibre.cjs.map +1 -1
- package/dist/maplibre.js +312 -110
- package/dist/maplibre.js.map +1 -1
- package/dist/maplibre.umd.js +312 -110
- package/dist/maplibre.umd.js.map +1 -1
- package/package.json +15 -13
package/dist/maplibre.cjs
CHANGED
|
@@ -9209,6 +9209,20 @@ class Color {
|
|
|
9209
9209
|
return str.length < 2 ? '0' + str : str;
|
|
9210
9210
|
}
|
|
9211
9211
|
}
|
|
9212
|
+
get rgb() {
|
|
9213
|
+
return `#${d2h(this.values[0])}${d2h(this.values[1])}${d2h(this.values[2])}`;
|
|
9214
|
+
function d2h(num) {
|
|
9215
|
+
if (num < 0)
|
|
9216
|
+
num = 0;
|
|
9217
|
+
if (num > 255)
|
|
9218
|
+
num = 255;
|
|
9219
|
+
const str = Math.round(num).toString(16).toUpperCase();
|
|
9220
|
+
return str.length < 2 ? '0' + str : str;
|
|
9221
|
+
}
|
|
9222
|
+
}
|
|
9223
|
+
get opacity() {
|
|
9224
|
+
return this.values[3] / 255;
|
|
9225
|
+
}
|
|
9212
9226
|
get alpha() {
|
|
9213
9227
|
return this.values[3];
|
|
9214
9228
|
}
|
|
@@ -9254,7 +9268,7 @@ class SVGRenderer {
|
|
|
9254
9268
|
const key = style.color.hex + translate;
|
|
9255
9269
|
let group = groups.get(key);
|
|
9256
9270
|
if (!group) {
|
|
9257
|
-
group = { segments: [], attrs:
|
|
9271
|
+
group = { segments: [], attrs: `${fillAttr(style.color)}${translate}` };
|
|
9258
9272
|
groups.set(key, group);
|
|
9259
9273
|
}
|
|
9260
9274
|
feature.geometry.forEach((ring) => {
|
|
@@ -9295,8 +9309,7 @@ class SVGRenderer {
|
|
|
9295
9309
|
segments: [],
|
|
9296
9310
|
attrs: [
|
|
9297
9311
|
'fill="none"',
|
|
9298
|
-
|
|
9299
|
-
`stroke-width="${roundedWidth}"`,
|
|
9312
|
+
strokeAttr(style.color, roundedWidth),
|
|
9300
9313
|
`stroke-linecap="${style.cap}"`,
|
|
9301
9314
|
`stroke-linejoin="${style.join}"`,
|
|
9302
9315
|
`stroke-miterlimit="${String(style.miterLimit)}"`,
|
|
@@ -9315,6 +9328,43 @@ class SVGRenderer {
|
|
|
9315
9328
|
}
|
|
9316
9329
|
this.#svg.push('</g>');
|
|
9317
9330
|
}
|
|
9331
|
+
drawCircles(features, opacity) {
|
|
9332
|
+
if (features.length === 0)
|
|
9333
|
+
return;
|
|
9334
|
+
if (opacity <= 0)
|
|
9335
|
+
return;
|
|
9336
|
+
this.#svg.push(`<g opacity="${String(opacity)}">`);
|
|
9337
|
+
const groups = new Map();
|
|
9338
|
+
features.forEach(([feature, style]) => {
|
|
9339
|
+
if (style.radius <= 0 || style.color.alpha <= 0)
|
|
9340
|
+
return;
|
|
9341
|
+
const translate = style.translate.isZero()
|
|
9342
|
+
? ''
|
|
9343
|
+
: ` transform="translate(${formatPoint(style.translate, this.#scale)})"`;
|
|
9344
|
+
const roundedRadius = roundValue(style.radius, this.#scale);
|
|
9345
|
+
const strokeAttrs = style.strokeWidth > 0
|
|
9346
|
+
? ` ${strokeAttr(style.strokeColor, roundValue(style.strokeWidth, this.#scale))}`
|
|
9347
|
+
: '';
|
|
9348
|
+
const key = [style.color.hex, roundedRadius, strokeAttrs, translate].join('\0');
|
|
9349
|
+
let group = groups.get(key);
|
|
9350
|
+
if (!group) {
|
|
9351
|
+
group = {
|
|
9352
|
+
points: [],
|
|
9353
|
+
attrs: `r="${roundedRadius}" ${fillAttr(style.color)}${strokeAttrs}${translate}`,
|
|
9354
|
+
};
|
|
9355
|
+
groups.set(key, group);
|
|
9356
|
+
}
|
|
9357
|
+
feature.geometry.forEach((ring) => {
|
|
9358
|
+
group.points.push(roundXY(ring[0], this.#scale));
|
|
9359
|
+
});
|
|
9360
|
+
});
|
|
9361
|
+
for (const { points, attrs } of groups.values()) {
|
|
9362
|
+
for (const [x, y] of points) {
|
|
9363
|
+
this.#svg.push(`<circle cx="${formatNum(x)}" cy="${formatNum(y)}" ${attrs} />`);
|
|
9364
|
+
}
|
|
9365
|
+
}
|
|
9366
|
+
this.#svg.push('</g>');
|
|
9367
|
+
}
|
|
9318
9368
|
drawRasterTiles(tiles, style) {
|
|
9319
9369
|
if (tiles.length === 0)
|
|
9320
9370
|
return;
|
|
@@ -9347,13 +9397,32 @@ class SVGRenderer {
|
|
|
9347
9397
|
this.#svg.push('</g>');
|
|
9348
9398
|
}
|
|
9349
9399
|
getString() {
|
|
9350
|
-
|
|
9351
|
-
|
|
9352
|
-
|
|
9353
|
-
|
|
9354
|
-
|
|
9400
|
+
const w = this.width.toFixed(0);
|
|
9401
|
+
const h = this.height.toFixed(0);
|
|
9402
|
+
const parts = [
|
|
9403
|
+
`<svg viewBox="0 0 ${w} ${h}" width="${w}" height="${h}" xmlns="http://www.w3.org/2000/svg">`,
|
|
9404
|
+
`<defs><clipPath id="vb"><rect width="${w}" height="${h}"/></clipPath></defs>`,
|
|
9405
|
+
`<g clip-path="url(#vb)">`,
|
|
9406
|
+
];
|
|
9407
|
+
if (this.#backgroundColor.alpha > 0) {
|
|
9408
|
+
parts.push(`<rect x="-1" y="-1" width="${(this.width + 2).toFixed(0)}" height="${(this.height + 2).toFixed(0)}" ${fillAttr(this.#backgroundColor)} />`);
|
|
9409
|
+
}
|
|
9410
|
+
parts.push(...this.#svg, '</g>', '</svg>');
|
|
9411
|
+
return parts.join('\n');
|
|
9355
9412
|
}
|
|
9356
9413
|
}
|
|
9414
|
+
function fillAttr(color) {
|
|
9415
|
+
let attr = `fill="${color.rgb}"`;
|
|
9416
|
+
if (color.alpha < 255)
|
|
9417
|
+
attr += ` fill-opacity="${color.opacity.toFixed(3)}"`;
|
|
9418
|
+
return attr;
|
|
9419
|
+
}
|
|
9420
|
+
function strokeAttr(color, width) {
|
|
9421
|
+
let attr = `stroke="${color.rgb}" stroke-width="${width}"`;
|
|
9422
|
+
if (color.alpha < 255)
|
|
9423
|
+
attr += ` stroke-opacity="${color.opacity.toFixed(3)}"`;
|
|
9424
|
+
return attr;
|
|
9425
|
+
}
|
|
9357
9426
|
function roundValue(v, scale) {
|
|
9358
9427
|
return (v * scale).toFixed(3);
|
|
9359
9428
|
}
|
|
@@ -9464,77 +9533,6 @@ function formatNum(tenths) {
|
|
|
9464
9533
|
return (negative ? '-' : '') + String(whole) + '.' + String(frac);
|
|
9465
9534
|
}
|
|
9466
9535
|
|
|
9467
|
-
class Point2D {
|
|
9468
|
-
x;
|
|
9469
|
-
y;
|
|
9470
|
-
constructor(x, y) {
|
|
9471
|
-
this.x = x;
|
|
9472
|
-
this.y = y;
|
|
9473
|
-
}
|
|
9474
|
-
isZero() {
|
|
9475
|
-
return this.x === 0 && this.y === 0;
|
|
9476
|
-
}
|
|
9477
|
-
scale(factor) {
|
|
9478
|
-
this.x *= factor;
|
|
9479
|
-
this.y *= factor;
|
|
9480
|
-
return this;
|
|
9481
|
-
}
|
|
9482
|
-
translate(offset) {
|
|
9483
|
-
this.x += offset.x;
|
|
9484
|
-
this.y += offset.y;
|
|
9485
|
-
return this;
|
|
9486
|
-
}
|
|
9487
|
-
getProject2Pixel() {
|
|
9488
|
-
const s = Math.sin((this.y * Math.PI) / 180.0);
|
|
9489
|
-
return new Point2D(this.x / 360.0 + 0.5, 0.5 - (0.25 * Math.log((1 + s) / (1 - s))) / Math.PI);
|
|
9490
|
-
}
|
|
9491
|
-
}
|
|
9492
|
-
class Feature {
|
|
9493
|
-
type;
|
|
9494
|
-
id;
|
|
9495
|
-
properties;
|
|
9496
|
-
patterns;
|
|
9497
|
-
geometry;
|
|
9498
|
-
constructor(opt) {
|
|
9499
|
-
this.type = opt.type;
|
|
9500
|
-
this.id = opt.id;
|
|
9501
|
-
this.properties = opt.properties;
|
|
9502
|
-
this.patterns = opt.patterns;
|
|
9503
|
-
this.geometry = opt.geometry;
|
|
9504
|
-
}
|
|
9505
|
-
getBbox() {
|
|
9506
|
-
let xMin = Infinity;
|
|
9507
|
-
let yMin = Infinity;
|
|
9508
|
-
let xMax = -Infinity;
|
|
9509
|
-
let yMax = -Infinity;
|
|
9510
|
-
this.geometry.forEach((ring) => {
|
|
9511
|
-
ring.forEach((point) => {
|
|
9512
|
-
if (xMin > point.x)
|
|
9513
|
-
xMin = point.x;
|
|
9514
|
-
if (yMin > point.y)
|
|
9515
|
-
yMin = point.y;
|
|
9516
|
-
if (xMax < point.x)
|
|
9517
|
-
xMax = point.x;
|
|
9518
|
-
if (yMax < point.y)
|
|
9519
|
-
yMax = point.y;
|
|
9520
|
-
});
|
|
9521
|
-
});
|
|
9522
|
-
return [xMin, yMin, xMax, yMax];
|
|
9523
|
-
}
|
|
9524
|
-
doesOverlap(bbox) {
|
|
9525
|
-
const featureBbox = this.getBbox();
|
|
9526
|
-
if (featureBbox[0] > bbox[2])
|
|
9527
|
-
return false;
|
|
9528
|
-
if (featureBbox[1] > bbox[3])
|
|
9529
|
-
return false;
|
|
9530
|
-
if (featureBbox[2] < bbox[0])
|
|
9531
|
-
return false;
|
|
9532
|
-
if (featureBbox[3] < bbox[1])
|
|
9533
|
-
return false;
|
|
9534
|
-
return true;
|
|
9535
|
-
}
|
|
9536
|
-
}
|
|
9537
|
-
|
|
9538
9536
|
/*
|
|
9539
9537
|
* bignumber.js v9.3.1
|
|
9540
9538
|
* A JavaScript library for arbitrary-precision arithmetic.
|
|
@@ -14166,6 +14164,77 @@ function union2(features, options = {}) {
|
|
|
14166
14164
|
else return multiPolygon(unioned, options.properties);
|
|
14167
14165
|
}
|
|
14168
14166
|
|
|
14167
|
+
class Point2D {
|
|
14168
|
+
x;
|
|
14169
|
+
y;
|
|
14170
|
+
constructor(x, y) {
|
|
14171
|
+
this.x = x;
|
|
14172
|
+
this.y = y;
|
|
14173
|
+
}
|
|
14174
|
+
isZero() {
|
|
14175
|
+
return this.x === 0 && this.y === 0;
|
|
14176
|
+
}
|
|
14177
|
+
scale(factor) {
|
|
14178
|
+
this.x *= factor;
|
|
14179
|
+
this.y *= factor;
|
|
14180
|
+
return this;
|
|
14181
|
+
}
|
|
14182
|
+
translate(offset) {
|
|
14183
|
+
this.x += offset.x;
|
|
14184
|
+
this.y += offset.y;
|
|
14185
|
+
return this;
|
|
14186
|
+
}
|
|
14187
|
+
getProject2Pixel() {
|
|
14188
|
+
const s = Math.sin((this.y * Math.PI) / 180.0);
|
|
14189
|
+
return new Point2D(this.x / 360.0 + 0.5, 0.5 - (0.25 * Math.log((1 + s) / (1 - s))) / Math.PI);
|
|
14190
|
+
}
|
|
14191
|
+
}
|
|
14192
|
+
class Feature {
|
|
14193
|
+
type;
|
|
14194
|
+
id;
|
|
14195
|
+
properties;
|
|
14196
|
+
patterns;
|
|
14197
|
+
geometry;
|
|
14198
|
+
constructor(opt) {
|
|
14199
|
+
this.type = opt.type;
|
|
14200
|
+
this.id = opt.id;
|
|
14201
|
+
this.properties = opt.properties;
|
|
14202
|
+
this.patterns = opt.patterns;
|
|
14203
|
+
this.geometry = opt.geometry;
|
|
14204
|
+
}
|
|
14205
|
+
getBbox() {
|
|
14206
|
+
let xMin = Infinity;
|
|
14207
|
+
let yMin = Infinity;
|
|
14208
|
+
let xMax = -Infinity;
|
|
14209
|
+
let yMax = -Infinity;
|
|
14210
|
+
this.geometry.forEach((ring) => {
|
|
14211
|
+
ring.forEach((point) => {
|
|
14212
|
+
if (xMin > point.x)
|
|
14213
|
+
xMin = point.x;
|
|
14214
|
+
if (yMin > point.y)
|
|
14215
|
+
yMin = point.y;
|
|
14216
|
+
if (xMax < point.x)
|
|
14217
|
+
xMax = point.x;
|
|
14218
|
+
if (yMax < point.y)
|
|
14219
|
+
yMax = point.y;
|
|
14220
|
+
});
|
|
14221
|
+
});
|
|
14222
|
+
return [xMin, yMin, xMax, yMax];
|
|
14223
|
+
}
|
|
14224
|
+
doesOverlap(bbox) {
|
|
14225
|
+
const featureBbox = this.getBbox();
|
|
14226
|
+
if (featureBbox[0] > bbox[2])
|
|
14227
|
+
return false;
|
|
14228
|
+
if (featureBbox[1] > bbox[3])
|
|
14229
|
+
return false;
|
|
14230
|
+
if (featureBbox[2] < bbox[0])
|
|
14231
|
+
return false;
|
|
14232
|
+
if (featureBbox[3] < bbox[1])
|
|
14233
|
+
return false;
|
|
14234
|
+
return true;
|
|
14235
|
+
}
|
|
14236
|
+
}
|
|
14237
|
+
|
|
14169
14238
|
function geojsonToFeature(id, polygonFeature) {
|
|
14170
14239
|
const geometry = polygonFeature.geometry.coordinates.map((ring) => {
|
|
14171
14240
|
return ring.map((coord) => new Point2D(coord[0], coord[1]));
|
|
@@ -14199,15 +14268,13 @@ function mergePolygons(featureList) {
|
|
|
14199
14268
|
const turfFeatures = [];
|
|
14200
14269
|
features.forEach((f) => {
|
|
14201
14270
|
const rings = f.geometry.map((ring) => ring.map((p) => [p.x, p.y]));
|
|
14202
|
-
|
|
14203
|
-
|
|
14204
|
-
|
|
14205
|
-
|
|
14206
|
-
|
|
14207
|
-
|
|
14208
|
-
|
|
14209
|
-
properties: f.properties,
|
|
14210
|
-
});
|
|
14271
|
+
turfFeatures.push({
|
|
14272
|
+
type: 'Feature',
|
|
14273
|
+
geometry: {
|
|
14274
|
+
type: 'Polygon',
|
|
14275
|
+
coordinates: rings,
|
|
14276
|
+
},
|
|
14277
|
+
properties: f.properties,
|
|
14211
14278
|
});
|
|
14212
14279
|
});
|
|
14213
14280
|
const merged = union2({
|
|
@@ -15816,24 +15883,16 @@ function writeUtf8(buf, str, pos) {
|
|
|
15816
15883
|
}
|
|
15817
15884
|
|
|
15818
15885
|
const TILE_EXTENT = 4096;
|
|
15819
|
-
async function
|
|
15886
|
+
async function loadVectorSource(source, job, layerFeatures) {
|
|
15887
|
+
const tiles = source.tiles;
|
|
15888
|
+
if (!tiles)
|
|
15889
|
+
return;
|
|
15820
15890
|
const { width, height } = job.renderer;
|
|
15821
15891
|
const { zoom, center } = job.view;
|
|
15822
|
-
const { sources } = job.style;
|
|
15823
|
-
const source = sources['versatiles-shortbread'];
|
|
15824
|
-
if (!source)
|
|
15825
|
-
return new Map();
|
|
15826
|
-
if (source.type !== 'vector' || !source.tiles) {
|
|
15827
|
-
console.error('Invalid source configuration. Expected a vector source with tile URLs.');
|
|
15828
|
-
console.error('Source config:', source);
|
|
15829
|
-
throw Error('Invalid source');
|
|
15830
|
-
}
|
|
15831
|
-
const sourceUrl = source.tiles[0];
|
|
15832
15892
|
const { zoomLevel, tileSize, tiles: tileCoordinates, } = calculateTileGrid(width, height, center, zoom, source.maxzoom);
|
|
15833
|
-
const layerFeatures = new Map();
|
|
15834
15893
|
await Promise.all(tileCoordinates.map(async ({ x, y, offsetX, offsetY }) => {
|
|
15835
15894
|
const offset = new Point2D(offsetX, offsetY);
|
|
15836
|
-
const tile = await getTile(
|
|
15895
|
+
const tile = await getTile(tiles[0], zoomLevel, x, y);
|
|
15837
15896
|
if (!tile)
|
|
15838
15897
|
return;
|
|
15839
15898
|
const vectorTile = new VectorTile(new Pbf(tile.buffer));
|
|
@@ -15877,14 +15936,108 @@ async function getLayerFeatures(job) {
|
|
|
15877
15936
|
}
|
|
15878
15937
|
}
|
|
15879
15938
|
}));
|
|
15880
|
-
|
|
15881
|
-
|
|
15882
|
-
|
|
15883
|
-
|
|
15884
|
-
|
|
15885
|
-
|
|
15939
|
+
}
|
|
15940
|
+
|
|
15941
|
+
function loadGeoJSONSource(sourceName, data, width, height, zoom, center, layerFeatures) {
|
|
15942
|
+
const existing = layerFeatures.get(sourceName);
|
|
15943
|
+
const features = existing ?? { points: [], linestrings: [], polygons: [] };
|
|
15944
|
+
if (!existing)
|
|
15945
|
+
layerFeatures.set(sourceName, features);
|
|
15946
|
+
const worldSize = 512 * 2 ** zoom;
|
|
15947
|
+
const centerMercator = center.getProject2Pixel();
|
|
15948
|
+
function projectCoord(coord) {
|
|
15949
|
+
const mercator = new Point2D(coord[0], coord[1]).getProject2Pixel();
|
|
15950
|
+
return new Point2D((mercator.x - centerMercator.x) * worldSize + width / 2, (mercator.y - centerMercator.y) * worldSize + height / 2);
|
|
15951
|
+
}
|
|
15952
|
+
function makeFeature(type, geometry, id, properties) {
|
|
15953
|
+
const feature = new Feature({ type, geometry, id, properties });
|
|
15954
|
+
if (!feature.doesOverlap([0, 0, width, height]))
|
|
15955
|
+
return null;
|
|
15956
|
+
return feature;
|
|
15957
|
+
}
|
|
15958
|
+
function extractPoints(geometry) {
|
|
15959
|
+
return geometry.flatMap((ring) => ring.map((p) => [p]));
|
|
15960
|
+
}
|
|
15961
|
+
function addFeature(type, geometry, id, properties) {
|
|
15962
|
+
switch (type) {
|
|
15963
|
+
case 'Point': {
|
|
15964
|
+
const f = makeFeature('Point', geometry, id, properties);
|
|
15965
|
+
if (f)
|
|
15966
|
+
features.points.push(f);
|
|
15967
|
+
break;
|
|
15968
|
+
}
|
|
15969
|
+
case 'LineString': {
|
|
15970
|
+
const f = makeFeature('LineString', geometry, id, properties);
|
|
15971
|
+
if (f) {
|
|
15972
|
+
features.linestrings.push(f);
|
|
15973
|
+
features.points.push(new Feature({ type: 'Point', geometry: extractPoints(geometry), id, properties }));
|
|
15974
|
+
}
|
|
15975
|
+
break;
|
|
15976
|
+
}
|
|
15977
|
+
case 'Polygon': {
|
|
15978
|
+
geometry.forEach((ring, ringIndex) => {
|
|
15979
|
+
const needsCW = ringIndex === 0;
|
|
15980
|
+
let area = 0;
|
|
15981
|
+
for (let i = 0; i < ring.length; i++) {
|
|
15982
|
+
const j = (i + 1) % ring.length;
|
|
15983
|
+
area += ring[i].x * ring[j].y;
|
|
15984
|
+
area -= ring[j].x * ring[i].y;
|
|
15985
|
+
}
|
|
15986
|
+
if (area < 0 !== needsCW)
|
|
15987
|
+
ring.reverse();
|
|
15988
|
+
});
|
|
15989
|
+
const f = makeFeature('Polygon', geometry, id, properties);
|
|
15990
|
+
if (f) {
|
|
15991
|
+
features.polygons.push(f);
|
|
15992
|
+
features.linestrings.push(new Feature({ type: 'LineString', geometry, id, properties }));
|
|
15993
|
+
features.points.push(new Feature({ type: 'Point', geometry: extractPoints(geometry), id, properties }));
|
|
15994
|
+
}
|
|
15995
|
+
break;
|
|
15996
|
+
}
|
|
15997
|
+
}
|
|
15998
|
+
}
|
|
15999
|
+
function processGeometry(geom, id, properties) {
|
|
16000
|
+
const coords = geom.coordinates;
|
|
16001
|
+
switch (geom.type) {
|
|
16002
|
+
case 'Point':
|
|
16003
|
+
addFeature('Point', [[projectCoord(coords)]], id, properties);
|
|
16004
|
+
break;
|
|
16005
|
+
case 'MultiPoint':
|
|
16006
|
+
addFeature('Point', coords.map((c) => [projectCoord(c)]), id, properties);
|
|
16007
|
+
break;
|
|
16008
|
+
case 'LineString':
|
|
16009
|
+
addFeature('LineString', [coords.map((c) => projectCoord(c))], id, properties);
|
|
16010
|
+
break;
|
|
16011
|
+
case 'MultiLineString':
|
|
16012
|
+
addFeature('LineString', coords.map((line) => line.map((c) => projectCoord(c))), id, properties);
|
|
16013
|
+
break;
|
|
16014
|
+
case 'Polygon':
|
|
16015
|
+
addFeature('Polygon', coords.map((ring) => ring.map((c) => projectCoord(c))), id, properties);
|
|
16016
|
+
break;
|
|
16017
|
+
case 'MultiPolygon':
|
|
16018
|
+
addFeature('Polygon', coords.flatMap((polygon) => polygon.map((ring) => ring.map((c) => projectCoord(c)))), id, properties);
|
|
16019
|
+
break;
|
|
16020
|
+
case 'GeometryCollection':
|
|
16021
|
+
for (const g of geom.geometries) {
|
|
16022
|
+
processGeometry(g, id, properties);
|
|
16023
|
+
}
|
|
16024
|
+
break;
|
|
16025
|
+
}
|
|
16026
|
+
}
|
|
16027
|
+
const geojson = data;
|
|
16028
|
+
switch (geojson.type) {
|
|
16029
|
+
case 'FeatureCollection':
|
|
16030
|
+
for (const f of geojson.features) {
|
|
16031
|
+
processGeometry(f.geometry, f.id, (f.properties ?? {}));
|
|
16032
|
+
}
|
|
16033
|
+
break;
|
|
16034
|
+
case 'Feature':
|
|
16035
|
+
processGeometry(geojson.geometry, geojson.id, (geojson.properties ?? {}));
|
|
16036
|
+
break;
|
|
16037
|
+
default:
|
|
16038
|
+
processGeometry(geojson, undefined, {});
|
|
16039
|
+
break;
|
|
15886
16040
|
}
|
|
15887
|
-
return layerFeatures;
|
|
15888
16041
|
}
|
|
15889
16042
|
|
|
15890
16043
|
async function getRasterTiles(job, sourceName) {
|
|
@@ -15915,6 +16068,34 @@ async function getRasterTiles(job, sourceName) {
|
|
|
15915
16068
|
return rasterTiles.filter((tile) => tile !== null);
|
|
15916
16069
|
}
|
|
15917
16070
|
|
|
16071
|
+
async function getLayerFeatures(job) {
|
|
16072
|
+
const { width, height } = job.renderer;
|
|
16073
|
+
const { zoom, center } = job.view;
|
|
16074
|
+
const { sources } = job.style;
|
|
16075
|
+
const layerFeatures = new Map();
|
|
16076
|
+
for (const [sourceName, sourceSpec] of Object.entries(sources)) {
|
|
16077
|
+
const source = sourceSpec;
|
|
16078
|
+
switch (source.type) {
|
|
16079
|
+
case 'vector':
|
|
16080
|
+
await loadVectorSource(source, job, layerFeatures);
|
|
16081
|
+
break;
|
|
16082
|
+
case 'geojson':
|
|
16083
|
+
if (source.data) {
|
|
16084
|
+
loadGeoJSONSource(sourceName, source.data, width, height, zoom, center, layerFeatures);
|
|
16085
|
+
}
|
|
16086
|
+
break;
|
|
16087
|
+
}
|
|
16088
|
+
}
|
|
16089
|
+
for (const [name, features] of layerFeatures) {
|
|
16090
|
+
layerFeatures.set(name, {
|
|
16091
|
+
points: features.points,
|
|
16092
|
+
linestrings: features.linestrings,
|
|
16093
|
+
polygons: mergePolygons(features.polygons),
|
|
16094
|
+
});
|
|
16095
|
+
}
|
|
16096
|
+
return layerFeatures;
|
|
16097
|
+
}
|
|
16098
|
+
|
|
15918
16099
|
/**
|
|
15919
16100
|
* Wraps a source/composite expression for per-feature evaluation.
|
|
15920
16101
|
*/
|
|
@@ -16080,7 +16261,7 @@ async function render(job) {
|
|
|
16080
16261
|
continue;
|
|
16081
16262
|
case 'fill':
|
|
16082
16263
|
{
|
|
16083
|
-
const polygons = layerFeatures.get(layerStyle.sourceLayer)?.polygons;
|
|
16264
|
+
const polygons = (layerFeatures.get(layerStyle.sourceLayer) ?? layerFeatures.get(layerStyle.source))?.polygons;
|
|
16084
16265
|
if (!polygons || polygons.length === 0)
|
|
16085
16266
|
continue;
|
|
16086
16267
|
const filter = featureFilter(layerStyle.filter);
|
|
@@ -16098,7 +16279,7 @@ async function render(job) {
|
|
|
16098
16279
|
continue;
|
|
16099
16280
|
case 'line':
|
|
16100
16281
|
{
|
|
16101
|
-
const lineStrings = layerFeatures.get(layerStyle.sourceLayer)?.linestrings;
|
|
16282
|
+
const lineStrings = (layerFeatures.get(layerStyle.sourceLayer) ?? layerFeatures.get(layerStyle.source))?.linestrings;
|
|
16102
16283
|
if (!lineStrings || lineStrings.length === 0)
|
|
16103
16284
|
continue;
|
|
16104
16285
|
const filter = featureFilter(layerStyle.filter);
|
|
@@ -16138,6 +16319,27 @@ async function render(job) {
|
|
|
16138
16319
|
}
|
|
16139
16320
|
continue;
|
|
16140
16321
|
case 'circle':
|
|
16322
|
+
{
|
|
16323
|
+
const points = (layerFeatures.get(layerStyle.sourceLayer) ?? layerFeatures.get(layerStyle.source))?.points;
|
|
16324
|
+
if (!points || points.length === 0)
|
|
16325
|
+
continue;
|
|
16326
|
+
const filter = featureFilter(layerStyle.filter);
|
|
16327
|
+
const pointFeatures = points.filter((feature) => filter.filter({ zoom }, feature));
|
|
16328
|
+
if (pointFeatures.length === 0)
|
|
16329
|
+
continue;
|
|
16330
|
+
renderer.drawCircles(pointFeatures.map((feature) => [
|
|
16331
|
+
feature,
|
|
16332
|
+
{
|
|
16333
|
+
color: new Color(getPaint('circle-color', feature)),
|
|
16334
|
+
radius: getPaint('circle-radius', feature),
|
|
16335
|
+
blur: getPaint('circle-blur', feature),
|
|
16336
|
+
translate: new Point2D(...getPaint('circle-translate', feature)),
|
|
16337
|
+
strokeWidth: getPaint('circle-stroke-width', feature),
|
|
16338
|
+
strokeColor: new Color(getPaint('circle-stroke-color', feature)),
|
|
16339
|
+
},
|
|
16340
|
+
]), getPaint('circle-opacity', pointFeatures[0]));
|
|
16341
|
+
}
|
|
16342
|
+
continue;
|
|
16141
16343
|
case 'color-relief':
|
|
16142
16344
|
case 'fill-extrusion':
|
|
16143
16345
|
case 'heatmap':
|