gis-tools-ts
Version:
A collection of geospatial tools primarily designed for WGS84, Web Mercator, and S2.
174 lines • 6.99 kB
JavaScript
import { encodingToCompression } from '../../index.js';
import { xyzToBBOX } from '../../geometry/wm/coords.js';
import { DrawType, MetadataBuilder } from 's2-tilejson';
import TileWorker, { getMaxzoom, getMinzoom } from './worker/tileWorker.js';
export * from './types.js';
/**
* Build vector tiles give a guide on what sources to parse data from and how to store it
* @param buildGuide - the user defined guide on building the vector tiles
*/
export async function toTiles(buildGuide) {
const { tileWriter, extension, projection, encoding } = buildGuide;
const worker = new TileWorker();
// STEP 1: Convert all features to tile slices of said features.
await toTilesSliceFeatures(buildGuide, worker);
// STEP 2: Ensure all data is prepped/sorted for reading/building tiles
await worker.sort();
// STEP 3: build metadata based on the guide
const metaBuilder = new MetadataBuilder();
const type = buildGuide.format === 'raster' ? 'raster' : 'vector';
updateBuilder(metaBuilder, buildGuide, type, extension ?? 'pbf');
// STEP 4: collect all existing multimap feature stores and build tiles
for await (const { face, zoom, x, y, data } of worker.buildTiles()) {
if (projection === 'S2') {
await tileWriter.writeTileS2(face, zoom, x, y, data);
// TODO: handle llbounds correctly
metaBuilder.addTileS2(face, zoom, x, y, [0, 0, 0, 0]);
}
else {
await tileWriter.writeTileWM(zoom, x, y, data);
metaBuilder.addTileWM(zoom, x, y, xyzToBBOX(x, y, zoom, false, 'WGS84'));
}
}
// STEP 5: Commit the metadata
await tileWriter.commit(metaBuilder.commit(), encodingToCompression(encoding ?? 'none'));
}
// /**
// * TODO: Merge tiles together
// * @param _tileReaders - the tile readers to merge
// * @param _tileWriter - the tile writer to write to
// */
// export async function mergeTiles(
// _tileReaders: TileReader[],
// _tileWriter: TileWriter,
// ): Promise<void> {}
/**
* STEP 1: Convert all features to tile slices of said features.
* @param buildGuide - the user defined guide on building the vector tiles
* @param worker - the vector tile worker to use
*/
async function toTilesSliceFeatures(buildGuide, worker) {
const { vectorSources, rasterSources, gridSources, layerGuides } = buildGuide;
const { projection, encoding, format, buildIndices } = buildGuide;
const featuresIterator = getFeature([
vectorSources,
rasterSources,
gridSources,
]);
// Prepare workers with init messages
const initMessage = {
type: 'init',
id: 0,
projection,
encoding,
format,
buildIndices,
layerGuides: prepareLayerGuides(layerGuides),
};
worker.handleMessage(initMessage);
// iterate over all features
for await (const nextFeature of featuresIterator) {
const { sourceName, feature } = nextFeature;
const featureMessage = { type: 'feature', sourceName, feature };
worker.handleMessage(featureMessage);
}
}
/**
* Prepare the layer guides for workers to be stringified
* @param layerGuides - the user defined guide on building the vector tiles
* @returns the stringified layer guides
*/
function prepareLayerGuides(layerGuides) {
return layerGuides.map((layer) => {
return {
...layer,
getValue: 'getValue' in layer && layer.getValue !== undefined
? JSON.stringify(layer.getValue)
: undefined,
onFeature: layer.onFeature !== undefined ? JSON.stringify(layer.onFeature) : undefined,
};
});
}
/**
* Get the features that will be stored in the tile
* @param sources - the vector/raster sources. Either:
* - the vector sources that the tile is built from and how the layers are to be stored.
* - the raster sources that will be conjoined into a single rgba pixel index for tile extraction
* @yields {FeatureIterateResult} - a features
*/
async function* getFeature(sources) {
for (const sourceObj of sources) {
if (sourceObj === undefined)
continue;
for (const [sourceName, source] of Object.entries(sourceObj)) {
for await (const feature of source)
yield { sourceName, feature };
}
}
}
/**
* @param metaBuilder - the metadata builder to update
* @param buildGuide - the user defined guide on building the vector tiles
* @param type - the type of source
* @param extensions - the extension name
*/
function updateBuilder(metaBuilder, buildGuide, type, extensions) {
const { name, description, version, projection, attribution } = buildGuide;
const encoding = 'encoding' in buildGuide ? buildGuide.encoding : 'none';
const layerGuides = 'layerGuides' in buildGuide ? buildGuide.layerGuides : [];
metaBuilder.setName(name);
metaBuilder.setExtension(extensions);
metaBuilder.setDescription(description ?? 'Built by GIS-Tools');
metaBuilder.setVersion(version ?? '1.0.0');
// NOTE: For now we only support xyz and fzxy
metaBuilder.setScheme(projection === 'WG' ? 'xyz' : 'fzxy'); // 'fzxy' | 'tfzxy' | 'xyz' | 'txyz' | 'tms'
metaBuilder.setType(type);
metaBuilder.setEncoding(encoding ?? 'none'); // 'gz' | 'br' | 'none'
if (attribution !== undefined) {
for (const [displayName, href] of Object.entries(attribution)) {
metaBuilder.addAttribution(displayName, href);
}
}
// Build metadata from layerGuides
for (const layer of layerGuides)
metaBuilder.addLayer(layer.layerName, buildLayerMetadata(layer));
}
/**
* Build layer metadata from layer guide
* @param layer - the layer guide to parse
* @returns the layer metadata
*/
function buildLayerMetadata(layer) {
const { description } = layer;
let drawTypes = [];
if ('vectorGuide' in layer) {
drawTypes = layer.drawTypes;
}
else if ('clusterGuide' in layer) {
drawTypes = [DrawType.Points];
}
else if ('rasterGuide' in layer) {
drawTypes = [DrawType.Raster];
}
else if ('gridGuide' in layer) {
drawTypes = [DrawType.Grid];
}
const minzoom = getMinzoom([layer]);
const maxzoom = getMaxzoom([layer]);
const shape = 'shape' in layer ? layer.shape : {};
const mShape = 'mShape' in layer ? layer.mShape : undefined;
return { description, minzoom, maxzoom, drawTypes, shape, mShape };
}
// /**
// * TODO: Find all cases where prepping the data could be done wrong by the user with
// * TODO: explinations of how to correct them.
// * TODO: - metadata must be correct. -
// * Check and display errors
// * @param _layerGuides - the user defined guide on building the vector tiles
// */
// function _findErrors(_layerGuides: LayerGuide[]): void {
// // for (const layerGuide of layerGuides) {
// // // const { metadata } = layerGuide;
// // }
// }
//# sourceMappingURL=index.js.map