@carto/api-client 0.4.6 → 0.4.7-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 (114) hide show
  1. package/build/api-client.cjs +3473 -1556
  2. package/build/api-client.cjs.map +1 -1
  3. package/build/api-client.d.cts +1389 -0
  4. package/build/api-client.d.ts +1389 -0
  5. package/build/api-client.js +3676 -0
  6. package/build/api-client.js.map +1 -0
  7. package/build/worker.d.ts +2 -0
  8. package/build/worker.js +1949 -0
  9. package/build/worker.js.map +1 -0
  10. package/package.json +58 -40
  11. package/src/api/carto-api-error.ts +1 -1
  12. package/src/api/query.ts +5 -5
  13. package/src/api/request-with-parameters.ts +6 -6
  14. package/src/client.ts +3 -3
  15. package/src/constants-internal.ts +5 -11
  16. package/src/constants.ts +28 -3
  17. package/src/deck/get-data-filter-extension-props.ts +146 -0
  18. package/src/deck/index.ts +1 -0
  19. package/src/filters/Filter.ts +179 -0
  20. package/src/filters/FilterTypes.ts +109 -0
  21. package/src/filters/geosjonFeatures.ts +32 -0
  22. package/src/filters/index.ts +6 -0
  23. package/src/filters/tileFeatures.ts +51 -0
  24. package/src/filters/tileFeaturesGeometries.ts +444 -0
  25. package/src/filters/tileFeaturesSpatialIndex.ts +119 -0
  26. package/src/filters.ts +4 -4
  27. package/src/geo.ts +12 -14
  28. package/src/global.d.ts +3 -8
  29. package/src/index.ts +7 -0
  30. package/src/models/common.ts +11 -9
  31. package/src/models/index.ts +1 -1
  32. package/src/models/model.ts +3 -4
  33. package/src/operations/aggregation.ts +154 -0
  34. package/src/operations/applySorting.ts +109 -0
  35. package/src/operations/groupBy.ts +59 -0
  36. package/src/operations/groupByDate.ts +98 -0
  37. package/src/operations/histogram.ts +66 -0
  38. package/src/operations/index.ts +6 -0
  39. package/src/operations/scatterPlot.ts +50 -0
  40. package/src/sources/base-source.ts +8 -8
  41. package/src/sources/boundary-query-source.ts +2 -2
  42. package/src/sources/boundary-table-source.ts +2 -2
  43. package/src/sources/h3-query-source.ts +7 -5
  44. package/src/sources/h3-table-source.ts +7 -5
  45. package/src/sources/h3-tileset-source.ts +20 -8
  46. package/src/sources/index.ts +26 -26
  47. package/src/sources/quadbin-query-source.ts +7 -5
  48. package/src/sources/quadbin-table-source.ts +7 -5
  49. package/src/sources/quadbin-tileset-source.ts +20 -8
  50. package/src/sources/raster-source.ts +3 -2
  51. package/src/sources/types.ts +9 -3
  52. package/src/sources/vector-query-source.ts +7 -5
  53. package/src/sources/vector-table-source.ts +7 -5
  54. package/src/sources/vector-tileset-source.ts +21 -8
  55. package/src/spatial-index.ts +4 -5
  56. package/src/types-internal.ts +11 -5
  57. package/src/types.ts +73 -15
  58. package/src/utils/dateUtils.ts +28 -0
  59. package/src/utils/getTileFormat.ts +9 -0
  60. package/src/utils/makeIntervalComplete.ts +17 -0
  61. package/src/utils/transformTileCoordsToWGS84.ts +77 -0
  62. package/src/utils/transformToTileCoords.ts +85 -0
  63. package/src/utils.ts +3 -3
  64. package/src/widget-sources/index.ts +3 -1
  65. package/src/widget-sources/types.ts +37 -25
  66. package/src/widget-sources/widget-query-source.ts +12 -5
  67. package/src/widget-sources/{widget-base-source.ts → widget-remote-source.ts} +55 -149
  68. package/src/widget-sources/widget-source.ts +145 -0
  69. package/src/widget-sources/widget-table-source.ts +12 -5
  70. package/src/widget-sources/widget-tileset-source-impl.ts +417 -0
  71. package/src/widget-sources/widget-tileset-source.ts +311 -0
  72. package/src/workers/constants.ts +13 -0
  73. package/src/workers/types.ts +19 -0
  74. package/src/workers/widget-tileset-worker.ts +40 -0
  75. package/build/api/carto-api-error.d.ts +0 -26
  76. package/build/api/endpoints.d.ts +0 -24
  77. package/build/api/index.d.ts +0 -5
  78. package/build/api/query.d.ts +0 -3
  79. package/build/api/request-with-parameters.d.ts +0 -10
  80. package/build/api-client.modern.js +0 -1742
  81. package/build/api-client.modern.js.map +0 -1
  82. package/build/client.d.ts +0 -14
  83. package/build/constants-internal.d.ts +0 -26
  84. package/build/constants.d.ts +0 -31
  85. package/build/filters.d.ts +0 -39
  86. package/build/geo.d.ts +0 -19
  87. package/build/index.d.ts +0 -11
  88. package/build/models/common.d.ts +0 -27
  89. package/build/models/index.d.ts +0 -3
  90. package/build/models/model.d.ts +0 -37
  91. package/build/sources/base-source.d.ts +0 -4
  92. package/build/sources/boundary-query-source.d.ts +0 -10
  93. package/build/sources/boundary-table-source.d.ts +0 -8
  94. package/build/sources/h3-query-source.d.ts +0 -5
  95. package/build/sources/h3-table-source.d.ts +0 -5
  96. package/build/sources/h3-tileset-source.d.ts +0 -4
  97. package/build/sources/index.d.ts +0 -26
  98. package/build/sources/quadbin-query-source.d.ts +0 -5
  99. package/build/sources/quadbin-table-source.d.ts +0 -5
  100. package/build/sources/quadbin-tileset-source.d.ts +0 -4
  101. package/build/sources/raster-source.d.ts +0 -4
  102. package/build/sources/types.d.ts +0 -366
  103. package/build/sources/vector-query-source.d.ts +0 -5
  104. package/build/sources/vector-table-source.d.ts +0 -5
  105. package/build/sources/vector-tileset-source.d.ts +0 -4
  106. package/build/spatial-index.d.ts +0 -14
  107. package/build/types-internal.d.ts +0 -52
  108. package/build/types.d.ts +0 -80
  109. package/build/utils.d.ts +0 -32
  110. package/build/widget-sources/index.d.ts +0 -4
  111. package/build/widget-sources/types.d.ts +0 -149
  112. package/build/widget-sources/widget-base-source.d.ts +0 -99
  113. package/build/widget-sources/widget-query-source.d.ts +0 -33
  114. package/build/widget-sources/widget-table-source.d.ts +0 -33
