some custom stuff
This commit is contained in:
@@ -95,7 +95,7 @@ func ParseCalendarDates(r io.Reader, callback func(types.CalendarDate)) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func ParseFeedInfos(r io.Reader, callback func(types.FeedInfo)) error {
|
||||
func ParseFeedInfos(r io.Reader, callback func(id string, data types.FeedInfo)) error {
|
||||
reader := csv.NewReader(r)
|
||||
if _, err := reader.Read(); err != nil {
|
||||
return err
|
||||
@@ -109,13 +109,13 @@ func ParseFeedInfos(r io.Reader, callback func(types.FeedInfo)) error {
|
||||
return err
|
||||
}
|
||||
feedInfo := types.FeedInfo{
|
||||
FeedID: record[0],
|
||||
FeedPublisherName: record[1],
|
||||
FeedPublisherURL: record[2],
|
||||
FeedLang: record[3],
|
||||
FeedVersion: record[4],
|
||||
//FeedID: record[0],
|
||||
PublisherName: record[1],
|
||||
PublisherURL: record[2],
|
||||
Language: record[3],
|
||||
Version: record[4],
|
||||
}
|
||||
callback(feedInfo)
|
||||
callback(record[0], feedInfo)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -135,6 +135,7 @@ func ParseRoutes(r io.Reader, callback func(types.Route)) error {
|
||||
}
|
||||
routeType, _ := strconv.Atoi(record[4])
|
||||
route := types.Route{
|
||||
Trips: make([]*types.Trip, 0),
|
||||
RouteID: record[0],
|
||||
AgencyID: record[1],
|
||||
RouteShortName: record[2],
|
||||
@@ -147,7 +148,7 @@ func ParseRoutes(r io.Reader, callback func(types.Route)) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func ParseStopTimes(r io.Reader, callback func(types.StopTime)) error {
|
||||
func ParseStopTimes(r io.Reader, callback func(data types.StopTime)) error {
|
||||
reader := csv.NewReader(r)
|
||||
if _, err := reader.Read(); err != nil {
|
||||
return err
|
||||
@@ -164,10 +165,10 @@ func ParseStopTimes(r io.Reader, callback func(types.StopTime)) error {
|
||||
pickupType, _ := strconv.Atoi(record[5])
|
||||
dropOffType, _ := strconv.Atoi(record[6])
|
||||
stopTime := types.StopTime{
|
||||
TripID: record[0],
|
||||
ArrivalTime: record[1],
|
||||
DepartureTime: record[2],
|
||||
StopID: record[3],
|
||||
TripId: record[0],
|
||||
ArrivalTime: types.ParseTimeString(record[1]),
|
||||
DepartureTime: types.ParseTimeString(record[2]),
|
||||
StopId: record[3],
|
||||
StopSequence: stopSequence,
|
||||
PickupType: pickupType,
|
||||
DropOffType: dropOffType,
|
||||
@@ -194,7 +195,9 @@ func ParseStops(r io.Reader, callback func(types.Stop)) error {
|
||||
stopLon, _ := strconv.ParseFloat(record[3], 64)
|
||||
locationType, _ := strconv.Atoi(record[4])
|
||||
stop := types.Stop{
|
||||
StopID: record[0],
|
||||
Trips: make(map[string]*types.Trip),
|
||||
Transfers: make([]*types.Transfer, 0),
|
||||
StopId: record[0],
|
||||
StopName: record[1],
|
||||
StopLat: stopLat,
|
||||
StopLon: stopLon,
|
||||
@@ -221,12 +224,12 @@ func ParseTransfers(r io.Reader, callback func(types.Transfer)) error {
|
||||
transferType, _ := strconv.Atoi(record[2])
|
||||
minTransferTime, _ := strconv.Atoi(record[3])
|
||||
transfer := types.Transfer{
|
||||
FromStopID: record[0],
|
||||
ToStopID: record[1],
|
||||
FromStopId: record[0],
|
||||
ToStopId: record[1],
|
||||
TransferType: transferType,
|
||||
MinTransferTime: minTransferTime,
|
||||
FromTripID: record[4],
|
||||
ToTripID: record[5],
|
||||
FromTripId: record[4],
|
||||
ToTripId: record[5],
|
||||
}
|
||||
callback(transfer)
|
||||
}
|
||||
@@ -247,9 +250,9 @@ func ParseTrips(r io.Reader, callback func(types.Trip)) error {
|
||||
return err
|
||||
}
|
||||
trip := types.Trip{
|
||||
RouteID: record[0],
|
||||
ServiceID: record[1],
|
||||
TripID: record[2],
|
||||
RouteId: record[0],
|
||||
ServiceId: record[1],
|
||||
TripId: record[2],
|
||||
TripHeadsign: record[3],
|
||||
TripShortName: record[4],
|
||||
}
|
||||
|
||||
103
pkg/reader/loader.go
Normal file
103
pkg/reader/loader.go
Normal file
@@ -0,0 +1,103 @@
|
||||
package reader
|
||||
|
||||
import (
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"git.tornberg.me/go-gtfs/pkg/types"
|
||||
)
|
||||
|
||||
type TripData struct {
|
||||
Stops map[string]*types.Stop
|
||||
Trips map[string]*types.Trip
|
||||
Routes map[string]*types.Route
|
||||
Agencies map[string]*types.Agency
|
||||
}
|
||||
|
||||
func LoadTripData(path string) (*TripData, error) {
|
||||
tp := &TripData{
|
||||
Stops: make(map[string]*types.Stop),
|
||||
Trips: make(map[string]*types.Trip),
|
||||
Routes: make(map[string]*types.Route),
|
||||
Agencies: make(map[string]*types.Agency),
|
||||
}
|
||||
|
||||
files := []string{"agency", "routes", "stops", "trips", "stop_times", "transfers"}
|
||||
for _, file := range files {
|
||||
|
||||
f, err := os.Open(filepath.Join(path, file+".txt"))
|
||||
if err != nil {
|
||||
log.Fatalf("failed to open %s: %v", file, err)
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
switch file {
|
||||
case "agency":
|
||||
err = ParseAgencies(f, func(a types.Agency) {
|
||||
tp.Agencies[a.AgencyID] = &a
|
||||
})
|
||||
case "routes":
|
||||
err = ParseRoutes(f, func(r types.Route) {
|
||||
tp.Routes[r.RouteID] = &r
|
||||
if ag, ok := tp.Agencies[r.AgencyID]; ok {
|
||||
r.Agency = ag
|
||||
ag.AddRoute(&r)
|
||||
}
|
||||
})
|
||||
case "stops":
|
||||
err = ParseStops(f, func(s types.Stop) {
|
||||
tp.Stops[s.StopId] = &s
|
||||
})
|
||||
case "trips":
|
||||
err = ParseTrips(f, func(t types.Trip) {
|
||||
trip := t
|
||||
if route, ok := tp.Routes[trip.RouteId]; ok {
|
||||
trip.SetRoute(route)
|
||||
route.AddTrip(&trip)
|
||||
} else {
|
||||
log.Printf("route %s not found", trip.RouteId)
|
||||
}
|
||||
if agency, ok := tp.Agencies[trip.AgencyID]; ok {
|
||||
trip.Agency = agency
|
||||
} else {
|
||||
log.Printf("agency %s not found", trip.AgencyID)
|
||||
}
|
||||
tp.Trips[trip.TripId] = &trip
|
||||
})
|
||||
case "stop_times":
|
||||
err = ParseStopTimes(f, func(st types.StopTime) {
|
||||
stop, ok := tp.Stops[st.StopId]
|
||||
if ok {
|
||||
st.SetStop(stop)
|
||||
} else {
|
||||
log.Printf("stop %s not found", st.StopId)
|
||||
}
|
||||
trp, ok := tp.Trips[st.TripId]
|
||||
if !ok {
|
||||
log.Printf("trip %s not found", st.TripId)
|
||||
|
||||
} else {
|
||||
stop.AddTrip(trp)
|
||||
trp.AddStopTime(&st)
|
||||
}
|
||||
|
||||
})
|
||||
case "transfers":
|
||||
err = ParseTransfers(f, func(tr types.Transfer) {
|
||||
//transfers = append(transfers, tr)
|
||||
stop, ok := tp.Stops[tr.FromStopId]
|
||||
if ok {
|
||||
stop.AddTransfer(&tr)
|
||||
} else {
|
||||
log.Printf("stop %s not found for transfer", tr.FromStopId)
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
if err != nil {
|
||||
return tp, err
|
||||
}
|
||||
}
|
||||
return tp, nil
|
||||
}
|
||||
@@ -1,9 +1,9 @@
|
||||
package types
|
||||
|
||||
type FeedInfo struct {
|
||||
FeedID string `json:"feed_id" csv:"feed_id"`
|
||||
FeedPublisherName string `json:"feed_publisher_name" csv:"feed_publisher_name"`
|
||||
FeedPublisherURL string `json:"feed_publisher_url" csv:"feed_publisher_url"`
|
||||
FeedLang string `json:"feed_lang" csv:"feed_lang"`
|
||||
FeedVersion string `json:"feed_version" csv:"feed_version"`
|
||||
// FeedID string `json:"feed_id" csv:"feed_id"`
|
||||
PublisherName string `json:"feed_publisher_name" csv:"feed_publisher_name"`
|
||||
PublisherURL string `json:"feed_publisher_url" csv:"feed_publisher_url"`
|
||||
Language string `json:"feed_lang" csv:"feed_lang"`
|
||||
Version string `json:"feed_version" csv:"feed_version"`
|
||||
}
|
||||
|
||||
@@ -1,17 +1,18 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"iter"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Stop struct {
|
||||
trips map[string]*Trip
|
||||
StopID string `json:"stop_id" csv:"stop_id"`
|
||||
StopName string `json:"stop_name" csv:"stop_name"`
|
||||
StopLat float64 `json:"stop_lat" csv:"stop_lat"`
|
||||
StopLon float64 `json:"stop_lon" csv:"stop_lon"`
|
||||
LocationType int `json:"location_type" csv:"location_type"`
|
||||
Transfers []*Transfer `json:"transfers" csv:"transfers"`
|
||||
Trips map[string]*Trip `json:"-" csv:"-"`
|
||||
StopId string `json:"stop_id" csv:"stop_id"`
|
||||
StopName string `json:"stop_name" csv:"stop_name"`
|
||||
StopLat float64 `json:"stop_lat" csv:"stop_lat"`
|
||||
StopLon float64 `json:"stop_lon" csv:"stop_lon"`
|
||||
LocationType int `json:"location_type" csv:"location_type"`
|
||||
Transfers []*Transfer `json:"-" csv:"transfers"`
|
||||
}
|
||||
|
||||
func (s *Stop) AddTransfer(transfer *Transfer) {
|
||||
@@ -22,21 +23,71 @@ func (s *Stop) AddTransfer(transfer *Transfer) {
|
||||
}
|
||||
|
||||
func (s *Stop) AddTrip(trip *Trip) {
|
||||
if s.trips == nil {
|
||||
s.trips = make(map[string]*Trip)
|
||||
}
|
||||
s.trips[trip.TripID] = trip
|
||||
|
||||
s.Trips[trip.TripId] = trip
|
||||
}
|
||||
|
||||
func (s *Stop) GetTripsAfter(time time.Time) []*Trip {
|
||||
var trips []*Trip
|
||||
for _, trip := range s.trips {
|
||||
for _, stop := range trip.Stops {
|
||||
if stop.StopID == s.StopID && stop.DepartsAfter(time) {
|
||||
trips = append(trips, trip)
|
||||
break
|
||||
type TripWithDepartureTime struct {
|
||||
*Trip
|
||||
DepartureTime SecondsAfterMidnight
|
||||
}
|
||||
|
||||
func (s *Stop) GetTripsAfter(when time.Time) iter.Seq[*TripWithDepartureTime] {
|
||||
startAfterMidnight := AsSecondsAfterMidnight(when)
|
||||
return func(yield func(*TripWithDepartureTime) bool) {
|
||||
for _, trip := range s.Trips {
|
||||
|
||||
for _, stop := range trip.Stops {
|
||||
if stop.StopId == s.StopId && stop.ArrivalTime >= startAfterMidnight {
|
||||
|
||||
if !yield(&TripWithDepartureTime{Trip: trip, DepartureTime: stop.DepartureTime}) {
|
||||
return
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Stop) GetUpcomingStops(start *StopTime) iter.Seq[*StopTime] {
|
||||
return func(yield func(*StopTime) bool) {
|
||||
found := false
|
||||
for _, trip := range s.Trips {
|
||||
for _, stop := range trip.Stops {
|
||||
if !found {
|
||||
if stop.StopId == start.StopId && stop.DepartureTime >= start.ArrivalTime {
|
||||
found = true
|
||||
}
|
||||
} else {
|
||||
if !yield(stop) {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return trips
|
||||
}
|
||||
|
||||
func (s *Stop) GetStopsAfter(when time.Time) iter.Seq2[*StopTime, *StopTime] {
|
||||
startAfterMidnight := AsSecondsAfterMidnight(when)
|
||||
return func(yield func(start, stop *StopTime) bool) {
|
||||
for _, trip := range s.Trips {
|
||||
found := false
|
||||
var start *StopTime
|
||||
for _, stop := range trip.Stops {
|
||||
if stop.StopId == s.StopId && stop.ArrivalTime >= startAfterMidnight {
|
||||
found = true
|
||||
start = stop
|
||||
}
|
||||
if found {
|
||||
if !yield(start, stop) {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,24 +1,34 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"iter"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
type StopTime struct {
|
||||
departureTime int
|
||||
Stop *Stop `json:"stop"`
|
||||
TripID string `json:"trip_id" csv:"trip_id"`
|
||||
ArrivalTime string `json:"arrival_time" csv:"arrival_time"`
|
||||
DepartureTime string `json:"departure_time" csv:"departure_time"`
|
||||
StopID string `json:"stop_id" csv:"stop_id"`
|
||||
StopSequence int `json:"stop_sequence" csv:"stop_sequence"`
|
||||
PickupType int `json:"pickup_type" csv:"pickup_type"`
|
||||
DropOffType int `json:"drop_off_type" csv:"drop_off_type"`
|
||||
type SecondsAfterMidnight int
|
||||
|
||||
func AsTime(s SecondsAfterMidnight) string {
|
||||
h := int(s) / 3600
|
||||
m := (int(s) % 3600) / 60
|
||||
sec := int(s) % 60
|
||||
return fmt.Sprintf("%02d:%02d:%02d", h, m, sec)
|
||||
}
|
||||
|
||||
func parseTime(s string) int {
|
||||
type StopTime struct {
|
||||
Stop *Stop `json:"stop"`
|
||||
TripId string `json:"trip_id" csv:"trip_id"`
|
||||
ArrivalTime SecondsAfterMidnight `json:"arrival_time" csv:"arrival_time"`
|
||||
DepartureTime SecondsAfterMidnight `json:"departure_time" csv:"departure_time"`
|
||||
StopId string `json:"stop_id" csv:"stop_id"`
|
||||
StopSequence int `json:"stop_sequence" csv:"stop_sequence"`
|
||||
PickupType int `json:"pickup_type" csv:"pickup_type"`
|
||||
DropOffType int `json:"drop_off_type" csv:"drop_off_type"`
|
||||
}
|
||||
|
||||
func ParseTimeString(s string) SecondsAfterMidnight {
|
||||
parts := strings.Split(s, ":")
|
||||
if len(parts) != 3 {
|
||||
return 0
|
||||
@@ -26,37 +36,29 @@ func parseTime(s string) int {
|
||||
h, _ := strconv.Atoi(parts[0])
|
||||
m, _ := strconv.Atoi(parts[1])
|
||||
sec, _ := strconv.Atoi(parts[2])
|
||||
return h*3600 + m*60 + sec
|
||||
return SecondsAfterMidnight(h*3600 + m*60 + sec)
|
||||
}
|
||||
|
||||
func (st *StopTime) DepartTimeAsSeconds() int {
|
||||
if st.departureTime > 0 {
|
||||
return st.departureTime
|
||||
}
|
||||
if st.DepartureTime == "" {
|
||||
return 0
|
||||
}
|
||||
|
||||
st.departureTime = parseTime(st.DepartureTime)
|
||||
return st.departureTime
|
||||
func AsSecondsAfterMidnight(when time.Time) SecondsAfterMidnight {
|
||||
return SecondsAfterMidnight(when.Hour()*3600 + when.Minute()*60 + when.Second())
|
||||
}
|
||||
|
||||
func (st *StopTime) DepartsAfter(when time.Time) bool {
|
||||
secondsAfterMidnight := st.DepartTimeAsSeconds()
|
||||
return secondsAfterMidnight >= when.Hour()*3600+when.Minute()*60+when.Second()
|
||||
|
||||
return st.DepartureTime >= AsSecondsAfterMidnight(when)
|
||||
}
|
||||
|
||||
// func (st *StopTime) GetPossibleTrips() iter.Seq[*Trip] {
|
||||
// return func(yield func(*Trip) bool) {
|
||||
// for _, trip := range st.Stop.trips {
|
||||
// if trip.TripID != st.TripID {
|
||||
// if !yield(trip) {
|
||||
// return
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
func (st *StopTime) GetPossibleTrips() iter.Seq[*Trip] {
|
||||
return func(yield func(*Trip) bool) {
|
||||
for _, trip := range st.Stop.Trips {
|
||||
if trip.TripId != st.TripId {
|
||||
if !yield(trip) {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (st *StopTime) SetStop(stop *Stop) {
|
||||
st.Stop = stop
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
package types
|
||||
|
||||
type Transfer struct {
|
||||
FromStopID string `json:"from_stop_id" csv:"from_stop_id"`
|
||||
ToStopID string `json:"to_stop_id" csv:"to_stop_id"`
|
||||
FromStopId string `json:"from_stop_id" csv:"from_stop_id"`
|
||||
ToStopId string `json:"to_stop_id" csv:"to_stop_id"`
|
||||
TransferType int `json:"transfer_type" csv:"transfer_type"`
|
||||
MinTransferTime int `json:"min_transfer_time" csv:"min_transfer_time"`
|
||||
FromTripID string `json:"from_trip_id" csv:"from_trip_id"`
|
||||
ToTripID string `json:"to_trip_id" csv:"to_trip_id"`
|
||||
FromTripId string `json:"from_trip_id" csv:"from_trip_id"`
|
||||
ToTripId string `json:"to_trip_id" csv:"to_trip_id"`
|
||||
}
|
||||
|
||||
@@ -8,9 +8,9 @@ import (
|
||||
type Trip struct {
|
||||
*Route
|
||||
Stops []*StopTime `json:"stops" csv:"stops"`
|
||||
RouteID string `json:"route_id" csv:"route_id"`
|
||||
ServiceID string `json:"service_id" csv:"service_id"`
|
||||
TripID string `json:"trip_id" csv:"trip_id"`
|
||||
RouteId string `json:"route_id" csv:"route_id"`
|
||||
ServiceId string `json:"service_id" csv:"service_id"`
|
||||
TripId string `json:"trip_id" csv:"trip_id"`
|
||||
TripHeadsign string `json:"trip_headsign" csv:"trip_headsign"`
|
||||
TripShortName string `json:"trip_short_name" csv:"trip_short_name"`
|
||||
}
|
||||
@@ -20,7 +20,7 @@ func (t *Trip) GetDirectPossibleDestinations(stop *Stop, when time.Time) iter.Se
|
||||
started := false
|
||||
for _, st := range t.Stops {
|
||||
if !started {
|
||||
if st.StopID == stop.StopID && st.PickupType == 0 {
|
||||
if st.StopId == stop.StopId && st.PickupType == 0 {
|
||||
started = true
|
||||
}
|
||||
continue
|
||||
@@ -44,3 +44,12 @@ func (t *Trip) AddStopTime(stopTime *StopTime) {
|
||||
}
|
||||
t.Stops = append(t.Stops, stopTime)
|
||||
}
|
||||
|
||||
func (t *Trip) Has(stop *Stop) (*StopTime, bool) {
|
||||
for _, st := range t.Stops {
|
||||
if st.StopId == stop.StopId {
|
||||
return st, true
|
||||
}
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user