Add a country-based region picker for offline data
Replaces "regenerate for the current viewport" with what Locus/OsmAnd- style apps give you: tick which countries you want offline (a Natural Earth-derived checklist, public domain, vendored), Apply rebuilds offline.pmtiles/terrain.pmtiles to cover the whole selection at once. Multiple regions can be offline simultaneously (e.g. Slovakia + Austria for a trip that crosses the border) — the selection itself is the source of truth (data/offline_regions.json), not a set of per-country files, so "remove a country" and "refresh a country" are both just "change the selection, then Apply." Confirmed against go-pmtiles' own source (not just its docs) before committing to this shape: `extract --region=<geojson>` treats a FeatureCollection's features as their union already (no dissolve/union math needed on our side — pmtiles/region.go's UnmarshalRegion just concatenates every feature's rings), and `merge` hard-errors on any overlapping tile id between inputs (merge.go's prepareInputs). That second fact rules out "extract each country separately, merge them all" — adjacent countries' region-extracts would share border tiles, since extraction copies whole tiles rather than clipping vector content to the polygon. The actual fix: extract per *source archive* (the Protomaps build, each intersecting Mapterhorn chunk), each call covering the whole current selection via one --region= file — the terrain pipeline already extracted-then-merged per source this way, it just needed a multi-country region instead of a single bbox.
This commit is contained in:
1 parent
f89dea5805
commit
53c9a82af2
10 files changed
+442
-170
No files matched your search
+34
-12
@@ -349,41 +349,63 @@ class AppTest < Minitest::Test
|
||||
assert_equal 502, response.status
|
||||
end
|
||||
|
||||
# --- offline regions ---
|
||||
|
||||
def test_offline_regions_lists_available_and_selected
|
||||
body = json_body(request.get("/api/offline-regions"))
|
||||
assert_operator body["available"].size, :>, 0
|
||||
assert_includes body["available"], { "code" => "SK", "name" => "Slovakia" }
|
||||
assert_equal [], body["selected"]
|
||||
end
|
||||
|
||||
def test_offline_regions_post_requires_login
|
||||
assert_equal 401, request.post("/api/offline-regions").status
|
||||
end
|
||||
|
||||
def test_offline_regions_post_saves_the_selection
|
||||
response = request.post("/api/offline-regions", params: { "codes" => %w[SK AT] }, **logged_in_env)
|
||||
assert_equal 200, response.status
|
||||
assert_equal %w[SK AT], json_body(request.get("/api/offline-regions"))["selected"]
|
||||
end
|
||||
|
||||
def test_offline_regions_post_rejects_an_unknown_code
|
||||
response = request.post("/api/offline-regions", params: { "codes" => ["XX"] }, **logged_in_env)
|
||||
assert_equal 400, response.status
|
||||
end
|
||||
|
||||
# --- pmtiles regeneration ---
|
||||
#
|
||||
# The full build (network + go-pmtiles) is covered in isolation, with
|
||||
# fakes injected, by test_pmtiles_builder.rb — same "keep this offline"
|
||||
# reasoning as the rest of this file. What's route-specific and worth
|
||||
# covering here is the bbox validation and the 409-while-running guard,
|
||||
# neither of which needs a real build to actually run.
|
||||
# covering here is the empty-selection validation and the
|
||||
# 409-while-running guard, neither of which needs a real build to
|
||||
# actually run.
|
||||
|
||||
def test_offline_tiles_build_status_defaults_to_idle
|
||||
assert_equal({ "state" => "idle" }, json_body(request.get("/api/offline-tiles/build-status")))
|
||||
assert_equal({ "state" => "idle" }, json_body(request.get("/api/terrain-tiles/build-status")))
|
||||
end
|
||||
|
||||
def test_offline_tiles_build_requires_a_bbox
|
||||
def test_offline_tiles_build_is_409_with_no_selection
|
||||
response = request.post("/api/offline-tiles/build", **logged_in_env)
|
||||
assert_equal 400, response.status
|
||||
assert_equal 409, response.status
|
||||
end
|
||||
|
||||
def test_terrain_tiles_build_requires_a_bbox
|
||||
def test_terrain_tiles_build_is_409_with_no_selection
|
||||
response = request.post("/api/terrain-tiles/build", **logged_in_env)
|
||||
assert_equal 400, response.status
|
||||
assert_equal 409, response.status
|
||||
end
|
||||
|
||||
# fetch_build_url blocks on a Queue, called directly (not through the
|
||||
# route, which has no way to inject a fake) so the build is
|
||||
# deterministically still "running" when the route call below happens.
|
||||
def test_offline_tiles_build_is_409_when_one_is_already_running
|
||||
Maptool::PmtilesBuilder.set_selected_regions(Maptool::App::DATA_DIR, ["SK"])
|
||||
gate = Queue.new
|
||||
thread = Maptool::PmtilesBuilder.build_offline([1, 2, 3, 4], data_dir: Maptool::App::DATA_DIR, fetch_build_url: -> { gate.pop })
|
||||
thread = Maptool::PmtilesBuilder.build_offline(data_dir: Maptool::App::DATA_DIR, fetch_build_url: -> { gate.pop })
|
||||
|
||||
response = request.post(
|
||||
"/api/offline-tiles/build",
|
||||
params: { south: "1", west: "2", north: "3", east: "4" },
|
||||
**logged_in_env,
|
||||
)
|
||||
response = request.post("/api/offline-tiles/build", **logged_in_env)
|
||||
assert_equal 409, response.status
|
||||
ensure
|
||||
gate.push("unused")
|
||||
|
||||
Reference in new issue
Block a user