вторник, 27 января 2009 г.

Формирование нестандартных графиков с использованием MSChart

24 ноября 2008 года, видимо, в противовес тегу canvas из наступающего стандарта html5, Microsoft выпустила серверный компонент ASP - MS Chart Control (доступен в сборке System.Web.UI.DataVisualization.Charting), посвященный построению графиков. Компонент на стороне сервера генерирует изображение графика и отправляет его клиенту картинкой в форматах BMP, EMF, PNG, JPEG, что очень удобно при удовлетворении требования браузерной совместимости.
Лично меня интересовали возможности компонента по работе с круговыми диаграммами, в частности, построение розы ветров. График такого типа Chart Control не поддерживает. Наиболее похожий - Polar Chart, выводящий данные точками, а не многоугольниками как требуется.
Осуществить модификацию внешнего вида диаграммы к нужному возможно определением собственного обработчика на событие OnPostPaint, обрабатывающего объект Graphics из System.Drawing. Обработчик должен получать два параметра:
  1. object - кто послал;
  2. System.Web.UI.DataVisualization.Charting.ChartPaintEventArgs - с чем послал.
Через object будут доступны исходные данные диаграммы для того, что бы знать, что рисовать на объекте Graphics. Ниже представлен пример получения данных диаграммы и объекта Graphics:
System.Web.UI.DataVisualization.Charting.Chart chart = (System.Web.UI.DataVisualization.Charting.Chart)sender;
System.Web.UI.DataVisualization.Charting.ChartArea area = chart.ChartAreas[0];

Graphics context = e.ChartGraphics.Graphics;
Это просто пример. Если будете его использоать - делайте это осторожно, т.к. график может содержать нуль или несколько пространств (ChartAreas).
Следующий этап работы - связывание исходных данных диаграммы с координатной плоскостью объекта Graphics. Как оказалось, до центра круговой диаграммы добраться довольно-таки сложно:
double xCenter = area.Position.Width / 2 + area.Position.X;
double yCenter = e.ChartGraphics.GetPositionFromAxis(area.Name, System.Web.UI.DataVisualization.Charting.AxisName.Y, area.AxisY.Minimum);
И это центр круговой диаграммы на объекте Graphics... Если положение горизонтального центра ещё можно предсказать, то положение центра вертикали объяснения не находит.
Одного центра для того, что бы комфортно развернуться на пространстве, предоставляемом объектом Graphics, не достаточно. Надо знать и радиус разворота - максимальный радиус окружности диаграммы на графике. Получить его возможно следующим образом:
double xRadius = Math.Abs(xCenter - e.ChartGraphics.GetPositionFromAxis(area.Name, System.Web.UI.DataVisualization.Charting.AxisName.X, 0f));
double yRadius = Math.Abs(yCenter - e.ChartGraphics.GetPositionFromAxis(area.Name, System.Web.UI.DataVisualization.Charting.AxisName.Y, area.AxisY.Maximum));
Через дебаггер не сложно удостовериться, что радиус окружности диаграммы по горизонтали не есть радиус окружности по вертикали. Это означает, что при позиционировании графических примитивов используется разный шаг координат в относительных величинах по осям OX и OY координатной плоскости. Таким образом, вычисление координаты точки, представленной радиальными координатами, на поле Graphics надо производить следующим образом:
double x = xLength * Math.Cos(angle) + xCenter;
double y = yLength * Math.Sin(angle) + yCenter;
, где xLength и yLength есть нормированные с учетом радиуса коордитаты точки в системе осей OX и OY.
Обладая всеми необходимыми данными о координатной плоскости Graphics возможно осуществить уверенное рисование нужных многоугольников
context.FillPolygon(new SolidBrush(s.Color), new PointF[] { center, point1, point2, point3 });
В итоге получаем подобие интересующей розы ветров:
Дальше осталась несложная работа по представлению розы в более привлекательном виде.

Если вас заинтересовал данный компонент, советую посетить коммьюнити.