Show build progress instead of a static "Building…"
CI / check (push) Successful in 18s
CI / deploy (push) Successful in 13s

terrain.pmtiles can take minutes (tiles fetched one at a time) with no
way to tell a slow build from a stuck one. Now reports tiles_done/
tiles_total into the same status Hash the frontend already polls, and
the panel shows elapsed time (both files) plus tile counts (terrain
only) so the numbers visibly move every ~3s poll instead of sitting on
one static string.
This commit is contained in:
randy committed 2026-09-11 11:57:43 +02:00
1 parent d72f8a3c94
commit bbe9f38bda
3 files changed
+68 -8

No files matched your search

+21 -7
View File
@@ -66,10 +66,14 @@ module Maptool
# fetch_tile: same injectable-seam reasoning as fetch_build_url above,
# for the many individual Terrarium tile fetches this needs instead of
# one index lookup.
# one index lookup. This build can run for minutes with nothing else to
# show for it, so write_terrarium_mbtiles reports tiles_done/tiles_total
# into the status Hash as it goes — otherwise a poller sees "running"
# the whole time with no way to tell it apart from stuck.
def self.build_terrain(bounds, data_dir:, fetch_tile: method(:fetch_terrarium_tile))
begin_run!(TERRAIN_MUTEX, TERRAIN_STATUS)
Thread.new { run_job(TERRAIN_MUTEX, TERRAIN_STATUS) { build_terrain_pmtiles(bounds, data_dir, fetch_tile) } }
progress = ->(done, total) { TERRAIN_MUTEX.synchronize { TERRAIN_STATUS.merge!(tiles_done: done, tiles_total: total) } }
Thread.new { run_job(TERRAIN_MUTEX, TERRAIN_STATUS) { build_terrain_pmtiles(bounds, data_dir, fetch_tile, progress) } }
end
def self.begin_run!(mutex, status)
@@ -117,12 +121,12 @@ module Maptool
# --- terrain.pmtiles ---
def self.build_terrain_pmtiles(bounds, data_dir, fetch_tile)
def self.build_terrain_pmtiles(bounds, data_dir, fetch_tile, progress)
south, west, north, east = bounds
tmp_mbtiles = File.join(Dir.tmpdir, "maptool-terrain-#{Process.pid}-#{Time.now.to_i}.mbtiles")
tmp_path = File.join(data_dir, "terrain.pmtiles.tmp")
write_terrarium_mbtiles(tmp_mbtiles, south, west, north, east, fetch_tile)
write_terrarium_mbtiles(tmp_mbtiles, south, west, north, east, fetch_tile, progress)
run_pmtiles!("convert", tmp_mbtiles, tmp_path)
File.rename(tmp_path, File.join(data_dir, "terrain.pmtiles"))
ensure
@@ -141,7 +145,7 @@ module Maptool
# park-sized region, slow for a big one. Add a small bounded-concurrency
# fetch pool if regenerating a large area turns out to matter in
# practice.
def self.write_terrarium_mbtiles(path, south, west, north, east, fetch_tile)
def self.write_terrarium_mbtiles(path, south, west, north, east, fetch_tile, progress)
db = SQLite3::Database.new(path)
db.execute_batch(<<~SQL)
CREATE TABLE metadata (name TEXT, value TEXT);
@@ -152,9 +156,19 @@ module Maptool
db.execute("INSERT INTO metadata (name, value) VALUES (?, ?)", [name, value])
end
(0..TERRAIN_MAX_ZOOM).each do |z|
tile_range(z, south, west, north, east).each do |x, y|
# tile_range per zoom computed once upfront (cheap — no fetching, just
# tile math) and reused below, so progress can report a real total
# from the very first call instead of only once the run finishes.
by_zoom = (0..TERRAIN_MAX_ZOOM).map { |z| [z, tile_range(z, south, west, north, east)] }
total = by_zoom.sum { |_z, tiles| tiles.size }
done = 0
progress.call(done, total)
by_zoom.each do |z, tiles|
tiles.each do |x, y|
bytes = fetch_tile.call(z, x, y)
done += 1
progress.call(done, total)
next unless bytes
# MBTiles uses TMS row numbering (0 at the south) — the opposite