Free cookie consent management tool by TermsFeed Policy Generator

source: branches/DataPreprocessing/HeuristicLab.DataPreprocessing.Views/3.4/PreprocessingDataTableView.cs @ 10947

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

Style fixes

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