Free cookie consent management tool by TermsFeed Policy Generator

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

Last change on this file since 14514 was 14514, checked in by pfleck, 7 years ago

#2709

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