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