/
shaperemeasurer.go
executable file
·146 lines (125 loc) · 3.57 KB
/
shaperemeasurer.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
// Copyright 2016 Patrick Brosi
// Authors: [email protected]
//
// Use of this source code is governed by a GPL v2
// license that can be found in the LICENSE file
package processors
import (
"fmt"
"github.com/patrickbr/gtfsparser"
gtfs "github.com/patrickbr/gtfsparser/gtfs"
"math"
"os"
)
// ShapeRemeasurer remeasure shapes
type ShapeRemeasurer struct {
}
// Run this ShapeRemeasurer on some feed
func (s ShapeRemeasurer) Run(feed *gtfsparser.Feed) {
fmt.Fprintf(os.Stdout, "Remeasuring shapes... ")
numchunks := MaxParallelism()
chunksize := (len(feed.Shapes) + numchunks - 1) / numchunks
chunks := make([][]*gtfs.Shape, numchunks)
curchunk := 0
for _, s := range feed.Shapes {
chunks[curchunk] = append(chunks[curchunk], s)
if len(chunks[curchunk]) == chunksize {
curchunk++
}
}
sem := make(chan empty, len(chunks))
for _, c := range chunks {
go func(chunk []*gtfs.Shape) {
for _, shp := range chunk {
s.remeasure(shp)
}
sem <- empty{}
}(c)
}
// wait for goroutines to finish
for i := 0; i < len(chunks); i++ {
<-sem
}
// fix small inconsistencies
for _, t := range feed.Trips {
for i, st := range t.StopTimes {
if st.HasDistanceTraveled() && t.Shape != nil && t.Shape.Points[len(t.Shape.Points)-1].HasDistanceTraveled() && st.Shape_dist_traveled() > t.Shape.Points[len(t.Shape.Points)-1].Dist_traveled {
t.StopTimes[i].SetShape_dist_traveled(t.Shape.Points[len(t.Shape.Points)-1].Dist_traveled)
}
}
}
fmt.Fprintf(os.Stdout, "done. (%d shapes remeasured)\n", len(feed.Shapes))
}
// Remeasure a single shape
func (s ShapeRemeasurer) remeasure(shape *gtfs.Shape) {
avgMeasure, nulled := s.remeasureKnown(shape)
if !nulled {
s.remeasureUnknown(shape, avgMeasure)
} else {
// no avg measurement found, null all values
for i := range shape.Points {
shape.Points[i].Dist_traveled = float32(math.NaN())
}
}
}
// Remeasure parts of the shape we could not guess the correct measurement by using
// the average measurement
func (s ShapeRemeasurer) remeasureUnknown(shape *gtfs.Shape, avgMeasure float64) {
lastUMIndex := -1
lastM := 0.0
for i := 0; i <= len(shape.Points); i++ {
if i == len(shape.Points) || shape.Points[i].HasDistanceTraveled() {
if lastUMIndex > -1 {
s.remeasureBetween(lastUMIndex, i, avgMeasure, lastM, shape)
lastUMIndex = -1
}
if i < len(shape.Points) {
lastM = float64(shape.Points[i].Dist_traveled)
}
} else if lastUMIndex == -1 {
lastUMIndex = i
}
}
}
// Remeasure parts of the shape we can guess by using surrounding points
func (s ShapeRemeasurer) remeasureKnown(shape *gtfs.Shape) (float64, bool) {
c := 0
m := 0.0
lastMIndex := -1
lastM := -1.0
hasLast := false
d := 0.0
for i := 0; i < len(shape.Points); i++ {
if i > 0 {
d = d + distP(&shape.Points[i-1], &shape.Points[i])
}
if shape.Points[i].HasDistanceTraveled() {
if hasLast && d > 0 {
localM := (float64(shape.Points[i].Dist_traveled) - lastM) / d
if i-lastMIndex > 1 {
s.remeasureBetween(lastMIndex+1, i, localM, lastM, shape)
}
m = m + localM
c++
}
lastMIndex = i
lastM = float64(shape.Points[i].Dist_traveled)
hasLast = shape.Points[i].HasDistanceTraveled()
d = 0
}
}
if c == 0 {
return 0, true
}
return m / float64(c), false
}
// Remeasure between points i and end
func (s ShapeRemeasurer) remeasureBetween(i int, end int, mPUnit float64, lastMeasure float64, shape *gtfs.Shape) {
d := 0.0
for ; i < end; i++ {
if i > 0 {
d = d + distP(&shape.Points[i-1], &shape.Points[i])
}
shape.Points[i].Dist_traveled = float32(lastMeasure + (d * mPUnit))
}
}