Plotting

Genie uses PlotlyJS to generate interactive plots in the browser. Particularly, it leverages the PlotlyJS.jl package and its syntax to declare plots in the Julia code.

A PLotlyJS plot has two components:

  • Data: an array of trace objects, each representing a distinct set of data points and their graphical representation within the plot. A trace object specifies the type of plot (scatter, bar, pie, heatmap, etc.) and contains properties such as color, line style or marker style. By combining multiple trace objects within the data array, you can create complex and layered visualizations with different data series and plot types.
using PlotlyBase

trace1 = scatter(
    x=[1, 2, 3, 4],
    y=[10, 15, 13, 17],
    mode="markers",
    name="Trace 1"
)

trace2 = scatter(
    x=[1, 2, 3, 4],
    y=[5, 9, 11, 12],
    mode="lines+markers",
    name="Trace 2",
    line=attr(color="red")
)
  • Layout: defines the plot's overall appearance and formatting, including settings for the plot title, axis labels, legends, margin settings, background colors, grid lines, and more.
layout = Layout(
    title="A Scatter Plot with Multiple Traces",
    xaxis=attr(
        title="X Axis Label",
        showgrid=false
    ),
    yaxis=attr(
        title="Y Axis Label",
        showgrid=true,
        range=[0, 20]
    )
)

To include a plot in a Genie app, first declare its data and layout as reactive variables

@out plotdata = [trace1, trace2]
@out plotlayout = layout

and add the plot to the UI with the low-code API

ui() = plot(:plotdata, layout=:plotlayout)

or with the plotly tag in the HTML code

<plotly :data="plotdata" :layout="plotlayout"> </plotly>

Defining plots from Plots.jl

It is also possible to obtain the plotdata from a plot defined with Plots.jl using the Plotly backend and the Plots.plotly_traces/Plots.plotly_layout functions as follows:

module App
using GenieFramework
import PlotlyBase, PlotlyKaleido, Plots
Plots.plotly()

p = Plots.plot([1, 2, 3], show=false)
@app begin
    @out traces = Plots.plotly_traces(p)
    @out layout = Plots.plotly_layout(p)
end

function ui()
    plot(:traces, layout=:layout)
end

@page("/", ui)
end

The plotly_traces function is loaded by Plots.plotly() in the integration with PlotlyBase and PlotlyKaleido. Therefore, PlotlyBase and PlotlyKaleido should be available installed and in the environment when Plots.plotly() is called for plotly_traces to be defined.

Defining plots in HTML

You can also define the plot entirely in the HTML code and just bind the data to Julia variables,

module App
using GenieFramework
@genietools

@app begin
    @out firstX = [1, 2, 3, 4, 5]
    @out firstY = [5, 0, 3, -1, 2]
    @out secondX = [1, 2, 3, 4, 5]
    @out secondY = [1, 4, 6, 4, 4]
end


@page("/", "app.jl.html")
Server.up()
end
<div>
    <plotly
      :data="[
        {
          x: firstX,
          y: firstY,
          mode: 'markers',
          type: 'scatter',
          marker: { color: 'rgb(201, 90, 218)', size: 12 }
        },
        {
          x: secondX,
          y: secondY,
          mode: 'lines+markers',
          type: 'scatter',
          line: { color: 'rgba(61, 185, 100, 0.8)', width: 1.5 },
          marker: { color: 'rgb(61, 185, 100)', size: 8 }
        }
      ]"
      :layout="{
        title: 'Complex StippleJS Plot Example',
        xaxis: { title: 'X Axis', range: [0, 5] },
        yaxis: { title: 'Y Axis', range: [-1, 6] },
        legend: { x: 0.5, y: -0.3, orientation: 'h' }
      }"
    />
  </div>

Note that this example uses the Javascript PlotlyJS, so the Julia PlotlyBase package is not needed.

Detecting mouse events on a plot

PlotlyJS allows the detection of various mouse events on a plot, such as clicks, hovers, selections, and relayouts. You can capture these events and execute especific code.

To enable event detection, you'll need to use a named ReactiveModel defined with @app ModelName. Note that, as of today, named models are shared across all sessions and users and so any user interaction will trigger a response across all browser windows.

Moreover, event handling code is attached to the ReactiveModel with the @mixin macro, and the watchplots function.

The example below shows how to work with the available mouse events.

module App
using GenieFramework, PlotlyBase, StipplePlotly
@genietools

trace1 = scatter(; x=1:4, y=[0, 2, 3, 5], fill="tozeroy")
trace2 = scatter(; x=1:4, y=[3, 5, 1, 7], fill="tonexty")

@app EventsModel begin
    @out traces = [trace1, trace2]
    @out plotlayout = PlotlyBase.Layout(title="Filled line chart")
    @mixin traces::PlotlyEvents
    @onchange traces_click begin
        @show traces_click
    end
    @onchange traces_hover begin
        @show traces_hover
    end
    @onchange traces_selected begin
        @show traces_selected
    end
    @onchange traces_relayout begin
        @show traces_relayout
    end
end

# start watchning plots when page is loaded
@mounted EventsModel watchplots()

ui() = [plot(:traces, layout=:plotlayout, syncevents = true)]

route("/") do
    global model
    model = EventsModel |> init |> handlers
    page(model, ui()) |> html
end

Server.isrunning() || Server.up()
end

Genie