-
Notifications
You must be signed in to change notification settings - Fork 56
Expand file tree
/
Copy pathobject.go
More file actions
152 lines (129 loc) · 3.72 KB
/
object.go
File metadata and controls
152 lines (129 loc) · 3.72 KB
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
package osm
import (
"fmt"
"strconv"
"strings"
)
// ObjectID encodes the type and ref of an osm object,
// e.g. nodes, ways, relations, changesets, notes and users.
type ObjectID int64
// Type returns the Type of the object.
func (id ObjectID) Type() Type {
switch id & typeMask {
case nodeMask:
return TypeNode
case wayMask:
return TypeWay
case relationMask:
return TypeRelation
case changesetMask:
return TypeChangeset
case noteMask:
return TypeNote
case userMask:
return TypeUser
case boundsMask:
return TypeBounds
}
panic("unknown type")
}
// Ref returns the ID reference for the object. Not unique without the type.
func (id ObjectID) Ref() int64 {
// handle negative ids correctly, if negative top 24 bits need to be set as 1
// want to do this in a non-branching way
// - shift left until 25th bit is now at first position
// - shift right back to original position,
// this option fill with same as the first position
return (int64((id&refMask)>>versionBits) << typeVersionBits) >> typeVersionBits
}
// Version returns the version of the object.
// Will return 0 if the object doesn't have versions like users, notes and changesets.
func (id ObjectID) Version() int {
return int(id & (versionMask))
}
// String returns "type/ref:version" for the object.
func (id ObjectID) String() string {
if id.Version() == 0 {
return fmt.Sprintf("%s/%d:-", id.Type(), id.Ref())
}
return fmt.Sprintf("%s/%d:%d", id.Type(), id.Ref(), id.Version())
}
// ParseObjectID takes a string and tries to determine the object id from it.
// The string must be formatted as "type/id:version", the same as the result of the String method.
func ParseObjectID(s string) (ObjectID, error) {
parts := strings.Split(s, "/")
if len(parts) != 2 {
return 0, fmt.Errorf("invalid element id: %v", s)
}
parts2 := strings.Split(parts[1], ":")
if l := len(parts2); l == 0 || l > 2 {
return 0, fmt.Errorf("invalid element id: %v", s)
}
var version int
ref, err := strconv.ParseInt(parts2[0], 10, 64)
if err != nil {
return 0, fmt.Errorf("invalid element id: %v: %v", s, err)
}
if len(parts2) == 2 && parts2[1] != "-" {
v, e := strconv.ParseInt(parts2[1], 10, 64)
if e != nil {
return 0, fmt.Errorf("invalid element id: %v: %v", s, err)
}
version = int(v)
}
oid, err := Type(parts[0]).objectID(ref, version)
if err != nil {
return 0, fmt.Errorf("invalid element id: %v: %v", s, err)
}
return oid, nil
}
// An Object represents a Node, Way, Relation, Changeset, Note or User only.
type Object interface {
ObjectID() ObjectID
// private is so that **ID types don't implement this interface.
private()
}
func (n *Node) private() {}
func (w *Way) private() {}
func (r *Relation) private() {}
func (c *Changeset) private() {}
func (n *Note) private() {}
func (u *User) private() {}
func (b *Bounds) private() {}
// Objects is a set of objects with some helpers
type Objects []Object
// ObjectIDs returns a slice of the object ids of the osm objects.
func (os Objects) ObjectIDs() ObjectIDs {
if len(os) == 0 {
return nil
}
ids := make(ObjectIDs, 0, len(os))
for _, o := range os {
ids = append(ids, o.ObjectID())
}
return ids
}
// ObjectIDs is a slice of ObjectIDs with some helpers on top.
type ObjectIDs []ObjectID
// A Scanner reads osm data from planet dump files.
// It is based on the bufio.Scanner, common usage.
// Scanners are not safe for parallel use. One should feed the
// objects into their own channel and have workers read from that.
//
// s := scanner.New(r)
// defer s.Close()
//
// for s.Next() {
// o := s.Object()
// // do something
// }
//
// if s.Err() != nil {
// // scanner did not complete fully
// }
type Scanner interface {
Scan() bool
Object() Object
Err() error
Close() error
}