@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/index.js
CHANGED
|
@@ -9050,6 +9050,20 @@ class Color {
|
|
|
9050
9050
|
return str.length < 2 ? '0' + str : str;
|
|
9051
9051
|
}
|
|
9052
9052
|
}
|
|
9053
|
+
get rgb() {
|
|
9054
|
+
return `#${d2h(this.values[0])}${d2h(this.values[1])}${d2h(this.values[2])}`;
|
|
9055
|
+
function d2h(num) {
|
|
9056
|
+
if (num < 0)
|
|
9057
|
+
num = 0;
|
|
9058
|
+
if (num > 255)
|
|
9059
|
+
num = 255;
|
|
9060
|
+
const str = Math.round(num).toString(16).toUpperCase();
|
|
9061
|
+
return str.length < 2 ? '0' + str : str;
|
|
9062
|
+
}
|
|
9063
|
+
}
|
|
9064
|
+
get opacity() {
|
|
9065
|
+
return this.values[3] / 255;
|
|
9066
|
+
}
|
|
9053
9067
|
get alpha() {
|
|
9054
9068
|
return this.values[3];
|
|
9055
9069
|
}
|
|
@@ -9095,7 +9109,7 @@ class SVGRenderer {
|
|
|
9095
9109
|
const key = style.color.hex + translate;
|
|
9096
9110
|
let group = groups.get(key);
|
|
9097
9111
|
if (!group) {
|
|
9098
|
-
group = { segments: [], attrs:
|
|
9112
|
+
group = { segments: [], attrs: `${fillAttr(style.color)}${translate}` };
|
|
9099
9113
|
groups.set(key, group);
|
|
9100
9114
|
}
|
|
9101
9115
|
feature.geometry.forEach((ring) => {
|
|
@@ -9136,8 +9150,7 @@ class SVGRenderer {
|
|
|
9136
9150
|
segments: [],
|
|
9137
9151
|
attrs: [
|
|
9138
9152
|
'fill="none"',
|
|
9139
|
-
|
|
9140
|
-
`stroke-width="${roundedWidth}"`,
|
|
9153
|
+
strokeAttr(style.color, roundedWidth),
|
|
9141
9154
|
`stroke-linecap="${style.cap}"`,
|
|
9142
9155
|
`stroke-linejoin="${style.join}"`,
|
|
9143
9156
|
`stroke-miterlimit="${String(style.miterLimit)}"`,
|
|
@@ -9156,6 +9169,43 @@ class SVGRenderer {
|
|
|
9156
9169
|
}
|
|
9157
9170
|
this.#svg.push('</g>');
|
|
9158
9171
|
}
|
|
9172
|
+
drawCircles(features, opacity) {
|
|
9173
|
+
if (features.length === 0)
|
|
9174
|
+
return;
|
|
9175
|
+
if (opacity <= 0)
|
|
9176
|
+
return;
|
|
9177
|
+
this.#svg.push(`<g opacity="${String(opacity)}">`);
|
|
9178
|
+
const groups = new Map();
|
|
9179
|
+
features.forEach(([feature, style]) => {
|
|
9180
|
+
if (style.radius <= 0 || style.color.alpha <= 0)
|
|
9181
|
+
return;
|
|
9182
|
+
const translate = style.translate.isZero()
|
|
9183
|
+
? ''
|
|
9184
|
+
: ` transform="translate(${formatPoint(style.translate, this.#scale)})"`;
|
|
9185
|
+
const roundedRadius = roundValue(style.radius, this.#scale);
|
|
9186
|
+
const strokeAttrs = style.strokeWidth > 0
|
|
9187
|
+
? ` ${strokeAttr(style.strokeColor, roundValue(style.strokeWidth, this.#scale))}`
|
|
9188
|
+
: '';
|
|
9189
|
+
const key = [style.color.hex, roundedRadius, strokeAttrs, translate].join('\0');
|
|
9190
|
+
let group = groups.get(key);
|
|
9191
|
+
if (!group) {
|
|
9192
|
+
group = {
|
|
9193
|
+
points: [],
|
|
9194
|
+
attrs: `r="${roundedRadius}" ${fillAttr(style.color)}${strokeAttrs}${translate}`,
|
|
9195
|
+
};
|
|
9196
|
+
groups.set(key, group);
|
|
9197
|
+
}
|
|
9198
|
+
feature.geometry.forEach((ring) => {
|
|
9199
|
+
group.points.push(roundXY(ring[0], this.#scale));
|
|
9200
|
+
});
|
|
9201
|
+
});
|
|
9202
|
+
for (const { points, attrs } of groups.values()) {
|
|
9203
|
+
for (const [x, y] of points) {
|
|
9204
|
+
this.#svg.push(`<circle cx="${formatNum(x)}" cy="${formatNum(y)}" ${attrs} />`);
|
|
9205
|
+
}
|
|
9206
|
+
}
|
|
9207
|
+
this.#svg.push('</g>');
|
|
9208
|
+
}
|
|
9159
9209
|
drawRasterTiles(tiles, style) {
|
|
9160
9210
|
if (tiles.length === 0)
|
|
9161
9211
|
return;
|
|
@@ -9188,13 +9238,32 @@ class SVGRenderer {
|
|
|
9188
9238
|
this.#svg.push('</g>');
|
|
9189
9239
|
}
|
|
9190
9240
|
getString() {
|
|
9191
|
-
|
|
9192
|
-
|
|
9193
|
-
|
|
9194
|
-
|
|
9195
|
-
|
|
9241
|
+
const w = this.width.toFixed(0);
|
|
9242
|
+
const h = this.height.toFixed(0);
|
|
9243
|
+
const parts = [
|
|
9244
|
+
`<svg viewBox="0 0 ${w} ${h}" width="${w}" height="${h}" xmlns="http://www.w3.org/2000/svg">`,
|
|
9245
|
+
`<defs><clipPath id="vb"><rect width="${w}" height="${h}"/></clipPath></defs>`,
|
|
9246
|
+
`<g clip-path="url(#vb)">`,
|
|
9247
|
+
];
|
|
9248
|
+
if (this.#backgroundColor.alpha > 0) {
|
|
9249
|
+
parts.push(`<rect x="-1" y="-1" width="${(this.width + 2).toFixed(0)}" height="${(this.height + 2).toFixed(0)}" ${fillAttr(this.#backgroundColor)} />`);
|
|
9250
|
+
}
|
|
9251
|
+
parts.push(...this.#svg, '</g>', '</svg>');
|
|
9252
|
+
return parts.join('\n');
|
|
9196
9253
|
}
|
|
9197
9254
|
}
|
|
9255
|
+
function fillAttr(color) {
|
|
9256
|
+
let attr = `fill="${color.rgb}"`;
|
|
9257
|
+
if (color.alpha < 255)
|
|
9258
|
+
attr += ` fill-opacity="${color.opacity.toFixed(3)}"`;
|
|
9259
|
+
return attr;
|
|
9260
|
+
}
|
|
9261
|
+
function strokeAttr(color, width) {
|
|
9262
|
+
let attr = `stroke="${color.rgb}" stroke-width="${width}"`;
|
|
9263
|
+
if (color.alpha < 255)
|
|
9264
|
+
attr += ` stroke-opacity="${color.opacity.toFixed(3)}"`;
|
|
9265
|
+
return attr;
|
|
9266
|
+
}
|
|
9198
9267
|
function roundValue(v, scale) {
|
|
9199
9268
|
return (v * scale).toFixed(3);
|
|
9200
9269
|
}
|
|
@@ -9305,77 +9374,6 @@ function formatNum(tenths) {
|
|
|
9305
9374
|
return (negative ? '-' : '') + String(whole) + '.' + String(frac);
|
|
9306
9375
|
}
|
|
9307
9376
|
|
|
9308
|
-
class Point2D {
|
|
9309
|
-
x;
|
|
9310
|
-
y;
|
|
9311
|
-
constructor(x, y) {
|
|
9312
|
-
this.x = x;
|
|
9313
|
-
this.y = y;
|
|
9314
|
-
}
|
|
9315
|
-
isZero() {
|
|
9316
|
-
return this.x === 0 && this.y === 0;
|
|
9317
|
-
}
|
|
9318
|
-
scale(factor) {
|
|
9319
|
-
this.x *= factor;
|
|
9320
|
-
this.y *= factor;
|
|
9321
|
-
return this;
|
|
9322
|
-
}
|
|
9323
|
-
translate(offset) {
|
|
9324
|
-
this.x += offset.x;
|
|
9325
|
-
this.y += offset.y;
|
|
9326
|
-
return this;
|
|
9327
|
-
}
|
|
9328
|
-
getProject2Pixel() {
|
|
9329
|
-
const s = Math.sin((this.y * Math.PI) / 180.0);
|
|
9330
|
-
return new Point2D(this.x / 360.0 + 0.5, 0.5 - (0.25 * Math.log((1 + s) / (1 - s))) / Math.PI);
|
|
9331
|
-
}
|
|
9332
|
-
}
|
|
9333
|
-
class Feature {
|
|
9334
|
-
type;
|
|
9335
|
-
id;
|
|
9336
|
-
properties;
|
|
9337
|
-
patterns;
|
|
9338
|
-
geometry;
|
|
9339
|
-
constructor(opt) {
|
|
9340
|
-
this.type = opt.type;
|
|
9341
|
-
this.id = opt.id;
|
|
9342
|
-
this.properties = opt.properties;
|
|
9343
|
-
this.patterns = opt.patterns;
|
|
9344
|
-
this.geometry = opt.geometry;
|
|
9345
|
-
}
|
|
9346
|
-
getBbox() {
|
|
9347
|
-
let xMin = Infinity;
|
|
9348
|
-
let yMin = Infinity;
|
|
9349
|
-
let xMax = -Infinity;
|
|
9350
|
-
let yMax = -Infinity;
|
|
9351
|
-
this.geometry.forEach((ring) => {
|
|
9352
|
-
ring.forEach((point) => {
|
|
9353
|
-
if (xMin > point.x)
|
|
9354
|
-
xMin = point.x;
|
|
9355
|
-
if (yMin > point.y)
|
|
9356
|
-
yMin = point.y;
|
|
9357
|
-
if (xMax < point.x)
|
|
9358
|
-
xMax = point.x;
|
|
9359
|
-
if (yMax < point.y)
|
|
9360
|
-
yMax = point.y;
|
|
9361
|
-
});
|
|
9362
|
-
});
|
|
9363
|
-
return [xMin, yMin, xMax, yMax];
|
|
9364
|
-
}
|
|
9365
|
-
doesOverlap(bbox) {
|
|
9366
|
-
const featureBbox = this.getBbox();
|
|
9367
|
-
if (featureBbox[0] > bbox[2])
|
|
9368
|
-
return false;
|
|
9369
|
-
if (featureBbox[1] > bbox[3])
|
|
9370
|
-
return false;
|
|
9371
|
-
if (featureBbox[2] < bbox[0])
|
|
9372
|
-
return false;
|
|
9373
|
-
if (featureBbox[3] < bbox[1])
|
|
9374
|
-
return false;
|
|
9375
|
-
return true;
|
|
9376
|
-
}
|
|
9377
|
-
}
|
|
9378
|
-
|
|
9379
9377
|
/*
|
|
9380
9378
|
* bignumber.js v9.3.1
|
|
9381
9379
|
* A JavaScript library for arbitrary-precision arithmetic.
|
|
@@ -14007,6 +14005,77 @@ function union2(features, options = {}) {
|
|
|
14007
14005
|
else return multiPolygon(unioned, options.properties);
|
|
14008
14006
|
}
|
|
14009
14007
|
|
|
14008
|
+
class Point2D {
|
|
14009
|
+
x;
|
|
14010
|
+
y;
|
|
14011
|
+
constructor(x, y) {
|
|
14012
|
+
this.x = x;
|
|
14013
|
+
this.y = y;
|
|
14014
|
+
}
|
|
14015
|
+
isZero() {
|
|
14016
|
+
return this.x === 0 && this.y === 0;
|
|
14017
|
+
}
|
|
14018
|
+
scale(factor) {
|
|
14019
|
+
this.x *= factor;
|
|
14020
|
+
this.y *= factor;
|
|
14021
|
+
return this;
|
|
14022
|
+
}
|
|
14023
|
+
translate(offset) {
|
|
14024
|
+
this.x += offset.x;
|
|
14025
|
+
this.y += offset.y;
|
|
14026
|
+
return this;
|
|
14027
|
+
}
|
|
14028
|
+
getProject2Pixel() {
|
|
14029
|
+
const s = Math.sin((this.y * Math.PI) / 180.0);
|
|
14030
|
+
return new Point2D(this.x / 360.0 + 0.5, 0.5 - (0.25 * Math.log((1 + s) / (1 - s))) / Math.PI);
|
|
14031
|
+
}
|
|
14032
|
+
}
|
|
14033
|
+
class Feature {
|
|
14034
|
+
type;
|
|
14035
|
+
id;
|
|
14036
|
+
properties;
|
|
14037
|
+
patterns;
|
|
14038
|
+
geometry;
|
|
14039
|
+
constructor(opt) {
|
|
14040
|
+
this.type = opt.type;
|
|
14041
|
+
this.id = opt.id;
|
|
14042
|
+
this.properties = opt.properties;
|
|
14043
|
+
this.patterns = opt.patterns;
|
|
14044
|
+
this.geometry = opt.geometry;
|
|
14045
|
+
}
|
|
14046
|
+
getBbox() {
|
|
14047
|
+
let xMin = Infinity;
|
|
14048
|
+
let yMin = Infinity;
|
|
14049
|
+
let xMax = -Infinity;
|
|
14050
|
+
let yMax = -Infinity;
|
|
14051
|
+
this.geometry.forEach((ring) => {
|
|
14052
|
+
ring.forEach((point) => {
|
|
14053
|
+
if (xMin > point.x)
|
|
14054
|
+
xMin = point.x;
|
|
14055
|
+
if (yMin > point.y)
|
|
14056
|
+
yMin = point.y;
|
|
14057
|
+
if (xMax < point.x)
|
|
14058
|
+
xMax = point.x;
|
|
14059
|
+
if (yMax < point.y)
|
|
14060
|
+
yMax = point.y;
|
|
14061
|
+
});
|
|
14062
|
+
});
|
|
14063
|
+
return [xMin, yMin, xMax, yMax];
|
|
14064
|
+
}
|
|
14065
|
+
doesOverlap(bbox) {
|
|
14066
|
+
const featureBbox = this.getBbox();
|
|
14067
|
+
if (featureBbox[0] > bbox[2])
|
|
14068
|
+
return false;
|
|
14069
|
+
if (featureBbox[1] > bbox[3])
|
|
14070
|
+
return false;
|
|
14071
|
+
if (featureBbox[2] < bbox[0])
|
|
14072
|
+
return false;
|
|
14073
|
+
if (featureBbox[3] < bbox[1])
|
|
14074
|
+
return false;
|
|
14075
|
+
return true;
|
|
14076
|
+
}
|
|
14077
|
+
}
|
|
14078
|
+
|
|
14010
14079
|
function geojsonToFeature(id, polygonFeature) {
|
|
14011
14080
|
const geometry = polygonFeature.geometry.coordinates.map((ring) => {
|
|
14012
14081
|
return ring.map((coord) => new Point2D(coord[0], coord[1]));
|
|
@@ -14040,15 +14109,13 @@ function mergePolygons(featureList) {
|
|
|
14040
14109
|
const turfFeatures = [];
|
|
14041
14110
|
features.forEach((f) => {
|
|
14042
14111
|
const rings = f.geometry.map((ring) => ring.map((p) => [p.x, p.y]));
|
|
14043
|
-
|
|
14044
|
-
|
|
14045
|
-
|
|
14046
|
-
|
|
14047
|
-
|
|
14048
|
-
|
|
14049
|
-
|
|
14050
|
-
properties: f.properties,
|
|
14051
|
-
});
|
|
14112
|
+
turfFeatures.push({
|
|
14113
|
+
type: 'Feature',
|
|
14114
|
+
geometry: {
|
|
14115
|
+
type: 'Polygon',
|
|
14116
|
+
coordinates: rings,
|
|
14117
|
+
},
|
|
14118
|
+
properties: f.properties,
|
|
14052
14119
|
});
|
|
14053
14120
|
});
|
|
14054
14121
|
const merged = union2({
|
|
@@ -15657,24 +15724,16 @@ function writeUtf8(buf, str, pos) {
|
|
|
15657
15724
|
}
|
|
15658
15725
|
|
|
15659
15726
|
const TILE_EXTENT = 4096;
|
|
15660
|
-
async function
|
|
15727
|
+
async function loadVectorSource(source, job, layerFeatures) {
|
|
15728
|
+
const tiles = source.tiles;
|
|
15729
|
+
if (!tiles)
|
|
15730
|
+
return;
|
|
15661
15731
|
const { width, height } = job.renderer;
|
|
15662
15732
|
const { zoom, center } = job.view;
|
|
15663
|
-
const { sources } = job.style;
|
|
15664
|
-
const source = sources['versatiles-shortbread'];
|
|
15665
|
-
if (!source)
|
|
15666
|
-
return new Map();
|
|
15667
|
-
if (source.type !== 'vector' || !source.tiles) {
|
|
15668
|
-
console.error('Invalid source configuration. Expected a vector source with tile URLs.');
|
|
15669
|
-
console.error('Source config:', source);
|
|
15670
|
-
throw Error('Invalid source');
|
|
15671
|
-
}
|
|
15672
|
-
const sourceUrl = source.tiles[0];
|
|
15673
15733
|
const { zoomLevel, tileSize, tiles: tileCoordinates, } = calculateTileGrid(width, height, center, zoom, source.maxzoom);
|
|
15674
|
-
const layerFeatures = new Map();
|
|
15675
15734
|
await Promise.all(tileCoordinates.map(async ({ x, y, offsetX, offsetY }) => {
|
|
15676
15735
|
const offset = new Point2D(offsetX, offsetY);
|
|
15677
|
-
const tile = await getTile(
|
|
15736
|
+
const tile = await getTile(tiles[0], zoomLevel, x, y);
|
|
15678
15737
|
if (!tile)
|
|
15679
15738
|
return;
|
|
15680
15739
|
const vectorTile = new VectorTile(new Pbf(tile.buffer));
|
|
@@ -15718,14 +15777,108 @@ async function getLayerFeatures(job) {
|
|
|
15718
15777
|
}
|
|
15719
15778
|
}
|
|
15720
15779
|
}));
|
|
15721
|
-
|
|
15722
|
-
|
|
15723
|
-
|
|
15724
|
-
|
|
15725
|
-
|
|
15726
|
-
|
|
15780
|
+
}
|
|
15781
|
+
|
|
15782
|
+
function loadGeoJSONSource(sourceName, data, width, height, zoom, center, layerFeatures) {
|
|
15783
|
+
const existing = layerFeatures.get(sourceName);
|
|
15784
|
+
const features = existing ?? { points: [], linestrings: [], polygons: [] };
|
|
15785
|
+
if (!existing)
|
|
15786
|
+
layerFeatures.set(sourceName, features);
|
|
15787
|
+
const worldSize = 512 * 2 ** zoom;
|
|
15788
|
+
const centerMercator = center.getProject2Pixel();
|
|
15789
|
+
function projectCoord(coord) {
|
|
15790
|
+
const mercator = new Point2D(coord[0], coord[1]).getProject2Pixel();
|
|
15791
|
+
return new Point2D((mercator.x - centerMercator.x) * worldSize + width / 2, (mercator.y - centerMercator.y) * worldSize + height / 2);
|
|
15792
|
+
}
|
|
15793
|
+
function makeFeature(type, geometry, id, properties) {
|
|
15794
|
+
const feature = new Feature({ type, geometry, id, properties });
|
|
15795
|
+
if (!feature.doesOverlap([0, 0, width, height]))
|
|
15796
|
+
return null;
|
|
15797
|
+
return feature;
|
|
15798
|
+
}
|
|
15799
|
+
function extractPoints(geometry) {
|
|
15800
|
+
return geometry.flatMap((ring) => ring.map((p) => [p]));
|
|
15801
|
+
}
|
|
15802
|
+
function addFeature(type, geometry, id, properties) {
|
|
15803
|
+
switch (type) {
|
|
15804
|
+
case 'Point': {
|
|
15805
|
+
const f = makeFeature('Point', geometry, id, properties);
|
|
15806
|
+
if (f)
|
|
15807
|
+
features.points.push(f);
|
|
15808
|
+
break;
|
|
15809
|
+
}
|
|
15810
|
+
case 'LineString': {
|
|
15811
|
+
const f = makeFeature('LineString', geometry, id, properties);
|
|
15812
|
+
if (f) {
|
|
15813
|
+
features.linestrings.push(f);
|
|
15814
|
+
features.points.push(new Feature({ type: 'Point', geometry: extractPoints(geometry), id, properties }));
|
|
15815
|
+
}
|
|
15816
|
+
break;
|
|
15817
|
+
}
|
|
15818
|
+
case 'Polygon': {
|
|
15819
|
+
geometry.forEach((ring, ringIndex) => {
|
|
15820
|
+
const needsCW = ringIndex === 0;
|
|
15821
|
+
let area = 0;
|
|
15822
|
+
for (let i = 0; i < ring.length; i++) {
|
|
15823
|
+
const j = (i + 1) % ring.length;
|
|
15824
|
+
area += ring[i].x * ring[j].y;
|
|
15825
|
+
area -= ring[j].x * ring[i].y;
|
|
15826
|
+
}
|
|
15827
|
+
if (area < 0 !== needsCW)
|
|
15828
|
+
ring.reverse();
|
|
15829
|
+
});
|
|
15830
|
+
const f = makeFeature('Polygon', geometry, id, properties);
|
|
15831
|
+
if (f) {
|
|
15832
|
+
features.polygons.push(f);
|
|
15833
|
+
features.linestrings.push(new Feature({ type: 'LineString', geometry, id, properties }));
|
|
15834
|
+
features.points.push(new Feature({ type: 'Point', geometry: extractPoints(geometry), id, properties }));
|
|
15835
|
+
}
|
|
15836
|
+
break;
|
|
15837
|
+
}
|
|
15838
|
+
}
|
|
15839
|
+
}
|
|
15840
|
+
function processGeometry(geom, id, properties) {
|
|
15841
|
+
const coords = geom.coordinates;
|
|
15842
|
+
switch (geom.type) {
|
|
15843
|
+
case 'Point':
|
|
15844
|
+
addFeature('Point', [[projectCoord(coords)]], id, properties);
|
|
15845
|
+
break;
|
|
15846
|
+
case 'MultiPoint':
|
|
15847
|
+
addFeature('Point', coords.map((c) => [projectCoord(c)]), id, properties);
|
|
15848
|
+
break;
|
|
15849
|
+
case 'LineString':
|
|
15850
|
+
addFeature('LineString', [coords.map((c) => projectCoord(c))], id, properties);
|
|
15851
|
+
break;
|
|
15852
|
+
case 'MultiLineString':
|
|
15853
|
+
addFeature('LineString', coords.map((line) => line.map((c) => projectCoord(c))), id, properties);
|
|
15854
|
+
break;
|
|
15855
|
+
case 'Polygon':
|
|
15856
|
+
addFeature('Polygon', coords.map((ring) => ring.map((c) => projectCoord(c))), id, properties);
|
|
15857
|
+
break;
|
|
15858
|
+
case 'MultiPolygon':
|
|
15859
|
+
addFeature('Polygon', coords.flatMap((polygon) => polygon.map((ring) => ring.map((c) => projectCoord(c)))), id, properties);
|
|
15860
|
+
break;
|
|
15861
|
+
case 'GeometryCollection':
|
|
15862
|
+
for (const g of geom.geometries) {
|
|
15863
|
+
processGeometry(g, id, properties);
|
|
15864
|
+
}
|
|
15865
|
+
break;
|
|
15866
|
+
}
|
|
15867
|
+
}
|
|
15868
|
+
const geojson = data;
|
|
15869
|
+
switch (geojson.type) {
|
|
15870
|
+
case 'FeatureCollection':
|
|
15871
|
+
for (const f of geojson.features) {
|
|
15872
|
+
processGeometry(f.geometry, f.id, (f.properties ?? {}));
|
|
15873
|
+
}
|
|
15874
|
+
break;
|
|
15875
|
+
case 'Feature':
|
|
15876
|
+
processGeometry(geojson.geometry, geojson.id, (geojson.properties ?? {}));
|
|
15877
|
+
break;
|
|
15878
|
+
default:
|
|
15879
|
+
processGeometry(geojson, undefined, {});
|
|
15880
|
+
break;
|
|
15727
15881
|
}
|
|
15728
|
-
return layerFeatures;
|
|
15729
15882
|
}
|
|
15730
15883
|
|
|
15731
15884
|
async function getRasterTiles(job, sourceName) {
|
|
@@ -15756,6 +15909,34 @@ async function getRasterTiles(job, sourceName) {
|
|
|
15756
15909
|
return rasterTiles.filter((tile) => tile !== null);
|
|
15757
15910
|
}
|
|
15758
15911
|
|
|
15912
|
+
async function getLayerFeatures(job) {
|
|
15913
|
+
const { width, height } = job.renderer;
|
|
15914
|
+
const { zoom, center } = job.view;
|
|
15915
|
+
const { sources } = job.style;
|
|
15916
|
+
const layerFeatures = new Map();
|
|
15917
|
+
for (const [sourceName, sourceSpec] of Object.entries(sources)) {
|
|
15918
|
+
const source = sourceSpec;
|
|
15919
|
+
switch (source.type) {
|
|
15920
|
+
case 'vector':
|
|
15921
|
+
await loadVectorSource(source, job, layerFeatures);
|
|
15922
|
+
break;
|
|
15923
|
+
case 'geojson':
|
|
15924
|
+
if (source.data) {
|
|
15925
|
+
loadGeoJSONSource(sourceName, source.data, width, height, zoom, center, layerFeatures);
|
|
15926
|
+
}
|
|
15927
|
+
break;
|
|
15928
|
+
}
|
|
15929
|
+
}
|
|
15930
|
+
for (const [name, features] of layerFeatures) {
|
|
15931
|
+
layerFeatures.set(name, {
|
|
15932
|
+
points: features.points,
|
|
15933
|
+
linestrings: features.linestrings,
|
|
15934
|
+
polygons: mergePolygons(features.polygons),
|
|
15935
|
+
});
|
|
15936
|
+
}
|
|
15937
|
+
return layerFeatures;
|
|
15938
|
+
}
|
|
15939
|
+
|
|
15759
15940
|
/**
|
|
15760
15941
|
* Wraps a source/composite expression for per-feature evaluation.
|
|
15761
15942
|
*/
|
|
@@ -15921,7 +16102,7 @@ async function render(job) {
|
|
|
15921
16102
|
continue;
|
|
15922
16103
|
case 'fill':
|
|
15923
16104
|
{
|
|
15924
|
-
const polygons = layerFeatures.get(layerStyle.sourceLayer)?.polygons;
|
|
16105
|
+
const polygons = (layerFeatures.get(layerStyle.sourceLayer) ?? layerFeatures.get(layerStyle.source))?.polygons;
|
|
15925
16106
|
if (!polygons || polygons.length === 0)
|
|
15926
16107
|
continue;
|
|
15927
16108
|
const filter = featureFilter(layerStyle.filter);
|
|
@@ -15939,7 +16120,7 @@ async function render(job) {
|
|
|
15939
16120
|
continue;
|
|
15940
16121
|
case 'line':
|
|
15941
16122
|
{
|
|
15942
|
-
const lineStrings = layerFeatures.get(layerStyle.sourceLayer)?.linestrings;
|
|
16123
|
+
const lineStrings = (layerFeatures.get(layerStyle.sourceLayer) ?? layerFeatures.get(layerStyle.source))?.linestrings;
|
|
15943
16124
|
if (!lineStrings || lineStrings.length === 0)
|
|
15944
16125
|
continue;
|
|
15945
16126
|
const filter = featureFilter(layerStyle.filter);
|
|
@@ -15979,6 +16160,27 @@ async function render(job) {
|
|
|
15979
16160
|
}
|
|
15980
16161
|
continue;
|
|
15981
16162
|
case 'circle':
|
|
16163
|
+
{
|
|
16164
|
+
const points = (layerFeatures.get(layerStyle.sourceLayer) ?? layerFeatures.get(layerStyle.source))?.points;
|
|
16165
|
+
if (!points || points.length === 0)
|
|
16166
|
+
continue;
|
|
16167
|
+
const filter = featureFilter(layerStyle.filter);
|
|
16168
|
+
const pointFeatures = points.filter((feature) => filter.filter({ zoom }, feature));
|
|
16169
|
+
if (pointFeatures.length === 0)
|
|
16170
|
+
continue;
|
|
16171
|
+
renderer.drawCircles(pointFeatures.map((feature) => [
|
|
16172
|
+
feature,
|
|
16173
|
+
{
|
|
16174
|
+
color: new Color(getPaint('circle-color', feature)),
|
|
16175
|
+
radius: getPaint('circle-radius', feature),
|
|
16176
|
+
blur: getPaint('circle-blur', feature),
|
|
16177
|
+
translate: new Point2D(...getPaint('circle-translate', feature)),
|
|
16178
|
+
strokeWidth: getPaint('circle-stroke-width', feature),
|
|
16179
|
+
strokeColor: new Color(getPaint('circle-stroke-color', feature)),
|
|
16180
|
+
},
|
|
16181
|
+
]), getPaint('circle-opacity', pointFeatures[0]));
|
|
16182
|
+
}
|
|
16183
|
+
continue;
|
|
15982
16184
|
case 'color-relief':
|
|
15983
16185
|
case 'fill-extrusion':
|
|
15984
16186
|
case 'heatmap':
|