Free cookie consent management tool by TermsFeed Policy Generator

source: branches/DataPreprocessing/HeuristicLab.DataPreprocessing.Views/3.3/PreprocessingDataTableView.cs @ 10871

Last change on this file since 10871 was 10871, checked in by mleitner, 10 years ago

Persist Histogram classification setting, disable legend for histogram

File size: 36.4 KB
Line 
1#region License Information
2/* HeuristicLab
3 * Copyright (C) 2002-2013 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
4 *
5 * This file is part of HeuristicLab.
6 *
7 * HeuristicLab is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * HeuristicLab is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with HeuristicLab. If not, see <http://www.gnu.org/licenses/>.
19 */
20#endregion
21
22using System;
23using System.Collections.Generic;
24using System.Drawing;
25using System.Linq;
26using System.Windows.Forms;
27using System.Windows.Forms.DataVisualization.Charting;
28using HeuristicLab.Collections;
29using HeuristicLab.Core.Views;
30using HeuristicLab.DataPreprocessing.Implementations;
31using HeuristicLab.MainForm;
32
33namespace HeuristicLab.Analysis.Views
34{
35  [View("Preprocessing DataTable View")]
36  [Content(typeof(PreprocessingDataTable), false)]
37  public partial class PreprocessingDataTableView : ItemView, IConfigureableView
38  {
39    protected List<Series> invisibleSeries;
40    protected Dictionary<IObservableList<double>, DataRow> valuesRowsTable;
41
42    public new PreprocessingDataTable Content
43    {
44      get { return (PreprocessingDataTable)base.Content; }
45      set { base.Content = value; }
46    }
47
48    public List<double> classification { get; set; }
49
50    public PreprocessingDataTableView()
51    {
52      InitializeComponent();
53      valuesRowsTable = new Dictionary<IObservableList<double>, DataRow>();
54      invisibleSeries = new List<Series>();
55      chart.CustomizeAllChartAreas();
56      chart.ChartAreas[0].CursorX.Interval = 1;
57    }
58
59    #region Event Handler Registration
60    protected override void DeregisterContentEvents()
61    {
62      foreach (DataRow row in Content.Rows)
63        DeregisterDataRowEvents(row);
64      Content.VisualPropertiesChanged -= new EventHandler(Content_VisualPropertiesChanged);
65      Content.Rows.ItemsAdded -= new CollectionItemsChangedEventHandler<DataRow>(Rows_ItemsAdded);
66      Content.Rows.ItemsRemoved -= new CollectionItemsChangedEventHandler<DataRow>(Rows_ItemsRemoved);
67      Content.Rows.ItemsReplaced -= new CollectionItemsChangedEventHandler<DataRow>(Rows_ItemsReplaced);
68      Content.Rows.CollectionReset -= new CollectionItemsChangedEventHandler<DataRow>(Rows_CollectionReset);
69
70      Content.SelectedRows.ItemsAdded -= new CollectionItemsChangedEventHandler<DataRow>(Rows_ItemsAdded);
71      Content.SelectedRows.ItemsRemoved -= new CollectionItemsChangedEventHandler<DataRow>(Rows_ItemsRemoved);
72      Content.SelectedRows.ItemsReplaced -= new CollectionItemsChangedEventHandler<DataRow>(Rows_ItemsReplaced);
73      Content.SelectedRows.CollectionReset -= new CollectionItemsChangedEventHandler<DataRow>(Rows_CollectionReset);
74      base.DeregisterContentEvents();
75    }
76    protected override void RegisterContentEvents()
77    {
78      base.RegisterContentEvents();
79      Content.VisualPropertiesChanged += new EventHandler(Content_VisualPropertiesChanged);
80      Content.Rows.ItemsAdded += new CollectionItemsChangedEventHandler<DataRow>(Rows_ItemsAdded);
81      Content.Rows.ItemsRemoved += new CollectionItemsChangedEventHandler<DataRow>(Rows_ItemsRemoved);
82      Content.Rows.ItemsReplaced += new CollectionItemsChangedEventHandler<DataRow>(Rows_ItemsReplaced);
83      Content.Rows.CollectionReset += new CollectionItemsChangedEventHandler<DataRow>(Rows_CollectionReset);
84
85      Content.SelectedRows.ItemsAdded += new CollectionItemsChangedEventHandler<DataRow>(SelectedRows_ItemsAdded);
86      Content.SelectedRows.ItemsRemoved += new CollectionItemsChangedEventHandler<DataRow>(SelectedRows_ItemsRemoved);
87      Content.SelectedRows.ItemsReplaced += new CollectionItemsChangedEventHandler<DataRow>(SelectedRows_ItemsReplaced);
88      Content.SelectedRows.CollectionReset += new CollectionItemsChangedEventHandler<DataRow>(SelectedRows_CollectionReset);
89    }
90
91
92    protected virtual void RegisterDataRowEvents(DataRow row) {
93      row.NameChanged += new EventHandler(Row_NameChanged);
94      row.VisualPropertiesChanged += new EventHandler(Row_VisualPropertiesChanged);
95      valuesRowsTable.Add(row.Values, row);
96      row.Values.ItemsAdded += new CollectionItemsChangedEventHandler<IndexedItem<double>>(Values_ItemsAdded);
97      row.Values.ItemsRemoved += new CollectionItemsChangedEventHandler<IndexedItem<double>>(Values_ItemsRemoved);
98      row.Values.ItemsReplaced += new CollectionItemsChangedEventHandler<IndexedItem<double>>(Values_ItemsReplaced);
99      row.Values.ItemsMoved += new CollectionItemsChangedEventHandler<IndexedItem<double>>(Values_ItemsMoved);
100      row.Values.CollectionReset += new CollectionItemsChangedEventHandler<IndexedItem<double>>(Values_CollectionReset);
101    }
102    protected virtual void DeregisterDataRowEvents(DataRow row)
103    {
104      row.Values.ItemsAdded -= new CollectionItemsChangedEventHandler<IndexedItem<double>>(Values_ItemsAdded);
105      row.Values.ItemsRemoved -= new CollectionItemsChangedEventHandler<IndexedItem<double>>(Values_ItemsRemoved);
106      row.Values.ItemsReplaced -= new CollectionItemsChangedEventHandler<IndexedItem<double>>(Values_ItemsReplaced);
107      row.Values.ItemsMoved -= new CollectionItemsChangedEventHandler<IndexedItem<double>>(Values_ItemsMoved);
108      row.Values.CollectionReset -= new CollectionItemsChangedEventHandler<IndexedItem<double>>(Values_CollectionReset);
109      valuesRowsTable.Remove(row.Values);
110      row.VisualPropertiesChanged -= new EventHandler(Row_VisualPropertiesChanged);
111      row.NameChanged -= new EventHandler(Row_NameChanged);
112    }
113    #endregion
114
115    protected override void OnContentChanged()
116    {
117      base.OnContentChanged();
118      invisibleSeries.Clear();
119      chart.ChartAreas[0].AxisX.Title = string.Empty;
120      chart.ChartAreas[0].AxisY.Title = string.Empty;
121      chart.ChartAreas[0].AxisY2.Title = string.Empty;
122      chart.Series.Clear();
123      if (Content != null)
124      {
125
126        if (classification != null)
127        {
128          chart.Titles[0].Text = Content.Name;
129        }
130
131        AddDataRows(Content.Rows);
132        AddSelectedDataRows(Content.SelectedRows);
133        ConfigureChartArea(chart.ChartAreas[0]);
134        RecalculateAxesScale(chart.ChartAreas[0]);
135      }
136    }
137
138    protected override void SetEnabledStateOfControls()
139    {
140      base.SetEnabledStateOfControls();
141      chart.Enabled = Content != null;
142    }
143
144    public void ShowConfiguration()
145    {
146      if (Content != null)
147      {
148        using (var dialog = new DataTableVisualPropertiesDialog(Content))
149        {
150          dialog.ShowDialog(this);
151        }
152      }
153      else MessageBox.Show("Nothing to configure.");
154    }
155    protected virtual void AddDataRows(IEnumerable<DataRow> rows)
156    {
157      foreach (var row in rows)
158      {
159        RegisterDataRowEvents(row);
160        var series = new Series(row.Name);
161        if (row.VisualProperties.DisplayName.Trim() != String.Empty) series.LegendText = row.VisualProperties.DisplayName;
162        else series.LegendText = row.Name;
163        ConfigureSeries(series, row);
164        FillSeriesWithRowValues(series, row);
165
166        if (classification == null)
167        {
168          chart.Series.Add(series);
169        }
170      }
171
172      ConfigureChartArea(chart.ChartAreas[0]);
173      RecalculateAxesScale(chart.ChartAreas[0]);
174      UpdateYCursorInterval();
175    }
176
177    protected virtual void AddSelectedDataRows(IEnumerable<DataRow> rows) {
178
179      // only paint selection for line chart
180      if (rows.Count() > 0 && rows.ElementAt(0).VisualProperties.ChartType == DataRowVisualProperties.DataRowChartType.Line)
181      {
182        // add dummy series for selction entry in legend
183        if (rows.Count() > 0 && chart.Series.FindByName("(Selection)") == null) {
184          Series series = new Series("(Selection)");
185          series.IsVisibleInLegend = true;
186          series.Color = Color.Green;
187          series.BorderWidth = 1;
188          series.BorderDashStyle = ChartDashStyle.Solid;
189          series.BorderColor = Color.Empty;
190          series.ChartType = SeriesChartType.FastLine;
191          chart.Series.Add(series);
192        }
193
194        foreach (var row in rows) {
195          row.VisualProperties.IsVisibleInLegend = false;
196          row.VisualProperties.Color = Color.Green;
197          //add selected to name in order to avoid naming conflict
198          var series = new Series(row.Name + "(Selected)");
199          ConfigureSeries(series, row);
200          FillSeriesWithRowValues(series, row);
201
202          if (classification == null) {
203            chart.Series.Add(series);
204          }
205        }
206      }
207    }
208
209    protected virtual void RemoveSelectedDataRows(IEnumerable<DataRow> rows) {
210
211      // only remove selection for line chart
212      if (rows.Count() > 0 && rows.ElementAt(0).VisualProperties.ChartType == DataRowVisualProperties.DataRowChartType.Line)
213      {
214        //remove selection entry in legend
215        if (Content.SelectedRows.Count == 0) {
216          Series series = chart.Series["(Selection)"];
217          chart.Series.Remove(series);
218        }
219
220        foreach (var row in rows) {
221          Series series = chart.Series[row.Name + "(Selected)"];
222          chart.Series.Remove(series);
223        }
224      }
225    }
226
227    protected virtual void RemoveDataRows(IEnumerable<DataRow> rows) {
228      foreach (var row in rows) {
229        DeregisterDataRowEvents(row);
230        Series series = chart.Series[row.Name];
231        chart.Series.Remove(series);
232        if (invisibleSeries.Contains(series))
233          invisibleSeries.Remove(series);
234      }
235      RecalculateAxesScale(chart.ChartAreas[0]);
236    }
237
238    private void ConfigureSeries(Series series, DataRow row)
239    {
240      RemoveCustomPropertyIfExists(series, "PointWidth");
241      series.BorderWidth = 1;
242      series.BorderDashStyle = ChartDashStyle.Solid;
243      series.BorderColor = Color.Empty;
244
245      if (row.VisualProperties.Color != Color.Empty)
246        series.Color = row.VisualProperties.Color;
247      else series.Color = Color.Empty;
248      series.IsVisibleInLegend = row.VisualProperties.IsVisibleInLegend;
249
250      switch (row.VisualProperties.ChartType) {
251        case DataRowVisualProperties.DataRowChartType.Line:
252          series.ChartType = SeriesChartType.FastLine;
253          series.BorderWidth = row.VisualProperties.LineWidth;
254          series.BorderDashStyle = ConvertLineStyle(row.VisualProperties.LineStyle);
255          break;
256        case DataRowVisualProperties.DataRowChartType.Bars:
257          // Bar is incompatible with anything but Bar and StackedBar*
258          if (!chart.Series.Any(x => x.ChartType != SeriesChartType.Bar && x.ChartType != SeriesChartType.StackedBar && x.ChartType != SeriesChartType.StackedBar100))
259            series.ChartType = SeriesChartType.Bar;
260          else
261          {
262            series.ChartType = SeriesChartType.FastPoint; //default
263            row.VisualProperties.ChartType = DataRowVisualProperties.DataRowChartType.Points;
264          }
265          break;
266        case DataRowVisualProperties.DataRowChartType.Columns:
267          series.ChartType = SeriesChartType.Column;
268          break;
269        case DataRowVisualProperties.DataRowChartType.Points:
270          series.ChartType = SeriesChartType.FastPoint;
271          break;
272        case DataRowVisualProperties.DataRowChartType.Histogram:
273          series.ChartType = SeriesChartType.StackedColumn;
274          series.SetCustomProperty("PointWidth", "1");
275          if (!series.Color.IsEmpty && series.Color.GetBrightness() < 0.25)
276            series.BorderColor = Color.White;
277          else series.BorderColor = Color.Black;
278          break;
279        case DataRowVisualProperties.DataRowChartType.StepLine:
280          series.ChartType = SeriesChartType.StepLine;
281          series.BorderWidth = row.VisualProperties.LineWidth;
282          series.BorderDashStyle = ConvertLineStyle(row.VisualProperties.LineStyle);
283          break;
284        default:
285          series.ChartType = SeriesChartType.FastPoint;
286          break;
287      }
288      series.YAxisType = row.VisualProperties.SecondYAxis ? AxisType.Secondary : AxisType.Primary;
289      series.XAxisType = row.VisualProperties.SecondXAxis ? AxisType.Secondary : AxisType.Primary;
290      if (row.VisualProperties.DisplayName.Trim() != String.Empty) series.LegendText = row.VisualProperties.DisplayName;
291      else series.LegendText = row.Name;
292
293      string xAxisTitle = string.IsNullOrEmpty(Content.VisualProperties.XAxisTitle)
294                      ? "X"
295                      : Content.VisualProperties.XAxisTitle;
296      string yAxisTitle = string.IsNullOrEmpty(Content.VisualProperties.YAxisTitle)
297                            ? "Y"
298                            : Content.VisualProperties.YAxisTitle;
299      series.ToolTip =
300        series.LegendText + Environment.NewLine +
301        xAxisTitle + " = " + "#INDEX," + Environment.NewLine +
302        yAxisTitle + " = " + "#VAL";
303    }
304
305    private void ConfigureChartArea(ChartArea area)
306    {
307      if (Content.VisualProperties.TitleFont != null) chart.Titles[0].Font = Content.VisualProperties.TitleFont;
308      if (!Content.VisualProperties.TitleColor.IsEmpty) chart.Titles[0].ForeColor = Content.VisualProperties.TitleColor;
309      chart.Titles[0].Text = Content.VisualProperties.Title;
310
311      if (Content.VisualProperties.AxisTitleFont != null) area.AxisX.TitleFont = Content.VisualProperties.AxisTitleFont;
312      if (!Content.VisualProperties.AxisTitleColor.IsEmpty) area.AxisX.TitleForeColor = Content.VisualProperties.AxisTitleColor;
313      area.AxisX.Title = Content.VisualProperties.XAxisTitle;
314
315      if (Content.VisualProperties.AxisTitleFont != null) area.AxisX2.TitleFont = Content.VisualProperties.AxisTitleFont;
316      if (!Content.VisualProperties.AxisTitleColor.IsEmpty) area.AxisX2.TitleForeColor = Content.VisualProperties.AxisTitleColor;
317      area.AxisX2.Title = Content.VisualProperties.SecondXAxisTitle;
318
319      if (Content.VisualProperties.AxisTitleFont != null) area.AxisY.TitleFont = Content.VisualProperties.AxisTitleFont;
320      if (!Content.VisualProperties.AxisTitleColor.IsEmpty) area.AxisY.TitleForeColor = Content.VisualProperties.AxisTitleColor;
321      area.AxisY.Title = Content.VisualProperties.YAxisTitle;
322
323      if (Content.VisualProperties.AxisTitleFont != null) area.AxisY2.TitleFont = Content.VisualProperties.AxisTitleFont;
324      if (!Content.VisualProperties.AxisTitleColor.IsEmpty) area.AxisY2.TitleForeColor = Content.VisualProperties.AxisTitleColor;
325      area.AxisY2.Title = Content.VisualProperties.SecondYAxisTitle;
326
327      area.AxisX.IsLogarithmic = Content.VisualProperties.XAxisLogScale;
328      area.AxisX2.IsLogarithmic = Content.VisualProperties.SecondXAxisLogScale;
329      area.AxisY.IsLogarithmic = Content.VisualProperties.YAxisLogScale;
330      area.AxisY2.IsLogarithmic = Content.VisualProperties.SecondYAxisLogScale;
331    }
332
333    private void RecalculateAxesScale(ChartArea area)
334    {
335      // Reset the axes bounds so that RecalculateAxesScale() will assign new bounds
336      foreach (Axis a in area.Axes)
337      {
338        a.Minimum = double.NaN;
339        a.Maximum = double.NaN;
340      }
341      area.RecalculateAxesScale();
342      area.AxisX.IsMarginVisible = false;
343      area.AxisX2.IsMarginVisible = false;
344
345      if (!Content.VisualProperties.XAxisMinimumAuto && !double.IsNaN(Content.VisualProperties.XAxisMinimumFixedValue)) area.AxisX.Minimum = Content.VisualProperties.XAxisMinimumFixedValue;
346      if (!Content.VisualProperties.XAxisMaximumAuto && !double.IsNaN(Content.VisualProperties.XAxisMaximumFixedValue)) area.AxisX.Maximum = Content.VisualProperties.XAxisMaximumFixedValue;
347      if (!Content.VisualProperties.SecondXAxisMinimumAuto && !double.IsNaN(Content.VisualProperties.SecondXAxisMinimumFixedValue)) area.AxisX2.Minimum = Content.VisualProperties.SecondXAxisMinimumFixedValue;
348      if (!Content.VisualProperties.SecondXAxisMaximumAuto && !double.IsNaN(Content.VisualProperties.SecondXAxisMaximumFixedValue)) area.AxisX2.Maximum = Content.VisualProperties.SecondXAxisMaximumFixedValue;
349      if (!Content.VisualProperties.YAxisMinimumAuto && !double.IsNaN(Content.VisualProperties.YAxisMinimumFixedValue)) area.AxisY.Minimum = Content.VisualProperties.YAxisMinimumFixedValue;
350      if (!Content.VisualProperties.YAxisMaximumAuto && !double.IsNaN(Content.VisualProperties.YAxisMaximumFixedValue)) area.AxisY.Maximum = Content.VisualProperties.YAxisMaximumFixedValue;
351      if (!Content.VisualProperties.SecondYAxisMinimumAuto && !double.IsNaN(Content.VisualProperties.SecondYAxisMinimumFixedValue)) area.AxisY2.Minimum = Content.VisualProperties.SecondYAxisMinimumFixedValue;
352      if (!Content.VisualProperties.SecondYAxisMaximumAuto && !double.IsNaN(Content.VisualProperties.SecondYAxisMaximumFixedValue)) area.AxisY2.Maximum = Content.VisualProperties.SecondYAxisMaximumFixedValue;
353      if (area.AxisX.Minimum >= area.AxisX.Maximum) area.AxisX.Maximum = area.AxisX.Minimum + 1;
354      if (area.AxisX2.Minimum >= area.AxisX2.Maximum) area.AxisX2.Maximum = area.AxisX2.Minimum + 1;
355      if (area.AxisY.Minimum >= area.AxisY.Maximum) area.AxisY.Maximum = area.AxisY.Minimum + 1;
356      if (area.AxisY2.Minimum >= area.AxisY2.Maximum) area.AxisY2.Maximum = area.AxisY2.Minimum + 1;
357    }
358
359    protected virtual void UpdateYCursorInterval()
360    {
361      double interestingValuesRange = (
362        from series in chart.Series
363        where series.Enabled
364        let values = (from point in series.Points
365                      where !point.IsEmpty
366                      select point.YValues[0]).DefaultIfEmpty(1.0)
367        let range = values.Max() - values.Min()
368        where range > 0.0
369        select range
370        ).DefaultIfEmpty(1.0).Min();
371
372      double digits = (int)Math.Log10(interestingValuesRange) - 3;
373      double yZoomInterval = Math.Pow(10, digits);
374      this.chart.ChartAreas[0].CursorY.Interval = yZoomInterval;
375    }
376
377    #region Event Handlers
378    #region Content Event Handlers
379    private void Content_VisualPropertiesChanged(object sender, EventArgs e)
380    {
381      if (InvokeRequired)
382        Invoke(new EventHandler(Content_VisualPropertiesChanged), sender, e);
383      else
384      {
385        ConfigureChartArea(chart.ChartAreas[0]);
386        RecalculateAxesScale(chart.ChartAreas[0]); // axes min/max could have changed
387      }
388    }
389    #endregion
390    #region Rows Event Handlers
391
392    private void SelectedRows_CollectionReset(object sender, CollectionItemsChangedEventArgs<DataRow> e) {
393      if (InvokeRequired)
394        Invoke(new CollectionItemsChangedEventHandler<DataRow>(Rows_ItemsReplaced), sender, e);
395      else {
396        RemoveSelectedDataRows(e.OldItems);
397        AddSelectedDataRows(e.Items);
398      }
399    }
400
401    private void SelectedRows_ItemsReplaced(object sender, CollectionItemsChangedEventArgs<DataRow> e) {
402      if (InvokeRequired)
403        Invoke(new CollectionItemsChangedEventHandler<DataRow>(Rows_ItemsReplaced), sender, e);
404      else {
405        RemoveSelectedDataRows(e.OldItems);
406        AddSelectedDataRows(e.Items);
407      }
408    }
409
410    private void SelectedRows_ItemsRemoved(object sender, CollectionItemsChangedEventArgs<DataRow> e) {
411      if (InvokeRequired)
412        Invoke(new CollectionItemsChangedEventHandler<DataRow>(Rows_ItemsRemoved), sender, e);
413      else {
414        RemoveSelectedDataRows(e.Items);
415      }
416    }
417
418    private void SelectedRows_ItemsAdded(object sender, CollectionItemsChangedEventArgs<DataRow> e) {
419      if (InvokeRequired)
420        Invoke(new CollectionItemsChangedEventHandler<DataRow>(Rows_ItemsAdded), sender, e);
421      else {
422        AddSelectedDataRows(e.Items);
423      }
424    }
425
426
427    private void Rows_ItemsAdded(object sender, CollectionItemsChangedEventArgs<DataRow> e) {
428      if (InvokeRequired)
429        Invoke(new CollectionItemsChangedEventHandler<DataRow>(Rows_ItemsAdded), sender, e);
430      else
431      {
432        AddDataRows(e.Items);
433      }
434    }
435    private void Rows_ItemsRemoved(object sender, CollectionItemsChangedEventArgs<DataRow> e)
436    {
437      if (InvokeRequired)
438        Invoke(new CollectionItemsChangedEventHandler<DataRow>(Rows_ItemsRemoved), sender, e);
439      else
440      {
441        RemoveDataRows(e.Items);
442      }
443    }
444    private void Rows_ItemsReplaced(object sender, CollectionItemsChangedEventArgs<DataRow> e)
445    {
446      if (InvokeRequired)
447        Invoke(new CollectionItemsChangedEventHandler<DataRow>(Rows_ItemsReplaced), sender, e);
448      else
449      {
450        RemoveDataRows(e.OldItems);
451        AddDataRows(e.Items);
452      }
453    }
454    private void Rows_CollectionReset(object sender, CollectionItemsChangedEventArgs<DataRow> e)
455    {
456      if (InvokeRequired)
457        Invoke(new CollectionItemsChangedEventHandler<DataRow>(Rows_CollectionReset), sender, e);
458      else
459      {
460        RemoveDataRows(e.OldItems);
461        AddDataRows(e.Items);
462      }
463    }
464    #endregion
465    #region Row Event Handlers
466    private void Row_VisualPropertiesChanged(object sender, EventArgs e)
467    {
468      if (InvokeRequired)
469        Invoke(new EventHandler(Row_VisualPropertiesChanged), sender, e);
470      else
471      {
472        DataRow row = (DataRow)sender;
473        Series series = chart.Series[row.Name];
474        series.Points.Clear();
475        ConfigureSeries(series, row);
476        FillSeriesWithRowValues(series, row);
477        RecalculateAxesScale(chart.ChartAreas[0]);
478      }
479    }
480    private void Row_NameChanged(object sender, EventArgs e)
481    {
482      if (InvokeRequired)
483        Invoke(new EventHandler(Row_NameChanged), sender, e);
484      else
485      {
486        DataRow row = (DataRow)sender;
487        chart.Series[row.Name].Name = row.Name;
488      }
489    }
490    #endregion
491    #region Values Event Handlers
492    private void Values_ItemsAdded(object sender, CollectionItemsChangedEventArgs<IndexedItem<double>> e)
493    {
494      if (InvokeRequired)
495        Invoke(new CollectionItemsChangedEventHandler<IndexedItem<double>>(Values_ItemsAdded), sender, e);
496      else
497      {
498        DataRow row = null;
499        valuesRowsTable.TryGetValue((IObservableList<double>)sender, out row);
500        if (row != null)
501        {
502          Series rowSeries = chart.Series[row.Name];
503          if (!invisibleSeries.Contains(rowSeries))
504          {
505            rowSeries.Points.Clear();
506            FillSeriesWithRowValues(rowSeries, row);
507            RecalculateAxesScale(chart.ChartAreas[0]);
508            UpdateYCursorInterval();
509          }
510        }
511      }
512    }
513    private void Values_ItemsRemoved(object sender, CollectionItemsChangedEventArgs<IndexedItem<double>> e)
514    {
515      if (InvokeRequired)
516        Invoke(new CollectionItemsChangedEventHandler<IndexedItem<double>>(Values_ItemsRemoved), sender, e);
517      else
518      {
519        DataRow row = null;
520        valuesRowsTable.TryGetValue((IObservableList<double>)sender, out row);
521        if (row != null)
522        {
523          Series rowSeries = chart.Series[row.Name];
524          if (!invisibleSeries.Contains(rowSeries))
525          {
526            rowSeries.Points.Clear();
527            FillSeriesWithRowValues(rowSeries, row);
528            RecalculateAxesScale(chart.ChartAreas[0]);
529            UpdateYCursorInterval();
530          }
531        }
532      }
533    }
534    private void Values_ItemsReplaced(object sender, CollectionItemsChangedEventArgs<IndexedItem<double>> e)
535    {
536      if (InvokeRequired)
537        Invoke(new CollectionItemsChangedEventHandler<IndexedItem<double>>(Values_ItemsReplaced), sender, e);
538      else
539      {
540        DataRow row = null;
541        valuesRowsTable.TryGetValue((IObservableList<double>)sender, out row);
542        if (row != null)
543        {
544          Series rowSeries = chart.Series[row.Name];
545          if (!invisibleSeries.Contains(rowSeries))
546          {
547            if (row.VisualProperties.ChartType == DataRowVisualProperties.DataRowChartType.Histogram)
548            {
549              rowSeries.Points.Clear();
550              FillSeriesWithRowValues(rowSeries, row);
551            }
552            else
553            {
554              foreach (IndexedItem<double> item in e.Items)
555              {
556                if (IsInvalidValue(item.Value))
557                  rowSeries.Points[item.Index].IsEmpty = true;
558                else
559                {
560                  rowSeries.Points[item.Index].YValues = new double[] { item.Value };
561                  rowSeries.Points[item.Index].IsEmpty = false;
562                }
563              }
564            }
565            RecalculateAxesScale(chart.ChartAreas[0]);
566            UpdateYCursorInterval();
567          }
568        }
569      }
570    }
571    private void Values_ItemsMoved(object sender, CollectionItemsChangedEventArgs<IndexedItem<double>> e)
572    {
573      if (InvokeRequired)
574        Invoke(new CollectionItemsChangedEventHandler<IndexedItem<double>>(Values_ItemsMoved), sender, e);
575      else
576      {
577        DataRow row = null;
578        valuesRowsTable.TryGetValue((IObservableList<double>)sender, out row);
579        if (row != null)
580        {
581          Series rowSeries = chart.Series[row.Name];
582          if (!invisibleSeries.Contains(rowSeries))
583          {
584            rowSeries.Points.Clear();
585            FillSeriesWithRowValues(rowSeries, row);
586            RecalculateAxesScale(chart.ChartAreas[0]);
587            UpdateYCursorInterval();
588          }
589        }
590      }
591    }
592
593    private void Values_CollectionReset(object sender, CollectionItemsChangedEventArgs<IndexedItem<double>> e)
594    {
595      if (InvokeRequired)
596        Invoke(new CollectionItemsChangedEventHandler<IndexedItem<double>>(Values_CollectionReset), sender, e);
597      else
598      {
599        DataRow row = null;
600        valuesRowsTable.TryGetValue((IObservableList<double>)sender, out row);
601        if (row != null)
602        {
603          Series rowSeries = chart.Series[row.Name];
604          if (!invisibleSeries.Contains(rowSeries))
605          {
606            rowSeries.Points.Clear();
607            FillSeriesWithRowValues(rowSeries, row);
608            RecalculateAxesScale(chart.ChartAreas[0]);
609            UpdateYCursorInterval();
610          }
611        }
612      }
613    }
614    #endregion
615    #endregion
616
617    #region Chart Event Handlers
618    private void chart_MouseDown(object sender, MouseEventArgs e)
619    {
620      HitTestResult result = chart.HitTest(e.X, e.Y);
621      if (result.ChartElementType == ChartElementType.LegendItem)
622      {
623        ToggleSeriesVisible(result.Series);
624      }
625    }
626    private void chart_MouseMove(object sender, MouseEventArgs e)
627    {
628      HitTestResult result = chart.HitTest(e.X, e.Y);
629      if (result.ChartElementType == ChartElementType.LegendItem)
630        this.Cursor = Cursors.Hand;
631      else
632        this.Cursor = Cursors.Default;
633    }
634    private void chart_CustomizeLegend(object sender, CustomizeLegendEventArgs e)
635    {
636      foreach (LegendItem legendItem in e.LegendItems)
637      {
638        var series = chart.Series[legendItem.SeriesName];
639        if (series != null)
640        {
641          bool seriesIsInvisible = invisibleSeries.Contains(series);
642          foreach (LegendCell cell in legendItem.Cells)
643          {
644            cell.ForeColor = seriesIsInvisible ? Color.Gray : Color.Black;
645          }
646        }
647      }
648    }
649    #endregion
650
651    private void ToggleSeriesVisible(Series series)
652    {
653
654      if (!Content.Rows.Any(x => x.Name == series.Name))
655        return;
656
657      if (!invisibleSeries.Contains(series))
658      {
659        series.Points.Clear();
660        invisibleSeries.Add(series);
661      }
662      else
663      {
664        invisibleSeries.Remove(series);
665        if (Content != null)
666        {
667
668          var row = (from r in Content.Rows
669                     where r.Name == series.Name
670                     select r).Single();
671          FillSeriesWithRowValues(series, row);
672          this.chart.Legends[series.Legend].ForeColor = Color.Black;
673          RecalculateAxesScale(chart.ChartAreas[0]);
674          UpdateYCursorInterval();
675        }
676      }
677    }
678
679    private void FillSeriesWithRowValues(Series series, DataRow row)
680    {
681      switch (row.VisualProperties.ChartType)
682      {
683        case DataRowVisualProperties.DataRowChartType.Histogram:
684          CalculateHistogram(series, row);
685          break;
686        default:
687          {
688            bool yLogarithmic = series.YAxisType == AxisType.Primary
689                                  ? Content.VisualProperties.YAxisLogScale
690                                  : Content.VisualProperties.SecondYAxisLogScale;
691            bool xLogarithmic = series.XAxisType == AxisType.Primary
692                                  ? Content.VisualProperties.XAxisLogScale
693                                  : Content.VisualProperties.SecondXAxisLogScale;
694            for (int i = 0; i < row.Values.Count; i++)
695            {
696              var value = row.Values[i];
697              var point = new DataPoint();
698              point.XValue = row.VisualProperties.StartIndexZero && !xLogarithmic ? i : i + 1;
699              if (IsInvalidValue(value) || (yLogarithmic && value <= 0))
700                point.IsEmpty = true;
701              else
702                point.YValues = new double[] { value };
703              series.Points.Add(point);
704            }
705          }
706          break;
707      }
708    }
709
710    // get minimum ignores nan values
711    private double GetMinimum(IEnumerable<double> values) {
712      double min = Double.MaxValue;
713
714      foreach (double value in values)
715      {
716        if (!Double.IsNaN(value) && value < min)
717          min = value;
718      }
719      return min;
720    }
721
722    //get maximium ignores nan values
723    private double GetMaximum(IEnumerable<double> values)
724    {
725      double max = Double.MinValue;
726
727      foreach (double value in values)
728      {
729        if (!Double.IsNaN(value) && value > max)
730          max = value;
731      }
732      return max;
733    }
734
735    protected virtual void CalculateHistogram(Series series, DataRow row)
736    {
737      if (classification != null)
738      {
739
740        var qry = row.Values.Select((i, index) => new { i, j = classification[index] })
741                           .GroupBy((x) => x.j).ToDictionary(x => x.Key, x => x.Select(v => v.i).ToList());
742        chart.Titles.Add(row.Name);
743        foreach (KeyValuePair<double, List<double>> entry in qry)
744        {
745          var s = new Series(row.Name + entry.Key);
746
747          ConfigureSeries(s, row);
748          AddPointsToHistogramSeries(s, row, entry.Value);
749
750          s.LegendText = entry.Key.ToString();
751
752          chart.Series.Add(s);
753        }
754      }
755      else
756      {
757        series.Points.Clear();
758        ConfigureSeries(series, row);
759        AddPointsToHistogramSeries(series, row, null);
760      }
761    }
762
763    private void AddPointsToHistogramSeries(Series series, DataRow row, List<double> values)
764    {
765
766      if (!row.Values.Any()) return;
767      int bins = row.VisualProperties.Bins;
768
769      double minValue = GetMinimum(row.Values);
770      double maxValue = GetMaximum(row.Values);
771      double intervalWidth = (maxValue - minValue) / bins;
772      if (intervalWidth < 0) return;
773      if (intervalWidth == 0)
774      {
775        series.Points.AddXY(minValue, row.Values.Count);
776        return;
777      }
778
779
780      if (!row.VisualProperties.ExactBins) {
781        intervalWidth = HumanRoundRange(intervalWidth);
782        minValue = Math.Floor(minValue / intervalWidth) * intervalWidth;
783        maxValue = Math.Ceiling(maxValue / intervalWidth) * intervalWidth;
784      }
785
786      double intervalCenter = intervalWidth / 2;
787
788      double min = 0.0, max = 0.0;
789      if (!Double.IsNaN(Content.VisualProperties.XAxisMinimumFixedValue) && !Content.VisualProperties.XAxisMinimumAuto)
790        min = Content.VisualProperties.XAxisMinimumFixedValue;
791      else min = minValue;
792      if (!Double.IsNaN(Content.VisualProperties.XAxisMaximumFixedValue) && !Content.VisualProperties.XAxisMaximumAuto)
793        max = Content.VisualProperties.XAxisMaximumFixedValue;
794      else max = maxValue + intervalWidth;
795
796      double axisInterval = intervalWidth / row.VisualProperties.ScaleFactor;
797
798      var area = chart.ChartAreas[0];
799      area.AxisX.Interval = axisInterval;
800
801      series.SetCustomProperty("PointWidth", "1"); // 0.8 is the default value
802
803      // get the range or intervals which define the grouping of the frequency values
804      var doubleRange = DoubleRange(min, max, intervalWidth).Skip(1).ToList();
805
806
807      if (values == null) {
808        values = row.Values.ToList(); ;
809      }
810
811      // aggregate the row values by unique key and frequency value
812      var valueFrequencies = (from v in values
813                              where !IsInvalidValue(v)
814                              orderby v
815                              group v by v into g
816                              select new Tuple<double, double>(g.First(), g.Count())).ToList();
817
818      //  shift the chart to the left so the bars are placed on the intervals
819      if (classification != null || valueFrequencies.First().Item1 < doubleRange.First()) {
820        series.Points.Add(new DataPoint(min - intervalWidth, 0));
821        series.Points.Add(new DataPoint(max + intervalWidth, 0));
822      }
823
824      // add data points
825      int j = 0;
826      foreach (var d in doubleRange)
827      {
828        double sum = 0.0;
829        // sum the frequency values that fall within the same interval
830        while (j < valueFrequencies.Count && valueFrequencies[j].Item1 < d)
831        {
832          sum += valueFrequencies[j].Item2;
833          ++j;
834        }
835        string xAxisTitle = string.IsNullOrEmpty(Content.VisualProperties.XAxisTitle)
836                              ? "X"
837                              : Content.VisualProperties.XAxisTitle;
838        string yAxisTitle = string.IsNullOrEmpty(Content.VisualProperties.YAxisTitle)
839                              ? "Y"
840                              : Content.VisualProperties.YAxisTitle;
841        series.Points.Add(new DataPoint(d - intervalCenter, sum)
842        {
843          ToolTip =
844            xAxisTitle + ": [" + (d - intervalWidth) + "-" + d + ")" + Environment.NewLine +
845            yAxisTitle + ": " + sum
846        });
847      }
848    }
849
850    #region Helpers
851    public static IEnumerable<double> DoubleRange(double min, double max, double step)
852    {
853      double i;
854      for (i = min; i <= max; i += step)
855        yield return i;
856
857      if (i != max + step)
858        yield return i;
859    }
860
861    protected void RemoveCustomPropertyIfExists(Series series, string property)
862    {
863      if (series.IsCustomPropertySet(property)) series.DeleteCustomProperty(property);
864    }
865
866    private double HumanRoundRange(double range)
867    {
868      double base10 = Math.Pow(10.0, Math.Floor(Math.Log10(range)));
869      double rounding = range / base10;
870      if (rounding <= 1.5) rounding = 1;
871      else if (rounding <= 2.25) rounding = 2;
872      else if (rounding <= 3.75) rounding = 2.5;
873      else if (rounding <= 7.5) rounding = 5;
874      else rounding = 10;
875      return rounding * base10;
876    }
877
878    private double HumanRoundMax(double max)
879    {
880      double base10;
881      if (max > 0) base10 = Math.Pow(10.0, Math.Floor(Math.Log10(max)));
882      else base10 = Math.Pow(10.0, Math.Ceiling(Math.Log10(-max)));
883      double rounding = (max > 0) ? base10 : -base10;
884      while (rounding < max) rounding += base10;
885      return rounding;
886    }
887
888    private ChartDashStyle ConvertLineStyle(DataRowVisualProperties.DataRowLineStyle dataRowLineStyle)
889    {
890      switch (dataRowLineStyle)
891      {
892        case DataRowVisualProperties.DataRowLineStyle.Dash:
893          return ChartDashStyle.Dash;
894        case DataRowVisualProperties.DataRowLineStyle.DashDot:
895          return ChartDashStyle.DashDot;
896        case DataRowVisualProperties.DataRowLineStyle.DashDotDot:
897          return ChartDashStyle.DashDotDot;
898        case DataRowVisualProperties.DataRowLineStyle.Dot:
899          return ChartDashStyle.Dot;
900        case DataRowVisualProperties.DataRowLineStyle.NotSet:
901          return ChartDashStyle.NotSet;
902        case DataRowVisualProperties.DataRowLineStyle.Solid:
903          return ChartDashStyle.Solid;
904        default:
905          return ChartDashStyle.NotSet;
906      }
907    }
908
909    protected static bool IsInvalidValue(double x)
910    {
911      return double.IsNaN(x) || x < (double)decimal.MinValue || x > (double)decimal.MaxValue;
912    }
913    #endregion
914  }
915}
Note: See TracBrowser for help on using the repository browser.