some custom stuff
This commit is contained in:
261
cmd/planner/astar.go
Normal file
261
cmd/planner/astar.go
Normal file
@@ -0,0 +1,261 @@
|
||||
package main
|
||||
|
||||
// import (
|
||||
// "container/heap"
|
||||
// "time"
|
||||
|
||||
// "git.tornberg.me/go-gtfs/pkg/types"
|
||||
// )
|
||||
|
||||
// // CostFactors defines the parameters for the cost function in A* search.
|
||||
// type CostFactors struct {
|
||||
// TransferPenalty time.Duration
|
||||
// MaxTransfers int
|
||||
// MaxWaitBetweenTrips time.Duration
|
||||
// MaxTravelDuration time.Duration
|
||||
// MaxDetourFactor float64
|
||||
// }
|
||||
|
||||
// // Heuristic function estimates the cost from a node to the goal.
|
||||
// // For pathfinding on a map, this is typically the straight-line distance.
|
||||
// type Heuristic func(from, to *types.Stop) time.Duration
|
||||
|
||||
// // AStarPlanner uses the A* algorithm to find routes.
|
||||
// type AStarPlanner struct {
|
||||
// *TripPlanner
|
||||
// CostFactors CostFactors
|
||||
// Heuristic Heuristic
|
||||
// }
|
||||
|
||||
// // NewAStarPlanner creates a new planner that uses the A* algorithm.
|
||||
// func NewAStarPlanner(tp *TripPlanner, factors CostFactors, heuristic Heuristic) *AStarPlanner {
|
||||
// return &AStarPlanner{
|
||||
// TripPlanner: tp,
|
||||
// CostFactors: factors,
|
||||
// Heuristic: heuristic,
|
||||
// }
|
||||
// }
|
||||
|
||||
// // findRoute implements a time-aware A* algorithm for routing.
|
||||
// func (p *AStarPlanner) findRoute(start, end string, when time.Time) *Route {
|
||||
// startStop := p.GetStop(start)
|
||||
// if startStop == nil {
|
||||
// return nil
|
||||
// }
|
||||
// goalStop := p.GetStop(end)
|
||||
// if goalStop == nil {
|
||||
// return nil
|
||||
// }
|
||||
// maxAllowedDistance := haversine(startStop.StopLat, startStop.StopLon, goalStop.StopLat, goalStop.StopLon) * p.CostFactors.MaxDetourFactor
|
||||
|
||||
// arrival := make(map[string]time.Time)
|
||||
// cost := make(map[string]time.Time)
|
||||
// prev := make(map[string]PathInfo)
|
||||
// pq := &priorityQueue{}
|
||||
// heap.Init(pq)
|
||||
|
||||
// arrival[start] = when
|
||||
// cost[start] = when
|
||||
// prev[start] = PathInfo{Prev: "", TripID: "", DepartureTime: when, Transfers: 0, LastTrip: "", WaitDuration: 0}
|
||||
// heap.Push(pq, &pqItem{Stop: start, Cost: when})
|
||||
|
||||
// for pq.Len() > 0 {
|
||||
// item := heap.Pop(pq).(*pqItem)
|
||||
// current := item.Stop
|
||||
// if storedCost, ok := cost[current]; !ok || item.Cost.After(storedCost) {
|
||||
// continue
|
||||
// }
|
||||
|
||||
// currentArrival, ok := arrival[current]
|
||||
// if !ok || currentArrival.IsZero() {
|
||||
// continue
|
||||
// }
|
||||
|
||||
// if current == end {
|
||||
// // Reconstruct path
|
||||
// return reconstructPath(p.TripPlanner, prev, start, end, arrival)
|
||||
// }
|
||||
|
||||
// currentStop := p.GetStop(current)
|
||||
// if currentStop == nil {
|
||||
// continue
|
||||
// }
|
||||
|
||||
// for _, edge := range p.graph[current] {
|
||||
// if edge.To == current {
|
||||
// continue
|
||||
// }
|
||||
// if info, ok := prev[current]; ok && info.Prev == edge.To && info.TripID == edge.TripID {
|
||||
// continue
|
||||
// }
|
||||
// nextStop := p.GetStop(edge.To)
|
||||
// if nextStop == nil {
|
||||
// continue
|
||||
// }
|
||||
|
||||
// distanceToGoal := haversine(nextStop.StopLat, nextStop.StopLon, goalStop.StopLat, goalStop.StopLon)
|
||||
// if distanceToGoal > maxAllowedDistance {
|
||||
// continue
|
||||
// }
|
||||
|
||||
// var arrivalTime time.Time
|
||||
// var departureTime time.Time
|
||||
// var waitDuration time.Duration
|
||||
|
||||
// if edge.TripID == "transfer" {
|
||||
// waitDuration = time.Duration(edge.Time) * time.Second
|
||||
// if waitDuration > p.CostFactors.MaxWaitBetweenTrips {
|
||||
// continue
|
||||
// }
|
||||
// arrivalTime = currentArrival.Add(waitDuration)
|
||||
// departureTime = arrivalTime
|
||||
// } else {
|
||||
// depSec := edge.DepartureTime
|
||||
// day := currentArrival.Truncate(24 * time.Hour)
|
||||
// departure := day.Add(time.Duration(depSec) * time.Second)
|
||||
// if departure.Before(currentArrival) {
|
||||
// departure = departure.Add(24 * time.Hour)
|
||||
// }
|
||||
// if departure.After(currentArrival) || departure.Equal(currentArrival) {
|
||||
// arrivalTime = departure.Add(time.Duration(edge.Time) * time.Second)
|
||||
// departureTime = departure
|
||||
// waitDuration = departureTime.Sub(currentArrival)
|
||||
// if waitDuration > p.CostFactors.MaxWaitBetweenTrips {
|
||||
// continue
|
||||
// }
|
||||
// } else {
|
||||
// continue
|
||||
// }
|
||||
// }
|
||||
|
||||
// if arrivalTime.Sub(when) > p.CostFactors.MaxTravelDuration {
|
||||
// continue
|
||||
// }
|
||||
|
||||
// currentTransfers := prev[current].Transfers
|
||||
// lastTrip := prev[current].LastTrip
|
||||
// newTransfers := currentTransfers
|
||||
// var newLastTrip string
|
||||
// if edge.TripID == "transfer" {
|
||||
// newLastTrip = lastTrip
|
||||
// } else {
|
||||
// newLastTrip = edge.TripID
|
||||
// if lastTrip != "" && lastTrip != edge.TripID {
|
||||
// newTransfers++
|
||||
// }
|
||||
// }
|
||||
// if newTransfers > p.CostFactors.MaxTransfers {
|
||||
// continue
|
||||
// }
|
||||
|
||||
// // A* cost calculation: g(n) + h(n)
|
||||
// // g(n) is the actual cost from the start, which is the arrival time with penalties.
|
||||
// gCost := arrivalTime
|
||||
// if edge.TripID != "transfer" && lastTrip != "" && lastTrip != edge.TripID {
|
||||
// gCost = gCost.Add(p.CostFactors.TransferPenalty)
|
||||
// }
|
||||
|
||||
// // h(n) is the heuristic cost from the current node to the goal.
|
||||
// hCost := p.Heuristic(nextStop, goalStop)
|
||||
// fCost := gCost.Add(hCost)
|
||||
|
||||
// existingCost, hasCost := cost[edge.To]
|
||||
// existingArrival := arrival[edge.To]
|
||||
// existingInfo, havePrev := prev[edge.To]
|
||||
// shouldRelax := !hasCost || existingCost.IsZero() || fCost.Before(existingCost)
|
||||
// if !shouldRelax && fCost.Equal(existingCost) {
|
||||
// if existingArrival.IsZero() || arrivalTime.Before(existingArrival) {
|
||||
// shouldRelax = true
|
||||
// } else if havePrev && arrivalTime.Equal(existingArrival) {
|
||||
// if newTransfers < existingInfo.Transfers {
|
||||
// shouldRelax = true
|
||||
// } else if waitDuration < existingInfo.WaitDuration {
|
||||
// shouldRelax = true
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// if shouldRelax {
|
||||
// ancestor := current
|
||||
// createsCycle := false
|
||||
// for ancestor != "" {
|
||||
// if ancestor == edge.To {
|
||||
// createsCycle = true
|
||||
// break
|
||||
// }
|
||||
// info, ok := prev[ancestor]
|
||||
// if !ok {
|
||||
// break
|
||||
// }
|
||||
// ancestor = info.Prev
|
||||
// }
|
||||
// if createsCycle {
|
||||
// continue
|
||||
// }
|
||||
// arrival[edge.To] = arrivalTime
|
||||
// cost[edge.To] = fCost
|
||||
// prev[edge.To] = PathInfo{Prev: current, TripID: edge.TripID, DepartureTime: departureTime, Transfers: newTransfers, LastTrip: newLastTrip, WaitDuration: waitDuration}
|
||||
// heap.Push(pq, &pqItem{Stop: edge.To, Cost: fCost})
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// return nil
|
||||
// }
|
||||
|
||||
// func reconstructPath(tp *TripPlanner, prev map[string]PathInfo, start, end string, arrival map[string]time.Time) *Route {
|
||||
// stopsPath := []string{}
|
||||
// current := end
|
||||
// visited := make(map[string]bool)
|
||||
// for current != "" {
|
||||
// if visited[current] {
|
||||
// break
|
||||
// }
|
||||
// visited[current] = true
|
||||
// stopsPath = append([]string{current}, stopsPath...)
|
||||
// if current == start {
|
||||
// break
|
||||
// }
|
||||
// info, ok := prev[current]
|
||||
// if !ok || info.Prev == "" {
|
||||
// break
|
||||
// }
|
||||
// current = info.Prev
|
||||
// }
|
||||
// legs := []Leg{}
|
||||
// var currentLeg *Leg
|
||||
// for i := 0; i < len(stopsPath)-1; i++ {
|
||||
// from := stopsPath[i]
|
||||
// to := stopsPath[i+1]
|
||||
// tripID := prev[to].TripID
|
||||
// if tripID != "transfer" {
|
||||
// if currentLeg == nil || currentLeg.TripID != tripID {
|
||||
// if currentLeg != nil {
|
||||
// legs = append(legs, *currentLeg)
|
||||
// }
|
||||
// trip := tp.GetTrip(tripID)
|
||||
// route := tp.GetRoute(trip.RouteId)
|
||||
// currentLeg = &Leg{
|
||||
// TripID: tripID,
|
||||
// From: from,
|
||||
// FromStop: tp.GetStop(from),
|
||||
// Trip: trip,
|
||||
// Agency: tp.GetAgency(route.AgencyID),
|
||||
// Route: route,
|
||||
// Stops: []string{from},
|
||||
// DepartureTime: prev[to].DepartureTime,
|
||||
// }
|
||||
// }
|
||||
// currentLeg.To = to
|
||||
// currentLeg.ToStop = tp.GetStop(to)
|
||||
// currentLeg.Stops = append(currentLeg.Stops, to)
|
||||
// currentLeg.ArrivalTime = arrival[to]
|
||||
// }
|
||||
// }
|
||||
// if currentLeg != nil {
|
||||
// currentLeg.To = stopsPath[len(stopsPath)-1]
|
||||
// currentLeg.ToStop = tp.GetStop(currentLeg.To)
|
||||
// currentLeg.ArrivalTime = arrival[stopsPath[len(stopsPath)-1]]
|
||||
// legs = append(legs, *currentLeg)
|
||||
// }
|
||||
|
||||
// return &Route{Legs: legs}
|
||||
// }
|
||||
Reference in New Issue
Block a user