▶함께배우는 프로세싱 :: Kepler2012
이번글에서는 프로세싱을 이용하여 데이터시각화를 제작한 예인 "kepler2012"코드를 하나 하나 뜯어보고자한다.
kepler2012 시각화소개
데이터시각화 전문가 Jer Thorp이 미 항공우주국(NASA)의 케플러 프로젝트의 일환으로 제작한 것이다. 제작자는 2012년에 Kepler2012라는 이름으로 1,236개에 달하는 행성데이터를 바탕으로 인터랙티브한 시각화를 제작하였으며, 부지런(?)하게도, 이후 1,091개의 행성을 업데이트했다. 이번예제에서 다룰 것은 "kepelr2012"로, 2012년 버전이다.
제작자의 Github주소 : http://github.com/blprnt/Kepler-Visualization
위의 url에서 전체 코드를 다운로드받자.
KeplerData
Kepler2012 프로세싱 파일을 열어보면 3가지 탭으로 구분되어있다.메인이라고 할 수 있는 Kepler2012와, Controls, Exoplanet가 그것이다.
*Exoplanet은 우리말로 "태양계의 외행성"을 뜻한다.
본격적으로 코드를 들여다보기전에, 한가지 살펴보아야할 것이 있다. 짐작했겠지만, kepler라는 이름은 수학자의 이름이며 이 코드에서도 케플러법칙이 반영된다. 깊숙히 살펴볼 필요는 없고, 기억을 더듬어보는 정도로만 살펴보도록 하자.
Kepler2012에서 사용하는 데이터
Kepler2012 에서 사용하는 데이터는 2가지이다.
Kepler2012에서 사용하는 데이터
- KeplerData
- planets2012_2
* 두 파일 모두 CSV(쉼표로 구분된 파일형식)
* KeplerData는 2011년의 데이터이며, planets2012_2는 이름 그대로 2012년의 데이터이다.
* KeplerData는 header를 포함하고, planets2012_2는 header를 포함하지 않는다.
* 이유 : 원 저작자의 말에 따르면, 이렇게 두 가지 데이터를 따로 구분하여 사용 및 적용했던 이유는 시각화의 바탕이되는 나사의 데이터포맷이 달랐기 때문이라고 한다. 이에따라 Kepler2012에서는 다른 2가지의 데이터형식에따라 void setup에서 데이터를 로드하는 방법을 다르게 적용하고 있다.
void getPlanets(String url, boolean is2012)
String[] pArray = loadStrings(url);
각 열을 바탕으로 각 행성을 만든다.
2012년 데이터가 맞으면 0을, 아니라면 1을 반환한다.(0과 1을 반환한 것은 시작지점을 다르게 잡기위함이다.) 다시말해 header의 존재여부에 따라서 데이터로드 시작점이 달라지게 된다.
int start = is2012 ? 0 : 1; // 2011년 데이터라면 header를 skip한다.
2011년 데이터라면 header를 건너뛰기 위해, 파라미터로 받아온 boolean값을 활용한다.
for(int i = start; i < pArray.length; i++){
ExoPlanet p;
if(is2012){
p = new ExoPlannet().fromCSV2012(split(pArray[i],".")).init();
} else {
p = new ExoPlanet().fromCSV(split(pArray[i].init();
}
planets.add(p);
maxSize = max(p.radius, maxSize);
minSize = min(p.radius, minSize);
if(p.KOI.equals("326.01") || p.KOI.equals("314.02")){
p.feature = true;
p.label = p.KOI;
}
}
▶if구문에서 2012년 버전 데이터와 2011년버전 데이터를 구분하여 데이터를 로드한다. 이때 불러온 데이터는 ArrayList형태의 'p'에 저장한다. 그리고 이 'p'를 ArrayLis형태인 'planets'에 추가한다.
▶maxSize와 minSize는 말그대로 최대/최소값을 찾기위한 방법이다.
ExoPlanet 클래스
//Constructor function
ExoPlanet(){
};
ExoPlanet fromCSV2012(String[] sa){
KOI = sa[0];
period = float(sa[1]);
radius = float(sa[2]);
axis = float(sa[3]);
temp = float(sa[4]));
return(this); // 이것자체를 리턴!
}
▶ExoPlanet의 데이터를 CSV형태의 데이터파일에서 불러오는 방법이다. "fromCSV2012"라는 이름에서 알 수 있듯, 이것은 2012년 데이터를 불러오는 경우에 사용되는 기능이다.
ExoPlanet fromCSV(Stirng[] sa){
KOI = sa[0];
period = float(sa[6]);
radius = float(sa[14]);
axis = float(sa[15]);
temp = float(sa[16]);
vFlag = int(sa[18]);
return(this);
}
▶여기서 2011년버전과 2012년 버전을 나누는 것은, header유무 때문이 아니다. header유무는 앞서 정리했던 getPlanets함수에서 고려했기때문에, 여기서는 그것을 다룰 필요가 없다. 여기서 나눈 이유는 두 데이터의 구성이 다르기 때문이다.
▶<planet2012_2>데이터와 <KeplerData>의 모습이다. 이렇게봐서는 뭐가 어떻게 다른지 알 수가 없지만,《kepler2012》저작자가 친절하게도 <planets2012_2>에 주석처리를 해주어서 알 수 있다.
▶예를들어 'period'항목의 경우 2012년 데이터의 경우 왼쪽에서 두 번째 열에 있으며, 2011년 데이터의 경우 왼쪽에서 일곱 번째 열에 위치해 있다. 이뿐 아니라 'radius'나 'axis'등 다른 열들도 일치하지 않기때문이 이렇게 2012년과 2011년 데이터를 로드하는데 다른 설정을 적용한 것이다.
ExoPlanet init(){
pixelRadius = radius * ER;
pixelAxis = axis * AU;
float periodInYears = period/365;
float periodInFrames = periodYears * YEAR;
theta = random(2 * PI);
thetaSpeed = (2 * PI);
return(this);
}
▶이 부분은 픽셀기반의 모션데이터와 색상, 기타 사항들을 ExoPlanet data로 초기화하는 부분이다. 여기서 변환상수가 적용된다.
▶ 세타값은 랜덤으로 설정한다.
변환상수 내용
ER : 픽셀당 지구의 반지름을 의미한다.
AU : 픽셀당 천문단위(Astronomical Unit)를 의미한다.
YEAR : 프레임당 1년을 의미한다.
▶ 변환상수에 대한 내용이다. 이 변환상수들의 값은 프로세싱 시작 후 전역변수로 설정한다.
// 업데이트!
void update() {
theta += thetaSpeed;
z += (tz - z) * 0.1;
}
▶여기서 세타값에 세타스피드가 더해져, 지속적으로 세타값은 변화하게 된다.
void render() 본격 그리기가 이루어지는 곳.
//본격적으로 화면에 그리기를 수행하는 부분
void render(){
float apixelAxis = pixelAxis;
if(axis > 1.06 && feature) {
apixelAxis = (( 1.06 + ((axis - 1.06) * (1 - flatness)) * AU ) + axis * 10;
}
float x = sin(theta * ( 1- flatness)) * apixelAxis;
float y = cos(theta * (1- flatness)) * apixelAxis;
pushMatrix(); //-------------------------------------------------- pushMatrix #1
translate(x, y, z); //--------------------------------------------------translate#1
rotateZ(-rot, z); //-------------------------------------------------- rotate #1
rotateX(-rot, x);
noStroke();
if(feature){
//만약, 기능(feature)한다면, 다시 translate()를 이용하여 좌표기준을 바꿔준다.
translate(0, 0, 1); //--------------------------------------------------translate#2
stroke(255, 255);
strokeWeight(2);
noFill();
ellipse(0, 0, pixelRadius + 10, pixelRadius + 10);
strokeWeight(1);
pushMatrix(); //-------------------------------------------------- pushMatrix #1
if(label.equals("Earth")){ //만약 label의 문자가 "Earth"와 같다면 실행한다.
stroke(#01FFFD, 50);
line(0, 0, -pixelAxis * flatness, 0);
}
rotate( (1 - flatness) * PI/2 ); //-------------------------------------------------- rotate #2
stroke(255, 100);
float r = max(50, 100 + (( 1- axis) * 200) ); //참고로, axis데이터의 평균값은 0.14이다.
r *= sqrt(1/zoom); //sqrt()는 괄호안 숫자의 제곱근을 계산하는 함수이다.
if(zoom > 0.5 || label.charAt(0) != '3'){ //**charAt()????????
line(0, 0, 0, -r);
translate(0, -r, -5); //--------------------------------------------------translate#3
rotate(-PI/2); //-------------------------------------------------- rotate #1
scale(1/zoom);
fill(255, 200);
text(label, 0, 4);
}
popMatrix(); //첫 popMatrix()이다.
}
fill(col);
noStroke();
ellipse(0, 0, pixelRadius, pixelRadius);
popMatrix(); // 두 번째 popMatrix()이다.
}
}
여기서 중요한것은 pushMatrix(), popMatrix(), translate(), ratate★()의 활용이다. 좌표계의 이동 또는 회전을 위한 함수호출은 좌표계의 바로 이전 상태에 상대적으로 반응한다. 따라서, 각 도형들이 독립적으로 움직이도록 하고 싶다면 pushmatrix(), popMatrix()기능을 활용하여 원래상태의 매트릭스를 복원해야한다. 무슨말인지 잘 이해가 안간다면 아래글에서 보다 자세한 내용을 확인하도록 하자!
함께배우는 프로세싱 :: pushMatix(), popMatrix(), translate(), rotate()의 활용 바로가기
1. 현재 변형 매트릭스를 저장한다.
2. 첫 번째 사각형을 이동시키고 회전시킨다.
3. 첫 번 째 사각형을 화면에 표시한다.
4. 2단계와 3단계의 영향을 받지 않기 위해 1단계의 매트릭스를 복원한다.
5. 두 번째 사각형을 이동하고 회전시킨다.
6. 두 번째 사각형을 화면에 표시한다.
자세한내용은
테스트1. rot.z의 구조파악
테스트2. 새로운 프로세싱파일을 생성하여, 나도 2가지 회전동시에 적용해보기
용어설명
KeplerData파일(2011년 데이터)
KOI
Dur : 통과시간, 첫 접촉부터 마지막 접촉까지 - HOURS
Depth : Transit depth at center of transit - 리듬위치를 조정(조절)
SNR,
t0, t0_unc, : 사이의 평균간격(모든 관찰되는 이동로와 선형 피팅에따라?)**[Average interval between transits based on a linear fit to all observerd transits and uncertainly - DAYS]
a/R*, a/R*_unc,
r/R*, r/R*_unc, : [10,11]
별의 반경과 불확실성의 비율
b, b_unc,
Rp, : [14] 행성의 반경 - 지구 반경 - EARTH RADII
a, : [15] 궤도기반의 반장경 - AU
* 반장경이란, 타원의 장축과 단축중 장축의 절반을 가리킨다.
Teq, : [16] 행성의 균형(평형)온도 - KELVIN
EB prob,
V, : [18] : Vetting fla
g[flag를 심사?]
1) 확정 및 발표 행성
2) 확률이 강한 후보 - 명확하게 정해진 테스트를 통과
3) 보통수준의 확률을 갖는 후보 - 명확한 테스트가 진행. 그러나 명확하게 탈락
4) 후속 테스트를 명확하게 진행할만큼의 전체제품군의 수가 부족함.
2012데이터
KOI : 케플러개체의 관심번호
Per : 이동로 사이의 평균 간격
Rad : 지구반경의 행성 반경이 6,378km[Planetary radius in Earth radii = 6478km]???
AU : 궤도 장반경
K_Teq : 행성의 균형온도
O/E_1 : 홀수의 비율에 짝수통과깊이[Ratio of odd to even numbered transit depths]??
O/E_2 : 홀수의 비율에 짝수통과깊이[Ratio of odd to even numbered transit depths]??
OCC : 소음으로 나눈 단계에서 상대적인 플럭스 수준 = 0.5???
*flux : 끊임없는 변화, 유동, 흐름
aa_dra 타겟과 비교했을대, RA안에서의 상대적인 소스위치
aa e_dar : 소스위치의 불확실성
as_ddec : 타겟과 비교하여 소스위치의 감속
_ dist : noise로 나눈 소스 위치까지의 거리
_MES : 여러 이벤트 통계
- 다니엘쉐프만 지음, 랜덤웍스 옮김, "프로세싱 날개를 달다", 비제이퍼블릭, 2011, p.317 [본문으로]
'시각화 > 프로세싱' 카테고리의 다른 글
▶함께배우는 프로세싱 :: KEY의 활용 (0) | 2015.03.16 |
---|---|
▶함께 배우는 프로세싱 :: Processing - bezierVertex (0) | 2015.03.15 |
▶함께배우는 프로세싱 :: generative design M6.1 Spring (0) | 2015.03.01 |
▶함께배우는 프로세싱 :: generative design P.2.2.3 Shape from agents (2) | 2015.02.03 |
▶프로세싱 활용노트 ③ - loadStrings(), arrayList() (0) | 2015.01.17 |