A deep dive into Data Visualization, D3.js and SVG

Data Visualization and D3.js

Sections:

What is Data Visualization

  • Data visualization is the presentation of data in a pictorial or graphical format.
  • It enables decision makers to see analytics presented visually
  • Decision makers can then grasp difficult concepts or identify new patterns.
  • With interactive visualization, you can take concepts further and teach concepts better.
    • You can drill down into charts and graphs for more detail,
    • and interactively change what data you see and how it’s processed.

Types of Visual Diagrams

A diagram is a symbolic representation of information according to some visualization technique.

Bar Charts

Bar Chart

  • A bar chart or bar graph is a chart or graph that presents categorical data with rectangular bars with heights or lengths proportional to the values that they represent.
  • The bars can be plotted vertically or horizontally.
  • A vertical bar chart is sometimes called a line graph.

Line Charts

Line Chart

  • A line chart or line graph is a type of chart which displays information as a series of data points called ‘markers’ connected by straight line segments.
  • It is a basic type of chart common in many fields.
  • It is similar to a scatter plot except that the measurement points are ordered (typically by their x-axis value) and joined with straight line segments.
  • A line chart is often used to visualize a trend in data over intervals of time – a time series – thus the line is often drawn chronologically.

Scatter Plots

Scatter Plots

  • A scatter plot (also called a scatter graph, scatter chart, scattergram, or scatter diagram) is a type of plot or mathematical diagram using Cartesian coordinates to display values for typically two variables for a set of data.
  • If the points are color-coded, one additional variable can be displayed.
  • The data is displayed as a collection of points, each having the value of one variable determining the position on the horizontal axis and the value of the other variable determining the position on the vertical axis

D3 API Docs

D3 4.0 is a collection of modules that are designed to work together * You can use the modules independently, or you can use them together as part of the default build. * The source and documentation for each module is available in its repository.

Follow the links below to learn more:

List of D3 Modules

Data space
  • query

    • d3-request
    • d3-queue
  • generation

    • d3-random
  • parsing

    • d3-dsv
    • d3-time
  • formatting

    • d3-time-format
    • d3-format
  • manipulation

    • d3-array
    • d3-collection
Graphic space
  • data transform

    • d3-scale
    • d3-geo-projection
  • geometry computation

    • d3-voronoi
    • d3-hull
    • d3-quadtree
    • d3-interpolate
    • d3-geo
  • dynamic geometry computation

    • d3-transition
    • d3-timer
    • d3-ease
  • visual variables mapping

    • d3-hierarchy
    • d3-sankey
    • d3-chord
    • d3-hexbin
    • d3-force
  • generation

    • d3-shape
    • d3-path
    • d3-polygon
  • converter

    • d3-color
View space
  • visual variables mapping (selection, data-binding, attributes)

    • d3-selection
    • d3-selection-multi
  • high level set of visual variables mapping (component)

    • d3-axis
Interaction space
  • internal events

    • d3-dispatch
  • user events and geometry computation

    • d3-zoom
    • d3-brush
    • d3-drag

Selections and Data

D3 Selections Documentation

  • Selections are immutable.
  • All selection methods that affect which elements are selected (or their order) return a new selection rather than modifying the current selection.
  • However, note that elements are necessarily mutable, as selections drive transformations of the document!

Selecting Elements

var anchor = d3.select("a");

This will select the first element that matches the specified selector string.

const enter = svg.selectAll('rect')

This will select all elements that match the specified selector string

  • If the selector is not a string, instead selects the specified array of nodes
  • this is useful if you already have a reference to nodes, such as this.childNodes within an event listener or a global such as document.links
d3.selectAll(document.links).style("color", "red");
var even = d3.selectAll("tr").filter(":nth-child(even)");

Notice here that you can a css pseudo selector and you can also pass a function

var even = d3.selectAll("tr").filter(function(d, i) { return i & 1; });

Block Builder Editor

  • A very nice editor to build d3 visualizations that get saved as gists

  • Click START CODING button

  • Click Login and you will be routed to github page to authorize application

  • You can then save your code here and get interactive editor

