@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,15 @@
1
+ var bboxClip = require('@turf/bbox-clip');
2
+ var bboxPolygon = require('@turf/bbox-polygon');
3
+ var union = require('@turf/union');
4
+ var invariant = require('@turf/invariant');
5
+ var helpers = require('@turf/helpers');
6
+
7
+ function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
8
+
9
+ var bboxClip__default = /*#__PURE__*/_interopDefaultLegacy(bboxClip);
10
+ var bboxPolygon__default = /*#__PURE__*/_interopDefaultLegacy(bboxPolygon);
11
+ var union__default = /*#__PURE__*/_interopDefaultLegacy(union);
12
+
1
13
  /**
2
14
  * @internal
3
15
  * @internalRemarks Source: @carto/react-core
@@ -22,21 +34,6 @@ function setClient(c) {
22
34
  client = c;
23
35
  }
24
36
 
25
- /**
26
- * Defines a step size increment for use with {@link TimeSeriesRequestOptions}.
27
- *
28
- * @internalRemarks Source: @carto/react-core
29
- */
30
- exports.GroupDateType = void 0;
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
- })(exports.GroupDateType || (exports.GroupDateType = {}));
40
37
  /**
41
38
  * Defines a comparator used when matching a column's values against given filter values.
42
39
  *
@@ -222,6 +219,129 @@ function getFilter(filters, _ref4) {
222
219
  return null;
223
220
  }
224
221
 
222
+ /**
223
+ * Returns a {@link SpatialFilter} for a given viewport, typically obtained
224
+ * from deck.gl's `viewport.getBounds()` method ([west, south, east, north]).
225
+ * If the viewport covers the entire world (to some margin of error in Web
226
+ * Mercator space), `undefined` is returned instead.
227
+ *
228
+ * If the viewport extends beyond longitude range [-180, +180], the polygon
229
+ * may be reformatted for compatibility with CARTO APIs.
230
+ */
231
+ function createViewportSpatialFilter(viewport) {
232
+ if (_isGlobalViewport(viewport)) {
233
+ return;
234
+ }
235
+ return createPolygonSpatialFilter(bboxPolygon__default["default"](viewport).geometry);
236
+ }
237
+ /**
238
+ * Returns a {@link SpatialFilter} for a given {@link Polygon} or
239
+ * {@link MultiPolygon}. If the polygon(s) extend outside longitude
240
+ * range [-180, +180], the result may be reformatted for compatibility
241
+ * with CARTO APIs.
242
+ */
243
+ function createPolygonSpatialFilter(spatialFilter) {
244
+ return spatialFilter && _normalizeGeometry(spatialFilter) || undefined;
245
+ }
246
+ /**
247
+ * Check if a viewport is large enough to represent a global coverage.
248
+ * In this case the spatial filter parameter for widget calculation is removed.
249
+ *
250
+ * @internalRemarks Source: @carto/react-core
251
+ */
252
+ function _isGlobalViewport(viewport) {
253
+ const [minx, miny, maxx, maxy] = viewport;
254
+ return maxx - minx > 179.5 * 2 && maxy - miny > 85.05 * 2;
255
+ }
256
+ /**
257
+ * Normalized a geometry, coming from a mask or a viewport. The parts
258
+ * spanning outside longitude range [-180, +180] are clipped and "folded"
259
+ * back to the valid range and unioned to the polygons inide that range.
260
+ *
261
+ * It results in a Polygon or MultiPolygon strictly inside the validity range.
262
+ *
263
+ * @internalRemarks Source: @carto/react-core
264
+ */
265
+ function _normalizeGeometry(geometry) {
266
+ const WORLD = [-180, -90, +180, +90];
267
+ const worldClip = _clean(bboxClip__default["default"](geometry, WORLD).geometry);
268
+ const geometryTxWest = _tx(geometry, 360);
269
+ const geometryTxEast = _tx(geometry, -360);
270
+ let result = worldClip;
271
+ if (result && geometryTxWest) {
272
+ const worldWestClip = _clean(bboxClip__default["default"](geometryTxWest, WORLD).geometry);
273
+ if (worldWestClip) {
274
+ const collection = helpers.featureCollection([helpers.feature(result), helpers.feature(worldWestClip)]);
275
+ const merged = union__default["default"](collection);
276
+ result = merged ? _clean(merged.geometry) : result;
277
+ }
278
+ }
279
+ if (result && geometryTxEast) {
280
+ const worldEastClip = _clean(bboxClip__default["default"](geometryTxEast, WORLD).geometry);
281
+ if (worldEastClip) {
282
+ const collection = helpers.featureCollection([helpers.feature(result), helpers.feature(worldEastClip)]);
283
+ const merged = union__default["default"](collection);
284
+ result = merged ? _clean(merged.geometry) : result;
285
+ }
286
+ }
287
+ return result;
288
+ }
289
+ /** @internalRemarks Source: @carto/react-core */
290
+ function _cleanPolygonCoords(cc) {
291
+ const coords = cc.filter(c => c.length > 0);
292
+ return coords.length > 0 ? coords : null;
293
+ }
294
+ /** @internalRemarks Source: @carto/react-core */
295
+ function _cleanMultiPolygonCoords(ccc) {
296
+ const coords = ccc.map(_cleanPolygonCoords).filter(cc => cc);
297
+ return coords.length > 0 ? coords : null;
298
+ }
299
+ /** @internalRemarks Source: @carto/react-core */
300
+ function _clean(geometry) {
301
+ if (!geometry) {
302
+ return null;
303
+ }
304
+ if (_isPolygon(geometry)) {
305
+ const coords = _cleanPolygonCoords(geometry.coordinates);
306
+ return coords ? helpers.polygon(coords).geometry : null;
307
+ }
308
+ if (_isMultiPolygon(geometry)) {
309
+ const coords = _cleanMultiPolygonCoords(geometry.coordinates);
310
+ return coords ? helpers.multiPolygon(coords).geometry : null;
311
+ }
312
+ return null;
313
+ }
314
+ /** @internalRemarks Source: @carto/react-core */
315
+ function _txContourCoords(cc, distance) {
316
+ return cc.map(c => [c[0] + distance, c[1]]);
317
+ }
318
+ /** @internalRemarks Source: @carto/react-core */
319
+ function _txPolygonCoords(ccc, distance) {
320
+ return ccc.map(cc => _txContourCoords(cc, distance));
321
+ }
322
+ /** @internalRemarks Source: @carto/react-core */
323
+ function _txMultiPolygonCoords(cccc, distance) {
324
+ return cccc.map(ccc => _txPolygonCoords(ccc, distance));
325
+ }
326
+ /** @internalRemarks Source: @carto/react-core */
327
+ function _tx(geometry, distance) {
328
+ if (geometry && invariant.getType(geometry) === 'Polygon') {
329
+ const coords = _txPolygonCoords(geometry.coordinates, distance);
330
+ return helpers.polygon(coords).geometry;
331
+ } else if (geometry && invariant.getType(geometry) === 'MultiPolygon') {
332
+ const coords = _txMultiPolygonCoords(geometry.coordinates, distance);
333
+ return helpers.multiPolygon(coords).geometry;
334
+ } else {
335
+ return null;
336
+ }
337
+ }
338
+ function _isPolygon(geometry) {
339
+ return invariant.getType(geometry) === 'Polygon';
340
+ }
341
+ function _isMultiPolygon(geometry) {
342
+ return invariant.getType(geometry) === 'MultiPolygon';
343
+ }
344
+
225
345
  /******************************************************************************
226
346
  * DEFAULTS
227
347
  */