@@ -1,32 +1,26 @@
1
- /**
2
- * Current version of @carto/api-client.
3
- * @internal
4
- */
5
- export const API_CLIENT_VERSION = __CARTO_API_CLIENT_VERSION;
6
-
7
1
  /** @internal */
8
2
  export const V3_MINOR_VERSION = '3.4';
9
3
 
10
- /** @internalRemarks Source: @carto/constants, @deck.gl/carto */
4
+ /** @privateRemarks Source: @carto/constants, @deck.gl/carto */
11
5
  export const DEFAULT_GEO_COLUMN = 'geom';
12
6
 
13
7
  /**
14
8
  * Fastly default limit is 8192; leave some padding.
15
- * @internalRemarks Source: @deck.gl/carto
9
+ * @privateRemarks Source: @deck.gl/carto
16
10
  */
17
11
  export const DEFAULT_MAX_LENGTH_URL = 7000;
18
12
 
19
- /** @internalRemarks Source: @deck.gl/carto */
13
+ /** @privateRemarks Source: @deck.gl/carto */
20
14
  export const DEFAULT_TILE_RESOLUTION = 0.5;
21
15
 
22
16
  /**
23
- * @internalRemarks Source: @deck.gl/carto
17
+ * @privateRemarks Source: @deck.gl/carto
24
18
  * @internal
25
19
  */
