docs: update elevation docs and changelog for two-button recalculation and DEM fix
This commit is contained in:
+35
-24
@@ -26,37 +26,48 @@ the last committed value. GPS noise is suppressed without losing real climbs.
|
|||||||
suppression, barometric vs GPS threshold difference, real climb approximation,
|
suppression, barometric vs GPS threshold difference, real climb approximation,
|
||||||
unknown-treated-as-gps invariant
|
unknown-treated-as-gps invariant
|
||||||
|
|
||||||
### New feature — DEM-based elevation recalculation from the edit drawer
|
### New feature — On-demand elevation recalculation from the edit drawer
|
||||||
|
|
||||||
A new **"Recalculate from terrain map (DEM)"** button in the activity edit
|
Two new buttons in the activity edit drawer fix inaccurate elevation stats
|
||||||
drawer replaces noisy GPS altitude with SRTM terrain data, then re-applies
|
without re-uploading the file:
|
||||||
hysteresis accumulation to compute corrected gain/loss.
|
|
||||||
|
|
||||||
This is the recommended fix for activities that still show inaccurate
|
**📐 Recalculate (hysteresis)** — re-applies source-aware hysteresis
|
||||||
elevation after the hysteresis improvement (e.g. activities recorded before
|
accumulation to the original recorded elevation. Fast, offline, no network
|
||||||
re-extracting from sources, or uploads where the source file had severe GPS
|
required. Best for barometric altimeters (Karoo 2, Garmin with
|
||||||
altitude noise).
|
`enhanced_altitude`, Wahoo) that were extracted before the noise-filtering
|
||||||
|
improvement.
|
||||||
|
|
||||||
How it works:
|
**⛰ Recalculate (DEM)** — replaces GPS altitude with SRTM terrain data, then
|
||||||
1. The server subsamples the activity's 1 Hz GPS track (one point every 10 s)
|
re-applies hysteresis. Best for GPS-only devices where the recorded altitude
|
||||||
2. Queries an Open-Elevation-compatible API for terrain elevation
|
is noisy.
|
||||||
3. Linearly interpolates DEM elevation back to every GPS-valid second
|
|
||||||
4. Applies 5 m hysteresis to compute the corrected gain and loss
|
|
||||||
5. Writes the updated `elevation_m` array to the timeseries (chart updates)
|
|
||||||
6. Patches `elevation_gain_m` / `elevation_loss_m` in the activity JSON and
|
|
||||||
`index.json` summary
|
|
||||||
|
|
||||||
- **`bincio/extract/dem.py`** (new) — `lookup_elevations()` (batched HTTP POST,
|
DEM pipeline (revised after discovering that a naive 5 m threshold produced
|
||||||
Open-Elevation wire format) + `recalculate_elevation()` (full pipeline above)
|
results worse than no correction on some activities):
|
||||||
- **`POST /api/activity/{id}/recalculate-elevation`** — on both `bincio serve`
|
1. Subsample GPS track to one point per 10 s
|
||||||
(auth-gated, triggers `merge_one` + rebuild) and `bincio edit` (no auth)
|
2. Query Open-Elevation API in batches of 512
|
||||||
|
3. Linearly interpolate back to the full 1 Hz series
|
||||||
|
4. Apply a **45 s sliding median filter** to suppress SRTM tile-boundary
|
||||||
|
steps (occur every ~7 s at cycling speed; were accumulating through 5 m
|
||||||
|
threshold and inflating gain by 50 %+)
|
||||||
|
5. Apply **10 m hysteresis** to the smoothed series
|
||||||
|
6. Back up original `elevation_m` as `elevation_m_original` in the timeseries
|
||||||
|
on the first DEM run (never overwrites an existing backup)
|
||||||
|
|
||||||
|
- **`bincio/extract/dem.py`** (new) — `lookup_elevations()`,
|
||||||
|
`recalculate_elevation()` (DEM + median + 10 m hysteresis),
|
||||||
|
`recalculate_elevation_hysteresis()` (offline, reads `elevation_m_original`
|
||||||
|
if available, uses 5 m/10 m source-aware threshold)
|
||||||
|
- **`POST /api/activity/{id}/recalculate-elevation/dem`** and
|
||||||
|
**`POST /api/activity/{id}/recalculate-elevation/hysteresis`** — on both
|
||||||
|
`bincio serve` (auth-gated, triggers `merge_one` + rebuild) and
|
||||||
|
`bincio edit` (no auth)
|
||||||
- **`bincio serve --dem-url URL`** / **`bincio edit --dem-url URL`** — override
|
- **`bincio serve --dem-url URL`** / **`bincio edit --dem-url URL`** — override
|
||||||
the default DEM endpoint (also read from `DEM_URL` env var)
|
the default DEM endpoint (also read from `DEM_URL` env var)
|
||||||
- Default DEM endpoint: **`https://api.open-elevation.com`** — works out of the
|
- Default DEM endpoint: **`https://api.open-elevation.com`** — works out of
|
||||||
box with no configuration
|
the box with no configuration
|
||||||
- **`GET /api/me`** response gains `dem_configured: bool`
|
- **`GET /api/me`** response gains `dem_configured: bool`
|
||||||
- **`EditDrawer.svelte`** — button with spinner, shows `↑ Xm ↓ Ym` on success
|
- **`EditDrawer.svelte`** — two side-by-side buttons with individual spinners,
|
||||||
or an inline error (e.g. if the DEM API is unreachable)
|
shows `↑ Xm ↓ Ym` on success or inline error
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
+45
-13
@@ -277,23 +277,55 @@ require re-extraction from source files.
|
|||||||
|
|
||||||
### Medium term — ✅ Implemented (2026-04-20)
|
### Medium term — ✅ Implemented (2026-04-20)
|
||||||
|
|
||||||
**On-demand DEM correction** via the edit drawer, using the Open-Elevation API
|
**Two on-demand recalculation options** in the activity edit drawer:
|
||||||
(SRTM30 data):
|
|
||||||
|
#### Option 1 — Hysteresis (fast, offline)
|
||||||
|
|
||||||
|
Re-applies the same source-aware hysteresis accumulation as the extract
|
||||||
|
pipeline directly to the recorded elevation, with no network calls.
|
||||||
|
|
||||||
|
- Uses `elevation_m_original` from the timeseries (the backup saved on the
|
||||||
|
first DEM run) if present; otherwise uses the current `elevation_m`.
|
||||||
|
- Threshold: **5 m** for barometric sources, **10 m** for GPS.
|
||||||
|
- Does not modify the elevation array in the timeseries — only patches
|
||||||
|
`elevation_gain_m` / `elevation_loss_m`.
|
||||||
|
- Best for: devices with a barometric altimeter (e.g. Karoo 2, Garmin with
|
||||||
|
`enhanced_altitude`) where the recorded elevation is already accurate but
|
||||||
|
was extracted before the hysteresis fix was deployed.
|
||||||
|
|
||||||
|
#### Option 2 — DEM terrain correction (SRTM30, requires network)
|
||||||
|
|
||||||
|
Replaces the recorded GPS altitude with terrain data from an
|
||||||
|
Open-Elevation-compatible API (SRTM30, ~30 m resolution):
|
||||||
|
|
||||||
1. GPS track subsampled to one point per 10 s to minimise API calls.
|
1. GPS track subsampled to one point per 10 s to minimise API calls.
|
||||||
2. Terrain elevation fetched via `POST https://api.open-elevation.com/api/v1/lookup`
|
2. Terrain elevation fetched via `POST https://api.open-elevation.com/api/v1/lookup`
|
||||||
in batches of 512.
|
in batches of 512.
|
||||||
3. DEM elevation linearly interpolated back to the full 1 Hz series.
|
3. DEM elevation linearly interpolated back to the full 1 Hz series.
|
||||||
4. 5 m hysteresis applied to the interpolated series.
|
4. **45 s sliding median filter** applied to suppress SRTM tile-boundary
|
||||||
5. Timeseries and activity JSON patched in place; chart and stats update immediately.
|
steps (these occur every ~7 s at cycling speed and accumulate as phantom
|
||||||
|
gain through a naive threshold).
|
||||||
|
5. **10 m hysteresis** applied to the smoothed series.
|
||||||
|
6. Original elevation backed up as `elevation_m_original` in the timeseries
|
||||||
|
(only on the first DEM run — never overwrites an existing backup).
|
||||||
|
7. Timeseries and activity JSON patched in place; chart and stats update.
|
||||||
|
|
||||||
Implementation: `bincio/extract/dem.py` + `POST /api/activity/{id}/recalculate-elevation`
|
Best for: GPS-only devices (no barometric sensor) where the recorded
|
||||||
on both servers. Default endpoint: `https://api.open-elevation.com`; override with
|
altitude is noisy and the DEM terrain is a better ground truth.
|
||||||
|
|
||||||
|
> **Why median + 10 m, not 5 m?** SRTM30 at 1 Hz produces step changes at
|
||||||
|
> tile boundaries of 1–3 m every few seconds. A 5 m threshold lets most of
|
||||||
|
> these through; they accumulate and can inflate the result by 50 %+. The
|
||||||
|
> 45 s median smooths the steps before the dead-band sees them; 10 m catches
|
||||||
|
> any residual outliers.
|
||||||
|
|
||||||
|
Implementation: `bincio/extract/dem.py` — `lookup_elevations()`,
|
||||||
|
`recalculate_elevation()`, `recalculate_elevation_hysteresis()`.
|
||||||
|
API endpoints: `POST /api/activity/{id}/recalculate-elevation/dem` and
|
||||||
|
`POST /api/activity/{id}/recalculate-elevation/hysteresis` on both servers.
|
||||||
|
Default DEM endpoint: `https://api.open-elevation.com`; override with
|
||||||
`--dem-url` or `DEM_URL` env var.
|
`--dem-url` or `DEM_URL` env var.
|
||||||
|
|
||||||
This is the recommended fix for activities uploaded before the hysteresis improvement,
|
|
||||||
or any activity where GPS noise is severe.
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Implementation status
|
## Implementation status
|
||||||
@@ -305,8 +337,8 @@ or any activity where GPS noise is severe.
|
|||||||
| `bincio/extract/parsers/gpx.py` | ✅ sets `altitude_source = "gps"` |
|
| `bincio/extract/parsers/gpx.py` | ✅ sets `altitude_source = "gps"` |
|
||||||
| `bincio/extract/parsers/tcx.py` | ✅ sets `altitude_source = "gps"` |
|
| `bincio/extract/parsers/tcx.py` | ✅ sets `altitude_source = "gps"` |
|
||||||
| `bincio/extract/metrics.py` | ✅ hysteresis `_elevation()` with source-aware threshold |
|
| `bincio/extract/metrics.py` | ✅ hysteresis `_elevation()` with source-aware threshold |
|
||||||
| `bincio/extract/dem.py` | ✅ `lookup_elevations()` + `recalculate_elevation()` |
|
| `bincio/extract/dem.py` | ✅ `lookup_elevations()` + `recalculate_elevation()` (median+10m) + `recalculate_elevation_hysteresis()` |
|
||||||
| `bincio/serve/server.py` | ✅ `POST /api/activity/{id}/recalculate-elevation` |
|
| `bincio/serve/server.py` | ✅ `POST /api/activity/{id}/recalculate-elevation/{dem\|hysteresis}` |
|
||||||
| `bincio/edit/server.py` | ✅ same endpoint (single-user) |
|
| `bincio/edit/server.py` | ✅ same endpoints (single-user) |
|
||||||
| `site/src/components/EditDrawer.svelte` | ✅ "Recalculate from terrain map" button |
|
| `site/src/components/EditDrawer.svelte` | ✅ two buttons: "Recalculate (hysteresis)" + "Recalculate (DEM)" |
|
||||||
| `tests/test_metrics.py` | ✅ 5 parametric tests |
|
| `tests/test_metrics.py` | ✅ 5 parametric tests |
|
||||||
|
|||||||
+14
-11
@@ -73,20 +73,23 @@ Click **Edit** on any activity to:
|
|||||||
|
|
||||||
Changes save instantly. The site rebuilds in the background.
|
Changes save instantly. The site rebuilds in the background.
|
||||||
|
|
||||||
### Recalculating elevation from terrain data
|
### Recalculating elevation
|
||||||
|
|
||||||
If an activity shows an unrealistic elevation gain (common with GPS-only devices on flat
|
If an activity shows an unrealistic elevation gain, the edit drawer has two buttons:
|
||||||
routes, or with older Garmin/Wahoo files), the edit drawer has a
|
|
||||||
**"Recalculate from terrain map (DEM)"** button.
|
|
||||||
|
|
||||||
Clicking it replaces the recorded GPS altitude with SRTM terrain data from the
|
**📐 Recalculate (hysteresis)** — recomputes gain and loss from the original recorded
|
||||||
[Open-Elevation API](https://open-elevation.com) and recomputes the gain and loss. The
|
elevation using a noise-filtering dead-band algorithm. Fast and offline — no network
|
||||||
elevation chart and the summary stats both update. This usually brings the numbers in
|
call. Best for devices with a barometric altimeter (Garmin, Karoo, Wahoo) whose
|
||||||
line with what Strava or your device's app reports.
|
elevation data is accurate but was extracted before the noise-filtering was improved.
|
||||||
|
|
||||||
> **Note:** The correction requires a GPS track (activities marked *No GPS* cannot be
|
**⛰ Recalculate (DEM)** — replaces the recorded GPS altitude with SRTM terrain data
|
||||||
> corrected). The DEM has ~30 m horizontal resolution, so very short or indoor activities
|
from the [Open-Elevation API](https://open-elevation.com) and recomputes gain and
|
||||||
> are not meaningfully improved.
|
loss. The elevation chart and summary stats both update. Best for GPS-only devices
|
||||||
|
(no barometric sensor) where the recorded altitude is noisy.
|
||||||
|
|
||||||
|
> **Note:** Both corrections require a GPS track (activities marked *No GPS* cannot be
|
||||||
|
> corrected). The DEM option uses ~30 m resolution terrain data; very short or indoor
|
||||||
|
> activities see little improvement from DEM correction.
|
||||||
|
|
||||||
### Photo gallery
|
### Photo gallery
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user