mirror of
https://github.com/sstent/garminsync-go.git
synced 2026-02-03 21:11:36 +00:00
checkpoint 1
This commit is contained in:
100
internal/parser/gpx_parser.go
Normal file
100
internal/parser/gpx_parser.go
Normal file
@@ -0,0 +1,100 @@
|
||||
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{})
|
||||
}
|
||||
Reference in New Issue
Block a user