001/*
002 * $Id: RGBCompositeControl.java,v 1.10 2011/03/24 16:06:32 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 */
030package edu.wisc.ssec.mcidasv.control;
031
032import java.awt.BorderLayout;
033import java.awt.Container;
034import java.awt.FlowLayout;
035import java.awt.GridLayout;
036import java.awt.event.ActionEvent;
037import java.awt.event.ActionListener;
038import java.rmi.RemoteException;
039import java.util.Iterator;
040
041import javax.swing.JButton;
042import javax.swing.JLabel;
043import javax.swing.JPanel;
044import javax.swing.JTextField;
045
046import visad.BaseColorControl;
047import visad.FieldImpl;
048import visad.FlatField;
049import visad.FunctionType;
050import visad.ScalarMap;
051import visad.ScalarMapControlEvent;
052import visad.ScalarMapEvent;
053import visad.ScalarMapListener;
054import visad.VisADException;
055import visad.Data;
056import visad.CoordinateSystem;
057
058import visad.georef.MapProjection;
059
060import ucar.unidata.data.DataChoice;
061import ucar.unidata.data.DataSelection;
062import ucar.unidata.data.grid.GridDataInstance;
063import ucar.unidata.idv.control.DisplayControlImpl;
064import ucar.unidata.util.ColorTable;
065import ucar.unidata.util.LogUtil;
066import ucar.unidata.util.Range;
067import ucar.visad.display.DisplayMaster;
068
069import edu.wisc.ssec.mcidasv.data.hydra.ImageRGBDisplayable;
070
071
072public class RGBCompositeControl extends DisplayControlImpl {
073
074   /** Displayable for the data */
075   private ImageRGBDisplayable imageDisplay;
076
077   private DisplayMaster displayMaster;
078
079   private ScalarMap redMap = null;
080   private ScalarMap grnMap = null;
081   private ScalarMap bluMap = null;
082
083   float[][] redTable = null;
084   float[][] grnTable = null;
085   float[][] bluTable = null;
086
087   final private double[] redRange = new double[] {Double.NaN, Double.NaN};
088   final private double[] grnRange = new double[] {Double.NaN, Double.NaN};
089   final private double[] bluRange = new double[] {Double.NaN, Double.NaN};
090
091   final double[] initRedRange = new double[] {Double.NaN, Double.NaN};
092   final double[] initGrnRange = new double[] {Double.NaN, Double.NaN};
093   final double[] initBluRange = new double[] {Double.NaN, Double.NaN};
094
095   private FieldImpl imageField = null;
096   private MapProjection mapProjection = null;
097
098
099   private double gamma = 1.0;
100
101   private double redGamma = 1.0;
102   private double grnGamma = 1.0;
103   private double bluGamma = 1.0;
104
105   private boolean hasRange = false;
106
107   private final JTextField gammaTxtFld =
108        new JTextField(Float.toString(1f), 4);
109   private final JTextField redGammaTxtFld =
110        new JTextField(Float.toString(1f), 4);
111   private final JTextField grnGammaTxtFld =
112        new JTextField(Float.toString(1f), 4);
113   private final JTextField bluGammaTxtFld =
114        new JTextField(Float.toString(1f), 4);
115
116   private final JTextField redLowTxtFld =
117        new JTextField(Float.toString(1f), 10);
118   private final JTextField redHighTxtFld =
119        new JTextField(Float.toString(1f), 10);
120   private final JTextField grnLowTxtFld =
121        new JTextField(Float.toString(1f), 10);
122   private final JTextField grnHighTxtFld =
123        new JTextField(Float.toString(1f), 10);
124   private final JTextField bluLowTxtFld =
125        new JTextField(Float.toString(1f), 10);
126   private final JTextField bluHighTxtFld =
127        new JTextField(Float.toString(1f), 10);
128
129   public RGBCompositeControl() {
130     super();
131   }
132
133   public boolean init(DataChoice dataChoice) throws VisADException, RemoteException {
134     displayMaster = getViewManager().getMaster();
135     DataSelection dataSelection = getDataSelection();
136     imageField = (FieldImpl) dataChoice.getData(dataSelection);
137
138
139     imageDisplay = new ImageRGBDisplayable("rgb composite", null, false, imageField);
140
141     Iterator iter = imageDisplay.getScalarMapSet().iterator();
142     while (iter.hasNext()) {
143       ScalarMap map = (ScalarMap) iter.next();
144       double[] datRng = map.getRange();
145       if (map.getScalarName().startsWith("redimage")) {
146                redMap = map;
147        }
148       if (map.getScalarName().startsWith("greenimage")) {
149                grnMap = map;
150        }
151       if (map.getScalarName().startsWith("blueimage")) {
152                bluMap = map;
153        }
154     }
155
156     if (checkRange()) { //- from unpersistence if true, initialize gui, ScalarMaps
157       double[] redRange = getRedRange();
158       double[] grnRange = getGrnRange();
159       double[] bluRange = getBluRange();
160
161       initRedRange[0] = redRange[0];
162       initRedRange[1] = redRange[1];
163       initGrnRange[0] = grnRange[0];
164       initGrnRange[1] = grnRange[1];
165       initBluRange[0] = bluRange[0];
166       initBluRange[1] = bluRange[1];
167
168       redLowTxtFld.setText(Float.toString((float)redRange[0]));
169       redHighTxtFld.setText(Float.toString((float)redRange[1]));
170       grnLowTxtFld.setText(Float.toString((float)grnRange[0]));
171       grnHighTxtFld.setText(Float.toString((float)grnRange[1]));
172       bluLowTxtFld.setText(Float.toString((float)bluRange[0]));
173       bluHighTxtFld.setText(Float.toString((float)bluRange[1]));
174   
175       gammaTxtFld.setText(Float.toString((float)gamma));
176       redGammaTxtFld.setText(Float.toString((float)redGamma));
177       grnGammaTxtFld.setText(Float.toString((float)grnGamma));
178       bluGammaTxtFld.setText(Float.toString((float)bluGamma));
179
180       redMap.setRange(redRange[0], redRange[1]);
181       grnMap.setRange(grnRange[0], grnRange[1]);
182       bluMap.setRange(bluRange[0], bluRange[1]);
183     } 
184     else {
185       redMap.resetAutoScale();
186       grnMap.resetAutoScale();
187       bluMap.resetAutoScale();
188
189       redMap.addScalarMapListener(new ColorMapListener(redMap, initRedRange, redRange, redLowTxtFld, redHighTxtFld));
190       grnMap.addScalarMapListener(new ColorMapListener(grnMap, initGrnRange, grnRange, grnLowTxtFld, grnHighTxtFld));
191       bluMap.addScalarMapListener(new ColorMapListener(bluMap, initBluRange, bluRange, bluLowTxtFld, bluHighTxtFld));
192     }
193
194     setShowInDisplayList(true);
195
196     addDisplayable(imageDisplay, FLAG_COLORTABLE);
197
198     return true;
199   }
200
201   public void initDone() {
202     redTable = ((BaseColorControl)redMap.getControl()).getTable();
203     grnTable = ((BaseColorControl)grnMap.getControl()).getTable();
204     bluTable = ((BaseColorControl)bluMap.getControl()).getTable();
205
206     float[][] newRedTbl = getZeroOutArray(redTable);
207     float[][] newGrnTbl = getZeroOutArray(grnTable);
208     float[][] newBluTbl = getZeroOutArray(bluTable);
209
210     for (int k=0; k<redTable[0].length; k++) {
211       newRedTbl[0][k] = (float) Math.pow(redTable[0][k], redGamma);
212       newGrnTbl[1][k] = (float) Math.pow(grnTable[1][k], grnGamma);
213       newBluTbl[2][k] = (float) Math.pow(bluTable[2][k], bluGamma);
214     }
215
216     try {
217       displayMaster.setDisplayInactive();
218       ((BaseColorControl)redMap.getControl()).setTable(newRedTbl);
219       ((BaseColorControl)grnMap.getControl()).setTable(newGrnTbl);
220       ((BaseColorControl)bluMap.getControl()).setTable(newBluTbl);
221       imageDisplay.loadData(imageField);
222       displayMaster.setDisplayActive();
223     } catch(Exception ex) {
224       LogUtil.logException("setDisplayInactive", ex);
225     }
226   }
227
228   public MapProjection getDataProjection() {
229     CoordinateSystem cs = null;
230     try {
231       if (imageField instanceof FlatField) {
232         cs = ((FunctionType)imageField.getType()).getDomain().getCoordinateSystem();
233       } 
234       else if (imageField instanceof FieldImpl) {
235         Data dat = imageField.getSample(0, false);
236         if (dat instanceof FlatField) {
237           FlatField img = (FlatField) dat;
238           cs = ((FunctionType)img.getType()).getDomain().getCoordinateSystem();
239         }
240       }
241     }
242     catch (Exception ex) {
243       LogUtil.logException("problem accessing data", ex);
244     }
245
246     if (cs instanceof MapProjection) mapProjection = (MapProjection) cs;
247
248     return mapProjection;
249   }
250
251   boolean checkRange() {
252     if (Double.isNaN(redRange[0]) || Double.isNaN(grnRange[0]) || Double.isNaN(bluRange[0])) {
253       return false;
254     }
255     else {
256       return true;
257     }
258   }
259
260   private void updateRedRange(double lo, double hi) {
261     redRange[0] = lo;
262     redRange[1] = hi;
263     try {
264       redMap.setRange(lo, hi);
265     } catch (VisADException ex) {
266       LogUtil.logException("redMap.setRange", ex);
267     } catch (RemoteException ex) {
268       LogUtil.logException("redMap.setRange", ex);
269     }
270   }
271
272   public void setRedRange(double[] range) {
273     redRange[0] = range[0];
274     redRange[1] = range[1];
275   }
276
277   public double[] getRedRange() {
278     return new double[] {redRange[0], redRange[1]};
279   }
280
281   private void updateGrnRange(double lo, double hi) {
282     grnRange[0] = lo;
283     grnRange[1] = hi;
284     try {
285       grnMap.setRange(lo, hi);
286     } catch (VisADException ex) {
287       LogUtil.logException("grnMap.setRange", ex);
288     } catch (RemoteException ex) {
289       LogUtil.logException("grnMap.setRange", ex);
290     }
291   }
292
293   public void setGrnRange(double[] range) {
294     grnRange[0] = range[0];
295     grnRange[1] = range[1];
296   }
297
298   public double[] getGrnRange() {
299     return new double[] {grnRange[0], grnRange[1]};
300   }
301
302   private void updateBluRange(double lo, double hi) {
303     bluRange[0] = lo;
304     bluRange[1] = hi;
305     try {
306       bluMap.setRange(lo, hi);
307     } catch (VisADException ex) {
308       LogUtil.logException("bluMap.setRange", ex);
309     } catch (RemoteException ex) {
310       LogUtil.logException("bluMap.setRange", ex);
311     }
312   }
313
314   public void setBluRange(double[] range) {
315     bluRange[0] = range[0];
316     bluRange[1] = range[1];
317   }
318
319   public double[] getBluRange() {
320     return new double[] {bluRange[0], bluRange[1]};
321   }
322
323   public void setRedGamma(double gamma) {
324     redGamma = gamma;
325   }
326
327   public double getRedGamma() {
328     return redGamma;
329   }
330
331   public void setGrnGamma(double gamma) {
332     grnGamma = gamma;
333   }
334
335   public double getGrnGamma() {
336     return grnGamma;
337   }
338
339   public void setBluGamma(double gamma) {
340     bluGamma = gamma;
341   }
342
343   public double getBluGamma() {
344     return bluGamma;
345   }
346
347   public void setGamma(double gamma) {
348     this.gamma = gamma;
349   }
350
351   public double getGamma() {
352     return gamma;
353   }
354
355   private void updateGamma(double gamma) {
356     setGamma(gamma);
357     setRedGamma(gamma);
358     setGrnGamma(gamma);
359     setBluGamma(gamma);
360     redGammaTxtFld.setText(Float.toString((float)gamma));
361     grnGammaTxtFld.setText(Float.toString((float)gamma));
362     bluGammaTxtFld.setText(Float.toString((float)gamma));
363        
364     float[][] newRedTbl = getZeroOutArray(redTable);
365     float[][] newGrnTbl = getZeroOutArray(grnTable);
366     float[][] newBluTbl = getZeroOutArray(bluTable);
367
368     for (int k=0; k<redTable[0].length; k++) {
369       newRedTbl[0][k] = (float) Math.pow(redTable[0][k], gamma);
370       newGrnTbl[1][k] = (float) Math.pow(grnTable[1][k], gamma);
371       newBluTbl[2][k] = (float) Math.pow(bluTable[2][k], gamma);
372     }
373     try {
374       displayMaster.setDisplayInactive();
375       ((BaseColorControl)redMap.getControl()).setTable(newRedTbl);
376       ((BaseColorControl)grnMap.getControl()).setTable(newGrnTbl);
377       ((BaseColorControl)bluMap.getControl()).setTable(newBluTbl);
378       displayMaster.setDisplayActive();
379     } catch(Exception ex) {
380       LogUtil.logException("setDisplayInactive", ex);
381     }
382   }
383
384   private void updateRedGamma(double gamma) {
385     setRedGamma(gamma);
386
387     float[][] newRedTbl = getZeroOutArray(redTable);
388
389     for (int k=0; k<redTable[0].length; k++) {
390       newRedTbl[0][k] = (float) Math.pow(redTable[0][k], gamma);
391     }
392
393     try {
394       displayMaster.setDisplayInactive();
395       ((BaseColorControl)redMap.getControl()).setTable(newRedTbl);
396       displayMaster.setDisplayActive();
397     } catch(Exception ex) {
398       LogUtil.logException("setDisplayInactive", ex);
399     }
400   }
401
402   private void updateGrnGamma(double gamma) {
403     setGrnGamma(gamma);
404
405     float[][] newGrnTbl = getZeroOutArray(grnTable);
406     for (int k=0; k<grnTable[0].length; k++) {
407       newGrnTbl[1][k] = (float) Math.pow(grnTable[1][k], gamma);
408     }
409
410     try {
411       displayMaster.setDisplayInactive();
412       ((BaseColorControl)grnMap.getControl()).setTable(newGrnTbl);
413       displayMaster.setDisplayActive();
414     } catch(Exception ex) {
415       LogUtil.logException("setDisplayInactive", ex);
416     }
417   }
418
419   private void updateBluGamma(double gamma) {
420     setBluGamma(gamma);
421
422     float[][] newBluTbl = getZeroOutArray(bluTable);
423     for (int k=0; k<bluTable[0].length; k++) {
424       newBluTbl[2][k] = (float) Math.pow(bluTable[2][k], gamma);
425     }
426
427     try {
428       displayMaster.setDisplayInactive();
429       ((BaseColorControl)bluMap.getControl()).setTable(newBluTbl);
430       displayMaster.setDisplayActive();
431     } catch(Exception ex) {
432       LogUtil.logException("setDisplayInactive", ex);
433     }
434   }
435
436   public float[][] getZeroOutArray(float[][] array) {
437     float[][] newArray = new float[array.length][array[0].length];
438     for (int i=0; i<newArray.length; i++) {
439       for (int j=0; j<newArray[0].length; j++) {
440         newArray[i][j] = 0f;
441       }
442     }
443     return newArray;
444   }
445
446   protected ColorTable getInitialColorTable() {
447     return getDisplayConventions().getParamColorTable("image");
448   }
449
450   public Container doMakeContents() {
451
452     JPanel bigPanel = new JPanel(new BorderLayout());
453     JPanel subPanel = new JPanel(new GridLayout(4,1));
454
455     JPanel gammaPanel = new JPanel(new FlowLayout());
456          final JLabel nameLabel = new JLabel("Gamma: ");
457
458          gammaTxtFld.addActionListener(new ActionListener() {
459              public void actionPerformed(ActionEvent e) {
460                  String tmp = gammaTxtFld.getText().trim();
461                  updateGamma(Double.valueOf(tmp));
462              }
463          });
464
465     gammaPanel.add(nameLabel);
466     gammaPanel.add(gammaTxtFld);
467
468     JPanel redPanel = new JPanel(new FlowLayout());
469     redPanel.add(new JLabel("Red range: "));
470   
471     redLowTxtFld.addActionListener(new ActionListener() {
472         public void actionPerformed(ActionEvent e) {
473            String tmp = redLowTxtFld.getText().trim();
474            updateRedRange(Double.valueOf(tmp), redRange[1]);
475         }
476     });
477     redPanel.add(redLowTxtFld);
478     redHighTxtFld.addActionListener(new ActionListener() {
479         public void actionPerformed(ActionEvent e) {
480            String tmp = redHighTxtFld.getText().trim();
481            updateRedRange(redRange[0], Double.valueOf(tmp));
482         }
483     });
484     redPanel.add(redHighTxtFld);
485
486     redGammaTxtFld.addActionListener(new ActionListener() {
487         public void actionPerformed(ActionEvent e) {
488             String tmp = redGammaTxtFld.getText().trim();
489             updateRedGamma(Double.valueOf(tmp));
490         }
491     });
492     redPanel.add(new JLabel("Gamma:"));
493     redPanel.add(redGammaTxtFld);
494
495     JButton button = new JButton("reset");
496     redPanel.add(button);
497     button.addActionListener(new ActionListener() {
498       public void actionPerformed(ActionEvent e) {
499         updateRedRange(initRedRange[0], initRedRange[1]);
500         redRange[0] = initRedRange[0];
501         redRange[1] = initRedRange[1];
502         redLowTxtFld.setText(Float.toString((float)redRange[0]));
503         redHighTxtFld.setText(Float.toString((float)redRange[1]));
504       }
505     });
506
507     JPanel grnPanel = new JPanel(new FlowLayout());
508     grnPanel.add(new JLabel("Green range: "));
509   
510     grnLowTxtFld.addActionListener(new ActionListener() {
511         public void actionPerformed(ActionEvent e) {
512            String tmp = grnLowTxtFld.getText().trim();
513            updateGrnRange(Double.valueOf(tmp), grnRange[1]);
514         }
515     });
516     grnPanel.add(grnLowTxtFld);
517     grnHighTxtFld.addActionListener(new ActionListener() {
518         public void actionPerformed(ActionEvent e) {
519            String tmp = grnHighTxtFld.getText().trim();
520            updateGrnRange(grnRange[0], Double.valueOf(tmp));
521         }
522     });
523     grnPanel.add(grnHighTxtFld);
524
525     grnGammaTxtFld.addActionListener(new ActionListener() {
526         public void actionPerformed(ActionEvent e) {
527             String tmp = grnGammaTxtFld.getText().trim();
528             updateGrnGamma(Double.valueOf(tmp));
529         }
530     });
531     grnPanel.add(new JLabel("Gamma:"));
532     grnPanel.add(grnGammaTxtFld);
533
534
535     button = new JButton("reset");
536     grnPanel.add(button);
537     button.addActionListener(new ActionListener() {
538       public void actionPerformed(ActionEvent e) {
539         updateGrnRange(initGrnRange[0], initGrnRange[1]);
540         grnRange[0] = initGrnRange[0];
541         grnRange[1] = initGrnRange[1];
542         grnLowTxtFld.setText(Float.toString((float)grnRange[0]));
543         grnHighTxtFld.setText(Float.toString((float)grnRange[1]));
544       }
545     });
546
547
548
549     JPanel bluPanel = new JPanel(new FlowLayout());
550     bluPanel.add(new JLabel("Blue range: "));
551   
552     bluLowTxtFld.addActionListener(new ActionListener() {
553         public void actionPerformed(ActionEvent e) {
554            String tmp = bluLowTxtFld.getText().trim();
555            updateBluRange(Double.valueOf(tmp), bluRange[1]);
556         }
557     });
558     bluPanel.add(bluLowTxtFld);
559     bluHighTxtFld.addActionListener(new ActionListener() {
560         public void actionPerformed(ActionEvent e) {
561            String tmp = bluHighTxtFld.getText().trim();
562            updateBluRange(bluRange[0], Double.valueOf(tmp));
563         }
564     });
565     bluPanel.add(bluHighTxtFld);
566
567     bluGammaTxtFld.addActionListener(new ActionListener() {
568         public void actionPerformed(ActionEvent e) {
569             String tmp = bluGammaTxtFld.getText().trim();
570             updateBluGamma(Double.valueOf(tmp));
571         }
572     });
573     bluPanel.add(new JLabel("Gamma:"));
574     bluPanel.add(bluGammaTxtFld);
575
576     button = new JButton("reset");
577     bluPanel.add(button);
578     button.addActionListener(new ActionListener() {
579       public void actionPerformed(ActionEvent e) {
580         updateBluRange(initBluRange[0], initBluRange[1]);
581         bluRange[0] = initBluRange[0];
582         bluRange[1] = initBluRange[1];
583         bluLowTxtFld.setText(Float.toString((float)bluRange[0]));
584         bluHighTxtFld.setText(Float.toString((float)bluRange[1]));
585       }
586     });
587
588
589     subPanel.add(redPanel);
590     subPanel.add(grnPanel);
591     subPanel.add(bluPanel);
592     subPanel.add(gammaPanel);
593
594     bigPanel.add(subPanel, BorderLayout.NORTH);
595
596     return bigPanel;
597   }
598
599  private class ColorMapListener implements ScalarMapListener
600  {
601    ScalarMap clrMap;
602
603    double[] range = null;
604    double[] initRange = null;
605
606    JTextField lowTxtFld;
607    JTextField highTxtFld;
608
609    ColorMapListener(ScalarMap clrMap, double[] initRange, double[] range, JTextField lowTxtFld, JTextField highTxtFld) {
610      this.clrMap = clrMap;
611      this.lowTxtFld = lowTxtFld;
612      this.highTxtFld = highTxtFld;
613      this.range = range;
614      this.initRange = initRange;
615    }
616
617
618    public void controlChanged(ScalarMapControlEvent event) throws RemoteException, VisADException {
619    }
620
621    public void mapChanged(ScalarMapEvent event) throws RemoteException, VisADException {       
622      if (event.getId() == event.AUTO_SCALE) {
623            double[] rng = clrMap.getRange();
624            boolean shouldRemove = false;
625            //Ghansham: decide whether it is first time. The cleaner way
626            if (!Double.isNaN(rng[0]) && !Double.isNaN(rng[1]) && Double.isNaN(initRange[0]) && Double.isNaN(initRange[1])) {
627                shouldRemove = true;
628            }
629            range[0] = rng[0];
630            range[1] = rng[1];
631            initRange[0] = rng[0];
632            initRange[1] = rng[1];
633            lowTxtFld.setText(Float.toString((float)rng[0]));
634            highTxtFld.setText(Float.toString((float)rng[1]));
635            //Ghansham:If its first time remove the scalarmaplistener and setRange manually to disable autscaling of the scalarmap
636            if(shouldRemove) {
637                clrMap.removeScalarMapListener(this);
638            //-Lock out auto-scaling
639                clrMap.disableAutoScale();
640            }
641      }
642      else if (event.getId() == event.MANUAL) {
643            double[] rng = clrMap.getRange();
644            range[0] = rng[0];
645            range[1] = rng[1];
646      }
647    }
648  }
649
650
651}