Inhaltsverzeichnis

OSRM (Open Source Routing Machine) ist eine Open-Source Routing-Engine für Straßennetze auf Basis von OpenStreetMap-Daten (OSM). Das Projekt ist in C++ implementiert und auf sehr schnelle Routenberechnung ausgelegt. Im Unterschied zu Geocodern wie Nominatim übersetzt OSRM keine Adressen in Koordinaten, sondern berechnet Wege zwischen bereits bekannten Koordinaten auf dem zugrunde liegenden Straßennetz.

OSRM kann sowohl als HTTP-Dienst betrieben als auch als Bibliothek genutzt werden. Die offizielle Dokumentation beschreibt dafür eine HTTP-API, eine C++ Bibliothek namens libosrm sowie einen Node.js Wrapper.

Demo https://router.project-osrm.org/route/v1/driving/16.3725,48.2082;16.3738,48.2100?overview=false

import requests
 
def get_route(start_lon: float, start_lat: float, end_lon: float, end_lat: float) -> dict:
    url = (
        f"http://localhost:5000/route/v1/driving/"
        f"{start_lon},{start_lat};{end_lon},{end_lat}"
    )
    params = {
        "overview": "false",
        "steps": "true"
    }
 
    response = requests.get(url, params=params, timeout=10)
    response.raise_for_status()
    return response.json()
 
 
result = get_route(16.37208, 48.20849, 16.37382, 48.20672)
 
route = result["routes"][0]
print("Distanz in Metern:", route["distance"])
print("Dauer in Sekunden:", route["duration"])

Datenbasis

OSRM verarbeitet OpenStreetMap-Daten in einem Vorbereitungsprozess und erzeugt daraus speziell optimierte Routing-Datenstrukturen. Laut Projektseite unterstützt OSRM den Import von OSM-Daten (siehe PBF), ist für sehr große Netze ausgelegt und bietet vorkonfigurierte Profile für Auto, Fahrrad und Fußweg. Das Routing-Verhalten kann über Profile angepasst werden.

Für die Datenvorbereitung werden Werkzeuge wie osrm-extract, osrm-contract, osrm-partition und osrm-customize verwendet. Welche Schritte nötig sind, hängt vom gewählten Routing-Algorithmus ab. Die Dokumentation und projektnahen Quellen unterscheiden dabei insbesondere zwischen CH (Contraction Hierarchies) und MLD (Multi-Level Dijkstra).

API-Endpunkte

Die HTTP-API von OSRM verwendet in Version 5.x das Protokoll v1. Die zentralen Dienste sind:

Route

Der Dienst route berechnet die schnellste Route zwischen Koordinaten auf Basis des gewählten Profils, zum Beispiel driving, bike oder foot. Er liefert unter anderem Distanz, Dauer und auf Wunsch Geometrien und Turn-by-Turn-Informationen.

Beispiel:

/route/v1/driving/16.37208,48.20849;16.37382,48.20672?overview=false

Nearest

Der Dienst nearest ordnet eine Koordinate dem nächstgelegenen Straßensegment zu. Das ist nützlich, um GPS-Punkte auf das Routing-Netz zu „snappen“.

Table

Der Dienst table berechnet eine Matrix aus Fahrzeiten oder Distanzen zwischen mehreren Koordinaten. Laut API-Dokumentation liefert table bei Distanzen nicht die Luftlinie, sondern die Distanzen der jeweils schnellsten Routen im Straßennetz.

Match

Der Dienst match dient dem Map-Matching. Dabei werden GPS-Spuren auf wahrscheinliche Straßenverläufe abgebildet. Das ist besonders für Tracking- oder Flottenanwendungen relevant.

Trip

Der Dienst trip berechnet eine sinnvolle Reihenfolge für mehrere Wegpunkte. Das Problem ist verwandt mit dem Traveling-Salesman-Problem und eignet sich für einfache Tourenplanung.

Tile

Der Dienst tile stellt Kachelinformationen für Debugging und Visualisierung bereit.

Profile

Das Routing-Verhalten von OSRM wird durch Lua-Profile bestimmt. Das Profil wird bereits bei der Datenaufbereitung festgelegt und definiert, welche Straßen befahrbar sind und wie Geschwindigkeiten, Einschränkungen oder Präferenzen behandelt werden. Die offiziellen Beispiele nennen insbesondere car, bike und foot.

