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