001/* 002 * This file is part of McIDAS-V 003 * 004 * Copyright 2007-2023 005 * Space Science and Engineering Center (SSEC) 006 * University of Wisconsin - Madison 007 * 1225 W. Dayton Street, Madison, WI 53706, USA 008 * https://www.ssec.wisc.edu/mcidas 009 * 010 * All Rights Reserved 011 * 012 * McIDAS-V is built on Unidata's IDV and SSEC's VisAD libraries, and 013 * some McIDAS-V source code is based on IDV and VisAD source code. 014 * 015 * McIDAS-V is free software; you can redistribute it and/or modify 016 * it under the terms of the GNU Lesser Public License as published by 017 * the Free Software Foundation; either version 3 of the License, or 018 * (at your option) any later version. 019 * 020 * McIDAS-V is distributed in the hope that it will be useful, 021 * but WITHOUT ANY WARRANTY; without even the implied warranty of 022 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 023 * GNU Lesser Public License for more details. 024 * 025 * You should have received a copy of the GNU Lesser Public License 026 * along with this program. If not, see http://www.gnu.org/licenses. 027 */ 028 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 -> { 727 WebBrowser.browse("https://www.ssec.wisc.edu/mcidas/software/v/resources/adt/McV_ADT_1p7.pdf"); 728 }); 729 730 GuiUtils.tmpInsets = GuiUtils.INSETS_5; 731 JComponent widgets = 732 GuiUtils.formLayout( 733 arr(left(hbox(arr(new JLabel("Storm Center Selection:"), manButton, autoButton), 5)), 734 filler(), 735 left(hbox(arr(manualStormSelectLabel), 10)), 736 filler(), 737 left(hbox(arr(filler(30, 1), latLonWidget))), filler(), 738 left(hbox(arr(autoStormSelectLabel), 10)), filler(), 739 left(hbox(arr(filler(30, 1), forecastBtn, forecastTypeBox, 740 forecastSelectLabel, forecastLabel), 5)), filler(), 741 left(hbox(arr(blankfield))), 742 filler(1, 5), 743 left(hbox(arr(new JLabel("HISTORY FILE INFORMATION")), 10)), filler(), 744 left(hbox(arr(filler(30, 1), historyBtn, new JLabel 745 ("Selected History File: "), selectedHistoryFile), 5)), 746 filler(), 747 left(hbox(arr(blankfield))), 748 filler(1, 5), 749 left(hbox(arr(new JLabel("PMW ANALYSIS")), 10)), filler(), 750 left(hbox(arr(filler(30, 1), PMWActivateButton, 751 pmwManDateLabel, pmwManDateTextField, pmwManTimeLabel, 752 pmwManTimeTextField, pmwManScoreLabel, pmwManScoreTextField), 5)), filler(), 753 left(hbox(arr(blankfield))), 754 filler(1, 5), 755 left(hbox(arr(new JLabel("MISCELLANEOUS OPTIONS")), 10)), filler(), 756 left(hbox(arr(filler(30, 1), new JLabel("MSLP Conversion Method:"), mslpDvorakButton, mslpCKZButton, ckzPenvLabel, ckzPenvTextField, ckz34radiusLabel, ckz34radiusTextField), 5)), filler(), 757 left(hbox(arr(filler(30, 1), sceneOverrideButton, OverrideLabel), 5)), filler(), 758 left(hbox(arr(filler(30, 1), LandFlagLabel, LandONButton, LandOFFButton, filler(20, 1), VOutLabel, V1MinButton, V10MinButton, filler(20, 1), RawTLabel, RawTTextField, RMWLabel, RMWTextField), 5)), filler(), 759 left(hbox(arr(filler(30, 1), ATCFOutputLabel, ATCFOutputButton, ATCFEntryStormLabel, ATCFEntryStormTextField, ATCFEntrySiteLabel, ATCFEntrySiteTextField), 5)), filler(), 760 left(hbox(arr(filler(80, 1), adtBtn, listBtn, helpLinkLabel), 20)), filler())); 761 762 JPanel controls = topLeft(widgets); 763 764 /* set up ADT Bulletin display area */ 765 resultArea = new JTextArea(); 766 resultArea.setEditable(false); 767 768 Font c = new Font("Courier", Font.BOLD, 12); 769 770 resultFrame = new JFrame("ADT Results"); 771 resultFrame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); 772 JScrollPane resultScroller = new JScrollPane(resultArea); 773 resultScroller.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS); 774 resultFrame.add(resultScroller, BorderLayout.CENTER); 775 resultFrame.setPreferredSize(new Dimension(400, 600)); 776 resultFrame.setFont(c); 777 778 /* set up ADT History File display area */ 779 historyFrame = new JFrame("ADT History File Listing"); 780 Container historyContainer = historyFrame.getContentPane(); 781 historyFrame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); 782 JPanel historyTextPanel = new JPanel(); 783 FlowLayout historyListLayout = new FlowLayout(); 784 historyTextPanel.setLayout(historyListLayout); 785 historyListLayout.setAlignment(FlowLayout.CENTER); 786 787 historyArea = new JTextArea(50,150); 788 historyArea.setEditable(false); 789 JScrollPane historyScroller = new JScrollPane(historyArea); 790 historyScroller.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS); 791 historyScroller.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED); 792 historyScroller.setPreferredSize(new Dimension(1200, 400)); 793 historyArea.setFont(c); 794 795 JPanel historyLabelPanel = new JPanel(); 796 FlowLayout HistoryLabelLayout = new FlowLayout(); 797 historyLabelPanel.setLayout(HistoryLabelLayout); 798 HistoryLabelLayout.setAlignment(FlowLayout.CENTER); 799 historyLabel = new JLabel("No History File Selected"); 800 historyLabel.setPreferredSize(new Dimension(800, 20)); 801 historyLabel.setFont(c); 802 803 /* history file Editing Date Selection window */ 804 JFrame historyDateFrame = new JFrame("History File Editor"); 805 Container historyDateContainer = historyDateFrame.getContentPane(); 806 historyDateFrame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); 807 JPanel historyDatePanel = new JPanel(); 808 FlowLayout DateStartEndLayout = new FlowLayout(); 809 historyDatePanel.setLayout(DateStartEndLayout); 810 DateStartEndLayout.setAlignment(FlowLayout.CENTER); 811 JLabel historyDateStartLabel = new JLabel("Start:"); 812 JLabel historyDateStartDateLabel = new JLabel("Date"); 813 JTextField historyDateStartDateTextField = new JTextField("0000XXX00", 10); 814 JLabel historyDateStartTimeLabel = new JLabel("Time"); 815 JTextField historyDateStartTimeTextField = new JTextField("-1", 8); 816 JLabel historyDateEndLabel = new JLabel("End"); 817 JLabel historyDateEndDateLabel = new JLabel("Date"); 818 JTextField historyDateEndDateTextField = new JTextField("0000XXX00", 10); 819 JLabel historyDateEndTimeLabel = new JLabel("Time"); 820 JTextField historyDateEndTimeTextField = new JTextField("-1", 8); 821 822 JPanel historyButtonPanel = new JPanel(); 823 FlowLayout HistoryButtonLayout = new FlowLayout(); 824 historyButtonPanel.setLayout(HistoryButtonLayout); 825 HistoryButtonLayout.setAlignment(FlowLayout.CENTER); 826 827 JButton historySaveListingBtn = new JButton("Write History"); 828 historySaveListingBtn.setPreferredSize(new Dimension(200, 20)); 829 historySaveListingBtn.addActionListener(ae -> { 830 GUIHistoryFileListingName = selectHistoryFileOutput(); 831 logger.debug("saving history listing file name={}", GUIHistoryFileListingName); 832 GUIHistoryListFormat = -1; 833 }); 834 JButton historyWriteATCFBtn = new JButton("Write ATCF"); 835 historyWriteATCFBtn.setPreferredSize(new Dimension(200, 20)); 836 historyWriteATCFBtn.addActionListener(ae -> { 837 GUIATCFOutputTF = true; 838 GUIHistoryListFormat = 0; 839 logger.debug("calling ATCFFileOutput"); 840 ATCFFileOutput(0); 841 }); 842 historyLabelPanel.add(historyLabel); 843 historyLabelPanel.setComponentOrientation(ComponentOrientation.LEFT_TO_RIGHT); 844 historyTextPanel.add(historyScroller); 845 historyTextPanel.setComponentOrientation(ComponentOrientation.LEFT_TO_RIGHT); 846 847 historyButtonPanel.add(historySaveListingBtn); 848 historyButtonPanel.add(historyWriteATCFBtn); 849 historyButtonPanel.setComponentOrientation(ComponentOrientation.LEFT_TO_RIGHT); 850 historyContainer.add(historyLabelPanel,BorderLayout.NORTH); 851 historyContainer.add(historyTextPanel,BorderLayout.CENTER); 852 historyContainer.add(historyButtonPanel,BorderLayout.SOUTH); 853 854 historyDateStartDateTextField.addActionListener(ae -> { 855 JTextField textField = (JTextField)ae.getSource(); 856 GUIStartDate = Functions.cmonth2julian(textField.getText()); 857 }); 858 historyDateStartTimeTextField.addActionListener(ae -> { 859 JTextField textField = (JTextField)ae.getSource(); 860 GUIStartTime = Integer.valueOf(textField.getText()); 861 }); 862 historyDateEndDateTextField.addActionListener(ae -> { 863 JTextField textField = (JTextField)ae.getSource(); 864 GUIEndDate = Functions.cmonth2julian(textField.getText()); 865 }); 866 historyDateEndTimeTextField.addActionListener(ae -> { 867 JTextField textField = (JTextField)ae.getSource(); 868 GUIEndTime = Integer.valueOf(textField.getText()); 869 }); 870 871 JPanel historyDateButtonPanel = new JPanel(); 872 FlowLayout DateButtonLayout = new FlowLayout(); 873 historyDateButtonPanel.setLayout(DateButtonLayout); 874 DateButtonLayout.setAlignment(FlowLayout.CENTER); 875 JRadioButton historyEditDeleteButton = new JRadioButton("Delete Records"); 876 historyEditDeleteButton.setActionCommand("Delete"); 877 historyEditDeleteButton.setSelected(false); 878 JRadioButton historyEditAddCommentButton = new JRadioButton("Add Comment"); 879 historyEditAddCommentButton.setActionCommand("Comment"); 880 historyEditAddCommentButton.setSelected(false); 881 ButtonGroup editgroup = new ButtonGroup(); 882 editgroup.add(historyEditDeleteButton); 883 editgroup.add(historyEditAddCommentButton); 884 JLabel historyEditAddCommentLabel = new JLabel("Comment:"); 885 JTextField historyEditAddCommentTextField = new JTextField("no comment entered", 25); 886 historyEditAddCommentTextField.setEnabled(false); 887 888 historyEditDeleteButton.addActionListener(ae -> { 889 // history Edit - Delete 890 historyEditDeleteButton.setSelected(true); 891 historyEditAddCommentButton.setSelected(false); 892 historyEditAddCommentLabel.setEnabled(false); 893 historyEditAddCommentTextField.setEnabled(false); 894 GUICommentAddTF = false; 895 GUIDeleteTF = true; 896 }); 897 898 historyEditAddCommentButton.addActionListener(ae -> { 899 // history Edit - Add Comment 900 historyEditDeleteButton.setSelected(false); 901 historyEditAddCommentButton.setSelected(true); 902 historyEditAddCommentLabel.setEnabled(true); 903 historyEditAddCommentTextField.setEnabled(true); 904 GUICommentAddTF = true; 905 GUIDeleteTF = false; 906 }); 907 historyEditAddCommentTextField.addActionListener(ae -> { 908 JTextField src = (JTextField)ae.getSource(); 909 GUICommentString = src.getText(); 910 }); 911 JPanel historyEditInputPanel = new JPanel(); 912 FlowLayout EditInputButtonLayout = new FlowLayout(); 913 historyEditInputPanel.setLayout(EditInputButtonLayout); 914 EditInputButtonLayout.setAlignment(FlowLayout.CENTER); 915 JButton historyEditApplyButton = new JButton("Apply Edits"); 916 historyEditApplyButton.setPreferredSize(new Dimension(150, 20)); 917 historyEditApplyButton.addActionListener(ae -> modifyHistoryFile()); 918 JButton historyEditCancelButton = new JButton("Cancel"); 919 historyEditCancelButton.setPreferredSize(new Dimension(150, 20)); 920 historyEditCancelButton.addActionListener(ae -> historyDateFrame.dispose()); 921 historyDatePanel.add(historyDateStartLabel); 922 historyDatePanel.add(historyDateStartDateLabel); 923 historyDatePanel.add(historyDateStartDateTextField); 924 historyDatePanel.add(historyDateStartTimeLabel); 925 historyDatePanel.add(historyDateStartTimeTextField); 926 historyDatePanel.add(historyDateEndLabel); 927 historyDatePanel.add(historyDateEndDateLabel); 928 historyDatePanel.add(historyDateEndDateTextField); 929 historyDatePanel.add(historyDateEndTimeLabel); 930 historyDatePanel.add(historyDateEndTimeTextField); 931 historyDatePanel.setComponentOrientation(ComponentOrientation.LEFT_TO_RIGHT); 932 historyDateButtonPanel.add(historyEditDeleteButton); 933 historyDateButtonPanel.add(historyEditAddCommentButton); 934 historyDateButtonPanel.add(historyEditAddCommentLabel); 935 historyDateButtonPanel.add(historyEditAddCommentTextField); 936 historyEditInputPanel.add(historyEditApplyButton); 937 historyEditInputPanel.add(historyEditCancelButton); 938 historyDateContainer.add(historyDatePanel, BorderLayout.NORTH); 939 historyDateContainer.add(historyDateButtonPanel, BorderLayout.CENTER); 940 historyDateContainer.add(historyEditInputPanel, BorderLayout.SOUTH); 941 942 /* set up Scene Type Override Window display window */ 943 overrideSceneFrame = new JFrame("Override Scene Type"); 944 overrideSceneFrame.setSize(new Dimension(400, 300)); 945 Container overrideSceneContainer = overrideSceneFrame.getContentPane(); 946 overrideSceneFrame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); 947 JPanel overrideSceneCurrentPanel = new JPanel(); 948 FlowLayout OverrideSceneCurrentLayout = new FlowLayout(); 949 overrideSceneCurrentPanel.setLayout(OverrideSceneCurrentLayout); 950 OverrideSceneCurrentLayout.setAlignment(FlowLayout.CENTER); 951 JLabel overrideSceneCurrentLabel = new JLabel("Current Scene Type:"); 952 overrideSceneCurrentValueLabel = new JLabel(SCENE_TYPES[Env.OverrideSceneTypeIndex]); 953 JPanel overrideSceneSelectPanel = new JPanel(); 954 FlowLayout OverrideSceneSelectLayout = new FlowLayout(); 955 overrideSceneCurrentPanel.setLayout(OverrideSceneSelectLayout); 956 OverrideSceneSelectLayout.setAlignment(FlowLayout.CENTER); 957 JLabel overrideSceneSelectLabel = new JLabel("Select New Scene Type:"); 958 overrideSceneTypeBox = new JComboBox<>(SCENE_TYPES); 959 overrideSceneTypeBox.setSelectedIndex(Env.OverrideSceneTypeIndex); 960 overrideSceneTypeBox.setPreferredSize(new Dimension(150, 20)); 961 // overrideSceneTypeBox.addActionListener(ame -> Env.OverrideSceneTypeIndex = overrideSceneTypeBox.getSelectedIndex()); 962 JPanel overrideSceneButtonPanel = new JPanel(); 963 FlowLayout OverrideSceneButtonLayout = new FlowLayout(); 964 overrideSceneButtonPanel.setLayout(OverrideSceneButtonLayout); 965 OverrideSceneButtonLayout.setAlignment(FlowLayout.CENTER); 966 JButton overrideSceneAcceptButton = new JButton("Accept New Scene"); 967 overrideSceneAcceptButton.setPreferredSize(new Dimension(190, 20)); 968 overrideSceneAcceptButton.addActionListener(ae -> { 969 // accept new scene selection 970 overrideSceneFrame.setVisible(false); 971 Env.OverrideSceneTypeIndex = overrideSceneTypeBox.getSelectedIndex(); 972 OverrideLabel.setText(SCENE_TYPE_PREFIX + SCENE_TYPES[Env.OverrideSceneTypeIndex]); 973 overrideSceneCurrentValueLabel.setText(SCENE_TYPES[Env.OverrideSceneTypeIndex]); 974 // runADTmain(); 975 }); 976 JButton overrideSceneCancelButton = new JButton("Keep Current Scene"); 977 overrideSceneCancelButton.setPreferredSize(new Dimension(190, 20)); 978 overrideSceneCancelButton.addActionListener(ae -> { 979 overrideSceneFrame.setVisible(false); 980 // runADTmain(); 981 }); 982 overrideSceneCurrentPanel.add(overrideSceneCurrentLabel); 983 overrideSceneCurrentPanel.add(overrideSceneCurrentValueLabel); 984 overrideSceneSelectPanel.add(overrideSceneSelectLabel); 985 overrideSceneSelectPanel.add(overrideSceneTypeBox); 986 overrideSceneButtonPanel.add(overrideSceneAcceptButton); 987 overrideSceneButtonPanel.add(overrideSceneCancelButton); 988 overrideSceneContainer.add(overrideSceneCurrentPanel, BorderLayout.NORTH); 989 overrideSceneContainer.add(overrideSceneSelectPanel, BorderLayout.CENTER); 990 overrideSceneContainer.add(overrideSceneButtonPanel, BorderLayout.SOUTH); 991 992 JScrollPane scrollPane = new JScrollPane(controls); 993 scrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS); 994 return scrollPane; 995 } 996 997 /** 998 * Do some cursory checking on validity of selected History file 999 * @param historyFileName 1000 * @return true is seems ok 1001 */ 1002 1003 private boolean validHistoryFile(String historyFileName) { 1004 boolean seemsOk = true; 1005 1006 History CurrentHistory = new History(); 1007 1008 try { 1009 logger.debug("trying to read history file {}", historyFileName); 1010 CurrentHistory.ReadHistoryFile(historyFileName); 1011 } catch (IOException exception) { 1012 logger.warn("History file %s is not valid", historyFileName); 1013 seemsOk = false; 1014 } 1015 1016 logger.debug("Number of history records: {}", History.HistoryNumberOfRecords()); 1017 if (History.HistoryNumberOfRecords() == 0) seemsOk = false; 1018 return seemsOk; 1019 } 1020 1021 @Override 1022 protected void getHelpMenuItems(List items, boolean forMenuBar) { 1023 items.add(GuiUtils.makeMenuItem("Details", this, "showDetails")); 1024 JMenuItem jmi = new JMenuItem("User's Guide"); 1025 GuiUtils.setIcon(jmi, "/auxdata/ui/icons/help.png"); 1026 jmi.addActionListener(e -> { 1027 WebBrowser.browse("https://www.ssec.wisc.edu/mcidas/software/v/resources/adt/McV_ADT_1p7.pdf"); 1028 }); 1029 items.add(jmi); 1030 } 1031 1032 private void runADTmain() { 1033 if (!running) { 1034 running = true; 1035 adtBtn.setEnabled(false); 1036 adtBtn.setText("Running"); 1037 Misc.run(() -> { 1038 runADT(); 1039 ExitADT(); 1040 }); 1041 } 1042 } 1043 1044 private void runADT() { 1045 Main StormADT = new Main(); 1046 String ADTRunOutput; 1047 String ErrorMessage; 1048 1049 if (GUIFileOverrideTF) { 1050 String GUIOverrideFilePath = System.getenv("ODTHOME"); 1051 if (GUIOverrideFilePath == null) { 1052 GUIOverrideFilePath = System.getenv("HOME"); 1053 } 1054 String GUIOverrideFile = GUIOverrideFilePath + "/runadt.nogui.inputs.txt"; 1055 /* GUIFileOverrideCheckBoxToggle(); change toggle back to OFF */ 1056 int RetVal = ReadGUIOverrideInputFile(GUIOverrideFile); 1057 if (RetVal == -1) { 1058 ErrorMessage = String.format("Error reading GUI override file %s\n",GUIOverrideFile); 1059 System.out.println(ErrorMessage); 1060 userMessage(ErrorMessage); 1061 ExitADT(); 1062 return; 1063 } 1064 } 1065 1066 loadADTenvParameters(); 1067 1068 boolean RunAuto = Env.AutoTF; 1069 1070 // In auto mode, make sure a valid forecast file was selected 1071 if (RunAuto) { 1072 if (GUIForecastFileName == null) { 1073 userMessage("A valid forecast file must be selected to use Automated mode."); 1074 ExitADT(); 1075 return; 1076 } 1077 } 1078 1079 /* set storm position either through automated storm selection or by manual choice */ 1080 GetImageDateTime(); 1081 int ReturnVal = StormADT.GetInitialPosition(); // should set up to throw exception instead of return value 1082 if (ReturnVal < 0) { 1083 ErrorMessage = "Error obtaining initial position... exiting ADT\n"; 1084 System.out.println(ErrorMessage); 1085 userMessage(ErrorMessage); 1086 ExitADT(); 1087 } else { 1088 if (RunAuto) { 1089 try { 1090 float CenterLatitude = (float)Env.SelectedLatitude; 1091 float CenterLongitude = (float)Env.SelectedLongitude; 1092 /* System.out.println("pre-ARCHER latitude=%f longitude=%f\n",CenterLatitude,CenterLongitude); */ 1093 GetImageData(CenterLatitude, CenterLongitude); 1094 } catch (Exception exception) { 1095 ErrorMessage = "Error reading IR data pre-ARCHER\n"; 1096 System.out.println(ErrorMessage); 1097 userMessage(ErrorMessage); 1098 ExitADT(); 1099 return; 1100 } 1101 StormADT.GetARCHERPosition(); 1102 } else { 1103 if (probeLocation == null) { 1104 ErrorMessage = "Please select storm center location manually and try again"; 1105 System.out.println(ErrorMessage); 1106 userMessage(ErrorMessage); 1107 ExitADT(); 1108 return; 1109 } else { 1110 Env.SelectedLatitude = probeLocation.getLatitude().getValue(); 1111 Env.SelectedLongitude = probeLocation.getLongitude().getValue(); 1112 } 1113 } 1114 1115 try { 1116 float CenterLatitude = (float) Env.SelectedLatitude; 1117 float CenterLongitude = (float) Env.SelectedLongitude; 1118 /* System.out.println("latitude=%f longitude=%f domain=%d\n",CenterLatitude,CenterLongitude,DomainID); */ 1119 GetImageData(CenterLatitude, CenterLongitude); 1120 } catch (Exception e) { 1121 ErrorMessage = "Error reading IR data in getimagedata()\n"; 1122 logger.error(ErrorMessage.trim(), e); 1123 userMessage(ErrorMessage); 1124 ExitADT(); 1125 return; 1126 } 1127 1128 // TJJ Jun 2017 Just about ready, a few more validation checks and we can run 1129 // If CKZ chosen as MSLP Conversion Method, need to validate Penv and 34kt Radius fields 1130 // This may not be the best place to do this, but it's better than not doing it ;-) 1131 1132 if (GUIUseCKZTF) { 1133 1134 String newPenvStr = ckzPenvTextField.getText(); 1135 boolean badPenv = false; 1136 try { 1137 int newPenv = Integer.valueOf(newPenvStr); 1138 if (newPenv > 0) { 1139 GUICKZPenv = newPenv; 1140 Env.CKZPenv = GUICKZPenv; 1141 } else { 1142 badPenv = true; 1143 } 1144 } catch (NumberFormatException nfe) { 1145 badPenv = true; 1146 } 1147 1148 if (badPenv) { 1149 // Throw up a warning and bail out 1150 showBadIntWarning("Penv", newPenvStr); 1151 return; 1152 } 1153 1154 String newRadiusStr = ckz34radiusTextField.getText(); 1155 boolean badNewRadius = false; 1156 try { 1157 int newRadius = Integer.valueOf(newRadiusStr); 1158 if (newRadius > 0) { 1159 GUICKZGaleRadius = newRadius; 1160 Env.CKZGaleRadius = GUICKZGaleRadius; 1161 } else { 1162 badNewRadius = true; 1163 } 1164 } catch (NumberFormatException nfe) { 1165 badNewRadius = true; 1166 } 1167 1168 if (badNewRadius) { 1169 // Throw up a warning and bail out 1170 showBadIntWarning("Radius", newRadiusStr); 1171 return; 1172 } 1173 1174 } 1175 1176 try { 1177 logger.debug("RUNNING ADT ANALYSIS"); 1178 ADTRunOutput = StormADT.RunADTAnalysis(runFullADTAnalysis,GUIHistoryFileName); 1179 } catch (IOException exception) { 1180 ErrorMessage = "Error with call to StormADT.RunADT()\n"; 1181 logger.error(ErrorMessage.trim(), exception); 1182 userMessage(ErrorMessage); 1183 ExitADT(); 1184 return; 1185 } 1186 if (GUIOverrideSceneTF) { 1187 /* System.out.println("Overriding scene type!!! Scene value=%d\n",InitialSceneTypeValue); */ 1188 overrideSceneCurrentValueLabel.setText(SCENE_TYPES[Env.OverrideSceneTypeIndex]); 1189 overrideSceneFrame.pack(); 1190 overrideSceneFrame.setVisible(true); 1191 ExitADT(); 1192 } else { 1193 logger.debug("done running ADT"); 1194 1195 resultArea.setText(ADTRunOutput); 1196 resultFrame.pack(); 1197 resultFrame.setVisible(true); 1198 1199 // TJJ Dec 2017 1200 // This is in reference to Request #11, Bug #17 from 1201 // http://mcidas.ssec.wisc.edu/inquiry-v/?inquiry=1187 1202 // Since the intent here is to modify the currently active history file by appending 1203 // one record, and since that record insert had been previously commented out below, 1204 // we'll assume this was never working properly in the first place. To prevent the 1205 // current History File from being clobbered, we just won't do the re-write for now, 1206 // since as is, a deep Exception zeros out the file, and the original file should 1207 // at the very least remain unmodified. 1208 1209// if (GUIHistoryFileName != null) { 1210// try { 1211// // int[] InsertRecs = History.InsertHistoryRecord(runFullADTAnalysis,GUIHistoryFileName); 1212// /* System.out.println("*** Modified=%d InsertOverwriteFlag=%d***\n",InsertRecs[0],InsertRecs[1]); */ 1213// int NumRecs = History.WriteHistoryFile(GUIHistoryFileName); 1214// ErrorMessage = String.format("Number of records written to history file: %d\n", NumRecs); 1215// } catch (IOException exception) { 1216// ErrorMessage = String.format("Error writing history file %s\n", GUIHistoryFileName); 1217// } catch (Exception e) { 1218// logger.error("Exception: ", e); 1219// ErrorMessage = String.format("Error writing history file %s\n", GUIHistoryFileName); 1220// } 1221// logger.warn(ErrorMessage.trim()); 1222// userMessage(ErrorMessage); 1223// } 1224 1225 if (GUIATCFRecordOutputTF) { 1226 ATCFFileOutput(-1); 1227 } 1228 1229 ExitADT(); 1230 } 1231 } 1232 } 1233 1234 /** 1235 * Show a warning about a certain parameter needing to be greater than zero. 1236 * 1237 * @param type Parameter name. Cannot be {@code null}. 1238 * @param badValue Erroneous value. Cannot be {@code null}. 1239 */ 1240 private void showBadIntWarning(String type, String badValue) { 1241 String msg = "Invalid %s value: %s\nPlease provide a positive integer."; 1242 JOptionPane.showMessageDialog(null, 1243 String.format(msg, type, badValue)); 1244 ExitADT(); 1245 } 1246 1247 private void ExitADT() { 1248 running = false; 1249 adtBtn.setEnabled(true); 1250 adtBtn.setText("Run Analysis"); 1251 } 1252 1253 /* 1254 * Override for additional local cleanup 1255 * (non-Javadoc) 1256 * @see ucar.unidata.idv.control.DisplayControlImpl#doRemove() 1257 */ 1258 1259 @Override public void doRemove() throws RemoteException, VisADException { 1260 super.doRemove(); 1261 if (resultFrame != null) { 1262 resultFrame.dispose(); 1263 } 1264 if (historyFrame != null) { 1265 historyFrame.dispose(); 1266 } 1267 } 1268 1269 private void listHistoryFile() { 1270 HistoryListOutput = null; 1271 1272 History CurrentHistory = new History(); 1273 1274 // Make sure a valid History File has been selected. At startup, value will be null 1275 if (GUIHistoryFileName == null) { 1276 JOptionPane.showMessageDialog(null, 1277 "Please first select a valid ADT History File."); 1278 return; 1279 } 1280 1281 try { 1282 logger.debug("trying to read history file {}", GUIHistoryFileName); 1283 CurrentHistory.ReadHistoryFile(GUIHistoryFileName); 1284 } catch (IOException exception) { 1285 String ErrorMessage = String.format("History file %s is not found",GUIHistoryFileName); 1286 logger.warn(ErrorMessage); 1287 userMessage(ErrorMessage); 1288 return; 1289 } 1290 1291 logger.debug("Number of history records: {}", History.HistoryNumberOfRecords()); 1292 1293 HistoryListOutput = History.ListHistory(0, -1, "CIMS", "99X"); 1294 historyLabel.setText(GUIHistoryFileName); 1295 historyArea.setText(HistoryListOutput); 1296 historyFrame.pack(); 1297 historyFrame.setVisible(true); 1298 1299 } 1300 1301 private void modifyHistoryFile() { 1302 1303 if (GUIDeleteTF) { 1304 // delete records 1305 int DeleteReturn[] = History.DeleteHistoryRecords(runFullADTAnalysis,GUIHistoryFileName); 1306 logger.debug("deleted {} records... modified {} records", DeleteReturn[1],DeleteReturn[0]); 1307 } else if( GUICommentAddTF) { 1308 // 1309 int CommentAddReturn = History.CommentHistoryRecords(GUICommentString); 1310 logger.debug("added comment to {} records",CommentAddReturn); 1311 } else { 1312 // invalid selection 1313 logger.warn("entered invalid selection!"); 1314 } 1315 1316 try { 1317 int HistoryFileRecords = History.WriteHistoryFile(GUIHistoryFileName); 1318 if (HistoryFileRecords >= 0) { 1319 logger.debug("wrote {} records to '{}'", HistoryFileRecords, GUIHistoryFileName); 1320 } 1321 } catch (IOException exception) { 1322 String ErrorMessage = String.format("error updating history file %s",GUIHistoryFileName); 1323 System.out.println(ErrorMessage); 1324 userMessage(ErrorMessage); 1325 } 1326 } 1327 1328 private String selectHistoryFile() { 1329 1330 String fileNameReturn = null; 1331 1332 JFrame historyFileFrame = new JFrame(); 1333 JFileChooser historyFileChooser = new JFileChooser(); 1334 String historyPath = System.getenv("ODTHISTORY"); 1335 if (historyPath == null) { 1336 historyPath = getLastPath("mcv.adt.lasthistorypath", System.getProperty("user.home")); 1337 } 1338 historyFileChooser.setCurrentDirectory(new File(historyPath)); 1339 historyFileChooser.setDialogTitle("Select ADT History File"); 1340 int returnVal = historyFileChooser.showOpenDialog(historyFileFrame); 1341 if (returnVal == JFileChooser.APPROVE_OPTION) { 1342 File file = historyFileChooser.getSelectedFile(); 1343 fileNameReturn = file.getAbsolutePath(); 1344 setLastPath("mcv.adt.lasthistorypath", file.getPath()); 1345 } 1346 1347 return fileNameReturn; 1348 } 1349 1350 /** 1351 * Returns the path that corresponds to the given McIDAS-V property ID. 1352 * 1353 * @param id ID used to store user's last selected path. 1354 * @param defaultPath Path to use if {@code id} has not been set. 1355 * 1356 * @return Either the {@code String} representation of the last selected 1357 * path, or {@code defaultPath}. 1358 */ 1359 private String getLastPath(String id, String defaultPath) { 1360 McIDASV mcv = (McIDASV)getIdv(); 1361 String path = defaultPath; 1362 if (mcv != null) { 1363 path = mcv.getObjectStore().get(id, defaultPath); 1364 } 1365 return path; 1366 } 1367 1368 /** 1369 * Sets the value of the given McIDAS-V property ID to the specified path. 1370 * 1371 * @param id ID to store. 1372 * @param path Path to associate with {@code id}. 1373 */ 1374 private void setLastPath(String id, String path) { 1375 String okayPath = (path != null) ? path : ""; 1376 McIDASV mcv = (McIDASV)getIdv(); 1377 if (mcv != null) { 1378 XmlObjectStore store = mcv.getObjectStore(); 1379 store.put(id, okayPath); 1380 store.saveIfNeeded(); 1381 } 1382 } 1383 1384 /** 1385 * Write a new ADT History File 1386 * @return true if ok 1387 */ 1388 1389 private String selectHistoryFileOutput() { 1390 1391 File saveFile = null; 1392 String ErrorMessage; 1393 1394 historyFileSaveChooser = new JFileChooser(); 1395 historyFileSaveChooser.setCurrentDirectory(null); 1396 historyFileSaveChooser.setDialogTitle("Save ADT History File"); 1397 int returnVal = historyFileSaveChooser.showSaveDialog(historyFrame); 1398 if (returnVal == JFileChooser.APPROVE_OPTION) { 1399 saveFile = historyFileSaveChooser.getSelectedFile(); 1400 try (FileWriter outFile = new FileWriter(saveFile)) { 1401 outFile.write(HistoryListOutput); 1402 outFile.flush(); 1403 outFile.close(); 1404 ErrorMessage = String.format("success writing history file output file %s\n",saveFile.toString()); 1405 } catch (IOException ex) { 1406 logger.error("problem writing to history file output", ex); 1407 ErrorMessage = String.format("error writing history file output file %s\n",saveFile.toString()); 1408 } 1409 System.out.println(ErrorMessage); 1410 userMessage(ErrorMessage); 1411 } 1412 1413 String saveFilePath = null; 1414 if (saveFile != null) { 1415 saveFilePath = saveFile.getAbsolutePath(); 1416 } 1417 return saveFilePath; 1418 1419 } 1420 1421 /** 1422 * Write out the ATCF file 1423 * @param outputstyle 1424 * @return true if written ok 1425 */ 1426 1427 private boolean ATCFFileOutput(int outputstyle) { 1428 File saveFile = null; 1429 String ATCFOutputFileName; 1430 String ATCFOutputFilePath; 1431 String ATCFFileOutput; 1432 String ATCFMessage; 1433 boolean writefileTF = false; 1434 boolean returnStatus = true; 1435 1436 if (outputstyle == 0) { 1437 // output entire history file in ATCF 1438 historyFileSaveChooser = new JFileChooser(); 1439 historyFileSaveChooser.setCurrentDirectory(null); 1440 historyFileSaveChooser.setDialogTitle("Write ATCF File"); 1441 int returnVal = historyFileSaveChooser.showSaveDialog(historyFrame); 1442 if (returnVal == JFileChooser.APPROVE_OPTION) { 1443 saveFile = historyFileSaveChooser.getSelectedFile(); 1444 writefileTF = true; 1445 } else if (returnVal == JFileChooser.CANCEL_OPTION) { 1446 // User has pressed cancel button 1447 writefileTF = false; 1448 } 1449 logger.debug("saving ATCF history listing file name={} writeTF={}", saveFile, writefileTF); 1450 } else { 1451 1452 GUIATCFStormID = ATCFEntryStormTextField.getText(); 1453 GUIATCFSiteID = ATCFEntrySiteTextField.getText(); 1454 1455 if ((GUIATCFStormID == null) || (GUIATCFSiteID == null)) { 1456 JOptionPane.showMessageDialog(this.getMainPanel(), "Please provide valid Storm and Site IDs for ATCF output."); 1457 return false; 1458 } 1459 1460 // Validate the Storm ID and Site ID inputs 1461 boolean siteStormValid = true; 1462 // Storm must be 3-char 1463 if (GUIATCFStormID.length() != 3) { 1464 siteStormValid = false; 1465 } else { 1466 // It is 3-char, make sure it's DDC (digit-digit-char) 1467 if (! GUIATCFStormID.matches("\\d\\d[A-Z]")) { 1468 siteStormValid = false; 1469 } 1470 } 1471 // Site must be 4-char 1472 if (GUIATCFSiteID.length() != 4) { 1473 siteStormValid = false; 1474 } 1475 1476 if (! siteStormValid) { 1477 JOptionPane.showMessageDialog(null, "Please provide valid Storm and Site IDs for ATCF output."); 1478 return false; 1479 } 1480 1481 // call routine to generate ATCF file name for single analysis record 1482 logger.debug("stormID={} siteID={}", GUIATCFStormID, GUIATCFSiteID); 1483 ATCFOutputFileName = Functions.adt_atcffilename(GUIATCFStormID,GUIATCFSiteID); 1484 logger.debug("atcf output name={}*", ATCFOutputFileName); 1485 ATCFOutputFilePath = System.getenv("ODTOUTPUT"); 1486 if (ATCFOutputFilePath == null) { 1487 ATCFOutputFilePath = System.getenv("HOME"); 1488 } 1489 logger.debug("atcf output path={}*", ATCFOutputFilePath); 1490 saveFile = new File(ATCFOutputFilePath + File.separator + ATCFOutputFileName); 1491 logger.debug("atcf output name={}*", saveFile.toString()); 1492 writefileTF = true; 1493 } 1494 // call routine to output file 1495 logger.info("Site ID: " + GUIATCFSiteID + ", Storm ID: " + GUIATCFStormID); 1496 if ((GUIATCFSiteID == null) || (GUIATCFStormID == null)) { 1497 JOptionPane.showMessageDialog(historyFrame, "You must first activate ATCF output"); 1498 return returnStatus; 1499 } 1500 ATCFFileOutput = History.ListHistory(outputstyle, GUIHistoryListFormat, GUIATCFSiteID, GUIATCFStormID); 1501 if (writefileTF) { 1502 try (FileWriter outFile = new FileWriter(saveFile)) { 1503 outFile.write(ATCFFileOutput); 1504 outFile.flush(); 1505 outFile.close(); 1506 ATCFMessage = String.format("Success writing ATCF file %s",saveFile); 1507 } catch (IOException ex) { 1508 logger.error("problem writing to ATCF file", ex); 1509 ATCFMessage = String.format("Error writing ATCF file %s",saveFile); 1510 } 1511 System.out.println(ATCFMessage); 1512 userMessage(ATCFMessage); 1513 } 1514 return returnStatus; 1515 } 1516 1517 private String selectForecastFile() { 1518 1519 String fileNameReturn = null; 1520 1521 logger.debug("in selectForecastFile"); 1522 JFrame forecastFileFrame = new JFrame(); 1523 JFileChooser forecastFileChooser = new JFileChooser(); 1524 String forecastPath = System.getenv("ODTAUTO"); 1525 if (forecastPath == null) { 1526 forecastPath = getLastPath("mcv.adt.lastforecastpath", System.getProperty("user.home")); 1527 } 1528 logger.debug("forecast path={}", forecastPath); 1529 forecastFileChooser.setCurrentDirectory(new File(forecastPath)); 1530 forecastFileChooser.setDialogTitle("Select ADT Forecast File"); 1531 int returnVal = forecastFileChooser.showOpenDialog(forecastFileFrame); 1532 logger.debug("retVal={}", returnVal); 1533 if (returnVal == JFileChooser.APPROVE_OPTION) { 1534 File file = forecastFileChooser.getSelectedFile(); 1535 fileNameReturn = file.getAbsolutePath(); 1536 setLastPath("mcv.adt.lastforecastpath", file.getPath()); 1537 } else { 1538 logger.error("error with file chooser"); 1539 } 1540 return fileNameReturn; 1541 } 1542 1543 private void getADTenvParameters() { 1544 History.InitCurrent(true); 1545 GUIHistoryFileName = null; 1546 1547 /* load initial ADT Environmental parameters */ 1548 GUIDeleteTF = Env.DeleteTF; 1549 GUIRunAutoTF = Env.AutoTF; 1550 GUIOverrideSceneTF = Env.OverSceneTF; 1551 GUIOverrideTF = Env.OverTF; 1552 GUIATCFOutputTF = Env.ATCFOutputTF; 1553 GUIATCFRecordOutputTF = Env.ATCFRecordOutputTF; 1554 GUIInitStrengthTF = Env.InitStrengthTF; 1555 GUILandFlagTF = Env.LandFlagTF; 1556 GUIUseCKZTF = Env.UseCKZTF; 1557 GUIVmax1or10TF = Env.Vmax1or10TF; 1558 GUICommentAddTF = Env.CommentAddTF; 1559 GUIPMWActivateTF = Env.UsePMWTF; 1560 1561 /* integer values */ 1562 GUIDomainID = Env.DomainID; 1563 GUIForecastType = Env.ForecastFileType; 1564 GUIMWJulianDate = Env.MWJulianDate; 1565 GUIMWHHMMSSTime = Env.MWHHMMSSTime; 1566 GUIStartDate = Env.StartJulianDate; 1567 GUIStartTime = Env.StartHHMMSSTime; 1568 GUIEndDate = Env.EndJulianDate; 1569 GUIEndTime = Env.EndHHMMSSTime; 1570 GUIHistoryListFormat = Env.HistoryListFormat; 1571 /* double values */ 1572 GUIRawTValue = Env.InitRawTValue; 1573 GUIMWScore = Env.MWScore; 1574 GUICKZGaleRadius = Env.CKZGaleRadius; 1575 GUICKZPenv = Env.CKZPenv; 1576 GUIRMWSize = Env.RMWSize; 1577 GUIUserLatitude = Env.SelectedLatitude; 1578 GUIUserLongitude = Env.SelectedLongitude; 1579 1580 GUIForecastFileName = Env.ForecastFileName; // needed? 1581 GUIHistoryFileListingName = Env.ASCIIOutputFileName; // needed? 1582 GUIATCFStormID = Env.StormIDString; 1583 GUIATCFSiteID = Env.ATCFSourceAgcyIDString; 1584 1585 } 1586 1587 private void loadADTenvParameters() { 1588 /* Env GlobalVariables = new Env(); */ 1589 1590 logger.debug("setting env parameters"); 1591 1592 // send ADT Environmental parameters to Env prior to running ADT 1593 // boolean values 1594 Env.DeleteTF = GUIDeleteTF; 1595 Env.AutoTF = GUIRunAutoTF; 1596 Env.OverTF = GUIOverrideTF; 1597 Env.ATCFOutputTF = GUIATCFOutputTF; 1598 Env.ATCFRecordOutputTF = GUIATCFRecordOutputTF; 1599 Env.InitStrengthTF = GUIInitStrengthTF; 1600 Env.LandFlagTF = GUILandFlagTF; 1601 Env.UseCKZTF = GUIUseCKZTF; 1602 Env.Vmax1or10TF = GUIVmax1or10TF; 1603 Env.CommentAddTF = GUICommentAddTF; 1604 Env.OverSceneTF = GUIOverrideSceneTF; 1605 Env.UsePMWTF = GUIPMWActivateTF; 1606 1607 // integer values 1608 Env.DomainID = GUIDomainID; 1609 Env.ForecastFileType = GUIForecastType; 1610 Env.MWJulianDate = GUIMWJulianDate; 1611 Env.MWHHMMSSTime = GUIMWHHMMSSTime; 1612 Env.StartJulianDate = GUIStartDate; 1613 Env.StartHHMMSSTime = GUIStartTime; 1614 Env.EndJulianDate = GUIEndDate; 1615 Env.EndHHMMSSTime = GUIEndTime; 1616 Env.HistoryListFormat = GUIHistoryListFormat; 1617 // double values 1618 Env.InitRawTValue = GUIRawTValue; 1619 Env.MWScore = GUIMWScore; 1620 Env.CKZGaleRadius = GUICKZGaleRadius; 1621 Env.CKZPenv = GUICKZPenv; 1622 Env.RMWSize = GUIRMWSize; 1623 Env.SelectedLatitude = GUIUserLatitude; 1624 Env.SelectedLongitude = GUIUserLongitude; 1625 1626 logger.debug("load forecast file name={}", GUIForecastFileName); 1627 Env.ForecastFileName = GUIForecastFileName; // needed? 1628 Env.ASCIIOutputFileName = GUIHistoryFileListingName; // needed? 1629 Env.StormIDString = GUIATCFStormID; 1630 Env.ATCFSourceAgcyIDString = GUIATCFSiteID; 1631 1632 } 1633 1634 private int ReadGUIOverrideInputFile(String GUIOverrideFile) { 1635 1636 logger.debug("opening file '{}'", GUIOverrideFile); 1637 1638 File GUIDataFile = new File(GUIOverrideFile); 1639 String delims = "[ ]+"; 1640 String line; 1641 int retval = 1; 1642 1643 GUIOverrideTF = false; 1644 GUIOverrideSceneTF = false; 1645 GUICommentString = null; 1646 GUIRunAutoTF = true; 1647 GUIDeleteTF = false; 1648 GUICommentAddTF = false; 1649 GUIStartDate = 1900001; 1650 GUIStartTime = 000000; 1651 GUIEndDate = 1900001; 1652 GUIEndTime = 000000; 1653 GUIUserLatitude = -99.5; 1654 GUIUserLongitude = -999.5; 1655 GUIDomainID = 0; 1656 runFullADTAnalysis = true; 1657 1658 try { 1659 Scanner GUIFile = new Scanner(GUIDataFile); 1660 while (GUIFile.hasNextLine()) { 1661 if ((line = GUIFile.nextLine()).isEmpty()){ 1662 break; 1663 } else { 1664 String[] tokens = line.split(delims); 1665 String IDstring = tokens[0]; 1666 String RecValue = tokens[1]; 1667 /* System.out.println("scanning IDstring=%s\n",IDstring); */ 1668 switch (IDstring) { 1669 case "ATCFOutputTF": 1670 GUIATCFOutputTF = Boolean.valueOf(RecValue); 1671 break; 1672 case "ATCFRecordOutputTF": 1673 GUIATCFRecordOutputTF = Boolean.valueOf(RecValue); 1674 break; 1675 case "InitStrengthTF": 1676 GUIInitStrengthTF = Boolean.valueOf(RecValue); 1677 break; 1678 case "LandFlagTF": 1679 GUILandFlagTF = Boolean.valueOf(RecValue); 1680 break; 1681 case "UseCKZTF": 1682 GUIUseCKZTF = Boolean.valueOf(RecValue); 1683 break; 1684 case "Vmax1or10TF": 1685 GUIVmax1or10TF = Boolean.valueOf(RecValue); 1686 break; 1687 case "UsePMWTF": 1688 GUIPMWActivateTF = Boolean.valueOf(RecValue); 1689 break; 1690 case "ForecastType": 1691 GUIForecastType = Integer.valueOf(RecValue); 1692 break; 1693 case "MWJulianDate": 1694 GUIMWJulianDate = Integer.valueOf(RecValue); 1695 break; 1696 case "MWHHMMSSTime": 1697 GUIMWHHMMSSTime = Integer.valueOf(RecValue); 1698 break; 1699 case "HistoryListFormat": 1700 GUIHistoryListFormat = Integer.valueOf(RecValue); 1701 break; 1702 case "RawTValue": 1703 GUIRawTValue = Double.valueOf(RecValue); 1704 break; 1705 case "MWScore": 1706 GUIMWScore = Double.valueOf(RecValue); 1707 break; 1708 case "CKZGaleRadius": 1709 GUICKZGaleRadius = Double.valueOf(RecValue); 1710 break; 1711 case "CKZPenv": 1712 GUICKZPenv = Double.valueOf(RecValue); 1713 break; 1714 case "RMWSize": 1715 GUIRMWSize = Double.valueOf(RecValue); 1716 break; 1717 case "HistoryFileName": 1718 GUIHistoryFileName = RecValue; 1719 break; 1720 case "ForecastFileName": 1721 GUIForecastFileName = RecValue; 1722 break; 1723 case "HistoryFileListingName": 1724 GUIHistoryFileListingName = RecValue; 1725 break; 1726 case "ATCFStormID": 1727 GUIATCFStormID = RecValue; 1728 break; 1729 case "ATCFSiteID": 1730 GUIATCFSiteID = RecValue; 1731 break; 1732 default: 1733 break; 1734 } 1735 } 1736 } 1737 GUIFile.close(); 1738 } catch (IOException ex) { 1739 retval = -1; 1740 } 1741 return retval; 1742 } 1743 1744 public void latLonWidgetChanged() { 1745 logger.debug("latlonwidgetchanged called"); 1746 try { 1747 logger.debug("latlon widget changed"); 1748 String message = latLonWidget.isValidValues(); 1749 if (message != null) { 1750 userMessage(message); 1751 return; 1752 } 1753 probeLocation = ucar.visad.Util.makeEarthLocation( 1754 latLonWidget.getLat(), latLonWidget.getLon()).getLatLonPoint(); 1755 } catch (Exception e) { 1756 logException("Handling LatLonWidget changed", e); 1757 } 1758 } 1759 1760 protected boolean shouldAddDisplayListener() { 1761 return true; 1762 } 1763 1764 protected boolean shouldAddControlListener() { 1765 return true; 1766 } 1767 1768 protected boolean canHandleEvents() { 1769 if (!getHaveInitialized() || (getMakeWindow() && !getWindowVisible())) { 1770 return false; 1771 } 1772 return isGuiShown(); 1773 } 1774 1775 public void handleDisplayChanged(DisplayEvent event) { 1776 super.handleDisplayChanged(event); 1777 if (canHandleEvents()) { 1778// int id = event.getId(); 1779// // String idstring = event.toString(); 1780// // InputEvent inputEvent = event.getInputEvent(); 1781// // System.out.println("event ID=%d %s\n",id,idstring); 1782// try { 1783// if (id == DisplayEvent.MOUSE_PRESSED_LEFT) { 1784// logger.debug("Manual Position Selection"); 1785// probeLocation = toEarth(event).getLatLonPoint(); 1786// updateProbeLocation(); 1787// } 1788// } catch (Exception e) { 1789// logException("Error selecting position with mouse", e); 1790// } 1791 } 1792 } 1793 1794 /** 1795 * Respond to the probe being dragged. 1796 * 1797 * @param event Event to handle. 1798 */ 1799 @Override public void propertyChange(PropertyChangeEvent event) { 1800 if (canHandleEvents() && SelectorDisplayable.PROPERTY_POSITION.equals(event.getPropertyName())) { 1801 try { 1802 RealTuple position = probe.getPosition(); 1803 double[] loc = position.getValues(); 1804 logger.debug("Manual Position Selection loc={}", loc); 1805 // note: loc[1] is apparently latitude, and loc[0] is longitude! 1806 probeLocation = 1807 makeEarthLocation(loc[1], loc[0], loc[2]).getLatLonPoint(); 1808 SwingUtilities.invokeLater(this::updatePositionWidget); 1809 } catch (VisADException | RemoteException ex) { 1810 logger.error("Error updating probe location", ex); 1811 } 1812 } else { 1813 super.propertyChange(event); 1814 } 1815 } 1816 1817 /** 1818 * Update {@link #latLonWidget} if it exists. 1819 * 1820 * <p>Note: must be called from the event dispatch thread.</p> 1821 */ 1822 private void updatePositionWidget() { 1823 if (latLonWidget != null) { 1824 try { 1825 logger.trace("attempting to update widget! lat={} lon={}", probeLocation.getLatitude(), probeLocation.getLongitude()); 1826 latLonWidget.setLat(getDisplayConventions().formatLatLon(probeLocation.getLatitude().getValue(CommonUnit.degree))); 1827 latLonWidget.setLon(getDisplayConventions().formatLatLon(probeLocation.getLongitude().getValue(CommonUnit.degree))); 1828 } catch (VisADException ex) { 1829 logger.error("Error updating GUI with probe position", ex); 1830 } 1831 } else { 1832 logger.trace("no lat/lon widget to update!"); 1833 } 1834 } 1835 1836 private void updateProbeLocation() { 1837 try { 1838 if (probeLocation == null) { 1839 return; 1840 } 1841 double lon = probeLocation.getLongitude().getValue(CommonUnit.degree); 1842 double lat = probeLocation.getLatitude().getValue(CommonUnit.degree); 1843 probe.setPosition( 1844 new RealTuple(RealTupleType.SpatialEarth3DTuple, new double[] { lon, lat, 0 })); 1845 probe.setVisible(true); 1846 1847 GUIUserLatitude = lat; // added TLO 1848 GUIUserLongitude = lon; // added TLO 1849 logger.debug("set lat/lon from probe at lat={} lon={}", GUIUserLatitude, GUIUserLongitude); 1850 if (latLonWidget != null) { 1851 latLonWidget.setLat(getDisplayConventions().formatLatLon( 1852 probeLocation.getLatitude().getValue(CommonUnit.degree))); 1853 latLonWidget.setLon(getDisplayConventions().formatLatLon( 1854 probeLocation.getLongitude().getValue(CommonUnit.degree))); 1855 } 1856 } catch (Exception e) { 1857 logException("Handling probe changed", e); 1858 } 1859 } 1860 1861 /** 1862 * Set the ProbeLocation property. 1863 * 1864 * @param value New value for ProbeLocation. 1865 */ 1866 public void setProbeLocation(LatLonPoint value) { 1867 probeLocation = value; 1868 } 1869 1870 /** 1871 * Get the ProbeLocation property. 1872 * 1873 * @return The ProbeLocation 1874 */ 1875 public LatLonPoint getProbeLocation() { 1876 return probeLocation; 1877 } 1878 1879 protected FlatField getFlatField(FieldImpl data) 1880 throws VisADException, RemoteException 1881 { 1882 FlatField ff; 1883 if (GridUtil.isSequence(data)) { 1884 ff = (FlatField)data.getSample(0); 1885 } else { 1886 ff = (FlatField)data; 1887 } 1888 return ff; 1889 } 1890 1891 public EarthLocation toEarth(DisplayEvent event) 1892 throws VisADException, RemoteException 1893 { 1894 NavigatedDisplay d = getNavigatedDisplay(); 1895 return (d == null) ? null : d.getEarthLocation(toBox(event)); 1896 } 1897 1898 private void GetImageDateTime() { 1899 1900 RealTuple timeTuple; 1901 Real tt; 1902 DateTime dat; 1903 1904 List infos = getDisplayInfos(); 1905 DisplayInfo displayInfo = (DisplayInfo) infos.get(0); 1906 1907 try { 1908 Animation anime = displayInfo.getViewManager().getAnimation(); 1909 Set timeSet = anime.getSet(); 1910 int pos = anime.getCurrent(); 1911 1912 timeTuple = DataUtility.getSample(timeSet, pos); 1913 tt = (Real) timeTuple.getComponent(0); 1914 dat = new DateTime(tt); 1915 } catch (VisADException e) { 1916 logException("Handling data", e); 1917 return; 1918 } catch (RemoteException f) { 1919 logger.warn("Something went wrong!", f); 1920 return; 1921 } 1922 1923 double curdate = dat.getValue(); 1924 logger.debug("curdate={}",curdate); 1925 1926 Date datevalue = new Date((long)curdate*1000); 1927 1928 SimpleDateFormat dateformat = new SimpleDateFormat("yyyyDDD"); 1929 SimpleDateFormat timeformat = new SimpleDateFormat("HHmmss"); 1930 dateformat.setTimeZone(TimeZone.getTimeZone("GMT")); 1931 timeformat.setTimeZone(TimeZone.getTimeZone("GMT")); 1932 1933 String JulianDate = dateformat.format(datevalue); 1934 String HHMMSSTime = timeformat.format(datevalue); 1935 int ImageDateInt = Integer.valueOf(JulianDate); 1936 int ImageTimeInt = Integer.valueOf(HHMMSSTime); 1937 // System.out.println("image date = %d image time=%d\n",ImageDateInt,ImageTimeInt); */ 1938 1939 Data.IRData_JulianDate = ImageDateInt; 1940 Data.IRData_HHMMSSTime = ImageTimeInt; 1941 1942 logger.debug("IMAGE DATE={} TIME={}", Data.IRData_JulianDate, Data.IRData_HHMMSSTime); 1943 } 1944 1945 private void GetImageData(float CenterLatitude, float CenterLongitude) { 1946 logger.debug("creating ReadIRImage()..."); 1947 1948 // ReadIRImage IRImage = new ReadIRImage(); 1949 1950 FlatField ffield; 1951 int SatelliteID; 1952 int channel; 1953 1954 List sources = new ArrayList(); 1955 1956 logger.debug("entering getimagedata"); 1957 boolean isTemp = false; 1958 choice.getDataSources(sources); 1959 try { 1960 List infos = getDisplayInfos(); 1961 DataInstance de = getDataInstance(); 1962 DisplayInfo displayInfo = (DisplayInfo) infos.get(0); 1963 1964 Animation anime = displayInfo.getViewManager().getAnimation(); 1965 // Set timeSet = anime.getSet(); 1966 int pos = anime.getCurrent(); 1967 ffield = DataUtil.getFlatField(de.getData()); 1968 DataSourceImpl dsi = (DataSourceImpl) sources.get(0); 1969 1970 if (dsi instanceof AddeImageDataSource) { 1971 ImageDataSource dds = (ImageDataSource) sources.get(0); 1972 List imageLists = dds.getImageList(); 1973 1974 AddeImageDescriptor aid = (AddeImageDescriptor) imageLists.get(pos); 1975 AreaDirectory ad = aid.getDirectory(); 1976 SatelliteID = ad.getSensorID(); 1977 int[] bands = ad.getBands(); 1978 channel = bands[0]; 1979 1980 isTemp = Util.isCompatible(ffield, AirTemperature.getRealType()); 1981 } else { 1982 channel = 4; 1983 SatelliteID = 70; 1984 // String name = ffield.getSample(0).getType().prettyString(); 1985 } 1986 } catch (VisADException e) { 1987 logException("Handling data", e); 1988 return; 1989 } catch (RemoteException f) { 1990 logger.warn("Something went wrong!", f); 1991 return; 1992 } 1993 1994 // String shortName = choice.getName(); 1995 1996 Env.UserDefineDomain = 0; // automated 1997 // String sidName = Functions.adt_sattypes(SatelliteID); 1998 1999 logger.debug("SatelliteID={}", SatelliteID); 2000 2001 try { 2002 ReadIRImage.ReadIRDataFile(ffield, 2003 CenterLatitude, 2004 CenterLongitude, 2005 SatelliteID, 2006 channel, 2007 isTemp); 2008 } 2009 catch (Exception ex) { 2010 logger.error("ReadIRImage failed", ex); 2011 } 2012 } 2013}