Dadurch lässt sich OSRM an verschiedene Einsatzszenarien anpassen, etwa für klassische Autonavigation, Fahrrad-Routing oder Fußwege. Eigene Profile sind möglich, wenn projektspezifische Regeln benötigt werden. Diese Schlussfolgerung ergibt sich aus der dokumentierten Profil-Logik und der Lua-basierten Konfiguration.

Setup

WSL Demo

Non persistent

#!/usr/bin/env bash
set -euo pipefail
 
PBF_PATH="${1:-}"
PROFILE="${2:-car}"
PORT="${PORT:-5000}"
CONTAINER_NAME="${CONTAINER_NAME:-osrm-demo}"
WORKDIR="${WORKDIR:-/tmp/osrm-demo-data}"
 
need_root() {
  if [[ "${EUID}" -ne 0 ]]; then
    echo "Bitte mit sudo starten:"
    echo "  sudo bash setup-osrm.sh /pfad/zur/datei.osm.pbf car"
    exit 1
  fi
}
 
check_args() {
  if [[ -z "$PBF_PATH" ]]; then
    echo "Fehler: Bitte Pfad zur .osm.pbf angeben."
    exit 1
  fi
 
  if [[ ! -f "$PBF_PATH" ]]; then
    echo "Fehler: Datei nicht gefunden: $PBF_PATH"
    exit 1
  fi
 
  case "$PROFILE" in
    car)  LUA_PROFILE="/opt/car.lua" ;;
    bike) LUA_PROFILE="/opt/bicycle.lua" ;;
    foot) LUA_PROFILE="/opt/foot.lua" ;;
    *)
      echo "Fehler: Unbekanntes Profil '$PROFILE'. Erlaubt: car | bike | foot"
      exit 1
      ;;
  esac
 
  export LUA_PROFILE
}
 
install_docker_if_missing() {
  if command -v docker >/dev/null 2>&1; then
    echo "Docker ist bereits vorhanden: $(docker --version || true)"
    return
  fi
 
  echo "==> Docker wird installiert"
 
  apt-get update
  apt-get install -y ca-certificates curl
 
  install -m 0755 -d /etc/apt/keyrings
  curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
  chmod a+r /etc/apt/keyrings/docker.asc
 
  . /etc/os-release
  echo \
    "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu ${UBUNTU_CODENAME:-$VERSION_CODENAME} stable" \
    > /etc/apt/sources.list.d/docker.list
 
  apt-get update
  apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
 
  if command -v service >/dev/null 2>&1; then
    service docker start || true
  fi
 
  if [[ -n "${SUDO_USER:-}" ]]; then
    usermod -aG docker "$SUDO_USER" || true
  fi
}
 
start_docker_if_needed() {
  if docker info >/dev/null 2>&1; then
    echo "Docker läuft bereits."
    return
  fi
 
  echo "==> Docker-Daemon wird gestartet"
  if command -v service >/dev/null 2>&1; then
    service docker start || true
  fi
 
  for _ in $(seq 1 20); do
    if docker info >/dev/null 2>&1; then
      echo "Docker läuft."
      return
    fi
    sleep 1
  done
 
  echo "Fehler: Docker ist installiert, aber der Daemon läuft nicht."
  exit 1
}
 
prepare_paths() {
  PBF_ABS="$(readlink -f "$PBF_PATH")"
  PBF_BASENAME="$(basename "$PBF_ABS")"
  PBF_NAME="${PBF_BASENAME%.osm.pbf}"
 
  mkdir -p "$WORKDIR"
 
  export PBF_ABS PBF_BASENAME PBF_NAME
}
 
cleanup_old_files() {
  echo "==> Alte OSRM-Dateien entfernen in $WORKDIR"
  find "$WORKDIR" -maxdepth 1 -type f \( \
    -name "${PBF_NAME}.osrm*" -o \
    -name "${PBF_NAME}.timestamp" -o \
    -name "${PBF_BASENAME}" \
  \) -print -delete || true
 
  if docker ps -a --format '{{.Names}}' | grep -qx "$CONTAINER_NAME"; then
    echo "==> Alten Container entfernen: $CONTAINER_NAME"
    docker rm -f "$CONTAINER_NAME" >/dev/null 2>&1 || true
  fi
}
 
prepare_input_file() {
  echo "==> Kopiere Eingabedatei nach $WORKDIR"
  cp -f "$PBF_ABS" "$WORKDIR/$PBF_BASENAME"
}
 