Playing with D3

Playing with D3

Selections Demo

Selections and Data Block Builder Demo

Selections Filter Demo

D3 Selections attr

  • If a value is specified, sets the attribute with the specified name to the specified value on the selected elements and returns this selection.

  • If the value is a constant, all elements are given the same attribute value

  • Otherwise, if the value is a function, it is evaluated for each selected element, in order, being passed the current datum (d), the current index (i), and the current group (nodes), with this as the current DOM element (nodes[i]).

  • The function’s return value is then used to set each element’s attribute. A null value will remove the specified attribute.

D3 Selections data

  • Joins the specified array of data with the selected elements, returning a new selection that represents the update selection: the elements successfully bound to data

D3 Selections call

  • Invokes the specified function exactly once, passing in this selection along with any optional arguments.

  • Returns this selection. This is equivalent to invoking the function by hand but facilitates method chaining.

For example, to set several styles in a reusable function:

function name(selection, first, last) {
  selection
      .attr("first-name", first)
      .attr("last-name", last);
}

D3 Selections text

  • If a value is specified, sets the text content to the specified value on all selected elements, replacing any existing child elements.

  • If the value is a constant, then all elements are given the same text content; otherwise, if the value is a function, it is evaluated for each selected element, in order, being passed the current datum (d), the current index (i), and the current group (nodes), with this as the current DOM element (nodes[i]).

  • The function’s return value is then used to set each element’s text content. A null value will clear the content.

D3 Selections append

  • If the specified type is a string, appends a new element of this type (tag name) as the last child of each selected element, or before the next following sibling in the update selection if this is an enter selection.

  • The latter behavior for enter selections allows you to insert elements into the DOM in an order consistent with the new bound data; however, note that selection.order may still be required if updating elements change order (i.e., if the order of new data is inconsistent with old data).

  • If the specified type is a function, it is evaluated for each selected element, in order, being passed the current datum (d), the current index (i), and the current group (nodes), with this as the current DOM element (nodes[i]). This function should return an element to be appended. (The function typically creates a new element, but it may instead return an existing element.)

For example, to append a DIV element to each paragraph:

d3.selectAll("p").append("div");

This is equivalent to:

d3.selectAll("p").append(function() {
  return document.createElement("div");
});

Which is equivalent to:

d3.selectAll("p").select(function() {
  return this.appendChild(document.createElement("div"));
});

D3 style class attr and more play

Enter and Append

D3 Enter docs

enter:

  • Returns the enter selection: placeholder nodes for each datum that had no corresponding DOM element in the selection.
  • The enter selection is empty for selections not returned by selection.data.

  • The enter selection is typically used to create “missing” elements corresponding to new data.

  • For example, to create DIV elements from an array of numbers:

const div = d3.select("body")
  .selectAll("div")
  .data([4, 8, 15, 16, 23, 42])
  .enter().append("div")
    .text(d => d);
  • If the body is initially empty, the above code will create six new DIV elements, append them to the body in-order, and assign their text content as the associated (string-coerced) number:
<div>4</div>
<div>8</div>
<div>15</div>
<div>16</div>
<div>23</div>
<div>42</div>

Enter and Append Demo

Svg Enter and Append

SVG Enter Append Call

Enter and Append with Circles

Enter and Append

Click Fork Button to Play with this on your own

Enter and Append with Existing Rects

SVG Basic Shapes

SVG Basic Shapes:

  • Rectangle
  • Circle
  • Ellipse
  • Straight Line
  • Polyline
  • Polygon
  • Path

The basic shapes that SVG provides are:

  • rectangle

  • circle

  • ellipse

  • straight line

  • polyline, polygon and path

Rectangles

<svg width="50" height="50">
    <rect x="0" y="0" width="50" height="50" />
</svg>

For us to be able to use an SVG rectangle it must be defined inside of the SVG tags.

The x and y - which is where the rectangle is drawn from and the height and width

The SVG coordinate space starts at the top left and goes to the bottom right as x and y coordinates increase.

So as the height increases, the rectangle will get longer down

