declare const L: any; import { PMTiles, Source } from "./index"; export const leafletRasterLayer = (source: PMTiles, options: any) => { const cls = L.GridLayer.extend({ createTile: function (coord: any, done: any) { if (!this.pmtilesHeaderLoaded) { source.getHeader().then((h) => { // Unfortunately this needs to call into private leaflet properties // and might break in future versions // because layer creation is synchronous and these are specified at // initialization time // TODO: set bounds? if (this.options.maxNativeZoom == undefined) { this.options.minNativeZoom = h.minZoom; } if (this.options.maxNativeZoom == undefined) { this.options.maxNativeZoom = h.maxZoom; } }); this.pmtilesHeaderLoaded = true; } const el: any = document.createElement("img"); const controller = new AbortController(); const signal = controller.signal; el.cancel = () => { controller.abort(); }; source .getZxy(coord.z, coord.x, coord.y, signal) .then((arr) => { if (arr) { const blob = new Blob([arr.data]); const imageUrl = window.URL.createObjectURL(blob); el.src = imageUrl; el.cancel = null; done(null, el); } }) .catch((e) => { if (e.name !== "AbortError") { throw e; } }); return el; }, _removeTile: function (key: string) { const tile = this._tiles[key]; if (!tile) { return; } if (tile.el.cancel) tile.el.cancel(); tile.el.width = 0; tile.el.height = 0; tile.el.deleted = true; L.DomUtil.remove(tile.el); delete this._tiles[key]; this.fire("tileunload", { tile: tile.el, coords: this._keyToTileCoords(key), }); }, }); return new cls(options); }; export class Protocol { tiles: Map; constructor() { this.tiles = new Map(); } add(p: PMTiles) { this.tiles.set(p.source.getKey(), p); } get(url: string) { return this.tiles.get(url); } tile = (params: any, callback: any) => { if (params.type == "json") { const pmtiles_url = params.url.substr(10); let instance = this.tiles.get(pmtiles_url); if (!instance) { instance = new PMTiles(pmtiles_url); this.tiles.set(pmtiles_url, instance); } instance.getHeader().then((h) => { const tilejson = { tiles: [params.url + "/{z}/{x}/{y}"], minzoom: h.minZoom, maxzoom: h.maxZoom, }; callback(null, tilejson, null, null); }); return { cancel: () => {}, }; } else { const re = new RegExp(/pmtiles:\/\/(.+)\/(\d+)\/(\d+)\/(\d+)/); const result = params.url.match(re); const pmtiles_url = result[1]; let instance = this.tiles.get(pmtiles_url); if (!instance) { instance = new PMTiles(pmtiles_url); this.tiles.set(pmtiles_url, instance); } const z = result[2]; const x = result[3]; const y = result[4]; const controller = new AbortController(); const signal = controller.signal; let cancel = () => { 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; } }); return { cancel: cancel, }; } }; }