Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 11 additions & 6 deletions gtfstidy.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,18 @@ package main
import (
"errors"
"fmt"
"github.com/patrickbr/gtfsparser"
"github.com/patrickbr/gtfsparser/gtfs"
"github.com/patrickbr/gtfstidy/processors"
"github.com/patrickbr/gtfswriter"
"github.com/paulmach/go.geojson"
flag "github.com/spf13/pflag"
"io/ioutil"
"os"
"path"
"strconv"
"strings"

"github.com/patrickbr/gtfsparser"
"github.com/patrickbr/gtfsparser/gtfs"
"github.com/patrickbr/gtfstidy/processors"
"github.com/patrickbr/gtfswriter"
geojson "github.com/paulmach/go.geojson"
flag "github.com/spf13/pflag"
)

func getGtfsPoly(poly [][][]float64) gtfsparser.Polygon {
Expand Down Expand Up @@ -170,6 +171,7 @@ func main() {
explicitCals := flag.BoolP("explicit-calendar", "", false, "add calendar.txt entry for every service, even irregular ones")
ensureTripHeadsigns := flag.BoolP("ensure-trip-headsigns", "", false, "write trip headsigns if missing")
ensureParents := flag.BoolP("ensure-stop-parents", "", false, "ensure that every stop (location_type=0) has a parent station")
fixTripHeadsigns := flag.BoolP("fix-trip-headsigns", "", false, "fixes trip headsigns pointing to previous stops")
keepColOrder := flag.BoolP("keep-col-order", "", false, "keep the original column ordering of the input feed")
keepFields := flag.BoolP("keep-additional-fields", "F", false, "keep all non-GTFS fields from the input")
dropTooFast := flag.BoolP("drop-too-fast-trips", "", false, "drop trips that are too fast to realistically occur")
Expand Down Expand Up @@ -641,6 +643,9 @@ func main() {
if *ensureTripHeadsigns {
minzers = append(minzers, processors.TripHeadsigner{})
}
if *fixTripHeadsigns {
minzers = append(minzers, processors.FixIntermediateHeadsigns{})
}

if *useRedTripMinimizer {
// to convert calendar_dates based services into regular calendar.txt services
Expand Down
72 changes: 72 additions & 0 deletions processors/intermediateheadsign.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
package processors

import (
"fmt"
"os"

"github.com/patrickbr/gtfsparser"
)

// FixIntermediateHeadsigns checks if the trip headsign matches an intermediate stop.
// If so, it sets the stop_headsign for previous stops to that intermediate name
// and updates the trip_headsign to the final destination.

type FixIntermediateHeadsigns struct{}

func (pro FixIntermediateHeadsigns) Run(feed *gtfsparser.Feed) {
fmt.Fprintf(os.Stdout, "Fixing intermediate headsigns... ")

count := 0

for _, trip := range feed.Trips {
if len(trip.StopTimes) < 2 {
continue
}

currentHeadsign := trip.Headsign
if *currentHeadsign == "" {
continue
}

lastStopIdx := len(trip.StopTimes) - 1
lastStop := trip.StopTimes[lastStopIdx].Stop()
if lastStop == nil {
continue
}

if *currentHeadsign == lastStop.Name {
continue
}

matchIndex := -1

// 1. Check if headsign is equal to a stop along the trip (except the last one)
for i := len(trip.StopTimes) - 2; i >= 0; i-- {
st := trip.StopTimes[i]
if st.Stop() != nil && st.Stop().Name == *currentHeadsign {
matchIndex = i
break
}
}

if matchIndex != -1 {
// Logic:
// Sequence: A -> B -> C (match) -> D -> E (last)
// Old Trip Headsign: C
// New Trip Headsign: E
// Stop Headsign for A, B: C

// Update trip headsign to the actual last stop
trip.Headsign = &lastStop.Name

// Update stop_headsign for all stops prior to the match
for j := 0; j < matchIndex; j++ {
trip.StopTimes[j].SetHeadsign(currentHeadsign)
}

count++
}
}

fmt.Fprintf(os.Stdout, "done. Fixed headsigns for %d trips.\n", count)
}