@carto/api-client 0.4.0-alpha.6 → 0.4.1-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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,13 @@
1
1
  # CHANGELOG
2
2
 
3
+ ## 0.4
4
+
5
+ ### 0.4.0
6
+
7
+ - feat: Add Picking Model API
8
+ - refactor: Migrate sources from `@deck.gl/carto` to `@carto/api-client`
9
+ - deps: Remove `@deck.gl/carto` bundled dependency
10
+
3
11
  ## 0.3
4
12
 
5
13
  ### 0.3.0
@@ -1,3 +1,3 @@
1
1
  import type { SourceOptions, QuerySourceOptions, QueryResult } from '../sources/types';
2
- export type QueryOptions = SourceOptions & Omit<QuerySourceOptions, 'spatialDataColumn'>;
2
+ export type QueryOptions = SourceOptions & QuerySourceOptions;
3
3
  export declare const query: (options: QueryOptions) => Promise<QueryResult>;
@@ -509,41 +509,43 @@ function executeModel(props) {
509
509
  data,
510
510
  filters,
511
511
  filtersLogicalOperator = 'and',
512
- geoColumn = DEFAULT_GEO_COLUMN
512
+ spatialDataType = 'geo',
513
+ spatialFiltersMode = 'intersects',
514
+ spatialFiltersResolution = 0
513
515
  } = source;
514
- const queryParameters = source.queryParameters ? JSON.stringify(source.queryParameters) : '';
515
516
  const queryParams = {
516
517
  type,
517
518
  client: clientId,
518
519
  source: data,
519
- params: JSON.stringify(params),
520
- queryParameters,
521
- filters: JSON.stringify(filters),
520
+ params,
521
+ queryParameters: source.queryParameters || '',
522
+ filters,
522
523
  filtersLogicalOperator
523
524
  };
525
+ const spatialDataColumn = source.spatialDataColumn || DEFAULT_GEO_COLUMN;
524
526
  // Picking Model API requires 'spatialDataColumn'.
525
527
  if (model === 'pick') {
526
- queryParams.spatialDataColumn = geoColumn;
528
+ queryParams.spatialDataColumn = spatialDataColumn;
527
529
  }
528
- // API supports multiple filters, we apply it only to geoColumn
530
+ // API supports multiple filters, we apply it only to spatialDataColumn
529
531
  const spatialFilters = source.spatialFilter ? {
530
- [geoColumn]: source.spatialFilter
532
+ [spatialDataColumn]: source.spatialFilter
531
533
  } : undefined;
532
534
  if (spatialFilters) {
533
- queryParams.spatialFilters = JSON.stringify(spatialFilters);
535
+ queryParams.spatialFilters = spatialFilters; // JSON.stringify(spatialFilters);
536
+ queryParams.spatialDataColumn = spatialDataColumn;
537
+ queryParams.spatialDataType = spatialDataType;
534
538
  }
535
- const urlWithSearchParams = url + '?' + new URLSearchParams(queryParams).toString();
539
+ if (spatialDataType !== 'geo') {
540
+ if (spatialFiltersResolution > 0) {
541
+ queryParams.spatialFiltersResolution = spatialFiltersResolution;
542
+ }
543
+ queryParams.spatialFiltersMode = spatialFiltersMode;
544
+ }
545
+ const urlWithSearchParams = url + '?' + objectToURLSearchParams(queryParams).toString();
536
546
  const isGet = urlWithSearchParams.length <= REQUEST_GET_MAX_URL_LENGTH;
537
547
  if (isGet) {
538
548
  url = urlWithSearchParams;
539
- } else {
540
- // undo the JSON.stringify, @TODO find a better pattern
541
- queryParams.params = params;
542
- queryParams.filters = filters;
543
- queryParams.queryParameters = source.queryParameters;
544
- if (spatialFilters) {
545
- queryParams.spatialFilters = spatialFilters;
546
- }
547
549
  }
548
550
  return makeCall({
549
551
  url,
@@ -557,6 +559,68 @@ function executeModel(props) {
557
559
  }
558
560
  });
559
561
  }
