! 제품 버전을 정확하게 입력해 주세요.
제품 버전이 정확하게 기재되어 있지 않은 경우,
최신 버전을 기준으로 안내 드리므로
더욱 빠르고 명확한 안내를 위해
제품 버전을 정확하게 입력해 주세요!

FlexChart를 사용해 타임 랩스 차트를 만드는 방법 > 블로그 & Tips

본문 바로가기

ComponentOne

블로그 & Tips

FlexChart를 사용해 타임 랩스 차트를 만드는 방법

페이지 정보

작성자 GrapeCity 작성일 2021-04-09 13:46 조회 5,503회 댓글 0건

본문

첨부파일

데이터 추세 시각화는 응용 프로그램에서 차트를 추가하게 되는 가장 중요한 사유 중 하나입니다. 이 블로그에서는 시간이 지나면서 추세가 어떻게 진행되는지 볼 수 있는 타임 랩스 차트를 만드는 방법에 관해 알아보겠습니다. FlexChart for WinForms를 사용해 현재 COVID-19 팬데믹이 전 세계 여러 국가에 어떤 영향을 미쳤는지 보여드리겠습니다. 아래에서 작동 중인 차트를 볼 수 있습니다.

이 블로그에서는 다음 작업을 수행하는 방법에 대해 알아보겠습니다.

  • FlexChart에 타임 랩스 효과 추가

  • 차트 계열에 강조 표시 및 페이드 효과 추가

  • 차트에 사용자 정의 콘텐츠 렌더링

  • 차트 LineMarker 사용자 정의


타임 랩스 차트에 사용할 데이터 소싱

이 블로그에서 사용하는 데이터의 출처는 Our World In Data입니다. 가져온 CSV 데이터는 다음과 같이 POCO 엔터티로서 구문 분석됩니다.

public class CoronaStat
{
    public string Country { get; set; }
    public DateTime Date { get; set; }
    public double TotalCases { get; set; }
}

각 국가는 차트에서 개별 계열을 나타내므로 아래와 같이 CSV 데이터 집합을 CoronaStat 개체 컬렉션으로 집계합니다.

Dictionary<string, List<CoronaStat>> completeData = new Dictionary<string, List<CoronaStat>>();
private void InitCumulativeData()
{
    start = DataService.CoronaStats.Min(x => x.Date);
    end = DataService.CoronaStats.Max(x => x.Date);
    completeData = DataService.CoronaStats.GroupBy(x => x.Country).ToDictionary(
        x => x.Key,
        x => x.ToList()
    );
}


타임 랩스 효과를 만드는 방법

타임 랩스 효과를 시작하기 전에 먼저 작업 대상인 차트 개체를 초기화하겠습니다. 다음 코드 조각은 차트 초기화 코드입니다.

flexChart = new FlexChart()
{
    Dock = DockStyle.Fill,
    BindingX = "Date",
    Binding = "TotalCases",
    ChartType = C1.Chart.ChartType.SplineSymbols,
    Palette = Palette.Darkly,
    BackColor = Color.White,
};
flexChart.Options.InterpolateNulls = true;
flexChart.AnimationSettings = C1.Chart.AnimationSettings.Axes;
flexChart.AnimationUpdate.Duration = 50;

flexChart.AxisX.MajorGrid = true;
flexChart.AxisX.Max = start.AddDays(30).ToOADate();
flexChart.AxisY.Min = 0;
flexChart.AxisY.Max = DataService.CoronaStats.Max(x => x.TotalCases);
flexChart.AxisY.LogBase = 10;

이제 차트에 그려질 각 국가별 차트에 계열을 추가하는 방법을 살펴보겠습니다. 다음 코드 조각을 사용해 이 계열을 추가합니다.

private void AddSeries(string name, DateTime? startDate = null, DateTime? endDate = null)
{
    currentData[name] = new ObservableCollection<CoronaStat>(completeData[name].Where(x=> x.Date>=startDate && x.Date<=endDate));
    var ser = new Series()
    {
        Name = name,
        SymbolSize = 3,
        DataSource = currentData[name]
    };
    flexChart.Series.Add(ser);
}

여기서 주의할 점은 ObservableCollection을 계열 DataSource로 사용했다는 것입니다. FlexChart는 ObservableCollection 알림을 추적하고 자체 업데이트를 통해 컬렉션 변경 사항을 표시합니다. 타임 랩스 효과를 추가하기 위해 Timer 및 RangeSlider를 사용해 일정한 시간 간격으로 이 ObservableCollections를 수정하겠습니다.

_timer = new Timer() { Interval = 50 };
_timer.Tick += _timer_Tick;

private void _timer_Tick(object sender, EventArgs e)
{
    if (rangeSlider.UpperValue < end.Subtract(start).TotalDays)
    {
        rangeSlider.UpperValue++;
        UpdateDisplayedSeries();
    }
}
Dictionary<string, ObservableCollection<CoronaStat>> currentData = new Dictionary<string, ObservableCollection<CoronaStat>>();

