UNPKG

@csi-foxbyte/cityjson-to-3d-tiles

Version:

A Node.js library that converts CityJSON files into Cesium 3D Tiles—complete with automatic texture atlas packing, Basis compression, three LOD levels, and customizable threading.

156 lines (135 loc) 4.68 kB
import { mkdir, rm } from "node:fs/promises"; import { generate3DTilesFromTileDatabase, generateTileDatabaseFromCityJSON, } from "../dist/index.js"; function parseBoolean(value: string | undefined, fallback: boolean): boolean { if (value === undefined || value === "") { return fallback; } switch (value.toLowerCase()) { case "1": case "true": case "yes": case "y": case "on": return true; case "0": case "false": case "no": case "n": case "off": return false; default: throw new Error(`Invalid boolean value: ${value}`); } } function parseInteger(value: string | undefined, fallback: number): number { if (value === undefined || value === "") { return fallback; } const parsed = Number.parseInt(value, 10); if (!Number.isInteger(parsed) || parsed <= 0) { throw new Error(`Invalid positive integer value: ${value}`); } return parsed; } function optionalString(value: string | undefined): string | undefined { if (!value) return undefined; const trimmed = value.trim(); return trimmed.length > 0 ? trimmed : undefined; } function getErrorMessage(error: unknown): string { if (error instanceof Error) return error.message; return String(error); } function shouldShowStackTrace(): boolean { const raw = (process.env.SHOW_STACK_TRACE ?? "").toLowerCase(); return raw === "1" || raw === "true" || raw === "yes" || raw === "on"; } function printNiceError(error: unknown): void { const message = getErrorMessage(error); console.error(""); console.error("Job failed."); console.error(`Reason: ${message}`); if (message.includes("No valid src srs found please provide one!")) { console.error(""); console.error("Hint: Source CRS (SRC_SRS) could not be detected."); console.error("Provide SRC_SRS explicitly, for example:"); console.error( ' -e SRC_SRS="+proj=utm +zone=32 +ellps=GRS80 +units=m +no_defs +type=crs"', ); console.error( "Or ensure CityJSON metadata.referenceSystem is present and valid.", ); } if (shouldShowStackTrace() && error instanceof Error && error.stack) { console.error(""); console.error("Stack trace:"); console.error(error.stack); } else { console.error(""); console.error("Set SHOW_STACK_TRACE=true to print full stack trace."); } } async function main() { const inputDir = process.env.INPUT_DIR ?? "/work"; const tilesOutputDir = process.env.OUTPUT_DIR ?? "/work/tiles"; const internalDbDir = process.env.INTERNAL_DB_DIR ?? "/tmp/cityjson-to-3d-tiles"; const appearance = process.env.APPEARANCE ?? "rgbTexture"; const threadCount = parseInteger(process.env.THREAD_COUNT, 4); const hasAlphaEnabled = parseBoolean(process.env.HAS_ALPHA_ENABLED, true); const simplifyAddresses = parseBoolean(process.env.SIMPLIFY_ADDRESSES, false); const srcSRS = optionalString(process.env.SRC_SRS); const destSRS = optionalString(process.env.DEST_SRS); // Keep temporary DB work on container-local storage for faster SQLite I/O. await rm(internalDbDir, { recursive: true, force: true }); await mkdir(internalDbDir, { recursive: true }); // Ensure previous run outputs are not picked up as input JSON. // Skip if OUTPUT_DIR is explicitly set — user owns that directory. if (!process.env.OUTPUT_DIR) { await rm(tilesOutputDir, { recursive: true, force: true }); await mkdir(tilesOutputDir, { recursive: true }); } console.log(`Using internal DB directory: ${internalDbDir}`); console.log("Step 1/2: build tile database from CityJSON"); const { dbFilePath } = await generateTileDatabaseFromCityJSON( inputDir, internalDbDir, appearance, (progress) => { const pct = Math.round(progress * 10000) / 100; console.log(`[cityjson] ${pct}%`); }, { threadCount, srcSRS, destSRS, }, ); console.log("Step 2/2: generate Cesium 3D Tiles"); await generate3DTilesFromTileDatabase( dbFilePath, tilesOutputDir, hasAlphaEnabled, (progress, files) => { const pct = Math.round(progress * 10000) / 100; if (files?.length) { console.log(`[tiles] ${pct}% (${files.length} file(s) in last batch)`); return; } console.log(`[tiles] ${pct}%`); }, { threadCount, simplifyAdresses: simplifyAddresses, }, ); console.log(`Done. Tiles written to: ${tilesOutputDir}`); } try { await main(); } catch (error) { printNiceError(error); process.exit(1); }