Feature: Rundweg-Vorschläge via OpenRouteService — 2/4/6 km, 3 Varianten, Navigation+Speichern — SW by-v478, APP_VER 455
This commit is contained in:
parent
b09a569689
commit
369eae5e5a
5 changed files with 396 additions and 19 deletions
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
import json, math, os, uuid
|
||||
import httpx
|
||||
import polyline as _polyline
|
||||
from fastapi import APIRouter, Depends, HTTPException, UploadFile, File
|
||||
from pydantic import BaseModel
|
||||
from typing import Optional, List
|
||||
|
|
@ -14,6 +15,8 @@ from routes.push import send_push_to_user
|
|||
|
||||
router = APIRouter()
|
||||
|
||||
ORS_API_KEY = os.getenv("ORS_API_KEY")
|
||||
|
||||
_MAX_AVG_KMH = 15.0 # Über diesem Wert wird die Route nicht für Stats/Trophäen gewertet
|
||||
|
||||
def _check_speed(distanz_km, dauer_min) -> bool:
|
||||
|
|
@ -172,6 +175,91 @@ async def create_route(data: RouteCreate, user=Depends(get_current_user)):
|
|||
return result
|
||||
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# POST /api/routes/suggest — Rundweg-Vorschlag via OpenRouteService
|
||||
# ------------------------------------------------------------------
|
||||
class SuggestRequest(BaseModel):
|
||||
lat: float
|
||||
lon: float
|
||||
distance_km: float # Zieldistanz in km (z.B. 2.0, 4.0, 6.0)
|
||||
seed: int = 0 # 0-4: verschiedene Routenvarianten
|
||||
|
||||
@router.post("/suggest")
|
||||
async def suggest_route(data: SuggestRequest, user=Depends(get_current_user)):
|
||||
if not (0.5 <= data.distance_km <= 15):
|
||||
raise HTTPException(400, "distance_km muss zwischen 0.5 und 15 liegen.")
|
||||
|
||||
if not ORS_API_KEY:
|
||||
raise HTTPException(503, "ORS nicht konfiguriert")
|
||||
|
||||
payload = {
|
||||
"coordinates": [[data.lon, data.lat]],
|
||||
"options": {
|
||||
"round_trip": {
|
||||
"length": data.distance_km * 1000,
|
||||
"points": 5,
|
||||
"seed": data.seed,
|
||||
},
|
||||
"avoid_features": ["ferries", "steps"],
|
||||
},
|
||||
"units": "m",
|
||||
"geometry": True,
|
||||
"instructions": False,
|
||||
}
|
||||
|
||||
try:
|
||||
async with httpx.AsyncClient(timeout=10) as client:
|
||||
resp = await client.post(
|
||||
"https://api.openrouteservice.org/v2/directions/foot-walking",
|
||||
headers={
|
||||
"Authorization": f"Bearer {ORS_API_KEY}",
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
json=payload,
|
||||
)
|
||||
except httpx.TimeoutException:
|
||||
raise HTTPException(504, "ORS-Anfrage hat das Zeitlimit überschritten.")
|
||||
|
||||
if resp.status_code != 200:
|
||||
try:
|
||||
detail = resp.json()
|
||||
except Exception:
|
||||
detail = resp.text
|
||||
raise HTTPException(502, f"ORS-Fehler: {detail}")
|
||||
|
||||
body = resp.json()
|
||||
try:
|
||||
route = body["routes"][0]
|
||||
geometry = route["geometry"]
|
||||
summary = route["summary"]
|
||||
distanz_m = summary.get("distance", data.distance_km * 1000)
|
||||
dauer_s = summary.get("duration", 0)
|
||||
except (KeyError, IndexError) as exc:
|
||||
raise HTTPException(502, f"Unerwartete ORS-Antwort: {exc}")
|
||||
|
||||
# encoded polyline → [[lat, lon], ...]
|
||||
points = _polyline.decode(geometry)
|
||||
gps_track = [{"lat": p[0], "lon": p[1]} for p in points]
|
||||
|
||||
distanz_km = round(distanz_m / 1000, 2)
|
||||
dauer_min = max(1, round(dauer_s / 60))
|
||||
|
||||
if distanz_km < 3:
|
||||
schwierigkeit = "leicht"
|
||||
elif distanz_km <= 5:
|
||||
schwierigkeit = "mittel"
|
||||
else:
|
||||
schwierigkeit = "anspruchsvoll"
|
||||
|
||||
return {
|
||||
"name": f"Rundweg {distanz_km:.0f} km",
|
||||
"gps_track": gps_track,
|
||||
"distanz_km": distanz_km,
|
||||
"dauer_min": dauer_min,
|
||||
"schwierigkeit": schwierigkeit,
|
||||
}
|
||||
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# GET /api/routes/{id} — Route mit vollem GPS-Track
|
||||
# ------------------------------------------------------------------
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue