@carto/api-client 0.1.0 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/geo.ts ADDED
@@ -0,0 +1,178 @@
1
+ import bboxClip from '@turf/bbox-clip';
2
+ import bboxPolygon from '@turf/bbox-polygon';
3
+ import union from '@turf/union';
4
+ import {getType} from '@turf/invariant';
5
+ import {polygon, multiPolygon, feature, featureCollection} from '@turf/helpers';
6
+ import type {BBox, Geometry, MultiPolygon, Polygon, Position} from 'geojson';
7
+ import {SpatialFilter} from './types';
8
+
9
+ /**
10
+ * Returns a {@link SpatialFilter} for a given viewport, typically obtained
11
+ * from deck.gl's `viewport.getBounds()` method ([west, south, east, north]).
12
+ * If the viewport covers the entire world (to some margin of error in Web
13
+ * Mercator space), `undefined` is returned instead.
14
+ *
15
+ * If the viewport extends beyond longitude range [-180, +180], the polygon
16
+ * may be reformatted for compatibility with CARTO APIs.
17
+ */
18
+ export function createViewportSpatialFilter(
19
+ viewport: BBox
20
+ ): SpatialFilter | undefined {
21
+ if (_isGlobalViewport(viewport)) {
22
+ return;
23
+ }
24
+ return createPolygonSpatialFilter(bboxPolygon(viewport).geometry);
25
+ }
26
+
27
+ /**
28
+ * Returns a {@link SpatialFilter} for a given {@link Polygon} or
29
+ * {@link MultiPolygon}. If the polygon(s) extend outside longitude
30
+ * range [-180, +180], the result may be reformatted for compatibility
31
+ * with CARTO APIs.
32
+ */
33
+ export function createPolygonSpatialFilter(
34
+ spatialFilter: Polygon | MultiPolygon
35
+ ): SpatialFilter | undefined {
36
+ return (spatialFilter && _normalizeGeometry(spatialFilter)) || undefined;
37
+ }
38
+
39
+ /**
40
+ * Check if a viewport is large enough to represent a global coverage.
41
+ * In this case the spatial filter parameter for widget calculation is removed.
42
+ *
43
+ * @internalRemarks Source: @carto/react-core
44
+ */
45
+ function _isGlobalViewport(viewport: BBox) {
46
+ const [minx, miny, maxx, maxy] = viewport;
47
+ return maxx - minx > 179.5 * 2 && maxy - miny > 85.05 * 2;
48
+ }
49
+
50
+ /**
51
+ * Normalized a geometry, coming from a mask or a viewport. The parts
52
+ * spanning outside longitude range [-180, +180] are clipped and "folded"
53
+ * back to the valid range and unioned to the polygons inide that range.
54
+ *
55
+ * It results in a Polygon or MultiPolygon strictly inside the validity range.
56
+ *
57
+ * @internalRemarks Source: @carto/react-core
58
+ */
59
+ function _normalizeGeometry(
60
+ geometry: Polygon | MultiPolygon
61
+ ): Polygon | MultiPolygon | null {
62
+ const WORLD = [-180, -90, +180, +90] as BBox;
63
+ const worldClip = _clean(
64
+ bboxClip(geometry, WORLD).geometry as Polygon | MultiPolygon
65
+ );
66
+
67
+ const geometryTxWest = _tx(geometry, 360);
68
+ const geometryTxEast = _tx(geometry, -360);
69
+
70
+ let result: Polygon | MultiPolygon | null = worldClip;
71
+
72
+ if (result && geometryTxWest) {
73
+ const worldWestClip = _clean(
74
+ bboxClip(geometryTxWest, WORLD).geometry as Polygon | MultiPolygon
75
+ );
76
+ if (worldWestClip) {
77
+ const collection = featureCollection([
78
+ feature(result),
79
+ feature(worldWestClip),
80
+ ]);
81
+ const merged = union(collection);
82
+ result = merged ? _clean(merged.geometry) : result;
83
+ }
84
+ }
85
+
86
+ if (result && geometryTxEast) {
87
+ const worldEastClip = _clean(
88
+ bboxClip(geometryTxEast, WORLD).geometry as Polygon | MultiPolygon
89
+ );
90
+ if (worldEastClip) {
91
+ const collection = featureCollection([
92
+ feature(result),
93
+ feature(worldEastClip),
94
+ ]);
95
+ const merged = union(collection);
96
+ result = merged ? _clean(merged.geometry) : result;
97
+ }
98
+ }
99
+
100
+ return result;
101
+ }
102
+
103
+ /** @internalRemarks Source: @carto/react-core */
104
+ function _cleanPolygonCoords(cc: Position[][]) {
105
+ const coords = cc.filter((c) => c.length > 0);
106
+ return coords.length > 0 ? coords : null;
107
+ }
108
+
109
+ /** @internalRemarks Source: @carto/react-core */
110
+ function _cleanMultiPolygonCoords(ccc: Position[][][]) {
111
+ const coords = ccc.map(_cleanPolygonCoords).filter((cc) => cc);
112
+ return coords.length > 0 ? coords : null;
113
+ }
114
+
115
+ /** @internalRemarks Source: @carto/react-core */
116
+ function _clean(
117
+ geometry: Polygon | MultiPolygon | null
118
+ ): Polygon | MultiPolygon | null {
119
+ if (!geometry) {
120
+ return null;
121
+ }
122
+
123
+ if (_isPolygon(geometry)) {
124
+ const coords = _cleanPolygonCoords((geometry as Polygon).coordinates);
125
+ return coords ? polygon(coords).geometry : null;
126
+ }
127
+
128
+ if (_isMultiPolygon(geometry)) {
129
+ const coords = _cleanMultiPolygonCoords(
130
+ (geometry as MultiPolygon).coordinates
131
+ );
132
+ return coords ? multiPolygon(coords as Position[][][]).geometry : null;
133
+ }
134
+
135
+ return null;
136
+ }
137
+
138
+ /** @internalRemarks Source: @carto/react-core */
139
+ function _txContourCoords(cc: Position[], distance: number) {
140
+ return cc.map((c) => [c[0] + distance, c[1]]);
141
+ }
142
+
143
+ /** @internalRemarks Source: @carto/react-core */
144
+ function _txPolygonCoords(ccc: Position[][], distance: number) {
145
+ return ccc.map((cc) => _txContourCoords(cc, distance));
146
+ }
147
+
148
+ /** @internalRemarks Source: @carto/react-core */
149
+ function _txMultiPolygonCoords(cccc: Position[][][], distance: number) {
150
+ return cccc.map((ccc) => _txPolygonCoords(ccc, distance));
151
+ }
152
+
153
+ /** @internalRemarks Source: @carto/react-core */
154
+ function _tx(geometry: Polygon | MultiPolygon, distance: number) {
155
+ if (geometry && getType(geometry) === 'Polygon') {
156
+ const coords = _txPolygonCoords(
157
+ (geometry as Polygon).coordinates,
158
+ distance
159
+ );
160
+ return polygon(coords).geometry;
161
+ } else if (geometry && getType(geometry) === 'MultiPolygon') {
162
+ const coords = _txMultiPolygonCoords(
163
+ (geometry as MultiPolygon).coordinates,
164
+ distance
165
+ );
166
+ return multiPolygon(coords).geometry;
167
+ } else {
168
+ return null;
169
+ }
170
+ }
171
+
172
+ function _isPolygon(geometry: Geometry): geometry is Polygon {
173
+ return getType(geometry) === 'Polygon';
174
+ }
175
+
176
+ function _isMultiPolygon(geometry: Geometry): geometry is MultiPolygon {
177
+ return getType(geometry) === 'MultiPolygon';
178
+ }
package/src/index.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  export * from './client.js';
2
2
  export * from './constants.js';
