@carto/api-client 0.5.0-alpha.8 → 0.5.0
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/CHANGELOG.md +30 -1
- package/build/api-client.cjs +15248 -3366
- package/build/api-client.cjs.map +1 -0
- package/build/api-client.d.cts +529 -182
- package/build/api-client.d.ts +529 -182
- package/build/api-client.js +15000 -3227
- package/build/api-client.js.map +1 -0
- package/build/worker.js +5118 -810
- package/build/worker.js.map +1 -0
- package/package.json +47 -31
- package/src/api/query.ts +2 -1
- package/src/constants-internal.ts +10 -0
- package/src/constants.ts +5 -1
- package/src/deck/get-data-filter-extension-props.ts +27 -9
- package/src/fetch-map/basemap-styles.ts +159 -0
- package/src/fetch-map/basemap.ts +120 -0
- package/src/fetch-map/fetch-map.ts +331 -0
- package/src/fetch-map/index.ts +13 -0
- package/src/fetch-map/layer-map.ts +461 -0
- package/src/fetch-map/parse-map.ts +425 -0
- package/src/fetch-map/source.ts +233 -0
- package/src/fetch-map/types.ts +268 -0
- package/src/fetch-map/utils.ts +69 -0
- package/src/filters/tileFeatures.ts +27 -10
- package/src/filters/tileFeaturesRaster.ts +122 -0
- package/src/index.ts +1 -0
- package/src/models/model.ts +0 -7
- package/src/sources/base-source.ts +4 -2
- package/src/sources/h3-tileset-source.ts +1 -1
- package/src/sources/quadbin-tileset-source.ts +1 -1
- package/src/sources/raster-source.ts +18 -5
- package/src/sources/types.ts +15 -8
- package/src/sources/vector-tileset-source.ts +1 -1
- package/src/spatial-index.ts +3 -84
- package/src/types.ts +16 -2
- package/src/widget-sources/index.ts +1 -0
- package/src/widget-sources/types.ts +0 -2
- package/src/widget-sources/widget-raster-source.ts +14 -0
- package/src/widget-sources/widget-remote-source.ts +16 -91
- package/src/widget-sources/widget-source.ts +9 -25
- package/src/widget-sources/widget-tileset-source-impl.ts +13 -16
- package/src/widget-sources/widget-tileset-source.ts +53 -47
- package/src/workers/widget-tileset-worker.ts +5 -7
|
@@ -0,0 +1,461 @@
|
|
|
1
|
+
import {deviation, extent, groupSort, median, variance} 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
|
+
scaleThreshold,
|
|
12
|
+
} from 'd3-scale';
|
|
13
|
+
import {format as d3Format} from 'd3-format';
|
|
14
|
+
import moment from 'moment-timezone';
|
|
15
|
+
|
|
16
|
+
export type LayerType =
|
|
17
|
+
| 'clusterTile'
|
|
18
|
+
| 'h3'
|
|
19
|
+
| 'heatmapTile'
|
|
20
|
+
| 'mvt'
|
|
21
|
+
| 'quadbin'
|
|
22
|
+
| 'raster'
|
|
23
|
+
| 'tileset';
|
|
24
|
+
|
|
25
|
+
import {createBinaryProxy, scaleIdentity} from './utils.js';
|
|
26
|
+
import {
|
|
27
|
+
CustomMarkersRange,
|
|
28
|
+
Dataset,
|
|
29
|
+
MapLayerConfig,
|
|
30
|
+
VisConfig,
|
|
31
|
+
VisualChannelField,
|
|
32
|
+
VisualChannels,
|
|
33
|
+
} from './types.js';
|
|
34
|
+
|
|
35
|
+
const SCALE_FUNCS: Record<string, () => any> = {
|
|
36
|
+
linear: scaleLinear,
|
|
37
|
+
ordinal: scaleOrdinal,
|
|
38
|
+
log: scaleLog,
|
|
39
|
+
point: scalePoint,
|
|
40
|
+
quantile: scaleQuantile,
|
|
41
|
+
quantize: scaleQuantize,
|
|
42
|
+
sqrt: scaleSqrt,
|
|
43
|
+
custom: scaleThreshold,
|
|
44
|
+
identity: scaleIdentity,
|
|
45
|
+
};
|
|
46
|
+
export type SCALE_TYPE = keyof typeof SCALE_FUNCS;
|
|
47
|
+
|
|
48
|
+
function identity<T>(v: T): T {
|
|
49
|
+
return v;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const UNKNOWN_COLOR = '#868d91';
|
|
53
|
+
|
|
54
|
+
export const AGGREGATION: Record<string, string> = {
|
|
55
|
+
average: 'MEAN',
|
|
56
|
+
maximum: 'MAX',
|
|
57
|
+
minimum: 'MIN',
|
|
58
|
+
sum: 'SUM',
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
export const OPACITY_MAP: Record<string, string> = {
|
|
62
|
+
getFillColor: 'opacity',
|
|
63
|
+
getLineColor: 'strokeOpacity',
|
|
64
|
+
getTextColor: 'opacity',
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
const AGGREGATION_FUNC: Record<string, (values: any, accessor: any) => any> = {
|
|
68
|
+
'count unique': (values: any, accessor: any) =>
|
|
69
|
+
groupSort(values, (v) => v.length, accessor).length,
|
|
70
|
+
median,
|
|
71
|
+
// Unfortunately mode() is only available in d3-array@3+ which is ESM only
|
|
72
|
+
mode: (values: any, accessor: any) =>
|
|
73
|
+
groupSort(values, (v) => v.length, accessor).pop(),
|
|
74
|
+
stddev: deviation,
|
|
75
|
+
variance,
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
const hexToRGBA = (c: any) => {
|
|
79
|
+
const {r, g, b, opacity} = rgb(c);
|
|
80
|
+
return [r, g, b, 255 * opacity];
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
// Kepler prop value -> Deck.gl prop value
|
|
84
|
+
// Supports nested definitions, and function transforms:
|
|
85
|
+
// {keplerProp: 'deckProp'} is equivalent to:
|
|
86
|
+
// {keplerProp: x => ({deckProp: x})}
|
|
87
|
+
const sharedPropMap = {
|
|
88
|
+
// Apply the value of Kepler `color` prop to the deck `getFillColor` prop
|
|
89
|
+
color: 'getFillColor',
|
|
90
|
+
isVisible: 'visible',
|
|
91
|
+
label: 'cartoLabel',
|
|
92
|
+
textLabel: {
|
|
93
|
+
alignment: 'getTextAlignmentBaseline',
|
|
94
|
+
anchor: 'getTextAnchor',
|
|
95
|
+
// Apply the value of Kepler `textLabel.color` prop to the deck `getTextColor` prop
|
|
96
|
+
color: 'getTextColor',
|
|
97
|
+
size: 'getTextSize',
|
|
98
|
+
},
|
|
99
|
+
visConfig: {
|
|
100
|
+
enable3d: 'extruded',
|
|
101
|
+
elevationScale: 'elevationScale',
|
|
102
|
+
filled: 'filled',
|
|
103
|
+
strokeColor: 'getLineColor',
|
|
104
|
+
stroked: 'stroked',
|
|
105
|
+
thickness: 'getLineWidth',
|
|
106
|
+
radius: 'getPointRadius',
|
|
107
|
+
wireframe: 'wireframe',
|
|
108
|
+
},
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
const customMarkersPropsMap = {
|
|
112
|
+
color: 'getIconColor',
|
|
113
|
+
visConfig: {
|
|
114
|
+
radius: 'getIconSize',
|
|
115
|
+
},
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
const heatmapTilePropsMap = {
|
|
119
|
+
visConfig: {
|
|
120
|
+
colorRange: (x: any) => ({colorRange: x.colors.map(hexToRGBA)}),
|
|
121
|
+
radius: 'radiusPixels',
|
|
122
|
+
},
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
const defaultProps = {
|
|
126
|
+
lineMiterLimit: 2,
|
|
127
|
+
lineWidthUnits: 'pixels',
|
|
128
|
+
pointRadiusUnits: 'pixels',
|
|
129
|
+
rounded: true,
|
|
130
|
+
wrapLongitude: false,
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
function mergePropMaps(
|
|
134
|
+
a: Record<string, any> = {},
|
|
135
|
+
b: Record<string, any> = {}
|
|
136
|
+
) {
|
|
137
|
+
return {...a, ...b, visConfig: {...a.visConfig, ...b.visConfig}};
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
const deprecatedLayerTypes = [
|
|
141
|
+
'geojson',
|
|
142
|
+
'grid',
|
|
143
|
+
'heatmap',
|
|
144
|
+
'hexagon',
|
|
145
|
+
'hexagonId',
|
|
146
|
+
'point',
|
|
147
|
+
];
|
|
148
|
+
|
|
149
|
+
export function getLayerProps(
|
|
150
|
+
type: LayerType,
|
|
151
|
+
config: MapLayerConfig,
|
|
152
|
+
dataset: Dataset
|
|
153
|
+
): {propMap: any; defaultProps: any} {
|
|
154
|
+
if (deprecatedLayerTypes.includes(type)) {
|
|
155
|
+
throw new Error(
|
|
156
|
+
`Outdated layer type: ${type}. Please open map in CARTO Builder to automatically migrate.`
|
|
157
|
+
);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
let basePropMap: any = sharedPropMap;
|
|
161
|
+
if (config.visConfig?.customMarkers) {
|
|
162
|
+
basePropMap = mergePropMaps(basePropMap, customMarkersPropsMap);
|
|
163
|
+
}
|
|
164
|
+
if (type === 'heatmapTile') {
|
|
165
|
+
basePropMap = mergePropMaps(basePropMap, heatmapTilePropsMap);
|
|
166
|
+
}
|
|
167
|
+
const {aggregationExp, aggregationResLevel} = dataset;
|
|
168
|
+
|
|
169
|
+
return {
|
|
170
|
+
propMap: basePropMap,
|
|
171
|
+
defaultProps: {
|
|
172
|
+
...defaultProps,
|
|
173
|
+
...(aggregationExp && {aggregationExp}),
|
|
174
|
+
...(aggregationResLevel && {aggregationResLevel}),
|
|
175
|
+
uniqueIdProperty: 'geoid',
|
|
176
|
+
},
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
function domainFromAttribute(
|
|
181
|
+
attribute: any,
|
|
182
|
+
scaleType: SCALE_TYPE,
|
|
183
|
+
scaleLength: number
|
|
184
|
+
) {
|
|
185
|
+
if (scaleType === 'ordinal' || scaleType === 'point') {
|
|
186
|
+
return attribute.categories
|
|
187
|
+
.map((c: any) => c.category)
|
|
188
|
+
.filter((c: any) => c !== undefined && c !== null);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
if (scaleType === 'quantile' && attribute.quantiles) {
|
|
192
|
+
return attribute.quantiles.global
|
|
193
|
+
? attribute.quantiles.global[scaleLength]
|
|
194
|
+
: attribute.quantiles[scaleLength];
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
let {min} = attribute;
|
|
198
|
+
if (scaleType === 'log' && min === 0) {
|
|
199
|
+
min = 1e-5;
|
|
200
|
+
}
|
|
201
|
+
return [min, attribute.max];
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
function domainFromValues(values: any, scaleType: SCALE_TYPE) {
|
|
205
|
+
if (scaleType === 'ordinal' || scaleType === 'point') {
|
|
206
|
+
return groupSort(
|
|
207
|
+
values,
|
|
208
|
+
(g) => -g.length,
|
|
209
|
+
(d) => d
|
|
210
|
+
);
|
|
211
|
+
} else if (scaleType === 'quantile') {
|
|
212
|
+
return values.sort((a: any, b: any) => a - b);
|
|
213
|
+
} else if (scaleType === 'log') {
|
|
214
|
+
const [d0, d1] = extent(values as number[]);
|
|
215
|
+
return [d0 === 0 ? 1e-5 : d0, d1];
|
|
216
|
+
}
|
|
217
|
+
return extent(values);
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
function calculateDomain(
|
|
221
|
+
data: any,
|
|
222
|
+
name: any,
|
|
223
|
+
scaleType: SCALE_TYPE,
|
|
224
|
+
scaleLength?: number
|
|
225
|
+
) {
|
|
226
|
+
if (data.tilestats) {
|
|
227
|
+
// Tileset data type
|
|
228
|
+
const {attributes} = data.tilestats.layers[0];
|
|
229
|
+
const attribute = attributes.find((a: any) => a.attribute === name);
|
|
230
|
+
return domainFromAttribute(attribute, scaleType, scaleLength as number);
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
return [0, 1];
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
function normalizeAccessor(accessor: any, data: any) {
|
|
237
|
+
if (data.features || data.tilestats) {
|
|
238
|
+
return (object: any, info: any) => {
|
|
239
|
+
if (object) {
|
|
240
|
+
return accessor(object.properties || object.__source.object.properties);
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
const {data, index} = info;
|
|
244
|
+
const proxy = createBinaryProxy(data, index);
|
|
245
|
+
return accessor(proxy);
|
|
246
|
+
};
|
|
247
|
+
}
|
|
248
|
+
return accessor;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
export function opacityToAlpha(opacity?: number) {
|
|
252
|
+
return opacity !== undefined
|
|
253
|
+
? Math.round(255 * Math.pow(opacity, 1 / 2.2))
|
|
254
|
+
: 255;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
function getAccessorKeys(name: string, aggregation?: string | null): string[] {
|
|
258
|
+
let keys = [name];
|
|
259
|
+
if (aggregation) {
|
|
260
|
+
// Snowflake will capitalized the keys, need to check lower and upper case version
|
|
261
|
+
keys = keys.concat(
|
|
262
|
+
[aggregation, aggregation.toUpperCase()].map((a) => `${name}_${a}`)
|
|
263
|
+
);
|
|
264
|
+
}
|
|
265
|
+
return keys;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
function findAccessorKey(keys: string[], properties: any): string[] {
|
|
269
|
+
for (const key of keys) {
|
|
270
|
+
if (key in properties) {
|
|
271
|
+
return [key];
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
throw new Error(
|
|
276
|
+
`Could not find property for any accessor key: ${keys.join(', ')}`
|
|
277
|
+
);
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
export function getColorValueAccessor(
|
|
281
|
+
{name}: VisualChannelField,
|
|
282
|
+
colorAggregation: string,
|
|
283
|
+
data: any
|
|
284
|
+
) {
|
|
285
|
+
const aggregator = AGGREGATION_FUNC[colorAggregation];
|
|
286
|
+
const accessor = (values: any) => aggregator(values, (p: any) => p[name]);
|
|
287
|
+
return normalizeAccessor(accessor, data);
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
export function getColorAccessor(
|
|
291
|
+
{name, colorColumn}: VisualChannelField,
|
|
292
|
+
scaleType: SCALE_TYPE,
|
|
293
|
+
{aggregation, range}: {aggregation: string; range: any},
|
|
294
|
+
opacity: number | undefined,
|
|
295
|
+
data: any
|
|
296
|
+
) {
|
|
297
|
+
const scale = calculateLayerScale(
|
|
298
|
+
colorColumn || name,
|
|
299
|
+
scaleType,
|
|
300
|
+
range,
|
|
301
|
+
data
|
|
302
|
+
);
|
|
303
|
+
const alpha = opacityToAlpha(opacity);
|
|
304
|
+
|
|
305
|
+
let accessorKeys = getAccessorKeys(name, aggregation);
|
|
306
|
+
const accessor = (properties: any) => {
|
|
307
|
+
if (!(accessorKeys[0] in properties)) {
|
|
308
|
+
accessorKeys = findAccessorKey(accessorKeys, properties);
|
|
309
|
+
}
|
|
310
|
+
const propertyValue = properties[accessorKeys[0]];
|
|
311
|
+
const {r, g, b} = rgb(scale(propertyValue));
|
|
312
|
+
return [r, g, b, propertyValue === null ? 0 : alpha];
|
|
313
|
+
};
|
|
314
|
+
return normalizeAccessor(accessor, data);
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
function calculateLayerScale(
|
|
318
|
+
name: any,
|
|
319
|
+
scaleType: SCALE_TYPE,
|
|
320
|
+
range: any,
|
|
321
|
+
data: any
|
|
322
|
+
) {
|
|
323
|
+
const scale = SCALE_FUNCS[scaleType]();
|
|
324
|
+
let domain: (string | number)[] = [];
|
|
325
|
+
let scaleColor: string[] = [];
|
|
326
|
+
|
|
327
|
+
if (scaleType !== 'identity') {
|
|
328
|
+
const {colorMap, colors} = range;
|
|
329
|
+
|
|
330
|
+
if (Array.isArray(colorMap)) {
|
|
331
|
+
colorMap.forEach(([value, color]) => {
|
|
332
|
+
domain.push(value);
|
|
333
|
+
scaleColor.push(color);
|
|
334
|
+
});
|
|
335
|
+
} else {
|
|
336
|
+
domain = calculateDomain(data, name, scaleType, colors.length);
|
|
337
|
+
scaleColor = colors;
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
if (scaleType === 'ordinal') {
|
|
341
|
+
domain = domain.slice(0, scaleColor.length);
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
scale.domain(domain);
|
|
346
|
+
scale.range(scaleColor);
|
|
347
|
+
scale.unknown(UNKNOWN_COLOR);
|
|
348
|
+
|
|
349
|
+
return scale;
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
const FALLBACK_ICON =
|
|
353
|
+
'data:image/svg+xml;charset=utf-8;base64,PHN2ZyB2aWV3Qm94PSIwIDAgMTAwIDEwMCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4NCiAgPGNpcmNsZSBjeD0iNTAiIGN5PSI1MCIgcj0iNTAiLz4NCjwvc3ZnPg==';
|
|
354
|
+
|
|
355
|
+
export function getIconUrlAccessor(
|
|
356
|
+
field: VisualChannelField | null | undefined,
|
|
357
|
+
range: CustomMarkersRange | null | undefined,
|
|
358
|
+
{
|
|
359
|
+
fallbackUrl,
|
|
360
|
+
maxIconSize,
|
|
361
|
+
useMaskedIcons,
|
|
362
|
+
}: {
|
|
363
|
+
fallbackUrl?: string | null;
|
|
364
|
+
maxIconSize: number;
|
|
365
|
+
useMaskedIcons?: boolean;
|
|
366
|
+
},
|
|
367
|
+
data: any
|
|
368
|
+
) {
|
|
369
|
+
const urlToUnpackedIcon = (url: string) => ({
|
|
370
|
+
id: `${url}@@${maxIconSize}`,
|
|
371
|
+
url,
|
|
372
|
+
width: maxIconSize,
|
|
373
|
+
height: maxIconSize,
|
|
374
|
+
mask: useMaskedIcons,
|
|
375
|
+
});
|
|
376
|
+
let unknownValue = fallbackUrl || FALLBACK_ICON;
|
|
377
|
+
|
|
378
|
+
if (range?.othersMarker) {
|
|
379
|
+
unknownValue = range.othersMarker;
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
const unknownIcon = urlToUnpackedIcon(unknownValue);
|
|
383
|
+
if (!range || !field) {
|
|
384
|
+
return () => unknownIcon;
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
const mapping: Record<string, any> = {};
|
|
388
|
+
for (const {value, markerUrl} of range.markerMap) {
|
|
389
|
+
if (markerUrl) {
|
|
390
|
+
mapping[value] = urlToUnpackedIcon(markerUrl);
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
const accessor = (properties: any) => {
|
|
395
|
+
const propertyValue = properties[field.name];
|
|
396
|
+
return mapping[propertyValue] || unknownIcon;
|
|
397
|
+
};
|
|
398
|
+
return normalizeAccessor(accessor, data);
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
export function getMaxMarkerSize(
|
|
402
|
+
visConfig: VisConfig,
|
|
403
|
+
visualChannels: VisualChannels
|
|
404
|
+
): number {
|
|
405
|
+
const {radiusRange, radius} = visConfig;
|
|
406
|
+
const {radiusField, sizeField} = visualChannels;
|
|
407
|
+
const field = radiusField || sizeField;
|
|
408
|
+
return Math.ceil(radiusRange && field ? radiusRange[1] : radius);
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
type Accessor = number | ((d: any, i: any) => number);
|
|
412
|
+
export function negateAccessor(accessor: Accessor): Accessor {
|
|
413
|
+
if (typeof accessor === 'function') {
|
|
414
|
+
return (d: any, i: any) => -accessor(d, i);
|
|
415
|
+
}
|
|
416
|
+
return -accessor;
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
export function getSizeAccessor(
|
|
420
|
+
{name}: VisualChannelField,
|
|
421
|
+
scaleType: SCALE_TYPE | undefined,
|
|
422
|
+
aggregation: string | null | undefined,
|
|
423
|
+
range: Iterable<Range> | null | undefined,
|
|
424
|
+
data: any
|
|
425
|
+
) {
|
|
426
|
+
const scale = scaleType ? SCALE_FUNCS[scaleType as any]() : identity;
|
|
427
|
+
if (scaleType) {
|
|
428
|
+
if (aggregation !== 'count') {
|
|
429
|
+
scale.domain(calculateDomain(data, name, scaleType));
|
|
430
|
+
}
|
|
431
|
+
scale.range(range);
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
let accessorKeys = getAccessorKeys(name, aggregation);
|
|
435
|
+
const accessor = (properties: any) => {
|
|
436
|
+
if (!(accessorKeys[0] in properties)) {
|
|
437
|
+
accessorKeys = findAccessorKey(accessorKeys, properties);
|
|
438
|
+
}
|
|
439
|
+
const propertyValue = properties[accessorKeys[0]];
|
|
440
|
+
return scale(propertyValue);
|
|
441
|
+
};
|
|
442
|
+
return normalizeAccessor(accessor, data);
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
const FORMATS: Record<string, (value: any) => string> = {
|
|
446
|
+
date: (s) => moment.utc(s).format('MM/DD/YY HH:mm:ssa'),
|
|
447
|
+
integer: d3Format('i'),
|
|
448
|
+
float: d3Format('.5f'),
|
|
449
|
+
timestamp: (s) => moment.utc(s).format('X'),
|
|
450
|
+
default: String,
|
|
451
|
+
};
|
|
452
|
+
|
|
453
|
+
export function getTextAccessor({name, type}: VisualChannelField, data: any) {
|
|
454
|
+
const format = FORMATS[type] || FORMATS.default;
|
|
455
|
+
const accessor = (properties: any) => {
|
|
456
|
+
return format(properties[name]);
|
|
457
|
+
};
|
|
458
|
+
return normalizeAccessor(accessor, data);
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
export {domainFromValues as _domainFromValues};
|