And as the width increases, the rectangle will get longer to the right.

var rectangles = [
  {"x": 0,   "y":   0, "width": 30, "height": 40},
  {"x": 50,  "y":  50, "width": 30, "height": 40},
  {"x": 100, "y": 100, "width": 30, "height": 40},
  {"x": 150, "y": 150, "width": 30, "height": 40}
];
var svgContainer = d3.select("body")
  .append("svg")
  .attr("width","560")
  .attr("height","900");
var svgRectangles = svgContainer
  .selectAll("rect")
  .data(rectangles)
  .enter()
  .append("rect");

This binds each JSON object to an SVG rectangle DOM element

svgRectangles
    .attr("x",      function (d,i) { return d.x;  })
    .attr("y",      function (d,i) { return d.y;      })
    .attr("width",  function (d,i) { return d.width;  })
    .attr("height", function (d,i) { return d.height; });

We use an anonymous function to extract the relevant information from each JSON object For the rectangle, this means the x, y, width and height information.

D3 Rectangle in SVG

Circles

<svg width="50" height="50">
    <circle cx="25" cy="25" r="25" />
</svg>

SVG circles must be defined inside of the SVG tags

The SVG circle takes in three main inputs:

  • cx which is where the center of the circle is drawn from
  • cy which is where the center of the circle is drawn from
  • r which is the radius of the circle.

The JSON objects should have the cx, cy and r properties.

var svgContainer = d3.select("body")
  .append("svg")
  .attr("width","560")
  .attr("height","900");

First, we define the SVG Container the rectangles will live in.

Note that we define the width and height of the SVG viewport container

var circles = [
  {"cx":  25, "cy":  25, "r": 20},
  {"cx":  75, "cy":  75, "r": 20},
  {"cx": 125, "cy": 125, "r": 20},
  {"cx": 175, "cy": 175, "r": 20}
];

The circles array contains 4 circle JSON Objects.

This will be the data source used to construct the circles

var svgCircles = svgContainer.selectAll("circle").data(circles).enter().append("circle");

This binds each JSON object to an SVG circle DOM element

svgCircles
    .attr("cx", function (d,i) { return d.cx; })
    .attr("cy", function (d,i) { return d.cy; })
    .attr("r",  function (d,i) { return d.r;  });

We use an anonymous function to extract the relevant information from each JSON object

For the circle, this means the cx, cy and r information.

Bonus if you can set the first SVG Circle to Red Color

Tip use the filter function with the svgCircles variable

D3 Circles in SVG

Ellipses

<svg width="50" height="50">
    <ellipse cx="25" cy="25" rx="15" ry="10" />
</svg>

SVG ellipses must be defined inside of the SVG tags.

The SVG ellipse takes in four main inputs:

  • cx which is where the center of the ellipse is drawn from
  • cy which is where the center of the ellipse is drawn from
  • x radius of the ellipse
  • y radius of the ellipse

The JSON object should have the cx, cy, rx and ry properties.

var svgContainer = d3.select("body")
  .append("svg")
  .attr("width","560")
  .attr("height","900");

Here we define the SVG Container the ellipses will live in.

Note that we define the width and height of the SVG viewport container

var ellipses = [
  {"cx":  25, "cy":  25, "rx": 15, "ry": 20},
  {"cx":  75, "cy":  75, "rx": 15, "ry": 20},
  {"cx": 125, "cy": 125, "rx": 15, "ry": 20},
  {"cx": 175, "cy": 175, "rx": 15, "ry": 20}
];

The ellipses array contains 4 ellipse JSON Objects.

This will be the data source used to construct the ellipses

var svgEllipses = svgContainer
  .selectAll("ellipse")
  .data(ellipses)
  .enter()
  .append("ellipse");
svgEllipses
    .attr("cx", function (d,i) { return d.cx; })
    .attr("cy", function (d,i) { return d.cy; })
    .attr("rx", function (d,i) { return d.rx; })
    .attr("ry", function (d,i) { return d.ry; });

We use an anonymous function to extract the relevant information from each JSON object

