SciNim / nim-plotly

plotly wrapper for nim-lang
https://scinim.github.io/nim-plotly/
MIT License
179 stars 15 forks source link

[Feature request] Ability to get the HTML/script in order to embed on a different site #81

Open PMunch opened 2 years ago

PMunch commented 2 years ago

I have a Raspberry Pi which is collecting temperature and humidity data with a cron-job. I wanted to display this data on a nice little website along with other things the Raspberry Pi controls. For a little chart I first thought of using Chart.js, but decided to have a look around the Nim ecosystem to see if I could find something here instead. Plotly looks cool (and I know that it uses JS to actually display the plot) and I'd want to use it, but I wish there was a way for me to get HTML which I could embed into the rest of the site. Currently only save is exposed which means I would have to save it to a file, then read and parse that file to get the bits I need, not great.

Any feedback appreciated, I believe this should be a fairly simple feature to implement seeing how most of this functionality is already there.

Vindaar commented 2 years ago

(note: typing on my phone, so will keep it short)

We define %operators for the traces and layout objects, that perform the conversion from nim types to the plotly JSON. So essentially you can just either import the HTML templates we define / write a similar one for your need and then fill the data using the JSON conversion.

In addition there's a toPlotJson procedure that converts a plot to an object that holds traces and layout of the Plot object, which can be convenient.

Looking at how it's done in the plotly_display.nim file should probably be clear. Tomorrow I can give you a more concrete example where I so such a thing.

Vindaar commented 2 years ago

Consider the HTML template we normally use for a plot:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <title>$title</title>
     <script src="https://cdn.plot.ly/plotly-latest.min.js"></script>
  </head>
  <body>
    <div id="plot0"></div>
    <script>
      $scriptTag
    </script>
    $saveImage
  </body>
</html>

The main important thing is the $scriptTag that we insert. $saveImage is just the additional JS code we use to, well, save a plot & $title is self explanatory.

What is fed into $scriptTag is just:

Plotly.newPlot('plot0', $data, $layout)

which is just the plotly API on the JS side to create a new plot.

Then $data and $layout are the JSON representations of the (possible multiple) Trace objects and the Layout object.

That means the easiest way to generate insert a plotly plot into your own HTML is something like this (starting from example 16):

const
  n = 70

var
  y = newSeq[float64](n)
  x = newSeq[float64](n)
  text = newSeq[string](n)
  sizes = newSeq[float64](n)
for i in 0 .. y.high:
  x[i] = i.float
  y[i] = sin(i.float)
  text[i] = $i & " has the sin value: " & $y[i]
  sizes[i] = float64(10 + (i mod 10))

let plt = scatterColor(x, y, y)
  .mode(PlotMode.LinesMarkers)
  .markersize(15)
  .toPlotJson()

# the returned `plt` is a `PlotJson` object with two fields:
# - traces: a JSON object of the traces that can just be inserted into the `$data` field
echo plt.traces.pretty()
# - layout: a JSON object for the layout that can just be inserted into the `$layout` field
echo plt.layout.pretty()

From there you can just have your own code that generates your HTML and plug these (maybe not using pretty but rather toUgly) into the HTML.

Does that make it clear?

PMunch commented 2 years ago

That does indeed make it clearer, but I'd love to have a single procedure that would return Plotly.newPlot(<passed in identifier>, plot.traces.toUgly(), plot.layout.toUgly()) or something like that. Just a nice little convenience procedure that takes SomePlot and returns the string I need to write to a script tag in my HTML (of course with a nice little usage example like the above in the README).