Compare commits
No commits in common. "e0d395ba9f2d8e2bedde9f604dea8d9d31c961fb" and "12a547c7aafa413cedf673a05d7d66b102084cf3" have entirely different histories.
e0d395ba9f
...
12a547c7aa
1
Makefile
1
Makefile
@ -4,5 +4,4 @@ dist-newstyle/build/x86_64-linux/ghc-9.6.5/souplesse-0.1.0.0/x/souplesse/build/s
|
|||||||
cabal build
|
cabal build
|
||||||
|
|
||||||
frontend/frontend.js: frontend/src/Main.elm frontend/src/Lib.elm frontend/src/Point.elm
|
frontend/frontend.js: frontend/src/Main.elm frontend/src/Lib.elm frontend/src/Point.elm
|
||||||
elm-test frontend/tests/
|
|
||||||
elm make --output=$@ $<
|
elm make --output=$@ $<
|
||||||
|
37
README.md
37
README.md
@ -117,43 +117,6 @@ change depending on the nature of the training effort. e.g.
|
|||||||
for a long slow ride we show total distance, for interval training
|
for a long slow ride we show total distance, for interval training
|
||||||
we show time spent in HR zones ...
|
we show time spent in HR zones ...
|
||||||
|
|
||||||
----
|
|
||||||
|
|
||||||
time axis wants to show ticks which are at least (portalWidth/4)
|
|
||||||
pixels apart, and choose the smallest of the following time intervals
|
|
||||||
which are greater than that width
|
|
||||||
|
|
||||||
1 second
|
|
||||||
5 seconds
|
|
||||||
15 seconds
|
|
||||||
30 seconds
|
|
||||||
60 seconds
|
|
||||||
5 minutes
|
|
||||||
15 minutes
|
|
||||||
1 hour
|
|
||||||
3 hours
|
|
||||||
6 hours
|
|
||||||
24 hours
|
|
||||||
|
|
||||||
if the full width is 240 seconds, show a tick every 60 second
|
|
||||||
if the full width is 20 seconds, show a tick every 5 seconds
|
|
||||||
|
|
||||||
if the width grows past 20 seconds, the distance between points shrinks
|
|
||||||
and therefore we put ticks less often
|
|
||||||
|
|
||||||
if width <= 4 * 5, 5 second tick
|
|
||||||
if width <= 4 * 15, 15 second tick
|
|
||||||
etc ...
|
|
||||||
|
|
||||||
to convert time to pixels, multiply by portalWidth / duration
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
portalWidth / duration
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Postgres
|
## Postgres
|
||||||
|
|
||||||
|
1
elm.json
1
elm.json
@ -15,7 +15,6 @@
|
|||||||
"elm/svg": "1.0.1",
|
"elm/svg": "1.0.1",
|
||||||
"elm/time": "1.0.0",
|
"elm/time": "1.0.0",
|
||||||
"elm/url": "1.0.0",
|
"elm/url": "1.0.0",
|
||||||
"elm-community/list-extra": "8.7.0",
|
|
||||||
"elm-explorations/test": "2.2.0",
|
"elm-explorations/test": "2.2.0",
|
||||||
"mpizenberg/elm-pointer-events": "5.0.0",
|
"mpizenberg/elm-pointer-events": "5.0.0",
|
||||||
"rtfeldman/elm-iso8601-date-strings": "1.1.4",
|
"rtfeldman/elm-iso8601-date-strings": "1.1.4",
|
||||||
|
@ -8,7 +8,6 @@ import Html.Events exposing (onClick, on)
|
|||||||
import Html.Events.Extra.Pointer as Pointer
|
import Html.Events.Extra.Pointer as Pointer
|
||||||
import Maybe exposing (Maybe)
|
import Maybe exposing (Maybe)
|
||||||
import Lib exposing(..)
|
import Lib exposing(..)
|
||||||
import List.Extra exposing(find)
|
|
||||||
import Json.Decode as D
|
import Json.Decode as D
|
||||||
import Http
|
import Http
|
||||||
import Point exposing(Point, Pos ,decoder)
|
import Point exposing(Point, Pos ,decoder)
|
||||||
@ -24,7 +23,6 @@ import Svg.Attributes as S exposing
|
|||||||
, fill
|
, fill
|
||||||
, points
|
, points
|
||||||
, stroke, strokeWidth, strokeOpacity)
|
, stroke, strokeWidth, strokeOpacity)
|
||||||
import Time exposing(Posix)
|
|
||||||
import Url.Parser exposing (Parser, (</>), (<?>), int, map, oneOf, s, string)
|
import Url.Parser exposing (Parser, (</>), (<?>), int, map, oneOf, s, string)
|
||||||
import Url.Parser.Query as Query
|
import Url.Parser.Query as Query
|
||||||
import Url exposing (Url)
|
import Url exposing (Url)
|
||||||
@ -240,40 +238,8 @@ newModel msg model =
|
|||||||
NewUrlRequest -> model
|
NewUrlRequest -> model
|
||||||
UrlChanged -> model
|
UrlChanged -> model
|
||||||
|
|
||||||
|
|
||||||
-- VIEW
|
-- VIEW
|
||||||
|
|
||||||
formatTime epoch =
|
|
||||||
let utc = Time.utc
|
|
||||||
time = Time.millisToPosix <| floor(epoch * 1000)
|
|
||||||
zeroed i = String.padLeft 2 '0' (String.fromInt i)
|
|
||||||
in String.fromInt (Time.toHour utc time)
|
|
||||||
++ ":" ++
|
|
||||||
zeroed (Time.toMinute utc time)
|
|
||||||
++ ":" ++
|
|
||||||
zeroed (Time.toSecond utc time)
|
|
||||||
|
|
||||||
timeTick duration =
|
|
||||||
let width = duration / 6
|
|
||||||
candidates =
|
|
||||||
[ 1
|
|
||||||
, 3
|
|
||||||
, 5
|
|
||||||
, 10
|
|
||||||
, 15
|
|
||||||
, 30
|
|
||||||
, 60
|
|
||||||
, 60 * 3
|
|
||||||
, 60 * 5
|
|
||||||
, 60 * 10
|
|
||||||
, 60 * 15
|
|
||||||
, 60 * 30
|
|
||||||
, 60 * 60
|
|
||||||
]
|
|
||||||
in case List.Extra.find (\ candidate -> width <= candidate) candidates of
|
|
||||||
Just n -> n
|
|
||||||
Nothing -> width
|
|
||||||
|
|
||||||
tileUrl : TileNumber -> ZoomLevel -> String
|
tileUrl : TileNumber -> ZoomLevel -> String
|
||||||
tileUrl {x,y} z =
|
tileUrl {x,y} z =
|
||||||
String.concat ["https://a.tile.openstreetmap.org",
|
String.concat ["https://a.tile.openstreetmap.org",
|
||||||
@ -306,8 +272,6 @@ measureView title colour fn allPoints =
|
|||||||
rangeYaxis = maxYaxis - minYaxis
|
rangeYaxis = maxYaxis - minYaxis
|
||||||
maxX = Point.duration allPoints
|
maxX = Point.duration allPoints
|
||||||
string = String.concat (List.map coords filteredPoints)
|
string = String.concat (List.map coords filteredPoints)
|
||||||
ttick = timeTick maxX
|
|
||||||
firstTimeTick = (toFloat (floor(startTime / ttick))) * ttick - startTime
|
|
||||||
ybar n = line
|
ybar n = line
|
||||||
[ fill "none"
|
[ fill "none"
|
||||||
, style "vector-effect" "non-scaling-stroke"
|
, style "vector-effect" "non-scaling-stroke"
|
||||||
@ -318,24 +282,6 @@ measureView title colour fn allPoints =
|
|||||||
, x2 (String.fromFloat (0.95 * maxX))
|
, x2 (String.fromFloat (0.95 * maxX))
|
||||||
, y2 (String.fromFloat (minYaxis + n * tickY))
|
, y2 (String.fromFloat (minYaxis + n * tickY))
|
||||||
] []
|
] []
|
||||||
xtick n =
|
|
||||||
let t = firstTimeTick + n * timeTick maxX
|
|
||||||
xpix = t * portalWidth/maxX
|
|
||||||
label = formatTime (t + startTime)
|
|
||||||
in
|
|
||||||
g []
|
|
||||||
[ line
|
|
||||||
[ fill "none"
|
|
||||||
, style "vector-effect" "non-scaling-stroke"
|
|
||||||
, strokeWidth "1"
|
|
||||||
, stroke "#aaa"
|
|
||||||
, x1 (String.fromFloat xpix)
|
|
||||||
, y1 "0"
|
|
||||||
, x2 (String.fromFloat xpix)
|
|
||||||
, y2 "180"
|
|
||||||
] []
|
|
||||||
]
|
|
||||||
|
|
||||||
ylabel n = Svg.text_
|
ylabel n = Svg.text_
|
||||||
[ x "99%", y (String.fromFloat (graphHeight - graphHeight * n * (tickY/rangeYaxis)))
|
[ x "99%", y (String.fromFloat (graphHeight - graphHeight * n * (tickY/rangeYaxis)))
|
||||||
, style "text-anchor" "end"
|
, style "text-anchor" "end"
|
||||||
@ -384,62 +330,8 @@ measureView title colour fn allPoints =
|
|||||||
, ylabel 1
|
, ylabel 1
|
||||||
, ylabel 2
|
, ylabel 2
|
||||||
, ylabel 3
|
, ylabel 3
|
||||||
, xtick 0
|
|
||||||
, xtick 1
|
|
||||||
, xtick 2
|
|
||||||
, xtick 3
|
|
||||||
, xtick 4
|
|
||||||
, xtick 5
|
|
||||||
]
|
]
|
||||||
|
|
||||||
timeAxis allPoints =
|
|
||||||
let filteredPoints = Point.downsample 300 allPoints
|
|
||||||
graphHeight = 30
|
|
||||||
startTime = case allPoints of
|
|
||||||
(p::_) -> p.time
|
|
||||||
_ -> 0
|
|
||||||
maxX = Point.duration allPoints
|
|
||||||
ttick = timeTick maxX
|
|
||||||
firstTimeTick = (toFloat (floor(startTime / ttick))) * ttick - startTime
|
|
||||||
xtick n =
|
|
||||||
let t = firstTimeTick + (toFloat n) * timeTick maxX
|
|
||||||
xpix = t * portalWidth/maxX
|
|
||||||
label = formatTime (t + startTime)
|
|
||||||
in
|
|
||||||
g []
|
|
||||||
[ line
|
|
||||||
[ fill "none"
|
|
||||||
, style "vector-effect" "non-scaling-stroke"
|
|
||||||
, strokeWidth "1"
|
|
||||||
, stroke "#333"
|
|
||||||
, x1 (String.fromFloat xpix)
|
|
||||||
, y1 "0"
|
|
||||||
, x2 (String.fromFloat xpix)
|
|
||||||
, y2 "10"
|
|
||||||
] []
|
|
||||||
, Svg.text_ [ x (String.fromFloat xpix)
|
|
||||||
, style "text-anchor" "middle"
|
|
||||||
, style "vertical-align" "bottom"
|
|
||||||
, y "25" ]
|
|
||||||
[ Svg.text label ]
|
|
||||||
]
|
|
||||||
xticks = List.map xtick <| List.range 0 6
|
|
||||||
bg = rect
|
|
||||||
[ x "0"
|
|
||||||
, width portalWidth
|
|
||||||
, height graphHeight
|
|
||||||
, fill "#eef"
|
|
||||||
, stroke "none"
|
|
||||||
] []
|
|
||||||
in
|
|
||||||
svg
|
|
||||||
[ width portalWidth
|
|
||||||
, height graphHeight
|
|
||||||
, preserveAspectRatio "none"
|
|
||||||
]
|
|
||||||
(bg::xticks)
|
|
||||||
|
|
||||||
|
|
||||||
cadenceView : List Point -> Svg Msg
|
cadenceView : List Point -> Svg Msg
|
||||||
cadenceView =
|
cadenceView =
|
||||||
measureView "cadence" "#44ee44" (.cadence >> Maybe.map toFloat)
|
measureView "cadence" "#44ee44" (.cadence >> Maybe.map toFloat)
|
||||||
@ -577,7 +469,6 @@ viewDiv model =
|
|||||||
[ div [] [ ifTrack model cadenceView ]
|
[ div [] [ ifTrack model cadenceView ]
|
||||||
, div [] [ ifTrack model powerView ]
|
, div [] [ ifTrack model powerView ]
|
||||||
, div [] [ ifTrack model eleView ]
|
, div [] [ ifTrack model eleView ]
|
||||||
, div [] [ ifTrack model timeAxis ]
|
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -4,19 +4,28 @@ import Lib exposing (..)
|
|||||||
import Test exposing (..)
|
import Test exposing (..)
|
||||||
import Expect exposing (Expectation)
|
import Expect exposing (Expectation)
|
||||||
|
|
||||||
|
|
||||||
specs: Test
|
specs: Test
|
||||||
specs =
|
specs =
|
||||||
let runCheck ticks lower upper expected =
|
|
||||||
let label = (Debug.toString (lower, upper)) ++ " -> " ++ (Debug.toString expected)
|
|
||||||
in test label <| \_ ->
|
|
||||||
Expect.equal expected (looseLabels ticks lower upper)
|
|
||||||
in
|
|
||||||
describe "looseLabels"
|
describe "looseLabels"
|
||||||
[ runCheck 10 0 100 (0.0, 100.0, 10)
|
[ test "0-100" <|
|
||||||
, runCheck 10 2 98 (0.0, 100.0, 10)
|
\_ ->
|
||||||
, runCheck 10 8 91 (0.0, 100.0, 10)
|
let (u, v, _) = looseLabels 10 0.0 100.0
|
||||||
, runCheck 8 1 32 (0, 40, 10)
|
in Expect.equal (0, 100) (u, v)
|
||||||
, runCheck 8 1 36 (0, 40, 10)
|
, test "2-98" <|
|
||||||
, runCheck 10 1 4 (1, 4, 0.5)
|
\_ ->
|
||||||
|
let (u, v, _) = looseLabels 10 2 98
|
||||||
|
in Expect.equal (0, 100) (u, v)
|
||||||
|
, test "8-91" <|
|
||||||
|
\_ ->
|
||||||
|
let (u, v, _) = looseLabels 10 8 91
|
||||||
|
in Expect.equal (0, 100) (u, v)
|
||||||
|
, test "1-32" <|
|
||||||
|
\_ ->
|
||||||
|
let (u, v, _) = looseLabels 8 1 32
|
||||||
|
in Expect.equal (0, 40) (u, v)
|
||||||
|
, test "1-4" <|
|
||||||
|
\_ ->
|
||||||
|
let (u, v, _) = looseLabels 10 1 4
|
||||||
|
in Expect.equal (1, 4) (u, v)
|
||||||
|
|
||||||
]
|
]
|
||||||
|
Loading…
Reference in New Issue
Block a user