run_osrm_pipeline() {
  echo "==> Eingabedatei: $PBF_ABS"
  echo "==> Arbeitsverzeichnis: $WORKDIR"
  echo "==> Profil: $PROFILE"
  echo "==> Port: $PORT"
 
  echo "==> OSRM-Image laden"
  docker pull osrm/osrm-backend
 
  echo "==> osrm-extract"
  docker run --rm -t \
    -v "$WORKDIR:/data" \
    osrm/osrm-backend \
    osrm-extract -p "$LUA_PROFILE" "/data/$PBF_BASENAME"
 
  echo "==> osrm-partition"
  docker run --rm -t \
    -v "$WORKDIR:/data" \
    osrm/osrm-backend \
    osrm-partition "/data/${PBF_NAME}.osrm"
 
  echo "==> osrm-customize"
  docker run --rm -t \
    -v "$WORKDIR:/data" \
    osrm/osrm-backend \
    osrm-customize "/data/${PBF_NAME}.osrm"
 
  echo "==> osrm-routed starten"
  docker run -d \
    --name "$CONTAINER_NAME" \
    -p "$PORT:5000" \
    -v "$WORKDIR:/data" \
    osrm/osrm-backend \
    osrm-routed --algorithm mld "/data/${PBF_NAME}.osrm" >/dev/null
}
 
print_done() {
  echo
  echo "Fertig."
  echo "Test:"
  echo "  http://localhost:${PORT}/route/v1/driving/16.3725,48.2082;16.3370,48.1980?overview=false"
  echo
  echo "Logs:"
  echo "  sudo docker logs -f $CONTAINER_NAME"
  echo
  echo "Stoppen:"
  echo "  sudo docker rm -f $CONTAINER_NAME"
}
 
need_root
check_args
install_docker_if_missing
start_docker_if_needed
prepare_paths
cleanup_old_files
prepare_input_file
run_osrm_pipeline
print_done
sudo bash setup-osrm.sh wien.osm.pbf car

Ubuntu Setup Manager

curl "https://download.geofabrik.de/europe/austria-latest.osm.pbf" -o austria-latest.osm.pbf
#!/usr/bin/env bash
set -euo pipefail
 
COMMAND="${1:-}"
PBF_PATH="${2:-}"
PROFILE="${3:-car}"
 
PORT="${PORT:-5000}"
CONTAINER_NAME="${CONTAINER_NAME:-osrm-demo}"
WORKDIR="${WORKDIR:-/var/lib/osrm}"
IMAGE="${IMAGE:-osrm/osrm-backend}"
 
need_root() {
  if [[ "${EUID}" -ne 0 ]]; then
    echo "Bitte mit sudo starten."
    echo "Beispiele:"
    echo "  sudo bash osrm-manager.sh build /pfad/austria-latest.osm.pbf car"
    echo "  sudo bash osrm-manager.sh start /pfad/austria-latest.osm.pbf car"
    exit 1
  fi
}
 
usage() {
  cat <<EOF
Verwendung:
  sudo bash osrm-manager.sh build  /pfad/datei.osm.pbf [car|bike|foot]
  sudo bash osrm-manager.sh start  /pfad/datei.osm.pbf [car|bike|foot]
  sudo bash osrm-manager.sh stop
  sudo bash osrm-manager.sh restart /pfad/datei.osm.pbf [car|bike|foot]
  sudo bash osrm-manager.sh status
  sudo bash osrm-manager.sh logs
  sudo bash osrm-manager.sh url [car|bike|foot]
 
Umgebungsvariablen:
  PORT=5000
  CONTAINER_NAME=osrm-demo
  WORKDIR=/var/lib/osrm
  IMAGE=osrm/osrm-backend
 
Hinweis:
  Für jedes Profil werden eigene Daten verwendet:
    /var/lib/osrm/car
    /var/lib/osrm/bike
    /var/lib/osrm/foot
EOF
}
 
check_profile() {
  case "$PROFILE" in
    car)
      LUA_PROFILE="/opt/car.lua"
      SERVICE_MODE="driving"
      ;;
    bike)
      LUA_PROFILE="/opt/bicycle.lua"
      SERVICE_MODE="cycling"
      ;;
    foot)
      LUA_PROFILE="/opt/foot.lua"
      SERVICE_MODE="walking"
      ;;
    *)
      echo "Fehler: Unbekanntes Profil '$PROFILE'. Erlaubt: car | bike | foot"
      exit 1
      ;;
  esac
 
  export LUA_PROFILE SERVICE_MODE
}
 
