@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.
Files changed (90) hide show
  1. package/LICENSE +19 -0
  2. package/README.md +5 -0
  3. package/dist/global-grid-layer/global-grid-cluster-layer.d.ts +36 -0
  4. package/dist/global-grid-layer/global-grid-cluster-layer.d.ts.map +1 -0
  5. package/dist/global-grid-layer/global-grid-cluster-layer.js +55 -0
  6. package/dist/global-grid-layer/global-grid-cluster-layer.js.map +1 -0
  7. package/dist/global-grid-layer/global-grid-layer.d.ts +21 -0
  8. package/dist/global-grid-layer/global-grid-layer.d.ts.map +1 -0
  9. package/dist/global-grid-layer/global-grid-layer.js +33 -0
  10. package/dist/global-grid-layer/global-grid-layer.js.map +1 -0
  11. package/dist/global-grid-systems/grids/a5-grid.d.ts +18 -0
  12. package/dist/global-grid-systems/grids/a5-grid.d.ts.map +1 -0
  13. package/dist/global-grid-systems/grids/a5-grid.js +23 -0
  14. package/dist/global-grid-systems/grids/a5-grid.js.map +1 -0
  15. package/dist/global-grid-systems/grids/geohash-grid.d.ts +15 -0
  16. package/dist/global-grid-systems/grids/geohash-grid.d.ts.map +1 -0
  17. package/dist/global-grid-systems/grids/geohash-grid.js +92 -0
  18. package/dist/global-grid-systems/grids/geohash-grid.js.map +1 -0
  19. package/dist/global-grid-systems/grids/global-grid.d.ts +31 -0
  20. package/dist/global-grid-systems/grids/global-grid.d.ts.map +1 -0
  21. package/dist/global-grid-systems/grids/global-grid.js +5 -0
  22. package/dist/global-grid-systems/grids/global-grid.js.map +1 -0
  23. package/dist/global-grid-systems/grids/h3-grid.d.ts +16 -0
  24. package/dist/global-grid-systems/grids/h3-grid.d.ts.map +1 -0
  25. package/dist/global-grid-systems/grids/h3-grid.js +29 -0
  26. package/dist/global-grid-systems/grids/h3-grid.js.map +1 -0
  27. package/dist/global-grid-systems/grids/quadkey-grid.d.ts +41 -0
  28. package/dist/global-grid-systems/grids/quadkey-grid.d.ts.map +1 -0
  29. package/dist/global-grid-systems/grids/quadkey-grid.js +166 -0
  30. package/dist/global-grid-systems/grids/quadkey-grid.js.map +1 -0
  31. package/dist/global-grid-systems/grids/s2-grid.d.ts +16 -0
  32. package/dist/global-grid-systems/grids/s2-grid.d.ts.map +1 -0
  33. package/dist/global-grid-systems/grids/s2-grid.js +74 -0
  34. package/dist/global-grid-systems/grids/s2-grid.js.map +1 -0
  35. package/dist/global-grid-systems/h3-js-bigint/h3-js-bigint.d.ts +9 -0
  36. package/dist/global-grid-systems/h3-js-bigint/h3-js-bigint.d.ts.map +1 -0
  37. package/dist/global-grid-systems/h3-js-bigint/h3-js-bigint.js +62 -0
  38. package/dist/global-grid-systems/h3-js-bigint/h3-js-bigint.js.map +1 -0
  39. package/dist/global-grid-systems/s2-geometry/s2-geometry.d.ts +13 -0
  40. package/dist/global-grid-systems/s2-geometry/s2-geometry.d.ts.map +1 -0
  41. package/dist/global-grid-systems/s2-geometry/s2-geometry.js +139 -0
  42. package/dist/global-grid-systems/s2-geometry/s2-geometry.js.map +1 -0
  43. package/dist/global-grid-systems/s2-geometry/s2-to-boundary.d.ts +14 -0
  44. package/dist/global-grid-systems/s2-geometry/s2-to-boundary.d.ts.map +1 -0
  45. package/dist/global-grid-systems/s2-geometry/s2-to-boundary.js +60 -0
  46. package/dist/global-grid-systems/s2-geometry/s2-to-boundary.js.map +1 -0
  47. package/dist/global-grid-systems/s2-geometry/s2-token.d.ts +13 -0
  48. package/dist/global-grid-systems/s2-geometry/s2-token.d.ts.map +1 -0
  49. package/dist/global-grid-systems/s2-geometry/s2-token.js +61 -0
  50. package/dist/global-grid-systems/s2-geometry/s2-token.js.map +1 -0
  51. package/dist/global-grid-systems/utils/geometry-utils.d.ts +4 -0
  52. package/dist/global-grid-systems/utils/geometry-utils.d.ts.map +1 -0
  53. package/dist/global-grid-systems/utils/geometry-utils.js +26 -0
  54. package/dist/global-grid-systems/utils/geometry-utils.js.map +1 -0
  55. package/dist/global-grid-systems/utils/h3-utils.d.ts +5 -0
  56. package/dist/global-grid-systems/utils/h3-utils.d.ts.map +1 -0
  57. package/dist/global-grid-systems/utils/h3-utils.js +41 -0
  58. package/dist/global-grid-systems/utils/h3-utils.js.map +1 -0
  59. package/dist/global-grid-systems/utils/hex-utils.d.ts +8 -0
  60. package/dist/global-grid-systems/utils/hex-utils.d.ts.map +1 -0
  61. package/dist/global-grid-systems/utils/hex-utils.js +34 -0
  62. package/dist/global-grid-systems/utils/hex-utils.js.map +1 -0
  63. package/dist/index.cjs +640 -0
  64. package/dist/index.cjs.map +7 -0
  65. package/dist/index.d.ts +10 -0
  66. package/dist/index.d.ts.map +1 -0
  67. package/dist/index.js +11 -0
  68. package/dist/index.js.map +1 -0
  69. package/dist/tile-source-layer/tile-source-layer.d.ts +45 -0
  70. package/dist/tile-source-layer/tile-source-layer.d.ts.map +1 -0
  71. package/dist/tile-source-layer/tile-source-layer.js +110 -0
  72. package/dist/tile-source-layer/tile-source-layer.js.map +1 -0
  73. package/package.json +53 -0
  74. package/src/global-grid-layer/global-grid-cluster-layer.ts +93 -0
  75. package/src/global-grid-layer/global-grid-layer.ts +54 -0
  76. package/src/global-grid-systems/grids/a5-grid.ts +38 -0
  77. package/src/global-grid-systems/grids/geohash-grid.ts +105 -0
  78. package/src/global-grid-systems/grids/global-grid.ts +40 -0
  79. package/src/global-grid-systems/grids/h3-grid.ts +45 -0
  80. package/src/global-grid-systems/grids/quadkey-grid.ts +190 -0
  81. package/src/global-grid-systems/grids/s2-grid.ts +86 -0
  82. package/src/global-grid-systems/h3-js-bigint/h3-js-bigint.ts +85 -0
  83. package/src/global-grid-systems/s2-geometry/s2-geometry.ts +180 -0
  84. package/src/global-grid-systems/s2-geometry/s2-to-boundary.ts +76 -0
  85. package/src/global-grid-systems/s2-geometry/s2-token.ts +70 -0
  86. package/src/global-grid-systems/utils/geometry-utils.ts +28 -0
  87. package/src/global-grid-systems/utils/h3-utils.ts +52 -0
  88. package/src/global-grid-systems/utils/hex-utils.ts +37 -0
  89. package/src/index.ts +15 -0
  90. 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
+ }