562
+ function objectToURLSearchParams(object) {
563
+ const params = new URLSearchParams();
564
+ for (const key in object) {
565
+ if (isPureObject(object[key])) {
566
+ params.append(key, JSON.stringify(object[key]));
567
+ } else if (Array.isArray(object[key])) {
568
+ params.append(key, JSON.stringify(object[key]));
569
+ } else if (object[key] === null) {
570
+ params.append(key, 'null');
571
+ } else if (object[key] !== undefined) {
572
+ params.append(key, String(object[key]));
573
+ }
574
+ }
575
+ return params;
576
+ }
577
+
578
+ const DEFAULT_TILE_SIZE = 512;
579
+ const QUADBIN_ZOOM_MAX_OFFSET = 4;
580
+ function getSpatialFiltersResolution(_ref) {
581
+ let {
582
+ source,
583
+ viewState
584
+ } = _ref;
585
+ assert(viewState, 'viewState prop is required to compute automatic spatialFiltersResolution when using spatialFilter with spatial indexes. Either pass a `spatialFiltersResolution` prop or a `viewState` prop to avoid this error');
586
+ const dataResolution = source.dataResolution ?? Number.MAX_VALUE;
587
+ const aggregationResLevel = source.aggregationResLevel ?? (source.spatialDataType === 'h3' ? DEFAULT_AGGREGATION_RES_LEVEL_H3 : DEFAULT_AGGREGATION_RES_LEVEL_QUADBIN);
588
+ const aggregationResLevelOffset = Math.max(0, Math.floor(aggregationResLevel));
589
+ const currentZoomInt = Math.ceil(viewState.zoom);
590
+ if (source.spatialDataType === 'h3') {
591
+ const tileSize = DEFAULT_TILE_SIZE;
592
+ const maxResolutionForZoom = maxH3SpatialFiltersResolutions.find(_ref2 => {
593
+ let [zoom] = _ref2;
594
+ return zoom === currentZoomInt;
595
+ })?.[1] ?? Math.max(0, currentZoomInt - 3);
596
+ const maxSpatialFiltersResolution = maxResolutionForZoom ? Math.min(dataResolution, maxResolutionForZoom) : dataResolution;
597
+ const hexagonResolution = getHexagonResolution(viewState, tileSize) + aggregationResLevelOffset;
598
+ return Math.min(hexagonResolution, maxSpatialFiltersResolution);
599
+ }
600
+ if (source.spatialDataType === 'quadbin') {
601
+ const maxResolutionForZoom = currentZoomInt + QUADBIN_ZOOM_MAX_OFFSET;
602
+ const maxSpatialFiltersResolution = Math.min(dataResolution, maxResolutionForZoom);
603
+ const quadsResolution = Math.floor(viewState.zoom) + aggregationResLevelOffset;
604
+ return Math.min(quadsResolution, maxSpatialFiltersResolution);
605
+ }
606
+ return undefined;
607
+ }
608
+ 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]];
609
+ // stolen from https://github.com/visgl/deck.gl/blob/master/modules/carto/src/layers/h3-tileset-2d.ts
610
+ // Relative scale factor (0 = no biasing, 2 = a few hexagons cover view)
611
+ const BIAS = 2;
612
+ // Resolution conversion function. Takes a WebMercatorViewport and returns
613
+ // a H3 resolution such that the screen space size of the hexagons is
614
+ // similar
615
+ function getHexagonResolution(viewport, tileSize) {
616
+ // Difference in given tile size compared to deck's internal 512px tile size,
617
+ // expressed as an offset to the viewport zoom.
618
+ const zoomOffset = Math.log2(tileSize / DEFAULT_TILE_SIZE);
619
+ const hexagonScaleFactor = 2 / 3 * (viewport.zoom - zoomOffset);
620
+ const latitudeScaleFactor = Math.log(1 / Math.cos(Math.PI * viewport.latitude / 180));
621
+ // Clip and bias
622
+ return Math.max(0, Math.floor(hexagonScaleFactor + latitudeScaleFactor - BIAS));
623
+ }
560
624
 
