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