@carto/api-client 0.4.3 → 0.5.0-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.
Files changed (93) hide show
  1. package/CHANGELOG.md +4 -0
  2. package/build/api/query.d.ts +1 -1
  3. package/build/api-client.cjs +2388 -261
  4. package/build/api-client.cjs.map +1 -1
  5. package/build/api-client.modern.js +2237 -262
  6. package/build/api-client.modern.js.map +1 -1
  7. package/build/constants.d.ts +22 -0
  8. package/build/filters/Filter.d.ts +13 -0
  9. package/build/filters/FilterTypes.d.ts +3 -0
  10. package/build/filters/geosjonFeatures.d.ts +8 -0
  11. package/build/filters/index.d.ts +6 -0
  12. package/build/filters/tileFeatures.d.ts +20 -0
  13. package/build/filters/tileFeaturesGeometries.d.ts +13 -0
  14. package/build/filters/tileFeaturesSpatialIndex.d.ts +10 -0
  15. package/build/index.d.ts +4 -0
  16. package/build/models/index.d.ts +1 -1
  17. package/build/models/model.d.ts +7 -1
  18. package/build/operations/aggregation.d.ts +8 -0
  19. package/build/operations/applySorting.d.ts +20 -0
  20. package/build/operations/groupBy.d.ts +15 -0
  21. package/build/operations/groupByDate.d.ts +11 -0
  22. package/build/operations/histogram.d.ts +13 -0
  23. package/build/operations/index.d.ts +6 -0
  24. package/build/operations/scatterPlot.d.ts +14 -0
  25. package/build/sources/h3-tileset-source.d.ts +2 -1
  26. package/build/sources/index.d.ts +1 -1
  27. package/build/sources/quadbin-tileset-source.d.ts +2 -1
  28. package/build/sources/types.d.ts +36 -41
  29. package/build/sources/vector-tileset-source.d.ts +2 -1
  30. package/build/spatial-index.d.ts +8 -0
  31. package/build/types-internal.d.ts +4 -0
  32. package/build/types.d.ts +61 -1
  33. package/build/utils/dateUtils.d.ts +10 -0
  34. package/build/utils/getTileFormat.d.ts +3 -0
  35. package/build/utils/makeIntervalComplete.d.ts +2 -0
  36. package/build/utils/transformTileCoordsToWGS84.d.ts +8 -0
  37. package/build/utils/transformToTileCoords.d.ts +9 -0
  38. package/build/utils.d.ts +1 -1
  39. package/build/widget-sources/index.d.ts +2 -1
  40. package/build/widget-sources/types.d.ts +40 -23
  41. package/build/widget-sources/widget-query-source.d.ts +2 -2
  42. package/build/widget-sources/widget-remote-source.d.ts +18 -0
  43. package/build/widget-sources/{widget-base-source.d.ts → widget-source.d.ts} +16 -41
  44. package/build/widget-sources/widget-table-source.d.ts +2 -2
  45. package/build/widget-sources/widget-tileset-source.d.ts +67 -0
  46. package/package.json +36 -35
  47. package/src/api/query.ts +1 -2
  48. package/src/constants.ts +25 -0
  49. package/src/filters/Filter.ts +169 -0
  50. package/src/filters/FilterTypes.ts +109 -0
  51. package/src/filters/geosjonFeatures.ts +32 -0
  52. package/src/filters/index.ts +6 -0
  53. package/src/filters/tileFeatures.ts +56 -0
  54. package/src/filters/tileFeaturesGeometries.ts +444 -0
  55. package/src/filters/tileFeaturesSpatialIndex.ts +119 -0
  56. package/src/index.ts +6 -0
  57. package/src/models/index.ts +1 -1
  58. package/src/models/model.ts +47 -24
  59. package/src/operations/aggregation.ts +154 -0
  60. package/src/operations/applySorting.ts +109 -0
  61. package/src/operations/groupBy.ts +59 -0
  62. package/src/operations/groupByDate.ts +98 -0
  63. package/src/operations/histogram.ts +66 -0
  64. package/src/operations/index.ts +6 -0
  65. package/src/operations/scatterPlot.ts +50 -0
  66. package/src/sources/h3-query-source.ts +7 -1
  67. package/src/sources/h3-table-source.ts +6 -1
  68. package/src/sources/h3-tileset-source.ts +18 -6
  69. package/src/sources/index.ts +1 -1
  70. package/src/sources/quadbin-query-source.ts +6 -1
  71. package/src/sources/quadbin-table-source.ts +6 -1
  72. package/src/sources/quadbin-tileset-source.ts +18 -6
  73. package/src/sources/raster-source.ts +1 -0
  74. package/src/sources/types.ts +41 -45
  75. package/src/sources/vector-query-source.ts +10 -3
  76. package/src/sources/vector-table-source.ts +10 -3
  77. package/src/sources/vector-tileset-source.ts +19 -6
  78. package/src/spatial-index.ts +111 -0
  79. package/src/types-internal.ts +6 -0
  80. package/src/types.ts +60 -2
  81. package/src/utils/dateUtils.ts +28 -0
  82. package/src/utils/getTileFormat.ts +9 -0
  83. package/src/utils/makeIntervalComplete.ts +17 -0
  84. package/src/utils/transformTileCoordsToWGS84.ts +77 -0
  85. package/src/utils/transformToTileCoords.ts +85 -0
  86. package/src/utils.ts +9 -6
  87. package/src/widget-sources/index.ts +2 -1
  88. package/src/widget-sources/types.ts +42 -23
  89. package/src/widget-sources/widget-query-source.ts +6 -3
  90. package/src/widget-sources/{widget-base-source.ts → widget-remote-source.ts} +169 -144
  91. package/src/widget-sources/widget-source.ts +160 -0
  92. package/src/widget-sources/widget-table-source.ts +6 -3
  93. package/src/widget-sources/widget-tileset-source.ts +396 -0
@@ -3,6 +3,10 @@ import bboxPolygon from '@turf/bbox-polygon';
3
3
  import union from '@turf/union';
4
4
  import { getType } from '@turf/invariant';
5
5
  import { featureCollection, feature, polygon, multiPolygon } from '@turf/helpers';
6
+ import intersects from '@turf/boolean-intersects';
7
+ import booleanWithin from '@turf/boolean-within';
8
+ import intersect from '@turf/intersect';
9
+ import { getResolution as getResolution$2, polygonToCells } from 'h3-js';
6
10
 
7
11
  /**
8
12
  * @internal
@@ -61,6 +65,31 @@ var ApiVersion;
61
65
  })(ApiVersion || (ApiVersion = {}));
62
66
  /** @internalRemarks Source: @carto/constants, @deck.gl/carto */
63
67
  const DEFAULT_API_BASE_URL = 'https://gcp-us-east1.api.carto.com';
68
+ /** @internalRemarks Source: @carto/react-core */
69
+ var TileFormat;
70
+ (function (TileFormat) {
71
+ TileFormat["MVT"] = "mvt";
72
+ TileFormat["JSON"] = "json";
73
+ TileFormat["GEOJSON"] = "geojson";
74
+ TileFormat["BINARY"] = "binary";
75
+ })(TileFormat || (TileFormat = {}));
76
+ /** @internalRemarks Source: @carto/react-core */
77
+ var SpatialIndex;
78
+ (function (SpatialIndex) {
79
+ SpatialIndex["H3"] = "h3";
80
+ SpatialIndex["S2"] = "s2";
81
+ SpatialIndex["QUADBIN"] = "quadbin";
82
+ })(SpatialIndex || (SpatialIndex = {}));
83
+ /** @internalRemarks Source: @carto/react-core */
84
+ var Provider;
85
+ (function (Provider) {
86
+ Provider["BIGQUERY"] = "bigquery";
87
+ Provider["REDSHIFT"] = "redshift";
88
+ Provider["POSTGRES"] = "postgres";
89
+ Provider["SNOWFLAKE"] = "snowflake";
90
+ Provider["DATABRICKS"] = "databricks";
91
+ Provider["DATABRICKS_REST"] = "databricksRest";
92
+ })(Provider || (Provider = {}));
64
93
 
65
94
  const FILTER_TYPES = new Set(Object.values(FilterType));
66
95
  const isFilterType = type => FILTER_TYPES.has(type);
@@ -103,7 +132,7 @@ function normalizeObjectKeys(el) {
103
132
  }, {});
104
133
  }
105
134
  /** @internalRemarks Source: @carto/react-core */
