Free cookie consent management tool by TermsFeed Policy Generator

source: branches/BubbleChart/HeuristicLab.Optimization.BubbleChart/3.3/BubbleChartView.cs @ 12388

Last change on this file since 12388 was 12388, checked in by pfleck, 9 years ago

#2379 Added BubbleChartView which can select the parts of the RecursiveDataItem tree which should be searched for the selected keys (axis).

File size: 10.1 KB
Line 
1#region License Information
2/* HeuristicLab
3 * Copyright (C) 2002-2015 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.Common;
28using HeuristicLab.Core;
29using HeuristicLab.Core.Views;
30using HeuristicLab.Data;
31using HeuristicLab.MainForm;
32
33namespace HeuristicLab.Optimization.BubbleChart {
34  [View("Bubble Chart (Recursive)")]
35  [Content(typeof(RecursiveDataItem), false)]
36  public partial class BubbleChartView : ItemView {
37
38    private enum SizeDimension { Constant = 0 }
39    private enum AxisDimension { Index = 0 }
40
41    private string xAxisValue;
42    private string yAxisValue;
43    private string sizeAxisValue;
44
45    private readonly Dictionary<RecursiveDataItem, int> itemToIndexMapping = new Dictionary<RecursiveDataItem, int>();
46    private readonly Dictionary<string, Dictionary<object, double>> categoricalMapping = new Dictionary<string, Dictionary<object, double>>();
47
48    public new RecursiveDataItem Content {
49      get { return (RecursiveDataItem)base.Content; }
50      set { base.Content = value; }
51    }
52
53    public BubbleChartView() {
54      InitializeComponent();
55    }
56
57    protected override void OnContentChanged() {
58      base.OnContentChanged();
59
60      UpdateTreeView();
61      UpdateComboBoxes();
62      UpdateDataPoints();
63      //UpdateCaption();
64    }
65
66    #region Tree Queries
67    private IEnumerable<string> GetAvailableKeys() {
68      var collector = new List<string>(Content.Data.Keys);
69      foreach (TreeNode node in treeView.Nodes)
70        GetAvailableKeys(node, collector);
71      return collector.Distinct();
72    }
73    private void GetAvailableKeys(TreeNode node, List<string> collector) {
74      var item = (RecursiveDataItem)node.Tag;
75      if (node.Checked)
76        collector.AddRange(item.Data.Keys);
77      foreach (TreeNode child in node.Nodes)
78        GetAvailableKeys(child, collector);
79    }
80
81    private IEnumerable<RecursiveDataItem> GetAvailableItems() {
82      var collector = new List<RecursiveDataItem>() { Content };
83      foreach (TreeNode child in treeView.Nodes)
84        GetAvailableItems(child, collector);
85      return collector;
86    }
87
88    private void GetAvailableItems(TreeNode node, List<RecursiveDataItem> collector) {
89      var item = (RecursiveDataItem)node.Tag;
90      if (node.Checked)
91        collector.Add(item);
92      foreach (TreeNode child in node.Nodes)
93        GetAvailableItems(child, collector);
94    }
95    #endregion
96
97    #region Update Controls
98    private void UpdateTreeView() {
99      treeView.Nodes.Clear();
100
101      if (Content != null) {
102        foreach (var child in Content.Children)
103          treeView.Nodes.Add(CreateTreeNode(child));
104      }
105
106      treeView.ExpandAll();
107      splitContainer.Panel1Collapsed = treeView.Nodes.Count == 0;
108    }
109    private TreeNode CreateTreeNode(RecursiveDataItem item) {
110      var node = new TreeNode(item.Name) {
111        Tag = item,
112        Checked = true
113      };
114      foreach (var child in item.Children)
115        node.Nodes.Add(CreateTreeNode(child));
116      return node;
117    }
118
119    private void UpdateComboBoxes() {
120      var selectedXAxis = (string)xAxisComboBox.SelectedItem;
121      var selectedYAxis = (string)yAxisComboBox.SelectedItem;
122      var selectedSizeAxis = (string)sizeComboBox.SelectedItem;
123      xAxisComboBox.Items.Clear();
124      yAxisComboBox.Items.Clear();
125      sizeComboBox.Items.Clear();
126
127      if (Content != null) {
128        var axisNames = GetAvailableKeys().ToArray();
129        var additionalAxisDimension = Enum.GetNames(typeof(AxisDimension));
130        var additionalSizeDimension = Enum.GetNames(typeof(SizeDimension));
131
132        xAxisComboBox.Items.AddRange(additionalAxisDimension);
133        xAxisComboBox.Items.AddRange(axisNames);
134        yAxisComboBox.Items.AddRange(additionalAxisDimension);
135        yAxisComboBox.Items.AddRange(axisNames);
136        sizeComboBox.Items.AddRange(additionalSizeDimension);
137        sizeComboBox.Items.AddRange(axisNames);
138        sizeComboBox.SelectedItem = SizeDimension.Constant.ToString();
139
140        bool changed = false;
141        if (selectedXAxis != null && xAxisComboBox.Items.Contains(selectedXAxis)) {
142          xAxisComboBox.SelectedItem = selectedXAxis;
143          changed = true;
144        }
145        if (selectedYAxis != null && yAxisComboBox.Items.Contains(selectedYAxis)) {
146          yAxisComboBox.SelectedItem = selectedYAxis;
147          changed = true;
148        }
149        if (selectedSizeAxis != null && sizeComboBox.Items.Contains(selectedSizeAxis)) {
150          sizeComboBox.SelectedItem = selectedSizeAxis;
151          changed = true;
152        }
153        if (changed) {
154          UpdateDataPoints();
155          //UpdateAxisLabels();
156        }
157      }
158    }
159
160    private void UpdateDataPoints() {
161      var series = chart.Series[0];
162      series.Points.Clear();
163      itemToIndexMapping.Clear();
164      categoricalMapping.Clear();
165      RebuildInverseIndex();
166
167      chart.ChartAreas[0].AxisX.IsMarginVisible = xAxisValue != AxisDimension.Index.ToString();
168      chart.ChartAreas[0].AxisY.IsMarginVisible = yAxisValue != AxisDimension.Index.ToString();
169
170      if (Content != null) {
171        var items = GetAvailableItems();
172        foreach (var item in items) {
173          var x = GetValue(item, xAxisValue);
174          var y = GetValue(item, yAxisValue);
175          var s = GetValue(item, sizeAxisValue);
176          if (x.HasValue && y.HasValue && s.HasValue) {
177            var dataPoint = new DataPoint(x.Value, new[] { y.Value, s.Value });
178            series.Points.Add(dataPoint);
179          }
180        }
181      }
182    }
183    private double? GetValue(RecursiveDataItem item, string key) {
184      if (item == null || string.IsNullOrEmpty(key))
185        return null;
186
187      if (Enum.IsDefined(typeof(AxisDimension), key)) {
188        var axisDimension = (AxisDimension)Enum.Parse(typeof(AxisDimension), key);
189        return GetValue(item, axisDimension);
190      } else if (Enum.IsDefined(typeof(SizeDimension), key)) {
191        var sizeDimension = (SizeDimension)Enum.Parse(typeof(SizeDimension), key);
192        return GetValue(item, sizeDimension);
193      } else if (item.Data.ContainsKey(key)) {
194        IItem value = item.Data[key];
195        var doubleValue = value as DoubleValue;
196        var intValue = value as IntValue;
197        var timeSpanValue = value as TimeSpanValue;
198        double? ret = null;
199        if (doubleValue != null) {
200          if (!double.IsNaN(doubleValue.Value) && !double.IsInfinity(doubleValue.Value))
201            ret = doubleValue.Value;
202        } else if (intValue != null)
203          ret = intValue.Value;
204        else if (timeSpanValue != null)
205          ret = timeSpanValue.Value.TotalSeconds;
206        else
207          ret = GetCategoricalValue(item, key, value.ToString());
208        return ret;
209      } else {
210        return null;
211      }
212    }
213    private double? GetCategoricalValue(RecursiveDataItem item, string key, string value) {
214      if (!categoricalMapping.ContainsKey(key)) {
215        categoricalMapping[key] = new Dictionary<object, double>();
216        var orderedCategories =
217          GetAvailableItems().Where(x => x.Data.ContainsKey(key)).Select(x => x.Data[key].ToString())
218            .Distinct().OrderBy(x => x, new NaturalStringComparer());
219        int count = 1;
220        foreach (var category in orderedCategories) {
221          categoricalMapping[key].Add(category, count);
222          count++;
223        }
224      }
225      if (!this.categoricalMapping[key].ContainsKey(value)) return null;
226      return this.categoricalMapping[key][value];
227    }
228    private double GetValue(RecursiveDataItem item, AxisDimension axisDimension) {
229      double value = double.NaN;
230      switch (axisDimension) {
231        case AxisDimension.Index:
232          value = itemToIndexMapping[item];
233          break;
234        default:
235          throw new ArgumentException("No handling strategy for " + axisDimension.ToString() + " is defined.");
236      }
237      return value;
238    }
239    private double GetValue(RecursiveDataItem item, SizeDimension sizeDimension) {
240      double value = double.NaN;
241      switch (sizeDimension) {
242        case SizeDimension.Constant:
243          value = 2;
244          break;
245        default:
246          throw new ArgumentException("No handling strategy for " + sizeDimension.ToString() + " is defined.");
247      }
248      return value;
249    }
250    private void RebuildInverseIndex() {
251      if (Content != null) {
252        itemToIndexMapping.Clear();
253        int i = 0;
254        foreach (var item in GetAvailableItems()) {
255          // ToDo: do not add if key (which one?) is not present within item
256          itemToIndexMapping.Add(item, i);
257          i++;
258        }
259      }
260    }
261    #endregion
262
263    #region Event Handlers
264    private void treeView_AfterCheck(object sender, TreeViewEventArgs e) {
265      UpdateComboBoxes();
266      UpdateDataPoints();
267    }
268
269    private void axisComboBox_SelectedValueChanged(object sender, EventArgs e) {
270      bool axisSelected = xAxisComboBox.SelectedIndex != -1 && yAxisComboBox.SelectedIndex != -1;
271
272      xAxisValue = (string)xAxisComboBox.SelectedItem;
273      yAxisValue = (string)yAxisComboBox.SelectedItem;
274      sizeAxisValue = (string)sizeComboBox.SelectedItem;
275
276      UpdateDataPoints();
277      //UpdateAxisLabels();
278    }
279    #endregion
280
281
282  }
283}
Note: See TracBrowser for help on using the repository browser.