From d2f09bea32489c7e7521b4e1e9a104a1167f49c3 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 15 Jul 2026 05:38:41 +0000 Subject: [PATCH] Honor "Hidden" points of interest for bus stops and declutter at low zoom Bus stop markers are our own Marker overlay drawn from Overpass data, not Apple's native POI layer, so setting Points of Interest to "Hidden" (which only sets MapStyle's pointsOfInterest category filter) had no effect on them -- they kept showing regardless of the setting. Separately, a bus route can have dozens of stops close together; zoomed out they overlap into an unreadable stack of pins (see reported screenshot: a wall of stacked bus icons across a multi-city view). Fixes: - Gate the bus stop markers on mapPointsOfInterestMode != .hidden so "Hidden" actually hides them. - Track the map's visible latitude span via onMapCameraChange and thin markers through the existing dedupedBusStops spatial merge, using a spacing that scales with the visible span -- full detail at close zoom, progressively fewer markers as the map zooms out. The full-fidelity routeBusStops list (used for dwell pacing during playback) is untouched; only the on-screen marker set is thinned. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_013DNqCsHShA4yNqgZ6ZZQy3 --- StikDebug/Views/MapSelectionView.swift | 35 ++++++++++++++++++++++---- 1 file changed, 30 insertions(+), 5 deletions(-) diff --git a/StikDebug/Views/MapSelectionView.swift b/StikDebug/Views/MapSelectionView.swift index 56f5a10..4364f83 100644 --- a/StikDebug/Views/MapSelectionView.swift +++ b/StikDebug/Views/MapSelectionView.swift @@ -1436,6 +1436,7 @@ struct LocationSimulationView: View { @State private var routePolyline: MKPolyline? @State private var routePlaybackSamples: [RoutePlaybackSample] = [] @State private var routeBusStops: [CLLocationCoordinate2D] = [] + @State private var visibleLatitudeDelta: CLLocationDegrees = 0.05 @State private var routePlaybackCoordinate: CLLocationCoordinate2D? @State private var simulatedCoordinate: CLLocationCoordinate2D? @State private var routeRequestID = UUID() @@ -1494,6 +1495,29 @@ struct LocationSimulationView: View { } } + /// Bus stop markers to actually draw on the map. Two adjustments on top of + /// the raw fetched `routeBusStops`: + /// + /// - Honors "Points of Interest: Hidden" — the stops are drawn as our own + /// Marker overlay, not Apple's native POI layer, so Apple's + /// `pointsOfInterest` style option (set from the same picker) has no + /// effect on them; that had to be handled here explicitly. + /// - Thins markers as the map zooms out, since a bus route easily has + /// dozens of stops that overlap into an unreadable stack of pins at a + /// city-wide zoom. Spacing scales with the visible latitude span so + /// roughly the same number of markers are visible on screen at any + /// zoom level; the full-fidelity `routeBusStops` list (used for dwell + /// pacing during playback) is untouched. + private var displayedBusStops: [CLLocationCoordinate2D] { + guard speedProfile == .bus, mapPointsOfInterestMode != .hidden else { return [] } + let visibleSpanMeters = visibleLatitudeDelta * 111_320 + let minimumSpacing = max( + SpeedProfileSettings.busStopSnapRadius, + visibleSpanMeters / 25 + ) + return dedupedBusStops(routeBusStops, radius: minimumSpacing) + } + private var speedProfile: SpeedProfile { SpeedProfile(rawValue: speedProfileRawValue) ?? .driving } @@ -1648,11 +1672,9 @@ struct LocationSimulationView: View { MapPolyline(routePolyline) .stroke(.blue.opacity(0.8), lineWidth: 5) } - if speedProfile == .bus { - ForEach(Array(routeBusStops.enumerated()), id: \.offset) { _, stop in - Marker("Bus Stop", systemImage: "bus.fill", coordinate: stop) - .tint(.orange) - } + ForEach(Array(displayedBusStops.enumerated()), id: \.offset) { _, stop in + Marker("Bus Stop", systemImage: "bus.fill", coordinate: stop) + .tint(.orange) } if let routeStartCoordinate { Marker("Start", coordinate: routeStartCoordinate) @@ -1680,6 +1702,9 @@ struct LocationSimulationView: View { .mapControls { MapCompass() } + .onMapCameraChange(frequency: .onEnd) { context in + visibleLatitudeDelta = context.region.span.latitudeDelta + } } .ignoresSafeArea() .onChange(of: coordinate.map(CoordinateSnapshot.init)) { _, new in