@deck.gl/carto 8.6.0 → 8.7.0-alpha.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 (43) hide show
  1. package/dist/dist.dev.js +24459 -726
  2. package/dist/es5/api/index.js +12 -0
  3. package/dist/es5/api/index.js.map +1 -1
  4. package/dist/es5/api/layer-map.js +255 -0
  5. package/dist/es5/api/layer-map.js.map +1 -0
  6. package/dist/es5/api/maps-api-common.js +10 -1
  7. package/dist/es5/api/maps-api-common.js.map +1 -1
  8. package/dist/es5/api/maps-client.js +18 -12
  9. package/dist/es5/api/maps-client.js.map +1 -1
  10. package/dist/es5/api/maps-v3-client.js +340 -42
  11. package/dist/es5/api/maps-v3-client.js.map +1 -1
  12. package/dist/es5/api/parseMap.js +211 -0
  13. package/dist/es5/api/parseMap.js.map +1 -0
  14. package/dist/es5/config.js +4 -2
  15. package/dist/es5/config.js.map +1 -1
  16. package/dist/es5/index.js +12 -0
  17. package/dist/es5/index.js.map +1 -1
  18. package/dist/esm/api/index.js +1 -1
  19. package/dist/esm/api/index.js.map +1 -1
  20. package/dist/esm/api/layer-map.js +219 -0
  21. package/dist/esm/api/layer-map.js.map +1 -0
  22. package/dist/esm/api/maps-api-common.js +4 -0
  23. package/dist/esm/api/maps-api-common.js.map +1 -1
  24. package/dist/esm/api/maps-client.js +11 -6
  25. package/dist/esm/api/maps-client.js.map +1 -1
  26. package/dist/esm/api/maps-v3-client.js +190 -17
  27. package/dist/esm/api/maps-v3-client.js.map +1 -1
  28. package/dist/esm/api/parseMap.js +202 -0
  29. package/dist/esm/api/parseMap.js.map +1 -0
  30. package/dist/esm/config.js +4 -4
  31. package/dist/esm/config.js.map +1 -1
  32. package/dist/esm/index.js +1 -1
  33. package/dist/esm/index.js.map +1 -1
  34. package/dist.min.js +278 -1
  35. package/package.json +7 -3
  36. package/src/api/index.js +1 -1
  37. package/src/api/layer-map.js +194 -0
  38. package/src/api/maps-api-common.js +5 -0
  39. package/src/api/maps-client.js +16 -3
  40. package/src/api/maps-v3-client.js +145 -12
  41. package/src/api/parseMap.js +192 -0
  42. package/src/config.js +4 -4
  43. package/src/index.js +2 -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.6.0",
5
+ "version": "8.7.0-alpha.1",
6
6
  "publishConfig": {
7
7
  "access": "public"
8
8
  },
@@ -36,7 +36,11 @@
36
36
  "@loaders.gl/tiles": "^3.0.8",
37
37
  "@math.gl/web-mercator": "^3.5.4",
38
38
  "cartocolor": "^4.0.2",
39
- "d3-scale": "^3.2.3"
39
+ "d3-array": "^2.8.0",
40
+ "d3-color": "^2.0.0",
41
+ "d3-format": "^2.0.0",
42
+ "d3-scale": "^3.2.3",
43
+ "moment-timezone": "^0.5.33"
40
44
  },
41
45
  "peerDependencies": {
42
46
  "@deck.gl/core": "^8.0.0",
@@ -44,5 +48,5 @@
44
48
  "@deck.gl/layers": "^8.0.0",
45
49
  "@loaders.gl/core": "^3.0.0"
46
50
  },
47
- "gitHead": "d9557a3dc37ae365340aefe53eab4055ba577394"
51
+ "gitHead": "dbaa3941a41f3ddceecad4d2d2879515d97d2510"
48
52
  }
package/src/api/index.js CHANGED
@@ -1,3 +1,3 @@
1
1
  export {FORMATS, MAP_TYPES, API_VERSIONS} from './maps-api-common';
