Free cookie consent management tool by TermsFeed Policy Generator

source: branches/DataPreprocessing Enhancements/HeuristicLab.DataPreprocessing.Views/3.4/ScatterPlotMultiView.cs @ 14725

Last change on this file since 14725 was 14725, checked in by mkommend, 7 years ago

#2709: Added grouping for multi scatter plot view.

File size: 20.3 KB
RevLine 
[10882]1using System;
2using System.Collections.Generic;
3using System.Drawing;
4using System.Linq;
5using System.Windows.Forms;
6using HeuristicLab.Analysis;
[14446]7using HeuristicLab.Analysis.Views;
[14381]8using HeuristicLab.Collections;
9using HeuristicLab.Data;
[10882]10using HeuristicLab.MainForm;
[14381]11using HeuristicLab.MainForm.WindowsForms;
[14514]12using RegressionType = HeuristicLab.Analysis.ScatterPlotDataRowVisualProperties.ScatterPlotDataRowRegressionType;
[10882]13
14namespace HeuristicLab.DataPreprocessing.Views {
[10987]15  [View("Scatter Plot Multi View")]
[14462]16  [Content(typeof(MultiScatterPlotContent), true)]
[14381]17  public partial class ScatterPlotMultiView : PreprocessingCheckedVariablesView {
[14580]18    private readonly IDictionary<string, Label> columnHeaderCache = new Dictionary<string, Label>();
19    private readonly IDictionary<string, VerticalLabel> rowHeaderCache = new Dictionary<string, VerticalLabel>();
20    private readonly IDictionary<Tuple<string/*col*/, string/*row*/>, Control> bodyCache = new Dictionary<Tuple<string, string>, Control>();
[14384]21
[10987]22    public ScatterPlotMultiView() {
[10882]23      InitializeComponent();
[14381]24
[14514]25      regressionTypeComboBox.DataSource = Enum.GetValues(typeof(RegressionType));
26      regressionTypeComboBox.SelectedItem = RegressionType.None;
27
[14388]28      #region Initialize Scrollbars
[14384]29      columnHeaderScrollPanel.HorizontalScroll.Enabled = true;
30      columnHeaderScrollPanel.VerticalScroll.Enabled = false;
31      columnHeaderScrollPanel.HorizontalScroll.Visible = false;
32      columnHeaderScrollPanel.VerticalScroll.Visible = false;
[14381]33
[14384]34      rowHeaderScrollPanel.HorizontalScroll.Enabled = false;
35      rowHeaderScrollPanel.VerticalScroll.Enabled = true;
36      rowHeaderScrollPanel.HorizontalScroll.Visible = false;
37      rowHeaderScrollPanel.VerticalScroll.Visible = false;
[14381]38
[14384]39      bodyScrollPanel.HorizontalScroll.Enabled = true;
40      bodyScrollPanel.VerticalScroll.Enabled = true;
41      bodyScrollPanel.HorizontalScroll.Visible = true;
42      bodyScrollPanel.VerticalScroll.Visible = true;
43      bodyScrollPanel.AutoScroll = true;
[14388]44      #endregion
[14381]45
[14384]46      bodyScrollPanel.MouseWheel += bodyScrollPanel_MouseWheel;
[10882]47    }
48
[14462]49    public new MultiScatterPlotContent Content {
50      get { return (MultiScatterPlotContent)base.Content; }
[10882]51      set { base.Content = value; }
52    }
53
54    protected override void OnContentChanged() {
55      base.OnContentChanged();
56      if (Content != null) {
[14725]57        groupingComboBox.Items.Add("");
58        foreach (string var in PreprocessingChartContent.GetVariableNamesForGrouping(Content.PreprocessingData)) {
59          groupingComboBox.Items.Add(var);
60        }
61        groupingComboBox.SelectedItem = Content.GroupingVariable;
62
[14381]63        GenerateCharts();
[10882]64      }
65    }
66
[14514]67    protected override void SetEnabledStateOfControls() {
68      base.SetEnabledStateOfControls();
69      var regressionType = (RegressionType)regressionTypeComboBox.SelectedValue;
70      polynomialRegressionOrderNumericUpDown.Enabled = regressionType == RegressionType.Polynomial;
71    }
72
[14381]73    protected override void CheckedItemsChanged(object sender, CollectionItemsChangedEventArgs<IndexedItem<StringValue>> checkedItems) {
74      base.CheckedItemsChanged(sender, checkedItems);
[14511]75      if (suppressCheckedChangedUpdate)
76        return;
[14384]77      foreach (var variable in checkedItems.Items.Select(i => i.Value.Value)) {
78        if (IsVariableChecked(variable))
79          AddChartToTable(variable);
80        else
81          RemoveChartFromTable(variable);
82      }
83    }
[10952]84
[14388]85    #region Add and remove charts
[14384]86    private void AddChartToTable(string variable) {
[14388]87      frameTableLayoutPanel.SuspendLayout();
[14384]88
[14388]89      // find index to insert
[14511]90      var variables = Content.VariableItemList.Select(v => v.Value).ToList();
[14388]91      int idx = variables              // all variables
92        .TakeWhile(t => t != variable) // ... until the variable that was checked
93        .Count(IsVariableChecked);     // ... how many checked variables
94
95      // add column header
96      var colH = columnHeaderTableLayoutPanel;
97      AddColumnHelper(colH, idx, _ => GetColumnHeader(variable));
98
99      // add row header
100      var rowH = rowHeaderTableLayoutPanel;
101      AddRowHelper(rowH, idx, _ => GetRowHeader(variable));
102
103      // add body
104      var body = bodyTableLayoutPanel;
105      var vars = GetCheckedVariables();
106      var varsMinus = vars.Except(new[] { variable }).ToList();
107      AddColumnHelper(body, idx, r => GetBody(variable, varsMinus[r])); // exclude "variable" because the row for it does not exist yet
108      AddRowHelper(body, idx, c => GetBody(vars[c], variable));
109
110      frameTableLayoutPanel.ResumeLayout(true);
[14381]111    }
[14388]112    private void AddColumnHelper(TableLayoutPanel tlp, int idx, Func<int, Control> creatorFunc) {
113      // add column
114      tlp.ColumnCount++;
115      tlp.ColumnStyles.Insert(idx, new ColumnStyle(SizeType.Absolute, GetColumnWidth()));
116      // shift right
[14580]117      for (int c = tlp.ColumnCount; c > idx - 1; c--) {
[14388]118        for (int r = 0; r < tlp.RowCount; r++) {
119          var control = tlp.GetControlFromPosition(c, r);
120          if (control != null) {
121            tlp.SetColumn(control, c + 1);
122          }
123        }
124      }
125      // add controls
126      for (int r = 0; r < tlp.RowCount; r++) {
127        if (tlp.GetControlFromPosition(idx, r) == null)
128          tlp.Controls.Add(creatorFunc(r), idx, r);
129      }
130
131    }
132    private void AddRowHelper(TableLayoutPanel tlp, int idx, Func<int, Control> creatorFunc) {
133      // add row
134      tlp.RowCount++;
135      tlp.RowStyles.Insert(idx, new RowStyle(SizeType.Absolute, GetRowHeight()));
136      // shift right
137      for (int r = tlp.RowCount; r > idx - 1; r--) {
138        for (int c = 0; c < tlp.ColumnCount; c++) {
139          var control = tlp.GetControlFromPosition(c, r);
140          if (control != null) {
141            tlp.SetRow(control, r + 1);
142          }
143        }
144      }
145      // add controls
146      for (int c = 0; c < tlp.ColumnCount; c++)
147        if (tlp.GetControlFromPosition(c, idx) == null)
148          tlp.Controls.Add(creatorFunc(c), c, idx);
149    }
150
[14384]151    private void RemoveChartFromTable(string variable) {
152      frameTableLayoutPanel.SuspendLayout();
[10987]153
[14384]154      // remove column header
155      var colH = columnHeaderTableLayoutPanel;
156      int colIdx = colH.GetColumn(colH.Controls[variable]);
157      RemoveColumnHelper(colH, colIdx);
158
159      // remove row header
160      var rowH = rowHeaderTableLayoutPanel;
161      int rowIdx = rowH.GetRow(rowH.Controls[variable]);
162      RemoveRowHelper(rowH, rowIdx);
163
164      // remove from body
165      var body = bodyTableLayoutPanel;
166      RemoveColumnHelper(body, colIdx);
167      RemoveRowHelper(body, rowIdx);
168
169      frameTableLayoutPanel.ResumeLayout(true);
170    }
171    private void RemoveColumnHelper(TableLayoutPanel tlp, int idx) {
[14388]172      // remove controls
[14384]173      for (int r = 0; r < tlp.RowCount; r++)
174        tlp.Controls.Remove(tlp.GetControlFromPosition(idx, r));
[14388]175      // shift left
[14384]176      for (int c = idx + 1; c < tlp.ColumnCount; c++) {
177        for (int r = 0; r < tlp.RowCount; r++) {
178          var control = tlp.GetControlFromPosition(c, r);
179          if (control != null) {
180            tlp.SetColumn(control, c - 1);
181          }
182        }
183      }
[14388]184      // delete column
[14384]185      tlp.ColumnStyles.RemoveAt(tlp.ColumnCount - 1);
186      tlp.ColumnCount--;
187    }
188    private void RemoveRowHelper(TableLayoutPanel tlp, int idx) {
[14388]189      // remove controls
[14384]190      for (int c = 0; c < tlp.ColumnCount; c++)
191        tlp.Controls.Remove(tlp.GetControlFromPosition(c, idx));
[14388]192      // shift left
[14384]193      for (int r = idx + 1; r < tlp.RowCount; r++) {
194        for (int c = 0; c < tlp.ColumnCount; c++) {
195          var control = tlp.GetControlFromPosition(c, r);
196          if (control != null) {
197            tlp.SetRow(control, r - 1);
198          }
199        }
200      }
[14388]201      // delete rows
[14384]202      tlp.RowStyles.RemoveAt(tlp.RowCount - 1);
203      tlp.RowCount--;
204    }
[14388]205    #endregion
[14384]206
[14388]207    #region Add/Remove/Update Variable
[14381]208    protected override void AddVariable(string name) {
209      base.AddVariable(name);
[14388]210      if (IsVariableChecked(name))
211        AddChartToTable(name);
[10952]212    }
[14381]213    protected override void RemoveVariable(string name) {
214      base.RemoveVariable(name);
[10952]215
[14384]216      // clear caches
217      columnHeaderCache.Remove(name);
218      rowHeaderCache.Remove(name);
219      var keys = bodyCache.Keys.Where(t => t.Item1 == name || t.Item2 == name).ToList();
220      foreach (var key in keys)
221        bodyCache.Remove(key);
222
223      if (IsVariableChecked(name)) {
224        RemoveChartFromTable(name);
225      }
[14381]226    }
227    protected override void UpdateVariable(string name) {
228      base.UpdateVariable(name);
[14388]229      RemoveVariable(name);
230      AddVariable(name);
[14381]231    }
232    protected override void ResetAllVariables() {
233      GenerateCharts();
234    }
235    #endregion
236
[14580]237    #region Creating Headers and Body
[14384]238    private Label GetColumnHeader(string variable) {
239      if (!columnHeaderCache.ContainsKey(variable)) {
240        columnHeaderCache.Add(variable, new Label() {
241          Text = variable,
242          TextAlign = ContentAlignment.MiddleCenter,
243          Name = variable,
244          Height = columnHeaderTableLayoutPanel.Height,
245          Dock = DockStyle.Fill,
246          Margin = new Padding(3)
247        });
248      }
249      return columnHeaderCache[variable];
250    }
251    private Label GetRowHeader(string variable) {
252      if (!rowHeaderCache.ContainsKey(variable)) {
[14514]253        rowHeaderCache.Add(variable, new VerticalLabel() {
[14384]254          Text = variable,
255          TextAlign = ContentAlignment.MiddleCenter,
256          Name = variable,
257          Width = rowHeaderTableLayoutPanel.Width,
[14514]258          Height = columnHeaderScrollPanel.Width,
[14384]259          Dock = DockStyle.Fill,
260          Margin = new Padding(3)
261        });
262      }
263      return rowHeaderCache[variable];
264    }
[14446]265    private Control GetBody(string colVariable, string rowVariable) {
[14384]266      var key = Tuple.Create(colVariable, rowVariable);
267      if (!bodyCache.ContainsKey(key)) {
268        if (rowVariable == colVariable) { // use historgram if x and y variable are equal
[14725]269          var dataTable = HistogramContent.CreateHistogram(Content.PreprocessingData, rowVariable, (string)groupingComboBox.SelectedItem, DataRowVisualProperties.DataRowHistogramAggregation.Overlapping);
270          foreach (var dataRow in dataTable.Rows)
271            dataRow.VisualProperties.IsVisibleInLegend = false;
272
[14459]273          var pcv = new DataTableControl {
[14384]274            Name = key.ToString(),
275            Content = dataTable,
276            Dock = DockStyle.Fill,
[14459]277            //ShowLegend = false,
278            //XAxisFormat = "G3"
[14384]279          };
[14459]280          //pcv.ChartDoubleClick += HistogramDoubleClick;
[14384]281          bodyCache.Add(key, pcv);
282        } else { //scatter plot
[14725]283          var scatterPlot = ScatterPlotContent.CreateScatterPlot(Content.PreprocessingData, colVariable, rowVariable, (string)groupingComboBox.SelectedItem);
[14514]284          var regressionType = (RegressionType)regressionTypeComboBox.SelectedValue;
285          int order = (int)polynomialRegressionOrderNumericUpDown.Value;
286          foreach (var row in scatterPlot.Rows) {
[14495]287            row.VisualProperties.PointSize = 3;
[14514]288            row.VisualProperties.IsRegressionVisibleInLegend = false;
289            row.VisualProperties.RegressionType = regressionType;
290            row.VisualProperties.PolynomialRegressionOrder = order;
291          }
[14446]292          scatterPlot.VisualProperties.Title = string.Empty;
[14514]293          var scatterPlotControl = new ScatterPlotControl {
[14384]294            Name = key.ToString(),
295            Content = scatterPlot,
296            Dock = DockStyle.Fill,
[14446]297            //ShowLegend = false,
298            //XAxisFormat = "G3"
[14384]299          };
[14514]300          //scatterPlotControl.DoubleClick += ScatterPlotDoubleClick; // ToDo: not working; double click is already handled by the chart
[14446]301          bodyCache.Add(key, scatterPlotControl);
[14384]302        }
303      }
304      return bodyCache[key];
305    }
306    #endregion
307
[14511]308    protected override void CheckedChangedUpdate() {
309      GenerateCharts();
310    }
311
[14381]312    #region Generate Charts
313    private void GenerateCharts() {
[14511]314      if (suppressCheckedChangedUpdate) return;
315
[14384]316      var variables = GetCheckedVariables();
[10987]317
[14384]318      // Clear old layouts and cache
319      foreach (var tableLayoutPanel in new[] { columnHeaderTableLayoutPanel, rowHeaderTableLayoutPanel, bodyTableLayoutPanel }) {
320        tableLayoutPanel.Controls.Clear();
321        tableLayoutPanel.ColumnStyles.Clear();
322        tableLayoutPanel.RowStyles.Clear();
[14381]323      }
[14384]324      columnHeaderCache.Clear();
325      rowHeaderCache.Clear();
326      bodyCache.Clear();
[10882]327
[14381]328      // Set row and column count
[14384]329      columnHeaderTableLayoutPanel.ColumnCount = variables.Count;
330      rowHeaderTableLayoutPanel.RowCount = variables.Count;
[14381]331      bodyTableLayoutPanel.ColumnCount = variables.Count;
332      bodyTableLayoutPanel.RowCount = variables.Count;
[10915]333
[14381]334      // Set column and row layout
335      for (int i = 0; i < variables.Count; i++) {
[14388]336        columnHeaderTableLayoutPanel.ColumnStyles.Add(new ColumnStyle(SizeType.Absolute, GetColumnWidth()));
337        rowHeaderTableLayoutPanel.RowStyles.Add(new RowStyle(SizeType.Absolute, GetRowHeight()));
338        bodyTableLayoutPanel.ColumnStyles.Add(new ColumnStyle(SizeType.Absolute, GetColumnWidth()));
339        bodyTableLayoutPanel.RowStyles.Add(new RowStyle(SizeType.Absolute, GetRowHeight()));
[10882]340      }
[10915]341
[14384]342      frameTableLayoutPanel.SuspendLayout();
[14381]343      AddHeaderToTableLayoutPanels();
344      AddChartsToTableLayoutPanel();
[14384]345      UpdateHeaderMargin();
346      frameTableLayoutPanel.ResumeLayout(true);
[10952]347    }
[10915]348
[14381]349    private void AddHeaderToTableLayoutPanels() {
[14384]350      int i = 0;
351      foreach (var variable in GetCheckedVariables()) {
352        columnHeaderTableLayoutPanel.Controls.Add(GetColumnHeader(variable), i, 0);
353        rowHeaderTableLayoutPanel.Controls.Add(GetRowHeader(variable), 0, i);
354        i++;
[14381]355      }
356    }
357    private void AddChartsToTableLayoutPanel() {
[14384]358      int c = 0;
359      foreach (var colVar in GetCheckedVariables()) {
360        if (!IsVariableChecked(colVar)) continue;
361        int r = 0;
362        foreach (var rowVar in GetCheckedVariables()) {
363          if (!IsVariableChecked(rowVar)) continue;
364          bodyTableLayoutPanel.Controls.Add(GetBody(colVar, rowVar), c, r);
365          r++;
[10915]366        }
[14384]367        c++;
[10915]368      }
[14514]369      UpdateRegressionLine();
[10882]370    }
371
[14381]372    #endregion
373
374    #region DoubleClick Events
[10952]375    //Open scatter plot in new tab with new content when double clicked
376    private void ScatterPlotDoubleClick(object sender, EventArgs e) {
[14446]377      var scatterPlotControl = (ScatterPlotControl)sender;
[14467]378      var scatterContent = new SingleScatterPlotContent(Content.PreprocessingData);
[14446]379      ScatterPlot scatterPlot = scatterPlotControl.Content;
[10915]380
[14381]381      //Extract variable names from scatter plot and set them in content
[10952]382      if (scatterPlot.Rows.Count == 1) {
[10992]383        string[] variables = scatterPlot.Rows.ElementAt(0).Name.Split(new string[] { " - " }, StringSplitOptions.None); // extract variable names from string
[10952]384        scatterContent.SelectedXVariable = variables[0];
385        scatterContent.SelectedYVariable = variables[1];
386      }
[14381]387
388      MainFormManager.MainForm.ShowContent(scatterContent, typeof(ScatterPlotSingleView));  // open in new tab
[10915]389    }
[10952]390
[14381]391    //open histogram in new tab with new content when double clicked
392    private void HistogramDoubleClick(object sender, EventArgs e) {
[14459]393      DataTableControl pcv = (DataTableControl)sender;
[14381]394      HistogramContent histoContent = new HistogramContent(Content.PreprocessingData);  // create new content     
[14511]395      //ToDo: histoContent.VariableItemList = Content.CreateVariableItemList();
[14459]396      var dataTable = pcv.Content;
[10952]397
[14381]398      //Set variable item list from with variable from data table
399      if (dataTable.Rows.Count == 1) { // only one data row should be in data table
[10952]400        string variableName = dataTable.Rows.ElementAt(0).Name;
401
402        // set only variable name checked
[10992]403        foreach (var checkedItem in histoContent.VariableItemList) {
[14381]404          histoContent.VariableItemList.SetItemCheckedState(checkedItem, checkedItem.Value == variableName);
[10952]405        }
406      }
[14381]407      MainFormManager.MainForm.ShowContent(histoContent, typeof(HistogramView));  // open in new tab
[10952]408    }
[14381]409    #endregion
[10952]410
[14388]411    #region Scrolling
[14384]412    private void bodyScrollPanel_Scroll(object sender, ScrollEventArgs e) {
[14381]413      SyncScroll();
[10952]414
[14384]415      UpdateHeaderMargin();
[10952]416    }
[14384]417    private void bodyScrollPanel_MouseWheel(object sender, MouseEventArgs e) {
[14381]418      // Scrolling with the mouse wheel is not captured in the Scoll event
419      SyncScroll();
420    }
421    private void SyncScroll() {
422      frameTableLayoutPanel.SuspendRepaint();
[10992]423
[14384]424      columnHeaderScrollPanel.HorizontalScroll.Minimum = bodyScrollPanel.HorizontalScroll.Minimum;
425      columnHeaderScrollPanel.HorizontalScroll.Maximum = bodyScrollPanel.HorizontalScroll.Maximum;
426      rowHeaderScrollPanel.VerticalScroll.Minimum = bodyScrollPanel.VerticalScroll.Minimum;
427      rowHeaderScrollPanel.VerticalScroll.Maximum = bodyScrollPanel.VerticalScroll.Maximum;
[10882]428
[14384]429      columnHeaderScrollPanel.HorizontalScroll.Value = Math.Max(bodyScrollPanel.HorizontalScroll.Value, 1);
430      rowHeaderScrollPanel.VerticalScroll.Value = Math.Max(bodyScrollPanel.VerticalScroll.Value, 1);
[14381]431      // minimum 1 is nececary  because of two factors:
432      // - setting the Value-property of Horizontal/VerticalScroll updates the internal state but the Value-property stays 0
433      // - setting the same number of the Value-property has no effect
434      // since the Value-property is always 0, setting it to 0 would have no effect; so it is set to 1 instead
[10992]435
[14381]436      frameTableLayoutPanel.ResumeRepaint(true);
437    }
[14384]438    // add a margin to the header table layouts if the scollbar is visible to account for the width/height of the scrollbar
439    private void UpdateHeaderMargin() {
440      columnHeaderScrollPanel.Margin = new Padding(0, 0, bodyScrollPanel.VerticalScroll.Visible ? SystemInformation.VerticalScrollBarWidth : 0, 0);
441      rowHeaderScrollPanel.Margin = new Padding(0, 0, 0, bodyScrollPanel.HorizontalScroll.Visible ? SystemInformation.HorizontalScrollBarHeight : 0);
442    }
[14388]443    #endregion
444
445    #region Sizing of Charts
446    private int GetColumnWidth() { return (int)(bodyScrollPanel.Width * ((float)widthTrackBar.Value / 100)); }
447    private int GetRowHeight() { return (int)(bodyScrollPanel.Height * ((float)heightTrackBar.Value / 100)); }
448    private void widthTrackBar_ValueChanged(object sender, EventArgs e) {
449      frameTableLayoutPanel.SuspendRepaint();
450      for (int i = 0; i < columnHeaderTableLayoutPanel.ColumnCount; i++) {
451        columnHeaderTableLayoutPanel.ColumnStyles[i].Width = GetColumnWidth();
452        bodyTableLayoutPanel.ColumnStyles[i].Width = GetColumnWidth();
453      }
454      frameTableLayoutPanel.ResumeRepaint(true);
455    }
456    private void heightTrackBar_ValueChanged(object sender, EventArgs e) {
457      frameTableLayoutPanel.SuspendRepaint();
458
459      for (int i = 0; i < rowHeaderTableLayoutPanel.RowCount; i++) {
460        rowHeaderTableLayoutPanel.RowStyles[i].Height = GetRowHeight();
461        bodyTableLayoutPanel.RowStyles[i].Height = GetRowHeight();
462      }
463      frameTableLayoutPanel.ResumeRepaint(true);
464    }
465    #endregion
[14514]466
467    #region Regression Line
468    private void regressionTypeComboBox_SelectedValueChanged(object sender, EventArgs e) {
469      var regressionType = (RegressionType)regressionTypeComboBox.SelectedValue;
470      polynomialRegressionOrderNumericUpDown.Enabled = regressionType == RegressionType.Polynomial;
471      UpdateRegressionLine();
472    }
473
474    private void polynomialRegressionOrderNumericUpDown_ValueChanged(object sender, EventArgs e) {
475      UpdateRegressionLine();
476    }
477
478    private void UpdateRegressionLine() {
479      var regressionType = (RegressionType)regressionTypeComboBox.SelectedValue;
480      int order = (int)polynomialRegressionOrderNumericUpDown.Value;
481
482      foreach (var control in bodyCache.Values) {
483        var scatterPlotControl = control as ScatterPlotControl;
484        if (scatterPlotControl != null) {
485          foreach (var row in scatterPlotControl.Content.Rows) {
486            row.VisualProperties.IsRegressionVisibleInLegend = false;
487            row.VisualProperties.RegressionType = regressionType;
488            row.VisualProperties.PolynomialRegressionOrder = order;
489          }
490        }
491      }
492    }
493    #endregion
[14725]494
495    private void groupingComboBox_SelectedIndexChanged(object sender, EventArgs e) {
496      GenerateCharts();
497    }
[14381]498  }
[10882]499}
[14514]500
Note: See TracBrowser for help on using the repository browser.