@carto/api-client 0.1.0 → 0.2.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.
- package/CHANGELOG.md +2 -2
- package/README.md +10 -26
- package/build/api-client.cjs +227 -44
- package/build/api-client.cjs.map +1 -1
- package/build/api-client.modern.js +221 -46
- package/build/api-client.modern.js.map +1 -1
- package/build/geo.d.ts +19 -0
- package/build/index.d.ts +1 -0
- package/build/types.d.ts +2 -1
- package/package.json +22 -14
- package/src/geo.ts +178 -0
- package/src/index.ts +1 -0
- package/src/sources/wrappers.ts +8 -4
- package/src/types.ts +2 -1
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
package/src/sources/wrappers.ts
CHANGED
|
@@ -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(
|
|
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(
|
|
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 =
|
|
27
|
+
export type SpatialFilter = Polygon | MultiPolygon;
|
|
27
28
|
|
|
28
29
|
/** @internalRemarks Source: @carto/react-api, @deck.gl/carto */
|
|
29
30
|
export interface Filter {
|