Free cookie consent management tool by TermsFeed Policy Generator

source: branches/DataPreprocessing/HeuristicLab.DataPreprocessing.Views/3.3/PreprocessingScatterPlotView.cs @ 10882

Last change on this file since 10882 was 10882, checked in by aesterer, 10 years ago

Added scatter plot view

File size: 20.8 KB
Line 
1#region License Information
2/* HeuristicLab
3 * Copyright (C) 2002-2013 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
4 *
5 * This file is part of HeuristicLab.
6 *
7 * HeuristicLab is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * HeuristicLab is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with HeuristicLab. If not, see <http://www.gnu.org/licenses/>.
19 */
20#endregion
21
22using System;
23using System.Collections.Generic;
24using System.Drawing;
25using System.Linq;
26using System.Windows.Forms;
27using System.Windows.Forms.DataVisualization.Charting;
28using HeuristicLab.Analysis;
29using HeuristicLab.Analysis.Views;
30using HeuristicLab.Collections;
31using HeuristicLab.Common;
32using HeuristicLab.Core.Views;
33using HeuristicLab.MainForm;
34
35namespace HeuristicLab.DataPreprocessing.Views {
36  [View("Preprocessing ScatterPlot View")]
37  [Content(typeof(ScatterPlot), false)]
38  public partial class PreprocessingScatterPlotView : ItemView, IConfigureableView {
39    protected List<Series> invisibleSeries;
40    protected Dictionary<IObservableList<Point2D<double>>, ScatterPlotDataRow> pointsRowsTable;
41
42    public new ScatterPlot Content {
43      get { return (ScatterPlot)base.Content; }
44      set { base.Content = value; }
45    }
46
47    public PreprocessingScatterPlotView() {
48      InitializeComponent();
49      pointsRowsTable = new Dictionary<IObservableList<Point2D<double>>, ScatterPlotDataRow>();
50      invisibleSeries = new List<Series>();
51      chart.CustomizeAllChartAreas();
52      chart.ChartAreas[0].CursorX.Interval = 1;
53    }
54
55    #region Event Handler Registration
56    protected override void DeregisterContentEvents() {
57      foreach (ScatterPlotDataRow row in Content.Rows)
58        DeregisterScatterPlotDataRowEvents(row);
59      Content.VisualPropertiesChanged -= new EventHandler(Content_VisualPropertiesChanged);
60      Content.Rows.ItemsAdded -= new CollectionItemsChangedEventHandler<ScatterPlotDataRow>(Rows_ItemsAdded);
61      Content.Rows.ItemsRemoved -= new CollectionItemsChangedEventHandler<ScatterPlotDataRow>(Rows_ItemsRemoved);
62      Content.Rows.ItemsReplaced -= new CollectionItemsChangedEventHandler<ScatterPlotDataRow>(Rows_ItemsReplaced);
63      Content.Rows.CollectionReset -= new CollectionItemsChangedEventHandler<ScatterPlotDataRow>(Rows_CollectionReset);
64      base.DeregisterContentEvents();
65    }
66    protected override void RegisterContentEvents() {
67      base.RegisterContentEvents();
68      Content.VisualPropertiesChanged += new EventHandler(Content_VisualPropertiesChanged);
69      Content.Rows.ItemsAdded += new CollectionItemsChangedEventHandler<ScatterPlotDataRow>(Rows_ItemsAdded);
70      Content.Rows.ItemsRemoved += new CollectionItemsChangedEventHandler<ScatterPlotDataRow>(Rows_ItemsRemoved);
71      Content.Rows.ItemsReplaced += new CollectionItemsChangedEventHandler<ScatterPlotDataRow>(Rows_ItemsReplaced);
72      Content.Rows.CollectionReset += new CollectionItemsChangedEventHandler<ScatterPlotDataRow>(Rows_CollectionReset);
73    }
74
75    protected virtual void RegisterScatterPlotDataRowEvents(ScatterPlotDataRow row) {
76      row.NameChanged += new EventHandler(Row_NameChanged);
77      row.VisualPropertiesChanged += new EventHandler(Row_VisualPropertiesChanged);
78      pointsRowsTable.Add(row.Points, row);
79      row.Points.ItemsAdded += new CollectionItemsChangedEventHandler<IndexedItem<Point2D<double>>>(Points_ItemsAdded);
80      row.Points.ItemsRemoved += new CollectionItemsChangedEventHandler<IndexedItem<Point2D<double>>>(Points_ItemsRemoved);
81      row.Points.ItemsReplaced += new CollectionItemsChangedEventHandler<IndexedItem<Point2D<double>>>(Points_ItemsReplaced);
82      row.Points.CollectionReset += new CollectionItemsChangedEventHandler<IndexedItem<Point2D<double>>>(Points_CollectionReset);
83    }
84    protected virtual void DeregisterScatterPlotDataRowEvents(ScatterPlotDataRow row) {
85      row.Points.ItemsAdded -= new CollectionItemsChangedEventHandler<IndexedItem<Point2D<double>>>(Points_ItemsAdded);
86      row.Points.ItemsRemoved -= new CollectionItemsChangedEventHandler<IndexedItem<Point2D<double>>>(Points_ItemsRemoved);
87      row.Points.ItemsReplaced -= new CollectionItemsChangedEventHandler<IndexedItem<Point2D<double>>>(Points_ItemsReplaced);
88      row.Points.CollectionReset -= new CollectionItemsChangedEventHandler<IndexedItem<Point2D<double>>>(Points_CollectionReset);
89      pointsRowsTable.Remove(row.Points);
90      row.VisualPropertiesChanged -= new EventHandler(Row_VisualPropertiesChanged);
91      row.NameChanged -= new EventHandler(Row_NameChanged);
92    }
93    #endregion
94
95    protected override void OnContentChanged() {
96      base.OnContentChanged();
97      invisibleSeries.Clear();
98      chart.Titles[0].Text = string.Empty;
99      chart.ChartAreas[0].AxisX.Title = string.Empty;
100      chart.ChartAreas[0].AxisY.Title = string.Empty;
101      chart.Series.Clear();
102      if (Content != null) {
103        chart.Titles[0].Text = Content.Name;
104        AddScatterPlotDataRows(Content.Rows);
105        ConfigureChartArea(chart.ChartAreas[0]);
106        RecalculateAxesScale(chart.ChartAreas[0]);
107      }
108    }
109
110    protected override void SetEnabledStateOfControls() {
111      base.SetEnabledStateOfControls();
112      chart.Enabled = Content != null;
113    }
114
115    public void ShowConfiguration() {
116      if (Content != null) {
117        using (ScatterPlotVisualPropertiesDialog dialog = new ScatterPlotVisualPropertiesDialog(Content)) {
118          dialog.ShowDialog(this);
119        }
120      } else MessageBox.Show("Nothing to configure.");
121    }
122
123    protected virtual void AddScatterPlotDataRows(IEnumerable<ScatterPlotDataRow> rows) {
124      foreach (var row in rows) {
125        RegisterScatterPlotDataRowEvents(row);
126        Series series = new Series(row.Name);
127        if (row.VisualProperties.DisplayName.Trim() != String.Empty) series.LegendText = row.VisualProperties.DisplayName;
128        else series.LegendText = row.Name;
129        ConfigureSeries(series, row);
130        FillSeriesWithRowValues(series, row);
131        chart.Series.Add(series);
132      }
133      ConfigureChartArea(chart.ChartAreas[0]);
134      RecalculateAxesScale(chart.ChartAreas[0]);
135      UpdateYCursorInterval();
136    }
137
138    protected virtual void RemoveScatterPlotDataRows(IEnumerable<ScatterPlotDataRow> rows) {
139      foreach (var row in rows) {
140        DeregisterScatterPlotDataRowEvents(row);
141        Series series = chart.Series[row.Name];
142        chart.Series.Remove(series);
143        if (invisibleSeries.Contains(series))
144          invisibleSeries.Remove(series);
145      }
146      RecalculateAxesScale(chart.ChartAreas[0]);
147    }
148
149    private void ConfigureSeries(Series series, ScatterPlotDataRow row) {
150      series.BorderWidth = 1;
151      series.BorderDashStyle = ChartDashStyle.Solid;
152      series.BorderColor = Color.Empty;
153
154      if (row.VisualProperties.Color != Color.Empty)
155        series.Color = row.VisualProperties.Color;
156      else series.Color = Color.Empty;
157      series.IsVisibleInLegend = row.VisualProperties.IsVisibleInLegend;
158      series.ChartType = SeriesChartType.FastPoint;
159      series.MarkerSize = row.VisualProperties.PointSize;
160      series.MarkerStyle = ConvertPointStyle(row.VisualProperties.PointStyle);
161      series.XAxisType = AxisType.Primary;
162      series.YAxisType = AxisType.Primary;
163
164      if (row.VisualProperties.DisplayName.Trim() != String.Empty) series.LegendText = row.VisualProperties.DisplayName;
165      else series.LegendText = row.Name;
166
167      string xAxisTitle = string.IsNullOrEmpty(Content.VisualProperties.XAxisTitle)
168                      ? "X"
169                      : Content.VisualProperties.XAxisTitle;
170      string yAxisTitle = string.IsNullOrEmpty(Content.VisualProperties.YAxisTitle)
171                            ? "Y"
172                            : Content.VisualProperties.YAxisTitle;
173      series.ToolTip =
174        series.LegendText + Environment.NewLine +
175        xAxisTitle + " = " + "#VALX," + Environment.NewLine +
176        yAxisTitle + " = " + "#VAL";
177    }
178
179    private void ConfigureChartArea(ChartArea area) {
180      if (Content.VisualProperties.TitleFont != null) chart.Titles[0].Font = Content.VisualProperties.TitleFont;
181      if (!Content.VisualProperties.TitleColor.IsEmpty) chart.Titles[0].ForeColor = Content.VisualProperties.TitleColor;
182      chart.Titles[0].Text = Content.VisualProperties.Title;
183
184      if (Content.VisualProperties.AxisTitleFont != null) area.AxisX.TitleFont = Content.VisualProperties.AxisTitleFont;
185      if (!Content.VisualProperties.AxisTitleColor.IsEmpty) area.AxisX.TitleForeColor = Content.VisualProperties.AxisTitleColor;
186      area.AxisX.Title = Content.VisualProperties.XAxisTitle;
187      area.AxisX.MajorGrid.Enabled = Content.VisualProperties.XAxisGrid;
188
189      if (Content.VisualProperties.AxisTitleFont != null) area.AxisY.TitleFont = Content.VisualProperties.AxisTitleFont;
190      if (!Content.VisualProperties.AxisTitleColor.IsEmpty) area.AxisY.TitleForeColor = Content.VisualProperties.AxisTitleColor;
191      area.AxisY.Title = Content.VisualProperties.YAxisTitle;
192      area.AxisY.MajorGrid.Enabled = Content.VisualProperties.YAxisGrid;
193    }
194
195    private void RecalculateAxesScale(ChartArea area) {
196      // Reset the axes bounds so that RecalculateAxesScale() will assign new bounds
197      foreach (Axis a in area.Axes) {
198        a.Minimum = double.NaN;
199        a.Maximum = double.NaN;
200      }
201      area.RecalculateAxesScale();
202      area.AxisX.IsMarginVisible = false;
203
204      if (!Content.VisualProperties.XAxisMinimumAuto && !double.IsNaN(Content.VisualProperties.XAxisMinimumFixedValue)) area.AxisX.Minimum = Content.VisualProperties.XAxisMinimumFixedValue;
205      if (!Content.VisualProperties.XAxisMaximumAuto && !double.IsNaN(Content.VisualProperties.XAxisMaximumFixedValue)) area.AxisX.Maximum = Content.VisualProperties.XAxisMaximumFixedValue;
206      if (!Content.VisualProperties.YAxisMinimumAuto && !double.IsNaN(Content.VisualProperties.YAxisMinimumFixedValue)) area.AxisY.Minimum = Content.VisualProperties.YAxisMinimumFixedValue;
207      if (!Content.VisualProperties.YAxisMaximumAuto && !double.IsNaN(Content.VisualProperties.YAxisMaximumFixedValue)) area.AxisY.Maximum = Content.VisualProperties.YAxisMaximumFixedValue;
208      if (area.AxisX.Minimum >= area.AxisX.Maximum) area.AxisX.Maximum = area.AxisX.Minimum + 1;
209      if (area.AxisY.Minimum >= area.AxisY.Maximum) area.AxisY.Maximum = area.AxisY.Minimum + 1;
210    }
211
212    protected virtual void UpdateYCursorInterval() {
213      double interestingValuesRange = (
214        from series in chart.Series
215        where series.Enabled
216        let values = (from point in series.Points
217                      where !point.IsEmpty
218                      select point.YValues[0]).DefaultIfEmpty(1.0)
219        let range = values.Max() - values.Min()
220        where range > 0.0
221        select range
222        ).DefaultIfEmpty(1.0).Min();
223
224      double digits = (int)Math.Log10(interestingValuesRange) - 3;
225      double yZoomInterval = Math.Pow(10, digits);
226      this.chart.ChartAreas[0].CursorY.Interval = yZoomInterval;
227    }
228
229    #region Event Handlers
230    #region Content Event Handlers
231
232    private void Content_VisualPropertiesChanged(object sender, EventArgs e) {
233      if (InvokeRequired)
234        Invoke(new EventHandler(Content_VisualPropertiesChanged), sender, e);
235      else {
236        ConfigureChartArea(chart.ChartAreas[0]);
237        RecalculateAxesScale(chart.ChartAreas[0]); // axes min/max could have changed
238      }
239    }
240    #endregion
241    #region Rows Event Handlers
242    private void Rows_ItemsAdded(object sender, CollectionItemsChangedEventArgs<ScatterPlotDataRow> e) {
243      if (InvokeRequired)
244        Invoke(new CollectionItemsChangedEventHandler<ScatterPlotDataRow>(Rows_ItemsAdded), sender, e);
245      else {
246        AddScatterPlotDataRows(e.Items);
247      }
248    }
249    private void Rows_ItemsRemoved(object sender, CollectionItemsChangedEventArgs<ScatterPlotDataRow> e) {
250      if (InvokeRequired)
251        Invoke(new CollectionItemsChangedEventHandler<ScatterPlotDataRow>(Rows_ItemsRemoved), sender, e);
252      else {
253        RemoveScatterPlotDataRows(e.Items);
254      }
255    }
256    private void Rows_ItemsReplaced(object sender, CollectionItemsChangedEventArgs<ScatterPlotDataRow> e) {
257      if (InvokeRequired)
258        Invoke(new CollectionItemsChangedEventHandler<ScatterPlotDataRow>(Rows_ItemsReplaced), sender, e);
259      else {
260        RemoveScatterPlotDataRows(e.OldItems);
261        AddScatterPlotDataRows(e.Items);
262      }
263    }
264    private void Rows_CollectionReset(object sender, CollectionItemsChangedEventArgs<ScatterPlotDataRow> e) {
265      if (InvokeRequired)
266        Invoke(new CollectionItemsChangedEventHandler<ScatterPlotDataRow>(Rows_CollectionReset), sender, e);
267      else {
268        RemoveScatterPlotDataRows(e.OldItems);
269        AddScatterPlotDataRows(e.Items);
270      }
271    }
272    #endregion
273    #region Row Event Handlers
274    private void Row_VisualPropertiesChanged(object sender, EventArgs e) {
275      if (InvokeRequired)
276        Invoke(new EventHandler(Row_VisualPropertiesChanged), sender, e);
277      else {
278        ScatterPlotDataRow row = (ScatterPlotDataRow)sender;
279        Series series = chart.Series[row.Name];
280        series.Points.Clear();
281        ConfigureSeries(series, row);
282        FillSeriesWithRowValues(series, row);
283        RecalculateAxesScale(chart.ChartAreas[0]);
284      }
285    }
286    private void Row_NameChanged(object sender, EventArgs e) {
287      if (InvokeRequired)
288        Invoke(new EventHandler(Row_NameChanged), sender, e);
289      else {
290        ScatterPlotDataRow row = (ScatterPlotDataRow)sender;
291        chart.Series[row.Name].Name = row.Name;
292      }
293    }
294    #endregion
295    #region Points Event Handlers
296    private void Points_ItemsAdded(object sender, CollectionItemsChangedEventArgs<IndexedItem<Point2D<double>>> e) {
297      if (InvokeRequired)
298        Invoke(new CollectionItemsChangedEventHandler<IndexedItem<Point2D<double>>>(Points_ItemsAdded), sender, e);
299      else {
300        ScatterPlotDataRow row = null;
301        pointsRowsTable.TryGetValue((IObservableList<Point2D<double>>)sender, out row);
302        if (row != null) {
303          Series rowSeries = chart.Series[row.Name];
304          if (!invisibleSeries.Contains(rowSeries)) {
305            rowSeries.Points.Clear();
306            FillSeriesWithRowValues(rowSeries, row);
307            RecalculateAxesScale(chart.ChartAreas[0]);
308            UpdateYCursorInterval();
309          }
310        }
311      }
312    }
313    private void Points_ItemsRemoved(object sender, CollectionItemsChangedEventArgs<IndexedItem<Point2D<double>>> e) {
314      if (InvokeRequired)
315        Invoke(new CollectionItemsChangedEventHandler<IndexedItem<Point2D<double>>>(Points_ItemsRemoved), sender, e);
316      else {
317        ScatterPlotDataRow row = null;
318        pointsRowsTable.TryGetValue((IObservableList<Point2D<double>>)sender, out row);
319        if (row != null) {
320          Series rowSeries = chart.Series[row.Name];
321          if (!invisibleSeries.Contains(rowSeries)) {
322            rowSeries.Points.Clear();
323            FillSeriesWithRowValues(rowSeries, row);
324            RecalculateAxesScale(chart.ChartAreas[0]);
325            UpdateYCursorInterval();
326          }
327        }
328      }
329    }
330    private void Points_ItemsReplaced(object sender, CollectionItemsChangedEventArgs<IndexedItem<Point2D<double>>> e) {
331      if (InvokeRequired)
332        Invoke(new CollectionItemsChangedEventHandler<IndexedItem<Point2D<double>>>(Points_ItemsReplaced), sender, e);
333      else {
334        ScatterPlotDataRow row = null;
335        pointsRowsTable.TryGetValue((IObservableList<Point2D<double>>)sender, out row);
336        if (row != null) {
337          Series rowSeries = chart.Series[row.Name];
338          if (!invisibleSeries.Contains(rowSeries)) {
339            rowSeries.Points.Clear();
340            FillSeriesWithRowValues(rowSeries, row);
341            RecalculateAxesScale(chart.ChartAreas[0]);
342            UpdateYCursorInterval();
343          }
344        }
345      }
346    }
347    private void Points_CollectionReset(object sender, CollectionItemsChangedEventArgs<IndexedItem<Point2D<double>>> e) {
348      if (InvokeRequired)
349        Invoke(new CollectionItemsChangedEventHandler<IndexedItem<Point2D<double>>>(Points_CollectionReset), sender, e);
350      else {
351        ScatterPlotDataRow row = null;
352        pointsRowsTable.TryGetValue((IObservableList<Point2D<double>>)sender, out row);
353        if (row != null) {
354          Series rowSeries = chart.Series[row.Name];
355          if (!invisibleSeries.Contains(rowSeries)) {
356            rowSeries.Points.Clear();
357            FillSeriesWithRowValues(rowSeries, row);
358            RecalculateAxesScale(chart.ChartAreas[0]);
359            UpdateYCursorInterval();
360          }
361        }
362      }
363    }
364    #endregion
365    #endregion
366
367    #region Chart Event Handlers
368    private void chart_MouseDown(object sender, MouseEventArgs e) {
369      HitTestResult result = chart.HitTest(e.X, e.Y);
370      if (result.ChartElementType == ChartElementType.LegendItem) {
371        ToggleSeriesVisible(result.Series);
372      }
373    }
374    private void chart_MouseMove(object sender, MouseEventArgs e) {
375      HitTestResult result = chart.HitTest(e.X, e.Y);
376      if (result.ChartElementType == ChartElementType.LegendItem)
377        this.Cursor = Cursors.Hand;
378      else
379        this.Cursor = Cursors.Default;
380    }
381    private void chart_CustomizeLegend(object sender, CustomizeLegendEventArgs e) {
382      foreach (LegendItem legendItem in e.LegendItems) {
383        var series = chart.Series[legendItem.SeriesName];
384        if (series != null) {
385          bool seriesIsInvisible = invisibleSeries.Contains(series);
386          foreach (LegendCell cell in legendItem.Cells) {
387            cell.ForeColor = seriesIsInvisible ? Color.Gray : Color.Black;
388          }
389        }
390      }
391    }
392    #endregion
393
394    private void ToggleSeriesVisible(Series series) {
395      if (!invisibleSeries.Contains(series)) {
396        series.Points.Clear();
397        invisibleSeries.Add(series);
398      } else {
399        invisibleSeries.Remove(series);
400        if (Content != null) {
401
402          var row = (from r in Content.Rows
403                     where r.Name == series.Name
404                     select r).Single();
405          FillSeriesWithRowValues(series, row);
406          this.chart.Legends[series.Legend].ForeColor = Color.Black;
407          RecalculateAxesScale(chart.ChartAreas[0]);
408          UpdateYCursorInterval();
409        }
410      }
411    }
412
413    private void FillSeriesWithRowValues(Series series, ScatterPlotDataRow row) {
414      for (int i = 0; i < row.Points.Count; i++) {
415        var value = row.Points[i];
416        DataPoint point = new DataPoint();
417        if (IsInvalidValue(value.X) || IsInvalidValue(value.Y))
418          point.IsEmpty = true;
419        else {
420          point.XValue = value.X;
421          point.YValues = new double[] { value.Y };
422        }
423        series.Points.Add(point);
424      }
425    }
426
427    #region Helpers
428    private MarkerStyle ConvertPointStyle(ScatterPlotDataRowVisualProperties.ScatterPlotDataRowPointStyle pointStyle) {
429      switch (pointStyle) {
430        case ScatterPlotDataRowVisualProperties.ScatterPlotDataRowPointStyle.Circle:
431          return MarkerStyle.Circle;
432        case ScatterPlotDataRowVisualProperties.ScatterPlotDataRowPointStyle.Cross:
433          return MarkerStyle.Cross;
434        case ScatterPlotDataRowVisualProperties.ScatterPlotDataRowPointStyle.Diamond:
435          return MarkerStyle.Diamond;
436        case ScatterPlotDataRowVisualProperties.ScatterPlotDataRowPointStyle.Square:
437          return MarkerStyle.Square;
438        case ScatterPlotDataRowVisualProperties.ScatterPlotDataRowPointStyle.Star4:
439          return MarkerStyle.Star4;
440        case ScatterPlotDataRowVisualProperties.ScatterPlotDataRowPointStyle.Star5:
441          return MarkerStyle.Star5;
442        case ScatterPlotDataRowVisualProperties.ScatterPlotDataRowPointStyle.Star6:
443          return MarkerStyle.Star6;
444        case ScatterPlotDataRowVisualProperties.ScatterPlotDataRowPointStyle.Star10:
445          return MarkerStyle.Star10;
446        case ScatterPlotDataRowVisualProperties.ScatterPlotDataRowPointStyle.Triangle:
447          return MarkerStyle.Triangle;
448        default:
449          return MarkerStyle.None;
450      }
451    }
452
453    protected static bool IsInvalidValue(double x) {
454      return double.IsNaN(x) || x < (double)decimal.MinValue || x > (double)decimal.MaxValue;
455    }
456    #endregion
457  }
458}
Note: See TracBrowser for help on using the repository browser.