D3 layouts help you create more advanced visualisations such as treemaps:

D3 layouts帮助您创造更加高级复杂的可视化图表,比如treemaps,packed circles,network graphs:

Layout is just a JavaScript function that takes your data as input and adds visual variables such as position and size to it.

一句话: layout就是一个接收你的data作为输入,而经过变换增加类似位置,大小等可视化变量到这个data上去的函数

比如tree layout就接收一个层次化的结构数据,而对每个node增加x,y坐标,这样这些节点就形成一个类树的图形:

D3有很多中hierarchy layouts(处理层次化数据)和chord layout(处理网络信息流向)和一个通用的force layout(物理现象的模拟)。

注意:你也可以创建你自己的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对象的形式存在,这一点下面做详细介绍。

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="" height="">
<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)

完整的代码:

  <svg width="320" height="320">
<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 packLayout = d3.pack()
.size([300, 300])
.padding(10) var rootNode = d3.hierarchy(data) rootNode.sum(function(d) {
return d.value;
}); packLayout(rootNode); 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 : '';
})
 

partition layout

partition layout 将一个矩形区域针对每一个层级都细分为一层。每一层针对本层里面每一个节点再做细分。D3’s partition layout is created using:

var partitionLayout = d3.partition();
partitionLayout.size([400, 200]);
rootNode.sum(function(d) {
return d.value;
});
partitionLayout(rootNode);

partition layout 将对每个node增加 x0, x1, y0 and y1 属性.

现在我们就可以给root的每个后代添加对应的rect元素并且修改其属性。