For the ellipse, this means the cx, cy, rx and ry information.

d3.selectAll('ellipse').data()

This will return the data bound to each ellipse

d3.selectAll('ellipse').nodes()

This will return each dom node for each ellipse

This binds each JSON object to an SVG ellipse DOM element

D3 Ellipses in SVG

Straight Lines

<svg width="50" height="50">
    <line x1="5" y1="5" x2="40" y2="40" stroke="gray" stroke-width="5" />
</svg>

SVG Straight Lines must be defined inside of the SVG tags.

The SVG Straight Line takes in six main inputs:

  • x1 which is where the line starts
  • y1 which is where the line starts
  • x2 which is where the line ends
  • y2 which is where the line ends
  • stroke which is the color of the line
  • stroke-width which is the stroke width

The stroke and stroke-width are necessary because an SVG line is dimensionless.

So by applying a stroke-width greater than zero, we can see the line.

The JSON object should have the x1, y1, x2, y2, stroke and stroke-width properties.

var svgContainer = d3.select("body")
  .append("svg")
  .attr("width","200")
  .attr("height","200");

First, we define the SVG Container the straight lines will live in.

Note that we define the width and height of the SVG viewport container

var straightLines = [
  { "x1":  0, "y1":  0, "x2": 40, "y2": 40, "stroke":"black", "stroke_width":5 },
  { "x1": 50, "y1": 50, "x2": 90, "y2": 90, "stroke":"black", "stroke_width":5 },
  { "x1":100, "y1":100, "x2":140, "y2":140, "stroke":"black", "stroke_width":5 },
  { "x1":150, "y1":150, "x2":190, "y2":190, "stroke":"black", "stroke_width":5 }
];

The straight lines array contains 4 straight line JSON Objects.

This will be the data source used to construct the straight lines

Note that for the stroke_width, we use an underscore to separate the stroke and width words

Why this is important will be seen shortly.

var svgStraightLines = svgContainer
  .selectAll("line")
  .data(straightLines)
  .enter()
  .append("line");

This binds each JSON object to an SVG Straight Line DOM element

svgStraightLines
    .attr("x1",           function (d,i) { return d.x1;           })
    .attr("y1",           function (d,i) { return d.y1;           })
    .attr("x2",           function (d,i) { return d.x2;           })
    .attr("y2",           function (d,i) { return d.y2;           })
    .attr("stroke",       function (d,i) { return d.stroke;       })
    .attr("stroke-width", function (d,i) { return d.stroke_width; });

We use an anonymous function to extract the relevant information from each JSON object

For the straight line, this means the x1, y1, x2, y2, stroke and stroke-width information.

Note, the stroke-width anonymous function uses the d.stroke {underscore} width.

If we have written stroke {dash} width, then JavaScript would have thought we were trying to do a subtraction.

This is very important to keep in mind as this can often cause bugs in the code.

D3 Straight Lines in SVG

Scales and Axis

D3 Scales Docs

Key Functions:

Scale Band

  • Constructs a new band scale with the empty domain, the unit range [0, 1], no padding, no rounding and center alignment.

Scale Linear

  • Constructs a new continuous scale with the unit domain [0, 1], the unit range [0, 1], the default interpolator and clamping disabled.

  • Linear scales are a good default choice for continuous quantitative data because they preserve proportional differences.

  • Each range value y can be expressed as a function of the domain value x: y = mx + b.

D3 Continuous domain

  • If domain is specified, sets the scale’s domain to the specified array of numbers.
  • The array must contain two or more elements.
  • If the elements in the given array are not numbers, they will be coerced to numbers.
  • If domain is not specified, returns a copy of the scale’s current domain.
var color = d3.scaleLinear()
    .domain([-1, 0, 1])
    .range(["red", "white", "green"]);

color(-0.5); // "rgb(255, 128, 128)"
color(+0.5); // "rgb(128, 192, 128)"

D3 Axis Docs

Scales and Axis

Notice in this file that I make a call to d3.csv

d3 request docs for csv and tsv files

  • Returns a new request for the CSV file at the specified url with the default mime type text/csv.
  • If no callback is specified, this is equivalent to:
