Split offline hiking trails per country, add a picker instead of one big file
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:
1 parent
079ac37787
commit
c6e4a455b6
12 files changed
+300
-184
No files matched your search
@@ -63,22 +63,6 @@ module Maptool
|
||||
end
|
||||
end
|
||||
|
||||
# Runs extract() over one .osm.pbf per selected country and combines the
|
||||
# results into a single FeatureCollection — a route relation crossing a
|
||||
# border (an E-path, say) shows up once per country whose extract it
|
||||
# was pulled from, deduplicated here by OSM relation id (properties.id)
|
||||
# rather than left to render as overlapping duplicate lines.
|
||||
# extract: same injectable-seam reasoning as PmtilesBuilder's own
|
||||
# download_extract:/fetch_manifest: — lets a test swap out the real
|
||||
# (osmium-shelling-out) implementation for a canned one.
|
||||
def self.extract_merged(pbf_paths, extract: method(:extract))
|
||||
seen = {}
|
||||
pbf_paths.each do |pbf_path|
|
||||
extract.call(pbf_path)["features"].each { |feature| seen[feature["properties"]["id"]] ||= feature }
|
||||
end
|
||||
{ "type" => "FeatureCollection", "features" => seen.values }
|
||||
end
|
||||
|
||||
# Pure — no subprocess — so it's cheaply testable against a canned OPL
|
||||
# fixture, same "keep the real logic testable without the real tool
|
||||
# installed" reasoning OverpassPois.to_geojson already gives for staying
|
||||
|
||||
@@ -299,26 +299,55 @@ module Maptool
|
||||
end
|
||||
private_class_method :extract_offline
|
||||
|
||||
# Runs over the same per-country .osm.pbf files sources above already
|
||||
# One GeoJSON per country (data/hiking_trails/<CODE>.geojson), not one
|
||||
# merged file — the frontend only ever needs to show a country at a
|
||||
# time the viewer actually picks (see HikingTrailsControl's country
|
||||
# checklist), and a combined multi-country file was needlessly making
|
||||
# every viewer fetch every selected country's trails regardless.
|
||||
#
|
||||
# Runs over the same per-country .osm.pbf files `sources` above already
|
||||
# downloaded/cached for offline.pmtiles itself — no separate download.
|
||||
# Best-effort: a hiking-trails extraction problem (a malformed PBF
|
||||
# region, osmium hiccup on one particular file) shouldn't fail the
|
||||
# whole offline.pmtiles build over what's a bonus overlay, not the
|
||||
# core map — logged into the sidecar meta's own "error" field instead,
|
||||
# so it's visible without taking build_offline itself down.
|
||||
# Cached the same way geofabrik_cache is: skipped for a country whose
|
||||
# extract is already newer than its source .osm.pbf, so growing the
|
||||
# region selection by one country only extracts that one, not every
|
||||
# already-covered country all over again.
|
||||
#
|
||||
# Best-effort per country: an extraction problem (a malformed PBF
|
||||
# region, an osmium hiccup) shouldn't fail the whole offline.pmtiles
|
||||
# build, or even the other countries in this same selection, over
|
||||
# what's a bonus overlay, not the core map.
|
||||
def self.extract_hiking_trails(sources, data_dir)
|
||||
geojson = HikingTrails.extract_merged(sources.map { |s| s["path"] })
|
||||
tmp_path = "#{hiking_trails_path(data_dir)}.tmp"
|
||||
File.write(tmp_path, JSON.generate(geojson))
|
||||
File.rename(tmp_path, hiking_trails_path(data_dir))
|
||||
rescue HikingTrails::ExtractError => e
|
||||
File.delete(tmp_path) if tmp_path && File.exist?(tmp_path)
|
||||
warn "hiking trails extraction failed, skipping: #{e.message}"
|
||||
FileUtils.mkdir_p(hiking_trails_dir(data_dir))
|
||||
sources.each do |source|
|
||||
out_path = hiking_trails_country_path(data_dir, source["code"])
|
||||
next if File.exist?(out_path) && File.mtime(out_path) >= File.mtime(source["path"])
|
||||
|
||||
tmp_path = "#{out_path}.tmp"
|
||||
begin
|
||||
File.write(tmp_path, JSON.generate(HikingTrails.extract(source["path"])))
|
||||
File.rename(tmp_path, out_path)
|
||||
rescue HikingTrails::ExtractError => e
|
||||
File.delete(tmp_path) if File.exist?(tmp_path)
|
||||
warn "hiking trails extraction failed for #{source["code"]}, skipping: #{e.message}"
|
||||
end
|
||||
end
|
||||
end
|
||||
private_class_method :extract_hiking_trails
|
||||
|
||||
def self.hiking_trails_path(data_dir)
|
||||
File.join(data_dir, "hiking_trails.geojson")
|
||||
def self.hiking_trails_dir(data_dir)
|
||||
File.join(data_dir, "hiking_trails")
|
||||
end
|
||||
|
||||
def self.hiking_trails_country_path(data_dir, code)
|
||||
File.join(hiking_trails_dir(data_dir), "#{code}.geojson")
|
||||
end
|
||||
|
||||
# Country codes with an already-extracted offline hiking-trails file,
|
||||
# sorted — not necessarily the same as the *current* region selection
|
||||
# (accumulates like geofabrik_cache does; a country deselected after
|
||||
# being built once is left alone rather than swept up here).
|
||||
def self.hiking_trails_countries(data_dir)
|
||||
Dir.glob(File.join(hiking_trails_dir(data_dir), "*.geojson")).map { |path| File.basename(path, ".geojson") }.sort
|
||||
end
|
||||
|
||||
# Renders a report_step string like "fetching PL (3/4) — done: SK, CZ —
|
||||
@@ -468,7 +497,7 @@ module Maptool
|
||||
|
||||
File.delete(File.join(data_dir, "offline.pmtiles")) if File.exist?(File.join(data_dir, "offline.pmtiles"))
|
||||
File.delete(offline_meta_path(data_dir)) if File.exist?(offline_meta_path(data_dir))
|
||||
File.delete(hiking_trails_path(data_dir)) if File.exist?(hiking_trails_path(data_dir))
|
||||
FileUtils.remove_entry(hiking_trails_dir(data_dir)) if Dir.exist?(hiking_trails_dir(data_dir))
|
||||
cache_dir = geofabrik_cache_dir(data_dir)
|
||||
FileUtils.remove_entry(cache_dir) if Dir.exist?(cache_dir)
|
||||
end
|
||||
|
||||
Reference in new issue
Block a user