Skip to content

Commit

Permalink
Add clamp so we can ignore out-of-bounds coordinates
Browse files Browse the repository at this point in the history
  • Loading branch information
keichan34 committed Nov 25, 2024
1 parent 4b97bbf commit 2abe481
Show file tree
Hide file tree
Showing 3 changed files with 47 additions and 25 deletions.
11 changes: 11 additions & 0 deletions src/lib/tilebelt_local.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,4 +77,15 @@ describe('pointToLocalTileFraction', () => {
assert.deepStrictEqual(tb.pointToLocalTileFraction(1024, 511, 511, 0, 11), [1022, 1022, 0, 11]);
assert.deepStrictEqual(tb.pointToLocalTileFraction(1024, 511, 511, 0, 9), [255.5, 255.5, 0, 9]);
});

test('works with clamp', () => {
assert.deepStrictEqual(tb.pointToLocalTileFraction(1024, 0, 0, 0, 0, true), [0, 0, 0, 0]);
assert.deepStrictEqual(tb.pointToLocalTileFraction(1024, 1100, 1100, 0, 0, true), [0, 0, 0, 0]);
});

test('throws an error for out-of-bounds coordinates', () => {
assert.throws(() => {
tb.pointToLocalTileFraction(1024, 1100, 1100, 0, 0, false);
});
});
});
60 changes: 35 additions & 25 deletions src/lib/tilebelt_local.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@ export function tile2meters(scale: number, z: number): number {
/**
* Get the smallest tile to cover a bbox
*/
export function bboxToLocalTile(scale: number, bboxCoords: BBox3D, minZoom?: number): XYFZTile {
const min = pointToLocalTile(scale, bboxCoords[0], bboxCoords[1], bboxCoords[2], 32);
const max = pointToLocalTile(scale, bboxCoords[3], bboxCoords[4], bboxCoords[5], 32);
export function bboxToLocalTile(scale: number, bboxCoords: BBox3D, minZoom?: number, clamp?: boolean): XYFZTile {
const min = pointToLocalTile(scale, bboxCoords[0], bboxCoords[1], bboxCoords[2], 32, clamp);
const max = pointToLocalTile(scale, bboxCoords[3], bboxCoords[4], bboxCoords[5], 32, clamp);
const bbox: BBox3D = [min[0], min[1], min[2], max[0], max[1], max[2]];

const z = Math.min(getBboxZoom(bbox), typeof minZoom !== 'undefined' ? minZoom : MAX_ZOOM);
Expand All @@ -29,39 +29,41 @@ export function bboxToLocalTile(scale: number, bboxCoords: BBox3D, minZoom?: num
}


export function calculateLocalZFXY(scale: number, input: XYPointWithAltitude | BBox3D, zoom: number): ZFXYTile {
export function calculateLocalZFXY(scale: number, input: XYPointWithAltitude | BBox3D, zoom: number, clamp?: boolean): ZFXYTile {
let bbox: BBox3D;
if (Array.isArray(input) && input.length === 6) {
bbox = input;
} else {
const i = input as XYPointWithAltitude;
bbox = [i.x, i.y, i.alt, i.x, i.y, i.alt];
}
const tile = bboxToLocalTile(scale, bbox, zoom);
const tile = bboxToLocalTile(scale, bbox, zoom, clamp);
return xyfzTileAryToObj(tile);
}

/**
* Get the smallest tile to cover a bbox
* not used right now
*/
export function localBboxToTile(scale: number, bboxCoords: BBox3D, minZoom?: number): XYFZTile {
const min = pointToLocalTile(scale, bboxCoords[0], bboxCoords[1], bboxCoords[2], 32);
const max = pointToLocalTile(scale, bboxCoords[3], bboxCoords[4], bboxCoords[5], 32);
const bbox: BBox3D = [min[0], min[1], min[2], max[0], max[1], max[2]];
// export function localBboxToTile(scale: number, bboxCoords: BBox3D, minZoom?: number): XYFZTile {
// const min = pointToLocalTile(scale, bboxCoords[0], bboxCoords[1], bboxCoords[2], 32);
// const max = pointToLocalTile(scale, bboxCoords[3], bboxCoords[4], bboxCoords[5], 32);
// const bbox: BBox3D = [min[0], min[1], min[2], max[0], max[1], max[2]];

const z = Math.min(getBboxZoom(bbox), typeof minZoom !== 'undefined' ? minZoom : MAX_ZOOM);
if (z === 0) return [0, 0, 0, 0];
const x = bbox[0] >>> (32 - z);
const y = bbox[1] >>> (32 - z);
const f = bbox[2] >>> (32 - z);
return [x, y, f, z];
}
// const z = Math.min(getBboxZoom(bbox), typeof minZoom !== 'undefined' ? minZoom : MAX_ZOOM);
// if (z === 0) return [0, 0, 0, 0];
// const x = bbox[0] >>> (32 - z);
// const y = bbox[1] >>> (32 - z);
// const f = bbox[2] >>> (32 - z);
// return [x, y, f, z];
// }

/**
* Get the tile for a point at a specified zoom level
* Set `clamp` to `true` to prevent out-of-bounds coordinates
*/
export function pointToLocalTile(scale: number, x: number, y: number, alt: number, z: number): XYFZTile {
var tile = pointToLocalTileFraction(scale, x, y, alt, z);
export function pointToLocalTile(scale: number, x: number, y: number, alt: number, z: number, clamp?: boolean): XYFZTile {
var tile = pointToLocalTileFraction(scale, x, y, alt, z, clamp);
tile[0] = Math.floor(tile[0]);
tile[1] = Math.floor(tile[1]);
tile[2] = Math.floor(tile[2]);
Expand All @@ -70,8 +72,9 @@ export function pointToLocalTile(scale: number, x: number, y: number, alt: numbe

/**
* Get the precise fractional tile location for a point at a zoom level
* Set `clamp` to `true` to prevent out-of-bounds coordinates
*/
export function pointToLocalTileFraction(scale: number, xMeters: number, yMeters: number, altMeters: number, z: number): XYFZTile {
export function pointToLocalTileFraction(scale: number, xMeters: number, yMeters: number, altMeters: number, z: number, clamp?: boolean): XYFZTile {
const z2 = Math.pow(2, z);
// const z2_1 = Math.pow(2, z - 1);

Expand All @@ -80,13 +83,20 @@ export function pointToLocalTileFraction(scale: number, xMeters: number, yMeters
// const x = (z2 * (xMeters / scale)) + z2_1;
// const y = (z2 * (yMeters / scale)) + z2_1;

const x = z2 * (xMeters / scale);
const y = z2 * (yMeters / scale);
const f = z2 * (altMeters / scale);
let x = z2 * (xMeters / scale);
let y = z2 * (yMeters / scale);
let f = z2 * (altMeters / scale);

// Detect out-of-bounds coordinates
if (x < 0 || x >= z2 || y < 0 || y >= z2 || f < 0 || f >= z2) {
throw new Error(`Point out of bounds: (${xMeters}, ${yMeters}, ${altMeters}) with scale ${scale}m (Point would have been (x=${x}, y=${y}, f=${f}, z=${z}) and maximum for this zoom is ${z2}).`);
if (!clamp) {
// Detect out-of-bounds coordinates
if (x < 0 || x >= z2 || y < 0 || y >= z2 || f < 0 || f >= z2) {
throw new Error(`Point out of bounds: (${xMeters}, ${yMeters}, ${altMeters}) with scale ${scale}m (Point would have been (x=${x}, y=${y}, f=${f}, z=${z}) and maximum for this zoom is ${z2}).`);
}
} else {
// Clamp to bounds
x = Math.max(0, Math.min(z2 - 1, x));
y = Math.max(0, Math.min(z2 - 1, y));
f = Math.max(0, Math.min(z2 - 1, f));
}
return [x, y, f, z];
}
1 change: 1 addition & 0 deletions src/local_spatial_id.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ export class LocalSpatialId {
this.namespace.scale,
input,
(typeof zoom !== 'undefined') ? zoom : DEFAULT_ZOOM,
true, // clamp to valid coordinates
);
}

Expand Down

0 comments on commit 2abe481

Please sign in to comment.