feat: improve planning engine travel handling, itinerary reordering, and scenario planners
Add TravelInfo initializers and city normalization helpers to fix repeat city-pair disambiguation. Improve drag-and-drop reordering with segment index tracking and source-row-aware zone calculation. Enhance all five scenario planners with better next-day departure handling and travel segment placement. Add comprehensive tests across all planners. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -42,6 +42,51 @@ struct TravelInfo: Codable, Hashable {
|
||||
var distanceMeters: Double?
|
||||
var durationSeconds: Double?
|
||||
|
||||
init(
|
||||
fromCity: String,
|
||||
toCity: String,
|
||||
segmentIndex: Int? = nil,
|
||||
distanceMeters: Double? = nil,
|
||||
durationSeconds: Double? = nil
|
||||
) {
|
||||
self.fromCity = fromCity.trimmingCharacters(in: .whitespacesAndNewlines)
|
||||
self.toCity = toCity.trimmingCharacters(in: .whitespacesAndNewlines)
|
||||
self.segmentIndex = segmentIndex
|
||||
self.distanceMeters = distanceMeters
|
||||
self.durationSeconds = durationSeconds
|
||||
}
|
||||
|
||||
init(
|
||||
segment: TravelSegment,
|
||||
segmentIndex: Int? = nil,
|
||||
distanceMeters: Double? = nil,
|
||||
durationSeconds: Double? = nil
|
||||
) {
|
||||
self.init(
|
||||
fromCity: segment.fromLocation.name,
|
||||
toCity: segment.toLocation.name,
|
||||
segmentIndex: segmentIndex,
|
||||
distanceMeters: distanceMeters ?? segment.distanceMeters,
|
||||
durationSeconds: durationSeconds ?? segment.durationSeconds
|
||||
)
|
||||
}
|
||||
|
||||
static func normalizeCityName(_ city: String) -> String {
|
||||
city.lowercased().trimmingCharacters(in: .whitespacesAndNewlines)
|
||||
}
|
||||
|
||||
var normalizedFromCity: String { Self.normalizeCityName(fromCity) }
|
||||
var normalizedToCity: String { Self.normalizeCityName(toCity) }
|
||||
|
||||
func matches(from: String, to: String) -> Bool {
|
||||
normalizedFromCity == Self.normalizeCityName(from)
|
||||
&& normalizedToCity == Self.normalizeCityName(to)
|
||||
}
|
||||
|
||||
func matches(segment: TravelSegment) -> Bool {
|
||||
matches(from: segment.fromLocation.name, to: segment.toLocation.name)
|
||||
}
|
||||
|
||||
var formattedDistance: String {
|
||||
guard let meters = distanceMeters else { return "" }
|
||||
let miles = meters / 1609.34
|
||||
|
||||
Reference in New Issue
Block a user