d3.select('svg g')
.selectAll('rect')
.data(rootNode.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.
  partitionLayout.padding(2) 如果我们希望修改分区的排列方向,我们可以在定义rect元素的属性时,swap x0,y0,x1,y1:
  .attr('x', function(d) { return d.y0; })
.attr('y', function(d) { return d.x0; })
.attr('width', function(d) { return d.y1 - d.y0; })
.attr('height', function(d) { return d.x1 - d.x0; });
 

我们也可以将x映射成一个旋转的角度,而y映射成半径长度,这样创建一个旭日分区:

  <svg width="320" height="320">
<g transform="translate(160, 160)"></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 radius = 150; var partitionLayout = d3.partition()
.size([2 * Math.PI, radius]); var arcGenerator = d3.arc()
.startAngle(function(d) { return d.x0; })
.endAngle(function(d) { return d.x1; })
.innerRadius(function(d) { return d.y0; })
.outerRadius(function(d) { return d.y1; }); var rootNode = d3.hierarchy(data) rootNode.sum(function(d) {
return d.value;
}); partitionLayout(rootNode); d3.select('svg g')
.selectAll('path')
.data(rootNode.descendants())
.enter()
.append('path')
.attr('d', arcGenerator);

chord layout

Chord图将一组节点之间的关系链接进行可视化。每个link都有关联的value. 比如我们可以列出不同国家之间的人口迁徙关系图

原始数据是一个矩阵 n x n  (n是元素的个数):

var data = [
[10, 20, 30],
[40, 60, 80],
[100, 200, 300]
];

第一行代表着从第一个item flows到第1,第2,第3个item。

第二行代表着从第二个item flows到第1,第2,第3个item。

我们通过chord()调用来创建layout

var chordGenerator = d3.chord();

使用.padAngle() (邻近的组之间的padding angle in radians), .sortGroups() (指定组出现的顺序), .sortSubgroups() (在每个group内部的分类) 而 .sortChords() 定义了z order of the chords.

var chords = chordGenerator(data);

返回chords数组. 数组的每一个元素都是一个包含了source 和 target属性的对象. 每一个source和target都有着startAngle 和endAngle 属性,这将用于定义每个chord

随后我们使用ribbon shape generator(路径生成器,和line, arc generator相对应) 将chord属性转换为path d属性 (see the Shapes chapter for more information on shape generators).

var ribbonGenerator = d3.ribbon().radius(200);

d3.select('g')
.selectAll('path')
.data(chords)
.enter()
.append('path')
.attr('d', ribbonGenerator)
  <svg width="500" height="500">
<g transform="translate(250, 250)"></g>
</svg>
 
var chordGenerator = d3.chord()
.sortSubgroups(d3.ascending)
.padAngle(0.04); var ribbonGenerator = d3.ribbon().radius(200); var data = [
[10, 20, 30],
[40, 60, 80],
[100, 200, 300]
]; var chords = chordGenerator(data); d3.select('g')
.selectAll('path')
.data(chords)
.enter()
.append('path')
.attr('d', ribbonGenerator)

 

d3js layout 深入理解的更多相关文章

  1. d3js shape深入理解

    本文将视图了解d3js提供的帮助我们创建矢量图形的helper函数,比如下面的: http://d3indepth.com/shapes/ lines curves pie chart segment ...

  2. Auto Layout深入理解,及masonry简单介绍

    本篇博客是本人在学习自己主动布局过程中对自己主动布局的理解和整理,分三部分介绍,内容可能会有所反复.见谅. 一.autosizing与Auto Layout对照,及Auto Layout简单介绍 1. ...

  3. d3js scales深入理解

    转自:https://www.cnblogs.com/kidsitcn/p/7182274.html 比例尺函数是这样的javascript函数: 接收通常是数字,日期,类别等data输入并且: 返回 ...

  4. d3js selections深入理解

    D3 selections选择DOM元素以便可以对这些dom元素做相应的操作,比如:更改其style,修改其属性,执行data-join操作,或者插入.删除相应elements 比如,如果给定5个ci ...

  5. Android线性布局(Linear Layout)

    Android线性布局(Linear Layout) LinearLayout是一个view组(view group),其包含的所有子view都以一个方向排列,垂直或是水平方向.我们能够用androi ...

  6. Log in Spring

    记录日志向来是企业级应用程序必须考虑的事情.早些年,一个项目一个日志功能或模块,然后有了log4j这样的产品.不知是log4j将记录日志做到了极致,或是技术含量不高,又或是经济利益不明显,它已成为了这 ...

  7. C++中的指针和数组

    最近看C++编程思想,看到第十三章动态内存管理的时候把自己给绕进去了,主要是在数据和指针这块弄混了.现在把找到的一些资料总结如下: 1. 数组是数组,指针是指针,两者并不等价: 2.数组在作为左值的时 ...

  8. 逐步探究ObjC的Weak技术底层

    前言 之前的文章有说过 Atomic 原子操作的原理,其作为一个特殊的修饰前缀,影响了存取操作. 在属性修饰定义中,还有另一类修饰前缀,他们分别是 strong weak assign copy,这些 ...

  9. 使用 jsDelivr 免费加速 GitHub Pages 博客的静态资源(二)

    之前写过一篇 使用 jsDelivr 免费加速 GitHub Pages 博客的静态资源,在那之后,又陆续想到并实施了几点利用 jsDelivr 进一步加速静态资源加载的措施,新起一篇作为记录和分享. ...

随机推荐

  1. IDEA里运行程序时出现Error:scalac:error while loading JUnit4 , Scala signature JUnit4 has wrong version错误的解决办法(图文详解)

    不多说,直接上干货! 问题详情 当我们在运行程序时,出现Error:scalac:error while loading JUnit4 , Scala signature JUnit4 has wro ...

  2. js时间戳差值转日期格式

    <script> function getRemainderTime (startTime){     var s1 = new Date(startTime.replace(/-/g,  ...

  3. 11 - JavaSE之GUI

    GUI(念法 gu yi) AWT AWT(Abstract Window Toolkit 抽象窗口开发包,在C# 或者 linux窗口开发类之上又封装一层,达到跨平台的目的)包括了很多类和接口,用于 ...

  4. Linux内存信息查看——free命令

    free 命令可以显示系统已用和空闲的内存情况.包括物理内存.交互区内存(swap)和内核缓冲区内存(buffer).共享内存将被忽略.在Linux系统监控的工具中,free命令是最经常使用的命令之一 ...

  5. Oracle数据库之PL/SQL程序基础设计

    一.PL/SQL块结构 前边我们已经介绍了PL/SQL块的结构,再来回顾一下: DECLARE /* * 声明部分——定义常量.变量.复杂数据类型.游标.用户自定义异常 */ BEGIN /* * 执 ...

  6. 不停机不停服务,MYSQL可以这样修改亿级数据表结构

    摘  要:本文阐述了MySQL DDL 的问题现状.pt-online-schema-change的工作原理,并实际利用pt-online-schema-change工具在线修改生产环境下1.6亿级数 ...

  7. 机器学习classification_report方法及precision精确率和recall召回率 说明

    classification_report简介 sklearn中的classification_report函数用于显示主要分类指标的文本报告.在报告中显示每个类的精确度,召回率,F1值等信息. 主要 ...

  8. 【转】jquery checkbox勾选/取消勾选的诡异问题

    转:http://www.cnblogs.com/KeenLeung/p/3799895.html 第一次执行,没问题,但第二次执行就有问题了,选择不了 prop()和attr()区别: (来源:ht ...

  9. 关于jquery的入门,简单的封装。

    看过不同的博客,觉得以下的博客写的比较简洁明了,通俗易懂. 关于jquery博客:http://www.cnblogs.com/moqiutao/p/6523924.html 关于js:http:// ...

  10. 撩课-Java每天5道面试题第24天

    151.springMVC和struts2的区别有哪些? .springmvc的入口是一个servlet即前端控制器(DispatchServlet), 而struts2入口是一个filter过虑器( ...