diff --git a/cmd/planner/planner.go b/cmd/planner/planner.go index d9a3efa..af897c6 100644 --- a/cmd/planner/planner.go +++ b/cmd/planner/planner.go @@ -94,6 +94,20 @@ func (tp *TripPlanner) Preprocess() error { return nil } +type History struct { + *types.StopTime + DistanceToEnd float64 + TravelTime types.SecondsAfterMidnight +} + +func NewHistory(st *types.StopTime, distanceToEnd float64, travelTime types.SecondsAfterMidnight) *History { + return &History{ + StopTime: st, + DistanceToEnd: distanceToEnd, + TravelTime: travelTime, + } +} + // 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) { @@ -105,19 +119,20 @@ func (tp *TripPlanner) FindRoute(from, to string, when time.Time) (*Route, error } possibleNextStops := make([]*types.StopTime, 0) - + var startTime *types.StopTime for start, stop := range fromStop.GetStopsAfter(when) { if stop.StopId == toStop.StopId { return &Route{ Legs: []Leg{NewLeg(start, stop)}, }, nil - } else { - possibleNextStops = append(possibleNextStops, start) + } else if from != stop.StopId { + startTime = start + possibleNextStops = append(possibleNextStops, stop) } } - slices.SortFunc(possibleNextStops, byArrivalTime(*toStop)) + slices.SortFunc(possibleNextStops, byDistanceTo(*toStop)) for _, nextStop := range possibleNextStops { - route, err := tp.findRoute(*nextStop, toStop, nextStop) + 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 } @@ -126,17 +141,36 @@ func (tp *TripPlanner) FindRoute(from, to string, when time.Time) (*Route, error return nil, fmt.Errorf("no route found") } -func byArrivalTime(end types.Stop) func(a, b *types.StopTime) int { +func byDistanceTo(end types.Stop) func(a, b *types.StopTime) int { return func(a, b *types.StopTime) int { - distanceA := haversine(a.Stop.StopLat, a.Stop.StopLon, end.StopLat, end.StopLon) * 1000 - distanceB := haversine(b.Stop.StopLat, b.Stop.StopLon, end.StopLat, end.StopLon) * 1000 - return (int(distanceA) - int(distanceB)) + (int(b.ArrivalTime - a.ArrivalTime)) + distanceA := haversine(a.Stop.StopLat, a.Stop.StopLon, end.StopLat, end.StopLon) + distanceB := haversine(b.Stop.StopLat, b.Stop.StopLon, end.StopLat, end.StopLon) + return (int(distanceA) - int(distanceB)) // + (int(b.ArrivalTime - a.ArrivalTime)) } } -func (tp *TripPlanner) findRoute(start types.StopTime, end *types.Stop, changes ...*types.StopTime) (*Route, error) { + +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 { + distance := possible.Stop.HaversineDistance(end) + for _, v := range visited { + if v.DistanceToEnd <= distance*1.2 { + return false + } + if v.TripId == possible.TripId || v.StopId == possible.StopId { + return false + } + } + + return distance <= lastDistance*1.2 + } +} + +func (tp *TripPlanner) findRoute(start types.StopTime, end *types.Stop, changes ...History) (*Route, error) { if len(changes) >= maxTransfers { return nil, fmt.Errorf("max transfers reached") } + isOk := shouldTryStop(end, changes...) possibleNextStops := make([]*types.StopTime, 0) for stop := range start.Stop.GetUpcomingStops(&start) { if stop.StopId == end.StopId { @@ -144,16 +178,16 @@ func (tp *TripPlanner) findRoute(start types.StopTime, end *types.Stop, changes Legs: CreateLegs(changes, stop), }, nil } else { - if !slices.ContainsFunc(changes, func(c *types.StopTime) bool { return c.StopId == stop.StopId }) { + if isOk(stop) { possibleNextStops = append(possibleNextStops, stop) } } } - slices.SortFunc(possibleNextStops, byArrivalTime(*end)) + slices.SortFunc(possibleNextStops, byDistanceTo(*end)) tries := 15 for _, nextStop := range possibleNextStops { - route, err := tp.findRoute(*nextStop, end, append(changes, nextStop)...) + 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 } @@ -165,14 +199,14 @@ func (tp *TripPlanner) findRoute(start types.StopTime, end *types.Stop, changes return nil, fmt.Errorf("no route found") } -func CreateLegs(stops []*types.StopTime, finalStop *types.StopTime) []Leg { +func CreateLegs(stops []History, finalStop *types.StopTime) []Leg { legs := make([]Leg, 0, len(stops)+1) var previousStop *types.StopTime for _, stop := range stops { if previousStop != nil { - legs = append(legs, NewLeg(previousStop, stop)) + legs = append(legs, NewLeg(previousStop, stop.StopTime)) } - previousStop = stop + previousStop = stop.StopTime } legs = append(legs, NewLeg(previousStop, finalStop)) return legs diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx index de4b082..c2c4573 100644 --- a/frontend/src/App.jsx +++ b/frontend/src/App.jsx @@ -49,6 +49,10 @@ function StopSelector({ stops, onChange, placeholder, inputId }) { const fetcher = (url) => fetch(url).then(res => res.ok ? res.json() : Promise.reject(res.statusText)); +const JsonView = ({ data }) => { ( +
{JSON.stringify(data, null, 2)}
+); }; + function App() { const { data: stops = [], error: stopsError } = useSWR('/api/stops', fetcher); const [from, setFrom] = useState({ @@ -133,7 +137,7 @@ function App() {

Routes from {from.name} to {to.name}

- {routes.length > 1 && ( + {/* {routes.length > 1 && (
{routes.map((_, index) => (
- )} - {routes[selectedRouteIndex]?.legs && + )} */} + + {/* {routes[selectedRouteIndex]?.legs && routes[selectedRouteIndex].legs.length > 0 && (
{routes[selectedRouteIndex].legs.map((leg, index) => { @@ -187,12 +192,12 @@ function App() { {stops.find((s) => s.stop_id === leg.to)?.stop_name || leg.to}

- )} */} + )} }
); - })} + })} - )} + )}*/} )} diff --git a/pkg/types/stop.go b/pkg/types/stop.go index 321f78e..bcfd5d7 100644 --- a/pkg/types/stop.go +++ b/pkg/types/stop.go @@ -2,6 +2,7 @@ package types import ( "iter" + "math" "time" ) @@ -51,6 +52,19 @@ func (s *Stop) GetTripsAfter(when time.Time) iter.Seq[*TripWithDepartureTime] { } } +func (s *Stop) HaversineDistance(other *Stop) float64 { + return haversine(s.StopLat, s.StopLon, other.StopLat, other.StopLon) +} + +func haversine(lat1, lon1, lat2, lon2 float64) float64 { + const R = 6371 // Earth radius in km + dLat := (lat2 - lat1) * math.Pi / 180 + dLon := (lon2 - lon1) * math.Pi / 180 + a := math.Sin(dLat/2)*math.Sin(dLat/2) + math.Cos(lat1*math.Pi/180)*math.Cos(lat2*math.Pi/180)*math.Sin(dLon/2)*math.Sin(dLon/2) + c := 2 * math.Atan2(math.Sqrt(a), math.Sqrt(1-a)) + return R * c +} + func (s *Stop) GetUpcomingStops(start *StopTime) iter.Seq[*StopTime] { return func(yield func(*StopTime) bool) { found := false