install_docker_if_missing() {
  if command -v docker >/dev/null 2>&1; then
    echo "Docker vorhanden: $(docker --version || true)"
    return
  fi
 
  echo "==> Docker wird installiert"
 
  apt-get update
  apt-get install -y ca-certificates curl
 
  install -m 0755 -d /etc/apt/keyrings
  curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
  chmod a+r /etc/apt/keyrings/docker.asc
 
  . /etc/os-release
  echo \
    "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu ${UBUNTU_CODENAME:-$VERSION_CODENAME} stable" \
    > /etc/apt/sources.list.d/docker.list
 
  apt-get update
  apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
 
  systemctl enable docker || true
  systemctl start docker || true
 
  if [[ -n "${SUDO_USER:-}" ]]; then
    usermod -aG docker "$SUDO_USER" || true
  fi
}
 
start_docker_if_needed() {
  if docker info >/dev/null 2>&1; then
    echo "Docker läuft."
    return
  fi
 
  echo "==> Docker-Daemon wird gestartet"
  systemctl enable docker || true
  systemctl start docker || true
 
  for _ in $(seq 1 20); do
    if docker info >/dev/null 2>&1; then
      echo "Docker läuft."
      return
    fi
    sleep 1
  done
 
  echo "Fehler: Docker ist installiert, aber der Daemon läuft nicht."
  exit 1
}
 
check_pbf_arg() {
  if [[ -z "$PBF_PATH" ]]; then
    echo "Fehler: Bitte Pfad zur .osm.pbf angeben."
    exit 1
  fi
 
  if [[ ! -f "$PBF_PATH" ]]; then
    echo "Fehler: Datei nicht gefunden: $PBF_PATH"
    exit 1
  fi
}
 
prepare_paths() {
  check_pbf_arg
 
  PBF_ABS="$(readlink -f "$PBF_PATH")"
  PBF_BASENAME="$(basename "$PBF_ABS")"
  PBF_NAME="${PBF_BASENAME%.osm.pbf}"
 
  PROFILE_DIR="${WORKDIR}/${PROFILE}"
  DATASET_FILE="${PROFILE_DIR}/${PBF_BASENAME}"
  OSRM_BASE="${PROFILE_DIR}/${PBF_NAME}.osrm"
  PROFILE_CONTAINER_NAME="${CONTAINER_NAME}-${PROFILE}"
 
  mkdir -p "$PROFILE_DIR"
 
  export PBF_ABS PBF_BASENAME PBF_NAME PROFILE_DIR DATASET_FILE OSRM_BASE PROFILE_CONTAINER_NAME
}
 
require_prepared_dataset() {
  local missing=0
  local required=(
    "${OSRM_BASE}"
    "${OSRM_BASE}.ramIndex"
    "${OSRM_BASE}.fileIndex"
    "${OSRM_BASE}.edges"
    "${OSRM_BASE}.geometry"
    "${OSRM_BASE}.turn_weight_penalties"
    "${OSRM_BASE}.turn_duration_penalties"
    "${OSRM_BASE}.datasource_names"
    "${OSRM_BASE}.names"
    "${OSRM_BASE}.properties"
    "${OSRM_BASE}.icd"
  )
 
  for f in "${required[@]}"; do
    if [[ ! -f "$f" ]]; then
      echo "Fehlt: $f"
      missing=1
    fi
  done
 
  if [[ "$missing" -ne 0 ]]; then
    echo
    echo "Fehler: OSRM-Datensatz unvollständig."
    echo "Führe zuerst aus:"
    echo "  sudo bash osrm-manager.sh build \"$PBF_ABS\" \"$PROFILE\""
    exit 1
  fi
}
 
remove_container_if_exists() {
  if docker ps -a --format '{{.Names}}' | grep -qx "$PROFILE_CONTAINER_NAME"; then
    echo "==> Entferne alten Container: $PROFILE_CONTAINER_NAME"
    docker rm -f "$PROFILE_CONTAINER_NAME" >/dev/null 2>&1 || true
  fi
}
 
