180 lines
6.8 KiB
Bash
Executable File
180 lines
6.8 KiB
Bash
Executable File
#!/usr/bin/env bash
|
||
# ============================================================================
|
||
# بناء حزمة التوجيه المحلية (Valhalla Routing Package) للأردن
|
||
# ============================================================================
|
||
# English:
|
||
# Builds an on-device Valhalla routing package for Jordan from the same OSM
|
||
# data used by the server-side GraphHopper engine, with real SRTM elevation
|
||
# baked into every edge (grade/ascend/descend), admin street context, and a
|
||
# tile extract tar that the tactical app downloads and routes against 100%
|
||
# offline via valhalla-mobile.
|
||
#
|
||
# Output: infrastructure/osm-data/routing-packages/
|
||
# - jordan-valhalla-<version>.tar (valhalla_tiles.tar + admins.sqlite)
|
||
# - jordan-routing-manifest.json (version, sha256, sizeBytes, builtAt)
|
||
#
|
||
# Runs in two modes:
|
||
# 1. Host mode : ./infrastructure/scripts/build-valhalla-tiles.sh (needs docker)
|
||
# 2. Container : docker compose --profile tiles run --rm valhalla-tiles
|
||
# ============================================================================
|
||
set -euo pipefail
|
||
|
||
VERSION="${JORDAN_ROUTING_VERSION:-jordan-1.0.0-$(date +%Y%m%d)}"
|
||
VALHALLA_IMG="ghcr.io/valhalla/valhalla:latest"
|
||
|
||
# Jordan bounding box with a small buffer so border corridors connect cleanly.
|
||
S=29.05; W=34.75; N=32.80; E=39.45
|
||
|
||
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
|
||
ROOT="$( cd "$SCRIPT_DIR/../.." && pwd )"
|
||
|
||
IN_CONTAINER=false
|
||
if [ -f /.dockerenv ] || [ "${VALHALLA_IN_CONTAINER:-0}" = "1" ]; then
|
||
IN_CONTAINER=true
|
||
fi
|
||
|
||
if [ "$IN_CONTAINER" = true ]; then
|
||
# Compose mounts ./infrastructure/osm-data at /data
|
||
DATA_DIR="/data"
|
||
else
|
||
DATA_DIR="$ROOT/infrastructure/osm-data"
|
||
fi
|
||
|
||
OUT_DIR="$DATA_DIR/routing-packages"
|
||
WORK_DIR="$DATA_DIR/valhalla-work"
|
||
MASTER_PBF="$DATA_DIR/master_map.osm.pbf"
|
||
|
||
log() { echo "🧭 $1"; }
|
||
|
||
run_vh() {
|
||
# Run a command inside the valhalla toolchain (locally or through docker).
|
||
if [ "$IN_CONTAINER" = true ]; then
|
||
bash -c "$1"
|
||
else
|
||
docker run --rm -v "$DATA_DIR":/data -w /data --entrypoint bash "$VALHALLA_IMG" -lc "$1"
|
||
fi
|
||
}
|
||
|
||
if [ ! -f "$MASTER_PBF" ]; then
|
||
echo "❌ master_map.osm.pbf not found at: $MASTER_PBF" >&2
|
||
echo " ضع ملف OSM المدمج في infrastructure/osm-data/master_map.osm.pbf أولاً." >&2
|
||
exit 1
|
||
fi
|
||
|
||
mkdir -p "$OUT_DIR" "$WORK_DIR"
|
||
|
||
log "[1/6] Cutting Jordan extract ($W,$S → $E,$N) ..."
|
||
if run_vh 'command -v osmium >/dev/null 2>&1' 2>/dev/null; then
|
||
run_vh "osmium extract --bbox $W,$S,$E,$N --strategy smart --overwrite \
|
||
/data/master_map.osm.pbf -o /data/valhalla-work/jordan_routing.osm.pbf"
|
||
else
|
||
log " osmium غير متوفر — جاري استخدام مستخرج Geofabrik للأردن (نفس مصدر OSM)."
|
||
curl -fL --retry 3 "https://download.geofabrik.de/asia/jordan-latest.osm.pbf" \
|
||
-o "$WORK_DIR/jordan_routing.osm.pbf"
|
||
fi
|
||
|
||
log "[2/6] Generating config + downloading SRTM elevation for Jordan ..."
|
||
run_vh '
|
||
set -euo pipefail
|
||
cd /data/valhalla-work
|
||
mkdir -p /data/valhalla-work/valhalla_tiles /data/valhalla-work/elevation
|
||
valhalla_build_config \
|
||
--mjolnir-tile-dir /data/valhalla-work/valhalla_tiles \
|
||
--mjolnir-concurrency "$(nproc)" \
|
||
--mjolnir-tile-extract /data/valhalla-work/valhalla_tiles.tar \
|
||
--mjolnir-admin /data/valhalla-work/admins.sqlite \
|
||
--additional-data-elevation /data/valhalla-work/elevation \
|
||
> /data/valhalla-work/valhalla.json
|
||
|
||
# SRTM 30m elevation for Jordan bbox
|
||
valhalla_build_elevation -c /data/valhalla-work/valhalla.json -b 34.75,29.05,39.45,32.80 || true
|
||
'
|
||
|
||
log "[3/6] Building admin database (governorate/street context) ..."
|
||
run_vh 'valhalla_build_admins --config /data/valhalla-work/valhalla.json /data/valhalla-work/jordan_routing.osm.pbf'
|
||
|
||
log "[4/6] Building routing graph tiles (roads + oneway + elevation grades) ..."
|
||
run_vh 'valhalla_build_tiles --config /data/valhalla-work/valhalla.json -j "$(nproc)" /data/valhalla-work/jordan_routing.osm.pbf'
|
||
|
||
log "[5/6] Creating tile extract (tar) and verifying a test route ..."
|
||
run_vh '
|
||
set -euo pipefail
|
||
cd /data/valhalla-work
|
||
rm -f /data/valhalla-work/valhalla_tiles.tar
|
||
valhalla_build_extract --config /data/valhalla-work/valhalla.json -v --overwrite
|
||
|
||
# فحص سلامة: عمان ← العقبة يجب أن ينجح بالكامل على الغراف المحلي.
|
||
valhalla_service /data/valhalla-work/valhalla.json route \
|
||
"{\"locations\":[{\"lat\":31.9539,\"lon\":35.9106},{\"lat\":29.5320,\"lon\":35.0060}],\"costing\":\"auto\",\"units\":\"kilometers\",\"directions_options\":{\"language\":\"ar\"}}" \
|
||
> /data/valhalla-work/test_route.json 2>/dev/null || true
|
||
python3 - <<'PY'
|
||
import json, sys
|
||
try:
|
||
with open("/data/valhalla-work/test_route.json") as f:
|
||
d = json.load(f)
|
||
t = d.get("trip", {})
|
||
status = t.get("status")
|
||
msg = t.get("status_message", "unknown error")
|
||
if status == 0:
|
||
dist = round(t.get("summary", {}).get("length", 0), 1)
|
||
maneuvers = sum(len(l.get("maneuvers", [])) for l in t.get("legs", []))
|
||
print(f" ✅ test route OK: {dist} km | {maneuvers} maneuvers")
|
||
else:
|
||
print(f" ⚠️ test route status: {status} ({msg})")
|
||
except Exception as e:
|
||
print(f" ℹ️ test route check note: {e}")
|
||
PY
|
||
'
|
||
|
||
log "[5.5/6] Extracting road graph (jordan_roads.db) for on-device SQLite routing ..."
|
||
PY_SCRIPT="$SCRIPT_DIR/extract_roads_graph.py"
|
||
if [ ! -f "$PY_SCRIPT" ] && [ -f "$DATA_DIR/../infrastructure/scripts/extract_roads_graph.py" ]; then
|
||
PY_SCRIPT="$DATA_DIR/../infrastructure/scripts/extract_roads_graph.py"
|
||
fi
|
||
|
||
if [ -f "$PY_SCRIPT" ]; then
|
||
python3 "$PY_SCRIPT" "$WORK_DIR/jordan_routing.osm.pbf" "$WORK_DIR/jordan_roads.db" || true
|
||
fi
|
||
|
||
log "[6/6] Packaging + manifest ..."
|
||
FINAL_TAR="$OUT_DIR/jordan-valhalla-$VERSION.tar"
|
||
rm -f "$FINAL_TAR"
|
||
|
||
PACK_ITEMS="valhalla_tiles.tar admins.sqlite"
|
||
if [ -f "$WORK_DIR/jordan_roads.db" ]; then
|
||
PACK_ITEMS="$PACK_ITEMS jordan_roads.db"
|
||
log " Included jordan_roads.db in package."
|
||
fi
|
||
|
||
tar -cf "$FINAL_TAR" -C "$WORK_DIR" $PACK_ITEMS
|
||
|
||
if [ "$IN_CONTAINER" = false ]; then
|
||
SIZE=$(stat -f%z "$FINAL_TAR" 2>/dev/null || stat -c%s "$FINAL_TAR")
|
||
SHA256=$(shasum -a 256 "$FINAL_TAR" | awk '{print $1}')
|
||
else
|
||
SIZE=$(wc -c < "$FINAL_TAR" | tr -d " ")
|
||
SHA256=$(sha256sum "$FINAL_TAR" | awk "{print \$1}")
|
||
fi
|
||
|
||
cat > "$OUT_DIR/jordan-routing-manifest.json" <<MANIFEST
|
||
{
|
||
"packageId": "jordan-valhalla-routing",
|
||
"version": "$VERSION",
|
||
"fileName": "jordan-valhalla-$VERSION.tar",
|
||
"sizeBytes": $SIZE,
|
||
"sha256": "$SHA256",
|
||
"engine": "valhalla",
|
||
"elevation": "srtm-30m",
|
||
"bbox": {"south": $S, "west": $W, "north": $N, "east": $E},
|
||
"builtAt": "$(date -u +%Y-%m-%dT%H:%M:%SZ)"
|
||
}
|
||
MANIFEST
|
||
|
||
chmod -R a+rwX "$OUT_DIR" 2>/dev/null || true
|
||
|
||
rm -rf "$WORK_DIR"
|
||
|
||
echo ""
|
||
echo "✅ Done. حزمة التوجيه المحلية جاهزة:"
|
||
ls -lh "$FINAL_TAR" "$OUT_DIR/jordan-routing-manifest.json"
|