@carto/api-client 0.4.0 → 0.4.1-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.
@@ -1,6 +1,7 @@
1
1
  import { Filter, FilterLogicalOperator, MapType, QueryParameters, SpatialFilter } from '../types.js';
2
2
  import { ModelRequestOptions } from './common.js';
3
3
  import { ApiVersion } from '../constants.js';
4
+ import { SpatialDataType, SpatialFilterPolyfillMode } from '../sources/types.js';
4
5
  /** @internalRemarks Source: @carto/react-api */
5
6
  declare const AVAILABLE_MODELS: readonly ["category", "histogram", "formula", "pick", "timeseries", "range", "scatterplot", "table"];
6
7
  export type Model = (typeof AVAILABLE_MODELS)[number];
@@ -14,9 +15,14 @@ export interface ModelSource {
14
15
  data: string;
15
16
  filters?: Record<string, Filter>;
16
17
  filtersLogicalOperator?: FilterLogicalOperator;
17
- geoColumn?: string;
18
18
  spatialFilter?: SpatialFilter;
19
19
  queryParameters?: QueryParameters;
20
+ spatialDataColumn?: string;
21
+ spatialDataType?: SpatialDataType;
22
+ spatialFiltersResolution?: number;
23
+ spatialFiltersMode?: SpatialFilterPolyfillMode;
24
+ /** original resolution of the spatial index data as stored in the DW */
25
+ dataResolution?: number;
20
26
  }
21
27
  /**
22
28
  * Execute a SQL model request.
@@ -38,6 +38,30 @@ export type SourceOptionalOptions = {
38
38
  * @default {@link DEFAULT_MAX_LENGTH_URL}
39
39
  */
40
40
  maxLengthURL?: number;
41
+ /**
42
+ * The column name and the type of geospatial support.
43
+ *
44
+ * If not present, defaults to `'geom'` for generic queries, `'quadbin'` for Quadbin sources and `'h3'` for H3 sources.
45
+ */
46
+ spatialDataColumn?: string;
47
+ /**
48
+ * The type of geospatial support. Defaults to `'geo'`.
49
+ */
50
+ spatialDataType?: SpatialDataType;
51
+ /**
52
+ * Relative resolution of a tile. Higher values increase density and data size. At `tileResolution = 1`, tile geometry is
53
+ * quantized to a 1024x1024 grid. Increasing or decreasing the resolution will increase or decrease the dimensions of
54
+ * the quantization grid proportionately.
55
+ *
56
+ * Supported `tileResolution` values, with corresponding grid sizes:
57
+ *
58
+ * - 0.25: 256x256
59
+ * - 0.5: 512x512
60
+ * - 1: 1024x1024
61
+ * - 2: 2048x2048
62
+ * - 4: 4096x4096
63
+ */
64
+ tileResolution?: TileResolution;
41
65
  };
42
66
  export type SourceOptions = SourceRequiredOptions & Partial<SourceOptionalOptions>;
