UNPKG

@loaders.gl/pmtiles

Version:

Framework-independent loader for the pmtiles format

107 lines (106 loc) 4.02 kB
// loaders.gl // SPDX-License-Identifier: MIT // Copyright (c) vis.gl contributors import { DataSource, resolvePath } from '@loaders.gl/loader-utils'; import { ImageLoader } from '@loaders.gl/images'; import { MVTLoader } from '@loaders.gl/mvt'; import * as pmtiles from 'pmtiles'; const { PMTiles } = pmtiles; import { parsePMTilesHeader } from "./lib/parse-pmtiles.js"; import { BlobSource } from "./lib/blob-source.js"; const VERSION = '1.0.0'; /** * Creates vector tile data sources for PMTiles urls or blobs */ export const PMTilesSource = { name: 'PMTiles', id: 'pmtiles', module: 'pmtiles', version: VERSION, extensions: ['pmtiles'], mimeTypes: ['application/octet-stream'], options: { url: undefined, pmtiles: {} }, type: 'pmtiles', fromUrl: true, fromBlob: true, testURL: (url) => url.endsWith('.pmtiles'), createDataSource: (url, props) => new PMTilesTileSource(url, props) }; /** * A PMTiles data source * @note Can be either a raster or vector tile source depending on the contents of the PMTiles file. */ export class PMTilesTileSource extends DataSource { data; props; mimeType = null; pmtiles; metadata; constructor(data, props) { super(props); this.props = props; const url = typeof data === 'string' ? resolvePath(data) : new BlobSource(data, 'pmtiles'); this.data = data; this.pmtiles = new PMTiles(url); this.getTileData = this.getTileData.bind(this); this.metadata = this.getMetadata(); } async getSchema() { return { fields: [], metadata: {} }; } async getMetadata() { const pmtilesHeader = await this.pmtiles.getHeader(); const pmtilesMetadata = (await this.pmtiles.getMetadata()) || {}; const metadata = parsePMTilesHeader(pmtilesHeader, pmtilesMetadata, { includeFormatHeader: false }, this.loadOptions); // Add additional attribution if necessary if (this.props.attributions) { metadata.attributions = [...this.props.attributions, ...(metadata.attributions || [])]; } if (metadata?.tileMIMEType) { this.mimeType = metadata?.tileMIMEType; } // TODO - do we need to allow tileSize to be overridden? Some PMTiles examples seem to suggest it. return metadata; } async getTile(tileParams) { const { x, y, z } = tileParams; const rangeResponse = await this.pmtiles.getZxy(z, x, y); const arrayBuffer = rangeResponse?.data; if (!arrayBuffer) { // console.error('No arrayBuffer', tileParams); return null; } return arrayBuffer; } // Tile Source interface implementation: deck.gl compatible API // TODO - currently only handles image tiles, not vector tiles async getTileData(tileParams) { const { x, y, z } = tileParams.index; const metadata = await this.metadata; switch (metadata.tileMIMEType) { case 'application/vnd.mapbox-vector-tile': return await this.getVectorTile({ x, y, z, layers: [] }); default: return await this.getImageTile({ x, y, z, layers: [] }); } } // ImageTileSource interface implementation async getImageTile(tileParams) { const arrayBuffer = await this.getTile(tileParams); return arrayBuffer ? await ImageLoader.parse(arrayBuffer, this.loadOptions) : null; } // VectorTileSource interface implementation async getVectorTile(tileParams) { const arrayBuffer = await this.getTile(tileParams); const loadOptions = { shape: 'geojson-table', mvt: { coordinates: 'wgs84', tileIndex: { x: tileParams.x, y: tileParams.y, z: tileParams.z }, ...this.loadOptions?.mvt }, ...this.loadOptions }; return arrayBuffer ? await MVTLoader.parse(arrayBuffer, loadOptions) : null; } }