Skip to content

Commit 60aa042

Browse files
authored
Fix issue with bad stop time interpolations when missing shape_dist_t… (#435)
Fix issue with bad stop time interpolations when missing shape_dist_traveled
1 parent 896bd4d commit 60aa042

1 file changed

Lines changed: 37 additions & 38 deletions

File tree

internal/geomcache/geomcache.go

Lines changed: 37 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@ func arePositionsSorted(a []float64) bool {
1212
if len(a) < 2 {
1313
return true
1414
}
15+
if a[0] == a[len(a)-1] {
16+
return false
17+
}
1518
for i := 1; i < len(a); i++ {
1619
if a[i] < a[i-1] {
1720
return false
@@ -22,24 +25,27 @@ func arePositionsSorted(a []float64) bool {
2225

2326
type ShapeInfo struct {
2427
Line []tlxy.Point
25-
Dists []float64
2628
DistLength float64
27-
Length float64
29+
}
30+
31+
type stopPositionInfo struct {
32+
Positions []float64
33+
DistLength float64
2834
}
2935

3036
// GeomCache helps speed up StopTime interpolating by caching various results
3137
type GeomCache struct {
32-
stopPositions map[string][]float64
3338
stops map[string]tlxy.Point
3439
shapes map[string]ShapeInfo
40+
stopPositions map[string]stopPositionInfo
3541
}
3642

3743
// NewGeomCache returns an initialized geomCache
3844
func NewGeomCache() *GeomCache {
3945
return &GeomCache{
40-
stopPositions: map[string][]float64{},
4146
stops: map[string]tlxy.Point{},
4247
shapes: map[string]ShapeInfo{},
48+
stopPositions: map[string]stopPositionInfo{},
4349
}
4450
}
4551

@@ -63,31 +69,16 @@ func (g *GeomCache) GetShapeInfo(eid string) ShapeInfo {
6369
}
6470

6571
func (g *GeomCache) AddShapeGeom(eid string, line []tlxy.Point, dists []float64) {
66-
// Check if already exists, re-use slice to reduce mem
67-
for _, s := range g.shapes {
68-
if tlxy.LineEquals(line, s.Line) {
69-
line = s.Line
70-
dists = s.Dists
71-
}
72-
}
7372
// Create shapeInfo
74-
si := ShapeInfo{
75-
Line: line,
76-
Length: tlxy.LengthHaversine(line),
77-
}
73+
si := ShapeInfo{Line: line}
7874
// Validate ShapeDistTraveled values
7975
if len(dists) > 0 && len(dists) == len(line) && dists[len(dists)-1]-dists[0] > 0 {
80-
// Use supplied ShapeDistTraveled values
81-
si.Dists = dists
82-
si.DistLength = dists[len(dists)-1]
83-
} else {
84-
// Calculate our own ShapeDistTraveled values
85-
si.Dists = make([]float64, len(line))
86-
for i := 1; i < len(line); i++ {
87-
si.Dists[i] = si.Dists[i-1] + tlxy.DistanceHaversine(line[i-1], line[i])
88-
}
8976
si.DistLength = dists[len(dists)-1]
9077
}
78+
// If we don't have ShapeDistTraveled values, calculate them
79+
if si.DistLength == 0 {
80+
si.DistLength = tlxy.LengthHaversine(line)
81+
}
9182
g.shapes[eid] = si
9283
}
9384

@@ -145,11 +136,10 @@ func (g *GeomCache) InterpolateStopTimes(trip *gtfs.Trip) ([]gtfs.StopTime, erro
145136
// TODO: move to somewhere else
146137
func (g *GeomCache) setStopTimeDists(shapeId string, patternId int64, sts []gtfs.StopTime) error {
147138
// Check cache
148-
length := 0.0
149139
stopPositionsKey := fmt.Sprintf("%s-%d", shapeId, patternId)
150-
stopPositions, ok := g.stopPositions[stopPositionsKey]
140+
stopPositionInfo, ok := g.stopPositions[stopPositionsKey]
151141
if !ok {
152-
// Generate the stop-to-stop geometry as fallback
142+
// Generate the stop-to-stop geometry
153143
stopLine := make([]tlxy.Point, len(sts))
154144
for i := 0; i < len(sts); i++ {
155145
point, ok := g.stops[sts[i].StopID.Val]
@@ -159,34 +149,43 @@ func (g *GeomCache) setStopTimeDists(shapeId string, patternId int64, sts []gtfs
159149
stopLine[i] = point
160150
}
161151

152+
// Get the known shape line and known shape distance
153+
var shapeLength float64
162154
var shapeLine []tlxy.Point
163155
if si, ok := g.shapes[shapeId]; ok {
164156
shapeLine = si.Line
165-
length = si.DistLength
157+
shapeLength = si.DistLength
166158
} else {
167159
shapeLine = stopLine
168-
length = tlxy.LengthHaversine(stopLine)
160+
shapeLength = tlxy.LengthHaversine(stopLine)
169161
}
170162

171163
// Calculate positions
172-
stopPositions = tlxy.LineRelativePositions(shapeLine, stopLine)
164+
stopPositions := tlxy.LineRelativePositions(shapeLine, stopLine)
173165

174-
// Check for simple or fallback positions
175-
if !arePositionsSorted(stopPositions) || len(stopLine) == 0 {
166+
// Check if the positions are sorted
167+
if !arePositionsSorted(stopPositions) {
176168
// log.For(ctx).Debug().Msgf("positions %f not increasing, falling back to stop positions; shapeline %f stopLine %f", positions, shapeline, stopLine)
177169
stopPositions = tlxy.LineRelativePositionsFallback(stopLine)
178-
if !arePositionsSorted(stopPositions) {
179-
return errors.New("fallback positions not sorted")
180-
}
181170
}
182-
g.stopPositions[stopPositionsKey] = stopPositions
171+
172+
// Check again
173+
if !arePositionsSorted(stopPositions) {
174+
return errors.New("fallback positions not sorted")
175+
}
176+
177+
stopPositionInfo.Positions = stopPositions
178+
stopPositionInfo.DistLength = shapeLength
179+
g.stopPositions[stopPositionsKey] = stopPositionInfo
183180
}
184-
if len(sts) != len(stopPositions) {
181+
182+
if len(sts) != len(stopPositionInfo.Positions) {
185183
return errors.New("unequal stoptimes and positions")
186184
}
185+
187186
// Set ShapeDistTraveled values
188187
for i := 0; i < len(sts); i++ {
189-
sts[i].ShapeDistTraveled.Set(stopPositions[i] * length)
188+
sts[i].ShapeDistTraveled.Set(stopPositionInfo.Positions[i] * stopPositionInfo.DistLength)
190189
}
191190
return nil
192191
}

0 commit comments

Comments
 (0)