Free cookie consent management tool by TermsFeed Policy Generator

source: trunk/sources/HeuristicLab.Optimization.Views/3.3/RunCollectionBoxPlotView.cs @ 4094

Last change on this file since 4094 was 4094, checked in by mkommend, 13 years ago

implemented first version of BoxPlotView (ticket #970)

File size: 13.5 KB
Line 
1#region License Information
2/* HeuristicLab
3 * Copyright (C) 2002-2010 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.ComponentModel;
25using System.Data;
26using System.Drawing;
27using System.Linq;
28using System.Text;
29using System.Windows.Forms;
30using System.Windows.Forms.DataVisualization.Charting;
31using HeuristicLab.Common;
32using HeuristicLab.Core;
33using HeuristicLab.Data;
34using HeuristicLab.MainForm;
35using HeuristicLab.MainForm.WindowsForms;
36
37namespace HeuristicLab.Optimization.Views {
38  [View("RunCollection BoxPlots")]
39  [Content(typeof(RunCollection), false)]
40  public partial class RunCollectionBoxPlotView : AsynchronousContentView {
41    private enum AxisDimension { Color = 0 }
42    private const string BoxPlotSeriesName = "BoxPlotSeries";
43    private const string BoxPlotChartAreaName = "BoxPlotChartArea";
44
45    private string xAxisValue;
46    private string yAxisValue;
47    private Dictionary<int, Dictionary<object, double>> categoricalMapping;
48    private Dictionary<double, Series> seriesCache;
49
50    public RunCollectionBoxPlotView() {
51      InitializeComponent();
52      this.categoricalMapping = new Dictionary<int, Dictionary<object, double>>();
53      this.seriesCache = new Dictionary<double, Series>();
54      this.chart.ChartAreas[0].Visible = false;
55      this.chart.Series.Clear();
56      this.chart.ChartAreas.Add(BoxPlotChartAreaName);
57    }
58
59    public new RunCollection Content {
60      get { return (RunCollection)base.Content; }
61      set { base.Content = value; }
62    }
63    public IStringConvertibleMatrix Matrix {
64      get { return this.Content; }
65    }
66
67    #region RunCollection and Run events
68    protected override void RegisterContentEvents() {
69      base.RegisterContentEvents();
70      Content.Reset += new EventHandler(Content_Reset);
71      Content.ColumnNamesChanged += new EventHandler(Content_ColumnNamesChanged);
72      Content.ItemsAdded += new HeuristicLab.Collections.CollectionItemsChangedEventHandler<IRun>(Content_ItemsAdded);
73      Content.ItemsRemoved += new HeuristicLab.Collections.CollectionItemsChangedEventHandler<IRun>(Content_ItemsRemoved);
74      Content.CollectionReset += new HeuristicLab.Collections.CollectionItemsChangedEventHandler<IRun>(Content_CollectionReset);
75      RegisterRunEvents(Content);
76    }
77    protected override void DeregisterContentEvents() {
78      base.DeregisterContentEvents();
79      Content.Reset -= new EventHandler(Content_Reset);
80      Content.ColumnNamesChanged -= new EventHandler(Content_ColumnNamesChanged);
81      Content.ItemsAdded -= new HeuristicLab.Collections.CollectionItemsChangedEventHandler<IRun>(Content_ItemsAdded);
82      Content.ItemsRemoved -= new HeuristicLab.Collections.CollectionItemsChangedEventHandler<IRun>(Content_ItemsRemoved);
83      Content.CollectionReset -= new HeuristicLab.Collections.CollectionItemsChangedEventHandler<IRun>(Content_CollectionReset);
84      DeregisterRunEvents(Content);
85    }
86
87    protected virtual void RegisterRunEvents(IEnumerable<IRun> runs) {
88      foreach (IRun run in runs)
89        run.Changed += new EventHandler(run_Changed);
90    }
91    protected virtual void DeregisterRunEvents(IEnumerable<IRun> runs) {
92      foreach (IRun run in runs)
93        run.Changed -= new EventHandler(run_Changed);
94    }
95
96    private void Content_CollectionReset(object sender, HeuristicLab.Collections.CollectionItemsChangedEventArgs<IRun> e) {
97      DeregisterRunEvents(e.OldItems);
98      RegisterRunEvents(e.Items);
99    }
100    private void Content_ItemsRemoved(object sender, HeuristicLab.Collections.CollectionItemsChangedEventArgs<IRun> e) {
101      DeregisterRunEvents(e.Items);
102    }
103    private void Content_ItemsAdded(object sender, HeuristicLab.Collections.CollectionItemsChangedEventArgs<IRun> e) {
104      RegisterRunEvents(e.Items);
105    }
106
107    private void Content_Reset(object sender, EventArgs e) {
108      if (InvokeRequired)
109        Invoke(new EventHandler(Content_Reset), sender, e);
110      else {
111        this.categoricalMapping.Clear();
112        UpdateDataPoints();
113      }
114    }
115    private void Content_ColumnNamesChanged(object sender, EventArgs e) {
116      if (InvokeRequired)
117        Invoke(new EventHandler(Content_ColumnNamesChanged), sender, e);
118      else {
119        UpdateComboBoxes();
120      }
121    }
122    private void run_Changed(object sender, EventArgs e) {
123      if (InvokeRequired)
124        this.Invoke(new EventHandler(run_Changed), sender, e);
125      else {
126        IRun run = (IRun)sender;
127        UpdateDataPoints();
128      }
129    }
130    #endregion
131
132    #region update comboboxes, datapoints, runs
133    protected override void OnContentChanged() {
134      base.OnContentChanged();
135      this.categoricalMapping.Clear();
136      UpdateComboBoxes();
137      UpdateDataPoints();
138    }
139
140    private void UpdateComboBoxes() {
141      string selectedXAxis = (string)this.xAxisComboBox.SelectedItem;
142      string selectedYAxis = (string)this.yAxisComboBox.SelectedItem;
143      this.xAxisComboBox.Items.Clear();
144      this.yAxisComboBox.Items.Clear();
145      if (Content != null) {
146        string[] additionalAxisDimension = Enum.GetNames(typeof(AxisDimension));
147        this.xAxisComboBox.Items.AddRange(additionalAxisDimension);
148        this.xAxisComboBox.Items.AddRange(Matrix.ColumnNames.ToArray());
149        this.yAxisComboBox.Items.AddRange(additionalAxisDimension);
150        this.yAxisComboBox.Items.AddRange(Matrix.ColumnNames.ToArray());
151
152        bool changed = false;
153        if (selectedXAxis != null && xAxisComboBox.Items.Contains(selectedXAxis)) {
154          xAxisComboBox.SelectedItem = selectedXAxis;
155          changed = true;
156        }
157        if (selectedYAxis != null && yAxisComboBox.Items.Contains(selectedYAxis)) {
158          yAxisComboBox.SelectedItem = selectedYAxis;
159          changed = true;
160        }
161        if (changed)
162          UpdateDataPoints();
163      }
164    }
165
166    private void UpdateDataPoints() {
167      this.chart.Series.Clear();
168      this.seriesCache.Clear();
169      if (Content != null) {
170        foreach (IRun run in this.Content.Where(r => r.Visible))
171          this.AddDataPoint(run);
172        foreach (Series s in this.seriesCache.Values)
173          this.chart.Series.Add(s);
174
175        if (seriesCache.Count > 0) {
176          Series boxPlotSeries = CreateBoxPlotSeries();
177          this.chart.Series.Add(boxPlotSeries);
178        }
179
180        UpdateAxisLabels();
181      }
182      UpdateNoRunsVisibleLabel();
183    }
184
185    private Series CreateBoxPlotSeries() {
186      Series boxPlotSeries = new Series(BoxPlotSeriesName);
187      string seriesNames = string.Concat(seriesCache.Keys.Select(x => x.ToString() + ";").ToArray());
188      seriesNames = seriesNames.Remove(seriesNames.Length - 1); //delete last ; from string
189
190      boxPlotSeries.ChartArea = BoxPlotChartAreaName;
191      boxPlotSeries.ChartType = SeriesChartType.BoxPlot;
192      boxPlotSeries["BoxPlotSeries"] = seriesNames;
193      boxPlotSeries["BoxPlotShowUnusualValues"] = "true";
194      boxPlotSeries["PointWidth"] = "0.4";
195      boxPlotSeries.BackGradientStyle = System.Windows.Forms.DataVisualization.Charting.GradientStyle.VerticalCenter;
196      boxPlotSeries.BackSecondaryColor = System.Drawing.Color.FromArgb(130, 224, 64, 10);
197      boxPlotSeries.BorderColor = System.Drawing.Color.FromArgb(64, 64, 64);
198      boxPlotSeries.Color = System.Drawing.Color.FromArgb(224, 64, 10);
199
200      return boxPlotSeries;
201    }
202
203    private void AddDataPoint(IRun run) {
204      double? xValue;
205      double? yValue;
206
207      if (!xAxisComboBox.DroppedDown)
208        this.xAxisValue = (string)xAxisComboBox.SelectedItem;
209      if (!yAxisComboBox.DroppedDown)
210        this.yAxisValue = (string)yAxisComboBox.SelectedItem;
211
212      xValue = GetValue(run, this.xAxisValue);
213      yValue = GetValue(run, this.yAxisValue);
214
215      if (xValue.HasValue && yValue.HasValue) {
216        if (!this.seriesCache.ContainsKey(xValue.Value))
217          seriesCache[xValue.Value] = new Series(xValue.Value.ToString());
218
219        Series series = seriesCache[xValue.Value];
220        DataPoint point = new DataPoint(xValue.Value, yValue.Value);
221        series.Points.Add(point);
222      }
223    }
224    #endregion
225
226    #region get values from run
227    private double? GetValue(IRun run, string columnName) {
228      if (run == null || string.IsNullOrEmpty(columnName))
229        return null;
230
231      if (Enum.IsDefined(typeof(AxisDimension), columnName)) {
232        AxisDimension axisDimension = (AxisDimension)Enum.Parse(typeof(AxisDimension), columnName);
233        return GetValue(run, axisDimension);
234      } else {
235        int columnIndex = Matrix.ColumnNames.ToList().IndexOf(columnName);
236        IItem value = Content.GetValue(run, columnIndex);
237        if (value == null)
238          return null;
239
240        DoubleValue doubleValue = value as DoubleValue;
241        IntValue intValue = value as IntValue;
242        TimeSpanValue timeSpanValue = value as TimeSpanValue;
243        double? ret = null;
244        if (doubleValue != null) {
245          if (!double.IsNaN(doubleValue.Value) && !double.IsInfinity(doubleValue.Value))
246            ret = doubleValue.Value;
247        } else if (intValue != null)
248          ret = intValue.Value;
249        else if (timeSpanValue != null) {
250          ret = timeSpanValue.Value.TotalSeconds;
251        } else
252          ret = GetCategoricalValue(columnIndex, value.ToString());
253
254        return ret;
255      }
256    }
257    private double GetCategoricalValue(int dimension, string value) {
258      if (!this.categoricalMapping.ContainsKey(dimension))
259        this.categoricalMapping[dimension] = new Dictionary<object, double>();
260      if (!this.categoricalMapping[dimension].ContainsKey(value)) {
261        if (this.categoricalMapping[dimension].Values.Count == 0)
262          this.categoricalMapping[dimension][value] = 1.0;
263        else
264          this.categoricalMapping[dimension][value] = this.categoricalMapping[dimension].Values.Max() + 1.0;
265      }
266      return this.categoricalMapping[dimension][value];
267    }
268    private double GetValue(IRun run, AxisDimension axisDimension) {
269      double value = double.NaN;
270      switch (axisDimension) {
271        case AxisDimension.Color: {
272            value = GetCategoricalValue(-1, run.Color.ToString());
273            break;
274          }
275        default: {
276            throw new ArgumentException("No handling strategy for " + axisDimension.ToString() + " is defined.");
277          }
278      }
279      return value;
280    }
281    #endregion
282
283    #region GUI events
284    private void UpdateNoRunsVisibleLabel() {
285      if (this.chart.Series.Count > 0)
286        noRunsLabel.Visible = false;
287      else
288        noRunsLabel.Visible = true;
289    }
290
291    private void AxisComboBox_SelectedIndexChanged(object sender, EventArgs e) {
292      UpdateDataPoints();
293    }
294    private void UpdateAxisLabels() {
295      Axis xAxis = this.chart.ChartAreas[BoxPlotChartAreaName].AxisX;
296      Axis yAxis = this.chart.ChartAreas[BoxPlotChartAreaName].AxisY;
297      int axisDimensionCount = Enum.GetNames(typeof(AxisDimension)).Count();
298      SetCustomAxisLabels(xAxis, xAxisComboBox.SelectedIndex - axisDimensionCount);
299      SetCustomAxisLabels(yAxis, yAxisComboBox.SelectedIndex - axisDimensionCount);
300    }
301
302    private void chart_AxisViewChanged(object sender, System.Windows.Forms.DataVisualization.Charting.ViewEventArgs e) {
303      this.UpdateAxisLabels();
304    }
305
306    private void SetCustomAxisLabels(Axis axis, int dimension) {
307      axis.CustomLabels.Clear();
308      if (categoricalMapping.ContainsKey(dimension)) {
309        CustomLabel label = null;
310        foreach (var pair in categoricalMapping[dimension]) {
311          string labelText = pair.Key.ToString();
312          if (labelText.Length > 25)
313            labelText = labelText.Substring(0, 25) + " ... ";
314          label = axis.CustomLabels.Add(pair.Value - 0.5, pair.Value + 0.5, labelText);
315          label.GridTicks = GridTickTypes.TickMark;
316        }
317        axis.IsLabelAutoFit = false;
318        axis.LabelStyle.Enabled = true;
319        axis.LabelStyle.Angle = 0;
320        axis.LabelStyle.TruncatedLabels = true;
321      } else if (dimension > 0 && Content.GetValue(0, dimension) is TimeSpanValue) {
322        this.chart.ChartAreas[0].RecalculateAxesScale();
323        Axis correspondingAxis = this.chart.ChartAreas[0].Axes.Where(x => x.Name == axis.Name).SingleOrDefault();
324        if (correspondingAxis == null)
325          correspondingAxis = axis;
326        for (double i = correspondingAxis.Minimum; i <= correspondingAxis.Maximum; i += correspondingAxis.LabelStyle.Interval) {
327          TimeSpan time = TimeSpan.FromSeconds(i);
328          string x = string.Format("{0:00}:{1:00}:{2:00}", (int)time.Hours, time.Minutes, time.Seconds);
329          axis.CustomLabels.Add(i - correspondingAxis.LabelStyle.Interval / 2, i + correspondingAxis.LabelStyle.Interval / 2, x);
330        }
331      }
332    }
333    #endregion
334  }
335}
Note: See TracBrowser for help on using the repository browser.