Learn D3.js in 90 minutes

Preview:

Citation preview

LEARNING D3.JS IN 90MINUTES

THIS SESSION~ WHAT ARE WE GOING TO DO ~

BEFORE WE STARTGet the code from:

You need a webserver for some of the examples:IntelliJ supports this (right click on HTML and run)Easiest is with python: check if you can run:

https://github.com/josdirksen/d3exercises

python -m SimpleHTTPServer (for Python 2) python3 -m http.server (for Python 3)

If not download the mongoose webserver, and you have

WHO AM ILove writing, and talking about technology (frontend as well

as backend). Writing about stuff for Packt and Manning

Twitter: Github: Contact me: Blog at:

@josdirksenhttp://www.github.com/josdirksen

jos.dirksen@gmail.comwww.smartjava.org

Plug: Expert Data Visualization with D3.js (Q1 2017)

WHAT ARE WE GOING TO DOI'll introduce a subject (couple of minutes)I'll Explain the exerciseYou can fill in the blanks in the provided code.

// Step 2. We can also select multiple elements. Use d3.select and // d3.selectAll to see the difference when selecting all the // <li> elements, and use either classed or attr to set the class// of these elements to 'step2'.

// d3.selectAll(...).classed(..., ...)

Halfway during the exercise I'll push an answer to git andDon't hesitate to ask questions!

WE'RE GOING TO TRY AND COVER...Learn how D3.js binds data to DOM elements.Make basic charts and diagramsVisualize hierarchical dataShow connected graphsCombine gis/geo information with opendataMake art with voronois

WHAT AREN'T WE GOING TO COVERD3.js is big, very big. We'll explore the basics for data

visualization, but there is a whole lot more. Most importantstuff we skip:

User interaction, mouse events, drag events.AnimationsZooming & draggingStreaming dataloading and queue dataTypescript, ES6, Scale.js bindings...

D3.JS INTRODUCTION

WHAT IS D3.JS"D3.js is a JavaScript library for manipulating documentsbased on data. D3 helps you bring data to life using

. D3's emphasis on web standards gives you thefull capabilities of modern browsers without tying yourself toa proprietary framework, combining powerful

and a to DOMmanipulation." --- d3js.org

HTML,SVG, and CSS

visualizationcomponents data-driven approach

WHAT IS IT NOTNot a charting library: But provides the building blocks(see nvd3.js, Rickshaw.js, c3.js).Not a drawing library: Doesn't draw data itself, uses SVGfor visualization.Not an SVG abstraction layer: Manipulates the DOM andSVG directly.Not a Canvas/WebGL library: Minimal support for Canvas,for WebGL use Three.js.Not a framework: Can be used with Angular 1/2, React,Vue.js, , is just a (very extensive) library.

ONLINE INFORMATIONLots of online resources,

Most for v3. current version is v4.API:

Bit overwhelming..But won't be in about 80 minutes

https://github.com/d3/d3/blob/master/API.md

WORK BY EXAMPLEUsually easier to work from examplesBest starting point: Most examples still use v3 however..

https://bl.ocks.org/

HELLOWORLD OF D3.JS

HELLOWORLD OF D3.JSGoal of this first example, is to learn how you can select

elements with D3.js, append new ones, remove elements, andset basic properties on the elements.

SELECTIONSd3.select and d3.selectAll

Uses the W3C selection syntax: uses querySelectorand querySelectorAll

Examples:d3.selectAll('p') // Select all p elements in the document. d3.select('p') // Select first p element in the document. // Selects 1st span element of all <p> in the document d3.selectAll('p').select('span') d3.select('#id') // select on id d3.select('.classname') // select on css class

WE CAN CHANGE THE PROPERTIESOFF THESE SELECTIONS

attr(), style(), html(), text(), classed()

Value can also be a function:

d3.selectAll('img.thumb').attr('height', 50) d3.selectAll('span').style('color', 'steelblue') d3.selectAll('span').html('<strong>d3.js</strong>') d3.selectAll('span').text('content') // conditionally set or remove classes d3.select('div').classed('container', true)

