/
mputil.go
176 lines (144 loc) · 3.65 KB
/
mputil.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
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
package mputil
import (
"time"
"github.com/paulmach/orb"
"github.com/paulmach/osm"
)
// Segment is a section of a multipolygon with some extra information
// on the member it came from.
type Segment struct {
Index uint32
Orientation orb.Orientation
Reversed bool
Line orb.LineString
}
// Reverse will reverse the line string of the segment.
func (s *Segment) Reverse() {
s.Reversed = !s.Reversed
s.Line.Reverse()
}
// First returns the first point in the segment linestring.
func (s Segment) First() orb.Point {
return s.Line[0]
}
// Last returns the last point in the segment linestring.
func (s Segment) Last() orb.Point {
return s.Line[len(s.Line)-1]
}
// MultiSegment is an ordered set of segments that form a continuous
// section of a multipolygon.
type MultiSegment []Segment
// First returns the first point in the list of linestrings.
func (ms MultiSegment) First() orb.Point {
return ms[0].Line[0]
}
// Last returns the last point in the list of linestrings.
func (ms MultiSegment) Last() orb.Point {
line := ms[len(ms)-1].Line
return line[len(line)-1]
}
// LineString converts a multisegment into a geo linestring object.
func (ms MultiSegment) LineString() orb.LineString {
length := 0
for _, s := range ms {
length += len(s.Line)
}
line := make(orb.LineString, 0, length)
for _, s := range ms {
line = append(line, s.Line...)
}
return line
}
// Ring converts the multisegment to a ring of the given orientation.
// It uses the orientation on the members if possible.
func (ms MultiSegment) Ring(o orb.Orientation) orb.Ring {
length := 0
for _, s := range ms {
length += len(s.Line)
}
ring := make(orb.Ring, 0, length)
haveOrient := false
reversed := false
for _, s := range ms {
if s.Orientation != 0 {
haveOrient = true
// if s.Orientation == o && s.Reversed {
// reversed = true
// }
// if s.Orientation != 0 && !s.Reversed {
// reversed = true
// }
if (s.Orientation == o) == s.Reversed {
reversed = true
}
}
ring = append(ring, s.Line...)
}
if (haveOrient && reversed) || (!haveOrient && ring.Orientation() != o) {
ring.Reverse()
}
return ring
}
// Orientation computes the orientation of a multisegment like if it was ring.
func (ms MultiSegment) Orientation() orb.Orientation {
area := 0.0
prev := ms.First()
// implicitly move everything to near the origin to help with roundoff
offset := prev
for _, segment := range ms {
for _, point := range segment.Line {
area += (prev[0]-offset[0])*(point[1]-offset[1]) -
(point[0]-offset[0])*(prev[1]-offset[1])
prev = point
}
}
if area > 0 {
return orb.CCW
}
return orb.CW
}
// Group will take the members and group them by inner our outer parts
// of the relation. Will also build the way geometry.
func Group(
members osm.Members,
ways map[osm.WayID]*osm.Way,
at time.Time,
) (outer, inner []Segment, tainted bool) {
for i, m := range members {
if m.Type != osm.TypeWay {
continue
}
w := ways[osm.WayID(m.Ref)]
if w == nil {
tainted = true
continue // could be not found error, or something else.
}
line := w.LineStringAt(at)
if len(line) != len(w.Nodes) {
tainted = true
}
// zero length ways exist and don't make any sense when
// building the multipolygon rings.
if len(line) == 0 {
continue
}
l := Segment{
Index: uint32(i),
Orientation: m.Orientation,
Reversed: false,
Line: line,
}
if m.Role == "outer" {
if l.Orientation == orb.CW {
l.Reverse()
}
outer = append(outer, l)
} else if m.Role == "inner" {
if l.Orientation == orb.CCW {
l.Reverse()
}
inner = append(inner, l)
}
}
return outer, inner, tainted
}