@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,180 @@
|
|
|
1
|
+
// math.gl
|
|
2
|
+
// SPDX-License-Identifier: MIT and ISC
|
|
3
|
+
// Copyright (c) vis.gl contributors
|
|
4
|
+
|
|
5
|
+
// math.gl, MIT license
|
|
6
|
+
/*
|
|
7
|
+
Adapted from s2-geometry under ISC License (ISC)
|
|
8
|
+
Copyright (c) 2012-2016, Jon Atkins <github@jonatkins.com>
|
|
9
|
+
Copyright (c) 2016, AJ ONeal <aj@daplie.com>
|
|
10
|
+
Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies.
|
|
11
|
+
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
export type S2Cell = {
|
|
15
|
+
face: number;
|
|
16
|
+
ij: [number, number];
|
|
17
|
+
level: number;
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
export function getS2Cell(s2Index: bigint): S2Cell {
|
|
21
|
+
const key = toHilbertQuadkey(s2Index);
|
|
22
|
+
const s2cell = fromHilbertQuadKey(key);
|
|
23
|
+
return s2cell;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
//
|
|
27
|
+
// Functional Style
|
|
28
|
+
//
|
|
29
|
+
const FACE_BITS = 3;
|
|
30
|
+
const MAX_LEVEL = 30;
|
|
31
|
+
const POS_BITS = 2 * MAX_LEVEL + 1; // 61 (60 bits of data, 1 bit lsb marker)
|
|
32
|
+
const RADIAN_TO_DEGREE = 180 / Math.PI;
|
|
33
|
+
|
|
34
|
+
/*
|
|
35
|
+
Original function taken from deck.gl doesn't support the case of (face <= 5)
|
|
36
|
+
It's fixed here.
|
|
37
|
+
*/
|
|
38
|
+
export function fromHilbertQuadKey(hilbertQuadkey: string): S2Cell {
|
|
39
|
+
const parts = hilbertQuadkey.split('/');
|
|
40
|
+
const face = parseInt(parts[0], 10);
|
|
41
|
+
const position = parts[1];
|
|
42
|
+
/*
|
|
43
|
+
Fix for the case of level==0 that corresponds to (face <= 5)
|
|
44
|
+
const maxLevel = position.length;
|
|
45
|
+
let level;
|
|
46
|
+
*/
|
|
47
|
+
const maxLevel = face > 5 ? position.length : 0;
|
|
48
|
+
let level = 0;
|
|
49
|
+
|
|
50
|
+
const point = [0, 0] as [number, number];
|
|
51
|
+
|
|
52
|
+
for (let i = maxLevel - 1; i >= 0; i--) {
|
|
53
|
+
level = maxLevel - i;
|
|
54
|
+
const bit = position[i];
|
|
55
|
+
let rx = 0;
|
|
56
|
+
let ry = 0;
|
|
57
|
+
if (bit === '1') {
|
|
58
|
+
ry = 1;
|
|
59
|
+
} else if (bit === '2') {
|
|
60
|
+
rx = 1;
|
|
61
|
+
ry = 1;
|
|
62
|
+
} else if (bit === '3') {
|
|
63
|
+
rx = 1;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const val = Math.pow(2, level - 1);
|
|
67
|
+
rotateAndFlipQuadrant(val, point, rx, ry);
|
|
68
|
+
|
|
69
|
+
point[0] += val * rx;
|
|
70
|
+
point[1] += val * ry;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
if (face % 2 === 1) {
|
|
74
|
+
const t = point[0];
|
|
75
|
+
point[0] = point[1];
|
|
76
|
+
point[1] = t;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
return {face, ij: point, level};
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export function toHilbertQuadkey(id: bigint): string {
|
|
83
|
+
let bin = id.toString(2);
|
|
84
|
+
|
|
85
|
+
while (bin.length < FACE_BITS + POS_BITS) {
|
|
86
|
+
// eslint-disable-next-line prefer-template
|
|
87
|
+
bin = '0' + bin;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// MUST come AFTER binstr has been left-padded with '0's
|
|
91
|
+
const lsbIndex = bin.lastIndexOf('1');
|
|
92
|
+
// substr(start, len)
|
|
93
|
+
// substring(start, end) // includes start, does not include end
|
|
94
|
+
const faceB = bin.substring(0, 3);
|
|
95
|
+
// posB will always be a multiple of 2 (or it's invalid)
|
|
96
|
+
const posB = bin.substring(3, lsbIndex);
|
|
97
|
+
const levelN = posB.length / 2;
|
|
98
|
+
|
|
99
|
+
const faceS = BigInt(`0b${faceB}`).toString(10);
|
|
100
|
+
|
|
101
|
+
/*
|
|
102
|
+
Here is a fix for the case when posB is an empty string that causes an exception in Long.fromString
|
|
103
|
+
|
|
104
|
+
let posS = Long.fromString(posB, true, 2).toString(4);
|
|
105
|
+
*/
|
|
106
|
+
let posS = '0';
|
|
107
|
+
if (levelN !== 0) {
|
|
108
|
+
// posB is not an empty string< because levelN!==0
|
|
109
|
+
posS = BigInt(`0b${posB}`).toString(4);
|
|
110
|
+
|
|
111
|
+
while (posS.length < levelN) {
|
|
112
|
+
// eslint-disable-next-line prefer-template
|
|
113
|
+
posS = '0' + posS;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
// Note, posS will be "0" for the level==0
|
|
117
|
+
// TODO: Is it ok?
|
|
118
|
+
|
|
119
|
+
return `${faceS}/${posS}`;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
export function IJToST(
|
|
123
|
+
ij: [number, number],
|
|
124
|
+
order: number,
|
|
125
|
+
offsets: [number, number]
|
|
126
|
+
): [number, number] {
|
|
127
|
+
const maxSize = 1 << order;
|
|
128
|
+
|
|
129
|
+
return [(ij[0] + offsets[0]) / maxSize, (ij[1] + offsets[1]) / maxSize];
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
function singleSTtoUV(st: number): number {
|
|
133
|
+
if (st >= 0.5) {
|
|
134
|
+
return (1 / 3.0) * (4 * st * st - 1);
|
|
135
|
+
}
|
|
136
|
+
return (1 / 3.0) * (1 - 4 * (1 - st) * (1 - st));
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
export function STToUV(st: [number, number]): [number, number] {
|
|
140
|
+
return [singleSTtoUV(st[0]), singleSTtoUV(st[1])];
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
export function FaceUVToXYZ(face: number, [u, v]: [number, number]): [number, number, number] {
|
|
144
|
+
switch (face) {
|
|
145
|
+
case 0:
|
|
146
|
+
return [1, u, v];
|
|
147
|
+
case 1:
|
|
148
|
+
return [-u, 1, v];
|
|
149
|
+
case 2:
|
|
150
|
+
return [-u, -v, 1];
|
|
151
|
+
case 3:
|
|
152
|
+
return [-1, -v, -u];
|
|
153
|
+
case 4:
|
|
154
|
+
return [v, -1, -u];
|
|
155
|
+
case 5:
|
|
156
|
+
return [v, u, -1];
|
|
157
|
+
default:
|
|
158
|
+
throw new Error('Invalid face');
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
export function XYZToLngLat([x, y, z]: [number, number, number]): [number, number] {
|
|
163
|
+
const lat = Math.atan2(z, Math.sqrt(x * x + y * y));
|
|
164
|
+
const lng = Math.atan2(y, x);
|
|
165
|
+
|
|
166
|
+
return [lng * RADIAN_TO_DEGREE, lat * RADIAN_TO_DEGREE];
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
function rotateAndFlipQuadrant(n: number, point: [number, number], rx: number, ry: number): void {
|
|
170
|
+
if (ry === 0) {
|
|
171
|
+
if (rx === 1) {
|
|
172
|
+
point[0] = n - 1 - point[0];
|
|
173
|
+
point[1] = n - 1 - point[1];
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
const x = point[0];
|
|
177
|
+
point[0] = point[1];
|
|
178
|
+
point[1] = x;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
// math.gl
|
|
2
|
+
// SPDX-License-Identifier: MIT
|
|
3
|
+
// Copyright (c) vis.gl contributors
|
|
4
|
+
|
|
5
|
+
import {IJToST, STToUV, FaceUVToXYZ, XYZToLngLat, getS2Cell} from './s2-geometry';
|
|
6
|
+
|
|
7
|
+
const MAX_RESOLUTION = 100;
|
|
8
|
+
|
|
9
|
+
export function getS2GeoBounds(s2Index: bigint): Float64Array {
|
|
10
|
+
const s2Cell = getS2Cell(s2Index);
|
|
11
|
+
return getS2GeoBoundsFromCell(s2Cell);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* The S2 cell edge is curved: http://s2geometry.io/
|
|
16
|
+
* This is more prominent at lower levels
|
|
17
|
+
* resolution is the number of segments to generate per edge.
|
|
18
|
+
* We exponentially reduce resolution as level increases so it doesn't affect perf
|
|
19
|
+
* when there are a large number of cells
|
|
20
|
+
*/
|
|
21
|
+
// eslint-disable-next-line max-statements
|
|
22
|
+
export function getS2GeoBoundsFromCell({
|
|
23
|
+
face,
|
|
24
|
+
ij,
|
|
25
|
+
level
|
|
26
|
+
}: {
|
|
27
|
+
face: number;
|
|
28
|
+
ij: [number, number];
|
|
29
|
+
level: number;
|
|
30
|
+
}): Float64Array {
|
|
31
|
+
const offsets = [
|
|
32
|
+
[0, 0],
|
|
33
|
+
[0, 1],
|
|
34
|
+
[1, 1],
|
|
35
|
+
[1, 0],
|
|
36
|
+
[0, 0]
|
|
37
|
+
];
|
|
38
|
+
|
|
39
|
+
const resolution = Math.max(1, Math.ceil(MAX_RESOLUTION * Math.pow(2, -level)));
|
|
40
|
+
const result = new Float64Array(4 * resolution * 2 + 2);
|
|
41
|
+
let ptIndex = 0;
|
|
42
|
+
let prevLng = 0;
|
|
43
|
+
|
|
44
|
+
for (let i = 0; i < 4; i++) {
|
|
45
|
+
const offset = offsets[i].slice(0) as [number, number];
|
|
46
|
+
const nextOffset = offsets[i + 1];
|
|
47
|
+
const stepI = (nextOffset[0] - offset[0]) / resolution;
|
|
48
|
+
const stepJ = (nextOffset[1] - offset[1]) / resolution;
|
|
49
|
+
|
|
50
|
+
for (let j = 0; j < resolution; j++) {
|
|
51
|
+
offset[0] += stepI;
|
|
52
|
+
offset[1] += stepJ;
|
|
53
|
+
// Cell can be represented by coordinates IJ, ST, UV, XYZ
|
|
54
|
+
// http://s2geometry.io/devguide/s2cell_hierarchy#coordinate-systems
|
|
55
|
+
const st = IJToST(ij, level, offset);
|
|
56
|
+
const uv = STToUV(st);
|
|
57
|
+
const xyz = FaceUVToXYZ(face, uv);
|
|
58
|
+
const lngLat = XYZToLngLat(xyz);
|
|
59
|
+
|
|
60
|
+
// Adjust longitude for Web Mercator projection
|
|
61
|
+
if (Math.abs(lngLat[1]) > 89.999) {
|
|
62
|
+
lngLat[0] = prevLng;
|
|
63
|
+
}
|
|
64
|
+
const deltaLng = lngLat[0] - prevLng;
|
|
65
|
+
lngLat[0] += deltaLng > 180 ? -360 : deltaLng < -180 ? 360 : 0;
|
|
66
|
+
|
|
67
|
+
result[ptIndex++] = lngLat[0];
|
|
68
|
+
result[ptIndex++] = lngLat[1];
|
|
69
|
+
prevLng = lngLat[0];
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
// close the loop
|
|
73
|
+
result[ptIndex++] = result[0];
|
|
74
|
+
result[ptIndex++] = result[1];
|
|
75
|
+
return result;
|
|
76
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
// math.gl
|
|
2
|
+
// SPDX-License-Identifier: MIT and Apache-2.0
|
|
3
|
+
// Copyright (c) vis.gl contributors
|
|
4
|
+
|
|
5
|
+
// s2-geometry is a pure JavaScript port of Google/Niantic's S2 Geometry library
|
|
6
|
+
// which is perfect since it works in the browser.
|
|
7
|
+
|
|
8
|
+
// const MAXIMUM_TOKEN_LENGTH = 16;
|
|
9
|
+
|
|
10
|
+
// INDEX CALCULATIONS
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Given an S2 token (String) this function convert the token to 64 bit id (Index)
|
|
14
|
+
* 'X' is the empty cell
|
|
15
|
+
* https://github.com/google/s2-geometry-library-java/blob/c04b68bf3197a9c34082327eeb3aec7ab7c85da1/src/com/google/common/geometry/S2CellId.java#L439
|
|
16
|
+
*/
|
|
17
|
+
export function getS2IndexFromToken(token: string): bigint {
|
|
18
|
+
if (token === 'X') {
|
|
19
|
+
token = '';
|
|
20
|
+
}
|
|
21
|
+
// pad token with zeros to make the length 16
|
|
22
|
+
const paddedToken = token.padEnd(16, '0');
|
|
23
|
+
return BigInt(`0x${paddedToken}`);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Convert a 64 bit number to a string token
|
|
28
|
+
* 'X' is the empty cell
|
|
29
|
+
*/
|
|
30
|
+
export function getS2TokenFromIndex(cellId: bigint): string {
|
|
31
|
+
if (cellId === 0n) {
|
|
32
|
+
return 'X';
|
|
33
|
+
}
|
|
34
|
+
let numZeroDigits = countTrailingZeros(cellId);
|
|
35
|
+
|
|
36
|
+
const remainder = numZeroDigits % 4;
|
|
37
|
+
numZeroDigits = (numZeroDigits - remainder) / 4;
|
|
38
|
+
const trailingZeroHexChars = numZeroDigits;
|
|
39
|
+
numZeroDigits *= 4;
|
|
40
|
+
|
|
41
|
+
const x = cellId >> BigInt(numZeroDigits);
|
|
42
|
+
const hexString = x.toString(16).replace(/0+$/, '');
|
|
43
|
+
const zeroString = Array(17 - trailingZeroHexChars - hexString.length).join('0');
|
|
44
|
+
return zeroString + hexString;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export function getS2ChildIndex(s2Index: bigint, index: number): bigint {
|
|
48
|
+
// Shift sentinel bit 2 positions to the right.
|
|
49
|
+
const newLsb = lsb(s2Index) >> 2n;
|
|
50
|
+
// Insert child index before the sentinel bit.
|
|
51
|
+
const childCellId: bigint = s2Index + BigInt(2 * index + 1 - 4) * newLsb;
|
|
52
|
+
return childCellId;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Return the lowest-numbered bit that is on for this cell id
|
|
57
|
+
* @private
|
|
58
|
+
*/
|
|
59
|
+
function lsb(cellId: bigint): bigint {
|
|
60
|
+
return cellId & (cellId + 1n); // eslint-disable-line
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function countTrailingZeros(n: bigint): number {
|
|
64
|
+
let count = 0;
|
|
65
|
+
while (n % 2n === 0n) {
|
|
66
|
+
n /= 2n;
|
|
67
|
+
count++;
|
|
68
|
+
}
|
|
69
|
+
return count;
|
|
70
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
// deck.gl
|
|
2
|
+
// SPDX-License-Identifier: MIT
|
|
3
|
+
// Copyright (c) vis.gl contributors
|
|
4
|
+
|
|
5
|
+
import {type CoordPair} from '../h3-js-bigint/h3-js-bigint';
|
|
6
|
+
|
|
7
|
+
// normalize longitudes w.r.t center (refLng), when not provided first vertex
|
|
8
|
+
export function normalizeLongitudes(vertices: CoordPair[], refLng?: number): void {
|
|
9
|
+
refLng = refLng === undefined ? vertices[0][0] : refLng;
|
|
10
|
+
for (const pt of vertices) {
|
|
11
|
+
const deltaLng = pt[0] - refLng;
|
|
12
|
+
if (deltaLng > 180) {
|
|
13
|
+
pt[0] -= 360;
|
|
14
|
+
} else if (deltaLng < -180) {
|
|
15
|
+
pt[0] += 360;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function flattenPolygon(vertices: number[][]): Float64Array {
|
|
21
|
+
const positions = new Float64Array(vertices.length * 2);
|
|
22
|
+
let i = 0;
|
|
23
|
+
for (const pt of vertices) {
|
|
24
|
+
positions[i++] = pt[0];
|
|
25
|
+
positions[i++] = pt[1];
|
|
26
|
+
}
|
|
27
|
+
return positions;
|
|
28
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
// deck.gl
|
|
2
|
+
// SPDX-License-Identifier: MIT
|
|
3
|
+
// Copyright (c) vis.gl contributors
|
|
4
|
+
|
|
5
|
+
import {lerp} from '@math.gl/core';
|
|
6
|
+
import {
|
|
7
|
+
type CoordPair,
|
|
8
|
+
type H3IndexInput, // Either string or SplitLong
|
|
9
|
+
cellToBoundary,
|
|
10
|
+
cellToLatLng
|
|
11
|
+
} from '../h3-js-bigint/h3-js-bigint';
|
|
12
|
+
import {normalizeLongitudes} from './geometry-utils';
|
|
13
|
+
|
|
14
|
+
// UTILITIES - TODO can these be generalized using the decoder?
|
|
15
|
+
|
|
16
|
+
// scale polygon vertices w.r.t center (hexId)
|
|
17
|
+
export function scalePolygon(hexId: H3IndexInput, vertices: CoordPair[], factor: number): void {
|
|
18
|
+
const [lat, lng] = cellToLatLng(hexId);
|
|
19
|
+
const actualCount = vertices.length;
|
|
20
|
+
|
|
21
|
+
// normalize with respect to center
|
|
22
|
+
normalizeLongitudes(vertices, lng);
|
|
23
|
+
|
|
24
|
+
// `cellToBoundary` returns same array object for first and last vertex (closed polygon),
|
|
25
|
+
// if so skip scaling the last vertex
|
|
26
|
+
const vertexCount = vertices[0] === vertices[actualCount - 1] ? actualCount - 1 : actualCount;
|
|
27
|
+
for (let i = 0; i < vertexCount; i++) {
|
|
28
|
+
vertices[i][0] = lerp(lng, vertices[i][0], factor);
|
|
29
|
+
vertices[i][1] = lerp(lat, vertices[i][1], factor);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// gets hexagon centroid
|
|
34
|
+
export function getHexagonCentroid(getHexagon, object, objectInfo) {
|
|
35
|
+
const hexagonId = getHexagon(object, objectInfo);
|
|
36
|
+
const [lat, lng] = cellToLatLng(hexagonId);
|
|
37
|
+
return [lng, lat];
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export function h3ToPolygon(hexId: H3IndexInput, coverage: number = 1): number[][] {
|
|
41
|
+
const vertices = cellToBoundary(hexId, true);
|
|
42
|
+
|
|
43
|
+
if (coverage !== 1) {
|
|
44
|
+
// scale and normalize vertices w.r.t to center
|
|
45
|
+
scalePolygon(hexId, vertices, coverage);
|
|
46
|
+
} else {
|
|
47
|
+
// normalize w.r.t to start vertex
|
|
48
|
+
normalizeLongitudes(vertices);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return vertices;
|
|
52
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Converts a hexadecimal string (with or without “0x” prefix) to a bigint.
|
|
3
|
+
* @param hex - The hex string representation (e.g., "0x1a2b", "FF", "00ff")
|
|
4
|
+
* @returns The corresponding bigint value.
|
|
5
|
+
* @throws {Error} If the input is not a valid hex string.
|
|
6
|
+
*/
|
|
7
|
+
export function hexToBigInt(hex: string): bigint {
|
|
8
|
+
if (typeof hex !== 'string') {
|
|
9
|
+
throw new Error(`hexToBigInt: expected string, got ${typeof hex}`);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
let s = hex.trim();
|
|
13
|
+
if (s.startsWith('0x') || s.startsWith('0X')) {
|
|
14
|
+
s = s.slice(2);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
if (s === '') {
|
|
18
|
+
throw new Error(`hexToBigInt: empty hex string`);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// validate: only hex digits
|
|
22
|
+
if (!/^[0-9A-Fa-f]+$/.test(s)) {
|
|
23
|
+
throw new Error(`hexToBigInt: invalid hex string “${hex}”`);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// Optional: normalize even length by prepending a zero if odd
|
|
27
|
+
if (s.length % 2 === 1) {
|
|
28
|
+
s = `0${ s}`;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// Use BigInt conversion from string with 0x prefix
|
|
32
|
+
try {
|
|
33
|
+
return BigInt(`0x${ s}`);
|
|
34
|
+
} catch (e) {
|
|
35
|
+
throw new Error(`hexToBigInt: cannot convert hex string “${hex}” to bigint: ${(e as Error).message}`);
|
|
36
|
+
}
|
|
37
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
// deck.gl-community
|
|
2
|
+
// SPDX-License-Identifier: MIT
|
|
3
|
+
// Copyright (c) vis.gl contributors
|
|
4
|
+
|
|
5
|
+
export type {TileSourceLayerProps} from './tile-source-layer/tile-source-layer';
|
|
6
|
+
export {TileSourceLayer} from './tile-source-layer/tile-source-layer';
|
|
7
|
+
|
|
8
|
+
export {GlobalGridLayer, type GlobalGridLayerProps} from './global-grid-layer/global-grid-layer';
|
|
9
|
+
|
|
10
|
+
export {type GlobalGrid} from './global-grid-systems/grids/global-grid';
|
|
11
|
+
export {A5Grid} from './global-grid-systems/grids/a5-grid';
|
|
12
|
+
export {H3Grid} from './global-grid-systems/grids/h3-grid';
|
|
13
|
+
export {S2Grid} from './global-grid-systems/grids/s2-grid';
|
|
14
|
+
export {GeohashGrid} from './global-grid-systems/grids/geohash-grid';
|
|
15
|
+
export {QuadkeyGrid} from './global-grid-systems/grids/quadkey-grid';
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
// deck.gl-community
|
|
2
|
+
// SPDX-License-Identifier: MIT
|
|
3
|
+
// Copyright (c) vis.gl contributors
|
|
4
|
+
|
|
5
|
+
import {CompositeLayer, Layer} from '@deck.gl/core';
|
|
6
|
+
import type {TileLayerProps} from '@deck.gl/geo-layers';
|
|
7
|
+
import {TileLayer} from '@deck.gl/geo-layers';
|
|
8
|
+
import {BitmapLayer, GeoJsonLayer, PathLayer} from '@deck.gl/layers';
|
|
9
|
+
import type {TileSource} from '@loaders.gl/loader-utils';
|
|
10
|
+
|
|
11
|
+
/* global window */
|
|
12
|
+
const devicePixelRatio = (typeof window !== 'undefined' && window.devicePixelRatio) || 1;
|
|
13
|
+
|
|
14
|
+
export type TileSourceLayerProps = TileLayerProps & {
|
|
15
|
+
tileSource: TileSource<any>;
|
|
16
|
+
showTileBorders?: boolean;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* A Deck.gl layer that renders a tile source
|
|
21
|
+
* Autodiscovers type of content (vector tile, bitmap, ...)
|
|
22
|
+
* Can render debug borders around tiles
|
|
23
|
+
* TODO - Change debug border color based on zoom level
|
|
24
|
+
*/
|
|
25
|
+
export class TileSourceLayer extends CompositeLayer<TileSourceLayerProps> {
|
|
26
|
+
static layerName = 'TileSourceLayer';
|
|
27
|
+
static defaultProps = {
|
|
28
|
+
...TileLayer.defaultProps,
|
|
29
|
+
showTileBorders: true
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
state: {
|
|
33
|
+
tileSource: TileSource<any> | null;
|
|
34
|
+
} = undefined!;
|
|
35
|
+
|
|
36
|
+
initializeState() {
|
|
37
|
+
this.setState({
|
|
38
|
+
tileSource: null
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
updateState({props, changeFlags}) {
|
|
43
|
+
this.setState({
|
|
44
|
+
tileSource: props.tileSource
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
renderLayers() {
|
|
49
|
+
const {tileSource, showTileBorders, metadata, onTilesLoad} = this.props as any;
|
|
50
|
+
const minZoom = metadata?.minZoom || 0;
|
|
51
|
+
const maxZoom = metadata?.maxZoom || 30;
|
|
52
|
+
|
|
53
|
+
return [
|
|
54
|
+
new TileLayer({
|
|
55
|
+
// HACK: Trigger new layer via id prop to force clear tile cache
|
|
56
|
+
id: String(tileSource.url),
|
|
57
|
+
getTileData: tileSource.getTileData,
|
|
58
|
+
// Assume the pmtiles file support HTTP/2, so we aren't limited by the browser to a certain number per domain.
|
|
59
|
+
maxRequests: 20,
|
|
60
|
+
|
|
61
|
+
pickable: true,
|
|
62
|
+
onViewportLoad: onTilesLoad,
|
|
63
|
+
autoHighlight: showTileBorders,
|
|
64
|
+
highlightColor: [60, 60, 60, 40],
|
|
65
|
+
minZoom,
|
|
66
|
+
maxZoom,
|
|
67
|
+
tileSize: 256,
|
|
68
|
+
// TOOD - why is this needed?
|
|
69
|
+
zoomOffset: devicePixelRatio === 1 ? -1 : 0,
|
|
70
|
+
renderSubLayers: renderSubLayers as any,
|
|
71
|
+
|
|
72
|
+
// Custom prop
|
|
73
|
+
tileSource,
|
|
74
|
+
showTileBorders
|
|
75
|
+
})
|
|
76
|
+
];
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function renderSubLayers(
|
|
81
|
+
props: TileSourceLayerProps & {tile: {index; bbox: {west; south; east; north}}}
|
|
82
|
+
) {
|
|
83
|
+
const {
|
|
84
|
+
tileSource,
|
|
85
|
+
showTileBorders,
|
|
86
|
+
minZoom,
|
|
87
|
+
maxZoom,
|
|
88
|
+
tile: {
|
|
89
|
+
index: {z: zoom},
|
|
90
|
+
bbox: {west, south, east, north}
|
|
91
|
+
}
|
|
92
|
+
} = props as any;
|
|
93
|
+
|
|
94
|
+
const layers: Layer[] = [];
|
|
95
|
+
|
|
96
|
+
const borderColor = zoom <= minZoom || zoom >= maxZoom ? [255, 0, 0, 255] : [0, 0, 255, 255];
|
|
97
|
+
|
|
98
|
+
switch (tileSource.mimeType) {
|
|
99
|
+
case 'application/vnd.mapbox-vector-tile':
|
|
100
|
+
layers.push(
|
|
101
|
+
new GeoJsonLayer({
|
|
102
|
+
id: `${props.id}-geojson`,
|
|
103
|
+
data: props.data as any,
|
|
104
|
+
pickable: true,
|
|
105
|
+
getFillColor: [0, 190, 80, 255],
|
|
106
|
+
lineWidthScale: 500,
|
|
107
|
+
lineWidthMinPixels: 0.5
|
|
108
|
+
})
|
|
109
|
+
);
|
|
110
|
+
break;
|
|
111
|
+
|
|
112
|
+
case 'image/png':
|
|
113
|
+
case 'image/jpeg':
|
|
114
|
+
case 'image/webp':
|
|
115
|
+
case 'image/avif':
|
|
116
|
+
layers.push(
|
|
117
|
+
new BitmapLayer(
|
|
118
|
+
props as any,
|
|
119
|
+
{
|
|
120
|
+
data: null,
|
|
121
|
+
image: props.data,
|
|
122
|
+
bounds: [west, south, east, north],
|
|
123
|
+
pickable: true
|
|
124
|
+
} as any
|
|
125
|
+
)
|
|
126
|
+
);
|
|
127
|
+
break;
|
|
128
|
+
|
|
129
|
+
default:
|
|
130
|
+
// eslint-disable-next-line no-console
|
|
131
|
+
console.error('Unknown tile mimeType', tileSource?.mimeType);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// Debug tile borders
|
|
135
|
+
if (showTileBorders) {
|
|
136
|
+
layers.push(
|
|
137
|
+
new PathLayer({
|
|
138
|
+
id: `${props.id}-border`,
|
|
139
|
+
data: [
|
|
140
|
+
[
|
|
141
|
+
[west, north],
|
|
142
|
+
[west, south],
|
|
143
|
+
[east, south],
|
|
144
|
+
[east, north],
|
|
145
|
+
[west, north]
|
|
146
|
+
]
|
|
147
|
+
],
|
|
148
|
+
getPath: (d) => d,
|
|
149
|
+
getColor: borderColor as any,
|
|
150
|
+
widthMinPixels: 4
|
|
151
|
+
})
|
|
152
|
+
);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
return layers;
|
|
156
|
+
}
|