@carto/api-client 0.4.6 → 0.4.7-alpha.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 +17 -1
- package/build/api/carto-api-error.d.ts +1 -1
- package/build/api/query.d.ts +1 -1
- package/build/api/request-with-parameters.d.ts +2 -2
- package/build/api-client.cjs +2365 -279
- package/build/api-client.cjs.map +1 -1
- package/build/api-client.modern.js +2274 -298
- package/build/api-client.modern.js.map +1 -1
- package/build/client.d.ts +2 -2
- package/build/constants-internal.d.ts +5 -5
- package/build/constants.d.ts +25 -3
- package/build/deck/get-data-filter-extension-props.d.ts +28 -0
- package/build/deck/index.d.ts +1 -0
- package/build/filters/Filter.d.ts +25 -0
- package/build/filters/FilterTypes.d.ts +3 -0
- package/build/filters/geosjonFeatures.d.ts +8 -0
- package/build/filters/index.d.ts +6 -0
- package/build/filters/tileFeatures.d.ts +20 -0
- package/build/filters/tileFeaturesGeometries.d.ts +13 -0
- package/build/filters/tileFeaturesSpatialIndex.d.ts +10 -0
- package/build/filters.d.ts +2 -2
- package/build/geo.d.ts +1 -1
- package/build/index.d.ts +5 -0
- package/build/models/common.d.ts +5 -4
- package/build/models/index.d.ts +1 -1
- package/build/models/model.d.ts +2 -2
- package/build/operations/aggregation.d.ts +8 -0
- package/build/operations/applySorting.d.ts +20 -0
- package/build/operations/groupBy.d.ts +15 -0
- package/build/operations/groupByDate.d.ts +11 -0
- package/build/operations/histogram.d.ts +13 -0
- package/build/operations/index.d.ts +6 -0
- package/build/operations/scatterPlot.d.ts +14 -0
- package/build/sources/base-source.d.ts +2 -2
- package/build/sources/boundary-query-source.d.ts +1 -1
- package/build/sources/boundary-table-source.d.ts +1 -1
- package/build/sources/h3-query-source.d.ts +2 -2
- package/build/sources/h3-table-source.d.ts +2 -2
- package/build/sources/h3-tileset-source.d.ts +1 -1
- package/build/sources/index.d.ts +26 -26
- package/build/sources/quadbin-query-source.d.ts +2 -2
- package/build/sources/quadbin-table-source.d.ts +2 -2
- package/build/sources/quadbin-tileset-source.d.ts +1 -1
- package/build/sources/raster-source.d.ts +1 -1
- package/build/sources/types.d.ts +3 -3
- package/build/sources/vector-query-source.d.ts +1 -1
- package/build/sources/vector-table-source.d.ts +1 -1
- package/build/sources/vector-tileset-source.d.ts +1 -1
- package/build/spatial-index.d.ts +3 -3
- package/build/types-internal.d.ts +9 -5
- package/build/types.d.ts +74 -14
- package/build/utils/dateUtils.d.ts +10 -0
- package/build/utils/getTileFormat.d.ts +3 -0
- package/build/utils/makeIntervalComplete.d.ts +2 -0
- package/build/utils/transformTileCoordsToWGS84.d.ts +8 -0
- package/build/utils/transformToTileCoords.d.ts +9 -0
- package/build/utils.d.ts +3 -3
- package/build/widget-sources/index.d.ts +3 -1
- package/build/widget-sources/types.d.ts +38 -25
- package/build/widget-sources/widget-query-source.d.ts +4 -3
- package/build/widget-sources/widget-remote-source.d.ts +18 -0
- package/build/widget-sources/{widget-base-source.d.ts → widget-source.d.ts} +16 -41
- package/build/widget-sources/widget-table-source.d.ts +4 -3
- package/build/widget-sources/widget-tileset-source.d.ts +75 -0
- package/package.json +46 -29
- package/src/api/carto-api-error.ts +1 -1
- package/src/api/query.ts +5 -5
- package/src/api/request-with-parameters.ts +6 -6
- package/src/client.ts +3 -3
- package/src/constants-internal.ts +5 -5
- package/src/constants.ts +28 -3
- package/src/deck/get-data-filter-extension-props.ts +164 -0
- package/src/deck/index.ts +1 -0
- package/src/filters/Filter.ts +179 -0
- package/src/filters/FilterTypes.ts +109 -0
- package/src/filters/geosjonFeatures.ts +32 -0
- package/src/filters/index.ts +6 -0
- package/src/filters/tileFeatures.ts +50 -0
- package/src/filters/tileFeaturesGeometries.ts +444 -0
- package/src/filters/tileFeaturesSpatialIndex.ts +119 -0
- package/src/filters.ts +4 -4
- package/src/geo.ts +12 -14
- package/src/index.ts +7 -0
- package/src/models/common.ts +11 -9
- package/src/models/index.ts +1 -1
- package/src/models/model.ts +3 -4
- package/src/operations/aggregation.ts +154 -0
- package/src/operations/applySorting.ts +109 -0
- package/src/operations/groupBy.ts +59 -0
- package/src/operations/groupByDate.ts +98 -0
- package/src/operations/histogram.ts +66 -0
- package/src/operations/index.ts +6 -0
- package/src/operations/scatterPlot.ts +50 -0
- package/src/sources/base-source.ts +8 -8
- package/src/sources/boundary-query-source.ts +2 -2
- package/src/sources/boundary-table-source.ts +2 -2
- package/src/sources/h3-query-source.ts +7 -5
- package/src/sources/h3-table-source.ts +7 -5
- package/src/sources/h3-tileset-source.ts +2 -2
- package/src/sources/index.ts +26 -26
- package/src/sources/quadbin-query-source.ts +7 -5
- package/src/sources/quadbin-table-source.ts +7 -5
- package/src/sources/quadbin-tileset-source.ts +2 -2
- package/src/sources/raster-source.ts +3 -2
- package/src/sources/types.ts +3 -3
- package/src/sources/vector-query-source.ts +7 -5
- package/src/sources/vector-table-source.ts +7 -5
- package/src/sources/vector-tileset-source.ts +2 -2
- package/src/spatial-index.ts +4 -5
- package/src/types-internal.ts +11 -5
- package/src/types.ts +73 -15
- package/src/utils/dateUtils.ts +28 -0
- package/src/utils/getTileFormat.ts +9 -0
- package/src/utils/makeIntervalComplete.ts +17 -0
- package/src/utils/transformTileCoordsToWGS84.ts +77 -0
- package/src/utils/transformToTileCoords.ts +85 -0
- package/src/utils.ts +3 -3
- package/src/widget-sources/index.ts +3 -1
- package/src/widget-sources/types.ts +39 -25
- package/src/widget-sources/widget-query-source.ts +12 -5
- package/src/widget-sources/{widget-base-source.ts → widget-remote-source.ts} +51 -171
- package/src/widget-sources/widget-source.ts +173 -0
- package/src/widget-sources/widget-table-source.ts +12 -5
- package/src/widget-sources/widget-tileset-source.ts +456 -0
package/src/models/common.ts
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import {InvalidColumnError} from '../utils.js';
|
|
2
2
|
|
|
3
|
-
/** @
|
|
3
|
+
/** @privateRemarks Source: @carto/react-api */
|
|
4
4
|
export interface ModelRequestOptions {
|
|
5
5
|
method: 'GET' | 'POST';
|
|
6
|
-
|
|
6
|
+
headers?: Record<string, string>;
|
|
7
|
+
signal?: AbortSignal;
|
|
7
8
|
otherOptions?: Record<string, unknown>;
|
|
8
9
|
body?: string;
|
|
9
10
|
}
|
|
@@ -16,7 +17,7 @@ interface ModelErrorResponse {
|
|
|
16
17
|
|
|
17
18
|
/**
|
|
18
19
|
* Return more descriptive error from API
|
|
19
|
-
* @
|
|
20
|
+
* @privateRemarks Source: @carto/react-api
|
|
20
21
|
*/
|
|
21
22
|
export function dealWithApiError({
|
|
22
23
|
response,
|
|
@@ -42,15 +43,15 @@ export function dealWithApiError({
|
|
|
42
43
|
case 403:
|
|
43
44
|
throw new Error('Forbidden access to the requested data');
|
|
44
45
|
default:
|
|
45
|
-
|
|
46
|
+
throw new Error(
|
|
46
47
|
data && data.error && typeof data.error === 'string'
|
|
47
48
|
? data.error
|
|
48
|
-
: JSON.stringify(data?.hint || data.error?.[0])
|
|
49
|
-
|
|
49
|
+
: JSON.stringify(data?.hint || data.error?.[0])
|
|
50
|
+
);
|
|
50
51
|
}
|
|
51
52
|
}
|
|
52
53
|
|
|
53
|
-
/** @
|
|
54
|
+
/** @privateRemarks Source: @carto/react-api */
|
|
54
55
|
export async function makeCall({
|
|
55
56
|
url,
|
|
56
57
|
accessToken,
|
|
@@ -68,19 +69,20 @@ export async function makeCall({
|
|
|
68
69
|
headers: {
|
|
69
70
|
Authorization: `Bearer ${accessToken}`,
|
|
70
71
|
...(isPost && {'Content-Type': 'application/json'}),
|
|
72
|
+
...opts.headers,
|
|
71
73
|
},
|
|
72
74
|
...(isPost && {
|
|
73
75
|
method: opts?.method,
|
|
74
76
|
body: opts?.body,
|
|
75
77
|
}),
|
|
76
|
-
signal: opts?.
|
|
78
|
+
signal: opts?.signal,
|
|
77
79
|
...opts?.otherOptions,
|
|
78
80
|
});
|
|
79
81
|
data = await response.json();
|
|
80
82
|
} catch (error) {
|
|
81
83
|
if ((error as Error).name === 'AbortError') throw error;
|
|
82
84
|
|
|
83
|
-
throw new Error(`Failed request: ${error}`);
|
|
85
|
+
throw new Error(`Failed request: ${error as Error}`);
|
|
84
86
|
}
|
|
85
87
|
|
|
86
88
|
if (!response.ok) {
|
package/src/models/index.ts
CHANGED
package/src/models/model.ts
CHANGED
|
@@ -6,13 +6,12 @@ import {
|
|
|
6
6
|
QueryParameters,
|
|
7
7
|
SpatialFilter,
|
|
8
8
|
} from '../types.js';
|
|
9
|
-
import {$TODO} from '../types-internal.js';
|
|
10
9
|
import {assert, isPureObject} from '../utils.js';
|
|
11
10
|
import {ModelRequestOptions, makeCall} from './common.js';
|
|
12
11
|
import {ApiVersion} from '../constants.js';
|
|
13
12
|
import {SpatialDataType, SpatialFilterPolyfillMode} from '../sources/types.js';
|
|
14
13
|
|
|
15
|
-
/** @
|
|
14
|
+
/** @privateRemarks Source: @carto/react-api */
|
|
16
15
|
const AVAILABLE_MODELS = [
|
|
17
16
|
'category',
|
|
18
17
|
'histogram',
|
|
@@ -51,7 +50,7 @@ const REQUEST_GET_MAX_URL_LENGTH = 2048;
|
|
|
51
50
|
|
|
52
51
|
/**
|
|
53
52
|
* Execute a SQL model request.
|
|
54
|
-
* @
|
|
53
|
+
* @privateRemarks Source: @carto/react-api
|
|
55
54
|
*/
|
|
56
55
|
export function executeModel(props: {
|
|
57
56
|
model: Model;
|
|
@@ -152,7 +151,7 @@ function objectToURLSearchParams(object: Record<string, unknown>) {
|
|
|
152
151
|
} else if (object[key] === null) {
|
|
153
152
|
params.append(key, 'null');
|
|
154
153
|
} else if (object[key] !== undefined) {
|
|
155
|
-
params.append(key, String(object[key]));
|
|
154
|
+
params.append(key, String(object[key] as unknown));
|
|
156
155
|
}
|
|
157
156
|
}
|
|
158
157
|
return params;
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
import {AggregationType} from '../types.js';
|
|
2
|
+
import {FeatureData} from '../types-internal.js';
|
|
3
|
+
|
|
4
|
+
/** @privateRemarks Source: @carto/react-core */
|
|
5
|
+
export type AggregationFunction = (
|
|
6
|
+
values: unknown[] | FeatureData[],
|
|
7
|
+
keys?: string[] | string,
|
|
8
|
+
joinOperation?: AggregationType
|
|
9
|
+
) => number;
|
|
10
|
+
|
|
11
|
+
/** @privateRemarks Source: @carto/react-core */
|
|
12
|
+
export const aggregationFunctions: Record<
|
|
13
|
+
Exclude<AggregationType, 'custom'>,
|
|
14
|
+
AggregationFunction
|
|
15
|
+
> = {
|
|
16
|
+
count: (values) => values.length,
|
|
17
|
+
min: (...args) => applyAggregationFunction(min, ...args),
|
|
18
|
+
max: (...args) => applyAggregationFunction(max, ...args),
|
|
19
|
+
sum: (...args) => applyAggregationFunction(sum, ...args),
|
|
20
|
+
avg: (...args) => applyAggregationFunction(avg, ...args),
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
/** @privateRemarks Source: @carto/react-core */
|
|
24
|
+
export function aggregate(
|
|
25
|
+
feature: FeatureData,
|
|
26
|
+
keys?: string[],
|
|
27
|
+
operation?: AggregationType
|
|
28
|
+
): unknown {
|
|
29
|
+
if (!keys?.length) {
|
|
30
|
+
throw new Error('Cannot aggregate a feature without having keys');
|
|
31
|
+
} else if (keys.length === 1) {
|
|
32
|
+
const value = feature[keys[0]];
|
|
33
|
+
return isPotentiallyValidNumber(value) ? Number(value) : value;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const aggregationFn =
|
|
37
|
+
aggregationFunctions[operation as Exclude<AggregationType, 'custom'>];
|
|
38
|
+
|
|
39
|
+
if (!aggregationFn) {
|
|
40
|
+
throw new Error(`${operation} isn't a valid aggregation function`);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
return aggregationFn(
|
|
44
|
+
keys.map((column) => {
|
|
45
|
+
const value = feature[column];
|
|
46
|
+
return isPotentiallyValidNumber(value) ? Number(value) : value;
|
|
47
|
+
})
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/*
|
|
52
|
+
* Forced casting to Number (just of non empty strings) allows to work-around
|
|
53
|
+
* some specific situations, where a big numeric field is transformed into a string when generating the tileset(eg.PG)
|
|
54
|
+
*/
|
|
55
|
+
function isPotentiallyValidNumber(value: unknown): boolean {
|
|
56
|
+
return typeof value === 'string' && value.trim().length > 0;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const applyAggregationFunction = (
|
|
60
|
+
aggFn: AggregationFunction,
|
|
61
|
+
values: unknown[] | FeatureData[],
|
|
62
|
+
keys?: string[] | string,
|
|
63
|
+
operation?: AggregationType
|
|
64
|
+
) => {
|
|
65
|
+
const normalizedKeys = normalizeKeys(keys);
|
|
66
|
+
const elements =
|
|
67
|
+
(normalizedKeys?.length || 0) <= 1
|
|
68
|
+
? filterFalsyElements(values as unknown[], normalizedKeys || [])
|
|
69
|
+
: values;
|
|
70
|
+
return aggFn(elements, keys, operation);
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
function filterFalsyElements(
|
|
74
|
+
values: unknown[] | FeatureData[],
|
|
75
|
+
keys: string[]
|
|
76
|
+
) {
|
|
77
|
+
const filterFn = (value: unknown) => value !== null && value !== undefined;
|
|
78
|
+
|
|
79
|
+
if (!keys?.length) {
|
|
80
|
+
return values.filter(filterFn);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
return (values as FeatureData[]).filter((v) => filterFn(v[keys[0]]));
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// Aggregation functions
|
|
87
|
+
function avg(
|
|
88
|
+
values: unknown[] | FeatureData[],
|
|
89
|
+
keys?: string[] | string,
|
|
90
|
+
joinOperation?: AggregationType
|
|
91
|
+
): number {
|
|
92
|
+
return sum(values, keys, joinOperation) / (values.length || 1);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function sum(
|
|
96
|
+
values: unknown[] | FeatureData[],
|
|
97
|
+
keys?: string[] | string,
|
|
98
|
+
joinOperation?: AggregationType
|
|
99
|
+
): number {
|
|
100
|
+
const normalizedKeys = normalizeKeys(keys);
|
|
101
|
+
|
|
102
|
+
if (normalizedKeys) {
|
|
103
|
+
return (values as FeatureData[]).reduce(
|
|
104
|
+
(a, b) => a + (aggregate(b, normalizedKeys, joinOperation) as number),
|
|
105
|
+
0
|
|
106
|
+
);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
return values.reduce((a: number, b: unknown) => a + (b as number), 0);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function min(
|
|
113
|
+
values: unknown[] | FeatureData[],
|
|
114
|
+
keys?: string[] | string,
|
|
115
|
+
joinOperation?: AggregationType
|
|
116
|
+
): number {
|
|
117
|
+
const normalizedKeys = normalizeKeys(keys);
|
|
118
|
+
if (normalizedKeys) {
|
|
119
|
+
return (values as FeatureData[]).reduce(
|
|
120
|
+
(a, b) =>
|
|
121
|
+
Math.min(a, aggregate(b, normalizedKeys, joinOperation) as number),
|
|
122
|
+
Infinity
|
|
123
|
+
);
|
|
124
|
+
}
|
|
125
|
+
return Math.min(...(values as number[]));
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
function max(
|
|
129
|
+
values: unknown[] | FeatureData[],
|
|
130
|
+
keys?: string[] | string,
|
|
131
|
+
joinOperation?: AggregationType
|
|
132
|
+
): number {
|
|
133
|
+
const normalizedKeys = normalizeKeys(keys);
|
|
134
|
+
if (normalizedKeys) {
|
|
135
|
+
return (values as FeatureData[]).reduce(
|
|
136
|
+
(a, b) =>
|
|
137
|
+
Math.max(a, aggregate(b, normalizedKeys, joinOperation) as number),
|
|
138
|
+
-Infinity
|
|
139
|
+
);
|
|
140
|
+
}
|
|
141
|
+
return Math.max(...(values as number[]));
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// Aux
|
|
145
|
+
|
|
146
|
+
// Keys can come as a string (one column) or a strings array (multiple column)
|
|
147
|
+
// Use always an array to make the code easier
|
|
148
|
+
function normalizeKeys(keys: unknown): string[] | undefined {
|
|
149
|
+
return Array.isArray(keys)
|
|
150
|
+
? keys
|
|
151
|
+
: typeof keys === 'string'
|
|
152
|
+
? [keys]
|
|
153
|
+
: undefined;
|
|
154
|
+
}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import {firstBy} from 'thenby';
|
|
2
|
+
import {SortDirection} from '../types.js';
|
|
3
|
+
import {FeatureData} from '../types-internal.js';
|
|
4
|
+
|
|
5
|
+
// TODO(cleanup): Could this be simplified?
|
|
6
|
+
type SortColumns = string | string[] | object[];
|
|
7
|
+
|
|
8
|
+
interface SortOptions {
|
|
9
|
+
sortBy?: SortColumns;
|
|
10
|
+
sortByDirection?: SortDirection;
|
|
11
|
+
sortByColumnType?: 'number' | 'string' | 'date';
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Apply sort structure to a collection of features
|
|
16
|
+
* @param features
|
|
17
|
+
* @param [sortOptions]
|
|
18
|
+
* @param [sortOptions.sortBy] - One or more columns to sort by
|
|
19
|
+
* @param [sortOptions.sortByDirection] - Direction by the columns will be sorted
|
|
20
|
+
* @param [sortOptions.sortByColumnType] - Column type
|
|
21
|
+
* @internal
|
|
22
|
+
* @privateRemarks Source: @carto/react-core
|
|
23
|
+
*/
|
|
24
|
+
export function applySorting(
|
|
25
|
+
features: FeatureData[],
|
|
26
|
+
{
|
|
27
|
+
sortBy,
|
|
28
|
+
sortByDirection = 'asc',
|
|
29
|
+
sortByColumnType = 'string',
|
|
30
|
+
}: SortOptions = {}
|
|
31
|
+
): FeatureData[] {
|
|
32
|
+
// If sortBy is undefined, pass all features
|
|
33
|
+
if (sortBy === undefined) {
|
|
34
|
+
return features;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// sortOptions exists, but are bad formatted
|
|
38
|
+
const isValidSortBy =
|
|
39
|
+
(Array.isArray(sortBy) && sortBy.length) || // sortBy can be an array of columns
|
|
40
|
+
typeof sortBy === 'string'; // or just one column
|
|
41
|
+
|
|
42
|
+
if (!isValidSortBy) {
|
|
43
|
+
throw new Error('Sorting options are bad formatted');
|
|
44
|
+
}
|
|
45
|
+
const sortFn = createSortFn({
|
|
46
|
+
sortBy,
|
|
47
|
+
sortByDirection,
|
|
48
|
+
sortByColumnType: sortByColumnType || 'string',
|
|
49
|
+
});
|
|
50
|
+
return features.sort(sortFn);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Aux
|
|
54
|
+
function createSortFn({
|
|
55
|
+
sortBy,
|
|
56
|
+
sortByDirection,
|
|
57
|
+
sortByColumnType,
|
|
58
|
+
}: Required<SortOptions>) {
|
|
59
|
+
const [firstSortOption, ...othersSortOptions] = normalizeSortByOptions({
|
|
60
|
+
sortBy,
|
|
61
|
+
sortByDirection,
|
|
62
|
+
sortByColumnType,
|
|
63
|
+
}) as Parameters<typeof firstBy>[];
|
|
64
|
+
|
|
65
|
+
let sortFn = firstBy(...firstSortOption);
|
|
66
|
+
for (const sortOptions of othersSortOptions) {
|
|
67
|
+
sortFn = sortFn.thenBy(...sortOptions);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
return sortFn;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function normalizeSortByOptions({
|
|
74
|
+
sortBy,
|
|
75
|
+
sortByDirection,
|
|
76
|
+
sortByColumnType,
|
|
77
|
+
}: Required<SortOptions>) {
|
|
78
|
+
const numberFormat = sortByColumnType === 'number' && {
|
|
79
|
+
cmp: (a: number, b: number) => a - b,
|
|
80
|
+
};
|
|
81
|
+
if (!Array.isArray(sortBy)) {
|
|
82
|
+
sortBy = [sortBy];
|
|
83
|
+
}
|
|
84
|
+
return sortBy.map((sortByEl) => {
|
|
85
|
+
// sortByEl is 'column'
|
|
86
|
+
if (typeof sortByEl === 'string') {
|
|
87
|
+
return [sortByEl, {direction: sortByDirection, ...numberFormat}];
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
if (Array.isArray(sortByEl)) {
|
|
91
|
+
// sortBy is ['column']
|
|
92
|
+
if (sortByEl[1] === undefined) {
|
|
93
|
+
return [sortByEl, {direction: sortByDirection, ...numberFormat}];
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// sortBy is ['column', { ... }]
|
|
97
|
+
if (typeof sortByEl[1] === 'object') {
|
|
98
|
+
const othersSortOptions = numberFormat
|
|
99
|
+
? {...numberFormat, ...sortByEl[1]}
|
|
100
|
+
: sortByEl[1];
|
|
101
|
+
return [
|
|
102
|
+
sortByEl[0],
|
|
103
|
+
{direction: sortByDirection, ...othersSortOptions},
|
|
104
|
+
];
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
return sortByEl;
|
|
108
|
+
});
|
|
109
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import {aggregationFunctions, aggregate} from './aggregation.js';
|
|
2
|
+
import {AggregationType} from '../types.js';
|
|
3
|
+
import {FeatureData} from '../types-internal.js';
|
|
4
|
+
|
|
5
|
+
/** @privateRemarks Source: @carto/react-core */
|
|
6
|
+
export type GroupByFeature = {
|
|
7
|
+
name: string;
|
|
8
|
+
value: number;
|
|
9
|
+
}[];
|
|
10
|
+
|
|
11
|
+
/** @privateRemarks Source: @carto/react-core */
|
|
12
|
+
export function groupValuesByColumn({
|
|
13
|
+
data,
|
|
14
|
+
valuesColumns,
|
|
15
|
+
joinOperation,
|
|
16
|
+
keysColumn,
|
|
17
|
+
operation,
|
|
18
|
+
}: {
|
|
19
|
+
data: FeatureData[];
|
|
20
|
+
valuesColumns?: string[];
|
|
21
|
+
joinOperation?: AggregationType;
|
|
22
|
+
keysColumn: string;
|
|
23
|
+
operation: AggregationType;
|
|
24
|
+
}): GroupByFeature | null {
|
|
25
|
+
if (Array.isArray(data) && data.length === 0) {
|
|
26
|
+
return null;
|
|
27
|
+
}
|
|
28
|
+
const groups = data.reduce((accumulator, item) => {
|
|
29
|
+
const group = item[keysColumn];
|
|
30
|
+
|
|
31
|
+
const values = accumulator.get(group) || [];
|
|
32
|
+
accumulator.set(group, values);
|
|
33
|
+
|
|
34
|
+
const aggregatedValue = aggregate(item, valuesColumns, joinOperation);
|
|
35
|
+
|
|
36
|
+
const isValid =
|
|
37
|
+
(operation === 'count' ? true : aggregatedValue !== null) &&
|
|
38
|
+
aggregatedValue !== undefined;
|
|
39
|
+
|
|
40
|
+
if (isValid) {
|
|
41
|
+
values.push(aggregatedValue);
|
|
42
|
+
accumulator.set(group, values);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return accumulator;
|
|
46
|
+
}, new Map()); // We use a map to be able to maintain the type in the key value
|
|
47
|
+
|
|
48
|
+
const targetOperation =
|
|
49
|
+
aggregationFunctions[operation as Exclude<AggregationType, 'custom'>];
|
|
50
|
+
|
|
51
|
+
if (targetOperation) {
|
|
52
|
+
return Array.from(groups).map(([name, value]) => ({
|
|
53
|
+
name,
|
|
54
|
+
value: targetOperation(value),
|
|
55
|
+
}));
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
return [];
|
|
59
|
+
}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import {AggregationType, GroupDateType} from '../types.js';
|
|
2
|
+
import {getUTCMonday} from '../utils/dateUtils.js';
|
|
3
|
+
import {aggregate, aggregationFunctions} from './aggregation.js';
|
|
4
|
+
import {GroupByFeature} from './groupBy.js';
|
|
5
|
+
|
|
6
|
+
const GROUP_KEY_FN_MAPPING: Record<GroupDateType, (date: Date) => number> = {
|
|
7
|
+
year: (date: Date) => Date.UTC(date.getUTCFullYear()),
|
|
8
|
+
month: (date: Date) => Date.UTC(date.getUTCFullYear(), date.getUTCMonth()),
|
|
9
|
+
week: (date: Date) => getUTCMonday(date),
|
|
10
|
+
day: (date: Date) =>
|
|
11
|
+
Date.UTC(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate()),
|
|
12
|
+
hour: (date: Date) =>
|
|
13
|
+
Date.UTC(
|
|
14
|
+
date.getUTCFullYear(),
|
|
15
|
+
date.getUTCMonth(),
|
|
16
|
+
date.getUTCDate(),
|
|
17
|
+
date.getUTCHours()
|
|
18
|
+
),
|
|
19
|
+
minute: (date: Date) =>
|
|
20
|
+
Date.UTC(
|
|
21
|
+
date.getUTCFullYear(),
|
|
22
|
+
date.getUTCMonth(),
|
|
23
|
+
date.getUTCDate(),
|
|
24
|
+
date.getUTCHours(),
|
|
25
|
+
date.getUTCMinutes()
|
|
26
|
+
),
|
|
27
|
+
second: (date: Date) =>
|
|
28
|
+
Date.UTC(
|
|
29
|
+
date.getUTCFullYear(),
|
|
30
|
+
date.getUTCMonth(),
|
|
31
|
+
date.getUTCDate(),
|
|
32
|
+
date.getUTCHours(),
|
|
33
|
+
date.getUTCMinutes(),
|
|
34
|
+
date.getUTCSeconds()
|
|
35
|
+
),
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
/** @privateRemarks Source: @carto/react-core */
|
|
39
|
+
export function groupValuesByDateColumn({
|
|
40
|
+
data,
|
|
41
|
+
valuesColumns,
|
|
42
|
+
joinOperation,
|
|
43
|
+
keysColumn,
|
|
44
|
+
groupType,
|
|
45
|
+
operation,
|
|
46
|
+
}: {
|
|
47
|
+
data: Record<string, unknown>[];
|
|
48
|
+
valuesColumns?: string[];
|
|
49
|
+
joinOperation?: Exclude<AggregationType, 'custom'>;
|
|
50
|
+
keysColumn: string;
|
|
51
|
+
groupType: GroupDateType;
|
|
52
|
+
operation?: Exclude<AggregationType, 'custom'>;
|
|
53
|
+
}): GroupByFeature | null {
|
|
54
|
+
if (Array.isArray(data) && data.length === 0) {
|
|
55
|
+
return null;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const groupKeyFn = GROUP_KEY_FN_MAPPING[groupType];
|
|
59
|
+
|
|
60
|
+
if (!groupKeyFn) {
|
|
61
|
+
return null;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const groups = data.reduce((acc, item) => {
|
|
65
|
+
const value = item[keysColumn];
|
|
66
|
+
const formattedValue = new Date(value as number);
|
|
67
|
+
const groupKey = groupKeyFn(formattedValue);
|
|
68
|
+
|
|
69
|
+
if (!isNaN(groupKey)) {
|
|
70
|
+
let groupedValues = acc.get(groupKey);
|
|
71
|
+
if (!groupedValues) {
|
|
72
|
+
groupedValues = [];
|
|
73
|
+
acc.set(groupKey, groupedValues);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const aggregatedValue = aggregate(item, valuesColumns, joinOperation);
|
|
77
|
+
|
|
78
|
+
const isValid = aggregatedValue !== null && aggregatedValue !== undefined;
|
|
79
|
+
|
|
80
|
+
if (isValid) {
|
|
81
|
+
groupedValues.push(aggregatedValue);
|
|
82
|
+
acc.set(groupKey, groupedValues);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
return acc;
|
|
87
|
+
}, new Map());
|
|
88
|
+
|
|
89
|
+
const targetOperation =
|
|
90
|
+
aggregationFunctions[operation as Exclude<AggregationType, 'custom'>];
|
|
91
|
+
|
|
92
|
+
return [...groups.entries()]
|
|
93
|
+
.map(([name, value]) => ({
|
|
94
|
+
name,
|
|
95
|
+
value: targetOperation(value),
|
|
96
|
+
}))
|
|
97
|
+
.sort((a, b) => a.name - b.name);
|
|
98
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import {aggregate, aggregationFunctions} from './aggregation.js';
|
|
2
|
+
import {AggregationType} from '../types.js';
|
|
3
|
+
import {FeatureData} from '../types-internal.js';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Histogram computation.
|
|
7
|
+
* @privateRemarks Source: @carto/react-core
|
|
8
|
+
*/
|
|
9
|
+
export function histogram({
|
|
10
|
+
data,
|
|
11
|
+
valuesColumns,
|
|
12
|
+
joinOperation,
|
|
13
|
+
ticks,
|
|
14
|
+
operation,
|
|
15
|
+
}: {
|
|
16
|
+
data: FeatureData[];
|
|
17
|
+
valuesColumns?: string[];
|
|
18
|
+
joinOperation?: Exclude<AggregationType, 'custom'>;
|
|
19
|
+
ticks: number[];
|
|
20
|
+
operation: Exclude<AggregationType, 'custom'>;
|
|
21
|
+
}): number[] {
|
|
22
|
+
if (Array.isArray(data) && data.length === 0) {
|
|
23
|
+
return [];
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const binsContainer = [Number.MIN_SAFE_INTEGER, ...ticks].map(
|
|
27
|
+
(tick, index, arr) => ({
|
|
28
|
+
bin: index,
|
|
29
|
+
start: tick,
|
|
30
|
+
end: index === arr.length - 1 ? Number.MAX_SAFE_INTEGER : arr[index + 1],
|
|
31
|
+
values: [] as number[],
|
|
32
|
+
})
|
|
33
|
+
);
|
|
34
|
+
|
|
35
|
+
data.forEach((feature) => {
|
|
36
|
+
const featureValue = aggregate(
|
|
37
|
+
feature,
|
|
38
|
+
valuesColumns,
|
|
39
|
+
joinOperation
|
|
40
|
+
) as number;
|
|
41
|
+
|
|
42
|
+
const isValid = featureValue !== null && featureValue !== undefined;
|
|
43
|
+
|
|
44
|
+
if (!isValid) {
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const binContainer = binsContainer.find(
|
|
49
|
+
(bin) => bin.start <= featureValue && bin.end > featureValue
|
|
50
|
+
);
|
|
51
|
+
|
|
52
|
+
if (!binContainer) {
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
binContainer.values.push(featureValue);
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
const targetOperation = aggregationFunctions[operation];
|
|
60
|
+
const transformedBins = binsContainer.map(
|
|
61
|
+
(binContainer) => binContainer.values
|
|
62
|
+
);
|
|
63
|
+
return transformedBins.map((values) =>
|
|
64
|
+
values.length ? targetOperation(values) : 0
|
|
65
|
+
);
|
|
66
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import {aggregate} from './aggregation.js';
|
|
2
|
+
import {FeatureData} from '../types-internal.js';
|
|
3
|
+
import {AggregationType} from '../types.js';
|
|
4
|
+
|
|
5
|
+
export type ScatterPlotFeature = [number, number][];
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Filters invalid features and formats data.
|
|
9
|
+
* @privateRemarks Source: @carto/react-core
|
|
10
|
+
*/
|
|
11
|
+
export function scatterPlot({
|
|
12
|
+
data,
|
|
13
|
+
xAxisColumns,
|
|
14
|
+
xAxisJoinOperation,
|
|
15
|
+
yAxisColumns,
|
|
16
|
+
yAxisJoinOperation,
|
|
17
|
+
}: {
|
|
18
|
+
data: FeatureData[];
|
|
19
|
+
xAxisColumns: string[];
|
|
20
|
+
xAxisJoinOperation?: AggregationType;
|
|
21
|
+
yAxisColumns: string[];
|
|
22
|
+
yAxisJoinOperation?: AggregationType;
|
|
23
|
+
}): ScatterPlotFeature {
|
|
24
|
+
return data.reduce(
|
|
25
|
+
(acc, feature) => {
|
|
26
|
+
const xValue = aggregate(
|
|
27
|
+
feature,
|
|
28
|
+
xAxisColumns,
|
|
29
|
+
xAxisJoinOperation
|
|
30
|
+
) as number;
|
|
31
|
+
|
|
32
|
+
const xIsValid = xValue !== null && xValue !== undefined;
|
|
33
|
+
|
|
34
|
+
const yValue = aggregate(
|
|
35
|
+
feature,
|
|
36
|
+
yAxisColumns,
|
|
37
|
+
yAxisJoinOperation
|
|
38
|
+
) as number;
|
|
39
|
+
|
|
40
|
+
const yIsValid = yValue !== null && yValue !== undefined;
|
|
41
|
+
|
|
42
|
+
if (xIsValid && yIsValid) {
|
|
43
|
+
acc.push([xValue, yValue]);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
return acc;
|
|
47
|
+
},
|
|
48
|
+
[] as [number, number][]
|
|
49
|
+
);
|
|
50
|
+
}
|
|
@@ -2,10 +2,10 @@
|
|
|
2
2
|
// SPDX-License-Identifier: MIT
|
|
3
3
|
// Copyright (c) vis.gl contributors
|
|
4
4
|
|
|
5
|
-
import {DEFAULT_API_BASE_URL} from '../constants';
|
|
6
|
-
import {DEFAULT_MAX_LENGTH_URL} from '../constants-internal';
|
|
7
|
-
import {buildSourceUrl} from '../api/endpoints';
|
|
8
|
-
import {requestWithParameters} from '../api/request-with-parameters';
|
|
5
|
+
import {DEFAULT_API_BASE_URL} from '../constants.js';
|
|
6
|
+
import {DEFAULT_MAX_LENGTH_URL} from '../constants-internal.js';
|
|
7
|
+
import {buildSourceUrl} from '../api/endpoints.js';
|
|
8
|
+
import {requestWithParameters} from '../api/request-with-parameters.js';
|
|
9
9
|
import type {
|
|
10
10
|
GeojsonResult,
|
|
11
11
|
JsonResult,
|
|
@@ -13,10 +13,10 @@ import type {
|
|
|
13
13
|
SourceRequiredOptions,
|
|
14
14
|
TilejsonMapInstantiation,
|
|
15
15
|
TilejsonResult,
|
|
16
|
-
} from './types';
|
|
17
|
-
import {MapType} from '../types';
|
|
18
|
-
import {APIErrorContext} from '../api';
|
|
19
|
-
import {getClient} from '../client';
|
|
16
|
+
} from './types.js';
|
|
17
|
+
import {MapType} from '../types.js';
|
|
18
|
+
import {APIErrorContext} from '../api/index.js';
|
|
19
|
+
import {getClient} from '../client.js';
|
|
20
20
|
|
|
21
21
|
export const SOURCE_DEFAULTS: SourceOptionalOptions = {
|
|
22
22
|
apiBaseUrl: DEFAULT_API_BASE_URL,
|
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
// Copyright (c) vis.gl contributors
|
|
4
4
|
|
|
5
5
|
import {QueryParameters} from '../types.js';
|
|
6
|
-
import {baseSource} from './base-source';
|
|
7
|
-
import type {FilterOptions, SourceOptions, TilejsonResult} from './types';
|
|
6
|
+
import {baseSource} from './base-source.js';
|
|
7
|
+
import type {FilterOptions, SourceOptions, TilejsonResult} from './types.js';
|
|
8
8
|
|
|
9
9
|
export type BoundaryQuerySourceOptions = SourceOptions &
|
|
10
10
|
FilterOptions & {
|