UNPKG

@deepgis/dem-dynamic-terrain

Version:

使用 GDAL 制作地形瓦片,支持 mapbox 和 terrarium 两种编码输出格式

165 lines (164 loc) 6.61 kB
import node_path from "node:path"; import gdal_async from "gdal-async"; function mapboxEncode(height) { const value = Math.floor((height + 10000) * 10); const r = value >> 16; const g = value >> 8 & 0x0000FF; const b = 0x0000FF & value; return [ r, g, b ]; } function terrariumEncode(height) { height += 32768; const r = Math.floor(height / 256.0); const g = Math.floor(height % 256); const b = Math.floor((height - Math.floor(height)) * 256.0); return [ r, g, b ]; } function getDriverByName(driverName) { const length = gdal_async.drivers.count(); const nameNormal = driverName.toUpperCase(); for(let i = 0; i < length; i++){ const driver = gdal_async.drivers.get(i); if (driver.description === nameNormal) return driver; } throw new Error(`\u{5F53}\u{524D}gdal\u{4E2D}\u{4E0D}\u{5B58}\u{5728}\u{8F93}\u{5165}\u{7684}\u{9A71}\u{52A8}\u{540D}\u{79F0}${nameNormal}`); } let dataset; let noData; let memDriver; let pngDriver; let outTileSize1; const invalidColor = [ 1, 134, 160 ]; function forEachHeightBuffer(heightBuffer, encode) { const channelLength = heightBuffer.length; const rBuffer = new Uint8Array(channelLength); const gBuffer = new Uint8Array(channelLength); const bBuffer = new Uint8Array(channelLength); const aBuffer = new Uint8Array(channelLength); for(let i = 0; i < channelLength; i++){ const heightVal = heightBuffer[i]; let color; color = heightVal === noData ? invalidColor : encode(heightVal); rBuffer[i] = color[0]; gBuffer[i] = color[1]; bBuffer[i] = color[2]; aBuffer[i] = 255; } return [ rBuffer, gBuffer, bBuffer, aBuffer ]; } function writeTerrainTile(overviewInfo, readinfo, writeinfo, encoding) { let readband; readband = void 0 === overviewInfo.index ? readinfo.ds.bands.get(1) : readinfo.ds.bands.get(1).overviews.get(overviewInfo.index); let dataType = readband.dataType; let heightBuffer; if (dataType == gdal_async.GDT_Byte) heightBuffer = new Uint8Array(writeinfo.wxsize * writeinfo.wysize); else if (dataType === gdal_async.GDT_Int16) heightBuffer = new Int16Array(writeinfo.wxsize * writeinfo.wysize); else if (dataType === gdal_async.GDT_Float32) heightBuffer = new Float32Array(writeinfo.wxsize * writeinfo.wysize); else if ('Int8' === dataType) { heightBuffer = new Int16Array(writeinfo.wxsize * writeinfo.wysize); dataType = gdal_async.GDT_Int16; } readband.pixels.read(readinfo.rx, readinfo.ry, readinfo.rxsize, readinfo.rysize, heightBuffer, { buffer_width: writeinfo.wxsize, buffer_height: writeinfo.wysize, data_type: dataType }); let encodeBuffers; if ('mapbox' === encoding) encodeBuffers = forEachHeightBuffer(heightBuffer, mapboxEncode); else if ('terrarium' === encoding) encodeBuffers = forEachHeightBuffer(heightBuffer, terrariumEncode); const _length = outTileSize1 * outTileSize1; const r = new Uint8Array(_length); const g = new Uint8Array(_length); const b = new Uint8Array(_length); const a = new Uint8Array(_length); for(let i = 0; i < _length; i++){ r[i] = invalidColor[0]; g[i] = invalidColor[1]; b[i] = invalidColor[2]; a[i] = 255; } const defaultBuffers = [ r, g, b, a ]; [ 1, 2, 3, 4 ].forEach((index)=>{ const writeband = writeinfo.ds.bands.get(index); writeband.pixels.write(0, 0, outTileSize1, outTileSize1, defaultBuffers[index - 1]); writeband.pixels.write(writeinfo.wx, writeinfo.wy, writeinfo.wxsize, writeinfo.wysize, encodeBuffers[index - 1]); }); } function writeRgbTile(overviewInfo, readinfo, writeinfo) { const bands = readinfo.ds.bands.count(); if (3 !== bands) throw new Error("\u8F93\u5165\u7684\u6805\u683C\u6587\u4EF6\u6CE2\u6BB5\u6570\u4E0D\u4E3A3"); for(let i = 0; i < bands; i++){ const bundIndex = i + 1; let readband; readband = void 0 === overviewInfo.index ? readinfo.ds.bands.get(bundIndex) : readinfo.ds.bands.get(bundIndex).overviews.get(overviewInfo.index); const dataType = readband.dataType; let colorBuffer; if (dataType === gdal_async.GDT_Int16) colorBuffer = new Int16Array(writeinfo.wxsize * writeinfo.wysize); else if (dataType === gdal_async.GDT_Float32) colorBuffer = new Float32Array(writeinfo.wxsize * writeinfo.wysize); else if (dataType === gdal_async.GDT_Byte) colorBuffer = new Uint8Array(writeinfo.wxsize * writeinfo.wysize); readband.pixels.read(readinfo.rx, readinfo.ry, readinfo.rxsize, readinfo.rysize, colorBuffer, { buffer_width: writeinfo.wxsize, buffer_height: writeinfo.wysize, data_type: dataType }); const _length = outTileSize1 * outTileSize1; const blank = new Uint8Array(_length); for(let i = 0; i < _length; i++)blank[i] = 0; const writeband = writeinfo.ds.bands.get(bundIndex); writeband.pixels.write(0, 0, outTileSize1, outTileSize1, blank); writeband.pixels.write(writeinfo.wx, writeinfo.wy, writeinfo.wxsize, writeinfo.wysize, colorBuffer); } const alphaBand = writeinfo.ds.bands.get(4); const alphaBuffer = new Uint8Array(outTileSize1 * outTileSize1); for(let i = 0; i < outTileSize1 * outTileSize1; i++)alphaBuffer[i] = 0; alphaBand.pixels.write(writeinfo.wx, writeinfo.wy, writeinfo.wxsize, writeinfo.wysize, alphaBuffer); } function createTile(createInfo) { const { outTileSize, overviewInfo, rb, wb, encoding, dsPath, x, y, z, outputTile } = createInfo; outTileSize1 = outTileSize; if (!dataset) { dataset = gdal_async.open(dsPath, 'r'); noData = dataset.bands.get(1).noDataValue; } if (!memDriver) memDriver = getDriverByName('mem'); const msmDS = memDriver.create('', outTileSize, outTileSize, 4); rb.ds = dataset; wb.ds = msmDS; const type = createInfo.type; 'terrain' === type ? writeTerrainTile(overviewInfo, rb, wb, encoding) : writeRgbTile(overviewInfo, rb, wb); const pngPath = node_path.join(outputTile, z.toString(), x.toString(), `${y}.png`); if (!pngDriver) pngDriver = getDriverByName('png'); const pngDs = pngDriver.createCopy(pngPath, msmDS); msmDS.flush(); msmDS.close(); pngDs.close(); } const create_tile = createTile; export { create_tile as default };