@deck.gl/carto 9.1.8 → 9.1.10

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 (81) 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 +475 -141
  11. package/dist/index.cjs +429 -142
  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 +5 -1
  24. package/dist/layers/heatmap-tile-layer.js.map +1 -1
  25. package/dist/layers/label-utils.d.ts +7 -0
  26. package/dist/layers/label-utils.d.ts.map +1 -0
  27. package/dist/layers/label-utils.js +218 -0
  28. package/dist/layers/label-utils.js.map +1 -0
  29. package/dist/layers/point-label-layer.d.ts +6 -0
  30. package/dist/layers/point-label-layer.d.ts.map +1 -1
  31. package/dist/layers/point-label-layer.js +13 -1
  32. package/dist/layers/point-label-layer.js.map +1 -1
  33. package/dist/layers/quadbin-tile-layer.d.ts.map +1 -1
  34. package/dist/layers/quadbin-tile-layer.js +5 -5
  35. package/dist/layers/quadbin-tile-layer.js.map +1 -1
  36. package/dist/layers/raster-tile-layer.d.ts.map +1 -1
  37. package/dist/layers/raster-tile-layer.js +7 -6
  38. package/dist/layers/raster-tile-layer.js.map +1 -1
  39. package/dist/layers/schema/carto-properties-tile-loader.js +1 -1
  40. package/dist/layers/schema/carto-properties-tile-loader.js.map +1 -1
  41. package/dist/layers/schema/carto-raster-tile-loader.js +1 -1
  42. package/dist/layers/schema/carto-raster-tile-loader.js.map +1 -1
  43. package/dist/layers/schema/carto-spatial-tile-loader.js +1 -1
  44. package/dist/layers/schema/carto-spatial-tile-loader.js.map +1 -1
  45. package/dist/layers/schema/carto-tile.d.ts +3 -1
  46. package/dist/layers/schema/carto-tile.d.ts.map +1 -1
  47. package/dist/layers/schema/carto-tile.js.map +1 -1
  48. package/dist/layers/schema/carto-vector-tile-loader.js +1 -1
  49. package/dist/layers/schema/carto-vector-tile-loader.js.map +1 -1
  50. package/dist/layers/schema/spatialjson-utils.d.ts +4 -7
  51. package/dist/layers/schema/spatialjson-utils.d.ts.map +1 -1
  52. package/dist/layers/schema/spatialjson-utils.js.map +1 -1
  53. package/dist/layers/utils.d.ts +3 -2
  54. package/dist/layers/utils.d.ts.map +1 -1
  55. package/dist/layers/utils.js +28 -7
  56. package/dist/layers/utils.js.map +1 -1
  57. package/dist/layers/vector-tile-layer.d.ts +12 -4
  58. package/dist/layers/vector-tile-layer.d.ts.map +1 -1
  59. package/dist/layers/vector-tile-layer.js +76 -35
  60. package/dist/layers/vector-tile-layer.js.map +1 -1
  61. package/dist/utils.d.ts +6 -1
  62. package/dist/utils.d.ts.map +1 -1
  63. package/dist/utils.js +53 -0
  64. package/dist/utils.js.map +1 -1
  65. package/dist.min.js +12 -12
  66. package/package.json +2 -2
  67. package/src/api/fetch-map.ts +2 -0
  68. package/src/api/parse-map.ts +2 -0
  69. package/src/layers/cluster-tile-layer.ts +7 -5
  70. package/src/layers/cluster-utils.ts +3 -29
  71. package/src/layers/h3-tile-layer.ts +6 -6
  72. package/src/layers/heatmap-tile-layer.ts +9 -1
  73. package/src/layers/label-utils.ts +304 -0
  74. package/src/layers/point-label-layer.ts +18 -1
  75. package/src/layers/quadbin-tile-layer.ts +6 -6
  76. package/src/layers/raster-tile-layer.ts +7 -6
  77. package/src/layers/schema/carto-tile.ts +3 -1
  78. package/src/layers/schema/spatialjson-utils.ts +4 -3
  79. package/src/layers/utils.ts +31 -7
  80. package/src/layers/vector-tile-layer.ts +100 -42
  81. package/src/utils.ts +79 -0
