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 */ 028 029package edu.wisc.ssec.mcidasv.control; 030 031import static ucar.unidata.util.GuiUtils.hbox; 032import static ucar.unidata.util.GuiUtils.filler; 033import static ucar.unidata.util.GuiUtils.left; 034import static ucar.unidata.util.GuiUtils.topLeft; 035import static edu.wisc.ssec.mcidasv.util.CollectionHelpers.arr; 036 037import java.awt.BorderLayout; 038import java.awt.ComponentOrientation; 039import java.awt.Container; 040import java.awt.Dimension; 041import java.awt.FlowLayout; 042import java.awt.Font; 043import java.beans.PropertyChangeEvent; 044import java.io.File; 045import java.io.FileWriter; 046import java.io.IOException; 047import java.rmi.RemoteException; 048import java.text.SimpleDateFormat; 049import java.util.ArrayList; 050import java.util.Date; 051import java.util.List; 052import java.util.Scanner; 053import java.util.TimeZone; 054 055import javax.swing.*; 056 057import edu.wisc.ssec.mcidasv.McIDASV; 058import edu.wisc.ssec.mcidasv.adt.Data; 059import edu.wisc.ssec.mcidasv.adt.Env; 060import edu.wisc.ssec.mcidasv.adt.Functions; 061import edu.wisc.ssec.mcidasv.adt.History; 062import edu.wisc.ssec.mcidasv.adt.Main; 063import edu.wisc.ssec.mcidasv.adt.ReadIRImage; 064import edu.wisc.ssec.mcidasv.util.WebBrowser; 065 066import org.slf4j.Logger; 067import org.slf4j.LoggerFactory; 068 069import ucar.unidata.data.DataChoice; 070import ucar.unidata.data.DataInstance; 071import ucar.unidata.data.DataSourceImpl; 072import ucar.unidata.data.DataUtil; 073import ucar.unidata.data.grid.GridUtil; 074import ucar.unidata.data.imagery.AddeImageDataSource; 075import ucar.unidata.data.imagery.AddeImageDescriptor; 076import ucar.unidata.data.imagery.ImageDataSource; 077import ucar.unidata.geoloc.LatLonRect; 078import ucar.unidata.idv.DisplayInfo; 079import ucar.unidata.idv.control.DisplayControlImpl; 080import ucar.unidata.ui.LatLonWidget; 081import ucar.unidata.util.GuiUtils; 082import ucar.unidata.util.Misc; 083import ucar.unidata.view.geoloc.NavigatedDisplay; 084import ucar.unidata.xml.XmlObjectStore; 085import ucar.visad.Util; 086import ucar.visad.display.Animation; 087import ucar.visad.display.PointProbe; 088import ucar.visad.display.SelectorDisplayable; 089import ucar.visad.quantities.AirTemperature; 090import visad.CommonUnit; 091import visad.DateTime; 092import visad.DisplayEvent; 093import visad.FieldImpl; 094import visad.FlatField; 095import visad.Real; 096import visad.RealTuple; 097import visad.RealTupleType; 098import visad.Set; 099import visad.VisADException; 100import visad.georef.EarthLocation; 101import visad.georef.EarthLocationTuple; 102import visad.georef.LatLonPoint; 103import visad.util.DataUtility; 104import edu.wisc.ssec.mcidas.AreaDirectory; 105 106/** 107 * Advanced Dvorak Technique Display Control 108 * Algorithm developed at UW Madison/CIMSS to objectively determine tropical 109 * cyclone intensity from geostationary satellite infrared imagery. 110 * 111 * @author Tim Olander 112 */ 113 114public class ADTControl extends DisplayControlImpl { 115 116 private static final Logger logger = LoggerFactory.getLogger(ADTControl.class); 117 118 // Tooltip strings for the various UI buttons and inputs 119 private static final String TOOLTIP_LAND_FLAG_ON = "Apply ADT Land Interaction Rule"; 120 private static final String TOOLTIP_LAND_FLAG_OFF = "Do Not Apply ADT Land Interaction Rule"; 121 private static final String TOOLTIP_MANUAL = "Manually Select Storm Center In Image"; 122 private static final String TOOLTIP_AUTOMATIC = "Select Forecast File For First Guess Below"; 123 private static final String TOOLTIP_HISTORY = "Choose a File to Retain and Store ADT Output Data"; 124 private static final String TOOLTIP_PMW = "Supplement Analysis With Passive Microwave Eye Score"; 125 private static final String TOOLTIP_PENV = "Environmental Mean Sea Level Pressure"; 126 private static final String TOOLTIP_34KT = "34 Knot Wind/Gale Radius"; 127 private static final String TOOLTIP_MSLP_FROM_DVORAK = "Utilize Dvorak Technique to Derive MSLP"; 128 private static final String TOOLTIP_MSLP_FROM_CKZ = "Utilize Coutney/Knaff/Zehr Wind Speed/Presssure Technique"; 129 private static final String TOOLTIP_RMW = "Manually Input Radius of Maximum Wind"; 130 private static final String TOOLTIP_RAW_T = "Manually Define Initial Value for New Storm"; 131 private static final String TOOLTIP_STORM_ID = "Three Character WMO Storm Identfier"; 132 private static final String TOOLTIP_SITE_ID = "Four Character Site Analysis Identifier"; 133 134 public static final String[] SCENE_TYPES = { 135 "Eye", "Pinhole Eye", "Large Eye", "CDO", "Embedded Center", 136 "Irregular CDO", "Curved Band", "Shear" 137 }; 138 139 private static final String[] FORECAST_TYPES = { 140 "ATCF", "DISC", "PACWARN", "GENERIC", "RMSC ICAO", "RMSC WTIO", 141 "TCWC AXAU", "BEST", "HURDAT" 142 }; 143 144 /** _more_ */ 145 private LatLonWidget latLonWidget; 146 147 /** the probe */ 148 private PointProbe probe; 149 150 /** _more_ */ 151 private LatLonPoint probeLocation; 152 153 /** _more_ */ 154 private DataChoice choice; 155 156 /** _more_ */ 157 private static boolean running = false; 158 159 private static boolean runFullADTAnalysis = false; 160 private static boolean GUIFileOverrideTF = false; 161 162 private static boolean GUIOverrideSceneTF; 163 164 private static boolean GUIRunAutoTF; 165 private static boolean GUIOverrideTF; 166 private static boolean GUIATCFOutputTF; 167 private static boolean GUIInitStrengthTF; 168 private static boolean GUILandFlagTF; 169 170 // Default Java boolean value is false - need to initialize if we want true 171 private boolean GUIUseCKZTF = false; 172 private static boolean GUIVmax1or10TF = true; 173 174 private static boolean GUICommentAddTF; 175 private static boolean GUIDeleteTF; 176 private static boolean GUIATCFRecordOutputTF; 177 private static boolean GUIPMWActivateTF; 178 179 // need to determine or provide option 180 private static int GUIDomainID; 181 182 // need to initialize pulldown menu 183 private static int GUIForecastType = 0; 184 185 private static int GUIMWJulianDate; 186 private static int GUIMWHHMMSSTime; 187 private static int GUIStartDate; 188 private static int GUIStartTime; 189 private static int GUIEndDate; 190 private static int GUIEndTime; 191 private static int GUIHistoryListFormat; 192 193 private static double GUIRawTValue; 194 private static double GUIMWScore; 195 private static double GUICKZGaleRadius; 196 private static double GUICKZPenv; 197 private static double GUIRMWSize; 198 private static double GUIUserLatitude; 199 private static double GUIUserLongitude; 200 201 private static String GUIForecastFileName; 202 private String GUIATCFStormID = null; 203 private String GUIATCFSiteID = null; 204 private static String GUIHistoryFileName; 205 private static String GUIHistoryFileListingName; 206 private static String GUICommentString; 207 208 /** _more_ */ 209 private JButton adtBtn; 210 211 private JButton forecastBtn; 212 213 private JButton PMWFileBtn; 214 215 private JRadioButton manButton; 216 217 // Button to relocate probe 218 private JButton moveProbeButton; 219 220 /** _more_ */ 221 private JComboBox<String> forecastTypeBox; 222 223 private JFrame resultFrame; 224 private JTextArea resultArea; 225 private JFrame historyFrame; 226 private JTextArea historyArea; 227 228 private JLabel selectedHistoryFile; 229 230 private JFileChooser historyFileSaveChooser; 231 232 private JFrame overrideSceneFrame; 233 private JLabel overrideSceneCurrentValueLabel; 234 private JComboBox<String> overrideSceneTypeBox; 235 236 // CKZ params will need to be validated before running 237 JTextField ckzPenvTextField = null; 238 JTextField ckz34radiusTextField = null; 239 private static final String DEFAULT_PENV = "1012"; 240 private static final String DEFAULT_RADIUS = "300"; 241 242 private JLabel historyLabel; 243 244 private static String HistoryListOutput; 245 246 private static final String SCENE_TYPE_PREFIX = "Current Scene Type: "; 247 248 JTextField ATCFEntryStormTextField = null; 249 JTextField ATCFEntrySiteTextField = null; 250 251 /** 252 * 253 */ 254 public ADTControl() { 255 super(); 256 } 257 258 @Override public boolean init(DataChoice choice) throws VisADException, 259 RemoteException { 260 logger.info("ADTControl constructor begin..."); 261 262 if (!super.init(choice)) { 263 return false; 264 } 265 this.choice = choice; 266 267 probe = new PointProbe(new RealTuple(RealTupleType.SpatialEarth3DTuple, 268 new double[] { 0.0, 0.0, 0.0 })); 269 270 probe.setVisible(false); 271 probe.setAutoSize(true); 272 probe.addPropertyChangeListener(this); 273 274 probe.setPointSize(getDisplayScale()); 275 addDisplayable(probe, FLAG_COLOR); 276 277 // obtain initial ADT environmental parameters 278 getADTenvParameters(); 279 280 // setup window contents in Controls Window 281 setContents(setupMainWindow()); 282 283 // TJJ Jun 2017 284 // We want to initialize probe to display center if in Manual mode 285 NavigatedDisplay d = getNavigatedDisplay(); 286 if (manButton.isSelected()) { 287 if (d != null) { 288 EarthLocation el = d.getCenterPoint(); 289 logger.debug("Initializing probe location to: {}, {}", el.getLatitude(), el.getLongitude()); 290 probeLocation = el.getLatLonPoint(); 291 probe.setVisible(true); 292 } 293 } 294 updateProbeLocation(); 295 return true; 296 } 297 298 private Container setupMainWindow() { 299 300 /* add Lat/Lon position display text areas */ 301 latLonWidget = new LatLonWidget(GuiUtils.makeActionListener(this, 302 "latLonWidgetChanged", null)); 303 moveProbeButton = new JButton("Move Probe"); 304 // TJJ add a strut and Probe button to the Lat-Lon widget panel 305 latLonWidget.add(Box.createHorizontalStrut(6)); 306 latLonWidget.add(moveProbeButton); 307 moveProbeButton.addActionListener(ae -> { 308 // Validate the manual lat/lon text boxes 309 String validLL = latLonWidget.isValidValues(); 310 if (validLL == null) { 311 // User provided valid lat/lon data, see if it's within 312 // our display bounds. If so, move the probe 313 NavigatedDisplay d = getNavigatedDisplay(); 314 if (manButton.isSelected()) { 315 if (d != null) { 316 EarthLocationTuple elt = null; 317 try { 318 elt = new EarthLocationTuple(latLonWidget.getLat(), latLonWidget.getLon(), Double.NaN); 319 // Make sure the new Earth location is within the bounds of our satellite IR image 320 LatLonRect bounds = d.getLatLonRect(); 321 logger.debug("Bounds min, max Lat: " + bounds.getLatMin() + ", " + bounds.getLatMax()); 322 logger.debug("Bounds min, max Lon: " + bounds.getLonMin() + ", " + bounds.getLonMax()); 323 logger.debug("ELT LatVal, LonVal: " + elt.getLatitude().getValue() + ", " + elt.getLongitude().getValue()); 324 if (bounds.contains(elt.getLatitude().getValue(), elt.getLongitude().getValue())) { 325 probeLocation = elt.getLatLonPoint(); 326 updateProbeLocation(); 327 } else { 328 JOptionPane.showMessageDialog(null, "Location provided is outside image bounds"); 329 } 330 } catch (VisADException | RemoteException ve) { 331 logException(ve); 332 } 333 } 334 } 335 } else { 336 JOptionPane.showMessageDialog(null, validLL); 337 } 338 }); 339 340 /* add Manual or Automated storm centering buttons */ 341 342 manButton = new JRadioButton("Manual"); 343 manButton.setActionCommand("Manual"); 344 manButton.setSelected(true); 345 manButton.setToolTipText(TOOLTIP_MANUAL); 346 JRadioButton autoButton = new JRadioButton("Automated"); 347 autoButton.setActionCommand("Automated"); 348 autoButton.setSelected(false); 349 autoButton.setToolTipText(TOOLTIP_AUTOMATIC); 350 ButtonGroup automangroup = new ButtonGroup(); 351 automangroup.add(manButton); 352 automangroup.add(autoButton); 353 354 /* add forecast file file selector button and file type menu */ 355 JLabel autoStormSelectLabel = new JLabel("AUTOMATED STORM SELECTION"); 356 JLabel manualStormSelectLabel = new JLabel("MANUAL STORM SELECTION"); 357 JLabel forecastSelectLabel = new JLabel("Selected Forecast File: "); 358 359 JLabel forecastLabel = new JLabel("No forecast file selected yet"); 360 361 manButton.addActionListener(ae -> { 362 // enable the manual lat/lon text boxes 363 latLonWidget.getLonField().setEnabled(true); 364 latLonWidget.getLatField().setEnabled(true); 365 autoStormSelectLabel.setEnabled(false); 366 manualStormSelectLabel.setEnabled(true); 367 forecastSelectLabel.setEnabled(false); 368 moveProbeButton.setEnabled(true); 369 forecastBtn.setEnabled(false); 370 forecastTypeBox.setEnabled(false); 371 GUIRunAutoTF = false; 372 }); 373 374 autoButton.addActionListener(ae -> { 375 // disable the manual lat/lon text boxes when in auto mode 376 latLonWidget.getLonField().setEnabled(false); 377 latLonWidget.getLatField().setEnabled(false); 378 autoStormSelectLabel.setEnabled(true); 379 manualStormSelectLabel.setEnabled(false); 380 forecastSelectLabel.setEnabled(true); 381 moveProbeButton.setEnabled(false); 382 forecastBtn.setEnabled(true); 383 forecastTypeBox.setEnabled(true); 384 GUIRunAutoTF = true; 385 System.out.println("running automated ADT!!!\n"); 386 }); 387 388 forecastBtn = new JButton("Select Forecast File"); 389 forecastBtn.setPreferredSize(new Dimension(200,30)); 390 forecastBtn.addActionListener(fbtn -> { 391 GUIForecastFileName = selectForecastFile(); 392 logger.trace("forecast file name={}", GUIForecastFileName); 393 forecastLabel.setText( 394 GUIForecastFileName.substring(GUIForecastFileName.lastIndexOf(File.separatorChar) + 1) 395 ); 396 }); 397 398 forecastTypeBox = new JComboBox<>(FORECAST_TYPES); 399 forecastTypeBox.setSelectedIndex(GUIForecastType); 400 forecastTypeBox.setPreferredSize(new Dimension(150,20)); 401 forecastTypeBox.addActionListener(ame -> { 402 GUIForecastType = forecastTypeBox.getSelectedIndex(); 403 logger.trace("forecast file type={}", GUIForecastType); 404 }); 405 406 forecastTypeBox.setToolTipText("Select Forecast File Type."); 407 autoStormSelectLabel.setEnabled(false); 408 forecastSelectLabel.setEnabled(false); 409 forecastBtn.setEnabled(false); 410 forecastTypeBox.setEnabled(false); 411 412 /* define default history file text field message */ 413 selectedHistoryFile = new JLabel("No history file selected yet"); 414 415 /* add history file selection button */ 416 JButton historyBtn = new JButton("Select History File"); 417 historyBtn.setToolTipText(TOOLTIP_HISTORY); 418 historyBtn.setPreferredSize(new Dimension(200, 30)); 419 historyBtn.addActionListener(hbtn -> { 420 GUIHistoryFileName = selectHistoryFile(); 421 logger.debug("history file name={}", GUIHistoryFileName); 422 423 // TJJ Dec 2017 424 // Do some cursory validation on History file before plowing ahead 425 if (! validHistoryFile(GUIHistoryFileName)) { 426 JOptionPane.showMessageDialog(null, 427 "Your selection does not appear to be a valid ADT History File."); 428 } else { 429 runFullADTAnalysis = true; 430 selectedHistoryFile.setText( 431 GUIHistoryFileName.substring(GUIHistoryFileName.lastIndexOf(File.separatorChar) + 1) 432 ); 433 } 434 }); 435 436 /* add main ADT analysis start button */ 437 adtBtn = new JButton("Run ADT Analysis"); 438 adtBtn.setPreferredSize(new Dimension(250, 50)); 439 adtBtn.addActionListener(ae -> runADTmain()); 440 441 /* add history file list/write button */ 442 JButton listBtn = new JButton("List/Write History File"); 443 listBtn.setPreferredSize(new Dimension(250, 50)); 444 listBtn.addActionListener(ae -> { 445 logger.debug("listing history file name={}", GUIHistoryFileName); 446 try { 447 listHistoryFile(); 448 } catch (NumberFormatException nfe) { 449 JOptionPane.showMessageDialog(null, 450 "Your selection does not appear to be a valid ADT History File."); 451 } 452 }); 453 454 // TJJ Jan 2017 455 // We'll keep the Manual vs. Automated PMW radio button group around 456 // in case code to support automated is added later. For now, only 457 // manual works in this version, so we'll just set the state of the 458 // buttons but not show them. 459 460 JRadioButton PMWManButton = new JRadioButton("Manual"); 461 PMWManButton.setActionCommand("Man"); 462 PMWManButton.setSelected(true); 463 PMWManButton.setEnabled(true); 464 465 JRadioButton PMWAutoButton = new JRadioButton("Automated"); 466 PMWAutoButton.setActionCommand("Auto"); 467 PMWAutoButton.setSelected(false); 468 PMWAutoButton.setEnabled(false); 469 470 /* PMW Manual options */ 471 JLabel pmwManDateLabel = new JLabel("Date:"); 472 JLabel pmwManTimeLabel = new JLabel("Time:"); 473 JLabel pmwManScoreLabel = new JLabel("Score:"); 474 JTextField pmwManDateTextField = new JTextField("1900JAN01", 8); 475 pmwManDateTextField.setToolTipText("YYYYMMMDD"); 476 pmwManDateTextField.addActionListener(ae -> { 477 /* read PMW overpass date */ 478 JTextField src = (JTextField) ae.getSource(); 479 GUIMWJulianDate = 480 Functions.cmonth2julian(src.getText()); 481 GUIMWScore = -99.0; 482 }); 483 JTextField pmwManTimeTextField = new JTextField("000000", 6); 484 pmwManTimeTextField.setToolTipText("HHMMSS"); 485 pmwManTimeTextField.addActionListener(ae -> { 486 /* read PMW overpass time */ 487 JTextField src = (JTextField) ae.getSource(); 488 GUIMWHHMMSSTime = Integer.valueOf(src.getText()); 489 GUIMWScore = -99.0; 490 }); 491 JTextField pmwManScoreTextField = new JTextField("-99.0", 4); 492 pmwManScoreTextField.setToolTipText("Eye Score Value"); 493 pmwManScoreTextField.addActionListener(ae -> { 494 /* read PMW overpass score */ 495 JTextField src = (JTextField) ae.getSource(); 496 GUIMWScore = Double.valueOf(src.getText()); 497 }); 498 pmwManDateTextField.setEnabled(false); 499 pmwManTimeTextField.setEnabled(false); 500 pmwManScoreTextField.setEnabled(false); 501 pmwManDateLabel.setEnabled(false); 502 pmwManTimeLabel.setEnabled(false); 503 pmwManScoreLabel.setEnabled(false); 504 505 ButtonGroup pmwgroup = new ButtonGroup(); 506 pmwgroup.add(PMWAutoButton); 507 pmwgroup.add(PMWManButton); 508 PMWAutoButton.addActionListener(ae -> { 509 /* enter file name */ 510 // Automated - file entry 511 PMWFileBtn.setEnabled(true); 512 pmwManDateTextField.setEnabled(false); 513 pmwManTimeTextField.setEnabled(false); 514 pmwManScoreTextField.setEnabled(false); 515 pmwManDateLabel.setEnabled(false); 516 pmwManTimeLabel.setEnabled(false); 517 pmwManScoreLabel.setEnabled(false); 518 }); 519 PMWManButton.addActionListener(ae -> { 520 /* enter date/time and score manually */ 521 // Maunal entry 522 PMWFileBtn.setEnabled(false); 523 pmwManDateTextField.setEnabled(true); 524 pmwManTimeTextField.setEnabled(true); 525 pmwManScoreTextField.setEnabled(true); 526 pmwManDateLabel.setEnabled(true); 527 pmwManTimeLabel.setEnabled(true); 528 pmwManScoreLabel.setEnabled(true); 529 }); 530 531 /* Add PMW Analysis option buttons and entry fields */ 532 JCheckBox PMWActivateButton = new JCheckBox("Activate"); 533 PMWActivateButton.setActionCommand("PMW"); 534 PMWActivateButton.setSelected(false); 535 PMWActivateButton.setEnabled(true); 536 PMWActivateButton.setToolTipText(TOOLTIP_PMW); 537 PMWActivateButton.addActionListener(ae -> { 538 // if on, turn off and vice versa 539 GUIPMWActivateTF = !GUIPMWActivateTF; 540 PMWManButton.setEnabled(GUIPMWActivateTF); 541 PMWManButton.setSelected(GUIPMWActivateTF); 542 pmwManDateTextField.setEnabled(GUIPMWActivateTF); 543 pmwManTimeTextField.setEnabled(GUIPMWActivateTF); 544 pmwManScoreTextField.setEnabled(GUIPMWActivateTF); 545 pmwManDateLabel.setEnabled(GUIPMWActivateTF); 546 pmwManTimeLabel.setEnabled(GUIPMWActivateTF); 547 pmwManScoreLabel.setEnabled(GUIPMWActivateTF); 548 PMWActivateButton.setSelected(GUIPMWActivateTF); 549 }); 550 551 /* add CKZ option buttons and entry fields */ 552 JLabel ckzPenvLabel = new JLabel("Penv:"); 553 ckzPenvLabel.setEnabled(false); 554 555 JLabel ckz34radiusLabel = new JLabel("34kt Radius:"); 556 ckz34radiusLabel.setEnabled(false); 557 558 ckzPenvTextField = new JTextField(DEFAULT_PENV, 5); 559 ckzPenvTextField.setToolTipText(TOOLTIP_PENV); 560 ckzPenvTextField.addActionListener(ae -> { 561 JTextField src = (JTextField)ae.getSource(); 562 GUICKZPenv = Integer.valueOf(src.getText()); 563 }); 564 ckz34radiusTextField = new JTextField(DEFAULT_RADIUS, 5); 565 ckz34radiusTextField.setToolTipText(TOOLTIP_34KT); 566 ckz34radiusTextField.addActionListener(ae -> { 567 JTextField src = (JTextField)ae.getSource(); 568 GUICKZGaleRadius = Integer.valueOf(src.getText()); 569 }); 570 ckzPenvTextField.setEnabled(false); 571 ckz34radiusTextField.setEnabled(false); 572 573 JRadioButton mslpDvorakButton = new JRadioButton("Dvorak"); 574 mslpDvorakButton.setActionCommand("Dvorak"); 575 mslpDvorakButton.setSelected(true); 576 mslpDvorakButton.setToolTipText(TOOLTIP_MSLP_FROM_DVORAK); 577 JRadioButton mslpCKZButton = new JRadioButton("CKZ"); 578 mslpCKZButton.setActionCommand("CKZ"); 579 mslpCKZButton.setSelected(false); 580 mslpCKZButton.setToolTipText(TOOLTIP_MSLP_FROM_CKZ); 581 ButtonGroup mslpgroup = new ButtonGroup(); 582 mslpgroup.add(mslpDvorakButton); 583 mslpgroup.add(mslpCKZButton); 584 mslpDvorakButton.addActionListener(ae -> { 585 // Dvorak 586 ckzPenvTextField.setEnabled(false); 587 ckz34radiusTextField.setEnabled(false); 588 ckzPenvLabel.setEnabled(false); 589 ckz34radiusLabel.setEnabled(false); 590 mslpDvorakButton.setSelected(true); 591 mslpCKZButton.setSelected(false); 592 GUIUseCKZTF = false; 593 }); 594 mslpCKZButton.addActionListener(ae -> { 595 // CKZ 596 ckzPenvTextField.setEnabled(true); 597 ckz34radiusTextField.setEnabled(true); 598 ckzPenvLabel.setEnabled(true); 599 ckz34radiusLabel.setEnabled(true); 600 mslpDvorakButton.setSelected(false); 601 mslpCKZButton.setSelected(true); 602 GUIUseCKZTF = true; 603 }); 604 605 /* various other keyword options */ 606 /* Initial classification entry -- RAWT */ 607 JLabel RawTLabel = new JLabel("Raw T:"); 608 JTextField RawTTextField = new JTextField("1.0", 4); 609 RawTTextField.setToolTipText(TOOLTIP_RAW_T); 610 RawTTextField.addActionListener(ae -> { 611 JTextField src = (JTextField)ae.getSource(); 612 GUIRawTValue = Double.valueOf(src.getText()); 613 GUIInitStrengthTF = GUIRawTValue >= 1.0; 614 }); 615 616 /* Radius of Max Wind entry -- RMW */ 617 JLabel RMWLabel = new JLabel("RMW:"); 618 JTextField RMWTextField = new JTextField("-99", 4); 619 RMWTextField.setToolTipText(TOOLTIP_RMW); 620 RMWTextField.addActionListener(ae -> { 621 JTextField src = (JTextField)ae.getSource(); 622 GUIRMWSize = Double.valueOf(src.getText()); 623 }); 624 625 /* Override option */ 626 JButton sceneOverrideButton = new JButton("Override Scene Type"); 627 JLabel OverrideLabel = new JLabel(SCENE_TYPE_PREFIX + SCENE_TYPES[Env.OverrideSceneTypeIndex]); 628 sceneOverrideButton.addActionListener(ae -> { 629 overrideSceneFrame.setVisible(true); 630 }); 631 632 /* ATCF Analysis Output Checkbox */ 633 634 JLabel ATCFOutputLabel = new JLabel("ATCF Output:"); 635 JCheckBox ATCFOutputButton = new JCheckBox("Activate"); 636 ATCFOutputButton.setActionCommand("ATCF"); 637 ATCFOutputButton.setSelected(false); 638 ATCFOutputButton.setEnabled(true); 639 640 JLabel ATCFEntryStormLabel = new JLabel("Storm ID:"); 641 ATCFEntryStormTextField = new JTextField("XXX", 8); 642 ATCFEntryStormTextField.setToolTipText(TOOLTIP_STORM_ID); 643 JLabel ATCFEntrySiteLabel = new JLabel("Site ID:"); 644 ATCFEntrySiteTextField = new JTextField("XXXX", 8); 645 ATCFEntrySiteTextField.setToolTipText(TOOLTIP_SITE_ID); 646 ATCFEntryStormLabel.setEnabled(false); 647 ATCFEntryStormTextField.setEnabled(false); 648 ATCFEntrySiteLabel.setEnabled(false); 649 ATCFEntrySiteTextField.setEnabled(false); 650 ATCFEntryStormTextField.addActionListener(ae -> { 651 JTextField src = (JTextField)ae.getSource(); 652 GUIATCFStormID = src.getText(); 653 }); 654 ATCFEntrySiteTextField.addActionListener(ae -> { 655 JTextField src = (JTextField)ae.getSource(); 656 GUIATCFSiteID = src.getText(); 657 }); 658 659 ATCFOutputButton.addActionListener(ae -> { 660 // if on, turn off and vice versa 661 GUIATCFRecordOutputTF = !GUIATCFRecordOutputTF; 662 ATCFEntryStormLabel.setEnabled(GUIATCFRecordOutputTF); 663 ATCFEntryStormTextField.setEnabled(GUIATCFRecordOutputTF); 664 ATCFEntrySiteLabel.setEnabled(GUIATCFRecordOutputTF); 665 ATCFEntrySiteTextField.setEnabled(GUIATCFRecordOutputTF); 666 ATCFOutputButton.setSelected(GUIATCFRecordOutputTF); 667 }); 668 669 /* Land Flag button -- LAND */ 670 JLabel LandFlagLabel = new JLabel("Land Flag:"); 671 JRadioButton LandONButton = new JRadioButton("ON"); 672 LandONButton.setActionCommand("On"); 673 LandONButton.setSelected(true); 674 LandONButton.setToolTipText(TOOLTIP_LAND_FLAG_ON); 675 JRadioButton LandOFFButton = new JRadioButton("OFF"); 676 LandOFFButton.setActionCommand("Off"); 677 LandOFFButton.setSelected(false); 678 LandOFFButton.setToolTipText(TOOLTIP_LAND_FLAG_OFF); 679 ButtonGroup landgroup = new ButtonGroup(); 680 landgroup.add(LandONButton); 681 landgroup.add(LandOFFButton); 682 LandONButton.addActionListener(ae -> { 683 // LAND=YES 684 LandONButton.setSelected(true); 685 LandOFFButton.setSelected(false); 686 GUILandFlagTF = true; 687 }); 688 LandOFFButton.addActionListener(ae -> { 689 // LAND=NO 690 LandONButton.setSelected(false); 691 LandOFFButton.setSelected(true); 692 GUILandFlagTF = false; 693 }); 694 695 /* Wind Speed Vmax output button -- VOUT */ 696 JLabel VOutLabel = new JLabel("VMax:"); 697 JRadioButton V1MinButton = new JRadioButton("One-minute"); 698 V1MinButton.setActionCommand("One"); 699 V1MinButton.setSelected(true); 700 V1MinButton.setToolTipText("Maximum Wind Speed Averaged Over"); 701 JRadioButton V10MinButton = new JRadioButton("Ten-minute"); 702 V10MinButton.setActionCommand("Ten"); 703 V10MinButton.setSelected(false); 704 V10MinButton.setToolTipText("Maximum Wind Speed Averaged Over"); 705 ButtonGroup voutgroup = new ButtonGroup(); 706 voutgroup.add(V1MinButton); 707 voutgroup.add(V10MinButton); 708 V1MinButton.addActionListener(ae -> { 709 // 1-minute winds 710 V1MinButton.setSelected(true); 711 V10MinButton.setSelected(false); 712 GUIVmax1or10TF = true; 713 }); 714 V10MinButton.addActionListener(ae -> { 715 // 10-minute winds 716 V1MinButton.setSelected(false); 717 V10MinButton.setSelected(true); 718 GUIVmax1or10TF = false; 719 }); 720 721 JLabel blankfield = new JLabel(""); 722 723 // TJJ Jan 2018 - interim link to Help for McV 1.7 release 724 JButton helpLinkLabel = new JButton("<html><a href=\"https://www.ssec.wisc.edu\">Help</a></html>"); 725 helpLinkLabel.setToolTipText("Opens ADT Help PDF in your system web browser"); 726 helpLinkLabel.addActionListener(e -> WebBrowser.browse("https://www.ssec.wisc.edu/mcidas/software/v/resources/adt/McV_ADT_1p7.pdf")); 727 728 GuiUtils.tmpInsets = GuiUtils.INSETS_5; 729 JComponent widgets = 730 GuiUtils.formLayout( 731 arr(left(hbox(arr(new JLabel("Storm Center Selection:"), manButton, autoButton), 5)), 732 filler(), 733 left(hbox(arr(manualStormSelectLabel), 10)), 734 filler(), 735 left(hbox(arr(filler(30, 1), latLonWidget))), filler(), 736 left(hbox(arr(autoStormSelectLabel), 10)), filler(), 737 left(hbox(arr(filler(30, 1), forecastBtn, forecastTypeBox, 738 forecastSelectLabel, forecastLabel), 5)), filler(), 739 left(hbox(arr(blankfield))), 740 filler(1, 5), 741 left(hbox(arr(new JLabel("HISTORY FILE INFORMATION")), 10)), filler(), 742 left(hbox(arr(filler(30, 1), historyBtn, new JLabel 743 ("Selected History File: "), selectedHistoryFile), 5)), 744 filler(), 745 left(hbox(arr(blankfield))), 746 filler(1, 5), 747 left(hbox(arr(new JLabel("PMW ANALYSIS")), 10)), filler(), 748 left(hbox(arr(filler(30, 1), PMWActivateButton, 749 pmwManDateLabel, pmwManDateTextField, pmwManTimeLabel, 750 pmwManTimeTextField, pmwManScoreLabel, pmwManScoreTextField), 5)), filler(), 751 left(hbox(arr(blankfield))), 752 filler(1, 5), 753 left(hbox(arr(new JLabel("MISCELLANEOUS OPTIONS")), 10)), filler(), 754 left(hbox(arr(filler(30, 1), new JLabel("MSLP Conversion Method:"), mslpDvorakButton, mslpCKZButton, ckzPenvLabel, ckzPenvTextField, ckz34radiusLabel, ckz34radiusTextField), 5)), filler(), 755 left(hbox(arr(filler(30, 1), sceneOverrideButton, OverrideLabel), 5)), filler(), 756 left(hbox(arr(filler(30, 1), LandFlagLabel, LandONButton, LandOFFButton, filler(20, 1), VOutLabel, V1MinButton, V10MinButton, filler(20, 1), RawTLabel, RawTTextField, RMWLabel, RMWTextField), 5)), filler(), 757 left(hbox(arr(filler(30, 1), ATCFOutputLabel, ATCFOutputButton, ATCFEntryStormLabel, ATCFEntryStormTextField, ATCFEntrySiteLabel, ATCFEntrySiteTextField), 5)), filler(), 758 left(hbox(arr(filler(80, 1), adtBtn, listBtn, helpLinkLabel), 20)), filler())); 759 760 JPanel controls = topLeft(widgets); 761 762 /* set up ADT Bulletin display area */ 763 resultArea = new JTextArea(); 764 resultArea.setEditable(false); 765 766 Font c = new Font("Courier", Font.BOLD, 12); 767 768 resultFrame = new JFrame("ADT Results"); 769 resultFrame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); 770 JScrollPane resultScroller = new JScrollPane(resultArea); 771 resultScroller.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS); 772 resultFrame.add(resultScroller, BorderLayout.CENTER); 773 resultFrame.setPreferredSize(new Dimension(400, 600)); 774 resultFrame.setFont(c); 775 776 /* set up ADT History File display area */ 777 historyFrame = new JFrame("ADT History File Listing"); 778 Container historyContainer = historyFrame.getContentPane(); 779 historyFrame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); 780 JPanel historyTextPanel = new JPanel(); 781 FlowLayout historyListLayout = new FlowLayout(); 782 historyTextPanel.setLayout(historyListLayout); 783 historyListLayout.setAlignment(FlowLayout.CENTER); 784 785 historyArea = new JTextArea(50,150); 786 historyArea.setEditable(false); 787 JScrollPane historyScroller = new JScrollPane(historyArea); 788 historyScroller.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS); 789 historyScroller.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED); 790 historyScroller.setPreferredSize(new Dimension(1200, 400)); 791 historyArea.setFont(c); 792 793 JPanel historyLabelPanel = new JPanel(); 794 FlowLayout HistoryLabelLayout = new FlowLayout(); 795 historyLabelPanel.setLayout(HistoryLabelLayout); 796 HistoryLabelLayout.setAlignment(FlowLayout.CENTER); 797 historyLabel = new JLabel("No History File Selected"); 798 historyLabel.setPreferredSize(new Dimension(800, 20)); 799 historyLabel.setFont(c); 800 801 /* history file Editing Date Selection window */ 802 JFrame historyDateFrame = new JFrame("History File Editor"); 803 Container historyDateContainer = historyDateFrame.getContentPane(); 804 historyDateFrame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); 805 JPanel historyDatePanel = new JPanel(); 806 FlowLayout DateStartEndLayout = new FlowLayout(); 807 historyDatePanel.setLayout(DateStartEndLayout); 808 DateStartEndLayout.setAlignment(FlowLayout.CENTER); 809 JLabel historyDateStartLabel = new JLabel("Start:"); 810 JLabel historyDateStartDateLabel = new JLabel("Date"); 811 JTextField historyDateStartDateTextField = new JTextField("0000XXX00", 10); 812 JLabel historyDateStartTimeLabel = new JLabel("Time"); 813 JTextField historyDateStartTimeTextField = new JTextField("-1", 8); 814 JLabel historyDateEndLabel = new JLabel("End"); 815 JLabel historyDateEndDateLabel = new JLabel("Date"); 816 JTextField historyDateEndDateTextField = new JTextField("0000XXX00", 10); 817 JLabel historyDateEndTimeLabel = new JLabel("Time"); 818 JTextField historyDateEndTimeTextField = new JTextField("-1", 8); 819 820 JPanel historyButtonPanel = new JPanel(); 821 FlowLayout HistoryButtonLayout = new FlowLayout(); 822 historyButtonPanel.setLayout(HistoryButtonLayout); 823 HistoryButtonLayout.setAlignment(FlowLayout.CENTER); 824 825 JButton historySaveListingBtn = new JButton("Write History"); 826 historySaveListingBtn.setPreferredSize(new Dimension(200, 20)); 827 historySaveListingBtn.addActionListener(ae -> { 828 GUIHistoryFileListingName = selectHistoryFileOutput(); 829 logger.debug("saving history listing file name={}", GUIHistoryFileListingName); 830 GUIHistoryListFormat = -1; 831 }); 832 JButton historyWriteATCFBtn = new JButton("Write ATCF"); 833 historyWriteATCFBtn.setPreferredSize(new Dimension(200, 20)); 834 historyWriteATCFBtn.addActionListener(ae -> { 835 GUIATCFOutputTF = true; 836 GUIHistoryListFormat = 0; 837 logger.debug("calling ATCFFileOutput"); 838 ATCFFileOutput(0); 839 }); 840 historyLabelPanel.add(historyLabel); 841 historyLabelPanel.setComponentOrientation(ComponentOrientation.LEFT_TO_RIGHT); 842 historyTextPanel.add(historyScroller); 843 historyTextPanel.setComponentOrientation(ComponentOrientation.LEFT_TO_RIGHT); 844 845 historyButtonPanel.add(historySaveListingBtn); 846 historyButtonPanel.add(historyWriteATCFBtn); 847 historyButtonPanel.setComponentOrientation(ComponentOrientation.LEFT_TO_RIGHT); 848 historyContainer.add(historyLabelPanel,BorderLayout.NORTH); 849 historyContainer.add(historyTextPanel,BorderLayout.CENTER); 850 historyContainer.add(historyButtonPanel,BorderLayout.SOUTH); 851 852 historyDateStartDateTextField.addActionListener(ae -> { 853 JTextField textField = (JTextField)ae.getSource(); 854 GUIStartDate = Functions.cmonth2julian(textField.getText()); 855 }); 856 historyDateStartTimeTextField.addActionListener(ae -> { 857 JTextField textField = (JTextField)ae.getSource(); 858 GUIStartTime = Integer.valueOf(textField.getText()); 859 }); 860 historyDateEndDateTextField.addActionListener(ae -> { 861 JTextField textField = (JTextField)ae.getSource(); 862 GUIEndDate = Functions.cmonth2julian(textField.getText()); 863 }); 864 historyDateEndTimeTextField.addActionListener(ae -> { 865 JTextField textField = (JTextField)ae.getSource(); 866 GUIEndTime = Integer.valueOf(textField.getText()); 867 }); 868 869 JPanel historyDateButtonPanel = new JPanel(); 870 FlowLayout DateButtonLayout = new FlowLayout(); 871 historyDateButtonPanel.setLayout(DateButtonLayout); 872 DateButtonLayout.setAlignment(FlowLayout.CENTER); 873 JRadioButton historyEditDeleteButton = new JRadioButton("Delete Records"); 874 historyEditDeleteButton.setActionCommand("Delete"); 875 historyEditDeleteButton.setSelected(false); 876 JRadioButton historyEditAddCommentButton = new JRadioButton("Add Comment"); 877 historyEditAddCommentButton.setActionCommand("Comment"); 878 historyEditAddCommentButton.setSelected(false); 879 ButtonGroup editgroup = new ButtonGroup(); 880 editgroup.add(historyEditDeleteButton); 881 editgroup.add(historyEditAddCommentButton); 882 JLabel historyEditAddCommentLabel = new JLabel("Comment:"); 883 JTextField historyEditAddCommentTextField = new JTextField("no comment entered", 25); 884 historyEditAddCommentTextField.setEnabled(false); 885 886 historyEditDeleteButton.addActionListener(ae -> { 887 // history Edit - Delete 888 historyEditDeleteButton.setSelected(true); 889 historyEditAddCommentButton.setSelected(false); 890 historyEditAddCommentLabel.setEnabled(false); 891 historyEditAddCommentTextField.setEnabled(false); 892 GUICommentAddTF = false; 893 GUIDeleteTF = true; 894 }); 895 896 historyEditAddCommentButton.addActionListener(ae -> { 897 // history Edit - Add Comment 898 historyEditDeleteButton.setSelected(false); 899 historyEditAddCommentButton.setSelected(true); 900 historyEditAddCommentLabel.setEnabled(true); 901 historyEditAddCommentTextField.setEnabled(true); 902 GUICommentAddTF = true; 903 GUIDeleteTF = false; 904 }); 905 historyEditAddCommentTextField.addActionListener(ae -> { 906 JTextField src = (JTextField)ae.getSource(); 907 GUICommentString = src.getText(); 908 }); 909 JPanel historyEditInputPanel = new JPanel(); 910 FlowLayout EditInputButtonLayout = new FlowLayout(); 911 historyEditInputPanel.setLayout(EditInputButtonLayout); 912 EditInputButtonLayout.setAlignment(FlowLayout.CENTER); 913 JButton historyEditApplyButton = new JButton("Apply Edits"); 914 historyEditApplyButton.setPreferredSize(new Dimension(150, 20)); 915 historyEditApplyButton.addActionListener(ae -> modifyHistoryFile()); 916 JButton historyEditCancelButton = new JButton("Cancel"); 917 historyEditCancelButton.setPreferredSize(new Dimension(150, 20)); 918 historyEditCancelButton.addActionListener(ae -> historyDateFrame.dispose()); 919 historyDatePanel.add(historyDateStartLabel); 920 historyDatePanel.add(historyDateStartDateLabel); 921 historyDatePanel.add(historyDateStartDateTextField); 922 historyDatePanel.add(historyDateStartTimeLabel); 923 historyDatePanel.add(historyDateStartTimeTextField); 924 historyDatePanel.add(historyDateEndLabel); 925 historyDatePanel.add(historyDateEndDateLabel); 926 historyDatePanel.add(historyDateEndDateTextField); 927 historyDatePanel.add(historyDateEndTimeLabel); 928 historyDatePanel.add(historyDateEndTimeTextField); 929 historyDatePanel.setComponentOrientation(ComponentOrientation.LEFT_TO_RIGHT); 930 historyDateButtonPanel.add(historyEditDeleteButton); 931 historyDateButtonPanel.add(historyEditAddCommentButton); 932 historyDateButtonPanel.add(historyEditAddCommentLabel); 933 historyDateButtonPanel.add(historyEditAddCommentTextField); 934 historyEditInputPanel.add(historyEditApplyButton); 935 historyEditInputPanel.add(historyEditCancelButton); 936 historyDateContainer.add(historyDatePanel, BorderLayout.NORTH); 937 historyDateContainer.add(historyDateButtonPanel, BorderLayout.CENTER); 938 historyDateContainer.add(historyEditInputPanel, BorderLayout.SOUTH); 939 940 /* set up Scene Type Override Window display window */ 941 overrideSceneFrame = new JFrame("Override Scene Type"); 942 overrideSceneFrame.setSize(new Dimension(400, 300)); 943 Container overrideSceneContainer = overrideSceneFrame.getContentPane(); 944 overrideSceneFrame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); 945 JPanel overrideSceneCurrentPanel = new JPanel(); 946 FlowLayout OverrideSceneCurrentLayout = new FlowLayout(); 947 overrideSceneCurrentPanel.setLayout(OverrideSceneCurrentLayout); 948 OverrideSceneCurrentLayout.setAlignment(FlowLayout.CENTER); 949 JLabel overrideSceneCurrentLabel = new JLabel("Current Scene Type:"); 950 overrideSceneCurrentValueLabel = new JLabel(SCENE_TYPES[Env.OverrideSceneTypeIndex]); 951 JPanel overrideSceneSelectPanel = new JPanel(); 952 FlowLayout OverrideSceneSelectLayout = new FlowLayout(); 953 overrideSceneCurrentPanel.setLayout(OverrideSceneSelectLayout); 954 OverrideSceneSelectLayout.setAlignment(FlowLayout.CENTER); 955 JLabel overrideSceneSelectLabel = new JLabel("Select New Scene Type:"); 956 overrideSceneTypeBox = new JComboBox<>(SCENE_TYPES); 957 overrideSceneTypeBox.setSelectedIndex(Env.OverrideSceneTypeIndex); 958 overrideSceneTypeBox.setPreferredSize(new Dimension(150, 20)); 959 // overrideSceneTypeBox.addActionListener(ame -> Env.OverrideSceneTypeIndex = overrideSceneTypeBox.getSelectedIndex()); 960 JPanel overrideSceneButtonPanel = new JPanel(); 961 FlowLayout OverrideSceneButtonLayout = new FlowLayout(); 962 overrideSceneButtonPanel.setLayout(OverrideSceneButtonLayout); 963 OverrideSceneButtonLayout.setAlignment(FlowLayout.CENTER); 964 JButton overrideSceneAcceptButton = new JButton("Accept New Scene"); 965 overrideSceneAcceptButton.setPreferredSize(new Dimension(190, 20)); 966 overrideSceneAcceptButton.addActionListener(ae -> { 967 // accept new scene selection 968 overrideSceneFrame.setVisible(false); 969 Env.OverrideSceneTypeIndex = overrideSceneTypeBox.getSelectedIndex(); 970 OverrideLabel.setText(SCENE_TYPE_PREFIX + SCENE_TYPES[Env.OverrideSceneTypeIndex]); 971 overrideSceneCurrentValueLabel.setText(SCENE_TYPES[Env.OverrideSceneTypeIndex]); 972 // runADTmain(); 973 }); 974 JButton overrideSceneCancelButton = new JButton("Keep Current Scene"); 975 overrideSceneCancelButton.setPreferredSize(new Dimension(190, 20)); 976 overrideSceneCancelButton.addActionListener(ae -> { 977 overrideSceneFrame.setVisible(false); 978 // runADTmain(); 979 }); 980 overrideSceneCurrentPanel.add(overrideSceneCurrentLabel); 981 overrideSceneCurrentPanel.add(overrideSceneCurrentValueLabel); 982 overrideSceneSelectPanel.add(overrideSceneSelectLabel); 983 overrideSceneSelectPanel.add(overrideSceneTypeBox); 984 overrideSceneButtonPanel.add(overrideSceneAcceptButton); 985 overrideSceneButtonPanel.add(overrideSceneCancelButton); 986 overrideSceneContainer.add(overrideSceneCurrentPanel, BorderLayout.NORTH); 987 overrideSceneContainer.add(overrideSceneSelectPanel, BorderLayout.CENTER); 988 overrideSceneContainer.add(overrideSceneButtonPanel, BorderLayout.SOUTH); 989 990 JScrollPane scrollPane = new JScrollPane(controls); 991 scrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS); 992 return scrollPane; 993 } 994 995 /** 996 * Do some cursory checking on validity of selected History file 997 * @param historyFileName 998 * @return true is seems ok 999 */ 1000 1001 private boolean validHistoryFile(String historyFileName) { 1002 boolean seemsOk = true; 1003 1004 History CurrentHistory = new History(); 1005 1006 try { 1007 logger.debug("trying to read history file {}", historyFileName); 1008 CurrentHistory.ReadHistoryFile(historyFileName); 1009 } catch (IOException exception) { 1010 logger.warn("History file %s is not valid", historyFileName); 1011 seemsOk = false; 1012 } 1013 1014 logger.debug("Number of history records: {}", History.HistoryNumberOfRecords()); 1015 if (History.HistoryNumberOfRecords() == 0) seemsOk = false; 1016 return seemsOk; 1017 } 1018 1019 @Override 1020 protected void getHelpMenuItems(List items, boolean forMenuBar) { 1021 items.add(GuiUtils.makeMenuItem("Details", this, "showDetails")); 1022 JMenuItem jmi = new JMenuItem("User's Guide"); 1023 GuiUtils.setIcon(jmi, "/auxdata/ui/icons/help.png"); 1024 jmi.addActionListener(e -> { 1025 WebBrowser.browse("https://www.ssec.wisc.edu/mcidas/software/v/resources/adt/McV_ADT_1p7.pdf"); 1026 }); 1027 items.add(jmi); 1028 } 1029 1030 private void runADTmain() { 1031 if (!running) { 1032 running = true; 1033 adtBtn.setEnabled(false); 1034 adtBtn.setText("Running"); 1035 Misc.run(() -> { 1036 runADT(); 1037 ExitADT(); 1038 }); 1039 } 1040 } 1041 1042 private void runADT() { 1043 Main StormADT = new Main(); 1044 String ADTRunOutput; 1045 String ErrorMessage; 1046 1047 if (GUIFileOverrideTF) { 1048 String GUIOverrideFilePath = System.getenv("ODTHOME"); 1049 if (GUIOverrideFilePath == null) { 1050 GUIOverrideFilePath = System.getenv("HOME"); 1051 } 1052 String GUIOverrideFile = GUIOverrideFilePath + "/runadt.nogui.inputs.txt"; 1053 /* GUIFileOverrideCheckBoxToggle(); change toggle back to OFF */ 1054 int RetVal = ReadGUIOverrideInputFile(GUIOverrideFile); 1055 if (RetVal == -1) { 1056 ErrorMessage = String.format("Error reading GUI override file %s\n",GUIOverrideFile); 1057 System.out.println(ErrorMessage); 1058 userMessage(ErrorMessage); 1059 ExitADT(); 1060 return; 1061 } 1062 } 1063 1064 loadADTenvParameters(); 1065 1066 boolean RunAuto = Env.AutoTF; 1067 1068 // In auto mode, make sure a valid forecast file was selected 1069 if (RunAuto) { 1070 if (GUIForecastFileName == null) { 1071 userMessage("A valid forecast file must be selected to use Automated mode."); 1072 ExitADT(); 1073 return; 1074 } 1075 } 1076 1077 /* set storm position either through automated storm selection or by manual choice */ 1078 GetImageDateTime(); 1079 int ReturnVal = StormADT.GetInitialPosition(); // should set up to throw exception instead of return value 1080 if (ReturnVal < 0) { 1081 ErrorMessage = "Error obtaining initial position... exiting ADT\n"; 1082 System.out.println(ErrorMessage); 1083 userMessage(ErrorMessage); 1084 ExitADT(); 1085 } else { 1086 if (RunAuto) { 1087 try { 1088 float CenterLatitude = (float)Env.SelectedLatitude; 1089 float CenterLongitude = (float)Env.SelectedLongitude; 1090 /* System.out.println("pre-ARCHER latitude=%f longitude=%f\n",CenterLatitude,CenterLongitude); */ 1091 GetImageData(CenterLatitude, CenterLongitude); 1092 } catch (Exception exception) { 1093 ErrorMessage = "Error reading IR data pre-ARCHER\n"; 1094 System.out.println(ErrorMessage); 1095 userMessage(ErrorMessage); 1096 ExitADT(); 1097 return; 1098 } 1099 StormADT.GetARCHERPosition(); 1100 } else { 1101 if (probeLocation == null) { 1102 ErrorMessage = "Please select storm center location manually and try again"; 1103 System.out.println(ErrorMessage); 1104 userMessage(ErrorMessage); 1105 ExitADT(); 1106 return; 1107 } else { 1108 Env.SelectedLatitude = probeLocation.getLatitude().getValue(); 1109 Env.SelectedLongitude = probeLocation.getLongitude().getValue(); 1110 } 1111 } 1112 1113 try { 1114 float CenterLatitude = (float) Env.SelectedLatitude; 1115 float CenterLongitude = (float) Env.SelectedLongitude; 1116 /* System.out.println("latitude=%f longitude=%f domain=%d\n",CenterLatitude,CenterLongitude,DomainID); */ 1117 GetImageData(CenterLatitude, CenterLongitude); 1118 } catch (Exception e) { 1119 ErrorMessage = "Error reading IR data in getimagedata()\n"; 1120 logger.error(ErrorMessage.trim(), e); 1121 userMessage(ErrorMessage); 1122 ExitADT(); 1123 return; 1124 } 1125 1126 // TJJ Jun 2017 Just about ready, a few more validation checks and we can run 1127 // If CKZ chosen as MSLP Conversion Method, need to validate Penv and 34kt Radius fields 1128 // This may not be the best place to do this, but it's better than not doing it ;-) 1129 1130 if (GUIUseCKZTF) { 1131 1132 String newPenvStr = ckzPenvTextField.getText(); 1133 boolean badPenv = false; 1134 try { 1135 int newPenv = Integer.valueOf(newPenvStr); 1136 if (newPenv > 0) { 1137 GUICKZPenv = newPenv; 1138 Env.CKZPenv = GUICKZPenv; 1139 } else { 1140 badPenv = true; 1141 } 1142 } catch (NumberFormatException nfe) { 1143 badPenv = true; 1144 } 1145 1146 if (badPenv) { 1147 // Throw up a warning and bail out 1148 showBadIntWarning("Penv", newPenvStr); 1149 return; 1150 } 1151 1152 String newRadiusStr = ckz34radiusTextField.getText(); 1153 boolean badNewRadius = false; 1154 try { 1155 int newRadius = Integer.valueOf(newRadiusStr); 1156 if (newRadius > 0) { 1157 GUICKZGaleRadius = newRadius; 1158 Env.CKZGaleRadius = GUICKZGaleRadius; 1159 } else { 1160 badNewRadius = true; 1161 } 1162 } catch (NumberFormatException nfe) { 1163 badNewRadius = true; 1164 } 1165 1166 if (badNewRadius) { 1167 // Throw up a warning and bail out 1168 showBadIntWarning("Radius", newRadiusStr); 1169 return; 1170 } 1171 1172 } 1173 1174 try { 1175 logger.debug("RUNNING ADT ANALYSIS"); 1176 ADTRunOutput = StormADT.RunADTAnalysis(runFullADTAnalysis,GUIHistoryFileName); 1177 } catch (IOException exception) { 1178 ErrorMessage = "Error with call to StormADT.RunADT()\n"; 1179 logger.error(ErrorMessage.trim(), exception); 1180 userMessage(ErrorMessage); 1181 ExitADT(); 1182 return; 1183 } 1184 if (GUIOverrideSceneTF) { 1185 /* System.out.println("Overriding scene type!!! Scene value=%d\n",InitialSceneTypeValue); */ 1186 overrideSceneCurrentValueLabel.setText(SCENE_TYPES[Env.OverrideSceneTypeIndex]); 1187 overrideSceneFrame.pack(); 1188 overrideSceneFrame.setVisible(true); 1189 ExitADT(); 1190 } else { 1191 logger.debug("done running ADT"); 1192 1193 resultArea.setText(ADTRunOutput); 1194 resultFrame.pack(); 1195 resultFrame.setVisible(true); 1196 1197 // TJJ Dec 2017 1198 // This is in reference to Request #11, Bug #17 from 1199 // http://mcidas.ssec.wisc.edu/inquiry-v/?inquiry=1187 1200 // Since the intent here is to modify the currently active history file by appending 1201 // one record, and since that record insert had been previously commented out below, 1202 // we'll assume this was never working properly in the first place. To prevent the 1203 // current History File from being clobbered, we just won't do the re-write for now, 1204 // since as is, a deep Exception zeros out the file, and the original file should 1205 // at the very least remain unmodified. 1206 1207// if (GUIHistoryFileName != null) { 1208// try { 1209// // int[] InsertRecs = History.InsertHistoryRecord(runFullADTAnalysis,GUIHistoryFileName); 1210// /* System.out.println("*** Modified=%d InsertOverwriteFlag=%d***\n",InsertRecs[0],InsertRecs[1]); */ 1211// int NumRecs = History.WriteHistoryFile(GUIHistoryFileName); 1212// ErrorMessage = String.format("Number of records written to history file: %d\n", NumRecs); 1213// } catch (IOException exception) { 1214// ErrorMessage = String.format("Error writing history file %s\n", GUIHistoryFileName); 1215// } catch (Exception e) { 1216// logger.error("Exception: ", e); 1217// ErrorMessage = String.format("Error writing history file %s\n", GUIHistoryFileName); 1218// } 1219// logger.warn(ErrorMessage.trim()); 1220// userMessage(ErrorMessage); 1221// } 1222 1223 if (GUIATCFRecordOutputTF) { 1224 ATCFFileOutput(-1); 1225 } 1226 1227 ExitADT(); 1228 } 1229 } 1230 } 1231 1232 /** 1233 * Show a warning about a certain parameter needing to be greater than zero. 1234 * 1235 * @param type Parameter name. Cannot be {@code null}. 1236 * @param badValue Erroneous value. Cannot be {@code null}. 1237 */ 1238 private void showBadIntWarning(String type, String badValue) { 1239 String msg = "Invalid %s value: %s\nPlease provide a positive integer."; 1240 JOptionPane.showMessageDialog(null, 1241 String.format(msg, type, badValue)); 1242 ExitADT(); 1243 } 1244 1245 private void ExitADT() { 1246 running = false; 1247 adtBtn.setEnabled(true); 1248 adtBtn.setText("Run Analysis"); 1249 } 1250 1251 /* 1252 * Override for additional local cleanup 1253 * (non-Javadoc) 1254 * @see ucar.unidata.idv.control.DisplayControlImpl#doRemove() 1255 */ 1256 1257 @Override public void doRemove() throws RemoteException, VisADException { 1258 super.doRemove(); 1259 if (resultFrame != null) { 1260 resultFrame.dispose(); 1261 } 1262 if (historyFrame != null) { 1263 historyFrame.dispose(); 1264 } 1265 } 1266 1267 private void listHistoryFile() { 1268 HistoryListOutput = null; 1269 1270 History CurrentHistory = new History(); 1271 1272 // Make sure a valid History File has been selected. At startup, value will be null 1273 if (GUIHistoryFileName == null) { 1274 JOptionPane.showMessageDialog(null, 1275 "Please first select a valid ADT History File."); 1276 return; 1277 } 1278 1279 try { 1280 logger.debug("trying to read history file {}", GUIHistoryFileName); 1281 CurrentHistory.ReadHistoryFile(GUIHistoryFileName); 1282 } catch (IOException exception) { 1283 String ErrorMessage = String.format("History file %s is not found",GUIHistoryFileName); 1284 logger.warn(ErrorMessage); 1285 userMessage(ErrorMessage); 1286 return; 1287 } 1288 1289 logger.debug("Number of history records: {}", History.HistoryNumberOfRecords()); 1290 1291 HistoryListOutput = History.ListHistory(0, -1, "CIMS", "99X"); 1292 historyLabel.setText(GUIHistoryFileName); 1293 historyArea.setText(HistoryListOutput); 1294 historyFrame.pack(); 1295 historyFrame.setVisible(true); 1296 1297 } 1298 1299 private void modifyHistoryFile() { 1300 1301 if (GUIDeleteTF) { 1302 // delete records 1303 int DeleteReturn[] = History.DeleteHistoryRecords(runFullADTAnalysis,GUIHistoryFileName); 1304 logger.debug("deleted {} records... modified {} records", DeleteReturn[1],DeleteReturn[0]); 1305 } else if( GUICommentAddTF) { 1306 // 1307 int CommentAddReturn = History.CommentHistoryRecords(GUICommentString); 1308 logger.debug("added comment to {} records",CommentAddReturn); 1309 } else { 1310 // invalid selection 1311 logger.warn("entered invalid selection!"); 1312 } 1313 1314 try { 1315 int HistoryFileRecords = History.WriteHistoryFile(GUIHistoryFileName); 1316 if (HistoryFileRecords >= 0) { 1317 logger.debug("wrote {} records to '{}'", HistoryFileRecords, GUIHistoryFileName); 1318 } 1319 } catch (IOException exception) { 1320 String ErrorMessage = String.format("error updating history file %s",GUIHistoryFileName); 1321 System.out.println(ErrorMessage); 1322 userMessage(ErrorMessage); 1323 } 1324 } 1325 1326 private String selectHistoryFile() { 1327 1328 String fileNameReturn = null; 1329 1330 JFrame historyFileFrame = new JFrame(); 1331 JFileChooser historyFileChooser = new JFileChooser(); 1332 String historyPath = System.getenv("ODTHISTORY"); 1333 if (historyPath == null) { 1334 historyPath = getLastPath("mcv.adt.lasthistorypath", System.getProperty("user.home")); 1335 } 1336 historyFileChooser.setCurrentDirectory(new File(historyPath)); 1337 historyFileChooser.setDialogTitle("Select ADT History File"); 1338 int returnVal = historyFileChooser.showOpenDialog(historyFileFrame); 1339 if (returnVal == JFileChooser.APPROVE_OPTION) { 1340 File file = historyFileChooser.getSelectedFile(); 1341 fileNameReturn = file.getAbsolutePath(); 1342 setLastPath("mcv.adt.lasthistorypath", file.getPath()); 1343 } 1344 1345 return fileNameReturn; 1346 } 1347 1348 /** 1349 * Returns the path that corresponds to the given McIDAS-V property ID. 1350 * 1351 * @param id ID used to store user's last selected path. 1352 * @param defaultPath Path to use if {@code id} has not been set. 1353 * 1354 * @return Either the {@code String} representation of the last selected 1355 * path, or {@code defaultPath}. 1356 */ 1357 private String getLastPath(String id, String defaultPath) { 1358 McIDASV mcv = (McIDASV)getIdv(); 1359 String path = defaultPath; 1360 if (mcv != null) { 1361 path = mcv.getObjectStore().get(id, defaultPath); 1362 } 1363 return path; 1364 } 1365 1366 /** 1367 * Sets the value of the given McIDAS-V property ID to the specified path. 1368 * 1369 * @param id ID to store. 1370 * @param path Path to associate with {@code id}. 1371 */ 1372 private void setLastPath(String id, String path) { 1373 String okayPath = (path != null) ? path : ""; 1374 McIDASV mcv = (McIDASV)getIdv(); 1375 if (mcv != null) { 1376 XmlObjectStore store = mcv.getObjectStore(); 1377 store.put(id, okayPath); 1378 store.saveIfNeeded(); 1379 } 1380 } 1381 1382 /** 1383 * Write a new ADT History File 1384 * @return true if ok 1385 */ 1386 1387 private String selectHistoryFileOutput() { 1388 1389 File saveFile = null; 1390 String ErrorMessage; 1391 1392 historyFileSaveChooser = new JFileChooser(); 1393 historyFileSaveChooser.setCurrentDirectory(null); 1394 historyFileSaveChooser.setDialogTitle("Save ADT History File"); 1395 int returnVal = historyFileSaveChooser.showSaveDialog(historyFrame); 1396 if (returnVal == JFileChooser.APPROVE_OPTION) { 1397 saveFile = historyFileSaveChooser.getSelectedFile(); 1398 try (FileWriter outFile = new FileWriter(saveFile)) { 1399 outFile.write(HistoryListOutput); 1400 outFile.flush(); 1401 outFile.close(); 1402 ErrorMessage = String.format("success writing history file output file %s\n",saveFile.toString()); 1403 } catch (IOException ex) { 1404 logger.error("problem writing to history file output", ex); 1405 ErrorMessage = String.format("error writing history file output file %s\n",saveFile.toString()); 1406 } 1407 System.out.println(ErrorMessage); 1408 userMessage(ErrorMessage); 1409 } 1410 1411 String saveFilePath = null; 1412 if (saveFile != null) { 1413 saveFilePath = saveFile.getAbsolutePath(); 1414 } 1415 return saveFilePath; 1416 1417 } 1418 1419 /** 1420 * Write out the ATCF file 1421 * @param outputstyle 1422 * @return true if written ok 1423 */ 1424 1425 private boolean ATCFFileOutput(int outputstyle) { 1426 File saveFile = null; 1427 String ATCFOutputFileName; 1428 String ATCFOutputFilePath; 1429 String ATCFFileOutput; 1430 String ATCFMessage; 1431 boolean writefileTF = false; 1432 boolean returnStatus = true; 1433 1434 if (outputstyle == 0) { 1435 // output entire history file in ATCF 1436 historyFileSaveChooser = new JFileChooser(); 1437 historyFileSaveChooser.setCurrentDirectory(null); 1438 historyFileSaveChooser.setDialogTitle("Write ATCF File"); 1439 int returnVal = historyFileSaveChooser.showSaveDialog(historyFrame); 1440 if (returnVal == JFileChooser.APPROVE_OPTION) { 1441 saveFile = historyFileSaveChooser.getSelectedFile(); 1442 writefileTF = true; 1443 } else if (returnVal == JFileChooser.CANCEL_OPTION) { 1444 // User has pressed cancel button 1445 writefileTF = false; 1446 } 1447 logger.debug("saving ATCF history listing file name={} writeTF={}", saveFile, writefileTF); 1448 } else { 1449 1450 GUIATCFStormID = ATCFEntryStormTextField.getText(); 1451 GUIATCFSiteID = ATCFEntrySiteTextField.getText(); 1452 1453 if ((GUIATCFStormID == null) || (GUIATCFSiteID == null)) { 1454 JOptionPane.showMessageDialog(this.getMainPanel(), "Please provide valid Storm and Site IDs for ATCF output."); 1455 return false; 1456 } 1457 1458 // Validate the Storm ID and Site ID inputs 1459 boolean siteStormValid = true; 1460 // Storm must be 3-char 1461 if (GUIATCFStormID.length() != 3) { 1462 siteStormValid = false; 1463 } else { 1464 // It is 3-char, make sure it's DDC (digit-digit-char) 1465 if (! GUIATCFStormID.matches("\\d\\d[A-Z]")) { 1466 siteStormValid = false; 1467 } 1468 } 1469 // Site must be 4-char 1470 if (GUIATCFSiteID.length() != 4) { 1471 siteStormValid = false; 1472 } 1473 1474 if (! siteStormValid) { 1475 JOptionPane.showMessageDialog(null, "Please provide valid Storm and Site IDs for ATCF output."); 1476 return false; 1477 } 1478 1479 // call routine to generate ATCF file name for single analysis record 1480 logger.debug("stormID={} siteID={}", GUIATCFStormID, GUIATCFSiteID); 1481 ATCFOutputFileName = Functions.adt_atcffilename(GUIATCFStormID,GUIATCFSiteID); 1482 logger.debug("atcf output name={}*", ATCFOutputFileName); 1483 ATCFOutputFilePath = System.getenv("ODTOUTPUT"); 1484 if (ATCFOutputFilePath == null) { 1485 ATCFOutputFilePath = System.getenv("HOME"); 1486 } 1487 logger.debug("atcf output path={}*", ATCFOutputFilePath); 1488 saveFile = new File(ATCFOutputFilePath + File.separator + ATCFOutputFileName); 1489 logger.debug("atcf output name={}*", saveFile.toString()); 1490 writefileTF = true; 1491 } 1492 // call routine to output file 1493 logger.info("Site ID: " + GUIATCFSiteID + ", Storm ID: " + GUIATCFStormID); 1494 if ((GUIATCFSiteID == null) || (GUIATCFStormID == null)) { 1495 JOptionPane.showMessageDialog(historyFrame, "You must first activate ATCF output"); 1496 return returnStatus; 1497 } 1498 ATCFFileOutput = History.ListHistory(outputstyle, GUIHistoryListFormat, GUIATCFSiteID, GUIATCFStormID); 1499 if (writefileTF) { 1500 try (FileWriter outFile = new FileWriter(saveFile)) { 1501 outFile.write(ATCFFileOutput); 1502 outFile.flush(); 1503 outFile.close(); 1504 ATCFMessage = String.format("Success writing ATCF file %s",saveFile); 1505 } catch (IOException ex) { 1506 logger.error("problem writing to ATCF file", ex); 1507 ATCFMessage = String.format("Error writing ATCF file %s",saveFile); 1508 } 1509 System.out.println(ATCFMessage); 1510 userMessage(ATCFMessage); 1511 } 1512 return returnStatus; 1513 } 1514 1515 private String selectForecastFile() { 1516 1517 String fileNameReturn = null; 1518 1519 logger.debug("in selectForecastFile"); 1520 JFrame forecastFileFrame = new JFrame(); 1521 JFileChooser forecastFileChooser = new JFileChooser(); 1522 String forecastPath = System.getenv("ODTAUTO"); 1523 if (forecastPath == null) { 1524 forecastPath = getLastPath("mcv.adt.lastforecastpath", System.getProperty("user.home")); 1525 } 1526 logger.debug("forecast path={}", forecastPath); 1527 forecastFileChooser.setCurrentDirectory(new File(forecastPath)); 1528 forecastFileChooser.setDialogTitle("Select ADT Forecast File"); 1529 int returnVal = forecastFileChooser.showOpenDialog(forecastFileFrame); 1530 logger.debug("retVal={}", returnVal); 1531 if (returnVal == JFileChooser.APPROVE_OPTION) { 1532 File file = forecastFileChooser.getSelectedFile(); 1533 fileNameReturn = file.getAbsolutePath(); 1534 setLastPath("mcv.adt.lastforecastpath", file.getPath()); 1535 } else { 1536 logger.error("error with file chooser"); 1537 } 1538 return fileNameReturn; 1539 } 1540 1541 private void getADTenvParameters() { 1542 History.InitCurrent(true); 1543 GUIHistoryFileName = null; 1544 1545 /* load initial ADT Environmental parameters */ 1546 GUIDeleteTF = Env.DeleteTF; 1547 GUIRunAutoTF = Env.AutoTF; 1548 GUIOverrideSceneTF = Env.OverSceneTF; 1549 GUIOverrideTF = Env.OverTF; 1550 GUIATCFOutputTF = Env.ATCFOutputTF; 1551 GUIATCFRecordOutputTF = Env.ATCFRecordOutputTF; 1552 GUIInitStrengthTF = Env.InitStrengthTF; 1553 GUILandFlagTF = Env.LandFlagTF; 1554 GUIUseCKZTF = Env.UseCKZTF; 1555 GUIVmax1or10TF = Env.Vmax1or10TF; 1556 GUICommentAddTF = Env.CommentAddTF; 1557 GUIPMWActivateTF = Env.UsePMWTF; 1558 1559 /* integer values */ 1560 GUIDomainID = Env.DomainID; 1561 GUIForecastType = Env.ForecastFileType; 1562 GUIMWJulianDate = Env.MWJulianDate; 1563 GUIMWHHMMSSTime = Env.MWHHMMSSTime; 1564 GUIStartDate = Env.StartJulianDate; 1565 GUIStartTime = Env.StartHHMMSSTime; 1566 GUIEndDate = Env.EndJulianDate; 1567 GUIEndTime = Env.EndHHMMSSTime; 1568 GUIHistoryListFormat = Env.HistoryListFormat; 1569 /* double values */ 1570 GUIRawTValue = Env.InitRawTValue; 1571 GUIMWScore = Env.MWScore; 1572 GUICKZGaleRadius = Env.CKZGaleRadius; 1573 GUICKZPenv = Env.CKZPenv; 1574 GUIRMWSize = Env.RMWSize; 1575 GUIUserLatitude = Env.SelectedLatitude; 1576 GUIUserLongitude = Env.SelectedLongitude; 1577 1578 GUIForecastFileName = Env.ForecastFileName; // needed? 1579 GUIHistoryFileListingName = Env.ASCIIOutputFileName; // needed? 1580 GUIATCFStormID = Env.StormIDString; 1581 GUIATCFSiteID = Env.ATCFSourceAgcyIDString; 1582 1583 } 1584 1585 private void loadADTenvParameters() { 1586 /* Env GlobalVariables = new Env(); */ 1587 1588 logger.debug("setting env parameters"); 1589 1590 // send ADT Environmental parameters to Env prior to running ADT 1591 // boolean values 1592 Env.DeleteTF = GUIDeleteTF; 1593 Env.AutoTF = GUIRunAutoTF; 1594 Env.OverTF = GUIOverrideTF; 1595 Env.ATCFOutputTF = GUIATCFOutputTF; 1596 Env.ATCFRecordOutputTF = GUIATCFRecordOutputTF; 1597 Env.InitStrengthTF = GUIInitStrengthTF; 1598 Env.LandFlagTF = GUILandFlagTF; 1599 Env.UseCKZTF = GUIUseCKZTF; 1600 Env.Vmax1or10TF = GUIVmax1or10TF; 1601 Env.CommentAddTF = GUICommentAddTF; 1602 Env.OverSceneTF = GUIOverrideSceneTF; 1603 Env.UsePMWTF = GUIPMWActivateTF; 1604 1605 // integer values 1606 Env.DomainID = GUIDomainID; 1607 Env.ForecastFileType = GUIForecastType; 1608 Env.MWJulianDate = GUIMWJulianDate; 1609 Env.MWHHMMSSTime = GUIMWHHMMSSTime; 1610 Env.StartJulianDate = GUIStartDate; 1611 Env.StartHHMMSSTime = GUIStartTime; 1612 Env.EndJulianDate = GUIEndDate; 1613 Env.EndHHMMSSTime = GUIEndTime; 1614 Env.HistoryListFormat = GUIHistoryListFormat; 1615 // double values 1616 Env.InitRawTValue = GUIRawTValue; 1617 Env.MWScore = GUIMWScore; 1618 Env.CKZGaleRadius = GUICKZGaleRadius; 1619 Env.CKZPenv = GUICKZPenv; 1620 Env.RMWSize = GUIRMWSize; 1621 Env.SelectedLatitude = GUIUserLatitude; 1622 Env.SelectedLongitude = GUIUserLongitude; 1623 1624 logger.debug("load forecast file name={}", GUIForecastFileName); 1625 Env.ForecastFileName = GUIForecastFileName; // needed? 1626 Env.ASCIIOutputFileName = GUIHistoryFileListingName; // needed? 1627 Env.StormIDString = GUIATCFStormID; 1628 Env.ATCFSourceAgcyIDString = GUIATCFSiteID; 1629 1630 } 1631 1632 private int ReadGUIOverrideInputFile(String GUIOverrideFile) { 1633 1634 logger.debug("opening file '{}'", GUIOverrideFile); 1635 1636 File GUIDataFile = new File(GUIOverrideFile); 1637 String delims = "[ ]+"; 1638 String line; 1639 int retval = 1; 1640 1641 GUIOverrideTF = false; 1642 GUIOverrideSceneTF = false; 1643 GUICommentString = null; 1644 GUIRunAutoTF = true; 1645 GUIDeleteTF = false; 1646 GUICommentAddTF = false; 1647 GUIStartDate = 1900001; 1648 GUIStartTime = 000000; 1649 GUIEndDate = 1900001; 1650 GUIEndTime = 000000; 1651 GUIUserLatitude = -99.5; 1652 GUIUserLongitude = -999.5; 1653 GUIDomainID = 0; 1654 runFullADTAnalysis = true; 1655 1656 try { 1657 Scanner GUIFile = new Scanner(GUIDataFile); 1658 while (GUIFile.hasNextLine()) { 1659 if ((line = GUIFile.nextLine()).isEmpty()){ 1660 break; 1661 } else { 1662 String[] tokens = line.split(delims); 1663 String IDstring = tokens[0]; 1664 String RecValue = tokens[1]; 1665 /* System.out.println("scanning IDstring=%s\n",IDstring); */ 1666 switch (IDstring) { 1667 case "ATCFOutputTF": 1668 GUIATCFOutputTF = Boolean.valueOf(RecValue); 1669 break; 1670 case "ATCFRecordOutputTF": 1671 GUIATCFRecordOutputTF = Boolean.valueOf(RecValue); 1672 break; 1673 case "InitStrengthTF": 1674 GUIInitStrengthTF = Boolean.valueOf(RecValue); 1675 break; 1676 case "LandFlagTF": 1677 GUILandFlagTF = Boolean.valueOf(RecValue); 1678 break; 1679 case "UseCKZTF": 1680 GUIUseCKZTF = Boolean.valueOf(RecValue); 1681 break; 1682 case "Vmax1or10TF": 1683 GUIVmax1or10TF = Boolean.valueOf(RecValue); 1684 break; 1685 case "UsePMWTF": 1686 GUIPMWActivateTF = Boolean.valueOf(RecValue); 1687 break; 1688 case "ForecastType": 1689 GUIForecastType = Integer.valueOf(RecValue); 1690 break; 1691 case "MWJulianDate": 1692 GUIMWJulianDate = Integer.valueOf(RecValue); 1693 break; 1694 case "MWHHMMSSTime": 1695 GUIMWHHMMSSTime = Integer.valueOf(RecValue); 1696 break; 1697 case "HistoryListFormat": 1698 GUIHistoryListFormat = Integer.valueOf(RecValue); 1699 break; 1700 case "RawTValue": 1701 GUIRawTValue = Double.valueOf(RecValue); 1702 break; 1703 case "MWScore": 1704 GUIMWScore = Double.valueOf(RecValue); 1705 break; 1706 case "CKZGaleRadius": 1707 GUICKZGaleRadius = Double.valueOf(RecValue); 1708 break; 1709 case "CKZPenv": 1710 GUICKZPenv = Double.valueOf(RecValue); 1711 break; 1712 case "RMWSize": 1713 GUIRMWSize = Double.valueOf(RecValue); 1714 break; 1715 case "HistoryFileName": 1716 GUIHistoryFileName = RecValue; 1717 break; 1718 case "ForecastFileName": 1719 GUIForecastFileName = RecValue; 1720 break; 1721 case "HistoryFileListingName": 1722 GUIHistoryFileListingName = RecValue; 1723 break; 1724 case "ATCFStormID": 1725 GUIATCFStormID = RecValue; 1726 break; 1727 case "ATCFSiteID": 1728 GUIATCFSiteID = RecValue; 1729 break; 1730 default: 1731 break; 1732 } 1733 } 1734 } 1735 GUIFile.close(); 1736 } catch (IOException ex) { 1737 retval = -1; 1738 } 1739 return retval; 1740 } 1741 1742 public void latLonWidgetChanged() { 1743 logger.debug("latlonwidgetchanged called"); 1744 try { 1745 logger.debug("latlon widget changed"); 1746 String message = latLonWidget.isValidValues(); 1747 if (message != null) { 1748 userMessage(message); 1749 return; 1750 } 1751 probeLocation = ucar.visad.Util.makeEarthLocation( 1752 latLonWidget.getLat(), latLonWidget.getLon()).getLatLonPoint(); 1753 } catch (Exception e) { 1754 logException("Handling LatLonWidget changed", e); 1755 } 1756 } 1757 1758 protected boolean shouldAddDisplayListener() { 1759 return true; 1760 } 1761 1762 protected boolean shouldAddControlListener() { 1763 return true; 1764 } 1765 1766 protected boolean canHandleEvents() { 1767 if (!getHaveInitialized() || (getMakeWindow() && !getWindowVisible())) { 1768 return false; 1769 } 1770 return isGuiShown(); 1771 } 1772 1773 public void handleDisplayChanged(DisplayEvent event) { 1774 super.handleDisplayChanged(event); 1775 if (canHandleEvents()) { 1776// int id = event.getId(); 1777// // String idstring = event.toString(); 1778// // InputEvent inputEvent = event.getInputEvent(); 1779// // System.out.println("event ID=%d %s\n",id,idstring); 1780// try { 1781// if (id == DisplayEvent.MOUSE_PRESSED_LEFT) { 1782// logger.debug("Manual Position Selection"); 1783// probeLocation = toEarth(event).getLatLonPoint(); 1784// updateProbeLocation(); 1785// } 1786// } catch (Exception e) { 1787// logException("Error selecting position with mouse", e); 1788// } 1789 } 1790 } 1791 1792 /** 1793 * Respond to the probe being dragged. 1794 * 1795 * @param event Event to handle. 1796 */ 1797 @Override public void propertyChange(PropertyChangeEvent event) { 1798 if (canHandleEvents() && SelectorDisplayable.PROPERTY_POSITION.equals(event.getPropertyName())) { 1799 try { 1800 RealTuple position = probe.getPosition(); 1801 double[] loc = position.getValues(); 1802 logger.debug("Manual Position Selection loc={}", loc); 1803 // note: loc[1] is apparently latitude, and loc[0] is longitude! 1804 probeLocation = 1805 makeEarthLocation(loc[1], loc[0], loc[2]).getLatLonPoint(); 1806 SwingUtilities.invokeLater(this::updatePositionWidget); 1807 } catch (VisADException | RemoteException ex) { 1808 logger.error("Error updating probe location", ex); 1809 } 1810 } else { 1811 super.propertyChange(event); 1812 } 1813 } 1814 1815 /** 1816 * Update {@link #latLonWidget} if it exists. 1817 * 1818 * <p>Note: must be called from the event dispatch thread.</p> 1819 */ 1820 private void updatePositionWidget() { 1821 if (latLonWidget != null) { 1822 try { 1823 logger.trace("attempting to update widget! lat={} lon={}", probeLocation.getLatitude(), probeLocation.getLongitude()); 1824 latLonWidget.setLat(getDisplayConventions().formatLatLon(probeLocation.getLatitude().getValue(CommonUnit.degree))); 1825 latLonWidget.setLon(getDisplayConventions().formatLatLon(probeLocation.getLongitude().getValue(CommonUnit.degree))); 1826 } catch (VisADException ex) { 1827 logger.error("Error updating GUI with probe position", ex); 1828 } 1829 } else { 1830 logger.trace("no lat/lon widget to update!"); 1831 } 1832 } 1833 1834 private void updateProbeLocation() { 1835 try { 1836 if (probeLocation == null) { 1837 return; 1838 } 1839 double lon = probeLocation.getLongitude().getValue(CommonUnit.degree); 1840 double lat = probeLocation.getLatitude().getValue(CommonUnit.degree); 1841 probe.setPosition( 1842 new RealTuple(RealTupleType.SpatialEarth3DTuple, new double[] { lon, lat, 0 })); 1843 probe.setVisible(true); 1844 1845 GUIUserLatitude = lat; // added TLO 1846 GUIUserLongitude = lon; // added TLO 1847 logger.debug("set lat/lon from probe at lat={} lon={}", GUIUserLatitude, GUIUserLongitude); 1848 if (latLonWidget != null) { 1849 latLonWidget.setLat(getDisplayConventions().formatLatLon( 1850 probeLocation.getLatitude().getValue(CommonUnit.degree))); 1851 latLonWidget.setLon(getDisplayConventions().formatLatLon( 1852 probeLocation.getLongitude().getValue(CommonUnit.degree))); 1853 } 1854 } catch (Exception e) { 1855 logException("Handling probe changed", e); 1856 } 1857 } 1858 1859 /** 1860 * Set the ProbeLocation property. 1861 * 1862 * @param value New value for ProbeLocation. 1863 */ 1864 public void setProbeLocation(LatLonPoint value) { 1865 probeLocation = value; 1866 } 1867 1868 /** 1869 * Get the ProbeLocation property. 1870 * 1871 * @return The ProbeLocation 1872 */ 1873 public LatLonPoint getProbeLocation() { 1874 return probeLocation; 1875 } 1876 1877 protected FlatField getFlatField(FieldImpl data) 1878 throws VisADException, RemoteException 1879 { 1880 FlatField ff; 1881 if (GridUtil.isSequence(data)) { 1882 ff = (FlatField)data.getSample(0); 1883 } else { 1884 ff = (FlatField)data; 1885 } 1886 return ff; 1887 } 1888 1889 public EarthLocation toEarth(DisplayEvent event) 1890 throws VisADException, RemoteException 1891 { 1892 NavigatedDisplay d = getNavigatedDisplay(); 1893 return (d == null) ? null : d.getEarthLocation(toBox(event)); 1894 } 1895 1896 private void GetImageDateTime() { 1897 1898 RealTuple timeTuple; 1899 Real tt; 1900 DateTime dat; 1901 1902 List infos = getDisplayInfos(); 1903 DisplayInfo displayInfo = (DisplayInfo) infos.get(0); 1904 1905 try { 1906 Animation anime = displayInfo.getViewManager().getAnimation(); 1907 Set timeSet = anime.getSet(); 1908 int pos = anime.getCurrent(); 1909 1910 timeTuple = DataUtility.getSample(timeSet, pos); 1911 tt = (Real) timeTuple.getComponent(0); 1912 dat = new DateTime(tt); 1913 } catch (VisADException e) { 1914 logException("Handling data", e); 1915 return; 1916 } catch (RemoteException f) { 1917 logger.warn("Something went wrong!", f); 1918 return; 1919 } 1920 1921 double curdate = dat.getValue(); 1922 logger.debug("curdate={}",curdate); 1923 1924 Date datevalue = new Date((long)curdate*1000); 1925 1926 SimpleDateFormat dateformat = new SimpleDateFormat("yyyyDDD"); 1927 SimpleDateFormat timeformat = new SimpleDateFormat("HHmmss"); 1928 dateformat.setTimeZone(TimeZone.getTimeZone("GMT")); 1929 timeformat.setTimeZone(TimeZone.getTimeZone("GMT")); 1930 1931 String JulianDate = dateformat.format(datevalue); 1932 String HHMMSSTime = timeformat.format(datevalue); 1933 int ImageDateInt = Integer.valueOf(JulianDate); 1934 int ImageTimeInt = Integer.valueOf(HHMMSSTime); 1935 // System.out.println("image date = %d image time=%d\n",ImageDateInt,ImageTimeInt); */ 1936 1937 Data.IRData_JulianDate = ImageDateInt; 1938 Data.IRData_HHMMSSTime = ImageTimeInt; 1939 1940 logger.debug("IMAGE DATE={} TIME={}", Data.IRData_JulianDate, Data.IRData_HHMMSSTime); 1941 } 1942 1943 private void GetImageData(float CenterLatitude, float CenterLongitude) { 1944 logger.debug("creating ReadIRImage()..."); 1945 1946 // ReadIRImage IRImage = new ReadIRImage(); 1947 1948 FlatField ffield; 1949 int SatelliteID; 1950 int channel; 1951 1952 List sources = new ArrayList(); 1953 1954 logger.debug("entering getimagedata"); 1955 boolean isTemp = false; 1956 choice.getDataSources(sources); 1957 try { 1958 List infos = getDisplayInfos(); 1959 DataInstance de = getDataInstance(); 1960 DisplayInfo displayInfo = (DisplayInfo) infos.get(0); 1961 1962 Animation anime = displayInfo.getViewManager().getAnimation(); 1963 // Set timeSet = anime.getSet(); 1964 int pos = anime.getCurrent(); 1965 ffield = DataUtil.getFlatField(de.getData()); 1966 DataSourceImpl dsi = (DataSourceImpl) sources.get(0); 1967 1968 if (dsi instanceof AddeImageDataSource) { 1969 ImageDataSource dds = (ImageDataSource) sources.get(0); 1970 List imageLists = dds.getImageList(); 1971 1972 AddeImageDescriptor aid = (AddeImageDescriptor) imageLists.get(pos); 1973 AreaDirectory ad = aid.getDirectory(); 1974 SatelliteID = ad.getSensorID(); 1975 int[] bands = ad.getBands(); 1976 channel = bands[0]; 1977 1978 isTemp = Util.isCompatible(ffield, AirTemperature.getRealType()); 1979 } else { 1980 channel = 4; 1981 SatelliteID = 70; 1982 // String name = ffield.getSample(0).getType().prettyString(); 1983 } 1984 } catch (VisADException e) { 1985 logException("Handling data", e); 1986 return; 1987 } catch (RemoteException f) { 1988 logger.warn("Something went wrong!", f); 1989 return; 1990 } 1991 1992 // String shortName = choice.getName(); 1993 1994 Env.UserDefineDomain = 0; // automated 1995 // String sidName = Functions.adt_sattypes(SatelliteID); 1996 1997 logger.debug("SatelliteID={}", SatelliteID); 1998 1999 try { 2000 ReadIRImage.ReadIRDataFile(ffield, 2001 CenterLatitude, 2002 CenterLongitude, 2003 SatelliteID, 2004 channel, 2005 isTemp); 2006 } 2007 catch (Exception ex) { 2008 logger.error("ReadIRImage failed", ex); 2009 } 2010 } 2011}