Free cookie consent management tool by TermsFeed Policy Generator

source: branches/HeuristicLab.Problems.Orienteering/HeuristicLab.DataPreprocessing.Views/3.4/PreprocessingDataTableView.cs @ 12694

Last change on this file since 12694 was 12694, checked in by abeham, 9 years ago

#2208: merged trunk changes

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