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