001    /*
002     * $Id: ScatterDisplay.java,v 1.61 2012/04/10 19:34:07 rink Exp $
003     *
004     * This file is part of McIDAS-V
005     *
006     * Copyright 2007-2012
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    
031    
032    package edu.wisc.ssec.mcidasv.control;
033    
034    import java.awt.BorderLayout;
035    import java.awt.Color;
036    import java.awt.Component;
037    import java.awt.Container;
038    import java.awt.Dimension;
039    import java.awt.FlowLayout;
040    import java.awt.GridLayout;
041    import java.awt.event.ActionEvent;
042    import java.awt.event.ActionListener;
043    import java.awt.event.WindowEvent;
044    import java.awt.event.WindowAdapter;
045    import java.awt.geom.Rectangle2D;
046    import java.net.URL;
047    import java.rmi.RemoteException;
048    import java.util.ArrayList;
049    import java.util.List;
050    import java.io.PrintWriter;
051    import java.io.File;
052    
053    import javax.swing.JFrame;
054    import javax.swing.ButtonGroup;
055    import javax.swing.ImageIcon;
056    import javax.swing.JComponent;
057    import javax.swing.JPanel;
058    import javax.swing.JRadioButton;
059    import javax.swing.JToggleButton;
060    import javax.swing.JButton;
061    import javax.swing.JScrollPane;
062    import javax.swing.JTable;
063    import javax.swing.JFileChooser;
064    import javax.swing.filechooser.FileFilter;
065    import javax.swing.filechooser.FileNameExtensionFilter;
066    import javax.swing.table.AbstractTableModel;
067    import javax.swing.table.TableCellRenderer;
068    import javax.swing.border.CompoundBorder;
069    import javax.swing.border.EmptyBorder;
070    import javax.swing.border.LineBorder;
071    
072    import org.slf4j.Logger;
073    import org.slf4j.LoggerFactory;
074    
075    import visad.AxisScale;
076    import visad.BaseColorControl;
077    import visad.CellImpl;
078    import visad.CoordinateSystem;
079    import visad.Data;
080    import visad.DelaunayCustom;
081    import visad.DisplayEvent;
082    import visad.DisplayListener;
083    import visad.Real;
084    import visad.FieldImpl;
085    import visad.FlatField;
086    import visad.FunctionType;
087    import visad.Gridded2DSet;
088    import visad.Gridded3DSet;
089    import visad.Integer1DSet;
090    import visad.Linear2DSet;
091    import visad.LinearLatLonSet;
092    import visad.RealTupleType;
093    import visad.MathType;
094    import visad.RealType;
095    import visad.SampledSet;
096    import visad.ScalarMap;
097    import visad.Set;
098    import visad.SetType;
099    import visad.UnionSet;
100    import visad.VisADException;
101    import visad.data.mcidas.BaseMapAdapter;
102    import visad.georef.MapProjection;
103    import visad.georef.TrivialMapProjection;
104    import visad.python.JPythonMethods;
105    
106    import ucar.unidata.data.DataAlias;
107    import ucar.unidata.data.DataChoice;
108    import ucar.unidata.data.DataSelection;
109    import ucar.unidata.data.grid.GridUtil;
110    import ucar.unidata.idv.DisplayConventions;
111    import ucar.unidata.idv.control.ColorTableWidget;
112    import ucar.unidata.idv.control.DisplayControlImpl;
113    import ucar.unidata.ui.colortable.ColorTableManager;
114    import ucar.unidata.util.ColorTable;
115    import ucar.unidata.util.LogUtil;
116    import ucar.unidata.util.Range;
117    import ucar.unidata.view.geoloc.MapProjectionDisplay;
118    import ucar.unidata.view.geoloc.MapProjectionDisplayJ3D;
119    import ucar.visad.display.DisplayMaster;
120    import ucar.visad.display.LineDrawing;
121    import ucar.visad.display.MapLines;
122    import ucar.visad.display.RGBDisplayable;
123    import ucar.visad.display.RubberBandBox;
124    import ucar.visad.display.XYDisplay;
125    
126    import edu.wisc.ssec.mcidasv.data.hydra.CurveDrawer;
127    import edu.wisc.ssec.mcidasv.data.hydra.HistogramField;
128    import edu.wisc.ssec.mcidasv.data.hydra.HydraRGBDisplayable;
129    import edu.wisc.ssec.mcidasv.data.hydra.MultiSpectralData;
130    import edu.wisc.ssec.mcidasv.data.hydra.SubsetRubberBandBox;
131    import edu.wisc.ssec.mcidasv.data.hydra.LongitudeLatitudeCoordinateSystem;
132    import edu.wisc.ssec.mcidasv.data.hydra.Statistics;
133    
134    public class ScatterDisplay extends DisplayControlImpl {
135    
136            private static final Logger logger = LoggerFactory.getLogger(ScatterDisplay.class);
137            
138            private Container container;
139        private FlatField X_field;
140        private FlatField Y_field;
141        private FlatField Area_field;
142        private double total_area;
143        private DisplayMaster scatterMaster = null;
144    
145        private DisplayMaster dspMasterX;
146        private DisplayMaster dspMasterY;
147    
148        private HistogramField histoField;
149    
150        private FlatField mask_field;
151        private float[][] mask_range;
152        private float[][] scatterFieldRange;
153        private Data X_data;
154        private Data Y_data;
155        private String X_name;
156        private String Y_name;
157        
158        private boolean cancel = false;
159    
160        private ScatterDisplayable scatterMarkDsp;
161    
162        private BoxCurveSwitch boxCurveSwitch;
163    
164        public DataChoice dataChoiceX = null;
165    
166        public DataChoice dataChoiceY = null;
167    
168        public DataSelection dataSelectionX = null;
169    
170        public DataSelection dataSelectionY = null;
171    
172        JComponent ctwCompX;
173    
174        JComponent ctwCompY;
175    
176        ColorTableWidget ctw;
177    
178        int n_selectors = 3;
179    
180        List<ScatterBoxSelector> scatterBoxSelectors = new ArrayList<ScatterBoxSelector>();
181    
182        List<ScatterCurveSelector> scatterCurveSelectors = new ArrayList<ScatterCurveSelector>();
183    
184        List<ImageBoxSelector> imageXBoxSelectors = new ArrayList<ImageBoxSelector>();
185    
186        List<ImageBoxSelector> imageYBoxSelectors = new ArrayList<ImageBoxSelector>();
187    
188        List<ImageCurveSelector> imageXCurveSelectors = new ArrayList<ImageCurveSelector>();
189    
190        List<ImageCurveSelector> imageYCurveSelectors = new ArrayList<ImageCurveSelector>();
191    
192        JToggleButton[] selectorToggleButtons = new JToggleButton[n_selectors];
193        Color[] selectorColors = new Color[] {Color.magenta, Color.green, Color.blue};
194        float[][] maskColorPalette = new float[][] {{0.8f,0f,0f},{0f,0.8f,0f},{0.8f,0f,0.8f}};
195        float[][] markColorPalette = new float[][] {{1f,0.8f,0f,0f},{1f,0f,0.8f,0f},{1f,0.8f,0f,0.8f}};
196    
197        JButton computeStatsButton;
198        MyStatsTable statsTable;
199       
200        boolean selectByCurve = false;
201    
202        public ScatterDisplay() {
203          super();
204          setHelpUrl("idv.controls.misc.scatteranalysiscontrol");
205        }
206        
207    
208        @Override public boolean init(List choices) throws VisADException, RemoteException {
209            if ((dataChoiceX != null) && (dataChoiceY != null)) {
210              setupFromUnpersistence();
211            }
212            else {
213                            try {
214                                    setup();
215                            } catch (VisADException vade) {
216                                    return false;
217                            }
218            }
219    
220            mask_field = new FlatField(
221                 new FunctionType(((FunctionType)X_field.getType()).getDomain(), RealType.Generic),
222                      X_field.getDomainSet());
223    
224            int len = X_field.getDomainSet().getLength();
225            mask_range = new float[1][len];
226            for (int t=0; t<len; t++) {
227              mask_range[0][t] = Float.NaN;
228            }
229            mask_range[0][0] = 0; //- field should not be all missing
230            mask_field.setSamples(mask_range, false);
231                                                                                                                                                      
232            try {
233              histoField = new HistogramField(X_field, Y_field, mask_field, 100, 10);
234            }
235            catch (Exception e) {
236              e.printStackTrace();
237            }
238    
239            Range rangeX = getImageRange(X_field);
240            Range rangeY = getImageRange(Y_field);
241            ColorTable clrTableX = getColorTable(X_field);
242            ColorTable clrTableY = getColorTable(Y_field);
243    
244            dspMasterX = makeImageDisplay(getDataProjection(X_field), X_field, mask_field, 
245                             rangeX, clrTableX);
246    
247            dspMasterY = makeImageDisplay(getDataProjection(Y_field), Y_field, mask_field, 
248                             rangeY, clrTableY);
249    
250            dspMasterX.addDisplayListener(new DisplayListener() {
251                @Override public void displayChanged(final DisplayEvent e) {
252                    double[] xProjection = dspMasterX.getProjectionMatrix();
253                    double[] yProjection = dspMasterY.getProjectionMatrix();
254                    if (xProjection.equals(yProjection))
255                        return;
256    
257                    try {
258                        dspMasterY.setProjectionMatrix(xProjection);
259                    } catch (Exception ex) {
260                        LogUtil.logException("dspMasterX.displayChanged", ex);
261                    }
262                }
263            });
264    
265            dspMasterY.addDisplayListener(new DisplayListener() {
266                @Override public void displayChanged(final DisplayEvent e) {
267                    double[] xProjection = dspMasterX.getProjectionMatrix();
268                    double[] yProjection = dspMasterY.getProjectionMatrix();
269                    if (yProjection.equals(xProjection))
270                        return;
271    
272                    try {
273                        dspMasterX.setProjectionMatrix(yProjection);
274                    } catch (Exception ex) {
275                        LogUtil.logException("dspMasterX.displayChanged", ex);
276                    }
277                }
278            });
279    
280            X_name = ((((FunctionType)X_field.getType()).getFlatRange().getRealComponents())[0]).getName();
281            Y_name = ((((FunctionType)Y_field.getType()).getFlatRange().getRealComponents())[0]).getName();
282    
283            if (statsTable != null) statsTable.setNames(X_name, Y_name);
284    
285            Grid2DReadoutProbe probeX = new Grid2DReadoutProbe(X_field, dspMasterX);
286            Grid2DReadoutProbe probeY = new Grid2DReadoutProbe(Y_field, dspMasterY);
287            probeX.doMakeProbe(Color.red, dspMasterX);
288            probeY.doMakeProbe(Color.red, dspMasterY);
289            
290            ImageControl dCntrl = new ImageControl((HydraRGBDisplayable)dspMasterX.getDisplayables(0), getDisplayConventions());
291            ctw = new ColorTableWidget(dCntrl, ColorTableManager.getManager(), clrTableX, rangeX);
292            ctwCompX = ctw.getLegendPanel(BOTTOM_LEGEND);
293            dCntrl.ctw = ctw;
294    
295            dCntrl = new ImageControl((HydraRGBDisplayable)dspMasterY.getDisplayables(0), getDisplayConventions());
296            ctw = new ColorTableWidget(dCntrl, ColorTableManager.getManager(), clrTableY, rangeY);
297            ctwCompY = ctw.getLegendPanel(BOTTOM_LEGEND);
298            dCntrl.ctw = ctw;
299    
300            return true;
301        }
302    
303        public void setup() throws VisADException, RemoteException {
304            dataSelectionX = getDataSelection();
305            dataChoiceX = getDataChoice();
306            X_data = dataChoiceX.getData(dataSelectionX);
307    
308            if (X_data instanceof FlatField) {
309              X_field = (FlatField) X_data;
310            } else if (X_data instanceof FieldImpl) {
311              X_field = (FlatField) ((FieldImpl)X_data).getSample(0);
312            }
313    
314            popupDataDialog("select Y Axis field", container, false, null);
315            
316            // if user canceled the popup, popupDataDialog will set the cancel flag
317            if (cancel) throw new VisADException("Scatter Display Canceled");
318    
319            dataSelectionY = getDataSelection();
320            dataChoiceY = getDataChoice();
321    
322            dataSelectionY.setGeoSelection(dataSelectionX.getGeoSelection());
323                                                                                                                                                      
324            Y_data = dataChoiceY.getData(dataSelectionY);
325    
326            if (Y_data instanceof FlatField) {
327              Y_field = (FlatField) Y_data;
328            } else if (Y_data instanceof FieldImpl) {
329              Y_field = (FlatField) ((FieldImpl)Y_data).getSample(0);
330            }
331    
332            if (!( X_field.getDomainSet().equals(Y_field.getDomainSet())))
333            {
334              Y_field = resample(X_field, Y_field);
335            }
336    
337            Area_field = JPythonMethods.createAreaField(X_field);
338            statsTable = new MyStatsTable();
339            
340        }
341    
342        public void setupFromUnpersistence() throws VisADException, RemoteException {
343            X_data = dataChoiceX.getData(dataSelectionX);
344            if (X_data instanceof FlatField) {
345              X_field = (FlatField) X_data;
346            } else if (X_data instanceof FieldImpl) {
347              X_field = (FlatField) ((FieldImpl)X_data).getSample(0);
348            }
349                                                                                                                                                      
350            Y_data = dataChoiceY.getData(dataSelectionY);
351            if (Y_data instanceof FlatField) {
352              Y_field = (FlatField) Y_data;
353            } else if (X_data instanceof FieldImpl) {
354              Y_field = (FlatField) ((FieldImpl)Y_data).getSample(0);
355            }
356        }
357    
358        @Override protected void popupDataDialog(final String dialogMessage,
359                                       Component from, boolean multiples,
360                                       List categories) {
361    
362            List<DataChoice> choices = selectDataChoices(dialogMessage, from,
363                                           multiples, categories);
364            if ((choices == null) || (choices.size() == 0)) {
365                logger.debug("popupDataDialog, no data choice, user canceled");
366                    cancel = true;
367                return;
368            }
369            final List clonedList =
370                DataChoice.cloneDataChoices((List)choices.get(0));
371            dataSelection = ((DataChoice) clonedList.get(0)).getDataSelection();
372            //- don't do this in a separate thread like the IDV does.
373            //- We want the dataChoice list updated before return.
374            try {
375              addNewData(clonedList);
376            } catch (Exception exc) {
377              logException("Selecting new data", exc);
378            }
379        }
380    
381    
382        @Override public void initDone() {
383           try {
384             DisplayMaster master = makeScatterDisplay();
385             for (int k=0; k<n_selectors; k++) {
386               scatterBoxSelectors.add(new ScatterBoxSelector(master, selectorColors[k], (float)k));
387               scatterCurveSelectors.add(new ScatterCurveSelector(master, selectorColors[k], (float)k));
388             }
389             master.draw();
390    
391             for (int k=0; k<n_selectors; k++) {
392               SubsetRubberBandBox X_subsetBox =
393                  new SubsetRubberBandBox(getIsLatLon(X_field), X_field,
394                          ((MapProjectionDisplayJ3D)dspMasterX).getDisplayCoordinateSystem(), 1, false);
395               X_subsetBox.setColor(selectorColors[k]);
396    
397               ImageBoxSelector markX = new ImageBoxSelector(X_subsetBox, X_field.getDomainSet(), dspMasterX, selectorColors[k], (float)k+1, statsTable);
398    
399               SubsetRubberBandBox Y_subsetBox =
400                  new SubsetRubberBandBox(getIsLatLon(Y_field), Y_field,
401                     ((MapProjectionDisplayJ3D)dspMasterY).getDisplayCoordinateSystem(), 1, false);
402               Y_subsetBox.setColor(selectorColors[k]);
403               ImageBoxSelector markY = new ImageBoxSelector(Y_subsetBox, Y_field.getDomainSet(), dspMasterY, selectorColors[k], (float)k+1, statsTable);
404    
405               markX.setOther(markY);
406               markY.setOther(markX);
407               imageXBoxSelectors.add(markX);
408               imageYBoxSelectors.add(markY);
409             }
410    
411             for (int k=0; k<n_selectors; k++) {
412               CurveDrawer curveDraw = new CurveDrawer(RealType.Longitude, RealType.Latitude, 1);
413               curveDraw.setColor(selectorColors[k]);
414               curveDraw.setLineWidth(2);
415               ImageCurveSelector curveX = new ImageCurveSelector(curveDraw, X_field, dspMasterX, selectorColors[k], (float) k+1, statsTable);
416               curveX.setActive(false);
417               curveDraw.addAction(curveX);
418               curveX.setVisible(false);
419               dspMasterX.addDisplayable(curveDraw);
420    
421               curveDraw = new CurveDrawer(RealType.Longitude, RealType.Latitude, 1);
422               curveDraw.setColor(selectorColors[k]);
423               curveDraw.setLineWidth(2);
424               ImageCurveSelector curveY = new ImageCurveSelector(curveDraw, Y_field, dspMasterY, selectorColors[k], (float) k+1, statsTable);
425               curveY.setActive(false);
426               curveDraw.addAction(curveY);
427               curveY.setVisible(false);
428               dspMasterY.addDisplayable(curveDraw);
429    
430               curveX.setOther(curveY);
431               curveY.setOther(curveX);
432               imageXCurveSelectors.add(curveX);
433               imageYCurveSelectors.add(curveY);
434             }
435    
436             for (int k=0; k<n_selectors; k++) {
437               JToggleButton jtog = selectorToggleButtons[k];
438              
439               jtog.addActionListener(new ActionListener() {
440                   @Override public void actionPerformed(ActionEvent e) {
441                      int idx = Integer.valueOf(e.getActionCommand());
442                      try {
443                        for (int i=0; i<n_selectors; i++) {
444                          ScatterBoxSelector boxSel = (ScatterBoxSelector) scatterBoxSelectors.get(i);
445                          ImageBoxSelector imageXbox = (ImageBoxSelector) imageXBoxSelectors.get(i);
446                          ImageBoxSelector imageYbox = (ImageBoxSelector) imageYBoxSelectors.get(i);
447                          ScatterCurveSelector curveSel = (ScatterCurveSelector) scatterCurveSelectors.get(i);
448                          ImageCurveSelector imageXcurve = (ImageCurveSelector) imageXCurveSelectors.get(i);
449                          ImageCurveSelector imageYcurve = (ImageCurveSelector) imageYCurveSelectors.get(i);
450                        
451                          if (i == idx) {
452                            if (!selectorToggleButtons[i].isSelected()) {
453    
454                              if (statsTable != null) statsTable.resetValues(i);
455    
456                              boxSel.setActive(false);
457                              boxSel.setVisible(false);
458                              boxSel.reset();
459                              imageXbox.setActive(false);
460                              imageXbox.setVisible(false);
461                              imageXbox.reset();
462                              imageYbox.setActive(false);
463                              imageYbox.setVisible(false);
464                              imageYbox.reset();
465    
466                              curveSel.setActive(false);
467                              curveSel.setVisible(false);
468                              curveSel.reset();
469                              imageXcurve.setActive(false);
470                              imageXcurve.setVisible(false);
471                              imageXcurve.reset();
472                              imageYcurve.setActive(false);
473                              imageYcurve.setVisible(false);
474                              imageYcurve.reset();
475    
476    
477                            }
478                            boxSel.setActive(!getSelectByCurve());
479                            boxSel.setVisible(!getSelectByCurve());
480                            imageXbox.setActive(!getSelectByCurve());
481                            imageXbox.setVisible(!getSelectByCurve());
482                            imageYbox.setActive(!getSelectByCurve());
483                            imageYbox.setVisible(!getSelectByCurve());
484    
485                            curveSel.setActive(getSelectByCurve());
486                            curveSel.setVisible(getSelectByCurve());
487                            imageXcurve.setActive(getSelectByCurve());
488                            imageXcurve.setVisible(getSelectByCurve());
489                            imageYcurve.setActive(getSelectByCurve());
490                            imageYcurve.setVisible(getSelectByCurve());
491                          }
492                          else {
493                            selectorToggleButtons[i].setSelected(false);
494                            boxSel.setActive(false);
495                            boxSel.setVisible(false);
496                            imageXbox.setActive(false);
497                            imageXbox.setVisible(false);
498                            imageYbox.setActive(false);
499                            imageYbox.setVisible(false);
500                            curveSel.setActive(false);
501                            curveSel.setVisible(false);
502                            imageXcurve.setActive(false);
503                            imageXcurve.setVisible(false);
504                            imageYcurve.setActive(false);
505                            imageYcurve.setVisible(false);
506                          }
507                        }
508                      }
509                      catch (Exception exc) {
510                        System.out.println(exc);
511                      }
512                   }});
513    
514               ScatterBoxSelector boxSel = (ScatterBoxSelector) scatterBoxSelectors.get(k);
515               ImageBoxSelector imageXbox = (ImageBoxSelector) imageXBoxSelectors.get(k);
516               ImageBoxSelector imageYbox = (ImageBoxSelector) imageYBoxSelectors.get(k);
517               ScatterCurveSelector curveSel = (ScatterCurveSelector) scatterCurveSelectors.get(k);
518               ImageCurveSelector imageXcurve = (ImageCurveSelector) imageXCurveSelectors.get(k);
519               ImageCurveSelector imageYcurve = (ImageCurveSelector) imageYCurveSelectors.get(k);
520    
521               if (k == 0) {
522                  jtog.setSelected(true);
523                  boxSel.setActive(!getSelectByCurve());
524                  boxSel.setVisible(!getSelectByCurve());
525                  imageXbox.setActive(!getSelectByCurve());
526                  imageXbox.setVisible(!getSelectByCurve());
527                  imageYbox.setActive(!getSelectByCurve());
528                  imageYbox.setVisible(!getSelectByCurve());
529    
530                  curveSel.setActive(getSelectByCurve());
531                  curveSel.setVisible(getSelectByCurve());
532                  imageXcurve.setActive(getSelectByCurve());
533                  imageXcurve.setVisible(getSelectByCurve());
534                  imageYcurve.setActive(getSelectByCurve());
535                  imageYcurve.setVisible(getSelectByCurve());
536                }
537                else {
538                  boxSel.setActive(false);
539                  boxSel.setVisible(false);
540                  imageXbox.setActive(false);
541                  imageXbox.setVisible(false);
542                  imageYbox.setActive(false);
543                  imageYbox.setVisible(false);
544                  curveSel.setActive(false);
545                  curveSel.setVisible(false);
546                  imageXcurve.setActive(false);
547                  imageXcurve.setVisible(false);
548                  imageYcurve.setActive(false);
549                  imageYcurve.setVisible(false);
550               }
551             }
552           }
553           catch (Exception e) {
554             e.printStackTrace();
555           }
556        }
557        
558        public DisplayMaster makeScatterDisplay() throws VisADException, RemoteException {
559    
560           ScatterDisplayable scatterDsp = new ScatterDisplayable("scatter",
561                       RealType.getRealType("mask"), markColorPalette, false);
562           float[] valsX = X_field.getFloats(false)[0];
563           float[] valsY = Y_field.getFloats(false)[0];
564           Integer1DSet set = new Integer1DSet(valsX.length);
565           FlatField scatter = new FlatField(
566               new FunctionType(RealType.Generic,
567                   new RealTupleType(RealType.XAxis, RealType.YAxis, RealType.getRealType("mask"))), set);
568           float[] mask = new float[valsX.length];
569           for (int k=0; k<mask.length; k++) mask[k] = 0;
570           scatterFieldRange = new float[][] {valsX, valsY, mask};
571           scatter.setSamples(scatterFieldRange);
572           scatterDsp.setPointSize(2f);
573           scatterDsp.setRangeForColor(0,n_selectors);
574    
575           float[] xRange = minmax(valsX);
576           float[] yRange = minmax(valsY);
577           
578           scatterDsp.setData(scatter);
579                                                                                                                                                      
580           scatterMarkDsp = new ScatterDisplayable("scatter",
581                       RealType.getRealType("mask"), markColorPalette, false);
582           set = new Integer1DSet(2);
583           scatter = new FlatField(
584               new FunctionType(RealType.Generic,
585                   new RealTupleType(RealType.XAxis, RealType.YAxis, RealType.getRealType("mask"))), set);
586           scatterMarkDsp.setData(scatter);
587           scatterMarkDsp.setPointSize(2f);
588           scatterMarkDsp.setRangeForColor(0,n_selectors);
589    
590           DisplayMaster master = scatterMaster;
591           ((XYDisplay)master).showAxisScales(true);
592           AxisScale scaleX = ((XYDisplay)master).getXAxisScale();
593           scaleX.setTitle(X_name);
594           AxisScale scaleY = ((XYDisplay)master).getYAxisScale();
595           scaleY.setTitle(Y_name);
596    
597           ((XYDisplay)master).setXRange((double)xRange[0], (double)xRange[1]);
598           ((XYDisplay)master).setYRange((double)yRange[0], (double)yRange[1]);
599           master.addDisplayable(scatterDsp);
600           master.addDisplayable(scatterMarkDsp);
601    
602           return master;
603        }
604    
605    
606        @Override public Container doMakeContents() {
607            JPanel pane = new JPanel(new GridLayout(1,3));
608    
609            Component[] comps = new Component[] {null, null, null};
610            comps[0] = dspMasterX.getComponent();
611            comps[1] = dspMasterY.getComponent();
612            comps[2] = getScatterTabComponent();
613    
614            JPanel panelX = new JPanel(new BorderLayout());
615            panelX.setBorder(new EmptyBorder(4,4,4,4));
616            panelX.add(comps[0], BorderLayout.CENTER);
617            panelX.add(ctwCompX, BorderLayout.SOUTH);
618    
619            JPanel panelY = new JPanel(new BorderLayout());
620            panelY.setBorder(new EmptyBorder(4,4,4,4));
621            panelY.add(comps[1], BorderLayout.CENTER);
622            panelY.add(ctwCompY, BorderLayout.SOUTH);
623    
624            JPanel panelS = new JPanel(new BorderLayout());
625            panelS.setBorder(new EmptyBorder(4,4,4,4));
626            panelS.add(comps[2], BorderLayout.CENTER);
627    
628            pane.add(panelX);
629            pane.add(panelY);
630            pane.add(panelS);
631    
632            
633            JPanel buttonPanel = new JPanel();
634            buttonPanel.setLayout(new FlowLayout());
635            JRadioButton boxSelect = new JRadioButton("Box");
636            boxSelect.setSelected(true);
637            JRadioButton curveSelect = new JRadioButton("Curve");
638            ButtonGroup buttonGroup = new ButtonGroup();
639            buttonGroup.add(boxSelect);
640            buttonGroup.add(curveSelect);
641            buttonPanel.add(boxSelect);
642            buttonPanel.add(curveSelect);
643    
644            boxCurveSwitch = new BoxCurveSwitch();
645            boxSelect.addActionListener(boxCurveSwitch);
646            curveSelect.addActionListener(boxCurveSwitch);
647            
648    
649            JPanel toggleButtonPanel = new JPanel(new FlowLayout());
650            for (int k=0; k<n_selectors; k++) {
651              JToggleButton jtog = 
652                 new JToggleButton(
653                     new ImageIcon(getClass().getResource("/edu/wisc/ssec/mcidasv/resources/icons/buttons/subset12.jpg")));
654              jtog.setBorder(new CompoundBorder(new LineBorder(selectorColors[k],2), new EmptyBorder(4,4,4,4)));
655              jtog.setActionCommand(String.valueOf(k));
656              toggleButtonPanel.add(jtog);
657              selectorToggleButtons[k] = jtog;
658            }
659    
660            buttonPanel.add(toggleButtonPanel);
661    
662            JButton computeStatsButton = new JButton("compute statistics");
663    
664            computeStatsButton.addActionListener(new ActionListener() {
665                public void actionPerformed(final ActionEvent e) {
666    
667                   if (statsTable == null) {
668                     statsTable = new MyStatsTable();
669                   }
670    
671                   statsTable.setIsShowing();
672                   statsTable.setFields(X_field, Y_field,0);
673                }
674            });
675    
676            buttonPanel.add(computeStatsButton);
677    
678            //-container = pane;
679            JPanel new_pane = new JPanel(new BorderLayout());
680            new_pane.add(pane, BorderLayout.CENTER);
681            new_pane.add(buttonPanel, BorderLayout.SOUTH);
682            container = new_pane;
683            return container;
684        }
685    
686    
687        protected Component getScatterTabComponent() {
688           try {
689             scatterMaster = new XYDisplay("Scatter", RealType.XAxis, RealType.YAxis);
690           } catch (Exception e) {
691             e.printStackTrace();
692           }
693           return scatterMaster.getComponent();
694        }
695    
696        public DisplayMaster makeImageDisplay(MapProjection mapProj, FlatField image, 
697                      FlatField mask_image, Range imageRange, ColorTable colorTable) 
698               throws VisADException, RemoteException {
699          MapProjectionDisplayJ3D mapProjDsp;
700          DisplayMaster dspMaster;
701    
702          mapProjDsp = new MapProjectionDisplayJ3D(MapProjectionDisplay.MODE_2Din3D);
703          mapProjDsp.enableRubberBanding(false);
704          dspMaster = mapProjDsp;
705          mapProjDsp.setMapProjection(mapProj);
706    
707          RealType imageRangeType =
708            (((FunctionType)image.getType()).getFlatRange().getRealComponents())[0];
709    
710          boolean alphaflag = false;
711          HydraRGBDisplayable imageDsp = new HydraRGBDisplayable("image", imageRangeType, null, alphaflag, null);
712    
713          imageDsp.setData(image);
714          dspMaster.addDisplayable(imageDsp);
715          addMapDisplayables(mapProjDsp);
716    
717          if (mask_image != null) {
718            RGBDisplayable maskDsp = 
719                new ScatterDisplayable("mask", RealType.Generic, maskColorPalette, false);
720            maskDsp.setData(mask_image);
721            maskDsp.setRangeForColor(0, n_selectors-1);
722            dspMaster.addDisplayable(maskDsp);
723          }
724    
725          dspMaster.draw();
726    
727          ScalarMap colorMap = imageDsp.getColorMap();
728          colorMap.setRange(imageRange.getMin(), imageRange.getMax());
729          BaseColorControl clrCntrl = (BaseColorControl) colorMap.getControl();
730          float[][] ct = colorTable.getColorTable();
731    
732          if ( !(alphaflag) && (ct.length == 4) ) {
733             float[][] new_ct = new float[3][];
734             new_ct[0] = ct[0];
735             new_ct[1] = ct[1];
736             new_ct[2] = ct[2];
737             ct = new_ct;
738          }
739    
740          clrCntrl.setTable(ct);
741    
742          return dspMaster;
743        }
744    
745        public Range getImageRange(FlatField image)
746               throws VisADException, RemoteException {
747          DisplayConventions dc = getDisplayConventions();
748          Range[] range = GridUtil.fieldMinMax(image);
749          Range imageRange = range[0];
750          RealType imageRangeType =
751            (((FunctionType)image.getType()).getFlatRange().getRealComponents())[0];
752          String canonicalName = DataAlias.aliasToCanonical(imageRangeType.getName());
753          Range dfltRange = dc.getParamRange(canonicalName, null);
754    
755          if (dfltRange == null) {
756            imageRange = range[0];
757          }
758          else if ((imageRange.getMax() - imageRange.getMin()) < (dfltRange.getMax() - dfltRange.getMin())) {
759          }
760          else {
761            imageRange = dfltRange;
762          }
763          return imageRange;
764        }
765    
766        public ColorTable getColorTable(FlatField image) 
767               throws VisADException, RemoteException {
768          RealType imageRangeType =
769            (((FunctionType)image.getType()).getFlatRange().getRealComponents())[0];
770          DisplayConventions dc = getDisplayConventions();
771          return dc.getParamColorTable(imageRangeType.getName());
772        }
773    
774    
775        public MapProjection getDataProjection(FlatField image) 
776               throws VisADException, RemoteException {
777          MapProjection mp = null;
778          //- get MapProjection from incoming image.  If none, use default method
779          FunctionType fnc_type = (FunctionType) image.getType();
780          RealTupleType rtt = fnc_type.getDomain();
781          CoordinateSystem cs = rtt.getCoordinateSystem();
782          Set domainSet = image.getDomainSet();
783    
784          if (cs instanceof visad.CachingCoordinateSystem) {
785            cs = ((visad.CachingCoordinateSystem)cs).getCachedCoordinateSystem();
786          }
787    
788          if (cs instanceof MapProjection) {
789            return (MapProjection) cs;
790          }
791          else if (cs instanceof LongitudeLatitudeCoordinateSystem) {
792            Rectangle2D rect = MultiSpectralData.getLonLatBoundingBox(image);
793            try {
794              mp = new LambertAEA(rect);
795            } catch (Exception e) {
796              System.out.println(" getDataProjection"+e);
797            }
798            return mp;
799          }
800    
801          float minLon = Float.NaN;
802          float minLat = Float.NaN;
803          float delLon = Float.NaN;
804          float delLat = Float.NaN;
805    
806          if (domainSet instanceof LinearLatLonSet) {
807             MathType type0 = ((SetType)domainSet.getType()).getDomain().getComponent(0);
808             int latI = RealType.Latitude.equals(type0) ? 0 : 1;
809             int lonI = (latI == 1) ? 0 : 1;
810    
811             float[] min = ((LinearLatLonSet)domainSet).getLow();
812             float[] max = ((LinearLatLonSet)domainSet).getHi();
813             minLon = min[lonI];
814             minLat = min[latI];
815             delLon = max[lonI] - min[lonI];
816             delLat = max[latI] - min[latI];
817    
818             try {
819                mp = new TrivialMapProjection(RealTupleType.SpatialEarth2DTuple,
820                        new Rectangle2D.Float(minLon, minLat, delLon, delLat));
821             } catch (Exception e) {
822                 logException("MultiSpectralControl.getDataProjection", e);
823             }
824    
825             return mp;
826          }
827          else if (domainSet instanceof Gridded2DSet) {
828            rtt = ((SetType)domainSet.getType()).getDomain();
829            rtt = RealTupleType.SpatialEarth2DTuple;
830            if (!(rtt.equals(RealTupleType.SpatialEarth2DTuple) || rtt.equals(RealTupleType.LatitudeLongitudeTuple))) {
831              minLon = -180f;
832              minLat = -90f;
833              delLon = 360f;
834              delLat = 180f;
835            }
836            else {
837              int latI = rtt.equals(RealTupleType.SpatialEarth2DTuple) ? 1 : 0;
838              int lonI = (latI == 1) ? 0 : 1;
839    
840              float[] min = ((Gridded2DSet)domainSet).getLow();
841              float[] max = ((Gridded2DSet)domainSet).getHi();
842              minLon = min[lonI];
843              minLat = min[latI];
844              delLon = max[lonI] - min[lonI];
845              delLat = max[latI] - min[latI];
846            }
847          }
848          
849          try {
850             mp = new TrivialMapProjection(RealTupleType.SpatialEarth2DTuple,
851                     new Rectangle2D.Float(minLon, minLat, delLon, delLat));
852          } catch (Exception e) {
853              logException("MultiSpectralControl.getDataProjection", e);
854          }
855    
856          return mp;
857        }
858    
859        public void addMapDisplayables(MapProjectionDisplayJ3D mapProjDsp) 
860               throws VisADException, RemoteException {
861            MapLines mapLines  = new MapLines("maplines");
862            URL      mapSource =
863            mapProjDsp.getClass().getResource("/auxdata/maps/OUTLSUPU");
864            try {
865                BaseMapAdapter mapAdapter = new BaseMapAdapter(mapSource);
866                mapLines.setMapLines(mapAdapter.getData());
867                mapLines.setColor(java.awt.Color.cyan);
868                mapProjDsp.addDisplayable(mapLines);
869            } catch (Exception excp) {
870                System.out.println("Can't open map file " + mapSource);
871                System.out.println(excp);
872            }
873                                                                                                                                                      
874            mapLines  = new MapLines("maplines");
875            mapSource =
876            mapProjDsp.getClass().getResource("/auxdata/maps/OUTLSUPW");
877            try {
878                BaseMapAdapter mapAdapter = new BaseMapAdapter(mapSource);
879                mapLines.setMapLines(mapAdapter.getData());
880                mapLines.setColor(java.awt.Color.cyan);
881                mapProjDsp.addDisplayable(mapLines);
882            } catch (Exception excp) {
883                System.out.println("Can't open map file " + mapSource);
884                System.out.println(excp);
885            }
886                                                                                                                                                      
887            mapLines  = new MapLines("maplines");
888            mapSource =
889            mapProjDsp.getClass().getResource("/auxdata/maps/OUTLHPOL");
890            try {
891                BaseMapAdapter mapAdapter = new BaseMapAdapter(mapSource);
892                mapLines.setMapLines(mapAdapter.getData());
893                mapLines.setColor(java.awt.Color.cyan);
894                mapProjDsp.addDisplayable(mapLines);
895            } catch (Exception excp) {
896                System.out.println("Can't open map file " + mapSource);
897                System.out.println(excp);
898            }
899        }
900    
901        public boolean getSelectByCurve() {
902          return selectByCurve;
903        }
904    
905        private FlatField resample(FlatField X_field, FlatField Y_field) throws VisADException, RemoteException {
906    
907           RealTupleType X_domainRef = null;
908           RealTupleType Y_domainRef = null;
909           float[][] coords = null;
910           int[] indexes = null;
911           float[][] Yvalues = Y_field.getFloats(false);
912           float[][] Xsamples = ((SampledSet)X_field.getDomainSet()).getSamples(false);
913    
914           CoordinateSystem X_cs = X_field.getDomainCoordinateSystem();
915           if (X_cs == null) {
916              RealTupleType X_domain = ((FunctionType)X_field.getType()).getDomain();
917           }
918           else {
919             X_domainRef = X_cs.getReference();
920           }
921    
922           CoordinateSystem Y_cs = Y_field.getDomainCoordinateSystem();
923           if (Y_cs == null) {
924              RealTupleType Y_domain = ((FunctionType)Y_field.getType()).getDomain();
925           }
926           else {
927             Y_domainRef = Y_cs.getReference();
928           }
929    
930           if ( X_domainRef != null && Y_domainRef != null) {
931             Xsamples = X_cs.toReference(Xsamples);
932             coords = Y_cs.fromReference(Xsamples);
933             indexes = ((SampledSet)Y_field.getDomainSet()).valueToIndex(coords);
934           }
935           else if ( X_domainRef == null && Y_domainRef != null ) {
936             Xsamples = Y_cs.fromReference(Xsamples);
937             indexes = ((SampledSet)Y_field.getDomainSet()).valueToIndex(Xsamples);
938           }
939           else if ( X_domainRef != null && Y_domainRef == null) {
940             Xsamples = X_cs.toReference(Xsamples);
941             Gridded2DSet domSet = (Gridded2DSet) Y_field.getDomainSet();
942    
943             // TODO this is a hack for the longitude range problem
944             float[] hi = domSet.getHi();
945             if (hi[0] <= 180f) {
946               for (int t=0; t<Xsamples[0].length; t++) {
947                 if (Xsamples[0][t] > 180f) Xsamples[0][t] -=360;
948               }
949             }
950             
951             indexes = ((SampledSet)Y_field.getDomainSet()).valueToIndex(Xsamples);
952           }
953           else if (X_domainRef == null && Y_domainRef == null) {
954             Gridded2DSet domSet = (Gridded2DSet) Y_field.getDomainSet();
955             indexes = domSet.valueToIndex(Xsamples);
956           }
957           
958           float[][] new_values = new float[1][indexes.length];
959           for (int k=0; k<indexes.length; k++) {
960              new_values[0][k] = Float.NaN;
961              if (indexes[k] >= 0) {
962                new_values[0][k] = Yvalues[0][indexes[k]];
963              }
964           }
965    
966           FunctionType ftype = new FunctionType(((FunctionType)X_field.getType()).getDomain(),
967                    ((FunctionType)Y_field.getType()).getRange());
968           Y_field = new FlatField(ftype, X_field.getDomainSet());
969           Y_field.setSamples(new_values);
970    
971           return Y_field;
972        }
973    
974    
975        //For creating a non-editable JTable
976        private class MyStatsTable extends AbstractTableModel {
977          String [][] data;
978          JTable table;
979          JFrame statsWindow;
980          int numCols;
981          boolean isShowing = false;
982          Color[] coltab = {new Color(0xf0f0f0), new Color(0xffd0ff), 
983                            new Color(0xd0ffd0), new Color(0xc0d0ff)};
984    
985          final int maxCols = 9;
986          String[] colNames = {"Stats Parameter","Whole Field X","Whole Field Y",
987            "Magenta X","Magenta Y", "Green X","Green Y","Blue X","Blue Y"};
988    
989          final int maxRows = 13;
990          final String[] rowNames = {"Maximum","Minimum",
991            "Number of points","Mean","Median","Variance","Kurtosis",
992            "Std Dev","Correlation","Difference Maximum",
993            "Difference Minimum","Difference Mean","Area [km^2]"};
994    
995          MyStatsTable() { super();
996            data = new String[maxRows][maxCols];
997            numCols = 1;
998    
999            for (int i=0; i<maxRows; i++) {
1000              data[i][0] = rowNames[i];
1001              for (int j=1; j<maxCols; j++) {
1002                data[i][j] = "  ";
1003              }
1004            }
1005    
1006    
1007            table = new JTable(this) {
1008              public Component prepareRenderer(
1009                TableCellRenderer renderer, int row, int col) {
1010                  Component comp = super.prepareRenderer(renderer, row, col);
1011                  Color c = Color.white;
1012                  if (col == 0) c = coltab[0];
1013                  if (col == 3 || col == 4) c = coltab[1];
1014                  if (col == 5 || col == 6) c = coltab[2];
1015                  if (col == 7 || col == 8) c = coltab[3];
1016                  comp.setBackground(c);
1017                  return comp;
1018                }
1019    
1020            };
1021            table.setFillsViewportHeight(true);
1022            table.setPreferredScrollableViewportSize(new Dimension(620,220));
1023            table.setRowSelectionAllowed(true);
1024            table.setColumnSelectionAllowed(false);
1025    
1026            JButton saveStatsButt = new JButton("Save As CSV");
1027            JScrollPane sp = new JScrollPane(table);
1028            statsWindow = new JFrame("Scatter Statistics");
1029            statsWindow.setLayout(new BorderLayout());
1030            statsWindow.getContentPane().add(sp,BorderLayout.NORTH);
1031            JPanel bpan = new JPanel(new FlowLayout());
1032            bpan.add(saveStatsButt);
1033            statsWindow.getContentPane().add(bpan,BorderLayout.SOUTH);
1034            statsWindow.setSize(650,340);
1035            statsWindow.pack();
1036            statsWindow.addWindowListener(new WindowAdapter() {
1037              public void windowClosing(WindowEvent e) {
1038                isShowing = false;
1039              }
1040            });
1041    
1042            saveStatsButt.addActionListener(new ActionListener() {
1043                public void actionPerformed(final ActionEvent e) {
1044                  JFileChooser chzr = new JFileChooser();
1045                    FileFilter filt = new FileNameExtensionFilter("csv","txt");
1046                    chzr.addChoosableFileFilter(filt);
1047                    int rv = chzr.showSaveDialog(statsWindow);
1048                    if (rv == JFileChooser.APPROVE_OPTION) {
1049                      try {
1050                        File fpw = chzr.getSelectedFile();
1051                        statsWindow.setTitle("Scatter Statistics saved to "+fpw.toString());
1052                        PrintWriter pw = new PrintWriter(fpw);
1053                        String line = "";
1054                        for (int k=0; k<colNames.length; k++) {
1055                          if (k != 0) line = line + ",";
1056                          line = line + colNames[k];
1057                        }
1058                        pw.println(line);
1059    
1060                        for (int i=0; i<data.length; i++) {
1061                          line = "";
1062                          for (int j=0; j<data[i].length; j++) {
1063                            if (j != 0) line = line+",";
1064                            line = line+data[i][j];
1065                          }
1066                          pw.println(line);
1067                        }
1068                        pw.flush();
1069                        pw.close();
1070                      } catch (Exception epw) {
1071                        statsWindow.setTitle("Scatter Statistics: File not saved");
1072                      }  
1073    
1074                    }
1075    
1076                }
1077            });
1078    
1079            isShowing = false;
1080            statsWindow.setVisible(false);
1081          }
1082    
1083          public void setIsShowing() {
1084            isShowing = true;
1085          }
1086    
1087          public void resetValues(int col) {
1088            for (int i=0; i<maxRows; i++) {
1089              int c = 2*col + 3;
1090              data[i][c] = " ";
1091              data[i][c+1] = " ";
1092            }
1093            fireTableStructureChanged();
1094          }
1095    
1096          public void setNames(String xn, String yn) {
1097            colNames[1] = colNames[3] = colNames[5] = colNames[7] =xn;
1098            colNames[2] = colNames[4] = colNames[6] = colNames[8] =yn;
1099          }
1100    
1101          // fx, fy are Fields, col = 0,1,2,3 (all, red, green, blue)
1102          public void setFields(FlatField fx, FlatField fy, int col) {
1103            statsWindow.setTitle("Scatter Statistics");
1104            try {
1105              Statistics sx = new Statistics(fx);
1106              Statistics sy = new Statistics(fy);
1107              Statistics diff = new Statistics((FlatField)fx.subtract(fy));
1108    
1109              int c = 2*col + 1;
1110              data[0][c] = fmtMe(((Real)sx.max()).getValue());
1111              data[0][c+1] = fmtMe(((Real)sy.max()).getValue());
1112    
1113              data[1][c] = fmtMe(((Real)sx.min()).getValue());
1114              data[1][c+1] = fmtMe(((Real)sy.min()).getValue());
1115    
1116              data[2][c] = String.format("%d",sx.numPoints());
1117              data[2][c+1] = String.format("%d",sy.numPoints());
1118    
1119              data[3][c] = fmtMe(((Real)sx.mean()).getValue());
1120              data[3][c+1] = fmtMe(((Real)sy.mean()).getValue());
1121    
1122              data[4][c] = fmtMe(((Real)sx.median()).getValue());
1123              data[4][c+1] = fmtMe(((Real)sy.median()).getValue());
1124    
1125              data[5][c] = fmtMe(((Real)sx.variance()).getValue());
1126              data[5][c+1] = fmtMe(((Real)sy.variance()).getValue());
1127    
1128              data[6][c] = fmtMe(((Real)sx.kurtosis()).getValue());
1129              data[6][c+1] = fmtMe(((Real)sy.kurtosis()).getValue());
1130    
1131              data[7][c] = fmtMe(((Real)sx.standardDeviation()).getValue());
1132              data[7][c+1] = fmtMe(((Real)sy.standardDeviation()).getValue());
1133    
1134              data[8][c] = fmtMe(((Real)sx.correlation(fy)).getValue());
1135              data[8][c+1] = " ";
1136    
1137              data[9][c] = fmtMe(((Real)diff.max()).getValue());
1138              data[9][c+1] = " ";
1139    
1140              data[10][c] = fmtMe(((Real)diff.min()).getValue());
1141              data[10][c+1] = " ";
1142    
1143              data[11][c] = fmtMe(((Real)diff.mean()).getValue());
1144              data[11][c+1] = " ";
1145    
1146              if (c == 1) {
1147                data[12][c] = " ";
1148              } else {
1149                data[12][c] = fmtMe(total_area);
1150              }
1151              data[12][c+1] = " ";
1152    
1153              if (c+2 > numCols) numCols = c+2;
1154              fireTableStructureChanged();
1155    
1156            } catch (VisADException exc) {
1157              System.out.println(exc.getMessage());
1158            } catch (Exception exc) {
1159              exc.printStackTrace();
1160            }
1161    
1162            if (isShowing) statsWindow.setVisible(true);
1163          }
1164    
1165          private String fmtMe(double val) {
1166    
1167            if (Math.abs(val) == 0.0) {
1168              return "0.00";
1169    
1170            } else if (Math.abs(val) > 9999.9 || Math.abs(val) < .0010) {
1171              return String.format("%.6e", val);
1172    
1173            } else if (Math.abs(val) < 1.0) {
1174              return String.format("%.5f", val);
1175    
1176            } else if (Math.abs(val) < 10.0) {
1177              return String.format("%.3f", val);
1178    
1179            } else {
1180              return String.format("%.2f", val);
1181            }
1182          }
1183    
1184          public void setPoints(float[][] markScatter, int len, int indx) {
1185            try {
1186              Integer1DSet sdset = new Integer1DSet(len);
1187              FlatField scattX = new FlatField(
1188                new FunctionType(RealType.Generic, RealType.Generic), sdset);
1189    
1190              float[][] scattValsX = new float[1][len];
1191              System.arraycopy(markScatter[0],0,scattValsX[0],0,len);
1192              scattX.setSamples(scattValsX, false);
1193    
1194              FlatField scattY = new FlatField(
1195                new FunctionType(RealType.Generic, RealType.Generic), sdset);
1196              float[][] scattValsY = new float[1][len];
1197              System.arraycopy(markScatter[1],0,scattValsY[0],0,len);
1198              scattY.setSamples(scattValsY, false);
1199    
1200              setFields(scattX, scattY, indx);
1201    
1202            } catch (Exception esd) {
1203              esd.printStackTrace();
1204            }
1205          }
1206    
1207          public int getRowCount() {
1208            return maxRows;
1209          }
1210          public int getColumnCount() {
1211            return numCols;
1212          }
1213          public String getValueAt(int row, int col) {
1214            return data[row][col];
1215          }
1216          public String getColumnName(int col) {
1217            return colNames[col];
1218          }
1219        }
1220    
1221    
1222        private class ScatterDisplayable extends RGBDisplayable {
1223           ScatterDisplayable(String name, RealType rgbRealType, float[][] colorPalette, boolean alphaflag) 
1224               throws VisADException, RemoteException {
1225             super(name, rgbRealType, colorPalette, alphaflag);
1226           }
1227        }
1228    
1229        private class ImageControl extends DisplayControlImpl {
1230          HydraRGBDisplayable rgbDisp;
1231          DisplayConventions dc;
1232          ColorTableWidget ctw;
1233    
1234          ImageControl(HydraRGBDisplayable rgbDisp, DisplayConventions dc) {
1235            super();
1236            this.rgbDisp = rgbDisp;
1237            this.dc = dc;
1238          }
1239    
1240          @Override public void setRange(Range r) throws VisADException, RemoteException {
1241              if (r != null) {
1242                  rgbDisp.setRangeForColor(r.getMin(), r.getMax());
1243              }
1244          }
1245    
1246          @Override public DisplayConventions getDisplayConventions() {
1247            return dc;
1248          }
1249    
1250          @Override public void setColorTable(ColorTable ct) {
1251            try {
1252              ctw.setColorTable(ct);
1253              ScalarMap colorMap = rgbDisp.getColorMap();
1254              BaseColorControl clrCntrl = (BaseColorControl) colorMap.getControl();
1255    
1256              // Force incoming color dimension to that of the colorMap
1257              //
1258              int numComps = clrCntrl.getNumberOfComponents();
1259              float[][] clrTable = ct.getColorTable();
1260              float[][] newTable = null;
1261              if (numComps != clrTable.length) {
1262                if (numComps < clrTable.length) {
1263                  newTable = new float[numComps][clrTable[0].length];
1264                  for (int k=0; k<numComps; k++) {
1265                    System.arraycopy(clrTable[k], 0, newTable[k], 0, newTable[0].length);
1266                  }
1267                }
1268                else if (numComps > clrTable.length) {
1269                  newTable = new float[numComps][clrTable[0].length];
1270                  for (int k=0; k<clrTable.length; k++) {
1271                    System.arraycopy(clrTable[k], 0, newTable[k], 0, newTable[0].length);
1272                  }
1273                  newTable[3] = new float[clrTable[0].length];
1274                }
1275              } else {
1276                  newTable = new float[numComps][clrTable[0].length];
1277                  for (int k = 0; k < clrTable.length; k++) {
1278                    System.arraycopy(clrTable[k], 0, newTable[k], 0, newTable[0].length);
1279                  }
1280              } 
1281              clrCntrl.setTable(newTable);
1282            } 
1283            catch (Exception e) {
1284              LogUtil.logException("Problem changing color table", e);
1285            }
1286          }
1287        }
1288    
1289        private class ImageCurveSelector extends CellImpl implements DisplayListener {
1290          boolean init = false;
1291          CurveDrawer curveDraw;
1292          DisplayMaster dspMaster;
1293          Gridded2DSet domainSet;
1294          CoordinateSystem cs;
1295          int domainLen_0;
1296          int domainLen_1;
1297          ImageCurveSelector other;
1298          UnionSet last_uSet = null;
1299          boolean imageLatLon = false;
1300          boolean active = true;
1301          float maskVal;
1302          LineDrawing lastCurve;
1303          MyStatsTable myTable = null;
1304          int myTableIndex = 0;
1305    
1306          ImageCurveSelector(CurveDrawer curveDraw, FlatField image, DisplayMaster master, Color color, float maskVal, MyStatsTable mst) 
1307               throws VisADException, RemoteException {
1308            this.curveDraw = curveDraw;
1309            this.maskVal = maskVal;
1310            this.myTable = mst;
1311            myTableIndex = 0;
1312            if (color == Color.magenta) myTableIndex = 1;
1313            if (color == Color.green) myTableIndex = 2;
1314            if (color == Color.blue) myTableIndex = 3;
1315            dspMaster = master;
1316            dspMaster.addDisplayListener(this);
1317            domainSet = (Gridded2DSet) image.getDomainSet();
1318            int[] lens = domainSet.getLengths();
1319            domainLen_0 = lens[0];
1320            domainLen_1 = lens[1];
1321            cs = ((FunctionType)image.getType()).getDomain().getCoordinateSystem();
1322            RealTupleType reference = null;
1323            if (cs != null) {
1324              reference = cs.getReference();
1325            }
1326            else {
1327              reference = ((SetType)domainSet.getType()).getDomain();
1328            }
1329            RealType[] rtypes = reference.getRealComponents();
1330            if (rtypes[0].equals(RealType.Latitude)) imageLatLon = true;
1331            lastCurve = new LineDrawing("lastCurve");
1332            lastCurve.setColor(color);
1333            lastCurve.setLineWidth(2);
1334            master.addDisplayable(lastCurve);
1335          }
1336    
1337          @Override public void displayChanged(DisplayEvent de)
1338                 throws VisADException, RemoteException {
1339             if ((de.getId() == DisplayEvent.MOUSE_RELEASED) && (active)) {
1340               UnionSet uSet = curveDraw.getCurves();
1341               if (uSet == last_uSet) return;
1342               SampledSet[] sets = uSet.getSets();
1343               int s_idx = sets.length-1;
1344               float[][] crv;
1345    
1346               if (cs != null) {
1347                 crv = sets[s_idx].getSamples();
1348                 if (imageLatLon) {
1349                    float[] tmp = crv[0];
1350                    crv[0] = crv[1];
1351                    crv[1] = tmp;
1352                 }
1353                 crv = cs.fromReference(crv);
1354                 crv = domainSet.valueToGrid(crv);
1355               }
1356               else {
1357                 crv = sets[s_idx].getSamples();
1358                 crv = domainSet.valueToGrid(crv);
1359               }
1360    
1361               float[][] onImage = new float[2][crv[0].length];
1362               int cnt = 0;
1363               for (int i=0; i<crv[0].length; i++) {
1364                 if ( ((crv[0][i] >= 0)&&(crv[0][i] <= domainLen_0)) &&
1365                      ((crv[1][i] >= 0)&&(crv[1][i] <= domainLen_1)) ) {
1366                   onImage[0][cnt] = crv[0][i];
1367                   onImage[1][cnt] = crv[1][i];
1368                   cnt++;
1369                 }
1370               }
1371               uSet = new UnionSet(new SampledSet[] {sets[s_idx]});
1372               last_uSet = uSet;
1373               lastCurve.setData(last_uSet);
1374               curveDraw.setCurves(uSet);
1375               other.updateCurve(sets[s_idx]);
1376    
1377               if (cnt == 0) {
1378                 return;
1379               }
1380    
1381               float[][] tmp = new float[2][cnt];
1382               System.arraycopy(onImage[0], 0, tmp[0], 0, cnt);
1383               System.arraycopy(onImage[1], 0, tmp[1], 0, cnt);
1384               onImage = tmp;
1385    
1386               float[] minmaxvals = minmax(onImage[0]);
1387               int low_0 = Math.round(minmaxvals[0]);
1388               int hi_0 = Math.round(minmaxvals[1]);
1389               minmaxvals = minmax(onImage[1]);
1390               int low_1 = Math.round(minmaxvals[0]);
1391               int hi_1 = Math.round(minmaxvals[1]);
1392    
1393               int len_0 = (hi_0 - low_0) + 1;
1394               int len_1 = (hi_1 - low_1) + 1;
1395               int len = len_0*len_1;
1396    
1397               tmp = new float[3][len];
1398               int[] tmpsel = new int[len];
1399    
1400               int num_inside = 0;
1401               for (int j=0; j<len_1; j++) {
1402                 for (int i=0; i<len_0; i++) {
1403                   int idx = (j+low_1)*domainLen_0 + (i+low_0);
1404                   float x = (float) (i + low_0);
1405                   float y = (float) (j + low_1);
1406                   if (DelaunayCustom.inside(crv, x, y)) {
1407                     tmp[0][num_inside] = scatterFieldRange[0][idx];
1408                     tmp[1][num_inside] = scatterFieldRange[1][idx];
1409                     tmp[2][num_inside] = maskVal;
1410                     tmpsel[num_inside] = idx;
1411                     num_inside++;
1412                   }
1413                 }
1414               }
1415               len = num_inside;
1416               float[][] markScatter = new float[3][len];
1417               System.arraycopy(tmp[0], 0, markScatter[0], 0, len);
1418               System.arraycopy(tmp[1], 0, markScatter[1], 0, len);
1419               System.arraycopy(tmp[2], 0, markScatter[2], 0, len);
1420    
1421    
1422               int last_len = 0;
1423               float[][] lastMark = ((FlatField)scatterMarkDsp.getData()).getFloats(false);
1424               tmp = new float[3][lastMark[0].length];
1425               for (int k=0; k<lastMark[0].length; k++) {
1426                 if (lastMark[2][k] != maskVal) {
1427                   tmp[0][last_len] = lastMark[0][k];
1428                   tmp[1][last_len] = lastMark[1][k];
1429                   tmp[2][last_len] = lastMark[2][k];
1430                   last_len++;
1431                 }
1432               }
1433    
1434               float[][] newMarkScatter = new float[3][len+last_len];
1435               System.arraycopy(tmp[0], 0, newMarkScatter[0], 0, last_len);
1436               System.arraycopy(tmp[1], 0, newMarkScatter[1], 0, last_len);
1437               System.arraycopy(tmp[2], 0, newMarkScatter[2], 0, last_len);
1438               System.arraycopy(markScatter[0], 0, newMarkScatter[0], last_len, len);
1439               System.arraycopy(markScatter[1], 0, newMarkScatter[1], last_len, len);
1440               System.arraycopy(markScatter[2], 0, newMarkScatter[2], last_len, len);
1441    
1442               Integer1DSet dset = new Integer1DSet(len+last_len);
1443               FlatField scatterFieldMark = new FlatField(
1444                 new FunctionType(RealType.Generic,
1445                    new RealTupleType(RealType.XAxis, RealType.YAxis, RealType.getRealType("mask"))), dset);
1446    
1447               scatterFieldMark.setSamples(newMarkScatter, false);
1448               scatterMarkDsp.setData(scatterFieldMark);
1449    
1450               if (myTable != null) {
1451                 int[] selected = new int[len];
1452                 System.arraycopy(tmpsel, 0, selected, 0, len);
1453                 total_area = JPythonMethods.computeSum(Area_field, selected);
1454                 myTable.setPoints(markScatter, len, myTableIndex);  
1455               }
1456    
1457             }
1458          }
1459    
1460          public void setActive(boolean active) {
1461            this.active = active;
1462          }
1463    
1464          public void reset() throws VisADException, RemoteException {
1465    
1466            float[][] lastMark = ((FlatField)scatterMarkDsp.getData()).getFloats(false);
1467            float[][] tmp = new float[3][lastMark[0].length];
1468            int cnt = 0;
1469            for (int k=0; k<lastMark[0].length; k++) {
1470              if (lastMark[2][k] != maskVal) {
1471                 tmp[0][cnt] = lastMark[0][k];
1472                 tmp[1][cnt] = lastMark[1][k];
1473                 tmp[2][cnt] = lastMark[2][k];
1474                 cnt++;
1475              }
1476            }
1477    
1478            RealTupleType type = ((SetType)curveDraw.getCurves().getType()).getDomain();
1479            curveDraw.setCurves(new UnionSet(new Gridded2DSet[]{
1480                new Gridded2DSet(type, new float[][] {
1481                { 0.0f }, { 0.0f }}, 1) }));
1482            
1483            lastCurve.setData(new UnionSet(new Gridded2DSet[]{
1484                new Gridded2DSet(type, new float[][] {
1485                { 0.0f }, { 0.0f }}, 1) }));
1486    
1487            FlatField scatterFieldMark = null;
1488            if (cnt == 0) {
1489            Integer1DSet dset = new Integer1DSet(2);
1490            scatterFieldMark = new FlatField(
1491            new FunctionType(RealType.Generic,
1492                  new RealTupleType(RealType.XAxis, RealType.YAxis, RealType.getRealType("mask"))), dset);
1493            float[][] markScatter = new float[3][2]; 
1494            for (int k=0; k<2; k++) {
1495              markScatter[0][k] = scatterFieldRange[0][k];
1496              markScatter[1][k] = scatterFieldRange[1][k];
1497              markScatter[2][k] = 0;
1498            }
1499            scatterFieldMark.setSamples(markScatter, false);
1500            }
1501            else {
1502              Integer1DSet dset = new Integer1DSet(cnt);
1503              scatterFieldMark = new FlatField(
1504              new FunctionType(RealType.Generic,
1505                    new RealTupleType(RealType.XAxis, RealType.YAxis, RealType.getRealType("mask"))), dset);
1506              float[][] markScatter = new float[3][cnt];
1507              for (int k=0; k<cnt; k++) {
1508                markScatter[0][k] = tmp[0][k];
1509                markScatter[1][k] = tmp[1][k];
1510                markScatter[2][k] = tmp[2][k];
1511              }
1512              scatterFieldMark.setSamples(markScatter, false);
1513            }
1514    
1515            scatterMarkDsp.setData(scatterFieldMark);
1516          }
1517    
1518          public void updateCurve(SampledSet set) throws VisADException, RemoteException {
1519            last_uSet = new UnionSet(new SampledSet[] {set});
1520            curveDraw.setCurves(last_uSet);
1521            lastCurve.setData(last_uSet);
1522          }
1523    
1524          public void setOther(ImageCurveSelector other) {
1525            this.other = other;
1526          }
1527    
1528          @Override public void doAction()
1529               throws VisADException, RemoteException {
1530            if (!init) {
1531              init = true;
1532              return;
1533            }
1534          }
1535    
1536          public void setVisible(boolean visible) throws VisADException, RemoteException {
1537            curveDraw.setVisible(visible);
1538          }
1539    
1540        }
1541    
1542        private class ImageBoxSelector extends CellImpl {
1543            boolean init = false;
1544            boolean active = true;
1545            SubsetRubberBandBox subsetBox;
1546            Set imageDomain;
1547            int domainLen_0;
1548            LineDrawing lastBox;
1549            ImageBoxSelector other;
1550            float maskVal;
1551            boolean earthCoordDomain = false;
1552            MyStatsTable myTable = null;
1553            int myTableIndex = 0;
1554    
1555            ImageBoxSelector(SubsetRubberBandBox subsetBox, Set imageDomain, DisplayMaster master, Color color, float maskVal, MyStatsTable mst) 
1556                throws VisADException, RemoteException {
1557              super();
1558              this.myTable = mst;
1559              myTableIndex = 0;
1560              if (color == Color.magenta) myTableIndex = 1;
1561              if (color == Color.green) myTableIndex = 2;
1562              if (color == Color.blue) myTableIndex = 3;
1563              this.subsetBox = subsetBox;
1564              this.imageDomain = imageDomain;
1565              int[] lens = ((Gridded2DSet)imageDomain).getLengths();
1566              this.maskVal = maskVal;
1567              domainLen_0 = lens[0];
1568              lastBox = new LineDrawing("last_box");
1569              lastBox.setColor(color);
1570              master.addDisplayable(lastBox);
1571              subsetBox.addAction(this);
1572              master.addDisplayable(subsetBox);
1573              RealTupleType rtt = ((SetType)imageDomain.getType()).getDomain();
1574              if (rtt.equals(RealTupleType.SpatialEarth2DTuple) || 
1575                  rtt.equals(RealTupleType.LatitudeLongitudeTuple)) {
1576                earthCoordDomain = true;
1577              }
1578            }
1579    
1580            @Override public void doAction()
1581                 throws VisADException, RemoteException
1582            {
1583               if (!init) {
1584                 init = true;
1585                 return;
1586               }
1587     
1588               if (!active) {
1589                 return;
1590               }
1591    
1592               Gridded2DSet set = subsetBox.getBounds();
1593               float[][] corners = set.getSamples(false);
1594               float[][] coords = corners;
1595    
1596               if ((imageDomain instanceof Linear2DSet) || !earthCoordDomain) {
1597                 coords = ((Gridded2DSet)imageDomain).valueToGrid(corners);
1598               }
1599    
1600               float[] coords_0 = coords[0];
1601               float[] coords_1 = coords[1];
1602    
1603               int low_0 = Math.round(Math.min(coords_0[0], coords_0[1]));
1604               int low_1 = Math.round(Math.min(coords_1[0], coords_1[1]));
1605               int hi_0  = Math.round(Math.max(coords_0[0], coords_0[1]));
1606               int hi_1  = Math.round(Math.max(coords_1[0], coords_1[1]));
1607    
1608               int len_0 = (hi_0 - low_0) + 1;
1609               int len_1 = (hi_1 - low_1) + 1;
1610               int len = len_0*len_1;
1611    
1612               float[][] markScatter = new float[3][len];
1613               int[] selected = new int[len];
1614    
1615               for (int j=0; j<len_1; j++) {
1616                 for (int i=0; i<len_0; i++) {
1617                   int idx = (j+low_1)*domainLen_0 + (i+low_0);
1618                   int k = j*len_0 + i;
1619                   markScatter[0][k] = scatterFieldRange[0][idx];
1620                   markScatter[1][k] = scatterFieldRange[1][idx];
1621                   markScatter[2][k] = maskVal;
1622                   selected[k] = idx;
1623                 }
1624               }
1625    
1626               int last_len = 0;
1627               float[][] lastMark = ((FlatField)scatterMarkDsp.getData()).getFloats(false);
1628               float[][] tmp = new float[3][lastMark[0].length];
1629               for (int k=0; k<lastMark[0].length; k++) {
1630                 if (lastMark[2][k] != maskVal) {
1631                   tmp[0][last_len] = lastMark[0][k];
1632                   tmp[1][last_len] = lastMark[1][k];
1633                   tmp[2][last_len] = lastMark[2][k];
1634                   last_len++;
1635                 }
1636               }
1637    
1638               float[][] newMarkScatter = new float[3][len+last_len];
1639               System.arraycopy(tmp[0], 0, newMarkScatter[0], 0, last_len);
1640               System.arraycopy(tmp[1], 0, newMarkScatter[1], 0, last_len);
1641               System.arraycopy(tmp[2], 0, newMarkScatter[2], 0, last_len);
1642               System.arraycopy(markScatter[0], 0, newMarkScatter[0], last_len, len);
1643               System.arraycopy(markScatter[1], 0, newMarkScatter[1], last_len, len);
1644               System.arraycopy(markScatter[2], 0, newMarkScatter[2], last_len, len);
1645    
1646               Integer1DSet dset = new Integer1DSet(len+last_len);
1647               FlatField scatterFieldMark = new FlatField(
1648                 new FunctionType(RealType.Generic,
1649                    new RealTupleType(RealType.XAxis, RealType.YAxis, RealType.getRealType("mask"))), dset);
1650    
1651               scatterFieldMark.setSamples(newMarkScatter, false);
1652               scatterMarkDsp.setData(scatterFieldMark);
1653    
1654               if (myTable != null) {
1655                 total_area = JPythonMethods.computeSum(Area_field, selected);
1656                 myTable.setPoints(markScatter, len, myTableIndex);  
1657               }
1658    
1659               updateBox();
1660            }
1661    
1662            public void setActive(boolean active) {
1663              this.active = active;
1664            }
1665    
1666            public void setVisible(boolean visible) throws VisADException, RemoteException {
1667              subsetBox.setVisible(visible);
1668              if (visible) {
1669                lastBox.setVisible(visible);
1670              }
1671            }
1672    
1673            public void reset() throws VisADException, RemoteException {
1674              Gridded2DSet set2D =
1675                 new Gridded2DSet(RealTupleType.SpatialCartesian2DTuple,
1676                                      new float[][] {{0},{0}}, 1);
1677              lastBox.setVisible(false);
1678              lastBox.setData(set2D);
1679    
1680              float[][] lastMark = ((FlatField)scatterMarkDsp.getData()).getFloats(false);
1681              float[][] tmp = new float[3][lastMark[0].length];
1682              int cnt = 0;
1683              for (int k=0; k<lastMark[0].length; k++) {
1684                if (lastMark[2][k] != maskVal) {
1685                   tmp[0][cnt] = lastMark[0][k];
1686                   tmp[1][cnt] = lastMark[1][k];
1687                   tmp[2][cnt] = lastMark[2][k];
1688                   cnt++;
1689                }
1690              }
1691    
1692              FlatField scatterFieldMark;
1693              if (cnt == 2) {
1694              Integer1DSet dset = new Integer1DSet(2);
1695              scatterFieldMark = new FlatField(
1696              new FunctionType(RealType.Generic,
1697                    new RealTupleType(RealType.XAxis, RealType.YAxis, RealType.getRealType("mask"))), dset);
1698              float[][] markScatter = new float[3][2];
1699              for (int k=0; k<2; k++) {
1700                markScatter[0][k] = scatterFieldRange[0][k];
1701                markScatter[1][k] = scatterFieldRange[1][k];
1702                markScatter[2][k] = 0;
1703              }
1704              scatterFieldMark.setSamples(markScatter, false);
1705              }
1706              else {
1707              Integer1DSet dset = new Integer1DSet(cnt);
1708              scatterFieldMark = new FlatField(
1709              new FunctionType(RealType.Generic,
1710                    new RealTupleType(RealType.XAxis, RealType.YAxis, RealType.getRealType("mask"))), dset);
1711              float[][] markScatter = new float[3][cnt];
1712              for (int k=0; k<cnt; k++) {
1713                markScatter[0][k] = tmp[0][k];
1714                markScatter[1][k] = tmp[1][k];
1715                markScatter[2][k] = tmp[2][k];
1716              }
1717              scatterFieldMark.setSamples(markScatter, false);
1718              }
1719    
1720              scatterMarkDsp.setData(scatterFieldMark);
1721            }
1722    
1723            public void setOther(ImageBoxSelector other) {
1724              this.other = other;
1725            }
1726    
1727            public void updateBox() throws VisADException, RemoteException {
1728              Gridded3DSet set3D = subsetBox.getLastBox();
1729              float[][] samples = set3D.getSamples(false);
1730              Gridded2DSet set2D = 
1731                 new Gridded2DSet(RealTupleType.SpatialCartesian2DTuple, 
1732                                      new float[][] {samples[0], samples[1]}, samples[0].length);
1733              lastBox.setData(set2D);
1734              other.updateBox(set2D);
1735            }
1736    
1737            public void updateBox(Gridded2DSet set2D) throws VisADException, RemoteException {
1738              lastBox.setData(set2D);
1739            }
1740    
1741        }
1742    
1743        private class ScatterBoxSelector extends CellImpl {
1744           boolean init = false;
1745           double[] x_coords = new double[2];
1746           double[] y_coords = new double[2];
1747           RubberBandBox rbb;
1748           LineDrawing selectBox;
1749           boolean active = true;
1750           float maskVal = 0;
1751    
1752           ScatterBoxSelector(DisplayMaster master, Color color, float maskVal) throws VisADException, RemoteException {
1753             selectBox = new LineDrawing("select");
1754             selectBox.setColor(color);
1755    
1756             rbb = new RubberBandBox(RealType.XAxis, RealType.YAxis, 1);
1757             rbb.setColor(color);
1758             rbb.addAction(this);
1759    
1760             master.addDisplayable(rbb);
1761             master.addDisplayable(selectBox);
1762             this.maskVal = maskVal;
1763           }
1764    
1765    
1766           @Override public void doAction() throws VisADException, RemoteException {
1767             if (!init) {
1768               init = true;
1769               return;
1770             }
1771    
1772             if (!active) {
1773               return;
1774             }
1775    
1776             Gridded2DSet set = rbb.getBounds();
1777             float[] low = set.getLow();
1778             float[] hi = set.getHi();
1779             x_coords[0] = low[0];
1780             x_coords[1] = hi[0];
1781             y_coords[0] = low[1];
1782             y_coords[1] = hi[1];
1783                                                                                                                                                      
1784             SampledSet[] sets = new SampledSet[4];
1785             sets[0] = new Gridded2DSet(RealTupleType.SpatialCartesian2DTuple, new float[][] {{low[0], hi[0]}, {low[1], low[1]}}, 2);
1786             sets[1] = new Gridded2DSet(RealTupleType.SpatialCartesian2DTuple, new float[][] {{hi[0], hi[0]}, {low[1], hi[1]}}, 2);
1787             sets[2] = new Gridded2DSet(RealTupleType.SpatialCartesian2DTuple, new float[][] {{hi[0], low[0]}, {hi[1], hi[1]}}, 2);
1788             sets[3] = new Gridded2DSet(RealTupleType.SpatialCartesian2DTuple, new float[][] {{low[0], low[0]}, {hi[1], low[1]}}, 2);
1789             UnionSet uset = new UnionSet(sets);
1790             selectBox.setData(uset);
1791    
1792             try {
1793               histoField.markMaskFieldByRange(x_coords, y_coords, maskVal);
1794             } catch (Exception e) {
1795               e.printStackTrace();
1796             }
1797           }
1798    
1799           public void setVisible(boolean visible) throws VisADException, RemoteException {
1800             rbb.setVisible(visible);
1801             if (visible) {
1802               selectBox.setVisible(visible);
1803             }
1804           }
1805    
1806           public void setActive(boolean active) {
1807             this.active = active;
1808           }
1809    
1810           public void reset() throws Exception {
1811             selectBox.setVisible(false);
1812             selectBox.setData(new Gridded2DSet(RealTupleType.SpatialCartesian2DTuple, new float[][] {{0f, 0f}, {0f, 0f}}, 2));
1813             histoField.resetMaskField(maskVal);
1814           }
1815       }
1816    
1817       private class ScatterCurveSelector extends CellImpl implements DisplayListener {
1818         CurveDrawer curveDraw;
1819         boolean init = false;
1820         UnionSet last_uSet = null;
1821         boolean active = true;
1822         float maskVal = 0;
1823         LineDrawing selectCurve;
1824    
1825         ScatterCurveSelector(DisplayMaster master, Color color, float maskVal) throws VisADException, RemoteException {
1826           curveDraw = new CurveDrawer(RealType.XAxis, RealType.YAxis, 1);
1827           curveDraw.setColor(color);
1828           curveDraw.setLineWidth(2);
1829           curveDraw.setData(new UnionSet(new Gridded2DSet[]{
1830                new Gridded2DSet(RealTupleType.SpatialCartesian2DTuple, new float[][] {
1831                { scatterFieldRange[0][0] }, { scatterFieldRange[1][0]}
1832            }, 1) }));
1833    
1834           selectCurve = new LineDrawing("select");
1835           selectCurve.setColor(color);
1836           selectCurve.setLineWidth(2);
1837           master.addDisplayable(curveDraw);
1838           master.addDisplayable(selectCurve);
1839           this.maskVal = maskVal;
1840    
1841           curveDraw.addAction(this);
1842           master.addDisplayListener(this);
1843         }
1844    
1845         @Override public void displayChanged(DisplayEvent de)
1846                throws VisADException, RemoteException {
1847           if ((de.getId() == DisplayEvent.MOUSE_RELEASED) && (active)) {
1848             UnionSet uSet = curveDraw.getCurves();
1849             if (uSet == last_uSet) return;
1850             SampledSet[] sets = uSet.getSets();
1851             int s_idx = sets.length-1;
1852             float[][] crv;
1853                                                                                                                                                      
1854             crv = sets[s_idx].getSamples();
1855             last_uSet = new UnionSet(new SampledSet[] {sets[s_idx]});
1856             curveDraw.setCurves(last_uSet);
1857             selectCurve.setData(last_uSet);
1858    
1859             try {
1860               histoField.clearMaskField(maskVal);
1861               histoField.markMaskFieldByCurve(crv, maskVal);
1862             } catch (Exception e) {
1863               e.printStackTrace();
1864             }
1865           }
1866         }
1867    
1868         @Override public  void doAction() throws VisADException, RemoteException {
1869           if (!init) {
1870             init = true;
1871             return;
1872           }
1873         }
1874    
1875         public void setVisible(boolean visible) throws VisADException, RemoteException {
1876           curveDraw.setVisible(visible);
1877         }
1878    
1879         public void setActive(boolean active) {
1880           this.active = active;
1881         }
1882    
1883         public void reset() throws Exception {
1884           curveDraw.setData(new UnionSet(new Gridded2DSet[]{
1885                new Gridded2DSet(RealTupleType.SpatialCartesian2DTuple, new float[][] {
1886                { scatterFieldRange[0][0] }, { scatterFieldRange[1][0]}
1887            }, 1) }));
1888           selectCurve.setData(new UnionSet(new Gridded2DSet[]{
1889                new Gridded2DSet(RealTupleType.SpatialCartesian2DTuple, new float[][] {
1890                { scatterFieldRange[0][0] }, { scatterFieldRange[1][0]}
1891            }, 1) }));
1892           histoField.resetMaskField(maskVal);
1893         }
1894       }
1895    
1896       private class BoxCurveSwitch implements ActionListener {
1897    
1898         public BoxCurveSwitch() {
1899         }
1900        
1901         @Override public void actionPerformed(ActionEvent ae) {
1902           String cmd = ae.getActionCommand();
1903           try {
1904           if (cmd.equals("Box")) {
1905             selectByCurve = false;
1906           } else if (cmd.equals("Curve")) {
1907             selectByCurve = true;
1908           }
1909           }
1910           catch (Exception e) {
1911             e.printStackTrace();
1912           }
1913         }
1914       }
1915    
1916        public static float[] minmax(float[] values) {
1917          float min =  Float.MAX_VALUE;
1918          float max = -Float.MAX_VALUE;
1919          for (int k = 0; k < values.length; k++) {
1920            float val = values[k];
1921            if ((val == val) && (val < Float.POSITIVE_INFINITY) && (val > Float.NEGATIVE_INFINITY)) {
1922              if (val < min) min = val;
1923              if (val > max) max = val;
1924            }
1925          }
1926          return new float[] {min, max};
1927        }
1928    
1929        public boolean getIsLatLon(FlatField field) throws VisADException, RemoteException {
1930          boolean isLL = false;
1931          FunctionType fnc_type = (FunctionType) field.getType();
1932          RealTupleType rtt = fnc_type.getDomain();
1933          if (rtt.equals(RealTupleType.LatitudeLongitudeTuple)) {
1934            isLL = true;
1935          }
1936          else if (!rtt.equals(RealTupleType.SpatialEarth2DTuple)) {
1937            rtt = fnc_type.getDomain().getCoordinateSystem().getReference();
1938            if ( rtt.equals(RealTupleType.LatitudeLongitudeTuple)) {
1939              isLL = true;
1940            }
1941          }
1942          return isLL;
1943        }
1944    }