mirror of
https://github.com/protomaps/PMTiles.git
synced 2026-02-04 10:51:07 +00:00
implement metadata and TileJSON-like responses for Cloudflare Workers [#169]
This commit is contained in:
@@ -31,9 +31,9 @@ test("parse tile default", () => {
|
|||||||
|
|
||||||
({ name, tile } = tile_path("/foo/11/22/33.pbf"));
|
({ name, tile } = tile_path("/foo/11/22/33.pbf"));
|
||||||
assert.strictEqual(name, "foo");
|
assert.strictEqual(name, "foo");
|
||||||
assert.strictEqual(tile[0], 11);
|
assert.strictEqual(tile![0], 11);
|
||||||
assert.strictEqual(tile[1], 22);
|
assert.strictEqual(tile![1], 22);
|
||||||
assert.strictEqual(tile[2], 33);
|
assert.strictEqual(tile![2], 33);
|
||||||
});
|
});
|
||||||
|
|
||||||
test("parse tile path setting", () => {
|
test("parse tile path setting", () => {
|
||||||
@@ -41,17 +41,17 @@ test("parse tile path setting", () => {
|
|||||||
"/foo/11/22/33.pbf",
|
"/foo/11/22/33.pbf",
|
||||||
"/{name}/{z}/{y}/{x}.{ext}"
|
"/{name}/{z}/{y}/{x}.{ext}"
|
||||||
);
|
);
|
||||||
assert.strictEqual(tile[1], 33);
|
assert.strictEqual(tile![1], 33);
|
||||||
assert.strictEqual(tile[2], 22);
|
assert.strictEqual(tile![2], 22);
|
||||||
assert.strictEqual(ext, "pbf");
|
assert.strictEqual(ext, "pbf");
|
||||||
({ ok, name, tile, ext } = tile_path(
|
({ ok, name, tile, ext } = tile_path(
|
||||||
"/tiles/foo/4/2/3.mvt",
|
"/tiles/foo/4/2/3.mvt",
|
||||||
"/tiles/{name}/{z}/{x}/{y}.{ext}"
|
"/tiles/{name}/{z}/{x}/{y}.{ext}"
|
||||||
));
|
));
|
||||||
assert.strictEqual(name, "foo");
|
assert.strictEqual(name, "foo");
|
||||||
assert.strictEqual(tile[0], 4);
|
assert.strictEqual(tile![0], 4);
|
||||||
assert.strictEqual(tile[1], 2);
|
assert.strictEqual(tile![1], 2);
|
||||||
assert.strictEqual(tile[2], 3);
|
assert.strictEqual(tile![2], 3);
|
||||||
assert.strictEqual(ext, "mvt");
|
assert.strictEqual(ext, "mvt");
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -70,3 +70,9 @@ test("parse tile path setting slash", () => {
|
|||||||
);
|
);
|
||||||
assert.strictEqual(name, "foo/bar");
|
assert.strictEqual(name, "foo/bar");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("parse tileset default", () => {
|
||||||
|
let { ok, name, tile, ext } = tile_path("/abcd.json");
|
||||||
|
assert.strictEqual(ok, true);
|
||||||
|
assert.strictEqual("abcd", name);
|
||||||
|
});
|
||||||
@@ -18,6 +18,7 @@ interface Env {
|
|||||||
ALLOWED_ORIGINS?: string;
|
ALLOWED_ORIGINS?: string;
|
||||||
PMTILES_PATH?: string;
|
PMTILES_PATH?: string;
|
||||||
TILE_PATH?: string;
|
TILE_PATH?: string;
|
||||||
|
TILESET_PATH?: string;
|
||||||
CACHE_MAX_AGE?: number;
|
CACHE_MAX_AGE?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -37,33 +38,57 @@ export const pmtiles_path = (name: string, setting?: string): string => {
|
|||||||
const TILE =
|
const TILE =
|
||||||
/^\/(?<NAME>[0-9a-zA-Z\/!\-_\.\*\'\(\)]+)\/(?<Z>\d+)\/(?<X>\d+)\/(?<Y>\d+).(?<EXT>[a-z]+)$/;
|
/^\/(?<NAME>[0-9a-zA-Z\/!\-_\.\*\'\(\)]+)\/(?<Z>\d+)\/(?<X>\d+)\/(?<Y>\d+).(?<EXT>[a-z]+)$/;
|
||||||
|
|
||||||
|
const TILESET = /^\/(?<NAME>[0-9a-zA-Z\/!\-_\.\*\'\(\)]+).json$/;
|
||||||
|
|
||||||
export const tile_path = (
|
export const tile_path = (
|
||||||
path: string,
|
path: string,
|
||||||
setting?: string
|
tile_setting?: string,
|
||||||
|
tileset_setting?: string
|
||||||
): {
|
): {
|
||||||
ok: boolean;
|
ok: boolean;
|
||||||
name: string;
|
name: string;
|
||||||
tile: [number, number, number];
|
tile?: [number, number, number];
|
||||||
ext: string;
|
ext: string;
|
||||||
} => {
|
} => {
|
||||||
let pattern = TILE;
|
let tile_pattern = TILE;
|
||||||
if (setting) {
|
if (tile_setting) {
|
||||||
// escape regex
|
// escape regex
|
||||||
setting = setting.replace(/[.*+?^$()|[\]\\]/g, "\\$&");
|
tile_setting = tile_setting.replace(/[.*+?^$()|[\]\\]/g, "\\$&");
|
||||||
setting = setting.replace("{name}", "(?<NAME>[0-9a-zA-Z/!-_.*'()]+)");
|
tile_setting = tile_setting.replace(
|
||||||
setting = setting.replace("{z}", "(?<Z>\\d+)");
|
"{name}",
|
||||||
setting = setting.replace("{x}", "(?<X>\\d+)");
|
"(?<NAME>[0-9a-zA-Z/!-_.*'()]+)"
|
||||||
setting = setting.replace("{y}", "(?<Y>\\d+)");
|
);
|
||||||
setting = setting.replace("{ext}", "(?<EXT>[a-z]+)");
|
tile_setting = tile_setting.replace("{z}", "(?<Z>\\d+)");
|
||||||
pattern = new RegExp(setting);
|
tile_setting = tile_setting.replace("{x}", "(?<X>\\d+)");
|
||||||
|
tile_setting = tile_setting.replace("{y}", "(?<Y>\\d+)");
|
||||||
|
tile_setting = tile_setting.replace("{ext}", "(?<EXT>[a-z]+)");
|
||||||
|
tile_pattern = new RegExp(tile_setting);
|
||||||
}
|
}
|
||||||
|
|
||||||
let match = path.match(pattern);
|
let tile_match = path.match(tile_pattern);
|
||||||
|
|
||||||
if (match) {
|
if (tile_match) {
|
||||||
const g = match.groups!;
|
const g = tile_match.groups!;
|
||||||
return { ok: true, name: g.NAME, tile: [+g.Z, +g.X, +g.Y], ext: g.EXT };
|
return { ok: true, name: g.NAME, tile: [+g.Z, +g.X, +g.Y], ext: g.EXT };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let tileset_pattern = TILESET;
|
||||||
|
if (tileset_setting) {
|
||||||
|
tileset_setting = tileset_setting.replace(/[.*+?^$()|[\]\\]/g, "\\$&");
|
||||||
|
tileset_setting = tileset_setting.replace(
|
||||||
|
"{name}",
|
||||||
|
"(?<NAME>[0-9a-zA-Z/!-_.*'()]+)"
|
||||||
|
);
|
||||||
|
tileset_pattern = new RegExp(tileset_setting);
|
||||||
|
}
|
||||||
|
|
||||||
|
let tileset_match = path.match(tileset_pattern);
|
||||||
|
|
||||||
|
if (tileset_match) {
|
||||||
|
const g = tileset_match.groups!;
|
||||||
|
return { ok: true, name: g.NAME, ext: "json" };
|
||||||
|
}
|
||||||
|
|
||||||
return { ok: false, name: "", tile: [0, 0, 0], ext: "" };
|
return { ok: false, name: "", tile: [0, 0, 0], ext: "" };
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -184,6 +209,53 @@ export default {
|
|||||||
const p = new PMTiles(source, CACHE, nativeDecompress);
|
const p = new PMTiles(source, CACHE, nativeDecompress);
|
||||||
try {
|
try {
|
||||||
const p_header = await p.getHeader();
|
const p_header = await p.getHeader();
|
||||||
|
|
||||||
|
if (!tile) {
|
||||||
|
const metadata = await p.getMetadata();
|
||||||
|
cacheable_headers.set("Content-Type", "application/json");
|
||||||
|
|
||||||
|
let ext = "";
|
||||||
|
if (p_header.tileType === TileType.Mvt) {
|
||||||
|
ext = ".mvt";
|
||||||
|
} else if (p_header.tileType === TileType.Png) {
|
||||||
|
ext = ".png";
|
||||||
|
} else if (p_header.tileType === TileType.Jpeg) {
|
||||||
|
ext = ".jpg";
|
||||||
|
} else if (p_header.tileType === TileType.Webp) {
|
||||||
|
ext = ".webp";
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: this needs to be based on the TILE_PATH setting
|
||||||
|
metadata.tiles = [
|
||||||
|
url.protocol +
|
||||||
|
"//" +
|
||||||
|
url.hostname +
|
||||||
|
"/" +
|
||||||
|
name +
|
||||||
|
"/{z}/{x}/{y}" +
|
||||||
|
ext,
|
||||||
|
];
|
||||||
|
metadata.bounds = [
|
||||||
|
p_header.minLon,
|
||||||
|
p_header.minLat,
|
||||||
|
p_header.maxLon,
|
||||||
|
p_header.maxLat,
|
||||||
|
];
|
||||||
|
metadata.center = [
|
||||||
|
p_header.centerLon,
|
||||||
|
p_header.centerLat,
|
||||||
|
p_header.centerZoom,
|
||||||
|
];
|
||||||
|
metadata.maxzoom = p_header.maxZoom;
|
||||||
|
metadata.minzoom = p_header.minZoom;
|
||||||
|
metadata.scheme = "xyz";
|
||||||
|
return cacheableResponse(
|
||||||
|
JSON.stringify(metadata),
|
||||||
|
cacheable_headers,
|
||||||
|
200
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if (tile[0] < p_header.minZoom || tile[0] > p_header.maxZoom) {
|
if (tile[0] < p_header.minZoom || tile[0] > p_header.maxZoom) {
|
||||||
return cacheableResponse(undefined, cacheable_headers, 404);
|
return cacheableResponse(undefined, cacheable_headers, 404);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user