copy_input_if_needed() {
  if [[ -f "$DATASET_FILE" ]]; then
    local src_size dst_size
    src_size="$(stat -c '%s' "$PBF_ABS")"
    dst_size="$(stat -c '%s' "$DATASET_FILE" 2>/dev/null || echo 0)"
 
    if [[ "$src_size" == "$dst_size" ]]; then
      echo "==> PBF bereits vorhanden: $DATASET_FILE"
      return
    fi
  fi
 
  echo "==> Kopiere Eingabedatei nach $PROFILE_DIR"
  cp -f "$PBF_ABS" "$DATASET_FILE"
}
 
cleanup_previous_dataset_files() {
  echo "==> Entferne alte OSRM-Dateien für $PBF_NAME in $PROFILE_DIR"
  find "$PROFILE_DIR" -maxdepth 1 -type f \
    \( -name "${PBF_NAME}.osrm*" -o -name "${PBF_NAME}.timestamp" \) \
    -print -delete || true
}
 
build_dataset() {
  echo "==> Image laden"
  docker pull "$IMAGE"
 
  copy_input_if_needed
  cleanup_previous_dataset_files
 
  echo "==> osrm-extract ($PROFILE)"
  docker run --rm -t \
    -v "$PROFILE_DIR:/data" \
    "$IMAGE" \
    osrm-extract -p "$LUA_PROFILE" "/data/$PBF_BASENAME"
 
  echo "==> osrm-partition"
  docker run --rm -t \
    -v "$PROFILE_DIR:/data" \
    "$IMAGE" \
    osrm-partition "/data/${PBF_NAME}.osrm"
 
  echo "==> osrm-customize"
  docker run --rm -t \
    -v "$PROFILE_DIR:/data" \
    "$IMAGE" \
    osrm-customize "/data/${PBF_NAME}.osrm"
 
  echo "==> Build abgeschlossen"
}
 
start_container() {
  require_prepared_dataset
  remove_container_if_exists
 
  echo "==> Starte Container $PROFILE_CONTAINER_NAME auf Port $PORT"
  docker run -d \
    --restart unless-stopped \
    --name "$PROFILE_CONTAINER_NAME" \
    -p "$PORT:5000" \
    -v "$PROFILE_DIR:/data" \
    "$IMAGE" \
    osrm-routed --algorithm mld "/data/${PBF_NAME}.osrm" >/dev/null
 
  echo
  echo "Läuft."
  echo "Test-URL:"
  echo "  http://localhost:${PORT}/route/v1/${SERVICE_MODE}/16.3725,48.2082;16.3738,48.2100?overview=false"
}
 
stop_container() {
  local found=0
  for p in car bike foot; do
    local name="${CONTAINER_NAME}-${p}"
    if docker ps -a --format '{{.Names}}' | grep -qx "$name"; then
      echo "==> Stoppe und entferne $name"
      docker rm -f "$name" >/dev/null 2>&1 || true
      found=1
    fi
  done
 
  if [[ "$found" -eq 0 ]]; then
    echo "Kein passender Container gefunden."
  fi
}
 
show_status() {
  echo "==> Docker"
  systemctl is-active docker || true
  echo
 
  echo "==> Container"
  docker ps -a --filter "name=^${CONTAINER_NAME}-" --format 'table {{.Names}}\t{{.Status}}\t{{.Ports}}'
 
  echo
  echo "==> Datensätze"
  for p in car bike foot; do
    local dir="${WORKDIR}/${p}"
    if [[ -d "$dir" ]]; then
      echo "--- $dir"
      ls -1 "$dir" | sed 's/^/  /' || true
    fi
  done
}
 
show_logs() {
  local found=0
  for p in car bike foot; do
    local name="${CONTAINER_NAME}-${p}"
    if docker ps -a --format '{{.Names}}' | grep -qx "$name"; then
      found=1
      echo "==> Logs: $name"
      docker logs --tail 100 "$name"
      echo
    fi
  done
 
  if [[ "$found" -eq 0 ]]; then
    echo "Kein passender Container gefunden."
  fi
}
 
print_url() {
  check_profile
  echo "http://localhost:${PORT}/route/v1/${SERVICE_MODE}/16.3725,48.2082;16.3738,48.2100?overview=false"
}
 
