How coverage is calculated
The server-side rules that turn an uploaded GPS track into Groomed surface-condition updates: the 10 m buffer, the 10% threshold, and what is written.
When the Grooming Tracker uploads a track, a TrailHUB server function analyses it and decides which trails to mark as groomed. This page describes exactly what it does so you can predict the result and verify it.
#Inputs
- The track: the GeoJSON
FeatureCollectionthe app uploaded. Only features whose geometry is a non-emptyLineStringare used. The app currently uploads exactly one. - The trail system's published GeoJSON: the same compiled file the public map uses (see Published GeoJSON). Only features that are non-empty
LineStrings and have both atrailIdand anameare considered. Point features, and trails stored asMultiLineString, are skipped.
If either set is empty the analysis stops and nothing is posted.
#Permission check
Before any geometry work, the function loads the trail system named in the track and checks that the uploading account is its owner or is listed in its users (trustees). If not, the track record is deleted and nothing else happens. This is why you must be a manager of the system you select in the app.
#The matching rule
For each track line and each eligible trail:
Buffer the track by 10 metres (0.01 km) on each side, producing a corridor polygon. If the track had several lines their buffers are combined.
Buffer the trail by the same 10 m.
Intersect the two buffers. If they do not overlap at all, the trail is not covered.
Measure the intersection's length and the trail's length (both in kilometres) and compute
coverage % = intersection length / trail length × 100If coverage > 10%, the trail is recorded as covered, with the percentage rounded to two decimals.
#What this means in practice
- A GPS error of a few metres is absorbed: the two 10 m buffers mean the track centreline can be up to roughly 20 m from the trail centreline and still count.
- Trails that run parallel within ~20 m of the one you groomed (a loop's return leg, a connector alongside) may also be credited.
- Crossing a trail at right angles contributes almost nothing to its coverage, so junction crossings rarely trip the 10% rule by themselves.
- The threshold is per trail and proportional: 10% of a 500 m connector is 50 m, 10% of a 12 km loop is 1.2 km. Grooming the first few hundred metres of a long trail will not mark it groomed.
- Coverage is binary for the update. A trail that was 11% covered and one that was 100% covered both get the same Groomed condition; the percentages are stored on the track record only.
#What gets written
If at least one trail passes the threshold, the function creates one update document for the trail system:
| Field | Value |
|---|---|
type | surfaceConditions |
label | Surface Conditions |
attachToTrails | true (it is a trail-level update) |
conditions | [ groomed ] only (shown as "Groomed" / "Damée") |
relevantTrails | one entry per covered trail: trailId, name, coveragePercentage |
expirationDate | 24 hours after analysis |
notify | false — subscribers are never emailed or texted by tracker updates |
createdBy | the account that uploaded the track |
createdOn | time of analysis |
It then stamps updatedOn / updatedBy on every covered trail and queues a compile request, so the public map, trail lists, embeds and the GET /api/ts/:id stats reflect the new condition within seconds.
Finally the track record itself is updated with analyzed: true, analyzedAt, coveredTrails (the list above) and totalTrailsCovered.
If no trail passes the threshold, no update is created; the track is still marked analysed with an empty coveredTrails list.
#Things it deliberately does not do
- It never changes a trail's status (Open / Caution / Closed). A closed trail that you groom stays closed but shows Groomed.
- It does not remove or replace earlier surface-condition updates. Two passes over the same trail produce two updates; both show until they expire.
- It does not post
track-set,skateGroomedorclassicGroomed. Edit the update in the web app or post an additional one via the Management API if you need those.
#Verifying in the Updates menu
- Sign in at trailhub.org and open the trail system.
- Open the Updates menu (the same place you post notices and snow reports).
- Find the Surface Conditions update created at the time of your upload. Its trail list is the set of covered trails and its condition is Groomed. It expires 24 hours after the upload.
- From there you can edit it (add
track-set, extend the expiry, remove a trail that was wrongly credited) or delete it.
You can also check with the Management API:
curl -H "Authorization: Bearer th_..." \
"https://trailhub.org/api/v1/trail-systems/TS_ID/updates"Updates created by the tracker have type: "surfaceConditions", conditions: ["groomed"] and a trailIds array of the covered trails. (They do not carry source: "api"; that marker is only set on updates posted through the API.)
#Why a trail you groomed was not marked
| Cause | Explanation |
|---|---|
| Coverage ≤ 10% | You covered too little of that trail's length. Check against the trail's distance in the web app. |
| Track too far from the trail | GPS drift or a parallel pass more than ~20 m from the mapped line. Check the trail geometry in the web app; a mis-drawn trail will never match. |
Trail stored as MultiLineString | Only LineString trail features are analysed. Redraw the trail as a single line in the web app. |
| Trail has no name | Features without a name are skipped. |
| Account not a manager of the system | The track was deleted without analysis. |
| Trail system has never been compiled | No published GeoJSON exists yet. Make any edit in the web app to trigger a compile. |
| Resume was used in the app | The pre-pause segment was discarded; see Known limitations. |
#Why a trail you did not groom was marked
Most often a parallel trail within ~20 m, or a trail that shares a long stretch with the one you groomed (overlapping geometries). Edit the update in the Updates menu to remove it, and consider adjusting the trail geometry if the two lines genuinely overlap.