001/*
002 * This file is part of McIDAS-V
003 *
004 * Copyright 2007-2024
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 https://www.gnu.org/licenses/.
027 */
028
029package edu.wisc.ssec.mcidasv.data.hydra;
030
031import java.awt.BorderLayout;
032import java.awt.FlowLayout;
033import java.awt.geom.Rectangle2D;
034import java.io.File;
035import java.rmi.RemoteException;
036import java.util.ArrayList;
037import java.util.Collections;
038import java.util.Enumeration;
039import java.util.HashMap;
040import java.util.Hashtable;
041import java.util.LinkedHashSet;
042import java.util.List;
043import java.util.Map;
044import java.util.Set;
045
046import javax.swing.JComponent;
047import javax.swing.JLabel;
048import javax.swing.JPanel;
049import javax.swing.JSplitPane;
050
051import org.slf4j.Logger;
052import org.slf4j.LoggerFactory;
053
054import ucar.unidata.data.DataCategory;
055import ucar.unidata.data.DataChoice;
056import ucar.unidata.data.DataSelection;
057import ucar.unidata.data.DataSelectionComponent;
058import ucar.unidata.data.DataSourceDescriptor;
059import ucar.unidata.data.DirectDataChoice;
060import ucar.unidata.data.GeoLocationInfo;
061import ucar.unidata.data.GeoSelection;
062import ucar.unidata.util.Misc;
063
064import visad.CommonUnit;
065import visad.CoordinateSystem;
066import visad.Data;
067import visad.FlatField;
068import visad.FunctionType;
069import visad.Gridded2DSet;
070import visad.Linear2DSet;
071import visad.RealTupleType;
072import visad.RealType;
073import visad.SetType;
074import visad.VisADException;
075import visad.georef.MapProjection;
076
077import edu.wisc.ssec.mcidasv.Constants;
078import edu.wisc.ssec.mcidasv.control.LambertAEA;
079import edu.wisc.ssec.mcidasv.data.ComboDataChoice;
080import edu.wisc.ssec.mcidasv.data.HydraDataSource;
081import edu.wisc.ssec.mcidasv.data.PreviewSelection;
082import edu.wisc.ssec.mcidasv.display.hydra.MultiSpectralDisplay;
083
084/**
085 * A data source for Multi Dimension Data 
086 */
087
088public class MultiSpectralDataSource extends HydraDataSource {
089
090        private static final Logger logger = LoggerFactory.getLogger(MultiSpectralDataSource.class);
091        
092        /** Sources file */
093    protected String filename;
094
095    protected MultiDimensionReader reader;
096
097    protected MultiDimensionAdapter[] adapters = null;
098
099    private static final String DATA_DESCRIPTION = "Multi Dimension Data";
100
101
102    private Map<String, double[]> defaultSubset;
103    private SwathAdapter swathAdapter;
104    private SpectrumAdapter spectrumAdapter;
105    private MultiSpectralData multiSpectData;
106
107    private List<MultiSpectralData> multiSpectData_s = new ArrayList<>();
108    private Map<String, MultiSpectralData> adapterMap = new HashMap<>();
109
110    private List categories;
111    private boolean hasImagePreview = false;
112    private boolean hasChannelSelect = false;
113
114    private boolean doAggregation = false;
115
116    private ComboDataChoice comboChoice;
117
118    private PreviewSelection previewSelection = null;
119    private FlatField previewImage = null;
120
121    public static final String paramKey = "paramKey";
122
123    /**
124     * Zero-argument constructor for construction via unpersistence.
125     */
126    public MultiSpectralDataSource() {}
127
128    public MultiSpectralDataSource(String fileName) throws VisADException {
129      this(null, Misc.newList(fileName), null);
130    }
131
132    /**
133     * Construct a new HYDRA hdf data source.
134     * @param  descriptor  descriptor for this {@code DataSource}
135     * @param  fileName  name of the hdf file to read
136     * @param  properties  hashtable of properties
137     *
138     * @throws VisADException problem creating data
139     */
140    public MultiSpectralDataSource(DataSourceDescriptor descriptor,
141                                 String fileName, Hashtable properties)
142            throws VisADException {
143        this(descriptor, Misc.newList(fileName), properties);
144    }
145
146    /**
147     * Construct a new HYDRA hdf data source.
148     * @param  descriptor  descriptor for this {@code DataSource}
149     * @param  newSources   List of filenames
150     * @param  properties  hashtable of properties
151     *
152     * @throws VisADException problem creating data
153     */
154    public MultiSpectralDataSource(DataSourceDescriptor descriptor,
155                                 List newSources, Hashtable properties)
156            throws VisADException {
157        super(descriptor, newSources, DATA_DESCRIPTION, properties);
158
159        this.filename = (String)sources.get(0);
160
161        try {
162          setup();
163        }
164        catch (Exception e) {
165          throw new VisADException("could not set up data source", e);
166        }
167    }
168
169    public void setup() throws Exception {
170        String name = (new File(filename)).getName();
171        // aggregations will use sets of NetCDFFile readers
172        List<NetCDFFile> ncdfal = new ArrayList<>();
173
174        try {
175          if (name.startsWith("NSS.HRPT.NP") && name.endsWith("obs.hdf")) { // get file union
176            String other = filename.replace("obs", "nav");
177            reader = NetCDFFile.makeUnion(filename, other);
178          }
179          /**
180          else if (name.startsWith("MYD021KM")) { //hack test code
181            //reader = new NetCDFFileUnion(new String[] {filename, "/Users/rink/Downloads/MYD03.A2011331.0405.005.2011332200700.hdf"}); 
182            reader = new NetCDFFile(filename);
183          }
184          */
185          else {
186                  if (sources.size() > 1) {
187                          for (int i = 0; i < sources.size(); i++) {
188                                  String s = (String) sources.get(i);
189                                  ncdfal.add(new NetCDFFile(s));
190                          }
191                          doAggregation = true;
192                  } else {
193                          reader = new NetCDFFile(filename);
194                  }
195          }
196        } catch (Exception e) {
197            logger.error("Cannot create NetCDF reader for file: " + filename, e);
198        }
199                                                                                                                                                     
200        Hashtable<String, String[]> properties = new Hashtable<>();
201
202        multiSpectData_s.clear();
203
204        // AIRS data
205        if (name.startsWith("AIRS")) {
206                // make two data choices, Radiance and BrightnessTemperature
207                // index 0: Rad, index 1: BT
208                int choiceCount = 2;
209                for (int i = 0; i < choiceCount; i++) {
210                Map<String, Object> table = SpectrumAdapter.getEmptyMetadataTable();
211                        if (i == 0) {
212                                table.put(SpectrumAdapter.array_name, "L1B_AIRS_Science/Data_Fields/radiances");
213                                table.put(SpectrumAdapter.range_name, "Radiance");
214                        } else {
215                                table.put(SpectrumAdapter.array_name, "L1B_AIRS_Science/Data_Fields/radiances");
216                                table.put(SpectrumAdapter.range_name, "BrightnessTemperature");
217                        }
218
219                        table.put(SpectrumAdapter.channelIndex_name, "Channel");
220                        table.put(SpectrumAdapter.ancillary_file_name, "/edu/wisc/ssec/mcidasv/data/hydra/resources/airs/L2.chan_prop.2003.11.19.v6.6.9.anc");
221                        table.put(SpectrumAdapter.x_dim_name, "GeoXTrack");
222                        table.put(SpectrumAdapter.y_dim_name, "GeoTrack");
223                        SpectrumAdapter spectrumAdapter = new AIRS_L1B_Spectrum(reader, table);
224
225                        table = SwathAdapter.getEmptyMetadataTable();
226                        if (i == 0) {
227                                table.put(SwathAdapter.array_name, "L1B_AIRS_Science/Data_Fields/radiances");
228                                table.put(SwathAdapter.range_name, "Radiance");
229                        } else {
230                                table.put(SwathAdapter.array_name, "L1B_AIRS_Science/Data_Fields/radiances");
231                                table.put(SwathAdapter.range_name, "BrightnessTemperature");
232                        }
233                        table.put("lon_array_name", "L1B_AIRS_Science/Geolocation_Fields/Longitude");
234                        table.put("lat_array_name", "L1B_AIRS_Science/Geolocation_Fields/Latitude");
235                        table.put("XTrack", "GeoXTrack");
236                        table.put("Track", "GeoTrack");
237                        table.put("geo_Track", "GeoTrack");
238                        table.put("geo_XTrack", "GeoXTrack");
239                        table.put(SpectrumAdapter.channelIndex_name, "Channel"); //- think about this?
240
241                        SwathAdapter swathAdapter = new SwathAdapter(reader, table);
242                        Map<String, double[]> subset = swathAdapter.getDefaultSubset();
243                        subset.put(SpectrumAdapter.channelIndex_name, new double[] {793,793,1});
244                        defaultSubset = subset;
245
246                        multiSpectData = new MultiSpectralData(swathAdapter, spectrumAdapter);
247                        // Need to change paramater and range for Radiance, default is Brightness Temp, so
248                        // do nothing for 2nd time through loop
249                        if (i == 0) {
250                                multiSpectData.setParamName("Radiance");
251                                float [] radianceRange = new float[] {-20.0f, 140.0f};
252                                multiSpectData.setDataRange(radianceRange);
253                        }
254                        DataCategory.createCategory("MultiSpectral");
255                        categories = DataCategory.parseCategories("MultiSpectral;MultiSpectral;IMAGE");
256                        hasChannelSelect = true;
257                        multiSpectData.init_wavenumber = 919.5f; 
258
259                        multiSpectData_s.add(multiSpectData);
260                }
261        }
262       else if ( name.startsWith("IASI_xxx_1C") && name.endsWith("h5")) {
263          Map<String, Object> table = SpectrumAdapter.getEmptyMetadataTable();
264          table.put(SpectrumAdapter.array_name, "U-MARF/EPS/IASI_xxx_1C/DATA/SPECT_DATA");
265          table.put(SpectrumAdapter.channelIndex_name, "dim2");
266          table.put(SpectrumAdapter.x_dim_name, "dim1");
267          table.put(SpectrumAdapter.y_dim_name, "dim0");
268          spectrumAdapter = new IASI_L1C_Spectrum(reader, table);
269                                                                                                                                             
270          table = SwathAdapter.getEmptyMetadataTable();
271          table.put("array_name", "U-MARF/EPS/IASI_xxx_1C/DATA/SPECT_DATA");
272          table.put("lon_array_name", "U-MARF/EPS/IASI_xxx_1C/DATA/SPECT_LON_ARRAY");
273          table.put("lat_array_name", "U-MARF/EPS/IASI_xxx_1C/DATA/SPECT_LAT_ARRAY");
274          table.put("XTrack", "dim1");
275          table.put("Track", "dim0");
276          table.put("geo_XTrack", "dim1");
277          table.put("geo_Track", "dim0");
278          table.put("product_name", "IASI_L1C_xxx");
279          table.put(SpectrumAdapter.channelIndex_name, "dim2");
280          swathAdapter = new IASI_L1C_SwathAdapter(reader, table);
281          Map<String, double[]> subset = swathAdapter.getDefaultSubset();
282          subset.put(SpectrumAdapter.channelIndex_name, new double[] {793,793,1});
283          defaultSubset = subset;
284          multiSpectData = new MultiSpectralData(swathAdapter, spectrumAdapter);
285          DataCategory.createCategory("MultiSpectral");
286          categories = DataCategory.parseCategories("MultiSpectral;MultiSpectral;");
287          multiSpectData.init_wavenumber = 919.5f; 
288          hasChannelSelect = true;
289          multiSpectData_s.add(multiSpectData);
290       }
291       else if ( name.startsWith("IASI")) {
292          Map<String, Object> table = SpectrumAdapter.getEmptyMetadataTable();
293          table.put(SpectrumAdapter.array_name, "observations");
294          table.put(SpectrumAdapter.channelIndex_name, "obsChannelIndex");
295          table.put(SpectrumAdapter.x_dim_name, "obsElement");
296          table.put(SpectrumAdapter.y_dim_name, "obsLine");
297          table.put(SpectrumAdapter.channels_name, "observationChannels");
298          spectrumAdapter = new SpectrumAdapter(reader, table);
299
300          table = SwathAdapter.getEmptyMetadataTable();
301          table.put("array_name", "observations");
302          table.put("lon_array_name", "obsLongitude");
303          table.put("lat_array_name", "obsLatitude");
304          table.put("XTrack", "obsElement");
305          table.put("Track", "obsLine");
306          table.put("geo_XTrack", "obsElement");
307          table.put("geo_Track", "obsLine");
308          table.put(SpectrumAdapter.channelIndex_name, "obsChannelIndex"); //- think about this?
309          swathAdapter = new SwathAdapter(reader, table);
310          Map<String, double[]> subset = swathAdapter.getDefaultSubset();
311          subset.put(SpectrumAdapter.channelIndex_name, new double[] {793,793,1});
312          defaultSubset = subset;
313          multiSpectData = new MultiSpectralData(swathAdapter, spectrumAdapter);
314          DataCategory.createCategory("MultiSpectral");
315          categories = DataCategory.parseCategories("MultiSpectral;MultiSpectral;");
316          multiSpectData.init_wavenumber = 919.5f; 
317          multiSpectData_s.add(multiSpectData);
318          hasChannelSelect = true;
319       }
320       else if (name.startsWith("MOD021KM") || name.startsWith("MYD021KM") || 
321               (name.startsWith("a1") && (name.indexOf("1000m") > 0)) || 
322               (name.startsWith("t1") && (name.indexOf("1000m") > 0)) ) {
323         Map<String, Object> table = SwathAdapter.getEmptyMetadataTable();
324         table.put("array_name", "MODIS_SWATH_Type_L1B/Data_Fields/EV_1KM_Emissive");
325         table.put("lon_array_name", "MODIS_SWATH_Type_L1B/Geolocation_Fields/Longitude");
326         table.put("lat_array_name", "MODIS_SWATH_Type_L1B/Geolocation_Fields/Latitude");
327         //table.put("lon_array_name", "MODIS_Swath_Type_GEO/Geolocation_Fields/Longitude");
328         //table.put("lat_array_name", "MODIS_Swath_Type_GEO/Geolocation_Fields/Latitude");
329         table.put("XTrack", "Max_EV_frames");
330         table.put("Track", "10*nscans");
331         table.put("geo_Track", "2*nscans");
332         table.put("geo_XTrack", "1KM_geo_dim");
333         //table.put("geo_Track", "nscans*10");
334         //table.put("geo_XTrack", "mframes");
335         table.put("scale_name", "radiance_scales");
336         table.put("offset_name", "radiance_offsets");
337         table.put("fill_value_name", "_FillValue");
338         table.put("range_name", "Emissive_Bands");
339         table.put(SpectrumAdapter.channelIndex_name, "Band_1KM_Emissive");
340         table.put(SwathAdapter.geo_track_offset_name, Double.toString(2.0));
341         table.put(SwathAdapter.geo_xtrack_offset_name, Double.toString(2.0));
342         table.put(SwathAdapter.geo_track_skip_name, Double.toString(5.0));
343         table.put(SwathAdapter.geo_xtrack_skip_name, Double.toString(5.0));
344         table.put(SwathAdapter.multiScaleDimensionIndex, Integer.toString(0));
345         
346         // initialize the aggregation reader object
347         logger.debug("Trying to create MODIS 1K GranuleAggregation reader...");
348         Set<String> products = new LinkedHashSet<>();
349         products.add((String) table.get("array_name"));
350         products.add("MODIS_SWATH_Type_L1B/Data_Fields/EV_1KM_RefSB");
351         products.add("MODIS_SWATH_Type_L1B/Data_Fields/EV_250_Aggr1km_RefSB");
352         products.add("MODIS_SWATH_Type_L1B/Data_Fields/EV_500_Aggr1km_RefSB");
353         products.add("MODIS_SWATH_Type_L1B/Data_Fields/EV_250_Aggr500_RefSB");
354         products.add("MODIS_SWATH_Type_L1B/Data_Fields/EV_250_RefSB");
355         products.add("MODIS_SWATH_Type_L1B/Data_Fields/EV_500_RefSB");
356         products.add((String) table.get("lon_array_name"));
357         products.add((String) table.get("lat_array_name"));
358         if (doAggregation) {
359                 try {
360                         reader = new GranuleAggregation(ncdfal, products, "10*nscans", "2*nscans", "Max_EV_frames");
361                 } catch (Exception e) {
362                         throw new VisADException("Unable to initialize aggregation reader", e);
363                 }
364         }
365
366         swathAdapter = new SwathAdapter(reader, table);
367         swathAdapter.setDefaultStride(10);
368         logger.debug("Trying to create MODIS 1K SwathAdapter...");
369
370         Map<String, double[]> subset = swathAdapter.getDefaultSubset();
371
372         table = SpectrumAdapter.getEmptyMetadataTable();
373         table.put(SpectrumAdapter.array_name, "MODIS_SWATH_Type_L1B/Data_Fields/EV_1KM_Emissive");
374         table.put(SpectrumAdapter.channelIndex_name, "Band_1KM_Emissive");
375         table.put(SpectrumAdapter.x_dim_name, "Max_EV_frames");
376         table.put(SpectrumAdapter.y_dim_name, "10*nscans");
377         table.put(SpectrumAdapter.channelValues, new float[]
378           {3.799f,3.992f,3.968f,4.070f,4.476f,4.549f,6.784f,7.345f,8.503f,
379            9.700f,11.000f,12.005f,13.351f,13.717f,13.908f,14.205f});
380         table.put(SpectrumAdapter.bandNames, new String[] 
381           {"20","21","22","23","24","25","27","28","29",
382            "30","31","32","33","34","35","36"});
383         table.put(SpectrumAdapter.channelType, "wavelength");
384         SpectrumAdapter spectrumAdapter = new SpectrumAdapter(reader, table);
385
386         multiSpectData = new MultiSpectralData(swathAdapter, spectrumAdapter, "MODIS", "Aqua");
387         multiSpectData.setInitialWavenumber(11.0f);
388         defaultSubset = multiSpectData.getDefaultSubset();
389
390         previewImage = multiSpectData.getImage(defaultSubset);
391         multiSpectData_s.add(multiSpectData);
392
393         //--- aggregate reflective bands
394         table = SwathAdapter.getEmptyMetadataTable();
395
396         table.put("array_name", "MODIS_SWATH_Type_L1B/Data_Fields/EV_1KM_RefSB");
397         table.put("lon_array_name", "MODIS_SWATH_Type_L1B/Geolocation_Fields/Longitude");
398         table.put("lat_array_name", "MODIS_SWATH_Type_L1B/Geolocation_Fields/Latitude");
399         table.put("XTrack", "Max_EV_frames");
400         table.put("Track", "10*nscans");
401         table.put("geo_Track", "2*nscans");
402         table.put("geo_XTrack", "1KM_geo_dim");
403         table.put("scale_name", "reflectance_scales");
404         table.put("offset_name", "reflectance_offsets");
405         table.put("fill_value_name", "_FillValue");
406         table.put("range_name", "EV_1KM_RefSB");
407         table.put(SpectrumAdapter.channelIndex_name, "Band_1KM_RefSB");
408
409         table.put(SwathAdapter.geo_track_offset_name, Double.toString(2.0));
410         table.put(SwathAdapter.geo_xtrack_offset_name, Double.toString(2.0));
411         table.put(SwathAdapter.geo_track_skip_name, Double.toString(5.0));
412         table.put(SwathAdapter.geo_xtrack_skip_name, Double.toString(5.0));
413         table.put(SwathAdapter.multiScaleDimensionIndex, Integer.toString(0));
414
415         SwathAdapter sadapt0 = new SwathAdapter(reader, table);
416         sadapt0.setDefaultStride(10);
417
418         table = SpectrumAdapter.getEmptyMetadataTable();
419         table.put(SpectrumAdapter.array_name, "MODIS_SWATH_Type_L1B/Data_Fields/EV_1KM_RefSB");
420         table.put(SpectrumAdapter.channelIndex_name, "Band_1KM_RefSB");
421         table.put(SpectrumAdapter.x_dim_name, "Max_EV_frames");
422         table.put(SpectrumAdapter.y_dim_name, "10*nscans");
423         table.put(SpectrumAdapter.channelValues, new float[]
424            {.412f,.450f,.487f,.531f,.551f,.666f,.668f,.677f,.679f,.748f,
425             .869f,.905f,.936f,.940f,1.375f});
426         table.put(SpectrumAdapter.bandNames, new String[]
427            {"8","9","10","11","12","13lo","13hi","14lo","14hi","15",
428             "16","17","18","19","26"});
429         table.put(SpectrumAdapter.channelType, "wavelength");
430         SpectrumAdapter specadap0 = new SpectrumAdapter(reader, table);
431         MultiSpectralData multispec0 = new MultiSpectralData(sadapt0, specadap0, "Reflectance", "Reflectance", "MODIS", "Aqua");
432
433         DataCategory.createCategory("MultiSpectral");
434         categories = DataCategory.parseCategories("MultiSpectral;MultiSpectral;IMAGE");
435         hasImagePreview = true;
436         hasChannelSelect = true;
437
438         table = SwathAdapter.getEmptyMetadataTable();
439
440         table.put("array_name", "MODIS_SWATH_Type_L1B/Data_Fields/EV_250_Aggr1km_RefSB");
441         table.put("lon_array_name", "MODIS_SWATH_Type_L1B/Geolocation_Fields/Longitude");
442         table.put("lat_array_name", "MODIS_SWATH_Type_L1B/Geolocation_Fields/Latitude");
443         table.put("XTrack", "Max_EV_frames");
444         table.put("Track", "10*nscans");
445         table.put("geo_Track", "2*nscans");
446         table.put("geo_XTrack", "1KM_geo_dim");
447         table.put("scale_name", "reflectance_scales");
448         table.put("offset_name", "reflectance_offsets");
449         table.put("fill_value_name", "_FillValue");
450         table.put("range_name", "Reflective_Bands");
451         table.put(SpectrumAdapter.channelIndex_name, "Band_250M");
452
453         table.put(SwathAdapter.geo_track_offset_name, Double.toString(2.0));
454         table.put(SwathAdapter.geo_xtrack_offset_name, Double.toString(2.0));
455         table.put(SwathAdapter.geo_track_skip_name, Double.toString(5.0));
456         table.put(SwathAdapter.geo_xtrack_skip_name, Double.toString(5.0));
457         table.put(SwathAdapter.multiScaleDimensionIndex, Integer.toString(0));
458
459         SwathAdapter sadapt1 = new SwathAdapter(reader, table);
460
461         table = SpectrumAdapter.getEmptyMetadataTable();
462         table.put(SpectrumAdapter.array_name, "MODIS_SWATH_Type_L1B/Data_Fields/EV_250_Aggr1km_RefSB");
463         table.put(SpectrumAdapter.channelIndex_name, "Band_250M");
464         table.put(SpectrumAdapter.x_dim_name, "Max_EV_frames");
465         table.put(SpectrumAdapter.y_dim_name, "10*nscans");
466         table.put(SpectrumAdapter.channelValues, new float[]
467            {.650f,.855f});
468         table.put(SpectrumAdapter.bandNames, new String[]
469            {"1","2"});
470         table.put(SpectrumAdapter.channelType, "wavelength");
471         SpectrumAdapter specadap1 = new SpectrumAdapter(reader, table);
472         MultiSpectralData multispec1 = new MultiSpectralData(sadapt1, specadap1, "Reflectance", "Reflectance", "MODIS", "Aqua");
473
474         table = SwathAdapter.getEmptyMetadataTable();
475
476         table.put("array_name", "MODIS_SWATH_Type_L1B/Data_Fields/EV_500_Aggr1km_RefSB");
477         table.put("lon_array_name", "MODIS_SWATH_Type_L1B/Geolocation_Fields/Longitude");
478         table.put("lat_array_name", "MODIS_SWATH_Type_L1B/Geolocation_Fields/Latitude");
479         table.put("XTrack", "Max_EV_frames");
480         table.put("Track", "10*nscans");
481         table.put("geo_Track", "2*nscans");
482         table.put("geo_XTrack", "1KM_geo_dim");
483         table.put("scale_name", "reflectance_scales");
484         table.put("offset_name", "reflectance_offsets");
485         table.put("fill_value_name", "_FillValue");
486         table.put("range_name", "EV_500_Aggr1km_RefSB");
487         table.put(SpectrumAdapter.channelIndex_name, "Band_500M");
488         table.put(SwathAdapter.geo_track_offset_name, Double.toString(2.0));
489         table.put(SwathAdapter.geo_xtrack_offset_name, Double.toString(2.0));
490         table.put(SwathAdapter.geo_track_skip_name, Double.toString(5.0));
491         table.put(SwathAdapter.geo_xtrack_skip_name, Double.toString(5.0));
492         table.put(SwathAdapter.multiScaleDimensionIndex, Integer.toString(0));
493
494
495         SwathAdapter sadapt2 = new SwathAdapter(reader, table);
496
497
498         table = SpectrumAdapter.getEmptyMetadataTable();
499         table.put(SpectrumAdapter.array_name, "MODIS_SWATH_Type_L1B/Data_Fields/EV_500_Aggr1km_RefSB");
500         table.put(SpectrumAdapter.channelIndex_name, "Band_500M");
501         table.put(SpectrumAdapter.x_dim_name, "Max_EV_frames");
502         table.put(SpectrumAdapter.y_dim_name, "10*nscans");
503         table.put(SpectrumAdapter.channelValues, new float[]
504            {.470f,.555f,1.240f,1.638f,2.130f});
505         table.put(SpectrumAdapter.bandNames, new String[]
506            {"3","4","5","6","7"});
507         table.put(SpectrumAdapter.channelType, "wavelength");
508         SpectrumAdapter specadap2 = new SpectrumAdapter(reader, table);
509         MultiSpectralData multispec2 = new MultiSpectralData(sadapt2, specadap2, "Reflectance", "Reflectance", "MODIS", "Aqua");
510
511         MultiSpectralAggr aggr = new MultiSpectralAggr(new MultiSpectralData[] {multispec1, multispec2, multispec0});
512         aggr.setInitialWavenumber(0.650f);
513         aggr.setDataRange(new float[] {0f, 0.8f});
514         multiSpectData_s.add(aggr);
515       }
516       else if (name.startsWith("MOD02QKM") || name.startsWith("MYD02QKM") ||
517               (name.startsWith("a1") && (name.indexOf("250m") > 0)) ||
518               (name.startsWith("t1") && (name.indexOf("250m") > 0)) ) {
519         Map<String, Object> table = SwathAdapter.getEmptyMetadataTable();
520         table.put("array_name", "MODIS_SWATH_Type_L1B/Data_Fields/EV_250_RefSB");
521         table.put("lon_array_name", "MODIS_SWATH_Type_L1B/Geolocation_Fields/Longitude");
522         table.put("lat_array_name", "MODIS_SWATH_Type_L1B/Geolocation_Fields/Latitude");
523         table.put("XTrack", "4*Max_EV_frames");
524         table.put("Track", "40*nscans");
525         table.put("geo_Track", "10*nscans");
526         table.put("geo_XTrack", "Max_EV_frames");
527         table.put("scale_name", "reflectance_scales");
528         table.put("offset_name", "reflectance_offsets");
529         table.put("fill_value_name", "_FillValue");
530         table.put("range_name", "Reflective_Bands");
531         table.put(SpectrumAdapter.channelIndex_name, "Band_250M");
532         table.put(SwathAdapter.geo_track_offset_name, Double.toString(0.0));
533         table.put(SwathAdapter.geo_xtrack_offset_name, Double.toString(0.0));
534         table.put(SwathAdapter.geo_track_skip_name, Double.toString(4.0));
535         table.put(SwathAdapter.geo_xtrack_skip_name, Double.toString(4.0));
536         table.put(SwathAdapter.multiScaleDimensionIndex, Integer.toString(0));
537         // initialize the aggregation reader object
538         logger.debug("Trying to create MODIS 1K GranuleAggregation reader...");
539         LinkedHashSet<String> products = new LinkedHashSet<String>();
540         products.add((String) table.get("array_name"));
541         products.add("MODIS_SWATH_Type_L1B/Data_Fields/EV_250_RefSB");
542         products.add((String) table.get("lon_array_name"));
543         products.add((String) table.get("lat_array_name"));
544         if (doAggregation) {
545                 try {
546                         reader = new GranuleAggregation(ncdfal, products, "40*nscans", "10*nscans", "4*Max_EV_frames");
547                 } catch (Exception e) {
548                         throw new VisADException("Unable to initialize aggregation reader", e);
549                 }
550         }
551         swathAdapter = new SwathAdapter(reader, table);
552         swathAdapter.setDefaultStride(40);
553
554         table = SpectrumAdapter.getEmptyMetadataTable();
555         table.put(SpectrumAdapter.array_name, "MODIS_SWATH_Type_L1B/Data_Fields/EV_250_RefSB");
556         table.put(SpectrumAdapter.channelIndex_name, "Band_250M");
557         table.put(SpectrumAdapter.x_dim_name, "4*Max_EV_frames");
558         table.put(SpectrumAdapter.y_dim_name, "40*nscans");
559         table.put(SpectrumAdapter.channelValues, new float[]
560            {.650f,.855f});
561         table.put(SpectrumAdapter.bandNames, new String[]
562            {"1","2"});
563         table.put(SpectrumAdapter.channelType, "wavelength");
564         SpectrumAdapter spectrumAdapter = new SpectrumAdapter(reader, table);
565
566         multiSpectData = new MultiSpectralData(swathAdapter, spectrumAdapter, "Reflectance", "Reflectance", "MODIS", "Aqua");
567         multiSpectData.setInitialWavenumber(0.650f);
568         multiSpectData.setDataRange(new float[] {0f, 0.8f});
569         defaultSubset = multiSpectData.getDefaultSubset();
570         previewImage = multiSpectData.getImage(defaultSubset);
571         multiSpectData_s.add(multiSpectData);
572
573         DataCategory.createCategory("MultiSpectral");
574         categories = DataCategory.parseCategories("MultiSpectral;MultiSpectral;IMAGE");
575         hasImagePreview = true;
576         hasChannelSelect = true;
577
578         multiSpectData_s.add(null);
579       }
580       else if (name.startsWith("MOD02HKM") || name.startsWith("MYD02HKM") ||
581               (name.startsWith("a1") && (name.indexOf("500m") > 0)) ||
582               (name.startsWith("t1") && (name.indexOf("500m") > 0)) ) {
583         Map<String, Object> table = SwathAdapter.getEmptyMetadataTable();
584         table.put("array_name", "MODIS_SWATH_Type_L1B/Data_Fields/EV_250_Aggr500_RefSB");
585         table.put("lon_array_name", "MODIS_SWATH_Type_L1B/Geolocation_Fields/Longitude");
586         table.put("lat_array_name", "MODIS_SWATH_Type_L1B/Geolocation_Fields/Latitude");
587         table.put("XTrack", "2*Max_EV_frames");
588         table.put("Track", "20*nscans");
589         table.put("geo_Track", "10*nscans");
590         table.put("geo_XTrack", "Max_EV_frames");
591         table.put("scale_name", "reflectance_scales");
592         table.put("offset_name", "reflectance_offsets");
593         table.put("fill_value_name", "_FillValue");
594         table.put("range_name", "Reflective_Bands");
595         table.put(SpectrumAdapter.channelIndex_name, "Band_250M");
596         table.put(SwathAdapter.geo_track_offset_name, Double.toString(0.0));
597         table.put(SwathAdapter.geo_xtrack_offset_name, Double.toString(0.0));
598         table.put(SwathAdapter.geo_track_skip_name, Double.toString(2.0));
599         table.put(SwathAdapter.geo_xtrack_skip_name, Double.toString(2.0));
600         table.put(SwathAdapter.multiScaleDimensionIndex, Integer.toString(0));
601
602         // initialize the aggregation reader object
603         logger.debug("Trying to create MODIS 1K GranuleAggregation reader...");
604         LinkedHashSet<String> products = new LinkedHashSet<String>();
605         products.add((String) table.get("array_name"));
606         products.add("MODIS_SWATH_Type_L1B/Data_Fields/EV_500_RefSB");
607         products.add("MODIS_SWATH_Type_L1B/Data_Fields/EV_250_Aggr500_RefSB");
608         products.add((String) table.get("lon_array_name"));
609         products.add((String) table.get("lat_array_name"));
610         if (doAggregation) {
611                 try {
612                         reader = new GranuleAggregation(ncdfal, products, "20*nscans", "10*nscans", "2*Max_EV_frames");
613                 } catch (Exception e) {
614                         throw new VisADException("Unable to initialize aggregation reader", e);
615                 }
616         }
617
618         SwathAdapter swathAdapter0 = new SwathAdapter(reader, table);
619         swathAdapter0.setDefaultStride(20);
620
621         table = SpectrumAdapter.getEmptyMetadataTable();
622         table.put(SpectrumAdapter.array_name, "MODIS_SWATH_Type_L1B/Data_Fields/EV_250_Aggr500_RefSB");
623         table.put(SpectrumAdapter.channelIndex_name, "Band_250M");
624         table.put(SpectrumAdapter.x_dim_name, "2*Max_EV_frames");
625         table.put(SpectrumAdapter.y_dim_name, "20*nscans");
626         table.put(SpectrumAdapter.channelValues, new float[]
627            {.650f,.855f});
628         table.put(SpectrumAdapter.bandNames, new String[]
629            {"1","2"});
630         table.put(SpectrumAdapter.channelType, "wavelength");
631         SpectrumAdapter spectrumAdapter0 = new SpectrumAdapter(reader, table);
632
633         MultiSpectralData multiSpectData0 = new MultiSpectralData(swathAdapter0, spectrumAdapter0, "Reflectance", "Reflectance", "MODIS", "Aqua");
634
635         table = SwathAdapter.getEmptyMetadataTable();
636         table.put("array_name", "MODIS_SWATH_Type_L1B/Data_Fields/EV_500_RefSB");
637         table.put("lon_array_name", "MODIS_SWATH_Type_L1B/Geolocation_Fields/Longitude");
638         table.put("lat_array_name", "MODIS_SWATH_Type_L1B/Geolocation_Fields/Latitude");
639         table.put("XTrack", "2*Max_EV_frames");
640         table.put("Track", "20*nscans");
641         table.put("geo_Track", "10*nscans");
642         table.put("geo_XTrack", "Max_EV_frames");
643         table.put("scale_name", "reflectance_scales");
644         table.put("offset_name", "reflectance_offsets");
645         table.put("fill_value_name", "_FillValue");
646         table.put("range_name", "Reflective_Bands");
647         table.put(SpectrumAdapter.channelIndex_name, "Band_500M");
648         table.put(SwathAdapter.geo_track_offset_name, Double.toString(0.0));
649         table.put(SwathAdapter.geo_xtrack_offset_name, Double.toString(0.0));
650         table.put(SwathAdapter.geo_track_skip_name, Double.toString(2.0));
651         table.put(SwathAdapter.geo_xtrack_skip_name, Double.toString(2.0));
652         table.put(SwathAdapter.multiScaleDimensionIndex, Integer.toString(0));
653
654         SwathAdapter swathAdapter1 = new SwathAdapter(reader, table);
655         swathAdapter1.setDefaultStride(20);
656
657         table = SpectrumAdapter.getEmptyMetadataTable();
658         table.put(SpectrumAdapter.array_name, "MODIS_SWATH_Type_L1B/Data_Fields/EV_500_RefSB");
659         table.put(SpectrumAdapter.channelIndex_name, "Band_500M");
660         table.put(SpectrumAdapter.x_dim_name, "2*Max_EV_frames");
661         table.put(SpectrumAdapter.y_dim_name, "20*nscans");
662         table.put(SpectrumAdapter.channelValues, new float[]
663            {.470f,.555f,1.240f,1.638f,2.130f});
664         table.put(SpectrumAdapter.bandNames, new String[]
665            {"3","4","5","6","7"});
666         table.put(SpectrumAdapter.channelType, "wavelength");
667         SpectrumAdapter spectrumAdapter1 = new SpectrumAdapter(reader, table);
668
669         MultiSpectralData multiSpectData1 = new MultiSpectralData(swathAdapter1, spectrumAdapter1, "Reflectance", "Reflectance", "MODIS", "Aqua");
670
671         MultiSpectralAggr aggr = 
672            new MultiSpectralAggr(new MultiSpectralData[] {multiSpectData0, multiSpectData1});
673         aggr.setInitialWavenumber(0.650f);
674         aggr.setDataRange(new float[] {0f, 0.8f});
675         multiSpectData_s.add(aggr);
676         multiSpectData = aggr;
677         defaultSubset = aggr.getDefaultSubset();
678         previewImage = aggr.getImage(defaultSubset);
679
680         DataCategory.createCategory("MultiSpectral");
681         categories = DataCategory.parseCategories("MultiSpectral;MultiSpectral;IMAGE");
682         hasImagePreview = true;
683         hasChannelSelect = true;
684
685         multiSpectData_s.add(null);
686       }
687       else if (name.startsWith("NSS.HRPT") && name.endsWith("level2.hdf")) {
688         Map<String, Object> swthTable = SwathAdapter.getEmptyMetadataTable();
689         swthTable.put("array_name", "temp_3_75um_nom");
690         swthTable.put("lon_array_name", "longitude");
691         swthTable.put("lat_array_name", "latitude");
692         swthTable.put("XTrack", "pixel_elements_along_scan_direction");
693         swthTable.put("Track", "scan_lines_along_track_direction");
694         swthTable.put("geo_Track", "scan_lines_along_track_direction");
695         swthTable.put("geo_XTrack", "pixel_elements_along_scan_direction");
696         swthTable.put("scale_name", "SCALE_FACTOR");
697         swthTable.put("offset_name", "ADD_OFFSET");
698         swthTable.put("fill_value_name", "_FILLVALUE");
699         swthTable.put("range_name", "Emmissive_Bands");
700         swthTable.put("unpack", "unpack");
701         swthTable.put("geo_scale_name", "SCALE_FACTOR");
702         swthTable.put("geo_offset_name", "ADD_OFFSET");
703         swthTable.put("geo_fillValue_name", "_FILLVALUE");
704
705
706         SwathAdapter swathAdapter0 = new SwathAdapter(reader, swthTable);
707         swathAdapter0.setDefaultStride(10);
708         Map<String, double[]> subset = swathAdapter0.getDefaultSubset();
709         defaultSubset = subset;
710
711         Map<String, Object> specTable = SpectrumAdapter.getEmptyMetadataTable();
712         specTable.put(SpectrumAdapter.array_name, "temp_3_75um_nom");
713         specTable.put(SpectrumAdapter.x_dim_name, "pixel_elements_along_scan_direction");
714         specTable.put(SpectrumAdapter.y_dim_name, "scan_lines_along_track_direction");
715         specTable.put(SpectrumAdapter.channelValues, new float[] {3.740f});
716         specTable.put(SpectrumAdapter.bandNames, new String[] {"ch3b"});
717         specTable.put(SpectrumAdapter.channelType, "wavelength");
718         SpectrumAdapter spectrumAdapter0 = new SpectrumAdapter(reader, specTable);
719
720         MultiSpectralData multiSpectData0 = new MultiSpectralData(swathAdapter0, spectrumAdapter0, "BrightnessTemp", "BrightnessTemp", null, null);
721
722         Map<String, Object> table = SwathAdapter.getEmptyMetadataTable();
723         table.put("array_name", "temp_11_0um_nom");
724         table.put("lon_array_name", "longitude");
725         table.put("lat_array_name", "latitude");
726         table.put("XTrack", "pixel_elements_along_scan_direction");
727         table.put("Track", "scan_lines_along_track_direction");
728         table.put("geo_Track", "scan_lines_along_track_direction");
729         table.put("geo_XTrack", "pixel_elements_along_scan_direction");
730         table.put("scale_name", "SCALE_FACTOR");
731         table.put("offset_name", "ADD_OFFSET");
732         table.put("fill_value_name", "_FILLVALUE");
733         table.put("range_name", "Emmissive_Bands");
734         table.put("unpack", "unpack");
735         table.put("geo_scale_name", "SCALE_FACTOR");
736         table.put("geo_offset_name", "ADD_OFFSET");
737         table.put("geo_fillValue_name", "_FILLVALUE");
738
739
740         SwathAdapter swathAdapter1 = new SwathAdapter(reader, table);
741         swathAdapter1.setDefaultStride(10);
742
743         table = SpectrumAdapter.getEmptyMetadataTable();
744         table.put(SpectrumAdapter.array_name, "temp_11_0um_nom");
745         table.put(SpectrumAdapter.x_dim_name, "pixel_elements_along_scan_direction");
746         table.put(SpectrumAdapter.y_dim_name, "scan_lines_along_track_direction");
747         table.put(SpectrumAdapter.channelValues, new float[] {10.80f});
748         table.put(SpectrumAdapter.bandNames, new String[] {"ch4"});
749         table.put(SpectrumAdapter.channelType, "wavelength");
750         SpectrumAdapter spectrumAdapter1 = new SpectrumAdapter(reader, table);
751
752         MultiSpectralData multiSpectData1 = new MultiSpectralData(swathAdapter1, spectrumAdapter1, "BrightnessTemp", "BrightnessTemp", null, null);
753
754         table = SwathAdapter.getEmptyMetadataTable();
755         table.put("array_name", "temp_12_0um_nom");
756         table.put("lon_array_name", "longitude");
757         table.put("lat_array_name", "latitude");
758         table.put("XTrack", "pixel_elements_along_scan_direction");
759         table.put("Track", "scan_lines_along_track_direction");
760         table.put("geo_Track", "scan_lines_along_track_direction");
761         table.put("geo_XTrack", "pixel_elements_along_scan_direction");
762         table.put("scale_name", "SCALE_FACTOR");
763         table.put("offset_name", "ADD_OFFSET");
764         table.put("fill_value_name", "_FILLVALUE");
765         table.put("range_name", "Emmissive_Bands");
766         table.put("unpack", "unpack");
767         table.put("geo_scale_name", "SCALE_FACTOR");
768         table.put("geo_offset_name", "ADD_OFFSET");
769         table.put("geo_fillValue_name", "_FILLVALUE");
770
771
772         SwathAdapter swathAdapter2 = new SwathAdapter(reader, table);
773         swathAdapter2.setDefaultStride(10);
774
775         table = SpectrumAdapter.getEmptyMetadataTable();
776         table.put(SpectrumAdapter.array_name, "temp_12_0um_nom");
777         table.put(SpectrumAdapter.x_dim_name, "pixel_elements_along_scan_direction");
778         table.put(SpectrumAdapter.y_dim_name, "scan_lines_along_track_direction");
779         table.put(SpectrumAdapter.channelValues, new float[] {12.00f});
780         table.put(SpectrumAdapter.bandNames, new String[] {"ch5"});
781         table.put(SpectrumAdapter.channelType, "wavelength");
782         SpectrumAdapter spectrumAdapter2 = new SpectrumAdapter(reader, table);
783
784         MultiSpectralData multiSpectData2 = new MultiSpectralData(swathAdapter2, spectrumAdapter2, "BrightnessTemp", "BrightnessTemp", null, null);
785
786
787         MultiSpectralAggr aggr = new MultiSpectralAggr(new MultiSpectralData[] {multiSpectData0, multiSpectData1, multiSpectData2});
788         aggr.setInitialWavenumber(3.740f);
789         aggr.setDataRange(new float[] {180f, 340f});
790         multiSpectData = aggr;
791         multiSpectData_s.add(aggr);
792         defaultSubset = aggr.getDefaultSubset();
793         previewImage = aggr.getImage(defaultSubset);
794
795         //- now do the reflective bands
796         swthTable.put("array_name", "refl_0_65um_nom");
797         swthTable.put("range_name", "Reflective_Bands");
798
799         swathAdapter0 = new SwathAdapter(reader, swthTable);
800         swathAdapter0.setDefaultStride(10);
801
802         specTable.put(SpectrumAdapter.array_name, "refl_0_65um_nom");
803         specTable.put(SpectrumAdapter.channelValues, new float[] {0.630f});
804         specTable.put(SpectrumAdapter.bandNames, new String[] {"ch1"});
805         spectrumAdapter0 = new SpectrumAdapter(reader, specTable);
806
807         multiSpectData0 = new MultiSpectralData(swathAdapter0, spectrumAdapter0, "Reflectance", "Reflectance", null, null);
808
809         swthTable.put("array_name", "refl_0_86um_nom");
810         swthTable.put("range_name", "Reflective_Bands");
811         
812         swathAdapter1 = new SwathAdapter(reader, swthTable);
813         swathAdapter1.setDefaultStride(10);
814         
815         specTable.put(SpectrumAdapter.array_name, "refl_0_86um_nom");
816         specTable.put(SpectrumAdapter.channelValues, new float[] {0.862f});
817         specTable.put(SpectrumAdapter.bandNames, new String[] {"ch2"});
818         spectrumAdapter1 = new SpectrumAdapter(reader, specTable);
819
820         multiSpectData1 = new MultiSpectralData(swathAdapter1, spectrumAdapter1, "Reflectance", "Reflectance", null, null);
821
822         swthTable.put("array_name", "refl_1_60um_nom");
823         swthTable.put("range_name", "Reflective_Bands");
824         
825         swathAdapter2 = new SwathAdapter(reader, swthTable);
826         swathAdapter2.setDefaultStride(10);
827         subset = swathAdapter2.getDefaultSubset();
828         defaultSubset = subset;
829         
830         specTable.put(SpectrumAdapter.array_name, "refl_1_60um_nom");
831         specTable.put(SpectrumAdapter.channelValues, new float[] {1.610f});
832         specTable.put(SpectrumAdapter.bandNames, new String[] {"ch3ab"});
833         spectrumAdapter2 = new SpectrumAdapter(reader, specTable);
834
835         multiSpectData2 = new MultiSpectralData(swathAdapter2, spectrumAdapter2, "Reflectance", "Reflectance", null, null);
836
837         aggr = new MultiSpectralAggr(new MultiSpectralData[] {multiSpectData0, multiSpectData1, multiSpectData2});
838         aggr.setInitialWavenumber(0.630f);
839         aggr.setDataRange(new float[] {0f, 100f});
840         multiSpectData_s.add(aggr);
841
842         categories = DataCategory.parseCategories("MultiSpectral;MultiSpectral;IMAGE");
843
844         hasImagePreview = true;
845         hasChannelSelect = true;
846       }
847       else {
848          Map<String, Object> table = SwathAdapter.getEmptyMetadataTable();
849          table.put("array_name", "MODIS_SWATH_Type_L1B/Data_Fields/EV_1KM_Emissive");
850          table.put("lon_array_name", "pixel_longitude");
851          table.put("lat_array_name", "pixel_latitude");
852          table.put("XTrack", "elements");
853          table.put("Track", "lines");
854          table.put("geo_Track", "lines");
855          table.put("geo_XTrack", "elements");
856          table.put("scale_name", "scale_factor");
857          table.put("offset_name", "add_offset");
858          table.put("fill_value_name", "_FillValue");
859          swathAdapter = new SwathAdapter(reader, table);
860          categories = DataCategory.parseCategories("2D grid;GRID-2D;");
861          defaultSubset = swathAdapter.getDefaultSubset();
862       }
863       setProperties(properties);
864    }
865
866    public void initAfterUnpersistence() {
867      try {
868        setup();
869      } 
870      catch (Exception e) {
871      }
872    }
873
874    /**
875     * Make and insert the {@link DataChoice DataChoices} for this {@code DataSource}.
876     */
877    public void doMakeDataChoices() {
878        try {
879          for (int k=0; k<multiSpectData_s.size(); k++) {
880            MultiSpectralData adapter = multiSpectData_s.get(k);
881            DataChoice choice = doMakeDataChoice(k, adapter);
882            adapterMap.put(choice.getName(), adapter);
883            addDataChoice(choice);
884          }
885        } catch(Exception e) {
886          logger.error("Problem making data choices", e);
887        }
888    }
889
890    public void addChoice(String name, Data data) {
891        ComboDataChoice combo = new ComboDataChoice(name + hashCode(), name, new Hashtable(), data);
892        addDataChoice(combo);
893        getDataContext().dataSourceChanged(this);
894    }
895
896    private DataChoice doMakeDataChoice(int idx, MultiSpectralData adapter) throws Exception {
897        String name = "_    ";
898        DataSelection dataSel = new MultiDimensionSubset();
899        if (adapter != null) {
900          name = adapter.getName();
901          //dataSel = new MultiDimensionSubset(defaultSubset);
902          dataSel = new MultiDimensionSubset(adapter.getDefaultSubset());
903        }
904
905        Hashtable subset = new Hashtable();
906        subset.put(MultiDimensionSubset.key, dataSel);
907        if (adapter != null) {
908          subset.put(MultiSpectralDataSource.paramKey, adapter.getParameter());
909        }
910
911        DirectDataChoice ddc = new DirectDataChoice(this, new Integer(idx), name, name, categories, subset);
912        ddc.setProperties(subset);
913        return ddc;
914    }
915
916    /**
917     * Check to see if this {@code HDFHydraDataSource} is equal to the object
918     * in question.
919     * @param o  object in question
920     * @return true if they are the same or equivalent objects
921     */
922    public boolean equals(Object o) {
923        if ( !(o instanceof MultiSpectralDataSource)) {
924            return false;
925        }
926        return (this == (MultiSpectralDataSource) o);
927    }
928
929    public MultiSpectralData getMultiSpectralData() {
930      return multiSpectData;
931    }
932
933    public MultiSpectralData getMultiSpectralData(DataChoice choice) {
934      return adapterMap.get(choice.getName());
935    }
936
937    public MultiSpectralData getMultiSpectralData(String name) {
938      return adapterMap.get(name);
939    }
940
941    public MultiSpectralData getMultiSpectralData(int idx) {
942      return multiSpectData_s.get(idx);
943    }
944
945    public String getDatasetName() {
946      return filename;
947    }
948
949    public void setDatasetName(String name) {
950      filename = name;
951    }
952
953    public ComboDataChoice getComboDataChoice() {
954      return comboChoice;
955    }
956
957    /**
958     * Called by the IDV's persistence manager in an effort to collect all of
959     * the files that should be included in a zipped bundle.
960     * 
961     * @return Singleton list containing the file that this data source came from.
962     */
963    @Override public List getDataPaths() {
964        return Collections.singletonList(filename);
965    }
966
967  /**
968    public HashMap getSubsetFromLonLatRect(MultiDimensionSubset select, GeoSelection geoSelection) {
969      GeoLocationInfo ginfo = geoSelection.getBoundingBox();
970      return adapters[0].getSubsetFromLonLatRect(select.getSubset(), ginfo.getMinLat(), ginfo.getMaxLat(),
971                                        ginfo.getMinLon(), ginfo.getMaxLon());
972    }
973   */
974
975    public synchronized Data getData(String name, Map<String, double[]> subset) throws VisADException, RemoteException {
976      MultiSpectralData msd =  getMultiSpectralData(name);
977      Data data = null;
978      try {
979        data = msd.getImage(subset);
980      } catch (Exception e) {
981        logger.error("Problem getting data", e);
982      }
983      return data;
984    }
985
986
987    public synchronized Data getData(DataChoice dataChoice, DataCategory category,
988                                DataSelection dataSelection, Hashtable requestProperties)
989                                throws VisADException, RemoteException {
990       return this.getDataInner(dataChoice, category, dataSelection, requestProperties);
991
992    }
993
994    protected Data getDataInner(DataChoice dataChoice, DataCategory category,
995                                DataSelection dataSelection, Hashtable requestProperties)
996                                throws VisADException, RemoteException {
997
998        //- this hack keeps the HydraImageProbe from doing a getData()
999        //- TODO: need to use categories?
1000        if (requestProperties != null) {
1001          if ((requestProperties.toString()).contains("ReadoutProbe")) {
1002            return null;
1003          }
1004        }
1005
1006        GeoLocationInfo ginfo = null;
1007        GeoSelection geoSelection = null;
1008        
1009        if ((dataSelection != null) && (dataSelection.getGeoSelection() != null)) {
1010          if (dataSelection.getGeoSelection().getBoundingBox() != null) {
1011            geoSelection = dataSelection.getGeoSelection();
1012          }
1013          else if (dataChoice.getDataSelection() != null) {
1014            geoSelection = dataChoice.getDataSelection().getGeoSelection();
1015          }
1016        }
1017
1018        if (geoSelection != null) {
1019          ginfo = geoSelection.getBoundingBox();
1020        }
1021
1022        Data data = null;
1023
1024        try {
1025            Map<String, double[]> subset = null;
1026            if (ginfo != null) {
1027              subset = swathAdapter.getSubsetFromLonLatRect(ginfo.getMinLat(), ginfo.getMaxLat(),
1028                                        ginfo.getMinLon(), ginfo.getMaxLon());
1029            }
1030            else {
1031              MultiDimensionSubset select = null;
1032              Hashtable table = dataChoice.getProperties();
1033              Enumeration keys = table.keys();
1034              while (keys.hasMoreElements()) {
1035                Object key = keys.nextElement();
1036                if (key instanceof MultiDimensionSubset) {
1037                  select = (MultiDimensionSubset) table.get(key);
1038                }
1039              }  
1040              if (select != null) {
1041                subset = select.getSubset();
1042              }
1043
1044              if (dataSelection != null) {
1045                  Hashtable props = dataSelection.getProperties();
1046                  if (props != null) {
1047                    if (props.containsKey(MultiDimensionSubset.key)) {
1048                      subset = (HashMap)((MultiDimensionSubset)props.get(MultiDimensionSubset.key)).getSubset();
1049                    }
1050                    else {
1051                      subset = defaultSubset;
1052                    }
1053                    if (props.containsKey(SpectrumAdapter.channelIndex_name)) {
1054                      int idx = ((Integer) props.get(SpectrumAdapter.channelIndex_name)).intValue();
1055                      double[] coords = subset.get(SpectrumAdapter.channelIndex_name);
1056                      if (coords == null) {
1057                        coords = new double[] {(double)idx, (double)idx, (double)1};
1058                        subset.put(SpectrumAdapter.channelIndex_name, coords);
1059                      }
1060                      else {
1061                        coords[0] = (double)idx;
1062                        coords[1] = (double)idx;
1063                        coords[2] = (double)1;
1064                      }
1065                   }
1066                 }
1067               }
1068            }
1069
1070            if (subset != null) {
1071              MultiSpectralData multiSpectData = getMultiSpectralData(dataChoice);
1072              if (multiSpectData != null) {
1073                data = multiSpectData.getImage(subset);
1074                data = applyProperties(data, requestProperties, subset);
1075              }
1076            }
1077        } catch (Exception e) {
1078            logger.error("Problem getting data", e);
1079        }
1080        return data;
1081    }
1082
1083    public MapProjection getDataProjection(Map<String, double[]> subset) {
1084      MapProjection mp = null;
1085      try {
1086        Rectangle2D rect =  multiSpectData.getLonLatBoundingBox(subset);
1087        mp = new LambertAEA(rect);
1088      }
1089      catch (Exception e) {
1090        logException("MultiSpectralDataSource.getDataProjection", e);
1091      }
1092      return mp;
1093    }
1094
1095    protected Data applyProperties(Data data, Hashtable requestProperties, Map<String, double[]> subset)
1096          throws VisADException, RemoteException {
1097      Data new_data = data;
1098
1099      if (requestProperties == null) {
1100        new_data = data;
1101        return new_data;
1102      }
1103      return new_data;
1104    }
1105
1106    protected void initDataSelectionComponents(
1107         List<DataSelectionComponent> components,
1108             final DataChoice dataChoice) {
1109
1110        // TJJ Nov 2014 - Why did we need an OS-specific check here? Seems this should be the choice 
1111        // each time and in fact Linux no longer works if we drop through, so commenting out [1805]
1112      // if (System.getProperty("os.name").equals("Mac OS X") && hasImagePreview && hasChannelSelect) {
1113      if (hasImagePreview && hasChannelSelect) {
1114        try {
1115          components.add(new ImageChannelSelection(new PreviewSelection(dataChoice, previewImage, null), new ChannelSelection(dataChoice)));
1116        } catch (Exception e) {
1117          logger.error("Problem creating ImageChannelSelection", e);
1118        }
1119      }
1120      else {
1121        if (hasImagePreview) {
1122          try {
1123            previewSelection = new PreviewSelection(dataChoice, previewImage, null);
1124            components.add(previewSelection);
1125          } catch (Exception e) {
1126            logger.error("Can't make PreviewSelection", e);
1127          }
1128        }
1129        if (hasChannelSelect) {
1130          try {
1131            components.add(new ChannelSelection(dataChoice));
1132          } catch (Exception e) {
1133            logger.error("Problem creating ChannelSelection", e);
1134          }
1135        }
1136      }
1137    }
1138
1139
1140  public static MapProjection getDataProjection(FlatField fltField) throws Exception {
1141    Rectangle2D rect = MultiSpectralData.getLonLatBoundingBox(fltField);
1142    MapProjection mp = new LambertAEA(rect, false);
1143    return mp;
1144  }
1145
1146   public static MapProjection getSwathProjection(FlatField image, float[][] corners) throws VisADException, RemoteException {
1147      MapProjection mp = null;
1148      FunctionType fnc_type = (FunctionType) image.getType();
1149      RealTupleType rtt = fnc_type.getDomain();
1150      CoordinateSystem cs = rtt.getCoordinateSystem();
1151      Gridded2DSet domainSet = (Gridded2DSet) image.getDomainSet();
1152
1153      if (cs instanceof visad.CachingCoordinateSystem) {
1154         cs = ((visad.CachingCoordinateSystem)cs).getCachedCoordinateSystem();
1155      }
1156
1157      if (cs instanceof LongitudeLatitudeCoordinateSystem) {
1158         try {
1159           mp = new LambertAEA(corners);
1160         } catch (Exception e) {
1161           logger.error("Problem creating LambertAEA", e);
1162         }
1163         return mp;
1164      }
1165      else {
1166         return null;
1167      }
1168   }
1169
1170
1171  public static Linear2DSet makeGrid(MapProjection mp, double res) throws Exception {
1172    Rectangle2D rect = mp.getDefaultMapArea();
1173
1174    int xLen = (int) (rect.getWidth()/res);
1175    int yLen = (int) (rect.getHeight()/res);
1176
1177    RealType xmap = RealType.getRealType("xmap", CommonUnit.meter);
1178    RealType ymap = RealType.getRealType("ymap", CommonUnit.meter);
1179
1180    RealTupleType rtt = new visad.RealTupleType(xmap, ymap, mp, null);
1181
1182    Linear2DSet grid = new Linear2DSet(rtt, rect.getX(), (xLen-1)*res, xLen,
1183                                            rect.getY(), (yLen-1)*res, yLen);
1184    return grid;
1185  }
1186
1187  public static Linear2DSet makeGrid(MapProjection mp, float[][] corners, float res) throws Exception {
1188     float[][] xy = mp.fromReference(corners);
1189
1190     float min_x = Float.MAX_VALUE;
1191     float min_y = Float.MAX_VALUE;
1192     float max_x = -Float.MAX_VALUE;
1193     float max_y = -Float.MAX_VALUE;
1194
1195     for (int k=0; k<xy[0].length;k++) {
1196       if (xy[0][k] < min_x) min_x = xy[0][k];
1197       if (xy[1][k] < min_y) min_y = xy[1][k];
1198       if (xy[0][k] > max_x) max_x = xy[0][k];
1199       if (xy[1][k] > max_y) max_y = xy[1][k];
1200     }
1201
1202     RealType xmap = RealType.getRealType("xmap", CommonUnit.meter);
1203     RealType ymap = RealType.getRealType("ymap", CommonUnit.meter);
1204
1205     RealTupleType rtt = new visad.RealTupleType(xmap, ymap, mp, null);
1206
1207     min_x = ((int) (min_x/res)) * res;
1208     max_x = ((int) (max_x/res)) * res;
1209     min_y = ((int) (min_y/res)) * res;
1210     max_y = ((int) (max_y/res)) * res;
1211
1212     float del_x = max_x - min_x;
1213     float del_y = max_y - min_y;
1214
1215     int xLen = (int) (del_x/res);
1216     int yLen = (int) (del_y/res);
1217
1218     Linear2DSet grid = new Linear2DSet(rtt, min_x, min_x + (xLen-1)*res, xLen,
1219                                             min_y, min_y + (yLen-1)*res, yLen);
1220
1221     return grid;
1222  }
1223
1224
1225  private static int count = 0;
1226
1227  /**
1228   * Resolution of grid should match the loweset of swaths.
1229   * @param  grid  the new domain (target of reproject).
1230   * @param  swaths  one or more swaths to be reprojected.
1231   * @param  mode  See ReprojectSwath for details.
1232   */
1233  public static FlatField swathToGrid(Linear2DSet grid, FlatField[] swaths, double mode) throws Exception {
1234     RealTupleType newRangeType = new RealTupleType(new RealType[] 
1235           {RealType.getRealType("redimage_"+count), RealType.getRealType("greenimage_"+count), RealType.getRealType("blueimage_"+count)});
1236     return swathToGrid(grid, swaths, newRangeType, mode);
1237  }
1238
1239  /**
1240   * Resolution of grid should match the loweset of swaths.
1241   * @param  grid  the new domain (target of reproject).
1242   * @param  swaths  one or more swaths to be reprojected.
1243   * @param  newRangeType user must supply.
1244   * @param  mode  See ReprojectSwath for details.
1245   */
1246  public static FlatField swathToGrid(Linear2DSet grid, FlatField[] swaths, RealTupleType newRangeType, double mode) throws Exception {
1247     int nSwths = swaths.length;
1248     boolean equalDom = true;
1249     for (int k=1; k<nSwths; k++) {
1250        if (!(swaths[0].getDomainSet().equals(swaths[k].getDomainSet()))) {
1251           equalDom = false;
1252           break;
1253        }
1254     }
1255     int tupDim = newRangeType.getNumberOfRealComponents();
1256     float[][] newRangeVals = new float[tupDim][];
1257     if (equalDom) {
1258         FunctionType ftype = (FunctionType) swaths[0].getType();
1259         Gridded2DSet domSet = (Gridded2DSet) swaths[0].getDomainSet();
1260         FlatField swath = new FlatField(new FunctionType(ftype.getDomain(), newRangeType), domSet);
1261         int cnt = 0;
1262         for (int k=0; k<nSwths; k++) {
1263            float[][] rngVals = swaths[k].getFloats(false);
1264            for (int t=0; t<rngVals.length; t++) {
1265                newRangeVals[cnt] = rngVals[t];
1266                cnt++;
1267            }
1268         }
1269         swath.setSamples(newRangeVals, false);
1270         
1271         return swathToGrid(grid, swath, mode);
1272     }
1273     else {
1274         FlatField newGrid = new FlatField(new FunctionType(grid.getType(), newRangeType), grid);
1275         FlatField[] fltFlds = new FlatField[nSwths];
1276         for (int k=0; k<nSwths; k++) {
1277             fltFlds[k] = swathToGrid(grid, swaths[k], mode);
1278         }
1279         int cnt = 0;
1280         for (int k=0; k<nSwths; k++) {
1281            float[][] rngVals = fltFlds[k].getFloats(false);
1282            for (int t=0; t<rngVals.length; t++) {
1283                newRangeVals[cnt] = rngVals[t];
1284                cnt++;
1285            }
1286         }
1287         newGrid.setSamples(newRangeVals, false);
1288         
1289         return newGrid;
1290     }
1291  }
1292
1293  /**
1294   * Grid and swath must both have a CoordinateSystem with an Earth Reference.
1295   * @param  grid  the new domain (target of reproject).
1296   * @param  swath swath to be reprojected.
1297   * @param  mode  See ReprojectSwath for details.
1298   */
1299
1300  public static FlatField swathToGrid(Linear2DSet grid, FlatField swath, double mode) throws Exception {
1301     return ReprojectSwath.swathToGrid(grid, swath, (int)mode);
1302  }
1303
1304  /* keep in here for now.
1305  public static FlatField swathToGrid(Linear2DSet grid, FlatField swath, double mode) throws Exception {
1306    FunctionType ftype = (FunctionType) swath.getType();
1307    Linear2DSet swathDomain = (Linear2DSet) swath.getDomainSet();
1308    int[] lens = swathDomain.getLengths();
1309    float[][] swathRange = swath.getFloats(false);
1310    int trackLen = lens[1];
1311    int xtrackLen = lens[0];
1312    int gridLen = grid.getLength();
1313    lens = grid.getLengths();
1314    int gridXLen = lens[0];
1315    int gridYLen = lens[1];
1316
1317    CoordinateSystem swathCoordSys = swathDomain.getCoordinateSystem();
1318    CoordinateSystem gridCoordSys = grid.getCoordinateSystem();
1319
1320    RealTupleType rtt = ((SetType)grid.getType()).getDomain();
1321    FlatField grdFF = new FlatField(new FunctionType(rtt, ftype.getRange()), grid);
1322    float[][] gridRange = grdFF.getFloats(false);
1323    int rngTupDim = gridRange.length;
1324
1325    float[][] swathGridCoord = new float[2][gridLen];
1326    byte[] numSwathPoints = new byte[gridLen];
1327
1328    int[] swathIndexAtGrid = null;
1329    if (true) {
1330      swathIndexAtGrid = new int[gridLen];
1331    }
1332
1333    float[][] grdCrd = new float[2][1];
1334    float[][] firstGridRange = new float[rngTupDim][gridLen];
1335    float[] sumRange = new float[rngTupDim];
1336    for (int t=0; t<rngTupDim; t++) {
1337       java.util.Arrays.fill(firstGridRange[t], Float.NaN);
1338       java.util.Arrays.fill(gridRange[t], Float.NaN);
1339    }
1340
1341    float g0_last = Float.NaN;
1342    float g1_last = Float.NaN;
1343    float xt_dist = Float.NaN;
1344
1345    for (int j=0; j < trackLen; j++) {
1346       for (int i=0; i < xtrackLen; i++) {
1347         int swathIdx = j*xtrackLen + i;
1348         float val = swathRange[0][swathIdx];
1349
1350         float[][] swathCoord = swathDomain.indexToValue(new int[] {swathIdx});
1351         float[][] swathEarthCoord = swathCoordSys.toReference(swathCoord);
1352
1353         float[][] gridValue = gridCoordSys.fromReference(swathEarthCoord);
1354         float[][] gridCoord = grid.valueToGrid(gridValue);
1355         float g0 = gridCoord[0][0];
1356         float g1 = gridCoord[1][0];
1357         int grdIdx  = (g0 != g0 || g1 != g1) ? -1 : ((int) (g0 + 0.5)) + gridXLen * ((int) (g1 + 0.5));
1358
1359
1360         // tooclose logic
1361         if (i >= 1) {
1362           float diff_0 = g0 - g0_last;
1363           float diff_1 = g1 - g1_last;
1364           xt_dist = (diff_0*diff_0) + (diff_1*diff_1);
1365         }
1366         g0_last = g0;
1367         g1_last = g1;
1368
1369         boolean tooclose = false;
1370         float closest = Float.MAX_VALUE;
1371         for (int n = -2; n < 3; n++) {
1372            for (int m = -2; m < 3; m++) {
1373               int k = grdIdx + (m + n*gridXLen);
1374               if (k >=0 && k < gridXLen*gridYLen) {
1375               if ( !Float.isNaN(swathGridCoord[0][k]) ) {
1376                  float del_0 = g0 - swathGridCoord[0][k];
1377                  float del_1 = g1 - swathGridCoord[1][k];
1378                  float dst = del_0*del_0 + del_1*del_1;
1379                  if (dst < closest) closest = dst;
1380               }
1381               }
1382            }
1383         }
1384         if (Math.sqrt((double)closest) < 0.86*Math.sqrt((double)xt_dist)) tooclose = true;
1385
1386
1387         int m=0;
1388         int n=0;
1389         int k = grdIdx + (m + n*gridXLen);
1390
1391         if ( !(Float.isNaN(val)) && ((k >=0) && (k < gridXLen*gridYLen)) && !tooclose) { // val or val[rngTupDim] ?
1392            float grdVal = firstGridRange[0][k];
1393
1394            if (Float.isNaN(grdVal)) {
1395               for (int t=0; t<rngTupDim; t++) {
1396                  firstGridRange[t][k] = swathRange[t][swathIdx];
1397               }
1398               swathGridCoord[0][k] = g0;
1399               swathGridCoord[1][k] = g1;
1400               swathIndexAtGrid[k] = swathIdx;
1401            }
1402         }
1403
1404       }
1405    }
1406
1407    float[][] gCoord = new float[2][1];
1408    if (mode > 0.0) {  // 2nd pass weighted average
1409    float sigma = 0.6f;
1410    float weight = 1f;
1411    float[] sumValue = new float[rngTupDim];
1412
1413    float[][] dst_sqrd = new float[5][5];
1414    for (int n=-2; n<3; n++) {
1415      for (int m=-2; m<3; m++) {
1416          float del_0 = m;
1417          float del_1 = n;
1418          dst_sqrd[n+2][m+2] = (float) Math.sqrt((double)(del_0*del_0 + del_1*del_1));
1419      }
1420    }
1421   
1422
1423    for (int j=2; j<gridYLen-2; j++) {
1424       for (int i=2; i<gridXLen-2; i++) {
1425         int grdIdx = i + j*gridXLen;
1426
1427         // don't do weighted average if a nearest neigbhor existed for the grid point
1428         if (mode == 2.0) {
1429           if (!Float.isNaN(firstGridRange[0][grdIdx])) {
1430              for (int t=0; t<rngTupDim; t++) {
1431                 gridRange[t][grdIdx] = firstGridRange[t][grdIdx];
1432              }
1433              continue;
1434           }
1435         }
1436
1437         int num = 0;
1438         float mag = 1f;
1439         float sumWeights = 0f;
1440         float[] dists = new float[25];
1441         float[][] values = new float[rngTupDim][25];
1442         for (int t=0; t<rngTupDim; t++) {
1443            sumValue[t] = 0f;
1444         }
1445
1446         for (int n = -1; n < 2; n++) {
1447            for (int m = -1; m < 2; m++) {
1448               int k = grdIdx + (m + n*gridXLen);
1449               if ( !Float.isNaN(firstGridRange[0][k]) ) {
1450                  dists[num] = dst_sqrd[n+2][m+2];
1451                  for (int t=0; t<rngTupDim; t++) {
1452                    values[t][num] = firstGridRange[t][k];
1453                  }
1454                  num++;
1455               }
1456            }
1457         }
1458
1459         if (num < 5) {
1460
1461         for (int n = -2; n < 3; n++) {
1462            for (int m = -2; m < 3; m++) {
1463               if ( (n == -2 || n == 2) || ((n <= 1 && n >= -1) && (m==-2 || m==2)) ) { // don't repeat inner box
1464               int k = grdIdx + (m + n*gridXLen);
1465               if ( !Float.isNaN(firstGridRange[0][k]) ) {
1466                  dists[num] = dst_sqrd[n+2][m+2];
1467                  for (int t=0; t<rngTupDim; t++) {
1468                    values[t][num] = firstGridRange[t][k];
1469                  }
1470                  num++;
1471               }
1472              }
1473            }
1474          }
1475
1476          if (num > 14 && num <= 21) sigma = 0.46f;
1477          if (num > 10 && num <= 14) sigma = 0.50f;
1478          if (num > 7 && num <= 10) sigma = 0.58f;
1479          if (num > 4 && num <= 7) sigma = 0.72f;
1480          if (num < 4) sigma = 1.44f;
1481          }
1482          else { // inner box only
1483            sigma = 0.40f;
1484          }
1485
1486          for (int q=0; q<num; q++) {
1487                float dstsqrd = dists[q];
1488                weight = (float) (mag/Math.exp((double)(dstsqrd/(sigma))));
1489                for (int t=0; t<rngTupDim; t++) {
1490                   sumValue[t] += values[t][q]*weight;
1491                }
1492                sumWeights += weight;
1493          }
1494
1495          for (int t=0; t<rngTupDim; t++) {
1496            gridRange[t][grdIdx] = sumValue[t]/sumWeights;
1497          }
1498
1499        }
1500      }
1501      grdFF.setSamples(gridRange);
1502    }
1503    else { // no averaging
1504      grdFF.setSamples(firstGridRange);
1505    }
1506
1507   return grdFF;
1508 }
1509 */
1510
1511 public static boolean validLonLat(float[][] lonlat) {
1512   float lon = lonlat[0][0];
1513   float lat = lonlat[1][0];
1514   return ((lon >= -180f && lon <= 360f) && (lat >= -90f && lat <= 90f));
1515 }
1516
1517}
1518
1519
1520class ChannelSelection extends DataSelectionComponent {
1521  
1522  private static final Logger logger = LoggerFactory.getLogger(ChannelSelection.class);
1523  
1524  DataChoice dataChoice;
1525  MultiSpectralDisplay display;
1526
1527  ChannelSelection(DataChoice dataChoice) throws Exception {
1528     super("Channels");
1529     this.dataChoice = dataChoice;
1530     display = new MultiSpectralDisplay((DirectDataChoice)dataChoice);
1531     display.showChannelSelector();
1532  }
1533
1534  protected JComponent doMakeContents() {
1535    try {
1536      JPanel panel = new JPanel(new BorderLayout());
1537      panel.add("Center", display.getDisplayComponent());
1538      if (display.getBandSelectComboBox() != null) {
1539        JPanel bandPanel = new JPanel(new FlowLayout());
1540        bandPanel.add(new JLabel("Band: "));
1541        bandPanel.add(display.getBandSelectComboBox());
1542        panel.add("South", bandPanel);
1543      }
1544      return panel;
1545    }
1546    catch (Exception e) {
1547      System.out.println(e);
1548    }
1549    return null;
1550  }
1551
1552  public void applyToDataSelection(DataSelection dataSelection) {
1553      try {
1554        dataSelection.putProperty(Constants.PROP_CHAN, display.getWaveNumber());
1555        dataSelection.putProperty(SpectrumAdapter.channelIndex_name, display.getChannelIndex());
1556      } catch (Exception e) {
1557        logger.error("Problem applying changes to dataselection", e);
1558      }
1559  }
1560}
1561
1562class ImageChannelSelection extends DataSelectionComponent {
1563   PreviewSelection previewSelection;
1564   ChannelSelection channelSelection;
1565
1566   ImageChannelSelection(PreviewSelection previewSelection, ChannelSelection channelSelection) {
1567     super("MultiSpectral");
1568     this.previewSelection = previewSelection;
1569     this.channelSelection = channelSelection;
1570   }
1571
1572   protected JComponent doMakeContents() {
1573      JSplitPane splitpane = new JSplitPane(JSplitPane.VERTICAL_SPLIT);
1574      splitpane.add(previewSelection.doMakeContents());
1575      splitpane.add(channelSelection.doMakeContents());
1576      splitpane.setContinuousLayout(true);
1577      splitpane.setOneTouchExpandable(true);
1578      splitpane.setResizeWeight(1);
1579      splitpane.setDividerSize(12);
1580      return splitpane;
1581   }
1582
1583   public void applyToDataSelection(DataSelection dataSelection) {
1584     previewSelection.applyToDataSelection(dataSelection);
1585     channelSelection.applyToDataSelection(dataSelection);
1586   }
1587
1588
1589}