@carto/api-client 0.5.0-alpha.1 → 0.5.0-alpha.3

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.
@@ -6,6 +6,7 @@ var helpers = require('@turf/helpers');
6
6
  var intersects = require('@turf/boolean-intersects');
7
7
  var booleanWithin = require('@turf/boolean-within');
8
8
  var intersect = require('@turf/intersect');
9
+ var quadbin = require('quadbin');
9
10
  var h3Js = require('h3-js');
10
11
 
11
12
  /**
@@ -796,6 +797,98 @@ const boundaryTableSource = function (options) {
796
797
  }
797
798
  };
798
799
 
800
+ const DEFAULT_TILE_SIZE = 512;
801
+ const QUADBIN_ZOOM_MAX_OFFSET = 4;
802
+ function getSpatialFiltersResolution(source, viewState) {
803
+ const dataResolution = source.dataResolution ?? Number.MAX_VALUE;
804
+ const aggregationResLevel = source.aggregationResLevel ?? (source.spatialDataType === 'h3' ? DEFAULT_AGGREGATION_RES_LEVEL_H3 : DEFAULT_AGGREGATION_RES_LEVEL_QUADBIN);
805
+ const aggregationResLevelOffset = Math.max(0, Math.floor(aggregationResLevel));
806
+ const currentZoomInt = Math.ceil(viewState.zoom);
807
+ if (source.spatialDataType === 'h3') {
808
+ const tileSize = DEFAULT_TILE_SIZE;
809
+ const maxResolutionForZoom = maxH3SpatialFiltersResolutions.find(_ref => {
810
+ let [zoom] = _ref;
811
+ return zoom === currentZoomInt;
812
+ })?.[1] ?? Math.max(0, currentZoomInt - 3);
813
+ const maxSpatialFiltersResolution = maxResolutionForZoom ? Math.min(dataResolution, maxResolutionForZoom) : dataResolution;
814
+ const hexagonResolution = _getHexagonResolution(viewState, tileSize) + aggregationResLevelOffset;
815
+ return Math.min(hexagonResolution, maxSpatialFiltersResolution);
816
+ }
817
+ if (source.spatialDataType === 'quadbin') {
818
+ const maxResolutionForZoom = currentZoomInt + QUADBIN_ZOOM_MAX_OFFSET;
819
+ const maxSpatialFiltersResolution = Math.min(dataResolution, maxResolutionForZoom);
820
+ const quadsResolution = Math.floor(viewState.zoom) + aggregationResLevelOffset;
821
+ return Math.min(quadsResolution, maxSpatialFiltersResolution);
822
+ }
823
+ return undefined;
824
+ }
825
+ 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]];
826
+ // stolen from https://github.com/visgl/deck.gl/blob/master/modules/carto/src/layers/h3-tileset-2d.ts
827
+ // Relative scale factor (0 = no biasing, 2 = a few hexagons cover view)
828
+ const BIAS = 2;
829
+ /**
830
+ * Resolution conversion function. Takes a WebMercatorViewport and returns
831
+ * a H3 resolution such that the screen space size of the hexagons is
832
+ * "similar" to the given tileSize on screen. Intended for use with deck.gl.
833
+ * @internal
834
+ */
835
+ function _getHexagonResolution(viewport, tileSize) {
836
+ // Difference in given tile size compared to deck's internal 512px tile size,
837
+ // expressed as an offset to the viewport zoom.
838
+ const zoomOffset = Math.log2(tileSize / DEFAULT_TILE_SIZE);
839
+ const hexagonScaleFactor = 2 / 3 * (viewport.zoom - zoomOffset);
840
+ const latitudeScaleFactor = Math.log(1 / Math.cos(Math.PI * viewport.latitude / 180));
841
+ // Clip and bias
842
+ return Math.max(0, Math.floor(hexagonScaleFactor + latitudeScaleFactor - BIAS));
843
+ }
844
+
845
+ /**
846
+ * Source for Widget API requests on a data source defined by a SQL query.
847
+ *
848
+ * Abstract class. Use {@link WidgetQuerySource} or {@link WidgetTableSource}.
849
+ */
850
+ class WidgetSource {
851
+ constructor(props) {
852
+ this.props = void 0;
853
+ this.props = {
854
+ ...WidgetSource.defaultProps,
855
+ ...props
856
+ };
857
+ }
858
+ _getModelSource(owner) {
859
+ const props = this.props;
860
+ return {
861
+ apiVersion: props.apiVersion,
862
+ apiBaseUrl: props.apiBaseUrl,
863
+ clientId: props.clientId,
864
+ accessToken: props.accessToken,
865
+ connectionName: props.connectionName,
866
+ filters: getApplicableFilters(owner, props.filters),
867
+ filtersLogicalOperator: props.filtersLogicalOperator,
868
+ spatialDataType: props.spatialDataType,
869
+ spatialDataColumn: props.spatialDataColumn,
870
+ dataResolution: props.dataResolution
871
+ };
872
+ }
873
+ _getSpatialFiltersResolution(source, spatialFilter, referenceViewState) {
874
+ // spatialFiltersResolution applies only to spatial index sources.
875
+ if (!spatialFilter || source.spatialDataType === 'geo') {
876
+ return;
877
+ }
878
+ if (!referenceViewState) {
879
+ throw new Error('Missing required option, "spatialIndexReferenceViewState".');
880
+ }
881
+ return getSpatialFiltersResolution(source, referenceViewState);
882
+ }
883
+ }
884
+ WidgetSource.defaultProps = {
885
+ apiVersion: exports.ApiVersion.V3,
886
+ apiBaseUrl: DEFAULT_API_BASE_URL,
887
+ clientId: getClient(),
888
+ filters: {},
889
+ filtersLogicalOperator: 'and'
890
+ };
891
+
799
892
  /**
800
893
  * Return more descriptive error from API
801
894
  * @internalRemarks Source: @carto/react-api
@@ -990,95 +1083,6 @@ function objectToURLSearchParams(object) {
990
1083
  return params;
991
1084
  }
992
1085
 
993
- const DEFAULT_TILE_SIZE = 512;
994
- const QUADBIN_ZOOM_MAX_OFFSET = 4;
995
- function getSpatialFiltersResolution(source, viewState) {
996
- const dataResolution = source.dataResolution ?? Number.MAX_VALUE;
997
- const aggregationResLevel = source.aggregationResLevel ?? (source.spatialDataType === 'h3' ? DEFAULT_AGGREGATION_RES_LEVEL_H3 : DEFAULT_AGGREGATION_RES_LEVEL_QUADBIN);
998
- const aggregationResLevelOffset = Math.max(0, Math.floor(aggregationResLevel));
999
- const currentZoomInt = Math.ceil(viewState.zoom);
1000
- if (source.spatialDataType === 'h3') {
1001
- const tileSize = DEFAULT_TILE_SIZE;
1002
- const maxResolutionForZoom = maxH3SpatialFiltersResolutions.find(_ref => {
1003
- let [zoom] = _ref;
1004
- return zoom === currentZoomInt;
1005
- })?.[1] ?? Math.max(0, currentZoomInt - 3);
1006
- const maxSpatialFiltersResolution = maxResolutionForZoom ? Math.min(dataResolution, maxResolutionForZoom) : dataResolution;
1007
- const hexagonResolution = getHexagonResolution(viewState, tileSize) + aggregationResLevelOffset;
1008
- return Math.min(hexagonResolution, maxSpatialFiltersResolution);
1009
- }
1010
- if (source.spatialDataType === 'quadbin') {
1011
- const maxResolutionForZoom = currentZoomInt + QUADBIN_ZOOM_MAX_OFFSET;
1012
- const maxSpatialFiltersResolution = Math.min(dataResolution, maxResolutionForZoom);
1013
- const quadsResolution = Math.floor(viewState.zoom) + aggregationResLevelOffset;
1014
- return Math.min(quadsResolution, maxSpatialFiltersResolution);
1015
- }
1016
- return undefined;
1017
- }
1018
- 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]];
1019
- // stolen from https://github.com/visgl/deck.gl/blob/master/modules/carto/src/layers/h3-tileset-2d.ts
1020
- // Relative scale factor (0 = no biasing, 2 = a few hexagons cover view)
1021
- const BIAS = 2;
1022
- // Resolution conversion function. Takes a WebMercatorViewport and returns
1023
- // a H3 resolution such that the screen space size of the hexagons is
1024
- // similar
1025
- function getHexagonResolution(viewport, tileSize) {
1026
- // Difference in given tile size compared to deck's internal 512px tile size,
1027
- // expressed as an offset to the viewport zoom.
1028
- const zoomOffset = Math.log2(tileSize / DEFAULT_TILE_SIZE);
1029
- const hexagonScaleFactor = 2 / 3 * (viewport.zoom - zoomOffset);
1030
- const latitudeScaleFactor = Math.log(1 / Math.cos(Math.PI * viewport.latitude / 180));
1031
- // Clip and bias
1032
- return Math.max(0, Math.floor(hexagonScaleFactor + latitudeScaleFactor - BIAS));
1033
- }
1034
-
1035
- /**
1036
- * Source for Widget API requests on a data source defined by a SQL query.
1037
- *
1038
- * Abstract class. Use {@link WidgetQuerySource} or {@link WidgetTableSource}.
1039
- */
1040
- class WidgetSource {
1041
- constructor(props) {
1042
- this.props = void 0;
1043
- this.props = {
1044
- ...WidgetSource.defaultProps,
1045
- ...props
1046
- };
1047
- }
1048
- _getModelSource(owner) {
1049
- const props = this.props;
1050
- return {
1051
- apiVersion: props.apiVersion,
1052
- apiBaseUrl: props.apiBaseUrl,
1053
- clientId: props.clientId,
1054
- accessToken: props.accessToken,
1055
- connectionName: props.connectionName,
1056
- filters: getApplicableFilters(owner, props.filters),
1057
- filtersLogicalOperator: props.filtersLogicalOperator,
1058
- spatialDataType: props.spatialDataType,
1059
- spatialDataColumn: props.spatialDataColumn,
1060
- dataResolution: props.dataResolution
1061
- };
1062
- }
1063
- _getSpatialFiltersResolution(source, spatialFilter, referenceViewState) {
1064
- // spatialFiltersResolution applies only to spatial index sources.
1065
- if (!spatialFilter || source.spatialDataType === 'geo') {
1066
- return;
1067
- }
1068
- if (!referenceViewState) {
1069
- throw new Error('Missing required option, "spatialIndexReferenceViewState".');
1070
- }
1071
- return getSpatialFiltersResolution(source, referenceViewState);
1072
- }
1073
- }
1074
- WidgetSource.defaultProps = {
1075
- apiVersion: exports.ApiVersion.V3,
1076
- apiBaseUrl: DEFAULT_API_BASE_URL,
1077
- clientId: getClient(),
1078
- filters: {},
1079
- filtersLogicalOperator: 'and'
1080
- };
1081
-
1082
1086
  /**
1083
1087
  * Source for Widget API requests.
1084
1088
  *
@@ -1491,38 +1495,6 @@ class WidgetQuerySource extends WidgetRemoteSource {
1491
1495
  }
1492
1496
  }
1493
1497
 
1494
- /**
1495
- * Source for Widget API requests on a data source defined as a table.
1496
- *
1497
- * Generally not intended to be constructed directly. Instead, call
1498
- * {@link vectorTableSource}, {@link h3TableSource}, or {@link quadbinTableSource},
1499
- * which can be shared with map layers. Sources contain a `widgetSource` property,
1500
- * for use by widget implementations.
1501
- *
1502
- * Example:
1503
- *
1504
- * ```javascript
1505
- * import { vectorTableSource } from '@carto/api-client';
1506
- *
1507
- * const data = vectorTableSource({
1508
- * accessToken: '••••',
1509
- * connectionName: 'carto_dw',
1510
- * tableName: 'carto-demo-data.demo_tables.retail_stores'
1511
- * });
1512
- *
1513
- * const { widgetSource } = await data;
1514
- * ```
1515
- */
1516
- class WidgetTableSource extends WidgetRemoteSource {
1517
- getModelSource(owner) {
1518
- return {
1519
- ...super._getModelSource(owner),
1520
- type: 'table',
1521
- data: this.props.tableName
1522
- };
1523
- }
1524
- }
1525
-
1526
1498
  function makeIntervalComplete(intervals) {
1527
1499
  return intervals.map(val => {
1528
1500
  if (val[0] === undefined || val[0] === null) {
@@ -2236,369 +2208,6 @@ function createIndicesForPoints(data) {
2236
2208
  data.pointIndices = pointIndices;
2237
2209
  }
2238
2210
 
2239
- // a tile is an array [x,y,z]
2240
- var d2r = Math.PI / 180,
2241
- r2d = 180 / Math.PI;
2242
- function tileToBBOX(tile) {
2243
- var e = tile2lon(tile[0] + 1, tile[2]);
2244
- var w = tile2lon(tile[0], tile[2]);
2245
- var s = tile2lat(tile[1] + 1, tile[2]);
2246
- var n = tile2lat(tile[1], tile[2]);
2247
- return [w, s, e, n];
2248
- }
2249
- function tileToGeoJSON(tile) {
2250
- var bbox = tileToBBOX(tile);
2251
- var poly = {
2252
- type: 'Polygon',
2253
- coordinates: [[[bbox[0], bbox[1]], [bbox[0], bbox[3]], [bbox[2], bbox[3]], [bbox[2], bbox[1]], [bbox[0], bbox[1]]]]
2254
- };
2255
- return poly;
2256
- }
2257
- function tile2lon(x, z) {
2258
- return x / Math.pow(2, z) * 360 - 180;
2259
- }
2260
- function tile2lat(y, z) {
2261
- var n = Math.PI - 2 * Math.PI * y / Math.pow(2, z);
2262
- return r2d * Math.atan(0.5 * (Math.exp(n) - Math.exp(-n)));
2263
- }
2264
- function pointToTile(lon, lat, z) {
2265
- var tile = pointToTileFraction(lon, lat, z);
2266
- tile[0] = Math.floor(tile[0]);
2267
- tile[1] = Math.floor(tile[1]);
2268
- return tile;
2269
- }
2270
- function getChildren(tile) {
2271
- 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]];
2272
- }
2273
- function getParent(tile) {
2274
- // top left
2275
- if (tile[0] % 2 === 0 && tile[1] % 2 === 0) {
2276
- return [tile[0] / 2, tile[1] / 2, tile[2] - 1];
2277
- }
2278
- // bottom left
2279
- else if (tile[0] % 2 === 0 && !tile[1] % 2 === 0) {
2280
- return [tile[0] / 2, (tile[1] - 1) / 2, tile[2] - 1];
2281
- }
2282
- // top right
2283
- else if (!tile[0] % 2 === 0 && tile[1] % 2 === 0) {
2284
- return [(tile[0] - 1) / 2, tile[1] / 2, tile[2] - 1];
2285
- }
2286
- // bottom right
2287
- else {
2288
- return [(tile[0] - 1) / 2, (tile[1] - 1) / 2, tile[2] - 1];
2289
- }
2290
- }
2291
- function getSiblings(tile) {
2292
- return getChildren(getParent(tile));
2293
- }
2294
- function hasSiblings(tile, tiles) {
2295
- var siblings = getSiblings(tile);
2296
- for (var i = 0; i < siblings.length; i++) {
2297
- if (!hasTile(tiles, siblings[i])) return false;
2298
- }
2299
- return true;
2300
- }
2301
- function hasTile(tiles, tile) {
2302
- for (var i = 0; i < tiles.length; i++) {
2303
- if (tilesEqual(tiles[i], tile)) return true;
2304
- }
2305
- return false;
2306
- }
2307
- function tilesEqual(tile1, tile2) {
2308
- return tile1[0] === tile2[0] && tile1[1] === tile2[1] && tile1[2] === tile2[2];
2309
- }
2310
- function tileToQuadkey(tile) {
2311
- var index = '';
2312
- for (var z = tile[2]; z > 0; z--) {
2313
- var b = 0;
2314
- var mask = 1 << z - 1;
2315
- if ((tile[0] & mask) !== 0) b++;
2316
- if ((tile[1] & mask) !== 0) b += 2;
2317
- index += b.toString();
2318
- }
2319
- return index;
2320
- }
2321
- function quadkeyToTile(quadkey) {
2322
- var x = 0;
2323
- var y = 0;
2324
- var z = quadkey.length;
2325
- for (var i = z; i > 0; i--) {
2326
- var mask = 1 << i - 1;
2327
- switch (quadkey[z - i]) {
2328
- case '0':
2329
- break;
2330
- case '1':
2331
- x |= mask;
2332
- break;
2333
- case '2':
2334
- y |= mask;
2335
- break;
2336
- case '3':
2337
- x |= mask;
2338
- y |= mask;
2339
- break;
2340
- }
2341
- }
2342
- return [x, y, z];
2343
- }
2344
- function bboxToTile(bboxCoords) {
2345
- var min = pointToTile(bboxCoords[0], bboxCoords[1], 32);
2346
- var max = pointToTile(bboxCoords[2], bboxCoords[3], 32);
2347
- var bbox = [min[0], min[1], max[0], max[1]];
2348
- var z = getBboxZoom(bbox);
2349
- if (z === 0) return [0, 0, 0];
2350
- var x = bbox[0] >>> 32 - z;
2351
- var y = bbox[1] >>> 32 - z;
2352
- return [x, y, z];
2353
- }
2354
- function getBboxZoom(bbox) {
2355
- var MAX_ZOOM = 28;
2356
- for (var z = 0; z < MAX_ZOOM; z++) {
2357
- var mask = 1 << 32 - (z + 1);
2358
- if ((bbox[0] & mask) != (bbox[2] & mask) || (bbox[1] & mask) != (bbox[3] & mask)) {
2359
- return z;
2360
- }
2361
- }
2362
- return MAX_ZOOM;
2363
- }
2364
- function pointToTileFraction(lon, lat, z) {
2365
- var sin = Math.sin(lat * d2r),
2366
- z2 = Math.pow(2, z),
2367
- x = z2 * (lon / 360 + 0.5),
2368
- y = z2 * (0.5 - 0.25 * Math.log((1 + sin) / (1 - sin)) / Math.PI);
2369
- return [x, y, z];
2370
- }
2371
- var tilebelt = {
2372
- tileToGeoJSON: tileToGeoJSON,
2373
- tileToBBOX: tileToBBOX,
2374
- getChildren: getChildren,
2375
- getParent: getParent,
2376
- getSiblings: getSiblings,
2377
- hasTile: hasTile,
2378
- hasSiblings: hasSiblings,
2379
- tilesEqual: tilesEqual,
2380
- tileToQuadkey: tileToQuadkey,
2381
- quadkeyToTile: quadkeyToTile,
2382
- pointToTile: pointToTile,
2383
- bboxToTile: bboxToTile,
2384
- pointToTileFraction: pointToTileFraction
2385
- };
2386
-
2387
- /**
2388
- * Given a geometry, create cells and return them in their raw form,
2389
- * as an array of cell identifiers.
2390
- *
2391
- * @alias tiles
2392
- * @param {Object} geom GeoJSON geometry
2393
- * @param {Object} limits an object with min_zoom and max_zoom properties
2394
- * specifying the minimum and maximum level to be tiled.
2395
- * @returns {Array<Array<number>>} An array of tiles given as [x, y, z] arrays
2396
- */
2397
- var tiles = getTiles;
2398
- function getTiles(geom, limits) {
2399
- var i,
2400
- tile,
2401
- coords = geom.coordinates,
2402
- maxZoom = limits.max_zoom,
2403
- tileHash = {},
2404
- tiles = [];
2405
- if (geom.type === 'Point') {
2406
- return [tilebelt.pointToTile(coords[0], coords[1], maxZoom)];
2407
- } else if (geom.type === 'MultiPoint') {
2408
- for (i = 0; i < coords.length; i++) {
2409
- tile = tilebelt.pointToTile(coords[i][0], coords[i][1], maxZoom);
2410
- tileHash[toID(tile[0], tile[1], tile[2])] = true;
2411
- }
2412
- } else if (geom.type === 'LineString') {
2413
- lineCover(tileHash, coords, maxZoom);
2414
- } else if (geom.type === 'MultiLineString') {
2415
- for (i = 0; i < coords.length; i++) {
2416
- lineCover(tileHash, coords[i], maxZoom);
2417
- }
2418
- } else if (geom.type === 'Polygon') {
2419
- polygonCover(tileHash, tiles, coords, maxZoom);
2420
- } else if (geom.type === 'MultiPolygon') {
2421
- for (i = 0; i < coords.length; i++) {
2422
- polygonCover(tileHash, tiles, coords[i], maxZoom);
2423
- }
2424
- } else {
2425
- throw new Error('Geometry type not implemented');
2426
- }
2427
- if (limits.min_zoom !== maxZoom) {
2428
- // sync tile hash and tile array so that both contain the same tiles
2429
- var len = tiles.length;
2430
- appendHashTiles(tileHash, tiles);
2431
- for (i = 0; i < len; i++) {
2432
- var t = tiles[i];
2433
- tileHash[toID(t[0], t[1], t[2])] = true;
2434
- }
2435
- return mergeTiles(tileHash, tiles, limits);
2436
- }
2437
- appendHashTiles(tileHash, tiles);
2438
- return tiles;
2439
- }
2440
- function mergeTiles(tileHash, tiles, limits) {
2441
- var mergedTiles = [];
2442
- for (var z = limits.max_zoom; z > limits.min_zoom; z--) {
2443
- var parentTileHash = {};
2444
- var parentTiles = [];
2445
- for (var i = 0; i < tiles.length; i++) {
2446
- var t = tiles[i];
2447
- if (t[0] % 2 === 0 && t[1] % 2 === 0) {
2448
- var id2 = toID(t[0] + 1, t[1], z),
2449
- id3 = toID(t[0], t[1] + 1, z),
2450
- id4 = toID(t[0] + 1, t[1] + 1, z);
2451
- if (tileHash[id2] && tileHash[id3] && tileHash[id4]) {
2452
- tileHash[toID(t[0], t[1], t[2])] = false;
2453
- tileHash[id2] = false;
2454
- tileHash[id3] = false;
2455
- tileHash[id4] = false;
2456
- var parentTile = [t[0] / 2, t[1] / 2, z - 1];
2457
- if (z - 1 === limits.min_zoom) mergedTiles.push(parentTile);else {
2458
- parentTileHash[toID(t[0] / 2, t[1] / 2, z - 1)] = true;
2459
- parentTiles.push(parentTile);
2460
- }
2461
- }
2462
- }
2463
- }
2464
- for (i = 0; i < tiles.length; i++) {
2465
- t = tiles[i];
2466
- if (tileHash[toID(t[0], t[1], t[2])]) mergedTiles.push(t);
2467
- }
2468
- tileHash = parentTileHash;
2469
- tiles = parentTiles;
2470
- }
2471
- return mergedTiles;
2472
- }
2473
- function polygonCover(tileHash, tileArray, geom, zoom) {
2474
- var intersections = [];
2475
- for (var i = 0; i < geom.length; i++) {
2476
- var ring = [];
2477
- lineCover(tileHash, geom[i], zoom, ring);
2478
- for (var j = 0, len = ring.length, k = len - 1; j < len; k = j++) {
2479
- var m = (j + 1) % len;
2480
- var y = ring[j][1];
2481
-
2482
- // add interesction if it's not local extremum or duplicate
2483
- if ((y > ring[k][1] || y > ring[m][1]) && (
2484
- // not local minimum
2485
- y < ring[k][1] || y < ring[m][1]) &&
2486
- // not local maximum
2487
- y !== ring[m][1]) intersections.push(ring[j]);
2488
- }
2489
- }
2490
- intersections.sort(compareTiles); // sort by y, then x
2491
-
2492
- for (i = 0; i < intersections.length; i += 2) {
2493
- // fill tiles between pairs of intersections
2494
- y = intersections[i][1];
2495
- for (var x = intersections[i][0] + 1; x < intersections[i + 1][0]; x++) {
2496
- var id = toID(x, y, zoom);
2497
- if (!tileHash[id]) {
2498
- tileArray.push([x, y, zoom]);
2499
- }
2500
- }
2501
- }
2502
- }
2503
- function compareTiles(a, b) {
2504
- return a[1] - b[1] || a[0] - b[0];
2505
- }
2506
- function lineCover(tileHash, coords, maxZoom, ring) {
2507
- var prevX, prevY;
2508
- for (var i = 0; i < coords.length - 1; i++) {
2509
- var start = tilebelt.pointToTileFraction(coords[i][0], coords[i][1], maxZoom),
2510
- stop = tilebelt.pointToTileFraction(coords[i + 1][0], coords[i + 1][1], maxZoom),
2511
- x0 = start[0],
2512
- y0 = start[1],
2513
- x1 = stop[0],
2514
- y1 = stop[1],
2515
- dx = x1 - x0,
2516
- dy = y1 - y0;
2517
- if (dy === 0 && dx === 0) continue;
2518
- var sx = dx > 0 ? 1 : -1,
2519
- sy = dy > 0 ? 1 : -1,
2520
- x = Math.floor(x0),
2521
- y = Math.floor(y0),
2522
- tMaxX = dx === 0 ? Infinity : Math.abs(((dx > 0 ? 1 : 0) + x - x0) / dx),
2523
- tMaxY = dy === 0 ? Infinity : Math.abs(((dy > 0 ? 1 : 0) + y - y0) / dy),
2524
- tdx = Math.abs(sx / dx),
2525
- tdy = Math.abs(sy / dy);
2526
- if (x !== prevX || y !== prevY) {
2527
- tileHash[toID(x, y, maxZoom)] = true;
2528
- if (ring && y !== prevY) ring.push([x, y]);
2529
- prevX = x;
2530
- prevY = y;
2531
- }
2532
- while (tMaxX < 1 || tMaxY < 1) {
2533
- if (tMaxX < tMaxY) {
2534
- tMaxX += tdx;
2535
- x += sx;
2536
- } else {
2537
- tMaxY += tdy;
2538
- y += sy;
2539
- }
2540
- tileHash[toID(x, y, maxZoom)] = true;
2541
- if (ring && y !== prevY) ring.push([x, y]);
2542
- prevX = x;
2543
- prevY = y;
2544
- }
2545
- }
2546
- if (ring && y === ring[0][1]) ring.pop();
2547
- }
2548
- function appendHashTiles(hash, tiles) {
2549
- var keys = Object.keys(hash);
2550
- for (var i = 0; i < keys.length; i++) {
2551
- tiles.push(fromID(+keys[i]));
2552
- }
2553
- }
2554
- function toID(x, y, z) {
2555
- var dim = 2 * (1 << z);
2556
- return (dim * y + x) * 32 + z;
2557
- }
2558
- function fromID(id) {
2559
- var z = id % 32,
2560
- dim = 2 * (1 << z),
2561
- xy = (id - z) / 32,
2562
- x = xy % dim,
2563
- y = (xy - x) / dim % dim;
2564
- return [x, y, z];
2565
- }
2566
-
2567
- const B = [0x5555555555555555n, 0x3333333333333333n, 0x0f0f0f0f0f0f0f0fn, 0x00ff00ff00ff00ffn, 0x0000ffff0000ffffn, 0x00000000ffffffffn];
2568
- const S = [0n, 1n, 2n, 4n, 8n, 16n];
2569
- function tileToCell(tile) {
2570
- if (tile.z < 0 || tile.z > 26) {
2571
- throw new Error('Wrong zoom');
2572
- }
2573
- const z = BigInt(tile.z);
2574
- let x = BigInt(tile.x) << 32n - z;
2575
- let y = BigInt(tile.y) << 32n - z;
2576
- for (let i = 0; i < 5; i++) {
2577
- const s = S[5 - i];
2578
- const b = B[4 - i];
2579
- x = (x | x << s) & b;
2580
- y = (y | y << s) & b;
2581
- }
2582
- const quadbin = 0x4000000000000000n | 1n << 59n |
2583
- // | (mode << 59) | (mode_dep << 57)
2584
- z << 52n | (x | y << 1n) >> 12n | 0xfffffffffffffn >> z * 2n;
2585
- return quadbin;
2586
- }
2587
- function getResolution$1(quadbin) {
2588
- return quadbin >> 52n & 0x1fn;
2589
- }
2590
- function geometryToCells(geometry, resolution) {
2591
- const zoom = Number(resolution);
2592
- return tiles(geometry, {
2593
- min_zoom: zoom,
2594
- max_zoom: zoom
2595
- }).map(([x, y, z]) => tileToCell({
2596
- x,
2597
- y,
2598
- z
2599
- }));
2600
- }
2601
-
2602
2211
  function tileFeaturesSpatialIndex(_ref) {
2603
2212
  let {
2604
2213
  tiles,
@@ -2640,7 +2249,7 @@ function getResolution(tiles, spatialIndex) {
2640
2249
  return;
2641
2250
  }
2642
2251
  if (spatialIndex === exports.SpatialIndex.QUADBIN) {
2643
- return Number(getResolution$1(data[0].id));
2252
+ return Number(quadbin.getResolution(data[0].id));
2644
2253
  }
2645
2254
  if (spatialIndex === exports.SpatialIndex.H3) {
2646
2255
  return h3Js.getResolution(data[0].id);
@@ -2651,7 +2260,7 @@ const bboxEast = [0, -90, 180, 90];
2651
2260
  function getCellsCoverGeometry(geometry, spatialIndex, resolution) {
2652
2261
  if (spatialIndex === exports.SpatialIndex.QUADBIN) {
2653
2262
  // @ts-expect-error TODO: Probably ought to be stricter about number vs. bigint types in this file.
2654
- return geometryToCells(geometry, resolution);
2263
+ return quadbin.geometryToCells(geometry, resolution);
2655
2264
  }
2656
2265
  if (spatialIndex === exports.SpatialIndex.H3) {
2657
2266
  // The current H3 polyfill algorithm can't deal with polygon segments of greater than 180 degrees longitude
@@ -2671,6 +2280,79 @@ function getSpatialIndex(spatialDataType) {
2671
2280
  }
2672
2281
  }
2673
2282
 
2283
+ function tileFeaturesRaster(_ref) {
2284
+ let {
2285
+ tiles,
2286
+ ...options
2287
+ } = _ref;
2288
+ // Cache band metadata for faster lookup while iterating over pixels.
2289
+ const bandMetadataByName = {};
2290
+ for (const band of options.rasterMetadata.bands) {
2291
+ bandMetadataByName[band.name] = band;
2292
+ }
2293
+ // Omit empty and invisible tiles for simpler processing and types.
2294
+ tiles = tiles.filter(isRasterTileVisible);
2295
+ if (tiles.length === 0) return [];
2296
+ // Raster tiles, and all pixels, are quadbin cells. Resolution of a pixel is
2297
+ // the resolution of the tile, plus the number of subdivisions. Block size
2298
+ // must be square, N x N, where N is a power of two.
2299
+ const tileResolution = quadbin.getResolution(tiles[0].index.q);
2300
+ const tileBlockSize = tiles[0].data.blockSize;
2301
+ const cellResolution = tileResolution + BigInt(Math.log2(tileBlockSize));
2302
+ // Compute covering cells for the spatial filter, at same resolution as the
2303
+ // raster pixels, to be used as a mask.
2304
+ const spatialFilterCells = new Set(quadbin.geometryToCells(options.spatialFilter, cellResolution));
2305
+ const data = new Map();
2306
+ for (const tile of tiles) {
2307
+ const parent = tile.index.q;
2308
+ const children = cellToChildrenSorted(parent, cellResolution);
2309
+ // For each pixel/cell within the spatial filter, create a FeatureData.
2310
+ // Order is row-major, starting from NW and ending at SE.
2311
+ for (let i = 0; i < children.length; i++) {
2312
+ if (!spatialFilterCells.has(children[i])) continue;
2313
+ const cellData = {};
2314
+ let cellDataExists = false;
2315
+ for (const band in tile.data.cells.numericProps) {
2316
+ const value = tile.data.cells.numericProps[band].value[i];
2317
+ // TODO(cleanup): nodata should be a number, not a string.
2318
+ if (Number(bandMetadataByName[band].nodata) !== value) {
2319
+ cellData[band] = tile.data.cells.numericProps[band].value[i];
2320
+ cellDataExists = true;
2321
+ }
2322
+ }
2323
+ if (cellDataExists) {
2324
+ data.set(children[i], cellData);
2325
+ }
2326
+ }
2327
+ }
2328
+ return Array.from(data.values());
2329
+ }
2330
+ /**
2331
+ * Detects whether a given {@link Tile} is a {@link RasterTile}.
2332
+ * @privateRemarks Method of detection is arbitrary, and may be changed.
2333
+ */
2334
+ function isRasterTile(tile) {
2335
+ return tile.data ? tile.data.hasOwnProperty('cells') : false;
2336
+ }
2337
+ function isRasterTileVisible(tile) {
2338
+ return !!(tile.isVisible && tile.data?.cells?.numericProps);
2339
+ }
2340
+ /**
2341
+ * For the raster format, children are sorted in row-major order, starting from
2342
+ * NW and ending at SE. Order returned by quadbin's cellToChildren() is not
2343
+ * defined (and not related to the raster format), so sort explicitly here.
2344
+ */
2345
+ function cellToChildrenSorted(parent, resolution) {
2346
+ return quadbin.cellToChildren(parent, resolution).sort((cellA, cellB) => {
2347
+ const tileA = quadbin.cellToTile(cellA);
2348
+ const tileB = quadbin.cellToTile(cellB);
2349
+ if (tileA.y !== tileB.y) {
2350
+ return tileA.y > tileB.y ? 1 : -1;
2351
+ }
2352
+ return tileA.x > tileB.x ? 1 : -1;
2353
+ });
2354
+ }
2355
+
2674
2356
  /** @internalRemarks Source: @carto/react-core */
2675
2357
  function tileFeatures(_ref) {
2676
2358
  let {
@@ -2680,6 +2362,7 @@ function tileFeatures(_ref) {
2680
2362
  tileFormat,
2681
2363
  spatialDataColumn = DEFAULT_GEO_COLUMN,
2682
2364
  spatialDataType,
2365
+ rasterMetadata,
2683
2366
  options = {}
2684
2367
  } = _ref;
2685
2368
  // TODO(cleanup): Is an empty response the expected result when spatialFilter
@@ -2687,20 +2370,30 @@ function tileFeatures(_ref) {
2687
2370
  if (!spatialFilter) {
2688
2371
  return [];
2689
2372
  }
2690
- if (spatialDataType !== 'geo') {
2691
- return tileFeaturesSpatialIndex({
2373
+ if (spatialDataType === 'geo') {
2374
+ return tileFeaturesGeometries({
2375
+ tiles,
2376
+ tileFormat,
2377
+ spatialFilter,
2378
+ uniqueIdProperty,
2379
+ options
2380
+ });
2381
+ }
2382
+ if (tiles.some(isRasterTile)) {
2383
+ assert$1(rasterMetadata, 'Missing raster metadata');
2384
+ return tileFeaturesRaster({
2692
2385
  tiles: tiles,
2693
2386
  spatialFilter,
2694
2387
  spatialDataColumn,
2695
- spatialDataType
2388
+ spatialDataType,
2389
+ rasterMetadata
2696
2390
  });
2697
2391
  }
2698
- return tileFeaturesGeometries({
2699
- tiles,
2700
- tileFormat,
2392
+ return tileFeaturesSpatialIndex({
2393
+ tiles: tiles,
2701
2394
  spatialFilter,
2702
- uniqueIdProperty,
2703
- options
2395
+ spatialDataColumn,
2396
+ spatialDataType
2704
2397
  });
2705
2398
  }
2706
2399
 
@@ -3185,9 +2878,7 @@ class WidgetTilesetSource extends WidgetSource {
3185
2878
  options,
3186
2879
  spatialFilter,
3187
2880
  uniqueIdProperty,
3188
- tileFormat: this.props.tileFormat,
3189
- spatialDataColumn: this.props.spatialDataColumn,
3190
- spatialDataType: this.props.spatialDataType
2881
+ ...this.props
3191
2882
  });
3192
2883
  }
3193
2884
  /** Loads features as GeoJSON (used for testing). */
@@ -3458,6 +3149,40 @@ function normalizeColumns(columns) {
3458
3149
  return Array.isArray(columns) ? columns : typeof columns === 'string' ? [columns] : [];
3459
3150
  }
3460
3151
 
3152
+ class WidgetRasterSource extends WidgetTilesetSource {}
3153
+
3154
+ /**
3155
+ * Source for Widget API requests on a data source defined as a table.
3156
+ *
3157
+ * Generally not intended to be constructed directly. Instead, call
3158
+ * {@link vectorTableSource}, {@link h3TableSource}, or {@link quadbinTableSource},
3159
+ * which can be shared with map layers. Sources contain a `widgetSource` property,
3160
+ * for use by widget implementations.
3161
+ *
3162
+ * Example:
3163
+ *
3164
+ * ```javascript
3165
+ * import { vectorTableSource } from '@carto/api-client';
3166
+ *
3167
+ * const data = vectorTableSource({
3168
+ * accessToken: '••••',
3169
+ * connectionName: 'carto_dw',
3170
+ * tableName: 'carto-demo-data.demo_tables.retail_stores'
3171
+ * });
3172
+ *
3173
+ * const { widgetSource } = await data;
3174
+ * ```
3175
+ */
3176
+ class WidgetTableSource extends WidgetRemoteSource {
3177
+ getModelSource(owner) {
3178
+ return {
3179
+ ...super._getModelSource(owner),
3180
+ type: 'table',
3181
+ data: this.props.tableName
3182
+ };
3183
+ }
3184
+ }
3185
+
3461
3186
  // deck.gl
3462
3187
  // SPDX-License-Identifier: MIT
3463
3188
  // Copyright (c) vis.gl contributors
@@ -3588,7 +3313,16 @@ const rasterSource = function (options) {
3588
3313
  if (filters) {
3589
3314
  urlParameters.filters = filters;
3590
3315
  }
3591
- return Promise.resolve(baseSource('raster', options, urlParameters));
3316
+ return Promise.resolve(baseSource('raster', options, urlParameters).then(result => ({
3317
+ ...result,
3318
+ widgetSource: new WidgetRasterSource({
3319
+ ...options,
3320
+ tileFormat: getTileFormat(result),
3321
+ spatialDataColumn: 'quadbin',
3322
+ spatialDataType: 'quadbin',
3323
+ rasterMetadata: result.raster_metadata
3324
+ })
3325
+ })));
3592
3326
  } catch (e) {
3593
3327
  return Promise.reject(e);
3594
3328
  }
@@ -3880,9 +3614,12 @@ exports.DEFAULT_API_BASE_URL = DEFAULT_API_BASE_URL;
3880
3614
  exports.FEATURE_GEOM_PROPERTY = FEATURE_GEOM_PROPERTY;
3881
3615
  exports.SOURCE_DEFAULTS = SOURCE_DEFAULTS;
3882
3616
  exports.WidgetQuerySource = WidgetQuerySource;
3617
+ exports.WidgetRasterSource = WidgetRasterSource;
3883
3618
  exports.WidgetRemoteSource = WidgetRemoteSource;
3619
+ exports.WidgetSource = WidgetSource;
3884
3620
  exports.WidgetTableSource = WidgetTableSource;
3885
3621
  exports.WidgetTilesetSource = WidgetTilesetSource;
3622
+ exports._getHexagonResolution = _getHexagonResolution;
3886
3623
  exports.addFilter = addFilter;
3887
3624
  exports.aggregate = aggregate;
3888
3625
  exports.aggregationFunctions = aggregationFunctions;