d3.request(url)
    .mimeType("text/csv")
    .response(function(xhr) { return d3.csvParse(xhr.responseText, row); });
d3.request(url)
    .mimeType("text/csv")
    .response(function(xhr) { return d3.csvParse(xhr.responseText, row); })
    .get(callback);

Here is our function in the blockbuilder file

// dataset for city demographics by city
d3.csv('demographic-states-by-city.csv', (err, data) => {
  data.forEach(d => {
    d[COUNT_HISPANICS] = Number(d[COUNT_HISPANICS]) + 0; // y
  });

  // get min/max
  var min = d3.min(data, d => d[COUNT_HISPANICS]);
  var max = d3.max(data, d => d[COUNT_HISPANICS]);
  // or use extent, which gives back [min, max]
  const extent = d3.extent(data, d => d[COUNT_HISPANICS]);

  // try different scales, change the ranges, see what happens
  const yScale = d3.scaleLinear()
    .domain(extent)
    .range([height, 0]);

  // try passing in tick valuess
  const yAxis = d3.axisLeft()
    .scale(yScale);

  const axis = d3.select('svg').append('g')
    .attr('transform', 'translate(40, 20)')
    .call(yAxis);

  const text = axis.selectAll('text')
    .attr('fill', d => d === 35 ? 'blue' : 'green')
});

d3.csv(url, row, callback);

Notice we provided a url and a callback but not row

D3 scale and axis methods

D3 array method extent

  • Returns the minimum and maximum value in the given array using natural order.

  • If the array is empty, returns [undefined, undefined].

  • An optional accessor function may be specified, which is equivalent to calling array.map(accessor) before computing the extent.

D3 scale method scaleTime

  • Constructs a new time scale with the domain [2000-01-01, 2000-01-02]
    • the unit range [0, 1], the default interpolator and clamping disabled.

D3 axis method axisBottom

  • Constructs a new bottom-oriented axis generator for the given scale, with empty tick arguments, a tick size of 6 and padding of 3.
  • In this orientation, ticks are drawn below the horizontal domain path.

Introduction to D3 Scales by Mike Bostock

Click Fork Button to Play with this on your own

Shapes

SVG Elements

SVG Paths

SVG Transform

Path Attribute

  • d3-shape calculates the path attribute

Imagine if you had to hand code this:

<svg width="190" height="160" xmlns="http://www.w3.org/2000/svg">

  <path d="M10 10 C 20 20, 40 20, 50 10" stroke="black" fill="transparent"/>
  <path d="M70 10 C 70 20, 120 20, 120 10" stroke="black" fill="transparent"/>
  <path d="M130 10 C 120 20, 180 20, 170 10" stroke="black" fill="transparent"/>
  <path d="M10 60 C 20 80, 40 80, 50 60" stroke="black" fill="transparent"/>
  <path d="M70 60 C 70 80, 110 80, 110 60" stroke="black" fill="transparent"/>
  <path d="M130 60 C 120 80, 180 80, 170 60" stroke="black" fill="transparent"/>
  <path d="M10 110 C 20 140, 40 140, 50 110" stroke="black" fill="transparent"/>
  <path d="M70 110 C 70 140, 110 140, 110 110" stroke="black" fill="transparent"/>
  <path d="M130 110 C 120 140, 180 140, 170 110" stroke="black" fill="transparent"/>

</svg>

Pie

Pie Chart

  • The pie generator does not produce a shape directly, but instead computes the necessary angles to represent a tabular dataset as a pie or donut chart
  • These angles can then be passed to an arc generator

D3 shape pie method

  • Constructs a new pie generator with the default settings.

D3 shape arc method

  • Constructs a new arc generator with the default settings.

D3 shape arc with args method

If the radii and angles are instead defined as constants, you can generate an arc without any arguments:

var arc = d3.arc()
    .innerRadius(0)
    .outerRadius(100)
    .startAngle(0)
    .endAngle(Math.PI / 2);

arc(); // "M0,-100A100,100,0,0,1,100,0L0,0Z"

