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"는 배치되는 위치에 따라 읽기 쉬운 각도로 변경한다.

요약

노드 수가 많아지면 여기 예제과 같은 임팩트가 있는 그림을 쓸 수 있다. 가로 길이의 토너먼트 표보다 컴팩트하게 표시할 수 있어 디자인성도 높다.




최종 수정 : 2024-01-18