001/*
002 * $Id: HydraRGBDisplayable.java,v 1.12 2011/03/24 16:06:33 davep Exp $
003 *
004 * This file is part of McIDAS-V
005 *
006 * Copyright 2007-2011
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
031package edu.wisc.ssec.mcidasv.data.hydra;
032
033import java.rmi.RemoteException;
034
035import java.util.Iterator;
036
037import java.awt.Color;
038
039import ucar.unidata.beans.*;
040
041import visad.*;
042
043import ucar.visad.display.DisplayMaster;
044import ucar.visad.display.Displayable;
045import ucar.visad.display.DisplayableData;
046import ucar.visad.display.ScalarMapSet;
047import ucar.unidata.util.Range;
048                                                                                                                                      
049import visad.RealType;
050import visad.ScalarMap;
051import visad.BadMappingException;
052import visad.LocalDisplay;
053import visad.DataReference;
054import visad.VisADException;
055import visad.UnimplementedException;
056import visad.bom.ImageRendererJ3D;
057import visad.java3d.DefaultRendererJ3D;
058import java.rmi.RemoteException;
059                                                                                                                                      
060import java.util.ArrayList;
061import java.util.Hashtable;
062import java.util.List;
063import java.util.Iterator;
064
065import edu.wisc.ssec.mcidasv.control.HydraControl;
066import edu.wisc.ssec.mcidasv.control.MultiSpectralControl;
067
068
069public class HydraRGBDisplayable extends DisplayableData {
070
071    /**
072     * The name of the "color palette" property.
073     */
074    public static final String COLOR_PALETTE = "colorPalette";
075
076    /**
077     * The name of the "RGB real-type" property.
078     */
079    public static final String RGB_REAL_TYPE = "rgbRealType";
080
081    /**
082     * The polygon fill style
083     */
084    public static final int POLYGON_FILL = 0;
085
086    /**
087     * The polygon line style
088     */
089    public static final int POLYGON_LINE = 1;
090
091    /**
092     * The polygon point style
093     */
094    public static final int POLYGON_POINT = 2;
095
096    /**
097     * Color Palette
098     */
099    private float[][] colorPalette = null;
100
101    /** color ScalarMap */
102    private volatile ScalarMap colorMap;
103
104    /** field index to Animation ScalarMap */
105    private volatile ScalarMap animMap;
106
107    /** control for ScalarMap */
108    private volatile BaseColorControl colorControl;
109
110    /** RealType for the ScalarMap */
111    private volatile RealType rgbRealType;
112
113    /** RealType for the SelectRange ScalarMap */
114    private ScalarMap selectMap = null;
115
116    /** RealType for the Animation ScalarMap */
117    private RealType indexRealType;
118
119    /** Control for select range */
120    private RangeControl selectControl;
121
122    /** RealType for the SelectRange ScalarMap */
123    private RealType selectRealType = null;
124
125    /** flag for whether alpha is used or not */
126    private boolean alphaflag;
127
128    /** local point size */
129    private float myPointSize;
130
131    /** low range for colors */
132    //private double lowRange = 315;           // low range for scalarmap
133    private double lowRange = Double.NaN;           // low range for scalarmap
134
135    /** high range for colors */
136    //private double highRange = 230;          // high range for scalarmap
137    private double highRange = Double.NaN;          // high range for scalarmap
138
139    /** default polygonMode */
140    private int polygonMode = POLYGON_FILL;
141
142    /** default curvedSize */
143    private int curvedSize = 10;
144
145    /** low range for select */
146    private double lowSelectedRange = Double.NaN;   // low range for scalarmap
147
148    /** high range for select */
149    private double highSelectedRange = Double.NaN;  // high range for scalarmap
150
151    /** low range for select map */
152    private double minSelect = Double.NaN;          // low range for scalarmap
153
154    /** high range for select map */
155    private double maxSelect = Double.NaN;          // high range for scalarmap
156
157    private HydraControl multiSpecCntrl;
158
159    private boolean useDefaultRenderer = false;
160
161    /**
162     * Constructs from a name for the Displayable and the type of the
163     * RGB parameter.
164     *
165     * @param name              The name for the displayable.
166     * @param rgbRealType       The type of the RGB parameter.  May be
167     *                          <code>null</code>.
168     * @param alphaflag         boolean - will use Display.RBGA if true
169     *                            otherwise only Display.RGB
170     * @throws VisADException   VisAD failure.
171     * @throws RemoteException  Java RMI failure.
172     */
173    public HydraRGBDisplayable(String name, RealType rgbRealType, RealType indexRealType, boolean alphaflag, 
174                 HydraControl multiSpecCntrl)
175            throws VisADException, RemoteException {
176        this(name, rgbRealType, indexRealType, null, alphaflag, multiSpecCntrl);
177    }
178
179    /**
180     * Constructs from a name for the Displayable and the type of the
181     * RGB parameter.
182     *
183     * @param name              The name for the displayable.
184     * @param rgbRealType       The type of the RGB parameter.  May be
185     *                          <code>null</code>.
186     * @param colorPalette      The initial colorPalette to use. May be
187     *                          <code>null</code> (Vis5D palette used
188     *                          as default).
189     * @param alphaflag         boolean - use Display.RBGA if true
190     * @throws VisADException   VisAD failure.
191     * @throws RemoteException  Java RMI failure.
192     */
193    public HydraRGBDisplayable(String name, RealType rgbRealType, RealType indexRealType, float[][] colorPalette, boolean alphaflag,
194                   HydraControl multiSpecCntrl)
195            throws VisADException, RemoteException {
196
197        super(name);
198        
199        this.rgbRealType  = rgbRealType;
200        this.selectRealType = rgbRealType;
201        this.indexRealType  = indexRealType;
202        this.colorPalette = colorPalette;
203        this.alphaflag    = alphaflag;
204        this.multiSpecCntrl = multiSpecCntrl;
205
206        if (rgbRealType != null) {
207            setColorMaps();
208            if (useDisplayUnitForColor()) {
209                setDisplayUnit(rgbRealType.getDefaultUnit());
210            } else {
211                setColorUnit(rgbRealType.getDefaultUnit());
212            }
213        }
214
215        if (indexRealType != null) {
216          //-setAnimationMap();
217          setSelectMap();
218        }
219
220        if (selectRealType != null) {
221          //setSelectMaps();
222        }
223    }
224
225    /**
226     * Does this object use the displayUnit (or the colorUnit) for its
227     * display unit. The default is true.  This allows derived classes
228     * to have this class use the colorUnit.
229     * @return  true if the display unit is the same as the color unit
230     */
231    protected boolean useDisplayUnitForColor() {
232        return true;
233    }
234
235
236    /**
237     * Constructs from another instance.  The following attributes are set from
238     * the other instance: color palette, the color RealType.
239     * @param that              The other instance.
240     * @throws VisADException   VisAD failure.
241     * @throws RemoteException  Java RMI failure.
242     */
243    protected HydraRGBDisplayable(HydraRGBDisplayable that)
244            throws VisADException, RemoteException {
245
246        super(that);
247        colorPalette = that.colorPalette;
248        rgbRealType  = that.rgbRealType;  // immutable object
249        alphaflag    = that.alphaflag;
250
251        if (rgbRealType != null) {
252            setColorMaps();
253        }
254    }
255
256    /**
257     * Sets the RealType of the RGB parameter.
258     * @param realType          The RealType of the RGB parameter.  May
259     *                          not be <code>null</code>.
260     * @throws VisADException   VisAD failure.
261     * @throws RemoteException  Java RMI failure.
262     */
263    public void setRGBRealType(RealType realType)
264            throws RemoteException, VisADException {
265
266        if ( !realType.equals(rgbRealType)) {
267            RealType oldValue = rgbRealType;
268            rgbRealType = realType;
269            setColorMaps();
270            if (useDisplayUnitForColor()) {
271                if ( !isUnitCompatible(rgbRealType, getDisplayUnit())) {
272                    setDisplayUnit(null);
273                }
274            } else {
275                if ( !isUnitCompatible(rgbRealType, getColorUnit())) {
276                    setColorUnit(null);
277                }
278            }
279            firePropertyChange(RGB_REAL_TYPE, oldValue, rgbRealType);
280        }
281    }
282
283    public ScalarMap getColorMap() {
284      return colorMap;
285    }
286
287    public ScalarMap getAnimationMap() {
288      return animMap;
289    }
290
291
292    /**
293     * Returns the RealType of the RGB parameter.
294     * @return                  The RealType of the color parameter.  May
295     *                          be <code>null</code>.
296     */
297    public RealType getRGBRealType() {
298        return rgbRealType;
299    }
300
301    /**
302     * Returns the RealType of the SelectRange parameter.
303     * @return                  The RealType of the select range parameter.  May
304     *                          be <code>null</code>.
305     */
306    public RealType getSelectRealType() {
307        return selectRealType;
308    }
309
310    protected DataRenderer getDataRenderer() throws VisADException {
311      if (useDefaultRenderer) {
312        return new DefaultRendererJ3D();
313      }
314      else {
315        return new ImageRendererJ3D();
316      }
317    }
318
319    public void setDefaultRenderer() {
320      useDefaultRenderer = true;
321    }
322
323    public void setImageRenderer() {
324      useDefaultRenderer = false;
325    }
326
327    /**
328     * Sets the set of ScalarMap-s of this instance.  The ScalarMap-s of
329     * this instance will be added to the set before the SCALAR_MAP_SET
330     * property is set.  This method fires a PropertyChangeEvent for
331     * SCALAR_MAP_SET with <code>null</code> for the old value and the new
332     * set of ScalarMap-s for the new Value.  Intermediate subclasses that
333     * have their own ScalarMap-s should override this method and invoke
334     * <code>super.setScalarMaps(ScalarMapSet)</code>.
335     * @param maps              The set of ScalarMap-s to be added.
336     * @throws BadMappingException      The RealType of the color parameter
337     *                          has not been set or its ScalarMap is alread in
338     *                          the set.
339     */
340    protected void setScalarMaps(ScalarMapSet maps)
341            throws BadMappingException {
342
343        if (colorMap == null) {
344            throw new BadMappingException(getClass().getName()
345                                          + ".setScalarMaps(ScalarMapSet): "
346                                          + "Color not yet set");
347        }
348
349        maps.add(colorMap);
350
351        if (selectMap != null) {
352
353            maps.add(selectMap);
354        }
355
356        super.setScalarMapSet(maps);
357    }
358
359    /**
360     * This method sets the color palette
361     * according to the color table in argument;
362     * pair this method with setRange(lo,high) to get
363     * a fixed association of color table and range of values.
364     *
365     * @param colorPalette     the color table or color-alpha table desired
366     * @throws VisADException  if a core VisAD failure occurs.
367     * @throws RemoteException if a Java RMI failure occurs.
368     */
369    public void setColorPalette(float[][] colorPalette)
370            throws RemoteException, VisADException {
371        if (colorControl != null) {
372            colorControl.setTable(colorPalette);
373        }
374
375        this.colorPalette = colorPalette;
376    }
377
378    /**
379     * Return the current color palette in this Displayable
380     *
381     * @return a color table float[3][len] or color-alpha table float[4][len]
382     */
383    public float[][] getColorPalette() {
384        return colorPalette;
385    }
386
387    /**
388     * Make a color palette representing this color and set it as the
389     * color pallete.
390     *
391     * @param  color  color to use
392     * @throws VisADException     VisAD failure.
393     * @throws RemoteException    Java RMI failure.
394     */
395    public void setColor(Color color) throws RemoteException, VisADException {
396        int       len   = 5;
397        float[][] table = new float[(alphaflag == true)
398                                    ? 4
399                                    : 3][len];
400        for (int m = 0; m < len; m++) {
401            table[0][m] = color.getRed() / 255.f;    // Red amount  
402            table[1][m] = color.getGreen() / 255.f;  // Green
403            table[2][m] = color.getBlue() / 255.f;   // Blue  
404        }
405        setColorPalette(table);
406    }
407
408    /**
409     * This method sets the color palette to shades of grey.
410     *
411     * @throws VisADException  if a core VisAD failure occurs.
412     * @throws RemoteException if a Java RMI failure occurs.
413     */
414    public final void setGreyPalette()
415            throws RemoteException, VisADException {
416
417        if (colorControl != null) {
418            colorControl.initGreyWedge();
419            setColorPalette(colorControl.getTable());
420        }
421    }
422
423    /**
424     * This method with no argument sets the default Vis5D color spectrum.
425     *
426     * @throws VisADException  if a core VisAD failure occurs.
427     * @throws RemoteException if a Java RMI failure occurs.
428     */
429    public final void setVisADPalette()
430            throws RemoteException, VisADException {
431
432        if (colorControl != null) {
433            colorControl.initVis5D();
434            setColorPalette(colorControl.getTable());
435        }
436    }
437
438    /**
439     * Set the upper and lower limit of the range values associated
440     * with a color table.
441     *
442     * @param low    the minimun value
443     * @param hi     the maximum value
444     * @deprecated   use setRangeForColor
445     *
446     * @throws RemoteException  Java RMI error
447     * @throws VisADException   problem creating VisAD object
448     */
449    public void setRange(double low, double hi)
450            throws VisADException, RemoteException {
451
452        setRangeForColor(low, hi);
453    }
454
455    /**
456     * Set the upper and lower limit of the range values associated
457     * with a color table.
458     *
459     * Matches method name in Contour2DDisplayable
460     *
461     * @param low               The minimum value of the parameter matched to
462     *                          the low end of the color table.
463     * @param hi                The maximum value of the parameter matched to
464     *                          the high end of the color table.
465     *
466     * @exception VisADException   VisAD failure.
467     * @exception RemoteException  Java RMI failure.
468     */
469    public void setRangeForColor(double low, double hi)
470            throws VisADException, RemoteException {
471        lowRange  = low;
472        highRange = hi;
473        if ((colorMap != null) && hasRange()) {
474            colorMap.setRange(low, hi);
475        }
476    }
477
478    /**
479     * Get the color range
480     *
481     * @return an array of the low and high values for the range
482     * @deprecated  use #getRangeForColor()
483     */
484    public double[] getRangeforColor() {
485        return getRangeForColor();
486    }
487
488    /**
489     * Get the color range
490     *
491     * @return an array of the low and high values for the range
492     */
493    public double[] getRangeForColor() {
494        return new double[]{ lowRange, highRange };
495    }
496
497    /**
498     * Apply the correct unit (either the displayUnit or the colorUnit)
499     * to the scalar map
500     *
501     * @param colorMap   ScalarMap to apply to
502     * @param rgbRealType  RealType for default Unit
503     *
504     * @throws RemoteException  Java RMI error
505     * @throws VisADException   problem creating VisAD object
506     */
507    private void applyUnit(ScalarMap colorMap, RealType rgbRealType)
508            throws VisADException, RemoteException {
509        if (useDisplayUnitForColor()) {
510            applyDisplayUnit(colorMap, rgbRealType);
511        } else {
512            applyColorUnit(colorMap, rgbRealType);
513        }
514    }
515
516
517    /**
518     * Set the units for the displayed range
519     *
520     * @param unit Unit for display
521     *
522     * @throws RemoteException  Java RMI error
523     * @throws VisADException   problem creating VisAD object
524     */
525    public void setDisplayUnit(Unit unit)
526            throws VisADException, RemoteException {
527        if (useDisplayUnitForColor()) {
528            //Make sure this unit is ok
529            checkUnit(rgbRealType, unit);
530        }
531        super.setDisplayUnit(unit);
532        if (useDisplayUnitForColor()) {
533            applyUnit(colorMap, rgbRealType);
534        }
535    }
536
537
538    /**
539     * Set the units for the displayed range
540     *
541     * @param unit Unit for display
542     *
543     * @throws RemoteException  Java RMI error
544     * @throws VisADException   problem creating VisAD object
545     */
546    public void setColorUnit(Unit unit)
547            throws VisADException, RemoteException {
548        if ( !useDisplayUnitForColor()) {
549            //Make sure this unit is ok
550            checkUnit(rgbRealType, unit);
551        }
552        super.setColorUnit(unit);
553        if ( !useDisplayUnitForColor()) {
554            applyUnit(colorMap, rgbRealType);
555        }
556    }
557
558
559    /**
560     * Returns whether this Displayable has a valid range (i.e., lowRange and
561     * highRange are both not NaN's
562     *
563     * @return true if range has been set
564     */
565    public boolean hasRange() {
566        return ( !Double.isNaN(lowRange) && !Double.isNaN(highRange));
567    }
568
569
570    /**
571     * Sets the size of points in this Displayable.
572     *
573     * @param   pointSize     Size of points (2 = normal)
574     *
575     * @throws VisADException     VisAD failure.
576     * @throws RemoteException    Java RMI failure.
577     */
578    public void setPointSize(float pointSize)
579            throws VisADException, RemoteException {
580
581        float oldValue;
582
583        synchronized (this) {
584            oldValue = myPointSize;
585
586            addConstantMap(new ConstantMap(pointSize, Display.PointSize));
587
588            myPointSize = pointSize;
589        }
590
591    }
592
593    /**
594     * Gets the point size associated with this LineDrawing
595     *
596     * @return  point size
597     */
598    public float getPointSize() {
599        return myPointSize;
600    }
601
602    /**
603     * Set the type of polygon display that should be used
604     *
605     * @param polygonMode  polygon mode
606     *
607     * @throws RemoteException  Java RMI error
608     * @throws VisADException   problem creating VisAD object
609     */
610    public void setPolygonMode(int polygonMode)
611            throws VisADException, RemoteException {
612        this.polygonMode = polygonMode;
613        addConstantMap(new ConstantMap(convertToVisADPolygonMode(polygonMode),
614                                       Display.PolygonMode));
615    }
616
617    /**
618     * Converts an RGBDisplayable Polygon mode to the appropriate
619     * (or default) VisAD mode
620     *
621     * @param myMode  polygon mode
622     * @return  Java3D mode
623     */
624    private int convertToVisADPolygonMode(int myMode) {
625        if (visad.util.Util.canDoJava3D()) {
626            switch (myMode) {
627
628              case POLYGON_FILL :
629                  return visad.java3d.DisplayImplJ3D.POLYGON_FILL;
630
631              case POLYGON_LINE :
632                  return visad.java3d.DisplayImplJ3D.POLYGON_LINE;
633
634              case POLYGON_POINT :
635                  return visad.java3d.DisplayImplJ3D.POLYGON_POINT;
636
637              default :
638                  return visad.java3d.DisplayImplJ3D.POLYGON_FILL;
639            }
640        } else {
641            return 0;
642        }
643    }
644
645    /**
646     * Return the type of polygon mode being used
647     *
648     * @return polygon mode
649     */
650    public int getPolygonMode() {
651        return polygonMode;
652    }
653
654    /**
655     * Set the curved size for textured displays
656     *
657     * @param curvedSize size to use (> 0)
658     *
659     * @throws RemoteException  Java RMI error
660     * @throws VisADException   problem creating VisAD object
661     */
662    public void setCurvedSize(int curvedSize)
663            throws VisADException, RemoteException {
664        this.curvedSize = curvedSize;
665        addConstantMap(makeCurvedSizeMap(curvedSize));
666    }
667
668    /**
669     * Create the ConstantMap for the texture curve size
670     *
671     * @param curvedSize   size for texture curve
672     * @return  ConstantMap
673     *
674     * @throws RemoteException  Java RMI error
675     * @throws VisADException   problem creating VisAD object
676     */
677    protected ConstantMap makeCurvedSizeMap(int curvedSize)
678            throws VisADException, RemoteException {
679        return new ConstantMap(curvedSize, Display.CurvedSize);
680    }
681
682    /**
683     * Return the size of a curved texture
684     * @return curved size
685     */
686    public int getCurvedSize() {
687        return curvedSize;
688    }
689
690    /**
691     * creates the ScalarMap for color and ColorControl for this Displayable.
692     *
693     * @throws VisADException   VisAD failure.
694     * @throws RemoteException  Java RMI failure.
695     */
696    private void setColorMaps() throws RemoteException, VisADException {
697
698        // ScalarMap is either mapping to Display.RGB (color only)
699        // or to Display.RGBA color plus transparency.
700        if ( !alphaflag) {
701            colorMap = new ScalarMap(rgbRealType, Display.RGB);
702        } else {
703            colorMap = new ScalarMap(rgbRealType, Display.RGBA);
704        }
705
706        applyUnit(colorMap, rgbRealType);
707
708        if (hasRange()) {
709           colorMap.setRange(lowRange, highRange);
710        }
711
712        colorMap.addScalarMapListener(new ScalarMapListener() {
713
714            public void controlChanged(ScalarMapControlEvent event)
715                    throws RemoteException, VisADException {
716
717                int id = event.getId();
718
719                if ((id == event.CONTROL_ADDED)
720                        || (id == event.CONTROL_REPLACED)) {
721                    colorControl = (BaseColorControl) colorMap.getControl();
722
723                    if (colorControl != null) {
724                        if (colorPalette != null) {
725                            colorControl.setTable(colorPalette);
726                        } else {
727                            colorPalette = colorControl.getTable();
728                        }
729                    }
730                }
731            }
732
733            public void mapChanged(ScalarMapEvent event)
734                    throws RemoteException, VisADException {
735                if ((event.getId() == event.AUTO_SCALE) && hasRange()) {
736                  double[] rng = colorMap.getRange();
737                  if (multiSpecCntrl != null) {
738                    multiSpecCntrl.updateRange(new Range(rng));
739                  }
740                }
741            }
742        });
743        ScalarMapSet maps = getScalarMapSet();  //new ScalarMapSet();
744        maps.add(colorMap);
745        setScalarMapSet(maps);
746    }
747
748    private void setAnimationMap() throws RemoteException, VisADException {
749      animMap = new ScalarMap(indexRealType, Display.Animation);
750      ScalarMapSet maps = getScalarMapSet();
751      maps.add(animMap);
752      setScalarMapSet(maps);
753    }
754
755    private void setSelectMap() throws RemoteException, VisADException {
756      animMap = new ScalarMap(indexRealType, Display.SelectValue);
757      ScalarMapSet maps = getScalarMapSet();
758      maps.add(animMap);
759      setScalarMapSet(maps);
760    }
761
762    /**
763     * Sets the RealType of the select parameter.
764     * @param realType          The RealType of the RGB parameter.  May
765     *                          not be <code>null</code>.
766     * @throws VisADException   VisAD failure.
767     * @throws RemoteException  Java RMI failure.
768     */
769    protected void setSelectRealType(RealType realType)
770            throws RemoteException, VisADException {
771
772        if ( !realType.equals(selectRealType)) {
773            RealType oldValue = selectRealType;
774            selectRealType = realType;
775            setSelectMaps();
776            if (useDisplayUnitForColor()) {
777                if ( !isUnitCompatible(selectRealType, getDisplayUnit())) {
778                    setDisplayUnit(null);
779                }
780            } else {
781                if ( !isUnitCompatible(selectRealType, getColorUnit())) {
782                    setColorUnit(null);
783                }
784            }
785        }
786    }
787
788    /**
789     * Returns whether this Displayable has a valid range
790     * (i.e., lowSelectedRange and highSelectedRange are both not NaN's
791     *
792     * @return true if range has been set
793     */
794    public boolean hasSelectedRange() {
795        return ( !Double.isNaN(lowSelectedRange)
796                 && !Double.isNaN(highSelectedRange));
797    }
798
799    /**
800     * Set selected range with the range for select
801     *
802     * @param low  low select value
803     * @param hi   hi select value
804     *
805     * @throws RemoteException  Java RMI error
806     * @throws VisADException   problem creating VisAD object
807     */
808    public void setSelectedRange(double low, double hi)
809            throws VisADException, RemoteException {
810
811        lowSelectedRange  = low;
812        highSelectedRange = hi;
813        if ((selectControl != null) && hasSelectedRange()) {
814            selectControl.setRange(new double[]{ low, hi });
815        }
816
817    }
818
819    /**
820     * Set the upper and lower limit of the range values associated
821     * with a color table.
822     *
823     * @param low    the minimun value
824     * @param hi     the maximum value
825     *
826     * @throws RemoteException  Java RMI error
827     * @throws VisADException   problem creating VisAD object
828     */
829    public void setRangeForSelect(double low, double hi)
830            throws VisADException, RemoteException {
831
832        minSelect = low;
833        maxSelect = hi;
834        if ((selectMap != null) && hasSelectMinMax()) {
835            selectMap.setRange(low, hi);
836        }
837    }
838
839    /**
840     * Check to see if the range has been set for the select
841     *
842     * @return true if it has
843     */
844    private boolean hasSelectMinMax() {
845        return ( !Double.isNaN(minSelect) && !Double.isNaN(maxSelect));
846    }
847
848    /**
849     * creates the ScalarMap for SelectRange and control for this Displayable.
850     *
851     * @throws VisADException   VisAD failure.
852     * @throws RemoteException  Java RMI failure.
853     */
854    private void setSelectMaps() throws RemoteException, VisADException {
855
856        selectMap = new ScalarMap(selectRealType, Display.SelectRange);
857
858        if (selectRealType.equals(rgbRealType)) {
859            applyUnit(selectMap, selectRealType);
860        }
861
862        if (hasSelectMinMax()) {
863            selectMap.setRange(minSelect, maxSelect);
864        }
865
866        selectMap.addScalarMapListener(new ScalarMapListener() {
867
868            public void controlChanged(ScalarMapControlEvent event)
869                    throws RemoteException, VisADException {
870
871                int id = event.getId();
872
873                if ((id == event.CONTROL_ADDED)
874                        || (id == event.CONTROL_REPLACED)) {
875                    selectControl = (RangeControl) selectMap.getControl();
876                    if (hasSelectedRange()) {
877                        selectControl.setRange(new double[]{ lowSelectedRange,
878                                                             highSelectedRange });
879                    }
880                }
881            }
882
883            public void mapChanged(ScalarMapEvent event)
884                    throws RemoteException, VisADException {
885                if ((event.getId() == event.AUTO_SCALE)
886                        && hasSelectMinMax()) {
887                    selectMap.setRange(minSelect, maxSelect);
888                }
889            }
890        });
891        ScalarMapSet maps = getScalarMapSet();  //new ScalarMapSet();
892        maps.add(selectMap);
893        setScalarMapSet(maps);
894    }
895
896}