Free cookie consent management tool by TermsFeed Policy Generator

source: branches/crossvalidation-2434/HeuristicLab.DataPreprocessing.Views/3.4/PreprocessingDataTableView.cs @ 14821

Last change on this file since 14821 was 14029, checked in by gkronber, 8 years ago

#2434: merged trunk changes r12934:14026 from trunk to branch

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