Split offline hiking trails per country, add a picker instead of one big file
CI / check (push) Successful in 21s
CI / deploy (push) Successful in 11s

data/hiking_trails.geojson is now data/hiking_trails/<CODE>.geojson —
one file per country instead of a merge, generated per-source
(PmtilesBuilder.extract_hiking_trails), cached the same way
geofabrik_cache is (skipped once a country's own extract is already
newer than its source .osm.pbf, so growing the selection by one
country only re-extracts that one). GET /api/hiking-trails now lists
available country codes instead of a single file's status.

The Overlays panel's "Hiking trails" item becomes an expandable
per-country checklist once any offline extract exists (same shape as
Radar's own expand), replacing the plain on/off checkbox — nobody
covering a dozen countries' worth of offline map data needs all of
their hiking trails loaded and parsed at once, so each ticked country
fetches its own GeoJSON independently instead.

HikingTrails.extract_merged (server-side multi-country merge, no
longer needed now that the client picks per country) removed again.
Verified the new extract_hiking_trails + caching skip live on brick
against a real cached country PBF.
This commit is contained in:
randy committed 2026-09-13 11:17:05 +02:00
1 parent 079ac37787
commit c6e4a455b6
12 files changed
+300 -184

No files matched your search

+8 -4
View File
@@ -39,13 +39,17 @@ class AppTest < Minitest::Test
assert_equal({ "available" => false }, json_body(response))
end
def test_hiking_trails_reports_unavailable_when_no_geojson_present
def test_hiking_trails_reports_no_countries_when_none_extracted_yet
response = request.get("/api/hiking-trails")
assert_equal({ "available" => false }, json_body(response))
assert_equal({ "countries" => [] }, json_body(response))
end
def test_hiking_trails_file_404s_when_no_geojson_present
assert_equal 404, request.get("/tiles/hiking_trails.geojson").status
def test_hiking_trails_file_404s_for_a_country_with_no_extract
assert_equal 404, request.get("/tiles/hiking_trails/SK.geojson").status
end
def test_hiking_trails_file_404s_for_a_malformed_country_code
assert_equal 404, request.get("/tiles/hiking_trails/../../etc/passwd.geojson").status
end
def test_kiwix_reports_unavailable_when_no_url_configured
-15
View File
@@ -111,21 +111,6 @@ class HikingTrailsTest < Minitest::Test
assert_equal [[[20.08765, 49.17912], [20.088, 49.18]]], coords
end
def test_extract_merged_deduplicates_by_relation_id_across_files
feature = ->(id) {
{ "type" => "Feature", "properties" => { "id" => id, "name" => "Trail #{id}" }, "geometry" => { "type" => "MultiLineString", "coordinates" => [] } }
}
# Relation 1 shows up in both "country" files (a cross-border trail);
# relation 2 only in the second.
per_file = { "at.osm.pbf" => [feature.call(1)], "cz.osm.pbf" => [feature.call(1), feature.call(2)] }
extract = ->(pbf_path) { { "type" => "FeatureCollection", "features" => per_file.fetch(pbf_path) } }
merged = Maptool::HikingTrails.extract_merged(%w[at.osm.pbf cz.osm.pbf], extract: extract)
ids = merged["features"].map { |f| f["properties"]["id"] }.sort
assert_equal [1, 2], ids
end
def test_parse_opl_ignores_a_dangling_member_reference_but_keeps_the_rest
opl = <<~OPL
w1 Thighway=path Nn10x20.087y49.179,n11x20.088y49.18
+30 -6
View File
@@ -216,20 +216,44 @@ class PmtilesBuilderTest < Minitest::Test
)
end
def test_build_offline_also_writes_a_hiking_trails_geojson
def test_build_offline_also_writes_a_per_country_hiking_trails_geojson
select_regions("SK")
thread = Maptool::PmtilesBuilder.build_offline(data_dir: @data_dir, download_extract: fake_download)
thread.join
# The fake osmium binary doesn't produce real OPL output, just
# confirms the file lands and is valid (empty) GeoJSON — the real
# parsing logic is covered by test_hiking_trails.rb against real OPL
# fixtures.
path = File.join(@data_dir, "hiking_trails.geojson")
assert_path_exists path
# confirms the file lands (one per country) and is valid (empty)
# GeoJSON — the real parsing logic is covered by test_hiking_trails.rb
# against real OPL fixtures.
assert_equal ["SK"], Maptool::PmtilesBuilder.hiking_trails_countries(@data_dir)
path = Maptool::PmtilesBuilder.hiking_trails_country_path(@data_dir, "SK")
assert_equal({ "type" => "FeatureCollection", "features" => [] }, JSON.parse(File.read(path)))
end
def test_build_offline_only_re_extracts_hiking_trails_for_a_newly_added_country
select_regions("SK")
Maptool::PmtilesBuilder.build_offline(data_dir: @data_dir, download_extract: fake_download).join
File.write(@log, "")
select_regions("SK", "AT")
Maptool::PmtilesBuilder.build_offline(
data_dir: @data_dir,
download_extract: fake_download,
fetch_last_modified: ->(_url) { "Wed, 10 Sep 2026 08:00:01 GMT" },
).join
# SK's cached extract is already newer than SK.osm.pbf (untouched
# since the first build), so only AT's hiking trails get (re-)extracted
# — SK.osm.pbf itself is also skipped by cached_extract's own
# Last-Modified check, "osmium merge" runs regardless since two
# countries are now selected.
assert_equal(
["osmium merge", "planetiler --osm-path", "osmium tags-filter", "osmium add-locations-to-ways", "osmium cat"],
log_lines,
)
assert_equal %w[AT SK], Maptool::PmtilesBuilder.hiking_trails_countries(@data_dir)
end
# The actual reasoning: hiking trails are a bonus overlay bolted onto the
# existing offline.pmtiles build, not the core map — an osmium hiccup on
# this one step shouldn't take down an otherwise-successful build.