001/*
002 * This file is part of McIDAS-V
003 *
004 * Copyright 2007-2024
005 * Space Science and Engineering Center (SSEC)
006 * University of Wisconsin - Madison
007 * 1225 W. Dayton Street, Madison, WI 53706, USA
008 * https://www.ssec.wisc.edu/mcidas/
009 * 
010 * All Rights Reserved
011 * 
012 * McIDAS-V is built on Unidata's IDV and SSEC's VisAD libraries, and
013 * some McIDAS-V source code is based on IDV and VisAD source code.  
014 * 
015 * McIDAS-V is free software; you can redistribute it and/or modify
016 * it under the terms of the GNU Lesser Public License as published by
017 * the Free Software Foundation; either version 3 of the License, or
018 * (at your option) any later version.
019 * 
020 * McIDAS-V is distributed in the hope that it will be useful,
021 * but WITHOUT ANY WARRANTY; without even the implied warranty of
022 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
023 * GNU Lesser Public License for more details.
024 * 
025 * You should have received a copy of the GNU Lesser Public License
026 * along with this program.  If not, see https://www.gnu.org/licenses/.
027 */
028
029package edu.wisc.ssec.mcidasv.chooser.adde;
030
031import static javax.swing.GroupLayout.DEFAULT_SIZE;
032import static javax.swing.GroupLayout.PREFERRED_SIZE;
033import static javax.swing.GroupLayout.Alignment.BASELINE;
034import static javax.swing.GroupLayout.Alignment.LEADING;
035import static javax.swing.KeyStroke.getKeyStroke;
036import static javax.swing.LayoutStyle.ComponentPlacement.RELATED;
037
038import java.awt.BorderLayout;
039import java.awt.Color;
040import java.awt.Component;
041import java.awt.FlowLayout;
042import java.awt.IllegalComponentStateException;
043import java.awt.Insets;
044import java.awt.Point;
045import java.awt.event.ActionEvent;
046import java.awt.event.ActionListener;
047import java.awt.event.KeyEvent;
048import java.awt.event.KeyListener;
049import java.text.SimpleDateFormat;
050import java.util.ArrayList;
051import java.util.Arrays;
052import java.util.Collections;
053import java.util.Date;
054import java.util.Hashtable;
055import java.util.Iterator;
056import java.util.List;
057import java.util.StringTokenizer;
058import java.util.Vector;
059import java.util.regex.Matcher;
060import java.util.regex.Pattern;
061
062import javax.swing.GroupLayout;
063import javax.swing.JButton;
064import javax.swing.JCheckBox;
065import javax.swing.JComboBox;
066import javax.swing.JComponent;
067import javax.swing.JDialog;
068import javax.swing.JLabel;
069import javax.swing.JOptionPane;
070import javax.swing.JPanel;
071import javax.swing.JSlider;
072import javax.swing.JTextField;
073import javax.swing.JToggleButton;
074import javax.swing.KeyStroke;
075import javax.swing.event.ChangeEvent;
076import javax.swing.event.ChangeListener;
077
078import org.slf4j.Logger;
079import org.slf4j.LoggerFactory;
080import org.w3c.dom.Element;
081
082import ucar.unidata.data.DataSelection;
083import ucar.unidata.data.imagery.AddeImageDescriptor;
084import ucar.unidata.data.imagery.AddeImageInfo;
085import ucar.unidata.data.imagery.BandInfo;
086import ucar.unidata.data.imagery.ImageDataSource;
087import ucar.unidata.data.imagery.ImageDataset;
088import ucar.unidata.idv.IdvResourceManager;
089import ucar.unidata.idv.chooser.IdvChooserManager;
090import ucar.unidata.idv.chooser.adde.AddeServer;
091import ucar.unidata.ui.LatLonWidget;
092import ucar.unidata.util.Format;
093import ucar.unidata.util.GuiUtils;
094import ucar.unidata.util.LogUtil;
095import ucar.unidata.util.Misc;
096import ucar.unidata.util.StringUtil;
097import ucar.unidata.util.TwoFacedObject;
098import ucar.unidata.xml.XmlNodeList;
099import ucar.unidata.xml.XmlObjectStore;
100import ucar.unidata.xml.XmlResourceCollection;
101import ucar.unidata.xml.XmlUtil;
102import ucar.visad.UtcDate;
103
104import visad.VisADException;
105import visad.DateTime;
106import visad.Gridded1DSet;
107
108import edu.wisc.ssec.mcidas.AreaDirectory;
109import edu.wisc.ssec.mcidas.AreaDirectoryList;
110import edu.wisc.ssec.mcidas.McIDASException;
111import edu.wisc.ssec.mcidas.adde.AddeSatBands;
112import edu.wisc.ssec.mcidas.adde.AddeURL;
113import edu.wisc.ssec.mcidas.adde.AddeURLException;
114import edu.wisc.ssec.mcidasv.servermanager.EntryStore;
115import edu.wisc.ssec.mcidasv.ui.JCalendarDateEditor;
116import edu.wisc.ssec.mcidasv.ui.JCalendarPicker;
117import edu.wisc.ssec.mcidasv.ui.JTimeRangePicker;
118import edu.wisc.ssec.mcidasv.util.McVGuiUtils;
119
120/**
121 * Widget to select images from a remote ADDE server Displays a list of the
122 * descriptors (names) of the image datasets available for a particular ADDE
123 * group on the remote server.
124 * 
125 * @author Don Murray
126 */
127
128public class AddeImageChooser extends AddeChooser implements
129        ucar.unidata.ui.imagery.ImageSelector {
130
131    private static final long serialVersionUID = 1L;
132
133    private static final Logger logger = LoggerFactory.getLogger(AddeImageChooser.class);
134
135    // TODO: get rid of this button right?
136    public static JToggleButton mineBtn = GuiUtils
137            .getToggleImageButton(
138                    "/edu/wisc/ssec/mcidasv/resources/icons/toolbar/internet-web-browser16.png",
139                    "/edu/wisc/ssec/mcidasv/resources/icons/toolbar/system-software-update16.png",
140                    0, 0, true);
141
142    /** _more_ */
143    public static final int SIZE_THRESHOLD = 50;
144
145    /** default magnification */
146    private static final int DEFAULT_MAG = 0;
147
148    /** flag for center */
149    private static final String PLACE_CENTER = "CENTER";
150
151    /** flag for upper left */
152    private static final String PLACE_ULEFT = "ULEFT";
153
154    /** flag for lower left */
155    private static final String PLACE_LLEFT = "LLEFT";
156
157    /** flag for upper right */
158    private static final String PLACE_URIGHT = "URIGHT";
159
160    /** flag for lower right */
161    private static final String PLACE_LRIGHT = "LRIGHT";
162
163    /** Property for the satband file */
164    protected static final String FILE_SATBAND = "SATBAND";
165
166    /** Property for image default value band */
167    protected static final String PROP_BAND = "BAND";
168
169    /** Property for image default value id */
170    protected static final String PROP_ID = "ID";
171
172    /** Property for image default value key */
173    protected static final String PROP_KEY = "key";
174
175    /** Property for image default value lat/lon */
176    protected static final String PROP_LATLON = "LATLON";
177
178    /** Property for image default value line/ele */
179    protected static final String PROP_LINELE = "LINELE";
180
181    /** Property for image default value loc */
182    protected static final String PROP_LOC = "LOC";
183
184    /** Property for image default value mag */
185    protected static final String PROP_MAG = "MAG";
186
187    /** Property for num */
188    protected static final String PROP_NUM = "NUM";
189
190    /** Property for image default value place */
191    protected static final String PROP_PLACE = "PLACE";
192
193    /** Property for image default value size */
194    protected static final String PROP_SIZE = "SIZE";
195
196    /** Property for image default value spac */
197    protected static final String PROP_SPAC = "SPAC";
198
199    /** Property for image default value unit */
200    protected static final String PROP_UNIT = "UNIT";
201
202    /** Property for image default value unit */
203    protected static final String PROP_NAV = "NAV";
204
205    /** This is the list of properties that are used in the advanced gui */
206    private static final String[] ADVANCED_PROPS = { PROP_UNIT, PROP_BAND,
207            PROP_PLACE, PROP_LOC, PROP_SIZE, PROP_MAG, PROP_NAV };
208
209    /** This is the list of labels used for the advanced gui */
210    private static final String[] ADVANCED_LABELS = { "Data Type:", "Channel:",
211            "Placement:", "Location:", "Image Size:", "Magnification:",
212            "Navigation Type:" };
213
214    /** Xml tag name for the defaults */
215    protected static final String TAG_DEFAULT = "default";
216
217    /** identifier for the default value */
218    protected static final String VALUE_DEFAULT = "default";
219
220    /** Xml attr name for the defaults */
221    protected static final String ATTR_NAME = "name";
222
223    /** Xml attr name for the defaults */
224    protected static final String ATTR_PATTERN = "pattern";
225
226    /** flag for setting properties */
227    private boolean amSettingProperties = false;
228
229    /** Are we currently reading times */
230    private Object readTimesTask;
231    
232    /** number of image times to list */
233    protected String numTimes = "all";
234
235    /** Holds the properties */
236    private JPanel propPanel;
237    
238    protected JTextField imageCountTextField;
239
240    /** Maps the PROP_ property name to the gui component */
241    private Hashtable propToComps = new Hashtable();
242
243    /**
244     * This is a list of hashtables, one per imagedefaults resource. The
245     * Hashtables map the pattern to the xml node
246     */
247    private List resourceMaps;
248
249    /** Holds the subsetting defaults */
250    private XmlResourceCollection addeDefaults;
251
252    /** archive date formatter */
253    private SimpleDateFormat archiveDayFormatter;
254
255    /** Input for lat/lon center point */
256    protected LatLonWidget latLonWidget;
257
258    /** Widget for the line magnification in the advanced section */
259    protected JSlider lineMagSlider;
260
261    /** Label for the line mag. in the advanced section */
262    protected JLabel lineMagLbl;
263
264    /** Widget for the element magnfication in the advanced section */
265    protected JSlider elementMagSlider;
266
267    /** Label for the element mag. in the advanced section */
268    protected JLabel elementMagLbl;
269
270    /** base number of lines */
271    private double baseNumLines = 0.0;
272
273    /** size label */
274    JLabel sizeLbl;
275
276    /** base number of lines */
277    private double baseNumElements = 0.0;
278
279    /** Widget to hold the number of elements in the advanced */
280    JTextField numElementsFld;
281
282    /** Widget to hold the number of lines in the advanced */
283    JTextField numLinesFld;
284
285    /** Widget for the line center point in the advanced section */
286    protected JTextField centerLineFld;
287
288    /** Widget for the element center point in the advanced section */
289    protected JTextField centerElementFld;
290
291    /** _more_ */
292    private JToggleButton lockBtn;
293
294    /** full resolution button */
295    private JButton fullResBtn;
296
297    /** Identifier for the maximum number of bands */
298    int MAX_BANDS = 100;
299
300    /** The last AreaDirectory we have seen. */
301    private AreaDirectory lastAD;
302
303    /** The current AreaDirectory used for properties */
304    private AreaDirectory propertiesAD;
305
306    /** The previous AreaDirectory used for properties */
307    private AreaDirectory prevPropertiesAD;
308
309    /** Mapping of area directory to list of BandInfos */
310    protected Hashtable bandTable;
311
312    /** Mapping of band index to {@link edu.wisc.ssec.mcidas.AreaDirectory}. */
313    protected Hashtable bandDirs;
314
315    /**
316     * The list of currently loaded AddeImageDescriptor-s
317     */
318    protected Vector imageDescriptors;
319
320    /** maximum size for the widget */
321    private static final int MAX_SIZE = 700;
322
323    /** Widget for selecting image units */
324    protected JComboBox unitComboBox;
325
326    /** place label */
327    private JLabel placeLbl;
328
329    /** the place string */
330    private String place;
331
332    /** location panel */
333    private GuiUtils.CardLayoutPanel locationPanel;
334
335    /** Widget for selecting image nav type */
336    protected JComboBox navComboBox;
337
338    /**
339     * Mapping of sensor id (String) to hashtable that maps Integer band number
340     * to name
341     */
342    private Hashtable sensorToBandToName;
343
344    /** A flag so we can debug the new way of processing sat band file */
345    private boolean useSatBandInfo = true;
346
347    /** Used to parse the sat band file */
348    private AddeSatBands satBandInfo;
349
350    /** Widget for selecting the band */
351    protected JComboBox bandComboBox;
352
353    /** string for ALL */
354    protected static final String ALL = "ALL";
355
356    /** object for selecting all bands */
357    protected static final TwoFacedObject ALLBANDS = new TwoFacedObject(
358            "All Bands", ALL);
359
360    /** object for selecting all calibrations */
361    protected static final TwoFacedObject ALLUNITS = new TwoFacedObject(
362            "All Types", ALL);
363
364    /**
365     * Keep track of the lines to element ratio
366     */
367    private double linesToElements = 1.0;
368
369    /**
370     * limit of slider
371     */
372    private static final int SLIDER_MAX = 29;
373
374    private static final String DEFAULT_ARCHIVE_IMAGE_COUNT = "100";
375
376    /**
377     * the list of band infos
378     */
379    private List<BandInfo> bandInfos;
380
381    /**
382     * Construct an Adde image selection widget
383     * 
384     * 
385     * @param mgr
386     *            The chooser manager
387     * @param root
388     *            The chooser.xml node
389     */
390    public AddeImageChooser(IdvChooserManager mgr, Element root) {
391        super(mgr, root);
392
393        addDescComp(loadButton);
394
395        archiveDayBtn = new JButton(DAY_TIME_RANGE_LABEL);
396        archiveDayBtn.addActionListener(e -> getArchiveDay());
397        archiveDayBtn.setToolTipText("Select a specific day and time range");
398        
399        // Initialize the image count to default
400        String numImage = getIdv().getStore().get(PREF_NUM_IMAGE_PRESET_IMGCHOOSER, AddeImageChooser.DEFAULT_ARCHIVE_IMAGE_COUNT);
401        imageCountTextField = new JTextField(numImage, 4);
402        imageCountTextField.addActionListener(e -> readTimes(false));
403        imageCountTextField.setToolTipText(
404            "<html>Enter a numerical value or the word ALL and press Enter<br/><br/>" +
405            "By default, up to the 100 most recent times are listed.<br/><br/>" +
406            "You may set this field to any positive integer, or the value ALL.<br/>" +
407            "Using ALL may take awhile for datasets with many times.</html>"
408        );
409
410        this.addeDefaults = getImageDefaults();
411    }
412
413    /**
414     * Number of absolute times to list in the chooser. 
415     * Must be a positive integer, or the word "ALL".
416     * Will throw up a dialog for invalid entries.
417     * 
418     * @return 0 for valid entries, -1 for invalid
419     */
420    
421    private int parseImageCount() {
422        String countStr = imageCountTextField.getText();
423        try {
424            int newCount = Integer.parseInt(countStr);
425            // Make sure it's reasonable 
426            if (newCount > 0) {
427                int addeParam = 0 - newCount + 1;
428                numTimes = "" + addeParam;
429            } else {
430                throw new NumberFormatException();
431            }
432        } catch (NumberFormatException nfe) {
433            // Still ok if they entered "ALL"
434            if (imageCountTextField.getText().isEmpty()) {
435                JOptionPane.showMessageDialog(this, 
436                        "Empty field, please enter a valid positive integer");
437                return -1;
438            }
439            if (! imageCountTextField.getText().equalsIgnoreCase("all")) {
440                JOptionPane.showMessageDialog(this, 
441                        "Invalid entry: " + imageCountTextField.getText());
442                return -1;
443            }
444            numTimes = imageCountTextField.getText();
445        }
446        XmlObjectStore imgStore = getIdv().getStore();
447        imgStore.put(PREF_NUM_IMAGE_PRESET_IMGCHOOSER, countStr);
448        imgStore.save();
449        return 0;
450    }
451
452    /**
453     * Get the xml resource collection that defines the image default xml
454     * 
455     * @return Image defaults resources
456     */
457    protected XmlResourceCollection getImageDefaults() {
458        return getIdv().getResourceManager().getXmlResources(
459                IdvResourceManager.RSC_IMAGEDEFAULTS);
460    }
461
462    /**
463     * Update labels, enable widgets, etc.
464     */
465    @Override protected void updateStatus() {
466        super.updateStatus();
467        if (getDoAbsoluteTimes()) {
468            setPropertiesState(getASelectedTime());
469        } else {
470            setPropertiesState(lastAD);
471        }
472
473        if (readTimesTask != null) {
474            if (taskOk(readTimesTask)) {
475                setStatus("Reading available times from server");
476            }
477        } else if ((getState() == STATE_CONNECTED) && getDoAbsoluteTimes() && !haveTimeSelected() && haveDescriptorSelected()) {
478            setStatus(MSG_TIMES);
479        }
480        enableWidgets();
481    }
482
483    /**
484     * Do we have times selected. Either we are doing absolute times and there
485     * are some selected in the list. Or we are doing relative times and we have
486     * done a connect to the server
487     * 
488     * @return Do we have times
489     */
490    public boolean timesOk() {
491        if (getDoAbsoluteTimes() && !haveTimeSelected()) {
492            return false;
493        }
494        return (lastAD != null);
495    }
496
497    /**
498     * Get the list of advanced property names
499     * 
500     * @return array of advanced property names
501     */
502    protected String[] getAdvancedProps() {
503        return ADVANCED_PROPS;
504    }
505
506    /**
507     * Get the list of advanced property labels
508     * 
509     * @return list of advanced property labels
510     */
511    protected String[] getAdvancedLabels() {
512        return ADVANCED_LABELS;
513    }
514
515    /**
516     * Convenience method for lazy people who don't want to call
517     * {@link ucar.unidata.util.LogUtil#logException(String, Throwable)}.
518     * 
519     * @param msg
520     *            log message
521     * @param exc
522     *            Exception to log
523     */
524    @Override public void logException(String msg, Exception exc) {
525        LogUtil.logException(msg, exc);
526    }
527
528    /**
529     * This allows derived classes to provide their own name for labeling, etc.
530     * 
531     * @return the dataset name
532     */
533    @Override public String getDataName() {
534        return "Image Data";
535    }
536
537    /**
538     * Get the descriptor widget label
539     * 
540     * @return label for the descriptor widget
541     */
542    @Override public String getDescriptorLabel() {
543        return "Image Type";
544    }
545        
546    /**
547     * Respond to a change in the descriptor list.
548     */
549    protected void checkSetNav() {
550        String descriptor = getDescriptor();
551        if (descriptor!=null) {
552            String[] suffixes = { "AMSU", "HIRS", "HRPT", "GAC", "LAC" };
553            for (int i=0; i<suffixes.length; i++) {
554                Pattern p = Pattern.compile("N\\d\\d" + suffixes[i]);
555                Matcher m = p.matcher(descriptor);
556                if (m.find()) {
557                    navComboBox.setSelectedIndex(1);
558                    break;
559                }
560            }
561        }
562    }
563
564    /**
565     * Get the name of the dataset.
566     * 
567     * @return descriptive name of the dataset.
568     */
569    public String getDatasetName() {
570        StringBuffer buf = new StringBuffer();
571        buf.append(getSelectedDescriptor());
572        if (bandComboBox != null && bandComboBox.getItemCount() > 1) {
573            buf.append(" (");
574            buf.append(bandComboBox.getSelectedItem());
575            buf.append(")");
576        }
577        return buf.toString();
578    }
579    
580    /**
581     * Check if we are ready to read times
582     * 
583     * @return true if times can be read
584     */
585    protected boolean canReadTimes() {
586        return haveDescriptorSelected();
587    }
588
589    /**
590     * Handle when the user presses the update button
591     * 
592     * @throws Exception
593     *             On badness
594     */
595    @Override public void handleUpdate() throws Exception {
596        if (getState() != STATE_CONNECTED) {
597            // If not connected then update the server list
598            updateServerList();
599        } else {
600            // If we are already connected then update the rest of the chooser
601            descriptorChanged();
602        }
603        updateStatus();
604    }
605
606    /**
607     * Do server connection stuff... override this with type-specific methods
608     */
609    @Override protected void readFromServer() {
610        archiveDay = null;
611        if (archiveDayBtn != null) {
612            archiveDayBtn.setText(DAY_TIME_RANGE_LABEL);
613        }
614        readSatBands();
615        super.readFromServer();
616    }
617
618    /**
619     * Overwrite base class method to clear out the lastAD member here.
620     */
621    @Override protected void clearTimesList() {
622        lastAD = null;
623        super.clearTimesList();
624    }
625
626    /**
627     * Show the day and time range dialog. This method is not meant to be called but is
628     * public by reason of implementation (or insanity).
629     */
630    
631    public void getArchiveDay() {
632        
633        final JDialog dialog = GuiUtils.createDialog("Set Day and Time Range", true);
634        final JCalendarPicker picker = new JCalendarPicker(false);
635        final JTimeRangePicker trp = new JTimeRangePicker();
636
637        if (archiveDay != null) {
638            if (archiveDayFormatter == null) {
639                archiveDayFormatter = new SimpleDateFormat(UtcDate.YMD_FORMAT);
640            }
641            Date d = null;
642            try {
643                d = archiveDayFormatter.parse(archiveDay);
644                picker.setDate(d);
645            } catch (Exception e) {
646                logException("parsing archive day " + archiveDay, e);
647            }
648        }
649
650        ActionListener listener = new ActionListener() {
651            public void actionPerformed(ActionEvent ae) {
652                String cmd = ae.getActionCommand();
653                if (cmd.equals(GuiUtils.CMD_OK)) {
654                    
655                    // bad time range, throw up error window
656                    if (! trp.timeRangeOk()) {
657                        String msg = "Time range is invalid.\n" + 
658                                     "Please provide valid hours, minutes and\n" +
659                                     "seconds, with End Time > Start Time.";
660                        Object[] params = { msg };
661                        JOptionPane.showMessageDialog(null, params, "Invalid Time Range", JOptionPane.OK_OPTION);
662                        return;
663                    } else {
664                        archiveBegTime = trp.getBegTimeStr(); 
665                        archiveEndTime = trp.getEndTimeStr(); 
666                    }
667                    try {
668                        archiveDay = picker.getUserSelectedDay();
669                        archiveDayBtn.setText(archiveDay);
670                    } catch (Exception e) {
671                    }
672
673                    setDoAbsoluteTimes(true);
674                    descriptorChanged();
675                }
676                dialog.dispose();
677            }
678        };
679
680        final JCalendarDateEditor dateEditor =
681            (JCalendarDateEditor)picker.getDateChooser().getDateEditor();
682        dateEditor.getUiComponent().addKeyListener(new KeyListener() {
683            @Override public void keyTyped(KeyEvent e) { }
684
685            @Override public void keyPressed(KeyEvent e) { }
686
687            @Override public void keyReleased(KeyEvent e) {
688                if (!Color.RED.equals(dateEditor.getForeground())) {
689                    KeyStroke stroke =
690                        getKeyStroke(e.getKeyCode(), e.getModifiers());
691                    if (stroke.getKeyCode() == KeyEvent.VK_ENTER) {
692                        try {
693                            archiveDay = picker.getUserSelectedDay();
694                            archiveDayBtn.setText(archiveDay);
695                        } catch (Exception ex) {
696                            // nothing to do
697                        }
698                        setDoAbsoluteTimes(true);
699                        descriptorChanged();
700                        dialog.dispose();
701                    }
702                }
703            }
704        });
705
706        JPanel buttons = GuiUtils.makeButtons(listener, new String[] {
707                GuiUtils.CMD_OK, GuiUtils.CMD_CANCEL });
708
709        JPanel dateTimePanel = new JPanel(new FlowLayout());
710        dateTimePanel.add(picker);
711        dateTimePanel.add(trp);
712        
713        JComponent contents = GuiUtils.topCenterBottom(GuiUtils.inset(GuiUtils
714                .lLabel("Please select a day and optional time range for this dataset:"), 10), GuiUtils
715                .inset(dateTimePanel, 10), buttons);
716        Point p = new Point(200, 200);
717        if (archiveDayBtn != null) {
718            try {
719                p = archiveDayBtn.getLocationOnScreen();
720            } catch (IllegalComponentStateException ice) {
721            }
722        }
723        dialog.setLocation(p);
724        dialog.getContentPane().add(contents);
725        dialog.pack();
726        dialog.setVisible(true);
727    }
728
729    /**
730     * Add the bottom advanced gui panel to the list
731     * 
732     * @param bottomComps
733     *            the bottom components
734     */
735    protected void getBottomComponents(List bottomComps) {
736
737        String[] propArray = getAdvancedProps();
738        String[] labelArray = getAdvancedLabels();
739        // List bottomComps = new ArrayList();
740        Insets dfltGridSpacing = new Insets(4, 0, 4, 0);
741        String dfltLblSpacing = " ";
742
743        boolean haveBand = Misc.toList(propArray).contains(PROP_BAND);
744        boolean haveNav = Misc.toList(propArray).contains(PROP_NAV);
745        for (int propIdx = 0; propIdx < propArray.length; propIdx++) {
746            JComponent propComp = null;
747            String prop = propArray[propIdx];
748            if (prop.equals(PROP_UNIT)) {
749                unitComboBox = new JComboBox();
750                addPropComp(PROP_UNIT, propComp = unitComboBox);
751                GuiUtils.setPreferredWidth(unitComboBox, 100);
752                if (haveBand) {
753                    bandComboBox = new JComboBox();
754                    bandComboBox.addActionListener(new ActionListener() {
755                        public void actionPerformed(ActionEvent e) {
756                            setAvailableUnits(propertiesAD, getSelectedBand());
757                        }
758                    });
759                    addPropComp(PROP_BAND, bandComboBox);
760
761                    propComp = GuiUtils.hbox(propComp, GuiUtils.inset(
762                            new JLabel("Channel:"), new Insets(0, 10, 0, 5)),
763                            bandComboBox, 5);
764                }
765            } else if (prop.equals(PROP_BAND)) {
766                // Moved to PROP_UNIT
767            } else if (prop.equals(PROP_PLACE)) {
768                // Moved to PROP_LOC
769            } else if (prop.equals(PROP_LOC)) {
770                placeLbl = GuiUtils.getFixedWidthLabel("");
771                changePlace(PLACE_CENTER);
772                addPropComp(PROP_PLACE, placeLbl);
773
774                latLonWidget = new LatLonWidget();
775                centerLineFld = new JTextField("", 3);
776                centerElementFld = new JTextField("", 3);
777
778                fullResBtn = GuiUtils.makeImageButton(
779                        "/auxdata/ui/icons/arrow_out.png", this,
780                        "setToFullResolution");
781                fullResBtn.setContentAreaFilled(false);
782                fullResBtn.setToolTipText("Set to full resolution");
783
784                // lockBtn =
785                // GuiUtils.getToggleImageButton(IdvUIManager.ICON_UNLOCK,
786                // IdvUIManager.ICON_LOCK, 0, 0, true);
787                // lockBtn.setContentAreaFilled(false);
788                // lockBtn.setSelected(true);
789                // lockBtn.setToolTipText(
790                // "Unlock to automatically change size when changing magnification");
791
792                final JButton centerPopupBtn = GuiUtils.getImageButton(
793                        "/auxdata/ui/icons/MapIcon16.png", getClass());
794                centerPopupBtn.setToolTipText("Center on current displays");
795                centerPopupBtn.addActionListener(new ActionListener() {
796                    public void actionPerformed(ActionEvent ae) {
797                        getIdv().getIdvUIManager().popupCenterMenu(
798                                centerPopupBtn, latLonWidget);
799                    }
800                });
801                JComponent centerPopup = GuiUtils.inset(centerPopupBtn,
802                        new Insets(0, 0, 0, 4));
803
804                GuiUtils.tmpInsets = dfltGridSpacing;
805                final JPanel latLonPanel = GuiUtils.hbox(new Component[] {
806                        GuiUtils.rLabel(" Lat:" + dfltLblSpacing),
807                        latLonWidget.getLatField(),
808                        GuiUtils.rLabel(" Lon:" + dfltLblSpacing),
809                        latLonWidget.getLonField(), new JLabel(" "),
810                        centerPopup });
811
812                final JPanel lineElementPanel = GuiUtils.hbox(new Component[] {
813                        GuiUtils.rLabel(" Line:" + dfltLblSpacing),
814                        centerLineFld,
815                        GuiUtils.rLabel(" Element:" + dfltLblSpacing),
816                        centerElementFld });
817
818                locationPanel = new GuiUtils.CardLayoutPanel();
819                locationPanel.addCard(latLonPanel);
820                locationPanel.addCard(lineElementPanel);
821
822                JButton locPosButton = GuiUtils.makeImageButton(
823                        "/auxdata/ui/icons/Refresh16.gif", this, "cyclePlace",
824                        null, true);
825
826                locPosButton.setToolTipText("Change place type");
827
828                JButton locTypeButton = GuiUtils.makeImageButton(
829                        "/auxdata/ui/icons/Refresh16.gif", locationPanel,
830                        "flip", null, true);
831                locTypeButton
832                        .setToolTipText("Toggle between Latitude/Longitude and Line/Element");
833
834                propComp = GuiUtils.hbox(new Component[] { locPosButton,
835                        placeLbl, locTypeButton, locationPanel }, 5);
836                addPropComp(PROP_LOC, propComp);
837            } else if (prop.equals(PROP_MAG)) {
838                boolean oldAmSettingProperties = amSettingProperties;
839                amSettingProperties = true;
840                ChangeListener lineListener = new javax.swing.event.ChangeListener() {
841                    public void stateChanged(ChangeEvent evt) {
842                        if (amSettingProperties) {
843                            return;
844                        }
845                        lineMagSliderChanged(!getLockButton().isSelected());
846                    }
847                };
848                ChangeListener elementListener = new ChangeListener() {
849                    public void stateChanged(javax.swing.event.ChangeEvent evt) {
850                        if (amSettingProperties) {
851                            return;
852                        }
853                        elementMagSliderChanged(true);
854                    }
855                };
856                JComponent[] lineMagComps = GuiUtils.makeSliderPopup(
857                        -SLIDER_MAX, SLIDER_MAX, 0, lineListener);
858                lineMagSlider = (JSlider) lineMagComps[1];
859                lineMagSlider.setMajorTickSpacing(1);
860                lineMagSlider.setSnapToTicks(true);
861                lineMagSlider.setExtent(1);
862                lineMagComps[0].setToolTipText("Change the line magnification");
863                JComponent[] elementMagComps = GuiUtils.makeSliderPopup(
864                        -SLIDER_MAX, SLIDER_MAX, 0, elementListener);
865                elementMagSlider = (JSlider) elementMagComps[1];
866                elementMagSlider.setExtent(1);
867                elementMagSlider.setMajorTickSpacing(1);
868                elementMagSlider.setSnapToTicks(true);
869                elementMagComps[0]
870                        .setToolTipText("Change the element magnification");
871                lineMagSlider
872                        .setToolTipText("Slide to set line magnification factor");
873                lineMagLbl = GuiUtils.getFixedWidthLabel(StringUtil.padLeft(
874                        "1", 4));
875                elementMagSlider
876                        .setToolTipText("Slide to set element magnification factor");
877                elementMagLbl = GuiUtils.getFixedWidthLabel(StringUtil.padLeft(
878                        "1", 4));
879                amSettingProperties = oldAmSettingProperties;
880
881                GuiUtils.tmpInsets = dfltGridSpacing;
882                /*
883                 * JPanel magPanel = GuiUtils.doLayout(new Component[] {
884                 * GuiUtils.rLabel("Line:" + dfltLblSpacing), lineMagLbl,
885                 * GuiUtils.inset(lineMagComps[0], new Insets(0, 4, 0, 0)),
886                 * GuiUtils.rLabel("   Element:" + dfltLblSpacing),
887                 * elementMagLbl, GuiUtils.inset(elementMagComps[0], new
888                 * Insets(0, 4, 0, 0)), }, 6, GuiUtils.WT_N, GuiUtils.WT_N);
889                 */
890                /*
891                 * JPanel magPanel = GuiUtils.doLayout(new Component[] {
892                 * lineMagLbl, GuiUtils.inset(lineMagComps[0], new Insets(0, 4,
893                 * 0, 0)), new JLabel("    X "), elementMagLbl,
894                 * GuiUtils.inset(elementMagComps[0], new Insets(0, 4, 0, 0)),
895                 * }, 6, GuiUtils.WT_N, GuiUtils.WT_N);
896                 */
897
898                JPanel magPanel = GuiUtils.doLayout(
899                        new Component[] {
900                                lineMagLbl,
901                                GuiUtils.inset(lineMagComps[0], new Insets(0,
902                                        4, 0, 0)),
903                                new JLabel("    X"),
904                                elementMagLbl,
905                                GuiUtils.inset(elementMagComps[0], new Insets(
906                                        0, 4, 0, 0)),
907                                GuiUtils.inset(getLockButton(), new Insets(0,
908                                        10, 0, 0)) }, 7, GuiUtils.WT_N,
909                        GuiUtils.WT_N);
910
911                addPropComp(PROP_MAG, propComp = magPanel);
912                if (haveNav) {
913                    navComboBox = new JComboBox();
914                    GuiUtils.setListData(navComboBox, Misc.newList(
915                            new TwoFacedObject("Default", "X"),
916                            new TwoFacedObject("Lat/Lon", "LALO")));
917                    addPropComp(PROP_NAV, navComboBox);
918                    boolean showNav = false;
919                    showNav = getProperty("includeNavComp", false);
920                    if (showNav) {
921                        propComp = GuiUtils.hbox(propComp, GuiUtils.inset(
922                                new JLabel("Navigation Type:"), new Insets(0,
923                                        10, 0, 5)), navComboBox, 5);
924                    }
925                }
926            } else if (prop.equals(PROP_SIZE)) {
927                numLinesFld = new JTextField("", 4);
928                numElementsFld = new JTextField("", 4);
929                numLinesFld.setToolTipText("Number of lines");
930                numElementsFld.setToolTipText("Number of elements");
931                GuiUtils.tmpInsets = dfltGridSpacing;
932                sizeLbl = GuiUtils.lLabel("");
933
934                /*
935                 * JPanel sizePanel = GuiUtils.left(GuiUtils.doLayout(new
936                 * Component[] { GuiUtils.rLabel("Lines:" + dfltLblSpacing),
937                 * numLinesFld, GuiUtils.rLabel(" Elements:" + dfltLblSpacing),
938                 * numElementsFld, new JLabel(" "), sizeLbl }, 6, GuiUtils.WT_N,
939                 * GuiUtils.WT_N));
940                 */
941
942                JPanel sizePanel = GuiUtils.left(GuiUtils.doLayout(
943                        new Component[] { numLinesFld, new JLabel(" X "),
944                                numElementsFld, fullResBtn, /* new JLabel(" "), */
945                                sizeLbl }, 5, GuiUtils.WT_N, GuiUtils.WT_N));
946                addPropComp(PROP_SIZE, propComp = sizePanel);
947            }
948
949            if (propComp != null) {
950                bottomComps.add(GuiUtils.rLabel(labelArray[propIdx]));
951                bottomComps.add(GuiUtils.left(propComp));
952            }
953
954        }
955
956        GuiUtils.tmpInsets = new Insets(3, 4, 0, 4);
957        propPanel = GuiUtils.doLayout(bottomComps, 2, GuiUtils.WT_N,
958                GuiUtils.WT_N);
959        enableWidgets();
960    }
961
962    /**
963     * Get the "lock" button
964     * 
965     * @return the lock button
966     */
967    private JToggleButton getLockButton() {
968        if (lockBtn == null) {
969            lockBtn = GuiUtils.getToggleImageButton(
970                    "/auxdata/ui/icons/Linked.gif",
971                    "/auxdata/ui/icons/Unlinked.gif", 0, 0, true);
972            lockBtn.setContentAreaFilled(false);
973            lockBtn.setSelected(true);
974            lockBtn
975                    .setToolTipText("Unlock to automatically change size when changing magnification");
976        }
977        return lockBtn;
978
979    }
980
981    /**
982     * Set to full resolution
983     */
984    public void setToFullResolution() {
985
986        if (propertiesAD == null) {
987            return;
988        }
989        amSettingProperties = true;
990        numLinesFld.setText("" + propertiesAD.getLines());
991        numElementsFld.setText("" + propertiesAD.getElements());
992        changePlace(PLACE_CENTER);
993        if (useLatLon()) {
994            locationPanel.flip();
995        }
996        centerLineFld.setText("" + (propertiesAD.getLines() / 2));
997        centerElementFld.setText("" + (propertiesAD.getElements() / 2));
998
999        setMagSliders(1, 1);
1000        amSettingProperties = false;
1001
1002    }
1003
1004    /**
1005     * Cycle the place
1006     */
1007    public void cyclePlace() {
1008        if (place.equals(PLACE_CENTER)) {
1009            changePlace(PLACE_ULEFT);
1010        } else {
1011            changePlace(PLACE_CENTER);
1012        }
1013    }
1014
1015    /**
1016     * Change the place
1017     * 
1018     * @param newPlace
1019     *            new place
1020     */
1021    public void changePlace(String newPlace) {
1022        this.place = newPlace;
1023        String s = translatePlace(place) + "=";
1024        placeLbl.setText(StringUtil.padRight(s, 12));
1025    }
1026
1027    /**
1028     * _more_
1029     * 
1030     * @param recomputeLineEleRatio
1031     *            _more_
1032     */
1033    protected void elementMagSliderChanged(boolean recomputeLineEleRatio) {
1034        int value = getElementMagValue();
1035        if ((Math.abs(value) < SLIDER_MAX)) {
1036            int lineMag = getLineMagValue();
1037            if (lineMag > value) {
1038                linesToElements = Math.abs(lineMag / (double) value);
1039            } else {
1040                linesToElements = Math.abs((double) value / lineMag);
1041            }
1042        }
1043        // System.out.println(" changelistener: linesToElements = " +
1044        // linesToElements);
1045        elementMagLbl.setText(StringUtil.padLeft("" + value, 4));
1046        if (!getLockButton().isSelected()) {
1047            if (value > 0) {
1048                numElementsFld.setText("" + (int) (baseNumElements * value));
1049            } else {
1050                numElementsFld.setText(""
1051                        + (int) (baseNumElements / (double) -value));
1052            }
1053        }
1054    }
1055
1056    /**
1057     * Handle the line mag slider changed event
1058     * 
1059     * 
1060     * @param autoSetSize
1061     *            _more_
1062     */
1063    protected void lineMagSliderChanged(boolean autoSetSize) {
1064        try {
1065            int value = getLineMagValue();
1066            lineMagLbl.setText(StringUtil.padLeft("" + value, 4));
1067            if (autoSetSize) {
1068                if (value > 0) {
1069                    numLinesFld.setText("" + (int) (baseNumLines * value));
1070                } else {
1071                    numLinesFld.setText(""
1072                            + (int) (baseNumLines / (double) -value));
1073                }
1074            }
1075
1076            if (value == 1) { // special case
1077                if (linesToElements < 1.0) {
1078                    value = (int) (-value / linesToElements);
1079                } else {
1080                    value = (int) (value * linesToElements);
1081                }
1082
1083            } else if (value > 1) {
1084                value = (int) (value * linesToElements);
1085
1086            } else {
1087                value = (int) (value / linesToElements);
1088            }
1089
1090            value = (value > 0) ? value - 1 : value + 1; // since slider is one
1091                                                            // off
1092            amSettingProperties = true;
1093            elementMagSlider.setValue(value);
1094            amSettingProperties = false;
1095            elementMagSliderChanged(false);
1096        } catch (Exception exc) {
1097            logException("Setting line magnification", exc);
1098        }
1099        // amSettingProperties = false;
1100    }
1101
1102    /**
1103     * Get the value of the line magnification slider.
1104     * 
1105     * @return The magnification value for the line
1106     */
1107    protected int getLineMagValue() {
1108        return getMagValue(lineMagSlider);
1109    }
1110
1111    /**
1112     * Get the value of the element magnification slider.
1113     * 
1114     * @return The magnification value for the element
1115     */
1116    protected int getElementMagValue() {
1117        return getMagValue(elementMagSlider);
1118    }
1119
1120    /**
1121     * Handle the absolute time selection changing
1122     */
1123    @Override protected void absoluteTimesSelectionChanged() {
1124        if (!getDoAbsoluteTimes()) {
1125            return;
1126        }
1127        if (getIdv().getProperty("idv.chooser.addeimage.updateontimechange",
1128                true)) {
1129            setPropertiesState(getASelectedTime(), false);
1130        }
1131    }
1132
1133    /**
1134     * Set the relative and absolute extra components.
1135     */
1136    @Override protected JPanel makeTimesPanel() {
1137        JPanel panel =
1138            super.makeTimesPanel(false, true, getIdv().getUseTimeDriver());
1139        JPanel buttonPanel = new JPanel(new FlowLayout());
1140        buttonPanel.add(archiveDayBtn);
1141        buttonPanel.add(new JLabel("Num Images: "));
1142        buttonPanel.add(imageCountTextField);
1143        underTimelistPanel.add(BorderLayout.CENTER, buttonPanel);
1144        return panel;
1145    }
1146
1147    /**
1148     * Associates the given JComponent with the PROP_ property identified by the
1149     * given propId
1150     * 
1151     * @param propId
1152     *            The property
1153     * @param comp
1154     *            The gui component that allows the user to set the property
1155     * 
1156     * @return Just returns the given comp
1157     */
1158    protected JComponent addPropComp(String propId, JComponent comp) {
1159        Object oldComp = propToComps.get(propId);
1160        if (oldComp != null) {
1161            throw new IllegalStateException("Already have a component defined:"
1162                    + propId);
1163        }
1164        propToComps.put(propId, comp);
1165        return comp;
1166    }
1167
1168    /**
1169     * Should we use the user supplied property
1170     * 
1171     * @param propId
1172     *            The property
1173     * 
1174     * @return Should use the value from the advanced widget
1175     */
1176    protected boolean usePropFromUser(String propId) {
1177        if (propToComps.get(propId) == null) {
1178            return false;
1179        }
1180        return true;
1181    }
1182
1183    /**
1184     * Get one of the selected times.
1185     * 
1186     * @return One of the selected times.
1187     */
1188    protected AreaDirectory getASelectedTime() {
1189        if (haveTimeSelected()) {
1190            List selected = getSelectedAbsoluteTimes();
1191            if (selected.size() > 0) {
1192                AddeImageDescriptor aid = (AddeImageDescriptor) selected.get(0);
1193                if (aid != null) {
1194                    return aid.getDirectory();
1195                }
1196            }
1197        }
1198        return null;
1199    }
1200
1201    /**
1202     * Enable or disable the GUI widgets based on what has been selected.
1203     */
1204    @Override protected void enableWidgets() {
1205        boolean descriptorState = ((getState() == STATE_CONNECTED) && canReadTimes());
1206
1207        for (int i = 0; i < compsThatNeedDescriptor.size(); i++) {
1208            JComponent comp = (JComponent) compsThatNeedDescriptor.get(i);
1209            GuiUtils.enableTree(comp, descriptorState);
1210        }
1211
1212        boolean timesOk = timesOk();
1213        if (propPanel != null) {
1214            GuiUtils.enableTree(propPanel, timesOk);
1215        }
1216
1217        // Require times to be selected
1218        GuiUtils.enableTree(loadButton, descriptorState && timesOk);
1219
1220        if (timesOk) {
1221            checkCenterEnabled();
1222        }
1223        checkTimesLists();
1224
1225        // TODO: This is temporary... absolute times on Windows makes the local
1226        // servers choke
1227        // Update: this works now, but leave it here as a reminder
1228        // boolean localWindowsServer = isLocalServer() &&
1229        // System.getProperty("os.name").startsWith("Windows");
1230        // setDoAbsoluteTimes(getDoAbsoluteTimes() && !localWindowsServer);
1231
1232        enableAbsoluteTimesList(getDoAbsoluteTimes() && descriptorState);
1233
1234        getRelativeTimesChooser().setEnabled(
1235                !getDoAbsoluteTimes() && descriptorState);
1236
1237        if (drivercbx != null) {
1238//            logger.trace("set drivercbx={}", anyTimeDrivers() && descriptorState);
1239            drivercbx.setEnabled(anyTimeDrivers() && descriptorState);
1240        }
1241        revalidate();
1242    }
1243
1244    /**
1245     * Check if we are using the lat/lon widget
1246     * 
1247     * @return true if we are using the lat/lon widget
1248     */
1249    protected boolean useLatLon() {
1250        return locationPanel.getVisibleIndex() == 0;
1251    }
1252
1253    /**
1254     * Enable or disable the center lat/lon and line/element widgets
1255     */
1256    protected void checkCenterEnabled() {
1257        // NOT NOW
1258        if (true) {
1259            return;
1260        }
1261
1262        boolean usingLatLon = useLatLon();
1263
1264        latLonWidget.getLatField().setEnabled(usingLatLon);
1265        latLonWidget.getLonField().setEnabled(usingLatLon);
1266        // centerLatLbl.setEnabled(usingLatLon);
1267        // centerLonLbl.setEnabled(usingLatLon);
1268
1269        centerLineFld.setEnabled(!usingLatLon);
1270        centerElementFld.setEnabled(!usingLatLon);
1271        // centerLineLbl.setEnabled( !usingLatLon);
1272        // centerElementLbl.setEnabled( !usingLatLon);
1273
1274    }
1275
1276    /**
1277     * Get the selected calibration unit.
1278     * 
1279     * @return the selected calibration unit
1280     */
1281    protected String getSelectedUnit() {
1282        if (unitComboBox==null || unitComboBox.getSelectedItem()==null) return "";
1283        String selection = (String) ((TwoFacedObject) unitComboBox.getSelectedItem()).getId();
1284        return selection;
1285    }
1286
1287    /**
1288     * Get the data type for this chooser
1289     * 
1290     * @return the data type
1291     */
1292    @Override public String getDataType() {
1293        return "IMAGE";
1294    }
1295
1296    /**
1297     * Get a description of the currently selected dataset
1298     * 
1299     * @return a description of the currently selected dataset
1300     * @deprecated use #getDatasetName()
1301     */
1302    @Deprecated public String getDatasetDescription() {
1303        return getDatasetName();
1304    }
1305
1306    /**
1307     * Read the set of image times available for the current server/group/type
1308     * This method is a wrapper, setting the wait cursor and wrapping the call
1309     * to {@link #readTimesInner(boolean)}; in a try/catch block
1310     */
1311    @Override public void readTimes() {
1312        readTimes(false);
1313    }
1314
1315    public void readTimes(boolean forceAll) {
1316        
1317        // Make sure there is a valid entry in the image count text field
1318        if (parseImageCount() < 0) return;
1319        
1320        clearTimesList();
1321        if (!canReadTimes()) {
1322            return;
1323        }
1324        Misc.run(new Runnable() {
1325            public void run() {
1326                updateStatus();
1327                showWaitCursor();
1328                try {
1329                    readTimesInner(forceAll);
1330                    checkSetNav();
1331                } catch (Exception e) {
1332                    handleConnectionError(e);
1333                }
1334                showNormalCursor();
1335                updateStatus();
1336            }
1337        });
1338    }
1339
1340    /**
1341     * _more_
1342     */
1343    @Override public void doCancel() {
1344        readTimesTask = null;
1345        setState(STATE_UNCONNECTED);
1346        super.doCancel();
1347    }
1348
1349    /** locking mutex */
1350    private Object MUTEX = new Object();
1351
1352    /**
1353     * Set the list of dates/times based on the image selection
1354     * 
1355     */
1356    
1357    protected void readTimesInner(boolean forceAll) {
1358        String descriptor = getDescriptor();
1359        // String archivePosition = forceAll ? "all" : numTimes;
1360        String archivePosition = numTimes;
1361        String pos = (getDoAbsoluteTimes() || (archiveDay != null))
1362                      ? archivePosition
1363                      : "0";
1364
1365        StringBuffer addeCmdBuff = getGroupUrl(REQ_IMAGEDIR, getGroup());
1366        String id = getSelectedStation();
1367        if (id != null) {
1368            appendKeyValue(addeCmdBuff, PROP_ID, id);
1369        }
1370        appendKeyValue(addeCmdBuff, PROP_DESCR, descriptor);
1371        appendKeyValue(addeCmdBuff, PROP_POS, "" + pos);
1372        if (archiveDay != null) {
1373            appendKeyValue(addeCmdBuff, PROP_DAY, archiveDay);
1374            appendKeyValue(addeCmdBuff, PROP_TIME, archiveBegTime + " " + archiveEndTime);
1375        }
1376        String url = addeCmdBuff.toString();
1377        readTimesTask = startTask();
1378        updateStatus();
1379        Object task = readTimesTask;
1380        try {
1381            AreaDirectoryList adir = new AreaDirectoryList(url);
1382            // Make sure no other loads are occurred
1383            boolean ok = stopTaskAndIsOk(task);
1384            if (!Misc.equals(readTimesTask, task) || !ok) {
1385                return;
1386            }
1387            readTimesTask = null;
1388
1389            synchronized (MUTEX) {
1390                // Array of AreaDirectory-s sorted by time
1391                AreaDirectory[][] dirs = adir.getSortedDirs();
1392                int numImages = dirs.length;
1393                imageDescriptors = new Vector();
1394                // TODO: Add a setBands method to AreaDirectory to replace bandtable
1395                bandTable = new Hashtable(numImages);
1396                int len = dirs[0].length;
1397                bandDirs = new Hashtable(len);
1398                for (int i = 0; i < len; i++) {
1399                    AreaDirectory dir = dirs[0][i];
1400                    int ilen = dirs[0][i].getBands().length;
1401                    for(int j = 0; j < ilen; j++){
1402                        int bindex = dirs[0][i].getBands()[j];
1403                        bandDirs.put(bindex, dir);
1404                    }
1405                }
1406                lastAD = null;
1407                for (int i = 0; i < numImages; i++) {
1408                    int bandIndex = 0;
1409                    lastAD = (AreaDirectory) dirs[i][0];
1410                    int[] allBands = new int[MAX_BANDS];
1411                    Vector[] allCals = new Vector[MAX_BANDS];
1412                    for (int j = 0; j < dirs[i].length; j++) {
1413                        int nbands = dirs[i][j].getNumberOfBands();
1414                        int[] abands = dirs[i][j].getBands();
1415                        Vector[] vb = dirs[i][j].getCalInfo();
1416                        for (int k = 0; k < nbands; k++) {
1417                            allBands[bandIndex] = abands[k];
1418                            allCals[bandIndex++] = vb[k];
1419                        }
1420                    }
1421                    int[] bands = new int[bandIndex];
1422                    System.arraycopy(allBands, 0, bands, 0, bandIndex);
1423                    Vector[] cals = new Vector[bandIndex];
1424                    System.arraycopy(allCals, 0, cals, 0, bandIndex);
1425                    lastAD.setCalInfo(cals);
1426                    bandTable.put(lastAD, bands);
1427                    AddeImageDescriptor aid = new AddeImageDescriptor(lastAD, null);
1428                    imageDescriptors.add(aid);
1429                }
1430
1431                Collections.sort(imageDescriptors);
1432                if (getDoAbsoluteTimes()) {
1433                    setAbsoluteTimes(imageDescriptors);
1434                }
1435                // revalidate();
1436            }
1437            setState(STATE_CONNECTED);
1438        } catch (McIDASException e) {
1439            logger.warn("Exception while reading times from server", e);
1440            stopTask(task);
1441            readTimesTask = null;
1442            handleConnectionError("\nError reading times", e);
1443            // you want this step because readTimes() will call updateStatus(),
1444            // and since we already have a server and dataset, all the user needs
1445            // to do is select an image type (descriptor).
1446            setState(STATE_CONNECTED);
1447        }
1448    }
1449
1450    /**
1451     * Set the selected times in the times list based on the input times
1452     * 
1453     * @param times Input times.
1454     */
1455    @Override protected void setSelectedTimes(DateTime[] times) {
1456        if ((times == null) || (times.length == 0)) {
1457            return;
1458        }
1459        DateTime[] imageTimes = new DateTime[times.length];
1460
1461        if (imageDescriptors != null) {
1462            imageTimes = new DateTime[imageDescriptors.size()];
1463            for (int idIdx = 0; idIdx < imageDescriptors.size(); idIdx++) {
1464                AddeImageDescriptor aid = (AddeImageDescriptor) imageDescriptors.get(idIdx);
1465                imageTimes[idIdx] = aid.getImageTime();
1466            }
1467        } else {
1468            imageTimes = times;
1469        }
1470        if (imageTimes.length > 0) {
1471            try {
1472                Gridded1DSet imageSet    = DateTime.makeTimeSet(imageTimes);
1473                int          numTimes    = times.length;
1474                double[][]   timesValues = new double[1][numTimes];
1475                for (int i = 0; i < times.length; i++) {
1476                    timesValues[0][i] = times[i].getValue(imageSet.getSetUnits()[0]);
1477                }
1478                setSelectedAbsoluteTimes(imageSet.doubleToIndex(timesValues));
1479                absoluteTimesSelectionChanged();
1480            } catch (VisADException ve) {
1481                logException("Unable to set times from display", ve);
1482            }
1483        }
1484    }
1485
1486    /**
1487     * Set the center location portion of the request. If the input from the
1488     * widget is null, use the centerpoint from the image descriptor.
1489     * 
1490     * @param aid Image descriptor for the image.
1491     */
1492    protected void setCenterLocation(AddeImageDescriptor aid) {
1493        String latPoint = "";
1494        String lonPoint = "";
1495        if (aid != null) {
1496            AreaDirectory ad = aid.getDirectory();
1497            latPoint = "" + ad.getCenterLatitude();
1498            lonPoint = "" + ad.getCenterLongitude();
1499        }
1500        if (!latPoint.trim().equals("")) {
1501            latLonWidget.setLat(latPoint);
1502        }
1503        if (!lonPoint.trim().equals("")) {
1504            latLonWidget.setLon(lonPoint);
1505        }
1506    }
1507
1508    /**
1509     * get the adde server grup type to use
1510     * 
1511     * @return group type
1512     */
1513    @Override protected String getGroupType() {
1514        return AddeServer.TYPE_IMAGE;
1515    }
1516
1517    /**
1518     * Does this selector have all of its state set to load in data
1519     * 
1520     * @return Has the user chosen everything they need to choose to load data
1521     */
1522    @Override protected boolean getGoodToGo() {
1523        // if(!super.getGoodToGo()) return false;
1524        if (getDoAbsoluteTimes()) {
1525            return getHaveAbsoluteTimesSelected();
1526        } else {
1527            return canReadTimes() && (lastAD != null);
1528        }
1529    }
1530
1531    /**
1532     * Returns a list of the images to load or null if none have been selected.
1533     * 
1534     * @return list get the list of image descriptors
1535     */
1536    @Override public List getImageList() {
1537        if (!timesOk()) {
1538            return null;
1539        }
1540        List images = new ArrayList();
1541        String defaultBand = getDefault(PROP_BAND, ALL);
1542        try {
1543            if (getDoRelativeTimes()) {
1544                AddeImageDescriptor firstDescriptor = (AddeImageDescriptor) imageDescriptors.get(0);
1545                AreaDirectory ad = firstDescriptor.getDirectory();
1546                int[] bands    = (int[]) bandTable.get(ad);
1547                bandInfos = makeBandInfos(ad, bands);
1548                int[] relativeTimesIndices = getRelativeTimeIndices();
1549                for (int i = 0; i < relativeTimesIndices.length; i++) {
1550                    AddeImageDescriptor aid = new AddeImageDescriptor(relativeTimesIndices[i], firstDescriptor);
1551                    AddeImageInfo aii = makeImageInfo(aid.getDirectory(), true, relativeTimesIndices[i]);
1552                    aii.setDebug(EntryStore.isAddeDebugEnabled(false));
1553                    if (aii.getBand() == null) aii.setBand(defaultBand);
1554                    aid.setImageInfo(aii);
1555                    aid.setSource(aii.getURLString());
1556                    images.add(aid);
1557                }
1558            } else {
1559                List selectedTimes = getSelectedAbsoluteTimes();
1560                for (int i = 0; i < selectedTimes.size(); i++) {
1561                    AddeImageDescriptor aid = new AddeImageDescriptor((AddeImageDescriptor) selectedTimes.get(i));
1562                    AddeImageInfo aii = makeImageInfo(aid.getDirectory(), false, i);
1563                    aii.setDebug(EntryStore.isAddeDebugEnabled(false));
1564                    if (aii.getBand() == null) aii.setBand(defaultBand);
1565                    aid.setImageInfo(aii);
1566                    aid.setSource(aii.getURLString());
1567                    images.add(aid);
1568                }
1569            }
1570        } catch (Exception exc) {
1571            logException("Error occured", exc);
1572            return null;
1573        }
1574        return images;
1575    }
1576
1577    /**
1578     * Process the image defaults resources
1579     */
1580    protected void initializeAddeDefaults() {
1581        resourceMaps = new ArrayList();
1582        if (addeDefaults == null) {
1583            return;
1584        }
1585        for (int resourceIdx = 0; resourceIdx < addeDefaults.size(); resourceIdx++) {
1586            Element root = addeDefaults.getRoot(resourceIdx);
1587            if (root == null) {
1588                continue;
1589            }
1590            Hashtable resourceMap = new Hashtable();
1591            resourceMaps.add(resourceMap);
1592
1593            XmlNodeList defaultNodes = XmlUtil.getElements(root, TAG_DEFAULT);
1594            for (int nodeIdx = 0; nodeIdx < defaultNodes.size(); nodeIdx++) {
1595                Element dfltNode = (Element) defaultNodes.item(nodeIdx);
1596                String pattern = XmlUtil.getAttribute(dfltNode, ATTR_PATTERN,
1597                        (String) null);
1598                if (pattern == null) {
1599                    pattern = XmlUtil.getAttribute(dfltNode, ATTR_NAME);
1600                }
1601                if (pattern != null) {
1602                    pattern = pattern.toLowerCase();
1603                }
1604                resourceMap.put(pattern, dfltNode);
1605            }
1606        }
1607    }
1608
1609    /**
1610     * Get the default value for a key
1611     * 
1612     * @param property
1613     *            property (key type)
1614     * @param dflt
1615     *            default value
1616     * @return value for key or dflt if not found
1617     */
1618    protected String getDefault(String property, String dflt) {
1619        if (resourceMaps == null) {
1620            initializeAddeDefaults();
1621        }
1622        property = property.toLowerCase();
1623
1624        String userDefault = null;
1625        String server = getServer();
1626        String group = getGroup();
1627        String descriptor = getDescriptor();
1628        String[] keys = { userDefault, server + ":" + group + "/" + descriptor,
1629                group + "/" + descriptor, server + ":" + group + "/*",
1630                group + "/*", server + ":*/" + descriptor, "*/" + descriptor,
1631                descriptor, server + ":*/*", server, "*" };
1632
1633        if (server != null) {
1634            if (PROP_USER.equals(property))
1635                return getLastAddedUser();
1636            if (PROP_PROJ.equals(property))
1637                return getLastAddedProj();
1638        }
1639
1640        for (int resourceIdx = 0; resourceIdx < resourceMaps.size(); resourceIdx++) {
1641            Hashtable resourceMap = (Hashtable) resourceMaps.get(resourceIdx);
1642            for (int keyIdx = 0; keyIdx < keys.length; keyIdx++) {
1643                String key = keys[keyIdx];
1644                if (key == null) {
1645                    continue;
1646                }
1647                key = key.toLowerCase();
1648                Element defaultNode = (Element)resourceMap.get(key);
1649                if (defaultNode == null) {
1650                    continue;
1651                }
1652                String value = XmlUtil.getAttribute(defaultNode, property, (String)null);
1653                if (value == null) {
1654                    continue;
1655                }
1656                if (value.equals(VALUE_DEFAULT)) {
1657                    return dflt;
1658                } else {
1659                    return value;
1660                }
1661            }
1662        }
1663        return dflt;
1664    }
1665
1666    /**
1667     * Get the image size string from the directory and defaults
1668     * 
1669     * @param ad
1670     *            image directory
1671     * @return request size
1672     */
1673    protected String getSizeString(AreaDirectory ad) {
1674        String retString = MAX_SIZE + " " + MAX_SIZE;
1675        if (ad != null) {
1676            int x = ad.getElements();
1677            int y = ad.getLines();
1678            if ((x < MAX_SIZE) && (y < MAX_SIZE)) {
1679                retString = x + " " + y;
1680            } else if ((x >= MAX_SIZE) && (y >= MAX_SIZE)) {
1681                retString = MAX_SIZE + " " + MAX_SIZE;
1682            } else if ((x >= MAX_SIZE) && (y < MAX_SIZE)) {
1683                retString = MAX_SIZE + " " + y;
1684            } else if ((x < MAX_SIZE) && (y >= MAX_SIZE)) {
1685                retString = x + " " + MAX_SIZE;
1686            }
1687        }
1688        return retString;
1689    }
1690
1691    /**
1692     * Check for valid lat/lon values
1693     * 
1694     * @return true if values are valid
1695     */
1696    protected boolean checkForValidValues() {
1697        if (usePropFromUser(PROP_LOC)) {
1698            if (useLatLon()) {
1699                String msg = latLonWidget.isValidValues();
1700                if ((msg != null) && (msg.length() > 0)) {
1701                    LogUtil.userMessage(msg);
1702                    return false;
1703                }
1704            }
1705        }
1706        return true;
1707    }
1708
1709    /**
1710     * Get the list of properties for the base URL
1711     * 
1712     * @return list of properties
1713     */
1714    protected String[] getBaseUrlProps() {
1715        return new String[] { PROP_DESCR, PROP_UNIT, PROP_SPAC, PROP_BAND,
1716                PROP_NAV, PROP_USER, PROP_PROJ, };
1717    }
1718
1719    /**
1720     * A utility that creates the url argument line for the given set of
1721     * properties.
1722     * 
1723     * @param props
1724     *            The PROP_ properties to make the request string for
1725     * @param ad
1726     *            The area directory.
1727     * 
1728     * @return The adde request string
1729     */
1730    protected String makeProps(String[] props, AreaDirectory ad) {
1731        StringBuffer buf = new StringBuffer();
1732        for (int propIdx = 0; propIdx < props.length; propIdx++) {
1733            appendKeyValue(buf, props[propIdx],
1734                    getPropValue(props[propIdx], ad));
1735        }
1736        return buf.toString();
1737    }
1738
1739    /**
1740     * Get the value for the given property. This can either be the value
1741     * supplied by the end user through the advanced GUI or is the default
1742     * 
1743     * @param prop
1744     *            The property
1745     * @param ad
1746     *            The AreaDirectory
1747     * 
1748     * @return The value of the property to use in the request string
1749     */
1750    protected String getPropValue(String prop, AreaDirectory ad) {
1751        if (usePropFromUser(prop)) {
1752            return getUserPropValue(prop, ad);
1753        }
1754
1755        // Handle size specially because we really want to get the minimum of
1756        // the default and the ad size
1757        if (PROP_SIZE.equals(prop)) {
1758            int[] size = getSize(ad);
1759            return size[0] + " " + size[1];
1760        }
1761
1762        return getDefault(prop, getDefaultPropValue(prop, ad, false));
1763    }
1764
1765    /**
1766     * Get the user supplied property value for the adde request string
1767     * 
1768     * @param prop
1769     *            The property
1770     * @param ad
1771     *            The AreaDirectory
1772     * 
1773     * @return The value, supplied by the user, of the property to use in the
1774     *         request string
1775     */
1776    protected String getUserPropValue(String prop, AreaDirectory ad) {
1777        if (PROP_LATLON.equals(prop) && (latLonWidget != null)) {
1778            // apparently the ADDE server can't handle long numbers
1779            return Format.dfrac(latLonWidget.getLat(), 5) + " "
1780                    + Format.dfrac(latLonWidget.getLon(), 5);
1781        }
1782        if (PROP_PLACE.equals(prop) && (placeLbl != null)) {
1783            return place;
1784        }
1785
1786        if (PROP_LINELE.equals(prop) && (centerLineFld != null)) {
1787            return centerLineFld.getText().trim() + " "
1788                    + centerElementFld.getText().trim();
1789        }
1790
1791        if (PROP_SIZE.equals(prop) && (numLinesFld != null)) {
1792            return numLinesFld.getText().trim() + " "
1793                    + numElementsFld.getText().trim();
1794        }
1795        if (PROP_MAG.equals(prop) && (lineMagSlider != null)) {
1796            return getLineMagValue() + " " + getElementMagValue();
1797        }
1798        if (PROP_BAND.equals(prop) && (bandComboBox != null)) {
1799
1800            Object selected = bandComboBox.getSelectedItem();
1801            if (selected != null) {
1802                if (selected.equals(ALLBANDS)) {
1803                    return ALLBANDS.toString();
1804                } else {
1805                    return "" + ((BandInfo) selected).getBandNumber();
1806                }
1807            }
1808        }
1809        if (PROP_UNIT.equals(prop)) {
1810            return getSelectedUnit();
1811        }
1812        if (PROP_NAV.equals(prop)) {
1813            return TwoFacedObject.getIdString(navComboBox.getSelectedItem());
1814        }
1815
1816        if (PROP_USER.equals(prop))
1817            return getLastAddedUser();
1818        if (PROP_PROJ.equals(prop))
1819            return getLastAddedProj();
1820
1821        return null;
1822    }
1823
1824    /**
1825     * Get the default property value for the adde request string
1826     * 
1827     * @param prop
1828     *            The property
1829     * @param ad
1830     *            The AreaDirectory
1831     * @param forDisplay
1832     *            Is this to display to the user in the gui
1833     * 
1834     * @return The default of the property to use in the request string
1835     */
1836    protected String getDefaultPropValue(String prop, AreaDirectory ad,
1837            boolean forDisplay) {
1838        if (PROP_USER.equals(prop)) {
1839            return DEFAULT_USER;
1840        }
1841        if (PROP_USER.equals(prop)) {
1842            return PLACE_CENTER;
1843        }
1844        if (PROP_PROJ.equals(prop)) {
1845            return DEFAULT_PROJ;
1846        }
1847        if (PROP_DESCR.equals(prop)) {
1848            return getDescriptor();
1849        }
1850        if (PROP_VERSION.equals(prop)) {
1851            return DEFAULT_VERSION;
1852        }
1853        if (PROP_COMPRESS.equals(prop)) {
1854            return "gzip";
1855        }
1856        if (PROP_PORT.equals(prop)) {
1857            return DEFAULT_PORT;
1858        }
1859        if (PROP_DEBUG.equals(prop)) {
1860//          return DEFAULT_DEBUG;
1861            Boolean.toString(EntryStore.isAddeDebugEnabled(false));
1862        }
1863        if (PROP_SIZE.equals(prop)) {
1864            if (ad != null) {
1865                return ad.getLines() + " " + ad.getElements();
1866            }
1867            return MAX_SIZE + " " + MAX_SIZE;
1868        }
1869        if (PROP_MAG.equals(prop)) {
1870            return "1 1";
1871        }
1872        // if (prop.equals(PROP_LOC) || prop.equals(PROP_LINELE)) {
1873        if (PROP_MAG.equals(prop)) {
1874            if (ad == null) {
1875                return "0 0";
1876            }
1877            return ad.getLines() / 2 + " " + ad.getElements() / 2;
1878        }
1879        // if (prop.equals(PROP_LATLON)) {
1880        if (PROP_LOC.equals(prop) || PROP_LATLON.equals(prop)) {
1881            if (ad == null) {
1882                return "0 0";
1883            }
1884            return ad.getCenterLatitude() + " " + ad.getCenterLongitude();
1885        }
1886        if (PROP_BAND.equals(prop)) {
1887            if (bandTable==null) return "";
1888            if (forDisplay) {
1889                return getBandName(ad, ((int[]) bandTable.get(ad))[0]);
1890            }
1891            return "" + ((int[]) bandTable.get(ad))[0];
1892        }
1893        if (PROP_SPAC.equals(prop)) {
1894            return getSelectedUnit().equalsIgnoreCase("BRIT") ? "1" : "4";
1895        }
1896        if (PROP_UNIT.equals(prop)) {
1897            return "X";
1898        }
1899        if (PROP_NAV.equals(prop)) {
1900            return "X";
1901        }
1902        return "";
1903    }
1904
1905    /**
1906     * Set the properties on the AddeImageInfo from the list of properties
1907     * 
1908     * @param aii
1909     *            The AddeImageInfo
1910     * @param props
1911     *            list of props to set
1912     * @param ad
1913     *            The AreaDirectory
1914     */
1915    protected void setImageInfoProps(AddeImageInfo aii, String[] props, AreaDirectory ad) {
1916        for (int i = 0; i < props.length; i++) {
1917            String prop = props[i];
1918            String value = getPropValue(prop, ad);
1919            if (prop.equals(PROP_USER)) {
1920                aii.setUser(value);
1921            } else if (prop.equals(PROP_PROJ)) {
1922                aii.setProject(Integer.parseInt(value));
1923            } else if (prop.equals(PROP_DESCR)) {
1924                aii.setDescriptor(value);
1925            } else if (prop.equals(PROP_VERSION)) {
1926                aii.setVersion(value);
1927            } else if (prop.equals(PROP_COMPRESS)) {
1928                int compVal = AddeURL.GZIP;
1929                if (value.equals("none") || value.equals("1")) {
1930                    compVal = AddeURL.NO_COMPRESS;
1931                } else if (value.equals("compress") || value.equals("2") || value.equals("true")) {
1932                    compVal = AddeURL.COMPRESS;
1933                }
1934                aii.setCompression(compVal);
1935            } else if (prop.equals(PROP_PORT)) {
1936                aii.setPort(Integer.parseInt(value));
1937            } else if (prop.equals(PROP_DEBUG)) {
1938//              aii.setDebug(Boolean.getBoolean(value));
1939                aii.setDebug(EntryStore.isAddeDebugEnabled(false));
1940            } else if (prop.equals(PROP_SPAC)) {
1941                aii.setSpacing(Integer.parseInt(value));
1942            } else if (prop.equals(PROP_UNIT)) {
1943                if (value.equals(ALLUNITS.getId())) {
1944                    value = getDefault(prop, getDefaultPropValue(prop, ad, false));
1945                }
1946                aii.setUnit(value);
1947            } else if (prop.equals(PROP_BAND)) {
1948                if (value.equals(ALLBANDS.toString())) {
1949                    value = getDefault(prop, getDefaultPropValue(prop, ad, false));
1950                }
1951                aii.setBand(value);
1952            } else if (prop.equals(PROP_NAV)) {
1953                aii.setNavType(value);
1954            } else if (prop.equals(PROP_ID)) {
1955                aii.setId(value);
1956            }
1957        }
1958    }
1959
1960    /**
1961     * Get the name of the selected band
1962     * 
1963     * @return the name of the band
1964     */
1965    public String getSelectedBandName() {
1966        return getBandName(propertiesAD, getSelectedBand());
1967    }
1968
1969    /**
1970     * Clear the properties widgets
1971     */
1972    protected void clearPropertiesWidgets() {
1973        if (latLonWidget != null) {
1974            latLonWidget.getLatField().setText("");
1975            latLonWidget.getLonField().setText("");
1976        }
1977        if (centerLineFld != null) {
1978            centerLineFld.setText("");
1979            centerElementFld.setText("");
1980        }
1981        if (numLinesFld != null) {
1982            if (sizeLbl != null) {
1983                sizeLbl.setText("");
1984            }
1985            numLinesFld.setText("");
1986            numElementsFld.setText("");
1987        }
1988        if (unitComboBox != null) {
1989            GuiUtils.setListData(unitComboBox, new Vector());
1990        }
1991        if (bandComboBox != null) {
1992            GuiUtils.setListData(bandComboBox, new Vector());
1993        }
1994
1995        setMagSliders(DEFAULT_MAG, DEFAULT_MAG);
1996
1997        if (placeLbl != null) {
1998            changePlace(PLACE_CENTER);
1999        }
2000
2001        if (navComboBox != null) {
2002            checkSetNav();
2003//          navComboBox.setSelectedIndex(0);
2004        }
2005        baseNumLines = 0.0;
2006        baseNumElements = 0.0;
2007    }
2008
2009    /**
2010     * Set the widgets with the state from the given AreaDirectory
2011     * 
2012     * @param ad
2013     *            AreaDirectory for the image
2014     */
2015    protected void setPropertiesState(AreaDirectory ad) {
2016        setPropertiesState(ad, false);
2017    }
2018
2019    /**
2020     * _more_
2021     * 
2022     * @param ad
2023     *            _more_
2024     * 
2025     * @return _more_
2026     */
2027    protected int[] getSize(AreaDirectory ad) {
2028        baseNumLines = ad.getLines();
2029        baseNumElements = ad.getElements();
2030
2031        String sizeDefault = getDefault(PROP_SIZE, (String) null);
2032        List toks = ((sizeDefault != null) ? StringUtil.split(sizeDefault, " ",
2033                true, true) : null);
2034        if ((toks == null) || (toks.size() == 0)) {
2035            return new int[] { (int) baseNumLines, (int) baseNumElements };
2036        } else {
2037            String lines = "" + toks.get(0);
2038            if (lines.equalsIgnoreCase(ALL)) {
2039                lines = "" + (int) baseNumLines;
2040            }
2041            int numLines = new Integer(lines.trim()).intValue();
2042
2043            String elems = (toks.size() > 1) ? "" + toks.get(1) : ""
2044                    + (int) baseNumElements;
2045            if (elems.equalsIgnoreCase(ALL)) {
2046                elems = "" + baseNumElements;
2047            }
2048            int numElements = new Integer(elems.trim()).intValue();
2049            return new int[] { (int) Math.min(numLines, baseNumLines),
2050                    (int) Math.min(numElements, baseNumElements) };
2051        }
2052
2053    }
2054
2055    /**
2056     * Set the widgets with the state from the given AreaDirectory
2057     * 
2058     * @param ad
2059     *            AreaDirectory for the image
2060     * @param force
2061     *            force an update regardless of the previous invocation
2062     */
2063    protected void setPropertiesState(AreaDirectory ad, boolean force) {
2064        if (amSettingProperties)
2065            return;
2066
2067        prevPropertiesAD = propertiesAD;
2068        propertiesAD = ad;
2069        if (!force && checkPropertiesEqual(prevPropertiesAD, propertiesAD))
2070            return;
2071
2072        amSettingProperties = true;
2073
2074        if (ad == null) {
2075            clearPropertiesWidgets();
2076            amSettingProperties = false;
2077            return;
2078        }
2079
2080        String[] propArray = getAdvancedProps();
2081
2082        if (numLinesFld != null) {
2083            int[] size = getSize(ad);
2084            numLinesFld.setText("" + size[0]);
2085            numElementsFld.setText("" + size[1]);
2086            if (sizeLbl != null) {
2087                String label = "  Raw size: " + ad.getLines() + " X "
2088                        + ad.getElements();
2089                sizeLbl.setText(label);
2090            }
2091        }
2092        if (latLonWidget != null) {
2093            latLonWidget.getLatField().setText("" + ad.getCenterLatitude());
2094            latLonWidget.getLonField().setText("" + ad.getCenterLongitude());
2095        }
2096        if (centerLineFld != null) {
2097            centerLineFld.setText("" + ad.getLines() / 2);
2098            centerElementFld.setText("" + ad.getElements() / 2);
2099        }
2100
2101        List<BandInfo> bandList = null;
2102        int[] bands = (int[]) bandTable.get(ad);
2103        if (bands != null)
2104            bandList = makeBandInfos(ad, bands);
2105        bandInfos = bandList;
2106
2107        if (bandComboBox != null) {
2108            List comboList = bandList;
2109            if (bandList.size() > 1) {
2110                comboList = new ArrayList();
2111                comboList.addAll(bandList);
2112                comboList.add(ALLBANDS);
2113            }
2114            GuiUtils.setListData(bandComboBox, comboList);
2115        }
2116
2117        setAvailableUnits(ad, getSelectedBand());
2118
2119        for (int propIdx = 0; propIdx < propArray.length; propIdx++) {
2120            String prop = propArray[propIdx];
2121            String value = getDefault(prop, getDefaultPropValue(prop, ad, false));
2122            if (value == null)
2123                value = "";
2124
2125            value = value.trim();
2126            if (prop.equals(PROP_LOC)) {
2127                String key = getDefault(PROP_KEY, PROP_LATLON);
2128
2129                boolean usingLineElement = key.equals(PROP_LINELE);
2130                if (usingLineElement) {
2131                    locationPanel.show(1);
2132                } else {
2133                    locationPanel.show(0);
2134                }
2135                if (usingLineElement) {
2136                    value = getDefault(PROP_LOC, getDefaultPropValue(
2137                            PROP_LINELE, ad, false));
2138                } else {
2139                    value = getDefault(PROP_LOC, getDefaultPropValue(
2140                            PROP_LATLON, ad, false));
2141                }
2142                String[] pair = getPair(value);
2143                if (pair != null) {
2144                    if (usingLineElement) {
2145                        centerLineFld.setText(pair[0]);
2146                        centerElementFld.setText(pair[1]);
2147                    } else {
2148                        latLonWidget.setLat(pair[0]);
2149                        latLonWidget.setLon(pair[1]);
2150                    }
2151                }
2152            } else if (prop.equals(PROP_BAND)) {
2153                if (value.equalsIgnoreCase((String) ALLBANDS.getId())) {
2154                    bandComboBox.setSelectedItem(ALLBANDS);
2155                } else {
2156                    int bandNum = 0;
2157                    try {
2158                        bandNum = Integer.parseInt(value);
2159                    } catch (NumberFormatException nfe) {
2160                    }
2161                    int index = BandInfo.findIndexByNumber(bandNum, bandList);
2162                    if (index != -1) {
2163                        bandComboBox.setSelectedIndex(index);
2164                    }
2165                }
2166            } else if (prop.equals(PROP_PLACE)) {
2167                changePlace(value);
2168            } else if (prop.equals(PROP_MAG)) {
2169                String[] pair = getPair(value);
2170                if (pair != null) {
2171                    setMagSliders(new Integer(pair[0]).intValue(), new Integer(
2172                            pair[1]).intValue());
2173                } else {
2174                    setMagSliders(DEFAULT_MAG, DEFAULT_MAG);
2175                }
2176            } else if (prop.equals(PROP_NAV)) {
2177                if (navComboBox != null) {
2178                    navComboBox.setSelectedIndex((value
2179                            .equalsIgnoreCase("LALO") ? 1 : 0));
2180                }
2181                checkSetNav();
2182            }
2183        }
2184        amSettingProperties = false;
2185
2186    }
2187
2188    /**
2189     * Set the mag slider values
2190     * 
2191     * @param lineValue
2192     *            the line value
2193     * @param elementValue
2194     *            the element value
2195     */
2196    protected void setMagSliders(int lineValue, int elementValue) {
2197        if (lineMagSlider != null) {
2198            if (lineValue > 0) {
2199                lineValue--;
2200            } else if (lineValue < 0) {
2201                lineValue++;
2202            }
2203            if (elementValue > 0) {
2204                elementValue--;
2205            } else if (elementValue < 0) {
2206                elementValue++;
2207            }
2208
2209            lineMagSlider.setValue(lineValue);
2210            elementMagSlider.setValue(elementValue);
2211            lineMagLbl.setText(StringUtil.padLeft("" + getLineMagValue(), 4));
2212            elementMagLbl.setText(StringUtil.padLeft("" + getElementMagValue(),
2213                    4));
2214            linesToElements = Math.abs(lineValue / (double) elementValue);
2215            if (Double.isNaN(linesToElements)) {
2216                linesToElements = 1.0;
2217            }
2218        }
2219    }
2220
2221    /**
2222     * Get the value of the given magnification slider.
2223     * 
2224     * @param slider
2225     *            The slider to get the value from
2226     * @return The magnification value
2227     */
2228    protected int getMagValue(JSlider slider) {
2229        // Value is [-SLIDER_MAX,SLIDER_MAX]. We change 0 and -1 to 1
2230        int value = slider.getValue();
2231        if (value >= 0) {
2232            return value + 1;
2233        }
2234        return value - 1;
2235    }
2236
2237    /**
2238     * Get a pair of properties
2239     * 
2240     * @param v
2241     *            a space separated string
2242     * 
2243     * @return an array of the two strings
2244     */
2245    protected String[] getPair(String v) {
2246        if (v == null) {
2247            return null;
2248        }
2249        v = v.trim();
2250        List toks = StringUtil.split(v, " ", true, true);
2251        if ((toks == null) || (toks.size() == 0)) {
2252            return null;
2253        }
2254        String tok1 = toks.get(0).toString();
2255        return new String[] { tok1,
2256                ((toks.size() > 1) ? toks.get(1).toString() : tok1) };
2257
2258    }
2259
2260    /**
2261     * Get the selected band from the advanced chooser
2262     * 
2263     * @return selected band number
2264     */
2265    protected int getSelectedBand() {
2266
2267        Object bi = (bandComboBox == null) ? null : bandComboBox
2268                .getSelectedItem();
2269        if ((bi == null) || bi.equals(ALLBANDS)) {
2270            return 0;
2271        }
2272        return ((BandInfo) bi).getBandNumber();
2273    }
2274
2275    /**
2276     * Translate a place name into a human readable form
2277     * 
2278     * @param place
2279     *            raw name
2280     * 
2281     * @return human readable name
2282     */
2283    protected String translatePlace(String place) {
2284        place = place.toUpperCase();
2285        if (place.equals(PLACE_ULEFT)) {
2286            return "Upper left";
2287        }
2288        if (place.equals(PLACE_LLEFT)) {
2289            return "Lower left";
2290        }
2291        if (place.equals(PLACE_URIGHT)) {
2292            return "Upper right";
2293        }
2294        if (place.equals(PLACE_LRIGHT)) {
2295            return "Lower right";
2296        }
2297        if (place.equals(PLACE_CENTER)) {
2298            return "Center";
2299        }
2300        return place;
2301    }
2302
2303    /**
2304     * Get the band name for a particular area
2305     * 
2306     * @param ad
2307     *            AreaDirectory
2308     * @param band
2309     *            band number
2310     * 
2311     * @return name of the band
2312     */
2313    protected String getBandName(AreaDirectory ad, int band) {
2314        // if (band== 0) return ALLBANDS.toString();
2315
2316        if (useSatBandInfo) {
2317            if (satBandInfo == null) {
2318                return "Band: " + band;
2319            }
2320            String[] descrs = satBandInfo.getBandDescr(ad.getSensorID(), ad
2321                    .getSourceType());
2322            if (descrs != null) {
2323                if ((band >= 0) && (band < descrs.length)) {
2324                    return descrs[band];
2325                }
2326            }
2327            return "Band: " + band;
2328        }
2329
2330        if (sensorToBandToName == null) {
2331            return "Band: " + band;
2332        }
2333        Hashtable bandToName = (Hashtable) sensorToBandToName.get(new Integer(
2334                ad.getSensorID()));
2335        String name = null;
2336        Integer bandInteger = new Integer(band);
2337
2338        if (bandToName != null) {
2339            name = (String) bandToName.get(bandInteger);
2340        }
2341        if (name == null) {
2342            name = "Band: " + band;
2343        }
2344        /*
2345         * else { name = band + " - " + name.trim(); }
2346         */
2347        return name;
2348    }
2349
2350    /**
2351     * Set the available units in the unit selector
2352     * 
2353     * @param ad
2354     *            AreaDirectory for the image
2355     * @param band
2356     *            band to use for units
2357     */
2358    protected void setAvailableUnits(AreaDirectory ad, int band) {
2359        List l = getAvailableUnits(ad, band);
2360        l.add(ALLUNITS);
2361        GuiUtils.setListData(unitComboBox, l);
2362        TwoFacedObject tfo = null;
2363        if ((bandComboBox != null) && (getSelectedBand() == 0)) {
2364            tfo = ALLUNITS;
2365        } else {
2366            String preferredUnit = getDefault(PROP_UNIT, "BRIT");
2367            tfo = TwoFacedObject.findId(preferredUnit, l);
2368        }
2369        if (tfo != null) {
2370            unitComboBox.setSelectedItem(tfo);
2371        }
2372    }
2373
2374    /**
2375     * Set the available units in the unit selector
2376     * 
2377     * @param ad
2378     *            AreaDirectory for the image
2379     * @param band
2380     *            band to use for units
2381     * 
2382     * @return List of available units
2383     */
2384    protected List<TwoFacedObject> getAvailableUnits(AreaDirectory ad, int band) {
2385        // get Vector array of Calibration types. Layout is
2386        // v[i] = band[i] and for each band, it is a vector of
2387        // strings of calibration names and descriptions
2388        // n = name, n+1 = desc.
2389        // for radar, we only have one band
2390        if (ad == null) {
2391            return new ArrayList<>();
2392        }
2393        int[] bands = (int[]) bandTable.get(ad);
2394        int index = (bands == null) ? 0 : Arrays.binarySearch(bands, band);
2395        if (index < 0) index = 0;
2396        Vector<TwoFacedObject> l = new Vector<>();
2397        Vector v = ad.getCalInfo()[index];
2398        TwoFacedObject tfo = null;
2399        int preferredUnitIndex = 0;
2400        String preferredUnit = getDefault(PROP_UNIT, "BRIT");
2401        if ((v != null) && (v.size() / 2 > 0)) {
2402            for (int i = 0; i < v.size() / 2; i++) {
2403                String name = (String) v.get(2 * i);
2404                String desc = (String) v.get(2 * i + 1);
2405                desc = StringUtil.camelCase(desc);
2406                tfo = new TwoFacedObject(desc, name);
2407                l.add(tfo);
2408                if (name.equalsIgnoreCase(preferredUnit)) {
2409                    preferredUnitIndex = i;
2410                }
2411            }
2412        } else {
2413            l.add(new TwoFacedObject("Raw Value", "RAW"));
2414        }
2415        return l;
2416    }
2417
2418    /**
2419     * Get the band name information from the server
2420     */
2421    protected void readSatBands() {
2422        satBandInfo = null;
2423        sensorToBandToName = null;
2424        List lines = null;
2425        try {
2426            StringBuffer buff = getUrl(REQ_TEXT);
2427            appendKeyValue(buff, PROP_FILE, FILE_SATBAND);
2428            lines = readTextLines(buff.toString());
2429            if (lines == null) {
2430                return;
2431            }
2432            if (useSatBandInfo) {
2433                satBandInfo = new AddeSatBands(StringUtil.listToStringArray(lines));
2434                return;
2435            }
2436        } catch (Exception e) {
2437            return;
2438        }
2439
2440        sensorToBandToName = new Hashtable();
2441
2442        for (int i = 0; i < lines.size(); i++) {
2443            if (!lines.get(i).toString().startsWith("Sat")) {
2444                continue;
2445            }
2446            List satIds = StringUtil.split(lines.get(i).toString(), " ", true,
2447                    true);
2448            satIds.remove(0);
2449            Hashtable bandToName = new Hashtable();
2450            for (int j = i + 1; j < lines.size(); j++, i++) {
2451                String line = lines.get(i).toString();
2452                line = line.trim();
2453                if (line.startsWith("EndSat")) {
2454                    break;
2455                }
2456
2457                int idx = line.indexOf(" ");
2458                if (idx < 0) {
2459                    continue;
2460                }
2461                String bandTok = line.substring(0, idx);
2462                try {
2463                    bandToName.put(Integer.decode(bandTok.trim()), line
2464                            .substring(idx).trim());
2465                } catch (NumberFormatException nfe) {
2466                }
2467            }
2468            for (int j = 0; j < satIds.size(); j++) {
2469                Integer sensorId = new Integer(satIds.get(j).toString());
2470                sensorToBandToName.put(sensorId, bandToName);
2471            }
2472        }
2473    }
2474
2475    /**
2476     * Make an AddeImageInfo from a URL and an AreaDirectory
2477     * 
2478     * @param dir
2479     *            AreaDirectory
2480     * @param isRelative
2481     *            true if is relative
2482     * @param num
2483     *            number (for relative images)
2484     * 
2485     * @return corresponding AddeImageInfo
2486     */
2487    protected AddeImageInfo makeImageInfo(AreaDirectory dir,
2488            boolean isRelative, int num) {
2489        AddeImageInfo info = new AddeImageInfo(getAddeServer().getName(),
2490                AddeImageInfo.REQ_IMAGEDATA, getGroup(), getDescriptor());
2491        if (isRelative) {
2492            info.setDatasetPosition((num == 0) ? 0 : -num);
2493        } else {
2494            info.setStartDate(dir.getNominalTime());
2495        }
2496        setImageInfoProps(info, getMiscKeyProps(), dir);
2497        setImageInfoProps(info, getBaseUrlProps(), dir);
2498
2499        String locKey = getDefault(PROP_KEY, PROP_LINELE);
2500        String locValue = null;
2501        if (usePropFromUser(PROP_LOC)) {
2502            if (useLatLon()) {
2503                locKey = PROP_LATLON;
2504                locValue = getUserPropValue(PROP_LATLON, dir);
2505            } else {
2506                locKey = PROP_LINELE;
2507                locValue = getUserPropValue(PROP_LINELE, dir);
2508            }
2509        } else {
2510            locValue = getPropValue(PROP_LOC, dir);
2511        }
2512        info.setLocateKey(locKey);
2513        info.setLocateValue(locValue);
2514
2515        String placeKey = getPropValue(PROP_PLACE, dir);
2516        info.setPlaceValue(placeKey);
2517
2518        String magKey = getPropValue(PROP_MAG, dir);
2519        int lmag = 1;
2520        int emag = 1;
2521        StringTokenizer tok = new StringTokenizer(magKey);
2522        lmag = (int) Misc.parseNumber((String) tok.nextElement());
2523        if (tok.hasMoreTokens()) {
2524            emag = (int) Misc.parseNumber((String) tok.nextElement());
2525        } else {
2526            emag = lmag;
2527        }
2528        info.setLineMag(lmag);
2529        info.setElementMag(emag);
2530
2531        int lines = dir.getLines();
2532        int elems = dir.getElements();
2533        String sizeKey = getPropValue(PROP_SIZE, dir);
2534        tok = new StringTokenizer(sizeKey);
2535        String size = (String) tok.nextElement();
2536        if (!size.equalsIgnoreCase("all")) {
2537            lines = (int) Misc.parseNumber(size);
2538            if (tok.hasMoreTokens()) {
2539                elems = (int) Misc.parseNumber((String) tok.nextElement());
2540            } else {
2541                elems = lines;
2542            }
2543        }
2544        info.setLines(lines);
2545        info.setElements(elems);
2546        /*
2547         * System.out.println("url = " + info.getURLString().toLowerCase() +
2548         * "\n");
2549         */
2550        return info;
2551    }
2552
2553    /**
2554     * Check to see if the two Area directories are equal
2555     * 
2556     * @param ad1
2557     *            first AD (may be null)
2558     * @param ad2
2559     *            second AD (may be null)
2560     * 
2561     * @return true if they are equal
2562     */
2563    protected boolean checkPropertiesEqual(AreaDirectory ad1, AreaDirectory ad2) {
2564        if (ad1 == null) {
2565            return false;
2566        }
2567        if (ad2 == null) {
2568            return false;
2569        }
2570        return Misc.equals(ad1, ad2)
2571                || ((ad1.getLines() == ad2.getLines())
2572                        && (ad1.getElements() == ad2.getElements()) && Arrays
2573                        .equals(ad1.getBands(), ad2.getBands()));
2574    }
2575
2576    /**
2577     * Get a description of the properties
2578     * 
2579     * @return a description
2580     */
2581    protected String getPropertiesDescription() {
2582        StringBuffer buf = new StringBuffer();
2583        String[] propArray = getAdvancedProps();
2584        List list = Misc.toList(propArray);
2585        if (list.contains(PROP_BAND)) {
2586            buf.append(getSelectedBandName());
2587            buf.append(", ");
2588        }
2589        if (list.contains(PROP_SIZE)) {
2590            buf.append("Size: ");
2591            String sizeKey = getUserPropValue(PROP_SIZE, propertiesAD);
2592            StringTokenizer tok = new StringTokenizer(sizeKey);
2593            if (tok.hasMoreTokens()) {
2594                String size = ((String) tok.nextElement()).trim();
2595                buf.append(size);
2596                buf.append("x");
2597                if (!size.equalsIgnoreCase("all")) {
2598                    if (tok.hasMoreTokens()) {
2599                        buf.append(((String) tok.nextElement()).trim());
2600                    } else {
2601                        buf.append(size);
2602                    }
2603                }
2604            }
2605        }
2606        return buf.toString();
2607    }
2608
2609    /**
2610     * Show the given error to the user. If it was an ADDE exception that was
2611     * a bad server error then print out a nice message.
2612     *
2613     * <p>Overridden in McIDAS-V to work with ADDE {@literal "archive"}
2614     * servers (servers that require a {@literal "DAY="} parameter).</p>
2615     *
2616     * @param e Exception to be handled. Cannot be {@code null}.
2617     */
2618    @Override protected void handleConnectionError(Exception e) {
2619        handleConnectionError("", e);
2620    }
2621
2622    /**
2623     * Show the given error to the user. If it was an ADDE exception that was
2624     * a bad server error then print out a nice message.
2625     *
2626     * <p>Overridden in McIDAS-V to work with ADDE {@literal "archive"}
2627     * servers (servers that require a {@literal "DAY="} parameter).</p>
2628     * 
2629     * @param details Details about the context of {@code e}. {@code null}
2630     * will be treated as an empty {@code String}.
2631     * @param e Exception to be handled. Cannot be {@code null}.
2632     */
2633    @Override protected void handleConnectionError(String details, Exception e) {
2634        if ((e != null) && (e.getMessage() != null)) {
2635            Throwable cause = e.getCause();
2636            if (cause.getMessage() != null) {
2637                String msg = cause.getMessage().toLowerCase();
2638                if ((cause instanceof AddeURLException) && msg.contains("must be used with archived datasets")) {
2639                    getArchiveDay();
2640                    return;
2641                }
2642            }
2643        }
2644        super.handleConnectionError(details, e);
2645    }
2646
2647    /**
2648     * Get the list of bands for the images
2649     * 
2650     * @param ad
2651     *            AreaDirectory
2652     * @param bands
2653     *            list of bands
2654     * @return list of BandInfos for the selected images
2655     */
2656    protected List<BandInfo> makeBandInfos(AreaDirectory ad, int[] bands) {
2657        // readSatBands();
2658        List<BandInfo> l = new ArrayList<>();
2659        if (ad != null) {
2660            if (bands != null) {
2661                for (int i = 0; i < bands.length; i++) {
2662                    int band = bands[i];
2663                    if (band > 0) {
2664                        BandInfo bi = new BandInfo(ad.getSensorID(), band);
2665                        bi.setBandDescription(getBandName(ad, band));
2666                        bi.setCalibrationUnits(getAvailableUnits(ad, band));
2667                        bi.setPreferredUnit(getDefault(PROP_UNIT, "BRIT"));
2668                        l.add(bi);
2669                    }
2670                }
2671            }
2672        }
2673        return l;
2674    }
2675    
2676    /**
2677     * Get the pregenerated bandInfos
2678     */
2679    protected List<BandInfo> getBandInfos() {
2680        return bandInfos;
2681    }
2682    
2683    /**
2684     * Get the list of BandInfos for the current selected images
2685     * 
2686     * @return list of BandInfos
2687     */
2688    public List<BandInfo> getSelectedBandInfos() {
2689        // update the BandInfo list based on what has been chosen
2690        List selectedBandInfos = new ArrayList<>();
2691        List selectedUnits = null;
2692        if (unitComboBox != null) {
2693            TwoFacedObject tfo = (TwoFacedObject) unitComboBox.getSelectedItem();
2694            if (!(tfo.equals(ALLUNITS))) { // specific unit requested
2695                selectedUnits = new ArrayList<>();
2696                selectedUnits.add(tfo);
2697            }
2698        }
2699        if (getSelectedBand() == 0) { // All bands selected
2700            if (selectedUnits != null) {
2701                for (Iterator iter = bandInfos.iterator(); iter.hasNext();) {
2702                    BandInfo newBI = new BandInfo((BandInfo) iter.next());
2703                    newBI.setCalibrationUnits(selectedUnits);
2704                    newBI.setPreferredUnit((String) ((TwoFacedObject) selectedUnits.get(0)).getId());
2705                    selectedBandInfos.add(newBI);
2706                }
2707            } else { // else All Bands, AllUnits
2708                selectedBandInfos = bandInfos;
2709            }
2710        } else { // not All selected;
2711            int index = BandInfo.findIndexByNumber(getSelectedBand(), bandInfos);
2712            BandInfo selectedBandInfo = null;
2713            if (index != -1) {
2714                selectedBandInfo = bandInfos.get(index);
2715            }
2716            if (selectedBandInfo != null) {
2717                if (selectedUnits != null) {
2718                    BandInfo newBI = new BandInfo(selectedBandInfo);
2719                    newBI.setCalibrationUnits(selectedUnits);
2720                    newBI.setPreferredUnit((String) ((TwoFacedObject) selectedUnits.get(0)).getId());
2721                    selectedBandInfos.add(newBI);
2722                } else {
2723                    selectedBandInfos.add(selectedBandInfo);
2724                }
2725            }
2726        }
2727        return selectedBandInfos;
2728    }
2729
2730    /**
2731     * Get the id for the default display type
2732     * 
2733     * @return the display id
2734     */
2735    @Override protected String getDefaultDisplayType() {
2736        return "imagedisplay";
2737    }
2738
2739    /**
2740     * User said go, we go. Simply get the list of images from the imageChooser
2741     * and create the ADDE.IMAGE DataSource
2742     * 
2743     */
2744    @Override public void doLoadInThread() {
2745        if (!checkForValidValues()) {
2746            return;
2747        }
2748        if (!getGoodToGo()) {
2749            updateStatus();
2750            return;
2751        }
2752
2753        List imageList = getImageList();
2754        if ((imageList == null) || (imageList.isEmpty())) {
2755            return;
2756        }
2757
2758        // Check for size threshold
2759        final int[] dim = { 0, 0 };
2760        AddeImageDescriptor aid = (AddeImageDescriptor) imageList.get(0);
2761        dim[0] = aid.getImageInfo().getElements();
2762        dim[1] = aid.getImageInfo().getLines();
2763        // System.err.println("dim:" + dim[0] + " x " + dim[1] + " # images:"
2764        // + imageList.size());
2765        int numPixels = dim[0] * dim[1] * imageList.size();
2766        double megs = (4 * numPixels) / (double) 1000000;
2767        
2768        //DAVEP: take this out--it should be warning in the data source, not the chooser
2769        boolean doSizeCheck = false;
2770        if (megs > SIZE_THRESHOLD && doSizeCheck) {
2771            final JCheckBox maintainSize = new JCheckBox(
2772                    "Maintain spatial extent", false);
2773            final JLabel sizeLbl = new JLabel(StringUtil.padRight("  "
2774                    + ((double) ((int) megs * 100)) / 100.0 + " MB", 14));
2775            GuiUtils.setFixedWidthFont(sizeLbl);
2776            final List[] listHolder = { imageList };
2777            final JSlider slider = new JSlider(2, (int) megs, (int) megs);
2778            slider.setMajorTickSpacing((int) (megs - 2) / 10);
2779            slider.setMinorTickSpacing((int) (megs - 2) / 10);
2780            // slider.setPaintTicks(true);
2781            slider.setSnapToTicks(true);
2782            final long timeNow = System.currentTimeMillis();
2783            ChangeListener sizeListener = new javax.swing.event.ChangeListener() {
2784                public void stateChanged(ChangeEvent evt) {
2785                    // A hack so we don't respond to the first event that we get
2786                    // from the slider when
2787                    // the dialog is first shown
2788                    if (System.currentTimeMillis() - timeNow < 500)
2789                        return;
2790                    JSlider slider = (JSlider) evt.getSource();
2791                    int pixelsPerImage = 1000000 * slider.getValue()
2792                            / listHolder[0].size() / 4;
2793                    double aspect = dim[1] / (double) dim[0];
2794                    int nx = (int) Math.sqrt(pixelsPerImage / aspect);
2795                    int ny = (int) (aspect * nx);
2796                    if (maintainSize.isSelected()) {
2797                        // doesn't work
2798                        lineMagSlider.setValue(getLineMagValue() - 1);
2799                        lineMagSliderChanged(true);
2800                    } else {
2801                        numElementsFld.setText("" + nx);
2802                        numLinesFld.setText("" + ny);
2803                    }
2804                    listHolder[0] = getImageList();
2805                    AddeImageDescriptor aid = (AddeImageDescriptor) listHolder[0]
2806                            .get(0);
2807                    dim[0] = aid.getImageInfo().getElements();
2808                    dim[1] = aid.getImageInfo().getLines();
2809                    int numPixels = dim[0] * dim[1] * listHolder[0].size();
2810                    double nmegs = (4 * numPixels) / (double) 1000000;
2811                    sizeLbl.setText(StringUtil.padRight("  "
2812                            + ((double) ((int) nmegs * 100)) / 100.0 + " MB",
2813                            14));
2814                }
2815            };
2816            slider.addChangeListener(sizeListener);
2817
2818            JComponent msgContents = GuiUtils
2819                    .vbox(
2820                            new JLabel(
2821                                    "<html>You are about to load "
2822                                            + megs
2823                                            + " MB of imagery.<br>Are you sure you want to do this?<p><hr><p></html>"),
2824                            GuiUtils.inset(GuiUtils.leftCenterRight(new JLabel(
2825                                    "Change Size: "),
2826                                    GuiUtils.inset(slider, 5), sizeLbl), 5));
2827
2828            if (!GuiUtils.askOkCancel("Image Size", msgContents)) {
2829                return;
2830            }
2831            imageList = listHolder[0];
2832        }
2833
2834        ImageDataset ids = new ImageDataset(getDatasetName(), imageList);
2835        // make properties Hashtable to hand the station name
2836        // to the AddeImageDataSource
2837        Hashtable ht = new Hashtable();
2838        ht.put(DataSelection.PROP_CHOOSERTIMEMATCHING, getDoTimeDrivers());
2839        getDataSourceProperties(ht);
2840        Object bandName = getSelectedBandName();
2841        if (bandName != null && !(bandName.equals(ALLBANDS.toString()))) {
2842            ht.put(DATA_NAME_KEY, bandName);
2843        }
2844        ht.put("allBands", bandDirs);
2845        makeDataSource(ids, getDataSourceId(), ht);
2846        saveServerState();
2847        // uncheck the check box every time click the add source button
2848        drivercbx.setSelected(false);
2849        enableTimeWidgets();
2850        setDoTimeDrivers(false);
2851    }
2852    
2853    /**
2854     * Return the data source ID.  Used by extending classes.
2855     */
2856    @Override protected String getDataSourceId() {
2857        return "ADDE.IMAGE";
2858    }
2859
2860    /**
2861     * Get the DataSource properties
2862     * 
2863     * @param ht
2864     *            Hashtable of properties
2865     */
2866    @Override protected void getDataSourceProperties(Hashtable ht) {
2867        super.getDataSourceProperties(ht);
2868        ht.put(DATASET_NAME_KEY, getDatasetName());
2869        ht.put(ImageDataSource.PROP_BANDINFO, getSelectedBandInfos());
2870    }
2871
2872    /**
2873     * _more_
2874     * 
2875     * @return _more_
2876     */
2877    protected List processPropertyComponents() {
2878        List bottomComps = new ArrayList();
2879        getBottomComponents(bottomComps);
2880        for (int i = 0; i < bottomComps.size(); i++) {
2881            addDescComp((JComponent) bottomComps.get(i));
2882        }
2883        return bottomComps;
2884    }
2885
2886    /**
2887     * Make the UI for this selector.
2888     * 
2889     * @return The gui
2890     */
2891    @Override public JComponent doMakeContents() {
2892        JPanel myPanel = new JPanel();
2893
2894        JLabel timesLabel = McVGuiUtils.makeLabelRight("Times:");
2895        addDescComp(timesLabel);
2896
2897        JPanel timesPanel = makeTimesPanel();
2898        timesPanel.setBorder(javax.swing.BorderFactory.createEtchedBorder());
2899        addDescComp(timesPanel);
2900
2901        JLabel imageLabel = McVGuiUtils.makeLabelRight("Other:");
2902        addDescComp(imageLabel);
2903
2904        List comps = new ArrayList();
2905        comps.addAll(processPropertyComponents());
2906        GuiUtils.tmpInsets = GRID_INSETS;
2907        JPanel imagePanel = GuiUtils.doLayout(comps, 2, GuiUtils.WT_NY, GuiUtils.WT_N);
2908        imagePanel.setBorder(javax.swing.BorderFactory.createEtchedBorder());
2909
2910        GroupLayout layout = new GroupLayout(myPanel);
2911        myPanel.setLayout(layout);
2912        layout.setHorizontalGroup(layout.createParallelGroup(LEADING)
2913            .addGroup(layout.createSequentialGroup()
2914                .addGroup(layout.createParallelGroup(LEADING)
2915                .addGroup(layout.createSequentialGroup()
2916                    .addComponent(descriptorLabel)
2917                    .addGap(GAP_RELATED)
2918                    .addComponent(descriptorComboBox))
2919                .addGroup(layout.createSequentialGroup()
2920                    .addComponent(timesLabel)
2921                    .addGap(GAP_RELATED)
2922                    .addComponent(timesPanel, PREFERRED_SIZE, DEFAULT_SIZE, Short.MAX_VALUE))
2923                .addGroup(layout.createSequentialGroup()
2924                    .addComponent(imageLabel)
2925                    .addGap(GAP_RELATED)
2926                    .addComponent(imagePanel, PREFERRED_SIZE, DEFAULT_SIZE, Short.MAX_VALUE)))));
2927
2928        layout.setVerticalGroup(layout.createParallelGroup(LEADING)
2929            .addGroup(layout.createSequentialGroup()
2930            .addGroup(layout.createParallelGroup(BASELINE)
2931                .addComponent(descriptorLabel)
2932                .addComponent(descriptorComboBox))
2933            .addPreferredGap(RELATED)
2934            .addGroup(layout.createParallelGroup(LEADING)
2935                .addComponent(timesLabel)
2936                .addComponent(timesPanel, PREFERRED_SIZE, DEFAULT_SIZE, Short.MAX_VALUE))
2937                .addPreferredGap(RELATED)
2938                .addGroup(layout.createParallelGroup(LEADING)
2939                    .addComponent(imageLabel)
2940                    .addComponent(imagePanel))));
2941
2942        setInnerPanel(myPanel);
2943        return super.doMakeContents();
2944    }
2945
2946    public JComponent doMakeContents(boolean doesOverride) {
2947        return doesOverride ? super.doMakeContents() : doMakeContents();
2948    }
2949
2950}