import * as fs from "fs"; import TiledFeatureSource from "../Logic/FeatureSource/TiledFeatureSource/TiledFeatureSource"; import StaticFeatureSource from "../Logic/FeatureSource/Sources/StaticFeatureSource"; import * as readline from "readline"; import ScriptUtils from "./ScriptUtils"; async function readFeaturesFromLineDelimitedJsonFile(inputFile: string): Promise { const fileStream = fs.createReadStream(inputFile); const rl = readline.createInterface({ input: fileStream, crlfDelay: Infinity }); // Note: we use the crlfDelay option to recognize all instances of CR LF // ('\r\n') in input.txt as a single line break. const allFeatures: any[] = [] // @ts-ignore for await (const line of rl) { try { allFeatures.push(JSON.parse(line)) } catch (e) { console.error("Could not parse", line) break } if (allFeatures.length % 10000 === 0) { ScriptUtils.erasableLog("Loaded ", allFeatures.length, "features up till now") } } return allFeatures } async function readGeojsonLineByLine(inputFile: string): Promise { const fileStream = fs.createReadStream(inputFile); const rl = readline.createInterface({ input: fileStream, crlfDelay: Infinity }); // Note: we use the crlfDelay option to recognize all instances of CR LF // ('\r\n') in input.txt as a single line break. const allFeatures: any[] = [] let featuresSeen = false // @ts-ignore for await (let line: string of rl) { if (!featuresSeen && line.startsWith("\"features\":")) { featuresSeen = true; continue; } if (!featuresSeen) { continue } if (line.endsWith(",")) { line = line.substring(0, line.length - 1) } try { allFeatures.push(JSON.parse(line)) } catch (e) { console.error("Could not parse", line) break } if (allFeatures.length % 10000 === 0) { ScriptUtils.erasableLog("Loaded ", allFeatures.length, "features up till now") } } return allFeatures } async function readFeaturesFromGeoJson(inputFile: string): Promise { try { return JSON.parse(fs.readFileSync(inputFile, "UTF-8")).features } catch (e) { // We retry, but with a line-by-line approach return await readGeojsonLineByLine(inputFile) } } async function main(args: string[]) { console.log("GeoJSON slicer") if (args.length < 3) { console.log("USAGE: ") return } const inputFile = args[0] const zoomlevel = Number(args[1]) const outputDirectory = args[2] if (!fs.existsSync(outputDirectory)) { fs.mkdirSync(outputDirectory) console.log("Directory created") } console.log("Using directory ", outputDirectory) let allFeatures: any []; if (inputFile.endsWith(".geojson")) { allFeatures = await readFeaturesFromGeoJson(inputFile) } else { allFeatures = await readFeaturesFromLineDelimitedJsonFile(inputFile) } console.log("Loaded all", allFeatures.length, "points") const keysToRemove = ["STRAATNMID", "GEMEENTE", "POSTCODE"] for (const f of allFeatures) { for (const keyToRm of keysToRemove) { delete f.properties[keyToRm] } delete f.bbox } //const knownKeys = Utils.Dedup([].concat(...allFeatures.map(f => Object.keys(f.properties)))) //console.log("Kept keys: ", knownKeys) TiledFeatureSource.createHierarchy( new StaticFeatureSource(allFeatures, false), { minZoomLevel: zoomlevel, maxZoomLevel: zoomlevel, maxFeatureCount: Number.MAX_VALUE, registerTile: tile => { const path = `${outputDirectory}/tile_${tile.z}_${tile.x}_${tile.y}.geojson` const features = tile.features.data.map(ff => ff.feature) features.forEach(f => { delete f.bbox }) fs.writeFileSync(path, JSON.stringify({ "type": "FeatureCollection", "features": features }, null, " ")) ScriptUtils.erasableLog("Written ", path, "which has ", tile.features.data.length, "features") } } ) } let args = [...process.argv] args.splice(0, 2) main(args).then(_ => { console.log("All done!") });