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 }