Free cookie consent management tool by TermsFeed Policy Generator

source: stable/HeuristicLab.DataPreprocessing.Views/3.4/PreprocessingDataTableView.cs @ 15097

Last change on this file since 15097 was 15097, checked in by pfleck, 7 years ago

#2713 #2715 #2765
Merged to stable

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