001/*
002 * This file is part of McIDAS-V
003 *
004 * Copyright 2007-2024
005 * Space Science and Engineering Center (SSEC)
006 * University of Wisconsin - Madison
007 * 1225 W. Dayton Street, Madison, WI 53706, USA
008 * https://www.ssec.wisc.edu/mcidas/
009 * 
010 * All Rights Reserved
011 * 
012 * McIDAS-V is built on Unidata's IDV and SSEC's VisAD libraries, and
013 * some McIDAS-V source code is based on IDV and VisAD source code.  
014 * 
015 * McIDAS-V is free software; you can redistribute it and/or modify
016 * it under the terms of the GNU Lesser Public License as published by
017 * the Free Software Foundation; either version 3 of the License, or
018 * (at your option) any later version.
019 * 
020 * McIDAS-V is distributed in the hope that it will be useful,
021 * but WITHOUT ANY WARRANTY; without even the implied warranty of
022 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
023 * GNU Lesser Public License for more details.
024 * 
025 * You should have received a copy of the GNU Lesser Public License
026 * along with this program.  If not, see https://www.gnu.org/licenses/.
027 */
028
029package edu.wisc.ssec.mcidasv.chooser;
030
031import java.awt.Component;
032import java.io.File;
033import java.util.Collections;
034import java.util.Hashtable;
035import java.util.Iterator;
036import java.util.List;
037import java.util.Vector;
038
039import javax.swing.JCheckBox;
040import javax.swing.JComboBox;
041import javax.swing.JComponent;
042import javax.swing.JFileChooser;
043import javax.swing.JLabel;
044import javax.swing.JPanel;
045import javax.swing.JTextField;
046
047import org.w3c.dom.Element;
048
049import ucar.unidata.data.DataSource;
050import ucar.unidata.data.radar.Level2RadarDataSource;
051import ucar.unidata.idv.chooser.IdvChooserManager;
052import ucar.unidata.idv.control.DisplayControlBase;
053import ucar.unidata.metdata.NamedStation;
054import ucar.unidata.metdata.NamedStationTable;
055import ucar.unidata.util.FileManager;
056import ucar.unidata.util.GuiUtils;
057import ucar.unidata.util.Misc;
058import ucar.unidata.util.PatternFileFilter;
059import ucar.unidata.util.PollingInfo;
060import ucar.unidata.util.TwoFacedObject;
061
062import edu.wisc.ssec.mcidasv.util.McVGuiUtils;
063
064/**
065 * A chooser for Level II NEXRAD data. This loads in
066 * files from the file system. Since (right now) the
067 * data does not contain the station we rely on
068 * the heuristic  of looking at the directory path
069 * name to see if it contains a station name.
070 * The user can also specify the station from the GUI
071 *
072 *
073 * @author IDV development team
074 * @version $Revision$Date: 2011/03/24 16:06:31 $
075 */
076public class Level2RadarChooser extends FileChooser {
077
078    /** Holds the predefined list of nexrad stations */
079    private JComboBox stationsCbx;
080
081    /** List of predefined nexrad stations */
082    private List nexradStations;
083
084    /** Label used in the widgets to show an unknown station */
085    private static String UNKNOWN_STATION = "I'm Feeling Lucky";
086
087    /**
088     * The data source id we pass the files to.
089     * This is the oone defined in idv/resources/datasources.xml
090     */
091    private static String DATA_TYPE = "FILE.LEVEL2RADAR";
092
093    /** the type for the CDM radar */
094    private static String CDM_DATA_TYPE = "FILE.RADAR";
095
096    /** checkbox for switching data types */
097    private JCheckBox typeCbx;
098
099    /**
100     * Create the chooser with the given chooser manager
101     * and xml root (from the xml that defines this chooser).
102     *
103     * @param mgr The manager
104     * @param root The xml
105     *
106     */
107    public Level2RadarChooser(IdvChooserManager mgr, Element root) {
108        super(mgr, root);
109    }
110
111    /**
112     * Label for {@link #getDataSourcesComponent()} selector.
113     *
114     * @return {@code String} to use as the label for data sources selector.
115     */
116    protected String getDataSourcesLabel() {
117        return "Station: ";
118    }
119
120    /**
121     * Overridden so that McIDAS-V can attempt auto-selecting the default data
122     * source type.
123     */
124    @Override protected JComboBox getDataSourcesComponent() {
125        stationsCbx = new JComboBox();
126        List stations = Misc.newList(UNKNOWN_STATION);
127        stations.addAll(nexradStations = getStations());
128        DisplayControlBase.setStations(stations, stationsCbx, false);
129        return stationsCbx;
130    }
131    
132    /**
133     * Get the tooltip for the load button
134     *
135     * @return The tooltip for the load button
136     */
137    protected String getLoadToolTip() {
138        return "Load the selected Level II radar files";
139    }
140
141    /**
142     * Make the file chooser
143     *
144     * @param path  the initial path
145     *
146     * @return the JFileChooser
147     */
148    protected JFileChooser doMakeFileChooser(String path) {
149        MyFileChooser fileChooser = new Level2RadarFileChooser(this, path);
150        fileChooser.addChoosableFileFilter(new PatternFileFilter(".*\\.raw$", "Raw files"));
151//        fileChooser.setApproveButtonText(ChooserPanel.CMD_LOAD);
152        fileChooser.setFileFilter(fileChooser.getAcceptAllFileFilter());
153        if (path != null) {
154            fileChooser.setCurrentDirectory(new File(path));
155        }
156        return fileChooser;
157    }
158    
159    /**
160     * Process the set of selected files
161     *
162     * @param files Array of files
163     * @param directory The last directory  chosen
164     *
165     * @return true if successful
166     */
167    protected boolean selectFilesInner(File[] files, final File directory) {
168        final Object selected =
169            ((TwoFacedObject) stationsCbx.getSelectedItem()).getId();
170
171        if (selected.equals(UNKNOWN_STATION)
172                && ((typeCbx != null) && !typeCbx.isSelected())) {
173            userMessage("Unknown location of selected files, "
174                        + "please select from list");
175            return false;
176        }
177
178        int recentCnt = getFileCount();
179        if (recentCnt <= 0) {
180            if ((files == null) || (files.length == 0)) {
181                userMessage("Please select one or more files");
182                return false;
183            }
184        }
185        if ((files != null) && (files.length > 0)) {
186            FileManager.addToHistory(files[0]);
187        }
188
189        String[] tmpDataLoc = getFileNames(((recentCnt <= 0)
190                                            ? files
191                                            : null));
192        if (recentCnt <= 0) {
193            if (tmpDataLoc == null) {
194                return false;
195            }
196        }
197
198        final Hashtable properties =
199            Misc.newHashtable(Level2RadarDataSource.STATION_LOCATION,
200                              selected);
201        String pattern = getFilePattern();
202        if ((pattern != null) && (pattern.length() > 0)) {
203            properties.put(DataSource.PROP_FILEPATTERN,
204                           pattern.toLowerCase());
205        } else {
206            pattern = null;
207        }
208
209        if (recentCnt > 0) {
210            properties.put(DataSource.MOST_RECENT, new Integer(recentCnt));
211            tmpDataLoc = new String[] { directory.toString() };
212            PollingInfo pollingInfo = new PollingInfo(directory.toString(),
213                                          60000, pattern, false, false);
214            pollingInfo.setMode(PollingInfo.MODE_COUNT);
215            pollingInfo.setFileCount(recentCnt);
216            properties.put(DataSource.PROP_POLLINFO, pollingInfo);
217        }
218
219        String dataType = ((typeCbx != null) && !typeCbx.isSelected())
220                          ? DATA_TYPE
221                          : CDM_DATA_TYPE;
222        // System.out.println("dataType = " + dataType);
223        makeDataSource(tmpDataLoc, dataType, properties);
224        return true;
225    }
226
227    /**
228     * Read in the nexrad stations from the
229     * idv/resources/nexradstns.xml resource
230     *
231     * @return List of of {@link ucar.unidata.metdata.NamedStation}-s
232     */
233    private List getStations() {
234        if (nexradStations == null) {
235            nexradStations = new Vector();
236            List radarLocations =
237                getIdv().getResourceManager().findLocationsByType("radar");
238            for (int i = 0; i < radarLocations.size(); i++) {
239                NamedStationTable nexrTable =
240                    (NamedStationTable) radarLocations.get(i);
241                nexradStations.addAll(nexrTable.values());
242            }
243            Collections.sort(nexradStations);
244        }
245        return nexradStations;
246    }
247
248    /**
249     * Try to guess at the station of the selected
250     * file based on directory name.
251     *
252     * @param file The selected file
253     */
254    protected void guessAtStation(File file) {
255
256        if ((file == null) || !file.isDirectory()) {
257            return;
258        }
259        if ((nexradStations == null) || nexradStations.isEmpty()) {
260            return;
261        }
262        File tmpFile = file;
263
264        //Walk up the directory tree, looking at the names of each file
265
266        //Use the  dirLevel so we only do the println on the first check.
267        //Though  we could use it to only check one or two directory levels
268        int     dirLevel = 0;
269        boolean found    = false;
270        while ((tmpFile != null) && (found == false)) {
271            String name = tmpFile.getName().toLowerCase();
272            for (Iterator iter =
273                    nexradStations.iterator(); iter.hasNext(); ) {
274                NamedStation station = (NamedStation) iter.next();
275                if (station == null) {
276                    continue;
277                }
278
279                String id = station.getIdentifier();
280                //Do a .equals - perhaps we do want to do the .indexOf check??
281                //Though that might mean some odd matches.
282                if (name.indexOf(id.toLowerCase()) >= 0) {
283                    stationsCbx.setSelectedItem(
284                        DisplayControlBase.createStationTfo(station));
285                    found = true;
286                    break;
287                }
288            }
289            dirLevel++;
290            tmpFile = tmpFile.getParentFile();
291        }
292        if ( !found) {
293            stationsCbx.setSelectedItem(UNKNOWN_STATION);
294        }
295    }
296
297    /**
298     * This class allows us to add in our own functionality
299     * to the file chooser. It has a hook to support the guessing
300     * of the station from the directory name and passes through
301     * to the chooser the select and cancel events
302     *
303     * @author IDV development team
304     */
305    public class Level2RadarFileChooser extends FileChooser.MyFileChooser {
306
307        /** my chooser */
308        Level2RadarChooser myChooser;
309
310        /** Keeps track of the last directory the user chose */
311        File lastDirectory = null;
312
313        /**
314         * Create the special file chooser
315         *
316         *
317         * @param chooser the chooser to relate to
318         * @param path  path to start with
319         */
320        public Level2RadarFileChooser(Level2RadarChooser chooser,
321                                      String path) {
322            super(path);
323            myChooser = chooser;
324        }
325
326        /**
327         * Try to guess at the  station name
328         *
329         * @param file The currently selected dir
330         */
331        public void setCurrentDirectory(File file) {
332            super.setCurrentDirectory(file);
333            if ( !Misc.equals(file, lastDirectory)) {
334                if (myChooser != null) {
335                    myChooser.guessAtStation(file);
336                }
337                lastDirectory = file;
338            }
339        }
340    }
341
342    /**
343     * Get the bottom panel for the chooser
344     * @return the bottom panel
345     */
346    protected JPanel getBottomPanel() {       
347        // do this because the original check is made before the list is inited
348        if (getFileChooser() != null) {
349            guessAtStation(getFileChooser().getCurrentDirectory());
350        }
351        JComponent recentComponent = getRecentFilesComponent();
352        Component [] components = recentComponent.getComponents();
353        if (components != null) {
354                for (int i = 0; i < components.length; i++) {
355                        if (components[i] instanceof JLabel) {
356                                McVGuiUtils.setComponentWidth((JLabel)components[i], McVGuiUtils.Width.SINGLE);
357                                McVGuiUtils.setLabelPosition((JLabel)components[i], McVGuiUtils.Position.RIGHT);
358                        }
359                        else if (components[i] instanceof JComboBox) {
360                                McVGuiUtils.setComponentWidth((JComboBox)components[i], McVGuiUtils.Width.DOUBLE);
361                        }
362                        else if (components[i] instanceof JTextField) {
363                                McVGuiUtils.setComponentWidth((JTextField)components[i], McVGuiUtils.Width.SINGLE);
364                        }
365                }
366                recentComponent = GuiUtils.left(GuiUtils.hbox(components));
367        }
368        return McVGuiUtils.makeLabeledComponent("Times:", recentComponent);
369    }
370    
371}
372