final
This commit is contained in:
126
engine-wrapper/main.py
Normal file
126
engine-wrapper/main.py
Normal file
@@ -0,0 +1,126 @@
|
||||
from fastapi import FastAPI, HTTPException
|
||||
from pydantic import BaseModel
|
||||
from typing import List, Optional
|
||||
import httpx
|
||||
import os
|
||||
from dotenv import load_dotenv
|
||||
|
||||
# Загрузка переменных окружения из .env файла
|
||||
load_dotenv()
|
||||
|
||||
app = FastAPI(title="OSRM API Wrapper")
|
||||
|
||||
# --- Модели для запросов и ответов ---
|
||||
|
||||
# Входная модель для обоих эндпоинтов: массив точек
|
||||
class PointsRequest(BaseModel):
|
||||
points: List[List[float]] # Формат: [[lat, lon], [lat, lon], ...]
|
||||
|
||||
# Модель ответа для /table
|
||||
class TableResponse(BaseModel):
|
||||
distances: List[List[float]]
|
||||
durations: List[List[float]]
|
||||
|
||||
# Модель для одного шага в маршруте (для /route)
|
||||
class RouteStep(BaseModel):
|
||||
location: List[float] # [lon, lat]
|
||||
distance: float
|
||||
type: str
|
||||
modifier: Optional[str] = ""
|
||||
name: str
|
||||
|
||||
# Модель ответа для /route
|
||||
class RouteResponse(BaseModel):
|
||||
steps: List[RouteStep]
|
||||
|
||||
# --- Конфигурация ---
|
||||
|
||||
# Чтение URL OSRM из переменной окружения
|
||||
OSRM_BASE_URL = os.getenv("OSRM_URL")
|
||||
|
||||
if not OSRM_BASE_URL:
|
||||
raise RuntimeError("Переменная окружения OSRM_URL не установлена.")
|
||||
|
||||
# --- Эндпоинты ---
|
||||
|
||||
@app.post("/table", response_model=TableResponse)
|
||||
async def table(request: PointsRequest):
|
||||
"""
|
||||
Принимает список координат и возвращает матрицы расстояний и времени в пути между ними.
|
||||
"""
|
||||
if len(request.points) < 2:
|
||||
raise HTTPException(status_code=400, detail="Нужно минимум 2 точки")
|
||||
|
||||
# OSRM ожидает формат lon,lat
|
||||
coords = ";".join(f"{lon},{lat}" for lat, lon in request.points)
|
||||
|
||||
# Эндпоинт для Table service
|
||||
url = f"{OSRM_BASE_URL}/table/v1/foot/{coords}?annotations=duration,distance"
|
||||
print(f"Запрос к OSRM: {url}")
|
||||
|
||||
async with httpx.AsyncClient() as client:
|
||||
try:
|
||||
resp = await client.get(url, timeout=10)
|
||||
resp.raise_for_status()
|
||||
except httpx.RequestError as exc:
|
||||
raise HTTPException(status_code=500, detail=f"Ошибка запроса к OSRM: {exc.request.url!r} - {exc}")
|
||||
except httpx.HTTPStatusError as exc:
|
||||
raise HTTPException(status_code=500, detail=f"OSRM вернул ошибку {exc.response.status_code}: {exc.response.text}")
|
||||
|
||||
data = resp.json()
|
||||
|
||||
distances = data.get("distances")
|
||||
durations = data.get("durations")
|
||||
|
||||
if distances is None or durations is None:
|
||||
raise HTTPException(status_code=500, detail="OSRM вернул некорректный ответ")
|
||||
|
||||
return TableResponse(distances=distances, durations=durations)
|
||||
|
||||
|
||||
@app.post("/route", response_model=RouteResponse)
|
||||
async def route(request: PointsRequest):
|
||||
"""
|
||||
Принимает две точки (начальную и конечную) и возвращает пошаговый маршрут.
|
||||
"""
|
||||
if len(request.points) != 2:
|
||||
raise HTTPException(status_code=400, detail="Для маршрута нужно ровно 2 точки")
|
||||
|
||||
coords = ";".join(f"{lon},{lat}" for lat, lon in request.points)
|
||||
|
||||
# Эндпоинт для Route service с параметром steps=true
|
||||
url = f"{OSRM_BASE_URL}/route/v1/foot/{coords}?steps=true"
|
||||
print(f"Запрос к OSRM: {url}")
|
||||
|
||||
async with httpx.AsyncClient() as client:
|
||||
try:
|
||||
resp = await client.get(url, timeout=10)
|
||||
resp.raise_for_status()
|
||||
except httpx.RequestError as exc:
|
||||
raise HTTPException(status_code=500, detail=f"Ошибка запроса к OSRM: {exc.request.url!r} - {exc}")
|
||||
except httpx.HTTPStatusError as exc:
|
||||
raise HTTPException(status_code=500, detail=f"OSRM вернул ошибку {exc.response.status_code}: {exc.response.text}")
|
||||
|
||||
data = resp.json()
|
||||
|
||||
if "routes" not in data or len(data["routes"]) == 0:
|
||||
raise HTTPException(status_code=500, detail="OSRM не вернул маршруты")
|
||||
|
||||
# Извлекаем шаги из ответа
|
||||
raw_steps = data["routes"][0]["legs"][0]["steps"]
|
||||
|
||||
formatted_steps = []
|
||||
for step in raw_steps:
|
||||
maneuver = step["maneuver"]
|
||||
formatted_steps.append(
|
||||
RouteStep(
|
||||
location=[maneuver["location"][1], maneuver["location"][0]],
|
||||
distance=step["distance"],
|
||||
type=maneuver["type"],
|
||||
modifier=maneuver.get("modifier"),
|
||||
name=step.get("name", "")
|
||||
)
|
||||
)
|
||||
|
||||
return RouteResponse(steps=formatted_steps)
|
||||
|
||||
Reference in New Issue
Block a user