@carto/api-client 0.5.14 → 0.5.15-alpha.raster-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/build/api-client.cjs +796 -51
- package/build/api-client.cjs.map +1 -1
- package/build/api-client.d.cts +111 -16
- package/build/api-client.d.ts +111 -16
- package/build/api-client.js +783 -49
- package/build/api-client.js.map +1 -1
- package/package.json +4 -2
- package/src/fetch-map/basemap-styles.ts +1 -1
- package/src/fetch-map/index.ts +5 -1
- package/src/fetch-map/layer-map.ts +49 -19
- package/src/fetch-map/parse-map.ts +180 -71
- package/src/fetch-map/raster-layer.ts +534 -0
- package/src/fetch-map/types.ts +14 -0
- package/src/fetch-map/vec-expr-evaluator.ts +386 -0
- package/src/index.ts +7 -1
- package/src/sources/types.ts +52 -11
|
@@ -0,0 +1,534 @@
|
|
|
1
|
+
import type {RasterMetadata, RasterMetadataBand} from '../sources/types.js';
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
createVecExprEvaluator,
|
|
5
|
+
type VecExprResult,
|
|
6
|
+
type VecExprVecLike,
|
|
7
|
+
} from './vec-expr-evaluator.js';
|
|
8
|
+
import type {
|
|
9
|
+
ColorBand,
|
|
10
|
+
ColorRange,
|
|
11
|
+
MapLayerConfig,
|
|
12
|
+
RasterLayerConfigColorBand,
|
|
13
|
+
VisualChannels,
|
|
14
|
+
} from './types.js';
|
|
15
|
+
import {createColorScale, type ScaleType} from './layer-map.js';
|
|
16
|
+
|
|
17
|
+
const UNKNOWN_COLOR = [134, 141, 145];
|
|
18
|
+
|
|
19
|
+
const RASTER_COLOR_BANDS = ['red', 'green', 'blue'] as const;
|
|
20
|
+
|
|
21
|
+
function getHasDataPredicate(noData: number | string | undefined) {
|
|
22
|
+
if (noData === 'nan') {
|
|
23
|
+
return (v: number) => !isNaN(v);
|
|
24
|
+
}
|
|
25
|
+
if (typeof noData === 'string') {
|
|
26
|
+
noData = parseFloat(noData);
|
|
27
|
+
}
|
|
28
|
+
if (typeof noData === 'number') {
|
|
29
|
+
return (v: number) => v !== noData && !isNaN(v);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
return () => true;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// this is data as seen in RasterLayer
|
|
36
|
+
type RasterLayerData = {
|
|
37
|
+
blockSize: number;
|
|
38
|
+
cells: BinaryDataCells;
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
// this is data as seen in RasterColumnLayer - the one that actually renders pixels
|
|
42
|
+
// (binary data is just wrapped)
|
|
43
|
+
type RasterColumnLayerData = {
|
|
44
|
+
length: number; // number of pixels
|
|
45
|
+
data: RasterLayerData;
|
|
46
|
+
|
|
47
|
+
// used to store buffers directly to be used by deck.gl, so we can skip accessors
|
|
48
|
+
attributes?: Record<string, ArrayLike<number>>;
|
|
49
|
+
|
|
50
|
+
// temporary storage for expression results filled in dataTransform
|
|
51
|
+
expressionEvalContext?: Record<string, ArrayLike<number>>;
|
|
52
|
+
customExpressionResults?: Record<string, VecExprResult>;
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
type BinaryDataCells = {
|
|
56
|
+
numericProps: Record<string, {value: VecExprVecLike}>;
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
function createRasterColumnLayerDataTransform(
|
|
60
|
+
transform: (dataWrapped: RasterColumnLayerData) => RasterColumnLayerData
|
|
61
|
+
) {
|
|
62
|
+
return (data: RasterLayerData | RasterColumnLayerData) => {
|
|
63
|
+
if (!data || !('data' in data) || !data?.data?.cells?.numericProps) {
|
|
64
|
+
// we're in RasterLayer, or we've got invalid data
|
|
65
|
+
return data as RasterLayerData;
|
|
66
|
+
}
|
|
67
|
+
// we only transform data when in RasterColumnLayer
|
|
68
|
+
return transform(data);
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function createEvaluationContext(
|
|
73
|
+
numericProps: Record<string, {value: VecExprVecLike}>,
|
|
74
|
+
noData: number | string | undefined
|
|
75
|
+
) {
|
|
76
|
+
// Optimization note
|
|
77
|
+
// Seems like Array.from(256k+typed array takes even 15ms), so _we_ can afford to copy the array if we really don't need it to
|
|
78
|
+
// only copy values to array only if we really see nodata in all bands
|
|
79
|
+
const hasData = getHasDataPredicate(noData);
|
|
80
|
+
const bands = Object.entries(numericProps).map(([bandName, {value}]) => ({
|
|
81
|
+
bandName,
|
|
82
|
+
values: value,
|
|
83
|
+
copied: false,
|
|
84
|
+
}));
|
|
85
|
+
|
|
86
|
+
const length = bands[0].values.length;
|
|
87
|
+
|
|
88
|
+
for (let i = 0; i < length; i++) {
|
|
89
|
+
let hasSomeData = false;
|
|
90
|
+
for (let j = 0; j < bands.length; j++) {
|
|
91
|
+
hasSomeData = hasSomeData || hasData(bands[j].values[i]);
|
|
92
|
+
}
|
|
93
|
+
if (!hasSomeData) {
|
|
94
|
+
for (let j = 0; j < bands.length; j++) {
|
|
95
|
+
if (!bands[j].copied) {
|
|
96
|
+
bands[j].copied = true;
|
|
97
|
+
bands[j].values = Array.from(bands[j].values);
|
|
98
|
+
}
|
|
99
|
+
bands[j].values[i] = NaN;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const context = bands.reduce(
|
|
105
|
+
(agg, {bandName, values}) => {
|
|
106
|
+
agg[bandName] = values;
|
|
107
|
+
return agg;
|
|
108
|
+
},
|
|
109
|
+
{} as Record<string, ArrayLike<number>>
|
|
110
|
+
);
|
|
111
|
+
return context;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function createExprDataTransform({
|
|
115
|
+
colorBand,
|
|
116
|
+
rasterMetadata,
|
|
117
|
+
usedSymbols,
|
|
118
|
+
}: {
|
|
119
|
+
colorBand: RasterLayerConfigColorBand | null | undefined;
|
|
120
|
+
rasterMetadata: RasterMetadata;
|
|
121
|
+
usedSymbols: string[];
|
|
122
|
+
}) {
|
|
123
|
+
if (!colorBand || !colorBand.type || colorBand.type === 'none') {
|
|
124
|
+
return undefined;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
const expr = colorBand?.type === 'expression' ? colorBand.value : undefined;
|
|
128
|
+
const vecExprEvaluator = expr ? createVecExprEvaluator(expr) : undefined;
|
|
129
|
+
|
|
130
|
+
const dataTransform = createRasterColumnLayerDataTransform(
|
|
131
|
+
(dataWrapped: RasterColumnLayerData) => {
|
|
132
|
+
const data = dataWrapped.data;
|
|
133
|
+
if (expr) {
|
|
134
|
+
const cachedResult = dataWrapped.customExpressionResults?.[expr];
|
|
135
|
+
if (cachedResult) {
|
|
136
|
+
return dataWrapped;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
let context = dataWrapped.expressionEvalContext;
|
|
141
|
+
if (!context) {
|
|
142
|
+
const usedNumericProps = usedSymbols.reduce(
|
|
143
|
+
(acc, symbol) => {
|
|
144
|
+
acc[symbol] = data.cells.numericProps[symbol];
|
|
145
|
+
return acc;
|
|
146
|
+
},
|
|
147
|
+
{} as Record<string, {value: VecExprVecLike}>
|
|
148
|
+
);
|
|
149
|
+
context = createEvaluationContext(
|
|
150
|
+
usedNumericProps,
|
|
151
|
+
rasterMetadata.nodata
|
|
152
|
+
);
|
|
153
|
+
dataWrapped = {
|
|
154
|
+
...dataWrapped,
|
|
155
|
+
expressionEvalContext: context,
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
if (!vecExprEvaluator || !expr) return dataWrapped;
|
|
160
|
+
|
|
161
|
+
const evalResult = vecExprEvaluator(context);
|
|
162
|
+
return {
|
|
163
|
+
...dataWrapped,
|
|
164
|
+
customExpressionResults: {
|
|
165
|
+
...dataWrapped.customExpressionResults,
|
|
166
|
+
[expr]: evalResult,
|
|
167
|
+
},
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
);
|
|
171
|
+
return dataTransform;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
function combineDataTransforms<T>(
|
|
175
|
+
dataTransforms: (((data: T) => T) | undefined)[]
|
|
176
|
+
): ((data: T) => T) | undefined {
|
|
177
|
+
const actualTransforms = dataTransforms.filter((v) => v) as ((
|
|
178
|
+
data: T
|
|
179
|
+
) => T)[];
|
|
180
|
+
|
|
181
|
+
if (actualTransforms.length === 0) return undefined;
|
|
182
|
+
if (actualTransforms.length === 1) return actualTransforms[0];
|
|
183
|
+
|
|
184
|
+
return (data: T) =>
|
|
185
|
+
actualTransforms.reduce(
|
|
186
|
+
(aggData, transformFun) => transformFun(aggData),
|
|
187
|
+
data
|
|
188
|
+
);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
function createRgbToColorBufferDataTransform({
|
|
192
|
+
bandDefs,
|
|
193
|
+
attribute,
|
|
194
|
+
}: {
|
|
195
|
+
bandDefs: Partial<Record<ColorBand, RasterLayerConfigColorBand>>;
|
|
196
|
+
attribute: string;
|
|
197
|
+
}) {
|
|
198
|
+
return createRasterColumnLayerDataTransform(
|
|
199
|
+
(dataWrapped: RasterColumnLayerData) => {
|
|
200
|
+
const length = dataWrapped.length;
|
|
201
|
+
|
|
202
|
+
const getBandBufferOrValue = (colorBand?: RasterLayerConfigColorBand) => {
|
|
203
|
+
if (colorBand?.type === 'expression') {
|
|
204
|
+
return dataWrapped.customExpressionResults?.[colorBand.value];
|
|
205
|
+
}
|
|
206
|
+
if (colorBand?.type === 'band') {
|
|
207
|
+
// we could use original band, but this one is already cleared from nodata if needed
|
|
208
|
+
return dataWrapped.expressionEvalContext?.[colorBand.value];
|
|
209
|
+
}
|
|
210
|
+
return 0;
|
|
211
|
+
};
|
|
212
|
+
|
|
213
|
+
const red = getBandBufferOrValue(bandDefs.red);
|
|
214
|
+
const green = getBandBufferOrValue(bandDefs.green);
|
|
215
|
+
const blue = getBandBufferOrValue(bandDefs.blue);
|
|
216
|
+
|
|
217
|
+
const colorBuffer = new Uint8Array(length * 4);
|
|
218
|
+
for (
|
|
219
|
+
let inputIndex = 0, outputIndex = 0;
|
|
220
|
+
inputIndex < length;
|
|
221
|
+
inputIndex++, outputIndex += 4
|
|
222
|
+
) {
|
|
223
|
+
const redRaw =
|
|
224
|
+
typeof red === 'number' ? red : red ? red[inputIndex] : NaN;
|
|
225
|
+
const greenRaw =
|
|
226
|
+
typeof green === 'number' ? green : green ? green[inputIndex] : NaN;
|
|
227
|
+
const blueRaw =
|
|
228
|
+
typeof blue === 'number' ? blue : blue ? blue[inputIndex] : NaN;
|
|
229
|
+
|
|
230
|
+
if (isNaN(redRaw) && isNaN(greenRaw) && isNaN(blueRaw)) {
|
|
231
|
+
// skip pixel
|
|
232
|
+
bufferSetRgba(colorBuffer, outputIndex, 0, 0, 0, 0);
|
|
233
|
+
} else {
|
|
234
|
+
bufferSetRgba(
|
|
235
|
+
colorBuffer,
|
|
236
|
+
outputIndex,
|
|
237
|
+
redRaw,
|
|
238
|
+
greenRaw,
|
|
239
|
+
blueRaw,
|
|
240
|
+
255
|
|
241
|
+
);
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
// clear cached buffers
|
|
246
|
+
// This transform is applied last -after expression & band evaluators which store and
|
|
247
|
+
// cache values for _this_ transform.
|
|
248
|
+
// Now, _assuming_ this is last transform we can clear those buffers.
|
|
249
|
+
dataWrapped.customExpressionResults = undefined;
|
|
250
|
+
dataWrapped.expressionEvalContext = undefined;
|
|
251
|
+
|
|
252
|
+
return {
|
|
253
|
+
...dataWrapped,
|
|
254
|
+
attributes: {
|
|
255
|
+
[attribute]: colorBuffer,
|
|
256
|
+
},
|
|
257
|
+
};
|
|
258
|
+
}
|
|
259
|
+
);
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
function getUsedSymbols(colorBands: RasterLayerConfigColorBand[]) {
|
|
263
|
+
return Array.from(
|
|
264
|
+
colorBands.reduce((symbols, band) => {
|
|
265
|
+
if (band.type === 'expression') {
|
|
266
|
+
const expressionSymbols =
|
|
267
|
+
createVecExprEvaluator(band.value)?.symbols || [];
|
|
268
|
+
expressionSymbols.forEach((symbol) => symbols.add(symbol));
|
|
269
|
+
}
|
|
270
|
+
if (band.type === 'band') {
|
|
271
|
+
symbols.add(band.value);
|
|
272
|
+
}
|
|
273
|
+
return symbols;
|
|
274
|
+
}, new Set<string>())
|
|
275
|
+
);
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
export function getRasterTileLayerStylePropsRgb({
|
|
279
|
+
layerConfig,
|
|
280
|
+
rasterMetadata,
|
|
281
|
+
visualChannels,
|
|
282
|
+
}: {
|
|
283
|
+
layerConfig: MapLayerConfig;
|
|
284
|
+
rasterMetadata: RasterMetadata;
|
|
285
|
+
visualChannels: VisualChannels;
|
|
286
|
+
}) {
|
|
287
|
+
const {visConfig} = layerConfig;
|
|
288
|
+
const {colorBands} = visConfig;
|
|
289
|
+
|
|
290
|
+
const bandDefs = {
|
|
291
|
+
red: colorBands?.find((band) => band.band === 'red'),
|
|
292
|
+
green: colorBands?.find((band) => band.band === 'green'),
|
|
293
|
+
blue: colorBands?.find((band) => band.band === 'blue'),
|
|
294
|
+
};
|
|
295
|
+
|
|
296
|
+
const rgbToInstanceFillColorsDataTransform =
|
|
297
|
+
createRgbToColorBufferDataTransform({
|
|
298
|
+
bandDefs,
|
|
299
|
+
attribute: 'instanceFillColors',
|
|
300
|
+
});
|
|
301
|
+
|
|
302
|
+
const usedSymbols = colorBands ? getUsedSymbols(colorBands) : [];
|
|
303
|
+
const bandTransforms = RASTER_COLOR_BANDS.map((band) =>
|
|
304
|
+
createExprDataTransform({
|
|
305
|
+
colorBand: bandDefs[band],
|
|
306
|
+
rasterMetadata,
|
|
307
|
+
usedSymbols,
|
|
308
|
+
})
|
|
309
|
+
);
|
|
310
|
+
const combinedDataTransform = combineDataTransforms([
|
|
311
|
+
...bandTransforms,
|
|
312
|
+
rgbToInstanceFillColorsDataTransform,
|
|
313
|
+
]);
|
|
314
|
+
|
|
315
|
+
return {
|
|
316
|
+
dataTransform: combinedDataTransform as () => any,
|
|
317
|
+
updateTriggers: getRasterTileLayerUpdateTriggers({
|
|
318
|
+
layerConfig,
|
|
319
|
+
visualChannels,
|
|
320
|
+
}),
|
|
321
|
+
};
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
function createBandColorScaleDataTransform({
|
|
325
|
+
bandName,
|
|
326
|
+
scaleFun,
|
|
327
|
+
nodata,
|
|
328
|
+
attribute,
|
|
329
|
+
}: {
|
|
330
|
+
bandName: string;
|
|
331
|
+
scaleFun: (v: number) => number[];
|
|
332
|
+
nodata: number | string | undefined;
|
|
333
|
+
attribute: string;
|
|
334
|
+
}) {
|
|
335
|
+
const hasData = getHasDataPredicate(nodata);
|
|
336
|
+
|
|
337
|
+
return createRasterColumnLayerDataTransform(
|
|
338
|
+
(dataWrapped: RasterColumnLayerData) => {
|
|
339
|
+
const length = dataWrapped.length;
|
|
340
|
+
const bandBuffer = dataWrapped.data.cells.numericProps[bandName].value;
|
|
341
|
+
const colorBuffer = new Uint8Array(length * 4);
|
|
342
|
+
|
|
343
|
+
for (let i = 0; i < length; i++) {
|
|
344
|
+
const rawValue = bandBuffer[i];
|
|
345
|
+
if (!hasData(rawValue)) {
|
|
346
|
+
// skip pixel
|
|
347
|
+
bufferSetRgba(colorBuffer, i * 4, 0, 0, 0, 0);
|
|
348
|
+
} else {
|
|
349
|
+
const colorRgb = scaleFun(rawValue);
|
|
350
|
+
bufferSetRgba(
|
|
351
|
+
colorBuffer,
|
|
352
|
+
i * 4,
|
|
353
|
+
colorRgb[0],
|
|
354
|
+
colorRgb[1],
|
|
355
|
+
colorRgb[2],
|
|
356
|
+
255
|
|
357
|
+
);
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
return {
|
|
361
|
+
...dataWrapped,
|
|
362
|
+
attributes: {
|
|
363
|
+
[attribute]: colorBuffer,
|
|
364
|
+
},
|
|
365
|
+
};
|
|
366
|
+
}
|
|
367
|
+
);
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
export function domainFromRasterMetadataBand(
|
|
371
|
+
band: RasterMetadataBand,
|
|
372
|
+
scaleType: ScaleType,
|
|
373
|
+
colorRange: ColorRange
|
|
374
|
+
) {
|
|
375
|
+
if (scaleType === 'ordinal') {
|
|
376
|
+
return colorRange.colorMap?.map(([value]) => value) || [];
|
|
377
|
+
}
|
|
378
|
+
if (scaleType === 'custom') {
|
|
379
|
+
if (colorRange.uiCustomScaleType === 'logarithmic') {
|
|
380
|
+
if (colorRange.colorMap) {
|
|
381
|
+
return colorRange.colorMap?.map(([value]) => value) || [];
|
|
382
|
+
}
|
|
383
|
+
return [band.stats.min, band.stats.max];
|
|
384
|
+
} else {
|
|
385
|
+
// actually custom, read colorMap
|
|
386
|
+
return colorRange.colorMap?.map(([value]) => value) || [];
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
const scaleLength = colorRange.colors.length;
|
|
390
|
+
if (scaleType === 'quantile') {
|
|
391
|
+
const quantiles = band.stats.quantiles?.[scaleLength];
|
|
392
|
+
if (!quantiles) {
|
|
393
|
+
return [0, 1];
|
|
394
|
+
}
|
|
395
|
+
return [band.stats.min, ...quantiles, band.stats.max];
|
|
396
|
+
}
|
|
397
|
+
return [band.stats.min, band.stats.max];
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
/**
|
|
401
|
+
* Get RasterLayerStyle props for ColorRange and UniqueValues modes
|
|
402
|
+
*
|
|
403
|
+
* Effectively, applies selected color scale applied to one band.
|
|
404
|
+
*/
|
|
405
|
+
export function getRasterTileLayerStylePropsScaledBand({
|
|
406
|
+
layerConfig,
|
|
407
|
+
rasterMetadata,
|
|
408
|
+
visualChannels,
|
|
409
|
+
}: {
|
|
410
|
+
layerConfig: MapLayerConfig;
|
|
411
|
+
visualChannels: VisualChannels;
|
|
412
|
+
rasterMetadata: RasterMetadata;
|
|
413
|
+
}) {
|
|
414
|
+
const {visConfig} = layerConfig;
|
|
415
|
+
const {colorField} = visualChannels;
|
|
416
|
+
const {rasterStyleType} = visConfig;
|
|
417
|
+
|
|
418
|
+
const colorRange =
|
|
419
|
+
rasterStyleType === 'ColorRange'
|
|
420
|
+
? visConfig.colorRange
|
|
421
|
+
: visConfig.uniqueValuesColorRange;
|
|
422
|
+
const scaleType =
|
|
423
|
+
rasterStyleType === 'ColorRange' ? visualChannels.colorScale : 'ordinal';
|
|
424
|
+
const bandInfo = rasterMetadata.bands.find(
|
|
425
|
+
(band) => band.name === colorField?.name
|
|
426
|
+
);
|
|
427
|
+
|
|
428
|
+
if (!colorField?.name || !scaleType || !colorRange || !bandInfo) {
|
|
429
|
+
return {};
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
const domain = domainFromRasterMetadataBand(bandInfo, scaleType, colorRange);
|
|
433
|
+
|
|
434
|
+
const scaleFun = createColorScale(
|
|
435
|
+
scaleType,
|
|
436
|
+
domain,
|
|
437
|
+
colorRange.colors.map(hexToRGB),
|
|
438
|
+
UNKNOWN_COLOR
|
|
439
|
+
);
|
|
440
|
+
|
|
441
|
+
const bandColorScaleDataTransform = createBandColorScaleDataTransform({
|
|
442
|
+
bandName: bandInfo.name,
|
|
443
|
+
scaleFun,
|
|
444
|
+
nodata: bandInfo?.nodata ?? rasterMetadata.nodata,
|
|
445
|
+
attribute: 'instanceFillColors',
|
|
446
|
+
});
|
|
447
|
+
|
|
448
|
+
return {
|
|
449
|
+
dataTransform: bandColorScaleDataTransform as () => any,
|
|
450
|
+
updateTriggers: getRasterTileLayerUpdateTriggers({
|
|
451
|
+
layerConfig,
|
|
452
|
+
visualChannels,
|
|
453
|
+
}),
|
|
454
|
+
};
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
export function getRasterTileLayerStyleProps({
|
|
458
|
+
layerConfig,
|
|
459
|
+
visualChannels,
|
|
460
|
+
rasterMetadata,
|
|
461
|
+
}: {
|
|
462
|
+
layerConfig: MapLayerConfig;
|
|
463
|
+
visualChannels: VisualChannels;
|
|
464
|
+
rasterMetadata: RasterMetadata;
|
|
465
|
+
}) {
|
|
466
|
+
const {visConfig} = layerConfig;
|
|
467
|
+
const {rasterStyleType} = visConfig;
|
|
468
|
+
|
|
469
|
+
if (rasterStyleType === 'Rgb') {
|
|
470
|
+
return getRasterTileLayerStylePropsRgb({
|
|
471
|
+
layerConfig,
|
|
472
|
+
rasterMetadata,
|
|
473
|
+
visualChannels,
|
|
474
|
+
});
|
|
475
|
+
} else {
|
|
476
|
+
return getRasterTileLayerStylePropsScaledBand({
|
|
477
|
+
layerConfig,
|
|
478
|
+
rasterMetadata,
|
|
479
|
+
visualChannels,
|
|
480
|
+
});
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
export function getRasterTileLayerUpdateTriggers({
|
|
485
|
+
layerConfig,
|
|
486
|
+
visualChannels,
|
|
487
|
+
}: {
|
|
488
|
+
layerConfig: MapLayerConfig;
|
|
489
|
+
visualChannels: VisualChannels;
|
|
490
|
+
}) {
|
|
491
|
+
const {visConfig} = layerConfig;
|
|
492
|
+
const {rasterStyleType} = visConfig;
|
|
493
|
+
const getFillColorUpdateTriggers: Record<string, unknown> = {
|
|
494
|
+
rasterStyleType,
|
|
495
|
+
};
|
|
496
|
+
if (rasterStyleType === 'ColorRange') {
|
|
497
|
+
getFillColorUpdateTriggers.colorRange = visConfig.colorRange?.colors;
|
|
498
|
+
getFillColorUpdateTriggers.colorMap = visConfig.colorRange?.colorMap;
|
|
499
|
+
getFillColorUpdateTriggers.colorScale = visualChannels.colorScale;
|
|
500
|
+
getFillColorUpdateTriggers.colorFieldId = visualChannels.colorField?.name;
|
|
501
|
+
} else if (rasterStyleType === 'UniqueValues') {
|
|
502
|
+
getFillColorUpdateTriggers.colorMap =
|
|
503
|
+
visConfig.uniqueValuesColorRange?.colorMap;
|
|
504
|
+
getFillColorUpdateTriggers.colorFieldId = visualChannels.colorField?.name;
|
|
505
|
+
} else if (rasterStyleType === 'Rgb') {
|
|
506
|
+
getFillColorUpdateTriggers.colorBands = visConfig.colorBands;
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
return {
|
|
510
|
+
getFillColor: getFillColorUpdateTriggers,
|
|
511
|
+
};
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
function bufferSetRgba(
|
|
515
|
+
target: VecExprVecLike,
|
|
516
|
+
index: number,
|
|
517
|
+
r: number,
|
|
518
|
+
g: number,
|
|
519
|
+
b: number,
|
|
520
|
+
a: number
|
|
521
|
+
) {
|
|
522
|
+
target[index + 0] = r;
|
|
523
|
+
target[index + 1] = g;
|
|
524
|
+
target[index + 2] = b;
|
|
525
|
+
target[index + 3] = a;
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
function hexToRGB(hexColor: string): [number, number, number] {
|
|
529
|
+
const r = parseInt(hexColor.slice(1, 3), 16);
|
|
530
|
+
const g = parseInt(hexColor.slice(3, 5), 16);
|
|
531
|
+
const b = parseInt(hexColor.slice(5, 7), 16);
|
|
532
|
+
|
|
533
|
+
return [r, g, b];
|
|
534
|
+
}
|
package/src/fetch-map/types.ts
CHANGED
|
@@ -39,6 +39,7 @@ export type ColorRange = {
|
|
|
39
39
|
colorMap: string[][] | undefined;
|
|
40
40
|
name: string;
|
|
41
41
|
type: string;
|
|
42
|
+
uiCustomScaleType?: 'logarithmic';
|
|
42
43
|
};
|
|
43
44
|
|
|
44
45
|
export type CustomMarkersRange = {
|
|
@@ -49,6 +50,14 @@ export type CustomMarkersRange = {
|
|
|
49
50
|
othersMarker?: string;
|
|
50
51
|
};
|
|
51
52
|
|
|
53
|
+
export type ColorBand = 'red' | 'green' | 'blue' | 'alpha';
|
|
54
|
+
|
|
55
|
+
export type RasterLayerConfigColorBand = {
|
|
56
|
+
band: ColorBand;
|
|
57
|
+
type: 'none' | 'band' | 'expression';
|
|
58
|
+
value: string; // band name or expression
|
|
59
|
+
};
|
|
60
|
+
|
|
52
61
|
export type VisConfig = {
|
|
53
62
|
filled?: boolean;
|
|
54
63
|
opacity?: number;
|
|
@@ -79,6 +88,11 @@ export type VisConfig = {
|
|
|
79
88
|
// type = clusterTile
|
|
80
89
|
clusterLevel?: number;
|
|
81
90
|
isTextVisible?: boolean;
|
|
91
|
+
|
|
92
|
+
rasterStyleType?: 'Rgb' | 'ColorRange' | 'UniqueValues';
|
|
93
|
+
colorBands?: RasterLayerConfigColorBand[];
|
|
94
|
+
|
|
95
|
+
uniqueValuesColorRange?: ColorRange;
|
|
82
96
|
};
|
|
83
97
|
|
|
84
98
|
export type TextLabel = {
|