26
20
  export const DEFAULT_AGGREGATION_RES_LEVEL_H3 = 4;
27
21
 
28
22
  /**
29
- * @internalRemarks Source: @deck.gl/carto
23
+ * @privateRemarks Source: @deck.gl/carto
30
24
  * @internal
31
25
  */
32
26
  export const DEFAULT_AGGREGATION_RES_LEVEL_QUADBIN = 6;
package/src/constants.ts CHANGED
@@ -10,7 +10,7 @@
10
10
  * };
11
11
  * ```
12
12
  *
13
- * @internalRemarks Source: @carto/react-api, @deck.gl/carto
13
+ * @privateRemarks Source: @carto/react-api, @deck.gl/carto
14
14
  */
15
15
  export enum FilterType {
16
16
  IN = 'in',
@@ -22,12 +22,37 @@ export enum FilterType {
22
22
  STRING_SEARCH = 'stringSearch',
23
23
  }
24
24
 
25
- /** @internalRemarks Source: @carto/constants */
25
+ /** @privateRemarks Source: @carto/constants */
26
26
  export enum ApiVersion {
27
27
  V1 = 'v1',
28
28
  V2 = 'v2',
29
29
  V3 = 'v3',
30
30
  }
31
31
 
32
- /** @internalRemarks Source: @carto/constants, @deck.gl/carto */
32
+ /** @privateRemarks Source: @carto/constants, @deck.gl/carto */
33
33
  export const DEFAULT_API_BASE_URL = 'https://gcp-us-east1.api.carto.com';
34
+
35
+ /** @privateRemarks Source: @carto/react-core */
36
+ export enum TileFormat {
37
+ MVT = 'mvt',
38
+ JSON = 'json',
39
+ GEOJSON = 'geojson',
40
+ BINARY = 'binary',
41
+ }
42
+
43
+ /** @privateRemarks Source: @carto/react-core */
44
+ export enum SpatialIndex {
45
+ H3 = 'h3',
46
+ S2 = 's2',
47
+ QUADBIN = 'quadbin',
48
+ }
49
+
50
+ /** @privateRemarks Source: @carto/react-core */
51
+ export enum Provider {
52
+ BIGQUERY = 'bigquery',
53
+ REDSHIFT = 'redshift',
54
+ POSTGRES = 'postgres',
55
+ SNOWFLAKE = 'snowflake',
56
+ DATABRICKS = 'databricks',
57
+ DATABRICKS_REST = 'databricksRest',
58
+ }
@@ -0,0 +1,146 @@
1
+ import {Feature} from 'geojson';
2
+ import {FilterLogicalOperator, Filters} from '../types.js';
3
+ import {FilterType} from '../constants.js';
4
+ import {_buildFeatureFilter} from '../filters/index.js';
5
+ import {FeatureData} from '../types-internal.js';
6
+
7
+ type TimeFilter = Filters['string'][FilterType.TIME] & {
8
+ params?: {offsetBy?: number};
9
+ };
10
+
11
+ /** @experimental Prefer type definition from deck.gl. */
12
+ export type _DataFilterExtensionProps = {
13
+ filterRange: number[][];
14
+ updateTriggers: Record<string, string>;
15
+ getFilterValue: (feature: Feature | FeatureData) => number[];
16
+ };
17
+
18
+ /**
19
+ * Creates props for DataFilterExtension, from `@deck.gl/extensions`, given
20
+ * a set of filters.
21
+ *
22
+ * @privateRemarks DataFilterExtension accepts up to 4 values to filter. This
23
+ * implementation uses the 1st for all filters except the time filter, and the
24
+ * 2nd for the time filter.
25
+ */
26
+ export function getDataFilterExtensionProps(
27
+ filters: Filters,
28
+ filtersLogicalOperator?: FilterLogicalOperator,
29
+ filterSize?: 0 | 1 | 2 | 3 | 4
30
+ ): _DataFilterExtensionProps {
31
+ filterSize ??= 4;
32
+ const {filtersWithoutTimeType, timeColumn, timeFilter} =
33
+ getFiltersByType(filters);
34
+ return {
35
+ filterRange: getFilterRange(timeFilter, filterSize),
36
+ updateTriggers: getUpdateTriggers(
37
+ filtersWithoutTimeType,
38
+ timeColumn,
39
+ timeFilter
40
+ ),
41
+ getFilterValue: getFilterValue(
42
+ filtersWithoutTimeType,
43
+ timeColumn,
44
+ timeFilter,
45
+ filterSize,
46
+ filtersLogicalOperator
47
+ ),
48
+ };
49
+ }
50
+
51
+ /** @internal */
52
+ function getFiltersByType(filters: Filters) {
53
+ const filtersWithoutTimeType: Filters = {};
54
+
55
+ let timeColumn: string | null = null;
56
+ let timeFilter: TimeFilter | null = null;
57
+
58
+ for (const [column, columnData] of Object.entries(filters)) {
59
+ for (const [type, typeData] of Object.entries(columnData) as [
60
+ FilterType,
61
+ unknown,
62
+ ][]) {
63
+ if (type === FilterType.TIME) {
64
+ timeColumn = column;
65
+ timeFilter = typeData as TimeFilter;
66
+ } else {
67
+ filtersWithoutTimeType[column] = {[type]: typeData};
68
+ }
69
+ }
70
+ }
71
+
72
+ return {
73
+ filtersWithoutTimeType,
74
+ timeColumn,
75
+ timeFilter,
76
+ };
77
+ }
78
+
79
+ /** @internal */
80
+ function getFilterRange(
81
+ timeFilter: TimeFilter | null,
82
+ filterSize: number
83
+ ): number[][] {
84
+ const result = Array(filterSize).fill([0, 0]);
85
+ // According to getFilterValue all filters are resolved as 0 or 1 in the first position of the array
86
+ // except the time filter value that is resolved with the real value of the feature in the second position of the array
87
+ result[0] = [1, 1];
88
+ if (timeFilter) {
89
+ const offsetBy = timeFilter.params?.offsetBy || 0;
90
+ result[1] = timeFilter.values[0].map((v) => v - offsetBy);
91
+ }
92
+ return result;
93
+ }
94
+
95
+ /** @internal */
96
+ function getUpdateTriggers(
97
+ filtersWithoutTimeType: Filters,
98
+ timeColumn: string | null,
99
+ timeFilter: TimeFilter | null
100
+ ) {
101
+ const result: Record<string, object> = {...filtersWithoutTimeType};
102
+
103
+ // We don't want to change the layer UpdateTriggers every time that the time filter changes
104
+ // because this filter is changed by the time series widget during its animation
105
+ // so we remove the time filter value from the `updateTriggers`
106
+ if (timeColumn && timeFilter) {
107
+ result[timeColumn] = {
108
+ ...result[timeColumn],
109
+ offsetBy: timeFilter.params?.offsetBy,
110
+ [FilterType.TIME]: {}, // Allows working with other filters, without an impact on performance.
111
+ };
112
+ }
113
+ return {
114
+ getFilterValue: JSON.stringify(result),
115
+ };
116
+ }
117
+
118
+ /** @internal */
119
+ function getFilterValue(
120
+ filtersWithoutTimeType: Filters,
121
+ timeColumn: string | null,
122
+ timeFilter: TimeFilter | null,
123
+ filterSize: number,
124
+ filtersLogicalOperator?: FilterLogicalOperator
125
+ ) {
126
+ const result = Array(filterSize).fill(0);
127
+ const featureFilter = _buildFeatureFilter({
128
+ filters: filtersWithoutTimeType,
129
+ type: 'number',
130
+ filtersLogicalOperator,
131
+ });
132
+
133
+ // We evaluate all filters except the time filter using _buildFeatureFilter function.
134
+ // For the time filter, we return the value of the feature and we will change the getFilterRange result
135
+ // every time this filter changes
136
+ return (feature: Feature | FeatureData) => {
137
+ result[0] = featureFilter(feature);
138
+
139
+ if (timeColumn && timeFilter) {
140
+ const offsetBy = timeFilter.params?.offsetBy || 0;
141
+ const f = (feature.properties || feature) as Record<string, unknown>;
142
+ result[1] = (f[timeColumn] as number) - offsetBy;
143
+ }
144
+ return result;
145
+ };
146
+ }
@@ -0,0 +1 @@
1
+ export * from './get-data-filter-extension-props.js';
@@ -0,0 +1,179 @@
1
+ import {filterFunctions} from './FilterTypes.js';
2
+ import {Filter, FilterLogicalOperator, Filters} from '../types.js';
3
+ import {Feature} from 'geojson';
4
+ import {FilterType} from '../constants.js';
5
+ import {FeatureData} from '../types-internal.js';
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
+ /**
48
+ * @internal
49
+ * @privateRemarks Exported for use in @deck.gl/carto's getDataFilterExtensionProps.
50
+ */
51
+ export function _buildFeatureFilter({
52
+ filters = {},
53
+ type = 'boolean',
54
+ filtersLogicalOperator = 'and',
55
+ }: {
56
+ filters?: Filters;
57
+ type?: 'number' | 'boolean';
58
+ filtersLogicalOperator?: FilterLogicalOperator;
59
+ }) {
60
+ const columns = Object.keys(filters);
61
+
62
+ if (!columns.length) {
63
+ return () => (type === 'number' ? 1 : true);
64
+ }
65
+
66
+ return (feature: Feature | FeatureData) => {
67
+ const f = feature.properties || feature;
68
+ const featurePassesFilter = passesFilter(
69
+ columns,
70
+ filters,
71
+ f as FeatureData,
72
+ filtersLogicalOperator
73
+ );
74
+
75
+ return type === 'number'
76
+ ? Number(featurePassesFilter)
77
+ : featurePassesFilter;
78
+ };
79
+ }
80
+
81
+ /**
82
+ * Apply certain filters to a collection of features.
83
+ * @internal
84
+ */
85
+ export function applyFilters(
86
+ features: FeatureData[],
87
+ filters: Filters,
88
+ filtersLogicalOperator: FilterLogicalOperator
89
+ ) {
90
+ return Object.keys(filters).length
91
+ ? features.filter(_buildFeatureFilter({filters, filtersLogicalOperator}))
92
+ : features;
93
+ }
94
+
95
+ /**
96
+ * Binary.
97
+ * @internal
98
+ */
99
+ export function buildBinaryFeatureFilter({filters = {}}: {filters: Filters}) {
100
+ const columns = Object.keys(filters);
101
+
102
+ if (!columns.length) {
103
+ return () => 1;
104
+ }
105
+
106
+ return (featureIdIdx: number, binaryData: BinaryFeature) =>
107
+ passesFilterUsingBinary(columns, filters, featureIdIdx, binaryData);
108
+ }
109
+
110
+ function getValueFromNumericProps(
111
+ featureIdIdx: number,
112
+ binaryData: BinaryFeature,
113
+ {column}: {column: string}
114
+ ) {
115
+ return binaryData.numericProps?.[column]?.value[featureIdIdx];
116
+ }
117
+
118
+ function getValueFromProperties(
119
+ featureIdIdx: number,
120
+ binaryData: BinaryFeature,
121
+ {column}: {column: string}
122
+ ) {
123
+ const propertyIdx = binaryData.featureIds.value[featureIdIdx];
124
+ return (binaryData.properties[propertyIdx] as Record<string, unknown>)?.[
125
+ column
126
+ ];
127
+ }
128
+
129
+ const GET_VALUE_BY_BINARY_PROP = {
130
+ properties: getValueFromProperties,
131
+ numericProps: getValueFromNumericProps,
132
+ };
133
+
134
+ function getBinaryPropertyByFilterValues(filterValues: unknown[]) {
135
+ return typeof filterValues.flat()[0] === 'string'
136
+ ? 'properties'
137
+ : 'numericProps';
138
+ }
139
+
140
+ function getFeatureValue(
141
+ featureIdIdx: number,
142
+ binaryData: any,
143
+ filter: {type: FilterType; column: string; values: unknown[]}
144
+ ) {
145
+ const {column, values} = filter;
146
+ const binaryProp = getBinaryPropertyByFilterValues(values);
147
+ const getFeatureValueFn = GET_VALUE_BY_BINARY_PROP[binaryProp];
148
+ return getFeatureValueFn(featureIdIdx, binaryData, {column});
149
+ }
150
+
151
+ function passesFilterUsingBinary(
152
+ columns: string[],
153
+ filters: Filters,
154
+ featureIdIdx: number,
155
+ binaryData: BinaryFeature
156
+ ) {
157
+ return columns.every((column) => {
158
+ const columnFilters = filters[column];
159
+
160
+ return Object.entries(columnFilters).every(([type, {values}]) => {
161
+ const filterFn = filterFunctions[type as FilterType];
162
+ if (!filterFn) {
163
+ throw new Error(`"${type}" filter is not implemented.`);
164
+ }
165
+
166
+ if (!values) return 0;
167
+
168
+ const featureValue = getFeatureValue(featureIdIdx, binaryData, {
169
+ type: type as FilterType,
170
+ column,
171
+ values,
172
+ });
173
+
174
+ if (featureValue === undefined || featureValue === null) return 0;
175
+
176
+ return filterFn(values, featureValue);
177
+ });
178
+ });
179
+ }
@@ -0,0 +1,109 @@
1
+ import {FilterType} from '../constants.js';
2
+ import {FilterInterval, StringSearchOptions} from '../types.js';
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 {FeatureData} from '../types-internal.js';
4
+ import {SpatialFilter} from '../types.js';
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,51 @@
1
+ import {SpatialFilter, SpatialIndexTile, Tile} from '../types.js';
2
+ import {tileFeaturesGeometries} from './tileFeaturesGeometries.js';
3
+ import {tileFeaturesSpatialIndex} from './tileFeaturesSpatialIndex.js';
4
+ import {TileFormat} from '../constants.js';
5
+ import {DEFAULT_GEO_COLUMN} from '../constants-internal.js';
6
+ import {FeatureData} from '../types-internal.js';
7
+ import {SpatialDataType} from '../sources/types.js';
8
+
9
+ /** @privateRemarks 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
+ /** @privateRemarks Source: @carto/react-core */
21
+ export type TileFeatureExtractOptions = {
22
+ storeGeometry?: boolean;
23
+ uniqueIdProperty?: string;
24
+ };
25
+
26
+ /** @privateRemarks Source: @carto/react-core */
27
+ export function tileFeatures({
28
+ tiles,
29
+ spatialFilter,
30
+ uniqueIdProperty,
31
+ tileFormat,
32
+ spatialDataColumn = DEFAULT_GEO_COLUMN,
33
+ spatialDataType,
34
+ options = {},
35
+ }: TileFeatures): FeatureData[] {
36
+ if (spatialDataType !== 'geo') {
37
+ return tileFeaturesSpatialIndex({
38
+ tiles: tiles as SpatialIndexTile[],
39
+ spatialFilter,
40
+ spatialDataColumn,
41
+ spatialDataType,
42
+ });
43
+ }
44
+ return tileFeaturesGeometries({
45
+ tiles,
46
+ tileFormat,
47
+ spatialFilter,
48
+ uniqueIdProperty,
49
+ options,
50
+ });
51
+ }