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 }