GPS route matching, grouping, and section detection for fitness activity data.
Given a collection of GPS tracks (running, cycling, etc.), tracematch can compare routes for similarity, cluster activities by route, and detect frequently-traveled sections across your activity history. Written in Rust. Runs on mobile via UniFFI, in the browser via WASM, or standalone.
Three methods for finding recurring sections in GPS traces. All produce Vec<FrequentSection> with polylines from actual GPS tracks.
Finds corridors where many activities converge. Works directly on raw GPS traces without route grouping. Best coverage for most use cases.
- Rasterise all tracks into grid cells, count unique tracks per cell
- Threshold to "hot" cells (cells visited by N or more unique activities)
- Jaccard-gated union-find: adjacent hot cells merge only if their track sets overlap sufficiently. This prevents over-merging at intersections where runners diverge.
- For each connected component, find each track's longest contiguous run through it
- Select the median-distance track as the representative polyline
- Postprocess: merge nearby sections, remove overlapping
Detects sections where distinct route groups overlap. Requires pre-computed route grouping. Best for finding shared stretches between different routes.
- Rasterise route representatives into cells (one rep per route group)
- Build inverted index mapping cells to route IDs
- 4-connected union-find with Jaccard gate on route sets
- Extract per-track portions from each connected component
- Select medoid trace, compute consensus polyline via weighted averaging
- Postprocess: fold splitting, heading and gradient splits, merge, dedup
Models GPS data as directed traffic flow to identify road junctions. Sections are edges between divergence points. Network-topology approach.
- Rasterise tracks, record cell-to-cell transitions with directional flow
- Identify junctions: cells where 3+ exit directions each carry 15%+ of traffic
- Merge nearby junctions within 2 cells
- Trace edges between junctions via BFS along highest-traffic cells
- Merge pass-through junctions where stub edges are shorter than 5 cells
- Select median-distance track for each edge as the section polyline
use tracematch::{SectionConfig, DetectionMethod};
let config = SectionConfig {
detection_method: DetectionMethod::Corridor, // default
proximity_threshold: 150.0,
min_section_length: 200.0,
..SectionConfig::default()
};
let sections = tracematch::detect_sections(&tracks, &sport_types, &groups, &config);Individual methods are also available directly: detect_sections_corridor(), detect_sections_flow_graph(), detect_sections_multiscale().
Compares two GPS tracks by Average Minimum Distance (AMD). For each point on route A, find the nearest point on route B and average the distances. Runs both directions to detect subsets.
use tracematch::{GpsPoint, RouteSignature, MatchConfig, compare_routes};
let track = vec![
GpsPoint::new(51.5074, -0.1278),
GpsPoint::new(51.5080, -0.1290),
GpsPoint::new(51.5090, -0.1300),
];
let config = MatchConfig::default();
let sig1 = RouteSignature::from_points("run-1", &track, &config).unwrap();
let sig2 = RouteSignature::from_points("run-2", &track, &config).unwrap();
compare_routes(&sig1, &sig2, &config); // Some(100% match, same direction)cargo run --example route_matchingClusters activities by similarity using Union-Find. Each activity starts in its own group. When two routes match, their groups merge.
use tracematch::{GpsPoint, RouteSignature, MatchConfig, group_signatures};
let track: Vec<GpsPoint> = (0..10)
.map(|i| GpsPoint::new(51.5074 + i as f64 * 0.001, -0.1278))
.collect();
let config = MatchConfig::default();
let sigs = vec![
RouteSignature::from_points("monday", &track, &config).unwrap(),
RouteSignature::from_points("wednesday", &track, &config).unwrap(),
];
let groups = group_signatures(&sigs, &config);
// One group: ["monday", "wednesday"]cargo run --example route_groupingCompare all three detection methods on your own GPS data. Place GPX files in a directory and run:
cargo run --release --example corpus_report --features synthetic -- ./my_runsExample output from a 426-track running corpus:
┌──────────────────────┬────────────┬────────────┬────────────┐
│ │ Density │ Flow │ Corridor │
├──────────────────────┼────────────┼────────────┼────────────┤
│ Sections │ 6 │ 4 │ 32 │
│ Time │ 52 ms │ 130 ms │ 178 ms │
│ Total visits │ 412 │ 606 │ 821 │
│ Median visits │ 63 │ 123 │ 6 │
│ Median distance (m) │ 890 │ 526 │ 1059 │
│ Max distance (m) │ 2960 │ 905 │ 10206 │
└──────────────────────┴────────────┴────────────┴────────────┘
Benchmarked on real GPS traces (140-490 points per track):
| Operation | Time |
|---|---|
| Create signature | 10-16 us |
| Compare two routes | 20-28 us |
| Group 20 routes | 750 us |
| Corridor detection (426 tracks) | 178 ms |
R-tree spatial indexing gives O(log n) nearest-neighbor queries. Rayon parallelism is optional via the parallel feature.
Run benchmarks: cargo bench --bench route_matching
cargo add tracematchImplemented algorithms:
-
Beckmann, N., Kriegel, H.-P., Schneider, R., & Seeger, B. (1990). The R*-tree: An efficient and robust access method for points and rectangles. SIGMOD, 322-331.
-
Tarjan, R. E. (1975). Efficiency of a good but not linear set union algorithm. JACM, 22(2), 215-225.
-
Douglas, D. H., & Peucker, T. K. (1973). Algorithms for the reduction of the number of points required to represent a digitized line. Cartographica, 10(2), 112-122.
-
Kaufman, L., & Rousseeuw, P. J. (1987). Clustering by means of medoids. Statistical Data Analysis Based on the L1-Norm, 405-416.
-
Zhang, T. Y. & Suen, C. Y. (1984). A fast parallel algorithm for thinning digital patterns. Communications of the ACM, 27(3), 236-239.
Conceptual inspiration:
-
Lee, J.-G., Han, J., & Whang, K.-Y. (2007). Trajectory clustering: A partition-and-group framework. SIGMOD, 593-604.
-
Xu, W., & Dong, S. (2022). Unsupervised trajectory segmentation based on multiple motion features. Wireless Comm. and Mobile Computing, 2022.
-
Yang, J., Mariescu-Istodor, R., & Fränti, P. (2019). Three rapid methods for averaging GPS segments. Applied Sciences, 9(22), 4899.
-
Zygouras, N., et al. Discovering corridors from GPS trajectories.
Apache-2.0