forked from fulltrend/go-grib2
-
Notifications
You must be signed in to change notification settings - Fork 1
/
reader.go
135 lines (110 loc) · 2.71 KB
/
reader.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
package grib2
import (
"encoding/binary"
"time"
"github.com/mssalnikov/go-grib2/internal"
"github.com/pkg/errors"
)
// GRIB2 is simplified GRIB2 file structure
type GRIB2 struct {
RefTime time.Time
VerfTime time.Time
ParamId int
Name string
Description string
Unit string
Level string
Values []Value
}
// Value is data item of GRIB2 file
type Value struct {
Longitude float64
Latitude float64
Value float32
}
// Read reads raw GRIB2 files and return slice of structured GRIB2 data
func Read(data []byte) ([]GRIB2, error) {
if data == nil {
return nil, errors.New("Raw data is nil")
}
dlen := len(data)
if dlen < 4 {
return nil, errors.New("Raw data should be 4 bytes at least")
}
gribs := []GRIB2{}
start := 0
eod := false
for !eod {
if string(data[0:4]) != "GRIB" {
return nil, errors.New("First 4 bytes of raw data must be 'GRIB'")
}
grib := GRIB2{
Values: []Value{},
}
sections := [][]byte{nil, nil, nil, nil, nil, nil, nil, nil}
size := 16
sections[0] = data[start : start+size]
start += size
prv := -1
cur := 0
eof := false
for !eof {
prv = cur
if prv == 7 {
// block is read -> export data to values
grib.RefTime = internal.RefTime(sections)
var err error
grib.VerfTime, err = internal.VerfTime(sections)
if err != nil {
return nil, errors.Wrapf(err, "Failed to get VerfTime")
}
grib.Name, grib.Description, grib.Unit, err = internal.GetInfo(sections)
if err != nil {
return nil, errors.Wrapf(err, "Failed to GetInfo")
}
grib.Level, err = internal.GetLevel(sections)
if err != nil {
return nil, errors.Wrapf(err, "Failed to GetLevel")
}
grib.ParamId = internal.GetParamId(sections)
var lon, lat []float64
err = internal.LatLon(sections, &lon, &lat)
if err != nil {
return nil, errors.Wrapf(err, "Failed to get longitude and latitude")
}
raw, err := internal.UnpackData(sections)
if err != nil {
return nil, errors.Wrapf(err, "Failed to unpack data")
}
c := len(lon)
v := make([]Value, c, c)
for i := 0; i < c; i++ {
v[i].Longitude = lon[i]
v[i].Latitude = lat[i]
v[i].Value = raw[i]
}
grib.Values = append(grib.Values, v...)
sections[2] = nil
sections[3] = nil
sections[4] = nil
sections[5] = nil
sections[6] = nil
sections[7] = nil
if string(data[start:start+4]) == "7777" {
eof = true
size = 4
}
} else {
size = int(binary.BigEndian.Uint32(data[start:]))
cur = int(data[start+4])
sections[cur] = data[start : start+size]
}
start += size
}
gribs = append(gribs, grib)
if start == dlen {
eod = true
}
}
return gribs, nil
}