001/*
002 * $Id: MultiDimensionDataSource.java,v 1.30 2011/03/24 16:06:33 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
031package edu.wisc.ssec.mcidasv.data.hydra;
032
033import java.awt.BorderLayout;
034import java.awt.Color;
035import java.awt.FlowLayout;
036import java.awt.event.ActionEvent;
037import java.awt.event.ActionListener;
038import java.io.File;
039import java.net.URL;
040import java.rmi.RemoteException;
041import java.util.Enumeration;
042import java.util.HashMap;
043import java.util.Hashtable;
044import java.util.List;
045
046import javax.swing.JComponent;
047import javax.swing.JLabel;
048import javax.swing.JPanel;
049import javax.swing.JTextField;
050
051import visad.CellImpl;
052import visad.Data;
053import visad.FlatField;
054import visad.Gridded2DSet;
055import visad.GriddedSet;
056import visad.RealTupleType;
057import visad.RealType;
058import visad.SampledSet;
059import visad.UnionSet;
060import visad.VisADException;
061import visad.data.mcidas.BaseMapAdapter;
062import visad.georef.MapProjection;
063
064import ucar.unidata.data.DataCategory;
065import ucar.unidata.data.DataChoice;
066import ucar.unidata.data.DataSelection;
067import ucar.unidata.data.DataSelectionComponent;
068import ucar.unidata.data.DataSourceDescriptor;
069import ucar.unidata.data.DirectDataChoice;
070import ucar.unidata.data.GeoLocationInfo;
071import ucar.unidata.data.GeoSelection;
072import ucar.unidata.geoloc.projection.LatLonProjection;
073import ucar.unidata.util.Misc;
074import ucar.unidata.view.geoloc.MapProjectionDisplay;
075import ucar.unidata.view.geoloc.MapProjectionDisplayJ3D;
076import ucar.visad.ProjectionCoordinateSystem;
077import ucar.visad.display.DisplayMaster;
078import ucar.visad.display.LineDrawing;
079import ucar.visad.display.MapLines;
080import ucar.visad.display.RubberBandBox;
081
082import edu.wisc.ssec.mcidasv.Constants;
083import edu.wisc.ssec.mcidasv.data.HydraDataSource;
084import edu.wisc.ssec.mcidasv.data.PreviewSelection;
085import edu.wisc.ssec.mcidasv.display.hydra.MultiSpectralDisplay;
086
087/**
088 * A data source for Multi Dimension Data 
089 */
090
091public class MultiDimensionDataSource extends HydraDataSource {
092
093    /** Sources file */
094    protected String filename;
095
096    protected MultiDimensionReader reader;
097
098    protected MultiDimensionAdapter[] adapters = null;
099    private HashMap<String, MultiDimensionAdapter> adapterMap = new HashMap<String, MultiDimensionAdapter>();
100
101
102    protected SpectrumAdapter spectrumAdapter;
103
104    private static final String DATA_DESCRIPTION = "Multi Dimension Data";
105
106    private HashMap defaultSubset;
107    public TrackAdapter track_adapter;
108    private MultiSpectralData multiSpectData;
109
110    private List categories;
111    private boolean hasImagePreview = false;
112    private boolean hasTrackPreview = false;
113
114    /**
115     * Zero-argument constructor for construction via unpersistence.
116     */
117    public MultiDimensionDataSource() {}
118
119    /**
120     * Construct a new HYDRA hdf data source.
121     * @param  descriptor  descriptor for this <code>DataSource</code>
122     * @param  fileName  name of the hdf file to read
123     * @param  properties  hashtable of properties
124     *
125     * @throws VisADException problem creating data
126     */
127    public MultiDimensionDataSource(DataSourceDescriptor descriptor,
128                                 String fileName, Hashtable properties)
129            throws VisADException {
130        this(descriptor, Misc.newList(fileName), properties);
131    }
132
133    /**
134     * Construct a new HYDRA hdf data source.
135     * @param  descriptor  descriptor for this <code>DataSource</code>
136     * @param  sources   List of filenames
137     * @param  properties  hashtable of properties
138     *
139     * @throws VisADException problem creating data
140     */
141    public MultiDimensionDataSource(DataSourceDescriptor descriptor,
142                                 List newSources, Hashtable properties)
143            throws VisADException {
144        super(descriptor, newSources, DATA_DESCRIPTION, properties);
145
146        this.filename = (String)sources.get(0);
147
148        try {
149          setup();
150        }
151        catch (Exception e) {
152          e.printStackTrace();
153          throw new VisADException();
154        }
155    }
156
157    public void setup() throws Exception {
158
159        try {
160            reader = new NetCDFFile(filename);
161        }
162        catch (Exception e) {
163          e.printStackTrace();
164          System.out.println("cannot create NetCDF reader for file: "+filename);
165        }
166
167        adapters = new MultiDimensionAdapter[1];
168        Hashtable<String, String[]> properties = new Hashtable<String, String[]>(); 
169
170        String name = (new File(filename)).getName();
171        if ( name.startsWith("MOD04") || name.startsWith("MYD04")) {
172          HashMap table = SwathAdapter.getEmptyMetadataTable();
173          table.put("array_name", "mod04/Data Fields/Optical_Depth_Land_And_Ocean");
174          table.put("lon_array_name", "mod04/Geolocation Fields/Longitude");
175          table.put("lat_array_name", "mod04/Geolocation Fields/Latitude");
176          table.put("XTrack", "Cell_Across_Swath");
177          table.put("Track", "Cell_Along_Swath");
178          table.put("geo_Track", "Cell_Along_Swath");
179          table.put("geo_XTrack", "Cell_Across_Swath");
180          table.put("scale_name", "scale_factor");
181          table.put("offset_name", "add_offset");
182          table.put("fill_value_name", "_FillValue");
183          table.put("range_name", "Optical_Depth_Land_And_Ocean");
184          adapters[0] = new SwathAdapter(reader, table);
185          categories = DataCategory.parseCategories("2D grid;GRID-2D;");
186          defaultSubset = adapters[0].getDefaultSubset();
187        }
188        else if ( name.startsWith("MOD06") || name.startsWith("MYD06") ) {
189          hasImagePreview = true;
190          String path = "mod06/Data Fields/";
191          String[] arrayNames = new String[] {"Cloud_Optical_Thickness", "Cloud_Effective_Radius", "Cloud_Water_Path"};
192          String[] arrayNames_5km = new String[] {"Cloud_Top_Pressure", "Cloud_Top_Temperature", "Cloud_Fraction"};
193          adapters = new MultiDimensionAdapter[arrayNames.length+arrayNames_5km.length];
194          for (int k=0; k<arrayNames.length; k++) {
195            HashMap table = SwathAdapter.getEmptyMetadataTable();
196            table.put("array_name", path.concat(arrayNames[k]));
197            table.put("lon_array_name", "mod06/Geolocation Fields/Longitude");
198            table.put("lat_array_name", "mod06/Geolocation Fields/Latitude");
199            table.put("XTrack", "Cell_Across_Swath_1km");
200            table.put("Track", "Cell_Along_Swath_1km");
201            table.put("geo_Track", "Cell_Along_Swath_5km");
202            table.put("geo_XTrack", "Cell_Across_Swath_5km");
203            table.put("scale_name", "scale_factor");
204            table.put("offset_name", "add_offset");
205            table.put("fill_value_name", "_FillValue");
206            table.put("range_name", arrayNames[k]);
207
208            table.put(SwathAdapter.geo_track_offset_name, Double.toString(2.0));
209            table.put(SwathAdapter.geo_xtrack_offset_name, Double.toString(2.0));
210            table.put(SwathAdapter.geo_track_skip_name, Double.toString(5.0));
211            table.put(SwathAdapter.geo_xtrack_skip_name, Double.toString(5.0148148148));
212
213            SwathAdapter swathAdapter = new SwathAdapter(reader, table);
214            swathAdapter.setDefaultStride(10);
215            defaultSubset = swathAdapter.getDefaultSubset();
216            adapters[k] = swathAdapter;
217          }
218
219          for (int k=0; k<arrayNames_5km.length; k++) {
220            HashMap table = SwathAdapter.getEmptyMetadataTable();
221            table.put("array_name", path.concat(arrayNames_5km[k]));
222            table.put("lon_array_name", "mod06/Geolocation Fields/Longitude");
223            table.put("lat_array_name", "mod06/Geolocation Fields/Latitude");
224            table.put("XTrack", "Cell_Across_Swath_5km");
225            table.put("Track", "Cell_Along_Swath_5km");
226            table.put("geo_Track", "Cell_Along_Swath_5km");
227            table.put("geo_XTrack", "Cell_Across_Swath_5km");
228            table.put("scale_name", "scale_factor");
229            table.put("offset_name", "add_offset");
230            table.put("fill_value_name", "_FillValue");
231            table.put("range_name", arrayNames_5km[k]);
232
233            SwathAdapter swathAdapter = new SwathAdapter(reader, table);
234            defaultSubset = swathAdapter.getDefaultSubset();
235            adapters[arrayNames.length+k] = swathAdapter;
236          }
237
238          categories = DataCategory.parseCategories("2D grid;GRID-2D;");
239       }
240       else if (name.startsWith("CAL_LID_L1")) {
241         HashMap table = ProfileAlongTrack.getEmptyMetadataTable();
242         table.put(ProfileAlongTrack.array_name, "Total_Attenuated_Backscatter_532");
243         table.put(ProfileAlongTrack.ancillary_file_name, "/edu/wisc/ssec/mcidasv/data/hydra/resources/calipso/altitude");
244         table.put(ProfileAlongTrack.trackDim_name, "dim0");
245         table.put(ProfileAlongTrack.vertDim_name, "dim1");
246         table.put(ProfileAlongTrack.profileTime_name, "Profile_Time");
247         table.put(ProfileAlongTrack.longitude_name, "Longitude");
248         table.put(ProfileAlongTrack.latitude_name, "Latitude");
249         ProfileAlongTrack adapter = new Calipso2D(reader, table);
250         ProfileAlongTrack3D adapter3D = new ProfileAlongTrack3D(adapter);
251         HashMap subset = adapter.getDefaultSubset();
252         adapters[0] = adapter3D;
253         defaultSubset = subset;
254         DataCategory.createCategory("ProfileAlongTrack");
255         categories = DataCategory.parseCategories("ProfileAlongTrack;ProfileAlongTrack;");
256
257         ArrayAdapter[] adapter_s = new ArrayAdapter[3];
258         table = ProfileAlongTrack.getEmptyMetadataTable();
259         table.put(ProfileAlongTrack.array_name, "Latitude");
260         table.put(ProfileAlongTrack.trackDim_name, "dim0");
261         table.put(ProfileAlongTrack.vertDim_name, "dim1");
262
263         adapter_s[0] = new ArrayAdapter(reader, table);
264         table = ProfileAlongTrack.getEmptyMetadataTable();
265         table.put(ProfileAlongTrack.array_name, "Surface_Elevation");
266         table.put(ProfileAlongTrack.trackDim_name, "dim0");
267         table.put(ProfileAlongTrack.vertDim_name, "dim1");
268
269         adapter_s[1] = new ArrayAdapter(reader, table);
270         table = ProfileAlongTrack.getEmptyMetadataTable();
271         table.put(ProfileAlongTrack.array_name, "Longitude");
272         table.put(ProfileAlongTrack.trackDim_name, "dim0");
273         table.put(ProfileAlongTrack.vertDim_name, "dim1");
274         adapter_s[2] = new ArrayAdapter(reader, table);
275
276         track_adapter = new TrackAdapter(adapter_s[2], adapter_s[0], adapter_s[1]);
277         properties.put("medianFilter", new String[] {Double.toString(8), Double.toString(16)});
278         properties.put("setBelowSfcMissing", new String[] {"true"});
279         hasTrackPreview = true;
280       }
281       else if (name.indexOf("2B-GEOPROF") > 0) {
282         HashMap table = ProfileAlongTrack.getEmptyMetadataTable();
283         table.put(ProfileAlongTrack.array_name, "2B-GEOPROF/Data Fields/Radar_Reflectivity");
284         table.put(ProfileAlongTrack.range_name, "2B-GEOPROF_RadarReflectivity");
285         table.put(ProfileAlongTrack.scale_name, "factor");
286         table.put(ProfileAlongTrack.offset_name, "offset");
287         table.put(ProfileAlongTrack.fill_value_name, "_FillValue");
288         table.put(ProfileAlongTrack.valid_range, "valid_range");
289         table.put(ProfileAlongTrack.ancillary_file_name, "/edu/wisc/ssec/mcidasv/data/hydra/resources/cloudsat/altitude");
290         table.put(ProfileAlongTrack.trackDim_name, "nray");
291         table.put(ProfileAlongTrack.vertDim_name, "nbin");
292         table.put(ProfileAlongTrack.profileTime_name, "2B-GEOPROF/Geolocation Fields/Profile_Time");
293         table.put(ProfileAlongTrack.longitude_name, "2B-GEOPROF/Geolocation Fields/Longitude");
294         table.put(ProfileAlongTrack.latitude_name, "2B-GEOPROF/Geolocation Fields/Latitude");
295         table.put(ProfileAlongTrack.product_name, "2B-GEOPROF");
296         ProfileAlongTrack adapter = new CloudSat2D(reader, table);
297         ProfileAlongTrack3D adapter3D = new ProfileAlongTrack3D(adapter);
298         HashMap subset = adapter.getDefaultSubset();
299         adapters[0] = adapter3D;
300         defaultSubset = subset;
301         DataCategory.createCategory("ProfileAlongTrack");
302         categories = DataCategory.parseCategories("ProfileAlongTrack;ProfileAlongTrack;");
303
304         ArrayAdapter[] adapter_s = new ArrayAdapter[3];
305         table = ProfileAlongTrack.getEmptyMetadataTable();
306         table.put(ProfileAlongTrack.array_name, "2B-GEOPROF/Geolocation Fields/Latitude");
307         table.put(ProfileAlongTrack.range_name, "Latitude");
308         table.put(ProfileAlongTrack.trackDim_name, "nray");
309         table.put(ProfileAlongTrack.vertDim_name, "nbin");
310         adapter_s[0] = new ArrayAdapter(reader, table);
311
312         table = ProfileAlongTrack.getEmptyMetadataTable();
313         table.put(ProfileAlongTrack.array_name, "2B-GEOPROF/Geolocation Fields/DEM_elevation");
314         table.put(ProfileAlongTrack.range_name, "DEM_elevation");
315         table.put(ProfileAlongTrack.trackDim_name, "nray");
316         table.put(ProfileAlongTrack.vertDim_name, "nbin");
317         adapter_s[1] = new ArrayAdapter(reader, table);
318
319         table = ProfileAlongTrack.getEmptyMetadataTable();
320         table.put(ProfileAlongTrack.array_name, "2B-GEOPROF/Geolocation Fields/Longitude");
321         table.put(ProfileAlongTrack.range_name, "Longitude");
322         table.put(ProfileAlongTrack.trackDim_name, "nray");
323         table.put(ProfileAlongTrack.vertDim_name, "nbin");
324         adapter_s[2] = new ArrayAdapter(reader, table);
325
326         //-track_adapter = new TrackAdapter(adapter_s[2], adapter_s[0], adapter_s[1]);
327         track_adapter = new TrackAdapter(adapter_s[2], adapter_s[0], null);
328         properties.put("medianFilter", new String[] {Double.toString(6), Double.toString(14)});
329         properties.put("setBelowSfcMissing", new String[] {"true"});
330         hasTrackPreview = true;
331       }
332       else if ( name.startsWith("MHSx_xxx_1B") && name.endsWith("h5")) {
333          HashMap table = SwathAdapter.getEmptyMetadataTable();
334          table.put("array_name", "U-MARF/EPS/MHSx_xxx_1B/DATA/Channel1");
335          table.put("lon_array_name", "U-MARF/EPS/IASI_xxx_1C/DATA/IMAGE_LON_ARRAY");
336          table.put("lat_array_name", "U-MARF/EPS/IASI_xxx_1C/DATA/IMAGE_LAT_ARRAY");
337          table.put("XTrack", "dim1");
338          table.put("Track", "dim0");
339          table.put("geo_XTrack", "dim1");
340          table.put("geo_Track", "dim0");
341          table.put("product_name", "MHSx_xxx_1B");
342          SwathAdapter swathAdapter = new SwathAdapter(reader, table);
343          adapters[0] = swathAdapter;
344          HashMap subset = swathAdapter.getDefaultSubset();
345          defaultSubset = subset;
346          categories = DataCategory.parseCategories("2D grid;GRID-2D;");
347       }
348       setProperties(properties);
349    }
350
351    public void initAfterUnpersistence() {
352      try {
353        setup();
354      } 
355      catch (Exception e) {
356      }
357    }
358
359    /**
360     * Make and insert the <code>DataChoice</code>-s for this
361     * <code>DataSource</code>.
362     */
363    public void doMakeDataChoices() {
364        DataChoice choice = null;
365        if (adapters != null) {
366          for (int idx=0; idx<adapters.length; idx++) {
367             try {
368               choice = doMakeDataChoice(idx, adapters[idx].getArrayName());
369               adapterMap.put(choice.getName(), adapters[idx]);
370             } 
371             catch (Exception e) {
372               e.printStackTrace();
373               System.out.println("doMakeDataChoice failed");
374             }
375
376             if (choice != null) {
377               addDataChoice(choice);
378             }
379          }
380        }
381    }
382
383    private DataChoice doMakeDataChoice(int idx, String var) throws Exception {
384        String name = var;
385        DataSelection dataSel = new MultiDimensionSubset(defaultSubset);
386        Hashtable subset = new Hashtable();
387        subset.put(new MultiDimensionSubset(), dataSel);
388        DirectDataChoice ddc = new DirectDataChoice(this, idx, name, name, categories, subset);
389
390        return ddc;
391    }
392
393    /**
394     * Check to see if this <code>HDFHydraDataSource</code> is equal to the object
395     * in question.
396     * @param o  object in question
397     * @return true if they are the same or equivalent objects
398     */
399    public boolean equals(Object o) {
400        if ( !(o instanceof MultiDimensionDataSource)) {
401            return false;
402        }
403        return (this == (MultiDimensionDataSource) o);
404    }
405
406    public MultiSpectralData getMultiSpectralData() {
407      return multiSpectData;
408    }
409
410    public String getDatasetName() {
411      return filename;
412    }
413
414    public void setDatasetName(String name) {
415      filename = name;
416    }
417
418    public HashMap getSubsetFromLonLatRect(MultiDimensionSubset select, GeoSelection geoSelection) {
419      GeoLocationInfo ginfo = geoSelection.getBoundingBox();
420      return adapters[0].getSubsetFromLonLatRect(select.getSubset(), ginfo.getMinLat(), ginfo.getMaxLat(),
421                                        ginfo.getMinLon(), ginfo.getMaxLon());
422    }
423
424
425    protected Data getDataInner(DataChoice dataChoice, DataCategory category,
426                                DataSelection dataSelection, Hashtable requestProperties)
427                                throws VisADException, RemoteException {
428
429        //- this hack keeps the HydraImageProbe from doing a getData()
430        //- TODO: need to use categories?
431        if (requestProperties != null) {
432          if ((requestProperties.toString()).equals("{prop.requester=MultiSpectral}")) {
433            return null;
434          }
435        }
436
437        GeoLocationInfo ginfo = null;
438        GeoSelection geoSelection = null;
439        
440        if ((dataSelection != null) && (dataSelection.getGeoSelection() != null)) {
441          geoSelection = (dataSelection.getGeoSelection().getBoundingBox() != null) ? dataSelection.getGeoSelection() :
442                                    dataChoice.getDataSelection().getGeoSelection();
443        }
444
445        if (geoSelection != null) {
446          ginfo = geoSelection.getBoundingBox();
447        }
448
449        Data data = null;
450        if (adapters == null) {
451          return data;
452        }
453
454        MultiDimensionAdapter adapter = null;
455
456        adapter = adapterMap.get(dataChoice.getName());
457
458        try {
459            HashMap subset = null;
460            if (ginfo != null) {
461              subset = adapter.getSubsetFromLonLatRect(ginfo.getMinLat(), ginfo.getMaxLat(),
462                                                       ginfo.getMinLon(), ginfo.getMaxLon(),
463                                                       geoSelection.getXStride(),
464                                                       geoSelection.getYStride(),
465                                                       geoSelection.getZStride());
466            }
467            else {
468              MultiDimensionSubset select = null;
469              Hashtable table = dataChoice.getProperties();
470              Enumeration keys = table.keys();
471              while (keys.hasMoreElements()) {
472                Object key = keys.nextElement();
473                if (key instanceof MultiDimensionSubset) {
474                  select = (MultiDimensionSubset) table.get(key);
475                }
476              }  
477
478              subset = select.getSubset();
479              
480              if (dataSelection != null) {
481                Hashtable props = dataSelection.getProperties();
482                if (props != null) {
483                }
484              }
485            }
486
487            if (subset != null) {
488              data = adapter.getData(subset);
489              data = applyProperties(data, requestProperties, subset);
490            }
491        } catch (Exception e) {
492            e.printStackTrace();
493            System.out.println("getData exception e=" + e);
494        }
495        return data;
496    }
497
498    protected Data applyProperties(Data data, Hashtable requestProperties, HashMap subset) 
499          throws VisADException, RemoteException {
500      Data new_data = data;
501
502      if (requestProperties == null) {
503        new_data = data;
504        return new_data;
505      }
506
507      if (requestProperties.containsKey("medianFilter")) {
508        String[] items = (String[]) requestProperties.get("medianFilter");
509        double window_lenx = Double.parseDouble(items[0]);
510        double window_leny = Double.parseDouble(items[1]);
511        GriddedSet domainSet = (GriddedSet) ((FlatField)data).getDomainSet();
512        int[] lens = domainSet.getLengths();
513        float[] range_values = (((FlatField)data).getFloats())[0];
514        range_values =
515           ProfileAlongTrack.medianFilter(range_values, lens[0], lens[1],
516                               (int)window_lenx, (int)window_leny);
517        ((FlatField)new_data).setSamples(new float[][] {range_values});
518      }
519      if (requestProperties.containsKey("setBelowSfcMissing")) {
520        String[] items = (String[]) requestProperties.get("setBelowSfcMissing");
521        FlatField track = (FlatField) track_adapter.getData(subset);
522        float[] sfcElev = (track.getFloats())[0];
523        FlatField field = (FlatField) new_data;
524        GriddedSet gset = (GriddedSet) field.getDomainSet();
525        float[][] samples = gset.getSamples(false);
526        int[] lens = gset.getLengths();
527        float[] range_values = (field.getFloats())[0];
528
529        int trkIdx = ((ProfileAlongTrack3D)adapters[0]).adapter2D.getTrackTupIdx();
530        int vrtIdx = ((ProfileAlongTrack3D)adapters[0]).adapter2D.getVertTupIdx();
531
532        int k = 0;
533        for (int j=0; j<lens[trkIdx]; j++) {
534          float val = sfcElev[j];
535          for (int i=0; i<lens[vrtIdx]; i++) {
536            if (vrtIdx < trkIdx) k = i + j*lens[0];
537            if (trkIdx < vrtIdx) k = j + i*lens[0];
538            if (samples[2][k] <= val || samples[2][k] < 0.0) {
539              range_values[k] = Float.NaN;
540            }
541          }
542        }
543        field.setSamples(new float[][] {range_values});
544      }
545      return new_data;
546    }
547
548    protected void initDataSelectionComponents(
549         List<DataSelectionComponent> components,
550             final DataChoice dataChoice) {
551
552      if (hasImagePreview) {
553        try {
554          FlatField image = (FlatField) getDataInner(dataChoice, null, null, null);
555          components.add(new PreviewSelection(dataChoice, image, null));
556        } catch (Exception e) {
557          System.out.println("Can't make PreviewSelection: "+e);
558          e.printStackTrace();
559        }
560      }
561      if (hasTrackPreview) {
562        try {
563          FlatField track = track_adapter.getData(track_adapter.getDefaultSubset());
564          components.add(new TrackSelection(dataChoice, track));
565        } catch (Exception e) {
566          System.out.println("Can't make PreviewSelection: "+e);
567          e.printStackTrace();
568        }
569      }
570    }
571}
572
573class TrackSelection extends DataSelectionComponent {
574      DataChoice dataChoice;
575      FlatField track;
576
577      double[] x_coords = new double[2];
578      double[] y_coords = new double[2];
579      boolean hasSubset = true;
580      MapProjectionDisplayJ3D mapProjDsp;
581      DisplayMaster dspMaster;
582
583      int trackStride;
584      int verticalStride;
585
586      JTextField trkStr;
587      JTextField vrtStr;
588
589
590   TrackSelection(DataChoice dataChoice, FlatField track) throws VisADException, RemoteException {
591        super("track");
592        this.dataChoice = dataChoice;
593        this.track = track;
594        mapProjDsp = new MapProjectionDisplayJ3D(MapProjectionDisplay.MODE_2Din3D);
595        mapProjDsp.enableRubberBanding(false);
596        dspMaster = mapProjDsp;
597        mapProjDsp.setMapProjection(getDataProjection());
598        LineDrawing trackDsp = new LineDrawing("track");
599        trackDsp.setLineWidth(2f);
600        trackDsp.setData(track);
601        mapProjDsp.addDisplayable(trackDsp);
602
603
604        MapLines mapLines  = new MapLines("maplines");
605        URL      mapSource =
606        mapProjDsp.getClass().getResource("/auxdata/maps/OUTLSUPU");
607        try {
608            BaseMapAdapter mapAdapter = new BaseMapAdapter(mapSource);
609            mapLines.setMapLines(mapAdapter.getData());
610            mapLines.setColor(java.awt.Color.cyan);
611            mapProjDsp.addDisplayable(mapLines);
612        } catch (Exception excp) {
613            System.out.println("Can't open map file " + mapSource);
614            System.out.println(excp);
615        }
616                                                                                                                                                     
617        mapLines  = new MapLines("maplines");
618        mapSource =
619        mapProjDsp.getClass().getResource("/auxdata/maps/OUTLSUPW");
620        try {
621            BaseMapAdapter mapAdapter = new BaseMapAdapter(mapSource);
622            mapLines.setMapLines(mapAdapter.getData());
623            mapLines.setColor(java.awt.Color.cyan);
624            mapProjDsp.addDisplayable(mapLines);
625        } catch (Exception excp) {
626            System.out.println("Can't open map file " + mapSource);
627            System.out.println(excp);
628        }
629                                                                                                                                                     
630        mapLines  = new MapLines("maplines");
631        mapSource =
632        mapProjDsp.getClass().getResource("/auxdata/maps/OUTLHPOL");
633        try {
634            BaseMapAdapter mapAdapter = new BaseMapAdapter(mapSource);
635            mapLines.setMapLines(mapAdapter.getData());
636            mapLines.setColor(java.awt.Color.cyan);
637            mapProjDsp.addDisplayable(mapLines);
638        } catch (Exception excp) {
639            System.out.println("Can't open map file " + mapSource);
640            System.out.println(excp);
641        }
642
643        final LineDrawing selectBox = new LineDrawing("select");
644        selectBox.setColor(Color.green);
645
646        final RubberBandBox rbb =
647            new RubberBandBox(RealType.Longitude, RealType.Latitude, 1);
648        rbb.setColor(Color.green);
649        rbb.addAction(new CellImpl() {
650          public void doAction()
651             throws VisADException, RemoteException
652           {
653              Gridded2DSet set = rbb.getBounds();
654              float[] low = set.getLow();
655              float[] hi = set.getHi();
656              x_coords[0] = low[0];
657              x_coords[1] = hi[0];
658              y_coords[0] = low[1];
659              y_coords[1] = hi[1];
660              
661              SampledSet[] sets = new SampledSet[4];
662              sets[0] = new Gridded2DSet(RealTupleType.SpatialEarth2DTuple, new float[][] {{low[0], hi[0]}, {low[1], low[1]}}, 2);
663              sets[1] = new Gridded2DSet(RealTupleType.SpatialEarth2DTuple, new float[][] {{hi[0], hi[0]}, {low[1], hi[1]}}, 2);
664              sets[2] = new Gridded2DSet(RealTupleType.SpatialEarth2DTuple, new float[][] {{hi[0], low[0]}, {hi[1], hi[1]}}, 2);
665              sets[3] = new Gridded2DSet(RealTupleType.SpatialEarth2DTuple, new float[][] {{low[0], low[0]}, {hi[1], low[1]}}, 2);
666              UnionSet uset = new UnionSet(sets);
667              selectBox.setData(uset);
668           }
669        });
670        dspMaster.addDisplayable(rbb);
671        dspMaster.addDisplayable(selectBox);
672
673        dspMaster.draw();
674   }
675
676       public MapProjection getDataProjection() {
677         MapProjection mp = null;
678         try {
679           mp = new ProjectionCoordinateSystem(new LatLonProjection());
680         } catch (Exception e) {
681             System.out.println(" getDataProjection"+e);
682         }
683         return mp;
684       }
685
686      protected JComponent doMakeContents() {
687        try {
688          JPanel panel = new JPanel(new BorderLayout());
689          panel.add("Center", dspMaster.getDisplayComponent());
690
691          JPanel stridePanel = new JPanel(new FlowLayout());
692          trkStr = new JTextField(Integer.toString(5), 3);
693          vrtStr = new JTextField(Integer.toString(2), 3);
694          trkStr.addActionListener(new ActionListener() {
695              public void actionPerformed(ActionEvent ae) {
696                setTrackStride(Integer.valueOf(trkStr.getText().trim()));
697              }
698          });
699          vrtStr.addActionListener(new ActionListener() {
700              public void actionPerformed(ActionEvent ae) {
701                setVerticalStride(Integer.valueOf(vrtStr.getText().trim()));
702              }
703          });
704
705          stridePanel.add(new JLabel("track stride: "));
706          stridePanel.add(trkStr);
707          stridePanel.add(new JLabel("vertical stride: "));
708          stridePanel.add(vrtStr);
709          panel.add("South", stridePanel);
710
711          return panel;
712        }
713        catch (Exception e) {
714          System.out.println(e);
715        }
716        return null;
717      }
718                                                                                                                                                     
719      public void setTrackStride(int stride) {
720        trackStride = stride;
721      }
722
723      public void setVerticalStride(int stride) {
724        verticalStride = stride;
725      }
726
727      public void setTrackStride() {
728        trackStride = Integer.valueOf(trkStr.getText().trim());
729      }
730
731      public void setVerticalStride() {
732        verticalStride = Integer.valueOf(vrtStr.getText().trim());
733      }
734
735      public void applyToDataSelection(DataSelection dataSelection) {
736         setTrackStride();
737         setVerticalStride();
738         if (hasSubset) {
739           GeoSelection geoSelect = new GeoSelection(
740                new GeoLocationInfo(y_coords[1], x_coords[0], y_coords[0], x_coords[1]));
741           geoSelect.setXStride(trackStride);
742           geoSelect.setYStride(verticalStride);
743           dataSelection.setGeoSelection(geoSelect);
744         }
745      }
746}