106
- function assert(condition, message) {
135
+ function assert$1(condition, message) {
107
136
  if (!condition) {
108
137
  throw new Error(message);
109
138
  }
@@ -606,7 +635,7 @@ function excludeURLParameters(baseUrlString, parameters) {
606
635
  return baseUrl.toString();
607
636
  }
608
637
 
609
- const _excluded$1 = ["accessToken", "connectionName", "cache"];
638
+ const _excluded$2 = ["accessToken", "connectionName", "cache"];
610
639
  const SOURCE_DEFAULTS = {
611
640
  apiBaseUrl: DEFAULT_API_BASE_URL,
612
641
  clientId: getClient(),
@@ -620,7 +649,7 @@ async function baseSource(endpoint, options, urlParameters) {
620
649
  connectionName,
621
650
  cache
622
651
  } = options,
623
- optionalOptions = _objectWithoutPropertiesLoose(options, _excluded$1);
652
+ optionalOptions = _objectWithoutPropertiesLoose(options, _excluded$2);
624
653
  const mergedOptions = _extends({}, SOURCE_DEFAULTS, {
625
654
  accessToken,
626
655
  connectionName,
@@ -686,6 +715,8 @@ async function baseSource(endpoint, options, urlParameters) {
686
715
  }
687
716
 
688
717
  // deck.gl
718
+ // SPDX-License-Identifier: MIT
719
+ // Copyright (c) vis.gl contributors
689
720
  const boundaryQuerySource = async function boundaryQuerySource(options) {
690
721
  const {
691
722
  columns,
@@ -711,6 +742,8 @@ const boundaryQuerySource = async function boundaryQuerySource(options) {
711
742
  };
712
743
 
713
744
  // deck.gl
745
+ // SPDX-License-Identifier: MIT
746
+ // Copyright (c) vis.gl contributors
714
747
  const boundaryTableSource = async function boundaryTableSource(options) {
715
748
  const {
716
749
  filters,
@@ -804,10 +837,10 @@ const REQUEST_GET_MAX_URL_LENGTH = 2048;
804
837
  * @internalRemarks Source: @carto/react-api
805
838
  */
806
839
  function executeModel(props) {
807
- assert(props.source, 'executeModel: missing source');
808
- assert(props.model, 'executeModel: missing model');
809
- assert(props.params, 'executeModel: missing params');
810
- assert(AVAILABLE_MODELS.includes(props.model), `executeModel: model provided isn't valid. Available models: ${AVAILABLE_MODELS.join(', ')}`);
840
+ assert$1(props.source, 'executeModel: missing source');
841
+ assert$1(props.model, 'executeModel: missing model');
842
+ assert$1(props.params, 'executeModel: missing params');
843
+ assert$1(AVAILABLE_MODELS.includes(props.model), `executeModel: model provided isn't valid. Available models: ${AVAILABLE_MODELS.join(', ')}`);
811
844
  const {
812
845
  model,
813
846
  source,
@@ -822,50 +855,52 @@ function executeModel(props) {
822
855
  connectionName,
823
856
  clientId
824
857
  } = source;
825
- assert(apiBaseUrl, 'executeModel: missing apiBaseUrl');
826
- assert(accessToken, 'executeModel: missing accessToken');
827
- assert(apiVersion === V3, 'executeModel: SQL Model API requires CARTO 3+');
828
- assert(type !== 'tileset', 'executeModel: Tilesets not supported');
858
+ assert$1(apiBaseUrl, 'executeModel: missing apiBaseUrl');
859
+ assert$1(accessToken, 'executeModel: missing accessToken');
860
+ assert$1(apiVersion === V3, 'executeModel: SQL Model API requires CARTO 3+');
861
+ assert$1(type !== 'tileset', 'executeModel: Tilesets not supported');
829
862
  let url = `${apiBaseUrl}/v3/sql/${connectionName}/model/${model}`;
830
863
  const {
831
864
  data,
832
865
  filters,
833
866
  filtersLogicalOperator = 'and',
834
- geoColumn = DEFAULT_GEO_COLUMN
867
+ spatialDataType = 'geo',
868
+ spatialFiltersMode = 'intersects',
869
+ spatialFiltersResolution = 0
835
870
  } = source;
836
- const queryParameters = source.queryParameters ? JSON.stringify(source.queryParameters) : '';
837
871
  const queryParams = {
838
872
  type,
839
873
  client: clientId,
840
874
  source: data,
841
- params: JSON.stringify(params),
842
- queryParameters,
843
- filters: JSON.stringify(filters),
875
+ params,
876
+ queryParameters: source.queryParameters || '',
877
+ filters,
844
878
  filtersLogicalOperator
845
879
  };
880
+ const spatialDataColumn = source.spatialDataColumn || DEFAULT_GEO_COLUMN;
846
881
  // Picking Model API requires 'spatialDataColumn'.
847
882
  if (model === 'pick') {
848
- queryParams.spatialDataColumn = geoColumn;
883
+ queryParams.spatialDataColumn = spatialDataColumn;
849
884
  }
850
- // API supports multiple filters, we apply it only to geoColumn
885
+ // API supports multiple filters, we apply it only to spatialDataColumn
851
886
  const spatialFilters = source.spatialFilter ? {
852
- [geoColumn]: source.spatialFilter
887
+ [spatialDataColumn]: source.spatialFilter
853
888
  } : undefined;
854
889
  if (spatialFilters) {
855
- queryParams.spatialFilters = JSON.stringify(spatialFilters);
890
+ queryParams.spatialFilters = spatialFilters;
891
+ queryParams.spatialDataColumn = spatialDataColumn;
892
+ queryParams.spatialDataType = spatialDataType;
893
+ }
894
+ if (spatialDataType !== 'geo') {
895
+ if (spatialFiltersResolution > 0) {
896
+ queryParams.spatialFiltersResolution = spatialFiltersResolution;
897
+ }
898
+ queryParams.spatialFiltersMode = spatialFiltersMode;
856
899
  }
857
- const urlWithSearchParams = url + '?' + new URLSearchParams(queryParams).toString();
900
+ const urlWithSearchParams = url + '?' + objectToURLSearchParams(queryParams).toString();
858
901
  const isGet = urlWithSearchParams.length <= REQUEST_GET_MAX_URL_LENGTH;
859
902
  if (isGet) {
860
903
  url = urlWithSearchParams;
861
- } else {
862
- // undo the JSON.stringify, @TODO find a better pattern
863
- queryParams.params = params;
864
- queryParams.filters = filters;
865
- queryParams.queryParameters = source.queryParameters;
866
- if (spatialFilters) {
867
- queryParams.spatialFilters = spatialFilters;
868
- }
869
904
  }
870
905
  return makeCall({
871
906
  url,
@@ -877,24 +912,72 @@ function executeModel(props) {
877
912
  })
878
913
  });
879
914
  }
915
+ function objectToURLSearchParams(object) {
916
+ const params = new URLSearchParams();
917
+ for (const key in object) {
918
+ if (isPureObject(object[key])) {
919
+ params.append(key, JSON.stringify(object[key]));
920
+ } else if (Array.isArray(object[key])) {
921
+ params.append(key, JSON.stringify(object[key]));
922
+ } else if (object[key] === null) {
923
+ params.append(key, 'null');
924
+ } else if (object[key] !== undefined) {
925
+ params.append(key, String(object[key]));
926
+ }
927
+ }
928
+ return params;
929
+ }
930
+
931
+ const DEFAULT_TILE_SIZE = 512;
932
+ const QUADBIN_ZOOM_MAX_OFFSET = 4;
933
+ function getSpatialFiltersResolution(source, viewState) {
934
+ var _source$dataResolutio, _source$aggregationRe;
935
+ const dataResolution = (_source$dataResolutio = source.dataResolution) != null ? _source$dataResolutio : Number.MAX_VALUE;
936
+ const aggregationResLevel = (_source$aggregationRe = source.aggregationResLevel) != null ? _source$aggregationRe : source.spatialDataType === 'h3' ? DEFAULT_AGGREGATION_RES_LEVEL_H3 : DEFAULT_AGGREGATION_RES_LEVEL_QUADBIN;
937
+ const aggregationResLevelOffset = Math.max(0, Math.floor(aggregationResLevel));
938
+ const currentZoomInt = Math.ceil(viewState.zoom);
939
+ if (source.spatialDataType === 'h3') {
940
+ var _maxH3SpatialFiltersR, _maxH3SpatialFiltersR2;
941
+ const tileSize = DEFAULT_TILE_SIZE;
942
+ const maxResolutionForZoom = (_maxH3SpatialFiltersR = (_maxH3SpatialFiltersR2 = maxH3SpatialFiltersResolutions.find(([zoom]) => zoom === currentZoomInt)) == null ? void 0 : _maxH3SpatialFiltersR2[1]) != null ? _maxH3SpatialFiltersR : Math.max(0, currentZoomInt - 3);
943
+ const maxSpatialFiltersResolution = maxResolutionForZoom ? Math.min(dataResolution, maxResolutionForZoom) : dataResolution;
944
+ const hexagonResolution = getHexagonResolution(viewState, tileSize) + aggregationResLevelOffset;
945
+ return Math.min(hexagonResolution, maxSpatialFiltersResolution);
946
+ }
947
+ if (source.spatialDataType === 'quadbin') {
948
+ const maxResolutionForZoom = currentZoomInt + QUADBIN_ZOOM_MAX_OFFSET;
949
+ const maxSpatialFiltersResolution = Math.min(dataResolution, maxResolutionForZoom);
950
+ const quadsResolution = Math.floor(viewState.zoom) + aggregationResLevelOffset;
951
+ return Math.min(quadsResolution, maxSpatialFiltersResolution);
952
+ }
953
+ return undefined;
954
+ }
955
+ const maxH3SpatialFiltersResolutions = [[20, 14], [19, 13], [18, 12], [17, 11], [16, 10], [15, 9], [14, 8], [13, 7], [12, 7], [11, 7], [10, 6], [9, 6], [8, 5], [7, 4], [6, 4], [5, 3], [4, 2], [3, 1], [2, 1], [1, 0]];
956
+ // stolen from https://github.com/visgl/deck.gl/blob/master/modules/carto/src/layers/h3-tileset-2d.ts
957
+ // Relative scale factor (0 = no biasing, 2 = a few hexagons cover view)
958
+ const BIAS = 2;
959
+ // Resolution conversion function. Takes a WebMercatorViewport and returns
960
+ // a H3 resolution such that the screen space size of the hexagons is
961
+ // similar
962
+ function getHexagonResolution(viewport, tileSize) {
963
+ // Difference in given tile size compared to deck's internal 512px tile size,
964
+ // expressed as an offset to the viewport zoom.
965
+ const zoomOffset = Math.log2(tileSize / DEFAULT_TILE_SIZE);
966
+ const hexagonScaleFactor = 2 / 3 * (viewport.zoom - zoomOffset);
967
+ const latitudeScaleFactor = Math.log(1 / Math.cos(Math.PI * viewport.latitude / 180));
968
+ // Clip and bias
969
+ return Math.max(0, Math.floor(hexagonScaleFactor + latitudeScaleFactor - BIAS));
970
+ }
880
971
 
881
- const _excluded = ["filterOwner", "spatialFilter", "abortController"],
882
- _excluded2 = ["filterOwner", "spatialFilter", "abortController"],
883
- _excluded3 = ["filterOwner", "spatialFilter", "abortController", "operationExp"],
884
- _excluded4 = ["filterOwner", "spatialFilter", "abortController"],
885
- _excluded5 = ["filterOwner", "spatialFilter", "abortController"],
886
- _excluded6 = ["filterOwner", "spatialFilter", "abortController"],
887
- _excluded7 = ["filterOwner", "spatialFilter", "abortController"],
888
- _excluded8 = ["filterOwner", "abortController", "spatialFilter"];
889
972
  /**
890
973
  * Source for Widget API requests on a data source defined by a SQL query.
891
974
  *
892
975
  * Abstract class. Use {@link WidgetQuerySource} or {@link WidgetTableSource}.
893
976
  */
894
- class WidgetBaseSource {
977
+ class WidgetSource {
895
978
  constructor(props) {
896
979
  this.props = void 0;
897
- this.props = _extends({}, WidgetBaseSource.defaultProps, props);
980
+ this.props = _extends({}, WidgetSource.defaultProps, props);
898
981
  }
899
982
  _getModelSource(owner) {
900
983
  const props = this.props;
@@ -906,31 +989,65 @@ class WidgetBaseSource {
906
989
  connectionName: props.connectionName,
907
990
  filters: getApplicableFilters(owner, props.filters),
908
991
  filtersLogicalOperator: props.filtersLogicalOperator,
909
- geoColumn: props.geoColumn
992
+ spatialDataType: props.spatialDataType,
993
+ spatialDataColumn: props.spatialDataColumn,
994
+ dataResolution: props.dataResolution
910
995
  };
911
996
  }
912
- /****************************************************************************
913
- * CATEGORIES
914
- */
915
- /**
916
- * Returns a list of labeled datapoints for categorical data. Suitable for
917
- * charts including grouped bar charts, pie charts, and tree charts.
918
- */
997
+ _getSpatialFiltersResolution(source, spatialFilter, referenceViewState) {
998
+ // spatialFiltersResolution applies only to spatial index sources.
999
+ if (!spatialFilter || source.spatialDataType === 'geo') {
1000
+ return;
1001
+ }
1002
+ if (!referenceViewState) {
1003
+ throw new Error('Missing required option, "spatialIndexReferenceViewState".');
1004
+ }
1005
+ return getSpatialFiltersResolution(source, referenceViewState);
1006
+ }
1007
+ }
1008
+ WidgetSource.defaultProps = {
1009
+ apiVersion: ApiVersion.V3,
1010
+ apiBaseUrl: DEFAULT_API_BASE_URL,
1011
+ clientId: getClient(),
1012
+ filters: {},
1013
+ filtersLogicalOperator: 'and'
1014
+ };
1015
+
1016
+ const _excluded$1 = ["filterOwner", "spatialFilter", "spatialFiltersMode", "spatialIndexReferenceViewState", "abortController"],
1017
+ _excluded2 = ["filterOwner", "spatialFilter", "spatialFiltersMode", "spatialIndexReferenceViewState", "abortController"],
1018
+ _excluded3 = ["filterOwner", "spatialFilter", "spatialFiltersMode", "spatialIndexReferenceViewState", "abortController", "operationExp"],
1019
+ _excluded4 = ["filterOwner", "spatialFilter", "spatialFiltersMode", "spatialIndexReferenceViewState", "abortController"],
1020
+ _excluded5 = ["filterOwner", "spatialFilter", "spatialFiltersMode", "spatialIndexReferenceViewState", "abortController"],
1021
+ _excluded6 = ["filterOwner", "spatialFilter", "spatialFiltersMode", "spatialIndexReferenceViewState", "abortController"],
1022
+ _excluded7 = ["filterOwner", "spatialFilter", "spatialFiltersMode", "spatialIndexReferenceViewState", "abortController"],
1023
+ _excluded8 = ["filterOwner", "abortController", "spatialFilter", "spatialFiltersMode", "spatialIndexReferenceViewState"];
1024
+ /**
1025
+ * Source for Widget API requests.
1026
+ *
1027
+ * Abstract class. Use {@link WidgetQuerySource} or {@link WidgetTableSource}.
1028
+ */
1029
+ class WidgetRemoteSource extends WidgetSource {
919
1030
  async getCategories(options) {
920
1031
  const {
921
1032
  filterOwner,
922
1033
  spatialFilter,
1034
+ spatialFiltersMode,
1035
+ spatialIndexReferenceViewState,
923
1036
  abortController
924
1037
  } = options,
925
- params = _objectWithoutPropertiesLoose(options, _excluded);
1038
+ params = _objectWithoutPropertiesLoose(options, _excluded$1);
926
1039
  const {
927
1040
  column,
928
1041
  operation,
929
1042
  operationColumn
930
1043
  } = params;
1044
+ const source = this.getModelSource(filterOwner);
1045
+ const spatialFiltersResolution = this._getSpatialFiltersResolution(source, spatialFilter, spatialIndexReferenceViewState);
931
1046
  return executeModel({
932
1047
  model: 'category',
933
- source: _extends({}, this.getModelSource(filterOwner), {
1048
+ source: _extends({}, source, {
1049
+ spatialFiltersResolution,
1050
+ spatialFiltersMode,
934
1051
  spatialFilter
935
1052
  }),
936
1053
  params: {
@@ -943,21 +1060,12 @@ class WidgetBaseSource {
943
1060
  }
944
1061
  }).then(res => normalizeObjectKeys(res.rows));
945
1062
  }
946
- /****************************************************************************
947
- * FEATURES
948
- */
949
- /**
950
- * Given a list of feature IDs (as found in `_carto_feature_id`) returns all
951
- * matching features. In datasets containing features with duplicate geometries,
952
- * feature IDs may be duplicated (IDs are a hash of geometry) and so more
953
- * results may be returned than IDs in the request.
954
- * @internal
955
- * @experimental
956
- */
957
1063
  async getFeatures(options) {
958
1064
  const {
959
1065
  filterOwner,
960
1066
  spatialFilter,
1067
+ spatialFiltersMode,
1068
+ spatialIndexReferenceViewState,
961
1069
  abortController
962
1070
  } = options,
963
1071
  params = _objectWithoutPropertiesLoose(options, _excluded2);
@@ -969,9 +1077,13 @@ class WidgetBaseSource {
969
1077
  limit,
970
1078
  tileResolution
971
1079
  } = params;
1080
+ const source = this.getModelSource(filterOwner);
1081
+ const spatialFiltersResolution = this._getSpatialFiltersResolution(source, spatialFilter, spatialIndexReferenceViewState);
972
1082
  return executeModel({
973
1083
  model: 'pick',
974
- source: _extends({}, this.getModelSource(filterOwner), {
1084
+ source: _extends({}, source, {
1085
+ spatialFiltersResolution,
1086
+ spatialFiltersMode,
975
1087
  spatialFilter
976
1088
  }),
977
1089
  params: {
@@ -992,17 +1104,12 @@ class WidgetBaseSource {
992
1104
  rows
993
1105
  }));
994
1106
  }
995
- /****************************************************************************
996
- * FORMULA
997
- */
998
- /**
999
- * Returns a scalar numerical statistic over all matching data. Suitable
1000
- * for 'headline' or 'scorecard' figures such as counts and sums.
1001
- */
1002
1107
  async getFormula(options) {
1003
1108
  const {
1004
1109
  filterOwner,
1005
1110
  spatialFilter,
1111
+ spatialFiltersMode,
1112
+ spatialIndexReferenceViewState,
1006
1113
  abortController,
1007
1114
  operationExp
1008
1115
  } = options,
@@ -1011,14 +1118,18 @@ class WidgetBaseSource {
1011
1118
  column,
1012
1119
  operation
1013
1120
  } = params;
1121
+ const source = this.getModelSource(filterOwner);
1122
+ const spatialFiltersResolution = this._getSpatialFiltersResolution(source, spatialFilter, spatialIndexReferenceViewState);
1014
1123
  return executeModel({
1015
1124
  model: 'formula',
1016
- source: _extends({}, this.getModelSource(filterOwner), {
1125
+ source: _extends({}, source, {
1126
+ spatialFiltersResolution,
1127
+ spatialFiltersMode,
1017
1128
  spatialFilter
1018
1129
  }),
1019
1130
  params: {
1020
1131
  column: column != null ? column : '*',
1021
- operation,
1132
+ operation: operation != null ? operation : 'count',
1022
1133
  operationExp
1023
1134
  },
1024
1135
  opts: {
@@ -1026,17 +1137,12 @@ class WidgetBaseSource {
1026
1137
  }
1027
1138
  }).then(res => normalizeObjectKeys(res.rows[0]));
1028
1139
  }
1029
- /****************************************************************************
1030
- * HISTOGRAM
1031
- */
1032
- /**
1033
- * Returns a list of labeled datapoints for 'bins' of data defined as ticks
1034
- * over a numerical range. Suitable for histogram charts.
1035
- */
1036
1140
  async getHistogram(options) {
1037
1141
  const {
1038
1142
  filterOwner,
1039
1143
  spatialFilter,
1144
+ spatialFiltersMode,
1145
+ spatialIndexReferenceViewState,
1040
1146
  abortController
1041
1147
  } = options,
1042
1148
  params = _objectWithoutPropertiesLoose(options, _excluded4);
@@ -1045,9 +1151,13 @@ class WidgetBaseSource {
1045
1151
  operation,
1046
1152
  ticks
1047
1153
  } = params;
1154
+ const source = this.getModelSource(filterOwner);
1155
+ const spatialFiltersResolution = this._getSpatialFiltersResolution(source, spatialFilter, spatialIndexReferenceViewState);
1048
1156
  const data = await executeModel({
1049
1157
  model: 'histogram',
1050
- source: _extends({}, this.getModelSource(filterOwner), {
1158
+ source: _extends({}, source, {
1159
+ spatialFiltersResolution,
1160
+ spatialFiltersMode,
1051
1161
  spatialFilter
1052
1162
  }),
1053
1163
  params: {
@@ -1071,27 +1181,25 @@ class WidgetBaseSource {
1071
1181
  }
1072
1182
  return [];
1073
1183
  }
1074
- /****************************************************************************
1075
- * RANGE
1076
- */
1077
- /**
1078
- * Returns a range (min and max) for a numerical column of matching rows.
1079
- * Suitable for displaying certain 'headline' or 'scorecard' statistics,
1080
- * or rendering a range slider UI for filtering.
1081
- */
1082
1184
  async getRange(options) {
1083
1185
  const {
1084
1186
  filterOwner,
1085
1187
  spatialFilter,
1188
+ spatialFiltersMode,
1189
+ spatialIndexReferenceViewState,
1086
1190
  abortController
1087
1191
  } = options,
1088
1192
  params = _objectWithoutPropertiesLoose(options, _excluded5);
1089
1193
  const {
1090
1194
  column
1091
1195
  } = params;
1196
+ const source = this.getModelSource(filterOwner);
1197
+ const spatialFiltersResolution = this._getSpatialFiltersResolution(source, spatialFilter, spatialIndexReferenceViewState);
1092
1198
  return executeModel({
1093
1199
  model: 'range',
1094
- source: _extends({}, this.getModelSource(filterOwner), {
1200
+ source: _extends({}, source, {
1201
+ spatialFiltersResolution,
1202
+ spatialFiltersMode,
1095
1203
  spatialFilter
1096
1204
  }),
1097
1205
  params: {
@@ -1102,17 +1210,12 @@ class WidgetBaseSource {
1102
1210
  }
1103
1211
  }).then(res => normalizeObjectKeys(res.rows[0]));
1104
1212
  }
1105
- /****************************************************************************
1106
- * SCATTER
1107
- */
1108
- /**
1109
- * Returns a list of bivariate datapoints defined as numerical 'x' and 'y'
1110
- * values. Suitable for rendering scatter plots.
1111
- */
1112
1213
  async getScatter(options) {
1113
1214
  const {
1114
1215
  filterOwner,
1115
1216
  spatialFilter,
1217
+ spatialFiltersMode,
1218
+ spatialIndexReferenceViewState,
1116
1219
  abortController
1117
1220
  } = options,
1118
1221
  params = _objectWithoutPropertiesLoose(options, _excluded6);
@@ -1122,11 +1225,15 @@ class WidgetBaseSource {
1122
1225
  yAxisColumn,
1123
1226
  yAxisJoinOperation
1124
1227
  } = params;
1228
+ const source = this.getModelSource(filterOwner);
1229
+ const spatialFiltersResolution = this._getSpatialFiltersResolution(source, spatialFilter, spatialIndexReferenceViewState);
1125
1230
  // Make sure this is sync with the same constant in cloud-native/maps-api
1126
1231
  const HARD_LIMIT = 500;
1127
1232
  return executeModel({
1128
1233
  model: 'scatterplot',
1129
- source: _extends({}, this.getModelSource(filterOwner), {
1234
+ source: _extends({}, source, {
1235
+ spatialFiltersResolution,
1236
+ spatialFiltersMode,
1130
1237
  spatialFilter
1131
1238
  }),
1132
1239
  params: {
@@ -1144,17 +1251,12 @@ class WidgetBaseSource {
1144
1251
  y
1145
1252
  }) => [x, y]));
1146
1253
  }
1147
- /****************************************************************************
1148
- * TABLE
1149
- */
1150
- /**
1151
- * Returns a list of arbitrary data rows, with support for pagination and
1152
- * sorting. Suitable for displaying tables and lists.
1153
- */
1154
1254
  async getTable(options) {
1155
1255
  const {
1156
1256
  filterOwner,
1157
1257
  spatialFilter,
1258
+ spatialFiltersMode,
1259
+ spatialIndexReferenceViewState,
1158
1260
  abortController
1159
1261
  } = options,
1160
1262
  params = _objectWithoutPropertiesLoose(options, _excluded7);
@@ -1165,9 +1267,13 @@ class WidgetBaseSource {
1165
1267
  offset = 0,
1166
1268
  limit = 10
1167
1269
  } = params;
1270
+ const source = this.getModelSource(filterOwner);
1271
+ const spatialFiltersResolution = this._getSpatialFiltersResolution(source, spatialFilter, spatialIndexReferenceViewState);
1168
1272
  return executeModel({
1169
1273
  model: 'table',
1170
- source: _extends({}, this.getModelSource(filterOwner), {
1274
+ source: _extends({}, source, {
1275
+ spatialFiltersResolution,
1276
+ spatialFiltersMode,
1171
1277
  spatialFilter
1172
1278
  }),
1173
1279
  params: {
@@ -1189,18 +1295,13 @@ class WidgetBaseSource {
1189
1295
  };
1190
1296
  });
1191
1297
  }
1192
- /****************************************************************************
1193
- * TIME SERIES
1194
- */
1195
- /**
1196
- * Returns a series of labeled numerical values, grouped into equally-sized
1197
- * time intervals. Suitable for rendering time series charts.
1198
- */
1199
1298
  async getTimeSeries(options) {
1200
1299
  const {
1201
1300
  filterOwner,
1202
1301
  abortController,
1203
- spatialFilter
1302
+ spatialFilter,
1303
+ spatialFiltersMode,
1304
+ spatialIndexReferenceViewState
1204
1305
  } = options,
1205
1306
  params = _objectWithoutPropertiesLoose(options, _excluded8);
1206
1307
  const {
@@ -1214,9 +1315,13 @@ class WidgetBaseSource {
1214
1315
  splitByCategoryLimit,
1215
1316
  splitByCategoryValues
1216
1317
  } = params;
1318
+ const source = this.getModelSource(filterOwner);
1319
+ const spatialFiltersResolution = this._getSpatialFiltersResolution(source, spatialFilter, spatialIndexReferenceViewState);
1217
1320
  return executeModel({
1218
1321
  model: 'timeseries',
1219
- source: _extends({}, this.getModelSource(filterOwner), {
1322
+ source: _extends({}, source, {
1323
+ spatialFiltersResolution,
1324
+ spatialFiltersMode,
1220
1325
  spatialFilter
1221
1326
  }),
1222
1327
  params: {
@@ -1242,14 +1347,6 @@ class WidgetBaseSource {
1242
1347
  });
1243
1348
  }
1244
1349
  }
1245
- WidgetBaseSource.defaultProps = {
1246
- apiVersion: ApiVersion.V3,
1247
- apiBaseUrl: DEFAULT_API_BASE_URL,
1248
- clientId: getClient(),
1249
- filters: {},
1250
- filtersLogicalOperator: 'and',
1251
- geoColumn: DEFAULT_GEO_COLUMN
1252
- };
1253
1350
 
1254
1351
  /**
1255
1352
  * Source for Widget API requests on a data source defined by a SQL query.
@@ -1273,7 +1370,7 @@ WidgetBaseSource.defaultProps = {
1273
1370
  * const { widgetSource } = await data;
1274
1371
  * ```
1275
1372
  */
1276
- class WidgetQuerySource extends WidgetBaseSource {
1373
+ class WidgetQuerySource extends WidgetRemoteSource {
1277
1374
  getModelSource(owner) {
1278
1375
  return _extends({}, super._getModelSource(owner), {
1279
1376
  type: 'query',
@@ -1305,7 +1402,7 @@ class WidgetQuerySource extends WidgetBaseSource {
1305
1402
  * const { widgetSource } = await data;
1306
1403
  * ```
1307
1404
  */
1308
- class WidgetTableSource extends WidgetBaseSource {
1405
+ class WidgetTableSource extends WidgetRemoteSource {
1309
1406
  getModelSource(owner) {
1310
1407
  return _extends({}, super._getModelSource(owner), {
1311
1408
  type: 'table',
@@ -1314,156 +1411,2022 @@ class WidgetTableSource extends WidgetBaseSource {
1314
1411
  }
1315
1412
  }
1316
1413
 
1317
- const h3QuerySource = async function h3QuerySource(options) {
1318
- const {
1319
- aggregationExp,
1320
- aggregationResLevel = DEFAULT_AGGREGATION_RES_LEVEL_H3,
1321
- sqlQuery,
1322
- spatialDataColumn = 'h3',
1323
- queryParameters,
1324
- filters
1325
- } = options;
1326
- const urlParameters = {
1327
- aggregationExp,
1328
- spatialDataColumn,
1329
- spatialDataType: 'h3',
1330
- q: sqlQuery
1331
- };
1332
- if (aggregationResLevel) {
1333
- urlParameters.aggregationResLevel = String(aggregationResLevel);
1334
- }
1335
- if (queryParameters) {
1336
- urlParameters.queryParameters = queryParameters;
1337
- }
1338
- if (filters) {
1339
- urlParameters.filters = filters;
1340
- }
1341
- return baseSource('query', options, urlParameters).then(result => _extends({}, result, {
1342
- widgetSource: new WidgetQuerySource(options)
1343
- }));
1344
- };
1414
+ function makeIntervalComplete(intervals) {
1415
+ return intervals.map(val => {
1416
+ if (val[0] === undefined || val[0] === null) {
1417
+ return [Number.MIN_SAFE_INTEGER, val[1]];
1418
+ }
1419
+ if (val[1] === undefined || val[1] === null) {
1420
+ return [val[0], Number.MAX_SAFE_INTEGER];
1421
+ }
1422
+ return val;
1423
+ });
1424
+ }
1345
1425
 
1346
- const h3TableSource = async function h3TableSource(options) {
1347
- const {
1348
- aggregationExp,
1349
- aggregationResLevel = DEFAULT_AGGREGATION_RES_LEVEL_H3,
1350
- spatialDataColumn = 'h3',
1351
- tableName,
1352
- filters
1353
- } = options;
1354
- const urlParameters = {
1355
- aggregationExp,
1356
- name: tableName,
1357
- spatialDataColumn,
1358
- spatialDataType: 'h3'
1426
+ const filterFunctions = {
1427
+ [FilterType.IN]: filterIn,
1428
+ [FilterType.BETWEEN]: filterBetween,
1429
+ [FilterType.TIME]: filterTime,
1430
+ [FilterType.CLOSED_OPEN]: filterClosedOpen,
1431
+ [FilterType.STRING_SEARCH]: filterStringSearch
1432
+ };
1433
+ function filterIn(filterValues, featureValue) {
1434
+ return filterValues.includes(featureValue);
1435
+ }
1436
+ // FilterTypes.BETWEEN
1437
+ function filterBetween(filterValues, featureValue) {
1438
+ const checkRange = range => {
1439
+ const [lowerBound, upperBound] = range;
1440
+ return featureValue >= lowerBound && featureValue <= upperBound;
1359
1441
  };
1360
- if (aggregationResLevel) {
1361
- urlParameters.aggregationResLevel = String(aggregationResLevel);
1362
- }
1363
- if (filters) {
1364
- urlParameters.filters = filters;
1442
+ return makeIntervalComplete(filterValues).some(checkRange);
1443
+ }
1444
+ function filterTime(filterValues, featureValue) {
1445
+ const featureValueAsTimestamp = new Date(featureValue).getTime();
1446
+ if (isFinite(featureValueAsTimestamp)) {
1447
+ return filterBetween(filterValues, featureValueAsTimestamp);
1448
+ } else {
1449
+ throw new Error(`Column used to filter by time isn't well formatted.`);
1365
1450
  }
1366
- return baseSource('table', options, urlParameters).then(result => _extends({}, result, {
1367
- widgetSource: new WidgetTableSource(options)
1368
- }));
1369
- };
1370
-
1371
- // deck.gl
1372
- const h3TilesetSource = async function h3TilesetSource(options) {
1373
- const {
1374
- tableName
1375
- } = options;
1376
- const urlParameters = {
1377
- name: tableName
1451
+ }
1452
+ // FilterTypes.CLOSED_OPEN
1453
+ function filterClosedOpen(filterValues, featureValue) {
1454
+ const checkRange = range => {
1455
+ const [lowerBound, upperBound] = range;
1456
+ return featureValue >= lowerBound && featureValue < upperBound;
1378
1457
  };
1379
- return baseSource('tileset', options, urlParameters);
1380
- };
1458
+ return makeIntervalComplete(filterValues).some(checkRange);
1459
+ }
1460
+ // FilterTypes.STRING_SEARCH
1461
+ function filterStringSearch(filterValues, featureValue, params = {}) {
1462
+ const normalizedFeatureValue = normalize(featureValue, params);
1463
+ const stringRegExp = params.useRegExp ? filterValues : filterValues.map(filterValue => {
1464
+ let stringRegExp = escapeRegExp(normalize(filterValue, params));
1465
+ if (params.mustStart) stringRegExp = `^${stringRegExp}`;
1466
+ if (params.mustEnd) stringRegExp = `${stringRegExp}$`;
1467
+ return stringRegExp;
1468
+ });
1469
+ const regex = new RegExp(stringRegExp.join('|'), params.caseSensitive ? 'g' : 'gi');
1470
+ return !!normalizedFeatureValue.match(regex);
1471
+ }
1472
+ // Aux
1473
+ const specialCharRegExp = /[.*+?^${}()|[\]\\]/g;
1474
+ const normalizeRegExp = /(?:[\^`\xA8\xAF\xB4\xB7\xB8\u02B0-\u034E\u0350-\u0357\u035D-\u0362\u0374\u0375\u037A\u0384\u0385\u0483-\u0487\u0559\u0591-\u05A1\u05A3-\u05BD\u05BF\u05C1\u05C2\u05C4\u064B-\u0652\u0657\u0658\u06DF\u06E0\u06E5\u06E6\u06EA-\u06EC\u0730-\u074A\u07A6-\u07B0\u07EB-\u07F5\u0818\u0819\u0898-\u089F\u08C9-\u08D2\u08E3-\u08FE\u093C\u094D\u0951-\u0954\u0971\u09BC\u09CD\u0A3C\u0A4D\u0ABC\u0ACD\u0AFD-\u0AFF\u0B3C\u0B4D\u0B55\u0BCD\u0C3C\u0C4D\u0CBC\u0CCD\u0D3B\u0D3C\u0D4D\u0DCA\u0E3A\u0E47-\u0E4C\u0E4E\u0EBA\u0EC8-\u0ECC\u0F18\u0F19\u0F35\u0F37\u0F39\u0F3E\u0F3F\u0F82-\u0F84\u0F86\u0F87\u0FC6\u1037\u1039\u103A\u1063\u1064\u1069-\u106D\u1087-\u108D\u108F\u109A\u109B\u135D-\u135F\u1714\u1715\u1734\u17C9-\u17D3\u17DD\u1939-\u193B\u1A60\u1A75-\u1A7C\u1A7F\u1AB0-\u1ABE\u1AC1-\u1ACB\u1B34\u1B44\u1B6B-\u1B73\u1BAA\u1BAB\u1BE6\u1BF2\u1BF3\u1C36\u1C37\u1C78-\u1C7D\u1CD0-\u1CE8\u1CED\u1CF4\u1CF7-\u1CF9\u1D2C-\u1D6A\u1DC4-\u1DCF\u1DF5-\u1DFF\u1FBD\u1FBF-\u1FC1\u1FCD-\u1FCF\u1FDD-\u1FDF\u1FED-\u1FEF\u1FFD\u1FFE\u2CEF-\u2CF1\u2E2F\u302A-\u302F\u3099-\u309C\u30FC\uA66F\uA67C\uA67D\uA67F\uA69C\uA69D\uA6F0\uA6F1\uA700-\uA721\uA788-\uA78A\uA7F8\uA7F9\uA806\uA82C\uA8C4\uA8E0-\uA8F1\uA92B-\uA92E\uA953\uA9B3\uA9C0\uA9E5\uAA7B-\uAA7D\uAABF-\uAAC2\uAAF6\uAB5B-\uAB5F\uAB69-\uAB6B\uABEC\uABED\uFB1E\uFE20-\uFE2F\uFF3E\uFF40\uFF70\uFF9E\uFF9F\uFFE3]|\uD800\uDEE0|\uD801[\uDF80-\uDF85\uDF87-\uDFB0\uDFB2-\uDFBA]|\uD802[\uDE38-\uDE3A\uDE3F\uDEE5\uDEE6]|\uD803[\uDD22-\uDD27\uDD4E\uDD69-\uDD6D\uDEFD-\uDEFF\uDF46-\uDF50\uDF82-\uDF85]|\uD804[\uDC46\uDC70\uDCB9\uDCBA\uDD33\uDD34\uDD73\uDDC0\uDDCA-\uDDCC\uDE35\uDE36\uDEE9\uDEEA\uDF3B\uDF3C\uDF4D\uDF66-\uDF6C\uDF70-\uDF74\uDFCE-\uDFD0\uDFD2\uDFD3\uDFE1\uDFE2]|\uD805[\uDC42\uDC46\uDCC2\uDCC3\uDDBF\uDDC0\uDE3F\uDEB6\uDEB7\uDF2B]|\uD806[\uDC39\uDC3A\uDD3D\uDD3E\uDD43\uDDE0\uDE34\uDE47\uDE99]|\uD807[\uDC3F\uDD42\uDD44\uDD45\uDD97\uDF41\uDF42\uDF5A]|\uD80D[\uDC47-\uDC55]|\uD818\uDD2F|\uD81A[\uDEF0-\uDEF4\uDF30-\uDF36]|\uD81B[\uDD6B\uDD6C\uDF8F-\uDF9F\uDFF0\uDFF1]|\uD82B[\uDFF0-\uDFF3\uDFF5-\uDFFB\uDFFD\uDFFE]|\uD833[\uDF00-\uDF2D\uDF30-\uDF46]|\uD834[\uDD67-\uDD69\uDD6D-\uDD72\uDD7B-\uDD82\uDD85-\uDD8B\uDDAA-\uDDAD]|\uD838[\uDC30-\uDC6D\uDD30-\uDD36\uDEAE\uDEEC-\uDEEF]|\uD839[\uDDEE\uDDEF]|\uD83A[\uDCD0-\uDCD6\uDD44-\uDD46\uDD48-\uDD4A])/g;
1475
+ function escapeRegExp(value) {
1476
+ return value.replace(specialCharRegExp, '\\$&');
1477
+ }
1478
+ function normalize(data, params) {
1479
+ let normalizedData = String(data);
1480
+ if (!params.keepSpecialCharacters) normalizedData = normalizedData.normalize('NFD').replace(normalizeRegExp, '');
1481
+ return normalizedData;
1482
+ }
1381
1483
 
1382
- // deck.gl
1383
- const rasterSource = async function rasterSource(options) {
1384
- const {
1385
- tableName,
1386
- filters
1387
- } = options;
1388
- const urlParameters = {
1389
- name: tableName
1484
+ const LOGICAL_OPERATOR_METHODS = {
1485
+ and: 'every',
1486
+ or: 'some'
1487
+ };
1488
+ function passesFilter(columns, filters, feature, filtersLogicalOperator) {
1489
+ const method = LOGICAL_OPERATOR_METHODS[filtersLogicalOperator];
1490
+ return columns[method](column => {
1491
+ const columnFilters = filters[column];
1492
+ const columnFilterTypes = Object.keys(columnFilters);
1493
+ if (!feature || feature[column] === null || feature[column] === undefined) {
1494
+ return false;
1495
+ }
1496
+ return columnFilterTypes.every(filter => {
1497
+ const filterFunction = filterFunctions[filter];
1498
+ if (!filterFunction) {
1499
+ throw new Error(`"${filter}" filter is not implemented.`);
1500
+ }
1501
+ return filterFunction(columnFilters[filter].values, feature[column], columnFilters[filter].params);
1502
+ });
1503
+ });
1504
+ }
1505
+ function buildFeatureFilter({
1506
+ filters = {},
1507
+ type = 'boolean',
1508
+ filtersLogicalOperator = 'and'
1509
+ }) {
1510
+ const columns = Object.keys(filters);
1511
+ if (!columns.length) {
1512
+ return () => type === 'number' ? 1 : true;
1513
+ }
1514
+ return feature => {
1515
+ const f = feature.properties || feature;
1516
+ const featurePassesFilter = passesFilter(columns, filters, f, filtersLogicalOperator);
1517
+ return type === 'number' ? Number(featurePassesFilter) : featurePassesFilter;
1390
1518
  };
1391
- if (filters) {
1392
- urlParameters.filters = filters;
1519
+ }
1520
+ // Apply certain filters to a collection of features
1521
+ function applyFilters(features, filters, filtersLogicalOperator) {
1522
+ return Object.keys(filters).length ? features.filter(buildFeatureFilter({
1523
+ filters,
1524
+ filtersLogicalOperator
1525
+ })) : features;
1526
+ }
1527
+ // Binary
1528
+ function buildBinaryFeatureFilter({
1529
+ filters = {}
1530
+ }) {
1531
+ const columns = Object.keys(filters);
1532
+ if (!columns.length) {
1533
+ return () => 1;
1393
1534
  }
1394
- return baseSource('raster', options, urlParameters);
1535
+ return (featureIdIdx, binaryData) => passesFilterUsingBinary(columns, filters, featureIdIdx, binaryData);
1536
+ }
1537
+ function getValueFromNumericProps(featureIdIdx, binaryData, {
1538
+ column
1539
+ }) {
1540
+ var _binaryData$numericPr;
1541
+ return (_binaryData$numericPr = binaryData.numericProps) == null || (_binaryData$numericPr = _binaryData$numericPr[column]) == null ? void 0 : _binaryData$numericPr.value[featureIdIdx];
1542
+ }
1543
+ function getValueFromProperties(featureIdIdx, binaryData, {
1544
+ column
1545
+ }) {
1546
+ var _binaryData$propertie;
1547
+ const propertyIdx = binaryData.featureIds.value[featureIdIdx];
1548
+ return (_binaryData$propertie = binaryData.properties[propertyIdx]) == null ? void 0 : _binaryData$propertie[column];
1549
+ }
1550
+ const GET_VALUE_BY_BINARY_PROP = {
1551
+ properties: getValueFromProperties,
1552
+ numericProps: getValueFromNumericProps
1395
1553
  };
1396
-
1397
- const quadbinQuerySource = async function quadbinQuerySource(options) {
1554
+ function getBinaryPropertyByFilterValues(filterValues) {
1555
+ return typeof filterValues.flat()[0] === 'string' ? 'properties' : 'numericProps';
1556
+ }
1557
+ function getFeatureValue(featureIdIdx, binaryData, filter) {
1398
1558
  const {
1399
- aggregationExp,
1400
- aggregationResLevel = DEFAULT_AGGREGATION_RES_LEVEL_QUADBIN,
1401
- sqlQuery,
1402
- spatialDataColumn = 'quadbin',
1403
- queryParameters,
1404
- filters
1405
- } = options;
1406
- const urlParameters = {
1407
- aggregationExp,
1408
- q: sqlQuery,
1409
- spatialDataColumn,
1410
- spatialDataType: 'quadbin'
1411
- };
1412
- if (aggregationResLevel) {
1413
- urlParameters.aggregationResLevel = String(aggregationResLevel);
1559
+ column,
1560
+ values
1561
+ } = filter;
1562
+ const binaryProp = getBinaryPropertyByFilterValues(values);
1563
+ const getFeatureValueFn = GET_VALUE_BY_BINARY_PROP[binaryProp];
1564
+ return getFeatureValueFn(featureIdIdx, binaryData, {
1565
+ column
1566
+ });
1567
+ }
1568
+ function passesFilterUsingBinary(columns, filters, featureIdIdx, binaryData) {
1569
+ return columns.every(column => {
1570
+ const columnFilters = filters[column];
1571
+ return Object.entries(columnFilters).every(([type, {
1572
+ values
1573
+ }]) => {
1574
+ const filterFn = filterFunctions[type];
1575
+ if (!filterFn) {
1576
+ throw new Error(`"${type}" filter is not implemented.`);
1577
+ }
1578
+ if (!values) return 0;
1579
+ const featureValue = getFeatureValue(featureIdIdx, binaryData, {
1580
+ type: type,
1581
+ column,
1582
+ values
1583
+ });
1584
+ if (featureValue === undefined || featureValue === null) return 0;
1585
+ return filterFn(values, featureValue);
1586
+ });
1587
+ });
1588
+ }
1589
+
1590
+ function geojsonFeatures({
1591
+ geojson,
1592
+ spatialFilter,
1593
+ uniqueIdProperty
1594
+ }) {
1595
+ let uniqueIdx = 0;
1596
+ const map = new Map();
1597
+ if (!spatialFilter) {
1598
+ return [];
1414
1599
  }
1415
- if (queryParameters) {
1416
- urlParameters.queryParameters = queryParameters;
1600
+ for (const feature of geojson.features) {
1601
+ const uniqueId = uniqueIdProperty ? feature.properties[uniqueIdProperty] : ++uniqueIdx;
1602
+ if (!map.has(uniqueId) && intersects(spatialFilter, feature)) {
1603
+ map.set(uniqueId, feature.properties);
1604
+ }
1417
1605
  }
1418
- if (filters) {
1419
- urlParameters.filters = filters;
1606
+ return Array.from(map.values());
1607
+ }
1608
+
1609
+ // math.gl
1610
+ // SPDX-License-Identifier: MIT
1611
+ // Copyright (c) vis.gl contributors
1612
+ const DEFAULT_CONFIG = {
1613
+ EPSILON: 1e-12,
1614
+ debug: false,
1615
+ precision: 4,
1616
+ printTypes: false,
1617
+ printDegrees: false,
1618
+ printRowMajor: true,
1619
+ _cartographicRadians: false
1620
+ };
1621
+ // Configuration is truly global as of v3.6 to ensure single config even if multiple copies of math.gl
1622
+ // Multiple copies of config can be quite tricky to debug...
1623
+ globalThis.mathgl = globalThis.mathgl || {
1624
+ config: {
1625
+ ...DEFAULT_CONFIG
1420
1626
  }
1421
- return baseSource('query', options, urlParameters).then(result => _extends({}, result, {
1422
- widgetSource: new WidgetQuerySource(options)
1423
- }));
1424
1627
  };
1425
-
1426
- const quadbinTableSource = async function quadbinTableSource(options) {
1427
- const {
1428
- aggregationExp,
1429
- aggregationResLevel = DEFAULT_AGGREGATION_RES_LEVEL_QUADBIN,
1430
- spatialDataColumn = 'quadbin',
1431
- tableName,
1432
- filters
1433
- } = options;
1434
- const urlParameters = {
1435
- aggregationExp,
1436
- name: tableName,
1437
- spatialDataColumn,
1438
- spatialDataType: 'quadbin'
1439
- };
1440
- if (aggregationResLevel) {
1441
- urlParameters.aggregationResLevel = String(aggregationResLevel);
1628
+ /**
1629
+ * Check if value is an "array"
1630
+ * Returns `true` if value is either an array or a typed array
1631
+ * Note: returns `false` for `ArrayBuffer` and `DataView` instances
1632
+ * @note isTypedArray and isNumericArray are often more useful in TypeScript
1633
+ */
1634
+ function isArray(value) {
1635
+ return Array.isArray(value) || ArrayBuffer.isView(value) && !(value instanceof DataView);
1636
+ }
1637
+ function lerp(a, b, t) {
1638
+ if (isArray(a)) {
1639
+ return a.map((ai, i) => lerp(ai, b[i], t));
1442
1640
  }
1443
- if (filters) {
1444
- urlParameters.filters = filters;
1641
+ return t * b + (1 - t) * a;
1642
+ }
1643
+
1644
+ // Replacement for the external assert method to reduce bundle size
1645
+ // Note: We don't use the second "message" argument in calling code,
1646
+ // so no need to support it here
1647
+ function assert(condition, message) {
1648
+ if (!condition) {
1649
+ throw new Error(message || '@math.gl/web-mercator: assertion failed.');
1445
1650
  }
1446
- return baseSource('table', options, urlParameters).then(result => _extends({}, result, {
1447
- widgetSource: new WidgetTableSource(options)
1448
- }));
1449
- };
1651
+ }
1450
1652
 
1451
- // deck.gl
1452
- const quadbinTilesetSource = async function quadbinTilesetSource(options) {
1453
- const {
1454
- tableName
1455
- } = options;
1456
- const urlParameters = {
1457
- name: tableName
1458
- };
1459
- return baseSource('tileset', options, urlParameters);
1460
- };
1653
+ // TODO - THE UTILITIES IN THIS FILE SHOULD BE IMPORTED FROM WEB-MERCATOR-VIEWPORT MODULE
1654
+ // CONSTANTS
1655
+ const PI = Math.PI;
1656
+ const PI_4 = PI / 4;
1657
+ const DEGREES_TO_RADIANS = PI / 180;
1658
+ const RADIANS_TO_DEGREES = 180 / PI;
1659
+ const TILE_SIZE = 512;
1660
+ /**
1661
+ * Project [lng,lat] on sphere onto [x,y] on 512*512 Mercator Zoom 0 tile.
1662
+ * Performs the nonlinear part of the web mercator projection.
1663
+ * Remaining projection is done with 4x4 matrices which also handles
1664
+ * perspective.
1665
+ *
1666
+ * @param lngLat - [lng, lat] coordinates
1667
+ * Specifies a point on the sphere to project onto the map.
1668
+ * @return [x,y] coordinates.
1669
+ */
1670
+ function lngLatToWorld(lngLat) {
1671
+ const [lng, lat] = lngLat;
1672
+ assert(Number.isFinite(lng));
1673
+ assert(Number.isFinite(lat) && lat >= -90 && lat <= 90, 'invalid latitude');
1674
+ const lambda2 = lng * DEGREES_TO_RADIANS;
1675
+ const phi2 = lat * DEGREES_TO_RADIANS;
1676
+ const x = TILE_SIZE * (lambda2 + PI) / (2 * PI);
1677
+ const y = TILE_SIZE * (PI + Math.log(Math.tan(PI_4 + phi2 * 0.5))) / (2 * PI);
1678
+ return [x, y];
1679
+ }
1680
+ /**
1681
+ * Unproject world point [x,y] on map onto {lat, lon} on sphere
1682
+ *
1683
+ * @param xy - array with [x,y] members
1684
+ * representing point on projected map plane
1685
+ * @return - array with [x,y] of point on sphere.
1686
+ * Has toArray method if you need a GeoJSON Array.
1687
+ * Per cartographic tradition, lat and lon are specified as degrees.
1688
+ */
1689
+ function worldToLngLat(xy) {
1690
+ const [x, y] = xy;
1691
+ const lambda2 = x / TILE_SIZE * (2 * PI) - PI;
1692
+ const phi2 = 2 * (Math.atan(Math.exp(y / TILE_SIZE * (2 * PI) - PI)) - PI_4);
1693
+ return [lambda2 * RADIANS_TO_DEGREES, phi2 * RADIANS_TO_DEGREES];
1694
+ }
1461
1695
 
1462
- const vectorQuerySource = async function vectorQuerySource(options) {
1463
- const {
1464
- columns,
1465
- filters,
1466
- spatialDataColumn = 'geom',
1696
+ const TRANSFORM_FN$1 = {
1697
+ Point: transformPoint$1,
1698
+ MultiPoint: transformMultiPoint$1,
1699
+ LineString: transformLineString$1,
1700
+ MultiLineString: transformMultiLineString$1,
1701
+ Polygon: transformPolygon$1,
1702
+ MultiPolygon: transformMultiPolygon$1
1703
+ };
1704
+ /**
1705
+ * Transform WGS84 coordinates to tile coords.
1706
+ * It's the inverse of deck.gl coordinate-transform (https://github.com/visgl/deck.gl/blob/master/modules/geo-layers/src/mvt-layer/coordinate-transform.js)
1707
+ *
1708
+ * @param geometry - any valid geojson geometry
1709
+ * @param bbox - geojson bbox
1710
+ */
1711
+ function transformToTileCoords(geometry, bbox) {
1712
+ const [west, south, east, north] = bbox;
1713
+ const nw = projectFlat([west, north]);
1714
+ const se = projectFlat([east, south]);
1715
+ const projectedBbox = [nw, se];
1716
+ if (geometry.type === 'GeometryCollection') {
1717
+ throw new Error('Unsupported geometry type GeometryCollection');
1718
+ }
1719
+ const transformFn = TRANSFORM_FN$1[geometry.type];
1720
+ const coordinates = transformFn(geometry.coordinates, projectedBbox);
1721
+ return _extends({}, geometry, {
1722
+ coordinates
1723
+ });
1724
+ }
1725
+ function transformPoint$1([pointX, pointY], [nw, se]) {
1726
+ const x = inverseLerp(nw[0], se[0], pointX);
1727
+ const y = inverseLerp(nw[1], se[1], pointY);
1728
+ return [x, y];
1729
+ }
1730
+ function getPoints$1(geometry, bbox) {
1731
+ return geometry.map(g => transformPoint$1(projectFlat(g), bbox));
1732
+ }
1733
+ function transformMultiPoint$1(multiPoint, bbox) {
1734
+ return getPoints$1(multiPoint, bbox);
1735
+ }
1736
+ function transformLineString$1(line, bbox) {
1737
+ return getPoints$1(line, bbox);
1738
+ }
1739
+ function transformMultiLineString$1(multiLineString, bbox) {
1740
+ return multiLineString.map(lineString => transformLineString$1(lineString, bbox));
1741
+ }
1742
+ function transformPolygon$1(polygon, bbox) {
1743
+ return polygon.map(polygonRing => getPoints$1(polygonRing, bbox));
1744
+ }
1745
+ function transformMultiPolygon$1(multiPolygon, bbox) {
1746
+ return multiPolygon.map(polygon => transformPolygon$1(polygon, bbox));
1747
+ }
1748
+ function projectFlat(xyz) {
1749
+ return lngLatToWorld(xyz);
1750
+ }
1751
+ function inverseLerp(a, b, x) {
1752
+ return (x - a) / (b - a);
1753
+ }
1754
+
1755
+ const TRANSFORM_FN = {
1756
+ Point: transformPoint,
1757
+ MultiPoint: transformMultiPoint,
1758
+ LineString: transformLineString,
1759
+ MultiLineString: transformMultiLineString,
1760
+ Polygon: transformPolygon,
1761
+ MultiPolygon: transformMultiPolygon
1762
+ };
1763
+ /**
1764
+ * Transform tile coords to WGS84 coordinates.
1765
+ *
1766
+ * @param geometry - any valid geojson geometry
1767
+ * @param bbox - geojson bbox
1768
+ */
1769
+ function transformTileCoordsToWGS84(geometry, bbox) {
1770
+ const [west, south, east, north] = bbox;
1771
+ const nw = lngLatToWorld([west, north]);
1772
+ const se = lngLatToWorld([east, south]);
1773
+ const projectedBbox = [nw, se];
1774
+ if (geometry.type === 'GeometryCollection') {
1775
+ throw new Error('Unsupported geometry type GeometryCollection');
1776
+ }
1777
+ const transformFn = TRANSFORM_FN[geometry.type];
1778
+ const coordinates = transformFn(geometry.coordinates, projectedBbox);
1779
+ return _extends({}, geometry, {
1780
+ coordinates
1781
+ });
1782
+ }
1783
+ function transformPoint([pointX, pointY], [nw, se]) {
1784
+ const x = lerp(nw[0], se[0], pointX);
1785
+ const y = lerp(nw[1], se[1], pointY);
1786
+ return worldToLngLat([x, y]);
1787
+ }
1788
+ function getPoints(geometry, bbox) {
1789
+ return geometry.map(g => transformPoint(g, bbox));
1790
+ }
1791
+ function transformMultiPoint(multiPoint, bbox) {
1792
+ return getPoints(multiPoint, bbox);
1793
+ }
1794
+ function transformLineString(line, bbox) {
1795
+ return getPoints(line, bbox);
1796
+ }
1797
+ function transformMultiLineString(multiLineString, bbox) {
1798
+ return multiLineString.map(lineString => transformLineString(lineString, bbox));
1799
+ }
1800
+ function transformPolygon(polygon, bbox) {
1801
+ return polygon.map(polygonRing => getPoints(polygonRing, bbox));
1802
+ }
1803
+ function transformMultiPolygon(multiPolygon, bbox) {
1804
+ return multiPolygon.map(polygon => transformPolygon(polygon, bbox));
1805
+ }
1806
+
1807
+ const FEATURE_GEOM_PROPERTY = '__geomValue';
1808
+ function tileFeaturesGeometries({
1809
+ tiles,
1810
+ tileFormat,
1811
+ spatialFilter,
1812
+ uniqueIdProperty,
1813
+ options
1814
+ }) {
1815
+ const map = new Map();
1816
+ for (const tile of tiles) {
1817
+ // Discard if it's not a visible tile (only check false value, not undefined)
1818
+ // or tile has not data
1819
+ if (tile.isVisible === false || !tile.data) {
1820
+ continue;
1821
+ }
1822
+ const bbox = [tile.bbox.west, tile.bbox.south, tile.bbox.east, tile.bbox.north];
1823
+ const bboxToGeom = bboxPolygon(bbox);
1824
+ const tileIsFullyVisible = booleanWithin(bboxToGeom, spatialFilter);
1825
+ // Clip the geometry to intersect with the tile
1826
+ const spatialFilterFeature = {
1827
+ type: 'Feature',
1828
+ geometry: spatialFilter,
1829
+ properties: {}
1830
+ };
1831
+ const clippedGeometryToIntersect = intersect(featureCollection([bboxToGeom, spatialFilterFeature]));
1832
+ if (!clippedGeometryToIntersect) {
1833
+ continue;
1834
+ }
1835
+ // We assume that MVT tileFormat uses local coordinates so we transform the geometry to intersect to tile coordinates [0..1],
1836
+ // while in the case of 'geojson' or binary, the geometries are already in WGS84
1837
+ const transformedGeometryToIntersect = tileFormat === TileFormat.MVT ? transformToTileCoords(clippedGeometryToIntersect.geometry, bbox) : clippedGeometryToIntersect.geometry;
1838
+ createIndicesForPoints(tile.data.points);
1839
+ calculateFeatures({
1840
+ map,
1841
+ tileIsFullyVisible,
1842
+ geometryIntersection: transformedGeometryToIntersect,
1843
+ data: tile.data.points,
1844
+ type: 'Point',
1845
+ bbox,
1846
+ tileFormat,
1847
+ uniqueIdProperty,
1848
+ options
1849
+ });
1850
+ calculateFeatures({
1851
+ map,
1852
+ tileIsFullyVisible,
1853
+ geometryIntersection: transformedGeometryToIntersect,
1854
+ data: tile.data.lines,
1855
+ type: 'LineString',
1856
+ bbox,
1857
+ tileFormat,
1858
+ uniqueIdProperty,
1859
+ options
1860
+ });
1861
+ calculateFeatures({
1862
+ map,
1863
+ tileIsFullyVisible,
1864
+ geometryIntersection: transformedGeometryToIntersect,
1865
+ data: tile.data.polygons,
1866
+ type: 'Polygon',
1867
+ bbox,
1868
+ tileFormat,
1869
+ uniqueIdProperty,
1870
+ options
1871
+ });
1872
+ }
1873
+ return Array.from(map.values());
1874
+ }
1875
+ function processTileFeatureProperties({
1876
+ map,
1877
+ data,
1878
+ startIndex,
1879
+ endIndex,
1880
+ type,
1881
+ bbox,
1882
+ tileFormat,
1883
+ uniqueIdProperty,
1884
+ storeGeometry,
1885
+ geometryIntersection
1886
+ }) {
1887
+ const tileProps = getPropertiesFromTile(data, startIndex);
1888
+ const uniquePropertyValue = getUniquePropertyValue(tileProps, uniqueIdProperty, map);
1889
+ if (!uniquePropertyValue || map.has(uniquePropertyValue)) {
1890
+ return;
1891
+ }
1892
+ let geometry = null;
1893
+ // Only calculate geometry if necessary
1894
+ if (storeGeometry || geometryIntersection) {
1895
+ const {
1896
+ positions
1897
+ } = data;
1898
+ const ringCoordinates = getRingCoordinatesFor(startIndex, endIndex, positions);
1899
+ geometry = getFeatureByType(ringCoordinates, type);
1900
+ }
1901
+ // If intersection is required, check before proceeding
1902
+ if (geometry && geometryIntersection && !intersects(geometry, geometryIntersection)) {
1903
+ return;
1904
+ }
1905
+ const properties = parseProperties(tileProps);
1906
+ // Only save geometry if necessary
1907
+ if (storeGeometry && geometry) {
1908
+ properties[FEATURE_GEOM_PROPERTY] = tileFormat === TileFormat.MVT ? transformTileCoordsToWGS84(geometry, bbox) : geometry;
1909
+ }
1910
+ map.set(uniquePropertyValue, properties);
1911
+ }
1912
+ function addIntersectedFeaturesInTile({
1913
+ map,
1914
+ data,
1915
+ geometryIntersection,
1916
+ type,
1917
+ bbox,
1918
+ tileFormat,
1919
+ uniqueIdProperty,
1920
+ options
1921
+ }) {
1922
+ const indices = getIndices(data);
1923
+ const storeGeometry = (options == null ? void 0 : options.storeGeometry) || false;
1924
+ for (let i = 0; i < indices.length - 1; i++) {
1925
+ const startIndex = indices[i];
1926
+ const endIndex = indices[i + 1];
1927
+ processTileFeatureProperties({
1928
+ map,
1929
+ data,
1930
+ startIndex,
1931
+ endIndex,
1932
+ type,
1933
+ bbox,
1934
+ tileFormat,
1935
+ uniqueIdProperty,
1936
+ storeGeometry,
1937
+ geometryIntersection
1938
+ });
1939
+ }
1940
+ }
1941
+ function getIndices(data) {
1942
+ let indices;
1943
+ switch (data.type) {
1944
+ case 'Point':
1945
+ // @ts-expect-error Missing or changed types?
1946
+ indices = data.pointIndices;
1947
+ break;
1948
+ case 'LineString':
1949
+ indices = data.pathIndices;
1950
+ break;
1951
+ case 'Polygon':
1952
+ indices = data.primitivePolygonIndices;
1953
+ break;
1954
+ default:
1955
+ throw new Error(`Unexpected type, "${data.type}"`);
1956
+ }
1957
+ return indices.value;
1958
+ }
1959
+ function getFeatureId(data, startIndex) {
1960
+ return data.featureIds.value[startIndex];
1961
+ }
1962
+ function getPropertiesFromTile(data, startIndex) {
1963
+ var _fields$featureId;
1964
+ const featureId = getFeatureId(data, startIndex);
1965
+ const {
1966
+ properties,
1967
+ numericProps,
1968
+ fields
1969
+ } = data;
1970
+ const result = {
1971
+ uniqueId: fields == null || (_fields$featureId = fields[featureId]) == null ? void 0 : _fields$featureId.id,
1972
+ properties: properties[featureId],
1973
+ numericProps: {}
1974
+ };
1975
+ for (const key in numericProps) {
1976
+ result.numericProps[key] = numericProps[key].value[startIndex];
1977
+ }
1978
+ return result;
1979
+ }
1980
+ function parseProperties(tileProps) {
1981
+ const {
1982
+ properties,
1983
+ numericProps
1984
+ } = tileProps;
1985
+ return Object.assign({}, properties, numericProps);
1986
+ }
1987
+ function getUniquePropertyValue(tileProps, uniqueIdProperty, map) {
1988
+ if (uniqueIdProperty) {
1989
+ return getValueFromTileProps(tileProps, uniqueIdProperty);
1990
+ }
1991
+ if (tileProps.uniqueId) {
1992
+ return tileProps.uniqueId;
1993
+ }
1994
+ const artificialId = map.size + 1; // a counter, assumed as a valid new id
1995
+ return getValueFromTileProps(tileProps, 'cartodb_id') || getValueFromTileProps(tileProps, 'geoid') || artificialId;
1996
+ }
1997
+ function getValueFromTileProps(tileProps, propertyName) {
1998
+ const {
1999
+ properties,
2000
+ numericProps
2001
+ } = tileProps;
2002
+ return numericProps[propertyName] || properties[propertyName];
2003
+ }
2004
+ function getFeatureByType(coordinates, type) {
2005
+ switch (type) {
2006
+ case 'Polygon':
2007
+ return {
2008
+ type: 'Polygon',
2009
+ coordinates: [coordinates]
2010
+ };
2011
+ case 'LineString':
2012
+ return {
2013
+ type: 'LineString',
2014
+ coordinates
2015
+ };
2016
+ case 'Point':
2017
+ return {
2018
+ type: 'Point',
2019
+ coordinates: coordinates[0]
2020
+ };
2021
+ default:
2022
+ throw new Error('Invalid geometry type');
2023
+ }
2024
+ }
2025
+ function getRingCoordinatesFor(startIndex, endIndex, positions) {
2026
+ const ringCoordinates = [];
2027
+ for (let j = startIndex; j < endIndex; j++) {
2028
+ ringCoordinates.push(Array.from(positions.value.subarray(j * positions.size, (j + 1) * positions.size)));
2029
+ }
2030
+ return ringCoordinates;
2031
+ }
2032
+ function calculateFeatures({
2033
+ map,
2034
+ tileIsFullyVisible,
2035
+ geometryIntersection,
2036
+ data,
2037
+ type,
2038
+ bbox,
2039
+ tileFormat,
2040
+ uniqueIdProperty,
2041
+ options
2042
+ }) {
2043
+ if (!(data != null && data.properties.length)) {
2044
+ return;
2045
+ }
2046
+ if (tileIsFullyVisible) {
2047
+ addAllFeaturesInTile({
2048
+ map,
2049
+ data,
2050
+ type,
2051
+ bbox,
2052
+ tileFormat,
2053
+ uniqueIdProperty,
2054
+ options
2055
+ });
2056
+ } else {
2057
+ addIntersectedFeaturesInTile({
2058
+ map,
2059
+ data,
2060
+ geometryIntersection,
2061
+ type,
2062
+ bbox,
2063
+ tileFormat,
2064
+ uniqueIdProperty,
2065
+ options
2066
+ });
2067
+ }
2068
+ }
2069
+ function addAllFeaturesInTile({
2070
+ map,
2071
+ data,
2072
+ type,
2073
+ bbox,
2074
+ tileFormat,
2075
+ uniqueIdProperty,
2076
+ options
2077
+ }) {
2078
+ const indices = getIndices(data);
2079
+ const storeGeometry = (options == null ? void 0 : options.storeGeometry) || false;
2080
+ for (let i = 0; i < indices.length - 1; i++) {
2081
+ const startIndex = indices[i];
2082
+ const endIndex = indices[i + 1];
2083
+ processTileFeatureProperties({
2084
+ map,
2085
+ data,
2086
+ startIndex,
2087
+ endIndex,
2088
+ type,
2089
+ bbox,
2090
+ tileFormat,
2091
+ uniqueIdProperty,
2092
+ storeGeometry
2093
+ });
2094
+ }
2095
+ }
2096
+ function createIndicesForPoints(data) {
2097
+ const featureIds = data.featureIds.value;
2098
+ const lastFeatureId = featureIds[featureIds.length - 1];
2099
+ const PointIndicesArray = featureIds.constructor;
2100
+ const pointIndices = {
2101
+ value: new PointIndicesArray(featureIds.length + 1),
2102
+ size: 1
2103
+ };
2104
+ pointIndices.value.set(featureIds);
2105
+ pointIndices.value.set([lastFeatureId + 1], featureIds.length);
2106
+ // @ts-expect-error Missing or changed types?
2107
+ data.pointIndices = pointIndices;
2108
+ }
2109
+
2110
+ // a tile is an array [x,y,z]
2111
+ var d2r = Math.PI / 180,
2112
+ r2d = 180 / Math.PI;
2113
+ function tileToBBOX(tile) {
2114
+ var e = tile2lon(tile[0] + 1, tile[2]);
2115
+ var w = tile2lon(tile[0], tile[2]);
2116
+ var s = tile2lat(tile[1] + 1, tile[2]);
2117
+ var n = tile2lat(tile[1], tile[2]);
2118
+ return [w, s, e, n];
2119
+ }
2120
+ function tileToGeoJSON(tile) {
2121
+ var bbox = tileToBBOX(tile);
2122
+ var poly = {
2123
+ type: 'Polygon',
2124
+ coordinates: [[[bbox[0], bbox[1]], [bbox[0], bbox[3]], [bbox[2], bbox[3]], [bbox[2], bbox[1]], [bbox[0], bbox[1]]]]
2125
+ };
2126
+ return poly;
2127
+ }
2128
+ function tile2lon(x, z) {
2129
+ return x / Math.pow(2, z) * 360 - 180;
2130
+ }
2131
+ function tile2lat(y, z) {
2132
+ var n = Math.PI - 2 * Math.PI * y / Math.pow(2, z);
2133
+ return r2d * Math.atan(0.5 * (Math.exp(n) - Math.exp(-n)));
2134
+ }
2135
+ function pointToTile(lon, lat, z) {
2136
+ var tile = pointToTileFraction(lon, lat, z);
2137
+ tile[0] = Math.floor(tile[0]);
2138
+ tile[1] = Math.floor(tile[1]);
2139
+ return tile;
2140
+ }
2141
+ function getChildren(tile) {
2142
+ return [[tile[0] * 2, tile[1] * 2, tile[2] + 1], [tile[0] * 2 + 1, tile[1] * 2, tile[2] + 1], [tile[0] * 2 + 1, tile[1] * 2 + 1, tile[2] + 1], [tile[0] * 2, tile[1] * 2 + 1, tile[2] + 1]];
2143
+ }
2144
+ function getParent(tile) {
2145
+ // top left
2146
+ if (tile[0] % 2 === 0 && tile[1] % 2 === 0) {
2147
+ return [tile[0] / 2, tile[1] / 2, tile[2] - 1];
2148
+ }
2149
+ // bottom left
2150
+ else if (tile[0] % 2 === 0 && !tile[1] % 2 === 0) {
2151
+ return [tile[0] / 2, (tile[1] - 1) / 2, tile[2] - 1];
2152
+ }
2153
+ // top right
2154
+ else if (!tile[0] % 2 === 0 && tile[1] % 2 === 0) {
2155
+ return [(tile[0] - 1) / 2, tile[1] / 2, tile[2] - 1];
2156
+ }
2157
+ // bottom right
2158
+ else {
2159
+ return [(tile[0] - 1) / 2, (tile[1] - 1) / 2, tile[2] - 1];
2160
+ }
2161
+ }
2162
+ function getSiblings(tile) {
2163
+ return getChildren(getParent(tile));
2164
+ }
2165
+ function hasSiblings(tile, tiles) {
2166
+ var siblings = getSiblings(tile);
2167
+ for (var i = 0; i < siblings.length; i++) {
2168
+ if (!hasTile(tiles, siblings[i])) return false;
2169
+ }
2170
+ return true;
2171
+ }
2172
+ function hasTile(tiles, tile) {
2173
+ for (var i = 0; i < tiles.length; i++) {
2174
+ if (tilesEqual(tiles[i], tile)) return true;
2175
+ }
2176
+ return false;
2177
+ }
2178
+ function tilesEqual(tile1, tile2) {
2179
+ return tile1[0] === tile2[0] && tile1[1] === tile2[1] && tile1[2] === tile2[2];
2180
+ }
2181
+ function tileToQuadkey(tile) {
2182
+ var index = '';
2183
+ for (var z = tile[2]; z > 0; z--) {
2184
+ var b = 0;
2185
+ var mask = 1 << z - 1;
2186
+ if ((tile[0] & mask) !== 0) b++;
2187
+ if ((tile[1] & mask) !== 0) b += 2;
2188
+ index += b.toString();
2189
+ }
2190
+ return index;
2191
+ }
2192
+ function quadkeyToTile(quadkey) {
2193
+ var x = 0;
2194
+ var y = 0;
2195
+ var z = quadkey.length;
2196
+ for (var i = z; i > 0; i--) {
2197
+ var mask = 1 << i - 1;
2198
+ switch (quadkey[z - i]) {
2199
+ case '0':
2200
+ break;
2201
+ case '1':
2202
+ x |= mask;
2203
+ break;
2204
+ case '2':
2205
+ y |= mask;
2206
+ break;
2207
+ case '3':
2208
+ x |= mask;
2209
+ y |= mask;
2210
+ break;
2211
+ }
2212
+ }
2213
+ return [x, y, z];
2214
+ }
2215
+ function bboxToTile(bboxCoords) {
2216
+ var min = pointToTile(bboxCoords[0], bboxCoords[1], 32);
2217
+ var max = pointToTile(bboxCoords[2], bboxCoords[3], 32);
2218
+ var bbox = [min[0], min[1], max[0], max[1]];
2219
+ var z = getBboxZoom(bbox);
2220
+ if (z === 0) return [0, 0, 0];
2221
+ var x = bbox[0] >>> 32 - z;
2222
+ var y = bbox[1] >>> 32 - z;
2223
+ return [x, y, z];
2224
+ }
2225
+ function getBboxZoom(bbox) {
2226
+ var MAX_ZOOM = 28;
2227
+ for (var z = 0; z < MAX_ZOOM; z++) {
2228
+ var mask = 1 << 32 - (z + 1);
2229
+ if ((bbox[0] & mask) != (bbox[2] & mask) || (bbox[1] & mask) != (bbox[3] & mask)) {
2230
+ return z;
2231
+ }
2232
+ }
2233
+ return MAX_ZOOM;
2234
+ }
2235
+ function pointToTileFraction(lon, lat, z) {
2236
+ var sin = Math.sin(lat * d2r),
2237
+ z2 = Math.pow(2, z),
2238
+ x = z2 * (lon / 360 + 0.5),
2239
+ y = z2 * (0.5 - 0.25 * Math.log((1 + sin) / (1 - sin)) / Math.PI);
2240
+ return [x, y, z];
2241
+ }
2242
+ var tilebelt = {
2243
+ tileToGeoJSON: tileToGeoJSON,
2244
+ tileToBBOX: tileToBBOX,
2245
+ getChildren: getChildren,
2246
+ getParent: getParent,
2247
+ getSiblings: getSiblings,
2248
+ hasTile: hasTile,
2249
+ hasSiblings: hasSiblings,
2250
+ tilesEqual: tilesEqual,
2251
+ tileToQuadkey: tileToQuadkey,
2252
+ quadkeyToTile: quadkeyToTile,
2253
+ pointToTile: pointToTile,
2254
+ bboxToTile: bboxToTile,
2255
+ pointToTileFraction: pointToTileFraction
2256
+ };
2257
+
2258
+ /**
2259
+ * Given a geometry, create cells and return them in their raw form,
2260
+ * as an array of cell identifiers.
2261
+ *
2262
+ * @alias tiles
2263
+ * @param {Object} geom GeoJSON geometry
2264
+ * @param {Object} limits an object with min_zoom and max_zoom properties
2265
+ * specifying the minimum and maximum level to be tiled.
2266
+ * @returns {Array<Array<number>>} An array of tiles given as [x, y, z] arrays
2267
+ */
2268
+ var tiles = getTiles;
2269
+ function getTiles(geom, limits) {
2270
+ var i,
2271
+ tile,
2272
+ coords = geom.coordinates,
2273
+ maxZoom = limits.max_zoom,
2274
+ tileHash = {},
2275
+ tiles = [];
2276
+ if (geom.type === 'Point') {
2277
+ return [tilebelt.pointToTile(coords[0], coords[1], maxZoom)];
2278
+ } else if (geom.type === 'MultiPoint') {
2279
+ for (i = 0; i < coords.length; i++) {
2280
+ tile = tilebelt.pointToTile(coords[i][0], coords[i][1], maxZoom);
2281
+ tileHash[toID(tile[0], tile[1], tile[2])] = true;
2282
+ }
2283
+ } else if (geom.type === 'LineString') {
2284
+ lineCover(tileHash, coords, maxZoom);
2285
+ } else if (geom.type === 'MultiLineString') {
2286
+ for (i = 0; i < coords.length; i++) {
2287
+ lineCover(tileHash, coords[i], maxZoom);
2288
+ }
2289
+ } else if (geom.type === 'Polygon') {
2290
+ polygonCover(tileHash, tiles, coords, maxZoom);
2291
+ } else if (geom.type === 'MultiPolygon') {
2292
+ for (i = 0; i < coords.length; i++) {
2293
+ polygonCover(tileHash, tiles, coords[i], maxZoom);
2294
+ }
2295
+ } else {
2296
+ throw new Error('Geometry type not implemented');
2297
+ }
2298
+ if (limits.min_zoom !== maxZoom) {
2299
+ // sync tile hash and tile array so that both contain the same tiles
2300
+ var len = tiles.length;
2301
+ appendHashTiles(tileHash, tiles);
2302
+ for (i = 0; i < len; i++) {
2303
+ var t = tiles[i];
2304
+ tileHash[toID(t[0], t[1], t[2])] = true;
2305
+ }
2306
+ return mergeTiles(tileHash, tiles, limits);
2307
+ }
2308
+ appendHashTiles(tileHash, tiles);
2309
+ return tiles;
2310
+ }
2311
+ function mergeTiles(tileHash, tiles, limits) {
2312
+ var mergedTiles = [];
2313
+ for (var z = limits.max_zoom; z > limits.min_zoom; z--) {
2314
+ var parentTileHash = {};
2315
+ var parentTiles = [];
2316
+ for (var i = 0; i < tiles.length; i++) {
2317
+ var t = tiles[i];
2318
+ if (t[0] % 2 === 0 && t[1] % 2 === 0) {
2319
+ var id2 = toID(t[0] + 1, t[1], z),
2320
+ id3 = toID(t[0], t[1] + 1, z),
2321
+ id4 = toID(t[0] + 1, t[1] + 1, z);
2322
+ if (tileHash[id2] && tileHash[id3] && tileHash[id4]) {
2323
+ tileHash[toID(t[0], t[1], t[2])] = false;
2324
+ tileHash[id2] = false;
2325
+ tileHash[id3] = false;
2326
+ tileHash[id4] = false;
2327
+ var parentTile = [t[0] / 2, t[1] / 2, z - 1];
2328
+ if (z - 1 === limits.min_zoom) mergedTiles.push(parentTile);else {
2329
+ parentTileHash[toID(t[0] / 2, t[1] / 2, z - 1)] = true;
2330
+ parentTiles.push(parentTile);
2331
+ }
2332
+ }
2333
+ }
2334
+ }
2335
+ for (i = 0; i < tiles.length; i++) {
2336
+ t = tiles[i];
2337
+ if (tileHash[toID(t[0], t[1], t[2])]) mergedTiles.push(t);
2338
+ }
2339
+ tileHash = parentTileHash;
2340
+ tiles = parentTiles;
2341
+ }
2342
+ return mergedTiles;
2343
+ }
2344
+ function polygonCover(tileHash, tileArray, geom, zoom) {
2345
+ var intersections = [];
2346
+ for (var i = 0; i < geom.length; i++) {
2347
+ var ring = [];
2348
+ lineCover(tileHash, geom[i], zoom, ring);
2349
+ for (var j = 0, len = ring.length, k = len - 1; j < len; k = j++) {
2350
+ var m = (j + 1) % len;
2351
+ var y = ring[j][1];
2352
+
2353
+ // add interesction if it's not local extremum or duplicate
2354
+ if ((y > ring[k][1] || y > ring[m][1]) && (
2355
+ // not local minimum
2356
+ y < ring[k][1] || y < ring[m][1]) &&
2357
+ // not local maximum
2358
+ y !== ring[m][1]) intersections.push(ring[j]);
2359
+ }
2360
+ }
2361
+ intersections.sort(compareTiles); // sort by y, then x
2362
+
2363
+ for (i = 0; i < intersections.length; i += 2) {
2364
+ // fill tiles between pairs of intersections
2365
+ y = intersections[i][1];
2366
+ for (var x = intersections[i][0] + 1; x < intersections[i + 1][0]; x++) {
2367
+ var id = toID(x, y, zoom);
2368
+ if (!tileHash[id]) {
2369
+ tileArray.push([x, y, zoom]);
2370
+ }
2371
+ }
2372
+ }
2373
+ }
2374
+ function compareTiles(a, b) {
2375
+ return a[1] - b[1] || a[0] - b[0];
2376
+ }
2377
+ function lineCover(tileHash, coords, maxZoom, ring) {
2378
+ var prevX, prevY;
2379
+ for (var i = 0; i < coords.length - 1; i++) {
2380
+ var start = tilebelt.pointToTileFraction(coords[i][0], coords[i][1], maxZoom),
2381
+ stop = tilebelt.pointToTileFraction(coords[i + 1][0], coords[i + 1][1], maxZoom),
2382
+ x0 = start[0],
2383
+ y0 = start[1],
2384
+ x1 = stop[0],
2385
+ y1 = stop[1],
2386
+ dx = x1 - x0,
2387
+ dy = y1 - y0;
2388
+ if (dy === 0 && dx === 0) continue;
2389
+ var sx = dx > 0 ? 1 : -1,
2390
+ sy = dy > 0 ? 1 : -1,
2391
+ x = Math.floor(x0),
2392
+ y = Math.floor(y0),
2393
+ tMaxX = dx === 0 ? Infinity : Math.abs(((dx > 0 ? 1 : 0) + x - x0) / dx),
2394
+ tMaxY = dy === 0 ? Infinity : Math.abs(((dy > 0 ? 1 : 0) + y - y0) / dy),
2395
+ tdx = Math.abs(sx / dx),
2396
+ tdy = Math.abs(sy / dy);
2397
+ if (x !== prevX || y !== prevY) {
2398
+ tileHash[toID(x, y, maxZoom)] = true;
2399
+ if (ring && y !== prevY) ring.push([x, y]);
2400
+ prevX = x;
2401
+ prevY = y;
2402
+ }
2403
+ while (tMaxX < 1 || tMaxY < 1) {
2404
+ if (tMaxX < tMaxY) {
2405
+ tMaxX += tdx;
2406
+ x += sx;
2407
+ } else {
2408
+ tMaxY += tdy;
2409
+ y += sy;
2410
+ }
2411
+ tileHash[toID(x, y, maxZoom)] = true;
2412
+ if (ring && y !== prevY) ring.push([x, y]);
2413
+ prevX = x;
2414
+ prevY = y;
2415
+ }
2416
+ }
2417
+ if (ring && y === ring[0][1]) ring.pop();
2418
+ }
2419
+ function appendHashTiles(hash, tiles) {
2420
+ var keys = Object.keys(hash);
2421
+ for (var i = 0; i < keys.length; i++) {
2422
+ tiles.push(fromID(+keys[i]));
2423
+ }
2424
+ }
2425
+ function toID(x, y, z) {
2426
+ var dim = 2 * (1 << z);
2427
+ return (dim * y + x) * 32 + z;
2428
+ }
2429
+ function fromID(id) {
2430
+ var z = id % 32,
2431
+ dim = 2 * (1 << z),
2432
+ xy = (id - z) / 32,
2433
+ x = xy % dim,
2434
+ y = (xy - x) / dim % dim;
2435
+ return [x, y, z];
2436
+ }
2437
+
2438
+ const B = [0x5555555555555555n, 0x3333333333333333n, 0x0f0f0f0f0f0f0f0fn, 0x00ff00ff00ff00ffn, 0x0000ffff0000ffffn, 0x00000000ffffffffn];
2439
+ const S = [0n, 1n, 2n, 4n, 8n, 16n];
2440
+ function tileToCell(tile) {
2441
+ if (tile.z < 0 || tile.z > 26) {
2442
+ throw new Error('Wrong zoom');
2443
+ }
2444
+ const z = BigInt(tile.z);
2445
+ let x = BigInt(tile.x) << 32n - z;
2446
+ let y = BigInt(tile.y) << 32n - z;
2447
+ for (let i = 0; i < 5; i++) {
2448
+ const s = S[5 - i];
2449
+ const b = B[4 - i];
2450
+ x = (x | x << s) & b;
2451
+ y = (y | y << s) & b;
2452
+ }
2453
+ const quadbin = 0x4000000000000000n | 1n << 59n |
2454
+ // | (mode << 59) | (mode_dep << 57)
2455
+ z << 52n | (x | y << 1n) >> 12n | 0xfffffffffffffn >> z * 2n;
2456
+ return quadbin;
2457
+ }
2458
+ function getResolution$1(quadbin) {
2459
+ return quadbin >> 52n & 0x1fn;
2460
+ }
2461
+ function geometryToCells(geometry, resolution) {
2462
+ const zoom = Number(resolution);
2463
+ return tiles(geometry, {
2464
+ min_zoom: zoom,
2465
+ max_zoom: zoom
2466
+ }).map(([x, y, z]) => tileToCell({
2467
+ x,
2468
+ y,
2469
+ z
2470
+ }));
2471
+ }
2472
+
2473
+ function tileFeaturesSpatialIndex({
2474
+ tiles,
2475
+ spatialFilter,
2476
+ spatialDataColumn,
2477
+ spatialDataType
2478
+ }) {
2479
+ const map = new Map();
2480
+ const spatialIndex = getSpatialIndex(spatialDataType);
2481
+ const resolution = getResolution(tiles, spatialIndex);
2482
+ const spatialIndexIDName = spatialDataColumn ? spatialDataColumn : spatialIndex;
2483
+ if (!resolution) {
2484
+ return [];
2485
+ }
2486
+ const cells = getCellsCoverGeometry(spatialFilter, spatialIndex, resolution);
2487
+ if (!(cells != null && cells.length)) {
2488
+ return [];
2489
+ }
2490
+ // We transform cells to Set to improve the performace
2491
+ const cellsSet = new Set(cells);
2492
+ for (const tile of tiles) {
2493
+ if (tile.isVisible === false || !tile.data) {
2494
+ continue;
2495
+ }
2496
+ tile.data.forEach(d => {
2497
+ if (cellsSet.has(d.id)) {
2498
+ map.set(d.id, _extends({}, d.properties, {
2499
+ [spatialIndexIDName]: d.id
2500
+ }));
2501
+ }
2502
+ });
2503
+ }
2504
+ return Array.from(map.values());
2505
+ }
2506
+ function getResolution(tiles, spatialIndex) {
2507
+ var _tiles$find;
2508
+ const data = (_tiles$find = tiles.find(tile => {
2509
+ var _tile$data;
2510
+ return (_tile$data = tile.data) == null ? void 0 : _tile$data.length;
2511
+ })) == null ? void 0 : _tiles$find.data;
2512
+ if (!data) {
2513
+ return;
2514
+ }
2515
+ if (spatialIndex === SpatialIndex.QUADBIN) {
2516
+ return Number(getResolution$1(data[0].id));
2517
+ }
2518
+ if (spatialIndex === SpatialIndex.H3) {
2519
+ return getResolution$2(data[0].id);
2520
+ }
2521
+ }
2522
+ const bboxWest = [-180, -90, 0, 90];
2523
+ const bboxEast = [0, -90, 180, 90];
2524
+ function getCellsCoverGeometry(geometry, spatialIndex, resolution) {
2525
+ if (spatialIndex === SpatialIndex.QUADBIN) {
2526
+ // @ts-expect-error TODO: Probably ought to be stricter about number vs. bigint types in this file.
2527
+ return geometryToCells(geometry, resolution);
2528
+ }
2529
+ if (spatialIndex === SpatialIndex.H3) {
2530
+ // The current H3 polyfill algorithm can't deal with polygon segments of greater than 180 degrees longitude
2531
+ // so we clip the geometry to be sure that none of them is greater than 180 degrees
2532
+ // https://github.com/uber/h3-js/issues/24#issuecomment-431893796
2533
+ return polygonToCells(bboxClip(geometry, bboxWest).geometry.coordinates, resolution, true).concat(polygonToCells(bboxClip(geometry, bboxEast).geometry.coordinates, resolution, true));
2534
+ }
2535
+ }
2536
+ function getSpatialIndex(spatialDataType) {
2537
+ switch (spatialDataType) {
2538
+ case 'h3':
2539
+ return SpatialIndex.H3;
2540
+ case 'quadbin':
2541
+ return SpatialIndex.QUADBIN;
2542
+ default:
2543
+ throw new Error('Unexpected spatial data type');
2544
+ }
2545
+ }
2546
+
2547
+ /** @internalRemarks Source: @carto/react-core */
2548
+ function tileFeatures({
2549
+ tiles,
2550
+ spatialFilter,
2551
+ uniqueIdProperty,
2552
+ tileFormat,
2553
+ spatialDataColumn = DEFAULT_GEO_COLUMN,
2554
+ spatialDataType,
2555
+ options = {}
2556
+ }) {
2557
+ // TODO(cleanup): Is an empty response the expected result when spatialFilter
2558
+ // is omitted? Why not make the parameter required, or return the full input?
2559
+ if (!spatialFilter) {
2560
+ return [];
2561
+ }
2562
+ if (spatialDataType !== 'geo') {
2563
+ return tileFeaturesSpatialIndex({
2564
+ tiles: tiles,
2565
+ spatialFilter,
2566
+ spatialDataColumn,
2567
+ spatialDataType
2568
+ });
2569
+ }
2570
+ return tileFeaturesGeometries({
2571
+ tiles,
2572
+ tileFormat,
2573
+ spatialFilter,
2574
+ uniqueIdProperty,
2575
+ options
2576
+ });
2577
+ }
2578
+
2579
+ /** @internalRemarks Source: @carto/react-core */
2580
+ const aggregationFunctions = {
2581
+ count: values => values.length,
2582
+ min: (...args) => applyAggregationFunction(min, ...args),
2583
+ max: (...args) => applyAggregationFunction(max, ...args),
2584
+ sum: (...args) => applyAggregationFunction(sum, ...args),
2585
+ avg: (...args) => applyAggregationFunction(avg, ...args)
2586
+ };
2587
+ /** @internalRemarks Source: @carto/react-core */
2588
+ function aggregate(feature, keys, operation) {
2589
+ if (!(keys != null && keys.length)) {
2590
+ throw new Error('Cannot aggregate a feature without having keys');
2591
+ } else if (keys.length === 1) {
2592
+ const value = feature[keys[0]];
2593
+ return isPotentiallyValidNumber(value) ? Number(value) : value;
2594
+ }
2595
+ const aggregationFn = aggregationFunctions[operation];
2596
+ if (!aggregationFn) {
2597
+ throw new Error(`${operation} isn't a valid aggregation function`);
2598
+ }
2599
+ return aggregationFn(keys.map(column => {
2600
+ const value = feature[column];
2601
+ return isPotentiallyValidNumber(value) ? Number(value) : value;
2602
+ }));
2603
+ }
2604
+ /*
2605
+ * Forced casting to Number (just of non empty strings) allows to work-around
2606
+ * some specific situations, where a big numeric field is transformed into a string when generating the tileset(eg.PG)
2607
+ */
2608
+ function isPotentiallyValidNumber(value) {
2609
+ return typeof value === 'string' && value.trim().length > 0;
2610
+ }
2611
+ const applyAggregationFunction = (aggFn, values, keys, operation) => {
2612
+ const normalizedKeys = normalizeKeys(keys);
2613
+ const elements = ((normalizedKeys == null ? void 0 : normalizedKeys.length) || 0) <= 1 ? filterFalsyElements(values, normalizedKeys || []) : values;
2614
+ return aggFn(elements, keys, operation);
2615
+ };
2616
+ function filterFalsyElements(values, keys) {
2617
+ const filterFn = value => value !== null && value !== undefined;
2618
+ if (!(keys != null && keys.length)) {
2619
+ return values.filter(filterFn);
2620
+ }
2621
+ return values.filter(v => filterFn(v[keys[0]]));
2622
+ }
2623
+ // Aggregation functions
2624
+ function avg(values, keys, joinOperation) {
2625
+ return sum(values, keys, joinOperation) / (values.length || 1);
2626
+ }
2627
+ function sum(values, keys, joinOperation) {
2628
+ const normalizedKeys = normalizeKeys(keys);
2629
+ if (normalizedKeys) {
2630
+ return values.reduce((a, b) => a + aggregate(b, normalizedKeys, joinOperation), 0);
2631
+ }
2632
+ return values.reduce((a, b) => a + b, 0);
2633
+ }
2634
+ function min(values, keys, joinOperation) {
2635
+ const normalizedKeys = normalizeKeys(keys);
2636
+ if (normalizedKeys) {
2637
+ return values.reduce((a, b) => Math.min(a, aggregate(b, normalizedKeys, joinOperation)), Infinity);
2638
+ }
2639
+ return Math.min(...values);
2640
+ }
2641
+ function max(values, keys, joinOperation) {
2642
+ const normalizedKeys = normalizeKeys(keys);
2643
+ if (normalizedKeys) {
2644
+ return values.reduce((a, b) => Math.max(a, aggregate(b, normalizedKeys, joinOperation)), -Infinity);
2645
+ }
2646
+ return Math.max(...values);
2647
+ }
2648
+ // Aux
2649
+ // Keys can come as a string (one column) or a strings array (multiple column)
2650
+ // Use always an array to make the code easier
2651
+ function normalizeKeys(keys) {
2652
+ return Array.isArray(keys) ? keys : typeof keys === 'string' ? [keys] : undefined;
2653
+ }
2654
+
2655
+ /***
2656
+ Copyright 2013 Teun Duynstee
2657
+
2658
+ Licensed under the Apache License, Version 2.0 (the "License");
2659
+ you may not use this file except in compliance with the License.
2660
+ You may obtain a copy of the License at
2661
+
2662
+ http://www.apache.org/licenses/LICENSE-2.0
2663
+
2664
+ Unless required by applicable law or agreed to in writing, software
2665
+ distributed under the License is distributed on an "AS IS" BASIS,
2666
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
2667
+ See the License for the specific language governing permissions and
2668
+ limitations under the License.
2669
+ */
2670
+ var thenBy_module = function () {
2671
+ function identity(v) {
2672
+ return v;
2673
+ }
2674
+ function ignoreCase(v) {
2675
+ return typeof v === "string" ? v.toLowerCase() : v;
2676
+ }
2677
+ function makeCompareFunction(f, opt) {
2678
+ opt = typeof opt === "object" ? opt : {
2679
+ direction: opt
2680
+ };
2681
+ if (typeof f != "function") {
2682
+ var prop = f;
2683
+ // make unary function
2684
+ f = function (v1) {
2685
+ return !!v1[prop] ? v1[prop] : "";
2686
+ };
2687
+ }
2688
+ if (f.length === 1) {
2689
+ // f is a unary function mapping a single item to its sort score
2690
+ var uf = f;
2691
+ var preprocess = opt.ignoreCase ? ignoreCase : identity;
2692
+ var cmp = opt.cmp || function (v1, v2) {
2693
+ return v1 < v2 ? -1 : v1 > v2 ? 1 : 0;
2694
+ };
2695
+ f = function (v1, v2) {
2696
+ return cmp(preprocess(uf(v1)), preprocess(uf(v2)));
2697
+ };
2698
+ }
2699
+ const descTokens = {
2700
+ "-1": '',
2701
+ desc: ''
2702
+ };
2703
+ if (opt.direction in descTokens) return function (v1, v2) {
2704
+ return -f(v1, v2);
2705
+ };
2706
+ return f;
2707
+ }
2708
+
2709
+ /* adds a secondary compare function to the target function (`this` context)
2710
+ which is applied in case the first one returns 0 (equal)
2711
+ returns a new compare function, which has a `thenBy` method as well */
2712
+ function tb(func, opt) {
2713
+ /* should get value false for the first call. This can be done by calling the
2714
+ exported function, or the firstBy property on it (for es6 module compatibility)
2715
+ */
2716
+ var x = typeof this == "function" && !this.firstBy ? this : false;
2717
+ var y = makeCompareFunction(func, opt);
2718
+ var f = x ? function (a, b) {
2719
+ return x(a, b) || y(a, b);
2720
+ } : y;
2721
+ f.thenBy = tb;
2722
+ return f;
2723
+ }
2724
+ tb.firstBy = tb;
2725
+ return tb;
2726
+ }();
2727
+
2728
+ /**
2729
+ * Apply sort structure to a collection of features
2730
+ * @param features
2731
+ * @param [sortOptions]
2732
+ * @param [sortOptions.sortBy] - One or more columns to sort by
2733
+ * @param [sortOptions.sortByDirection] - Direction by the columns will be sorted
2734
+ * @param [sortOptions.sortByColumnType] - Column type
2735
+ * @internal
2736
+ * @internalRemarks Source: @carto/react-core
2737
+ */
2738
+ function applySorting(features, {
2739
+ sortBy,
2740
+ sortByDirection = 'asc',
2741
+ sortByColumnType = 'string'
2742
+ } = {}) {
2743
+ // If sortBy is undefined, pass all features
2744
+ if (sortBy === undefined) {
2745
+ return features;
2746
+ }
2747
+ // sortOptions exists, but are bad formatted
2748
+ const isValidSortBy = Array.isArray(sortBy) && sortBy.length ||
2749
+ // sortBy can be an array of columns
2750
+ typeof sortBy === 'string'; // or just one column
2751
+ if (!isValidSortBy) {
2752
+ throw new Error('Sorting options are bad formatted');
2753
+ }
2754
+ const sortFn = createSortFn({
2755
+ sortBy,
2756
+ sortByDirection,
2757
+ sortByColumnType: sortByColumnType || 'string'
2758
+ });
2759
+ return features.sort(sortFn);
2760
+ }
2761
+ // Aux
2762
+ function createSortFn({
2763
+ sortBy,
2764
+ sortByDirection,
2765
+ sortByColumnType
2766
+ }) {
2767
+ const [firstSortOption, ...othersSortOptions] = normalizeSortByOptions({
2768
+ sortBy,
2769
+ sortByDirection,
2770
+ sortByColumnType
2771
+ });
2772
+ let sortFn = thenBy_module.firstBy(...firstSortOption);
2773
+ for (let sortOptions of othersSortOptions) {
2774
+ sortFn = sortFn.thenBy(...sortOptions);
2775
+ }
2776
+ return sortFn;
2777
+ }
2778
+ function normalizeSortByOptions({
2779
+ sortBy,
2780
+ sortByDirection,
2781
+ sortByColumnType
2782
+ }) {
2783
+ const numberFormat = sortByColumnType === 'number' && {
2784
+ cmp: (a, b) => a - b
2785
+ };
2786
+ if (!Array.isArray(sortBy)) {
2787
+ sortBy = [sortBy];
2788
+ }
2789
+ return sortBy.map(sortByEl => {
2790
+ // sortByEl is 'column'
2791
+ if (typeof sortByEl === 'string') {
2792
+ return [sortByEl, _extends({
2793
+ direction: sortByDirection
2794
+ }, numberFormat)];
2795
+ }
2796
+ if (Array.isArray(sortByEl)) {
2797
+ // sortBy is ['column']
2798
+ if (sortByEl[1] === undefined) {
2799
+ return [sortByEl, _extends({
2800
+ direction: sortByDirection
2801
+ }, numberFormat)];
2802
+ }
2803
+ // sortBy is ['column', { ... }]
2804
+ if (typeof sortByEl[1] === 'object') {
2805
+ const othersSortOptions = numberFormat ? _extends({}, numberFormat, sortByEl[1]) : sortByEl[1];
2806
+ return [sortByEl[0], _extends({
2807
+ direction: sortByDirection
2808
+ }, othersSortOptions)];
2809
+ }
2810
+ }
2811
+ return sortByEl;
2812
+ });
2813
+ }
2814
+
2815
+ /** @internalRemarks Source: @carto/react-core */
2816
+ function groupValuesByColumn({
2817
+ data,
2818
+ valuesColumns,
2819
+ joinOperation,
2820
+ keysColumn,
2821
+ operation
2822
+ }) {
2823
+ if (Array.isArray(data) && data.length === 0) {
2824
+ return null;
2825
+ }
2826
+ const groups = data.reduce((accumulator, item) => {
2827
+ const group = item[keysColumn];
2828
+ const values = accumulator.get(group) || [];
2829
+ accumulator.set(group, values);
2830
+ const aggregatedValue = aggregate(item, valuesColumns, joinOperation);
2831
+ const isValid = (operation === 'count' ? true : aggregatedValue !== null) && aggregatedValue !== undefined;
2832
+ if (isValid) {
2833
+ values.push(aggregatedValue);
2834
+ accumulator.set(group, values);
2835
+ }
2836
+ return accumulator;
2837
+ }, new Map()); // We use a map to be able to maintain the type in the key value
2838
+ const targetOperation = aggregationFunctions[operation];
2839
+ if (targetOperation) {
2840
+ return Array.from(groups).map(([name, value]) => ({
2841
+ name,
2842
+ value: targetOperation(value)
2843
+ }));
2844
+ }
2845
+ return [];
2846
+ }
2847
+
2848
+ /**
2849
+ * Returns midnight (local time) on the Monday preceeding a given date, in
2850
+ * milliseconds since the UNIX epoch.
2851
+ */
2852
+ /**
2853
+ * Returns midnight (UTC) on the Monday preceeding a given date, in
2854
+ * milliseconds since the UNIX epoch.
2855
+ */
2856
+ function getUTCMonday(date) {
2857
+ const dateCp = new Date(date);
2858
+ const day = dateCp.getUTCDay();
2859
+ const diff = dateCp.getUTCDate() - day + (day ? 1 : -6); // adjust when day is sunday
2860
+ dateCp.setUTCDate(diff);
2861
+ return Date.UTC(dateCp.getUTCFullYear(), dateCp.getUTCMonth(), dateCp.getUTCDate());
2862
+ }
2863
+
2864
+ const GROUP_KEY_FN_MAPPING = {
2865
+ year: date => Date.UTC(date.getUTCFullYear()),
2866
+ month: date => Date.UTC(date.getUTCFullYear(), date.getUTCMonth()),
2867
+ week: date => getUTCMonday(date),
2868
+ day: date => Date.UTC(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate()),
2869
+ hour: date => Date.UTC(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate(), date.getUTCHours()),
2870
+ minute: date => Date.UTC(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate(), date.getUTCHours(), date.getUTCMinutes()),
2871
+ second: date => Date.UTC(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate(), date.getUTCHours(), date.getUTCMinutes(), date.getUTCSeconds())
2872
+ };
2873
+ /** @internalRemarks Source: @carto/react-core */
2874
+ function groupValuesByDateColumn({
2875
+ data,
2876
+ valuesColumns,
2877
+ joinOperation,
2878
+ keysColumn,
2879
+ groupType,
2880
+ operation
2881
+ }) {
2882
+ if (Array.isArray(data) && data.length === 0) {
2883
+ return null;
2884
+ }
2885
+ const groupKeyFn = GROUP_KEY_FN_MAPPING[groupType];
2886
+ if (!groupKeyFn) {
2887
+ return null;
2888
+ }
2889
+ const groups = data.reduce((acc, item) => {
2890
+ const value = item[keysColumn];
2891
+ const formattedValue = new Date(value);
2892
+ const groupKey = groupKeyFn(formattedValue);
2893
+ if (!isNaN(groupKey)) {
2894
+ let groupedValues = acc.get(groupKey);
2895
+ if (!groupedValues) {
2896
+ groupedValues = [];
2897
+ acc.set(groupKey, groupedValues);
2898
+ }
2899
+ const aggregatedValue = aggregate(item, valuesColumns, joinOperation);
2900
+ const isValid = aggregatedValue !== null && aggregatedValue !== undefined;
2901
+ if (isValid) {
2902
+ groupedValues.push(aggregatedValue);
2903
+ acc.set(groupKey, groupedValues);
2904
+ }
2905
+ }
2906
+ return acc;
2907
+ }, new Map());
2908
+ const targetOperation = aggregationFunctions[operation];
2909
+ return [...groups.entries()].map(([name, value]) => ({
2910
+ name,
2911
+ value: targetOperation(value)
2912
+ })).sort((a, b) => a.name - b.name);
2913
+ }
2914
+
2915
+ /**
2916
+ * Histogram computation.
2917
+ * @internalRemarks Source: @carto/react-core
2918
+ */
2919
+ function histogram({
2920
+ data,
2921
+ valuesColumns,
2922
+ joinOperation,
2923
+ ticks,
2924
+ operation
2925
+ }) {
2926
+ if (Array.isArray(data) && data.length === 0) {
2927
+ return [];
2928
+ }
2929
+ const binsContainer = [Number.MIN_SAFE_INTEGER, ...ticks].map((tick, index, arr) => ({
2930
+ bin: index,
2931
+ start: tick,
2932
+ end: index === arr.length - 1 ? Number.MAX_SAFE_INTEGER : arr[index + 1],
2933
+ values: []
2934
+ }));
2935
+ data.forEach(feature => {
2936
+ const featureValue = aggregate(feature, valuesColumns, joinOperation);
2937
+ const isValid = featureValue !== null && featureValue !== undefined;
2938
+ if (!isValid) {
2939
+ return;
2940
+ }
2941
+ const binContainer = binsContainer.find(bin => bin.start <= featureValue && bin.end > featureValue);
2942
+ if (!binContainer) {
2943
+ return;
2944
+ }
2945
+ binContainer.values.push(featureValue);
2946
+ });
2947
+ const targetOperation = aggregationFunctions[operation];
2948
+ const transformedBins = binsContainer.map(binContainer => binContainer.values);
2949
+ return transformedBins.map(values => values.length ? targetOperation(values) : 0);
2950
+ }
2951
+
2952
+ /**
2953
+ * Filters invalid features and formats data.
2954
+ * @internalRemarks Source: @carto/react-core
2955
+ */
2956
+ function scatterPlot({
2957
+ data,
2958
+ xAxisColumns,
2959
+ xAxisJoinOperation,
2960
+ yAxisColumns,
2961
+ yAxisJoinOperation
2962
+ }) {
2963
+ return data.reduce((acc, feature) => {
2964
+ const xValue = aggregate(feature, xAxisColumns, xAxisJoinOperation);
2965
+ const xIsValid = xValue !== null && xValue !== undefined;
2966
+ const yValue = aggregate(feature, yAxisColumns, yAxisJoinOperation);
2967
+ const yIsValid = yValue !== null && yValue !== undefined;
2968
+ if (xIsValid && yIsValid) {
2969
+ acc.push([xValue, yValue]);
2970
+ }
2971
+ return acc;
2972
+ }, []);
2973
+ }
2974
+
2975
+ const _excluded = ["filterOwner", "spatialFilter", "abortController"];
2976
+ /**
2977
+ * Source for Widget API requests on a data source defined by a tileset.
2978
+ *
2979
+ * Generally not intended to be constructed directly. Instead, call
2980
+ * {@link vectorTilesetSource}, {@link h3TilesetSource}, or {@link quadbinTilesetSource},
2981
+ * which can be shared with map layers. Sources contain a `widgetSource`
2982
+ * property, for use by widget implementations.
2983
+ *
2984
+ * Example:
2985
+ *
2986
+ * ```javascript
2987
+ * import { vectorTilesetSource } from '@carto/api-client';
2988
+ *
2989
+ * const data = vectorTilesetSource({
2990
+ * accessToken: '••••',
2991
+ * connectionName: 'carto_dw',
2992
+ * tableName: 'carto-demo-data.demo_rasters.my_tileset_source'
2993
+ * });
2994
+ *
2995
+ * const { widgetSource } = await data;
2996
+ * ```
2997
+ */
2998
+ class WidgetTilesetSource extends WidgetSource {
2999
+ constructor(...args) {
3000
+ super(...args);
3001
+ this._features = [];
3002
+ }
3003
+ getModelSource(owner) {
3004
+ return _extends({}, super._getModelSource(owner), {
3005
+ type: 'tileset',
3006
+ data: this.props.tableName
3007
+ });
3008
+ }
3009
+ /** Loads features as a list of tiles (typically provided by deck.gl). */
3010
+ loadTiles({
3011
+ tiles,
3012
+ spatialFilter,
3013
+ uniqueIdProperty,
3014
+ options
3015
+ }) {
3016
+ this._features = tileFeatures({
3017
+ tiles,
3018
+ options,
3019
+ spatialFilter,
3020
+ uniqueIdProperty,
3021
+ tileFormat: this.props.tileFormat,
3022
+ spatialDataColumn: this.props.spatialDataColumn,
3023
+ spatialDataType: this.props.spatialDataType
3024
+ });
3025
+ }
3026
+ /** Loads features as GeoJSON (used for testing). */
3027
+ loadGeoJSON({
3028
+ geojson,
3029
+ spatialFilter,
3030
+ uniqueIdProperty
3031
+ }) {
3032
+ this._features = geojsonFeatures({
3033
+ geojson,
3034
+ spatialFilter,
3035
+ uniqueIdProperty
3036
+ });
3037
+ }
3038
+ async getFeatures(options) {
3039
+ throw new Error('getFeatures not supported for tilesets');
3040
+ }
3041
+ async getFormula({
3042
+ column = '*',
3043
+ operation = 'count',
3044
+ joinOperation,
3045
+ filterOwner
3046
+ }) {
3047
+ if (operation === 'custom') {
3048
+ throw new Error('Custom aggregation not supported for tilesets');
3049
+ }
3050
+ if (!this._features.length) {
3051
+ return {
3052
+ value: null
3053
+ };
3054
+ }
3055
+ // Column is required except when operation is 'count'.
3056
+ if (column && column !== '*' || operation !== 'count') {
3057
+ assertColumn(this._features, column);
3058
+ }
3059
+ const filteredFeatures = this._getFilteredFeatures(filterOwner);
3060
+ if (filteredFeatures.length === 0 && operation !== 'count') {
3061
+ return {
3062
+ value: null
3063
+ };
3064
+ }
3065
+ const targetOperation = aggregationFunctions[operation];
3066
+ return {
3067
+ value: targetOperation(filteredFeatures, column, joinOperation)
3068
+ };
3069
+ }
3070
+ async getHistogram({
3071
+ operation = 'count',
3072
+ ticks,
3073
+ column,
3074
+ joinOperation,
3075
+ filterOwner
3076
+ }) {
3077
+ if (!this._features.length) {
3078
+ return [];
3079
+ }
3080
+ const filteredFeatures = this._getFilteredFeatures(filterOwner);
3081
+ assertColumn(this._features, column);
3082
+ return histogram({
3083
+ data: filteredFeatures,
3084
+ valuesColumns: normalizeColumns(column),
3085
+ joinOperation,
3086
+ ticks,
3087
+ operation
3088
+ });
3089
+ }
3090
+ async getCategories({
3091
+ column,
3092
+ operation = 'count',
3093
+ operationColumn,
3094
+ joinOperation,
3095
+ filterOwner
3096
+ }) {
3097
+ if (!this._features.length) {
3098
+ return [];
3099
+ }
3100
+ const filteredFeatures = this._getFilteredFeatures(filterOwner);
3101
+ assertColumn(this._features, column, operationColumn);
3102
+ const groups = groupValuesByColumn({
3103
+ data: filteredFeatures,
3104
+ valuesColumns: normalizeColumns(operationColumn || column),
3105
+ joinOperation,
3106
+ keysColumn: column,
3107
+ operation
3108
+ });
3109
+ return groups || [];
3110
+ }
3111
+ async getScatter({
3112
+ xAxisColumn,
3113
+ yAxisColumn,
3114
+ xAxisJoinOperation,
3115
+ yAxisJoinOperation,
3116
+ filterOwner
3117
+ }) {
3118
+ if (!this._features.length) {
3119
+ return [];
3120
+ }
3121
+ const filteredFeatures = this._getFilteredFeatures(filterOwner);
3122
+ assertColumn(this._features, xAxisColumn, yAxisColumn);
3123
+ return scatterPlot({
3124
+ data: filteredFeatures,
3125
+ xAxisColumns: normalizeColumns(xAxisColumn),
3126
+ xAxisJoinOperation,
3127
+ yAxisColumns: normalizeColumns(yAxisColumn),
3128
+ yAxisJoinOperation
3129
+ });
3130
+ }
3131
+ async getTable(options) {
3132
+ const {
3133
+ filterOwner
3134
+ } = options,
3135
+ params = _objectWithoutPropertiesLoose(options, _excluded);
3136
+ const {
3137
+ columns,
3138
+ searchFilterColumn,
3139
+ searchFilterText,
3140
+ sortBy,
3141
+ sortDirection,
3142
+ sortByColumnType,
3143
+ offset = 0,
3144
+ limit = 10
3145
+ } = params;
3146
+ if (!this._features.length) {
3147
+ return {
3148
+ rows: [],
3149
+ totalCount: 0
3150
+ };
3151
+ }
3152
+ // Filter.
3153
+ let filteredFeatures = this._getFilteredFeatures(filterOwner);
3154
+ // Search.
3155
+ // TODO: Could we get the same behavior by applying filters in loadTiles()?
3156
+ if (searchFilterColumn && searchFilterText) {
3157
+ filteredFeatures = filteredFeatures.filter(row => row[searchFilterColumn] && String(row[searchFilterColumn]).toLowerCase().includes(String(searchFilterText).toLowerCase()));
3158
+ }
3159
+ // Sort.
3160
+ let rows = applySorting(filteredFeatures, {
3161
+ sortBy,
3162
+ sortByDirection: sortDirection,
3163
+ sortByColumnType
3164
+ });
3165
+ const totalCount = rows.length;
3166
+ // Offset and limit.
3167
+ rows = rows.slice(Math.min(offset, totalCount), Math.min(offset + limit, totalCount));
3168
+ // Select columns.
3169
+ rows = rows.map(srcRow => {
3170
+ const dstRow = {};
3171
+ for (const column of columns) {
3172
+ dstRow[column] = srcRow[column];
3173
+ }
3174
+ return dstRow;
3175
+ });
3176
+ return {
3177
+ rows,
3178
+ totalCount
3179
+ };
3180
+ }
3181
+ async getTimeSeries({
3182
+ column,
3183
+ stepSize,
3184
+ operation,
3185
+ operationColumn,
3186
+ joinOperation,
3187
+ filterOwner
3188
+ }) {
3189
+ if (!this._features.length) {
3190
+ return {
3191
+ rows: []
3192
+ };
3193
+ }
3194
+ const filteredFeatures = this._getFilteredFeatures(filterOwner);
3195
+ assertColumn(this._features, column, operationColumn);
3196
+ const rows = groupValuesByDateColumn({
3197
+ data: filteredFeatures,
3198
+ valuesColumns: normalizeColumns(operationColumn || column),
3199
+ keysColumn: column,
3200
+ groupType: stepSize,
3201
+ operation,
3202
+ joinOperation
3203
+ }) || [];
3204
+ return {
3205
+ rows
3206
+ };
3207
+ }
3208
+ async getRange({
3209
+ column,
3210
+ filterOwner
3211
+ }) {
3212
+ if (!this._features.length) {
3213
+ // TODO: Is this the only nullable response in the Widgets API? If so,
3214
+ // can we do something more consistent?
3215
+ return null;
3216
+ }
3217
+ assertColumn(this._features, column);
3218
+ const filteredFeatures = this._getFilteredFeatures(filterOwner);
3219
+ return {
3220
+ min: aggregationFunctions.min(filteredFeatures, column),
3221
+ max: aggregationFunctions.max(filteredFeatures, column)
3222
+ };
3223
+ }
3224
+ /****************************************************************************
3225
+ * INTERNAL
3226
+ */
3227
+ _getFilteredFeatures(filterOwner) {
3228
+ return applyFilters(this._features, getApplicableFilters(filterOwner, this.props.filters), this.props.filtersLogicalOperator || 'and');
3229
+ }
3230
+ }
3231
+ function assertColumn(features, ...columnArgs) {
3232
+ // TODO(cleanup): Can drop support for multiple column shapes here?
3233
+ // Due to the multiple column shape, we normalise it as an array with normalizeColumns
3234
+ const columns = Array.from(new Set(columnArgs.map(normalizeColumns).flat()));
3235
+ const featureKeys = Object.keys(features[0]);
3236
+ const invalidColumns = columns.filter(column => !featureKeys.includes(column));
3237
+ if (invalidColumns.length) {
3238
+ throw new InvalidColumnError(`Missing column(s): ${invalidColumns.join(', ')}`);
3239
+ }
3240
+ }
3241
+ function normalizeColumns(columns) {
3242
+ return Array.isArray(columns) ? columns : typeof columns === 'string' ? [columns] : [];
3243
+ }
3244
+
3245
+ const h3QuerySource = async function h3QuerySource(options) {
3246
+ const {
3247
+ aggregationExp,
3248
+ aggregationResLevel = DEFAULT_AGGREGATION_RES_LEVEL_H3,
3249
+ sqlQuery,
3250
+ spatialDataColumn = 'h3',
3251
+ queryParameters,
3252
+ filters
3253
+ } = options;
3254
+ const urlParameters = {
3255
+ aggregationExp,
3256
+ spatialDataColumn,
3257
+ spatialDataType: 'h3',
3258
+ q: sqlQuery
3259
+ };
3260
+ if (aggregationResLevel) {
3261
+ urlParameters.aggregationResLevel = String(aggregationResLevel);
3262
+ }
3263
+ if (queryParameters) {
3264
+ urlParameters.queryParameters = queryParameters;
3265
+ }
3266
+ if (filters) {
3267
+ urlParameters.filters = filters;
3268
+ }
3269
+ return baseSource('query', options, urlParameters).then(result => _extends({}, result, {
3270
+ widgetSource: new WidgetQuerySource(_extends({}, options, {
3271
+ // NOTE: passing redundant spatialDataColumn here to apply the default value 'h3'
3272
+ spatialDataColumn,
3273
+ spatialDataType: 'h3'
3274
+ }))
3275
+ }));
3276
+ };
3277
+
3278
+ const h3TableSource = async function h3TableSource(options) {
3279
+ const {
3280
+ aggregationExp,
3281
+ aggregationResLevel = DEFAULT_AGGREGATION_RES_LEVEL_H3,
3282
+ spatialDataColumn = 'h3',
3283
+ tableName,
3284
+ filters
3285
+ } = options;
3286
+ const urlParameters = {
3287
+ aggregationExp,
3288
+ name: tableName,
3289
+ spatialDataColumn,
3290
+ spatialDataType: 'h3'
3291
+ };
3292
+ if (aggregationResLevel) {
3293
+ urlParameters.aggregationResLevel = String(aggregationResLevel);
3294
+ }
3295
+ if (filters) {
3296
+ urlParameters.filters = filters;
3297
+ }
3298
+ return baseSource('table', options, urlParameters).then(result => _extends({}, result, {
3299
+ widgetSource: new WidgetTableSource(_extends({}, options, {
3300
+ // NOTE: passing redundant spatialDataColumn here to apply the default value 'h3'
3301
+ spatialDataColumn,
3302
+ spatialDataType: 'h3'
3303
+ }))
3304
+ }));
3305
+ };
3306
+
3307
+ function getTileFormat(tilejson) {
3308
+ const tileParams = new URL(tilejson.tiles[0]).searchParams;
3309
+ return tileParams.get('formatTiles') === 'mvt' ? TileFormat.MVT : TileFormat.BINARY;
3310
+ }
3311
+
3312
+ const h3TilesetSource = async function h3TilesetSource(options) {
3313
+ const {
3314
+ tableName,
3315
+ spatialDataColumn = 'h3'
3316
+ } = options;
3317
+ const urlParameters = {
3318
+ name: tableName
3319
+ };
3320
+ return baseSource('tileset', options, urlParameters).then(result => _extends({}, result, {
3321
+ widgetSource: new WidgetTilesetSource(_extends({}, options, {
3322
+ tileFormat: getTileFormat(result),
3323
+ spatialDataColumn,
3324
+ spatialDataType: 'h3'
3325
+ }))
3326
+ }));
3327
+ };
3328
+
3329
+ // deck.gl
3330
+ // SPDX-License-Identifier: MIT
3331
+ // Copyright (c) vis.gl contributors
3332
+ const rasterSource = async function rasterSource(options) {
3333
+ const {
3334
+ tableName,
3335
+ filters
3336
+ } = options;
3337
+ const urlParameters = {
3338
+ name: tableName
3339
+ };
3340
+ if (filters) {
3341
+ urlParameters.filters = filters;
3342
+ }
3343
+ return baseSource('raster', options, urlParameters);
3344
+ };
3345
+
3346
+ const quadbinQuerySource = async function quadbinQuerySource(options) {
3347
+ const {
3348
+ aggregationExp,
3349
+ aggregationResLevel = DEFAULT_AGGREGATION_RES_LEVEL_QUADBIN,
3350
+ sqlQuery,
3351
+ spatialDataColumn = 'quadbin',
3352
+ queryParameters,
3353
+ filters
3354
+ } = options;
3355
+ const urlParameters = {
3356
+ aggregationExp,
3357
+ q: sqlQuery,
3358
+ spatialDataColumn,
3359
+ spatialDataType: 'quadbin'
3360
+ };
3361
+ if (aggregationResLevel) {
3362
+ urlParameters.aggregationResLevel = String(aggregationResLevel);
3363
+ }
3364
+ if (queryParameters) {
3365
+ urlParameters.queryParameters = queryParameters;
3366
+ }
3367
+ if (filters) {
3368
+ urlParameters.filters = filters;
3369
+ }
3370
+ return baseSource('query', options, urlParameters).then(result => _extends({}, result, {
3371
+ widgetSource: new WidgetQuerySource(_extends({}, options, {
3372
+ // NOTE: passing redundant spatialDataColumn here to apply the default value 'quadbin'
3373
+ spatialDataColumn,
3374
+ spatialDataType: 'quadbin'
3375
+ }))
3376
+ }));
3377
+ };
3378
+
3379
+ const quadbinTableSource = async function quadbinTableSource(options) {
3380
+ const {
3381
+ aggregationExp,
3382
+ aggregationResLevel = DEFAULT_AGGREGATION_RES_LEVEL_QUADBIN,
3383
+ spatialDataColumn = 'quadbin',
3384
+ tableName,
3385
+ filters
3386
+ } = options;
3387
+ const urlParameters = {
3388
+ aggregationExp,
3389
+ name: tableName,
3390
+ spatialDataColumn,
3391
+ spatialDataType: 'quadbin'
3392
+ };
3393
+ if (aggregationResLevel) {
3394
+ urlParameters.aggregationResLevel = String(aggregationResLevel);
3395
+ }
3396
+ if (filters) {
3397
+ urlParameters.filters = filters;
3398
+ }
3399
+ return baseSource('table', options, urlParameters).then(result => _extends({}, result, {
3400
+ widgetSource: new WidgetTableSource(_extends({}, options, {
3401
+ // NOTE: passing redundant spatialDataColumn here to apply the default value 'quadbin'
3402
+ spatialDataColumn,
3403
+ spatialDataType: 'quadbin'
3404
+ }))
3405
+ }));
3406
+ };
3407
+
3408
+ const quadbinTilesetSource = async function quadbinTilesetSource(options) {
3409
+ const {
3410
+ tableName,
3411
+ spatialDataColumn = 'quadbin'
3412
+ } = options;
3413
+ const urlParameters = {
3414
+ name: tableName
3415
+ };
3416
+ return baseSource('tileset', options, urlParameters).then(result => _extends({}, result, {
3417
+ widgetSource: new WidgetTilesetSource(_extends({}, options, {
3418
+ tileFormat: getTileFormat(result),
3419
+ spatialDataColumn,
3420
+ spatialDataType: 'quadbin'
3421
+ }))
3422
+ }));
3423
+ };
3424
+
3425
+ const vectorQuerySource = async function vectorQuerySource(options) {
3426
+ const {
3427
+ columns,
3428
+ filters,
3429
+ spatialDataColumn = DEFAULT_GEO_COLUMN,
1467
3430
  sqlQuery,
1468
3431
  tileResolution = DEFAULT_TILE_RESOLUTION,
1469
3432
  queryParameters,
@@ -1488,7 +3451,10 @@ const vectorQuerySource = async function vectorQuerySource(options) {
1488
3451
  urlParameters.aggregationExp = aggregationExp;
1489
3452
  }
1490
3453
  return baseSource('query', options, urlParameters).then(result => _extends({}, result, {
1491
- widgetSource: new WidgetQuerySource(options)
3454
+ widgetSource: new WidgetQuerySource(_extends({}, options, {
3455
+ spatialDataColumn,
3456
+ spatialDataType: 'geo'
3457
+ }))
1492
3458
  }));
1493
3459
  };
1494
3460
 
@@ -1496,7 +3462,7 @@ const vectorTableSource = async function vectorTableSource(options) {
1496
3462
  const {
1497
3463
  columns,
1498
3464
  filters,
1499
- spatialDataColumn = 'geom',
3465
+ spatialDataColumn = DEFAULT_GEO_COLUMN,
1500
3466
  tableName,
1501
3467
  tileResolution = DEFAULT_TILE_RESOLUTION,
1502
3468
  aggregationExp
@@ -1517,19 +3483,28 @@ const vectorTableSource = async function vectorTableSource(options) {
1517
3483
  urlParameters.aggregationExp = aggregationExp;
1518
3484
  }
1519
3485
  return baseSource('table', options, urlParameters).then(result => _extends({}, result, {
1520
- widgetSource: new WidgetTableSource(options)
3486
+ widgetSource: new WidgetTableSource(_extends({}, options, {
3487
+ spatialDataColumn,
3488
+ spatialDataType: 'geo'
3489
+ }))
1521
3490
  }));
1522
3491
  };
1523
3492
 
1524
- // deck.gl
1525
3493
  const vectorTilesetSource = async function vectorTilesetSource(options) {
1526
3494
  const {
1527
- tableName
3495
+ tableName,
3496
+ spatialDataColumn = DEFAULT_GEO_COLUMN
1528
3497
  } = options;
1529
3498
  const urlParameters = {
1530
3499
  name: tableName
1531
3500
  };
1532
- return baseSource('tileset', options, urlParameters);
3501
+ return baseSource('tileset', options, urlParameters).then(result => _extends({}, result, {
3502
+ widgetSource: new WidgetTilesetSource(_extends({}, options, {
3503
+ tileFormat: getTileFormat(result),
3504
+ spatialDataColumn,
3505
+ spatialDataType: 'geo'
3506
+ }))
3507
+ }));
1533
3508
  };
1534
3509
 
1535
3510
  const query = async function query(options) {
@@ -1574,5 +3549,5 @@ const query = async function query(options) {
1574
3549
  });
1575
3550
  };
1576
3551
 
1577
- export { ApiVersion, CartoAPIError, DEFAULT_API_BASE_URL, FilterType, SOURCE_DEFAULTS, WidgetBaseSource, WidgetQuerySource, WidgetTableSource, addFilter, boundaryQuerySource, boundaryTableSource, buildPublicMapUrl, buildStatsUrl, clearFilters, createPolygonSpatialFilter, createViewportSpatialFilter, getClient, getFilter, h3QuerySource, h3TableSource, h3TilesetSource, hasFilter, quadbinQuerySource, quadbinTableSource, quadbinTilesetSource, query, rasterSource, removeFilter, requestWithParameters, setClient, vectorQuerySource, vectorTableSource, vectorTilesetSource };
3552
+ export { ApiVersion, CartoAPIError, DEFAULT_API_BASE_URL, FEATURE_GEOM_PROPERTY, FilterType, Provider, SOURCE_DEFAULTS, SpatialIndex, TileFormat, WidgetQuerySource, WidgetRemoteSource, WidgetTableSource, WidgetTilesetSource, addFilter, aggregate, aggregationFunctions, applyFilters, applySorting, boundaryQuerySource, boundaryTableSource, buildBinaryFeatureFilter, buildFeatureFilter, buildPublicMapUrl, buildStatsUrl, clearFilters, createPolygonSpatialFilter, createViewportSpatialFilter, filterFunctions, geojsonFeatures, getClient, getFilter, groupValuesByColumn, groupValuesByDateColumn, h3QuerySource, h3TableSource, h3TilesetSource, hasFilter, histogram, makeIntervalComplete, quadbinQuerySource, quadbinTableSource, quadbinTilesetSource, query, rasterSource, removeFilter, requestWithParameters, scatterPlot, setClient, tileFeatures, tileFeaturesGeometries, tileFeaturesSpatialIndex, transformToTileCoords, vectorQuerySource, vectorTableSource, vectorTilesetSource };
1578
3553
  //# sourceMappingURL=api-client.modern.js.map