001/*
002 * This file is part of McIDAS-V
003 *
004 * Copyright 2007-2017
005 * Space Science and Engineering Center (SSEC)
006 * University of Wisconsin - Madison
007 * 1225 W. Dayton Street, Madison, WI 53706, USA
008 * https://www.ssec.wisc.edu/mcidas
009 * 
010 * All Rights Reserved
011 * 
012 * McIDAS-V is built on Unidata's IDV and SSEC's VisAD libraries, and
013 * some McIDAS-V source code is based on IDV and VisAD source code.  
014 * 
015 * McIDAS-V is free software; you can redistribute it and/or modify
016 * it under the terms of the GNU Lesser Public License as published by
017 * the Free Software Foundation; either version 3 of the License, or
018 * (at your option) any later version.
019 * 
020 * McIDAS-V is distributed in the hope that it will be useful,
021 * but WITHOUT ANY WARRANTY; without even the implied warranty of
022 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
023 * GNU Lesser Public License for more details.
024 * 
025 * You should have received a copy of the GNU Lesser Public License
026 * along with this program.  If not, see http://www.gnu.org/licenses.
027 */
028package edu.wisc.ssec.mcidasv.data.hydra;
029
030import java.io.File;
031import java.rmi.RemoteException;
032import java.util.Enumeration;
033import java.util.HashMap;
034import java.util.Hashtable;
035import java.util.List;
036import java.util.Map;
037
038import javax.swing.JOptionPane;
039
040import org.slf4j.Logger;
041import org.slf4j.LoggerFactory;
042
043import ucar.nc2.NetcdfFile;
044import ucar.nc2.Variable;
045import ucar.unidata.data.DataCategory;
046import ucar.unidata.data.DataChoice;
047import ucar.unidata.data.DataSelection;
048import ucar.unidata.data.DataSelectionComponent;
049import ucar.unidata.data.DataSourceDescriptor;
050import ucar.unidata.data.DirectDataChoice;
051import ucar.unidata.data.GeoLocationInfo;
052import ucar.unidata.data.GeoSelection;
053import ucar.unidata.util.Misc;
054
055import visad.Data;
056import visad.FlatField;
057import visad.GriddedSet;
058import visad.VisADException;
059
060import edu.wisc.ssec.mcidasv.data.HydraDataSource;
061import edu.wisc.ssec.mcidasv.data.PreviewSelection;
062
063/**
064 * A data source for Multi Dimension Data 
065 */
066
067public class MultiDimensionDataSource extends HydraDataSource {
068
069    private static final Logger logger = LoggerFactory.getLogger(MultiDimensionDataSource.class);
070
071    /** Sources file */
072    protected String filename;
073
074    protected MultiDimensionReader reader;
075
076    protected MultiDimensionAdapter[] adapters = null;
077    protected Map[] defaultSubsets = null;
078    private Map<String, MultiDimensionAdapter> adapterMap = new HashMap<>();
079    protected Hashtable[] propsArray = null;
080    protected List[] categoriesArray = null;
081
082    protected SpectrumAdapter spectrumAdapter;
083
084    private static final String DATA_DESCRIPTION = "Multi Dimension Data";
085
086    private Map<String, double[]> defaultSubset;
087    public TrackAdapter track_adapter;
088    private MultiSpectralData multiSpectData;
089
090    private List categories;
091    private boolean hasImagePreview = false;
092    private boolean hasTrackPreview = false;
093    
094    private TrackSelection trackSelection = null;
095
096    /**
097     * Zero-argument constructor for construction via unpersistence.
098     */
099    public MultiDimensionDataSource() {}
100
101    /**
102     * Construct a new HYDRA hdf data source.
103     * @param  descriptor  descriptor for this {@code DataSource}
104     * @param  fileName  name of the hdf file to read
105     * @param  properties  hashtable of properties
106     *
107     * @throws VisADException problem creating data
108     */
109    public MultiDimensionDataSource(DataSourceDescriptor descriptor,
110                                 String fileName, Hashtable properties)
111            throws VisADException {
112        this(descriptor, Misc.newList(fileName), properties);
113    }
114
115    /**
116     * Construct a new HYDRA hdf data source.
117     * @param  descriptor  descriptor for this {@code DataSource}
118     * @param  newSources  List of filenames
119     * @param  properties  hashtable of properties
120     *
121     * @throws VisADException problem creating data
122     */
123    public MultiDimensionDataSource(DataSourceDescriptor descriptor,
124                                 List newSources, Hashtable properties)
125            throws VisADException {
126        super(descriptor, newSources, DATA_DESCRIPTION, properties);
127
128        this.filename = (String) sources.get(0);
129
130        try {
131          setup();
132        } catch (Exception e) {
133          throw new VisADException("could not set up MultiDimensionDataSource", e);
134        }
135    }
136
137    public void setup() throws Exception {
138
139        try {
140          if (filename.contains("MYD02SSH")) { // get file union
141            String other = (String) sources.get(1);
142            if (filename.endsWith("nav.hdf")) {
143              String tmp = filename;
144              filename = other;
145              other = tmp;
146            }
147            reader = NetCDFFile.makeUnion(filename, other);
148          }
149          else {
150            reader = new NetCDFFile(filename);
151          }
152        } catch (Exception e) {
153          logger.error("Cannot create NetCDF reader for file: " + filename, e);
154        }
155
156        adapters = new MultiDimensionAdapter[2];
157        defaultSubsets = new HashMap[2]; 
158        Hashtable<String, String[]> properties = new Hashtable<>();
159        
160        String name = (new File(filename)).getName();
161
162        if (name.startsWith("MOD04") || name.startsWith("MYD04")) {
163          Map<String, Object> table = SwathAdapter.getEmptyMetadataTable();
164          table.put("array_name", "mod04/Data_Fields/Optical_Depth_Land_And_Ocean");
165          table.put("lon_array_name", "mod04/Geolocation_Fields/Longitude");
166          table.put("lat_array_name", "mod04/Geolocation_Fields/Latitude");
167          table.put("XTrack", "Cell_Across_Swath");
168          table.put("Track", "Cell_Along_Swath");
169          table.put("geo_Track", "Cell_Along_Swath");
170          table.put("geo_XTrack", "Cell_Across_Swath");
171          table.put("scale_name", "scale_factor");
172          table.put("offset_name", "add_offset");
173          table.put("fill_value_name", "_FillValue");
174          table.put("range_name", "Optical_Depth_Land_And_Ocean");
175          adapters[0] = new SwathAdapter(reader, table);
176          categories = DataCategory.parseCategories("2D grid;GRID-2D;");
177          defaultSubset = adapters[0].getDefaultSubset();
178          defaultSubsets[0] = defaultSubset;
179          hasImagePreview = true;
180        }
181        else if (name.startsWith("MOD06") || name.startsWith("MYD06")) {
182          hasImagePreview = true;
183          String path = "mod06/Data_Fields/";
184          String[] arrayNames = new String[] {"Cloud_Optical_Thickness", "Cloud_Effective_Radius", "Cloud_Water_Path"};
185          String[] arrayNames_5km = new String[] {"Cloud_Top_Pressure", "Cloud_Top_Temperature", "Cloud_Fraction"};
186  
187          adapters = new MultiDimensionAdapter[arrayNames.length+arrayNames_5km.length];
188          defaultSubsets = new HashMap[arrayNames.length+arrayNames_5km.length];
189          categoriesArray = new List[adapters.length];
190
191          
192          for (int k=0; k<arrayNames.length; k++) {
193            Map<String, Object> table = SwathAdapter.getEmptyMetadataTable();
194            table.put("array_name", path.concat(arrayNames[k]));
195            table.put("lon_array_name", "mod06/Geolocation_Fields/Longitude");
196            table.put("lat_array_name", "mod06/Geolocation_Fields/Latitude");
197            table.put("XTrack", "Cell_Across_Swath_1km");
198            table.put("Track", "Cell_Along_Swath_1km");
199            table.put("geo_Track", "Cell_Along_Swath_5km");
200            table.put("geo_XTrack", "Cell_Across_Swath_5km");
201            table.put("scale_name", "scale_factor");
202            table.put("offset_name", "add_offset");
203            table.put("fill_value_name", "_FillValue");
204            table.put("range_name", arrayNames[k]);
205
206            table.put(SwathAdapter.geo_track_offset_name, Double.toString(2.0));
207            table.put(SwathAdapter.geo_xtrack_offset_name, Double.toString(2.0));
208            table.put(SwathAdapter.geo_track_skip_name, Double.toString(5.0));
209            table.put(SwathAdapter.geo_xtrack_skip_name, Double.toString(5.0148148148));
210
211            SwathAdapter swathAdapter = new SwathAdapter(reader, table);
212            swathAdapter.setDefaultStride(10);
213            defaultSubset = swathAdapter.getDefaultSubset();
214            adapters[k] = swathAdapter;
215            defaultSubsets[k] = defaultSubset;
216            categoriesArray[k] = DataCategory.parseCategories("1km swath;GRID-2D;");
217          }
218
219          for (int k=0; k<arrayNames_5km.length; k++) {
220            Map<String, Object> 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            defaultSubsets[arrayNames.length+k] = defaultSubset;
237            categoriesArray[arrayNames.length+k] = DataCategory.parseCategories("5km swath;GRID-2D;");
238          }
239       }
240       else if (name.startsWith("a1") && name.contains("mod06")) {
241          hasImagePreview = true;
242          String[] arrayNames = new String[] {"Cloud_Optical_Thickness", "Cloud_Effective_Radius", "Cloud_Water_Path"};
243          String[] arrayNames_5km = new String[] {"Cloud_Top_Pressure", "Cloud_Top_Temperature", "Cloud_Fraction"};
244
245          adapters = new MultiDimensionAdapter[arrayNames.length+arrayNames_5km.length];
246          defaultSubsets = new HashMap[arrayNames.length+arrayNames_5km.length];
247          categoriesArray = new List[adapters.length];
248
249
250          for (int k=0; k<arrayNames.length; k++) {
251            Map<String, Object> table = SwathAdapter.getEmptyMetadataTable();
252            table.put("array_name", arrayNames[k]);
253            table.put("lon_array_name", "Longitude");
254            table.put("lat_array_name", "Latitude");
255            table.put("array_dimension_names", new String[] {"Cell_Along_Swath_1km", "Cell_Across_Swath_1km"});
256            table.put("lon_array_dimension_names", new String[] {"Cell_Along_Swath_1km", "Cell_Across_Swath_1km"});
257            table.put("lat_array_dimension_names", new String[] {"Cell_Along_Swath_1km", "Cell_Across_Swath_1km"});
258            table.put("XTrack", "Cell_Across_Swath_1km");
259            table.put("Track", "Cell_Along_Swath_1km");
260            table.put("geo_Track", "Cell_Along_Swath_5km");
261            table.put("geo_XTrack", "Cell_Across_Swath_5km");
262            table.put("scale_name", "scale_factor");
263            table.put("offset_name", "add_offset");
264            table.put("fill_value_name", "_FillValue");
265            table.put("range_name", arrayNames[k]);
266
267            table.put(SwathAdapter.geo_track_offset_name, Double.toString(2.0));
268            table.put(SwathAdapter.geo_xtrack_offset_name, Double.toString(2.0));
269            table.put(SwathAdapter.geo_track_skip_name, Double.toString(5.0));
270            table.put(SwathAdapter.geo_xtrack_skip_name, Double.toString(5.0148148148));
271
272            SwathAdapter swathAdapter = new SwathAdapter(reader, table);
273            swathAdapter.setDefaultStride(10);
274            defaultSubset = swathAdapter.getDefaultSubset();
275            adapters[k] = swathAdapter;
276            defaultSubsets[k] = defaultSubset;
277            categoriesArray[k] = DataCategory.parseCategories("1km swath;GRID-2D;");
278          }
279
280          for (int k=0; k<arrayNames_5km.length; k++) {
281            Map<String, Object> table = SwathAdapter.getEmptyMetadataTable();
282            table.put("array_name", arrayNames_5km[k]);
283            table.put("lon_array_name", "Longitude");
284            table.put("lat_array_name", "Latitude");
285            table.put("array_dimension_names", new String[] {"Cell_Along_Swath_5km", "Cell_Across_Swath_5km"});
286            table.put("lon_array_dimension_names", new String[] {"Cell_Along_Swath_5km", "Cell_Across_Swath_5km"});
287            table.put("lat_array_dimension_names", new String[] {"Cell_Along_Swath_5km", "Cell_Across_Swath_5km"});
288            table.put("XTrack", "Cell_Across_Swath_5km");
289            table.put("Track", "Cell_Along_Swath_5km");
290            table.put("geo_Track", "Cell_Along_Swath_5km");
291            table.put("geo_XTrack", "Cell_Across_Swath_5km");
292            table.put("scale_name", "scale_factor");
293            table.put("offset_name", "add_offset");
294            table.put("fill_value_name", "_FillValue");
295            table.put("range_name", arrayNames_5km[k]);
296
297            SwathAdapter swathAdapter = new SwathAdapter(reader, table);
298            defaultSubset = swathAdapter.getDefaultSubset();
299            adapters[arrayNames.length+k] = swathAdapter;
300            defaultSubsets[arrayNames.length+k] = defaultSubset;
301            categoriesArray[arrayNames.length+k] = DataCategory.parseCategories("5km swath;GRID-2D;");
302          }
303       }
304       else if (name.contains("HSRL2_B200") && name.endsWith(".h5")) {
305         Map<String, Object> table;
306         adapters = new MultiDimensionAdapter[5];
307         defaultSubsets = new HashMap[5];
308         propsArray = new Hashtable[5];
309         
310         String dataPath = "DataProducts/";
311         String[] arrayNames = new String[] {"532_total_attn_bsc", "1064_total_attn_bsc", "355_total_attn_bsc"};
312         String[] rangeNames = new String[] {"Total_Attenuated_Backscatter_532", "Total_Attenuated_Backscatter_1064", "Total_Attenuated_Backscatter_355"};
313         
314         String[] arrayNameAOT = new String[] {"532_AOT_hi_col", "355_AOT_hi_col"};
315         String[] rangeNamesAOT = new String[] {};
316         
317
318         for (int k=0; k<arrayNames.length; k++) {
319            table = ProfileAlongTrack.getEmptyMetadataTable();
320            table.put(ProfileAlongTrack.array_name, dataPath+arrayNames[k]);
321            table.put(ProfileAlongTrack.range_name, rangeNames[k]);
322            table.put(ProfileAlongTrack.trackDim_name, "dim0");
323            table.put(ProfileAlongTrack.vertDim_name, "dim1");
324            table.put(ProfileAlongTrack.profileTime_name, "ApplanixIMU/gps_time");
325            table.put(ProfileAlongTrack.longitude_name, "ApplanixIMU/gps_lon");
326            table.put(ProfileAlongTrack.latitude_name, "ApplanixIMU/gps_lat");
327            table.put("array_dimension_names", new String[] {"dim0", "dim1"});
328            ProfileAlongTrack adapter = new HSRL2D(reader, table);
329            ProfileAlongTrack3D adapter3D = new ProfileAlongTrack3D(adapter);
330            Map<String, double[]> subset = adapter.getDefaultSubset();
331            adapters[k] = adapter3D;
332            defaultSubset = subset;
333            defaultSubsets[k] = defaultSubset;
334
335            properties.put("medianFilter", new String[] {Integer.toString(adapter.getMedianFilterWindowHeight()), Integer.toString(adapter.getMedianFilterWindowWidth())});
336            properties.put("setBelowSfcMissing", new String[] {"true"});
337            propsArray[k] = properties;
338         }
339         
340         DataCategory.createCategory("ProfileAlongTrack");
341         categories = DataCategory.parseCategories("ProfileAlongTrack;ProfileAlongTrack;");
342
343         hasTrackPreview = true;
344
345         ArrayAdapter[] adapter_s = new ArrayAdapter[3];
346         table = ProfileAlongTrack.getEmptyMetadataTable();
347         table.put(ProfileAlongTrack.array_name, "ApplanixIMU/gps_lat");
348         table.put(ProfileAlongTrack.trackDim_name, "dim0");
349         table.put(ProfileAlongTrack.vertDim_name, "dim1");
350         table.put("array_dimension_names", new String[] {"dim0", "dim1"});
351         adapter_s[0] = new ArrayAdapter(reader, table);
352
353         table = ProfileAlongTrack.getEmptyMetadataTable();
354         table.put(ProfileAlongTrack.array_name, "UserInput/DEM_altitude");
355         table.put(ProfileAlongTrack.trackDim_name, "dim0");
356         table.put(ProfileAlongTrack.vertDim_name, "dim1");
357         table.put("array_dimension_names", new String[] {"dim0", "dim1"});
358         adapter_s[1] = new ArrayAdapter(reader, table);
359         /*
360         adapter_s[1].setRangeProcessor(new RangeProcessor() { // Eventually handle unit conversions better.
361              public float[] processRange(float[] fvals, Map<String, double[]> subset) {
362                 for (int i=0; i<fvals.length; i++) {
363                    fvals[i] *= 1000; //km -> m
364                 }
365                 return fvals;
366              }
367         });
368         */
369
370         table = ProfileAlongTrack.getEmptyMetadataTable();
371         table.put(ProfileAlongTrack.array_name, "ApplanixIMU/gps_lon");
372         table.put(ProfileAlongTrack.trackDim_name, "dim0");
373         table.put(ProfileAlongTrack.vertDim_name, "dim1");
374         table.put("array_dimension_names", new String[] {"dim0", "dim1"});
375         adapter_s[2] = new ArrayAdapter(reader, table);
376
377         TrackDomain track_domain = new TrackDomain(adapter_s[2], adapter_s[0], adapter_s[1]);
378         track_adapter = new TrackAdapter(track_domain, adapter_s[1]);
379         
380         TrackAdapter trkAdapter = new TrackAdapter(new TrackDomain(adapter_s[2], adapter_s[0], adapter_s[1]), adapter_s[1]);
381         trkAdapter.setName("Track3D");
382         
383         trkAdapter = new TrackAdapter(new TrackDomain(adapter_s[2], adapter_s[0]), adapter_s[1]);
384         trkAdapter.setName("Track2D");
385       }
386       else if (name.startsWith("CAL_LID_L1")) {
387
388           // Make sure the variables we need are present. If not, this is not a valid
389           // L1 CALIPSO file McV can work with.
390           
391           if (! ((hasVariable("Latitude")) &&
392                          (hasVariable("Longitude")) &&
393                          (hasVariable("Surface_Elevation")) &&
394                          (hasVariable("Tropopause_Height")) &&
395                          (hasVariable("Total_Attenuated_Backscatter_532"))) 
396                  ) {
397                   // Pop up a dialog letting user know we can't work wit this data
398                        String msg = "McIDAS-V is unable to read this Level 1 CALIPSO file.\n" +
399                                        "If you believe this is a valid file which should be supported,\n" +
400                                        "please contact the MUG or post a message on the MUG Forum.";
401                        Object[] params = { msg };
402                        JOptionPane.showMessageDialog(null, params, "Data Validity Test Failure", JOptionPane.OK_OPTION);
403                        throw new Exception("Unable to load CALIPSO data");
404           }
405           
406         adapters = new MultiDimensionAdapter[4];
407         defaultSubsets = new HashMap[4];
408         propsArray = new Hashtable[4]; 
409         
410         Map<String, Object> table = ProfileAlongTrack.getEmptyMetadataTable();
411         table.put(ProfileAlongTrack.array_name, "Total_Attenuated_Backscatter_532");
412         table.put(ProfileAlongTrack.ancillary_file_name, "/edu/wisc/ssec/mcidasv/data/hydra/resources/calipso/altitude");
413         table.put(ProfileAlongTrack.trackDim_name, "dim0");
414         table.put(ProfileAlongTrack.vertDim_name, "dim1");
415         table.put(ProfileAlongTrack.profileTime_name, "Profile_Time");
416         table.put(ProfileAlongTrack.longitude_name, "Longitude");
417         table.put(ProfileAlongTrack.latitude_name, "Latitude");
418         table.put("array_dimension_names", new String[] {"dim0", "dim1"}); 
419         ProfileAlongTrack adapter = new Calipso2D(reader, table);
420         ProfileAlongTrack3D adapter3D = new ProfileAlongTrack3D(adapter);
421         Map<String, double[]> subset = adapter.getDefaultSubset();
422         adapters[0] = adapter3D;
423         defaultSubset = subset;
424         defaultSubsets[0] = defaultSubset;
425         DataCategory.createCategory("ProfileAlongTrack");
426         categories = DataCategory.parseCategories("ProfileAlongTrack;ProfileAlongTrack;");
427
428         properties.put("medianFilter", new String[] {Integer.toString(adapter.getMedianFilterWindowHeight()), Integer.toString(adapter.getMedianFilterWindowWidth())});
429         properties.put("setBelowSfcMissing", new String[] {"true"});
430         propsArray[0] = properties;
431
432         ArrayAdapter[] adapter_s = new ArrayAdapter[3];
433         table = ProfileAlongTrack.getEmptyMetadataTable();
434         table.put(ProfileAlongTrack.array_name, "Latitude");
435         table.put(ProfileAlongTrack.trackDim_name, "dim0");
436         table.put(ProfileAlongTrack.vertDim_name, "dim1");
437         table.put("array_dimension_names", new String[] {"dim0", "dim1"}); 
438         adapter_s[0] = new ArrayAdapter(reader, table);
439
440         table = ProfileAlongTrack.getEmptyMetadataTable();
441         table.put(ProfileAlongTrack.array_name, "Surface_Elevation");
442         table.put(ProfileAlongTrack.trackDim_name, "dim0");
443         table.put(ProfileAlongTrack.vertDim_name, "dim1");
444         table.put("array_dimension_names", new String[] {"dim0", "dim1"}); 
445         adapter_s[1] = new ArrayAdapter(reader, table);
446         adapter_s[1].setRangeProcessor(new RangeProcessor() { // Eventually handle unit conversions better.
447              public float[] processRange(float[] fvals, Map<String, double[]> subset) {
448                 for (int i=0; i<fvals.length; i++) {
449                    fvals[i] *= 1000; //km -> m 
450                 }
451                 return fvals;
452              }
453         });
454
455         table = ProfileAlongTrack.getEmptyMetadataTable();
456         table.put(ProfileAlongTrack.array_name, "Longitude");
457         table.put(ProfileAlongTrack.trackDim_name, "dim0");
458         table.put(ProfileAlongTrack.vertDim_name, "dim1");
459         table.put("array_dimension_names", new String[] {"dim0", "dim1"}); 
460         adapter_s[2] = new ArrayAdapter(reader, table);
461
462         TrackDomain track_domain = new TrackDomain(adapter_s[2], adapter_s[0], adapter_s[1]);
463         track_adapter = new TrackAdapter(track_domain, adapter_s[1]);
464
465         table = ProfileAlongTrack.getEmptyMetadataTable();
466         table.put(ProfileAlongTrack.array_name, "Tropopause_Height");
467         table.put(ProfileAlongTrack.trackDim_name, "dim0");
468         table.put(ProfileAlongTrack.vertDim_name, "dim1");
469         table.put("array_dimension_names", new String[] {"dim0", "dim1"}); 
470         ArrayAdapter trop_height = new ArrayAdapter(reader, table);
471         track_domain = new TrackDomain(adapter_s[2], adapter_s[0], trop_height);
472         adapters[1] = new TrackAdapter(track_domain, trop_height);
473         defaultSubsets[1] = adapters[1].getDefaultSubset();
474
475         adapters[2] = new TrackAdapter(new TrackDomain(adapter_s[2], adapter_s[0], adapter_s[1]), adapter_s[1]);
476         ((TrackAdapter)adapters[2]).setName("Track3D");
477         defaultSubsets[2] = adapters[2].getDefaultSubset();
478
479         adapters[3] = new TrackAdapter(new TrackDomain(adapter_s[2], adapter_s[0]), adapter_s[1]);
480         ((TrackAdapter)adapters[3]).setName("Track2D");
481         defaultSubsets[3] = adapters[3].getDefaultSubset();
482         
483
484         hasTrackPreview = true;
485       }
486       else if (name.startsWith("CAL_LID_L2")) {
487           
488           // Make sure the variables we need are present. If not, this is not a valid
489           // L2 CALIPSO file McV can work with.
490           
491           if (! ((hasVariable("Latitude")) &&
492                          (hasVariable("Longitude")) &&
493                          (hasVariable("DEM_Surface_Elevation")) &&
494                          (hasVariable("Layer_Top_Altitude"))) 
495                  ) {
496                   // Pop up a dialog letting user know we can't work wit this data
497                        String msg = "McIDAS-V is unable to read this Level 2 CALIPSO file.\n" +
498                                        "If you believe this is a valid file which should be supported,\n" +
499                                        "please contact the MUG or post a message on the MUG Forum.";
500                        Object[] params = { msg };
501                        JOptionPane.showMessageDialog(null, params, "Data Validity Test Failure", JOptionPane.OK_OPTION);
502                        throw new Exception("Unable to load CALIPSO data");
503           }
504           
505         adapters = new MultiDimensionAdapter[4];
506         defaultSubsets = new HashMap[4];
507         propsArray = new Hashtable[4];
508
509         ArrayAdapter[] adapter_s = new ArrayAdapter[3];
510
511         adapter_s[0] = createTrackVertArrayAdapter("Longitude");
512         adapter_s[1] = createTrackVertArrayAdapter("Latitude");
513         adapter_s[2] = createTrackVertArrayAdapter("DEM_Surface_Elevation");
514
515         TrackDomain track_domain = new TrackDomain(adapter_s[0], adapter_s[1], adapter_s[2]);
516         track_adapter = new TrackAdapter(track_domain, adapter_s[2]);
517         adapters[1] = track_adapter;
518         defaultSubsets[1] = track_adapter.getDefaultSubset();
519
520         ArrayAdapter layer_top_altitude = createTrackVertArrayAdapter("Layer_Top_Altitude");
521
522         RangeProcessor rngProcessor =
523             new RangeProcessor(1.0f, 0.0f, -Float.MAX_VALUE, Float.MAX_VALUE, -9999.0f);
524         layer_top_altitude.setRangeProcessor(rngProcessor);
525
526         track_domain = new TrackDomain(adapter_s[0], adapter_s[1], layer_top_altitude);
527         adapters[0] = new TrackAdapter(track_domain, layer_top_altitude);
528         defaultSubsets[0] = adapters[0].getDefaultSubset();
529
530         /** another layer, how to show all?
531         adapters[2] = new TrackAdapter(track_domain, layer_top_altitude);
532         ((TrackAdapter)adapters[2]).setListIndex(1);
533         defaultSubsets[2] = adapters[2].getDefaultSubset();
534         */
535
536         adapters[2] = new TrackAdapter(new TrackDomain(adapter_s[0], adapter_s[1]), adapter_s[2]);
537         ((TrackAdapter)adapters[2]).setName("Track2D");
538         defaultSubsets[2] = adapters[2].getDefaultSubset();
539
540         adapters[3] = new TrackAdapter(new TrackDomain(adapter_s[0], adapter_s[1], adapter_s[2]), adapter_s[2]);
541         ((TrackAdapter)adapters[3]).setName("Track3D");
542         defaultSubsets[3] = adapters[3].getDefaultSubset();
543
544         DataCategory.createCategory("ProfileAlongTrack");
545         categories = DataCategory.parseCategories("ProfileAlongTrack;ProfileAlongTrack;");
546
547         hasTrackPreview = true;
548       }
549       else if (name.indexOf("2B-GEOPROF") > 0) {
550         adapters = new MultiDimensionAdapter[4];
551         defaultSubsets = new HashMap[4];
552         propsArray = new Hashtable[4];
553
554         Map<String, Object> table = ProfileAlongTrack.getEmptyMetadataTable();
555         table.put(ProfileAlongTrack.array_name, "2B-GEOPROF/Data_Fields/Radar_Reflectivity");
556         table.put(ProfileAlongTrack.range_name, "2B-GEOPROF_RadarReflectivity");
557         table.put(ProfileAlongTrack.scale_name, "factor");
558         table.put(ProfileAlongTrack.offset_name, "offset");
559         table.put(ProfileAlongTrack.fill_value_name, "_FillValue");
560         table.put(ProfileAlongTrack.valid_range, "valid_range");
561         table.put(ProfileAlongTrack.ancillary_file_name, "/edu/wisc/ssec/mcidasv/data/hydra/resources/cloudsat/altitude");
562         table.put(ProfileAlongTrack.trackDim_name, "nray");
563         table.put(ProfileAlongTrack.vertDim_name, "nbin");
564         table.put(ProfileAlongTrack.profileTime_name, "2B-GEOPROF/Geolocation_Fields/Profile_Time");
565         table.put(ProfileAlongTrack.longitude_name, "2B-GEOPROF/Geolocation_Fields/Longitude");
566         table.put(ProfileAlongTrack.latitude_name, "2B-GEOPROF/Geolocation_Fields/Latitude");
567         table.put(ProfileAlongTrack.product_name, "2B-GEOPROF");
568         ProfileAlongTrack adapter = new CloudSat2D(reader, table);
569         ProfileAlongTrack3D adapter3D = new ProfileAlongTrack3D(adapter);
570         Map<String, double[]> subset = adapter.getDefaultSubset();
571         adapters[0] = adapter3D;
572         defaultSubset = subset;
573         defaultSubsets[0] = defaultSubset;
574         DataCategory.createCategory("ProfileAlongTrack");
575         categories = DataCategory.parseCategories("ProfileAlongTrack;ProfileAlongTrack;");
576
577         ArrayAdapter[] adapter_s = new ArrayAdapter[3];
578         table = ProfileAlongTrack.getEmptyMetadataTable();
579         table.put(ProfileAlongTrack.array_name, "2B-GEOPROF/Geolocation_Fields/Latitude");
580         table.put(ProfileAlongTrack.range_name, "Latitude");
581         table.put(ProfileAlongTrack.trackDim_name, "nray");
582         adapter_s[0] = new ArrayAdapter(reader, table);
583
584         table = ProfileAlongTrack.getEmptyMetadataTable();
585         table.put(ProfileAlongTrack.array_name, "2B-GEOPROF/Geolocation_Fields/DEM_elevation");
586         table.put(ProfileAlongTrack.range_name, "DEM_elevation");
587         table.put(ProfileAlongTrack.trackDim_name, "nray");
588         adapter_s[1] = new ArrayAdapter(reader, table);
589         adapter_s[1].setRangeProcessor(new RangeProcessor() { // need this because we don't want -9999 mapped to NaN
590              public float[] processRange(short[] svals, Map<String, double[]> subset) {
591                  float[] fvals = new float[svals.length];
592                  for (int i=0; i<svals.length; i++) {
593                     short sval = svals[i];
594                     if (sval == -9999) {
595                        fvals[i] = 0f;
596                     }
597                     else {
598                       fvals[i] = sval;
599                     }
600                  }
601                  return fvals;
602               }
603         });
604
605         table = ProfileAlongTrack.getEmptyMetadataTable();
606         table.put(ProfileAlongTrack.array_name, "2B-GEOPROF/Geolocation_Fields/Longitude");
607         table.put(ProfileAlongTrack.range_name, "Longitude");
608         table.put(ProfileAlongTrack.trackDim_name, "nray");
609         adapter_s[2] = new ArrayAdapter(reader, table);
610
611         TrackDomain track_domain = new TrackDomain(adapter_s[2], adapter_s[0], adapter_s[1]);
612         track_adapter = new TrackAdapter(track_domain, adapter_s[1]);
613
614         adapters[2] = new TrackAdapter(new TrackDomain(adapter_s[2], adapter_s[0], adapter_s[1]), adapter_s[1]);
615         ((TrackAdapter)adapters[2]).setName("Track3D");
616         defaultSubsets[2] = adapters[2].getDefaultSubset();
617
618         adapters[1] = new TrackAdapter(new TrackDomain(adapter_s[2], adapter_s[0]), adapter_s[1]);
619         ((TrackAdapter)adapters[1]).setName("Track2D");
620         defaultSubsets[1] = adapters[1].getDefaultSubset();
621
622
623         properties.put("medianFilter", new String[] {Integer.toString(adapter.getMedianFilterWindowHeight()), Integer.toString(adapter.getMedianFilterWindowWidth())});
624         properties.put("setBelowSfcMissing", new String[] {"true"});
625         propsArray[0] = properties;
626         hasTrackPreview = true;
627       }
628       else if ( name.startsWith("MHSx_xxx_1B") && name.endsWith("h5")) {
629          Map<String, Object> table = SwathAdapter.getEmptyMetadataTable();
630          table.put("array_name", "U-MARF/EPS/MHSx_xxx_1B/DATA/Channel1");
631          table.put("lon_array_name", "U-MARF/EPS/IASI_xxx_1C/DATA/IMAGE_LON_ARRAY");
632          table.put("lat_array_name", "U-MARF/EPS/IASI_xxx_1C/DATA/IMAGE_LAT_ARRAY");
633          table.put("XTrack", "dim1");
634          table.put("Track", "dim0");
635          table.put("geo_XTrack", "dim1");
636          table.put("geo_Track", "dim0");
637          table.put("product_name", "MHSx_xxx_1B");
638          SwathAdapter swathAdapter = new SwathAdapter(reader, table);
639          adapters[0] = swathAdapter;
640          Map<String, double[]> subset = swathAdapter.getDefaultSubset();
641          defaultSubset = subset;
642          defaultSubsets[0] = defaultSubset;
643          categories = DataCategory.parseCategories("2D grid;GRID-2D;");
644       }
645       else if ( name.startsWith("MYD02SSH") ) {
646         String[] arrayNames = null;
647
648         if (name.endsWith("level2.hdf")) {
649           arrayNames = new String[] {"cld_press_acha", "cld_temp_acha", "cld_height_acha", "cloud_type",
650                                             "cloud_albedo_0_65um_nom", "cloud_transmission_0_65um_nom", "cloud_fraction"};
651         }
652         else if (name.endsWith("obs.hdf")) {
653           arrayNames = new String[] {"refl_0_65um_nom", "refl_0_86um_nom", "refl_3_75um_nom", "refl_1_60um_nom", "refl_1_38um_nom",
654                                      "temp_3_75um_nom", "temp_11_0um_nom", "temp_12_0um_nom", "temp_6_7um_nom",
655                                      "temp_8_5um_nom", "temp_13_3um_nom"};
656         }
657  
658         adapters = new MultiDimensionAdapter[arrayNames.length];
659         defaultSubsets = new HashMap[arrayNames.length];
660         propsArray = new Hashtable[arrayNames.length]; 
661
662         for (int k=0; k<arrayNames.length; k++) {
663           Map<String, Object> swthTable = SwathAdapter.getEmptyMetadataTable();
664           swthTable.put("array_name", arrayNames[k]);
665           swthTable.put("lon_array_name", "pixel_longitude");
666           swthTable.put("lat_array_name", "pixel_latitude");
667           swthTable.put("XTrack", "pixel_elements_along_scan_direction");
668           swthTable.put("Track", "scan_lines_along_track_direction");
669           swthTable.put("geo_Track", "scan_lines_along_track_direction");
670           swthTable.put("geo_XTrack", "pixel_elements_along_scan_direction");
671           swthTable.put("scale_name", "SCALE_FACTOR");
672           swthTable.put("offset_name", "ADD_OFFSET");
673           swthTable.put("fill_value_name", "_FILLVALUE");
674           swthTable.put("geo_scale_name", "SCALE_FACTOR");
675           swthTable.put("geo_offset_name", "ADD_OFFSET");
676           swthTable.put("geo_fillValue_name", "_FILLVALUE");
677           swthTable.put("range_name", arrayNames[k]);
678           swthTable.put("unpack", "unpack");
679
680           SwathAdapter swathAdapter0 = new SwathAdapter(reader, swthTable);
681           Map<String, double[]> subset = swathAdapter0.getDefaultSubset();
682           defaultSubset = subset;
683           adapters[k] = swathAdapter0;
684           defaultSubsets[k] = defaultSubset;
685         }
686         categories = DataCategory.parseCategories("2D grid;GRID-2D;");
687         hasImagePreview = true;
688       }
689       else if (name.contains("AWG_OZONE") ) {
690         String[] arrayNames = new String[] {"ColumnOzone"};
691
692         adapters = new MultiDimensionAdapter[arrayNames.length];
693         defaultSubsets = new HashMap[arrayNames.length];
694         propsArray = new Hashtable[arrayNames.length];
695         
696         for (int k=0; k<arrayNames.length; k++) {
697           Map<String, Object> swthTable = SwathAdapter.getEmptyMetadataTable();
698           swthTable.put("array_name", arrayNames[k]);
699           swthTable.put("lon_array_name", "Longitude");
700           swthTable.put("lat_array_name", "Latitude");
701           swthTable.put("XTrack", "Columns");
702           swthTable.put("Track", "Rows");
703           swthTable.put("fill_value_name", "_FillValue");
704           swthTable.put("geo_Track", "Rows");
705           swthTable.put("geo_XTrack", "Columns");
706           swthTable.put("geo_fillValue_name", "_FillValue");
707           swthTable.put("range_name", arrayNames[k]);
708
709           SwathAdapter swathAdapter0 = new SwathAdapter(reader, swthTable);
710           swathAdapter0.setDefaultStride(5);
711           Map<String, double[]> subset = swathAdapter0.getDefaultSubset();
712           defaultSubset = subset;
713           adapters[k] = swathAdapter0;
714           defaultSubsets[k] = defaultSubset;
715         }
716
717         categories = DataCategory.parseCategories("2D grid;GRID-2D;");
718         hasImagePreview = true;
719       }
720       else if (name.contains("AWG_CLOUD_MASK") ) {
721         String[] arrayNames = new String[] {"CloudMask"};
722
723         adapters = new MultiDimensionAdapter[arrayNames.length];
724         defaultSubsets = new HashMap[arrayNames.length];
725         propsArray = new Hashtable[arrayNames.length];
726
727         for (int k=0; k<arrayNames.length; k++) {
728           Map<String, Object> swthTable = SwathAdapter.getEmptyMetadataTable();
729           swthTable.put("array_name", arrayNames[k]);
730           swthTable.put("lon_array_name", "Longitude");
731           swthTable.put("lat_array_name", "Latitude");
732           swthTable.put("XTrack", "Columns");
733           swthTable.put("Track", "Rows");
734           swthTable.put("fill_value_name", "_FillValue");
735           swthTable.put("geo_Track", "Rows");
736           swthTable.put("geo_XTrack", "Columns");
737           swthTable.put("geo_fillValue_name", "_FillValue");
738           swthTable.put("range_name", arrayNames[k]);
739
740           SwathAdapter swathAdapter0 = new SwathAdapter(reader, swthTable);
741           swathAdapter0.setDefaultStride(5);
742           Map<String, double[]> subset = swathAdapter0.getDefaultSubset();
743           defaultSubset = subset;
744           adapters[k] = swathAdapter0;
745           defaultSubsets[k] = defaultSubset;
746         }
747
748         categories = DataCategory.parseCategories("2D grid;GRID-2D;");
749         hasImagePreview = true;
750       }
751       else if (name.contains("AWG_CLOUD_HEIGHT")) {
752         String[] arrayNames = new String[] {"CldTopTemp", "CldTopPres", "CldTopHght"};
753
754         adapters = new MultiDimensionAdapter[arrayNames.length];
755         defaultSubsets = new HashMap[arrayNames.length];
756         propsArray = new Hashtable[arrayNames.length];
757
758         for (int k=0; k<arrayNames.length; k++) {
759           Map<String, Object> swthTable = SwathAdapter.getEmptyMetadataTable();
760           swthTable.put("array_name", arrayNames[k]);
761           swthTable.put("lon_array_name", "Longitude");
762           swthTable.put("lat_array_name", "Latitude");
763           swthTable.put("XTrack", "Columns");
764           swthTable.put("Track", "Rows");
765           swthTable.put("scale_name", "scale_factor");
766           swthTable.put("offset_name", "add_offset");
767           swthTable.put("fill_value_name", "_FillValue");
768           swthTable.put("geo_Track", "Rows");
769           swthTable.put("geo_XTrack", "Columns");
770           swthTable.put("geo_scale_name", "scale_factor");
771           swthTable.put("geo_offset_name", "add_offset");
772           swthTable.put("geo_fillValue_name", "_FillValue");
773           swthTable.put("range_name", arrayNames[k]);
774           swthTable.put("unpack", "unpack");
775
776           SwathAdapter swathAdapter0 = new SwathAdapter(reader, swthTable);
777           swathAdapter0.setDefaultStride(5);
778           Map<String, double[]> subset = swathAdapter0.getDefaultSubset();
779           defaultSubset = subset;
780           adapters[k] = swathAdapter0;
781           defaultSubsets[k] = defaultSubset;
782         }
783         categories = DataCategory.parseCategories("2D grid;GRID-2D;");
784         hasImagePreview = true;
785       }
786       else if (name.startsWith("geocatL2") && name.endsWith("ci.hdf")) {
787         String[] arrayNames = new String[] {"box_average_11um_ctc", "box_average_11um_ctc_scaled", "conv_init", "cloud_type"};
788
789         adapters = new MultiDimensionAdapter[arrayNames.length];
790         defaultSubsets = new HashMap[arrayNames.length];
791         propsArray = new Hashtable[arrayNames.length];
792
793         for (int k=0; k<arrayNames.length; k++) {
794           Map<String, Object> swthTable = SwathAdapter.getEmptyMetadataTable();
795           swthTable.put("array_name", arrayNames[k]);
796           swthTable.put("lon_array_name", "lon");
797           swthTable.put("lat_array_name", "lat");
798           swthTable.put("XTrack", "Elements");
799           swthTable.put("Track", "Lines");
800           swthTable.put("geo_Track", "Lines");
801           swthTable.put("geo_XTrack", "Elements");
802           swthTable.put("range_name", arrayNames[k]);
803
804           SwathAdapter swathAdapter0 = new SwathAdapter(reader, swthTable);
805           swathAdapter0.setDefaultStride(1);
806           Map<String, double[]> subset = swathAdapter0.getDefaultSubset();
807           defaultSubset = subset;
808           adapters[k] = swathAdapter0;
809           defaultSubsets[k] = defaultSubset;
810         }
811         categories = DataCategory.parseCategories("2D grid;GRID-2D;");
812         hasImagePreview = true;
813       } 
814       else {
815         String[] arrayNames = new String[] {"baseline_cmask_seviri_cloud_mask", "baseline_ctype_seviri_cloud_type",
816                                             "baseline_ctype_seviri_cloud_phase", "baseline_cld_hght_seviri_cloud_top_pressure",
817                                             "baseline_cld_hght_seviri_cloud_top_height"};
818
819         adapters = new MultiDimensionAdapter[arrayNames.length];
820         defaultSubsets = new HashMap[arrayNames.length];
821         propsArray = new Hashtable[arrayNames.length]; 
822
823         for (int k=0; k<arrayNames.length; k++) {
824           Map<String, Object> swthTable = SwathAdapter.getEmptyMetadataTable();
825           swthTable.put("array_name", arrayNames[k]);
826           swthTable.put("lon_array_name", "pixel_longitude");
827           swthTable.put("lat_array_name", "pixel_latitude");
828           swthTable.put("XTrack", "elements");
829           swthTable.put("Track", "lines");
830           swthTable.put("scale_name", "scale_factor");
831           swthTable.put("offset_name", "add_offset");
832           swthTable.put("fill_value_name", "_FillValue");
833           swthTable.put("geo_Track", "lines");
834           swthTable.put("geo_XTrack", "elements");
835           swthTable.put("geo_scale_name", "scale_factor");
836           swthTable.put("geo_offset_name", "add_offset");
837           swthTable.put("geo_fillValue_name", "_FillValue");
838           swthTable.put("range_name", arrayNames[k]);
839           swthTable.put("unpack", "unpack");
840
841           SwathAdapter swathAdapter0 = new SwathAdapter(reader, swthTable);
842           swathAdapter0.setDefaultStride(2);
843           Map<String, double[]> subset = swathAdapter0.getDefaultSubset();
844           defaultSubset = subset;
845           adapters[k] = swathAdapter0;
846           defaultSubsets[k] = defaultSubset;
847         }
848         categories = DataCategory.parseCategories("2D grid;GRID-2D;");
849         hasImagePreview = true;
850       }
851
852       setProperties(properties);
853    }
854
855    public void initAfterUnpersistence() {
856      try {
857        setup();
858      } catch (Exception e) {
859        logger.error("could not set up after unpersisting", e);
860      }
861    }
862
863    /**
864     * Make and insert the {@link DataChoice DataChoices} for this {@code DataSource}.
865     */
866    public void doMakeDataChoices() {
867        DataChoice choice = null;
868        if (adapters != null) {
869          for (int idx=0; idx<adapters.length; idx++) {
870             try {
871               String arrayName = (adapters[idx] == null) ? "_     " : adapters[idx].getArrayName();
872               choice = doMakeDataChoice(idx, arrayName);
873               adapterMap.put(choice.getName(), adapters[idx]);
874             } catch (Exception e) {
875               logger.error("error making data choices", e);
876             }
877
878             if (choice != null) {
879               addDataChoice(choice);
880             }
881          }
882        }
883    }
884
885    private DataChoice doMakeDataChoice(int idx, String var) throws Exception {
886        String name = var;
887        DataSelection dataSel = (defaultSubsets[idx] == null) ? new MultiDimensionSubset() : new MultiDimensionSubset(defaultSubsets[idx]);
888        Hashtable props = new Hashtable();
889        props.put(new MultiDimensionSubset(), dataSel);
890
891        if (propsArray != null) {
892          if (propsArray[idx] != null) {
893            propsArray[idx].put(new MultiDimensionSubset(), dataSel);
894            props = propsArray[idx];
895          }
896        }
897        DirectDataChoice ddc = null;
898
899        if (categories != null) {
900           ddc = new DirectDataChoice(this, idx, name, name, categories, props);
901        }
902        else {
903           ddc = new DirectDataChoice(this, idx, name, name, categoriesArray[idx], props);
904        }
905
906        return ddc;
907    }
908
909    /**
910     * Check to see if this {@code HDFHydraDataSource} is equal to the object
911     * in question.
912     * @param o  object in question
913     * @return true if they are the same or equivalent objects
914     */
915    public boolean equals(Object o) {
916        if ( !(o instanceof MultiDimensionDataSource)) {
917            return false;
918        }
919        return (this == (MultiDimensionDataSource) o);
920    }
921
922    public MultiSpectralData getMultiSpectralData() {
923      return multiSpectData;
924    }
925
926    public String getDatasetName() {
927      return filename;
928    }
929
930    public void setDatasetName(String name) {
931      filename = name;
932    }
933
934    public Map<String, double[]> getSubsetFromLonLatRect(MultiDimensionSubset select, GeoSelection geoSelection) {
935      GeoLocationInfo ginfo = geoSelection.getBoundingBox();
936      logger.debug("ginfo0: " + ginfo);
937      return adapters[0].getSubsetFromLonLatRect(select.getSubset(), ginfo.getMinLat(), ginfo.getMaxLat(),
938                                        ginfo.getMinLon(), ginfo.getMaxLon());
939    }
940
941    public synchronized Data getData(DataChoice dataChoice, DataCategory category,
942                                DataSelection dataSelection, Hashtable requestProperties)
943                                throws VisADException, RemoteException {
944       return this.getDataInner(dataChoice, category, dataSelection, requestProperties);
945    }
946
947
948    protected Data getDataInner(DataChoice dataChoice, DataCategory category,
949                                DataSelection dataSelection, Hashtable requestProperties)
950                                throws VisADException, RemoteException {
951
952        MultiDimensionAdapter adapter = null;
953        adapter = adapterMap.get(dataChoice.getName());
954
955        Hashtable dataChoiceProps = dataChoice.getProperties();
956
957        //- this hack keeps the HydraImageProbe from doing a getData()
958        //- TODO: need to use categories?
959        if (requestProperties != null) {
960          if ((requestProperties.toString()).equals("{prop.requester=MultiSpectral}")) {
961            return null;
962          }
963        }
964
965        GeoLocationInfo ginfo = null;
966        GeoSelection geoSelection = null;
967        
968        if ((dataSelection != null) && (dataSelection.getGeoSelection() != null)) {
969
970          if (dataSelection.getGeoSelection().getBoundingBox() != null) {
971            geoSelection = dataSelection.getGeoSelection();
972          }
973          else { // no bounding box in the incoming DataSelection. Check the dataChoice.
974            DataSelection datSelFromChoice = dataChoice.getDataSelection();
975            if (datSelFromChoice != null) {
976              geoSelection = datSelFromChoice.getGeoSelection();
977            }
978          }
979        }
980
981        if (geoSelection != null) {
982          ginfo = geoSelection.getBoundingBox();
983        }
984
985        // Still no geo info so check for the lon/lat b.b. in this datasource (last set by DataSelectionComponent)
986        if (ginfo == null) {
987           DataSelection localDataSelection = getDataSelection();
988           if (localDataSelection != null) {
989              geoSelection = localDataSelection.getGeoSelection();
990              if (geoSelection != null) {
991                 ginfo = geoSelection.getBoundingBox();
992              }
993           }
994        }
995
996        Data data = null;
997        if (adapters == null) {
998          return data;
999        }
1000
1001        Map<String, double[]> subset = null;
1002        MultiDimensionSubset select = null;
1003
1004        Hashtable table = dataChoice.getProperties();
1005        Enumeration keys = table.keys();
1006        while (keys.hasMoreElements()) {
1007           Object key = keys.nextElement();
1008           if (key instanceof MultiDimensionSubset) {
1009              select = (MultiDimensionSubset) table.get(key);
1010           }
1011        }
1012
1013        try {
1014            subset = null;
1015            if (ginfo != null) {
1016                if (trackSelection != null) {
1017                        boolean trackStrideOk = trackSelection.setTrackStride();
1018                        boolean verticalStrideOk = trackSelection.setVerticalStride();
1019                        
1020                        if (trackStrideOk && verticalStrideOk) {
1021                        subset = adapter.getSubsetFromLonLatRect(ginfo.getMinLat(), ginfo.getMaxLat(),
1022                                        ginfo.getMinLon(), ginfo.getMaxLon(),
1023                                        trackSelection.trackStride,
1024                                        trackSelection.verticalStride,
1025                                        geoSelection.getZStride());
1026                        } else {
1027                                // one of the strides is not an integer, let user know
1028                            String msg = "Either the Track or Vertical Stride is invalid.\n" +
1029                                         "Stride values must be positive integers.\n";
1030                                Object[] params = { msg };
1031                                JOptionPane.showMessageDialog(null, params, "Invalid Stride", JOptionPane.OK_OPTION);
1032                                return null;
1033                        }
1034                } else {
1035                        subset = adapter.getSubsetFromLonLatRect(ginfo.getMinLat(), ginfo.getMaxLat(),
1036                                        ginfo.getMinLon(), ginfo.getMaxLon(),
1037                                        geoSelection.getXStride(),
1038                                        geoSelection.getYStride(),
1039                                        geoSelection.getZStride());
1040                }
1041              if (subset == null && select != null) {
1042                subset = select.getSubset();
1043              }
1044            }
1045            else { // no IDV incoming spatial selection info, so check for HYDRA specific via Properties
1046              if (select != null) {
1047                subset = select.getSubset();
1048              }
1049              
1050              if (dataSelection != null) {
1051                Hashtable props = dataSelection.getProperties();
1052              }
1053            }
1054            
1055            if (subset != null) {
1056              data = adapter.getData(subset);
1057              data = applyProperties(data, dataChoiceProps, subset);
1058            }
1059        } catch (Exception e) {
1060            logger.error("getData exception", e);
1061        }
1062
1063        return data;
1064    }
1065
1066    protected Data applyProperties(Data data, Hashtable requestProperties, Map<String, double[]> subset)
1067          throws VisADException, RemoteException, Exception {
1068      Data new_data = data;
1069
1070      if (requestProperties == null) {
1071        new_data = data;
1072        return new_data;
1073      }
1074
1075      if (requestProperties.containsKey("medianFilter")) {
1076        String[] items = (String[]) requestProperties.get("medianFilter");
1077        int windowVertLen = Integer.parseInt(items[0]);
1078        int windowHorzLen = Integer.parseInt(items[1]);
1079        GriddedSet domainSet = (GriddedSet) ((FlatField)data).getDomainSet();
1080        int[] lens = domainSet.getLengths();
1081        float[] range_values = (((FlatField)data).getFloats())[0];
1082        range_values =
1083           ProfileAlongTrack.medianFilter(range_values, lens[0], lens[1], windowHorzLen, windowVertLen);
1084        ((FlatField)new_data).setSamples(new float[][] {range_values});
1085      }
1086      if (requestProperties.containsKey("setBelowSfcMissing")) {
1087        FlatField track = (FlatField) track_adapter.getData(subset);
1088        float[] sfcElev = (track.getFloats())[0];
1089        FlatField field = (FlatField) new_data;
1090        GriddedSet gset = (GriddedSet) field.getDomainSet();
1091        float[][] samples = gset.getSamples(false);
1092        int[] lens = gset.getLengths();
1093        float[] range_values = (field.getFloats())[0];
1094
1095        int trkIdx = ((ProfileAlongTrack3D)adapters[0]).adapter2D.getTrackTupIdx();
1096        int vrtIdx = ((ProfileAlongTrack3D)adapters[0]).adapter2D.getVertTupIdx();
1097
1098        int k = 0;
1099        for (int j=0; j<lens[trkIdx]; j++) {
1100          float val = sfcElev[j];
1101          for (int i=0; i<lens[vrtIdx]; i++) {
1102            if (vrtIdx < trkIdx) k = i + j*lens[0];
1103            if (trkIdx < vrtIdx) k = j + i*lens[0];
1104            if (samples[2][k] <= val) {
1105              range_values[k] = Float.NaN;
1106            }
1107          }
1108        }
1109        field.setSamples(new float[][] {range_values});
1110      }
1111      return new_data;
1112    }
1113
1114    protected void initDataSelectionComponents(
1115         List<DataSelectionComponent> components,
1116             final DataChoice dataChoice) {
1117
1118      if (hasImagePreview) {
1119        try {
1120          FlatField image = (FlatField) getDataInner(dataChoice, null, null, null);
1121          components.add(new PreviewSelection(dataChoice, image, null));
1122        } catch (Exception e) {
1123          logger.error("cannot make preview selection", e);
1124        }
1125      }
1126      if (hasTrackPreview) {
1127        try {
1128          FlatField track = track_adapter.getData(track_adapter.getDefaultSubset());     
1129          Map defaultSubset = (adapterMap.get(dataChoice.getName())).getDefaultSubset();
1130          trackSelection = new TrackSelection(dataChoice, track, defaultSubset);
1131          components.add(trackSelection);
1132        } catch (Exception e) {
1133          logger.error("cannot make preview selection", e);
1134        }
1135      }
1136    }
1137
1138    private String getTrackDimensionName(String variableName) {
1139        return getVariableDimensionName(variableName, 0);
1140    }
1141
1142    private String getVerticalDimensionName(String variableName) {
1143        return getVariableDimensionName(variableName, 1);
1144    }
1145
1146    private String getVariableDimensionName(String variableName, int dimension) {
1147        NetcdfFile ncfile = ((NetCDFFile)reader).getNetCDFFile();
1148        Variable v = ncfile.findVariable(variableName);
1149        String name = null;
1150        if (v != null) {
1151            name = v.getDimension(dimension).getFullName();
1152        }
1153        return name;
1154    }
1155
1156    private boolean hasVariable(String variableName) {
1157        NetcdfFile ncfile = ((NetCDFFile) reader).getNetCDFFile();
1158        return ncfile.findVariable(variableName) != null;
1159    }
1160
1161    private ArrayAdapter createTrackVertArrayAdapter(String variableName) {
1162        Map<String, Object> table = SwathAdapter.getEmptyMetadataTable();
1163
1164        String trackDimName = getTrackDimensionName(variableName);
1165        String vertDimName = getVerticalDimensionName(variableName);
1166
1167        table.put(ProfileAlongTrack.array_name, variableName);
1168        table.put(ProfileAlongTrack.trackDim_name, trackDimName);
1169        table.put(ProfileAlongTrack.vertDim_name, vertDimName);
1170        table.put("array_dimension_names", new String[] { trackDimName, vertDimName });
1171
1172        return new ArrayAdapter(reader, table);
1173    }
1174}