converted tile
diff --git a/src/geo/bboxUtils.ts b/src/geo/bboxUtils.ts
index febf4df..2649e3e 100644
--- a/src/geo/bboxUtils.ts
+++ b/src/geo/bboxUtils.ts
@@ -66,7 +66,7 @@ export const bboxFromTiles = (minTile: ITile, maxTile: ITile): BBox2d => {
};
/**
- * coverts bbox to covering tile range of specified zoom level
+ * converts bbox to covering tile range of specified zoom level
* @param bbox
* @param zoom target zoom level
* @returns covering tile range
@@ -90,8 +90,8 @@ export const bboxToTileRange = (bbox: BBox2d, zoom: number): ITileRange => {
return {
minX: minTile.x,
minY: minTile.y,
- maxX: maxTile.x,
- maxY: maxTile.y,
+ maxX: maxTile.x - 1, // exclusive upper bound: maxTile is the first tile outside the bbox (right/above)
+ maxY: maxTile.y - 1, // exclusive upper bound: maxTile is the first tile outside the bbox (right/above)
zoom,
};
};
diff --git a/src/geo/tileBatcher.ts b/src/geo/tileBatcher.ts
index dcf12fa..acb62ba 100644
--- a/src/geo/tileBatcher.ts
+++ b/src/geo/tileBatcher.ts
@@ -10,22 +10,24 @@ async function* tileBatchGenerator(batchSize: number, ranges: AsyncGenerator
maxX means (no partial row)
+ const remaining = range.maxX - reminderX + 1;
if (remaining > requiredForFullBatch) {
targetRanges.push({
minX: reminderX,
- maxX: reminderX + requiredForFullBatch,
+ maxX: reminderX + requiredForFullBatch - 1,
minY: range.minY,
- maxY: range.minY + 1,
+ maxY: range.minY,
zoom: range.zoom,
});
yield await Promise.resolve(targetRanges);
@@ -36,9 +38,9 @@ async function* tileBatchGenerator(batchSize: number, ranges: AsyncGenerator 0) {
@@ -55,27 +56,26 @@ async function* tileBatchGenerator(batchSize: number, ranges: AsyncGenerator 0 && range.minY < range.maxY) {
- const endX = Math.min(range.minX + requiredForFullBatch, range.maxX);
+ //add partial line at row beginning
+ if (requiredForFullBatch > 0 && range.minY <= range.maxY) {
+ const endX = Math.min(range.minX + requiredForFullBatch - 1, range.maxX);
targetRanges.push({
minX: range.minX,
maxX: endX,
minY: range.minY,
- maxY: range.minY + 1,
+ maxY: range.minY,
zoom: range.zoom,
});
- requiredForFullBatch -= endX - range.minX;
- if (endX < range.maxX) {
- reminderX = endX;
- } else {
+ requiredForFullBatch -= endX - range.minX + 1;
+ reminderX = endX + 1;
+ if (endX >= range.maxX) {
range.minY++;
}
}
diff --git a/src/geo/tileRanger.ts b/src/geo/tileRanger.ts
index 1fa2274..60ac31c 100644
--- a/src/geo/tileRanger.ts
+++ b/src/geo/tileRanger.ts
@@ -32,21 +32,21 @@ export class TileRanger {
public tileToRange(tile: ITile, zoom: number): ITileRange {
let minX: number, minY: number, maxX: number, maxY: number;
minX = tile.x;
- maxX = tile.x + 1;
+ maxX = tile.x;
minY = tile.y;
- maxY = tile.y + 1;
+ maxY = tile.y;
if (tile.zoom < zoom) {
const dz = zoom - tile.zoom;
minX = minX << dz;
- maxX = maxX << dz;
+ maxX = ((tile.x + 1) << dz) - 1; // (tile.x+1) is the exclusive next tile; shift then -1 gives the inclusive last sub-tile
minY = minY << dz;
- maxY = maxY << dz;
+ maxY = ((tile.y + 1) << dz) - 1; // same logic for Y axis
} else if (tile.zoom > zoom) {
const dz = tile.zoom - zoom;
minX = minX >> dz;
minY = minY >> dz;
- maxX = minX + 1;
- maxY = minY + 1;
+ maxX = minX; // tile maps to a single parent tile; inclusive range collapses to one tile
+ maxY = minY;
}
return {
minX,
@@ -163,8 +163,8 @@ export class TileRanger {
/////////////////////////////////////////////////////////////////////////////////////////////////
//find base hashes
const minimalRange = bboxToTileRange(bbox, minZoom);
- for (let x = minimalRange.minX; x < minimalRange.maxX; x++) {
- for (let y = minimalRange.minY; y < minimalRange.maxY; y++) {
+ for (let x = minimalRange.minX; x <= minimalRange.maxX; x++) {
+ for (let y = minimalRange.minY; y <= minimalRange.maxY; y++) {
/////////////////////////////////////////////////////////////////////////////////////////////////
/// Step 6: for every tile in the current range:
/// Step 7: check the tile intersection with the footprint
diff --git a/src/geo/tiles.ts b/src/geo/tiles.ts
index ac38933..64a4813 100644
--- a/src/geo/tiles.ts
+++ b/src/geo/tiles.ts
@@ -80,7 +80,7 @@ export function degreesPerPixel(zoomLevel: number): number {
}
/**
- * coverts tile coordinates between ll and ul
+ * converts tile coordinates between ll and ul
* @param tile source tile
* @returns converted tile
*/
@@ -142,8 +142,8 @@ export function tileToBbox(tile: ITile): BBox2d {
* @param ITileRange
* @returns
*/
-export function tileRangeToTilesCount(batch: ITileRange): number {
- return (batch.maxX - batch.minX) * (batch.maxY - batch.minY);
+export function tileRangeToTilesCount(tileRange: ITileRange): number {
+ return (tileRange.maxX - tileRange.minX + 1) * (tileRange.maxY - tileRange.minY + 1);
}
/**
diff --git a/src/geo/tilesGenerator.ts b/src/geo/tilesGenerator.ts
index 97d912f..79da9f1 100644
--- a/src/geo/tilesGenerator.ts
+++ b/src/geo/tilesGenerator.ts
@@ -2,8 +2,8 @@ import { ITile, ITileRange } from '../models/interfaces/geo/iTile';
export async function* tilesGenerator(rangeGen: AsyncIterable): AsyncGenerator {
for await (const range of rangeGen) {
- for (let x = range.minX; x < range.maxX; x++) {
- for (let y = range.minY; y < range.maxY; y++) {
+ for (let x = range.minX; x <= range.maxX; x++) {
+ for (let y = range.minY; y <= range.maxY; y++) {
yield await Promise.resolve({
x,
y,
diff --git a/tests/unit/geo/bboxUtils.spec.ts b/tests/unit/geo/bboxUtils.spec.ts
index 39addca..06b53c8 100644
--- a/tests/unit/geo/bboxUtils.spec.ts
+++ b/tests/unit/geo/bboxUtils.spec.ts
@@ -33,7 +33,7 @@ describe('bboxUtils', () => {
});
describe('bboxToTileRange', () => {
- it('coverts bbox to expected tile range (no rounding, single tile)', () => {
+ it('converts bbox to expected tile range (no rounding, single tile)', () => {
const bbox = [0, 0, 45, 45] as BBox2d;
const range = bboxToTileRange(bbox, 2);
@@ -41,14 +41,14 @@ describe('bboxUtils', () => {
const expectedRange = {
minX: 4,
minY: 2,
- maxX: 5,
- maxY: 3,
+ maxX: 4,
+ maxY: 2,
zoom: 2,
};
expect(range).toEqual(expectedRange);
});
- it('coverts bbox to expected tile range (no rounding)', () => {
+ it('converts bbox to expected tile range (no rounding)', () => {
const bbox = [0, 0, 90, 45] as BBox2d;
const range = bboxToTileRange(bbox, 2);
@@ -56,14 +56,14 @@ describe('bboxUtils', () => {
const expectedRange = {
minX: 4,
minY: 2,
- maxX: 6,
- maxY: 3,
+ maxX: 5,
+ maxY: 2,
zoom: 2,
};
expect(range).toEqual(expectedRange);
});
- it('coverts bbox to expected tile range (rounding down)', () => {
+ it('converts bbox to expected tile range (rounding down)', () => {
const bbox = [0, 0, 45, 45] as BBox2d;
const range = bboxToTileRange(bbox, 1);
@@ -71,14 +71,14 @@ describe('bboxUtils', () => {
const expectedRange = {
minX: 2,
minY: 1,
- maxX: 3,
- maxY: 2,
+ maxX: 2,
+ maxY: 1,
zoom: 1,
};
expect(range).toEqual(expectedRange);
});
- it('coverts bbox to expected tile range (rounding up)', () => {
+ it('converts bbox to expected tile range (rounding up)', () => {
const bbox = [0, 0, 45, 45.1] as BBox2d;
const range = bboxToTileRange(bbox, 3);
@@ -86,8 +86,8 @@ describe('bboxUtils', () => {
const expectedRange = {
minX: 8,
minY: 4,
- maxX: 10,
- maxY: 7,
+ maxX: 9,
+ maxY: 6,
zoom: 3,
};
expect(range).toEqual(expectedRange);
diff --git a/tests/unit/geo/tileBatcher.spec.ts b/tests/unit/geo/tileBatcher.spec.ts
index 2f14600..107fddb 100644
--- a/tests/unit/geo/tileBatcher.spec.ts
+++ b/tests/unit/geo/tileBatcher.spec.ts
@@ -5,9 +5,9 @@ describe('GeoHashBatcher', () => {
describe('#getResource', () => {
it('return expected batches for complex data', async function () {
const ranges = [
- { minX: 0, minY: 2, maxX: 5, maxY: 4, zoom: 8 },
- { minX: 0, minY: 6, maxX: 2, maxY: 8, zoom: 8 },
- { minX: 0, minY: 8, maxX: 1, maxY: 11, zoom: 8 },
+ { minX: 0, minY: 2, maxX: 4, maxY: 3, zoom: 8 },
+ { minX: 0, minY: 6, maxX: 1, maxY: 7, zoom: 8 },
+ { minX: 0, minY: 8, maxX: 0, maxY: 10, zoom: 8 },
];
const rangeAsyncGen = (async function* () {
@@ -25,27 +25,27 @@ describe('GeoHashBatcher', () => {
// expectation
const expectedBatches = [
- [{ minX: 0, maxX: 3, minY: 2, maxY: 3, zoom: 8 }],
+ [{ minX: 0, maxX: 2, minY: 2, maxY: 2, zoom: 8 }],
[
- { minX: 3, maxX: 5, minY: 2, maxY: 3, zoom: 8 },
- { minX: 0, maxX: 1, minY: 3, maxY: 4, zoom: 8 },
+ { minX: 3, maxX: 4, minY: 2, maxY: 2, zoom: 8 },
+ { minX: 0, maxX: 0, minY: 3, maxY: 3, zoom: 8 },
],
- [{ minX: 1, maxX: 4, minY: 3, maxY: 4, zoom: 8 }],
+ [{ minX: 1, maxX: 3, minY: 3, maxY: 3, zoom: 8 }],
[
- { minX: 4, maxX: 5, minY: 3, maxY: 4, zoom: 8 },
- { minX: 0, maxX: 2, minY: 6, maxY: 7, zoom: 8 },
+ { minX: 4, maxX: 4, minY: 3, maxY: 3, zoom: 8 },
+ { minX: 0, maxX: 1, minY: 6, maxY: 6, zoom: 8 },
],
[
- { minX: 0, maxX: 2, minY: 7, maxY: 8, zoom: 8 },
- { minX: 0, maxX: 1, minY: 8, maxY: 9, zoom: 8 },
+ { minX: 0, maxX: 1, minY: 7, maxY: 7, zoom: 8 },
+ { minX: 0, maxX: 0, minY: 8, maxY: 8, zoom: 8 },
],
- [{ minX: 0, maxX: 1, minY: 9, maxY: 11, zoom: 8 }],
+ [{ minX: 0, maxX: 0, minY: 9, maxY: 10, zoom: 8 }],
];
expect(batches).toEqual(expectedBatches);
});
it('return expected batch for single tile', async function () {
- const ranges = [{ minX: 0, minY: 2, maxX: 1, maxY: 3, zoom: 8 }];
+ const ranges = [{ minX: 0, minY: 2, maxX: 0, maxY: 2, zoom: 8 }];
const rangeAsyncGen = (async function* () {
yield await Promise.resolve(ranges[0]);
})();
@@ -58,12 +58,12 @@ describe('GeoHashBatcher', () => {
}
// expectation
- const expectedBatches = [[{ minX: 0, maxX: 1, minY: 2, maxY: 3, zoom: 8 }]];
+ const expectedBatches = [[{ minX: 0, maxX: 0, minY: 2, maxY: 2, zoom: 8 }]];
expect(batches).toEqual(expectedBatches);
});
it('return empty batch on invalid empty x', async function () {
- const ranges = [{ minX: 0, minY: 2, maxX: 0, maxY: 3, zoom: 8 }];
+ const ranges = [{ minX: 1, minY: 2, maxX: 0, maxY: 3, zoom: 8 }];
const rangeAsyncGen = (async function* () {
yield await Promise.resolve(ranges[0]);
})();
@@ -81,7 +81,7 @@ describe('GeoHashBatcher', () => {
});
it('return empty batch on invalid empty y', async function () {
- const ranges = [{ minX: 0, minY: 2, maxX: 4, maxY: 2, zoom: 8 }];
+ const ranges = [{ minX: 0, minY: 3, maxX: 4, maxY: 2, zoom: 8 }];
const rangeAsyncGen = (async function* () {
yield await Promise.resolve(ranges[0]);
})();
@@ -98,8 +98,8 @@ describe('GeoHashBatcher', () => {
expect(batches).toEqual(expectedBatches);
});
- it('return proper tiles for batch size that is power of 2', async function () {
- const ranges = [{ minX: 0, minY: 16, maxX: 16, maxY: 32, zoom: 5 }];
+ it('yields each tile individually when batch size is 1', async function () {
+ const ranges = [{ minX: 0, minY: 16, maxX: 15, maxY: 31, zoom: 5 }];
const rangeAsyncGen = (async function* () {
yield await Promise.resolve(ranges[0]);
})();
@@ -113,9 +113,9 @@ describe('GeoHashBatcher', () => {
// expectation
const expectedBatches: ITileRange[][] = [];
- for (let y = 16; y < 32; y++) {
- for (let x = 0; x < 16; x++) {
- expectedBatches.push([{ minX: x, maxX: x + 1, minY: y, maxY: y + 1, zoom: 5 }]);
+ for (let y = 16; y <= 31; y++) {
+ for (let x = 0; x <= 15; x++) {
+ expectedBatches.push([{ minX: x, maxX: x, minY: y, maxY: y, zoom: 5 }]);
}
}
expect(batches).toEqual(expectedBatches);
diff --git a/tests/unit/geo/tileRanger.spec.ts b/tests/unit/geo/tileRanger.spec.ts
index 5b5d1af..8aba55d 100644
--- a/tests/unit/geo/tileRanger.spec.ts
+++ b/tests/unit/geo/tileRanger.spec.ts
@@ -22,8 +22,8 @@ describe('TileRanger', () => {
const expectedRange = {
minX: 1,
minY: 1,
- maxX: 2,
- maxY: 2,
+ maxX: 1,
+ maxY: 1,
zoom: 2,
};
expect(range).toEqual(expectedRange);
@@ -41,8 +41,8 @@ describe('TileRanger', () => {
const expectedRange = {
minX: 2,
minY: 2,
- maxX: 4,
- maxY: 4,
+ maxX: 3,
+ maxY: 3,
zoom: 3,
};
expect(range).toEqual(expectedRange);
@@ -60,8 +60,8 @@ describe('TileRanger', () => {
const expectedRange = {
minX: 0,
minY: 0,
- maxX: 1,
- maxY: 1,
+ maxX: 0,
+ maxY: 0,
zoom: 1,
};
expect(range).toEqual(expectedRange);
@@ -82,16 +82,16 @@ describe('TileRanger', () => {
const expectedRanges = [
{
minX: 3,
- maxX: 5,
+ maxX: 4,
minY: 2,
- maxY: 3,
+ maxY: 2,
zoom: 2,
},
];
expect(ranges).toEqual(expectedRanges);
});
- it('encodes none bbox polygon properly', async () => {
+ it('encodes non-bbox polygon properly', async () => {
const poly = polygon([
[
[-45, 0],
@@ -108,36 +108,36 @@ describe('TileRanger', () => {
}
const expectedRanges = [
- { minX: 24, minY: 16, maxX: 25, maxY: 17, zoom: 5 },
- { minX: 25, minY: 16, maxX: 26, maxY: 17, zoom: 5 },
- { minX: 25, minY: 17, maxX: 26, maxY: 18, zoom: 5 },
- { minX: 26, minY: 16, maxX: 28, maxY: 18, zoom: 5 },
- { minX: 26, minY: 18, maxX: 27, maxY: 19, zoom: 5 },
- { minX: 27, minY: 18, maxX: 28, maxY: 19, zoom: 5 },
- { minX: 27, minY: 19, maxX: 28, maxY: 20, zoom: 5 },
- { minX: 28, minY: 16, maxX: 32, maxY: 20, zoom: 5 },
- { minX: 28, minY: 20, maxX: 29, maxY: 21, zoom: 5 },
- { minX: 29, minY: 20, maxX: 30, maxY: 21, zoom: 5 },
- { minX: 29, minY: 21, maxX: 30, maxY: 22, zoom: 5 },
- { minX: 30, minY: 20, maxX: 32, maxY: 22, zoom: 5 },
- { minX: 30, minY: 22, maxX: 31, maxY: 23, zoom: 5 },
- { minX: 31, minY: 22, maxX: 32, maxY: 23, zoom: 5 },
- { minX: 31, minY: 23, maxX: 32, maxY: 24, zoom: 5 },
- { minX: 32, minY: 16, maxX: 36, maxY: 20, zoom: 5 },
- { minX: 36, minY: 16, maxX: 38, maxY: 18, zoom: 5 },
- { minX: 38, minY: 16, maxX: 39, maxY: 17, zoom: 5 },
- { minX: 39, minY: 16, maxX: 40, maxY: 17, zoom: 5 },
- { minX: 38, minY: 17, maxX: 39, maxY: 18, zoom: 5 },
- { minX: 36, minY: 18, maxX: 37, maxY: 19, zoom: 5 },
- { minX: 37, minY: 18, maxX: 38, maxY: 19, zoom: 5 },
- { minX: 36, minY: 19, maxX: 37, maxY: 20, zoom: 5 },
- { minX: 32, minY: 20, maxX: 34, maxY: 22, zoom: 5 },
- { minX: 34, minY: 20, maxX: 35, maxY: 21, zoom: 5 },
- { minX: 35, minY: 20, maxX: 36, maxY: 21, zoom: 5 },
- { minX: 34, minY: 21, maxX: 35, maxY: 22, zoom: 5 },
- { minX: 32, minY: 22, maxX: 33, maxY: 23, zoom: 5 },
- { minX: 33, minY: 22, maxX: 34, maxY: 23, zoom: 5 },
- { minX: 32, minY: 23, maxX: 33, maxY: 24, zoom: 5 },
+ { minX: 24, minY: 16, maxX: 24, maxY: 16, zoom: 5 },
+ { minX: 25, minY: 16, maxX: 25, maxY: 16, zoom: 5 },
+ { minX: 25, minY: 17, maxX: 25, maxY: 17, zoom: 5 },
+ { minX: 26, minY: 16, maxX: 27, maxY: 17, zoom: 5 },
+ { minX: 26, minY: 18, maxX: 26, maxY: 18, zoom: 5 },
+ { minX: 27, minY: 18, maxX: 27, maxY: 18, zoom: 5 },
+ { minX: 27, minY: 19, maxX: 27, maxY: 19, zoom: 5 },
+ { minX: 28, minY: 16, maxX: 31, maxY: 19, zoom: 5 },
+ { minX: 28, minY: 20, maxX: 28, maxY: 20, zoom: 5 },
+ { minX: 29, minY: 20, maxX: 29, maxY: 20, zoom: 5 },
+ { minX: 29, minY: 21, maxX: 29, maxY: 21, zoom: 5 },
+ { minX: 30, minY: 20, maxX: 31, maxY: 21, zoom: 5 },
+ { minX: 30, minY: 22, maxX: 30, maxY: 22, zoom: 5 },
+ { minX: 31, minY: 22, maxX: 31, maxY: 22, zoom: 5 },
+ { minX: 31, minY: 23, maxX: 31, maxY: 23, zoom: 5 },
+ { minX: 32, minY: 16, maxX: 35, maxY: 19, zoom: 5 },
+ { minX: 36, minY: 16, maxX: 37, maxY: 17, zoom: 5 },
+ { minX: 38, minY: 16, maxX: 38, maxY: 16, zoom: 5 },
+ { minX: 39, minY: 16, maxX: 39, maxY: 16, zoom: 5 },
+ { minX: 38, minY: 17, maxX: 38, maxY: 17, zoom: 5 },
+ { minX: 36, minY: 18, maxX: 36, maxY: 18, zoom: 5 },
+ { minX: 37, minY: 18, maxX: 37, maxY: 18, zoom: 5 },
+ { minX: 36, minY: 19, maxX: 36, maxY: 19, zoom: 5 },
+ { minX: 32, minY: 20, maxX: 33, maxY: 21, zoom: 5 },
+ { minX: 34, minY: 20, maxX: 34, maxY: 20, zoom: 5 },
+ { minX: 35, minY: 20, maxX: 35, maxY: 20, zoom: 5 },
+ { minX: 34, minY: 21, maxX: 34, maxY: 21, zoom: 5 },
+ { minX: 32, minY: 22, maxX: 32, maxY: 22, zoom: 5 },
+ { minX: 33, minY: 22, maxX: 33, maxY: 22, zoom: 5 },
+ { minX: 32, minY: 23, maxX: 32, maxY: 23, zoom: 5 },
];
expect(tileRanges).toEqual(expectedRanges);
});
diff --git a/tests/unit/geo/tiles.spec.ts b/tests/unit/geo/tiles.spec.ts
index ad4e8e2..4167c96 100644
--- a/tests/unit/geo/tiles.spec.ts
+++ b/tests/unit/geo/tiles.spec.ts
@@ -114,14 +114,14 @@ describe('tiles', () => {
describe('tileRangeToTilesCount', () => {
it('Check calculation for area calculation - tiles count by tiles range', function () {
const batch: ITileRange = {
- maxX: 180,
- minX: -180,
- maxY: 90,
- minY: -90,
- zoom: 0,
+ minX: 0,
+ maxX: 7,
+ minY: 0,
+ maxY: 3,
+ zoom: 2,
};
const areaResult = tileRangeToTilesCount(batch);
- const expectedResult = 64800;
+ const expectedResult = 32;
expect(areaResult).toEqual(expectedResult);
});
corner tile for bbox with maximal x,y values