forked from qedus/osmpbf
-
Notifications
You must be signed in to change notification settings - Fork 2
/
blob_decoder.go
136 lines (117 loc) · 3.38 KB
/
blob_decoder.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
package gosmonaut
import (
"bytes"
"compress/zlib"
"errors"
"fmt"
"github.com/MorbZ/gosmonaut/OSMPBF"
)
// DecoderType represents the decoder that is used for parsing PBF blob data.
type DecoderType int
// Order is important: First decoder is the default
const (
// FastDecoder is a custom implementation of the protobuf format. It is
// optimized for decoding of PBF files. Rather than unmarshalling it streams
// the entities and thus reduces GC overhead. The fast blob decoder lacks
// support of some protobuf features which include groups and unpacked
// varint arrays. It is supposed to fail when it encounters a feature it
// doesn't support.
FastDecoder DecoderType = iota
// GoDecoder uses the official Golang Protobuf package. All protobuf
// messages will be unmarshalled to temporary objects before processing.
GoDecoder
)
/* Blob Decoder Interfaces */
type blobDecoder interface {
decode(*OSMPBF.Blob, OSMType) ([]entityParser, OSMTypeSet, error)
}
type entityParser interface {
next() (id int64, tags OSMTags, err error)
}
type nodeParser interface {
entityParser
coords() (lat, lon float64, err error)
}
type wayParser interface {
entityParser
refs() ([]int64, error)
}
type relationParser interface {
entityParser
ids() ([]int64, error)
roles() ([]string, error)
types() ([]OSMType, error)
}
/* Helpers */
func getBlobData(blob *OSMPBF.Blob, buf *bytes.Buffer) ([]byte, error) {
data := blob.GetData()
switch v := data.(type) {
case *OSMPBF.Blob_Raw:
return v.Raw, nil
case *OSMPBF.Blob_ZlibData:
r, err := zlib.NewReader(bytes.NewReader(v.ZlibData))
if err != nil {
return nil, err
}
if buf == nil {
buf = new(bytes.Buffer)
} else {
buf.Reset()
}
buf.Grow(int(blob.GetRawSize()))
_, err = buf.ReadFrom(r)
if err != nil {
return nil, err
}
if buf.Len() != int(blob.GetRawSize()) {
err = fmt.Errorf("raw blob data size %d but expected %d", buf.Len(), blob.GetRawSize())
return nil, err
}
return buf.Bytes(), nil
case *OSMPBF.Blob_LzmaData:
return nil, errors.New("unsupported compression format: LZMA")
case *OSMPBF.Blob_Lz4Data:
return nil, errors.New("unsupported compression format: LZ4")
case *OSMPBF.Blob_ZstdData:
return nil, errors.New("unsupported compression format: ZSTD")
case *OSMPBF.Blob_OBSOLETEBzip2Data:
return nil, errors.New("deprecated compression format: bzip2")
default:
return nil, errors.New("unknown blob data")
}
}
func decodeCoord(offset, granularity, coord int64) float64 {
return 1e-9 * float64((offset + (granularity * coord)))
}
// Make tags from stringtable and two parallel arrays of IDs
func extractTags(st stringTable, keyIDs, valueIDs []uint32) (OSMTags, error) {
if len(keyIDs) != len(valueIDs) {
return nil, errors.New("length of tag key and value arrays differs")
}
tags := NewOSMTags(len(keyIDs))
for index, keyID := range keyIDs {
key, val, err := st.extractTag(int(keyID), int(valueIDs[index]))
if err != nil {
return nil, err
}
tags.Set(key, val)
}
return tags, nil
}
/* String Table */
type stringTable []string
func (st stringTable) get(i int) (string, error) {
if i >= len(st) {
return "", errors.New("String table index out of bounds")
}
return st[i], nil
}
func (st stringTable) extractTag(keyID, valueID int) (key, val string, err error) {
if key, err = st.get(keyID); err != nil {
return
}
if val, err = st.get(valueID); err != nil {
return
}
return
}