Skip to content

Commit

Permalink
Add quadbinToBoundary utility
Browse files Browse the repository at this point in the history
  • Loading branch information
jmgaya authored Oct 7, 2024
1 parent d38c5e2 commit 42abad1
Show file tree
Hide file tree
Showing 6 changed files with 283 additions and 6 deletions.
24 changes: 24 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
## Quickstart

To install and build `quadbin-js` locally from source:

```bash
# install dependencies
yarn

# build package once
yarn build
```

To run tests, coverage, or a linter, you should execute `yarn build`, and afterward:

```bash
# run tests once
yarn test
```

## Releases

1. Create a new version: `yarn version [ major | minor | patch | prerelease ]`

2. Execute `yarn publish`
32 changes: 31 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -79,4 +79,34 @@ Converts quadbin cell into a xyz tile.
function geometryToCells(geometry: GeoJSONGeometry, resolution: bigint): bigint
```

Returns a list of cells covering a GeoJSON geometry at a given resolution
## cellToBoundary

```javascript
function cellToBoundary(quadbin: Quadbin): Polygon
```

Converts a Quadbin cell identifier into a geographical boundary represented as a polygon

## cellToOffset

```javascript
function cellToOffset(quadbin: Quadbin): [number, number, number]
```

Converts a Quadbin cell identifier into world coordinates offset values

## cellToWorldBounds

```javascript
function cellToWorldBounds(quadbin: Quadbin, coverage: number): [number[], number[]]
```

Computes the world bounds (in Web Mercator coordinates) for a given Quadbin cell, taking into account the cell's coverage area

## getCellPolygon

```javascript
function getCellPolygon(quadbin: Quadbin, coverage = 1): number[]
```

Generates the geographical polygon (in longitude and latitude) that represents the boundaries of a Quadbin cell, optionally taking into account coverage
15 changes: 12 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,20 +32,23 @@
"build:esm": "tsc -p tsconfig/tsconfig.esm.json",
"build:types": "tsc -p tsconfig/tsconfig.types.json",
"build:umd": "webpack --config tsconfig/webpack.config.cjs",
"lint": "npx prettier --check src",
"lint": "prettier --check src",
"test": "yarn lint && yarn test-fast",
"test-fast": "npx ts-node node_modules/tape/bin/tape test/**/*.spec.js"
"test-fast": "ts-node node_modules/tape/bin/tape test/**/*.spec.js",
"prepublishOnly": "yarn build"
},
"browser": {
"jsdom": false
},
"devDependencies": {
"@babel/register": "^7.13.0",
"@types/geojson": "^7946.0.14",
"babel-loader": "^8.0.0",
"babel-preset-minify": "^0.5.0",
"prettier": "^2.4.1",
"tape": "^5.3.0",
"ts-loader": "^9.2.5",
"ts-node": "^10.9.2",
"typescript": "^4.4.4",
"webpack": "^5.52.1",
"webpack-cli": "^4.8.0"
Expand All @@ -54,6 +57,12 @@
"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": {
"node": "14.21.3",
"yarn": "1.22.22"
}
}
38 changes: 38 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import {tiles} from '@mapbox/tile-cover';
import {worldToLngLat} from '@math.gl/web-mercator';
import type {Polygon} from 'geojson';

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

const TILE_SIZE = 512;

export function cellToOffset(quadbin: Quadbin): [number, number, number] {
const {x, y, z} = cellToTile(quadbin);
const scale = TILE_SIZE / (1 << z);
return [x * scale, TILE_SIZE - y * scale, scale];
}

export function cellToWorldBounds(quadbin: Quadbin, coverage: number): [number[], number[]] {
const [xOffset, yOffset, scale] = cellToOffset(quadbin);
return [
[xOffset, yOffset],
[xOffset + coverage * scale, yOffset - coverage * scale]
];
}

export function getCellPolygon(quadbin: Quadbin, coverage = 1): number[] {
const [topLeft, bottomRight] = cellToWorldBounds(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 {
return BigInt(`0x${hex}`);
}
Expand Down Expand Up @@ -89,3 +114,16 @@ export function geometryToCells(geometry, resolution: bigint): Quadbin[] {
max_zoom: zoom
}).map(([x, y, z]) => tileToCell({x, y, z}));
}

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

return {type: 'Polygon', coordinates: [boundary]};
}
60 changes: 58 additions & 2 deletions test/index.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import {
cellToParent,
geometryToCells,
getResolution,
hexToBigInt
cellToBoundary
} from 'quadbin';

import {tileToQuadkey} from './quadkey-utils.js';
Expand All @@ -16,6 +16,8 @@ const TEST_TILES = [
{x: 1023, y: 2412, z: 23, q: 5291729562728627583n}
];

const ANY_QUADBIN = BigInt(524800);

test('Quadbin conversion', async t => {
for (const {x, y, z, q} of TEST_TILES) {
const tile = {x, y, z};
Expand Down Expand Up @@ -51,7 +53,6 @@ test('Quadbin getParent', async t => {
import PointGeometry from './data/PointGeometry.json' assert {type: 'json'};
import MultiPointGeometry from './data/MultiPointGeometry.json' assert {type: 'json'};
import LineStringGeometry from './data/LineStringGeometry.json' assert {type: 'json'};
import MultiLineStringGeometry from './data/MultiLineStringGeometry.json' assert {type: 'json'};
import PolygonGeometry from './data/PolygonGeometry.json' assert {type: 'json'};
import PolygonAntimeridianGeometry from './data/PolygonAntimeridianGeometry.json' assert {type: 'json'};
import MultiPolygonGeometry from './data/MultiPolygonGeometry.json' assert {type: 'json'};
Expand All @@ -78,3 +79,58 @@ test('Quadbin geometryToCells', async t => {
}
t.end();
});

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.deepEquals(result, expectedPolygon);
}

t.end();
});
Loading

0 comments on commit 42abad1

Please sign in to comment.