001/* 002 * This file is part of McIDAS-V 003 * 004 * Copyright 2007-2016 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 029package edu.wisc.ssec.mcidasv.display.hydra; 030 031import java.awt.Color; 032import java.awt.Component; 033import java.awt.event.ActionEvent; 034import java.awt.event.ActionListener; 035import java.rmi.RemoteException; 036import java.util.ArrayList; 037import java.util.Enumeration; 038import java.util.HashMap; 039import java.util.Hashtable; 040import java.util.List; 041import java.util.Map; 042 043import javax.swing.JComboBox; 044 045import org.slf4j.Logger; 046import org.slf4j.LoggerFactory; 047 048import visad.CellImpl; 049import visad.ConstantMap; 050import visad.DataReference; 051import visad.DataReferenceImpl; 052import visad.Display; 053import visad.DisplayEvent; 054import visad.DisplayListener; 055import visad.FlatField; 056import visad.FunctionType; 057import visad.Gridded1DSet; 058import visad.Gridded2DSet; 059import visad.LocalDisplay; 060import visad.Real; 061import visad.RealTuple; 062import visad.RealTupleType; 063import visad.RealType; 064import visad.ScalarMap; 065import visad.VisADException; 066import visad.bom.RubberBandBoxRendererJ3D; 067 068import ucar.unidata.data.DirectDataChoice; 069import ucar.unidata.idv.ViewManager; 070import ucar.unidata.util.LogUtil; 071import ucar.visad.display.DisplayableData; 072import ucar.visad.display.XYDisplay; 073 074import edu.wisc.ssec.mcidasv.control.HydraCombo; 075import edu.wisc.ssec.mcidasv.control.HydraControl; 076import edu.wisc.ssec.mcidasv.control.LinearCombo; 077import edu.wisc.ssec.mcidasv.control.MultiSpectralControl; 078import edu.wisc.ssec.mcidasv.data.HydraDataSource; 079import edu.wisc.ssec.mcidasv.data.hydra.GrabLineRendererJ3D; 080import edu.wisc.ssec.mcidasv.data.hydra.HydraRGBDisplayable; 081import edu.wisc.ssec.mcidasv.data.hydra.MultiDimensionSubset; 082import edu.wisc.ssec.mcidasv.data.hydra.MultiSpectralData; 083import edu.wisc.ssec.mcidasv.data.hydra.MultiSpectralDataSource; 084import edu.wisc.ssec.mcidasv.data.hydra.SuomiNPPDataSource; 085 086public 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<>(); 120 private HashMap<String, DataReference> idToRef = new HashMap<>(); 121 private HashMap<DataReference, ConstantMap[]> colorMaps = new HashMap<>(); 122 123 private HydraControl displayControl; 124 125 private DisplayableData imageDisplay = null; 126 127 private XYDisplay master; 128 129 private Gridded1DSet domainSet; 130 131 private JComboBox bandSelectComboBox = null; 132 133 public MultiSpectralDisplay(final HydraControl control) 134 throws VisADException, RemoteException 135 { 136 displayControl = control; 137 dataChoice = (DirectDataChoice)displayControl.getDataChoice(); 138 139 init(); 140 } 141 142 public MultiSpectralDisplay(final DirectDataChoice dataChoice) 143 throws VisADException, RemoteException 144 { 145 this.dataChoice = dataChoice; 146 init(); 147 } 148 149 // TODO: generalize this so that you can grab the image data for any 150 // channel 151 public FlatField getImageData() { 152 try { 153 if ((imageExpired) || (image == null)) { 154 imageExpired = false; 155 156 MultiDimensionSubset select = null; 157 Hashtable table = dataChoice.getProperties(); 158 Enumeration keys = table.keys(); 159 while (keys.hasMoreElements()) { 160 Object key = keys.nextElement(); 161 if (key instanceof MultiDimensionSubset) { 162 select = (MultiDimensionSubset) table.get(key); 163 } 164 } 165 Map<String, double[]> subset = select.getSubset(); 166 image = data.getImage(waveNumber, subset); 167 image = changeRangeType(image, uniqueRangeType); 168 } 169 } catch (Exception e) { 170 LogUtil.logException("MultiSpectralDisplay.getImageData", e); 171 } 172 173 return image; 174 } 175 176 public FlatField getImageDataFrom(final float channel) { 177 FlatField imageData = null; 178 try { 179 MultiDimensionSubset select = null; 180 Hashtable table = dataChoice.getProperties(); 181 Enumeration keys = table.keys(); 182 while (keys.hasMoreElements()) { 183 Object key = keys.nextElement(); 184 if (key instanceof MultiDimensionSubset) { 185 select = (MultiDimensionSubset) table.get(key); 186 } 187 } 188 Map<String, double[]> subset = select.getSubset(); 189 imageData = data.getImage(channel, subset); 190 uniqueRangeType = RealType.getRealType(rangeType.getName()+"_"+cnt++); 191 imageData = changeRangeType(imageData, uniqueRangeType); 192 } catch (Exception e) { 193 LogUtil.logException("MultiSpectralDisplay.getImageDataFrom", e); 194 } 195 return imageData; 196 } 197 198 private FlatField changeRangeType(FlatField image, RealType newRangeType) throws VisADException, RemoteException { 199 FunctionType ftype = (FunctionType)image.getType(); 200 FlatField new_image = new FlatField( 201 new FunctionType(ftype.getDomain(), newRangeType), image.getDomainSet()); 202 new_image.setSamples(image.getFloats(false), false); 203 return new_image; 204 } 205 206 public XYDisplay getMaster() { 207 return master; 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 Map<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.setHydraControl(displayControl); 438 selector.setSelectedValue(waveNumber); 439 selectors.put(id, selector); 440 return selector; 441 } 442 443 public DragLine getSelector(final String id) { 444 return selectors.get(id); 445 } 446 447 public float getSelectorValue(final String id) { 448 DragLine selector = selectors.get(id); 449 if (selector == null) 450 return Float.NaN; 451 return selector.getSelectedValue(); 452 } 453 454 public void setSelectorValue(final String id, final float value) 455 throws VisADException, RemoteException 456 { 457 DragLine selector = selectors.get(id); 458 if (selector != null) 459 selector.setSelectedValue(value); 460 } 461 462 // BAD BAD BAD BAD 463 public void updateControlSelector(final String id, final float value) { 464 if (displayControl == null) 465 return; 466 if (displayControl instanceof LinearCombo) { 467 ((LinearCombo)displayControl).updateSelector(id, value); 468 } else if (displayControl instanceof HydraCombo) { 469 ((HydraCombo)displayControl).updateComboPanel(id, value); 470 } 471 } 472 473 public DragLine removeSelector(final String id) { 474 DragLine selector = selectors.remove(id); 475 if (selector == null) 476 return null; 477 selector.annihilate(); 478 return selector; 479 } 480 481 public List<DragLine> getSelectors() { 482 return new ArrayList<DragLine>(selectors.values()); 483 } 484 485 /** 486 * @return Whether or not the channel selector is being displayed. 487 */ 488 public boolean displayingChannel() { 489 return (getSelector(channelSelector) != null); 490 } 491 492 public void removeRef(final DataReference thing) throws VisADException, 493 RemoteException 494 { 495 if (display == null) 496 return; 497 498 synchronized (displayedThings) { 499 displayedThings.remove(thing); 500 colorMaps.remove(thing); 501 idToRef.remove(thing.getName()); 502 display.removeReference(thing); 503 } 504 } 505 506 public void addRef(final DataReference thing, final Color color) 507 throws VisADException, RemoteException 508 { 509 if (display == null) 510 return; 511 512 synchronized (displayedThings) { 513 ConstantMap[] colorMap = makeColorMap(color); 514 515 displayedThings.add(thing); 516 idToRef.put(thing.getName(), thing); 517 ConstantMap[] constMaps; 518 if (data.hasBandNames()) { 519 constMaps = new ConstantMap[colorMap.length+2]; 520 System.arraycopy(colorMap, 0, constMaps, 0, colorMap.length); 521 constMaps[colorMap.length] = new ConstantMap(1f, Display.PointMode); 522 constMaps[colorMap.length+1] = new ConstantMap(5f, Display.PointSize); 523 } else { 524 constMaps = colorMap; 525 } 526 colorMaps.put(thing, constMaps); 527 528 display.addReference(thing, constMaps); 529 } 530 } 531 532 public void updateRef(final DataReference thing, final Color color) 533 throws VisADException, RemoteException 534 { 535 ConstantMap[] colorMap = makeColorMap(color); 536 ConstantMap[] constMaps; 537 if (data.hasBandNames()) { 538 constMaps = new ConstantMap[colorMap.length+2]; 539 System.arraycopy(colorMap, 0, constMaps, 0, colorMap.length); 540 constMaps[colorMap.length] = new ConstantMap(1f, Display.PointMode); 541 constMaps[colorMap.length+1] = new ConstantMap(5f, Display.PointSize); 542 } else { 543 constMaps = colorMap; 544 } 545 colorMaps.put(thing, constMaps); 546 idToRef.put(thing.getName(), thing); 547 refreshDisplay(); 548 } 549 550 public void reorderDataRefsById(final List<String> dataRefIds) { 551 if (dataRefIds == null) 552 throw new NullPointerException(""); 553 554 synchronized (displayedThings) { 555 try { 556 displayedThings.clear(); 557 for (String refId : dataRefIds) { 558 DataReference ref = idToRef.get(refId); 559 ConstantMap[] color = colorMaps.get(ref); 560 display.removeReference(ref); 561 display.addReference(ref, color); 562 } 563 } catch (Exception e) { } 564 } 565 } 566 567 // TODO: needs work 568 public boolean setWaveNumber(final float val) { 569 if (data == null) 570 return false; 571 572 if (waveNumber == val) 573 return true; 574 575 try { 576 if (spectrum == null) { 577 spectrum = data.getSpectrum(new int[] { 1, 1 }); 578 } 579 580 Gridded1DSet domain = (Gridded1DSet)spectrum.getDomainSet(); 581 int[] idx = domain.valueToIndex(new float[][] { { val } }); 582 float[][] tmp = domain.indexToValue(idx); 583 float channel = tmp[0][0]; 584 585 setSelectorValue(channelSelector, channel); 586 587 imageExpired = true; 588 } catch (Exception e) { 589 LogUtil.logException("MultiSpectralDisplay.setDisplayedWaveNum", e); 590 return false; 591 } 592 593 waveNumber = val; 594 595 if (data.hasBandNames()) { 596 String name = data.getBandNameFromWaveNumber(waveNumber); 597 bandSelectComboBox.setSelectedItem(name); 598 } 599 600 return true; 601 } 602 603 /** 604 * @return The ConstantMap representation of {@code color}. 605 */ 606 public static ConstantMap[] makeColorMap(final Color color) 607 throws VisADException, RemoteException 608 { 609 float r = color.getRed() / 255f; 610 float g = color.getGreen() / 255f; 611 float b = color.getBlue() / 255f; 612 float a = color.getAlpha() / 255f; 613 return new ConstantMap[] { new ConstantMap(r, Display.Red), 614 new ConstantMap(g, Display.Green), 615 new ConstantMap(b, Display.Blue), 616 new ConstantMap(a, Display.Alpha) }; 617 } 618 619 /** 620 * Provides {@code master} some sensible default attributes. 621 */ 622 private static void setDisplayMasterAttributes(final XYDisplay master) 623 throws VisADException, RemoteException 624 { 625 master.showAxisScales(true); 626 master.setAspect(2.5, 0.75); 627 628 double[] proj = master.getProjectionMatrix(); 629 proj[0] = 0.35; 630 proj[5] = 0.35; 631 proj[10] = 0.35; 632 633 master.setProjectionMatrix(proj); 634 } 635 636 /** 637 * @return The minimum and maximum values found on the x-axis. 638 */ 639 private static float[] getXRange(final Gridded1DSet domain) { 640 return new float[] { domain.getLow()[0], domain.getHi()[0] }; 641 } 642 643 public static RealType getRangeType(final FlatField spectrum) { 644 return (((FunctionType)spectrum.getType()).getFlatRange().getRealComponents())[0]; 645 } 646 647 private static RealType getDomainType(final FlatField spectrum) { 648 return (((FunctionType)spectrum.getType()).getDomain().getRealComponents())[0]; 649 } 650 651 private static class RubberBandBox extends CellImpl { 652 653 private static final String RBB = "_rubberband"; 654 655 private DataReference rubberBand; 656 657 private boolean init = false; 658 659 private ScalarMap xmap; 660 661 private ScalarMap ymap; 662 663 public RubberBandBox(final MultiSpectralDisplay msd, 664 final ScalarMap x, final ScalarMap y) throws VisADException, 665 RemoteException 666 { 667 RealType domainType = msd.getDomainType(); 668 RealType rangeType = msd.getRangeType(); 669 670 LocalDisplay display = msd.getDisplay(); 671 672 rubberBand = new DataReferenceImpl(hashCode() + RBB); 673 rubberBand.setData(new RealTuple(new RealTupleType(domainType, 674 rangeType), new double[] { Double.NaN, Double.NaN })); 675 676 display.addReferences(new RubberBandBoxRendererJ3D(domainType, 677 rangeType, 1, 1), new DataReference[] { rubberBand }, null); 678 679 xmap = x; 680 ymap = y; 681 682 this.addReference(rubberBand); 683 } 684 685 public void doAction() throws VisADException, RemoteException { 686 if (!init) { 687 init = true; 688 return; 689 } 690 691 Gridded2DSet set = (Gridded2DSet)rubberBand.getData(); 692 693 float[] low = set.getLow(); 694 float[] high = set.getHi(); 695 696 xmap.setRange(low[0], high[0]); 697 ymap.setRange(low[1], high[1]); 698 } 699 } 700 701 public static class DragLine extends CellImpl { 702 private final String selectorId = hashCode() + "_selector"; 703 private final String lineId = hashCode() + "_line"; 704 private final String controlId; 705 706 private ConstantMap[] mappings = new ConstantMap[5]; 707 708 private DataReference line; 709 710 private DataReference selector; 711 712 private MultiSpectralDisplay multiSpectralDisplay; 713 714 private HydraControl hydraControl; 715 716 private RealType domainType; 717 private RealType rangeType; 718 719 private RealTupleType tupleType; 720 721 private LocalDisplay display; 722 723 private float[] YRANGE; 724 725 private float lastSelectedValue; 726 727 public DragLine(final MultiSpectralDisplay msd, final String controlId, final Color color) throws Exception { 728 this(msd, controlId, makeColorMap(color)); 729 } 730 731 public DragLine(final MultiSpectralDisplay msd, final String controlId, final Color color, float[] YRANGE) throws Exception { 732 this(msd, controlId, makeColorMap(color), YRANGE); 733 } 734 735 public DragLine(final MultiSpectralDisplay msd, final String controlId, 736 final ConstantMap[] color) throws Exception 737 { 738 this(msd, controlId, color, new float[] {180f, 320f}); 739 } 740 741 public DragLine(final MultiSpectralDisplay msd, final String controlId, 742 final ConstantMap[] color, float[] YRANGE) throws Exception 743 { 744 if (msd == null) 745 throw new NullPointerException("must provide a non-null MultiSpectralDisplay"); 746 if (controlId == null) 747 throw new NullPointerException("must provide a non-null control ID"); 748 if (color == null) 749 throw new NullPointerException("must provide a non-null color"); 750 751 this.controlId = controlId; 752 this.multiSpectralDisplay = msd; 753 this.YRANGE = YRANGE; 754 lastSelectedValue = multiSpectralDisplay.getWaveNumber(); 755 756 for (int i = 0; i < color.length; i++) { 757 mappings[i] = (ConstantMap)color[i].clone(); 758 } 759 mappings[4] = new ConstantMap(-0.5, Display.YAxis); 760 761 Gridded1DSet domain = multiSpectralDisplay.getDomainSet(); 762 763 domainType = multiSpectralDisplay.getDomainType(); 764 rangeType = multiSpectralDisplay.getRangeType(); 765 tupleType = new RealTupleType(domainType, rangeType); 766 767 selector = new DataReferenceImpl(selectorId); 768 line = new DataReferenceImpl(lineId); 769 770 display = multiSpectralDisplay.getDisplay(); 771 772 display.addReferences(new GrabLineRendererJ3D(domain), new DataReference[] { selector }, new ConstantMap[][] { mappings }); 773 display.addReference(line, cloneMappedColor(color)); 774 775 addReference(selector); 776 } 777 778 private static ConstantMap[] cloneMappedColor(final ConstantMap[] color) throws Exception { 779 assert color != null && color.length >= 3 : color; 780 return new ConstantMap[] { 781 (ConstantMap)color[0].clone(), 782 (ConstantMap)color[1].clone(), 783 (ConstantMap)color[2].clone(), 784 }; 785 } 786 787 public void annihilate() { 788 try { 789 display.removeReference(selector); 790 display.removeReference(line); 791 } catch (Exception e) { 792 LogUtil.logException("DragLine.annihilate", e); 793 } 794 } 795 796 public String getControlId() { 797 return controlId; 798 } 799 800 /** 801 * Handles drag and drop updates. 802 */ 803 public void doAction() throws VisADException, RemoteException { 804 setSelectedValue(getSelectedValue()); 805 } 806 807 public float getSelectedValue() { 808 float val = (float)display.getDisplayRenderer().getDirectAxisValue(domainType); 809 if (Float.isNaN(val)) 810 val = lastSelectedValue; 811 return val; 812 } 813 814 public void setSelectedValue(final float val) throws VisADException, 815 RemoteException 816 { 817 // don't do work for stupid values 818 if ((Float.isNaN(val)) 819 || (selector.getThing() != null && val == lastSelectedValue)) 820 return; 821 822 line.setData(new Gridded2DSet(tupleType, 823 new float[][] { { val, val }, { YRANGE[0], YRANGE[1] } }, 2)); 824 825 selector.setData(new Real(domainType, val)); 826 lastSelectedValue = val; 827 828 if (hydraControl instanceof MultiSpectralControl) { 829 ((MultiSpectralControl) hydraControl).setWavelengthLabel 830 ( 831 MultiSpectralControl.WAVENUMLABEL + val 832 ); 833 } 834 multiSpectralDisplay.updateControlSelector(controlId, val); 835 } 836 837 /** 838 * Set the display control so we can call back and update 839 * wavelength readout in real time. 840 * 841 * @param hydraControl the display control to set 842 */ 843 844 public void setHydraControl(HydraControl hydraControl) { 845 this.hydraControl = hydraControl; 846 } 847 } 848}