출처: 데브피아/금명식(kgmoss)
[강좌 - 2] 깔끔한 차트(CHART)가 필요하십니까.
부제 - 라인차트 그리기
이번 강좌가 제가 하는 전체 강좌의 핵심부분입니다.
예제를 시간날때 조금씩 만들어서 부드럽게 연결되지 못하는 부분이 있습니다.
(글을 편하게 쓰겠습니다.)
이번에는 제대로 된 라인 그래프를 그릴 것이다.
Data는 엑셀에 들어 있는 데이터를 불러와서, 그 데이터 대로 그래프를 그린다.
Data는 날짜-값 의 형식으로 저장이 되어 있다.
위의 그림은 첨부된 예제에 나온 그림 그대로 이다.
A. 차트를 그리기 전 몇가지 주의사항
PictureBox(이하 pBox) 에서 좌표(0,0)는 어디일까?
pBox에서의 좌표 (0,0)는 좌상단이다(그림의 숫자 142.33 부분)
차트를 그릴때 X축과 Y축이 만나는지점-좌하단(그림의 숫자와 날짜가 만나는부분)- 을 (0,0)으로 오해 하지 말자.
B. 차트 그리기(설명하기가 조금 힘듭니다.. 직접 그리면서 설명하면 좋으련만..)
아래는 차트를 그리는 전체 소스 이다.
따로 설명할 길이 없기 때문에 소스 중간중간에 설명을 주석식으로 붙이겠다.
'대부분의 변수는 비율로 계산 된다.
'임의의 어떤 숫자로 값을 정해도 될 수 있으나, 비율로 계산하면 폼의 크기 변경 등에 바로 적용할 수 있다.
Dim i As Integer
Dim k As Integer
Dim blankValue As Double '임의 Blank 값
Dim totWidth As Double 'chart의 실제 넓이
Dim totHeight As Double 'chart의 실제 높이
Dim unitWidth As Double 'chart의 한 유닛(하나의 DATA)의 높이
Dim unitHeight As Double 'chart의 한 유닛(하나의 DATA)의 넓이
Dim MaxValue As Double 'chart DATA의 max값
Dim MinValue As Double 'chart DATA의 min값
Dim YgubunTxt As Double 'Y축 값을 적을 공간
Dim XgubunTxt As Double 'X축 값을 적을 공간
Dim partX As Double 'X축 보조선을 그릴 간격
Dim partY As Double 'Y축 보조선을 그릴 간격
pBox.Cls
'data를 배열에 저장
GetData
'pBox.BackColor = RGB(253, 255, 230) '배경색 변경시 사용
blankValue = pBox.ScaleHeight * (5 / 100) '(5 / 100)는 임의로 정한 수치입니다.
'차트를 그리자면 차트를 그릴 Data의 Max, Min값이 꼭 필요하다.
'Max, Min 값을 알아야만 차트의 범위를 계산 할 수 있다.
MaxValue = MaxChartData()
MinValue = MinChartData()
'X, Y축에 값을 쓸 공간 - X축은 Data, Y축은 날짜가 들어갈 것이다.
'이 공간을 만들지 않으면, 차트 위에 글씨가 쓰여질 것이다.
'y축에 값을 쓸 공간을 정한다.
YgubunTxt = blankValue * 3
'y축에 값을 쓸 공간을 정한다.
XgubunTxt = blankValue * 2
'아래 두 변수는 차트를 그릴 부분의 전체 Height와 Width이다.
'예를들어 totWidth는 전체 pBox의 넓이에서 우리가 정한 여유공간(좌,우)와 왼쪽 텍스트가 들어갈 공간 YgubunTxt 를 뺀 공간이다.
totWidth = pBox.ScaleWidth - (blankValue * 2) - YgubunTxt 'BlankValue가 왼쪽, 오른쪽 2곳이므로
totHeight = pBox.ScaleHeight - (blankValue * 2) - XgubunTxt 'BlankValue가 아래, 위쪽 2곳이므로
'차트의 보조선을 그릴 간격을 의미 한다.
partX = totHeight / 4 'X축 보조선 간격
partY = totWidth / 5 'Y축 보조선 간격
'중요한 변수이다.
'차트를 그린다는 것은 점과 점을 연결하는 선을 그리는 것이다.
'하나의 Data는 날짜-값 의 형태로 표현이 되어 있기 때문에, 이것을 정확히 pBox에 넣기 위해서는 그 값이 나타내는 범위를 정확히 정해 주어야 한다.
unitWidth = (totWidth) / (totCnt)
unitHeight = (MaxValue - MinValue) / (totHeight)
'chart 그리기
For i = 0 To totCnt - 2
'실제 차트를 그리는 것은 아래 Line 메서드 하나이다.
'설명을 쉽게 하기 위해 값들을 풀어서 써 놓았다.( 변수를 합할 수 있고, 더 좋은 로직이 있다면 그것을 써라. 이 예제는 강좌용일 뿐이다.)
'totCnt는 전체 Data의 갯수, ChartData는 차트그릴 Data가 들어간 배열변수
pBox.Line ((blankValue + YgubunTxt + (i * unitWidth)) + ((unitWidth * 2) / 4), _ 'X1
totHeight + blankValue - ((ChartData(i, 1) - MinValue) / unitHeight)) _ 'Y1
-(blankValue + YgubunTxt + ((i + 1) * unitWidth) + (unitWidth * 2 / 4), _ 'X2
totHeight + blankValue - ((ChartData(i + 1, 1) - MinValue) / unitHeight)), RGB(255, 0, 0) 'Y2, 색
Next i
' 위의 Line 메서드 설명
'1. X1값
' for문이 없다고 가정을 할때, 처음 시작하는 X 좌표는 어디인가?
' 위의 그림으로 보면 좌상단의 빨간색 선이 시작하는 부분의 X좌표이다.
' X좌표는 pBox에서 왼쪽부터 얼마나 떨어졌는지 찾으면 된다.
' 그러므로 그림을 그리지 않는 부분(blankValue + YgubunTxt)은 띄우고
' 하나의 Data가 차지하는 공간을 띄운다(i * unitWidth)
' 뒤의 ((unitWidth * 2) / 4) 부분은 (unitWidth / 2)로 치환될 수 있고(나중에 막대 그래프를 그리기 쉽게 하기 위해 위와 같은 형태이다)
' (unitWidth / 2)는 하나의 Data의 점이 찍힐 unitWidth가 넓기 때문에 중간에 찍기 위해 만든 코드이다.
'2. Y1값
' 우리들이 차트를 그리는 방법에서의 Y값은 차트의 밑부분(그림에서 날짜가 들어간부분)에서 시작하여 그린다(이 지점을 0으로 생각한다)
' 하지만 pBox에서의 Y축 0는 우리가 생각하는 Y축 0의 지점과는 정 반대이다.
' 그래서 우리가 생각하는 0지점을 만들기 위해 다음과 같은 작업을 한다.
' totHeight + blankValue 이렇게 하면 차트가 차지할 전체에서 아래쪽 빈 공간만 포함된 우리들이 원하는 좌표가 된다
' 이제 위의 값에서 각 Data가 차지하는 공간을 빼 주기만 하면 된다.
' ((ChartData(i, 1) - MinValue) / unitHeight
' 실제 값이 들어간 ChartData(i, 1)에서 Data의 MinValue를 뺀 이유는
' 우리가 그릴 그래프틑 1 ~ 100 까지의 그래프를 그리는 것이 아니라, MinData에서 부터 MaxData까지 그리는 그래프 이다.
' 그러므로 차트의 Y축 0의 값은 MinValue가 되어야 한다.
' 뒤에 unitHeight를 나누어준 이유는 Data의 값이 어느 정도의 높이를 차지 하는지 계산하기 위해서이다.
'위의 설명을 제대로 이해 했다면, 아래는 별 문제 없이 넘어갈 것이다.
'Y보조선, 값 보이기
For i = 0 To 4
pBox.Line ((blankValue + YgubunTxt - 50), blankValue + (partX * i)) _
-(pBox.ScaleWidth - blankValue, blankValue + (partX * i)), RGB(187, 187, 187)
pBox.CurrentY = blankValue + (partX * i) - 80
pBox.CurrentX = XgubunTxt / 2
pBox.Print Format(MaxValue - (MaxValue - MinValue) / 4 * i, "###.00")
'Print메서드를 이용하기 위해서는 X,Y값을 미리 조정해 두어야 한다.
Next
'X의 값들(날짜)는 정밀하지 않을 수 있다.
'정밀하게 표현하자면 더욱 세밀한 제어가 필요하다.(차트는 흐름을 보는것이지, 값을 일일이 비교하면서 보는 것이 아니다.)
'X보조선, 값 보이기
For i = 0 To 5
Select Case i
Case 0
pBox.Line ((blankValue + YgubunTxt + ((unitWidth * 2) / 4) + (partY * i)), totHeight - blankValue + XgubunTxt - 50) _
-((blankValue + YgubunTxt + ((unitWidth * 2) / 4) + (partY * i)), totHeight - blankValue + XgubunTxt + 50), RGB(187, 187, 187)
pBox.CurrentY = pBox.ScaleHeight - blankValue - (YgubunTxt / 2)
pBox.CurrentX = blankValue + XgubunTxt
pBox.Print ChartData(0, 0)
Case Else
pBox.Line ((blankValue + YgubunTxt) + (partY * i) - ((unitWidth * 2) / 4), totHeight - blankValue + XgubunTxt - 50) _
-((blankValue + YgubunTxt) + (partY * i) - ((unitWidth * 2) / 4), totHeight - blankValue + XgubunTxt + 50), RGB(187, 187, 187)
pBox.CurrentY = pBox.ScaleHeight - blankValue - (YgubunTxt / 2)
If i = 5 Then
pBox.CurrentX = XgubunTxt + (partY * i) - 180
Else
pBox.CurrentX = XgubunTxt + (partY * i)
End If
'totCnt -1 - (partY * i)
'pBox.Print ChartData((partY * i / totWidth) * totCnt, 0)
pBox.Print ChartData(Round(totCnt / 5) * (i), 0)
End Select
Next
'보조선 그리기
pBox.Line (blankValue + YgubunTxt, blankValue)-(pBox.ScaleWidth - blankValue, pBox.ScaleHeight - blankValue - XgubunTxt), , B 'RGB(187, 187, 187), B '보조선출력
이로써 라인차트 그리기의 설명이 모드 끝났다.
처음 보는 사람들이 대부분일 것이고, 설명이 부족한 관계로 조금 어려 울 수 있다.
이 프로그램을 보자면 먼저 변수들의 의미부터 파악을 한 후, 나름대로 중요한 부분이라고 생각하는 파란색 부분을 잘 살펴보면 도움이 될 것이다.
--------------------------------------------------------------------------------
다음 강좌는 막대그래프와, 차트의 응용(마우스 움직일때 보조선 나타내기) 가 이어집니다.
강좌를 처음 써 보는 것이어서 많이 부족하고, 힘들고 그러네요..
한분이라도 도움이 되었으면 합니다..