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