@deck.gl/carto 8.9.0-alpha.4 → 8.9.0-beta.1

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 (111) hide show
  1. package/dist/dist.dev.js +13597 -58102
  2. package/dist/es5/api/carto-api-error.js +87 -0
  3. package/dist/es5/api/carto-api-error.js.map +1 -0
  4. package/dist/es5/api/index.js +8 -0
  5. package/dist/es5/api/index.js.map +1 -1
  6. package/dist/es5/api/layer-map.js +13 -1
  7. package/dist/es5/api/layer-map.js.map +1 -1
  8. package/dist/es5/api/maps-api-common.js +9 -1
  9. package/dist/es5/api/maps-api-common.js.map +1 -1
  10. package/dist/es5/api/maps-v3-client.js +145 -113
  11. package/dist/es5/api/maps-v3-client.js.map +1 -1
  12. package/dist/es5/api/parseMap.js +32 -15
  13. package/dist/es5/api/parseMap.js.map +1 -1
  14. package/dist/es5/index.js +6 -0
  15. package/dist/es5/index.js.map +1 -1
  16. package/dist/es5/layers/carto-layer.js.map +1 -1
  17. package/dist/es5/layers/carto-tile-layer.js.map +1 -1
  18. package/dist/es5/layers/quadbin-layer.js +3 -1
  19. package/dist/es5/layers/quadbin-layer.js.map +1 -1
  20. package/dist/es5/layers/quadbin-tile-layer.js +8 -2
  21. package/dist/es5/layers/quadbin-tile-layer.js.map +1 -1
  22. package/dist/es5/layers/quadbin-utils.js +5 -3
  23. package/dist/es5/layers/quadbin-utils.js.map +1 -1
  24. package/dist/es5/layers/spatial-index-tile-layer.js +21 -0
  25. package/dist/es5/layers/spatial-index-tile-layer.js.map +1 -1
  26. package/dist/esm/api/carto-api-error.js +51 -0
  27. package/dist/esm/api/carto-api-error.js.map +1 -0
  28. package/dist/esm/api/index.js +1 -0
  29. package/dist/esm/api/index.js.map +1 -1
  30. package/dist/esm/api/layer-map.js +11 -1
  31. package/dist/esm/api/layer-map.js.map +1 -1
  32. package/dist/esm/api/maps-api-common.js +6 -0
  33. package/dist/esm/api/maps-api-common.js.map +1 -1
  34. package/dist/esm/api/maps-v3-client.js +70 -41
  35. package/dist/esm/api/maps-v3-client.js.map +1 -1
  36. package/dist/esm/api/parseMap.js +36 -16
  37. package/dist/esm/api/parseMap.js.map +1 -1
  38. package/dist/esm/index.js +1 -1
  39. package/dist/esm/index.js.map +1 -1
  40. package/dist/esm/layers/carto-layer.js.map +1 -1
  41. package/dist/esm/layers/carto-tile-layer.js.map +1 -1
  42. package/dist/esm/layers/quadbin-layer.js +3 -1
  43. package/dist/esm/layers/quadbin-layer.js.map +1 -1
  44. package/dist/esm/layers/quadbin-tile-layer.js +4 -4
  45. package/dist/esm/layers/quadbin-tile-layer.js.map +1 -1
  46. package/dist/esm/layers/quadbin-utils.js +4 -4
  47. package/dist/esm/layers/quadbin-utils.js.map +1 -1
  48. package/dist/esm/layers/spatial-index-tile-layer.js +20 -0
  49. package/dist/esm/layers/spatial-index-tile-layer.js.map +1 -1
  50. package/dist.min.js +24 -278
  51. package/package.json +13 -13
  52. package/src/api/carto-api-error.ts +67 -0
  53. package/src/api/index.ts +2 -0
  54. package/src/api/layer-map.ts +15 -3
  55. package/src/api/maps-api-common.ts +8 -0
  56. package/src/api/maps-v3-client.ts +44 -38
  57. package/src/api/parseMap.ts +24 -16
  58. package/src/index.ts +2 -1
  59. package/src/layers/carto-layer.ts +4 -3
  60. package/src/layers/carto-tile-layer.ts +1 -1
  61. package/src/layers/quadbin-layer.ts +4 -2
  62. package/src/layers/quadbin-tile-layer.ts +4 -2
  63. package/src/layers/quadbin-utils.ts +4 -4
  64. package/src/layers/spatial-index-tile-layer.ts +28 -1
  65. package/typed/api/carto-api-error.d.ts +24 -0
  66. package/typed/api/carto-api-error.d.ts.map +1 -0
  67. package/typed/api/index.d.ts +2 -0
  68. package/typed/api/index.d.ts.map +1 -1
  69. package/typed/api/index.js +3 -0
  70. package/typed/api/layer-map.d.ts.map +1 -1
  71. package/typed/api/layer-map.js +264 -0
  72. package/typed/api/maps-api-common.d.ts +7 -0
  73. package/typed/api/maps-api-common.d.ts.map +1 -1
  74. package/typed/api/maps-api-common.js +50 -0
  75. package/typed/api/maps-client.js +125 -0
  76. package/typed/api/maps-v3-client.d.ts.map +1 -1
  77. package/typed/api/maps-v3-client.js +304 -0
  78. package/typed/api/parseMap.d.ts.map +1 -1
  79. package/typed/api/parseMap.js +178 -0
  80. package/typed/basemap.js +9 -0
  81. package/typed/config.js +35 -0
  82. package/typed/index.d.ts +2 -2
  83. package/typed/index.d.ts.map +1 -1
  84. package/typed/index.js +10 -0
  85. package/typed/layers/carto-layer.d.ts +3 -2
  86. package/typed/layers/carto-layer.d.ts.map +1 -1
  87. package/typed/layers/carto-layer.js +173 -0
  88. package/typed/layers/carto-tile-layer.js +105 -0
  89. package/typed/layers/h3-tile-layer.js +47 -0
  90. package/typed/layers/h3-tileset-2d.js +85 -0
  91. package/typed/layers/quadbin-layer.d.ts.map +1 -1
  92. package/typed/layers/quadbin-tile-layer.d.ts +4 -1
  93. package/typed/layers/quadbin-tile-layer.d.ts.map +1 -1
  94. package/typed/layers/quadbin-utils.d.ts +2 -2
  95. package/typed/layers/quadbin-utils.d.ts.map +1 -1
  96. package/typed/layers/quadkey-tile-layer.d.ts +21 -0
  97. package/typed/layers/quadkey-tile-layer.d.ts.map +1 -0
  98. package/typed/layers/quadkey-tile-layer.js +42 -0
  99. package/typed/layers/quadkey-tileset-2d.d.ts +15 -0
  100. package/typed/layers/quadkey-tileset-2d.d.ts.map +1 -0
  101. package/typed/layers/quadkey-tileset-2d.js +36 -0
  102. package/typed/layers/schema/carto-tile.js +161 -0
  103. package/typed/layers/spatial-index-tile-layer.d.ts +11 -1
  104. package/typed/layers/spatial-index-tile-layer.d.ts.map +1 -1
  105. package/typed/layers/spatial-index-tile-layer.js +40 -0
  106. package/typed/style/color-bins-style.js +12 -0
  107. package/typed/style/color-categories-style.js +16 -0
  108. package/typed/style/color-continuous-style.js +12 -0
  109. package/typed/style/palette.js +62 -0
  110. package/typed/style/utils.js +15 -0
  111. package/typed/utils.js +4 -0
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@deck.gl/carto",
3
3
  "description": "CARTO official integration with Deck.gl. Build geospatial applications using CARTO and Deck.gl.",
