Skip to content

Commit

Permalink
fix(dggs-s2): Replace long module with bigint (#6)
Browse files Browse the repository at this point in the history
  • Loading branch information
ibgreen authored Jul 12, 2024
1 parent 6b1f058 commit 39b6f6e
Show file tree
Hide file tree
Showing 6 changed files with 27 additions and 25 deletions.
3 changes: 1 addition & 2 deletions modules/dggs-s2/src/converters/s2-to-boundary.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
// math.gl, MIT license

import Long from 'long';
import {IJToST, STToUV, FaceUVToXYZ, XYZToLngLat} from '../s2geometry/s2-geometry';
import {getS2Cell} from '../s2geometry/s2-cell-utils';

const MAX_RESOLUTION = 100;

export function getS2GeoBounds(s2Index: Long): Float64Array {
export function getS2GeoBounds(s2Index: bigint): Float64Array {
const s2Cell = getS2Cell(s2Index);
return getS2GeoBoundsFromCell(s2Cell);
}
Expand Down
33 changes: 20 additions & 13 deletions modules/dggs-s2/src/s2-token-functions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@
// s2-geometry is a pure JavaScript port of Google/Niantic's S2 Geometry library
// which is perfect since it works in the browser.

import Long from 'long';

// const MAXIMUM_TOKEN_LENGTH = 16;

// INDEX CALCULATIONS
Expand All @@ -14,48 +12,57 @@ import Long from 'long';
* 'X' is the empty cell
* https://github.com/google/s2-geometry-library-java/blob/c04b68bf3197a9c34082327eeb3aec7ab7c85da1/src/com/google/common/geometry/S2CellId.java#L439
*/
export function getS2IndexFromToken(token: string): Long {
export function getS2IndexFromToken(token: string): bigint {
if (token === 'X') {
token = '';
}
// pad token with zeros to make the length 16
const paddedToken = token.padEnd(16, '0');
return Long.fromString(paddedToken, true, 16);
return BigInt(`0x${paddedToken}`);
}

/**
* Convert a 64 bit number to a string token
* 'X' is the empty cell
*/
export function getS2TokenFromIndex(cellId: Long): string {
if (cellId.isZero()) {
export function getS2TokenFromIndex(cellId: bigint): string {
if (cellId === 0n) {
return 'X';
}
let numZeroDigits = cellId.countTrailingZeros();
let numZeroDigits = countTrailingZeros(cellId);

const remainder = numZeroDigits % 4;
numZeroDigits = (numZeroDigits - remainder) / 4;
const trailingZeroHexChars = numZeroDigits;
numZeroDigits *= 4;

const x = cellId.shiftRightUnsigned(numZeroDigits);
const x = cellId >> BigInt(numZeroDigits);
const hexString = x.toString(16).replace(/0+$/, '');
const zeroString = Array(17 - trailingZeroHexChars - hexString.length).join('0');
return zeroString + hexString;
}

export function getS2ChildIndex(s2Index: Long, index: number): Long {
export function getS2ChildIndex(s2Index: bigint, index: number): bigint {
// Shift sentinel bit 2 positions to the right.
const newLsb = lsb(s2Index).shiftRightUnsigned(2);
const newLsb = lsb(s2Index) >> 2n;
// Insert child index before the sentinel bit.
const childCellId: Long = s2Index.add(Long.fromNumber(2 * index + 1 - 4).multiply(newLsb));
const childCellId: bigint = s2Index + BigInt(2 * index + 1 - 4) * newLsb;
return childCellId;
}

/**
* Return the lowest-numbered bit that is on for this cell id
* @private
*/
function lsb(cellId: Long): Long {
return cellId.and(cellId.not().add(1)); // eslint-disable-line
function lsb(cellId: bigint): bigint {
return cellId & (cellId + 1n); // eslint-disable-line
}

function countTrailingZeros(n: bigint): number {
let count = 0;
while (n % 2n === 0n) {
n /= 2n;
count++;
}
return count;
}
3 changes: 1 addition & 2 deletions modules/dggs-s2/src/s2geometry/s2-cell-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,8 @@

import type {S2Cell} from './s2-geometry';
import {fromHilbertQuadKey, toHilbertQuadkey} from './s2-geometry';
import Long from 'long';

export function getS2Cell(s2Index: Long): S2Cell {
export function getS2Cell(s2Index: bigint): S2Cell {
const key = toHilbertQuadkey(s2Index);
const s2cell = fromHilbertQuadKey(key);
return s2cell;
Expand Down
8 changes: 3 additions & 5 deletions modules/dggs-s2/src/s2geometry/s2-geometry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,6 @@ Permission to use, copy, modify, and/or distribute this software for any purpose
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.
*/

import Long from 'long';

//
// Functional Style
//
Expand Down Expand Up @@ -71,7 +69,7 @@ export function fromHilbertQuadKey(hilbertQuadkey: string): S2Cell {
return {face, ij: point, level};
}

export function toHilbertQuadkey(id: Long): string {
export function toHilbertQuadkey(id: bigint): string {
let bin = id.toString(2);

while (bin.length < FACE_BITS + POS_BITS) {
Expand All @@ -88,7 +86,7 @@ export function toHilbertQuadkey(id: Long): string {
const posB = bin.substring(3, lsbIndex);
const levelN = posB.length / 2;

const faceS = Long.fromString(faceB, true, 2).toString(10);
const faceS = BigInt(`0b${faceB}`).toString(10);

/*
Here is a fix for the case when posB is an empty string that causes an exception in Long.fromString
Expand All @@ -98,7 +96,7 @@ export function toHilbertQuadkey(id: Long): string {
let posS = '0';
if (levelN !== 0) {
// posB is not an empty string< because levelN!==0
posS = Long.fromString(posB, true, 2).toString(4);
posS = BigInt(`0b${posB}`).toString(4);

while (posS.length < levelN) {
// eslint-disable-next-line prefer-template
Expand Down
3 changes: 1 addition & 2 deletions modules/dggs-s2/test/s2-geometry.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import test from 'tape-promise/tape';

import {_toHilbertQuadKey as toHilbertQuadKey} from '@math.gl/dggs-s2';
import {S2} from 's2-geometry';
import Long from 'long';

// TODO - restore test
test.skip('S2#toHilbertQuadkey', (t) => {
Expand All @@ -19,7 +18,7 @@ test.skip('S2#toHilbertQuadkey', (t) => {
for (const point of TEST_COORDINATES) {
for (const level of TEST_LEVELS) {
const key = S2.latLngToKey(point.lat, point.lng, level);
const id = Long.fromString(S2.keyToId(key), true);
const id = BigInt(S2.keyToId(key));
const token = id.toString(16).replace(/0+$/, '');

t.comment(`level ${level}, id: ${id.toString()}, token: ${token}`);
Expand Down
2 changes: 1 addition & 1 deletion modules/dggs-s2/test/s2-utils.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ test('getS2BoundaryFlat', (t) => {
'5b', // face 2
'6b', // face 3
'ab', // face 5
'4/001003',
// '4/001003', TODO - not supported
'54', // antimeridian
'5c' // antimeridian
// new Long(0, -2138636288, false),
Expand Down

0 comments on commit 39b6f6e

Please sign in to comment.