show ticks on time axis

This commit is contained in:
Daniel Barlow 2024-11-17 16:16:46 +00:00
parent 54af226e52
commit e0d395ba9f
3 changed files with 148 additions and 1 deletions

View File

@ -117,6 +117,43 @@ change depending on the nature of the training effort. e.g.
for a long slow ride we show total distance, for interval training
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

View File

@ -2,7 +2,7 @@
"type": "application",
"source-directories": [
"frontend/src",
"frontend/tests"
"frontend/tests"
],
"elm-version": "0.19.1",
"dependencies": {
@ -15,6 +15,7 @@
"elm/svg": "1.0.1",
"elm/time": "1.0.0",
"elm/url": "1.0.0",
"elm-community/list-extra": "8.7.0",
"elm-explorations/test": "2.2.0",
"mpizenberg/elm-pointer-events": "5.0.0",
"rtfeldman/elm-iso8601-date-strings": "1.1.4",

View File

@ -8,6 +8,7 @@ import Html.Events exposing (onClick, on)
import Html.Events.Extra.Pointer as Pointer
import Maybe exposing (Maybe)
import Lib exposing(..)
import List.Extra exposing(find)
import Json.Decode as D
import Http
import Point exposing(Point, Pos ,decoder)
@ -23,6 +24,7 @@ import Svg.Attributes as S exposing
, fill
, points
, stroke, strokeWidth, strokeOpacity)
import Time exposing(Posix)
import Url.Parser exposing (Parser, (</>), (<?>), int, map, oneOf, s, string)
import Url.Parser.Query as Query
import Url exposing (Url)
@ -238,8 +240,40 @@ newModel msg model =
NewUrlRequest -> model
UrlChanged -> model
-- 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 {x,y} z =
String.concat ["https://a.tile.openstreetmap.org",
@ -272,6 +306,8 @@ measureView title colour fn allPoints =
rangeYaxis = maxYaxis - minYaxis
maxX = Point.duration allPoints
string = String.concat (List.map coords filteredPoints)
ttick = timeTick maxX
firstTimeTick = (toFloat (floor(startTime / ttick))) * ttick - startTime
ybar n = line
[ fill "none"
, style "vector-effect" "non-scaling-stroke"
@ -282,6 +318,24 @@ measureView title colour fn allPoints =
, x2 (String.fromFloat (0.95 * maxX))
, 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_
[ x "99%", y (String.fromFloat (graphHeight - graphHeight * n * (tickY/rangeYaxis)))
, style "text-anchor" "end"
@ -330,8 +384,62 @@ measureView title colour fn allPoints =
, ylabel 1
, ylabel 2
, 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 =
measureView "cadence" "#44ee44" (.cadence >> Maybe.map toFloat)
@ -469,6 +577,7 @@ viewDiv model =
[ div [] [ ifTrack model cadenceView ]
, div [] [ ifTrack model powerView ]
, div [] [ ifTrack model eleView ]
, div [] [ ifTrack model timeAxis ]
]
]