001/*
002 * This file is part of McIDAS-V
003 *
004 * Copyright 2007-2023
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         
307         String dataPath = "DataProducts/";
308         String[] arrayNames = new String[] {"532_total_attn_bsc", "1064_total_attn_bsc", "355_total_attn_bsc"};
309         String[] rangeNames = new String[] {"Total_Attenuated_Backscatter_532", "Total_Attenuated_Backscatter_1064", "Total_Attenuated_Backscatter_355"};
310         
311         String[] arrayNameAOT = new String[] {"532_AOT_hi_col", "355_AOT_hi_col"};
312         String[] rangeNamesAOT = new String[] {};
313         
314         int numAdapters = arrayNames.length;
315         adapters = new MultiDimensionAdapter[numAdapters];
316         defaultSubsets = new HashMap[numAdapters];
317         propsArray = new Hashtable[numAdapters];
318         
319         for (int k = 0; k < numAdapters; k++) {
320            table = ProfileAlongTrack.getEmptyMetadataTable();
321            table.put(ProfileAlongTrack.array_name, dataPath+arrayNames[k]);
322            table.put(ProfileAlongTrack.range_name, rangeNames[k]);
323            table.put(ProfileAlongTrack.trackDim_name, "dim0");
324            table.put(ProfileAlongTrack.vertDim_name, "dim1");
325            table.put(ProfileAlongTrack.profileTime_name, "ApplanixIMU/gps_time");
326            table.put(ProfileAlongTrack.longitude_name, "ApplanixIMU/gps_lon");
327            table.put(ProfileAlongTrack.latitude_name, "ApplanixIMU/gps_lat");
328            table.put("array_dimension_names", new String[] {"dim0", "dim1"});
329            ProfileAlongTrack adapter = new HSRL2D(reader, table);
330            ProfileAlongTrack3D adapter3D = new ProfileAlongTrack3D(adapter);
331            Map<String, double[]> subset = adapter.getDefaultSubset();
332            adapters[k] = adapter3D;
333            defaultSubset = subset;
334            defaultSubsets[k] = defaultSubset;
335
336            properties.put("medianFilter", new String[] {Integer.toString(adapter.getMedianFilterWindowHeight()), Integer.toString(adapter.getMedianFilterWindowWidth())});
337            properties.put("setBelowSfcMissing", new String[] {"true"});
338            propsArray[k] = properties;
339         }
340         
341         DataCategory.createCategory("ProfileAlongTrack");
342         categories = DataCategory.parseCategories("ProfileAlongTrack;ProfileAlongTrack;");
343
344         hasTrackPreview = true;
345
346         ArrayAdapter[] adapter_s = new ArrayAdapter[3];
347         table = ProfileAlongTrack.getEmptyMetadataTable();
348         table.put(ProfileAlongTrack.array_name, "ApplanixIMU/gps_lat");
349         table.put(ProfileAlongTrack.trackDim_name, "dim0");
350         table.put(ProfileAlongTrack.vertDim_name, "dim1");
351         table.put("array_dimension_names", new String[] {"dim0", "dim1"});
352         adapter_s[0] = new ArrayAdapter(reader, table);
353
354         table = ProfileAlongTrack.getEmptyMetadataTable();
355         table.put(ProfileAlongTrack.array_name, "UserInput/DEM_altitude");
356         table.put(ProfileAlongTrack.trackDim_name, "dim0");
357         table.put(ProfileAlongTrack.vertDim_name, "dim1");
358         table.put("array_dimension_names", new String[] {"dim0", "dim1"});
359         adapter_s[1] = new ArrayAdapter(reader, table);
360         /*
361         adapter_s[1].setRangeProcessor(new RangeProcessor() { // Eventually handle unit conversions better.
362              public float[] processRange(float[] fvals, Map<String, double[]> subset) {
363                 for (int i=0; i<fvals.length; i++) {
364                    fvals[i] *= 1000; //km -> m
365                 }
366                 return fvals;
367              }
368         });
369         */
370
371         table = ProfileAlongTrack.getEmptyMetadataTable();
372         table.put(ProfileAlongTrack.array_name, "ApplanixIMU/gps_lon");
373         table.put(ProfileAlongTrack.trackDim_name, "dim0");
374         table.put(ProfileAlongTrack.vertDim_name, "dim1");
375         table.put("array_dimension_names", new String[] {"dim0", "dim1"});
376         adapter_s[2] = new ArrayAdapter(reader, table);
377
378         TrackDomain track_domain = new TrackDomain(adapter_s[2], adapter_s[0], adapter_s[1]);
379         track_adapter = new TrackAdapter(track_domain, adapter_s[1]);
380         
381         TrackAdapter trkAdapter = new TrackAdapter(new TrackDomain(adapter_s[2], adapter_s[0], adapter_s[1]), adapter_s[1]);
382         trkAdapter.setName("Track3D");
383         
384         trkAdapter = new TrackAdapter(new TrackDomain(adapter_s[2], adapter_s[0]), adapter_s[1]);
385         trkAdapter.setName("Track2D");
386       }
387       else if (name.startsWith("CAL_LID_L1")) {
388
389           // Make sure the variables we need are present. If not, this is not a valid
390           // L1 CALIPSO file McV can work with.
391           
392           if (! ((hasVariable("Latitude")) &&
393                          (hasVariable("Longitude")) &&
394                          (hasVariable("Surface_Elevation")) &&
395                          (hasVariable("Tropopause_Height")) &&
396                          (hasVariable("Total_Attenuated_Backscatter_532"))) 
397                  ) {
398                   // Pop up a dialog letting user know we can't work wit this data
399                        String msg = "McIDAS-V is unable to read this Level 1 CALIPSO file.\n" +
400                                        "If you believe this is a valid file which should be supported,\n" +
401                                        "please contact the MUG or post a message on the MUG Forum.";
402                        Object[] params = { msg };
403                        JOptionPane.showMessageDialog(null, params, "Data Validity Test Failure", JOptionPane.OK_OPTION);
404                        throw new Exception("Unable to load CALIPSO data");
405           }
406           
407         adapters = new MultiDimensionAdapter[4];
408         defaultSubsets = new HashMap[4];
409         propsArray = new Hashtable[4]; 
410         
411         Map<String, Object> table = ProfileAlongTrack.getEmptyMetadataTable();
412         table.put(ProfileAlongTrack.array_name, "Total_Attenuated_Backscatter_532");
413         table.put(ProfileAlongTrack.ancillary_file_name, "/edu/wisc/ssec/mcidasv/data/hydra/resources/calipso/altitude");
414         table.put(ProfileAlongTrack.trackDim_name, "dim0");
415         table.put(ProfileAlongTrack.vertDim_name, "dim1");
416         table.put(ProfileAlongTrack.profileTime_name, "Profile_Time");
417         table.put(ProfileAlongTrack.longitude_name, "Longitude");
418         table.put(ProfileAlongTrack.latitude_name, "Latitude");
419         table.put("array_dimension_names", new String[] {"dim0", "dim1"}); 
420         ProfileAlongTrack adapter = new Calipso2D(reader, table);
421         ProfileAlongTrack3D adapter3D = new ProfileAlongTrack3D(adapter);
422         Map<String, double[]> subset = adapter.getDefaultSubset();
423         adapters[0] = adapter3D;
424         defaultSubset = subset;
425         defaultSubsets[0] = defaultSubset;
426         DataCategory.createCategory("ProfileAlongTrack");
427         categories = DataCategory.parseCategories("ProfileAlongTrack;ProfileAlongTrack;");
428
429         properties.put("medianFilter", new String[] {Integer.toString(adapter.getMedianFilterWindowHeight()), Integer.toString(adapter.getMedianFilterWindowWidth())});
430         properties.put("setBelowSfcMissing", new String[] {"true"});
431         propsArray[0] = properties;
432
433         ArrayAdapter[] adapter_s = new ArrayAdapter[3];
434         table = ProfileAlongTrack.getEmptyMetadataTable();
435         table.put(ProfileAlongTrack.array_name, "Latitude");
436         table.put(ProfileAlongTrack.trackDim_name, "dim0");
437         table.put(ProfileAlongTrack.vertDim_name, "dim1");
438         table.put("array_dimension_names", new String[] {"dim0", "dim1"}); 
439         adapter_s[0] = new ArrayAdapter(reader, table);
440
441         table = ProfileAlongTrack.getEmptyMetadataTable();
442         table.put(ProfileAlongTrack.array_name, "Surface_Elevation");
443         table.put(ProfileAlongTrack.trackDim_name, "dim0");
444         table.put(ProfileAlongTrack.vertDim_name, "dim1");
445         table.put("array_dimension_names", new String[] {"dim0", "dim1"}); 
446         adapter_s[1] = new ArrayAdapter(reader, table);
447         adapter_s[1].setRangeProcessor(new RangeProcessor() { // Eventually handle unit conversions better.
448              public float[] processRange(float[] fvals, Map<String, double[]> subset) {
449                 for (int i=0; i<fvals.length; i++) {
450                    fvals[i] *= 1000; //km -> m 
451                 }
452                 return fvals;
453              }
454         });
455
456         table = ProfileAlongTrack.getEmptyMetadataTable();
457         table.put(ProfileAlongTrack.array_name, "Longitude");
458         table.put(ProfileAlongTrack.trackDim_name, "dim0");
459         table.put(ProfileAlongTrack.vertDim_name, "dim1");
460         table.put("array_dimension_names", new String[] {"dim0", "dim1"}); 
461         adapter_s[2] = new ArrayAdapter(reader, table);
462
463         TrackDomain track_domain = new TrackDomain(adapter_s[2], adapter_s[0], adapter_s[1]);
464         track_adapter = new TrackAdapter(track_domain, adapter_s[1]);
465
466         table = ProfileAlongTrack.getEmptyMetadataTable();
467         table.put(ProfileAlongTrack.array_name, "Tropopause_Height");
468         table.put(ProfileAlongTrack.trackDim_name, "dim0");
469         table.put(ProfileAlongTrack.vertDim_name, "dim1");
470         table.put("array_dimension_names", new String[] {"dim0", "dim1"}); 
471         ArrayAdapter trop_height = new ArrayAdapter(reader, table);
472         track_domain = new TrackDomain(adapter_s[2], adapter_s[0], trop_height);
473         adapters[1] = new TrackAdapter(track_domain, trop_height);
474         defaultSubsets[1] = adapters[1].getDefaultSubset();
475
476         adapters[2] = new TrackAdapter(new TrackDomain(adapter_s[2], adapter_s[0], adapter_s[1]), adapter_s[1]);
477         ((TrackAdapter)adapters[2]).setName("Track3D");
478         defaultSubsets[2] = adapters[2].getDefaultSubset();
479
480         adapters[3] = new TrackAdapter(new TrackDomain(adapter_s[2], adapter_s[0]), adapter_s[1]);
481         ((TrackAdapter)adapters[3]).setName("Track2D");
482         defaultSubsets[3] = adapters[3].getDefaultSubset();
483         
484
485         hasTrackPreview = true;
486       }
487       else if (name.startsWith("CAL_LID_L2")) {
488           
489           // Make sure the variables we need are present. If not, this is not a valid
490           // L2 CALIPSO file McV can work with.
491           
492           if (! ((hasVariable("Latitude")) &&
493                          (hasVariable("Longitude")) &&
494                          (hasVariable("DEM_Surface_Elevation")) &&
495                          (hasVariable("Layer_Top_Altitude"))) 
496                  ) {
497                   // Pop up a dialog letting user know we can't work wit this data
498                        String msg = "McIDAS-V is unable to read this Level 2 CALIPSO file.\n" +
499                                        "If you believe this is a valid file which should be supported,\n" +
500                                        "please contact the MUG or post a message on the MUG Forum.";
501                        Object[] params = { msg };
502                        JOptionPane.showMessageDialog(null, params, "Data Validity Test Failure", JOptionPane.OK_OPTION);
503                        throw new Exception("Unable to load CALIPSO data");
504           }
505           
506         adapters = new MultiDimensionAdapter[4];
507         defaultSubsets = new HashMap[4];
508         propsArray = new Hashtable[4];
509
510         ArrayAdapter[] adapter_s = new ArrayAdapter[3];
511
512         adapter_s[0] = createTrackVertArrayAdapter("Longitude");
513         adapter_s[1] = createTrackVertArrayAdapter("Latitude");
514         adapter_s[2] = createTrackVertArrayAdapter("DEM_Surface_Elevation");
515
516         TrackDomain track_domain = new TrackDomain(adapter_s[0], adapter_s[1], adapter_s[2]);
517         track_adapter = new TrackAdapter(track_domain, adapter_s[2]);
518         adapters[1] = track_adapter;
519         defaultSubsets[1] = track_adapter.getDefaultSubset();
520
521         ArrayAdapter layer_top_altitude = createTrackVertArrayAdapter("Layer_Top_Altitude");
522
523         RangeProcessor rngProcessor =
524             new RangeProcessor(1.0f, 0.0f, -Float.MAX_VALUE, Float.MAX_VALUE, -9999.0f);
525         layer_top_altitude.setRangeProcessor(rngProcessor);
526
527         track_domain = new TrackDomain(adapter_s[0], adapter_s[1], layer_top_altitude);
528         adapters[0] = new TrackAdapter(track_domain, layer_top_altitude);
529         defaultSubsets[0] = adapters[0].getDefaultSubset();
530
531         /** another layer, how to show all?
532         adapters[2] = new TrackAdapter(track_domain, layer_top_altitude);
533         ((TrackAdapter)adapters[2]).setListIndex(1);
534         defaultSubsets[2] = adapters[2].getDefaultSubset();
535         */
536
537         adapters[2] = new TrackAdapter(new TrackDomain(adapter_s[0], adapter_s[1]), adapter_s[2]);
538         ((TrackAdapter)adapters[2]).setName("Track2D");
539         defaultSubsets[2] = adapters[2].getDefaultSubset();
540
541         adapters[3] = new TrackAdapter(new TrackDomain(adapter_s[0], adapter_s[1], adapter_s[2]), adapter_s[2]);
542         ((TrackAdapter)adapters[3]).setName("Track3D");
543         defaultSubsets[3] = adapters[3].getDefaultSubset();
544
545         DataCategory.createCategory("ProfileAlongTrack");
546         categories = DataCategory.parseCategories("ProfileAlongTrack;ProfileAlongTrack;");
547
548         hasTrackPreview = true;
549       }
550       else if (name.indexOf("2B-GEOPROF") > 0) {
551         adapters = new MultiDimensionAdapter[3];
552         defaultSubsets = new HashMap[3];
553         propsArray = new Hashtable[3];
554
555         Map<String, Object> table = ProfileAlongTrack.getEmptyMetadataTable();
556         table.put(ProfileAlongTrack.array_name, "2B-GEOPROF/Data_Fields/Radar_Reflectivity");
557         table.put(ProfileAlongTrack.range_name, "2B-GEOPROF_RadarReflectivity");
558         table.put(ProfileAlongTrack.scale_name, "factor");
559         table.put(ProfileAlongTrack.offset_name, "offset");
560         table.put(ProfileAlongTrack.fill_value_name, "_FillValue");
561         table.put(ProfileAlongTrack.valid_range, "valid_range");
562         table.put(ProfileAlongTrack.ancillary_file_name, "/edu/wisc/ssec/mcidasv/data/hydra/resources/cloudsat/altitude");
563         table.put(ProfileAlongTrack.trackDim_name, "nray");
564         table.put(ProfileAlongTrack.vertDim_name, "nbin");
565         table.put(ProfileAlongTrack.profileTime_name, "2B-GEOPROF/Geolocation_Fields/Profile_Time");
566         table.put(ProfileAlongTrack.longitude_name, "2B-GEOPROF/Geolocation_Fields/Longitude");
567         table.put(ProfileAlongTrack.latitude_name, "2B-GEOPROF/Geolocation_Fields/Latitude");
568         table.put(ProfileAlongTrack.product_name, "2B-GEOPROF");
569         ProfileAlongTrack adapter = new CloudSat2D(reader, table);
570         ProfileAlongTrack3D adapter3D = new ProfileAlongTrack3D(adapter);
571         Map<String, double[]> subset = adapter.getDefaultSubset();
572         adapters[0] = adapter3D;
573         defaultSubset = subset;
574         defaultSubsets[0] = defaultSubset;
575         DataCategory.createCategory("ProfileAlongTrack");
576         categories = DataCategory.parseCategories("ProfileAlongTrack;ProfileAlongTrack;");
577
578         ArrayAdapter[] adapter_s = new ArrayAdapter[3];
579         table = ProfileAlongTrack.getEmptyMetadataTable();
580         table.put(ProfileAlongTrack.array_name, "2B-GEOPROF/Geolocation_Fields/Latitude");
581         table.put(ProfileAlongTrack.range_name, "Latitude");
582         table.put(ProfileAlongTrack.trackDim_name, "nray");
583         adapter_s[0] = new ArrayAdapter(reader, table);
584
585         table = ProfileAlongTrack.getEmptyMetadataTable();
586         table.put(ProfileAlongTrack.array_name, "2B-GEOPROF/Geolocation_Fields/DEM_elevation");
587         table.put(ProfileAlongTrack.range_name, "DEM_elevation");
588         table.put(ProfileAlongTrack.trackDim_name, "nray");
589         adapter_s[1] = new ArrayAdapter(reader, table);
590         adapter_s[1].setRangeProcessor(new RangeProcessor() { // need this because we don't want -9999 mapped to NaN
591              public float[] processRange(short[] svals, Map<String, double[]> subset) {
592                  float[] fvals = new float[svals.length];
593                  for (int i=0; i<svals.length; i++) {
594                     short sval = svals[i];
595                     if (sval == -9999) {
596                        fvals[i] = 0f;
597                     }
598                     else {
599                       fvals[i] = sval;
600                     }
601                  }
602                  return fvals;
603               }
604         });
605
606         table = ProfileAlongTrack.getEmptyMetadataTable();
607         table.put(ProfileAlongTrack.array_name, "2B-GEOPROF/Geolocation_Fields/Longitude");
608         table.put(ProfileAlongTrack.range_name, "Longitude");
609         table.put(ProfileAlongTrack.trackDim_name, "nray");
610         adapter_s[2] = new ArrayAdapter(reader, table);
611
612         TrackDomain track_domain = new TrackDomain(adapter_s[2], adapter_s[0], adapter_s[1]);
613         track_adapter = new TrackAdapter(track_domain, adapter_s[1]);
614
615         adapters[2] = new TrackAdapter(new TrackDomain(adapter_s[2], adapter_s[0], adapter_s[1]), adapter_s[1]);
616         ((TrackAdapter)adapters[2]).setName("Track3D");
617         defaultSubsets[2] = adapters[2].getDefaultSubset();
618
619         adapters[1] = new TrackAdapter(new TrackDomain(adapter_s[2], adapter_s[0]), adapter_s[1]);
620         ((TrackAdapter)adapters[1]).setName("Track2D");
621         defaultSubsets[1] = adapters[1].getDefaultSubset();
622
623
624         properties.put("medianFilter", new String[] {Integer.toString(adapter.getMedianFilterWindowHeight()), Integer.toString(adapter.getMedianFilterWindowWidth())});
625         properties.put("setBelowSfcMissing", new String[] {"true"});
626         propsArray[0] = properties;
627         hasTrackPreview = true;
628       }
629       else if ( name.startsWith("MHSx_xxx_1B") && name.endsWith("h5")) {
630          Map<String, Object> table = SwathAdapter.getEmptyMetadataTable();
631          table.put("array_name", "U-MARF/EPS/MHSx_xxx_1B/DATA/Channel1");
632          table.put("lon_array_name", "U-MARF/EPS/IASI_xxx_1C/DATA/IMAGE_LON_ARRAY");
633          table.put("lat_array_name", "U-MARF/EPS/IASI_xxx_1C/DATA/IMAGE_LAT_ARRAY");
634          table.put("XTrack", "dim1");
635          table.put("Track", "dim0");
636          table.put("geo_XTrack", "dim1");
637          table.put("geo_Track", "dim0");
638          table.put("product_name", "MHSx_xxx_1B");
639          SwathAdapter swathAdapter = new SwathAdapter(reader, table);
640          adapters[0] = swathAdapter;
641          Map<String, double[]> subset = swathAdapter.getDefaultSubset();
642          defaultSubset = subset;
643          defaultSubsets[0] = defaultSubset;
644          categories = DataCategory.parseCategories("2D grid;GRID-2D;");
645       }
646       else if ( name.startsWith("MYD02SSH") ) {
647         String[] arrayNames = null;
648
649         if (name.endsWith("level2.hdf")) {
650           arrayNames = new String[] {"cld_press_acha", "cld_temp_acha", "cld_height_acha", "cloud_type",
651                                             "cloud_albedo_0_65um_nom", "cloud_transmission_0_65um_nom", "cloud_fraction"};
652         }
653         else if (name.endsWith("obs.hdf")) {
654           arrayNames = new String[] {"refl_0_65um_nom", "refl_0_86um_nom", "refl_3_75um_nom", "refl_1_60um_nom", "refl_1_38um_nom",
655                                      "temp_3_75um_nom", "temp_11_0um_nom", "temp_12_0um_nom", "temp_6_7um_nom",
656                                      "temp_8_5um_nom", "temp_13_3um_nom"};
657         }
658  
659         adapters = new MultiDimensionAdapter[arrayNames.length];
660         defaultSubsets = new HashMap[arrayNames.length];
661         propsArray = new Hashtable[arrayNames.length]; 
662
663         for (int k=0; k<arrayNames.length; k++) {
664           Map<String, Object> swthTable = SwathAdapter.getEmptyMetadataTable();
665           swthTable.put("array_name", arrayNames[k]);
666           swthTable.put("lon_array_name", "pixel_longitude");
667           swthTable.put("lat_array_name", "pixel_latitude");
668           swthTable.put("XTrack", "pixel_elements_along_scan_direction");
669           swthTable.put("Track", "scan_lines_along_track_direction");
670           swthTable.put("geo_Track", "scan_lines_along_track_direction");
671           swthTable.put("geo_XTrack", "pixel_elements_along_scan_direction");
672           swthTable.put("scale_name", "SCALE_FACTOR");
673           swthTable.put("offset_name", "ADD_OFFSET");
674           swthTable.put("fill_value_name", "_FILLVALUE");
675           swthTable.put("geo_scale_name", "SCALE_FACTOR");
676           swthTable.put("geo_offset_name", "ADD_OFFSET");
677           swthTable.put("geo_fillValue_name", "_FILLVALUE");
678           swthTable.put("range_name", arrayNames[k]);
679           swthTable.put("unpack", "unpack");
680
681           SwathAdapter swathAdapter0 = new SwathAdapter(reader, swthTable);
682           Map<String, double[]> subset = swathAdapter0.getDefaultSubset();
683           defaultSubset = subset;
684           adapters[k] = swathAdapter0;
685           defaultSubsets[k] = defaultSubset;
686         }
687         categories = DataCategory.parseCategories("2D grid;GRID-2D;");
688         hasImagePreview = true;
689       }
690       else if (name.contains("AWG_OZONE") ) {
691         String[] arrayNames = new String[] {"ColumnOzone"};
692
693         adapters = new MultiDimensionAdapter[arrayNames.length];
694         defaultSubsets = new HashMap[arrayNames.length];
695         propsArray = new Hashtable[arrayNames.length];
696         
697         for (int k=0; k<arrayNames.length; k++) {
698           Map<String, Object> swthTable = SwathAdapter.getEmptyMetadataTable();
699           swthTable.put("array_name", arrayNames[k]);
700           swthTable.put("lon_array_name", "Longitude");
701           swthTable.put("lat_array_name", "Latitude");
702           swthTable.put("XTrack", "Columns");
703           swthTable.put("Track", "Rows");
704           swthTable.put("fill_value_name", "_FillValue");
705           swthTable.put("geo_Track", "Rows");
706           swthTable.put("geo_XTrack", "Columns");
707           swthTable.put("geo_fillValue_name", "_FillValue");
708           swthTable.put("range_name", arrayNames[k]);
709
710           SwathAdapter swathAdapter0 = new SwathAdapter(reader, swthTable);
711           swathAdapter0.setDefaultStride(5);
712           Map<String, double[]> subset = swathAdapter0.getDefaultSubset();
713           defaultSubset = subset;
714           adapters[k] = swathAdapter0;
715           defaultSubsets[k] = defaultSubset;
716         }
717
718         categories = DataCategory.parseCategories("2D grid;GRID-2D;");
719         hasImagePreview = true;
720       }
721       else if (name.contains("AWG_CLOUD_MASK") ) {
722         String[] arrayNames = new String[] {"CloudMask"};
723
724         adapters = new MultiDimensionAdapter[arrayNames.length];
725         defaultSubsets = new HashMap[arrayNames.length];
726         propsArray = new Hashtable[arrayNames.length];
727
728         for (int k=0; k<arrayNames.length; k++) {
729           Map<String, Object> swthTable = SwathAdapter.getEmptyMetadataTable();
730           swthTable.put("array_name", arrayNames[k]);
731           swthTable.put("lon_array_name", "Longitude");
732           swthTable.put("lat_array_name", "Latitude");
733           swthTable.put("XTrack", "Columns");
734           swthTable.put("Track", "Rows");
735           swthTable.put("fill_value_name", "_FillValue");
736           swthTable.put("geo_Track", "Rows");
737           swthTable.put("geo_XTrack", "Columns");
738           swthTable.put("geo_fillValue_name", "_FillValue");
739           swthTable.put("range_name", arrayNames[k]);
740
741           SwathAdapter swathAdapter0 = new SwathAdapter(reader, swthTable);
742           swathAdapter0.setDefaultStride(5);
743           Map<String, double[]> subset = swathAdapter0.getDefaultSubset();
744           defaultSubset = subset;
745           adapters[k] = swathAdapter0;
746           defaultSubsets[k] = defaultSubset;
747         }
748
749         categories = DataCategory.parseCategories("2D grid;GRID-2D;");
750         hasImagePreview = true;
751       }
752       else if (name.contains("AWG_CLOUD_HEIGHT")) {
753         String[] arrayNames = new String[] {"CldTopTemp", "CldTopPres", "CldTopHght"};
754
755         adapters = new MultiDimensionAdapter[arrayNames.length];
756         defaultSubsets = new HashMap[arrayNames.length];
757         propsArray = new Hashtable[arrayNames.length];
758
759         for (int k=0; k<arrayNames.length; k++) {
760           Map<String, Object> swthTable = SwathAdapter.getEmptyMetadataTable();
761           swthTable.put("array_name", arrayNames[k]);
762           swthTable.put("lon_array_name", "Longitude");
763           swthTable.put("lat_array_name", "Latitude");
764           swthTable.put("XTrack", "Columns");
765           swthTable.put("Track", "Rows");
766           swthTable.put("scale_name", "scale_factor");
767           swthTable.put("offset_name", "add_offset");
768           swthTable.put("fill_value_name", "_FillValue");
769           swthTable.put("geo_Track", "Rows");
770           swthTable.put("geo_XTrack", "Columns");
771           swthTable.put("geo_scale_name", "scale_factor");
772           swthTable.put("geo_offset_name", "add_offset");
773           swthTable.put("geo_fillValue_name", "_FillValue");
774           swthTable.put("range_name", arrayNames[k]);
775           swthTable.put("unpack", "unpack");
776
777           SwathAdapter swathAdapter0 = new SwathAdapter(reader, swthTable);
778           swathAdapter0.setDefaultStride(5);
779           Map<String, double[]> subset = swathAdapter0.getDefaultSubset();
780           defaultSubset = subset;
781           adapters[k] = swathAdapter0;
782           defaultSubsets[k] = defaultSubset;
783         }
784         categories = DataCategory.parseCategories("2D grid;GRID-2D;");
785         hasImagePreview = true;
786       }
787       else if (name.startsWith("geocatL2") && name.endsWith("ci.hdf")) {
788         String[] arrayNames = new String[] {"box_average_11um_ctc", "box_average_11um_ctc_scaled", "conv_init", "cloud_type"};
789
790         adapters = new MultiDimensionAdapter[arrayNames.length];
791         defaultSubsets = new HashMap[arrayNames.length];
792         propsArray = new Hashtable[arrayNames.length];
793
794         for (int k=0; k<arrayNames.length; k++) {
795           Map<String, Object> swthTable = SwathAdapter.getEmptyMetadataTable();
796           swthTable.put("array_name", arrayNames[k]);
797           swthTable.put("lon_array_name", "lon");
798           swthTable.put("lat_array_name", "lat");
799           swthTable.put("XTrack", "Elements");
800           swthTable.put("Track", "Lines");
801           swthTable.put("geo_Track", "Lines");
802           swthTable.put("geo_XTrack", "Elements");
803           swthTable.put("range_name", arrayNames[k]);
804
805           SwathAdapter swathAdapter0 = new SwathAdapter(reader, swthTable);
806           swathAdapter0.setDefaultStride(1);
807           Map<String, double[]> subset = swathAdapter0.getDefaultSubset();
808           defaultSubset = subset;
809           adapters[k] = swathAdapter0;
810           defaultSubsets[k] = defaultSubset;
811         }
812         categories = DataCategory.parseCategories("2D grid;GRID-2D;");
813         hasImagePreview = true;
814       } 
815       else {
816         String[] arrayNames = new String[] {"baseline_cmask_seviri_cloud_mask", "baseline_ctype_seviri_cloud_type",
817                                             "baseline_ctype_seviri_cloud_phase", "baseline_cld_hght_seviri_cloud_top_pressure",
818                                             "baseline_cld_hght_seviri_cloud_top_height"};
819
820         adapters = new MultiDimensionAdapter[arrayNames.length];
821         defaultSubsets = new HashMap[arrayNames.length];
822         propsArray = new Hashtable[arrayNames.length]; 
823
824         for (int k=0; k<arrayNames.length; k++) {
825           Map<String, Object> swthTable = SwathAdapter.getEmptyMetadataTable();
826           swthTable.put("array_name", arrayNames[k]);
827           swthTable.put("lon_array_name", "pixel_longitude");
828           swthTable.put("lat_array_name", "pixel_latitude");
829           swthTable.put("XTrack", "elements");
830           swthTable.put("Track", "lines");
831           swthTable.put("scale_name", "scale_factor");
832           swthTable.put("offset_name", "add_offset");
833           swthTable.put("fill_value_name", "_FillValue");
834           swthTable.put("geo_Track", "lines");
835           swthTable.put("geo_XTrack", "elements");
836           swthTable.put("geo_scale_name", "scale_factor");
837           swthTable.put("geo_offset_name", "add_offset");
838           swthTable.put("geo_fillValue_name", "_FillValue");
839           swthTable.put("range_name", arrayNames[k]);
840           swthTable.put("unpack", "unpack");
841
842           SwathAdapter swathAdapter0 = new SwathAdapter(reader, swthTable);
843           swathAdapter0.setDefaultStride(2);
844           Map<String, double[]> subset = swathAdapter0.getDefaultSubset();
845           defaultSubset = subset;
846           adapters[k] = swathAdapter0;
847           defaultSubsets[k] = defaultSubset;
848         }
849         categories = DataCategory.parseCategories("2D grid;GRID-2D;");
850         hasImagePreview = true;
851       }
852
853       setProperties(properties);
854    }
855
856    public void initAfterUnpersistence() {
857      try {
858        setup();
859      } catch (Exception e) {
860        logger.error("could not set up after unpersisting", e);
861      }
862    }
863
864    /**
865     * Make and insert the {@link DataChoice DataChoices} for this {@code DataSource}.
866     */
867    public void doMakeDataChoices() {
868        DataChoice choice = null;
869        if (adapters != null) {
870          for (int idx=0; idx<adapters.length; idx++) {
871             try {
872               String arrayName = (adapters[idx] == null) ? "_     " : adapters[idx].getArrayName();
873               choice = doMakeDataChoice(idx, arrayName);
874               adapterMap.put(choice.getName(), adapters[idx]);
875             } catch (Exception e) {
876               logger.error("error making data choices", e);
877             }
878
879             if (choice != null) {
880               addDataChoice(choice);
881             }
882          }
883        }
884    }
885
886    private DataChoice doMakeDataChoice(int idx, String var) throws Exception {
887        String name = var;
888        DataSelection dataSel = (defaultSubsets[idx] == null) ? new MultiDimensionSubset() : new MultiDimensionSubset(defaultSubsets[idx]);
889        Hashtable props = new Hashtable();
890        props.put(new MultiDimensionSubset(), dataSel);
891
892        if (propsArray != null) {
893          if (propsArray[idx] != null) {
894            propsArray[idx].put(new MultiDimensionSubset(), dataSel);
895            props = propsArray[idx];
896          }
897        }
898        DirectDataChoice ddc = null;
899
900        if (categories != null) {
901           ddc = new DirectDataChoice(this, idx, name, name, categories, props);
902        }
903        else {
904           ddc = new DirectDataChoice(this, idx, name, name, categoriesArray[idx], props);
905        }
906
907        return ddc;
908    }
909
910    /**
911     * Check to see if this {@code HDFHydraDataSource} is equal to the object
912     * in question.
913     * @param o  object in question
914     * @return true if they are the same or equivalent objects
915     */
916    public boolean equals(Object o) {
917        if ( !(o instanceof MultiDimensionDataSource)) {
918            return false;
919        }
920        return (this == (MultiDimensionDataSource) o);
921    }
922
923    public MultiSpectralData getMultiSpectralData() {
924      return multiSpectData;
925    }
926
927    public String getDatasetName() {
928      return filename;
929    }
930
931    public void setDatasetName(String name) {
932      filename = name;
933    }
934
935    public Map<String, double[]> getSubsetFromLonLatRect(MultiDimensionSubset select, GeoSelection geoSelection) {
936      GeoLocationInfo ginfo = geoSelection.getBoundingBox();
937      logger.debug("ginfo0: " + ginfo);
938      return adapters[0].getSubsetFromLonLatRect(select.getSubset(), ginfo.getMinLat(), ginfo.getMaxLat(),
939                                        ginfo.getMinLon(), ginfo.getMaxLon());
940    }
941
942    public synchronized Data getData(DataChoice dataChoice, DataCategory category,
943                                DataSelection dataSelection, Hashtable requestProperties)
944                                throws VisADException, RemoteException {
945       return this.getDataInner(dataChoice, category, dataSelection, requestProperties);
946    }
947
948
949    protected Data getDataInner(DataChoice dataChoice, DataCategory category,
950                                DataSelection dataSelection, Hashtable requestProperties)
951                                throws VisADException, RemoteException {
952
953        MultiDimensionAdapter adapter = null;
954        adapter = adapterMap.get(dataChoice.getName());
955
956        Hashtable dataChoiceProps = dataChoice.getProperties();
957
958        //- this hack keeps the HydraImageProbe from doing a getData()
959        //- TODO: need to use categories?
960        if (requestProperties != null) {
961          if ((requestProperties.toString()).equals("{prop.requester=MultiSpectral}")) {
962            return null;
963          }
964        }
965
966        GeoLocationInfo ginfo = null;
967        GeoSelection geoSelection = null;
968        
969        if ((dataSelection != null) && (dataSelection.getGeoSelection() != null)) {
970
971          if (dataSelection.getGeoSelection().getBoundingBox() != null) {
972            geoSelection = dataSelection.getGeoSelection();
973          }
974          else { // no bounding box in the incoming DataSelection. Check the dataChoice.
975            DataSelection datSelFromChoice = dataChoice.getDataSelection();
976            if (datSelFromChoice != null) {
977              geoSelection = datSelFromChoice.getGeoSelection();
978            }
979          }
980        }
981
982        if (geoSelection != null) {
983          ginfo = geoSelection.getBoundingBox();
984        }
985
986        // Still no geo info so check for the lon/lat b.b. in this datasource (last set by DataSelectionComponent)
987        if (ginfo == null) {
988           DataSelection localDataSelection = getDataSelection();
989           if (localDataSelection != null) {
990              geoSelection = localDataSelection.getGeoSelection();
991              if (geoSelection != null) {
992                 ginfo = geoSelection.getBoundingBox();
993              }
994           }
995        }
996
997        Data data = null;
998        if (adapters == null) {
999          return data;
1000        }
1001
1002        Map<String, double[]> subset = null;
1003        MultiDimensionSubset select = null;
1004
1005        Hashtable table = dataChoice.getProperties();
1006        Enumeration keys = table.keys();
1007        while (keys.hasMoreElements()) {
1008           Object key = keys.nextElement();
1009           if (key instanceof MultiDimensionSubset) {
1010              select = (MultiDimensionSubset) table.get(key);
1011           }
1012        }
1013
1014        try {
1015            subset = null;
1016            if (ginfo != null) {
1017                if (trackSelection != null) {
1018                        boolean trackStrideOk = trackSelection.setTrackStride();
1019                        boolean verticalStrideOk = trackSelection.setVerticalStride();
1020                        
1021                        if (trackStrideOk && verticalStrideOk) {
1022                        subset = adapter.getSubsetFromLonLatRect(ginfo.getMinLat(), ginfo.getMaxLat(),
1023                                        ginfo.getMinLon(), ginfo.getMaxLon(),
1024                                        trackSelection.trackStride,
1025                                        trackSelection.verticalStride,
1026                                        geoSelection.getZStride());
1027                        } else {
1028                                // one of the strides is not an integer, let user know
1029                            String msg = "Either the Track or Vertical Stride is invalid.\n" +
1030                                         "Stride values must be positive integers.\n";
1031                                Object[] params = { msg };
1032                                JOptionPane.showMessageDialog(null, params, "Invalid Stride", JOptionPane.OK_OPTION);
1033                                return null;
1034                        }
1035                } else {
1036                        subset = adapter.getSubsetFromLonLatRect(ginfo.getMinLat(), ginfo.getMaxLat(),
1037                                        ginfo.getMinLon(), ginfo.getMaxLon(),
1038                                        geoSelection.getXStride(),
1039                                        geoSelection.getYStride(),
1040                                        geoSelection.getZStride());
1041                }
1042              if (subset == null && select != null) {
1043                subset = select.getSubset();
1044              }
1045            }
1046            else { // no IDV incoming spatial selection info, so check for HYDRA specific via Properties
1047                
1048                if (trackSelection != null) {
1049                    boolean trackStrideOk = trackSelection.setTrackStride();
1050                    boolean verticalStrideOk = trackSelection.setVerticalStride();
1051                    if ((! trackStrideOk) || (! verticalStrideOk)) {
1052                        // one of the strides is not an integer, let user know
1053                        String msg = "Either the Track or Vertical Stride is invalid.\n" +
1054                                "Stride values must be positive integers.\n";
1055                        Object[] params = { msg };
1056                        JOptionPane.showMessageDialog(null, params, "Invalid Stride", JOptionPane.OK_OPTION);
1057                        return null;
1058                    }
1059                    boolean lengthPercent = trackSelection.setLengthPercent();
1060                    if (! lengthPercent) {
1061                        // specified percentage of total track is invalid
1062                        String msg = "Percent of total track is invalid.\n" +
1063                                "Value must be between 1 and 100.\n";
1064                        Object[] params = { msg };
1065                        JOptionPane.showMessageDialog(null, params, "Invalid Track Percentage", JOptionPane.OK_OPTION);
1066                        return null;
1067                    }
1068                }
1069                
1070              if (select != null) {
1071                subset = select.getSubset();
1072              }
1073              
1074              if (dataSelection != null) {
1075                Hashtable props = dataSelection.getProperties();
1076              }
1077            }
1078            
1079            if (subset != null) {
1080              data = adapter.getData(subset);
1081              data = applyProperties(data, dataChoiceProps, subset);
1082            }
1083        } catch (Exception e) {
1084            logger.error("getData exception", e);
1085        }
1086
1087        return data;
1088    }
1089
1090    protected Data applyProperties(Data data, Hashtable requestProperties, Map<String, double[]> subset)
1091          throws VisADException, RemoteException, Exception {
1092      Data new_data = data;
1093
1094      if (requestProperties == null) {
1095        new_data = data;
1096        return new_data;
1097      }
1098
1099      if (requestProperties.containsKey("medianFilter")) {
1100        String[] items = (String[]) requestProperties.get("medianFilter");
1101        int windowVertLen = Integer.parseInt(items[0]);
1102        int windowHorzLen = Integer.parseInt(items[1]);
1103        GriddedSet domainSet = (GriddedSet) ((FlatField)data).getDomainSet();
1104        int[] lens = domainSet.getLengths();
1105        float[] range_values = (((FlatField)data).getFloats())[0];
1106        range_values =
1107           ProfileAlongTrack.medianFilter(range_values, lens[0], lens[1], windowHorzLen, windowVertLen);
1108        ((FlatField)new_data).setSamples(new float[][] {range_values});
1109      }
1110      if (requestProperties.containsKey("setBelowSfcMissing")) {
1111        FlatField track = (FlatField) track_adapter.getData(subset);
1112        float[] sfcElev = (track.getFloats())[0];
1113        FlatField field = (FlatField) new_data;
1114        GriddedSet gset = (GriddedSet) field.getDomainSet();
1115        float[][] samples = gset.getSamples(false);
1116        int[] lens = gset.getLengths();
1117        float[] range_values = (field.getFloats())[0];
1118
1119        int trkIdx = ((ProfileAlongTrack3D)adapters[0]).adapter2D.getTrackTupIdx();
1120        int vrtIdx = ((ProfileAlongTrack3D)adapters[0]).adapter2D.getVertTupIdx();
1121
1122        int k = 0;
1123        for (int j=0; j<lens[trkIdx]; j++) {
1124          float val = sfcElev[j];
1125          for (int i=0; i<lens[vrtIdx]; i++) {
1126            if (vrtIdx < trkIdx) k = i + j*lens[0];
1127            if (trkIdx < vrtIdx) k = j + i*lens[0];
1128            if (samples[2][k] <= val) {
1129              range_values[k] = Float.NaN;
1130            }
1131          }
1132        }
1133        field.setSamples(new float[][] {range_values});
1134      }
1135      return new_data;
1136    }
1137
1138    protected void initDataSelectionComponents(
1139         List<DataSelectionComponent> components,
1140             final DataChoice dataChoice) {
1141
1142      if (hasImagePreview) {
1143        try {
1144          FlatField image = (FlatField) getDataInner(dataChoice, null, null, null);
1145          components.add(new PreviewSelection(dataChoice, image, null));
1146        } catch (Exception e) {
1147          logger.error("cannot make preview selection", e);
1148        }
1149      }
1150      if (hasTrackPreview) {
1151        try {
1152          FlatField track = track_adapter.getData(track_adapter.getDefaultSubset());     
1153          Map defaultSubset = (adapterMap.get(dataChoice.getName())).getDefaultSubset();
1154          trackSelection = new TrackSelection(dataChoice, track, defaultSubset);
1155          components.add(trackSelection);
1156        } catch (Exception e) {
1157          logger.error("cannot make preview selection", e);
1158        }
1159      }
1160    }
1161
1162    private String getTrackDimensionName(String variableName) {
1163        return getVariableDimensionName(variableName, 0);
1164    }
1165
1166    private String getVerticalDimensionName(String variableName) {
1167        return getVariableDimensionName(variableName, 1);
1168    }
1169
1170    private String getVariableDimensionName(String variableName, int dimension) {
1171        NetcdfFile ncfile = ((NetCDFFile)reader).getNetCDFFile();
1172        Variable v = ncfile.findVariable(variableName);
1173        String name = null;
1174        if (v != null) {
1175            name = v.getDimension(dimension).getFullName();
1176        }
1177        return name;
1178    }
1179
1180    private boolean hasVariable(String variableName) {
1181        NetcdfFile ncfile = ((NetCDFFile) reader).getNetCDFFile();
1182        return ncfile.findVariable(variableName) != null;
1183    }
1184
1185    private ArrayAdapter createTrackVertArrayAdapter(String variableName) {
1186        Map<String, Object> table = SwathAdapter.getEmptyMetadataTable();
1187
1188        String trackDimName = getTrackDimensionName(variableName);
1189        String vertDimName = getVerticalDimensionName(variableName);
1190
1191        table.put(ProfileAlongTrack.array_name, variableName);
1192        table.put(ProfileAlongTrack.trackDim_name, trackDimName);
1193        table.put(ProfileAlongTrack.vertDim_name, vertDimName);
1194        table.put("array_dimension_names", new String[] { trackDimName, vertDimName });
1195
1196        return new ArrayAdapter(reader, table);
1197    }
1198}