Area Chart

Enter and Update

Mikes Blog Post on Object Constancy

Animated transitions are pretty, but they also serve a purpose: they make it easier to follow the data. This is known as object constancy: a graphical element that represents a particular data point (such as Ohio) can be tracked visually through the transition. This lessens the cognitive burden by using preattentive processing of motion rather than sequential scanning of labels.

  • To achieve object constancy with D3.js, specify a key function as the second argument to selection.data.
  • This function takes a data point as input and returns a corresponding key: a string, such as a name, that uniquely identifies the data point.

For example, the bar chart above defines data as objects:

{
  "State": "ND",
  "Total": 641481,
  "Under 5 Years": 0.065,
  "5 to 13 Years": 0.105,
  "14 to 17 Years": 0.053,
  "18 to 24 Years": 0.129,
  "16 Years and Over": 0.804,
  "18 Years and Over": 0.777,
  "15 to 44 Years": 0.410,
  "45 to 64 Years": 0.260,
  "65 Years and Over": 0.147,
  "85 Years and Over": 0.028
}

with a suitable key function

function key(d) {
  return d.State;
}

Thinking with Joins

Mike Bostock’s Blog Post on Thinking with Joins

  • Data points joined to existing elements produce the update (inner) selection.
  • Leftover unbound data produce the enter selection (left), which represents missing elements.
  • Likewise, any remaining unbound elements produce the exit selection (right), which represents elements to be removed.

Now we can unravel the mysterious enter-append sequence through the data join:

  1. First, svg.selectAll(“circle”) returns a new empty selection, since the SVG container was empty. The parent node of this selection is the SVG container.

  2. This selection is then joined to an array of data, resulting in three new selections that represent the three possible states: enter, update, and exit. Since the selection was empty, the update and exit selections are empty, while the enter selection contains a placeholder for each new datum.

  3. The update selection is returned by selection.data, while the enter and exit selections hang off the update selection; selection.enter thus returns the enter selection.

  4. The missing elements are added to the SVG container by calling selection.append on the enter selection. This appends a new circle for each data point to the SVG

Exit and Merge

Enter and Update

Transitions

  • A transition is a selection-like interface for animating changes to the DOM.
  • Instead of applying changes instantaneously, transitions smoothly interpolate the DOM from its current state to the desired target state over a given duration.

To apply a transition, select elements, call selection.transition, and then make the desired changes.

For example:

d3.select("body")
  .transition()
    .style("background-color", "red");
  • Transitions are derived from selections via selection.transition.
  • You can also create a transition on the document root element using d3.transition.

selection.transition

  • Returns a new transition on the given selection with the specified name.
  • If a name is not specified, null is used.
  • The new transition is only exclusive with other transitions of the same name.
var t = d3.transition()
    .duration(750)
    .ease(d3.easeLinear);

d3.selectAll(".apple").transition(t)
    .style("fill", "red");

d3.selectAll(".orange").transition(t)
    .style("fill", "orange");

Circle Transitions

Click Fork Button to Play with this on your own

Force Layout

Force Layout Docs

In the domain of information visualization, physical simulations are useful for studying networks and hierarchies!

Force Layout is good at providing insights on the relationships between connections

d3 force layout guide

To give credit where it is due, the following content below is derived from the above url

D3 force layout uses a physics based simulator for positioning visual elements.

Forces can be set up between elements, For example:

  • all elements repel one another

  • elements are attracted to center(s) of gravity

  • linked elements are a fixed distance apart (network visualisation)

  • elements may not overlap (collision detection)

Force Simulation

There are 4 steps in setting up force simulation:

  1. Create an array of objects

  2. Call forceSimulation, passing in the array of objects.

  3. Add one or more force functions (forceManyBody, forceCenter, forceCollide, etc) to the system

  4. Set up callback function to update the element positions after each tick.

const dimensions = {
  width: 300,
  height: 300
};

const nodes = [{}, {}, {}, {}, {}];

