This commit is contained in:
2025-11-15 17:53:50 +01:00
parent 1bebded484
commit 5dc296805a
15 changed files with 634 additions and 688 deletions

View File

@@ -4,7 +4,6 @@ import (
"fmt"
"log"
"slices"
"sort"
"time"
"git.tornberg.me/go-gtfs/pkg/reader"
@@ -41,41 +40,41 @@ const (
func NewTripPlanner(data *reader.TripData) *TripPlanner {
return &TripPlanner{
TripData: data,
graph: make(map[string][]Edge),
//graph: make(map[string][]Edge),
}
}
// Preprocess builds the routing graph and precomputes routes
func (tp *TripPlanner) Preprocess() error {
if hosjo, ok := tp.Stops["740025287"]; ok {
trips := hosjo.GetTripsAfter(time.Now())
for trip := range trips {
log.Printf("Trip %s (%s):", trip.TripShortName, trip.TripHeadsign)
for stop := range trip.GetDirectPossibleDestinations(hosjo, time.Now()) {
log.Printf("- Stop %s at %s", stop.Stop.StopName, types.AsTime(stop.DepartureTime))
}
}
// if hosjo, ok := tp.Stops["740025287"]; ok {
// trips := hosjo.GetTripsAfter(time.Now())
// for trip := range trips {
// log.Printf("Trip %s (%s):", trip.TripShortName, trip.TripHeadsign)
// for stop := range trip.GetDirectPossibleDestinations(hosjo, time.Now()) {
// log.Printf("- Stop %s at %s", stop.Stop.StopName, types.AsTime(stop.DepartureTime))
// }
// }
}
// }
// Build graph with trip edges
for tripID, trip := range tp.Trips {
sts := trip.Stops
sort.Slice(sts, func(i, j int) bool {
return sts[i].StopSequence < sts[j].StopSequence
})
for i := 0; i < len(sts)-1; i++ {
from := sts[i].StopId
to := sts[i+1].StopId
departure := sts[i].DepartureTime
arrival := sts[i+1].DepartureTime
timeDiff := arrival - departure
if timeDiff > 0 {
tp.graph[from] = append(tp.graph[from], Edge{To: to, TripID: tripID, Time: timeDiff, DepartureTime: departure})
}
}
}
// // Build graph with trip edges
// for tripID, trip := range tp.Trips {
// sts := trip.Stops
// sort.Slice(sts, func(i, j int) bool {
// return sts[i].StopSequence < sts[j].StopSequence
// })
// for i := 0; i < len(sts)-1; i++ {
// from := sts[i].StopId
// to := sts[i+1].StopId
// departure := sts[i].DepartureTime
// arrival := sts[i+1].DepartureTime
// timeDiff := arrival - departure
// if timeDiff > 0 {
// tp.graph[from] = append(tp.graph[from], Edge{To: to, TripID: tripID, Time: timeDiff, DepartureTime: departure})
// }
// }
// }
// // Add transfer edges
// for _, tr := range tp.transfers {
@@ -100,8 +99,8 @@ type History struct {
TravelTime types.SecondsAfterMidnight
}
func NewHistory(st *types.StopTime, distanceToEnd float64, travelTime types.SecondsAfterMidnight) *History {
return &History{
func NewHistory(st *types.StopTime, distanceToEnd float64, travelTime types.SecondsAfterMidnight) History {
return History{
StopTime: st,
DistanceToEnd: distanceToEnd,
TravelTime: travelTime,
@@ -109,7 +108,7 @@ func NewHistory(st *types.StopTime, distanceToEnd float64, travelTime types.Seco
}
// FindRoutes finds the best routes (up to num) between two stops starting at the given time
func (tp *TripPlanner) FindRoute(from, to string, when time.Time) (*Route, error) {
func (tp *TripPlanner) FindRoute(from, to string, when time.Time) ([]*Route, error) {
fromStop := tp.GetStop(from)
toStop := tp.GetStop(to)
@@ -117,28 +116,61 @@ func (tp *TripPlanner) FindRoute(from, to string, when time.Time) (*Route, error
if fromStop == nil || toStop == nil {
return nil, fmt.Errorf("invalid from or to stop")
}
routes := make([]*Route, 0)
usedRouteIds := make(map[string]struct{})
for trip := range fromStop.GetTripsAfter(when) {
if _, used := usedRouteIds[trip.RouteId]; used {
continue
}
for i := len(trip.Stops) - 1; i >= 0; i-- {
stop := trip.Stops[i]
if stop.StopId == toStop.StopId {
usedRouteIds[trip.RouteId] = struct{}{}
routes = append(routes, &Route{
Legs: []Leg{NewLeg(trip.Stops[0], trip.Stops[i])},
})
break
} else if stop.StopId == fromStop.StopId {
break
} else if stop.PickupType == 0 {
distance := stop.Stop.HaversineDistance(toStop)
}
}
}
possibleNextStops := make([]*types.StopTime, 0)
var startTime *types.StopTime
for start, stop := range fromStop.GetStopsAfter(when) {
if stop.StopId == toStop.StopId {
return &Route{
routes = append(routes, &Route{
Legs: []Leg{NewLeg(start, stop)},
}, nil
})
} else if from != stop.StopId {
startTime = start
possibleNextStops = append(possibleNextStops, stop)
}
}
slices.SortFunc(possibleNextStops, byDistanceTo(*toStop))
for _, nextStop := range possibleNextStops {
route, err := tp.findRoute(*nextStop, toStop, *NewHistory(startTime, startTime.Stop.HaversineDistance(toStop), types.AsSecondsAfterMidnight(when)), *NewHistory(nextStop, nextStop.Stop.HaversineDistance(toStop), nextStop.ArrivalTime-startTime.ArrivalTime))
if err == nil && route != nil {
return route, nil
}
}
// startTime = start
route, err := tp.findRoute(*start, toStop, NewHistory(start, start.Stop.HaversineDistance(toStop), 0), NewHistory(stop, stop.Stop.HaversineDistance(toStop), stop.ArrivalTime-start.DepartureTime))
return nil, fmt.Errorf("no route found")
if err == nil && route != nil {
routes = append(routes, route)
}
}
}
// slices.SortFunc(possibleNextStops, byDistanceTo(*toStop))
// for _, nextStop := range possibleNextStops {
// route, err := tp.findRoute(*nextStop, toStop, *NewHistory(startTime, startTime.Stop.HaversineDistance(toStop), types.AsSecondsAfterMidnight(when)), *NewHistory(nextStop, nextStop.Stop.HaversineDistance(toStop), nextStop.ArrivalTime-startTime.ArrivalTime))
// if err == nil && route != nil {
// return route, nil
// }
// }
slices.SortFunc(routes, func(a, b *Route) int {
transfersA := len(a.Legs) - 1
transfersB := len(b.Legs) - 1
if transfersA != transfersB {
return transfersA - transfersB
}
return a.Duration() - b.Duration() - (transfersA-transfersB)*int(transferPenalty.Seconds())
})
return routes[:min(len(routes), 10)], nil
}
func byDistanceTo(end types.Stop) func(a, b *types.StopTime) int {
@@ -149,10 +181,38 @@ func byDistanceTo(end types.Stop) func(a, b *types.StopTime) int {
}
}
func isInCorrectDirection(from, possible, end *types.Stop) bool {
if from.StopId == end.StopId || possible.StopId == end.StopId {
return true
}
if from.StopId == possible.StopId {
return false
}
startToEndLat := end.StopLat - from.StopLat
startToEndLon := end.StopLon - from.StopLon
startToPossibleLat := possible.StopLat - from.StopLat
startToPossibleLon := possible.StopLon - from.StopLon
dotProduct := startToEndLat*startToPossibleLat + startToEndLon*startToPossibleLon
return dotProduct > -0.4 && dotProduct < 0.4
}
func shouldTryStop(end *types.Stop, visited ...History) func(possible *types.StopTime) bool {
lastDistance := visited[len(visited)-1].Stop.HaversineDistance(end)
return func(possible *types.StopTime) bool {
if end.StopId == possible.StopId {
return true
}
if possible.DepartureTime > visited[len(visited)-1].DepartureTime+types.SecondsAfterMidnight(maxWaitBetweenTrips.Seconds()) {
return false
}
if possible.DropOffType == 1 {
return false
}
// if !isInCorrectDirection(visited[len(visited)-1].Stop, possible.Stop, end) {
// return false
// }
distance := possible.Stop.HaversineDistance(end)
for _, v := range visited {
if v.DistanceToEnd <= distance*1.2 {
return false
@@ -187,7 +247,7 @@ func (tp *TripPlanner) findRoute(start types.StopTime, end *types.Stop, changes
tries := 15
for _, nextStop := range possibleNextStops {
route, err := tp.findRoute(*nextStop, end, append(changes, *NewHistory(nextStop, nextStop.Stop.HaversineDistance(end), nextStop.ArrivalTime-start.ArrivalTime))...)
route, err := tp.findRoute(*nextStop, end, append(changes, NewHistory(nextStop, nextStop.Stop.HaversineDistance(end), nextStop.ArrivalTime-start.ArrivalTime))...)
if err == nil && route != nil {
return route, nil
}
@@ -213,9 +273,24 @@ func CreateLegs(stops []History, finalStop *types.StopTime) []Leg {
}
func NewLeg(fromStop, toStop *types.StopTime) Leg {
trip, ok := toStop.Stop.Trips[toStop.TripId]
if !ok {
log.Printf("trip %s not found for stop %s", toStop.TripId, toStop.Stop.StopName)
return Leg{
From: fromStop,
To: toStop,
}
}
return Leg{
From: fromStop,
To: toStop,
Trip: &JSONTrip{
TripId: trip.TripId,
RouteId: trip.RouteID,
AgencyName: trip.Agency.AgencyName,
TripHeadsign: trip.TripHeadsign,
TripShortName: trip.TripShortName,
},
}
}