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