let simulation = d3.forceSimulation(nodes)
  .force('charge', d3.forceManyBody())
  .force('center', d3.forceCenter(dimensions.width / 2, dimensions.height / 2))
  .on('tick', ticked);

function ticked() {
  var u = d3.select('svg')
    .selectAll('circle')
    .data(nodes)

  u.enter()
    .append('circle')
    .attr('r', 5)
    .merge(u)
    .attr('cx', function(d) {
      return d.x
    })
    .attr('cy', function(d) {
      return d.y
    })

  u.exit().remove()
}

D3 Force Functions

Force functions are added to the simulation using .force() where the first argument is a user defined id and the second argument the force function:

simulation.force('charge', d3.forceManyBody())

forceCenter

  • forceCenter (for setting the center of gravity of the system)

Creates a new centering force with the specified x- and y- coordinates. If x and y are not specified, they default to ⟨0,0⟩.

forceCenter is useful (if not essential) for centering your elements as a whole about a center point. (Without it elements might disappear off the page.)

Initialis with a center position:

d3.forceCenter(100, 100)

or using the configuration functions .x() and .y():

d3.forceCenter().x(100).y(100)

simulation.force('center', d3.forceCenter(100, 100))

forceManyBody

  • forceManyBody (for making elements attract or repel one another)

Creates a new many-body force with the default parameters.

forceManyBody causes all elements to attract or repel one another. The strength of the attraction or repulsion can be set using .strength() where a positive value will cause elements to attract one another while a negative value causes elements to repel each other. The default value is -30.

simulation.force('charge', d3.forceManyBody().strength(-20))

forceCollide

Creates a new circle collision force with the specified radius. If radius is not specified, it defaults to the constant one for all nodes.

forceCollide is used to stop elements overlapping and is useful when ‘clumping’ circles together.

We must specify the radius of the elements using .radius():

var numNodes = 100
var nodes = d3.range(numNodes).map(function(d) {
  return {radius: Math.random() * 25}
})

var simulation = d3.forceSimulation(nodes)
  .force('charge', d3.forceManyBody().strength(5))
  .force('center', d3.forceCenter(width / 2, height / 2))
  .force('collision', d3.forceCollide().radius(function(d) {
    return d.radius
  }))

forceX and forceY

forceX and forceY cause elements to be attracted towards specified position(s).

forceX

  • forceX for attracting elements to a given point

Creates a new positioning force along the x-axis towards the given position x. If x is not specified, it defaults to 0.

simulation.force('x', d3.forceX().x(function(d) {
  return xCenter[d.category];
}));

forceY

  • forceY for attracting elements to a given point

Creates a new positioning force along the y-axis towards the given position y. If y is not specified, it defaults to 0.

  • forceLink for creating a fixed distance between connected elements

Creates a new link force with the specified links and default parameters. If links is not specified, it defaults to the empty array.

  • forceLink pushes linked elements to be a fixed distance apart.
  • It requires an array of links that specify which elements we want to link together. * Each link object specifies a source and target element, where the value is the element’s array index:
var links = [
  {source: 0, target: 1},
  {source: 0, target: 2},
  {source: 0, target: 3},
  {source: 1, target: 6},
  {source: 3, target: 4},
  {source: 3, target: 7},
  {source: 4, target: 5},
  {source: 4, target: 7}
]

We pass our links array into the forceLink function using .links():

simulation.force('link', d3.forceLink().links(links))

Once again all credit for this content goes to d3indepth.com/force-layout/

Force Layout

Force Layout

Force Layout Forked Demo

D3 and Vuejs

D3 and Vuejs jsFiddle

Data Visualization Best Practices

There is a wonderful illustration in this Tableau White Paper on Data Visualization Best Practices

General Data Visualization Resources

Data Visualization Experts and Companies

  • Jer Thorp - Data artist.
  • Nicolas Feltron - Data artist.
  • Stamen - Data visualization company, excellent blog, heavy focus on maps.
  • Fathom - Another interesting data visualization company.

D3 Data Visualization Gitbook

Github Repo

If you like this information please get more details and challenges at Data Visualization Workshop and star the repository.

comments powered by Disqus