Add running route planner with quality comparison and GPX export
This commit is contained in:
@@ -0,0 +1,77 @@
|
||||
# Route planner
|
||||
|
||||
Open **Routes** in Stride. Select the start on the map, enter latitude/longitude,
|
||||
or use your location. Choose a loop or select a separate finish, distance (1–50 km),
|
||||
hill difficulty, surface preference and ranking priority. Generated candidates show
|
||||
their actual distance, ascent, route geometry, elevation profile and quality coverage.
|
||||
Save a named route privately, reopen it, download GPX, or delete it.
|
||||
|
||||
## Configuration
|
||||
|
||||
Apply migrations with `uv run --locked flask --app app db upgrade`.
|
||||
Set `ORS_API_KEY` in the web process environment and restart the web process.
|
||||
For local development, the app also reads `instance/ors-api-key` when the environment
|
||||
key is absent. This private file is ignored by Git; production only uses environment
|
||||
configuration. Keep the file within your private Windows account/workspace.
|
||||
For Dokku, configure `ORS_API_KEY` through your normal secret-management workflow.
|
||||
The key stays server-side and is never included in rendered HTML or requests from
|
||||
the browser. Obtain a key at https://openrouteservice.org/ and review the provider's
|
||||
current quotas. The planner sends selected coordinates to the fixed ORS HTTPS
|
||||
endpoint. Browser map tiles use the app's existing OpenStreetMap tile service.
|
||||
|
||||
Without a key, generation is disabled with a configuration notice; saved routes,
|
||||
maps and export remain available. No synthetic routes are shown in the real app.
|
||||
|
||||
## Generation and ranking
|
||||
|
||||
The server makes at most three requests per generation, each with a 12-second
|
||||
network timeout and a 4 MB response limit. Requests use `foot-walking`, elevation,
|
||||
and `surface`/`waytype` extras. Loops use three deterministic round-trip seeds.
|
||||
Point-to-point candidates include a direct route and two estimated side waypoints
|
||||
to seek a longer route near the target. Duplicate geometries are removed. This
|
||||
bounded search can return fewer than three routes and cannot guarantee a distance
|
||||
or ascent target. The interface flags results outside ±5% distance.
|
||||
|
||||
Preferences rank candidates; they are not strict exclusions. Hill targets are
|
||||
approximately 3, 12 or 25 metres ascent/km. The cost combines distance deviation,
|
||||
ascent mismatch, mapped road share, missing surface coverage, optional repeated
|
||||
edges and surface preference. The priority weights distance, hills or quality
|
||||
more strongly. This is an explainable heuristic, not a safety or fitness score.
|
||||
|
||||
Quality percentages follow ORS documented waytype and surface IDs:
|
||||
- Paths/tracks: path, track, cycleway and footway.
|
||||
- Road share: state road, road and street; this does not measure traffic.
|
||||
- Paved: paved, asphalt, concrete, legacy cobblestone, metal and paving stones.
|
||||
- Missing surface data is unknown; absent extras do not become zero.
|
||||
- Repeated distance estimates subsequent traversals of the same rounded geometry
|
||||
edge in either direction. Different segmentation can miss repeated sections.
|
||||
|
||||
Crossing counts, lighting, current access and live traffic are unverified.
|
||||
GPX includes the route and available elevation, without fictional timestamps.
|
||||
No direct Garmin course upload or place-name search is included in this version.
|
||||
|
||||
## Persistence and validation
|
||||
|
||||
Candidates and saved routes are server-side and owner-scoped. Other users,
|
||||
including friends, cannot view, mutate or export them. Save and delete use CSRF
|
||||
protection. Unsaved candidates older than one day are cleaned on the owner's next
|
||||
generation. The library lists the latest 100 saved routes. Generation is limited
|
||||
to six requests/minute and 60/day using the existing limiter storage configuration.
|
||||
|
||||
Offline tests mock ORS and cover input validation, ranking, point-to-point and
|
||||
round-trip request shapes, deduplication, missing attributes, repeated sections,
|
||||
CSRF, ownership, save/delete/export and draft cleanup. Live ORS verification needs
|
||||
a configured API key; mocked tests do not establish live provider availability.
|
||||
|
||||
Live local verification on 2026-10-03 used public Centennial Park test coordinates.
|
||||
Loop and point-to-point routing each returned three distinct candidates; private
|
||||
save, reload and GPX export succeeded in a disposable in-memory test database.
|
||||
The best loop was 5,030.9 m with 61.5 m ascent and 90% mapped paths/footways.
|
||||
The local browser rendered the real route, quality figures and elevation profile
|
||||
without console errors. The suite passed 129 tests. Live provider category IDs
|
||||
arrive as decimal numbers; a regression test covers that response format.
|
||||
|
||||
Provider references:
|
||||
https://giscience.github.io/openrouteservice/api-reference/endpoints/directions/extra-info/
|
||||
https://giscience.github.io/openrouteservice/api-reference/endpoints/directions/extra-info/waytype
|
||||
https://giscience.github.io/openrouteservice/api-reference/endpoints/directions/extra-info/surface
|
||||
Reference in New Issue
Block a user