main() {
  need_root
 
  case "$COMMAND" in
    build)
      check_profile
      install_docker_if_missing
      start_docker_if_needed
      prepare_paths
      build_dataset
      ;;
    start)
      check_profile
      install_docker_if_missing
      start_docker_if_needed
      prepare_paths
      start_container
      ;;
    restart)
      check_profile
      install_docker_if_missing
      start_docker_if_needed
      prepare_paths
      remove_container_if_exists
      start_container
      ;;
    stop)
      install_docker_if_missing
      start_docker_if_needed
      stop_container
      ;;
    status)
      install_docker_if_missing
      start_docker_if_needed
      show_status
      ;;
    logs)
      install_docker_if_missing
      start_docker_if_needed
      show_logs
      ;;
    url)
      print_url
      ;;
    *)
      usage
      exit 1
      ;;
  esac
}
 
main "$@"
sudo bash setup-osrm.sh build /home/austria-latest.osm.pbf foot
sudo bash setup-osrm.sh start /home/austria-latest.osm.pbf foot
sudo bash setup-osrm.sh status
sudo bash osrm-manager.sh stop
sudo bash osrm-manager.sh logs

Ubuntu

Walking

Nur foot, auf WAF getestet)

#!/usr/bin/env bash
set -Eeuo pipefail
 
OSRM_DIR="/opt/osrm-austria"
DATA_DIR="${OSRM_DIR}/data"
SERVICE_FILE="/etc/systemd/system/osrm-austria.service"
PBF_URL="https://download.geofabrik.de/europe/austria-latest.osm.pbf"
PBF_FILE="${DATA_DIR}/austria-latest.osm.pbf"
CONTAINER_IMAGE="ghcr.io/project-osrm/osrm-backend:latest"
CONTAINER_NAME="osrm-austria"
PORT="5000"
DATASET="austria-latest"
ROUTED_PATH="/data/${DATASET}"
 
log() {
    echo "[+] $*"
}
 
fail() {
    echo "[!] FEHLER: $*" >&2
    exit 1
}
 
require_root() {
    [[ "${EUID}" -eq 0 ]] || fail "Bitte als root ausführen."
}
 
on_error() {
    local exit_code=$?
    echo
    echo "[!] Das Skript ist fehlgeschlagen. Exit-Code: ${exit_code}"
    echo "[!] Diagnose:"
    echo "    free -h"
    echo "    df -h"
    echo "    ls -lh ${DATA_DIR}"
    echo "    journalctl -u osrm-austria.service -n 100 --no-pager"
    exit "${exit_code}"
}
trap on_error ERR
 
install_packages() {
    log "Installiere Basis-Pakete ..."
    apt-get update
    apt-get install -y \
        ca-certificates \
        curl \
        gnupg \
        lsb-release \
        wget \
        systemd
}
 
install_docker() {
    if command -v docker >/dev/null 2>&1; then
        log "Docker ist bereits installiert."
        systemctl enable docker >/dev/null 2>&1 || true
        systemctl start docker
        return
    fi
 
    log "Installiere Docker ..."
    install -m 0755 -d /etc/apt/keyrings
    curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
    chmod a+r /etc/apt/keyrings/docker.asc
 
    cat > /etc/apt/sources.list.d/docker.list <<EOF
deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu $(. /etc/os-release && echo "$VERSION_CODENAME") stable
EOF
 
    apt-get update
    apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
 
    systemctl enable docker
    systemctl start docker
}
 
prepare_dirs() {
    log "Erzeuge Verzeichnisse ..."
    mkdir -p "${DATA_DIR}"
}
 
download_data() {
    if [[ -s "${PBF_FILE}" ]]; then
        log "PBF-Datei existiert bereits: ${PBF_FILE}"
        return
    fi
 
    log "Lade Österreich-OSM-Daten herunter ..."
    wget -O "${PBF_FILE}" "${PBF_URL}"
    [[ -s "${PBF_FILE}" ]] || fail "Download fehlgeschlagen: ${PBF_FILE} fehlt oder ist leer."
}
 
pull_image() {
    log "Lade OSRM-Container-Image ..."
    docker pull "${CONTAINER_IMAGE}"
}
 
cleanup_old_artifacts() {
    log "Entferne alte OSRM-Artefakte ..."
 
    rm -f \
        "${DATA_DIR}/${DATASET}.osrm"* \
        "${DATA_DIR}/${DATASET}.osrm."* \
        "${DATA_DIR}/${DATASET}.osrm"
 
    [[ -s "${PBF_FILE}" ]] || fail "PBF-Datei fehlt nach Cleanup: ${PBF_FILE}"
}
 