private void UpdateDisplayedSeries(bool selectionChanged = false)
{
    var startDate = start.AddDays(rangeSlider.LowerValue);
    var endDate = start.AddDays(rangeSlider.UpperValue);

  // Other Code to Add or Remove series as per the selected countries.

  // Add or remove data from Series source as per selected range.
    foreach(var key in currentData.Keys)
    {
        var toRemove = currentData[key].Where(x => x.Date < startDate || x.Date > endDate).ToList();
        toRemove.ForEach(x => currentData[key].Remove(x));

        var toAdd = completeData[key].Where(x => !currentData[key].Contains(x) && x.Date >= startDate && x.Date <= endDate).ToList();

        var toPrepend = toAdd.Where(x => x.Date < currentData[key].FirstOrDefault()?.Date).ToList();
        toPrepend.Reverse();
        toPrepend.ForEach(x => currentData[key].Insert(0, x));

        var toAppend = toAdd.Where(x => currentData[key].LastOrDefault() == null || x.Date > currentData[key].LastOrDefault().Date).ToList();
        toAppend.ForEach(x => currentData[key].Add(x));
    }

    flexChart.AxisX.Min = start.AddDays(rangeSlider.LowerValue).ToOADate();
    flexChart.AxisX.Max = start.AddDays(rangeSlider.UpperValue).ToOADate() + 30;
}

계열이 시간 경과에 따라 이동하는 모습을 보여주는 타임 랩스 효과에 필요한 것은 이게 전부입니다.


차트 계열에 강조 표시 및 페이드 효과 추가

FlexChart는 클릭하여 차트의 개별 계열 또는 요소를 선택할 수 있는 기본 선택 방법을 제공합니다. 하지만 선택 메커니즘을 직접 만들 수도 있습니다. 이 섹션에서는 차트의 HitTest 및 스타일 지정 API를 사용해 차트 계열에 강조 표시 및 페이드 효과를 만듭니다. 주로 다음 요소를 사용합니다.

차트의 HitTest 메서드가 반환하는 HitTestInfo 개체는 지정한 좌표에서 FlexChart 컨트롤의 일부에 관한 완전한 세부 정보를 제공합니다. 우리는 이 정보를 사용해 사용자가 마우스 포인터로 가리키는 계열이 어떤 것인지 식별합니다. 어떤 계열인지 알아낸 후에는 Series.Style을 사용해 StrokeColor를 사용자 정의하여 강조 표시/페이드 효과를 추가합니다. 다음 코드는 세부 정보를 보여 줍니다.

public static void AddSeriesHighlightEffect(this FlexChart chart)
{
    chart.MouseMove += (s, e) =>
    {
        var hitInfo = chart.HitTest(e.Location);
        var palette = (chart as IPalette);
        var fadeColor = Color.FromArgb(100, Color.LightGray);
        if (hitInfo != null && hitInfo.ChartElement == ChartElement.Legend && hitInfo.Series != null)
        {
            chart.BeginUpdate();
            foreach (var ser in chart.Series)
            {
                ser.Style.StrokeColor = ser.Style.FillColor = hitInfo.Series.Name == ser.Name ?
                Color.FromArgb((int)palette.GetColor(chart.Series.IndexOf(ser))) :
                fadeColor;
            }
            chart.EndUpdate();
        }
        else if(chart.Series.Any(ser=>ser.Style.StrokeColor == fadeColor))
        {
            chart.BeginUpdate();
            foreach (var ser in chart.Series)
            {
                ser.Style.StrokeColor = ser.Style.FillColor = Color.FromArgb((int)palette.GetColor(chart.Series.IndexOf(ser)));
            }
            chart.EndUpdate();
        }
    };
}


사용자 정의 콘텐츠를 렌더링하는 방법

강조 표시/페이드 효과는 차트 그림에서 계열을 빠르게 식별할 수 있게 해주는 훌륭한 옵션입니다. 하지만 사용자 상호 작용이 필요합니다. 사용자 상호 작용 없이 그림에서 계열을 식별할 수 있는 또 다른 방법은 계열 이름을 표시하는 선 머리에 레이블을 추가하는 것입니다. FlexChart의 RenderingEngine의 DrawString() 메서드를 사용해 이 레이블을 추가할 수 있습니다. FlexChart의 RenderingEngine을 활용해 텍스트, 이미지, 셰이프와 같은 사용자 정의 콘텐츠를 차트 그림 영역에 그릴 수 있습니다. 아래 코드 조각은 SymbolRendering 이벤트를 사용해 이러한 콘텐츠를 그리는 방법을 보여줍니다.

public static void AddEndMarker(this Series series, IList source)
{
    series.SymbolRendering += (s, e) =>
    {
        if (e.Index == source.Count - 1)
        {
            e.Engine.DrawPolygon(new double[] { e.Point.X, e.Point.X, e.Point.X + 8 }, new double[] { e.Point.Y - 4, e.Point.Y + 4, e.Point.Y});
            e.Engine.SetFont(new Font("Segoe-UI", 8, FontStyle.Bold));
            e.Engine.DrawString(series.Name, new _Point(e.Point.X + 10, e.Point.Y));
        }
    };
}