561
625
  /**
562
626
  * Source for Widget API requests on a data source defined by a SQL query.
@@ -581,7 +645,8 @@ class WidgetBaseSource {
581
645
  connectionName: props.connectionName,
582
646
  filters: getApplicableFilters(owner, props.filters),
583
647
  filtersLogicalOperator: props.filtersLogicalOperator,
584
- geoColumn: props.geoColumn
648
+ spatialDataType: props.spatialDataType,
649
+ spatialDataColumn: props.spatialDataColumn
585
650
  };
586
651
  }
587
652
  /****************************************************************************
@@ -597,7 +662,9 @@ class WidgetBaseSource {
597
662
  const {
598
663
  filterOwner,
599
664
  spatialFilter,
665
+ spatialFiltersMode,
600
666
  abortController,
667
+ viewState,
601
668
  ...params
602
669
  } = options;
603
670
  const {
@@ -605,10 +672,20 @@ class WidgetBaseSource {
605
672
  operation,
606
673
  operationColumn
607
674
  } = params;
675
+ const source = _this.getModelSource(filterOwner);
676
+ let spatialFiltersResolution;
677
+ if (spatialFilter && source.spatialDataType !== 'geo') {
678
+ spatialFiltersResolution = getSpatialFiltersResolution({
679
+ source,
680
+ viewState
681
+ });
682
+ }
608
683
  return Promise.resolve(executeModel({
609
684
  model: 'category',
610
685
  source: {
611
- ..._this.getModelSource(filterOwner),
686
+ ...source,
687
+ spatialFiltersResolution,
688
+ spatialFiltersMode,
612
689
  spatialFilter
613
690
  },
614
691
  params: {
@@ -641,7 +718,9 @@ class WidgetBaseSource {
641
718
  const {
642
719
  filterOwner,
643
720
  spatialFilter,
721
+ spatialFiltersMode,
644
722
  abortController,
723
+ viewState,
645
724
  ...params
646
725
  } = options;
647
726
  const {
@@ -652,10 +731,20 @@ class WidgetBaseSource {
652
731
  limit,
653
732
  tileResolution
654
733
  } = params;
734
+ const source = _this2.getModelSource(filterOwner);
735
+ let spatialFiltersResolution;
736
+ if (spatialFilter && source.spatialDataType !== 'geo') {
737
+ spatialFiltersResolution = getSpatialFiltersResolution({
738
+ source,
739
+ viewState
740
+ });
741
+ }
655
742
  return Promise.resolve(executeModel({
656
743
  model: 'pick',
657
744
  source: {
658
- ..._this2.getModelSource(filterOwner),
745
+ ...source,
746
+ spatialFiltersResolution,
747
+ spatialFiltersMode,
659
748
  spatialFilter
660
749
  },
661
750
  params: {
@@ -689,18 +778,30 @@ class WidgetBaseSource {
689
778
  const {
690
779
  filterOwner,
691
780
  spatialFilter,
781
+ spatialFiltersMode,
692
782
  abortController,
693
783
  operationExp,
784
+ viewState,
694
785
  ...params
695
786
  } = options;
696
787
  const {
697
788
  column,
698
789
  operation
699
790
  } = params;
791
+ const source = _this3.getModelSource(filterOwner);
792
+ let spatialFiltersResolution;
793
+ if (spatialFilter && source.spatialDataType !== 'geo') {
794
+ spatialFiltersResolution = getSpatialFiltersResolution({
795
+ source,
796
+ viewState
797
+ });
798
+ }
700
799
  return Promise.resolve(executeModel({
701
800
  model: 'formula',
702
801
  source: {
703
- ..._this3.getModelSource(filterOwner),
802
+ ...source,
803
+ spatialFiltersResolution,
804
+ spatialFiltersMode,
704
805
  spatialFilter
705
806
  },
706
807
  params: {
@@ -729,7 +830,9 @@ class WidgetBaseSource {
729
830
  const {
730
831
  filterOwner,
731
832
  spatialFilter,
833
+ spatialFiltersMode,
732
834
  abortController,
835
+ viewState,
733
836
  ...params
734
837
  } = options;
735
838
  const {
@@ -737,10 +840,20 @@ class WidgetBaseSource {
737
840
  operation,
738
841
  ticks
739
842
  } = params;
843
+ const source = _this4.getModelSource(filterOwner);
844
+ let spatialFiltersResolution;
845
+ if (spatialFilter && source.spatialDataType !== 'geo') {
846
+ spatialFiltersResolution = getSpatialFiltersResolution({
847
+ source,
848
+ viewState
849
+ });
850
+ }
740
851
  return Promise.resolve(executeModel({
741
852
  model: 'histogram',
742
853
  source: {
743
- ..._this4.getModelSource(filterOwner),
854
+ ...source,
855
+ spatialFiltersResolution,
856
+ spatialFiltersMode,
744
857
  spatialFilter
745
858
  },
746
859
  params: {
@@ -785,16 +898,28 @@ class WidgetBaseSource {
785
898
  const {
786
899
  filterOwner,
787
900
  spatialFilter,
901
+ spatialFiltersMode,
788
902
  abortController,
903
+ viewState,
789
904
  ...params
790
905
  } = options;
791
906
  const {
792
907
  column
793
908
  } = params;
909
+ const source = _this5.getModelSource(filterOwner);
910
+ let spatialFiltersResolution;
911
+ if (spatialFilter && source.spatialDataType !== 'geo') {
912
+ spatialFiltersResolution = getSpatialFiltersResolution({
913
+ source,
914
+ viewState
915
+ });
916
+ }
794
917
  return Promise.resolve(executeModel({
795
918
  model: 'range',
796
919
  source: {
797
- ..._this5.getModelSource(filterOwner),
920
+ ...source,
921
+ spatialFiltersResolution,
922
+ spatialFiltersMode,
798
923
  spatialFilter
799
924
  },
800
925
  params: {
@@ -821,7 +946,9 @@ class WidgetBaseSource {
821
946
  const {
822
947
  filterOwner,
823
948
  spatialFilter,
949
+ spatialFiltersMode,
824
950
  abortController,
951
+ viewState,
825
952
  ...params
826
953
  } = options;
827
954
  const {
@@ -830,12 +957,22 @@ class WidgetBaseSource {
830
957
  yAxisColumn,
831
958
  yAxisJoinOperation
832
959
  } = params;
960
+ const source = _this6.getModelSource(filterOwner);
961
+ let spatialFiltersResolution;
962
+ if (spatialFilter && source.spatialDataType !== 'geo') {
963
+ spatialFiltersResolution = getSpatialFiltersResolution({
964
+ source,
965
+ viewState
966
+ });
967
+ }
833
968
  // Make sure this is sync with the same constant in cloud-native/maps-api
834
969
  const HARD_LIMIT = 500;
835
970
  return Promise.resolve(executeModel({
836
971
  model: 'scatterplot',
837
972
  source: {
838
- ..._this6.getModelSource(filterOwner),
973
+ ...source,
974
+ spatialFiltersResolution,
975
+ spatialFiltersMode,
839
976
  spatialFilter
840
977
  },
841
978
  params: {
@@ -872,7 +1009,9 @@ class WidgetBaseSource {
872
1009
  const {
873
1010
  filterOwner,
874
1011
  spatialFilter,
1012
+ spatialFiltersMode,
875
1013
  abortController,
1014
+ viewState,
876
1015
  ...params
877
1016
  } = options;
878
1017
  const {
@@ -882,10 +1021,20 @@ class WidgetBaseSource {
882
1021
  offset = 0,
883
1022
  limit = 10
884
1023
  } = params;
1024
+ const source = _this7.getModelSource(filterOwner);
1025
+ let spatialFiltersResolution;
1026
+ if (spatialFilter && source.spatialDataType !== 'geo') {
1027
+ spatialFiltersResolution = getSpatialFiltersResolution({
1028
+ source,
1029
+ viewState
1030
+ });
1031
+ }
885
1032
  return Promise.resolve(executeModel({
886
1033
  model: 'table',
887
1034
  source: {
888
- ..._this7.getModelSource(filterOwner),
1035
+ ...source,
1036
+ spatialFiltersResolution,
1037
+ spatialFiltersMode,
889
1038
  spatialFilter
890
1039
  },
891
1040
  params: {
@@ -921,6 +1070,8 @@ class WidgetBaseSource {
921
1070
  filterOwner,
922
1071
  abortController,
923
1072
  spatialFilter,
1073
+ spatialFiltersMode,
1074
+ viewState,
924
1075
  ...params
925
1076
  } = options;
926
1077
  const {
@@ -934,10 +1085,20 @@ class WidgetBaseSource {
934
1085
  splitByCategoryLimit,
935
1086
  splitByCategoryValues
936
1087
  } = params;
1088
+ const source = _this8.getModelSource(filterOwner);
1089
+ let spatialFiltersResolution;
1090
+ if (spatialFilter && source.spatialDataType !== 'geo') {
1091
+ spatialFiltersResolution = getSpatialFiltersResolution({
1092
+ source,
1093
+ viewState
1094
+ });
1095
+ }
937
1096
  return Promise.resolve(executeModel({
938
1097
  model: 'timeseries',
939
1098
  source: {
940
- ..._this8.getModelSource(filterOwner),
1099
+ ...source,
1100
+ spatialFiltersResolution,
1101
+ spatialFiltersMode,
941
1102
  spatialFilter
942
1103
  },
943
1104
  params: {
@@ -968,8 +1129,7 @@ WidgetBaseSource.defaultProps = {
968
1129
  apiBaseUrl: DEFAULT_API_BASE_URL,
969
1130
  clientId: getClient(),
970
1131
  filters: {},
971
- filtersLogicalOperator: 'and',
972
- geoColumn: DEFAULT_GEO_COLUMN
1132
+ filtersLogicalOperator: 'and'
973
1133
  };
974
1134
 
975
1135
  /**
@@ -1425,7 +1585,12 @@ const h3QuerySource = function (options) {
1425
1585
  }
1426
1586
  return Promise.resolve(baseSource('query', options, urlParameters).then(result => ({
1427
1587
  ...result,
1428
- widgetSource: new WidgetQuerySource(options)
1588
+ widgetSource: new WidgetQuerySource({
1589
+ ...options,
1590
+ // NOTE: passing redundant spatialDataColumn here to apply the default value 'h3'
1591
+ spatialDataColumn,
1592
+ spatialDataType: 'h3'
1593
+ })
1429
1594
  })));
1430
1595
  } catch (e) {
1431
1596
  return Promise.reject(e);
@@ -1456,7 +1621,12 @@ const h3TableSource = function (options) {
1456
1621
  }
1457
1622
  return Promise.resolve(baseSource('table', options, urlParameters).then(result => ({
1458
1623
  ...result,
1459
- widgetSource: new WidgetTableSource(options)
1624
+ widgetSource: new WidgetTableSource({
1625
+ ...options,
1626
+ // NOTE: passing redundant spatialDataColumn here to apply the default value 'h3'
1627
+ spatialDataColumn,
1628
+ spatialDataType: 'h3'
1629
+ })
1460
1630
  })));
1461
1631
  } catch (e) {
1462
1632
  return Promise.reject(e);
@@ -1525,7 +1695,12 @@ const quadbinQuerySource = function (options) {
1525
1695
  }
1526
1696
  return Promise.resolve(baseSource('query', options, urlParameters).then(result => ({
1527
1697
  ...result,
1528
- widgetSource: new WidgetQuerySource(options)
1698
+ widgetSource: new WidgetQuerySource({
1699
+ ...options,
1700
+ // NOTE: passing redundant spatialDataColumn here to apply the default value 'quadbin'
1701
+ spatialDataColumn,
1702
+ spatialDataType: 'quadbin'
1703
+ })
1529
1704
  })));
1530
1705
  } catch (e) {
1531
1706
  return Promise.reject(e);
@@ -1556,7 +1731,12 @@ const quadbinTableSource = function (options) {
1556
1731
  }
1557
1732
  return Promise.resolve(baseSource('table', options, urlParameters).then(result => ({
1558
1733
  ...result,
1559
- widgetSource: new WidgetTableSource(options)
1734
+ widgetSource: new WidgetTableSource({
1735
+ ...options,
1736
+ // NOTE: passing redundant spatialDataColumn here to apply the default value 'quadbin'
1737
+ spatialDataColumn,
1738
+ spatialDataType: 'quadbin'
1739
+ })
1560
1740
  })));
1561
1741
  } catch (e) {
1562
1742
  return Promise.reject(e);
@@ -1606,7 +1786,10 @@ const vectorQuerySource = function (options) {
1606
1786
  }
1607
1787
  return Promise.resolve(baseSource('query', options, urlParameters).then(result => ({
1608
1788
  ...result,
1609
- widgetSource: new WidgetQuerySource(options)
1789
+ widgetSource: new WidgetQuerySource({
1790
+ ...options,
1791
+ spatialDataType: 'geo'
1792
+ })
1610
1793
  })));
1611
1794
  } catch (e) {
1612
1795
  return Promise.reject(e);
@@ -1637,7 +1820,10 @@ const vectorTableSource = function (options) {
1637
1820
  }
1638
1821
  return Promise.resolve(baseSource('table', options, urlParameters).then(result => ({
1639
1822
  ...result,
1640
- widgetSource: new WidgetTableSource(options)
1823
+ widgetSource: new WidgetTableSource({
1824
+ ...options,
1825
+ spatialDataType: 'geo'
1826
+ })
1641
1827
  })));
1642
1828
  } catch (e) {
1643
1829
  return Promise.reject(e);