feat: DEM-based elevation recalculation via edit drawer button
Adds a "Recalculate from terrain map (DEM)" button to the activity edit
drawer. On click it queries an Open-Elevation-compatible API to replace
GPS altitude with SRTM terrain data, applies 5m hysteresis, and updates
the activity's elevation stats and timeseries chart in place.
- bincio/extract/dem.py: lookup_elevations() (batched HTTP POST) +
recalculate_elevation() (subsample → DEM → interpolate → hysteresis →
patch activity JSON, timeseries JSON, index.json)
- POST /api/activity/{id}/recalculate-elevation on both serve and edit
servers; serve endpoint is auth-gated and triggers merge + rebuild
- --dem-url flag (also DEM_URL env var) on bincio serve and bincio edit;
logged at startup; missing URL returns a clear 503 with setup instructions
- /api/me response gains dem_configured bool
- EditDrawer: button with loading state, shows new ↑/↓ values on success
This commit is contained in:
@@ -158,6 +158,7 @@ webroot: Path | None = None # nginx webroot — when set, trigger full rebuil
|
||||
strava_client_id: str = ""
|
||||
strava_client_secret: str = ""
|
||||
public_url: str = "" # e.g. "https://yourdomain.com" — used for OAuth redirect URIs
|
||||
dem_url: str = "" # Open-Elevation-compatible API base URL; empty = feature disabled
|
||||
_db = None # sqlite3.Connection, opened lazily
|
||||
|
||||
|
||||
@@ -404,6 +405,7 @@ async def me(bincio_session: Optional[str] = Cookie(default=None)) -> JSONRespon
|
||||
"display_name": user.display_name,
|
||||
"is_admin": user.is_admin,
|
||||
"store_originals_default": store_orig != "false",
|
||||
"dem_configured": bool(dem_url),
|
||||
})
|
||||
|
||||
|
||||
@@ -1265,6 +1267,39 @@ async def post_activity(
|
||||
return JSONResponse({"ok": True})
|
||||
|
||||
|
||||
@app.post("/api/activity/{activity_id}/recalculate-elevation")
|
||||
async def recalculate_elevation_endpoint(
|
||||
activity_id: str,
|
||||
bincio_session: Optional[str] = Cookie(default=None),
|
||||
) -> JSONResponse:
|
||||
"""Replace GPS altitude with DEM terrain elevation and recompute gain/loss.
|
||||
|
||||
Requires --dem-url to be set when starting bincio serve.
|
||||
"""
|
||||
user = _require_user(bincio_session)
|
||||
_check_id(activity_id)
|
||||
if not dem_url:
|
||||
raise HTTPException(
|
||||
503,
|
||||
"DEM URL not configured. "
|
||||
"Pass --dem-url <api-url> to bincio serve (e.g. https://api.open-elevation.com).",
|
||||
)
|
||||
dd = _get_data_dir() / user.handle
|
||||
if not (dd / "activities" / f"{activity_id}.json").exists():
|
||||
raise HTTPException(404, "Activity not found")
|
||||
try:
|
||||
from bincio.extract.dem import recalculate_elevation
|
||||
from bincio.render.merge import merge_one
|
||||
result = recalculate_elevation(dd, activity_id, dem_url)
|
||||
merge_one(dd, activity_id)
|
||||
_trigger_rebuild(user.handle)
|
||||
return JSONResponse(result)
|
||||
except FileNotFoundError as e:
|
||||
raise HTTPException(404, str(e))
|
||||
except ValueError as e:
|
||||
raise HTTPException(422, str(e))
|
||||
|
||||
|
||||
@app.delete("/api/activity/{activity_id}", response_model=GenericResponse)
|
||||
async def delete_activity(
|
||||
activity_id: str,
|
||||
|
||||
Reference in New Issue
Block a user