Compare commits
No commits in common. "dcaa38e5fe86ff660053800d83181f7d0999eb9a" and "7860a189b35c73835929ad4fd2e807dedd5e0056" have entirely different histories.
dcaa38e5fe
...
7860a189b3
@ -55,8 +55,9 @@ _Do not look below this line_
|
|||||||
|
|
||||||
## WIP, Puzzles and TODO
|
## WIP, Puzzles and TODO
|
||||||
|
|
||||||
|
* [done] Pos can't include elevation if it's sometimes unknown
|
||||||
* do we even need Track? will it ever be anything more than a collection
|
* do we even need Track? will it ever be anything more than a collection
|
||||||
of Points?
|
of Points?
|
||||||
* can we lose this "if isJust lat && isJust lon && isJust ts" wart?
|
* [done] need a real gpx file with namespace decls before we can parse power and stuff
|
||||||
* probably we should store points in a more efficient form than
|
* [done] tests seem to pass without <gpx> element?
|
||||||
a singly-linked list
|
* [done] stop returning bogus data when missing required elements (e.g. time)
|
||||||
|
@ -1,10 +1,4 @@
|
|||||||
module Main where
|
module Main where
|
||||||
|
|
||||||
import Control.Exception
|
|
||||||
import Track(parseFile)
|
|
||||||
import Data.List as List
|
|
||||||
|
|
||||||
main :: IO ()
|
main :: IO ()
|
||||||
main = do
|
main = putStrLn "Hello, Haskell!"
|
||||||
points <- Track.parseFile "track.gpx"
|
|
||||||
putStrLn ("loaded " ++ (show (List.length points)))
|
|
||||||
|
27
lib/Track.hs
27
lib/Track.hs
@ -10,7 +10,6 @@ module Track
|
|||||||
heartRate,
|
heartRate,
|
||||||
time,
|
time,
|
||||||
parse,
|
parse,
|
||||||
parseFile,
|
|
||||||
Track.length,
|
Track.length,
|
||||||
)
|
)
|
||||||
where
|
where
|
||||||
@ -21,16 +20,14 @@ import Data.List as List
|
|||||||
import Data.List qualified
|
import Data.List qualified
|
||||||
import Data.Map as Map
|
import Data.Map as Map
|
||||||
import Data.Maybe
|
import Data.Maybe
|
||||||
import Data.Either
|
|
||||||
import Data.Text qualified
|
import Data.Text qualified
|
||||||
import Data.Text.Lazy as T
|
import Data.Text.Lazy as T
|
||||||
import Data.Time
|
import Data.Time
|
||||||
import Data.Time.ISO8601 qualified
|
import Data.Time.ISO8601 qualified
|
||||||
import Debug.Trace (trace, traceShow)
|
import Debug.Trace (trace, traceShow)
|
||||||
import Text.Read (readMaybe)
|
|
||||||
import Text.XML
|
import Text.XML
|
||||||
import Text.XML.Cursor as Cursor
|
import Text.XML.Cursor as Cursor
|
||||||
|
import Text.Read (readMaybe)
|
||||||
data Pos = Pos Float Float (Maybe Float) deriving (Show, Eq)
|
data Pos = Pos Float Float (Maybe Float) deriving (Show, Eq)
|
||||||
|
|
||||||
type Power = Maybe Int
|
type Power = Maybe Int
|
||||||
@ -63,12 +60,14 @@ instance Exception BadFile
|
|||||||
|
|
||||||
elToPoint :: Cursor -> Either SomeException Point
|
elToPoint :: Cursor -> Either SomeException Point
|
||||||
elToPoint c =
|
elToPoint c =
|
||||||
let lat = listToMaybe (attribute "lat" c) >>= asFloat
|
case node c of
|
||||||
lon = listToMaybe (attribute "lon" c) >>= asFloat
|
NodeElement (Element _ attrs _) ->
|
||||||
|
let lat = (listToMaybe $ attribute "lat" c ) >>= asFloat
|
||||||
|
lon = (listToMaybe $ attribute "lon" c ) >>= asFloat
|
||||||
|
ele = listToMaybe $ child c >>= element (gpxNS "ele") >>= child >>= content
|
||||||
ts =
|
ts =
|
||||||
listToMaybe (child c >>= element (gpxNS "time") >>= child >>= content)
|
listToMaybe (child c >>= element (gpxNS "time") >>= child >>= content)
|
||||||
>>= (Data.Time.ISO8601.parseISO8601 . Data.Text.unpack)
|
>>= (Data.Time.ISO8601.parseISO8601 . Data.Text.unpack)
|
||||||
ele = listToMaybe (child c >>= element (gpxNS "ele") >>= child >>= content) >>= asFloat
|
|
||||||
gpxtpx =
|
gpxtpx =
|
||||||
child c
|
child c
|
||||||
>>= element (gpxNS "extensions")
|
>>= element (gpxNS "extensions")
|
||||||
@ -85,15 +84,16 @@ elToPoint c =
|
|||||||
then
|
then
|
||||||
Right $
|
Right $
|
||||||
Point
|
Point
|
||||||
(Pos (fromJust lat) (fromJust lon) ele)
|
(Pos (fromJust lat) (fromJust lon) (ele >>= asFloat))
|
||||||
(fromJust ts)
|
(fromJust ts)
|
||||||
(listToMaybe cadence >>= asInt)
|
(listToMaybe cadence >>= asInt)
|
||||||
(listToMaybe power >>= asInt)
|
(listToMaybe power >>= asInt)
|
||||||
(listToMaybe hr >>= asInt)
|
(listToMaybe hr >>= asInt)
|
||||||
else Left (toException (BadFile "missing a required attribute"))
|
else Left (toException (BadFile "missing a required attribute"))
|
||||||
where
|
where
|
||||||
asFloat v = (readMaybe :: String -> Maybe Float) (Data.Text.unpack v)
|
asFloat v = return (Data.Text.unpack v) >>= (readMaybe :: String -> Maybe Float)
|
||||||
asInt v = (readMaybe :: String -> Maybe Int) (Data.Text.unpack v)
|
asInt v = return (Data.Text.unpack v) >>= (readMaybe :: String -> Maybe Int)
|
||||||
|
_ -> Left (toException (BadFile "did not find trkpt"))
|
||||||
|
|
||||||
getPoints :: Cursor -> Either SomeException [Point]
|
getPoints :: Cursor -> Either SomeException [Point]
|
||||||
getPoints c =
|
getPoints c =
|
||||||
@ -112,10 +112,3 @@ parse str = do
|
|||||||
|
|
||||||
length :: Track -> Int
|
length :: Track -> Int
|
||||||
length = Data.List.length
|
length = Data.List.length
|
||||||
|
|
||||||
-- parseFile :: FilePath -> IO [Point]
|
|
||||||
parseFile name = do
|
|
||||||
gpx <- Text.XML.readFile def name
|
|
||||||
return $ case getPoints (fromDocument gpx) of
|
|
||||||
Left err -> []
|
|
||||||
Right points -> points
|
|
||||||
|
@ -68,9 +68,7 @@ executable souplesse
|
|||||||
-- other-extensions:
|
-- other-extensions:
|
||||||
|
|
||||||
-- Other library packages from which modules are imported.
|
-- Other library packages from which modules are imported.
|
||||||
build-depends:
|
build-depends: base ^>=4.18.2.1
|
||||||
base ^>=4.18.2.1
|
|
||||||
, souplesse-lib
|
|
||||||
|
|
||||||
-- Directories containing source files.
|
-- Directories containing source files.
|
||||||
hs-source-dirs: app
|
hs-source-dirs: app
|
||||||
|
@ -4,8 +4,8 @@ module Main where
|
|||||||
|
|
||||||
import Control.Exception
|
import Control.Exception
|
||||||
import Data.Either
|
import Data.Either
|
||||||
import Data.List as List
|
|
||||||
import Data.Time qualified
|
import Data.Time qualified
|
||||||
|
import Data.List as List
|
||||||
import Debug.Trace (trace, traceShow)
|
import Debug.Trace (trace, traceShow)
|
||||||
import System.Exit qualified as Exit
|
import System.Exit qualified as Exit
|
||||||
import Test.HUnit
|
import Test.HUnit
|
||||||
@ -84,8 +84,8 @@ testMalformed =
|
|||||||
in assertBool "catches syntax error" (isLeft trk)
|
in assertBool "catches syntax error" (isLeft trk)
|
||||||
|
|
||||||
testMissingAttrs =
|
testMissingAttrs =
|
||||||
let els =
|
let els = [
|
||||||
[ [r|
|
[r|
|
||||||
<trkpt lon="2" latsdf="51">
|
<trkpt lon="2" latsdf="51">
|
||||||
<time>2024-10-23T08:34:59.779+01:00</time>
|
<time>2024-10-23T08:34:59.779+01:00</time>
|
||||||
</trkpt>
|
</trkpt>
|
||||||
@ -101,14 +101,18 @@ testMissingAttrs =
|
|||||||
</trkpt>
|
</trkpt>
|
||||||
|]
|
|]
|
||||||
]
|
]
|
||||||
in TestCase $
|
in
|
||||||
|
TestCase $
|
||||||
assertBool
|
assertBool
|
||||||
"failed to catch missing/malformed attribute"
|
"failed to catch missing/malformed attribute"
|
||||||
(List.all (isLeft . Track.parse . wrapPoint) els)
|
(List.all isLeft
|
||||||
|
(List.map (\ text -> Track.parse (wrapPoint text))
|
||||||
|
els))
|
||||||
|
|
||||||
|
|
||||||
test2 =
|
test2 =
|
||||||
TestList
|
TestList [
|
||||||
[ TestCase $
|
TestCase $
|
||||||
either
|
either
|
||||||
(assertFailure . displayException)
|
(assertFailure . displayException)
|
||||||
( \(p : _) ->
|
( \(p : _) ->
|
||||||
@ -130,6 +134,7 @@ test2 =
|
|||||||
(Track.parse onepointWithAttrs)
|
(Track.parse onepointWithAttrs)
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
test3 =
|
test3 =
|
||||||
TestCase $
|
TestCase $
|
||||||
either
|
either
|
||||||
|
Loading…
Reference in New Issue
Block a user