2
2
  export {getDataV2, CONNECTIONS} from './maps-client';
3
- export {getData, mapInstantiation} from './maps-v3-client';
3
+ export {fetchLayerData, fetchMap, getData, mapInstantiation} from './maps-v3-client';
@@ -0,0 +1,194 @@
1
+ import {extent} from 'd3-array';
2
+ import {rgb} from 'd3-color';
3
+ import {
4
+ scaleLinear,
5
+ scaleOrdinal,
6
+ scaleLog,
7
+ scalePoint,
8
+ scaleQuantile,
9
+ scaleQuantize,
10
+ scaleSqrt
11
+ } from 'd3-scale';
12
+ import {format as d3Format} from 'd3-format';
13
+ import moment from 'moment-timezone';
14
+ import {H3HexagonLayer, MVTLayer} from '@deck.gl/geo-layers';
15
+ import {GeoJsonLayer} from '@deck.gl/layers';
16
+
17
+ const SCALE_FUNCS = {
18
+ linear: scaleLinear,
19
+ ordinal: scaleOrdinal,
20
+ log: scaleLog,
21
+ point: scalePoint,
22
+ quantile: scaleQuantile,
23
+ quantize: scaleQuantize,
24
+ sqrt: scaleSqrt
25
+ };
26
+
27
+ // Kepler -> Deck.gl
28
+ const sharedPropMap = {
29
+ color: 'getFillColor',
30
+ isVisible: 'visible',
31
+ textLabel: {
32
+ alignment: 'getTextAlignmentBaseline',
33
+ anchor: 'getTextAnchor',
34
+ color: 'getTextColor',
35
+ size: 'getTextSize'
36
+ },
37
+ visConfig: {
38
+ enable3d: 'extruded',
39
+ filled: 'filled',
40
+ opacity: 'opacity',
41
+ strokeColor: 'getLineColor',
42
+ stroked: 'stroked',
43
+ thickness: 'getLineWidth',
44
+ radius: 'getPointRadius',
45
+ wireframe: 'wireframe'
46
+ }
47
+ };
48
+
49
+ const RADIUS_DOWNSCALE = 0.2;
50
+
51
+ const defaultProps = {
52
+ lineMiterLimit: 2,
53
+ lineWidthUnits: 'pixels',
54
+ pointRadiusScale: RADIUS_DOWNSCALE,
55
+ pointRadiusUnits: 'pixels',
56
+ rounded: true,
57
+ wrapLongitude: false
58
+ };
59
+
60
+ function mergePropMaps(a, b) {
61
+ return {...a, ...b, visConfig: {...a.visConfig, ...b.visConfig}};
62
+ }
63
+
64
+ export const LAYER_MAP = {
65
+ point: {
66
+ Layer: GeoJsonLayer,
67
+ propMap: mergePropMaps(sharedPropMap, {
68
+ visConfig: {outline: 'stroked'}
69
+ }),
70
+ defaultProps
71
+ },
72
+ geojson: {
73
+ Layer: GeoJsonLayer,
74
+ propMap: sharedPropMap,
75
+ defaultProps: {...defaultProps, lineWidthScale: 2}
76
+ },
77
+ hexagonId: {
78
+ Layer: H3HexagonLayer,
79
+ propMap: mergePropMaps(sharedPropMap, {
80
+ visConfig: {coverage: 'coverage', elevationScale: 'elevationScale'}
81
+ }),
82
+ defaultProps: {...defaultProps, getHexagon: d => d.h3}
83
+ },
84
+ mvt: {
85
+ Layer: MVTLayer,
86
+ propMap: sharedPropMap,
87
+ defaultProps: {...defaultProps, pointRadiusScale: 0.3, lineWidthScale: 2}
88
+ }
89
+ };
90
+
91
+ function domainFromAttribute(attribute, scaleType) {
92
+ if (scaleType === 'ordinal' || scaleType === 'point') {
93
+ return attribute.categories.map(c => c.category).filter(c => c !== undefined && c !== null);
94
+ }
95
+
96
+ let {min} = attribute;
97
+ if (scaleType === 'log' && min === 0) {
98
+ min = 1e-5;
99
+ }
100
+ return [min, attribute.max];
101
+ }
102
+
103
+ function domainFromValues(values, scaleType) {
104
+ if (scaleType === 'ordinal') {
105
+ return [...new Set(values)].sort();
106
+ }
107
+ return extent(values);
108
+ }
109
+
110
+ function calculateDomain(data, name, scaleType) {
111
+ if (data.tilestats) {
112
+ // Tileset data type
113
+ const {attributes} = data.tilestats.layers[0];
114
+ const attribute = attributes.find(a => a.attribute === name);
115
+ return domainFromAttribute(attribute, scaleType);
116
+ } else if (data.features) {
117
+ // GeoJSON data type
118
+ const values = data.features.map(({properties}) => properties[name]);
119
+ return domainFromValues(values, scaleType);
120
+ } else if (Array.isArray(data) && data[0][name]) {
121
+ // JSON data type
122
+ const values = data.map(properties => properties[name]);
123
+ return domainFromValues(values, scaleType);
124
+ }
125
+
126
+ return [0, 1];
127
+ }
128
+
129
+ function normalizeAccessor(accessor, data) {
130
+ if (data.features || data.tilestats) {
131
+ return ({properties}) => {
132
+ return accessor(properties);
133
+ };
134
+ }
135
+ return accessor;
136
+ }
137
+
138
+ export function getColorAccessor({name}, scaleType, {colors}, opacity, data) {
139
+ const scale = SCALE_FUNCS[scaleType]();
140
+ scale.domain(calculateDomain(data, name, scaleType));
141
+ scale.range(colors);
142
+ const alpha = opacity !== undefined ? 255 * Math.pow(opacity, 1 / 2.2) : 255;
143
+
144
+ const accessor = properties => {
145
+ const rgba = rgb(scale(properties[name]));
146
+ return [rgba.r, rgba.g, rgba.b, alpha];
147
+ };
148
+ return normalizeAccessor(accessor, data);
149
+ }
150
+
151
+ export function getSizeAccessor({name}, scaleType, range, data) {
152
+ const scale = SCALE_FUNCS[scaleType]();
153
+ scale.domain(calculateDomain(data, name, scaleType));
154
+ scale.range(range);
155
+
156
+ const accessor = properties => {
157
+ return scale(properties[name]);
158
+ };
159
+ return normalizeAccessor(accessor, data);
160
+ }
161
+
162
+ const FORMATS = {
163
+ date: s => moment.utc(s).format('MM/DD/YY HH:mm:ssa'),
164
+ integer: d3Format('i'),
165
+ float: d3Format('.5f'),
166
+ timestamp: s => moment.utc(s).format('X'),
167
+ default: String
168
+ };
169
+
170
+ export function getTextAccessor({name, type}, data) {
171
+ const format = FORMATS[type] || FORMATS.default;
172
+ const accessor = properties => {
173
+ return format(properties[name]);
174
+ };
175
+ return normalizeAccessor(accessor, data);
176
+ }
177
+
178
+ export function getTextPixelOffsetAccessor({alignment, anchor, size}, radius) {
179
+ const padding = 20;
180
+ const signX = anchor === 'middle' ? 0 : anchor === 'start' ? 1 : -1;
181
+ const signY = alignment === 'center' ? 0 : alignment === 'bottom' ? 1 : -1;
182
+ const sizeOffset = alignment === 'center' ? 0 : size;
183
+
184
+ const calculateOffset = r => {
185
+ r = RADIUS_DOWNSCALE * r;
186
+ return [signX * (r + padding), signY * (r + padding + sizeOffset)];
187
+ };
188
+
189
+ return typeof radius === 'function'
190
+ ? d => {
191
+ return calculateOffset(radius(d));
192
+ }
193
+ : calculateOffset(radius);
194
+ }
@@ -7,6 +7,11 @@ export const API_VERSIONS = {
7
7
  V3: 'v3'
8
8
  };
