Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion changeset.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ type ChangesetID int64

// ObjectID is a helper returning the object id for this changeset id.
func (id ChangesetID) ObjectID() ObjectID {
return ObjectID(changesetMask | (id << versionBits))
return ObjectID(changesetMask | ((id << versionBits) & refVersionMask))
}

// Changesets is a collection with some helper functions attached.
Expand Down
4 changes: 2 additions & 2 deletions changeset_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -262,9 +262,9 @@ func TestChangeset_MarshalXML(t *testing.T) {
}

func TestChangesets_IDs(t *testing.T) {
cs := Changesets{{ID: 1}, {ID: 2}}
cs := Changesets{{ID: 1}, {ID: 2}, {ID: -3}}

csids := []ChangesetID{1, 2}
csids := []ChangesetID{1, 2, -3}
if ids := cs.IDs(); !reflect.DeepEqual(ids, csids) {
t.Errorf("incorrect changeset id: %v", csids)
}
Expand Down
21 changes: 18 additions & 3 deletions element.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,12 @@ func (id ElementID) Type() Type {

// Ref return the ID reference for the element. Not unique without the type.
func (id ElementID) Ref() int64 {
return int64((id & refMask) >> versionBits)
// 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 element.
Expand Down Expand Up @@ -180,7 +185,14 @@ type elementsSort Elements
func (es elementsSort) Len() int { return len(es) }
func (es elementsSort) Swap(i, j int) { es[i], es[j] = es[j], es[i] }
func (es elementsSort) Less(i, j int) bool {
return es[i].ElementID() < es[j].ElementID()
iid := es[i].ElementID()
jid := es[j].ElementID()

if iid&typeMask != jid&typeMask {
return iid&typeMask < jid&typeMask
}

return ((iid << typeVersionBits) >> typeVersionBits) < ((jid << typeVersionBits) >> typeVersionBits)
}

// ElementIDs is a list of element ids with helper functions on top.
Expand Down Expand Up @@ -213,5 +225,8 @@ func (ids ElementIDs) Sort() {
func (ids elementIDsSort) Len() int { return len(ids) }
func (ids elementIDsSort) Swap(i, j int) { ids[i], ids[j] = ids[j], ids[i] }
func (ids elementIDsSort) Less(i, j int) bool {
return ids[i] < ids[j]
if ids[i]&typeMask != ids[j]&typeMask {
return ids[i]&typeMask < ids[j]&typeMask
}
return ((ids[i] << typeVersionBits) >> typeVersionBits) < ((ids[j] << typeVersionBits) >> typeVersionBits)
}
22 changes: 22 additions & 0 deletions element_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,14 +27,26 @@ func TestElementID_ids(t *testing.T) {
t.Errorf("incorrect id: %v", v)
}

if v := NodeID(-1).ElementID(1).NodeID(); v != -1 {
t.Errorf("incorrect id: %v", v)
}

if v := NodeID(1).ElementID(1).NodeID(); v != 1 {
t.Errorf("incorrect id: %v", v)
}

if v := WayID(-1).ElementID(1).WayID(); v != -1 {
t.Errorf("incorrect id: %v", v)
}

if v := WayID(1).ElementID(1).WayID(); v != 1 {
t.Errorf("incorrect id: %v", v)
}

if v := RelationID(-1).ElementID(1).RelationID(); v != -1 {
t.Errorf("incorrect id: %v", v)
}

if v := RelationID(1).ElementID(1).RelationID(); v != 1 {
t.Errorf("incorrect id: %v", v)
}
Expand Down Expand Up @@ -79,6 +91,10 @@ func TestParseElementID(t *testing.T) {
string string
id ElementID
}{
{
name: "negative id",
id: NodeID(-1).ElementID(1),
},
{
name: "node",
id: NodeID(0).ElementID(1),
Expand Down Expand Up @@ -232,6 +248,7 @@ func TestElements_Sort(t *testing.T) {
es := Elements{
&Node{ID: 1, Version: 4},
&Node{ID: 1, Version: 5},
&Node{ID: -1, Version: 5},
&Way{ID: 2, Version: 6},
&Relation{ID: 3, Version: 7},
&Way{ID: 2, Version: 5},
Expand All @@ -240,6 +257,7 @@ func TestElements_Sort(t *testing.T) {
es.Sort()

expected := ElementIDs{
NodeID(-1).ElementID(5),
NodeID(1).ElementID(4),
NodeID(1).ElementID(5),
NodeID(4).ElementID(8),
Expand Down Expand Up @@ -279,12 +297,16 @@ func TestElementIDs_Sort(t *testing.T) {
ids := ElementIDs{
RelationID(1).ElementID(1),
NodeID(1).ElementID(2),
NodeID(-1).ElementID(3),
NodeID(-1).ElementID(2),
WayID(2).ElementID(3),
WayID(1).ElementID(2),
WayID(1).ElementID(1),
}

expected := ElementIDs{
NodeID(-1).ElementID(2),
NodeID(-1).ElementID(3),
NodeID(1).ElementID(2),
WayID(1).ElementID(1),
WayID(1).ElementID(2),
Expand Down
17 changes: 15 additions & 2 deletions feature.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,13 +67,18 @@ const (
featureMask = 0x7FFFFFFFFFFF0000
typeMask = 0x7F00000000000000

refVersionMask = refMask | versionMask

typeBits = 8
boundsMask = 0x0800000000000000
nodeMask = 0x1000000000000000
wayMask = 0x2000000000000000
relationMask = 0x3000000000000000
changesetMask = 0x4000000000000000
noteMask = 0x5000000000000000
userMask = 0x6000000000000000

typeVersionBits = typeBits + versionBits
)

// A FeatureID is an identifier for a feature in OSM.
Expand All @@ -97,7 +102,12 @@ func (id FeatureID) Type() Type {

// Ref return the ID reference for the feature. Not unique without the type.
func (id FeatureID) Ref() int64 {
return int64((id & refMask) >> versionBits)
// 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
}

// ObjectID is a helper to convert the id to an object id.
Expand Down Expand Up @@ -205,5 +215,8 @@ func (ids FeatureIDs) Sort() {
func (ids featureIDsSort) Len() int { return len(ids) }
func (ids featureIDsSort) Swap(i, j int) { ids[i], ids[j] = ids[j], ids[i] }
func (ids featureIDsSort) Less(i, j int) bool {
return ids[i] < ids[j]
if ids[i]&typeMask != ids[j]&typeMask {
return ids[i]&typeMask < ids[j]&typeMask
}
return ids[i].Ref() < ids[j].Ref()
}
19 changes: 19 additions & 0 deletions feature_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,14 +35,26 @@ func TestFeatureID_ids(t *testing.T) {
t.Errorf("incorrect version: %v", v)
}

if v := NodeID(-1).FeatureID().NodeID(); v != -1 {
t.Errorf("incorrect id: %v", v)
}

if v := NodeID(1).FeatureID().NodeID(); v != 1 {
t.Errorf("incorrect id: %v", v)
}

if v := WayID(-1).FeatureID().WayID(); v != -1 {
t.Errorf("incorrect id: %v", v)
}

if v := WayID(1).FeatureID().WayID(); v != 1 {
t.Errorf("incorrect id: %v", v)
}

if v := RelationID(-1).FeatureID().RelationID(); v != -1 {
t.Errorf("incorrect id: %v", v)
}

if v := RelationID(1).FeatureID().RelationID(); v != 1 {
t.Errorf("incorrect id: %v", v)
}
Expand Down Expand Up @@ -94,6 +106,11 @@ func TestFeature_String(t *testing.T) {
id FeatureID
expected string
}{
{
name: "node",
id: NodeID(-1).FeatureID(),
expected: "node/-1",
},
{
name: "node",
id: NodeID(1).FeatureID(),
Expand Down Expand Up @@ -200,12 +217,14 @@ func TestFeatureIDs_Counts(t *testing.T) {
func TestFeatureIDs_Sort(t *testing.T) {
ids := FeatureIDs{
RelationID(1).FeatureID(),
NodeID(-1).FeatureID(),
NodeID(1).FeatureID(),
WayID(2).FeatureID(),
WayID(1).FeatureID(),
}

expected := FeatureIDs{
NodeID(-1).FeatureID(),
NodeID(1).FeatureID(),
WayID(1).FeatureID(),
WayID(2).FeatureID(),
Expand Down
2 changes: 1 addition & 1 deletion node.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ func (id NodeID) ObjectID(v int) ObjectID {

// FeatureID is a helper returning the feature id for this node id.
func (id NodeID) FeatureID() FeatureID {
return FeatureID(nodeMask | (id << versionBits))
return FeatureID(nodeMask | ((id << versionBits) & refVersionMask))
}

// ElementID is a helper to convert the id to an element id.
Expand Down
19 changes: 14 additions & 5 deletions node_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -90,19 +90,28 @@ func TestNodes_ids(t *testing.T) {
ns := Nodes{
{ID: 1, Version: 3},
{ID: 2, Version: 4},
{ID: -3, Version: 5},
}

eids := ElementIDs{NodeID(1).ElementID(3), NodeID(2).ElementID(4)}
eids := ElementIDs{
NodeID(1).ElementID(3),
NodeID(2).ElementID(4),
NodeID(-3).ElementID(5),
}
if ids := ns.ElementIDs(); !reflect.DeepEqual(ids, eids) {
t.Errorf("incorrect element ids: %v", ids)
}

fids := FeatureIDs{NodeID(1).FeatureID(), NodeID(2).FeatureID()}
fids := FeatureIDs{
NodeID(1).FeatureID(),
NodeID(2).FeatureID(),
NodeID(-3).FeatureID(),
}
if ids := ns.FeatureIDs(); !reflect.DeepEqual(ids, fids) {
t.Errorf("incorrect feature ids: %v", ids)
}

nids := []NodeID{1, 2}
nids := []NodeID{1, 2, -3}
if ids := ns.IDs(); !reflect.DeepEqual(ids, nids) {
t.Errorf("incorrect node ids: %v", nids)
}
Expand All @@ -113,7 +122,7 @@ func TestNodes_SortByIDVersion(t *testing.T) {
{ID: 7, Version: 3},
{ID: 2, Version: 4},
{ID: 5, Version: 2},
{ID: 5, Version: 3},
{ID: -1, Version: 3},
{ID: 5, Version: 4},
{ID: 3, Version: 4},
{ID: 4, Version: 4},
Expand All @@ -123,11 +132,11 @@ func TestNodes_SortByIDVersion(t *testing.T) {
ns.SortByIDVersion()

eids := ElementIDs{
NodeID(-1).ElementID(3),
NodeID(2).ElementID(4),
NodeID(3).ElementID(4),
NodeID(4).ElementID(4),
NodeID(5).ElementID(2),
NodeID(5).ElementID(3),
NodeID(5).ElementID(4),
NodeID(7).ElementID(3),
NodeID(9).ElementID(4),
Expand Down
2 changes: 1 addition & 1 deletion note.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ type NoteID int64

// ObjectID is a helper returning the object id for this note id.
func (id NoteID) ObjectID() ObjectID {
return ObjectID(noteMask | (id << versionBits))
return ObjectID(noteMask | ((id << versionBits) & refVersionMask))
}

const dateLayout = "2006-01-02 15:04:05 MST"
Expand Down
14 changes: 13 additions & 1 deletion note_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,18 @@ func TestNote_ObjectID(t *testing.T) {
}

if v := id.Ref(); v != 123 {
t.Errorf("incorrect ref: %v", 123)
t.Errorf("incorrect ref: %v", v)
}

// negative id
n = Note{ID: -123}
id = n.ObjectID()

if v := id.Type(); v != TypeNote {
t.Errorf("incorrect type: %v", v)
}

if v := id.Ref(); v != -123 {
t.Errorf("incorrect ref: %v", v)
}
}
7 changes: 6 additions & 1 deletion object.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,12 @@ func (id ObjectID) Type() Type {

// Ref returns the ID reference for the object. Not unique without the type.
func (id ObjectID) Ref() int64 {
return int64((id & refMask) >> versionBits)
// 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.
Expand Down
6 changes: 6 additions & 0 deletions object_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@ func TestParseObjectID(t *testing.T) {
name: "zero version node",
id: NodeID(3).ObjectID(0),
},
{
name: "negative id",
id: NodeID(-3).ObjectID(10),
},
{
name: "way",
id: WayID(10).ObjectID(2),
Expand Down Expand Up @@ -103,6 +107,7 @@ func TestParseObjectID(t *testing.T) {
func TestObjects_ObjectIDs(t *testing.T) {
es := Objects{
&Node{ID: 1, Version: 5},
&Node{ID: -1, Version: 5},
&Way{ID: 2, Version: 6},
&Relation{ID: 3, Version: 7},
&Node{ID: 4, Version: 8},
Expand All @@ -112,6 +117,7 @@ func TestObjects_ObjectIDs(t *testing.T) {

expected := ObjectIDs{
NodeID(1).ObjectID(5),
NodeID(-1).ObjectID(5),
WayID(2).ObjectID(6),
RelationID(3).ObjectID(7),
NodeID(4).ObjectID(8),
Expand Down
25 changes: 25 additions & 0 deletions osmxml/scanner_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,31 @@ func TestScanner_bounds(t *testing.T) {
}
}

func TestScanner_negativeId(t *testing.T) {
f, err := os.Open("testdata/error.xml")
if err != nil {
t.Fatalf("could not open file: %v", err)
}
defer f.Close()

scanner := New(context.Background(), f)
defer scanner.Close()

// just check the first node
scanner.Scan()

o := scanner.Object()
oid := o.ObjectID()

if v := oid.Type(); v != "node" {
t.Errorf("type should be node, got %v", v)
}

if v := oid.Ref(); v != -25490 {
t.Errorf("id incorrect, got %0x ", v)
}
}

func TestAndorra(t *testing.T) {
f, err := os.Open("../testdata/andorra-latest.osm.bz2")
if err != nil {
Expand Down
Loading
Loading