162 lines
8.9 KiB
Bash
Executable file
162 lines
8.9 KiB
Bash
Executable file
#!/usr/bin/env bash
|
||
# Build / refresh OSRM routing graph для site-finder (#39).
|
||
#
|
||
# Готовит .osrm-файлы (MLD pipeline) из OpenStreetMap-экстракта Свердловской
|
||
# области и кладёт их в ./data/osrm/, откуда их монтирует docker-compose.prod.yml
|
||
# (service `osrm`, volume ./data/osrm:/data → osrm-routed читает /data/<region>.osrm).
|
||
#
|
||
# === Где запускать ===
|
||
# На VPS из /opt/gendesign: bash scripts/build_osrm.sh
|
||
# (требует docker + ~5GB свободного диска + ~3GB свободной RAM на время extract).
|
||
#
|
||
# === ВАЖНО: это offline-задача, НЕ часть deploy.yml ===
|
||
# Download ~600MB-1.5GB + build 30-50 мин. Запускается ВРУЧНУЮ оркестратором,
|
||
# НЕ на каждом push. deploy.yml только поднимает уже готовый osrm-контейнер.
|
||
#
|
||
# === Idempotent / re-runnable ===
|
||
# Повторный запуск перекачивает свежий pbf и пересобирает граф (overwrite).
|
||
# Безопасно гонять ежемесячно (osm.pbf на Geofabrik обновляется ежедневно).
|
||
# # monthly refresh: cron на VPS, напр. 0 4 1 * * bash /opt/gendesign/scripts/build_osrm.sh >> /var/log/osrm-build.log 2>&1
|
||
# После refresh: docker compose -p gendesign -f docker-compose.prod.yml up -d --force-recreate --no-deps osrm
|
||
#
|
||
# === Регион / источник pbf ===
|
||
# Geofabrik НЕ публикует Свердловскую область отдельным extract'ом — только
|
||
# «Уральский ФО» (ural-fed-district). Два режима (CLIP=1 по умолчанию):
|
||
#
|
||
# CLIP=1 (default, РЕКОМЕНДОВАНО): качаем Ural-FO pbf и обрезаем по bbox
|
||
# Свердл. обл. через `osmium extract`. Результат легче → меньше RAM/диск
|
||
# в build и в runtime (osrm-routed warm ~0.8-1.2GB вместо ~1.5GB+ на
|
||
# весь Ural-FO). Минус: нужен extra-шаг osmium (запускаем в docker).
|
||
#
|
||
# CLIP=0: используем весь Ural-FO pbf как есть (zero-clip, проще, но тяжелее —
|
||
# граф Курган/Тюмень/ХМАО/ЯНАО грузится в RAM без пользы для Свердл).
|
||
# Бери, если RAM не жмёт и нужен запас на соседние регионы.
|
||
#
|
||
# bbox Свердловской обл (грубый, с запасом): lon 57.2..66.2, lat 56.0..62.0.
|
||
# Можно сузить до ЕКБ-агломерации если RAM критична (lon 59.0..62.5, lat 56.2..58.2)
|
||
# через EKB_TIGHT=1.
|
||
#
|
||
# === Профили ===
|
||
# Car (driving) — основной, строится всегда → <region>.osrm*.
|
||
# Foot (walking) — опциональный Phase-2, строится при WALK=1 → <region>-foot.osrm*.
|
||
# Для walking нужен второй compose-service (osrm-walk) — см. docs/osrm-routing.md.
|
||
# Этот PR car-first; walk выключен по умолчанию (issue хочет оба eventually).
|
||
#
|
||
# === Проверка после build ===
|
||
# docker compose -p gendesign -f docker-compose.prod.yml up -d osrm
|
||
# docker compose -p gendesign -f docker-compose.prod.yml exec osrm \
|
||
# sh -c 'echo OK' # контейнер жив
|
||
# # route-probe С ХОСТА (внутри образа нет curl) — временно прокинуть порт:
|
||
# docker run --rm --network gendesign_default curlimages/curl:8 -fsS \
|
||
# 'http://osrm:5000/route/v1/driving/60.6,56.84;60.65,56.86'
|
||
# ↑ ожидаем JSON {"code":"Ok","routes":[...]}.
|
||
|
||
set -euo pipefail
|
||
|
||
# --- Config (override через env) -------------------------------------------
|
||
OSRM_IMAGE="${OSRM_IMAGE:-osrm/osrm-backend:latest}"
|
||
OSMIUM_IMAGE="${OSMIUM_IMAGE:-stefda/osmium-tool:latest}"
|
||
REGION="${OSRM_REGION:-sverdlovsk}" # имя выходных .osrm* (= OSRM_REGION в compose)
|
||
PBF_URL="${PBF_URL:-https://download.geofabrik.de/russia/ural-fed-district-latest.osm.pbf}"
|
||
CLIP="${CLIP:-1}" # 1 = обрезать по bbox Свердл, 0 = весь Ural-FO
|
||
EKB_TIGHT="${EKB_TIGHT:-0}" # 1 = узкий bbox только ЕКБ-агломерация
|
||
WALK="${WALK:-0}" # 1 = ещё и пеший профиль <region>-foot.osrm*
|
||
CAR_PROFILE="${CAR_PROFILE:-/opt/car.lua}" # встроенные профили в osrm-backend образе
|
||
FOOT_PROFILE="${FOOT_PROFILE:-/opt/foot.lua}"
|
||
|
||
# bbox: left,bottom,right,top (lon_min,lat_min,lon_max,lat_max)
|
||
if [ "$EKB_TIGHT" = "1" ]; then
|
||
BBOX="${BBOX:-59.0,56.2,62.5,58.2}" # ЕКБ-агломерация (минимум RAM)
|
||
else
|
||
BBOX="${BBOX:-57.2,56.0,66.2,62.0}" # вся Свердловская обл (с запасом)
|
||
fi
|
||
|
||
# Каталог host (= ./data/osrm в compose). Скрипт зовём из /opt/gendesign.
|
||
DATA_DIR="${DATA_DIR:-$(pwd)/data/osrm}"
|
||
mkdir -p "$DATA_DIR"
|
||
|
||
RAW_PBF="$DATA_DIR/${REGION}-raw.osm.pbf" # сырой Ural-FO (или clip-источник)
|
||
WORK_PBF="$DATA_DIR/${REGION}.osm.pbf" # то, что реально пойдёт в osrm-extract
|
||
|
||
echo "=== OSRM build ==="
|
||
echo " region : $REGION"
|
||
echo " pbf source : $PBF_URL"
|
||
echo " clip to bbox : CLIP=$CLIP BBOX=$BBOX (EKB_TIGHT=$EKB_TIGHT)"
|
||
echo " walking profile : WALK=$WALK"
|
||
echo " data dir (host) : $DATA_DIR"
|
||
echo ""
|
||
|
||
# --- 1. Download ------------------------------------------------------------
|
||
# -N: качаем только если на сервере новее (idempotent monthly refresh).
|
||
echo "=== 1. Download pbf (Ural-FO ≈600MB) ==="
|
||
if command -v wget >/dev/null 2>&1; then
|
||
wget -N -O "$RAW_PBF" "$PBF_URL"
|
||
else
|
||
curl -fSL --time-cond "$RAW_PBF" -o "$RAW_PBF" "$PBF_URL"
|
||
fi
|
||
|
||
# --- 2. Clip (optional) -----------------------------------------------------
|
||
if [ "$CLIP" = "1" ]; then
|
||
echo "=== 2. Clip to Свердл bbox через osmium ($BBOX) ==="
|
||
docker run --rm -v "$DATA_DIR:/data" "$OSMIUM_IMAGE" \
|
||
osmium extract -b "$BBOX" "/data/$(basename "$RAW_PBF")" \
|
||
-o "/data/$(basename "$WORK_PBF")" --overwrite
|
||
else
|
||
echo "=== 2. No clip (CLIP=0) — используем Ural-FO целиком ==="
|
||
cp -f "$RAW_PBF" "$WORK_PBF"
|
||
fi
|
||
|
||
# --- helper: запуск osrm-backend образа на /data --------------------------
|
||
osrm() {
|
||
docker run --rm -v "$DATA_DIR:/data" "$OSRM_IMAGE" "$@"
|
||
}
|
||
|
||
# --- 3. MLD pipeline (car) --------------------------------------------------
|
||
# MLD = extract → partition → customize. НЕ osrm-contract (то CH-алгоритм,
|
||
# несовместим с --algorithm mld в compose).
|
||
#
|
||
# osrm-extract пишет <pbf-без-.osm.pbf>.osrm. Имя входного pbf специально равно
|
||
# целевому базовому имени (<region>.osm.pbf → <region>.osrm; <region>-foot.osm.pbf
|
||
# → <region>-foot.osrm), поэтому partition/customize работают по тому же базовому
|
||
# имени и переименование не требуется.
|
||
build_profile() {
|
||
local profile="$1" src_pbf="$2"
|
||
local base extracted
|
||
base="$(basename "${src_pbf%.osm.pbf}")"
|
||
extracted="/data/${base}.osrm"
|
||
echo "--- extract ($profile → ${base}.osrm) ---"
|
||
osrm osrm-extract -p "$profile" "/data/$(basename "$src_pbf")"
|
||
echo "--- partition (${base}.osrm) ---"
|
||
osrm osrm-partition "$extracted"
|
||
echo "--- customize (${base}.osrm) ---"
|
||
osrm osrm-customize "$extracted"
|
||
}
|
||
|
||
echo "=== 3. Build car profile → ${REGION}.osrm* ==="
|
||
build_profile "$CAR_PROFILE" "$WORK_PBF"
|
||
|
||
# --- 4. MLD pipeline (foot, optional) --------------------------------------
|
||
if [ "$WALK" = "1" ]; then
|
||
echo "=== 4. Build foot profile → ${REGION}-foot.osrm* ==="
|
||
# Отдельная копия pbf под пеший граф (osrm-extract overwrite'ит базу).
|
||
WALK_PBF="$DATA_DIR/${REGION}-foot.osm.pbf"
|
||
cp -f "$WORK_PBF" "$WALK_PBF"
|
||
build_profile "$FOOT_PROFILE" "$WALK_PBF"
|
||
else
|
||
echo "=== 4. Skip foot profile (WALK=0) — car-first, см. docs/osrm-routing.md ==="
|
||
fi
|
||
|
||
# --- 5. Cleanup heavy intermediates ----------------------------------------
|
||
echo "=== 5. Cleanup промежуточных .pbf (граф .osrm* уже собран) ==="
|
||
rm -f "$RAW_PBF" "$WORK_PBF" "${DATA_DIR}/${REGION}-foot.osm.pbf" 2>/dev/null || true
|
||
|
||
echo ""
|
||
echo "=== DONE ==="
|
||
echo "Артефакты в $DATA_DIR:"
|
||
ls -lh "$DATA_DIR"/*.osrm* 2>/dev/null | awk '{print " "$5"\t"$9}' || true
|
||
echo ""
|
||
echo "Дальше:"
|
||
echo " docker compose -p gendesign -f docker-compose.prod.yml up -d osrm"
|
||
echo " # verify (curl с хоста — внутри образа curl нет):"
|
||
echo " docker run --rm --network gendesign_default curlimages/curl:8 -fsS \\"
|
||
echo " 'http://osrm:5000/route/v1/driving/60.6,56.84;60.65,56.86'"
|