@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.
Files changed (124) hide show
  1. package/CHANGELOG.md +17 -1
  2. package/build/api/carto-api-error.d.ts +1 -1
  3. package/build/api/query.d.ts +1 -1
  4. package/build/api/request-with-parameters.d.ts +2 -2
  5. package/build/api-client.cjs +2365 -279
  6. package/build/api-client.cjs.map +1 -1
  7. package/build/api-client.modern.js +2274 -298
  8. package/build/api-client.modern.js.map +1 -1
  9. package/build/client.d.ts +2 -2
  10. package/build/constants-internal.d.ts +5 -5
  11. package/build/constants.d.ts +25 -3
  12. package/build/deck/get-data-filter-extension-props.d.ts +28 -0
  13. package/build/deck/index.d.ts +1 -0
  14. package/build/filters/Filter.d.ts +25 -0
  15. package/build/filters/FilterTypes.d.ts +3 -0
  16. package/build/filters/geosjonFeatures.d.ts +8 -0
  17. package/build/filters/index.d.ts +6 -0
  18. package/build/filters/tileFeatures.d.ts +20 -0
  19. package/build/filters/tileFeaturesGeometries.d.ts +13 -0
  20. package/build/filters/tileFeaturesSpatialIndex.d.ts +10 -0
  21. package/build/filters.d.ts +2 -2
  22. package/build/geo.d.ts +1 -1
  23. package/build/index.d.ts +5 -0
  24. package/build/models/common.d.ts +5 -4
  25. package/build/models/index.d.ts +1 -1
  26. package/build/models/model.d.ts +2 -2
  27. package/build/operations/aggregation.d.ts +8 -0
  28. package/build/operations/applySorting.d.ts +20 -0
  29. package/build/operations/groupBy.d.ts +15 -0
  30. package/build/operations/groupByDate.d.ts +11 -0
  31. package/build/operations/histogram.d.ts +13 -0
  32. package/build/operations/index.d.ts +6 -0
  33. package/build/operations/scatterPlot.d.ts +14 -0
  34. package/build/sources/base-source.d.ts +2 -2
  35. package/build/sources/boundary-query-source.d.ts +1 -1
  36. package/build/sources/boundary-table-source.d.ts +1 -1
  37. package/build/sources/h3-query-source.d.ts +2 -2
  38. package/build/sources/h3-table-source.d.ts +2 -2
  39. package/build/sources/h3-tileset-source.d.ts +1 -1
  40. package/build/sources/index.d.ts +26 -26
  41. package/build/sources/quadbin-query-source.d.ts +2 -2
  42. package/build/sources/quadbin-table-source.d.ts +2 -2
  43. package/build/sources/quadbin-tileset-source.d.ts +1 -1
  44. package/build/sources/raster-source.d.ts +1 -1
  45. package/build/sources/types.d.ts +3 -3
  46. package/build/sources/vector-query-source.d.ts +1 -1
  47. package/build/sources/vector-table-source.d.ts +1 -1
  48. package/build/sources/vector-tileset-source.d.ts +1 -1
  49. package/build/spatial-index.d.ts +3 -3
  50. package/build/types-internal.d.ts +9 -5
  51. package/build/types.d.ts +74 -14
  52. package/build/utils/dateUtils.d.ts +10 -0
  53. package/build/utils/getTileFormat.d.ts +3 -0
  54. package/build/utils/makeIntervalComplete.d.ts +2 -0
  55. package/build/utils/transformTileCoordsToWGS84.d.ts +8 -0
  56. package/build/utils/transformToTileCoords.d.ts +9 -0
  57. package/build/utils.d.ts +3 -3
  58. package/build/widget-sources/index.d.ts +3 -1
  59. package/build/widget-sources/types.d.ts +38 -25
  60. package/build/widget-sources/widget-query-source.d.ts +4 -3
  61. package/build/widget-sources/widget-remote-source.d.ts +18 -0
  62. package/build/widget-sources/{widget-base-source.d.ts → widget-source.d.ts} +16 -41
  63. package/build/widget-sources/widget-table-source.d.ts +4 -3
  64. package/build/widget-sources/widget-tileset-source.d.ts +75 -0
  65. package/package.json +46 -29
  66. package/src/api/carto-api-error.ts +1 -1
  67. package/src/api/query.ts +5 -5
  68. package/src/api/request-with-parameters.ts +6 -6
  69. package/src/client.ts +3 -3
  70. package/src/constants-internal.ts +5 -5
  71. package/src/constants.ts +28 -3
  72. package/src/deck/get-data-filter-extension-props.ts +164 -0
  73. package/src/deck/index.ts +1 -0
  74. package/src/filters/Filter.ts +179 -0
  75. package/src/filters/FilterTypes.ts +109 -0
  76. package/src/filters/geosjonFeatures.ts +32 -0
  77. package/src/filters/index.ts +6 -0
  78. package/src/filters/tileFeatures.ts +50 -0
  79. package/src/filters/tileFeaturesGeometries.ts +444 -0
  80. package/src/filters/tileFeaturesSpatialIndex.ts +119 -0
  81. package/src/filters.ts +4 -4
  82. package/src/geo.ts +12 -14
  83. package/src/index.ts +7 -0
  84. package/src/models/common.ts +11 -9
  85. package/src/models/index.ts +1 -1
  86. package/src/models/model.ts +3 -4
  87. package/src/operations/aggregation.ts +154 -0
  88. package/src/operations/applySorting.ts +109 -0
  89. package/src/operations/groupBy.ts +59 -0
  90. package/src/operations/groupByDate.ts +98 -0
  91. package/src/operations/histogram.ts +66 -0
  92. package/src/operations/index.ts +6 -0
  93. package/src/operations/scatterPlot.ts +50 -0
  94. package/src/sources/base-source.ts +8 -8
  95. package/src/sources/boundary-query-source.ts +2 -2
  96. package/src/sources/boundary-table-source.ts +2 -2
  97. package/src/sources/h3-query-source.ts +7 -5
  98. package/src/sources/h3-table-source.ts +7 -5
  99. package/src/sources/h3-tileset-source.ts +2 -2
  100. package/src/sources/index.ts +26 -26
  101. package/src/sources/quadbin-query-source.ts +7 -5
  102. package/src/sources/quadbin-table-source.ts +7 -5
  103. package/src/sources/quadbin-tileset-source.ts +2 -2
  104. package/src/sources/raster-source.ts +3 -2
  105. package/src/sources/types.ts +3 -3
  106. package/src/sources/vector-query-source.ts +7 -5
  107. package/src/sources/vector-table-source.ts +7 -5
  108. package/src/sources/vector-tileset-source.ts +2 -2
  109. package/src/spatial-index.ts +4 -5
  110. package/src/types-internal.ts +11 -5
  111. package/src/types.ts +73 -15
  112. package/src/utils/dateUtils.ts +28 -0
  113. package/src/utils/getTileFormat.ts +9 -0
  114. package/src/utils/makeIntervalComplete.ts +17 -0
  115. package/src/utils/transformTileCoordsToWGS84.ts +77 -0
  116. package/src/utils/transformToTileCoords.ts +85 -0
  117. package/src/utils.ts +3 -3
  118. package/src/widget-sources/index.ts +3 -1
  119. package/src/widget-sources/types.ts +39 -25
  120. package/src/widget-sources/widget-query-source.ts +12 -5
  121. package/src/widget-sources/{widget-base-source.ts → widget-remote-source.ts} +51 -171
  122. package/src/widget-sources/widget-source.ts +173 -0
  123. package/src/widget-sources/widget-table-source.ts +12 -5
  124. package/src/widget-sources/widget-tileset-source.ts +456 -0
