/
gpx.go
107 lines (88 loc) · 2.91 KB
/
gpx.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
package main
import (
"encoding/xml"
"fmt"
"io/ioutil"
"log"
"math"
"github.com/tchayen/triangolatte"
)
// GPX is the root element in the XML file.
type GPX struct {
Trk Trk `xml:"trk"`
Time string `xml:"metadata>time"`
}
// Trk represents a track - an ordered list of points describing a path.
type Trk struct {
Name string `xml:"name"`
Trkseg []Trkseg `xml:"trkseg"`
}
// Trkseg is a Track Segment - it holds a list of Track Points which are
// logically connected in order. To represent a single GPS track where GPS
// reception was lost, or the GPS receiver was turned off, start a new Track
// Segment for each continuous span of track data.
type Trkseg struct {
Trkpt []Trkpt `xml:"trkpt"`
}
// Trkpt is a Track Point - geographic point with optional elevation and time.
type Trkpt struct {
Latitude float64 `xml:"lat,attr"`
Longitude float64 `xml:"lon,attr"`
Elevation float64 `xml:"ele"`
Time string `xml:"time"`
}
// Origin shift comes from the circumference of the Earth in meters (6378137).
const originShift = 2.0 * math.Pi * 6378137 / 2.0
// degreesToMeters converts longitude and latitude using WGS84 Geodetic Datum to
// meters with Spherical Mercator projection, known officially under EPSG:3857
// codename.
//
// X is longitude, Y is latitude.
func degreesToMeters(point triangolatte.Point) triangolatte.Point {
return triangolatte.Point{
X: point.X * originShift / 180.0,
Y: math.Log(math.Tan((90.0+point.Y)*math.Pi/360.0)) / (math.Pi / 180.0) * originShift / 180.0,
}
}
// XMLToGPX takes byte array from *.xml file and returns parsed GPX.
func XMLToGPX(data []byte) (gpx GPX, err error) {
err = xml.Unmarshal(data, &gpx)
return
}
// GPXToPoints takes parsed GPX data and returns array of arrays of points
// (divided into segments as in GPX source).
func GPXToPoints(gpx GPX) [][]triangolatte.Point {
segmentPoints := make([][]triangolatte.Point, len(gpx.Trk.Trkseg))
for i := range gpx.Trk.Trkseg {
segmentPoints[i] = make([]triangolatte.Point, len(gpx.Trk.Trkseg[i].Trkpt))
for j := range gpx.Trk.Trkseg[i].Trkpt {
trackPoint := gpx.Trk.Trkseg[i].Trkpt[j]
lon, lat := trackPoint.Longitude, trackPoint.Latitude
point := triangolatte.Point{X: lon, Y: lat}
segmentPoints[i][j] = degreesToMeters(point)
}
}
return segmentPoints
}
// triangulatePoints takes array of arrays of points and triangulates them.
func triangulatePoints(points [][]triangolatte.Point) [][]float64 {
triangles := make([][]float64, len(points))
for i := range points {
triangles[i], _ = triangolatte.Line(triangolatte.Miter, points[i], 2)
}
return triangles
}
func main() {
// Load data from file.
data, err := ioutil.ReadFile("../../assets/gpx_tmp")
if err != nil {
log.Fatal("Could not read file")
}
gpx, err := XMLToGPX(data)
if err != nil {
log.Fatal("Failed to parse GPX file")
}
segmentPoints := GPXToPoints(gpx)
triangles := triangulatePoints(segmentPoints)
fmt.Println(triangles)
}