diff --git a/FuelBoard/RoadDistanceService.swift b/FuelBoard/RoadDistanceService.swift index 812ce35..2205770 100644 --- a/FuelBoard/RoadDistanceService.swift +++ b/FuelBoard/RoadDistanceService.swift @@ -15,17 +15,22 @@ import MapKit import WidgetKit enum RoadDistanceService { - /// How many nearest stations to route per pass (bounds MKDirections calls). - static let candidatesPerPass = 12 + /// Upper bound on stations routed per pass, so a pass stays a bounded set of + /// route calls. Raised from 12 so stations past the old nearest-12 cutoff + /// still get real road distances instead of a straight-line fallback. + static let candidatesPerPass = 40 + /// Only route stations within this straight-line radius (km). Covers the + /// largest search radius the UI exposes (15 mi ≈ 24.1 km) plus margin, so + /// every station a widget/Live Activity/list can actually show gets routed. + static let maxRadiusKM: Double = 25 /// Don't route again more often than this (minutes). static let throttleMinutes: Double = 10 /// Recompute when the user moves more than this (metres) from the last /// source location. static let moveThresholdMeters: Double = 400 - /// Refreshes the cached road distances for the nearest `candidatesPerPass` - /// stations around `lat`/`lng`. Throttled by time + distance; safe to call - /// on every location fix. + /// Refreshes the cached road distances for the in-radius stations around + /// `lat`/`lng`. Throttled by time + distance; safe to call on every fix. static func refreshIfNeeded(stations: [FuelStation], lat: Double, lng: Double) async { guard !stations.isEmpty else { return } @@ -38,11 +43,12 @@ enum RoadDistanceService { } } - // Candidate stations: nearest by straight-line (these are what widgets - // and the Live Activity show in-radius). + // Candidate stations: the nearest-by-straight-line subset that the UI + // could actually display, capped so a pass stays bounded. let nearest = stations .sorted { $0.distanceKM(to: lat, lng2: lng) < $1.distanceKM(to: lat, lng2: lng) } .prefix(candidatesPerPass) + .filter { $0.distanceKM(to: lat, lng2: lng) <= maxRadiusKM } let origin = CLLocationCoordinate2D(latitude: lat, longitude: lng) var entries: [String: Double] = [:] diff --git a/FuelBoardTests/Tests/FuelBoardSharedTests/FuelBoardTests.swift b/FuelBoardTests/Tests/FuelBoardSharedTests/FuelBoardTests.swift index 34123de..d16c7b5 100644 --- a/FuelBoardTests/Tests/FuelBoardSharedTests/FuelBoardTests.swift +++ b/FuelBoardTests/Tests/FuelBoardSharedTests/FuelBoardTests.swift @@ -624,6 +624,14 @@ final class OfflineDataLabelTests: XCTestCase { // MARK: - Road distance cache final class RoadDistanceCacheTests: XCTestCase { + override func setUp() { + super.setUp() + // Keychain persists across invocations, so a cache left by an earlier + // test or run would pollute these. Overwrite with an empty, far-away + // cache (source at (0,0)) so every test starts from a clean slate. + FuelStore.saveRoadDistances(sourceLat: 0, sourceLng: 0, entries: [:]) + } + private func station(_ id: String, _ lat: Double, _ lng: Double) -> FuelStation { FuelStation(id: id, name: id, brand: "X", address: "", postcode: "", lat: lat, lng: lng, prices: [:], priceUpdated: nil)