UNPKG

s2-tools

Version:

A collection of geospatial tools primarily designed for WGS84, Web Mercator, and S2.

243 lines 7.29 kB
import { readVarint } from './varint'; /** * Enum representing a compression algorithm used. * 0 = unknown compression, for if you must use a different or unspecified algorithm. * 1 = no compression. * 2 = gzip * 3 = brotli * 4 = zstd */ export var Compression; (function (Compression) { /** unknown compression, for if you must use a different or unspecified algorithm. */ Compression[Compression["Unknown"] = 0] = "Unknown"; /** no compression. */ Compression[Compression["None"] = 1] = "None"; /** gzip. */ Compression[Compression["Gzip"] = 2] = "Gzip"; /** brotli. */ Compression[Compression["Brotli"] = 3] = "Brotli"; /** zstd. */ Compression[Compression["Zstd"] = 4] = "Zstd"; })(Compression || (Compression = {})); /** * Describe the type of tiles stored in the archive. * 0 is unknown/other, 1 is a vector tile spec. */ export var TileType; (function (TileType) { /** unknown/other. */ TileType[TileType["Unknown"] = 0] = "Unknown"; /** Vector tiles. */ TileType[TileType["Pbf"] = 1] = "Pbf"; /** Image tiles. */ TileType[TileType["Png"] = 2] = "Png"; /** Image tiles. */ TileType[TileType["Jpeg"] = 3] = "Jpeg"; /** Image tiles. */ TileType[TileType["Webp"] = 4] = "Webp"; /** Image tiles. */ TileType[TileType["Avif"] = 5] = "Avif"; })(TileType || (TileType = {})); export const HEADER_SIZE_BYTES = 127; export const ROOT_SIZE = 16_384; /** * Rotate a point by n degrees. * @param n - the rotation size * @param xy - the point * @param rx - the x rotation * @param ry - the y rotation */ function rotate(n, xy, rx, ry) { if (ry === 0) { if (rx === 1) { xy[0] = n - 1 - xy[0]; xy[1] = n - 1 - xy[1]; } const t = xy[0]; xy[0] = xy[1]; xy[1] = t; } } /** * Get the tile ID on the given level. * @param zoom - the zoom level * @param pos - the tile position * @returns - the tile */ function idOnLevel(zoom, pos) { const n = 2 ** zoom; let rx = pos; let ry = pos; let t = pos; const xy = [0, 0]; let s = 1; while (s < n) { rx = 1 & (t / 2); ry = 1 & (t ^ rx); rotate(s, xy, rx, ry); xy[0] += s * rx; xy[1] += s * ry; t = t / 4; s *= 2; } return [zoom, xy[0], xy[1]]; } const tzValues = [ 0, 1, 5, 21, 85, 341, 1365, 5461, 21845, 87381, 349525, 1398101, 5592405, 22369621, 89478485, 357913941, 1431655765, 5726623061, 22906492245, 91625968981, 366503875925, 1466015503701, 5864062014805, 23456248059221, 93824992236885, 375299968947541, 1501199875790165, ]; /** * Convert Z,X,Y to a Hilbert TileID. * @param zoom - the zoom level * @param x - the x coordinate * @param y - the y coordinate * @returns - the Hilbert encoded TileID */ export function zxyToTileID(zoom, x, y) { if (zoom > 26) { throw Error('Tile zoom level exceeds max safe number limit (26)'); } if (x > 2 ** zoom - 1 || y > 2 ** zoom - 1) { throw Error('tile x/y outside zoom level bounds'); } const acc = tzValues[zoom]; const n = 2 ** zoom; let rx = 0; let ry = 0; let d = 0; const xy = [x, y]; let s = n / 2; while (true) { rx = (xy[0] & s) > 0 ? 1 : 0; ry = (xy[1] & s) > 0 ? 1 : 0; d += s * s * ((3 * rx) ^ ry); rotate(s, xy, rx, ry); if (s <= 1) break; s = s / 2; } return acc + d; } /** * Convert a Hilbert TileID to Z,X,Y. * @param i - the encoded tile ID * @returns - the decoded Z,X,Y */ export function tileIDToZxy(i) { let acc = 0; for (let z = 0; z < 27; z++) { const numTiles = (0x1 << z) * (0x1 << z); if (acc + numTiles > i) { return idOnLevel(z, i - acc); } acc += numTiles; } throw Error('Tile zoom level exceeds max safe number limit (26)'); } /** * Low-level function for looking up a TileID or leaf directory inside a directory. * @param entries - the directory entries * @param tileID - the tile ID * @returns the entry associated with the tile, or null if not found */ export function findTile(entries, tileID) { let m = 0; let n = entries.length - 1; while (m <= n) { const k = (n + m) >> 1; const cmp = tileID - entries[k].tileID; if (cmp > 0) { m = k + 1; } else if (cmp < 0) { n = k - 1; } else { return entries[k]; } } // at this point, m > n if (n >= 0) { if (entries[n].runLength === 0) return entries[n]; if (tileID - entries[n].tileID < entries[n].runLength) return entries[n]; } return null; } /** * Parse raw header bytes into a Header object. * @param bytes - the raw header bytes * @returns the parsed header */ export function bytesToHeader(bytes) { const dv = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength); // if (dv.getUint16(0, true) !== 0x4d50) { // throw new Error('Wrong magic number for PMTiles archive'); // } return { specVersion: dv.getUint8(7), rootDirectoryOffset: getUint64(dv, 8), rootDirectoryLength: getUint64(dv, 16), jsonMetadataOffset: getUint64(dv, 24), jsonMetadataLength: getUint64(dv, 32), leafDirectoryOffset: getUint64(dv, 40), leafDirectoryLength: getUint64(dv, 48), tileDataOffset: getUint64(dv, 56), tileDataLength: getUint64(dv, 64), numAddressedTiles: getUint64(dv, 72), numTileEntries: getUint64(dv, 80), numTileContents: getUint64(dv, 88), clustered: dv.getUint8(96) === 1, internalCompression: dv.getUint8(97), tileCompression: dv.getUint8(98), tileType: dv.getUint8(99), minZoom: dv.getUint8(100), maxZoom: dv.getUint8(101), }; } /** * Deserialize a directory from a Uint8Array. * @param buffer - the buffer to deserialize * @returns - the deserialized entries */ export function deserializeDir(buffer) { const p = { buf: new Uint8Array(buffer), pos: 0 }; const numEntries = readVarint(p); const entries = []; let lastID = 0; for (let i = 0; i < numEntries; i++) { const v = readVarint(p); entries.push({ tileID: lastID + v, offset: 0, length: 0, runLength: 1 }); lastID += v; } // run lengths, lengths, and offsets for (let i = 0; i < numEntries; i++) entries[i].runLength = readVarint(p); for (let i = 0; i < numEntries; i++) entries[i].length = readVarint(p); for (let i = 0; i < numEntries; i++) { const v = readVarint(p); if (v === 0 && i > 0) { entries[i].offset = entries[i - 1].offset + entries[i - 1].length; } else { entries[i].offset = v - 1; } } return entries; } /** * Get a 64-bit number from a DataView * @param dv - a DataView * @param offset - the offset in the DataView * @returns - the decoded 64-bit number */ export function getUint64(dv, offset) { const wh = dv.getUint32(offset + 4, true); const wl = dv.getUint32(offset, true); return wh * 2 ** 32 + wl; } //# sourceMappingURL=pmtiles.js.map