@carto/api-client 0.0.44 → 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.
@@ -1,3 +1,9 @@
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 { featureCollection, feature, polygon, multiPolygon } from '@turf/helpers';
6
+
1
7
  /**
2
8
  * @internal
3
9
  * @internalRemarks Source: @carto/react-core
@@ -22,21 +28,6 @@ function setClient(c) {
22
28
  client = c;
23
29
  }
24
30
 
25
- /**
26
- * Defines a step size increment for use with {@link TimeSeriesRequestOptions}.
27
- *
28
- * @internalRemarks Source: @carto/react-core
29
- */
30
- var GroupDateType;
31
- (function (GroupDateType) {
32
- GroupDateType["YEARS"] = "year";
33
- GroupDateType["MONTHS"] = "month";
34
- GroupDateType["WEEKS"] = "week";
35
- GroupDateType["DAYS"] = "day";
36
- GroupDateType["HOURS"] = "hour";
37
- GroupDateType["MINUTES"] = "minute";
38
- GroupDateType["SECONDS"] = "second";
39
- })(GroupDateType || (GroupDateType = {}));
40
31
  /**
41
32
  * Defines a comparator used when matching a column's values against given filter values.
42
33
  *
@@ -221,6 +212,129 @@ function getFilter(filters, {
221
212
  return null;
222
213
  }
223
214
 
215
+ /**
216
+ * Returns a {@link SpatialFilter} for a given viewport, typically obtained
217
+ * from deck.gl's `viewport.getBounds()` method ([west, south, east, north]).
218
+ * If the viewport covers the entire world (to some margin of error in Web
219
+ * Mercator space), `undefined` is returned instead.
220
+ *
221
+ * If the viewport extends beyond longitude range [-180, +180], the polygon
222
+ * may be reformatted for compatibility with CARTO APIs.
223
+ */
224
+ function createViewportSpatialFilter(viewport) {
225
+ if (_isGlobalViewport(viewport)) {
226
+ return;
227
+ }
228
+ return createPolygonSpatialFilter(bboxPolygon(viewport).geometry);
229
+ }
230
+ /**
231
+ * Returns a {@link SpatialFilter} for a given {@link Polygon} or
232
+ * {@link MultiPolygon}. If the polygon(s) extend outside longitude
233
+ * range [-180, +180], the result may be reformatted for compatibility
234
+ * with CARTO APIs.
235
+ */
236
+ function createPolygonSpatialFilter(spatialFilter) {
237
+ return spatialFilter && _normalizeGeometry(spatialFilter) || undefined;
238
+ }
239
+ /**
240
+ * Check if a viewport is large enough to represent a global coverage.
241
+ * In this case the spatial filter parameter for widget calculation is removed.
242
+ *
243
+ * @internalRemarks Source: @carto/react-core
244
+ */
245
+ function _isGlobalViewport(viewport) {
246
+ const [minx, miny, maxx, maxy] = viewport;
247
+ return maxx - minx > 179.5 * 2 && maxy - miny > 85.05 * 2;
248
+ }
249
+ /**
250
+ * Normalized a geometry, coming from a mask or a viewport. The parts
251
+ * spanning outside longitude range [-180, +180] are clipped and "folded"
252
+ * back to the valid range and unioned to the polygons inide that range.
253
+ *
254
+ * It results in a Polygon or MultiPolygon strictly inside the validity range.
255
+ *
256
+ * @internalRemarks Source: @carto/react-core
257
+ */
258
+ function _normalizeGeometry(geometry) {
259
+ const WORLD = [-180, -90, +180, +90];
260
+ const worldClip = _clean(bboxClip(geometry, WORLD).geometry);
261
+ const geometryTxWest = _tx(geometry, 360);
262
+ const geometryTxEast = _tx(geometry, -360);
263
+ let result = worldClip;
264
+ if (result && geometryTxWest) {
265
+ const worldWestClip = _clean(bboxClip(geometryTxWest, WORLD).geometry);
266
+ if (worldWestClip) {
267
+ const collection = featureCollection([feature(result), feature(worldWestClip)]);
268
+ const merged = union(collection);
269
+ result = merged ? _clean(merged.geometry) : result;
270
+ }
271
+ }
272
+ if (result && geometryTxEast) {
273
+ const worldEastClip = _clean(bboxClip(geometryTxEast, WORLD).geometry);
274
+ if (worldEastClip) {
275
+ const collection = featureCollection([feature(result), feature(worldEastClip)]);
276
+ const merged = union(collection);
277
+ result = merged ? _clean(merged.geometry) : result;
278
+ }
279
+ }
280
+ return result;
281
+ }
282
+ /** @internalRemarks Source: @carto/react-core */
283
+ function _cleanPolygonCoords(cc) {
284
+ const coords = cc.filter(c => c.length > 0);
285
+ return coords.length > 0 ? coords : null;
286
+ }
287
+ /** @internalRemarks Source: @carto/react-core */
288
+ function _cleanMultiPolygonCoords(ccc) {
289
+ const coords = ccc.map(_cleanPolygonCoords).filter(cc => cc);
290
+ return coords.length > 0 ? coords : null;
291
+ }
292
+ /** @internalRemarks Source: @carto/react-core */
293
+ function _clean(geometry) {
294
+ if (!geometry) {
295
+ return null;
296
+ }
297
+ if (_isPolygon(geometry)) {
298
+ const coords = _cleanPolygonCoords(geometry.coordinates);
299
+ return coords ? polygon(coords).geometry : null;
300
+ }
301
+ if (_isMultiPolygon(geometry)) {
302
+ const coords = _cleanMultiPolygonCoords(geometry.coordinates);
303
+ return coords ? multiPolygon(coords).geometry : null;
304
+ }
305
+ return null;
306
+ }
307
+ /** @internalRemarks Source: @carto/react-core */
308
+ function _txContourCoords(cc, distance) {
309
+ return cc.map(c => [c[0] + distance, c[1]]);
310
+ }
311
+ /** @internalRemarks Source: @carto/react-core */
312
+ function _txPolygonCoords(ccc, distance) {
313
+ return ccc.map(cc => _txContourCoords(cc, distance));
314
+ }
315
+ /** @internalRemarks Source: @carto/react-core */
316
+ function _txMultiPolygonCoords(cccc, distance) {
317
+ return cccc.map(ccc => _txPolygonCoords(ccc, distance));
318
+ }
319
+ /** @internalRemarks Source: @carto/react-core */
320
+ function _tx(geometry, distance) {
321
+ if (geometry && getType(geometry) === 'Polygon') {
322
+ const coords = _txPolygonCoords(geometry.coordinates, distance);
323
+ return polygon(coords).geometry;
324
+ } else if (geometry && getType(geometry) === 'MultiPolygon') {
325
+ const coords = _txMultiPolygonCoords(geometry.coordinates, distance);
326
+ return multiPolygon(coords).geometry;
327
+ } else {
328
+ return null;
329
+ }
330
+ }
331
+ function _isPolygon(geometry) {
332
+ return getType(geometry) === 'Polygon';
333
+ }
334
+ function _isMultiPolygon(geometry) {
335
+ return getType(geometry) === 'MultiPolygon';
336
+ }
337
+
224
338
  function _extends() {
225
339
  return _extends = Object.assign ? Object.assign.bind() : function (n) {
226
340
  for (var e = 1; e < arguments.length; e++) {
@@ -234,7 +348,7 @@ function _objectWithoutPropertiesLoose(r, e) {
234
348
  if (null == r) return {};
235
349
  var t = {};
236
350
  for (var n in r) if ({}.hasOwnProperty.call(r, n)) {
237
- if (e.indexOf(n) >= 0) continue;
351
+ if (e.includes(n)) continue;
238
352
  t[n] = r[n];
239
353
  }
240
354
  return t;
@@ -653,8 +767,8 @@ class WidgetBaseSource {
653
767
  columns,
654
768
  sortBy,
655
769
  sortDirection,
656
- page = 0,
657
- rowsPerPage = 10
770
+ offset = 0,
771
+ limit = 10
658
772
  } = params;
659
773
  return executeModel({
660
774
  model: 'table',
@@ -665,8 +779,8 @@ class WidgetBaseSource {
665
779
  column: columns,
666
780
  sortBy,
667
781
  sortDirection,
668
- limit: rowsPerPage,
669
- offset: page * rowsPerPage
782
+ limit,
783
+ offset
670
784
  },
671
785
  opts: {
672
786
  abortController
@@ -801,14 +915,13 @@ class WidgetTableSource extends WidgetBaseSource {
801
915
  }
802
916
  }
803
917
 
804
- // loaders.gl
805
- const isObject = x => x !== null && typeof x === 'object';
806
- const isPureObject = x => isObject(x) && x.constructor === {}.constructor;
807
-
808
918
  const DEFAULT_TILE_RESOLUTION = 0.5;
809
919
  const DEFAULT_AGGREGATION_RES_LEVEL_H3 = 4;
810
920
  const DEFAULT_AGGREGATION_RES_LEVEL_QUADBIN = 6;
811
921
 
922
+ const isObject = x => x !== null && typeof x === 'object';
923
+ const isPureObject = x => isObject(x) && x.constructor === {}.constructor;
924
+
812
925
  /**
813
926
  *
814
927
  * Custom error for reported errors in CARTO Maps API.
@@ -816,7 +929,7 @@ const DEFAULT_AGGREGATION_RES_LEVEL_QUADBIN = 6;
816
929
  *
817
930
  */
818
931
  class CartoAPIError extends Error {
819
- constructor(error, errorContext, response) {
932
+ constructor(error, errorContext, response, responseJson) {
820
933
  let responseString = 'Failed to connect';
821
934
  if (response) {
822
935
  responseString = 'Server returned: ';
@@ -842,6 +955,7 @@ class CartoAPIError extends Error {
842
955
  super(message);
843
956
  this.name = 'CartoAPIError';
844
957
  this.response = response;
958
+ this.responseJson = responseJson;
845
959
  this.error = error;
846
960
  this.errorContext = errorContext;
847
961
  }
@@ -856,15 +970,8 @@ function formatErrorKey(key) {
856
970
  const DEFAULT_API_BASE_URL = 'https://gcp-us-east1.api.carto.com';
857
971
  const DEFAULT_CLIENT = 'deck-gl-carto';
858
972
  const V3_MINOR_VERSION = '3.4';
859
- const MAX_GET_LENGTH = 8192;
860
- const DEFAULT_PARAMETERS = {
861
- v: V3_MINOR_VERSION,
862
- deckglVersion: "0.0.44"
863
- };
864
- const DEFAULT_HEADERS = {
865
- Accept: 'application/json',
866
- 'Content-Type': 'application/json'
867
- };
973
+ // Fastly default limit is 8192; leave some padding.
974
+ const DEFAULT_MAX_LENGTH_URL = 7000;
868
975
 
869
976
  function joinPath(...args) {
870
977
  return args.map(part => part.endsWith('/') ? part.slice(0, -1) : part).join('/');
@@ -881,36 +988,42 @@ function buildSourceUrl({
881
988
  }
882
989
 
883
990
  /**
884
- * Simple encode parameter
991
+ * Parameters added to all requests issued with `requestWithParameters()`.
992
+ * These parameters override parameters already in the base URL, but not
993
+ * user-provided parameters.
885
994
  */
886
- function encodeParameter(name, value) {
887
- if (isPureObject(value) || Array.isArray(value)) {
888
- return `${name}=${encodeURIComponent(JSON.stringify(value))}`;
889
- }
890
- return `${name}=${encodeURIComponent(value)}`;
891
- }
995
+ const DEFAULT_PARAMETERS = {
996
+ v: V3_MINOR_VERSION,
997
+ deckglVersion: "0.1.1"
998
+ };
999
+ const DEFAULT_HEADERS = {
1000
+ Accept: 'application/json',
1001
+ 'Content-Type': 'application/json'
1002
+ };
892
1003
  const REQUEST_CACHE = new Map();
893
1004
  async function requestWithParameters({
894
1005
  baseUrl,
895
- parameters,
896
- headers: customHeaders,
897
- errorContext
1006
+ parameters = {},
1007
+ headers: customHeaders = {},
1008
+ errorContext,
1009
+ maxLengthURL = DEFAULT_MAX_LENGTH_URL
898
1010
  }) {
899
1011
  parameters = {
900
1012
  ...DEFAULT_PARAMETERS,
901
1013
  ...parameters
902
1014
  };
903
- const key = createCacheKey(baseUrl, parameters || {}, customHeaders || {});
1015
+ baseUrl = excludeURLParameters(baseUrl, Object.keys(parameters));
1016
+ const key = createCacheKey(baseUrl, parameters, customHeaders);
904
1017
  if (REQUEST_CACHE.has(key)) {
905
1018
  return REQUEST_CACHE.get(key);
906
1019
  }
907
- const url = parameters ? createURLWithParameters(baseUrl, parameters) : baseUrl;
1020
+ const url = createURLWithParameters(baseUrl, parameters);
908
1021
  const headers = {
909
1022
  ...DEFAULT_HEADERS,
910
1023
  ...customHeaders
911
1024
  };
912
1025
  /* global fetch */
913
- const fetchPromise = url.length > MAX_GET_LENGTH ? fetch(baseUrl, {
1026
+ const fetchPromise = url.length > maxLengthURL ? fetch(baseUrl, {
914
1027
  method: 'POST',
915
1028
  body: JSON.stringify(parameters),
916
1029
  headers
@@ -918,17 +1031,19 @@ async function requestWithParameters({
918
1031
  headers
919
1032
  });
920
1033
  let response;
1034
+ let responseJson;
921
1035
  const jsonPromise = fetchPromise.then(_response => {
922
1036
  response = _response;
923
1037
  return response.json();
924
1038
  }).then(json => {
1039
+ responseJson = json;
925
1040
  if (!response || !response.ok) {
926
1041
  throw new Error(json.error);
927
1042
  }
928
1043
  return json;
929
1044
  }).catch(error => {
930
1045
  REQUEST_CACHE.delete(key);
931
- throw new CartoAPIError(error, errorContext, response);
1046
+ throw new CartoAPIError(error, errorContext, response, responseJson);
932
1047
  });
933
1048
  REQUEST_CACHE.set(key, jsonPromise);
934
1049
  return jsonPromise;
@@ -942,9 +1057,33 @@ function createCacheKey(baseUrl, parameters, headers) {
942
1057
  headers: headerEntries
943
1058
  });
944
1059
  }
945
- function createURLWithParameters(baseUrl, parameters) {
946
- const encodedParameters = Object.entries(parameters).map(([key, value]) => encodeParameter(key, value));
947
- return `${baseUrl}?${encodedParameters.join('&')}`;
1060
+ /**
1061
+ * Appends query string parameters to a URL. Existing URL parameters are kept,
1062
+ * unless there is a conflict, in which case the new parameters override
1063
+ * those already in the URL.
1064
+ */
1065
+ function createURLWithParameters(baseUrlString, parameters) {
1066
+ const baseUrl = new URL(baseUrlString);
1067
+ for (const [key, value] of Object.entries(parameters)) {
1068
+ if (isPureObject(value) || Array.isArray(value)) {
1069
+ baseUrl.searchParams.set(key, JSON.stringify(value));
1070
+ } else {
1071
+ baseUrl.searchParams.set(key, value.toString());
1072
+ }
1073
+ }
1074
+ return baseUrl.toString();
1075
+ }
1076
+ /**
1077
+ * Deletes query string parameters from a URL.
1078
+ */
1079
+ function excludeURLParameters(baseUrlString, parameters) {
1080
+ const baseUrl = new URL(baseUrlString);
1081
+ for (const param of parameters) {
1082
+ if (baseUrl.searchParams.has(param)) {
1083
+ baseUrl.searchParams.delete(param);
1084
+ }
1085
+ }
1086
+ return baseUrl.toString();
948
1087
  }
949
1088
 
950
1089
  /* eslint-disable camelcase */
@@ -952,7 +1091,8 @@ const SOURCE_DEFAULTS = {
952
1091
  apiBaseUrl: DEFAULT_API_BASE_URL,
953
1092
  clientId: DEFAULT_CLIENT,
954
1093
  format: 'tilejson',
955
- headers: {}
1094
+ headers: {},
1095
+ maxLengthURL: DEFAULT_MAX_LENGTH_URL
956
1096
  };
957
1097
  async function baseSource(endpoint, options, urlParameters) {
958
1098
  const {
@@ -975,6 +1115,7 @@ async function baseSource(endpoint, options, urlParameters) {
975
1115
  const baseUrl = buildSourceUrl(mergedOptions);
976
1116
  const {
977
1117
  clientId,
1118
+ maxLengthURL,
978
1119
  format
979
1120
  } = mergedOptions;
980
1121
  const headers = {
@@ -995,7 +1136,8 @@ async function baseSource(endpoint, options, urlParameters) {
995
1136
  baseUrl,
996
1137
  parameters,
997
1138
  headers,
998
- errorContext
1139
+ errorContext,
1140
+ maxLengthURL
999
1141
  });
1000
1142
  const dataUrl = mapInstantiation[format].url[0];
1001
1143
  if (cache) {
@@ -1006,7 +1148,8 @@ async function baseSource(endpoint, options, urlParameters) {
1006
1148
  const json = await requestWithParameters({
1007
1149
  baseUrl: dataUrl,
1008
1150
  headers,
1009
- errorContext
1151
+ errorContext,
1152
+ maxLengthURL
1010
1153
  });
1011
1154
  if (accessToken) {
1012
1155
  json.accessToken = accessToken;
@@ -1016,7 +1159,8 @@ async function baseSource(endpoint, options, urlParameters) {
1016
1159
  return await requestWithParameters({
1017
1160
  baseUrl: dataUrl,
1018
1161
  headers,
1019
- errorContext
1162
+ errorContext,
1163
+ maxLengthURL
1020
1164
  });
1021
1165
  }
1022
1166
 
@@ -1025,13 +1169,11 @@ const boundaryQuerySource = async function (options) {
1025
1169
  columns,
1026
1170
  filters,
1027
1171
  tilesetTableName,
1028
- matchingColumn = 'id',
1029
1172
  propertiesSqlQuery,
1030
1173
  queryParameters
1031
1174
  } = options;
1032
1175
  const urlParameters = {
1033
1176
  tilesetTableName,
1034
- matchingColumn,
1035
1177
  propertiesSqlQuery
1036
1178
  };
1037
1179
  if (columns) {
@@ -1051,12 +1193,10 @@ const boundaryTableSource = async function (options) {
1051
1193
  filters,
1052
1194
  tilesetTableName,
1053
1195
  columns,
1054
- matchingColumn = 'id',
1055
1196
  propertiesTableName
1056
1197
  } = options;
1057
1198
  const urlParameters = {
1058
1199
  tilesetTableName,
1059
- matchingColumn,
1060
1200
  propertiesTableName
1061
1201
  };
1062
1202
  if (columns) {
@@ -1075,7 +1215,8 @@ const h3QuerySource$1 = async function (options) {
1075
1215
  aggregationResLevel = DEFAULT_AGGREGATION_RES_LEVEL_H3,
1076
1216
  sqlQuery,
1077
1217
  spatialDataColumn = 'h3',
1078
- queryParameters
1218
+ queryParameters,
1219
+ filters
1079
1220
  } = options;
1080
1221
  const urlParameters = {
1081
1222
  aggregationExp,
@@ -1089,6 +1230,9 @@ const h3QuerySource$1 = async function (options) {
1089
1230
  if (queryParameters) {
1090
1231
  urlParameters.queryParameters = queryParameters;
1091
1232
  }
1233
+ if (filters) {
1234
+ urlParameters.filters = filters;
1235
+ }
1092
1236
  return baseSource('query', options, urlParameters);
1093
1237
  };
1094
1238
 
@@ -1098,7 +1242,8 @@ const h3TableSource$1 = async function (options) {
1098
1242
  aggregationExp,
1099
1243
  aggregationResLevel = DEFAULT_AGGREGATION_RES_LEVEL_H3,
1100
1244
  spatialDataColumn = 'h3',
1101
- tableName
1245
+ tableName,
1246
+ filters
1102
1247
  } = options;
1103
1248
  const urlParameters = {
1104
1249
  aggregationExp,
@@ -1109,6 +1254,9 @@ const h3TableSource$1 = async function (options) {
1109
1254
  if (aggregationResLevel) {
1110
1255
  urlParameters.aggregationResLevel = String(aggregationResLevel);
1111
1256
  }
1257
+ if (filters) {
1258
+ urlParameters.filters = filters;
1259
+ }
1112
1260
  return baseSource('table', options, urlParameters);
1113
1261
  };
1114
1262
 
@@ -1124,11 +1272,15 @@ const h3TilesetSource = async function (options) {
1124
1272
 
1125
1273
  const rasterSource = async function (options) {
1126
1274
  const {
1127
- tableName
1275
+ tableName,
1276
+ filters
1128
1277
  } = options;
1129
1278
  const urlParameters = {
1130
1279
  name: tableName
1131
1280
  };
1281
+ if (filters) {
1282
+ urlParameters.filters = filters;
1283
+ }
1132
1284
  return baseSource('raster', options, urlParameters);
1133
1285
  };
1134
1286
 
@@ -1139,7 +1291,8 @@ const quadbinQuerySource$1 = async function (options) {
1139
1291
  aggregationResLevel = DEFAULT_AGGREGATION_RES_LEVEL_QUADBIN,
1140
1292
  sqlQuery,
1141
1293
  spatialDataColumn = 'quadbin',
1142
- queryParameters
1294
+ queryParameters,
1295
+ filters
1143
1296
  } = options;
1144
1297
  const urlParameters = {
1145
1298
  aggregationExp,
@@ -1153,6 +1306,9 @@ const quadbinQuerySource$1 = async function (options) {
1153
1306
  if (queryParameters) {
1154
1307
  urlParameters.queryParameters = queryParameters;
1155
1308
  }
1309
+ if (filters) {
1310
+ urlParameters.filters = filters;
1311
+ }
1156
1312
  return baseSource('query', options, urlParameters);
1157
1313
  };
1158
1314
 
@@ -1162,7 +1318,8 @@ const quadbinTableSource$1 = async function (options) {
1162
1318
  aggregationExp,
1163
1319
  aggregationResLevel = DEFAULT_AGGREGATION_RES_LEVEL_QUADBIN,
1164
1320
  spatialDataColumn = 'quadbin',
1165
- tableName
1321
+ tableName,
1322
+ filters
1166
1323
  } = options;
1167
1324
  const urlParameters = {
1168
1325
  aggregationExp,
@@ -1173,6 +1330,9 @@ const quadbinTableSource$1 = async function (options) {
1173
1330
  if (aggregationResLevel) {
1174
1331
  urlParameters.aggregationResLevel = String(aggregationResLevel);
1175
1332
  }
1333
+ if (filters) {
1334
+ urlParameters.filters = filters;
1335
+ }
1176
1336
  return baseSource('table', options, urlParameters);
1177
1337
  };
1178
1338
 
@@ -1320,5 +1480,5 @@ function assignDefaultProps(props) {
1320
1480
  }
1321
1481
  }
1322
1482
 
1323
- export { FilterType, GroupDateType, WidgetBaseSource, WidgetQuerySource, WidgetTableSource, addFilter, clearFilters, getClient, getFilter, h3QuerySource, h3TableSource, hasFilter, quadbinQuerySource, quadbinTableSource, removeFilter, setClient, vectorQuerySource, vectorTableSource };
1483
+ export { FilterType, WidgetBaseSource, WidgetQuerySource, WidgetTableSource, addFilter, clearFilters, createPolygonSpatialFilter, createViewportSpatialFilter, getClient, getFilter, h3QuerySource, h3TableSource, hasFilter, quadbinQuerySource, quadbinTableSource, removeFilter, setClient, vectorQuerySource, vectorTableSource };
1324
1484
  //# sourceMappingURL=api-client.modern.js.map