@@ -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) {
@@ -3,11 +3,12 @@
3
3
  // Copyright (c) vis.gl contributors
4
4
 
5
5
  import {registerLoaders} from '@loaders.gl/core';
6
+ import {BinaryFeatureCollection, BinaryPointFeature} from '@loaders.gl/schema';
6
7
  import CartoPropertiesTileLoader from './schema/carto-properties-tile-loader';
7
8
  import CartoVectorTileLoader from './schema/carto-vector-tile-loader';
8
9
  registerLoaders([CartoPropertiesTileLoader, CartoVectorTileLoader]);
9
10
 
10
- import {DefaultProps} from '@deck.gl/core';
11
+ import {DefaultProps, Layer, LayersList} from '@deck.gl/core';
11
12
  import {ClipExtension, CollisionFilterExtension} from '@deck.gl/extensions';
12
13
  import {
13
14
  MVTLayer,
@@ -15,17 +16,21 @@ import {
15
16
  TileLayer,
16
17
  _getURLFromTemplate,
17
18
  _Tile2DHeader,
18
- _TileLoadProps as TileLoadProps
19
+ _TileLoadProps as TileLoadProps,
20
+ GeoBoundingBox
19
21
  } from '@deck.gl/geo-layers';
20
22
  import {GeoJsonLayer} from '@deck.gl/layers';
21
23
 
22
24
  import type {TilejsonResult} from '@carto/api-client';
23
- import {TilejsonPropType, injectAccessToken, mergeBoundaryData} from './utils';
25
+ import {TilejsonPropType, mergeLoadOptions, mergeBoundaryData} from './utils';
24
26
  import {DEFAULT_TILE_SIZE} from '../constants';
27
+ import {createPointsFromLines, createPointsFromPolygons} from './label-utils';
28
+ import {createEmptyBinary} from '../utils';
25
29
  import PointLabelLayer from './point-label-layer';
26
30
 
27
31
  const defaultProps: DefaultProps<VectorTileLayerProps> = {
28
32
  ...MVTLayer.defaultProps,
33
+ autoLabels: false,
29
34
  data: TilejsonPropType,
30
35
  dataComparator: TilejsonPropType.equal,
31
36
  tileSize: DEFAULT_TILE_SIZE
@@ -38,6 +43,12 @@ export type VectorTileLayerProps<FeaturePropertiesT = unknown> = _VectorTileLaye
38
43
  /** Properties added by VectorTileLayer. */
39
44
  type _VectorTileLayerProps = {
40
45
  data: null | TilejsonResult | Promise<TilejsonResult>;
46
+
47
+ /**
48
+ * If true, create labels for lines and polygons.
49
+ * Specify uniqueIdProperty to only create a single label for each unique feature.
50
+ */
51
+ autoLabels: boolean | {uniqueIdProperty: string};
41
52
  };
42
53
 
43
54
  // @ts-ignore
@@ -75,11 +86,11 @@ export default class VectorTileLayer<
75
86
  }
76
87
 
77
88
  getLoadOptions(): any {
78
- const loadOptions = super.getLoadOptions() || {};
79
89
  const tileJSON = this.props.data as TilejsonResult;
80
- injectAccessToken(loadOptions, tileJSON.accessToken);
81
- loadOptions.gis = {format: 'binary'}; // Use binary for MVT loading
82
- return loadOptions;
90
+ return mergeLoadOptions(super.getLoadOptions(), {
91
+ fetch: {headers: {Authorization: `Bearer ${tileJSON.accessToken}`}},
92
+ gis: {format: 'binary'} // Use binary for MVT loading
93
+ });
83
94
  }
84
95
 
85
96
  /* eslint-disable camelcase */
@@ -121,38 +132,20 @@ export default class VectorTileLayer<
121
132
  /* eslint-enable camelcase */
122
133
 
123
134
  renderSubLayers(
124
- props: TileLayer['props'] & {
135
+ props: VectorTileLayer['props'] & {
125
136
  id: string;
126
137
  data: any;
127
138
  _offset: number;
128
139
  tile: _Tile2DHeader;
129
140
  }
130
- ): GeoJsonLayer | null {
141
+ ): GeoJsonLayer | GeoJsonLayer[] | null {
131
142
  if (props.data === null) {
132
143
  return null;
133
144
  }
134
145
 
135
- if (this.state.mvt) {
136
- return super.renderSubLayers(props) as GeoJsonLayer;
137
- }
138
-
139
- const tileBbox = props.tile.bbox as any;
140
- const {west, south, east, north} = tileBbox;
146
+ const tileBbox = props.tile.bbox as GeoBoundingBox;
141
147
 
142
- const extensions = [new ClipExtension(), ...(props.extensions || [])];
143
- const clipProps = {
144
- clipBounds: [west, south, east, north]
145
- };
146
-
147
- const applyClipExtensionToSublayerProps = (subLayerId: string) => {
148
- return {
149
- [subLayerId]: {
150
- ...clipProps,
151
- ...props?._subLayerProps?.[subLayerId],
152
- extensions: [...extensions, ...(props?._subLayerProps?.[subLayerId]?.extensions || [])]
153
- }
154
- };
155
- };
148
+ const subLayers: GeoJsonLayer[] = [];
156
149
 
157
150
  const defaultToPointLabelLayer = {
158
151
  'points-text': {
@@ -166,21 +159,86 @@ export default class VectorTileLayer<
166
159
  }
167
160
  };
168
161
 
169
- const subLayerProps = {
170
- ...props,
171
- autoHighlight: false,
172
- // Do not perform clipping on points (#9059)
173
- _subLayerProps: {
174
- ...props._subLayerProps,
175
- ...defaultToPointLabelLayer,
176
- ...applyClipExtensionToSublayerProps('polygons-fill'),
177
- ...applyClipExtensionToSublayerProps('polygons-stroke'),
178
- ...applyClipExtensionToSublayerProps('linestrings')
162
+ if (this.state.mvt) {
163
+ subLayers.push(super.renderSubLayers(props) as GeoJsonLayer);
164
+ } else {
165
+ const {west, south, east, north} = tileBbox;
166
+
167
+ const extensions = [new ClipExtension(), ...(props.extensions || [])];
168
+ const clipProps = {
169
+ clipBounds: [west, south, east, north]
170
+ };
171
+
172
+ const applyClipExtensionToSublayerProps = (subLayerId: string) => {
173
+ return {
174
+ [subLayerId]: {
175
+ ...clipProps,
176
+ ...props?._subLayerProps?.[subLayerId],
177
+ extensions: [...extensions, ...(props?._subLayerProps?.[subLayerId]?.extensions || [])]
178
+ }
179
+ };
180
+ };
181
+
182
+ const subLayerProps = {
183
+ ...props,
184
+ data: {...props.data, tileBbox},
185
+ autoHighlight: false,
186
+ // Do not perform clipping on points (#9059)
187
+ _subLayerProps: {
188
+ ...props._subLayerProps,
189
+ ...defaultToPointLabelLayer,
190
+ ...applyClipExtensionToSublayerProps('polygons-fill'),
191
+ ...applyClipExtensionToSublayerProps('polygons-stroke'),
192
+ ...applyClipExtensionToSublayerProps('linestrings')
193
+ }
194
+ };
195
+
196
+ subLayers.push(new GeoJsonLayer(subLayerProps));
197
+ }
198
+
199
+ // Add labels
200
+ if (subLayers[0] && props.autoLabels) {
201
+ const labelData = createEmptyBinary();
202
+ if (props.data.lines && props.data.lines.positions.value.length > 0) {
203
+ labelData.points = createPointsFromLines(
204
+ props.data.lines,
205
+ typeof props.autoLabels === 'object' ? props.autoLabels.uniqueIdProperty : undefined
206
+ ) as BinaryPointFeature;
179
207
  }
180
- };
208
+ if (props.data.polygons && props.data.polygons.positions.value.length > 0) {
209
+ labelData.points = createPointsFromPolygons(props.data.polygons, tileBbox, props);
210
+ }
211
+
212
+ subLayers.push(
213
+ subLayers[0].clone({
214
+ id: `${props.id}-labels`,
215
+ data: labelData,
216
+ pickable: false,
217
+ autoHighlight: false
218
+ })
219
+ );
220
+ }
221
+ return subLayers;
222
+ }
181
223
 
182
- const subLayer = new GeoJsonLayer(subLayerProps);
183
- return subLayer;
224
+ renderLayers(): Layer | null | LayersList {
225
+ const layers = super.renderLayers() as LayersList;
226
+ if (!this.props.autoLabels) {
227
+ return layers;
228
+ }
229
+
230
+ // Sort layers so that label layers are rendered after the main layer
231
+ const validLayers = layers.flat().filter(Boolean) as Layer[];
232
+ validLayers.sort((a: Layer, b: Layer) => {
233
+ const aHasLabel = a.id.includes('labels');
234
+ const bHasLabel = b.id.includes('labels');
235
+ if (aHasLabel && !bHasLabel) return 1;
236
+ if (!aHasLabel && bHasLabel) return -1;
237
+ return 0;
238
+ });
239
+ return validLayers.map(l =>
240
+ l.id.includes('labels') ? l.clone({highlightedObjectIndex: -1}) : l
241
+ );
184
242
  }
185
243
 
186
244
  protected override _isWGS84(): boolean {
package/src/utils.ts CHANGED
@@ -2,6 +2,12 @@
2
2
  // SPDX-License-Identifier: MIT
3
3
  // Copyright (c) vis.gl contributors
4
4
 
5
+ import {
6
+ BinaryAttribute,
7
+ BinaryFeature,
8
+ BinaryFeatureCollection,
9
+ BinaryPointFeature
10
+ } from '@loaders.gl/schema';
5
11
  import {log} from '@deck.gl/core';
6
12
  import type {Properties, NumericProps} from './layers/schema/spatialjson-utils';
7
13
 
@@ -76,3 +82,76 @@ export const isObject: (x: unknown) => boolean = x => x !== null && typeof x ===
76
82
 
77
83
  export const isPureObject: (x: any) => boolean = x =>
78
84
  isObject(x) && x.constructor === {}.constructor;
85
+
86
+ // Helpers for binary data
87
+ const EMPTY_UINT16ARRAY = new Uint16Array();
88
+ const EMPTY_BINARY_PROPS: Omit<BinaryPointFeature, 'type'> = {
89
+ positions: {value: new Float32Array(), size: 2},
90
+ properties: [],
91
+ numericProps: {},
92
+ featureIds: {value: EMPTY_UINT16ARRAY, size: 1},
93
+ globalFeatureIds: {value: EMPTY_UINT16ARRAY, size: 1}
94
+ };
95
+
96
+ export function createEmptyBinary(): Required<BinaryFeatureCollection> {
97
+ return {
98
+ shape: 'binary-feature-collection',
99
+ points: {
100
+ type: 'Point',
101
+ ...EMPTY_BINARY_PROPS
102
+ },
103
+ lines: {
104
+ type: 'LineString',
105
+ pathIndices: {value: EMPTY_UINT16ARRAY, size: 1},
106
+ ...EMPTY_BINARY_PROPS
107
+ },
108
+ polygons: {
109
+ type: 'Polygon',
110
+ polygonIndices: {value: EMPTY_UINT16ARRAY, size: 1},
111
+ primitivePolygonIndices: {value: EMPTY_UINT16ARRAY, size: 1},
112
+ ...EMPTY_BINARY_PROPS
113
+ }
114
+ };
115
+ }
116
+
117
+ export function createBinaryPointFeature(
118
+ positions: number[] | Float32Array | Float64Array,
119
+ featureIds: number[] | Uint16Array | Uint32Array,
120
+ globalFeatureIds: number[] | Uint16Array | Uint32Array,
121
+ numericProps: NumericProps,
122
+ properties: Properties,
123
+ size: 2 | 3 = 2
124
+ ): BinaryPointFeature {
125
+ return {
126
+ type: 'Point',
127
+ positions: {value: new Float32Array(positions), size},
128
+ featureIds: {value: new Uint16Array(featureIds), size: 1},
129
+ globalFeatureIds: {value: new Uint32Array(globalFeatureIds), size: 1},
130
+ numericProps,
131
+ properties
132
+ };
133
+ }
134
+
135
+ export function initializeNumericProps(
136
+ numPoints: number,
137
+ sourceProps?: NumericProps
138
+ ): NumericProps {
139
+ const numericProps: NumericProps = {};
140
+ if (sourceProps) {
141
+ Object.keys(sourceProps).forEach(prop => {
142
+ numericProps[prop] = {value: new Float32Array(numPoints), size: 1};
143
+ });
144
+ }
145
+ return numericProps;
146
+ }
147
+
148
+ export function copyNumericProps(
149
+ sourceProps: NumericProps,
150
+ targetProps: NumericProps,
151
+ sourceIndex: number,
152
+ targetIndex: number
153
+ ): void {
154
+ Object.keys(sourceProps).forEach(prop => {
155
+ targetProps[prop].value[targetIndex] = sourceProps[prop].value[sourceIndex];
156
+ });
157
+ }