From ab2357a746abec537d87bb99f6f6822af5820788 Mon Sep 17 00:00:00 2001 From: Don McCurdy Date: Tue, 21 Jan 2025 14:51:52 -0500 Subject: [PATCH 1/3] feat(cellToChildren): Initial commit --- src/index.ts | 23 +++++++++++++++++++++++ test/index.spec.js | 16 +++++++++++++++- 2 files changed, 38 insertions(+), 1 deletion(-) diff --git a/src/index.ts b/src/index.ts index 561967c..a2e9239 100644 --- a/src/index.ts +++ b/src/index.ts @@ -107,6 +107,29 @@ export function cellToParent(quadbin: Quadbin): Quadbin { return parent; } +export function cellToChildren(quadbin: Quadbin, resolution: bigint): Quadbin[] { + if (resolution < 0 || resolution > 26 || resolution < getResolution(quadbin)) { + throw new Error('Invalid resolution'); + } + + const zoomLevelMask = ~(0x1fn << 52n); + const blockRange = 1n << ((resolution - ((quadbin >> 52n) & 0x1fn)) << 1n); + const sqrtBlockRange = 1n << (resolution - ((quadbin >> 52n) & 0x1fn)); + const blockShift = 52n - (resolution << 1n); + + const childBase = + ((quadbin & zoomLevelMask) | (resolution << 52n)) & ~((blockRange - 1n) << blockShift); + + const children: Quadbin[] = []; + for (let blockRow = 0n; blockRow < sqrtBlockRange; blockRow++) { + for (let blockColumn = 0n; blockColumn < sqrtBlockRange; blockColumn++) { + children.push(childBase | ((blockRow * sqrtBlockRange + blockColumn) << blockShift)); + } + } + + return children; +} + export function geometryToCells(geometry, resolution: bigint): Quadbin[] { const zoom = Number(resolution); return tiles(geometry, { diff --git a/test/index.spec.js b/test/index.spec.js index 0521b83..86c8a8b 100644 --- a/test/index.spec.js +++ b/test/index.spec.js @@ -1,11 +1,13 @@ import test from 'tape'; import { + cellToBoundary, tileToCell, + cellToChildren, cellToTile, cellToParent, geometryToCells, getResolution, - cellToBoundary + hexToBigInt } from 'quadbin'; import {tileToQuadkey} from './quadkey-utils.js'; @@ -49,6 +51,18 @@ test('Quadbin getParent', async t => { t.end(); }); +test('Quadbin getChildren', async t => { + const parent = 5224972163924099071n; // res=8 + t.deepEquals(cellToChildren(parent, 8n), [parent], 'children at resolution + 0'); + t.deepEquals( + cellToChildren(parent, 9n), + [5229475712011862015n, 5229475729191731199n, 5229475746371600383n, 5229475763551469567n], + 'children at resolution + 1' + ); + t.deepEquals(cellToChildren(parent, 10n).length, 16, 'children at resolution + 2'); + t.end(); +}); + // Zoom:26 test not agreeing with Python import PointGeometry from './data/PointGeometry.json' with {type: 'json'}; import MultiPointGeometry from './data/MultiPointGeometry.json' with {type: 'json'}; From f57f2acac583d1a640b938b481980e9634ad23df Mon Sep 17 00:00:00 2001 From: Don McCurdy Date: Wed, 22 Jan 2025 16:39:15 -0500 Subject: [PATCH 2/3] clean up test names --- test/index.spec.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/index.spec.js b/test/index.spec.js index 86c8a8b..998dd05 100644 --- a/test/index.spec.js +++ b/test/index.spec.js @@ -33,7 +33,7 @@ test('Quadbin conversion', async t => { t.end(); }); -test('Quadbin getParent', async t => { +test('Quadbin cellToParent', async t => { let tile = {x: 134, y: 1238, z: 10}; const quadkey = tileToQuadkey(tile); @@ -51,7 +51,7 @@ test('Quadbin getParent', async t => { t.end(); }); -test('Quadbin getChildren', async t => { +test('Quadbin cellToChildren', async t => { const parent = 5224972163924099071n; // res=8 t.deepEquals(cellToChildren(parent, 8n), [parent], 'children at resolution + 0'); t.deepEquals( From 7a60ea7c939d85ed40198bfea1682703234023df Mon Sep 17 00:00:00 2001 From: Don McCurdy Date: Wed, 22 Jan 2025 16:53:36 -0500 Subject: [PATCH 3/3] improve readability about order of cellToChildren result --- src/index.ts | 3 +++ test/index.spec.js | 17 ++++++++++++++--- 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/src/index.ts b/src/index.ts index a2e9239..aac1e84 100644 --- a/src/index.ts +++ b/src/index.ts @@ -107,6 +107,9 @@ export function cellToParent(quadbin: Quadbin): Quadbin { return parent; } +/** + * Returns the children of a cell, in row-major order starting from NW and ending at SE. + */ export function cellToChildren(quadbin: Quadbin, resolution: bigint): Quadbin[] { if (resolution < 0 || resolution > 26 || resolution < getResolution(quadbin)) { throw new Error('Invalid resolution'); diff --git a/test/index.spec.js b/test/index.spec.js index 998dd05..65399b1 100644 --- a/test/index.spec.js +++ b/test/index.spec.js @@ -52,14 +52,25 @@ test('Quadbin cellToParent', async t => { }); test('Quadbin cellToChildren', async t => { - const parent = 5224972163924099071n; // res=8 + const parentTile = { z: 8, x: 59, y: 97 }; + const parent = tileToCell(parentTile); + t.deepEquals(cellToChildren(parent, 8n), [parent], 'children at resolution + 0'); + + // Order is row major, starting from NW and ending at SE. t.deepEquals( - cellToChildren(parent, 9n), - [5229475712011862015n, 5229475729191731199n, 5229475746371600383n, 5229475763551469567n], + cellToChildren(parent, 9n).map(cellToTile), + [ + { z: 9, x: 118, y: 194 }, // nw + { z: 9, x: 119, y: 194 }, // ne + { z: 9, x: 118, y: 195 }, // sw + { z: 9, x: 119, y: 195 } // se + ], 'children at resolution + 1' ); + t.deepEquals(cellToChildren(parent, 10n).length, 16, 'children at resolution + 2'); + t.end(); });