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.display.hydra;
030
031 import java.awt.Color;
032 import java.awt.Component;
033 import java.awt.event.ActionEvent;
034 import java.awt.event.ActionListener;
035 import java.rmi.RemoteException;
036 import java.util.ArrayList;
037 import java.util.Enumeration;
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.JComboBox;
044
045 import org.slf4j.Logger;
046 import org.slf4j.LoggerFactory;
047
048 import visad.CellImpl;
049 import visad.ConstantMap;
050 import visad.DataReference;
051 import visad.DataReferenceImpl;
052 import visad.Display;
053 import visad.DisplayEvent;
054 import visad.DisplayListener;
055 import visad.FlatField;
056 import visad.FunctionType;
057 import visad.Gridded1DSet;
058 import visad.Gridded2DSet;
059 import visad.LocalDisplay;
060 import visad.Real;
061 import visad.RealTuple;
062 import visad.RealTupleType;
063 import visad.RealType;
064 import visad.ScalarMap;
065 import visad.VisADException;
066 import visad.bom.RubberBandBoxRendererJ3D;
067
068 import ucar.unidata.data.DirectDataChoice;
069 import ucar.unidata.idv.ViewManager;
070 import ucar.unidata.util.LogUtil;
071 import ucar.visad.display.DisplayableData;
072 import ucar.visad.display.XYDisplay;
073
074 import edu.wisc.ssec.mcidasv.control.HydraCombo;
075 import edu.wisc.ssec.mcidasv.control.HydraControl;
076 import edu.wisc.ssec.mcidasv.control.LinearCombo;
077 import edu.wisc.ssec.mcidasv.control.MultiSpectralControl;
078 import edu.wisc.ssec.mcidasv.data.HydraDataSource;
079 import edu.wisc.ssec.mcidasv.data.hydra.GrabLineRendererJ3D;
080 import edu.wisc.ssec.mcidasv.data.hydra.HydraRGBDisplayable;
081 import edu.wisc.ssec.mcidasv.data.hydra.MultiDimensionSubset;
082 import edu.wisc.ssec.mcidasv.data.hydra.MultiSpectralData;
083 import edu.wisc.ssec.mcidasv.data.hydra.MultiSpectralDataSource;
084 import edu.wisc.ssec.mcidasv.data.hydra.SuomiNPPDataSource;
085
086 public class MultiSpectralDisplay implements DisplayListener {
087
088 private static final Logger logger = LoggerFactory.getLogger(MultiSpectralDisplay.class);
089
090 private static final String DISP_NAME = "Spectrum";
091 private static int cnt = 1;
092
093 private DirectDataChoice dataChoice;
094
095 private ViewManager viewManager;
096
097 private float[] initialRangeX;
098 private float[] initialRangeY = { 180f, 320f };
099
100 private RealType domainType;
101 private RealType rangeType;
102 private RealType uniqueRangeType;
103
104 private ScalarMap xmap;
105 private ScalarMap ymap;
106
107 private LocalDisplay display;
108
109 private FlatField image;
110
111 private FlatField spectrum = null;
112
113 private boolean imageExpired = true;
114
115 private MultiSpectralData data;
116
117 private float waveNumber;
118
119 private List<DataReference> displayedThings = new ArrayList<DataReference>();
120 private HashMap<String, DataReference> idToRef = new HashMap<String, DataReference>();
121 private HashMap<DataReference, ConstantMap[]> colorMaps =
122 new HashMap<DataReference, ConstantMap[]>();
123
124 private HydraControl displayControl;
125
126 private DisplayableData imageDisplay = null;
127
128 private XYDisplay master;
129
130 private Gridded1DSet domainSet;
131
132 private JComboBox bandSelectComboBox = null;
133
134 public MultiSpectralDisplay(final HydraControl control)
135 throws VisADException, RemoteException
136 {
137 displayControl = control;
138 dataChoice = (DirectDataChoice)displayControl.getDataChoice();
139
140 init();
141 }
142
143 public MultiSpectralDisplay(final DirectDataChoice dataChoice)
144 throws VisADException, RemoteException
145 {
146 this.dataChoice = dataChoice;
147 init();
148 }
149
150 // TODO: generalize this so that you can grab the image data for any
151 // channel
152 public FlatField getImageData() {
153 try {
154 if ((imageExpired) || (image == null)) {
155 imageExpired = false;
156
157 MultiDimensionSubset select = null;
158 Hashtable table = dataChoice.getProperties();
159 Enumeration keys = table.keys();
160 while (keys.hasMoreElements()) {
161 Object key = keys.nextElement();
162 if (key instanceof MultiDimensionSubset) {
163 select = (MultiDimensionSubset) table.get(key);
164 }
165 }
166 HashMap subset = select.getSubset();
167 image = data.getImage(waveNumber, subset);
168 image = changeRangeType(image, uniqueRangeType);
169 }
170 } catch (Exception e) {
171 LogUtil.logException("MultiSpectralDisplay.getImageData", e);
172 }
173
174 return image;
175 }
176
177 public FlatField getImageDataFrom(final float channel) {
178 FlatField imageData = null;
179 try {
180 MultiDimensionSubset select = null;
181 Hashtable table = dataChoice.getProperties();
182 Enumeration keys = table.keys();
183 while (keys.hasMoreElements()) {
184 Object key = keys.nextElement();
185 if (key instanceof MultiDimensionSubset) {
186 select = (MultiDimensionSubset) table.get(key);
187 }
188 }
189 HashMap subset = select.getSubset();
190 imageData = data.getImage(channel, subset);
191 uniqueRangeType = RealType.getRealType(rangeType.getName()+"_"+cnt++);
192 imageData = changeRangeType(imageData, uniqueRangeType);
193 } catch (Exception e) {
194 LogUtil.logException("MultiSpectralDisplay.getImageDataFrom", e);
195 }
196 return imageData;
197 }
198
199 private FlatField changeRangeType(FlatField image, RealType newRangeType) throws VisADException, RemoteException {
200 FunctionType ftype = (FunctionType)image.getType();
201 FlatField new_image = new FlatField(
202 new FunctionType(ftype.getDomain(), newRangeType), image.getDomainSet());
203 new_image.setSamples(image.getFloats(false), false);
204 return new_image;
205 }
206
207
208 public LocalDisplay getDisplay() {
209 return display;
210 }
211
212 public Component getDisplayComponent() {
213 return master.getDisplayComponent();
214 }
215
216 public RealType getDomainType() {
217 return domainType;
218 }
219
220 public RealType getRangeType() {
221 return rangeType;
222 }
223
224 public ViewManager getViewManager() {
225 return viewManager;
226 }
227
228 public MultiSpectralData getMultiSpectralData() {
229 return data;
230 }
231
232 public Gridded1DSet getDomainSet() {
233 return domainSet;
234 }
235
236 private void init() throws VisADException, RemoteException {
237
238 HydraDataSource source =
239 (HydraDataSource) dataChoice.getDataSource();
240
241 // TODO revisit this, may want to move method up to base class HydraDataSource
242 if (source instanceof SuomiNPPDataSource) {
243 data = ((SuomiNPPDataSource) source).getMultiSpectralData(dataChoice);
244 }
245
246 if (source instanceof MultiSpectralDataSource) {
247 data = ((MultiSpectralDataSource) source).getMultiSpectralData(dataChoice);
248 }
249
250 waveNumber = data.init_wavenumber;
251
252 try {
253 spectrum = data.getSpectrum(new int[] { 1, 1 });
254 } catch (Exception e) {
255 LogUtil.logException("MultiSpectralDisplay.init", e);
256 }
257
258 domainSet = (Gridded1DSet)spectrum.getDomainSet();
259 initialRangeX = getXRange(domainSet);
260 initialRangeY = data.getDataRange();
261
262 domainType = getDomainType(spectrum);
263 rangeType = getRangeType(spectrum);
264
265 master = new XYDisplay(DISP_NAME, domainType, rangeType);
266
267 setDisplayMasterAttributes(master);
268
269 // set up the x- and y-axis
270 xmap = new ScalarMap(domainType, Display.XAxis);
271 ymap = new ScalarMap(rangeType, Display.YAxis);
272
273 xmap.setRange(initialRangeX[0], initialRangeX[1]);
274 ymap.setRange(initialRangeY[0], initialRangeY[1]);
275
276 display = master.getDisplay();
277 display.addMap(xmap);
278 display.addMap(ymap);
279 display.addDisplayListener(this);
280
281 new RubberBandBox(this, xmap, ymap);
282
283 if (displayControl == null) { //- add in a ref for the default spectrum, ie no DisplayControl
284 DataReferenceImpl spectrumRef = new DataReferenceImpl(hashCode() + "_spectrumRef");
285 spectrumRef.setData(spectrum);
286 addRef(spectrumRef, Color.WHITE);
287 }
288
289 if (data.hasBandNames()) {
290 bandSelectComboBox = new JComboBox(data.getBandNames().toArray());
291 bandSelectComboBox.setSelectedItem(data.init_bandName);
292 bandSelectComboBox.addActionListener(new ActionListener() {
293 public void actionPerformed(ActionEvent e) {
294 String bandName = (String)bandSelectComboBox.getSelectedItem();
295 if (bandName == null)
296 return;
297
298 HashMap<String, Float> bandMap = data.getBandNameMap();
299 if (bandMap == null)
300 return;
301
302 if (!bandMap.containsKey(bandName))
303 return;
304
305 setWaveNumber(bandMap.get(bandName));
306 }
307 });
308 }
309 }
310
311 public JComboBox getBandSelectComboBox() {
312 return bandSelectComboBox;
313 }
314
315 // TODO: HACK!!
316 public void setDisplayControl(final HydraControl control) {
317 displayControl = control;
318 }
319
320 public void displayChanged(final DisplayEvent e) throws VisADException, RemoteException {
321 // TODO: write a method like isChannelUpdate(EVENT_ID)? or maybe just
322 // deal with a super long if-statement and put an "OR MOUSE_RELEASED"
323 // up here?
324 if (e.getId() == DisplayEvent.MOUSE_RELEASED_CENTER) {
325 float val = (float)display.getDisplayRenderer().getDirectAxisValue(domainType);
326 setWaveNumber(val);
327 if (displayControl != null)
328 displayControl.handleChannelChange(val);
329 }
330 else if (e.getId() == DisplayEvent.MOUSE_PRESSED_LEFT) {
331 if (e.getInputEvent().isControlDown()) {
332 xmap.setRange(initialRangeX[0], initialRangeX[1]);
333 ymap.setRange(initialRangeY[0], initialRangeY[1]);
334 }
335 }
336 else if (e.getId() == DisplayEvent.MOUSE_RELEASED) {
337 float val = getSelectorValue(channelSelector);
338 if (val != waveNumber) {
339 // TODO: setWaveNumber needs to be rethought, as it calls
340 // setSelectorValue which is redundant in the cases of dragging
341 // or clicking
342 setWaveNumber(val);
343 if (displayControl != null)
344 displayControl.handleChannelChange(val);
345 }
346 }
347 }
348
349 public DisplayableData getImageDisplay() {
350 if (imageDisplay == null) {
351 try {
352 uniqueRangeType = RealType.getRealType(rangeType.getName()+"_"+cnt++);
353 imageDisplay = new HydraRGBDisplayable("image", uniqueRangeType, null, true, displayControl);
354 } catch (Exception e) {
355 LogUtil.logException("MultiSpectralDisplay.getImageDisplay", e);
356 }
357 }
358 return imageDisplay;
359 }
360
361 public float getWaveNumber() {
362 return waveNumber;
363 }
364
365 public int getChannelIndex() throws Exception {
366 return data.getChannelIndexFromWavenumber(waveNumber);
367 }
368
369 public void refreshDisplay() throws VisADException, RemoteException {
370 if (display == null)
371 return;
372
373 synchronized (displayedThings) {
374 for (DataReference ref : displayedThings) {
375 display.removeReference(ref);
376 display.addReference(ref, colorMaps.get(ref));
377 }
378 }
379 }
380
381 public boolean hasNullData() {
382 try {
383 synchronized (displayedThings) {
384 for (DataReference ref : displayedThings) {
385 if (ref.getData() == null)
386 return true;
387 }
388 }
389 } catch (Exception e) { }
390 return false;
391 }
392
393 /** ID of the selector that controls the displayed channel. */
394 private final String channelSelector = hashCode() + "_chanSelect";
395
396 /** The map of selector IDs to selectors. */
397 private final Map<String, DragLine> selectors =
398 new HashMap<String, DragLine>();
399
400 public void showChannelSelector() {
401 try {
402 createSelector(channelSelector, Color.GREEN);
403 } catch (Exception e) {
404 LogUtil.logException("MultiSpectralDisplay.showChannelSelector", e);
405 }
406 }
407
408 public void hideChannelSelector() {
409 try {
410 DragLine selector = removeSelector(channelSelector);
411 selector = null;
412 } catch (Exception e) {
413 LogUtil.logException("MultiSpectralDisplay.hideChannelSelector", e);
414 }
415 }
416
417 public DragLine createSelector(final String id, final Color color) throws Exception {
418 if (id == null)
419 throw new NullPointerException("selector id cannot be null");
420 if (color == null)
421 throw new NullPointerException("selector color cannot be null");
422 return createSelector(id, makeColorMap(color));
423 }
424
425 public DragLine createSelector(final String id, final ConstantMap[] color) throws Exception {
426 if (id == null)
427 throw new NullPointerException("selector id cannot be null");
428 if (color == null)
429 throw new NullPointerException("selector color cannot be null");
430
431 if (selectors.containsKey(id))
432 return selectors.get(id);
433
434 DragLine selector = new DragLine(this, id, color, initialRangeY);
435 selector.setHydraControl(displayControl);
436 selector.setSelectedValue(waveNumber);
437 selectors.put(id, selector);
438 return selector;
439 }
440
441 public DragLine getSelector(final String id) {
442 return selectors.get(id);
443 }
444
445 public float getSelectorValue(final String id) {
446 DragLine selector = selectors.get(id);
447 if (selector == null)
448 return Float.NaN;
449 return selector.getSelectedValue();
450 }
451
452 public void setSelectorValue(final String id, final float value)
453 throws VisADException, RemoteException
454 {
455 DragLine selector = selectors.get(id);
456 if (selector != null)
457 selector.setSelectedValue(value);
458 }
459
460 // BAD BAD BAD BAD
461 public void updateControlSelector(final String id, final float value) {
462 if (displayControl == null)
463 return;
464 if (displayControl instanceof LinearCombo) {
465 ((LinearCombo)displayControl).updateSelector(id, value);
466 } else if (displayControl instanceof HydraCombo) {
467 ((HydraCombo)displayControl).updateComboPanel(id, value);
468 }
469 }
470
471 public DragLine removeSelector(final String id) {
472 DragLine selector = selectors.remove(id);
473 if (selector == null)
474 return null;
475 selector.annihilate();
476 return selector;
477 }
478
479 public List<DragLine> getSelectors() {
480 return new ArrayList<DragLine>(selectors.values());
481 }
482
483 /**
484 * @return Whether or not the channel selector is being displayed.
485 */
486 public boolean displayingChannel() {
487 return (getSelector(channelSelector) != null);
488 }
489
490 public void removeRef(final DataReference thing) throws VisADException,
491 RemoteException
492 {
493 if (display == null)
494 return;
495
496 synchronized (displayedThings) {
497 displayedThings.remove(thing);
498 colorMaps.remove(thing);
499 idToRef.remove(thing.getName());
500 display.removeReference(thing);
501 }
502 }
503
504 public void addRef(final DataReference thing, final Color color)
505 throws VisADException, RemoteException
506 {
507 if (display == null)
508 return;
509
510 synchronized (displayedThings) {
511 ConstantMap[] colorMap = makeColorMap(color);
512
513 displayedThings.add(thing);
514 idToRef.put(thing.getName(), thing);
515 ConstantMap[] constMaps;
516 if (data.hasBandNames()) {
517 constMaps = new ConstantMap[colorMap.length+2];
518 System.arraycopy(colorMap, 0, constMaps, 0, colorMap.length);
519 constMaps[colorMap.length] = new ConstantMap(1f, Display.PointMode);
520 constMaps[colorMap.length+1] = new ConstantMap(5f, Display.PointSize);
521 } else {
522 constMaps = colorMap;
523 }
524 colorMaps.put(thing, constMaps);
525
526 display.addReference(thing, constMaps);
527 }
528 }
529
530 public void updateRef(final DataReference thing, final Color color)
531 throws VisADException, RemoteException
532 {
533 ConstantMap[] colorMap = makeColorMap(color);
534 ConstantMap[] constMaps;
535 if (data.hasBandNames()) {
536 constMaps = new ConstantMap[colorMap.length+2];
537 System.arraycopy(colorMap, 0, constMaps, 0, colorMap.length);
538 constMaps[colorMap.length] = new ConstantMap(1f, Display.PointMode);
539 constMaps[colorMap.length+1] = new ConstantMap(5f, Display.PointSize);
540 } else {
541 constMaps = colorMap;
542 }
543 colorMaps.put(thing, constMaps);
544 idToRef.put(thing.getName(), thing);
545 refreshDisplay();
546 }
547
548 public void reorderDataRefsById(final List<String> dataRefIds) {
549 if (dataRefIds == null)
550 throw new NullPointerException("");
551
552 synchronized (displayedThings) {
553 try {
554 displayedThings.clear();
555 for (String refId : dataRefIds) {
556 DataReference ref = idToRef.get(refId);
557 ConstantMap[] color = colorMaps.get(ref);
558 display.removeReference(ref);
559 display.addReference(ref, color);
560 }
561 } catch (Exception e) { }
562 }
563 }
564
565 // TODO: needs work
566 public boolean setWaveNumber(final float val) {
567 if (data == null)
568 return false;
569
570 if (waveNumber == val)
571 return true;
572
573 try {
574 if (spectrum == null) {
575 spectrum = data.getSpectrum(new int[] { 1, 1 });
576 }
577
578 Gridded1DSet domain = (Gridded1DSet)spectrum.getDomainSet();
579 int[] idx = domain.valueToIndex(new float[][] { { val } });
580 float[][] tmp = domain.indexToValue(idx);
581 float channel = tmp[0][0];
582
583 setSelectorValue(channelSelector, channel);
584
585 imageExpired = true;
586 } catch (Exception e) {
587 LogUtil.logException("MultiSpectralDisplay.setDisplayedWaveNum", e);
588 return false;
589 }
590
591 waveNumber = val;
592
593 if (data.hasBandNames()) {
594 String name = data.getBandNameFromWaveNumber(waveNumber);
595 bandSelectComboBox.setSelectedItem(name);
596 }
597
598 return true;
599 }
600
601 /**
602 * @return The ConstantMap representation of <code>color</code>.
603 */
604 public static ConstantMap[] makeColorMap(final Color color)
605 throws VisADException, RemoteException
606 {
607 float r = color.getRed() / 255f;
608 float g = color.getGreen() / 255f;
609 float b = color.getBlue() / 255f;
610 float a = color.getAlpha() / 255f;
611 return new ConstantMap[] { new ConstantMap(r, Display.Red),
612 new ConstantMap(g, Display.Green),
613 new ConstantMap(b, Display.Blue),
614 new ConstantMap(a, Display.Alpha) };
615 }
616
617 /**
618 * Provides <code>master</code> some sensible default attributes.
619 */
620 private static void setDisplayMasterAttributes(final XYDisplay master)
621 throws VisADException, RemoteException
622 {
623 master.showAxisScales(true);
624 master.setAspect(2.5, 0.75);
625
626 double[] proj = master.getProjectionMatrix();
627 proj[0] = 0.35;
628 proj[5] = 0.35;
629 proj[10] = 0.35;
630
631 master.setProjectionMatrix(proj);
632 }
633
634 /**
635 * @return The minimum and maximum values found on the x-axis.
636 */
637 private static float[] getXRange(final Gridded1DSet domain) {
638 return new float[] { domain.getLow()[0], domain.getHi()[0] };
639 }
640
641 public static RealType getRangeType(final FlatField spectrum) {
642 return (((FunctionType)spectrum.getType()).getFlatRange().getRealComponents())[0];
643 }
644
645 private static RealType getDomainType(final FlatField spectrum) {
646 return (((FunctionType)spectrum.getType()).getDomain().getRealComponents())[0];
647 }
648
649 private static class RubberBandBox extends CellImpl {
650
651 private static final String RBB = "_rubberband";
652
653 private DataReference rubberBand;
654
655 private boolean init = false;
656
657 private ScalarMap xmap;
658
659 private ScalarMap ymap;
660
661 public RubberBandBox(final MultiSpectralDisplay msd,
662 final ScalarMap x, final ScalarMap y) throws VisADException,
663 RemoteException
664 {
665 RealType domainType = msd.getDomainType();
666 RealType rangeType = msd.getRangeType();
667
668 LocalDisplay display = msd.getDisplay();
669
670 rubberBand = new DataReferenceImpl(hashCode() + RBB);
671 rubberBand.setData(new RealTuple(new RealTupleType(domainType,
672 rangeType), new double[] { Double.NaN, Double.NaN }));
673
674 display.addReferences(new RubberBandBoxRendererJ3D(domainType,
675 rangeType, 1, 1), new DataReference[] { rubberBand }, null);
676
677 xmap = x;
678 ymap = y;
679
680 this.addReference(rubberBand);
681 }
682
683 public void doAction() throws VisADException, RemoteException {
684 if (!init) {
685 init = true;
686 return;
687 }
688
689 Gridded2DSet set = (Gridded2DSet)rubberBand.getData();
690
691 float[] low = set.getLow();
692 float[] high = set.getHi();
693
694 xmap.setRange(low[0], high[0]);
695 ymap.setRange(low[1], high[1]);
696 }
697 }
698
699 public static class DragLine extends CellImpl {
700 private final String selectorId = hashCode() + "_selector";
701 private final String lineId = hashCode() + "_line";
702 private final String controlId;
703
704 private ConstantMap[] mappings = new ConstantMap[5];
705
706 private DataReference line;
707
708 private DataReference selector;
709
710 private MultiSpectralDisplay multiSpectralDisplay;
711
712 private HydraControl hydraControl;
713
714 private RealType domainType;
715 private RealType rangeType;
716
717 private RealTupleType tupleType;
718
719 private LocalDisplay display;
720
721 private float[] YRANGE;
722
723 private float lastSelectedValue;
724
725 public DragLine(final MultiSpectralDisplay msd, final String controlId, final Color color) throws Exception {
726 this(msd, controlId, makeColorMap(color));
727 }
728
729 public DragLine(final MultiSpectralDisplay msd, final String controlId, final Color color, float[] YRANGE) throws Exception {
730 this(msd, controlId, makeColorMap(color), YRANGE);
731 }
732
733 public DragLine(final MultiSpectralDisplay msd, final String controlId,
734 final ConstantMap[] color) throws Exception
735 {
736 this(msd, controlId, color, new float[] {180f, 320f});
737 }
738
739 public DragLine(final MultiSpectralDisplay msd, final String controlId,
740 final ConstantMap[] color, float[] YRANGE) throws Exception
741 {
742 if (msd == null)
743 throw new NullPointerException("must provide a non-null MultiSpectralDisplay");
744 if (controlId == null)
745 throw new NullPointerException("must provide a non-null control ID");
746 if (color == null)
747 throw new NullPointerException("must provide a non-null color");
748
749 this.controlId = controlId;
750 this.multiSpectralDisplay = msd;
751 this.YRANGE = YRANGE;
752 lastSelectedValue = multiSpectralDisplay.getWaveNumber();
753
754 for (int i = 0; i < color.length; i++) {
755 mappings[i] = (ConstantMap)color[i].clone();
756 }
757 mappings[4] = new ConstantMap(-0.5, Display.YAxis);
758
759 Gridded1DSet domain = multiSpectralDisplay.getDomainSet();
760
761 domainType = multiSpectralDisplay.getDomainType();
762 rangeType = multiSpectralDisplay.getRangeType();
763 tupleType = new RealTupleType(domainType, rangeType);
764
765 selector = new DataReferenceImpl(selectorId);
766 line = new DataReferenceImpl(lineId);
767
768 display = multiSpectralDisplay.getDisplay();
769
770 display.addReferences(new GrabLineRendererJ3D(domain), new DataReference[] { selector }, new ConstantMap[][] { mappings });
771 display.addReference(line, cloneMappedColor(color));
772
773 addReference(selector);
774 }
775
776 private static ConstantMap[] cloneMappedColor(final ConstantMap[] color) throws Exception {
777 assert color != null && color.length >= 3 : color;
778 return new ConstantMap[] {
779 (ConstantMap)color[0].clone(),
780 (ConstantMap)color[1].clone(),
781 (ConstantMap)color[2].clone(),
782 };
783 }
784
785 public void annihilate() {
786 try {
787 display.removeReference(selector);
788 display.removeReference(line);
789 } catch (Exception e) {
790 LogUtil.logException("DragLine.annihilate", e);
791 }
792 }
793
794 public String getControlId() {
795 return controlId;
796 }
797
798 /**
799 * Handles drag and drop updates.
800 */
801 public void doAction() throws VisADException, RemoteException {
802 setSelectedValue(getSelectedValue());
803 }
804
805 public float getSelectedValue() {
806 float val = (float)display.getDisplayRenderer().getDirectAxisValue(domainType);
807 if (Float.isNaN(val))
808 val = lastSelectedValue;
809 return val;
810 }
811
812 public void setSelectedValue(final float val) throws VisADException,
813 RemoteException
814 {
815 // don't do work for stupid values
816 if ((Float.isNaN(val))
817 || (selector.getThing() != null && val == lastSelectedValue))
818 return;
819
820 line.setData(new Gridded2DSet(tupleType,
821 new float[][] { { val, val }, { YRANGE[0], YRANGE[1] } }, 2));
822
823 selector.setData(new Real(domainType, val));
824 lastSelectedValue = val;
825
826 if (hydraControl instanceof MultiSpectralControl) {
827 ((MultiSpectralControl) hydraControl).setWavelengthLabel
828 (
829 MultiSpectralControl.WAVENUMLABEL + val
830 );
831 }
832 multiSpectralDisplay.updateControlSelector(controlId, val);
833 }
834
835 /**
836 * Set the display control so we can call back and update
837 * wavelength readout in real time.
838 *
839 * @param hydraControl the display control to set
840 */
841
842 public void setHydraControl(HydraControl hydraControl) {
843 this.hydraControl = hydraControl;
844 }
845 }
846 }