001 /*
002 * $Id: AddeRaobChooser.java,v 1.33 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 package edu.wisc.ssec.mcidasv.chooser.adde;
031
032 import static javax.swing.GroupLayout.DEFAULT_SIZE;
033 import static javax.swing.GroupLayout.PREFERRED_SIZE;
034 import static javax.swing.GroupLayout.Alignment.LEADING;
035 import static javax.swing.GroupLayout.Alignment.TRAILING;
036 import static javax.swing.LayoutStyle.ComponentPlacement.RELATED;
037
038 import java.awt.Dimension;
039 import java.awt.Point;
040 import java.awt.event.ActionEvent;
041 import java.awt.event.ActionListener;
042 import java.awt.event.ItemEvent;
043 import java.awt.event.ItemListener;
044 import java.awt.event.MouseAdapter;
045 import java.awt.event.MouseEvent;
046 import java.beans.PropertyChangeEvent;
047 import java.beans.PropertyChangeListener;
048 import java.util.ArrayList;
049 import java.util.Arrays;
050 import java.util.Collections;
051 import java.util.Enumeration;
052 import java.util.Hashtable;
053 import java.util.List;
054 import java.util.Map;
055 import java.util.Vector;
056
057 import javax.swing.GroupLayout;
058 import javax.swing.JButton;
059 import javax.swing.JCheckBox;
060 import javax.swing.JComboBox;
061 import javax.swing.JComponent;
062 import javax.swing.JLabel;
063 import javax.swing.JList;
064 import javax.swing.JMenuItem;
065 import javax.swing.JPanel;
066 import javax.swing.JPopupMenu;
067 import javax.swing.JScrollPane;
068 import javax.swing.JTextField;
069 import javax.swing.ListModel;
070 import javax.swing.ListSelectionModel;
071 import javax.swing.SwingUtilities;
072 import javax.swing.event.ListSelectionEvent;
073 import javax.swing.event.ListSelectionListener;
074
075 import org.w3c.dom.Element;
076
077 import edu.wisc.ssec.mcidas.McIDASUtil;
078 import edu.wisc.ssec.mcidas.adde.AddePointDataReader;
079 import edu.wisc.ssec.mcidas.adde.DataSetInfo;
080
081 import visad.DateTime;
082
083 import ucar.unidata.data.sounding.RaobDataSet;
084 import ucar.unidata.data.sounding.SoundingOb;
085 import ucar.unidata.data.sounding.SoundingStation;
086 import ucar.unidata.gis.mcidasmap.McidasMap;
087 import ucar.unidata.idv.chooser.IdvChooserManager;
088 import ucar.unidata.metdata.Station;
089 import ucar.unidata.util.GuiUtils;
090 import ucar.unidata.util.LogUtil;
091 import ucar.unidata.util.Misc;
092 import ucar.unidata.view.CompositeRenderer;
093 import ucar.unidata.view.station.StationLocationMap;
094
095 import edu.wisc.ssec.mcidasv.data.adde.AddeSoundingAdapter;
096 import edu.wisc.ssec.mcidasv.util.McVGuiUtils;
097 import edu.wisc.ssec.mcidasv.util.McVGuiUtils.Width;
098
099 /**
100 * A chooser class for selecting Raob data.
101 * Mostly just a wrapper around a
102 * {@link ucar.unidata.view.sounding.SoundingSelector}
103 * that does most of the work
104 *
105 * @author IDV development team
106 * @version $Revision: 1.33 $Date: 2012/02/19 17:35:35 $
107 */
108
109
110 public class AddeRaobChooser extends AddePointDataChooser {
111
112 /** Property for the data type. */
113 public static String DATA_TYPE = "RAOB";
114
115 /** Significant level objects corresponding to mandatory level objects */
116 private Hashtable descriptorTable2 = new Hashtable();
117 private JComboBox descriptorComboBox2 = new JComboBox();
118 protected String[] descriptorNames2;
119 private String LABEL_SELECT2 = " -- Optional Significant Levels -- ";
120 private JCheckBox showAll = new JCheckBox("Show all sources");
121 private Object readSatelliteTask;
122
123 /** This flag keeps track of observed/satellite soundings */
124 private boolean satelliteSounding = false;
125
126 /** Selector for times when pointing to satellite data (required field) */
127 private JLabel satelliteTimeLabel = McVGuiUtils.makeLabelRight("");
128 private JPanel satelliteTimePanel;
129 private JButton satelliteTimeButton;
130 private JComboBox satelliteTimeComboBox;
131 private JTextField satellitePixelTextField;
132 private String satelliteTime = "";
133 private String satellitePixel = "1";
134 private List satelliteTimes = new ArrayList();
135
136 /** We need to be able to enable/disable this based on sounding type */
137 private JCheckBox mainHoursCbx;
138
139 /** This is a virtual timestamp that tracks if the threaded adde connection should be aborted or not */
140 private int connectionStep = 0;
141
142 /** handle on the station update task */
143 private Object readStationTask;
144
145 /** list of times */
146 private JList timesList;
147
148 /** list of observations */
149 private JList obsList;
150
151 /** selected observations */
152 private Vector selectedObs = new Vector();
153
154 /** sounding adapter used by this selector */
155 AddeSoundingAdapter soundingAdapter;
156
157 /** flag for 0 and 12z only */
158 private boolean showMainHoursOnly = true;
159
160 /**
161 * Construct a <code>RaobChooser</code> using the manager
162 * and the root XML that defines this object.
163 *
164 * @param mgr <code>IdvChooserManager</code> that controls this chooser.
165 * @param root root element of the XML that defines this object
166 */
167 public AddeRaobChooser(IdvChooserManager mgr, Element root) {
168 super(mgr, root);
169
170 setSelectString(" -- Select Mandatory Levels -- ");
171
172 descriptorComboBox2.addItemListener(new ItemListener() {
173 public void itemStateChanged(ItemEvent e) {
174 if ( !ignoreDescriptorChange
175 && (e.getStateChange() == e.SELECTED)) {
176 descriptorChanged(false);
177 }
178 }
179 });
180 descriptorComboBox2.setEnabled(false);
181
182 showAll.addItemListener(new ItemListener() {
183 public void itemStateChanged(ItemEvent e) {
184 if (getState() == STATE_CONNECTED) {
185 doConnect();
186 }
187 }
188 });
189
190 satelliteTimeComboBox = new JComboBox();
191 satelliteTimeComboBox.setEditable(true);
192 satelliteTimeComboBox.addItemListener(new ItemListener() {
193 public void itemStateChanged(ItemEvent e) {
194 if (e.getStateChange()==e.DESELECTED) return;
195 satelliteTime = satelliteTimeComboBox.getSelectedItem().toString();
196 Misc.run(new Runnable() {
197 public void run() {
198 setAvailableStations(true);
199 }
200 });
201 }
202 });
203
204 satelliteTimeButton = McVGuiUtils.makeImageButton(ICON_UPDATE, "Request list of available times from server");
205 satelliteTimeButton.addActionListener(new ActionListener() {
206 public void actionPerformed(ActionEvent e) {
207 sampleTimes();
208 }
209 });
210
211 satellitePixelTextField = new JTextField(satellitePixel);
212 satellitePixelTextField.addActionListener(new ActionListener() {
213 public void actionPerformed(ActionEvent e) {
214 satellitePixel = satellitePixelTextField.getText().replace('-', ' ');
215 Misc.run(new Runnable() {
216 public void run() {
217 setAvailableStations(true);
218 }
219 });
220 }
221 });
222 }
223
224 /**
225 * Tell the AddeChooser our name
226 *
227 * @return The name
228 */
229 public String getDataName() {
230 return "Sounding Data";
231 }
232
233 /**
234 * Get the descriptor widget label.
235 *
236 * @return label for the descriptor widget
237 */
238 public String getDescriptorLabel() {
239 return "Soundings";
240 }
241
242 /**
243 * get default display to create
244 *
245 * @return default display
246 */
247 protected String getDefaultDisplayType() {
248 return "raob_skewt";
249 }
250
251 /**
252 * Get the mandatory dataset name.
253 *
254 * @return mandatory dataset name
255 */
256 private String getMandatoryDataset() {
257 if (getDescriptor() == null) return null;
258 return getGroup() + "/" + getDescriptor();
259 }
260
261 /**
262 * Get the sig level dataset name.
263 *
264 * @return sig level dataset name
265 */
266 private String getSigLevelDataset() {
267 if (getDescriptor2() == null) return getMandatoryDataset();
268 return getGroup() + "/" + getDescriptor2();
269 }
270
271 /**
272 * Add a listener to the given combobox that will set the
273 * state to unconnected
274 *
275 * @param box The box to listen to.
276 */
277 protected void clearOnChange(final JComboBox box) {
278 box.addItemListener(new ItemListener() {
279 public void itemStateChanged(ItemEvent e) {
280 if ( !ignoreStateChangedEvents) {
281 setState(STATE_UNCONNECTED);
282 GuiUtils.setListData(descriptorComboBox, new Vector());
283 GuiUtils.setListData(descriptorComboBox2, new Vector());
284 }
285 }
286 });
287 }
288
289 /**
290 * Reset the descriptor stuff
291 */
292 protected void resetDescriptorBox() {
293 ignoreDescriptorChange = true;
294 descriptorComboBox.setSelectedItem(LABEL_SELECT);
295 if (descriptorComboBox2 != null) {
296 descriptorComboBox2.setSelectedItem(LABEL_SELECT2);
297 descriptorComboBox2.setEnabled(false);
298 }
299 ignoreDescriptorChange = false;
300 }
301
302 /**
303 * Initialize the descriptor list from a list of names
304 *
305 * @param names list of names
306 */
307 protected void setDescriptors2(String[] names2) {
308 synchronized (WIDGET_MUTEX) {
309 ignoreDescriptorChange = true;
310 descriptorComboBox2.removeAllItems();
311 descriptorComboBox2.addItem(LABEL_SELECT2);
312 descriptorNames2 = names2;
313 if ((names2 == null) || (names2.length == 0)) {
314 ignoreDescriptorChange = false;
315 return;
316 }
317 for (int j = 0; j < names2.length; j++) {
318 descriptorComboBox2.addItem(names2[j]);
319 }
320 ignoreDescriptorChange = false;
321 }
322 }
323
324 /**
325 * Get the selected descriptor.
326 *
327 * @return the currently selected descriptor.
328 */
329 protected String getDescriptor2() {
330 if (descriptorTable2 == null) {
331 return null;
332 }
333 String selection = (String) descriptorComboBox2.getSelectedItem();
334 if (selection == null) {
335 return null;
336 }
337 if (selection.equals(LABEL_SELECT2)) {
338 return null;
339 }
340 if (!selection.contains(nameSeparator)) {
341 return (String)descriptorTable2.get(selection);
342 }
343 else {
344 String[] toks = selection.split(nameSeparator);
345 String key = toks[1].trim();
346 return (String)descriptorTable2.get(key);
347 }
348 }
349
350 /**
351 * Method to call if the server changed.
352 */
353 protected void connectToServer() {
354 clearStations();
355 setDescriptors2(null);
356 super.connectToServer();
357 setAvailableStations(true);
358 }
359
360 /**
361 * Do we have times selected.
362 * @return Do we have times
363 */
364 public boolean timesOk() {
365 return haveTimeSelected();
366 }
367
368 /**
369 * Are there any times selected.
370 *
371 * @return Any times selected.
372 */
373 protected boolean haveTimeSelected() {
374 if (selectedObs!=null) {
375 if (selectedObs.size() > 0) return true;
376 }
377 return false;
378 }
379
380 /**
381 * Do nothing for read times...
382 * doUpdateInner handles all of this with an AddeSoundingAdapter
383 */
384 public void readTimes() { }
385
386 /**
387 * Wrapper for sampleTimesInner
388 * Starts in a new thread and handles UI updating
389 */
390 private void sampleTimes() {
391 readSatelliteTask = startTask();
392 enableWidgets();
393 Misc.run(new Runnable() {
394 public void run() {
395 sampleTimesInner();
396 if(stopTaskAndIsOk(readSatelliteTask)) {
397 readSatelliteTask = null;
398 GuiUtils.setListData(satelliteTimeComboBox, satelliteTimes);
399 revalidate();
400 } else {
401 //User pressed cancel
402 setState(STATE_UNCONNECTED);
403 }
404 }
405 });
406 updateStatus();
407 }
408
409 /**
410 * Different way of reading times... for satellite soundings, do the following:
411 * PTLIST GROUP/DESCRIPTOR.Z SEL='ROW X; COL Y' PAR=TIME
412 * where Z starts at 0 (expect an error), then goes to 1 and increases monotonically in outer loop until error
413 * and X starts at 1 and increases monotonically in middle loop until error
414 * and Y starts at 1 and increases by 25000 or so in inner loop until error
415 * This samples times across the dataset
416 */
417 private void sampleTimesInner() {
418 if (getDescriptor()==null) return;
419 showWaitCursor();
420 int posMax = 9999;
421 int rowMax = 9999;
422 int colMax = 999999;
423 int colSkip = 24000;
424 int consecutiveFailures = 0;
425 Map<String, String> acctInfo = getAccountingInfo();
426 String user = acctInfo.get("user");
427 String proj = acctInfo.get("proj");
428 String appendUserProj = "";
429 if (!(user.equals("") || proj.equals("")))
430 appendUserProj += "&user=" + user + "&proj=" + proj;
431 satelliteTimes = new ArrayList();
432 for (int pos = 0; pos < posMax; pos++) {
433 for (int row=1; row<rowMax; row++) {
434 for (int col=1; col<colMax; col+=colSkip) {
435
436 String[] paramString = new String[] {
437 "group", getGroup(), "descr", getDescriptor(), "param", "DAY TIME", "num", "1",
438 "pos", Integer.toString(pos),
439 "select", "'ROW " + row + "; COL " + col + "'"
440 };
441 String request = Misc.makeUrl("adde", getServer(), "/point", paramString);
442 request += appendUserProj;
443 try {
444 AddePointDataReader dataReader = new AddePointDataReader(request);
445 int[][] data = dataReader.getData();
446 if (data[0].length == 0) throw new Exception();
447 for (int i = 0; i < data[0].length; i++) {
448 int day = data[0][i];
449 int time = data[1][i];
450 DateTime dt = new DateTime(McIDASUtil.mcDayTimeToSecs(day, time));
451 String timeString = dt.timeString().substring(0,5);
452 if (satelliteTimes.indexOf(timeString) < 0) {
453 satelliteTimes.add(timeString);
454 }
455 }
456 // Reset consecutive failure count when you get good data
457 consecutiveFailures=0;
458 }
459 catch (Exception e) {
460
461 // We are at the beginning of a position
462 // Log a failure and increment the position
463 if (col==1 && row==1) {
464 row=rowMax;
465 consecutiveFailures++;
466 // If we have failed a few times in a row, bail completely
467 if (consecutiveFailures > 2) {
468 pos=posMax;
469 }
470 }
471
472 // If we failed at the first column, increment the position
473 if (col==1) row=rowMax;
474
475 // We have an exception, increment the row
476 col = colMax;
477
478 }
479 }
480 }
481 }
482
483 Collections.sort(satelliteTimes);
484 showNormalCursor();
485 }
486
487 /**
488 * Generate a list of image descriptors for the descriptor list.
489 */
490 protected void readDescriptors() {
491 try {
492 StringBuffer buff = getGroupUrl(REQ_DATASETINFO, getGroup());
493 buff.append("&type=" + getDataType());
494 DataSetInfo dsinfo = new DataSetInfo(buff.toString());
495 descriptorTable = dsinfo.getDescriptionTable();
496 descriptorTable2 = new Hashtable();
497
498 if (!showAll.isSelected()) {
499 // Filter out anything not Upper Air Mandatory or Significant
500 for (Enumeration enumeration = descriptorTable.keys(); enumeration.hasMoreElements();) {
501 Object key = enumeration.nextElement();
502 String keyString = key.toString();
503 String descriptorString = descriptorTable.get(key).toString();
504 if (keyString.toUpperCase().indexOf("MAND") >= 0 || descriptorString.indexOf("MAND") >= 0) {
505 continue;
506 }
507 if (keyString.toUpperCase().indexOf("SIG") >= 0 || descriptorString.indexOf("SIG") >= 0) {
508 descriptorTable2.put(key, descriptorTable.get(key));
509 descriptorTable.remove(key);
510 continue;
511 }
512 if (keyString.toUpperCase().indexOf("UPPER AIR") >= 0 ||
513 descriptorString.indexOf("UPPER") >= 0 ||
514 descriptorString.indexOf("UPPR") >= 0) {
515 descriptorTable2.put(key, descriptorTable.get(key));
516 continue;
517 }
518 if (keyString.toUpperCase().indexOf("SOUNDER") >= 0 ||
519 descriptorString.indexOf("SND") >= 0 ||
520 descriptorString.indexOf("SNDR") >= 0) {
521 descriptorTable2.put(key, descriptorTable.get(key));
522 continue;
523 }
524 if (keyString.toUpperCase().indexOf("GRET") >= 0 || descriptorString.indexOf("GRET") >= 0) {
525 descriptorTable2.put(key, descriptorTable.get(key));
526 continue;
527 }
528 if (keyString.toUpperCase().indexOf("SRET") >= 0 || descriptorString.indexOf("SRET") >= 0) {
529 descriptorTable2.put(key, descriptorTable.get(key));
530 continue;
531 }
532 descriptorTable.remove(key);
533 }
534 }
535 else {
536 // We have been told to Show All... put all descriptors into both categories
537 for (Enumeration enumeration = descriptorTable.keys(); enumeration.hasMoreElements();) {
538 Object key = enumeration.nextElement();
539 descriptorTable2.put(key, descriptorTable.get(key));
540 }
541 }
542
543 String[] names = new String[descriptorTable.size()];
544 Enumeration enumeration = descriptorTable.keys();
545 for (int i = 0; enumeration.hasMoreElements(); i++) {
546 Object thisElement = enumeration.nextElement();
547 if (!isLocalServer())
548 names[i] = descriptorTable.get(thisElement).toString() + nameSeparator + thisElement.toString();
549 else
550 names[i] = thisElement.toString();
551 }
552 Arrays.sort(names);
553 setDescriptors(names);
554
555 String[] names2 = new String[descriptorTable2.size()];
556 Enumeration enumeration2 = descriptorTable2.keys();
557 for (int i = 0; enumeration2.hasMoreElements(); i++) {
558 Object thisElement2 = enumeration2.nextElement();
559 if (!isLocalServer())
560 names2[i] = descriptorTable2.get(thisElement2).toString() + nameSeparator + thisElement2.toString();
561 else
562 names2[i] = nameSeparator + thisElement2.toString();
563 }
564 Arrays.sort(names2);
565 setDescriptors2(names2);
566
567 setState(STATE_CONNECTED);
568 } catch (Exception e) {
569 handleConnectionError(e);
570 }
571 }
572
573 /**
574 * See if we are pointing to observed or satellite soundings
575 */
576 private void checkSetObsSat() {
577 System.out.println("checkSetObsSat: init");
578 if (getServer() == null || getGroup() == null || getDescriptor() == null) return;
579 System.out.println("checkSetObsSat: start");
580 satelliteSounding = false;
581 showWaitCursor();
582 Map<String, String> acctInfo = getAccountingInfo();
583 System.out.println("got acct info");
584 String user = acctInfo.get("user");
585 String proj = acctInfo.get("proj");
586 String[] paramString = new String[] {
587 "group", getGroup(), "descr", getDescriptor(), "param", "ZS", "num", "1", "pos", "all"
588 };
589 String request = Misc.makeUrl("adde", getServer(), "/point", paramString);
590 if (!(user.equals("") || proj.equals("")))
591 request += "&user=" + user + "&proj=" + proj;
592 System.out.println("Making request: " + request);
593 try {
594 AddePointDataReader dataReader = new AddePointDataReader(request);
595 }
596 catch (Exception e) {
597 if (e.getMessage().indexOf("Accounting data") >= 0) handleConnectionError(e);
598 else satelliteSounding = true;
599 }
600
601 showNormalCursor();
602 System.out.println("checkSetObsSat: done: " + satelliteSounding);
603 }
604
605 /**
606 * Override clearStations to clear times as well
607 */
608 protected void clearStations() {
609 super.clearStations();
610 clearTimes();
611 }
612
613 /**
614 * Remove all times from the user lists
615 */
616 protected void clearTimes() {
617 if (obsList!=null) obsList.setListData(new Vector());
618 if (timesList!=null) timesList.setListData(new Vector());
619 }
620
621 /**
622 * Update labels, etc.
623 */
624 protected void updateStatus() {
625 super.updateStatus();
626 if (getState() != STATE_CONNECTED) {
627 resetDescriptorBox();
628 clearStations();
629 }
630 else {
631 if (getDescriptor() == null) {
632 if (descriptorComboBox2 != null) {
633 descriptorComboBox2.setSelectedItem(LABEL_SELECT2);
634 }
635 clearStations();
636 setStatus("Select mandatory levels dataset");
637 return;
638 }
639 }
640 if (readSatelliteTask!=null) {
641 if(taskOk(readSatelliteTask)) {
642 setStatus("Reading sounding info from server");
643 } else {
644 readSatelliteTask = null;
645 setState(STATE_UNCONNECTED);
646 }
647 }
648 if (readStationTask!=null) {
649 if(taskOk(readStationTask)) {
650 setStatus("Reading available stations from server");
651 } else {
652 readStationTask = null;
653 setState(STATE_UNCONNECTED);
654 }
655 }
656 enableWidgets();
657 }
658
659 /**
660 * Overwrite base class method to create the station map
661 * with the appropriate properties.
662 *
663 * @return The new station map
664 */
665 protected StationLocationMap createStationMap() {
666 return new StationLocationMap(true) {
667 public void setDeclutter(boolean declutter) {
668 super.setDeclutter(declutter);
669 updateStatus();
670 }
671 };
672 }
673
674 /**
675 * Initialize the stations
676 *
677 * @param stationMap The station map
678 */
679 protected void initStationMap(StationLocationMap stationMap) {
680 CompositeRenderer renderer = new CompositeRenderer();
681 renderer.addRenderer(new McidasMap("/auxdata/maps/OUTLSUPW"));
682 renderer.addRenderer(new McidasMap("/auxdata/maps/OUTLSUPU"));
683 renderer.setColor(MAP_COLOR);
684 stationMap.setMapRenderer(renderer);
685
686 stationMap.addPropertyChangeListener(new PropertyChangeListener() {
687 public void propertyChange(PropertyChangeEvent pe) {
688 if (pe.getPropertyName().equals(
689 StationLocationMap.SELECTED_PROPERTY)) {
690 stationSelected((Station) pe.getNewValue());
691 } else if (pe.getPropertyName().equals(
692 StationLocationMap.UNSELECTED_PROPERTY)) {
693 stationUnselected((Station) pe.getNewValue());
694 } else if (pe.getPropertyName().equals(
695 StationLocationMap.ALL_UNSELECTED_PROPERTY)) {
696 unselectAll();
697 }
698 }
699 });
700
701 }
702
703 /**
704 * Handle a station selection
705 *
706 * @param station selected station
707 */
708 private void stationSelected(Station station) {
709 List selectedTimes = getSelectedTimes();
710 if ((selectedTimes == null) || (selectedTimes.size() < 1)) {
711 return;
712 }
713 for (int i = 0; i < selectedTimes.size(); i++) {
714 DateTime dt = (DateTime) selectedTimes.get(i);
715 List times =
716 soundingAdapter.getSoundingTimes((SoundingStation) station);
717 if ((times != null) && (times.size() > 0)) {
718 if (times.contains(dt)) {
719 SoundingOb newObs = new SoundingOb((SoundingStation)station, dt);
720 if ( !selectedObs.contains(newObs)) {
721 selectedObs.add(newObs);
722 }
723 }
724 }
725 }
726 obsList.setListData(selectedObs);
727 updateStatus();
728 }
729
730 /**
731 * Unselect a station
732 *
733 * @param station station to unselect
734 */
735 private void stationUnselected(Station station) {
736 List selectedTimes = getSelectedTimes();
737 if ((selectedTimes == null) || (selectedTimes.size() < 1)) {
738 return;
739 }
740 for (int i = 0; i < selectedTimes.size(); i++) {
741 SoundingOb newObs = new SoundingOb((SoundingStation)station,
742 (DateTime) selectedTimes.get(i));
743 if (selectedObs.contains(newObs)) {
744 selectedObs.remove(newObs);
745 }
746 }
747 obsList.setListData(selectedObs);
748 updateStatus();
749 }
750
751 /**
752 * Unselect all station
753 */
754 private void unselectAll() {
755 List selectedTimes = getSelectedTimes();
756 if ((selectedTimes == null) || (selectedTimes.size() < 1)) {
757 return;
758 }
759 selectedObs.removeAllElements();
760 obsList.setListData(selectedObs);
761 updateStatus();
762 }
763
764 /**
765 * This looks in the selectedList of SoundingOb-s for all stations
766 * that are selected for the current time. It creates and returns
767 * a list of the Station-s held by these current SoundingOb-s
768 *
769 * @return list of currently selected stations
770 */
771 // Question: why does this care about current time?
772 // more than one time can be selected...
773 private List getCurrentSelectedStations() {
774 List current = new ArrayList();
775 // DateTime currentTime = getSelectedTime();
776 for (int i = 0; i < selectedObs.size(); i++) {
777 SoundingOb ob = (SoundingOb) selectedObs.get(i);
778 // if (ob.getTimestamp().equals(currentTime)) {
779 current.add(ob.getStation());
780 // }
781 }
782 return current;
783 }
784
785 /**
786 * Get the current list of stations that are selected
787 */
788 private void setStations() {
789 stationMap.setStations(soundingAdapter.getStations(),
790 getCurrentSelectedStations(), stationMap.getDeclutter());
791 stationMap.redraw();
792 }
793
794 /**
795 * Set the SoundingAdapter used by this selector
796 *
797 * @param newAdapter new adapter
798 */
799 protected void setSoundingAdapter(AddeSoundingAdapter newAdapter) {
800 soundingAdapter = newAdapter;
801 selectedObs.removeAllElements();
802 obsList.setListData(selectedObs);
803 setStations();
804 setTimesListData(null);
805 updateStatus();
806 }
807
808 /**
809 * Set the data in the times list
810 *
811 * @param selected a list of times that should be selected
812 */
813 private void setTimesListData(List selected) {
814 if (soundingAdapter==null) return;
815 DateTime[] times = soundingAdapter.getSoundingTimes();
816 if (times != null) {
817 timesList.setListData(times);
818 if ((selected != null) && (selected.size() > 0)) {
819 ListModel lm = timesList.getModel();
820 int[] indices = new int[times.length];
821 int l = 0;
822 for (int i = 0; i < lm.getSize(); i++) {
823 if (selected.contains(lm.getElementAt(i))) {
824 indices[l++] = i;
825 }
826 }
827 if (l > 0) {
828 int[] selectedIndices = new int[l];
829 System.arraycopy(indices, 0, selectedIndices, 0, l);
830 timesList.setSelectedIndices(selectedIndices);
831 timesList.ensureIndexIsVisible(selectedIndices[l - 1]);
832 } else {
833 timesList.setSelectedValue(times[times.length - 1], true);
834 }
835 } else if (times.length > 0) {
836 timesList.setSelectedValue(times[times.length - 1], true);
837 }
838 } else {
839 LogUtil.userMessage("No data available");
840 }
841 }
842
843 /**
844 * Get the selected time.
845 *
846 * @return the time selected in the list
847 */
848 public DateTime getSelectedTime() {
849 return (DateTime) timesList.getSelectedValue();
850 }
851
852 /**
853 * Get the selected time.
854 *
855 * @return the time selected in the list
856 */
857 public List getSelectedTimes() {
858 return Misc.toList(timesList.getSelectedValues());
859 }
860
861 /**
862 * Create the list of times.
863 *
864 * @return List of times
865 */
866 private JList createTimesList() {
867 timesList = new JList();
868 timesList.setSelectionMode(
869 ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
870 timesList.addListSelectionListener(new ListSelectionListener() {
871 public void valueChanged(ListSelectionEvent e) {
872 if ( !timesList.isSelectionEmpty()
873 && !e.getValueIsAdjusting()) {
874 Object[] t = timesList.getSelectedValues();
875 newTimes(Misc.toList(t));
876 }
877 }
878 });
879 return timesList;
880 }
881
882 /**
883 * Set the new times
884 *
885 * @param times new times to use
886 */
887 private void newTimes(List times) {
888 if (stationMap == null) return;
889 List current = stationMap.getSelectedStations();
890 if ((current == null) || (current.size() < 1)) {
891 return;
892 }
893 selectedObs.removeAllElements();
894 for (int i = 0; i < times.size(); i++) {
895 DateTime dt = (DateTime) times.get(i);
896 for (int j = 0; j < current.size(); j++) {
897 SoundingStation ss = (SoundingStation) current.get(j);
898 List ssTimes =
899 soundingAdapter.getSoundingTimes(ss);
900 if ((ssTimes != null) && (times.size() > 0)) {
901 if (ssTimes.contains(dt)) {
902 SoundingOb newObs = new SoundingOb(ss, dt);
903 if ( !selectedObs.contains(newObs)) {
904 selectedObs.add(newObs);
905 }
906 }
907 }
908 }
909 }
910 obsList.setListData(selectedObs);
911 updateStatus();
912 }
913
914 /**
915 * Get the selected soundings
916 *
917 * @return List of selected soundings
918 */
919 public List getSelectedSoundings() {
920 return selectedObs;
921 }
922
923 /**
924 * Handle the selection of an ob
925 *
926 * @param event MouseEvent for selection
927 */
928 private void obsListClicked(MouseEvent event) {
929 if ( !SwingUtilities.isRightMouseButton(event)) {
930 return;
931 }
932 int index = obsList.locationToIndex(new Point(event.getX(),
933 event.getY()));
934 if ((index < 0) || (index >= selectedObs.size())) {
935 return;
936 }
937
938 final SoundingOb obs = (SoundingOb) selectedObs.get(index);
939
940 JPopupMenu popup = new JPopupMenu();
941 JMenuItem mi;
942
943 mi = new JMenuItem("Remove " + obs);
944 mi.addActionListener(new ActionListener() {
945 public void actionPerformed(ActionEvent e) {
946 selectedObs.remove(obs);
947 obsList.setListData(selectedObs);
948 updateStatus();
949 stationMap.setSelectedStations(getCurrentSelectedStations());
950 }
951 });
952
953 popup.add(mi);
954
955 mi = new JMenuItem("Remove all");
956 mi.addActionListener(new ActionListener() {
957 public void actionPerformed(ActionEvent e) {
958 selectedObs.removeAllElements();
959 obsList.setListData(selectedObs);
960 updateStatus();
961 stationMap.setSelectedStations(getCurrentSelectedStations());
962 }
963 });
964
965 popup.add(mi);
966
967 popup.show(obsList, event.getX(), event.getY());
968 }
969
970 /**
971 * Update the widget with the latest data.
972 *
973 * @throws Exception On badness
974 */
975 public void handleUpdate() throws Exception {
976 if (getState() != STATE_CONNECTED) {
977 //If not connected then update the server list
978 updateServerList();
979 } else {
980 //If we are already connected then update the rest of the chooser
981 descriptorChanged();
982 }
983 updateStatus();
984 }
985
986 /**
987 * Enable or disable the GUI widgets based on what has been
988 * selected.
989 */
990 protected void enableWidgets() {
991 super.enableWidgets();
992 boolean readingTask = (readSatelliteTask!=null || readStationTask!=null);
993 if (mainHoursCbx != null) mainHoursCbx.setVisible(!satelliteSounding);
994 if (descriptorComboBox2 != null) {
995 if (satelliteSounding) setDescriptors2(null);
996 descriptorComboBox2.setVisible(!satelliteSounding);
997 descriptorComboBox2.setEnabled(!readingTask &&
998 descriptorComboBox.getSelectedIndex() > 0);
999 }
1000 if (satelliteTimePanel!=null) {
1001 satelliteTimePanel.setVisible(satelliteSounding);
1002 GuiUtils.enableTree(satelliteTimePanel, !readingTask);
1003 if (satelliteSounding)
1004 satelliteTimeLabel.setText("Time:");
1005 else
1006 satelliteTimeLabel.setText("");
1007 }
1008 if (showAll!=null) showAll.setEnabled(!readingTask);
1009 }
1010
1011 /**
1012 * Respond to a change in the descriptor list.
1013 */
1014 protected void descriptorChanged() {
1015 descriptorChanged(true);
1016 }
1017
1018 /**
1019 * Respond to a change in the descriptor list.
1020 */
1021 protected void descriptorChanged(final boolean checkObsSat) {
1022 satelliteSounding = false;
1023 readSatelliteTask = startTask();
1024 enableWidgets();
1025 Misc.run(new Runnable() {
1026 public void run() {
1027 if (checkObsSat) checkSetObsSat();
1028 setAvailableStations(true);
1029 updateStatus();
1030 if(stopTaskAndIsOk(readSatelliteTask)) {
1031 readSatelliteTask = null;
1032 updateStatus();
1033 revalidate();
1034 } else {
1035 //User pressed cancel
1036 setState(STATE_UNCONNECTED);
1037 }
1038 }
1039 });
1040 updateStatus();
1041 }
1042
1043 /**
1044 * Update the station map with available stations.
1045 */
1046 private void setAvailableStations(final boolean forceNewAdapter) {
1047 if (getMandatoryDataset() == null) {
1048 updateStatus();
1049 return;
1050 }
1051 showWaitCursor();
1052 readStationTask = startTask();
1053 clearSelectedStations();
1054 updateStatus();
1055 doUpdateInner(forceNewAdapter);
1056 if(stopTaskAndIsOk(readStationTask)) {
1057 readStationTask = null;
1058 updateStatus();
1059 revalidate();
1060 } else {
1061 //User pressed cancel
1062 setState(STATE_UNCONNECTED);
1063 }
1064 showNormalCursor();
1065 }
1066
1067
1068 /**
1069 * Really update station map.
1070 *
1071 * @param forceNewAdapter If true then create a new adapter.
1072 * Else, tell the existing one to update.
1073 */
1074 private void doUpdateInner(final boolean forceNewAdapter) {
1075 try {
1076 if (forceNewAdapter || soundingAdapter == null) {
1077 AddeSoundingAdapter newAdapter;
1078 if (!satelliteSounding) {
1079 newAdapter = new AddeSoundingAdapter(getServer(),
1080 getMandatoryDataset(),
1081 getSigLevelDataset(),
1082 showMainHoursOnly,
1083 this);
1084 }
1085 else {
1086 newAdapter = new AddeSoundingAdapter(getServer(),
1087 getMandatoryDataset(),
1088 getSigLevelDataset(),
1089 satelliteTime,
1090 satellitePixel,
1091 this);
1092 }
1093 soundingAdapter = null;
1094 setSoundingAdapter(newAdapter);
1095 } else {
1096 List times = getSelectedTimes();
1097 soundingAdapter.update();
1098 setStations();
1099 setTimesListData(times);
1100 }
1101 } catch (Exception exc) {
1102 LogUtil.logException("Updating sounding data", exc);
1103 }
1104 }
1105
1106 /**
1107 * Load the data source in a thread
1108 */
1109 public void doLoadInThread() {
1110 List soundings = getSelectedSoundings();
1111 if (soundings.size() == 0) {
1112 userMessage("Please select one or more soundings.");
1113 return;
1114 }
1115 Hashtable ht = new Hashtable();
1116 getDataSourceProperties(ht);
1117
1118 makeDataSource(new RaobDataSet(soundingAdapter, soundings), DATA_TYPE, ht);
1119 saveServerState();
1120 }
1121
1122 /**
1123 * Add the times selector to the component.
1124 * @return superclass component with extra stuff
1125 */
1126 protected JPanel makeTimesPanel() {
1127
1128 // Make the 0 & 12 checkbox
1129 mainHoursCbx = new JCheckBox("00 & 12Z only", showMainHoursOnly);
1130 mainHoursCbx.addActionListener(new ActionListener() {
1131 public void actionPerformed(ActionEvent ev) {
1132 showMainHoursOnly = ((JCheckBox) ev.getSource()).isSelected();
1133 Misc.run(new Runnable() {
1134 public void run() {
1135 setAvailableStations(true);
1136 }
1137 });
1138 }
1139 });
1140
1141 // Make the select panel
1142 JScrollPane availablePanel = new JScrollPane(createTimesList());
1143 availablePanel.setPreferredSize(new Dimension(175, 50));
1144 JPanel selectPanel = GuiUtils.centerBottom(availablePanel, mainHoursCbx);
1145 selectPanel.setBorder(javax.swing.BorderFactory.createTitledBorder("Available"));
1146
1147 // Make the selected panel
1148 obsList = new JList();
1149 obsList.addMouseListener(new MouseAdapter() {
1150 public void mouseClicked(MouseEvent e) {
1151 obsListClicked(e);
1152 }
1153 });
1154 JScrollPane selectedPanel = new JScrollPane(obsList);
1155 selectedPanel.setPreferredSize(new Dimension(175, 50));
1156 selectedPanel.setBorder(javax.swing.BorderFactory.createTitledBorder("Selected"));
1157
1158 // Make the container panel
1159 JPanel timesPanel = new JPanel();
1160 selectPanel.setBackground(timesPanel.getBackground());
1161 selectedPanel.setBackground(timesPanel.getBackground());
1162
1163 GroupLayout layout = new GroupLayout(timesPanel);
1164 timesPanel.setLayout(layout);
1165 layout.setHorizontalGroup(
1166 layout.createParallelGroup(LEADING)
1167 .addGroup(layout.createSequentialGroup()
1168 // .addContainerGap()
1169 .addComponent(selectPanel, DEFAULT_SIZE, DEFAULT_SIZE, Short.MAX_VALUE)
1170 .addGap(GAP_RELATED)
1171 .addComponent(selectedPanel, DEFAULT_SIZE, DEFAULT_SIZE, Short.MAX_VALUE)
1172 )
1173 // .addContainerGap())
1174 );
1175 layout.setVerticalGroup(
1176 layout.createParallelGroup(LEADING)
1177 .addGroup(layout.createSequentialGroup()
1178 // .addContainerGap()
1179 .addGroup(layout.createParallelGroup(TRAILING)
1180 .addComponent(selectedPanel, LEADING, DEFAULT_SIZE, DEFAULT_SIZE, Short.MAX_VALUE)
1181 .addComponent(selectPanel, LEADING, DEFAULT_SIZE, DEFAULT_SIZE, Short.MAX_VALUE))
1182 )
1183 // .addContainerGap())
1184 );
1185
1186 JComponent temp = super.makeTimesPanel();
1187 temp.setBorder(javax.swing.BorderFactory.createEtchedBorder());
1188 McVGuiUtils.setComponentHeight(timesPanel, temp);
1189
1190 return timesPanel;
1191 }
1192
1193 /**
1194 * Make the UI for this selector.
1195 *
1196 * @return The gui
1197 */
1198 public JComponent doMakeContents() {
1199 JPanel myPanel = new JPanel();
1200
1201 McVGuiUtils.setComponentWidth(descriptorComboBox, Width.DOUBLEDOUBLE);
1202 McVGuiUtils.setComponentWidth(descriptorComboBox2, descriptorComboBox);
1203 McVGuiUtils.setComponentWidth(satelliteTimeComboBox, Width.DOUBLE);
1204 McVGuiUtils.setComponentWidth(satellitePixelTextField, Width.DOUBLE);
1205
1206 satelliteTimePanel = McVGuiUtils.sideBySide(
1207 McVGuiUtils.sideBySide(satelliteTimeComboBox, satelliteTimeButton),
1208 McVGuiUtils.makeLabeledComponent("IDN:", satellitePixelTextField)
1209 );
1210 satelliteTimePanel.setVisible(false);
1211
1212 JPanel extraPanel = McVGuiUtils.sideBySide(
1213 GuiUtils.left(McVGuiUtils.sideBySide(descriptorComboBox2, satelliteTimePanel, 0)),
1214 GuiUtils.right(showAll));
1215
1216 // McVGuiUtils.setComponentWidth(extraPanel, descriptorComboBox);
1217
1218 JLabel stationLabel = McVGuiUtils.makeLabelRight("Stations:");
1219 addServerComp(stationLabel);
1220
1221 JComponent stationPanel = getStationMap();
1222 registerStatusComp("stations", stationPanel);
1223 // addServerComp(stationPanel);
1224 addDescComp(stationPanel);
1225
1226 JLabel timesLabel = McVGuiUtils.makeLabelRight("");
1227 // addServerComp(timesLabel);
1228 addDescComp(timesLabel);
1229
1230 JPanel timesPanel = makeTimesPanel();
1231 // timesPanel.setBorder(javax.swing.BorderFactory.createEtchedBorder());
1232 // addServerComp(timesPanel);
1233 addDescComp(timesPanel);
1234
1235 enableWidgets();
1236 updateStatus();
1237
1238 GroupLayout layout = new GroupLayout(myPanel);
1239 myPanel.setLayout(layout);
1240 layout.setHorizontalGroup(
1241 layout.createParallelGroup(LEADING)
1242 .addGroup(layout.createSequentialGroup()
1243 .addGroup(layout.createParallelGroup(LEADING)
1244 .addGroup(layout.createSequentialGroup()
1245 .addComponent(descriptorLabel)
1246 .addGap(GAP_RELATED)
1247 .addComponent(descriptorComboBox))
1248 .addGroup(layout.createSequentialGroup()
1249 .addComponent(satelliteTimeLabel)
1250 .addGap(GAP_RELATED)
1251 .addComponent(extraPanel))
1252 .addGroup(layout.createSequentialGroup()
1253 .addComponent(stationLabel)
1254 .addGap(GAP_RELATED)
1255 .addComponent(stationPanel, PREFERRED_SIZE, DEFAULT_SIZE, Short.MAX_VALUE))
1256 .addGroup(layout.createSequentialGroup()
1257 .addComponent(timesLabel)
1258 .addGap(GAP_RELATED)
1259 .addComponent(timesPanel, PREFERRED_SIZE, DEFAULT_SIZE, Short.MAX_VALUE))))
1260 );
1261 layout.setVerticalGroup(
1262 layout.createParallelGroup(LEADING)
1263 .addGroup(layout.createSequentialGroup()
1264 .addGroup(layout.createParallelGroup(LEADING)
1265 .addComponent(descriptorLabel)
1266 .addComponent(descriptorComboBox))
1267 .addPreferredGap(RELATED)
1268 .addGroup(layout.createParallelGroup(LEADING)
1269 .addComponent(satelliteTimeLabel)
1270 .addComponent(extraPanel))
1271 .addPreferredGap(RELATED)
1272 .addGroup(layout.createParallelGroup(LEADING)
1273 .addComponent(stationLabel)
1274 .addComponent(stationPanel, PREFERRED_SIZE, DEFAULT_SIZE, Short.MAX_VALUE))
1275 .addPreferredGap(RELATED)
1276 .addGroup(layout.createParallelGroup(LEADING)
1277 .addComponent(timesLabel)
1278 .addComponent(timesPanel, PREFERRED_SIZE, DEFAULT_SIZE, Short.MAX_VALUE))
1279 .addPreferredGap(RELATED))
1280 );
1281
1282
1283
1284
1285
1286 setInnerPanel(myPanel);
1287 return super.doMakeContents(true);
1288 }
1289
1290 }