You need to store the internal state of the plot in your model.
It can be as simple as:
import ZoomPlot as Plot
type alias Model =
{ plotState : Plot.State Point }
or if you have multiple plots on your page:
type alias Model =
{ plot1 : Plot.State Point
, plot2 : Plot.State Point
, plot3 : Plot.State Point
}
init : State data
All plots have the same initial state regardless of individual configuration.
init : Model
init =
{ plot1 = Plot.init
, plot2 = Plot.init
, plot3 = Plot.init
}
Plot.Msg
in your Msg
Include it within a constructor for easy pattern matching in your update function:
type Msg
= MyPlotMsg (Plot.Msg Point)
| NoOp
or if you need routing for several plots:
type MyPlot
= Plot1
| Plot2
type Msg
= ToPlot MyPlot (Plot.Msg Point)
update : Msg data -> State data -> State data
Naturally you need to handle the plot messages in your update function. This is what Plot.update
is for.
update : Msg -> Model -> Model
update msg model =
case msg of
ToPlot plotMsg ->
{ model
| plotState =
Plot.update
plotMsg
model.plotState
}
If you need routing to update multiple plots:
case msg of
ToPlot Plot1 plotMsg ->
{ model
| plot1 =
Plot.update
plotMsg
model.plot1
}
ToPlot Plot2 plotMsg ->
{ model
| plot2 =
Plot.update
plotMsg
model.plot2
}
points : { toMsg : Msg LineChart.Coordinate.Point -> msg, data : List LineChart.Coordinate.Point } -> Config LineChart.Coordinate.Point msg
Use this configuration as a starting point when your data is just a List Point
.
type alias Point =
{ x : Float, y : Float }
myPoints =
[ Point 11 120
, Point 12 121
, Point 13 120.5
]
Plot.points
{ toMsg = ToPlot
, data = myPoints
}
|> Plot.drawHtml model.plotState
draw : State data -> Config data msg -> Element msg
Use this function to place your line chart within your view.
import Element
view model =
Element.layout
[ Element.padding 20 ]
(Plot.points
{ toMsg = ToPlot
, data = myPoints
}
|> Plot.draw model.plotState
)
drawHtml : State data -> Config data msg -> Html msg
If you, for some reason, are not using mdgriffith/elm-ui you can use this draw function instead which outputs regular Html msg
.
Customization is done by mutating the Config
output from Plot.points
and Plot.custom
before it is inserted into draw
.
width : Basics.Float -> Config data msg -> Config data msg
Set the plot width in pixels.
height : Basics.Float -> Config data msg -> Config data msg
Set the plot height in pixels.
xAxisLabel : String -> Config data msg -> Config data msg
Set a string for labeling of the x-axis. Adjustment of margins and label offsets could be required to get the desired result.
Default is ""
yAxisLabel : String -> Config data msg -> Config data msg
Set a string for labeling of the y-axis. Adjustment of margins and label offsets could be required to get the desired result.
Default is ""
showLegends : Basics.Bool -> Config data msg -> Config data msg
Set whether legends for your plot lines should be drawn or not. If you turn this on you most likely also need to adjust marginRight
for them to not get cut off.
Default is False
.
numberFormat : (Basics.Float -> String) -> Config data msg -> Config data msg
Set the function which turns floats to strings for the axis labels (on non-time axes).
import FormatNumber
import FormatNumber.Locales
myPlotConfig
|> Plot.numberFormat
(\float ->
FormatNumber.format
FormatNumber.Locales.frenchLocale
float
)
|> Plot.draw model.myPlot
Default is:
defaultFormat : Float -> String
defaultFormat number =
FormatNumber.format
FormatNumber.Locales.usLocale
number
xIsTime : Basics.Bool -> Config data msg -> Config data msg
Set whether the x-axis should display its values as time or not.
Default is False
.
timezone : Time.Zone -> Config data msg -> Config data msg
Set which timezone your Time.Posix
values should be converted into for your time axis tick labels.
Only matters when Plot.xIsTime True
.
default is Time.utc
.
language : DateFormat.Language.Language -> Config data msg -> Config data msg
Set what language dates on the time axis will be in.
Only matters when Plot.xIsTime True
.
import DateFormat.Language
myPlotConfig
|> Plot.language DateFormat.Language.swedish
|> Plot.draw model.myPlot
Default is DateFormat.Language.english
.
labelFunc : (data -> String) -> Config data msg -> Config data msg
Set extra hover label row. Hoverlabels for the x and y coordinates are always on, but if you want to add something extra on a row above them you use this.
You can for example just use a string field from your data type:
myPlotConfig
|> Plot.labelFunc .rocketInventorName
|> Plot.draw model.myPlot
Default is \_ -> ""
Size in pixels of the different margins around the actual plot. This is real estate that may or may not be needed by tick labels, legends and axis labels.
marginLeft : Basics.Float -> Config data msg -> Config data msg
Default is 60
marginRight : Basics.Float -> Config data msg -> Config data msg
Default is 30
marginTop : Basics.Float -> Config data msg -> Config data msg
Default is 20
marginBottom : Basics.Float -> Config data msg -> Config data msg
Default is 30
Amount in pixels for moving around the axis labels.
Positive y adjusts upwards contrary to Svg standard.
xAxisLabelOffsetX : Basics.Float -> Config data msg -> Config data msg
Default is 0
xAxisLabelOffsetY : Basics.Float -> Config data msg -> Config data msg
Default is 0
yAxisLabelOffsetX : Basics.Float -> Config data msg -> Config data msg
Default is 0
yAxisLabelOffsetY : Basics.Float -> Config data msg -> Config data msg
Default is 0
What if your data isn't in the form of { x : Float, y : Float}
?
Then you will need:
custom : { lines : List (LineChart.Series data), toMsg : Msg data -> msg, xAcc : data -> Basics.Float, yAcc : data -> Basics.Float, pointDecoder : LineChart.Coordinate.Point -> data } -> Config data msg
Let's say that your data is a list of this type:
import Time
type alias ExampleType =
{ time : Time.Posix
, thrust : Float
, altitude : Float
, latestBufferLine : String
, errorLog : Maybe String
}
If you want to plot data like this you can't use Plot.points
, because it only works with data of type List Point
.
Instead you can use Plot.custom
like this:
import LineChart
import LineChart.Dots as Dots
import LineChart.Colors as Colors
Plot.custom
{ lines =
[ LineChart.line
Colors.blue
Dots.circle
"rocket 1"
listOfExampleType1
, LineChart.line
Colors.purple
Dots.square
"rocket 2"
listOfExampleType2
]
, toMsg = ToPlot
, xAcc = .time >> posixToMillis >> toFloat
, yAcc = .thrust
, pointDecoder = myPointDecoder
}
The inputs xAcc
and yAcc
are whatever functions that will turn your type into floats to be plotted on the line chart.
Most often they are just getters plus whatever functions are needed to convert their values into floats.
You may also have noticed the myPointDecoder
above.
The internals of the package needs a way to convert Point back to your type.
You basically have to write a function that puts the coordinates of a Point
into an "empty" instance of your type:
myPointDecoder : Point -> ExampleType
myPointDecoder { x, y } =
let
xTime =
x |> round |> millisToPosix
in
ExampleType xTime y 0 "" Nothing
This package uses the fork peterszerzo/line-charts since it contains necessary updates to terezka's great original package.
I advise you to do the same (at least in projects using elm-zoom-plot).