import fs from 'node:fs'; import PMTiles from 'pmtiles'; import { isValidHttpUrl } from './utils.js'; const PMTilesFileSource = class { constructor(fd) { this.fd = fd; } getKey() { return this.fd; } async getBytes(offset, length) { const buffer = Buffer.alloc(length); await ReadFileBytes(this.fd, buffer, offset); return { data: BufferToArrayBuffer(buffer) }; } }; const ReadFileBytes = async (fd, buffer, offset) => { return new Promise((resolve, reject) => { fs.read(fd, buffer, 0, buffer.length, offset, (err) => { if (err) { return reject(err); } resolve(); }); }); }; export const PMtilesOpen = (FilePath) => { let pmtiles = undefined; let fd = undefined; if (isValidHttpUrl(FilePath)) { const source = new PMTiles.FetchSource(FilePath); pmtiles = new PMTiles.PMTiles(source); } else { fd = fs.openSync(FilePath, 'r'); const source = new PMTilesFileSource(fd); pmtiles = new PMTiles.PMTiles(source); } return { pmtiles: pmtiles, fd: fd }; }; export const PMtilesClose = (fd) => { fs.closeSync(fd); }; export const GetPMtilesInfo = async (pmtiles) => { const header = await pmtiles.getHeader(); const metadata = await pmtiles.getMetadata(); //Add missing metadata from header const bounds = [header.minLon, header.minLat, header.maxLon, header.maxLat]; const center = [header.centerLon, header.centerLat, header.centerZoom]; metadata['bounds'] = bounds; metadata['center'] = center; metadata['minzoom'] = header.minZoom; metadata['maxzoom'] = header.maxZoom; metadata['format'] = GetPmtilesTileType(header.tileType).type; return metadata; }; export const GetPMtilesTile = async (pmtiles, z, x, y) => { const header = await pmtiles.getHeader(); const TileType = GetPmtilesTileType(header.tileType); let zxyTile = await pmtiles.getZxy(z, x, y); if (zxyTile && zxyTile.data) { zxyTile = ArrayBufferToBuffer(zxyTile.data); } else { zxyTile = undefined; } return { data: zxyTile, header: TileType.header }; }; const GetPmtilesTileType = (typenum) => { let head = {}; let tileType; switch (typenum) { case 0: tileType = 'Unknown'; break; case 1: tileType = 'pbf'; head['Content-Type'] = 'application/x-protobuf'; break; case 2: tileType = 'png'; head['Content-Type'] = 'image/png'; break; case 3: tileType = 'jpeg'; head['Content-Type'] = 'image/jpeg'; break; case 4: tileType = 'webp'; head['Content-Type'] = 'image/webp'; break; case 5: tileType = 'avif'; head['Content-Type'] = 'image/avif'; break; } return { type: tileType, header: head }; }; const BufferToArrayBuffer = (buffer) => { const arrayBuffer = new ArrayBuffer(buffer.length); const view = new Uint8Array(arrayBuffer); for (let i = 0; i < buffer.length; ++i) { view[i] = buffer[i]; } return arrayBuffer; }; const ArrayBufferToBuffer = (array_buffer) => { var buffer = Buffer.alloc(array_buffer.byteLength); var view = new Uint8Array(array_buffer); for (var i = 0; i < buffer.length; ++i) { buffer[i] = view[i]; } return buffer; };