9
9
 
10
+ export const DEFAULT_MAPS_URL_FORMAT = {
11
+ [API_VERSIONS.V1]: `https://${DEFAULT_USER_COMPONENT_IN_URL}.carto.com/api/v1/map`,
12
+ [API_VERSIONS.V2]: `https://maps-api-v2.${DEFAULT_REGION_COMPONENT_IN_URL}.carto.com/user/${DEFAULT_USER_COMPONENT_IN_URL}`
13
+ };
14
+
10
15
  export const MAP_TYPES = {
11
16
  QUERY: 'query',
12
17
  TABLE: 'table',
@@ -1,9 +1,10 @@
1
1
  /**
2
2
  * Maps API Client for Maps API v1 and Maps API v2
3
3
  */
4
- import {getDefaultCredentials} from '../config';
4
+ import {defaultClassicCredentials, getDefaultCredentials} from '../config';
5
5
  import {
6
6
  API_VERSIONS,
7
+ DEFAULT_MAPS_URL_FORMAT,
7
8
  DEFAULT_REGION_COMPONENT_IN_URL,
8
9
  DEFAULT_USER_COMPONENT_IN_URL,
9
10
  encodeParameter,
@@ -22,8 +23,20 @@ const TILE_EXTENT = 4096;
22
23
  * Obtain a TileJson from Maps API v1 and v2
23
24
  */
24
25
  export async function getDataV2({type, source, credentials}) {
25
- const localCreds = {...getDefaultCredentials(), ...credentials};
26
- const {apiVersion} = localCreds;
26
+ const defaultCredentials = getDefaultCredentials();
27
+ const apiVersion = (credentials && credentials.apiVersion) || defaultCredentials.apiVersion;
28
+ // Only pick up default credentials if they have been defined for
29
+ // correct API version
30
+ const localCreds = {
31
+ ...defaultClassicCredentials,
32
+ ...(defaultCredentials.apiVersion === apiVersion && defaultCredentials),
33
+ ...credentials
34
+ };
35
+
36
+ if (!localCreds.mapsUrl) {
37
+ localCreds.mapsUrl = DEFAULT_MAPS_URL_FORMAT[apiVersion];
38
+ }
39
+
27
40
  let url;
28
41
 
29
42
  const connection = type === 'tileset' ? CONNECTIONS.BIGQUERY : CONNECTIONS.CARTO;
@@ -3,6 +3,7 @@
3
3
  */
4
4
  import {getDefaultCredentials, buildMapsUrlFromBase} from '../config';
5
5
  import {API_VERSIONS, encodeParameter, FORMATS, MAP_TYPES} from './maps-api-common';
6
+ import {parseMap} from './parseMap';
6
7
  import {log} from '@deck.gl/core';
7
8
 
8
9
  const MAX_GET_LENGTH = 2048;
@@ -68,9 +69,11 @@ function dealWithError({response, error}) {
68
69
  /**
69
70
  * Build a URL with all required parameters
70
71
  */
71
- function getParameters({type, source, geoColumn, columns}) {
72
- const encodedClient = encodeParameter('client', 'deck-gl-carto');
73
- const parameters = [encodedClient];
72
+ function getParameters({type, source, geoColumn, columns, schema}) {
73
+ const parameters = [encodeParameter('client', 'deck-gl-carto')];
74
+ if (schema) {
75
+ parameters.push(encodeParameter('schema', true));
76
+ }
74
77
 
75
78
  const sourceName = type === MAP_TYPES.QUERY ? 'q' : 'name';
76
79
  parameters.push(encodeParameter(sourceName, source));
@@ -93,10 +96,11 @@ export async function mapInstantiation({
93
96
  connection,
94
97
  credentials,
95
98
  geoColumn,
96
- columns
99
+ columns,
100
+ schema
97
101
  }) {
98
102
  const baseUrl = `${credentials.mapsUrl}/${connection}/${type}`;
99
- const url = `${baseUrl}?${getParameters({type, source, geoColumn, columns})}`;
103
+ const url = `${baseUrl}?${getParameters({type, source, geoColumn, columns, schema})}`;
100
104
  const {accessToken} = credentials;
101
105
 
102
106
  const format = 'json';
@@ -123,9 +127,7 @@ function getUrlFromMetadata(metadata, format) {
123
127
  return null;
124
128
  }
125
129
 
126
- export async function getData({type, source, connection, credentials, geoColumn, columns, format}) {
127
- const localCreds = {...getDefaultCredentials(), ...credentials};
128
-
130
+ function checkFetchLayerDataParameters({type, source, connection, localCreds}) {
129
131
  log.assert(connection, 'Must define connection');
130
132
  log.assert(type, 'Must define a type');
131
133
  log.assert(source, 'Must define a source');
@@ -133,7 +135,58 @@ export async function getData({type, source, connection, credentials, geoColumn,
133
135
  log.assert(localCreds.apiVersion === API_VERSIONS.V3, 'Method only available for v3');
134
136
  log.assert(localCreds.apiBaseUrl, 'Must define apiBaseUrl');
135
137
  log.assert(localCreds.accessToken, 'Must define an accessToken');
136
- log.assert(localCreds.mapsUrl, 'mapsUrl cannot be undefined');
138
+ }
139
+
140
+ export async function fetchLayerData({
141
+ type,
142
+ source,
143
+ connection,
144
+ credentials,
145
+ geoColumn,
146
+ columns,
147
+ format,
148
+ schema
149
+ }) {
150
+ // Internally we split data fetching into two parts to allow us to
151
+ // conditionally fetch the actual data, depending on the metadata state
152
+ const {url, accessToken, mapFormat, metadata} = await _fetchDataUrl({
153
+ type,
154
+ source,
155
+ connection,
156
+ credentials,
157
+ geoColumn,
158
+ columns,
159
+ format,
160
+ schema
161
+ });
162
+
163
+ const data = await request({url, format: mapFormat, accessToken});
164
+ const result = {data, format: mapFormat};
165
+ if (schema) {
166
+ result.schema = metadata.schema;
167
+ }
168
+
169
+ return result;
170
+ }
171
+
172
+ async function _fetchDataUrl({
173
+ type,
174
+ source,
175
+ connection,
176
+ credentials,
177
+ geoColumn,
178
+ columns,
179
+ format,
180
+ schema
181
+ }) {
182
+ const defaultCredentials = getDefaultCredentials();
183
+ // Only pick up default credentials if they have been defined for
184
+ // correct API version
185
+ const localCreds = {
186
+ ...(defaultCredentials.apiVersion === API_VERSIONS.V3 && defaultCredentials),
187
+ ...credentials
188
+ };
189
+ checkFetchLayerDataParameters({type, source, connection, localCreds});
137
190
 
138
191
  if (!localCreds.mapsUrl) {
139
192
  localCreds.mapsUrl = buildMapsUrlFromBase(localCreds.apiBaseUrl);
@@ -145,7 +198,8 @@ export async function getData({type, source, connection, credentials, geoColumn,
145
198
  connection,
146
199
  credentials: localCreds,
147
200
  geoColumn,
148
- columns
201
+ columns,
202
+ schema
149
203
  });
150
204
  let url;
151
205
  let mapFormat;
@@ -156,7 +210,7 @@ export async function getData({type, source, connection, credentials, geoColumn,
156
210
  log.assert(url, `Format ${format} not available`);
157
211
  } else {
158
212
  // guess map format
159
- const prioritizedFormats = [FORMATS.GEOJSON, FORMATS.NDJSON, FORMATS.TILEJSON];
213
+ const prioritizedFormats = [FORMATS.GEOJSON, FORMATS.JSON, FORMATS.NDJSON, FORMATS.TILEJSON];
160
214
  for (const f of prioritizedFormats) {
161
215
  url = getUrlFromMetadata(metadata, f);
162
216
  if (url) {
@@ -167,6 +221,85 @@ export async function getData({type, source, connection, credentials, geoColumn,
167
221
  }
168
222
 
169
223
  const {accessToken} = localCreds;
224
+ return {url, accessToken, mapFormat, metadata};
225
+ }
226
+
227
+ export async function getData({type, source, connection, credentials, geoColumn, columns, format}) {
228
+ log.deprecated('getData', 'fetchLayerData')();
229
+ const layerData = await fetchLayerData({
230
+ type,
231
+ source,
232
+ connection,
233
+ credentials,
234
+ geoColumn,
235
+ columns,
236
+ format,
237
+ schema: false
238
+ });
239
+ return layerData.data;
240
+ }
241
+
242
+ /* global setInterval, URLSearchParams */
243
+ async function _fetchMapDataset(dataset, accessToken) {
244
+ // First fetch metadata
245
+ const {connectionName: connection, source, type} = dataset;
246
+ const {url, mapFormat} = await _fetchDataUrl({
247
+ credentials: {accessToken},
248
+ connection,
249
+ source,
250
+ type
251
+ });
252
+
253
+ // Extract the last time the data changed
254
+ const cache = parseInt(new URLSearchParams(url).get('cache'), 10);
255
+ if (cache && dataset.cache === cache) {
256
+ return false;
257
+ }
258
+ dataset.cache = cache;
259
+
260
+ // Only fetch if the data has changed
261
+ const data = await request({url, format: mapFormat, accessToken});
262
+ dataset.data = data;
263
+ return true;
264
+ }
265
+
266
+ async function fillInMapDatasets({datasets, publicToken}) {
267
+ const promises = datasets.map(dataset => _fetchMapDataset(dataset, publicToken));
268
+ return await Promise.all(promises);
269
+ }
270
+
271
+ export async function fetchMap({mapId, credentials, autoRefresh, onNewData}) {
272
+ const localCreds = {...getDefaultCredentials(), ...credentials};
273
+
274
+ log.assert(mapId, 'Must define map id');
275
+
276
+ log.assert(localCreds.apiVersion === API_VERSIONS.V3, 'Method only available for v3');
277
+ log.assert(localCreds.apiBaseUrl, 'Must define apiBaseUrl');
278
+ if (!localCreds.mapsUrl) {
279
+ localCreds.mapsUrl = buildMapsUrlFromBase(localCreds.apiBaseUrl);
280
+ }
281
+
282
+ if (autoRefresh || onNewData) {
283
+ log.assert(onNewData, 'Must define `onNewData` when using autoRefresh');
284
+ log.assert(typeof onNewData === 'function', '`onNewData` must be a function');
285
+ log.assert(autoRefresh > 0, '`autoRefresh` must be a positive number');
286
+ }
287
+
288
+ const url = `${localCreds.mapsUrl}/public/${mapId}`;
289
+ const map = await request({url});
290
+
291
+ // Periodically check if the data has changed. Note that this
292
+ // will not update when a map is published.
293
+ if (autoRefresh) {
294
+ setInterval(async () => {
295
+ const changed = await fillInMapDatasets(map);
296
+ if (changed.some(v => v === true)) {
297
+ onNewData(parseMap(map));
298
+ }
299
+ }, autoRefresh * 1000);
300
+ }
170
301
 
171
- return await request({url, format: mapFormat, accessToken});
302
+ // Mutates map.datasets so that dataset.data contains data
303
+ await fillInMapDatasets(map);
304
+ return parseMap(map);
172
305
  }
@@ -0,0 +1,192 @@
1
+ import GL from '@luma.gl/constants';
2
+ import {
3
+ LAYER_MAP,
4
+ getColorAccessor,
5
+ getSizeAccessor,
6
+ getTextAccessor,
7
+ getTextPixelOffsetAccessor
8
+ } from './layer-map';
9
+ import {_flatten as flatten, log} from '@deck.gl/core';
10
+
11
+ export function parseMap(json) {
12
+ const {keplerMapConfig, datasets} = json;
13
+ log.assert(keplerMapConfig.version === 'v1', 'Only support Kepler v1');
14
+ const {mapState, mapStyle} = keplerMapConfig.config;
15
+ const {layers, layerBlending, interactionConfig} = keplerMapConfig.config.visState;
16
+
17
+ return {
18
+ id: json.id,
19
+ title: json.title,
20
+ description: json.description,
21
+ createdAt: json.createdAt,
22
+ updatedAt: json.updatedAt,
23
+ mapState,
24
+ mapStyle,
25
+ layers: extractTextLayers(layers.reverse()).map(({id, type, config, visualChannels}) => {
26
+ log.assert(type in LAYER_MAP, `Unsupported layer type: ${type}`);
27
+ const {Layer, propMap, defaultProps} = LAYER_MAP[type];
28
+
29
+ const {dataId} = config;
30
+ const dataset = datasets.find(d => d.id === dataId);
31
+ log.assert(dataset, `No dataset matching dataId: ${dataId}`);
32
+ const {data} = dataset;
33
+ log.assert(data, `No data loaded for dataId: ${dataId}`);
34
+ return new Layer({
35
+ id,
36
+ data,
37
+ ...defaultProps,
38
+ ...createBlendingProps(layerBlending),
39
+ ...(!config.textLabel && createInteractionProps(interactionConfig)),
40
+ ...createStyleProps(config, propMap),
41
+ ...createChannelProps(visualChannels, type, config, data) // Must come after style
42
+ });
43
+ })
44
+ };
45
+ }
46
+
47
+ function extractTextLayers(layers) {
48
+ return flatten(
49
+ layers.map(({id, config, ...rest}) => {
50
+ const {textLabel, ...configRest} = config;
51
+ return [
52
+ // Original layer without textLabel
53
+ {id, config: configRest, ...rest},
54
+
55
+ // One layer per valid text label, with full opacity
56
+ ...textLabel.filter(t => t.field).map(t => {
57
+ return {
58
+ id: `id-label-${t.field.name}`,
59
+ config: {
60
+ textLabel: t,
61
+ ...configRest,
62
+ visConfig: {...configRest.visConfig, opacity: 1}
63
+ },
64
+ ...rest
65
+ };
66
+ })
67
+ ];
68
+ })
69
+ );
70
+ }
71
+
72
+ function createBlendingProps(layerBlending) {
73
+ if (layerBlending === 'additive') {
74
+ return {
75
+ parameters: {
76
+ blendFunc: [GL.SRC_ALPHA, GL.DST_ALPHA],
77
+ blendEquation: GL.FUNC_ADD
78
+ }
79
+ };
80
+ } else if (layerBlending === 'subtractive') {
81
+ return {
82
+ parameters: {
83
+ blendFunc: [GL.ONE, GL.ONE_MINUS_DST_COLOR, GL.SRC_ALPHA, GL.DST_ALPHA],
84
+ blendEquation: [GL.FUNC_SUBTRACT, GL.FUNC_ADD]
85
+ }
86
+ };
87
+ }
88
+
89
+ return {};
90
+ }
91
+
92
+ function createInteractionProps(interactionConfig) {
93
+ const pickable = interactionConfig && interactionConfig.tooltip.enabled;
94
+ return {
95
+ autoHighlight: pickable,
96
+ pickable
97
+ };
98
+ }
99
+
100
+ function mapProps(source, target, mapping) {
101
+ if (Array.isArray(source)) {
102
+ source = source[0];
103
+ }
104
+ if (!source) {
105
+ return;
106
+ }
107
+ for (const sourceKey in mapping) {
108
+ const sourceValue = source[sourceKey];
109
+ const targetKey = mapping[sourceKey];
110
+ if (sourceValue === undefined) {
111
+ // eslint-disable-next-line no-continue
112
+ continue;
113
+ }
114
+ if (typeof targetKey === 'string') {
115
+ target[targetKey] = sourceValue;
116
+ } else if (typeof targetKey === 'object') {
117
+ // Nested definition, recurse down one level
118
+ mapProps(sourceValue, target, targetKey);
119
+ }
120
+ }
121
+ }
122
+
123
+ function createStyleProps(config, mapping) {
124
+ const result = {};
125
+ mapProps(config, result, mapping);
126
+ result.highlightColor = config.visConfig.enable3d ? [255, 255, 255, 60] : [252, 242, 26, 255];
127
+ return result;
128
+ }
129
+
130
+ /* eslint-disable complexity */
131
+ function createChannelProps(visualChannels, type, config, data) {
132
+ const {
133
+ colorField,
134
+ colorScale,
135
+ sizeField,
136
+ sizeScale,
137
+ strokeColorField,
138
+ strokeColorScale
139
+ } = visualChannels;
140
+ let {heightField, heightScale} = visualChannels;
141
+ if (type === 'hexagonId') {
142
+ heightField = sizeField;
143
+ heightScale = sizeScale;
144
+ }
145
+ const {textLabel, visConfig} = config;
146
+ const result = {};
147
+ const textLabelField = textLabel && textLabel.field;
148
+ if (colorField) {
149
+ result.getFillColor = getColorAccessor(
150
+ colorField,
151
+ colorScale,
152
+ visConfig.colorRange,
153
+ 1, // Rely on layer opacity
154
+ data
155
+ );
156
+ }
157
+ if (strokeColorField) {
158
+ const opacity =
159
+ visConfig.strokeOpacity !== undefined ? visConfig.strokeOpacity / visConfig.opacity : 1;
160
+ result.getLineColor = getColorAccessor(
161
+ strokeColorField,
162
+ strokeColorScale,
163
+ visConfig.colorRange,
164
+ opacity,
165
+ data
166
+ );
167
+ }
168
+ if (heightField) {
169
+ result.getElevation = getSizeAccessor(
170
+ heightField,
171
+ heightScale,
172
+ visConfig.heightRange || visConfig.sizeRange,
173
+ data
174
+ );
175
+ }
176
+ if (sizeField) {
177
+ result.getPointRadius = getSizeAccessor(
178
+ sizeField,
179
+ sizeScale,
180
+ visConfig.radiusRange || visConfig.sizeRange,
181
+ data
182
+ );
183
+ }
184
+ if (textLabelField) {
185
+ result.getText = getTextAccessor(textLabelField, data);
186
+ result.pointType = 'text';
187
+ const radius = result.getPointRadius || visConfig.radius;
188
+ result.getTextPixelOffset = getTextPixelOffsetAccessor(textLabel, radius);
189
+ }
190
+
191
+ return result;
192
+ }
package/src/config.js CHANGED
@@ -1,6 +1,6 @@
1
- import {API_VERSIONS} from './api/maps-api-common';
1
+ import {API_VERSIONS, DEFAULT_MAPS_URL_FORMAT} from './api/maps-api-common';
2
2
 
3
- const defaultClassicCredentials = {
3
+ export const defaultClassicCredentials = {
4
4
  username: 'public',
5
5
  apiKey: 'default_public',
6
6
  region: 'us',
@@ -22,7 +22,7 @@ export function setDefaultCredentials(opts) {
22
22
 
23
23
  switch (apiVersion) {
24
24
  case API_VERSIONS.V1:
25
- opts.mapsUrl = opts.mapsUrl || 'https://{user}.carto.com/api/v1/map';
25
+ opts.mapsUrl = opts.mapsUrl || DEFAULT_MAPS_URL_FORMAT[apiVersion];
26
26
  credentials = {
27
27
  apiVersion,
28
28
  ...defaultClassicCredentials,
@@ -31,7 +31,7 @@ export function setDefaultCredentials(opts) {
31
31
  break;
32
32
 
33
33
  case API_VERSIONS.V2:
34
- opts.mapsUrl = opts.mapsUrl || 'https://maps-api-v2.{region}.carto.com/user/{user}';
34
+ opts.mapsUrl = opts.mapsUrl || DEFAULT_MAPS_URL_FORMAT[apiVersion];
35
35
  credentials = {
36
36
  apiVersion,
37
37
  ...defaultClassicCredentials,