@deck.gl-community/geo-layers 9.2.0-beta.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.
- package/LICENSE +19 -0
- package/README.md +5 -0
- package/dist/global-grid-layer/global-grid-cluster-layer.d.ts +36 -0
- package/dist/global-grid-layer/global-grid-cluster-layer.d.ts.map +1 -0
- package/dist/global-grid-layer/global-grid-cluster-layer.js +55 -0
- package/dist/global-grid-layer/global-grid-cluster-layer.js.map +1 -0
- package/dist/global-grid-layer/global-grid-layer.d.ts +21 -0
- package/dist/global-grid-layer/global-grid-layer.d.ts.map +1 -0
- package/dist/global-grid-layer/global-grid-layer.js +33 -0
- package/dist/global-grid-layer/global-grid-layer.js.map +1 -0
- package/dist/global-grid-systems/grids/a5-grid.d.ts +18 -0
- package/dist/global-grid-systems/grids/a5-grid.d.ts.map +1 -0
- package/dist/global-grid-systems/grids/a5-grid.js +23 -0
- package/dist/global-grid-systems/grids/a5-grid.js.map +1 -0
- package/dist/global-grid-systems/grids/geohash-grid.d.ts +15 -0
- package/dist/global-grid-systems/grids/geohash-grid.d.ts.map +1 -0
- package/dist/global-grid-systems/grids/geohash-grid.js +92 -0
- package/dist/global-grid-systems/grids/geohash-grid.js.map +1 -0
- package/dist/global-grid-systems/grids/global-grid.d.ts +31 -0
- package/dist/global-grid-systems/grids/global-grid.d.ts.map +1 -0
- package/dist/global-grid-systems/grids/global-grid.js +5 -0
- package/dist/global-grid-systems/grids/global-grid.js.map +1 -0
- package/dist/global-grid-systems/grids/h3-grid.d.ts +16 -0
- package/dist/global-grid-systems/grids/h3-grid.d.ts.map +1 -0
- package/dist/global-grid-systems/grids/h3-grid.js +29 -0
- package/dist/global-grid-systems/grids/h3-grid.js.map +1 -0
- package/dist/global-grid-systems/grids/quadkey-grid.d.ts +41 -0
- package/dist/global-grid-systems/grids/quadkey-grid.d.ts.map +1 -0
- package/dist/global-grid-systems/grids/quadkey-grid.js +166 -0
- package/dist/global-grid-systems/grids/quadkey-grid.js.map +1 -0
- package/dist/global-grid-systems/grids/s2-grid.d.ts +16 -0
- package/dist/global-grid-systems/grids/s2-grid.d.ts.map +1 -0
- package/dist/global-grid-systems/grids/s2-grid.js +74 -0
- package/dist/global-grid-systems/grids/s2-grid.js.map +1 -0
- package/dist/global-grid-systems/h3-js-bigint/h3-js-bigint.d.ts +9 -0
- package/dist/global-grid-systems/h3-js-bigint/h3-js-bigint.d.ts.map +1 -0
- package/dist/global-grid-systems/h3-js-bigint/h3-js-bigint.js +62 -0
- package/dist/global-grid-systems/h3-js-bigint/h3-js-bigint.js.map +1 -0
- package/dist/global-grid-systems/s2-geometry/s2-geometry.d.ts +13 -0
- package/dist/global-grid-systems/s2-geometry/s2-geometry.d.ts.map +1 -0
- package/dist/global-grid-systems/s2-geometry/s2-geometry.js +139 -0
- package/dist/global-grid-systems/s2-geometry/s2-geometry.js.map +1 -0
- package/dist/global-grid-systems/s2-geometry/s2-to-boundary.d.ts +14 -0
- package/dist/global-grid-systems/s2-geometry/s2-to-boundary.d.ts.map +1 -0
- package/dist/global-grid-systems/s2-geometry/s2-to-boundary.js +60 -0
- package/dist/global-grid-systems/s2-geometry/s2-to-boundary.js.map +1 -0
- package/dist/global-grid-systems/s2-geometry/s2-token.d.ts +13 -0
- package/dist/global-grid-systems/s2-geometry/s2-token.d.ts.map +1 -0
- package/dist/global-grid-systems/s2-geometry/s2-token.js +61 -0
- package/dist/global-grid-systems/s2-geometry/s2-token.js.map +1 -0
- package/dist/global-grid-systems/utils/geometry-utils.d.ts +4 -0
- package/dist/global-grid-systems/utils/geometry-utils.d.ts.map +1 -0
- package/dist/global-grid-systems/utils/geometry-utils.js +26 -0
- package/dist/global-grid-systems/utils/geometry-utils.js.map +1 -0
- package/dist/global-grid-systems/utils/h3-utils.d.ts +5 -0
- package/dist/global-grid-systems/utils/h3-utils.d.ts.map +1 -0
- package/dist/global-grid-systems/utils/h3-utils.js +41 -0
- package/dist/global-grid-systems/utils/h3-utils.js.map +1 -0
- package/dist/global-grid-systems/utils/hex-utils.d.ts +8 -0
- package/dist/global-grid-systems/utils/hex-utils.d.ts.map +1 -0
- package/dist/global-grid-systems/utils/hex-utils.js +34 -0
- package/dist/global-grid-systems/utils/hex-utils.js.map +1 -0
- package/dist/index.cjs +640 -0
- package/dist/index.cjs.map +7 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +11 -0
- package/dist/index.js.map +1 -0
- package/dist/tile-source-layer/tile-source-layer.d.ts +45 -0
- package/dist/tile-source-layer/tile-source-layer.d.ts.map +1 -0
- package/dist/tile-source-layer/tile-source-layer.js +110 -0
- package/dist/tile-source-layer/tile-source-layer.js.map +1 -0
- package/package.json +53 -0
- package/src/global-grid-layer/global-grid-cluster-layer.ts +93 -0
- package/src/global-grid-layer/global-grid-layer.ts +54 -0
- package/src/global-grid-systems/grids/a5-grid.ts +38 -0
- package/src/global-grid-systems/grids/geohash-grid.ts +105 -0
- package/src/global-grid-systems/grids/global-grid.ts +40 -0
- package/src/global-grid-systems/grids/h3-grid.ts +45 -0
- package/src/global-grid-systems/grids/quadkey-grid.ts +190 -0
- package/src/global-grid-systems/grids/s2-grid.ts +86 -0
- package/src/global-grid-systems/h3-js-bigint/h3-js-bigint.ts +85 -0
- package/src/global-grid-systems/s2-geometry/s2-geometry.ts +180 -0
- package/src/global-grid-systems/s2-geometry/s2-to-boundary.ts +76 -0
- package/src/global-grid-systems/s2-geometry/s2-token.ts +70 -0
- package/src/global-grid-systems/utils/geometry-utils.ts +28 -0
- package/src/global-grid-systems/utils/h3-utils.ts +52 -0
- package/src/global-grid-systems/utils/hex-utils.ts +37 -0
- package/src/index.ts +15 -0
- package/src/tile-source-layer/tile-source-layer.ts +156 -0
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
// math.gl
|
|
2
|
+
// SPDX-License-Identifier: MIT
|
|
3
|
+
// Copyright (c) vis.gl contributors
|
|
4
|
+
|
|
5
|
+
import {type Bounds2D} from '@math.gl/types';
|
|
6
|
+
import {type GlobalGrid} from './global-grid';
|
|
7
|
+
|
|
8
|
+
/** Decoder for the geohash global grid system */
|
|
9
|
+
export const GeohashGrid = {
|
|
10
|
+
name: 'geohash',
|
|
11
|
+
hasNumericRepresentation: false,
|
|
12
|
+
|
|
13
|
+
cellToLngLat: (geohash: string | bigint): [number, number] =>
|
|
14
|
+
getGeohashLngLat(getString(geohash)),
|
|
15
|
+
cellToBoundary: (geohash: string | bigint): [number, number][] =>
|
|
16
|
+
getGeohashBoundary(getString(geohash))
|
|
17
|
+
// cellToBoundaryPolygonFlat: (geohash: bigint): number[] => getGeohashBoundaryFlat(geohash),
|
|
18
|
+
// cellToBounds: (geohash: bigint): Bounds2D => getGeohashBounds(geohash)
|
|
19
|
+
} as const satisfies GlobalGrid;
|
|
20
|
+
|
|
21
|
+
const getString = (geohash: string | bigint): string => {
|
|
22
|
+
if (typeof geohash !== 'string') {
|
|
23
|
+
throw new Error('geohash must be a string');
|
|
24
|
+
}
|
|
25
|
+
return geohash;
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
const BASE32_CODES = '0123456789bcdefghjkmnpqrstuvwxyz';
|
|
29
|
+
const BASE32_CODES_DICT: Record<string, number> = {};
|
|
30
|
+
for (let i = 0; i < BASE32_CODES.length; i++) {
|
|
31
|
+
BASE32_CODES_DICT[BASE32_CODES.charAt(i)] = i;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const MIN_LAT = -90;
|
|
35
|
+
const MAX_LAT = 90;
|
|
36
|
+
const MIN_LON = -180;
|
|
37
|
+
const MAX_LON = 180;
|
|
38
|
+
|
|
39
|
+
/** Return center lng,lat of geohash cell */
|
|
40
|
+
function getGeohashLngLat(geohash: string): [number, number] {
|
|
41
|
+
const [[s, w], [n, e]] = getGeohashBounds(geohash);
|
|
42
|
+
return [(e + w) / 2, (n + s) / 2];
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/** Return boundary polygon of geohash cell as lng,lat array */
|
|
46
|
+
function getGeohashBoundary(geohash: string): [number, number][] {
|
|
47
|
+
const [[s, w], [n, e]] = getGeohashBounds(geohash);
|
|
48
|
+
return [
|
|
49
|
+
[e, n],
|
|
50
|
+
[e, s],
|
|
51
|
+
[w, s],
|
|
52
|
+
[w, n],
|
|
53
|
+
[e, n]
|
|
54
|
+
];
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/** Return boundary polygon of geohash cell as flat array */
|
|
58
|
+
export function getGeohashBoundaryFlat(geohash: string): number[] {
|
|
59
|
+
const [[s, w], [n, e]] = getGeohashBounds(geohash);
|
|
60
|
+
return [e, n, e, s, w, s, w, n, e, n];
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* @note Adapted from ngeohash decode_bbox
|
|
65
|
+
*/
|
|
66
|
+
/* eslint-disable max-depth */
|
|
67
|
+
export function getGeohashBounds(geohash: string): Bounds2D {
|
|
68
|
+
let isLon = true;
|
|
69
|
+
let maxLat = MAX_LAT;
|
|
70
|
+
let minLat = MIN_LAT;
|
|
71
|
+
let maxLon = MAX_LON;
|
|
72
|
+
let minLon = MIN_LON;
|
|
73
|
+
let mid: number;
|
|
74
|
+
|
|
75
|
+
let hashValue = 0;
|
|
76
|
+
for (let i = 0, l = geohash.length; i < l; i++) {
|
|
77
|
+
const code = geohash[i].toLowerCase();
|
|
78
|
+
hashValue = BASE32_CODES_DICT[code];
|
|
79
|
+
|
|
80
|
+
for (let bits = 4; bits >= 0; bits--) {
|
|
81
|
+
const bit = (hashValue >> bits) & 1;
|
|
82
|
+
if (isLon) {
|
|
83
|
+
mid = (maxLon + minLon) / 2;
|
|
84
|
+
if (bit === 1) {
|
|
85
|
+
minLon = mid;
|
|
86
|
+
} else {
|
|
87
|
+
maxLon = mid;
|
|
88
|
+
}
|
|
89
|
+
} else {
|
|
90
|
+
mid = (maxLat + minLat) / 2;
|
|
91
|
+
if (bit === 1) {
|
|
92
|
+
minLat = mid;
|
|
93
|
+
} else {
|
|
94
|
+
maxLat = mid;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
isLon = !isLon;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
return [
|
|
102
|
+
[minLat, minLon],
|
|
103
|
+
[maxLat, maxLon]
|
|
104
|
+
];
|
|
105
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
// deck.gl
|
|
2
|
+
// SPDX-License-Identifier: MIT
|
|
3
|
+
// Copyright (c) vis.gl contributors
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Copy of same type in math.gl. They are structurally identical.
|
|
7
|
+
* This type declares a "standardized" API for accessing basic DGGS token decoding
|
|
8
|
+
* DGGS modules export objects that satisfy this "interface",
|
|
9
|
+
* allowing different DGGS systems can be used interchangeably.
|
|
10
|
+
*/
|
|
11
|
+
export type GlobalGrid = {
|
|
12
|
+
/** The name of the DGGS */
|
|
13
|
+
name: string;
|
|
14
|
+
|
|
15
|
+
/** Does this grid have a numeric representation of cells? */
|
|
16
|
+
hasNumericRepresentation: boolean;
|
|
17
|
+
|
|
18
|
+
/** Initialization function. Should be idempotent, i.e. it may be called multiple times */
|
|
19
|
+
initialize?: () => void;
|
|
20
|
+
|
|
21
|
+
/** Is the argument a valid index in this grid? */
|
|
22
|
+
isValidCell?: (cell: string | bigint) => boolean;
|
|
23
|
+
|
|
24
|
+
/** Convert a string token to a binary cell index */
|
|
25
|
+
tokenToCell?: (token: string) => bigint;
|
|
26
|
+
/** Convert a binary cell index to a token */
|
|
27
|
+
cellToToken?: (cell: string | bigint) => string;
|
|
28
|
+
|
|
29
|
+
/** Convert a long, lat to a cell */
|
|
30
|
+
lngLatToCell?: (lngLat: [number, number], resolution) => bigint;
|
|
31
|
+
/** Convert a long, lat to a string token (H3 index) */
|
|
32
|
+
lngLatToToken?: (lngLat: [number, number], resolution) => string;
|
|
33
|
+
|
|
34
|
+
/** @returns the center of the cell */
|
|
35
|
+
cellToLngLat: (cell: string | bigint) => [number, number];
|
|
36
|
+
/** @returns the boundary of the cell, as an array of coordinate arrays */
|
|
37
|
+
cellToBoundary: (cell: string | bigint) => [number, number][];
|
|
38
|
+
/** @returns the bounds of the cell */
|
|
39
|
+
cellsToBoundaryMultiPolygon?: (cells: string[] | bigint[]) => [number, number][][][];
|
|
40
|
+
};
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
// deck.gl
|
|
2
|
+
// SPDX-License-Identifier: MIT
|
|
3
|
+
// Copyright (c) vis.gl contributors
|
|
4
|
+
|
|
5
|
+
import {type GlobalGrid} from './global-grid';
|
|
6
|
+
import {
|
|
7
|
+
latLngToCell,
|
|
8
|
+
cellToBoundary,
|
|
9
|
+
cellToLatLng,
|
|
10
|
+
cellsToMultiPolygon,
|
|
11
|
+
h3IndexToBigInt
|
|
12
|
+
} from '../h3-js-bigint/h3-js-bigint';
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* "Standardized" API for working with H3 DGGS tokens.
|
|
16
|
+
* @note Inspired by similar work in math.gl. May replace by an import in the future.
|
|
17
|
+
*/
|
|
18
|
+
export const H3Grid = {
|
|
19
|
+
name: 'H3',
|
|
20
|
+
hasNumericRepresentation: true,
|
|
21
|
+
|
|
22
|
+
// See `main/bundle.ts`, installs a check for the H3 library.
|
|
23
|
+
initialize: () => {},
|
|
24
|
+
|
|
25
|
+
tokenToCell: (token: string) => h3IndexToBigInt(token),
|
|
26
|
+
|
|
27
|
+
lngLatToToken: (lngLat: [number, number], resolution: number) =>
|
|
28
|
+
latLngToCell(lngLat[1], lngLat[0], resolution),
|
|
29
|
+
lngLatToCell: (lngLat: [number, number], resolution: number) =>
|
|
30
|
+
h3IndexToBigInt(latLngToCell(lngLat[1], lngLat[0], resolution)),
|
|
31
|
+
|
|
32
|
+
cellToLngLat: (cell: string | bigint) => reverseLatLngInPlace(cellToLatLng(cell)),
|
|
33
|
+
cellToBoundary: (cell: string | bigint) => cellToBoundary(cell),
|
|
34
|
+
cellsToBoundaryMultiPolygon: (cells: string[] | bigint[]) => cellsToMultiPolygon(cells, true)
|
|
35
|
+
} as const satisfies GlobalGrid;
|
|
36
|
+
|
|
37
|
+
// HELPERS
|
|
38
|
+
|
|
39
|
+
/** Reverse the latitude and longitude in place to avoid minting new arrays. */
|
|
40
|
+
function reverseLatLngInPlace(latLng: [number, number]): [number, number] {
|
|
41
|
+
const temp = latLng[0];
|
|
42
|
+
latLng[0] = latLng[1];
|
|
43
|
+
latLng[1] = temp;
|
|
44
|
+
return latLng;
|
|
45
|
+
}
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
// math.gl
|
|
2
|
+
// SPDX-License-Identifier: MIT
|
|
3
|
+
// Copyright (c) vis.gl contributors
|
|
4
|
+
|
|
5
|
+
import {type Bounds2D} from '@math.gl/types';
|
|
6
|
+
import {type GlobalGrid} from './global-grid';
|
|
7
|
+
|
|
8
|
+
const TILE_SIZE = 512;
|
|
9
|
+
|
|
10
|
+
/** Decoder for the quadkey DGGS */
|
|
11
|
+
export const QuadkeyGrid = {
|
|
12
|
+
name: 'quadkey',
|
|
13
|
+
hasNumericRepresentation: true,
|
|
14
|
+
|
|
15
|
+
// cellToToken: (cell: bigint): string => quadkeyCellToToken(cell),
|
|
16
|
+
// tokenToCell: (cell: string): bigint => quadKeyToBigint(cell),
|
|
17
|
+
|
|
18
|
+
cellToLngLat: (cell: string | bigint): [number, number] => quadkeyCellToLngLat(getString(cell)),
|
|
19
|
+
cellToBoundary: (cell: string | bigint): [number, number][] =>
|
|
20
|
+
quadkeyCellToBoundary(getString(cell))
|
|
21
|
+
|
|
22
|
+
// cellToBoundaryPolygonFlat: (cell: string | bigint): number[] => getQuadkeyBoundaryFlat(cell),
|
|
23
|
+
// cellToBounds: (cell: string | bigint): Bounds2D => getQuadkeyBounds(cell)
|
|
24
|
+
} as const satisfies GlobalGrid;
|
|
25
|
+
|
|
26
|
+
/** Quadkey only supports strings */
|
|
27
|
+
const getString = (cell: string | bigint): string => {
|
|
28
|
+
if (typeof cell !== 'string') {
|
|
29
|
+
throw new Error('geohash must be a string');
|
|
30
|
+
}
|
|
31
|
+
return cell;
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
function quadkeyCellToLngLat(quadkey: string): [number, number] {
|
|
35
|
+
const [topLeft, bottomRight] = quadkeyToWorldBounds(quadkey);
|
|
36
|
+
const [w, n] = worldToLngLat(topLeft);
|
|
37
|
+
const [e, s] = worldToLngLat(bottomRight);
|
|
38
|
+
return [(e + w) / 2, (s + n) / 2];
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function quadkeyCellToBoundary(quadkey: string): [number, number][] {
|
|
42
|
+
const [topLeft, bottomRight] = quadkeyToWorldBounds(quadkey);
|
|
43
|
+
const [w, n] = worldToLngLat(topLeft);
|
|
44
|
+
const [e, s] = worldToLngLat(bottomRight);
|
|
45
|
+
return [
|
|
46
|
+
[e, n],
|
|
47
|
+
[e, s],
|
|
48
|
+
[w, s],
|
|
49
|
+
[w, n],
|
|
50
|
+
[e, n]
|
|
51
|
+
];
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export function quadkeyCellToBoundaryFlat(quadkey: string): number[] {
|
|
55
|
+
const [topLeft, bottomRight] = quadkeyToWorldBounds(quadkey);
|
|
56
|
+
const [w, n] = worldToLngLat(topLeft);
|
|
57
|
+
const [e, s] = worldToLngLat(bottomRight);
|
|
58
|
+
return [e, n, e, s, w, s, w, n, e, n];
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export function quadkeyCellToBounds(quadkey: string): Bounds2D {
|
|
62
|
+
const [topLeft, bottomRight] = quadkeyToWorldBounds(quadkey);
|
|
63
|
+
const [w, n] = worldToLngLat(topLeft);
|
|
64
|
+
const [e, s] = worldToLngLat(bottomRight);
|
|
65
|
+
return [
|
|
66
|
+
[w, s],
|
|
67
|
+
[e, n]
|
|
68
|
+
];
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export function quadkeyToWorldBounds(quadkey: string): [[number, number], [number, number]] {
|
|
72
|
+
let x = 0;
|
|
73
|
+
let y = 0;
|
|
74
|
+
let mask = 1 << quadkey.length;
|
|
75
|
+
const scale = mask / TILE_SIZE;
|
|
76
|
+
|
|
77
|
+
for (let i = 0; i < quadkey.length; i++) {
|
|
78
|
+
mask >>= 1;
|
|
79
|
+
const q = parseInt(quadkey[i], 10);
|
|
80
|
+
if (q % 2) x |= mask;
|
|
81
|
+
if (q > 1) y |= mask;
|
|
82
|
+
}
|
|
83
|
+
return [
|
|
84
|
+
[x / scale, TILE_SIZE - y / scale],
|
|
85
|
+
[(x + 0.99) / scale, TILE_SIZE - (y + 0.99) / scale]
|
|
86
|
+
];
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// CONSTANTS
|
|
90
|
+
const PI = Math.PI;
|
|
91
|
+
const PI_4 = PI / 4;
|
|
92
|
+
const RADIANS_TO_DEGREES = 180 / PI;
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Unproject world point [x,y] on map onto {lat, lon} on sphere
|
|
96
|
+
*
|
|
97
|
+
* @param xy - array with [x,y] members
|
|
98
|
+
* representing point on projected map plane
|
|
99
|
+
* @return - array with [x,y] of point on sphere.
|
|
100
|
+
* Has toArray method if you need a GeoJSON Array.
|
|
101
|
+
* Per cartographic tradition, lat and lon are specified as degrees.
|
|
102
|
+
*/
|
|
103
|
+
function worldToLngLat(xy: number[]): [number, number] {
|
|
104
|
+
const [x, y] = xy;
|
|
105
|
+
const lambda2 = (x / TILE_SIZE) * (2 * PI) - PI;
|
|
106
|
+
const phi2 = 2 * (Math.atan(Math.exp((y / TILE_SIZE) * (2 * PI) - PI)) - PI_4);
|
|
107
|
+
return [lambda2 * RADIANS_TO_DEGREES, phi2 * RADIANS_TO_DEGREES];
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// EXPERIMENTAL BIGINT ENCODING FOR QUADKEYS
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Encodes a QuadKey string into a bigint.
|
|
114
|
+
* @param quadkey - The QuadKey string.
|
|
115
|
+
* @returns The bigint representation of the QuadKey.
|
|
116
|
+
*/
|
|
117
|
+
export function quadKeyToBigint(quadkey: string): bigint {
|
|
118
|
+
let result = 0n;
|
|
119
|
+
for (const digit of quadkey) {
|
|
120
|
+
const value = BigInt(parseInt(digit, 4));
|
|
121
|
+
result = (result << 2n) | value;
|
|
122
|
+
}
|
|
123
|
+
const zoom = quadkey.length;
|
|
124
|
+
// Shift the result to align with the most significant bits
|
|
125
|
+
const shift = 64 - 5 - zoom * 2; // Reserve 5 bits for zoom level
|
|
126
|
+
result = result << BigInt(shift);
|
|
127
|
+
// Append the zoom level in the least significant 5 bits
|
|
128
|
+
return result | BigInt(zoom);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Decodes a bigint-encoded QuadKey back into its string representation.
|
|
133
|
+
* @param encoded - The bigint representation of the QuadKey with embedded zoom level.
|
|
134
|
+
* @returns The decoded QuadKey string.
|
|
135
|
+
*/
|
|
136
|
+
export function bigintToQuadKey(encoded: bigint): string {
|
|
137
|
+
const zoom = Number(encoded & 0b11111n); // Extract the last 5 bits for zoom level
|
|
138
|
+
const shift = BigInt(64 - 5 - zoom * 2); // Calculate the shift to align the QuadKey bits
|
|
139
|
+
const quadkeyBits = (encoded >> shift) & ((1n << BigInt(zoom * 2)) - 1n); // Extract QuadKey bits
|
|
140
|
+
let quadkey = '';
|
|
141
|
+
for (let i = zoom - 1; i >= 0; i--) {
|
|
142
|
+
const digit = Number((quadkeyBits >> BigInt(i * 2)) & 0b11n);
|
|
143
|
+
quadkey += digit.toString();
|
|
144
|
+
}
|
|
145
|
+
return quadkey;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Checks if the child QuadKey is contained within the parent QuadKey.
|
|
150
|
+
* @param parent - The bigint representation of the parent QuadKey.
|
|
151
|
+
* @param child - The bigint representation of the child QuadKey.
|
|
152
|
+
* @returns True if the child is contained within the parent; otherwise, false.
|
|
153
|
+
*/
|
|
154
|
+
export function isContained(parent: bigint, child: bigint): boolean {
|
|
155
|
+
const parentZoom = Number(parent & 0b11111n); // Extract zoom level from parent
|
|
156
|
+
const childZoom = Number(child & 0b11111n); // Extract zoom level from child
|
|
157
|
+
|
|
158
|
+
if (parentZoom >= childZoom) return false;
|
|
159
|
+
|
|
160
|
+
const parentShift = BigInt(64 - 5 - parentZoom * 2);
|
|
161
|
+
const childShift = BigInt(64 - 5 - childZoom * 2);
|
|
162
|
+
|
|
163
|
+
const parentPrefix = (parent >> parentShift) & ((1n << BigInt(parentZoom * 2)) - 1n);
|
|
164
|
+
const childPrefix = (child >> childShift) & ((1n << BigInt(parentZoom * 2)) - 1n);
|
|
165
|
+
|
|
166
|
+
return parentPrefix === childPrefix;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Decodes a bigint-encoded QuadKey into tile X, tile Y, and zoom level.
|
|
171
|
+
* @param encoded - The bigint representation of the QuadKey with embedded zoom level.
|
|
172
|
+
* @returns An object containing tileX, tileY, and zoom.
|
|
173
|
+
*/
|
|
174
|
+
export function decodeBigintQuadKey(encoded: bigint): {tileX: number; tileY: number; zoom: number} {
|
|
175
|
+
const zoom = Number(encoded & 0b11111n); // Extract the last 5 bits for zoom level
|
|
176
|
+
const quadKeyBits = encoded >> 5n; // Remove the zoom level bits
|
|
177
|
+
|
|
178
|
+
let tileX = 0;
|
|
179
|
+
let tileY = 0;
|
|
180
|
+
|
|
181
|
+
for (let i = 0; i < zoom; i++) {
|
|
182
|
+
const shift = BigInt((zoom - i - 1) * 2);
|
|
183
|
+
const digit = Number((quadKeyBits >> shift) & 0b11n);
|
|
184
|
+
const mask = 1 << (zoom - i - 1);
|
|
185
|
+
if (digit & 1) tileX |= mask;
|
|
186
|
+
if (digit & 2) tileY |= mask;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
return {tileX, tileY, zoom};
|
|
190
|
+
}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
// math.gl
|
|
2
|
+
// SPDX-License-Identifier: MIT
|
|
3
|
+
// Copyright (c) vis.gl contributors
|
|
4
|
+
|
|
5
|
+
import {type Bounds2D} from '@math.gl/types';
|
|
6
|
+
import {type GlobalGrid} from './global-grid';
|
|
7
|
+
|
|
8
|
+
import {getS2IndexFromToken, getS2TokenFromIndex} from '../s2-geometry/s2-token';
|
|
9
|
+
import {getS2GeoBounds} from '../s2-geometry/s2-to-boundary';
|
|
10
|
+
import {getS2Cell, IJToST, STToUV, FaceUVToXYZ, XYZToLngLat} from '../s2-geometry/s2-geometry';
|
|
11
|
+
|
|
12
|
+
/** Decoder for the S2 DGGS */
|
|
13
|
+
export const S2Grid = {
|
|
14
|
+
name: 'S2',
|
|
15
|
+
hasNumericRepresentation: true,
|
|
16
|
+
|
|
17
|
+
tokenToCell: (cell: string): bigint => getS2IndexFromToken(cell),
|
|
18
|
+
cellToToken: (cell: string | bigint): string => getS2TokenFromIndex(getBigInt(cell)),
|
|
19
|
+
cellToLngLat: (cell: string | bigint): [number, number] => getS2LngLat(getBigInt(cell)),
|
|
20
|
+
cellToBoundary: (cell: string | bigint): [number, number][] => getS2Boundary(getBigInt(cell))
|
|
21
|
+
|
|
22
|
+
// cellToBoundaryPolygonFlat: (cell: bigint): number[] => getS2BoundaryFlat(cell),
|
|
23
|
+
// cellToBounds: (cell: bigint): Bounds2D => getS2Bounds(cell)
|
|
24
|
+
} as const satisfies GlobalGrid;
|
|
25
|
+
|
|
26
|
+
/** S2 functions operate on bigints */
|
|
27
|
+
const getBigInt = (value: string | bigint): bigint => {
|
|
28
|
+
return typeof value === 'string' ? getS2IndexFromToken(value) : value;
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Retrieve S2 geometry center
|
|
33
|
+
*/
|
|
34
|
+
export function getS2LngLat(cell: bigint): [number, number] {
|
|
35
|
+
const s2Cell = getS2Cell(cell);
|
|
36
|
+
|
|
37
|
+
const st = IJToST(s2Cell.ij, s2Cell.level, [0.5, 0.5]);
|
|
38
|
+
const uv = STToUV(st);
|
|
39
|
+
const xyz = FaceUVToXYZ(s2Cell.face, uv);
|
|
40
|
+
const lngLat = XYZToLngLat(xyz);
|
|
41
|
+
|
|
42
|
+
return lngLat;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Get a polygon with corner coordinates for an s2 cell
|
|
47
|
+
* @param - This can be an S2 key or token
|
|
48
|
+
* @return {Float64Array} - a simple polygon in flat array format: [lng0, lat0, lng1, lat1, ...]
|
|
49
|
+
* - the polygon is closed, i.e. last coordinate is a copy of the first coordinate
|
|
50
|
+
*/
|
|
51
|
+
function getS2BoundaryFlat(cell: bigint): number[] {
|
|
52
|
+
const float64Array = getS2GeoBounds(cell);
|
|
53
|
+
// TODO - inefficient
|
|
54
|
+
return Array.from(float64Array);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function getS2Boundary(cell: bigint): [number, number][] {
|
|
58
|
+
const flatBoundary = getS2BoundaryFlat(cell);
|
|
59
|
+
const boundary: [number, number][] = [];
|
|
60
|
+
for (let i = 0; i < flatBoundary.length; i += 2) {
|
|
61
|
+
boundary.push([flatBoundary[i], flatBoundary[i + 1]]);
|
|
62
|
+
}
|
|
63
|
+
return boundary;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export function getS2Bounds(cell: bigint): Bounds2D {
|
|
67
|
+
const flatBoundary = getS2Boundary(cell);
|
|
68
|
+
|
|
69
|
+
// We know that we have at least one point, no Infinity will be returned.
|
|
70
|
+
let xmin = Infinity;
|
|
71
|
+
let ymin = Infinity;
|
|
72
|
+
let xmax = -Infinity;
|
|
73
|
+
let ymax = -Infinity;
|
|
74
|
+
|
|
75
|
+
for (const [x, y] of flatBoundary) {
|
|
76
|
+
if (x < xmin) xmin = x;
|
|
77
|
+
if (x > xmax) xmax = x;
|
|
78
|
+
if (y < ymin) ymin = y;
|
|
79
|
+
if (y > ymax) ymax = y;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
return [
|
|
83
|
+
[xmin, ymin],
|
|
84
|
+
[xmax, ymax]
|
|
85
|
+
];
|
|
86
|
+
}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
// deck.gl
|
|
2
|
+
// SPDX-License-Identifier: MIT
|
|
3
|
+
// Copyright (c) vis.gl contributors
|
|
4
|
+
|
|
5
|
+
// A thin wrapper around h3-js that support BigInts
|
|
6
|
+
// Note: incurs some overhead as we need to convert between BigInt and SplitLong
|
|
7
|
+
|
|
8
|
+
import {
|
|
9
|
+
type CoordPair,
|
|
10
|
+
type SplitLong,
|
|
11
|
+
latLngToCell,
|
|
12
|
+
cellToBoundary as _cellToBoundary,
|
|
13
|
+
cellToLatLng as _cellToLatLng,
|
|
14
|
+
cellsToMultiPolygon as _cellsToMultiPolygon,
|
|
15
|
+
h3IndexToSplitLong as _h3IndexToSplitLong
|
|
16
|
+
// splitLongToH3Index as _splitLongToH3Index
|
|
17
|
+
} from 'h3-js';
|
|
18
|
+
|
|
19
|
+
export {type CoordPair, type SplitLong, latLngToCell};
|
|
20
|
+
|
|
21
|
+
export type H3IndexInput = bigint | string | SplitLong;
|
|
22
|
+
|
|
23
|
+
const scratchSplitLong: SplitLong = [0, 0];
|
|
24
|
+
|
|
25
|
+
export function h3IndexToSplitLong(h3Index: H3IndexInput, target?: SplitLong): SplitLong {
|
|
26
|
+
if (typeof h3Index === 'bigint') {
|
|
27
|
+
return bigIntToSplitLong(h3Index, target ?? [0, 0]);
|
|
28
|
+
}
|
|
29
|
+
return _h3IndexToSplitLong(h3Index);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function h3IndexToBigInt(h3Index: H3IndexInput): bigint {
|
|
33
|
+
if (typeof h3Index === 'bigint') {
|
|
34
|
+
return h3Index;
|
|
35
|
+
}
|
|
36
|
+
const splitLong = h3IndexToSplitLong(h3Index, scratchSplitLong);
|
|
37
|
+
return splitLongToBigInt(splitLong);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export function cellToLatLng(h3Index: H3IndexInput): [number, number] {
|
|
41
|
+
const splitLong = h3IndexToSplitLong(h3Index, scratchSplitLong);
|
|
42
|
+
const latLng = _cellToLatLng(splitLong);
|
|
43
|
+
return latLng;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export function cellToBoundary(h3Index: H3IndexInput, formatAsGeoJson?: boolean): CoordPair[] {
|
|
47
|
+
const splitLong = h3IndexToSplitLong(h3Index, scratchSplitLong);
|
|
48
|
+
const vertices = _cellToBoundary(splitLong, formatAsGeoJson);
|
|
49
|
+
return vertices;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export function cellsToMultiPolygon(
|
|
53
|
+
h3Indexes: H3IndexInput[],
|
|
54
|
+
formatAsGeoJson?: boolean
|
|
55
|
+
): CoordPair[][][] {
|
|
56
|
+
const splitLongs = h3Indexes.map((h3Index) => h3IndexToSplitLong(h3Index));
|
|
57
|
+
const polygons = _cellsToMultiPolygon(splitLongs, formatAsGeoJson);
|
|
58
|
+
return polygons;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// HELPERS
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Splits a bigint into a 2-element array of 32-bit numbers.
|
|
65
|
+
* @param value - The bigint to split.
|
|
66
|
+
* @returns A 2-element array containing the high and low 32-bit numbers.
|
|
67
|
+
*/
|
|
68
|
+
function bigIntToSplitLong(value: bigint, target: SplitLong = [0, 0]): SplitLong {
|
|
69
|
+
// Mask lower 32 bits
|
|
70
|
+
target[0] = Number(value & 0xffffffffn); // BigInt & BigInt ⇒ BigInt :contentReference[oaicite:2]{index=2}
|
|
71
|
+
// Shift right to get upper 32 bits
|
|
72
|
+
target[1] = Number((value >> 32n) & 0xffffffffn); // BigInt >> BigInt ⇒ BigInt :contentReference[oaicite:3]{index=3}
|
|
73
|
+
return target;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Joins two 32-bit numbers into a bigint.
|
|
78
|
+
* @param high - The high 32-bit number.
|
|
79
|
+
* @param low - The low 32-bit number.
|
|
80
|
+
* @returns The combined bigint.
|
|
81
|
+
*/
|
|
82
|
+
function splitLongToBigInt([low, high]: SplitLong): bigint {
|
|
83
|
+
// Shift high half back and OR with low half
|
|
84
|
+
return (BigInt(high) << 32n) | BigInt(low); // BigInt << BigInt ⇒ BigInt; BigInt | BigInt ⇒ BigInt :contentReference[oaicite:4]{index=4}
|
|
85
|
+
}
|