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 }