D3 layouts help you create more advanced visualisations such as treemaps:javascript
D3 layouts幫助您創造更加高級複雜的可視化圖表,好比treemaps,packed circles,network graphs:html
Layout is just a JavaScript function that takes your data as input and adds visual variables such as position and size to it.java
一句話: layout就是一個接收你的data做爲輸入,而通過變換增長相似位置,大小等可視化變量到這個data上去的函數node
好比tree layout就接收一個層次化的結構數據,而對每一個node增長x,y座標,這樣這些節點就造成一個類樹的圖形:ios
D3有不少中hierarchy layouts(處理層次化數據)和chord layout(處理網絡信息流向)和一個通用的force layout(物理現象的模擬)。json
注意:你也能夠建立你本身的layout.好比你能夠建立一個簡單的函數,該函數僅僅給源data數組添加位置信息,這樣的函數就能夠被認爲是一個layout數組
Hierarchical layouts
咱們來看下面的層次化數據:網絡
{"name":"A1","children":[{"name":"B1","children":[{"name":"C1","value":100},{"name":"C2","value":300},{"name":"C3","value":200}]},{"name":"B2","value":200}]}
在這節裏咱們未來看看tree, cluster, treemap, pack和partition layout.注意:treemap, pack和partition被用於layout(轉換)層次關係,這種層次關係圖表中節點nodes有一個關聯的數字值(好比:銷售額,人口數量等).數據結構
D3 V4要求層次化輸入數據規整後必須以d3.hierarchy對象的形式存在,這一點下面作詳細介紹。app
d3.hierarchy
一個d3.hierarchy
object 是一種能夠表達層次關係的數據結構。該object有一些實現獲取好比:ancestor, descendant, leaf nodes信息(用於計算nodes之間的鏈接path)的預約義方法。對象自己能夠經過d3.hierarchy(data)來生成。
var data = { "name": "A1", "children": [ { "name": "B1", "children": [ { "name": "C1", "value": 100 }, { "name": "C2", "value": 300 }, { "name": "C3", "value": 200 } ] }, { "name": "B2", "value": 200 } ] } var root = d3.hierarchy(data)
通常狀況下你沒必要直接對該hierarchy object操做,可是可使用其定義的一些方法,好比:
root.descendants();
root.links()
root.descendants()
返回一個扁平的數組來表達root的子孫後代,而root.links()則返回一個扁平的對象數組來表達全部的父子links
More examples of hierarchy functions
tree layout
tree
layout將層級關係中的節點安排成一個tree like arrangement.
咱們經過下面的代碼首先來建立一個tree
var treeLayout = d3.tree();
咱們使用.size()來配置tree的
treeLayout.size([400, 200]);
隨後咱們能夠調用treeLayout函數,傳入咱們的hierarchy object root:
treeLayout(root);
這個函數執行的結果是會將root的每個node都增長上x和y的value
接着,咱們能夠:
- 使用
root.descendants()
來獲得全部節點的一個數組 - 將這個數組data join到circles(或者任何其餘的svg element)
- 使用layout產生的x,y來給每一個節點定位其座標位置
而且。。。
- 使用
root.links()
來得到全部links數組 - 將links數組join到line (or path) elements
- 使用link的source和target的x,y座標值來畫出每一個line(也就是設置其d屬性)
(注意root.links()
每個數組元素都是一個包含了表明link的source和target的對象)
// Nodes d3.select('svg g.nodes') .selectAll('circle.node') .data(root.descendants()) .enter() .append('circle') .classed('node', true) .attr('cx', function(d) {return d.x;}) .attr('cy', function(d) {return d.y;}) .attr('r', 4); // Links d3.select('svg g.links') .selectAll('line.link') .data(root.links()) .enter() .append('line') .classed('link', true) .attr('x1', function(d) {return d.source.x;}) .attr('y1', function(d) {return d.source.y;}) .attr('x2', function(d) {return d.target.x;}) .attr('y2', function(d) {return d.target.y;});
cluster layout
cluster
layout 和 tree
layout 是很類似的,主要的區別是全部的葉子節點都將放置在相同的深度
<svg width="400" height="220"> <g transform="translate(5, 5)"> <g class="links"></g> <g class="nodes"></g> </g> </svg>
var data = { "name": "A1", "children": [ { "name": "B1", "children": [ { "name": "C1", "value": 100 }, { "name": "C2", "value": 300 }, { "name": "C3", "value": 200 } ] }, { "name": "B2", "value": 200 } ] } var clusterLayout = d3.cluster() .size([400, 200]) var root = d3.hierarchy(data) clusterLayout(root) // Nodes d3.select('svg g.nodes') .selectAll('circle.node') .data(root.descendants()) .enter() .append('circle') .classed('node', true) .attr('cx', function(d) {return d.x;}) .attr('cy', function(d) {return d.y;}) .attr('r', 4); // Links d3.select('svg g.links') .selectAll('line.link') .data(root.links()) .enter() .append('line') .classed('link', true) .attr('x1', function(d) {return d.source.x;}) .attr('y1', function(d) {return d.source.y;}) .attr('x2', function(d) {return d.target.x;}) .attr('y2', function(d) {return d.target.y;});
treemap layout
Treemaps用於可視化地表明層級關係,每一個item都有一個相關的value
好比,咱們能夠將世界人口數據視做層次化的:第一級表明region,第二級表明各個country.一個treemap經過一個矩形表明一個國家(矩形的大小則和其人口數量大小成比例),而最終將每一個region組合在一塊兒:
var treemapLayout = d3.treemap();
treemapLayout .size([400, 200]) .paddingOuter(10);
須要注意的是:在咱們應用layout到咱們的 hierarchy 以前,咱們必須先運行 .sum()
在hierarchy上. 這個方法將遍歷整顆樹,而且在每一個節點上設置.value以表明該節點下的全部子節點的數值之和
var root = d3.hierarchy(data)
root.sum(function(d) { return d.value; });
須要注意的是咱們給.sum()傳入了一個accessor function以便指定咱們要對哪一個屬性來作sum操做.
咱們如今能夠調用treemapLayout函數來對hierarchy object作轉換:
treemapLayout(root);
這時layout將添加4個屬性x0,x1,y0,y1到每一個節點上去,而這些值將指定treemap中每一個矩形的大小尺寸。
如今咱們就能夠將layout的輸出轉換數據用於可視化了,方法是:將rect和layout data join起來,隨後更新其x,y,width,height屬性:
d3.select('svg g') .selectAll('rect') .data(root.descendants()) .enter() .append('rect') .attr('x', function(d) { return d.x0; }) .attr('y', function(d) { return d.y0; }) .attr('width', function(d) { return d.x1 - d.x0; }) .attr('height', function(d) { return d.y1 - d.y0; })
若是咱們但願對每一個矩形增長label,咱們能夠join g 元素到這個layout data,而且增長rect和text元素到每一個g元素中。
var nodes = d3.select('svg g') .selectAll('g') .data(rootNode.descendants()) .enter() .append('g') .attr('transform', function(d) {return 'translate(' + [d.x0, d.y0] + ')'}) nodes .append('rect') .attr('width', function(d) { return d.x1 - d.x0; }) .attr('height', function(d) { return d.y1 - d.y0; }) nodes .append('text') .attr('dx', 4) .attr('dy', 14) .text(function(d) { return d.data.name; })
完整的代碼及效果以下:
<svg width="420" height="220"> <g></g> </svg>
var data = { "name": "A1", "children": [ { "name": "B1", "children": [ { "name": "C1", "value": 100 }, { "name": "C2", "value": 300 }, { "name": "C3", "value": 200 } ] }, { "name": "B2", "value": 200 } ] }; var treemapLayout = d3.treemap() .size([400, 200]) .paddingOuter(16); var rootNode = d3.hierarchy(data) rootNode.sum(function(d) { return d.value; }); treemapLayout(rootNode); var nodes = d3.select('svg g') .selectAll('g') .data(rootNode.descendants()) .enter() .append('g') .attr('transform', function(d) {return 'translate(' + [d.x0, d.y0] + ')'}) nodes .append('rect') .attr('width', function(d) { return d.x1 - d.x0; }) .attr('height', function(d) { return d.y1 - d.y0; }) nodes .append('text') .attr('dx', 4) .attr('dy', 14) .text(function(d) { return d.data.name; })
treemap
layouts 還能夠有如下配置方法:
- the padding around a node’s children can be set using
.paddingOuter
- the padding between sibling nodes can be set using
.paddingInner
- outer and inner padding can be set at the same time using
.padding
- the outer padding can also be fine tuned using
.paddingTop
,.paddingBottom
,.paddingLeft
and.paddingRight
.
var treemapLayout = d3.treemap() .size([400, 200]) .paddingTop(20) .paddingInner(2);
在上面的代碼中, paddingTop
is 20 and paddingInner
is 2.
Treemaps也可使用不一樣的平鋪策略,d3js自己提供如下幾種內置的策略可供選用 (treemapBinary
, treemapDice
, treemapSlice
, treemapSliceDice
, treemapSquarify
) 這些策略經過 .tile()來選擇
:
treemapLayout.tile(d3.treemapDice)
treemapBinary
strives for a balance between horizontal and vertical partitions, treemapDice
partitions horizontally, treemapSlice
partitions vertically, treemapSliceDice
alternates between horizontal and vertical partioning and treemapSquarify
allows the aspect ratio of the rectangles to be influenced.
The effect of different squarify ratios can be seen here.
pack layout
pack layout和tree layout是相似的,只是咱們使用circles而不是rects來表明一個節點而已. 在下面的例子中,每一個country都由一個圓來代替(其半徑的大小對應着相應的population)而全部國家以region來作分組.
var packLayout = d3.pack();
packLayout.size([300, 300]);
和treemap同樣,咱們必須在hierarchy object root被pack layout調用以前,在該對象上調用
.sum()以便獲取彙總數據
:
rootNode.sum(function(d) { return d.value; }); packLayout(rootNode);
pack
layout 爲每一個node增長了x,y和r屬性。
如今咱們就能夠將layout轉換後的結果數據和circle元素join起來從而實現可視化。爲root的每個descendant增長一個circle元素。
d3.select('svg g') .selectAll('circle') .data(rootNode.descendants()) .enter() .append('circle') .attr('cx', function(d) { return d.x; }) .attr('cy', function(d) { return d.y; }) .attr('r', function(d) { return d.r; })
相似地,也能夠經過g元素來組合circle以及對應的labels:
var nodes = d3.select('svg g') .selectAll('g') .data(rootNode.descendants()) .enter() .append('g') .attr('transform', function(d) {return 'translate(' + [d.x, d.y] + ')'}) nodes .append('circle') .attr('r', function(d) { return d.r; }) nodes .append('text') .attr('dy', 4) .text(function(d) { return d.children === undefined ? d.data.name : ''; })
咱們可使用 .padding()來配置每一個圓之間的padding
packLayout.padding(10)完整的代碼: