Skip to content

Commit

Permalink
feat: log out center of xyz tile so that we can plot it on a map easily
Browse files Browse the repository at this point in the history
  • Loading branch information
blacha committed Sep 30, 2019
1 parent 7c32821 commit 0cc380d
Show file tree
Hide file tree
Showing 7 changed files with 93 additions and 24 deletions.
2 changes: 2 additions & 0 deletions packages/lambda-xyz/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,9 @@ export async function handleRequest(event: ALBEvent, context: Context, logger: t
if (pathMatch == null) {
return makeResponse(404, 'Path not found');
}
const latLon = tile256.projection.getLatLonCenterFromTile(pathMatch.x, pathMatch.y, pathMatch.z);

logger.info({ latLon }, 'RenderTile');
const tiffs = await Promise.all(Tiffs);
const buffer = await tile256.tile(tiffs, pathMatch.x, pathMatch.y, pathMatch.z, logger);

Expand Down
1 change: 1 addition & 0 deletions packages/shared/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
},
"dependencies": {
"aws-sdk": "^2.508.0",
"global-mercator": "^3.1.0",
"ulid": "^2.3.0"
},
"devDependencies": {
Expand Down
26 changes: 13 additions & 13 deletions packages/shared/src/__test__/bounds.tile.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { Projection } from '../projection';
export function approxEqual(numA: number, numB: number, text: string, variance = 0.001): void {
const diff = Math.abs(numA - numB);
if (diff > variance) {
throw new Error(`${text} (${numA - numB} < ${variance})`);
throw new Error(`${text} (${numA} vs ${numB}) should be less than ${variance}`);
}
}
export function approxBounds(boundsA: Bounds | null, boundsB: Bounds | null): void {
Expand Down Expand Up @@ -32,23 +32,23 @@ describe('TilingBounds', () => {
const expectedBaseSize = Bounds.fromJson({ width: 9.53125, height: 13, y: 153.65625, x: 246.14062500000006 });

it('should tile 0,0,0', () => {
const bounds = projection.getPixelBoundsFromMeters(tifBoundingBox, 0);
const bounds = projection.getPixelsBoundsFromMeters(tifBoundingBox, 0);
approxBounds(bounds, expectedBaseSize);

const screenBounds = projection.getPixelsFromXYZ(0, 0);
const screenBounds = projection.getPixelsFromTile(0, 0);
const intersection = bounds.intersection(screenBounds);

approxBounds(intersection, expectedBaseSize);
});

it('should tile 1,1,1', () => {
const [x, y, z] = [1, 1, 1];
const bounds = projection.getPixelBoundsFromMeters(tifBoundingBox, z);
const bounds = projection.getPixelsBoundsFromMeters(tifBoundingBox, z);
const expectedBaseSizeScaled = expectedBaseSize.scale(2, 2);

approxBounds(bounds, expectedBaseSizeScaled);

const screenBounds = projection.getPixelsFromXYZ(x, y);
const screenBounds = projection.getPixelsFromTile(x, y);
const intersection = bounds.intersection(screenBounds);

approxBounds(intersection, expectedBaseSizeScaled);
Expand All @@ -66,13 +66,13 @@ describe('TilingBounds', () => {
*/
it('should tile [15, 9, 4] & [15, 10, 4]', () => {
const [x, z] = [15, 4];
const bounds = projection.getPixelBoundsFromMeters(tifBoundingBox, z);
const bounds = projection.getPixelsBoundsFromMeters(tifBoundingBox, z);
const expectedBaseSizeScaled = expectedBaseSize.scale(2 ** z, 2 ** z);

approxBounds(bounds, expectedBaseSizeScaled);

const screenBounds9 = projection.getPixelsFromXYZ(x, 9);
const screenBounds10 = projection.getPixelsFromXYZ(x, 10);
const screenBounds9 = projection.getPixelsFromTile(x, 9);
const screenBounds10 = projection.getPixelsFromTile(x, 10);
expect(screenBounds9.toJson()).toEqual({ width: 256, height: 256, y: 2304, x: 3840 });
expect(screenBounds10.toJson()).toEqual({ width: 256, height: 256, y: 2560, x: 3840 });

Expand Down Expand Up @@ -109,13 +109,13 @@ describe('TilingBounds', () => {
const z = 5;

const tileBounds = new Bounds(30, 19, 1, 1);
const bounds = projection.getPixelBoundsFromMeters(tifBoundingBox, z);
const bounds = projection.getPixelsBoundsFromMeters(tifBoundingBox, z);
const expectedBaseSizeScaled = expectedBaseSize.scale(2 ** z, 2 ** z);

approxBounds(bounds, expectedBaseSizeScaled);

const screenTopLeft = projection.getPixelsFromXYZ(tileBounds.x, tileBounds.y);
const screenTopRight = projection.getPixelsFromXYZ(tileBounds.right, tileBounds.y);
const screenTopLeft = projection.getPixelsFromTile(tileBounds.x, tileBounds.y);
const screenTopRight = projection.getPixelsFromTile(tileBounds.right, tileBounds.y);

expect(screenTopLeft.toJson()).toEqual({ width: 256, height: 256, y: 4864, x: 7680 });
expect(screenTopRight.toJson()).toEqual({ width: 256, height: 256, y: 4864, x: 7936 });
Expand All @@ -131,8 +131,8 @@ describe('TilingBounds', () => {
expect(intersectionTopLeft.height).toEqual(203);
expect(intersectionTopRight.height).toEqual(203);

const screenBottomLeft = projection.getPixelsFromXYZ(tileBounds.x, tileBounds.bottom);
const screenBottomRight = projection.getPixelsFromXYZ(tileBounds.right, tileBounds.bottom);
const screenBottomLeft = projection.getPixelsFromTile(tileBounds.x, tileBounds.bottom);
const screenBottomRight = projection.getPixelsFromTile(tileBounds.right, tileBounds.bottom);

expect(screenBottomLeft.toJson()).toEqual({ width: 256, height: 256, y: 5120, x: 7680 });
expect(screenBottomRight.toJson()).toEqual({ width: 256, height: 256, y: 5120, x: 7936 });
Expand Down
38 changes: 38 additions & 0 deletions packages/shared/src/__test__/projection.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/* eslint-disable @typescript-eslint/explicit-function-return-type */
import { LatLon, Projection } from '../projection';
import { approxEqual } from './bounds.tile.test';

const Wgs84Bound = { lat: 85.0511287798066, lon: 180 };

describe('Projection', () => {
const proj256 = new Projection(256);
const proj512 = new Projection(512);

it('should create tile sized bounds', () => {
const bounds256 = proj256.getPixelsFromTile(1, 2);
expect(bounds256.toJson()).toEqual({ x: 256, y: 512, width: 256, height: 256 });

const bounds512 = proj512.getPixelsFromTile(1, 2);
expect(bounds512.toJson()).toEqual({ x: 512, y: 1024, width: 512, height: 512 });
});

it('should get center of tile', () => {
const latLon0 = proj256.getLatLonCenterFromTile(0, 0, 0);
expect(latLon0).toEqual({ lat: 0, lon: 0 });

const latLonA = proj256.getLatLonCenterFromTile(0, 0, 1);
const latLonB = proj256.getLatLonCenterFromTile(0, 1, 1);
const latLonC = proj256.getLatLonCenterFromTile(1, 1, 1);
const latLonD = proj256.getLatLonCenterFromTile(1, 0, 1);

function compareLatLon(latLonA: LatLon, latLonB: LatLon) {
approxEqual(latLonA.lat, latLonB.lat, 'lat', 0.0001);
approxEqual(latLonA.lon, latLonB.lon, 'lon', 0.0001);
}

compareLatLon(latLonA, { lat: Wgs84Bound.lat / 2, lon: -Wgs84Bound.lon / 2 });
compareLatLon(latLonB, { lat: -Wgs84Bound.lat / 2, lon: -Wgs84Bound.lon / 2 });
compareLatLon(latLonC, { lat: -Wgs84Bound.lat / 2, lon: Wgs84Bound.lon / 2 });
compareLatLon(latLonD, { lat: Wgs84Bound.lat / 2, lon: Wgs84Bound.lon / 2 });
});
});
36 changes: 28 additions & 8 deletions packages/shared/src/projection.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
import { Bounds, Point } from './bounds';
import * as Mercator from 'global-mercator';

export interface LatLon {
lat: number;
lon: number;
}

export class Projection {
/** Size of the earth ESPG:900913 constant */
Expand All @@ -24,21 +30,35 @@ export class Projection {
}

/** Convert a XYZ tile into a screen bounding box */
public getPixelsFromXYZ(x: number, y: number): Bounds {
public getPixelsFromTile(x: number, y: number): Bounds {
return new Bounds(x * this.tileSize, y * this.tileSize, this.tileSize, this.tileSize);
}

/**
* Convert a XYZ tile into the raster bounds for the tile
* @param x tile X offset
* @param y tile Y offset
* @param tX tile X offset
* @param tY tile Y offset
* @param zoom WebMercator zoom level
*/
public getPixelsFromMeters(x: number, y: number, zoom: number): Point {
public getPixelsFromMeters(tX: number, tY: number, zoom: number): Point {
const res = this.getResolution(zoom);
const px = (x + Projection.OriginShift) / res;
const py = (y + Projection.OriginShift) / res;
return { x: px, y: py };
const pX = (tX + Projection.OriginShift) / res;
const pY = (tY + Projection.OriginShift) / res;
return { x: pX, y: pY };
}

/**
* Get the center of a XYZ tile in LatLon
* @param tX Tile X offset
* @param tY Tile Y offset
* @param zoom
*/
public getLatLonCenterFromTile(tX: number, tY: number, zoom: number): LatLon {
const [minX, minY, maxX, maxY] = Mercator.googleToBBox([tX, tY, zoom]);
return {
lat: (maxY + minY) / 2,
lon: (maxX + minX) / 2,
};
}

/**
Expand All @@ -47,7 +67,7 @@ export class Projection {
* @param extent Extent in meters in the format of [minX,minY,maxX,maxY]
* @param zoom Web mercator zoom level
*/
public getPixelBoundsFromMeters(extent: [number, number, number, number], zoom: number): Bounds {
public getPixelsBoundsFromMeters(extent: [number, number, number, number], zoom: number): Bounds {
const upperLeftMeters = this.getPixelsFromMeters(extent[0], -extent[3], zoom);
const lowerRightMeters = this.getPixelsFromMeters(extent[2], -extent[1], zoom);
return Bounds.fromUpperLeftLowerRight(upperLeftMeters, lowerRightMeters);
Expand Down
9 changes: 6 additions & 3 deletions packages/tiler/src/tiler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,9 +53,9 @@ export class Tiler {
protected getRasterTiffIntersection(tiff: CogTiff, x: number, y: number, z: number): RasterPixelBounds | null {
const extentMeters = tiff.images[0].bbox;
/** Raster pixels of the output tile */
const screenBoundsPx = this.projection.getPixelsFromXYZ(x, y);
const screenBoundsPx = this.projection.getPixelsFromTile(x, y);
/** Raster pixels of the input geotiff */
const tiffBoundsPx = this.projection.getPixelBoundsFromMeters(extentMeters, z);
const tiffBoundsPx = this.projection.getPixelsBoundsFromMeters(extentMeters, z);

/** Raster pixels that need to be filled by this tiff */
const intersectionPx = tiffBoundsPx.intersection(screenBoundsPx);
Expand Down Expand Up @@ -137,7 +137,10 @@ export class Tiler {
if (rasterBounds == null) {
return null;
}
logger.info({ x, y, z, tiff: tiff.source.name, inBounds: true, duration: Date.now() - startIntersectionTime }, 'TiffBoundsCheck');
logger.info(
{ x, y, z, tiff: tiff.source.name, inBounds: true, duration: Date.now() - startIntersectionTime },
'TiffBoundsCheck',
);
// Find the best internal overview tiff to use with the desired XYZ resolution
const targetResolution = this.projection.getResolution(z);
const img = tiff.getImageByResolution(targetResolution);
Expand Down
5 changes: 5 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -4363,6 +4363,11 @@ global-dirs@^0.1.1:
dependencies:
ini "^1.3.4"

global-mercator@^3.1.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/global-mercator/-/global-mercator-3.1.0.tgz#011b37862f1ca858400d3e97a4e88f527148af2a"
integrity sha512-Ws/8nyCHDFzpDQjfr6fcbWY+yU/Oh6rk7+WXNr/pZl7O1WCktAnMZy+9wpJ2stNCqM2SWwvZoqWbIzTiFN++2A==

global-modules@2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/global-modules/-/global-modules-2.0.0.tgz#997605ad2345f27f51539bea26574421215c7780"
Expand Down

0 comments on commit 0cc380d

Please sign in to comment.