d3 기본도형 path

path는 경로라는 의미로 종이 위에 펜을 두고, 펜을 움직여 그 경로를 모양으로 한다고 생각하면 된다.

핵심적인 속성은 d가 있다.

이 d속성을 이용하여 경로를 설정하게 된다.

d

d의 속성은 다음과 같다.

  1. Move To: M, m
  2. Line To: L, l, H, h, V, v
  3. Cubic Bézier Curve: C, c, S, s
  4. Quadratic Bézier Curve: Q, q, T, t
  5. Elliptical Arc Curve: A, a
  6. ClosePath: Z, z

사실 d3를 다루며 다른 도형에 대해선 이해할 수 있어도, 이 path 도형을 이해하는데에는 좀 오랜 시간이 걸렸다. 그래서 다시 하나씩 정리해보았다.

1. Move To

펜을 들어 지정된 곳에 놓는 것으로 생각할 수 있다. 즉 현재 지점을 이동하는 거을 의미한다.

CommandParametersNotes
M(x,y)+현재 지점을 x,y로 옮긴다.
m(dx,dy)+현재 경로를 (curX,curY)라고 할 때 현재 지점을 (curX+dx,curY+dy)로 옮긴다.

2. Line To

선을 그리는 정의이다.

CommandParametersNotes
L(x,y)+현재 지점에서 x,y까지 선을 그린다.
l(dx,dy)+현재 경로를 (curX,curY)라고 할 때 현재 지점에서 (curX+dx,curY+dy)까지 선을 그린다.
Hx+현재 지점에서 x,y까지 수평선을 그린다.
h(dx,dy)+현재 경로를 (curX,curY)라고 할 때 현재 지점에서 (curX+dx,curY+dy)까지 수평선을 그린다.
V(x,y)+현재 지점에서 x,y까지 수직선을 그린다.
v(dx,dy)+현재 경로를 (curX,curY)라고 할 때 현재 지점에서 (curX+dx,curY+dy)까지 수직선을 그린다.

3. Cubic Bézier Curve

4개의 점을 사용하는 부드러운 곡선 정의이다.

시작점 (current point)
(Po = {xo, yo})

끝점
(Pn = {xn, yn})

시작 쪽 제어 점
(Pcs = {xcs, ycs}) (controls curvature near the start of the curve)

끝 쪽 제어 점
(Pce = {xce, yce}) (controls curvature near the end of the curve)

같은 시간동안 시작 점에서 시작 쪽 제어 점으로 움직이는 점, 시작 쪽 제어 점에서 끝 쪽 제어 점으로 움직이는 점, 끝 쪽 제어 점에서 끝 점으로 움직이는 점을 각각 Pstarttocontrol, Pcontroltocontrol, Pcontroltoend으로 정의할 수 있다.

또한 PstarttoControl과 Pcontroltocontrol, Pcontrol과 Pcontroltoend을 연결하고 같은 시간동안 각각의 선의 시작점에서부터 끝 점까지 움직이는 점을 정의할 수 있다.

이러한 점을 각각 Pbezierstart, Pbezierend라고 하면 같은 시간동안 이 점의 움직임을 연결한 곡선이 Bezier 곡선이다.

말로 표현하면 좀 어려운데, Ref를 참조하면 이해가 더 쉽다.

CommandParametersNotes
C(x1,y1,x2,y2,x,y)+현재 지점에서 x,y까지 3차 베지어 곡선을 그린다. 끝 점은 x,y 시작 제어 점은 x1,y1, 끝 제어 점은 x2,y2로 정의된다.
c(dx1,dy1,dx2,dy2,dx,dy)+현재 지점에서 끝점까지 3차 베지어 곡선을 그린다. 현재 지점을 (curX,curY) 라고 할 때 끝 점은 (curX+dx,curY+dy), 시작 제어 점은 (curX+dx1,curY+y1), 끝 제어 점은 (curX+dx2,curY+dy2)로 정의된다.
S( x2, y2, x, y)+C와 똑같이 동작한다. 다만 시작점을 이전 베지어곡선의 끝점으로 한다.
s( dx2, dy2, dx, dy)+c와 똑같이 동작한다. 다만 시작점을 이전 베지어곡선의 끝점으로 한다.

4. Quadratic Bézier Curve

세 점을 사용하는 2차 베지어 곡선이다.

시작 점
Po = {xo, yo}

끝 점
Pn = {xn, yn}

