d3 workshop
DESCRIPTION
D3 workshop conducted by Kiryl Verkhovin on Rolling Scopes meet up series.TRANSCRIPT
D3 WORKSHOP
DATADRIVEN
DOCUMENTS
“D3 is not a compatibility layer,so if your browser doesn't support
standards, you're out of luck.Sorry!”
CREATE YOUR OWN VISUALIZATIONS
D3 IS NOT A CHARTING LIBRARYFor basic visualizations like "bar chart" use:
highchartsjqPlotnvd3...
CHORD DIAGRAM
0k
5k
10k
15k
20k
25k
0k
5k
10k
15k20k
0k
5k
10k
15k
20k
25k
30k
35k
40k0k
5k
see details
COOL... BUTI STILL WANNA BAR CHART!
DATA
var data = [3, 5, 1, 7, 9];
USAGE
barChart(data);
SIMPLE BAR CHART
CREATE HOST PAGE
<!DOCTYPE html><html><head> <title>Bar chart</title> <style type="text/css"> div { height: 100px; background-color: green; margin-bottom: 10px;
color: white; line-height: 100px; font-size: 72px; text-align: right; padding-right: 10px; } </style></head><body></body><script src="../lib/d3.js" charset="utf-8"></script><script>window.data = [3, 5, 1, 7, 9]</script><script src="bar.live.js"></script></html>
NO D3
data.forEach(function (d) { var bar = document.createElement('div'); document.body.appendChild(bar); bar.style.setProperty('width', d * 100 + 'px'); bar.innerText = d;});
MIN D3
data.forEach(function (d) { d3.select('body').append('div') .style('width', d * 100 + 'px') .text(d);});
MAX D3
CREATE 3 DIVS
var body = d3.select('body');
body.append('div');body.append('div');body.append('div');
MODIFY DIVSfunction barChart() { body.selectAll('div') .style( 'background-color', 'blue') .text( 'Hello');}
DATA JOIN
//JOINvar binding = body.selectAll('div') .data(data);
binding.style('background-color', 'blue') .style('width', function (d) { return d * 50 + 'px'; }) .text(function (d) { return d; });
ENTER
//JOINvar binding = body.selectAll('div') .data(data);
//ENTERbinding.enter().append('div');
UPDATE + ENTER
//JOINvar binding = body.selectAll('div') .data(data);
//UPDATEbinding.style('background-color', 'blue')
//ENTERbinding.enter().append('div');
//ENTER + UPDATEbinding.style('width', function (d) { return d * 50 + 'px'; }) .text(function (d) { return d; });
EXIT
//JOINvar binding = body.selectAll('div') .data(data);
...
//EXITbinding.exit().remove();
GENERAL UPDATE PATTERNfunction barChart(data) { //JOIN var binding = body.selectAll('div') .data(data);
//UPDATE binding.style('background-color', 'blue');
//ENTER binding.enter().append('div');
//UPDATE + ENTER binding.style('width', function (d) { return d * 50 + 'px'; }) .text(function (d) { return d; });
//EXIT binding.exit().style('background-color', 'red').remove();}
MORE ABOUT SELECTIONS AND JOINSThree Little CirclesThinking with JoinsHow Selections Work
SCALESScales are functions that map from an input domain to an output
range.
learn more
SCALES
var scale = d3.scale.linear() .domain([20, 80]) .range([0, 120]);
scale(50); // return 60
SCALE BAR CHART
var scale = d3.scale.linear() .domain([0, d3.max(data)]) // -> in elephants .range([0, document.body.clientWidth]); // -> in px
...
//UPDATE + ENTERbinding.style('width', function (d) { return scale(d) + 'px'; }) .text(function (d) { return d; });
TRANSITIONSINTERPOLATE VALUES OVER TIME
numberscolorsgeometric transformsstrings with embedded numbers (e.g., "96px")
learn more
SVG <CIRCLE>
<svg width="800" height="800"> <circle cx="400" cy="400" r="200" fill="blue"></circle></svg>
MAKE CIRCLE ALIVE
var state1 = { r: 100, fill: 'red', cx: 200, cy: 200, opacity: 0.5 };var state2 = { r: 300, fill: 'blue', cx: 400, cy: 400, opacity: 1 };var circle = d3.select('circle');
function applyState(state) { circle.transition() .duration(2500) .ease('elastic') .attr('r', state.r) .attr('fill', state.fill) .attr('cx', state.cx) .attr('cy', state.cy) .style('opacity', state.opacity);}
TRANSITION DEMO
LET'S APPLY IT TO BAR CHART
//ENTERbars.enter().append('div') .style('width', 0);
//ENTER + UPDATEbars.text(function (d) { return d; }) .transition() .style('width', function (d) { return scale(d) + 'px'; });
BREAK
DONUT CHART
SVG <PATH>
<svg width="190px" height="160px"> <path d="M10 80 Q 52.5 10, 95 80 T 180 80" stroke="black" fill="transparent"/></svg>
SVG <G>The g element is a container used to group objects.
Transformations applied to the g element are performed on all ofits child elements.
<svg width="250" height="100"> <circle cx="50" cy="50" r="45" /> <g stroke="green" fill="white" transform="translate(90, 0)"> <circle cx="50" cy="50" r="45" /> <circle cx="80" cy="50" r="45" /> </g></svg>
D3 LAYOUTSLayouts are reusable algorithms that generate data, not display.
see more
FORCE LAYOUT
TREE LAYOUT
TREEMAP LAYOUT
CHORD LAYOUT
AND SOME MORE...
PIE LAYOUT
var pie = d3.layout.pie();
pie([20, 50, 100]);
/* result ->[ {"data":20,"value":20,"startAngle":5.54,"endAngle":6.28}, {"data":50,"value":50,"startAngle":3.71,"endAngle":5.54}, {"data":100,"value":100,"startAngle":0,"endAngle":3.71}]*/
ARC GENERATORvar arc = d3.svg.arc().innerRadius(r / 2).outerRadius(r);
arc({"startAngle":0,"endAngle":3.71});//-> "M0,-20A20,20 0 1,1 -10.76581016580034,16.85518707917385L-5.38290508290017,8.427593539586924A10,10 0 1,0 0,-10Z"
DEMO DONUT CHART V.1
ADD COLORSvar color = d3.scale.category10();
color(0);//-> "#1f77b4"
DEMO DONUT CHART V.2
ADD LABELS//translate to arc's centertext.attr('transform', function (d) { return 'translate(' + arc.centroid(d) + ')';})//center horizontally.attr('text-anchor', 'middle')//center vertically.attr('dy', '.35em')
DEMO DONUT CHART V.3
DEMO DONUT CHART V.4
BREAK
FINDING DATA SOURCESGovernment ;)GeoCommonsNaturalEarthData
States, Provinces
Populated Places
NATURAL EARTH
ESRI SHAPEFILEPopular geospatial vector data format for geographic
information system software.
BinaryBunch of files (*.dbf, *.shp, ...)Not in JSON :)
GEOJSON{ "type": "FeatureCollection", "features": [{ "type": "Feature", "properties": {}, "geometry": { "type": "Polygon", "coordinates": [ [ [27.4603271484375, 53.92698552779884], [27.5537109375, 53.97870483500941], [27.740478515625, 53.930219863940025], [27.696533203125, 53.81038242731128], [27.4932861328125, 53.80713881129993], [27.4603271484375, 53.92698552779884] ] ] } }]}
Learn more...
TOPOJSONExtension of GeoJSON that encodes topology.
Join shared lines into arcs.
BENEFITS:
smaller size (80% smaller)automatic mesh generation
TOPOJSON{ "type": "Topology", "objects": { "collection": { "type": "GeometryCollection", "geometries": [{ "type": "Polygon", "arcs": [ [0] ] }] } }, "arcs": [ [ [0, 6985], [3333, 3014], [6666, -2826], [-1568, -6984], [-7255, -189], [-1176, 6985] ] ], "transform": { "scale": [0.000028017938512601262, 0.00001715831820276837], "translate": [27.4603271484375, 53.80713881129993] }, "bbox": [27.4603271484375, 53.80713881129993, 27.740478515625, 53.97870483500941]
INSTALLING TOOLSGDAL/OGR2OGR
TOPOJSON
HTTP SERVER
GDAL/OGR2OGRUBUNTU
add-apt-repository ppa:ubuntugis/ppaapt-get updateapt-get install gdal-bin
GDAL/OGR2OGRMAC
brew install gdal
GDAL/OGR2OGRWIN
gdal.msi
TOPOJSONnpm install –g topojson
HTTP SERVERnpm install –g http-server
CONVERTING TO GEOJSONogr2ogr \ -f GeoJSON \ -where "iso_a2 = 'BY'" \ states.geojson \ ne_10m_admin_1_states_provinces.shp
ogr2ogr \ -f GeoJSON \ -where "iso_a2 = 'BY'" \ places.geojson \ ne_10m_populated_places.shp
ISO 3166-1 alpha-2
CONVERTING TO TOPOJSONtopojson \ --id-property iso_3166_2 \ -p name=NAME \ -p name \ -o belarus.topojson \ states.geojson \ places.geojson
ISO 3166-2
LOADING DATAd3.json('belarus.topojson', function (error, data) { console.log(data);}
SVG VS CANVASD3 support both of them.
DEMO MAP V.1
CONVERTING TOPOJSON BACK TO GEOJSONvar states = topojson.feature(data, data.objects.states);
PROJECTIONvar projection = d3.geo.mercator() .translate([400, 400]) .rotate([-27.55, 0]) .center([0, 53.916667]) .scale(2200);
GEO PATH GENERATORvar path = d3.geo.path() .projection(projection);
DEMO MAP V.2
DRAWING BOUNDARIESvar mesh = topojson.mesh( belarus, belarus.objects.states, function(a, b) { return a !== b; });
svg.append('path') .datum(mesh) .attr('d', path) .attr('class', 'state-boundary');
DRAWING PLACESPOINTS
var places = topojson.feature(data, data.objects.places);
//POINTSsvg.append('path') .datum(places) .attr('d', path) .attr('class', 'place-point');
DRAWING PLACESLABELS
//LABELSsvg.selectAll('.place-label') .data(places.features) .enter().append('text') .attr('class', function (d) { return 'place-label ' + d.properties.name; }) .attr('transform', function(d) { return 'translate(' + projection(d.geometry.coordinates) + ')'; }) .attr('dy', function (d) { if(d.properties.name === 'Maladzyechna') { return '-0.35em' } return '.35em'; }) .attr('dx', 7) .text(function(d) { return d.properties.name; });
DEMO MAP V.3
HELPFUL TOOLSMapShaperGeoJSON.IOQGIS
BYE BYE