update
This commit is contained in:
@@ -94,6 +94,20 @@ func (tp *TripPlanner) Preprocess() error {
|
|||||||
return nil
|
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
|
// 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) {
|
||||||
|
|
||||||
@@ -105,19 +119,20 @@ func (tp *TripPlanner) FindRoute(from, to string, when time.Time) (*Route, error
|
|||||||
}
|
}
|
||||||
|
|
||||||
possibleNextStops := make([]*types.StopTime, 0)
|
possibleNextStops := make([]*types.StopTime, 0)
|
||||||
|
var startTime *types.StopTime
|
||||||
for start, stop := range fromStop.GetStopsAfter(when) {
|
for start, stop := range fromStop.GetStopsAfter(when) {
|
||||||
if stop.StopId == toStop.StopId {
|
if stop.StopId == toStop.StopId {
|
||||||
return &Route{
|
return &Route{
|
||||||
Legs: []Leg{NewLeg(start, stop)},
|
Legs: []Leg{NewLeg(start, stop)},
|
||||||
}, nil
|
}, nil
|
||||||
} else {
|
} else if from != stop.StopId {
|
||||||
possibleNextStops = append(possibleNextStops, start)
|
startTime = start
|
||||||
|
possibleNextStops = append(possibleNextStops, stop)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
slices.SortFunc(possibleNextStops, byArrivalTime(*toStop))
|
slices.SortFunc(possibleNextStops, byDistanceTo(*toStop))
|
||||||
for _, nextStop := range possibleNextStops {
|
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 {
|
if err == nil && route != nil {
|
||||||
return 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")
|
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 {
|
return func(a, b *types.StopTime) int {
|
||||||
distanceA := haversine(a.Stop.StopLat, a.Stop.StopLon, end.StopLat, end.StopLon) * 1000
|
distanceA := haversine(a.Stop.StopLat, a.Stop.StopLon, end.StopLat, end.StopLon)
|
||||||
distanceB := haversine(b.Stop.StopLat, b.Stop.StopLon, end.StopLat, end.StopLon) * 1000
|
distanceB := haversine(b.Stop.StopLat, b.Stop.StopLon, end.StopLat, end.StopLon)
|
||||||
return (int(distanceA) - int(distanceB)) + (int(b.ArrivalTime - a.ArrivalTime))
|
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 {
|
if len(changes) >= maxTransfers {
|
||||||
return nil, fmt.Errorf("max transfers reached")
|
return nil, fmt.Errorf("max transfers reached")
|
||||||
}
|
}
|
||||||
|
isOk := shouldTryStop(end, changes...)
|
||||||
possibleNextStops := make([]*types.StopTime, 0)
|
possibleNextStops := make([]*types.StopTime, 0)
|
||||||
for stop := range start.Stop.GetUpcomingStops(&start) {
|
for stop := range start.Stop.GetUpcomingStops(&start) {
|
||||||
if stop.StopId == end.StopId {
|
if stop.StopId == end.StopId {
|
||||||
@@ -144,16 +178,16 @@ func (tp *TripPlanner) findRoute(start types.StopTime, end *types.Stop, changes
|
|||||||
Legs: CreateLegs(changes, stop),
|
Legs: CreateLegs(changes, stop),
|
||||||
}, nil
|
}, nil
|
||||||
} else {
|
} else {
|
||||||
if !slices.ContainsFunc(changes, func(c *types.StopTime) bool { return c.StopId == stop.StopId }) {
|
if isOk(stop) {
|
||||||
possibleNextStops = append(possibleNextStops, stop)
|
possibleNextStops = append(possibleNextStops, stop)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
slices.SortFunc(possibleNextStops, byArrivalTime(*end))
|
slices.SortFunc(possibleNextStops, byDistanceTo(*end))
|
||||||
|
|
||||||
tries := 15
|
tries := 15
|
||||||
for _, nextStop := range possibleNextStops {
|
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 {
|
if err == nil && route != nil {
|
||||||
return 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")
|
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)
|
legs := make([]Leg, 0, len(stops)+1)
|
||||||
var previousStop *types.StopTime
|
var previousStop *types.StopTime
|
||||||
for _, stop := range stops {
|
for _, stop := range stops {
|
||||||
if previousStop != nil {
|
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))
|
legs = append(legs, NewLeg(previousStop, finalStop))
|
||||||
return legs
|
return legs
|
||||||
|
|||||||
@@ -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 fetcher = (url) => fetch(url).then(res => res.ok ? res.json() : Promise.reject(res.statusText));
|
||||||
|
|
||||||
|
const JsonView = ({ data }) => { (
|
||||||
|
<pre className="json-view">{JSON.stringify(data, null, 2)}</pre>
|
||||||
|
); };
|
||||||
|
|
||||||
function App() {
|
function App() {
|
||||||
const { data: stops = [], error: stopsError } = useSWR('/api/stops', fetcher);
|
const { data: stops = [], error: stopsError } = useSWR('/api/stops', fetcher);
|
||||||
const [from, setFrom] = useState({
|
const [from, setFrom] = useState({
|
||||||
@@ -133,7 +137,7 @@ function App() {
|
|||||||
<h2 className="route-title">
|
<h2 className="route-title">
|
||||||
Routes from {from.name} to {to.name}
|
Routes from {from.name} to {to.name}
|
||||||
</h2>
|
</h2>
|
||||||
{routes.length > 1 && (
|
{/* {routes.length > 1 && (
|
||||||
<div className="route-selector">
|
<div className="route-selector">
|
||||||
{routes.map((_, index) => (
|
{routes.map((_, index) => (
|
||||||
<button
|
<button
|
||||||
@@ -146,8 +150,9 @@ function App() {
|
|||||||
</button>
|
</button>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)} */}
|
||||||
{routes[selectedRouteIndex]?.legs &&
|
<JsonView data={routes} />
|
||||||
|
{/* {routes[selectedRouteIndex]?.legs &&
|
||||||
routes[selectedRouteIndex].legs.length > 0 && (
|
routes[selectedRouteIndex].legs.length > 0 && (
|
||||||
<div className="route">
|
<div className="route">
|
||||||
{routes[selectedRouteIndex].legs.map((leg, index) => {
|
{routes[selectedRouteIndex].legs.map((leg, index) => {
|
||||||
@@ -187,12 +192,12 @@ function App() {
|
|||||||
{stops.find((s) => s.stop_id === leg.to)?.stop_name ||
|
{stops.find((s) => s.stop_id === leg.to)?.stop_name ||
|
||||||
leg.to}
|
leg.to}
|
||||||
</p>
|
</p>
|
||||||
)} */}
|
)} }
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}*/}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package types
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"iter"
|
"iter"
|
||||||
|
"math"
|
||||||
"time"
|
"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] {
|
func (s *Stop) GetUpcomingStops(start *StopTime) iter.Seq[*StopTime] {
|
||||||
return func(yield func(*StopTime) bool) {
|
return func(yield func(*StopTime) bool) {
|
||||||
found := false
|
found := false
|
||||||
|
|||||||
Reference in New Issue
Block a user