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 }