@deck.gl/carto 8.7.8 → 8.8.0-alpha.3

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 (53) hide show
  1. package/dist/dist.dev.js +33261 -40106
  2. package/dist/es5/api/layer-map.js +60 -21
  3. package/dist/es5/api/layer-map.js.map +1 -1
  4. package/dist/es5/api/maps-api-common.js +1 -0
  5. package/dist/es5/api/maps-api-common.js.map +1 -1
  6. package/dist/es5/api/maps-v3-client.js +56 -31
  7. package/dist/es5/api/maps-v3-client.js.map +1 -1
  8. package/dist/es5/api/parseMap.js +14 -3
  9. package/dist/es5/api/parseMap.js.map +1 -1
  10. package/dist/es5/index.js +16 -0
  11. package/dist/es5/index.js.map +1 -1
  12. package/dist/es5/layers/carto-layer.js +35 -12
  13. package/dist/es5/layers/carto-layer.js.map +1 -1
  14. package/dist/es5/layers/h3-tile-layer.js +96 -0
  15. package/dist/es5/layers/h3-tile-layer.js.map +1 -0
  16. package/dist/es5/layers/h3-tileset-2d.js +165 -0
  17. package/dist/es5/layers/h3-tileset-2d.js.map +1 -0
  18. package/dist/es5/layers/quadkey-tile-layer.js +97 -0
  19. package/dist/es5/layers/quadkey-tile-layer.js.map +1 -0
  20. package/dist/es5/layers/quadkey-tileset-2d.js +89 -0
  21. package/dist/es5/layers/quadkey-tileset-2d.js.map +1 -0
  22. package/dist/esm/api/layer-map.js +39 -10
  23. package/dist/esm/api/layer-map.js.map +1 -1
  24. package/dist/esm/api/maps-api-common.js +1 -0
  25. package/dist/esm/api/maps-api-common.js.map +1 -1
  26. package/dist/esm/api/maps-v3-client.js +58 -30
  27. package/dist/esm/api/maps-v3-client.js.map +1 -1
  28. package/dist/esm/api/parseMap.js +15 -4
  29. package/dist/esm/api/parseMap.js.map +1 -1
  30. package/dist/esm/index.js +2 -0
  31. package/dist/esm/index.js.map +1 -1
  32. package/dist/esm/layers/carto-layer.js +34 -7
  33. package/dist/esm/layers/carto-layer.js.map +1 -1
  34. package/dist/esm/layers/h3-tile-layer.js +61 -0
  35. package/dist/esm/layers/h3-tile-layer.js.map +1 -0
  36. package/dist/esm/layers/h3-tileset-2d.js +103 -0
  37. package/dist/esm/layers/h3-tileset-2d.js.map +1 -0
  38. package/dist/esm/layers/quadkey-tile-layer.js +61 -0
  39. package/dist/esm/layers/quadkey-tile-layer.js.map +1 -0
  40. package/dist/esm/layers/quadkey-tileset-2d.js +48 -0
  41. package/dist/esm/layers/quadkey-tileset-2d.js.map +1 -0
  42. package/dist.min.js +14 -14
  43. package/package.json +9 -8
  44. package/src/api/layer-map.ts +46 -10
  45. package/src/api/maps-api-common.ts +4 -3
  46. package/src/api/maps-v3-client.ts +78 -29
  47. package/src/api/parseMap.ts +20 -5
  48. package/src/index.ts +4 -0
  49. package/src/layers/{carto-layer.js → carto-layer.ts} +155 -22
  50. package/src/layers/h3-tile-layer.js +44 -0
  51. package/src/layers/h3-tileset-2d.js +82 -0
  52. package/src/layers/quadkey-tile-layer.js +42 -0
  53. package/src/layers/quadkey-tileset-2d.js +36 -0
@@ -1,16 +1,30 @@
1
- import {CompositeLayer, log} from '@deck.gl/core';
1
+ import {CompositeLayer, Layer, log} from '@deck.gl/core';
2
2
  import CartoTileLayer from './carto-tile-layer';
3
+ import H3TileLayer from './h3-tile-layer';
4
+ import QuadkeyTileLayer from './quadkey-tile-layer';
3
5
  import {MVTLayer} from '@deck.gl/geo-layers';
4
6
  import {GeoJsonLayer} from '@deck.gl/layers';
5
7
  import {fetchLayerData, getDataV2, API_VERSIONS} from '../api';
