121 lines
2.6 KiB
Go
121 lines
2.6 KiB
Go
|
package openname
|
||
|
|
||
|
import (
|
||
|
"math"
|
||
|
"os"
|
||
|
"strings"
|
||
|
"time"
|
||
|
|
||
|
"github.com/dhconnelly/rtreego"
|
||
|
"github.com/fofanov/go-osgb"
|
||
|
"github.com/twpayne/go-gpx"
|
||
|
)
|
||
|
|
||
|
type GPXSummarizer struct {
|
||
|
rt *rtreego.Rtree
|
||
|
trans osgb.CoordinateTransformer
|
||
|
}
|
||
|
|
||
|
func NewGPXSummarizer(rt *rtreego.Rtree) (*GPXSummarizer, error) {
|
||
|
trans, err := osgb.NewOSTN15Transformer()
|
||
|
if err != nil {
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
}
|
||
|
return &GPXSummarizer{rt: rt, trans: trans}, nil
|
||
|
}
|
||
|
|
||
|
func insideLoc(p rtreego.Point, loc *Record) bool {
|
||
|
return p[0] >= loc.MbrXMin && p[0] <= loc.MbrXMax && p[1] >= loc.MbrYMin && p[1] <= loc.MbrYMax
|
||
|
}
|
||
|
|
||
|
func distance(p1, p2 rtreego.Point) float64 {
|
||
|
if len(p1) != len(p2) {
|
||
|
panic("Length mismatch")
|
||
|
}
|
||
|
var s float64
|
||
|
for i := range p1 {
|
||
|
d := p1[i] - p2[i]
|
||
|
s += d * d
|
||
|
}
|
||
|
return math.Sqrt(s) / 1000.0
|
||
|
}
|
||
|
|
||
|
type POI struct {
|
||
|
Name string
|
||
|
Distance float64
|
||
|
}
|
||
|
|
||
|
type TrackSummary struct {
|
||
|
Name string
|
||
|
Time time.Time
|
||
|
Link string
|
||
|
Start string
|
||
|
Finish string
|
||
|
Distance float64
|
||
|
Ascent float64
|
||
|
PointsOfInterest []POI
|
||
|
}
|
||
|
|
||
|
func (gs *GPXSummarizer) SummarizeTrack(filename string) (*TrackSummary, error) {
|
||
|
r, err := os.Open(filename)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
g, err := gpx.Read(r)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
var s TrackSummary
|
||
|
s.Name = g.Metadata.Name
|
||
|
s.Time = g.Metadata.Time
|
||
|
for _, l := range g.Metadata.Link {
|
||
|
if strings.HasPrefix(l.HREF, "http") {
|
||
|
s.Link = l.HREF
|
||
|
break
|
||
|
}
|
||
|
}
|
||
|
|
||
|
var prevPlace string
|
||
|
var prevPoint rtreego.Point
|
||
|
var prevHeight float64
|
||
|
|
||
|
init := true
|
||
|
for _, trk := range g.Trk {
|
||
|
for _, seg := range trk.TrkSeg {
|
||
|
for _, p := range seg.TrkPt {
|
||
|
gpsCoord := osgb.NewETRS89Coord(p.Lon, p.Lat, p.Ele)
|
||
|
ngCoord, err := gs.trans.ToNationalGrid(gpsCoord)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
thisPoint := rtreego.Point{ngCoord.Easting, ngCoord.Northing}
|
||
|
thisHeight := ngCoord.Height
|
||
|
nn, _ := gs.rt.NearestNeighbor(thisPoint).(*Record)
|
||
|
if init {
|
||
|
s.Start = nn.Name
|
||
|
prevPlace = nn.Name
|
||
|
prevPoint = thisPoint
|
||
|
prevHeight = thisHeight
|
||
|
s.PointsOfInterest = append(s.PointsOfInterest, POI{nn.Name, 0.0})
|
||
|
init = false
|
||
|
continue
|
||
|
}
|
||
|
s.Distance += distance(thisPoint, prevPoint)
|
||
|
if ascent := thisHeight - prevHeight; ascent > 0 {
|
||
|
s.Ascent += ascent
|
||
|
}
|
||
|
if insideLoc(thisPoint, nn) && nn.Name != prevPlace {
|
||
|
s.PointsOfInterest = append(s.PointsOfInterest, POI{nn.Name, s.Distance})
|
||
|
prevPlace = nn.Name
|
||
|
}
|
||
|
prevPoint = thisPoint
|
||
|
prevHeight = thisHeight
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
s.Finish = prevPlace
|
||
|
return &s, nil
|
||
|
}
|