4
4
  "license": "MIT",
5
- "version": "8.9.0-alpha.4",
5
+ "version": "8.9.0-beta.1",
6
6
  "publishConfig": {
7
7
  "access": "public"
8
8
  },
@@ -28,28 +28,28 @@
28
28
  ],
29
29
  "sideEffects": false,
30
30
  "scripts": {
31
- "build-bundle": "webpack --config ../../scripts/bundle.config.js",
32
- "prepublishOnly": "npm run build-bundle && npm run build-bundle -- --env.dev"
31
+ "build-bundle": "ocular-bundle ./bundle.ts",
32
+ "prepublishOnly": "npm run build-bundle && npm run build-bundle -- --env=dev"
33
33
  },
34
34
  "dependencies": {
35
- "@loaders.gl/gis": "^3.2.5",
36
- "@loaders.gl/loader-utils": "^3.2.5",
37
- "@loaders.gl/mvt": "^3.2.5",
38
- "@loaders.gl/tiles": "^3.2.5",
35
+ "@loaders.gl/gis": "^3.3.0-alpha.10",
36
+ "@loaders.gl/loader-utils": "^3.3.0-alpha.10",
37
+ "@loaders.gl/mvt": "^3.3.0-alpha.10",
38
+ "@loaders.gl/tiles": "^3.3.0-alpha.10",
39
39
  "@luma.gl/constants": "^8.5.16",
40
40
  "@math.gl/web-mercator": "^3.6.2",
41
41
  "cartocolor": "^4.0.2",
42
- "d3-array": "^2.8.0",
43
- "d3-color": "^2.0.0",
44
- "d3-format": "^2.0.0",
45
- "d3-scale": "^3.2.3",
42
+ "d3-array": "^3.2.0",
43
+ "d3-color": "^3.1.0",
44
+ "d3-format": "^3.1.0",
45
+ "d3-scale": "^4.0.0",
46
46
  "h3-js": "^3.7.0",
47
47
  "moment-timezone": "^0.5.33",
48
48
  "pbf": "^3.2.1",
49
49
  "quadbin": "^0.1.2"
50
50
  },
