001    /*
002     * This file is part of McIDAS-V
003     *
004     * Copyright 2007-2013
005     * Space Science and Engineering Center (SSEC)
006     * University of Wisconsin - Madison
007     * 1225 W. Dayton Street, Madison, WI 53706, USA
008     * https://www.ssec.wisc.edu/mcidas
009     * 
010     * All Rights Reserved
011     * 
012     * McIDAS-V is built on Unidata's IDV and SSEC's VisAD libraries, and
013     * some McIDAS-V source code is based on IDV and VisAD source code.  
014     * 
015     * McIDAS-V is free software; you can redistribute it and/or modify
016     * it under the terms of the GNU Lesser Public License as published by
017     * the Free Software Foundation; either version 3 of the License, or
018     * (at your option) any later version.
019     * 
020     * McIDAS-V is distributed in the hope that it will be useful,
021     * but WITHOUT ANY WARRANTY; without even the implied warranty of
022     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
023     * GNU Lesser Public License for more details.
024     * 
025     * You should have received a copy of the GNU Lesser Public License
026     * along with this program.  If not, see http://www.gnu.org/licenses.
027     */
028    
029    package edu.wisc.ssec.mcidasv.chooser.adde;
030    
031    import static javax.swing.GroupLayout.DEFAULT_SIZE;
032    import static javax.swing.GroupLayout.PREFERRED_SIZE;
033    import static javax.swing.GroupLayout.Alignment.BASELINE;
034    import static javax.swing.GroupLayout.Alignment.LEADING;
035    import static javax.swing.LayoutStyle.ComponentPlacement.RELATED;
036    
037    import java.util.ArrayList;
038    import java.util.Hashtable;
039    import java.util.Iterator;
040    import java.util.List;
041    import java.util.StringTokenizer;
042    
043    import javax.swing.GroupLayout;
044    import javax.swing.JComboBox;
045    import javax.swing.JComponent;
046    import javax.swing.JLabel;
047    import javax.swing.JPanel;
048    
049    import org.w3c.dom.Element;
050    
051    import edu.wisc.ssec.mcidas.AreaDirectory;
052    import edu.wisc.ssec.mcidas.AreaDirectoryList;
053    import edu.wisc.ssec.mcidas.AreaFileException;
054    import edu.wisc.ssec.mcidas.McIDASUtil;
055    
056    import ucar.unidata.data.imagery.AddeImageInfo;
057    import ucar.unidata.data.imagery.ImageDataSource;
058    import ucar.unidata.idv.chooser.IdvChooserManager;
059    import ucar.unidata.idv.chooser.adde.AddeServer;
060    import ucar.unidata.metdata.NamedStationTable;
061    import ucar.unidata.util.LogUtil;
062    import ucar.unidata.util.Misc;
063    
064    
065    import edu.wisc.ssec.mcidasv.util.McVGuiUtils;
066    
067    /**
068     * Widget to select NEXRAD radar images from a remote ADDE server
069     * Displays a list of the descriptors (names) of the radar datasets
070     * available for a particular ADDE group on the remote server.
071     *
072     * @author Don Murray
073     */
074    public class AddeRadarChooser extends AddeImageChooser {
075    
076        /** Use to list the stations */
077        protected static final String VALUE_LIST = "list";
078    
079        /** This is the list of properties that are used in the advanced gui */
080        private static final String[] RADAR_PROPS = { PROP_UNIT };
081    
082        /** This is the list of labels used for the advanced gui */
083        private static final String[] RADAR_LABELS = { "Data Type:" };
084    
085        /** Am I currently reading the stations */
086        private boolean readingStations = false;
087    
088        /** handle on the station update task */
089        private Object readStationTask;
090    
091        /** station table */
092        private List nexradStations;
093    
094    
095    
096        /**
097         * Construct an Adde image selection widget displaying information
098         * for the specified dataset located on the specified server.
099         *
100         *
101         *
102         * @param mgr The chooser manager
103         * @param root The chooser.xml node
104         */
105        public AddeRadarChooser(IdvChooserManager mgr, Element root) {
106            super(mgr, root);
107            this.nexradStations =
108                getIdv().getResourceManager().findLocationsByType("radar");
109        }
110    
111        /**
112         * get the adde server grup type to use
113         *
114         * @return group type
115         */
116        protected String getGroupType() {
117            return AddeServer.TYPE_RADAR;
118        }
119    
120        /**
121         * Overwrite base class method to return the correct name
122         * (used for labeling, etc.)
123         *
124         * @return  data name specific to this selector
125         */
126        public String getDataName() {
127            return "Radar Data";
128        }
129    
130        @Override public String getDataType() {
131            return "RADAR";
132        }
133    
134        /**
135         * _more_
136         *
137         * @return _more_
138         */
139        public String getDescriptorLabel() {
140            return "Product";
141        }
142    
143        /**
144         * Get the size of the image list
145         *
146         * @return the image list size
147         */
148        protected int getImageListSize() {
149            return 6;
150        }
151        
152        /**
153         * Get a description of the currently selected dataset
154         *
155         * @return the data set description.
156         */
157        public String getDatasetName() {
158            return getSelectedStation() + " (" + super.getDatasetName() + ")";
159        }
160    
161        /**
162         * Method to call if the server changed.
163         */
164        protected void connectToServer() {
165            clearStations();
166            super.connectToServer();
167            setAvailableStations();
168        }
169    
170        /**
171         * Check if we are ready to read times
172         *
173         * @return  true if times can be read
174         */
175        protected boolean canReadTimes() {
176            return super.canReadTimes() && (getSelectedStation() != null);
177        }
178    
179        /**
180         * Get the advanced property names
181         *
182         * @return array of advanced properties
183         */
184        protected String[] getAdvancedProps() {
185            return RADAR_PROPS;
186        }
187    
188        /**
189         * Get the labels for the advanced properties
190         *
191         * @return array of labels
192         */
193        protected String[] getAdvancedLabels() {
194            return RADAR_LABELS;
195        }
196    
197        /**
198         * Update labels, etc.
199         */
200        protected void updateStatus() {
201            super.updateStatus();
202            if (getState() != STATE_CONNECTED) {
203                clearStations();
204            }
205            if (readStationTask!=null) {
206                if(taskOk(readStationTask)) {
207                    setStatus("Reading available stations from server");
208                } else {
209                    readStationTask  = null;
210                    setState(STATE_UNCONNECTED);
211                }
212            }
213        }
214    
215        /**
216         * A new station was selected. Update the gui.
217         *
218         * @param stations List of selected stations
219         */
220        protected void newSelectedStations(List stations) {
221            super.newSelectedStations(stations);
222            descriptorChanged();
223        }
224    
225        /**
226         *  Generate a list of radar ids for the id list.
227         */
228        private void setAvailableStations() {
229            readStationTask = startTask();
230            clearSelectedStations();
231            updateStatus();
232            List stations = readStations();
233            if(stopTaskAndIsOk(readStationTask)) {
234                readStationTask = null;
235                if (stations != null) {
236                    getStationMap().setStations(stations);
237                } else {
238                    clearStations();
239                }
240                updateStatus();
241                revalidate();
242            } else {
243                //User pressed cancel
244                setState(STATE_UNCONNECTED);
245                return;
246            }
247        }
248    
249        /**
250         * Generate a list of radar ids for the id list.
251         *
252         * @return  list of station IDs
253         */
254        private List readStations() {
255            ArrayList stations = new ArrayList();
256            try {
257                if ((descriptorNames == null) || (descriptorNames.length == 0)) {
258                    return stations;
259                }
260                StringBuffer buff        = getGroupUrl(REQ_IMAGEDIR, getGroup());
261                String       descrForIds = descriptorNames[0];
262                // try to use base reflectivity if it's available.
263                for (int i = 0; i < descriptorNames.length; i++) {
264                    if ((descriptorNames[i] != null)
265                            && descriptorNames[i].toLowerCase().startsWith(
266                                "base")) {
267                        descrForIds = descriptorNames[i];
268                        break;
269                    }
270                }
271                appendKeyValue(buff, PROP_DESCR,
272                               getDescriptorFromSelection(descrForIds));
273                appendKeyValue(buff, PROP_ID, VALUE_LIST);
274                Hashtable         seen    = new Hashtable();
275                AreaDirectoryList dirList =
276                    new AreaDirectoryList(buff.toString());
277                for (Iterator it = dirList.getDirs().iterator(); it.hasNext(); ) {
278                    AreaDirectory ad = (AreaDirectory) it.next();
279                    String stationId =
280                        McIDASUtil.intBitsToString(ad.getValue(20)).trim();
281                    //Check for uniqueness
282                    if (seen.get(stationId) != null) {
283                        continue;
284                    }
285                    seen.put(stationId, stationId);
286                    //System.err.println ("id:" + stationId);
287                    Object station = findStation(stationId);
288                    if (station != null) {
289                        stations.add(station);
290                    }
291                }
292            } catch (AreaFileException e) {
293                String msg = e.getMessage();
294                if (msg.toLowerCase().indexOf(
295                        "no images meet the selection criteria") >= 0) {
296                    LogUtil.userErrorMessage(
297                        "No stations could be found on the server");
298                } else {
299                    handleConnectionError(e);
300                }
301                stations = new ArrayList();
302                setState(STATE_UNCONNECTED);
303            }
304            return stations;
305        }
306    
307        /**
308         * Find the station for the given ID
309         *
310         * @param stationId  the station ID
311         *
312         * @return  the station or null if not found
313         */
314        private Object findStation(String stationId) {
315            for (int i = 0; i < nexradStations.size(); i++) {
316                NamedStationTable table =
317                    (NamedStationTable) nexradStations.get(i);
318                Object station = table.get(stationId);
319                if (station != null) {
320                    return station;
321                }
322            }
323            return null;
324        }
325    
326        public void doCancel() {
327            readStationTask = null;
328            super.doCancel();
329        }
330    
331        /**
332         * Get the list of properties for the base URL
333         * @return list of properties
334         */
335        protected String[] getBaseUrlProps() {
336            return new String[] { PROP_DESCR, PROP_ID, PROP_UNIT, PROP_SPAC,
337                                  PROP_BAND, PROP_USER, PROP_PROJ, };
338        }
339    
340        /**
341         * Overwrite the base class method to return the default property value
342         * for PROP_ID.
343         *
344         * @param prop The property
345         * @param ad The area directory
346         * @param forDisplay Is this to show the end user in the gui.
347         *
348         * @return The value of the property
349         */
350        protected String getDefaultPropValue(String prop, AreaDirectory ad,
351                                             boolean forDisplay) {
352            if (prop.equals(PROP_ID)) {
353                return getSelectedStation();
354            }
355            return super.getDefaultPropValue(prop, ad, forDisplay);
356        }
357    
358        /**
359         * Get a description of the properties
360         *
361         * @return  a description
362         */
363        protected String getPropertiesDescription() {
364            StringBuffer buf = new StringBuffer();
365            if (unitComboBox != null) {
366                buf.append(getAdvancedLabels()[0]);
367                buf.append(" ");
368                buf.append(unitComboBox.getSelectedItem().toString());
369            }
370            return buf.toString();
371        }
372    
373        /**
374         * get properties
375         *
376         * @param ht properties
377         */
378        protected void getDataSourceProperties(Hashtable ht) {
379            unitComboBox.setSelectedItem(ALLUNITS);
380            super.getDataSourceProperties(ht);
381            ht.put(ImageDataSource.PROP_IMAGETYPE, ImageDataSource.TYPE_RADAR);
382        }
383        
384        /**
385         * Get the time popup widget
386         *
387         * @return  a widget for selecing the day
388         */
389        protected JComponent getExtraTimeComponent() {
390            JPanel filler = new JPanel();
391            McVGuiUtils.setComponentHeight(filler, new JComboBox());
392            return filler;
393        }
394        
395        /**
396         * Make the UI for this selector.
397         *
398         * @return The gui
399         */
400        public JComponent doMakeContents() {      
401            JPanel myPanel = new JPanel();
402                    
403            JLabel stationLabel = McVGuiUtils.makeLabelRight("Station:");
404            addServerComp(stationLabel);
405    
406            JComponent stationPanel = getStationMap();
407            registerStatusComp("stations", stationPanel);
408            addServerComp(stationPanel);
409            
410            JLabel timesLabel = McVGuiUtils.makeLabelRight("Times:");
411            addDescComp(timesLabel);
412            
413            JPanel timesPanel = makeTimesPanel();
414            timesPanel.setBorder(javax.swing.BorderFactory.createEtchedBorder());
415            addDescComp(timesPanel);
416            
417            // We need to create this but never show it... AddeImageChooser requires it to be instantiated
418            unitComboBox = new JComboBox();
419            
420            enableWidgets();
421    
422            GroupLayout layout = new GroupLayout(myPanel);
423            myPanel.setLayout(layout);
424            layout.setHorizontalGroup(
425                layout.createParallelGroup(LEADING)
426                .addGroup(layout.createSequentialGroup()
427                    .addGroup(layout.createParallelGroup(LEADING)
428                        .addGroup(layout.createSequentialGroup()
429                            .addComponent(descriptorLabel)
430                            .addGap(GAP_RELATED)
431                            .addComponent(descriptorComboBox))
432                        .addGroup(layout.createSequentialGroup()
433                            .addComponent(stationLabel)
434                            .addGap(GAP_RELATED)
435                            .addComponent(stationPanel, PREFERRED_SIZE, DEFAULT_SIZE, Short.MAX_VALUE))
436                        .addGroup(layout.createSequentialGroup()
437                            .addComponent(timesLabel)
438                            .addGap(GAP_RELATED)
439                            .addComponent(timesPanel, PREFERRED_SIZE, DEFAULT_SIZE, Short.MAX_VALUE))))
440            );
441            layout.setVerticalGroup(
442                layout.createParallelGroup(LEADING)
443                .addGroup(layout.createSequentialGroup()
444                    .addGroup(layout.createParallelGroup(BASELINE)
445                        .addComponent(descriptorLabel)
446                        .addComponent(descriptorComboBox))
447                    .addPreferredGap(RELATED)
448                    .addGroup(layout.createParallelGroup(LEADING)
449                        .addComponent(stationLabel)
450                        .addComponent(stationPanel, PREFERRED_SIZE, DEFAULT_SIZE, Short.MAX_VALUE))
451                    .addPreferredGap(RELATED)
452                    .addGroup(layout.createParallelGroup(LEADING)
453                        .addComponent(timesLabel)
454                        .addComponent(timesPanel, PREFERRED_SIZE, DEFAULT_SIZE, Short.MAX_VALUE)))
455            );
456            
457            setInnerPanel(myPanel);
458            return super.doMakeContents(true);
459        }
460        
461        /**
462         * Get the default value for a key
463         * 
464         * @return null for SIZE, else super
465         */
466        protected String getDefault(String property, String dflt) {
467            if (PROP_SIZE.equals(property)) {
468                    return dflt;
469            }
470            return super.getDefault(property, dflt);
471        }
472        
473        /**
474         * Make an AddeImageInfo from a URL and an AreaDirectory
475         * 
476         * @param dir
477         *            AreaDirectory
478         * @param isRelative
479         *            true if is relative
480         * @param num
481         *            number (for relative images)
482         * 
483         * @return corresponding AddeImageInfo
484         */
485        protected AddeImageInfo makeImageInfo(AreaDirectory dir,
486                boolean isRelative, int num) {
487            AddeImageInfo info = new AddeImageInfo(getAddeServer().getName(),
488                    AddeImageInfo.REQ_IMAGEDATA, getGroup(), getDescriptor());
489            if (isRelative) {
490                info.setDatasetPosition((num == 0) ? 0 : -num);
491            } else {
492                info.setStartDate(dir.getNominalTime());
493            }
494            setImageInfoProps(info, getMiscKeyProps(), dir);
495            setImageInfoProps(info, getBaseUrlProps(), dir);
496    
497            info.setLocateKey(PROP_LINELE);
498            info.setLocateValue("0 0 F");
499            info.setPlaceValue("ULEFT");
500            
501            String magKey = getPropValue(PROP_MAG, dir);
502            int lmag = 1;
503            int emag = 1;
504            StringTokenizer tok = new StringTokenizer(magKey);
505            lmag = (int) Misc.parseNumber((String) tok.nextElement());
506            if (tok.hasMoreTokens()) {
507                emag = (int) Misc.parseNumber((String) tok.nextElement());
508            } else {
509                emag = lmag;
510            }
511            info.setLineMag(lmag);
512            info.setElementMag(emag);
513    
514            int lines = dir.getLines();
515            int elems = dir.getElements();
516            String sizeKey = getPropValue(PROP_SIZE, dir);
517            tok = new StringTokenizer(sizeKey);
518            String size = (String) tok.nextElement();
519            if (!size.equalsIgnoreCase("all")) {
520                lines = (int) Misc.parseNumber(size);
521                if (tok.hasMoreTokens()) {
522                    elems = (int) Misc.parseNumber((String) tok.nextElement());
523                } else {
524                    elems = lines;
525                }
526            }
527            info.setLines(lines);
528            info.setElements(elems);
529            /*
530             * System.out.println("url = " + info.getURLString().toLowerCase() +
531             * "\n");
532             */
533            return info;
534        }
535    }