navigo
simply manipulate GPS/geospatial data — in rust
See CHANGELOG.md for release notes.
api / usage
location
A location is a GPS coordinate defined by a longitude, a latitude, and an altitude.
let location = Location ;
- distance to another location (km):
let distance: f64 = paris.calculate_distance_to;
- bearing to another location (degrees):
let bearing: f64 = paris.calculate_bearing_to;
- elevation change to another location:
let elevation: Elevation = paris.calculate_elevation_to;
// elevation.positive — gain in meters
// elevation.negative — loss in meters
- check if inside a bounding box:
let area = Area ;
let is_in: bool = location.is_in_area;
- check if inside a radius (km):
let is_in: bool = location.is_in_radius;
trace
Trace::new ingests raw GPS locations and precomputes everything in one shot:
- Douglas-Peucker simplification (for traces > 1 000 points, ε = 15 m)
- Cumulative distances
- Denoised cumulative elevation gain / loss (median smoothing + hysteresis)
- Smoothed slope at each point
- Peaks and valleys (AMPD algorithm with prominence filter)
- Qualifying climb segments (Garmin-style thresholds)
A Trace is never empty — construction fails with TraceError::EmptyTrace
if locations is empty, so every other method can assume at least one point.
let trace: = new;
// or via the convenience wrapper:
let trace: = build_trace;
precomputed fields
trace.locations // Vec<Location> — simplified working set
trace.cumulative_distances // Vec<f64> — km from start, [0] == 0.0
trace.cumulative_elevation_gains // Vec<f64> — denoised gain in meters
trace.cumulative_elevation_losses // Vec<f64> — denoised loss in meters
trace.slopes // Vec<f64> — % grade at each point
trace.peaks // Vec<usize> — indices of detected peaks
trace.valleys // Vec<usize> — indices of detected valleys
trace.climbs // Vec<ClimbStats> — qualifying climb segments
trace.total_distance // f64 — total distance in km
trace.total_elevation_gain // f64 — total denoised gain in meters
trace.total_elevation_loss // f64 — total denoised loss in meters
methods
- total length (km):
let length: f64 = trace.length; // alias for total_distance
- location at a cumulative distance (km):
let loc: = trace.point_at_distance;
- index at a cumulative distance (binary search):
let idx: usize = trace.index_at_distance;
- slice between two distance marks (km, both ends inclusive):
let section: = trace.slice_between_distances;
- closest location to a point (early-stop heuristic for loop courses):
let = trace.find_closest_point.unwrap;
// start search from a known index (e.g. to handle loop courses):
let result = trace.find_closest_point_from;
- bounding box (never fails — a trace always has at least one point):
let area: Area = trace.area;
- sub-section by index range (inclusive):
let section: = trace.get_section;
climb stats
Climbs are qualified using Garmin Climb Pro thresholds:
distance ≥ 500 m, average gradient ≥ 3 %, distance × gradient > 3 500 m·%
WebAssembly
The library can be compiled to WASM for use in web applications via the wasm feature.
how it works
All data lives in WASM linear memory. The JS side holds a thin pointer (WasmTrace).
Only the boundaries cross the WASM↔JS membrane — scalars are free (registers), bulk arrays are copied once on demand.
buildTrace(Float64Array) ← one O(n) copy JS→WASM, null if no points
│
▼
WasmTrace stays in WASM memory
│
├── trace.total_distance → free (register)
├── trace.find_closest_point() → free (scalars in/out)
├── trace.locations_flat → one O(n) copy, cache it
└── trace.free() ← you must call this (no GC bridge)
build
usage
import init from './navigo.js';
await ;
// build — one copy in, all computation in WASM
const pts = ;
const trace = ;
// → WasmTrace, or null if pts carries no points
// scalar getters — free
trace.; // number (km)
trace.; // number (m)
trace.; // number (m)
trace.; // number
// array getters — copy once, then cache on the JS side
const locs = trace.; // Float64Array [lon,lat,alt,…]
const dists = trace.; // Float64Array (km)
const gains = trace.; // Float64Array (m)
const losses = trace.; // Float64Array (m)
const slopes = trace.; // Float64Array (%)
const peaks = trace.; // Uint32Array (indices)
const valleys = trace.; // Uint32Array (indices)
// query methods — scalars in, one small object out
trace.;
// → { longitude, latitude, altitude } | null
trace.;
// → number
trace.;
// → { location: { longitude, latitude, altitude }, index, distance } | null
trace.;
// → same shape | null (use on live-tracking loops)
trace.;
// → Float64Array [lon,lat,alt,…] | null
trace.;
// → Float64Array [lon,lat,alt,…] | null
trace.;
// → { min_longitude, max_longitude, min_latitude, max_latitude }
trace.;
// → { positive, negative } (raw, non-denoised)
trace.;
// → [{ start_index, end_index, start_dist_km, climb_dist_km,
// elevation_gain, summit_elev, avg_gradient }, …]
// always release when done — Rust allocator has no GC bridge
trace.;
memory management
WasmTrace lives in WASM linear memory. The JS object is just a pointer — Rust cannot reclaim it when the JS variable is GC'd. Always call .free(), or register a FinalizationRegistry:
const registry = ;
const trace = ;
registry.;