diff --git a/VERSION b/VERSION index e90170d..1c49fec 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1242 \ No newline at end of file +1243 \ No newline at end of file diff --git a/backend/main.py b/backend/main.py index 5cbb96b..b473156 100644 --- a/backend/main.py +++ b/backend/main.py @@ -371,6 +371,7 @@ app.mount("/css", StaticFiles(directory=f"{STATIC_DIR}/css"), name="css") app.mount("/js", StaticFiles(directory=f"{STATIC_DIR}/js"), name="js") app.mount("/icons", StaticFiles(directory=f"{STATIC_DIR}/icons"), name="icons") app.mount("/img", StaticFiles(directory=f"{STATIC_DIR}/img"), name="img") +app.mount("/sounds", StaticFiles(directory=f"{STATIC_DIR}/sounds"), name="sounds") # Yaro-Navi-Sounds # Selbst-gehostete Vektor-Tiles (.pmtiles) — liegen im data-Volume, NICHT im Image. # WICHTIG: Starlettes StaticFiles/FileResponse liefert hinter unserer BaseHTTPMiddleware diff --git a/backend/routes/routen.py b/backend/routes/routen.py index db57d55..ee3bac7 100644 --- a/backend/routes/routen.py +++ b/backend/routes/routen.py @@ -73,6 +73,31 @@ def _simplify_track(track: list, max_pts: int = 40) -> list: return [track[round(i * step)] for i in range(max_pts)] +def _decode_polyline3d(encoded: str) -> list: + """ORS-Polyline MIT Elevation: Tripel (lat, lon, ele), Präzision 1e5/1e5/1e2. + Das polyline-Paket kann nur 2D — es würde die Höhen-Deltas als nächste + Koordinate fehlinterpretieren und Müll liefern.""" + coords, idx, lat, lon, ele = [], 0, 0, 0, 0 + n = len(encoded) + while idx < n: + deltas = [] + for _ in range(3): + result, shift = 0, 0 + while True: + b = ord(encoded[idx]) - 63 + idx += 1 + result |= (b & 0x1F) << shift + shift += 5 + if b < 0x20: + break + deltas.append(~(result >> 1) if result & 1 else result >> 1) + lat += deltas[0] + lon += deltas[1] + ele += deltas[2] + coords.append((lat / 1e5, lon / 1e5, ele / 1e2)) + return coords + + def _parse(row) -> dict: d = dict(row) if isinstance(d.get('gps_track'), str): @@ -249,6 +274,7 @@ async def suggest_route(data: SuggestRequest, user=Depends(get_current_user)): }, "units": "m", "geometry": True, + "elevation": True, # Anstieg/Abstieg für ehrliche Schwierigkeit (René 2026-06-06) "instructions": False, } @@ -282,16 +308,20 @@ async def suggest_route(data: SuggestRequest, user=Depends(get_current_user)): 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] + # encoded polyline → Track. ACHTUNG: mit elevation=True codiert ORS 3D + # (lat, lon, ele) — der Standard-2D-Decoder würde Müll liefern. + points = _decode_polyline3d(geometry) + gps_track = [{"lat": p[0], "lon": p[1], "alt": round(p[2])} for p in points] distanz_km = round(distanz_m / 1000, 2) dauer_min = max(1, round(dauer_s / 60)) + ascent_m = round(summary.get("ascent", 0)) - if distanz_km < 3: + # Schwierigkeit aus Distanz UND Höhenmetern (René 2026-06-06) — vorher nur km: + # eine flache 6-km-Runde ist nicht „anspruchsvoll", 3 km steil bergauf nicht „leicht". + if distanz_km < 4 and ascent_m < 50: schwierigkeit = "leicht" - elif distanz_km <= 5: + elif distanz_km <= 7 and ascent_m < 150: schwierigkeit = "mittel" else: schwierigkeit = "anspruchsvoll" @@ -319,6 +349,7 @@ async def suggest_route(data: SuggestRequest, user=Depends(get_current_user)): "gps_track": gps_track, "distanz_km": distanz_km, "dauer_min": dauer_min, + "hoehenmeter": ascent_m, "schwierigkeit": schwierigkeit, "weekly_remaining": weekly_remaining, } diff --git a/backend/static/index.html b/backend/static/index.html index 88c1392..bc12e79 100644 --- a/backend/static/index.html +++ b/backend/static/index.html @@ -86,14 +86,14 @@ Ban Yaro - + - - - - - + + + + + @@ -612,11 +612,11 @@ - - - - - + + + + + @@ -626,7 +626,7 @@ - + diff --git a/backend/static/js/app.js b/backend/static/js/app.js index f3a7937..6f467eb 100644 --- a/backend/static/js/app.js +++ b/backend/static/js/app.js @@ -3,7 +3,7 @@ Router, State-Management, Navigation, Initialisierung. ============================================================ */ -const APP_VER = '1242'; // ← bei jedem Deploy mit Frontend-Änderungen erhöhen +const APP_VER = '1243'; // ← bei jedem Deploy mit Frontend-Änderungen erhöhen const APP_VERSION = '1.6.0'; // ← semantische Version, wird bei make release gesetzt window.APP_VER = APP_VER; // global verfügbar für andere Module (z.B. offline-indicator) window.APP_VERSION = APP_VERSION; diff --git a/backend/static/js/pages/routes.js b/backend/static/js/pages/routes.js index c458736..8a19671 100644 --- a/backend/static/js/pages/routes.js +++ b/backend/static/js/pages/routes.js @@ -102,13 +102,16 @@ window.Page_routes = (() => { function _barks(n, pitch, gap) { if (!enabled()) return; const sample = files && (pitch > 1.3 ? files.klaeffen : files.wuff); - if (sample) { // echte Aufnahme: n-mal hintereinander + if (sample) { // echte Aufnahme (Schäferhund, /sounds/*.mp3) + // klaeffen.mp3 ist bereits eine ~2,8-s-Bell-SEQUENZ → nur 1× abspielen; + // wuff.mp3 ist ein einzelner Beller → n-mal mit Pause. + const reps = sample === files.klaeffen ? 1 : n; let i = 0; const play = () => { - if (i++ >= n) return; + if (i++ >= reps) return; sample.currentTime = 0; sample.play().catch(() => {}); - setTimeout(play, gap * 1000 + 200); + setTimeout(play, gap * 1000 + 350); }; play(); return; @@ -672,6 +675,9 @@ window.Page_routes = (() => { ${UI.icon('timer')} ${UI.escape(durStr)} + ${result.hoehenmeter != null ? ` + ${UI.icon('trend-up')} ${result.hoehenmeter} hm + ` : ''} ${diffLabel ? ` - + Ban Yaro — Die Hunde-App für Deutschland, Österreich & Schweiz diff --git a/backend/static/sounds/klaeffen.mp3 b/backend/static/sounds/klaeffen.mp3 new file mode 100644 index 0000000..3fe5bed Binary files /dev/null and b/backend/static/sounds/klaeffen.mp3 differ diff --git a/backend/static/sounds/wuff.mp3 b/backend/static/sounds/wuff.mp3 new file mode 100644 index 0000000..648ceeb Binary files /dev/null and b/backend/static/sounds/wuff.mp3 differ diff --git a/backend/static/sw.js b/backend/static/sw.js index 31f22ab..f652d26 100644 --- a/backend/static/sw.js +++ b/backend/static/sw.js @@ -4,7 +4,7 @@ ============================================================ */ // ← EINZIGE Stelle für die Version — STATIC_ASSETS und CACHE_VERSION leiten sich ab -const VER = '1242'; +const VER = '1243'; const CACHE_VERSION = `by-v${VER}`; const CACHE_STATIC = `${CACHE_VERSION}-static`; const CACHE_TILES = 'ban-yaro-tiles-v1'; // bleibt über SW-Updates erhalten @@ -32,6 +32,9 @@ const PRIORITY_PAGES = [ '/js/map-offline.js', '/js/map-gl-markers.js', '/js/map-gl-mini.js', + // Yaro-Navi-Sounds — müssen auch im Funkloch bellen (zusammen ~40 KB) + '/sounds/wuff.mp3', + '/sounds/klaeffen.mp3', ]; // index.html wird NICHT pre-gecacht (immer Network-First)