@@ -700,8 +820,8 @@ class WidgetBaseSource {
700
820
  columns,
701
821
  sortBy,
702
822
  sortDirection,
703
- page = 0,
704
- rowsPerPage = 10
823
+ offset = 0,
824
+ limit = 10
705
825
  } = params;
706
826
  return Promise.resolve(executeModel({
707
827
  model: 'table',
@@ -713,8 +833,8 @@ class WidgetBaseSource {
713
833
  column: columns,
714
834
  sortBy,
715
835
  sortDirection,
716
- limit: rowsPerPage,
717
- offset: page * rowsPerPage
836
+ limit,
837
+ offset
718
838
  },
719
839
  opts: {
720
840
  abortController
@@ -857,14 +977,13 @@ class WidgetTableSource extends WidgetBaseSource {
857
977
  }
858
978
  }
859
979
 
860
- // loaders.gl
861
- const isObject = x => x !== null && typeof x === 'object';
862
- const isPureObject = x => isObject(x) && x.constructor === {}.constructor;
863
-
864
980
  const DEFAULT_TILE_RESOLUTION = 0.5;
865
981
  const DEFAULT_AGGREGATION_RES_LEVEL_H3 = 4;
866
982
  const DEFAULT_AGGREGATION_RES_LEVEL_QUADBIN = 6;
867
983
 
984
+ const isObject = x => x !== null && typeof x === 'object';
985
+ const isPureObject = x => isObject(x) && x.constructor === {}.constructor;
986
+
868
987
  /**
869
988
  *
870
989
  * Custom error for reported errors in CARTO Maps API.
@@ -872,7 +991,7 @@ const DEFAULT_AGGREGATION_RES_LEVEL_QUADBIN = 6;
872
991
  *
873
992
  */
874
993
  class CartoAPIError extends Error {
875
- constructor(error, errorContext, response) {
994
+ constructor(error, errorContext, response, responseJson) {
876
995
  let responseString = 'Failed to connect';
877
996
  if (response) {
878
997
  responseString = 'Server returned: ';
@@ -898,6 +1017,7 @@ class CartoAPIError extends Error {
898
1017
  super(message);
899
1018
  this.name = 'CartoAPIError';
900
1019
  this.response = response;
1020
+ this.responseJson = responseJson;
901
1021
  this.error = error;
902
1022
  this.errorContext = errorContext;
903
1023
  }
@@ -912,15 +1032,8 @@ function formatErrorKey(key) {
912
1032
  const DEFAULT_API_BASE_URL = 'https://gcp-us-east1.api.carto.com';
913
1033
  const DEFAULT_CLIENT = 'deck-gl-carto';
914
1034
  const V3_MINOR_VERSION = '3.4';
915
- const MAX_GET_LENGTH = 8192;
916
- const DEFAULT_PARAMETERS = {
917
- v: V3_MINOR_VERSION,
918
- deckglVersion: "0.0.44"
919
- };
920
- const DEFAULT_HEADERS = {
921
- Accept: 'application/json',
922
- 'Content-Type': 'application/json'
923
- };
1035
+ // Fastly default limit is 8192; leave some padding.
1036
+ const DEFAULT_MAX_LENGTH_URL = 7000;
924
1037
 
925
1038
  function joinPath(...args) {
926
1039
  return args.map(part => part.endsWith('/') ? part.slice(0, -1) : part).join('/');
@@ -937,36 +1050,42 @@ function buildSourceUrl({
937
1050
  }
938
1051
 
939
1052
  /**
940
- * Simple encode parameter
1053
+ * Parameters added to all requests issued with `requestWithParameters()`.
1054
+ * These parameters override parameters already in the base URL, but not
1055
+ * user-provided parameters.
941
1056
  */
942
- function encodeParameter(name, value) {
943
- if (isPureObject(value) || Array.isArray(value)) {
944
- return `${name}=${encodeURIComponent(JSON.stringify(value))}`;
945
- }
946
- return `${name}=${encodeURIComponent(value)}`;
947
- }
1057
+ const DEFAULT_PARAMETERS = {
1058
+ v: V3_MINOR_VERSION,
1059
+ deckglVersion: "0.1.1"
1060
+ };
1061
+ const DEFAULT_HEADERS = {
1062
+ Accept: 'application/json',
1063
+ 'Content-Type': 'application/json'
1064
+ };
948
1065
  const REQUEST_CACHE = new Map();
949
1066
  async function requestWithParameters({
950
1067
  baseUrl,
951
- parameters,
952
- headers: customHeaders,
953
- errorContext
1068
+ parameters = {},
1069
+ headers: customHeaders = {},
1070
+ errorContext,
1071
+ maxLengthURL = DEFAULT_MAX_LENGTH_URL
954
1072
  }) {
955
1073
  parameters = {
956
1074
  ...DEFAULT_PARAMETERS,
957
1075
  ...parameters
958
1076
  };
959
- const key = createCacheKey(baseUrl, parameters || {}, customHeaders || {});
1077
+ baseUrl = excludeURLParameters(baseUrl, Object.keys(parameters));
1078
+ const key = createCacheKey(baseUrl, parameters, customHeaders);
960
1079
  if (REQUEST_CACHE.has(key)) {
961
1080
  return REQUEST_CACHE.get(key);
962
1081
  }
963
- const url = parameters ? createURLWithParameters(baseUrl, parameters) : baseUrl;
1082
+ const url = createURLWithParameters(baseUrl, parameters);
964
1083
  const headers = {
965
1084
  ...DEFAULT_HEADERS,
966
1085
  ...customHeaders
967
1086
  };
968
1087
  /* global fetch */
969
- const fetchPromise = url.length > MAX_GET_LENGTH ? fetch(baseUrl, {
1088
+ const fetchPromise = url.length > maxLengthURL ? fetch(baseUrl, {
970
1089
  method: 'POST',
971
1090
  body: JSON.stringify(parameters),
972
1091
  headers
@@ -974,17 +1093,19 @@ async function requestWithParameters({
974
1093
  headers
975
1094
  });
976
1095
  let response;
1096
+ let responseJson;
977
1097
  const jsonPromise = fetchPromise.then(_response => {
978
1098
  response = _response;
979
1099
  return response.json();
980
1100
  }).then(json => {
1101
+ responseJson = json;
981
1102
  if (!response || !response.ok) {
982
1103
  throw new Error(json.error);
983
1104
  }
984
1105
  return json;
985
1106
  }).catch(error => {
986
1107
  REQUEST_CACHE.delete(key);
987
- throw new CartoAPIError(error, errorContext, response);
1108
+ throw new CartoAPIError(error, errorContext, response, responseJson);
988
1109
  });
989
1110
  REQUEST_CACHE.set(key, jsonPromise);
990
1111
  return jsonPromise;
@@ -998,9 +1119,33 @@ function createCacheKey(baseUrl, parameters, headers) {
998
1119
  headers: headerEntries
999
1120
  });
1000
1121
  }
1001
- function createURLWithParameters(baseUrl, parameters) {
1002
- const encodedParameters = Object.entries(parameters).map(([key, value]) => encodeParameter(key, value));
1003
- return `${baseUrl}?${encodedParameters.join('&')}`;
1122
+ /**
1123
+ * Appends query string parameters to a URL. Existing URL parameters are kept,
1124
+ * unless there is a conflict, in which case the new parameters override
1125
+ * those already in the URL.
1126
+ */
1127
+ function createURLWithParameters(baseUrlString, parameters) {
1128
+ const baseUrl = new URL(baseUrlString);
1129
+ for (const [key, value] of Object.entries(parameters)) {
1130
+ if (isPureObject(value) || Array.isArray(value)) {
1131
+ baseUrl.searchParams.set(key, JSON.stringify(value));
1132
+ } else {
1133
+ baseUrl.searchParams.set(key, value.toString());
1134
+ }
1135
+ }
1136
+ return baseUrl.toString();
1137
+ }
1138
+ /**
1139
+ * Deletes query string parameters from a URL.
1140
+ */
1141
+ function excludeURLParameters(baseUrlString, parameters) {
1142
+ const baseUrl = new URL(baseUrlString);
1143
+ for (const param of parameters) {
1144
+ if (baseUrl.searchParams.has(param)) {
1145
+ baseUrl.searchParams.delete(param);
1146
+ }
1147
+ }
1148
+ return baseUrl.toString();
1004
1149
  }
1005
1150
 
1006
1151
  /* eslint-disable camelcase */
@@ -1008,7 +1153,8 @@ const SOURCE_DEFAULTS = {
1008
1153
  apiBaseUrl: DEFAULT_API_BASE_URL,
1009
1154
  clientId: DEFAULT_CLIENT,
1010
1155
  format: 'tilejson',
1011
- headers: {}
1156
+ headers: {},
1157
+ maxLengthURL: DEFAULT_MAX_LENGTH_URL
1012
1158
  };
1013
1159
  async function baseSource(endpoint, options, urlParameters) {
1014
1160
  const {
@@ -1031,6 +1177,7 @@ async function baseSource(endpoint, options, urlParameters) {
1031
1177
  const baseUrl = buildSourceUrl(mergedOptions);
1032
1178
  const {
1033
1179
  clientId,
1180
+ maxLengthURL,
1034
1181
  format
1035
1182
  } = mergedOptions;
1036
1183
  const headers = {
@@ -1051,7 +1198,8 @@ async function baseSource(endpoint, options, urlParameters) {
1051
1198
  baseUrl,
1052
1199
  parameters,
1053
1200
  headers,
1054
- errorContext
1201
+ errorContext,
1202
+ maxLengthURL
1055
1203
  });
1056
1204
  const dataUrl = mapInstantiation[format].url[0];
1057
1205
  if (cache) {
@@ -1062,7 +1210,8 @@ async function baseSource(endpoint, options, urlParameters) {
1062
1210
  const json = await requestWithParameters({
1063
1211
  baseUrl: dataUrl,
1064
1212
  headers,
1065
- errorContext
1213
+ errorContext,
1214
+ maxLengthURL
1066
1215
  });
1067
1216
  if (accessToken) {
1068
1217
  json.accessToken = accessToken;
@@ -1072,7 +1221,8 @@ async function baseSource(endpoint, options, urlParameters) {
1072
1221
  return await requestWithParameters({
1073
1222
  baseUrl: dataUrl,
1074
1223
  headers,
1075
- errorContext
1224
+ errorContext,
1225
+ maxLengthURL
1076
1226
  });
1077
1227
  }
1078
1228
 
@@ -1081,13 +1231,11 @@ const boundaryQuerySource = async function (options) {
1081
1231
  columns,
1082
1232
  filters,
1083
1233
  tilesetTableName,
1084
- matchingColumn = 'id',
1085
1234
  propertiesSqlQuery,
1086
1235
  queryParameters
1087
1236
  } = options;
1088
1237
  const urlParameters = {
1089
1238
  tilesetTableName,
1090
- matchingColumn,
1091
1239
  propertiesSqlQuery
1092
1240
  };
1093
1241
  if (columns) {
@@ -1107,12 +1255,10 @@ const boundaryTableSource = async function (options) {
1107
1255
  filters,
1108
1256
  tilesetTableName,
1109
1257
  columns,
1110
- matchingColumn = 'id',
1111
1258
  propertiesTableName
1112
1259
  } = options;
1113
1260
  const urlParameters = {
1114
1261
  tilesetTableName,
1115
- matchingColumn,
1116
1262
  propertiesTableName
1117
1263
  };
1118
1264
  if (columns) {
@@ -1131,7 +1277,8 @@ const h3QuerySource$1 = async function (options) {
1131
1277
  aggregationResLevel = DEFAULT_AGGREGATION_RES_LEVEL_H3,
1132
1278
  sqlQuery,
1133
1279
  spatialDataColumn = 'h3',
1134
- queryParameters
1280
+ queryParameters,
1281
+ filters
1135
1282
  } = options;
1136
1283
  const urlParameters = {
1137
1284
  aggregationExp,
@@ -1145,6 +1292,9 @@ const h3QuerySource$1 = async function (options) {
1145
1292
  if (queryParameters) {
1146
1293
  urlParameters.queryParameters = queryParameters;
1147
1294
  }
1295
+ if (filters) {
1296
+ urlParameters.filters = filters;
1297
+ }
1148
1298
  return baseSource('query', options, urlParameters);
1149
1299
  };
1150
1300
 
@@ -1154,7 +1304,8 @@ const h3TableSource$1 = async function (options) {
1154
1304
  aggregationExp,
1155
1305
  aggregationResLevel = DEFAULT_AGGREGATION_RES_LEVEL_H3,
1156
1306
  spatialDataColumn = 'h3',
1157
- tableName
1307
+ tableName,
1308
+ filters
1158
1309
  } = options;
1159
1310
  const urlParameters = {
1160
1311
  aggregationExp,
@@ -1165,6 +1316,9 @@ const h3TableSource$1 = async function (options) {
1165
1316
  if (aggregationResLevel) {
1166
1317
  urlParameters.aggregationResLevel = String(aggregationResLevel);
1167
1318
  }
1319
+ if (filters) {
1320
+ urlParameters.filters = filters;
1321
+ }
1168
1322
  return baseSource('table', options, urlParameters);
1169
1323
  };
1170
1324
 
@@ -1180,11 +1334,15 @@ const h3TilesetSource = async function (options) {
1180
1334
 
1181
1335
  const rasterSource = async function (options) {
1182
1336
  const {
1183
- tableName
1337
+ tableName,
1338
+ filters
1184
1339
  } = options;
1185
1340
  const urlParameters = {
1186
1341
  name: tableName
1187
1342
  };
1343
+ if (filters) {
1344
+ urlParameters.filters = filters;
1345
+ }
1188
1346
  return baseSource('raster', options, urlParameters);
1189
1347
  };
1190
1348
 
@@ -1195,7 +1353,8 @@ const quadbinQuerySource$1 = async function (options) {
1195
1353
  aggregationResLevel = DEFAULT_AGGREGATION_RES_LEVEL_QUADBIN,
1196
1354
  sqlQuery,
1197
1355
  spatialDataColumn = 'quadbin',
1198
- queryParameters
1356
+ queryParameters,
1357
+ filters
1199
1358
  } = options;
1200
1359
  const urlParameters = {
1201
1360
  aggregationExp,
@@ -1209,6 +1368,9 @@ const quadbinQuerySource$1 = async function (options) {
1209
1368
  if (queryParameters) {
1210
1369
  urlParameters.queryParameters = queryParameters;
1211
1370
  }
1371
+ if (filters) {
1372
+ urlParameters.filters = filters;
1373
+ }
1212
1374
  return baseSource('query', options, urlParameters);
1213
1375
  };
1214
1376
 
@@ -1218,7 +1380,8 @@ const quadbinTableSource$1 = async function (options) {
1218
1380
  aggregationExp,
1219
1381
  aggregationResLevel = DEFAULT_AGGREGATION_RES_LEVEL_QUADBIN,
1220
1382
  spatialDataColumn = 'quadbin',
1221
- tableName
1383
+ tableName,
1384
+ filters
1222
1385
  } = options;
1223
1386
  const urlParameters = {
1224
1387
  aggregationExp,
@@ -1229,6 +1392,9 @@ const quadbinTableSource$1 = async function (options) {
1229
1392
  if (aggregationResLevel) {
1230
1393
  urlParameters.aggregationResLevel = String(aggregationResLevel);
1231
1394
  }
1395
+ if (filters) {
1396
+ urlParameters.filters = filters;
1397
+ }
1232
1398
  return baseSource('table', options, urlParameters);
1233
1399
  };
1234
1400
 
@@ -1418,6 +1584,8 @@ exports.WidgetQuerySource = WidgetQuerySource;
1418
1584
  exports.WidgetTableSource = WidgetTableSource;
1419
1585
  exports.addFilter = addFilter;
1420
1586
  exports.clearFilters = clearFilters;
1587
+ exports.createPolygonSpatialFilter = createPolygonSpatialFilter;
1588
+ exports.createViewportSpatialFilter = createViewportSpatialFilter;
1421
1589
  exports.getClient = getClient;
1422
1590
  exports.getFilter = getFilter;
1423
1591
  exports.h3QuerySource = h3QuerySource;