UNPKG

epubjs

Version:

Render ePub documents in the browser, across many devices

151 lines (149 loc) 56 kB
<?xml version="1.0" encoding="UTF-8" standalone="no"?> <!DOCTYPE html><html xmlns="http://www.w3.org/1999/xhtml" xmlns:epub="http://www.idpf.org/2007/ops" xmlns:m="http://www.w3.org/1998/Math/MathML" xmlns:pls="http://www.w3.org/2005/01/pronunciation-lexicon" xmlns:ssml="http://www.w3.org/2001/10/synthesis" xmlns:svg="http://www.w3.org/2000/svg"><head><title>Chapter 11. Layouts</title><link rel="stylesheet" type="text/css" href="core.css"/><meta name="generator" content="DocBook XSL Stylesheets V1.76.1"/><link rel="up" href="index.html" title="Interactive Data Visualization for the Web"/><link rel="prev" href="ch10.html" title="Chapter 10. Interactivity"/><link rel="next" href="ch12.html" title="Chapter 12. Geomapping"/></head><body><section class="chapter" title="Chapter 11. Layouts" epub:type="chapter" id="_layouts"><div class="titlepage"><div><div><h2 class="title">Chapter 11. Layouts</h2></div></div></div><p>Contrary to what the name implies, D3 <span class="emphasis"><em>layouts</em></span> do not, in fact, lay anything out for you on the screen. The layout methods have no direct <span class="emphasis"><em>visual</em></span> output. Rather, D3 layouts take data that you provide and remap or otherwise transform it, thereby generating <span class="emphasis"><em>new</em></span> data that is more convenient for a specific visual task. It’s still up to you to take that new data and generate visuals from it.<a id="ix_lay" class="indexterm"/><a id="I_indexterm11_id315841" class="indexterm"/></p><p>Here is a complete list of all D3 layouts:</p><div class="itemizedlist"><ul class="itemizedlist"><li class="listitem"> Bundle </li><li class="listitem"> Chord </li><li class="listitem"> Cluster </li><li class="listitem"> Force </li><li class="listitem"> Histogram </li><li class="listitem"> Pack </li><li class="listitem"> Partition </li><li class="listitem"> Pie </li><li class="listitem"> Stack </li><li class="listitem"> Tree </li><li class="listitem"> Treemap </li></ul></div><p>In this chapter, I introduce three of the most common: <span class="emphasis"><em>pie</em></span>, <span class="emphasis"><em>stack</em></span>, and <span class="emphasis"><em>force</em></span>. Each layout performs a different function, and each has its own idiosyncrasies.</p><p>If you are curious about any of the other D3 layouts, check out <a class="ulink" href="https://github.com/mbostock/d3/wiki/Gallery" target="_top">the many examples on the D3 website</a>, and be sure to reference <a class="ulink" href="https://github.com/mbostock/d3/wiki/Layouts" target="_top">the official API documentation on layouts</a>.</p><div class="sect1" title="Pie Layout"><div class="titlepage"><div><div><h2 class="title" style="clear: both" id="_pie_layout">Pie Layout</h2></div></div></div><p><code class="literal">d3.layout.pie()</code> might not be as delicious as it sounds, but it’s still worthy of your attention. Obviously, its typical use is for creating pie charts, like the example in <a class="xref" href="ch11.html#simple_pie_chart" title="Figure 11-1. A simple pie chart">Figure 11-1</a>.</p><div class="figure"><a id="simple_pie_chart"/><div class="figure-contents"><div class="mediaobject"><a id="I_mediaobject11_id316014"/><img src="httpatomoreillycomsourceoreillyimages1614848.png" alt="A simple pie chart"/></div></div><div class="figure-title">Figure 11-1. A simple pie chart</div></div><p>Feel free to open up the sample code for this in <span class="emphasis"><em>01_pie.html</em></span> and poke around.<a id="I_indexterm11_id316040" class="indexterm"/><a id="I_indexterm11_id316048" class="indexterm"/><a id="I_indexterm11_id316054" class="indexterm"/></p><p>To draw those pretty wedges, we need to know a number of measurements, including an inner and outer radius for each wedge, plus the starting and ending angles. The purpose of the pie layout is to take your data and calculate all those messy angles for you, sparing you from ever having to think about radians.</p><p>Remember radians? In case you don’t, here’s a quick refresher. Just as there are 360° in a circle, there are 2π radians. So π radians equals 180°, or half a circle. Most people find it easier to think in terms of degrees; computers prefer radians.<a id="I_indexterm11_id316075" class="indexterm"/><a id="I_indexterm11_id316081" class="indexterm"/></p><p>For this pie chart, let’s start, as usual, with a very simple dataset:</p><a id="I_programlisting11_id316092"/><pre class="programlisting"><code class="kd">var</code> <code class="nx">dataset</code> <code class="o">=</code> <code class="p">[</code> <code class="mi">5</code><code class="p">,</code> <code class="mi">10</code><code class="p">,</code> <code class="mi">20</code><code class="p">,</code> <code class="mi">45</code><code class="p">,</code> <code class="mi">6</code><code class="p">,</code> <code class="mi">25</code> <code class="p">];</code></pre><p>We can define a default pie layout very simply as:</p><a id="I_programlisting11_id316101"/><pre class="programlisting"><code class="kd">var</code> <code class="nx">pie</code> <code class="o">=</code> <code class="nx">d3</code><code class="p">.</code><code class="nx">layout</code><code class="p">.</code><code class="nx">pie</code><code class="p">();</code></pre><p>Then, all that remains is to hand off our data to the new <code class="literal">pie()</code> function, as in <code class="literal">pie(dataset)</code>. Compare the datasets before and after in <a class="xref" href="ch11.html#pieified_data" title="Figure 11-2. Your data, pie-ified">Figure 11-2</a>.</p><div class="figure"><a id="pieified_data"/><div class="figure-contents"><div class="mediaobject"><a id="I_mediaobject11_id316135"/><img src="httpatomoreillycomsourceoreillyimages1614849.png" alt="Your data, pie-ified"/></div></div><div class="figure-title">Figure 11-2. Your data, pie-ified</div></div><p>The pie layout takes our simple array of numbers and generates an array of objects, one object for each value. Each of those objects now has a few new values—most important, <code class="literal">startAngle</code> and <code class="literal">endAngle</code>. Wow, that was easy!</p><p>Now, to actually draw the wedges, we turn to <code class="literal">d3.svg.arc()</code>, a handy built-in function for drawing arcs as SVG <code class="literal">path</code> elements. We haven’t talked about <code class="literal">path</code>s yet, but they are SVG’s answer to drawing irregular forms. Anything that’s not a <code class="literal">rect</code>, <code class="literal">circle</code>, or another basic shape can be drawn as a <code class="literal">path</code>. The catch is, the syntax for defining <code class="literal">path</code> values is not particularly human-friendly.<a id="I_indexterm11_id316198" class="indexterm"/><a id="I_indexterm11_id316204" class="indexterm"/> For example, here’s the code for the big, red wedge in <a class="xref" href="ch11.html#simple_pie_chart" title="Figure 11-1. A simple pie chart">Figure 11-1</a>:</p><a id="I_programlisting11_id316219"/><pre class="programlisting"><code class="nt">&lt;path</code> <code class="na">fill=</code><code class="s">"#d62728"</code> <code class="na">d=</code><code class="s">"M9.184850993605149e-15,-150A150,150 0 0,1</code> <code class="s"> 83.99621792063931,124.27644738657631L0,0Z"</code><code class="nt">&gt;&lt;/path&gt;</code></pre><p>If you can understand that, then you don’t need this book.</p><p>The bottom line is, it’s best to let functions like <code class="literal">d3.svg.arc()</code> handle generating <code class="literal">path</code>s programatically. You don’t want to try writing this stuff out by hand.</p><p>Arcs are defined as custom functions, and they require inner and outer radius values:</p><a id="I_programlisting11_id316245"/><pre class="programlisting"><code class="kd">var</code> <code class="nx">w</code> <code class="o">=</code> <code class="mi">300</code><code class="p">;</code> <code class="kd">var</code> <code class="nx">h</code> <code class="o">=</code> <code class="mi">300</code><code class="p">;</code> <code class="kd">var</code> <code class="nx">outerRadius</code> <code class="o">=</code> <code class="nx">w</code> <code class="o">/</code> <code class="mi">2</code><code class="p">;</code> <code class="kd">var</code> <code class="nx">innerRadius</code> <code class="o">=</code> <code class="mi">0</code><code class="p">;</code> <code class="kd">var</code> <code class="nx">arc</code> <code class="o">=</code> <code class="nx">d3</code><code class="p">.</code><code class="nx">svg</code><code class="p">.</code><code class="nx">arc</code><code class="p">()</code> <code class="p">.</code><code class="nx">innerRadius</code><code class="p">(</code><code class="nx">innerRadius</code><code class="p">)</code> <code class="p">.</code><code class="nx">outerRadius</code><code class="p">(</code><code class="nx">outerRadius</code><code class="p">);</code></pre><p>Here I’m setting the size of the whole chart to be 300 by 300 square. Then I’m setting the <code class="literal">outerRadius</code> to half of that, or 150. The <code class="literal">innerRadius</code> is zero. We’ll revisit <code class="literal">innerRadius</code> in a moment.</p><p>We’re ready to draw some wedges! First, we create the SVG element, per usual:</p><a id="I_programlisting11_id316276"/><pre class="programlisting"><code class="c1">//Create SVG element</code> <code class="kd">var</code> <code class="nx">svg</code> <code class="o">=</code> <code class="nx">d3</code><code class="p">.</code><code class="nx">select</code><code class="p">(</code><code class="s2">"body"</code><code class="p">)</code> <code class="p">.</code><code class="nx">append</code><code class="p">(</code><code class="s2">"svg"</code><code class="p">)</code> <code class="p">.</code><code class="nx">attr</code><code class="p">(</code><code class="s2">"width"</code><code class="p">,</code> <code class="nx">w</code><code class="p">)</code> <code class="p">.</code><code class="nx">attr</code><code class="p">(</code><code class="s2">"height"</code><code class="p">,</code> <code class="nx">h</code><code class="p">);</code></pre><p>Then we can create new groups for each incoming wedge, binding the pie-ified data to the new elements, and translating each group into the center of the chart, so the <code class="literal">path</code>s will appear in the right place:</p><a id="I_programlisting11_id316292"/><pre class="programlisting"><code class="c1">//Set up groups</code> <code class="kd">var</code> <code class="nx">arcs</code> <code class="o">=</code> <code class="nx">svg</code><code class="p">.</code><code class="nx">selectAll</code><code class="p">(</code><code class="s2">"g.arc"</code><code class="p">)</code> <code class="p">.</code><code class="nx">data</code><code class="p">(</code><code class="nx">pie</code><code class="p">(</code><code class="nx">dataset</code><code class="p">))</code> <code class="p">.</code><code class="nx">enter</code><code class="p">()</code> <code class="p">.</code><code class="nx">append</code><code class="p">(</code><code class="s2">"g"</code><code class="p">)</code> <code class="p">.</code><code class="nx">attr</code><code class="p">(</code><code class="s2">"class"</code><code class="p">,</code> <code class="s2">"arc"</code><code class="p">)</code> <code class="p">.</code><code class="nx">attr</code><code class="p">(</code><code class="s2">"transform"</code><code class="p">,</code> <code class="s2">"translate("</code> <code class="o">+</code> <code class="nx">outerRadius</code> <code class="o">+</code> <code class="s2">", "</code> <code class="o">+</code> <code class="nx">outerRadius</code> <code class="o">+</code> <code class="s2">")"</code><code class="p">);</code></pre><p>Note that we’re saving a reference to each newly created <code class="literal">g</code> in a variable called <code class="literal">arcs</code>.</p><p>Finally, within each new <code class="literal">g</code>, we append a <code class="literal">path</code>. A <code class="literal">path</code>s path description is defined in the <code class="literal">d</code> attribute. So here we call the <code class="literal">arc</code> generator, which generates the path information based on the data already bound to this group:</p><a id="I_programlisting11_id316335"/><pre class="programlisting"><code class="c1">//Draw arc paths</code> <code class="nx">arcs</code><code class="p">.</code><code class="nx">append</code><code class="p">(</code><code class="s2">"path"</code><code class="p">)</code> <code class="p">.</code><code class="nx">attr</code><code class="p">(</code><code class="s2">"fill"</code><code class="p">,</code> <code class="kd">function</code><code class="p">(</code><code class="nx">d</code><code class="p">,</code> <code class="nx">i</code><code class="p">)</code> <code class="p">{</code> <code class="k">return</code> <code class="nx">color</code><code class="p">(</code><code class="nx">i</code><code class="p">);</code> <code class="p">})</code> <code class="p">.</code><code class="nx">attr</code><code class="p">(</code><code class="s2">"d"</code><code class="p">,</code> <code class="nx">arc</code><code class="p">);</code></pre><p>Oh, and you might be wondering where those colors are coming from. If you check out <span class="emphasis"><em>01_pie.html</em></span>, you’ll note this line:</p><a id="I_programlisting11_id316350"/><pre class="programlisting"><code class="kd">var</code> <code class="nx">color</code> <code class="o">=</code> <code class="nx">d3</code><code class="p">.</code><code class="nx">scale</code><code class="p">.</code><code class="nx">category10</code><code class="p">();</code></pre><p>D3 has a number of handy ways to generate categorical colors. These might not be your favorite colors, but they are quick to drop into any visualization while you’re in the prototyping stage. <code class="literal">d3.scale.category10()</code> creates an <span class="emphasis"><em>ordinal</em></span> scale with an output range of 10 different colors. (See <a class="ulink" href="https://github.com/mbostock/d3/wiki/Ordinal-Scales" target="_top">the wiki</a> for more information on these color scales as well as perceptually calibrated color palettes, based on research by Cynthia Brewer, that are included with D3.)<a id="I_indexterm11_id316376" class="indexterm"/><a id="I_indexterm11_id316383" class="indexterm"/><a id="I_indexterm11_id316389" class="indexterm"/></p><p>Lastly, we can generate text labels for each wedge:</p><a id="I_programlisting11_id316400"/><pre class="programlisting"><code class="nx">arcs</code><code class="p">.</code><code class="nx">append</code><code class="p">(</code><code class="s2">"text"</code><code class="p">)</code> <code class="p">.</code><code class="nx">attr</code><code class="p">(</code><code class="s2">"transform"</code><code class="p">,</code> <code class="kd">function</code><code class="p">(</code><code class="nx">d</code><code class="p">)</code> <code class="p">{</code> <code class="k">return</code> <code class="s2">"translate("</code> <code class="o">+</code> <code class="nx">arc</code><code class="p">.</code><code class="nx">centroid</code><code class="p">(</code><code class="nx">d</code><code class="p">)</code> <code class="o">+</code> <code class="s2">")"</code><code class="p">;</code> <code class="p">})</code> <code class="p">.</code><code class="nx">attr</code><code class="p">(</code><code class="s2">"text-anchor"</code><code class="p">,</code> <code class="s2">"middle"</code><code class="p">)</code> <code class="p">.</code><code class="nx">text</code><code class="p">(</code><code class="kd">function</code><code class="p">(</code><code class="nx">d</code><code class="p">)</code> <code class="p">{</code> <code class="k">return</code> <code class="nx">d</code><code class="p">.</code><code class="nx">value</code><code class="p">;</code> <code class="p">});</code></pre><p>Note that in <code class="literal">text()</code>, we reference the value with <code class="literal">d.value</code> instead of just <code class="literal">d</code>. This is because we bound the pie-ified data, so instead of referencing our original array (<code class="literal">d</code>), we have to reference the array of objects (<code class="literal">d.value</code>).</p><p>The only thing new here is <code class="literal">arc.centroid(d)</code>. WTH? A <span class="emphasis"><em>centroid</em></span> is the calculated center point of any shape, whether that shape is regular (like a square) or highly irregular (like an outline of the state of Maryland). <code class="literal">arc.centroid()</code> is a super-helpful function that calculates and returns the center point of any arc. We translate each <code class="literal">text</code> label element to each arc’s centroid, and that’s how we get the text labels to float right in the middle of each wedge.<a id="I_indexterm11_id316452" class="indexterm"/></p><p>Bonus tip: Remember how <code class="literal">arc()</code> required an <code class="literal">innerRadius</code> value? We can expand that to anything greater than zero, and our pie chart becomes a <span class="emphasis"><em>ring</em></span> chart like the one<a id="I_indexterm11_id316474" class="indexterm"/><a id="I_indexterm11_id316480" class="indexterm"/> shown in <a class="xref" href="ch11.html#simple_ring_chart" title="Figure 11-3. A simple ring chart">Figure 11-3</a>:</p><a id="I_programlisting11_id316495"/><pre class="programlisting"><code class="kd">var</code> <code class="nx">innerRadius</code> <code class="o">=</code> <code class="nx">w</code> <code class="o">/</code> <code class="mi">3</code><code class="p">;</code></pre><div class="figure"><a id="simple_ring_chart"/><div class="figure-contents"><div class="mediaobject"><a id="I_mediaobject11_id316508"/><img src="httpatomoreillycomsourceoreillyimages1614850.png" alt="A simple ring chart"/></div></div><div class="figure-title">Figure 11-3. A simple ring chart</div></div><p>Check it out in <span class="emphasis"><em>02_ring.html</em></span>.</p><p>One more thing: The pie layout automatically reordered our data values from largest to smallest. Remember, we started with <code class="literal">[ 5, 10, 20, 45, 6, 25 ]</code>, so the small value of 6 should have appeared between 45 and 25, but no—the layout sorted our values in descending order, so the chart began with 45 at the 12 o’clock position, and everything just goes clockwise from there.</p></div><div class="sect1" title="Stack Layout"><div class="titlepage"><div><div><h2 class="title" style="clear: both" id="_stack_layout">Stack Layout</h2></div></div></div><p><code class="literal">d3.layout.stack()</code> converts two-dimensional data into “stacked” data; it calculates a baseline value for each datum, so you can “stack” layers of data on top of one another. This can be used to generate stacked bar charts, stacked area charts, and even streamgraphs (which are just stacked area charts but without the rigid starting baseline value of zero).<a id="I_indexterm11_id316561" class="indexterm"/><a id="I_indexterm11_id316571" class="indexterm"/><a id="I_indexterm11_id316577" class="indexterm"/></p><p>For example, we’ll start with the stacked bar chart in <a class="xref" href="ch11.html#simple_stacked_bar_chart" title="Figure 11-4. A simple stacked bar chart">Figure 11-4</a>.</p><div class="figure"><a id="simple_stacked_bar_chart"/><div class="figure-contents"><div class="mediaobject"><a id="I_mediaobject11_id316603"/><img src="httpatomoreillycomsourceoreillyimages1614851.png" alt="A simple stacked bar chart"/></div></div><div class="figure-title">Figure 11-4. A simple stacked bar chart</div></div><p>Let’s say you had some data like this:</p><a id="I_programlisting11_id316624"/><pre class="programlisting"><code class="kd">var</code> <code class="nx">dataset</code> <code class="o">=</code> <code class="p">[</code> <code class="p">{</code> <code class="nx">apples</code><code class="o">:</code> <code class="mi">5</code><code class="p">,</code> <code class="nx">oranges</code><code class="o">:</code> <code class="mi">10</code><code class="p">,</code> <code class="nx">grapes</code><code class="o">:</code> <code class="mi">22</code> <code class="p">},</code> <code class="p">{</code> <code class="nx">apples</code><code class="o">:</code> <code class="mi">4</code><code class="p">,</code> <code class="nx">oranges</code><code class="o">:</code> <code class="mi">12</code><code class="p">,</code> <code class="nx">grapes</code><code class="o">:</code> <code class="mi">28</code> <code class="p">},</code> <code class="p">{</code> <code class="nx">apples</code><code class="o">:</code> <code class="mi">2</code><code class="p">,</code> <code class="nx">oranges</code><code class="o">:</code> <code class="mi">19</code><code class="p">,</code> <code class="nx">grapes</code><code class="o">:</code> <code class="mi">32</code> <code class="p">},</code> <code class="p">{</code> <code class="nx">apples</code><code class="o">:</code> <code class="mi">7</code><code class="p">,</code> <code class="nx">oranges</code><code class="o">:</code> <code class="mi">23</code><code class="p">,</code> <code class="nx">grapes</code><code class="o">:</code> <code class="mi">35</code> <code class="p">},</code> <code class="p">{</code> <code class="nx">apples</code><code class="o">:</code> <code class="mi">23</code><code class="p">,</code> <code class="nx">oranges</code><code class="o">:</code> <code class="mi">17</code><code class="p">,</code> <code class="nx">grapes</code><code class="o">:</code> <code class="mi">43</code> <code class="p">}</code> <code class="p">];</code></pre><p>The first step is to rearrange that data into an array of arrays. Each array represents the data for one category (e.g., apples, oranges, or grapes). Within each category array, you’ll need an object for each data value, which itself must contain an <code class="literal">x</code> and a <code class="literal">y</code> value. The <code class="literal">x</code> in our case is just an ID number. The <code class="literal">y</code> is the actual data value:</p><a id="I_programlisting11_id316653"/><pre class="programlisting"><code class="kd">var</code> <code class="nx">dataset</code> <code class="o">=</code> <code class="p">[</code> <code class="p">[</code> <code class="p">{</code> <code class="nx">x</code><code class="o">:</code> <code class="mi">0</code><code class="p">,</code> <code class="nx">y</code><code class="o">:</code> <code class="mi">5</code> <code class="p">},</code> <code class="p">{</code> <code class="nx">x</code><code class="o">:</code> <code class="mi">1</code><code class="p">,</code> <code class="nx">y</code><code class="o">:</code> <code class="mi">4</code> <code class="p">},</code> <code class="p">{</code> <code class="nx">x</code><code class="o">:</code> <code class="mi">2</code><code class="p">,</code> <code class="nx">y</code><code class="o">:</code> <code class="mi">2</code> <code class="p">},</code> <code class="p">{</code> <code class="nx">x</code><code class="o">:</code> <code class="mi">3</code><code class="p">,</code> <code class="nx">y</code><code class="o">:</code> <code class="mi">7</code> <code class="p">},</code> <code class="p">{</code> <code class="nx">x</code><code class="o">:</code> <code class="mi">4</code><code class="p">,</code> <code class="nx">y</code><code class="o">:</code> <code class="mi">23</code> <code class="p">}</code> <code class="p">],</code> <code class="p">[</code> <code class="p">{</code> <code class="nx">x</code><code class="o">:</code> <code class="mi">0</code><code class="p">,</code> <code class="nx">y</code><code class="o">:</code> <code class="mi">10</code> <code class="p">},</code> <code class="p">{</code> <code class="nx">x</code><code class="o">:</code> <code class="mi">1</code><code class="p">,</code> <code class="nx">y</code><code class="o">:</code> <code class="mi">12</code> <code class="p">},</code> <code class="p">{</code> <code class="nx">x</code><code class="o">:</code> <code class="mi">2</code><code class="p">,</code> <code class="nx">y</code><code class="o">:</code> <code class="mi">19</code> <code class="p">},</code> <code class="p">{</code> <code class="nx">x</code><code class="o">:</code> <code class="mi">3</code><code class="p">,</code> <code class="nx">y</code><code class="o">:</code> <code class="mi">23</code> <code class="p">},</code> <code class="p">{</code> <code class="nx">x</code><code class="o">:</code> <code class="mi">4</code><code class="p">,</code> <code class="nx">y</code><code class="o">:</code> <code class="mi">17</code> <code class="p">}</code> <code class="p">],</code> <code class="p">[</code> <code class="p">{</code> <code class="nx">x</code><code class="o">:</code> <code class="mi">0</code><code class="p">,</code> <code class="nx">y</code><code class="o">:</code> <code class="mi">22</code> <code class="p">},</code> <code class="p">{</code> <code class="nx">x</code><code class="o">:</code> <code class="mi">1</code><code class="p">,</code> <code class="nx">y</code><code class="o">:</code> <code class="mi">28</code> <code class="p">},</code> <code class="p">{</code> <code class="nx">x</code><code class="o">:</code> <code class="mi">2</code><code class="p">,</code> <code class="nx">y</code><code class="o">:</code> <code class="mi">32</code> <code class="p">},</code> <code class="p">{</code> <code class="nx">x</code><code class="o">:</code> <code class="mi">3</code><code class="p">,</code> <code class="nx">y</code><code class="o">:</code> <code class="mi">35</code> <code class="p">},</code> <code class="p">{</code> <code class="nx">x</code><code class="o">:</code> <code class="mi">4</code><code class="p">,</code> <code class="nx">y</code><code class="o">:</code> <code class="mi">43</code> <code class="p">}</code> <code class="p">]</code> <code class="p">];</code></pre><p>Our original <code class="literal">dataset</code> looks like <a class="xref" href="ch11.html#prestacked_data" title="Figure 11-5. The data before stacking">Figure 11-5</a> in the console.</p><div class="figure"><a id="prestacked_data"/><div class="figure-contents"><div class="mediaobject"><a id="I_mediaobject11_id316683"/><img src="httpatomoreillycomsourceoreillyimages1614852.png" alt="The data before stacking"/></div></div><div class="figure-title">Figure 11-5. The data before stacking</div></div><p>Then we initialize our stack layout function, and call it on <code class="literal">dataset</code>:</p><a id="I_programlisting11_id316710"/><pre class="programlisting"><code class="kd">var</code> <code class="nx">stack</code> <code class="o">=</code> <code class="nx">d3</code><code class="p">.</code><code class="nx">layout</code><code class="p">.</code><code class="nx">stack</code><code class="p">();</code> <code class="nx">stack</code><code class="p">(</code><code class="nx">dataset</code><code class="p">);</code></pre><p>Now the stacked data is shown in <a class="xref" href="ch11.html#stacked_data" title="Figure 11-6. The data after stacking">Figure 11-6</a>.</p><div class="figure"><a id="stacked_data"/><div class="figure-contents"><div class="mediaobject"><a id="I_mediaobject11_id316732"/><img src="httpatomoreillycomsourceoreillyimages1614853.png" alt="The data after stacking"/></div></div><div class="figure-title">Figure 11-6. The data after stacking</div></div><p>Can you spot the difference? In the stacked data, each object has been given a <code class="literal">y0</code> value. This is the <span class="emphasis"><em>baseline</em></span> value. Notice that the <code class="literal">y0</code> baseline value is equal to the sum of all the <code class="literal">y</code> values in the preceding categories. For example, reading left to right across the top, the first object’s <code class="literal">y</code> value is 5, and its <code class="literal">y0</code> is zero. To the right (in the oranges column), the first object has a <code class="literal">y</code> value of 10, and a <code class="literal">y0</code> of 5. (Aha! That’s just the value of the first object’s <code class="literal">y</code>!) On the far right (in the grapes column), we see a <code class="literal">y</code> value of 22, and a <code class="literal">y0</code> of 15 (which is just 5 + 10).</p><p>To “stack” elements visually, now we can reference each data object’s baseline value as well as its height. See all the code in <span class="emphasis"><em>03_stacked_bar.html</em></span>. (I leave it as an exercise to you to align the stacks against the bottom x-axis.) Here’s the critical excerpt:</p><a id="I_programlisting11_id316806"/><pre class="programlisting"><code class="kd">var</code> <code class="nx">rects</code> <code class="o">=</code> <code class="nx">groups</code><code class="p">.</code><code class="nx">selectAll</code><code class="p">(</code><code class="s2">"rect"</code><code class="p">)</code> <code class="p">.</code><code class="nx">data</code><code class="p">(</code><code class="kd">function</code><code class="p">(</code><code class="nx">d</code><code class="p">)</code> <code class="p">{</code> <code class="k">return</code> <code class="nx">d</code><code class="p">;</code> <code class="p">})</code> <code class="p">.</code><code class="nx">enter</code><code class="p">()</code> <code class="p">.</code><code class="nx">append</code><code class="p">(</code><code class="s2">"rect"</code><code class="p">)</code> <code class="p">.</code><code class="nx">attr</code><code class="p">(</code><code class="s2">"x"</code><code class="p">,</code> <code class="kd">function</code><code class="p">(</code><code class="nx">d</code><code class="p">,</code> <code class="nx">i</code><code class="p">)</code> <code class="p">{</code> <code class="k">return</code> <code class="nx">xScale</code><code class="p">(</code><code class="nx">i</code><code class="p">);</code> <code class="p">})</code> <code class="p">.</code><code class="nx">attr</code><code class="p">(</code><code class="s2">"y"</code><code class="p">,</code> <code class="kd">function</code><code class="p">(</code><code class="nx">d</code><code class="p">)</code> <code class="p">{</code> <code class="k">return</code> <code class="nx">yScale</code><code class="p">(</code><code class="nx">d</code><code class="p">.</code><code class="nx">y0</code><code class="p">);</code> <code class="p">})</code> <code class="p">.</code><code class="nx">attr</code><code class="p">(</code><code class="s2">"height"</code><code class="p">,</code> <code class="kd">function</code><code class="p">(</code><code class="nx">d</code><code class="p">)</code> <code class="p">{</code> <code class="k">return</code> <code class="nx">yScale</code><code class="p">(</code><code class="nx">d</code><code class="p">.</code><code class="nx">y</code><code class="p">);</code> <code class="p">})</code> <code class="p">.</code><code class="nx">attr</code><code class="p">(</code><code class="s2">"width"</code><code class="p">,</code> <code class="nx">xScale</code><code class="p">.</code><code class="nx">rangeBand</code><code class="p">());</code></pre><p>Note how for <code class="literal">y</code> and <code class="literal">height</code> we reference <code class="literal">d.y0</code> and <code class="literal">d.y</code>, respectively.</p></div><div class="sect1" title="Force Layout"><div class="titlepage"><div><div><h2 class="title" style="clear: both" id="_force_layout">Force Layout</h2></div></div></div><p>Force-directed layouts are so-called because they use simulations of physical <span class="emphasis"><em>forces</em></span> to arrange elements on the screen. Arguably, they are a bit overused, yet they make a great demo and just look <span class="emphasis"><em>so darn cool</em></span>. Everyone wants to learn how to make one, so let’s talk about it.<a id="ix_lflay" class="indexterm"/><a id="ix_force" class="indexterm"/></p><p>Force layouts are typically used with network data. In computer science, this kind of dataset is called a <span class="emphasis"><em>graph</em></span>. A simple graph is a list of <span class="emphasis"><em>nodes</em></span> and <span class="emphasis"><em>edges</em></span>. The nodes are entities in the dataset, and the edges are the <span class="emphasis"><em>connections</em></span> between nodes. Some nodes will be connected by edges, and others won’t. Nodes are commonly represented as circles, and edges as lines. But of course the visual representation is up to you—D3 just helps manage all the mechanics behind the scenes.<a id="I_indexterm11_id316910" class="indexterm"/><a id="I_indexterm11_id316922" class="indexterm"/><a id="I_indexterm11_id316928" class="indexterm"/></p><p>The physical metaphor here is of particles that repel each other, yet are also connected by springs. The repelling forces push particles away from each other, preventing visual overlap, and the springs prevent them from just flying out into space, thereby keeping them on the screen where we can see them.</p><p><a class="xref" href="ch11.html#force_layouta" title="Figure 11-7. A simple force layout">Figure 11-7</a> provides a visual preview of what we’re coding toward.</p><div class="figure"><a id="force_layouta"/><div class="figure-contents"><div class="mediaobject"><a id="I_mediaobject11_id316955"/><img src="httpatomoreillycomsourceoreillyimages1614854.png.jpg" alt="A simple force layout"/></div></div><div class="figure-title">Figure 11-7. A simple force layout</div></div><p>D3’s force layout expects us to provide nodes and edges separately, as arrays of objects. Here we have one <code class="literal">dataset</code> object that contains two elements, <code class="literal">nodes</code> and <code class="literal">edges</code>, each of which is itself an array of objects:</p><a id="I_programlisting11_id316989"/><pre class="programlisting"><code class="kd">var</code> <code class="nx">dataset</code> <code class="o">=</code> <code class="p">{</code> <code class="nx">nodes</code><code class="o">:</code> <code class="p">[</code> <code class="p">{</code> <code class="nx">name</code><code class="o">:</code> <code class="s2">"Adam"</code> <code class="p">},</code> <code class="p">{</code> <code class="nx">name</code><code class="o">:</code> <code class="s2">"Bob"</code> <code class="p">},</code> <code class="p">{</code> <code class="nx">name</code><code class="o">:</code> <code class="s2">"Carrie"</code> <code class="p">},</code> <code class="p">{</code> <code class="nx">name</code><code class="o">:</code> <code class="s2">"Donovan"</code> <code class="p">},</code> <code class="p">{</code> <code class="nx">name</code><code class="o">:</code> <code class="s2">"Edward"</code> <code class="p">},</code> <code class="p">{</code> <code class="nx">name</code><code class="o">:</code> <code class="s2">"Felicity"</code> <code class="p">},</code> <code class="p">{</code> <code class="nx">name</code><code class="o">:</code> <code class="s2">"George"</code> <code class="p">},</code> <code class="p">{</code> <code class="nx">name</code><code class="o">:</code> <code class="s2">"Hannah"</code> <code class="p">},</code> <code class="p">{</code> <code class="nx">name</code><code class="o">:</code> <code class="s2">"Iris"</code> <code class="p">},</code> <code class="p">{</code> <code class="nx">name</code><code class="o">:</code> <code class="s2">"Jerry"</code> <code class="p">}</code> <code class="p">],</code> <code class="nx">edges</code><code class="o">:</code> <code class="p">[</code> <code class="p">{</code> <code class="nx">source</code><code class="o">:</code> <code class="mi">0</code><code class="p">,</code> <code class="nx">target</code><code class="o">:</code> <code class="mi">1</code> <code class="p">},</code> <code class="p">{</code> <code class="nx">source</code><code class="o">:</code> <code class="mi">0</code><code class="p">,</code> <code class="nx">target</code><code class="o">:</code> <code class="mi">2</code> <code class="p">},</code> <code class="p">{</code> <code class="nx">source</code><code class="o">:</code> <code class="mi">0</code><code class="p">,</code> <code class="nx">target</code><code class="o">:</code> <code class="mi">3</code> <code class="p">},</code> <code class="p">{</code> <code class="nx">source</code><code class="o">:</code> <code class="mi">0</code><code class="p">,</code> <code class="nx">target</code><code class="o">:</code> <code class="mi">4</code> <code class="p">},</code> <code class="p">{</code> <code class="nx">source</code><code class="o">:</code> <code class="mi">1</code><code class="p">,</code> <code class="nx">target</code><code class="o">:</code> <code class="mi">5</code> <code class="p">},</code> <code class="p">{</code> <code class="nx">source</code><code class="o">:</code> <code class="mi">2</code><code class="p">,</code> <code class="nx">target</code><code class="o">:</code> <code class="mi">5</code> <code class="p">},</code> <code class="p">{</code> <code class="nx">source</code><code class="o">:</code> <code class="mi">2</code><code class="p">,</code> <code class="nx">target</code><code class="o">:</code> <code class="mi">5</code> <code class="p">},</code> <code class="p">{</code> <code class="nx">source</code><code class="o">:</code> <code class="mi">3</code><code class="p">,</code> <code class="nx">target</code><code class="o">:</code> <code class="mi">4</code> <code class="p">},</code> <code class="p">{</code> <code class="nx">source</code><code class="o">:</code> <code class="mi">5</code><code class="p">,</code> <code class="nx">target</code><code class="o">:</code> <code class="mi">8</code> <code class="p">},</code> <code class="p">{</code> <code class="nx">source</code><code class="o">:</code> <code class="mi">5</code><code class="p">,</code> <code class="nx">target</code><code class="o">:</code> <code class="mi">9</code> <code class="p">},</code> <code class="p">{</code> <code class="nx">source</code><code class="o">:</code> <code class="mi">6</code><code class="p">,</code> <code class="nx">target</code><code class="o">:</code> <code class="mi">7</code> <code class="p">},</code> <code class="p">{</code> <code class="nx">source</code><code class="o">:</code> <code class="mi">7</code><code class="p">,</code> <code class="nx">target</code><code class="o">:</code> <code class="mi">8</code> <code class="p">},</code> <code class="p">{</code> <code class="nx">source</code><code class="o">:</code> <code class="mi">8</code><code class="p">,</code> <code class="nx">target</code><code class="o">:</code> <code class="mi">9</code> <code class="p">}</code> <code class="p">]</code> <code class="p">};</code></pre><p>As usual for D3, you can store whatever data you like within these objects. Our nodes are simple—just names of people. The edges contain two values each: a source ID and a target ID. These IDs correspond to the nodes above, so ID number 3 is Donovan, for example. If 3 is connected to 4, then Donovan is connected to Edward.</p><p>The data shown is a bare minimum for using the force layout. You can add more information, and, in fact, D3 will itself add a lot more data to what we’ve provided, as we’ll see in a moment.</p><p>Here’s how to initialize a force layout:</p><a id="I_programlisting11_id317018"/><pre class="programlisting"><code class="kd">var</code> <code class="nx">force</code> <code class="o">=</code> <code class="nx">d3</code><code class="p">.</code><code class="nx">layout</code><code class="p">.</code><code class="nx">force</code><code class="p">()</code> <code class="p">.</code><code class="nx">nodes</code><code class="p">(</code><code class="nx">dataset</code><code class="p">.</code><code class="nx">nodes</code><code class="p">)</code> <code class="p">.</code><code class="nx">links</code><code class="p">(</code><code class="nx">dataset</code><code class="p">.</code><code class="nx">edges</code><code class="p">)</code> <code class="p">.</code><code class="nx">size</code><code class="p">([</code><code class="nx">w</code><code class="p">,</code> <code class="nx">h</code><code class="p">])</code> <code class="p">.</code><code class="nx">start</code><code class="p">();</code></pre><p>Again, this is just the bare minimum. We specify the nodes and links to be used, the size of the available space, and then call <code class="literal">start()</code> when we’re ready to go.</p><p>This generates a default force layout, but the default won’t be ideal for every dataset. As you would expect, there are all kinds of options for customization. <a class="ulink" href="https://github.com/mbostock/d3/wiki/Force-Layout" target="_top">See the API wiki</a> for all the gory details. Here, I’ve increased the <code class="literal">linkDistance</code> (the length of the edges between connected nodes) as well as the negative <code class="literal">charge</code> between nodes, so they will repel each other more. (It’s not personal; I just want them to spread out a bit.)</p><a id="I_programlisting11_id317053"/><pre class="programlisting"><code class="kd">var</code> <code class="nx">force</code> <code class="o">=</code> <code class="nx">d3</code><code class="p">.</code><code class="nx">layout</code><code class="p">.</code><code class="nx">force</code><code class="p">()</code> <code class="p">.</code><code class="nx">nodes</code><code class="p">(</code><code class="nx">dataset</code><code class="p">.</code><code class="nx">nodes</code><code class="p">)</code> <code class="p">.</code><code class="nx">links</code><code class="p">(</code><code class="nx">dataset</code><code class="p">.</code><code class="nx">edges</code><code class="p">)</code> <code class="p">.</code><code class="nx">size</code><code class="p">([</code><code class="nx">w</code><code class="p">,</code> <code class="nx">h</code><code class="p">])</code> <code class="p">.</code><code class="nx">linkDistance</code><code class="p">([</code><code class="mi">50</code><code class="p">])</code> <code class="c1">// &lt;-- New!</code> <code class="p">.</code><code class="nx">charge</code><code class="p">([</code><code class="o">-</code><code class="mi">100</code><code class="p">])</code> <code class="c1">// &lt;-- New!</code> <code class="p">.</code><code class="nx">start</code><code class="p">();</code></pre><p>Next, we create an SVG line for each edge:</p><a id="I_programlisting11_id317066"/><pre class="programlisting"><code class="kd">var</code> <code class="nx">edges</code> <code class="o">=</code> <code class="nx">svg</code><code class="p">.</code><code class="nx">selectAll</code><code class="p">(</code><code class="s2">"line"</code><code class="p">)</code> <code class="p">.</code><code class="nx">data</code><code class="p">(</code><code class="nx">dataset</code><code class="p">.</code><code class="nx">edges</code><code class="p">)</code> <code class="p">.</code><code class="nx">enter</code><code class="p">()</code> <code class="p">.</code><code class="nx">append</code><code class="p">(</code><code class="s2">"line"</code><code class="p">)</code> <code class="p">.</code><code class="nx">style</code><code class="p">(</code><code class="s2">"stroke"</code><code class="p">,</code> <code class="s2">"#ccc"</code><code class="p">)</code> <code class="p">.</code><code class="nx">style</code><code class="p">(</code><code class="s2">"stroke-width"</code><code class="p">,</code> <code class="mi">1</code><code class="p">);</code></pre><p>Note that I set all the lines to have the same stroke color and weight, but of course you could set this dynamically based on data (say, thicker or darker lines for “stronger” connections, or some other value).</p><p>Then, we create an SVG circle for each node:</p><a id="I_programlisting11_id317082"/><pre class="programlisting"><code class="kd">var</code> <code class="nx">nodes</code> <code class="o">=</code> <code class="nx">svg</code><code class="p">.</code><code class="nx">selectAll</code><code class="p">(</code><code class="s2">"circle"</code><code class="p">)</code> <code class="p">.</code><code class="nx">data</code><code class="p">(</code><code class="nx">dataset</code><code class="p">.</code><code class="nx">nodes</code><code class="p">)</code> <code class="p">.</code><code class="nx">enter</code><code class="p">()</code> <code class="p">.</code><code class="nx">append</code><code class="p">(</code><code class="s2">"circle"</code><code class="p">)</code> <code class="p">.</code><code class="nx">attr</code><code class="p">(</code><code class="s2">"r"</code><code class="p">,</code> <code class="mi">10</code><code class="p">)</code> <code class="p">.</code><code class="nx">style</code><code class="p">(</code><code class="s2">"fill"</code><code class="p">,</code> <code class="kd">function</code><code class="p">(</code><code class="nx">d</code><code class="p">,</code> <code class="nx">i</code><code class="p">)</code> <code class="p">{</code> <code class="k">return</code> <code class="nx">colors</code><code class="p">(</code><code class="nx">i</code><code class="p">);</code> <code class="p">})</code> <code class="p">.</code><code class="nx">call</code><code class="p">(</code><code class="nx">force</code><code class="p">.</code><code class="nx">drag</code><code class="p">);</code></pre><p>I set all circles to have the same radius, but each gets a different color fill, just because it’s prettier that way. Of course, these values could be set dynamically, too, for a more useful visualization.</p><p>You’ll notice the last line of code, which enabled drag-and-drop interaction. (Comment that out, and the user won’t be able to move nodes around.)</p><p>Lastly, we have to specify what happens when the force layout “ticks.” Yes, these ticks are different from the axis ticks addressed earlier, and definitely different from those little blood-sucking insects. Physics simulations use the word “tick” to refer to the passage of some amount of time, like the ticking second hand of a clock. For example, if an animation were running at 30 frames per second, you could have one tick represent 1/30th of a second. Then each time the simulation ticked, you’d see the calculations of motion update in real time. In some applications, it’s useful to run ticks faster than actual time. For example, if you were trying to model the effects of climate change on the planet 50 years from now, you wouldn’t want to wait 50 years to see the results, so you’d program the system to tick on ahead, faster than real time.<a id="I_indexterm11_id317110" class="indexterm"/></p><p>For our purposes, what you need to know is that D3’s force layout “ticks” forward through time, just like every other physics simulation. With each tick, the force layout adjusts the position values for each node and edge according to the rules we specified when the layout was first intialized. To see this progress visually, we need to update the associated elements—the lines and circles:</p><a id="I_programlisting11_id317125"/><pre class="programlisting"><code class="nx">force</code><code class="p">.</code><code class="nx">on</code><code class="p">(</code><code class="s2">"tick"</code><code class="p">,</code> <code class="kd">function</code><code class="p">()</code> <code class="p">{</code> <code class="nx">edges</code><code class="p">.</code><code class="nx">attr</code><code class="p">(</code><code class="s2">"x1"</code><code class="p">,</code> <code class="kd">function</code><code class="p">(</code><code class="nx">d</code><code class="p">)</code> <code class="p">{</code> <code class="k">return</code> <code class="nx">d</code><code class="p">.</code><code class="nx">source</code><code class="p">.</code><code class="nx">x</code><code class="p">;</code> <code class="p">})</code> <code class="p">.</code><code class="nx">attr</code><code class="p">(</code><code class="s2">"y1"</code><code class="p">,</code> <code class="kd">function</code><code class="p">(</code><code class="nx">d</code><code class="p">)</code> <code class="p">{</code> <code class="k">return</code> <code class="nx">d</code><code class="p">.</code><code class="nx">source</code><code class="p">.</code><code class="nx">y</code><code class="p">;</code> <code class="p">})</code> <code class="p">.</code><code class="nx">attr</code><code class="p">(</code><code class="s2">"x2"</code><code class="p">,</code> <code class="kd">function</code><code class="p">(</code><code class="nx">d</code><code class="p">)</code> <code class="p">{</code> <code class="k">return</code> <code class="nx">d</code><code class="p">.</code><code class="nx">target</c