Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to output SVG in a Jupyter notebook using jsdom, D3 and IJavascript

I'm not strong on frontend development but I have toyed around with a lot of Javascript and D3 recently. Being used to doing scientific analysis in Python using Jupyter Notebooks, I figured it should be possible to use a similar workflow for developing scientific visualizations with D3 using JS code in a Jupiter notebook with a JS kernel. I've looked at n-riesco's IJavascript project and it seems promising, but when trying to import D3 the notebook throws an error:

// npm install d3
var d3 = require('d3');

throws

ReferenceError: document is not defined

I'm guessing this is because there is no DOM in the Jupyter environment (because Mike Bostock says so). In fact if I import jsdom, D3 will also import successfully. Awesome! However, now, I cannot select anything because, well... I'm guessing because there is nothing to select in the Jupyter environment.

The things I would like to do is either something like:

$$svg$$ = "<svg><rect width=1000 height=1000/></svg>";
var svg = d3.select("svg")
// Beautiful D3 code

Or (less cool but also favorable), get a reference to the DOM of some local server, that I can then manipulate by executing code in the notebook.


Update

With the introduction of Observable, there is no longer any need to use JavaScript in Jupyter notebooks. Observable is an awesome JavaScript notebook environment that can pretty much do everything. For example, the thing I wanted to do when I asked this question, can be done as simply as:

enter image description here

like image 721
Ulf Aslak Avatar asked Apr 19 '17 06:04

Ulf Aslak


1 Answers

Let me give first a working example (tested on [email protected], [email protected] and [email protected]) and then some clarifications:

var jsdom = require("jsdom");
global.document = jsdom.jsdom();

var d3 = require("d3");

var svg = d3.select(document.body).append("svg");

svg.append("rect")
    .attr("width", 80)
    .attr("height", 80)
    .style("fill", "orange");

$$.svg(svg.node().outerHTML);

screenshot from 2017-04-20 09-32-53

This solution:

  • uses the DOM provided by jsdom.

  • uses d3 to create an <svg> node and append a <rect>

  • uses outerHTML to get the string representation of the <svg> node created in the previous steps

  • and finally instructs IJavascript to return this string to the Jupyter notebook as an SVG result.

In theory, IJavascript could inject javascript code into the Jupyter notebook (to manipulate its DOM), but this code may not run (depending on the security policies of the Jupyter frontend).


Since your interest is scientific analysis, you may be interested in a module I published recently: ijavascript-plotly.


I know this answer skims through many concepts. Please, use the comments below, if you need me to update the answer and explain anything further.

like image 90
Nico Avatar answered Sep 20 '22 15:09

Nico