mirror of
https://github.com/sstent/garminsync-go.git
synced 2025-12-06 08:01:52 +00:00
101 lines
2.3 KiB
Go
101 lines
2.3 KiB
Go
package parser
|
|
|
|
import (
|
|
"encoding/xml"
|
|
"math"
|
|
"time"
|
|
|
|
"github.com/yourusername/garminsync/internal/parser/activity"
|
|
)
|
|
|
|
// GPX represents the root element of a GPX file
|
|
type GPX struct {
|
|
XMLName xml.Name `xml:"gpx"`
|
|
Trk Trk `xml:"trk"`
|
|
}
|
|
|
|
// Trk represents a track in a GPX file
|
|
type Trk struct {
|
|
Name string `xml:"name"`
|
|
TrkSeg []TrkSeg `xml:"trkseg"`
|
|
}
|
|
|
|
// TrkSeg represents a track segment in a GPX file
|
|
type TrkSeg struct {
|
|
TrkPt []TrkPt `xml:"trkpt"`
|
|
}
|
|
|
|
// TrkPt represents a track point in a GPX file
|
|
type TrkPt struct {
|
|
Lat float64 `xml:"lat,attr"`
|
|
Lon float64 `xml:"lon,attr"`
|
|
Ele float64 `xml:"ele"`
|
|
Time string `xml:"time"`
|
|
}
|
|
|
|
// GPXParser implements the Parser interface for GPX files
|
|
type GPXParser struct{}
|
|
|
|
func (p *GPXParser) Parse(data []byte) (*activity.Activity, error) {
|
|
var gpx GPX
|
|
if err := xml.Unmarshal(data, &gpx); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if len(gpx.Trk.TrkSeg) == 0 || len(gpx.Trk.TrkSeg[0].TrkPt) == 0 {
|
|
return nil, ErrNoTrackData
|
|
}
|
|
|
|
// Process track points
|
|
points := gpx.Trk.TrkSeg[0].TrkPt
|
|
startTime, _ := time.Parse(time.RFC3339, points[0].Time)
|
|
endTime, _ := time.Parse(time.RFC3339, points[len(points)-1].Time)
|
|
|
|
activity := &activity.Activity{
|
|
ActivityType: "hiking",
|
|
StartTime: startTime,
|
|
Duration: int(endTime.Sub(startTime).Seconds()),
|
|
StartLatitude: points[0].Lat,
|
|
StartLongitude: points[0].Lon,
|
|
}
|
|
|
|
// Calculate distance and elevation
|
|
var totalDistance, elevationGain float64
|
|
prev := points[0]
|
|
|
|
for i := 1; i < len(points); i++ {
|
|
curr := points[i]
|
|
totalDistance += haversine(prev.Lat, prev.Lon, curr.Lat, curr.Lon)
|
|
|
|
if curr.Ele > prev.Ele {
|
|
elevationGain += curr.Ele - prev.Ele
|
|
}
|
|
prev = curr
|
|
}
|
|
|
|
activity.Distance = totalDistance
|
|
activity.ElevationGain = elevationGain
|
|
|
|
return activity, nil
|
|
}
|
|
|
|
// haversine calculates the distance between two points on Earth
|
|
func haversine(lat1, lon1, lat2, lon2 float64) float64 {
|
|
const R = 6371000 // Earth radius in meters
|
|
φ1 := lat1 * math.Pi / 180
|
|
φ2 := lat2 * math.Pi / 180
|
|
Δφ := (lat2 - lat1) * math.Pi / 180
|
|
Δλ := (lon2 - lon1) * math.Pi / 180
|
|
|
|
a := math.Sin(Δφ/2)*math.Sin(Δφ/2) +
|
|
math.Cos(φ1)*math.Cos(φ2)*
|
|
math.Sin(Δλ/2)*math.Sin(Δλ/2)
|
|
c := 2 * math.Atan2(math.Sqrt(a), math.Sqrt(1-a))
|
|
|
|
return R * c
|
|
}
|
|
|
|
func init() {
|
|
RegisterParser(".gpx", &GPXParser{})
|
|
}
|