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