@deck.gl/carto 9.1.9 → 9.1.11

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.
Files changed (79) hide show
  1. package/dist/api/fetch-map.d.ts.map +1 -1
  2. package/dist/api/fetch-map.js +1 -0
  3. package/dist/api/fetch-map.js.map +1 -1
  4. package/dist/api/parse-map.d.ts.map +1 -1
  5. package/dist/api/parse-map.js.map +1 -1
  6. package/dist/cartoPropertiesTile-worker.js +9 -1
  7. package/dist/cartoRasterTile-worker.js +9 -1
  8. package/dist/cartoSpatialTile-worker.js +9 -1
  9. package/dist/cartoVectorTile-worker.js +9 -1
  10. package/dist/dist.dev.js +469 -138
  11. package/dist/index.cjs +423 -139
  12. package/dist/index.cjs.map +4 -4
  13. package/dist/layers/cluster-tile-layer.d.ts.map +1 -1
  14. package/dist/layers/cluster-tile-layer.js +6 -5
  15. package/dist/layers/cluster-tile-layer.js.map +1 -1
  16. package/dist/layers/cluster-utils.d.ts.map +1 -1
  17. package/dist/layers/cluster-utils.js +3 -28
  18. package/dist/layers/cluster-utils.js.map +1 -1
  19. package/dist/layers/h3-tile-layer.d.ts.map +1 -1
  20. package/dist/layers/h3-tile-layer.js +5 -5
  21. package/dist/layers/h3-tile-layer.js.map +1 -1
  22. package/dist/layers/heatmap-tile-layer.d.ts.map +1 -1
  23. package/dist/layers/heatmap-tile-layer.js.map +1 -1
  24. package/dist/layers/label-utils.d.ts +7 -0
  25. package/dist/layers/label-utils.d.ts.map +1 -0
  26. package/dist/layers/label-utils.js +218 -0
  27. package/dist/layers/label-utils.js.map +1 -0
  28. package/dist/layers/point-label-layer.d.ts +6 -0
  29. package/dist/layers/point-label-layer.d.ts.map +1 -1
  30. package/dist/layers/point-label-layer.js +13 -1
  31. package/dist/layers/point-label-layer.js.map +1 -1
  32. package/dist/layers/quadbin-tile-layer.d.ts.map +1 -1
  33. package/dist/layers/quadbin-tile-layer.js +5 -5
  34. package/dist/layers/quadbin-tile-layer.js.map +1 -1
  35. package/dist/layers/raster-tile-layer.js +4 -4
  36. package/dist/layers/raster-tile-layer.js.map +1 -1
  37. package/dist/layers/schema/carto-properties-tile-loader.js +1 -1
  38. package/dist/layers/schema/carto-properties-tile-loader.js.map +1 -1
  39. package/dist/layers/schema/carto-raster-tile-loader.js +1 -1
  40. package/dist/layers/schema/carto-raster-tile-loader.js.map +1 -1
  41. package/dist/layers/schema/carto-spatial-tile-loader.js +1 -1
  42. package/dist/layers/schema/carto-spatial-tile-loader.js.map +1 -1
  43. package/dist/layers/schema/carto-tile.d.ts +3 -1
  44. package/dist/layers/schema/carto-tile.d.ts.map +1 -1
  45. package/dist/layers/schema/carto-tile.js.map +1 -1
  46. package/dist/layers/schema/carto-vector-tile-loader.js +1 -1
  47. package/dist/layers/schema/carto-vector-tile-loader.js.map +1 -1
  48. package/dist/layers/schema/spatialjson-utils.d.ts +4 -7
  49. package/dist/layers/schema/spatialjson-utils.d.ts.map +1 -1
  50. package/dist/layers/schema/spatialjson-utils.js.map +1 -1
  51. package/dist/layers/utils.d.ts +3 -2
  52. package/dist/layers/utils.d.ts.map +1 -1
  53. package/dist/layers/utils.js +28 -7
  54. package/dist/layers/utils.js.map +1 -1
  55. package/dist/layers/vector-tile-layer.d.ts +12 -4
  56. package/dist/layers/vector-tile-layer.d.ts.map +1 -1
  57. package/dist/layers/vector-tile-layer.js +76 -35
  58. package/dist/layers/vector-tile-layer.js.map +1 -1
  59. package/dist/utils.d.ts +6 -1
  60. package/dist/utils.d.ts.map +1 -1
  61. package/dist/utils.js +53 -0
  62. package/dist/utils.js.map +1 -1
  63. package/dist.min.js +12 -12
  64. package/package.json +2 -2
  65. package/src/api/fetch-map.ts +2 -0
  66. package/src/api/parse-map.ts +2 -0
  67. package/src/layers/cluster-tile-layer.ts +7 -5
  68. package/src/layers/cluster-utils.ts +3 -29
  69. package/src/layers/h3-tile-layer.ts +6 -6
  70. package/src/layers/heatmap-tile-layer.ts +2 -0
  71. package/src/layers/label-utils.ts +304 -0
  72. package/src/layers/point-label-layer.ts +18 -1
  73. package/src/layers/quadbin-tile-layer.ts +6 -6
  74. package/src/layers/raster-tile-layer.ts +4 -4
  75. package/src/layers/schema/carto-tile.ts +3 -1
  76. package/src/layers/schema/spatialjson-utils.ts +4 -3
  77. package/src/layers/utils.ts +31 -7
  78. package/src/layers/vector-tile-layer.ts +100 -42
  79. package/src/utils.ts +79 -0
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "description": "CARTO official integration with Deck.gl. Build geospatial applications using CARTO and Deck.gl.",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
- "version": "9.1.9",
6
+ "version": "9.1.11",
7
7
  "publishConfig": {
8
8
  "access": "public"
9
9
  },
