webdggrid 1.0.4 → 1.0.7
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/dist/webdggrid.js +1 -1
- package/dist/webdggrid.js.map +1 -1
- package/dist/webdggrid.umd.js +1 -1
- package/dist/webdggrid.umd.js.map +1 -1
- package/lib-esm/libdggrid.wasm.js +1 -1
- package/lib-esm/webdggrid.js +414 -71
- package/lib-esm/webdggrid.js.map +1 -1
- package/lib-wasm/Makefile +3205 -0
- package/lib-wasm/libdggrid.js +6 -6
- package/lib-wasm/libdggrid.wasm +0 -0
- package/package.json +8 -5
- package/readme.md +72 -2
- package/src-ts/webdggrid.ts +541 -88
- package/types/webdggrid.d.ts +431 -45
- package/types/webdggrid.d.ts.map +1 -1
- package/lib-wasm/libdggrid.html +0 -1
package/src-ts/webdggrid.ts
CHANGED
|
@@ -1,69 +1,256 @@
|
|
|
1
1
|
// @ts-ignore
|
|
2
2
|
import { loadWasm, unloadWasm } from './libdggrid.wasm.js';
|
|
3
|
+
import { Feature, FeatureCollection, GeoJsonProperties, Polygon, Position } from 'geojson';
|
|
3
4
|
|
|
4
5
|
/**
|
|
5
|
-
*
|
|
6
|
+
* The shape of each cell in the Discrete Global Grid System.
|
|
7
|
+
*
|
|
8
|
+
* DGGRID supports four cell topologies. The most common choice for geospatial
|
|
9
|
+
* analysis is `HEXAGON` because hexagonal cells have equal adjacency (every
|
|
10
|
+
* neighbour shares an edge), uniform area, and minimal boundary-to-area ratio.
|
|
6
11
|
*
|
|
7
12
|
* @export
|
|
8
|
-
* @enum {
|
|
13
|
+
* @enum {string}
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* ```ts
|
|
17
|
+
* import { Topology } from 'webdggrid';
|
|
18
|
+
*
|
|
19
|
+
* dggs.setDggs({ topology: Topology.HEXAGON, ... }, 4);
|
|
20
|
+
* ```
|
|
9
21
|
*/
|
|
10
22
|
export enum Topology {
|
|
23
|
+
/** Six-sided cells — the default and most widely used topology. */
|
|
11
24
|
'HEXAGON' = 'HEXAGON',
|
|
25
|
+
/** Three-sided cells. */
|
|
12
26
|
'TRIANGLE' = 'TRIANGLE',
|
|
13
|
-
|
|
27
|
+
/** Four-sided diamond cells (squares rotated 45°). */
|
|
14
28
|
'DIAMOND' = 'DIAMOND',
|
|
15
29
|
}
|
|
30
|
+
|
|
16
31
|
/**
|
|
17
|
-
*
|
|
32
|
+
* The map projection used to place the polyhedron faces onto the sphere.
|
|
33
|
+
*
|
|
34
|
+
* - **ISEA** (Icosahedral Snyder Equal Area) — preserves cell area at the cost
|
|
35
|
+
* of shape distortion. Recommended for most analytical use-cases.
|
|
36
|
+
* - **FULLER** (Fuller/Dymaxion) — minimises shape distortion but does not
|
|
37
|
+
* preserve equal area.
|
|
18
38
|
*
|
|
19
39
|
* @export
|
|
20
|
-
* @enum {
|
|
40
|
+
* @enum {string}
|
|
41
|
+
*
|
|
42
|
+
* @example
|
|
43
|
+
* ```ts
|
|
44
|
+
* import { Projection } from 'webdggrid';
|
|
45
|
+
*
|
|
46
|
+
* dggs.setDggs({ projection: Projection.ISEA, ... }, 4);
|
|
47
|
+
* ```
|
|
21
48
|
*/
|
|
22
49
|
export enum Projection {
|
|
50
|
+
/** Icosahedral Snyder Equal Area projection — equal-area cells. */
|
|
23
51
|
'ISEA' = 'ISEA',
|
|
52
|
+
/** Fuller/Dymaxion projection — shape-preserving cells. */
|
|
24
53
|
'FULLER' = 'FULLER',
|
|
25
54
|
}
|
|
26
55
|
|
|
56
|
+
/**
|
|
57
|
+
* A simple geographic coordinate expressed as latitude and longitude in
|
|
58
|
+
* decimal degrees (WGS-84).
|
|
59
|
+
*
|
|
60
|
+
* @example
|
|
61
|
+
* ```ts
|
|
62
|
+
* const coord: Coordinate = { lat: 51.5, lng: -0.1 };
|
|
63
|
+
* ```
|
|
64
|
+
*/
|
|
27
65
|
export interface Coordinate {
|
|
66
|
+
/** Latitude in decimal degrees. Range: −90 to 90. */
|
|
28
67
|
lat: number;
|
|
68
|
+
/** Longitude in decimal degrees. Range: −180 to 180. */
|
|
29
69
|
lng: number;
|
|
30
70
|
}
|
|
31
71
|
|
|
72
|
+
/**
|
|
73
|
+
* GeoJSON `properties` object attached to every cell feature returned by
|
|
74
|
+
* {@link Webdggrid.sequenceNumToGridFeatureCollection}.
|
|
75
|
+
*
|
|
76
|
+
* All numeric identifiers are `BigInt` because DGGRID cell sequence numbers
|
|
77
|
+
* can exceed the safe integer range of IEEE-754 doubles at high resolutions.
|
|
78
|
+
*
|
|
79
|
+
* > **Note for MapLibre / Mapbox users:** structured-clone (used internally
|
|
80
|
+
* > by these libraries' Web Workers) cannot serialise `BigInt`. Convert `id`
|
|
81
|
+
* > to a string before calling `source.setData()`.
|
|
82
|
+
*/
|
|
83
|
+
export type DGGSGeoJsonProperty = GeoJsonProperties & {
|
|
84
|
+
/**
|
|
85
|
+
* The DGGS sequence number (cell ID) of this feature.
|
|
86
|
+
* Unique within a given DGGS configuration and resolution.
|
|
87
|
+
*/
|
|
88
|
+
id?: BigInt;
|
|
89
|
+
/** Column index in an (i, j) address scheme, if available. */
|
|
90
|
+
i?: BigInt;
|
|
91
|
+
/** Row index in an (i, j) address scheme, if available. */
|
|
92
|
+
j?: BigInt;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Full configuration of a Discrete Global Grid System.
|
|
97
|
+
*
|
|
98
|
+
* A DGGS is fully defined by its polyhedron orientation (`poleCoordinates`,
|
|
99
|
+
* `azimuth`), the subdivision scheme (`aperture`), the cell shape
|
|
100
|
+
* (`topology`), and the map projection (`projection`).
|
|
101
|
+
*
|
|
102
|
+
* Pass this object to {@link Webdggrid.setDggs} to switch between grid
|
|
103
|
+
* configurations at runtime.
|
|
104
|
+
*
|
|
105
|
+
* @example
|
|
106
|
+
* ```ts
|
|
107
|
+
* const myDggs: IDGGSProps = {
|
|
108
|
+
* poleCoordinates: { lat: 58.28, lng: 11.25 }, // Snyder orientation
|
|
109
|
+
* azimuth: 0,
|
|
110
|
+
* aperture: 4,
|
|
111
|
+
* topology: Topology.HEXAGON,
|
|
112
|
+
* projection: Projection.ISEA,
|
|
113
|
+
* };
|
|
114
|
+
* dggs.setDggs(myDggs, 5);
|
|
115
|
+
* ```
|
|
116
|
+
*/
|
|
32
117
|
export interface IDGGSProps {
|
|
118
|
+
/**
|
|
119
|
+
* Geographic location of the icosahedron pole used to orient the grid.
|
|
120
|
+
* Changing this rotates the entire grid on the globe, which can be used
|
|
121
|
+
* to minimise cell distortion over a region of interest.
|
|
122
|
+
* Defaults to `{ lat: 0, lng: 0 }`.
|
|
123
|
+
*/
|
|
33
124
|
poleCoordinates: Coordinate;
|
|
125
|
+
/**
|
|
126
|
+
* Azimuth of the icosahedron pole in decimal degrees.
|
|
127
|
+
* Rotates the grid around the pole axis. Defaults to `0`.
|
|
128
|
+
*/
|
|
34
129
|
azimuth: number;
|
|
130
|
+
/**
|
|
131
|
+
* Subdivision aperture — the number of child cells each parent cell is
|
|
132
|
+
* divided into when moving to the next finer resolution.
|
|
133
|
+
*
|
|
134
|
+
* | Aperture | Cells at res *r* (HEXAGON/ISEA) |
|
|
135
|
+
* |---|---|
|
|
136
|
+
* | 3 | 2 + 10 × 3^r |
|
|
137
|
+
* | 4 | 2 + 10 × 4^r |
|
|
138
|
+
* | 7 | 2 + 10 × 7^r |
|
|
139
|
+
*
|
|
140
|
+
* Aperture `4` is the most common choice and is the default.
|
|
141
|
+
*/
|
|
35
142
|
aperture: 3 | 4 | 5 | 7;
|
|
143
|
+
/** Shape of each cell. See {@link Topology}. */
|
|
36
144
|
topology: Topology;
|
|
145
|
+
/** Projection used to map the polyhedron faces onto the sphere. See {@link Projection}. */
|
|
37
146
|
projection: Projection;
|
|
38
147
|
}
|
|
39
148
|
|
|
149
|
+
/**
|
|
150
|
+
* Rewraps a polygon ring that crosses the antimeridian so that all longitudes
|
|
151
|
+
* are in a contiguous range (some may exceed 180°). This is the format
|
|
152
|
+
* expected by MapLibre GL / Mapbox GL globe projection for antimeridian cells.
|
|
153
|
+
* For renderers that require standard [-180, 180] coordinates, use the raw
|
|
154
|
+
* output from {@link Webdggrid.sequenceNumToGrid} directly.
|
|
155
|
+
*/
|
|
156
|
+
export function unwrapAntimeridianRing(ring: Position[]): Position[] {
|
|
157
|
+
const lons = ring.map((p) => p[0]);
|
|
158
|
+
const minLon = Math.min(...lons);
|
|
159
|
+
const maxLon = Math.max(...lons);
|
|
160
|
+
if (maxLon - minLon <= 180) return ring;
|
|
161
|
+
return ring.map(([lon, lat]) => [lon < 0 ? lon + 360 : lon, lat]);
|
|
162
|
+
}
|
|
163
|
+
|
|
40
164
|
const DEFAULT_RESOLUTION = 1;
|
|
41
165
|
const DEFAULT_DGGS = {
|
|
42
166
|
poleCoordinates: { lat: 0, lng: 0 },
|
|
43
167
|
azimuth: 0,
|
|
44
168
|
topology: Topology.HEXAGON,
|
|
45
169
|
projection: Projection.ISEA,
|
|
46
|
-
aperture:
|
|
170
|
+
aperture: 4
|
|
47
171
|
} as IDGGSProps;
|
|
48
172
|
|
|
173
|
+
/**
|
|
174
|
+
* Main entry point for the WebDggrid library.
|
|
175
|
+
*
|
|
176
|
+
* `Webdggrid` wraps the DGGRID C++ library compiled to WebAssembly and exposes
|
|
177
|
+
* methods for:
|
|
178
|
+
* - **Grid configuration** — choose topology, projection, aperture, and
|
|
179
|
+
* resolution via {@link setDggs} / {@link setResolution}.
|
|
180
|
+
* - **Coordinate conversion** — convert between geographic coordinates and
|
|
181
|
+
* DGGS cell IDs (sequence numbers) with {@link geoToSequenceNum} and
|
|
182
|
+
* {@link sequenceNumToGeo}.
|
|
183
|
+
* - **Grid geometry** — retrieve the polygon boundary of any cell with
|
|
184
|
+
* {@link sequenceNumToGrid} or export a ready-to-render GeoJSON
|
|
185
|
+
* `FeatureCollection` with {@link sequenceNumToGridFeatureCollection}.
|
|
186
|
+
* - **Grid statistics** — query cell counts, areas, and spacings with
|
|
187
|
+
* {@link nCells}, {@link cellAreaKM}, and {@link cellDistKM}.
|
|
188
|
+
*
|
|
189
|
+
* ## Quick start
|
|
190
|
+
*
|
|
191
|
+
* ```ts
|
|
192
|
+
* import { Webdggrid } from 'webdggrid';
|
|
193
|
+
*
|
|
194
|
+
* const dggs = await Webdggrid.load();
|
|
195
|
+
*
|
|
196
|
+
* // Convert a geographic point to its DGGS cell ID at resolution 5
|
|
197
|
+
* const [cellId] = dggs.geoToSequenceNum([[-73.9857, 40.7484]], 5);
|
|
198
|
+
*
|
|
199
|
+
* // Get the polygon boundary of that cell as GeoJSON
|
|
200
|
+
* const geojson = dggs.sequenceNumToGridFeatureCollection([cellId], 5);
|
|
201
|
+
* ```
|
|
202
|
+
*
|
|
203
|
+
* ## Lifecycle
|
|
204
|
+
*
|
|
205
|
+
* The WASM module is a singleton. Call {@link Webdggrid.load} once and reuse
|
|
206
|
+
* the returned instance throughout your application. Call
|
|
207
|
+
* {@link Webdggrid.unload} when you are completely done to free memory.
|
|
208
|
+
*/
|
|
49
209
|
export class Webdggrid {
|
|
50
210
|
|
|
211
|
+
/**
|
|
212
|
+
* The active DGGS configuration used by all conversion and statistics
|
|
213
|
+
* methods. Change it at any time via {@link setDggs}.
|
|
214
|
+
*
|
|
215
|
+
* Defaults to ISEA4H (ISEA projection, aperture 4, hexagon topology,
|
|
216
|
+
* pole at 0° N 0° E, azimuth 0°).
|
|
217
|
+
*/
|
|
51
218
|
dggs: IDGGSProps = DEFAULT_DGGS;
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* The active grid resolution. Higher values produce finer, smaller cells.
|
|
222
|
+
* The valid range depends on the aperture — for aperture 4 the practical
|
|
223
|
+
* limit is around resolution 15 before cell counts become unwieldy.
|
|
224
|
+
*
|
|
225
|
+
* Change via {@link setResolution} or pass an explicit `resolution`
|
|
226
|
+
* argument to any conversion method.
|
|
227
|
+
*
|
|
228
|
+
* Defaults to `1`.
|
|
229
|
+
*/
|
|
52
230
|
resolution: number = DEFAULT_RESOLUTION;
|
|
53
231
|
|
|
54
232
|
private constructor(protected _module: any) {
|
|
55
233
|
this._module = _module;
|
|
56
|
-
|
|
57
234
|
}
|
|
58
235
|
|
|
59
236
|
/**
|
|
60
|
-
* Compiles and instantiates the
|
|
61
|
-
*
|
|
237
|
+
* Compiles and instantiates the DGGRID WebAssembly module.
|
|
238
|
+
*
|
|
239
|
+
* This is the only way to construct a `Webdggrid` instance. The method is
|
|
240
|
+
* asynchronous because WebAssembly compilation is prohibited on the main
|
|
241
|
+
* thread for buffers larger than 4 KB.
|
|
242
|
+
*
|
|
243
|
+
* ```ts
|
|
244
|
+
* const dggs = await Webdggrid.load();
|
|
245
|
+
* ```
|
|
246
|
+
*
|
|
62
247
|
* ::: info
|
|
63
|
-
* In general WebAssembly compilation is disallowed on the main thread if
|
|
248
|
+
* In general WebAssembly compilation is disallowed on the main thread if
|
|
249
|
+
* the buffer size is larger than 4 KB, hence forcing `load` to be
|
|
250
|
+
* asynchronous.
|
|
64
251
|
* :::
|
|
65
|
-
*
|
|
66
|
-
* @returns A promise to
|
|
252
|
+
*
|
|
253
|
+
* @returns A promise that resolves to a fully initialised `Webdggrid` instance.
|
|
67
254
|
*/
|
|
68
255
|
static load(): Promise<typeof Webdggrid> {
|
|
69
256
|
return loadWasm().then((module: any) => {
|
|
@@ -72,22 +259,47 @@ export class Webdggrid {
|
|
|
72
259
|
}
|
|
73
260
|
|
|
74
261
|
/**
|
|
75
|
-
*
|
|
262
|
+
* Releases the compiled WASM instance and frees its memory.
|
|
263
|
+
*
|
|
264
|
+
* Call this when your application no longer needs the library. After
|
|
265
|
+
* calling `unload`, any existing `Webdggrid` instances become unusable —
|
|
266
|
+
* you must call {@link load} again to create a new one.
|
|
76
267
|
*/
|
|
77
268
|
static unload() {
|
|
78
269
|
unloadWasm();
|
|
79
270
|
}
|
|
80
271
|
|
|
81
272
|
/**
|
|
82
|
-
*
|
|
273
|
+
* Returns the version string of the underlying DGGRID C++ library.
|
|
274
|
+
*
|
|
275
|
+
* ```ts
|
|
276
|
+
* console.log(dggs.version()); // e.g. "8.3b"
|
|
277
|
+
* ```
|
|
278
|
+
*
|
|
279
|
+
* @returns The DGGRID C++ library version string.
|
|
83
280
|
*/
|
|
84
281
|
version(): string {
|
|
85
282
|
return this._module.Webdggrid.prototype.version();
|
|
86
283
|
}
|
|
87
284
|
|
|
88
285
|
/**
|
|
89
|
-
*
|
|
90
|
-
*
|
|
286
|
+
* Sets both the DGGS configuration and the resolution in one call.
|
|
287
|
+
*
|
|
288
|
+
* All subsequent conversion and statistics methods will use this
|
|
289
|
+
* configuration unless they receive an explicit `resolution` argument.
|
|
290
|
+
*
|
|
291
|
+
* ```ts
|
|
292
|
+
* dggs.setDggs({
|
|
293
|
+
* poleCoordinates: { lat: 0, lng: 0 },
|
|
294
|
+
* azimuth: 0,
|
|
295
|
+
* aperture: 4,
|
|
296
|
+
* topology: Topology.HEXAGON,
|
|
297
|
+
* projection: Projection.ISEA,
|
|
298
|
+
* }, 5);
|
|
299
|
+
* ```
|
|
300
|
+
*
|
|
301
|
+
* @param dggs - The new DGGS configuration. Defaults to ISEA4H at pole (0,0).
|
|
302
|
+
* @param resolution - The new resolution level. Defaults to `1`.
|
|
91
303
|
*/
|
|
92
304
|
setDggs(dggs: IDGGSProps = DEFAULT_DGGS, resolution: number = DEFAULT_RESOLUTION) {
|
|
93
305
|
this.dggs = dggs;
|
|
@@ -95,38 +307,66 @@ export class Webdggrid {
|
|
|
95
307
|
}
|
|
96
308
|
|
|
97
309
|
/**
|
|
98
|
-
*
|
|
99
|
-
*
|
|
100
|
-
*
|
|
310
|
+
* Returns the currently active grid resolution.
|
|
311
|
+
*
|
|
312
|
+
* ```ts
|
|
313
|
+
* dggs.setResolution(7);
|
|
314
|
+
* console.log(dggs.getResolution()); // 7
|
|
315
|
+
* ```
|
|
316
|
+
*
|
|
317
|
+
* @returns The current resolution level.
|
|
101
318
|
*/
|
|
102
319
|
getResolution(): number {
|
|
103
320
|
return this.resolution;
|
|
104
321
|
}
|
|
322
|
+
|
|
105
323
|
/**
|
|
106
|
-
*
|
|
107
|
-
*
|
|
108
|
-
*
|
|
324
|
+
* Sets the grid resolution used by default in all conversion and
|
|
325
|
+
* statistics methods.
|
|
326
|
+
*
|
|
327
|
+
* ```ts
|
|
328
|
+
* dggs.setResolution(5);
|
|
329
|
+
* const count = dggs.nCells(); // uses resolution 5
|
|
330
|
+
* ```
|
|
331
|
+
*
|
|
332
|
+
* @param resolution - The new resolution level. Must be a positive integer.
|
|
109
333
|
*/
|
|
110
334
|
setResolution(resolution: number) {
|
|
111
335
|
this.resolution = resolution;
|
|
112
336
|
}
|
|
113
337
|
|
|
114
338
|
/**
|
|
115
|
-
* test
|
|
116
|
-
*
|
|
117
|
-
* @
|
|
118
|
-
* @memberof WebDggrid
|
|
339
|
+
* Internal test helper that invokes the WASM module's `_main` entry point.
|
|
340
|
+
* Not intended for production use.
|
|
341
|
+
* @internal
|
|
119
342
|
*/
|
|
120
343
|
_main() {
|
|
121
344
|
return this._module._main();
|
|
122
345
|
}
|
|
123
346
|
|
|
124
347
|
/**
|
|
125
|
-
*
|
|
126
|
-
*
|
|
127
|
-
*
|
|
128
|
-
*
|
|
129
|
-
*
|
|
348
|
+
* Returns the total number of cells that tile the entire globe at the
|
|
349
|
+
* given resolution under the current DGGS configuration.
|
|
350
|
+
*
|
|
351
|
+
* Cell counts grow exponentially with resolution. For the default ISEA4H
|
|
352
|
+
* grid:
|
|
353
|
+
*
|
|
354
|
+
* | Resolution | Approx. cell count |
|
|
355
|
+
* |---|---|
|
|
356
|
+
* | 1 | 42 |
|
|
357
|
+
* | 2 | 162 |
|
|
358
|
+
* | 3 | 642 |
|
|
359
|
+
* | 4 | 2 562 |
|
|
360
|
+
* | 5 | 10 242 |
|
|
361
|
+
* | 6 | 40 962 |
|
|
362
|
+
*
|
|
363
|
+
* ```ts
|
|
364
|
+
* const total = dggs.nCells(3); // 642
|
|
365
|
+
* ```
|
|
366
|
+
*
|
|
367
|
+
* @param resolution - Resolution level to query. Defaults to the instance's
|
|
368
|
+
* current {@link resolution}.
|
|
369
|
+
* @returns Total number of cells at the given resolution.
|
|
130
370
|
*/
|
|
131
371
|
nCells(resolution: number = DEFAULT_RESOLUTION): number {
|
|
132
372
|
const {
|
|
@@ -138,8 +378,8 @@ export class Webdggrid {
|
|
|
138
378
|
} = this.dggs;
|
|
139
379
|
|
|
140
380
|
const cellCount = this._module.nCells(
|
|
141
|
-
lat,
|
|
142
381
|
lng,
|
|
382
|
+
lat,
|
|
143
383
|
azimuth,
|
|
144
384
|
aperture,
|
|
145
385
|
resolution,
|
|
@@ -150,6 +390,21 @@ export class Webdggrid {
|
|
|
150
390
|
return cellCount as number;
|
|
151
391
|
}
|
|
152
392
|
|
|
393
|
+
/**
|
|
394
|
+
* Returns the average area of a single cell in square kilometres at the
|
|
395
|
+
* given resolution.
|
|
396
|
+
*
|
|
397
|
+
* Because ISEA guarantees equal-area cells, all cells have the same area
|
|
398
|
+
* when using the `ISEA` projection. With `FULLER` the value is an average.
|
|
399
|
+
*
|
|
400
|
+
* ```ts
|
|
401
|
+
* const areakm2 = dggs.cellAreaKM(5);
|
|
402
|
+
* ```
|
|
403
|
+
*
|
|
404
|
+
* @param resolution - Resolution level to query. Defaults to the instance's
|
|
405
|
+
* current {@link resolution}.
|
|
406
|
+
* @returns Average cell area in km².
|
|
407
|
+
*/
|
|
153
408
|
cellAreaKM(resolution: number = DEFAULT_RESOLUTION): number {
|
|
154
409
|
const {
|
|
155
410
|
poleCoordinates: { lat, lng },
|
|
@@ -159,9 +414,9 @@ export class Webdggrid {
|
|
|
159
414
|
aperture,
|
|
160
415
|
} = this.dggs;
|
|
161
416
|
|
|
162
|
-
const cellCount = this._module.
|
|
163
|
-
lat,
|
|
417
|
+
const cellCount = this._module.cellAreaKM(
|
|
164
418
|
lng,
|
|
419
|
+
lat,
|
|
165
420
|
azimuth,
|
|
166
421
|
aperture,
|
|
167
422
|
resolution,
|
|
@@ -172,6 +427,21 @@ export class Webdggrid {
|
|
|
172
427
|
return cellCount as number;
|
|
173
428
|
}
|
|
174
429
|
|
|
430
|
+
/**
|
|
431
|
+
* Returns the average centre-to-centre distance between neighbouring cells
|
|
432
|
+
* in kilometres at the given resolution.
|
|
433
|
+
*
|
|
434
|
+
* This is useful for estimating spatial join radii or selecting a
|
|
435
|
+
* resolution that matches a target spatial scale.
|
|
436
|
+
*
|
|
437
|
+
* ```ts
|
|
438
|
+
* const spacingKm = dggs.cellDistKM(5);
|
|
439
|
+
* ```
|
|
440
|
+
*
|
|
441
|
+
* @param resolution - Resolution level to query. Defaults to the instance's
|
|
442
|
+
* current {@link resolution}.
|
|
443
|
+
* @returns Average cell spacing in km.
|
|
444
|
+
*/
|
|
175
445
|
cellDistKM(resolution: number = DEFAULT_RESOLUTION): number {
|
|
176
446
|
const {
|
|
177
447
|
poleCoordinates: { lat, lng },
|
|
@@ -181,9 +451,9 @@ export class Webdggrid {
|
|
|
181
451
|
aperture,
|
|
182
452
|
} = this.dggs;
|
|
183
453
|
|
|
184
|
-
const cellCount = this._module.
|
|
185
|
-
lat,
|
|
454
|
+
const cellCount = this._module.cellDistKM(
|
|
186
455
|
lng,
|
|
456
|
+
lat,
|
|
187
457
|
azimuth,
|
|
188
458
|
aperture,
|
|
189
459
|
resolution,
|
|
@@ -194,6 +464,22 @@ export class Webdggrid {
|
|
|
194
464
|
return cellCount as number;
|
|
195
465
|
}
|
|
196
466
|
|
|
467
|
+
/**
|
|
468
|
+
* Returns the characteristic length scale (CLS) of the grid at the given
|
|
469
|
+
* resolution — defined as the square root of the average cell area.
|
|
470
|
+
*
|
|
471
|
+
* CLS provides a single scalar that summarises the spatial granularity of
|
|
472
|
+
* the grid, useful for comparing resolutions across different DGGS
|
|
473
|
+
* configurations.
|
|
474
|
+
*
|
|
475
|
+
* ```ts
|
|
476
|
+
* const cls = dggs.gridStatCLS(4);
|
|
477
|
+
* ```
|
|
478
|
+
*
|
|
479
|
+
* @param resolution - Resolution level to query. Defaults to the instance's
|
|
480
|
+
* current {@link resolution}.
|
|
481
|
+
* @returns Grid CLS value.
|
|
482
|
+
*/
|
|
197
483
|
gridStatCLS(resolution: number = DEFAULT_RESOLUTION): number {
|
|
198
484
|
const {
|
|
199
485
|
poleCoordinates: { lat, lng },
|
|
@@ -203,9 +489,9 @@ export class Webdggrid {
|
|
|
203
489
|
aperture,
|
|
204
490
|
} = this.dggs;
|
|
205
491
|
|
|
206
|
-
const cellCount = this._module.
|
|
207
|
-
lat,
|
|
492
|
+
const cellCount = this._module.gridStatCLS(
|
|
208
493
|
lng,
|
|
494
|
+
lat,
|
|
209
495
|
azimuth,
|
|
210
496
|
aperture,
|
|
211
497
|
resolution,
|
|
@@ -215,11 +501,35 @@ export class Webdggrid {
|
|
|
215
501
|
|
|
216
502
|
return cellCount as number;
|
|
217
503
|
}
|
|
504
|
+
|
|
218
505
|
/**
|
|
219
|
-
* Converts an array of
|
|
220
|
-
*
|
|
221
|
-
*
|
|
222
|
-
*
|
|
506
|
+
* Converts an array of geographic coordinates to their corresponding DGGS
|
|
507
|
+
* cell sequence numbers (cell IDs) at the given resolution.
|
|
508
|
+
*
|
|
509
|
+
* Each coordinate is mapped to the single cell whose boundary contains it.
|
|
510
|
+
* Multiple coordinates that fall within the same cell will return the same
|
|
511
|
+
* sequence number.
|
|
512
|
+
*
|
|
513
|
+
* Coordinates must be supplied in **`[lng, lat]`** order (GeoJSON
|
|
514
|
+
* convention), **not** `[lat, lng]`.
|
|
515
|
+
*
|
|
516
|
+
* ```ts
|
|
517
|
+
* // New York City
|
|
518
|
+
* const ids = dggs.geoToSequenceNum([[-74.006, 40.7128]], 5);
|
|
519
|
+
* console.log(ids); // [12345n]
|
|
520
|
+
*
|
|
521
|
+
* // Multiple points at once
|
|
522
|
+
* const ids2 = dggs.geoToSequenceNum(
|
|
523
|
+
* [[-74.006, 40.7128], [2.3522, 48.8566]],
|
|
524
|
+
* 5
|
|
525
|
+
* );
|
|
526
|
+
* ```
|
|
527
|
+
*
|
|
528
|
+
* @param coordinates - Array of `[lng, lat]` pairs in decimal degrees.
|
|
529
|
+
* @param resolution - Resolution at which to perform the lookup. Defaults
|
|
530
|
+
* to the instance's current {@link resolution}.
|
|
531
|
+
* @returns Array of `BigInt` sequence numbers, one per input coordinate,
|
|
532
|
+
* in the same order.
|
|
223
533
|
*/
|
|
224
534
|
geoToSequenceNum(
|
|
225
535
|
coordinates: number[][],
|
|
@@ -237,8 +547,8 @@ export class Webdggrid {
|
|
|
237
547
|
const yCoords = coordinates.map((coord) => coord[1]);
|
|
238
548
|
|
|
239
549
|
const resultArray = this._module.DgGEO_to_SEQNUM(
|
|
240
|
-
lat,
|
|
241
550
|
lng,
|
|
551
|
+
lat,
|
|
242
552
|
azimuth,
|
|
243
553
|
aperture,
|
|
244
554
|
resolution,
|
|
@@ -250,16 +560,29 @@ export class Webdggrid {
|
|
|
250
560
|
|
|
251
561
|
return resultArray;
|
|
252
562
|
}
|
|
563
|
+
|
|
253
564
|
/**
|
|
254
|
-
*
|
|
255
|
-
*
|
|
256
|
-
*
|
|
257
|
-
*
|
|
565
|
+
* Converts an array of DGGS cell sequence numbers to the geographic
|
|
566
|
+
* coordinates of their centroids.
|
|
567
|
+
*
|
|
568
|
+
* The returned coordinates are in **`[lng, lat]`** order (GeoJSON
|
|
569
|
+
* convention).
|
|
570
|
+
*
|
|
571
|
+
* ```ts
|
|
572
|
+
* const centroids = dggs.sequenceNumToGeo([1n, 2n, 3n], 3);
|
|
573
|
+
* // [[lng0, lat0], [lng1, lat1], [lng2, lat2]]
|
|
574
|
+
* ```
|
|
575
|
+
*
|
|
576
|
+
* @param sequenceNum - Array of `BigInt` cell IDs to look up.
|
|
577
|
+
* @param resolution - Resolution at which the IDs were generated. Defaults
|
|
578
|
+
* to the instance's current {@link resolution}.
|
|
579
|
+
* @returns Array of `[lng, lat]` centroid positions, one per input ID, in
|
|
580
|
+
* the same order.
|
|
258
581
|
*/
|
|
259
582
|
sequenceNumToGeo(
|
|
260
583
|
sequenceNum: bigint[],
|
|
261
584
|
resolution: number = DEFAULT_RESOLUTION
|
|
262
|
-
):
|
|
585
|
+
): Position[] {
|
|
263
586
|
const {
|
|
264
587
|
poleCoordinates: { lat, lng },
|
|
265
588
|
azimuth,
|
|
@@ -269,8 +592,8 @@ export class Webdggrid {
|
|
|
269
592
|
} = this.dggs;
|
|
270
593
|
|
|
271
594
|
const resultArray = this._module.SEQNUM_to_GEO(
|
|
272
|
-
lat,
|
|
273
595
|
lng,
|
|
596
|
+
lat,
|
|
274
597
|
azimuth,
|
|
275
598
|
aperture,
|
|
276
599
|
resolution,
|
|
@@ -279,24 +602,46 @@ export class Webdggrid {
|
|
|
279
602
|
sequenceNum
|
|
280
603
|
);
|
|
281
604
|
|
|
282
|
-
const size = resultArray.length/2;
|
|
283
|
-
const arrayOfArrays:number[][] = [];
|
|
605
|
+
const size = resultArray.length / 2;
|
|
606
|
+
const arrayOfArrays: number[][] = [];
|
|
284
607
|
for (let i = 0; i < size; i += 1) {
|
|
285
|
-
arrayOfArrays.push([resultArray[i], resultArray[i+size]]);
|
|
608
|
+
arrayOfArrays.push([resultArray[i], resultArray[i + size]]);
|
|
286
609
|
}
|
|
287
610
|
|
|
288
611
|
return arrayOfArrays;
|
|
289
612
|
}
|
|
613
|
+
|
|
290
614
|
/**
|
|
291
|
-
*
|
|
292
|
-
*
|
|
293
|
-
*
|
|
294
|
-
*
|
|
615
|
+
* Snaps an array of geographic coordinates to the centroid of the DGGS
|
|
616
|
+
* cell that contains each point.
|
|
617
|
+
*
|
|
618
|
+
* This is equivalent to calling {@link geoToSequenceNum} followed by
|
|
619
|
+
* {@link sequenceNumToGeo} but is more efficient because it avoids
|
|
620
|
+
* returning the intermediate sequence numbers.
|
|
621
|
+
*
|
|
622
|
+
* Useful for spatial aggregation: all points that fall within the same
|
|
623
|
+
* cell will map to the identical centroid coordinate.
|
|
624
|
+
*
|
|
625
|
+
* Coordinates must be in **`[lng, lat]`** order.
|
|
626
|
+
*
|
|
627
|
+
* ```ts
|
|
628
|
+
* const snapped = dggs.geoToGeo(
|
|
629
|
+
* [[-74.006, 40.7128], [-74.010, 40.720]],
|
|
630
|
+
* 5
|
|
631
|
+
* );
|
|
632
|
+
* // Both points snap to the same centroid if they share a cell
|
|
633
|
+
* ```
|
|
634
|
+
*
|
|
635
|
+
* @param coordinates - Array of `[lng, lat]` pairs in decimal degrees.
|
|
636
|
+
* @param resolution - Resolution at which to perform the snapping. Defaults
|
|
637
|
+
* to the instance's current {@link resolution}.
|
|
638
|
+
* @returns Array of `[lng, lat]` cell centroid positions, one per input
|
|
639
|
+
* coordinate, in the same order.
|
|
295
640
|
*/
|
|
296
641
|
geoToGeo(
|
|
297
642
|
coordinates: number[][],
|
|
298
643
|
resolution: number = DEFAULT_RESOLUTION
|
|
299
|
-
):
|
|
644
|
+
): Position[] {
|
|
300
645
|
const {
|
|
301
646
|
poleCoordinates: { lat, lng },
|
|
302
647
|
azimuth,
|
|
@@ -309,8 +654,8 @@ export class Webdggrid {
|
|
|
309
654
|
const yCoords = coordinates.map((coord) => coord[1]);
|
|
310
655
|
|
|
311
656
|
const resultArray = this._module.GEO_to_GEO(
|
|
312
|
-
lat,
|
|
313
657
|
lng,
|
|
658
|
+
lat,
|
|
314
659
|
azimuth,
|
|
315
660
|
aperture,
|
|
316
661
|
resolution,
|
|
@@ -320,47 +665,155 @@ export class Webdggrid {
|
|
|
320
665
|
yCoords
|
|
321
666
|
);
|
|
322
667
|
|
|
323
|
-
const size = resultArray.length/2;
|
|
324
|
-
const arrayOfArrays:number[][] = [];
|
|
668
|
+
const size = resultArray.length / 2;
|
|
669
|
+
const arrayOfArrays: number[][] = [];
|
|
325
670
|
for (let i = 0; i < size; i += 1) {
|
|
326
|
-
arrayOfArrays.push([resultArray[i], resultArray[i+size]]);
|
|
671
|
+
arrayOfArrays.push([resultArray[i], resultArray[i + size]]);
|
|
327
672
|
}
|
|
328
673
|
|
|
329
674
|
return arrayOfArrays;
|
|
330
675
|
}
|
|
331
676
|
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
677
|
+
/**
|
|
678
|
+
* Returns the polygon boundary vertices for each cell in `sequenceNum`.
|
|
679
|
+
*
|
|
680
|
+
* Each cell is represented as an array of `[lng, lat]` vertex positions.
|
|
681
|
+
* The ring is **not** automatically closed (the first and last vertex are
|
|
682
|
+
* different) — close it yourself if your renderer requires it.
|
|
683
|
+
*
|
|
684
|
+
* Prefer {@link sequenceNumToGridFeatureCollection} when you need
|
|
685
|
+
* GeoJSON output ready for a mapping library.
|
|
686
|
+
*
|
|
687
|
+
* ```ts
|
|
688
|
+
* const rings = dggs.sequenceNumToGrid([1n, 2n], 3);
|
|
689
|
+
* // rings[0] = [[lng0,lat0], [lng1,lat1], ..., [lng5,lat5]] (hexagon)
|
|
690
|
+
* ```
|
|
691
|
+
*
|
|
692
|
+
* @param sequenceNum - Array of `BigInt` cell IDs whose boundaries to
|
|
693
|
+
* retrieve.
|
|
694
|
+
* @param resolution - Resolution at which the IDs were generated. Defaults
|
|
695
|
+
* to the instance's current {@link resolution}.
|
|
696
|
+
* @returns A 2-D array: `result[i]` is the vertex ring of `sequenceNum[i]`.
|
|
697
|
+
* Each vertex is a `[lng, lat]` position.
|
|
698
|
+
* @throws If the WASM module encounters an invalid cell ID.
|
|
699
|
+
*/
|
|
700
|
+
sequenceNumToGrid(
|
|
701
|
+
sequenceNum: bigint[],
|
|
702
|
+
resolution: number = DEFAULT_RESOLUTION
|
|
703
|
+
): Position[][] {
|
|
704
|
+
const {
|
|
705
|
+
poleCoordinates: { lat, lng },
|
|
706
|
+
azimuth,
|
|
707
|
+
topology,
|
|
708
|
+
projection,
|
|
709
|
+
aperture,
|
|
710
|
+
} = this.dggs;
|
|
348
711
|
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
712
|
+
const inputSize = sequenceNum.length;
|
|
713
|
+
|
|
714
|
+
let resultArray = [];
|
|
715
|
+
try {
|
|
716
|
+
resultArray = this._module.SeqNumGrid(
|
|
717
|
+
lng,
|
|
718
|
+
lat,
|
|
719
|
+
azimuth,
|
|
720
|
+
aperture,
|
|
721
|
+
resolution,
|
|
722
|
+
topology,
|
|
723
|
+
projection,
|
|
724
|
+
sequenceNum
|
|
725
|
+
);
|
|
726
|
+
} catch (e) {
|
|
727
|
+
console.error(this._module.getExceptionMessage(e).toString());
|
|
728
|
+
throw(e);
|
|
352
729
|
}
|
|
353
730
|
|
|
354
|
-
const
|
|
731
|
+
const allShapeVertexes = resultArray.slice(0, inputSize);
|
|
732
|
+
|
|
733
|
+
const sumVertexes = allShapeVertexes.reduce((accumulator, currentValue) => {
|
|
734
|
+
return accumulator + currentValue;
|
|
735
|
+
}, 0);
|
|
736
|
+
|
|
737
|
+
const featureSet: Position[][] = [];
|
|
355
738
|
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
739
|
+
let xOffset = inputSize;
|
|
740
|
+
let yOffset = inputSize + sumVertexes;
|
|
741
|
+
for (let i = 0; i < allShapeVertexes.length; i += 1) {
|
|
742
|
+
const numVertexes = allShapeVertexes[i];
|
|
359
743
|
|
|
360
|
-
|
|
361
|
-
|
|
744
|
+
const currentShapeXVertexes = resultArray.slice(xOffset, xOffset + numVertexes);
|
|
745
|
+
const currentShapeYVertexes = resultArray.slice(yOffset, yOffset + numVertexes);
|
|
746
|
+
|
|
747
|
+
const coordinates: Position[] = [];
|
|
748
|
+
for (let i = 0; i < numVertexes; i += 1) {
|
|
749
|
+
coordinates.push([currentShapeXVertexes[i], currentShapeYVertexes[i]]);
|
|
750
|
+
}
|
|
751
|
+
featureSet.push(unwrapAntimeridianRing(coordinates));
|
|
752
|
+
xOffset += numVertexes;
|
|
753
|
+
yOffset += numVertexes;
|
|
362
754
|
}
|
|
363
|
-
};
|
|
364
755
|
|
|
365
|
-
|
|
756
|
+
return featureSet;
|
|
757
|
+
}
|
|
758
|
+
|
|
759
|
+
/**
|
|
760
|
+
* Converts an array of DGGS cell IDs into a GeoJSON `FeatureCollection`
|
|
761
|
+
* where each `Feature` is a `Polygon` representing the cell boundary.
|
|
762
|
+
*
|
|
763
|
+
* This is the primary method for rendering DGGS cells with mapping
|
|
764
|
+
* libraries such as MapLibre GL JS, Leaflet, or deck.gl.
|
|
765
|
+
*
|
|
766
|
+
* Each feature includes:
|
|
767
|
+
* - `geometry` — a closed `Polygon` in `[lng, lat]` coordinate order.
|
|
768
|
+
* - `id` — the cell sequence number (converted to `string` recommended
|
|
769
|
+
* before passing to MapLibre to avoid BigInt serialisation errors).
|
|
770
|
+
* - `properties.id` — same value as `id`, accessible inside layer
|
|
771
|
+
* expressions.
|
|
772
|
+
*
|
|
773
|
+
* ```ts
|
|
774
|
+
* const ids = dggs.geoToSequenceNum([[-74.006, 40.7128]], 5);
|
|
775
|
+
* const fc = dggs.sequenceNumToGridFeatureCollection(ids, 5);
|
|
776
|
+
*
|
|
777
|
+
* // MapLibre / structured-clone safe: convert BigInt → string
|
|
778
|
+
* fc.features.forEach(f => {
|
|
779
|
+
* if (typeof f.id === 'bigint') f.id = f.id.toString();
|
|
780
|
+
* if (f.properties?.id) f.properties.id = f.properties.id.toString();
|
|
781
|
+
* });
|
|
782
|
+
*
|
|
783
|
+
* map.getSource('grid').setData(fc);
|
|
784
|
+
* ```
|
|
785
|
+
*
|
|
786
|
+
* @param sequenceNum - Array of `BigInt` cell IDs to convert.
|
|
787
|
+
* @param resolution - Resolution at which the IDs were generated. Defaults
|
|
788
|
+
* to the instance's current {@link resolution}.
|
|
789
|
+
* @returns A GeoJSON `FeatureCollection` of `Polygon` features, one per
|
|
790
|
+
* input cell ID.
|
|
791
|
+
*/
|
|
792
|
+
sequenceNumToGridFeatureCollection(
|
|
793
|
+
sequenceNum: bigint[],
|
|
794
|
+
resolution: number = DEFAULT_RESOLUTION
|
|
795
|
+
): FeatureCollection<Polygon, DGGSGeoJsonProperty> {
|
|
796
|
+
|
|
797
|
+
const coordinatesArray = this.sequenceNumToGrid(sequenceNum, resolution);
|
|
798
|
+
|
|
799
|
+
const features = coordinatesArray.map((coordinates, index) => {
|
|
800
|
+
const seqNum = sequenceNum[index];
|
|
801
|
+
return {
|
|
802
|
+
type: 'Feature',
|
|
803
|
+
geometry: {
|
|
804
|
+
type: 'Polygon',
|
|
805
|
+
coordinates: [coordinates]
|
|
806
|
+
},
|
|
807
|
+
id: seqNum as unknown as number,
|
|
808
|
+
properties: {
|
|
809
|
+
id: seqNum
|
|
810
|
+
} as DGGSGeoJsonProperty
|
|
811
|
+
} as Feature<Polygon, DGGSGeoJsonProperty>;
|
|
812
|
+
});
|
|
813
|
+
|
|
814
|
+
return {
|
|
815
|
+
type: 'FeatureCollection',
|
|
816
|
+
features,
|
|
817
|
+
};
|
|
818
|
+
}
|
|
366
819
|
}
|