diff --git a/js/adapters.ts b/js/adapters.ts index c22e5eb..a6035fe 100644 --- a/js/adapters.ts +++ b/js/adapters.ts @@ -1,5 +1,5 @@ declare const L: any; -import { PMTiles, Source } from "./index"; +import { PMTiles, Source, TileType } from "./index"; export const leafletRasterLayer = (source: PMTiles, options: any) => { const cls = L.GridLayer.extend({ @@ -51,6 +51,28 @@ export const leafletRasterLayer = (source: PMTiles, options: any) => { return new cls(options); }; +// copied from MapLibre /util/ajax.ts +type RequestParameters = { + url: string; + headers?: any; + method?: "GET" | "POST" | "PUT"; + body?: string; + type?: "string" | "json" | "arrayBuffer"; + credentials?: "same-origin" | "include"; + collectResourceTiming?: boolean; +}; + +type ResponseCallback = ( + error?: Error | null, + data?: any | null, + cacheControl?: string | null, + expires?: string | null +) => void; + +type Cancelable = { + cancel: () => void; +}; + export class Protocol { tiles: Map; @@ -66,7 +88,10 @@ export class Protocol { return this.tiles.get(url); } - tile = (params: any, callback: any) => { + tile = ( + params: RequestParameters, + callback: ResponseCallback + ): Cancelable => { if (params.type == "json") { const pmtiles_url = params.url.substr(10); let instance = this.tiles.get(pmtiles_url); @@ -75,6 +100,8 @@ export class Protocol { this.tiles.set(pmtiles_url, instance); } + // TODO: create vector_layers if present to return valid TileJSON + instance.getHeader().then((h) => { const tilejson = { tiles: [params.url + "/{z}/{x}/{y}"], @@ -83,13 +110,18 @@ export class Protocol { }; callback(null, tilejson, null, null); }); - return { cancel: () => {}, }; } else { const re = new RegExp(/pmtiles:\/\/(.+)\/(\d+)\/(\d+)\/(\d+)/); const result = params.url.match(re); + if (!result) { + throw new Error("Invalid PMTiles protocol URL"); + return { + cancel: () => {}, + }; + } const pmtiles_url = result[1]; let instance = this.tiles.get(pmtiles_url); @@ -107,25 +139,31 @@ export class Protocol { controller.abort(); }; - instance - .getZxy(+z, +x, +y, signal) - .then((resp) => { - if (resp) { - callback( - null, - new Uint8Array(resp.data), - resp.cacheControl, - resp.expires - ); - } else { - callback(null, new Uint8Array(), null, null); - } - }) - .catch((e) => { - if (e.name !== "AbortError") { - throw e; - } - }); + instance.getHeader().then((header) => { + instance! + .getZxy(+z, +x, +y, signal) + .then((resp) => { + if (resp) { + callback( + null, + new Uint8Array(resp.data), + resp.cacheControl, + resp.expires + ); + } else { + if (header.tileType == TileType.Mvt) { + callback(null, new Uint8Array(), null, null); + } else { + callback(null, null, null, null); + } + } + }) + .catch((e) => { + if ((e as Error).name !== "AbortError") { + throw e; + } + }); + }); return { cancel: cancel, };