001 /*
002 * This file is part of McIDAS-V
003 *
004 * Copyright 2007-2013
005 * Space Science and Engineering Center (SSEC)
006 * University of Wisconsin - Madison
007 * 1225 W. Dayton Street, Madison, WI 53706, USA
008 * https://www.ssec.wisc.edu/mcidas
009 *
010 * All Rights Reserved
011 *
012 * McIDAS-V is built on Unidata's IDV and SSEC's VisAD libraries, and
013 * some McIDAS-V source code is based on IDV and VisAD source code.
014 *
015 * McIDAS-V is free software; you can redistribute it and/or modify
016 * it under the terms of the GNU Lesser Public License as published by
017 * the Free Software Foundation; either version 3 of the License, or
018 * (at your option) any later version.
019 *
020 * McIDAS-V is distributed in the hope that it will be useful,
021 * but WITHOUT ANY WARRANTY; without even the implied warranty of
022 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
023 * GNU Lesser Public License for more details.
024 *
025 * You should have received a copy of the GNU Lesser Public License
026 * along with this program. If not, see http://www.gnu.org/licenses.
027 */
028
029 package edu.wisc.ssec.mcidasv.control;
030
031 import java.awt.Color;
032 import java.awt.Container;
033 import java.awt.FlowLayout;
034 import java.awt.event.ActionEvent;
035 import java.awt.event.ActionListener;
036 import java.rmi.RemoteException;
037 import java.util.ArrayList;
038 import java.util.HashMap;
039 import java.util.Hashtable;
040 import java.util.List;
041 import java.util.Map;
042
043 import javax.swing.JButton;
044 import javax.swing.JComboBox;
045 import javax.swing.JComponent;
046 import javax.swing.JLabel;
047 import javax.swing.JPanel;
048 import javax.swing.JTabbedPane;
049 import javax.swing.JTextField;
050 import javax.swing.border.LineBorder;
051
052 import org.python.core.PyObject;
053
054 import visad.ConstantMap;
055 import visad.Data;
056 import visad.VisADException;
057 import visad.georef.MapProjection;
058
059 import ucar.unidata.data.DataChoice;
060 import ucar.unidata.data.DataSource;
061 import ucar.unidata.data.DirectDataChoice;
062 import ucar.unidata.idv.MapViewManager;
063 import ucar.unidata.util.GuiUtils;
064 import ucar.unidata.util.LogUtil;
065 import ucar.unidata.view.geoloc.MapProjectionDisplay;
066
067 import edu.wisc.ssec.mcidasv.Constants;
068 import edu.wisc.ssec.mcidasv.McIDASV;
069 import edu.wisc.ssec.mcidasv.control.LinearCombo.Combination;
070 import edu.wisc.ssec.mcidasv.control.LinearCombo.Selector;
071 import edu.wisc.ssec.mcidasv.data.hydra.MultiDimensionSubset;
072 import edu.wisc.ssec.mcidasv.data.hydra.MultiSpectralData;
073 import edu.wisc.ssec.mcidasv.data.hydra.MultiSpectralDataSource;
074 import edu.wisc.ssec.mcidasv.display.hydra.MultiSpectralDisplay;
075 import edu.wisc.ssec.mcidasv.jython.Console;
076 import edu.wisc.ssec.mcidasv.jython.ConsoleCallback;
077
078 public class HydraCombo extends HydraControl {
079
080
081 private MultiSpectralDisplay display;
082
083 private DataChoice dataChoice = null;
084
085 private CombinationPanel comboPanel;
086
087 private final Hashtable<String, Object> persistable = new Hashtable<String, Object>();
088
089 private MultiSpectralDataSource source;
090
091 float init_wavenumber;
092
093 private Map<String, Selector> selectorMap = new HashMap<String, Selector>();
094
095 private static final String defaultButtonLabel = "Compute New Field";
096 private JButton computeButton = new JButton(defaultButtonLabel);
097
098 public HydraCombo() {
099 super();
100 setHelpUrl("idv.controls.hydra.channelcombinationcontrol");
101 }
102
103 @Override public boolean init(final DataChoice choice)
104 throws VisADException, RemoteException {
105 ((McIDASV)getIdv()).getMcvDataManager().setHydraControl(choice, this);
106 dataChoice = choice;
107 List<DataSource> sources = new ArrayList<DataSource>();
108 choice.getDataSources(sources);
109 source = ((MultiSpectralDataSource)sources.get(0));
110
111 Float fieldSelectorChannel = (Float)getDataSelection().getProperty(Constants.PROP_CHAN);
112 if (fieldSelectorChannel == null)
113 fieldSelectorChannel = 0f;
114 init_wavenumber = fieldSelectorChannel;
115
116 display = new MultiSpectralDisplay((DirectDataChoice)choice);
117 display.setWaveNumber(fieldSelectorChannel);
118 display.setDisplayControl(this);
119
120 ((McIDASV)getIdv()).getMcvDataManager().setHydraDisplay(choice, display);
121
122 comboPanel = new CombinationPanel(this);
123 if (!persistable.isEmpty()) {
124 comboPanel.unpersistData(persistable);
125 persistable.clear();
126 }
127 return true;
128 }
129
130 @Override public void initDone() {
131 MapViewManager viewManager = (MapViewManager) getViewManager();
132 MapProjectionDisplay dispMaster =
133 (MapProjectionDisplay) viewManager.getMaster();
134 try {
135 dispMaster.setMapProjection(getDataProjection());
136 } catch (Exception e) {
137 logException("problem setting MapProjection", e);
138 }
139
140 getIdv().getIdvUIManager().showDashboard();
141 }
142
143 public Hashtable<String, Object> getPersistable() {
144 return comboPanel.persistData();
145 }
146
147 public void setPersistable(final Hashtable<String, Object> table) {
148 persistable.clear();
149 persistable.putAll(table);
150 }
151
152 @Override public MapProjection getDataProjection() {
153 MapProjection mp = null;
154 HashMap subset = null;
155 Hashtable table = dataChoice.getProperties();
156 MultiDimensionSubset dataSel =
157 (MultiDimensionSubset) table.get(MultiDimensionSubset.key);
158 if (dataSel != null) {
159 subset = dataSel.getSubset();
160 }
161 mp = source.getDataProjection(subset);
162 return mp;
163 }
164
165 @Override public Container doMakeContents() {
166 JTabbedPane pane = new JTabbedPane();
167 pane.add("Channel Combination Tool", GuiUtils.inset(getComboTab(), 5));
168 GuiUtils.handleHeavyWeightComponentsInTabs(pane);
169 return pane;
170 }
171
172 public void updateComboPanel(final String id, final float value) {
173 if (comboPanel == null)
174 return;
175 comboPanel.updateSelector(id, value);
176 }
177
178 protected void enableSelectorForWrapper(final SelectorWrapper wrapper) {
179 if (comboPanel == null)
180 return;
181 comboPanel.enableSelector(wrapper, false);
182 }
183
184 protected void disableSelectorForWrapper(final SelectorWrapper wrapper) {
185 if (comboPanel == null)
186 return;
187 comboPanel.disableSelector(wrapper, false);
188 }
189
190 protected MultiSpectralDisplay getMultiSpectralDisplay() {
191 return display;
192 }
193
194 protected JButton getComputeButton() {
195 return computeButton;
196 }
197
198 private JComponent getComboTab() {
199 computeButton.addActionListener(new ActionListener() {
200 public void actionPerformed(final ActionEvent e) {
201 comboPanel.queueCombination();
202 computeButton.setEnabled(false);
203 showWaitCursor();
204 }
205 });
206 // wrap compute button in JPanel to retain preferred size
207 JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.CENTER));
208 buttonPanel.add(computeButton);
209 JPanel tmp = GuiUtils.topCenterBottom(display.getDisplayComponent(), comboPanel.getPanel(), buttonPanel);
210 return tmp;
211 }
212
213 public void addCombination(final String name, final Data combination) {
214 if (combination != null)
215 source.addChoice(name, combination);
216 }
217
218 public void addCombination(final Combination combination) {
219 if (combination != null)
220 source.addChoice(combination.getName(), combination.getData());
221 }
222
223 protected void addSelector(final Selector selector) throws Exception {
224 ConstantMap[] mapping = selector.getColor();
225 float r = new Double(mapping[0].getConstant()).floatValue();
226 float g = new Double(mapping[1].getConstant()).floatValue();
227 float b = new Double(mapping[2].getConstant()).floatValue();
228 Color javaColor = new Color(r, g, b);
229 display.createSelector(selector.getId(), javaColor);
230 display.setSelectorValue(selector.getId(), selector.getWaveNumber());
231 selectorMap.put(selector.getId(), selector);
232 }
233
234 public void moveSelector(final String id, final float wavenum) {
235 if (!selectorMap.containsKey(id))
236 return;
237 display.updateControlSelector(id, wavenum);
238 }
239
240 public void updateSelector(final String id, final float wavenum) {
241 if (!selectorMap.containsKey(id))
242 return;
243
244 selectorMap.get(id).setWaveNumber(wavenum);
245 }
246
247
248
249 public enum DataType { HYPERSPECTRAL, MULTISPECTRAL };
250
251 public enum WrapperState { ENABLED, DISABLED };
252
253 public static class CombinationPanel implements ConsoleCallback {
254 private final SelectorWrapper a;
255 private final SelectorWrapper b;
256 private final SelectorWrapper c;
257 private final SelectorWrapper d;
258
259 private final OperationXY ab;
260 private final OperationXY cd;
261
262 private final CombineOperations abcd;
263
264 private final MultiSpectralDisplay display;
265
266 private final HydraCombo control;
267
268 private final Console console;
269
270 private final Map<String, Selector> selectorMap = new HashMap<String, Selector>();
271 private final Map<String, SelectorWrapper> wrapperMap = new HashMap<String, SelectorWrapper>();
272
273 private final DataType dataType;
274
275 public CombinationPanel(final HydraCombo control) {
276 this.control = control;
277 display = control.getMultiSpectralDisplay();
278 if (display == null)
279 throw new NullPointerException("Display hasn't been initialized");
280
281 MultiSpectralData data = display.getMultiSpectralData();
282 if (data.hasBandNames())
283 dataType = DataType.MULTISPECTRAL;
284 else
285 dataType = DataType.HYPERSPECTRAL;
286
287 this.console = new Console();
288 console.setCallbackHandler(this);
289
290 a = makeWrapper("a", Color.RED);
291 b = makeWrapper("b", Color.GREEN);
292 c = makeWrapper("c", Color.BLUE);
293 d = makeWrapper("d", Color.MAGENTA);
294
295 enableSelector(a, true);
296
297 ab = new OperationXY(this, a, b);
298 cd = new OperationXY(this, c, d);
299
300 abcd = new CombineOperations(this, ab, cd);
301 }
302
303 // public Console getConsole() {
304 // return console;
305 // }
306
307 public void ranBlock(final String line) {
308 PyObject jythonObj = console.getJythonObject("combo");
309 // if (jythonObj instanceof PyJavaInstance) {
310 Object combination = jythonObj.__tojava__(Object.class);
311 if (combination instanceof Combination) {
312 control.addCombination((Combination)combination);
313 control.getComputeButton().setEnabled(true);
314 control.showNormalCursor();
315 }
316 // }
317 }
318
319 public void updateSelector(final String id, final float channel) {
320 if (!selectorMap.containsKey(id))
321 return;
322
323 Selector selector = selectorMap.get(id);
324 selector.setWaveNumber(channel);
325 wrapperMap.get(id).update();
326 }
327
328 protected void addSelector(final Selector selector, final boolean enabled) throws Exception {
329 String id = selector.getId();
330 if (enabled) {
331 display.createSelector(id, selector.getColor());
332 display.setSelectorValue(id, selector.getWaveNumber());
333 }
334 selectorMap.put(id, selector);
335 }
336
337 protected void disableSelector(final SelectorWrapper wrapper, final boolean disableWrapper) {
338 if (disableWrapper)
339 wrapper.disable();
340
341 try {
342 display.removeSelector(wrapper.getSelector().getId());
343 } catch (Exception e) {
344 LogUtil.logException("HydraCombo.disableSelector", e);
345 }
346 }
347
348 protected void enableSelector(final SelectorWrapper wrapper, final boolean enableWrapper) {
349 if (enableWrapper)
350 wrapper.enable();
351
352 try {
353 Selector selector = wrapper.getSelector();
354 String id = selector.getId();
355 display.createSelector(id, selector.getColor());
356 display.setSelectorValue(id, selector.getWaveNumber());
357 } catch (Exception e) {
358 LogUtil.logException("HydraCombo.disableSelector", e);
359 }
360 }
361
362 private SelectorWrapper makeWrapper(final String var, final Color color) {
363 try {
364 ConstantMap[] mappedColor = MultiSpectralDisplay.makeColorMap(color);
365
366 SelectorWrapper tmp;
367 if (dataType == DataType.HYPERSPECTRAL)
368 tmp = new HyperspectralSelectorWrapper(var, mappedColor, control, console);
369 else
370 tmp = new MultispectralSelectorWrapper(var, mappedColor, control, console);
371 addSelector(tmp.getSelector(), false);
372 wrapperMap.put(tmp.getSelector().getId(), tmp);
373 // console.injectObject(var, new PyJavaInstance(tmp.getSelector()));
374 console.injectObject(var, tmp.getSelector());
375 return tmp;
376 } catch (Exception e) {
377 LogUtil.logException("HydraCombo.makeWrapper", e);
378 }
379 return null;
380 }
381
382 public JPanel getPanel() {
383 JPanel panel = new JPanel(new FlowLayout());
384 panel.add(new JLabel("("));
385 panel.add(a.getPanel());
386 panel.add(ab.getBox());
387 panel.add(b.getPanel());
388 panel.add(new JLabel(")"));
389 panel.add(abcd.getBox());
390 panel.add(new JLabel("("));
391 panel.add(c.getPanel());
392 panel.add(cd.getBox());
393 panel.add(d.getPanel());
394 panel.add(new JLabel(")"));
395 return panel;
396 }
397
398 public void queueCombination() {
399 String jy = "combo="+abcd.getJython();
400 System.err.println("jython=" + jy);
401 console.queueLine(jy);
402 }
403
404 public Hashtable<String, Object> persistData() {
405 Hashtable<String, Object> table = new Hashtable<String, Object>();
406
407 table.put("a", a.persistSelectorWrapper());
408 table.put("b", b.persistSelectorWrapper());
409 table.put("c", c.persistSelectorWrapper());
410 table.put("d", d.persistSelectorWrapper());
411 table.put("ab", ab.getOperation());
412 table.put("cd", cd.getOperation());
413 table.put("abcd", abcd.getOperation());
414 return table;
415 }
416
417 public void unpersistData(final Hashtable<String, Object> table) {
418 Hashtable<String, String> tableA = (Hashtable<String, String>)table.get("a");
419 Hashtable<String, String> tableB = (Hashtable<String, String>)table.get("b");
420 Hashtable<String, String> tableC = (Hashtable<String, String>)table.get("c");
421 Hashtable<String, String> tableD = (Hashtable<String, String>)table.get("d");
422
423 a.unpersistSelectorWrapper(tableA);
424 b.unpersistSelectorWrapper(tableB);
425 c.unpersistSelectorWrapper(tableC);
426 d.unpersistSelectorWrapper(tableD);
427
428 String opAb = (String)table.get("ab");
429 String opCd = (String)table.get("cd");
430 String opAbcd = (String)table.get("abcd");
431
432 ab.setOperation(opAb);
433 cd.setOperation(opCd);
434 abcd.setOperation(opAbcd);
435 }
436 }
437
438 private static class OperationXY {
439 private static final String[] OPERATIONS = { "+", "-", "/", "*", " " };
440 private static final String INVALID_OP = " ";
441 private final JComboBox combo = new JComboBox(OPERATIONS);
442
443 private CombinationPanel comboPanel;
444 private SelectorWrapper x;
445 private SelectorWrapper y;
446
447 public OperationXY(final CombinationPanel comboPanel, final SelectorWrapper x, final SelectorWrapper y) {
448 this.x = x;
449 this.y = y;
450 this.comboPanel = comboPanel;
451 combo.setSelectedItem(" ");
452 combo.addActionListener(new ActionListener() {
453 public void actionPerformed(final ActionEvent e) {
454 if (getOperation().equals(" ")) {
455 comboPanel.disableSelector(y, true);
456 } else {
457 comboPanel.enableSelector(y, true);
458 }
459 }
460 });
461 }
462
463 public void disable() {
464 comboPanel.disableSelector(x, true);
465 combo.setSelectedItem(INVALID_OP);
466 comboPanel.disableSelector(y, true);
467 }
468
469 public void enable() {
470 comboPanel.enableSelector(x, true);
471 }
472
473 public String getOperation() {
474 return (String)combo.getSelectedItem();
475 }
476
477 public void setOperation(final String operation) {
478 combo.setSelectedItem(operation);
479 }
480
481 public JComboBox getBox() {
482 return combo;
483 }
484
485 public String getJython() {
486 String operation = getOperation();
487 if (operation.equals(INVALID_OP))
488 operation = "";
489
490 String jython = x.getJython() + operation + y.getJython();
491 if (x.isValid() && y.isValid())
492 return "(" + jython + ")";
493 return jython;
494 }
495 }
496
497 private static class CombineOperations {
498 private static final String[] OPERATIONS = { "+", "-", "/", "*", " "};
499 private static final String INVALID_OP = " ";
500 private final JComboBox combo = new JComboBox(OPERATIONS);
501
502 private OperationXY x;
503 private OperationXY y;
504
505 public CombineOperations(final CombinationPanel comboPanel, final OperationXY x, final OperationXY y) {
506 this.x = x;
507 this.y = y;
508 combo.setSelectedItem(INVALID_OP);
509 combo.addActionListener(new ActionListener() {
510 public void actionPerformed(final ActionEvent e) {
511 if (getOperation().equals(" ")) {
512 y.disable();
513 } else {
514 y.enable();
515 }
516 }
517 });
518 }
519
520 public String getOperation() {
521 return (String)combo.getSelectedItem();
522 }
523
524 public void setOperation(final String operation) {
525 combo.setSelectedItem(operation);
526 }
527
528 public JComboBox getBox() {
529 return combo;
530 }
531
532 public String getJython() {
533 String operation = getOperation();
534 if (operation.equals(INVALID_OP))
535 operation = "";
536
537 String jython = x.getJython() + operation + y.getJython();
538 return jython;
539 }
540 }
541
542 private static abstract class SelectorWrapper {
543 protected static final String BLANK = "-----";
544 private String variable;
545 private final ConstantMap[] color;
546 protected final Selector selector;
547 protected final MultiSpectralDisplay display;
548 protected final MultiSpectralData data;
549 private final JTextField scale = new JTextField(String.format("%3.1f", 1.0));
550 protected WrapperState currentState = WrapperState.DISABLED;
551
552 public SelectorWrapper(final String variable, final ConstantMap[] color, final HydraCombo control, final Console console) {
553 this.display = control.getMultiSpectralDisplay();
554 this.data = control.getMultiSpectralDisplay().getMultiSpectralData();
555 this.variable = variable;
556 this.color = color;
557 this.selector = new Selector(control.init_wavenumber, color, control, console);
558 this.selector.addName(variable);
559 }
560
561 public Selector getSelector() {
562 return selector;
563 }
564
565 public JPanel getPanel() {
566 JPanel panel = new JPanel(new FlowLayout());
567 JComponent comp = getWavenumberComponent();
568 float r = new Double(color[0].getConstant()).floatValue();
569 float g = new Double(color[1].getConstant()).floatValue();
570 float b = new Double(color[2].getConstant()).floatValue();
571 comp.setBorder(new LineBorder(new Color(r, g, b), 2));
572 panel.add(scale);
573 panel.add(comp);
574 return panel;
575 }
576
577 public String getJython() {
578 if (!isValid())
579 return "";
580 return "(" + scale.getText() + "*" + variable + ")";
581 }
582
583 public boolean isValid() {
584 return !getValue().equals(BLANK);
585 }
586
587 public void enable() {
588 setValue(Float.toString(selector.getWaveNumber()));
589 currentState = WrapperState.ENABLED;
590 }
591
592 public void disable() {
593 setValue(BLANK);
594 currentState = WrapperState.DISABLED;
595 }
596
597 public void update() {
598 setValue(Float.toString(selector.getWaveNumber()));
599 }
600
601 public Hashtable<String, String> persistSelectorWrapper() {
602 Hashtable<String, String> table = new Hashtable<String, String>(3);
603 String scaleText = scale.getText();
604 String waveText = getValue();
605
606 if (scaleText == null || scaleText.length() == 0)
607 scaleText = "1.0";
608 if (waveText == null || waveText.length() == 0 || !isValid())
609 waveText = BLANK;
610
611 table.put("variable", variable);
612 table.put("scale", scale.getText());
613 table.put("wave", getValue());
614 return table;
615 }
616
617 public void unpersistSelectorWrapper(final Hashtable<String, String> table) {
618 variable = table.get("variable");
619 selector.addName(variable);
620 scale.setText(String.format("%3.1f", new Float(table.get("scale"))));
621
622 String waveText = table.get("wave");
623 if (waveText.equals(BLANK)) {
624 disable();
625 } else {
626 float wave = new Float(table.get("wave"));
627 selector.setWaveNumber(wave);
628 if (isValid())
629 update();
630 }
631 }
632
633 public abstract JComponent getWavenumberComponent();
634
635 public abstract void setValue(final String value);
636
637 public abstract String getValue();
638 }
639
640 private static class HyperspectralSelectorWrapper extends SelectorWrapper {
641 private final JTextField wavenumber;
642 public HyperspectralSelectorWrapper(final String variable, final ConstantMap[] color, final HydraCombo control, final Console console) {
643 super(variable, color, control, console);
644 wavenumber = new JTextField(BLANK, 7);
645
646 wavenumber.addActionListener(new ActionListener() {
647 public void actionPerformed(final ActionEvent e) {
648 String textVal = wavenumber.getText();
649 if (textVal.equals(BLANK))
650 return;
651
652 float wave = Float.parseFloat(textVal.trim());
653 control.updateComboPanel(getSelector().getId(), wave);
654 }
655 });
656 }
657
658 @Override public JComponent getWavenumberComponent() {
659 return wavenumber;
660 }
661
662 @Override public void setValue(final String value) {
663 if (value == null)
664 throw new NullPointerException("");
665
666 if (!value.equals(BLANK)) {
667 float wave = Float.parseFloat(value);
668 String fmt = String.format("%7.2f", wave);
669 wavenumber.setText(fmt);
670 }
671 }
672
673 public String getValue() {
674 return wavenumber.getText();
675 }
676 }
677
678 private static class MultispectralSelectorWrapper extends SelectorWrapper {
679
680 private final JComboBox bands;
681
682 public MultispectralSelectorWrapper(final String variable, final ConstantMap[] color, final HydraCombo control, final Console console) {
683 super(variable, color, control, console);
684 removeMSDListeners();
685 bands = bigBadBox(control);
686 }
687
688 private void removeMSDListeners() {
689 JComboBox box = display.getBandSelectComboBox();
690 for (ActionListener l : box.getActionListeners())
691 box.removeActionListener(l);
692 }
693
694 private JComboBox bigBadBox(final HydraCombo control) {
695 final JComboBox box = new JComboBox();
696 box.addItem(BLANK);
697
698 for (String name : data.getBandNames())
699 box.addItem(name);
700
701 final SelectorWrapper wrapper = this;
702 box.addActionListener(new ActionListener() {
703 public void actionPerformed(final ActionEvent e) {
704 String selected = (String)box.getSelectedItem();
705 float wave = Float.NaN;
706
707 if (!selected.equals(BLANK)) {
708 Map<String, Float> map = data.getBandNameMap();
709 if (map.containsKey(selected))
710 wave = map.get(selected);
711
712 if (currentState == WrapperState.DISABLED)
713 control.enableSelectorForWrapper(wrapper);
714 control.updateComboPanel(getSelector().getId(), wave);
715 } else {
716 control.disableSelectorForWrapper(wrapper);
717 }
718 }
719 });
720
721 return box;
722 }
723
724 @Override public JComponent getWavenumberComponent() {
725 return bands;
726 }
727
728 @Override public void setValue(final String value) {
729 if (value == null)
730 throw new NullPointerException();
731
732 if (!value.equals(BLANK)) {
733 String name = data.getBandNameFromWaveNumber(Float.parseFloat(value));
734 bands.setSelectedItem(name);
735 } else {
736 bands.setSelectedItem(BLANK);
737 }
738 }
739
740 @Override public String getValue() {
741 return (String)bands.getSelectedItem();
742 }
743 }
744 }