@carto/api-client 0.4.3 → 0.5.0-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 (93) hide show
  1. package/CHANGELOG.md +4 -0
  2. package/build/api/query.d.ts +1 -1
  3. package/build/api-client.cjs +2388 -261
  4. package/build/api-client.cjs.map +1 -1
  5. package/build/api-client.modern.js +2237 -262
  6. package/build/api-client.modern.js.map +1 -1
  7. package/build/constants.d.ts +22 -0
  8. package/build/filters/Filter.d.ts +13 -0
  9. package/build/filters/FilterTypes.d.ts +3 -0
  10. package/build/filters/geosjonFeatures.d.ts +8 -0
  11. package/build/filters/index.d.ts +6 -0
  12. package/build/filters/tileFeatures.d.ts +20 -0
  13. package/build/filters/tileFeaturesGeometries.d.ts +13 -0
  14. package/build/filters/tileFeaturesSpatialIndex.d.ts +10 -0
  15. package/build/index.d.ts +4 -0
  16. package/build/models/index.d.ts +1 -1
  17. package/build/models/model.d.ts +7 -1
  18. package/build/operations/aggregation.d.ts +8 -0
  19. package/build/operations/applySorting.d.ts +20 -0
  20. package/build/operations/groupBy.d.ts +15 -0
  21. package/build/operations/groupByDate.d.ts +11 -0
  22. package/build/operations/histogram.d.ts +13 -0
  23. package/build/operations/index.d.ts +6 -0
  24. package/build/operations/scatterPlot.d.ts +14 -0
  25. package/build/sources/h3-tileset-source.d.ts +2 -1
  26. package/build/sources/index.d.ts +1 -1
  27. package/build/sources/quadbin-tileset-source.d.ts +2 -1
  28. package/build/sources/types.d.ts +36 -41
  29. package/build/sources/vector-tileset-source.d.ts +2 -1
  30. package/build/spatial-index.d.ts +8 -0
  31. package/build/types-internal.d.ts +4 -0
  32. package/build/types.d.ts +61 -1
  33. package/build/utils/dateUtils.d.ts +10 -0
  34. package/build/utils/getTileFormat.d.ts +3 -0
  35. package/build/utils/makeIntervalComplete.d.ts +2 -0
  36. package/build/utils/transformTileCoordsToWGS84.d.ts +8 -0
  37. package/build/utils/transformToTileCoords.d.ts +9 -0
  38. package/build/utils.d.ts +1 -1
  39. package/build/widget-sources/index.d.ts +2 -1
  40. package/build/widget-sources/types.d.ts +40 -23
  41. package/build/widget-sources/widget-query-source.d.ts +2 -2
  42. package/build/widget-sources/widget-remote-source.d.ts +18 -0
  43. package/build/widget-sources/{widget-base-source.d.ts → widget-source.d.ts} +16 -41
  44. package/build/widget-sources/widget-table-source.d.ts +2 -2
  45. package/build/widget-sources/widget-tileset-source.d.ts +67 -0
  46. package/package.json +36 -35
  47. package/src/api/query.ts +1 -2
  48. package/src/constants.ts +25 -0
  49. package/src/filters/Filter.ts +169 -0
  50. package/src/filters/FilterTypes.ts +109 -0
  51. package/src/filters/geosjonFeatures.ts +32 -0
  52. package/src/filters/index.ts +6 -0
  53. package/src/filters/tileFeatures.ts +56 -0
  54. package/src/filters/tileFeaturesGeometries.ts +444 -0
  55. package/src/filters/tileFeaturesSpatialIndex.ts +119 -0
  56. package/src/index.ts +6 -0
  57. package/src/models/index.ts +1 -1
  58. package/src/models/model.ts +47 -24
  59. package/src/operations/aggregation.ts +154 -0
  60. package/src/operations/applySorting.ts +109 -0
  61. package/src/operations/groupBy.ts +59 -0
  62. package/src/operations/groupByDate.ts +98 -0
  63. package/src/operations/histogram.ts +66 -0
  64. package/src/operations/index.ts +6 -0
  65. package/src/operations/scatterPlot.ts +50 -0
  66. package/src/sources/h3-query-source.ts +7 -1
  67. package/src/sources/h3-table-source.ts +6 -1
  68. package/src/sources/h3-tileset-source.ts +18 -6
  69. package/src/sources/index.ts +1 -1
  70. package/src/sources/quadbin-query-source.ts +6 -1
  71. package/src/sources/quadbin-table-source.ts +6 -1
  72. package/src/sources/quadbin-tileset-source.ts +18 -6
  73. package/src/sources/raster-source.ts +1 -0
  74. package/src/sources/types.ts +41 -45
  75. package/src/sources/vector-query-source.ts +10 -3
  76. package/src/sources/vector-table-source.ts +10 -3
  77. package/src/sources/vector-tileset-source.ts +19 -6
  78. package/src/spatial-index.ts +111 -0
  79. package/src/types-internal.ts +6 -0
  80. package/src/types.ts +60 -2
  81. package/src/utils/dateUtils.ts +28 -0
  82. package/src/utils/getTileFormat.ts +9 -0
  83. package/src/utils/makeIntervalComplete.ts +17 -0
  84. package/src/utils/transformTileCoordsToWGS84.ts +77 -0
  85. package/src/utils/transformToTileCoords.ts +85 -0
  86. package/src/utils.ts +9 -6
  87. package/src/widget-sources/index.ts +2 -1
  88. package/src/widget-sources/types.ts +42 -23
  89. package/src/widget-sources/widget-query-source.ts +6 -3
  90. package/src/widget-sources/{widget-base-source.ts → widget-remote-source.ts} +169 -144
  91. package/src/widget-sources/widget-source.ts +160 -0
  92. package/src/widget-sources/widget-table-source.ts +6 -3
  93. package/src/widget-sources/widget-tileset-source.ts +396 -0
