#!/usr/bin/env bash # OpenStreetMap routing graph build (docs/offroad/PLAN.md R10.3a-b, Appendix E.7). PC only: the VM never builds graphs. # # tools/geodata/run offroad-graph/build-osm --region spike-utah # Utah extract (dev default) # HG_OSM_EXTRACT=north-america/canada/quebec tools/geodata/run offroad-graph/build-osm --region spike-mauricie # HG_OSM_PBF=/data/downloads/osm/north-america-latest.osm.pbf tools/geodata/run offroad-graph/build-osm --region us-west # HG_OSM_EXTRACT=north-america/us/delaware tools/geodata/run offroad-graph/build-osm --bbox -75.8,38.4,-75.0,39.9 # HG_DB_URL=none tools/geodata/run offroad-graph/build-osm --fixture # fixtures/osm-mini.osm → the test archive # # Reads the extract with GDAL's OSM driver (MIT; osmconf.ini) clipped to the region's box, straight into files: the # ways and the ford, fuel and charging points. Any GDAL ERROR fails the run. osm_graph.py keeps the vehicle ways, sets # each one's per-class state and attributes, nodes them (route-graph/lib/noding.py, 1 m T-snap) and adds the climb (HG_TERRAIN) into # /data/build/offroad-graph//osm/: edges-osm.geojsonseq, pois-osm.geojsonseq, report-osm.json. Then the tiles # (route-graph/lib/tiles.py --graph offroad-osm: layers e and p, the ODbL notice in the metadata) into /data/tiles/offroad-graph-osm.pmtiles, # with the sizes in tiles.json; offroad-graph/register --graph offroad-osm records the build. # ODbL (osm-offroad-graph, osm-derived.json): its own files, archive and rows; nothing here reads or writes geo.routes, # any official table or the official graph's files, and the two archives are never joined (RJ-14). set -euo pipefail HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" # shellcheck source=../lib/common.sh source "$HERE/../lib/common.sh" region="" bbox="" fixture="" while [[ $# -gt 0 ]]; do case "$1" in --region) region="$2"; shift 2 ;; --bbox) bbox="$2"; shift 2 ;; --fixture) fixture=1; shift ;; *) die "usage: offroad-graph/build-osm [--region | --bbox w,s,e,n | --fixture]" ;; esac done # A recorded register entry (ODbL: own archive and rows, credit, the 4.6 offer) before anything is written. python3 -I "$HERE/../republishing/republishing.py" require dataset:osm-offroad-graph if [[ -n "$fixture" ]]; then region=fixture-osm bbox="-180,-85,180,85" PBF="$HERE/fixtures/osm-mini.osm" version=20261021-fixture source_version="OpenStreetMap-style fixture (synthetic)" else [[ -n "$region" || -n "$bbox" ]] || die "usage: offroad-graph/build-osm [--region | --bbox w,s,e,n | --fixture]" if [[ -n "$region" ]]; then bbox="$(python3 -c 'import sys, yaml r = yaml.safe_load(open(sys.argv[1])).get(sys.argv[2]) if not r: sys.exit("no region %s in regions.yaml" % sys.argv[2]) print(",".join(str(v) for v in r["bbox"]))' "$HERE/regions.yaml" "$region")" else region="bbox-${bbox//[,.]/_}" # the same name offroad-graph/build gives the box fi EXTRACT="${HG_OSM_EXTRACT:-north-america/us/utah}" PBF="${HG_OSM_PBF:-}" DOWNLOADS=/data/downloads/osm if [[ -z "$PBF" ]]; then [[ "$EXTRACT" =~ ^[a-z0-9/-]+$ ]] || die "bad extract name: $EXTRACT" PBF="$DOWNLOADS/${EXTRACT//\//-}-latest.osm.pbf" mkdir -p "$DOWNLOADS" if [[ -z "$(find "$PBF" -mtime -7 2>/dev/null)" ]]; then # Geofabrik refreshes daily; a week-old file is reused log "Downloading https://download.geofabrik.de/$EXTRACT-latest.osm.pbf" curl -fsSL --retry 3 -A "HuntGrid geodata pipeline" -o "$PBF.part" "https://download.geofabrik.de/$EXTRACT-latest.osm.pbf" mv -f "$PBF.part" "$PBF" fi fi [[ -f "$PBF" ]] || die "no OSM file at $PBF" [[ -n "${HG_GIT_SHA:-}" ]] || die "HG_GIT_SHA is not set (tools/geodata/run sets it): the graph version needs the commit" version="$(date -u +%Y%m%d)-$HG_GIT_SHA" source_version="OpenStreetMap $(date -u -r "$PBF" +%Y-%m-%d)" fi IFS=, read -r w s e n <<<"$bbox" for v in "$w" "$s" "$e" "$n"; do [[ "$v" =~ ^-?[0-9]+(\.[0-9]+)?$ ]] || die "bad box: $bbox"; done out="/data/build/offroad-graph/$region/osm" mkdir -p "$out" "$BUILD_DIR" started=$SECONDS read_layer() { # rm -f "$3" ogr2ogr -f GeoJSONSeq "$3" "$PBF" "$1" -oo CONFIG_FILE="$HERE/osmconf.ini" -where "$2" -spat "$w" "$s" "$e" "$n" \ -lco RS=NO -lco COORDINATE_PRECISION=7 -dim XY --config CPL_TMPDIR "$BUILD_DIR" --config OSM_MAX_TMPFILE_SIZE 4096 \ --config OSM_COMPRESS_NODES YES 2> "$out/gdal.err" || { cat "$out/gdal.err" >&2; die "ogr2ogr failed on $1"; } if grep -q '^ERROR' "$out/gdal.err"; then cat "$out/gdal.err" >&2; die "GDAL reported errors reading $1"; fi } log "Ways and points of $region from $(basename "$PBF") ($(du -h "$PBF" | cut -f1))" read_layer lines "highway IN ('track','path','unclassified','service','residential','tertiary','secondary','primary','trunk') OR piste_type = 'snowmobile'" "$out/lines.geojsonseq" read_layer points "ford IS NOT NULL OR amenity IN ('fuel','charging_station')" "$out/points.geojsonseq" read_layer multipolygons "amenity IN ('fuel','charging_station')" "$out/areas.geojsonseq" # stations mapped as areas cat "$out/areas.geojsonseq" >>"$out/points.geojsonseq" log "$(wc -l <"$out/lines.geojsonseq") ways, $(wc -l <"$out/points.geojsonseq") ford, fuel and charging points ($((SECONDS - started)) s)" climb=(--terrain "${HG_TERRAIN:-/data/tiles/terrain.pmtiles}") [[ -n "${HG_NO_ELEVATION:-}" || -n "$fixture" ]] && climb=(--no-elevation) # a look without terrain: no up/dn/gmax python3 "$HERE/osm_graph.py" "$out/lines.geojsonseq" "$out/points.geojsonseq" "$out" "${climb[@]}" printf '{"region": "%s", "bbox": [%s, %s, %s, %s], "osm_extract": "%s", "source_version": "%s"}\n' \ "$region" "$w" "$s" "$e" "$n" "$(basename "$PBF")" "$source_version" >"$out/region.json" # for register --graph osm archive="$out/offroad-graph-osm.pmtiles" [[ -n "$fixture" ]] && archive="$out/osm-mini.pmtiles" python3 "$HERE/../route-graph/lib/tiles.py" "$out/edges-osm.geojsonseq" "$archive" --graph offroad-osm --pois "$out/pois-osm.geojsonseq" \ --version "$version" --source-version "$source_version" --attribution "© OpenStreetMap contributors" --report "$out/tiles.json" if [[ -n "$fixture" ]]; then log "wrote $archive: copy it to tests/fixtures/offroad-graph/" exit 0 fi publish_tiles "$archive" log "published /data/tiles/offroad-graph-osm.pmtiles ($version) in $((SECONDS - started)) s; sizes in $out/tiles.json" log "next: tools/geodata/run offroad-graph/register --graph offroad-osm --region $region"