open-vector-tile
Version:
This library reads/writes Open Vector Tiles
164 lines • 6.11 kB
JavaScript
import { MapboxVectorLayer } from './mapbox/index.js';
import { BaseVectorLayer } from './base/index.js';
import { ColumnCacheReader, ColumnCacheWriter, GridData, ImageData, OVectorLayer, writeGridData, writeImageData, writeOVLayer, } from './open/index.js';
import { PbfReader, Pbf as Protobuf } from 'pbf-ts';
/**
* # Open Vector Tile
*
* ## Description
* A Vector Tile may parse either Mapbox or OpenVector Tile Layers
* The input is a Uint8Array that has encoded protobuffer messages.
*
* Types of layers include:
* - Vector data - vector points, lines, and polygons with 3D coordinates, properties, and/or m-values
* - Image data - raster data that is RGB(A) encoded
* - Grid data: data that has a max-min range, works much like an image but has floating/double
* precision point values for each point on the grid
*
* ## Usage
*
* ```ts
* const fs = from 'fs';
* import { VectorTile } from 'open-vector-tile';
*
* // assume you can read (.pbf | .mvt | .ovt)
* const fixture = fs.readFileSync('./x-y-z.vector.pbf');
* // Or load with bun:
* const fixture = await Bun.file('./x-y-z.vector.pbf').arrayBuffer();
* // load the protobuf parsing it directly
* const tile = new VectorTile(fixture);
*
* // VECTOR API:
*
* // example layer
* const { landuse } = tile.layers;
*
* // grab the first feature
* const firstFeature = landuse.feature(0);
* // grab the geometry
* const geometry = firstFeature.loadGeometry();
* // OR specifically ask for a geometry type
* const points = firstFeature.loadPoints();
* const lines = firstFeature.loadLines();
* const polys = firstFeature.loadPolys();
*
* // If you want to take advantage of the pre-tessellated and indexed geometries
* // and you're loading the data for a renderer, you can grab the pre-tessellated geometry
* const [flatGeometry, indices] = firstFeature.loadGeometryFlat();
*
* // IMAGE API
*
* // example layer
* const { satellite } = tile.images;
* // grab the image data
* const data = satellite.image(); // Uint8Array
*
* // GRIDDED API
*
* // example layer
* const { elevation } = tile.grids;
* // grab the grid data
* const data = elevation.grid(); // number[]
* ```
*/
export class VectorTile {
#columns;
layers = {};
#layerIndexes = [];
images = {};
grids = {};
/**
* @param data - the input data to parse
* @param end - the size of the data, leave blank to parse the entire data
*/
constructor(data, end = 0) {
const pbf = new PbfReader(data);
pbf.readFields(this.#readTile, this, end);
this.#readLayers(pbf);
}
/**
* Read in the tile data
* @param tag - the tag to read
* @param vectorTile - the vector tile to mutate
* @param pbf - the Protobuf to pull the appropriate data from
*/
#readTile(tag, vectorTile, pbf) {
if (tag === 1 || tag === 3) {
const layer = new MapboxVectorLayer(pbf, pbf.readVarint() + pbf.pos, tag === 1);
if (layer.length !== 0)
vectorTile.layers[layer.name] = layer;
}
else if (tag === 4) {
// store the position of each layer for later retrieval.
// Columns must be prepped before reading the layer.
vectorTile.#layerIndexes.push(pbf.pos);
}
else if (tag === 5) {
vectorTile.#columns = new ColumnCacheReader(pbf, pbf.readVarint() + pbf.pos);
}
else if (tag === 6) {
const gridData = new GridData(pbf, pbf.readVarint() + pbf.pos);
vectorTile.grids[gridData.name] = gridData;
}
else if (tag === 7) {
const imageData = new ImageData(pbf, pbf.readVarint() + pbf.pos);
vectorTile.images[imageData.name] = imageData;
}
}
/**
* @param pbf - the pbf to read from
*/
#readLayers(pbf) {
for (const pos of this.#layerIndexes) {
pbf.pos = pos;
const layer = new OVectorLayer(pbf, pbf.readVarint() + pbf.pos, this.#columns);
this.layers[layer.name] = layer;
}
}
}
/**
* Write a tile to a Protobuf. and return a buffer
* You have the option to store:
* - Vector data - vector points, lines, and polygons with 3D coordinates, properties, and/or m-values
* - Image data - raster data that is RGB(A) encoded
* - Grid data: data that has a max-min range, works much like an image but has floating/double
* precision point values for each point on the grid
* @param tile - the tile may be a base vector tile or a S2/Mapbox vector tile
* @param images - if provided, the tile will include image(s)
* @param griddedData - if provodied, the grid based data to encode with specs on how to encode
* @param verbose - whether to print debug messages
* @returns - a protobuffer encoded buffer using the Open Vector Tile Spec
*/
export function writeOVTile(tile, images, griddedData, verbose = false) {
const pbf = new Protobuf();
const cache = new ColumnCacheWriter();
// first write layers
if (tile !== undefined) {
for (const key in tile.layers) {
const layer = tile.layers[key];
if (layer instanceof OVectorLayer)
continue;
const baseLayer = layer instanceof MapboxVectorLayer ? BaseVectorLayer.fromMapboxVectorLayer(layer) : layer;
if (verbose === true)
console.info('writing layer', baseLayer.name);
pbf.writeMessage(4, writeOVLayer, { layer: baseLayer, cache, verbose });
}
// now we can write columns
pbf.writeMessage(5, ColumnCacheWriter.write, cache);
}
// write the image if applicable
if (images !== undefined) {
for (const image of images) {
pbf.writeBytesField(7, writeImageData(image));
}
}
// write the grid data if provided
if (griddedData !== undefined) {
for (const gridData of griddedData) {
pbf.writeBytesField(6, writeGridData(gridData));
}
return pbf.commit();
}
return pbf.commit();
}
//# sourceMappingURL=vectorTile.js.map