001/* 002 * This file is part of McIDAS-V 003 * 004 * Copyright 2007-2025 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 https://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 */ 106 107public class AddeRaobChooser extends AddePointDataChooser { 108 109 private static final long serialVersionUID = 1L; 110 111 /** Property for the data type. */ 112 public static String DATA_TYPE = "RAOB"; 113 114 /** Significant level objects corresponding to mandatory level objects */ 115 private Hashtable descriptorTable2 = new Hashtable(); 116 private JComboBox descriptorComboBox2 = new JComboBox(); 117 protected String[] descriptorNames2; 118 private String LABEL_SELECT2 = " -- Optional Significant Levels -- "; 119 private JCheckBox showAll = new JCheckBox("Show all sources"); 120 private Object readSatelliteTask; 121 122 /** This flag keeps track of observed/satellite soundings */ 123 private boolean satelliteSounding = false; 124 125 /** Selector for times when pointing to satellite data (required field) */ 126 private JLabel satelliteTimeLabel = McVGuiUtils.makeLabelRight(""); 127 private JPanel satelliteTimePanel; 128 private JButton satelliteTimeButton; 129 private JComboBox satelliteTimeComboBox; 130 private JTextField satellitePixelTextField; 131 private String satelliteTime = ""; 132 private String satellitePixel = "1"; 133 private List satelliteTimes = new ArrayList(); 134 135 /** We need to be able to enable/disable this based on sounding type */ 136 private JCheckBox mainHoursCbx; 137 138 /** This is a virtual timestamp that tracks if the threaded adde connection should be aborted or not */ 139 private int connectionStep = 0; 140 141 /** handle on the station update task */ 142 private Object readStationTask; 143 144 /** list of times */ 145 private JList timesList; 146 147 /** list of observations */ 148 private JList obsList; 149 150 /** selected observations */ 151 private Vector selectedObs = new Vector(); 152 153 /** sounding adapter used by this selector */ 154 AddeSoundingAdapter soundingAdapter; 155 156 /** flag for 0 and 12z only */ 157 private boolean showMainHoursOnly = true; 158 159 /** 160 * Construct a {@code RaobChooser} using the manager 161 * and the root XML that defines this object. 162 * 163 * @param mgr {@code IdvChooserManager} that controls this chooser. 164 * @param root Root element of the XML that defines this object. 165 */ 166 public AddeRaobChooser(IdvChooserManager mgr, Element root) { 167 super(mgr, root); 168 169 setSelectString(" -- Select Mandatory Levels -- "); 170 171 descriptorComboBox2.addItemListener(new ItemListener() { 172 public void itemStateChanged(ItemEvent e) { 173 if ( !ignoreDescriptorChange 174 && (e.getStateChange() == ItemEvent.SELECTED)) { 175 descriptorChanged(false); 176 } 177 } 178 }); 179 descriptorComboBox2.setEnabled(false); 180 181 showAll.addItemListener(new ItemListener() { 182 public void itemStateChanged(ItemEvent e) { 183 if (getState() == STATE_CONNECTED) { 184 doConnect(); 185 } 186 } 187 }); 188 189 satelliteTimeComboBox = new JComboBox(); 190 satelliteTimeComboBox.setEditable(true); 191 satelliteTimeComboBox.addItemListener(new ItemListener() { 192 public void itemStateChanged(ItemEvent e) { 193 if (e.getStateChange() == ItemEvent.DESELECTED) return; 194 satelliteTime = satelliteTimeComboBox.getSelectedItem().toString(); 195 Misc.run(new Runnable() { 196 public void run() { 197 setAvailableStations(true); 198 } 199 }); 200 } 201 }); 202 203 satelliteTimeButton = McVGuiUtils.makeImageButton(ICON_UPDATE, "Request list of available times from server"); 204 satelliteTimeButton.addActionListener(new ActionListener() { 205 public void actionPerformed(ActionEvent e) { 206 sampleTimes(); 207 } 208 }); 209 210 satellitePixelTextField = new JTextField(satellitePixel); 211 satellitePixelTextField.addActionListener(new ActionListener() { 212 public void actionPerformed(ActionEvent e) { 213 satellitePixel = satellitePixelTextField.getText().replace('-', ' '); 214 Misc.run(new Runnable() { 215 public void run() { 216 setAvailableStations(true); 217 } 218 }); 219 } 220 }); 221 } 222 223 /** 224 * Tell the AddeChooser our name 225 * 226 * @return The name 227 */ 228 public String getDataName() { 229 return "Sounding Data"; 230 } 231 232 /** 233 * Get the descriptor widget label. 234 * 235 * @return label for the descriptor widget 236 */ 237 public String getDescriptorLabel() { 238 return "Soundings"; 239 } 240 241 /** 242 * get default display to create 243 * 244 * @return default display 245 */ 246 protected String getDefaultDisplayType() { 247 return "raob_skewt"; 248 } 249 250 /** 251 * Get the mandatory dataset name. 252 * 253 * @return mandatory dataset name 254 */ 255 private String getMandatoryDataset() { 256 if (getDescriptor() == null) return null; 257 return getGroup() + "/" + getDescriptor(); 258 } 259 260 /** 261 * Get the sig level dataset name. 262 * 263 * @return sig level dataset name 264 */ 265 private String getSigLevelDataset() { 266 if (getDescriptor2() == null) return getMandatoryDataset(); 267 return getGroup() + "/" + getDescriptor2(); 268 } 269 270 /** 271 * Add a listener to the given combobox that will set the 272 * state to unconnected. 273 * 274 * @param box The box to listen to. 275 */ 276 protected void clearOnChange(final JComboBox box) { 277 box.addItemListener(new ItemListener() { 278 public void itemStateChanged(ItemEvent e) { 279 if ( !ignoreStateChangedEvents) { 280 setState(STATE_UNCONNECTED); 281 GuiUtils.setListData(descriptorComboBox, new Vector()); 282 GuiUtils.setListData(descriptorComboBox2, new Vector()); 283 } 284 } 285 }); 286 } 287 288 /** 289 * Reset the descriptor stuff. 290 */ 291 protected void resetDescriptorBox() { 292 ignoreDescriptorChange = true; 293 descriptorComboBox.setSelectedItem(LABEL_SELECT); 294 if (descriptorComboBox2 != null) { 295 descriptorComboBox2.setSelectedItem(LABEL_SELECT2); 296 descriptorComboBox2.setEnabled(false); 297 } 298 ignoreDescriptorChange = false; 299 } 300 301 /** 302 * Initialize the descriptor list from a list of names. 303 * 304 * @param names2 List of names. 305 */ 306 protected void setDescriptors2(String[] names2) { 307 synchronized (WIDGET_MUTEX) { 308 ignoreDescriptorChange = true; 309 descriptorComboBox2.removeAllItems(); 310 descriptorComboBox2.addItem(LABEL_SELECT2); 311 descriptorNames2 = names2; 312 if ((names2 == null) || (names2.length == 0)) { 313 ignoreDescriptorChange = false; 314 return; 315 } 316 for (int j = 0; j < names2.length; j++) { 317 descriptorComboBox2.addItem(names2[j]); 318 } 319 ignoreDescriptorChange = false; 320 } 321 } 322 323 /** 324 * Get the selected descriptor. 325 * 326 * @return the currently selected descriptor. 327 */ 328 protected String getDescriptor2() { 329 if (descriptorTable2 == null) { 330 return null; 331 } 332 String selection = (String) descriptorComboBox2.getSelectedItem(); 333 if (selection == null) { 334 return null; 335 } 336 if (selection.equals(LABEL_SELECT2)) { 337 return null; 338 } 339 if (!selection.contains(nameSeparator)) { 340 return (String)descriptorTable2.get(selection); 341 } 342 else { 343 String[] toks = selection.split(nameSeparator); 344 String key = toks[1].trim(); 345 return (String)descriptorTable2.get(key); 346 } 347 } 348 349 /** 350 * Method to call if the server changed. 351 */ 352 protected void connectToServer() { 353 clearStations(); 354 setDescriptors2(null); 355 super.connectToServer(); 356 setAvailableStations(true); 357 } 358 359 /** 360 * Do we have times selected. 361 * @return Do we have times 362 */ 363 public boolean timesOk() { 364 return haveTimeSelected(); 365 } 366 367 /** 368 * Are there any times selected. 369 * 370 * @return Any times selected. 371 */ 372 protected boolean haveTimeSelected() { 373 if (selectedObs!=null) { 374 if (selectedObs.size() > 0) return true; 375 } 376 return false; 377 } 378 379 /** 380 * Do nothing for read times... 381 * doUpdateInner handles all of this with an AddeSoundingAdapter 382 */ 383 public void readTimes() { } 384 385 /** 386 * Wrapper for sampleTimesInner 387 * Starts in a new thread and handles UI updating 388 */ 389 private void sampleTimes() { 390 readSatelliteTask = startTask(); 391 enableWidgets(); 392 Misc.run(new Runnable() { 393 public void run() { 394 sampleTimesInner(); 395 if(stopTaskAndIsOk(readSatelliteTask)) { 396 readSatelliteTask = null; 397 GuiUtils.setListData(satelliteTimeComboBox, satelliteTimes); 398 revalidate(); 399 } else { 400 //User pressed cancel 401 setState(STATE_UNCONNECTED); 402 } 403 } 404 }); 405 updateStatus(); 406 } 407 408 /** 409 * Different way of reading times... for satellite soundings, do the following: 410 * PTLIST GROUP/DESCRIPTOR.Z SEL='ROW X; COL Y' PAR=TIME 411 * where Z starts at 0 (expect an error), then goes to 1 and increases monotonically in outer loop until error 412 * and X starts at 1 and increases monotonically in middle loop until error 413 * and Y starts at 1 and increases by 25000 or so in inner loop until error 414 * This samples times across the dataset 415 */ 416 private void sampleTimesInner() { 417 if (getDescriptor()==null) return; 418 showWaitCursor(); 419 int posMax = 9999; 420 int rowMax = 9999; 421 int colMax = 999999; 422 int colSkip = 24000; 423 int consecutiveFailures = 0; 424 Map<String, String> acctInfo = getAccountingInfo(); 425 String user = acctInfo.get("user"); 426 String proj = acctInfo.get("proj"); 427 String appendUserProj = ""; 428 if (!(user.equals("") || proj.equals(""))) 429 appendUserProj += "&user=" + user + "&proj=" + proj; 430 satelliteTimes = new ArrayList(); 431 for (int pos = 0; pos < posMax; pos++) { 432 for (int row=1; row<rowMax; row++) { 433 for (int col=1; col<colMax; col+=colSkip) { 434 435 String[] paramString = new String[] { 436 "group", getGroup(), "descr", getDescriptor(), "param", "DAY TIME", "num", "1", 437 "pos", Integer.toString(pos), 438 "select", "'ROW " + row + "; COL " + col + "'" 439 }; 440 String request = Misc.makeUrl("adde", getServer(), "/point", paramString); 441 request += appendUserProj; 442 try { 443 AddePointDataReader dataReader = new AddePointDataReader(request); 444 int[][] data = dataReader.getData(); 445 if (data[0].length == 0) throw new Exception(); 446 for (int i = 0; i < data[0].length; i++) { 447 int day = data[0][i]; 448 int time = data[1][i]; 449 DateTime dt = new DateTime(McIDASUtil.mcDayTimeToSecs(day, time)); 450 String timeString = dt.timeString().substring(0,5); 451 if (satelliteTimes.indexOf(timeString) < 0) { 452 satelliteTimes.add(timeString); 453 } 454 } 455 // Reset consecutive failure count when you get good data 456 consecutiveFailures=0; 457 } 458 catch (Exception e) { 459 460 // We are at the beginning of a position 461 // Log a failure and increment the position 462 if (col==1 && row==1) { 463 row=rowMax; 464 consecutiveFailures++; 465 // If we have failed a few times in a row, bail completely 466 if (consecutiveFailures > 2) { 467 pos=posMax; 468 } 469 } 470 471 // If we failed at the first column, increment the position 472 if (col==1) row=rowMax; 473 474 // We have an exception, increment the row 475 col = colMax; 476 477 } 478 } 479 } 480 } 481 482 Collections.sort(satelliteTimes); 483 showNormalCursor(); 484 } 485 486 /** 487 * Generate a list of image descriptors for the descriptor list. 488 */ 489 protected void readDescriptors() { 490 try { 491 StringBuffer buff = getGroupUrl(REQ_DATASETINFO, getGroup()); 492 buff.append("&type=" + getDataType()); 493 DataSetInfo dsinfo = new DataSetInfo(buff.toString()); 494 descriptorTable = dsinfo.getDescriptionTable(); 495 descriptorTable2 = new Hashtable(); 496 497 if (!showAll.isSelected()) { 498 // Filter out anything not Upper Air Mandatory or Significant 499 for (Enumeration enumeration = descriptorTable.keys(); enumeration.hasMoreElements();) { 500 Object key = enumeration.nextElement(); 501 String keyString = key.toString(); 502 String descriptorString = descriptorTable.get(key).toString(); 503 if (keyString.toUpperCase().indexOf("MAND") >= 0 || descriptorString.indexOf("MAND") >= 0) { 504 continue; 505 } 506 if (keyString.toUpperCase().indexOf("SIG") >= 0 || descriptorString.indexOf("SIG") >= 0) { 507 descriptorTable2.put(key, descriptorTable.get(key)); 508 descriptorTable.remove(key); 509 continue; 510 } 511 if (keyString.toUpperCase().indexOf("UPPER AIR") >= 0 || 512 descriptorString.indexOf("UPPER") >= 0 || 513 descriptorString.indexOf("UPPR") >= 0) { 514 descriptorTable2.put(key, descriptorTable.get(key)); 515 continue; 516 } 517 if (keyString.toUpperCase().indexOf("SOUNDER") >= 0 || 518 descriptorString.indexOf("SND") >= 0 || 519 descriptorString.indexOf("SNDR") >= 0) { 520 descriptorTable2.put(key, descriptorTable.get(key)); 521 continue; 522 } 523 if (keyString.toUpperCase().indexOf("GRET") >= 0 || descriptorString.indexOf("GRET") >= 0) { 524 descriptorTable2.put(key, descriptorTable.get(key)); 525 continue; 526 } 527 if (keyString.toUpperCase().indexOf("SRET") >= 0 || descriptorString.indexOf("SRET") >= 0) { 528 descriptorTable2.put(key, descriptorTable.get(key)); 529 continue; 530 } 531 descriptorTable.remove(key); 532 } 533 } 534 else { 535 // We have been told to Show All... put all descriptors into both categories 536 for (Enumeration enumeration = descriptorTable.keys(); enumeration.hasMoreElements();) { 537 Object key = enumeration.nextElement(); 538 descriptorTable2.put(key, descriptorTable.get(key)); 539 } 540 } 541 542 String[] names = new String[descriptorTable.size()]; 543 Enumeration enumeration = descriptorTable.keys(); 544 for (int i = 0; enumeration.hasMoreElements(); i++) { 545 Object thisElement = enumeration.nextElement(); 546 if (!isLocalServer()) 547 names[i] = descriptorTable.get(thisElement).toString() + nameSeparator + thisElement.toString(); 548 else 549 names[i] = thisElement.toString(); 550 } 551 Arrays.sort(names); 552 setDescriptors(names); 553 554 String[] names2 = new String[descriptorTable2.size()]; 555 Enumeration enumeration2 = descriptorTable2.keys(); 556 for (int i = 0; enumeration2.hasMoreElements(); i++) { 557 Object thisElement2 = enumeration2.nextElement(); 558 if (!isLocalServer()) 559 names2[i] = descriptorTable2.get(thisElement2).toString() + nameSeparator + thisElement2.toString(); 560 else 561 names2[i] = nameSeparator + thisElement2.toString(); 562 } 563 Arrays.sort(names2); 564 setDescriptors2(names2); 565 566 setState(STATE_CONNECTED); 567 } catch (Exception e) { 568 handleConnectionError(e); 569 } 570 } 571 572 /** 573 * See if we are pointing to observed or satellite soundings 574 */ 575 private void checkSetObsSat() { 576 System.out.println("checkSetObsSat: init"); 577 if (getServer() == null || getGroup() == null || getDescriptor() == null) return; 578 System.out.println("checkSetObsSat: start"); 579 satelliteSounding = false; 580 showWaitCursor(); 581 Map<String, String> acctInfo = getAccountingInfo(); 582 System.out.println("got acct info"); 583 String user = acctInfo.get("user"); 584 String proj = acctInfo.get("proj"); 585 String[] paramString = new String[] { 586 "group", getGroup(), "descr", getDescriptor(), "param", "ZS", "num", "1", "pos", "all" 587 }; 588 String request = Misc.makeUrl("adde", getServer(), "/point", paramString); 589 if (!(user.equals("") || proj.equals(""))) 590 request += "&user=" + user + "&proj=" + proj; 591 System.out.println("Making request: " + request); 592 try { 593 AddePointDataReader dataReader = new AddePointDataReader(request); 594 } 595 catch (Exception e) { 596 if (e.getMessage().indexOf("Accounting data") >= 0) handleConnectionError(e); 597 else satelliteSounding = true; 598 } 599 600 showNormalCursor(); 601 System.out.println("checkSetObsSat: done: " + satelliteSounding); 602 } 603 604 /** 605 * Override clearStations to clear times as well 606 */ 607 protected void clearStations() { 608 super.clearStations(); 609 clearTimes(); 610 } 611 612 /** 613 * Remove all times from the user lists 614 */ 615 protected void clearTimes() { 616 if (obsList!=null) obsList.setListData(new Vector()); 617 if (timesList!=null) timesList.setListData(new Vector()); 618 } 619 620 /** 621 * Update labels, etc. 622 */ 623 protected void updateStatus() { 624 super.updateStatus(); 625 if (getState() != STATE_CONNECTED) { 626 resetDescriptorBox(); 627 clearStations(); 628 } 629 else { 630 if (getDescriptor() == null) { 631 if (descriptorComboBox2 != null) { 632 descriptorComboBox2.setSelectedItem(LABEL_SELECT2); 633 } 634 clearStations(); 635 setStatus("Select mandatory levels dataset"); 636 return; 637 } 638 } 639 if (readSatelliteTask!=null) { 640 if(taskOk(readSatelliteTask)) { 641 setStatus("Reading sounding info from server"); 642 } else { 643 readSatelliteTask = null; 644 setState(STATE_UNCONNECTED); 645 } 646 } 647 if (readStationTask!=null) { 648 if(taskOk(readStationTask)) { 649 setStatus("Reading available stations from server"); 650 } else { 651 readStationTask = null; 652 setState(STATE_UNCONNECTED); 653 } 654 } 655 enableWidgets(); 656 } 657 658 /** 659 * Overwrite base class method to create the station map 660 * with the appropriate properties. 661 * 662 * @return The new station map 663 */ 664 protected StationLocationMap createStationMap() { 665 return new StationLocationMap(true) { 666 public void setDeclutter(boolean declutter) { 667 super.setDeclutter(declutter); 668 updateStatus(); 669 } 670 }; 671 } 672 673 /** 674 * Initialize the stations 675 * 676 * @param stationMap The station map 677 */ 678 protected void initStationMap(StationLocationMap stationMap) { 679 CompositeRenderer renderer = new CompositeRenderer(); 680 renderer.addRenderer(new McidasMap("/auxdata/maps/OUTLSUPW")); 681 renderer.addRenderer(new McidasMap("/auxdata/maps/OUTLSUPU")); 682 renderer.setColor(MAP_COLOR); 683 stationMap.setMapRenderer(renderer); 684 685 stationMap.addPropertyChangeListener(new PropertyChangeListener() { 686 public void propertyChange(PropertyChangeEvent pe) { 687 if (pe.getPropertyName().equals( 688 StationLocationMap.SELECTED_PROPERTY)) { 689 stationSelected((Station) pe.getNewValue()); 690 } else if (pe.getPropertyName().equals( 691 StationLocationMap.UNSELECTED_PROPERTY)) { 692 stationUnselected((Station) pe.getNewValue()); 693 } else if (pe.getPropertyName().equals( 694 StationLocationMap.ALL_UNSELECTED_PROPERTY)) { 695 unselectAll(); 696 } 697 } 698 }); 699 700 } 701 702 /** 703 * Handle a station selection 704 * 705 * @param station selected station 706 */ 707 private void stationSelected(Station station) { 708 List selectedTimes = getSelectedTimes(); 709 if ((selectedTimes == null) || (selectedTimes.size() < 1)) { 710 return; 711 } 712 for (int i = 0; i < selectedTimes.size(); i++) { 713 DateTime dt = (DateTime) selectedTimes.get(i); 714 List times = 715 soundingAdapter.getSoundingTimes((SoundingStation) station); 716 if ((times != null) && (times.size() > 0)) { 717 if (times.contains(dt)) { 718 SoundingOb newObs = new SoundingOb((SoundingStation)station, dt); 719 if ( !selectedObs.contains(newObs)) { 720 selectedObs.add(newObs); 721 } 722 } 723 } 724 } 725 obsList.setListData(selectedObs); 726 updateStatus(); 727 } 728 729 /** 730 * Unselect a station 731 * 732 * @param station station to unselect 733 */ 734 private void stationUnselected(Station station) { 735 List selectedTimes = getSelectedTimes(); 736 if ((selectedTimes == null) || (selectedTimes.size() < 1)) { 737 return; 738 } 739 for (int i = 0; i < selectedTimes.size(); i++) { 740 SoundingOb newObs = new SoundingOb((SoundingStation)station, 741 (DateTime) selectedTimes.get(i)); 742 if (selectedObs.contains(newObs)) { 743 selectedObs.remove(newObs); 744 } 745 } 746 obsList.setListData(selectedObs); 747 updateStatus(); 748 } 749 750 /** 751 * Unselect all station 752 */ 753 private void unselectAll() { 754 List selectedTimes = getSelectedTimes(); 755 if ((selectedTimes == null) || (selectedTimes.size() < 1)) { 756 return; 757 } 758 selectedObs.removeAllElements(); 759 obsList.setListData(selectedObs); 760 updateStatus(); 761 } 762 763 /** 764 * This looks in the selectedList of SoundingOb-s for all stations 765 * that are selected for the current time. It creates and returns 766 * a list of the Station-s held by these current SoundingOb-s 767 * 768 * @return list of currently selected stations 769 */ 770 // Question: why does this care about current time? 771 // more than one time can be selected... 772 private List getCurrentSelectedStations() { 773 List current = new ArrayList(); 774// DateTime currentTime = getSelectedTime(); 775 for (int i = 0; i < selectedObs.size(); i++) { 776 SoundingOb ob = (SoundingOb) selectedObs.get(i); 777// if (ob.getTimestamp().equals(currentTime)) { 778 current.add(ob.getStation()); 779// } 780 } 781 return current; 782 } 783 784 /** 785 * Get the current list of stations that are selected 786 */ 787 private void setStations() { 788 stationMap.setStations(soundingAdapter.getStations(), 789 getCurrentSelectedStations(), stationMap.getDeclutter()); 790 stationMap.redraw(); 791 } 792 793 /** 794 * Set the SoundingAdapter used by this selector 795 * 796 * @param newAdapter new adapter 797 */ 798 protected void setSoundingAdapter(AddeSoundingAdapter newAdapter) { 799 soundingAdapter = newAdapter; 800 selectedObs.removeAllElements(); 801 obsList.setListData(selectedObs); 802 setStations(); 803 setTimesListData(null); 804 updateStatus(); 805 } 806 807 /** 808 * Set the data in the times list 809 * 810 * @param selected a list of times that should be selected 811 */ 812 private void setTimesListData(List selected) { 813 if (soundingAdapter==null) return; 814 DateTime[] times = soundingAdapter.getSoundingTimes(); 815 if (times != null) { 816 timesList.setListData(times); 817 if ((selected != null) && (selected.size() > 0)) { 818 ListModel lm = timesList.getModel(); 819 int[] indices = new int[times.length]; 820 int l = 0; 821 for (int i = 0; i < lm.getSize(); i++) { 822 if (selected.contains(lm.getElementAt(i))) { 823 indices[l++] = i; 824 } 825 } 826 if (l > 0) { 827 int[] selectedIndices = new int[l]; 828 System.arraycopy(indices, 0, selectedIndices, 0, l); 829 timesList.setSelectedIndices(selectedIndices); 830 timesList.ensureIndexIsVisible(selectedIndices[l - 1]); 831 } else { 832 timesList.setSelectedValue(times[times.length - 1], true); 833 } 834 } else if (times.length > 0) { 835 timesList.setSelectedValue(times[times.length - 1], true); 836 } 837 } else { 838 LogUtil.userMessage("No data available"); 839 } 840 } 841 842 /** 843 * Get the selected time. 844 * 845 * @return the time selected in the list 846 */ 847 public DateTime getSelectedTime() { 848 return (DateTime)timesList.getSelectedValue(); 849 } 850 851 /** 852 * Get the selected time. 853 * 854 * @return the time selected in the list 855 */ 856 public List getSelectedTimes() { 857 return timesList.getSelectedValuesList(); 858 } 859 860 /** 861 * Create the list of times. 862 * 863 * @return List of times 864 */ 865 private JList createTimesList() { 866 timesList = new JList(); 867 timesList.setSelectionMode( 868 ListSelectionModel.MULTIPLE_INTERVAL_SELECTION); 869 timesList.addListSelectionListener(new ListSelectionListener() { 870 public void valueChanged(ListSelectionEvent e) { 871 if ( !timesList.isSelectionEmpty() 872 && !e.getValueIsAdjusting()) { 873 newTimes(timesList.getSelectedValuesList()); 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, 584); 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 setInnerPanel(myPanel); 1281 return super.doMakeContents(true); 1282 } 1283 1284}