-
Notifications
You must be signed in to change notification settings - Fork 1
/
file.go
117 lines (94 loc) · 2.68 KB
/
file.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
package srtm
import (
"compress/gzip"
"encoding/binary"
"io"
"os"
"regexp"
"strings"
"github.com/pkg/errors"
)
// ErrInvalidHGTFileName is returned when a HGT file name does not match
// pattern of a valid file which indicates lat/lon
var ErrInvalidHGTFileName = errors.New("invalid HGT file name")
// should this be option to support non-30m data?
const squareSize = 3601 // 30m SRTM data is 3601 squares tall/wide
var srtmParseName = regexp.MustCompile(`(N|S)(\d\d)(E|W)(\d\d\d)\.hgt(\.gz)?`)
// ReadFile is a helper func around Read that reads a SRTM file, decompressing
// if necessary, and returns SRTM elevation data
func ReadFile(file string) (points []Point, err error) {
f, err := os.Open(file)
if err != nil {
return points, err
}
defer f.Close()
if strings.HasSuffix(file, ".gz") {
rdr, err := gzip.NewReader(f)
if err != nil {
return points, err
}
defer rdr.Close()
return Read(file, rdr)
}
return Read(file, f)
}
// Read reads elevation for points from a SRTM file
func Read(fname string, r io.Reader) (points []Point, err error) {
swCorner, err := GetFileCorner(fname)
if err != nil {
return points, errors.Wrap(err, "could not get corner coordinates from file name")
}
points = make([]Point, squareSize*squareSize)
pIdx := 0
// Latitude
for row := 0; row < squareSize; row++ {
lat := swCorner.Latitude + float64(row)/float64(squareSize)
// Longitude
for col := 0; col < squareSize; col++ {
lon := swCorner.Longitude + float64(col)/float64(squareSize)
var elev int16
readErr := binary.Read(r, binary.BigEndian, &elev)
if readErr != nil {
return points, errors.Wrapf(err, "EOF before %d?", squareSize)
}
points[pIdx] = Point{
Latitude: lat,
Longitude: lon,
Elevation: elev,
}
pIdx++
}
}
return points, nil
}
// GetFileCorner returns the southwest point contained in a HGT file.
// Coordinates in the file are relative to this point
func GetFileCorner(file string) (p Point, err error) {
fnameParts := srtmParseName.FindStringSubmatch(file)
if fnameParts == nil {
return p, ErrInvalidHGTFileName
}
swLatitude, err := dToDecimal(fnameParts[1] + fnameParts[2])
if err != nil {
return p, errors.Wrap(err, "could not get Latitude from file name")
}
swLongitude, err := dToDecimal(fnameParts[3] + fnameParts[4])
if err != nil {
return p, errors.Wrap(err, "could not get Longitude from file name")
}
p = Point{
Latitude: swLatitude,
Longitude: swLongitude,
}
return p, err
}
// IsHGT returns true if fname appears to be a SRTM HGT file
func IsHGT(fname string) bool {
if strings.HasSuffix(fname, ".hgt") {
return true
}
if strings.HasSuffix(fname, ".hgt.gz") {
return true
}
return false
}