mapcomplete/scripts/slice.ts

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

157 lines
4.8 KiB
TypeScript
Raw Normal View History

2021-10-27 01:18:05 +02:00
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";
2022-01-16 01:59:06 +01:00
import {Utils} from "../Utils";
2021-10-27 01:18:05 +02:00
2021-12-09 17:56:37 +01:00
/**
2022-01-26 21:40:38 +01:00
* This script slices a big newline-delimeted geojson file into tiled geojson
2021-12-09 17:56:37 +01:00
* It was used to convert the CRAB-data into geojson tiles
*/
async function readFeaturesFromLineDelimitedJsonFile(inputFile: string): Promise<any[]> {
const fileStream = fs.createReadStream(inputFile);
2021-10-27 01:18:05 +02:00
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.
2021-10-27 01:18:05 +02:00
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")
}
2021-10-27 01:18:05 +02:00
}
return allFeatures
}
2021-10-27 01:18:05 +02:00
async function readGeojsonLineByLine(inputFile: string): Promise<any[]> {
2021-10-27 01:18:05 +02:00
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
2021-10-27 01:18:05 +02:00
// @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 {
2021-10-27 01:18:05 +02:00
allFeatures.push(JSON.parse(line))
} catch (e) {
2021-10-27 01:18:05 +02:00
console.error("Could not parse", line)
break
}
if (allFeatures.length % 10000 === 0) {
2021-10-27 01:18:05 +02:00
ScriptUtils.erasableLog("Loaded ", allFeatures.length, "features up till now")
}
}
return allFeatures
}
async function readFeaturesFromGeoJson(inputFile: string): Promise<any[]> {
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: <input-file.geojson> <target-zoom-level> <output-directory>")
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")) {
2022-01-16 01:59:06 +01:00
console.log("Detected geojson")
allFeatures = await readFeaturesFromGeoJson(inputFile)
} else {
2022-01-16 01:59:06 +01:00
console.log("Loading as newline-delimited features")
allFeatures = await readFeaturesFromLineDelimitedJsonFile(inputFile)
}
2022-01-16 01:59:06 +01:00
allFeatures = Utils.NoNull(allFeatures)
2021-10-27 01:18:05 +02:00
console.log("Loaded all", allFeatures.length, "points")
const keysToRemove = ["STRAATNMID", "GEMEENTE", "POSTCODE"]
2021-10-27 01:18:05 +02:00
for (const f of allFeatures) {
2022-01-26 21:40:38 +01:00
if (f.properties === null) {
2022-01-16 01:59:06 +01:00
console.log("Got a feature without properties!", f)
continue
}
2021-10-27 01:18:05 +02:00
for (const keyToRm of keysToRemove) {
delete f.properties[keyToRm]
}
delete f.bbox
2021-10-27 01:18:05 +02:00
}
TiledFeatureSource.createHierarchy(
StaticFeatureSource.fromGeojson(allFeatures),
2021-10-27 01:18:05 +02:00
{
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
})
2021-10-27 01:18:05 +02:00
fs.writeFileSync(path, JSON.stringify({
"type": "FeatureCollection",
"features": features
2021-10-27 01:18:05 +02:00
}, null, " "))
ScriptUtils.erasableLog("Written ", path, "which has ", tile.features.data.length, "features")
2021-10-27 01:18:05 +02:00
}
}
)
}
let args = [...process.argv]
args.splice(0, 2)
main(args).then(_ => {
console.log("All done!")
});