souplesse/lib/Track.hs

124 lines
3.0 KiB
Haskell
Raw Normal View History

2024-10-28 23:35:36 +00:00
{-# LANGUAGE OverloadedStrings #-}
2024-10-30 21:03:11 +00:00
module Track
( Track,
Pos (..),
pos,
elevation,
cadence,
power,
time,
parse,
Track.length,
)
where
import Control.Exception
import Data.List as List
import Data.List qualified
import Data.Map as Map
import Data.Maybe
2024-10-30 21:03:11 +00:00
import Data.Text qualified
import Data.Text.Lazy as T
2024-10-27 23:13:39 +00:00
import Data.Time
2024-10-30 21:03:11 +00:00
import Data.Time.ISO8601 qualified
import Debug.Trace (trace, traceShow)
2024-10-28 23:35:36 +00:00
import Text.XML
import Text.XML.Cursor as Cursor
data Pos = Pos Float Float deriving (Show, Eq)
2024-10-30 21:03:11 +00:00
2024-10-28 23:35:36 +00:00
type Power = Maybe Int
2024-10-30 21:03:11 +00:00
2024-10-28 23:35:36 +00:00
type Cadence = Maybe Int
2024-10-30 21:03:11 +00:00
2024-10-28 23:35:36 +00:00
type HeartRate = Maybe Int
2024-10-30 21:03:11 +00:00
data Point = Point
{ pos :: Pos,
elevation :: Maybe Float,
time :: UTCTime,
cadence :: Cadence,
power :: Power,
heartRate :: HeartRate
}
deriving (Show)
2024-10-27 23:13:39 +00:00
2024-10-29 19:35:17 +00:00
-- TODO do we even need this type?
2024-10-27 23:13:39 +00:00
type Track = [Point]
2024-10-30 17:17:48 +00:00
gpxNS localName =
Name localName (Just "http://www.topografix.com/GPX/1/1") Nothing
tpxNS localName =
Name localName (Just "http://www.garmin.com/xmlschemas/TrackPointExtension/v2") Nothing
2024-10-30 13:30:33 +00:00
2024-10-28 23:35:36 +00:00
mkPoint pos =
Point
pos
2024-10-29 21:22:49 +00:00
Nothing
2024-10-30 21:03:11 +00:00
(UTCTime (toEnum 60631) 43200)
2024-10-30 17:17:48 +00:00
Nothing
Nothing
Nothing
2024-10-28 23:35:36 +00:00
elToPoint :: Cursor -> Point
2024-10-29 21:22:49 +00:00
elToPoint c =
case node c of
2024-10-28 23:35:36 +00:00
NodeElement (Element _ attrs _) ->
2024-10-30 21:03:11 +00:00
let lat = getAttr "lat"
lon = getAttr "lon"
ele = child c >>= element (gpxNS "ele") >>= child >>= content
ts = child c >>= element (gpxNS "time") >>= child >>= content
gpxtpx =
child c
>>= element (gpxNS "extensions")
>>= child
>>= element (tpxNS "TrackPointExtension")
>>= child
cadence =
gpxtpx
>>= element (tpxNS "cad")
>>= child
>>= content
power =
gpxtpx
>>= element (Name "PowerInWatts" (Just "http://www.garmin.com/xmlschemas/PowerExtension/v1") Nothing)
>>= child
>>= content
in Point
(Pos lat lon)
( (listToMaybe ele) >>= return . asFloat )
2024-10-30 21:03:11 +00:00
( case ts of
[e] -> case Data.Time.ISO8601.parseISO8601 (Data.Text.unpack e) of
Just utime -> utime
_ -> UTCTime (toEnum 0) 0
_ -> UTCTime (toEnum 0) 0
)
( (listToMaybe cadence) >>= return . asInt)
( (listToMaybe power) >>= return . asInt)
2024-10-30 21:03:11 +00:00
Nothing
2024-10-29 21:22:49 +00:00
where
asFloat v = (read (Data.Text.unpack v) :: Float)
2024-10-30 17:17:48 +00:00
asInt v = (read (Data.Text.unpack v) :: Int)
getAttr name = maybe 0 asFloat (Map.lookup name attrs)
_ -> mkPoint (Pos 0 0)
2024-10-28 23:35:36 +00:00
getPoints :: Cursor -> [Point]
getPoints c =
2024-10-30 21:03:11 +00:00
let trkpts =
element (gpxNS "gpx") c
>>= child
>>= element (gpxNS "trk")
>>= descendant
>>= element (gpxNS "trkpt")
in List.map elToPoint trkpts
2024-10-28 23:35:36 +00:00
parse :: String -> Either SomeException [Point]
parse str = do
gpx <- parseText def (T.pack str)
return (getPoints (fromDocument gpx))
2024-10-28 23:35:36 +00:00
2024-10-27 23:13:39 +00:00
length :: Track -> Int
2024-10-30 14:25:54 +00:00
length = Data.List.length