001/*
002 * This file is part of McIDAS-V
003 *
004 * Copyright 2007-2024
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 https://www.gnu.org/licenses/.
027 */
028
029package edu.wisc.ssec.mcidasv.data.hydra;
030
031import java.rmi.RemoteException;
032
033import ucar.unidata.data.grid.GridUtil;
034import ucar.visad.display.DisplayableData;
035import ucar.visad.display.GridDisplayable;
036import ucar.visad.display.ScalarMapSet;
037
038import visad.BadMappingException;
039import visad.BaseColorControl;
040import visad.ConstantMap;
041import visad.DataRenderer;
042import visad.Display;
043import visad.DisplayException;
044import visad.DisplayRealType;
045import visad.FieldImpl;
046import visad.GraphicsModeControl;
047import visad.LocalDisplay;
048import visad.RealTupleType;
049import visad.RealType;
050import visad.ScalarMap;
051import visad.ScalarMapControlEvent;
052import visad.ScalarMapEvent;
053import visad.ScalarMapListener;
054import visad.Set;
055import visad.TupleType;
056import visad.VisADException;
057import visad.bom.ImageRendererJ3D;
058
059/**
060 * Provides support for a Displayable that needs a map to
061 * (Display.Red,Display.Green,Display.Blue)
062 *
063 * @author IDV development team
064 * @version $Revision$
065 */
066public class ImageRGBDisplayable extends DisplayableData implements GridDisplayable {
067
068    /** color ScalarMaps */
069    private volatile ScalarMap[] colorMaps = { null, null, null };
070
071    /** color MathType */
072    private volatile RealTupleType colorTupleType;
073
074    /** color palette */
075    private float[][] colorPalette;
076
077    /** What do we map with */
078    private DisplayRealType mapType = Display.RGB;
079
080    /** flag for whether we use Alpha channel or not */
081    private boolean doAlpha = false;
082
083    /**
084     * Constructs from a name for the Displayable and the type of the
085     * RGB parameter.
086     *
087     * @param name              The name for the displayable.
088     * @throws VisADException   VisAD failure.
089     * @throws RemoteException  Java RMI failure.
090     */
091    
092    public ImageRGBDisplayable(String name)
093            throws VisADException, RemoteException {
094        this(name, false);
095    }
096
097    /**
098     * Constructs from a name for the Displayable and the type of the
099     * RGB parameter.
100     *
101     * @param name              The name for the displayable.
102     * @param doAlpha           true to map to RGBA
103     * @throws VisADException   VisAD failure.
104     * @throws RemoteException  Java RMI failure.
105     */
106    
107    public ImageRGBDisplayable(String name, boolean doAlpha)
108            throws VisADException, RemoteException {
109        this(name, BaseColorControl.initTableGreyWedge(new float[(doAlpha)
110                ? 4
111                : 3][255]), doAlpha);
112    }
113
114    /**
115     * Constructs from a name for the Displayable and the type of the
116     * RGB parameter.
117     *
118     * @param name              The name for the displayable.
119     * @param colorPalette      The color palette
120     * @param doAlpha           true to map to RGBA
121     * @throws VisADException   VisAD failure.
122     * @throws RemoteException  Java RMI failure.
123     */
124    
125    public ImageRGBDisplayable(String name, float[][] colorPalette,
126                               boolean doAlpha)
127            throws VisADException, RemoteException {
128        this(name, colorPalette, doAlpha, null);
129    }
130
131    /**
132     * Constructs from another instance.  The following attributes are set from
133     * the other instance: color palette, the color RealType.
134     * @param that              The other instance.
135     * @throws VisADException   VisAD failure.
136     * @throws RemoteException  Java RMI failure.
137     */
138    
139    protected ImageRGBDisplayable(ImageRGBDisplayable that)
140            throws VisADException, RemoteException {
141
142        super(that);
143        this.doAlpha   = that.doAlpha;
144        colorTupleType = that.colorTupleType;
145        colorPalette   = Set.copyFloats(that.colorPalette);
146        if (colorTupleType != null) {
147            setColorMaps();
148        }
149    }
150
151    public ImageRGBDisplayable(String name, float[][] colorPalette, boolean doAlpha, FieldImpl field)
152            throws VisADException, RemoteException {
153        super(name);
154        this.doAlpha = doAlpha;
155        if (doAlpha) {
156            mapType   = Display.RGBA;
157            colorMaps = new ScalarMap[] { null, null, null, null };
158        }
159
160        addConstantMaps(new ConstantMap[] {
161            new ConstantMap(GraphicsModeControl.SUM_COLOR_MODE,
162                            Display.ColorMode),
163            new ConstantMap(1.0, Display.MissingTransparent) });
164
165
166        if (field != null) {
167          TupleType     tt       = GridUtil.getParamType(field);
168          RealTupleType ffldType = new RealTupleType(tt.getRealComponents());
169
170          if ((getColorTupleType() == null)
171                 || !ffldType.equals(getColorTupleType())) {
172              setColorTupleType(ffldType);
173          }
174        }
175    }
176
177    /**
178     * Set the data into the Displayable; set RGB Type
179     *
180     *
181     * @param field an image or sequence of images
182     * @exception VisADException  from construction of VisAd objects
183     * @exception RemoteException from construction of VisAD objects
184     */
185    
186    public void loadData(FieldImpl field)
187            throws VisADException, RemoteException {
188        setData(field);
189    }
190
191
192    /**
193     * Get the RealTupleType of the RGB parameter.
194     *  @return The RealTupleType of the RGB parameters.
195     *         May be {@code null}.
196     */
197    public RealTupleType getColorTupleType() {
198        return colorTupleType;
199    }
200
201    /**
202     * Sets the RealTupleType of the RGB parameter.
203     * @param realTupleType     The RealTupleType of the RGB parameters.  May
204     *                          not be {@code null}.
205     * @throws VisADException   VisAD failure.
206     * @throws RemoteException  Java RMI failure.
207     */
208    protected void setColorTupleType(RealTupleType realTupleType)
209            throws RemoteException, VisADException {
210
211        if ( !realTupleType.equals(colorTupleType)) {
212            colorTupleType = realTupleType;
213            setColorMaps();
214        }
215    }
216
217
218    /**
219     * Returns the RealTupleType of the RGB parameter.
220     * @return                  The RealTupleType of the color parameter.  May
221     *                          be {@code null}.
222     * @deprecated  use getColorTupleType()
223     */
224    public RealTupleType getRGBRealTupleType() {
225        return colorTupleType;
226    }
227
228
229    /**
230     * Sets the set of ScalarMap-s of this instance.  The ScalarMap-s of
231     * this instance will be added to the set before the SCALAR_MAP_SET
232     * property is set.  This method fires a PropertyChangeEvent for
233     * SCALAR_MAP_SET with {@code null} for the old value and the new
234     * set of ScalarMap-s for the new Value.  Intermediate subclasses that
235     * have their own ScalarMap-s should override this method and invoke
236     * {@code super.setScalarMaps(ScalarMapSet)}.
237     * @param maps              The set of ScalarMap-s to be added.
238     * @throws BadMappingException      The RealType of the color parameter
239     *                          has not been set or its ScalarMap is already in
240     *                          the set.
241     */
242    protected void setScalarMaps(ScalarMapSet maps)
243            throws BadMappingException {
244
245        if (colorMaps[0] == null) {
246            throw new BadMappingException(getClass().getName()
247                                          + ".setScalarMaps(ScalarMapSet): "
248                                          + "Color not yet set");
249        }
250
251        for (int i = 0; i < colorMaps.length; i++) {
252            maps.add(colorMaps[i]);
253        }
254        super.setScalarMapSet(maps);
255    }
256
257    /**
258     * Set the alpha. Unused.
259     *
260     * @param alpha alpha
261     *
262     * @throws RemoteException On badness
263     * @throws VisADException On badness
264     */
265    public void setAlpha(float alpha) throws RemoteException, VisADException {
266        addConstantMaps(new ConstantMap[] {
267            new ConstantMap(alpha, Display.Alpha) });
268    }
269
270    /**
271     * creates the ScalarMaps for color  for this Displayable.
272     *
273     * @throws VisADException   VisAD failure.
274     * @throws RemoteException  Java RMI failure.
275     */
276    private void setColorMaps() throws RemoteException, VisADException {
277
278        ScalarMapSet set = new ScalarMapSet();
279        for (int i = 0; i < colorMaps.length; i++) {
280            colorMaps[i] =
281                new ScalarMap((RealType) colorTupleType.getComponent(i),
282                              mapType);
283            /* TODO: maybe allow user to set range.  If so, just copy
284               logic from RGBDisplayable */
285            //-TDR colorMaps[i].setRange(0, 255);
286            set.add(colorMaps[i]);
287            final int colorMapIndex = i;
288            colorMaps[i].addScalarMapListener(new ScalarMapListener() {
289                public void controlChanged(ScalarMapControlEvent event)
290                        throws RemoteException, VisADException {
291                    int id = event.getId();
292                    if ((id == ScalarMapEvent.CONTROL_ADDED)
293                            || (id == ScalarMapEvent.CONTROL_REPLACED)) {
294                        setColorsInControls(colorPalette, colorMapIndex);
295                    }
296                }
297
298                public void mapChanged(ScalarMapEvent event)
299                        throws RemoteException, VisADException { 
300                }
301            });
302        }
303
304        setScalarMapSet(set);
305        setColorsInControls(colorPalette);
306    }
307
308    /**
309     * Set the display.
310     *
311     * @param display  display to set this into
312     *
313     * @throws DisplayException Display type exception
314     * @throws RemoteException  Java RMI error
315     * @throws VisADException   problem creating VisAD object
316     */
317    public void setDisplay(LocalDisplay display)
318            throws DisplayException, VisADException, RemoteException {
319        super.setDisplay(display);
320        setColorsInControls(colorPalette);
321    }
322
323    /**
324     * This method sets the color palette
325     * according to the color table in argument;
326     * pair this method with setRange(lo,high) to get
327     * a fixed association of color table and range of values.
328     *
329     * @param colorPalette     the color table or color-alpha table desired
330     * @throws VisADException  if a core VisAD failure occurs.
331     * @throws RemoteException if a Java RMI failure occurs.
332     */
333    public void setColorPalette(float[][] colorPalette)
334            throws RemoteException, VisADException {
335
336        setColorsInControls(colorPalette);
337        this.colorPalette = colorPalette;
338    }
339
340    /**
341     * Return the current color palette in this Displayable
342     *
343     * @return a color table float[3][len] or color-alpha table float[4][len]
344     */
345    public float[][] getColorPalette() {
346        return colorPalette;
347    }
348
349
350    /**
351     * Set colors for the controls of all color maps.
352     *
353     * @param colorPalette The 3xN color palette array
354     *
355     * @throws RemoteException  Java RMI error
356     * @throws VisADException   problem creating VisAD object
357     */
358    private void setColorsInControls(float[][] colorPalette)
359            throws RemoteException, VisADException {
360
361        for (int i = 0; i < colorMaps.length; i++) {
362            setColorsInControls(colorPalette, i);
363        }
364    }
365
366
367
368
369    /**
370     * Set colors for the control defined by the given colorMapIndex (0, 1, or 2).
371     *
372     * @param colorPalette The 3xN color palette array
373     * @param colorMapIndex Which of the color maps are we setting the color of.
374     *
375     * @throws RemoteException  Java RMI error
376     * @throws VisADException   problem creating VisAD object
377     */
378    private void setColorsInControls(float[][] colorPalette,
379                                     int colorMapIndex)
380            throws RemoteException, VisADException {
381        if (colorPalette == null) {
382            return;
383        }
384
385
386        if (colorMaps[colorMapIndex] == null) {
387            return;
388        }
389
390        BaseColorControl bcc =
391            (BaseColorControl) colorMaps[colorMapIndex].getControl();
392
393        if (bcc != null) {
394            float[][] table =
395                new float[colorMaps.length][colorPalette[0].length];
396            table[colorMapIndex] = colorPalette[colorMapIndex];
397            bcc.setTable(table);
398        }
399    }
400
401    protected DataRenderer getDataRenderer() throws VisADException {
402     
403      ImageRendererJ3D myRenderer = new ImageRendererJ3D();
404      return myRenderer;
405
406    }
407
408
409    /**
410     * Set whether this GridDisplayable should have the data colored
411     * by another parameter.  This implementation is a no-op.
412     *
413     * @param yesno true if colored by another
414     */
415    public void setColoredByAnother(boolean yesno) {}
416
417}