6
8
  import {
7
9
  COLUMNS_SUPPORT,
10
+ Format,
8
11
  FORMATS,
9
12
  GEO_COLUMN_SUPPORT,
13
+ MapType,
10
14
  MAP_TYPES,
15
+ TileFormat,
11
16
  TILE_FORMATS
12
17
  } from '../api/maps-api-common';
13
- import {getDefaultCredentials} from '../config';
18
+ import {
19
+ ClassicCredentials,
20
+ CloudNativeCredentials,
21
+ Credentials,
22
+ getDefaultCredentials
23
+ } from '../config';
24
+ import {CompositeLayerProps, LayerProps} from 'modules/core/src/types/layer-props';
25
+ import {ChangeFlags} from 'modules/core/src/lib/layer-state';
26
+ import {FetchLayerDataResult} from '../api/maps-v3-client';
27
+ import {assert} from '../utils';
14
28
 
15
29
  const defaultProps = {
16
30
  // (String, required): data resource to load. table name, sql query or tileset name.
@@ -46,19 +60,105 @@ const defaultProps = {
46
60
  columns: {type: 'array', value: null}
47
61
  };
48
62
 
49
- export default class CartoLayer extends CompositeLayer {
50
- initializeState() {
63
+ export interface CartoLayerProps<DataT = any> extends CompositeLayerProps<DataT> {
64
+ /**
65
+ * Either a SQL query or a name of dataset/tileset.
66
+ */
67
+ data: string;
68
+
69
+ /**
70
+ * Data type.
71
+ *
72
+ * Possible values are:
73
+ * * `MAP_TYPES.QUERY`, if data is a SQL query.
74
+ * * `MAP_TYPES.TILESET`, if data is a tileset name.
75
+ * * `MAP_TYPES.TABLE`, if data is a dataset name. Only supported with API v3.
76
+ */
77
+ type: MapType;
78
+
79
+ /**
80
+ * Name of the connection registered in the CARTO workspace.
81
+ *
82
+ * Required when apiVersion is `API_VERSIONS.V3`.
83
+ */
84
+ connection?: string;
85
+
86
+ /**
87
+ * Use to override the default data format.
88
+ *
89
+ * Only supported when apiVersion is `API_VERSIONS.V3`.
90
+ *
91
+ * Possible values are: `FORMATS.GEOJSON`, `FORMATS.JSON` and `FORMATS.TILEJSON`.
92
+ */
93
+ format?: Format;
94
+
95
+ /**
96
+ * Use to override the default tile data format.
97
+ *
98
+ * Only supported when apiVersion is `API_VERSIONS.V3` and format is `FORMATS.TILEJSON`.
99
+ *
100
+ * Possible values are: `TILE_FORMATS.BINARY`, `TILE_FORMATS.GEOJSON` and `TILE_FORMATS.MVT`.
101
+ */
102
+ formatTiles?: TileFormat;
103
+
104
+ /**
105
+ * Name of the geo_column in the CARTO platform.
106
+ *
107
+ * Use this override the default column (`'geom'`), from which the geometry information should be fetched.
108
+ *
109
+ * Only supported when apiVersion is `API_VERSIONS.V3` and type is `MAP_TYPES.TABLE`.
110
+ */
111
+ geoColumn?: string;
112
+
113
+ /**
114
+ * Names of columns to fetch.
115
+ *
116
+ * By default, all columns are fetched
117
+ *
118
+ * Only supported when apiVersion is `API_VERSIONS.V3` and type is `MAP_TYPES.TABLE`.
119
+ */
120
+ columns?: string[];
121
+
122
+ /**
123
+ * A string pointing to a unique attribute at the result of the query.
124
+ *
125
+ * A unique attribute is needed for highlighting with vector tiles when a feature is split across two or more tiles.
126
+ */
127
+ uniqueIdProperty?: string;
128
+
129
+ /**
130
+ * Optional. Overrides the configuration to connect with CARTO.
131
+ *
132
+ * @see Credentials
133
+ */
134
+ credentials?: Credentials;
135
+
136
+ /**
137
+ * Called when the request to the CARTO Maps API failed.
138
+ *
139
+ * By default the Error is thrown.
140
+ */
141
+ onDataError?: (err: unknown) => void;
142
+
143
+ clientId?: string;
144
+ }
145
+
146
+ export default class CartoLayer<DataT = any> extends CompositeLayer<CartoLayerProps<DataT>> {
147
+ static layerName = 'CartoLayer';
148
+ static defaultProps = defaultProps as any;
149
+
150
+ initializeState(): void {
51
151
  this.state = {
52
152
  data: null,
53
153
  apiVersion: null
54
154
  };
55
155
  }
56
156
 
57
- get isLoaded() {
157
+ get isLoaded(): boolean {
58
158
  return this.getSubLayers().length > 0 && super.isLoaded;
59
159
  }
60
160
 
61
- _checkProps(props) {
161
+ _checkProps(props: CartoLayerProps): void {
62
162
  const {type, credentials, connection, geoColumn, columns} = props;
63
163
  const localCreds = {...getDefaultCredentials(), ...credentials};
64
164
  const {apiVersion} = localCreds;
@@ -97,7 +197,16 @@ export default class CartoLayer extends CompositeLayer {
97
197
  }
98
198
  }
99
199
 
100
- updateState({props, oldProps, changeFlags}) {
200
+ updateState({
201
+ props,
202
+ oldProps,
203
+ changeFlags
204
+ }: {
205
+ props: CartoLayerProps;
206
+ oldProps: CartoLayerProps;
207
+ context: any;
208
+ changeFlags: ChangeFlags;
209
+ }): void {
101
210
  this._checkProps(props);
102
211
  const shouldUpdateData =
103
212
  changeFlags.dataChanged ||
@@ -111,40 +220,57 @@ export default class CartoLayer extends CompositeLayer {
111
220
 
112
221
  if (shouldUpdateData) {
113
222
  this.setState({data: null, apiVersion: null});
223
+ // eslint-disable-next-line @typescript-eslint/no-floating-promises
114
224
  this._updateData();
115
225
  }
116
226
  }
117
227
 
118
- async _updateData() {
228
+ async _updateData(): Promise<void> {
119
229
  try {
120
- const {type, data: source, clientId, credentials, ...rest} = this.props;
230
+ const {type, data: source, clientId, credentials, connection, ...rest} = this.props;
121
231
  const localConfig = {...getDefaultCredentials(), ...credentials};
122
232
  const {apiVersion} = localConfig;
123
233
 
124
- let result;
234
+ let result: FetchLayerDataResult;
125
235
  if (apiVersion === API_VERSIONS.V1 || apiVersion === API_VERSIONS.V2) {
126
- result = {data: await getDataV2({type, source, credentials})};
236
+ result = {
237
+ data: await getDataV2({type, source, credentials: credentials as ClassicCredentials})
238
+ };
127
239
  } else {
128
- result = await fetchLayerData({type, source, clientId, credentials, ...rest});
240
+ result = await fetchLayerData({
241
+ type,
242
+ source,
243
+ clientId,
244
+ credentials: credentials as CloudNativeCredentials,
245
+ connection,
246
+ ...rest
247
+ });
129
248
  }
130
249
 
131
250
  this.setState({...result, apiVersion});
132
- this.props.onDataLoad(result.data);
251
+
252
+ this.props.onDataLoad?.(result.data, {
253
+ propName: 'data',
254
+ layer: this
255
+ });
133
256
  } catch (err) {
134
257
  if (this.props.onDataError) {
135
- this.props.onDataError(err);
258
+ this.props.onDataError(err as Error);
136
259
  } else {
137
260
  throw err;
138
261
  }
139
262
  }
140
263
  }
141
264
 
142
- _getSubLayerAndProps() {
265
+ _getSubLayerAndProps(): [any, LayerProps] {
266
+ assert(this.state);
267
+
143
268
  const {data, format, apiVersion} = this.state;
144
269
 
145
270
  const {uniqueIdProperty} = defaultProps;
146
- const props = {uniqueIdProperty, ...this.props};
147
- delete props.data;
271
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
272
+ const {data: _notUsed, ...propsNoData} = this.props;
273
+ const props = {uniqueIdProperty, ...propsNoData};
148
274
 
149
275
  if (apiVersion === API_VERSIONS.V1 || apiVersion === API_VERSIONS.V2) {
150
276
  return [MVTLayer, props];
@@ -155,8 +281,16 @@ export default class CartoLayer extends CompositeLayer {
155
281
  const tileUrl = new URL(data.tiles[0]);
156
282
 
157
283
  props.formatTiles =
158
- props.formatTiles || tileUrl.searchParams.get('formatTiles') || TILE_FORMATS.MVT;
284
+ props.formatTiles ||
285
+ (tileUrl.searchParams.get('formatTiles') as TileFormat) ||
286
+ TILE_FORMATS.MVT;
159
287
 
288
+ if (data.scheme === 'h3') {
289
+ return [H3TileLayer, props];
290
+ }
291
+ if (data.scheme === 'quadkey') {
292
+ return [QuadkeyTileLayer, props];
293
+ }
160
294
  return props.formatTiles === TILE_FORMATS.MVT ? [MVTLayer, props] : [CartoTileLayer, props];
161
295
  }
162
296
 
@@ -164,7 +298,9 @@ export default class CartoLayer extends CompositeLayer {
164
298
  return [GeoJsonLayer, props];
165
299
  }
166
300
 
167
- renderLayers() {
301
+ renderLayers(): Layer | null {
302
+ assert(this.state);
303
+
168
304
  const {data} = this.state;
169
305
 
170
306
  if (!data) return null;
@@ -184,6 +320,3 @@ export default class CartoLayer extends CompositeLayer {
184
320
  );
185
321
  }
186
322
  }
187
-
188
- CartoLayer.layerName = 'CartoLayer';
189
- CartoLayer.defaultProps = defaultProps;
@@ -0,0 +1,44 @@
1
+ import {CompositeLayer} from '@deck.gl/core';
2
+ import {H3HexagonLayer, TileLayer} from '@deck.gl/geo-layers';
3
+ import H3Tileset2D from './h3-tileset-2d';
4
+
5
+ const renderSubLayers = props => {
6
+ const {data} = props;
7
+ const {index} = props.tile;
8
+ if (!data || !data.length) return null;
9
+
10
+ return new H3HexagonLayer(props, {
11
+ centerHexagon: index,
12
+ highPrecision: true
13
+ });
14
+ };
15
+
16
+ export default class H3TileLayer extends CompositeLayer {
17
+ initializeState() {
18
+ this.setState({data: null, tileJSON: null});
19
+ }
20
+
21
+ updateState({props, oldProps, context, changeFlags}) {
22
+ if (changeFlags.dataChanged) {
23
+ let {data} = this.props;
24
+ const tileJSON = data;
25
+ data = tileJSON.tiles;
26
+ this.setState({data, tileJSON});
27
+ }
28
+ }
29
+
30
+ renderLayers() {
31
+ const {data} = this.state;
32
+ return [
33
+ new TileLayer(this.props, {
34
+ id: 'h3-tile-layer',
35
+ data,
36
+ getHexagon: d => d.id,
37
+ TilesetClass: H3Tileset2D,
38
+ renderSubLayers
39
+ })
40
+ ];
41
+ }
42
+ }
43
+
44
+ H3TileLayer.layerName = 'H3TileLayer';
@@ -0,0 +1,82 @@
1
+ import {_Tileset2D as Tileset2D} from '@deck.gl/geo-layers';
2
+ import {polyfill, getRes0Indexes, h3GetResolution, h3ToGeoBoundary, h3ToParent} from 'h3-js';
3
+
4
+ export function getHexagonsInBoundingBox({west, north, east, south}, resolution) {
5
+ if (resolution === 0) {
6
+ return getRes0Indexes();
7
+ }
8
+ if (east - west > 180) {
9
+ // This is a known issue in h3-js: polyfill does not work correctly
10
+ // when longitude span is larger than 180 degrees.
11
+ return getHexagonsInBoundingBox({west, north, east: 0, south}, resolution).concat(
12
+ getHexagonsInBoundingBox({west: 0, north, east, south}, resolution)
13
+ );
14
+ }
15
+
16
+ // `polyfill()` fills based on hexagon center, which means tiles vanish
17
+ // prematurely. Get more accurate coverage by oversampling
18
+ const oversample = 1;
19
+ const h3Indices = polyfill(
20
+ [
21
+ [
22
+ [west, north],
23
+ [west, south],
24
+ [east, south],
25
+ [east, north],
26
+ [west, north]
27
+ ]
28
+ ],
29
+ resolution + oversample,
30
+ true
31
+ );
32
+
33
+ return oversample ? [...new Set(h3Indices.map(i => h3ToParent(i, resolution)))] : h3Indices;
34
+ }
35
+
36
+ export function tileToBoundingBox(index) {
37
+ const coordinates = h3ToGeoBoundary(index);
38
+ const latitudes = coordinates.map(c => c[0]);
39
+ const longitudes = coordinates.map(c => c[1]);
40
+ const west = Math.min(...longitudes);
41
+ const south = Math.min(...latitudes);
42
+ const east = Math.max(...longitudes);
43
+ const north = Math.max(...latitudes);
44
+ return {west, south, east, north};
45
+ }
46
+
47
+ export default class H3Tileset2D extends Tileset2D {
48
+ getTileIndices({viewport, minZoom, maxZoom}) {
49
+ const [east, south, west, north] = viewport.getBounds();
50
+
51
+ // TODO ignores extent
52
+ let z = viewport.zoom;
53
+ if (typeof minZoom === 'number' && Number.isFinite(minZoom) && z < minZoom) {
54
+ z = minZoom;
55
+ }
56
+ if (typeof maxZoom === 'number' && Number.isFinite(maxZoom) && z > maxZoom) {
57
+ z = maxZoom;
58
+ }
59
+
60
+ // Heuristic to get h3 resolution
61
+ const resolution = Math.max(0, Math.floor((2 * z) / 3) - 2);
62
+ return getHexagonsInBoundingBox({west, north, east, south}, resolution).map(i => ({i}));
63
+ }
64
+
65
+ getTileId({i}) {
66
+ return i;
67
+ }
68
+
69
+ getTileMetadata({i}) {
70
+ return {bbox: tileToBoundingBox(i)};
71
+ }
72
+
73
+ getTileZoom({i}) {
74
+ return h3GetResolution(i);
75
+ }
76
+
77
+ getParentIndex(index) {
78
+ const resolution = h3GetResolution(index.i);
79
+ const i = h3ToParent(index.i, resolution - 1);
80
+ return {i};
81
+ }
82
+ }
@@ -0,0 +1,42 @@
1
+ import {CompositeLayer} from '@deck.gl/core';
2
+ import {QuadkeyLayer, TileLayer} from '@deck.gl/geo-layers';
3
+ import QuadkeyTileset2D from './quadkey-tileset-2d';
4
+
5
+ const renderSubLayers = props => {
6
+ const {data} = props;
7
+ if (!data || !data.length) return null;
8
+ return new QuadkeyLayer(props);
9
+ };
10
+
11
+ export default class QuadkeyTileLayer extends CompositeLayer {
12
+ initializeState() {
13
+ this.setState({data: null, tileJSON: null});
14
+ }
15
+
16
+ updateState({props, oldProps, context, changeFlags}) {
17
+ if (changeFlags.dataChanged) {
18
+ let {data} = this.props;
19
+ const tileJSON = data;
20
+ data = tileJSON.tiles;
21
+ this.setState({data, tileJSON});
22
+ }
23
+ }
24
+
25
+ renderLayers() {
26
+ const {data, tileJSON} = this.state;
27
+ const {maxresolution} = tileJSON;
28
+ const maxZoom = maxresolution - this.props.aggregationResLevel;
29
+ return [
30
+ new TileLayer(this.props, {
31
+ id: 'quadkey-tile-layer',
32
+ data,
33
+ getQuadkey: d => d.id,
34
+ TilesetClass: QuadkeyTileset2D,
35
+ renderSubLayers,
36
+ maxZoom
37
+ })
38
+ ];
39
+ }
40
+ }
41
+
42
+ QuadkeyTileLayer.layerName = 'QuadkeyTileLayer';
@@ -0,0 +1,36 @@
1
+ import {_Tileset2D as Tileset2D} from '@deck.gl/geo-layers';
2
+
3
+ function tileToQuadkey(tile) {
4
+ let index = '';
5
+ for (let z = tile.z; z > 0; z--) {
6
+ let b = 0;
7
+ const mask = 1 << (z - 1);
8
+ if ((tile.x & mask) !== 0) b++;
9
+ if ((tile.y & mask) !== 0) b += 2;
10
+ index += b.toString();
11
+ }
12
+ return {i: index};
13
+ }
14
+
15
+ export default class QuadkeyTileset2D extends Tileset2D {
16
+ getTileIndices(opts) {
17
+ return super.getTileIndices(opts).map(tileToQuadkey);
18
+ }
19
+
20
+ getTileId({i}) {
21
+ return i;
22
+ }
23
+
24
+ getTileMetadata() {
25
+ return {};
26
+ }
27
+
28
+ getTileZoom({i}) {
29
+ return i.length;
30
+ }
31
+
32
+ getParentIndex(index) {
33
+ const i = index.i.slice(0, -1);
34
+ return {i};
35
+ }
36
+ }