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