Skip to content

Commit

Permalink
add getTileJson method to PMTiles class [#239, #247]
Browse files Browse the repository at this point in the history
  • Loading branch information
bdon committed Sep 18, 2024
1 parent 6ed85a2 commit 3188c7a
Show file tree
Hide file tree
Showing 5 changed files with 92 additions and 56 deletions.
47 changes: 47 additions & 0 deletions js/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,15 @@ export interface Entry {
runLength: number;
}

interface MetadataLike {
attribution?: string;
name?: string;
version?: string;
// biome-ignore lint: TileJSON spec
vector_layers?: string;
description?: string;
}

/**
* Enum representing a compression algorithm used.
* 0 = unknown compression, for if you must use a different or unspecified algorithm.
Expand Down Expand Up @@ -212,6 +221,15 @@ export enum TileType {
Avif = 5,
}

export function tileTypeExt(t: TileType): string {
if (t === TileType.Mvt) return ".mvt";
if (t === TileType.Png) return ".png";
if (t === TileType.Jpeg) return ".jpg";
if (t === TileType.Webp) return ".webp";
if (t === TileType.Avif) return ".avif";
return "";
}

const HEADER_SIZE_BYTES = 127;

/**
Expand Down Expand Up @@ -1056,4 +1074,33 @@ export class PMTiles {
throw e;
}
}

/**
* Return TileJSON 3.0.0: https://github.com/mapbox/tilejson-spec
*
* baseTilesURL is the URL, excluding the suffix /{z}/{x}/{y}.{ext}.
* For example, if the desired tiles URL is http://example.com/tileset/{z}/{x}/{y}.mvt,
* the baseTilesURL should be https://example.com/tileset
*/
async getTileJson(baseTilesUrl: string): Promise<unknown> {
const header = await this.getHeader();
const metadata = (await this.getMetadata()) as MetadataLike;
const ext = tileTypeExt(header.tileType);

return {
tilejson: "3.0.0",
scheme: "xyz",
tiles: [`${baseTilesUrl}/{z}/{x}/{y}${ext}`],
// biome-ignore lint: TileJSON spec
vector_layers: metadata.vector_layers,
attribution: metadata.attribution,
description: metadata.description,
name: metadata.name,
version: metadata.version,
bounds: [header.minLon, header.minLat, header.maxLon, header.maxLat],
center: [header.centerLon, header.centerLat, header.centerZoom],
minzoom: header.minZoom,
maxzoom: header.maxZoom,
};
}
}
39 changes: 39 additions & 0 deletions js/test/v3.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,12 @@ import {
RangeResponse,
SharedPromiseCache,
Source,
TileType,
findTile,
getUint64,
readVarint,
tileIdToZxy,
tileTypeExt,
zxyToTileId,
} from "../index";

Expand Down Expand Up @@ -376,3 +378,40 @@ test("pmtiles get metadata", async () => {
});

// echo '{"type":"Polygon","coordinates":[[[0,0],[0,1],[1,0],[0,0]]]}' | ./tippecanoe -zg -o test_fixture_2.pmtiles

test("get file extension", async () => {
assert.equal("", tileTypeExt(TileType.Unknown));
assert.equal(".mvt", tileTypeExt(TileType.Mvt));
assert.equal(".png", tileTypeExt(TileType.Png));
assert.equal(".jpg", tileTypeExt(TileType.Jpeg));
assert.equal(".webp", tileTypeExt(TileType.Webp));
assert.equal(".avif", tileTypeExt(TileType.Avif));
});

interface TileJsonLike {
tilejson: string;
scheme: string;
tiles: string[];
description?: string;
name?: string;
attribution?: string;
version?: string;
}

test("pmtiles get TileJSON", async () => {
const source = new TestNodeFileSource(
"test/data/test_fixture_1.pmtiles",
"1"
);
const p = new PMTiles(source);
const tilejson = (await p.getTileJson(
"https://example.com/foo"
)) as TileJsonLike;
assert.equal("3.0.0", tilejson.tilejson);
assert.equal("xyz", tilejson.scheme);
assert.equal("https://example.com/foo/{z}/{x}/{y}.mvt", tilejson.tiles[0]);
assert.equal(undefined, tilejson.attribution);
assert.equal("test_fixture_1.pmtiles", tilejson.description);
assert.equal("test_fixture_1.pmtiles", tilejson.name);
assert.equal("2", tilejson.version);
});
12 changes: 2 additions & 10 deletions serverless/aws/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import {
Source,
TileType,
} from "../../../js/index";
import { pmtiles_path, tileJSON, tile_path } from "../../shared/index";
import { pmtiles_path, tile_path } from "../../shared/index";

import { createHash } from "crypto";
import zlib from "zlib";
Expand Down Expand Up @@ -177,15 +177,7 @@ export const handlerRaw = async (
}
headers["Content-Type"] = "application/json";

const t = tileJSON(
header,
await p.getMetadata(),
process.env.PUBLIC_HOSTNAME ||
event.headers["x-distribution-domain-name"] ||
"",
name
);

const t = p.getTileJson(`https://${process.env.PUBLIC_HOSTNAME || event.headers["x-distribution-domain-name"] || ""}/${name}`);
return apiResp(200, JSON.stringify(t), false, headers);
}

Expand Down
11 changes: 3 additions & 8 deletions serverless/cloudflare/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import {
Source,
TileType,
} from "../../../js/index";
import { pmtiles_path, tileJSON, tile_path } from "../../shared/index";
import { pmtiles_path, tile_path } from "../../shared/index";

interface Env {
// biome-ignore lint: config name
Expand Down Expand Up @@ -159,14 +159,9 @@ export default {

if (!tile) {
cacheableHeaders.set("Content-Type", "application/json");

const t = tileJSON(
pHeader,
await p.getMetadata(),
env.PUBLIC_HOSTNAME || url.hostname,
name
const t = p.getTileJson(
`https://${env.PUBLIC_HOSTNAME || url.hostname}/${name}`
);

return cacheableResponse(JSON.stringify(t), cacheableHeaders, 200);
}

Expand Down
39 changes: 1 addition & 38 deletions serverless/shared/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import { Header, TileType } from "../../js/index";

export const pmtiles_path = (name: string, setting?: string): string => {
if (setting) {
return setting.replaceAll("{name}", name);
Expand Down Expand Up @@ -35,39 +33,4 @@ export const tile_path = (
}

return { ok: false, name: "", tile: [0, 0, 0], ext: "" };
};

export const tileJSON = (
header: Header,
metadata: any,
hostname: string,
tileset_name: string
) => {
let ext = "";
if (header.tileType === TileType.Mvt) {
ext = ".mvt";
} else if (header.tileType === TileType.Png) {
ext = ".png";
} else if (header.tileType === TileType.Jpeg) {
ext = ".jpg";
} else if (header.tileType === TileType.Webp) {
ext = ".webp";
} else if (header.tileType === TileType.Avif) {
ext = ".avif";
}

return {
tilejson: "3.0.0",
scheme: "xyz",
tiles: ["https://" + hostname + "/" + tileset_name + "/{z}/{x}/{y}" + ext],
vector_layers: metadata.vector_layers,
attribution: metadata.attribution,
description: metadata.description,
name: metadata.name,
version: metadata.version,
bounds: [header.minLon, header.minLat, header.maxLon, header.maxLat],
center: [header.centerLon, header.centerLat, header.centerZoom],
minzoom: header.minZoom,
maxzoom: header.maxZoom,
};
};
};

0 comments on commit 3188c7a

Please sign in to comment.