more
This commit is contained in:
@@ -13,7 +13,6 @@ import (
|
|||||||
// TripPlanner handles preprocessed transit data for efficient routing
|
// TripPlanner handles preprocessed transit data for efficient routing
|
||||||
type TripPlanner struct {
|
type TripPlanner struct {
|
||||||
*reader.TripData
|
*reader.TripData
|
||||||
graph map[string][]Edge
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@@ -22,6 +21,8 @@ const (
|
|||||||
maxWaitBetweenTrips = 1 * time.Hour
|
maxWaitBetweenTrips = 1 * time.Hour
|
||||||
//trajectoryAngleTolerance = 220.0
|
//trajectoryAngleTolerance = 220.0
|
||||||
maxTravelDuration = 12 * time.Hour
|
maxTravelDuration = 12 * time.Hour
|
||||||
|
maxInitialWait = 90 * time.Minute
|
||||||
|
searchWindow = 4 * time.Hour
|
||||||
)
|
)
|
||||||
|
|
||||||
// NewTripPlanner creates a new trip planner instance
|
// NewTripPlanner creates a new trip planner instance
|
||||||
@@ -115,18 +116,51 @@ func tripStopIndex(trip *types.Trip, st *types.StopTime) int {
|
|||||||
|
|
||||||
// FindRoutes finds the best routes (up to num) between two stops starting at the given time
|
// 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)
|
fromStop := tp.GetStop(from)
|
||||||
toStop := tp.GetStop(to)
|
toStop := tp.GetStop(to)
|
||||||
|
|
||||||
if fromStop == nil || toStop == nil {
|
if fromStop == nil || toStop == nil {
|
||||||
return nil, fmt.Errorf("invalid from or to stop")
|
return nil, fmt.Errorf("invalid from or to stop")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
exclude := make(map[string]struct{})
|
||||||
|
results := make([]*Route, 0, 3)
|
||||||
|
timeCursor := when
|
||||||
|
const maxAttempts = 5
|
||||||
|
var lastErr error
|
||||||
|
|
||||||
|
for attempts := 0; attempts < maxAttempts && len(results) < 3; attempts++ {
|
||||||
|
limit := 3 - len(results)
|
||||||
|
routes, err := tp.searchRoutesAStar(fromStop, toStop, timeCursor, exclude, limit)
|
||||||
|
if err == nil {
|
||||||
|
results = append(results, routes...)
|
||||||
|
} else {
|
||||||
|
lastErr = err
|
||||||
|
}
|
||||||
|
timeCursor = timeCursor.Add(10 * time.Minute)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(results) == 0 {
|
||||||
|
if lastErr != nil {
|
||||||
|
return nil, lastErr
|
||||||
|
}
|
||||||
|
return nil, fmt.Errorf("no route found")
|
||||||
|
}
|
||||||
|
|
||||||
|
return results, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tp *TripPlanner) searchRoutesAStar(fromStop, toStop *types.Stop, when time.Time, exclude map[string]struct{}, limit int) ([]*Route, error) {
|
||||||
|
if limit <= 0 {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
open := priorityQueue{}
|
open := priorityQueue{}
|
||||||
heap.Init(&open)
|
heap.Init(&open)
|
||||||
bestCost := make(map[string]float64)
|
bestCost := make(map[string]float64)
|
||||||
startSeconds := types.AsSecondsAfterMidnight(when)
|
startSeconds := types.AsSecondsAfterMidnight(when)
|
||||||
|
startSecondsFloat := float64(startSeconds)
|
||||||
initCount := 0
|
initCount := 0
|
||||||
|
initialWaitLimit := maxInitialWait.Seconds()
|
||||||
|
searchWindowLimit := searchWindow.Seconds()
|
||||||
|
|
||||||
for tripWithDeparture := range fromStop.GetTripsAfter(when) {
|
for tripWithDeparture := range fromStop.GetTripsAfter(when) {
|
||||||
trip := tripWithDeparture.Trip
|
trip := tripWithDeparture.Trip
|
||||||
@@ -138,10 +172,17 @@ func (tp *TripPlanner) FindRoute(from, to string, when time.Time) ([]*Route, err
|
|||||||
if idx < 0 {
|
if idx < 0 {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
wait := float64(startStopTime.DepartureTime - startSeconds)
|
departureDelta := float64(startStopTime.DepartureTime) - startSecondsFloat
|
||||||
|
if searchWindow > 0 && departureDelta > searchWindowLimit {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
wait := departureDelta
|
||||||
if wait < 0 {
|
if wait < 0 {
|
||||||
wait = 0
|
wait = 0
|
||||||
}
|
}
|
||||||
|
if maxInitialWait > 0 && wait > initialWaitLimit {
|
||||||
|
continue
|
||||||
|
}
|
||||||
node := &searchNode{
|
node := &searchNode{
|
||||||
stopTime: startStopTime,
|
stopTime: startStopTime,
|
||||||
index: idx,
|
index: idx,
|
||||||
@@ -155,23 +196,41 @@ func (tp *TripPlanner) FindRoute(from, to string, when time.Time) ([]*Route, err
|
|||||||
}
|
}
|
||||||
|
|
||||||
if initCount == 0 {
|
if initCount == 0 {
|
||||||
return nil, fmt.Errorf("no departures from %s after %s", from, when.Format(time.RFC3339))
|
return nil, fmt.Errorf("no departures from %s after %s", fromStop.StopName, when.Format(time.RFC3339))
|
||||||
}
|
}
|
||||||
|
|
||||||
routes := make([]*Route, 0, 3)
|
routes := make([]*Route, 0, limit)
|
||||||
|
|
||||||
for open.Len() > 0 && len(routes) < 3 {
|
for open.Len() > 0 && len(routes) < limit {
|
||||||
current := heap.Pop(&open).(*pqItem).node
|
current := heap.Pop(&open).(*pqItem).node
|
||||||
if current.stopTime.StopId == toStop.StopId {
|
if current.stopTime.StopId == toStop.StopId {
|
||||||
route := buildRouteFromNode(current)
|
route := buildRouteFromNode(current)
|
||||||
if route != nil {
|
if route != nil {
|
||||||
|
signature := routeSignature(route)
|
||||||
|
if signature == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if _, seen := exclude[signature]; seen {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
exclude[signature] = struct{}{}
|
||||||
routes = append(routes, route)
|
routes = append(routes, route)
|
||||||
|
if len(routes) >= limit {
|
||||||
|
break
|
||||||
|
}
|
||||||
}
|
}
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if current.g > maxTravelDuration.Seconds() {
|
if current.g > maxTravelDuration.Seconds() {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
if searchWindow > 0 {
|
||||||
|
currentDelta := float64(current.stopTime.DepartureTime) - startSecondsFloat
|
||||||
|
if currentDelta > searchWindowLimit {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
tpp := tp.Trips[current.stopTime.TripId]
|
tpp := tp.Trips[current.stopTime.TripId]
|
||||||
if tpp != nil {
|
if tpp != nil {
|
||||||
for i := current.index + 1; i < len(tpp.Stops); i++ {
|
for i := current.index + 1; i < len(tpp.Stops); i++ {
|
||||||
@@ -187,6 +246,12 @@ func (tp *TripPlanner) FindRoute(from, to string, when time.Time) ([]*Route, err
|
|||||||
if newCost > maxTravelDuration.Seconds() {
|
if newCost > maxTravelDuration.Seconds() {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
if searchWindow > 0 {
|
||||||
|
nextDepartureDelta := float64(next.DepartureTime) - startSecondsFloat
|
||||||
|
if nextDepartureDelta > searchWindowLimit {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
key := nodeKey(next)
|
key := nodeKey(next)
|
||||||
if prev, ok := bestCost[key]; ok && newCost >= prev {
|
if prev, ok := bestCost[key]; ok && newCost >= prev {
|
||||||
continue
|
continue
|
||||||
@@ -225,6 +290,12 @@ func (tp *TripPlanner) FindRoute(from, to string, when time.Time) ([]*Route, err
|
|||||||
if newCost > maxTravelDuration.Seconds() {
|
if newCost > maxTravelDuration.Seconds() {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
if searchWindow > 0 {
|
||||||
|
transferDepartureDelta := float64(transferStopTime.DepartureTime) - startSecondsFloat
|
||||||
|
if transferDepartureDelta > searchWindowLimit {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
idx := tripStopIndex(otherTrip, transferStopTime)
|
idx := tripStopIndex(otherTrip, transferStopTime)
|
||||||
if idx < 0 {
|
if idx < 0 {
|
||||||
continue
|
continue
|
||||||
@@ -281,6 +352,18 @@ func buildRouteFromNode(goal *searchNode) *Route {
|
|||||||
return &Route{Legs: legs}
|
return &Route{Legs: legs}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func routeSignature(route *Route) string {
|
||||||
|
if route == nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
if len(route.Legs) == 0 {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
start := int(route.StartTime())
|
||||||
|
duration := route.Duration()
|
||||||
|
return fmt.Sprintf("%d:%d", start, duration)
|
||||||
|
}
|
||||||
|
|
||||||
func NewLeg(fromStop, toStop *types.StopTime) Leg {
|
func NewLeg(fromStop, toStop *types.StopTime) Leg {
|
||||||
trip, ok := toStop.Stop.Trips[toStop.TripId]
|
trip, ok := toStop.Stop.Trips[toStop.TripId]
|
||||||
if !ok {
|
if !ok {
|
||||||
|
|||||||
Reference in New Issue
Block a user