Reading and acting
Understand positions and topology
Join a position to its track, convert distances and handle partial networks without inventing a route.
A track, a fraction and a direction
After game.snapshot(), use observation.track(id) and platform(id) for already indexed joins. To frequently look up nodes or track metrics, build your indexes once from nodes and trackMetrics in that same batch. A Position contains trackId, fraction and direction. The fraction runs from 0 to 1 from the track origin; direction +1 or −1 describes traversal and does not change that origin.
package wiki.trackpositions
import fr.nimby.sdk.Observation
import fr.nimby.sdk.Position
import fr.nimby.sdk.TrackDirection
import fr.nimby.sdk.TrackId
class TrackPositions(snapshot: Observation) {
private val metrics = snapshot.trackMetrics?.associateBy { it.trackId }
fun atMetres(track: TrackId, offsetM: Double, direction: TrackDirection): Position? {
val metric = metrics?.get(track.value) ?: return null
if (!offsetM.isFinite() || offsetM !in 0.0..metric.lengthM) return null
return metric.positionAtMetres(offsetM, direction)
}
}
Create TrackPositions with the returned Observation, then call atMetres for the required tracks. This example function returns null when the metric is missing or the distance lies outside the track; it avoids a linear search for every position. Replace the index when adopting a new snapshot.
| Conversion | Contract |
|---|---|
| metric.offsetM(fraction) | Distance from the origin, with a finite fraction in [0, 1]. |
| metric.fraction(offsetM) | Finite distance between 0 and lengthM; no extrapolation. |
| metric.distanceM(a, b) | Positive distance on this same track, with no route selection. |
| metric.positionAtMetres(m, direction) | Locally calculated position; this result does not authorise construction. |
Handle missing relationships
A capture may not supply every relationship. A missing node link does not prove there is a buffer stop. A junction describes connections and directions, not a train’s reserved route. Traversals must detect cycles, branches and missing references, with a distance or segment-count limit.
| Data | Interpretation |
|---|---|
| TrackUsage | An interval on a track associated with a train. Intervals may overlap; their order does not form a path. |
| reservations / occupations | null: table unavailable. A supplied table describes reservations and physical occupation separately. |
| selectedPath | Observed path for the selected train in a full capture; do not infer it from sorted reservations. |
| Platform | Connects a platform track to a station; not every track is a platform. |
The specialised game.trains.snapshot does not request the full network. Its track or occupation tables therefore cannot establish that the map is empty. In a Native tool, start from context.network() and the tool API topology helpers to avoid reimplementing those traversals.