51
51
  "devDependencies": {
52
- "@loaders.gl/schema": "^3.2.5",
52
+ "@loaders.gl/schema": "^3.3.0-alpha.10",
53
53
  "@types/d3-array": "^3.0.2",
54
54
  "@types/d3-color": "^1.4.2",
55
55
  "@types/d3-scale": "^3.0.0"
@@ -62,5 +62,5 @@
62
62
  "@deck.gl/layers": "^8.0.0",
63
63
  "@loaders.gl/core": "^3.0.0"
64
64
  },
65
- "gitHead": "f34f77e2b050306ade160a5628ea2844a4ceabc2"
65
+ "gitHead": "47223d1ea084c3f9b0c9f0f9736682850bb5db85"
66
66
  }
@@ -0,0 +1,67 @@
1
+ import {MapType, RequestType} from './maps-api-common';
2
+
3
+ export type APIErrorContext = {
4
+ requestType: RequestType;
5
+ mapId?: string;
6
+ connection?: string;
7
+ source?: string;
8
+ type?: MapType;
9
+ };
10
+
11
+ /**
12
+ *
13
+ * Custom error for reported errors in CARTO Maps API.
14
+ * Provides useful debugging information in console and context for applications.
15
+ *
16
+ */
17
+ export class CartoAPIError extends Error {
18
+ /** Source error from server */
19
+ error: Error;
20
+
21
+ /** Context (API call & parameters) in which error occured */
22
+ errorContext: APIErrorContext;
23
+
24
+ /** Response from server */
25
+ response?: Response;
26
+
27
+ constructor(error: Error, errorContext: APIErrorContext, response?: Response) {
28
+ let responseString = 'Failed to connect';
29
+ if (response) {
30
+ responseString = 'Server returned: ';
31
+ if (response.status === 400) {
32
+ responseString += 'Bad request';
33
+ } else if (response.status === 401 || response.status === 403) {
34
+ responseString += 'Unauthorized access';
35
+ } else if (response.status === 404) {
36
+ responseString += 'Not found';
37
+ } else {
38
+ responseString += 'Error';
39
+ }
40
+
41
+ responseString += ` (${response.status}):`;
42
+ }
43
+ responseString += ` ${error.message || error}`;
44
+
45
+ let message = `${errorContext.requestType} API request failed`;
46
+ message += `\n${responseString}`;
47
+ for (const key of Object.keys(errorContext)) {
48
+ if (key === 'requestType') continue; // eslint-disable-line no-continue
49
+ message += `\n${formatErrorKey(key)}: ${errorContext[key]}`;
50
+ }
51
+ message += '\n';
52
+
53
+ super(message);
54
+
55
+ this.name = 'CartoAPIError';
56
+ this.response = response;
57
+ this.error = error;
58
+ this.errorContext = errorContext;
59
+ }
60
+ }
61
+
62
+ /**
63
+ * Converts camelCase to Camel Case
64
+ */
65
+ function formatErrorKey(key) {
66
+ return key.replace(/([A-Z])/g, ' $1').replace(/^./, s => s.toUpperCase());
67
+ }
package/src/api/index.ts CHANGED
@@ -1,3 +1,5 @@
1
+ export {CartoAPIError} from './carto-api-error';
2
+ export type {APIErrorContext} from './carto-api-error';
1
3
  export {FORMATS, TILE_FORMATS, MAP_TYPES, API_VERSIONS} from './maps-api-common';
2
4
  export type {QueryParameters} from './maps-api-common';
3
5
  export {getDataV2, CONNECTIONS} from './maps-client';
@@ -78,14 +78,19 @@ const hexToRGBA = c => {
78
78
  return [r, g, b, 255 * opacity];
79
79
  };
80
80
 