@@ -0,0 +1,169 @@
1
+ import {filterFunctions} from './FilterTypes';
2
+ import {Filter, FilterLogicalOperator, Filters} from '../types';
3
+ import {Feature} from 'geojson';
4
+ import {FilterType} from '../constants';
5
+ import {FeatureData} from '../types-internal';
6
+ import {BinaryFeature} from '@loaders.gl/schema';
7
+
8
+ const LOGICAL_OPERATOR_METHODS: Record<
9
+ FilterLogicalOperator,
10
+ 'every' | 'some'
11
+ > = {
12
+ and: 'every',
13
+ or: 'some',
14
+ };
15
+
16
+ function passesFilter(
17
+ columns: string[],
18
+ filters: Filters,
19
+ feature: FeatureData,
20
+ filtersLogicalOperator: FilterLogicalOperator
21
+ ): boolean {
22
+ const method = LOGICAL_OPERATOR_METHODS[filtersLogicalOperator];
23
+ return columns[method]((column) => {
24
+ const columnFilters = filters[column];
25
+ const columnFilterTypes = Object.keys(columnFilters) as FilterType[];
26
+
27
+ if (!feature || feature[column] === null || feature[column] === undefined) {
28
+ return false;
29
+ }
30
+
31
+ return columnFilterTypes.every((filter) => {
32
+ const filterFunction = filterFunctions[filter];
33
+
34
+ if (!filterFunction) {
35
+ throw new Error(`"${filter}" filter is not implemented.`);
36
+ }
37
+
38
+ return filterFunction(
39
+ columnFilters[filter]!.values,
40
+ feature[column],
41
+ (columnFilters[filter] as Filter[FilterType.STRING_SEARCH])!.params
42
+ );
43
+ });
44
+ });
45
+ }
46
+
47
+ export function buildFeatureFilter({
48
+ filters = {},
49
+ type = 'boolean',
50
+ filtersLogicalOperator = 'and',
51
+ }: {
52
+ filters?: Filters;
53
+ type?: 'number' | 'boolean';
54
+ filtersLogicalOperator?: FilterLogicalOperator;
55
+ }) {
56
+ const columns = Object.keys(filters);
57
+
58
+ if (!columns.length) {
59
+ return () => (type === 'number' ? 1 : true);
60
+ }
61
+
62
+ return (feature: Feature | FeatureData) => {
63
+ const f = feature.properties || feature;
64
+ const featurePassesFilter = passesFilter(
65
+ columns,
66
+ filters,
67
+ f as FeatureData,
68
+ filtersLogicalOperator
69
+ );
70
+
71
+ return type === 'number'
72
+ ? Number(featurePassesFilter)
73
+ : featurePassesFilter;
74
+ };
75
+ }
76
+
77
+ // Apply certain filters to a collection of features
78
+ export function applyFilters(
79
+ features: FeatureData[],
80
+ filters: Filters,
81
+ filtersLogicalOperator: FilterLogicalOperator
82
+ ) {
83
+ return Object.keys(filters).length
84
+ ? features.filter(buildFeatureFilter({filters, filtersLogicalOperator}))
85
+ : features;
86
+ }
87
+
88
+ // Binary
89
+ export function buildBinaryFeatureFilter({filters = {}}: {filters: Filters}) {
90
+ const columns = Object.keys(filters);
91
+
92
+ if (!columns.length) {
93
+ return () => 1;
94
+ }
95
+
96
+ return (featureIdIdx: number, binaryData: BinaryFeature) =>
97
+ passesFilterUsingBinary(columns, filters, featureIdIdx, binaryData);
98
+ }
99
+
100
+ function getValueFromNumericProps(
101
+ featureIdIdx: number,
102
+ binaryData: BinaryFeature,
103
+ {column}: {column: string}
104
+ ) {
105
+ return binaryData.numericProps?.[column]?.value[featureIdIdx];
106
+ }
107
+
108
+ function getValueFromProperties(
109
+ featureIdIdx: number,
110
+ binaryData: BinaryFeature,
111
+ {column}: {column: string}
112
+ ) {
113
+ const propertyIdx = binaryData.featureIds.value[featureIdIdx];
114
+ return (binaryData.properties[propertyIdx] as Record<string, unknown>)?.[
115
+ column
116
+ ];
117
+ }
118
+
119
+ const GET_VALUE_BY_BINARY_PROP = {
120
+ properties: getValueFromProperties,
121
+ numericProps: getValueFromNumericProps,
122
+ };
123
+
124
+ function getBinaryPropertyByFilterValues(filterValues: unknown[]) {
125
+ return typeof filterValues.flat()[0] === 'string'
126
+ ? 'properties'
127
+ : 'numericProps';
128
+ }
129
+
130
+ function getFeatureValue(
131
+ featureIdIdx: number,
132
+ binaryData: any,
133
+ filter: {type: FilterType; column: string; values: unknown[]}
134
+ ) {
135
+ const {column, values} = filter;
136
+ const binaryProp = getBinaryPropertyByFilterValues(values);
137
+ const getFeatureValueFn = GET_VALUE_BY_BINARY_PROP[binaryProp];
138
+ return getFeatureValueFn(featureIdIdx, binaryData, {column});
139
+ }
140
+
141
+ function passesFilterUsingBinary(
142
+ columns: string[],
143
+ filters: Filters,
144
+ featureIdIdx: number,
145
+ binaryData: BinaryFeature
146
+ ) {
147
+ return columns.every((column) => {
148
+ const columnFilters = filters[column];
149
+
150
+ return Object.entries(columnFilters).every(([type, {values}]) => {
151
+ const filterFn = filterFunctions[type as FilterType];
152
+ if (!filterFn) {
153
+ throw new Error(`"${type}" filter is not implemented.`);
154
+ }
155
+
156
+ if (!values) return 0;
157
+
158
+ const featureValue = getFeatureValue(featureIdIdx, binaryData, {
159
+ type: type as FilterType,
160
+ column,
161
+ values,
162
+ });
163
+
164
+ if (featureValue === undefined || featureValue === null) return 0;
165
+
166
+ return filterFn(values, featureValue);
167
+ });
168
+ });
169
+ }
@@ -0,0 +1,109 @@
1
+ import {FilterType} from '../constants';
2
+ import {FilterInterval, StringSearchOptions} from '../types';
3
+ import {makeIntervalComplete} from '../utils/makeIntervalComplete.js';
4
+
5
+ export type FilterFunction = (
6
+ filterValues: unknown[],
7
+ featureValue: unknown,
8
+ params?: Record<string, unknown>
9
+ ) => boolean;
10
+
11
+ export const filterFunctions: Record<FilterType, FilterFunction> = {
12
+ [FilterType.IN]: filterIn,
13
+ [FilterType.BETWEEN]: filterBetween,
14
+ [FilterType.TIME]: filterTime,
15
+ [FilterType.CLOSED_OPEN]: filterClosedOpen,
16
+ [FilterType.STRING_SEARCH]: filterStringSearch,
17
+ };
18
+
19
+ function filterIn(filterValues: unknown[], featureValue: unknown): boolean {
20
+ return filterValues.includes(featureValue);
21
+ }
22
+
23
+ // FilterTypes.BETWEEN
24
+ function filterBetween(
25
+ filterValues: unknown[],
26
+ featureValue: unknown
27
+ ): boolean {
28
+ const checkRange = (range: [number, number]) => {
29
+ const [lowerBound, upperBound] = range;
30
+ return (
31
+ (featureValue as number) >= lowerBound &&
32
+ (featureValue as number) <= upperBound
33
+ );
34
+ };
35
+
36
+ return makeIntervalComplete(filterValues as FilterInterval[]).some(
37
+ checkRange
38
+ );
39
+ }
40
+
41
+ function filterTime(filterValues: unknown[], featureValue: unknown) {
42
+ const featureValueAsTimestamp = new Date(featureValue as number).getTime();
43
+ if (isFinite(featureValueAsTimestamp)) {
44
+ return filterBetween(filterValues, featureValueAsTimestamp);
45
+ } else {
46
+ throw new Error(`Column used to filter by time isn't well formatted.`);
47
+ }
48
+ }
49
+
50
+ // FilterTypes.CLOSED_OPEN
51
+ function filterClosedOpen(
52
+ filterValues: unknown[],
53
+ featureValue: unknown
54
+ ): boolean {
55
+ const checkRange = (range: [number, number]) => {
56
+ const [lowerBound, upperBound] = range;
57
+ return (
58
+ (featureValue as number) >= lowerBound &&
59
+ (featureValue as number) < upperBound
60
+ );
61
+ };
62
+
63
+ return makeIntervalComplete(filterValues as [number, number][]).some(
64
+ checkRange
65
+ );
66
+ }
67
+
68
+ // FilterTypes.STRING_SEARCH
69
+ function filterStringSearch(
70
+ filterValues: unknown[],
71
+ featureValue: unknown,
72
+ params: StringSearchOptions = {}
73
+ ): boolean {
74
+ const normalizedFeatureValue = normalize(featureValue, params);
75
+ const stringRegExp = params.useRegExp
76
+ ? filterValues
77
+ : filterValues.map((filterValue) => {
78
+ let stringRegExp = escapeRegExp(normalize(filterValue, params));
79
+
80
+ if (params.mustStart) stringRegExp = `^${stringRegExp}`;
81
+ if (params.mustEnd) stringRegExp = `${stringRegExp}$`;
82
+
83
+ return stringRegExp;
84
+ });
85
+
86
+ const regex = new RegExp(
87
+ stringRegExp.join('|'),
88
+ params.caseSensitive ? 'g' : 'gi'
89
+ );
90
+ return !!normalizedFeatureValue.match(regex);
91
+ }
92
+
93
+ // Aux
94
+ const specialCharRegExp = /[.*+?^${}()|[\]\\]/g;
95
+ const normalizeRegExp = /\p{Diacritic}/gu;
96
+
97
+ function escapeRegExp(value: string) {
98
+ return value.replace(specialCharRegExp, '\\$&');
99
+ }
100
+
101
+ function normalize(data: unknown, params: StringSearchOptions) {
102
+ let normalizedData = String(data);
103
+ if (!params.keepSpecialCharacters)
104
+ normalizedData = normalizedData
105
+ .normalize('NFD')
106
+ .replace(normalizeRegExp, '');
107
+
108
+ return normalizedData;
109
+ }
@@ -0,0 +1,32 @@
1
+ import intersects from '@turf/boolean-intersects';
2
+ import {FeatureCollection} from 'geojson';
3
+ import {SpatialFilter} from '..';
4
+ import {FeatureData} from '../types-internal';
5
+
6
+ export function geojsonFeatures({
7
+ geojson,
8
+ spatialFilter,
9
+ uniqueIdProperty,
10
+ }: {
11
+ geojson: FeatureCollection;
12
+ spatialFilter: SpatialFilter;
13
+ uniqueIdProperty?: string;
14
+ }): FeatureData[] {
15
+ let uniqueIdx = 0;
16
+ const map = new Map();
17
+
18
+ if (!spatialFilter) {
19
+ return [];
20
+ }
21
+
22
+ for (const feature of geojson.features) {
23
+ const uniqueId = uniqueIdProperty
24
+ ? feature.properties![uniqueIdProperty]
25
+ : ++uniqueIdx;
26
+ if (!map.has(uniqueId) && intersects(spatialFilter, feature)) {
27
+ map.set(uniqueId, feature.properties);
28
+ }
29
+ }
30
+
31
+ return Array.from(map.values());
32
+ }
@@ -0,0 +1,6 @@
1
+ export * from './Filter.js';
2
+ export * from './FilterTypes.js';
3
+ export * from './geosjonFeatures.js';
4
+ export * from './tileFeatures.js';
5
+ export * from './tileFeaturesGeometries.js';
6
+ export * from './tileFeaturesSpatialIndex.js';
@@ -0,0 +1,56 @@
1
+ import {SpatialFilter, SpatialIndexTile, Tile} from '../types';
2
+ import {tileFeaturesGeometries} from './tileFeaturesGeometries';
3
+ import {tileFeaturesSpatialIndex} from './tileFeaturesSpatialIndex';
4
+ import {SpatialIndex, TileFormat} from '../constants';
5
+ import {DEFAULT_GEO_COLUMN} from '../constants-internal';
6
+ import {FeatureData} from '../types-internal';
7
+ import {SpatialDataType} from '../sources/types';
8
+
9
+ /** @internalRemarks Source: @carto/react-core */
10
+ export type TileFeatures = {
11
+ tiles: Tile[];
12
+ tileFormat: TileFormat;
13
+ spatialDataType: SpatialDataType;
14
+ spatialDataColumn?: string;
15
+ spatialFilter?: SpatialFilter;
16
+ uniqueIdProperty?: string;
17
+ options?: TileFeatureExtractOptions;
18
+ };
19
+
20
+ /** @internalRemarks Source: @carto/react-core */
21
+ export type TileFeatureExtractOptions = {
22
+ storeGeometry?: boolean;
23
+ };
24
+
25
+ /** @internalRemarks Source: @carto/react-core */
26
+ export function tileFeatures({
27
+ tiles,
28
+ spatialFilter,
29
+ uniqueIdProperty,
30
+ tileFormat,
31
+ spatialDataColumn = DEFAULT_GEO_COLUMN,
32
+ spatialDataType,
33
+ options = {},
34
+ }: TileFeatures): FeatureData[] {
35
+ // TODO(cleanup): Is an empty response the expected result when spatialFilter
36
+ // is omitted? Why not make the parameter required, or return the full input?
37
+ if (!spatialFilter) {
38
+ return [];
39
+ }
40
+
41
+ if (spatialDataType !== 'geo') {
42
+ return tileFeaturesSpatialIndex({
43
+ tiles: tiles as SpatialIndexTile[],
44
+ spatialFilter,
45
+ spatialDataColumn,
46
+ spatialDataType,
47
+ });
48
+ }
49
+ return tileFeaturesGeometries({
50
+ tiles,
51
+ tileFormat,
52
+ spatialFilter,
53
+ uniqueIdProperty,
54
+ options,
55
+ });
56
+ }