제어 점
Pc = {xc, yc} (controls curvature)

CommandParametersNotes
Q(x1,y1,x,y)+현재 지점에서 x,y까지 2차 베지어 곡선을 그린다.
q(dx1,dy1,dx,dy)+현재 지점에서 끝점까지 3차 베지어 곡선을 그린다. 현재 지점을 (curX,curY) 라고 할 때 끝 점은 (curX+dx,curY+dy)으로 정의된다.
T(x, y)+Q와 똑같이 동작한다. 다만 시작점을 이전 베지어곡선의 끝점으로 한다.
t(dx, dy)+q와 똑같이 동작한다. 다만 시작점을 이전 베지어곡선의 끝점으로 한다.

5. Elliptical Arc Curve

타원의 일부로 정의된 곡선이다. 즉 타원의 호이다.

CommandParametersNotes
A(rx, ry, angle, large-arc-flag, sweep-flag, x, y)+현재 지점에서 x,y까지 호를 그린다.
rx,ry는 타원의 두 반지름이다.
angle은 x축에 대한 타원의 회전 각도이다.
large-arc-flag, sweep-flag는 4개의 호가 가능하므로 어떤 호를 그릴지 선택한다.
large-arc-flog는 큰 호(1) 작은호(0)을 선택할 수 있다.
sweep-flag는 시계 방향 호(1) 반시계 방향 호(0)을 선택할 수 있다.
좌표 x,y는 다음 명령의 새 현재 점이 된다.
a(rx, ry, angle, large-arc-flag, sweep-flag, dx, dy)+현재 지점에서 끝점까지 호를 그린다. 현재 지점을 (curX,curY) 라고 할 때 끝 점은 (curX+dx,curY+dy)으로 정의된다.

6. Close Path

현재 위치에서 경로의 첫 번째 점까지 직선을 그린다.

CommandParametersNotes
Z,z경로의 마지막 지점을 시작 지점과 연결하여 현재 경로를 닫는다. 두 점이 다른 좌표에 있으면 두 점 사이에 직선이 그려진다.

interpolate

d3.interpolate를 사용하여 선을 보간할 수 있다.

아래의 링크를 참조하면 된다.(혹은 공식문서)

https://web.archive.org/web/20201029202235/https://www.dashingd3js.com/svg-paths-and-d3js

Ref

https://ko.javascript.info/bezier-curve
https://blog.coderifleman.com/2016/12/30/bezier-curves/

d3 데이터를 dom에 바인딩하기

d3의 가장 기초가 되는 메소드에 대해 알아보자

select

조작하고자 하는 DOM 요소를 선택할 수 있다.

반환되는 값은 selection 객체이다.

해당하는 결과가 없을 경우 빈 객체를 반환한다.

부모의 데이터와 인덱스등의 상태를 보존한 상태로 선택한다.

selectAll

선택자와 함께 DOM 요소를 여러 개 선택한다.

반환되는 값은 selection 객체 배열이다.

해당하는 결과가 없을경우 빈 객체 배열을 반환한다.

부모의 데이터와 인덱스등의 상태를 보존하지 않은 상태로 새 그룹을 만든다.

즉 새로운 데이터를 바인딩하기 위해선 select가 아닌 selectAll을 사용해야한다.

datum

요소에 단일 데이터를 바인딩 할 때 사용한다.

실제 DOM요소와의 연결을 계산하지 않는다.(enter,update,exit)

단순히 모든 요소에 할당한다.

1
2
3
4
5
6
const data = 1;
d3.select("body").datam(data).enter().append("div");

const datas = [1, 2, 3];
d3.select("body").datam(datas).enter().append("div");
// 모든 요소에 1,2,3을 할당

parameter를 제공하지 않을경우, 선택된 첫번째 요소의 데이터만을 반환한다.

위의 예시에서는 1을 반환한다.

data

데이터 배열을 selection 객체와 연결한다.

실제 DOM 요소와 연결하기 위해 이를 계산한다.

때문에 data를 수행할 경우 selection 객체는 enter,exit,update의 메소드를 갖게된다.

즉 데이터 각각을 실제 DOM 요소와 연결할 수 있다.

data를 수행할 경우 index를 key로 갖게 되는데, data함수 내부의 두 번째 parameter에 key function을 제공하여 고유한 키를 갖도록 할 수 있다.

