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