D3.js cluster 원주에 노드를 배치하는 방법
계층 구조(hierarchy)의 데이터 구조와 데이터 준비에 대해 hierarchy 데이터 구조 및 사용법름 참조하라.
예제 프로그램
샘플 코드
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>D3 v5 hierarchy cluster radial v4/v5</title>
</head>
<body>
<svg width="800" height="600"></svg>
<script src="https://d3js.org/d3.v5.min.js"></script>
<script>
// 1. 그리려는 데이터 준비
var width = document.querySelector("svg").clientWidth;
var height = document.querySelector("svg").clientHeight;
var data = {
"name": "A",
"children": [
{ "name": "B" },
{
"name": "C",
"children": [{ "name": "D" }, { "name": "E" }, { "name": "F" }]
},
{ "name": "G" },
{
"name": "H",
"children": [{ "name": "I" }, { "name": "J" }]
},
{ "name": "K" },
{
"name": "L",
"children": [{ "name": "M" }, { "name": "N" }]
},
{ "name": "O" },
{ "name": "P" }
]
};
var rx = width / 2;
var ry = height / 2
// 2. 그리려는 데이터 변환
root = d3.hierarchy(data);
var cluster = d3.cluster().size([360, ry - 80])
cluster(root);
// 3. SVG 요소 설정
g = d3.select("svg").append("g").attr("transform", "translate(" + rx + "," + ry + ")");
var link = g.selectAll(".link")
.data(root.links())
.enter()
.append("path")
.attr("class", "link")
.attr("fill", "none")
.attr("stroke", "#555")
.attr("stroke-width", "1.5px")
.attr("opacity", "0.6")
.attr("d", d3.linkRadial()
.angle(function (d) { return (d.x + 90) * Math.PI / 180; }) //
.radius(function (d) { return d.y; }));
var node = g.selectAll(".node")
.data(root.descendants())
.enter()
.append("g")
.attr("transform", function (d) { return "rotate(" + (d.x) + ")translate(" + d.y + ")"; })
node.append("circle")
.attr("r", 8)
.attr("stroke", "steelblue")
.attr("stroke-width", "1.5px")
.attr("fill", "white");
node.append("text")
.attr("dy", 3)
.attr("dx", function (d) { return d.x < 90 || d.x > 270 ? 8 : -8; })
.style("text-anchor", function (d) { return d.x < 90 || d.x > 270 ? "start" : "end"; })
.attr("font-size", "200%")
.attr("transform", function (d) { return d.x < 90 || d.x > 270 ? null : "rotate(180)"; })
.text(function (d) { return d.data.name; });
</script>
</body>
</html>
예제 코드 설명
1. 그리려는 데이터 준비
그리려는 데이터를 준비한다. cluster의 기본적인 사용법은 cluster 사용법, 데이터 구조의 자세한 것은 hierarchy 데이터 구조 및 사용법을 참조하라.
2. 그리려는 데이터 변환
root = d3.hierarchy(data);
var cluster = d3.cluster().size([360, ry - 80])
cluster(root);
준비된 데이터를 그리려는 데이터 구조로 변경한다. “준비한 데이터 → hierarchy용 데이터 → 그리기 종류별(이번은 cluster)의 데이터"으로 2단계 변환을 한다. 여기에서는 x
좌표에 회전 각도, y
좌표에 반경을 설정하여 극좌표 시스템의 위치를 계산한다.
3. svg 요소 배치
g = d3.select("svg").append("g").attr("transform", "translate(" + rx + "," + ry + ")");
시작하기 svg 영역에 그룹을 나타내는 "g"
요소를 설정하여 전체 중심 좌표를 설정한다. 이 그룹 요소 안에 노드와 링크를 설정한다.
var link = g.selectAll(".link")
.data(root.links())
.enter()
.append("path")
.attr("class", "link")
.attr("fill", "none")
.attr("stroke", "#555")
.attr("stroke-width", "1.5px")
.attr("opacity", "0.6")
.attr("d", d3.linkRadial()
.angle(function(d) { return (d.x + 90) / 180 * Math.PI; })
.radius(function(d) { return d.y; }));
링크를 설정한다.
root.links()
이 코드는 계층 구조의 루트에서 링크를 배열로 추출하는 함수이다. [{"source": nodedata ,"tareget": nodedata },...]
와 같은 형태의 배열이 정의된다.
d3.linkRadial()
.angle(function(d) { return (d.x + 90) / 180 * Math.PI; })
.radius(function(d) { return d.y; })
이 코드는 극좌표계로 설정된 노드간을 연결하는 svg
요소의 path
의 "d"
속성의 설정치를 돌려주는 함수이다. 각도는 각도를 정의하지만 라디안이라는 점에 유의해야 한다.
var node = g.selectAll(".node")
.data(root.descendants())
.enter()
.append("g")
.attr("transform", function(d) { return "rotate(" + (d.x) + ")translate(" + d.y + ")"; })
node.append("circle")
.attr("r", 8)
.attr("stroke", "steelblue")
.attr("stroke-width", "1.5px")
.attr("fill", "white");
node.append("text")
.attr("dy", 3)
.attr("dx", function(d) { return d.x < 90 || d.x > 270 ? 8 : -8; })
.style("text-anchor", function(d) { return d.x < 90 || d.x > 270 ? "start" : "end"; })
.attr("font-size", "200%")
.attr("transform", function(d) { return d.x < 90 || d.x > 270 ? null : "rotate(180)"; })
.text(function(d) { return d.data.name; });
노드를 설정한다. 시작하기 전에 "g"
요소를 설정하고 그 안에 "circle"
과 "text"
를 설정한다.
root.descendants()
이 코드는 계층 구조의 노드를 배열로 검색하는 함수이다. 또, transform
속성으로 회전각을 설정하고 있지만, 링크의 설정시와 90도 어긋나므로 주의가 필요하다. "text"
는 배치되는 위치에 따라 읽기 쉬운 각도로 변경한다.
요약
노드 수가 많아지면 여기 예제과 같은 임팩트가 있는 그림을 쓸 수 있다. 가로 길이의 토너먼트 표보다 컴팩트하게 표시할 수 있어 디자인성도 높다.