001/*
002 * This file is part of McIDAS-V
003 *
004 * Copyright 2007-2025
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 */
028package edu.wisc.ssec.mcidasv.control;
029
030import edu.wisc.ssec.mcidasv.data.hydra.ImageRGBDisplayable;
031
032import net.miginfocom.swing.MigLayout;
033
034import org.slf4j.Logger;
035import org.slf4j.LoggerFactory;
036import ucar.unidata.data.DataChoice;
037import ucar.unidata.data.DataSelection;
038import ucar.unidata.idv.ViewManager;
039import ucar.unidata.idv.control.DisplayControlImpl;
040import ucar.unidata.util.ColorTable;
041import ucar.unidata.util.LogUtil;
042import ucar.visad.display.DisplayMaster;
043
044import visad.*;
045import visad.georef.MapProjection;
046
047import javax.swing.Box;
048import javax.swing.JButton;
049import javax.swing.JCheckBox;
050import javax.swing.JLabel;
051import javax.swing.JPanel;
052import javax.swing.JTextField;
053
054import java.awt.Container;
055import java.awt.FlowLayout;
056import java.rmi.RemoteException;
057import java.util.Hashtable;
058import java.util.Iterator;
059
060public class RGBCompositeControl extends DisplayControlImpl {
061
062    public final static String FORMULA_IN_PROGRESS_FLAG = "Formula_Active";
063    private static final Logger logger = LoggerFactory.getLogger(RGBCompositeControl.class);
064    /** Displayable for the data */
065    private ImageRGBDisplayable imageDisplay;
066
067    private DisplayMaster displayMaster;
068
069    private ScalarMap redMap = null;
070    private ScalarMap grnMap = null;
071    private ScalarMap bluMap = null;
072
073    float[][] redTable = null;
074    float[][] grnTable = null;
075    float[][] bluTable = null;
076
077    final private double[] redRange = { Double.NaN, Double.NaN };
078    final private double[] grnRange = { Double.NaN, Double.NaN };
079    final private double[] bluRange = { Double.NaN, Double.NaN };
080
081    final double[] initRedRange = { Double.NaN, Double.NaN };
082    final double[] initGrnRange = { Double.NaN, Double.NaN };
083    final double[] initBluRange = { Double.NaN, Double.NaN };
084
085    private FieldImpl imageField = null;
086    private MapProjection mapProjection = null;
087
088    private static final double DEFAULT_GAMMA = 1.0d;
089    private double gamma = DEFAULT_GAMMA;
090
091    private double redGamma = DEFAULT_GAMMA;
092    private double grnGamma = DEFAULT_GAMMA;
093    private double bluGamma = DEFAULT_GAMMA;
094
095    private JCheckBox matchFieldsCbox = null;
096
097    private final JTextField gammaTxtFld =
098        new JTextField(Double.toString(gamma), 4);
099    private final JTextField redGammaTxtFld =
100        new JTextField(Double.toString(redGamma), 4);
101    private final JTextField grnGammaTxtFld =
102        new JTextField(Double.toString(grnGamma), 4);
103    private final JTextField bluGammaTxtFld =
104        new JTextField(Double.toString(bluGamma), 4);
105
106    private final JTextField redLowTxtFld =
107        new JTextField(Float.toString(1.0f), 10);
108    private final JTextField redHighTxtFld =
109        new JTextField(Float.toString(1.0f), 10);
110    private final JTextField grnLowTxtFld =
111        new JTextField(Float.toString(1.0f), 10);
112    private final JTextField grnHighTxtFld =
113        new JTextField(Float.toString(1.0f), 10);
114    private final JTextField bluLowTxtFld =
115        new JTextField(Float.toString(1.0f), 10);
116    private final JTextField bluHighTxtFld =
117        new JTextField(Float.toString(1.0f), 10);
118
119    public boolean init(DataChoice dataChoice) throws VisADException, RemoteException {
120
121        displayMaster = getViewManager().getMaster();
122        DataSelection dataSelection = getDataSelection();
123
124        // TJJ Jul 2014
125        // by sharing a property via the active View Manager, we can signal all three
126        // preview windows they are part of an in-progress RGB Composite. If so, it
127        // appears we need to use a shared HydraContext so our geographic coverage
128        // subset applies across channels.
129
130        Hashtable ht = getIdv().getViewManager().getProperties();
131        ht.put(FORMULA_IN_PROGRESS_FLAG, true);
132
133        imageField = (FieldImpl) dataChoice.getData(dataSelection);
134
135        imageDisplay = new ImageRGBDisplayable("rgb composite", null, false, imageField);
136
137        ht.put(FORMULA_IN_PROGRESS_FLAG, false);
138
139        Iterator iter = imageDisplay.getScalarMapSet().iterator();
140        while (iter.hasNext()) {
141            ScalarMap map = (ScalarMap) iter.next();
142            if (map.getScalarName().startsWith("redimage")) {
143                redMap = map;
144            }
145            if (map.getScalarName().startsWith("greenimage")) {
146                grnMap = map;
147            }
148            if (map.getScalarName().startsWith("blueimage")) {
149                bluMap = map;
150            }
151        }
152
153        if (checkRange()) { //- from unpersistence if true, initialize gui, ScalarMaps
154            double[] redRange = getRedRange();
155            double[] grnRange = getGrnRange();
156            double[] bluRange = getBluRange();
157
158            initRedRange[0] = redRange[0];
159            initRedRange[1] = redRange[1];
160            initGrnRange[0] = grnRange[0];
161            initGrnRange[1] = grnRange[1];
162            initBluRange[0] = bluRange[0];
163            initBluRange[1] = bluRange[1];
164
165            redLowTxtFld.setText(Float.toString((float)redRange[0]));
166            redHighTxtFld.setText(Float.toString((float)redRange[1]));
167            grnLowTxtFld.setText(Float.toString((float)grnRange[0]));
168            grnHighTxtFld.setText(Float.toString((float)grnRange[1]));
169            bluLowTxtFld.setText(Float.toString((float)bluRange[0]));
170            bluHighTxtFld.setText(Float.toString((float)bluRange[1]));
171
172            gammaTxtFld.setText(Float.toString((float)gamma));
173            redGammaTxtFld.setText(Float.toString((float)redGamma));
174            grnGammaTxtFld.setText(Float.toString((float)grnGamma));
175            bluGammaTxtFld.setText(Float.toString((float)bluGamma));
176
177            redMap.setRange(redRange[0], redRange[1]);
178            grnMap.setRange(grnRange[0], grnRange[1]);
179            bluMap.setRange(bluRange[0], bluRange[1]);
180        } else {
181            redMap.resetAutoScale();
182            grnMap.resetAutoScale();
183            bluMap.resetAutoScale();
184
185            redMap.addScalarMapListener(new ColorMapListener(redMap, initRedRange, redRange, redLowTxtFld, redHighTxtFld));
186            grnMap.addScalarMapListener(new ColorMapListener(grnMap, initGrnRange, grnRange, grnLowTxtFld, grnHighTxtFld));
187            bluMap.addScalarMapListener(new ColorMapListener(bluMap, initBluRange, bluRange, bluLowTxtFld, bluHighTxtFld));
188        }
189
190        setShowInDisplayList(true);
191
192        addDisplayable(imageDisplay, FLAG_COLORTABLE | FLAG_ZPOSITION);
193
194        return true;
195    }
196
197    public void initDone() {
198        while (true) {
199            if (null != redMap.getControl()) {
200                redTable = ((BaseColorControl) redMap.getControl()).getTable();
201                break;
202            }
203        }
204        while (true) {
205            if (null != grnMap.getControl()) {
206                grnTable = ((BaseColorControl) grnMap.getControl()).getTable();
207                break;
208            }
209        }
210
211        while (true) {
212            if (null != bluMap.getControl()) {
213                bluTable = ((BaseColorControl) bluMap.getControl()).getTable();
214                break;
215            }
216        }
217
218        float[][] newRedTbl = getZeroOutArray(redTable);
219        float[][] newGrnTbl = getZeroOutArray(grnTable);
220        float[][] newBluTbl = getZeroOutArray(bluTable);
221
222        for (int k=0; k<redTable[0].length; k++) {
223            newRedTbl[0][k] = (float) Math.pow(redTable[0][k], redGamma);
224            newGrnTbl[1][k] = (float) Math.pow(grnTable[1][k], grnGamma);
225            newBluTbl[2][k] = (float) Math.pow(bluTable[2][k], bluGamma);
226        }
227
228        try {
229            displayMaster.setDisplayInactive();
230            ((BaseColorControl)redMap.getControl()).setTable(newRedTbl);
231            ((BaseColorControl)grnMap.getControl()).setTable(newGrnTbl);
232            ((BaseColorControl)bluMap.getControl()).setTable(newBluTbl);
233            imageDisplay.loadData(imageField);
234            displayMaster.setDisplayActive();
235        } catch (Exception ex) {
236            LogUtil.logException("setDisplayInactive", ex);
237        }
238    }
239
240    public MapProjection getDataProjection() {
241        CoordinateSystem cs = null;
242        try {
243            if (imageField instanceof FlatField) {
244                cs = ((FunctionType)imageField.getType()).getDomain().getCoordinateSystem();
245            }
246            else if (imageField instanceof FieldImpl) {
247                Data dat = imageField.getSample(0, false);
248                if (dat instanceof FlatField) {
249                    FlatField img = (FlatField) dat;
250                    cs = ((FunctionType)img.getType()).getDomain().getCoordinateSystem();
251                }
252            }
253        }
254        catch (Exception ex) {
255            LogUtil.logException("problem accessing data", ex);
256        }
257
258        if (cs instanceof MapProjection) {
259            mapProjection = (MapProjection)cs;
260        }
261
262        return mapProjection;
263    }
264
265    boolean checkRange() {
266        return !(Double.isNaN(redRange[0]) || Double.isNaN(grnRange[0]) || Double.isNaN(bluRange[0]));
267    }
268
269    private void updateRedRange(double lo, double hi) {
270        redRange[0] = lo;
271        redRange[1] = hi;
272        redHighTxtFld.setText(Float.toString((float)hi));
273        redLowTxtFld.setText(Float.toString((float)lo));
274        try {
275            redMap.setRange(lo, hi);
276        } catch (VisADException | RemoteException ex) {
277            LogUtil.logException("redMap.setRange", ex);
278        }
279    }
280
281    public void setRedRange(double[] range) {
282        redRange[0] = range[0];
283        redRange[1] = range[1];
284    }
285
286    public double[] getRedRange() {
287        return new double[] {redRange[0], redRange[1]};
288    }
289
290    private void updateGrnRange(double lo, double hi) {
291        grnRange[0] = lo;
292        grnRange[1] = hi;
293        grnHighTxtFld.setText(Float.toString((float)hi));
294        grnLowTxtFld.setText(Float.toString((float)lo));
295        try {
296            grnMap.setRange(lo, hi);
297        } catch (VisADException | RemoteException ex) {
298            LogUtil.logException("grnMap.setRange", ex);
299        }
300    }
301
302    public void setGrnRange(double[] range) {
303        grnRange[0] = range[0];
304        grnRange[1] = range[1];
305    }
306
307    public double[] getGrnRange() {
308        return new double[] {grnRange[0], grnRange[1]};
309    }
310
311    private void updateBluRange(double lo, double hi) {
312        bluRange[0] = lo;
313        bluRange[1] = hi;
314        bluHighTxtFld.setText(Float.toString((float)hi));
315        bluLowTxtFld.setText(Float.toString((float)lo));
316        try {
317            bluMap.setRange(lo, hi);
318        } catch (VisADException | RemoteException ex) {
319            LogUtil.logException("bluMap.setRange", ex);
320        }
321    }
322
323    public void setBluRange(double[] range) {
324        bluRange[0] = range[0];
325        bluRange[1] = range[1];
326    }
327
328    public double[] getBluRange() {
329        return new double[] {bluRange[0], bluRange[1]};
330    }
331
332    public void setRedGamma(double gamma) {
333        redGamma = gamma;
334    }
335
336    public double getRedGamma() {
337        return redGamma;
338    }
339
340    public void setGrnGamma(double gamma) {
341        grnGamma = gamma;
342    }
343
344    public double getGrnGamma() {
345        return grnGamma;
346    }
347
348    public void setBluGamma(double gamma) {
349        bluGamma = gamma;
350    }
351
352    public double getBluGamma() {
353        return bluGamma;
354    }
355
356    public void setGamma(double gamma) {
357        this.gamma = gamma;
358    }
359
360    public double getGamma() {
361        return gamma;
362    }
363
364    public void reapplyAllGammaSettings() {
365        updateRedGamma(getRedGamma());
366        updateGrnGamma(getGrnGamma());
367        updateBluGamma(getBluGamma());
368    }
369
370    /**
371     * TJJ - quick hack, just do something visually jarring to test path
372     */
373
374    /**
375     * Computes a Rayleigh scattering corrected 2D grid for visible range data.
376     *
377     * @param visibleDataGrid      2D grid of remote sensing data in the visible range
378     * (assuming it represents top-of-atmosphere radiance or reflectance).
379     * @param satelliteZenithGrid  2D grid of satellite zenith angles (in degrees).
380     * @param solarZenithGrid      2D grid of solar zenith angles (in degrees).
381     * @param satelliteAzimuthGrid 2D grid of satellite azimuth angles (in degrees).
382     * @param solarAzimuthGrid     2D grid of solar azimuth angles (in degrees).
383     * @param wavelengthVisible    Wavelength of the visible band (in micrometers).
384     * @param atmosphericPressure  Atmospheric pressure at the surface (in hPa).
385     * @return A 2D grid representing the Rayleigh scattering corrected data.
386     * @throws IllegalArgumentException if input grid dimensions are inconsistent.
387     */
388
389    public static FieldImpl correctRayleighVisible(FieldImpl visibleField,
390                                               FieldImpl satelliteZenithField,
391                                               FieldImpl solarZenithField,
392                                               FieldImpl satelliteAzimuthField,
393                                               FieldImpl solarAzimuthField,
394                                               double wavelengthVisible,
395                                               double atmosphericPressure)
396            throws VisADException, RemoteException {
397
398        double[][] visibleDataGrid = visibleField.getValues();
399        double[][] satelliteZenithGrid = satelliteZenithField.getValues();
400        double[][] solarZenithGrid = solarZenithField.getValues();
401        double[][] satelliteAzimuthGrid = satelliteAzimuthField.getValues();
402        double[][] solarAzimuthGrid = solarAzimuthField.getValues();
403
404        int rows = visibleDataGrid.length;
405        int cols = visibleDataGrid[0].length;
406
407        // float[][] correctedDataGrid = new float[rows][cols];
408
409        for (int i = 0; i < rows; i++) {
410            for (int j = 0; j < cols; j++) {
411                float thetaV = (float) Math.toRadians(satelliteZenithGrid[i][j]);
412                float thetaS = (float) Math.toRadians(solarZenithGrid[i][j]);
413                float phiV = (float) Math.toRadians(satelliteAzimuthGrid[i][j]);
414                float phiS = (float) Math.toRadians(solarAzimuthGrid[i][j]);
415
416                double rhoRayleigh = calculateRayleighReflectance(wavelengthVisible, thetaS, thetaV, phiS, phiV, atmosphericPressure);
417                visibleDataGrid[i][j] = (float) (visibleDataGrid[i][j] - rhoRayleigh);
418            }
419        }
420        visibleField.setSamples(visibleDataGrid);
421
422        logger.info("3141 - made it out");
423        return visibleField;
424    }
425
426    // Calculate Rayleigh reflectance - McIDAS Inquiry #3055-3141
427    private static double calculateRayleighReflectance(
428            double wavelength, double solarZenithRad, double satelliteZenithRad,
429            double solarAzimuthRad, double satelliteAzimuthRad, double pressureHPa) {
430
431        double tau_ro = (0.008569 / Math.pow(wavelength, 4))
432                        * (1 + (0.0113 / Math.pow(wavelength, 2)) + (0.00013 / Math.pow(wavelength, 4)));
433
434        // \tau_{ro} = \frac{0.008569}{\lambda^4} \cdot (1 + \frac{0.0113}{\lambda^2} + \frac{0.00013}{\lambda^4})
435
436        double pressure_rat = pressureHPa / 1013.25;
437        // P = \frac{pHPa}{atmP}
438
439        double tau = pressure_rat * tau_ro; // this is the only thing matters next
440        // \tau = P \cdot \tau_{ro}
441
442        double scattering_angle_cos = Math.cos(solarZenithRad) * Math.cos(satelliteZenithRad)
443                                    + Math.sin(solarZenithRad) * Math.sin(satelliteZenithRad)
444                                    * Math.cos(satelliteAzimuthRad - solarAzimuthRad);
445
446        // \cos (\Theta) = \cos (\theta_{sun})\cos (\theta_{sat}) + \sin (\theta_{sun})\sin (\theta_{sat}) \cdot \cos(\phi_{sat} - \phi_{sun})
447
448        double P_big_theta = 0.75 * (1 + Math.pow(scattering_angle_cos, 2)); // another important value
449        // P(\Theta) = 0.75 * (1 + (\cos(\Theta))^2)
450
451        double mu_1 = Math.cos(solarZenithRad);
452        double mu_2 = Math.cos(satelliteZenithRad);
453
454        return tau * P_big_theta * (1 / (4 * mu_1 * mu_2));
455        // \tau \cdot P(\Theta) \cdot (\frac{1}{4 \cdot \mu_1 \cdot \mu_2})
456    }
457
458    private void applyRayleighCorrection() {
459        float[][] newRedTbl = getZeroOutArray(redTable);
460        float[][] newGrnTbl = getZeroOutArray(grnTable);
461        float[][] newBluTbl = getZeroOutArray(bluTable);
462
463        for (int k = 0; k < redTable[0].length; k++) {
464            newRedTbl[0][k] = (float) Math.pow(redTable[0][k], 0.2);
465            newGrnTbl[1][k] = (float) Math.pow(grnTable[1][k], 0.2);
466            newBluTbl[2][k] = (float) Math.pow(bluTable[2][k], 0.2);
467        }
468        try {
469            displayMaster.setDisplayInactive();
470            ((BaseColorControl) redMap.getControl()).setTable(newRedTbl);
471            ((BaseColorControl) grnMap.getControl()).setTable(newGrnTbl);
472            ((BaseColorControl) bluMap.getControl()).setTable(newBluTbl);
473            displayMaster.setDisplayActive();
474        } catch (Exception e) {
475            LogUtil.logException("setDisplayInactive", e);
476        }
477    }
478
479    private void updateGamma(double gamma) {
480        setGamma(gamma);
481        setRedGamma(gamma);
482        setGrnGamma(gamma);
483        setBluGamma(gamma);
484        redGammaTxtFld.setText(Float.toString((float)gamma));
485        grnGammaTxtFld.setText(Float.toString((float)gamma));
486        bluGammaTxtFld.setText(Float.toString((float)gamma));
487
488        float[][] newRedTbl = getZeroOutArray(redTable);
489        float[][] newGrnTbl = getZeroOutArray(grnTable);
490        float[][] newBluTbl = getZeroOutArray(bluTable);
491
492        for (int k=0; k<redTable[0].length; k++) {
493            newRedTbl[0][k] = (float) Math.pow(redTable[0][k], gamma);
494            newGrnTbl[1][k] = (float) Math.pow(grnTable[1][k], gamma);
495            newBluTbl[2][k] = (float) Math.pow(bluTable[2][k], gamma);
496        }
497        try {
498            displayMaster.setDisplayInactive();
499            ((BaseColorControl)redMap.getControl()).setTable(newRedTbl);
500            ((BaseColorControl)grnMap.getControl()).setTable(newGrnTbl);
501            ((BaseColorControl)bluMap.getControl()).setTable(newBluTbl);
502            displayMaster.setDisplayActive();
503        } catch (Exception ex) {
504            LogUtil.logException("setDisplayInactive", ex);
505        }
506    }
507
508    private void updateRedGamma(double gamma) {
509        setRedGamma(gamma);
510
511        float[][] newRedTbl = getZeroOutArray(redTable);
512
513        for (int k=0; k<redTable[0].length; k++) {
514            newRedTbl[0][k] = (float) Math.pow(redTable[0][k], gamma);
515        }
516
517        try {
518            displayMaster.setDisplayInactive();
519            ((BaseColorControl)redMap.getControl()).setTable(newRedTbl);
520            displayMaster.setDisplayActive();
521        } catch (Exception ex) {
522            LogUtil.logException("setDisplayInactive", ex);
523        }
524    }
525
526    private void updateGrnGamma(double gamma) {
527        setGrnGamma(gamma);
528
529        float[][] newGrnTbl = getZeroOutArray(grnTable);
530        for (int k=0; k<grnTable[0].length; k++) {
531            newGrnTbl[1][k] = (float) Math.pow(grnTable[1][k], gamma);
532        }
533
534        try {
535            displayMaster.setDisplayInactive();
536            ((BaseColorControl)grnMap.getControl()).setTable(newGrnTbl);
537            displayMaster.setDisplayActive();
538        } catch (Exception ex) {
539            LogUtil.logException("setDisplayInactive", ex);
540        }
541    }
542
543    private void updateBluGamma(double gamma) {
544        setBluGamma(gamma);
545
546        float[][] newBluTbl = getZeroOutArray(bluTable);
547        for (int k=0; k<bluTable[0].length; k++) {
548            newBluTbl[2][k] = (float) Math.pow(bluTable[2][k], gamma);
549        }
550
551        try {
552            displayMaster.setDisplayInactive();
553            ((BaseColorControl)bluMap.getControl()).setTable(newBluTbl);
554            displayMaster.setDisplayActive();
555        } catch (Exception ex) {
556            LogUtil.logException("setDisplayInactive", ex);
557        }
558    }
559
560    public float[][] getZeroOutArray(float[][] array) {
561        float[][] newArray = new float[array.length][array[0].length];
562        for (int i=0; i<newArray.length; i++) {
563            for (int j=0; j<newArray[0].length; j++) {
564                newArray[i][j] = 0.0f;
565            }
566        }
567        return newArray;
568    }
569
570    protected ColorTable getInitialColorTable() {
571        return getDisplayConventions().getParamColorTable("image");
572    }
573
574    void setAllFields(String txtl1, String txtl2) {
575        Double l1 = Double.valueOf(txtl1.trim());
576        Double l2 = Double.valueOf(txtl2.trim());
577        bluRange[0] = l1;
578        bluRange[1] = l2;
579        redRange[0] = l1;
580        redRange[1] = l2;
581        grnRange[0] = l1;
582        grnRange[1] = l2;
583        updateRedRange(redRange[0], redRange[1]);
584        updateBluRange(redRange[0], redRange[1]);
585        updateGrnRange(redRange[0], redRange[1]);
586
587        redLowTxtFld.setText(txtl1);
588        grnLowTxtFld.setText(txtl1);
589        bluLowTxtFld.setText(txtl1);
590
591        redHighTxtFld.setText(txtl2);
592        bluHighTxtFld.setText(txtl2);
593        grnHighTxtFld.setText(txtl2);
594    }
595
596    public Container doMakeContents() {
597
598        JButton allGammaButton = new JButton("Apply to All Gamma Fields");
599        allGammaButton.addActionListener(e -> {
600            String tmp = gammaTxtFld.getText().trim();
601            updateGamma(Double.valueOf(tmp));
602        });
603
604        gammaTxtFld.addActionListener(e -> {
605            String tmp = gammaTxtFld.getText().trim();
606            updateGamma(Double.valueOf(tmp));
607        });
608
609        // McIDAS Inquiry #3193-3141
610        redLowTxtFld.addActionListener(e -> {
611            if (matchFieldsCbox.isSelected())
612                setAllFields(redLowTxtFld.getText(),redHighTxtFld.getText());
613
614            Double l1 = Double.valueOf(redLowTxtFld.getText().trim());
615            Double l2 = Double.valueOf(redHighTxtFld.getText().trim());
616            redRange[0] = l1;
617            redRange[1] = l2;
618            updateRedRange(redRange[0], redRange[1]);
619
620        });
621
622        redHighTxtFld.addActionListener(e -> {
623            if (matchFieldsCbox.isSelected())
624                setAllFields(redLowTxtFld.getText(),redHighTxtFld.getText());
625
626            Double l1 = Double.valueOf(redLowTxtFld.getText().trim());
627            Double l2 = Double.valueOf(redHighTxtFld.getText().trim());
628            redRange[0] = l1;
629            redRange[1] = l2;
630            updateRedRange(redRange[0], redRange[1]);
631        });
632
633        redGammaTxtFld.addActionListener(e -> {
634            String tmp = redGammaTxtFld.getText().trim();
635            updateRedGamma(Double.valueOf(tmp));
636
637            if (matchFieldsCbox.isSelected()) {
638                grnGammaTxtFld.setText(tmp);
639                bluGammaTxtFld.setText(tmp);
640                updateBluGamma(Double.valueOf(tmp));
641                updateGrnGamma(Double.valueOf(tmp));
642            }
643        });
644
645        JButton redReset = new JButton("Reset");
646        redReset.addActionListener(e -> {
647            updateRedRange(initRedRange[0], initRedRange[1]);
648            redRange[0] = initRedRange[0];
649            redRange[1] = initRedRange[1];
650            redLowTxtFld.setText(Float.toString((float)redRange[0]));
651            redHighTxtFld.setText(Float.toString((float)redRange[1]));
652            updateRedGamma(1.0);
653            redGammaTxtFld.setText("1.0");
654        });
655
656        grnLowTxtFld.addActionListener(e -> {
657            if (matchFieldsCbox.isSelected())
658                setAllFields(grnLowTxtFld.getText(),grnHighTxtFld.getText());
659
660            Double l1 = Double.valueOf(grnLowTxtFld.getText().trim());
661            Double l2 = Double.valueOf(grnHighTxtFld.getText().trim());
662            grnRange[0] = l1;
663            grnRange[1] = l2;
664            updateGrnRange(grnRange[0], grnRange[1]);
665        });
666
667        grnHighTxtFld.addActionListener(e -> {
668            if (matchFieldsCbox.isSelected())
669                setAllFields(grnLowTxtFld.getText(),grnHighTxtFld.getText());
670
671            Double l1 = Double.valueOf(grnLowTxtFld.getText().trim());
672            Double l2 = Double.valueOf(grnHighTxtFld.getText().trim());
673            grnRange[0] = l1;
674            grnRange[1] = l2;
675            updateGrnRange(grnRange[0], grnRange[1]);
676        });
677
678        grnGammaTxtFld.addActionListener(e -> {
679            String tmp = grnGammaTxtFld.getText().trim();
680            updateGrnGamma(Double.valueOf(tmp));
681
682            if (matchFieldsCbox.isSelected()) {
683                redGammaTxtFld.setText(tmp);
684                bluGammaTxtFld.setText(tmp);
685                updateRedGamma(Double.valueOf(tmp));
686                updateGrnGamma(Double.valueOf(tmp));
687            }
688        });
689
690        JButton grnReset = new JButton("Reset");
691        grnReset.addActionListener(e -> {
692            updateGrnRange(initGrnRange[0], initGrnRange[1]);
693            grnRange[0] = initGrnRange[0];
694            grnRange[1] = initGrnRange[1];
695            grnLowTxtFld.setText(Float.toString((float)grnRange[0]));
696            grnHighTxtFld.setText(Float.toString((float)grnRange[1]));
697            updateGrnGamma(1.0);
698            grnGammaTxtFld.setText("1.0");
699        });
700
701        bluLowTxtFld.addActionListener(e -> {
702            if (matchFieldsCbox.isSelected())
703                setAllFields(bluLowTxtFld.getText(),bluHighTxtFld.getText());
704
705            Double l1 = Double.valueOf(bluLowTxtFld.getText().trim());
706            Double l2 = Double.valueOf(bluHighTxtFld.getText().trim());
707            bluRange[0] = l1;
708            bluRange[1] = l2;
709            updateBluRange(bluRange[0], bluRange[1]);
710        });
711
712        bluHighTxtFld.addActionListener(e -> {
713            if (matchFieldsCbox.isSelected())
714                setAllFields(bluLowTxtFld.getText(),bluHighTxtFld.getText());
715
716            Double l1 = Double.valueOf(bluLowTxtFld.getText().trim());
717            Double l2 = Double.valueOf(bluHighTxtFld.getText().trim());
718            bluRange[0] = l1;
719            bluRange[1] = l2;
720            updateBluRange(bluRange[0], bluRange[1]);
721        });
722
723        bluGammaTxtFld.addActionListener(e -> {
724            String tmp = bluGammaTxtFld.getText().trim();
725            updateBluGamma(Double.valueOf(tmp));
726
727            if (matchFieldsCbox.isSelected()) {
728                grnGammaTxtFld.setText(tmp);
729                redGammaTxtFld.setText(tmp);
730                updateGrnGamma(Double.valueOf(tmp));
731                updateRedGamma(Double.valueOf(tmp));
732            }
733        });
734
735        JButton bluReset = new JButton("Reset");
736        bluReset.addActionListener(e -> {
737            updateBluRange(initBluRange[0], initBluRange[1]);
738            bluRange[0] = initBluRange[0];
739            bluRange[1] = initBluRange[1];
740            bluLowTxtFld.setText(Float.toString((float)bluRange[0]));
741            bluHighTxtFld.setText(Float.toString((float)bluRange[1]));
742            updateBluGamma(1.0);
743            bluGammaTxtFld.setText("1.0");
744        });
745
746        JButton applyButton = new JButton("Apply");
747        applyButton.addActionListener(e -> {
748            String redLow = redLowTxtFld.getText().trim();
749            String redHigh = redHighTxtFld.getText().trim();
750            updateRedRange(Double.valueOf(redLow), Double.valueOf(redHigh));
751            String grnLow = grnLowTxtFld.getText().trim();
752            String grnHigh = grnHighTxtFld.getText().trim();
753            updateGrnRange(Double.valueOf(grnLow), Double.valueOf(grnHigh));
754            String bluLow = bluLowTxtFld.getText().trim();
755            String bluHigh = bluHighTxtFld.getText().trim();
756            updateBluRange(Double.valueOf(bluLow), Double.valueOf(bluHigh));
757
758            String tmp1 = redGammaTxtFld.getText().trim();
759            updateRedGamma(Double.valueOf(tmp1));
760            String tmp2 = grnGammaTxtFld.getText().trim();
761            updateGrnGamma(Double.valueOf(tmp2));
762            String tmp3 = bluGammaTxtFld.getText().trim();
763            updateBluGamma(Double.valueOf(tmp3));
764        });
765
766        // McIDAS Inquiry #3193-3141
767        matchFieldsCbox = new JCheckBox();
768        matchFieldsCbox.setToolTipText("When enabled, changing a setting for one color changes the setting for all colors.");
769        JPanel topPanel = new JPanel(new MigLayout());
770        topPanel.add(new JLabel("Match fields: "));
771        topPanel.add(matchFieldsCbox, "wrap");
772        topPanel.add(new JLabel("Red Range: "));
773        topPanel.add(redLowTxtFld);
774        topPanel.add(redHighTxtFld);
775        topPanel.add(new JLabel("Red Gamma: "));
776        topPanel.add(redGammaTxtFld);
777        topPanel.add(redReset, "wrap");
778
779        topPanel.add(new JLabel("Green Range: "));
780        topPanel.add(grnLowTxtFld);
781        topPanel.add(grnHighTxtFld);
782        topPanel.add(new JLabel("Green Gamma: "));
783        topPanel.add(grnGammaTxtFld);
784        topPanel.add(grnReset, "wrap");
785
786        topPanel.add(new JLabel("Blue Range: "));
787        topPanel.add(bluLowTxtFld);
788        topPanel.add(bluHighTxtFld);
789        topPanel.add(new JLabel("Blue Gamma: "));
790        topPanel.add(bluGammaTxtFld);
791        topPanel.add(bluReset, "wrap");
792
793        topPanel.add(Box.createHorizontalStrut(2), "span 5");
794        topPanel.add(applyButton, "wrap");
795
796        JPanel bottomPanel = new JPanel(new MigLayout());
797        bottomPanel.add(new JLabel("Common Gamma: "));
798        bottomPanel.add(gammaTxtFld);
799        bottomPanel.add(allGammaButton, "wrap");
800        bottomPanel.add(new JLabel("Vertical Position: "));
801        bottomPanel.add(doMakeZPositionSlider());
802
803        JPanel mainPanel = new JPanel(new MigLayout("wrap 1", "[grow]", ""));
804        mainPanel.add(topPanel, "growx");
805        mainPanel.add(bottomPanel, "growx");
806
807        return mainPanel;
808    }
809
810    private class ColorMapListener implements ScalarMapListener {
811        ScalarMap clrMap;
812
813        double[] range = null;
814        double[] initRange = null;
815
816        JTextField lowTxtFld;
817        JTextField highTxtFld;
818
819        ColorMapListener(ScalarMap clrMap, double[] initRange, double[] range, JTextField lowTxtFld, JTextField highTxtFld) {
820            this.clrMap = clrMap;
821            this.lowTxtFld = lowTxtFld;
822            this.highTxtFld = highTxtFld;
823            this.range = range;
824            this.initRange = initRange;
825        }
826
827        public void controlChanged(ScalarMapControlEvent event) throws RemoteException, VisADException {
828        }
829
830        @Override
831        public void mapChanged(ScalarMapEvent event) throws RemoteException, VisADException {
832            if (event.getId() == ScalarMapEvent.AUTO_SCALE) {
833                double[] rng = clrMap.getRange();
834                boolean shouldRemove = false;
835                //Ghansham: decide whether it is first time. The cleaner way
836                if (!Double.isNaN(rng[0]) && !Double.isNaN(rng[1]) && Double.isNaN(initRange[0]) && Double.isNaN(initRange[1])) {
837                    shouldRemove = true;
838                }
839                range[0] = rng[0];
840                range[1] = rng[1];
841                initRange[0] = rng[0];
842                initRange[1] = rng[1];
843                lowTxtFld.setText(Float.toString((float)rng[0]));
844                highTxtFld.setText(Float.toString((float)rng[1]));
845                //Ghansham:If its first time remove the scalarmaplistener and setRange manually to disable autscaling of the scalarmap
846                if (shouldRemove) {
847                    clrMap.removeScalarMapListener(this);
848                    //-Lock out auto-scaling
849                    clrMap.disableAutoScale();
850                }
851            } else if (event.getId() == ScalarMapEvent.MANUAL) {
852                double[] rng = clrMap.getRange();
853                range[0] = rng[0];
854                range[1] = rng[1];
855            }
856        }
857    }
858}