├── .gitignore
├── LICENSE
├── README.md
├── osm.go
└── osm_test.go
/.gitignore:
--------------------------------------------------------------------------------
1 | # Compiled Object files, Static and Dynamic libs (Shared Objects)
2 | *.o
3 | *.a
4 | *.so
5 |
6 | # Folders
7 | _obj
8 | _test
9 |
10 | # Architecture specific extensions/prefixes
11 | *.[568vq]
12 | [568vq].out
13 |
14 | *.cgo1.go
15 | *.cgo2.c
16 | _cgo_defun.c
17 | _cgo_gotypes.go
18 | _cgo_export.*
19 |
20 | _testmain.go
21 |
22 | *.exe
23 | *.test
24 | *.prof
25 |
26 | # Ignoring test data in project directory
27 | *.osm
28 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2016 Lukas Rist
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # go-osm
2 |
3 | OpenStreetMap XML Data parser in golang
4 |
5 | ## Install
6 |
7 | `go get github.com/glaslos/go-osm`
8 |
9 | ## Usage
10 |
11 | ```go
12 | package main
13 |
14 | import (
15 | "fmt"
16 | "github.com/glaslos/go-osm"
17 | )
18 |
19 | var data = `
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 | `
29 |
30 | func main() {
31 | m, _ := osm.DecodeString(data)
32 | // 64.0912791 -21.9271369
33 | fmt.Println(m.Nodes[0].Lat, m.Nodes[0].Lng)
34 | }
35 | ```
--------------------------------------------------------------------------------
/osm.go:
--------------------------------------------------------------------------------
1 | package osm
2 |
3 | import (
4 | "encoding/xml"
5 | "io"
6 | "os"
7 | "strings"
8 | "time"
9 | )
10 |
11 | // Osm struct
12 | type Map struct {
13 | Bounds Bounds
14 | Nodes []Node
15 | Ways []Way
16 | Relations []Relation
17 | }
18 |
19 | // Bounds struct
20 | type Bounds struct {
21 | XMLName xml.Name `xml:"bounds"`
22 | Minlat float64 `xml:"minlat,attr"`
23 | Minlon float64 `xml:"minlon,attr"`
24 | Maxlat float64 `xml:"maxlat,attr"`
25 | Maxlon float64 `xml:"maxlon,attr"`
26 | }
27 |
28 | // Location struct
29 | type Location struct {
30 | Type string
31 | Coordinates []float64
32 | }
33 |
34 | // Tag struct
35 | type Tag struct {
36 | XMLName xml.Name `xml:"tag"`
37 | Key string `xml:"k,attr"`
38 | Value string `xml:"v,attr"`
39 | }
40 |
41 | // Elem is a OSM base element
42 | type Elem struct {
43 | ID int64 `xml:"id,attr"`
44 | Loc Location
45 | Version int `xml:"version,attr"`
46 | Ts time.Time `xml:"timestamp,attr"`
47 | UID int64 `xml:"uid,attr"`
48 | User string `xml:"user,attr"`
49 | ChangeSet int64 `xml:"changeset,attr"`
50 | }
51 |
52 | // Node structure
53 | type Node struct {
54 | Elem
55 | XMLName xml.Name `xml:"node"`
56 | Lat float64 `xml:"lat,attr"`
57 | Lng float64 `xml:"lon,attr"`
58 | Tag []Tag `xml:"tag"`
59 | }
60 |
61 | // Way struct
62 | type Way struct {
63 | Elem
64 | XMLName xml.Name `xml:"way"`
65 | Tags map[string]string
66 | RTags []Tag `xml:"tag"`
67 | Nds []struct {
68 | ID int64 `xml:"ref,attr"`
69 | } `xml:"nd"`
70 | }
71 |
72 | // Member struct
73 | type Member struct {
74 | Type string `xml:"type,attr"`
75 | Ref int64 `xml:"ref,attr"`
76 | Role string `xml:"role,attr"`
77 | }
78 |
79 | // Relation struct
80 | type Relation struct {
81 | Elem
82 | Visible bool `xml:"visible,attr"`
83 | Version string `xml:"version,attr"`
84 | Members []Member `xml:"member"`
85 | Tags []Tag `xml:"tag"`
86 | }
87 |
88 | // DecodeFile an OSM file
89 | func DecodeFile(fileName string) (*Map, error) {
90 |
91 | file, err := os.Open(fileName)
92 | if err != nil {
93 | return nil, err
94 | }
95 | defer file.Close()
96 |
97 | return Decode(file)
98 | }
99 |
100 | func DecodeString(data string) (*Map, error) {
101 | return Decode(strings.NewReader(data))
102 | }
103 |
104 | // Decode an reader
105 | func Decode(reader io.Reader) (*Map, error) {
106 | var (
107 | o = new(Map)
108 | err error
109 | )
110 |
111 | decoder := xml.NewDecoder(reader)
112 | for {
113 | token, _ := decoder.Token()
114 | if token == nil {
115 | break
116 | }
117 |
118 | switch typedToken := token.(type) {
119 | case xml.StartElement:
120 | switch typedToken.Name.Local {
121 | case "bounds":
122 | var b Bounds
123 | err = decoder.DecodeElement(&b, &typedToken)
124 | if err != nil {
125 | return nil, err
126 | }
127 | o.Bounds = b
128 |
129 | case "node":
130 | var n Node
131 | err = decoder.DecodeElement(&n, &typedToken)
132 | if err != nil {
133 | return nil, err
134 | }
135 | o.Nodes = append(o.Nodes, n)
136 |
137 | case "way":
138 | var w Way
139 | err = decoder.DecodeElement(&w, &typedToken)
140 | if err != nil {
141 | return nil, err
142 | }
143 | o.Ways = append(o.Ways, w)
144 |
145 | case "relation":
146 | var r Relation
147 | err = decoder.DecodeElement(&r, &typedToken)
148 | if err != nil {
149 | return nil, err
150 | }
151 | o.Relations = append(o.Relations, r)
152 | }
153 | }
154 | }
155 | return o, nil
156 | }
157 |
--------------------------------------------------------------------------------
/osm_test.go:
--------------------------------------------------------------------------------
1 | package osm
2 |
3 | import (
4 | "bytes"
5 | "io/ioutil"
6 | "os"
7 | "testing"
8 | )
9 |
10 | func TestUnmarshalFile(t *testing.T) {
11 | testFileName := "testfile.osm"
12 | err := ioutil.WriteFile(testFileName, testData, 0600)
13 | if err != nil {
14 | t.Fatal(err)
15 | }
16 | defer func() {
17 | os.Remove(testFileName)
18 | }()
19 |
20 | o, err := DecodeFile(testFileName)
21 | if err != nil {
22 | t.Fatal(err)
23 | }
24 | if len(o.Nodes) != 1 {
25 | t.Fatal("Map has only one node")
26 | }
27 | if len(o.Ways) != 1 {
28 | t.Fatal("Map has only one way")
29 | }
30 | if len(o.Relations) != 1 {
31 | t.Fatal("Map has only one relation")
32 | }
33 | }
34 |
35 | func TestUnmarshalData(t *testing.T) {
36 | o, e := Decode(bytes.NewReader(testData))
37 | if e != nil {
38 | t.Fatal(e)
39 | }
40 | if len(o.Nodes) != 1 {
41 | t.Fatal("Map has only one node")
42 | }
43 | if len(o.Ways) != 1 {
44 | t.Fatal("Map has only one way")
45 | }
46 | if len(o.Relations) != 1 {
47 | t.Fatal("Map has only one relation")
48 | }
49 | }
50 |
51 | var testData = []byte(`
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 |
--------------------------------------------------------------------------------