UNPKG

@versatiles/google-cloud

Version:
86 lines (85 loc) 3.41 kB
import { Container as VersatilesContainer } from '@versatiles/container'; import { guessStyle } from '@versatiles/style'; import { readFileSync } from 'fs'; const filenamePreview = new URL('../../../static/preview.html', import.meta.url).pathname; const bufferPreview = readFileSync(filenamePreview); export class Versatiles { etag; #container; #header; #metadata; #url; constructor(container, header, metadata, url, etag) { this.#container = container; this.#header = header; this.#metadata = metadata; this.#url = url; this.etag = etag; } static async fromReader(reader, url, etag) { const container = new VersatilesContainer(reader); const header = await container.getHeader(); const metadata = await container.getMetadata() ?? ''; return new Versatiles(container, header, metadata, url, etag); } async serve(query, responder) { // Log serving versatiles if verbose mode is enabled responder.log(`serve versatiles query: ${JSON.stringify(query)}`); // Handle different queries: preview, meta.json, style.json, or tile queries switch (query) { case '?preview': await this.sendPreview(responder); return; case '?meta.json': await this.sendMeta(responder); return; case '?style.json': await this.sendStyle(responder); return; } // Extract tile coordinates from the query and serve the requested tile const match = /^\?(?<z>\d+)\/(?<x>\d+)\/(?<y>\d+)/.exec(query); if (match != null) { const { z, x, y } = match.groups; const coordinates = { x: parseInt(x, 10), y: parseInt(y, 10), z: parseInt(z, 10) }; await this.sendTile(responder, coordinates); return; } responder.error(400, 'get parameter must be "?preview", "?meta.json", "?style.json", or "?{z}/{x}/{y}"'); return; } async sendPreview(responder) { await responder.respond(bufferPreview, 'text/html', 'raw'); } async sendMeta(responder) { await responder.respond(this.#metadata, 'application/json', 'raw'); } async sendStyle(responder) { responder.log('respond with style.json'); try { const tileJson = JSON.parse(this.#metadata); tileJson.tiles = [`${this.#url}?{z}/{x}/{y}`]; const style = await guessStyle(tileJson, {}); await responder.respond(JSON.stringify(style), 'application/json', 'raw'); } catch (error) { const message = error instanceof Error ? error.message : String(error); responder.error(500, `server side error: ${message}`); } return; } async sendTile(responder, coordinates) { const { x, y, z } = coordinates; responder.log(`fetch tile x:${x}, y:${y}, z:${z}`); const tile = await this.#container.getTile(z, x, y); // Return error for invalid queries if (tile == null) { responder.error(204, `no map tile at ${z}/${x}/${y}`); } else { responder.log(`return tile ${z}/${x}/${y}`); await responder.respond(tile, this.#header.tileMime, this.#header.tileCompression); } return; } }