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