1 /* -------------------------------------------------------------------
2 GeoVISTA Center (Penn State, Dept. of Geography)
3 Java source file for the class VisualClassifier
4 Copyright (c), 2002, GeoVISTA Center
5 All Rights Reserved.
6 Original Author: Frank Hardisty
7 $Author: hardisty $
8 $Id: VisualClassifier.java,v 1.5 2003/07/18 18:15:48 hardisty Exp $
9 $Date: 2003/07/18 18:15:48 $
10 Reference: Document no:
11 ___ ___
12 ------------------------------------------------------------------- *
13
14 */
15
16
17 package edu.psu.geovista.symbolization;
18
19 //import edu.psu.geovista.ui.panel.*;
20 import java.awt.*;
21 import java.awt.event.*;
22 import java.awt.geom.*;
23 import java.awt.image.*;
24 import java.util.*;
25
26 import javax.swing.*;
27 import javax.swing.event.*;
28
29 import edu.psu.geovista.classification.*;
30 import edu.psu.geovista.ui.event.*;
31 import edu.psu.geovista.common.event.*;
32 import edu.psu.geovista.common.color.Pallet;
33
34 //import javax.swing.colorchooser.*;
35
36 //public class VisualClassifier extends JPanel implements ActionListener {
37 public class VisualClassifier extends JPanel implements ActionListener, ComponentListener,
38 PalletListener{
39 //Jin Chen: for extending purpose, change private to protected for following fields:
40 // symbolizationPanel,colors,dataColors,colorerLinear,colorClasser,classPick
41 protected ColorRampPicker symbolizationPanel;
42 //XXX in future, we want to support much beyond colors.
43 protected transient Color[] colors;
44 protected transient Color[] dataColors;
45 private transient Shape[] symbols;
46
47 public transient boolean[] anchored;
48
49 private transient double[] data;
50 private transient int[] classificationIndex;
51
52
53 transient private int nClasses;
54 transient private int nSymbols;
55 transient private boolean update;
56 transient private boolean interpolate;
57 transient private boolean setupFinished;
58
59 private transient JCheckBox updateBox;
60 private transient JCheckBox interpolateBox;
61 public static final String COMMAND_COLORS_CHANGED = "colors";
62 public static final String COMMAND_BEAN_REGISTERED = "hi!";
63
64 private int currOrientation = this.X_AXIS;
65 public static final int X_AXIS = 0;
66 public static final int Y_AXIS = 1;
67 public transient boolean orientationInParentIsX = false;
68
69
70 protected ColorSymbolizerLinear colorerLinear;
71 protected ColorSymbolClassificationSimple colorClasser;
72
73 protected ClassifierPicker classPick;
74
75 private transient TexturePaint texPaint;
76 private transient int oldIndication = 0;
77
78
79 public VisualClassifier() {
80 super();
81 this.addComponentListener(this);
82 this.colorClasser = new ColorSymbolClassificationSimple();
83 this.colorerLinear = new ColorSymbolizerLinear();
84 this.setupFinished = false;
85 this.nClasses = ColorRampPicker.DEFAULT_NUM_SWATCHES;
86 this.update = true;
87 this.interpolate = true;
88
89 this.classPick = new ClassifierPicker();
90 classPick.addActionListener(this);
91
92 this.makeSymbolizationPanel();
93 this.setLayout(new BoxLayout(this, BoxLayout.X_AXIS));
94 this.add(classPick);
95 this.setVariableChooserMode(ClassifierPicker.VARIABLE_CHOOSER_MODE_ACTIVE);
96 //this.add(classificationPanel);
97 this.add(symbolizationPanel);
98 this.setupFinished = true;
99 this.makeColors();
100 this.setPreferredSize(new Dimension(300,40));
101 /*
102 int prefHeight = (int)this.classPick.getPreferredSize().getHeight();
103 prefHeight = prefHeight + (int)this.symbolizationPanel.getPreferredSize().getHeight();
104 int prefWidth = (int)this.classPick.getPreferredSize().getWidth();
105 if (prefWidth > (int)this.symbolizationPanel.getPreferredSize().getWidth()) {
106 prefWidth = (int)this.symbolizationPanel.getPreferredSize().getWidth(); //take the smaller one
107 }
108 this.setPreferredSize(new Dimension(prefWidth, prefHeight));
109 */
110 this.revalidate();
111 this.makeTexPaint();
112 }
113
114 private void makeTexPaint(){
115
116 int texSize = 4;
117 Rectangle2D.Float rect = new Rectangle2D.Float(0,0,texSize,texSize);
118 BufferedImage buff = new BufferedImage(texSize,texSize,BufferedImage.TYPE_INT_ARGB);
119 Graphics2D g2 = buff.createGraphics();
120 Color trans = new Color(0,0,0,0); //clear black
121 g2.setColor(trans);
122 g2.fillRect(0,0,texSize,texSize);
123 g2.setColor(Color.blue);
124 g2.drawLine(0,0,32,32);
125
126 texPaint = new TexturePaint(buff,rect);
127
128 }
129 private void makeSymbolizationPanel() {
130 symbolizationPanel = new ColorRampPicker();
131 this.add(symbolizationPanel);
132 symbolizationPanel.addActionListener(this);
133 }
134
135 private void makeColors(){
136 Color[] pickerColors = this.symbolizationPanel.getColors();
137 boolean[] pickerAnchors = this.symbolizationPanel.getAnchored();
138 //now we interpolate...linearly
139 int nPicks = pickerColors.length;
140 double picksPerColor = (double)nPicks/(double)this.nClasses;
141 if (this.colors == null){
142 this.colors = new Color[nClasses];
143 } else if (this.colors.length != nClasses) {
144 this.colors = new Color[nClasses];
145 }
146
147 for (int i = 0; i < nClasses; i++) {
148 double whichColor = (double)i * picksPerColor;
149 int index = (int)Math.floor(whichColor);
150 //System.out.println("i = " + i + "index = " + index);
151 this.colors[i] = pickerColors[index];
152
153 }
154 if (this.interpolate) {
155 //for each lock in the picker, find the class that is closest.
156 double colorsPerPick = (double)(this.nClasses-1)/(double)(nPicks-1);
157 //int[] newAnchors = new int[pickerAnchors.length];
158 Vector newAnchors = new Vector();
159 for (int i = 0; i < pickerAnchors.length; i++) {
160 double whichClass = (double)i * colorsPerPick;
161 int aClass = (int)Math.round(whichClass);
162 if (pickerAnchors[i]){
163 Integer Ind = new Integer(aClass);
164 newAnchors.add(Ind);
165 }
166 }
167
168 boolean[] colorAnchors = new boolean[nClasses];
169 if (newAnchors.size() > 2) {
170 for (Enumeration e = newAnchors.elements() ; e.hasMoreElements() ;) {
171 Integer ind = (Integer)e.nextElement();
172 colorAnchors[ind.intValue()] = true;
173 }
174
175 colorAnchors[0] = true;
176 colorAnchors[colorAnchors.length -1] = true;
177 } else if (newAnchors.size() == 2) {
178 colorAnchors[0] = true;
179 colorAnchors[colorAnchors.length -1] = true;
180 } else if (newAnchors.size() == 1) {
181 colorAnchors[0] = true;
182 }
183 //now find those durn colors!
184 this.symbolizationPanel.getRamp().rampColors(this.colors,colorAnchors);
185 this.colorerLinear.setRampingColors(this.colors);
186 this.colorerLinear.setAnchors(colorAnchors);
187 this.colorClasser.setColorer(this.colorerLinear);
188 }//end if interpolate
189 }
190 public Color[] findDataColors(){
191 if (this.classPick.getDataSet() == null){
192 return null;
193 }
194 int currVar = this.classPick.getCurrVariableIndex();
195 Classifier classer = this.classPick.getClasser();
196 double[] data = this.classPick.getDataSet().getNumericDataAsDouble(currVar);
197 int[] classedData = classer.classify(data,classPick.getNClasses());
198 if (this.dataColors == null || this.dataColors.length != classedData.length){
199 this.dataColors = new Color[classedData.length];
200 }
201 this.colors = this.symbolizationPanel.getColors();
202 for (int i = 0; i < classedData.length; i++){
203 int aClass = classedData[i];
204 if (aClass < 0) aClass = 0;//$$$ egregious hack
205 Color c = this.colors[aClass];
206 this.dataColors[i] = c;
207 }
208 return this.dataColors;
209 }
210
211
212 /*** Listens to the check boxen. */
213 class CheckBoxListener implements ItemListener {
214 public void itemStateChanged(ItemEvent e) {
215 if (e.getSource().equals(VisualClassifier.this.updateBox)){
216 if (e.getStateChange() == ItemEvent.SELECTED && VisualClassifier.this.setupFinished){
217 VisualClassifier.this.update = true;
218 VisualClassifier.this.makeColors();
219 VisualClassifier.this.fireActionPerformed(VisualClassifier.COMMAND_COLORS_CHANGED);
220 VisualClassifier.this.fireColorClassifierPerformed();
221 VisualClassifier.this.fireColorArrayChanged();
222
223 } else if (e.getStateChange() == ItemEvent.DESELECTED){
224
225 VisualClassifier.this.update = false;
226 }
227 } else if (e.getSource().equals(VisualClassifier.this.interpolateBox)){
228 if (e.getStateChange() == ItemEvent.SELECTED && VisualClassifier.this.setupFinished){
229 VisualClassifier.this.interpolate = true;
230 VisualClassifier.this.makeColors();
231 VisualClassifier.this.fireActionPerformed(VisualClassifier.COMMAND_COLORS_CHANGED);
232 VisualClassifier.this.fireColorClassifierPerformed();
233 VisualClassifier.this.fireColorArrayChanged();
234
235 } else if (e.getStateChange() == ItemEvent.DESELECTED){
236 VisualClassifier.this.interpolate = false;
237 VisualClassifier.this.makeColors();
238 VisualClassifier.this.fireActionPerformed(VisualClassifier.COMMAND_COLORS_CHANGED);
239 VisualClassifier.this.fireColorClassifierPerformed();
240 VisualClassifier.this.fireColorArrayChanged();
241 }
242 }
243 }
244 }
245 class SliderListener implements ChangeListener {
246 public void stateChanged(ChangeEvent e) {
247 JSlider source = (JSlider)e.getSource();
248 //if (!source.getValueIsAdjusting()) {
249
250 int fps = (int)source.getValue();
251 if (fps == 0) {
252 //if (!frozen) stopAnimation();
253 } else {
254 VisualClassifier.this.symbolizationPanel.setNSwatches(fps);
255 VisualClassifier.this.nClasses = fps;
256 VisualClassifier.this.makeColors();
257 VisualClassifier.this.fireActionPerformed(VisualClassifier.COMMAND_COLORS_CHANGED);
258 VisualClassifier.this.fireColorClassifierPerformed();
259 VisualClassifier.this.fireColorArrayChanged();
260 }
261 //}
262 }
263 }
264 public ColorRampPicker getSymbolizationPanel() {
265 return symbolizationPanel;
266 }
267 public void setSymbolizationPanel(ColorRampPicker symbolizationPanel) {
268 this.symbolizationPanel = symbolizationPanel;
269 this.makeColors();
270 System.out.println("VC, settingSymbolizationPanel");
271 }
272 public ColorSymbolClassification getColorSymbolClassification(){
273 return this.colorClasser;
274 }
275 public void setColorSymbolClassification(ColorSymbolClassification colorClasser){
276 this.colorClasser = (ColorSymbolClassificationSimple)colorClasser;
277 }
278
279 public ColorSymbolizer getColorSymbolizer(){
280 System.out.println("VC, gettingColorSymbolizer");
281 return this.colorerLinear;
282 }
283
284
285
286 public void setColorSymbolizer(ColorSymbolizer colorerLinear){
287 this.colorerLinear = (ColorSymbolizerLinear)colorerLinear;
288 }
289
290 public Color[] getColorForObservations(){
291 return this.findDataColors();
292 }
293 public Color[] getColors() {
294 //System.out.println("VC, gettingColors");
295 this.colors = this.symbolizationPanel.getColors();
296 return this.colors;
297 }
298
299 public void setColors(Color[] colors) {
300 this.colors = colors;
301 }
302
303 public void palletChanged(PalletEvent e){
304 Pallet pal = e.getPallet();
305 int maxColors = pal.getRecommendedMaxLength();
306 System.out.println("max colors = " + maxColors);
307
308 //we can't go over the max, or it blows null exceptions
309 //so, we interpolate....
310
311 int numClasses = this.classPick.getNClasses();
312 Color[] cols = null;
313 if (numClasses <= maxColors){
314 cols = pal.getColors(numClasses);
315 } else {
316 //we have more colors wanted than the pallet can give us
317 //todo: make this work
318 //this.assignColors
319 cols = pal.getColors(numClasses);
320 }
321
322
323 this.setColors(cols);
324 this.symbolizationPanel.setColors(cols);
325 this.revalidate();
326 this.repaint();
327 }
328
329 private Color[] assignColors(Color[] currColors, int numWanted){
330 Color[] newColors = new Color[numWanted];
331
332 return newColors;
333 }
334 public void actionPerformed(ActionEvent e) {
335 String command = e.getActionCommand();
336 if (command == ColorRampPicker.COMMAND_SWATCH_COLOR_CHANGED){
337 this.makeColors();
338 this.fireActionPerformed(this.COMMAND_COLORS_CHANGED);
339 this.fireColorClassifierPerformed();
340 VisualClassifier.this.fireColorArrayChanged();
341 } else if (command == this.classPick.COMMAND_CLASSES_CHANGED) {
342 int nClasses = this.classPick.getNClasses();
343 this.symbolizationPanel.setNSwatches(nClasses);
344 this.nClasses = nClasses;
345 this.makeColors();
346 this.fireActionPerformed(this.COMMAND_COLORS_CHANGED);
347 this.fireColorClassifierPerformed();
348 VisualClassifier.this.fireColorArrayChanged();
349 } else if (command == this.classPick.COMMAND_SELECTED_VARIABLE_CHANGED) {
350
351 this.fireActionPerformed(this.classPick.COMMAND_SELECTED_VARIABLE_CHANGED);
352 this.fireColorArrayChanged();
353 } else if (command == this.classPick.COMMAND_SELECTED_CLASSIFIER_CHANGED) {
354 //this.colorClasser.setColorer(this.colorerLinear);
355 this.colorClasser.setClasser(this.classPick.getClasser());
356 fireColorClassifierPerformed();
357 VisualClassifier.this.fireColorArrayChanged();
358 }
359
360 //need to pass this along, if we are a FoldupPanel
361 //super.actionPerformed(e);
362 }
363 //start component event handling
364 //note: this class only listens to itself
365 public void componentHidden(ComponentEvent e) {}
366
367 public void componentMoved(ComponentEvent e) {}
368
369 public void componentShown(ComponentEvent e) {}
370
371 public void componentResized(ComponentEvent e) {
372 double pickPrefWidth = this.classPick.getPreferredSize().getWidth();
373
374 int prefWidth = (int)(pickPrefWidth * 1.5);
375 if (this.getWidth() >= prefWidth) {
376 this.changeOrientation(this.X_AXIS);
377 } else {
378 this.changeOrientation(this.Y_AXIS);
379 }
380
381 }
382 //end component event handling
383
384 public void changeOrientation(int orientation){
385 if (orientation == this.currOrientation) {
386 return;
387 } else if (orientation == this.X_AXIS) {
388 Component[] comps = new Component[this.getComponentCount()];
389 for (int i = 0; i < this.getComponentCount(); i++) {
390 comps[i] = this.getComponent(i);
391 }
392 this.setLayout(new BoxLayout(this,BoxLayout.X_AXIS));
393 for (int i = 0; i < this.getComponentCount(); i++) {
394 this.add(comps[i]);
395 }
396 this.currOrientation = this.X_AXIS;
397 this.setPreferredSize(new Dimension(300,20));
398 this.revalidate();
399 } else if (orientation == this.Y_AXIS) {
400 Component[] comps = new Component[this.getComponentCount()];
401 for (int i = 0; i < this.getComponentCount(); i++) {
402 comps[i] = this.getComponent(i);
403 }
404 this.setLayout(new BoxLayout(this,BoxLayout.Y_AXIS));
405 for (int i = 0; i < this.getComponentCount(); i++) {
406 this.add(comps[i]);
407 }
408 this.currOrientation = this.Y_AXIS;
409 this.setPreferredSize(new Dimension(300,40));
410 this.revalidate();
411 }
412
413 }
414
415 /***
416 * implements ColorArrayListener
417 */
418 public void addColorArrayListener(ColorArrayListener l) {
419 listenerList.add(ColorArrayListener.class, l);
420 //this.fireColorArrayChanged(); //so that if any class registers
421
422 //it gets an event
423 }
424
425 /***
426 * removes an ColorArrayListener from the component
427 */
428 public void removeColorArrayListener(ColorArrayListener l) {
429 listenerList.remove(ColorArrayListener.class, l);
430 }
431
432 /***
433 * Notify all listeners that have registered interest for
434 * notification on this event type. The event instance
435 * is lazily created using the parameters passed into
436 * the fire method.
437 * @see EventListenerList
438 */
439 private void fireColorArrayChanged() {
440 // Guaranteed to return a non-null array
441 Object[] listeners = listenerList.getListenerList();
442 ColorArrayEvent e = null;
443
444 // Process the listeners last to first, notifying
445 // those that are interested in this event
446 for (int i = listeners.length - 2; i >= 0; i -= 2) {
447 if (listeners[i] == ColorArrayListener.class) {
448 // Lazily create the event:
449 if (e == null) {
450 e = new ColorArrayEvent(this, this.findDataColors());
451 }
452
453 ((ColorArrayListener) listeners[i + 1]).colorArrayChanged(e);
454 }
455 } //next i
456 }
457 //}
458 /***
459 * implements ColorClassifierListener
460 */
461 public void addColorClassifierListener (ColorClassifierListener l) {
462 listenerList.add(ColorClassifierListener.class, l);
463 this.fireColorClassifierPerformed();//so that if any class registers
464 //it gets an event
465 }
466
467 /***
468 * removes an ColorClassifierListener from the component
469 */
470 public void removeColorClassifierListener (ColorClassifierListener l) {
471 listenerList.remove(ColorClassifierListener.class, l);
472 }
473 /***
474 * Notify all listeners that have registered interest for
475 * notification on this event type. The event instance
476 * is lazily created using the parameters passed into
477 * the fire method.
478 * @see EventListenerList
479 */
480 private void fireColorClassifierPerformed () {
481 if (update) {
482 // Guaranteed to return a non-null array
483 Object[] listeners = listenerList.getListenerList();
484 ColorClassifierEvent e = null;
485 // Process the listeners last to first, notifying
486 // those that are interested in this event
487 for (int i = listeners.length - 2; i >= 0; i -= 2) {
488 if (listeners[i] == ColorClassifierListener.class) {
489 // Lazily create the event:
490 if (e == null) {
491 e = new ColorClassifierEvent(this,this.getColorSymbolClassification());
492 }
493 if (this.orientationInParentIsX == true){
494 e.setOrientation(e.SOURCE_ORIENTATION_X);
495 }else {
496 e.setOrientation(e.SOURCE_ORIENTATION_Y);
497 }
498 ((ColorClassifierListener)listeners[i + 1]).colorClassifierChanged(e);
499 }
500 }//next i
501 }//end if
502 }
503 /***
504 * implements ActionListener
505 */
506 public void addActionListener (ActionListener l) {
507 listenerList.add(ActionListener.class, l);
508 this.fireActionPerformed(this.COMMAND_BEAN_REGISTERED);
509 }
510
511 /***
512 * removes an ActionListener from the component
513 */
514 public void removeActionListener (ActionListener l) {
515 listenerList.remove(ActionListener.class, l);
516 }
517 /***
518 * Notify all listeners that have registered interest for
519 * notification on this event type. The event instance
520 * is lazily created using the parameters passed into
521 * the fire method.
522 * @see EventListenerList
523 */
524 private void fireActionPerformed (String command) {
525 if (update) {
526 // Guaranteed to return a non-null array
527 Object[] listeners = listenerList.getListenerList();
528 ActionEvent e = null;
529 // Process the listeners last to first, notifying
530 // those that are interested in this event
531 for (int i = listeners.length - 2; i >= 0; i -= 2) {
532 if (listeners[i] == ActionListener.class) {
533 // Lazily create the event:
534 if (e == null) {
535 e = new ActionEvent(this, ActionEvent.ACTION_PERFORMED, command);
536 }
537 ((ActionListener)listeners[i + 1]).actionPerformed(e);
538 }
539 }//next i
540 }//end if
541 }
542
543 public void setIndicatedClass(int indicClass) {
544 //clear old indication
545 //if it still exists
546 if (this.oldIndication < this.symbolizationPanel.getPanSet().length) {
547 this.symbolizationPanel.getPanSet()[this.oldIndication].setTexPaint(null);
548 }
549
550 if (indicClass < 0) { //used for null or out of range indication
551 this.repaint(); //clear old indication
552 return;
553 }
554 if (indicClass >= this.symbolizationPanel.getPanSet().length) {
555 this.repaint(); //clear old indication
556 return;
557 }
558
559 this.symbolizationPanel.getPanSet()[indicClass].setTexPaint(this.texPaint);
560 //System.out.println("bivarviz, indicated swatch = " + indicSwatch);
561 this.oldIndication = indicClass;
562 this.repaint();
563 }
564 public boolean[] getAnchored() {
565 return anchored;
566 }
567 public void setAnchored(boolean[] anchored) {
568 this.anchored = anchored;
569 }
570 public int[] getClassificationIndex() {
571 return classificationIndex;
572 }
573 public void setClassificationIndex(int[] classificationIndex) {
574 this.classificationIndex = classificationIndex;
575 }
576 public ClassifierPicker getClassPick() {
577 return classPick;
578 }
579 public void setClassPick(ClassifierPicker classPick) {
580 this.classPick = classPick;
581 }
582 public ColorSymbolClassificationSimple getColorClasser() {
583 return colorClasser;
584 }
585 public void setColorClasser(ColorSymbolClassificationSimple colorClasser) {
586 this.colorClasser = colorClasser;
587 }
588 public ColorSymbolizerLinear getColorerLinear() {
589 return colorerLinear;
590 }
591 public void setColorerLinear(ColorSymbolizerLinear colorerLinear) {
592 this.colorerLinear = colorerLinear;
593 }
594 public int getCurrOrientation() {
595 return currOrientation;
596 }
597 public void setCurrOrientation(int currOrientation) {
598 this.currOrientation = currOrientation;
599 }
600 public int getVariableChooserMode() {
601 return this.classPick.getVariableChooserMode();
602 }
603 public void setVariableChooserMode(int variableChooserMode) {
604 if (this.classPick.getVariableChooserMode() != variableChooserMode) {
605 this.classPick.setVariableChooserMode(variableChooserMode);
606
607 }
608 }
609
610 public void setCurrVariableIndex(int index){
611 this.classPick.setCurrVariableIndex(index);
612 }
613
614 public void setData(Object[] data) {
615 this.classPick.setData(data);
616 }
617 public int getCurrVariableIndex() {
618 return this.classPick.getCurrVariableIndex();
619 }
620
621 public void setHighColor(Color c){
622 this.symbolizationPanel.setHighColor(c);
623 }
624 public void setOrientationInParentIsX(boolean orientationInParentIsX) {
625 this.orientationInParentIsX = orientationInParentIsX;
626 }
627
628 }
This page was automatically generated by Maven