89 lines
5.4 KiB
Markdown
89 lines
5.4 KiB
Markdown
# Route planner
|
||
|
||
Open **Routes** in Stride. Select the start on the map, enter latitude/longitude,
|
||
search by typing a place/address and choosing a suggestion, or use your location.
|
||
Both start and finish support search with arrow-key/Enter selection. Suggestions
|
||
are biased towards the current map view, without restricting the search region.
|
||
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 is included in this version.
|
||
|
||
Place searches use the same server-side ORS key and a fixed geocoding endpoint.
|
||
Typing waits 650 ms before searching; old responses are discarded when the query
|
||
changes. Queries are bounded to 3–200 characters with at most five results and an
|
||
eight-second provider timeout. The authenticated POST endpoint requires CSRF and
|
||
is limited to 30 searches/minute and 600/day. Provider labels are inserted as text.
|
||
Start/finish pins use self-contained CSS markers, so no external marker images
|
||
are required. Selected pins can be dragged to adjust their location.
|
||
|
||
## 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
|