run_extract() {
    log "Starte osrm-extract (walking, 1 Thread) ..."
    OMP_NUM_THREADS=1 docker run --rm -t \
        -e OMP_NUM_THREADS=1 \
        -v "${DATA_DIR}:/data" \
        "${CONTAINER_IMAGE}" \
        osrm-extract \
        -p /opt/foot.lua \
        "${ROUTED_PATH}.osm.pbf"
 
    [[ -f "${DATA_DIR}/${DATASET}.osrm.ebg" ]] || fail "osrm-extract unvollständig: ${DATASET}.osrm.ebg fehlt."
    [[ -f "${DATA_DIR}/${DATASET}.osrm.properties" ]] || fail "osrm-extract unvollständig: ${DATASET}.osrm.properties fehlt."
    [[ -f "${DATA_DIR}/${DATASET}.osrm.timestamp" ]] || fail "osrm-extract unvollständig: ${DATASET}.osrm.timestamp fehlt."
}
 
run_partition() {
    log "Starte osrm-partition ..."
    docker run --rm -t \
        -v "${DATA_DIR}:/data" \
        "${CONTAINER_IMAGE}" \
        osrm-partition \
        "${ROUTED_PATH}"
 
    [[ -f "${DATA_DIR}/${DATASET}.osrm.partition" ]] || fail "osrm-partition unvollständig: ${DATASET}.osrm.partition fehlt."
}
 
run_customize() {
    log "Starte osrm-customize ..."
    docker run --rm -t \
        -v "${DATA_DIR}:/data" \
        "${CONTAINER_IMAGE}" \
        osrm-customize \
        "${ROUTED_PATH}"
 
    [[ -f "${DATA_DIR}/${DATASET}.osrm.cells" ]] || fail "osrm-customize unvollständig: ${DATASET}.osrm.cells fehlt."
    [[ -f "${DATA_DIR}/${DATASET}.osrm.mldgr" ]] || fail "osrm-customize unvollständig: ${DATASET}.osrm.mldgr fehlt."
}
 
create_service() {
    log "Erzeuge systemd-Service ..."
 
    cat > "${SERVICE_FILE}" <<EOF
[Unit]
Description=OSRM Austria Walking Server
After=docker.service network-online.target
Requires=docker.service
Wants=network-online.target
 
[Service]
Type=simple
Restart=always
RestartSec=5
TimeoutStartSec=0
ExecStartPre=-/usr/bin/docker rm -f ${CONTAINER_NAME}
ExecStart=/usr/bin/docker run --rm --name ${CONTAINER_NAME} -p ${PORT}:5000 -v ${DATA_DIR}:/data ${CONTAINER_IMAGE} osrm-routed --algorithm mld ${ROUTED_PATH}
ExecStop=/usr/bin/docker stop ${CONTAINER_NAME}
 
[Install]
WantedBy=multi-user.target
EOF
 
    systemctl daemon-reload
    systemctl enable osrm-austria.service
}
 
start_service() {
    log "Starte OSRM-Service ..."
    systemctl restart osrm-austria.service
    sleep 3
    systemctl --no-pager --full status osrm-austria.service
}
 
show_result() {
    echo
    log "Fertig."
    echo
    echo "Test:"
    echo "curl 'http://127.0.0.1:${PORT}/route/v1/walking/16.3725,48.2082;16.3738,48.2100?overview=false'"
    echo
    echo "Status:"
    echo "systemctl status osrm-austria.service --no-pager -l"
    echo
    echo "Logs:"
    echo "journalctl -u osrm-austria.service -f"
}
 
main() {
    require_root
    install_packages
    install_docker
    prepare_dirs
    download_data
    pull_image
    cleanup_old_artifacts
    run_extract
    run_partition
    run_customize
    create_service
    start_service
    show_result
}
 
main "$@"

Walking, Bycicle, Driving

#!/usr/bin/env bash
set -Eeuo pipefail
 
OSRM_BASE="/opt/osrm-austria"
SERVICE_DIR="/etc/systemd/system"
PBF_URL="https://download.geofabrik.de/europe/austria-latest.osm.pbf"
CONTAINER_IMAGE="ghcr.io/project-osrm/osrm-backend:latest"
 
