2
* This program is free software; you can redistribute it and/or modify
3
* it under the terms of the GNU General Public License as published by
4
* the Free Software Foundation; either version 2 of the License, or
5
* (at your option) any later version.
7
* This program is distributed in the hope that it will be useful,
8
* but WITHOUT ANY WARRANTY; without even the implied warranty of
9
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10
* GNU General Public License for more details.
12
* You should have received a copy of the GNU General Public License
13
* along with this program; if not, write to the Free Software
14
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19
* Copyright (C) 1999 University of Waikato, Hamilton, New Zealand
23
package weka.gui.visualize;
25
import weka.core.Attribute;
26
import weka.core.FastVector;
27
import weka.core.Instance;
28
import weka.core.Instances;
29
import weka.gui.ExtensionFileFilter;
30
import weka.gui.Logger;
32
import java.awt.BorderLayout;
33
import java.awt.Color;
34
import java.awt.Component;
35
import java.awt.Dimension;
36
import java.awt.Graphics;
37
import java.awt.GridBagConstraints;
38
import java.awt.GridBagLayout;
39
import java.awt.GridLayout;
40
import java.awt.Insets;
41
import java.awt.event.ActionEvent;
42
import java.awt.event.ActionListener;
43
import java.awt.event.InputEvent;
44
import java.awt.event.MouseAdapter;
45
import java.awt.event.MouseEvent;
46
import java.awt.event.MouseMotionAdapter;
47
import java.io.BufferedReader;
48
import java.io.BufferedWriter;
50
import java.io.FileReader;
51
import java.io.FileWriter;
52
import java.io.Writer;
53
import java.util.Random;
55
import javax.swing.BorderFactory;
56
import javax.swing.DefaultComboBoxModel;
57
import javax.swing.JButton;
58
import javax.swing.JComboBox;
59
import javax.swing.JFileChooser;
60
import javax.swing.JFrame;
61
import javax.swing.JLabel;
62
import javax.swing.JOptionPane;
63
import javax.swing.JPanel;
64
import javax.swing.JSlider;
65
import javax.swing.SwingConstants;
66
import javax.swing.event.ChangeEvent;
67
import javax.swing.event.ChangeListener;
68
import javax.swing.filechooser.FileFilter;
71
* This panel allows the user to visualize a dataset (and if provided) a
72
* classifier's/clusterer's predictions in two dimensions.
74
* If the user selects a nominal attribute as the colouring attribute then
75
* each point is drawn in a colour that corresponds to the discrete value
76
* of that attribute for the instance. If the user selects a numeric
77
* attribute to colour on, then the points are coloured using a spectrum
78
* ranging from blue to red (low values to high).
80
* When a classifier's predictions are supplied they are plotted in one
81
* of two ways (depending on whether the class is nominal or numeric).<br>
82
* For nominal class: an error made by a classifier is plotted as a square
83
* in the colour corresponding to the class it predicted.<br>
84
* For numeric class: predictions are plotted as varying sized x's, where
85
* the size of the x is related to the magnitude of the error.
87
* @author Mark Hall (mhall@cs.waikato.ac.nz)
88
* @author Malcolm Ware (mfw4@cs.waikato.ac.nz)
89
* @version $Revision: 1.28 $
91
public class VisualizePanel
92
extends PrintablePanel {
94
/** for serialization */
95
private static final long serialVersionUID = 240108358588153943L;
97
/** Inner class to handle plotting */
98
protected class PlotPanel
99
extends PrintablePanel
100
implements Plot2DCompanion {
102
/** for serialization */
103
private static final long serialVersionUID = -4823674171136494204L;
105
/** The actual generic plotting panel */
106
protected Plot2D m_plot2D = new Plot2D();
108
/** The instances from the master plot */
109
protected Instances m_plotInstances=null;
111
/** The master plot */
112
protected PlotData2D m_originalPlot=null;
114
/** Indexes of the attributes to go on the x and y axis and the attribute
115
to use for colouring and the current shape for drawing */
116
protected int m_xIndex=0;
117
protected int m_yIndex=0;
118
protected int m_cIndex=0;
119
protected int m_sIndex=0;
121
/**the offsets of the axes once label metrics are calculated */
122
private int m_XaxisStart=0;
123
private int m_YaxisStart=0;
124
private int m_XaxisEnd=0;
125
private int m_YaxisEnd=0;
127
/** True if the user is currently dragging a box. */
128
private boolean m_createShape;
130
/** contains all the shapes that have been drawn for these attribs */
131
private FastVector m_shapes;
133
/** contains the points of the shape currently being drawn. */
134
private FastVector m_shapePoints;
136
/** contains the position of the mouse (used for rubberbanding). */
137
private Dimension m_newMousePos;
141
this.setBackground(m_plot2D.getBackground());
142
this.setLayout(new BorderLayout());
143
this.add(m_plot2D, BorderLayout.CENTER);
144
m_plot2D.setPlotCompanion(this);
146
m_createShape = false;
148
m_shapePoints = null;
149
m_newMousePos = new Dimension();
151
this.addMouseListener(new MouseAdapter() {
153
public void mousePressed(MouseEvent e) {
154
if ((e.getModifiers() & MouseEvent.BUTTON1_MASK) == MouseEvent.BUTTON1_MASK) {
157
//do nothing it will get dealt to in the clicked method
159
else if (m_sIndex == 1) {
160
m_createShape = true;
161
m_shapePoints = new FastVector(5);
162
m_shapePoints.addElement(new Double(m_sIndex));
163
m_shapePoints.addElement(new Double(e.getX()));
164
m_shapePoints.addElement(new Double(e.getY()));
165
m_shapePoints.addElement(new Double(e.getX()));
166
m_shapePoints.addElement(new Double(e.getY()));
167
// Graphics g = PlotPanel.this.getGraphics();
168
Graphics g = m_plot2D.getGraphics();
169
g.setColor(Color.black);
170
g.setXORMode(Color.white);
171
g.drawRect(((Double)m_shapePoints.elementAt(1)).intValue(),
172
((Double)m_shapePoints.elementAt(2)).intValue(),
173
((Double)m_shapePoints.elementAt(3)).intValue() -
174
((Double)m_shapePoints.elementAt(1)).intValue(),
175
((Double)m_shapePoints.elementAt(4)).intValue() -
176
((Double)m_shapePoints.elementAt(2)).intValue());
179
//System.out.println("clicked");
181
//System.out.println("clicked");
184
public void mouseClicked(MouseEvent e) {
186
if ((m_sIndex == 2 || m_sIndex == 3) &&
188
(e.getModifiers() & MouseEvent.BUTTON1_MASK) == MouseEvent.BUTTON1_MASK)) {
190
//then it has been started already.
192
Graphics g = m_plot2D.getGraphics();
193
g.setColor(Color.black);
194
g.setXORMode(Color.white);
195
if ((e.getModifiers() & MouseEvent.BUTTON1_MASK) == MouseEvent.BUTTON1_MASK &&
197
m_shapePoints.addElement(new
198
Double(m_plot2D.convertToAttribX(e.getX())));
200
m_shapePoints.addElement(new
201
Double(m_plot2D.convertToAttribY(e.getY())));
203
m_newMousePos.width = e.getX();
204
m_newMousePos.height = e.getY();
205
g.drawLine((int)Math.ceil
206
(m_plot2D.convertToPanelX
207
(((Double)m_shapePoints.
208
elementAt(m_shapePoints.size() - 2)).
212
(m_plot2D.convertToPanelY
213
(((Double)m_shapePoints.
214
elementAt(m_shapePoints.size() - 1)).
216
m_newMousePos.width, m_newMousePos.height);
219
else if (m_sIndex == 3) {
220
//then extend the lines to infinity
221
//(100000 or so should be enough).
222
//the area is selected by where the user right clicks
225
m_createShape = false;
226
if (m_shapePoints.size() >= 5) {
227
double cx = Math.ceil
228
(m_plot2D.convertToPanelX
229
(((Double)m_shapePoints.elementAt
230
(m_shapePoints.size() - 4)).doubleValue()));
232
double cx2 = Math.ceil
233
(m_plot2D.convertToPanelX
234
(((Double)m_shapePoints.elementAt
235
(m_shapePoints.size() - 2)).doubleValue())) -
240
double cy = Math.ceil
242
convertToPanelY(((Double)m_shapePoints.
243
elementAt(m_shapePoints.size() - 3)).
245
double cy2 = Math.ceil
246
(m_plot2D.convertToPanelY(((Double)m_shapePoints.
247
elementAt(m_shapePoints.size() - 1)).
248
doubleValue())) - cy;
252
double cxa = Math.ceil(m_plot2D.convertToPanelX
253
(((Double)m_shapePoints.
256
double cxa2 = Math.ceil(m_plot2D.convertToPanelX
257
(((Double)m_shapePoints.
259
doubleValue())) - cxa;
263
double cya = Math.ceil
264
(m_plot2D.convertToPanelY
265
(((Double)m_shapePoints.elementAt(4)).
267
double cya2 = Math.ceil
268
(m_plot2D.convertToPanelY
269
(((Double)m_shapePoints.elementAt(2)).
270
doubleValue())) - cya;
274
m_shapePoints.setElementAt
275
(new Double(m_plot2D.convertToAttribX(cxa2 + cxa)), 1);
277
m_shapePoints.setElementAt
278
(new Double(m_plot2D.convertToAttribY(cy2 + cy)),
279
m_shapePoints.size() - 1);
281
m_shapePoints.setElementAt
282
(new Double(m_plot2D.convertToAttribX(cx2 + cx)),
283
m_shapePoints.size() - 2);
285
m_shapePoints.setElementAt
286
(new Double(m_plot2D.convertToAttribY(cya2 + cya)), 2);
289
//determine how infinity line should be built
291
cy = Double.POSITIVE_INFINITY;
292
cy2 = Double.NEGATIVE_INFINITY;
293
if (((Double)m_shapePoints.elementAt(1)).
295
((Double)m_shapePoints.elementAt(3)).
297
if (((Double)m_shapePoints.elementAt(2)).
299
((Double)m_shapePoints.elementAt(4)).
301
cy = ((Double)m_shapePoints.elementAt(2)).
305
if (((Double)m_shapePoints.elementAt
306
(m_shapePoints.size() - 2)).doubleValue() >
307
((Double)m_shapePoints.elementAt
308
(m_shapePoints.size() - 4)).doubleValue()) {
309
if (((Double)m_shapePoints.elementAt
310
(m_shapePoints.size() - 3)).
312
((Double)m_shapePoints.elementAt
313
(m_shapePoints.size() - 1)).doubleValue()) {
314
cy2 = ((Double)m_shapePoints.lastElement()).
318
m_shapePoints.addElement(new Double(cy));
319
m_shapePoints.addElement(new Double(cy2));
321
if (!inPolyline(m_shapePoints, m_plot2D.convertToAttribX
323
m_plot2D.convertToAttribY(e.getY()))) {
324
Double tmp = (Double)m_shapePoints.
325
elementAt(m_shapePoints.size() - 2);
326
m_shapePoints.setElementAt
327
(m_shapePoints.lastElement(),
328
m_shapePoints.size() - 2);
329
m_shapePoints.setElementAt
330
(tmp, m_shapePoints.size() - 1);
333
if (m_shapes == null) {
334
m_shapes = new FastVector(4);
336
m_shapes.addElement(m_shapePoints);
338
m_submit.setText("Submit");
339
m_submit.setActionCommand("Submit");
341
m_submit.setEnabled(true);
344
m_shapePoints = null;
345
PlotPanel.this.repaint();
349
//then close the shape
350
m_createShape = false;
351
if (m_shapePoints.size() >= 7) {
352
m_shapePoints.addElement(m_shapePoints.elementAt(1));
353
m_shapePoints.addElement(m_shapePoints.elementAt(2));
354
if (m_shapes == null) {
355
m_shapes = new FastVector(4);
357
m_shapes.addElement(m_shapePoints);
359
m_submit.setText("Submit");
360
m_submit.setActionCommand("Submit");
362
m_submit.setEnabled(true);
364
m_shapePoints = null;
365
PlotPanel.this.repaint();
370
else if ((e.getModifiers() & MouseEvent.BUTTON1_MASK) == MouseEvent.BUTTON1_MASK) {
371
//then this is the first point
372
m_createShape = true;
373
m_shapePoints = new FastVector(17);
374
m_shapePoints.addElement(new Double(m_sIndex));
375
m_shapePoints.addElement(new
376
Double(m_plot2D.convertToAttribX(e.getX()))); //the new point
377
m_shapePoints.addElement(new
378
Double(m_plot2D.convertToAttribY(e.getY())));
379
m_newMousePos.width = e.getX(); //the temp mouse point
380
m_newMousePos.height = e.getY();
382
Graphics g = m_plot2D.getGraphics();
383
g.setColor(Color.black);
384
g.setXORMode(Color.white);
385
g.drawLine((int)Math.ceil
386
(m_plot2D.convertToPanelX(((Double)m_shapePoints.
387
elementAt(1)).doubleValue())),
389
(m_plot2D.convertToPanelY(((Double)m_shapePoints.
390
elementAt(2)).doubleValue())),
391
m_newMousePos.width, m_newMousePos.height);
396
if ((e.getModifiers() & InputEvent.BUTTON1_MASK) ==
397
InputEvent.BUTTON1_MASK) {
399
m_plot2D.searchPoints(e.getX(),e.getY(), false);
401
m_plot2D.searchPoints(e.getX(), e.getY(), true);
407
public void mouseReleased(MouseEvent e) {
410
if (((Double)m_shapePoints.elementAt(0)).intValue() == 1) {
411
m_createShape = false;
412
Graphics g = m_plot2D.getGraphics();
413
g.setColor(Color.black);
414
g.setXORMode(Color.white);
415
g.drawRect(((Double)m_shapePoints.elementAt(1)).
417
((Double)m_shapePoints.elementAt(2)).intValue(),
418
((Double)m_shapePoints.elementAt(3)).intValue() -
419
((Double)m_shapePoints.elementAt(1)).intValue(),
420
((Double)m_shapePoints.elementAt(4)).intValue() -
421
((Double)m_shapePoints.elementAt(2)).intValue());
424
if (checkPoints(((Double)m_shapePoints.elementAt(1)).
426
((Double)m_shapePoints.elementAt(2)).
428
checkPoints(((Double)m_shapePoints.elementAt(3)).
430
((Double)m_shapePoints.elementAt(4)).
432
//then the points all land on the screen
433
//now do special check for the rectangle
434
if (((Double)m_shapePoints.elementAt(1)).doubleValue() <
435
((Double)m_shapePoints.elementAt(3)).doubleValue()
437
((Double)m_shapePoints.elementAt(2)).doubleValue() <
438
((Double)m_shapePoints.elementAt(4)).doubleValue()) {
439
//then the rectangle is valid
440
if (m_shapes == null) {
441
m_shapes = new FastVector(2);
443
m_shapePoints.setElementAt(new
444
Double(m_plot2D.convertToAttribX(((Double)m_shapePoints.
447
m_shapePoints.setElementAt(new
448
Double(m_plot2D.convertToAttribY(((Double)m_shapePoints.
451
m_shapePoints.setElementAt(new
452
Double(m_plot2D.convertToAttribX(((Double)m_shapePoints.
455
m_shapePoints.setElementAt(new
456
Double(m_plot2D.convertToAttribY(((Double)m_shapePoints.
460
m_shapes.addElement(m_shapePoints);
462
m_submit.setText("Submit");
463
m_submit.setActionCommand("Submit");
465
m_submit.setEnabled(true);
467
PlotPanel.this.repaint();
470
m_shapePoints = null;
476
this.addMouseMotionListener(new MouseMotionAdapter() {
477
public void mouseDragged(MouseEvent e) {
478
//check if the user is dragging a box
480
if (((Double)m_shapePoints.elementAt(0)).intValue() == 1) {
481
Graphics g = m_plot2D.getGraphics();
482
g.setColor(Color.black);
483
g.setXORMode(Color.white);
484
g.drawRect(((Double)m_shapePoints.elementAt(1)).intValue(),
485
((Double)m_shapePoints.elementAt(2)).intValue(),
486
((Double)m_shapePoints.elementAt(3)).intValue() -
487
((Double)m_shapePoints.elementAt(1)).intValue(),
488
((Double)m_shapePoints.elementAt(4)).intValue() -
489
((Double)m_shapePoints.elementAt(2)).intValue());
491
m_shapePoints.setElementAt(new Double(e.getX()), 3);
492
m_shapePoints.setElementAt(new Double(e.getY()), 4);
494
g.drawRect(((Double)m_shapePoints.elementAt(1)).intValue(),
495
((Double)m_shapePoints.elementAt(2)).intValue(),
496
((Double)m_shapePoints.elementAt(3)).intValue() -
497
((Double)m_shapePoints.elementAt(1)).intValue(),
498
((Double)m_shapePoints.elementAt(4)).intValue() -
499
((Double)m_shapePoints.elementAt(2)).intValue());
505
public void mouseMoved(MouseEvent e) {
507
if (((Double)m_shapePoints.elementAt(0)).intValue() == 2 ||
508
((Double)m_shapePoints.elementAt(0)).intValue() == 3) {
509
Graphics g = m_plot2D.getGraphics();
510
g.setColor(Color.black);
511
g.setXORMode(Color.white);
512
g.drawLine((int)Math.ceil(m_plot2D.convertToPanelX
513
(((Double)m_shapePoints.elementAt
514
(m_shapePoints.size() - 2)).
516
(int)Math.ceil(m_plot2D.convertToPanelY
517
(((Double)m_shapePoints.elementAt
518
(m_shapePoints.size() - 1)).
520
m_newMousePos.width, m_newMousePos.height);
522
m_newMousePos.width = e.getX();
523
m_newMousePos.height = e.getY();
525
g.drawLine((int)Math.ceil(m_plot2D.convertToPanelX
526
(((Double)m_shapePoints.elementAt
527
(m_shapePoints.size() - 2)).
529
(int)Math.ceil(m_plot2D.convertToPanelY
530
(((Double)m_shapePoints.elementAt
531
(m_shapePoints.size() - 1)).
533
m_newMousePos.width, m_newMousePos.height);
540
m_submit.addActionListener(new ActionListener() {
541
public void actionPerformed(ActionEvent e) {
543
if (e.getActionCommand().equals("Submit")) {
544
if (m_splitListener != null && m_shapes != null) {
545
//then send the split to the listener
546
Instances sub_set1 = new Instances(m_plot2D.getMasterPlot().
547
m_plotInstances, 500);
548
Instances sub_set2 = new Instances(m_plot2D.getMasterPlot().
549
m_plotInstances, 500);
551
if (m_plot2D.getMasterPlot().
552
m_plotInstances != null) {
554
for (int noa = 0 ; noa < m_plot2D.getMasterPlot().
555
m_plotInstances.numInstances(); noa++) {
556
if (!m_plot2D.getMasterPlot().
557
m_plotInstances.instance(noa).isMissing(m_xIndex) &&
558
!m_plot2D.getMasterPlot().
559
m_plotInstances.instance(noa).isMissing(m_yIndex)){
561
if (inSplit(m_plot2D.getMasterPlot().
562
m_plotInstances.instance(noa))) {
563
sub_set1.add(m_plot2D.getMasterPlot().
564
m_plotInstances.instance(noa));
567
sub_set2.add(m_plot2D.getMasterPlot().
568
m_plotInstances.instance(noa));
572
FastVector tmp = m_shapes;
574
m_splitListener.userDataEvent(new
575
VisualizePanelEvent(tmp, sub_set1, sub_set2, m_xIndex,
579
else if (m_shapes != null &&
580
m_plot2D.getMasterPlot().m_plotInstances != null) {
581
Instances sub_set1 = new Instances(m_plot2D.getMasterPlot().
582
m_plotInstances, 500);
584
for (int noa = 0 ; noa < m_plot2D.getMasterPlot().
585
m_plotInstances.numInstances(); noa++) {
586
if (inSplit(m_plot2D.getMasterPlot().
587
m_plotInstances.instance(noa))) {
588
sub_set1.add(m_plot2D.getMasterPlot().
589
m_plotInstances.instance(noa));
595
int [] nSizes = null;
596
int [] nTypes = null;
600
if (m_originalPlot == null) {
601
//this sets these instances as the instances
603
m_originalPlot = m_plot2D.getMasterPlot();
607
nTypes = new int[count];
608
nSizes = new int[count];
610
for (int noa = 0; noa < m_plot2D.getMasterPlot().
611
m_plotInstances.numInstances();
613
if (inSplit(m_plot2D.getMasterPlot().
614
m_plotInstances.instance(noa))) {
616
nTypes[count] = m_plot2D.getMasterPlot().
618
nSizes[count] = m_plot2D.getMasterPlot().
626
PlotData2D newPlot = new PlotData2D(sub_set1);
629
newPlot.setShapeSize(nSizes);
630
newPlot.setShapeType(nTypes);
632
m_plot2D.removeAllPlots();
634
VisualizePanel.this.addPlot(newPlot);
635
} catch (Exception ex) {
636
System.err.println(ex);
637
ex.printStackTrace();
641
VisualizePanel.this.setXIndex(x);
642
VisualizePanel.this.setYIndex(y);
643
} catch(Exception er) {
644
System.out.println("Error : " + er);
645
// System.out.println("Part of user input so had to" +
650
else if (e.getActionCommand().equals("Reset")) {
654
m_plot2D.removeAllPlots();
656
VisualizePanel.this.addPlot(m_originalPlot);
657
} catch (Exception ex) {
658
System.err.println(ex);
659
ex.printStackTrace();
663
VisualizePanel.this.setXIndex(x);
664
VisualizePanel.this.setYIndex(y);
665
} catch(Exception er) {
666
System.out.println("Error : " + er);
672
m_cancel.addActionListener(new ActionListener() {
673
public void actionPerformed(ActionEvent e) {
675
PlotPanel.this.repaint();
682
* @return The FastVector containing all the shapes.
684
public FastVector getShapes() {
690
* Sets the list of shapes to empty and also cancels
691
* the current shape being drawn (if applicable).
693
public void cancelShapes() {
695
if (m_splitListener == null) {
696
m_submit.setText("Reset");
697
m_submit.setActionCommand("Reset");
699
if (m_originalPlot == null ||
700
m_originalPlot.m_plotInstances == m_plotInstances) {
701
m_submit.setEnabled(false);
704
m_submit.setEnabled(true);
708
m_submit.setEnabled(false);
711
m_createShape = false;
712
m_shapePoints = null;
718
* This can be used to set the shapes that should appear.
719
* @param v The list of shapes.
721
public void setShapes(FastVector v) {
722
//note that this method should be fine for doubles,
723
//but anything else that uses something other than doubles
724
//(or uneditable objects) could have unsafe copies.
727
m_shapes = new FastVector(v.size());
728
for (int noa = 0; noa < v.size(); noa++) {
729
temp = new FastVector(((FastVector)v.elementAt(noa)).size());
730
m_shapes.addElement(temp);
731
for (int nob = 0; nob < ((FastVector)v.elementAt(noa)).size()
734
temp.addElement(((FastVector)v.elementAt(noa)).elementAt(nob));
746
* This will check the values of the screen points passed and make sure
747
* that they land on the screen
748
* @param x1 The x coord.
749
* @param y1 The y coord.
750
* @return true if the point would land on the screen
752
private boolean checkPoints(double x1, double y1) {
753
if (x1 < 0 || x1 > this.getSize().width || y1 < 0
754
|| y1 > this.getSize().height) {
761
* This will check if an instance is inside or outside of the current
763
* @param i The instance to check.
764
* @return True if 'i' falls inside the shapes, false otherwise.
766
public boolean inSplit(Instance i) {
767
//this will check if the instance lies inside the shapes or not
769
if (m_shapes != null) {
771
double x1, y1, x2, y2;
772
for (int noa = 0; noa < m_shapes.size(); noa++) {
773
stmp = (FastVector)m_shapes.elementAt(noa);
774
if (((Double)stmp.elementAt(0)).intValue() == 1) {
776
x1 = ((Double)stmp.elementAt(1)).doubleValue();
777
y1 = ((Double)stmp.elementAt(2)).doubleValue();
778
x2 = ((Double)stmp.elementAt(3)).doubleValue();
779
y2 = ((Double)stmp.elementAt(4)).doubleValue();
780
if (i.value(m_xIndex) >= x1 && i.value(m_xIndex) <= x2 &&
781
i.value(m_yIndex) <= y1 && i.value(m_yIndex) >= y2) {
782
//then is inside split so return true;
786
else if (((Double)stmp.elementAt(0)).intValue() == 2) {
788
if (inPoly(stmp, i.value(m_xIndex), i.value(m_yIndex))) {
792
else if (((Double)stmp.elementAt(0)).intValue() == 3) {
794
if (inPolyline(stmp, i.value(m_xIndex), i.value(m_yIndex))) {
804
* Checks to see if the coordinate passed is inside the ployline
805
* passed, Note that this is done using attribute values and not there
806
* respective screen values.
807
* @param ob The polyline.
808
* @param x The x coord.
809
* @param y The y coord.
810
* @return True if it falls inside the polyline, false otherwise.
812
private boolean inPolyline(FastVector ob, double x, double y) {
813
//this works similar to the inPoly below except that
814
//the first and last lines are treated as extending infinite in one
816
//then infinitly in the x dirction their is a line that will
817
//normaly be infinite but
818
//can be finite in one or both directions
823
double x1, y1, x2, y2;
825
for (int noa = 1; noa < ob.size() - 4; noa+= 2) {
826
y1 = ((Double)ob.elementAt(noa+1)).doubleValue();
827
y2 = ((Double)ob.elementAt(noa+3)).doubleValue();
828
x1 = ((Double)ob.elementAt(noa)).doubleValue();
829
x2 = ((Double)ob.elementAt(noa+2)).doubleValue();
831
//System.err.println(y1 + " " + y2 + " " + x1 + " " + x2);
834
if (noa == 1 && noa == ob.size() - 6) {
835
//then do special test first and last edge
837
change = (y - y1) / vecy;
838
if (vecx * change + x1 >= x) {
845
if ((y < y2 && vecy > 0) || (y > y2 && vecy < 0)) {
846
//now just determine intersection or not
847
change = (y - y1) / vecy;
848
if (vecx * change + x1 >= x) {
849
//then intersection on horiz
854
else if (noa == ob.size() - 6) {
855
//then do special test on last edge
856
if ((y <= y1 && vecy < 0) || (y >= y1 && vecy > 0)) {
857
change = (y - y1) / vecy;
858
if (vecx * change + x1 >= x) {
863
else if ((y1 <= y && y < y2) || (y2 < y && y <= y1)) {
864
//then continue tests.
866
//then lines are parallel stop tests in
867
//ofcourse it should never make it this far
870
change = (y - y1) / vecy;
871
if (vecx * change + x1 >= x) {
872
//then intersects on horiz
879
//now check for intersection with the infinity line
880
y1 = ((Double)ob.elementAt(ob.size() - 2)).doubleValue();
881
y2 = ((Double)ob.elementAt(ob.size() - 1)).doubleValue();
885
if (y1 >= y && y > y2) {
890
//then the line segment is inverted
891
if (y1 >= y || y > y2) {
896
if ((countx % 2) == 1) {
906
* This checks to see if The coordinate passed is inside
907
* the polygon that was passed.
908
* @param ob The polygon.
909
* @param x The x coord.
910
* @param y The y coord.
911
* @return True if the coordinate is in the polygon, false otherwise.
913
private boolean inPoly(FastVector ob, double x, double y) {
914
//brief on how this works
915
//it draws a line horizontally from the point to the right (infinitly)
916
//it then sees how many lines of the polygon intersect this,
917
//if it is even then the point is
918
// outside the polygon if it's odd then it's inside the polygon
922
double x1, y1, x2, y2;
923
for (int noa = 1; noa < ob.size() - 2; noa += 2) {
924
y1 = ((Double)ob.elementAt(noa+1)).doubleValue();
925
y2 = ((Double)ob.elementAt(noa+3)).doubleValue();
926
if ((y1 <= y && y < y2) || (y2 < y && y <= y1)) {
927
//then continue tests.
930
//then lines are parallel stop tests for this line
933
x1 = ((Double)ob.elementAt(noa)).doubleValue();
934
x2 = ((Double)ob.elementAt(noa+2)).doubleValue();
936
change = (y - y1) / vecy;
937
if (vecx * change + x1 >= x) {
938
//then add to count as an intersected line
944
if ((count % 2) == 1) {
945
//then lies inside polygon
946
//System.out.println("in");
950
//System.out.println("out");
953
//System.out.println("WHAT?!?!?!?!!?!??!?!");
958
* Set level of jitter and repaint the plot using the new jitter value
959
* @param j the level of jitter
961
public void setJitter(int j) {
962
m_plot2D.setJitter(j);
966
* Set the index of the attribute to go on the x axis
967
* @param x the index of the attribute to use on the x axis
969
public void setXindex(int x) {
971
// this just ensures that the shapes get disposed of
972
//if the attribs change
977
m_plot2D.setXindex(x);
985
* Set the index of the attribute to go on the y axis
986
* @param y the index of the attribute to use on the y axis
988
public void setYindex(int y) {
990
// this just ensures that the shapes get disposed of
991
//if the attribs change
996
m_plot2D.setYindex(y);
1004
* Set the index of the attribute to use for colouring
1005
* @param c the index of the attribute to use for colouring
1007
public void setCindex(int c) {
1009
m_plot2D.setCindex(c);
1010
if (m_showAttBars) {
1011
m_attrib.setCindex(c, m_plot2D.getMaxC(), m_plot2D.getMinC());
1013
m_classPanel.setCindex(c);
1018
* Set the index of the attribute to use for the shape.
1019
* @param s the index of the attribute to use for the shape
1021
public void setSindex(int s) {
1022
if (s != m_sIndex) {
1023
m_shapePoints = null;
1024
m_createShape = false;
1031
* Clears all existing plots and sets a new master plot
1032
* @param newPlot the new master plot
1033
* @exception Exception if plot could not be added
1035
public void setMasterPlot(PlotData2D newPlot) throws Exception {
1036
m_plot2D.removeAllPlots();
1037
this.addPlot(newPlot);
1041
* Adds a plot. If there are no plots so far this plot becomes
1042
* the master plot and, if it has a custom colour defined then
1043
* the class panel is removed.
1044
* @param newPlot the plot to add.
1045
* @exception Exception if plot could not be added
1047
public void addPlot(PlotData2D newPlot) throws Exception {
1048
if (m_plot2D.getPlots().size() == 0) {
1049
m_plot2D.addPlot(newPlot);
1050
if (m_plotSurround.getComponentCount() > 1 &&
1051
m_plotSurround.getComponent(1) == m_attrib &&
1054
m_attrib.setInstances(newPlot.m_plotInstances);
1055
m_attrib.setCindex(0);m_attrib.setX(0); m_attrib.setY(0);
1056
} catch (Exception ex) {
1057
// more attributes than the panel can handle?
1058
// Due to hard coded constraints in GridBagLayout
1059
m_plotSurround.remove(m_attrib);
1060
System.err.println("Warning : data contains more attributes "
1061
+"than can be displayed as attribute bars.");
1062
if (m_Log != null) {
1063
m_Log.logMessage("Warning : data contains more attributes "
1064
+"than can be displayed as attribute bars.");
1067
} else if (m_showAttBars) {
1069
m_attrib.setInstances(newPlot.m_plotInstances);
1070
m_attrib.setCindex(0);m_attrib.setX(0); m_attrib.setY(0);
1071
GridBagConstraints constraints = new GridBagConstraints();
1072
constraints.fill = GridBagConstraints.BOTH;
1073
constraints.insets = new Insets(0, 0, 0, 0);
1074
constraints.gridx=4;constraints.gridy=0;constraints.weightx=1;
1075
constraints.gridwidth=1;constraints.gridheight=1;
1076
constraints.weighty=5;
1077
m_plotSurround.add(m_attrib, constraints);
1078
} catch (Exception ex) {
1079
System.err.println("Warning : data contains more attributes "
1080
+"than can be displayed as attribute bars.");
1081
if (m_Log != null) {
1082
m_Log.logMessage("Warning : data contains more attributes "
1083
+"than can be displayed as attribute bars.");
1087
m_classPanel.setInstances(newPlot.m_plotInstances);
1089
plotReset(newPlot.m_plotInstances, newPlot.getCindex());
1090
if (newPlot.m_useCustomColour) {
1091
VisualizePanel.this.remove(m_classSurround);
1093
m_legendPanel.setPlotList(m_plot2D.getPlots());
1094
m_ColourCombo.setEnabled(false);
1097
if (!newPlot.m_useCustomColour) {
1098
VisualizePanel.this.add(m_classSurround, BorderLayout.SOUTH);
1099
m_ColourCombo.setEnabled(true);
1101
if (m_plot2D.getPlots().size() == 1) {
1104
m_plot2D.addPlot(newPlot);
1105
m_legendPanel.setPlotList(m_plot2D.getPlots());
1110
* Remove the attibute panel and replace it with the legend panel
1112
protected void switchToLegend() {
1114
if (m_plotSurround.getComponentCount() > 1 &&
1115
m_plotSurround.getComponent(1) == m_attrib) {
1116
m_plotSurround.remove(m_attrib);
1119
if (m_plotSurround.getComponentCount() > 1 &&
1120
m_plotSurround.getComponent(1) == m_legendPanel) {
1124
GridBagConstraints constraints = new GridBagConstraints();
1125
constraints.fill = GridBagConstraints.BOTH;
1126
constraints.insets = new Insets(0, 0, 0, 0);
1127
constraints.gridx=4;constraints.gridy=0;constraints.weightx=1;
1128
constraints.gridwidth=1;constraints.gridheight=1;
1129
constraints.weighty=5;
1130
m_plotSurround.add(m_legendPanel, constraints);
1132
m_ShapeCombo.setEnabled(false);
1136
* Reset the visualize panel's buttons and the plot panels instances
1138
* @param inst the data
1139
* @param cIndex the color index
1141
private void plotReset(Instances inst, int cIndex) {
1142
if (m_splitListener == null) {
1143
m_submit.setText("Reset");
1144
m_submit.setActionCommand("Reset");
1145
//if (m_origInstances == null || m_origInstances == inst) {
1146
if (m_originalPlot == null || m_originalPlot.m_plotInstances == inst) {
1147
m_submit.setEnabled(false);
1150
m_submit.setEnabled(true);
1154
m_submit.setEnabled(false);
1157
m_plotInstances = inst;
1158
if (m_splitListener != null) {
1159
m_plotInstances.randomize(new Random());
1168
* Set a list of colours to use for plotting points
1169
* @param cols a list of java.awt.Colors
1171
public void setColours(FastVector cols) {
1172
m_plot2D.setColours(cols);
1177
* This will draw the shapes created onto the panel.
1178
* For best visual, this should be the first thing to be drawn
1179
* (and it currently is).
1180
* @param gx The graphics context.
1182
private void drawShapes(Graphics gx) {
1183
//FastVector tmp = m_plot.getShapes();
1185
if (m_shapes != null) {
1188
for (int noa = 0; noa < m_shapes.size(); noa++) {
1189
stmp = (FastVector)m_shapes.elementAt(noa);
1190
if (((Double)stmp.elementAt(0)).intValue() == 1) {
1192
x1 = (int)m_plot2D.convertToPanelX(((Double)stmp.elementAt(1)).
1194
y1 = (int)m_plot2D.convertToPanelY(((Double)stmp.elementAt(2)).
1196
x2 = (int)m_plot2D.convertToPanelX(((Double)stmp.elementAt(3)).
1198
y2 = (int)m_plot2D.convertToPanelY(((Double)stmp.elementAt(4)).
1201
gx.setColor(Color.gray);
1202
gx.fillRect(x1, y1, x2 - x1, y2 - y1);
1203
gx.setColor(Color.black);
1204
gx.drawRect(x1, y1, x2 - x1, y2 - y1);
1207
else if (((Double)stmp.elementAt(0)).intValue() == 2) {
1210
ar1 = getXCoords(stmp);
1211
ar2 = getYCoords(stmp);
1212
gx.setColor(Color.gray);
1213
gx.fillPolygon(ar1, ar2, (stmp.size() - 1) / 2);
1214
gx.setColor(Color.black);
1215
gx.drawPolyline(ar1, ar2, (stmp.size() - 1) / 2);
1217
else if (((Double)stmp.elementAt(0)).intValue() == 3) {
1220
FastVector tmp = makePolygon(stmp);
1221
ar1 = getXCoords(tmp);
1222
ar2 = getYCoords(tmp);
1224
gx.setColor(Color.gray);
1225
gx.fillPolygon(ar1, ar2, (tmp.size() - 1) / 2);
1226
gx.setColor(Color.black);
1227
gx.drawPolyline(ar1, ar2, (tmp.size() - 1) / 2);
1232
if (m_shapePoints != null) {
1233
//then the current image needs to be refreshed
1234
if (((Double)m_shapePoints.elementAt(0)).intValue() == 2 ||
1235
((Double)m_shapePoints.elementAt(0)).intValue() == 3) {
1236
gx.setColor(Color.black);
1237
gx.setXORMode(Color.white);
1239
ar1 = getXCoords(m_shapePoints);
1240
ar2 = getYCoords(m_shapePoints);
1241
gx.drawPolyline(ar1, ar2, (m_shapePoints.size() - 1) / 2);
1242
m_newMousePos.width = (int)Math.ceil
1243
(m_plot2D.convertToPanelX(((Double)m_shapePoints.elementAt
1244
(m_shapePoints.size() - 2)).doubleValue()));
1246
m_newMousePos.height = (int)Math.ceil
1247
(m_plot2D.convertToPanelY(((Double)m_shapePoints.elementAt
1248
(m_shapePoints.size() - 1)).doubleValue()));
1250
gx.drawLine((int)Math.ceil
1251
(m_plot2D.convertToPanelX(((Double)m_shapePoints.elementAt
1252
(m_shapePoints.size() - 2)).
1254
(int)Math.ceil(m_plot2D.convertToPanelY
1255
(((Double)m_shapePoints.elementAt
1256
(m_shapePoints.size() - 1)).
1258
m_newMousePos.width, m_newMousePos.height);
1265
* This is called for polylines to see where there two lines that
1266
* extend to infinity cut the border of the view.
1267
* @param x1 an x point along the line
1268
* @param y1 the accompanying y point.
1269
* @param x2 The x coord of the end point of the line.
1270
* @param y2 The y coord of the end point of the line.
1271
* @param x 0 or the width of the border line if it has one.
1272
* @param y 0 or the height of the border line if it has one.
1273
* @param offset The offset for the border line (either for x or y
1274
* dependant on which one doesn't change).
1275
* @return double array that contains the coordinate for the point
1276
* that the polyline cuts the border (which ever side that may be).
1278
private double[] lineIntersect(double x1, double y1, double x2, double y2,
1279
double x, double y, double offset) {
1280
//the first 4 params are thestart and end points of a line
1281
//the next param is either 0 for no change in x or change in x,
1282
//the next param is the same for y
1283
//the final 1 is the offset for either x or y (which ever has no change)
1286
double xn = -100, yn = -100;
1289
if ((x1 <= offset && offset < x2) || (x1 >= offset && offset > x2)) {
1292
change = (offset - x2) / xval;
1293
yn = (y1 - y2) * change + y2;
1294
if (0 <= yn && yn <= y) {
1305
if ((y1 <= offset && offset < y2) || (y1 >= offset && offset > y2)) {
1308
change = (offset - y2) / yval;
1309
xn = (x1 - x2) * change + x2;
1310
if (0 <= xn && xn <= x) {
1319
double[] ret = new double[2];
1327
* This will convert a polyline to a polygon for drawing purposes
1328
* So that I can simply use the polygon drawing function.
1329
* @param v The polyline to convert.
1330
* @return A FastVector containing the polygon.
1332
private FastVector makePolygon(FastVector v) {
1333
FastVector building = new FastVector(v.size() + 10);
1334
double x1, y1, x2, y2;
1335
int edge1 = 0, edge2 = 0;
1336
for (int noa = 0; noa < v.size() - 2; noa++) {
1337
building.addElement(new Double(((Double)v.elementAt(noa)).
1341
//now clip the lines
1342
double[] new_coords;
1343
//note lineIntersect , expects the values to have been converted to
1345
//note the first point passed is the one that gets shifted.
1346
x1 = m_plot2D.convertToPanelX(((Double)v.elementAt(1)).doubleValue());
1347
y1 = m_plot2D.convertToPanelY(((Double)v.elementAt(2)).doubleValue());
1348
x2 = m_plot2D.convertToPanelX(((Double)v.elementAt(3)).doubleValue());
1349
y2 = m_plot2D.convertToPanelY(((Double)v.elementAt(4)).doubleValue());
1353
new_coords = lineIntersect(x1, y1, x2, y2, 0, this.getHeight(), 0);
1355
if (new_coords[0] < 0) {
1359
new_coords = lineIntersect(x1, y1, x2, y2, this.getWidth(), 0, 0);
1364
new_coords = lineIntersect(x1, y1, x2, y2, this.getWidth(), 0,
1370
else if (x1 > this.getWidth()) {
1372
new_coords = lineIntersect(x1, y1, x2, y2, 0, this.getHeight(),
1375
if (new_coords[0] < 0) {
1379
new_coords = lineIntersect(x1, y1, x2, y2, this.getWidth(), 0, 0);
1384
new_coords = lineIntersect(x1, y1, x2, y2, this.getWidth(), 0,
1392
new_coords = lineIntersect(x1, y1, x2, y2, this.getWidth(), 0, 0);
1397
new_coords = lineIntersect(x1, y1, x2, y2, this.getWidth(), 0,
1402
building.setElementAt(new
1403
Double(m_plot2D.convertToAttribX(new_coords[0])), 1);
1404
building.setElementAt(new
1405
Double(m_plot2D.convertToAttribY(new_coords[1])), 2);
1407
x1 = m_plot2D.convertToPanelX(((Double)v.elementAt(v.size() - 4)).
1409
y1 = m_plot2D.convertToPanelY(((Double)v.elementAt(v.size() - 3)).
1411
x2 = m_plot2D.convertToPanelX(((Double)v.elementAt(v.size() - 6)).
1413
y2 = m_plot2D.convertToPanelY(((Double)v.elementAt(v.size() - 5)).
1418
new_coords = lineIntersect(x1, y1, x2, y2, 0, this.getHeight(), 0);
1420
if (new_coords[0] < 0) {
1424
new_coords = lineIntersect(x1, y1, x2, y2, this.getWidth(), 0, 0);
1429
new_coords = lineIntersect(x1, y1, x2, y2, this.getWidth(), 0,
1435
else if (x1 > this.getWidth()) {
1437
new_coords = lineIntersect(x1, y1, x2, y2, 0, this.getHeight(),
1440
if (new_coords[0] < 0) {
1444
new_coords = lineIntersect(x1, y1, x2, y2, this.getWidth(), 0, 0);
1449
new_coords = lineIntersect(x1, y1, x2, y2, this.getWidth(), 0,
1457
new_coords = lineIntersect(x1, y1, x2, y2, this.getWidth(), 0, 0);
1462
new_coords = lineIntersect(x1, y1, x2, y2, this.getWidth(), 0,
1467
building.setElementAt(new
1468
Double(m_plot2D.convertToAttribX(new_coords[0])), building.size() - 2);
1469
building.setElementAt(new
1470
Double(m_plot2D.convertToAttribY(new_coords[1])), building.size() - 1);
1473
//trust me this complicated piece of code will
1474
//determine what points on the boundary of the view to add to the polygon
1477
xp = this.getWidth() * ((edge2 & 1) ^ ((edge2 & 2) / 2));
1478
yp = this.getHeight() * ((edge2 & 2) / 2);
1479
//System.out.println(((-1 + 4) % 4) + " hoi");
1481
if (inPolyline(v, m_plot2D.convertToAttribX(xp),
1482
m_plot2D.convertToAttribY(yp))) {
1483
//then add points in a clockwise direction
1484
building.addElement(new Double(m_plot2D.convertToAttribX(xp)));
1485
building.addElement(new Double(m_plot2D.convertToAttribY(yp)));
1486
for (int noa = (edge2 + 1) % 4; noa != edge1; noa = (noa + 1) % 4) {
1487
xp = this.getWidth() * ((noa & 1) ^ ((noa & 2) / 2));
1488
yp = this.getHeight() * ((noa & 2) / 2);
1489
building.addElement(new Double(m_plot2D.convertToAttribX(xp)));
1490
building.addElement(new Double(m_plot2D.convertToAttribY(yp)));
1494
xp = this.getWidth() * ((edge2 & 2) / 2);
1495
yp = this.getHeight() * (1 & ~((edge2 & 1) ^ ((edge2 & 2) / 2)));
1496
if (inPolyline(v, m_plot2D.convertToAttribX(xp),
1497
m_plot2D.convertToAttribY(yp))) {
1498
//then add points in anticlockwise direction
1499
building.addElement(new Double(m_plot2D.convertToAttribX(xp)));
1500
building.addElement(new Double(m_plot2D.convertToAttribY(yp)));
1501
for (int noa = (edge2 + 3) % 4; noa != edge1; noa = (noa + 3) % 4) {
1502
xp = this.getWidth() * ((noa & 2) / 2);
1503
yp = this.getHeight() * (1 & ~((noa & 1) ^ ((noa & 2) / 2)));
1504
building.addElement(new Double(m_plot2D.convertToAttribX(xp)));
1505
building.addElement(new Double(m_plot2D.convertToAttribY(yp)));
1513
* This will extract from a polygon shape its x coodrdinates
1514
* so that an awt.Polygon can be created.
1515
* @param v The polygon shape.
1516
* @return an int array containing the screen x coords for the polygon.
1518
private int[] getXCoords(FastVector v) {
1519
int cach = (v.size() - 1) / 2;
1520
int[] ar = new int[cach];
1521
for (int noa = 0; noa < cach; noa ++) {
1522
ar[noa] = (int)m_plot2D.convertToPanelX(((Double)v.elementAt(noa * 2 +
1529
* This will extract from a polygon shape its y coordinates
1530
* so that an awt.Polygon can be created.
1531
* @param v The polygon shape.
1532
* @return an int array containing the screen y coords for the polygon.
1534
private int[] getYCoords(FastVector v) {
1535
int cach = (v.size() - 1) / 2;
1536
int[] ar = new int[cach];
1537
for (int noa = 0; noa < cach; noa ++) {
1538
ar[noa] = (int)m_plot2D.
1539
convertToPanelY(((Double)v.elementAt(noa * 2 + 2)).
1546
* Renders the polygons if necessary
1547
* @param gx the graphics context
1549
public void prePlot(Graphics gx) {
1550
super.paintComponent(gx);
1551
if (m_plotInstances != null) {
1552
drawShapes(gx); // will be in paintComponent of ShapePlot2D
1559
/** default colours for colouring discrete class */
1560
protected Color [] m_DefaultColors = {Color.blue,
1565
new Color(255, 0, 255),
1567
new Color(255, 0, 0),
1568
new Color(0, 255, 0),
1571
/** Lets the user select the attribute for the x axis */
1572
protected JComboBox m_XCombo = new JComboBox();
1574
/** Lets the user select the attribute for the y axis */
1575
protected JComboBox m_YCombo = new JComboBox();
1577
/** Lets the user select the attribute to use for colouring */
1578
protected JComboBox m_ColourCombo = new JComboBox();
1580
/** Lets the user select the shape they want to create for instance
1582
protected JComboBox m_ShapeCombo = new JComboBox();
1584
/** Button for the user to enter the splits. */
1585
protected JButton m_submit = new JButton("Submit");
1587
/** Button for the user to remove all splits. */
1588
protected JButton m_cancel = new JButton("Clear");
1590
/** Button for the user to open the visualized set of instances */
1591
protected JButton m_openBut = new JButton("Open");
1593
/** Button for the user to save the visualized set of instances */
1594
protected JButton m_saveBut = new JButton("Save");
1596
/** Stop the combos from growing out of control */
1597
private Dimension COMBO_SIZE = new Dimension(250, m_saveBut
1598
.getPreferredSize().height);
1600
/** file chooser for saving instances */
1601
protected JFileChooser m_FileChooser
1602
= new JFileChooser(new File(System.getProperty("user.dir")));
1604
/** Filter to ensure only arff files are selected */
1605
protected FileFilter m_ArffFilter =
1606
new ExtensionFileFilter(Instances.FILE_EXTENSION, "Arff data files");
1608
/** Label for the jitter slider */
1609
protected JLabel m_JitterLab= new JLabel("Jitter",SwingConstants.RIGHT);
1611
/** The jitter slider */
1612
protected JSlider m_Jitter = new JSlider(0,50,0);
1614
/** The panel that displays the plot */
1615
protected PlotPanel m_plot = new PlotPanel();
1617
/** The panel that displays the attributes , using color to represent
1618
* another attribute. */
1619
protected AttributePanel m_attrib = new AttributePanel();
1621
/** The panel that displays legend info if there is more than one plot */
1622
protected LegendPanel m_legendPanel = new LegendPanel();
1624
/** Panel that surrounds the plot panel with a titled border */
1625
protected JPanel m_plotSurround = new JPanel();
1627
/** Panel that surrounds the class panel with a titled border */
1628
protected JPanel m_classSurround = new JPanel();
1630
/** An optional listener that we will inform when ComboBox selections
1632
protected ActionListener listener = null;
1634
/** An optional listener that we will inform when the user creates a
1635
* split to seperate instances. */
1636
protected VisualizePanelListener m_splitListener = null;
1638
/** The name of the plot (not currently displayed, but can be used
1639
in the containing Frame or Panel) */
1640
protected String m_plotName = "";
1642
/** The panel that displays the legend for the colouring attribute */
1643
protected ClassPanel m_classPanel = new ClassPanel();
1645
/** The list of the colors used */
1646
protected FastVector m_colorList;
1648
/** These hold the names of preferred columns to visualize on---if the
1649
user has defined them in the Visualize.props file */
1650
protected String m_preferredXDimension = null;
1651
protected String m_preferredYDimension = null;
1652
protected String m_preferredColourDimension = null;
1654
/** Show the attribute bar panel */
1655
protected boolean m_showAttBars = true;
1658
protected Logger m_Log;
1661
* Sets the Logger to receive informational messages
1663
* @param newLog the Logger that will now get info messages
1665
public void setLog(Logger newLog) {
1670
* This constructor allows a VisualizePanelListener to be set.
1672
* @param ls the listener to use
1674
public VisualizePanel(VisualizePanelListener ls) {
1676
m_splitListener = ls;
1680
* Set the properties for the VisualizePanel
1682
* @param relationName the name of the relation, can be null
1684
private void setProperties(String relationName) {
1685
if (VisualizeUtils.VISUALIZE_PROPERTIES != null) {
1686
String thisClass = this.getClass().getName();
1687
if (relationName == null) {
1689
String showAttBars = thisClass+".displayAttributeBars";
1691
String val = VisualizeUtils.VISUALIZE_PROPERTIES.
1692
getProperty(showAttBars);
1694
//System.err.println("Displaying attribute bars ");
1695
m_showAttBars = true;
1697
if (val.compareTo("true") == 0 || val.compareTo("on") == 0) {
1698
//System.err.println("Displaying attribute bars ");
1699
m_showAttBars = true;
1701
m_showAttBars = false;
1706
System.err.println("Looking for preferred visualization dimensions for "
1709
String xcolKey = thisClass+"."+relationName+".XDimension";
1710
String ycolKey = thisClass+"."+relationName+".YDimension";
1711
String ccolKey = thisClass+"."+relationName+".ColourDimension";
1713
m_preferredXDimension = VisualizeUtils.VISUALIZE_PROPERTIES.
1714
getProperty(xcolKey);
1716
if (m_preferredXDimension == null) {
1717
System.err.println("No preferred X dimension found in "
1718
+VisualizeUtils.PROPERTY_FILE
1721
System.err.println("Setting preferred X dimension to "
1722
+m_preferredXDimension);
1724
m_preferredYDimension = VisualizeUtils.VISUALIZE_PROPERTIES.
1725
getProperty(ycolKey);
1727
if (m_preferredYDimension == null) {
1728
System.err.println("No preferred Y dimension found in "
1729
+VisualizeUtils.PROPERTY_FILE
1732
System.err.println("Setting preferred dimension Y to "
1733
+m_preferredYDimension);
1735
m_preferredColourDimension = VisualizeUtils.VISUALIZE_PROPERTIES.
1736
getProperty(ccolKey);
1738
if (m_preferredColourDimension == null) {
1739
System.err.println("No preferred Colour dimension found in "
1740
+VisualizeUtils.PROPERTY_FILE
1743
System.err.println("Setting preferred Colour dimension to "
1744
+m_preferredColourDimension);
1753
public VisualizePanel() {
1755
setProperties(null);
1756
m_FileChooser.setFileFilter(m_ArffFilter);
1757
m_FileChooser.setFileSelectionMode(JFileChooser.FILES_ONLY);
1759
m_XCombo.setToolTipText("Select the attribute for the x axis");
1760
m_YCombo.setToolTipText("Select the attribute for the y axis");
1761
m_ColourCombo.setToolTipText("Select the attribute to colour on");
1762
m_ShapeCombo.setToolTipText("Select the shape to use for data selection");
1764
m_XCombo.setPreferredSize(COMBO_SIZE);
1765
m_YCombo.setPreferredSize(COMBO_SIZE);
1766
m_ColourCombo.setPreferredSize(COMBO_SIZE);
1767
m_ShapeCombo.setPreferredSize(COMBO_SIZE);
1769
m_XCombo.setMaximumSize(COMBO_SIZE);
1770
m_YCombo.setMaximumSize(COMBO_SIZE);
1771
m_ColourCombo.setMaximumSize(COMBO_SIZE);
1772
m_ShapeCombo.setMaximumSize(COMBO_SIZE);
1774
m_XCombo.setMinimumSize(COMBO_SIZE);
1775
m_YCombo.setMinimumSize(COMBO_SIZE);
1776
m_ColourCombo.setMinimumSize(COMBO_SIZE);
1777
m_ShapeCombo.setMinimumSize(COMBO_SIZE);
1779
m_XCombo.setEnabled(false);
1780
m_YCombo.setEnabled(false);
1781
m_ColourCombo.setEnabled(false);
1782
m_ShapeCombo.setEnabled(false);
1784
// tell the class panel and the legend panel that we want to know when
1786
m_classPanel.addRepaintNotify(this);
1787
m_legendPanel.addRepaintNotify(this);
1789
m_colorList = new FastVector(10);
1790
for (int noa = m_colorList.size(); noa < 10; noa++) {
1791
Color pc = m_DefaultColors[noa % 10];
1794
for (int j=0;j<ija;j++) {
1798
m_colorList.addElement(pc);
1800
m_plot.setColours(m_colorList);
1801
m_classPanel.setColours(m_colorList);
1802
m_attrib.setColours(m_colorList);
1803
m_attrib.addAttributePanelListener(new AttributePanelListener() {
1804
public void attributeSelectionChange(AttributePanelEvent e) {
1806
m_XCombo.setSelectedIndex(e.m_indexVal);
1808
m_YCombo.setSelectedIndex(e.m_indexVal);
1813
m_XCombo.addActionListener(new ActionListener() {
1814
public void actionPerformed(ActionEvent e) {
1815
int selected = m_XCombo.getSelectedIndex();
1819
m_plot.setXindex(selected);
1821
// try sending on the event if anyone is listening
1822
if (listener != null) {
1823
listener.actionPerformed(e);
1828
m_YCombo.addActionListener(new ActionListener() {
1829
public void actionPerformed(ActionEvent e) {
1830
int selected = m_YCombo.getSelectedIndex();
1834
m_plot.setYindex(selected);
1836
// try sending on the event if anyone is listening
1837
if (listener != null) {
1838
listener.actionPerformed(e);
1843
m_ColourCombo.addActionListener(new ActionListener() {
1844
public void actionPerformed(ActionEvent e) {
1845
int selected = m_ColourCombo.getSelectedIndex();
1849
m_plot.setCindex(selected);
1851
if (listener != null) {
1852
listener.actionPerformed(e);
1858
m_ShapeCombo.addActionListener(new ActionListener() {
1859
public void actionPerformed(ActionEvent e) {
1860
int selected = m_ShapeCombo.getSelectedIndex();
1864
m_plot.setSindex(selected);
1865
// try sending on the event if anyone is listening
1866
if (listener != null) {
1867
listener.actionPerformed(e);
1873
///////////////////////////////////////
1875
m_Jitter.addChangeListener(new ChangeListener() {
1876
public void stateChanged(ChangeEvent e) {
1877
m_plot.setJitter(m_Jitter.getValue());
1881
m_openBut.setToolTipText("Loads previously saved instances from a file");
1882
m_openBut.addActionListener(new ActionListener() {
1883
public void actionPerformed(ActionEvent e) {
1884
openVisibleInstances();
1888
m_saveBut.setEnabled(false);
1889
m_saveBut.setToolTipText("Save the visible instances to a file");
1890
m_saveBut.addActionListener(new ActionListener() {
1891
public void actionPerformed(ActionEvent e) {
1892
saveVisibleInstances();
1896
JPanel combos = new JPanel();
1897
GridBagLayout gb = new GridBagLayout();
1898
GridBagConstraints constraints = new GridBagConstraints();
1901
m_XCombo.setLightWeightPopupEnabled(false);
1902
m_YCombo.setLightWeightPopupEnabled(false);
1903
m_ColourCombo.setLightWeightPopupEnabled(false);
1904
m_ShapeCombo.setLightWeightPopupEnabled(false);
1905
combos.setBorder(BorderFactory.createEmptyBorder(10, 5, 10, 5));
1907
combos.setLayout(gb);
1908
constraints.gridx=0;constraints.gridy=0;constraints.weightx=5;
1909
constraints.fill = GridBagConstraints.HORIZONTAL;
1910
constraints.gridwidth=2;constraints.gridheight=1;
1911
constraints.insets = new Insets(0,2,0,2);
1912
combos.add(m_XCombo,constraints);
1913
constraints.gridx=2;constraints.gridy=0;constraints.weightx=5;
1914
constraints.gridwidth=2;constraints.gridheight=1;
1915
combos.add(m_YCombo,constraints);
1916
constraints.gridx=0;constraints.gridy=1;constraints.weightx=5;
1917
constraints.gridwidth=2;constraints.gridheight=1;
1918
combos.add(m_ColourCombo,constraints);
1920
constraints.gridx=2;constraints.gridy=1;constraints.weightx=5;
1921
constraints.gridwidth=2;constraints.gridheight=1;
1922
combos.add(m_ShapeCombo,constraints);
1925
JPanel mbts = new JPanel();
1926
mbts.setLayout(new GridLayout(1,4));
1927
mbts.add(m_submit); mbts.add(m_cancel); mbts.add(m_openBut); mbts.add(m_saveBut);
1929
constraints.gridx=0;constraints.gridy=2;constraints.weightx=5;
1930
constraints.gridwidth=2;constraints.gridheight=1;
1931
combos.add(mbts, constraints);
1933
////////////////////////////////
1934
constraints.gridx=2;constraints.gridy=2;constraints.weightx=5;
1935
constraints.gridwidth=1;constraints.gridheight=1;
1936
constraints.insets = new Insets(10,0,0,5);
1937
combos.add(m_JitterLab,constraints);
1938
constraints.gridx=3;constraints.gridy=2;
1939
constraints.weightx=5;
1940
constraints.insets = new Insets(10,0,0,0);
1941
combos.add(m_Jitter,constraints);
1943
m_classSurround = new JPanel();
1945
setBorder(BorderFactory.createTitledBorder("Class colour"));
1946
m_classSurround.setLayout(new BorderLayout());
1948
m_classPanel.setBorder(BorderFactory.createEmptyBorder(15,10,10,10));
1949
m_classSurround.add(m_classPanel, BorderLayout.CENTER);
1951
GridBagLayout gb2 = new GridBagLayout();
1952
m_plotSurround.setBorder(BorderFactory.createTitledBorder("Plot"));
1953
m_plotSurround.setLayout(gb2);
1955
constraints.fill = GridBagConstraints.BOTH;
1956
constraints.insets = new Insets(0, 0, 0, 10);
1957
constraints.gridx=0;constraints.gridy=0;constraints.weightx=3;
1958
constraints.gridwidth=4;constraints.gridheight=1;constraints.weighty=5;
1959
m_plotSurround.add(m_plot, constraints);
1961
if (m_showAttBars) {
1962
constraints.insets = new Insets(0, 0, 0, 0);
1963
constraints.gridx=4;constraints.gridy=0;constraints.weightx=1;
1964
constraints.gridwidth=1;constraints.gridheight=1;constraints.weighty=5;
1965
m_plotSurround.add(m_attrib, constraints);
1968
setLayout(new BorderLayout());
1969
add(combos, BorderLayout.NORTH);
1970
add(m_plotSurround, BorderLayout.CENTER);
1971
add(m_classSurround, BorderLayout.SOUTH);
1973
String [] SNames = new String [4];
1974
SNames[0] = "Select Instance";
1975
SNames[1] = "Rectangle";
1976
SNames[2] = "Polygon";
1977
SNames[3] = "Polyline";
1979
m_ShapeCombo.setModel(new DefaultComboBoxModel(SNames));
1980
m_ShapeCombo.setEnabled(true);
1984
* displays the previously saved instances
1986
* @param insts the instances to display
1987
* @throws Exception if display is not possible
1989
protected void openVisibleInstances(Instances insts) throws Exception {
1990
PlotData2D tempd = new PlotData2D(insts);
1991
tempd.setPlotName(insts.relationName());
1992
tempd.addInstanceNumberAttribute();
1993
m_plot.m_plot2D.removeAllPlots();
1997
Component parent = getParent();
1998
while (parent != null) {
1999
if (parent instanceof JFrame) {
2000
((JFrame) parent).setTitle(
2001
"Weka Classifier Visualize: "
2002
+ insts.relationName()
2003
+ " (display only)");
2007
parent = parent.getParent();
2013
* Loads previously saved instances from a file
2015
protected void openVisibleInstances() {
2017
int returnVal = m_FileChooser.showOpenDialog(this);
2018
if (returnVal == JFileChooser.APPROVE_OPTION) {
2019
File sFile = m_FileChooser.getSelectedFile();
2020
if (!sFile.getName().toLowerCase().
2021
endsWith(Instances.FILE_EXTENSION)) {
2022
sFile = new File(sFile.getParent(), sFile.getName() + Instances.FILE_EXTENSION);
2024
File selected = sFile;
2025
Instances insts = new Instances(new BufferedReader(new FileReader(selected)));
2026
openVisibleInstances(insts);
2028
} catch (Exception ex) {
2029
ex.printStackTrace();
2030
m_plot.m_plot2D.removeAllPlots();
2031
JOptionPane.showMessageDialog(
2034
"Error loading file...",
2035
JOptionPane.ERROR_MESSAGE);
2040
* Save the currently visible set of instances to a file
2042
private void saveVisibleInstances() {
2043
FastVector plots = m_plot.m_plot2D.getPlots();
2044
if (plots != null) {
2045
PlotData2D master = (PlotData2D)plots.elementAt(0);
2046
Instances saveInsts = new Instances(master.getPlotInstances());
2047
for (int i = 1; i < plots.size(); i++) {
2048
PlotData2D temp = (PlotData2D)plots.elementAt(i);
2049
Instances addInsts = temp.getPlotInstances();
2050
for (int j = 0; j < addInsts.numInstances(); j++) {
2051
saveInsts.add(addInsts.instance(j));
2055
int returnVal = m_FileChooser.showSaveDialog(this);
2056
if (returnVal == JFileChooser.APPROVE_OPTION) {
2057
File sFile = m_FileChooser.getSelectedFile();
2058
if (!sFile.getName().toLowerCase().
2059
endsWith(Instances.FILE_EXTENSION)) {
2060
sFile = new File(sFile.getParent(), sFile.getName()
2061
+ Instances.FILE_EXTENSION);
2063
File selected = sFile;
2064
Writer w = new BufferedWriter(new FileWriter(selected));
2065
w.write(saveInsts.toString());
2068
} catch (Exception ex) {
2069
ex.printStackTrace();
2076
* Sets the index used for colouring. If this method is called then
2077
* the supplied index is used and the combo box for selecting colouring
2078
* attribute is disabled.
2079
* @param index the index of the attribute to use for colouring
2081
public void setColourIndex(int index) {
2083
m_ColourCombo.setSelectedIndex(index);
2085
m_ColourCombo.setSelectedIndex(0);
2087
m_ColourCombo.setEnabled(false);
2092
* Set the index of the attribute for the x axis
2093
* @param index the index for the x axis
2094
* @exception Exception if index is out of range.
2096
public void setXIndex(int index) throws Exception {
2097
if (index >= 0 && index < m_XCombo.getItemCount()) {
2098
m_XCombo.setSelectedIndex(index);
2100
throw new Exception("x index is out of range!");
2105
* Get the index of the attribute on the x axis
2106
* @return the index of the attribute on the x axis
2108
public int getXIndex() {
2109
return m_XCombo.getSelectedIndex();
2113
* Set the index of the attribute for the y axis
2114
* @param index the index for the y axis
2115
* @exception Exception if index is out of range.
2117
public void setYIndex(int index) throws Exception {
2118
if (index >= 0 && index < m_YCombo.getItemCount()) {
2119
m_YCombo.setSelectedIndex(index);
2121
throw new Exception("y index is out of range!");
2126
* Get the index of the attribute on the y axis
2127
* @return the index of the attribute on the x axis
2129
public int getYIndex() {
2130
return m_YCombo.getSelectedIndex();
2134
* Get the index of the attribute selected for coloring
2135
* @return the index of the attribute on the x axis
2137
public int getCIndex() {
2138
return m_ColourCombo.getSelectedIndex();
2142
* Get the index of the shape selected for creating splits.
2143
* @return The index of the shape.
2145
public int getSIndex() {
2146
return m_ShapeCombo.getSelectedIndex();
2150
* Set the shape for creating splits.
2151
* @param index The index of the shape.
2152
* @exception Exception if index is out of range.
2154
public void setSIndex(int index) throws Exception {
2155
if (index >= 0 && index < m_ShapeCombo.getItemCount()) {
2156
m_ShapeCombo.setSelectedIndex(index);
2159
throw new Exception("s index is out of range!");
2164
* Add a listener for this visualize panel
2165
* @param act an ActionListener
2167
public void addActionListener(ActionListener act) {
2172
* Set a name for this plot
2173
* @param plotName the name for the plot
2175
public void setName(String plotName) {
2176
m_plotName = plotName;
2180
* Returns the name associated with this plot. "" is returned if no
2182
* @return the name of the plot
2184
public String getName() {
2189
* Get the master plot's instances
2190
* @return the master plot's instances
2192
public Instances getInstances() {
2193
return m_plot.m_plotInstances;
2197
* Sets the Colors in use for a different attrib
2198
* if it is not a nominal attrib and or does not have
2199
* more possible values then this will do nothing.
2200
* otherwise it will add default colors to see that
2201
* there is a color for the attrib to begin with.
2202
* @param a The index of the attribute to color.
2203
* @param i The instances object that contains the attribute.
2205
protected void newColorAttribute(int a, Instances i) {
2206
if (i.attribute(a).isNominal()) {
2207
for (int noa = m_colorList.size(); noa < i.attribute(a).numValues();
2209
Color pc = m_DefaultColors[noa % 10];
2212
for (int j=0;j<ija;j++) {
2216
m_colorList.addElement(pc);
2218
m_plot.setColours(m_colorList);
2219
m_attrib.setColours(m_colorList);
2220
m_classPanel.setColours(m_colorList);
2226
* This will set the shapes for the instances.
2227
* @param l A list of the shapes, providing that
2228
* the objects in the lists are non editable the data will be
2231
public void setShapes(FastVector l) {
2232
m_plot.setShapes(l);
2236
* Tells the panel to use a new set of instances.
2237
* @param inst a set of Instances
2239
public void setInstances(Instances inst) {
2240
if (inst.numAttributes() > 0 && inst.numInstances() > 0) {
2241
newColorAttribute(inst.numAttributes()-1, inst);
2244
PlotData2D temp = new PlotData2D(inst);
2245
temp.setPlotName(inst.relationName());
2248
setMasterPlot(temp);
2249
} catch (Exception ex) {
2250
System.err.println(ex);
2251
ex.printStackTrace();
2256
* initializes the comboboxes based on the data
2258
* @param inst the data to base the combobox-setup on
2260
public void setUpComboBoxes(Instances inst) {
2261
setProperties(inst.relationName());
2264
if (inst.numAttributes() > 1) {
2268
String [] XNames = new String [inst.numAttributes()];
2269
String [] YNames = new String [inst.numAttributes()];
2270
String [] CNames = new String [inst.numAttributes()];
2271
for (int i = 0; i < XNames.length; i++) {
2273
switch (inst.attribute(i).type()) {
2274
case Attribute.NOMINAL:
2277
case Attribute.NUMERIC:
2280
case Attribute.STRING:
2283
case Attribute.DATE:
2286
case Attribute.RELATIONAL:
2292
XNames[i] = "X: "+ inst.attribute(i).name()+type;
2293
YNames[i] = "Y: "+ inst.attribute(i).name()+type;
2294
CNames[i] = "Colour: "+ inst.attribute(i).name()+type;
2295
if (m_preferredXDimension != null) {
2296
if (m_preferredXDimension.compareTo(inst.attribute(i).name()) == 0) {
2298
//System.err.println("Found preferred X dimension");
2301
if (m_preferredYDimension != null) {
2302
if (m_preferredYDimension.compareTo(inst.attribute(i).name()) == 0) {
2304
//System.err.println("Found preferred Y dimension");
2307
if (m_preferredColourDimension != null) {
2308
if (m_preferredColourDimension.
2309
compareTo(inst.attribute(i).name()) == 0) {
2311
//System.err.println("Found preferred Colour dimension");
2315
m_XCombo.setModel(new DefaultComboBoxModel(XNames));
2316
m_YCombo.setModel(new DefaultComboBoxModel(YNames));
2318
m_ColourCombo.setModel(new DefaultComboBoxModel(CNames));
2319
//m_ShapeCombo.setModel(new DefaultComboBoxModel(SNames));
2320
//m_ShapeCombo.setEnabled(true);
2321
m_XCombo.setEnabled(true);
2322
m_YCombo.setEnabled(true);
2324
if (m_splitListener == null) {
2325
m_ColourCombo.setEnabled(true);
2326
m_ColourCombo.setSelectedIndex(inst.numAttributes()-1);
2328
m_plotSurround.setBorder((BorderFactory.createTitledBorder("Plot: "
2329
+inst.relationName())));
2338
m_ColourCombo.setSelectedIndex(prefC);
2340
} catch (Exception ex) {
2341
System.err.println("Problem setting preferred Visualization dimensions");
2346
* Set the master plot for the visualize panel
2347
* @param newPlot the new master plot
2348
* @exception Exception if the master plot could not be set
2350
public void setMasterPlot(PlotData2D newPlot) throws Exception {
2351
m_plot.setMasterPlot(newPlot);
2352
setUpComboBoxes(newPlot.m_plotInstances);
2353
m_saveBut.setEnabled(true);
2358
* Set a new plot to the visualize panel
2359
* @param newPlot the new plot to add
2360
* @exception Exception if the plot could not be added
2362
public void addPlot(PlotData2D newPlot) throws Exception {
2363
m_plot.addPlot(newPlot);
2364
if (m_plot.m_plot2D.getMasterPlot() != null) {
2365
setUpComboBoxes(newPlot.m_plotInstances);
2367
m_saveBut.setEnabled(true);
2372
* Main method for testing this class
2374
* @param args the commandline parameters
2376
public static void main(String [] args) {
2378
if (args.length < 1) {
2379
System.err.println("Usage : weka.gui.visualize.VisualizePanel "
2380
+"<dataset> [<dataset> <dataset>...]");
2384
final javax.swing.JFrame jf =
2385
new javax.swing.JFrame("Weka Explorer: Visualize");
2386
jf.setSize(500,400);
2387
jf.getContentPane().setLayout(new BorderLayout());
2388
final VisualizePanel sp = new VisualizePanel();
2390
jf.getContentPane().add(sp, BorderLayout.CENTER);
2391
jf.addWindowListener(new java.awt.event.WindowAdapter() {
2392
public void windowClosing(java.awt.event.WindowEvent e) {
2398
jf.setVisible(true);
2399
if (args.length >= 1) {
2400
for (int j = 0; j < args.length; j++) {
2401
System.err.println("Loading instances from " + args[j]);
2402
java.io.Reader r = new java.io.BufferedReader(
2403
new java.io.FileReader(args[j]));
2404
Instances i = new Instances(r);
2405
i.setClassIndex(i.numAttributes()-1);
2406
PlotData2D pd1 = new PlotData2D(i);
2409
pd1.setPlotName("Master plot");
2410
sp.setMasterPlot(pd1);
2412
pd1.setPlotName("Plot "+(j+1));
2413
pd1.m_useCustomColour = true;
2414
pd1.m_customColour = (j % 2 == 0) ? Color.red : Color.blue;
2419
} catch (Exception ex) {
2420
ex.printStackTrace();
2421
System.err.println(ex.getMessage());