43
67
  export type AggregationOptions = {
@@ -55,6 +79,10 @@ export type AggregationOptions = {
55
79
  * @default 6 for quadbin and 4 for h3 sources
56
80
  */
57
81
  aggregationResLevel?: number;
82
+ /**
83
+ * Original resolution of the spatial index data as stored in the DW
84
+ */
85
+ dataResolution?: number;
58
86
  };
59
87
  export type FilterOptions = {
60
88
  /**
@@ -63,28 +91,8 @@ export type FilterOptions = {
63
91
  filters?: Filters;
64
92
  };
65
93
  export type QuerySourceOptions = {
66
- /**
67
- * The column name and the type of geospatial support.
68
- *
69
- * If not present, defaults to `'geom'` for generic queries, `'quadbin'` for Quadbin sources and `'h3'` for H3 sources.
70
- */
71
- spatialDataColumn?: string;
72
- /** SQL query. */
94
+ /** Full SQL query with query paremeter placeholders (if any). */
73
95
  sqlQuery: string;
74
- /**
75
- * Relative resolution of a tile. Higher values increase density and data size. At `tileResolution = 1`, tile geometry is
76
- * quantized to a 1024x1024 grid. Increasing or decreasing the resolution will increase or decrease the dimensions of
77
- * the quantization grid proportionately.
78
- *
79
- * Supported `tileResolution` values, with corresponding grid sizes:
80
- *
81
- * - 0.25: 256x256
82
- * - 0.5: 512x512
83
- * - 1: 1024x1024
84
- * - 2: 2048x2048
85
- * - 4: 4096x4096
86
- */
87
- tileResolution?: TileResolution;
88
96
  /**
89
97
  * Values for named or positional paramteres in the query.
90
98
  *
@@ -116,26 +124,6 @@ export type TableSourceOptions = {
116
124
  * Fully qualified name of table.
117
125
  */
118
126
  tableName: string;
119
- /**
120
- * The column name and the type of geospatial support.
121
- *
122
- * If not present, defaults to `'geom'` for generic tables, `'quadbin'` for Quadbin sources and `'h3'` for H3 sources.
123
- */
124
- spatialDataColumn?: string;
125
- /**
126
- * Relative resolution of a tile. Higher values increase density and data size. At `tileResolution = 1`, tile geometry is
127
- * quantized to a 1024x1024 grid. Increasing or decreasing the resolution will increase or decrease the dimensions of
128
- * the quantization grid proportionately.
129
- *
130
- * Supported `tileResolution` values, with corresponding grid sizes:
131
- *
132
- * - 0.25: 256x256
133
- * - 0.5: 512x512
134
- * - 1: 1024x1024
135
- * - 2: 2048x2048
136
- * - 4: 4096x4096
137
- */
138
- tileResolution?: TileResolution;
139
127
  };
140
128
  export type TilesetSourceOptions = {
141
129
  /**
@@ -152,6 +140,13 @@ export type ColumnsOption = {
152
140
  columns?: string[];
153
141
  };
154
142
  export type SpatialDataType = 'geo' | 'h3' | 'quadbin';
143
+ /**
144
+ * Strategy used for covering spatial filter geometry with spatial indexes.
145
+ * See https://docs.carto.com/data-and-analysis/analytics-toolbox-for-bigquery/sql-reference/quadbin#quadbin_polyfill_mode
146
+ * or https://docs.carto.com/data-and-analysis/analytics-toolbox-for-bigquery/sql-reference/h3#h3_polyfill_mode for more information.
147
+ * @internalRemarks Source: cloud-native maps-api
148
+ * */
149
+ export type SpatialFilterPolyfillMode = 'center' | 'intersects' | 'contains';
155
150
  export type TilejsonMapInstantiation = MapInstantiation & {
156
151
  tilejson: {
157
152
  url: string[];
@@ -0,0 +1,11 @@
1
+ import type { ModelSource } from './models/model';
2
+ import type { AggregationOptions } from './sources/types';
3
+ import type { ViewState } from './widget-sources';
4
+ export declare function getSpatialFiltersResolution({ source, viewState, }: {
5
+ source: Partial<ModelSource & AggregationOptions>;
6
+ viewState?: ViewState;
7
+ }): number | undefined;
8
+ export declare function getHexagonResolution(viewport: {
9
+ zoom: number;
10
+ latitude: number;
11
+ }, tileSize: number): number;
package/build/utils.d.ts CHANGED
@@ -14,7 +14,7 @@ type Row<T> = Record<string, T> | Record<string, T>[] | T[] | T;
14
14
  */
15
15
  export declare function normalizeObjectKeys<T, R extends Row<T>>(el: R): R;
16
16
  /** @internalRemarks Source: @carto/react-core */
17
- export declare function assert(condition: unknown, message: string): void;
17
+ export declare function assert(condition: unknown, message: string): asserts condition;
18
18
  /**
19
19
  * @internalRemarks Source: @carto/react-core
20
20
  * @internal
@@ -1,13 +1,20 @@
1
- import { TileResolution } from '../sources/types';
1
+ import { SpatialFilterPolyfillMode, TileResolution } from '../sources/types';
2
2
  import { GroupDateType, SortColumnType, SortDirection, SpatialFilter } from '../types';
3
3
  /******************************************************************************
4
4
  * WIDGET API REQUESTS
5
5
  */
6
+ export interface ViewState {
7
+ zoom: number;
8
+ latitude: number;
9
+ longitude: number;
10
+ }
6
11
  /** Common options for {@link WidgetBaseSource} requests. */
7
12
  interface BaseRequestOptions {
8
13
  spatialFilter?: SpatialFilter;
14
+ spatialFiltersMode?: SpatialFilterPolyfillMode;
9
15
  abortController?: AbortController;
10
16
  filterOwner?: string;
17
+ viewState?: ViewState;
11
18
  }
12
19
  /** Options for {@link WidgetBaseSource#getCategories}. */
13
20
  export interface CategoryRequestOptions extends BaseRequestOptions {
@@ -5,7 +5,6 @@ import { SourceOptions } from '../sources/index.js';
5
5
  import { ApiVersion } from '../constants.js';
6
6
  export interface WidgetBaseSourceProps extends Omit<SourceOptions, 'filters'> {
7
7
  apiVersion?: ApiVersion;
8
- geoColumn?: string;
9
8
  filters?: Record<string, Filter>;
10
9
  filtersLogicalOperator?: FilterLogicalOperator;
11
10
  }
package/package.json CHANGED
@@ -4,7 +4,7 @@
4
4
  "repository": "github:CartoDB/carto-api-client",
5
5
  "author": "Don McCurdy <donmccurdy@carto.com>",
6
6
  "packageManager": "yarn@4.3.1",
7
- "version": "0.4.0",
7
+ "version": "0.4.1-alpha.0",
8
8
  "license": "MIT",
9
9
  "publishConfig": {
10
10
  "access": "public",
package/src/api/query.ts CHANGED
@@ -12,8 +12,7 @@ import {buildQueryUrl} from './endpoints';
12
12
  import {requestWithParameters} from './request-with-parameters';
13
13
  import {APIErrorContext} from './carto-api-error';
14
14
 
15
- export type QueryOptions = SourceOptions &
16
- Omit<QuerySourceOptions, 'spatialDataColumn'>;
15
+ export type QueryOptions = SourceOptions & QuerySourceOptions;
17
16
  type UrlParameters = {q: string; queryParameters?: string};
18
17
 
19
18
  export const query = async function (
@@ -7,9 +7,10 @@ import {
7
7
  SpatialFilter,
8
8
  } from '../types.js';
9
9
  import {$TODO} from '../types-internal.js';
10
- import {assert} from '../utils.js';
10
+ import {assert, isPureObject} from '../utils.js';
11
11
  import {ModelRequestOptions, makeCall} from './common.js';
12
12
  import {ApiVersion} from '../constants.js';
13
+ import {SpatialDataType, SpatialFilterPolyfillMode} from '../sources/types.js';
13
14
 
14
15
  /** @internalRemarks Source: @carto/react-api */
15
16
  const AVAILABLE_MODELS = [
@@ -35,9 +36,14 @@ export interface ModelSource {
35
36
  data: string;
36
37
  filters?: Record<string, Filter>;
37
38
  filtersLogicalOperator?: FilterLogicalOperator;
38
- geoColumn?: string;
39
39
  spatialFilter?: SpatialFilter;
40
40
  queryParameters?: QueryParameters;
41
+ spatialDataColumn?: string;
42
+ spatialDataType?: SpatialDataType;
43
+ spatialFiltersResolution?: number;
44
+ spatialFiltersMode?: SpatialFilterPolyfillMode;
45
+ /** original resolution of the spatial index data as stored in the DW */
46
+ dataResolution?: number;
41
47
  }
42
48
 
43
49
  const {V3} = ApiVersion;
@@ -79,50 +85,51 @@ export function executeModel(props: {
79
85
  data,
80
86
  filters,
81
87
  filtersLogicalOperator = 'and',
82
- geoColumn = DEFAULT_GEO_COLUMN,
88
+ spatialDataType = 'geo',
89
+ spatialFiltersMode = 'intersects',
90
+ spatialFiltersResolution = 0,
83
91
  } = source;
84
92
 
85
- const queryParameters = source.queryParameters
86
- ? JSON.stringify(source.queryParameters)
87
- : '';
88
-
89
- const queryParams: Record<string, string> = {
93
+ const queryParams: Record<string, unknown> = {
90
94
  type,
91
95
  client: clientId,
92
96
  source: data,
93
- params: JSON.stringify(params),
94
- queryParameters,
95
- filters: JSON.stringify(filters),
97
+ params,
98
+ queryParameters: source.queryParameters || '',
99
+ filters,
96
100
  filtersLogicalOperator,
97
101
  };
98
102
 
103
+ const spatialDataColumn = source.spatialDataColumn || DEFAULT_GEO_COLUMN;
104
+
99
105
  // Picking Model API requires 'spatialDataColumn'.
100
106
  if (model === 'pick') {
101
- queryParams.spatialDataColumn = geoColumn;
107
+ queryParams.spatialDataColumn = spatialDataColumn;
102
108
  }
103
109
 
104
- // API supports multiple filters, we apply it only to geoColumn
110
+ // API supports multiple filters, we apply it only to spatialDataColumn
105
111
  const spatialFilters = source.spatialFilter
106
- ? {[geoColumn]: source.spatialFilter}
112
+ ? {[spatialDataColumn]: source.spatialFilter}
107
113
  : undefined;
108
114
 
109
115
  if (spatialFilters) {
110
- queryParams.spatialFilters = JSON.stringify(spatialFilters);
116
+ queryParams.spatialFilters = spatialFilters; // JSON.stringify(spatialFilters);
117
+ queryParams.spatialDataColumn = spatialDataColumn;
118
+ queryParams.spatialDataType = spatialDataType;
119
+ }
120
+
121
+ if (spatialDataType !== 'geo') {
122
+ if (spatialFiltersResolution > 0) {
123
+ queryParams.spatialFiltersResolution = spatialFiltersResolution;
124
+ }
125
+ queryParams.spatialFiltersMode = spatialFiltersMode;
111
126
  }
112
127
 
113
128
  const urlWithSearchParams =
114
- url + '?' + new URLSearchParams(queryParams).toString();
129
+ url + '?' + objectToURLSearchParams(queryParams).toString();
115
130
  const isGet = urlWithSearchParams.length <= REQUEST_GET_MAX_URL_LENGTH;
116
131
  if (isGet) {
117
132
  url = urlWithSearchParams;
118
- } else {
119
- // undo the JSON.stringify, @TODO find a better pattern
120
- queryParams.params = params as $TODO;
121
- queryParams.filters = filters as $TODO;
122
- queryParams.queryParameters = source.queryParameters as $TODO;
123
- if (spatialFilters) {
124
- queryParams.spatialFilters = spatialFilters as $TODO;
125
- }
126
133
  }
127
134
  return makeCall({
128
135
  url,
@@ -134,3 +141,19 @@ export function executeModel(props: {
134
141
  },
135
142
  });
136
143
  }
144
+
145
+ function objectToURLSearchParams(object: Record<string, unknown>) {
146
+ const params = new URLSearchParams();
147
+ for (const key in object) {
148
+ if (isPureObject(object[key])) {
149
+ params.append(key, JSON.stringify(object[key]));
150
+ } else if (Array.isArray(object[key])) {
151
+ params.append(key, JSON.stringify(object[key]));
152
+ } else if (object[key] === null) {
153
+ params.append(key, 'null');
154
+ } else if (object[key] !== undefined) {
155
+ params.append(key, String(object[key]));
156
+ }
157
+ }
158
+ return params;
159
+ }
@@ -19,6 +19,7 @@ export type H3QuerySourceOptions = SourceOptions &
19
19
  QuerySourceOptions &
20
20
  AggregationOptions &
21
21
  FilterOptions;
22
+
22
23
  type UrlParameters = {
23
24
  aggregationExp: string;
24
25
  aggregationResLevel?: string;
@@ -59,7 +60,12 @@ export const h3QuerySource = async function (
59
60
  return baseSource<UrlParameters>('query', options, urlParameters).then(
60
61
  (result) => ({
61
62
  ...(result as TilejsonResult),
62
- widgetSource: new WidgetQuerySource(options),
63
+ widgetSource: new WidgetQuerySource({
64
+ ...options,
65
+ // NOTE: passing redundant spatialDataColumn here to apply the default value 'h3'
66
+ spatialDataColumn,
67
+ spatialDataType: 'h3',
68
+ }),
63
69
  })
64
70
  );
65
71
  };
@@ -55,7 +55,12 @@ export const h3TableSource = async function (
55
55
  return baseSource<UrlParameters>('table', options, urlParameters).then(
56
56
  (result) => ({
57
57
  ...(result as TilejsonResult),
58
- widgetSource: new WidgetTableSource(options),
58
+ widgetSource: new WidgetTableSource({
59
+ ...options,
60
+ // NOTE: passing redundant spatialDataColumn here to apply the default value 'h3'
61
+ spatialDataColumn,
62
+ spatialDataType: 'h3',
63
+ }),
59
64
  })
60
65
  );
61
66
  };
@@ -60,7 +60,12 @@ export const quadbinQuerySource = async function (
60
60
  return baseSource<UrlParameters>('query', options, urlParameters).then(
61
61
  (result) => ({
62
62
  ...(result as TilejsonResult),
63
- widgetSource: new WidgetQuerySource(options),
63
+ widgetSource: new WidgetQuerySource({
64
+ ...options,
65
+ // NOTE: passing redundant spatialDataColumn here to apply the default value 'quadbin'
66
+ spatialDataColumn,
67
+ spatialDataType: 'quadbin',
68
+ }),
64
69
  })
65
70
  );
66
71
  };
@@ -56,7 +56,12 @@ export const quadbinTableSource = async function (
56
56
  return baseSource<UrlParameters>('table', options, urlParameters).then(
57
57
  (result) => ({
58
58
  ...(result as TilejsonResult),
59
- widgetSource: new WidgetTableSource(options),
59
+ widgetSource: new WidgetTableSource({
60
+ ...options,
61
+ // NOTE: passing redundant spatialDataColumn here to apply the default value 'quadbin'
62
+ spatialDataColumn,
63
+ spatialDataType: 'quadbin',
64
+ }),
60
65
  })
61
66
  );
62
67
  };
@@ -47,6 +47,33 @@ export type SourceOptionalOptions = {
47
47
  * @default {@link DEFAULT_MAX_LENGTH_URL}
48
48
  */
49
49
  maxLengthURL?: number;
50
+
51
+ /**
52
+ * The column name and the type of geospatial support.
53
+ *
54
+ * If not present, defaults to `'geom'` for generic queries, `'quadbin'` for Quadbin sources and `'h3'` for H3 sources.
55
+ */
56
+ spatialDataColumn?: string;
57
+
58
+ /**
59
+ * The type of geospatial support. Defaults to `'geo'`.
60
+ */
61
+ spatialDataType?: SpatialDataType;
62
+
63
+ /**
64
+ * Relative resolution of a tile. Higher values increase density and data size. At `tileResolution = 1`, tile geometry is
65
+ * quantized to a 1024x1024 grid. Increasing or decreasing the resolution will increase or decrease the dimensions of
66
+ * the quantization grid proportionately.
67
+ *
68
+ * Supported `tileResolution` values, with corresponding grid sizes:
69
+ *
70
+ * - 0.25: 256x256
71
+ * - 0.5: 512x512
72
+ * - 1: 1024x1024
73
+ * - 2: 2048x2048
74
+ * - 4: 4096x4096
75
+ */
76
+ tileResolution?: TileResolution;
50
77
  };
51
78
 
52
79
  export type SourceOptions = SourceRequiredOptions &
@@ -68,6 +95,11 @@ export type AggregationOptions = {
68
95
  * @default 6 for quadbin and 4 for h3 sources
69
96
  */
70
97
  aggregationResLevel?: number;
98
+
99
+ /**
100
+ * Original resolution of the spatial index data as stored in the DW
101
+ */
102
+ dataResolution?: number;
71
103
  };
72
104
 
73
105
  export type FilterOptions = {
@@ -78,31 +110,9 @@ export type FilterOptions = {
78
110
  };
79
111
 
80
112
  export type QuerySourceOptions = {
81
- /**
82
- * The column name and the type of geospatial support.
83
- *
84
- * If not present, defaults to `'geom'` for generic queries, `'quadbin'` for Quadbin sources and `'h3'` for H3 sources.
85
- */
86
- spatialDataColumn?: string;
87
-
88
- /** SQL query. */
113
+ /** Full SQL query with query paremeter placeholders (if any). */
89
114
  sqlQuery: string;
90
115
 
91
- /**
92
- * Relative resolution of a tile. Higher values increase density and data size. At `tileResolution = 1`, tile geometry is
93
- * quantized to a 1024x1024 grid. Increasing or decreasing the resolution will increase or decrease the dimensions of
94
- * the quantization grid proportionately.
95
- *
96
- * Supported `tileResolution` values, with corresponding grid sizes:
97
- *
98
- * - 0.25: 256x256
99
- * - 0.5: 512x512
100
- * - 1: 1024x1024
101
- * - 2: 2048x2048
102
- * - 4: 4096x4096
103
- */
104
- tileResolution?: TileResolution;
105
-
106
116
  /**
107
117
  * Values for named or positional paramteres in the query.
108
118
  *
@@ -135,28 +145,6 @@ export type TableSourceOptions = {
135
145
  * Fully qualified name of table.
136
146
  */
137
147
  tableName: string;
138
-
139
- /**
140
- * The column name and the type of geospatial support.
141
- *
142
- * If not present, defaults to `'geom'` for generic tables, `'quadbin'` for Quadbin sources and `'h3'` for H3 sources.
143
- */
144
- spatialDataColumn?: string;
145
-
146
- /**
147
- * Relative resolution of a tile. Higher values increase density and data size. At `tileResolution = 1`, tile geometry is
148
- * quantized to a 1024x1024 grid. Increasing or decreasing the resolution will increase or decrease the dimensions of
149
- * the quantization grid proportionately.
150
- *
151
- * Supported `tileResolution` values, with corresponding grid sizes:
152
- *
153
- * - 0.25: 256x256
154
- * - 0.5: 512x512
155
- * - 1: 1024x1024
156
- * - 2: 2048x2048
157
- * - 4: 4096x4096
158
- */
159
- tileResolution?: TileResolution;
160
148
  };
161
149
 
162
150
  export type TilesetSourceOptions = {
@@ -177,6 +165,14 @@ export type ColumnsOption = {
177
165
 
178
166
  export type SpatialDataType = 'geo' | 'h3' | 'quadbin';
179
167
 
168
+ /**
169
+ * Strategy used for covering spatial filter geometry with spatial indexes.
170
+ * See https://docs.carto.com/data-and-analysis/analytics-toolbox-for-bigquery/sql-reference/quadbin#quadbin_polyfill_mode
171
+ * or https://docs.carto.com/data-and-analysis/analytics-toolbox-for-bigquery/sql-reference/h3#h3_polyfill_mode for more information.
172
+ * @internalRemarks Source: cloud-native maps-api
173
+ * */
174
+ export type SpatialFilterPolyfillMode = 'center' | 'intersects' | 'contains';
175
+
180
176
  export type TilejsonMapInstantiation = MapInstantiation & {
181
177
  tilejson: {url: string[]};
182
178
  };
@@ -64,7 +64,10 @@ export const vectorQuerySource = async function (
64
64
  return baseSource<UrlParameters>('query', options, urlParameters).then(
65
65
  (result) => ({
66
66
  ...(result as TilejsonResult),
67
- widgetSource: new WidgetQuerySource(options),
67
+ widgetSource: new WidgetQuerySource({
68
+ ...options,
69
+ spatialDataType: 'geo',
70
+ }),
68
71
  })
69
72
  );
70
73
  };
@@ -22,6 +22,7 @@ export type VectorTableSourceOptions = SourceOptions &
22
22
  TableSourceOptions &
23
23
  FilterOptions &
24
24
  ColumnsOption;
25
+
25
26
  type UrlParameters = {
26
27
  columns?: string;
27
28
  filters?: Record<string, unknown>;
@@ -58,7 +59,10 @@ export const vectorTableSource = async function (
58
59
  return baseSource<UrlParameters>('table', options, urlParameters).then(
59
60
  (result) => ({
60
61
  ...(result as TilejsonResult),
61
- widgetSource: new WidgetTableSource(options),
62
+ widgetSource: new WidgetTableSource({
63
+ ...options,
64
+ spatialDataType: 'geo',
65
+ }),
62
66
  })
63
67
  );
64
68
  };
@@ -0,0 +1,119 @@
1
+ import {
2
+ DEFAULT_AGGREGATION_RES_LEVEL_H3,
3
+ DEFAULT_AGGREGATION_RES_LEVEL_QUADBIN,
4
+ } from './constants-internal';
5
+ import type {ModelSource} from './models/model';
6
+ import type {AggregationOptions} from './sources/types';
7
+ import {assert} from './utils';
8
+ import type {ViewState} from './widget-sources';
9
+
10
+ const DEFAULT_TILE_SIZE = 512;
11
+ const QUADBIN_ZOOM_MAX_OFFSET = 4;
12
+
13
+ export function getSpatialFiltersResolution({
14
+ source,
15
+ viewState,
16
+ }: {
17
+ source: Partial<ModelSource & AggregationOptions>;
18
+ viewState?: ViewState;
19
+ }) {
20
+ assert(
21
+ viewState,
22
+ 'viewState prop is required to compute automatic spatialFiltersResolution when using spatialFilter with spatial indexes. Either pass a `spatialFiltersResolution` prop or a `viewState` prop to avoid this error'
23
+ );
24
+
25
+ const dataResolution = source.dataResolution ?? Number.MAX_VALUE;
26
+
27
+ const aggregationResLevel =
28
+ source.aggregationResLevel ??
29
+ (source.spatialDataType === 'h3'
30
+ ? DEFAULT_AGGREGATION_RES_LEVEL_H3
31
+ : DEFAULT_AGGREGATION_RES_LEVEL_QUADBIN);
32
+
33
+ const aggregationResLevelOffset = Math.max(
34
+ 0,
35
+ Math.floor(aggregationResLevel)
36
+ );
37
+
38
+ const currentZoomInt = Math.ceil(viewState.zoom);
39
+ if (source.spatialDataType === 'h3') {
40
+ const tileSize = DEFAULT_TILE_SIZE;
41
+ const maxResolutionForZoom =
42
+ maxH3SpatialFiltersResolutions.find(
43
+ ([zoom]) => zoom === currentZoomInt
44
+ )?.[1] ?? Math.max(0, currentZoomInt - 3);
45
+
46
+ const maxSpatialFiltersResolution = maxResolutionForZoom
47
+ ? Math.min(dataResolution, maxResolutionForZoom)
48
+ : dataResolution;
49
+
50
+ const hexagonResolution =
51
+ getHexagonResolution(viewState, tileSize) + aggregationResLevelOffset;
52
+
53
+ return Math.min(hexagonResolution, maxSpatialFiltersResolution);
54
+ }
55
+
56
+ if (source.spatialDataType === 'quadbin') {
57
+ const maxResolutionForZoom = currentZoomInt + QUADBIN_ZOOM_MAX_OFFSET;
58
+ const maxSpatialFiltersResolution = Math.min(
59
+ dataResolution,
60
+ maxResolutionForZoom
61
+ );
62
+
63
+ const quadsResolution =
64
+ Math.floor(viewState.zoom) + aggregationResLevelOffset;
65
+ return Math.min(quadsResolution, maxSpatialFiltersResolution);
66
+ }
67
+
68
+ return undefined;
69
+ }
70
+
71
+ const maxH3SpatialFiltersResolutions = [
72
+ [20, 14],
73
+ [19, 13],
74
+ [18, 12],
75
+ [17, 11],
76
+ [16, 10],
77
+ [15, 9],
78
+ [14, 8],
79
+ [13, 7],
80
+ [12, 7],
81
+ [11, 7],
82
+ [10, 6],
83
+ [9, 6],
84
+ [8, 5],
85
+ [7, 4],
86
+ [6, 4],
87
+ [5, 3],
88
+ [4, 2],
89
+ [3, 1],
90
+ [2, 1],
91
+ [1, 0],
92
+ ];
93
+
94
+ // stolen from https://github.com/visgl/deck.gl/blob/master/modules/carto/src/layers/h3-tileset-2d.ts
95
+
96
+ // Relative scale factor (0 = no biasing, 2 = a few hexagons cover view)
97
+ const BIAS = 2;
98
+
99
+ // Resolution conversion function. Takes a WebMercatorViewport and returns
100
+ // a H3 resolution such that the screen space size of the hexagons is
101
+ // similar
102
+ export function getHexagonResolution(
103
+ viewport: {zoom: number; latitude: number},
104
+ tileSize: number
105
+ ): number {
106
+ // Difference in given tile size compared to deck's internal 512px tile size,
107
+ // expressed as an offset to the viewport zoom.
108
+ const zoomOffset = Math.log2(tileSize / DEFAULT_TILE_SIZE);
109
+ const hexagonScaleFactor = (2 / 3) * (viewport.zoom - zoomOffset);
110
+ const latitudeScaleFactor = Math.log(
111
+ 1 / Math.cos((Math.PI * viewport.latitude) / 180)
112
+ );
113
+
114
+ // Clip and bias
115
+ return Math.max(
116
+ 0,
117
+ Math.floor(hexagonScaleFactor + latitudeScaleFactor - BIAS)
118
+ );
119
+ }
package/src/utils.ts CHANGED
@@ -57,7 +57,7 @@ export function normalizeObjectKeys<T, R extends Row<T>>(el: R): R {
57
57
  }
58
58
 
59
59
  /** @internalRemarks Source: @carto/react-core */
60
- export function assert(condition: unknown, message: string) {
60
+ export function assert(condition: unknown, message: string): asserts condition {
61
61
  if (!condition) {
62
62
  throw new Error(message);
63
63
  }