3
3
  export * from './filters.js';
4
+ export * from './geo.js';
4
5
  export * from './sources/index.js';
5
6
  export * from './types.js';
@@ -81,7 +81,7 @@ export async function h3TableSource(
81
81
  props: H3TableSourceOptions
82
82
  ): Promise<H3TableSourceResponse> {
83
83
  assignDefaultProps(props);
84
- const response = await _h3TableSource(props);
84
+ const response = await _h3TableSource(props as _H3TableSourceOptions);
85
85
  return {...response, widgetSource: new WidgetTableSource(props)};
86
86
  }
87
87
 
@@ -90,7 +90,7 @@ export async function h3QuerySource(
90
90
  props: H3QuerySourceOptions
91
91
  ): Promise<H3QuerySourceResponse> {
92
92
  assignDefaultProps(props);
93
- const response = await _h3QuerySource(props);
93
+ const response = await _h3QuerySource(props as _H3QuerySourceOptions);
94
94
  return {...response, widgetSource: new WidgetQuerySource(props)};
95
95
  }
96
96
 
@@ -109,7 +109,9 @@ export async function quadbinTableSource(
109
109
  props: QuadbinTableSourceOptions & WidgetBaseSourceProps
110
110
  ): Promise<QuadbinTableSourceResponse> {
111
111
  assignDefaultProps(props);
112
- const response = await _quadbinTableSource(props);
112
+ const response = await _quadbinTableSource(
113
+ props as _QuadbinTableSourceOptions
114
+ );
113
115
  return {...response, widgetSource: new WidgetTableSource(props)};
114
116
  }
115
117
 
@@ -118,7 +120,9 @@ export async function quadbinQuerySource(
118
120
  props: QuadbinQuerySourceOptions & WidgetBaseSourceProps
119
121
  ): Promise<QuadbinQuerySourceResponse> {
120
122
  assignDefaultProps(props);
121
- const response = await _quadbinQuerySource(props);
123
+ const response = await _quadbinQuerySource(
124
+ props as _QuadbinQuerySourceOptions
125
+ );
122
126
  return {...response, widgetSource: new WidgetQuerySource(props)};
123
127
  }
124
128
 
package/src/types.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import type {FilterType} from './constants.js';
2
+ import type {Polygon, MultiPolygon} from 'geojson';
2
3
 
3
4
  /******************************************************************************
4
5
  * AGGREGATION
@@ -23,7 +24,7 @@ export type AggregationType =
23
24
  */
24
25
 
25
26
  /** @internalRemarks Source: @carto/react-api */
26
- export type SpatialFilter = GeoJSON.Polygon | GeoJSON.MultiPolygon;
27
+ export type SpatialFilter = Polygon | MultiPolygon;
27
28
 
28
29
  /** @internalRemarks Source: @carto/react-api, @deck.gl/carto */
29
30
  export interface Filter {