Skip to content

Commit

Permalink
PR Feedback
Browse files Browse the repository at this point in the history
  • Loading branch information
jmgaya committed Oct 1, 2024
1 parent 132bf42 commit 7f279fe
Show file tree
Hide file tree
Showing 4 changed files with 91 additions and 42 deletions.
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,8 @@
"node": ">=14"
},
"dependencies": {
"@mapbox/tile-cover": "3.0.1"
"@mapbox/tile-cover": "3.0.1",
"@math.gl/web-mercator": "^4.1.0"
},
"packageManager": "[email protected]",
"volta": {
Expand Down
41 changes: 22 additions & 19 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import {tiles} from '@mapbox/tile-cover';
import {worldToLngLat} from '@math.gl/web-mercator';
import type {Polygon} from 'geojson';

const B = [
Expand All @@ -14,25 +15,27 @@ const S = [0n, 1n, 2n, 4n, 8n, 16n];
type Quadbin = bigint;
type Tile = {x: number; y: number; z: number};

function tileToLongitude(tile: ReturnType<typeof cellToTile>, offset: number) {
const {x, z} = tile;
return 180 * ((2.0 * (x + offset)) / (1 << z) - 1.0);
}
const TILE_SIZE = 512;

function tileToLatitude(tile: ReturnType<typeof cellToTile>, offset: number) {
const {y, z} = tile;
const expy = Math.exp(-((2.0 * (y + offset)) / (1 << z) - 1) * Math.PI);
return 360 * (Math.atan(expy) / Math.PI - 0.25);
function quadbinToOffset(quadbin: bigint): [number, number, number] {
const {x, y, z} = cellToTile(quadbin);
const scale = TILE_SIZE / (1 << z);
return [x * scale, TILE_SIZE - y * scale, scale];
}

function cellToBoundingBox(cell: bigint) {
const tile = cellToTile(cell);
const xmin = tileToLongitude(tile, 0);
const xmax = tileToLongitude(tile, 1);
const ymin = tileToLatitude(tile, 1);
const ymax = tileToLatitude(tile, 0);
function quadbinToWorldBounds(quadbin: bigint, coverage: number): [number[], number[]] {
const [xOffset, yOffset, scale] = quadbinToOffset(quadbin);
return [
[xOffset, yOffset],
[xOffset + coverage * scale, yOffset - coverage * scale]
];
}

return [xmin, ymin, xmax, ymax];
function getQuadbinPolygon(quadbin: bigint, coverage = 1): number[] {
const [topLeft, bottomRight] = quadbinToWorldBounds(quadbin, coverage);
const [w, n] = worldToLngLat(topLeft);
const [e, s] = worldToLngLat(bottomRight);
return [e, n, e, s, w, s, w, n, e, n];
}

export function hexToBigInt(hex: string): bigint {
Expand Down Expand Up @@ -113,13 +116,13 @@ export function geometryToCells(geometry, resolution: bigint): Quadbin[] {
}

export function cellToBoundary(cell: bigint): Polygon {
const bbox = cellToBoundingBox(cell);
const bbox = getQuadbinPolygon(cell);
const boundary = [
[bbox[0], bbox[3]],
[bbox[0], bbox[1]],
[bbox[2], bbox[1]],
[bbox[2], bbox[3]],
[bbox[0], bbox[3]]
[bbox[4], bbox[5]],
[bbox[6], bbox[7]],
[bbox[0], bbox[1]]
];

return {type: 'Polygon', coordinates: [boundary]};
Expand Down
70 changes: 48 additions & 22 deletions test/index.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -80,30 +80,56 @@ test('Quadbin geometryToCells', async t => {
t.end();
});

test('cellToBoundary works with quadbins', t => {
const result = cellToBoundary(ANY_QUADBIN);

t.equal(result.type, 'Polygon', 'Should return a Polygon');
t.ok(Array.isArray(result.coordinates), 'Coordinates should be an array');
t.equal(result.coordinates.length, 1, 'Should have one boundary array');

t.ok(result.coordinates[0].length === 5, 'Boundary should have 5 points');

t.end();
});

test('cellToBoundary works with Quadbins near the antimeridian', t => {
for (const quadbin of [
BigInt(536903670), // Longitude near +180°
BigInt(536870921) // Longitude near -180°
test('Quadbin cellToBoundary', t => {
for (const {quadbin, expectedPolygon} of [
{
quadbin: BigInt(524800),
expectedPolygon: {
type: 'Polygon',
coordinates: [
[
[180, 85.0511287798066],
[180, -85.05112877980659],
[-180, -85.05112877980659],
[-180, 85.0511287798066],
[180, 85.0511287798066]
]
]
}
},
{
quadbin: BigInt(536903670), // Longitude near +180°
expectedPolygon: {
type: 'Polygon',
coordinates: [
[
[180, 85.0511287798066],
[180, -85.05112877980659],
[-180, -85.05112877980659],
[-180, 85.0511287798066],
[180, 85.0511287798066]
]
]
}
},
{
quadbin: BigInt(536870921), // Longitude near -180°
expectedPolygon: {
type: 'Polygon',
coordinates: [
[
[180, 85.0511287798066],
[180, -85.05112877980659],
[-180, -85.05112877980659],
[-180, 85.0511287798066],
[180, 85.0511287798066]
]
]
}
}
]) {
const result = cellToBoundary(quadbin);

t.equal(result.type, 'Polygon', 'Should return a Polygon');
t.ok(
result.coordinates[0][0][0] > 170 || result.coordinates[0][0][0] < -170,
'Longitude should be near the antimeridian'
);
t.deepEquals(result, expectedPolygon);
}

t.end();
Expand Down
19 changes: 19 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,25 @@
dependencies:
tilebelt "^1.0.1"

"@math.gl/[email protected]":
version "4.1.0"
resolved "https://registry.yarnpkg.com/@math.gl/core/-/core-4.1.0.tgz#2f4a1644c6f8fb50aacae57a02f1297f933aefbd"
integrity sha512-FrdHBCVG3QdrworwrUSzXIaK+/9OCRLscxI2OUy6sLOHyHgBMyfnEGs99/m3KNvs+95BsnQLWklVfpKfQzfwKA==
dependencies:
"@math.gl/types" "4.1.0"

"@math.gl/[email protected]":
version "4.1.0"
resolved "https://registry.yarnpkg.com/@math.gl/types/-/types-4.1.0.tgz#ce28c06bcfe07d21311e00aeb25de82fecf7f393"
integrity sha512-clYZdHcmRvMzVK5fjeDkQlHUzXQSNdZ7s4xOqC3nJPgz4C/TZkUecTo9YS4PruZqtDda/ag4erndP0MIn40dGA==

"@math.gl/web-mercator@^4.1.0":
version "4.1.0"
resolved "https://registry.yarnpkg.com/@math.gl/web-mercator/-/web-mercator-4.1.0.tgz#b244112b2805ba68cdecc76f3d12578d05271a1d"
integrity sha512-HZo3vO5GCMkXJThxRJ5/QYUYRr3XumfT8CzNNCwoJfinxy5NtKUd7dusNTXn7yJ40UoB8FMIwkVwNlqaiRZZAw==
dependencies:
"@math.gl/core" "4.1.0"

"@tsconfig/node10@^1.0.7":
version "1.0.11"
resolved "https://registry.yarnpkg.com/@tsconfig/node10/-/node10-1.0.11.tgz#6ee46400685f130e278128c7b38b7e031ff5b2f2"
Expand Down

0 comments on commit 7f279fe

Please sign in to comment.