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