declare -A PROFILE_LUA=(
  [driving]="/opt/car.lua"
  [bicycle]="/opt/bicycle.lua"
  [foot]="/opt/foot.lua"
)
 
declare -A PROFILE_PORT=(
  [driving]="5000"
  [bicycle]="5001"
  [foot]="5002"
)
 
log() { echo "[+] $*"; }
fail() { echo "[!] FEHLER: $*" >&2; exit 1; }
 
require_root() {
  [[ "${EUID}" -eq 0 ]] || fail "Bitte als root ausführen."
}
 
install_packages() {
  apt-get update
  apt-get install -y ca-certificates curl gnupg lsb-release wget systemd
}
 
install_docker() {
  if command -v docker >/dev/null 2>&1; then
    systemctl enable docker >/dev/null 2>&1 || true
    systemctl start docker
    return
  fi
 
  install -m 0755 -d /etc/apt/keyrings
  curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
  chmod a+r /etc/apt/keyrings/docker.asc
 
  cat > /etc/apt/sources.list.d/docker.list <<EOF
deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu $(. /etc/os-release && echo "$VERSION_CODENAME") stable
EOF
 
  apt-get update
  apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
  systemctl enable docker
  systemctl start docker
}
 
prepare_profile_dir() {
  local profile="$1"
  mkdir -p "${OSRM_BASE}/${profile}"
}
 
download_pbf() {
  local profile="$1"
  local pbf_file="${OSRM_BASE}/${profile}/austria-latest.osm.pbf"
 
  if [[ ! -s "${pbf_file}" ]]; then
    wget -O "${pbf_file}" "${PBF_URL}"
  fi
 
  [[ -s "${pbf_file}" ]] || fail "PBF fehlt für ${profile}"
}
 
build_profile() {
  local profile="$1"
  local lua="${PROFILE_LUA[$profile]}"
  local dir="${OSRM_BASE}/${profile}"
 
  log "Baue Profil ${profile} ..."
 
  rm -f "${dir}/austria-latest.osrm"*
 
  docker run --rm -t \
    -e OMP_NUM_THREADS=1 \
    -v "${dir}:/data" \
    "${CONTAINER_IMAGE}" \
    osrm-extract -p "${lua}" /data/austria-latest.osm.pbf
 
  docker run --rm -t \
    -v "${dir}:/data" \
    "${CONTAINER_IMAGE}" \
    osrm-partition /data/austria-latest.osrm
 
  docker run --rm -t \
    -v "${dir}:/data" \
    "${CONTAINER_IMAGE}" \
    osrm-customize /data/austria-latest.osrm
 
  [[ -f "${dir}/austria-latest.osrm.cells" ]] || fail "Build fehlgeschlagen für ${profile}"
}
 
create_service() {
  local profile="$1"
  local port="${PROFILE_PORT[$profile]}"
  local dir="${OSRM_BASE}/${profile}"
  local service="${SERVICE_DIR}/osrm-austria-${profile}.service"
  local container="osrm-austria-${profile}"
 
  cat > "${service}" <<EOF
[Unit]
Description=OSRM Austria ${profile} Server
After=docker.service network-online.target
Requires=docker.service
Wants=network-online.target
 
[Service]
Type=simple
Restart=always
RestartSec=5
TimeoutStartSec=0
ExecStartPre=-/usr/bin/docker rm -f ${container}
ExecStart=/usr/bin/docker run --rm --name ${container} -p ${port}:5000 -v ${dir}:/data ${CONTAINER_IMAGE} osrm-routed --algorithm mld /data/austria-latest.osrm
ExecStop=/usr/bin/docker stop ${container}
 
[Install]
WantedBy=multi-user.target
EOF
}
 
start_service() {
  local profile="$1"
  systemctl daemon-reload
  systemctl enable "osrm-austria-${profile}.service"
  systemctl restart "osrm-austria-${profile}.service"
}
 
main() {
  require_root
  install_packages
  install_docker
  docker pull "${CONTAINER_IMAGE}"
 
  for profile in driving bicycle foot; do
    prepare_profile_dir "${profile}"
    download_pbf "${profile}"
    build_profile "${profile}"
    create_service "${profile}"
    start_service "${profile}"
  done
 
  echo
  echo "Fertig."
  echo "driving: http://127.0.0.1:5000"
  echo "bicycle: http://127.0.0.1:5001"
  echo "foot:    http://127.0.0.1:5002"
}
main "$@"