// d is bound data, i is the index d3.selectAll('img').attr('src', function(d, i) { return 'image' + i + '.png'; });

MODIFY THE SELECTIONappend(): Append child to each element of selection

remove(): Remove all the elements in the selection

insert(): Insert an element before another element

d3.selectAll('p').append('span').

// remove the first li of every lu d3.selectAll('ul').select('li').remove()

// insert a new span as the first element of the p d3.selectAll('p').insert('span', ':first-child');

Selections are immutable, these actions return newselections, so we can chain them!

BUT JQUERY CAN DO...With you manipulate elements from a selection,with you bind data (next example)

has many data visualization utils and APIs and focusses on web app related plugins.

But there is a big overlap

both are librarieshave and use a .

Similar to D3.js: Raphael.js and Processing.js

jQueryD3.js

D3.jsjQuery

DOM manipulationCSS/W3C selectors

fluent API

LET'S GET STARTED$ git clone https://github.com/josdirksen/d3-exercises $ python -m SimpleHTTPServer

Once again, if you don't have python, you can usemongoose:

View at http://localhost:8000/src/exercise-01/E01.html

EXERCISE 1: LEARNING D3.JSFollow steps described in:<ROOT>/src/exercise-01/js/E01.js

Use d3.select(), d3.selectAll(), class(),attr(), append(), remove(), html(), text()If needed reference D3.js select API:

You'll get approximately 5 minutes.https://github.com/d3/d3-selection

Should you have question? Please ask.

USE DATABINDINGWITH D3.JS

D3.JS DATA BINDINGLearn how to create data driven document by usin the basicselect(), enter(), merge(), remove() cycle when

binding data to a selection

BASIC DATABINDINGBind data to a selection with data(...)

<!-- Input Dom --> <span></span> <span></span> <span></span> <span></span>

var values=['a', 'b', 'c']; d3.selectAll('span') .data(values) .text(function(d) {return d});

<!-- Output Dom --> <span>a</span> <span>b</span> <span>c</span> <span></span>

DATA DRIVEN ELEMENTSAssume an empty DOM

var values=['a', 'b', 'c']; var selection = d3.selectAll('span').data(values)

A selection is actually more than just the found elements

enter(): placeholder selection for unbound data // for all unbound data append a span attribute selection.enter().append('span').text(function(d) {return d});

exit(): placeholder selection for le�over elements // remove all the leftover elements selection.exit().remove()