@@ -1,9 +1,10 @@
1
1
  import {InvalidColumnError} from '../utils.js';
2
2
 
3
- /** @internalRemarks Source: @carto/react-api */
3
+ /** @privateRemarks Source: @carto/react-api */
4
4
  export interface ModelRequestOptions {
5
5
  method: 'GET' | 'POST';
6
- abortController?: AbortController;
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
- * @internalRemarks Source: @carto/react-api
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
- const msg =
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
- throw new Error(msg);
49
+ : JSON.stringify(data?.hint || data.error?.[0])
50
+ );
50
51
  }
51
52
  }
52
53
 
53
- /** @internalRemarks Source: @carto/react-api */
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?.abortController?.signal,
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) {
@@ -1,3 +1,3 @@
1
1
  export {executeModel} from './model.js';
2
- export type {Model} from './model.js';
2
+ export type {Model, ModelSource} from './model.js';
3
3
  export type {ModelRequestOptions} from './common.js';
@@ -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
- /** @internalRemarks Source: @carto/react-api */
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
- * @internalRemarks Source: @carto/react-api
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,6 @@
1
+ export * from './aggregation.js';
2
+ export * from './applySorting.js';
3
+ export * from './groupBy.js';
4
+ export * from './groupByDate.js';
5
+ export * from './histogram.js';
6
+ export * from './scatterPlot.js';
@@ -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 & {