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
* nearest
* table
* match
* trip
* tile
====Route====
Der Dienst [[https://project-osrm.org/docs/v5.24.0/api/#route-service|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 [[https://project-osrm.org/docs/v5.24.0/api/#nearest-service|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 [[https://project-osrm.org/docs/v5.24.0/api/#table-service|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 [[https://project-osrm.org/docs/v5.24.0/api/#match-service|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 [[https://project-osrm.org/docs/v5.24.0/api/#trip-service|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 [[https://project-osrm.org/docs/v5.24.0/api/#tile-service|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=====
* Disk > 15GB
* RAM > 8GB
====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 </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====
* foot.lua
* bicycle.lua
* car.lua
===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 < "${SERVICE_FILE}" <
===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 < "${service}" <
=====Links=====
* [[https://project-osrm.org/|Homepage]]