@deck.gl/carto 8.6.2 → 8.7.0-alpha.2
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 +24009 -380
- package/dist/es5/api/index.js +14 -8
- package/dist/es5/api/index.js.map +1 -1
- package/dist/es5/api/layer-map.js +244 -0
- package/dist/es5/api/layer-map.js.map +1 -0
- package/dist/es5/api/maps-api-common.js +9 -13
- package/dist/es5/api/maps-api-common.js.map +1 -1
- package/dist/es5/api/maps-client.js +98 -157
- package/dist/es5/api/maps-client.js.map +1 -1
- package/dist/es5/api/maps-v3-client.js +325 -304
- package/dist/es5/api/maps-v3-client.js.map +1 -1
- package/dist/es5/api/parseMap.js +210 -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 -39
- 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 +6 -39
- package/dist/es5/layers/carto-bqtiler-layer.js.map +1 -1
- package/dist/es5/layers/carto-layer.js +130 -211
- package/dist/es5/layers/carto-layer.js.map +1 -1
- package/dist/es5/layers/carto-sql-layer.js +6 -39
- package/dist/es5/layers/carto-sql-layer.js.map +1 -1
- package/dist/es5/style/color-bins-style.js +14 -13
- package/dist/es5/style/color-bins-style.js.map +1 -1
- package/dist/es5/style/color-categories-style.js +16 -39
- package/dist/es5/style/color-categories-style.js.map +1 -1
- package/dist/es5/style/color-continuous-style.js +14 -13
- package/dist/es5/style/color-continuous-style.js.map +1 -1
- package/dist/es5/style/palette.js +15 -21
- package/dist/es5/style/palette.js.map +1 -1
- package/dist/es5/style/utils.js +6 -13
- 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-client.js +1 -1
- package/dist/esm/api/maps-client.js.map +1 -1
- package/dist/esm/api/maps-v3-client.js +145 -16
- package/dist/esm/api/maps-v3-client.js.map +1 -1
- package/dist/esm/api/parseMap.js +194 -0
- package/dist/esm/api/parseMap.js.map +1 -0
- package/dist/esm/config.js +11 -26
- package/dist/esm/config.js.map +1 -1
- package/dist/esm/index.js +1 -1
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/layers/carto-layer.js +7 -9
- package/dist/esm/layers/carto-layer.js.map +1 -1
- package/dist/esm/style/color-bins-style.js.map +1 -1
- package/dist/esm/style/color-continuous-style.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-client.js +3 -7
- package/src/api/maps-v3-client.js +114 -11
- package/src/api/parseMap.js +182 -0
- package/src/config.js +8 -39
- package/src/index.js +1 -0
- package/src/layers/carto-layer.js +3 -5
- package/src/style/color-bins-style.js +1 -3
- package/src/style/color-continuous-style.js +1 -3
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.2",
|
|
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": "67bc5fe69149ae933a5f203724ad58f58c4a1737"
|
|
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] !== undefined) {
|
|
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 ? Math.round(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
|
+
}
|
package/src/api/maps-client.js
CHANGED
|
@@ -56,7 +56,7 @@ export async function getDataV2({type, source, credentials}) {
|
|
|
56
56
|
|
|
57
57
|
default:
|
|
58
58
|
throw new Error(
|
|
59
|
-
`Invalid maps API version. It
|
|
59
|
+
`Invalid maps API version. It should be ${API_VERSIONS.V1} or ${API_VERSIONS.V2}`
|
|
60
60
|
);
|
|
61
61
|
}
|
|
62
62
|
}
|
|
@@ -95,15 +95,11 @@ function dealWithError({response, json, credentials}) {
|
|
|
95
95
|
switch (response.status) {
|
|
96
96
|
case 401:
|
|
97
97
|
throw new Error(
|
|
98
|
-
`Unauthorized access to Maps API: invalid combination of user ('${
|
|
99
|
-
credentials.username
|
|
100
|
-
}') and apiKey ('${credentials.apiKey}')`
|
|
98
|
+
`Unauthorized access to Maps API: invalid combination of user ('${credentials.username}') and apiKey ('${credentials.apiKey}')`
|
|
101
99
|
);
|
|
102
100
|
case 403:
|
|
103
101
|
throw new Error(
|
|
104
|
-
`Unauthorized access to dataset: the provided apiKey('${
|
|
105
|
-
credentials.apiKey
|
|
106
|
-
}') doesn't provide access to the requested data`
|
|
102
|
+
`Unauthorized access to dataset: the provided apiKey('${credentials.apiKey}') doesn't provide access to the requested data`
|
|
107
103
|
);
|
|
108
104
|
|
|
109
105
|
default:
|
|
@@ -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,79 @@ export async function getData({type, source, connection, credentials, geoColumn,
|
|
|
211
238
|
});
|
|
212
239
|
return layerData.data;
|
|
213
240
|
}
|
|
241
|
+
|
|
242
|
+
/* global clearInterval, 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({cartoMapId, credentials, autoRefresh, onNewData}) {
|
|
272
|
+
const defaultCredentials = getDefaultCredentials();
|
|
273
|
+
const localCreds = {
|
|
274
|
+
...(defaultCredentials.apiVersion === API_VERSIONS.V3 && defaultCredentials),
|
|
275
|
+
...credentials
|
|
276
|
+
};
|
|
277
|
+
|
|
278
|
+
log.assert(cartoMapId, `Must define CARTO map id: fetchMap({cartoMapId: 'XXXX-XXXX-XXXX'})`);
|
|
279
|
+
|
|
280
|
+
log.assert(localCreds.apiVersion === API_VERSIONS.V3, 'Method only available for v3');
|
|
281
|
+
log.assert(localCreds.apiBaseUrl, 'Must define apiBaseUrl');
|
|
282
|
+
if (!localCreds.mapsUrl) {
|
|
283
|
+
localCreds.mapsUrl = buildMapsUrlFromBase(localCreds.apiBaseUrl);
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
if (autoRefresh || onNewData) {
|
|
287
|
+
log.assert(onNewData, 'Must define `onNewData` when using autoRefresh');
|
|
288
|
+
log.assert(typeof onNewData === 'function', '`onNewData` must be a function');
|
|
289
|
+
log.assert(autoRefresh > 0, '`autoRefresh` must be a positive number');
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
const url = `${localCreds.mapsUrl}/public/${cartoMapId}`;
|
|
293
|
+
const map = await request({url});
|
|
294
|
+
|
|
295
|
+
// Periodically check if the data has changed. Note that this
|
|
296
|
+
// will not update when a map is published.
|
|
297
|
+
let stopAutoRefresh;
|
|
298
|
+
if (autoRefresh) {
|
|
299
|
+
const intervalId = setInterval(async () => {
|
|
300
|
+
const changed = await fillInMapDatasets(map);
|
|
301
|
+
if (changed.some(v => v === true)) {
|
|
302
|
+
onNewData(parseMap(map));
|
|
303
|
+
}
|
|
304
|
+
}, autoRefresh * 1000);
|
|
305
|
+
stopAutoRefresh = () => {
|
|
306
|
+
clearInterval(intervalId);
|
|
307
|
+
};
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
// Mutates map.datasets so that dataset.data contains data
|
|
311
|
+
await fillInMapDatasets(map);
|
|
312
|
+
return {
|
|
313
|
+
...parseMap(map),
|
|
314
|
+
...{stopAutoRefresh}
|
|
315
|
+
};
|
|
316
|
+
}
|
|
@@ -0,0 +1,182 @@
|
|
|
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
|
+
initialViewState: 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
|
|
57
|
+
.filter(t => t.field)
|
|
58
|
+
.map(t => {
|
|
59
|
+
return {
|
|
60
|
+
id: `${id}-label-${t.field.name}`,
|
|
61
|
+
config: {
|
|
62
|
+
textLabel: t,
|
|
63
|
+
...configRest,
|
|
64
|
+
visConfig: {...configRest.visConfig, opacity: 1}
|
|
65
|
+
},
|
|
66
|
+
...rest
|
|
67
|
+
};
|
|
68
|
+
})
|
|
69
|
+
];
|
|
70
|
+
})
|
|
71
|
+
);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function createBlendingProps(layerBlending) {
|
|
75
|
+
if (layerBlending === 'additive') {
|
|
76
|
+
return {
|
|
77
|
+
parameters: {
|
|
78
|
+
blendFunc: [GL.SRC_ALPHA, GL.DST_ALPHA],
|
|
79
|
+
blendEquation: GL.FUNC_ADD
|
|
80
|
+
}
|
|
81
|
+
};
|
|
82
|
+
} else if (layerBlending === 'subtractive') {
|
|
83
|
+
return {
|
|
84
|
+
parameters: {
|
|
85
|
+
blendFunc: [GL.ONE, GL.ONE_MINUS_DST_COLOR, GL.SRC_ALPHA, GL.DST_ALPHA],
|
|
86
|
+
blendEquation: [GL.FUNC_SUBTRACT, GL.FUNC_ADD]
|
|
87
|
+
}
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
return {};
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function createInteractionProps(interactionConfig) {
|
|
95
|
+
const pickable = interactionConfig && interactionConfig.tooltip.enabled;
|
|
96
|
+
return {
|
|
97
|
+
autoHighlight: pickable,
|
|
98
|
+
pickable
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function mapProps(source, target, mapping) {
|
|
103
|
+
for (const sourceKey in mapping) {
|
|
104
|
+
const sourceValue = source[sourceKey];
|
|
105
|
+
const targetKey = mapping[sourceKey];
|
|
106
|
+
if (sourceValue === undefined) {
|
|
107
|
+
// eslint-disable-next-line no-continue
|
|
108
|
+
continue;
|
|
109
|
+
}
|
|
110
|
+
if (typeof targetKey === 'string') {
|
|
111
|
+
target[targetKey] = sourceValue;
|
|
112
|
+
} else if (typeof targetKey === 'object') {
|
|
113
|
+
// Nested definition, recurse down one level
|
|
114
|
+
mapProps(sourceValue, target, targetKey);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function createStyleProps(config, mapping) {
|
|
120
|
+
const result = {};
|
|
121
|
+
mapProps(config, result, mapping);
|
|
122
|
+
result.highlightColor = config.visConfig.enable3d ? [255, 255, 255, 60] : [252, 242, 26, 255];
|
|
123
|
+
return result;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/* eslint-disable complexity */
|
|
127
|
+
function createChannelProps(visualChannels, type, config, data) {
|
|
128
|
+
const {colorField, colorScale, sizeField, sizeScale, strokeColorField, strokeColorScale} =
|
|
129
|
+
visualChannels;
|
|
130
|
+
let {heightField, heightScale} = visualChannels;
|
|
131
|
+
if (type === 'hexagonId') {
|
|
132
|
+
heightField = sizeField;
|
|
133
|
+
heightScale = sizeScale;
|
|
134
|
+
}
|
|
135
|
+
const {textLabel, visConfig} = config;
|
|
136
|
+
const result = {};
|
|
137
|
+
const textLabelField = textLabel && textLabel.field;
|
|
138
|
+
if (colorField) {
|
|
139
|
+
result.getFillColor = getColorAccessor(
|
|
140
|
+
colorField,
|
|
141
|
+
colorScale,
|
|
142
|
+
visConfig.colorRange,
|
|
143
|
+
1, // Rely on layer opacity
|
|
144
|
+
data
|
|
145
|
+
);
|
|
146
|
+
}
|
|
147
|
+
if (strokeColorField) {
|
|
148
|
+
const opacity =
|
|
149
|
+
visConfig.strokeOpacity !== undefined ? visConfig.strokeOpacity / visConfig.opacity : 1;
|
|
150
|
+
result.getLineColor = getColorAccessor(
|
|
151
|
+
strokeColorField,
|
|
152
|
+
strokeColorScale,
|
|
153
|
+
visConfig.colorRange,
|
|
154
|
+
opacity,
|
|
155
|
+
data
|
|
156
|
+
);
|
|
157
|
+
}
|
|
158
|
+
if (heightField) {
|
|
159
|
+
result.getElevation = getSizeAccessor(
|
|
160
|
+
heightField,
|
|
161
|
+
heightScale,
|
|
162
|
+
visConfig.heightRange || visConfig.sizeRange,
|
|
163
|
+
data
|
|
164
|
+
);
|
|
165
|
+
}
|
|
166
|
+
if (sizeField) {
|
|
167
|
+
result.getPointRadius = getSizeAccessor(
|
|
168
|
+
sizeField,
|
|
169
|
+
sizeScale,
|
|
170
|
+
visConfig.radiusRange || visConfig.sizeRange,
|
|
171
|
+
data
|
|
172
|
+
);
|
|
173
|
+
}
|
|
174
|
+
if (textLabelField) {
|
|
175
|
+
result.getText = getTextAccessor(textLabelField, data);
|
|
176
|
+
result.pointType = 'text';
|
|
177
|
+
const radius = result.getPointRadius || visConfig.radius;
|
|
178
|
+
result.getTextPixelOffset = getTextPixelOffsetAccessor(textLabel, radius);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
return result;
|
|
182
|
+
}
|
package/src/config.js
CHANGED
|
@@ -1,60 +1,29 @@
|
|
|
1
|
-
import {API_VERSIONS
|
|
1
|
+
import {API_VERSIONS} from './api/maps-api-common';
|
|
2
2
|
|
|
3
3
|
export const defaultClassicCredentials = {
|
|
4
|
-
username: 'public',
|
|
5
4
|
apiKey: 'default_public',
|
|
6
5
|
region: 'us',
|
|
7
|
-
|
|
6
|
+
username: 'public'
|
|
8
7
|
};
|
|
9
8
|
|
|
10
|
-
const defaultCloudNativeCredentials = {
|
|
11
|
-
|
|
12
|
-
apiBaseUrl: null,
|
|
13
|
-
mapsUrl: null
|
|
9
|
+
export const defaultCloudNativeCredentials = {
|
|
10
|
+
apiBaseUrl: 'https://gcp-us-east1.api.carto.com'
|
|
14
11
|
};
|
|
15
12
|
|
|
16
13
|
let credentials = {};
|
|
17
14
|
|
|
18
15
|
setDefaultCredentials({});
|
|
19
16
|
|
|
20
|
-
export function setDefaultCredentials(
|
|
21
|
-
|
|
17
|
+
export function setDefaultCredentials({apiVersion, ...rest}) {
|
|
18
|
+
apiVersion = apiVersion || API_VERSIONS.V3;
|
|
22
19
|
|
|
23
20
|
switch (apiVersion) {
|
|
24
21
|
case API_VERSIONS.V1:
|
|
25
|
-
opts.mapsUrl = opts.mapsUrl || DEFAULT_MAPS_URL_FORMAT[apiVersion];
|
|
26
|
-
credentials = {
|
|
27
|
-
apiVersion,
|
|
28
|
-
...defaultClassicCredentials,
|
|
29
|
-
...opts
|
|
30
|
-
};
|
|
31
|
-
break;
|
|
32
|
-
|
|
33
22
|
case API_VERSIONS.V2:
|
|
34
|
-
|
|
35
|
-
credentials = {
|
|
36
|
-
apiVersion,
|
|
37
|
-
...defaultClassicCredentials,
|
|
38
|
-
...opts
|
|
39
|
-
};
|
|
40
|
-
|
|
23
|
+
credentials = {apiVersion, ...defaultClassicCredentials, ...rest};
|
|
41
24
|
break;
|
|
42
25
|
case API_VERSIONS.V3:
|
|
43
|
-
|
|
44
|
-
throw new Error(
|
|
45
|
-
`API version ${
|
|
46
|
-
API_VERSIONS.V3
|
|
47
|
-
} requires to define apiBaseUrl at credentials. Go to https://app.carto.com to get your apiBaseUrl.`
|
|
48
|
-
);
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
const apiBaseUrl = opts.apiBaseUrl || defaultCloudNativeCredentials.apiBaseUrl;
|
|
52
|
-
opts.mapsUrl = opts.mapsUrl || buildMapsUrlFromBase(apiBaseUrl);
|
|
53
|
-
credentials = {
|
|
54
|
-
apiVersion,
|
|
55
|
-
...defaultCloudNativeCredentials,
|
|
56
|
-
...opts
|
|
57
|
-
};
|
|
26
|
+
credentials = {apiVersion, ...defaultCloudNativeCredentials, ...rest};
|
|
58
27
|
break;
|
|
59
28
|
default:
|
|
60
29
|
throw new Error(`Invalid API version ${apiVersion}. Use API_VERSIONS enum.`);
|