@deck.gl/carto 8.6.3 → 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.
- package/dist/dist.dev.js +24388 -720
- package/dist/es5/api/index.js +14 -8
- package/dist/es5/api/index.js.map +1 -1
- package/dist/es5/api/layer-map.js +255 -0
- package/dist/es5/api/layer-map.js.map +1 -0
- package/dist/es5/api/maps-api-common.js +13 -9
- package/dist/es5/api/maps-api-common.js.map +1 -1
- package/dist/es5/api/maps-client.js +157 -98
- package/dist/es5/api/maps-client.js.map +1 -1
- package/dist/es5/api/maps-v3-client.js +535 -190
- package/dist/es5/api/maps-v3-client.js.map +1 -1
- package/dist/es5/api/parseMap.js +211 -0
- package/dist/es5/api/parseMap.js.map +1 -0
- package/dist/es5/basemap.js +1 -1
- package/dist/es5/basemap.js.map +1 -1
- package/dist/es5/config.js +23 -21
- package/dist/es5/config.js.map +1 -1
- package/dist/es5/index.js +22 -16
- package/dist/es5/index.js.map +1 -1
- package/dist/es5/layers/carto-bqtiler-layer.js +39 -6
- package/dist/es5/layers/carto-bqtiler-layer.js.map +1 -1
- package/dist/es5/layers/carto-layer.js +211 -132
- package/dist/es5/layers/carto-layer.js.map +1 -1
- package/dist/es5/layers/carto-sql-layer.js +39 -6
- package/dist/es5/layers/carto-sql-layer.js.map +1 -1
- package/dist/es5/style/color-bins-style.js +11 -10
- package/dist/es5/style/color-bins-style.js.map +1 -1
- package/dist/es5/style/color-categories-style.js +38 -13
- package/dist/es5/style/color-categories-style.js.map +1 -1
- package/dist/es5/style/color-continuous-style.js +11 -10
- package/dist/es5/style/color-continuous-style.js.map +1 -1
- package/dist/es5/style/palette.js +19 -11
- package/dist/es5/style/palette.js.map +1 -1
- package/dist/es5/style/utils.js +13 -6
- package/dist/es5/style/utils.js.map +1 -1
- package/dist/esm/api/index.js +1 -1
- package/dist/esm/api/index.js.map +1 -1
- package/dist/esm/api/layer-map.js +219 -0
- package/dist/esm/api/layer-map.js.map +1 -0
- package/dist/esm/api/maps-v3-client.js +135 -16
- package/dist/esm/api/maps-v3-client.js.map +1 -1
- package/dist/esm/api/parseMap.js +202 -0
- package/dist/esm/api/parseMap.js.map +1 -0
- package/dist/esm/index.js +1 -1
- package/dist/esm/index.js.map +1 -1
- package/dist.min.js +278 -1
- package/package.json +7 -3
- package/src/api/index.js +1 -1
- package/src/api/layer-map.js +194 -0
- package/src/api/maps-v3-client.js +103 -11
- package/src/api/parseMap.js +192 -0
- package/src/index.js +1 -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.
|
|
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-
|
|
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": "
|
|
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 {fetchLayerData, 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
|
+
}
|
|
@@ -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;
|
|
@@ -126,7 +127,7 @@ function getUrlFromMetadata(metadata, format) {
|
|
|
126
127
|
return null;
|
|
127
128
|
}
|
|
128
129
|
|
|
129
|
-
function
|
|
130
|
+
function checkFetchLayerDataParameters({type, source, connection, localCreds}) {
|
|
130
131
|
log.assert(connection, 'Must define connection');
|
|
131
132
|
log.assert(type, 'Must define a type');
|
|
132
133
|
log.assert(source, 'Must define a source');
|
|
@@ -145,6 +146,38 @@ export async function fetchLayerData({
|
|
|
145
146
|
columns,
|
|
146
147
|
format,
|
|
147
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
|
|
148
181
|
}) {
|
|
149
182
|
const defaultCredentials = getDefaultCredentials();
|
|
150
183
|
// Only pick up default credentials if they have been defined for
|
|
@@ -153,7 +186,7 @@ export async function fetchLayerData({
|
|
|
153
186
|
...(defaultCredentials.apiVersion === API_VERSIONS.V3 && defaultCredentials),
|
|
154
187
|
...credentials
|
|
155
188
|
};
|
|
156
|
-
|
|
189
|
+
checkFetchLayerDataParameters({type, source, connection, localCreds});
|
|
157
190
|
|
|
158
191
|
if (!localCreds.mapsUrl) {
|
|
159
192
|
localCreds.mapsUrl = buildMapsUrlFromBase(localCreds.apiBaseUrl);
|
|
@@ -177,7 +210,7 @@ export async function fetchLayerData({
|
|
|
177
210
|
log.assert(url, `Format ${format} not available`);
|
|
178
211
|
} else {
|
|
179
212
|
// guess map format
|
|
180
|
-
const prioritizedFormats = [FORMATS.GEOJSON, FORMATS.NDJSON, FORMATS.TILEJSON];
|
|
213
|
+
const prioritizedFormats = [FORMATS.GEOJSON, FORMATS.JSON, FORMATS.NDJSON, FORMATS.TILEJSON];
|
|
181
214
|
for (const f of prioritizedFormats) {
|
|
182
215
|
url = getUrlFromMetadata(metadata, f);
|
|
183
216
|
if (url) {
|
|
@@ -188,17 +221,11 @@ export async function fetchLayerData({
|
|
|
188
221
|
}
|
|
189
222
|
|
|
190
223
|
const {accessToken} = localCreds;
|
|
191
|
-
|
|
192
|
-
const data = await request({url, format: mapFormat, accessToken});
|
|
193
|
-
const result = {data, format: mapFormat};
|
|
194
|
-
if (schema) {
|
|
195
|
-
result.schema = metadata.schema;
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
return result;
|
|
224
|
+
return {url, accessToken, mapFormat, metadata};
|
|
199
225
|
}
|
|
200
226
|
|
|
201
227
|
export async function getData({type, source, connection, credentials, geoColumn, columns, format}) {
|
|
228
|
+
log.deprecated('getData', 'fetchLayerData')();
|
|
202
229
|
const layerData = await fetchLayerData({
|
|
203
230
|
type,
|
|
204
231
|
source,
|
|
@@ -211,3 +238,68 @@ export async function getData({type, source, connection, credentials, geoColumn,
|
|
|
211
238
|
});
|
|
212
239
|
return layerData.data;
|
|
213
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
|
+
}
|
|
301
|
+
|
|
302
|
+
// Mutates map.datasets so that dataset.data contains data
|
|
303
|
+
await fillInMapDatasets(map);
|
|
304
|
+
return parseMap(map);
|
|
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
|
+
}
|