D3
OG
Old school D3 from simpler times
All examples
By author
By category
About
cailongl
Full window
Github gist
World Countries Hierarchy
介绍:集群图,展现父子关系,血缘关系,比如此图就是地域的区分,对于集群图,处理数据是比较麻烦的。
d3.stratify
将平级的数据变成一个有层级结构的数据,
d3.hierarchy
作用于上边生成的数据,定义数据的x,y坐标。
d3.cluster
生成集群布局方法,作用于上边的数据,那么所有节点的位置就有了
最好的学习方法就是动手试试。
<!DOCTYPE html> <meta charset='utf-8'> <head> <title>Simple Radial Tree (d3 v4; CSV; 63 Lines)</title> <script src="https://d3js.org/d3.v4.js"></script> <link href="//fonts.googleapis.com/css?family=Open+Sans" rel="stylesheet" type="text/css"> </head> <style> html { background-color: black; } .glow { /* This trick adds a heavy white shadow around the text. */ text-shadow: -1px -1px 3px black, -1px 1px 3px black, 1px -1px 3px black, 1px 1px 3px black; } text { font-family: "Open Sans", sans-serif; pointer-events: none; alignment-baseline: central; fill: white; } path { fill: none; stroke: white; /* #4fb9b5; */ } </style> <svg> <g></g> </svg> <script> var vWidth = 900; var vHeight = 800; // 定义 字体 var vFontSize = [6,10,18,22]; // d3.scaleOrdinal https://github.com/d3/d3-scale/blob/master/README.md#scaleOrdinal // 生成一个比例尺 地域和颜色的映射关系 返回的是一个方法,参数就是domain中的值,eg:vColor('Oceania') => '#ff6698' // .range 如果范围内的元素少于域中的元素,则该比例将重复使用范围的 相当于domain的 DomainLength % rangeLength - 1。 var vColor = d3.scaleOrdinal().domain(["Oceania", "Africa", "Europe", "Latin America", "Asia"]) .range(["#ff6698", "#ffb366", "#ffff66", "#98ff66", "#6698ff"]); // .range(["#fb5454", "#f1fb54", "#54befb"]); // 选中svg 设置高度 宽度 // 选中svg下的g 居中 // https://github.com/d3/d3-selection/blob/master/README.md#selecting-elements // https://github.com/d3/d3-selection/blob/master/README.md#modifying-elements var g = d3.select('svg').attr('width', vWidth).attr('height', vHeight) .select('g').attr('transform', 'translate(' + vWidth/2 + ',' + vHeight/2 + ')'); // 加载csv文件数据 d3.csv https://github.com/d3/d3-request/blob/master/README.md#csv d3.csv('country-hierarchy.csv', function(error, vCsvData) { // vCsvData 返回的数据是一个json array 不过还有一个 columns 属性返回的是 表头的数组 if (error) throw error; // d3.stratify https://github.com/d3/d3-hierarchy/blob/master/README.md#stratify // stratify 把数据生成一个有新层次结构的数据 有当前数据,层级数——最顶级为0,有children // d3.stratify 需指定id,parentId 然后分等级,此处是数据本身就有id,parentId vData = d3.stratify()(vCsvData); drawViz(vData); }); function drawViz(vData) { // node.each https://github.com/d3/d3-hierarchy/blob/master/README.md#node_each // 让层级数据中的每个节点掉用这个方法 vData.each( function(d){ // d 为当前的节点 这个数据是四层,depth 为0,1,2,3 height 为3,2,1,0 // 这个方法就是,除了最顶级的world ,为取颜色而给每个node一个属性 属于这个["Oceania", "Africa", "Europe", "Latin America", "Asia"]中的哪个 switch (d.depth) { case 1: d.data.leg = d.id; break; case 2: d.data.leg = d.parent.id; break; case 3: d.data.leg = d.parent.parent.id; break; }}); // d3.cluster https://github.com/d3/d3-hierarchy/blob/master/README.md#cluster // 生成一个集群布局 为根元素和后代设置x,y属性 // 如果指定了大小,则将此集群布局的大小设置为指定的数字[width,height]的双元素数组,并返回此集群布局。如果未指定大小,则返回当前布局大小,默认为[1,1]。 null的布局大小表示将使用节点大小。例如,为了产生径向布局,[360,半径]的大小对应于360°的宽度和半径的深度 var vLayout = d3.cluster().size([2 * Math.PI, Math.min(vWidth, vHeight)/2 - 130]); // 径向布局 360度 然后取宽高中最小的为半径,再有离边界130的距离 // d3.hierarchy https://github.com/d3/d3-hierarchy/blob/master/README.md#hierarchy // 计算分层布局,需要一个有根节点的分层级的数据 然后计算root根的x,y var vRoot = d3.hierarchy(vData); // node.descendants https://github.com/d3/d3-hierarchy/blob/master/README.md#node_descendants // 有了根节点的数据,计算后代 , 返回后代节点的数组,从此节点开始,然后以拓扑顺序跟随每个子节点 var vNodes = vRoot.descendants(); // node.links https://github.com/d3/d3-hierarchy/blob/master/README.md#node_links // 将数据给到vLayout定义的集群布局 生成links 返回的是链接数组,每个链接是定义源和目标属性的对象。每个链接的来源是父节点 source,目标是一个子节点target。 var vLinks = vLayout(vRoot).links(); // 该有数据有了开始画图了 画links g.selectAll('path').data(vLinks).enter().append('path') // d3.linkRadial https://github.com/d3/d3-shape/blob/master/README.md#linkRadial 用径向切线返回一个新的链接生成器。例如,以下就是为了,以中心为根的树状图链接 .attr('d', d3.linkRadial() .angle(function (d) { return d.x; }) .radius(function (d) { return d.y; })); // 画node var node = g.selectAll(".node").data(vNodes).enter().append('g') // d3.pointRadial https://github.com/d3/d3-shape/blob/master/README.md#pointRadial // 以弧度返回给定角度的点[x,y],其中(12点钟)处的值为0,顺时针方向为正角度,半径为给定半径 .attr('transform', function(d) { return "translate(" + d3.pointRadial(d.x, d.y) + ")"; }); // 为每个node 给个lable node.append("text") // id 就是名字 .text(function (d){ return d.data.data.id; }) // 定义字体 height 为3的最大号 总共四层结构 3,2,1,0 .attr("font-size", function (d){ return vFontSize[d.height] + "pt"; }) // 文字要以中心自己旋转 .attr("transform", function(d) { return "rotate(" + textRotation(d) + ")" }) .attr("text-anchor", function (d){ // 如果是最低等级的元素 中心右边的元素,文字start与node对齐 中心左边的元素文字的end与node对齐 if(d.height === 0){ return (d.x > Math.PI) ? "end" : "start"; } // 如果是高级元素 就和中间对齐 else { return "middle"; } }) .attr("dx", function (d){ // 最低级的元素 中心右边的元素,右偏移2px 中心左边的左偏移2px if(d.depth === 3){ return (d.x > Math.PI) ? "-2px" : "2px"; } else { return "0px"; } }) .style("fill", function(d){ //if(d.data.data.least_devd_country === "Yes") { return "blue";} //else if (d.data.data.devd_region === "Yes") { return "green";} // 上色 return vColor(d.data.data.leg); }) // 除最低的元素 其他都给class glow .classed("glow", function (d){ return d.height !== 0; }); function textRotation(d) { var angle = d.x / Math.PI * 180 + 90; // 一级和二级的不旋转 其他的旋转 if (d.depth < 2) { return 0;} // 在中心右侧的,旋转 d.x / Math.PI * 180 - 90 else if (angle <= 270) { return angle - 180;} // 中心左侧的 d.x / Math.PI * 180 + 90 else { return angle;} } } </script>
https://d3js.org/d3.v4.js