001/*
002 * This file is part of McIDAS-V
003 *
004 * Copyright 2007-2017
005 * Space Science and Engineering Center (SSEC)
006 * University of Wisconsin - Madison
007 * 1225 W. Dayton Street, Madison, WI 53706, USA
008 * https://www.ssec.wisc.edu/mcidas
009 * 
010 * All Rights Reserved
011 * 
012 * McIDAS-V is built on Unidata's IDV and SSEC's VisAD libraries, and
013 * some McIDAS-V source code is based on IDV and VisAD source code.  
014 * 
015 * McIDAS-V is free software; you can redistribute it and/or modify
016 * it under the terms of the GNU Lesser Public License as published by
017 * the Free Software Foundation; either version 3 of the License, or
018 * (at your option) any later version.
019 * 
020 * McIDAS-V is distributed in the hope that it will be useful,
021 * but WITHOUT ANY WARRANTY; without even the implied warranty of
022 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
023 * GNU Lesser Public License for more details.
024 * 
025 * You should have received a copy of the GNU Lesser Public License
026 * along with this program.  If not, see http://www.gnu.org/licenses.
027 */
028
029package edu.wisc.ssec.mcidasv.data.adde;
030
031import java.awt.Component;
032import java.awt.Container;
033import java.awt.Dimension;
034import java.awt.event.ActionEvent;
035import java.awt.event.ActionListener;
036import java.io.File;
037import java.io.RandomAccessFile;
038import java.rmi.RemoteException;
039import java.text.SimpleDateFormat;
040import java.util.ArrayList;
041import java.util.Arrays;
042import java.util.Collections;
043import java.util.Date;
044import java.util.Enumeration;
045import java.util.HashMap;
046import java.util.Hashtable;
047import java.util.Iterator;
048import java.util.List;
049import java.util.Map;
050import java.util.StringTokenizer;
051import java.util.TimeZone;
052import java.util.TreeMap;
053
054import javax.swing.BoxLayout;
055import javax.swing.JCheckBox;
056import javax.swing.JComponent;
057import javax.swing.JLabel;
058import javax.swing.JPanel;
059import javax.swing.JScrollPane;
060import javax.swing.JTabbedPane;
061
062import org.slf4j.Logger;
063import org.slf4j.LoggerFactory;
064
065import edu.wisc.ssec.mcidas.AREAnav;
066import edu.wisc.ssec.mcidas.AreaDirectory;
067import edu.wisc.ssec.mcidas.AreaDirectoryList;
068import edu.wisc.ssec.mcidas.AreaFile;
069import edu.wisc.ssec.mcidas.AreaFileException;
070import edu.wisc.ssec.mcidas.adde.AddeImageURL;
071import edu.wisc.ssec.mcidas.adde.AddeTextReader;
072import edu.wisc.ssec.mcidas.adde.AddeURL;
073import edu.wisc.ssec.mcidasv.data.GeoLatLonSelection;
074import edu.wisc.ssec.mcidasv.data.GeoPreviewSelection;
075
076import visad.CommonUnit;
077import visad.Data;
078import visad.DateTime;
079import visad.FlatField;
080import visad.FunctionType;
081import visad.MathType;
082import visad.RealType;
083import visad.Set;
084import visad.VisADException;
085import visad.data.DataRange;
086import visad.data.mcidas.AREACoordinateSystem;
087import visad.data.mcidas.AreaAdapter;
088import visad.georef.MapProjection;
089import visad.meteorology.ImageSequence;
090import visad.meteorology.ImageSequenceImpl;
091import visad.meteorology.SingleBandedImage;
092
093import ucar.nc2.iosp.mcidas.McIDASAreaProjection;
094import ucar.unidata.data.BadDataException;
095import ucar.unidata.data.CompositeDataChoice;
096import ucar.unidata.data.DataCategory;
097import ucar.unidata.data.DataChoice;
098import ucar.unidata.data.DataSelection;
099import ucar.unidata.data.DataSelectionComponent;
100import ucar.unidata.data.DataSourceDescriptor;
101import ucar.unidata.data.DirectDataChoice;
102import ucar.unidata.data.GeoLocationInfo;
103import ucar.unidata.data.GeoSelection;
104import ucar.unidata.data.imagery.AddeImageDataSource;
105import ucar.unidata.data.imagery.AddeImageDescriptor;
106import ucar.unidata.data.imagery.AddeImageInfo;
107import ucar.unidata.data.imagery.BandInfo;
108import ucar.unidata.data.imagery.ImageDataset;
109import ucar.unidata.geoloc.LatLonPoint;
110import ucar.unidata.geoloc.ProjectionImpl;
111import ucar.unidata.idv.DisplayControl;
112import ucar.unidata.util.GuiUtils;
113import ucar.unidata.util.IOUtil;
114import ucar.unidata.util.LogUtil;
115import ucar.unidata.util.PollingInfo;
116import ucar.unidata.util.StringUtil;
117import ucar.unidata.util.ThreeDSize;
118import ucar.unidata.util.TwoFacedObject;
119import ucar.visad.UtcDate;
120import ucar.visad.Util;
121import ucar.visad.data.AreaImageFlatField;
122
123/**
124 * Abstract DataSource class for images files.
125 */
126
127public class AddeImageParameterDataSource extends AddeImageDataSource {
128
129    private static final Logger logger = LoggerFactory.getLogger(AddeImageParameterDataSource.class);
130
131    /**
132     * Public keys for server, group, dataset, user, project.
133     */
134    public final static String SIZE_KEY = "size";
135    public final static String PLACE_KEY = "place";
136    public final static String LATLON_KEY = "latlon";
137    public final static String LINELE_KEY = "linele";
138    public final static String MAG_KEY = "mag";
139    public final static String BAND_KEY = "band";
140    public final static String BANDINFO_KEY = "bandinfo";
141    public final static String UNIT_KEY = "unit";
142    public final static String PREVIEW_KEY = "preview";
143    public final static String SPAC_KEY = "spac";
144    public final static String NAV_KEY = "nav";
145    public final static String AUX_KEY = "aux";
146    public final static String DOC_KEY = "doc";
147    public final static String SPACING_BRIT = "1";
148    public final static String SPACING_NON_BRIT = "4";
149
150    /** The first projection we find */
151    protected ProjectionImpl sampleProjection;
152    public MapProjection sampleMapProjection;
153
154    /* ADDE request string */
155    private String source;
156    private String baseSource;
157
158    /* properties for this data source */
159    private Hashtable sourceProps;
160    private Hashtable selectionProps;
161
162    private int lineResolution;
163    private int elementResolution;
164    private float lRes;
165    private float eRes;
166    private int lineMag = 1;
167    private int elementMag = 1;
168
169    private GeoSelection lastGeoSelection;
170    private DataChoice lastChoice = null;
171    private Boolean showPreview = Boolean.FALSE;
172    private FlatField previewImage = null;
173    private MapProjection previewProjection;
174    private Hashtable initProps;
175
176    private AreaDirectory previewDir = null;
177    private AREAnav previewNav = null;
178    private boolean haveDataSelectionComponents = false;
179
180    private GeoPreviewSelection previewSel;
181    private GeoLatLonSelection laLoSel;
182
183    private String choiceName;
184
185    private String saveCoordType;
186    private String savePlace;
187    private double saveLat;
188    private double saveLon;
189    private int saveNumLine;
190    private int saveNumEle;
191    private int saveLineMag;
192    private int saveEleMag;
193    private Boolean saveShowPreview;
194
195    private String displaySource;
196
197    protected List<DataChoice> stashedChoices = null;
198    private List iml = new ArrayList();
199    private List saveImageList = new ArrayList();
200
201    private int previewLineRes = 1;
202    private int previewEleRes = 1;
203    
204    // Do binary search on preview bounds for GEO sensors to speed up overall response time
205    boolean doGeoSpeedup = false;
206
207    /** Whether or not this DataSource was loaded from a bundle. */
208    private boolean fromBundle = false;
209    
210    /** Are any of the data choices based upon remote files? */
211    private boolean hasRemoteChoices = false;
212
213    private Map<String, AreaDirectory> requestIdToDirectory = new HashMap<String, AreaDirectory>();
214    
215    public AddeImageParameterDataSource() {} 
216
217    /**
218     * Creates a {@code AddeImageParameterDataSource} with a single ADDE URL.
219     * <b>Note:</b> the URLs should point at {@literal "image"} data.
220     * 
221     * @param descriptor {@link ucar.unidata.data.DataSourceDescriptor DataSourceDescriptor} for this data source.
222     * @param image ADDE URL
223     * @param properties The properties for this data source.
224     * 
225     * @throws VisADException
226     */
227    public AddeImageParameterDataSource(DataSourceDescriptor descriptor, String image,
228                               Hashtable properties)
229            throws VisADException {
230        super(descriptor, new String[] { image }, properties);
231        logger.trace("1: desc={}, image={}, properties={}", new Object[] { descriptor, image, properties });
232    }
233
234    /**
235     * Create a new AddeImageParameterDataSource with an array of ADDE URL strings.
236     * <b>Note:</b> the URLs should point at {@literal "image"} data.
237     * 
238     * @param descriptor {@link ucar.unidata.data.DataSourceDescriptor DataSourceDescriptor} for this data source.
239     * @param images Array of ADDE URLs.
240     * @param properties Properties for this data source.
241     * 
242     * @throws VisADException
243     */
244    public AddeImageParameterDataSource(DataSourceDescriptor descriptor, String[] images,
245                           Hashtable properties) throws VisADException {
246        super(descriptor, images, properties);
247        logger.trace("2: desc={}, images={}, properties={}", new Object[] { descriptor, images, properties });
248    }
249
250    /**
251     * Creates a new {@code AddeImageParameterDataSource} with an 
252     * {@link java.util.List List} of ADDE URL strings.
253     * <b>Note:</b> the URLs should point at {@literal "image"} data.
254     * 
255     * @param descriptor {@link ucar.unidata.data.DataSourceDescriptor DataSourceDescriptor} for this data source.
256     * @param images {@code List} of ADDE URL strings.
257     * @param properties Properties for this data source.
258     * 
259     * @throws VisADException
260     */
261    public AddeImageParameterDataSource(DataSourceDescriptor descriptor, List images,
262                           Hashtable properties) throws VisADException {
263        super(descriptor, images, properties);
264        logger.trace("3: desc={}, images={}, properties={}", new Object[] { descriptor, images, properties });
265    }
266
267    /**
268     * Create a new AddeImageParameterDataSource with the given dataset.
269     * 
270     * @param descriptor {@link ucar.unidata.data.DataSourceDescriptor DataSourceDescriptor} for this data source.
271     * @param ids Dataset.
272     * @param properties Properties for this data source.
273     * 
274     * @throws VisADException
275     */
276    
277    public AddeImageParameterDataSource(DataSourceDescriptor descriptor, ImageDataset ids,
278                           Hashtable properties) throws VisADException {
279        super(descriptor, ids, properties);
280        logger.trace("4: desc={}, ids={}, properties={}", new Object[] { descriptor, ids, properties });
281        this.sourceProps = properties;
282        if (properties.containsKey((Object)PREVIEW_KEY)) {
283            this.showPreview = (Boolean)(properties.get((Object)PREVIEW_KEY));
284            saveShowPreview = showPreview;
285        } else {
286            if (saveShowPreview != null) {
287                showPreview = saveShowPreview;
288            }
289        }
290        
291        List descs = ids.getImageDescriptors();
292        AddeImageDescriptor aid = (AddeImageDescriptor)descs.get(0);
293        this.source = aid.getSource();
294        if (this.source.contains("localhost")) {
295            AreaDirectory areaDirectory = aid.getDirectory();
296            if (!sourceProps.containsKey((Object)UNIT_KEY)) {
297                if (!sourceProps.containsKey((Object)BAND_KEY)) {
298                    String calType = areaDirectory.getCalibrationType();
299                    if (!calType.equals("RAW")) {
300                        sourceProps.put(UNIT_KEY, calType);
301                        int[] bandNums = areaDirectory.getBands();
302                        String bandString = new Integer(bandNums[0]).toString();
303                        sourceProps.put(BAND_KEY, bandString);
304                    }
305                }
306            }
307        }
308        setMag();
309        getAreaDirectory(properties);
310    }
311
312    @Override protected void propertiesChanged() {
313        logger.trace("fired");
314        super.propertiesChanged();
315    }
316
317    @Override protected boolean initDataFromPollingInfo() {
318        boolean result = super.initDataFromPollingInfo();
319        logger.trace("result={}", result);
320        return result;
321    }
322
323    @Override protected boolean isPolling() {
324        boolean result = super.isPolling();
325        logger.trace("isPolling={}", result);
326        return result;
327    }
328
329    @Override public void setPollingInfo(PollingInfo value) {
330        logger.trace("value={}", value);
331        super.setPollingInfo(value);
332    }
333
334    @Override protected boolean hasPollingInfo() {
335        boolean result = super.hasPollingInfo();
336        logger.trace("hasPollingInfo={}", result);
337        return result;
338    }
339
340    @Override public PollingInfo getPollingInfo() {
341        PollingInfo result = super.getPollingInfo();
342        logger.trace("getPollingInfo={}", result);
343        return result;
344    }
345
346    @Override public void initAfterUnpersistence() {
347        logger.trace("unbundled!");
348        super.initAfterUnpersistence();
349
350        if (this.sourceProps.containsKey(PREVIEW_KEY)) {
351            this.showPreview = (Boolean)this.sourceProps.get(PREVIEW_KEY);
352            if (this.showPreview == null) {
353                this.showPreview = Boolean.FALSE;
354            }
355            this.saveShowPreview = this.showPreview;
356        }
357        
358        this.fromBundle = true;
359        List<AddeImageDescriptor> descriptors = (List<AddeImageDescriptor>) getImageList();
360        this.source = descriptors.get(0).getSource(); // TODO: why not use the source from
361                                                      // each AddeImageDescriptor?
362        for (AddeImageDescriptor descriptor : descriptors) {
363            if (!isFromFile(descriptor)) {
364                this.hasRemoteChoices = true;
365                break;
366            }
367        }
368    }
369
370    @Override public boolean canSaveDataToLocalDisk() {
371        return true;
372    }
373
374    private Hashtable<String, DataSelection> choiceToSel = new Hashtable<String, DataSelection>();
375
376    public DataSelection getSelForChoice(final DataChoice choice) {
377        String key = choice.getName();
378        return choiceToSel.get(key);
379    }
380    public boolean hasSelForChoice(final DataChoice choice) {
381        String key = choice.getName();
382        return choiceToSel.containsKey(key);
383    }
384    public void putSelForChoice(final DataChoice choice, final DataSelection sel) {
385        String key = choice.getName();
386        choiceToSel.put(key, sel);
387    }
388
389    /**
390     * Save files to local disk
391     *
392     * @param prefix destination dir and file prefix
393     * @param loadId For JobManager
394     * @param changeLinks Change internal file references
395     *
396     * @return Files copied
397     *
398     * @throws Exception On badness
399     */
400    
401    @Override protected List saveDataToLocalDisk(String prefix, Object loadId, boolean changeLinks) throws Exception {
402        logger.trace("prefix={} loadId={} changeLinks={}", new Object[] { prefix, loadId, changeLinks });
403        final List<JCheckBox> checkboxes = new ArrayList<JCheckBox>();
404        List categories = new ArrayList();
405        Hashtable catMap = new Hashtable();
406        Hashtable currentDataChoices = new Hashtable();
407
408        List displays = getIdv().getDisplayControls();
409        for (int i = 0; i < displays.size(); i++) {
410            List dataChoices = ((DisplayControl)displays.get(i)).getDataChoices();
411            if (dataChoices == null) {
412                continue;
413            }
414            List finalOnes = new ArrayList();
415            for (int j = 0; j < dataChoices.size(); j++) {
416                ((DataChoice)dataChoices.get(j)).getFinalDataChoices(finalOnes);
417            }
418            for (int dcIdx = 0; dcIdx < finalOnes.size(); dcIdx++) {
419                DataChoice dc = (DataChoice)finalOnes.get(dcIdx);
420                if (!(dc instanceof DirectDataChoice)) {
421                    continue;
422                }
423                DirectDataChoice ddc = (DirectDataChoice) dc;
424                if (ddc.getDataSource() != this) {
425                    continue;
426                }
427                currentDataChoices.put(ddc.getName(), "");
428            }
429        }
430
431        for (int i = 0; i < dataChoices.size(); i++) {
432            DataChoice dataChoice = (DataChoice) dataChoices.get(i);
433            if (!(dataChoice instanceof DirectDataChoice)) {
434                continue;
435            }
436
437            // skip over datachoices that the user has not already loaded.
438            // (but fill the "slot" with null (it's a hack to signify that 
439            // the "download" loop should skip over the data choice associated 
440            // with this slot)
441            if (!currentDataChoices.containsKey(dataChoice.getName())) {
442                checkboxes.add(null); // 
443                continue;
444            }
445
446            String label = dataChoice.getDescription();
447            if (label.length() > 30) {
448                label = label.substring(0, 29) + "...";
449            }
450            JCheckBox cbx =
451                new JCheckBox(label, 
452                              currentDataChoices.get(dataChoice.getName())
453                              != null);
454            ThreeDSize size = (ThreeDSize)dataChoice.getProperty(SIZE_KEY);
455            cbx.setToolTipText(dataChoice.getName());
456            checkboxes.add(cbx);
457            DataCategory dc = dataChoice.getDisplayCategory();
458            if (dc == null) {
459                dc = DataCategory.createCategory(DataCategory.CATEGORY_IMAGE);
460            }
461            List comps = (List)catMap.get(dc);
462            if (comps == null) {
463                comps = new ArrayList();
464                catMap.put(dc, comps);
465                categories.add(dc);
466            }
467            comps.add(cbx);
468            comps.add(GuiUtils.filler());
469            if (size != null) {
470                JLabel sizeLabel = GuiUtils.rLabel(size.getSize() + "  ");
471                sizeLabel.setToolTipText(size.getLabel());
472                comps.add(sizeLabel);
473            } else {
474                comps.add(new JLabel(""));
475            }
476        }
477        final JCheckBox allCbx = new JCheckBox("Select All");
478        allCbx.addActionListener(new ActionListener() {
479            public void actionPerformed(ActionEvent ae) {
480                for (JCheckBox cbx : checkboxes) {
481                    if (cbx != null) {
482                        cbx.setSelected(allCbx.isSelected());
483                    }
484                }
485            }
486        });
487
488        JTabbedPane tab = new JTabbedPane(JTabbedPane.LEFT);
489
490        for (int i = 0; i < categories.size(); i++) {
491            List comps = (List) catMap.get(categories.get(i));
492            JPanel innerPanel = GuiUtils.doLayout(comps, 3, GuiUtils.WT_NYN, GuiUtils.WT_N);
493            JScrollPane sp = new JScrollPane(GuiUtils.top(innerPanel));
494            sp.setPreferredSize(new Dimension(500, 400));
495            JPanel top = GuiUtils.right(GuiUtils.rLabel("  "));
496            JComponent inner = GuiUtils.inset(GuiUtils.topCenter(top, sp), 5);
497            tab.addTab(categories.get(i).toString(), inner);
498        }
499
500        JComponent contents = tab;
501        contents = GuiUtils.topCenter(
502            GuiUtils.inset(
503                GuiUtils.leftRight(
504                    new JLabel("Select the fields to download"),
505                    allCbx), 5), contents);
506        JLabel label = new JLabel(getNameForDataSource(this, 50, true));
507        contents = GuiUtils.topCenter(label, contents);
508        contents = GuiUtils.inset(contents, 5);
509        if (!GuiUtils.showOkCancelDialog(null, "", contents, null)) {
510            return null;
511        }
512
513        // iterate through user's selection to build list of things to download
514        List<String> realUrls = new ArrayList<String>();
515        List<AddeImageDescriptor> descriptorsToSave = new ArrayList<AddeImageDescriptor>();
516        List<BandInfo> bandInfos = (List<BandInfo>)getProperty(PROP_BANDINFO, (Object)null);
517        List<BandInfo> savedBands = new ArrayList<BandInfo>();
518        for (int i = 0; i < dataChoices.size(); i++) {
519            DataChoice dataChoice = (DataChoice)dataChoices.get(i);
520            if (!(dataChoice instanceof DirectDataChoice)) {
521                continue;
522            }
523            JCheckBox cbx = (JCheckBox)checkboxes.get(i);
524            if (cbx == null || !cbx.isSelected()) {
525                continue;
526            }
527
528            if (dataChoice.getDataSelection() == null) {
529                dataChoice.setDataSelection(getSelForChoice(dataChoice));
530            }
531            logger.trace("selected choice={} id={}", dataChoice.getName(), dataChoice.getId());
532            List<AddeImageDescriptor> descriptors = getDescriptors(dataChoice, dataChoice.getDataSelection());
533            logger.trace("descriptors={}", descriptors);
534            
535            BandInfo bandInfo;
536            Object dataChoiceId = dataChoice.getId();
537            if (dataChoiceId instanceof BandInfo) {
538                bandInfo = (BandInfo)dataChoiceId;
539            } else {
540                bandInfo = bandInfos.get(0);
541            }
542            String preferredUnit = bandInfo.getPreferredUnit();
543            List<TwoFacedObject> filteredCalUnits = new ArrayList<TwoFacedObject>();
544            for (TwoFacedObject tfo : (List<TwoFacedObject>)bandInfo.getCalibrationUnits()) {
545                if (preferredUnit.equals(tfo.getId())) {
546                    filteredCalUnits.add(tfo);
547                }
548            }
549            bandInfo.setCalibrationUnits(filteredCalUnits);
550            savedBands.add(bandInfo);
551
552            DataSelection selection = dataChoice.getDataSelection();
553            if (selection == null) {
554                if (getSelForChoice(dataChoice) != null) {
555                    selection = getSelForChoice(dataChoice);
556                } else {
557                    selection = getDataSelection();
558                }
559            }
560
561            Hashtable selectionProperties = selection.getProperties();
562//            Hashtable selectionProperties;
563//            if (selection != null) {
564//                selectionProperties = selection.getProperties();
565//            } else {
566//                DataSelection sel = this.getDataSelection();
567//                selectionProperties = new Hashtable();
568//            }
569            logger.trace("bandinfo.getUnit={} selection props={}", bandInfo.getPreferredUnit(), selectionProperties);
570            for (AddeImageDescriptor descriptor : descriptors) {
571//                AddeImageInfo aii = (AddeImageInfo)descriptor.getImageInfo().clone();
572                if (!isFromFile(descriptor)) {
573                    String src = descriptor.getSource();
574                    logger.trace("src before={}", src);
575                    src = replaceKey(src, AddeImageURL.KEY_UNIT, bandInfo.getPreferredUnit());
576                    if (selectionProperties.containsKey(AddeImageURL.KEY_PLACE)) {
577                        src = replaceKey(src, AddeImageURL.KEY_PLACE, selectionProperties.get(AddeImageURL.KEY_PLACE));
578                    }
579                    if (selectionProperties.containsKey(AddeImageURL.KEY_LATLON)) {
580                        src = replaceKey(src, AddeImageURL.KEY_LINEELE, AddeImageURL.KEY_LATLON, selectionProperties.get(AddeImageURL.KEY_LATLON));
581                    }
582                    if (selectionProperties.containsKey(AddeImageURL.KEY_LINEELE)) {
583                        src = removeKey(src, AddeImageURL.KEY_LATLON);
584                        src = replaceKey(src, AddeImageURL.KEY_LINEELE, selectionProperties.get(AddeImageURL.KEY_LINEELE));
585                    }
586                    if (selectionProperties.containsKey(AddeImageURL.KEY_MAG)) {
587                        src = replaceKey(src, AddeImageURL.KEY_MAG, selectionProperties.get(AddeImageURL.KEY_MAG));
588                    }
589                    if (selectionProperties.containsKey(AddeImageURL.KEY_SIZE)) {
590                        src = replaceKey(src, AddeImageURL.KEY_SIZE, selectionProperties.get(AddeImageURL.KEY_SIZE));
591                    }
592                    logger.trace("src after={}", src);
593                    descriptor.setSource(src);
594                }
595                descriptorsToSave.add(descriptor);
596            }
597//          descriptorsToSave.addAll(descriptors);
598        }
599        if (!savedBands.isEmpty()) {
600            setProperty(PROP_BANDINFO, savedBands);
601        }
602        if (descriptorsToSave.isEmpty()) {
603            return null;
604        }
605
606        // Start the load, showing the dialog
607        List<String> suffixes = new ArrayList<String>();
608        SimpleDateFormat sdf = new SimpleDateFormat("_" + DATAPATH_DATE_FORMAT);
609        sdf.setTimeZone(TimeZone.getTimeZone("GMT"));
610        for (int i = 0; i < descriptorsToSave.size(); i++) {
611            AddeImageDescriptor descriptor = descriptorsToSave.get(i);
612            AddeImageInfo aii = descriptor.getImageInfo();
613            DateTime dttm = (DateTime)timeMap.get(descriptor.getSource());
614            if (dttm != null) {
615                suffixes.add(sdf.format(ucar.visad.Util.makeDate(dttm)) + ".area");
616            } else if (aii != null) {
617                String suffix = "_Band"+aii.getBand()+"_Unit"+aii.getUnit()+"_Pos"+i+".area";
618                suffixes.add(suffix);
619                logger.trace("test suffix={}", suffix);
620            } else {
621                suffixes.add(i + ".area");
622            }
623            realUrls.add(descriptor.getSource());
624        }
625        logger.trace("urls={}", realUrls);
626        logger.trace("prefix={}", prefix);
627        logger.trace("suffixes={}", suffixes);
628        logger.trace("loadId={}", loadId);
629        List newFiles = IOUtil.writeTo(realUrls, prefix, suffixes, loadId);
630        logger.trace("files={}", newFiles);
631        if (newFiles == null) {
632            logger.trace("failed while in writeTo?");
633            return null;
634        } else {
635            logger.trace("finished writeTo!");
636        }
637        if (changeLinks) {
638            imageList = newFiles;
639        }
640
641        // write 0 as the first word
642        for (int i = 0; i < newFiles.size(); i++) {
643            try {
644                RandomAccessFile to = new RandomAccessFile((String)newFiles.get(i), "rw");
645                to.seek(0);
646                to.writeInt(0);
647                to.close();
648            } catch (Exception e) {
649                logger.error("unable to set first word to zero", e);
650            }
651        }
652
653
654//        if (geoSubset != null) {
655//            geoSubset.clearStride();
656//            geoSubset.setBoundingBox(null);
657//            if (geoSelectionPanel != null) {
658//                geoSelectionPanel.initWith(doMakeGeoSelectionPanel());
659//            }
660//        }
661
662//        List newFiles = Misc.newList(path);
663//        if (changeLinks) {
664//            //Get rid of the resolver URL
665//            getProperties().remove(PROP_RESOLVERURL);
666//            setNewFiles(newFiles);
667//        }
668//        
669        logger.trace("returning={}", newFiles);
670        return newFiles;
671    }
672
673    @Override protected String getDataPrefix() {
674        String tmp = StringUtil.replace(getName(), ' ', "");
675        tmp = StringUtil.replace(tmp, '/', "");
676        tmp = StringUtil.replace(tmp, "(AllBands)", "");
677        tmp = IOUtil.cleanFileName(tmp);
678        logger.trace("data prefix={}", tmp);
679        return tmp;
680    }
681    
682    /**
683     * A utility method that helps us deal with legacy bundles that used to
684     * have String file names as the id of a data choice.
685     *
686     * @param object     May be an AddeImageDescriptor (for new bundles) or a
687     *                   String that is converted to an image descriptor.
688     * @return The image descriptor.
689     */
690    @Override public AddeImageDescriptor getDescriptor(Object object) {
691//        logger.trace("--------------------");
692        if (object == null) {
693//            logger.trace("null obj");
694            return null;
695        }
696        if (object instanceof DataChoice) {
697            object = ((DataChoice)object).getId();
698            logger.trace("datachoice getId={}", object);
699        }
700        if (object instanceof ImageDataInfo) {
701            int index = ((ImageDataInfo) object).getIndex();
702            if (index < myDataChoices.size()) {
703                DataChoice dc = (DataChoice)myDataChoices.get(index);
704                Object tmpObject = dc.getId();
705                if (tmpObject instanceof ImageDataInfo) {
706//                    logger.trace("returning imagedatainfo");
707                    return ((ImageDataInfo)tmpObject).getAid();
708                }
709            }
710//            logger.trace("invalid idx for imagedatainfo? (idx={} vs size={})", index, myDataChoices.size());
711            return null;
712            //            return ((ImageDataInfo) object).getAid();
713        }
714
715        if (object instanceof AddeImageDescriptor) {
716//            logger.trace("already addeimagedesc! desc={}", object);
717            return (AddeImageDescriptor)object;
718        }
719        AddeImageDescriptor tmp = new AddeImageDescriptor(object.toString());
720//        logger.trace("return descriptor={}", tmp);
721//        logger.trace("--------------------");
722        return tmp;
723    }
724
725    /**
726     *  Overwrite base class  method to return the name of this class.
727     *
728     *  @return The name.
729     */
730    public String getImageDataSourceName() {
731        return "Adde Image Data Source (Parameter)";
732    }
733
734    private void setMag() {
735        Object magKey = (Object)"mag";
736        if (sourceProps.containsKey(magKey)) {
737            String magVal = (String)(sourceProps.get(magKey));
738            String[] magVals = magVal.split(" ");
739            this.lineMag = new Integer(magVals[0]).intValue();
740            this.elementMag = new Integer(magVals[1]).intValue();
741        }
742    }
743
744    private void getAreaDirectory(Hashtable properties) {
745        String addeCmdBuff = source;
746        if (addeCmdBuff.contains("BAND=")) {
747            String bandStr = getKey(addeCmdBuff, "BAND");
748            if (bandStr.length() == 0) {
749                addeCmdBuff = replaceKey(addeCmdBuff, "BAND", "1");
750            }
751        }
752        if (addeCmdBuff.contains("MAG=")) {
753            String[] segs = addeCmdBuff.split("MAG=");
754            String seg0 = segs[0];
755            String seg1 = segs[1];
756            int indx = seg1.indexOf("&");
757            seg1 = seg1.substring(indx);
758            String magString = lineMag + " " + elementMag;
759            addeCmdBuff = seg0 + "MAG=" + magString + seg1;
760        }
761        addeCmdBuff = addeCmdBuff.replace("imagedata", "imagedir");
762        AreaDirectoryList dirList = null;
763        try {
764            dirList = new AreaDirectoryList(addeCmdBuff);
765        } catch (Exception e) {
766            try {
767                List<BandInfo> bandInfos = (List<BandInfo>)getProperty(PROP_BANDINFO, (Object)null);
768                BandInfo bi = bandInfos.get(0);
769//                String bandStr = new Integer(bi.getBandNumber()).toString();
770                addeCmdBuff = replaceKey(addeCmdBuff, "BAND", bi.getBandNumber());
771                dirList = new AreaDirectoryList(addeCmdBuff);
772            } catch (Exception eOpen) {
773                setInError(true);
774                logger.error("problem opening AREA file", eOpen);
775            }
776        }
777
778        try {
779            List areaDirs = dirList.getDirs();
780            AreaDirectory ad = (AreaDirectory)areaDirs.get(0);
781            float[] res = getLineEleResolution(ad);
782            float resol = res[0];
783            if (this.lineMag < 0) {
784                resol *= Math.abs(this.lineMag);
785            }
786//            this.lineResolution = ad.getValue(11);
787            this.lineResolution = ad.getValue(AreaFile.AD_LINERES);
788            this.lRes = resol;
789            resol = res[1];
790            if (this.elementMag < 0) {
791                resol *= Math.abs(this.elementMag);
792            }
793//            this.elementResolution = ad.getValue(12);
794            this.elementResolution = ad.getValue(AreaFile.AD_ELEMRES);
795            this.eRes = resol;
796        } catch (Exception e) {
797            setInError(true);
798            logger.error("getting area directory", e);
799        }
800        baseSource = addeCmdBuff;
801    }
802
803    protected void initDataSelectionComponents(
804        List components, final DataChoice dataChoice)
805    {
806        if (fromBundle && !hasRemoteChoices) {
807            components.add(new BundlePreviewSelection("Region (Disabled)"));
808            components.add(new BundlePreviewSelection("Advanced (Disabled)"));
809            return;
810        }
811
812        getIdv().showWaitCursor();
813
814        boolean hasImagePreview = true;
815        if (this.showPreview == null) {
816            this.showPreview = true;
817        }
818        boolean basically = false;
819        if (this.lastChoice != null) {
820            basically = dataChoice.basicallyEquals(this.lastChoice);
821        }
822        logger.trace("dataChoice={}", dataChoice);
823        // check for comps and whether or not dataChoice is hooping right back into line
824        if (this.haveDataSelectionComponents && dataChoice.equals(this.lastChoice)) {
825            try {
826                // did the datachoice ever actually get data?
827                if (dataChoice.getDataSelection() == null) {
828                    if (!basically) {
829                        logger.trace("creating geolatlonselection");
830                        this.laLoSel = new GeoLatLonSelection(this,
831                                         dataChoice, this.initProps, this.previewProjection,
832                                         previewDir, previewNav);
833
834                        this.lineMag = this.laLoSel.getLineMag();
835                        this.elementMag = this.laLoSel.getElementMag();
836
837                    /* DAVEP: Force preview on. "No preview" means blank image */
838//                    this.previewSel = new GeoPreviewSelection(this, dataChoice, this.previewImage,
839//                                     this.laLoSel, this.previewProjection,
840//                                     this.lineMag, this.elementMag, this.showPreview);
841                        logger.trace("1: creating geopreviewselection: has geoprevsel: {}", this.previewSel!=null);
842                        this.previewSel = new GeoPreviewSelection(this, dataChoice, this.previewImage,
843                            this.laLoSel, this.previewProjection,
844                            this.lineMag, this.elementMag, true);
845                    }
846//                    this.lineMag = this.laLoSel.getLineMag();
847//                    this.elementMag = this.laLoSel.getElementMag();
848//
849//                    /* DAVEP: Force preview on. "No preview" means blank image */
850////                    this.previewSel = new GeoPreviewSelection(this, dataChoice, this.previewImage,
851////                                     this.laLoSel, this.previewProjection,
852////                                     this.lineMag, this.elementMag, this.showPreview);
853//                    logger.trace("1: creating geopreviewselection: has geoprevsel: {}", this.previewSel!=null);
854//                    this.previewSel = new GeoPreviewSelection(this, dataChoice, this.previewImage,
855//                            this.laLoSel, this.previewProjection,
856//                            this.lineMag, this.elementMag, true);
857                }
858                components.add(this.previewSel);
859                components.add(this.laLoSel);
860            } catch (Exception e) {
861                logger.error("error while repeating addition of selection components", e);
862                getIdv().showNormalCursor();
863            }
864        } else {
865            try {
866                hasImagePreview = makePreviewImage(dataChoice);
867                if (basically) {
868                    getSaveComponents();
869                }
870            } catch (Exception e) {
871                JLabel label = new JLabel("Can't make preview image");
872                JPanel contents = GuiUtils.top(GuiUtils.inset(label, label.getText().length() + 12));
873                GuiUtils.showOkDialog(null, "No Preview Image", contents, null);
874                getIdv().showNormalCursor();
875                logger.error("problem creating preview image", e);
876                return;
877            }
878            this.lastChoice = dataChoice;
879            if (hasImagePreview) {
880                try {
881                    String magStr = getKey(baseSource, MAG_KEY);
882                    String saveMagStr = magStr;
883                    String[] vals = StringUtil.split(magStr, " ", 2);
884                    Integer iVal = new Integer(vals[0]);
885                    int lMag = iVal.intValue() * -1;
886                    if (lMag == -1) {
887                        lMag = 1;
888                    }
889                    iVal = new Integer(vals[1]);
890                    int eMag = iVal.intValue() * -1;
891                    if (eMag == -1) {
892                        eMag = 1;
893                    }
894                    magStr = lMag + " " + eMag;
895                    replaceKey(MAG_KEY, magStr);
896//                    String saveStr = baseSource;
897//                    if (!showPreview) {
898//                        replaceKey(SIZE_KEY, "2 2");
899//                    }
900                    AreaAdapter aa = null;
901                    AREACoordinateSystem acs = null;
902                    try {
903                        logger.trace("creating AreaFile from src={}", baseSource);
904                        if (showPreview) {
905                            aa = new AreaAdapter(baseSource, false);
906                            this.previewImage = (FlatField)aa.getImage();
907                        } else {
908                            this.previewImage = Util.makeField(0, 1, 1, 0, 1, 1, 0, "TEMP");
909                        }
910                        
911                        AreaFile af = new AreaFile(baseSource);
912                        previewNav = af.getNavigation();
913                        AreaDirectory ad = af.getAreaDirectory();
914                        this.lineResolution = ad.getValue(AreaFile.AD_LINERES);
915                        this.elementResolution = ad.getValue(AreaFile.AD_ELEMRES);
916                        acs = new AREACoordinateSystem(af);
917                    } catch (Exception e) {
918                        String excp = e.toString();
919                        int indx = excp.lastIndexOf(":");
920                        String errorText = excp.substring(indx+1);
921                        JLabel label = new JLabel(errorText);
922                        JPanel contents = GuiUtils.top(GuiUtils.inset(label, label.getText().length() + 12));
923                        GuiUtils.showOkDialog(null, "Can't Make Geographical Selection Tabs", contents, null);
924                        getIdv().showNormalCursor();
925                        logger.error("problem creating preview image", e);
926                        return;
927                    }
928                    this.initProps = new Hashtable();
929                    Enumeration propEnum = sourceProps.keys();
930                    for (int i = 0; propEnum.hasMoreElements(); i++) {
931                        String key = propEnum.nextElement().toString();
932                        Object val = sourceProps.get(key);
933                        key = key.toUpperCase();
934                        if (val instanceof String) {
935                            String str = (String)val;
936                            val = (Object)(str.toUpperCase());
937                        }
938                        this.initProps.put(key,val);
939                    }
940                    replaceKey(MAG_KEY, saveMagStr);
941                    magStr = getKey(baseSource, MAG_KEY);
942                    vals = StringUtil.split(magStr, " ", 2);
943                    iVal = new Integer(vals[0]);
944                    lMag = iVal.intValue();
945                    iVal = new Integer(vals[1]);
946                    eMag = iVal.intValue();
947
948                    this.initProps.put("LRES", String.valueOf((this.lRes)));
949                    this.initProps.put("ERES", String.valueOf((this.eRes)));
950                    this.initProps.put("PLRES", String.valueOf((this.previewLineRes)));
951                    this.initProps.put("PERES", String.valueOf((this.previewEleRes)));
952                    this.previewProjection = (MapProjection)acs;
953
954                    String coordType = "";
955                    double coords[] = { 0.0, 0.0 };
956                    
957                    logger.trace("basically={} laLoSel==null?={}", basically, (this.laLoSel==null));
958                    if (!basically) {
959                        if (this.laLoSel != null) {
960                            coordType = this.laLoSel.getCoordinateType();
961                            if (coordType.equals(this.laLoSel.getLatLonType())) {
962                                coords[0] = this.laLoSel.getLatitude();
963                                coords[1] = this.laLoSel.getLongitude();
964                            } else {
965                                coords[0] = (double)this.laLoSel.getLine();
966                                coords[1] = (double)this.laLoSel.getElement();
967                            }
968
969                            // turns out that laLoSel is reused for datachoices
970                            // from the same source. if you don't update laLoSel's
971                            // dataChoice, it'll apply whatever data selection
972                            // you set up... to the first data choice that you
973                            // loaded! (and causing an NPE when attempting to
974                            // bundle the dataselection for the newly-selected
975                            // datachoice.
976                            this.previewSel.setDataChoice(dataChoice);
977                            this.laLoSel.setDataChoice(dataChoice);
978                            this.laLoSel.setPreviewLineRes(this.previewLineRes);
979                            this.laLoSel.setPreviewEleRes(this.previewEleRes);
980                            this.laLoSel.update(previewDir, this.previewProjection, previewNav,
981                                           coordType, coords);
982                            
983                        } else {
984                            this.laLoSel = new GeoLatLonSelection(this, 
985                                          dataChoice, this.initProps, this.previewProjection,
986                                          previewDir, previewNav);
987                            this.lineMag = this.laLoSel.getLineMag();
988                            this.elementMag = this.laLoSel.getElementMag();
989                        }
990                    } else {
991                        if (this.laLoSel != null) {
992                            this.previewSel.setDataChoice(dataChoice);
993                            this.laLoSel.setDataChoice(dataChoice);
994                        }
995                    }
996                    /* DAVEP: Force preview on. "No preview" means blank image */
997//                    this.previewSel = new GeoPreviewSelection(this, dataChoice, this.previewImage, 
998//                                     this.laLoSel, this.previewProjection,
999//                                     this.lineMag, this.elementMag, this.showPreview);
1000                    logger.trace("even reaching this point?");
1001                    this.previewSel = new GeoPreviewSelection(this, dataChoice, this.previewImage, 
1002                            this.laLoSel, this.previewProjection,
1003                            this.lineMag, this.elementMag, showPreview);
1004                    logger.trace("how about this one?");
1005                } catch (Exception e) {
1006                    logger.error("problem making selection components", e);
1007                    getIdv().showNormalCursor();
1008                }
1009                this.haveDataSelectionComponents = true;
1010//                replaceKey(MAG_KEY, (Object)(this.lineMag + " " + this.elementMag));
1011                replaceKey(MAG_KEY, (this.lineMag + " " + this.elementMag));
1012                components.add(this.previewSel);
1013                components.add(this.laLoSel);
1014            }
1015        }
1016        if (this.previewSel != null) {
1017            this.previewSel.initBox();
1018        }
1019        getIdv().showNormalCursor();
1020    }
1021
1022    /**
1023     * A hook to allow this data source to add data selection components
1024     * to the IDV field selector
1025     *
1026     * @param dataChoice the data choice
1027     *
1028     * @return list of components
1029     */
1030//    @Override public List<DataSelectionComponent> getDataSelectionComponents(DataChoice dataChoice) {
1031////        List<DataSelectionComponent> dataSelectionComponents = new ArrayList<DataSelectionComponent>();
1032////        initDataSelectionComponents(dataSelectionComponents, dataChoice);
1033////        return dataSelectionComponents;
1034//        return new ArrayList<DataSelectionComponent>();
1035//    }
1036    
1037    private boolean makePreviewImage(DataChoice dataChoice) {
1038        logger.trace("starting with dataChoice={}", dataChoice);
1039        getIdv().showWaitCursor();
1040
1041        boolean msgFlag = false;
1042        showPreview = saveShowPreview;
1043        List<BandInfo> bandInfos = (List<BandInfo>)getProperty(PROP_BANDINFO, (Object) null);
1044        BandInfo bi = null;
1045
1046        String saveBand = getKey(source, BAND_KEY);
1047
1048        int bandIdx = 0;
1049
1050        logger.trace("band index stuff: saveBand={}, bandIdx={}, source={}", new Object[] { saveBand, bandIdx, source });
1051        List<TwoFacedObject> calList = null;
1052        try {
1053            Object dcObj = dataChoice.getId();
1054            if (dcObj instanceof BandInfo) {
1055                bi = (BandInfo) dcObj;
1056                Integer bandInt = new Integer(bandInfos.indexOf(dcObj)+1);
1057                saveBand = bandInt.toString();
1058            } else {
1059                msgFlag = true;
1060                bi = bandInfos.get(bandIdx);
1061                this.showPreview = false;
1062            }
1063            // pull out the list of cal units, we'll need for type check later...
1064            calList = bi.getCalibrationUnits();
1065            
1066            // TJJ Dec 2017
1067            // Kinda hacky (but then again so is this entire class) :-/
1068            // Do our GEO speedup code for known GEO sensor IDs
1069            int sensorID = bi.getSensor();
1070            if (sensorIsGEO(sensorID)) {
1071                doGeoSpeedup = true;
1072            }
1073
1074            logger.trace("replacing band: new={} from={}", bi.getBandNumber(), source);
1075//            source = replaceKey(source, BAND_KEY, (Object) (bi.getBandNumber()));
1076            source = replaceKey(source, BAND_KEY, bi.getBandNumber());
1077            // if we're replacing the band, replace cal type with preferred  
1078            // type for that band
1079            logger.trace("replacing unit: new={} from={}", bi.getPreferredUnit(), source);
1080//            source = replaceKey(source, UNIT_KEY, (Object) bi.getPreferredUnit());
1081            source = replaceKey(source, UNIT_KEY, bi.getPreferredUnit());
1082        } catch (Exception excp) {
1083            handlePreviewImageError(1, excp);
1084        }
1085        String name = dataChoice.getName();
1086        int idx = name.lastIndexOf('_');
1087        String unit = name.substring(idx + 1);
1088
1089        // if this is not a valid cal unit (e.g. could be set to a plugin formula name)
1090        // set it to something valid
1091        boolean validCal = false;
1092        for (TwoFacedObject tfo : calList) {
1093            if (unit.equals((String) tfo.getId())) {
1094                validCal = true;
1095                break;
1096            }
1097        }
1098        if (!validCal) {
1099            unit = bi.getPreferredUnit();
1100        }
1101
1102        if (getKey(source, UNIT_KEY).length() == 0) {
1103            logger.trace("non-empty unit, replacing: new={} from={}", unit, source);
1104//            source = replaceKey(source, UNIT_KEY, (Object)(unit));
1105            source = replaceKey(source, UNIT_KEY, unit);
1106        }
1107
1108        AddeImageDescriptor aid = null;
1109        while (aid == null) {
1110            try {
1111                logger.trace("creating new AddeImageDescriptor from {}", this.source);
1112                aid = new AddeImageDescriptor(this.source);
1113            } catch (Exception excp) {
1114                msgFlag = true;
1115                if (bandIdx > (bandInfos.size() - 1)) {
1116                        getIdv().showNormalCursor();
1117                    return false;
1118                }
1119                
1120                bi = bandInfos.get(bandIdx);
1121                logger.trace("replacing band: new={} from={}", bi.getBandNumber(), source);
1122//                source = replaceKey(source, BAND_KEY, (Object)(bi.getBandNumber()));
1123                source = replaceKey(source, BAND_KEY, bi.getBandNumber());
1124                ++bandIdx;
1125            }
1126        }
1127//        previewDir = getPreviewDirectory(aid);
1128        AddeImageDescriptor previewDescriptor = getPreviewDirectory(aid);
1129        previewDir = previewDescriptor.getDirectory();
1130        logger.trace("using previewDir={}", previewDir);
1131//        try {
1132//            logger.trace("preview areadir: stlines={} stelements={} lines={} elements={}", new Object[] { previewDir.getValue(AreaFile.AD_STLINE), previewDir.getValue(AreaFile.AD_STELEM), previewDir.getLines(), previewDir.getElements() });
1133//        } catch (Exception e) {
1134//            logger.error("error logging areadir preview", e);
1135//        }
1136        int eMag = 1;
1137        int lMag = 1;
1138        int eSize = 1;
1139        int lSize = 1;
1140        try {
1141            int plMag = 1;
1142            int peMag = 1;
1143            Object magKey = (Object) "mag";
1144            if (sourceProps.containsKey(magKey)) {
1145                String magVal = (String)(sourceProps.get(magKey));
1146                String[] magVals = magVal.split(" ");
1147                peMag = new Integer(magVals[0]).intValue();
1148                plMag = new Integer(magVals[1]).intValue();
1149            }
1150            double feSize = (double) previewDir.getElements();
1151            double flSize = (double) previewDir.getLines();
1152            double feMag = (double) peMag;
1153            double flMag = (double) plMag;
1154            if (feSize > flSize) {
1155                feMag = feSize / 525.0;
1156                flMag = feMag * (double) plMag / (double) peMag;
1157            } else {
1158                flMag = flSize/500.0;
1159                feMag = flMag * (double) peMag / (double) plMag;
1160            }
1161            eMag = (int) Math.ceil(feMag);
1162            lMag = (int) Math.ceil(flMag);
1163        } catch(Exception excp) {
1164           handlePreviewImageError(3, excp);
1165        }
1166        if (eMag < 1) eMag = 1;
1167        if (lMag < 1) lMag = 1;
1168
1169        eSize = 525;
1170        lSize = 500;
1171        if ((baseSource == null) || msgFlag) {
1172            logger.trace("replacing\nbaseSource={}\nsource={}", baseSource, source);
1173            baseSource = source;
1174        }
1175        this.previewLineRes = lMag;
1176        this.previewEleRes = eMag;
1177        String uLStr = "0 0 F";
1178        // MJH getting uLStr from previewDir here breaks Himawari-8,
1179        // and is apparently unnecessary in general, so just use "0 0 F" always.
1180        // try {
1181        //     int startLine = previewDir.getValue(AreaFile.AD_STLINE);
1182        //     int startEle = previewDir.getValue(AreaFile.AD_STELEM);
1183        //     uLStr = startLine + " " + startEle + " I";
1184        // } catch (Exception e) {
1185        // }
1186//        String src = aid.getSource();
1187        String src = previewDescriptor.getSource();
1188        logger.trace("building preview request from src={}", src);
1189        
1190        src = removeKey(src, LATLON_KEY);
1191        src = replaceKey(src, LINELE_KEY, uLStr);
1192        src = replaceKey(src, PLACE_KEY, "ULEFT");
1193        src = replaceKey(src, SIZE_KEY,(lSize + " " + eSize));
1194        src = replaceKey(src, MAG_KEY, (lMag + " " + eMag));
1195        src = replaceKey(src, BAND_KEY, bi.getBandNumber());
1196        src = replaceKey(src, UNIT_KEY, unit);
1197//        if (aid.getIsRelative()) {
1198//            logger.trace("injecting POS={}", aid.getRelativeIndex());
1199//            src = replaceKey(src, "POS", (Object)aid.getRelativeIndex());
1200//        }
1201//        if (previewDescriptor.getIsRelative()) {
1202//            logger.trace("inject POS={} into src={}", previewDescriptor.getRelativeIndex(), src);
1203//            src = replaceKey(src, "POS", (Object)previewDescriptor.getRelativeIndex());
1204//            src = replaceKey(src, "POS", previewDescriptor.getRelativeIndex());
1205//        }
1206
1207        logger.trace("creating AddeImageDescriptor from src={}", src);
1208        try {
1209            aid = new AddeImageDescriptor(src);
1210        } catch (Exception excp) {
1211            handlePreviewImageError(4, excp);
1212            src = replaceKey(src, BAND_KEY, saveBand);
1213            aid = new AddeImageDescriptor(src);
1214            src = replaceKey(src, BAND_KEY, bi.getBandNumber());
1215        }
1216        if (msgFlag && (!"ALL".equals(saveBand))) {
1217            src = replaceKey(src, BAND_KEY, saveBand);
1218        }
1219        logger.trace("overwriting\nbaseSource={}\nsrc={}", baseSource, src);
1220        baseSource = src;
1221        getIdv().showNormalCursor();
1222        return true;
1223    }
1224
1225    /**
1226     * Return true if the Sensor is Geostationary
1227     * 
1228     * @param McIDAS Sensor Source number. 
1229     *  See https://www.ssec.wisc.edu/mcidas/doc/users_guide/2017.2/app_c-1.html
1230     * @return true if ID matches a defined GEO sensor
1231     */
1232    
1233    private boolean sensorIsGEO(int sensorID) {
1234        boolean isGEO = false;
1235        
1236        // early GEO through FY
1237        if ((sensorID >= 12) && (sensorID <= 40)) isGEO = true; 
1238        
1239        // Meteosat
1240        if ((sensorID >= 51) && (sensorID <= 58)) isGEO = true;
1241        if (sensorID == 354) isGEO = true;
1242
1243        // GOES 8 - 12
1244        if ((sensorID >= 70) && (sensorID <= 79)) isGEO = true;
1245
1246        // GMS, MTSAT (which is actually pre-8 Himawari), H-8/9
1247        if ((sensorID >= 82) && (sensorID <= 86)) isGEO = true;
1248        if ((sensorID >= 286) && (sensorID <= 288)) isGEO = true;
1249
1250        // Feng Yun
1251        if ((sensorID >= 95) && (sensorID <= 97)) isGEO = true;
1252        if ((sensorID >= 275) && (sensorID <= 277)) isGEO = true;
1253
1254        // GOES 13 - 17
1255        // NOTE: Assumes GOES-17 will take the 188 to 190 slots
1256        if ((sensorID >= 180) && (sensorID <= 190)) isGEO = true;
1257
1258        // Kalpana and INSAT
1259        if ((sensorID >= 230) && (sensorID <= 232)) isGEO = true;
1260
1261        // COMS
1262        if (sensorID == 250) isGEO = true;
1263        
1264        return isGEO;
1265    }
1266
1267    /**
1268     * Show the given error to the user. 
1269     *
1270     * @param excp The exception
1271     */
1272    protected void handlePreviewImageError(int flag, Exception excp) {
1273        getIdv().showNormalCursor();
1274        LogUtil.userErrorMessage("Error in makePreviewImage  e=" + flag + " " + excp);
1275    }
1276
1277    public static String removeKey(String src, String key) {
1278        String returnString = src;
1279        key = key.toUpperCase() + '=';
1280        if (returnString.contains(key)) {
1281            String[] segs = returnString.split(key);
1282            String seg0 = segs[0];
1283            String seg1 = segs[1];
1284            int indx = seg1.indexOf('&');
1285            if (indx >= 0) {
1286                seg1 = seg1.substring(indx + 1);
1287            }
1288            returnString = seg0 + seg1;
1289        }
1290        return returnString;
1291    }
1292
1293    public static String replaceKey(String sourceUrl, String key, Object value) {
1294        String returnString = sourceUrl;
1295
1296        // make sure we got valid key/value pair
1297        if ((key == null) || (value == null)) {
1298            return returnString;
1299        }
1300
1301        key = key.toUpperCase() + '=';
1302        String strValue = value.toString();
1303        if (returnString.contains(key)) {
1304            String[] segs = returnString.split(key);
1305            String seg0 = segs[0];
1306            String seg1 = segs[1];
1307            int indx = seg1.indexOf('&');
1308            if (indx < 0) {
1309                seg1 = "";
1310            } else if (indx > 0) {
1311                seg1 = seg1.substring(indx);
1312            }
1313            returnString = seg0 + key + strValue + seg1;
1314        } else {
1315            returnString = returnString + '&' + key + strValue;
1316        }
1317
1318        // if key is for cal units, and it was changed to BRIT,
1319        // must change the spacing key too 
1320        if ((key.equals(UNIT_KEY + '=')) && ("BRIT".equals(strValue))) {
1321            returnString = replaceKey(returnString, SPAC_KEY, SPAC_KEY, SPACING_BRIT);
1322        } else {
1323            returnString = replaceKey(returnString, SPAC_KEY, SPAC_KEY, SPACING_NON_BRIT); 
1324        }
1325        return returnString;
1326    }
1327
1328    public static String replaceKey(String src, String oldKey, String newKey, Object value) {
1329        String returnString = src;
1330        oldKey = oldKey.toUpperCase() + '=';
1331        newKey = newKey.toUpperCase() + '=';
1332        if (returnString.contains(oldKey)) {
1333            String[] segs = returnString.split(oldKey);
1334            String seg0 = segs[0];
1335            String seg1 = segs[1];
1336            int indx = seg1.indexOf('&');
1337            if (indx < 0) {
1338                seg1 = "";
1339            } else if (indx > 0) {
1340                seg1 = seg1.substring(indx);
1341            }
1342            returnString = seg0 + newKey + value.toString() + seg1;
1343        }
1344        else {
1345            returnString = returnString + '&' + newKey + value.toString();
1346        }
1347        return returnString;
1348    }
1349
1350    private <T> void replaceKey(String key, T value) {
1351        baseSource = replaceKey(baseSource, key, value);
1352    }
1353
1354    public static String getKey(String src, String key) {
1355        String returnString = "";
1356        key = key.toUpperCase() + '=';
1357        if (src.contains(key)) {
1358            String[] segs = src.split(key);
1359            segs = segs[1].split("&");
1360            returnString = segs[0];
1361        }
1362        return returnString;
1363    }
1364
1365
1366    /**
1367     * Create the set of {@link ucar.unidata.data.DataChoice} that represent
1368     * the data held by this data source.  We create one top-level
1369     * {@link ucar.unidata.data.CompositeDataChoice} that represents
1370     * all of the image time steps. We create a set of children
1371     * {@link ucar.unidata.data.DirectDataChoice}, one for each time step.
1372     */
1373    
1374    public void doMakeDataChoices() {
1375        super.doMakeDataChoices();
1376        List<BandInfo> bandInfos = (List<BandInfo>) getProperty(PROP_BANDINFO, (Object) null);
1377        String name = "";
1378        if (this.choiceName != null) {
1379            name = this.choiceName;
1380        }
1381        if (name.length() != 0) {
1382            logger.trace("already have a name={}", name);
1383            return;
1384        }
1385        if (!sourceProps.containsKey(UNIT_KEY)) {
1386            logger.trace("sourceProps has no unit key={}", sourceProps);
1387            return;
1388        }
1389        BandInfo bi = null;
1390        if (sourceProps.containsKey(BAND_KEY)) {
1391            int bandProp = new Integer((String)(sourceProps.get(BAND_KEY))).intValue();
1392            int bandIndex = BandInfo.findIndexByNumber(bandProp, bandInfos);
1393            bi = (BandInfo)bandInfos.get(bandIndex);
1394            if (sourceProps.containsKey(UNIT_KEY)) {
1395                bi.setPreferredUnit((String)(sourceProps.get(UNIT_KEY)));
1396            } else {
1397                bi.setPreferredUnit("");
1398            }
1399            name = makeBandParam(bi);
1400        }
1401        else if (sourceProps.containsKey(BANDINFO_KEY)) {
1402            ArrayList al = (ArrayList) sourceProps.get(BANDINFO_KEY);
1403            bi = (BandInfo) al.get(0);
1404            name = makeBandParam(bi);
1405        }
1406        if (stashedChoices != null) {
1407            int numChoices = stashedChoices.size();
1408            for (int i = 0; i < numChoices; i++) {
1409               DataChoice choice = (DataChoice) stashedChoices.get(i);
1410               if (name.equals(choice.getName())) {
1411                   setProperty(PROP_DATACHOICENAME, choice.getName());
1412               }
1413            }
1414        }
1415    }
1416
1417    /**
1418     * Overridden so that McIDAS-V can <i>attempt</i> to return the correct
1419     * {@code DataSelection} for the current {@code DataChoice}.
1420     */
1421    
1422    @Override public DataSelection getDataSelection() {
1423        DataSelection tmp;
1424
1425        if (this.laLoSel != null) {
1426            logger.trace("* mcv getSelForChoice: choice='{}'", this.laLoSel.getDataChoice());
1427            tmp = this.getSelForChoice(this.laLoSel.getDataChoice());
1428        } else {
1429            logger.trace("* idvland getDataSelection laLoSel=null: {}; choiceToSel=null: {}", (this.laLoSel==null), (this.choiceToSel==null));
1430            tmp = super.getDataSelection();
1431        }
1432//        if (this.laLoSel == null || this.choiceToSel == null || !this.choiceToSel.containsKey(this.laLoSel.getDataChoice())) {
1433//            logger.trace("* idvland getDataSelection laLoSel=null: {}; choiceToSel=null: {}", (this.laLoSel==null), (this.choiceToSel==null));
1434//            tmp = super.getDataSelection();
1435//        } else if (this.laLoSel != null) {
1436//            logger.trace("* mcv getSelForChoice");
1437//            tmp = this.getSelForChoice(this.laLoSel.getDataChoice());
1438//        }
1439        if (tmp != null) {
1440            logger.trace("return selection props={} geo={}", tmp.getProperties(), tmp.getGeoSelection());
1441        } else {
1442            logger.trace("return selection props=null geo=null choiceToSel={} :(", this.choiceToSel);
1443        }
1444        return tmp;
1445    }
1446
1447    /**
1448     * Overridden so that McIDAS-V can associate this data source's current 
1449     * {@code DataChoice} with the given {@code DataSelection}.
1450     */
1451    
1452    @Override public void setDataSelection(DataSelection s) {
1453        GeoSelection tmp = s.getGeoSelection();
1454        if (tmp != null && this.laLoSel != null) {
1455            GeoLocationInfo bbox = tmp.getBoundingBox();
1456            GeoLocationInfo laloBbox = this.laLoSel.getGeoLocationInfo();
1457            tmp.setBoundingBox(laloBbox);
1458            logger.trace("incoming bbox={} laLo bbox={}", bbox, laloBbox);
1459        }
1460
1461        super.setDataSelection(s);
1462
1463        if (this.laLoSel != null) {
1464//            logger.trace("putting selection for choice={} s={}", this.laLoSel.getDataChoice(), s);
1465            this.putSelForChoice(this.laLoSel.getDataChoice(), s);
1466        } else {
1467            logger.trace("laLoSel is null; s={}", s);
1468        }
1469        logger.trace("setting selection props={} geo={}", s.getProperties(), s.getGeoSelection());
1470    }
1471    
1472    /**
1473     * Insert the new DataChoice into the dataChoice list.
1474     *
1475     * @param choice   new choice to add
1476     */
1477    
1478    protected void addDataChoice(DataChoice choice) {
1479        logger.trace("choice={}", choice);
1480        super.addDataChoice(choice);
1481        if (stashedChoices == null) {
1482            stashedChoices = new ArrayList();
1483        }
1484        stashedChoices.add(choice);
1485    }
1486
1487    /**
1488     * Checks to see if a given {@code AddeImageDescriptor} is based upon a 
1489     * local (or remote) file.
1490     * 
1491     * <p>The check is pretty simple: is {@code descriptor.getSource()} a valid
1492     * path?
1493     * 
1494     * @param descriptor {@code AddeImageDescriptor} of questionable origins. Shouldn't be {@code null}.
1495     * 
1496     * @return {@code true} if {@code descriptor}'s source is a valid path.
1497     */
1498    
1499    public static boolean isFromFile(final AddeImageDescriptor descriptor) {
1500        return new File(descriptor.getSource()).exists();
1501    }
1502
1503    /**
1504     * Create the actual data represented by the given
1505     * {@link ucar.unidata.data.DataChoice}.
1506     *
1507     * @param dataChoice        Either the
1508     *                          {@link ucar.unidata.data.CompositeDataChoice}
1509     *                          representing all time steps or a
1510     *                          {@link ucar.unidata.data.DirectDataChoice}
1511     *                          representing a single time step.
1512     * @param category          Not really used.
1513     * @param dataSelection     Defines any time subsets.
1514     * @param requestProperties extra request properties
1515     *
1516     * @return The image or image sequence data.
1517     *
1518     * @throws RemoteException    Java RMI problem
1519     * @throws VisADException     VisAD problem
1520     */
1521    
1522    protected Data getDataInner(DataChoice dataChoice, DataCategory category,
1523                                DataSelection dataSelection,
1524                                Hashtable requestProperties)
1525            throws VisADException, RemoteException {
1526        Data img = null;
1527        iml = new ArrayList();
1528
1529        if (dataSelection == null) {
1530            return null;
1531        }
1532        setDataSelection(dataSelection);
1533
1534        GeoSelection geoSelection = dataSelection.getGeoSelection(true);
1535        if (geoSelection == null) {
1536            return null;
1537        }
1538
1539        boolean validState = geoSelection.getHasValidState();
1540        if (!validState) {
1541            return null;
1542        }
1543
1544        if (this.lastGeoSelection == null) {
1545            this.lastGeoSelection = geoSelection;
1546        }
1547
1548        this.selectionProps = dataSelection.getProperties();
1549        Enumeration propEnum = this.selectionProps.keys();
1550        while (propEnum.hasMoreElements()) {
1551            String key = propEnum.nextElement().toString();
1552            if (key.compareToIgnoreCase(LATLON_KEY) == 0) {
1553                String val = (String)this.selectionProps.get(key);
1554                if (val.contains("NaN")) {
1555                    return img;
1556                }
1557            }
1558            if (key.compareToIgnoreCase(LINELE_KEY) == 0) {
1559                String val = (String)this.selectionProps.get(key);
1560                if (val.contains("NaN")) {
1561                    return img;
1562                }
1563            }
1564        }
1565
1566        if (this.selectionProps.containsKey("MAG")) {
1567            String str = (String)this.selectionProps.get("MAG");
1568            String[] strs = StringUtil.split(str, " ", 2);
1569            this.lineMag = new Integer(strs[0]).intValue();
1570            this.elementMag = new Integer(strs[1]).intValue();
1571        }
1572        this.choiceName = dataChoice.getName();
1573        if (this.choiceName != null) {
1574            setProperty(PROP_DATACHOICENAME, this.choiceName);
1575        }
1576        try {
1577            img = super.getDataInner(dataChoice, category, dataSelection, requestProperties);
1578        } catch (Exception e) {
1579            String displaySrc = getDisplaySource();
1580            if (displaySrc != null) {
1581                AddeImageDescriptor aid = new AddeImageDescriptor(displaySrc);
1582                dataChoice.setId((Object)aid);
1583                img = super.getDataInner(dataChoice, category, dataSelection, requestProperties);
1584            }
1585        }
1586        return img;
1587    }
1588
1589    /**
1590     * Check if the DataChoice has a BandInfo for its ID
1591     *
1592     * @param dataChoice  choice to check
1593     *
1594     * @return true if the choice ID is a BandInfo
1595     */
1596    
1597    private boolean hasBandInfo(DataChoice dataChoice) {
1598        Object id = dataChoice.getId();
1599        return id instanceof BandInfo;
1600    }
1601
1602    /** _more_ */
1603    AreaDirectory[][] currentDirs;
1604
1605    /**
1606     * Create the  image sequence defined by the given dataChoice.
1607     *
1608     * @param dataChoice     The choice.
1609     * @param subset     any time subsets.
1610     * @return The image sequence.
1611     *
1612     * @throws RemoteException    Java RMI problem
1613     * @throws VisADException     VisAD problem
1614     */
1615    
1616    protected ImageSequence makeImageSequence(DataChoice dataChoice, DataSelection subset)
1617            throws VisADException, RemoteException {
1618
1619//        if (dataChoice.getDataSelection() == null) {
1620//            dataChoice.setDataSelection(subset);
1621//        }
1622        Hashtable subsetProperties = subset.getProperties();
1623        Enumeration propEnum = subsetProperties.keys();
1624        int numLines = 0;
1625        int numEles = 0;
1626        while (propEnum.hasMoreElements()) {
1627            String key = propEnum.nextElement().toString();
1628            if (key.compareToIgnoreCase(SIZE_KEY) == 0) {
1629                String sizeStr = (String)(subsetProperties.get(key));
1630                String[] vals = StringUtil.split(sizeStr, " ", 2);
1631                Integer iVal = new Integer(vals[0]);
1632                numLines = iVal.intValue();
1633                iVal = new Integer(vals[1]);
1634                numEles = iVal.intValue();
1635                break;
1636            }
1637        }
1638
1639        if (sampleMapProjection == null) {
1640            String addeCmdBuff = baseSource;
1641            AreaFile af = null;
1642            try {
1643                af = new AreaFile(addeCmdBuff);
1644            } catch (Exception eOpen) {
1645                logger.error("could not open area file: {}", eOpen);
1646                setInError(true);
1647                throw new BadDataException("Opening area file: " + eOpen.getMessage(), eOpen);
1648            }
1649            try {
1650                McIDASAreaProjection map = new McIDASAreaProjection(af);
1651                AREACoordinateSystem acs = new AREACoordinateSystem(af);
1652                sampleMapProjection = (MapProjection)acs;
1653                sampleProjection = map;
1654            } catch (Exception e) {
1655                logger.error("making area projection: {}", e);
1656                setInError(true);
1657                throw new BadDataException("Making area projection: " + e.getMessage(), e);
1658            }
1659        }
1660        AREACoordinateSystem macs = (AREACoordinateSystem)sampleMapProjection;
1661        int[] dirBlk = macs.getDirBlock();
1662        if (numLines == 0) {
1663            double elelin[][] = new double[2][2];
1664            double latlon[][] = new double[2][2];
1665            GeoSelection gs = subset.getGeoSelection();
1666            GeoLocationInfo gli = gs.getBoundingBox();
1667            if ((gli == null) && (lastGeoSelection != null)) {
1668                subset.setGeoSelection(lastGeoSelection);
1669                gs = lastGeoSelection;
1670                gli = gs.getBoundingBox();
1671            }
1672            LatLonPoint llp = gli.getUpperLeft();
1673            latlon[0][0] = llp.getLatitude();
1674            latlon[1][0] = llp.getLongitude();
1675            llp = gli.getLowerRight();
1676            latlon[0][1] = llp.getLatitude();
1677            latlon[1][1] = llp.getLongitude();
1678            elelin = macs.fromReference(latlon);
1679            numLines = (int)(Math.abs(elelin[1][0] - elelin[1][1]))*dirBlk[11];
1680            numEles = (int)(Math.abs(elelin[0][1] - elelin[0][0]))*dirBlk[12];
1681        }
1682
1683        try {
1684            descriptorsToUse = new ArrayList();
1685            if (hasBandInfo(dataChoice)) {
1686                descriptorsToUse = getDescriptors(dataChoice, subset);
1687            } else {
1688                List choices = (dataChoice instanceof CompositeDataChoice)
1689                               ? getChoicesFromSubset(
1690                                   (CompositeDataChoice) dataChoice, subset)
1691                               : Arrays.asList(new DataChoice[] {
1692                                   dataChoice });
1693                for (Iterator iter = choices.iterator(); iter.hasNext(); ) {
1694                    DataChoice          subChoice = (DataChoice) iter.next();
1695                    AddeImageDescriptor aid =
1696                        getDescriptor(subChoice.getId());
1697                    if (aid == null) {
1698                        continue;
1699                    }
1700                    DateTime dttm = aid.getImageTime();
1701                    if ((subset != null) && (dttm != null)) {
1702                        List times = getTimesFromDataSelection(subset,
1703                                         dataChoice);
1704                        if ((times != null) && (times.indexOf(dttm) == -1)) {
1705                            continue;
1706                        }
1707                    }
1708                    descriptorsToUse.add(aid);
1709                }
1710            }
1711
1712            if (descriptorsToUse == null || descriptorsToUse.size() == 0) {
1713                return null;
1714            }
1715            AddeImageInfo biggestPosition = null;
1716            int           pos             = 0;
1717            boolean       anyRelative     = false;
1718            // Find the descriptor with the largest position
1719            for (Iterator iter = descriptorsToUse.iterator(); iter.hasNext(); ) {
1720                AddeImageDescriptor aid = (AddeImageDescriptor) iter.next();
1721                if (aid.getIsRelative()) {
1722                    anyRelative = true;
1723                }
1724                AddeImageInfo       aii = aid.getImageInfo();
1725
1726                // Are we dealing with area files here?
1727                if (aii == null) {
1728                    break;
1729                }
1730
1731                // Check if this is absolute time
1732                if ((aii.getStartDate() != null) || (aii.getEndDate() != null)) {
1733                    biggestPosition = null;
1734                    break;
1735                }
1736                if ((biggestPosition == null) || (Math.abs(aii.getDatasetPosition()) > pos)) {
1737                    pos             = Math.abs(aii.getDatasetPosition());
1738                    biggestPosition = aii;
1739                }
1740            }
1741
1742            if (getCacheDataToDisk() && anyRelative && (biggestPosition != null)) {
1743                biggestPosition.setRequestType(AddeImageInfo.REQ_IMAGEDIR);
1744                AreaDirectoryList adl = new AreaDirectoryList(biggestPosition.getURLString());
1745                biggestPosition.setRequestType(AddeImageInfo.REQ_IMAGEDATA);
1746                currentDirs = adl.getSortedDirs();
1747            } else {
1748                currentDirs = null;
1749            }
1750
1751            int cnt = 1;
1752            DataChoice parent = dataChoice.getParent();
1753            final List<SingleBandedImage> images = new ArrayList<SingleBandedImage>();
1754            MathType rangeType = null;
1755            for (Iterator iter = descriptorsToUse.iterator(); iter.hasNext(); ) {
1756                final AddeImageDescriptor aid = (AddeImageDescriptor) iter.next();
1757                if (currentDirs != null) {
1758                    int idx = Math.abs(aid.getImageInfo().getDatasetPosition());
1759                    if (idx >= currentDirs.length) {
1760                        continue;
1761                    }
1762                }
1763
1764                String label = "";
1765                if (parent != null) {
1766                    label = label + parent.toString() + ' ';
1767                } else {
1768                    DataCategory displayCategory = dataChoice.getDisplayCategory();
1769                    if (displayCategory != null) {
1770                        label = label + displayCategory + ' ';
1771                    }
1772                }
1773                label = label + dataChoice.toString();
1774                final String readLabel = "Time: " + (cnt++) + '/'
1775                    + descriptorsToUse.size() + ' '
1776                    + label;
1777
1778                String src = aid.getSource();
1779                if (!isFromFile(aid)) {
1780                    try {
1781                        src = replaceKey(src, LINELE_KEY, (Object)("1 1"));
1782                        String sizeString = "10 10";
1783                        src = replaceKey(src, SIZE_KEY, (Object)(sizeString));
1784                        String name = dataChoice.getName();
1785                        int idx = name.lastIndexOf('_');
1786                        String unit = name.substring(idx+1);
1787                        if (getKey(src, UNIT_KEY).length() == 0) {
1788                            src = replaceKey(src, UNIT_KEY, (Object)(unit));
1789                        }
1790                        int lSize = numLines;
1791                        int eSize = numEles;
1792                        sizeString = lSize + " " + eSize;
1793                        src = replaceKey(src, SIZE_KEY, (Object)(sizeString));
1794                        src = replaceKey(src, MAG_KEY, (Object)(this.lineMag + " " + this.elementMag));
1795                        aid.setSource(src);
1796                    } catch (Exception exc) {
1797                        logger.error("error trying to adjust AddeImageDescriptor: {}", exc);
1798                        super.makeImageSequence(dataChoice, subset);
1799                    }
1800                }
1801
1802                try {
1803                    SingleBandedImage image = makeImage(aid, rangeType, true, readLabel, subset);
1804                    if (image != null) {
1805                        if(rangeType==null) {
1806                            rangeType = ((FunctionType) image.getType()).getRange();
1807                        }
1808                        synchronized (images) {
1809                            images.add(image);
1810                        }
1811                    }
1812                } catch (VisADException e) {
1813                    logger.error("avoiding visad exception: ",e);
1814                } catch (RemoteException e) {
1815                    logger.error("avoiding remote exception: ", e);
1816                }
1817            }
1818
1819            TreeMap imageMap = new TreeMap();
1820            for (SingleBandedImage image : images) {
1821                imageMap.put(image.getStartTime(), image);
1822            }
1823            List<SingleBandedImage> sortedImages = (List<SingleBandedImage>) new ArrayList(imageMap.values());
1824            if ((sortedImages.size() > 0) && (sortedImages.get(0) instanceof AreaImageFlatField)) {
1825                DataRange[] sampleRanges = null;
1826                Set domainSet = null;
1827                for (SingleBandedImage sbi : sortedImages) {
1828                    AreaImageFlatField aiff = (AreaImageFlatField) sbi;
1829                    sampleRanges = aiff.getRanges(true);
1830                    if (domainSet == null) {
1831                        domainSet = aiff.getDomainSet();
1832                    }
1833                    if ((sampleRanges != null) && (sampleRanges.length > 0)) {
1834                        for (int rangeIdx = 0; rangeIdx < sampleRanges.length; rangeIdx++) {
1835                            DataRange r = sampleRanges[rangeIdx];
1836                            if (Double.isInfinite(r.getMin()) || Double.isInfinite(r.getMax())) {
1837                                sampleRanges = null;
1838                                break;
1839                            }
1840                        }
1841                    }
1842                    if (sampleRanges != null) {
1843                        break;
1844                    }
1845                }
1846
1847                if (sampleRanges != null) {
1848                    for (SingleBandedImage sbi : sortedImages) {
1849                        AreaImageFlatField aiff = (AreaImageFlatField) sbi;
1850                        aiff.setSampleRanges(sampleRanges);
1851                        aiff.setDomainIfNeeded(domainSet);
1852                    }
1853                }
1854            }
1855
1856            SingleBandedImage[] imageArray =
1857                (SingleBandedImage[]) sortedImages.toArray(
1858                    new SingleBandedImage[sortedImages.size()]);
1859            FunctionType imageFunction =
1860                (FunctionType) imageArray[0].getType();
1861            FunctionType ftype = new FunctionType(RealType.Time,
1862                                     imageFunction);
1863            return new ImageSequenceImpl(ftype, imageArray);
1864        } catch (Exception exc) {
1865            throw new ucar.unidata.util.WrapperException(exc);
1866        }
1867    }
1868    
1869    /**
1870     * Create the single image defined by the given 
1871     * {@link ucar.unidata.data.imagery.AddeImageDescriptor AddeImageDescriptor}.
1872     *
1873     * @param aid Holds image directory and location of the desired image.
1874     * @param rangeType {@literal "rangeType"} to use (if non-{@code null}).
1875     * @param fromSequence _more_
1876     * @param readLabel 
1877     * @param subset geographical subsetting info
1878     *
1879     * @return The data.
1880     *
1881     * @throws RemoteException Java RMI problem
1882     * @throws VisADException VisAD problem
1883     */
1884    
1885    private SingleBandedImage makeImage(AddeImageDescriptor aid,
1886                                        MathType rangeType,
1887                                        boolean fromSequence, 
1888                                        String readLabel, DataSelection subset)
1889            throws VisADException, RemoteException {
1890        
1891        if (aid == null) {
1892            return null;
1893        }
1894
1895        logger.trace("incoming src={} DateTime={} readLabel={}", new Object[] { aid.getSource(), aid.getImageTime(), readLabel });
1896        String src = aid.getSource();
1897
1898        Hashtable props = subset.getProperties();
1899        
1900//        String areaDirectoryKey = getKey(src, "POS");
1901        String areaDirectoryKey = null;
1902        if (aid.getIsRelative()) {
1903            areaDirectoryKey = getKey(src, "POS");
1904        } else {
1905            areaDirectoryKey = aid.getImageTime().toString();
1906//            areaDirectoryKey = getKey(src, "TIME");
1907        }
1908        AreaDirectory hacked = requestIdToDirectory.get(areaDirectoryKey);
1909
1910        // it only makes sense to set the following properties for things
1911        // coming from an ADDE server
1912        if (!isFromFile(aid)) {
1913            if (props.containsKey("PLACE")) {
1914                src = replaceKey(src, "PLACE", props.get("PLACE"));
1915            }
1916            if (props.containsKey("LATLON")) { 
1917                src = replaceKey(src, "LINELE", "LATLON", props.get("LATLON"));
1918            }
1919            if (props.containsKey("LINELE")) {
1920                src = removeKey(src, "LATLON");
1921                src = replaceKey(src, "LINELE", props.get("LINELE"));
1922            }
1923            if (props.containsKey("MAG")) {
1924                src = replaceKey(src, "MAG", props.get("MAG"));
1925            }
1926        }
1927        if (hacked != null) {
1928            logger.trace("adjusted src={} areaDirectoryKey='{}' hacked lat={} lon={}", new Object[] { src, areaDirectoryKey, hacked.getCenterLatitude(), hacked.getCenterLongitude() });
1929        }
1930        aid.setSource(src);
1931
1932        SingleBandedImage result;
1933        result = (SingleBandedImage)getCache(src);
1934        if (result != null) {
1935            setDisplaySource(src, props);
1936            return result;
1937        }
1938
1939        // For now handle non ADDE URLs here
1940        try {
1941            AddeImageInfo aii = aid.getImageInfo();
1942            AreaDirectory areaDir = null;
1943            try {
1944                if (aii != null) {
1945                    logger.trace("imageinfo={}", aii.toString());
1946                    if (currentDirs != null) {
1947                        int pos = Math.abs(aii.getDatasetPosition());
1948                        int band = 0;
1949                        String bandString = aii.getBand();
1950                        if ((bandString != null) && !AddeURL.ALL.equals(bandString)) {
1951                            band = new Integer(bandString).intValue();
1952                        }
1953                        // TODO: even though the band is non-zero we might only 
1954                        // get back one band
1955                        band = 0;
1956                        areaDir = currentDirs[currentDirs.length - pos - 1][band];
1957                    } else {
1958                        // If its absolute time then just use the AD from the descriptor
1959                        if ((aii.getStartDate() != null) || (aii.getEndDate() != null)) {
1960                            areaDir = aid.getDirectory();
1961                        } else {
1962                        }
1963                    }
1964                } else {
1965                    logger.trace("uh oh");
1966                }
1967            } catch (Exception exc) {
1968                LogUtil.printMessage("error looking up area dir");
1969                logger.error("error looking up area dir", exc);
1970                return null;
1971            }
1972
1973            if (areaDir == null) {
1974                areaDir = aid.getDirectory();
1975            }
1976
1977            if (!getCacheDataToDisk()) {
1978                areaDir = null;
1979            }
1980
1981            if (!fromSequence || (aid.getIsRelative() && (currentDirs == null))) {
1982                areaDir = null;
1983            }
1984
1985            if (areaDir != null) {
1986                if (isFromFile(aid)) {
1987                  if (rangeType == null) {
1988                      result = AreaImageFlatField.createImmediate(aid, readLabel);
1989                  } else {
1990                      // Else, pass in the already created range type
1991                      result  = AreaImageFlatField.create(aid, areaDir, rangeType, readLabel);
1992                  }
1993                }
1994
1995            } else {
1996                src = aid.getSource();
1997                try {
1998                    savePlace = this.laLoSel.getPlace();
1999                    saveLat = this.laLoSel.getLatitude();
2000                    saveLon = this.laLoSel.getLongitude();
2001                    saveNumLine = this.laLoSel.getNumLines();
2002                    saveNumEle = this.laLoSel.getNumEles();
2003                    saveLineMag = this.laLoSel.getLineMag();
2004                    saveEleMag = this.laLoSel.getElementMag();
2005                } catch (Exception e) {
2006                    logger.error("error reading from laLoSel", e);
2007//                    savePlace = getSavePlace();
2008//                    this.laLoSel.setPlace(savePlace);
2009//                    saveLat = getSaveLat();
2010//                    this.laLoSel.setLatitude(saveLat);
2011//                    saveLon = getSaveLon();
2012//                    this.laLoSel.setLongitude(saveLon);
2013//                    saveNumLine = getSaveNumLine();
2014//                    this.laLoSel.setNumLines(saveNumLine);
2015//                    saveNumEle = getSaveNumEle();
2016//                    this.laLoSel.setNumEles(saveNumEle);
2017//                    saveLineMag = getSaveLineMag();
2018//                    this.laLoSel.setLineMag(saveLineMag);
2019//                    saveEleMag = getSaveEleMag();
2020//                    this.laLoSel.setElementMag(saveEleMag);
2021                    this.laLoSel.setPlace(savePlace);
2022                    this.laLoSel.setLatitude(saveLat);
2023                    this.laLoSel.setLongitude(saveLon);
2024                    this.laLoSel.setNumLines(saveNumLine);
2025                    this.laLoSel.setNumEles(saveNumEle);
2026                    this.laLoSel.setLineMag(saveLineMag);
2027                    this.laLoSel.setElementMag(saveEleMag);
2028                }
2029
2030                src = replaceKey(src, PLACE_KEY, savePlace);
2031                src = removeKey(src, LINELE_KEY);
2032                if (getKey(src, LATLON_KEY).length() != 0) {
2033                    String latStr = Double.toString(saveLat);
2034                    if (latStr.length() > 8) {
2035                        latStr = latStr.substring(0,7);
2036                    }
2037                    String lonStr = Double.toString(saveLon);
2038                    if (lonStr.length() > 9) {
2039                        lonStr = lonStr.substring(0,8);
2040                    }
2041                    src = replaceKey(src, LATLON_KEY, latStr + ' ' + lonStr);
2042                }
2043                src = replaceKey(src, SIZE_KEY, saveNumLine + ' ' + saveNumEle);
2044                src = replaceKey(src, MAG_KEY, saveLineMag + ' ' + saveEleMag);
2045            }
2046
2047//            try {
2048//                AreaAdapter aa = new AreaAdapter(src, false);
2049//                logger.trace("Getting a new aa={} for src=: {}", aa, src);
2050//                areaDir = previewDir;
2051//                result = aa.getImage();
2052//            } catch (VisADException e) {
2053//                logger.error("attempting to swallow non-fatal visad exception: ", e);
2054//            } catch (java.io.IOException e) {
2055//                logger.error("attempting to swallow non-fatal I/O exception: ", e);
2056//            } finally {
2057//                putCache(src, result);
2058//                aid.setSource(src);
2059//                iml.add(aid);
2060//                setImageList(iml);
2061//                setDisplaySource(src, props);
2062//                return result;
2063//            }
2064
2065            AreaAdapter aa = new AreaAdapter(src, false);
2066            logger.trace("Getting a new aa={} for src=: {}", aa, src);
2067            areaDir = previewDir;
2068            result = aa.getImage();
2069
2070            putCache(src, result);
2071            aid.setSource(src);
2072            iml.add(aid);
2073            setImageList(iml);
2074            setDisplaySource(src, props);
2075            return result;
2076
2077        } catch (java.io.IOException ioe) {
2078            throw new VisADException("Error creating AreaAdapter", ioe);
2079        }
2080    }
2081    
2082    /**
2083     * Make a parameter name for the BandInfo
2084     *
2085     * @param bi    the BandInfo in question
2086     *
2087     * @return  a name for the parameter
2088     */
2089    
2090    private static String makeBandParam(BandInfo bi) {
2091        return new StringBuilder()
2092            .append(bi.getSensor())
2093            .append("_Band")
2094            .append(bi.getBandNumber())
2095            .append('_')
2096            .append(bi.getPreferredUnit()).toString();
2097    }
2098    
2099    private static String makeBandParam(AddeImageDescriptor descriptor) {
2100        AreaDirectory areaDir = descriptor.getDirectory();
2101        if (areaDir == null) {
2102            throw new NullPointerException("No AREA directory!");
2103        }
2104        return new StringBuilder()
2105            .append(areaDir.getSensorID())
2106            .append("_Band")
2107            .append(areaDir.getBands()[0])
2108            .append('_')
2109            .append(areaDir.getCalibrationType()).toString();
2110    }
2111
2112    /**
2113     * Get a list of descriptors from the choice and subset
2114     *
2115     * @param dataChoice  Data choice
2116     * @param subset  subsetting info
2117     *
2118     * @return  list of descriptors matching the selection
2119     */
2120    
2121    public List getDescriptors(DataChoice dataChoice, DataSelection subset) {
2122//        logger.trace("choice={} subset props={} geo={}", new Object[] { dataChoice, subset.getProperties(), subset.getGeoSelection() });
2123        int linRes = this.lineResolution;
2124        int eleRes = this.elementResolution;
2125        int newLinRes = linRes;
2126        int newEleRes = eleRes;
2127//        List<TwoFacedObject> times = getTimesFromDataSelection(subset, dataChoice);
2128        List times = getTimesFromDataSelection(subset, dataChoice);
2129        boolean usingTimeDriver = ((subset != null) && (subset.getTimeDriverTimes() != null));
2130        if (usingTimeDriver) {
2131            times = subset.getTimeDriverTimes();
2132        }
2133
2134//        if (dataChoice.getDataSelection() == null) {
2135//            logger.trace("setting datasel!");
2136//            dataChoice.setDataSelection(subset);
2137//        }
2138        if ((times == null) || times.isEmpty()) {
2139            times = imageTimes;
2140        }
2141//        List<AddeImageDescriptor> descriptors = new ArrayList<AddeImageDescriptor>(times.size());
2142        List descriptors = new ArrayList();
2143
2144        if (usingTimeDriver) {
2145            if (imageList.isEmpty()) {
2146                return imageList;
2147            }
2148            AddeImageDescriptor aid = getDescriptor(imageList.get(0));
2149            if (aid.getImageInfo() != null) {
2150                try {
2151                    AddeImageInfo aii =
2152                        (AddeImageInfo) aid.getImageInfo().clone();
2153                    // set the start and end dates
2154                    Collections.sort(times);
2155                    DateTime start = (DateTime) times.get(0);
2156                    DateTime end   = (DateTime) times.get(times.size() - 1);
2157                    // In ADDE, you can't specify something like DAY=2011256 2011257 TIME=23:45:00 01:45:00
2158                    // and expect that to be 2011256/23:45 to 2011257 01:45.  Time ranges are on a per day
2159                    // basis.  So, we see if the starting time is a different day than the ending day and if so,
2160                    // we set the start time to be 00Z on the first day an 23:59Z on the end day.
2161                    // Even worse is that for archive datasets, you can't span multiple days.  So make separate
2162                    // requests for each day.
2163                    String       startDay = UtcDate.getYMD(start);
2164                    String       endDay   = UtcDate.getYMD(end);
2165                    List<String> days     = new ArrayList<>(times.size());
2166                    if (!startDay.equals(endDay)) {
2167                        days = getUniqueDayStrings(times);
2168                    } else {
2169                        days.add(startDay);
2170                    }
2171                    Map<DateTime, AreaDirectory> dateDir = new HashMap<>(days.size());
2172                    List<DateTime> dirTimes = new ArrayList<>(days.size() * 10);
2173                    for (String day : days) {
2174                        startDay = day + " 00:00:00";
2175                        endDay   = day + " 23:59:59";
2176                        start = DateTime.createDateTime(startDay,
2177                            DateTime.DEFAULT_TIME_FORMAT);
2178                        end = UtcDate.createDateTime(endDay,
2179                            DateTime.DEFAULT_TIME_FORMAT);
2180                        aii.setStartDate(new Date((long) (start
2181                            .getValue(CommonUnit.secondsSinceTheEpoch) * 1000)));
2182                        aii.setEndDate(new Date((long) (end
2183                            .getValue(CommonUnit.secondsSinceTheEpoch) * 1000)));
2184                        // make the request for the times (AreaDirectoryList)
2185                        aii.setRequestType(AddeURL.REQ_IMAGEDIR);
2186                        AreaDirectoryList ad;
2187                        try {  // we may be asking for a date that doesn't exist
2188                            ad = new AreaDirectoryList(aii.getURLString());
2189                        } catch (AreaFileException afe) {
2190                            // If there's an error, we just ignore it.  In the
2191                            // end, the descriptor list will be empty if there is no
2192                            // data for any of the days.
2193                            continue;
2194
2195                            // TODO: This is a hack because different servers return different
2196                            // messages.  AREA and GINI servers seem to have "no images" in the
2197                            // exception message when there are no images.
2198                            //String message = afe.getMessage().toLowerCase();
2199                            //if (message.indexOf("no images") >= 0 ||
2200                            //    message.indexOf("error generating list of files") >= 0) {
2201                            //    continue;
2202                            //} else {
2203                            //    throw afe;
2204                            //}
2205
2206                        }
2207                        AreaDirectory[][] dirs = ad.getSortedDirs();
2208                        for (int d = 0; d < dirs.length; d++) {
2209                            AreaDirectory dir = dirs[d][0];
2210                            DateTime dirTime =
2211                                new DateTime(dir.getNominalTime());
2212                            dateDir.put(dirTime, dir);
2213                            dirTimes.add(dirTime);
2214                        }
2215                    }
2216                    List<DateTime> matchedTimes = selectTimesFromList(subset,
2217                        dirTimes, times);
2218                    for (DateTime dirTime : matchedTimes) {
2219                        AreaDirectory dir = dateDir.get(dirTime);
2220                        // shouldn't happen, but what the hey
2221                        if (dir == null) {
2222                            continue;
2223                        }
2224                        AddeImageInfo newaii =
2225                            (AddeImageInfo) aid.getImageInfo().clone();
2226                        newaii.setRequestType(aii.REQ_IMAGEDATA);
2227                        newaii.setStartDate(dir.getNominalTime());
2228                        newaii.setEndDate(dir.getNominalTime());
2229                        setBandInfo(dataChoice, newaii);
2230                        AddeImageDescriptor newaid =
2231                            new AddeImageDescriptor(dir,
2232                                newaii.getURLString(), newaii);
2233                        newaid.setIsRelative(false);
2234                        descriptors.add(newaid);
2235                    }
2236                } catch (CloneNotSupportedException cnse) {
2237                    logger.error("Unable to clone AddeImageInfo (aii)", cnse);
2238                } catch (VisADException vader) {
2239                    logger.error("Unable to get date values", vader);
2240                } catch (AreaFileException afe) {
2241                    logger.error("Unable to make ADDE request", afe);
2242                } catch (Exception excp) {
2243                    logger.error("Exception occurred while handling time driver", excp);
2244                }
2245                // we do this so save data local will work.  However, if
2246                // this then gets set to be the time driver, it would not
2247                // necessarily be correct
2248                imageList = descriptors;
2249                return descriptors;
2250            } else if (imageList != null) {
2251                return imageList;
2252            }
2253        }
2254
2255        int choiceBandNum = ((BandInfo) dataChoice.getId()).getBandNumber();
2256        int choiceSensorId = ((BandInfo) dataChoice.getId()).getSensor();
2257        String choicePrefUnit = ((BandInfo) dataChoice.getId()).getPreferredUnit();
2258        for (Iterator iter = times.iterator(); iter.hasNext(); ) {
2259            Object time  = iter.next();
2260            AddeImageDescriptor found = null;
2261            AddeImageDescriptor foundTimeMatch = null;
2262            if (saveImageList.isEmpty()) {
2263                saveImageList = getImageList();
2264            }
2265            for (Iterator iter2 = saveImageList.iterator(); iter2.hasNext(); ) {
2266                AddeImageDescriptor aid = getDescriptor(iter2.next());
2267                if (aid != null) {
2268                    if (aid.getIsRelative()) {
2269                        Object id;
2270                        if (time instanceof TwoFacedObject) {
2271                            id = ((TwoFacedObject)time).getId();
2272                        } else {
2273                            id = time;
2274                        }
2275                        if ((id instanceof Integer) && ((Integer)id).intValue() == aid.getRelativeIndex()) {
2276                            found = aid;
2277                            break;
2278                        }
2279                    } else {
2280                        int aidBand = aid.getDirectory().getBands()[0];
2281                        int aidSensorId = aid.getDirectory().getSensorID();
2282                        String calType = aid.getDirectory().getCalibrationType();
2283                        if (foundTimeMatch == null && aid.getImageTime().equals(time)) {
2284                            logger.trace("found time match {}", time);
2285                            foundTimeMatch = aid;
2286                        }
2287                        if (aid.getImageTime().equals(time) && choiceBandNum == aidBand && choiceSensorId == aidSensorId && choicePrefUnit.equals(calType)) {
2288                            // the problem is here!
2289                            logger.trace("found aid={} src={}", makeBandParam(aid), aid.getSource());
2290                            logger.trace("target info: param={}", dataChoice.getName());
2291                            found = aid;
2292                            break;
2293                        }
2294                    }
2295                }
2296            }
2297
2298            if (found == null && foundTimeMatch != null) {
2299                logger.trace("good enough!?");
2300                found = foundTimeMatch;
2301            }
2302            
2303            if (found != null) {
2304                try {
2305                    AddeImageDescriptor desc = new AddeImageDescriptor(found);
2306                    // Sometimes we might have a null imageinfo
2307                    if (desc.getImageInfo() != null) {
2308                        AddeImageInfo aii =
2309                            (AddeImageInfo) desc.getImageInfo().clone();
2310                        BandInfo bi = (BandInfo) dataChoice.getId();
2311                        List<BandInfo> bandInfos =
2312                            (List<BandInfo>) getProperty(PROP_BANDINFO, (Object) null);
2313                        boolean hasBand = true;
2314                        // If this data source has been changed after we have create a display 
2315                        // then the possibility exists that the bandinfo contained by the incoming
2316                        // data choice might not be valid. If it isn't then default to the first 
2317                        // one in the list
2318                        if (bandInfos != null) {
2319                            hasBand = bandInfos.contains(bi);
2320                            if(!hasBand) {
2321                            }
2322                            if(!hasBand && bandInfos.size() > 0) {
2323                                bi = bandInfos.get(0);
2324                            } else {
2325                                //Not sure what to do here.
2326                            }
2327                        }
2328                        aii.setBand("" + bi.getBandNumber());
2329                        aii.setPlaceValue("ULEFT");
2330
2331                        try {
2332                            AddeImageDescriptor newAid = new AddeImageDescriptor(aii.getURLString());
2333                            AreaDirectory newAd = newAid.getDirectory();
2334                            newLinRes = newAd.getValue(AreaFile.AD_LINERES);
2335                            newEleRes = newAd.getValue(AreaFile.AD_ELEMRES);
2336                        } catch (Exception e) {
2337                            logger.error("resetting resolution", e);
2338                        }
2339
2340                        double[][] projCoords = new double[2][2];
2341                        try {
2342                            AreaDirectory ad = desc.getDirectory();
2343                            double lin = (double) ad.getValue(AreaFile.AD_STLINE);
2344                            double ele = (double) ad.getValue(AreaFile.AD_STELEM);
2345                            aii.setLocateKey("LINELE");
2346                            aii.setLocateValue((int)lin + " " + (int)ele);
2347                            projCoords[0][0] = lin;
2348                            projCoords[1][0] = ele;
2349                            lin += (double)ad.getValue(AreaFile.AD_NUMLINES);
2350                            ele += (double)ad.getValue(AreaFile.AD_NUMELEMS);
2351                            projCoords[0][1] = lin;
2352                            projCoords[1][1] = ele;
2353                        } catch (Exception e) {
2354                            logger.error("problem with adjusting projCoords?", e);
2355                            return descriptors;
2356                        }
2357                        int lins = Math.abs((int)(projCoords[1][1] - projCoords[1][0]));
2358                        int eles = Math.abs((int)(projCoords[0][1] - projCoords[0][0]));
2359                        lins = lins*linRes/newLinRes;
2360                        if (this.lineMag > 0) {
2361                            lins *= this.lineMag;
2362                        } else {
2363                            lins /= -this.lineMag;
2364                        }
2365
2366                        eles = eles*eleRes/newEleRes;
2367
2368                        if (elementMag > 0) {
2369                            eles *= elementMag;
2370                        } else {
2371                            eles /= -elementMag;
2372                        }
2373
2374                        aii.setLines(lins);
2375                        aii.setElements(eles);
2376                        desc.setImageInfo(aii);
2377                        desc.setSource(aii.getURLString());
2378                    }
2379                    descriptors.add(desc);
2380                } catch (CloneNotSupportedException cnse) {}
2381            }
2382        }
2383        return descriptors;
2384    }
2385
2386    /**
2387     * Get the subset of the composite based on the selection
2388     *
2389     * @param choice  composite choice
2390     * @param subset  time selection
2391     *
2392     * @return subset list
2393     */
2394    
2395    private List getChoicesFromSubset(CompositeDataChoice choice,
2396                                      DataSelection subset) {
2397        List choices = choice.getDataChoices();
2398        if (subset == null) {
2399            return choices;
2400        }
2401        List times = subset.getTimes();
2402        if (times == null) {
2403            return choices;
2404        }
2405        times = TwoFacedObject.getIdList(times);
2406        List   subChoices = new ArrayList();
2407        Object firstTime  = times.get(0);
2408        if (firstTime instanceof Integer) {
2409            for (Iterator iter = times.iterator(); iter.hasNext(); ) {
2410                subChoices.add(
2411                    choices.get(((Integer) iter.next()).intValue()));
2412            }
2413        } else {  // TODO: what if they are DateTimes?
2414            subChoices.addAll(choices);
2415        }
2416        return subChoices;
2417    }
2418
2419    private List<AreaDirectory> getPreviewDirectories(final AddeImageDescriptor imageDescriptor) {
2420        List<AreaDirectory> directories = new ArrayList<AreaDirectory>(imageTimes.size());
2421        
2422        return directories;
2423    }
2424    
2425//    private AreaDirectory getPreviewDirectory(AddeImageDescriptor aid) {
2426//        AreaDirectory directory = aid.getDirectory();
2427//        AddeImageDescriptor descriptor = null;
2428//        int times = imageTimes.size();
2429//        if (times == 1) {
2430//            logger.trace("only looking for a single time; returning AreaDirectory grabbed from incoming AddeImageDescriptor. directory={}", directory);
2431//            return directory;
2432//        }
2433//        String src = aid.getSource();
2434//        logger.trace("URL for incoming AddeImageDescriptor: {}", src);
2435//        src = removeKey(src, LATLON_KEY);
2436//        src = removeKey(src, LINELE_KEY);
2437//        src = removeKey(src, PLACE_KEY);
2438//        src = removeKey(src, SIZE_KEY);
2439//        src = removeKey(src, UNIT_KEY);
2440//        src = removeKey(src, MAG_KEY);
2441//        src = removeKey(src, SPAC_KEY);
2442//        src = removeKey(src, NAV_KEY);
2443//        src = removeKey(src, AUX_KEY);
2444//        src = removeKey(src, DOC_KEY);
2445//
2446//        int maxLine = 0;
2447//        int maxEle = 0;
2448//        int imageSize = 0;
2449//        src = src.replace("imagedata", "imagedir");
2450//        boolean isRelative = aid.getIsRelative();
2451//        for (int i=0; i<times; i++) {
2452//            if (isRelative) {
2453//                src = replaceKey(src, "POS", new Integer(i).toString());
2454//            } else {
2455//                DateTime dt = (DateTime)imageTimes.get(i);
2456//                String timeStr = dt.timeString();
2457//                timeStr = timeStr.replace("Z", " ");
2458//                src = removeKey(src, "POS");
2459//                src = replaceKey(src, "TIME", timeStr + timeStr + "I");
2460//            }
2461//            try {
2462//                logger.trace("attempting to create AreaDirectoryList using src={}", src);
2463//                AreaDirectoryList dirList = new AreaDirectoryList(src);
2464//                List ad = dirList.getDirs();
2465//                AreaDirectory areaDir = (AreaDirectory)ad.get(0);
2466//                logger.trace("created AreaDirectory: {}", areaDir);
2467//                int lines = areaDir.getLines();
2468//                int eles =  areaDir.getElements();
2469//                if (imageSize < lines*eles) {
2470//                    imageSize = lines * eles;
2471//                    maxLine = lines;
2472//                    maxEle = eles;
2473//                    directory = areaDir;
2474//                    descriptor = new AddeImageDescriptor(src);
2475//                }
2476//            } catch (Exception e) {
2477//                logger.error("problem when dealing with AREA directory", e);
2478//            }
2479//        }
2480//        logger.trace("returning AreaDirectory: {}", directory);
2481//        logger.trace("could return AddeImageDescriptor:\nisRelative={}\nrelativeIndex={}\ntime={}\ndirectory={}\n", new Object[] { descriptor.getIsRelative(), descriptor.getRelativeIndex(), descriptor.getImageTime(), descriptor.getDirectory()});
2482//        return directory;
2483//    }
2484
2485    private AddeImageDescriptor getPreviewDirectory(AddeImageDescriptor aid) {
2486        AreaDirectory directory = aid.getDirectory();
2487        AddeImageDescriptor descriptor = null;
2488        int times = imageTimes.size();
2489        if (times == 1) {
2490            logger.trace("only looking for a single time; returning AreaDirectory grabbed from incoming AddeImageDescriptor. directory={}", directory);
2491//            return directory;
2492            return aid;
2493        }
2494
2495        String src = aid.getSource();
2496        logger.trace("URL for incoming AddeImageDescriptor: {}", src);
2497
2498        src = removeKey(src, LATLON_KEY);
2499        src = removeKey(src, LINELE_KEY);
2500        src = removeKey(src, PLACE_KEY);
2501        src = removeKey(src, SIZE_KEY);
2502        src = removeKey(src, UNIT_KEY);
2503        src = removeKey(src, MAG_KEY);
2504        src = removeKey(src, SPAC_KEY);
2505        src = removeKey(src, NAV_KEY);
2506        src = removeKey(src, AUX_KEY);
2507        src = removeKey(src, DOC_KEY);
2508
2509        int maxLine = 0;
2510        int maxEle = 0;
2511        int imageSize = 0;
2512        int prevSize = 0;
2513        src = src.replace("imagedata", "imagedir");
2514        boolean isRelative = aid.getIsRelative();
2515
2516        List<String> previewUrls = new ArrayList<String>(times);
2517
2518        if (isRelative) {
2519                
2520                // TJJ Mar 2015 - (1891, R14)
2521                // Iterate through list to determine relative position different
2522                // if the objects are VisAD DateTime instead of TwoFacedObject
2523                
2524                boolean isTwoFaced = false;
2525                List<?> tmpList = getAllDateTimes();
2526                if ((tmpList != null) && (! tmpList.isEmpty())) {
2527                        Object firstItem = tmpList.get(0);
2528                        if (firstItem instanceof TwoFacedObject) isTwoFaced = true;
2529                }
2530                
2531                int maxIndex;
2532                        if (isTwoFaced) {
2533                                maxIndex = Integer.MIN_VALUE;
2534                                for (TwoFacedObject tfo : (List<TwoFacedObject>) getAllDateTimes()) {
2535                                        int relativeIndex = ((Integer) tfo.getId()).intValue();
2536                                        if (relativeIndex > maxIndex) {
2537                                                maxIndex = relativeIndex;
2538                                        }
2539                                }
2540                        } else {
2541                                maxIndex = Integer.MIN_VALUE;
2542                                int relativeIndex = 0;
2543                                double maxTime = Double.MIN_VALUE;
2544                                for (DateTime dt : (List<DateTime>) getAllDateTimes()) {
2545                                        double d = dt.getValue();
2546                                        if (d > maxTime) {
2547                                                maxIndex = relativeIndex;
2548                                                maxTime = d;
2549                                        }
2550                                        relativeIndex++;
2551                                }
2552                        }
2553            
2554//            TwoFacedObject tfo = (TwoFacedObject)this.getAllDateTimes().get(0);
2555            // negate maxIndex so we can get things like POS=0 or POS=-4
2556            maxIndex = 0 - maxIndex;
2557            logger.trace("using maxIndex={}", maxIndex);
2558            src = replaceKey(src, "POS", Integer.toString(maxIndex));
2559            previewUrls.add(src);
2560            
2561        } else {
2562            for (int i = 0; i < times; i++) {
2563                DateTime dt = (DateTime)imageTimes.get(i);
2564                String timeStr = dt.timeString();
2565                timeStr = timeStr.replace("Z", " ");
2566                src = removeKey(src, "POS");
2567                src = replaceKey(src, "TIME", timeStr + timeStr + 'I');
2568                logger.trace("using time value: ", timeStr + timeStr + 'I');
2569                logger.trace("added absolute time preview url: {}", src);
2570                previewUrls.add(src);
2571            }
2572        }
2573        
2574        if (! isRelative && (previewUrls.size() == 1)) {
2575            logger.trace("only a single previewUrl; returning '{}'", previewUrls.get(0));
2576            return new AddeImageDescriptor(previewUrls.get(0));
2577        } else {
2578            logger.trace("preparing to examine previewUrls={}", previewUrls);
2579        }
2580        
2581        try {
2582            
2583            // TJJ Nov 2017
2584            // As an interim speedup, for Inq #2496, we are going to binary search the list
2585            // intstead of iterating through every single URL to make large loop times 
2586            // (e.g. 50 GOES-16 MESO images) more tolerable.
2587            
2588            if (doGeoSpeedup) {
2589
2590                int loIdx = 0;
2591                int hiIdx = previewUrls.size() - 1;
2592                boolean directionInc = true;
2593                boolean sizeMatch = false;
2594                int iterations = 0;
2595
2596                while (! sizeMatch) {
2597
2598                    String previewUrl = null;
2599                    if (directionInc) {
2600                        previewUrl = previewUrls.get(loIdx);
2601                        loIdx++;
2602                        directionInc = false;
2603                    } else {
2604                        previewUrl = previewUrls.get(hiIdx);
2605                        hiIdx--;
2606                        directionInc = true;
2607                    }
2608
2609                    // Sanity check - if we get to the middle, we just quit and use what we found
2610                    if ((loIdx > hiIdx) && (! isRelative)) break;
2611
2612                    logger.trace("attempting to create AreaDirectoryList using previewUrl={}", previewUrl);
2613                    AreaDirectoryList directoryList = new AreaDirectoryList(previewUrl);
2614                    logger.trace("created directoryList! size={}\n{}", directoryList.getDirs().size(), directoryList);
2615                    List<AreaDirectory> areaDirectories = (List<AreaDirectory>) directoryList.getDirs();
2616
2617                    for (int i = 0; i < areaDirectories.size(); i++) {
2618                        iterations++;
2619                        AreaDirectory areaDirectory = areaDirectories.get(i);
2620                        String pos = Integer.toString(0 - i);
2621                        int lines = areaDirectory.getLines();
2622                        int elements = areaDirectory.getElements();
2623                        int currentDimensions = lines * elements;
2624                        logger.trace("image pos={} lines={} elements={} lat={} lon={} startTime='{}' nominalTime='{}' currentDimensions={} areaDirectory={}", new Object[] { pos, lines, elements, areaDirectory.getCenterLatitude(), areaDirectory.getCenterLongitude(), areaDirectory.getStartTime(), areaDirectory.getNominalTime(), currentDimensions, areaDirectory });
2625                        String key = null;
2626                        if (isRelative) {
2627                            key = pos;
2628                        } else {
2629                            try {
2630                                DateTime reformatted = new DateTime(areaDirectory.getNominalTime());
2631                                key = reformatted.toString();
2632                            } catch (VisADException e) {
2633                                logger.error("could not reformat time string='"+areaDirectory.getNominalTime().toString()+"'", e);
2634                                key = areaDirectory.getNominalTime().toString();
2635                            }
2636                        }
2637                        requestIdToDirectory.put(key, areaDirectory);
2638                        if (imageSize < currentDimensions) {
2639
2640                            imageSize = currentDimensions;
2641                            maxLine = lines;
2642                            maxEle = elements;
2643                            directory = areaDirectory;
2644                            // TODO(jon): should be grabbing coord sys from chooser setting (HOW TO DO THAT!?)
2645                            String latlonString = areaDirectory.getCenterLatitude() + " " + areaDirectory.getCenterLongitude() + " E";
2646                            String largestPreviewUrl = previewUrl.replace("imagedir", "imagedata");
2647                            largestPreviewUrl = replaceKey(largestPreviewUrl, "PLACE", "CENTER");
2648                            largestPreviewUrl = replaceKey(largestPreviewUrl, "LATLON", latlonString);
2649                            Hashtable dataSourceProperties = this.getProperties();
2650                            if (dataSourceProperties.containsKey("navigation")) {
2651                                largestPreviewUrl = replaceKey(largestPreviewUrl, "NAV", dataSourceProperties.get("navigation"));
2652                            }
2653
2654                            if (isRelative) {
2655                                largestPreviewUrl = replaceKey(largestPreviewUrl, "POS", pos);
2656                            } else {
2657                                logger.trace("need to set DAY and TIME keywords for absolute times!");
2658                            }
2659
2660                            logger.trace("found new max size! old={} new={} url={}", new Object[] { imageSize, currentDimensions, largestPreviewUrl });
2661                            descriptor = new AddeImageDescriptor(areaDirectory, largestPreviewUrl);
2662
2663                        }
2664
2665                        if (prevSize == currentDimensions) {
2666                            logger.debug("Exiting Preview URL loop after " + iterations + " iterations...");
2667                            sizeMatch = true;
2668                        }
2669                        prevSize = currentDimensions;
2670                    }
2671                }
2672            } else {
2673                
2674                // Old way - go through all preview URLs looking for largest geographic coverage
2675
2676                for (String previewUrl : previewUrls) {
2677
2678                    logger.trace("attempting to create AreaDirectoryList using previewUrl={}", previewUrl);
2679                    AreaDirectoryList directoryList = new AreaDirectoryList(previewUrl);
2680                    logger.trace("created directoryList! size={}\n{}", directoryList.getDirs().size(), directoryList);
2681                    List<AreaDirectory> areaDirectories = (List<AreaDirectory>) directoryList.getDirs();
2682
2683                    for (int i = 0; i < areaDirectories.size(); i++) {
2684
2685                        AreaDirectory areaDirectory = areaDirectories.get(i);
2686                        String pos = Integer.toString(0 - i);
2687                        int lines = areaDirectory.getLines();
2688                        int elements = areaDirectory.getElements();
2689                        int currentDimensions = lines * elements;
2690                        logger.trace("image pos={} lines={} elements={} lat={} lon={} startTime='{}' nominalTime='{}' currentDimensions={} areaDirectory={}", new Object[] { pos, lines, elements, areaDirectory.getCenterLatitude(), areaDirectory.getCenterLongitude(), areaDirectory.getStartTime(), areaDirectory.getNominalTime(), currentDimensions, areaDirectory });
2691                        String key = null;
2692                        if (isRelative) {
2693                            key = pos;
2694                        } else {
2695                            try {
2696                                DateTime reformatted = new DateTime(areaDirectory.getNominalTime());
2697                                key = reformatted.toString();
2698                            } catch (VisADException e) {
2699                                logger.error("could not reformat time string='"+areaDirectory.getNominalTime().toString()+"'", e);
2700                                key = areaDirectory.getNominalTime().toString();
2701                            }
2702                        }
2703                        requestIdToDirectory.put(key, areaDirectory);
2704                        if (imageSize < currentDimensions) {
2705
2706                            imageSize = currentDimensions;
2707                            maxLine = lines;
2708                            maxEle = elements;
2709                            directory = areaDirectory;
2710                            // TODO(jon): should be grabbing coord sys from chooser setting (HOW TO DO THAT!?)
2711                            String latlonString = areaDirectory.getCenterLatitude() + " " + areaDirectory.getCenterLongitude() + " E";
2712                            String largestPreviewUrl = previewUrl.replace("imagedir", "imagedata");
2713                            largestPreviewUrl = replaceKey(largestPreviewUrl, "PLACE", "CENTER");
2714                            largestPreviewUrl = replaceKey(largestPreviewUrl, "LATLON", latlonString);
2715                            Hashtable dataSourceProperties = this.getProperties();
2716                            if (dataSourceProperties.containsKey("navigation")) {
2717                                largestPreviewUrl = replaceKey(largestPreviewUrl, "NAV", dataSourceProperties.get("navigation"));
2718                            }
2719
2720                            if (isRelative) {
2721                                largestPreviewUrl = replaceKey(largestPreviewUrl, "POS", pos);
2722                            } else {
2723                                logger.trace("need to set DAY and TIME keywords for absolute times!");
2724                            }
2725
2726                            logger.trace("found new max size! old={} new={} url={}", new Object[] { imageSize, currentDimensions, largestPreviewUrl });
2727                            descriptor = new AddeImageDescriptor(areaDirectory, largestPreviewUrl);
2728
2729                        }
2730                    }
2731                }
2732            }
2733        } catch (AreaFileException areaException) {
2734            logger.error("problem when dealing with AREA directory", areaException);
2735        }
2736//        for (int i = 0; i < times; i++) {
2737//            if (isRelative) {
2738//                src = replaceKey(src, "POS", new Integer(i).toString());
2739//            } else {
2740//                DateTime dt = (DateTime)imageTimes.get(i);
2741//                String timeStr = dt.timeString();
2742//                timeStr = timeStr.replace("Z", " ");
2743//                src = removeKey(src, "POS");
2744//                src = replaceKey(src, "TIME", timeStr + timeStr + "I");
2745//            }
2746//            // don't forget to NOT negate POS=0
2747//            try {
2748//                logger.trace("attempting to create AreaDirectoryList using src={}", src);
2749//                AreaDirectoryList dirList = new AreaDirectoryList(src);
2750//                List ad = dirList.getDirs();
2751//                AreaDirectory areaDir = (AreaDirectory)ad.get(0);
2752//                logger.trace("created AreaDirectory: {}", areaDir);
2753//                int lines = areaDir.getLines();
2754//                int eles =  areaDir.getElements();
2755//                logger.trace("image lines={} eles={} for src={}", new Object[] { lines, eles, src });
2756//                if (imageSize < lines*eles) {
2757//                    logger.trace("found new max size! old={} new={}", imageSize, lines*eles);
2758//                    imageSize = lines * eles;
2759//                    maxLine = lines;
2760//                    maxEle = eles;
2761//                    directory = areaDir;
2762//                    descriptor = new AddeImageDescriptor(src);
2763//                }
2764//            } catch (Exception e) {
2765//                logger.error("problem when dealing with AREA directory", e);
2766//            }
2767//        }
2768//        
2769
2770        logger.trace("returning AreaDirectory: {}", directory);
2771//        logger.trace("could return AddeImageDescriptor:\nisRelative={}\nrelativeIndex={}\ntime={}\ndirectory={}\n", new Object[] { descriptor.getIsRelative(), descriptor.getRelativeIndex(), descriptor.getImageTime(), descriptor.getDirectory()});
2772        return descriptor;
2773    }
2774
2775    private String getServer(String urlString) {
2776        int ix = urlString.indexOf("//") + 2;
2777        String temp = urlString.substring(ix);
2778        ix = temp.indexOf("/");
2779        String retStr = temp.substring(0, ix);
2780        return retStr;
2781    }
2782
2783    public void setDisplaySource(String src, Hashtable props) {
2784         if (!props.isEmpty()) {
2785             Enumeration propEnum = props.keys();
2786             for (int i=0; propEnum.hasMoreElements(); i++) {
2787                 String key = propEnum.nextElement().toString();
2788                 Object val = props.get(key);
2789                 if (getKey(src, key).length() != 0) {
2790                     src = replaceKey(src, key, val);
2791                 }
2792             }
2793         }
2794         this.displaySource = src;
2795         String unit = getKey(src, UNIT_KEY);
2796         if (unit.length() != 0) {
2797             sourceProps.put(UNIT_KEY.toUpperCase(), unit);
2798         }
2799    }
2800
2801    public String getDisplaySource() {
2802        return this.displaySource;
2803    }
2804
2805
2806    private float[] getLineEleResolution(AreaDirectory ad) {
2807        logger.trace("ad: {} sensor: {}", ad, ad.getSensorID());
2808        float[] res = {(float)1.0, (float)1.0};
2809        int sensor = ad.getSensorID();
2810        List lines = null;
2811        try {
2812            String buff = getUrl();
2813
2814            lines = readTextLines(buff);
2815            if (lines == null) {
2816                return res;
2817            }
2818
2819            int gotit = -1;
2820            String[] cards = StringUtil.listToStringArray(lines);
2821            logger.trace("cards: {}", cards);
2822
2823            for (int i = 0; i < cards.length; i++) {
2824                if ( ! cards[i].startsWith("Sat ")) continue;
2825                StringTokenizer st = new StringTokenizer(cards[i]," ");
2826                String temp = st.nextToken();  // throw away the key
2827                int m = st.countTokens();
2828                for (int k = 0; k < m; k++) {
2829                    int ss = Integer.parseInt(st.nextToken().trim());
2830                    if (ss == sensor) {
2831                        gotit = i;
2832                        break;
2833                    }
2834                }
2835
2836                if (gotit != -1) {
2837                    break;
2838                }
2839            }
2840
2841            if (gotit == -1) {
2842                return res;
2843            }
2844
2845            int gotSrc = -1;
2846            for (int i = gotit; i < cards.length; i++) {
2847                if (cards[i].startsWith("EndSat")) {
2848                    return res;
2849                }
2850                if (!cards[i].startsWith("B") ) {
2851                    continue;
2852                }
2853                StringTokenizer tok = new StringTokenizer(cards[i]);
2854                String str = tok.nextToken();
2855                str = tok.nextToken();
2856                Float flt = new Float(str);
2857                res[0] = flt.floatValue();
2858                str = tok.nextToken();
2859                flt = new Float(str);
2860                res[1] = flt.floatValue();
2861                return res;
2862            }
2863        } catch (Exception e) {
2864            logger.error("problem getting the line+element rez", e);
2865        }
2866        return res;
2867    }
2868
2869    /**
2870     * Read the adde text url and return the lines of text.
2871     * If unsuccessful return null.
2872     *
2873     * @param url adde url to a text file
2874     *
2875     * @return List of lines or {@code null} if in error.
2876     */
2877    
2878    protected List readTextLines(String url) {
2879        AddeTextReader reader = new AddeTextReader(url);
2880        List lines = null;
2881        if ("OK".equals(reader.getStatus())) {
2882            lines = reader.getLinesOfText();
2883        }
2884        return lines;
2885    }
2886
2887    /**
2888     * Create the first part of the ADDE request URL
2889     * 
2890     * @return ADDE URL prefix
2891     */
2892    
2893    protected String getUrl() {
2894        String str = source;
2895        str = str.replaceFirst("imagedata", "text");
2896        int indx = str.indexOf("VERSION");
2897        str = str.substring(0, indx);
2898        str = str.concat("file=SATBAND");
2899        return str;
2900    }
2901
2902    public Hashtable getSourceProps() {
2903        return this.sourceProps;
2904    }
2905
2906    public void setSourceProps(Hashtable sourceProps) {
2907        this.sourceProps = sourceProps;
2908    }
2909
2910    public String getChoiceName() {
2911        return this.choiceName;
2912    }
2913
2914    public void setChoiceName(String choiceName) {
2915        this.choiceName = choiceName;
2916    }
2917
2918    public String getSavePlace() {
2919        return this.savePlace;
2920    }
2921
2922    public void setSavePlace(String savePlace) {
2923        this.savePlace = savePlace;
2924    }
2925
2926    public double getSaveLat() {
2927        return this.saveLat;
2928    }
2929
2930    public void setSaveLat(double saveLat) {
2931        this.saveLat = saveLat;
2932    }
2933
2934    public double getSaveLon() {
2935        return this.saveLon;
2936    }
2937
2938    public void setSaveLon(double saveLon) {
2939        this.saveLon = saveLon;
2940    }
2941
2942    public int getSaveNumLine() {
2943        return this.saveNumLine;
2944    }
2945
2946    public void setSaveNumLine(int saveNumLine) {
2947        this.saveNumLine = saveNumLine;
2948    }
2949
2950    public int getSaveNumEle() {
2951        return this.saveNumEle;
2952    }
2953
2954    public void setSaveNumEle(int saveNumEle) {
2955        this.saveNumEle = saveNumEle;
2956    }
2957
2958    public int getSaveLineMag() {
2959        return this.saveLineMag;
2960    }
2961
2962    public void setSaveLineMag(int saveLineMag) {
2963        this.saveLineMag = saveLineMag;
2964    }
2965
2966    public int getSaveEleMag() {
2967        return this.saveEleMag;
2968    }
2969
2970    public void setSaveEleMag(int saveEleMag) {
2971        this.saveEleMag = saveEleMag;
2972    }
2973
2974    public String getSource() {
2975        return this.source;
2976    }
2977
2978    public void setSource(String source) {
2979        this.source = source;
2980    }
2981
2982    public boolean getShowPreview() {
2983        return this.showPreview;
2984    }
2985
2986    public void setShowPreview(boolean showPreview) {
2987        this.showPreview = showPreview;
2988    }
2989
2990    public boolean getSaveShowPreview() {
2991        return this.saveShowPreview;
2992    }
2993
2994    public void setSaveShowPreview(boolean saveShowPreview) {
2995        this.saveShowPreview = saveShowPreview;
2996    }
2997
2998    private void getSaveComponents() {
2999        saveCoordType = this.laLoSel.getCoordinateType();
3000        savePlace = this.laLoSel.getPlace();
3001        if (saveCoordType.equals(this.laLoSel.getLatLonType())) {
3002            saveLat = this.laLoSel.getLatitude();
3003            saveLon = this.laLoSel.getLongitude();
3004        }
3005        saveNumLine = this.laLoSel.getNumLines();
3006        saveNumEle = this.laLoSel.getNumEles();
3007        saveLineMag = this.laLoSel.getLineMag();
3008        saveEleMag = this.laLoSel.getElementMag();
3009    }
3010
3011    /**
3012     * Return the list of times held by the DataSelection member.
3013     *
3014     * @return  DataSelection times
3015     */
3016    
3017    public List getDateTimeSelection() {
3018//        return super.getDateTimeSelection();
3019        DataSelection s = getDataSelection();
3020        if (s == null) {
3021            logger.trace("oh no getDataSelection is null :(");
3022            return null;
3023        } else {
3024            return s.getTimes();
3025        }
3026    }
3027
3028    /**
3029     * Set the list of selected times for this data source. This is used
3030     * for XML persistence.
3031     *
3032     * @param selectedTimes   List of selected times
3033     */
3034    
3035    public void setDateTimeSelection(List selectedTimes) {
3036//        //Check to see if we need to convert the absolute times into an index list.
3037//        if (holdsDateTimes(selectedTimes) && (timesList != null)) {
3038//            selectedTimes = Misc.getIndexList(selectedTimes,
3039//                getAllDateTimes());
3040//        }
3041//        getDataSelection().setTimes(selectedTimes);
3042        super.setDateTimeSelection(selectedTimes);
3043        List selected = getDateTimeSelection();
3044        logger.trace("incoming: {} result: {}", selectedTimes, selected);
3045    }
3046
3047    protected boolean canDoProgressiveResolution() {
3048        return false;
3049    }
3050
3051    public boolean getIsProgressiveResolution() {
3052        return false;
3053    }
3054
3055    public void setIsProgressiveResolution(boolean isPG) {
3056
3057    }
3058
3059    public boolean getMatchDisplayRegion() {
3060        return false;
3061    }
3062
3063    public static class BundlePreviewSelection extends DataSelectionComponent {
3064        final String label;
3065        public BundlePreviewSelection(final String label) {
3066            super(label);
3067            this.label = label;
3068        }
3069
3070        @Override protected JComponent doMakeContents() {
3071            // TODO Auto-generated method stub
3072            JPanel panel = new JPanel();
3073            panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS));
3074            JLabel label1 = new JLabel("Area coverage has been defined by the data bundle;");
3075            JLabel label2 = new JLabel("further subsetting is not currently supported.");
3076            label1.setAlignmentX(Component.CENTER_ALIGNMENT);
3077            label2.setAlignmentX(Container.CENTER_ALIGNMENT);
3078            panel.add(label1);
3079            panel.add(label2);
3080            return panel;
3081        }
3082
3083        @Override public void applyToDataSelection(DataSelection dataSelection) {
3084        }
3085
3086        /**
3087         * Overridden to disable these dummy tabs from showing up in properties
3088         * dialog.
3089         */
3090        @Override public boolean getShowInControlProperties() {
3091            return false;
3092        }
3093    }
3094}