Free cookie consent management tool by TermsFeed Policy Generator

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

Last change on this file since 4211 was 4211, checked in by mkommend, 12 years ago

customized x-axis labels in BoxPlotView (ticket #1135)

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