001/*
002 * $Id: AddeImageParameterDataSource.java,v 1.26 2011/03/30 19:14:50 jbeavers Exp $
003 *
004 * This file is part of McIDAS-V
005 *
006 * Copyright 2007-2011
007 * Space Science and Engineering Center (SSEC)
008 * University of Wisconsin - Madison
009 * 1225 W. Dayton Street, Madison, WI 53706, USA
010 * https://www.ssec.wisc.edu/mcidas
011 * 
012 * All Rights Reserved
013 * 
014 * McIDAS-V is built on Unidata's IDV and SSEC's VisAD libraries, and
015 * some McIDAS-V source code is based on IDV and VisAD source code.  
016 * 
017 * McIDAS-V is free software; you can redistribute it and/or modify
018 * it under the terms of the GNU Lesser Public License as published by
019 * the Free Software Foundation; either version 3 of the License, or
020 * (at your option) any later version.
021 * 
022 * McIDAS-V is distributed in the hope that it will be useful,
023 * but WITHOUT ANY WARRANTY; without even the implied warranty of
024 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
025 * GNU Lesser Public License for more details.
026 * 
027 * You should have received a copy of the GNU Lesser Public License
028 * along with this program.  If not, see http://www.gnu.org/licenses.
029 */
030
031package edu.wisc.ssec.mcidasv.data.adde;
032
033import java.awt.Component;
034import java.awt.Container;
035import java.awt.Dimension;
036import java.awt.event.ActionEvent;
037import java.awt.event.ActionListener;
038import java.io.File;
039import java.io.RandomAccessFile;
040import java.rmi.RemoteException;
041import java.text.SimpleDateFormat;
042import java.util.ArrayList;
043import java.util.Arrays;
044import java.util.Comparator;
045import java.util.Enumeration;
046import java.util.Hashtable;
047import java.util.Iterator;
048import java.util.List;
049import java.util.StringTokenizer;
050import java.util.TimeZone;
051import java.util.TreeMap;
052
053import javax.swing.BoxLayout;
054import javax.swing.JCheckBox;
055import javax.swing.JComponent;
056import javax.swing.JLabel;
057import javax.swing.JPanel;
058import javax.swing.JScrollPane;
059import javax.swing.JTabbedPane;
060
061import org.slf4j.Logger;
062import org.slf4j.LoggerFactory;
063
064import edu.wisc.ssec.mcidas.AREAnav;
065import edu.wisc.ssec.mcidas.AreaDirectory;
066import edu.wisc.ssec.mcidas.AreaDirectoryList;
067import edu.wisc.ssec.mcidas.AreaFile;
068import edu.wisc.ssec.mcidas.adde.AddeImageURL;
069import edu.wisc.ssec.mcidas.adde.AddeTextReader;
070
071import visad.Data;
072import visad.DateTime;
073import visad.FlatField;
074import visad.FunctionType;
075import visad.MathType;
076import visad.RealType;
077import visad.Set;
078import visad.VisADException;
079import visad.data.DataRange;
080import visad.data.mcidas.AREACoordinateSystem;
081import visad.data.mcidas.AreaAdapter;
082import visad.georef.MapProjection;
083import visad.meteorology.ImageSequence;
084import visad.meteorology.ImageSequenceImpl;
085import visad.meteorology.ImageSequenceManager;
086import visad.meteorology.SingleBandedImage;
087import visad.util.ThreadManager;
088
089import ucar.nc2.iosp.mcidas.McIDASAreaProjection;
090import ucar.unidata.data.BadDataException;
091import ucar.unidata.data.CompositeDataChoice;
092import ucar.unidata.data.DataCategory;
093import ucar.unidata.data.DataChoice;
094import ucar.unidata.data.DataSelection;
095import ucar.unidata.data.DataSelectionComponent;
096import ucar.unidata.data.DataSourceDescriptor;
097import ucar.unidata.data.DirectDataChoice;
098import ucar.unidata.data.GeoLocationInfo;
099import ucar.unidata.data.GeoSelection;
100import ucar.unidata.data.imagery.AddeImageDataSource;
101import ucar.unidata.data.imagery.AddeImageDescriptor;
102import ucar.unidata.data.imagery.AddeImageInfo;
103import ucar.unidata.data.imagery.BandInfo;
104import ucar.unidata.data.imagery.ImageDataset;
105import ucar.unidata.geoloc.LatLonPoint;
106import ucar.unidata.geoloc.ProjectionImpl;
107import ucar.unidata.idv.DisplayControl;
108import ucar.unidata.util.GuiUtils;
109import ucar.unidata.util.IOUtil;
110import ucar.unidata.util.LogUtil;
111import ucar.unidata.util.PollingInfo;
112import ucar.unidata.util.StringUtil;
113import ucar.unidata.util.ThreeDSize;
114import ucar.unidata.util.TwoFacedObject;
115import ucar.visad.Util;
116import ucar.visad.data.AreaImageFlatField;
117
118import edu.wisc.ssec.mcidasv.data.GeoLatLonSelection;
119import edu.wisc.ssec.mcidasv.data.GeoPreviewSelection;
120
121/**
122 * Abstract DataSource class for images files.
123 */
124public class AddeImageParameterDataSource extends AddeImageDataSource {
125
126    private static final Logger logger = LoggerFactory.getLogger(AddeImageParameterDataSource.class);
127
128    /**
129     * Public keys for server, group, dataset, user, project.
130     */
131    public final static String SIZE_KEY = "size";
132    public final static String PLACE_KEY = "place";
133    public final static String LATLON_KEY = "latlon";
134    public final static String LINELE_KEY = "linele";
135    public final static String MAG_KEY = "mag";
136    public final static String BAND_KEY = "band";
137    public final static String BANDINFO_KEY = "bandinfo";
138    public final static String UNIT_KEY = "unit";
139    public final static String PREVIEW_KEY = "preview";
140    public final static String SPAC_KEY = "spac";
141    public final static String NAV_KEY = "nav";
142    public final static String AUX_KEY = "aux";
143    public final static String DOC_KEY = "doc";
144    public final static String SPACING_BRIT = "1";
145    public final static String SPACING_NON_BRIT = "4";
146
147    /** The first projection we find */
148    protected ProjectionImpl sampleProjection;
149    public MapProjection sampleMapProjection;
150
151    /** list of twod categories */
152    private List twoDCategories;
153
154    /** list of 2D time series categories */
155    private List twoDTimeSeriesCategories;
156
157    /** list of twod categories */
158    private List bandCategories;
159
160    /** list of 2D time series categories */
161    private List bandTimeSeriesCategories;
162
163    /* ADDE request string */
164    private String source;
165    private String baseSource;
166
167    /* properties for this data source */
168    private Hashtable sourceProps;
169    private Hashtable selectionProps;
170
171    private int lineResolution;
172    private int elementResolution;
173    private float lRes;
174    private float eRes;
175    private int lineMag = 1;
176    private int elementMag = 1;
177
178    private GeoSelection lastGeoSelection;
179    private DataChoice lastChoice = null;
180    private Boolean showPreview = Boolean.FALSE;
181    private FlatField previewImage = null;
182    private MapProjection previewProjection;
183    private Hashtable initProps;
184
185    private AreaDirectory previewDir = null;
186    private AREAnav previewNav = null;
187    private boolean haveDataSelectionComponents = false;
188
189    private GeoPreviewSelection previewSel;
190    private GeoLatLonSelection laLoSel;
191
192    private String choiceName;
193
194    private String saveCoordType;
195    private String savePlace;
196    private double saveLat;
197    private double saveLon;
198    private int saveNumLine;
199    private int saveNumEle;
200    private int saveLineMag;
201    private int saveEleMag;
202    private Boolean saveShowPreview;
203
204    private String displaySource;
205
206    protected List<DataChoice> stashedChoices = null;
207    private List iml = new ArrayList();
208    private List saveImageList = new ArrayList();
209
210    private int previewLineRes = 1;
211    private int previewEleRes = 1;
212
213    /** Whether or not this DataSource was loaded from a bundle. */
214    private boolean fromBundle = false;
215    
216    /** Are any of the data choices based upon remote files? */
217    private boolean hasRemoteChoices = false;
218
219    public AddeImageParameterDataSource() {} 
220
221    /**
222     * Creates a {@code AddeImageParameterDataSource} with a single ADDE URL.
223     * <b>Note:</b> the URLs should point at {@literal "image"} data.
224     * 
225     * @param descriptor {@link ucar.unidata.data.DataSourceDescriptor DataSourceDescriptor} for this data source.
226     * @param image ADDE URL
227     * @param properties The properties for this data source.
228     * 
229     * @throws VisADException
230     */
231    public AddeImageParameterDataSource(DataSourceDescriptor descriptor, String image,
232                               Hashtable properties)
233            throws VisADException {
234        super(descriptor, new String[] { image }, properties);
235        logger.trace("desc={}, image={}, properties={}", new Object[] { descriptor, image, properties });
236    }
237
238    /**
239     * Create a new AddeImageParameterDataSource with an array of ADDE URL strings.
240     * <b>Note:</b> the URLs should point at {@literal "image"} data.
241     * 
242     * @param descriptor {@link ucar.unidata.data.DataSourceDescriptor DataSourceDescriptor} for this data source.
243     * @param images Array of ADDE URLs.
244     * @param properties Properties for this data source.
245     * 
246     * @throws VisADException
247     */
248    public AddeImageParameterDataSource(DataSourceDescriptor descriptor, String[] images,
249                           Hashtable properties) throws VisADException {
250        super(descriptor, images, properties);
251        logger.trace("desc={}, images={}, properties={}", new Object[] { descriptor, images, properties });
252    }
253
254    /**
255     * Creates a new {@code AddeImageParameterDataSource} with an 
256     * {@link java.util.List List} of ADDE URL strings.
257     * <b>Note:</b> the URLs should point at {@literal "image"} data.
258     * 
259     * @param descriptor {@link ucar.unidata.data.DataSourceDescriptor DataSourceDescriptor} for this data source.
260     * @param images {@code List} of ADDE URL strings.
261     * @param properties Properties for this data source.
262     * 
263     * @throws VisADException
264     */
265    public AddeImageParameterDataSource(DataSourceDescriptor descriptor, List images,
266                           Hashtable properties) throws VisADException {
267        super(descriptor, images, properties);
268        logger.trace("desc={}, images={}, properties={}", new Object[] { descriptor, images, properties });
269    }
270
271    /**
272     * Create a new AddeImageParameterDataSource with the given dataset.
273     * 
274     * @param descriptor {@link ucar.unidata.data.DataSourceDescriptor DataSourceDescriptor} for this data source.
275     * @param ids Dataset.
276     * @param properties Properties for this data source.
277     * 
278     * @throws VisADException
279     */
280    public AddeImageParameterDataSource(DataSourceDescriptor descriptor, ImageDataset ids,
281                           Hashtable properties) throws VisADException {
282        super(descriptor, ids, properties);
283        logger.trace("desc={}, ids={}, properties={}", new Object[] { descriptor, ids, properties });
284        this.sourceProps = properties;
285        if (properties.containsKey((Object)PREVIEW_KEY)) {
286            this.showPreview = (Boolean)(properties.get((Object)PREVIEW_KEY));
287            saveShowPreview = showPreview;
288        } else {
289            if (saveShowPreview != null) {
290                showPreview = saveShowPreview;
291            }
292        }
293        
294        List descs = ids.getImageDescriptors();
295        AddeImageDescriptor aid = (AddeImageDescriptor)descs.get(0);
296        this.source = aid.getSource();
297        if (this.source.contains("localhost")) {
298            AreaDirectory areaDirectory = aid.getDirectory();
299            if (!sourceProps.containsKey((Object)UNIT_KEY)) {
300                if (!sourceProps.containsKey((Object)BAND_KEY)) {
301                    String calType = areaDirectory.getCalibrationType();
302                    if (!calType.equals("RAW")) {
303                        sourceProps.put(UNIT_KEY, calType);
304                        int[] bandNums = areaDirectory.getBands();
305                        String bandString = new Integer(bandNums[0]).toString();
306                        sourceProps.put(BAND_KEY, bandString);
307                    }
308                }
309            }
310        }
311        setMag();
312        getAreaDirectory(properties);
313    }
314
315    @Override protected void propertiesChanged() {
316        logger.trace("fired");
317        super.propertiesChanged();
318    }
319
320    @Override protected boolean initDataFromPollingInfo() {
321        boolean result = super.initDataFromPollingInfo();
322        logger.trace("result={}", result);
323        return result;
324    }
325
326    @Override protected boolean isPolling() {
327        boolean result = super.isPolling();
328        logger.trace("isPolling={}", result);
329        return result;
330    }
331
332    @Override public void setPollingInfo(PollingInfo value) {
333        logger.trace("value={}", value);
334        super.setPollingInfo(value);
335    }
336
337    @Override protected boolean hasPollingInfo() {
338        boolean result = super.hasPollingInfo();
339        logger.trace("hasPollingInfo={}", result);
340        return result;
341    }
342
343    @Override public PollingInfo getPollingInfo() {
344        PollingInfo result = super.getPollingInfo();
345        logger.trace("getPollingInfo={}", result);
346        return result;
347    }
348
349    @Override public void initAfterUnpersistence() {
350        logger.trace("unbundled!");
351        super.initAfterUnpersistence();
352
353        if (this.sourceProps.containsKey(PREVIEW_KEY)) {
354            this.showPreview = (Boolean)this.sourceProps.get(PREVIEW_KEY);
355            if (this.showPreview == null) {
356                this.showPreview = Boolean.FALSE;
357            }
358            this.saveShowPreview = this.showPreview;
359        }
360        
361        this.fromBundle = true;
362        List<AddeImageDescriptor> descriptors = (List<AddeImageDescriptor>)getImageList();
363        this.source = descriptors.get(0).getSource(); // TODO: why not use the source from
364                                                      // each AddeImageDescriptor?
365        for (AddeImageDescriptor descriptor : descriptors) {
366            if (!isFromFile(descriptor)) {
367                this.hasRemoteChoices = true;
368                break;
369            }
370        }
371    }
372
373    @Override public boolean canSaveDataToLocalDisk() {
374        return true;
375    }
376
377    private Hashtable<DataChoice, DataSelection> choiceToSel = new Hashtable<DataChoice, DataSelection>();
378
379    public DataSelection getSelForChoice(final DataChoice choice) {
380        return choiceToSel.get(choice);
381    }
382    public boolean hasSelForChoice(final DataChoice choice) {
383        return choiceToSel.containsKey(choice);
384    }
385    public void putSelForChoice(final DataChoice choice, final DataSelection sel) {
386        choiceToSel.put(choice, 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    @Override protected List saveDataToLocalDisk(String prefix, Object loadId, boolean changeLinks) throws Exception {
401        logger.trace("prefix={} loadId={} changeLinks={}", new Object[] { prefix, loadId, changeLinks });
402        final List<JCheckBox> checkboxes = new ArrayList<JCheckBox>();
403        List categories = new ArrayList();
404        Hashtable catMap = new Hashtable();
405        Hashtable currentDataChoices = new Hashtable();
406
407        List displays = getIdv().getDisplayControls();
408        for (int i = 0; i < displays.size(); i++) {
409            List dataChoices = ((DisplayControl)displays.get(i)).getDataChoices();
410            if (dataChoices == null) {
411                continue;
412            }
413            List finalOnes = new ArrayList();
414            for (int j = 0; j < dataChoices.size(); j++) {
415                ((DataChoice)dataChoices.get(j)).getFinalDataChoices(finalOnes);
416            }
417            for (int dcIdx = 0; dcIdx < finalOnes.size(); dcIdx++) {
418                DataChoice dc = (DataChoice)finalOnes.get(dcIdx);
419                if (!(dc instanceof DirectDataChoice)) {
420                    continue;
421                }
422                DirectDataChoice ddc = (DirectDataChoice) dc;
423                if (ddc.getDataSource() != this) {
424                    continue;
425                }
426                currentDataChoices.put(ddc.getName(), "");
427            }
428        }
429
430        for (int i = 0; i < dataChoices.size(); i++) {
431            DataChoice dataChoice = (DataChoice) dataChoices.get(i);
432            if (!(dataChoice instanceof DirectDataChoice)) {
433                continue;
434            }
435
436            // skip over datachoices that the user has not already loaded.
437            // (but fill the "slot" with null (it's a hack to signify that 
438            // the "download" loop should skip over the data choice associated 
439            // with this slot)
440            if (!currentDataChoices.containsKey(dataChoice.getName())) {
441                checkboxes.add(null); // 
442                continue;
443            }
444
445            String label = dataChoice.getDescription();
446            if (label.length() > 30) {
447                label = label.substring(0, 29) + "...";
448            }
449            JCheckBox cbx =
450                new JCheckBox(label, 
451                              currentDataChoices.get(dataChoice.getName())
452                              != null);
453            ThreeDSize size = (ThreeDSize)dataChoice.getProperty(SIZE_KEY);
454            cbx.setToolTipText(dataChoice.getName());
455            checkboxes.add(cbx);
456            DataCategory dc = dataChoice.getDisplayCategory();
457            List comps = (List)catMap.get(dc);
458            if (comps == null) {
459                comps = new ArrayList();
460                catMap.put(dc, comps);
461                categories.add(dc);
462            }
463            comps.add(cbx);
464            comps.add(GuiUtils.filler());
465            if (size != null) {
466                JLabel sizeLabel = GuiUtils.rLabel(size.getSize() + "  ");
467                sizeLabel.setToolTipText(size.getLabel());
468                comps.add(sizeLabel);
469            } else {
470                comps.add(new JLabel(""));
471            }
472        }
473        final JCheckBox allCbx = new JCheckBox("Select All");
474        allCbx.addActionListener(new ActionListener() {
475            public void actionPerformed(ActionEvent ae) {
476                for (JCheckBox cbx : checkboxes) {
477                    if (cbx != null) {
478                        cbx.setSelected(allCbx.isSelected());
479                    }
480                }
481            }
482        });
483        List catComps = new ArrayList();
484        JTabbedPane tab = new JTabbedPane(JTabbedPane.LEFT);
485
486        for (int i = 0; i < categories.size(); i++) {
487            List comps = (List)catMap.get(categories.get(i));
488            JPanel innerPanel = GuiUtils.doLayout(comps, 3, GuiUtils.WT_NYN, GuiUtils.WT_N);
489            JScrollPane sp = new JScrollPane(GuiUtils.top(innerPanel));
490            sp.setPreferredSize(new Dimension(500, 400));
491            JPanel top = GuiUtils.right(GuiUtils.rLabel("  "));
492            JComponent inner = GuiUtils.inset(GuiUtils.topCenter(top, sp), 5);
493            tab.addTab(categories.get(i).toString(), inner);
494        }
495
496        JComponent contents = tab;
497        contents = GuiUtils.topCenter(
498            GuiUtils.inset(
499                GuiUtils.leftRight(
500                    new JLabel("Select the fields to download"),
501                    allCbx), 5), contents);
502        JLabel label = new JLabel(getNameForDataSource(this, 50, true));
503        contents = GuiUtils.topCenter(label, contents);
504        contents = GuiUtils.inset(contents, 5);
505        if (!GuiUtils.showOkCancelDialog(null, "", contents, null)) {
506            return null;
507        }
508
509        // iterate through user's selection to build list of things to download
510        List<String> realUrls = new ArrayList<String>();
511        List<AddeImageDescriptor> descriptorsToSave = new ArrayList<AddeImageDescriptor>();
512        List<BandInfo> bandInfos = (List<BandInfo>)getProperty(PROP_BANDINFO, (Object)null);
513        List<BandInfo> savedBands = new ArrayList<BandInfo>();
514        for (int i = 0; i < dataChoices.size(); i++) {
515            DataChoice dataChoice = (DataChoice)dataChoices.get(i);
516            if (!(dataChoice instanceof DirectDataChoice)) {
517                continue;
518            }
519            JCheckBox cbx = (JCheckBox)checkboxes.get(i);
520            if (cbx == null || !cbx.isSelected()) {
521                continue;
522            }
523
524            if (dataChoice.getDataSelection() == null) {
525                dataChoice.setDataSelection(getSelForChoice(dataChoice));
526            }
527            logger.trace("selected choice={} id={}", dataChoice.getName(), dataChoice.getId());
528            List<AddeImageDescriptor> descriptors = getDescriptors(dataChoice, dataChoice.getDataSelection());
529            logger.trace("descriptors={}", descriptors);
530            
531            BandInfo bandInfo;
532            Object dataChoiceId = dataChoice.getId();
533            if (dataChoiceId instanceof BandInfo) {
534                bandInfo = (BandInfo)dataChoiceId;
535            } else {
536                bandInfo = bandInfos.get(0);
537            }
538            String preferredUnit = bandInfo.getPreferredUnit();
539            List<TwoFacedObject> filteredCalUnits = new ArrayList<TwoFacedObject>();
540            for (TwoFacedObject tfo : (List<TwoFacedObject>)bandInfo.getCalibrationUnits()) {
541                if (preferredUnit.equals(tfo.getId())) {
542                    filteredCalUnits.add(tfo);
543                }
544            }
545            bandInfo.setCalibrationUnits(filteredCalUnits);
546            savedBands.add(bandInfo);
547
548            DataSelection selection = dataChoice.getDataSelection();
549            if (selection == null) {
550                if (getSelForChoice(dataChoice) != null) {
551                    selection = getSelForChoice(dataChoice);
552                } else {
553                    selection = getDataSelection();
554                }
555            }
556
557            Hashtable selectionProperties = selection.getProperties();
558//            Hashtable selectionProperties;
559//            if (selection != null) {
560//                selectionProperties = selection.getProperties();
561//            } else {
562//                DataSelection sel = this.getDataSelection();
563//                selectionProperties = new Hashtable();
564//            }
565            logger.trace("bandinfo.getUnit={} selection props={}", bandInfo.getPreferredUnit(), selectionProperties);
566            for (AddeImageDescriptor descriptor : descriptors) {
567//                AddeImageInfo aii = (AddeImageInfo)descriptor.getImageInfo().clone();
568                String src = descriptor.getSource();
569                logger.trace("src before={}", src);
570                src = replaceKey(src, AddeImageURL.KEY_UNIT, bandInfo.getPreferredUnit());
571                if (selectionProperties.containsKey(AddeImageURL.KEY_PLACE)) {
572                    src = replaceKey(src, AddeImageURL.KEY_PLACE, selectionProperties.get(AddeImageURL.KEY_PLACE));
573                }
574                if (selectionProperties.containsKey(AddeImageURL.KEY_LATLON)) {
575                    src = replaceKey(src, AddeImageURL.KEY_LINEELE, AddeImageURL.KEY_LATLON, selectionProperties.get(AddeImageURL.KEY_LATLON));
576                }
577                if (selectionProperties.containsKey(AddeImageURL.KEY_LINEELE)) {
578                    src = removeKey(src, AddeImageURL.KEY_LATLON);
579                    src = replaceKey(src, AddeImageURL.KEY_LINEELE, selectionProperties.get(AddeImageURL.KEY_LINEELE));
580                }
581                if (selectionProperties.containsKey(AddeImageURL.KEY_MAG)) {
582                    src = replaceKey(src, AddeImageURL.KEY_MAG, selectionProperties.get(AddeImageURL.KEY_MAG));
583                }
584                if (selectionProperties.containsKey(AddeImageURL.KEY_SIZE)) {
585                    src = replaceKey(src, AddeImageURL.KEY_SIZE, selectionProperties.get(AddeImageURL.KEY_SIZE));
586                }
587                logger.trace("src after={}", src);
588                descriptor.setSource(src);
589                descriptorsToSave.add(descriptor);
590            }
591//          descriptorsToSave.addAll(descriptors);
592        }
593        if (!savedBands.isEmpty()) {
594            setProperty(PROP_BANDINFO, savedBands);
595        }
596        if (descriptorsToSave.isEmpty()) {
597            return null;
598        }
599
600        //Start the load, showing the dialog
601        List<String> suffixes = new ArrayList<String>();
602        SimpleDateFormat sdf = new SimpleDateFormat("_" + DATAPATH_DATE_FORMAT);
603        sdf.setTimeZone(TimeZone.getTimeZone("GMT"));
604        for (int i = 0; i < descriptorsToSave.size(); i++) {
605            AddeImageDescriptor descriptor = descriptorsToSave.get(i);
606            AddeImageInfo aii = descriptor.getImageInfo();
607            DateTime dttm = (DateTime)timeMap.get(descriptor.getSource());
608            if (dttm != null) {
609                suffixes.add(sdf.format(ucar.visad.Util.makeDate(dttm)) + ".area");
610            } else if (aii != null) {
611                String suffix = "_Band"+aii.getBand()+"_Unit"+aii.getUnit()+"_Pos"+i+".area";
612                suffixes.add(suffix);
613                logger.trace("test suffix={}", suffix);
614            } else {
615                suffixes.add(i + ".area");
616            }
617            realUrls.add(descriptor.getSource());
618        }
619        logger.trace("urls={}", realUrls);
620        logger.trace("prefix={}", prefix);
621        logger.trace("suffixes={}", suffixes);
622        logger.trace("loadId={}", loadId);
623        List newFiles = IOUtil.writeTo(realUrls, prefix, suffixes, loadId);
624        logger.trace("files={}", newFiles);
625        if (newFiles == null) {
626            logger.trace("failed while in writeTo?");
627            return null;
628        } else {
629            logger.trace("finished writeTo!");
630        }
631        if (changeLinks) {
632            imageList = newFiles;
633        }
634
635        // write 0 as the first word
636        for (int i = 0; i < newFiles.size(); i++) {
637            try {
638                RandomAccessFile to = new RandomAccessFile((String)newFiles.get(i), "rw");
639                to.seek(0);
640                to.writeInt(0);
641                to.close();
642            } catch (Exception e) {
643                logger.error("unable to set first word to zero", e);
644            }
645        }
646
647
648//        if (geoSubset != null) {
649//            geoSubset.clearStride();
650//            geoSubset.setBoundingBox(null);
651//            if (geoSelectionPanel != null) {
652//                geoSelectionPanel.initWith(doMakeGeoSelectionPanel());
653//            }
654//        }
655
656//        List newFiles = Misc.newList(path);
657//        if (changeLinks) {
658//            //Get rid of the resolver URL
659//            getProperties().remove(PROP_RESOLVERURL);
660//            setNewFiles(newFiles);
661//        }
662//        
663        logger.trace("returning={}", newFiles);
664        return newFiles;
665    }
666
667    @Override protected String getDataPrefix() {
668        String tmp = StringUtil.replace(getName(), ' ', "");
669        tmp = StringUtil.replace(tmp, '/', "");
670        tmp = StringUtil.replace(tmp, "(AllBands)", "");
671        tmp = IOUtil.cleanFileName(tmp);
672        logger.trace("data prefix={}", tmp);
673        return tmp;
674    }
675    
676    /**
677     * A utility method that helps us deal with legacy bundles that used to
678     * have String file names as the id of a data choice.
679     *
680     * @param object     May be an AddeImageDescriptor (for new bundles) or a
681     *                   String that is converted to an image descriptor.
682     * @return The image descriptor.
683     */
684    @Override public AddeImageDescriptor getDescriptor(Object object) {
685//        logger.trace("--------------------");
686        if (object == null) {
687//            logger.trace("null obj");
688            return null;
689        }
690        if (object instanceof DataChoice) {
691            object = ((DataChoice)object).getId();
692            logger.trace("datachoice getId={}", object);
693        }
694        if (object instanceof ImageDataInfo) {
695            int index = ((ImageDataInfo) object).getIndex();
696            if (index < myDataChoices.size()) {
697                DataChoice dc = (DataChoice)myDataChoices.get(index);
698                Object tmpObject = dc.getId();
699                if (tmpObject instanceof ImageDataInfo) {
700//                    logger.trace("returning imagedatainfo");
701                    return ((ImageDataInfo)tmpObject).getAid();
702                }
703            }
704//            logger.trace("invalid idx for imagedatainfo? (idx={} vs size={})", index, myDataChoices.size());
705            return null;
706            //            return ((ImageDataInfo) object).getAid();
707        }
708
709        if (object instanceof AddeImageDescriptor) {
710//            logger.trace("already addeimagedesc! desc={}", object);
711            return (AddeImageDescriptor)object;
712        }
713        AddeImageDescriptor tmp = new AddeImageDescriptor(object.toString());
714//        logger.trace("return descriptor={}", tmp);
715//        logger.trace("--------------------");
716        return tmp;
717    }
718
719    /**
720     *  Overwrite base class  method to return the name of this class.
721     *
722     *  @return The name.
723     */
724    public String getImageDataSourceName() {
725        return "Adde Image Data Source (Parameter)";
726    }
727
728    private void setMag() {
729        Object magKey = (Object)"mag";
730        if (sourceProps.containsKey(magKey)) {
731            String magVal = (String)(sourceProps.get(magKey));
732            String[] magVals = magVal.split(" ");
733            this.lineMag = new Integer(magVals[0]).intValue();
734            this.elementMag = new Integer(magVals[1]).intValue();
735        }
736    }
737
738    private void getAreaDirectory(Hashtable properties) {
739        String addeCmdBuff = source;
740        if (addeCmdBuff.contains("BAND=")) {
741            String bandStr = getKey(addeCmdBuff, "BAND");
742            if (bandStr.length() == 0) {
743                addeCmdBuff = replaceKey(addeCmdBuff, "BAND", "1");
744            }
745        }
746        if (addeCmdBuff.contains("MAG=")) {
747            String[] segs = addeCmdBuff.split("MAG=");
748            String seg0 = segs[0];
749            String seg1 = segs[1];
750            int indx = seg1.indexOf("&");
751            seg1 = seg1.substring(indx);
752            String magString = lineMag + " " + elementMag;
753            addeCmdBuff = seg0 + "MAG=" + magString + seg1;
754        }
755        addeCmdBuff = addeCmdBuff.replace("imagedata", "imagedir");
756        AreaDirectoryList dirList = null;
757        try {
758            dirList = new AreaDirectoryList(addeCmdBuff);
759        } catch (Exception e) {
760            try {
761                List<BandInfo> bandInfos = (List<BandInfo>)getProperty(PROP_BANDINFO, (Object)null);
762                BandInfo bi = bandInfos.get(0);
763                String bandStr = new Integer(bi.getBandNumber()).toString();
764                addeCmdBuff = replaceKey(addeCmdBuff, "BAND", bandStr);
765                dirList = new AreaDirectoryList(addeCmdBuff);
766            } catch (Exception eOpen) {
767                setInError(true);
768                logger.error("problem opening AREA file", eOpen);
769            }
770        }
771
772        try {
773            List areaDirs = dirList.getDirs();
774            AreaDirectory ad = (AreaDirectory)areaDirs.get(0);
775            float[] res = getLineEleResolution(ad);
776            float resol = res[0];
777            if (this.lineMag < 0)
778                resol *= Math.abs(this.lineMag);
779            this.lineResolution = ad.getValue(11);
780            this.lRes = resol;
781            resol = res[1];
782            if (this.elementMag < 0)
783                resol *= Math.abs(this.elementMag);
784            this.elementResolution = ad.getValue(12);
785            this.eRes = resol;
786        } catch (Exception e) {
787            setInError(true);
788            logger.error("getting area directory", e);
789        }
790        baseSource = addeCmdBuff;
791    }
792
793    protected void initDataSelectionComponents(
794                   List<DataSelectionComponent> components, final DataChoice dataChoice) {
795
796        if (fromBundle && !hasRemoteChoices) {
797            components.add(new BundlePreviewSelection("Region (Disabled)"));
798            components.add(new BundlePreviewSelection("Advanced (Disabled)"));
799            return;
800        }
801
802        getDataContext().getIdv().showWaitCursor();
803        
804        boolean hasImagePreview = true;
805        if (this.showPreview == null) {
806            this.showPreview = true;
807        }
808        boolean basically = false;
809        if (this.lastChoice != null) {
810            basically = dataChoice.basicallyEquals(this.lastChoice);
811        }
812        logger.trace("dataChoice={}", dataChoice);
813        // check for comps and whether or not dataChoice is hooping right back into line
814        if (this.haveDataSelectionComponents && dataChoice.equals(this.lastChoice)) {
815            try {
816                // did the datachoice ever actually get data?
817                if (dataChoice.getDataSelection() == null) {
818                    if (!basically) {
819                        this.laLoSel = new GeoLatLonSelection(this, 
820                                         dataChoice, this.initProps, this.previewProjection,
821                                         previewDir, previewNav);
822                    }
823                    this.lineMag = this.laLoSel.getLineMag();
824                    this.elementMag = this.laLoSel.getElementMag();
825                    
826                    /* DAVEP: Force preview on. "No preview" means blank image */
827//                    this.previewSel = new GeoPreviewSelection(this, dataChoice, this.previewImage, 
828//                                     this.laLoSel, this.previewProjection,
829//                                     this.lineMag, this.elementMag, this.showPreview);
830                    this.previewSel = new GeoPreviewSelection(this, dataChoice, this.previewImage, 
831                            this.laLoSel, this.previewProjection,
832                            this.lineMag, this.elementMag, true);
833                }
834                components.add(this.previewSel);
835                components.add(this.laLoSel);
836            } catch (Exception e) {
837                logger.error("error while repeating addition of selection components", e);
838                getDataContext().getIdv().showNormalCursor();
839            }
840        } else {
841            try {
842                hasImagePreview = makePreviewImage(dataChoice);
843                if (basically) {
844                    getSaveComponents();
845                }
846            } catch (Exception e) {
847                JLabel label = new JLabel("Can't make preview image");
848                JPanel contents = GuiUtils.top(GuiUtils.inset(label, label.getText().length() + 12));
849                GuiUtils.showOkDialog(null, "No Preview Image", contents, null);
850                getDataContext().getIdv().showNormalCursor();
851                logger.error("problem creating preview image", e);
852                return;
853            }
854            this.lastChoice = dataChoice;
855            if (hasImagePreview) {
856                try {
857                    String magStr = getKey(baseSource, MAG_KEY);
858                    String saveMagStr = magStr;
859                    String[] vals = StringUtil.split(magStr, " ", 2);
860                    Integer iVal = new Integer(vals[0]);
861                    int lMag = iVal.intValue() * -1;
862                    if (lMag == -1) {
863                        lMag = 1;
864                    }
865                    iVal = new Integer(vals[1]);
866                    int eMag = iVal.intValue() * -1;
867                    if (eMag == -1) {
868                        eMag = 1;
869                    }
870                    magStr = lMag + " " + eMag;
871                    replaceKey(MAG_KEY, magStr);
872//                    String saveStr = baseSource;
873//                    if (!showPreview) {
874//                        replaceKey(SIZE_KEY, "2 2");
875//                    }
876                    AreaAdapter aa = null;
877                    AREACoordinateSystem acs = null;
878                    try {
879                        
880                        if (showPreview) {
881                                aa = new AreaAdapter(baseSource, false);
882                                this.previewImage = (FlatField)aa.getImage();
883                        }
884                        else {
885                                this.previewImage = Util.makeField(0, 1, 1, 0, 1, 1, 0, "TEMP");
886                        }
887                        
888                        AreaFile af = new AreaFile(baseSource);
889                        previewNav = af.getNavigation();
890                        AreaDirectory ad = af.getAreaDirectory();
891                        this.lineResolution = ad.getValue(11);
892                        this.elementResolution = ad.getValue(12);
893                        acs = new AREACoordinateSystem(af);
894                    } catch (Exception e) {
895                        String excp = e.toString();
896                        int indx = excp.lastIndexOf(":");
897                        String errorText = excp.substring(indx+1);
898                        JLabel label = new JLabel(errorText);
899                        JPanel contents = GuiUtils.top(GuiUtils.inset(label, label.getText().length() + 12));
900                        GuiUtils.showOkDialog(null, "Can't Make Geographical Selection Tabs", contents, null);
901                        getDataContext().getIdv().showNormalCursor();
902                        logger.error("problem creating preview image", e);
903                        return;
904                    }
905                    this.initProps = new Hashtable();
906                    Enumeration propEnum = sourceProps.keys();
907                    for (int i = 0; propEnum.hasMoreElements(); i++) {
908                        String key = propEnum.nextElement().toString();
909                        Object val = sourceProps.get(key);
910                        key = key.toUpperCase();
911                        if (val instanceof String) {
912                            String str = (String)val;
913                            val = (Object)(str.toUpperCase());
914                        }
915                        this.initProps.put(key,val);
916                    }
917                    replaceKey(MAG_KEY, saveMagStr);
918                    magStr = getKey(baseSource, MAG_KEY);
919                    vals = StringUtil.split(magStr, " ", 2);
920                    iVal = new Integer(vals[0]);
921                    lMag = iVal.intValue();
922                    iVal = new Integer(vals[1]);
923                    eMag = iVal.intValue();
924
925                    this.initProps.put("LRES", String.valueOf((this.lRes)));
926                    this.initProps.put("ERES", String.valueOf((this.eRes)));
927                    this.initProps.put("PLRES", String.valueOf((this.previewLineRes)));
928                    this.initProps.put("PERES", String.valueOf((this.previewEleRes)));
929                    this.previewProjection = (MapProjection)acs;
930
931                    String coordType = "";
932                    double coords[] = { 0.0, 0.0 };
933                    
934                    logger.trace("basically={} laLoSel==null?={}", basically, (this.laLoSel==null));
935                    if (!basically) {
936                        if (this.laLoSel != null) {
937                            coordType = this.laLoSel.getCoordinateType();
938                            if (coordType.equals(this.laLoSel.getLatLonType())) {
939                                coords[0] = this.laLoSel.getLatitude();
940                                coords[1] = this.laLoSel.getLongitude();
941                            } else {
942                                coords[0] = (double)this.laLoSel.getLine();
943                                coords[1] = (double)this.laLoSel.getElement();
944                            }
945
946                            // turns out that laLoSel is reused for datachoices
947                            // from the same source. if you don't update laLoSel's
948                            // dataChoice, it'll apply whatever data selection
949                            // you set up... to the first data choice that you
950                            // loaded! (and causing an NPE when attempting to
951                            // bundle the dataselection for the newly-selected
952                            // datachoice.
953                            this.previewSel.setDataChoice(dataChoice);
954                            this.laLoSel.setDataChoice(dataChoice);
955                            this.laLoSel.setPreviewLineRes(this.previewLineRes);
956                            this.laLoSel.setPreviewEleRes(this.previewEleRes);
957                            this.laLoSel.update(previewDir, this.previewProjection, previewNav,
958                                           coordType, coords);
959                            
960                        } else {
961                            this.laLoSel = new GeoLatLonSelection(this, 
962                                          dataChoice, this.initProps, this.previewProjection,
963                                          previewDir, previewNav);
964                            this.lineMag = this.laLoSel.getLineMag();
965                            this.elementMag = this.laLoSel.getElementMag();
966                        }
967                    } else {
968                        if (this.laLoSel != null) {
969                            this.previewSel.setDataChoice(dataChoice);
970                            this.laLoSel.setDataChoice(dataChoice);
971                        }
972                    }
973                    /* DAVEP: Force preview on. "No preview" means blank image */
974//                    this.previewSel = new GeoPreviewSelection(this, dataChoice, this.previewImage, 
975//                                     this.laLoSel, this.previewProjection,
976//                                     this.lineMag, this.elementMag, this.showPreview);
977                    this.previewSel = new GeoPreviewSelection(this, dataChoice, this.previewImage, 
978                            this.laLoSel, this.previewProjection,
979                            this.lineMag, this.elementMag, true);
980                    
981                } catch (Exception e) {
982                    logger.error("problem making selection components", e);
983                    getDataContext().getIdv().showNormalCursor();
984                }
985                this.haveDataSelectionComponents = true;
986                replaceKey(MAG_KEY, (Object)(this.lineMag + " " + this.elementMag));
987                components.add(this.previewSel);
988                components.add(this.laLoSel);
989            }
990        }
991        if (this.previewSel != null) {
992            this.previewSel.initBox();
993        }
994        getDataContext().getIdv().showNormalCursor();
995    }
996
997    /**
998     * A hook to allow this data source to add data selection components
999     * to the IDV field selector
1000     *
1001     * @param dataChoice the data choice
1002     *
1003     * @return list of components
1004     */
1005//    @Override public List<DataSelectionComponent> getDataSelectionComponents(DataChoice dataChoice) {
1006////        List<DataSelectionComponent> dataSelectionComponents = new ArrayList<DataSelectionComponent>();
1007////        initDataSelectionComponents(dataSelectionComponents, dataChoice);
1008////        return dataSelectionComponents;
1009//        return new ArrayList<DataSelectionComponent>();
1010//    }
1011    
1012    private boolean makePreviewImage(DataChoice dataChoice) {
1013
1014        getDataContext().getIdv().showWaitCursor();
1015
1016        boolean msgFlag = false;
1017        showPreview = saveShowPreview;
1018        List<BandInfo> bandInfos = (List<BandInfo>) getProperty(PROP_BANDINFO, (Object) null);
1019        BandInfo bi = null;
1020        String saveBand = getKey(source, BAND_KEY);
1021        int bandIdx = 0;
1022        List<TwoFacedObject> calList = null;
1023        try {
1024            Object dcObj = dataChoice.getId();
1025            if (dcObj instanceof BandInfo) {
1026                bi = (BandInfo) dcObj;
1027                Integer bandInt = new Integer(bandInfos.indexOf(dcObj)+1);
1028                saveBand = bandInt.toString();
1029            } else {
1030                msgFlag = true;
1031                bi = bandInfos.get(bandIdx);
1032                this.showPreview = false;
1033            }
1034            // pull out the list of cal units, we'll need for type check later...
1035            calList = bi.getCalibrationUnits();
1036            source = replaceKey(source, BAND_KEY, (Object) (bi.getBandNumber()));
1037            // if we're replacing the band, replace cal type with preferred  
1038            // type for that band
1039            source = replaceKey(source, UNIT_KEY, (Object) bi.getPreferredUnit());
1040        } catch (Exception excp) {
1041            handlePreviewImageError(1, excp);
1042        }
1043        String name = dataChoice.getName();
1044        int idx = name.lastIndexOf("_");
1045        String unit = name.substring(idx + 1);
1046
1047        // if this is not a valid cal unit (e.g. could be set to a plugin formula name)
1048        // set it to something valid
1049        boolean validCal = false;
1050        for (TwoFacedObject tfo : calList) {
1051            if (unit.equals((String) tfo.getId())) {
1052                validCal = true;
1053                break;
1054            }
1055        }
1056        if (!validCal) {
1057            unit = bi.getPreferredUnit();
1058        }
1059
1060        if (getKey(source, UNIT_KEY).length() == 0) {
1061            source = replaceKey(source, UNIT_KEY, (Object)(unit));
1062        }
1063
1064        AddeImageDescriptor aid = null;
1065        while (aid == null) {
1066            try {
1067                aid = new AddeImageDescriptor(this.source);
1068            } catch (Exception excp) {
1069                msgFlag = true;
1070                if (bandIdx > bandInfos.size()) {
1071                    return false;
1072                }
1073                bi = bandInfos.get(bandIdx);
1074                source = replaceKey(source, BAND_KEY, (Object)(bi.getBandNumber()));
1075                ++bandIdx;
1076            }
1077        }
1078        previewDir = getPreviewDirectory(aid);
1079        int eMag = 1;
1080        int lMag = 1;
1081        int eSize = 1;
1082        int lSize = 1;
1083        try {
1084            int plMag = 1;
1085            int peMag = 1;
1086            Object magKey = (Object)"mag";
1087            if (sourceProps.containsKey(magKey)) {
1088                String magVal = (String)(sourceProps.get(magKey));
1089                String[] magVals = magVal.split(" ");
1090                peMag = new Integer(magVals[0]).intValue();
1091                plMag = new Integer(magVals[1]).intValue();
1092            }
1093            double feSize = (double)previewDir.getElements();
1094            double flSize = (double)previewDir.getLines();
1095            double feMag = (double)peMag;
1096            double flMag = (double)plMag;
1097            if (feSize > flSize) {
1098                feMag = feSize/525.0;
1099                flMag = feMag * (double)plMag/(double)peMag;
1100            } else {
1101                flMag = flSize/500.0;
1102                feMag = flMag * (double)peMag/(double)plMag;
1103            }
1104            eMag = (int)Math.ceil(feMag);
1105            lMag = (int)Math.ceil(flMag);
1106        } catch(Exception excp) {
1107           handlePreviewImageError(3, excp);
1108        }
1109        if (eMag < 1) eMag = 1;
1110        if (lMag < 1) lMag = 1;
1111
1112        eSize = 525;
1113        lSize = 500;
1114        if ((baseSource == null) || msgFlag) {
1115            baseSource = source;
1116        }
1117        this.previewLineRes = lMag;
1118        this.previewEleRes = eMag;
1119        String uLStr = "0 0 F";
1120        try {
1121            int startLine = previewDir.getValue(5);
1122            int startEle = previewDir.getValue(6);
1123            uLStr = startLine + " " + startEle + " I";
1124        } catch (Exception e) {
1125        }
1126        String src = aid.getSource();
1127        
1128        src = removeKey(src, LATLON_KEY);
1129        src = replaceKey(src, LINELE_KEY, (Object)uLStr);
1130        src = replaceKey(src, PLACE_KEY, (Object)("ULEFT"));
1131        src = replaceKey(src, SIZE_KEY, (Object)(lSize + " " + eSize));
1132        src = replaceKey(src, MAG_KEY, (Object)(lMag + " " + eMag));
1133        src = replaceKey(src, BAND_KEY, (Object)(bi.getBandNumber()));
1134        src = replaceKey(src, UNIT_KEY, (Object)(unit));
1135        
1136        try {
1137            aid = new AddeImageDescriptor(src);
1138        } catch (Exception excp) {
1139            handlePreviewImageError(4, excp);
1140            src = replaceKey(src, BAND_KEY, (Object)saveBand);
1141            aid = new AddeImageDescriptor(src);
1142            src = replaceKey(src, BAND_KEY, (Object)(bi.getBandNumber()));
1143        }
1144        if (msgFlag && (!"ALL".equals(saveBand))) {
1145            src = replaceKey(src, BAND_KEY, (Object)saveBand);
1146        }
1147        baseSource = src;
1148
1149        getDataContext().getIdv().showNormalCursor();
1150
1151        return true;
1152    }
1153
1154    /**
1155     * Show the given error to the user. 
1156     *
1157     * @param excp The exception
1158     */
1159    protected void handlePreviewImageError(int flag, Exception excp) {
1160        getDataContext().getIdv().showNormalCursor();
1161        LogUtil.userErrorMessage("Error in makePreviewImage  e=" + flag + " " + excp);
1162    }
1163
1164    private String removeKey(String src, String key) {
1165        String returnString = src;
1166        key = key.toUpperCase() + '=';
1167        if (returnString.contains(key)) {
1168            String[] segs = returnString.split(key);
1169            String seg0 = segs[0];
1170            String seg1 = segs[1];
1171            int indx = seg1.indexOf("&");
1172            if (indx >= 0) {
1173                seg1 = seg1.substring(indx+1);
1174            }
1175            returnString = seg0 + seg1;
1176        }
1177        return returnString;
1178    }
1179
1180    private String replaceKey(String src, String key, Object val) {
1181        String returnString = src;
1182        // make sure we got valid key/val pair
1183        if ((key == null) || (val == null)) {
1184            return returnString;
1185        }
1186        key = key.toUpperCase() + '=';
1187        if (returnString.contains(key)) {
1188            String[] segs = returnString.split(key);
1189            String seg0 = segs[0];
1190            String seg1 = segs[1];
1191            int indx = seg1.indexOf("&");
1192            if (indx < 0) {
1193                seg1 = "";
1194            } else if (indx > 0) {
1195                seg1 = seg1.substring(indx);
1196            }
1197            returnString = seg0 + key + val + seg1;
1198        } else {
1199            returnString = returnString + '&' + key + val;
1200        }
1201        // if key is for cal units, and it was changed to BRIT,
1202        // must change the spacing key too 
1203        if ((key.equals(UNIT_KEY + "=")) && ("BRIT".equals(val))) {
1204            returnString = replaceKey(returnString, SPAC_KEY, SPAC_KEY, SPACING_BRIT);
1205        } else {
1206            returnString = replaceKey(returnString, SPAC_KEY, SPAC_KEY, SPACING_NON_BRIT); 
1207        }
1208        return returnString;
1209    }
1210
1211    private String replaceKey(String src, String oldKey, String newKey, Object val) {
1212        String returnString = src;
1213        oldKey = oldKey.toUpperCase() + '=';
1214        newKey = newKey.toUpperCase() + '=';
1215        if (returnString.contains(oldKey)) {
1216            String[] segs = returnString.split(oldKey);
1217            String seg0 = segs[0];
1218            String seg1 = segs[1];
1219            int indx = seg1.indexOf("&");
1220            if (indx < 0) {
1221                seg1 = "";
1222            } else if (indx > 0) {
1223                seg1 = seg1.substring(indx);
1224            }
1225            returnString = seg0 + newKey + val + seg1;
1226        }
1227        else {
1228            returnString = returnString + '&' + newKey + val;
1229        }
1230        return returnString;
1231    }
1232
1233    private void replaceKey(String key, Object val) {
1234        baseSource = replaceKey(baseSource, key, val);
1235    }
1236
1237    private String getKey(String src, String key) {
1238        String returnString = "";
1239        key = key.toUpperCase() + '=';
1240        if (src.contains(key)) {
1241            String[] segs = src.split(key);
1242            segs = segs[1].split("&");
1243            returnString = segs[0];
1244        }
1245        return returnString;
1246    }
1247
1248
1249    /**
1250     * Create the set of {@link ucar.unidata.data.DataChoice} that represent
1251     * the data held by this data source.  We create one top-level
1252     * {@link ucar.unidata.data.CompositeDataChoice} that represents
1253     * all of the image time steps. We create a set of children
1254     * {@link ucar.unidata.data.DirectDataChoice}, one for each time step.
1255     */
1256    public void doMakeDataChoices() {
1257        super.doMakeDataChoices();
1258        List<BandInfo> bandInfos = (List<BandInfo>)getProperty(PROP_BANDINFO, (Object)null);
1259        String name = "";
1260        if (this.choiceName != null) {
1261            name = this.choiceName;
1262        }
1263        if (name.length() != 0) {
1264            logger.trace("already have a name={}", name);
1265            return;
1266        }
1267        if (!sourceProps.containsKey(UNIT_KEY)) {
1268            logger.trace("sourceProps has no unit key={}", sourceProps);
1269            return;
1270        }
1271        BandInfo bi = null;
1272        if (sourceProps.containsKey(BAND_KEY)) {
1273            int bandProp = new Integer((String)(sourceProps.get(BAND_KEY))).intValue();
1274            int bandIndex = BandInfo.findIndexByNumber(bandProp, bandInfos);
1275            bi = (BandInfo)bandInfos.get(bandIndex);
1276            if (sourceProps.containsKey(UNIT_KEY)) {
1277                bi.setPreferredUnit((String)(sourceProps.get(UNIT_KEY)));
1278            } else {
1279                bi.setPreferredUnit("");
1280            }
1281            name = makeBandParam(bi);
1282        }
1283        else if (sourceProps.containsKey(BANDINFO_KEY)) {
1284            ArrayList al = (ArrayList)sourceProps.get(BANDINFO_KEY);
1285            bi = (BandInfo)al.get(0);
1286            name = makeBandParam(bi);
1287        }
1288        if (stashedChoices != null) {
1289            int numChoices = stashedChoices.size();
1290            for (int i = 0; i < numChoices; i++) {
1291               DataChoice choice = (DataChoice)stashedChoices.get(i);
1292               if (name.equals(choice.getName())) {
1293                   setProperty(PROP_DATACHOICENAME, choice.getName());
1294               }
1295            }
1296        }
1297    }
1298
1299    /**
1300     * Overridden so that McIDAS-V can <i>attempt</i> to return the correct
1301     * {@code DataSelection} for the current {@code DataChoice}.
1302     */
1303    @Override public DataSelection getDataSelection() {
1304        DataSelection tmp;
1305        if (this.laLoSel == null || this.choiceToSel == null || !this.choiceToSel.containsKey(this.laLoSel.getDataChoice())) {
1306            logger.trace("* idvland getDataSelection");
1307            tmp = super.getDataSelection();
1308        } else {
1309            logger.trace("* mcv getSelForChoice");
1310            tmp = this.getSelForChoice(this.laLoSel.getDataChoice());
1311        }
1312        logger.trace("return selection props={} geo={}", tmp.getProperties(), tmp.getGeoSelection());
1313        return tmp;
1314    }
1315
1316    /**
1317     * Overridden so that McIDAS-V can associate this data source's current 
1318     * {@code DataChoice} with the given {@code DataSelection}.
1319     */
1320    @Override public void setDataSelection(DataSelection s) {
1321        super.setDataSelection(s);
1322        if (this.laLoSel != null) {
1323            this.putSelForChoice(this.laLoSel.getDataChoice(), s);
1324        }
1325        logger.trace("setting selection props={} geo={}", s.getProperties(), s.getGeoSelection());
1326    }
1327    
1328//    @Override public int canShowParameter(String name) {
1329//        int result = super.canShowParameter(name);
1330//        switch (result) {
1331//            case 0: //show=yes
1332//                logger.trace("can show param={}", name);
1333//                break;
1334//            case 1: // show=hide
1335//                logger.trace("hide param={}", name);
1336//                break;
1337//            case 2: // show=no
1338//                logger.trace("no show param={}", name);
1339//                break;
1340//            default:
1341//                logger.trace("trouble for param={}", name);
1342//                break;
1343//        }
1344//        return result;
1345//
1346//    }
1347    
1348    /**
1349     * Insert the new DataChoice into the dataChoice list.
1350     *
1351     * @param choice   new choice to add
1352     */
1353    protected void addDataChoice(DataChoice choice) {
1354        logger.trace("choice={}", choice);
1355        super.addDataChoice(choice);
1356        if (stashedChoices == null) {
1357            stashedChoices = new ArrayList();
1358        }
1359        stashedChoices.add(choice);
1360    }
1361
1362
1363    /**
1364     * Initialize the {@link ucar.unidata.data.DataCategory} objects that
1365     * this data source uses. 
1366     */
1367    private void makeCategories() {
1368        twoDTimeSeriesCategories =
1369            DataCategory.parseCategories("IMAGE-2D-TIME;", false);
1370        twoDCategories = DataCategory.parseCategories("IMAGE-2D;", false);
1371        bandCategories = DataCategory.parseCategories("IMAGE-BAND;", false);
1372        bandTimeSeriesCategories =
1373            DataCategory.parseCategories("IMAGE-BAND-TIME;", false);
1374
1375    }
1376
1377    /**
1378     * Checks to see if a given {@code AddeImageDescriptor} is based upon a 
1379     * local (or remote) file.
1380     * 
1381     * <p>The check is pretty simple: is {@code descriptor.getSource()} a valid
1382     * path?
1383     * 
1384     * @param descriptor {@code AddeImageDescriptor} of questionable origins. Shouldn't be {@code null}.
1385     * 
1386     * @return {@code true} if {@code descriptor}'s source is a valid path.
1387     */
1388    public static boolean isFromFile(final AddeImageDescriptor descriptor) {
1389        return new File(descriptor.getSource()).exists();
1390    }
1391
1392    /**
1393     * Create the actual data represented by the given
1394     * {@link ucar.unidata.data.DataChoice}.
1395     *
1396     * @param dataChoice        Either the
1397     *                          {@link ucar.unidata.data.CompositeDataChoice}
1398     *                          representing all time steps or a
1399     *                          {@link ucar.unidata.data.DirectDataChoice}
1400     *                          representing a single time step.
1401     * @param category          Not really used.
1402     * @param dataSelection     Defines any time subsets.
1403     * @param requestProperties extra request properties
1404     *
1405     * @return The image or image sequence data.
1406     *
1407     * @throws RemoteException    Java RMI problem
1408     * @throws VisADException     VisAD problem
1409     */
1410    protected Data getDataInner(DataChoice dataChoice, DataCategory category,
1411                                DataSelection dataSelection,
1412                                Hashtable requestProperties)
1413            throws VisADException, RemoteException {
1414        Data img = null;
1415        iml = new ArrayList();
1416
1417        if (dataSelection == null) {
1418            return null;
1419        }
1420        setDataSelection(dataSelection);
1421
1422        GeoSelection geoSelection = dataSelection.getGeoSelection(true);
1423        if (geoSelection == null) {
1424            return null;
1425        }
1426
1427        boolean validState = geoSelection.getHasValidState();
1428        if (!validState) {
1429            return null;
1430        }
1431
1432        if (this.lastGeoSelection == null) {
1433            this.lastGeoSelection = geoSelection;
1434        }
1435
1436        this.selectionProps = dataSelection.getProperties();
1437        Enumeration propEnum = this.selectionProps.keys();
1438        for (int i = 0; propEnum.hasMoreElements(); i++) {
1439            String key = propEnum.nextElement().toString();
1440            if (key.compareToIgnoreCase(LATLON_KEY) == 0) {
1441                String val = (String)this.selectionProps.get(key);
1442                if (val.contains("NaN")) {
1443                    return img;
1444                }
1445            }
1446            if (key.compareToIgnoreCase(LINELE_KEY) == 0) {
1447                String val = (String)this.selectionProps.get(key);
1448                if (val.contains("NaN")) {
1449                    return img;
1450                }
1451            }
1452        }
1453
1454        if (this.selectionProps.containsKey("MAG")) {
1455            String str = (String)this.selectionProps.get("MAG");
1456            String[] strs = StringUtil.split(str, " ", 2);
1457            this.lineMag = new Integer(strs[0]).intValue();
1458            this.elementMag = new Integer(strs[1]).intValue();
1459        }
1460        this.choiceName = dataChoice.getName();
1461        if (this.choiceName != null) {
1462            setProperty(PROP_DATACHOICENAME, this.choiceName);
1463        }
1464        try {
1465            img = super.getDataInner(dataChoice, category, dataSelection, requestProperties);
1466        } catch (Exception e) {
1467            String displaySrc = getDisplaySource();
1468            if (displaySrc != null) {
1469                AddeImageDescriptor aid = new AddeImageDescriptor(displaySrc);
1470                dataChoice.setId((Object)aid);
1471                img = super.getDataInner(dataChoice, category, dataSelection, requestProperties);
1472            }
1473        }
1474        return img;
1475    }
1476
1477    /**
1478     * Check if the DataChoice has a BandInfo for it's Id
1479     *
1480     * @param dataChoice  choice to check
1481     *
1482     * @return true if the choice ID is a BandInfo
1483     */
1484    private boolean hasBandInfo(DataChoice dataChoice) {
1485        Object id = dataChoice.getId();
1486        return id instanceof BandInfo;
1487    }
1488
1489    /** _more_ */
1490    AreaDirectory[][] currentDirs;
1491
1492    /**
1493     * Create the  image sequence defined by the given dataChoice.
1494     *
1495     * @param dataChoice     The choice.
1496     * @param subset     any time subsets.
1497     * @return The image sequence.
1498     *
1499     * @throws RemoteException    Java RMI problem
1500     * @throws VisADException     VisAD problem
1501     */
1502    protected ImageSequence makeImageSequence(DataChoice dataChoice,
1503            DataSelection subset)
1504            throws VisADException, RemoteException {
1505
1506//        if (dataChoice.getDataSelection() == null) {
1507//            dataChoice.setDataSelection(subset);
1508//        }
1509        Hashtable subsetProperties = subset.getProperties();
1510        Enumeration propEnum = subsetProperties.keys();
1511        int numLines = 0;
1512        int numEles = 0;
1513        for (int i=0; propEnum.hasMoreElements(); i++) {
1514            String key = propEnum.nextElement().toString();
1515            if (key.compareToIgnoreCase(SIZE_KEY) == 0) {
1516                String sizeStr = (String)(subsetProperties.get(key));
1517                String[] vals = StringUtil.split(sizeStr, " ", 2);
1518                Integer iVal = new Integer(vals[0]);
1519                numLines = iVal.intValue();
1520                iVal = new Integer(vals[1]);
1521                numEles = iVal.intValue();
1522                break;
1523            }
1524        }
1525
1526        if (sampleMapProjection == null) {
1527            String addeCmdBuff = baseSource;
1528            AreaFile af = null;
1529            try {
1530                af = new AreaFile(addeCmdBuff);
1531            } catch (Exception eOpen) {
1532                logger.error("could not open area file: {}", eOpen);
1533                setInError(true);
1534                throw new BadDataException("Opening area file: " + eOpen.getMessage());
1535            }
1536            try {
1537                McIDASAreaProjection map = new McIDASAreaProjection(af);
1538                AREACoordinateSystem acs = new AREACoordinateSystem(af);
1539                sampleMapProjection = (MapProjection)acs;
1540                sampleProjection = map;
1541            } catch (Exception e) {
1542                logger.error("making area projection: {}", e);
1543                setInError(true);
1544                throw new BadDataException("Making area projection: " + e.getMessage());
1545            }
1546        }
1547        AREACoordinateSystem macs = (AREACoordinateSystem)sampleMapProjection;
1548        int[] dirBlk = macs.getDirBlock();
1549        if (numLines == 0) {
1550            double elelin[][] = new double[2][2];
1551            double latlon[][] = new double[2][2];
1552            GeoSelection gs = subset.getGeoSelection();
1553            GeoLocationInfo gli = gs.getBoundingBox();
1554            if ((gli == null) && (lastGeoSelection != null)) {
1555                subset.setGeoSelection(lastGeoSelection);
1556                gs = lastGeoSelection;
1557                gli = gs.getBoundingBox();
1558            }
1559            LatLonPoint llp = gli.getUpperLeft();
1560            latlon[0][0] = llp.getLatitude();
1561            latlon[1][0] = llp.getLongitude();
1562            llp = gli.getLowerRight();
1563            latlon[0][1] = llp.getLatitude();
1564            latlon[1][1] = llp.getLongitude();
1565            elelin = macs.fromReference(latlon);
1566            numLines = (int)(Math.abs(elelin[1][0] - elelin[1][1]))*dirBlk[11];
1567            numEles = (int)(Math.abs(elelin[0][1] - elelin[0][0]))*dirBlk[12];
1568        }
1569
1570        try {
1571            List descriptorsToUse = new ArrayList();
1572            if (hasBandInfo(dataChoice)) {
1573                descriptorsToUse = getDescriptors(dataChoice, subset);
1574            } else {
1575                List choices = (dataChoice instanceof CompositeDataChoice)
1576                               ? getChoicesFromSubset(
1577                                   (CompositeDataChoice) dataChoice, subset)
1578                               : Arrays.asList(new DataChoice[] {
1579                                   dataChoice });
1580                for (Iterator iter = choices.iterator(); iter.hasNext(); ) {
1581                    DataChoice          subChoice = (DataChoice) iter.next();
1582                    AddeImageDescriptor aid =
1583                        getDescriptor(subChoice.getId());
1584                    if (aid == null) {
1585                        continue;
1586                    }
1587                    DateTime dttm = aid.getImageTime();
1588                    if ((subset != null) && (dttm != null)) {
1589                        List times = getTimesFromDataSelection(subset,
1590                                         dataChoice);
1591                        if ((times != null) && (times.indexOf(dttm) == -1)) {
1592                            continue;
1593                        }
1594                    }
1595                    descriptorsToUse.add(aid);
1596                }
1597            }
1598
1599            if (descriptorsToUse.size() == 0) {
1600                return null;
1601            }
1602            AddeImageInfo biggestPosition = null;
1603            int           pos             = 0;
1604            boolean       anyRelative     = false;
1605            // Find the descriptor with the largets position
1606            for (Iterator iter =
1607                    descriptorsToUse.iterator(); iter.hasNext(); ) {
1608                AddeImageDescriptor aid = (AddeImageDescriptor) iter.next();
1609                if (aid.getIsRelative()) {
1610                    anyRelative = true;
1611                }
1612                AddeImageInfo       aii = aid.getImageInfo();
1613
1614                //Are we dealing with area files here?
1615                if (aii == null) {
1616                    break;
1617                }
1618
1619                //Check if this is absolute time
1620                if ((aii.getStartDate() != null)
1621                        || (aii.getEndDate() != null)) {
1622                    biggestPosition = null;
1623                    break;
1624                }
1625                if ((biggestPosition == null)
1626                    || (Math.abs(aii.getDatasetPosition()) > pos)) {
1627                    pos             = Math.abs(aii.getDatasetPosition());
1628                    biggestPosition = aii;
1629                }
1630            }
1631
1632            if (getCacheDataToDisk() && anyRelative
1633                && (biggestPosition != null)) {
1634                biggestPosition.setRequestType(AddeImageInfo.REQ_IMAGEDIR);
1635                AreaDirectoryList adl =
1636                    new AreaDirectoryList(biggestPosition.getURLString());
1637                biggestPosition.setRequestType(AddeImageInfo.REQ_IMAGEDATA);
1638                currentDirs = adl.getSortedDirs();
1639            } else {
1640                currentDirs = null;
1641            }
1642
1643            ThreadManager threadManager =
1644                new ThreadManager("image data reading");
1645            final ImageSequenceManager sequenceManager =
1646                new ImageSequenceManager();
1647            int           cnt      = 1;
1648            DataChoice    parent   = dataChoice.getParent();
1649            final List<SingleBandedImage> images =
1650                new ArrayList<SingleBandedImage>();
1651            MathType rangeType = null;
1652            for (Iterator iter =
1653                    descriptorsToUse.iterator(); iter.hasNext(); ) {
1654                final AddeImageDescriptor aid =
1655                    (AddeImageDescriptor) iter.next();
1656                if (currentDirs != null) {
1657                    int idx =
1658                        Math.abs(aid.getImageInfo().getDatasetPosition());
1659                    if (idx >= currentDirs.length) {
1660                        continue;
1661                    }
1662                }
1663
1664                String label = "";
1665                if (parent != null) {
1666                    label = label + parent.toString() + ' ';
1667                } else {
1668                    DataCategory displayCategory =
1669                        dataChoice.getDisplayCategory();
1670                    if (displayCategory != null) {
1671                        label = label + displayCategory + ' ';
1672                    }
1673                }
1674                label = label + dataChoice.toString();
1675                final String readLabel = "Time: " + (cnt++) + '/'
1676                    + descriptorsToUse.size() + ' '
1677                    + label;
1678
1679                String src = aid.getSource();
1680                if (!isFromFile(aid)) {
1681                    try {
1682                        src = replaceKey(src, LINELE_KEY, (Object)("1 1"));
1683                        String sizeString = "10 10";
1684                        src = replaceKey(src, SIZE_KEY, (Object)(sizeString));
1685                        String name = dataChoice.getName();
1686                        int idx = name.lastIndexOf('_');
1687                        String unit = name.substring(idx+1);
1688                        if (getKey(src, UNIT_KEY).length() == 0) {
1689                            src = replaceKey(src, UNIT_KEY, (Object)(unit));
1690                        }
1691                        int lSize = numLines;
1692                        int eSize = numEles;
1693                        sizeString = lSize + " " + eSize;
1694                        src = replaceKey(src, SIZE_KEY, (Object)(sizeString));
1695                        src = replaceKey(src, MAG_KEY, (Object)(this.lineMag + " " + this.elementMag));
1696                        aid.setSource(src);
1697                    } catch (Exception exc) {
1698                        super.makeImageSequence(dataChoice, subset);
1699                    }
1700                }
1701
1702                SingleBandedImage image = makeImage(aid, rangeType, true,
1703                                                    readLabel, subset);
1704                if (image != null) {
1705                    if(rangeType==null) {
1706                        rangeType = ((FunctionType) image.getType()).getRange();
1707                    }
1708                    synchronized (images) {
1709                        images.add(image);
1710                    }
1711                }
1712            }
1713
1714            TreeMap imageMap = new TreeMap();
1715            for (SingleBandedImage image : images) {
1716                imageMap.put(image.getStartTime(), image);
1717            }
1718            List<SingleBandedImage> sortedImages =
1719                (List<SingleBandedImage>) new ArrayList(imageMap.values());
1720            if ((sortedImages.size() > 0)
1721                    && (sortedImages.get(0) instanceof AreaImageFlatField)) {
1722                DataRange[] sampleRanges = null;
1723                Set domainSet = null;
1724                for (SingleBandedImage sbi : sortedImages) {
1725                    AreaImageFlatField aiff = (AreaImageFlatField) sbi;
1726                    sampleRanges = aiff.getRanges(true);
1727                    if (domainSet == null) {
1728                        domainSet = aiff.getDomainSet();
1729                    }
1730                    if ((sampleRanges != null) && (sampleRanges.length > 0)) {
1731                        for (int rangeIdx = 0; rangeIdx < sampleRanges.length;
1732                                rangeIdx++) {
1733                            DataRange r = sampleRanges[rangeIdx];
1734                            if (Double.isInfinite(r.getMin())
1735                                || Double.isInfinite(r.getMax())) {
1736                                sampleRanges = null;
1737                                break;
1738                            }
1739                        }
1740                    }
1741                    if (sampleRanges != null) {
1742                        break;
1743                    }
1744                }
1745
1746                if (sampleRanges != null) {
1747                    for (SingleBandedImage sbi : sortedImages) {
1748                        AreaImageFlatField aiff = (AreaImageFlatField) sbi;
1749                        aiff.setSampleRanges(sampleRanges);
1750                        aiff.setDomainIfNeeded(domainSet);
1751                    }
1752                }
1753            }
1754
1755            SingleBandedImage[] imageArray =
1756                (SingleBandedImage[]) sortedImages.toArray(
1757                    new SingleBandedImage[sortedImages.size()]);
1758            FunctionType imageFunction =
1759                (FunctionType) imageArray[0].getType();
1760            FunctionType ftype = new FunctionType(RealType.Time,
1761                                     imageFunction);
1762            return new ImageSequenceImpl(ftype, imageArray);
1763        } catch (Exception exc) {
1764            throw new ucar.unidata.util.WrapperException(exc);
1765        }
1766
1767    }
1768
1769    /**
1770     * Create the single image defined by the given {@link ucar.unidata.data.imagery.AddeImageDescriptor AddeImageDescriptor}.
1771     *
1772     * @param aid Holds image directory and location of the desired image.
1773     * @param rangeType {@literal "rangeType"} to use (if non-{@code null}).
1774     * @param fromSequence _more_
1775     * @param readLabel 
1776     * @param subset geographical subsetting info
1777     *
1778     * @return The data.
1779     *
1780     * @throws RemoteException Java RMI problem
1781     * @throws VisADException VisAD problem
1782     */
1783    private SingleBandedImage makeImage(AddeImageDescriptor aid,
1784                                        MathType rangeType,
1785                                        boolean fromSequence, 
1786                                        String readLabel, DataSelection subset)
1787            throws VisADException, RemoteException {
1788        
1789        if (aid == null) {
1790            return null;
1791        }
1792
1793        logger.trace("incoming src={} readLabel={}", aid.getSource(), readLabel);
1794        String src = aid.getSource();
1795
1796        Hashtable props = subset.getProperties();
1797        // it only makes sense to set the following properties for things
1798        // coming from an ADDE server
1799        if (!isFromFile(aid)) {
1800            if (props.containsKey("PLACE")) {
1801                src = replaceKey(src, "PLACE", props.get("PLACE"));
1802            }
1803            if (props.containsKey("LATLON")) { 
1804                src = replaceKey(src, "LINELE", "LATLON", props.get("LATLON"));
1805            }
1806            if (props.containsKey("LINELE")) {
1807                src = removeKey(src, "LATLON");
1808                src = replaceKey(src, "LINELE", props.get("LINELE"));
1809            }
1810            if (props.containsKey("MAG")) {
1811                src = replaceKey(src, "MAG", props.get("MAG"));
1812            }
1813        }
1814        aid.setSource(src);
1815
1816        SingleBandedImage result;
1817//        SingleBandedImage result = (SingleBandedImage)getCache(src);
1818//        if (result != null) {
1819//            setDisplaySource(src, props);
1820//            return result;
1821//        }
1822
1823        //For now handle non adde urls here
1824        try {
1825            AddeImageInfo aii = aid.getImageInfo();
1826            AreaDirectory areaDir = null;
1827            try {
1828                if (aii != null) {
1829                    logger.trace("imageinfo={}", aii.toString());
1830                    if (currentDirs != null) {
1831                        int pos = Math.abs(aii.getDatasetPosition());
1832                        int band = 0;
1833                        String bandString = aii.getBand();
1834                        if ((bandString != null) && !aii.ALL.equals(bandString)) {
1835                            band = new Integer(bandString).intValue();
1836                        }
1837                        // TODO: even though the band is non-zero we might only 
1838                        // get back one band
1839                        band = 0;
1840                        areaDir = currentDirs[currentDirs.length - pos - 1][band];
1841                    } else {
1842                        // If its absolute time then just use the AD from the descriptor
1843                        if ((aii.getStartDate() != null) || (aii.getEndDate() != null)) {
1844                            areaDir = aid.getDirectory();
1845                        } else {
1846                        }
1847                    }
1848                } else {
1849                    logger.trace("uh oh");
1850                }
1851            } catch (Exception exc) {
1852                LogUtil.printMessage("error looking up area dir");
1853                logger.error("error looking up area dir", exc);
1854                return null;
1855            }
1856
1857            if (areaDir == null) {
1858                areaDir = aid.getDirectory();
1859            }
1860
1861            if (!getCacheDataToDisk()) {
1862                areaDir = null;
1863            }
1864
1865            if (!fromSequence || (aid.getIsRelative() && (currentDirs == null))) {
1866                areaDir = null;
1867            }
1868
1869            if (areaDir != null) {
1870                int hash = ((aii != null)
1871                            ? aii.getURLString().hashCode()
1872                            : areaDir.hashCode());
1873                if (rangeType == null) {
1874                    result = AreaImageFlatField.createImmediate(aid, readLabel);
1875                } else {
1876                    //Else, pass in the already created range type
1877                    result  = AreaImageFlatField.create(aid, areaDir, rangeType, readLabel);
1878                }
1879            } else {
1880                src = aid.getSource();
1881                try {
1882                    savePlace = this.laLoSel.getPlace();
1883                    saveLat = this.laLoSel.getLatitude();
1884                    saveLon = this.laLoSel.getLongitude();
1885                    saveNumLine = this.laLoSel.getNumLines();
1886                    saveNumEle = this.laLoSel.getNumEles();
1887                    saveLineMag = this.laLoSel.getLineMag();
1888                    saveEleMag = this.laLoSel.getElementMag();
1889                } catch (Exception e) {
1890                    logger.error("error reading from laLoSel", e);
1891//                    savePlace = getSavePlace();
1892//                    this.laLoSel.setPlace(savePlace);
1893//                    saveLat = getSaveLat();
1894//                    this.laLoSel.setLatitude(saveLat);
1895//                    saveLon = getSaveLon();
1896//                    this.laLoSel.setLongitude(saveLon);
1897//                    saveNumLine = getSaveNumLine();
1898//                    this.laLoSel.setNumLines(saveNumLine);
1899//                    saveNumEle = getSaveNumEle();
1900//                    this.laLoSel.setNumEles(saveNumEle);
1901//                    saveLineMag = getSaveLineMag();
1902//                    this.laLoSel.setLineMag(saveLineMag);
1903//                    saveEleMag = getSaveEleMag();
1904//                    this.laLoSel.setElementMag(saveEleMag);
1905                    this.laLoSel.setPlace(savePlace);
1906                    this.laLoSel.setLatitude(saveLat);
1907                    this.laLoSel.setLongitude(saveLon);
1908                    this.laLoSel.setNumLines(saveNumLine);
1909                    this.laLoSel.setNumEles(saveNumEle);
1910                    this.laLoSel.setLineMag(saveLineMag);
1911                    this.laLoSel.setElementMag(saveEleMag);
1912                }
1913
1914                src = replaceKey(src, PLACE_KEY, savePlace);
1915                src = removeKey(src, LINELE_KEY);
1916                if (getKey(src, LATLON_KEY).length() != 0) {
1917                    String latStr = Double.toString(saveLat);
1918                    if (latStr.length() > 8) {
1919                        latStr = latStr.substring(0,7);
1920                    }
1921                    String lonStr = Double.toString(saveLon);
1922                    if (lonStr.length() > 9) {
1923                        lonStr = lonStr.substring(0,8);
1924                    }
1925                    src = replaceKey(src, LATLON_KEY, latStr + ' ' + lonStr);
1926                }
1927                src = replaceKey(src, SIZE_KEY, saveNumLine + ' ' + saveNumEle);
1928                src = replaceKey(src, MAG_KEY, saveLineMag + ' ' + saveEleMag);
1929            }
1930
1931            logger.trace("wtf: {}", src);
1932            AreaAdapter aa = new AreaAdapter(src, false);
1933            areaDir = previewDir;
1934            result = aa.getImage();
1935            putCache(src, result);
1936            aid.setSource(src);
1937            iml.add(aid);
1938            setImageList(iml);
1939            setDisplaySource(src, props);
1940            return result;
1941        } catch (java.io.IOException ioe) {
1942            throw new VisADException("Creating AreaAdapter - " + ioe);
1943        }
1944    }
1945    
1946    /**
1947     * Make a parmeter name for the BandInfo
1948     *
1949     * @param bi    the BandInfo in question
1950     *
1951     * @return  a name for the parameter
1952     */
1953    private static String makeBandParam(BandInfo bi) {
1954        return new StringBuilder()
1955            .append(bi.getSensor())
1956            .append("_Band")
1957            .append(bi.getBandNumber())
1958            .append('_')
1959            .append(bi.getPreferredUnit()).toString();
1960    }
1961    
1962    private static String makeBandParam(AddeImageDescriptor descriptor) {
1963        AreaDirectory areaDir = descriptor.getDirectory();
1964        if (areaDir == null) {
1965            throw new NullPointerException("No AREA directory!");
1966        }
1967        return new StringBuilder()
1968            .append(areaDir.getSensorID())
1969            .append("_Band")
1970            .append(areaDir.getBands()[0])
1971            .append('_')
1972            .append(areaDir.getCalibrationType()).toString();
1973    }
1974
1975    /**
1976     * Get the object that we use to display relative time. Relative time is defined
1977     * using an integer index, 0...n. We don't want to show the actual integer.
1978     * Rather we want to show "Third most recent", "Fourth most recent", etc.
1979     *
1980     * @param aid The image descriptor
1981     * @return The object that represents the relative time index of the aid
1982     */
1983    private Object getRelativeTimeObject(AddeImageDescriptor aid) {
1984        return new TwoFacedObject(aid.toString(),
1985                                  new Integer(aid.getRelativeIndex()));
1986    }
1987
1988    /**
1989     * Sort the list of data choices on their time
1990     *
1991     * @param choices The data choices
1992     *
1993     * @return The data choices sorted
1994     */
1995    private List sortChoices(List choices) {
1996        Object[]   choicesArray = choices.toArray();
1997        Comparator comp         = new Comparator() {
1998            public int compare(Object o1, Object o2) {
1999                AddeImageDescriptor aid1 = getDescriptor(o1);
2000                AddeImageDescriptor aid2 = getDescriptor(o2);
2001                if ((aid1 == null) || (aid2 == null)) {
2002                    return -1;
2003                }
2004                if (aid1.getIsRelative()) {
2005                    if (aid1.getRelativeIndex() < aid2.getRelativeIndex()) {
2006                        return 0;
2007                    } else if (aid1.getRelativeIndex()
2008                               == aid2.getRelativeIndex()) {
2009                        return 1;
2010                    }
2011                    return -1;
2012                }
2013                return aid1.getImageTime().compareTo(aid2.getImageTime());
2014            }
2015        };
2016        Arrays.sort(choicesArray, comp);
2017        return new ArrayList(Arrays.asList(choicesArray));
2018
2019    }
2020
2021    /**
2022     * Get a list of descriptors from the choice and subset
2023     *
2024     * @param dataChoice  Data choice
2025     * @param subset  subsetting info
2026     *
2027     * @return  list of descriptors matching the selection
2028     */
2029    public List getDescriptors(DataChoice dataChoice, DataSelection subset) {
2030//        logger.trace("choice={} subset props={} geo={}", new Object[] { dataChoice, subset.getProperties(), subset.getGeoSelection() });
2031        int linRes = this.lineResolution;
2032        int eleRes = this.elementResolution;
2033        int newLinRes = linRes;
2034        int newEleRes = eleRes;
2035        List times = getTimesFromDataSelection(subset, dataChoice);
2036//        if (dataChoice.getDataSelection() == null) {
2037//            logger.trace("setting datasel!");
2038//            dataChoice.setDataSelection(subset);
2039//        }
2040        if ((times == null) || times.isEmpty()) {
2041            times = imageTimes;
2042        }
2043        List descriptors = new ArrayList();
2044        Object choiceId = dataChoice.getId();
2045//        if (choiceId instanceof BandInfo) {
2046//            
2047//        }
2048        int choiceBandNum = ((BandInfo)dataChoice.getId()).getBandNumber();
2049        int choiceSensorId = ((BandInfo)dataChoice.getId()).getSensor();
2050        String choicePrefUnit = ((BandInfo)dataChoice.getId()).getPreferredUnit();
2051        for (Iterator iter = times.iterator(); iter.hasNext(); ) {
2052            Object time  = iter.next();
2053            AddeImageDescriptor found = null;
2054            AddeImageDescriptor foundTimeMatch = null;
2055            if (saveImageList.isEmpty()) {
2056                saveImageList = getImageList();
2057            }
2058            for (Iterator iter2 = saveImageList.iterator(); iter2.hasNext(); ) {
2059                AddeImageDescriptor aid = getDescriptor(iter2.next());
2060                if (aid != null) {
2061                    if (aid.getIsRelative()) {
2062                        Object id;
2063                        if (time instanceof TwoFacedObject) {
2064                            id = ((TwoFacedObject)time).getId();
2065                        } else {
2066                            id = time;
2067                        }
2068                        if ((id instanceof Integer) && ((Integer)id).intValue() == aid.getRelativeIndex()) {
2069                            found = aid;
2070                            break;
2071                        }
2072                    } else {
2073                        int aidBand = aid.getDirectory().getBands()[0];
2074                        int aidSensorId = aid.getDirectory().getSensorID();
2075                        String calType = aid.getDirectory().getCalibrationType();
2076                        if (foundTimeMatch == null && aid.getImageTime().equals(time)) {
2077                            logger.trace("found time match {}", time);
2078                            foundTimeMatch = aid;
2079                        }
2080                        if (aid.getImageTime().equals(time) && choiceBandNum == aidBand && choiceSensorId == aidSensorId && choicePrefUnit.equals(calType)) {
2081                            // the problem is here!
2082                            logger.trace("found aid={} src={}", makeBandParam(aid), aid.getSource());
2083                            logger.trace("target info: param={}", dataChoice.getName());
2084                            found = aid;
2085                            break;
2086                        }
2087                    }
2088                }
2089            }
2090
2091            if (found == null && foundTimeMatch != null) {
2092                logger.trace("good enough!?");
2093                found = foundTimeMatch;
2094            }
2095            
2096            if (found != null) {
2097                try {
2098                    AddeImageDescriptor desc = new AddeImageDescriptor(found);
2099                    //Sometimes we might have a null imageinfo
2100                    if(desc.getImageInfo()!=null) {
2101                        AddeImageInfo aii =
2102                            (AddeImageInfo) desc.getImageInfo().clone();
2103                        BandInfo bi = (BandInfo) dataChoice.getId();
2104                        List<BandInfo> bandInfos =
2105                            (List<BandInfo>) getProperty(PROP_BANDINFO, (Object) null);
2106                        boolean hasBand = true;
2107                        //If this data source has been changed after we have create a display 
2108                        //then the possibility exists that the bandinfo contained by the incoming
2109                        //data choice might not be valid. If it isn't then default to the first 
2110                        //one in the list
2111                        if(bandInfos != null) {
2112                            hasBand = bandInfos.contains(bi);
2113                            if(!hasBand) {
2114                            }
2115                            if(!hasBand && bandInfos.size() > 0) {
2116                                bi = bandInfos.get(0);
2117                            } else {
2118                                //Not sure what to do here.
2119                            }
2120                        }
2121                        aii.setBand("" + bi.getBandNumber());
2122                        aii.setPlaceValue("ULEFT");
2123
2124                        try {
2125                            AddeImageDescriptor newAid = new AddeImageDescriptor(aii.getURLString());
2126                            AreaDirectory newAd = newAid.getDirectory();
2127                            newLinRes = newAd.getValue(11);
2128                            newEleRes = newAd.getValue(12);
2129                        } catch (Exception e) {
2130                            logger.error("resetting resolution", e);
2131                        }
2132
2133                        double[][] projCoords = new double[2][2];
2134                        try {
2135                            AreaDirectory ad = desc.getDirectory();
2136                            double lin = (double)ad.getValue(5);
2137                            double ele = (double)ad.getValue(6);
2138                            aii.setLocateKey("LINELE");
2139                            aii.setLocateValue((int)lin + " " + (int)ele);
2140                            projCoords[0][0] = lin;
2141                            projCoords[1][0] = ele;
2142                            lin += (double)ad.getValue(8);
2143                            ele += (double)ad.getValue(9);
2144                            projCoords[0][1] = lin;
2145                            projCoords[1][1] = ele;
2146                        } catch (Exception e) {
2147                            logger.error("problem with adjusting projCoords?", e);
2148                            return descriptors;
2149                        }
2150                        int lins = Math.abs((int)(projCoords[1][1] - projCoords[1][0]));
2151                        int eles = Math.abs((int)(projCoords[0][1] - projCoords[0][0]));
2152                        lins = lins*linRes/newLinRes;
2153                        if (this.lineMag > 0) {
2154                            lins *= this.lineMag;
2155                        } else {
2156                            lins /= -this.lineMag;
2157                        }
2158
2159                        eles = eles*eleRes/newEleRes;
2160
2161                        if (elementMag > 0) {
2162                            eles *= elementMag;
2163                        } else {
2164                            eles /= -elementMag;
2165                        }
2166
2167                        aii.setLines(lins);
2168                        aii.setElements(eles);
2169                        desc.setImageInfo(aii);
2170                        desc.setSource(aii.getURLString());
2171                    }
2172                    descriptors.add(desc);
2173                } catch (CloneNotSupportedException cnse) {}
2174            }
2175        }
2176        return descriptors;
2177    }
2178
2179    /**
2180     * Get the subset of the composite based on the selection
2181     *
2182     * @param choice  composite choice
2183     * @param subset  time selection
2184     *
2185     * @return subset list
2186     */
2187    private List getChoicesFromSubset(CompositeDataChoice choice,
2188                                      DataSelection subset) {
2189        List choices = choice.getDataChoices();
2190        if (subset == null) {
2191            return choices;
2192        }
2193        List times = subset.getTimes();
2194        if (times == null) {
2195            return choices;
2196        }
2197        times = TwoFacedObject.getIdList(times);
2198        List   subChoices = new ArrayList();
2199        Object firstTime  = times.get(0);
2200        if (firstTime instanceof Integer) {
2201            for (Iterator iter = times.iterator(); iter.hasNext(); ) {
2202                subChoices.add(
2203                    choices.get(((Integer) iter.next()).intValue()));
2204            }
2205        } else {  // TODO: what if they are DateTimes?
2206            subChoices.addAll(choices);
2207        }
2208        return subChoices;
2209    }
2210
2211    private AreaDirectory getPreviewDirectory(AddeImageDescriptor aid) {
2212        AreaDirectory directory = aid.getDirectory();
2213        int times = imageTimes.size();
2214        if (times == 1) return directory;
2215        String src = aid.getSource();
2216
2217        src = removeKey(src, LATLON_KEY);
2218        src = removeKey(src, LINELE_KEY);
2219        src = removeKey(src, PLACE_KEY);
2220        src = removeKey(src, SIZE_KEY);
2221        src = removeKey(src, UNIT_KEY);
2222        src = removeKey(src, MAG_KEY);
2223        src = removeKey(src, SPAC_KEY);
2224        src = removeKey(src, NAV_KEY);
2225        src = removeKey(src, AUX_KEY);
2226        src = removeKey(src, DOC_KEY);
2227
2228        int maxLine = 0;
2229        int maxEle = 0;
2230        int imageSize = 0;
2231        src = src.replace("imagedata", "imagedir");
2232        boolean isRelative = aid.getIsRelative();
2233        for (int i=0; i<times; i++) {
2234            if (isRelative) {
2235                src = replaceKey(src, "POS", new Integer(i).toString());
2236            } else {
2237                DateTime dt = (DateTime)imageTimes.get(i);
2238                String timeStr = dt.timeString();
2239                timeStr = timeStr.replace("Z", " ");
2240                src = removeKey(src, "POS");
2241                src = replaceKey(src, "TIME", timeStr + timeStr + "I");
2242            }
2243            try {
2244                AreaDirectoryList dirList = new AreaDirectoryList(src);
2245                List ad = dirList.getDirs();
2246                AreaDirectory areaDir = (AreaDirectory)ad.get(0);
2247                int lines = areaDir.getLines();
2248                int eles =  areaDir.getElements();
2249                if (imageSize < lines*eles) {
2250                    imageSize = lines * eles;
2251                    maxLine = lines;
2252                    maxEle = eles;
2253                    directory = areaDir;
2254                }
2255            } catch (Exception e) {
2256                logger.error("problem when dealing with AREA directory", e);
2257            }
2258        }
2259
2260        return directory;
2261    }
2262
2263    private String getServer(String urlString) {
2264        int ix = urlString.indexOf("//") + 2;
2265        String temp = urlString.substring(ix);
2266        ix = temp.indexOf("/");
2267        String retStr = temp.substring(0, ix);
2268        return retStr;
2269    }
2270
2271    public void setDisplaySource(String src, Hashtable props) {
2272         if (!props.isEmpty()) {
2273             Enumeration propEnum = props.keys();
2274             for (int i=0; propEnum.hasMoreElements(); i++) {
2275                 String key = propEnum.nextElement().toString();
2276                 Object val = props.get(key);
2277                 if (getKey(src, key).length() != 0) {
2278                     src = replaceKey(src, key, val);
2279                 }
2280             }
2281         }
2282         this.displaySource = src;
2283         String unit = getKey(src, UNIT_KEY);
2284         if (unit.length() != 0) {
2285             sourceProps.put(UNIT_KEY.toUpperCase(), unit);
2286         }
2287    }
2288
2289    public String getDisplaySource() {
2290        return this.displaySource;
2291    }
2292
2293
2294    private float[] getLineEleResolution(AreaDirectory ad) {
2295        logger.trace("ad: {} sensor: {}", ad, ad.getSensorID());
2296        float[] res = {(float)1.0, (float)1.0};
2297        int sensor = ad.getSensorID();
2298        List lines = null;
2299        try {
2300            String buff = getUrl();
2301
2302            lines = readTextLines(buff);
2303            if (lines == null) {
2304                return res;
2305            }
2306
2307            int gotit = -1;
2308            String[] cards = StringUtil.listToStringArray(lines);
2309            logger.trace("cards: {}", cards);
2310
2311            for (int i=0; i<cards.length; i++) {
2312                if ( ! cards[i].startsWith("Sat ")) continue;
2313                StringTokenizer st = new StringTokenizer(cards[i]," ");
2314                String temp = st.nextToken();  // throw away the key
2315                int m = st.countTokens();
2316                for (int k=0; k<m; k++) {
2317                    int ss = Integer.parseInt(st.nextToken().trim());
2318                    if (ss == sensor) {
2319                        gotit = i;
2320                        break;
2321                    }
2322                }
2323
2324                if (gotit != -1) {
2325                    break;
2326                }
2327            }
2328
2329            if (gotit == -1) {
2330                return res;
2331            }
2332
2333            int gotSrc = -1;
2334            for (int i=gotit; i<cards.length; i++) {
2335                if (cards[i].startsWith("EndSat")) {
2336                        return res;
2337                }
2338                if (!cards[i].startsWith("B") ) {
2339                        continue;
2340                }
2341                StringTokenizer tok = new StringTokenizer(cards[i]);
2342                String str = tok.nextToken();
2343                str = tok.nextToken();
2344                Float flt = new Float(str);
2345                res[0] = flt.floatValue();
2346                str = tok.nextToken();
2347                flt = new Float(str);
2348                res[1] = flt.floatValue();
2349                return res;
2350            }
2351        } catch (Exception e) {
2352            logger.error("problem getting the line+element rez", e);
2353        }
2354        return res;
2355    }
2356
2357
2358    /**
2359     * Read the adde text url and return the lines of text.
2360     * If unsuccessful return null.
2361     *
2362     * @param url adde url to a text file
2363     *
2364     * @return List of lines or {@code null} if in error.
2365     */
2366    protected List readTextLines(String url) {
2367        AddeTextReader reader = new AddeTextReader(url);
2368        List lines = null;
2369        if ("OK".equals(reader.getStatus())) {
2370            lines = reader.getLinesOfText();
2371        }
2372        return lines;
2373    }
2374
2375    /**
2376     * Create the first part of the ADDE request URL
2377     *
2378     * @param requestType type of request
2379     * @return ADDE URL prefix
2380     */
2381    protected String getUrl() {
2382        String str = source;
2383        str = str.replaceFirst("imagedata", "text");
2384        int indx = str.indexOf("VERSION");
2385        str = str.substring(0, indx);
2386        str = str.concat("file=SATBAND");
2387        return str;
2388    }
2389
2390    public Hashtable getSourceProps() {
2391        return this.sourceProps;
2392    }
2393
2394    public void setSourceProps(Hashtable sourceProps) {
2395        this.sourceProps = sourceProps;
2396    }
2397
2398//    public MapProjection getSampleMapProjection() {
2399//        return this.sampleMapProjection;
2400//    }
2401//
2402//    public void setSampleMapProjection(MapProjection sampleMapProjection) {
2403//        this.sampleMapProjection = sampleMapProjection;
2404//    }
2405
2406    public String getChoiceName() {
2407        return this.choiceName;
2408    }
2409
2410    public void setChoiceName(String choiceName) {
2411        this.choiceName = choiceName;
2412    }
2413
2414//    public MapProjection getPreviewProjection() {
2415//        return this.previewProjection;
2416//    }
2417//
2418//    public void setPreviewProjection(MapProjection previewProjection) {
2419//        this.previewProjection = previewProjection;
2420//    }
2421//
2422//    public AreaDirectory getPreviewDir() {
2423//        return this.previewDir;
2424//    }
2425//
2426//    public void setPreviewDir(AreaDirectory previewDir) {
2427//        this.previewDir = previewDir;
2428//    }
2429
2430    public String getSavePlace() {
2431        return this.savePlace;
2432    }
2433
2434    public void setSavePlace(String savePlace) {
2435        this.savePlace = savePlace;
2436    }
2437
2438    public double getSaveLat() {
2439        return this.saveLat;
2440    }
2441
2442    public void setSaveLat(double saveLat) {
2443        this.saveLat = saveLat;
2444    }
2445
2446    public double getSaveLon() {
2447        return this.saveLon;
2448    }
2449
2450    public void setSaveLon(double saveLon) {
2451        this.saveLon = saveLon;
2452    }
2453
2454    public int getSaveNumLine() {
2455        return this.saveNumLine;
2456    }
2457
2458    public void setSaveNumLine(int saveNumLine) {
2459        this.saveNumLine = saveNumLine;
2460    }
2461
2462    public int getSaveNumEle() {
2463        return this.saveNumEle;
2464    }
2465
2466    public void setSaveNumEle(int saveNumEle) {
2467        this.saveNumEle = saveNumEle;
2468    }
2469
2470    public int getSaveLineMag() {
2471        return this.saveLineMag;
2472    }
2473
2474    public void setSaveLineMag(int saveLineMag) {
2475        this.saveLineMag = saveLineMag;
2476    }
2477
2478    public int getSaveEleMag() {
2479        return this.saveEleMag;
2480    }
2481
2482    public void setSaveEleMag(int saveEleMag) {
2483        this.saveEleMag = saveEleMag;
2484    }
2485
2486    public String getSource() {
2487        return this.source;
2488    }
2489
2490    public void setSource(String source) {
2491        this.source = source;
2492    }
2493
2494    public boolean getShowPreview() {
2495        return this.showPreview;
2496    }
2497
2498    public void setShowPreview(boolean showPreview) {
2499        this.showPreview = showPreview;
2500    }
2501
2502    public boolean getSaveShowPreview() {
2503        return this.saveShowPreview;
2504    }
2505
2506    public void setSaveShowPreview(boolean saveShowPreview) {
2507        this.saveShowPreview = saveShowPreview;
2508    }
2509
2510    private void getSaveComponents() {
2511        saveCoordType = this.laLoSel.getCoordinateType();
2512        savePlace = this.laLoSel.getPlace();
2513        if (saveCoordType.equals(this.laLoSel.getLatLonType())) {
2514            saveLat = this.laLoSel.getLatitude();
2515            saveLon = this.laLoSel.getLongitude();
2516        }
2517        saveNumLine = this.laLoSel.getNumLines();
2518        saveNumEle = this.laLoSel.getNumEles();
2519        saveLineMag = this.laLoSel.getLineMag();
2520        saveEleMag = this.laLoSel.getElementMag();
2521    }
2522    
2523    public static class BundlePreviewSelection extends DataSelectionComponent {
2524        final String label;
2525        public BundlePreviewSelection(final String label) {
2526            super(label);
2527            this.label = label;
2528        }
2529
2530        @Override protected JComponent doMakeContents() {
2531            // TODO Auto-generated method stub
2532            JPanel panel = new JPanel();
2533            panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS));
2534            JLabel label1 = new JLabel("Area coverage has been defined by the data bundle;");
2535            JLabel label2 = new JLabel("further subsetting is not currently supported.");
2536            label1.setAlignmentX(Component.CENTER_ALIGNMENT);
2537            label2.setAlignmentX(Container.CENTER_ALIGNMENT);
2538            panel.add(label1);
2539            panel.add(label2);
2540            return panel;
2541        }
2542
2543        @Override public void applyToDataSelection(DataSelection dataSelection) {
2544        }
2545
2546        /**
2547         * Overridden to disable these dummy tabs from showing up in properties
2548         * dialog.
2549         */
2550        @Override public boolean getShowInControlProperties() {
2551            return false;
2552        }
2553    }
2554}