@@ -75,5 +75,5 @@
75
75
  "@loaders.gl/core": "^4.2.0",
76
76
  "@luma.gl/core": "^9.1.5"
77
77
  },
78
- "gitHead": "475ddb7430a845490f29a0a171b945b801b031f6"
78
+ "gitHead": "13816c3da4f7deab1538dbc590d5267349a512e2"
79
79
  }
@@ -2,6 +2,8 @@
2
2
  // SPDX-License-Identifier: MIT
3
3
  // Copyright (c) vis.gl contributors
4
4
 
5
+ /* eslint-disable @typescript-eslint/no-shadow */
6
+
5
7
  import {
6
8
  DEFAULT_API_BASE_URL,
7
9
  APIErrorContext,
@@ -2,6 +2,8 @@
2
2
  // SPDX-License-Identifier: MIT
3
3
  // Copyright (c) vis.gl contributors
4
4
 
5
+ /* eslint-disable no-shadow */
6
+
5
7
  import {ColorParameters} from '@luma.gl/core';
6
8
  import {Layer, log} from '@deck.gl/core';
7
9
  import {
@@ -2,6 +2,8 @@
2
2
  // SPDX-License-Identifier: MIT
3
3
  // Copyright (c) vis.gl contributors
4
4
 
5
+ /* eslint-disable no-shadow */
6
+
5
7
  import {GeoJsonLayer, GeoJsonLayerProps} from '@deck.gl/layers';
6
8
  import {
7
9
  TileLayer,
@@ -38,7 +40,7 @@ import {DEFAULT_TILE_SIZE} from '../constants';
38
40
  import QuadbinTileset2D from './quadbin-tileset-2d';
39
41
  import {getQuadbinPolygon} from './quadbin-utils';
40
42
  import CartoSpatialTileLoader from './schema/carto-spatial-tile-loader';
41
- import {injectAccessToken, TilejsonPropType} from './utils';
43
+ import {TilejsonPropType, mergeLoadOptions} from './utils';
42
44
  import type {TilejsonResult} from '@carto/api-client';
43
45
 
44
46
  registerLoaders([CartoSpatialTileLoader]);
@@ -216,11 +218,11 @@ export default class ClusterTileLayer<
216
218
  static defaultProps = defaultProps;
217
219
 
218
220
  getLoadOptions(): any {
219
- const loadOptions = super.getLoadOptions() || {};
220
221
  const tileJSON = this.props.data as TilejsonResult;
221
- injectAccessToken(loadOptions, tileJSON.accessToken);
222
- loadOptions.cartoSpatialTile = {...loadOptions.cartoSpatialTile, scheme: 'quadbin'};
223
- return loadOptions;
222
+ return mergeLoadOptions(super.getLoadOptions(), {
223
+ fetch: {headers: {Authorization: `Bearer ${tileJSON.accessToken}`}},
224
+ cartoSpatialTile: {scheme: 'quadbin'}
225
+ });
224
226
  }
225
227
 
226
228
  renderLayers(): Layer | null | LayersList {
@@ -6,6 +6,7 @@ import {cellToParent} from 'quadbin';
6
6
  import {_Tile2DHeader as Tile2DHeader} from '@deck.gl/geo-layers';
7
7
  import {Accessor, log} from '@deck.gl/core';
8
8
  import {BinaryFeatureCollection} from '@loaders.gl/schema';
9
+ import {createBinaryPointFeature, createEmptyBinary} from '../utils';
9
10
 
10
11
  export type Aggregation = 'any' | 'average' | 'count' | 'min' | 'max' | 'sum';
11
12
  export type AggregationProperties<FeaturePropertiesT> = {
@@ -137,15 +138,6 @@ export function computeAggregationStats<FeaturePropertiesT>(
137
138
  return stats;
138
139
  }
139
140
 
140
- const EMPTY_UINT16ARRAY = new Uint16Array();
141
- const EMPTY_BINARY_PROPS = {
142
- positions: {value: new Float32Array(), size: 2},
143
- properties: [],
144
- numericProps: {},
145
- featureIds: {value: EMPTY_UINT16ARRAY, size: 1},
146
- globalFeatureIds: {value: EMPTY_UINT16ARRAY, size: 1}
147
- };
148
-
149
141
  type BinaryFeatureCollectionWithStats<FeaturePropertiesT> = Omit<
150
142
  BinaryFeatureCollection,
151
143
  'points'
@@ -168,25 +160,7 @@ export function clustersToBinary<FeaturePropertiesT>(
168
160
  }
169
161
 
170
162
  return {
171
- shape: 'binary-feature-collection',
172
- points: {
173
- type: 'Point',
174
- positions: {value: positions, size: 2},
175
- properties: data,
176
- numericProps: {},
177
- featureIds: {value: featureIds, size: 1},
178
- globalFeatureIds: {value: featureIds, size: 1}
179
- },
180
- lines: {
181
- type: 'LineString',
182
- pathIndices: {value: EMPTY_UINT16ARRAY, size: 1},
183
- ...EMPTY_BINARY_PROPS
184
- },
185
- polygons: {
186
- type: 'Polygon',
187
- polygonIndices: {value: EMPTY_UINT16ARRAY, size: 1},
188
- primitivePolygonIndices: {value: EMPTY_UINT16ARRAY, size: 1},
189
- ...EMPTY_BINARY_PROPS
190
- }
163
+ ...createEmptyBinary(),
164
+ points: createBinaryPointFeature(positions, featureIds, featureIds, {}, data)
191
165
  };
192
166
  }
@@ -2,12 +2,12 @@
2
2
  // SPDX-License-Identifier: MIT
3
3
  // Copyright (c) vis.gl contributors
4
4
 
5
- import {CompositeLayer, CompositeLayerProps, Layer, LayersList, DefaultProps} from '@deck.gl/core';
5
+ import {CompositeLayer, CompositeLayerProps, DefaultProps} from '@deck.gl/core';
6
6
  import {H3HexagonLayer, H3HexagonLayerProps} from '@deck.gl/geo-layers';
7
7
  import H3Tileset2D, {getHexagonResolution} from './h3-tileset-2d';
8
8
  import SpatialIndexTileLayer, {SpatialIndexTileLayerProps} from './spatial-index-tile-layer';
9
9
  import type {TilejsonResult} from '@carto/api-client';
10
- import {injectAccessToken, TilejsonPropType} from './utils';
10
+ import {TilejsonPropType, mergeLoadOptions} from './utils';
11
11
  import {DEFAULT_TILE_SIZE} from '../constants';
12
12
 
13
13
  export const renderSubLayers = props => {
@@ -47,11 +47,11 @@ export default class H3TileLayer<DataT = any, ExtraPropsT extends {} = {}> exten
47
47
  }
48
48
 
49
49
  getLoadOptions(): any {
50
- const loadOptions = super.getLoadOptions() || {};
51
50
  const tileJSON = this.props.data as TilejsonResult;
52
- injectAccessToken(loadOptions, tileJSON.accessToken);
53
- loadOptions.cartoSpatialTile = {...loadOptions.cartoSpatialTile, scheme: 'h3'};
54
- return loadOptions;
51
+ return mergeLoadOptions(super.getLoadOptions(), {
52
+ fetch: {headers: {Authorization: `Bearer ${tileJSON.accessToken}`}},
53
+ cartoSpatialTile: {scheme: 'h3'}
54
+ });
55
55
  }
56
56
 
57
57
  renderLayers(): SpatialIndexTileLayer | null {
@@ -2,6 +2,8 @@
2
2
  // SPDX-License-Identifier: MIT
3
3
  // Copyright (c) vis.gl contributors
4
4
 
5
+ /* eslint-disable @typescript-eslint/no-shadow */
6
+
5
7
  import type {ShaderModule} from '@luma.gl/shadertools';
6
8
  import {getResolution} from 'quadbin';
7
9
 
@@ -0,0 +1,304 @@
1
+ import {GeoBoundingBox} from '@deck.gl/geo-layers';
2
+ import {TypedArray} from '@loaders.gl/loader-utils';
3
+ import {BinaryPointFeature, BinaryLineFeature, BinaryPolygonFeature} from '@loaders.gl/schema';
4
+ import {copyNumericProps, createBinaryPointFeature, initializeNumericProps} from '../utils';
5
+
6
+ type Vec2 = [number, number] | TypedArray;
7
+ type TileBBox = GeoBoundingBox;
8
+ type Properties = BinaryPointFeature['properties'];
9
+ type LineInfo = {index: number; length: number};
10
+
11
+ export function createPointsFromLines(
12
+ lines: BinaryLineFeature,
13
+ uniqueIdProperty?: string
14
+ ): BinaryPointFeature | null {
15
+ const hasNumericUniqueId = uniqueIdProperty ? uniqueIdProperty in lines.numericProps : false;
16
+ const idToLineInfo = new Map<string | number | undefined, LineInfo>();
17
+
18
+ // First pass: find the longest line for each unique ID
19
+ // If we don't have a uniqueIdProperty, treat each line as unique
20
+ for (let i = 0; i < lines.pathIndices.value.length - 1; i++) {
21
+ const pathIndex = lines.pathIndices.value[i];
22
+ const featureId = lines.featureIds.value[pathIndex];
23
+ let uniqueId: string | number | undefined;
24
+
25
+ if (uniqueIdProperty === undefined) {
26
+ uniqueId = featureId;
27
+ } else if (hasNumericUniqueId) {
28
+ uniqueId = lines.numericProps[uniqueIdProperty].value[pathIndex];
29
+ } else if (lines.properties[featureId] && uniqueIdProperty in lines.properties[featureId]) {
30
+ uniqueId = lines.properties[featureId][uniqueIdProperty];
31
+ } else {
32
+ uniqueId = undefined;
33
+ }
34
+ const length = getLineLength(lines, i);
35
+ if (!idToLineInfo.has(uniqueId) || length > idToLineInfo.get(uniqueId)!.length) {
36
+ idToLineInfo.set(uniqueId, {index: i, length});
37
+ }
38
+ }
39
+
40
+ const positions: number[] = [];
41
+ const properties: Properties = [];
42
+ const featureIds: number[] = [];
43
+ const globalFeatureIds: number[] = [];
44
+ const numericProps = initializeNumericProps(idToLineInfo.size, lines.numericProps);
45
+
46
+ // Second pass: create points for the longest line of each unique ID
47
+ let pointIndex = 0;
48
+ for (const [_, {index}] of idToLineInfo) {
49
+ const midpoint = getLineMidpoint(lines, index);
50
+ positions.push(...midpoint);
51
+
52
+ const pathIndex = lines.pathIndices.value[index];
53
+ const featureId = lines.featureIds.value[pathIndex];
54
+ featureIds.push(pointIndex);
55
+ properties.push(lines.properties[featureId]);
56
+ globalFeatureIds.push(lines.globalFeatureIds.value[pathIndex]);
57
+ copyNumericProps(lines.numericProps, numericProps, pathIndex, pointIndex);
58
+ pointIndex++;
59
+ }
60
+
61
+ return createBinaryPointFeature(
62
+ positions,
63
+ featureIds,
64
+ globalFeatureIds,
65
+ numericProps,
66
+ properties
67
+ );
68
+ }
69
+
70
+ export function createPointsFromPolygons(
71
+ polygons: Required<BinaryPolygonFeature>,
72
+ tileBbox: TileBBox,
73
+ props: any
74
+ ): BinaryPointFeature {
75
+ const {west, south, east, north} = tileBbox;
76
+ const tileArea = (east - west) * (north - south);
77
+ const minPolygonArea = tileArea * 0.0001; // 0.1% threshold
78
+
79
+ const positions: number[] = [];
80
+ const properties: Properties = [];
81
+ const featureIds: number[] = [];
82
+ const globalFeatureIds: number[] = [];
83
+ const numericProps = initializeNumericProps(
84
+ polygons.polygonIndices.value.length - 1,
85
+ polygons.numericProps
86
+ );
87
+
88
+ // Process each polygon
89
+ let pointIndex = 0;
90
+ let triangleIndex = 0;
91
+ const {extruded} = props;
92
+ for (let i = 0; i < polygons.polygonIndices.value.length - 1; i++) {
93
+ const startIndex = polygons.polygonIndices.value[i];
94
+ const endIndex = polygons.polygonIndices.value[i + 1];
95
+
96
+ // Skip small polygons
97
+ if (getPolygonArea(polygons, i) < minPolygonArea) {
98
+ continue;
99
+ }
100
+
101
+ const centroid = getPolygonCentroid(polygons, i);
102
+ let maxArea = -1;
103
+ let largestTriangleCenter: [number, number] = [0, 0];
104
+ let centroidIsInside = false;
105
+
106
+ // Scan triangles until we find ones that don't belong to this polygon
107
+ while (triangleIndex < polygons.triangles.value.length) {
108
+ const i1 = polygons.triangles.value[triangleIndex];
109
+
110
+ // If we've moved past the current polygon's triangles, break
111
+ if (i1 >= endIndex) {
112
+ break;
113
+ }
114
+
115
+ // If we've already found a triangle containing the centroid, skip the rest
116
+ if (centroidIsInside) {
117
+ triangleIndex += 3;
118
+ continue;
119
+ }
120
+
121
+ const i2 = polygons.triangles.value[triangleIndex + 1];
122
+ const i3 = polygons.triangles.value[triangleIndex + 2];
123
+ const v1 = polygons.positions.value.subarray(
124
+ i1 * polygons.positions.size,
125
+ i1 * polygons.positions.size + polygons.positions.size
126
+ );
127
+ const v2 = polygons.positions.value.subarray(
128
+ i2 * polygons.positions.size,
129
+ i2 * polygons.positions.size + polygons.positions.size
130
+ );
131
+ const v3 = polygons.positions.value.subarray(
132
+ i3 * polygons.positions.size,
133
+ i3 * polygons.positions.size + polygons.positions.size
134
+ );
135
+
136
+ if (isPointInTriangle(centroid, v1, v2, v3)) {
137
+ centroidIsInside = true;
138
+ } else {
139
+ const area = getTriangleArea(v1, v2, v3);
140
+ if (area > maxArea) {
141
+ maxArea = area;
142
+ largestTriangleCenter = [(v1[0] + v2[0] + v3[0]) / 3, (v1[1] + v2[1] + v3[1]) / 3];
143
+ }
144
+ }
145
+
146
+ triangleIndex += 3;
147
+ }
148
+
149
+ const labelPoint = centroidIsInside ? centroid : largestTriangleCenter;
150
+ if (isPointInBounds(labelPoint, tileBbox)) {
151
+ positions.push(...labelPoint);
152
+ const featureId = polygons.featureIds.value[startIndex];
153
+ if (extruded) {
154
+ const elevation = props.getElevation(undefined, {
155
+ data: polygons,
156
+ index: featureId
157
+ });
158
+ positions.push(elevation * props.elevationScale);
159
+ }
160
+ properties.push(polygons.properties[featureId]);
161
+ featureIds.push(pointIndex);
162
+ globalFeatureIds.push(polygons.globalFeatureIds.value[startIndex]);
163
+ copyNumericProps(polygons.numericProps, numericProps, startIndex, pointIndex);
164
+ pointIndex++;
165
+ }
166
+ }
167
+
168
+ // Trim numeric properties arrays to actual size
169
+ if (polygons.numericProps) {
170
+ Object.keys(numericProps).forEach(prop => {
171
+ numericProps[prop].value = numericProps[prop].value.slice(0, pointIndex);
172
+ });
173
+ }
174
+
175
+ return createBinaryPointFeature(
176
+ positions,
177
+ featureIds,
178
+ globalFeatureIds,
179
+ numericProps,
180
+ properties,
181
+ extruded ? 3 : 2
182
+ );
183
+ }
184
+
185
+ // Helper functions
186
+ function getPolygonArea(polygons: Required<BinaryPolygonFeature>, index: number): number {
187
+ const {
188
+ positions: {value: positions, size},
189
+ polygonIndices: {value: indices},
190
+ triangles: {value: triangles}
191
+ } = polygons;
192
+
193
+ const startIndex = indices[index];
194
+ const endIndex = indices[index + 1];
195
+ let area = 0;
196
+ let triangleIndex = 0;
197
+
198
+ // Find first triangle of this polygon
199
+ // Note: this assumes tirnagles and polygon indices are sorted.
200
+ // This is true for the current implementation of geojsonToBinary
201
+ while (triangleIndex < triangles.length) {
202
+ const i1 = triangles[triangleIndex];
203
+ if (i1 >= startIndex) break;
204
+ triangleIndex += 3;
205
+ }
206
+
207
+ // Process triangles until we hit the next polygon
208
+ while (triangleIndex < triangles.length) {
209
+ const i1 = triangles[triangleIndex];
210
+ if (i1 >= endIndex) break;
211
+
212
+ const i2 = triangles[triangleIndex + 1];
213
+ const i3 = triangles[triangleIndex + 2];
214
+ const v1 = positions.subarray(i1 * size, i1 * size + size);
215
+ const v2 = positions.subarray(i2 * size, i2 * size + size);
216
+ const v3 = positions.subarray(i3 * size, i3 * size + size);
217
+
218
+ area += getTriangleArea(v1, v2, v3);
219
+ triangleIndex += 3;
220
+ }
221
+
222
+ return area;
223
+ }
224
+
225
+ function isPointInBounds([x, y]: [number, number], {west, east, south, north}: TileBBox): boolean {
226
+ return x >= west && x < east && y >= south && y < north;
227
+ }
228
+
229
+ function isPointInTriangle(p: Vec2, v1: Vec2, v2: Vec2, v3: Vec2): boolean {
230
+ const area = Math.abs((v2[0] - v1[0]) * (v3[1] - v1[1]) - (v3[0] - v1[0]) * (v2[1] - v1[1])) / 2;
231
+ const area1 = Math.abs((v1[0] - p[0]) * (v2[1] - p[1]) - (v2[0] - p[0]) * (v1[1] - p[1])) / 2;
232
+ const area2 = Math.abs((v2[0] - p[0]) * (v3[1] - p[1]) - (v3[0] - p[0]) * (v2[1] - p[1])) / 2;
233
+ const area3 = Math.abs((v3[0] - p[0]) * (v1[1] - p[1]) - (v1[0] - p[0]) * (v3[1] - p[1])) / 2;
234
+
235
+ // Account for floating point precision
236
+ return Math.abs(area - (area1 + area2 + area3)) < 1e-10;
237
+ }
238
+
239
+ function getTriangleArea([x1, y1]: Vec2, [x2, y2]: Vec2, [x3, y3]: Vec2): number {
240
+ return Math.abs((x1 * (y2 - y3) + x2 * (y3 - y1) + x3 * (y1 - y2)) / 2);
241
+ }
242
+
243
+ function getPolygonCentroid(polygons: BinaryPolygonFeature, index: number): [number, number] {
244
+ const {
245
+ positions: {value: positions, size}
246
+ } = polygons;
247
+ const startIndex = size * polygons.polygonIndices.value[index];
248
+ const endIndex = size * polygons.polygonIndices.value[index + 1];
249
+
250
+ let minX = Infinity;
251
+ let minY = Infinity;
252
+ let maxX = -Infinity;
253
+ let maxY = -Infinity;
254
+
255
+ for (let i = startIndex; i < endIndex; i += size) {
256
+ const [x, y] = positions.subarray(i, i + 2);
257
+ minX = Math.min(minX, x);
258
+ minY = Math.min(minY, y);
259
+ maxX = Math.max(maxX, x);
260
+ maxY = Math.max(maxY, y);
261
+ }
262
+
263
+ return [(minX + maxX) / 2, (minY + maxY) / 2];
264
+ }
265
+
266
+ function getSegmentLength(lines: BinaryLineFeature, index: number): number {
267
+ const {
268
+ positions: {value}
269
+ } = lines;
270
+ const [x1, y1, x2, y2] = value.subarray(index, index + 4);
271
+ return Math.sqrt(Math.pow(x2 - x1, 2) + Math.pow(y2 - y1, 2));
272
+ }
273
+
274
+ function getLineLength(lines: BinaryLineFeature, index: number): number {
275
+ const {
276
+ positions: {size}
277
+ } = lines;
278
+ const startIndex = size * lines.pathIndices.value[index];
279
+ const endIndex = size * lines.pathIndices.value[index + 1];
280
+ let length = 0;
281
+ for (let j = startIndex; j < endIndex; j += size) {
282
+ length += getSegmentLength(lines, j);
283
+ }
284
+ return length;
285
+ }
286
+
287
+ function getLineMidpoint(lines: BinaryLineFeature, index: number): [number, number] {
288
+ const {
289
+ positions: {value: positions},
290
+ pathIndices: {value: pathIndices}
291
+ } = lines;
292
+ const startIndex = pathIndices[index] * 2;
293
+ const endIndex = pathIndices[index + 1] * 2;
294
+ const numPoints = (endIndex - startIndex) / 2;
295
+
296
+ if (numPoints === 2) {
297
+ // For lines with only two vertices, interpolate between them
298
+ const [x1, y1, x2, y2] = positions.subarray(startIndex, startIndex + 4);
299
+ return [(x1 + x2) / 2, (y1 + y2) / 2];
300
+ }
301
+ // For lines with multiple vertices, use the middle vertex
302
+ const midPointIndex = startIndex + Math.floor(numPoints / 2) * 2;
303
+ return [positions[midPointIndex], positions[midPointIndex + 1]];
304
+ }
@@ -9,13 +9,15 @@ import {
9
9
  CompositeLayerProps,
10
10
  DefaultProps,
11
11
  Layer,
12
- LayersList
12
+ LayersList,
13
+ log
13
14
  } from '@deck.gl/core';
14
15
  import {
15
16
  TextLayer,
16
17
  TextLayerProps,
17
18
  _TextBackgroundLayer as TextBackgroundLayer
18
19
  } from '@deck.gl/layers';
20
+ import VectorTileLayer from './vector-tile-layer';
19
21
 
20
22
  const [LEFT, TOP, RIGHT, BOTTOM] = [0, 1, 2, 3];
21
23
 
@@ -97,6 +99,12 @@ type _PointLabelLayerProps<DataT> = TextLayerProps<DataT> & {
97
99
  secondarySizeScale?: number;
98
100
  };
99
101
 
102
+ /**
103
+ * PointLabelLayer is a layer that renders point labels.
104
+ * It is a composite layer that renders a primary and secondary label.
105
+ * It behaves like a TextLayer except that getTextSize is **not supported**
106
+ * and the text size for the primary label must be set with **sizeScale**.
107
+ */
100
108
  export default class PointLabelLayer<
101
109
  DataT = any,
102
110
  ExtraProps extends {} = {}
@@ -194,6 +202,15 @@ export default class PointLabelLayer<
194
202
  updateTriggers
195
203
  } = this.props;
196
204
 
205
+ if (sizeScale < 2) {
206
+ const propName = (this.parent as VectorTileLayer)?.props?.textSizeScale
207
+ ? 'textSizeScale'
208
+ : 'sizeScale';
209
+ log.warn(
210
+ `${propName} has small value (${sizeScale}). Note getTextSize is not supported on PointLabelLayer`
211
+ )();
212
+ }
213
+
197
214
  return new EnhancedTextLayer(
198
215
  this.getSubLayerProps({
199
216
  id,
@@ -2,13 +2,13 @@
2
2
  // SPDX-License-Identifier: MIT
3
3
  // Copyright (c) vis.gl contributors
4
4
 
5
- import {CompositeLayer, CompositeLayerProps, Layer, LayersList, DefaultProps} from '@deck.gl/core';
5
+ import {CompositeLayer, CompositeLayerProps, DefaultProps} from '@deck.gl/core';
6
6
  import QuadbinLayer, {QuadbinLayerProps} from './quadbin-layer';
7
7
  import QuadbinTileset2D from './quadbin-tileset-2d';
8
8
  import SpatialIndexTileLayer, {SpatialIndexTileLayerProps} from './spatial-index-tile-layer';
9
9
  import {hexToBigInt} from 'quadbin';
10
10
  import type {TilejsonResult} from '@carto/api-client';
11
- import {injectAccessToken, TilejsonPropType} from './utils';
11
+ import {TilejsonPropType, mergeLoadOptions} from './utils';
12
12
  import {DEFAULT_TILE_SIZE} from '../constants';
13
13
 
14
14
  export const renderSubLayers = props => {
@@ -43,11 +43,11 @@ export default class QuadbinTileLayer<
43
43
  static defaultProps = defaultProps;
44
44
 
45
45
  getLoadOptions(): any {
46
- const loadOptions = super.getLoadOptions() || {};
47
46
  const tileJSON = this.props.data as TilejsonResult;
48
- injectAccessToken(loadOptions, tileJSON.accessToken);
49
- loadOptions.cartoSpatialTile = {...loadOptions.cartoSpatialTile, scheme: 'quadbin'};
50
- return loadOptions;
47
+ return mergeLoadOptions(super.getLoadOptions(), {
48
+ fetch: {headers: {Authorization: `Bearer ${tileJSON.accessToken}`}},
49
+ cartoSpatialTile: {scheme: 'quadbin'}
50
+ });
51
51
  }
52
52
 
53
53
  renderLayers(): SpatialIndexTileLayer | null {
@@ -13,7 +13,7 @@ import {
13
13
  import RasterLayer, {RasterLayerProps} from './raster-layer';
14
14
  import QuadbinTileset2D from './quadbin-tileset-2d';
15
15
  import type {TilejsonResult} from '@carto/api-client';
16
- import {injectAccessToken, TilejsonPropType} from './utils';
16
+ import {TilejsonPropType, mergeLoadOptions} from './utils';
17
17
  import {DEFAULT_TILE_SIZE} from '../constants';
18
18
  import {TileLayer, TileLayerProps} from '@deck.gl/geo-layers';
19
19
  import {copy, PostProcessModifier} from './post-process-utils';
@@ -62,10 +62,10 @@ export default class RasterTileLayer<
62
62
  static defaultProps = defaultProps;
63
63
 
64
64
  getLoadOptions(): any {
65
- const loadOptions = super.getLoadOptions() || {};
66
65
  const tileJSON = this.props.data as TilejsonResult;
67
- injectAccessToken(loadOptions, tileJSON.accessToken);
68
- return loadOptions;
66
+ return mergeLoadOptions(super.getLoadOptions(), {
67
+ fetch: {headers: {Authorization: `Bearer ${tileJSON.accessToken}`}}
68
+ });
69
69
  }
70
70
 
71
71
  renderLayers(): Layer | null | LayersList {
@@ -3,6 +3,7 @@
3
3
  // Copyright (c) vis.gl contributors
4
4
 
5
5
  import {readPackedTypedArray} from './fast-pbf';
6
+ import {TypedArray} from '@loaders.gl/loader-utils';
6
7
 
7
8
  // KeyValueObject ========================================
8
9
  interface KeyValueObject {
@@ -88,7 +89,8 @@ class FieldsReader {
88
89
  // NumericProp ========================================
89
90
 
90
91
  export interface NumericProp {
91
- value: number[];
92
+ value: TypedArray;
93
+ size: number;
92
94
  }
93
95
 
94
96
  class NumericPropReader {
@@ -3,17 +3,18 @@
3
3
  // Copyright (c) vis.gl contributors
4
4
 
5
5
  import {bigIntToHex} from 'quadbin';
6
+ import {BinaryPointFeature} from '@loaders.gl/schema';
6
7
 
7
8
  export type IndexScheme = 'h3' | 'quadbin';
8
9
  type TypedArray = Float32Array | Float64Array;
9
10
 
10
11
  export type Indices = {value: BigUint64Array};
11
- export type NumericProps = Record<string, {value: number[] | TypedArray}>;
12
- export type Properties = Record<string, string | number | boolean | null>;
12
+ export type NumericProps = BinaryPointFeature['numericProps'];
13
+ export type Properties = BinaryPointFeature['properties'];
13
14
  export type Cells = {
14
15
  indices: Indices;
15
16
  numericProps: NumericProps;
16
- properties: Properties[];
17
+ properties: Properties;
17
18
  };
18
19
  export type SpatialBinary = {scheme?: IndexScheme; cells: Cells};
19
20
  export type SpatialJson = {
@@ -9,17 +9,41 @@ import {_deepEqual as deepEqual} from '@deck.gl/core';
9
9
  import type {TilejsonResult} from '@carto/api-client';
10
10
 
11
11
  /**
12
- * Adds access token to Authorization header in loadOptions
12
+ * Merges load options with additional options, creating a new object without mutating the input.
13
+ * Handles nested objects through recursive deep merge with protection against circular references.
13
14
  */
14
- export function injectAccessToken(loadOptions: any, accessToken: string): void {
15
- if (!loadOptions?.fetch?.headers?.Authorization) {
16
- loadOptions.fetch = {
17
- ...loadOptions.fetch,
18
- headers: {...loadOptions.fetch?.headers, Authorization: `Bearer ${accessToken}`}
19
- };
15
+ export function mergeLoadOptions(loadOptions: any, additionalOptions: any, depth = 0): any {
16
+ if (!loadOptions) {
17
+ return additionalOptions;
20
18
  }
19
+ if (!additionalOptions) {
20
+ return loadOptions;
21
+ }
22
+
23
+ // Safety check against deep recursion
24
+ if (depth > 10) {
25
+ return additionalOptions;
26
+ }
27
+
28
+ const result = {...loadOptions};
29
+
30
+ for (const key in additionalOptions) {
31
+ const value = additionalOptions[key];
32
+ // Skip circular references
33
+ if (value === loadOptions || value === additionalOptions) {
34
+ continue;
35
+ }
36
+ if (typeof value === 'object' && value !== null) {
37
+ result[key] = mergeLoadOptions(loadOptions[key], value, depth + 1);
38
+ } else {
39
+ result[key] = value;
40
+ }
41
+ }
42
+
43
+ return result;
21
44
  }
22
45
 
46
+ // eslint-disable-next-line max-statements
23
47
  export function mergeBoundaryData(geometry: VectorTile, properties: PropertiesTile): VectorTile {
24
48
  const mapping = {};
25
49
  for (const {geoid, ...rest} of properties.properties) {