MapComplete/scripts/generatePmTilesExtracts.ts

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

132 lines
4.9 KiB
TypeScript
Raw Normal View History

import Script from "./Script"
2025-09-25 14:03:02 +02:00
import { existsSync, mkdirSync, writeFileSync } from "fs"
2025-07-31 12:18:57 +02:00
import { Utils } from "../src/Utils"
2025-09-25 14:03:02 +02:00
import { OfflineBasemapManager } from "../src/Logic/OfflineBasemapManager"
import { PmTilesExtractGenerator } from "./pmTilesExtractGenerator"
class GeneratePmTilesExtracts extends Script {
2025-07-31 01:11:12 +02:00
private targetDir: string
private skipped: number = 0
private extractsGenerator: PmTilesExtractGenerator
constructor() {
2025-08-13 23:06:38 +02:00
super(
"Generates many pmtiles-archive from planet-latest.pmtiles. Expects the `pmtiles`-executable to be at `/data/pmtiles`." +
2025-10-11 14:03:42 +02:00
"\n\n" +
"The first argument should be the 'targetDirectory', where many subdirectories will be made containing the subarchives. A second argument (defaulting to [targetDir]/planet-latest.pmtiles) is the source data"
2025-08-13 23:06:38 +02:00
)
}
public *generateColumnIfNeeded(
2025-08-13 23:06:38 +02:00
z: number,
x: number,
boundary: number,
maxzoom?: number
): Generator<Promise<any>> {
const lastFileForColumn = this.extractsGenerator.getFilename(z, x, boundary - 1)
if (existsSync(lastFileForColumn)) {
// Skip this column, already exists
console.log("Skipping column ", x, "at zoom", z)
this.skipped += boundary
return
}
2025-10-11 14:03:42 +02:00
console.log("Starting column", x, "at zoom", z, "as", lastFileForColumn, "does not exist")
for (let y = 0; y < boundary; y++) {
yield this.extractsGenerator.generateArchive(z, x, y, maxzoom)
}
}
2025-08-13 23:06:38 +02:00
private *generateField(z: number, maxzoom?: number): Generator<Promise<void>> {
const boundary = Math.max(1, 2 << (z - 1))
for (let x = 0; x < boundary; x++) {
2025-09-25 14:26:07 +02:00
if (!existsSync(this.targetDir + "/" + z + "/" + x)) {
mkdirSync(this.targetDir + "/" + z + "/" + x)
2025-09-25 14:03:02 +02:00
}
for (const promise of this.generateColumnIfNeeded(z, x, boundary, maxzoom)) {
yield promise
}
}
}
2025-08-13 23:06:38 +02:00
private *generateAll(): Generator<Promise<void>> {
const zoomlevels: Record<number, number> = OfflineBasemapManager.zoomelevels
for (const key in zoomlevels) {
const minzoom: number = Number(key)
const maxzoom: number | undefined = zoomlevels[key]
2025-09-25 14:26:07 +02:00
if (!existsSync(this.targetDir + "/" + key)) {
mkdirSync(this.targetDir + "/" + key)
2025-09-25 14:03:02 +02:00
}
for (const promise of this.generateField(minzoom, maxzoom)) {
yield promise
}
}
}
createBatch<T>(generator: Generator<T>, length: number): T[] {
const batch = []
do {
const next = generator.next()
if (next.done) {
return batch
}
batch.push(next.value)
} while (batch.length < length)
return batch
}
2025-07-31 01:11:12 +02:00
async main(args: string[]): Promise<void> {
this.targetDir = args[0]
2025-10-11 14:03:42 +02:00
const sourceFile = this.targetDir + "/planet-latest.pmtiles"
2025-07-31 01:11:12 +02:00
if (!this.targetDir) {
2025-09-25 14:26:07 +02:00
console.log("Please specify a target directory. Did you forget '--' in vite-node?")
2025-07-31 01:11:12 +02:00
return
}
this.extractsGenerator = new PmTilesExtractGenerator(sourceFile, this.targetDir)
let estimate = 0
for (const key in OfflineBasemapManager.zoomelevels) {
const z: number = Number(key)
2025-08-01 02:05:17 +02:00
const boundary = 2 << z
estimate += boundary * boundary
}
2025-07-31 01:11:45 +02:00
console.log("Target dir is:", this.targetDir)
2025-09-25 14:03:02 +02:00
const numberOfThreads = 24
const generator = this.generateAll()
let batch: Promise<void>[] = []
let done = 0
2025-07-31 12:18:57 +02:00
const startDate = new Date()
do {
batch = this.createBatch(generator, numberOfThreads)
await Promise.all(batch)
done += batch.length
2025-07-31 12:18:57 +02:00
const now = new Date()
const timeElapsed = (now.getTime() - startDate.getTime()) / 1000
2025-08-13 23:06:38 +02:00
const speed = ("" + done / timeElapsed).substring(0, 5)
const perc = ("" + (100 * (done + this.skipped)) / estimate).substring(0, 5)
const etaSecond = Math.floor(((estimate - done - this.skipped) * timeElapsed) / done)
console.log(
"Completed",
numberOfThreads,
`processes; ${
done + this.skipped
} / ${estimate}, ${perc}%, ${speed} tile/second, ETA: ${Utils.toHumanTime(
etaSecond
)}`
)
} while (batch.length > 0)
2025-10-11 14:03:42 +02:00
writeFileSync(
this.targetDir + "/Last_pm_tile_extracts.txt",
[
new Date().getTime() + "",
2025-09-25 14:03:02 +02:00
new Date().toISOString(),
"# The script converting the planet-latest.pmtiles into sub-archives has been successfully executed at the stated time",
2025-10-11 14:03:42 +02:00
].join("\n"),
"utf-8"
2025-09-25 14:03:02 +02:00
)
}
}
new GeneratePmTilesExtracts().run()