Files
Nikidze e3f8caf59f final
2025-10-31 22:08:55 +03:00

127 lines
4.7 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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)