How a bend becomes a cue

Nine decisions turn a change of direction in a track into a spoken instruction. Every threshold here was set on the evidence of a real file, and all of them live in one frozen object in src/route/match/thresholds.js.

Every number here is read from that object, every glyph is drawn by the app's own icon module, and the u-turn verdict is the matcher's own function answering. Nothing is routed: the turns come from the shape of the line.

Decision one

The angle picks the band

Measured as a bearing over a fixed distance of line either side of the point, never between neighbouring vertices: at a kerb radius those sit centimetres apart and the bearing between them is noise.

The u-turn band is provisional. Everything in it leaves this ruler as u-turn and is then re-examined, because the angle on its own cannot tell a hairpin from a turn-round. That is decision five.

The whole run

9 decisions, in the order the code makes them

Decision five, in full

Is it a u-turn, or a hairpin wearing a big angle?

A u-turn returns on the identical line. A near-reversal onto a road running alongside is a sided u-turn, worth one instruction instead of two consecutive lefts. Anything else is a sharp turn, and calling it a u-turn is misleading. The angle cannot separate them: in one file two turns 13° apart are a sharp left and a u-turn, because one comes back 31 m away and the other 16 m.

    Separation is a median, not a minimum

    The two paths always meet at the turn itself, so the closest approach is about zero for every turn ever measured. So the median gap over the probe distance is measured instead.

    The way boundary does what names cannot

    OpenStreetMap splits its ways at junctions, so a real u-turn crosses a way boundary and a mountain switchback does not. Names cannot do this job: both legs of one reported u-turn are called Sportlaan.

    The rules, running

    Put a bend in and watch it fall out

    These are the matcher's own branches, in the order it tries them. uTurnBranch reports which test fired and isBendOnSameRoad decides whether the cue survives, so the page cannot answer differently from the app.

    negative left, positive right

      What the rider can move

      Two settings, four thresholds behind them

      SettingMovesValueWhat changes

      The guard tests the angle, not the symbol. So a 160° switchback that decision five renamed right_sharp still survives with hairpin warnings off, because its angle is past uTurnDeg. Defensible, and not what "hairpin warnings off" sounds like it does. Worth a decision either way.