Skip to content

Keep stop-focus color when a vehicle is selected (#1893 regression)#1899

Merged
bmander merged 1 commit into
mainfrom
fix/vehicle-select-keeps-stop-focus-color
Jul 16, 2026
Merged

Keep stop-focus color when a vehicle is selected (#1893 regression)#1899
bmander merged 1 commit into
mainfrom
fix/vehicle-select-keeps-stop-focus-color

Conversation

@bmander

@bmander bmander commented Jul 16, 2026

Copy link
Copy Markdown
Contributor

Problem

After #1893 ("Focus vehicles on exact trip geometry and stops"), tapping a vehicle while a route is emphasized inside stop focus reverted the route line to its GTFS color and layered the exact trip over a generic route-direction underlay — so it read as if the map had dropped into standalone route mode. The intuitive behavior is that the route keeps its stop-focus (adjacency) color and does not print the generic route-direction geometry.

Root cause

#1893 added showSelectionPresentation(tripId) to the selection collector, so every vehicle tap now calls publishMapPresentation(), whose new selected-trip branch unconditionally:

  • colored the exact trip with currentRouteColor() (the route's GTFS color), and
  • drew selectedDirectionUnderlay(...) (the whole direction shape, also GTFS-colored) beneath it.

In whole-route standalone mode that's correct (there's no adjacency color). But inside stop focus the emphasized route is drawn from the adjacency palette (_focusedRouteColors), so selecting a vehicle visibly swapped the adjacency-colored route for a GTFS trip over a generic underlay.

Fix

In RouteMapController.publishMapPresentation(), resolve the selected trip's color from the adjacency palette (routeColors[selected.routeDirection]) when the route is emphasized in stop focus, falling back to the GTFS color only in whole-route mode where no adjacency entry exists. In that emphasized case, also drop the generic direction underlay — the adjacency-colored trip already reads on its own, and the underlay is what made it look like route mode.

  • Stop focus + vehicle selected: exact trip in adjacency color, focused-stop siblings beneath, no generic underlay.
  • Whole-route standalone + vehicle selected: unchanged (GTFS trip + direction underlay).

As a small bonus this skips the O(n) underlay geometry build in the stop-focus case.

Testing

  • ./gradlew :onebusaway-android:compileObaGoogleDebugKotlin -PwarningsAsErrors=true — clean (CI warnings gate).
  • Built + installed obaGoogleDebug on device for on-device confirmation of the rendering.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Bug Fixes
    • Improved selected trip highlighting when viewing a stop’s route details.
    • Preserved stop-specific route colors for selected vehicles and trips.
    • Retained appropriate fallback coloring and directional styling when stop-specific colors are unavailable.

#1893 routed every vehicle selection through publishMapPresentation(), whose
new selected-trip branch always drew the exact trip in the route's GTFS color
(currentRouteColor()) over a generic direction underlay. Inside stop focus,
where the emphasized route is drawn in its adjacency color, tapping a vehicle
therefore reverted the route to its GTFS hue and layered the trip over the
generic route-direction geometry — reading as standalone route mode.

Resolve the selected trip's color from the adjacency palette
(routeColors[selected.routeDirection]) when the route is emphasized in stop
focus, falling back to the GTFS color only in whole-route mode where no
adjacency entry exists. In that emphasized case also drop the generic
direction underlay: the adjacency-colored trip already reads on its own, and
the underlay is what made it look like route mode. Whole-route standalone
selection is unchanged (GTFS trip + underlay).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jul 16, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 7cc378b4-2407-4176-a90b-ab94fcf7df01

📥 Commits

Reviewing files that changed from the base of the PR and between 2049fdb and 2224083.

📒 Files selected for processing (1)
  • onebusaway-android/src/main/java/org/onebusaway/android/map/RouteMapController.kt

📝 Walkthrough

Walkthrough

Selected trip rendering in stop focus now prefers route-direction adjacency colors and suppresses the selected-direction underlay when adjacency coloring is available, while retaining GTFS color and underlay fallbacks.

Changes

Selected trip rendering

Layer / File(s) Summary
Adjacency color and underlay composition
onebusaway-android/src/main/java/org/onebusaway/android/map/RouteMapController.kt
Selected trip polylines use the route-direction adjacency color when present, and only include the selected-direction underlay when that color is unavailable.

Estimated code review effort: 2 (Simple) | ~10 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main fix: preserving stop-focus color when a vehicle is selected.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/vehicle-select-keeps-stop-focus-color

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@bmander
bmander merged commit 0f9bfa6 into main Jul 16, 2026
3 checks passed
@bmander
bmander deleted the fix/vehicle-select-keeps-stop-focus-color branch July 16, 2026 20:18
bmander added a commit that referenced this pull request Jul 16, 2026
) (#1916)

publishMapPresentation() decided "am I in stop focus with this route
emphasized?" by checking whether an adjacency-color lookup happened to
return non-null — a proxy for the real stopFocusSession state one line
up. An opposite-direction selected vehicle inside stop focus (its
direction absent from the focused stop's trip set) would silently fall
back to GTFS color + the generic underlay, the exact regression #1899
fixed.

Extract selectedTripStyle() into RouteViewGeometry.kt: the underlay
decision now keys off stopFocusActive directly, while the color still
falls back to the GTFS color on an adjacency-color miss. Table-tested
across all four (stopFocusActive x adjacency-color-present)
combinations. Also drops the always-empty badges computation on the
selected-trip path.

Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
bmander added a commit that referenced this pull request Jul 17, 2026
The controller's publishMapPresentation() merged three presentation modes
(selected-vehicle exact trip, plain base route, stop-focus adjacency) via
early returns, and the #1899 regression was exactly a missed interaction
between two of them (adjacency colour vs. the generic direction underlay) —
yet none of that policy had a JVM test.

Extract the merge into a pure assembleRouteMapPresentation() in the new
RouteMapPresentationPlan.kt, beside RouteViewGeometry.kt: a function of (base
polylines/stops, focus session data, focused geometry/stops/routes, palette,
mode flags, selected-trip render inputs) -> a RouteMapPresentation render
plan. The controller keeps IO/lifecycle and now just resolves the IO-backed
pieces (selectedTripRenderInput) and forwards each plan field to the render /
stop layers. The one Location-dependent step, the focused-stop projection,
enters as a lazy thunk so the assembler stays plain-data and JVM-testable
across every branch.

Also dedup the nearest-point-across-polylines kernel shared by
projectFocusedStops and projectStopsOntoPolylines (moved to the new file) and
give both a unit home.

Tests:
- RouteMapPresentationPlanTest (JVM): the three-mode precedence, the
  adjacency-colour/underlay interaction (both sides of the #1899 case), the
  degenerate-selection fall-through, and selection-wins-over-focus.
- RouteStopProjectionTest (instrumented): the projection geometry, which snaps
  against android.location.Location and so can't run in this module's
  plain-JVM tests. Verified on-device (6/6 pass).

No behaviour change: the assembler is a faithful extraction of the prior
branching.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
bmander added a commit that referenced this pull request Jul 17, 2026
…) (#1920)

* RouteMapController: extract pure presentation assembly (#1907)

The controller's publishMapPresentation() merged three presentation modes
(selected-vehicle exact trip, plain base route, stop-focus adjacency) via
early returns, and the #1899 regression was exactly a missed interaction
between two of them (adjacency colour vs. the generic direction underlay) —
yet none of that policy had a JVM test.

Extract the merge into a pure assembleRouteMapPresentation() in the new
RouteMapPresentationPlan.kt, beside RouteViewGeometry.kt: a function of (base
polylines/stops, focus session data, focused geometry/stops/routes, palette,
mode flags, selected-trip render inputs) -> a RouteMapPresentation render
plan. The controller keeps IO/lifecycle and now just resolves the IO-backed
pieces (selectedTripRenderInput) and forwards each plan field to the render /
stop layers. The one Location-dependent step, the focused-stop projection,
enters as a lazy thunk so the assembler stays plain-data and JVM-testable
across every branch.

Also dedup the nearest-point-across-polylines kernel shared by
projectFocusedStops and projectStopsOntoPolylines (moved to the new file) and
give both a unit home.

Tests:
- RouteMapPresentationPlanTest (JVM): the three-mode precedence, the
  adjacency-colour/underlay interaction (both sides of the #1899 case), the
  degenerate-selection fall-through, and selection-wins-over-focus.
- RouteStopProjectionTest (instrumented): the projection geometry, which snaps
  against android.location.Location and so can't run in this module's
  plain-JVM tests. Verified on-device (6/6 pass).

No behaviour change: the assembler is a faithful extraction of the prior
branching.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* Cleanup: laziness, single lookup, file split for the presentation assembler

Follow-up polish on the #1907 extraction (no behaviour change):

- Restore the laziness the extraction lost. SelectedTripRenderInput built its
  directionUnderlay (a per-segment RoutePolyline copy) and stopPresentation
  (a Location stop projection) eagerly on every publish with a selected
  vehicle, but the assembler discards the underlay whenever the route has an
  adjacency colour and the stop projection whenever the trip is undrawable.
  The pre-extraction code computed both only inside the branch that used them.
  Defer both as thunks (mirroring the existing projectedFocusStops thunk) so
  each is computed only when its branch is taken.
- Collapse the selected-vehicle branch to one adjacency-colour lookup and drop
  the redundant `selected != null` guard (selectedTrip is non-null only when
  selected is), restoring the original single-nullable shape.
- Drop the now-dead `= { emptyMap() }` default on projectedFocusStops — the
  sole production caller always supplies it; make the contract explicit.
- Move the Location-dependent projection kernels (projectFocusedStops,
  projectStopsOntoPolylines, the shared nearest-point kernel) out of
  RouteMapPresentationPlan.kt into a new RouteStopProjection.kt, matching the
  instrumented RouteStopProjectionTest's name and leaving the assembler file
  cleanly JVM-pure.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* Document why whole-route emphasis keeps the base route; pin it with a test

Address CodeRabbit review on #1920: the exact-equality showBaseRoute check
(it.routeDirection == emphasizedRoute) was flagged as incorrectly keeping the
base route in whole-route mode. It is intentional, not a bug:

- In whole-route mode emphasizedRoute carries a null direction, so basePolylines
  is the merged both-directions shape. The single-direction adjacency lines cover
  only part of it, so the base must stay — dropping it (matching route id alone)
  would erase the unfocused direction.
- The proposed null-direction match would also empty the focused-stop
  presentation, since routeDirectionsByStopId's exact-match filter drops every
  concrete-direction trip against a null-direction key.

Expand the comment to record this, and add a JVM test pinning that whole-route
mode keeps the base route (and base stops) under a same-route focused stop. No
behaviour change.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant