s2maps-gpu
Version:
S2 Maps GPU - An open source, high-performance, and GPU-accelerated map engine for rendering large-scale, interactive maps.
127 lines (126 loc) • 4.35 kB
JavaScript
import { Color } from 'style/color/index.js';
import parseFeature from 's2/style/parseFeature.js';
/**
* Color function to convert a color to either RBG or LCH
* @param lch - flag to use lch if true
* @returns a color parsing function
*/
export const colorFunc = (lch) => {
return (i) => {
const color = new Color(i);
return lch ? color.getLCH() : color.getRGB();
};
};
/**
* Clamp tool to ensure the number is between -1 and 1
* @param i - input number
* @returns clamped number
*/
export const clamp = (i) => Math.max(-1, Math.min(1, i));
/**
* # Vector Worker
*
* Base class for all vector workers.
* Ensurses that all vector workers can reuse things like prepping and shipping interactive features,
* flusing, id-generation, etc.
*/
export default class VectorWorker {
idGen;
gpuType;
interactiveMap = new Map();
/**
* @param idGen - id generator to ensure features don't overlap
* @param gpuType - the GPU context of the map renderer (WebGL(1|2) | WebGPU)
*/
constructor(idGen, gpuType) {
this.idGen = idGen;
this.gpuType = gpuType;
}
/**
* Add an interactive feature
* @param id - feature id
* @param properties - feature properties
* @param workerLayer - worker layer to pull the interactive-properties from
*/
_addInteractiveFeature(id, properties, workerLayer) {
const { cursor, name, source, layer } = workerLayer;
this.interactiveMap.set(id, {
__id: id,
__cursor: cursor,
__name: name,
__source: source,
__layer: layer,
...properties,
});
}
/**
* Flush a tile-request to the render thread
* @param mapID - id of the map to ship the data back to
* @param tile - tile request
* @param sourceName - name of the source the data belongs to
* @param _wait - wait function. Not needed at this flush level.
*/
async flush(mapID, tile, sourceName, _wait) {
await this.postInteractive(mapID, sourceName, tile.id);
}
/**
* Build code for a vector layer
* @param design - the design to modify
* @returns the build function
*/
buildCode(design) {
const featureFunctions = [];
for (const [input, cb] of design) {
featureFunctions.push(parseFeature(input, cb));
}
return (zoom, properties) => {
// prep codes
const webgl2Code = [];
const webgl1Code = featureFunctions.flatMap((func) => func(webgl2Code, properties, zoom));
return [webgl1Code, webgl2Code];
};
}
/**
* Post an interactive feature set to the render thread
* @param mapID - id of the map to ship the data back to
* @param sourceName - name of the source the data belongs to
* @param tileID - tile id the features belong to
*/
postInteractive(mapID, sourceName, tileID) {
if (this.interactiveMap.size === 0)
return;
const interactiveGuide = [];
const interactiveData = [];
const textEncoder = new TextEncoder();
let offset = 0;
for (const [id, properties] of this.interactiveMap) {
const uint8Array = textEncoder.encode(JSON.stringify(properties));
const length = uint8Array.length;
interactiveGuide.push(id, offset, offset + length);
for (const byte of uint8Array)
interactiveData.push(byte);
offset += length;
}
this.interactiveMap.clear();
// Upon building the batches, convert to buffers and ship.
const interactiveGuideBuffer = new Uint32Array(interactiveGuide).buffer;
const interactiveDataBuffer = new Uint8ClampedArray(interactiveData).buffer;
// ship the vector data.
postMessage({
mapID,
type: 'interactive',
sourceName,
tileID,
interactiveGuideBuffer,
interactiveDataBuffer,
}, [interactiveGuideBuffer, interactiveDataBuffer]);
}
}
/**
* A convenience function to convert an ID to an RGBA encoded color
* @param id - the id to convert
* @returns an RGBA encoded color
*/
export function idToRGB(id) {
return [id & 255, (id >> 8) & 255, (id >> 16) & 255, 0];
}