1
2
3
4
5
6
7
const data = 1;
d3.select("body").data([data]).enter().append("div");
// 배열로 제공하였다.

const datas = [1, 2, 3];
d3.select("body").data(datas).enter().append("div");
// 실제 DOM 요소와의 연결이 존재하지 않는 2,3번째 요소에 2,3을 할당하여 enter

이중 enter()는 selection 객체에 바인드된 데이터 중에 실제 DOM을 가지지 못하는 요소를 찾아내 가상의 객체로 만들어 반환한다.

즉 데이터의 수가 DOM요소보다 많을 경우 하는 작업이다.

1
2
const data = [1, 2, 3];
d3.select("body").data(data).enter().append("div");

update는 업데이트된 데이터에 따라 어떻게 작업할지를 명시한다.

data 자체가 update이다.

1
2
const data = [1, 2, 3];
d3.select("body").data(data).attr("update");

exit은 업데이트된 데이터에 따라 요소를 제거할 때에 사용된다.

즉 데이터의 수가 DOM요소보다 적을 경우 하는 작업이다.

1
2
const data = [1, 2, 3];
d3.select("body").data(data).exit().remove();

parameter를 제공하지 않을경우, 선택된 요소에 대해 바인딩 된 데이터를 배열로 반환한다.

위의 예시에서는 [1,2,3] 을 반환한다.

join

enter,exit,update를 함수 하나에 정의할 수 있다.

즉 예전 general update pattern에 대한 대안이다.

일일히 정의해줄 수도 있다.

1
2
3
4
5
6
7
8
const data = [1, 2, 3];
d3.select("body")
.data(data)
.join(
(enter) => enter.append("div"),
(update) => update.attr("class", "update"),
(exit) => exit.remove()
);

예시

1
2
3
<div>hi</div>
<div>hi</div>
<div>hi</div>
1
2
3
4
const data = [1, 2, 3];
let dom = d3.select("body").selectAll("div").data(data).enter();

dom.append("div").text("hello");

즉 위의 코드의 결과는 다음과 같다.(enter)

1
2
3
4
5
<div>hi</div>
<div>hi</div>
<div>hi</div>
<div>hello</div>
<div>hello</div>
1
2
3
<div>hi</div>
<div>hi</div>
<div>hi</div>
1
2
const data = [1, 2, 3];
d3.select("body").selectAll("div").data(data).text("hello");

다음과 같이 수정하면 hello 5개로 구성된 HTML을 얻을 수 있다.(update)

1
2
3
4
5
<div>hello</div>
<div>hello</div>
<div>hello</div>
<div>hello</div>
<div>hello</div>

Ref

https://github.com/d3/d3-selection#joining-data
https://bost.ocks.org/mike/join/
https://bost.ocks.org/mike/nest/
https://stackoverflow.com/questions/13728402/what-is-the-difference-d3-datum-vs-data
https://www.intothevoid.io/data-visualization/understanding-d3-data-vs-datum/

데이터 시각화의 기본적인 프로세스

오렐리를 통해 데이터 시각화 책을 쓴 Ben Fry는 그의 책 첫 번째 장에서 데이터 시각화 프로세스의 일련의 단계를 정의하였다.

d3를 다루다보면 약간 막히는 부분이 생기는데 어느 프로세스에 있는지 항상 생각하면 더 수월하게 개발할 수 있을 것 같아서 메모한다.

  1. 데이터 수집

데이터를 수집한다.

  1. 데이터 구문분석

데이터를 이루고 있는 구성 성분으로 분해하고 “데이터” 로서 정의한다.

d3의 경우 이름, 형식, 태그등의 데이터 속성을 key,value 형태의 객체로 정의하는 과정이다.

  1. 데이터 필터링

데이터 시각화에 필요하지 않은 데이터를 필터링 해야한다.

  1. 데이터 마이닝

데이터 안에서 통계적 규칙이나 패턴을 분석하여 가치있는 정보로 만든다.

데이터를 표현하기 전 데이터에 대한 기본적인 이해를 돕는 과정이다.

  1. 데이터 표현방법 정의

막대 그래프, 트리등의 시각적 모델을 선택한다.

  1. 데이터의 표현을 정제

css3,html5,svg등의 마크업을 개선한다. 또한 여러 색상이론과 그래픽 디자인 이론에 따라 데이터의 표현을 개선한다.

  1. 상호작용 추가

데이터를 조작하거나 표시되는 기능을 제어하는 방법을 추가한다.