LEADS TO THIS PATTERNfunction update(values) { // select all the current span elements and assign the data var selection = d3.selectAll('span').data(values) // for the unbound data, create a new span, and attach a class var newElements = selection.enter() .append('span') .attr('class', 'someClass');

// Merge combines two selections. For the combined selection // set the value of the span to the bound datum. newElements.merge(selection).text(function(d) {return d});

// and remove the now obsolete elements selection.exit().remove(); }

This is the basic pattern you use to bind data, add new DOMelements, updated existing ones, and remove obsolete ones.

WHICH PROVIDES US WITHAn easy way to let our data determine our elements.Reduces unnecessary creation of elements.Allow all kinds of hooks e.g. for animations.

// assume values contains some random numbers function update(values) { var selection = d3.selectAll('circle').data(values) var newElements = selection.enter() .append('circle') .attr('class', 'someClass') .attr('cx', function(d, i) {return i * 50}) .attr('cy', function() {return 50}) .merge(selection) .transition().duration(2000) .attr('r', function(d, i) {return d});

// and remove the now obsolete elements selection.exit().remove(); }

EXERCISE 2: GOAL

EXERCISE 2: DATABINDINGFollow steps described in:<ROOT>/src/exercise-02/js/E02.js

Use (same as previous) and enter(), exit(),append(), remove()If needed reference D3.js select API:

If time permits look at transistions API:

You'll get approximately 5 minutes.

https://github.com/d3/d3-selection

https://github.com/d3/d3-selection

Should you have question? Please ask.

BASIC CHARTS ANDDIAGRAMS

BASIC CHARTS AND ELEMENTSLearn how to create basic charts and elements using scales

and generators.

SVG, GROUPS AND POSITIONINGAbsolute positioning:

<rect x="100", y="100"></rect> <circle cx="100", cy="100"></circle>

Provides a g element for grouping

g doesn't have an x or y like attribute, uses transformAll attributes on this element apply on children<g transform="translate(50 200)">...</g> <!-- positioning --> <g transform="scale(0.5 0.3)">...</g> <!-- sizing --> <g transform="rotate(45)">...</g> <!-- rotate the g -->

These actions can be combinedBut positioning is still difficult

POSITIONING WITH SCALESA scale translates an input domain to an output rangeDifferent scales ( )

: continuous input to continuous outputscalePow, scaleLog, scaleTime

: continuous input to interpolatorscaleQuantize: continuous input to discrete rangescaleQuantile: sampled input to discrete rangescaleThreshold: scaleQuantize with thresholdsscaleOrdinal: discrete input to discrete range

, scalePointLots of useful helper functions

https://github.com/d3/d3-scalescaleLinear

scaleSequential

scaleBand

SCALELINEAR: CONTINUOUS INPUTTO CONTINUOUS OUTPUT

var x = d3.scaleLinear() .domain([10, 130]) // e.g the min and max of your data .range([0, 960]); // the width of your chart

x(20); // 80 x(50); // 320

var color = d3.scaleLinear() .domain([10, 100]) .range(["brown", "steelblue"]);

color(20); // "#9a3439" color(50); // "#7b5167"

WHAT CAN WE INTERPOLATE?The domain must be numbers, the range can be any of this:

And if this doesn't match, you can create your own.

SCALESEQUENTIAL: CONTINUOUSINPUT TO INTERPOLATOR

var color = d3.scaleSequential(d3.interpolatePlasma).domain([0, 100]);color(30); // #8f0da4 color(60); // #e16462 color(80); // #fca636

Many interpolators available:

HOW WOULD YOU USE THIS?// say our values contains data from 1984 to 2014 var min = d3.min(values, function (d) { return d.value; }); var max = d3.max(values, function (d) { return d.value; }); // evenly divide the years along the xAxis var xScale = d3.scaleLinear().domain([1984,2014]).range([0, 1080]); // evenly divide the values along the yAxis var yScale = d3.scaleLinear() .domain([min, max]).range([0, 700]); // get a color var col = d3.scaleSequential(d3.interpolatePlasma).domain([0, 100]);

d3.selectAll("rect").data(values).enter() .append("rect") .attr("x", xScale) // xScale = (d: any) => Numeric .attr("y", function(d) {return yScale(d)}) // alternatively .attr("fill", col)

SCALES AND AXISD3 provides easy way to create an axis:

https://github.com/d3/d3-axisvar min = d3.min(values, function (d) { return d.value; }); var max = d3.max(values, function (d) { return d.value; });

var yScale = d3.scaleLinear() .domain([min, max]).range([0, 700]);

// s denotes that we want to use international system of units // to display axis values var bottomAxis = d3.axisBottom().scale(yScale).ticks(20, "s");

Results in:

EXERCISE 3: GOAL

BEFORE WE STARTD3.js support easy loading csv, tsv, json, xml

name,sex,amount Emma,F,20355 Olivia,F,19553 ...

d3.csv('data/yob2015.txt', function (d) { return { name: d.name, sex: d.sex, amount: +d.amount }; }, function (data) { }

ALSO FOR MULTIPLE FILESd3.queue() .defer(d3.json, "./data/world-110m.v1.json") .defer(d3.csv, "./data/worldbank_popular_2014.csv") .defer(d3.csv, "./data/iso-mapping.csv") .await(function (error, topoData, worldbank, mapping) { }

EXERCISE 3: WORKING WITH SCALESFollow steps described in:<ROOT>/src/exercise-03/js/E03.js

If needed reference D3.js APIs:

You'll get approximately 8 minutes.

https://github.com/d3/d3-scalehttps://github.com/d3/d3-axis

Should you have question? Please ask.

USING SVG FOR CHARTSSVG provides a large number of elements:

But limited primitive shapes: <circle>, <ellipse>,<line>, <path>, <polygon>, <polyline>, <rect>

https://developer.mozilla.org/en-US/docs/Web/SVG

THE PATH ELEMENT IS VERSATILEthe d attribute describes a path:

<path class="arc" d="M136.86141570725613,-17.69047457265137A138,138, 0,0,1,124.60150267951192,59.31665474390461L62.948628653284814,28. 257214134993035A69,69,0,0,0,68.61145448511735,-7.312203049469534Z" style="fill: rgb(252, 160, 130);"></path>

M, L, A, C, A, Q, T ... for lines, arcs, curvesHard to determine the correct values yourselfD3.js provides generators for complex shapes

THE ARC GENERATORWe'll create an arc segment, a part of a pie chart

var arc = d3.arc() .outerRadius(height/2 * 0.6).innerRadius(height/2 * 0.3); // create the right half of a pie chart arc({ startAngle: 0, endAngle: Math.PI });

// "M1.469576158976824e-14,-240A240,240,0,1,1,1.469576158976824e-14, // 240L7.34788079488412e-15,120A120,120,0,1,0,7.34788079488412e-15, // -120Z"

COMBINED WITH THE PIE FUNCTIONPiechart: d3.pie() to generate config for d3.arc

var arc = d3.arc() .outerRadius(height/2 * 0.6).innerRadius(height/2 * 0.3); var data = [{count:10}, {count:20}, {count:30}] var pie = d3.pie() .padAngle(0.04) .value(function (d) { return d.count; });

var arcs = pie(data) // "[{"data":{"count":10},"index":2,"value":10, // "startAngle":5.215987755982988, // "endAngle":6.283185307179586,"padAngle":0.04}, // {"data":{"count":20},"index":1,"value":20, // "startAngle":3.121592653589793, // "endAngle":5.215987755982988,"padAngle":0.04} ... selectAll("path").data(arcs).enter() .append("path").attr("d", arc);

ANOTHER STANDARD PATTERN1. Define generator which creates paths based on properties.

(d3.arc)2. Define generator which creates config for other generators

(d3.pie)3. Pass data to step 2, result is enriched data with config.4. Use the normal selectAll(), enter(), merge(),exit() pattern

EXERCISE 4: GOAL

EXERCISE 4, CREATE A PIE CHARTFollow steps described in:<ROOT>/src/exercise-04/js/E04.js

If needed reference D3.js APIs:

You'll get approximately 5 minutes.https://github.com/d3/d3-shape

VISUALIZING TREESTRUCTURES

TREE AND HIERARCHIESSee what is possible in d3 to visualizes nested trees of data

D3.JS SUPPORTS TREE DATAAPI: Many standard visualizations: d3.tree()

https://github.com/d3/d3-hierarchy

D3.CLUSTER()Same as the tree, but leaves are at the same position

D3.TREEMAP()Supports different clustering algorithms

D3.PACK()Same as d3.treemap, but with circles

OR YOUR CUSTOM IMPLEMENTATION

Sample

ALL FOLLOW SAME APPROACH1. Load the data (d3.csv,d3.tsv,d3.json etc.)2. Convert the data into a tree stucture3. Pass it through a generator4. Use the output from the generator to draw the chart

NESTED DATA: D3.STRATISFYd3.stratisfy() can follow ids in your data

id,parentId,name,description 180580,,Felidae,cats 180581,552363,Lynx,lynxes 180582,180581,rufus,Bobcat 180583,180582,rufus,bobcat 180584,180581,lynx,Eurasian Lynx 180585,180581,canadensis,Canada lynx`

var stratify = d3.stratify(); var root = stratify(data);

NESTED: D3.NEST / D3.HIERARCHYd3.nest() can group data (multiple times) based on a key

"Country (en)";"Country (de)";"Country (local)";"Country code";"Continent""Afghanistan";"Afghanistan";"Afganistan/Afqanestan";"AF";"Asia"; "Egypt";"Ãgypten";"Misr";"EG";"Africa";

var entries = d3.nest() .key(function (d) {return d.Continent; }) .entries(data);

var root = d3.hierarchy({values: entries}, function(d) { return d.values; })

USE A GENERATORWith data in the correct structure, we can use a generator// normal node tree var tree = d3.tree() .size([height, width]) .separation(function(a, b) { return (a.parent === b.parent ? 5 : 13) });

// create a treemap var tree = d3.treemap() .size([width, height]) .padding(2) .tile(d3.treemapSquarify.ratio(1))

// enrich the root tree(root)

WHICH ENRICHES THE STRUCTURE

root.leaves() // return all the nodes without children root.descendants() // return array of this and all descendants

chart.selectAll(".node") .data(root.descendants()) .enter(..)

EXERCISE 5: GOAL

EXERCISE 5, CREATE A TREEMAPFollow steps described in:<ROOT>/src/exercise-05/js/E05.js

If needed reference D3.js APIs:

You'll get approximately 8 minutes.https://github.com/d3/d3-hierarchy

VISUALIZES GRAPHSOF DATA

VISUALIZE GRAPHSQuick introduction on different ways how to use the force,

hord, and matrix layouts to visualize a graph of data

FORCE LAYOUT FOR GRAPHSAPI: A graph is a set of nodes.Define forces which are applied:

to nodesto links

Run a simulation

https://github.com/d3/d3-force

CAN APPLY DIFFERENT FORCESforceCenter: keeps nodes in the centerforceCollide: nodes have a radius, avoid collisionsforceLink: push/pull nodes together based on distanceforceManyBody: simulate attraction or repulsionbetween nodesforceX: force nodes towards specific positionforceY: force nodes towards specific position

BASIC SETUPvar graph = ...

// define forces var simulation = d3.forceSimulation() .force("link", d3.forceLink().id(function(d) { return d.id; })) .force("charge", d3.forceManyBody()) .force("center", d3.forceCenter(width / 2, height / 2));

// run a simulation simulation .nodes(graph.nodes) .on("tick", ticked);

// do something on each tick function ticked() {...}

FORCE LAYOUT MULTIPLE FORCES

~ example ~

ALSO USE WITHOUT LINKS

~ example ~

ALTERNATIVE: CHORD DIAGRAM

~ example ~

ALTERNATIVE: MATRIX DIAGRAM

~ example ~

EXERCISE 6: GOALPlay around with the different forces and see the effect.

EXERCISE 6, APPLY FORCESFollow steps described in:<ROOT>/src/exercise-06/js/E06.js

If needed reference D3.js APIs:

You'll get approximately 5 minutes.https://github.com/d3/d3-force

GIS AND GEO DATA

GEOD3.js has extensive support for working with geo data. We'll

explore different ways of visualizing and interacting with geodata.

BEST PART OF D3.JSAt least for me..

CAUSE IT LOOKS NICE

AND INFORMATIVE

AND COLOURFUL

QUICK NOTE ON PROJECTIONSProjection defines how the coordinates from the source areprojected to a flat canvas. The projection:Sinu Mollweide

Example

CREATING A MAPGoing to explain how to create this:

Example

HOW TO LOAD DATAMost common format for GIS data is ArcGIS shapefile.

binary formatUse QGis, Mapshaper, OGR to convert to open formatsD3.js can work with:

GeoJSON: A standard supported by many applications.TopoJSON: A specific optimized format for smaller files.

GEOJSON AND TOPOJSON FORMATSCan contain multiple geometries.Each geometry is described (usually) as a path.Can contain additional properties

population, unemployment rate, etc.one file for everything

Try to work with TopoJSON, since it's much smaller{"type":"Topology","objects":{"cb_2015_us_county_500k": {"type": "GeometryCollection","geometries":[{"type":"Polygon","properties": {"GEOID":"01005","NAME":"Barbour"},"id":"01005","arcs":[[0,1,2,3 ,4,5,6,-7,6,7,8,9,10,11,12,13,14 ...

WORKING WITH TOPO DATA IN D3.JS(pretty much the same way as we did before)

1. Setup a path generator and projection2. Load the data3. Use the projection to generate path segments

LOAD THE DATA(this is prepared data, where value contains percentage of

internet users){"type":"Topology","objects":{"countries":{"type":"GeometryCollection" "geometries":[{"type":"Polygon","id":"004","arcs":[[0,1,2,3,4,5]], "properties":{"value":"7","countryA":"AFG","name":"Afghanistan"}}, {"type":"MultiPolygon","id":"024","arcs":[[[6,7,8,9 ....

d3.json("./data/world-110m-inet.json", function(loadedTopo) { // by using topojson.feature, we convert the topoJson to geojson, // where each country is a single feature in the features array. countries = topojson.feature(loadedTopo, loadedTopo.objects.countries).features; });

WHEN LOADED THE DATA LOOKSLIKE THIS

SETUP THE PATH GENERATORvar projection = d3.geoNaturalEarth() var path = d3.geoPath().projection(projection)

For all the supported projections see:

Pretty much any projection you can think offRelatively easy to create your own one

https://github.com/d3/d3-geo/https://github.com/d3/d3-geo-projection

WITH A GENERATOR AND THE DATAvar projection = d3.geoNaturalEarth() var path = d3.geoPath().projection(projection)

d3.json("./data/world-110m-inet.json", function(loadedTopo) { countries = topojson.feature(loadedTopo, loadedTopo.objects.countries).features;

svg.selectAll('.country').data(countries).enter() .append("path") .classed('country', true) .attr("d", path); }

And that's it..

AND YOU'RE DONE!

But colors?

ADD A SIMPLE SCALEvar color = d3.scaleSequential(d3.interpolateGreens).domain([0,100]) var projection = d3.geoNaturalEarth() var path = d3.geoPath().projection(projection)

d3.json("./data/world-110m-inet.json", function(loadedTopo) { countries = topojson.feature(loadedTopo, loadedTopo.objects.countries).features;

svg.selectAll('.country').data(countries).enter() .append("path") .classed('country', true) .attr("d", path); .attr("fill", function(d) {return d.properties.value ? color(+d.properties.value) : '#ccc'}); }

EASY RIGHT?

EXERCISE 7: GOALRender the US election

EXERCISE 7, RENDER THE USELECTION RESULTS

Follow steps described in:<ROOT>/src/exercise-07/js/E07.js

If needed reference D3.js APIs:

You'll get approximately 10 minutes.

https://github.com/d3/d3-geo/https://github.com/d3/d3-geo-projection

D3.JS AND GENERATIVEART

NOT EVERYTHING SHOULD HAVE AMEANING

D3.JS SUPPORTS VORONOISIn mathematics, a Voronoi diagram is a partitioning of a planeinto regions based on distance to points in a specific subset ofthe plane. That set of points (called seeds, sites, or generators)

is specified beforehand, and for each seed there is acorresponding region consisting of all points closer to that

seed than to any other.

VORONOI

MUST GO DEEPER

MUST GO DEEPER

AND DEEPER

AND DEEPER

ANY DEEPER BREAKS MY BROWSERThis is rendered as SVG elementsWe could output to canvas as well

EXERCISE 7, PLAY AROUND WITHTHE VORONOI CODE

Not really an exercise, but allows you to experiment withvoronois.

Follow steps described in:<ROOT>/src/exercise-08/js/E08.js

@josdirken ~ Exercises for this session: https://github.com/josdirksen/d3exercises

THANK YOU!

Recommended