81
- // Kepler -> Deck.gl
81
+ // Kepler prop value -> Deck.gl prop value
82
+ // Supports nested definitions, and function transforms:
83
+ // {keplerProp: 'deckProp'} is equivalent to:
84
+ // {keplerProp: x => ({deckProp: x})}
82
85
  const sharedPropMap = {
86
+ // Apply the value of Kepler `color` prop to the deck `getFillColor` prop
83
87
  color: 'getFillColor',
84
88
  isVisible: 'visible',
85
89
  label: 'cartoLabel',
86
90
  textLabel: {
87
91
  alignment: 'getTextAlignmentBaseline',
88
92
  anchor: 'getTextAnchor',
93
+ // Apply the value of Kepler `textLabel.color` prop to the deck `getTextColor` prop
89
94
  color: 'getTextColor',
90
95
  size: 'getTextSize'
91
96
  },
@@ -153,7 +158,12 @@ export function getLayer(
153
158
  > = {
154
159
  point: {
155
160
  Layer: GeoJsonLayer,
156
- propMap: {visConfig: {outline: 'stroked'}}
161
+ propMap: {
162
+ columns: {
163
+ altitude: x => ({parameters: {depthTest: Boolean(x)}})
164
+ },
165
+ visConfig: {outline: 'stroked'}
166
+ }
157
167
  },
158
168
  geojson: {
159
169
  Layer: GeoJsonLayer
@@ -425,7 +435,9 @@ export function getSizeAccessor(
425
435
  ) {
426
436
  const scale = scaleType ? SCALE_FUNCS[scaleType as any]() : identity;
427
437
  if (scaleType) {
428
- scale.domain(calculateDomain(data, name, scaleType));
438
+ if (aggregation !== 'count') {
439
+ scale.domain(calculateDomain(data, name, scaleType));
440
+ }
429
441
  scale.range(range);
430
442
  }
431
443
 
@@ -71,6 +71,14 @@ type MapInstantiationFormats = Record<
71
71
  }
72
72
  >;
73
73
 
74
+ export const REQUEST_TYPES = {
75
+ DATA: 'Map data',
76
+ INSTANTIATION: 'Map instantiation',
77
+ PUBLIC_MAP: 'Public map',
78
+ TILE_STATS: 'Tile stats'
79
+ } as const;
80
+ export type RequestType = ValuesOf<typeof REQUEST_TYPES>;
81
+
74
82
  /**
75
83
  * Simple encode parameter
76
84
  */
@@ -18,15 +18,19 @@ import {
18
18
  MapType,
19
19
  MAP_TYPES,
20
20
  QueryParameters,
21
+ REQUEST_TYPES,
21
22
  SchemaField,
22
23
  TileFormat,
23
24
  TILE_FORMATS
24
25
  } from './maps-api-common';
26
+
27
+ import {APIErrorContext, CartoAPIError} from './carto-api-error';
28
+
25
29
  import {parseMap} from './parseMap';
26
30
  import {log} from '@deck.gl/core';
27
31
  import {assert} from '../utils';
28
32
 
29
- const MAX_GET_LENGTH = 2048;
33
+ const MAX_GET_LENGTH = 8192;
30
34
  const DEFAULT_CLIENT = 'deck-gl-carto';
31
35
  const V3_MINOR_VERSION = '3.1';
32
36
 
@@ -37,6 +41,7 @@ interface RequestParams {
37
41
  headers?: Headers;
38
42
  accessToken?: string;
39
43
  body?: any;
44
+ errorContext: APIErrorContext;
40
45
  }
41
46
 
42
47
  /**
@@ -47,7 +52,8 @@ async function request({
47
52
  url,
48
53
  headers: customHeaders,
49
54
  accessToken,
50
- body
55
+ body,
56
+ errorContext
51
57
  }: RequestParams): Promise<Response> {
52
58
  const headers: Headers = {
53
59
  ...customHeaders,
@@ -70,7 +76,7 @@ async function request({
70
76
  body
71
77
  });
72
78
  } catch (error) {
73
- throw new Error(`Failed to connect to Maps API: ${error}`);
79
+ throw new CartoAPIError(error as Error, errorContext);
74
80
  }
75
81
  }
76
82
 
@@ -79,13 +85,14 @@ async function requestJson<T = unknown>({
79
85
  url,
80
86
  headers,
81
87
  accessToken,
82
- body
88
+ body,
89
+ errorContext
83
90
  }: RequestParams): Promise<T> {
84
- const response = await request({method, url, headers, accessToken, body});
91
+ const response = await request({method, url, headers, accessToken, body, errorContext});
85
92
  const json = await response.json();
86
93
 
87
94
  if (!response.ok) {
88
- dealWithError({response, error: json.error});
95
+ throw new CartoAPIError(json.error, errorContext, response);
89
96
  }
90
97
  return json as T;
91
98
  }
@@ -95,33 +102,19 @@ async function requestData({
95
102
  url,
96
103
  accessToken,
97
104
  format,
98
- body
105
+ body,
106
+ errorContext
99
107
  }: RequestParams & {
100
108
  format: Format;
101
109
  }): Promise<Response | unknown> {
102
110
  if (format === FORMATS.NDJSON) {
103
- return request({method, url, accessToken, body});
111
+ return request({method, url, accessToken, body, errorContext});
104
112
  }
105
113
 
106
- const data = await requestJson<any>({method, url, accessToken, body});
114
+ const data = await requestJson<any>({method, url, accessToken, body, errorContext});
107
115
  return data.rows ? data.rows : data;
108
116
  }
109
117
 
110
- /**
111
- * Display proper message from Maps API error
112
- */
113
- function dealWithError({response, error}: {response: Response; error?: string}): never {
114
- switch (response.status) {
115
- case 400:
116
- throw new Error(`Bad request. ${error}`);
117
- case 401:
118
- case 403:
119
- throw new Error(`Unauthorized access. ${error}`);
120
- default:
121
- throw new Error(error);
122
- }
123
- }
124
-
125
118
  type FetchLayerDataParams = {
126
119
  type: MapType;
127
120
  source: string;
@@ -211,17 +204,28 @@ export async function mapInstantiation({
211
204
  })}`;
212
205
  const {accessToken} = credentials;
213
206
 
207
+ const errorContext = {requestType: REQUEST_TYPES.INSTANTIATION, connection, type, source};
214
208
  if (url.length > MAX_GET_LENGTH && type === MAP_TYPES.QUERY) {
215
209
  // need to be a POST request
216
210
  const body = JSON.stringify({
217
211
  q: source,
218
212
  client: clientId || DEFAULT_CLIENT,
213
+ aggregationResLevel,
214
+ aggregationExp,
215
+ geoColumn,
219
216
  queryParameters
220
217
  });
221
- return await requestJson({method: 'POST', url: baseUrl, headers, accessToken, body});
218
+ return await requestJson({
219
+ method: 'POST',
220
+ url: baseUrl,
221
+ headers,
222
+ accessToken,
223
+ body,
224
+ errorContext
225
+ });
222
226
  }
223
227
 
224
- return await requestJson({url, headers, accessToken});
228
+ return await requestJson({url, headers, accessToken, errorContext});
225
229
  }
226
230
 
227
231
  function getUrlFromMetadata(metadata: MapInstantiation, format: Format): string | null {
@@ -267,13 +271,6 @@ function checkFetchLayerDataParameters({
267
271
  assert(!aggregationExp, 'Have aggregationExp, but geoColumn parameter is missing');
268
272
  assert(!aggregationResLevel, 'Have aggregationResLevel, but geoColumn parameter is missing');
269
273
  }
270
-
271
- if (!aggregationExp) {
272
- assert(
273
- !aggregationResLevel,
274
- 'Have aggregationResLevel, but aggregationExp parameter is missing'
275
- );
276
- }
277
274
  }
278
275
 
279
276
  export interface FetchLayerDataResult {
@@ -314,7 +311,8 @@ export async function fetchLayerData({
314
311
  queryParameters
315
312
  });
316
313
 
317
- const data = await requestData({url, format: mapFormat, accessToken});
314
+ const errorContext = {requestType: REQUEST_TYPES.DATA, connection, type, source};
315
+ const data = await requestData({url, format: mapFormat, accessToken, errorContext});
318
316
  const result: FetchLayerDataResult = {data, format: mapFormat, schema: metadata.schema};
319
317
  return result;
320
318
  }
@@ -444,7 +442,8 @@ async function _fetchMapDataset(
444
442
  dataset.cache = cache;
445
443
 
446
444
  // Only fetch if the data has changed
447
- dataset.data = await requestData({url, format: mapFormat, accessToken});
445
+ const errorContext = {requestType: REQUEST_TYPES.DATA, connection, type, source};
446
+ dataset.data = await requestData({url, format: mapFormat, accessToken, errorContext});
448
447
 
449
448
  return true;
450
449
  }
@@ -460,12 +459,13 @@ async function _fetchTilestats(
460
459
  const statsUrl = buildStatsUrlFromBase(credentials.apiBaseUrl);
461
460
  let url = `${statsUrl}/${connection}/`;
462
461
  if (type === MAP_TYPES.QUERY) {
463
- url += `${attribute}?q=${source}`;
462
+ url += `${attribute}?${encodeParameter('q', source)}`;
464
463
  } else {
465
464
  // MAP_TYPE.TABLE
466
465
  url += `${source}/${attribute}`;
467
466
  }
468
- const stats = await requestData({url, format: FORMATS.JSON, accessToken});
467
+ const errorContext = {requestType: REQUEST_TYPES.TILE_STATS, connection, type, source};
468
+ const stats = await requestData({url, format: FORMATS.JSON, accessToken, errorContext});
469
469
 
470
470
  // Replace tilestats for attribute with value from API
471
471
  const {attributes} = dataset.data.tilestats.layers[0];
@@ -562,7 +562,8 @@ export async function fetchMap({
562
562
  }
563
563
 
564
564
  const url = `${localCreds.mapsUrl}/public/${cartoMapId}`;
565
- const map = await requestJson<any>({url, headers, accessToken});
565
+ const errorContext = {requestType: REQUEST_TYPES.PUBLIC_MAP, mapId: cartoMapId};
566
+ const map = await requestJson<any>({url, headers, accessToken, errorContext});
566
567
 
567
568
  // Periodically check if the data has changed. Note that this
568
569
  // will not update when a map is published.
@@ -586,7 +587,12 @@ export async function fetchMap({
586
587
  const geojsonDatasetIds = geojsonLayers.map(({config}) => config.dataId);
587
588
  map.datasets.forEach(dataset => {
588
589
  if (geojsonDatasetIds.includes(dataset.id)) {
590
+ const {config} = geojsonLayers.find(({config}) => config.dataId === dataset.id);
589
591
  dataset.format = 'geojson';
592
+ // Support for very old maps. geoColumn was not stored in the past
593
+ if (!dataset.geoColumn && config.columns.geojson) {
594
+ dataset.geoColumn = config.columns.geojson;
595
+ }
590
596
  }
591
597
  });
592
598
 
@@ -40,14 +40,15 @@ export function parseMap(json) {
40
40
  const {data} = dataset;
41
41
  assert(data, `No data loaded for dataId: ${dataId}`);
42
42
  const {Layer, propMap, defaultProps} = getLayer(type, config, dataset);
43
+ const styleProps = createStyleProps(config, propMap);
43
44
  return new Layer({
44
45
  id,
45
46
  data,
46
47
  ...defaultProps,
47
- ...createBlendingProps(layerBlending),
48
48
  ...(!config.textLabel && createInteractionProps(interactionConfig)),
49
- ...createStyleProps(config, propMap),
49
+ ...styleProps,
50
50
  ...createChannelProps(visualChannels, type, config, data), // Must come after style
51
+ ...createParametersProp(layerBlending, styleProps.parameters || {}), // Must come after style
51
52
  ...createLoadOptions(token)
52
53
  });
53
54
  } catch (e: any) {
@@ -86,24 +87,16 @@ function extractTextLayers(layers) {
86
87
  );
87
88
  }
88
89
 
89
- function createBlendingProps(layerBlending) {
90
+ function createParametersProp(layerBlending, parameters: Record<string, any>) {
90
91
  if (layerBlending === 'additive') {
91
- return {
92
- parameters: {
93
- blendFunc: [GL.SRC_ALPHA, GL.DST_ALPHA],
94
- blendEquation: GL.FUNC_ADD
95
- }
96
- };
92
+ parameters.blendFunc = [GL.SRC_ALPHA, GL.DST_ALPHA];
93
+ parameters.blendEquation = GL.FUNC_ADD;
97
94
  } else if (layerBlending === 'subtractive') {
98
- return {
99
- parameters: {
100
- blendFunc: [GL.ONE, GL.ONE_MINUS_DST_COLOR, GL.SRC_ALPHA, GL.DST_ALPHA],
101
- blendEquation: [GL.FUNC_SUBTRACT, GL.FUNC_ADD]
102
- }
103
- };
95
+ parameters.blendFunc = [GL.ONE, GL.ONE_MINUS_DST_COLOR, GL.SRC_ALPHA, GL.DST_ALPHA];
96
+ parameters.blendEquation = [GL.FUNC_SUBTRACT, GL.FUNC_ADD];
104
97
  }
105
98
 
106
- return {};
99
+ return Object.keys(parameters).length ? {parameters} : {};
107
100
  }
108
101
 
109
102
  function createInteractionProps(interactionConfig) {
@@ -206,6 +199,21 @@ function createChannelProps(
206
199
  );
207
200
  }
208
201
 
202
+ if (type === 'point') {
203
+ const altitude = config.columns?.altitude;
204
+ if (altitude) {
205
+ result.dataTransform = data => {
206
+ data.features.forEach(({geometry, properties}) => {
207
+ const {type, coordinates} = geometry;
208
+ if (type === 'Point') {
209
+ coordinates[2] = properties[altitude];
210
+ }
211
+ });
212
+ return data;
213
+ };
214
+ }
215
+ }
216
+
209
217
  if (radiusField || sizeField) {
210
218
  result.getPointRadius = getSizeAccessor(
211
219
  // @ts-ignore
package/src/index.ts CHANGED
@@ -12,10 +12,11 @@ export {
12
12
  TILE_FORMATS,
13
13
  MAP_TYPES,
14
14
  API_VERSIONS,
15
+ CartoAPIError,
15
16
  fetchLayerData,
16
17
  fetchMap,
17
18
  getDataV2 as _getDataV2,
18
19
  mapInstantiation as _mapInstantiation
19
20
  } from './api';
20
- export type {QueryParameters} from './api';
21
+ export type {APIErrorContext, QueryParameters} from './api';
21
22
  export type {CartoLayerProps} from './layers/carto-layer';
@@ -9,6 +9,7 @@ import {
9
9
 
10
10
  import {MVTLayer} from '@deck.gl/geo-layers';
11
11
  import {fetchLayerData, getDataV2, API_VERSIONS} from '../api';
12
+ import {CartoAPIError} from '../api/carto-api-error';
12
13
  import {layerFromTileDataset} from '../api/layer-map';
13
14
  import {
14
15
  COLUMNS_SUPPORT,
@@ -145,9 +146,9 @@ type _CartoLayerProps = {
145
146
  /**
146
147
  * Called when the request to the CARTO Maps API failed.
147
148
  *
148
- * By default the Error is thrown.
149
+ * By default the CartoAPIError is thrown.
149
150
  */
150
- onDataError?: (err: unknown) => void;
151
+ onDataError?: (err: CartoAPIError) => void;
151
152
 
152
153
  clientId?: string;
153
154
 
@@ -272,7 +273,7 @@ export default class CartoLayer<ExtraProps = {}> extends CompositeLayer<
272
273
  });
273
274
  } catch (err) {
274
275
  if (this.props.onDataError) {
275
- this.props.onDataError(err as Error);
276
+ this.props.onDataError(err as CartoAPIError);
276
277
  } else {
277
278
  throw err;
278
279
  }
@@ -13,7 +13,7 @@ import {
13
13
  _TileLoadProps as TileLoadProps
14
14
  } from '@deck.gl/geo-layers';
15
15
  import {GeoJsonLayer} from '@deck.gl/layers';
16
- import {binaryToGeojson, geojsonToBinary} from '@loaders.gl/gis';
16
+ import {binaryToGeojson} from '@loaders.gl/gis';
17
17
  import type {BinaryFeatures} from '@loaders.gl/schema';
18
18
  import {TileFormat, TILE_FORMATS} from '../api/maps-api-common';
19
19
  import type {Feature} from 'geojson';
@@ -30,14 +30,16 @@ export default class QuadbinLayer<DataT = any, ExtraProps = {}> extends GeoCellL
30
30
  static defaultProps = defaultProps;
31
31
 
32
32
  indexToBounds(): Partial<GeoCellLayer['props']> | null {
33
- const {data, getQuadbin} = this.props;
33
+ const {data, extruded, getQuadbin} = this.props;
34
+ // To avoid z-fighting reduce polygon footprint when extruding
35
+ const coverage = extruded ? 0.99 : 1;
34
36
 
35
37
  return {
36
38
  data,
37
39
  _normalize: false,
38
40
  positionFormat: 'XY',
39
41
 
40
- getPolygon: (x: DataT, objectInfo) => getQuadbinPolygon(getQuadbin(x, objectInfo))
42
+ getPolygon: (x: DataT, objectInfo) => getQuadbinPolygon(getQuadbin(x, objectInfo), coverage)
41
43
  };
42
44
  }
43
45
  }
@@ -9,12 +9,14 @@ import {
9
9
  import QuadbinLayer, {QuadbinLayerProps} from './quadbin-layer';
10
10
  import QuadbinTileset2D from './quadbin-tileset-2d';
11
11
  import SpatialIndexTileLayer from './spatial-index-tile-layer';
12
+ import {hexToBigInt} from 'quadbin';
12
13
 
13
- const renderSubLayers = props => {
14
+ export const renderSubLayers = props => {
14
15
  const {data} = props;
15
16
  if (!data || !data.length) return null;
17
+ const isBigInt = typeof data[0].id === 'bigint';
16
18
  return new QuadbinLayer(props, {
17
- getQuadbin: d => d.id
19
+ getQuadbin: isBigInt ? d => d.id : d => hexToBigInt(d.id)
18
20
  });
19
21
  };
20
22
 
@@ -3,18 +3,18 @@ import {cellToTile} from 'quadbin';
3
3
 
4
4
  const TILE_SIZE = 512;
5
5
 
6
- export function quadbinToWorldBounds(quadbin: bigint): [number[], number[]] {
6
+ export function quadbinToWorldBounds(quadbin: bigint, coverage: number): [number[], number[]] {
7
7
  const {x, y, z} = cellToTile(quadbin);
8
8
  const mask = 1 << z;
9
9
  const scale = mask / TILE_SIZE;
10
10
  return [
11
11
  [x / scale, TILE_SIZE - y / scale],
12
- [(x + 0.99) / scale, TILE_SIZE - (y + 0.99) / scale]
12
+ [(x + coverage) / scale, TILE_SIZE - (y + coverage) / scale]
13
13
  ];
14
14
  }
15
15
 
16
- export function getQuadbinPolygon(quadbin: bigint): number[] {
17
- const [topLeft, bottomRight] = quadbinToWorldBounds(quadbin);
16
+ export function getQuadbinPolygon(quadbin: bigint, coverage = 1): number[] {
17
+ const [topLeft, bottomRight] = quadbinToWorldBounds(quadbin, coverage);
18
18
  const [w, n] = worldToLngLat(topLeft);
19
19
  const [e, s] = worldToLngLat(bottomRight);
20
20
  return [e, n, e, s, w, s, w, n, e, n];
@@ -1,4 +1,5 @@
1
1
  import {registerLoaders} from '@loaders.gl/core';
2
+ import {DefaultProps, UpdateParameters} from '@deck.gl/core';
2
3
  import CartoSpatialTileLoader from './schema/carto-spatial-tile-loader';
3
4
  registerLoaders([CartoSpatialTileLoader]);
4
5
 
@@ -15,8 +16,25 @@ function isFeatureIdDefined(value: unknown): boolean {
15
16
  return value !== undefined && value !== null && value !== '';
16
17
  }
17
18
 
18
- export default class SpatialIndexTileLayer<ExtraProps = {}> extends TileLayer<any, ExtraProps> {
19
+ const defaultProps: DefaultProps<SpatialIndexTileLayerProps> = {
20
+ aggregationResLevel: 4
21
+ };
22
+
23
+ /** All properties supported by SpatialIndexTileLayer. */
24
+ export type SpatialIndexTileLayerProps<DataT = any> = _SpatialIndexTileLayerProps<DataT> &
25
+ TileLayer<DataT>;
26
+
27
+ /** Properties added by SpatialIndexTileLayer. */
28
+ type _SpatialIndexTileLayerProps<DataT = any> = {
29
+ aggregationResLevel?: number;
30
+ };
31
+
32
+ export default class SpatialIndexTileLayer<DataT = any, ExtraProps = {}> extends TileLayer<
33
+ DataT,
34
+ ExtraProps & Required<_SpatialIndexTileLayerProps<DataT>>
35
+ > {
19
36
  static layerName = 'SpatialIndexTileLayer';
37
+ static defaultProps = defaultProps;
20
38
 
21
39
  getTileData(tile: TileLoadProps) {
22
40
  const {data, getTileData, fetch} = this.props;
@@ -48,6 +66,15 @@ export default class SpatialIndexTileLayer<ExtraProps = {}> extends TileLayer<an
48
66
  return fetch(tile.url, {propName: 'data', layer: this, loadOptions, signal});
49
67
  }
50
68
 
69
+ updateState(params: UpdateParameters<this>) {
70
+ const {props, oldProps} = params;
71
+ if (props.aggregationResLevel !== oldProps.aggregationResLevel) {
72
+ // Tileset cache is invalid when resLevel changes
73
+ this.setState({tileset: null});
74
+ }
75
+ super.updateState(params);
76
+ }
77
+
51
78
  protected _updateAutoHighlight(info: PickingInfo): void {
52
79
  const {hoveredFeatureId} = this.state;
53
80
  const hoveredFeature = info.object;
@@ -0,0 +1,24 @@
1
+ import { MapType, RequestType } from './maps-api-common';
2
+ export declare type APIErrorContext = {
3
+ requestType: RequestType;
4
+ mapId?: string;
5
+ connection?: string;
6
+ source?: string;
7
+ type?: MapType;
8
+ };
9
+ /**
10
+ *
11
+ * Custom error for reported errors in CARTO Maps API.
12
+ * Provides useful debugging information in console and context for applications.
13
+ *
14
+ */
15
+ export declare class CartoAPIError extends Error {
16
+ /** Source error from server */
17
+ error: Error;
18
+ /** Context (API call & parameters) in which error occured */
19
+ errorContext: APIErrorContext;
20
+ /** Response from server */
21
+ response?: Response;
22
+ constructor(error: Error, errorContext: APIErrorContext, response?: Response);
23
+ }
24
+ //# sourceMappingURL=carto-api-error.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"carto-api-error.d.ts","sourceRoot":"","sources":["../../src/api/carto-api-error.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,OAAO,EAAE,WAAW,EAAC,MAAM,mBAAmB,CAAC;AAEvD,oBAAY,eAAe,GAAG;IAC5B,WAAW,EAAE,WAAW,CAAC;IACzB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,OAAO,CAAC;CAChB,CAAC;AAEF;;;;;GAKG;AACH,qBAAa,aAAc,SAAQ,KAAK;IACtC,+BAA+B;IAC/B,KAAK,EAAE,KAAK,CAAC;IAEb,6DAA6D;IAC7D,YAAY,EAAE,eAAe,CAAC;IAE9B,2BAA2B;IAC3B,QAAQ,CAAC,EAAE,QAAQ,CAAC;gBAER,KAAK,EAAE,KAAK,EAAE,YAAY,EAAE,eAAe,EAAE,QAAQ,CAAC,EAAE,QAAQ;CAiC7E"}
@@ -1,3 +1,5 @@
1
+ export { CartoAPIError } from './carto-api-error';
2
+ export type { APIErrorContext } from './carto-api-error';
1
3
  export { FORMATS, TILE_FORMATS, MAP_TYPES, API_VERSIONS } from './maps-api-common';
2
4
  export type { QueryParameters } from './maps-api-common';
3
5
  export { getDataV2, CONNECTIONS } from './maps-client';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/api/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,OAAO,EAAE,YAAY,EAAE,SAAS,EAAE,YAAY,EAAC,MAAM,mBAAmB,CAAC;AACjF,YAAY,EAAC,eAAe,EAAC,MAAM,mBAAmB,CAAC;AACvD,OAAO,EAAC,SAAS,EAAE,WAAW,EAAC,MAAM,eAAe,CAAC;AACrD,OAAO,EAAC,cAAc,EAAE,QAAQ,EAAE,gBAAgB,EAAC,MAAM,kBAAkB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/api/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,aAAa,EAAC,MAAM,mBAAmB,CAAC;AAChD,YAAY,EAAC,eAAe,EAAC,MAAM,mBAAmB,CAAC;AACvD,OAAO,EAAC,OAAO,EAAE,YAAY,EAAE,SAAS,EAAE,YAAY,EAAC,MAAM,mBAAmB,CAAC;AACjF,YAAY,EAAC,eAAe,EAAC,MAAM,mBAAmB,CAAC;AACvD,OAAO,EAAC,SAAS,EAAE,WAAW,EAAC,MAAM,eAAe,CAAC;AACrD,OAAO,EAAC,cAAc,EAAE,QAAQ,EAAE,gBAAgB,EAAC,MAAM,kBAAkB,CAAC"}