001/*
002 * This file is part of McIDAS-V
003 *
004 * Copyright 2007-2015
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
030package edu.wisc.ssec.mcidasv.control;
031
032import java.awt.BorderLayout;
033import java.awt.Color;
034import java.awt.Component;
035import java.awt.Container;
036import java.awt.Dimension;
037import java.awt.FlowLayout;
038import java.awt.GridLayout;
039import java.awt.event.ActionEvent;
040import java.awt.event.ActionListener;
041import java.awt.event.WindowEvent;
042import java.awt.event.WindowAdapter;
043import java.awt.geom.Rectangle2D;
044import java.net.URL;
045import java.rmi.RemoteException;
046import java.util.ArrayList;
047import java.util.List;
048import java.io.PrintWriter;
049import java.io.File;
050
051import javax.swing.JFrame;
052import javax.swing.ButtonGroup;
053import javax.swing.ImageIcon;
054import javax.swing.JComponent;
055import javax.swing.JPanel;
056import javax.swing.JRadioButton;
057import javax.swing.JToggleButton;
058import javax.swing.JButton;
059import javax.swing.JScrollPane;
060import javax.swing.JTable;
061import javax.swing.JFileChooser;
062import javax.swing.filechooser.FileFilter;
063import javax.swing.filechooser.FileNameExtensionFilter;
064import javax.swing.table.AbstractTableModel;
065import javax.swing.table.TableCellRenderer;
066import javax.swing.border.CompoundBorder;
067import javax.swing.border.EmptyBorder;
068import javax.swing.border.LineBorder;
069
070import org.slf4j.Logger;
071import org.slf4j.LoggerFactory;
072
073import visad.AxisScale;
074import visad.BaseColorControl;
075import visad.CellImpl;
076import visad.CoordinateSystem;
077import visad.Data;
078import visad.DelaunayCustom;
079import visad.DisplayEvent;
080import visad.DisplayListener;
081import visad.Real;
082import visad.FieldImpl;
083import visad.FlatField;
084import visad.FunctionType;
085import visad.Gridded2DSet;
086import visad.Gridded3DSet;
087import visad.Integer1DSet;
088import visad.Linear2DSet;
089import visad.LinearLatLonSet;
090import visad.RealTupleType;
091import visad.MathType;
092import visad.RealType;
093import visad.SampledSet;
094import visad.ScalarMap;
095import visad.Set;
096import visad.SetType;
097import visad.UnionSet;
098import visad.VisADException;
099import visad.data.mcidas.BaseMapAdapter;
100import visad.georef.MapProjection;
101import visad.georef.TrivialMapProjection;
102import visad.python.JPythonMethods;
103
104import ucar.unidata.data.DataAlias;
105import ucar.unidata.data.DataChoice;
106import ucar.unidata.data.DataSelection;
107import ucar.unidata.data.grid.GridUtil;
108import ucar.unidata.idv.DisplayConventions;
109import ucar.unidata.idv.control.ColorTableWidget;
110import ucar.unidata.idv.control.DisplayControlImpl;
111import ucar.unidata.ui.colortable.ColorTableManager;
112import ucar.unidata.util.ColorTable;
113import ucar.unidata.util.LogUtil;
114import ucar.unidata.util.Range;
115import ucar.unidata.view.geoloc.MapProjectionDisplay;
116import ucar.unidata.view.geoloc.MapProjectionDisplayJ3D;
117import ucar.visad.display.DisplayMaster;
118import ucar.visad.display.LineDrawing;
119import ucar.visad.display.MapLines;
120import ucar.visad.display.RGBDisplayable;
121import ucar.visad.display.RubberBandBox;
122import ucar.visad.display.XYDisplay;
123
124import edu.wisc.ssec.mcidasv.data.hydra.CurveDrawer;
125import edu.wisc.ssec.mcidasv.data.hydra.HistogramField;
126import edu.wisc.ssec.mcidasv.data.hydra.HydraRGBDisplayable;
127import edu.wisc.ssec.mcidasv.data.hydra.MultiSpectralData;
128import edu.wisc.ssec.mcidasv.data.hydra.SubsetRubberBandBox;
129import edu.wisc.ssec.mcidasv.data.hydra.LongitudeLatitudeCoordinateSystem;
130import edu.wisc.ssec.mcidasv.data.hydra.Statistics;
131import edu.wisc.ssec.mcidasv.data.StatsTable;
132
133public 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}