@@ -169,16 +169,18 @@ impl Navigator for NavigationController {
169169 ..
170170 } = initial_trip_state
171171 {
172- // If the user starts off-route, suppress instructions for the same reason as
173- // in `create_intermediate_trip_state`: the snap-derived distance to the next
174- // maneuver is geometrically unsound, so any countdown surfaced from it would
175- // mislead the user.
176- let ( visual_instruction, spoken_instruction) =
177- if deviation == RouteDeviation :: NoDeviation {
178- ( visual_instruction, spoken_instruction)
179- } else {
180- ( None , None )
181- } ;
172+ // If the user starts completely off the route, suppress instructions for the
173+ // same reason as in `create_intermediate_trip_state`: the snap-derived distance
174+ // to the next maneuver is geometrically unsound, so any countdown surfaced
175+ // from it would mislead the user. `OffStepOnRoute` is intentionally not
176+ // suppressed here — the user is still on the route polyline (just on a future
177+ // step), and the step-advance flow will reconcile shortly.
178+ let ( visual_instruction, spoken_instruction) = if deviation. is_completely_off_route ( )
179+ {
180+ ( None , None )
181+ } else {
182+ ( visual_instruction, spoken_instruction)
183+ } ;
182184 TripState :: Navigating {
183185 current_step_geometry_index,
184186 user_location,
@@ -383,28 +385,32 @@ impl NavigationController {
383385 ) ;
384386
385387 // Visual + spoken instructions are derived from the *snapped* distance to
386- // the next maneuver. While off-route, that snap is geometrically unsound:
387- // a user laterally far from the route still projects onto it somewhere, and
388- // the resulting "distance to next maneuver" is the phantom snap's distance,
389- // not the user's. Surfacing instructions paced off that phantom distance
390- // produces wrong countdowns to maneuvers the user can't take from their
391- // current position. Suppress both while off-route — the off-route signal
392- // (deviation + the consumer's own alert) is the right cue to surface;
393- // resume normal instruction emission on return. Apps that don't want this
394- // policy can configure `RouteDeviationTracking::None`.
395- let ( visual_instruction, spoken_instruction) =
396- if deviation == RouteDeviation :: NoDeviation {
397- (
398- current_step
399- . get_active_visual_instruction ( progress. distance_to_next_maneuver )
400- . cloned ( ) ,
401- current_step
402- . get_current_spoken_instruction ( progress. distance_to_next_maneuver )
403- . cloned ( ) ,
404- )
405- } else {
406- ( None , None )
407- } ;
388+ // the next maneuver. When the user is completely off the route, that snap
389+ // is geometrically unsound: a user laterally far from the route still
390+ // projects onto it somewhere, and the resulting "distance to next maneuver"
391+ // is the phantom snap's distance, not the user's. Surfacing instructions
392+ // paced off that phantom distance produces wrong countdowns to maneuvers
393+ // the user can't take from their current position. Suppress both while
394+ // completely off-route — the deviation flag (and the consumer's own alert)
395+ // is the right cue to surface; resume normal instruction emission on
396+ // return. `OffStepOnRoute` is intentionally not suppressed: the user is
397+ // still on the route polyline, and the step-advance flow will reconcile
398+ // shortly. Apps that don't want any of this policy can configure
399+ // `RouteDeviationTracking::None`.
400+ let ( visual_instruction, spoken_instruction) = if deviation
401+ . is_completely_off_route ( )
402+ {
403+ ( None , None )
404+ } else {
405+ (
406+ current_step
407+ . get_active_visual_instruction ( progress. distance_to_next_maneuver )
408+ . cloned ( ) ,
409+ current_step
410+ . get_current_spoken_instruction ( progress. distance_to_next_maneuver )
411+ . cloned ( ) ,
412+ )
413+ } ;
408414 let annotation_json = current_step_geometry_index
409415 . and_then ( |index| current_step. get_annotation_at_current_index ( index) ) ;
410416
@@ -790,7 +796,8 @@ mod tests {
790796 // Note: `update_user_location` computes deviation against the *previous* trip
791797 // state's user_location (mod.rs:289), so the deviation flag lags one tick. The
792798 // first off-route update therefore still reports NoDeviation; the second flips it
793- // to OffRoute, and that is when instruction suppression activates.
799+ // to `Deviation { kind: CompletelyOffRoute }` (the user is 55km from every step),
800+ // and that is when instruction suppression activates.
794801 let off_route_loc = make_user_location (
795802 coord ! ( x: start_coord. lng + 0.5 , y: start_coord. lat + 0.5 ) ,
796803 5.0 ,
@@ -805,24 +812,24 @@ mod tests {
805812 ..
806813 } => {
807814 assert ! (
808- matches! ( deviation, RouteDeviation :: OffRoute { .. } ) ,
809- "expected OffRoute on second off-route tick, got {deviation:?}"
815+ deviation. is_completely_off_route ( ) ,
816+ "expected CompletelyOffRoute on second off-route tick, got {deviation:?}"
810817 ) ;
811818 assert ! (
812819 visual_instruction. is_none( ) ,
813- "visual_instruction must be None while off-route, got {visual_instruction:?}"
820+ "visual_instruction must be None while completely off-route, got {visual_instruction:?}"
814821 ) ;
815822 assert ! (
816823 spoken_instruction. is_none( ) ,
817- "spoken_instruction must be None while off-route, got {spoken_instruction:?}"
824+ "spoken_instruction must be None while completely off-route, got {spoken_instruction:?}"
818825 ) ;
819826 }
820827 other => panic ! ( "expected Navigating, got {other:?}" ) ,
821828 } ;
822829
823830 // 3. Update back to the on-route location. The first return tick still carries
824- // the lagged OffRoute flag (deviation is computed from the previous user_location,
825- // which was off-route), so update twice to clear the lag.
831+ // the lagged CompletelyOffRoute flag (deviation is computed from the previous
832+ // user_location, which was off-route), so update twice to clear the lag.
826833 let recovered_lagged = controller. update_user_location ( on_route_loc. clone ( ) , off_state) ;
827834 let recovered = controller. update_user_location ( on_route_loc, recovered_lagged) ;
828835 match recovered. trip_state ( ) {
@@ -893,8 +900,8 @@ mod tests {
893900 ..
894901 } => {
895902 assert ! (
896- matches! ( deviation, RouteDeviation :: OffRoute { .. } ) ,
897- "expected OffRoute on initial state at off-route location, got {deviation:?}"
903+ deviation. is_completely_off_route ( ) ,
904+ "expected CompletelyOffRoute on initial state at off-route location, got {deviation:?}"
898905 ) ;
899906 assert ! (
900907 visual_instruction. is_none( ) ,
0 commit comments