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 }