show ticks on time axis
This commit is contained in:
parent
54af226e52
commit
e0d395ba9f
37
README.md
37
README.md
@ -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
|
||||
|
||||
|
3
elm.json
3
elm.json
@ -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",
|
||||
|
@ -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 ]
|
||||
]
|
||||
]
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user