차트 LineMarker 사용자 정의

LineMarkers는 차트 그림 영역에 그려진 가로/세로 선으로서, 주어진 좌표에서 하나 이상의 데이터 요소가 지닌 값을 가져오는 데 유용합니다. 다음 코드 조각은 해당 차트에 LineMarker를 추가합니다.

var marker = new LineMarker(chart)
{
    Lines = LineMarkerLines.Vertical,
    LineColor = Color.DarkSlateBlue,
    Interaction = LineMarkerInteraction.Move,
    Content = null,
};

추가된 LineMarker는 일반적으로 대부분은 충분하며 필요한 경우 그리기 이벤트를 사용해 사용자 정의할 수 있습니다. 다음은 FlexChart의 Hit-Test 기능 및 WinForms 기본 그래픽을 LineMarker의 그리기 이벤트와 결합하여 표식을 사용자 정의하는 것을 나타낸 것입니다.

<a name="_bookmark1"></a>marker.Paint += (s, e) =>  
{  
PaintMarkerContent(chart, marker, e.Graphics);  
};  
private static void PaintMarkerContent(FlexChart chart, LineMarker marker, Graphics graphics)  
{  
if (marker.Visible)  
{  
var pointsToDraw = new List<MarkerPoint>();  
var hitTest = chart.HitTest(new Point(marker.X, marker.Y), MeasureOption.X); string maxLengthName = string.Empty;  
string xString = string.Empty;  
if (hitTest != null && hitTest.X != null && hitTest.Distance < 3)  
//Logic for collecting points details for the X value  
}  
pointsToDraw = pointsToDraw.OrderByDescending(x => x.Value).ToList(); if (pointsToDraw.Count > 0)  
{  
var font = new Font("Segoe-UI", 10);  
var itemSize = graphics.MeasureString(maxLengthName, font);  
var size = new SizeF(itemSize.Width + 100, (itemSize.Height + 2) * (pointsToDraw.Count + 1) + 15); var location = new PointF(marker.X + 5, marker.Y - size.Height / 2);  
graphics.FillRectangle(new SolidBrush(Color.White), new RectangleF(location, size));  
graphics.FillRectangle(new SolidBrush(Color.FromArgb(50, Color.DarkSlateBlue)), new RectangleF (location, size));

location.Y += 2;  
graphics.FillRectangle(Brushes.DarkSlateBlue, new RectangleF(location, new SizeF(size.Width, itemSize.Height + 10)));  
graphics.DrawString(xString, new Font("Segoe-UI", 12), Brushes.White, location.X + 5, location.Y +

</div>

<div>

5);

location.Y += itemSize.Height + 12;  
for (int idx = 0; idx < pointsToDraw.Count; idx++)  
{

</div>

<div>

var markerPoint = pointsToDraw[idx];  
graphics.FillEllipse(new SolidBrush(markerPoint.Color), location.X + 5, location.Y + 2, 10, 10); graphics.DrawString($"{markerPoint.Name} + {markerPoint.Value}", font, new SolidBrush  
(markerPoint.Color), location.X + 25, location.Y);  
graphics.FillEllipse(new SolidBrush(markerPoint.Color), markerPoint.Location.X - 5, markerPoint.  
Location.Y - 5, 10, 10);  
location.Y += itemSize.Height + 2;  
}  
}  
}


FlexChart를 사용해 타임 랩스 차트를 사용하는 방법


FlexChart로 타임 랩스 차트 업그레이드

FlexChart는 DataLabels, LineMarkers, Annotations 등 여러 가지 유용한 기능을 즉시 제공하는 강력한 데이터 시각화 컨트롤입니다. 이러한 기능만으로도 모든 차트 작성 응용 프로그램의 일상적인 요구 기능 대부분을 충분히 충족할 수 있지만, FlexChart는 이 블로그에서 보시는 바와 같이 그 외 다른 요구 사항도 모두 충족할 수 있는 풍부한 API를 제공합니다. 이 블로그에서는 응용 프로그램에서 바로 사용할 수 있도록 사용자 정의를 확장 메서드로 수행했습니다. 전체 샘플은 첨부파일을 다운로드하여 확인하실 수 있습니다.

FlexChart에 대해 자세히 알아보려면 GrapeCity 웹 사이트를 방문해 보세요.


  • 페이스북으로 공유
  • 트위터로  공유
  • 링크 복사
  • 카카오톡으로 보내기

댓글목록

등록된 댓글이 없습니다.

메시어스 홈페이지를 통해 제품에 대해서 더 자세히 알아 보세요!
홈페이지 바로가기

태그1

메시어스 홈페이지를 통해 제품에 대해서 더 자세히 알아 보세요!
홈페이지 바로가기
이메일 : sales-kor@mescius.com | 전화 : 1670-0583 | 경기도 과천시 과천대로 7길 33, 디테크타워 B동 1107호 메시어스(주) 대표자 : 허경명 | 사업자등록번호 : 123-84-00981 | 통신판매업신고번호 : 2013-경기안양-00331 ⓒ 2024 MESCIUS inc. All rights reserved.