-
Notifications
You must be signed in to change notification settings - Fork 48
/
relation.go
256 lines (206 loc) · 7.23 KB
/
relation.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
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
package osm
import (
"sort"
"time"
"github.com/paulmach/orb"
)
// RelationID is the primary key of a relation.
// A relation is uniquely identifiable by the id + version.
type RelationID int64
// ObjectID is a helper returning the object id for this relation id.
func (id RelationID) ObjectID(v int) ObjectID {
return ObjectID(id.ElementID(v))
}
// FeatureID is a helper returning the feature id for this relation id.
func (id RelationID) FeatureID() FeatureID {
return FeatureID(relationMask | id<<versionBits)
}
// ElementID is a helper to convert the id to an element id.
func (id RelationID) ElementID(v int) ElementID {
return id.FeatureID().ElementID(v)
}
// Relation is an collection of nodes, ways and other relations
// with some defining attributes.
type Relation struct {
XMLName xmlNameJSONTypeRel `xml:"relation" json:"type"`
ID RelationID `xml:"id,attr" json:"id"`
User string `xml:"user,attr" json:"user,omitempty"`
UserID UserID `xml:"uid,attr" json:"uid,omitempty"`
Visible bool `xml:"visible,attr" json:"visible"`
Version int `xml:"version,attr" json:"version,omitempty"`
ChangesetID ChangesetID `xml:"changeset,attr" json:"changeset,omitempty"`
Timestamp time.Time `xml:"timestamp,attr" json:"timestamp,omitempty"`
Tags Tags `xml:"tag" json:"tags,omitempty"`
Members Members `xml:"member" json:"members"`
// Committed, is the estimated time this object was committed
// and made visible in the central OSM database.
Committed *time.Time `xml:"committed,attr,omitempty" json:"committed,omitempty"`
// Updates are changes to the members of this relation independent
// of an update to the relation itself. The OSM api allows a child
// to be updated without any changes to the parent.
Updates Updates `xml:"update,omitempty" json:"updates,omitempty"`
// Bounds are included by overpass, and maybe others
Bounds *Bounds `xml:"bounds,omitempty" json:"bounds,omitempty"`
}
// Members represents an ordered list of relation members.
type Members []Member
// Member is a member of a relation.
type Member struct {
Type Type `xml:"type,attr" json:"type"`
Ref int64 `xml:"ref,attr" json:"ref"`
Role string `xml:"role,attr" json:"role"`
Version int `xml:"version,attr,omitempty" json:"version,omitempty"`
ChangesetID ChangesetID `xml:"changeset,attr,omitempty" json:"changeset,omitempty"`
// Node location if Type == Node
// Closest vertex to centroid if Type == Way
// Empty/invalid if Type == Relation
Lat float64 `xml:"lat,attr,omitempty" json:"lat,omitempty"`
Lon float64 `xml:"lon,attr,omitempty" json:"lon,omitempty"`
// Orientation is the direction of the way around a ring of a multipolygon.
// Only valid for multipolygon or boundary relations.
Orientation orb.Orientation `xml:"orientation,attr,omitempty" json:"orientation,omitempty"`
// Nodes are sometimes included in members of type way to include the lat/lon
// path of the way. Overpass returns xml like this.
Nodes WayNodes `xml:"nd" json:"nodes,omitempty"`
}
// ObjectID returns the object id of the relation.
func (r *Relation) ObjectID() ObjectID {
return r.ID.ObjectID(r.Version)
}
// FeatureID returns the feature id of the relation.
func (r *Relation) FeatureID() FeatureID {
return r.ID.FeatureID()
}
// ElementID returns the element id of the relation.
func (r *Relation) ElementID() ElementID {
return r.ID.ElementID(r.Version)
}
// FeatureID returns the feature id of the member.
func (m Member) FeatureID() FeatureID {
switch m.Type {
case TypeNode:
return NodeID(m.Ref).FeatureID()
case TypeWay:
return WayID(m.Ref).FeatureID()
case TypeRelation:
return RelationID(m.Ref).FeatureID()
}
panic("unknown type")
}
// ElementID returns the element id of the member.
func (m Member) ElementID() ElementID {
return m.FeatureID().ElementID(m.Version)
}
// Point returns the orb.Point location for the member.
// Will be (0, 0) if the relation is not annotated.
// For way members this location is annotated as the "surface point".
func (m Member) Point() orb.Point {
return orb.Point{m.Lon, m.Lat}
}
// CommittedAt returns the best estimate on when this element
// became was written/committed into the database.
func (r *Relation) CommittedAt() time.Time {
if r.Committed != nil {
return *r.Committed
}
return r.Timestamp
}
// TagMap returns the element tags as a key/value map.
func (r *Relation) TagMap() map[string]string {
return r.Tags.Map()
}
// ApplyUpdatesUpTo will apply the updates to this object upto and including
// the given time.
func (r *Relation) ApplyUpdatesUpTo(t time.Time) error {
var notApplied []Update
for _, u := range r.Updates {
if u.Timestamp.After(t) {
notApplied = append(notApplied, u)
continue
}
if err := r.applyUpdate(u); err != nil {
return err
}
}
r.Updates = notApplied
return nil
}
// applyUpdate will modify the current relation and dictated by the given update.
// Will return UpdateIndexOutOfRangeError if the update index is too large.
func (r *Relation) applyUpdate(u Update) error {
if u.Index >= len(r.Members) {
return &UpdateIndexOutOfRangeError{Index: u.Index}
}
r.Members[u.Index].Version = u.Version
r.Members[u.Index].ChangesetID = u.ChangesetID
r.Members[u.Index].Lat = u.Lat
r.Members[u.Index].Lon = u.Lon
if u.Reverse {
r.Members[u.Index].Orientation *= -1
}
return nil
}
// FeatureIDs returns the a list of feature ids for the members.
func (ms Members) FeatureIDs() FeatureIDs {
ids := make(FeatureIDs, len(ms), len(ms)+1)
for i, m := range ms {
ids[i] = m.FeatureID()
}
return ids
}
// ElementIDs returns the a list of element ids for the members.
func (ms Members) ElementIDs() ElementIDs {
ids := make(ElementIDs, len(ms), len(ms)+1)
for i, m := range ms {
ids[i] = m.ElementID()
}
return ids
}
// MarshalJSON allows the members to be marshalled as defined by the
// overpass osmjson. This function is a wrapper to marshal null as [].
func (ms Members) MarshalJSON() ([]byte, error) {
if len(ms) == 0 {
return []byte(`[]`), nil
}
return marshalJSON([]Member(ms))
}
// Relations is a list of relations with some helper functions attached.
type Relations []*Relation
// IDs returns the ids for all the relations.
func (rs Relations) IDs() []RelationID {
result := make([]RelationID, len(rs))
for i, r := range rs {
result[i] = r.ID
}
return result
}
// FeatureIDs returns the feature ids for all the relations.
func (rs Relations) FeatureIDs() FeatureIDs {
result := make(FeatureIDs, len(rs))
for i, r := range rs {
result[i] = r.FeatureID()
}
return result
}
// ElementIDs returns the element ids for all the relations.
func (rs Relations) ElementIDs() ElementIDs {
result := make(ElementIDs, len(rs))
for i, r := range rs {
result[i] = r.ElementID()
}
return result
}
type relationsSort Relations
// SortByIDVersion will sort the set of relations first by id and then version
// in ascending order.
func (rs Relations) SortByIDVersion() {
sort.Sort(relationsSort(rs))
}
func (rs relationsSort) Len() int { return len(rs) }
func (rs relationsSort) Swap(i, j int) { rs[i], rs[j] = rs[j], rs[i] }
func (rs relationsSort) Less(i, j int) bool {
if rs[i].ID == rs[j].ID {
return rs[i].Version < rs[j].Version
}
return rs[i].ID < rs[j].ID
}